diff --git a/PepperDashEssentials/Room/Cotija/CotijaConfig.cs b/PepperDashEssentials/AppServer/CotijaConfig.cs
similarity index 100%
rename from PepperDashEssentials/Room/Cotija/CotijaConfig.cs
rename to PepperDashEssentials/AppServer/CotijaConfig.cs
diff --git a/PepperDashEssentials/Room/Cotija/CotijaDdvc01DeviceBridge.cs b/PepperDashEssentials/AppServer/CotijaDdvc01DeviceBridge.cs
similarity index 100%
rename from PepperDashEssentials/Room/Cotija/CotijaDdvc01DeviceBridge.cs
rename to PepperDashEssentials/AppServer/CotijaDdvc01DeviceBridge.cs
diff --git a/PepperDashEssentials/Room/Cotija/CotijaSystemController.cs b/PepperDashEssentials/AppServer/CotijaSystemController.cs
similarity index 93%
rename from PepperDashEssentials/Room/Cotija/CotijaSystemController.cs
rename to PepperDashEssentials/AppServer/CotijaSystemController.cs
index ab06a774..1785163e 100644
--- a/PepperDashEssentials/Room/Cotija/CotijaSystemController.cs
+++ b/PepperDashEssentials/AppServer/CotijaSystemController.cs
@@ -14,7 +14,8 @@ using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using PepperDash.Core;
-using PepperDash.Essentials.Core;
+using PepperDash.Essentials.Core;
+using PepperDash.Essentials.Core.Config;
using PepperDash.Essentials.Room.Cotija;
namespace PepperDash.Essentials
@@ -82,6 +83,36 @@ namespace PepperDash.Essentials
CrestronConsole.AddNewConsoleCommand(TestHttpRequest,
"mobilehttprequest", "Tests an HTTP get to URL given", ConsoleAccessLevelEnum.AccessOperator);
+ CrestronConsole.AddNewConsoleCommand(PrintActionDictionaryPaths, "showactionpaths", "Prints the paths in teh Action Dictionary", ConsoleAccessLevelEnum.AccessOperator);
+
+
+ CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
+ }
+
+ ///
+ /// Sends message to server to indicate the system is shutting down
+ ///
+ ///
+ void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType)
+ {
+ if (programEventType == eProgramStatusEventType.Stopping && WSClient.Connected)
+ {
+ SendMessageToServer(JObject.FromObject( new
+ {
+ type = "/system/close"
+ }));
+
+ }
+ }
+
+ public void PrintActionDictionaryPaths(object o)
+ {
+ Debug.Console(0, this, "ActionDictionary Contents:");
+
+ foreach (var item in ActionDictionary)
+ {
+ Debug.Console(0, this, "{0}", item.Key);
+ }
}
///
@@ -158,6 +189,12 @@ namespace PepperDash.Essentials
///
void AuthorizeSystem(string code)
{
+ if (string.IsNullOrEmpty(SystemUuid))
+ {
+ CrestronConsole.ConsoleCommandResponse("System does not have a UUID. Please ensure proper portal-format configuration is loaded and restart.");
+ return;
+ }
+
if (string.IsNullOrEmpty(code))
{
CrestronConsole.ConsoleCommandResponse("Please enter a user code to authorize a system");
@@ -244,6 +281,8 @@ namespace PepperDash.Essentials
/// URL of the server, including the port number, if not 80. Format: "serverUrlOrIp:port"
void RegisterSystemToServer()
{
+
+
var ready = RegisterLockEvent.Wait(20000);
if (!ready)
{
@@ -258,7 +297,6 @@ namespace PepperDash.Essentials
confObject.Info.RuntimeInfo.AppName = Assembly.GetExecutingAssembly().GetName().Name;
var version = Assembly.GetExecutingAssembly().GetName().Version;
confObject.Info.RuntimeInfo.AssemblyVersion = string.Format("{0}.{1}.{2}", version.Major, version.Minor, version.Build);
- confObject.Info.RuntimeInfo.OsVersion = Crestron.SimplSharp.CrestronEnvironment.OSVersion.Firmware;
string postBody = JsonConvert.SerializeObject(confObject);
SystemUuid = confObject.SystemUuid;
@@ -452,8 +490,8 @@ namespace PepperDash.Essentials
{
SendMessageToServer(JObject.FromObject(new
{
- type = "/heartbeat-ack"
- }));
+ type = "/system/heartbeatAck"
+ }));
var code = content["userCode"];
if(code != null)
diff --git a/PepperDashEssentials/Room/Cotija/DeviceTypeInterfaces/IChannelExtensions.cs b/PepperDashEssentials/AppServer/DeviceTypeInterfaces/IChannelExtensions.cs
similarity index 76%
rename from PepperDashEssentials/Room/Cotija/DeviceTypeInterfaces/IChannelExtensions.cs
rename to PepperDashEssentials/AppServer/DeviceTypeInterfaces/IChannelExtensions.cs
index b26a7f99..dd23d85a 100644
--- a/PepperDashEssentials/Room/Cotija/DeviceTypeInterfaces/IChannelExtensions.cs
+++ b/PepperDashEssentials/AppServer/DeviceTypeInterfaces/IChannelExtensions.cs
@@ -14,9 +14,9 @@ namespace PepperDash.Essentials.Room.Cotija
{
var prefix = string.Format(@"/device/{0}/", (dev as IKeyed).Key);
- controller.AddAction(prefix + "chanup", new PressAndHoldAction(dev.ChannelUp));
- controller.AddAction(prefix + "chandown", new PressAndHoldAction(dev.ChannelDown));
- controller.AddAction(prefix + "lastchan", new PressAndHoldAction(dev.LastChannel));
+ controller.AddAction(prefix + "chanUp", new PressAndHoldAction(dev.ChannelUp));
+ controller.AddAction(prefix + "chanDown", new PressAndHoldAction(dev.ChannelDown));
+ controller.AddAction(prefix + "lastChan", new PressAndHoldAction(dev.LastChannel));
controller.AddAction(prefix + "guide", new PressAndHoldAction(dev.Guide));
controller.AddAction(prefix + "info", new PressAndHoldAction(dev.Info));
controller.AddAction(prefix + "exit", new PressAndHoldAction(dev.Exit));
@@ -26,9 +26,9 @@ namespace PepperDash.Essentials.Room.Cotija
{
var prefix = string.Format(@"/device/{0}/", (dev as IKeyed).Key);
- controller.RemoveAction(prefix + "chanup");
- controller.RemoveAction(prefix + "chandown");
- controller.RemoveAction(prefix + "lastchan");
+ controller.RemoveAction(prefix + "chanUp");
+ controller.RemoveAction(prefix + "chanDown");
+ controller.RemoveAction(prefix + "lastChan");
controller.RemoveAction(prefix + "guide");
controller.RemoveAction(prefix + "info");
controller.RemoveAction(prefix + "exit");
diff --git a/PepperDashEssentials/Room/Cotija/DeviceTypeInterfaces/IColorExtensions.cs b/PepperDashEssentials/AppServer/DeviceTypeInterfaces/IColorExtensions.cs
similarity index 100%
rename from PepperDashEssentials/Room/Cotija/DeviceTypeInterfaces/IColorExtensions.cs
rename to PepperDashEssentials/AppServer/DeviceTypeInterfaces/IColorExtensions.cs
diff --git a/PepperDashEssentials/Room/Cotija/DeviceTypeInterfaces/IDPadExtensions.cs b/PepperDashEssentials/AppServer/DeviceTypeInterfaces/IDPadExtensions.cs
similarity index 100%
rename from PepperDashEssentials/Room/Cotija/DeviceTypeInterfaces/IDPadExtensions.cs
rename to PepperDashEssentials/AppServer/DeviceTypeInterfaces/IDPadExtensions.cs
diff --git a/PepperDashEssentials/Room/Cotija/DeviceTypeInterfaces/IDvrExtensions.cs b/PepperDashEssentials/AppServer/DeviceTypeInterfaces/IDvrExtensions.cs
similarity index 100%
rename from PepperDashEssentials/Room/Cotija/DeviceTypeInterfaces/IDvrExtensions.cs
rename to PepperDashEssentials/AppServer/DeviceTypeInterfaces/IDvrExtensions.cs
diff --git a/PepperDashEssentials/Room/Cotija/DeviceTypeInterfaces/INumericExtensions.cs b/PepperDashEssentials/AppServer/DeviceTypeInterfaces/INumericExtensions.cs
similarity index 86%
rename from PepperDashEssentials/Room/Cotija/DeviceTypeInterfaces/INumericExtensions.cs
rename to PepperDashEssentials/AppServer/DeviceTypeInterfaces/INumericExtensions.cs
index 7246cb51..376ed57b 100644
--- a/PepperDashEssentials/Room/Cotija/DeviceTypeInterfaces/INumericExtensions.cs
+++ b/PepperDashEssentials/AppServer/DeviceTypeInterfaces/INumericExtensions.cs
@@ -24,8 +24,8 @@ namespace PepperDash.Essentials.Room.Cotija
controller.AddAction(prefix + "num7", new PressAndHoldAction(dev.Digit0));
controller.AddAction(prefix + "num8", new PressAndHoldAction(dev.Digit0));
controller.AddAction(prefix + "num9", new PressAndHoldAction(dev.Digit0));
- controller.AddAction(prefix + "dash", new PressAndHoldAction(dev.KeypadAccessoryButton1));
- controller.AddAction(prefix + "enter", new PressAndHoldAction(dev.KeypadAccessoryButton2));
+ controller.AddAction(prefix + "numDash", new PressAndHoldAction(dev.KeypadAccessoryButton1));
+ controller.AddAction(prefix + "numEnter", new PressAndHoldAction(dev.KeypadAccessoryButton2));
// Deal with the Accessory functions on the numpad later
}
@@ -43,8 +43,8 @@ namespace PepperDash.Essentials.Room.Cotija
controller.RemoveAction(prefix + "num7");
controller.RemoveAction(prefix + "num8");
controller.RemoveAction(prefix + "num9");
- controller.RemoveAction(prefix + "dash");
- controller.RemoveAction(prefix + "enter");
+ controller.RemoveAction(prefix + "numDash");
+ controller.RemoveAction(prefix + "numEnter");
}
}
}
\ No newline at end of file
diff --git a/PepperDashEssentials/Room/Cotija/DeviceTypeInterfaces/IPowerExtensions.cs b/PepperDashEssentials/AppServer/DeviceTypeInterfaces/IPowerExtensions.cs
similarity index 66%
rename from PepperDashEssentials/Room/Cotija/DeviceTypeInterfaces/IPowerExtensions.cs
rename to PepperDashEssentials/AppServer/DeviceTypeInterfaces/IPowerExtensions.cs
index 68b36675..732d2740 100644
--- a/PepperDashEssentials/Room/Cotija/DeviceTypeInterfaces/IPowerExtensions.cs
+++ b/PepperDashEssentials/AppServer/DeviceTypeInterfaces/IPowerExtensions.cs
@@ -14,18 +14,18 @@ namespace PepperDash.Essentials.Room.Cotija
{
var prefix = string.Format(@"/device/{0}/", (dev as IKeyed).Key);
- controller.AddAction(prefix + "poweron", new Action(dev.PowerOn));
- controller.AddAction(prefix + "poweroff", new Action(dev.PowerOff));
- controller.AddAction(prefix + "powertoggle", new Action(dev.PowerToggle));
+ controller.AddAction(prefix + "powerOn", new Action(dev.PowerOn));
+ controller.AddAction(prefix + "powerOff", new Action(dev.PowerOff));
+ controller.AddAction(prefix + "powerToggle", new Action(dev.PowerToggle));
}
public static void UnlinkActions(this IPower dev, CotijaSystemController controller)
{
var prefix = string.Format(@"/device/{0}/", (dev as IKeyed).Key);
- controller.RemoveAction(prefix + "poweron");
- controller.RemoveAction(prefix + "poweroff");
- controller.RemoveAction(prefix + "powertoggle");
+ controller.RemoveAction(prefix + "powerOn");
+ controller.RemoveAction(prefix + "powerOff");
+ controller.RemoveAction(prefix + "powerToggle");
}
}
diff --git a/PepperDashEssentials/Room/Cotija/DeviceTypeInterfaces/ISetTopBoxControlsExtensions.cs b/PepperDashEssentials/AppServer/DeviceTypeInterfaces/ISetTopBoxControlsExtensions.cs
similarity index 87%
rename from PepperDashEssentials/Room/Cotija/DeviceTypeInterfaces/ISetTopBoxControlsExtensions.cs
rename to PepperDashEssentials/AppServer/DeviceTypeInterfaces/ISetTopBoxControlsExtensions.cs
index 7fa7d1b1..99198fa6 100644
--- a/PepperDashEssentials/Room/Cotija/DeviceTypeInterfaces/ISetTopBoxControlsExtensions.cs
+++ b/PepperDashEssentials/AppServer/DeviceTypeInterfaces/ISetTopBoxControlsExtensions.cs
@@ -14,7 +14,7 @@ namespace PepperDash.Essentials.Room.Cotija
{
var prefix = string.Format(@"/device/{0}/", (dev as IKeyed).Key);
- controller.AddAction(prefix + "dvrlist", new PressAndHoldAction(dev.DvrList));
+ controller.AddAction(prefix + "dvrList", new PressAndHoldAction(dev.DvrList));
controller.AddAction(prefix + "replay", new PressAndHoldAction(dev.Replay));
}
@@ -22,7 +22,7 @@ namespace PepperDash.Essentials.Room.Cotija
{
var prefix = string.Format(@"/device/{0}/", (dev as IKeyed).Key);
- controller.RemoveAction(prefix + "dvrlist");
+ controller.RemoveAction(prefix + "dvrList");
controller.RemoveAction(prefix + "replay");
}
}
diff --git a/PepperDashEssentials/Room/Cotija/DeviceTypeInterfaces/ITransportExtensions.cs b/PepperDashEssentials/AppServer/DeviceTypeInterfaces/ITransportExtensions.cs
similarity index 86%
rename from PepperDashEssentials/Room/Cotija/DeviceTypeInterfaces/ITransportExtensions.cs
rename to PepperDashEssentials/AppServer/DeviceTypeInterfaces/ITransportExtensions.cs
index 34bda457..9463d95f 100644
--- a/PepperDashEssentials/Room/Cotija/DeviceTypeInterfaces/ITransportExtensions.cs
+++ b/PepperDashEssentials/AppServer/DeviceTypeInterfaces/ITransportExtensions.cs
@@ -17,8 +17,8 @@ namespace PepperDash.Essentials.Room.Cotija
controller.AddAction(prefix + "play", new PressAndHoldAction(dev.Play));
controller.AddAction(prefix + "pause", new PressAndHoldAction(dev.Pause));
controller.AddAction(prefix + "stop", new PressAndHoldAction(dev.Stop));
- controller.AddAction(prefix + "prevtrack", new PressAndHoldAction(dev.ChapPlus));
- controller.AddAction(prefix + "nexttrack", new PressAndHoldAction(dev.ChapMinus));
+ controller.AddAction(prefix + "prevTrack", new PressAndHoldAction(dev.ChapPlus));
+ controller.AddAction(prefix + "nextTrack", new PressAndHoldAction(dev.ChapMinus));
controller.AddAction(prefix + "rewind", new PressAndHoldAction(dev.Rewind));
controller.AddAction(prefix + "ffwd", new PressAndHoldAction(dev.FFwd));
controller.AddAction(prefix + "record", new PressAndHoldAction(dev.Record));
@@ -31,8 +31,8 @@ namespace PepperDash.Essentials.Room.Cotija
controller.RemoveAction(prefix + "play");
controller.RemoveAction(prefix + "pause");
controller.RemoveAction(prefix + "stop");
- controller.RemoveAction(prefix + "prevtrack");
- controller.RemoveAction(prefix + "nexttrack");
+ controller.RemoveAction(prefix + "prevTrack");
+ controller.RemoveAction(prefix + "nextTrack");
controller.RemoveAction(prefix + "rewind");
controller.RemoveAction(prefix + "ffwd");
controller.RemoveAction(prefix + "record");
diff --git a/PepperDashEssentials/AppServer/Interfaces.cs b/PepperDashEssentials/AppServer/Interfaces.cs
new file mode 100644
index 00000000..df651ef8
--- /dev/null
+++ b/PepperDashEssentials/AppServer/Interfaces.cs
@@ -0,0 +1,60 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Crestron.SimplSharp;
+
+using PepperDash.Essentials.Core;
+
+namespace PepperDash.Essentials.Room.Cotija
+{
+ ///
+ /// Represents a room whose configuration is derived from runtime data,
+ /// perhaps from another program, and that the data may not be fully
+ /// available at startup.
+ ///
+ public interface IDelayedConfiguration
+ {
+ event EventHandler ConfigurationIsReady;
+ }
+}
+
+namespace PepperDash.Essentials
+{
+ ///
+ /// For rooms with a single presentation source, change event
+ ///
+ public interface IHasCurrentSourceInfoChange
+ {
+ string CurrentSourceInfoKey { get; }
+ SourceListItem CurrentSourceInfo { get; }
+ event SourceInfoChangeHandler CurrentSingleSourceChange;
+ }
+
+
+ ///
+ /// For rooms with routing
+ ///
+ public interface IRunRouteAction
+ {
+ void RunRouteAction(string routeKey);
+
+ void RunRouteAction(string routeKey, Action successCallback);
+ }
+
+ ///
+ /// For rooms that default presentation only routing
+ ///
+ public interface IRunDefaultPresentRoute
+ {
+ bool RunDefaultPresentRoute();
+ }
+
+ ///
+ /// For rooms that have default presentation and calling routes
+ ///
+ public interface IRunDefaultCallRoute : IRunDefaultPresentRoute
+ {
+ bool RunDefaultCallRoute();
+ }
+}
\ No newline at end of file
diff --git a/PepperDashEssentials/AppServer/Messengers/VideoCodecBaseMessenger.cs b/PepperDashEssentials/AppServer/Messengers/VideoCodecBaseMessenger.cs
new file mode 100644
index 00000000..67180b35
--- /dev/null
+++ b/PepperDashEssentials/AppServer/Messengers/VideoCodecBaseMessenger.cs
@@ -0,0 +1,259 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Crestron.SimplSharp;
+
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+
+using PepperDash.Essentials.Devices.Common.Codec;
+using PepperDash.Essentials.Devices.Common.VideoCodec;
+
+namespace PepperDash.Essentials.AppServer.Messengers
+{
+ ///
+ /// Provides a messaging bridge for a VideoCodecBase
+ ///
+ public class VideoCodecBaseMessenger
+ {
+ ///
+ ///
+ ///
+ public VideoCodecBase Codec { get; private set; }
+
+ public CotijaSystemController AppServerController { get; private set; }
+
+ public string MessagePath { get; private set; }
+
+ ///
+ ///
+ ///
+ ///
+ public VideoCodecBaseMessenger(VideoCodecBase codec, string messagePath)
+ {
+ if (codec == null)
+ throw new ArgumentNullException("codec");
+ if (string.IsNullOrEmpty(messagePath))
+ throw new ArgumentException("messagePath must not be empty or null");
+
+ MessagePath = messagePath;
+ Codec = codec;
+ codec.CallStatusChange += new EventHandler(codec_CallStatusChange);
+ codec.IsReadyChange += new EventHandler(codec_IsReadyChange);
+
+ var dirCodec = codec as IHasDirectory;
+ if (dirCodec != null)
+ {
+ dirCodec.DirectoryResultReturned += new EventHandler(dirCodec_DirectoryResultReturned);
+ }
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ void dirCodec_DirectoryResultReturned(object sender, DirectoryEventArgs e)
+ {
+ var dir = e.Directory;
+ PostStatusMessage(new
+ {
+ currentDirectory = e.Directory
+ });
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ void codec_IsReadyChange(object sender, EventArgs e)
+ {
+ PostStatusMessage(new
+ {
+ isReady = true
+ });
+ }
+
+ ///
+ /// Registers this codec's messaging with an app server controller
+ ///
+ ///
+ public void RegisterWithAppServer(CotijaSystemController appServerController)
+ {
+ if (appServerController == null)
+ throw new ArgumentNullException("appServerController");
+
+ AppServerController = appServerController;
+
+ appServerController.AddAction("/device/videoCodec/isReady", new Action(SendIsReady));
+ appServerController.AddAction("/device/videoCodec/fullStatus", new Action(SendVtcFullMessageObject));
+ appServerController.AddAction("/device/videoCodec/dial", new Action(s => Codec.Dial(s)));
+ appServerController.AddAction("/device/videoCodec/endCallById", new Action(s =>
+ {
+ var call = GetCallWithId(s);
+ if (call != null)
+ Codec.EndCall(call);
+ }));
+ appServerController.AddAction(MessagePath + "/endAllCalls", new Action(Codec.EndAllCalls));
+ appServerController.AddAction(MessagePath + "/dtmf", new Action(s => Codec.SendDtmf(s)));
+ appServerController.AddAction(MessagePath + "/rejectById", new Action(s =>
+ {
+ var call = GetCallWithId(s);
+ if (call != null)
+ Codec.RejectCall(call);
+ }));
+ appServerController.AddAction(MessagePath + "/acceptById", new Action(s =>
+ {
+ var call = GetCallWithId(s);
+ if (call != null)
+ Codec.AcceptCall(call);
+ }));
+ appServerController.AddAction(MessagePath + "/directoryRoot", new Action(GetDirectoryRoot));
+ appServerController.AddAction(MessagePath + "/directoryById", new Action(s => GetDirectory(s)));
+ appServerController.AddAction(MessagePath + "/directorySearch", new Action(s => DirectorySearch(s)));
+ appServerController.AddAction(MessagePath + "/privacyModeOn", new Action(Codec.PrivacyModeOn));
+ appServerController.AddAction(MessagePath + "/privacyModeOff", new Action(Codec.PrivacyModeOff));
+ appServerController.AddAction(MessagePath + "/privacyModeToggle", new Action(Codec.PrivacyModeToggle));
+ appServerController.AddAction(MessagePath + "/sharingStart", new Action(Codec.StartSharing));
+ appServerController.AddAction(MessagePath + "/sharingStop", new Action(Codec.StopSharing));
+ appServerController.AddAction(MessagePath + "/standbyOn", new Action(Codec.StandbyActivate));
+ appServerController.AddAction(MessagePath + "/standbyOff", new Action(Codec.StandbyDeactivate));
+ }
+
+ public void GetFullStatusMessage()
+ {
+
+ }
+
+ ///
+ /// Helper to grab a call with string ID
+ ///
+ ///
+ ///
+ CodecActiveCallItem GetCallWithId(string id)
+ {
+ return Codec.ActiveCalls.FirstOrDefault(c => c.Id == id);
+ }
+
+ ///
+ ///
+ ///
+ ///
+ void DirectorySearch(string s)
+ {
+ var dirCodec = Codec as IHasDirectory;
+ if (dirCodec != null)
+ {
+ dirCodec.SearchDirectory(s);
+ }
+ }
+
+ ///
+ ///
+ ///
+ ///
+ void GetDirectory(string id)
+ {
+ var dirCodec = Codec as IHasDirectory;
+ if(dirCodec == null)
+ {
+ return;
+ }
+ dirCodec.GetDirectoryFolderContents(id);
+ }
+
+ ///
+ ///
+ ///
+ void GetDirectoryRoot()
+ {
+ var dirCodec = Codec as IHasDirectory;
+ if (dirCodec == null)
+ {
+ // do something else?
+ return;
+ }
+ if (!dirCodec.PhonebookSyncState.InitialSyncComplete)
+ {
+ PostStatusMessage(new
+ {
+ initialSyncComplete = false
+ });
+ return;
+ }
+
+ PostStatusMessage(new
+ {
+ currentDirectory = dirCodec.DirectoryRoot
+ });
+ }
+
+ ///
+ /// Handler for codec changes
+ ///
+ void codec_CallStatusChange(object sender, CodecCallStatusItemChangeEventArgs e)
+ {
+ SendVtcFullMessageObject();
+ }
+
+ ///
+ ///
+ ///
+ void SendIsReady()
+ {
+ PostStatusMessage(new
+ {
+ isReady = Codec.IsReady
+ });
+ }
+
+ ///
+ /// Helper method to build call status for vtc
+ ///
+ ///
+ void SendVtcFullMessageObject()
+ {
+ if (!Codec.IsReady)
+ {
+ return;
+ }
+
+ var info = Codec.CodecInfo;
+ PostStatusMessage(new
+ {
+ isInCall = Codec.IsInCall,
+ privacyModeIsOn = Codec.PrivacyModeIsOnFeedback.BoolValue,
+ sharingContentIsOn = Codec.SharingContentIsOnFeedback.BoolValue,
+ sharingSource = Codec.SharingSourceFeedback.StringValue,
+ standbyIsOn = Codec.StandbyIsOnFeedback.StringValue,
+ calls = Codec.ActiveCalls,
+ info = new
+ {
+ autoAnswerEnabled = info.AutoAnswerEnabled,
+ e164Alias = info.E164Alias,
+ h323Id = info.H323Id,
+ ipAddress = info.IpAddress,
+ sipPhoneNumber = info.SipPhoneNumber,
+ sipURI = info.SipUri
+ },
+ showSelfViewByDefault = Codec.ShowSelfViewByDefault,
+ hasDirectory = Codec is IHasDirectory
+ });
+ }
+
+ ///
+ /// Helper for posting status message
+ ///
+ /// The contents of the content object
+ void PostStatusMessage(object contentObject)
+ {
+ AppServerController.SendMessageToServer(JObject.FromObject(new
+ {
+ type = MessagePath,
+ content = contentObject
+ }));
+ }
+ }
+}
\ No newline at end of file
diff --git a/PepperDashEssentials/Room/Cotija/RoomBridges/CotijaBridgeBase.cs b/PepperDashEssentials/AppServer/RoomBridges/CotijaBridgeBase.cs
similarity index 100%
rename from PepperDashEssentials/Room/Cotija/RoomBridges/CotijaBridgeBase.cs
rename to PepperDashEssentials/AppServer/RoomBridges/CotijaBridgeBase.cs
diff --git a/PepperDashEssentials/Room/Cotija/RoomBridges/CotijaDdvc01RoomBridge.cs b/PepperDashEssentials/AppServer/RoomBridges/CotijaDdvc01RoomBridge.cs
similarity index 81%
rename from PepperDashEssentials/Room/Cotija/RoomBridges/CotijaDdvc01RoomBridge.cs
rename to PepperDashEssentials/AppServer/RoomBridges/CotijaDdvc01RoomBridge.cs
index 48bd33fa..2ccb03d9 100644
--- a/PepperDashEssentials/Room/Cotija/RoomBridges/CotijaDdvc01RoomBridge.cs
+++ b/PepperDashEssentials/AppServer/RoomBridges/CotijaDdvc01RoomBridge.cs
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
+using Crestron.SimplSharp.Reflection;
using Crestron.SimplSharpPro.EthernetCommunication;
using Newtonsoft.Json;
@@ -235,13 +236,12 @@ namespace PepperDash.Essentials.Room.Cotija
EISC.PulseBool(BoolJoin.SourceHasChanged);
}));
-#warning CHANGE to activityshare. Perhaps
Parent.AddAction(@"/room/room1/defaultsource", new Action(() =>
EISC.PulseBool(BoolJoin.ActivitySharePress)));
- Parent.AddAction(@"/room/room1/masterVolumeLevel", new Action(u =>
+ Parent.AddAction(@"/room/room1/volumes/master/level", new Action(u =>
EISC.SetUshort(UshortJoin.MasterVolumeLevel, u)));
- Parent.AddAction(@"/room/room1/masterVolumeMuteToggle", new Action(() =>
+ Parent.AddAction(@"/room/room1/volumes/master/muteToggle", new Action(() =>
EISC.PulseBool(BoolJoin.MasterVolumeIsMuted)));
Parent.AddAction(@"/room/room1/shutdownStart", new Action(() =>
@@ -250,6 +250,18 @@ namespace PepperDash.Essentials.Room.Cotija
EISC.PulseBool(BoolJoin.ShutdownEnd)));
Parent.AddAction(@"/room/room1/shutdownCancel", new Action(() =>
EISC.PulseBool(BoolJoin.ShutdownCancel)));
+
+
+ // Source Device (Current Source)'
+
+ SourceDeviceMapDictionary sourceJoinMap = new SourceDeviceMapDictionary();
+
+ var prefix = @"/device/currentSource/";
+
+ foreach (var item in sourceJoinMap)
+ {
+ Parent.AddAction(string.Format("{0}{1}", prefix, item.Key), new PressAndHoldAction(b => EISC.SetBool(item.Value, b)));
+ }
}
///
@@ -273,16 +285,29 @@ namespace PepperDash.Essentials.Room.Cotija
// Volume things
EISC.SetUShortSigAction(UshortJoin.MasterVolumeLevel, u =>
- PostStatusMessage(new
- {
- masterVolumeLevel = u
- }));
+ PostStatusMessage(new
+ {
+ volumes = new
+ {
+ master = new
+ {
+ level = u
+ }
+ }
+ }));
EISC.SetBoolSigAction(BoolJoin.MasterVolumeIsMuted, b =>
- PostStatusMessage(new
- {
- masterVolumeMuteState = b
- }));
+ PostStatusMessage(new
+ {
+ volumes = new
+ {
+ master = new
+ {
+ muted = b
+ }
+ }
+ }));
+
// shutdown things
EISC.SetSigTrueAction(BoolJoin.ShutdownCancel, new Action(() =>
@@ -316,15 +341,25 @@ namespace PepperDash.Essentials.Room.Cotija
var co = ConfigReader.ConfigObject;
+ co.Info.RuntimeInfo.AppName = Assembly.GetExecutingAssembly().GetName().Name;
+ var version = Assembly.GetExecutingAssembly().GetName().Version;
+ co.Info.RuntimeInfo.AssemblyVersion = string.Format("{0}.{1}.{2}", version.Major, version.Minor, version.Build);
+
+
//Room
if (co.Rooms == null)
- co.Rooms = new List();
- var rm = new EssentialsRoomConfig();
- if (co.Rooms.Count == 0)
- {
- Debug.Console(0, this, "Adding room to config");
- co.Rooms.Add(rm);
- }
+ co.Rooms = new List();
+ var rm = new DeviceConfig();
+ if (co.Rooms.Count == 0)
+ {
+ Debug.Console(0, this, "Adding room to config");
+ co.Rooms.Add(rm);
+ }
+ else
+ {
+ Debug.Console(0, this, "Replacing Room[0] in config");
+ co.Rooms[0] = rm;
+ }
rm.Name = EISC.StringOutput[501].StringValue;
rm.Key = "room1";
rm.Type = "ddvc01";
@@ -356,12 +391,12 @@ namespace PepperDash.Essentials.Room.Cotija
// volume control names
var volCount = EISC.UShortOutput[701].UShortValue;
- // use Volumes object or?
- rmProps.VolumeSliderNames = new List();
- for(uint i = 701; i <= 700 + volCount; i++)
- {
- rmProps.VolumeSliderNames.Add(EISC.StringInput[i].StringValue);
- }
+ //// use Volumes object or?
+ //rmProps.VolumeSliderNames = new List();
+ //for(uint i = 701; i <= 700 + volCount; i++)
+ //{
+ // rmProps.VolumeSliderNames.Add(EISC.StringInput[i].StringValue);
+ //}
// There should be cotija devices in here, I think...
if(co.Devices == null)
@@ -397,7 +432,7 @@ namespace PepperDash.Essentials.Room.Cotija
SourceKey = key,
};
newSl.Add(key, newSLI);
-
+
string group = "genericsource";
if (groupMap.ContainsKey(type))
{
@@ -431,13 +466,43 @@ namespace PepperDash.Essentials.Room.Cotija
{
if (ConfigIsLoaded)
{
- PostStatusMessage(new
- {
- isOn = EISC.BooleanOutput[BoolJoin.RoomIsOn].BoolValue,
- selectedSourceKey = EISC.StringOutput[StringJoin.SelectedSourceKey].StringValue,
- masterVolumeLevel = EISC.UShortOutput[UshortJoin.MasterVolumeLevel].UShortValue,
- masterVolumeMuteState = EISC.BooleanOutput[BoolJoin.MasterVolumeIsMuted].BoolValue
- });
+ var count = EISC.UShortOutput[801].UShortValue;
+
+ Debug.Console(1, this, "The Fader Count is : {0}", count);
+
+ // build volumes object, serialize and put in content of method below
+
+ var auxFaders = new List();
+
+ // Create auxFaders
+ for (uint i = 2; i <= count; i++)
+ {
+ auxFaders.Add(
+ new Volume(string.Format("level-{0}", i),
+ EISC.UShortOutput[i].UShortValue,
+ EISC.BooleanOutput[i].BoolValue,
+ EISC.StringOutput[800 + i].StringValue,
+ true,
+ "someting.png"));
+ }
+
+ var volumes = new Volumes();
+
+ volumes.Master = new Volume("master",
+ EISC.UShortOutput[UshortJoin.MasterVolumeLevel].UShortValue,
+ EISC.BooleanOutput[BoolJoin.MasterVolumeIsMuted].BoolValue,
+ EISC.StringOutput[801].StringValue,
+ true,
+ "something.png");
+
+ volumes.AuxFaders = auxFaders;
+
+ PostStatusMessage(new
+ {
+ isOn = EISC.BooleanOutput[BoolJoin.RoomIsOn].BoolValue,
+ selectedSourceKey = EISC.StringOutput[StringJoin.SelectedSourceKey].StringValue,
+ volumes = volumes
+ });
}
else
{
@@ -448,8 +513,6 @@ namespace PepperDash.Essentials.Room.Cotija
}
}
-
-
///
/// Helper for posting status message
///
diff --git a/PepperDashEssentials/Room/Cotija/RoomBridges/CotijaEssentialsHuddleSpaceRoomBridge.cs b/PepperDashEssentials/AppServer/RoomBridges/CotijaEssentialsHuddleSpaceRoomBridge.cs
similarity index 51%
rename from PepperDashEssentials/Room/Cotija/RoomBridges/CotijaEssentialsHuddleSpaceRoomBridge.cs
rename to PepperDashEssentials/AppServer/RoomBridges/CotijaEssentialsHuddleSpaceRoomBridge.cs
index 2f3cf440..24f2340a 100644
--- a/PepperDashEssentials/Room/Cotija/RoomBridges/CotijaEssentialsHuddleSpaceRoomBridge.cs
+++ b/PepperDashEssentials/AppServer/RoomBridges/CotijaEssentialsHuddleSpaceRoomBridge.cs
@@ -5,15 +5,22 @@ using System.Text;
using Crestron.SimplSharp;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
+
+using PepperDash.Core;
+using PepperDash.Essentials.AppServer.Messengers;
using PepperDash.Essentials.Core;
using PepperDash.Essentials.Room.Cotija;
+using PepperDash.Essentials.Devices.Common.Codec;
+using PepperDash.Essentials.Devices.Common.VideoCodec;
namespace PepperDash.Essentials
{
public class CotijaEssentialsHuddleSpaceRoomBridge : CotijaBridgeBase
{
- public EssentialsHuddleSpaceRoom Room { get; private set; }
+ public EssentialsRoomBase Room { get; private set; }
+
+ public VideoCodecBaseMessenger VCMessenger { get; private set; }
///
///
@@ -31,7 +38,7 @@ namespace PepperDash.Essentials
///
///
///
- public CotijaEssentialsHuddleSpaceRoomBridge(EssentialsHuddleSpaceRoom room):
+ public CotijaEssentialsHuddleSpaceRoomBridge(EssentialsRoomBase room):
base("mobileControlBridge-essentialsHuddle", "Essentials Mobile Control Bridge-Huddle")
{
Room = room;
@@ -51,22 +58,73 @@ namespace PepperDash.Essentials
// doesn't need to know about everything.
// Source Changes and room off
- Parent.AddAction(string.Format(@"/room/{0}/status", Room.Key), new Action(() => Room_RoomFullStatus(Room)));
- Parent.AddAction(string.Format(@"/room/{0}/source", Room.Key), new Action(c => Room.RunRouteAction(c.SourceListItem)));
- Parent.AddAction(string.Format(@"/room/{0}/defaultsource", Room.Key), new Action(Room.RunDefaultRoute));
+ Parent.AddAction(string.Format(@"/room/{0}/status", Room.Key), new Action(() => SendFullStatus(Room)));
- Parent.AddAction(string.Format(@"/room/{0}/masterVolumeLevel", Room.Key), new Action(u =>
- (Room.CurrentVolumeControls as IBasicVolumeWithFeedback).SetVolume(u)));
- Parent.AddAction(string.Format(@"/room/{0}/masterVolumeMuteToggle", Room.Key), new Action(() => Room.CurrentVolumeControls.MuteToggle()));
+ var routeRoom = Room as IRunRouteAction;
+ if(routeRoom != null)
+ Parent.AddAction(string.Format(@"/room/{0}/source", Room.Key), new Action(c =>
+ routeRoom.RunRouteAction(c.SourceListItem)));
+
+ var defaultRoom = Room as IRunDefaultPresentRoute;
+ if(defaultRoom != null)
+ Parent.AddAction(string.Format(@"/room/{0}/defaultsource", Room.Key), new Action(() => defaultRoom.RunDefaultPresentRoute()));
+
+ var volumeRoom = Room as IHasCurrentVolumeControls;
+ if (volumeRoom != null)
+ {
+ Parent.AddAction(string.Format(@"/room/{0}/volumes/master/level", Room.Key), new Action(u =>
+ (volumeRoom.CurrentVolumeControls as IBasicVolumeWithFeedback).SetVolume(u)));
+ Parent.AddAction(string.Format(@"/room/{0}/volumes/master/mute", Room.Key), new Action(() =>
+ volumeRoom.CurrentVolumeControls.MuteToggle()));
+ volumeRoom.CurrentVolumeDeviceChange += new EventHandler(Room_CurrentVolumeDeviceChange);
+
+ // Registers for initial volume events, if possible
+ var currentVolumeDevice = volumeRoom.CurrentVolumeControls as IBasicVolumeWithFeedback;
+ if (currentVolumeDevice != null)
+ {
+ currentVolumeDevice.MuteFeedback.OutputChange += VolumeLevelFeedback_OutputChange;
+ currentVolumeDevice.VolumeLevelFeedback.OutputChange += VolumeLevelFeedback_OutputChange;
+ }
+ }
+
+ var sscRoom = Room as IHasCurrentSourceInfoChange;
+ if(sscRoom != null)
+ sscRoom.CurrentSingleSourceChange += new SourceInfoChangeHandler(Room_CurrentSingleSourceChange);
+
+ var vcRoom = Room as IHasVideoCodec;
+ if (vcRoom != null)
+ {
+ var codec = vcRoom.VideoCodec;
+ VCMessenger = new VideoCodecBaseMessenger(vcRoom.VideoCodec, "/device/videoCodec");
+ VCMessenger.RegisterWithAppServer(Parent);
+
+ // May need to move this or remove this
+ codec.CallStatusChange += new EventHandler(codec_CallStatusChange);
+
+ vcRoom.IsSharingFeedback.OutputChange += new EventHandler(IsSharingFeedback_OutputChange);
+
+ //Parent.AddAction("/device/videoCodec/dial", new Action(s => codec.Dial(s)));
+ //Parent.AddAction("/device/videoCodec/endCall", new Action(s =>
+ //{
+ // var call = codec.ActiveCalls.FirstOrDefault(c => c.Id == s);
+ // if (call != null)
+ // {
+ // codec.EndCall(call);
+ // }
+ //}));
+ //Parent.AddAction("/device/videoCodec/endAllCalls", new Action(() => codec.EndAllCalls()));
+ }
+
+ var defCallRm = Room as IRunDefaultCallRoute;
+ if (defCallRm != null)
+ {
+ Parent.AddAction(string.Format(@"/room/{0}/activityVideo", Room.Key), new Action(()=>defCallRm.RunDefaultCallRoute()));
+ }
Parent.AddAction(string.Format(@"/room/{0}/shutdownStart", Room.Key), new Action(() => Room.StartShutdown(eShutdownType.Manual)));
Parent.AddAction(string.Format(@"/room/{0}/shutdownEnd", Room.Key), new Action(() => Room.ShutdownPromptTimer.Finish()));
Parent.AddAction(string.Format(@"/room/{0}/shutdownCancel", Room.Key), new Action(() => Room.ShutdownPromptTimer.Cancel()));
- Room.CurrentSingleSourceChange += new SourceInfoChangeHandler(Room_CurrentSingleSourceChange);
-
- Room.CurrentVolumeDeviceChange += new EventHandler(Room_CurrentVolumeDeviceChange);
-
Room.OnFeedback.OutputChange += OnFeedback_OutputChange;
Room.IsCoolingDownFeedback.OutputChange += IsCoolingDownFeedback_OutputChange;
Room.IsWarmingUpFeedback.OutputChange += IsWarmingUpFeedback_OutputChange;
@@ -74,20 +132,67 @@ namespace PepperDash.Essentials
Room.ShutdownPromptTimer.HasStarted += ShutdownPromptTimer_HasStarted;
Room.ShutdownPromptTimer.HasFinished += ShutdownPromptTimer_HasFinished;
Room.ShutdownPromptTimer.WasCancelled += ShutdownPromptTimer_WasCancelled;
+ }
- // Registers for initial volume events, if possible
- var currentVolumeDevice = Room.CurrentVolumeControls;
-
- if (currentVolumeDevice != null)
+ ///
+ ///
+ ///
+ ///
+ ///
+ void IsSharingFeedback_OutputChange(object sender, FeedbackEventArgs e)
+ {
+ // sharing source
+ string shareText;
+ bool isSharing;
+#warning This share update needs to happen on source change as well!
+ var vcRoom = Room as IHasVideoCodec;
+ var srcInfoRoom = Room as IHasCurrentSourceInfoChange;
+ if (vcRoom.VideoCodec.SharingContentIsOnFeedback.BoolValue && srcInfoRoom.CurrentSourceInfo != null)
{
- if (currentVolumeDevice is IBasicVolumeWithFeedback)
- {
- var newDev = currentVolumeDevice as IBasicVolumeWithFeedback;
- newDev.MuteFeedback.OutputChange += VolumeLevelFeedback_OutputChange;
- newDev.VolumeLevelFeedback.OutputChange += VolumeLevelFeedback_OutputChange;
- }
+ shareText = srcInfoRoom.CurrentSourceInfo.PreferredName;
+ isSharing = true;
}
+ else
+ {
+ shareText = "None";
+ isSharing = false;
+ }
+
+ PostStatusMessage(new
+ {
+ share = new
+ {
+ currentShareText = shareText,
+ isSharing = isSharing
+ }
+ });
+ }
+
+ ///
+ /// Handler for codec changes
+ ///
+ void codec_CallStatusChange(object sender, CodecCallStatusItemChangeEventArgs e)
+ {
+ PostStatusMessage(new
+ {
+ calls = GetCallsMessageObject(),
+ //vtc = GetVtcCallsMessageObject()
+ });
+
+ }
+
+ ///
+ /// Helper for posting status message
+ ///
+ /// The contents of the content object
+ void PostStatusMessage(object contentObject)
+ {
+ Parent.SendMessageToServer(JObject.FromObject(new
+ {
+ type = "/room/status/",
+ content = contentObject
+ }));
}
///
@@ -143,14 +248,12 @@ namespace PepperDash.Essentials
///
///
///
- void IsWarmingUpFeedback_OutputChange(object sender, EventArgs e)
+ void IsWarmingUpFeedback_OutputChange(object sender, FeedbackEventArgs e)
{
- JObject roomStatus = new JObject();
- roomStatus.Add("isWarmingUp", (sender as BoolFeedback).BoolValue);
- JObject message = new JObject();
- message.Add("type", "/room/status/");
- message.Add("content", roomStatus);
- Parent.SendMessageToServer(message);
+ PostStatusMessage(new
+ {
+ isWarmingUp = e.BoolValue
+ });
}
///
@@ -158,14 +261,12 @@ namespace PepperDash.Essentials
///
///
///
- void IsCoolingDownFeedback_OutputChange(object sender, EventArgs e)
+ void IsCoolingDownFeedback_OutputChange(object sender, FeedbackEventArgs e)
{
- JObject roomStatus = new JObject();
- roomStatus.Add("isCoolingDown", (sender as BoolFeedback).BoolValue);
- JObject message = new JObject();
- message.Add("type", "/room/status/");
- message.Add("content", roomStatus);
- Parent.SendMessageToServer(message);
+ PostStatusMessage(new
+ {
+ isCoolingDown = e.BoolValue
+ });
}
///
@@ -173,27 +274,12 @@ namespace PepperDash.Essentials
///
///
///
- void OnFeedback_OutputChange(object sender, EventArgs e)
+ void OnFeedback_OutputChange(object sender, FeedbackEventArgs e)
{
- /* Example message
- * {
- "type":"/room/status",
- "content": {
- "isOn": false
- }
- }
- */
-
- JObject roomStatus = new JObject();
-
- roomStatus.Add("isOn", (sender as BoolFeedback).BoolValue);
-
- JObject message = new JObject();
-
- message.Add("type", "/room/status/");
- message.Add("content", roomStatus);
-
- Parent.SendMessageToServer(message);
+ PostStatusMessage(new
+ {
+ isOn = e.BoolValue
+ });
}
void Room_CurrentVolumeDeviceChange(object sender, VolumeDeviceChangeEventArgs e)
@@ -201,54 +287,53 @@ namespace PepperDash.Essentials
if (e.OldDev is IBasicVolumeWithFeedback)
{
var oldDev = e.OldDev as IBasicVolumeWithFeedback;
-
- oldDev.MuteFeedback.OutputChange -= VolumeLevelFeedback_OutputChange;
+ oldDev.MuteFeedback.OutputChange -= MuteFeedback_OutputChange;
oldDev.VolumeLevelFeedback.OutputChange -= VolumeLevelFeedback_OutputChange;
}
if (e.NewDev is IBasicVolumeWithFeedback)
{
var newDev = e.NewDev as IBasicVolumeWithFeedback;
-
- newDev.MuteFeedback.OutputChange += VolumeLevelFeedback_OutputChange;
+ newDev.MuteFeedback.OutputChange += MuteFeedback_OutputChange;
newDev.VolumeLevelFeedback.OutputChange += VolumeLevelFeedback_OutputChange;
}
}
- void VolumeLevelFeedback_OutputChange(object sender, EventArgs e)
+ ///
+ /// Event handler for mute changes
+ ///
+ void MuteFeedback_OutputChange(object sender, FeedbackEventArgs e)
+ {
+ PostStatusMessage(new
+ {
+ volumes = new
+ {
+ master = new
+ {
+ muted = e.BoolValue
+ }
+ }
+ });
+ }
+
+ ///
+ /// Handles Volume changes on room
+ ///
+ void VolumeLevelFeedback_OutputChange(object sender, FeedbackEventArgs e)
{
- /* Example message
- * {
- "type":"/room/status",
- "content": {
- "masterVolumeLevel": 12345,
- "masterVolumeMuteState": false
- }
- }
- */
-
- var huddleRoom = Room as EssentialsHuddleSpaceRoom;
-
- if(huddleRoom.CurrentVolumeControls is IBasicVolumeWithFeedback)
- {
- JObject roomStatus = new JObject();
-
- if (huddleRoom.CurrentVolumeControls is IBasicVolumeWithFeedback)
- {
- var currentVolumeConstrols = huddleRoom.CurrentVolumeControls as IBasicVolumeWithFeedback;
- roomStatus.Add("masterVolumeLevel", currentVolumeConstrols.VolumeLevelFeedback.IntValue);
- roomStatus.Add("masterVolumeMuteState", currentVolumeConstrols.MuteFeedback.BoolValue);
- }
-
- JObject message = new JObject();
-
- message.Add("type", "/room/status/");
- message.Add("content", roomStatus);
-
- Parent.SendMessageToServer(message);
- }
+ PostStatusMessage(new
+ {
+ volumes = new
+ {
+ master = new
+ {
+ level = e.IntValue
+ }
+ }
+ });
}
+
void Room_CurrentSingleSourceChange(EssentialsRoomBase room, PepperDash.Essentials.Core.SourceListItem info, ChangeType type)
{
/* Example message
@@ -310,16 +395,11 @@ namespace PepperDash.Essentials
if (dev is ITransport)
(dev as ITransport).LinkActions(Parent);
- var huddleRoom = room as EssentialsHuddleSpaceRoom;
- JObject roomStatus = new JObject();
- roomStatus.Add("selectedSourceKey", huddleRoom.CurrentSourceInfoKey);
-
- JObject message = new JObject();
-
- message.Add("type", "/room/status/");
- message.Add("content", roomStatus);
-
- Parent.SendMessageToServer(message);
+ var srcRm = room as IHasCurrentSourceInfoChange;
+ PostStatusMessage(new
+ {
+ selectedSourceKey = srcRm.CurrentSourceInfoKey
+ });
}
}
}
@@ -328,52 +408,84 @@ namespace PepperDash.Essentials
/// Posts the full status of the room to the server
///
///
- void Room_RoomFullStatus(EssentialsRoomBase room)
+ void SendFullStatus(EssentialsRoomBase room)
{
- /* Example message
- * {
- "type":"/room/status",
- "content": {
- "selectedSourceKey": "off",
- "isOn": false,
- "masterVolumeLevel": 50,
- "masterVolumeMuteState": false
- }
- }
- */
-
- JObject roomStatus = new JObject();
-
- var huddleRoom = room as EssentialsHuddleSpaceRoom;
- roomStatus.Add("isOn", huddleRoom.OnFeedback.BoolValue);
- roomStatus.Add("selectedSourceKey", huddleRoom.CurrentSourceInfoKey);
-
-
- if(huddleRoom.CurrentVolumeControls is IBasicVolumeWithFeedback)
- {
- var currentVolumeConstrols = huddleRoom.CurrentVolumeControls as IBasicVolumeWithFeedback;
- roomStatus.Add("masterVolumeLevel", currentVolumeConstrols.VolumeLevelFeedback.IntValue);
- roomStatus.Add("masterVolumeMuteState", currentVolumeConstrols.MuteFeedback.BoolValue);
- }
-
- JObject message = new JObject();
-
- message.Add("type", "/room/status/");
- message.Add("content", roomStatus);
-
- Parent.SendMessageToServer(message);
+ var sourceKey = room is IHasCurrentSourceInfoChange ? (room as IHasCurrentSourceInfoChange).CurrentSourceInfoKey : null;
+
+ var rmVc = room as IHasCurrentVolumeControls;
+ var volumes = new Volumes();
+ if (rmVc != null)
+ {
+ var vc = rmVc.CurrentVolumeControls as IBasicVolumeWithFeedback;
+ if (rmVc != null)
+ {
+ volumes.Master = new Volume("master", vc.VolumeLevelFeedback.UShortValue, vc.MuteFeedback.BoolValue, "Volume", true, "");
+ }
+ }
+ PostStatusMessage(new
+ {
+ calls = GetCallsMessageObject(),
+ isOn = room.OnFeedback.BoolValue,
+ selectedSourceKey = sourceKey,
+ vtc = GetVtcCallsMessageObject(),
+ volumes = volumes
+ });
}
+
+ ///
+ /// Helper to return a anonymous object with the call data for JSON message
+ ///
+ ///
+ object GetCallsMessageObject()
+ {
+ var callRm = Room as IHasVideoCodec;
+ if (callRm == null)
+ return null;
+ return new
+ {
+ activeCalls = callRm.VideoCodec.ActiveCalls,
+ callType = callRm.CallTypeFeedback.IntValue,
+ inCall = callRm.InCallFeedback.BoolValue,
+ isSharing = callRm.IsSharingFeedback.BoolValue,
+ privacyModeIsOn = callRm.PrivacyModeIsOnFeedback.BoolValue
+ };
+ }
+
+ ///
+ /// Helper method to build call status for vtc
+ ///
+ ///
+ object GetVtcCallsMessageObject()
+ {
+ var callRm = Room as IHasVideoCodec;
+ object vtc = null;
+ if (callRm != null)
+ {
+ var codec = callRm.VideoCodec;
+ vtc = new
+ {
+ isInCall = codec.IsInCall,
+ calls = codec.ActiveCalls
+ };
+ }
+ return vtc;
+ }
}
+ ///
+ ///
+ ///
public class SourceSelectMessageContent
{
public string SourceListItem { get; set; }
- //public string Destination { get; set; }
- //public string SourceSelect { get; set; }
}
+ ///
+ ///
+ ///
+ ///
public delegate void PressAndHoldAction(bool b);
}
\ No newline at end of file
diff --git a/PepperDashEssentials/AppServer/RoomBridges/SourceDeviceMapDictionary.cs b/PepperDashEssentials/AppServer/RoomBridges/SourceDeviceMapDictionary.cs
new file mode 100644
index 00000000..976c4121
--- /dev/null
+++ b/PepperDashEssentials/AppServer/RoomBridges/SourceDeviceMapDictionary.cs
@@ -0,0 +1,104 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Crestron.SimplSharp;
+
+namespace PepperDash.Essentials.Room.Cotija
+{
+
+ ///
+ /// Contains all of the default joins that map to API funtions
+ ///
+ public class SourceDeviceMapDictionary : Dictionary
+ {
+
+ public SourceDeviceMapDictionary(): base()
+ {
+ var dictionary = new Dictionary
+ {
+ {"preset01", 101},
+ {"preset02", 102},
+ {"preset03", 103},
+ {"preset04", 104},
+ {"preset05", 105},
+ {"preset06", 106},
+ {"preset07", 107},
+ {"preset08", 108},
+ {"preset09", 109},
+ {"preset10", 110},
+ {"preset11", 111},
+ {"preset12", 112},
+ {"preset13", 113},
+ {"preset14", 114},
+ {"preset15", 115},
+ {"preset16", 116},
+ {"preset17", 117},
+ {"preset18", 118},
+ {"preset19", 119},
+ {"preset20", 120},
+ {"preset21", 121},
+ {"preset22", 122},
+ {"preset23", 123},
+ {"preset24", 124},
+
+ {"num0", 130},
+ {"num1", 131},
+ {"num2", 132},
+ {"num3", 133},
+ {"num4", 134},
+ {"num5", 135},
+ {"num6", 136},
+ {"num7", 137},
+ {"num8", 138},
+ {"num9", 139},
+ {"numDash", 140},
+ {"numEnter", 141},
+ {"chanUp", 142},
+ {"chanDown", 143},
+ {"lastChan", 144},
+ {"exit", 145},
+ {"powerToggle", 146},
+ {"red", 147},
+ {"green", 148},
+ {"yellow", 149},
+ {"blue", 150},
+ {"video", 151},
+ {"previous", 152},
+ {"next", 153},
+ {"rewind", 154},
+ {"ffwd", 155},
+ {"closedCaption", 156},
+ {"stop", 157},
+ {"pause", 158},
+ {"up", 159},
+ {"down", 160},
+ {"left", 161},
+ {"right", 162},
+ {"settings", 163},
+ {"info", 164},
+ {"return", 165},
+ {"guide", 166},
+ {"reboot", 167},
+ {"dvrList", 168},
+ {"replay", 169},
+ {"play", 170},
+ {"select", 171},
+ {"record", 172},
+ {"menu", 173},
+ {"topMenu", 174},
+ {"prevTrack", 175},
+ {"nextTrack", 176},
+ {"powerOn", 177},
+ {"powerOff", 178},
+ {"dot", 179}
+
+ };
+
+ foreach (var item in dictionary)
+ {
+ this.Add(item.Key, item.Value);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/PepperDashEssentials/AppServer/Volumes.cs b/PepperDashEssentials/AppServer/Volumes.cs
new file mode 100644
index 00000000..38de8ef2
--- /dev/null
+++ b/PepperDashEssentials/AppServer/Volumes.cs
@@ -0,0 +1,55 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Crestron.SimplSharp;
+
+using Newtonsoft.Json;
+
+namespace PepperDash.Essentials.Room.Cotija
+{
+ public class Volumes
+ {
+ [JsonProperty("master")]
+ public Volume Master { get; set; }
+
+ [JsonProperty("auxFaders")]
+ public List AuxFaders { get; set; }
+
+ public Volumes()
+ {
+ AuxFaders = new List();
+ }
+ }
+
+ public class Volume
+ {
+ [JsonProperty("key")]
+ public string Key { get; set; }
+
+ [JsonProperty("level")]
+ public ushort Level { get; set; }
+
+ [JsonProperty("muted")]
+ public bool Muted { get; set; }
+
+ [JsonProperty("label")]
+ public string Label { get; set; }
+
+ [JsonProperty("hasMute")]
+ public bool HasMute { get; set; }
+
+ [JsonProperty("muteIcon")]
+ public string MuteIcon { get; set; }
+
+ public Volume(string key, ushort level, bool muted, string label, bool hasMute, string muteIcon)
+ {
+ Key = key;
+ Level = level;
+ Muted = muted;
+ Label = label;
+ HasMute = hasMute;
+ MuteIcon = muteIcon;
+ }
+ }
+}
\ No newline at end of file
diff --git a/PepperDashEssentials/Bridges/BridgeBase.cs b/PepperDashEssentials/Bridges/BridgeBase.cs
new file mode 100644
index 00000000..cf2d2f27
--- /dev/null
+++ b/PepperDashEssentials/Bridges/BridgeBase.cs
@@ -0,0 +1,214 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Crestron.SimplSharp;
+using Crestron.SimplSharpPro.EthernetCommunication;
+
+using PepperDash.Core;
+using PepperDash.Essentials.Core;
+using PepperDash.Essentials.Core.Devices;
+
+namespace PepperDash.Essentials.Bridges
+{
+ ///
+ /// Base class for all bridge class variants
+ ///
+ public class BridgeBase : Device
+ {
+ public BridgeApi Api { get; private set; }
+
+ public BridgeBase(string key) :
+ base(key)
+ {
+
+ }
+
+ }
+
+ ///
+ /// Base class for bridge API variants
+ ///
+ public abstract class BridgeApi : Device
+ {
+ public BridgeApi(string key) :
+ base(key)
+ {
+
+ }
+
+ }
+
+ ///
+ /// Bridge API using EISC
+ ///
+ public class EiscApi : BridgeApi
+ {
+ public ThreeSeriesTcpIpEthernetIntersystemCommunications Eisc { get; private set; }
+
+
+ public EiscApi(string key, uint ipid, string hostname) :
+ base(key)
+ {
+ Eisc = new ThreeSeriesTcpIpEthernetIntersystemCommunications(ipid, hostname, Global.ControlSystem);
+
+ Eisc.SigChange += new Crestron.SimplSharpPro.DeviceSupport.SigEventHandler(Eisc_SigChange);
+
+ Eisc.Register();
+ }
+
+ ///
+ /// Handles incoming sig changes
+ ///
+ ///
+ ///
+ void Eisc_SigChange(object currentDevice, Crestron.SimplSharpPro.SigEventArgs args)
+ {
+ if (Debug.Level >= 1)
+ Debug.Console(1, this, "BridgeApi EISC change: {0} {1}={2}", args.Sig.Type, args.Sig.Number, args.Sig.StringValue);
+ var uo = args.Sig.UserObject;
+ if (uo is Action)
+ (uo as Action)(args.Sig.BoolValue);
+ else if (uo is Action)
+ (uo as Action)(args.Sig.UShortValue);
+ else if (uo is Action)
+ (uo as Action)(args.Sig.StringValue);
+ }
+ }
+
+
+
+
+ ///
+ /// Defines each type and it's matching API type
+ ///
+ public static class DeviceApiFactory
+ {
+ public static Dictionary TypeMap = new Dictionary
+ {
+ { typeof(DmChassisController), typeof(DmChassisControllerApi) },
+ { typeof(IBasicCommunication), typeof(IBasicCommunicationApi) }
+ //{ typeof(SomeShittyDisplayController), typeof(SomeShittyDisplayControllerApi) }
+ };
+ }
+
+
+ ///
+ /// API class for IBasicCommunication devices
+ ///
+ public class IBasicCommunicationApi : DeviceApiBase
+ {
+ public IBasicCommunication Device { get; set; }
+
+ SerialFeedback TextReceivedFeedback;
+
+ public IBasicCommunicationApi(IBasicCommunication dev)
+ {
+ TextReceivedFeedback = new SerialFeedback();
+
+ Device = dev;
+
+ SetupFeedbacks();
+
+ ActionApi = new Dictionary
+ {
+ { "connect", new Action(Device.Connect) },
+ { "disconnect", new Action(Device.Disconnect) },
+ { "connectstate", new Action( b => ConnectByState(b) ) },
+ { "sendtext", new Action( s => Device.SendText(s) ) }
+
+ };
+
+ FeedbackApi = new Dictionary
+ {
+ { "isconnected", new BoolFeedback( () => Device.IsConnected ) },
+ { "textrecieved", TextReceivedFeedback }
+ };
+ }
+
+ ///
+ /// Controls connection based on state of input
+ ///
+ ///
+ void ConnectByState(bool state)
+ {
+ if (state)
+ Device.Connect();
+ else
+ Device.Disconnect();
+ }
+
+ void SetupFeedbacks()
+ {
+ Device.TextReceived += new EventHandler(Device_TextReceived);
+
+ if(Device is ISocketStatus)
+ (Device as ISocketStatus).ConnectionChange += new EventHandler(IBasicCommunicationApi_ConnectionChange);
+ }
+
+ void IBasicCommunicationApi_ConnectionChange(object sender, GenericSocketStatusChageEventArgs e)
+ {
+ FeedbackApi["isconnected"].FireUpdate();
+ }
+
+ void Device_TextReceived(object sender, GenericCommMethodReceiveTextArgs e)
+ {
+ TextReceivedFeedback.FireUpdate(e.Text);
+ }
+ }
+
+
+
+ public class DmChassisController : Device
+ {
+ public DmChassisController(string key)
+ : base(key)
+ {
+
+ }
+
+ public void SetInput(int input)
+ {
+ Debug.Console(2, this, "Dm Chassis {0}, input {1}", Key, input);
+ }
+ }
+
+ ///
+ /// Each flavor of API is a static class with static properties and a static constructor that
+ /// links up the things to do.
+ ///
+ public class DmChassisControllerApi : DeviceApiBase
+ {
+ IntFeedback Output1Feedback;
+ IntFeedback Output2Feedback;
+
+ public DmChassisControllerApi(DmChassisController dev)
+ {
+ Output1Feedback = new IntFeedback( new Func(() => 1));
+ Output2Feedback = new IntFeedback( new Func(() => 2));
+
+ ActionApi = new Dictionary
+ {
+
+ };
+
+ FeedbackApi = new Dictionary
+ {
+ { "Output-1/fb", Output1Feedback },
+ { "Output-2/fb", Output2Feedback }
+ };
+ }
+
+ ///
+ /// Factory method
+ ///
+ ///
+ ///
+ public static DmChassisControllerApi GetActionApiForDevice(DmChassisController dev)
+ {
+ return new DmChassisControllerApi(dev);
+ }
+ }
+
+
+}
\ No newline at end of file
diff --git a/PepperDashEssentials/Bridges/BridgeFactory.cs b/PepperDashEssentials/Bridges/BridgeFactory.cs
new file mode 100644
index 00000000..97cc6fce
--- /dev/null
+++ b/PepperDashEssentials/Bridges/BridgeFactory.cs
@@ -0,0 +1,184 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Crestron.SimplSharp;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using PepperDash.Essentials.Core;
+using PepperDash.Essentials.Core.Config;
+using PepperDash.Core;
+using PepperDash.Essentials.Core.Routing;
+using Crestron.SimplSharpPro;
+using Crestron.SimplSharpPro.EthernetCommunication;
+
+namespace PepperDash.Essentials
+{
+ public class BridgeFactory
+ {
+ public static IKeyed GetDevice(PepperDash.Essentials.Core.Config.DeviceConfig dc)
+ {
+ // ? why is this static JTA 2018-06-13?
+
+ var key = dc.Key;
+ var name = dc.Name;
+ var type = dc.Type;
+ var properties = dc.Properties;
+ var propAnon = new { };
+ JsonConvert.DeserializeAnonymousType(dc.Properties.ToString(), propAnon);
+
+ var typeName = dc.Type.ToLower();
+ var groupName = dc.Group.ToLower();
+
+ Debug.Console(0, "Name {0}, Key {1}, Type {2}, Properties {3}", name, key, type, properties.ToString());
+ if (typeName == "dm")
+ {
+ return new DmBridge(key, name, properties);
+ }
+ else if (typeName == "comm")
+ {
+ return new CommBridge(key, name, properties);
+ }
+ else
+ return null;
+ }
+ }
+
+ public class DmBridge : Device
+ {
+ public EiscBridgeProperties Properties { get; private set; }
+
+ public PepperDash.Essentials.DM.DmChassisController DmSwitch { get; private set; }
+
+ public DmBridge(string key, string name, JToken properties) : base(key, name)
+ {
+ Properties = JsonConvert.DeserializeObject(properties.ToString());
+ }
+
+ public override bool CustomActivate()
+ {
+ // Create EiscApis
+ if (Properties.Eiscs != null)
+ {
+ foreach (var eisc in Properties.Eiscs)
+ {
+ var ApiEisc = new BridgeApiEisc(eisc.IpId, eisc.Hostname);
+
+ ApiEisc.Eisc.SetUShortSigAction(101, u => DmSwitch.ExecuteSwitch(u,1, eRoutingSignalType.Video));
+ ApiEisc.Eisc.SetUShortSigAction(102, u => DmSwitch.ExecuteSwitch(u,2, eRoutingSignalType.Video));
+ }
+ }
+
+ foreach (var device in DeviceManager.AllDevices)
+ {
+ if (device.Key == this.Properties.ParentDeviceKey)
+ {
+ Debug.Console(0, "deviceKey {0} Matches", device.Key);
+ DmSwitch = DeviceManager.GetDeviceForKey(device.Key) as PepperDash.Essentials.DM.DmChassisController;
+ }
+ else
+ {
+ Debug.Console(0, "deviceKey {0} doesn't match", device.Key);
+ }
+ }
+
+ Debug.Console(0, "Bridge {0} Activated", this.Name);
+ return true;
+ }
+ }
+
+ public class CommBridge : Device
+ {
+ public CommBridgeProperties Properties { get; private set; }
+
+ public List CommDevices { get; private set; }
+
+ public CommBridge(string key, string name, JToken properties)
+ : base(key, name)
+ {
+ Properties = JsonConvert.DeserializeObject(properties.ToString());
+ }
+
+ public override bool CustomActivate()
+ {
+ // Create EiscApis
+ if (Properties.Eiscs != null)
+ {
+ foreach (var eisc in Properties.Eiscs)
+ {
+ var ApiEisc = new BridgeApiEisc(eisc.IpId, eisc.Hostname);
+ }
+ }
+
+ foreach (var deviceKey in Properties.CommDevices)
+ {
+ var device = DeviceManager.GetDeviceForKey(deviceKey);
+
+ if (device != null)
+ {
+ Debug.Console(0, "deviceKey {0} Found in Device Manager", device.Key);
+ CommDevices.Add(device as IBasicCommunication);
+ }
+ else
+ {
+ Debug.Console(0, "deviceKey {0} Not Found in Device Manager", deviceKey);
+ }
+ }
+
+ // Iterate through all the CommDevices and link up their Actions and Feedbacks
+
+ Debug.Console(0, "Bridge {0} Activated", this.Name);
+
+ return true;
+ }
+ }
+
+
+ public class EiscBridgeProperties
+ {
+ public string ParentDeviceKey { get; set; }
+ public eApiType ApiType { get; set; }
+ public List Eiscs { get; set; }
+ public string ApiOverrideFilePath { get; set; }
+
+ public class EiscProperties
+ {
+ public string IpId { get; set; }
+ public string Hostname { get; set; }
+ }
+ }
+
+ public class CommBridgeProperties : EiscBridgeProperties
+ {
+ public List CommDevices { get; set; }
+ }
+
+ public enum eApiType { Eisc = 0 }
+
+ public class BridgeApiEisc
+ {
+ public uint Ipid { get; private set; }
+ public ThreeSeriesTcpIpEthernetIntersystemCommunications Eisc { get; private set; }
+
+ public BridgeApiEisc(string ipid, string hostname)
+ {
+ Ipid = (UInt32)int.Parse(ipid, System.Globalization.NumberStyles.HexNumber);
+ Eisc = new ThreeSeriesTcpIpEthernetIntersystemCommunications(Ipid, hostname, Global.ControlSystem);
+ Eisc.Register();
+ Eisc.SigChange += Eisc_SigChange;
+ Debug.Console(0, "BridgeApiEisc Created at Ipid {0}", ipid);
+ }
+ void Eisc_SigChange(object currentDevice, Crestron.SimplSharpPro.SigEventArgs args)
+ {
+ if (Debug.Level >= 1)
+ Debug.Console(1, "BridgeApiEisc change: {0} {1}={2}", args.Sig.Type, args.Sig.Number, args.Sig.StringValue);
+ var uo = args.Sig.UserObject;
+ if (uo is Action)
+ (uo as Action)(args.Sig.BoolValue);
+ else if (uo is Action)
+ (uo as Action)(args.Sig.UShortValue);
+ else if (uo is Action)
+ (uo as Action)(args.Sig.StringValue);
+ }
+ }
+}
\ No newline at end of file
diff --git a/PepperDashEssentials/Config/ConfigReader.cs b/PepperDashEssentials/Config/ConfigReader.cs
deleted file mode 100644
index 5c517a13..00000000
--- a/PepperDashEssentials/Config/ConfigReader.cs
+++ /dev/null
@@ -1,188 +0,0 @@
-using System;
-using System.Linq;
-using Crestron.SimplSharp;
-using Crestron.SimplSharp.CrestronIO;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
-using PepperDash.Core;
-using PepperDash.Essentials.Core;
-using PepperDash.Essentials.Core.Config;
-
-namespace PepperDash.Essentials
-{
- ///
- /// Loads the ConfigObject from the file
- ///
- public class ConfigReader
- {
- public static EssentialsConfig ConfigObject { get; private set; }
-
- public static bool LoadConfig2()
- {
- Debug.Console(0, Debug.ErrorLogLevel.Notice, "Loading unmerged system/template portal configuration file.");
- try
- {
- var filePath = Global.FilePathPrefix + "configurationFile.json";
-
- Debug.Console(0, Debug.ErrorLogLevel.Notice, "Attempting to load config file: '{0}'", filePath);
-
- if (!File.Exists(filePath))
- {
- Debug.Console(0, Debug.ErrorLogLevel.Error,
- "ERROR: Configuration file not present. Please load file to {0} and reset program", filePath);
- return false;
- }
-
- using (StreamReader fs = new StreamReader(filePath))
- {
- var doubleObj = JObject.Parse(fs.ReadToEnd());
- ConfigObject = MergeConfigs(doubleObj).ToObject();
-
- // Extract SystemUrl and TemplateUrl into final config output
-
- if (doubleObj["system_url"] != null)
- {
- ConfigObject.SystemUrl = doubleObj["system_url"].Value();
- }
-
- if (doubleObj["template_url"] != null)
- {
- ConfigObject.TemplateUrl= doubleObj["template_url"].Value();
- }
- }
-
- Debug.Console(0, Debug.ErrorLogLevel.Notice, "Successfully Loaded Merged Config");
-
- return true;
- }
- catch (Exception e)
- {
- Debug.Console(0, Debug.ErrorLogLevel.Error, "ERROR: Config load failed: \r{0}", e);
- return false;
- }
- }
-
-
- static JObject MergeConfigs(JObject doubleConfig)
- {
- var system = JObject.FromObject(doubleConfig["system"]);
- var template = JObject.FromObject(doubleConfig["template"]);
- var merged = new JObject();
-
- // Put together top-level objects
- // skip any objects that don't have template objects
-
- if (system["info"] != null)
- merged.Add("info", Merge(template["info"], system["info"]));
- else
- merged.Add("info", template["info"]);
-
- merged.Add("devices", MergeArraysOnTopLevelProperty(template["devices"] as JArray,
- system["devices"] as JArray, "uid"));
-
- if (template["rooms"] != null)
- {
- if (system["rooms"] == null)
- merged.Add("rooms", template["rooms"]);
- else
- merged.Add("rooms", MergeArraysOnTopLevelProperty(template["rooms"] as JArray,
- system["rooms"] as JArray, "key"));
- }
-
- if (template["sourceLists"] != null)
- {
- if (system["sourceLists"] == null)
- merged.Add("sourceLists", template["sourceLists"]);
- else
- merged.Add("sourceLists", Merge(template["sourceLists"], system["sourceLists"]));
- }
-
- // Template tie lines take precdence. Config tool doesn't do them at system
- // level anyway...
- if (template["tieLines"] != null)
- merged.Add("tieLines", template["tieLines"]);
- //else if (system["tieLines"] != null)
- // merged.Add("tieLines", system["tieLines"]);
- //else
- // merged.Add("tieLines", new JArray());
-
- Debug.Console(2, "MERGED CONFIG RESULT: \x0d\x0a{0}", merged);
- return merged;
- }
-
- ///
- /// Merges the contents of a base and a delta array, matching the entries on a top-level property
- /// given by propertyName. Returns a merge of them. Items in the delta array that do not have
- /// a matched item in base array will not be merged.
- ///
- static JArray MergeArraysOnTopLevelProperty(JArray a1, JArray a2, string propertyName)
- {
- var result = new JArray();
- if (a2 == null)
- result = a1;
- else if (a1 != null)
- {
- for (int i = 0; i < a1.Count(); i++)
- {
- var a1Dev = a1[i];
- // Try to get a system device and if found, merge it onto template
- var a2Match = a2.FirstOrDefault(t => t[propertyName].Equals(a1Dev[propertyName]));// t.Value("uid") == tmplDev.Value("uid"));
- if (a2Match != null)
- {
- var mergedItem = Merge(a1Dev, a2Match);// Merge(JObject.FromObject(a1Dev), JObject.FromObject(a2Match));
- result.Add(mergedItem);
- }
- else
- result.Add(a1Dev);
- }
- }
- return result;
- }
-
-
- ///
- /// Helper for using with JTokens. Converts to JObject
- ///
- static JObject Merge(JToken t1, JToken t2)
- {
- return Merge(JObject.FromObject(t1), JObject.FromObject(t2));
- }
-
- ///
- /// Merge b ONTO a
- ///
- ///
- ///
- static JObject Merge(JObject o1, JObject o2)
- {
- foreach (var o2Prop in o2)
- {
- var o1Value = o1[o2Prop.Key];
- if (o1Value == null)
- o1.Add(o2Prop.Key, o2Prop.Value);
- else
- {
- JToken replacement = null;
- if (o2Prop.Value.HasValues && o1Value.HasValues) // Drill down
- replacement = Merge(JObject.FromObject(o1Value), JObject.FromObject(o2Prop.Value));
- else
- replacement = o2Prop.Value;
- o1[o2Prop.Key].Replace(replacement);
- }
- }
- return o1;
- }
-
- ///
- /// Returns the group for a given device key in config
- ///
- ///
- ///
- public static string GetGroupForDeviceKey(string key)
- {
- var dev = ConfigObject.Devices.FirstOrDefault(d => d.Key.Equals(key, StringComparison.OrdinalIgnoreCase));
- return dev == null ? null : dev.Group;
- }
-
- }
-}
\ No newline at end of file
diff --git a/PepperDashEssentials/Config/EssentialsConfig.cs b/PepperDashEssentials/Config/EssentialsConfig.cs
deleted file mode 100644
index 71c2be9f..00000000
--- a/PepperDashEssentials/Config/EssentialsConfig.cs
+++ /dev/null
@@ -1,67 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Text.RegularExpressions;
-
-using Crestron.SimplSharp.CrestronIO;
-using Newtonsoft.Json;
-
-using PepperDash.Core;
-using PepperDash.Essentials.Core.Config;
-using PepperDash.Essentials.Room.Config;
-
-namespace PepperDash.Essentials
-{
- ///
- /// Loads the ConfigObject from the file
- ///
- public class EssentialsConfig : BasicConfig
- {
- [JsonProperty("system_url")]
- public string SystemUrl { get; set; }
-
- [JsonProperty("template_url")]
- public string TemplateUrl { get; set; }
-
-
- //public CotijaConfig Cotija { get; private set; }
-
- [JsonProperty("systemUuid")]
- public string SystemUuid
- {
- get
- {
- var result = Regex.Match(SystemUrl, @"https?:\/\/.*\/systems\/(.*)\/#.*");
-
- string uuid = result.Groups[1].Value;
-
- return uuid;
- }
- }
-
- [JsonProperty("templateUuid")]
- public string TemplateUuid
- {
- get
- {
- var result = Regex.Match(TemplateUrl, @"https?:\/\/.*\/templates\/(.*)\/#.*");
-
- string uuid = result.Groups[1].Value;
-
- return uuid;
- }
- }
-
- [JsonProperty("rooms")]
- public List Rooms { get; set; }
- }
-
- ///
- ///
- ///
- public class SystemTemplateConfigs
- {
- public EssentialsConfig System { get; set; }
-
- public EssentialsConfig Template { get; set; }
- }
-}
\ No newline at end of file
diff --git a/PepperDashEssentials/ControlSystem.cs b/PepperDashEssentials/ControlSystem.cs
index 06c63c05..f33537ae 100644
--- a/PepperDashEssentials/ControlSystem.cs
+++ b/PepperDashEssentials/ControlSystem.cs
@@ -6,9 +6,11 @@ using Crestron.SimplSharpPro;
using Crestron.SimplSharpPro.CrestronThread;
using PepperDash.Core;
using PepperDash.Essentials.Core;
+using PepperDash.Essentials.Core.Config;
using PepperDash.Essentials.Devices.Common;
using PepperDash.Essentials.DM;
using PepperDash.Essentials.Fusion;
+using PepperDash.Essentials.Room.Config;
using PepperDash.Essentials.Room.Cotija;
namespace PepperDash.Essentials
@@ -35,6 +37,8 @@ namespace PepperDash.Essentials
//CrestronConsole.AddNewConsoleCommand(s => GoWithLoad(), "go", "Loads configuration file",
// ConsoleAccessLevelEnum.AccessOperator);
+ // CrestronConsole.AddNewConsoleCommand(S => { ConfigWriter.WriteConfigFile(null); }, "writeconfig", "writes the current config to a file", ConsoleAccessLevelEnum.AccessOperator);
+
CrestronConsole.AddNewConsoleCommand(s =>
{
foreach (var tl in TieLineCollection.Default)
@@ -94,7 +98,7 @@ namespace PepperDash.Essentials
{
filePathPrefix = directoryPrefix + dirSeparator + "User" + dirSeparator;
- Debug.Console(0, Debug.ErrorLogLevel.Notice, "Starting Essentials v{0} on XiO Edge Server", versionString);
+ Debug.Console(0, Debug.ErrorLogLevel.Notice, "Starting Essentials v{0} on Virtual Control Server", versionString);
}
Global.SetFilePathPrefix(filePathPrefix);
@@ -291,7 +295,7 @@ namespace PepperDash.Essentials
foreach (var roomConfig in ConfigReader.ConfigObject.Rooms)
{
- var room = roomConfig.GetRoomObject();
+ var room = EssentialsRoomConfigHelper.GetRoomObject(roomConfig) as EssentialsRoomBase;
if (room != null)
{
if (room is EssentialsHuddleSpaceRoom)
@@ -316,10 +320,16 @@ namespace PepperDash.Essentials
Debug.Console(0, Debug.ErrorLogLevel.Notice, "Room is EssentialsHuddleVtc1Room, attempting to add to DeviceManager with Fusion");
DeviceManager.AddDevice(new EssentialsHuddleVtc1FusionController((EssentialsHuddleVtc1Room)room, 0xf1));
+
+ Debug.Console(0, Debug.ErrorLogLevel.Notice, "Attempting to build Cotija Bridge...");
+ // Cotija bridge
+ var bridge = new CotijaEssentialsHuddleSpaceRoomBridge(room);
+ AddBridgePostActivationHelper(bridge); // Lets things happen later when all devices are present
+ DeviceManager.AddDevice(bridge);
}
else
{
- Debug.Console(0, Debug.ErrorLogLevel.Notice, "Room is NOT EssentialsHuddleSpaceRoom, attempting to add to DeviceManager w/o Fusion");
+ Debug.Console(0, Debug.ErrorLogLevel.Notice, "Room is NOT EssentialsRoom, attempting to add to DeviceManager w/o Fusion");
DeviceManager.AddDevice(room);
}
@@ -343,7 +353,8 @@ namespace PepperDash.Essentials
var parent = DeviceManager.AllDevices.FirstOrDefault(d => d.Key == "appServer") as CotijaSystemController;
if (parent == null)
{
- Debug.Console(0, bridge, "ERROR: Cannot connect bridge. System controller not present");
+ Debug.Console(0, bridge, "ERROR: Cannot connect app server room bridge. System controller not present");
+ return;
}
Debug.Console(0, bridge, "Linking to parent controller");
bridge.AddParent(parent);
diff --git a/PepperDashEssentials/Factory/DeviceFactory.cs b/PepperDashEssentials/Factory/DeviceFactory.cs
index 16afc536..432802d0 100644
--- a/PepperDashEssentials/Factory/DeviceFactory.cs
+++ b/PepperDashEssentials/Factory/DeviceFactory.cs
@@ -1,92 +1,99 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using Crestron.SimplSharp;
-using Crestron.SimplSharp.CrestronIO;
-using Crestron.SimplSharpPro;
-
-using Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
-using PepperDash.Core;
-using PepperDash.Essentials.Core;
-using PepperDash.Essentials.Core.Config;
-
-namespace PepperDash.Essentials
-{
- public class DeviceFactory
- {
- public static IKeyed GetDevice(DeviceConfig dc)
- {
- var key = dc.Key;
- var name = dc.Name;
- var type = dc.Type;
- var properties = dc.Properties;
-
- var typeName = dc.Type.ToLower();
-
- if (typeName == "amplifier")
- {
- return new Amplifier(dc.Key, dc.Name);
- }
- else if (dc.Group.ToLower() == "touchpanel") // typeName.StartsWith("tsw"))
- {
- return UiDeviceFactory.GetUiDevice(dc);
- }
-
- else if (typeName == "mockdisplay")
- {
- return new MockDisplay(key, name);
- }
-
- else if (typeName == "generic")
- {
- return new Device(key, name);
- }
-
- // MOVE into something else???
- else if (typeName == "basicirdisplay")
- {
- var ir = IRPortHelper.GetIrPort(properties);
- if (ir != null)
- return new BasicIrDisplay(key, name, ir.Port, ir.FileName);
- }
-
- else if (typeName == "commmock")
- {
- var comm = CommFactory.CreateCommForDevice(dc);
- var props = JsonConvert.DeserializeObject(
- properties.ToString());
- return new ConsoleCommMockDevice(key, name, props, comm);
- }
-
- else if (typeName == "appserver")
- {
- var props = JsonConvert.DeserializeObject(properties.ToString());
- return new CotijaSystemController(key, name, props);
- }
-
- else if (typeName == "mobilecontrolbridge-ddvc01")
- {
- var comm = CommFactory.GetControlPropertiesConfig(dc);
-
- var bridge = new PepperDash.Essentials.Room.Cotija.CotijaDdvc01RoomBridge(key, name, comm.IpIdInt);
- bridge.AddPreActivationAction(() =>
- {
- var parent = DeviceManager.AllDevices.FirstOrDefault(d => d.Key == "appServer") as CotijaSystemController;
- if (parent == null)
- {
- Debug.Console(0, bridge, "ERROR: Cannot connect bridge. System controller not present");
- }
- Debug.Console(0, bridge, "Linking to parent controller");
- bridge.AddParent(parent);
- parent.AddBridge(bridge);
- });
-
- return bridge;
- }
-
- return null;
- }
- }
-
-}
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Crestron.SimplSharp;
+using Crestron.SimplSharp.CrestronIO;
+using Crestron.SimplSharpPro;
+
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using PepperDash.Core;
+using PepperDash.Essentials.Core;
+using PepperDash.Essentials.Core.Config;
+
+namespace PepperDash.Essentials
+{
+ public class DeviceFactory
+ {
+ public static IKeyed GetDevice(DeviceConfig dc)
+ {
+ var key = dc.Key;
+ var name = dc.Name;
+ var type = dc.Type;
+ var properties = dc.Properties;
+
+ var typeName = dc.Type.ToLower();
+
+ if (typeName == "amplifier")
+ {
+ return new Amplifier(dc.Key, dc.Name);
+ }
+ else if (dc.Group.ToLower() == "touchpanel") // typeName.StartsWith("tsw"))
+ {
+ return UiDeviceFactory.GetUiDevice(dc);
+ }
+
+ else if (typeName == "mockdisplay")
+ {
+ return new MockDisplay(key, name);
+ }
+
+ else if (typeName == "generic")
+ {
+ return new Device(key, name);
+ }
+
+ // MOVE into something else???
+ else if (typeName == "basicirdisplay")
+ {
+ var ir = IRPortHelper.GetIrPort(properties);
+ if (ir != null)
+ return new BasicIrDisplay(key, name, ir.Port, ir.FileName);
+ }
+
+ else if (typeName == "commmock")
+ {
+ var comm = CommFactory.CreateCommForDevice(dc);
+ var props = JsonConvert.DeserializeObject(
+ properties.ToString());
+ return new ConsoleCommMockDevice(key, name, props, comm);
+ }
+
+ else if (typeName == "appserver")
+ {
+ var props = JsonConvert.DeserializeObject(properties.ToString());
+ return new CotijaSystemController(key, name, props);
+ }
+
+ else if (typeName == "mobilecontrolbridge-ddvc01")
+ {
+ var comm = CommFactory.GetControlPropertiesConfig(dc);
+
+ var bridge = new PepperDash.Essentials.Room.Cotija.CotijaDdvc01RoomBridge(key, name, comm.IpIdInt);
+ bridge.AddPreActivationAction(() =>
+ {
+ var parent = DeviceManager.AllDevices.FirstOrDefault(d => d.Key == "appServer") as CotijaSystemController;
+ if (parent == null)
+ {
+ Debug.Console(0, bridge, "ERROR: Cannot connect bridge. System controller not present");
+ }
+ Debug.Console(0, bridge, "Linking to parent controller");
+ bridge.AddParent(parent);
+ parent.AddBridge(bridge);
+ });
+
+ return bridge;
+ }
+
+ else if (typeName == "roomonwhenoccupancydetectedfeature")
+ {
+ var props = JsonConvert.DeserializeObject(properties.ToString());
+
+ return new Room.Behaviours.RoomOnToDefaultSourceWhenOccupied(key, props);
+ }
+
+ return null;
+ }
+ }
+
+}
diff --git a/PepperDashEssentials/OTHER/Fusion/EssentialsHuddleSpaceFusionSystemControllerBase.cs b/PepperDashEssentials/OTHER/Fusion/EssentialsHuddleSpaceFusionSystemControllerBase.cs
index a463fa50..609597d8 100644
--- a/PepperDashEssentials/OTHER/Fusion/EssentialsHuddleSpaceFusionSystemControllerBase.cs
+++ b/PepperDashEssentials/OTHER/Fusion/EssentialsHuddleSpaceFusionSystemControllerBase.cs
@@ -1,1465 +1,1595 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-
-using Crestron.SimplSharp;
-using Crestron.SimplSharp.CrestronIO;
-using Crestron.SimplSharp.CrestronXml;
-using Crestron.SimplSharp.CrestronXml.Serialization;
-using Crestron.SimplSharp.CrestronXmlLinq;
-using Crestron.SimplSharpPro;
-using Crestron.SimplSharpPro.DeviceSupport;
-using Crestron.SimplSharpPro.Fusion;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
-
-using PepperDash.Core;
-using PepperDash.Essentials;
-using PepperDash.Essentials.Core;
-using PepperDash.Essentials.Devices.Common;
-using PepperDash.Essentials.Devices.Common.Occupancy;
-
-
-
-namespace PepperDash.Essentials.Fusion
-{
- public class EssentialsHuddleSpaceFusionSystemControllerBase : Device, IOccupancyStatusProvider
- {
- public event EventHandler ScheduleChange;
- //public event EventHandler MeetingEndWarning;
- //public event EventHandler NextMeetingBeginWarning;
-
- protected FusionRoom FusionRoom;
- protected EssentialsRoomBase Room;
- Dictionary SourceToFeedbackSigs =
- new Dictionary();
-
- StatusMonitorCollection ErrorMessageRollUp;
-
- protected StringSigData CurrentRoomSourceNameSig;
-
- #region System Info Sigs
- //StringSigData SystemName;
- //StringSigData Model;
- //StringSigData SerialNumber;
- //StringSigData Uptime;
- #endregion
-
-
- #region Processor Info Sigs
- StringSigData Ip1;
- StringSigData Ip2;
- StringSigData Gateway;
- StringSigData Hostname;
- StringSigData Domain;
- StringSigData Dns1;
- StringSigData Dns2;
- StringSigData Mac1;
- StringSigData Mac2;
- StringSigData NetMask1;
- StringSigData NetMask2;
- StringSigData Firmware;
-
- StringSigData[] Program = new StringSigData[10];
- #endregion
-
- #region Default Display Source Sigs
-
- BooleanSigData[] Source = new BooleanSigData[10];
-
- #endregion
-
- RoomSchedule CurrentSchedule;
-
- Event NextMeeting;
-
- Event CurrentMeeting;
-
- protected string RoomGuid
- {
- get
- {
- return GUIDs.RoomGuid;
- }
-
- }
-
- uint IpId;
-
- FusionRoomGuids GUIDs;
-
- bool GuidFileExists;
-
- bool IsRegisteredForSchedulePushNotifications = false;
-
- CTimer PollTimer = null;
-
- CTimer PushNotificationTimer = null;
-
- CTimer DailyTimeRequestTimer = null;
-
- // Default poll time is 5 min unless overridden by config value
- public long SchedulePollInterval = 300000;
-
- public long PushNotificationTimeout = 5000;
-
- protected Dictionary FusionStaticAssets;
-
- // For use with local occ sensor devices which will relay to Fusion the current occupancy status
- protected FusionRemoteOccupancySensor FusionRemoteOccSensor;
-
- // For use with occ sensor attached to a scheduling panel in Fusion
- protected FusionOccupancySensorAsset FusionOccSensor;
-
- public BoolFeedback RoomIsOccupiedFeedback { get; private set; }
-
- protected Func RoomIsOccupiedFeedbackFunc
- {
- get
- {
- return () => FusionRemoteOccSensor.RoomOccupied.OutputSig.BoolValue;
- }
- }
-
- //ScheduleResponseEvent NextMeeting;
-
- public EssentialsHuddleSpaceFusionSystemControllerBase(EssentialsRoomBase room, uint ipId)
- : base(room.Key + "-fusion")
- {
-
- try
- {
-
- Room = room;
-
- IpId = ipId;
-
- FusionStaticAssets = new Dictionary();
-
- GUIDs = new FusionRoomGuids();
-
- var mac = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_MAC_ADDRESS, 0);
-
- var slot = Global.ControlSystem.ProgramNumber;
-
- string guidFilePath = Global.FilePathPrefix + string.Format(@"{0}-FusionGuids.json", InitialParametersClass.ProgramIDTag);
-
- GuidFileExists = File.Exists(guidFilePath);
-
- // Check if file exists
- if (!GuidFileExists)
- {
- // Does not exist. Create GUIDs
- GUIDs = new FusionRoomGuids(Room.Name, ipId, GUIDs.GenerateNewRoomGuid(slot, mac), FusionStaticAssets);
- }
- else
- {
- // Exists. Read GUIDs
- ReadGuidFile(guidFilePath);
- }
-
- CreateSymbolAndBasicSigs(IpId);
- SetUpSources();
- SetUpCommunitcationMonitors();
- SetUpDisplay();
- SetUpError();
- ExecuteCustomSteps();
-
- if (Room.RoomOccupancy != null)
- {
- if (Room.OccupancyStatusProviderIsRemote)
- SetUpRemoteOccupancy();
- else
- {
- SetUpLocalOccupancy();
- }
- }
-
- // Make it so!
- FusionRVI.GenerateFileForAllFusionDevices();
-
- GenerateGuidFile(guidFilePath);
- }
- catch (Exception e)
- {
- Debug.Console(0, this, Debug.ErrorLogLevel.Error, "Error Building Fusion System Controller: {0}", e);
- }
- }
-
- ///
- /// Used for extension classes to execute whatever steps are necessary before generating the RVI and GUID files
- ///
- protected virtual void ExecuteCustomSteps()
- {
-
- }
-
- ///
- /// Generates the guid file in NVRAM. If the file already exists it will be overwritten.
- ///
- /// path for the file
- void GenerateGuidFile(string filePath)
- {
- if (string.IsNullOrEmpty(filePath))
- {
- Debug.Console(0, this, "Error writing guid file. No path specified.");
- return;
- }
-
- CCriticalSection _fileLock = new CCriticalSection();
-
- try
- {
- if (_fileLock == null || _fileLock.Disposed)
- return;
-
- _fileLock.Enter();
-
- Debug.Console(1, this, "Writing GUIDs to file");
-
- if (FusionOccSensor == null)
- GUIDs = new FusionRoomGuids(Room.Name, IpId, RoomGuid, FusionStaticAssets);
- else
- GUIDs = new FusionRoomGuids(Room.Name, IpId, RoomGuid, FusionStaticAssets, FusionOccSensor);
-
- var JSON = JsonConvert.SerializeObject(GUIDs, Newtonsoft.Json.Formatting.Indented);
-
- using (StreamWriter sw = new StreamWriter(filePath))
- {
- sw.Write(JSON);
- sw.Flush();
- }
-
- Debug.Console(1, this, "Guids successfully written to file '{0}'", filePath);
-
- }
- catch (Exception e)
- {
- Debug.Console(0, this, "Error writing guid file: {0}", e);
- }
- finally
- {
- if (_fileLock != null && !_fileLock.Disposed)
- _fileLock.Leave();
- }
- }
-
- ///
- /// Reads the guid file from NVRAM
- ///
- /// path for te file
- void ReadGuidFile(string filePath)
- {
- if(string.IsNullOrEmpty(filePath))
- {
- Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Error reading guid file. No path specified.");
- return;
- }
-
- CCriticalSection _fileLock = new CCriticalSection();
-
- try
- {
- if(_fileLock == null || _fileLock.Disposed)
- return;
-
- _fileLock.Enter();
-
- if(File.Exists(filePath))
- {
- var JSON = File.ReadToEnd(filePath, Encoding.ASCII);
-
- GUIDs = JsonConvert.DeserializeObject(JSON);
-
- IpId = GUIDs.IpId;
-
- FusionStaticAssets = GUIDs.StaticAssets;
-
- }
-
- Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Fusion Guids successfully read from file: {0}", filePath);
-
- Debug.Console(1, this, "\nRoom Name: {0}\nIPID: {1:x}\n RoomGuid: {2}", Room.Name, IpId, RoomGuid);
-
- foreach (var item in FusionStaticAssets)
- {
- Debug.Console(1, this, "\nAsset Name: {0}\nAsset No: {1}\n Guid: {2}", item.Value.Name, item.Value.SlotNumber, item.Value.InstanceId);
- }
- }
- catch (Exception e)
- {
- Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Error reading guid file: {0}", e);
- }
- finally
- {
- if(_fileLock != null && !_fileLock.Disposed)
- _fileLock.Leave();
- }
-
- }
-
- protected virtual void CreateSymbolAndBasicSigs(uint ipId)
- {
- Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Creating Fusion Room symbol with GUID: {0}", RoomGuid);
-
- FusionRoom = new FusionRoom(ipId, Global.ControlSystem, Room.Name, RoomGuid);
- FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.Use();
- FusionRoom.ExtenderFusionRoomDataReservedSigs.Use();
-
- FusionRoom.Register();
-
- FusionRoom.FusionStateChange += new FusionStateEventHandler(FusionRoom_FusionStateChange);
-
- FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.DeviceExtenderSigChange += new DeviceExtenderJoinChangeEventHandler(FusionRoomSchedule_DeviceExtenderSigChange);
- FusionRoom.ExtenderFusionRoomDataReservedSigs.DeviceExtenderSigChange += new DeviceExtenderJoinChangeEventHandler(ExtenderFusionRoomDataReservedSigs_DeviceExtenderSigChange);
- FusionRoom.OnlineStatusChange += new OnlineStatusChangeEventHandler(FusionRoom_OnlineStatusChange);
-
- CrestronConsole.AddNewConsoleCommand(RequestFullRoomSchedule, "FusReqRoomSchedule", "Requests schedule of the room for the next 24 hours", ConsoleAccessLevelEnum.AccessOperator);
- CrestronConsole.AddNewConsoleCommand(ModifyMeetingEndTimeConsoleHelper, "FusReqRoomSchMod", "Ends or extends a meeting by the specified time", ConsoleAccessLevelEnum.AccessOperator);
- CrestronConsole.AddNewConsoleCommand(CreateAsHocMeeting, "FusCreateMeeting", "Creates and Ad Hoc meeting for on hour or until the next meeting", ConsoleAccessLevelEnum.AccessOperator);
-
- // Room to fusion room
- Room.OnFeedback.LinkInputSig(FusionRoom.SystemPowerOn.InputSig);
-
- // Moved to
- CurrentRoomSourceNameSig = FusionRoom.CreateOffsetStringSig(84, "Display 1 - Current Source", eSigIoMask.InputSigOnly);
- // Don't think we need to get current status of this as nothing should be alive yet.
- (Room as EssentialsHuddleSpaceRoom).CurrentSingleSourceChange += new SourceInfoChangeHandler(Room_CurrentSourceInfoChange);
-
-
- FusionRoom.SystemPowerOn.OutputSig.SetSigFalseAction((Room as EssentialsHuddleSpaceRoom).PowerOnToDefaultOrLastSource);
- FusionRoom.SystemPowerOff.OutputSig.SetSigFalseAction(() => (Room as EssentialsHuddleSpaceRoom).RunRouteAction("roomOff"));
- // NO!! room.RoomIsOn.LinkComplementInputSig(FusionRoom.SystemPowerOff.InputSig);
- FusionRoom.ErrorMessage.InputSig.StringValue =
- "3: 7 Errors: This is a really long error message;This is a really long error message;This is a really long error message;This is a really long error message;This is a really long error message;This is a really long error message;This is a really long error message;";
-
- GetProcessorEthernetValues();
-
- GetSystemInfo();
-
- GetProcessorInfo();
-
- CrestronEnvironment.EthernetEventHandler += new EthernetEventHandler(CrestronEnvironment_EthernetEventHandler);
- }
-
- protected void CrestronEnvironment_EthernetEventHandler(EthernetEventArgs ethernetEventArgs)
- {
- if (ethernetEventArgs.EthernetEventType == eEthernetEventType.LinkUp)
- {
- GetProcessorEthernetValues();
- }
- }
-
- protected void GetSystemInfo()
- {
- //SystemName.InputSig.StringValue = Room.Name;
- //Model.InputSig.StringValue = InitialParametersClass.ControllerPromptName;
- //SerialNumber.InputSig.StringValue = InitialParametersClass.
-
- string response = string.Empty;
-
- var systemReboot = FusionRoom.CreateOffsetBoolSig(74, "Processor - Reboot", eSigIoMask.OutputSigOnly);
- systemReboot.OutputSig.SetSigFalseAction(() => CrestronConsole.SendControlSystemCommand("reboot", ref response));
- }
-
- protected void GetProcessorEthernetValues()
- {
- Ip1 = FusionRoom.CreateOffsetStringSig(50, "Info - Processor - IP 1", eSigIoMask.InputSigOnly);
- Ip2 = FusionRoom.CreateOffsetStringSig(51, "Info - Processor - IP 2", eSigIoMask.InputSigOnly);
- Gateway = FusionRoom.CreateOffsetStringSig(52, "Info - Processor - Gateway", eSigIoMask.InputSigOnly);
- Hostname = FusionRoom.CreateOffsetStringSig(53, "Info - Processor - Hostname", eSigIoMask.InputSigOnly);
- Domain = FusionRoom.CreateOffsetStringSig(54, "Info - Processor - Domain", eSigIoMask.InputSigOnly);
- Dns1 = FusionRoom.CreateOffsetStringSig(55, "Info - Processor - DNS 1", eSigIoMask.InputSigOnly);
- Dns2 = FusionRoom.CreateOffsetStringSig(56, "Info - Processor - DNS 2", eSigIoMask.InputSigOnly);
- Mac1 = FusionRoom.CreateOffsetStringSig(57, "Info - Processor - MAC 1", eSigIoMask.InputSigOnly);
- Mac2 = FusionRoom.CreateOffsetStringSig(58, "Info - Processor - MAC 2", eSigIoMask.InputSigOnly);
- NetMask1 = FusionRoom.CreateOffsetStringSig(59, "Info - Processor - Net Mask 1", eSigIoMask.InputSigOnly);
- NetMask2 = FusionRoom.CreateOffsetStringSig(60, "Info - Processor - Net Mask 2", eSigIoMask.InputSigOnly);
-
- // Interface =0
- Ip1.InputSig.StringValue = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, 0);
- Gateway.InputSig.StringValue = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_ROUTER, 0);
- Hostname.InputSig.StringValue = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_HOSTNAME, 0);
- Domain.InputSig.StringValue = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_DOMAIN_NAME, 0);
-
- var dnsServers = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_DNS_SERVER, 0).Split(',');
- Dns1.InputSig.StringValue = dnsServers[0];
- if (dnsServers.Length > 1)
- Dns2.InputSig.StringValue = dnsServers[1];
-
- Mac1.InputSig.StringValue = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_MAC_ADDRESS, 0);
- NetMask1.InputSig.StringValue = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_MASK, 0);
-
- // Interface 1
-
- if (InitialParametersClass.NumberOfEthernetInterfaces > 1) // Only get these values if the processor has more than 1 NIC
- {
- Ip2.InputSig.StringValue = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, 1);
- Mac2.InputSig.StringValue = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_MAC_ADDRESS, 1);
- NetMask2.InputSig.StringValue = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_MASK, 1);
- }
- }
-
- protected void GetProcessorInfo()
- {
-
- Firmware = FusionRoom.CreateOffsetStringSig(61, "Info - Processor - Firmware", eSigIoMask.InputSigOnly);
-
- if (CrestronEnvironment.DevicePlatform != eDevicePlatform.Server)
- {
- for (int i = 0; i < Global.ControlSystem.NumProgramsSupported; i++)
- {
- var join = 62 + i;
- var progNum = i + 1;
- Program[i] = FusionRoom.CreateOffsetStringSig((uint)join, string.Format("Info - Processor - Program {0}", progNum), eSigIoMask.InputSigOnly);
- }
- }
-
- Firmware.InputSig.StringValue = InitialParametersClass.FirmwareVersion;
-
- }
-
- void GetTouchpanelInfo()
- {
- // TODO Get IP and Project Name from TP
- }
-
- protected void FusionRoom_OnlineStatusChange(GenericBase currentDevice, OnlineOfflineEventArgs args)
- {
- if (args.DeviceOnLine)
- {
- CrestronEnvironment.Sleep(200);
-
- // Send Push Notification Action request:
-
- string requestID = "InitialPushRequest";
-
-
- string actionRequest =
- string.Format("\n{0}\n", requestID) +
- "RegisterPushModel\n" +
- "\n" +
- "\n" +
- "\n" +
- "\n" +
- "\n" +
- "\n" +
- "\n" +
- "\n" +
- "\n" +
- "\n" +
- "\n" +
- "\n" +
- "\n" +
- "\n" +
- "\n" +
- "\n" +
- "\n" +
- "\n" +
- "\n" +
- "\n" +
- "\n";
-
- Debug.Console(2, this, "Sending Fusion ActionRequest: \n{0}", actionRequest);
-
- FusionRoom.ExtenderFusionRoomDataReservedSigs.ActionQuery.StringValue = actionRequest;
-
-
- // Request current Fusion Server Time
- RequestLocalDateTime(null);
-
- // Setup timer to request time daily
- if (DailyTimeRequestTimer != null && !DailyTimeRequestTimer.Disposed)
- {
- DailyTimeRequestTimer.Stop();
- DailyTimeRequestTimer.Dispose();
- }
-
- DailyTimeRequestTimer = new CTimer(RequestLocalDateTime, null, 86400000, 86400000);
-
- DailyTimeRequestTimer.Reset(86400000, 86400000);
- }
-
- }
-
- ///
- /// Requests the local date and time from the Fusion Server
- ///
- ///
- public void RequestLocalDateTime(object callbackObject)
- {
- string timeRequestID = "TimeRequest";
-
- string timeRequest = string.Format("{0}", timeRequestID);
-
- FusionRoom.ExtenderFusionRoomDataReservedSigs.LocalDateTimeQuery.StringValue = timeRequest;
- }
-
- ///
- /// Generates a room schedule request for this room for the next 24 hours.
- ///
- /// string identifying this request. Used with a corresponding ScheduleResponse value
- public void RequestFullRoomSchedule(object callbackObject)
- {
- DateTime now = DateTime.Today;
-
- string currentTime = now.ToString("s");
-
- string requestTest =
- string.Format("FullSchedleRequest{0}{1}24", RoomGuid, currentTime);
-
- Debug.Console(2, this, "Sending Fusion ScheduleQuery: \n{0}", requestTest);
-
- FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.ScheduleQuery.StringValue = requestTest;
-
- if (IsRegisteredForSchedulePushNotifications)
- PushNotificationTimer.Stop();
- }
-
- ///
- /// Wrapper method to allow console commands to modify the current meeting end time
- ///
- /// meetingID extendTime
- public void ModifyMeetingEndTimeConsoleHelper(string command)
- {
- string requestID;
- string meetingID = null;
- int extendMinutes = -1;
-
- requestID = "ModifyMeetingTest12345";
-
- try
- {
- var tokens = command.Split(' ');
-
- meetingID = tokens[0];
- extendMinutes = Int32.Parse(tokens[1]);
-
- }
- catch (Exception e)
- {
- Debug.Console(1, this, "Error parsing console command: {0}", e);
- }
-
- ModifyMeetingEndTime(requestID, extendMinutes);
-
- }
-
- ///
- /// Ends or Extends the current meeting by the specified number of minutes.
- ///
- /// Number of minutes to extend the meeting. A value of 0 will end the meeting.
- public void ModifyMeetingEndTime(string requestID, int extendMinutes)
- {
- if(CurrentMeeting == null)
- {
- Debug.Console(1, this, "No meeting in progress. Unable to modify end time.");
- return;
- }
-
- if (extendMinutes > -1)
- {
- if(extendMinutes > 0)
- {
- var extendTime = CurrentMeeting.dtEnd - DateTime.Now;
- double extendMinutesRaw = extendTime.TotalMinutes;
-
- extendMinutes = extendMinutes + (int)Math.Round(extendMinutesRaw);
- }
-
-
- string requestTest = string.Format(
- "{0}{1}MeetingChange"
- , requestID, RoomGuid, CurrentMeeting.MeetingID, extendMinutes);
-
- Debug.Console(1, this, "Sending MeetingChange Request: \n{0}", requestTest);
-
- FusionRoom.ExtenderFusionRoomDataReservedSigs.ActionQuery.StringValue = requestTest;
- }
- else
- {
- Debug.Console(1, this, "Invalid time specified");
- }
-
-
- }
-
- ///
- /// Creates and Ad Hoc meeting with a duration of 1 hour, or until the next meeting if in less than 1 hour.
- ///
- public void CreateAsHocMeeting(string command)
- {
- string requestID = "CreateAdHocMeeting";
-
- DateTime now = DateTime.Now.AddMinutes(1);
-
- now.AddSeconds(-now.Second);
-
- // Assume 1 hour meeting if possible
- DateTime dtEnd = now.AddHours(1);
-
- // Check if room is available for 1 hour before next meeting
- if (NextMeeting != null)
- {
- var roomAvailable = NextMeeting.dtEnd.Subtract(dtEnd);
-
- if (roomAvailable.TotalMinutes < 60)
- {
- /// Room not available for full hour, book until next meeting starts
- dtEnd = NextMeeting.dtEnd;
- }
- }
-
- string createMeetingRequest =
- "" +
- string.Format("{0}", requestID) +
- string.Format("{0}", RoomGuid) +
- "" +
- string.Format("{0}", now.ToString("s")) +
- string.Format("{0}", dtEnd.ToString("s")) +
- "AdHoc Meeting" +
- "Room User" +
- "Example Message" +
- "" +
- "";
-
- Debug.Console(2, this, "Sending CreateMeeting Request: \n{0}", createMeetingRequest);
-
- FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.CreateMeeting.StringValue = createMeetingRequest;
-
- //Debug.Console(1, this, "Sending CreateMeeting Request: \n{0}", command);
-
- //FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.CreateMeeting.StringValue = command;
-
- }
-
- ///
- /// Event handler method for Device Extender sig changes
- ///
- ///
- ///
- protected void ExtenderFusionRoomDataReservedSigs_DeviceExtenderSigChange(DeviceExtender currentDeviceExtender, SigEventArgs args)
- {
- Debug.Console(2, this, "Event: {0}\n Sig: {1}\nFusionResponse:\n{2}", args.Event, args.Sig.Name, args.Sig.StringValue);
-
-
- if (args.Sig == FusionRoom.ExtenderFusionRoomDataReservedSigs.ActionQueryResponse)
- {
- try
- {
- XmlDocument message = new XmlDocument();
-
- message.LoadXml(args.Sig.StringValue);
-
- var actionResponse = message["ActionResponse"];
-
- if (actionResponse != null)
- {
- var requestID = actionResponse["RequestID"];
-
- if (requestID.InnerText == "InitialPushRequest")
- {
- if (actionResponse["ActionID"].InnerText == "RegisterPushModel")
- {
- var parameters = actionResponse["Parameters"];
-
- foreach (XmlElement parameter in parameters)
- {
- if (parameter.HasAttributes)
- {
- var attributes = parameter.Attributes;
-
- if (attributes["ID"].Value == "Registered")
- {
- var isRegistered = Int32.Parse(attributes["Value"].Value);
-
- if (isRegistered == 1)
- {
- IsRegisteredForSchedulePushNotifications = true;
-
- if (PollTimer != null && !PollTimer.Disposed)
- {
- PollTimer.Stop();
- PollTimer.Dispose();
- }
-
- PushNotificationTimer = new CTimer(RequestFullRoomSchedule, null, PushNotificationTimeout, PushNotificationTimeout);
-
- PushNotificationTimer.Reset(PushNotificationTimeout, PushNotificationTimeout);
- }
- else if (isRegistered == 0)
- {
- IsRegisteredForSchedulePushNotifications = false;
-
- if (PushNotificationTimer != null && !PushNotificationTimer.Disposed)
- {
- PushNotificationTimer.Stop();
- PushNotificationTimer.Dispose();
- }
-
- PollTimer = new CTimer(RequestFullRoomSchedule, null, SchedulePollInterval, SchedulePollInterval);
-
- PollTimer.Reset(SchedulePollInterval, SchedulePollInterval);
- }
- }
- }
- }
- }
- }
- }
- }
- catch (Exception e)
- {
- Debug.Console(1, this, "Error parsing ActionQueryResponse: {0}", e);
- }
- }
- else if (args.Sig == FusionRoom.ExtenderFusionRoomDataReservedSigs.LocalDateTimeQueryResponse)
- {
- try
- {
- XmlDocument message = new XmlDocument();
-
- message.LoadXml(args.Sig.StringValue);
-
- var localDateTimeResponse = message["LocalTimeResponse"];
-
- if (localDateTimeResponse != null)
- {
- var localDateTime = localDateTimeResponse["LocalDateTime"];
-
- if (localDateTime != null)
- {
- var tempLocalDateTime = localDateTime.InnerText;
-
- DateTime currentTime = DateTime.Parse(tempLocalDateTime);
-
- Debug.Console(1, this, "DateTime from Fusion Server: {0}", currentTime);
-
- // Parse time and date from response and insert values
- CrestronEnvironment.SetTimeAndDate((ushort)currentTime.Hour, (ushort)currentTime.Minute, (ushort)currentTime.Second, (ushort)currentTime.Month, (ushort)currentTime.Day, (ushort)currentTime.Year);
-
- Debug.Console(1, this, "Processor time set to {0}", CrestronEnvironment.GetLocalTime());
- }
- }
- }
- catch (Exception e)
- {
- Debug.Console(1, this, "Error parsing LocalDateTimeQueryResponse: {0}", e);
- }
- }
- }
-
- ///
- /// Event handler method for Device Extender sig changes
- ///
- ///
- ///
- protected void FusionRoomSchedule_DeviceExtenderSigChange(DeviceExtender currentDeviceExtender, SigEventArgs args)
- {
- Debug.Console(2, this, "Scehdule Response Event: {0}\n Sig: {1}\nFusionResponse:\n{2}", args.Event, args.Sig.Name, args.Sig.StringValue);
-
-
- if (args.Sig == FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.ScheduleResponse)
- {
- try
- {
- ScheduleResponse scheduleResponse = new ScheduleResponse();
-
- XmlDocument message = new XmlDocument();
-
- message.LoadXml(args.Sig.StringValue);
-
- var response = message["ScheduleResponse"];
-
- if (response != null)
- {
- // Check for push notification
- if (response["RequestID"].InnerText == "RVRequest")
- {
- var action = response["Action"];
-
- if (action.OuterXml.IndexOf("RequestSchedule") > -1)
- {
- PushNotificationTimer.Reset(PushNotificationTimeout, PushNotificationTimeout);
- }
- }
- else // Not a push notification
- {
- CurrentSchedule = new RoomSchedule(); // Clear Current Schedule
- CurrentMeeting = null; // Clear Current Meeting
- NextMeeting = null; // Clear Next Meeting
-
- bool isNextMeeting = false;
-
- foreach (XmlElement element in message.FirstChild.ChildNodes)
- {
- if (element.Name == "RequestID")
- {
- scheduleResponse.RequestID = element.InnerText;
- }
- else if (element.Name == "RoomID")
- {
- scheduleResponse.RoomID = element.InnerText;
- }
- else if (element.Name == "RoomName")
- {
- scheduleResponse.RoomName = element.InnerText;
- }
- else if (element.Name == "Event")
- {
- Debug.Console(2, this, "Event Found:\n{0}", element.OuterXml);
-
- XmlReader reader = new XmlReader(element.OuterXml);
-
- Event tempEvent = new Event();
-
- tempEvent = CrestronXMLSerialization.DeSerializeObject(reader);
-
- scheduleResponse.Events.Add(tempEvent);
-
- // Check is this is the current event
- if (tempEvent.dtStart <= DateTime.Now && tempEvent.dtEnd >= DateTime.Now)
- {
- CurrentMeeting = tempEvent; // Set Current Meeting
- isNextMeeting = true; // Flag that next element is next meeting
- }
-
- if (isNextMeeting)
- {
- NextMeeting = tempEvent; // Set Next Meeting
- isNextMeeting = false;
- }
-
- CurrentSchedule.Meetings.Add(tempEvent);
- }
-
- }
-
- PrintTodaysSchedule();
-
- if (!IsRegisteredForSchedulePushNotifications)
- PollTimer.Reset(SchedulePollInterval, SchedulePollInterval);
-
- // Fire Schedule Change Event
- var handler = ScheduleChange;
-
- if (handler != null)
- {
- handler(this, new ScheduleChangeEventArgs() { Schedule = CurrentSchedule });
- }
-
- }
- }
-
-
-
- }
- catch (Exception e)
- {
- Debug.Console(1, this, "Error parsing ScheduleResponse: {0}", e);
- }
- }
- else if (args.Sig == FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.CreateResponse)
- {
- Debug.Console(2, this, "Create Meeting Response Event: {0}\n Sig: {1}\nFusionResponse:\n{2}", args.Event, args.Sig.Name, args.Sig.StringValue);
- }
-
- }
-
- ///
- /// Prints today's schedule to console for debugging
- ///
- void PrintTodaysSchedule()
- {
- if (Debug.Level > 1)
- {
- if (CurrentSchedule.Meetings.Count > 0)
- {
- Debug.Console(1, this, "Today's Schedule for '{0}'\n", Room.Name);
-
- foreach (Event e in CurrentSchedule.Meetings)
- {
- Debug.Console(1, this, "Subject: {0}", e.Subject);
- Debug.Console(1, this, "Organizer: {0}", e.Organizer);
- Debug.Console(1, this, "MeetingID: {0}", e.MeetingID);
- Debug.Console(1, this, "Start Time: {0}", e.dtStart);
- Debug.Console(1, this, "End Time: {0}", e.dtEnd);
- Debug.Console(1, this, "Duration: {0}\n", e.DurationInMinutes);
- }
- }
- }
- }
-
- protected virtual void SetUpSources()
- {
- // Sources
- var dict = ConfigReader.ConfigObject.GetSourceListForKey((Room as EssentialsHuddleSpaceRoom).SourceListKey);
- if (dict != null)
- {
- // NEW PROCESS:
- // Make these lists and insert the fusion attributes by iterating these
- var setTopBoxes = dict.Where(d => d.Value.SourceDevice is ISetTopBoxControls);
- uint i = 1;
- foreach (var kvp in setTopBoxes)
- {
- TryAddRouteActionSigs("Display 1 - Source TV " + i, 188 + i, kvp.Key, kvp.Value.SourceDevice);
- i++;
- if (i > 5) // We only have five spots
- break;
- }
-
- var discPlayers = dict.Where(d => d.Value.SourceDevice is IDiscPlayerControls);
- i = 1;
- foreach (var kvp in discPlayers)
- {
- TryAddRouteActionSigs("Display 1 - Source DVD " + i, 181 + i, kvp.Key, kvp.Value.SourceDevice);
- i++;
- if (i > 5) // We only have five spots
- break;
- }
-
- var laptops = dict.Where(d => d.Value.SourceDevice is Laptop);
- i = 1;
- foreach (var kvp in laptops)
- {
- TryAddRouteActionSigs("Display 1 - Source Laptop " + i, 166 + i, kvp.Key, kvp.Value.SourceDevice);
- i++;
- if (i > 10) // We only have ten spots???
- break;
- }
-
- foreach (var kvp in dict)
- {
- var usageDevice = kvp.Value.SourceDevice as IUsageTracking;
-
- if (usageDevice != null)
- {
- usageDevice.UsageTracker = new UsageTracking(usageDevice as Device);
- usageDevice.UsageTracker.UsageIsTracked = true;
- usageDevice.UsageTracker.DeviceUsageEnded += new EventHandler(UsageTracker_DeviceUsageEnded);
- }
- }
-
- }
- else
- {
- Debug.Console(1, this, "WARNING: Config source list '{0}' not found for room '{1}'",
- (Room as EssentialsHuddleSpaceRoom).SourceListKey, Room.Key);
- }
- }
-
- ///
- /// Collects usage data from source and sends to Fusion
- ///
- ///
- ///
- protected void UsageTracker_DeviceUsageEnded(object sender, DeviceUsageEventArgs e)
- {
- var deviceTracker = sender as UsageTracking;
-
- var configDevice = ConfigReader.ConfigObject.Devices.Where(d => d.Key.Equals(deviceTracker.Parent));
-
- string group = ConfigReader.GetGroupForDeviceKey(deviceTracker.Parent.Key);
-
- string currentMeetingId = "-";
-
- if (CurrentMeeting != null)
- currentMeetingId = CurrentMeeting.MeetingID;
-
- //String Format: "USAGE||[Date YYYY-MM-DD]||[Time HH-mm-ss]||TIME||[Asset_Type]||[Asset_Name]||[Minutes_used]||[Asset_ID]||[Meeting_ID]"
- // [Asset_ID] property does not appear to be used in Crestron SSI examples. They are sending "-" instead so that's what is replicated here
- string deviceUsage = string.Format("USAGE||{0}||{1}||TIME||{2}||{3}||-||{4}||-||{5}||{6}||\r\n", e.UsageEndTime.ToString("yyyy-MM-dd"), e.UsageEndTime.ToString("HH:mm:ss"),
- group, deviceTracker.Parent.Name, e.MinutesUsed, "-", currentMeetingId);
-
- Debug.Console(1, this, "Device usage for: {0} ended at {1}. In use for {2} minutes", deviceTracker.Parent.Name, e.UsageEndTime, e.MinutesUsed);
-
- FusionRoom.DeviceUsage.InputSig.StringValue = deviceUsage;
-
- Debug.Console(1, this, "Device usage string: {0}", deviceUsage);
- }
-
-
- protected void TryAddRouteActionSigs(string attrName, uint attrNum, string routeKey, Device pSrc)
- {
- Debug.Console(2, this, "Creating attribute '{0}' with join {1} for source {2}",
- attrName, attrNum, pSrc.Key);
- try
- {
- var sigD = FusionRoom.CreateOffsetBoolSig(attrNum, attrName, eSigIoMask.InputOutputSig);
- // Need feedback when this source is selected
- // Event handler, added below, will compare source changes with this sig dict
- SourceToFeedbackSigs.Add(pSrc, sigD.InputSig);
-
- // And respond to selection in Fusion
- sigD.OutputSig.SetSigFalseAction(() => (Room as EssentialsHuddleSpaceRoom).RunRouteAction(routeKey));
- }
- catch (Exception)
- {
- Debug.Console(2, this, "Error creating Fusion signal {0} {1} for device '{2}'. THIS NEEDS REWORKING", attrNum, attrName, pSrc.Key);
- }
- }
-
- ///
- ///
- ///
- void SetUpCommunitcationMonitors()
- {
- uint displayNum = 0;
- uint touchpanelNum = 0;
- uint xpanelNum = 0;
-
- // Attach to all room's devices with monitors.
- //foreach (var dev in DeviceManager.Devices)
- foreach (var dev in DeviceManager.GetDevices())
- {
- if (!(dev is ICommunicationMonitor))
- continue;
-
- string attrName = null;
- uint attrNum = 1;
-
- //var keyNum = ExtractNumberFromKey(dev.Key);
- //if (keyNum == -1)
- //{
- // Debug.Console(1, this, "WARNING: Cannot link device '{0}' to numbered Fusion monitoring attributes",
- // dev.Key);
- // continue;
- //}
- //uint attrNum = Convert.ToUInt32(keyNum);
-
- // Check for UI devices
- var uiDev = dev as EssentialsTouchpanelController;
- if (uiDev != null)
- {
- if (uiDev.Panel is Crestron.SimplSharpPro.UI.XpanelForSmartGraphics)
- {
- attrNum = attrNum + touchpanelNum;
-
- if (attrNum > 10)
- continue;
- attrName = "Online - XPanel " + attrNum;
- attrNum += 160;
-
- touchpanelNum++;
- }
- else
- {
- attrNum = attrNum + xpanelNum;
-
- if (attrNum > 10)
- continue;
- attrName = "Online - Touch Panel " + attrNum;
- attrNum += 150;
-
- xpanelNum++;
- }
- }
-
- //else
- if (dev is DisplayBase)
- {
- attrNum = attrNum + displayNum;
- if (attrNum > 10)
- continue;
- attrName = "Online - Display " + attrNum;
- attrNum += 170;
-
- displayNum++;
- }
- //else if (dev is DvdDeviceBase)
- //{
- // if (attrNum > 5)
- // continue;
- // attrName = "Device Ok - DVD " + attrNum;
- // attrNum += 260;
- //}
- // add set top box
-
- // add Cresnet roll-up
-
- // add DM-devices roll-up
-
- if (attrName != null)
- {
- // Link comm status to sig and update
- var sigD = FusionRoom.CreateOffsetBoolSig(attrNum, attrName, eSigIoMask.InputSigOnly);
- var smd = dev as ICommunicationMonitor;
- sigD.InputSig.BoolValue = smd.CommunicationMonitor.Status == MonitorStatus.IsOk;
- smd.CommunicationMonitor.StatusChange += (o, a) =>
- { sigD.InputSig.BoolValue = a.Status == MonitorStatus.IsOk; };
- Debug.Console(0, this, "Linking '{0}' communication monitor to Fusion '{1}'", dev.Key, attrName);
- }
- }
- }
-
- protected virtual void SetUpDisplay()
- {
- try
- {
- //Setup Display Usage Monitoring
-
- var displays = DeviceManager.AllDevices.Where(d => d is DisplayBase);
-
- // Consider updating this in multiple display systems
-
- foreach (DisplayBase display in displays)
- {
- display.UsageTracker = new UsageTracking(display);
- display.UsageTracker.UsageIsTracked = true;
- display.UsageTracker.DeviceUsageEnded += new EventHandler(UsageTracker_DeviceUsageEnded);
- }
-
- var defaultDisplay = (Room as EssentialsHuddleSpaceRoom).DefaultDisplay as DisplayBase;
- if (defaultDisplay == null)
- {
- Debug.Console(1, this, "Cannot link null display to Fusion because default display is null");
- return;
- }
-
- var dispPowerOnAction = new Action(b => { if (!b) defaultDisplay.PowerOn(); });
- var dispPowerOffAction = new Action(b => { if (!b) defaultDisplay.PowerOff(); });
-
- // Display to fusion room sigs
- FusionRoom.DisplayPowerOn.OutputSig.UserObject = dispPowerOnAction;
- FusionRoom.DisplayPowerOff.OutputSig.UserObject = dispPowerOffAction;
- defaultDisplay.PowerIsOnFeedback.LinkInputSig(FusionRoom.DisplayPowerOn.InputSig);
- if (defaultDisplay is IDisplayUsage)
- (defaultDisplay as IDisplayUsage).LampHours.LinkInputSig(FusionRoom.DisplayUsage.InputSig);
-
-
-
- MapDisplayToRoomJoins(1, 158, defaultDisplay);
-
-
- var deviceConfig = ConfigReader.ConfigObject.Devices.FirstOrDefault(d => d.Key.Equals(defaultDisplay.Key));
-
- //Check for existing asset in GUIDs collection
-
- var tempAsset = new FusionAsset();
-
- if (FusionStaticAssets.ContainsKey(deviceConfig.Uid))
- {
- tempAsset = FusionStaticAssets[deviceConfig.Uid];
- }
- else
- {
- // Create a new asset
- tempAsset = new FusionAsset(FusionRoomGuids.GetNextAvailableAssetNumber(FusionRoom), defaultDisplay.Name, "Display", "");
- FusionStaticAssets.Add(deviceConfig.Uid, tempAsset);
- }
-
- var dispAsset = FusionRoom.CreateStaticAsset(tempAsset.SlotNumber, tempAsset.Name, "Display", tempAsset.InstanceId);
- dispAsset.PowerOn.OutputSig.UserObject = dispPowerOnAction;
- dispAsset.PowerOff.OutputSig.UserObject = dispPowerOffAction;
- defaultDisplay.PowerIsOnFeedback.LinkInputSig(dispAsset.PowerOn.InputSig);
- // NO!! display.PowerIsOn.LinkComplementInputSig(dispAsset.PowerOff.InputSig);
- // Use extension methods
- dispAsset.TrySetMakeModel(defaultDisplay);
- dispAsset.TryLinkAssetErrorToCommunication(defaultDisplay);
- }
- catch (Exception e)
- {
- Debug.Console(1, this, "Error setting up display in Fusion: {0}", e);
- }
-
- }
-
- ///
- /// Maps room attributes to a display at a specified index
- ///
- ///
- /// a
- protected virtual void MapDisplayToRoomJoins(int displayIndex, int joinOffset, DisplayBase display)
- {
- string displayName = string.Format("Display {0} - ", displayIndex);
-
-
- if (display == (Room as EssentialsHuddleSpaceRoom).DefaultDisplay)
- {
- // Display volume
- var defaultDisplayVolume = FusionRoom.CreateOffsetUshortSig(50, "Volume - Fader01", eSigIoMask.InputOutputSig);
- defaultDisplayVolume.OutputSig.UserObject = new Action(b => (display as IBasicVolumeWithFeedback).SetVolume(b));
- (display as IBasicVolumeWithFeedback).VolumeLevelFeedback.LinkInputSig(defaultDisplayVolume.InputSig);
-
- // Power on
- var defaultDisplayPowerOn = FusionRoom.CreateOffsetBoolSig((uint)joinOffset, displayName + "Power On", eSigIoMask.InputOutputSig);
- defaultDisplayPowerOn.OutputSig.UserObject = new Action(b => { if (!b) display.PowerOn(); });
- display.PowerIsOnFeedback.LinkInputSig(defaultDisplayPowerOn.InputSig);
-
- // Power Off
- var defaultDisplayPowerOff = FusionRoom.CreateOffsetBoolSig((uint)joinOffset + 1, displayName + "Power Off", eSigIoMask.InputOutputSig);
- defaultDisplayPowerOn.OutputSig.UserObject = new Action(b => { if (!b) display.PowerOff(); }); ;
- display.PowerIsOnFeedback.LinkInputSig(defaultDisplayPowerOn.InputSig);
-
- // Current Source
- var defaultDisplaySourceNone = FusionRoom.CreateOffsetBoolSig((uint)joinOffset + 8, displayName + "Source None", eSigIoMask.InputOutputSig);
- defaultDisplaySourceNone.OutputSig.UserObject = new Action(b => { if (!b) (Room as EssentialsHuddleSpaceRoom).RunRouteAction("roomOff"); }); ;
- }
- }
-
- void SetUpError()
- {
- // Roll up ALL device errors
- ErrorMessageRollUp = new StatusMonitorCollection(this);
- foreach (var dev in DeviceManager.GetDevices())
- {
- var md = dev as ICommunicationMonitor;
- if (md != null)
- {
- ErrorMessageRollUp.AddMonitor(md.CommunicationMonitor);
- Debug.Console(2, this, "Adding '{0}' to room's overall error monitor", md.CommunicationMonitor.Parent.Key);
- }
- }
- ErrorMessageRollUp.Start();
- FusionRoom.ErrorMessage.InputSig.StringValue = ErrorMessageRollUp.Message;
- ErrorMessageRollUp.StatusChange += (o, a) =>
- {
- FusionRoom.ErrorMessage.InputSig.StringValue = ErrorMessageRollUp.Message;
- };
-
- }
-
- ///
- /// Sets up a local occupancy sensor, such as one attached to a Fusion Scheduling panel. The occupancy status of the room will be read from Fusion
- ///
- void SetUpLocalOccupancy()
- {
- RoomIsOccupiedFeedback = new BoolFeedback(RoomIsOccupiedFeedbackFunc);
-
- FusionRoom.FusionAssetStateChange += new FusionAssetStateEventHandler(FusionRoom_FusionAssetStateChange);
-
- // Build Occupancy Asset?
- // Link sigs?
-
- //Room.SetRoomOccupancy(this as IOccupancyStatusProvider, 0);
-
-
- }
-
- void FusionRoom_FusionAssetStateChange(FusionBase device, FusionAssetStateEventArgs args)
- {
- if (args.EventId == FusionAssetEventId.RoomOccupiedReceivedEventId || args.EventId == FusionAssetEventId.RoomUnoccupiedReceivedEventId)
- RoomIsOccupiedFeedback.FireUpdate();
-
- }
-
- ///
- /// Sets up remote occupancy that will relay the occupancy status determined by local system devices to Fusion
- ///
- void SetUpRemoteOccupancy()
- {
-
- // Need to have the room occupancy object first and somehow determine the slot number of the Occupancy asset but will not be able to use the UID from config likely.
- // Consider defining an object just for Room Occupancy (either eAssetType.Occupancy Sensor (local) or eAssetType.RemoteOccupancySensor (from Fusion sched. panel)) and reserving slot 4 for that asset (statics would start at 5)
-
- //if (Room.OccupancyObj != null)
- //{
-
- var tempOccAsset = GUIDs.OccupancyAsset;
-
- if(tempOccAsset == null)
- {
- FusionOccSensor = new FusionOccupancySensorAsset(eAssetType.OccupancySensor);
- tempOccAsset = FusionOccSensor;
- }
-
- var occSensorAsset = FusionRoom.CreateOccupancySensorAsset(tempOccAsset.SlotNumber, tempOccAsset.Name, "Occupancy Sensor", tempOccAsset.InstanceId);
-
- occSensorAsset.RoomOccupied.AddSigToRVIFile = true;
-
- var occSensorShutdownMinutes = FusionRoom.CreateOffsetUshortSig(70, "Occ Shutdown - Minutes", eSigIoMask.InputOutputSig);
-
- // Tie to method on occupancy object
- //occSensorShutdownMinutes.OutputSig.UserObject(new Action(ushort)(b => Room.OccupancyObj.SetShutdownMinutes(b));
-
-
- Room.RoomOccupancy.RoomIsOccupiedFeedback.LinkInputSig(occSensorAsset.RoomOccupied.InputSig);
- //}
- }
-
- ///
- /// Helper to get the number from the end of a device's key string
- ///
- /// -1 if no number matched
- int ExtractNumberFromKey(string key)
- {
- var capture = System.Text.RegularExpressions.Regex.Match(key, @"\b(\d+)");
- if (!capture.Success)
- return -1;
- else return Convert.ToInt32(capture.Groups[1].Value);
- }
-
- ///
- /// Event handler for when room source changes
- ///
- protected void Room_CurrentSourceInfoChange(EssentialsRoomBase room, SourceListItem info, ChangeType type)
- {
- // Handle null. Nothing to do when switching from or to null
- if (info == null || info.SourceDevice == null)
- return;
-
- var dev = info.SourceDevice;
- if (type == ChangeType.WillChange)
- {
- if (SourceToFeedbackSigs.ContainsKey(dev))
- SourceToFeedbackSigs[dev].BoolValue = false;
- }
- else
- {
- if (SourceToFeedbackSigs.ContainsKey(dev))
- SourceToFeedbackSigs[dev].BoolValue = true;
- var name = (room == null ? "" : room.Name);
- CurrentRoomSourceNameSig.InputSig.StringValue = info.SourceDevice.Name;
- }
- }
-
- protected void FusionRoom_FusionStateChange(FusionBase device, FusionStateEventArgs args)
- {
-
- // The sig/UO method: Need separate handlers for fixed and user sigs, all flavors,
- // even though they all contain sigs.
-
- var sigData = (args.UserConfiguredSigDetail as BooleanSigDataFixedName);
- if (sigData != null)
- {
- var outSig = sigData.OutputSig;
- if (outSig.UserObject is Action)
- (outSig.UserObject as Action).Invoke(outSig.BoolValue);
- else if (outSig.UserObject is Action)
- (outSig.UserObject as Action).Invoke(outSig.UShortValue);
- else if (outSig.UserObject is Action)
- (outSig.UserObject as Action).Invoke(outSig.StringValue);
- return;
- }
-
- var attrData = (args.UserConfiguredSigDetail as BooleanSigData);
- if (attrData != null)
- {
- var outSig = attrData.OutputSig;
- if (outSig.UserObject is Action)
- (outSig.UserObject as Action).Invoke(outSig.BoolValue);
- else if (outSig.UserObject is Action)
- (outSig.UserObject as Action).Invoke(outSig.UShortValue);
- else if (outSig.UserObject is Action)
- (outSig.UserObject as Action).Invoke(outSig.StringValue);
- return;
- }
-
- }
- }
-
-
- public static class FusionRoomExtensions
- {
- ///
- /// Creates and returns a fusion attribute. The join number will match the established Simpl
- /// standard of 50+, and will generate a 50+ join in the RVI. It calls
- /// FusionRoom.AddSig with join number - 49
- ///
- /// The new attribute
- public static BooleanSigData CreateOffsetBoolSig(this FusionRoom fr, uint number, string name, eSigIoMask mask)
- {
- if (number < 50) throw new ArgumentOutOfRangeException("number", "Cannot be less than 50");
- number -= 49;
- fr.AddSig(eSigType.Bool, number, name, mask);
- return fr.UserDefinedBooleanSigDetails[number];
- }
-
- ///
- /// Creates and returns a fusion attribute. The join number will match the established Simpl
- /// standard of 50+, and will generate a 50+ join in the RVI. It calls
- /// FusionRoom.AddSig with join number - 49
- ///
- /// The new attribute
- public static UShortSigData CreateOffsetUshortSig(this FusionRoom fr, uint number, string name, eSigIoMask mask)
- {
- if (number < 50) throw new ArgumentOutOfRangeException("number", "Cannot be less than 50");
- number -= 49;
- fr.AddSig(eSigType.UShort, number, name, mask);
- return fr.UserDefinedUShortSigDetails[number];
- }
-
- ///
- /// Creates and returns a fusion attribute. The join number will match the established Simpl
- /// standard of 50+, and will generate a 50+ join in the RVI. It calls
- /// FusionRoom.AddSig with join number - 49
- ///
- /// The new attribute
- public static StringSigData CreateOffsetStringSig(this FusionRoom fr, uint number, string name, eSigIoMask mask)
- {
- if (number < 50) throw new ArgumentOutOfRangeException("number", "Cannot be less than 50");
- number -= 49;
- fr.AddSig(eSigType.String, number, name, mask);
- return fr.UserDefinedStringSigDetails[number];
- }
-
- ///
- /// Creates and returns a static asset
- ///
- /// the new asset
- public static FusionStaticAsset CreateStaticAsset(this FusionRoom fr, uint number, string name, string type, string instanceId)
- {
- Debug.Console(0, "Adding Fusion Static Asset '{0}' to slot {1} with GUID: '{2}'", name, number, instanceId);
-
- fr.AddAsset(eAssetType.StaticAsset, number, name, type, instanceId);
- return fr.UserConfigurableAssetDetails[number].Asset as FusionStaticAsset;
- }
-
- public static FusionOccupancySensor CreateOccupancySensorAsset(this FusionRoom fr, uint number, string name, string type, string instanceId)
- {
- Debug.Console(0, "Adding Fusion Occupancy Sensor Asset '{0}' to slot {1} with GUID: '{2}'", name, number, instanceId);
-
- fr.AddAsset(eAssetType.OccupancySensor, number, name, type, instanceId);
- return fr.UserConfigurableAssetDetails[number].Asset as FusionOccupancySensor;
- }
- }
-
- //************************************************************************************************
- ///
- /// Extensions to enhance Fusion room, asset and signal creation.
- ///
- public static class FusionStaticAssetExtensions
- {
- ///
- /// Tries to set a Fusion asset with the make and model of a device.
- /// If the provided Device is IMakeModel, will set the corresponding parameters on the fusion static asset.
- /// Otherwise, does nothing.
- ///
- public static void TrySetMakeModel(this FusionStaticAsset asset, Device device)
- {
- var mm = device as IMakeModel;
- if (mm != null)
- {
- asset.ParamMake.Value = mm.DeviceMake;
- asset.ParamModel.Value = mm.DeviceModel;
- }
- }
-
- ///
- /// Tries to attach the AssetError input on a Fusion asset to a Device's
- /// CommunicationMonitor.StatusChange event. Does nothing if the device is not
- /// IStatusMonitor
- ///
- ///
- ///
- public static void TryLinkAssetErrorToCommunication(this FusionStaticAsset asset, Device device)
- {
- if (device is ICommunicationMonitor)
- {
- var monitor = (device as ICommunicationMonitor).CommunicationMonitor;
- monitor.StatusChange += (o, a) =>
- {
- // Link connected and error inputs on asset
- asset.Connected.InputSig.BoolValue = a.Status == MonitorStatus.IsOk;
- asset.AssetError.InputSig.StringValue = a.Status.ToString();
- };
- // set current value
- asset.Connected.InputSig.BoolValue = monitor.Status == MonitorStatus.IsOk;
- asset.AssetError.InputSig.StringValue = monitor.Status.ToString();
- }
- }
- }
-
-
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+using Crestron.SimplSharp;
+using Crestron.SimplSharp.CrestronIO;
+using Crestron.SimplSharp.CrestronXml;
+using Crestron.SimplSharp.CrestronXml.Serialization;
+using Crestron.SimplSharp.CrestronXmlLinq;
+using Crestron.SimplSharpPro;
+using Crestron.SimplSharpPro.DeviceSupport;
+using Crestron.SimplSharpPro.Fusion;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+
+using PepperDash.Core;
+using PepperDash.Essentials;
+using PepperDash.Essentials.Core;
+using PepperDash.Essentials.Core.Config;
+using PepperDash.Essentials.Devices.Common;
+using PepperDash.Essentials.Devices.Common.Occupancy;
+
+
+
+namespace PepperDash.Essentials.Fusion
+{
+ public class EssentialsHuddleSpaceFusionSystemControllerBase : Device, IOccupancyStatusProvider
+ {
+ public event EventHandler ScheduleChange;
+ //public event EventHandler MeetingEndWarning;
+ //public event EventHandler NextMeetingBeginWarning;
+
+ public event EventHandler RoomInfoChange;
+
+ public FusionCustomPropertiesBridge CustomPropertiesBridge = new FusionCustomPropertiesBridge();
+
+ protected FusionRoom FusionRoom;
+ protected EssentialsRoomBase Room;
+ Dictionary SourceToFeedbackSigs =
+ new Dictionary();
+
+ StatusMonitorCollection ErrorMessageRollUp;
+
+ protected StringSigData CurrentRoomSourceNameSig;
+
+ #region System Info Sigs
+ //StringSigData SystemName;
+ //StringSigData Model;
+ //StringSigData SerialNumber;
+ //StringSigData Uptime;
+ #endregion
+
+
+ #region Processor Info Sigs
+ StringSigData Ip1;
+ StringSigData Ip2;
+ StringSigData Gateway;
+ StringSigData Hostname;
+ StringSigData Domain;
+ StringSigData Dns1;
+ StringSigData Dns2;
+ StringSigData Mac1;
+ StringSigData Mac2;
+ StringSigData NetMask1;
+ StringSigData NetMask2;
+ StringSigData Firmware;
+
+ StringSigData[] Program = new StringSigData[10];
+ #endregion
+
+ #region Default Display Source Sigs
+
+ BooleanSigData[] Source = new BooleanSigData[10];
+
+ #endregion
+
+ RoomSchedule CurrentSchedule;
+
+ Event NextMeeting;
+
+ Event CurrentMeeting;
+
+ protected string RoomGuid
+ {
+ get
+ {
+ return GUIDs.RoomGuid;
+ }
+
+ }
+
+ uint IpId;
+
+ FusionRoomGuids GUIDs;
+
+ bool GuidFileExists;
+
+ bool IsRegisteredForSchedulePushNotifications = false;
+
+ CTimer PollTimer = null;
+
+ CTimer PushNotificationTimer = null;
+
+ CTimer DailyTimeRequestTimer = null;
+
+ // Default poll time is 5 min unless overridden by config value
+ public long SchedulePollInterval = 300000;
+
+ public long PushNotificationTimeout = 5000;
+
+ protected Dictionary FusionStaticAssets;
+
+ // For use with local occ sensor devices which will relay to Fusion the current occupancy status
+ protected FusionRemoteOccupancySensor FusionRemoteOccSensor;
+
+ // For use with occ sensor attached to a scheduling panel in Fusion
+ protected FusionOccupancySensorAsset FusionOccSensor;
+
+ public BoolFeedback RoomIsOccupiedFeedback { get; private set; }
+
+ protected Func RoomIsOccupiedFeedbackFunc
+ {
+ get
+ {
+ return () => FusionRemoteOccSensor.RoomOccupied.OutputSig.BoolValue;
+ }
+ }
+
+ //ScheduleResponseEvent NextMeeting;
+
+ public EssentialsHuddleSpaceFusionSystemControllerBase(EssentialsRoomBase room, uint ipId)
+ : base(room.Key + "-fusion")
+ {
+
+ try
+ {
+
+ Room = room;
+
+ IpId = ipId;
+
+ FusionStaticAssets = new Dictionary();
+
+ GUIDs = new FusionRoomGuids();
+
+ var mac = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_MAC_ADDRESS, 0);
+
+ var slot = Global.ControlSystem.ProgramNumber;
+
+ string guidFilePath = Global.FilePathPrefix + string.Format(@"{0}-FusionGuids.json", InitialParametersClass.ProgramIDTag);
+
+ GuidFileExists = File.Exists(guidFilePath);
+
+ // Check if file exists
+ if (!GuidFileExists)
+ {
+ // Does not exist. Create GUIDs
+ GUIDs = new FusionRoomGuids(Room.Name, ipId, GUIDs.GenerateNewRoomGuid(slot, mac), FusionStaticAssets);
+ }
+ else
+ {
+ // Exists. Read GUIDs
+ ReadGuidFile(guidFilePath);
+ }
+
+ CreateSymbolAndBasicSigs(IpId);
+ SetUpSources();
+ SetUpCommunitcationMonitors();
+ SetUpDisplay();
+ SetUpError();
+ ExecuteCustomSteps();
+
+ if (Room.RoomOccupancy != null)
+ {
+ if (Room.OccupancyStatusProviderIsRemote)
+ SetUpRemoteOccupancy();
+ else
+ {
+ SetUpLocalOccupancy();
+ }
+ }
+
+ // Make it so!
+ FusionRVI.GenerateFileForAllFusionDevices();
+
+ GenerateGuidFile(guidFilePath);
+ }
+ catch (Exception e)
+ {
+ Debug.Console(0, this, Debug.ErrorLogLevel.Error, "Error Building Fusion System Controller: {0}", e);
+ }
+ }
+
+ ///
+ /// Used for extension classes to execute whatever steps are necessary before generating the RVI and GUID files
+ ///
+ protected virtual void ExecuteCustomSteps()
+ {
+
+ }
+
+ ///
+ /// Generates the guid file in NVRAM. If the file already exists it will be overwritten.
+ ///
+ /// path for the file
+ void GenerateGuidFile(string filePath)
+ {
+ if (string.IsNullOrEmpty(filePath))
+ {
+ Debug.Console(0, this, "Error writing guid file. No path specified.");
+ return;
+ }
+
+ CCriticalSection _fileLock = new CCriticalSection();
+
+ try
+ {
+ if (_fileLock == null || _fileLock.Disposed)
+ return;
+
+ _fileLock.Enter();
+
+ Debug.Console(1, this, "Writing GUIDs to file");
+
+ if (FusionOccSensor == null)
+ GUIDs = new FusionRoomGuids(Room.Name, IpId, RoomGuid, FusionStaticAssets);
+ else
+ GUIDs = new FusionRoomGuids(Room.Name, IpId, RoomGuid, FusionStaticAssets, FusionOccSensor);
+
+ var JSON = JsonConvert.SerializeObject(GUIDs, Newtonsoft.Json.Formatting.Indented);
+
+ using (StreamWriter sw = new StreamWriter(filePath))
+ {
+ sw.Write(JSON);
+ sw.Flush();
+ }
+
+ Debug.Console(1, this, "Guids successfully written to file '{0}'", filePath);
+
+ }
+ catch (Exception e)
+ {
+ Debug.Console(0, this, "Error writing guid file: {0}", e);
+ }
+ finally
+ {
+ if (_fileLock != null && !_fileLock.Disposed)
+ _fileLock.Leave();
+ }
+ }
+
+ ///
+ /// Reads the guid file from NVRAM
+ ///
+ /// path for te file
+ void ReadGuidFile(string filePath)
+ {
+ if(string.IsNullOrEmpty(filePath))
+ {
+ Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Error reading guid file. No path specified.");
+ return;
+ }
+
+ CCriticalSection _fileLock = new CCriticalSection();
+
+ try
+ {
+ if(_fileLock == null || _fileLock.Disposed)
+ return;
+
+ _fileLock.Enter();
+
+ if(File.Exists(filePath))
+ {
+ var JSON = File.ReadToEnd(filePath, Encoding.ASCII);
+
+ GUIDs = JsonConvert.DeserializeObject(JSON);
+
+ IpId = GUIDs.IpId;
+
+ FusionStaticAssets = GUIDs.StaticAssets;
+
+ }
+
+ Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Fusion Guids successfully read from file: {0}", filePath);
+
+ Debug.Console(1, this, "\nRoom Name: {0}\nIPID: {1:x}\n RoomGuid: {2}", Room.Name, IpId, RoomGuid);
+
+ foreach (var item in FusionStaticAssets)
+ {
+ Debug.Console(1, this, "\nAsset Name: {0}\nAsset No: {1}\n Guid: {2}", item.Value.Name, item.Value.SlotNumber, item.Value.InstanceId);
+ }
+ }
+ catch (Exception e)
+ {
+ Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Error reading guid file: {0}", e);
+ }
+ finally
+ {
+ if(_fileLock != null && !_fileLock.Disposed)
+ _fileLock.Leave();
+ }
+
+ }
+
+ protected virtual void CreateSymbolAndBasicSigs(uint ipId)
+ {
+ Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Creating Fusion Room symbol with GUID: {0}", RoomGuid);
+
+ FusionRoom = new FusionRoom(ipId, Global.ControlSystem, Room.Name, RoomGuid);
+ FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.Use();
+ FusionRoom.ExtenderFusionRoomDataReservedSigs.Use();
+
+ FusionRoom.Register();
+
+ FusionRoom.FusionStateChange += new FusionStateEventHandler(FusionRoom_FusionStateChange);
+
+ FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.DeviceExtenderSigChange += new DeviceExtenderJoinChangeEventHandler(FusionRoomSchedule_DeviceExtenderSigChange);
+ FusionRoom.ExtenderFusionRoomDataReservedSigs.DeviceExtenderSigChange += new DeviceExtenderJoinChangeEventHandler(ExtenderFusionRoomDataReservedSigs_DeviceExtenderSigChange);
+ FusionRoom.OnlineStatusChange += new OnlineStatusChangeEventHandler(FusionRoom_OnlineStatusChange);
+
+ CrestronConsole.AddNewConsoleCommand(RequestFullRoomSchedule, "FusReqRoomSchedule", "Requests schedule of the room for the next 24 hours", ConsoleAccessLevelEnum.AccessOperator);
+ CrestronConsole.AddNewConsoleCommand(ModifyMeetingEndTimeConsoleHelper, "FusReqRoomSchMod", "Ends or extends a meeting by the specified time", ConsoleAccessLevelEnum.AccessOperator);
+ CrestronConsole.AddNewConsoleCommand(CreateAsHocMeeting, "FusCreateMeeting", "Creates and Ad Hoc meeting for on hour or until the next meeting", ConsoleAccessLevelEnum.AccessOperator);
+
+ // Room to fusion room
+ Room.OnFeedback.LinkInputSig(FusionRoom.SystemPowerOn.InputSig);
+
+ // Moved to
+ CurrentRoomSourceNameSig = FusionRoom.CreateOffsetStringSig(84, "Display 1 - Current Source", eSigIoMask.InputSigOnly);
+ // Don't think we need to get current status of this as nothing should be alive yet.
+ (Room as EssentialsHuddleSpaceRoom).CurrentSingleSourceChange += new SourceInfoChangeHandler(Room_CurrentSourceInfoChange);
+
+
+ FusionRoom.SystemPowerOn.OutputSig.SetSigFalseAction((Room as EssentialsHuddleSpaceRoom).PowerOnToDefaultOrLastSource);
+ FusionRoom.SystemPowerOff.OutputSig.SetSigFalseAction(() => (Room as EssentialsHuddleSpaceRoom).RunRouteAction("roomOff"));
+ // NO!! room.RoomIsOn.LinkComplementInputSig(FusionRoom.SystemPowerOff.InputSig);
+ FusionRoom.ErrorMessage.InputSig.StringValue =
+ "3: 7 Errors: This is a really long error message;This is a really long error message;This is a really long error message;This is a really long error message;This is a really long error message;This is a really long error message;This is a really long error message;";
+
+ SetUpEthernetValues();
+
+ GetProcessorEthernetValues();
+
+ GetSystemInfo();
+
+ GetProcessorInfo();
+
+ CrestronEnvironment.EthernetEventHandler += new EthernetEventHandler(CrestronEnvironment_EthernetEventHandler);
+ }
+
+ protected void CrestronEnvironment_EthernetEventHandler(EthernetEventArgs ethernetEventArgs)
+ {
+ if (ethernetEventArgs.EthernetEventType == eEthernetEventType.LinkUp)
+ {
+ GetProcessorEthernetValues();
+ }
+ }
+
+ protected void GetSystemInfo()
+ {
+ //SystemName.InputSig.StringValue = Room.Name;
+ //Model.InputSig.StringValue = InitialParametersClass.ControllerPromptName;
+ //SerialNumber.InputSig.StringValue = InitialParametersClass.
+
+ string response = string.Empty;
+
+ var systemReboot = FusionRoom.CreateOffsetBoolSig(74, "Processor - Reboot", eSigIoMask.OutputSigOnly);
+ systemReboot.OutputSig.SetSigFalseAction(() => CrestronConsole.SendControlSystemCommand("reboot", ref response));
+ }
+
+ protected void SetUpEthernetValues()
+ {
+ Ip1 = FusionRoom.CreateOffsetStringSig(50, "Info - Processor - IP 1", eSigIoMask.InputSigOnly);
+ Ip2 = FusionRoom.CreateOffsetStringSig(51, "Info - Processor - IP 2", eSigIoMask.InputSigOnly);
+ Gateway = FusionRoom.CreateOffsetStringSig(52, "Info - Processor - Gateway", eSigIoMask.InputSigOnly);
+ Hostname = FusionRoom.CreateOffsetStringSig(53, "Info - Processor - Hostname", eSigIoMask.InputSigOnly);
+ Domain = FusionRoom.CreateOffsetStringSig(54, "Info - Processor - Domain", eSigIoMask.InputSigOnly);
+ Dns1 = FusionRoom.CreateOffsetStringSig(55, "Info - Processor - DNS 1", eSigIoMask.InputSigOnly);
+ Dns2 = FusionRoom.CreateOffsetStringSig(56, "Info - Processor - DNS 2", eSigIoMask.InputSigOnly);
+ Mac1 = FusionRoom.CreateOffsetStringSig(57, "Info - Processor - MAC 1", eSigIoMask.InputSigOnly);
+ Mac2 = FusionRoom.CreateOffsetStringSig(58, "Info - Processor - MAC 2", eSigIoMask.InputSigOnly);
+ NetMask1 = FusionRoom.CreateOffsetStringSig(59, "Info - Processor - Net Mask 1", eSigIoMask.InputSigOnly);
+ NetMask2 = FusionRoom.CreateOffsetStringSig(60, "Info - Processor - Net Mask 2", eSigIoMask.InputSigOnly);
+ }
+
+ protected void GetProcessorEthernetValues()
+ {
+ Ip1.InputSig.StringValue = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, 0);
+ Gateway.InputSig.StringValue = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_ROUTER, 0);
+ Hostname.InputSig.StringValue = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_HOSTNAME, 0);
+ Domain.InputSig.StringValue = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_DOMAIN_NAME, 0);
+
+ var dnsServers = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_DNS_SERVER, 0).Split(',');
+ Dns1.InputSig.StringValue = dnsServers[0];
+ if (dnsServers.Length > 1)
+ Dns2.InputSig.StringValue = dnsServers[1];
+
+ Mac1.InputSig.StringValue = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_MAC_ADDRESS, 0);
+ NetMask1.InputSig.StringValue = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_MASK, 0);
+
+ // Interface 1
+
+ if (InitialParametersClass.NumberOfEthernetInterfaces > 1) // Only get these values if the processor has more than 1 NIC
+ {
+ Ip2.InputSig.StringValue = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, 1);
+ Mac2.InputSig.StringValue = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_MAC_ADDRESS, 1);
+ NetMask2.InputSig.StringValue = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_MASK, 1);
+ }
+ }
+
+ protected void GetProcessorInfo()
+ {
+
+ Firmware = FusionRoom.CreateOffsetStringSig(61, "Info - Processor - Firmware", eSigIoMask.InputSigOnly);
+
+ if (CrestronEnvironment.DevicePlatform != eDevicePlatform.Server)
+ {
+ for (int i = 0; i < Global.ControlSystem.NumProgramsSupported; i++)
+ {
+ var join = 62 + i;
+ var progNum = i + 1;
+ Program[i] = FusionRoom.CreateOffsetStringSig((uint)join, string.Format("Info - Processor - Program {0}", progNum), eSigIoMask.InputSigOnly);
+ }
+ }
+
+ Firmware.InputSig.StringValue = InitialParametersClass.FirmwareVersion;
+
+ }
+
+ protected void GetCustomProperties()
+ {
+ if (FusionRoom.IsOnline)
+ {
+ string fusionRoomCustomPropertiesRequest = @"RoomConfigurationRequest";
+
+ FusionRoom.ExtenderFusionRoomDataReservedSigs.RoomConfigQuery.StringValue = fusionRoomCustomPropertiesRequest;
+ }
+ }
+
+ void GetTouchpanelInfo()
+ {
+ // TODO Get IP and Project Name from TP
+ }
+
+ protected void FusionRoom_OnlineStatusChange(GenericBase currentDevice, OnlineOfflineEventArgs args)
+ {
+ if (args.DeviceOnLine)
+ {
+ CrestronEnvironment.Sleep(200);
+
+ // Send Push Notification Action request:
+
+ string requestID = "InitialPushRequest";
+
+
+ string actionRequest =
+ string.Format("\n{0}\n", requestID) +
+ "RegisterPushModel\n" +
+ "\n" +
+ "\n" +
+ "\n" +
+ "\n" +
+ "\n" +
+ "\n" +
+ "\n" +
+ "\n" +
+ "\n" +
+ "\n" +
+ "\n" +
+ "\n" +
+ "\n" +
+ "\n" +
+ "\n" +
+ "\n" +
+ "\n" +
+ "\n" +
+ "\n" +
+ "\n" +
+ "\n";
+
+ Debug.Console(2, this, "Sending Fusion ActionRequest: \n{0}", actionRequest);
+
+ FusionRoom.ExtenderFusionRoomDataReservedSigs.ActionQuery.StringValue = actionRequest;
+
+ GetCustomProperties();
+
+ // Request current Fusion Server Time
+ RequestLocalDateTime(null);
+
+ // Setup timer to request time daily
+ if (DailyTimeRequestTimer != null && !DailyTimeRequestTimer.Disposed)
+ {
+ DailyTimeRequestTimer.Stop();
+ DailyTimeRequestTimer.Dispose();
+ }
+
+ DailyTimeRequestTimer = new CTimer(RequestLocalDateTime, null, 86400000, 86400000);
+
+ DailyTimeRequestTimer.Reset(86400000, 86400000);
+ }
+
+ }
+
+ ///
+ /// Requests the local date and time from the Fusion Server
+ ///
+ ///
+ public void RequestLocalDateTime(object callbackObject)
+ {
+ string timeRequestID = "TimeRequest";
+
+ string timeRequest = string.Format("{0}", timeRequestID);
+
+ FusionRoom.ExtenderFusionRoomDataReservedSigs.LocalDateTimeQuery.StringValue = timeRequest;
+ }
+
+ ///
+ /// Generates a room schedule request for this room for the next 24 hours.
+ ///
+ /// string identifying this request. Used with a corresponding ScheduleResponse value
+ public void RequestFullRoomSchedule(object callbackObject)
+ {
+ DateTime now = DateTime.Today;
+
+ string currentTime = now.ToString("s");
+
+ string requestTest =
+ string.Format("FullSchedleRequest{0}{1}24", RoomGuid, currentTime);
+
+ Debug.Console(2, this, "Sending Fusion ScheduleQuery: \n{0}", requestTest);
+
+ FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.ScheduleQuery.StringValue = requestTest;
+
+ if (IsRegisteredForSchedulePushNotifications)
+ PushNotificationTimer.Stop();
+ }
+
+ ///
+ /// Wrapper method to allow console commands to modify the current meeting end time
+ ///
+ /// meetingID extendTime
+ public void ModifyMeetingEndTimeConsoleHelper(string command)
+ {
+ string requestID;
+ string meetingID = null;
+ int extendMinutes = -1;
+
+ requestID = "ModifyMeetingTest12345";
+
+ try
+ {
+ var tokens = command.Split(' ');
+
+ meetingID = tokens[0];
+ extendMinutes = Int32.Parse(tokens[1]);
+
+ }
+ catch (Exception e)
+ {
+ Debug.Console(1, this, "Error parsing console command: {0}", e);
+ }
+
+ ModifyMeetingEndTime(requestID, extendMinutes);
+
+ }
+
+ ///
+ /// Ends or Extends the current meeting by the specified number of minutes.
+ ///
+ /// Number of minutes to extend the meeting. A value of 0 will end the meeting.
+ public void ModifyMeetingEndTime(string requestID, int extendMinutes)
+ {
+ if(CurrentMeeting == null)
+ {
+ Debug.Console(1, this, "No meeting in progress. Unable to modify end time.");
+ return;
+ }
+
+ if (extendMinutes > -1)
+ {
+ if(extendMinutes > 0)
+ {
+ var extendTime = CurrentMeeting.dtEnd - DateTime.Now;
+ double extendMinutesRaw = extendTime.TotalMinutes;
+
+ extendMinutes = extendMinutes + (int)Math.Round(extendMinutesRaw);
+ }
+
+
+ string requestTest = string.Format(
+ "{0}{1}MeetingChange"
+ , requestID, RoomGuid, CurrentMeeting.MeetingID, extendMinutes);
+
+ Debug.Console(1, this, "Sending MeetingChange Request: \n{0}", requestTest);
+
+ FusionRoom.ExtenderFusionRoomDataReservedSigs.ActionQuery.StringValue = requestTest;
+ }
+ else
+ {
+ Debug.Console(1, this, "Invalid time specified");
+ }
+
+
+ }
+
+ ///
+ /// Creates and Ad Hoc meeting with a duration of 1 hour, or until the next meeting if in less than 1 hour.
+ ///
+ public void CreateAsHocMeeting(string command)
+ {
+ string requestID = "CreateAdHocMeeting";
+
+ DateTime now = DateTime.Now.AddMinutes(1);
+
+ now.AddSeconds(-now.Second);
+
+ // Assume 1 hour meeting if possible
+ DateTime dtEnd = now.AddHours(1);
+
+ // Check if room is available for 1 hour before next meeting
+ if (NextMeeting != null)
+ {
+ var roomAvailable = NextMeeting.dtEnd.Subtract(dtEnd);
+
+ if (roomAvailable.TotalMinutes < 60)
+ {
+ /// Room not available for full hour, book until next meeting starts
+ dtEnd = NextMeeting.dtEnd;
+ }
+ }
+
+ string createMeetingRequest =
+ "" +
+ string.Format("{0}", requestID) +
+ string.Format("{0}", RoomGuid) +
+ "" +
+ string.Format("{0}", now.ToString("s")) +
+ string.Format("{0}", dtEnd.ToString("s")) +
+ "AdHoc Meeting" +
+ "Room User" +
+ "Example Message" +
+ "" +
+ "";
+
+ Debug.Console(2, this, "Sending CreateMeeting Request: \n{0}", createMeetingRequest);
+
+ FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.CreateMeeting.StringValue = createMeetingRequest;
+
+ //Debug.Console(1, this, "Sending CreateMeeting Request: \n{0}", command);
+
+ //FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.CreateMeeting.StringValue = command;
+
+ }
+
+ ///
+ /// Event handler method for Device Extender sig changes
+ ///
+ ///
+ ///
+ protected void ExtenderFusionRoomDataReservedSigs_DeviceExtenderSigChange(DeviceExtender currentDeviceExtender, SigEventArgs args)
+ {
+ Debug.Console(2, this, "Event: {0}\n Sig: {1}\nFusionResponse:\n{2}", args.Event, args.Sig.Name, args.Sig.StringValue);
+
+
+ if (args.Sig == FusionRoom.ExtenderFusionRoomDataReservedSigs.ActionQueryResponse)
+ {
+ try
+ {
+ XmlDocument message = new XmlDocument();
+
+ message.LoadXml(args.Sig.StringValue);
+
+ var actionResponse = message["ActionResponse"];
+
+ if (actionResponse != null)
+ {
+ var requestID = actionResponse["RequestID"];
+
+ if (requestID.InnerText == "InitialPushRequest")
+ {
+ if (actionResponse["ActionID"].InnerText == "RegisterPushModel")
+ {
+ var parameters = actionResponse["Parameters"];
+
+ foreach (XmlElement parameter in parameters)
+ {
+ if (parameter.HasAttributes)
+ {
+ var attributes = parameter.Attributes;
+
+ if (attributes["ID"].Value == "Registered")
+ {
+ var isRegistered = Int32.Parse(attributes["Value"].Value);
+
+ if (isRegistered == 1)
+ {
+ IsRegisteredForSchedulePushNotifications = true;
+
+ if (PollTimer != null && !PollTimer.Disposed)
+ {
+ PollTimer.Stop();
+ PollTimer.Dispose();
+ }
+
+ PushNotificationTimer = new CTimer(RequestFullRoomSchedule, null, PushNotificationTimeout, PushNotificationTimeout);
+
+ PushNotificationTimer.Reset(PushNotificationTimeout, PushNotificationTimeout);
+ }
+ else if (isRegistered == 0)
+ {
+ IsRegisteredForSchedulePushNotifications = false;
+
+ if (PushNotificationTimer != null && !PushNotificationTimer.Disposed)
+ {
+ PushNotificationTimer.Stop();
+ PushNotificationTimer.Dispose();
+ }
+
+ PollTimer = new CTimer(RequestFullRoomSchedule, null, SchedulePollInterval, SchedulePollInterval);
+
+ PollTimer.Reset(SchedulePollInterval, SchedulePollInterval);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ Debug.Console(1, this, "Error parsing ActionQueryResponse: {0}", e);
+ }
+ }
+ else if (args.Sig == FusionRoom.ExtenderFusionRoomDataReservedSigs.LocalDateTimeQueryResponse)
+ {
+ try
+ {
+ XmlDocument message = new XmlDocument();
+
+ message.LoadXml(args.Sig.StringValue);
+
+ var localDateTimeResponse = message["LocalTimeResponse"];
+
+ if (localDateTimeResponse != null)
+ {
+ var localDateTime = localDateTimeResponse["LocalDateTime"];
+
+ if (localDateTime != null)
+ {
+ var tempLocalDateTime = localDateTime.InnerText;
+
+ DateTime currentTime = DateTime.Parse(tempLocalDateTime);
+
+ Debug.Console(1, this, "DateTime from Fusion Server: {0}", currentTime);
+
+ // Parse time and date from response and insert values
+ CrestronEnvironment.SetTimeAndDate((ushort)currentTime.Hour, (ushort)currentTime.Minute, (ushort)currentTime.Second, (ushort)currentTime.Month, (ushort)currentTime.Day, (ushort)currentTime.Year);
+
+ Debug.Console(1, this, "Processor time set to {0}", CrestronEnvironment.GetLocalTime());
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ Debug.Console(1, this, "Error parsing LocalDateTimeQueryResponse: {0}", e);
+ }
+ }
+ else if (args.Sig == FusionRoom.ExtenderFusionRoomDataReservedSigs.RoomConfigResponse)
+ {
+ // Room info response with custom properties
+
+ string roomConfigResponseArgs = args.Sig.StringValue.Replace("&", "and");
+
+ Debug.Console(1, this, "Fusion Response: \n {0}", roomConfigResponseArgs);
+
+ try
+ {
+ XmlDocument roomConfigResponse = new XmlDocument();
+
+ roomConfigResponse.LoadXml(roomConfigResponseArgs);
+
+ var requestRoomConfiguration = roomConfigResponse["RoomConfigurationResponse"];
+
+ if (requestRoomConfiguration != null)
+ {
+ RoomInformation roomInformation = new RoomInformation();
+
+ foreach (XmlElement e in roomConfigResponse.FirstChild.ChildNodes)
+ {
+ if (e.Name == "RoomInformation")
+ {
+ XmlReader roomInfo = new XmlReader(e.OuterXml);
+
+ roomInformation = CrestronXMLSerialization.DeSerializeObject(roomInfo);
+ }
+ else if (e.Name == "CustomFields")
+ {
+ foreach (XmlElement el in e)
+ {
+ FusionCustomProperty customProperty = new FusionCustomProperty();
+
+ if (el.Name == "CustomField")
+ {
+ customProperty.ID = el.Attributes["ID"].Value;
+ }
+
+ foreach (XmlElement elm in el)
+ {
+ if (elm.Name == "CustomFieldName")
+ {
+ customProperty.CustomFieldName = elm.InnerText;
+ }
+ if (elm.Name == "CustomFieldType")
+ {
+ customProperty.CustomFieldType = elm.InnerText;
+ }
+ if (elm.Name == "CustomFieldValue")
+ {
+ customProperty.CustomFieldValue = elm.InnerText;
+ }
+ }
+
+ roomInformation.FusionCustomProperties.Add(customProperty);
+ }
+ }
+ }
+
+ var handler = RoomInfoChange;
+ if (handler != null)
+ handler(this, new EventArgs());
+
+ CustomPropertiesBridge.EvaluateRoomInfo(Room.Key, roomInformation);
+ }
+ }
+ catch (Exception e)
+ {
+ Debug.Console(1, this, "Error parsing Custom Properties response: {0}", e);
+ }
+ //PrintRoomInfo();
+ //getRoomInfoBusy = false;
+ //_DynFusion.API.EISC.BooleanInput[Constants.GetRoomInfo].BoolValue = getRoomInfoBusy;
+ }
+
+ }
+
+ ///
+ /// Event handler method for Device Extender sig changes
+ ///
+ ///
+ ///
+ protected void FusionRoomSchedule_DeviceExtenderSigChange(DeviceExtender currentDeviceExtender, SigEventArgs args)
+ {
+ Debug.Console(2, this, "Scehdule Response Event: {0}\n Sig: {1}\nFusionResponse:\n{2}", args.Event, args.Sig.Name, args.Sig.StringValue);
+
+
+ if (args.Sig == FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.ScheduleResponse)
+ {
+ try
+ {
+ ScheduleResponse scheduleResponse = new ScheduleResponse();
+
+ XmlDocument message = new XmlDocument();
+
+ message.LoadXml(args.Sig.StringValue);
+
+ var response = message["ScheduleResponse"];
+
+ if (response != null)
+ {
+ // Check for push notification
+ if (response["RequestID"].InnerText == "RVRequest")
+ {
+ var action = response["Action"];
+
+ if (action.OuterXml.IndexOf("RequestSchedule") > -1)
+ {
+ PushNotificationTimer.Reset(PushNotificationTimeout, PushNotificationTimeout);
+ }
+ }
+ else // Not a push notification
+ {
+ CurrentSchedule = new RoomSchedule(); // Clear Current Schedule
+ CurrentMeeting = null; // Clear Current Meeting
+ NextMeeting = null; // Clear Next Meeting
+
+ bool isNextMeeting = false;
+
+ foreach (XmlElement element in message.FirstChild.ChildNodes)
+ {
+ if (element.Name == "RequestID")
+ {
+ scheduleResponse.RequestID = element.InnerText;
+ }
+ else if (element.Name == "RoomID")
+ {
+ scheduleResponse.RoomID = element.InnerText;
+ }
+ else if (element.Name == "RoomName")
+ {
+ scheduleResponse.RoomName = element.InnerText;
+ }
+ else if (element.Name == "Event")
+ {
+ Debug.Console(2, this, "Event Found:\n{0}", element.OuterXml);
+
+ XmlReader reader = new XmlReader(element.OuterXml);
+
+ Event tempEvent = new Event();
+
+ tempEvent = CrestronXMLSerialization.DeSerializeObject(reader);
+
+ scheduleResponse.Events.Add(tempEvent);
+
+ // Check is this is the current event
+ if (tempEvent.dtStart <= DateTime.Now && tempEvent.dtEnd >= DateTime.Now)
+ {
+ CurrentMeeting = tempEvent; // Set Current Meeting
+ isNextMeeting = true; // Flag that next element is next meeting
+ }
+
+ if (isNextMeeting)
+ {
+ NextMeeting = tempEvent; // Set Next Meeting
+ isNextMeeting = false;
+ }
+
+ CurrentSchedule.Meetings.Add(tempEvent);
+ }
+
+ }
+
+ PrintTodaysSchedule();
+
+ if (!IsRegisteredForSchedulePushNotifications)
+ PollTimer.Reset(SchedulePollInterval, SchedulePollInterval);
+
+ // Fire Schedule Change Event
+ var handler = ScheduleChange;
+
+ if (handler != null)
+ {
+ handler(this, new ScheduleChangeEventArgs() { Schedule = CurrentSchedule });
+ }
+
+ }
+ }
+
+
+
+ }
+ catch (Exception e)
+ {
+ Debug.Console(1, this, "Error parsing ScheduleResponse: {0}", e);
+ }
+ }
+ else if (args.Sig == FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.CreateResponse)
+ {
+ Debug.Console(2, this, "Create Meeting Response Event: {0}\n Sig: {1}\nFusionResponse:\n{2}", args.Event, args.Sig.Name, args.Sig.StringValue);
+ }
+
+ }
+
+ ///
+ /// Prints today's schedule to console for debugging
+ ///
+ void PrintTodaysSchedule()
+ {
+ if (Debug.Level > 1)
+ {
+ if (CurrentSchedule.Meetings.Count > 0)
+ {
+ Debug.Console(1, this, "Today's Schedule for '{0}'\n", Room.Name);
+
+ foreach (Event e in CurrentSchedule.Meetings)
+ {
+ Debug.Console(1, this, "Subject: {0}", e.Subject);
+ Debug.Console(1, this, "Organizer: {0}", e.Organizer);
+ Debug.Console(1, this, "MeetingID: {0}", e.MeetingID);
+ Debug.Console(1, this, "Start Time: {0}", e.dtStart);
+ Debug.Console(1, this, "End Time: {0}", e.dtEnd);
+ Debug.Console(1, this, "Duration: {0}\n", e.DurationInMinutes);
+ }
+ }
+ }
+ }
+
+ protected virtual void SetUpSources()
+ {
+ // Sources
+ var dict = ConfigReader.ConfigObject.GetSourceListForKey((Room as EssentialsHuddleSpaceRoom).SourceListKey);
+ if (dict != null)
+ {
+ // NEW PROCESS:
+ // Make these lists and insert the fusion attributes by iterating these
+ var setTopBoxes = dict.Where(d => d.Value.SourceDevice is ISetTopBoxControls);
+ uint i = 1;
+ foreach (var kvp in setTopBoxes)
+ {
+ TryAddRouteActionSigs("Display 1 - Source TV " + i, 188 + i, kvp.Key, kvp.Value.SourceDevice);
+ i++;
+ if (i > 5) // We only have five spots
+ break;
+ }
+
+ var discPlayers = dict.Where(d => d.Value.SourceDevice is IDiscPlayerControls);
+ i = 1;
+ foreach (var kvp in discPlayers)
+ {
+ TryAddRouteActionSigs("Display 1 - Source DVD " + i, 181 + i, kvp.Key, kvp.Value.SourceDevice);
+ i++;
+ if (i > 5) // We only have five spots
+ break;
+ }
+
+ var laptops = dict.Where(d => d.Value.SourceDevice is Laptop);
+ i = 1;
+ foreach (var kvp in laptops)
+ {
+ TryAddRouteActionSigs("Display 1 - Source Laptop " + i, 166 + i, kvp.Key, kvp.Value.SourceDevice);
+ i++;
+ if (i > 10) // We only have ten spots???
+ break;
+ }
+
+ foreach (var kvp in dict)
+ {
+ var usageDevice = kvp.Value.SourceDevice as IUsageTracking;
+
+ if (usageDevice != null)
+ {
+ usageDevice.UsageTracker = new UsageTracking(usageDevice as Device);
+ usageDevice.UsageTracker.UsageIsTracked = true;
+ usageDevice.UsageTracker.DeviceUsageEnded += new EventHandler(UsageTracker_DeviceUsageEnded);
+ }
+ }
+
+ }
+ else
+ {
+ Debug.Console(1, this, "WARNING: Config source list '{0}' not found for room '{1}'",
+ (Room as EssentialsHuddleSpaceRoom).SourceListKey, Room.Key);
+ }
+ }
+
+ ///
+ /// Collects usage data from source and sends to Fusion
+ ///
+ ///
+ ///
+ protected void UsageTracker_DeviceUsageEnded(object sender, DeviceUsageEventArgs e)
+ {
+ var deviceTracker = sender as UsageTracking;
+
+ var configDevice = ConfigReader.ConfigObject.Devices.Where(d => d.Key.Equals(deviceTracker.Parent));
+
+ string group = ConfigReader.GetGroupForDeviceKey(deviceTracker.Parent.Key);
+
+ string currentMeetingId = "-";
+
+ if (CurrentMeeting != null)
+ currentMeetingId = CurrentMeeting.MeetingID;
+
+ //String Format: "USAGE||[Date YYYY-MM-DD]||[Time HH-mm-ss]||TIME||[Asset_Type]||[Asset_Name]||[Minutes_used]||[Asset_ID]||[Meeting_ID]"
+ // [Asset_ID] property does not appear to be used in Crestron SSI examples. They are sending "-" instead so that's what is replicated here
+ string deviceUsage = string.Format("USAGE||{0}||{1}||TIME||{2}||{3}||-||{4}||-||{5}||{6}||\r\n", e.UsageEndTime.ToString("yyyy-MM-dd"), e.UsageEndTime.ToString("HH:mm:ss"),
+ group, deviceTracker.Parent.Name, e.MinutesUsed, "-", currentMeetingId);
+
+ Debug.Console(1, this, "Device usage for: {0} ended at {1}. In use for {2} minutes", deviceTracker.Parent.Name, e.UsageEndTime, e.MinutesUsed);
+
+ FusionRoom.DeviceUsage.InputSig.StringValue = deviceUsage;
+
+ Debug.Console(1, this, "Device usage string: {0}", deviceUsage);
+ }
+
+
+ protected void TryAddRouteActionSigs(string attrName, uint attrNum, string routeKey, Device pSrc)
+ {
+ Debug.Console(2, this, "Creating attribute '{0}' with join {1} for source {2}",
+ attrName, attrNum, pSrc.Key);
+ try
+ {
+ var sigD = FusionRoom.CreateOffsetBoolSig(attrNum, attrName, eSigIoMask.InputOutputSig);
+ // Need feedback when this source is selected
+ // Event handler, added below, will compare source changes with this sig dict
+ SourceToFeedbackSigs.Add(pSrc, sigD.InputSig);
+
+ // And respond to selection in Fusion
+ sigD.OutputSig.SetSigFalseAction(() => (Room as EssentialsHuddleSpaceRoom).RunRouteAction(routeKey));
+ }
+ catch (Exception)
+ {
+ Debug.Console(2, this, "Error creating Fusion signal {0} {1} for device '{2}'. THIS NEEDS REWORKING", attrNum, attrName, pSrc.Key);
+ }
+ }
+
+ ///
+ ///
+ ///
+ void SetUpCommunitcationMonitors()
+ {
+ uint displayNum = 0;
+ uint touchpanelNum = 0;
+ uint xpanelNum = 0;
+
+ // Attach to all room's devices with monitors.
+ //foreach (var dev in DeviceManager.Devices)
+ foreach (var dev in DeviceManager.GetDevices())
+ {
+ if (!(dev is ICommunicationMonitor))
+ continue;
+
+ string attrName = null;
+ uint attrNum = 1;
+
+ //var keyNum = ExtractNumberFromKey(dev.Key);
+ //if (keyNum == -1)
+ //{
+ // Debug.Console(1, this, "WARNING: Cannot link device '{0}' to numbered Fusion monitoring attributes",
+ // dev.Key);
+ // continue;
+ //}
+ //uint attrNum = Convert.ToUInt32(keyNum);
+
+ // Check for UI devices
+ var uiDev = dev as EssentialsTouchpanelController;
+ if (uiDev != null)
+ {
+ if (uiDev.Panel is Crestron.SimplSharpPro.UI.XpanelForSmartGraphics)
+ {
+ attrNum = attrNum + touchpanelNum;
+
+ if (attrNum > 10)
+ continue;
+ attrName = "Online - XPanel " + attrNum;
+ attrNum += 160;
+
+ touchpanelNum++;
+ }
+ else
+ {
+ attrNum = attrNum + xpanelNum;
+
+ if (attrNum > 10)
+ continue;
+ attrName = "Online - Touch Panel " + attrNum;
+ attrNum += 150;
+
+ xpanelNum++;
+ }
+ }
+
+ //else
+ if (dev is DisplayBase)
+ {
+ attrNum = attrNum + displayNum;
+ if (attrNum > 10)
+ continue;
+ attrName = "Online - Display " + attrNum;
+ attrNum += 170;
+
+ displayNum++;
+ }
+ //else if (dev is DvdDeviceBase)
+ //{
+ // if (attrNum > 5)
+ // continue;
+ // attrName = "Device Ok - DVD " + attrNum;
+ // attrNum += 260;
+ //}
+ // add set top box
+
+ // add Cresnet roll-up
+
+ // add DM-devices roll-up
+
+ if (attrName != null)
+ {
+ // Link comm status to sig and update
+ var sigD = FusionRoom.CreateOffsetBoolSig(attrNum, attrName, eSigIoMask.InputSigOnly);
+ var smd = dev as ICommunicationMonitor;
+ sigD.InputSig.BoolValue = smd.CommunicationMonitor.Status == MonitorStatus.IsOk;
+ smd.CommunicationMonitor.StatusChange += (o, a) =>
+ { sigD.InputSig.BoolValue = a.Status == MonitorStatus.IsOk; };
+ Debug.Console(0, this, "Linking '{0}' communication monitor to Fusion '{1}'", dev.Key, attrName);
+ }
+ }
+ }
+
+ protected virtual void SetUpDisplay()
+ {
+ try
+ {
+ //Setup Display Usage Monitoring
+
+ var displays = DeviceManager.AllDevices.Where(d => d is DisplayBase);
+
+ // Consider updating this in multiple display systems
+
+ foreach (DisplayBase display in displays)
+ {
+ display.UsageTracker = new UsageTracking(display);
+ display.UsageTracker.UsageIsTracked = true;
+ display.UsageTracker.DeviceUsageEnded += new EventHandler(UsageTracker_DeviceUsageEnded);
+ }
+
+ var defaultDisplay = (Room as EssentialsHuddleSpaceRoom).DefaultDisplay as DisplayBase;
+ if (defaultDisplay == null)
+ {
+ Debug.Console(1, this, "Cannot link null display to Fusion because default display is null");
+ return;
+ }
+
+ var dispPowerOnAction = new Action(b => { if (!b) defaultDisplay.PowerOn(); });
+ var dispPowerOffAction = new Action(b => { if (!b) defaultDisplay.PowerOff(); });
+
+ // Display to fusion room sigs
+ FusionRoom.DisplayPowerOn.OutputSig.UserObject = dispPowerOnAction;
+ FusionRoom.DisplayPowerOff.OutputSig.UserObject = dispPowerOffAction;
+ defaultDisplay.PowerIsOnFeedback.LinkInputSig(FusionRoom.DisplayPowerOn.InputSig);
+ if (defaultDisplay is IDisplayUsage)
+ (defaultDisplay as IDisplayUsage).LampHours.LinkInputSig(FusionRoom.DisplayUsage.InputSig);
+
+
+
+ MapDisplayToRoomJoins(1, 158, defaultDisplay);
+
+
+ var deviceConfig = ConfigReader.ConfigObject.Devices.FirstOrDefault(d => d.Key.Equals(defaultDisplay.Key));
+
+ //Check for existing asset in GUIDs collection
+
+ var tempAsset = new FusionAsset();
+
+ if (FusionStaticAssets.ContainsKey(deviceConfig.Uid))
+ {
+ tempAsset = FusionStaticAssets[deviceConfig.Uid];
+ }
+ else
+ {
+ // Create a new asset
+ tempAsset = new FusionAsset(FusionRoomGuids.GetNextAvailableAssetNumber(FusionRoom), defaultDisplay.Name, "Display", "");
+ FusionStaticAssets.Add(deviceConfig.Uid, tempAsset);
+ }
+
+ var dispAsset = FusionRoom.CreateStaticAsset(tempAsset.SlotNumber, tempAsset.Name, "Display", tempAsset.InstanceId);
+ dispAsset.PowerOn.OutputSig.UserObject = dispPowerOnAction;
+ dispAsset.PowerOff.OutputSig.UserObject = dispPowerOffAction;
+ defaultDisplay.PowerIsOnFeedback.LinkInputSig(dispAsset.PowerOn.InputSig);
+ // NO!! display.PowerIsOn.LinkComplementInputSig(dispAsset.PowerOff.InputSig);
+ // Use extension methods
+ dispAsset.TrySetMakeModel(defaultDisplay);
+ dispAsset.TryLinkAssetErrorToCommunication(defaultDisplay);
+ }
+ catch (Exception e)
+ {
+ Debug.Console(1, this, "Error setting up display in Fusion: {0}", e);
+ }
+
+ }
+
+ ///
+ /// Maps room attributes to a display at a specified index
+ ///
+ ///
+ /// a
+ protected virtual void MapDisplayToRoomJoins(int displayIndex, int joinOffset, DisplayBase display)
+ {
+ string displayName = string.Format("Display {0} - ", displayIndex);
+
+
+ if (display == (Room as EssentialsHuddleSpaceRoom).DefaultDisplay)
+ {
+ // Display volume
+ var defaultDisplayVolume = FusionRoom.CreateOffsetUshortSig(50, "Volume - Fader01", eSigIoMask.InputOutputSig);
+ defaultDisplayVolume.OutputSig.UserObject = new Action(b => (display as IBasicVolumeWithFeedback).SetVolume(b));
+ (display as IBasicVolumeWithFeedback).VolumeLevelFeedback.LinkInputSig(defaultDisplayVolume.InputSig);
+
+ // Power on
+ var defaultDisplayPowerOn = FusionRoom.CreateOffsetBoolSig((uint)joinOffset, displayName + "Power On", eSigIoMask.InputOutputSig);
+ defaultDisplayPowerOn.OutputSig.UserObject = new Action(b => { if (!b) display.PowerOn(); });
+ display.PowerIsOnFeedback.LinkInputSig(defaultDisplayPowerOn.InputSig);
+
+ // Power Off
+ var defaultDisplayPowerOff = FusionRoom.CreateOffsetBoolSig((uint)joinOffset + 1, displayName + "Power Off", eSigIoMask.InputOutputSig);
+ defaultDisplayPowerOn.OutputSig.UserObject = new Action(b => { if (!b) display.PowerOff(); }); ;
+ display.PowerIsOnFeedback.LinkInputSig(defaultDisplayPowerOn.InputSig);
+
+ // Current Source
+ var defaultDisplaySourceNone = FusionRoom.CreateOffsetBoolSig((uint)joinOffset + 8, displayName + "Source None", eSigIoMask.InputOutputSig);
+ defaultDisplaySourceNone.OutputSig.UserObject = new Action(b => { if (!b) (Room as EssentialsHuddleSpaceRoom).RunRouteAction("roomOff"); }); ;
+ }
+ }
+
+ void SetUpError()
+ {
+ // Roll up ALL device errors
+ ErrorMessageRollUp = new StatusMonitorCollection(this);
+ foreach (var dev in DeviceManager.GetDevices())
+ {
+ var md = dev as ICommunicationMonitor;
+ if (md != null)
+ {
+ ErrorMessageRollUp.AddMonitor(md.CommunicationMonitor);
+ Debug.Console(2, this, "Adding '{0}' to room's overall error monitor", md.CommunicationMonitor.Parent.Key);
+ }
+ }
+ ErrorMessageRollUp.Start();
+ FusionRoom.ErrorMessage.InputSig.StringValue = ErrorMessageRollUp.Message;
+ ErrorMessageRollUp.StatusChange += (o, a) =>
+ {
+ FusionRoom.ErrorMessage.InputSig.StringValue = ErrorMessageRollUp.Message;
+ };
+
+ }
+
+ ///
+ /// Sets up a local occupancy sensor, such as one attached to a Fusion Scheduling panel. The occupancy status of the room will be read from Fusion
+ ///
+ void SetUpLocalOccupancy()
+ {
+ RoomIsOccupiedFeedback = new BoolFeedback(RoomIsOccupiedFeedbackFunc);
+
+ FusionRoom.FusionAssetStateChange += new FusionAssetStateEventHandler(FusionRoom_FusionAssetStateChange);
+
+ // Build Occupancy Asset?
+ // Link sigs?
+
+ //Room.SetRoomOccupancy(this as IOccupancyStatusProvider, 0);
+
+
+ }
+
+ void FusionRoom_FusionAssetStateChange(FusionBase device, FusionAssetStateEventArgs args)
+ {
+ if (args.EventId == FusionAssetEventId.RoomOccupiedReceivedEventId || args.EventId == FusionAssetEventId.RoomUnoccupiedReceivedEventId)
+ RoomIsOccupiedFeedback.FireUpdate();
+
+ }
+
+ ///
+ /// Sets up remote occupancy that will relay the occupancy status determined by local system devices to Fusion
+ ///
+ void SetUpRemoteOccupancy()
+ {
+
+ // Need to have the room occupancy object first and somehow determine the slot number of the Occupancy asset but will not be able to use the UID from config likely.
+ // Consider defining an object just for Room Occupancy (either eAssetType.Occupancy Sensor (local) or eAssetType.RemoteOccupancySensor (from Fusion sched. panel)) and reserving slot 4 for that asset (statics would start at 5)
+
+ //if (Room.OccupancyObj != null)
+ //{
+
+ var tempOccAsset = GUIDs.OccupancyAsset;
+
+ if(tempOccAsset == null)
+ {
+ FusionOccSensor = new FusionOccupancySensorAsset(eAssetType.OccupancySensor);
+ tempOccAsset = FusionOccSensor;
+ }
+
+ var occSensorAsset = FusionRoom.CreateOccupancySensorAsset(tempOccAsset.SlotNumber, tempOccAsset.Name, "Occupancy Sensor", tempOccAsset.InstanceId);
+
+ occSensorAsset.RoomOccupied.AddSigToRVIFile = true;
+
+ var occSensorShutdownMinutes = FusionRoom.CreateOffsetUshortSig(70, "Occ Shutdown - Minutes", eSigIoMask.InputOutputSig);
+
+ // Tie to method on occupancy object
+ //occSensorShutdownMinutes.OutputSig.UserObject(new Action(ushort)(b => Room.OccupancyObj.SetShutdownMinutes(b));
+
+
+ Room.RoomOccupancy.RoomIsOccupiedFeedback.LinkInputSig(occSensorAsset.RoomOccupied.InputSig);
+ //}
+ }
+
+ ///
+ /// Helper to get the number from the end of a device's key string
+ ///
+ /// -1 if no number matched
+ int ExtractNumberFromKey(string key)
+ {
+ var capture = System.Text.RegularExpressions.Regex.Match(key, @"\b(\d+)");
+ if (!capture.Success)
+ return -1;
+ else return Convert.ToInt32(capture.Groups[1].Value);
+ }
+
+ ///
+ /// Event handler for when room source changes
+ ///
+ protected void Room_CurrentSourceInfoChange(EssentialsRoomBase room, SourceListItem info, ChangeType type)
+ {
+ // Handle null. Nothing to do when switching from or to null
+ if (info == null || info.SourceDevice == null)
+ return;
+
+ var dev = info.SourceDevice;
+ if (type == ChangeType.WillChange)
+ {
+ if (SourceToFeedbackSigs.ContainsKey(dev))
+ SourceToFeedbackSigs[dev].BoolValue = false;
+ }
+ else
+ {
+ if (SourceToFeedbackSigs.ContainsKey(dev))
+ SourceToFeedbackSigs[dev].BoolValue = true;
+ var name = (room == null ? "" : room.Name);
+ CurrentRoomSourceNameSig.InputSig.StringValue = info.SourceDevice.Name;
+ }
+ }
+
+ protected void FusionRoom_FusionStateChange(FusionBase device, FusionStateEventArgs args)
+ {
+
+ // The sig/UO method: Need separate handlers for fixed and user sigs, all flavors,
+ // even though they all contain sigs.
+
+ var sigData = (args.UserConfiguredSigDetail as BooleanSigDataFixedName);
+ if (sigData != null)
+ {
+ var outSig = sigData.OutputSig;
+ if (outSig.UserObject is Action)
+ (outSig.UserObject as Action).Invoke(outSig.BoolValue);
+ else if (outSig.UserObject is Action)
+ (outSig.UserObject as Action).Invoke(outSig.UShortValue);
+ else if (outSig.UserObject is Action)
+ (outSig.UserObject as Action).Invoke(outSig.StringValue);
+ return;
+ }
+
+ var attrData = (args.UserConfiguredSigDetail as BooleanSigData);
+ if (attrData != null)
+ {
+ var outSig = attrData.OutputSig;
+ if (outSig.UserObject is Action)
+ (outSig.UserObject as Action).Invoke(outSig.BoolValue);
+ else if (outSig.UserObject is Action)
+ (outSig.UserObject as Action).Invoke(outSig.UShortValue);
+ else if (outSig.UserObject is Action)
+ (outSig.UserObject as Action).Invoke(outSig.StringValue);
+ return;
+ }
+
+ }
+ }
+
+
+ public static class FusionRoomExtensions
+ {
+ ///
+ /// Creates and returns a fusion attribute. The join number will match the established Simpl
+ /// standard of 50+, and will generate a 50+ join in the RVI. It calls
+ /// FusionRoom.AddSig with join number - 49
+ ///
+ /// The new attribute
+ public static BooleanSigData CreateOffsetBoolSig(this FusionRoom fr, uint number, string name, eSigIoMask mask)
+ {
+ if (number < 50) throw new ArgumentOutOfRangeException("number", "Cannot be less than 50");
+ number -= 49;
+ fr.AddSig(eSigType.Bool, number, name, mask);
+ return fr.UserDefinedBooleanSigDetails[number];
+ }
+
+ ///
+ /// Creates and returns a fusion attribute. The join number will match the established Simpl
+ /// standard of 50+, and will generate a 50+ join in the RVI. It calls
+ /// FusionRoom.AddSig with join number - 49
+ ///
+ /// The new attribute
+ public static UShortSigData CreateOffsetUshortSig(this FusionRoom fr, uint number, string name, eSigIoMask mask)
+ {
+ if (number < 50) throw new ArgumentOutOfRangeException("number", "Cannot be less than 50");
+ number -= 49;
+ fr.AddSig(eSigType.UShort, number, name, mask);
+ return fr.UserDefinedUShortSigDetails[number];
+ }
+
+ ///
+ /// Creates and returns a fusion attribute. The join number will match the established Simpl
+ /// standard of 50+, and will generate a 50+ join in the RVI. It calls
+ /// FusionRoom.AddSig with join number - 49
+ ///
+ /// The new attribute
+ public static StringSigData CreateOffsetStringSig(this FusionRoom fr, uint number, string name, eSigIoMask mask)
+ {
+ if (number < 50) throw new ArgumentOutOfRangeException("number", "Cannot be less than 50");
+ number -= 49;
+ fr.AddSig(eSigType.String, number, name, mask);
+ return fr.UserDefinedStringSigDetails[number];
+ }
+
+ ///
+ /// Creates and returns a static asset
+ ///
+ /// the new asset
+ public static FusionStaticAsset CreateStaticAsset(this FusionRoom fr, uint number, string name, string type, string instanceId)
+ {
+ Debug.Console(0, "Adding Fusion Static Asset '{0}' to slot {1} with GUID: '{2}'", name, number, instanceId);
+
+ fr.AddAsset(eAssetType.StaticAsset, number, name, type, instanceId);
+ return fr.UserConfigurableAssetDetails[number].Asset as FusionStaticAsset;
+ }
+
+ public static FusionOccupancySensor CreateOccupancySensorAsset(this FusionRoom fr, uint number, string name, string type, string instanceId)
+ {
+ Debug.Console(0, "Adding Fusion Occupancy Sensor Asset '{0}' to slot {1} with GUID: '{2}'", name, number, instanceId);
+
+ fr.AddAsset(eAssetType.OccupancySensor, number, name, type, instanceId);
+ return fr.UserConfigurableAssetDetails[number].Asset as FusionOccupancySensor;
+ }
+ }
+
+ //************************************************************************************************
+ ///
+ /// Extensions to enhance Fusion room, asset and signal creation.
+ ///
+ public static class FusionStaticAssetExtensions
+ {
+ ///
+ /// Tries to set a Fusion asset with the make and model of a device.
+ /// If the provided Device is IMakeModel, will set the corresponding parameters on the fusion static asset.
+ /// Otherwise, does nothing.
+ ///
+ public static void TrySetMakeModel(this FusionStaticAsset asset, Device device)
+ {
+ var mm = device as IMakeModel;
+ if (mm != null)
+ {
+ asset.ParamMake.Value = mm.DeviceMake;
+ asset.ParamModel.Value = mm.DeviceModel;
+ }
+ }
+
+ ///
+ /// Tries to attach the AssetError input on a Fusion asset to a Device's
+ /// CommunicationMonitor.StatusChange event. Does nothing if the device is not
+ /// IStatusMonitor
+ ///
+ ///
+ ///
+ public static void TryLinkAssetErrorToCommunication(this FusionStaticAsset asset, Device device)
+ {
+ if (device is ICommunicationMonitor)
+ {
+ var monitor = (device as ICommunicationMonitor).CommunicationMonitor;
+ monitor.StatusChange += (o, a) =>
+ {
+ // Link connected and error inputs on asset
+ asset.Connected.InputSig.BoolValue = a.Status == MonitorStatus.IsOk;
+ asset.AssetError.InputSig.StringValue = a.Status.ToString();
+ };
+ // set current value
+ asset.Connected.InputSig.BoolValue = monitor.Status == MonitorStatus.IsOk;
+ asset.AssetError.InputSig.StringValue = monitor.Status.ToString();
+ }
+ }
+ }
+
+ public class RoomInformation
+ {
+ public string ID { get; set; }
+ public string Name { get; set; }
+ public string Location { get; set; }
+ public string Description { get; set; }
+ public string TimeZone { get; set; }
+ public string WebcamURL { get; set; }
+ public string BacklogMsg { get; set; }
+ public string SubErrorMsg { get; set; }
+ public string EmailInfo { get; set; }
+ public List FusionCustomProperties { get; set; }
+
+ public RoomInformation()
+ {
+ FusionCustomProperties = new List();
+ }
+ }
+ public class FusionCustomProperty
+ {
+ public string ID { get; set; }
+ public string CustomFieldName { get; set; }
+ public string CustomFieldType { get; set; }
+ public string CustomFieldValue { get; set; }
+
+ public FusionCustomProperty()
+ {
+
+ }
+
+ public FusionCustomProperty(string id)
+ {
+ ID = id;
+ }
+ }
}
\ No newline at end of file
diff --git a/PepperDashEssentials/OTHER/Fusion/EssentialsHuddleVtc1FusionController.cs b/PepperDashEssentials/OTHER/Fusion/EssentialsHuddleVtc1FusionController.cs
index 214e3537..14148a12 100644
--- a/PepperDashEssentials/OTHER/Fusion/EssentialsHuddleVtc1FusionController.cs
+++ b/PepperDashEssentials/OTHER/Fusion/EssentialsHuddleVtc1FusionController.cs
@@ -1,354 +1,348 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-
-using Crestron.SimplSharp;
-using Crestron.SimplSharp.CrestronIO;
-using Crestron.SimplSharpPro;
-using Crestron.SimplSharpPro.DeviceSupport;
-using Crestron.SimplSharpPro.Fusion;
-
-
-using PepperDash.Core;
-using PepperDash.Essentials;
-using PepperDash.Essentials.Core;
-using PepperDash.Essentials.Devices.Common;
-using PepperDash.Essentials.Devices.Common.Occupancy;
-
-namespace PepperDash.Essentials.Fusion
-{
- public class EssentialsHuddleVtc1FusionController : EssentialsHuddleSpaceFusionSystemControllerBase
- {
- BooleanSigData CodecIsInCall;
-
- public EssentialsHuddleVtc1FusionController(EssentialsHuddleVtc1Room room, uint ipId)
- : base(room, ipId)
- {
-
- }
-
- ///
- /// Called in base class constructor before RVI and GUID files are built
- ///
- protected override void ExecuteCustomSteps()
- {
- SetUpCodec();
- }
-
- ///
- /// Creates a static asset for the codec and maps the joins to the main room symbol
- ///
- void SetUpCodec()
- {
- try
- {
- var codec = (Room as EssentialsHuddleVtc1Room).VideoCodec;
-
- if (codec == null)
- {
- Debug.Console(1, this, "Cannot link codec to Fusion because codec is null");
- return;
- }
-
- codec.UsageTracker = new UsageTracking(codec);
- codec.UsageTracker.UsageIsTracked = true;
- codec.UsageTracker.DeviceUsageEnded += UsageTracker_DeviceUsageEnded;
-
- var codecPowerOnAction = new Action(b => { if (!b) codec.StandbyDeactivate(); });
- var codecPowerOffAction = new Action(b => { if (!b) codec.StandbyActivate(); });
-
- // Map FusionRoom Attributes:
-
- // Codec volume
- var codecVolume = FusionRoom.CreateOffsetUshortSig(50, "Volume - Fader01", eSigIoMask.InputOutputSig);
- codecVolume.OutputSig.UserObject = new Action(b => (codec as IBasicVolumeWithFeedback).SetVolume(b));
- (codec as IBasicVolumeWithFeedback).VolumeLevelFeedback.LinkInputSig(codecVolume.InputSig);
-
- // In Call Status
- CodecIsInCall = FusionRoom.CreateOffsetBoolSig(69, "Conf - VC 1 In Call", eSigIoMask.InputSigOnly);
- codec.CallStatusChange += new EventHandler(codec_CallStatusChange);
-
- // Online status
- if (codec is ICommunicationMonitor)
- {
- var c = codec as ICommunicationMonitor;
- var codecOnline = FusionRoom.CreateOffsetBoolSig(122, "Online - VC 1", eSigIoMask.InputSigOnly);
- codecOnline.InputSig.BoolValue = c.CommunicationMonitor.Status == MonitorStatus.IsOk;
- c.CommunicationMonitor.StatusChange += (o, a) =>
- {
- codecOnline.InputSig.BoolValue = a.Status == MonitorStatus.IsOk;
- };
- Debug.Console(0, this, "Linking '{0}' communication monitor to Fusion '{1}'", codec.Key, "Online - VC 1");
- }
-
- // Codec IP Address
- bool codecHasIpInfo = false;
- var codecComm = codec.Communication;
-
- string codecIpAddress = string.Empty;
- int codecIpPort = 0;
-
- StringSigData codecIpAddressSig;
- StringSigData codecIpPortSig;
-
- if(codecComm is GenericSshClient)
- {
- codecIpAddress = (codecComm as GenericSshClient).Hostname;
- codecIpPort = (codecComm as GenericSshClient).Port;
- codecHasIpInfo = true;
- }
- else if (codecComm is GenericTcpIpClient)
- {
- codecIpAddress = (codecComm as GenericTcpIpClient).Hostname;
- codecIpPort = (codecComm as GenericTcpIpClient).Port;
- codecHasIpInfo = true;
- }
-
- if (codecHasIpInfo)
- {
- codecIpAddressSig = FusionRoom.CreateOffsetStringSig(121, "IP Address - VC", eSigIoMask.InputSigOnly);
- codecIpAddressSig.InputSig.StringValue = codecIpAddress;
-
- codecIpPortSig = FusionRoom.CreateOffsetStringSig(150, "IP Port - VC", eSigIoMask.InputSigOnly);
- codecIpPortSig.InputSig.StringValue = codecIpPort.ToString();
- }
-
- var tempAsset = new FusionAsset();
-
- var deviceConfig = ConfigReader.ConfigObject.Devices.FirstOrDefault(c => c.Key.Equals(codec.Key));
-
- if (FusionStaticAssets.ContainsKey(deviceConfig.Uid))
- {
- tempAsset = FusionStaticAssets[deviceConfig.Uid];
- }
- else
- {
- // Create a new asset
- tempAsset = new FusionAsset(FusionRoomGuids.GetNextAvailableAssetNumber(FusionRoom), codec.Name, "Codec", "");
- FusionStaticAssets.Add(deviceConfig.Uid, tempAsset);
- }
-
- var codecAsset = FusionRoom.CreateStaticAsset(tempAsset.SlotNumber, tempAsset.Name, "Display", tempAsset.InstanceId);
- codecAsset.PowerOn.OutputSig.UserObject = codecPowerOnAction;
- codecAsset.PowerOff.OutputSig.UserObject = codecPowerOffAction;
- codec.StandbyIsOnFeedback.LinkComplementInputSig(codecAsset.PowerOn.InputSig);
-
- // TODO: Map relevant attributes on asset symbol
-
- codecAsset.TrySetMakeModel(codec);
- codecAsset.TryLinkAssetErrorToCommunication(codec);
- }
- catch (Exception e)
- {
- Debug.Console(1, this, "Error setting up codec in Fusion: {0}", e);
- }
- }
-
- void codec_CallStatusChange(object sender, PepperDash.Essentials.Devices.Common.Codec.CodecCallStatusItemChangeEventArgs e)
- {
- var codec = (Room as EssentialsHuddleVtc1Room).VideoCodec;
-
- CodecIsInCall.InputSig.BoolValue = codec.IsInCall;
- }
-
- // These methods are overridden because they access the room class which is of a different type
-
- protected override void CreateSymbolAndBasicSigs(uint ipId)
- {
- Debug.Console(1, this, "Creating Fusion Room symbol with GUID: {0}", RoomGuid);
-
- FusionRoom = new FusionRoom(ipId, Global.ControlSystem, Room.Name, RoomGuid);
- FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.Use();
- FusionRoom.ExtenderFusionRoomDataReservedSigs.Use();
-
- FusionRoom.Register();
-
- FusionRoom.FusionStateChange += FusionRoom_FusionStateChange;
-
- FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.DeviceExtenderSigChange += FusionRoomSchedule_DeviceExtenderSigChange;
- FusionRoom.ExtenderFusionRoomDataReservedSigs.DeviceExtenderSigChange += ExtenderFusionRoomDataReservedSigs_DeviceExtenderSigChange;
- FusionRoom.OnlineStatusChange += FusionRoom_OnlineStatusChange;
-
- CrestronConsole.AddNewConsoleCommand(RequestFullRoomSchedule, "FusReqRoomSchedule", "Requests schedule of the room for the next 24 hours", ConsoleAccessLevelEnum.AccessOperator);
- CrestronConsole.AddNewConsoleCommand(ModifyMeetingEndTimeConsoleHelper, "FusReqRoomSchMod", "Ends or extends a meeting by the specified time", ConsoleAccessLevelEnum.AccessOperator);
- CrestronConsole.AddNewConsoleCommand(CreateAsHocMeeting, "FusCreateMeeting", "Creates and Ad Hoc meeting for on hour or until the next meeting", ConsoleAccessLevelEnum.AccessOperator);
-
- // Room to fusion room
- Room.OnFeedback.LinkInputSig(FusionRoom.SystemPowerOn.InputSig);
-
- // Moved to
- CurrentRoomSourceNameSig = FusionRoom.CreateOffsetStringSig(84, "Display 1 - Current Source", eSigIoMask.InputSigOnly);
- // Don't think we need to get current status of this as nothing should be alive yet.
- (Room as EssentialsHuddleVtc1Room).CurrentSingleSourceChange += Room_CurrentSourceInfoChange;
-
-
- FusionRoom.SystemPowerOn.OutputSig.SetSigFalseAction((Room as EssentialsHuddleVtc1Room).PowerOnToDefaultOrLastSource);
- FusionRoom.SystemPowerOff.OutputSig.SetSigFalseAction(() => (Room as EssentialsHuddleVtc1Room).RunRouteAction("roomOff"));
- // NO!! room.RoomIsOn.LinkComplementInputSig(FusionRoom.SystemPowerOff.InputSig);
- FusionRoom.ErrorMessage.InputSig.StringValue =
- "3: 7 Errors: This is a really long error message;This is a really long error message;This is a really long error message;This is a really long error message;This is a really long error message;This is a really long error message;This is a really long error message;";
-
- GetProcessorEthernetValues();
-
- GetSystemInfo();
-
- GetProcessorInfo();
-
- CrestronEnvironment.EthernetEventHandler += CrestronEnvironment_EthernetEventHandler;
- }
-
- protected override void SetUpSources()
- {
- // Sources
- var dict = ConfigReader.ConfigObject.GetSourceListForKey((Room as EssentialsHuddleVtc1Room).SourceListKey);
- if (dict != null)
- {
- // NEW PROCESS:
- // Make these lists and insert the fusion attributes by iterating these
- var setTopBoxes = dict.Where(d => d.Value.SourceDevice is ISetTopBoxControls);
- uint i = 1;
- foreach (var kvp in setTopBoxes)
- {
- TryAddRouteActionSigs("Display 1 - Source TV " + i, 188 + i, kvp.Key, kvp.Value.SourceDevice);
- i++;
- if (i > 5) // We only have five spots
- break;
- }
-
- var discPlayers = dict.Where(d => d.Value.SourceDevice is IDiscPlayerControls);
- i = 1;
- foreach (var kvp in discPlayers)
- {
- TryAddRouteActionSigs("Display 1 - Source DVD " + i, 181 + i, kvp.Key, kvp.Value.SourceDevice);
- i++;
- if (i > 5) // We only have five spots
- break;
- }
-
- var laptops = dict.Where(d => d.Value.SourceDevice is Laptop);
- i = 1;
- foreach (var kvp in laptops)
- {
- TryAddRouteActionSigs("Display 1 - Source Laptop " + i, 166 + i, kvp.Key, kvp.Value.SourceDevice);
- i++;
- if (i > 10) // We only have ten spots???
- break;
- }
-
- foreach (var kvp in dict)
- {
- var usageDevice = kvp.Value.SourceDevice as IUsageTracking;
-
- if (usageDevice != null)
- {
- usageDevice.UsageTracker = new UsageTracking(usageDevice as Device);
- usageDevice.UsageTracker.UsageIsTracked = true;
- usageDevice.UsageTracker.DeviceUsageEnded += new EventHandler(UsageTracker_DeviceUsageEnded);
- }
- }
-
- }
- else
- {
- Debug.Console(1, this, "WARNING: Config source list '{0}' not found for room '{1}'",
- (Room as EssentialsHuddleVtc1Room).SourceListKey, Room.Key);
- }
- }
-
- protected override void SetUpDisplay()
- {
- try
- {
- //Setup Display Usage Monitoring
-
- var displays = DeviceManager.AllDevices.Where(d => d is DisplayBase);
-
- // Consider updating this in multiple display systems
-
- foreach (DisplayBase display in displays)
- {
- display.UsageTracker = new UsageTracking(display);
- display.UsageTracker.UsageIsTracked = true;
- display.UsageTracker.DeviceUsageEnded += new EventHandler(UsageTracker_DeviceUsageEnded);
- }
-
- var defaultDisplay = (Room as EssentialsHuddleVtc1Room).DefaultDisplay as DisplayBase;
- if (defaultDisplay == null)
- {
- Debug.Console(1, this, "Cannot link null display to Fusion because default display is null");
- return;
- }
-
- var dispPowerOnAction = new Action(b => { if (!b) defaultDisplay.PowerOn(); });
- var dispPowerOffAction = new Action(b => { if (!b) defaultDisplay.PowerOff(); });
-
- // Display to fusion room sigs
- FusionRoom.DisplayPowerOn.OutputSig.UserObject = dispPowerOnAction;
- FusionRoom.DisplayPowerOff.OutputSig.UserObject = dispPowerOffAction;
- defaultDisplay.PowerIsOnFeedback.LinkInputSig(FusionRoom.DisplayPowerOn.InputSig);
- if (defaultDisplay is IDisplayUsage)
- (defaultDisplay as IDisplayUsage).LampHours.LinkInputSig(FusionRoom.DisplayUsage.InputSig);
-
-
-
- MapDisplayToRoomJoins(1, 158, defaultDisplay);
-
-
- var deviceConfig = ConfigReader.ConfigObject.Devices.FirstOrDefault(d => d.Key.Equals(defaultDisplay.Key));
-
- //Check for existing asset in GUIDs collection
-
- var tempAsset = new FusionAsset();
-
- if (FusionStaticAssets.ContainsKey(deviceConfig.Uid))
- {
- tempAsset = FusionStaticAssets[deviceConfig.Uid];
- }
- else
- {
- // Create a new asset
- tempAsset = new FusionAsset(FusionRoomGuids.GetNextAvailableAssetNumber(FusionRoom), defaultDisplay.Name, "Display", "");
- FusionStaticAssets.Add(deviceConfig.Uid, tempAsset);
- }
-
- var dispAsset = FusionRoom.CreateStaticAsset(tempAsset.SlotNumber, tempAsset.Name, "Display", tempAsset.InstanceId);
- dispAsset.PowerOn.OutputSig.UserObject = dispPowerOnAction;
- dispAsset.PowerOff.OutputSig.UserObject = dispPowerOffAction;
- defaultDisplay.PowerIsOnFeedback.LinkInputSig(dispAsset.PowerOn.InputSig);
- // NO!! display.PowerIsOn.LinkComplementInputSig(dispAsset.PowerOff.InputSig);
- // Use extension methods
- dispAsset.TrySetMakeModel(defaultDisplay);
- dispAsset.TryLinkAssetErrorToCommunication(defaultDisplay);
- }
- catch (Exception e)
- {
- Debug.Console(1, this, "Error setting up display in Fusion: {0}", e);
- }
-
- }
-
- protected override void MapDisplayToRoomJoins(int displayIndex, int joinOffset, DisplayBase display)
- {
- string displayName = string.Format("Display {0} - ", displayIndex);
-
-
- if (display == (Room as EssentialsHuddleVtc1Room).DefaultDisplay)
- {
- // Power on
- var defaultDisplayPowerOn = FusionRoom.CreateOffsetBoolSig((uint)joinOffset, displayName + "Power On", eSigIoMask.InputOutputSig);
- defaultDisplayPowerOn.OutputSig.UserObject = new Action(b => { if (!b) display.PowerOn(); });
- display.PowerIsOnFeedback.LinkInputSig(defaultDisplayPowerOn.InputSig);
-
- // Power Off
- var defaultDisplayPowerOff = FusionRoom.CreateOffsetBoolSig((uint)joinOffset + 1, displayName + "Power Off", eSigIoMask.InputOutputSig);
- defaultDisplayPowerOn.OutputSig.UserObject = new Action(b => { if (!b) display.PowerOff(); }); ;
- display.PowerIsOnFeedback.LinkInputSig(defaultDisplayPowerOn.InputSig);
-
- // Current Source
- var defaultDisplaySourceNone = FusionRoom.CreateOffsetBoolSig((uint)joinOffset + 8, displayName + "Source None", eSigIoMask.InputOutputSig);
- defaultDisplaySourceNone.OutputSig.UserObject = new Action(b => { if (!b) (Room as EssentialsHuddleVtc1Room).RunRouteAction("roomOff"); }); ;
- }
- }
- }
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+using Crestron.SimplSharp;
+using Crestron.SimplSharp.CrestronIO;
+using Crestron.SimplSharpPro;
+using Crestron.SimplSharpPro.DeviceSupport;
+using Crestron.SimplSharpPro.Fusion;
+
+
+using PepperDash.Core;
+using PepperDash.Essentials;
+using PepperDash.Essentials.Core;
+using PepperDash.Essentials.Core.Config;
+using PepperDash.Essentials.Devices.Common;
+using PepperDash.Essentials.Devices.Common.Occupancy;
+
+namespace PepperDash.Essentials.Fusion
+{
+ public class EssentialsHuddleVtc1FusionController : EssentialsHuddleSpaceFusionSystemControllerBase
+ {
+ BooleanSigData CodecIsInCall;
+
+ public EssentialsHuddleVtc1FusionController(EssentialsHuddleVtc1Room room, uint ipId)
+ : base(room, ipId)
+ {
+
+ }
+
+ ///
+ /// Called in base class constructor before RVI and GUID files are built
+ ///
+ protected override void ExecuteCustomSteps()
+ {
+ SetUpCodec();
+ }
+
+ ///
+ /// Creates a static asset for the codec and maps the joins to the main room symbol
+ ///
+ void SetUpCodec()
+ {
+ try
+ {
+ var codec = (Room as EssentialsHuddleVtc1Room).VideoCodec;
+
+ if (codec == null)
+ {
+ Debug.Console(1, this, "Cannot link codec to Fusion because codec is null");
+ return;
+ }
+
+ codec.UsageTracker = new UsageTracking(codec);
+ codec.UsageTracker.UsageIsTracked = true;
+ codec.UsageTracker.DeviceUsageEnded += UsageTracker_DeviceUsageEnded;
+
+ var codecPowerOnAction = new Action(b => { if (!b) codec.StandbyDeactivate(); });
+ var codecPowerOffAction = new Action(b => { if (!b) codec.StandbyActivate(); });
+
+ // Map FusionRoom Attributes:
+
+ // Codec volume
+ var codecVolume = FusionRoom.CreateOffsetUshortSig(50, "Volume - Fader01", eSigIoMask.InputOutputSig);
+ codecVolume.OutputSig.UserObject = new Action(b => (codec as IBasicVolumeWithFeedback).SetVolume(b));
+ (codec as IBasicVolumeWithFeedback).VolumeLevelFeedback.LinkInputSig(codecVolume.InputSig);
+
+ // In Call Status
+ CodecIsInCall = FusionRoom.CreateOffsetBoolSig(69, "Conf - VC 1 In Call", eSigIoMask.InputSigOnly);
+ codec.CallStatusChange += new EventHandler(codec_CallStatusChange);
+
+ // Online status
+ if (codec is ICommunicationMonitor)
+ {
+ var c = codec as ICommunicationMonitor;
+ var codecOnline = FusionRoom.CreateOffsetBoolSig(122, "Online - VC 1", eSigIoMask.InputSigOnly);
+ codecOnline.InputSig.BoolValue = c.CommunicationMonitor.Status == MonitorStatus.IsOk;
+ c.CommunicationMonitor.StatusChange += (o, a) =>
+ {
+ codecOnline.InputSig.BoolValue = a.Status == MonitorStatus.IsOk;
+ };
+ Debug.Console(0, this, "Linking '{0}' communication monitor to Fusion '{1}'", codec.Key, "Online - VC 1");
+ }
+
+ // Codec IP Address
+ bool codecHasIpInfo = false;
+ var codecComm = codec.Communication;
+
+ string codecIpAddress = string.Empty;
+ int codecIpPort = 0;
+
+ StringSigData codecIpAddressSig;
+ StringSigData codecIpPortSig;
+
+ if(codecComm is GenericSshClient)
+ {
+ codecIpAddress = (codecComm as GenericSshClient).Hostname;
+ codecIpPort = (codecComm as GenericSshClient).Port;
+ codecHasIpInfo = true;
+ }
+ else if (codecComm is GenericTcpIpClient)
+ {
+ codecIpAddress = (codecComm as GenericTcpIpClient).Hostname;
+ codecIpPort = (codecComm as GenericTcpIpClient).Port;
+ codecHasIpInfo = true;
+ }
+
+ if (codecHasIpInfo)
+ {
+ codecIpAddressSig = FusionRoom.CreateOffsetStringSig(121, "IP Address - VC", eSigIoMask.InputSigOnly);
+ codecIpAddressSig.InputSig.StringValue = codecIpAddress;
+
+ codecIpPortSig = FusionRoom.CreateOffsetStringSig(150, "IP Port - VC", eSigIoMask.InputSigOnly);
+ codecIpPortSig.InputSig.StringValue = codecIpPort.ToString();
+ }
+
+ var tempAsset = new FusionAsset();
+
+ var deviceConfig = ConfigReader.ConfigObject.Devices.FirstOrDefault(c => c.Key.Equals(codec.Key));
+
+ if (FusionStaticAssets.ContainsKey(deviceConfig.Uid))
+ {
+ tempAsset = FusionStaticAssets[deviceConfig.Uid];
+ }
+ else
+ {
+ // Create a new asset
+ tempAsset = new FusionAsset(FusionRoomGuids.GetNextAvailableAssetNumber(FusionRoom), codec.Name, "Codec", "");
+ FusionStaticAssets.Add(deviceConfig.Uid, tempAsset);
+ }
+
+ var codecAsset = FusionRoom.CreateStaticAsset(tempAsset.SlotNumber, tempAsset.Name, "Display", tempAsset.InstanceId);
+ codecAsset.PowerOn.OutputSig.UserObject = codecPowerOnAction;
+ codecAsset.PowerOff.OutputSig.UserObject = codecPowerOffAction;
+ codec.StandbyIsOnFeedback.LinkComplementInputSig(codecAsset.PowerOn.InputSig);
+
+ // TODO: Map relevant attributes on asset symbol
+
+ codecAsset.TrySetMakeModel(codec);
+ codecAsset.TryLinkAssetErrorToCommunication(codec);
+ }
+ catch (Exception e)
+ {
+ Debug.Console(1, this, "Error setting up codec in Fusion: {0}", e);
+ }
+ }
+
+ void codec_CallStatusChange(object sender, PepperDash.Essentials.Devices.Common.Codec.CodecCallStatusItemChangeEventArgs e)
+ {
+ var codec = (Room as EssentialsHuddleVtc1Room).VideoCodec;
+
+ CodecIsInCall.InputSig.BoolValue = codec.IsInCall;
+ }
+
+ // These methods are overridden because they access the room class which is of a different type
+
+ protected override void CreateSymbolAndBasicSigs(uint ipId)
+ {
+ Debug.Console(1, this, "Creating Fusion Room symbol with GUID: {0}", RoomGuid);
+
+ FusionRoom = new FusionRoom(ipId, Global.ControlSystem, Room.Name, RoomGuid);
+ FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.Use();
+ FusionRoom.ExtenderFusionRoomDataReservedSigs.Use();
+
+ FusionRoom.Register();
+
+ FusionRoom.FusionStateChange += FusionRoom_FusionStateChange;
+
+ FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.DeviceExtenderSigChange += FusionRoomSchedule_DeviceExtenderSigChange;
+ FusionRoom.ExtenderFusionRoomDataReservedSigs.DeviceExtenderSigChange += ExtenderFusionRoomDataReservedSigs_DeviceExtenderSigChange;
+ FusionRoom.OnlineStatusChange += FusionRoom_OnlineStatusChange;
+
+ CrestronConsole.AddNewConsoleCommand(RequestFullRoomSchedule, "FusReqRoomSchedule", "Requests schedule of the room for the next 24 hours", ConsoleAccessLevelEnum.AccessOperator);
+ CrestronConsole.AddNewConsoleCommand(ModifyMeetingEndTimeConsoleHelper, "FusReqRoomSchMod", "Ends or extends a meeting by the specified time", ConsoleAccessLevelEnum.AccessOperator);
+ CrestronConsole.AddNewConsoleCommand(CreateAsHocMeeting, "FusCreateMeeting", "Creates and Ad Hoc meeting for on hour or until the next meeting", ConsoleAccessLevelEnum.AccessOperator);
+
+ // Room to fusion room
+ Room.OnFeedback.LinkInputSig(FusionRoom.SystemPowerOn.InputSig);
+
+ // Moved to
+ CurrentRoomSourceNameSig = FusionRoom.CreateOffsetStringSig(84, "Display 1 - Current Source", eSigIoMask.InputSigOnly);
+ // Don't think we need to get current status of this as nothing should be alive yet.
+ (Room as EssentialsHuddleVtc1Room).CurrentSingleSourceChange += Room_CurrentSourceInfoChange;
+
+
+ FusionRoom.SystemPowerOn.OutputSig.SetSigFalseAction((Room as EssentialsHuddleVtc1Room).PowerOnToDefaultOrLastSource);
+ FusionRoom.SystemPowerOff.OutputSig.SetSigFalseAction(() => (Room as EssentialsHuddleVtc1Room).RunRouteAction("roomOff"));
+ // NO!! room.RoomIsOn.LinkComplementInputSig(FusionRoom.SystemPowerOff.InputSig);
+
+
+ CrestronEnvironment.EthernetEventHandler += CrestronEnvironment_EthernetEventHandler;
+ }
+
+ protected override void SetUpSources()
+ {
+ // Sources
+ var dict = ConfigReader.ConfigObject.GetSourceListForKey((Room as EssentialsHuddleVtc1Room).SourceListKey);
+ if (dict != null)
+ {
+ // NEW PROCESS:
+ // Make these lists and insert the fusion attributes by iterating these
+ var setTopBoxes = dict.Where(d => d.Value.SourceDevice is ISetTopBoxControls);
+ uint i = 1;
+ foreach (var kvp in setTopBoxes)
+ {
+ TryAddRouteActionSigs("Display 1 - Source TV " + i, 188 + i, kvp.Key, kvp.Value.SourceDevice);
+ i++;
+ if (i > 5) // We only have five spots
+ break;
+ }
+
+ var discPlayers = dict.Where(d => d.Value.SourceDevice is IDiscPlayerControls);
+ i = 1;
+ foreach (var kvp in discPlayers)
+ {
+ TryAddRouteActionSigs("Display 1 - Source DVD " + i, 181 + i, kvp.Key, kvp.Value.SourceDevice);
+ i++;
+ if (i > 5) // We only have five spots
+ break;
+ }
+
+ var laptops = dict.Where(d => d.Value.SourceDevice is Laptop);
+ i = 1;
+ foreach (var kvp in laptops)
+ {
+ TryAddRouteActionSigs("Display 1 - Source Laptop " + i, 166 + i, kvp.Key, kvp.Value.SourceDevice);
+ i++;
+ if (i > 10) // We only have ten spots???
+ break;
+ }
+
+ foreach (var kvp in dict)
+ {
+ var usageDevice = kvp.Value.SourceDevice as IUsageTracking;
+
+ if (usageDevice != null)
+ {
+ usageDevice.UsageTracker = new UsageTracking(usageDevice as Device);
+ usageDevice.UsageTracker.UsageIsTracked = true;
+ usageDevice.UsageTracker.DeviceUsageEnded += new EventHandler(UsageTracker_DeviceUsageEnded);
+ }
+ }
+
+ }
+ else
+ {
+ Debug.Console(1, this, "WARNING: Config source list '{0}' not found for room '{1}'",
+ (Room as EssentialsHuddleVtc1Room).SourceListKey, Room.Key);
+ }
+ }
+
+ protected override void SetUpDisplay()
+ {
+ try
+ {
+ //Setup Display Usage Monitoring
+
+ var displays = DeviceManager.AllDevices.Where(d => d is DisplayBase);
+
+ // Consider updating this in multiple display systems
+
+ foreach (DisplayBase display in displays)
+ {
+ display.UsageTracker = new UsageTracking(display);
+ display.UsageTracker.UsageIsTracked = true;
+ display.UsageTracker.DeviceUsageEnded += new EventHandler(UsageTracker_DeviceUsageEnded);
+ }
+
+ var defaultDisplay = (Room as EssentialsHuddleVtc1Room).DefaultDisplay as DisplayBase;
+ if (defaultDisplay == null)
+ {
+ Debug.Console(1, this, "Cannot link null display to Fusion because default display is null");
+ return;
+ }
+
+ var dispPowerOnAction = new Action(b => { if (!b) defaultDisplay.PowerOn(); });
+ var dispPowerOffAction = new Action(b => { if (!b) defaultDisplay.PowerOff(); });
+
+ // Display to fusion room sigs
+ FusionRoom.DisplayPowerOn.OutputSig.UserObject = dispPowerOnAction;
+ FusionRoom.DisplayPowerOff.OutputSig.UserObject = dispPowerOffAction;
+ defaultDisplay.PowerIsOnFeedback.LinkInputSig(FusionRoom.DisplayPowerOn.InputSig);
+ if (defaultDisplay is IDisplayUsage)
+ (defaultDisplay as IDisplayUsage).LampHours.LinkInputSig(FusionRoom.DisplayUsage.InputSig);
+
+
+
+ MapDisplayToRoomJoins(1, 158, defaultDisplay);
+
+
+ var deviceConfig = ConfigReader.ConfigObject.Devices.FirstOrDefault(d => d.Key.Equals(defaultDisplay.Key));
+
+ //Check for existing asset in GUIDs collection
+
+ var tempAsset = new FusionAsset();
+
+ if (FusionStaticAssets.ContainsKey(deviceConfig.Uid))
+ {
+ tempAsset = FusionStaticAssets[deviceConfig.Uid];
+ }
+ else
+ {
+ // Create a new asset
+ tempAsset = new FusionAsset(FusionRoomGuids.GetNextAvailableAssetNumber(FusionRoom), defaultDisplay.Name, "Display", "");
+ FusionStaticAssets.Add(deviceConfig.Uid, tempAsset);
+ }
+
+ var dispAsset = FusionRoom.CreateStaticAsset(tempAsset.SlotNumber, tempAsset.Name, "Display", tempAsset.InstanceId);
+ dispAsset.PowerOn.OutputSig.UserObject = dispPowerOnAction;
+ dispAsset.PowerOff.OutputSig.UserObject = dispPowerOffAction;
+ defaultDisplay.PowerIsOnFeedback.LinkInputSig(dispAsset.PowerOn.InputSig);
+ // NO!! display.PowerIsOn.LinkComplementInputSig(dispAsset.PowerOff.InputSig);
+ // Use extension methods
+ dispAsset.TrySetMakeModel(defaultDisplay);
+ dispAsset.TryLinkAssetErrorToCommunication(defaultDisplay);
+ }
+ catch (Exception e)
+ {
+ Debug.Console(1, this, "Error setting up display in Fusion: {0}", e);
+ }
+
+ }
+
+ protected override void MapDisplayToRoomJoins(int displayIndex, int joinOffset, DisplayBase display)
+ {
+ string displayName = string.Format("Display {0} - ", displayIndex);
+
+
+ if (display == (Room as EssentialsHuddleVtc1Room).DefaultDisplay)
+ {
+ // Power on
+ var defaultDisplayPowerOn = FusionRoom.CreateOffsetBoolSig((uint)joinOffset, displayName + "Power On", eSigIoMask.InputOutputSig);
+ defaultDisplayPowerOn.OutputSig.UserObject = new Action(b => { if (!b) display.PowerOn(); });
+ display.PowerIsOnFeedback.LinkInputSig(defaultDisplayPowerOn.InputSig);
+
+ // Power Off
+ var defaultDisplayPowerOff = FusionRoom.CreateOffsetBoolSig((uint)joinOffset + 1, displayName + "Power Off", eSigIoMask.InputOutputSig);
+ defaultDisplayPowerOn.OutputSig.UserObject = new Action(b => { if (!b) display.PowerOff(); }); ;
+ display.PowerIsOnFeedback.LinkInputSig(defaultDisplayPowerOn.InputSig);
+
+ // Current Source
+ var defaultDisplaySourceNone = FusionRoom.CreateOffsetBoolSig((uint)joinOffset + 8, displayName + "Source None", eSigIoMask.InputOutputSig);
+ defaultDisplaySourceNone.OutputSig.UserObject = new Action(b => { if (!b) (Room as EssentialsHuddleVtc1Room).RunRouteAction("roomOff"); }); ;
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/PepperDashEssentials/OTHER/Fusion/FusionCustomPropertiesBridge.cs b/PepperDashEssentials/OTHER/Fusion/FusionCustomPropertiesBridge.cs
new file mode 100644
index 00000000..6dface95
--- /dev/null
+++ b/PepperDashEssentials/OTHER/Fusion/FusionCustomPropertiesBridge.cs
@@ -0,0 +1,102 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Crestron.SimplSharp;
+
+using PepperDash.Core;
+using PepperDash.Essentials.Core;
+using PepperDash.Essentials.Core.Config;
+using PepperDash.Essentials.Room.Behaviours;
+
+namespace PepperDash.Essentials.Fusion
+{
+ ///
+ /// Handles mapping Fusion Custom Property values to system properties
+ ///
+ public class FusionCustomPropertiesBridge
+ {
+
+ ///
+ /// Evaluates the room info and custom properties from Fusion and updates the system properties aa needed
+ ///
+ ///
+ public void EvaluateRoomInfo(string roomKey, RoomInformation roomInfo)
+ {
+ var runtimeConfigurableDevices = DeviceManager.AllDevices.Where(d => d is IRuntimeConfigurableDevice);
+
+ try
+ {
+ foreach (var device in runtimeConfigurableDevices)
+ {
+ // Get the current device config so new values can be overwritten over existing
+ var deviceConfig = (device as IRuntimeConfigurableDevice).GetDeviceConfig();
+
+ if (device is RoomOnToDefaultSourceWhenOccupied)
+ {
+ var devConfig = (deviceConfig as RoomOnToDefaultSourceWhenOccupiedConfig);
+
+ var enableFeature = roomInfo.FusionCustomProperties.FirstOrDefault(p => p.ID.Equals("EnRoomOnWhenOccupied"));
+ if (enableFeature != null)
+ devConfig.EnableRoomOnWhenOccupied = bool.Parse(enableFeature.CustomFieldValue);
+
+ var enableTime = roomInfo.FusionCustomProperties.FirstOrDefault(p => p.ID.Equals("RoomOnWhenOccupiedStartTime"));
+ if (enableTime != null)
+ devConfig.OccupancyStartTime = enableTime.CustomFieldValue;
+
+ var disableTime = roomInfo.FusionCustomProperties.FirstOrDefault(p => p.ID.Equals("RoomOnWhenOccupiedEndTime"));
+ if (disableTime != null)
+ devConfig.OccupancyEndTime = disableTime.CustomFieldValue;
+
+ var enableSunday = roomInfo.FusionCustomProperties.FirstOrDefault(p => p.ID.Equals("EnRoomOnWhenOccupiedSun"));
+ if (enableSunday != null)
+ devConfig.EnableSunday = bool.Parse(enableSunday.CustomFieldValue);
+
+ var enableMonday = roomInfo.FusionCustomProperties.FirstOrDefault(p => p.ID.Equals("EnRoomOnWhenOccupiedMon"));
+ if (enableMonday != null)
+ devConfig.EnableMonday = bool.Parse(enableMonday.CustomFieldValue);
+
+ var enableTuesday = roomInfo.FusionCustomProperties.FirstOrDefault(p => p.ID.Equals("EnRoomOnWhenOccupiedTue"));
+ if (enableTuesday != null)
+ devConfig.EnableTuesday = bool.Parse(enableTuesday.CustomFieldValue);
+
+ var enableWednesday = roomInfo.FusionCustomProperties.FirstOrDefault(p => p.ID.Equals("EnRoomOnWhenOccupiedWed"));
+ if (enableWednesday != null)
+ devConfig.EnableWednesday = bool.Parse(enableWednesday.CustomFieldValue);
+
+ var enableThursday = roomInfo.FusionCustomProperties.FirstOrDefault(p => p.ID.Equals("EnRoomOnWhenOccupiedThu"));
+ if (enableThursday != null)
+ devConfig.EnableThursday = bool.Parse(enableThursday.CustomFieldValue);
+
+ var enableFriday = roomInfo.FusionCustomProperties.FirstOrDefault(p => p.ID.Equals("EnRoomOnWhenOccupiedFri"));
+ if (enableFriday != null)
+ devConfig.EnableFriday = bool.Parse(enableFriday.CustomFieldValue);
+
+ var enableSaturday = roomInfo.FusionCustomProperties.FirstOrDefault(p => p.ID.Equals("EnRoomOnWhenOccupiedSat"));
+ if (enableSaturday != null)
+ devConfig.EnableSaturday = bool.Parse(enableSaturday.CustomFieldValue);
+
+ deviceConfig = devConfig;
+ }
+
+ // Set the config on the device
+ (device as IRuntimeConfigurableDevice).SetDeviceConfig(deviceConfig);
+ }
+
+ //var roomConfig = ConfigReader.ConfigObject.Rooms.FirstOrDefault(r => r.Key.Equals(roomKey);
+
+ //if(roomConfig != null)
+ //{
+ // roomConfig.Name = roomInfo.Name;
+
+ // // Update HelpMessage in room properties
+ // roomConfig.Properties.
+ //}
+ }
+ catch (Exception e)
+ {
+ Debug.Console(1, "FusionCustomPropetiesBridge: Error mapping properties: {0}", e);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/PepperDashEssentials/PepperDashEssentials.csproj b/PepperDashEssentials/PepperDashEssentials.csproj
index b4bfc66a..59c180ee 100644
--- a/PepperDashEssentials/PepperDashEssentials.csproj
+++ b/PepperDashEssentials/PepperDashEssentials.csproj
@@ -99,12 +99,19 @@
False
..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpReflectionInterface.dll
+
+ False
+ ..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpTimerEventInterface.dll
+
+
+
+
@@ -119,8 +126,6 @@
-
-
@@ -128,6 +133,7 @@
+
@@ -135,25 +141,28 @@
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -178,7 +187,7 @@
-
+
@@ -191,7 +200,7 @@
-
+
diff --git a/PepperDashEssentials/Properties/AssemblyInfo.cs b/PepperDashEssentials/Properties/AssemblyInfo.cs
index 7541bd8a..36c70ef4 100644
--- a/PepperDashEssentials/Properties/AssemblyInfo.cs
+++ b/PepperDashEssentials/Properties/AssemblyInfo.cs
@@ -3,6 +3,6 @@
[assembly: AssemblyTitle("PepperDashEssentials")]
[assembly: AssemblyCompany("PepperDash Technology Corp")]
[assembly: AssemblyProduct("PepperDashEssentials")]
-[assembly: AssemblyCopyright("Copyright © PepperDash Technology Corp 2017")]
-[assembly: AssemblyVersion("1.2.4.*")]
+[assembly: AssemblyCopyright("Copyright © PepperDash Technology Corp 2018")]
+[assembly: AssemblyVersion("1.2.6.*")]
diff --git a/PepperDashEssentials/Room/Behaviours/RoomOnToDefaultSourceWhenOccupied.cs b/PepperDashEssentials/Room/Behaviours/RoomOnToDefaultSourceWhenOccupied.cs
new file mode 100644
index 00000000..823ed2b7
--- /dev/null
+++ b/PepperDashEssentials/Room/Behaviours/RoomOnToDefaultSourceWhenOccupied.cs
@@ -0,0 +1,534 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+using Crestron.SimplSharp;
+using Crestron.SimplSharp.Scheduler;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+
+using PepperDash.Core;
+using PepperDash.Essentials.Core;
+using PepperDash.Essentials.Core.Config;
+using PepperDash.Essentials.Devices.Common.Occupancy;
+
+namespace PepperDash.Essentials.Room.Behaviours
+{
+ ///
+ /// A device that when linked to a room can power the room on when enabled during scheduled hours.
+ ///
+ public class RoomOnToDefaultSourceWhenOccupied : Device, IRuntimeConfigurableDevice
+ {
+ RoomOnToDefaultSourceWhenOccupiedConfig Config;
+
+ public bool FeatureEnabled { get; private set; }
+
+ public DateTime FeatureEnabledTime { get; private set; }
+
+ ScheduledEvent FeatureEnableEvent;
+
+ const string FeatureEnableEventName = "EnableRoomOnToDefaultSourceWhenOccupied";
+
+ public DateTime FeatureDisabledTime { get; private set; }
+
+ ScheduledEvent FeatureDisableEvent;
+
+ const string FeatureDisableEventName = "DisableRoomOnToDefaultSourceWhenOccupied";
+
+ ScheduledEventGroup FeatureEventGroup;
+
+ public EssentialsRoomBase Room { get; private set; }
+
+ private Fusion.EssentialsHuddleSpaceFusionSystemControllerBase FusionRoom;
+
+ public RoomOnToDefaultSourceWhenOccupied(string key, RoomOnToDefaultSourceWhenOccupiedConfig config)
+ : base(key)
+ {
+ Config = config;
+
+ FeatureEventGroup = new ScheduledEventGroup(this.Key);
+
+ FeatureEventGroup.RetrieveAllEvents();
+
+ // Add to the global class for tracking
+ Scheduler.AddEventGroup(FeatureEventGroup);
+
+ AddPostActivationAction(() =>
+ {
+ // Subscribe to room event to know when RoomOccupancy is set and ready to be subscribed to
+ if (Room != null)
+ Room.RoomOccupancyIsSet += new EventHandler(RoomOccupancyIsSet);
+
+ else
+ Debug.Console(1, this, "Room has no RoomOccupancy object set");
+
+ var fusionRoomKey = Config.RoomKey + "-fusion";
+
+ FusionRoom = DeviceManager.GetDeviceForKey(fusionRoomKey) as Fusion.EssentialsHuddleSpaceFusionSystemControllerBase;
+
+ if (FusionRoom == null)
+ Debug.Console(1, this, "Unable to get Fusion Room from Device Manager with key: {0}", fusionRoomKey);
+ });
+ }
+
+ public override bool CustomActivate()
+ {
+ SetUpDevice();
+
+ return base.CustomActivate();
+ }
+
+ void SetUpDevice()
+ {
+ Room = DeviceManager.GetDeviceForKey(Config.RoomKey) as EssentialsRoomBase;
+
+ if (Room != null)
+ {
+ try
+ {
+ FeatureEnabledTime = DateTime.Parse(Config.OccupancyStartTime);
+
+ if (FeatureEnabledTime != null)
+ {
+ Debug.Console(1, this, "Enabled Time: {0}", FeatureEnabledTime.ToString());
+ }
+ else
+ Debug.Console(1, this, "Unable to parse {0} to DateTime", Config.OccupancyStartTime);
+ }
+ catch (Exception e)
+ {
+ Debug.Console(1, this, "Unable to parse OccupancyStartTime property: {0} \n Error: {1}", Config.OccupancyStartTime, e);
+ }
+
+ try
+ {
+ FeatureDisabledTime = DateTime.Parse(Config.OccupancyEndTime);
+
+ if (FeatureDisabledTime != null)
+ {
+ Debug.Console(1, this, "Disabled Time: {0}", FeatureDisabledTime.ToString());
+ }
+ else
+ Debug.Console(1, this, "Unable to parse {0} to DateTime", Config.OccupancyEndTime);
+ }
+ catch (Exception e)
+ {
+ Debug.Console(1, this, "Unable to parse a DateTime config value \n Error: {1}", e);
+ }
+
+ if (!Config.EnableRoomOnWhenOccupied)
+ FeatureEventGroup.ClearAllEvents();
+ else
+ {
+ AddEnableEventToGroup();
+
+ AddDisableEventToGroup();
+
+ FeatureEventGroup.UserGroupCallBack += new ScheduledEventGroup.UserEventGroupCallBack(FeatureEventGroup_UserGroupCallBack);
+
+ FeatureEventGroup.EnableAllEvents();
+ }
+
+ FeatureEnabled = CheckIfFeatureShouldBeEnabled();
+ }
+ else
+ Debug.Console(1, this, "Unable to get room from Device Manager with key: {0}", Config.RoomKey);
+ }
+
+ ///
+ /// Returns a JObject of the device config properties as they currently exist at runtime
+ ///
+ ///
+ public JToken GetLocalConfigProperties()
+ {
+ return JToken.FromObject(Config);
+ }
+
+ public object GetDeviceConfig()
+ {
+ return Config;
+ }
+
+ public void SetDeviceConfig(object config)
+ {
+ var newConfig = config as RoomOnToDefaultSourceWhenOccupiedConfig;
+
+ Config = newConfig;
+
+ ConfigWriter.UpdateDeviceProperties(this.Key, GetLocalConfigProperties());
+
+ SetUpDevice();
+ }
+
+ ///
+ /// Subscribe to feedback from RoomIsOccupiedFeedback on Room
+ ///
+ ///
+ ///
+ void RoomOccupancyIsSet(object sender, EventArgs e)
+ {
+ if (Room.RoomOccupancy != null)
+ {
+ Room.RoomOccupancy.RoomIsOccupiedFeedback.OutputChange -= RoomIsOccupiedFeedback_OutputChange;
+ Room.RoomOccupancy.RoomIsOccupiedFeedback.OutputChange += new EventHandler(RoomIsOccupiedFeedback_OutputChange);
+ Debug.Console(1, this, "Subscribed to RoomOccupancy status from: '{0}'", Room.Key);
+ }
+ }
+
+ void FeatureEventGroup_UserGroupCallBack(ScheduledEvent SchEvent, ScheduledEventCommon.eCallbackReason type)
+ {
+ if (type == ScheduledEventCommon.eCallbackReason.NormalExpiration)
+ {
+ if (SchEvent.Name == FeatureEnableEventName)
+ {
+ if (Config.EnableRoomOnWhenOccupied)
+ FeatureEnabled = true;
+
+ Debug.Console(1, this, "*****Feature Enabled by event.*****");
+ }
+ else if (SchEvent.Name == FeatureDisableEventName)
+ {
+ FeatureEnabled = false;
+
+ Debug.Console(1, this, "*****Feature Disabled by event.*****");
+ }
+ }
+ }
+
+ ///
+ /// Checks if the feature should be currently enabled. Used on startup if processor starts after start time but before end time
+ ///
+ ///
+ bool CheckIfFeatureShouldBeEnabled()
+ {
+ bool enabled = false;
+
+ if(Config.EnableRoomOnWhenOccupied)
+ {
+ Debug.Console(1, this, "Current Time: {0} \n FeatureEnabledTime: {1} \n FeatureDisabledTime: {2}", DateTime.Now, FeatureEnabledTime, FeatureDisabledTime);
+
+ if (DateTime.Now.TimeOfDay.CompareTo(FeatureEnabledTime.TimeOfDay) >= 0 && FeatureDisabledTime.TimeOfDay.CompareTo(DateTime.Now.TimeOfDay) > 0)
+ {
+ if (SchedulerUtilities.CheckIfDayOfWeekMatchesRecurrenceDays(DateTime.Now, CalculateDaysOfWeekRecurrence()))
+ {
+ enabled = true;
+ }
+ }
+ }
+
+ if(enabled)
+ Debug.Console(1, this, "*****Feature Enabled*****");
+ else
+ Debug.Console(1, this, "*****Feature Disabled*****");
+
+ return enabled;
+ }
+
+ ///
+ /// Respond to Occupancy status event
+ ///
+ ///
+ ///
+ void RoomIsOccupiedFeedback_OutputChange(object sender, FeedbackEventArgs e)
+ {
+ Debug.Console(1, this, "RoomIsOccupiedFeeback.OutputChange event fired. e.BoolValue: {0}", e.BoolValue);
+ if(e.BoolValue)
+ {
+ // Occupancy detected
+
+ if (FeatureEnabled)
+ {
+ // Check room power state first
+ if (!Room.OnFeedback.BoolValue)
+ {
+ Debug.Console(1, this, "Powering Room on to default source");
+ Room.RunDefaultPresentRoute();
+ }
+ }
+ }
+ }
+
+ void CreateEvent(ScheduledEvent schEvent, string name)
+ {
+ Debug.Console(1, this, "Adding Event: '{0}'", name);
+ // Create the event
+ if (schEvent == null)
+ schEvent = new ScheduledEvent(name, FeatureEventGroup);
+
+ // Set up its initial properties
+
+ if(!schEvent.Acknowledgeable)
+ schEvent.Acknowledgeable = true;
+
+ if(!schEvent.Persistent)
+ schEvent.Persistent = true;
+
+ schEvent.DateAndTime.SetFirstDayOfWeek(ScheduledEventCommon.eFirstDayOfWeek.Sunday);
+
+ // Set config driven properties
+
+ if (schEvent.Name == FeatureEnableEventName)
+ {
+ schEvent.Description = "Enables the RoomOnToDefaultSourceWhenOccupiedFeature";
+
+ var eventRecurrennce = CalculateDaysOfWeekRecurrence();
+
+ var eventTime = new DateTime();
+
+ // Check to make sure the date for this event is in the future
+ if (DateTime.Now.CompareTo(FeatureEnabledTime) > 0)
+ eventTime = FeatureEnabledTime.AddDays(1);
+ else
+ eventTime = FeatureEnabledTime;
+
+ Debug.Console(1, this, "eventTime (before recurrence check): {0}", eventTime);
+
+ // Check day of week against recurrence days and move date ahead as necessary to avoid throwing an exception by trying to set the event
+ // start date on a day of the week that doesn't match teh recurrence values
+ while(!SchedulerUtilities.CheckIfDayOfWeekMatchesRecurrenceDays(eventTime, eventRecurrennce))
+ {
+ eventTime = eventTime.AddDays(1);
+ Debug.Console(1, this, "eventTime does not fall on a recurrence weekday. eventTime: {0}", eventTime);
+ }
+
+ schEvent.DateAndTime.SetAbsoluteEventTime(eventTime);
+
+ Debug.Console(1, this, "Event '{0}' Absolute time set to {1}", schEvent.Name, schEvent.DateAndTime.ToString());
+
+ CalculateAndSetAcknowledgeExpirationTimeout(schEvent, FeatureEnabledTime, FeatureDisabledTime);
+
+ schEvent.Recurrence.Weekly(eventRecurrennce);
+
+ }
+ else if (schEvent.Name == FeatureDisableEventName)
+ {
+ schEvent.Description = "Disables the RoomOnToDefaultSourceWhenOccupiedFeature";
+
+ // Check to make sure the date for this event is in the future
+ if (DateTime.Now.CompareTo(FeatureDisabledTime) > 0)
+ schEvent.DateAndTime.SetAbsoluteEventTime(FeatureDisabledTime.AddDays(1));
+ else
+ schEvent.DateAndTime.SetAbsoluteEventTime(FeatureDisabledTime);
+
+ Debug.Console(1, this, "Event '{0}' Absolute time set to {1}", schEvent.Name, schEvent.DateAndTime.ToString());
+
+ CalculateAndSetAcknowledgeExpirationTimeout(schEvent, FeatureDisabledTime, FeatureEnabledTime);
+
+ schEvent.Recurrence.Daily();
+ }
+ }
+
+ void CalculateAndSetAcknowledgeExpirationTimeout(ScheduledEvent schEvent, DateTime time1, DateTime time2)
+ {
+ Debug.Console(1, this, "time1.Hour = {0}", time1.Hour);
+ Debug.Console(1, this, "time2.Hour = {0}", time2.Hour);
+ Debug.Console(1, this, "time1.Minute = {0}", time1.Minute);
+ Debug.Console(1, this, "time2.Minute = {0}", time2.Minute);
+
+ // Calculate the Acknowledge Expiration timer to be the time between the enable and dispable events, less one minute
+ var ackHours = time2.Hour - time1.Hour;
+ if(ackHours < 0)
+ ackHours = ackHours + 24;
+ var ackMinutes = time2.Minute - time1.Minute;
+
+ Debug.Console(1, this, "ackHours = {0}, ackMinutes = {1}", ackHours, ackMinutes);
+
+ var ackTotalMinutes = ((ackHours * 60) + ackMinutes) - 1;
+
+ var ackExpHour = ackTotalMinutes / 60;
+ var ackExpMinutes = ackTotalMinutes % 60;
+
+ Debug.Console(1, this, "Acknowledge Expiration Timeout: {0} hours, {1} minutes", ackExpHour, ackExpMinutes);
+
+ schEvent.AcknowledgeExpirationTimeout.Hour = (ushort)(ackHours);
+ schEvent.AcknowledgeExpirationTimeout.Minute = (ushort)(ackExpMinutes);
+ }
+
+ ///
+ /// Checks existing event to see if it matches the execution time
+ ///
+ ///
+ ///
+ bool CheckExistingEventTimeForMatch(ScheduledEvent existingEvent, DateTime newTime)
+ {
+ bool isMatch = true;
+
+ // Check to see if hour and minute match
+ if (existingEvent.DateAndTime.Hour != newTime.Hour || existingEvent.DateAndTime.Minute != newTime.Minute)
+ return false;
+
+
+ return isMatch;
+ }
+
+ ///
+ /// Checks existing event to see if it matches the recurrence days
+ ///
+ ///
+ ///
+ ///
+ bool CheckExistingEventRecurrenceForMatch(ScheduledEvent existingEvent, ScheduledEventCommon.eWeekDays eWeekdays)
+ {
+ bool isMatch = true;
+
+ // Check to see if recurrence matches
+ if (eWeekdays != existingEvent.Recurrence.RecurrenceDays)
+ return false;
+
+ return isMatch;
+ }
+
+ ///
+ /// Adds the Enable event to the local event group and sets its properties based on config
+ ///
+ void AddEnableEventToGroup()
+ {
+ if (!FeatureEventGroup.ScheduledEvents.ContainsKey(FeatureEnableEventName))
+ {
+ CreateEvent(FeatureEnableEvent, FeatureEnableEventName);
+ }
+ else
+ {
+ // Check if existing event has same time and recurrence as config values
+
+ FeatureEnableEvent = FeatureEventGroup.ScheduledEvents[FeatureEnableEventName];
+ Debug.Console(1, this, "Enable event already found in group");
+
+ // Check config times and days against DateAndTime of existing event. If different, delete existing event and create new event
+ if(!CheckExistingEventTimeForMatch(FeatureEnableEvent, FeatureEnabledTime) || !CheckExistingEventRecurrenceForMatch(FeatureEnableEvent, CalculateDaysOfWeekRecurrence()))
+ {
+ Debug.Console(1, this, "Existing event does not match new config properties. Deleting exisiting event: '{0}'", FeatureEnableEvent.Name);
+ FeatureEventGroup.DeleteEvent(FeatureEnableEvent);
+
+ FeatureEnableEvent = null;
+
+ CreateEvent(FeatureEnableEvent, FeatureEnableEventName);
+ }
+ }
+
+ }
+
+ ///
+ /// Adds the Enable event to the local event group and sets its properties based on config
+ ///
+ void AddDisableEventToGroup()
+ {
+ if (!FeatureEventGroup.ScheduledEvents.ContainsKey(FeatureDisableEventName))
+ {
+ CreateEvent(FeatureDisableEvent, FeatureDisableEventName);
+ }
+ else
+ {
+ FeatureDisableEvent = FeatureEventGroup.ScheduledEvents[FeatureDisableEventName];
+ Debug.Console(1, this, "Disable event already found in group");
+
+ // Check config times against DateAndTime of existing event. If different, delete existing event and create new event
+ if(!CheckExistingEventTimeForMatch(FeatureDisableEvent, FeatureDisabledTime))
+ {
+ Debug.Console(1, this, "Existing event does not match new config properties. Deleting exisiting event: '{0}'", FeatureDisableEvent.Name);
+
+ FeatureEventGroup.DeleteEvent(FeatureDisableEvent);
+
+ FeatureDisableEvent = null;
+
+ CreateEvent(FeatureDisableEvent, FeatureDisableEventName);
+ }
+ }
+ }
+
+
+ ///
+ /// Calculates the correct bitfield enum value for the event recurrence based on the config values
+ ///
+ ///
+ ScheduledEventCommon.eWeekDays CalculateDaysOfWeekRecurrence()
+ {
+ ScheduledEventCommon.eWeekDays value = new ScheduledEventCommon.eWeekDays();
+
+ if (Config.EnableSunday)
+ value = value | ScheduledEventCommon.eWeekDays.Sunday;
+ if (Config.EnableMonday)
+ value = value | ScheduledEventCommon.eWeekDays.Monday;
+ if (Config.EnableTuesday)
+ value = value | ScheduledEventCommon.eWeekDays.Tuesday;
+ if (Config.EnableWednesday)
+ value = value | ScheduledEventCommon.eWeekDays.Wednesday;
+ if (Config.EnableThursday)
+ value = value | ScheduledEventCommon.eWeekDays.Thursday;
+ if (Config.EnableFriday)
+ value = value | ScheduledEventCommon.eWeekDays.Friday;
+ if (Config.EnableSaturday)
+ value = value | ScheduledEventCommon.eWeekDays.Saturday;
+
+ return value;
+ }
+
+ ///
+ /// Callback for event that enables feature. Enables feature if config property is true
+ ///
+ ///
+ ///
+ void FeatureEnableEvent_UserCallBack(ScheduledEvent SchEvent, ScheduledEventCommon.eCallbackReason type)
+ {
+ if (type == ScheduledEventCommon.eCallbackReason.NormalExpiration)
+ {
+ if(Config.EnableRoomOnWhenOccupied)
+ FeatureEnabled = true;
+
+ Debug.Console(1, this, "RoomOnToDefaultSourceWhenOccupied Feature Enabled.");
+ }
+ }
+
+ ///
+ /// Callback for event that enables feature. Disables feature
+ ///
+ ///
+ ///
+ void FeatureDisableEvent_UserCallBack(ScheduledEvent SchEvent, ScheduledEventCommon.eCallbackReason type)
+ {
+ if (type == ScheduledEventCommon.eCallbackReason.NormalExpiration)
+ {
+ FeatureEnabled = false;
+
+ Debug.Console(1, this, "RoomOnToDefaultSourceWhenOccupied Feature Disabled.");
+ }
+ }
+ }
+
+ public class RoomOnToDefaultSourceWhenOccupiedConfig
+ {
+ [JsonProperty("roomKey")]
+ public string RoomKey { get; set; }
+
+ [JsonProperty("enableRoomOnWhenOccupied")]
+ public bool EnableRoomOnWhenOccupied { get; set; }
+
+ [JsonProperty("occupancyStartTime")]
+ public string OccupancyStartTime { get; set; }
+
+ [JsonProperty("occupancyEndTime")]
+ public string OccupancyEndTime { get; set; }
+
+ [JsonProperty("enableSunday")]
+ public bool EnableSunday { get; set; }
+
+ [JsonProperty("enableMonday")]
+ public bool EnableMonday { get; set; }
+
+ [JsonProperty("enableTuesday")]
+ public bool EnableTuesday { get; set; }
+
+ [JsonProperty("enableWednesday")]
+ public bool EnableWednesday { get; set; }
+
+ [JsonProperty("enableThursday")]
+ public bool EnableThursday { get; set; }
+
+ [JsonProperty("enableFriday")]
+ public bool EnableFriday { get; set; }
+
+ [JsonProperty("enableSaturday")]
+ public bool EnableSaturday { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/PepperDashEssentials/Room/Config/EssentialsRoomConfig.cs b/PepperDashEssentials/Room/Config/EssentialsRoomConfig.cs
index 246bb42b..049c15dc 100644
--- a/PepperDashEssentials/Room/Config/EssentialsRoomConfig.cs
+++ b/PepperDashEssentials/Room/Config/EssentialsRoomConfig.cs
@@ -1,334 +1,334 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-
-using Crestron.SimplSharp;
-using Newtonsoft.Json;
-
-using PepperDash.Core;
-using PepperDash.Essentials;
-using PepperDash.Essentials.Core;
-using PepperDash.Essentials.Core.Config;
-
-namespace PepperDash.Essentials.Room.Config
-{
- public class EssentialsRoomConfig : DeviceConfig
- {
- ///
- /// Returns a room object from this config data
- ///
- ///
- public Device GetRoomObject()
- {
- var typeName = Type.ToLower();
- if (typeName == "huddle")
- {
- var props = JsonConvert.DeserializeObject
- (this.Properties.ToString());
- var disp = DeviceManager.GetDeviceForKey(props.DefaultDisplayKey) as IRoutingSinkWithSwitching;
- var audio = DeviceManager.GetDeviceForKey(props.DefaultAudioKey) as IRoutingSinkNoSwitching;
- var huddle = new EssentialsHuddleSpaceRoom(Key, Name, disp, audio, props);
-
- if (props.Occupancy != null)
- huddle.SetRoomOccupancy(DeviceManager.GetDeviceForKey(props.Occupancy.DeviceKey) as
- PepperDash.Essentials.Devices.Common.Occupancy.IOccupancyStatusProvider, props.Occupancy.TimoutMinutes);
- huddle.LogoUrl = props.Logo.GetUrl();
- huddle.SourceListKey = props.SourceListKey;
- huddle.DefaultSourceItem = props.DefaultSourceItem;
- huddle.DefaultVolume = (ushort)(props.Volumes.Master.Level * 65535 / 100);
- return huddle;
- }
- else if (typeName == "presentation")
- {
- var props = JsonConvert.DeserializeObject
- (this.Properties.ToString());
- var displaysDict = new Dictionary();
- uint i = 1;
- foreach (var dispKey in props.DisplayKeys) // read in the ordered displays list
- {
- var disp = DeviceManager.GetDeviceForKey(dispKey) as IRoutingSinkWithSwitching;
- displaysDict.Add(i++, disp);
- }
-
- // Get the master volume control
- IBasicVolumeWithFeedback masterVolumeControlDev = props.Volumes.Master.GetDevice();
-
-
- var presRoom = new EssentialsPresentationRoom(Key, Name, displaysDict, masterVolumeControlDev, props);
- return presRoom;
- }
- else if (typeName == "huddlevtc1")
- {
- var props = JsonConvert.DeserializeObject
- (this.Properties.ToString());
- var disp = DeviceManager.GetDeviceForKey(props.DefaultDisplayKey) as IRoutingSinkWithSwitching;
-
- var codec = DeviceManager.GetDeviceForKey(props.VideoCodecKey) as
- PepperDash.Essentials.Devices.Common.VideoCodec.VideoCodecBase;
-
- var rm = new EssentialsHuddleVtc1Room(Key, Name, disp, codec, codec, props);
- // Add Occupancy object from config
-
- if (props.Occupancy != null)
- rm.SetRoomOccupancy(DeviceManager.GetDeviceForKey(props.Occupancy.DeviceKey) as
- PepperDash.Essentials.Devices.Common.Occupancy.IOccupancyStatusProvider, props.Occupancy.TimoutMinutes);
- rm.LogoUrl = props.Logo.GetUrl();
- rm.SourceListKey = props.SourceListKey;
- rm.DefaultSourceItem = props.DefaultSourceItem;
- rm.DefaultVolume = (ushort)(props.Volumes.Master.Level * 65535 / 100);
-
- rm.MicrophonePrivacy = GetMicrophonePrivacy(props, rm); // Get Microphone Privacy object, if any
-
- rm.Emergency = GetEmergency(props, rm); // Get emergency object, if any
-
- return rm;
- }
- else if (typeName == "ddvc01Bridge")
- {
- return new Device(Key, Name); // placeholder device that does nothing.
- }
-
- return null;
- }
-
- ///
- /// Gets and operating, standalone emergegncy object that can be plugged into a room.
- /// Returns null if there is no emergency defined
- ///
- EssentialsRoomEmergencyBase GetEmergency(EssentialsRoomPropertiesConfig props, EssentialsRoomBase room)
- {
- // This emergency
- var emergency = props.Emergency;
- if (emergency != null)
- {
- //switch on emergency type here. Right now only contact and shutdown
- var e = new EssentialsRoomEmergencyContactClosure(room.Key + "-emergency", props.Emergency, room);
- DeviceManager.AddDevice(e);
- }
- return null;
- }
-
- ///
- ///
- ///
- ///
- ///
- ///
- PepperDash.Essentials.Devices.Common.Microphones.MicrophonePrivacyController GetMicrophonePrivacy(
- EssentialsRoomPropertiesConfig props, EssentialsHuddleVtc1Room room)
- {
- var microphonePrivacy = props.MicrophonePrivacy;
- if (microphonePrivacy == null)
- {
- Debug.Console(0, "ERROR: Cannot create microphone privacy with null properties");
- return null;
- }
- // Get the MicrophonePrivacy device from the device manager
- var mP = (DeviceManager.GetDeviceForKey(props.MicrophonePrivacy.DeviceKey) as
- PepperDash.Essentials.Devices.Common.Microphones.MicrophonePrivacyController);
- // Set this room as the IPrivacy device
- if (mP == null)
- {
- Debug.Console(0, "ERROR: Selected device {0} is not MicrophonePrivacyController", props.MicrophonePrivacy.DeviceKey);
- return null;
- }
- mP.SetPrivacyDevice(room);
-
- var behaviour = props.MicrophonePrivacy.Behaviour.ToLower();
-
- if (behaviour == null)
- {
- Debug.Console(0, "WARNING: No behaviour defined for MicrophonePrivacyController");
- return null;
- }
- if (behaviour == "trackroomstate")
- {
- // Tie LED enable to room power state
- room.OnFeedback.OutputChange += (o, a) =>
- {
- if (room.OnFeedback.BoolValue)
- mP.EnableLeds = true;
- else
- mP.EnableLeds = false;
- };
-
- mP.EnableLeds = room.OnFeedback.BoolValue;
- }
- else if (behaviour == "trackcallstate")
- {
- // Tie LED enable to room power state
- room.InCallFeedback.OutputChange += (o, a) =>
- {
- if (room.InCallFeedback.BoolValue)
- mP.EnableLeds = true;
- else
- mP.EnableLeds = false;
- };
-
- mP.EnableLeds = room.InCallFeedback.BoolValue;
- }
-
- return mP;
- }
-
- }
-
- ///
- ///
- ///
- public class EssentialsRoomPropertiesConfig
- {
- [JsonProperty("addresses")]
- public EssentialsRoomAddressPropertiesConfig Addresses { get; set; }
-
- [JsonProperty("description")]
- public string Description { get; set; }
-
- [JsonProperty("emergency")]
- public EssentialsRoomEmergencyConfig Emergency { get; set; }
-
- [JsonProperty("help")]
- public EssentialsHelpPropertiesConfig Help { get; set; }
-
- [JsonProperty("helpMessage")]
- public string HelpMessage { get; set; }
-
- [JsonProperty("environment")]
- public EssentialsEnvironmentPropertiesConfig Environment { get; set; }
-
- [JsonProperty("logo")]
- public EssentialsLogoPropertiesConfig Logo { get; set; }
-
- [JsonProperty("microphonePrivacy")]
- public EssentialsRoomMicrophonePrivacyConfig MicrophonePrivacy { get; set; }
-
- [JsonProperty("occupancy")]
- public EssentialsRoomOccSensorConfig Occupancy { get; set; }
-
- [JsonProperty("oneButtonMeeting")]
- public EssentialsOneButtonMeetingPropertiesConfig OneButtonMeeting { get; set; }
-
- [JsonProperty("shutdownVacancySeconds")]
- public int ShutdownVacancySeconds { get; set; }
-
- [JsonProperty("shutdownPromptSeconds")]
- public int ShutdownPromptSeconds { get; set; }
-
- [JsonProperty("tech")]
- public EssentialsRoomTechConfig Tech { get; set; }
-
- [JsonProperty("volumes")]
- public EssentialsRoomVolumesConfig Volumes { get; set; }
-
- [JsonProperty("zeroVolumeWhenSwtichingVolumeDevices")]
- public bool ZeroVolumeWhenSwtichingVolumeDevices { get; set; }
- }
-
- public class EssentialsEnvironmentPropertiesConfig
- {
- public bool Enabled { get; set; }
-
- [JsonProperty("deviceKeys")]
- public List DeviceKeys { get; set; }
-
- public EssentialsEnvironmentPropertiesConfig()
- {
- DeviceKeys = new List();
- }
-
- }
-
- public class EssentialsRoomMicrophonePrivacyConfig
- {
- [JsonProperty("deviceKey")]
- public string DeviceKey { get; set; }
-
- [JsonProperty("behaviour")]
- public string Behaviour { get; set; }
- }
-
- ///
- /// Properties for the help text box
- ///
- public class EssentialsHelpPropertiesConfig
- {
- [JsonProperty("message")]
- public string Message { get; set; }
-
- [JsonProperty("showCallButton")]
- public bool ShowCallButton { get; set; }
-
- ///
- /// Defaults to "Call Help Desk"
- ///
- [JsonProperty("callButtonText")]
- public string CallButtonText { get; set; }
-
- public EssentialsHelpPropertiesConfig()
- {
- CallButtonText = "Call Help Desk";
- }
- }
-
- ///
- ///
- ///
- public class EssentialsOneButtonMeetingPropertiesConfig
- {
- [JsonProperty("enable")]
- public bool Enable { get; set; }
- }
-
- public class EssentialsRoomAddressPropertiesConfig
- {
- [JsonProperty("phoneNumber")]
- public string PhoneNumber { get; set; }
-
- [JsonProperty("sipAddress")]
- public string SipAddress { get; set; }
- }
-
-
- ///
- /// Properties for the room's logo on panels
- ///
- public class EssentialsLogoPropertiesConfig
- {
- [JsonProperty("type")]
- public string Type { get; set; }
-
- [JsonProperty("url")]
- public string Url { get; set; }
- ///
- /// Gets either the custom URL, a local-to-processor URL, or null if it's a default logo
- ///
- public string GetUrl()
- {
- if (Type == "url")
- return Url;
- if (Type == "system")
- return string.Format("http://{0}:8080/logo.png",
- CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, 0));
- return null;
- }
- }
-
- ///
- /// Represents occupancy sensor(s) setup for a room
- ///
- public class EssentialsRoomOccSensorConfig
- {
- [JsonProperty("deviceKey")]
- public string DeviceKey { get; set; }
-
- [JsonProperty("timoutMinutes")]
- public int TimoutMinutes { get; set; }
- }
-
- public class EssentialsRoomTechConfig
- {
- [JsonProperty("password")]
- public string Password { get; set; }
- }
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+using Crestron.SimplSharp;
+using Newtonsoft.Json;
+
+using PepperDash.Core;
+using PepperDash.Essentials;
+using PepperDash.Essentials.Core;
+using PepperDash.Essentials.Core.Config;
+
+namespace PepperDash.Essentials.Room.Config
+{
+ public class EssentialsRoomConfigHelper
+ {
+ ///
+ /// Returns a room object from this config data
+ ///
+ ///
+ public static Device GetRoomObject(DeviceConfig roomConfig)
+ {
+ var typeName = roomConfig.Type.ToLower();
+ if (typeName == "huddle")
+ {
+ var props = JsonConvert.DeserializeObject
+ (roomConfig.Properties.ToString());
+ var disp = DeviceManager.GetDeviceForKey(props.DefaultDisplayKey) as IRoutingSinkWithSwitching;
+ var audio = DeviceManager.GetDeviceForKey(props.DefaultAudioKey) as IRoutingSinkNoSwitching;
+ var huddle = new EssentialsHuddleSpaceRoom(roomConfig.Key, roomConfig.Name, disp, audio, props);
+
+ if (props.Occupancy != null)
+ huddle.SetRoomOccupancy(DeviceManager.GetDeviceForKey(props.Occupancy.DeviceKey) as
+ PepperDash.Essentials.Devices.Common.Occupancy.IOccupancyStatusProvider, props.Occupancy.TimoutMinutes);
+ huddle.LogoUrl = props.Logo.GetUrl();
+ huddle.SourceListKey = props.SourceListKey;
+ huddle.DefaultSourceItem = props.DefaultSourceItem;
+ huddle.DefaultVolume = (ushort)(props.Volumes.Master.Level * 65535 / 100);
+ return huddle;
+ }
+ //else if (typeName == "presentation")
+ //{
+ // var props = JsonConvert.DeserializeObject
+ // (this.Properties.ToString());
+ // var displaysDict = new Dictionary();
+ // uint i = 1;
+ // foreach (var dispKey in props.DisplayKeys) // read in the ordered displays list
+ // {
+ // var disp = DeviceManager.GetDeviceForKey(dispKey) as IRoutingSinkWithSwitching;
+ // displaysDict.Add(i++, disp);
+ // }
+
+ // // Get the master volume control
+ // IBasicVolumeWithFeedback masterVolumeControlDev = props.Volumes.Master.GetDevice();
+
+
+ // var presRoom = new EssentialsPresentationRoom(Key, Name, displaysDict, masterVolumeControlDev, props);
+ // return presRoom;
+ //}
+ else if (typeName == "huddlevtc1")
+ {
+ var props = JsonConvert.DeserializeObject
+ (roomConfig.Properties.ToString());
+ var disp = DeviceManager.GetDeviceForKey(props.DefaultDisplayKey) as IRoutingSinkWithSwitching;
+
+ var codec = DeviceManager.GetDeviceForKey(props.VideoCodecKey) as
+ PepperDash.Essentials.Devices.Common.VideoCodec.VideoCodecBase;
+
+ var rm = new EssentialsHuddleVtc1Room(roomConfig.Key, roomConfig.Name, disp, codec, codec, props);
+ // Add Occupancy object from config
+
+ if (props.Occupancy != null)
+ rm.SetRoomOccupancy(DeviceManager.GetDeviceForKey(props.Occupancy.DeviceKey) as
+ PepperDash.Essentials.Devices.Common.Occupancy.IOccupancyStatusProvider, props.Occupancy.TimoutMinutes);
+ rm.LogoUrl = props.Logo.GetUrl();
+ rm.SourceListKey = props.SourceListKey;
+ rm.DefaultSourceItem = props.DefaultSourceItem;
+ rm.DefaultVolume = (ushort)(props.Volumes.Master.Level * 65535 / 100);
+
+ rm.MicrophonePrivacy = GetMicrophonePrivacy(props, rm); // Get Microphone Privacy object, if any
+
+ rm.Emergency = GetEmergency(props, rm); // Get emergency object, if any
+
+ return rm;
+ }
+ else if (typeName == "ddvc01Bridge")
+ {
+ return new Device(roomConfig.Key, roomConfig.Name); // placeholder device that does nothing.
+ }
+
+ return null;
+ }
+
+ ///
+ /// Gets and operating, standalone emergegncy object that can be plugged into a room.
+ /// Returns null if there is no emergency defined
+ ///
+ static EssentialsRoomEmergencyBase GetEmergency(EssentialsRoomPropertiesConfig props, EssentialsRoomBase room)
+ {
+ // This emergency
+ var emergency = props.Emergency;
+ if (emergency != null)
+ {
+ //switch on emergency type here. Right now only contact and shutdown
+ var e = new EssentialsRoomEmergencyContactClosure(room.Key + "-emergency", props.Emergency, room);
+ DeviceManager.AddDevice(e);
+ }
+ return null;
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ static PepperDash.Essentials.Devices.Common.Microphones.MicrophonePrivacyController GetMicrophonePrivacy(
+ EssentialsRoomPropertiesConfig props, EssentialsHuddleVtc1Room room)
+ {
+ var microphonePrivacy = props.MicrophonePrivacy;
+ if (microphonePrivacy == null)
+ {
+ Debug.Console(0, "ERROR: Cannot create microphone privacy with null properties");
+ return null;
+ }
+ // Get the MicrophonePrivacy device from the device manager
+ var mP = (DeviceManager.GetDeviceForKey(props.MicrophonePrivacy.DeviceKey) as
+ PepperDash.Essentials.Devices.Common.Microphones.MicrophonePrivacyController);
+ // Set this room as the IPrivacy device
+ if (mP == null)
+ {
+ Debug.Console(0, "ERROR: Selected device {0} is not MicrophonePrivacyController", props.MicrophonePrivacy.DeviceKey);
+ return null;
+ }
+ mP.SetPrivacyDevice(room);
+
+ var behaviour = props.MicrophonePrivacy.Behaviour.ToLower();
+
+ if (behaviour == null)
+ {
+ Debug.Console(0, "WARNING: No behaviour defined for MicrophonePrivacyController");
+ return null;
+ }
+ if (behaviour == "trackroomstate")
+ {
+ // Tie LED enable to room power state
+ room.OnFeedback.OutputChange += (o, a) =>
+ {
+ if (room.OnFeedback.BoolValue)
+ mP.EnableLeds = true;
+ else
+ mP.EnableLeds = false;
+ };
+
+ mP.EnableLeds = room.OnFeedback.BoolValue;
+ }
+ else if (behaviour == "trackcallstate")
+ {
+ // Tie LED enable to room power state
+ room.InCallFeedback.OutputChange += (o, a) =>
+ {
+ if (room.InCallFeedback.BoolValue)
+ mP.EnableLeds = true;
+ else
+ mP.EnableLeds = false;
+ };
+
+ mP.EnableLeds = room.InCallFeedback.BoolValue;
+ }
+
+ return mP;
+ }
+
+ }
+
+ ///
+ ///
+ ///
+ public class EssentialsRoomPropertiesConfig
+ {
+ [JsonProperty("addresses")]
+ public EssentialsRoomAddressPropertiesConfig Addresses { get; set; }
+
+ [JsonProperty("description")]
+ public string Description { get; set; }
+
+ [JsonProperty("emergency")]
+ public EssentialsRoomEmergencyConfig Emergency { get; set; }
+
+ [JsonProperty("help")]
+ public EssentialsHelpPropertiesConfig Help { get; set; }
+
+ [JsonProperty("helpMessage")]
+ public string HelpMessage { get; set; }
+
+ [JsonProperty("environment")]
+ public EssentialsEnvironmentPropertiesConfig Environment { get; set; }
+
+ [JsonProperty("logo")]
+ public EssentialsLogoPropertiesConfig Logo { get; set; }
+
+ [JsonProperty("microphonePrivacy")]
+ public EssentialsRoomMicrophonePrivacyConfig MicrophonePrivacy { get; set; }
+
+ [JsonProperty("occupancy")]
+ public EssentialsRoomOccSensorConfig Occupancy { get; set; }
+
+ [JsonProperty("oneButtonMeeting")]
+ public EssentialsOneButtonMeetingPropertiesConfig OneButtonMeeting { get; set; }
+
+ [JsonProperty("shutdownVacancySeconds")]
+ public int ShutdownVacancySeconds { get; set; }
+
+ [JsonProperty("shutdownPromptSeconds")]
+ public int ShutdownPromptSeconds { get; set; }
+
+ [JsonProperty("tech")]
+ public EssentialsRoomTechConfig Tech { get; set; }
+
+ [JsonProperty("volumes")]
+ public EssentialsRoomVolumesConfig Volumes { get; set; }
+
+ [JsonProperty("zeroVolumeWhenSwtichingVolumeDevices")]
+ public bool ZeroVolumeWhenSwtichingVolumeDevices { get; set; }
+ }
+
+ public class EssentialsEnvironmentPropertiesConfig
+ {
+ public bool Enabled { get; set; }
+
+ [JsonProperty("deviceKeys")]
+ public List DeviceKeys { get; set; }
+
+ public EssentialsEnvironmentPropertiesConfig()
+ {
+ DeviceKeys = new List();
+ }
+
+ }
+
+ public class EssentialsRoomMicrophonePrivacyConfig
+ {
+ [JsonProperty("deviceKey")]
+ public string DeviceKey { get; set; }
+
+ [JsonProperty("behaviour")]
+ public string Behaviour { get; set; }
+ }
+
+ ///
+ /// Properties for the help text box
+ ///
+ public class EssentialsHelpPropertiesConfig
+ {
+ [JsonProperty("message")]
+ public string Message { get; set; }
+
+ [JsonProperty("showCallButton")]
+ public bool ShowCallButton { get; set; }
+
+ ///
+ /// Defaults to "Call Help Desk"
+ ///
+ [JsonProperty("callButtonText")]
+ public string CallButtonText { get; set; }
+
+ public EssentialsHelpPropertiesConfig()
+ {
+ CallButtonText = "Call Help Desk";
+ }
+ }
+
+ ///
+ ///
+ ///
+ public class EssentialsOneButtonMeetingPropertiesConfig
+ {
+ [JsonProperty("enable")]
+ public bool Enable { get; set; }
+ }
+
+ public class EssentialsRoomAddressPropertiesConfig
+ {
+ [JsonProperty("phoneNumber")]
+ public string PhoneNumber { get; set; }
+
+ [JsonProperty("sipAddress")]
+ public string SipAddress { get; set; }
+ }
+
+
+ ///
+ /// Properties for the room's logo on panels
+ ///
+ public class EssentialsLogoPropertiesConfig
+ {
+ [JsonProperty("type")]
+ public string Type { get; set; }
+
+ [JsonProperty("url")]
+ public string Url { get; set; }
+ ///
+ /// Gets either the custom URL, a local-to-processor URL, or null if it's a default logo
+ ///
+ public string GetUrl()
+ {
+ if (Type == "url")
+ return Url;
+ if (Type == "system")
+ return string.Format("http://{0}:8080/logo.png",
+ CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, 0));
+ return null;
+ }
+ }
+
+ ///
+ /// Represents occupancy sensor(s) setup for a room
+ ///
+ public class EssentialsRoomOccSensorConfig
+ {
+ [JsonProperty("deviceKey")]
+ public string DeviceKey { get; set; }
+
+ [JsonProperty("timoutMinutes")]
+ public int TimoutMinutes { get; set; }
+ }
+
+ public class EssentialsRoomTechConfig
+ {
+ [JsonProperty("password")]
+ public string Password { get; set; }
+ }
}
\ No newline at end of file
diff --git a/PepperDashEssentials/Room/Cotija/CotijaSystemController.cs.orig b/PepperDashEssentials/Room/Cotija/CotijaSystemController.cs.orig
deleted file mode 100644
index 0dc8b651..00000000
--- a/PepperDashEssentials/Room/Cotija/CotijaSystemController.cs.orig
+++ /dev/null
@@ -1,694 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Text.RegularExpressions;
-using Crestron.SimplSharp;
-using Crestron.SimplSharp.CrestronIO;
-using Crestron.SimplSharp.Reflection;
-using Crestron.SimplSharpPro.CrestronThread;
-using Crestron.SimplSharp.CrestronWebSocketClient;
-using Crestron.SimplSharpPro;
-using Crestron.SimplSharp.Net.Http;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
-
-using PepperDash.Core;
-using PepperDash.Essentials.Core;
-using PepperDash.Essentials.Room.Cotija;
-
-namespace PepperDash.Essentials
-{
- public class CotijaSystemController : Device
- {
- WebSocketClient WSClient;
-
- ///
- /// Prevents post operations from stomping on each other and getting lost
- ///
- CEvent PostLockEvent = new CEvent(true, true);
-
- CEvent RegisterLockEvent = new CEvent(true, true);
-
- public CotijaConfig Config { get; private set; }
-
- Dictionary ActionDictionary = new Dictionary(StringComparer.InvariantCultureIgnoreCase);
-
- Dictionary PushedActions = new Dictionary();
-
- CTimer ServerHeartbeatCheckTimer;
-
- long ServerHeartbeatInterval = 20000;
-
- CTimer ServerReconnectTimer;
-
- long ServerReconnectInterval = 5000;
-
- string SystemUuid;
-
- List RoomBridges = new List();
-
- long ButtonHeartbeatInterval = 1000;
-
- ///
- /// Used for tracking HTTP debugging
- ///
- bool HttpDebugEnabled;
-
- ///
- ///
- ///
- ///
- ///
- ///
- public CotijaSystemController(string key, string name, CotijaConfig config) : base(key, name)
- {
- Config = config;
- Debug.Console(0, this, "Mobile UI controller initializing for server:{0}", config.ServerUrl);
-
- CrestronConsole.AddNewConsoleCommand(AuthorizeSystem,
- "mobileauth", "Authorizes system to talk to cotija server", ConsoleAccessLevelEnum.AccessOperator);
- CrestronConsole.AddNewConsoleCommand(s => ShowInfo(),
- "mobileinfo", "Shows information for current mobile control session", ConsoleAccessLevelEnum.AccessOperator);
- CrestronConsole.AddNewConsoleCommand(s => {
- s = s.Trim();
- if(!string.IsNullOrEmpty(s))
- {
- HttpDebugEnabled = (s.Trim() != "0");
- }
- CrestronConsole.ConsoleCommandResponse("HTTP Debug {0}", HttpDebugEnabled ? "Enabled" : "Disabled");
- },
- "mobilehttpdebug", "1 enables more verbose HTTP response debugging", ConsoleAccessLevelEnum.AccessOperator);
- CrestronConsole.AddNewConsoleCommand(TestHttpRequest,
- "mobilehttprequest", "Tests an HTTP get to URL given", ConsoleAccessLevelEnum.AccessOperator);
-
- }
-
- ///
- /// Adds an action to the dictionary
- ///
- /// The path of the API command
- /// The action to be triggered by the commmand
- public void AddAction(string key, object action)
- {
- if (!ActionDictionary.ContainsKey(key))
- {
- ActionDictionary.Add(key, action);
- }
- else
- {
- Debug.Console(1, this, "Cannot add action with key '{0}' because key already exists in ActionDictionary.", key);
- }
- }
-
- ///
- /// Removes an action from the dictionary
- ///
- ///
- public void RemoveAction(string key)
- {
- if (ActionDictionary.ContainsKey(key))
- ActionDictionary.Remove(key);
- }
-
- ///
- ///
- ///
- ///
- public void AddBridge(CotijaBridgeBase bridge)
- {
- RoomBridges.Add(bridge);
- var b = bridge as IDelayedConfiguration;
- if (b != null)
- {
- Debug.Console(0, this, "Adding room bridge with delayed configuration");
- b.ConfigurationIsReady += new EventHandler(bridge_ConfigurationIsReady);
- }
- else
- {
- Debug.Console(0, this, "Adding room bridge and sending configuration");
- RegisterSystemToServer();
- }
- }
-
- ///
- ///
- ///
- ///
- ///
- void bridge_ConfigurationIsReady(object sender, EventArgs e)
- {
- Debug.Console(1, this, "Bridge ready. Registering");
- // send the configuration object to the server
- RegisterSystemToServer();
- }
-
- ///
- ///
- ///
- ///
- void ReconnectToServerTimerCallback(object o)
- {
- RegisterSystemToServer();
- }
-
- ///
- /// Verifies system connection with servers
- ///
- ///
- void AuthorizeSystem(string code)
- {
- if (string.IsNullOrEmpty(code))
- {
- CrestronConsole.ConsoleCommandResponse("Please enter a user code to authorize a system");
- return;
- }
-
- var req = new HttpClientRequest();
- string url = string.Format("http://{0}/api/system/grantcode/{1}/{2}", Config.ServerUrl, code, SystemUuid);
- Debug.Console(0, this, "Authorizing to: {0}", url);
-
- if (string.IsNullOrEmpty(Config.ServerUrl))
- {
- CrestronConsole.ConsoleCommandResponse("Config URL address is not set. Check portal configuration");
- return;
- }
- try
- {
- req.Url.Parse(url);
- new HttpClient().DispatchAsync(req, (r, e) =>
- {
- CheckHttpDebug(r, e);
- if (e == HTTP_CALLBACK_ERROR.COMPLETED)
- {
- if (r.Code == 200)
- {
- Debug.Console(0, "System authorized, sending config.");
- RegisterSystemToServer();
- }
- else if (r.Code == 404)
- {
- if (r.ContentString.Contains("codeNotFound"))
- {
- Debug.Console(0, "Authorization failed, code not found for system UUID {0}", SystemUuid);
- }
- else if (r.ContentString.Contains("uuidNotFound"))
- {
- Debug.Console(0, "Authorization failed, uuid {0} not found. Check Essentials configuration is correct",
- SystemUuid);
- }
- }
- }
- else
- Debug.Console(0, this, "Error {0} in authorizing system", e);
- });
- }
- catch (Exception e)
- {
- Debug.Console(0, this, "Error in authorizing: {0}", e);
- }
- }
-
- ///
- /// Dumps info in response to console command.
- ///
- void ShowInfo()
- {
- var url = Config != null ? Config.ServerUrl : "No config";
- string name;
- string code;
- if (RoomBridges != null && RoomBridges.Count > 0)
- {
- name = RoomBridges[0].RoomName;
- code = RoomBridges[0].UserCode;
- }
- else
- {
- name = "No config";
- code = "Not available";
- }
- var conn = WSClient == null ? "No client" : (WSClient.Connected ? "Yes" : "No");
-
- CrestronConsole.ConsoleCommandResponse(@"Mobile Control Information:
- Server address: {0}
- System Name: {1}
- System UUID: {2}
- System User code: {3}
- Connected?: {4}", url, name, SystemUuid,
- code, conn);
- }
-
- ///
- /// Registers the room with the server
- ///
- /// URL of the server, including the port number, if not 80. Format: "serverUrlOrIp:port"
- void RegisterSystemToServer()
- {
- var ready = RegisterLockEvent.Wait(20000);
- if (!ready)
- {
- Debug.Console(1, this, "RegisterSystemToServer failed to enter after 20 seconds. Ignoring");
- return;
- }
- RegisterLockEvent.Reset();
-
- try
- {
- var confObject = ConfigReader.ConfigObject;
- confObject.Info.RuntimeInfo.AppName = Assembly.GetExecutingAssembly().GetName().Name;
- var version = Assembly.GetExecutingAssembly().GetName().Version;
- confObject.Info.RuntimeInfo.AssemblyVersion = string.Format("{0}.{1}.{2}", version.Major, version.Minor, version.Build);
- confObject.Info.RuntimeInfo.OsVersion = Crestron.SimplSharp.CrestronEnvironment.OSVersion.Firmware;
-
- string postBody = JsonConvert.SerializeObject(confObject);
- SystemUuid = confObject.SystemUuid;
-
- if (string.IsNullOrEmpty(postBody))
- {
- Debug.Console(1, this, "ERROR: Config body is empty. Cannot register with server.");
- }
- else
- {
- var regClient = new HttpClient();
- regClient.Verbose = true;
- regClient.KeepAlive = true;
-
- string url = string.Format("http://{0}/api/system/join/{1}", Config.ServerUrl, SystemUuid);
- Debug.Console(1, this, "Joining server at {0}", url);
-
- HttpClientRequest request = new HttpClientRequest();
- request.Url.Parse(url);
- request.RequestType = RequestType.Post;
- request.Header.SetHeaderValue("Content-Type", "application/json");
- request.ContentString = postBody;
-
- var err = regClient.DispatchAsync(request, RegistrationConnectionCallback);
- }
-
- }
- catch (Exception e)
- {
- Debug.Console(0, this, "ERROR: Initilizing Room: {0}", e);
- RegisterLockEvent.Set();
- StartReconnectTimer();
- }
-
- }
-
- ///
- /// Sends a message to the server from a room
- ///
- /// room from which the message originates
- /// object to be serialized and sent in post body
- public void SendMessageToServer(JObject o)
- {
-
- if (WSClient != null && WSClient.Connected)
- {
- string message = JsonConvert.SerializeObject(o, Formatting.None, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
- Debug.Console(1, this, "Message TX: {0}", message);
- var messageBytes = System.Text.Encoding.UTF8.GetBytes(message);
- WSClient.Send(messageBytes, (uint)messageBytes.Length, WebSocketClient.WEBSOCKET_PACKET_TYPES.LWS_WS_OPCODE_07__TEXT_FRAME);
- //WSClient.SendAsync(messageBytes, (uint)messageBytes.Length, WebSocketClient.WEBSOCKET_PACKET_TYPES.LWS_WS_OPCODE_07__TEXT_FRAME);
- }
-<<<<<<< HEAD
-
-=======
-
->>>>>>> feature/ecs-684
- }
-
- ///
- /// Disconnects the SSE Client and stops the heartbeat timer
- ///
- ///
- void DisconnectStreamClient(string command)
- {
- //if(SseClient != null)
- // SseClient.Disconnect();
-
- if (WSClient != null && WSClient.Connected)
- WSClient.Disconnect();
-
- if (ServerHeartbeatCheckTimer != null)
- {
- ServerHeartbeatCheckTimer.Stop();
-
- ServerHeartbeatCheckTimer = null;
- }
- }
-
- ///
- /// The callback that fires when we get a response from our registration attempt
- ///
- ///
- ///
- void RegistrationConnectionCallback(HttpClientResponse resp, HTTP_CALLBACK_ERROR err)
- {
- CheckHttpDebug(resp, err);
- Debug.Console(1, this, "RegistrationConnectionCallback: {0}", err);
- try
- {
- if (resp != null && resp.Code == 200)
- {
- if(ServerReconnectTimer != null)
- {
- ServerReconnectTimer.Stop();
- ServerReconnectTimer = null;
- }
-
- // Success here!
- ConnectStreamClient();
- }
- else
- {
- if (resp != null)
- Debug.Console(1, this, "Response from server: {0}\n{1}", resp.Code, err);
- else
- {
- Debug.Console(1, this, "Null response received from server.");
- }
- StartReconnectTimer();
- }
- }
- catch (Exception e)
- {
- Debug.Console(1, this, "Error Initializing Stream Client: {0}", e);
- StartReconnectTimer();
- }
- RegisterLockEvent.Set();
- }
-
- ///
- /// Executes when we don't get a heartbeat message in time. Triggers reconnect.
- ///
- /// For CTimer callback. Not used
- void HeartbeatExpiredTimerCallback(object o)
- {
- Debug.Console(1, this, "Heartbeat Timer Expired.");
- if (ServerHeartbeatCheckTimer != null)
- {
- ServerHeartbeatCheckTimer.Stop();
- ServerHeartbeatCheckTimer = null;
- }
- StartReconnectTimer();
- }
-
- ///
- ///
- ///
- ///
- ///
- void StartReconnectTimer()
- {
- // Start the reconnect timer
- if (ServerReconnectTimer == null)
- {
- ServerReconnectTimer = new CTimer(ReconnectToServerTimerCallback, null, ServerReconnectInterval, ServerReconnectInterval);
- Debug.Console(1, this, "Reconnect Timer Started.");
- }
- ServerReconnectTimer.Reset(ServerReconnectInterval, ServerReconnectInterval);
- }
-
- ///
- ///
- ///
- ///
- ///
- void ResetOrStartHearbeatTimer()
- {
- if (ServerHeartbeatCheckTimer == null)
- {
- ServerHeartbeatCheckTimer = new CTimer(HeartbeatExpiredTimerCallback, null, ServerHeartbeatInterval, ServerHeartbeatInterval);
-
- Debug.Console(1, this, "Heartbeat Timer Started.");
- }
-
- ServerHeartbeatCheckTimer.Reset(ServerHeartbeatInterval, ServerHeartbeatInterval);
- }
-
-
- ///
- /// Connects the SSE Client
- ///
- ///
- void ConnectStreamClient()
- {
- Debug.Console(0, this, "Initializing Stream client to server.");
-
- if (WSClient == null)
- {
- WSClient = new WebSocketClient();
- }
- WSClient.URL = string.Format("wss://{0}/system/join/{1}", Config.ServerUrl, this.SystemUuid);
- WSClient.Connect();
- Debug.Console(0, this, "Websocket connected");
- WSClient.ReceiveCallBack = WebsocketReceiveCallback;
- //WSClient.SendCallBack = WebsocketSendCallback;
- WSClient.ReceiveAsync();
- }
-
- ///
- /// Resets reconnect timer and updates usercode
- ///
- ///
- void HandleHeartBeat(JToken content)
- {
- var code = content["userCode"];
- if(code != null)
- {
- foreach (var b in RoomBridges)
- {
- b.SetUserCode(code.Value());
- }
- }
- ResetOrStartHearbeatTimer();
- }
-
- ///
- /// Outputs debug info when enabled
- ///
- ///
- ///
- ///
- void CheckHttpDebug(HttpClientResponse r, HTTP_CALLBACK_ERROR e)
- {
- if (HttpDebugEnabled)
- {
- Debug.Console(0, this, "------ Begin HTTP Debug ---------------------------------------");
- Debug.Console(0, this, "HTTP Response URL: {0}", r.ResponseUrl.ToString());
- Debug.Console(0, this, "HTTP Response 'error' {0}", e);
- Debug.Console(0, this, "HTTP Response code: {0}", r.Code);
- Debug.Console(0, this, "HTTP Response content: \r{0}", r.ContentString);
- Debug.Console(0, this, "------ End HTTP Debug -----------------------------------------");
- }
- }
-
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- int WebsocketReceiveCallback(byte[] data, uint length, WebSocketClient.WEBSOCKET_PACKET_TYPES opcode,
- WebSocketClient.WEBSOCKET_RESULT_CODES err)
- {
- var rx = System.Text.Encoding.UTF8.GetString(data, 0, (int)length);
- if(rx.Length > 0)
- ParseStreamRx(rx);
- WSClient.ReceiveAsync();
- return 1;
- }
-
- ///
- /// Callback to catch possible errors in sending via the websocket
- ///
- ///
- ///
- int WebsocketSendCallback(Crestron.SimplSharp.CrestronWebSocketClient.WebSocketClient.WEBSOCKET_RESULT_CODES result)
- {
- Debug.Console(1, this, "SendCallback result: {0}", result);
-
- return 1;
- }
-
- ///
- ///
- ///
- ///
- ///
- void ParseStreamRx(string message)
- {
- if(string.IsNullOrEmpty(message))
- return;
-
- Debug.Console(1, this, "Message RX: '{0}'", message);
- try
- {
- var messageObj = JObject.Parse(message);
-
- var type = messageObj["type"].Value();
-
- if (type == "hello")
- {
- ResetOrStartHearbeatTimer();
- }
- else if (type == "/system/heartbeat")
- {
- HandleHeartBeat(messageObj["content"]);
- }
- else if (type == "close")
- {
- WSClient.Disconnect();
-
- ServerHeartbeatCheckTimer.Stop();
- // Start the reconnect timer
- StartReconnectTimer();
- }
- else
- {
- // Check path against Action dictionary
- if (ActionDictionary.ContainsKey(type))
- {
- var action = ActionDictionary[type];
-
- if (action is Action)
- {
- (action as Action)();
- }
- else if (action is PressAndHoldAction)
- {
- var stateString = messageObj["content"]["state"].Value();
-
- // Look for a button press event
- if (!string.IsNullOrEmpty(stateString))
- {
- switch (stateString)
- {
- case "true":
- {
- if (!PushedActions.ContainsKey(type))
- {
- PushedActions.Add(type, new CTimer(o =>
- {
- (action as PressAndHoldAction)(false);
- PushedActions.Remove(type);
- }, null, ButtonHeartbeatInterval, ButtonHeartbeatInterval));
- }
- // Maybe add an else to reset the timer
- break;
- }
- case "held":
- {
- if (!PushedActions.ContainsKey(type))
- {
- PushedActions[type].Reset(ButtonHeartbeatInterval, ButtonHeartbeatInterval);
- }
- return;
- }
- case "false":
- {
- if (PushedActions.ContainsKey(type))
- {
- PushedActions[type].Stop();
- PushedActions.Remove(type);
- }
- break;
- }
- }
-
- (action as PressAndHoldAction)(stateString == "true");
- }
- }
- else if (action is Action)
- {
- var stateString = messageObj["content"]["state"].Value();
-
- if (!string.IsNullOrEmpty(stateString))
- {
- (action as Action)(stateString == "true");
- }
- }
- else if (action is Action)
- {
- (action as Action)(messageObj["content"]["value"].Value());
- }
- else if (action is Action)
- {
- (action as Action)(messageObj["content"]["value"].Value());
- }
- else if (action is Action)
- {
- (action as Action)(messageObj["content"]
- .ToObject());
- }
- }
- else
- {
- Debug.Console(1, this, "-- Warning: Incoming message has no registered handler");
- }
- }
- }
- catch (Exception err)
- {
- //Debug.Console(1, "SseMessageLengthBeforeFailureCount: {0}", SseMessageLengthBeforeFailureCount);
- //SseMessageLengthBeforeFailureCount = 0;
- Debug.Console(1, this, "Unable to parse message: {0}", err);
- }
- }
-
- void TestHttpRequest(string s)
- {
- {
- s = s.Trim();
- if (string.IsNullOrEmpty(s))
- {
- PrintTestHttpRequestUsage();
- return;
- }
- var tokens = s.Split(' ');
- if (tokens.Length < 2)
- {
- CrestronConsole.ConsoleCommandResponse("Too few paramaters\r");
- PrintTestHttpRequestUsage();
- return;
- }
-
- try
- {
- var url = tokens[1];
- if (tokens[0].ToLower() == "get")
- {
- var resp = new HttpClient().Get(url);
- CrestronConsole.ConsoleCommandResponse("RESPONSE:\r{0}\r\r", resp);
- }
- else if (tokens[0].ToLower() == "post")
- {
- var resp = new HttpClient().Post(url, new byte[] { });
- CrestronConsole.ConsoleCommandResponse("RESPONSE:\r{0}\r\r", resp);
- }
-
- else
- {
- CrestronConsole.ConsoleCommandResponse("Only get or post supported\r");
- PrintTestHttpRequestUsage();
- }
- }
- catch (HttpException e)
- {
- CrestronConsole.ConsoleCommandResponse("Exception in request:\r");
- CrestronConsole.ConsoleCommandResponse("Response URL: {0}\r", e.Response.ResponseUrl);
- CrestronConsole.ConsoleCommandResponse("Response Error Code: {0}\r", e.Response.Code);
- CrestronConsole.ConsoleCommandResponse("Response body: {0}\r", e.Response.ContentString);
- }
-
- }
- }
-
- void PrintTestHttpRequestUsage()
- {
- CrestronConsole.ConsoleCommandResponse("Usage: mobilehttprequest:N get/post url\r");
- }
- }
-}
\ No newline at end of file
diff --git a/PepperDashEssentials/Room/Cotija/Interfaces.cs b/PepperDashEssentials/Room/Cotija/Interfaces.cs
deleted file mode 100644
index 1aab82fb..00000000
--- a/PepperDashEssentials/Room/Cotija/Interfaces.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using Crestron.SimplSharp;
-
-namespace PepperDash.Essentials.Room.Cotija
-{
- public interface IDelayedConfiguration
- {
- event EventHandler ConfigurationIsReady;
- }
-}
\ No newline at end of file
diff --git a/PepperDashEssentials/Room/Types/EssentialsHuddleSpaceRoom.cs b/PepperDashEssentials/Room/Types/EssentialsHuddleSpaceRoom.cs
index 5493fd9d..a1a821d4 100644
--- a/PepperDashEssentials/Room/Types/EssentialsHuddleSpaceRoom.cs
+++ b/PepperDashEssentials/Room/Types/EssentialsHuddleSpaceRoom.cs
@@ -1,462 +1,469 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using Crestron.SimplSharp;
-
-using PepperDash.Core;
-using PepperDash.Essentials.Core;
-using PepperDash.Essentials.Room.Config;
-
-namespace PepperDash.Essentials
-{
- public class EssentialsHuddleSpaceRoom : EssentialsRoomBase, IHasCurrentSourceInfoChange
- {
- public event EventHandler CurrentVolumeDeviceChange;
- public event SourceInfoChangeHandler CurrentSingleSourceChange;
-
- protected override Func OnFeedbackFunc
- {
- get
- {
- return () =>
- {
- var disp = DefaultDisplay as DisplayBase;
- var val = CurrentSourceInfo != null
- && CurrentSourceInfo.Type == eSourceListItemType.Route
- && disp != null;
- //&& disp.PowerIsOnFeedback.BoolValue;
- return val;
- };
- }
- }
- ///
- ///
- ///
- protected override Func IsWarmingFeedbackFunc
- {
- get
- {
- return () =>
- {
- var disp = DefaultDisplay as DisplayBase;
- if (disp != null)
- return disp.IsWarmingUpFeedback.BoolValue;
- else
- return false;
- };
- }
- }
- ///
- ///
- ///
- protected override Func IsCoolingFeedbackFunc
- {
- get
- {
- return () =>
- {
- var disp = DefaultDisplay as DisplayBase;
- if (disp != null)
- return disp.IsCoolingDownFeedback.BoolValue;
- else
- return false;
- };
- }
- }
-
- public EssentialsRoomPropertiesConfig Config { get; private set; }
-
- public IRoutingSinkWithSwitching DefaultDisplay { get; private set; }
- public IRoutingSinkNoSwitching DefaultAudioDevice { get; private set; }
- public IBasicVolumeControls DefaultVolumeControls { get; private set; }
-
- public bool ExcludeFromGlobalFunctions { get; set; }
-
- ///
- /// The config name of the source list
- ///
- public string SourceListKey { get; set; }
-
- public string DefaultSourceItem { get; set; }
-
- public ushort DefaultVolume { get; set; }
-
- ///
- /// If room is off, enables power on to last source. Default true
- ///
- public bool EnablePowerOnToLastSource { get; set; }
- string LastSourceKey;
-
- ///
- ///
- ///
- public IBasicVolumeControls CurrentVolumeControls
- {
- get { return _CurrentAudioDevice; }
- set
- {
- if (value == _CurrentAudioDevice) return;
-
- var oldDev = _CurrentAudioDevice;
- // derigister this room from the device, if it can
- if (oldDev is IInUseTracking)
- (oldDev as IInUseTracking).InUseTracker.RemoveUser(this, "audio");
- var handler = CurrentVolumeDeviceChange;
- if (handler != null)
- CurrentVolumeDeviceChange(this, new VolumeDeviceChangeEventArgs(oldDev, value, ChangeType.WillChange));
- _CurrentAudioDevice = value;
- if (handler != null)
- CurrentVolumeDeviceChange(this, new VolumeDeviceChangeEventArgs(oldDev, value, ChangeType.DidChange));
- // register this room with new device, if it can
- if (_CurrentAudioDevice is IInUseTracking)
- (_CurrentAudioDevice as IInUseTracking).InUseTracker.AddUser(this, "audio");
- }
- }
- IBasicVolumeControls _CurrentAudioDevice;
-
- ///
- /// The SourceListItem last run - containing names and icons
- ///
- public SourceListItem CurrentSourceInfo
- {
- get { return _CurrentSourceInfo; }
- private set
- {
- if (value == _CurrentSourceInfo) return;
-
- var handler = CurrentSingleSourceChange;
- // remove from in-use tracker, if so equipped
- if(_CurrentSourceInfo != null && _CurrentSourceInfo.SourceDevice is IInUseTracking)
- (_CurrentSourceInfo.SourceDevice as IInUseTracking).InUseTracker.RemoveUser(this, "control");
-
- if (handler != null)
- handler(this, _CurrentSourceInfo, ChangeType.WillChange);
-
- _CurrentSourceInfo = value;
-
- // add to in-use tracking
- if (_CurrentSourceInfo != null && _CurrentSourceInfo.SourceDevice is IInUseTracking)
- (_CurrentSourceInfo.SourceDevice as IInUseTracking).InUseTracker.AddUser(this, "control");
- if (handler != null)
- handler(this, _CurrentSourceInfo, ChangeType.DidChange);
- }
- }
- SourceListItem _CurrentSourceInfo;
-
- public string CurrentSourceInfoKey { get; private set; }
-
- ///
- ///
- ///
- ///
- ///
- public EssentialsHuddleSpaceRoom(string key, string name, IRoutingSinkWithSwitching defaultDisplay,
- IRoutingSinkNoSwitching defaultAudio, EssentialsRoomPropertiesConfig config)
- : base(key, name)
- {
- Config = config;
- DefaultDisplay = defaultDisplay;
- DefaultAudioDevice = defaultAudio;
- if (defaultAudio is IBasicVolumeControls)
- DefaultVolumeControls = defaultAudio as IBasicVolumeControls;
- else if (defaultAudio is IHasVolumeDevice)
- DefaultVolumeControls = (defaultAudio as IHasVolumeDevice).VolumeDevice;
- CurrentVolumeControls = DefaultVolumeControls;
-
- var disp = DefaultDisplay as DisplayBase;
- if (disp != null)
- {
- // Link power, warming, cooling to display
- disp.PowerIsOnFeedback.OutputChange += (o, a) =>
- {
- if (disp.PowerIsOnFeedback.BoolValue != OnFeedback.BoolValue)
- {
- if (!disp.PowerIsOnFeedback.BoolValue)
- CurrentSourceInfo = null;
- OnFeedback.FireUpdate();
- }
- };
-
- disp.IsWarmingUpFeedback.OutputChange += (o, a) =>
- {
- IsWarmingUpFeedback.FireUpdate();
- if (!IsWarmingUpFeedback.BoolValue)
- (DefaultDisplay as IBasicVolumeWithFeedback).SetVolume(DefaultVolume);
- };
- disp.IsCoolingDownFeedback.OutputChange += (o, a) =>
- {
- IsCoolingDownFeedback.FireUpdate();
- };
- }
-
- SourceListKey = "default";
- EnablePowerOnToLastSource = true;
- }
-
-
- ///
- ///
- ///
- protected override void EndShutdown()
- {
- SetDefaultLevels();
-
- RunDefaultRoute();
-
- CrestronEnvironment.Sleep(1000);
-
- RunRouteAction("roomOff");
- }
-
- ///
- /// Routes the default source item, if any
- ///
- public void RunDefaultRoute()
- {
- //if (DefaultSourceItem != null && !OnFeedback.BoolValue)
- RunRouteAction(DefaultSourceItem);
- }
-
- ///
- ///
- ///
- ///
- public void RunRouteAction(string routeKey)
- {
- RunRouteAction(routeKey, null);
- }
-
- ///
- /// Gets a source from config list SourceListKey and dynamically build and executes the
- /// route or commands
- ///
- ///
- public void RunRouteAction(string routeKey, Action successCallback)
- {
- // Run this on a separate thread
- new CTimer(o =>
- {
- Debug.Console(1, this, "Run route action '{0}'", routeKey);
- var dict = ConfigReader.ConfigObject.GetSourceListForKey(SourceListKey);
- if(dict == null)
- {
- Debug.Console(1, this, "WARNING: Config source list '{0}' not found", SourceListKey);
- return;
- }
-
- // Try to get the list item by it's string key
- if (!dict.ContainsKey(routeKey))
- {
- Debug.Console(1, this, "WARNING: No item '{0}' found on config list '{1}'",
- routeKey, SourceListKey);
- return;
- }
-
- var item = dict[routeKey];
- //Debug.Console(2, this, "Action {0} has {1} steps",
- // item.SourceKey, item.RouteList.Count);
-
- // End usage timer on last source
- if (!string.IsNullOrEmpty(LastSourceKey))
- {
- var lastSource = dict[LastSourceKey].SourceDevice;
-
- try
- {
- if (lastSource != null && lastSource is IUsageTracking)
- (lastSource as IUsageTracking).UsageTracker.EndDeviceUsage();
- }
- catch (Exception e)
- {
- Debug.Console(1, this, "*#* EXCEPTION in end usage tracking (257):\r{0}", e);
- }
- }
-
- // Let's run it
- if (routeKey.ToLower() != "roomoff")
- {
- LastSourceKey = routeKey;
- }
- else
- {
- CurrentSourceInfoKey = null;
- }
-
- foreach (var route in item.RouteList)
- {
- // if there is a $defaultAll on route, run two separate
- if (route.DestinationKey.Equals("$defaultAll", StringComparison.OrdinalIgnoreCase))
- {
- // Going to assume a single-path route for now
- var tempVideo = new SourceRouteListItem
- {
- DestinationKey = "$defaultDisplay",
- SourceKey = route.SourceKey,
- Type = eRoutingSignalType.Video
- };
- DoRoute(tempVideo);
-
- //var tempAudio = new SourceRouteListItem
- //{
- // DestinationKey = "$defaultAudio",
- // SourceKey = route.SourceKey,
- // Type = eRoutingSignalType.Audio
- //};
- //DoRoute(tempAudio);
- //continue; -- not sure why this was here
- }
- else
- DoRoute(route);
- }
-
- // Start usage timer on routed source
- if (item.SourceDevice is IUsageTracking)
- {
- (item.SourceDevice as IUsageTracking).UsageTracker.StartDeviceUsage();
- }
-
-
-
-
- // Set volume control, using default if non provided
- IBasicVolumeControls volDev = null;
- // Handle special cases for volume control
- if (string.IsNullOrEmpty(item.VolumeControlKey)
- || item.VolumeControlKey.Equals("$defaultAudio", StringComparison.OrdinalIgnoreCase))
- volDev = DefaultVolumeControls;
- else if (item.VolumeControlKey.Equals("$defaultDisplay", StringComparison.OrdinalIgnoreCase))
- volDev = DefaultDisplay as IBasicVolumeControls;
- // Or a specific device, probably rarely used.
- else
- {
- var dev = DeviceManager.GetDeviceForKey(item.VolumeControlKey);
- if (dev is IBasicVolumeControls)
- volDev = dev as IBasicVolumeControls;
- else if (dev is IHasVolumeDevice)
- volDev = (dev as IHasVolumeDevice).VolumeDevice;
- }
-
- if (volDev != CurrentVolumeControls)
- {
- // zero the volume on the device we are leaving.
- // Set the volume to default on device we are entering
- if (ZeroVolumeWhenSwtichingVolumeDevices && CurrentVolumeControls is IBasicVolumeWithFeedback)
- {
- var vd = CurrentVolumeControls as IBasicVolumeWithFeedback;
- SavedVolumeLevels[vd] = (uint)vd.VolumeLevelFeedback.IntValue;
- vd.SetVolume(0);
- }
- CurrentVolumeControls = volDev;
- if (ZeroVolumeWhenSwtichingVolumeDevices && CurrentVolumeControls is IBasicVolumeWithFeedback)
- {
- var vd = CurrentVolumeControls as IBasicVolumeWithFeedback;
- ushort vol = (SavedVolumeLevels.ContainsKey(vd) ? (ushort)SavedVolumeLevels[vd] : DefaultVolume);
- vd.SetVolume(vol);
- }
- }
-
-
-
- // store the name and UI info for routes
- if (item.SourceKey == "$off")
- {
- CurrentSourceInfoKey = routeKey;
- CurrentSourceInfo = null;
- }
- else if (item.SourceKey != null)
- {
- CurrentSourceInfoKey = routeKey;
- CurrentSourceInfo = item;
- }
- // And finally, set the "control". This will trigger event
- //CurrentControlDevice = DeviceManager.GetDeviceForKey(item.SourceKey) as Device;
-
- OnFeedback.FireUpdate();
-
- // report back when done
- if (successCallback != null)
- successCallback();
-
- }, 0); // end of CTimer
- }
-
- ///
- /// Will power the room on with the last-used source
- ///
- public void PowerOnToDefaultOrLastSource()
- {
- if (!EnablePowerOnToLastSource || LastSourceKey == null)
- return;
- RunRouteAction(LastSourceKey);
- }
-
- ///
- /// Does what it says
- ///
- public override void SetDefaultLevels()
- {
- Debug.Console(1, this, "Restoring default levels");
- var vc = CurrentVolumeControls as IBasicVolumeWithFeedback;
- if (vc != null)
- vc.SetVolume(DefaultVolume);
- }
-
- ///
- ///
- ///
- ///
- ///
- bool DoRoute(SourceRouteListItem route)
- {
- IRoutingSinkNoSwitching dest = null;
-
- if (route.DestinationKey.Equals("$defaultaudio", StringComparison.OrdinalIgnoreCase))
- dest = DefaultAudioDevice;
- else if (route.DestinationKey.Equals("$defaultDisplay", StringComparison.OrdinalIgnoreCase))
- dest = DefaultDisplay;
- else
- dest = DeviceManager.GetDeviceForKey(route.DestinationKey) as IRoutingSinkNoSwitching;
-
- if (dest == null)
- {
- Debug.Console(1, this, "Cannot route, unknown destination '{0}'", route.DestinationKey);
- return false;
- }
-
- if (route.SourceKey.Equals("$off", StringComparison.OrdinalIgnoreCase))
- {
- dest.ReleaseRoute();
- if (dest is IPower)
- (dest as IPower).PowerOff();
- }
- else
- {
- var source = DeviceManager.GetDeviceForKey(route.SourceKey) as IRoutingOutputs;
- if (source == null)
- {
- Debug.Console(1, this, "Cannot route unknown source '{0}' to {1}", route.SourceKey, route.DestinationKey);
- return false;
- }
- dest.ReleaseAndMakeRoute(source, route.Type);
- }
- return true;
- }
-
- public override void RoomVacatedForTimeoutPeriod(object o)
- {
- //Implement this
- }
-
- ///
- /// Runs "roomOff" action on all rooms not set to ExcludeFromGlobalFunctions
- ///
- public static void AllRoomsOff()
- {
- var allRooms = DeviceManager.AllDevices.Where(d =>
- d is EssentialsHuddleSpaceRoom && !(d as EssentialsHuddleSpaceRoom).ExcludeFromGlobalFunctions);
- foreach (var room in allRooms)
- (room as EssentialsHuddleSpaceRoom).RunRouteAction("roomOff");
- }
- }
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Crestron.SimplSharp;
+
+using PepperDash.Core;
+using PepperDash.Essentials.Core;
+using PepperDash.Essentials.Core.Config;
+using PepperDash.Essentials.Room.Config;
+
+namespace PepperDash.Essentials
+{
+ public class EssentialsHuddleSpaceRoom : EssentialsRoomBase, IHasCurrentSourceInfoChange, IRunRouteAction, IRunDefaultPresentRoute
+ {
+ public event EventHandler CurrentVolumeDeviceChange;
+ public event SourceInfoChangeHandler CurrentSingleSourceChange;
+
+ protected override Func OnFeedbackFunc
+ {
+ get
+ {
+ return () =>
+ {
+ var disp = DefaultDisplay as DisplayBase;
+ var val = CurrentSourceInfo != null
+ && CurrentSourceInfo.Type == eSourceListItemType.Route
+ && disp != null;
+ //&& disp.PowerIsOnFeedback.BoolValue;
+ return val;
+ };
+ }
+ }
+ ///
+ ///
+ ///
+ protected override Func IsWarmingFeedbackFunc
+ {
+ get
+ {
+ return () =>
+ {
+ var disp = DefaultDisplay as DisplayBase;
+ if (disp != null)
+ return disp.IsWarmingUpFeedback.BoolValue;
+ else
+ return false;
+ };
+ }
+ }
+ ///
+ ///
+ ///
+ protected override Func IsCoolingFeedbackFunc
+ {
+ get
+ {
+ return () =>
+ {
+ var disp = DefaultDisplay as DisplayBase;
+ if (disp != null)
+ return disp.IsCoolingDownFeedback.BoolValue;
+ else
+ return false;
+ };
+ }
+ }
+
+ public EssentialsRoomPropertiesConfig Config { get; private set; }
+
+ public IRoutingSinkWithSwitching DefaultDisplay { get; private set; }
+ public IRoutingSinkNoSwitching DefaultAudioDevice { get; private set; }
+ public IBasicVolumeControls DefaultVolumeControls { get; private set; }
+
+ public bool ExcludeFromGlobalFunctions { get; set; }
+
+ ///
+ /// The config name of the source list
+ ///
+ public string SourceListKey { get; set; }
+
+ public string DefaultSourceItem { get; set; }
+
+ public ushort DefaultVolume { get; set; }
+
+ ///
+ /// If room is off, enables power on to last source. Default true
+ ///
+ public bool EnablePowerOnToLastSource { get; set; }
+ string LastSourceKey;
+
+ ///
+ ///
+ ///
+ public IBasicVolumeControls CurrentVolumeControls
+ {
+ get { return _CurrentAudioDevice; }
+ set
+ {
+ if (value == _CurrentAudioDevice) return;
+
+ var oldDev = _CurrentAudioDevice;
+ // derigister this room from the device, if it can
+ if (oldDev is IInUseTracking)
+ (oldDev as IInUseTracking).InUseTracker.RemoveUser(this, "audio");
+ var handler = CurrentVolumeDeviceChange;
+ if (handler != null)
+ CurrentVolumeDeviceChange(this, new VolumeDeviceChangeEventArgs(oldDev, value, ChangeType.WillChange));
+ _CurrentAudioDevice = value;
+ if (handler != null)
+ CurrentVolumeDeviceChange(this, new VolumeDeviceChangeEventArgs(oldDev, value, ChangeType.DidChange));
+ // register this room with new device, if it can
+ if (_CurrentAudioDevice is IInUseTracking)
+ (_CurrentAudioDevice as IInUseTracking).InUseTracker.AddUser(this, "audio");
+ }
+ }
+ IBasicVolumeControls _CurrentAudioDevice;
+
+ ///
+ /// The SourceListItem last run - containing names and icons
+ ///
+ public SourceListItem CurrentSourceInfo
+ {
+ get { return _CurrentSourceInfo; }
+ private set
+ {
+ if (value == _CurrentSourceInfo) return;
+
+ var handler = CurrentSingleSourceChange;
+ // remove from in-use tracker, if so equipped
+ if(_CurrentSourceInfo != null && _CurrentSourceInfo.SourceDevice is IInUseTracking)
+ (_CurrentSourceInfo.SourceDevice as IInUseTracking).InUseTracker.RemoveUser(this, "control");
+
+ if (handler != null)
+ handler(this, _CurrentSourceInfo, ChangeType.WillChange);
+
+ _CurrentSourceInfo = value;
+
+ // add to in-use tracking
+ if (_CurrentSourceInfo != null && _CurrentSourceInfo.SourceDevice is IInUseTracking)
+ (_CurrentSourceInfo.SourceDevice as IInUseTracking).InUseTracker.AddUser(this, "control");
+ if (handler != null)
+ handler(this, _CurrentSourceInfo, ChangeType.DidChange);
+ }
+ }
+ SourceListItem _CurrentSourceInfo;
+
+ public string CurrentSourceInfoKey { get; private set; }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ public EssentialsHuddleSpaceRoom(string key, string name, IRoutingSinkWithSwitching defaultDisplay,
+ IRoutingSinkNoSwitching defaultAudio, EssentialsRoomPropertiesConfig config)
+ : base(key, name)
+ {
+ Config = config;
+ DefaultDisplay = defaultDisplay;
+ DefaultAudioDevice = defaultAudio;
+ if (defaultAudio is IBasicVolumeControls)
+ DefaultVolumeControls = defaultAudio as IBasicVolumeControls;
+ else if (defaultAudio is IHasVolumeDevice)
+ DefaultVolumeControls = (defaultAudio as IHasVolumeDevice).VolumeDevice;
+ CurrentVolumeControls = DefaultVolumeControls;
+
+ var disp = DefaultDisplay as DisplayBase;
+ if (disp != null)
+ {
+ // Link power, warming, cooling to display
+ disp.PowerIsOnFeedback.OutputChange += (o, a) =>
+ {
+ if (disp.PowerIsOnFeedback.BoolValue != OnFeedback.BoolValue)
+ {
+ if (!disp.PowerIsOnFeedback.BoolValue)
+ CurrentSourceInfo = null;
+ OnFeedback.FireUpdate();
+ }
+ };
+
+ disp.IsWarmingUpFeedback.OutputChange += (o, a) =>
+ {
+ IsWarmingUpFeedback.FireUpdate();
+ if (!IsWarmingUpFeedback.BoolValue)
+ (DefaultDisplay as IBasicVolumeWithFeedback).SetVolume(DefaultVolume);
+ };
+ disp.IsCoolingDownFeedback.OutputChange += (o, a) =>
+ {
+ IsCoolingDownFeedback.FireUpdate();
+ };
+ }
+
+ SourceListKey = "default";
+ EnablePowerOnToLastSource = true;
+ }
+
+
+ ///
+ ///
+ ///
+ protected override void EndShutdown()
+ {
+ SetDefaultLevels();
+
+ RunDefaultPresentRoute();
+
+ CrestronEnvironment.Sleep(1000);
+
+ RunRouteAction("roomOff");
+ }
+
+ ///
+ /// Routes the default source item, if any
+ ///
+ public override bool RunDefaultPresentRoute()
+ {
+ if (DefaultSourceItem == null)
+ {
+ Debug.Console(0, this, "Unable to run default present route, DefaultSourceItem is null.");
+ return false;
+ }
+
+ RunRouteAction(DefaultSourceItem);
+ return true;
+ }
+
+ ///
+ ///
+ ///
+ ///
+ public void RunRouteAction(string routeKey)
+ {
+ RunRouteAction(routeKey, null);
+ }
+
+ ///
+ /// Gets a source from config list SourceListKey and dynamically build and executes the
+ /// route or commands
+ ///
+ ///
+ public void RunRouteAction(string routeKey, Action successCallback)
+ {
+ // Run this on a separate thread
+ new CTimer(o =>
+ {
+ Debug.Console(1, this, "Run route action '{0}'", routeKey);
+ var dict = ConfigReader.ConfigObject.GetSourceListForKey(SourceListKey);
+ if(dict == null)
+ {
+ Debug.Console(1, this, "WARNING: Config source list '{0}' not found", SourceListKey);
+ return;
+ }
+
+ // Try to get the list item by it's string key
+ if (!dict.ContainsKey(routeKey))
+ {
+ Debug.Console(1, this, "WARNING: No item '{0}' found on config list '{1}'",
+ routeKey, SourceListKey);
+ return;
+ }
+
+ var item = dict[routeKey];
+ //Debug.Console(2, this, "Action {0} has {1} steps",
+ // item.SourceKey, item.RouteList.Count);
+
+ // End usage timer on last source
+ if (!string.IsNullOrEmpty(LastSourceKey))
+ {
+ var lastSource = dict[LastSourceKey].SourceDevice;
+
+ try
+ {
+ if (lastSource != null && lastSource is IUsageTracking)
+ (lastSource as IUsageTracking).UsageTracker.EndDeviceUsage();
+ }
+ catch (Exception e)
+ {
+ Debug.Console(1, this, "*#* EXCEPTION in end usage tracking (257):\r{0}", e);
+ }
+ }
+
+ // Let's run it
+ if (routeKey.ToLower() != "roomoff")
+ {
+ LastSourceKey = routeKey;
+ }
+ else
+ {
+ CurrentSourceInfoKey = null;
+ }
+
+ foreach (var route in item.RouteList)
+ {
+ // if there is a $defaultAll on route, run two separate
+ if (route.DestinationKey.Equals("$defaultAll", StringComparison.OrdinalIgnoreCase))
+ {
+ // Going to assume a single-path route for now
+ var tempVideo = new SourceRouteListItem
+ {
+ DestinationKey = "$defaultDisplay",
+ SourceKey = route.SourceKey,
+ Type = eRoutingSignalType.Video
+ };
+ DoRoute(tempVideo);
+
+ //var tempAudio = new SourceRouteListItem
+ //{
+ // DestinationKey = "$defaultAudio",
+ // SourceKey = route.SourceKey,
+ // Type = eRoutingSignalType.Audio
+ //};
+ //DoRoute(tempAudio);
+ //continue; -- not sure why this was here
+ }
+ else
+ DoRoute(route);
+ }
+
+ // Start usage timer on routed source
+ if (item.SourceDevice is IUsageTracking)
+ {
+ (item.SourceDevice as IUsageTracking).UsageTracker.StartDeviceUsage();
+ }
+
+
+
+
+ // Set volume control, using default if non provided
+ IBasicVolumeControls volDev = null;
+ // Handle special cases for volume control
+ if (string.IsNullOrEmpty(item.VolumeControlKey)
+ || item.VolumeControlKey.Equals("$defaultAudio", StringComparison.OrdinalIgnoreCase))
+ volDev = DefaultVolumeControls;
+ else if (item.VolumeControlKey.Equals("$defaultDisplay", StringComparison.OrdinalIgnoreCase))
+ volDev = DefaultDisplay as IBasicVolumeControls;
+ // Or a specific device, probably rarely used.
+ else
+ {
+ var dev = DeviceManager.GetDeviceForKey(item.VolumeControlKey);
+ if (dev is IBasicVolumeControls)
+ volDev = dev as IBasicVolumeControls;
+ else if (dev is IHasVolumeDevice)
+ volDev = (dev as IHasVolumeDevice).VolumeDevice;
+ }
+
+ if (volDev != CurrentVolumeControls)
+ {
+ // zero the volume on the device we are leaving.
+ // Set the volume to default on device we are entering
+ if (ZeroVolumeWhenSwtichingVolumeDevices && CurrentVolumeControls is IBasicVolumeWithFeedback)
+ {
+ var vd = CurrentVolumeControls as IBasicVolumeWithFeedback;
+ SavedVolumeLevels[vd] = (uint)vd.VolumeLevelFeedback.IntValue;
+ vd.SetVolume(0);
+ }
+ CurrentVolumeControls = volDev;
+ if (ZeroVolumeWhenSwtichingVolumeDevices && CurrentVolumeControls is IBasicVolumeWithFeedback)
+ {
+ var vd = CurrentVolumeControls as IBasicVolumeWithFeedback;
+ ushort vol = (SavedVolumeLevels.ContainsKey(vd) ? (ushort)SavedVolumeLevels[vd] : DefaultVolume);
+ vd.SetVolume(vol);
+ }
+ }
+
+
+
+ // store the name and UI info for routes
+ if (item.SourceKey == "$off")
+ {
+ CurrentSourceInfoKey = routeKey;
+ CurrentSourceInfo = null;
+ }
+ else if (item.SourceKey != null)
+ {
+ CurrentSourceInfoKey = routeKey;
+ CurrentSourceInfo = item;
+ }
+ // And finally, set the "control". This will trigger event
+ //CurrentControlDevice = DeviceManager.GetDeviceForKey(item.SourceKey) as Device;
+
+ OnFeedback.FireUpdate();
+
+ // report back when done
+ if (successCallback != null)
+ successCallback();
+
+ }, 0); // end of CTimer
+ }
+
+ ///
+ /// Will power the room on with the last-used source
+ ///
+ public override void PowerOnToDefaultOrLastSource()
+ {
+ if (!EnablePowerOnToLastSource || LastSourceKey == null)
+ return;
+ RunRouteAction(LastSourceKey);
+ }
+
+ ///
+ /// Does what it says
+ ///
+ public override void SetDefaultLevels()
+ {
+ Debug.Console(1, this, "Restoring default levels");
+ var vc = CurrentVolumeControls as IBasicVolumeWithFeedback;
+ if (vc != null)
+ vc.SetVolume(DefaultVolume);
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ bool DoRoute(SourceRouteListItem route)
+ {
+ IRoutingSinkNoSwitching dest = null;
+
+ if (route.DestinationKey.Equals("$defaultaudio", StringComparison.OrdinalIgnoreCase))
+ dest = DefaultAudioDevice;
+ else if (route.DestinationKey.Equals("$defaultDisplay", StringComparison.OrdinalIgnoreCase))
+ dest = DefaultDisplay;
+ else
+ dest = DeviceManager.GetDeviceForKey(route.DestinationKey) as IRoutingSinkNoSwitching;
+
+ if (dest == null)
+ {
+ Debug.Console(1, this, "Cannot route, unknown destination '{0}'", route.DestinationKey);
+ return false;
+ }
+
+ if (route.SourceKey.Equals("$off", StringComparison.OrdinalIgnoreCase))
+ {
+ dest.ReleaseRoute();
+ if (dest is IPower)
+ (dest as IPower).PowerOff();
+ }
+ else
+ {
+ var source = DeviceManager.GetDeviceForKey(route.SourceKey) as IRoutingOutputs;
+ if (source == null)
+ {
+ Debug.Console(1, this, "Cannot route unknown source '{0}' to {1}", route.SourceKey, route.DestinationKey);
+ return false;
+ }
+ dest.ReleaseAndMakeRoute(source, route.Type);
+ }
+ return true;
+ }
+
+ public override void RoomVacatedForTimeoutPeriod(object o)
+ {
+ //Implement this
+ }
+
+ ///
+ /// Runs "roomOff" action on all rooms not set to ExcludeFromGlobalFunctions
+ ///
+ public static void AllRoomsOff()
+ {
+ var allRooms = DeviceManager.AllDevices.Where(d =>
+ d is EssentialsHuddleSpaceRoom && !(d as EssentialsHuddleSpaceRoom).ExcludeFromGlobalFunctions);
+ foreach (var room in allRooms)
+ (room as EssentialsHuddleSpaceRoom).RunRouteAction("roomOff");
+ }
+ }
}
\ No newline at end of file
diff --git a/PepperDashEssentials/Room/Types/EssentialsHuddleVtc1Room.cs b/PepperDashEssentials/Room/Types/EssentialsHuddleVtc1Room.cs
index 60027142..12a54592 100644
--- a/PepperDashEssentials/Room/Types/EssentialsHuddleVtc1Room.cs
+++ b/PepperDashEssentials/Room/Types/EssentialsHuddleVtc1Room.cs
@@ -5,14 +5,16 @@ using System.Text;
using Crestron.SimplSharp;
using PepperDash.Core;
-using PepperDash.Essentials.Core;
+using PepperDash.Essentials.Core;
+using PepperDash.Essentials.Core.Config;
using PepperDash.Essentials.Room.Config;
using PepperDash.Essentials.Devices.Common.Codec;
using PepperDash.Essentials.Devices.Common.VideoCodec;
namespace PepperDash.Essentials
{
- public class EssentialsHuddleVtc1Room : EssentialsRoomBase, IHasCurrentSourceInfoChange, IPrivacy, IHasCurrentVolumeControls
+ public class EssentialsHuddleVtc1Room : EssentialsRoomBase, IHasCurrentSourceInfoChange,
+ IPrivacy, IHasCurrentVolumeControls, IRunRouteAction, IRunDefaultCallRoute, IHasVideoCodec
{
public event EventHandler CurrentVolumeDeviceChange;
public event SourceInfoChangeHandler CurrentSingleSourceChange;
@@ -282,10 +284,11 @@ namespace PepperDash.Essentials
///
/// Routes the default source item, if any. Returns true when default route exists
///
- public bool RunDefaultPresentRoute()
+ public override bool RunDefaultPresentRoute()
{
- //if (DefaultSourceItem != null)
+ if (DefaultSourceItem != null)
RunRouteAction(DefaultSourceItem);
+
return DefaultSourceItem != null;
}
@@ -531,7 +534,7 @@ namespace PepperDash.Essentials
///
/// Will power the room on with the last-used source
///
- public void PowerOnToDefaultOrLastSource()
+ public override void PowerOnToDefaultOrLastSource()
{
if (!EnablePowerOnToLastSource || LastSourceKey == null)
return;
diff --git a/PepperDashEssentials/Room/Types/EssentialsPresentationRoom.cs b/PepperDashEssentials/Room/Types/EssentialsPresentationRoom.cs
index 86de8977..c21c265f 100644
--- a/PepperDashEssentials/Room/Types/EssentialsPresentationRoom.cs
+++ b/PepperDashEssentials/Room/Types/EssentialsPresentationRoom.cs
@@ -1,437 +1,437 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using Crestron.SimplSharp;
+//using System;
+//using System.Collections.Generic;
+//using System.Linq;
+//using System.Text;
+//using Crestron.SimplSharp;
-using PepperDash.Core;
-using PepperDash.Essentials.Core;
-using PepperDash.Essentials.Room.Config;
+//using PepperDash.Core;
+//using PepperDash.Essentials.Core;
+//using PepperDash.Essentials.Room.Config;
-namespace PepperDash.Essentials
-{
- public class EssentialsPresentationRoom : EssentialsRoomBase, IHasCurrentSourceInfoChange
- {
- public event EventHandler CurrentVolumeDeviceChange;
- public event SourceInfoChangeHandler CurrentSingleSourceChange;
- public event SourceInfoChangeHandler CurrentDisplay1SourceChange;
- public event SourceInfoChangeHandler CurrentDisplay2SourceChange;
+//namespace PepperDash.Essentials
+//{
+// public class EssentialsPresentationRoom : EssentialsRoomBase, IHasCurrentSourceInfoChange
+// {
+// public event EventHandler CurrentVolumeDeviceChange;
+// public event SourceInfoChangeHandler CurrentSingleSourceChange;
+// public event SourceInfoChangeHandler CurrentDisplay1SourceChange;
+// public event SourceInfoChangeHandler CurrentDisplay2SourceChange;
- protected override Func OnFeedbackFunc { get {
- return () => (CurrentSingleSourceInfo != null
- && CurrentSingleSourceInfo.Type != eSourceListItemType.Off)
- || (Display1SourceInfo != null
- && Display1SourceInfo.Type != eSourceListItemType.Off)
- || (Display2SourceInfo != null
- && Display2SourceInfo.Type != eSourceListItemType.Off); } }
+// protected override Func OnFeedbackFunc { get {
+// return () => (CurrentSingleSourceInfo != null
+// && CurrentSingleSourceInfo.Type != eSourceListItemType.Off)
+// || (Display1SourceInfo != null
+// && Display1SourceInfo.Type != eSourceListItemType.Off)
+// || (Display2SourceInfo != null
+// && Display2SourceInfo.Type != eSourceListItemType.Off); } }
- protected override Func IsWarmingFeedbackFunc { get { return () =>false;; } }
- protected override Func IsCoolingFeedbackFunc { get { return () => false; } }
+// protected override Func IsWarmingFeedbackFunc { get { return () =>false;; } }
+// protected override Func IsCoolingFeedbackFunc { get { return () => false; } }
- public EssentialsPresentationRoomPropertiesConfig Config { get; private set; }
+// public EssentialsPresentationRoomPropertiesConfig Config { get; private set; }
- public Dictionary Displays { get; private set; }
+// public Dictionary Displays { get; private set; }
- public IRoutingSinkNoSwitching DefaultAudioDevice { get; private set; }
- public IBasicVolumeControls DefaultVolumeControls { get; private set; }
+// public IRoutingSinkNoSwitching DefaultAudioDevice { get; private set; }
+// public IBasicVolumeControls DefaultVolumeControls { get; private set; }
- ///
- /// The config name of the source list
- ///
- public string SourceListKey { get; set; }
+// ///
+// /// The config name of the source list
+// ///
+// public string SourceListKey { get; set; }
- ///
- /// If room is off, enables power on to last source. Default true
- ///
- public bool EnablePowerOnToLastSource { get; set; }
- string LastSourceKey;
+// ///
+// /// If room is off, enables power on to last source. Default true
+// ///
+// public bool EnablePowerOnToLastSource { get; set; }
+// string LastSourceKey;
- public enum eVideoRoutingMode
- {
- SelectSourceSelectDisplay, SourceToAllDisplays
- }
+// public enum eVideoRoutingMode
+// {
+// SelectSourceSelectDisplay, SourceToAllDisplays
+// }
- public eVideoRoutingMode VideoRoutingMode { get; set; }
+// public eVideoRoutingMode VideoRoutingMode { get; set; }
- public enum eAudioRoutingMode
- {
- AudioFollowsLastVideo, SelectAudioFromDisplay
- }
+// public enum eAudioRoutingMode
+// {
+// AudioFollowsLastVideo, SelectAudioFromDisplay
+// }
- ///
- ///
- ///
- public IBasicVolumeControls CurrentVolumeControls
- {
- get { return _CurrentAudioDevice; }
- set
- {
- if (value == _CurrentAudioDevice) return;
+// ///
+// ///
+// ///
+// public IBasicVolumeControls CurrentVolumeControls
+// {
+// get { return _CurrentAudioDevice; }
+// set
+// {
+// if (value == _CurrentAudioDevice) return;
- var oldDev = _CurrentAudioDevice;
- // derigister this room from the device, if it can
- if (oldDev is IInUseTracking)
- (oldDev as IInUseTracking).InUseTracker.RemoveUser(this, "audio");
- var handler = CurrentVolumeDeviceChange;
- if (handler != null)
- CurrentVolumeDeviceChange(this, new VolumeDeviceChangeEventArgs(oldDev, value, ChangeType.WillChange));
- _CurrentAudioDevice = value;
- if (handler != null)
- CurrentVolumeDeviceChange(this, new VolumeDeviceChangeEventArgs(oldDev, value, ChangeType.DidChange));
- // register this room with new device, if it can
- if (_CurrentAudioDevice is IInUseTracking)
- (_CurrentAudioDevice as IInUseTracking).InUseTracker.AddUser(this, "audio");
- }
- }
- IBasicVolumeControls _CurrentAudioDevice;
+// var oldDev = _CurrentAudioDevice;
+// // derigister this room from the device, if it can
+// if (oldDev is IInUseTracking)
+// (oldDev as IInUseTracking).InUseTracker.RemoveUser(this, "audio");
+// var handler = CurrentVolumeDeviceChange;
+// if (handler != null)
+// CurrentVolumeDeviceChange(this, new VolumeDeviceChangeEventArgs(oldDev, value, ChangeType.WillChange));
+// _CurrentAudioDevice = value;
+// if (handler != null)
+// CurrentVolumeDeviceChange(this, new VolumeDeviceChangeEventArgs(oldDev, value, ChangeType.DidChange));
+// // register this room with new device, if it can
+// if (_CurrentAudioDevice is IInUseTracking)
+// (_CurrentAudioDevice as IInUseTracking).InUseTracker.AddUser(this, "audio");
+// }
+// }
+// IBasicVolumeControls _CurrentAudioDevice;
- ///
- /// The SourceListItem last run - containing names and icons. The complex setter is
- /// to add/remove this room to the source's InUseTracking, if it is capable
- ///
- public SourceListItem CurrentSingleSourceInfo
- {
- get { return _CurrentSingleSourceInfo; }
- private set
- {
- if (value == _CurrentSingleSourceInfo) return;
+// ///
+// /// The SourceListItem last run - containing names and icons. The complex setter is
+// /// to add/remove this room to the source's InUseTracking, if it is capable
+// ///
+// public SourceListItem CurrentSingleSourceInfo
+// {
+// get { return _CurrentSingleSourceInfo; }
+// private set
+// {
+// if (value == _CurrentSingleSourceInfo) return;
- var handler = CurrentSingleSourceChange;
- // remove from in-use tracker, if so equipped
- if(_CurrentSingleSourceInfo != null && _CurrentSingleSourceInfo.SourceDevice is IInUseTracking)
- (_CurrentSingleSourceInfo.SourceDevice as IInUseTracking).InUseTracker.RemoveUser(this, "control");
+// var handler = CurrentSingleSourceChange;
+// // remove from in-use tracker, if so equipped
+// if(_CurrentSingleSourceInfo != null && _CurrentSingleSourceInfo.SourceDevice is IInUseTracking)
+// (_CurrentSingleSourceInfo.SourceDevice as IInUseTracking).InUseTracker.RemoveUser(this, "control");
- if (handler != null)
- handler(this, _CurrentSingleSourceInfo, ChangeType.WillChange);
+// if (handler != null)
+// handler(this, _CurrentSingleSourceInfo, ChangeType.WillChange);
- _CurrentSingleSourceInfo = value;
+// _CurrentSingleSourceInfo = value;
- // add to in-use tracking
- if (_CurrentSingleSourceInfo != null && _CurrentSingleSourceInfo.SourceDevice is IInUseTracking)
- (_CurrentSingleSourceInfo.SourceDevice as IInUseTracking).InUseTracker.AddUser(this, "control");
- if (handler != null)
- handler(this, _CurrentSingleSourceInfo, ChangeType.DidChange);
- }
- }
- SourceListItem _CurrentSingleSourceInfo;
+// // add to in-use tracking
+// if (_CurrentSingleSourceInfo != null && _CurrentSingleSourceInfo.SourceDevice is IInUseTracking)
+// (_CurrentSingleSourceInfo.SourceDevice as IInUseTracking).InUseTracker.AddUser(this, "control");
+// if (handler != null)
+// handler(this, _CurrentSingleSourceInfo, ChangeType.DidChange);
+// }
+// }
+// SourceListItem _CurrentSingleSourceInfo;
- public SourceListItem Display1SourceInfo
- {
- get { return _Display1SourceInfo; }
- set
- {
- if (value == _Display1SourceInfo) return;
+// public SourceListItem Display1SourceInfo
+// {
+// get { return _Display1SourceInfo; }
+// set
+// {
+// if (value == _Display1SourceInfo) return;
- var handler = CurrentDisplay1SourceChange;
- if (handler != null)
- handler(this, _Display1SourceInfo, ChangeType.WillChange);
+// var handler = CurrentDisplay1SourceChange;
+// if (handler != null)
+// handler(this, _Display1SourceInfo, ChangeType.WillChange);
- _Display1SourceInfo = value;
+// _Display1SourceInfo = value;
- if (handler != null)
- handler(this, _Display1SourceInfo, ChangeType.DidChange);
- }
- }
- SourceListItem _Display1SourceInfo;
+// if (handler != null)
+// handler(this, _Display1SourceInfo, ChangeType.DidChange);
+// }
+// }
+// SourceListItem _Display1SourceInfo;
- public SourceListItem Display2SourceInfo
- {
- get { return _Display2SourceInfo; }
- set
- {
- if (value == _Display2SourceInfo) return;
+// public SourceListItem Display2SourceInfo
+// {
+// get { return _Display2SourceInfo; }
+// set
+// {
+// if (value == _Display2SourceInfo) return;
- var handler = CurrentDisplay2SourceChange;
- if (handler != null)
- handler(this, _Display2SourceInfo, ChangeType.WillChange);
+// var handler = CurrentDisplay2SourceChange;
+// if (handler != null)
+// handler(this, _Display2SourceInfo, ChangeType.WillChange);
- _Display2SourceInfo = value;
+// _Display2SourceInfo = value;
- if (handler != null)
- handler(this, _Display2SourceInfo, ChangeType.DidChange);
- }
- }
- SourceListItem _Display2SourceInfo;
+// if (handler != null)
+// handler(this, _Display2SourceInfo, ChangeType.DidChange);
+// }
+// }
+// SourceListItem _Display2SourceInfo;
- ///
- /// If an audio dialer is available for this room
- ///
- public bool HasAudioDialer { get { return false; } }
- ///
- ///
- ///
- ///
- ///
- public EssentialsPresentationRoom(string key, string name,
- Dictionary displays,
- IBasicVolumeWithFeedback defaultVolume, EssentialsPresentationRoomPropertiesConfig config)
- : base(key, name)
- {
- Config = config;
- Displays = displays;
+// ///
+// /// If an audio dialer is available for this room
+// ///
+// public bool HasAudioDialer { get { return false; } }
+// ///
+// ///
+// ///
+// ///
+// ///
+// public EssentialsPresentationRoom(string key, string name,
+// Dictionary displays,
+// IBasicVolumeWithFeedback defaultVolume, EssentialsPresentationRoomPropertiesConfig config)
+// : base(key, name)
+// {
+// Config = config;
+// Displays = displays;
- DefaultVolumeControls = defaultVolume;
- CurrentVolumeControls = defaultVolume;
+// DefaultVolumeControls = defaultVolume;
+// CurrentVolumeControls = defaultVolume;
- //DefaultAudioDevice = defaultAudio;
- //if (defaultAudio is IBasicVolumeControls)
- // DefaultVolumeControls = defaultAudio as IBasicVolumeControls;
- //else if (defaultAudio is IHasVolumeDevice)
- // DefaultVolumeControls = (defaultAudio as IHasVolumeDevice).VolumeDevice;
+// //DefaultAudioDevice = defaultAudio;
+// //if (defaultAudio is IBasicVolumeControls)
+// // DefaultVolumeControls = defaultAudio as IBasicVolumeControls;
+// //else if (defaultAudio is IHasVolumeDevice)
+// // DefaultVolumeControls = (defaultAudio as IHasVolumeDevice).VolumeDevice;
- SourceListKey = "default";
- EnablePowerOnToLastSource = true;
- }
+// SourceListKey = "default";
+// EnablePowerOnToLastSource = true;
+// }
- ///
- /// Run the same source to all destinations
- ///
- ///
- public void RouteSourceToAllDestinations(SourceListItem sourceItem)
- {
- if (Config.Volumes.Master != null)
- {
- var audioDev = DeviceManager.GetDeviceForKey(Config.Volumes.Master.DeviceKey);
- if (audioDev is IBasicVolumeWithFeedback)
- {
+// ///
+// /// Run the same source to all destinations
+// ///
+// ///
+// public void RouteSourceToAllDestinations(SourceListItem sourceItem)
+// {
+// if (Config.Volumes.Master != null)
+// {
+// var audioDev = DeviceManager.GetDeviceForKey(Config.Volumes.Master.DeviceKey);
+// if (audioDev is IBasicVolumeWithFeedback)
+// {
- }
- }
+// }
+// }
- foreach (var display in Displays.Values)
- {
- if (sourceItem != null)
- DoVideoRoute(sourceItem.SourceKey, display.Key);
- else
- DoVideoRoute("$off", display.Key);
- }
- Display1SourceInfo = sourceItem;
- Display2SourceInfo = sourceItem;
- CurrentSingleSourceInfo = sourceItem;
- OnFeedback.FireUpdate();
- }
+// foreach (var display in Displays.Values)
+// {
+// if (sourceItem != null)
+// DoVideoRoute(sourceItem.SourceKey, display.Key);
+// else
+// DoVideoRoute("$off", display.Key);
+// }
+// Display1SourceInfo = sourceItem;
+// Display2SourceInfo = sourceItem;
+// CurrentSingleSourceInfo = sourceItem;
+// OnFeedback.FireUpdate();
+// }
- public void SourceToDisplay1(SourceListItem sourceItem)
- {
- DoVideoRoute(sourceItem.SourceKey, Displays[1].Key);
- Display1SourceInfo = sourceItem;
- OnFeedback.FireUpdate();
- }
+// public void SourceToDisplay1(SourceListItem sourceItem)
+// {
+// DoVideoRoute(sourceItem.SourceKey, Displays[1].Key);
+// Display1SourceInfo = sourceItem;
+// OnFeedback.FireUpdate();
+// }
- public void SourceToDisplay2(SourceListItem sourceItem)
- {
- DoVideoRoute(sourceItem.SourceKey, Displays[2].Key);
- Display2SourceInfo = sourceItem;
- OnFeedback.FireUpdate();
- }
+// public void SourceToDisplay2(SourceListItem sourceItem)
+// {
+// DoVideoRoute(sourceItem.SourceKey, Displays[2].Key);
+// Display2SourceInfo = sourceItem;
+// OnFeedback.FireUpdate();
+// }
- ///
- /// Basic source -> destination routing
- ///
- void DoVideoRoute(string sourceKey, string destinationKey)
- {
- new CTimer(o =>
- {
- var dest = DeviceManager.GetDeviceForKey(destinationKey) as IRoutingSinkNoSwitching;
- if (dest == null)
- {
- Debug.Console(1, this, "Cannot route. Destination '{0}' not found", destinationKey);
- return;
- }
- // off is special case
- if (sourceKey.Equals("$off", StringComparison.OrdinalIgnoreCase))
- {
- dest.ReleaseRoute();
- if (dest is IPower)
- (dest as IPower).PowerOff();
- return;
- }
+// ///
+// /// Basic source -> destination routing
+// ///
+// void DoVideoRoute(string sourceKey, string destinationKey)
+// {
+// new CTimer(o =>
+// {
+// var dest = DeviceManager.GetDeviceForKey(destinationKey) as IRoutingSinkNoSwitching;
+// if (dest == null)
+// {
+// Debug.Console(1, this, "Cannot route. Destination '{0}' not found", destinationKey);
+// return;
+// }
+// // off is special case
+// if (sourceKey.Equals("$off", StringComparison.OrdinalIgnoreCase))
+// {
+// dest.ReleaseRoute();
+// if (dest is IPower)
+// (dest as IPower).PowerOff();
+// return;
+// }
- var source = DeviceManager.GetDeviceForKey(sourceKey) as IRoutingOutputs;
- if (source == null)
- {
- Debug.Console(1, this, "Cannot route. Source '{0}' not found", sourceKey);
- return;
- }
- dest.ReleaseAndMakeRoute(source, eRoutingSignalType.Video);
- }, 0);
- }
+// var source = DeviceManager.GetDeviceForKey(sourceKey) as IRoutingOutputs;
+// if (source == null)
+// {
+// Debug.Console(1, this, "Cannot route. Source '{0}' not found", sourceKey);
+// return;
+// }
+// dest.ReleaseAndMakeRoute(source, eRoutingSignalType.Video);
+// }, 0);
+// }
- ///
- ///
- ///
- protected override void EndShutdown()
- {
- RunRouteAction("roomoff");
- }
+// ///
+// ///
+// ///
+// protected override void EndShutdown()
+// {
+// RunRouteAction("roomoff");
+// }
- ///
- ///
- ///
- ///
- public void RunRouteAction(string routeKey)
- {
- RunRouteAction(routeKey, null);
- }
+// ///
+// ///
+// ///
+// ///
+// public void RunRouteAction(string routeKey)
+// {
+// RunRouteAction(routeKey, null);
+// }
- ///
- /// Gets a source from config list SourceListKey and dynamically build and executes the
- /// route or commands
- ///
- ///
- public void RunRouteAction(string routeKey, Action successCallback)
- {
- // Run this on a separate thread
- new CTimer(o =>
- {
- Debug.Console(1, this, "Run room action '{0}'", routeKey);
- var dict = ConfigReader.ConfigObject.GetSourceListForKey(SourceListKey);
- if(dict == null)
- {
- Debug.Console(1, this, "WARNING: Config source list '{0}' not found", SourceListKey);
- return;
- }
+// ///
+// /// Gets a source from config list SourceListKey and dynamically build and executes the
+// /// route or commands
+// ///
+// ///
+// public void RunRouteAction(string routeKey, Action successCallback)
+// {
+// // Run this on a separate thread
+// new CTimer(o =>
+// {
+// Debug.Console(1, this, "Run room action '{0}'", routeKey);
+// var dict = ConfigReader.ConfigObject.GetSourceListForKey(SourceListKey);
+// if(dict == null)
+// {
+// Debug.Console(1, this, "WARNING: Config source list '{0}' not found", SourceListKey);
+// return;
+// }
- // Try to get the list item by it's string key
- if (!dict.ContainsKey(routeKey))
- {
- Debug.Console(1, this, "WARNING: No item '{0}' found on config list '{1}'",
- routeKey, SourceListKey);
- return;
- }
+// // Try to get the list item by it's string key
+// if (!dict.ContainsKey(routeKey))
+// {
+// Debug.Console(1, this, "WARNING: No item '{0}' found on config list '{1}'",
+// routeKey, SourceListKey);
+// return;
+// }
- var item = dict[routeKey];
- //Debug.Console(2, this, "Action {0} has {1} steps",
- // item.SourceKey, item.RouteList.Count);
+// var item = dict[routeKey];
+// //Debug.Console(2, this, "Action {0} has {1} steps",
+// // item.SourceKey, item.RouteList.Count);
- // Let's run it
- if (routeKey.ToLower() != "roomoff")
- LastSourceKey = routeKey;
+// // Let's run it
+// if (routeKey.ToLower() != "roomoff")
+// LastSourceKey = routeKey;
- foreach (var route in item.RouteList)
- {
- // if there is a $defaultAll on route, run two separate
- if (route.DestinationKey.Equals("$defaultAll", StringComparison.OrdinalIgnoreCase))
- {
- var tempAudio = new SourceRouteListItem
- {
- DestinationKey = "$defaultDisplay",
- SourceKey = route.SourceKey,
- Type = eRoutingSignalType.Video
- };
- DoRoute(tempAudio);
+// foreach (var route in item.RouteList)
+// {
+// // if there is a $defaultAll on route, run two separate
+// if (route.DestinationKey.Equals("$defaultAll", StringComparison.OrdinalIgnoreCase))
+// {
+// var tempAudio = new SourceRouteListItem
+// {
+// DestinationKey = "$defaultDisplay",
+// SourceKey = route.SourceKey,
+// Type = eRoutingSignalType.Video
+// };
+// DoRoute(tempAudio);
- var tempVideo = new SourceRouteListItem
- {
- DestinationKey = "$defaultAudio",
- SourceKey = route.SourceKey,
- Type = eRoutingSignalType.Audio
- };
- DoRoute(tempVideo);
- continue;
- }
- else
- DoRoute(route);
- }
+// var tempVideo = new SourceRouteListItem
+// {
+// DestinationKey = "$defaultAudio",
+// SourceKey = route.SourceKey,
+// Type = eRoutingSignalType.Audio
+// };
+// DoRoute(tempVideo);
+// continue;
+// }
+// else
+// DoRoute(route);
+// }
- // Set volume control on room, using default if non provided
- IBasicVolumeControls volDev = null;
- // Handle special cases for volume control
- if (string.IsNullOrEmpty(item.VolumeControlKey)
- || item.VolumeControlKey.Equals("$defaultAudio", StringComparison.OrdinalIgnoreCase))
- volDev = DefaultVolumeControls;
- //else if (item.VolumeControlKey.Equals("$defaultDisplay", StringComparison.OrdinalIgnoreCase))
- // volDev = DefaultDisplay as IBasicVolumeControls;
- // Or a specific device, probably rarely used.
- else
- {
- var dev = DeviceManager.GetDeviceForKey(item.VolumeControlKey);
- if (dev is IBasicVolumeControls)
- volDev = dev as IBasicVolumeControls;
- else if (dev is IHasVolumeDevice)
- volDev = (dev as IHasVolumeDevice).VolumeDevice;
- }
- CurrentVolumeControls = volDev;
+// // Set volume control on room, using default if non provided
+// IBasicVolumeControls volDev = null;
+// // Handle special cases for volume control
+// if (string.IsNullOrEmpty(item.VolumeControlKey)
+// || item.VolumeControlKey.Equals("$defaultAudio", StringComparison.OrdinalIgnoreCase))
+// volDev = DefaultVolumeControls;
+// //else if (item.VolumeControlKey.Equals("$defaultDisplay", StringComparison.OrdinalIgnoreCase))
+// // volDev = DefaultDisplay as IBasicVolumeControls;
+// // Or a specific device, probably rarely used.
+// else
+// {
+// var dev = DeviceManager.GetDeviceForKey(item.VolumeControlKey);
+// if (dev is IBasicVolumeControls)
+// volDev = dev as IBasicVolumeControls;
+// else if (dev is IHasVolumeDevice)
+// volDev = (dev as IHasVolumeDevice).VolumeDevice;
+// }
+// CurrentVolumeControls = volDev;
- // store the name and UI info for routes
- if (item.SourceKey != null)
- CurrentSingleSourceInfo = item;
- // And finally, set the "control". This will trigger event
- //CurrentControlDevice = DeviceManager.GetDeviceForKey(item.SourceKey) as Device;
+// // store the name and UI info for routes
+// if (item.SourceKey != null)
+// CurrentSingleSourceInfo = item;
+// // And finally, set the "control". This will trigger event
+// //CurrentControlDevice = DeviceManager.GetDeviceForKey(item.SourceKey) as Device;
- OnFeedback.FireUpdate();
+// OnFeedback.FireUpdate();
- // report back when done
- if (successCallback != null)
- successCallback();
- }, 0); // end of CTimer
- }
+// // report back when done
+// if (successCallback != null)
+// successCallback();
+// }, 0); // end of CTimer
+// }
- ///
- /// Will power the room on with the last-used source
- ///
- public void PowerOnToDefaultOrLastSource()
- {
- if (!EnablePowerOnToLastSource || LastSourceKey == null)
- return;
- RunRouteAction(LastSourceKey);
- }
+// ///
+// /// Will power the room on with the last-used source
+// ///
+// public void PowerOnToDefaultOrLastSource()
+// {
+// if (!EnablePowerOnToLastSource || LastSourceKey == null)
+// return;
+// RunRouteAction(LastSourceKey);
+// }
- ///
- /// Does what it says
- ///
- public override void SetDefaultLevels()
- {
- Debug.Console(0, this, "SetDefaultLevels not implemented");
- }
+// ///
+// /// Does what it says
+// ///
+// public override void SetDefaultLevels()
+// {
+// Debug.Console(0, this, "SetDefaultLevels not implemented");
+// }
- ///
- ///
- ///
- ///
- ///
- bool DoRoute(SourceRouteListItem route)
- {
- IRoutingSinkNoSwitching dest = null;
+// ///
+// ///
+// ///
+// ///
+// ///
+// bool DoRoute(SourceRouteListItem route)
+// {
+// IRoutingSinkNoSwitching dest = null;
- if (route.DestinationKey.Equals("$defaultaudio", StringComparison.OrdinalIgnoreCase))
- dest = DefaultAudioDevice;
- //else if (route.DestinationKey.Equals("$defaultDisplay", StringComparison.OrdinalIgnoreCase))
- // dest = DefaultDisplay;
- else
- dest = DeviceManager.GetDeviceForKey(route.DestinationKey) as IRoutingSinkNoSwitching;
+// if (route.DestinationKey.Equals("$defaultaudio", StringComparison.OrdinalIgnoreCase))
+// dest = DefaultAudioDevice;
+// //else if (route.DestinationKey.Equals("$defaultDisplay", StringComparison.OrdinalIgnoreCase))
+// // dest = DefaultDisplay;
+// else
+// dest = DeviceManager.GetDeviceForKey(route.DestinationKey) as IRoutingSinkNoSwitching;
- if (dest == null)
- {
- Debug.Console(1, this, "Cannot route, unknown destination '{0}'", route.DestinationKey);
- return false;
- }
+// if (dest == null)
+// {
+// Debug.Console(1, this, "Cannot route, unknown destination '{0}'", route.DestinationKey);
+// return false;
+// }
- if (route.SourceKey.Equals("$off", StringComparison.OrdinalIgnoreCase))
- {
- dest.ReleaseRoute();
- if (dest is IPower)
- (dest as IPower).PowerOff();
- }
- else
- {
- var source = DeviceManager.GetDeviceForKey(route.SourceKey) as IRoutingOutputs;
- if (source == null)
- {
- Debug.Console(1, this, "Cannot route unknown source '{0}' to {1}", route.SourceKey, route.DestinationKey);
- return false;
- }
- dest.ReleaseAndMakeRoute(source, route.Type);
- }
- return true;
- }
+// if (route.SourceKey.Equals("$off", StringComparison.OrdinalIgnoreCase))
+// {
+// dest.ReleaseRoute();
+// if (dest is IPower)
+// (dest as IPower).PowerOff();
+// }
+// else
+// {
+// var source = DeviceManager.GetDeviceForKey(route.SourceKey) as IRoutingOutputs;
+// if (source == null)
+// {
+// Debug.Console(1, this, "Cannot route unknown source '{0}' to {1}", route.SourceKey, route.DestinationKey);
+// return false;
+// }
+// dest.ReleaseAndMakeRoute(source, route.Type);
+// }
+// return true;
+// }
- public override void RoomVacatedForTimeoutPeriod(object o)
- {
- //Implement this
- }
+// public override void RoomVacatedForTimeoutPeriod(object o)
+// {
+// //Implement this
+// }
- }
-}
\ No newline at end of file
+// }
+//}
\ No newline at end of file
diff --git a/PepperDashEssentials/Room/Types/EssentialsRoomBase.cs b/PepperDashEssentials/Room/Types/EssentialsRoomBase.cs
index 6251c067..a364fa23 100644
--- a/PepperDashEssentials/Room/Types/EssentialsRoomBase.cs
+++ b/PepperDashEssentials/Room/Types/EssentialsRoomBase.cs
@@ -1,272 +1,294 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using Crestron.SimplSharp;
-using PepperDash.Core;
-using PepperDash.Essentials.Core;
-using PepperDash.Essentials.Devices.Common.Occupancy;
-
-namespace PepperDash.Essentials
-{
- ///
- ///
- ///
- public interface IHasCurrentSourceInfoChange
- {
- event SourceInfoChangeHandler CurrentSingleSourceChange;
- }
-
- ///
- ///
- ///
- public abstract class EssentialsRoomBase : Device
- {
- ///
- ///
- ///
- public BoolFeedback OnFeedback { get; private set; }
-
- public BoolFeedback IsWarmingUpFeedback { get; private set; }
- public BoolFeedback IsCoolingDownFeedback { get; private set; }
-
- public IOccupancyStatusProvider RoomOccupancy { get; private set; }
-
- public bool OccupancyStatusProviderIsRemote { get; private set; }
-
- protected abstract Func IsWarmingFeedbackFunc { get; }
- protected abstract Func IsCoolingFeedbackFunc { get; }
-
- ///
- /// Timer used for informing the UIs of a shutdown
- ///
- public SecondsCountdownTimer ShutdownPromptTimer { get; private set; }
-
- ///
- ///
- ///
- public int ShutdownPromptSeconds { get; set; }
- public int ShutdownVacancySeconds { get; set; }
- public eShutdownType ShutdownType { get; private set; }
-
- public PepperDash.Essentials.Room.EssentialsRoomEmergencyBase Emergency { get; set; }
-
- public PepperDash.Essentials.Devices.Common.Microphones.MicrophonePrivacyController MicrophonePrivacy { get; set; }
-
- public string LogoUrl { get; set; }
-
- protected SecondsCountdownTimer RoomVacancyShutdownTimer { get; private set; }
-
- public eVacancyMode VacancyMode { get; private set; }
-
- ///
- /// Seconds after vacancy prompt is displayed until shutdown
- ///
- protected int RoomVacancyShutdownSeconds;
-
- ///
- /// Seconds after vacancy detected until prompt is displayed
- ///
- protected int RoomVacancyShutdownPromptSeconds;
-
- ///
- ///
- ///
- protected abstract Func OnFeedbackFunc { get; }
-
- protected Dictionary SavedVolumeLevels = new Dictionary();
-
- ///
- /// When volume control devices change, should we zero the one that we are leaving?
- ///
- public bool ZeroVolumeWhenSwtichingVolumeDevices { get; private set; }
-
- ///
- ///
- ///
- ///
- ///
- public EssentialsRoomBase(string key, string name) : base(key, name)
- {
- // Setup the ShutdownPromptTimer
- ShutdownPromptTimer = new SecondsCountdownTimer(Key + "-offTimer");
- ShutdownPromptTimer.IsRunningFeedback.OutputChange += (o, a) =>
- {
- if (!ShutdownPromptTimer.IsRunningFeedback.BoolValue)
- ShutdownType = eShutdownType.None;
- };
- ShutdownPromptTimer.HasFinished += (o, a) => Shutdown(); // Shutdown is triggered
-
- ShutdownPromptSeconds = 60;
- ShutdownVacancySeconds = 120;
- ShutdownType = eShutdownType.None;
-
- RoomVacancyShutdownTimer = new SecondsCountdownTimer(Key + "-vacancyOffTimer");
- //RoomVacancyShutdownTimer.IsRunningFeedback.OutputChange += (o, a) =>
- //{
- // if (!RoomVacancyShutdownTimer.IsRunningFeedback.BoolValue)
- // ShutdownType = ShutdownType.Vacancy;
- //};
- RoomVacancyShutdownTimer.HasFinished += new EventHandler(RoomVacancyShutdownPromptTimer_HasFinished); // Shutdown is triggered
-
- RoomVacancyShutdownPromptSeconds = 1500; // 25 min to prompt warning
- RoomVacancyShutdownSeconds = 240; // 4 min after prompt will trigger shutdown prompt
- VacancyMode = eVacancyMode.None;
-
- OnFeedback = new BoolFeedback(OnFeedbackFunc);
-
- IsWarmingUpFeedback = new BoolFeedback(IsWarmingFeedbackFunc);
- IsCoolingDownFeedback = new BoolFeedback(IsCoolingFeedbackFunc);
- }
-
- void RoomVacancyShutdownPromptTimer_HasFinished(object sender, EventArgs e)
- {
- switch (VacancyMode)
- {
- case eVacancyMode.None:
- StartRoomVacancyTimer(eVacancyMode.InInitialVacancy);
- break;
- case eVacancyMode.InInitialVacancy:
- StartRoomVacancyTimer(eVacancyMode.InShutdownWarning);
- break;
- case eVacancyMode.InShutdownWarning:
- {
- StartShutdown(eShutdownType.Vacancy);
- Debug.Console(0, this, "Shutting Down due to vacancy.");
- break;
- }
- default:
- break;
- }
- }
-
- ///
- ///
- ///
- ///
- public void StartShutdown(eShutdownType type)
- {
- // Check for shutdowns running. Manual should override other shutdowns
-
- if (type == eShutdownType.Manual)
- ShutdownPromptTimer.SecondsToCount = ShutdownPromptSeconds;
- else if (type == eShutdownType.Vacancy)
- ShutdownPromptTimer.SecondsToCount = ShutdownVacancySeconds;
- ShutdownType = type;
- ShutdownPromptTimer.Start();
- }
-
- public void StartRoomVacancyTimer(eVacancyMode mode)
- {
- if (mode == eVacancyMode.None)
- RoomVacancyShutdownTimer.SecondsToCount = RoomVacancyShutdownPromptSeconds;
- else if (mode == eVacancyMode.InInitialVacancy)
- RoomVacancyShutdownTimer.SecondsToCount = RoomVacancyShutdownSeconds;
- VacancyMode = mode;
- RoomVacancyShutdownTimer.Start();
-
- Debug.Console(0, this, "Vacancy Timer Started.");
- }
-
- ///
- /// Resets the vacancy mode and shutsdwon the room
- ///
- public void Shutdown()
- {
- VacancyMode = eVacancyMode.None;
- EndShutdown();
- }
-
- ///
- /// This method is for the derived class to define it's specific shutdown
- /// requirements but should not be called directly. It is called by Shutdown()
- ///
- protected abstract void EndShutdown();
-
-
- ///
- /// Override this to implement a default volume level(s) method
- ///
- public abstract void SetDefaultLevels();
-
- ///
- /// Sets the object to be used as the IOccupancyStatusProvider for the room. Can be an Occupancy Aggregator or a specific device
- ///
- ///
- public void SetRoomOccupancy(IOccupancyStatusProvider statusProvider, int timeoutMinutes)
- {
- if (statusProvider == null)
- {
- Debug.Console(0, this, "ERROR: Occupancy sensor device is null");
- return;
- }
-
- // If status provider is fusion, set flag to remote
- if (statusProvider is PepperDash.Essentials.Fusion.EssentialsHuddleSpaceFusionSystemControllerBase)
- OccupancyStatusProviderIsRemote = true;
-
- if(timeoutMinutes > 0)
- RoomVacancyShutdownSeconds = timeoutMinutes * 60;
-
- RoomOccupancy = statusProvider;
-
- RoomOccupancy.RoomIsOccupiedFeedback.OutputChange += RoomIsOccupiedFeedback_OutputChange;
- }
-
- void RoomIsOccupiedFeedback_OutputChange(object sender, EventArgs e)
- {
- if (RoomOccupancy.RoomIsOccupiedFeedback.BoolValue == false)
- {
- Debug.Console(1, this, "Notice: Vacancy Detected");
- // Trigger the timer when the room is vacant
- StartRoomVacancyTimer(eVacancyMode.InInitialVacancy);
- }
- else
- {
- Debug.Console(1, this, "Notice: Occupancy Detected");
- // Reset the timer when the room is occupied
-
- RoomVacancyShutdownTimer.Cancel();
- }
- }
-
- //void SwapVolumeDevices(IBasicVolumeControls currentDevice, IBasicVolumeControls newDevice)
- //{
-
- //}
-
- ///
- /// Executes when RoomVacancyShutdownTimer expires. Used to trigger specific room actions as needed. Must nullify the timer object when executed
- ///
- ///
- public abstract void RoomVacatedForTimeoutPeriod(object o);
- }
-
- ///
- /// To describe the various ways a room may be shutting down
- ///
- public enum eShutdownType
- {
- None = 0,
- External,
- Manual,
- Vacancy
- }
-
- public enum eVacancyMode
- {
- None = 0,
- InInitialVacancy,
- InShutdownWarning
- }
-
- ///
- ///
- ///
- public enum eWarmingCoolingMode
- {
- None,
- Warming,
- Cooling
- }
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Crestron.SimplSharp;
+using Crestron.SimplSharp.Scheduler;
+
+using PepperDash.Core;
+using PepperDash.Essentials.Core;
+using PepperDash.Essentials.Devices.Common.Occupancy;
+
+namespace PepperDash.Essentials
+{
+ ///
+ ///
+ ///
+ public abstract class EssentialsRoomBase : Device
+ {
+ ///
+ ///
+ ///
+ public BoolFeedback OnFeedback { get; private set; }
+
+ ///
+ /// Fires when the RoomOccupancy object is set
+ ///
+ public event EventHandler RoomOccupancyIsSet;
+
+ public BoolFeedback IsWarmingUpFeedback { get; private set; }
+ public BoolFeedback IsCoolingDownFeedback { get; private set; }
+
+ public IOccupancyStatusProvider RoomOccupancy { get; private set; }
+
+ public bool OccupancyStatusProviderIsRemote { get; private set; }
+
+ protected abstract Func IsWarmingFeedbackFunc { get; }
+ protected abstract Func IsCoolingFeedbackFunc { get; }
+
+ ///
+ /// Timer used for informing the UIs of a shutdown
+ ///
+ public SecondsCountdownTimer ShutdownPromptTimer { get; private set; }
+
+ ///
+ ///
+ ///
+ public int ShutdownPromptSeconds { get; set; }
+ public int ShutdownVacancySeconds { get; set; }
+ public eShutdownType ShutdownType { get; private set; }
+
+ public PepperDash.Essentials.Room.EssentialsRoomEmergencyBase Emergency { get; set; }
+
+ public PepperDash.Essentials.Devices.Common.Microphones.MicrophonePrivacyController MicrophonePrivacy { get; set; }
+
+ public string LogoUrl { get; set; }
+
+ protected SecondsCountdownTimer RoomVacancyShutdownTimer { get; private set; }
+
+ public eVacancyMode VacancyMode { get; private set; }
+
+ ///
+ /// Seconds after vacancy prompt is displayed until shutdown
+ ///
+ protected int RoomVacancyShutdownSeconds;
+
+ ///
+ /// Seconds after vacancy detected until prompt is displayed
+ ///
+ protected int RoomVacancyShutdownPromptSeconds;
+
+ ///
+ ///
+ ///
+ protected abstract Func OnFeedbackFunc { get; }
+
+ protected Dictionary SavedVolumeLevels = new Dictionary();
+
+ ///
+ /// When volume control devices change, should we zero the one that we are leaving?
+ ///
+ public bool ZeroVolumeWhenSwtichingVolumeDevices { get; private set; }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ public EssentialsRoomBase(string key, string name) : base(key, name)
+ {
+ // Setup the ShutdownPromptTimer
+ ShutdownPromptTimer = new SecondsCountdownTimer(Key + "-offTimer");
+ ShutdownPromptTimer.IsRunningFeedback.OutputChange += (o, a) =>
+ {
+ if (!ShutdownPromptTimer.IsRunningFeedback.BoolValue)
+ ShutdownType = eShutdownType.None;
+ };
+ ShutdownPromptTimer.HasFinished += (o, a) => Shutdown(); // Shutdown is triggered
+
+ ShutdownPromptSeconds = 60;
+ ShutdownVacancySeconds = 120;
+ ShutdownType = eShutdownType.None;
+
+ RoomVacancyShutdownTimer = new SecondsCountdownTimer(Key + "-vacancyOffTimer");
+ //RoomVacancyShutdownTimer.IsRunningFeedback.OutputChange += (o, a) =>
+ //{
+ // if (!RoomVacancyShutdownTimer.IsRunningFeedback.BoolValue)
+ // ShutdownType = ShutdownType.Vacancy;
+ //};
+ RoomVacancyShutdownTimer.HasFinished += new EventHandler(RoomVacancyShutdownPromptTimer_HasFinished); // Shutdown is triggered
+
+ RoomVacancyShutdownPromptSeconds = 1500; // 25 min to prompt warning
+ RoomVacancyShutdownSeconds = 240; // 4 min after prompt will trigger shutdown prompt
+ VacancyMode = eVacancyMode.None;
+
+ OnFeedback = new BoolFeedback(OnFeedbackFunc);
+
+ IsWarmingUpFeedback = new BoolFeedback(IsWarmingFeedbackFunc);
+ IsCoolingDownFeedback = new BoolFeedback(IsCoolingFeedbackFunc);
+
+ AddPostActivationAction(() =>
+ {
+ if (RoomOccupancy != null)
+ OnRoomOccupancyIsSet();
+ });
+ }
+
+ void RoomVacancyShutdownPromptTimer_HasFinished(object sender, EventArgs e)
+ {
+ switch (VacancyMode)
+ {
+ case eVacancyMode.None:
+ StartRoomVacancyTimer(eVacancyMode.InInitialVacancy);
+ break;
+ case eVacancyMode.InInitialVacancy:
+ StartRoomVacancyTimer(eVacancyMode.InShutdownWarning);
+ break;
+ case eVacancyMode.InShutdownWarning:
+ {
+ StartShutdown(eShutdownType.Vacancy);
+ Debug.Console(0, this, "Shutting Down due to vacancy.");
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ ///
+ ///
+ ///
+ ///
+ public void StartShutdown(eShutdownType type)
+ {
+ // Check for shutdowns running. Manual should override other shutdowns
+
+ if (type == eShutdownType.Manual)
+ ShutdownPromptTimer.SecondsToCount = ShutdownPromptSeconds;
+ else if (type == eShutdownType.Vacancy)
+ ShutdownPromptTimer.SecondsToCount = ShutdownVacancySeconds;
+ ShutdownType = type;
+ ShutdownPromptTimer.Start();
+ }
+
+ public void StartRoomVacancyTimer(eVacancyMode mode)
+ {
+ if (mode == eVacancyMode.None)
+ RoomVacancyShutdownTimer.SecondsToCount = RoomVacancyShutdownPromptSeconds;
+ else if (mode == eVacancyMode.InInitialVacancy)
+ RoomVacancyShutdownTimer.SecondsToCount = RoomVacancyShutdownSeconds;
+ VacancyMode = mode;
+ RoomVacancyShutdownTimer.Start();
+
+ Debug.Console(0, this, "Vacancy Timer Started.");
+ }
+
+ ///
+ /// Resets the vacancy mode and shutsdwon the room
+ ///
+ public void Shutdown()
+ {
+ VacancyMode = eVacancyMode.None;
+ EndShutdown();
+ }
+
+ ///
+ /// This method is for the derived class to define it's specific shutdown
+ /// requirements but should not be called directly. It is called by Shutdown()
+ ///
+ protected abstract void EndShutdown();
+
+
+ ///
+ /// Override this to implement a default volume level(s) method
+ ///
+ public abstract void SetDefaultLevels();
+
+ ///
+ /// Sets the object to be used as the IOccupancyStatusProvider for the room. Can be an Occupancy Aggregator or a specific device
+ ///
+ ///
+ public void SetRoomOccupancy(IOccupancyStatusProvider statusProvider, int timeoutMinutes)
+ {
+ if (statusProvider == null)
+ {
+ Debug.Console(0, this, "ERROR: Occupancy sensor device is null");
+ return;
+ }
+
+ // If status provider is fusion, set flag to remote
+ if (statusProvider is PepperDash.Essentials.Fusion.EssentialsHuddleSpaceFusionSystemControllerBase)
+ OccupancyStatusProviderIsRemote = true;
+
+ if(timeoutMinutes > 0)
+ RoomVacancyShutdownSeconds = timeoutMinutes * 60;
+
+ RoomOccupancy = statusProvider;
+
+ OnRoomOccupancyIsSet();
+
+ RoomOccupancy.RoomIsOccupiedFeedback.OutputChange -= RoomIsOccupiedFeedback_OutputChange;
+ RoomOccupancy.RoomIsOccupiedFeedback.OutputChange += RoomIsOccupiedFeedback_OutputChange;
+
+ Debug.Console(0, this, "Room Occupancy set to device: '{0}'", (statusProvider as Device).Key);
+ }
+
+ void OnRoomOccupancyIsSet()
+ {
+ var handler = RoomOccupancyIsSet;
+ if (handler != null)
+ handler(this, new EventArgs());
+ }
+
+ ///
+ /// To allow base class to power room on to last source
+ ///
+ public abstract void PowerOnToDefaultOrLastSource();
+
+ ///
+ /// To allow base class to power room on to default source
+ ///
+ ///
+ public abstract bool RunDefaultPresentRoute();
+
+ void RoomIsOccupiedFeedback_OutputChange(object sender, EventArgs e)
+ {
+ if (RoomOccupancy.RoomIsOccupiedFeedback.BoolValue == false)
+ {
+ Debug.Console(1, this, "Notice: Vacancy Detected");
+ // Trigger the timer when the room is vacant
+ StartRoomVacancyTimer(eVacancyMode.InInitialVacancy);
+ }
+ else
+ {
+ Debug.Console(1, this, "Notice: Occupancy Detected");
+ // Reset the timer when the room is occupied
+ RoomVacancyShutdownTimer.Cancel();
+ }
+ }
+
+ ///
+ /// Executes when RoomVacancyShutdownTimer expires. Used to trigger specific room actions as needed. Must nullify the timer object when executed
+ ///
+ ///
+ public abstract void RoomVacatedForTimeoutPeriod(object o);
+ }
+
+ ///
+ /// To describe the various ways a room may be shutting down
+ ///
+ public enum eShutdownType
+ {
+ None = 0,
+ External,
+ Manual,
+ Vacancy
+ }
+
+ public enum eVacancyMode
+ {
+ None = 0,
+ InInitialVacancy,
+ InShutdownWarning
+ }
+
+ ///
+ ///
+ ///
+ public enum eWarmingCoolingMode
+ {
+ None,
+ Warming,
+ Cooling
+ }
}
\ No newline at end of file
diff --git a/PepperDashEssentials/UIDrivers/Essentials/EssentialsPresentationPanelAvFunctionsDriver.cs b/PepperDashEssentials/UIDrivers/Essentials/EssentialsPresentationPanelAvFunctionsDriver.cs
index e7e13020..edda29e0 100644
--- a/PepperDashEssentials/UIDrivers/Essentials/EssentialsPresentationPanelAvFunctionsDriver.cs
+++ b/PepperDashEssentials/UIDrivers/Essentials/EssentialsPresentationPanelAvFunctionsDriver.cs
@@ -1,1048 +1,1048 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using Crestron.SimplSharp;
-using Crestron.SimplSharpPro;
+//using System;
+//using System.Collections.Generic;
+//using System.Linq;
+//using Crestron.SimplSharp;
+//using Crestron.SimplSharpPro;
-using PepperDash.Core;
-using PepperDash.Essentials.Core;
-using PepperDash.Essentials.Core.Config;
-using PepperDash.Essentials.Core.SmartObjects;
-using PepperDash.Essentials.Core.PageManagers;
+//using PepperDash.Core;
+//using PepperDash.Essentials.Core;
+//using PepperDash.Essentials.Core.Config;
+//using PepperDash.Essentials.Core.SmartObjects;
+//using PepperDash.Essentials.Core.PageManagers;
-namespace PepperDash.Essentials
-{
- ///
- ///
- ///
- public class EssentialsPresentationPanelAvFunctionsDriver : PanelDriverBase
- {
- ///
- /// Smart Object 3200
- ///
- SubpageReferenceList SourcesSrl;
+//namespace PepperDash.Essentials
+//{
+// ///
+// ///
+// ///
+// public class EssentialsPresentationPanelAvFunctionsDriver : PanelDriverBase
+// {
+// ///
+// /// Smart Object 3200
+// ///
+// SubpageReferenceList SourcesSrl;
- ///
- /// For tracking feedback on last selected
- ///
- BoolInputSig LastSelectedSourceSig;
+// ///
+// /// For tracking feedback on last selected
+// ///
+// BoolInputSig LastSelectedSourceSig;
- ///
- /// The source that has been selected and is awaiting assignment to a display
- ///
- SourceListItem PendingSource;
+// ///
+// /// The source that has been selected and is awaiting assignment to a display
+// ///
+// SourceListItem PendingSource;
- bool IsSharingModeAdvanced;
+// bool IsSharingModeAdvanced;
- CrestronTouchpanelPropertiesConfig Config;
+// CrestronTouchpanelPropertiesConfig Config;
- public enum UiDisplayMode
- {
- PresentationMode, AudioSetup
- }
+// public enum UiDisplayMode
+// {
+// PresentationMode, AudioSetup
+// }
- ///
- /// Whether volume ramping from this panel will show the volume
- /// gauge popup.
- ///
- public bool ShowVolumeGauge { get; set; }
+// ///
+// /// Whether volume ramping from this panel will show the volume
+// /// gauge popup.
+// ///
+// public bool ShowVolumeGauge { get; set; }
- ///
- /// The amount of time that the volume buttons stays on screen, in ms
- ///
- public uint VolumeButtonPopupTimeout
- {
- get { return VolumeButtonsPopupFeedback.TimeoutMs; }
- set { VolumeButtonsPopupFeedback.TimeoutMs = value; }
- }
+// ///
+// /// The amount of time that the volume buttons stays on screen, in ms
+// ///
+// public uint VolumeButtonPopupTimeout
+// {
+// get { return VolumeButtonsPopupFeedback.TimeoutMs; }
+// set { VolumeButtonsPopupFeedback.TimeoutMs = value; }
+// }
- ///
- /// The amount of time that the volume gauge stays on screen, in ms
- ///
- public uint VolumeGaugePopupTimeout
- {
- get { return VolumeGaugeFeedback.TimeoutMs; }
- set { VolumeGaugeFeedback.TimeoutMs = value; }
- }
-
- ///
- ///
- ///
- public uint PowerOffTimeout { get; set; }
-
- ///
- ///
- ///
- public string DefaultRoomKey
- {
- get { return _DefaultRoomKey; }
- set
- {
- _DefaultRoomKey = value;
- CurrentRoom = DeviceManager.GetDeviceForKey(value) as EssentialsPresentationRoom;
- }
- }
- string _DefaultRoomKey;
-
- ///
- ///
- ///
- public EssentialsPresentationRoom CurrentRoom
- {
- get { return _CurrentRoom; }
- set
- {
- SetCurrentRoom(value);
- }
- }
- EssentialsPresentationRoom _CurrentRoom;
-
- ///
- /// For hitting feedback
- ///
- BoolInputSig ShareButtonSig;
- BoolInputSig EndMeetingButtonSig;
-
- ///
- /// Controls the extended period that the volume gauge shows on-screen,
- /// as triggered by Volume up/down operations
- ///
- BoolFeedbackPulseExtender VolumeGaugeFeedback;
-
- ///
- /// Controls the period that the volume buttons show on non-hard-button
- /// interfaces
- ///
- BoolFeedbackPulseExtender VolumeButtonsPopupFeedback;
-
- ///
- /// The parent driver for this
- ///
- PanelDriverBase Parent;
-
- /////
- ///// Driver that manages advanced sharing features
- /////
- //DualDisplaySimpleOrAdvancedRouting DualDisplayUiDriver;
-
- ///
- /// All children attached to this driver. For hiding and showing as a group.
- ///
- List ChildDrivers = new List();
-
- List CurrentDisplayModeSigsInUse = new List();
-
- ///
- /// Smart Object 15022
- ///
- SubpageReferenceList ActivityFooterSrl;
-
- ///
- /// Tracks which audio page group the UI is in
- ///
- UiDisplayMode CurrentDisplayMode;
-
- ///
- /// The AV page mangagers that have been used, to keep them alive for later
- ///
- Dictionary