diff --git a/PepperDashEssentials/AppServer/CotijaSystemController.cs b/PepperDashEssentials/AppServer/CotijaSystemController.cs
index 1785163e..23af9779 100644
--- a/PepperDashEssentials/AppServer/CotijaSystemController.cs
+++ b/PepperDashEssentials/AppServer/CotijaSystemController.cs
@@ -1,733 +1,845 @@
-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 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.Core.Config;
-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);
-
- 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);
- }
- }
-
- ///
- /// 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(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");
- 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);
-
- 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);
- }
-
- }
-
- ///
- /// 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)
- {
- SendMessageToServer(JObject.FromObject(new
- {
- type = "/system/heartbeatAck"
- }));
-
- 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");
- }
- }
+using PepperDash.Essentials.Core.Config;
+using PepperDash.Essentials.Room.Cotija;
+
+namespace PepperDash.Essentials
+{
+ public class CotijaSystemController : Device
+ {
+ WebSocketClient WSClient;
+
+ //bool LinkUp;
+
+ ///
+ /// 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;
+
+ DateTime LastAckMessage;
+
+ 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;
+
+ SystemUuid = ConfigReader.ConfigObject.SystemUuid;
+
+ 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);
+
+ CrestronConsole.AddNewConsoleCommand(PrintActionDictionaryPaths, "mobileshowactionpaths",
+ "Prints the paths in the Action Dictionary", ConsoleAccessLevelEnum.AccessOperator);
+ CrestronConsole.AddNewConsoleCommand(s => ConnectWebsocketClient(), "mobileconnect",
+ "Forces connect of websocket", ConsoleAccessLevelEnum.AccessOperator);
+ CrestronConsole.AddNewConsoleCommand(s => CleanUpWebsocketClient(), "mobiledisco",
+ "Disconnects websocket", ConsoleAccessLevelEnum.AccessOperator);
+
+ CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
+ CrestronEnvironment.EthernetEventHandler += new EthernetEventHandler(CrestronEnvironment_EthernetEventHandler);
+
+ }
+
+ ///
+ /// If config rooms is empty or null then go
+ ///
+ ///
+ public override bool CustomActivate()
+ {
+ if (ConfigReader.ConfigObject.Rooms == null || ConfigReader.ConfigObject.Rooms.Count == 0)
+ {
+ Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Config contains no rooms. Registering with Server.");
+ RegisterSystemToServer();
+ }
+
+ return base.CustomActivate();
+ }
+
+ ///
+ ///
+ ///
+ ///
+ void CrestronEnvironment_EthernetEventHandler(EthernetEventArgs args)
+ {
+ Debug.Console(1, this, Debug.ErrorLogLevel.Warning, "Ethernet status change, port {0}: {1}",
+ args.EthernetAdapter, args.EthernetEventType);
+
+ if (args.EthernetEventType == eEthernetEventType.LinkDown && WSClient != null && args.EthernetAdapter == WSClient.EthernetAdapter)
+ {
+ CleanUpWebsocketClient();
+ }
+ }
+
+ ///
+ /// Sends message to server to indicate the system is shutting down
+ ///
+ ///
+ void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType)
+ {
+ if (programEventType == eProgramStatusEventType.Stopping && WSClient.Connected)
+ {
+ CleanUpWebsocketClient();
+ }
+ }
+
+ public void PrintActionDictionaryPaths(object o)
+ {
+ Debug.Console(0, this, "ActionDictionary Contents:");
+
+ foreach (var item in ActionDictionary)
+ {
+ Debug.Console(0, this, "{0}", item.Key);
+ }
+ }
+
+ ///
+ /// 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");
+ //SystemUuid = ConfigReader.ConfigObject.SystemUuid;
+ RegisterSystemToServer();
+ }
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ void bridge_ConfigurationIsReady(object sender, EventArgs e)
+ {
+ Debug.Console(1, this, "Bridge ready. Registering");
+ //SystemUuid = ConfigReader.ConfigObject.SystemUuid;
+ // send the configuration object to the server
+ RegisterSystemToServer();
+ }
+
+ ///
+ ///
+ ///
+ ///
+ void ReconnectToServerTimerCallback(object o)
+ {
+ RegisterSystemToServer();
+ }
+
+ ///
+ /// Verifies system connection with servers
+ ///
+ ///
+ void AuthorizeSystem(string code)
+ {
+ //SystemUuid = ConfigReader.ConfigObject.SystemUuid;
+
+ 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");
+ 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.");
+#warning This registration may need to wait for config ready. Maybe.
+ 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");
+ var secSinceLastAck = DateTime.Now - LastAckMessage;
+
+
+ CrestronConsole.ConsoleCommandResponse(@"Mobile Control Information:
+ Server address: {0}
+ System Name: {1}
+ System UUID: {2}
+ System User code: {3}
+ Connected?: {4}
+ Seconds Since Last Ack: {5}", url, name, SystemUuid,
+ code, conn, secSinceLastAck.Seconds);
+ }
+
+ ///
+ /// Registers the room with the server
+ ///
+ /// URL of the server, including the port number, if not 80. Format: "serverUrlOrIp:port"
+ void RegisterSystemToServer()
+ {
+ ConnectWebsocketClient();
+ }
+
+ ///
+ /// Connects the Websocket Client
+ ///
+ ///
+ void ConnectWebsocketClient()
+ {
+
+ Debug.Console(1, this, "Initializing Stream client to server.");
+
+ if (WSClient != null)
+ {
+ Debug.Console(1, this, "Cleaning up previous socket");
+ CleanUpWebsocketClient();
+ }
+
+ WSClient = new WebSocketClient();
+ WSClient.URL = string.Format("wss://{0}/system/join/{1}", Config.ServerUrl, this.SystemUuid);
+ WSClient.ConnectionCallBack = Websocket_ConnectCallback;
+ WSClient.ConnectAsync();
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ int Websocket_ConnectCallback(WebSocketClient.WEBSOCKET_RESULT_CODES code)
+ {
+ if (code == WebSocketClient.WEBSOCKET_RESULT_CODES.WEBSOCKET_CLIENT_SUCCESS)
+ {
+ StopServerReconnectTimer();
+ Debug.Console(1, this, "Websocket connected");
+ WSClient.DisconnectCallBack = Websocket_DisconnectCallback;
+ WSClient.SendCallBack = Websocket_SendCallback;
+ WSClient.ReceiveCallBack = Websocket_ReceiveCallback;
+ WSClient.ReceiveAsync();
+ SendMessageObjectToServer(new
+ {
+ type = "hello"
+ });
+ }
+ else
+ {
+ if (code == WebSocketClient.WEBSOCKET_RESULT_CODES.WEBSOCKET_CLIENT_HTTP_HANDSHAKE_TOKEN_ERROR)
+ {
+ // This is the case when app server is running behind a websever and app server is down
+ Debug.Console(1, this, Debug.ErrorLogLevel.Warning, "Web socket connection failed. Check that app server is running behind web server");
+ }
+ else if (code == WebSocketClient.WEBSOCKET_RESULT_CODES.WEBSOCKET_CLIENT_SOCKET_CONNECTION_FAILED)
+ {
+ // this will be the case when webserver is unreachable
+ Debug.Console(1, this, Debug.ErrorLogLevel.Warning, "Web socket connection failed");
+ }
+ else
+ {
+ Debug.Console(1, this, Debug.ErrorLogLevel.Warning, "Web socket connection failure: {0}", code);
+ }
+ StartServerReconnectTimer();
+ }
+
+ return 0;
+ }
+
+ ///
+ /// After a "hello" from the server, sends config and stuff
+ ///
+ void SendInitialMessage()
+ {
+ Debug.Console(1, this, "Sending initial join message");
+ 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);
+
+ var msg = new
+ {
+ type = "join",
+ content = new
+ {
+ config = confObject
+ }
+ };
+ SendMessageObjectToServer(msg);
+ }
+
+ ///
+ /// Sends any object type to server
+ ///
+ ///
+ public void SendMessageObjectToServer(object o)
+ {
+ SendMessageToServer(JObject.FromObject(o));
+ }
+
+ ///
+ /// 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 });
+
+ if (!message.Contains("/system/heartbeat"))
+ Debug.Console(1, this, "Message TX: {0}", message);
+ //else
+ // Debug.Console(1, this, "TX messages contains /system/heartbeat");
+
+ var messageBytes = System.Text.Encoding.UTF8.GetBytes(message);
+ var result = WSClient.Send(messageBytes, (uint)messageBytes.Length, WebSocketClient.WEBSOCKET_PACKET_TYPES.LWS_WS_OPCODE_07__TEXT_FRAME);
+ if (result != WebSocketClient.WEBSOCKET_RESULT_CODES.WEBSOCKET_CLIENT_SUCCESS)
+ {
+ Debug.Console(1, this, "Socket send result error: {0}", result);
+ }
+ }
+ else if (!WSClient.Connected)
+ {
+ Debug.Console(1, this, "Cannot send. Not connected {0}");
+ }
+ }
+
+ ///
+ /// Disconnects the SSE Client and stops the heartbeat timer
+ ///
+ ///
+ void CleanUpWebsocketClient()
+ {
+ Debug.Console(1, this, "Disconnecting websocket");
+ if (WSClient != null)
+ {
+ WSClient.SendCallBack = null;
+ WSClient.ReceiveCallBack = null;
+ WSClient.ConnectionCallBack = null;
+ WSClient.DisconnectCallBack = null;
+ if (WSClient.Connected)
+ {
+ WSClient.Disconnect();
+ }
+ WSClient = null;
+ }
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ void StartServerReconnectTimer()
+ {
+ StopServerReconnectTimer();
+ ServerReconnectTimer = new CTimer(ReconnectToServerTimerCallback, ServerReconnectInterval);
+ Debug.Console(1, this, "Reconnect Timer Started.");
+ }
+
+ ///
+ /// Does what it says
+ ///
+ void StopServerReconnectTimer()
+ {
+ if (ServerReconnectTimer != null)
+ {
+ ServerReconnectTimer.Stop();
+ ServerReconnectTimer = null;
+ }
+ }
+
+ ///
+ /// 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;
+ }
+ CleanUpWebsocketClient();
+ StartServerReconnectTimer();
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ void ResetOrStartHearbeatTimer()
+ {
+ if (ServerHeartbeatCheckTimer == null)
+ {
+ ServerHeartbeatCheckTimer = new CTimer(HeartbeatExpiredTimerCallback, null, ServerHeartbeatInterval, ServerHeartbeatInterval);
+ Debug.Console(1, this, "Heartbeat Timer Started.");
+ }
+ else
+ {
+ ServerHeartbeatCheckTimer.Reset(ServerHeartbeatInterval, ServerHeartbeatInterval);
+ }
+ }
+
+ ///
+ /// Waits two and goes again
+ ///
+ void ReconnectStreamClient()
+ {
+ new CTimer(o => ConnectWebsocketClient(), 2000);
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ int Websocket_DisconnectCallback(WebSocketClient.WEBSOCKET_RESULT_CODES code, object o)
+ {
+ Debug.Console(1, this, Debug.ErrorLogLevel.Warning, "Websocket disconnected with code: {0}", code);
+
+ if (ServerHeartbeatCheckTimer != null)
+ ServerHeartbeatCheckTimer.Stop();
+ // Start the reconnect timer
+ StartServerReconnectTimer();
+ return 0;
+ }
+
+
+ ///
+ /// Resets reconnect timer and updates usercode
+ ///
+ ///
+ void HandleHeartBeat(JToken content)
+ {
+ SendMessageToServer(JObject.FromObject(new
+ {
+ type = "/system/heartbeatAck"
+ }));
+
+ 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)
+ {
+ try
+ {
+ Debug.Console(0, this, "------ Begin HTTP Debug ---------------------------------------");
+ if (r != null)
+ {
+ Debug.Console(0, this, "HTTP Response URL: {0}", r.ResponseUrl != null ? r.ResponseUrl.ToString() : "NONE");
+ Debug.Console(0, this, "HTTP Response code: {0}", r.Code);
+ Debug.Console(0, this, "HTTP Response content: \r{0}", r.ContentString);
+ }
+ else
+ {
+ Debug.Console(0, this, "No HTTP response");
+ }
+ Debug.Console(0, this, "HTTP Response 'error' {0}", e);
+ Debug.Console(0, this, "------ End HTTP Debug -----------------------------------------");
+ }
+ catch (Exception ex)
+ {
+ Debug.Console(0, this, "HttpDebugError: {0}", ex);
+ }
+ }
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ int Websocket_ReceiveCallback(byte[] data, uint length, WebSocketClient.WEBSOCKET_PACKET_TYPES opcode,
+ WebSocketClient.WEBSOCKET_RESULT_CODES err)
+ {
+ if (opcode == WebSocketClient.WEBSOCKET_PACKET_TYPES.LWS_WS_OPCODE_07__TEXT_FRAME)
+ {
+ var rx = System.Text.Encoding.UTF8.GetString(data, 0, (int)length);
+ if (rx.Length > 0)
+ ParseStreamRx(rx);
+ WSClient.ReceiveAsync();
+ }
+
+ else if (opcode == WebSocketClient.WEBSOCKET_PACKET_TYPES.LWS_WS_OPCODE_07__CLOSE)
+ {
+ Debug.Console(1, this, "Websocket disconnect received from remote");
+ CleanUpWebsocketClient();
+ }
+ else
+ {
+ Debug.Console(1, this, "websocket rx opcode/err {0}/{1}", opcode, err);
+ WSClient.ReceiveAsync();
+ }
+ return 0;
+ }
+
+ ///
+ /// Callback to catch possible errors in sending via the websocket
+ ///
+ ///
+ ///
+ int Websocket_SendCallback(Crestron.SimplSharp.CrestronWebSocketClient.WebSocketClient.WEBSOCKET_RESULT_CODES result)
+ {
+ if(result != WebSocketClient.WEBSOCKET_RESULT_CODES.WEBSOCKET_CLIENT_SUCCESS)
+ Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "SendCallback questionable result: {0}", result);
+ return 1;
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ void ParseStreamRx(string message)
+ {
+ if(string.IsNullOrEmpty(message))
+ return;
+
+ if (!message.Contains("/system/heartbeat"))
+ Debug.Console(1, this, "Message RX: {0}", message);
+ else
+ {
+ LastAckMessage = DateTime.Now;
+ //Debug.Console(1, this, "RX message contains /system/heartbeat");
+ }
+
+ try
+ {
+ var messageObj = JObject.Parse(message);
+
+ var type = messageObj["type"].Value();
+
+ if (type == "hello")
+ {
+ SendInitialMessage();
+ ResetOrStartHearbeatTimer();
+ }
+ else if (type == "/system/heartbeat")
+ {
+ HandleHeartBeat(messageObj["content"]);
+ }
+ else if (type == "close")
+ {
+ Debug.Console(1, this, "Received close message from server.");
+ // DisconnectWebsocketClient();
+
+ if (ServerHeartbeatCheckTimer != null)
+ ServerHeartbeatCheckTimer.Stop();
+ }
+ 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/AppServer/Messengers/AtcDdvc01Messenger.cs b/PepperDashEssentials/AppServer/Messengers/AtcDdvc01Messenger.cs
new file mode 100644
index 00000000..8e28d995
--- /dev/null
+++ b/PepperDashEssentials/AppServer/Messengers/AtcDdvc01Messenger.cs
@@ -0,0 +1,187 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Crestron.SimplSharp;
+using Crestron.SimplSharpPro.DeviceSupport;
+using Crestron.SimplSharpPro.EthernetCommunication;
+
+using PepperDash.Core;
+using PepperDash.Essentials.Core;
+using PepperDash.Essentials.Devices.Common.Codec;
+
+namespace PepperDash.Essentials.AppServer.Messengers
+{
+ public class Ddvc01AtcMessenger : MessengerBase
+ {
+ BasicTriList EISC;
+
+ const uint BDialHangup = 221;
+ const uint BIncomingAnswer = 251;
+ const uint BIncomingReject = 252;
+ const uint BSpeedDial1 = 241;
+ const uint BSpeedDial2 = 242;
+ const uint BSpeedDial3 = 243;
+ const uint BSpeedDial4 = 244;
+
+ ///
+ /// 201
+ ///
+ const uint SCurrentDialString = 201;
+ ///
+ /// 211
+ ///
+ const uint SCurrentCallNumber = 211;
+ ///
+ /// 212
+ ///
+ const uint SCurrentCallName = 212;
+ ///
+ /// 221
+ ///
+ const uint SHookState = 221;
+ ///
+ /// 222
+ ///
+ const uint SCallDirection = 222;
+
+ ///
+ ///
+ ///
+ Dictionary DTMFMap = new Dictionary
+ {
+ { "1", 201 },
+ { "2", 202 },
+ { "3", 203 },
+ { "4", 204 },
+ { "5", 205 },
+ { "6", 206 },
+ { "7", 207 },
+ { "8", 208 },
+ { "9", 209 },
+ { "0", 210 },
+ { "*", 211 },
+ { "#", 212 },
+ };
+
+ CodecActiveCallItem CurrentCallItem;
+
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ public Ddvc01AtcMessenger(string key, BasicTriList eisc, string messagePath)
+ : base(key, messagePath)
+ {
+ EISC = eisc;
+
+ CurrentCallItem = new CodecActiveCallItem();
+ CurrentCallItem.Type = eCodecCallType.Audio;
+ CurrentCallItem.Id = "-audio-";
+ }
+
+ ///
+ ///
+ ///
+ void SendFullStatus()
+ {
+
+
+ this.PostStatusMessage(new
+ {
+ calls = GetCurrentCallList(),
+ currentCallString = EISC.GetString(SCurrentCallNumber),
+ currentDialString = EISC.GetString(SCurrentDialString),
+ isInCall = EISC.GetString(SHookState) == "Connected"
+ });
+ }
+
+ ///
+ ///
+ ///
+ ///
+ protected override void CustomRegisterWithAppServer(CotijaSystemController appServerController)
+ {
+ //EISC.SetStringSigAction(SCurrentDialString, s => PostStatusMessage(new { currentDialString = s }));
+
+ EISC.SetStringSigAction(SHookState, s =>
+ {
+ CurrentCallItem.Status = (eCodecCallStatus)Enum.Parse(typeof(eCodecCallStatus), s, true);
+ //GetCurrentCallList();
+ SendCallsList();
+ });
+
+ EISC.SetStringSigAction(SCurrentCallNumber, s =>
+ {
+ CurrentCallItem.Number = s;
+ SendCallsList();
+ });
+
+ EISC.SetStringSigAction(SCurrentCallName, s =>
+ {
+ CurrentCallItem.Name = s;
+ SendCallsList();
+ });
+
+ EISC.SetStringSigAction(SCallDirection, s =>
+ {
+ CurrentCallItem.Direction = (eCodecCallDirection)Enum.Parse(typeof(eCodecCallDirection), s, true);
+ SendCallsList();
+ });
+
+ // Add press and holds using helper
+ Action addPHAction = (s, u) =>
+ AppServerController.AddAction(MessagePath + s, new PressAndHoldAction(b => EISC.SetBool(u, b)));
+
+ // Add straight pulse calls
+ Action addAction = (s, u) =>
+ AppServerController.AddAction(MessagePath + s, new Action(() => EISC.PulseBool(u, 100)));
+ addAction("/endCallById", BDialHangup);
+ addAction("/acceptById", BIncomingAnswer);
+ addAction("/rejectById", BIncomingReject);
+ addAction("/speedDial1", BSpeedDial1);
+ addAction("/speedDial2", BSpeedDial2);
+ addAction("/speedDial3", BSpeedDial3);
+ addAction("/speedDial4", BSpeedDial4);
+
+ // Get status
+ AppServerController.AddAction(MessagePath + "/fullStatus", new Action(SendFullStatus));
+ // Dial on string
+ AppServerController.AddAction(MessagePath + "/dial", new Action(s => EISC.SetString(SCurrentDialString, s)));
+ // Pulse DTMF
+ AppServerController.AddAction(MessagePath + "/dtmf", new Action(s =>
+ {
+ if (DTMFMap.ContainsKey(s))
+ {
+ EISC.PulseBool(DTMFMap[s], 100);
+ }
+ }));
+ }
+
+ void SendCallsList()
+ {
+ PostStatusMessage(new
+ {
+ calls = GetCurrentCallList(),
+ });
+ }
+
+ ///
+ /// Turns the
+ ///
+ ///
+ List GetCurrentCallList()
+ {
+ if (CurrentCallItem.Status == eCodecCallStatus.Disconnected)
+ {
+ return new List();
+ }
+ else
+ {
+ return new List() { CurrentCallItem };
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/PepperDashEssentials/AppServer/Messengers/AudioCodecBaseMessenger.cs b/PepperDashEssentials/AppServer/Messengers/AudioCodecBaseMessenger.cs
new file mode 100644
index 00000000..b37b80da
--- /dev/null
+++ b/PepperDashEssentials/AppServer/Messengers/AudioCodecBaseMessenger.cs
@@ -0,0 +1,102 @@
+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.AudioCodec;
+
+namespace PepperDash.Essentials.AppServer.Messengers
+{
+ ///
+ /// Provides a messaging bridge for an AudioCodecBase device
+ ///
+ public class AudioCodecBaseMessenger : MessengerBase
+ {
+ ///
+ /// Device being bridged
+ ///
+ public AudioCodecBase Codec { get; set; }
+
+ ///
+ /// Constuctor
+ ///
+ ///
+ ///
+ ///
+ public AudioCodecBaseMessenger(string key, AudioCodecBase codec, string messagePath)
+ : base(key, messagePath)
+ {
+ if (codec == null)
+ throw new ArgumentNullException("codec");
+
+ Codec = codec;
+ codec.CallStatusChange += new EventHandler(codec_CallStatusChange);
+
+ }
+
+ protected override void CustomRegisterWithAppServer(CotijaSystemController appServerController)
+ {
+ appServerController.AddAction(MessagePath + "/fullStatus", new Action(SendAtcFullMessageObject));
+ appServerController.AddAction(MessagePath + "/dial", new Action(s => Codec.Dial(s)));
+ appServerController.AddAction(MessagePath + "/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);
+ }));
+ }
+
+ ///
+ /// Helper to grab a call with string ID
+ ///
+ ///
+ ///
+ CodecActiveCallItem GetCallWithId(string id)
+ {
+ return Codec.ActiveCalls.FirstOrDefault(c => c.Id == id);
+ }
+
+ void codec_CallStatusChange(object sender, CodecCallStatusItemChangeEventArgs e)
+ {
+ SendAtcFullMessageObject();
+ }
+
+ ///
+ /// Helper method to build call status for vtc
+ ///
+ ///
+ void SendAtcFullMessageObject()
+ {
+
+ var info = Codec.CodecInfo;
+ PostStatusMessage(new
+ {
+ isInCall = Codec.IsInCall,
+ calls = Codec.ActiveCalls,
+ info = new
+ {
+ phoneNumber = info.PhoneNumber
+ }
+ });
+ }
+ }
+}
\ No newline at end of file
diff --git a/PepperDashEssentials/AppServer/Messengers/MessengerBase.cs b/PepperDashEssentials/AppServer/Messengers/MessengerBase.cs
new file mode 100644
index 00000000..f9833afb
--- /dev/null
+++ b/PepperDashEssentials/AppServer/Messengers/MessengerBase.cs
@@ -0,0 +1,81 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Crestron.SimplSharp;
+
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+
+using PepperDash.Core;
+
+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 abstract class MessengerBase : IKeyed
+ {
+ public string Key { get; private set; }
+
+ ///
+ ///
+ ///
+ public CotijaSystemController AppServerController { get; private set; }
+
+ public string MessagePath { get; private set; }
+
+ ///
+ ///
+ ///
+ ///
+ public MessengerBase(string key, string messagePath)
+ {
+ Key = key;
+
+ if (string.IsNullOrEmpty(messagePath))
+ throw new ArgumentException("messagePath must not be empty or null");
+
+ MessagePath = messagePath;
+ }
+
+
+ ///
+ /// Registers this messenger with appserver controller
+ ///
+ ///
+ public void RegisterWithAppServer(CotijaSystemController appServerController)
+ {
+ if (appServerController == null)
+ throw new ArgumentNullException("appServerController");
+
+ AppServerController = appServerController;
+ CustomRegisterWithAppServer(AppServerController);
+ }
+
+ ///
+ /// Implemented in extending classes. Wire up API calls and feedback here
+ ///
+ ///
+ abstract protected void CustomRegisterWithAppServer(CotijaSystemController appServerController);
+
+ ///
+ /// Helper for posting status message
+ ///
+ /// The contents of the content object
+ protected void PostStatusMessage(object contentObject)
+ {
+ if (AppServerController != null)
+ {
+ AppServerController.SendMessageToServer(JObject.FromObject(new
+ {
+ type = MessagePath,
+ content = contentObject
+ }));
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/PepperDashEssentials/AppServer/Messengers/SystemMonitorMessenger.cs b/PepperDashEssentials/AppServer/Messengers/SystemMonitorMessenger.cs
new file mode 100644
index 00000000..7b56ab29
--- /dev/null
+++ b/PepperDashEssentials/AppServer/Messengers/SystemMonitorMessenger.cs
@@ -0,0 +1,92 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Crestron.SimplSharp;
+
+using Newtonsoft.Json;
+
+using PepperDash.Core;
+using PepperDash.Essentials.Core.Monitoring;
+
+namespace PepperDash.Essentials.AppServer.Messengers
+{
+ public class SystemMonitorMessenger : MessengerBase
+ {
+ public SystemMonitorController SysMon { get; private set; }
+
+ public SystemMonitorMessenger(string key, SystemMonitorController sysMon, string messagePath)
+ : base(key, messagePath)
+ {
+ if (sysMon == null)
+ throw new ArgumentNullException("sysMon");
+
+ SysMon = sysMon;
+
+ SysMon.SystemMonitorPropertiesChanged += new EventHandler(SysMon_SystemMonitorPropertiesChanged);
+
+ foreach (var p in SysMon.ProgramStatusFeedbackCollection)
+ {
+ p.Value.ProgramInfoChanged += new EventHandler(ProgramInfoChanged);
+ }
+
+ CrestronConsole.AddNewConsoleCommand(s => SendFullStatusMessage(), "SendFullSysMonStatus", "Sends the full System Monitor Status", ConsoleAccessLevelEnum.AccessOperator);
+ }
+
+ ///
+ /// Posts the program information message
+ ///
+ ///
+ ///
+ void ProgramInfoChanged(object sender, ProgramInfoEventArgs e)
+ {
+ Debug.Console(1, "Posting Status Message: {0}", e.ProgramInfo.ToString());
+ PostStatusMessage(e.ProgramInfo);
+ }
+
+ ///
+ /// Posts the system monitor properties
+ ///
+ ///
+ ///
+ void SysMon_SystemMonitorPropertiesChanged(object sender, EventArgs e)
+ {
+ SendSystemMonitorStatusMessage();
+ }
+
+ void SendFullStatusMessage()
+ {
+ SendSystemMonitorStatusMessage();
+
+ foreach (var p in SysMon.ProgramStatusFeedbackCollection)
+ {
+ PostStatusMessage(p.Value.ProgramInfo);
+ }
+ }
+
+ void SendSystemMonitorStatusMessage()
+ {
+ Debug.Console(1, "Posting System Monitor Status Message.");
+
+ // This takes a while, launch a new thread
+ CrestronInvoke.BeginInvoke((o) =>
+ {
+ PostStatusMessage(new
+ {
+ timeZone = SysMon.TimeZoneFeedback.IntValue,
+ timeZoneName = SysMon.TimeZoneTextFeedback.StringValue,
+ ioControllerVersion = SysMon.IOControllerVersionFeedback.StringValue,
+ snmpVersion = SysMon.SnmpVersionFeedback.StringValue,
+ bacnetVersion = SysMon.BACnetAppVersionFeedback.StringValue,
+ controllerVersion = SysMon.ControllerVersionFeedback.StringValue
+ });
+ });
+ }
+
+ protected override void CustomRegisterWithAppServer(CotijaSystemController appServerController)
+ {
+ AppServerController.AddAction(MessagePath + "/fullStatus", new Action(SendFullStatusMessage));
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/PepperDashEssentials/AppServer/Messengers/VideoCodecBaseMessenger.cs b/PepperDashEssentials/AppServer/Messengers/VideoCodecBaseMessenger.cs
index 67180b35..5e545aac 100644
--- a/PepperDashEssentials/AppServer/Messengers/VideoCodecBaseMessenger.cs
+++ b/PepperDashEssentials/AppServer/Messengers/VideoCodecBaseMessenger.cs
@@ -13,31 +13,25 @@ using PepperDash.Essentials.Devices.Common.VideoCodec;
namespace PepperDash.Essentials.AppServer.Messengers
{
///
- /// Provides a messaging bridge for a VideoCodecBase
+ /// Provides a messaging bridge for a VideoCodecBase device
///
- public class VideoCodecBaseMessenger
+ public class VideoCodecBaseMessenger : MessengerBase
{
///
///
///
public VideoCodecBase Codec { get; private set; }
- public CotijaSystemController AppServerController { get; private set; }
-
- public string MessagePath { get; private set; }
-
///
///
///
///
- public VideoCodecBaseMessenger(VideoCodecBase codec, string messagePath)
+ public VideoCodecBaseMessenger(string key, VideoCodecBase codec, string messagePath)
+ : base(key, 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);
@@ -47,7 +41,31 @@ namespace PepperDash.Essentials.AppServer.Messengers
{
dirCodec.DirectoryResultReturned += new EventHandler(dirCodec_DirectoryResultReturned);
}
+
+ var recCodec = codec as IHasCallHistory;
+ if (recCodec != null)
+ {
+ recCodec.CallHistory.RecentCallsListHasChanged += new EventHandler(CallHistory_RecentCallsListHasChanged);
+ }
}
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ void CallHistory_RecentCallsListHasChanged(object sender, EventArgs e)
+ {
+ var recents = (sender as CodecCallHistory).RecentCalls;
+
+ if (recents != null)
+ {
+ PostStatusMessage(new
+ {
+ recentCalls = recents
+ });
+ }
+ }
///
///
@@ -56,7 +74,6 @@ namespace PepperDash.Essentials.AppServer.Messengers
///
void dirCodec_DirectoryResultReturned(object sender, DirectoryEventArgs e)
{
- var dir = e.Directory;
PostStatusMessage(new
{
currentDirectory = e.Directory
@@ -77,16 +94,11 @@ namespace PepperDash.Essentials.AppServer.Messengers
}
///
- /// Registers this codec's messaging with an app server controller
+ /// Called from base's RegisterWithAppServer method
///
///
- public void RegisterWithAppServer(CotijaSystemController appServerController)
+ protected override void CustomRegisterWithAppServer(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)));
@@ -113,6 +125,7 @@ namespace PepperDash.Essentials.AppServer.Messengers
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 + "/getCallHistory", new Action(GetCallHistory));
appServerController.AddAction(MessagePath + "/privacyModeOn", new Action(Codec.PrivacyModeOn));
appServerController.AddAction(MessagePath + "/privacyModeOff", new Action(Codec.PrivacyModeOff));
appServerController.AddAction(MessagePath + "/privacyModeToggle", new Action(Codec.PrivacyModeToggle));
@@ -122,6 +135,24 @@ namespace PepperDash.Essentials.AppServer.Messengers
appServerController.AddAction(MessagePath + "/standbyOff", new Action(Codec.StandbyDeactivate));
}
+ void GetCallHistory()
+ {
+ var codec = (Codec as IHasCallHistory);
+
+ if (codec != null)
+ {
+ var recents = codec.CallHistory.RecentCalls;
+
+ if (recents != null)
+ {
+ PostStatusMessage(new
+ {
+ recentCalls = recents
+ });
+ }
+ }
+ }
+
public void GetFullStatusMessage()
{
@@ -239,21 +270,10 @@ namespace PepperDash.Essentials.AppServer.Messengers
sipURI = info.SipUri
},
showSelfViewByDefault = Codec.ShowSelfViewByDefault,
- hasDirectory = Codec is IHasDirectory
+ hasDirectory = Codec is IHasDirectory,
+ hasRecents = Codec is IHasCallHistory,
+ hasCameras = Codec is IHasCameraControl
});
}
-
- ///
- /// 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/AppServer/RoomBridges/CotijaDdvc01RoomBridge.cs b/PepperDashEssentials/AppServer/RoomBridges/CotijaDdvc01RoomBridge.cs
index 2ccb03d9..55c1f61b 100644
--- a/PepperDashEssentials/AppServer/RoomBridges/CotijaDdvc01RoomBridge.cs
+++ b/PepperDashEssentials/AppServer/RoomBridges/CotijaDdvc01RoomBridge.cs
@@ -10,6 +10,7 @@ using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using PepperDash.Core;
+using PepperDash.Essentials.AppServer.Messengers;
using PepperDash.Essentials.Core;
using PepperDash.Essentials.Core.Config;
using PepperDash.Essentials.Room.Config;
@@ -26,6 +27,14 @@ namespace PepperDash.Essentials.Room.Cotija
///
public const uint RoomIsOn = 301;
+ ///
+ /// 41
+ ///
+ public const uint PromptForCode = 41;
+ ///
+ /// 42
+ ///
+ public const uint ClientJoined = 42;
///
/// 51
///
@@ -60,14 +69,15 @@ namespace PepperDash.Essentials.Room.Cotija
/// 63
///
public const uint ShutdownStart = 63;
-
-
-
///
/// 72
///
public const uint SourceHasChanged = 72;
///
+ /// 261 - The start of the range of speed dial visibles
+ ///
+ public const uint SpeedDialVisibleStartJoin = 261;
+ ///
/// 501
///
public const uint ConfigIsReady = 501;
@@ -92,6 +102,16 @@ namespace PepperDash.Essentials.Room.Cotija
/// 71
///
public const uint SelectedSourceKey = 71;
+
+ ///
+ /// 241
+ ///
+ public const uint SpeedDialNameStartJoin = 241;
+
+ ///
+ /// 251
+ ///
+ public const uint SpeedDialNumberStartJoin = 251;
///
/// 501
@@ -145,6 +165,8 @@ namespace PepperDash.Essentials.Room.Cotija
CotijaDdvc01DeviceBridge SourceBridge;
+ Ddvc01AtcMessenger AtcMessenger;
+
///
///
@@ -182,6 +204,10 @@ namespace PepperDash.Essentials.Room.Cotija
SetupFunctions();
SetupFeedbacks();
+ var key = this.Key + "-" + Parent.Key;
+ AtcMessenger = new Ddvc01AtcMessenger(key, EISC, "/device/audioCodec");
+ AtcMessenger.RegisterWithAppServer(Parent);
+
EISC.SigChange += EISC_SigChange;
EISC.OnlineStatusChange += (o, a) =>
{
@@ -216,8 +242,6 @@ namespace PepperDash.Essentials.Room.Cotija
}
}, "mobilebridgedump", "Dumps DDVC01 bridge EISC data b,u,s", ConsoleAccessLevelEnum.AccessOperator);
- CrestronConsole.AddNewConsoleCommand(s => LoadConfigValues(), "loadddvc", "", ConsoleAccessLevelEnum.AccessOperator);
-
return base.CustomActivate();
}
@@ -227,6 +251,9 @@ namespace PepperDash.Essentials.Room.Cotija
///
void SetupFunctions()
{
+#warning need join numbers for these
+ Parent.AddAction(@"/room/room1/promptForCode", new Action(() => EISC.PulseBool(BoolJoin.PromptForCode)));
+ Parent.AddAction(@"/room/room1/clientJoined", new Action(() => EISC.PulseBool(BoolJoin.ClientJoined)));
Parent.AddAction(@"/room/room1/status", new Action(SendFullStatus));
@@ -238,6 +265,10 @@ namespace PepperDash.Essentials.Room.Cotija
Parent.AddAction(@"/room/room1/defaultsource", new Action(() =>
EISC.PulseBool(BoolJoin.ActivitySharePress)));
+ Parent.AddAction(@"/room/room1/activityVideo", new Action(() =>
+ EISC.PulseBool(BoolJoin.ActivityVideoCallPress)));
+ Parent.AddAction(@"/room/room1/activityPhone", new Action(() =>
+ EISC.PulseBool(BoolJoin.ActivityPhoneCallPress)));
Parent.AddAction(@"/room/room1/volumes/master/level", new Action(u =>
EISC.SetUshort(UshortJoin.MasterVolumeLevel, u)));
@@ -250,20 +281,26 @@ 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)));
- }
}
+ ///
+ ///
+ ///
+ ///
+ void SetupSourceFunctions(string devKey)
+ {
+ SourceDeviceMapDictionary sourceJoinMap = new SourceDeviceMapDictionary();
+
+ var prefix = string.Format("/device/{0}/", devKey);
+
+ foreach (var item in sourceJoinMap)
+ {
+ var join = item.Value;
+ Parent.AddAction(string.Format("{0}{1}", prefix, item.Key), new PressAndHoldAction(b => EISC.SetBool(join, b)));
+ }
+ }
+
+
///
/// Links feedbacks to whatever is gonna happen!
///
@@ -345,10 +382,10 @@ namespace PepperDash.Essentials.Room.Cotija
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();
+ //if (co.Rooms == null)
+ // always start fresh in case simpl changed
+ co.Rooms = new List();
var rm = new DeviceConfig();
if (co.Rooms.Count == 0)
{
@@ -388,6 +425,11 @@ namespace PepperDash.Essentials.Room.Cotija
var name = EISC.StringOutput[i + 1].StringValue;
rmProps.SpeedDials.Add(new DDVC01SpeedDial { Number = num, Name = name});
}
+
+ // This MAY need a check
+ rmProps.AudioCodecKey = "audioCodec";
+ rmProps.VideoCodecKey = null; // "videoCodec";
+
// volume control names
var volCount = EISC.UShortOutput[701].UShortValue;
@@ -403,7 +445,10 @@ namespace PepperDash.Essentials.Room.Cotija
co.Devices = new List();
// clear out previous DDVC devices
- co.Devices.RemoveAll(d => d.Key.StartsWith("source-", StringComparison.OrdinalIgnoreCase));
+ co.Devices.RemoveAll(d =>
+ d.Key.StartsWith("source-", StringComparison.OrdinalIgnoreCase)
+ || d.Key.Equals("audioCodec", StringComparison.OrdinalIgnoreCase)
+ || d.Key.Equals("videoCodec", StringComparison.OrdinalIgnoreCase));
rmProps.SourceListKey = "default";
rm.Properties = JToken.FromObject(rmProps);
@@ -422,6 +467,7 @@ namespace PepperDash.Essentials.Room.Cotija
break;
var icon = EISC.StringOutput[651 + i].StringValue;
var key = EISC.StringOutput[671 + i].StringValue;
+
var type = EISC.StringOutput[701 + i].StringValue;
Debug.Console(0, this, "Adding source {0} '{1}'", key, name);
@@ -430,6 +476,7 @@ namespace PepperDash.Essentials.Room.Cotija
Name = name,
Order = (int)i + 1,
SourceKey = key,
+ Type = eSourceListItemType.Route
};
newSl.Add(key, newSLI);
@@ -447,10 +494,50 @@ namespace PepperDash.Essentials.Room.Cotija
Type = type
};
co.Devices.Add(devConf);
+
+ if (group.ToLower().StartsWith("settopbox")) // Add others here as needed
+ {
+ SetupSourceFunctions(key);
+ }
}
co.SourceLists.Add("default", newSl);
+ // build "audioCodec" config if we need
+ if (!string.IsNullOrEmpty(rmProps.AudioCodecKey))
+ {
+ var acFavs = new List();
+ for (uint i = 0; i < 4; i++)
+ {
+ if (!EISC.GetBool(BoolJoin.SpeedDialVisibleStartJoin + i))
+ {
+ break;
+ }
+ acFavs.Add(new PepperDash.Essentials.Devices.Common.Codec.CodecActiveCallItem()
+ {
+ Name = EISC.GetString(StringJoin.SpeedDialNameStartJoin + i),
+ Number = EISC.GetString(StringJoin.SpeedDialNumberStartJoin + i),
+ Type = PepperDash.Essentials.Devices.Common.Codec.eCodecCallType.Audio
+ });
+ }
+
+ var acProps = new
+ {
+ favorites = acFavs
+ };
+
+ var acStr = "audioCodec";
+ var acConf = new DeviceConfig()
+ {
+ Group = acStr,
+ Key = acStr,
+ Name = acStr,
+ Type = acStr,
+ Properties = JToken.FromObject(acProps)
+ };
+ co.Devices.Add(acConf);
+ }
+
Debug.Console(0, this, "******* CONFIG FROM DDVC: \r{0}", JsonConvert.SerializeObject(ConfigReader.ConfigObject, Formatting.Indented));
var handler = ConfigurationIsReady;
@@ -462,6 +549,9 @@ namespace PepperDash.Essentials.Room.Cotija
ConfigIsLoaded = true;
}
+ ///
+ ///
+ ///
void SendFullStatus()
{
if (ConfigIsLoaded)
@@ -585,86 +675,5 @@ namespace PepperDash.Essentials.Room.Cotija
EISC.StringInput[StringJoin.UserCodeToSystem].StringValue = UserCode;
EISC.StringInput[StringJoin.ServerUrl].StringValue = Parent.Config.ClientAppUrl;
}
-
- ///
- ///
- ///
- ///
- ///
- void SourceChange(string oldKey, string newKey)
- {
- /* Example message
- * {
- "type":"/room/status",
- "content": {
- "selectedSourceKey": "off",
- }
- }
- */
- //if (type == ChangeType.WillChange)
- //{
- // // Disconnect from previous source
-
- // if (info != null)
- // {
- // var previousDev = info.SourceDevice;
-
- // // device type interfaces
- // if (previousDev is ISetTopBoxControls)
- // (previousDev as ISetTopBoxControls).UnlinkActions(Parent);
- // // common interfaces
- // if (previousDev is IChannel)
- // (previousDev as IChannel).UnlinkActions(Parent);
- // if (previousDev is IColor)
- // (previousDev as IColor).UnlinkActions(Parent);
- // if (previousDev is IDPad)
- // (previousDev as IDPad).UnlinkActions(Parent);
- // if (previousDev is IDvr)
- // (previousDev as IDvr).UnlinkActions(Parent);
- // if (previousDev is INumericKeypad)
- // (previousDev as INumericKeypad).UnlinkActions(Parent);
- // if (previousDev is IPower)
- // (previousDev as IPower).UnlinkActions(Parent);
- // if (previousDev is ITransport)
- // (previousDev as ITransport).UnlinkActions(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.PostToServer(message);
- //}
- //else
- //{
- // if (info != null)
- // {
- // var dev = info.SourceDevice;
-
- // if (dev is ISetTopBoxControls)
- // (dev as ISetTopBoxControls).LinkActions(Parent);
- // if (dev is IChannel)
- // (dev as IChannel).LinkActions(Parent);
- // if (dev is IColor)
- // (dev as IColor).LinkActions(Parent);
- // if (dev is IDPad)
- // (dev as IDPad).LinkActions(Parent);
- // if (dev is IDvr)
- // (dev as IDvr).LinkActions(Parent);
- // if (dev is INumericKeypad)
- // (dev as INumericKeypad).LinkActions(Parent);
- // if (dev is IPower)
- // (dev as IPower).LinkActions(Parent);
- // if (dev is ITransport)
- // (dev as ITransport).LinkActions(Parent);
- // }
- //}
- }
}
}
\ No newline at end of file
diff --git a/PepperDashEssentials/AppServer/RoomBridges/CotijaEssentialsHuddleSpaceRoomBridge.cs b/PepperDashEssentials/AppServer/RoomBridges/CotijaEssentialsHuddleSpaceRoomBridge.cs
index 24f2340a..976cb484 100644
--- a/PepperDashEssentials/AppServer/RoomBridges/CotijaEssentialsHuddleSpaceRoomBridge.cs
+++ b/PepperDashEssentials/AppServer/RoomBridges/CotijaEssentialsHuddleSpaceRoomBridge.cs
@@ -12,6 +12,7 @@ using PepperDash.Essentials.Core;
using PepperDash.Essentials.Room.Cotija;
using PepperDash.Essentials.Devices.Common.Codec;
using PepperDash.Essentials.Devices.Common.VideoCodec;
+using PepperDash.Essentials.Devices.Common.AudioCodec;
namespace PepperDash.Essentials
{
@@ -22,6 +23,8 @@ namespace PepperDash.Essentials
public VideoCodecBaseMessenger VCMessenger { get; private set; }
+ public AudioCodecBaseMessenger ACMessenger { get; private set; }
+
///
///
///
@@ -74,7 +77,7 @@ namespace PepperDash.Essentials
{
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(() =>
+ Parent.AddAction(string.Format(@"/room/{0}/volumes/master/muteToggle", Room.Key), new Action(() =>
volumeRoom.CurrentVolumeControls.MuteToggle()));
volumeRoom.CurrentVolumeDeviceChange += new EventHandler(Room_CurrentVolumeDeviceChange);
@@ -82,7 +85,7 @@ namespace PepperDash.Essentials
var currentVolumeDevice = volumeRoom.CurrentVolumeControls as IBasicVolumeWithFeedback;
if (currentVolumeDevice != null)
{
- currentVolumeDevice.MuteFeedback.OutputChange += VolumeLevelFeedback_OutputChange;
+ currentVolumeDevice.MuteFeedback.OutputChange += MuteFeedback_OutputChange;
currentVolumeDevice.VolumeLevelFeedback.OutputChange += VolumeLevelFeedback_OutputChange;
}
}
@@ -92,29 +95,25 @@ namespace PepperDash.Essentials
sscRoom.CurrentSingleSourceChange += new SourceInfoChangeHandler(Room_CurrentSingleSourceChange);
var vcRoom = Room as IHasVideoCodec;
- if (vcRoom != null)
+ if (vcRoom != null && vcRoom.VideoCodec != null)
{
var codec = vcRoom.VideoCodec;
- VCMessenger = new VideoCodecBaseMessenger(vcRoom.VideoCodec, "/device/videoCodec");
+ var key = vcRoom.VideoCodec.Key + "-" + parent.Key;
+ VCMessenger = new VideoCodecBaseMessenger(key, 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 acRoom = Room as IHasAudioCodec;
+ if (acRoom != null && acRoom.AudioCodec != null)
+ {
+ var codec = acRoom.AudioCodec;
+ var key = acRoom.AudioCodec.Key + "-" + parent.Key;
+ ACMessenger = new AudioCodecBaseMessenger(key, acRoom.AudioCodec, "/device/audioCodec");
+ ACMessenger.RegisterWithAppServer(Parent);
+ }
+
var defCallRm = Room as IRunDefaultCallRoute;
if (defCallRm != null)
{
@@ -169,18 +168,18 @@ namespace PepperDash.Essentials
});
}
- ///
- /// Handler for codec changes
- ///
- void codec_CallStatusChange(object sender, CodecCallStatusItemChangeEventArgs e)
- {
- PostStatusMessage(new
- {
- calls = GetCallsMessageObject(),
- //vtc = GetVtcCallsMessageObject()
- });
+ /////
+ ///// Handler for codec changes
+ /////
+ //void codec_CallStatusChange(object sender, CodecCallStatusItemChangeEventArgs e)
+ //{
+ // PostStatusMessage(new
+ // {
+ // calls = GetCallsMessageObject(),
+ // //vtc = GetVtcCallsMessageObject()
+ // });
- }
+ //}
///
/// Helper for posting status message
@@ -425,53 +424,52 @@ namespace PepperDash.Essentials
PostStatusMessage(new
{
- calls = GetCallsMessageObject(),
+ //calls = GetCallsMessageObject(),
isOn = room.OnFeedback.BoolValue,
selectedSourceKey = sourceKey,
- vtc = GetVtcCallsMessageObject(),
+ //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 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;
- }
-
+ /////
+ ///// 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;
+ //}
}
///
diff --git a/PepperDashEssentials/Bridges/._EssentialsLightsBridge.cs b/PepperDashEssentials/Bridges/._EssentialsLightsBridge.cs
new file mode 100644
index 00000000..069fb4b1
Binary files /dev/null and b/PepperDashEssentials/Bridges/._EssentialsLightsBridge.cs differ
diff --git a/PepperDashEssentials/Bridges/BridgeBase.cs b/PepperDashEssentials/Bridges/BridgeBase.cs
index cf2d2f27..537b5d59 100644
--- a/PepperDashEssentials/Bridges/BridgeBase.cs
+++ b/PepperDashEssentials/Bridges/BridgeBase.cs
@@ -5,9 +5,17 @@ using System.Text;
using Crestron.SimplSharp;
using Crestron.SimplSharpPro.EthernetCommunication;
+using Newtonsoft.Json;
+
using PepperDash.Core;
using PepperDash.Essentials.Core;
+using PepperDash.Essentials.Core.Lighting;
using PepperDash.Essentials.Core.Devices;
+using PepperDash.Essentials.Devices.Common;
+using PepperDash.Essentials.Core.Config;
+using PepperDash.Essentials.Core.CrestronIO;
+using PepperDash.Essentials.DM;
+using PepperDash.Essentials.Devices.Common.Cameras;
namespace PepperDash.Essentials.Bridges
{
@@ -44,17 +52,93 @@ namespace PepperDash.Essentials.Bridges
///
public class EiscApi : BridgeApi
{
+ public EiscApiPropertiesConfig PropertiesConfig { get; private set; }
+
public ThreeSeriesTcpIpEthernetIntersystemCommunications Eisc { get; private set; }
- public EiscApi(string key, uint ipid, string hostname) :
- base(key)
+ public EiscApi(DeviceConfig dc) :
+ base(dc.Key)
{
- Eisc = new ThreeSeriesTcpIpEthernetIntersystemCommunications(ipid, hostname, Global.ControlSystem);
+ PropertiesConfig = JsonConvert.DeserializeObject(dc.Properties.ToString());
+
+ Eisc = new ThreeSeriesTcpIpEthernetIntersystemCommunications(PropertiesConfig.Control.IpIdInt, PropertiesConfig.Control.TcpSshProperties.Address, Global.ControlSystem);
Eisc.SigChange += new Crestron.SimplSharpPro.DeviceSupport.SigEventHandler(Eisc_SigChange);
Eisc.Register();
+
+ AddPostActivationAction( () =>
+ {
+ Debug.Console(1, this, "Linking Devices...");
+
+ foreach (var d in PropertiesConfig.Devices)
+ {
+ var device = DeviceManager.GetDeviceForKey(d.DeviceKey);
+
+ if (device != null)
+ {
+ if (device is PepperDash.Essentials.Core.Monitoring.SystemMonitorController)
+ {
+ (device as PepperDash.Essentials.Core.Monitoring.SystemMonitorController).LinkToApi(Eisc, d.JoinStart, d.JoinMapKey);
+ continue;
+ }
+ else if (device is GenericComm)
+ {
+ (device as GenericComm).LinkToApi(Eisc, d.JoinStart, d.JoinMapKey);
+ continue;
+ }
+ else if (device is CameraBase)
+ {
+ (device as CameraBase).LinkToApi(Eisc, d.JoinStart, d.JoinMapKey);
+ continue;
+ }
+ else if (device is PepperDash.Essentials.Core.TwoWayDisplayBase)
+ {
+ (device as TwoWayDisplayBase).LinkToApi(Eisc, d.JoinStart, d.JoinMapKey);
+ continue;
+ }
+ else if (device is DmChassisController)
+ {
+ (device as DmChassisController).LinkToApi(Eisc, d.JoinStart, d.JoinMapKey);
+ continue;
+ }
+
+ else if (device is DmTxControllerBase)
+ {
+ (device as DmTxControllerBase).LinkToApi(Eisc, d.JoinStart, d.JoinMapKey);
+ continue;
+ }
+ else if (device is DmRmcControllerBase)
+ {
+ (device as DmRmcControllerBase).LinkToApi(Eisc, d.JoinStart, d.JoinMapKey);
+ continue;
+ }
+ else if (device is GenericRelayDevice)
+ {
+ (device as GenericRelayDevice).LinkToApi(Eisc, d.JoinStart, d.JoinMapKey);
+ continue;
+ }
+ else if (device is IDigitalInput)
+ {
+ (device as IDigitalInput).LinkToApi(Eisc, d.JoinStart, d.JoinMapKey);
+ continue;
+ }
+ else if (device is LightingBase)
+ {
+ (device as LightingBase).LinkToApi(Eisc, d.JoinStart, d.JoinMapKey);
+ continue;
+ }
+ else if (device is DigitalLogger)
+ {
+ (device as DigitalLogger).LinkToApi(Eisc, d.JoinStart, d.JoinMapKey);
+ continue;
+ }
+ }
+ }
+
+ Debug.Console(1, this, "Devices Linked.");
+ });
}
///
@@ -65,7 +149,7 @@ namespace PepperDash.Essentials.Bridges
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);
+ Debug.Console(1, this, "EiscApi 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);
@@ -76,138 +160,27 @@ namespace PepperDash.Essentials.Bridges
}
}
-
-
-
- ///
- /// Defines each type and it's matching API type
- ///
- public static class DeviceApiFactory
+ public class EiscApiPropertiesConfig
{
- public static Dictionary TypeMap = new Dictionary
+ [JsonProperty("control")]
+ public EssentialsControlPropertiesConfig Control { get; set; }
+
+ [JsonProperty("devices")]
+ public List Devices { get; set; }
+
+
+ public class ApiDevice
{
- { typeof(DmChassisController), typeof(DmChassisControllerApi) },
- { typeof(IBasicCommunication), typeof(IBasicCommunicationApi) }
- //{ typeof(SomeShittyDisplayController), typeof(SomeShittyDisplayControllerApi) }
- };
- }
+ [JsonProperty("deviceKey")]
+ public string DeviceKey { get; set; }
+ [JsonProperty("joinStart")]
+ public uint JoinStart { get; set; }
- ///
- /// 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 }
- };
+ [JsonProperty("joinMapKey")]
+ public string JoinMapKey { get; set; }
}
- ///
- /// 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);
- }
}
diff --git a/PepperDashEssentials/Bridges/BridgeFactory.cs b/PepperDashEssentials/Bridges/BridgeFactory.cs
index 97cc6fce..8b9ba75c 100644
--- a/PepperDashEssentials/Bridges/BridgeFactory.cs
+++ b/PepperDashEssentials/Bridges/BridgeFactory.cs
@@ -9,176 +9,133 @@ using PepperDash.Essentials.Core;
using PepperDash.Essentials.Core.Config;
using PepperDash.Core;
using PepperDash.Essentials.Core.Routing;
+using PepperDash.Essentials.Bridges;
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?
+public class BridgeFactory
+{
+ public static IKeyed GetDevice(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 key = dc.Key;
+ var name = dc.Name;
+ var type = dc.Type;
+ var properties = dc.Properties;
+ var propAnon = new { };
- var typeName = dc.Type.ToLower();
- var groupName = dc.Group.ToLower();
+ 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);
+ //Debug.Console(2, "Name {0}, Key {1}, Type {2}, Properties {3}", name, key, type, properties.ToString());
+
+ if (typeName == "eiscapi")
+ {
+ return new EiscApi(dc);
+ }
+
+ return null;
+ }
+}
+
+
+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);
}
- else if (typeName == "comm")
- {
- return new CommBridge(key, name, properties);
- }
- else
- return null;
}
- }
- public class DmBridge : Device
- {
- public EiscBridgeProperties Properties { get; private set; }
+ foreach (var deviceKey in Properties.CommDevices)
+ {
+ var device = DeviceManager.GetDeviceForKey(deviceKey);
- 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));
- }
+ if (device != null)
+ {
+ Debug.Console(0, "deviceKey {0} Found in Device Manager", device.Key);
+ CommDevices.Add(device as IBasicCommunication);
}
-
- 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);
- }
+ else
+ {
+ Debug.Console(0, "deviceKey {0} Not Found in Device Manager", deviceKey);
}
-
- Debug.Console(0, "Bridge {0} Activated", this.Name);
- return true;
}
+
+ // Iterate through all the CommDevices and link up their Actions and Feedbacks
+
+ 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 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 EiscProperties
+ {
+ public string IpId { get; set; }
+ public string Hostname { get; set; }
}
+}
- public class CommBridgeProperties : EiscBridgeProperties
- {
- public List CommDevices { get; set; }
- }
+public class CommBridgeProperties : EiscBridgeProperties
+{
+ public List CommDevices { get; set; }
+}
- public enum eApiType { Eisc = 0 }
+public enum eApiType { Eisc = 0 }
- public class BridgeApiEisc
- {
- public uint Ipid { get; private set; }
- public ThreeSeriesTcpIpEthernetIntersystemCommunications Eisc { get; private set; }
+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);
- }
+ 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);
}
-}
\ No newline at end of file
+ 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/Bridges/Bridges.BridgeFactory.cs b/PepperDashEssentials/Bridges/Bridges.BridgeFactory.cs
new file mode 100644
index 00000000..18dd0c71
--- /dev/null
+++ b/PepperDashEssentials/Bridges/Bridges.BridgeFactory.cs
@@ -0,0 +1,90 @@
+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;
+using PepperDash.Essentials.Bridges;
+
+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(2, "Name {0}, Key {1}, Type {2}, Properties {3}", name, key, type, properties.ToString());
+ if (typeName == "essentialdm")
+ {
+ return new EssentialDM(key, name, properties);
+ }
+ else if (typeName == "essentialcomm")
+ {
+ Debug.Console(2, "Launch Essential Comm");
+ return new EssentialComm(key, name, properties);
+ }
+ else if (typeName == "essentialdsp")
+ {
+ Debug.Console(2, "Launch EssentialDsp");
+ return new EssentialDsp(key, name, properties);
+ }
+ else if (typeName == "essentialstvone")
+ {
+ Debug.Console(2, "Launch essentialstvone");
+ return new EssentialsTVOne(key, name, properties);
+ }
+ else if (typeName == "essentialslighting")
+ {
+ Debug.Console(2, "Launch essentialslighting");
+ return new EssentialsLightsBridge(key, name, properties);
+ }
+ else if (typeName == "eiscapi")
+ {
+ return new EiscApi(dc);
+ }
+ return null;
+ }
+ }
+ public class BridgeApiEisc {
+ public uint Ipid;
+ public ThreeSeriesTcpIpEthernetIntersystemCommunications Eisc;
+ public BridgeApiEisc(string ipid) {
+ Ipid = (UInt32)int.Parse(ipid, System.Globalization.NumberStyles.HexNumber);
+ Eisc = new ThreeSeriesTcpIpEthernetIntersystemCommunications(Ipid, "127.0.0.2", Global.ControlSystem);
+ Eisc.Register();
+ Eisc.SigChange += Eisc_SigChange;
+ Debug.Console(2, "BridgeApiEisc Created at Ipid {0}", ipid);
+ }
+ void Eisc_SigChange(object currentDevice, Crestron.SimplSharpPro.SigEventArgs args) {
+ if (Debug.Level >= 1)
+ Debug.Console(2, "DDVC 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);
+ }
+ }
+
+ }
+
+
+
+
\ No newline at end of file
diff --git a/PepperDashEssentials/Bridges/CameraControllerBridge.cs b/PepperDashEssentials/Bridges/CameraControllerBridge.cs
new file mode 100644
index 00000000..05b67287
--- /dev/null
+++ b/PepperDashEssentials/Bridges/CameraControllerBridge.cs
@@ -0,0 +1,187 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Crestron.SimplSharp;
+using Crestron.SimplSharpPro.DeviceSupport;
+using PepperDash.Core;
+using PepperDash.Essentials.Core;
+using PepperDash.Essentials.Devices.Common;
+
+namespace PepperDash.Essentials.Bridges
+{
+ public static class CameraControllerApiExtensions
+ {
+
+ public static BasicTriList _TriList;
+ public static CameraControllerJoinMap JoinMap;
+ public static void LinkToApi(this PepperDash.Essentials.Devices.Common.Cameras.CameraBase cameraDevice, BasicTriList trilist, uint joinStart, string joinMapKey)
+ {
+ JoinMap = JoinMapHelper.GetJoinMapForDevice(joinMapKey) as CameraControllerJoinMap;
+
+ _TriList = trilist;
+ if (JoinMap == null)
+ {
+ JoinMap = new CameraControllerJoinMap();
+ }
+
+ JoinMap.OffsetJoinNumbers(joinStart);
+ Debug.Console(1, "Linking to Trilist '{0}'", trilist.ID.ToString("X"));
+ Debug.Console(0, "Linking to Bridge Type {0}", cameraDevice.GetType().Name.ToString());
+
+ var commMonitor = cameraDevice as ICommunicationMonitor;
+ commMonitor.CommunicationMonitor.IsOnlineFeedback.LinkInputSig(trilist.BooleanInput[JoinMap.IsOnline]);
+
+
+ trilist.SetBoolSigAction(JoinMap.Left, (b) =>
+ {
+ if (b)
+ {
+ cameraDevice.PanLeft();
+ }
+ else
+ {
+ cameraDevice.Stop();
+ }
+ });
+ trilist.SetBoolSigAction(JoinMap.Right, (b) =>
+ {
+ if (b)
+ {
+ cameraDevice.PanRight();
+ }
+ else
+ {
+ cameraDevice.Stop();
+ }
+ });
+
+ trilist.SetBoolSigAction(JoinMap.Up, (b) =>
+ {
+ if (b)
+ {
+ cameraDevice.TiltUp();
+ }
+ else
+ {
+ cameraDevice.Stop();
+ }
+ });
+ trilist.SetBoolSigAction(JoinMap.Down, (b) =>
+ {
+ if (b)
+ {
+ cameraDevice.TiltDown();
+ }
+ else
+ {
+ cameraDevice.Stop();
+ }
+ });
+
+ trilist.SetBoolSigAction(JoinMap.ZoomIn, (b) =>
+ {
+ if (b)
+ {
+ cameraDevice.ZoomIn();
+ }
+ else
+ {
+ cameraDevice.Stop();
+ }
+ });
+
+ trilist.SetBoolSigAction(JoinMap.ZoomOut, (b) =>
+ {
+ if (b)
+ {
+ cameraDevice.ZoomOut();
+ }
+ else
+ {
+ cameraDevice.Stop();
+ }
+ });
+
+
+ if (cameraDevice.GetType().Name.ToString().ToLower() == "cameravisca")
+ {
+ var viscaCamera = cameraDevice as PepperDash.Essentials.Devices.Common.Cameras.CameraVisca;
+ trilist.SetSigTrueAction(JoinMap.PowerOn, () => viscaCamera.PowerOn());
+ trilist.SetSigTrueAction(JoinMap.PowerOff, () => viscaCamera.PowerOff());
+
+ viscaCamera.PowerIsOnFeedback.LinkInputSig(trilist.BooleanInput[JoinMap.PowerOn]);
+ viscaCamera.PowerIsOnFeedback.LinkComplementInputSig(trilist.BooleanInput[JoinMap.PowerOff]);
+
+ viscaCamera.CommunicationMonitor.IsOnlineFeedback.LinkInputSig(trilist.BooleanInput[JoinMap.IsOnline]);
+ for (int i = 0; i < JoinMap.NumberOfPresets; i++)
+ {
+ int tempNum = i;
+ trilist.SetSigTrueAction((ushort)(JoinMap.PresetRecallOffset + tempNum), () =>
+ {
+ viscaCamera.RecallPreset(tempNum);
+ });
+ trilist.SetSigTrueAction((ushort)(JoinMap.PresetSaveOffset + tempNum), () =>
+ {
+ viscaCamera.SavePreset(tempNum);
+ });
+ }
+ }
+
+
+
+ }
+
+
+ }
+ public class CameraControllerJoinMap : JoinMapBase
+ {
+ public uint IsOnline { get; set; }
+ public uint PowerOff { get; set; }
+ public uint PowerOn { get; set; }
+ public uint Up { get; set; }
+ public uint Down { get; set; }
+ public uint Left { get; set; }
+ public uint Right { get; set; }
+ public uint ZoomIn { get; set; }
+ public uint ZoomOut { get; set; }
+ public uint PresetRecallOffset { get; set; }
+ public uint PresetSaveOffset { get; set; }
+ public uint NumberOfPresets { get; set; }
+
+ public CameraControllerJoinMap()
+ {
+ // Digital
+ IsOnline = 9;
+ PowerOff = 8;
+ PowerOn = 7;
+ Up = 1;
+ Down = 2;
+ Left = 3;
+ Right = 4;
+ ZoomIn = 5;
+ ZoomOut = 6;
+ PresetRecallOffset = 10;
+ PresetSaveOffset = 30;
+ NumberOfPresets = 5;
+ // Analog
+ }
+
+ public override void OffsetJoinNumbers(uint joinStart)
+ {
+ var joinOffset = joinStart - 1;
+
+ IsOnline = IsOnline + joinOffset;
+ PowerOff = PowerOff + joinOffset;
+ PowerOn = PowerOn + joinOffset;
+ Up = Up + joinOffset;
+ Down = Down + joinOffset;
+ Left = Left + joinOffset;
+ Right = Right + joinOffset;
+ ZoomIn = ZoomIn + joinOffset;
+ ZoomOut = ZoomOut + joinOffset;
+ PresetRecallOffset = PresetRecallOffset + joinOffset;
+ PresetSaveOffset = PresetSaveOffset + joinOffset;
+ }
+ }
+}
\ No newline at end of file
diff --git a/PepperDashEssentials/Bridges/DigitalLoggerBridge.cs b/PepperDashEssentials/Bridges/DigitalLoggerBridge.cs
new file mode 100644
index 00000000..1c78b5a0
--- /dev/null
+++ b/PepperDashEssentials/Bridges/DigitalLoggerBridge.cs
@@ -0,0 +1,77 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Crestron.SimplSharp;
+using Crestron.SimplSharpPro.DeviceSupport;
+using PepperDash.Core;
+using PepperDash.Essentials.Core;
+using PepperDash.Essentials.Devices.Common;
+
+namespace PepperDash.Essentials.Bridges
+{
+ public static class DigitalLoggerApiExtensions
+ {
+ public static void LinkToApi(this DigitalLogger DigitalLogger, BasicTriList trilist, uint joinStart, string joinMapKey)
+ {
+ var joinMap = JoinMapHelper.GetJoinMapForDevice(joinMapKey) as DigitalLoggerJoinMap;
+
+ if (joinMap == null)
+ joinMap = new DigitalLoggerJoinMap();
+
+ joinMap.OffsetJoinNumbers(joinStart);
+ Debug.Console(1, DigitalLogger, "Linking to Trilist '{0}'", trilist.ID.ToString("X"));
+ for (uint i = 1; i <= DigitalLogger.CircuitCount; i++)
+ {
+ var circuit = i;
+ DigitalLogger.CircuitNameFeedbacks[circuit - 1].LinkInputSig(trilist.StringInput[joinMap.CircuitNames + circuit]);
+ DigitalLogger.CircuitIsCritical[circuit - 1].LinkInputSig(trilist.BooleanInput[joinMap.CircuitIsCritical + circuit]);
+ DigitalLogger.CircuitState[circuit - 1].LinkInputSig(trilist.BooleanInput[joinMap.CircuitState + circuit]);
+ trilist.SetSigTrueAction(joinMap.CircuitCycle + circuit, () => DigitalLogger.CycleCircuit(circuit - 1));
+ trilist.SetSigTrueAction(joinMap.CircuitOnCmd + circuit, () => DigitalLogger.TurnOnCircuit(circuit - 1));
+ trilist.SetSigTrueAction(joinMap.CircuitOffCmd + circuit, () => DigitalLogger.TurnOffCircuit(circuit - 1));
+
+ }
+ }
+ }
+ public class DigitalLoggerJoinMap : JoinMapBase
+ {
+ public uint IsOnline { get; set; }
+ public uint CircuitNames { get; set; }
+ public uint CircuitState { get; set; }
+ public uint CircuitCycle { get; set; }
+ public uint CircuitIsCritical { get; set; }
+ public uint CircuitOnCmd { get; set; }
+ public uint CircuitOffCmd { get; set; }
+ public DigitalLoggerJoinMap()
+ {
+ // Digital
+ IsOnline = 9;
+ CircuitState = 0;
+ CircuitCycle = 0;
+ CircuitIsCritical = 10;
+ CircuitOnCmd = 10;
+ CircuitOffCmd = 20;
+ // Serial
+ CircuitNames = 0;
+ // Analog
+ }
+
+ public override void OffsetJoinNumbers(uint joinStart)
+ {
+ var joinOffset = joinStart - 1;
+
+ IsOnline = IsOnline + joinOffset;
+ CircuitNames = CircuitNames + joinOffset;
+ CircuitState = CircuitState + joinOffset;
+ CircuitCycle = CircuitCycle + joinOffset;
+ CircuitIsCritical = CircuitIsCritical + joinOffset;
+ CircuitOnCmd = CircuitOnCmd + joinOffset;
+ CircuitOffCmd = CircuitOffCmd + joinOffset;
+
+
+
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/PepperDashEssentials/Bridges/DisplayControllerBridge.cs b/PepperDashEssentials/Bridges/DisplayControllerBridge.cs
new file mode 100644
index 00000000..8c266589
--- /dev/null
+++ b/PepperDashEssentials/Bridges/DisplayControllerBridge.cs
@@ -0,0 +1,171 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Crestron.SimplSharp;
+using Crestron.SimplSharpPro.DeviceSupport;
+using PepperDash.Core;
+using PepperDash.Essentials.Core;
+using PepperDash.Essentials.Devices.Common;
+
+namespace PepperDash.Essentials.Bridges
+{
+ public static class DisplayControllerApiExtensions
+ {
+
+ public static BasicTriList _TriList;
+ public static DisplayControllerJoinMap JoinMap;
+ public static int InputNumber;
+ public static IntFeedback InputNumberFeedback;
+ public static List InputKeys = new List();
+ public static void LinkToApi(this PepperDash.Essentials.Core.TwoWayDisplayBase displayDevice, BasicTriList trilist, uint joinStart, string joinMapKey)
+ {
+ JoinMap = JoinMapHelper.GetJoinMapForDevice(joinMapKey) as DisplayControllerJoinMap;
+ _TriList = trilist;
+
+ if (JoinMap == null)
+ {
+ JoinMap = new DisplayControllerJoinMap();
+ }
+
+ JoinMap.OffsetJoinNumbers(joinStart);
+ Debug.Console(1, "Linking to Trilist '{0}'", trilist.ID.ToString("X"));
+ Debug.Console(0, "Linking to Bridge Type {0}", displayDevice.GetType().Name.ToString());
+
+ trilist.StringInput[JoinMap.Name].StringValue = displayDevice.GetType().Name.ToString();
+
+ InputNumberFeedback = new IntFeedback(() => { return InputNumber;});
+ InputNumberFeedback.LinkInputSig(trilist.UShortInput[JoinMap.InputSelect]);
+ var commMonitor = displayDevice as ICommunicationMonitor;
+ commMonitor.CommunicationMonitor.IsOnlineFeedback.LinkInputSig(trilist.BooleanInput[JoinMap.IsOnline]);
+
+ // Poewer Off
+ trilist.SetSigTrueAction(JoinMap.PowerOff, () =>
+ {
+ InputNumber = 102;
+ InputNumberFeedback.FireUpdate();
+ displayDevice.PowerOff();
+ });
+
+ displayDevice.PowerIsOnFeedback.OutputChange += new EventHandler(PowerIsOnFeedback_OutputChange);
+ displayDevice.PowerIsOnFeedback.LinkComplementInputSig(trilist.BooleanInput[JoinMap.PowerOff]);
+
+ // Poewer On
+ trilist.SetSigTrueAction(JoinMap.PowerOn, () =>
+ {
+ InputNumber = 0;
+ InputNumberFeedback.FireUpdate();
+ displayDevice.PowerOn();
+ });
+
+
+ displayDevice.PowerIsOnFeedback.LinkInputSig(trilist.BooleanInput[JoinMap.PowerOn]);
+
+ int count = 1;
+ foreach (var input in displayDevice.InputPorts)
+ {
+ InputKeys.Add(input.Key.ToString());
+ var tempKey = InputKeys.ElementAt(count - 1);
+ trilist.SetSigTrueAction((ushort)(JoinMap.InputSelectOffset + count), () => { displayDevice.ExecuteSwitch(displayDevice.InputPorts[tempKey].Selector); });
+ trilist.StringInput[(ushort)(JoinMap.InputNamesOffset + count)].StringValue = input.Key.ToString();
+ count++;
+ }
+
+ displayDevice.CurrentInputFeedback.OutputChange += new EventHandler(CurrentInputFeedback_OutputChange);
+ trilist.SetUShortSigAction(JoinMap.InputSelect, (a) =>
+ {
+ if (a == 0)
+ {
+ displayDevice.PowerOff();
+ InputNumber = 0;
+ }
+ else if (a > 0 && a < displayDevice.InputPorts.Count && a != InputNumber)
+ {
+ displayDevice.ExecuteSwitch(displayDevice.InputPorts.ElementAt(a - 1).Selector);
+ InputNumber = a;
+ }
+ else if (a == 102)
+ {
+ displayDevice.PowerToggle();
+
+ }
+ InputNumberFeedback.FireUpdate();
+ });
+
+ }
+
+ static void CurrentInputFeedback_OutputChange(object sender, FeedbackEventArgs e)
+ {
+
+ Debug.Console(0, "CurrentInputFeedback_OutputChange {0}", e.StringValue);
+
+ }
+
+ static void PowerIsOnFeedback_OutputChange(object sender, FeedbackEventArgs e)
+ {
+
+ // Debug.Console(0, "PowerIsOnFeedback_OutputChange {0}", e.BoolValue);
+ if (!e.BoolValue)
+ {
+ InputNumber = 102;
+ InputNumberFeedback.FireUpdate();
+
+ }
+ else
+ {
+ InputNumber = 0;
+ InputNumberFeedback.FireUpdate();
+ }
+ }
+
+
+
+
+ }
+ public class DisplayControllerJoinMap : JoinMapBase
+ {
+ public uint Name { get; set; }
+ public uint InputNamesOffset { get; set; }
+ public uint InputSelectOffset { get; set; }
+ public uint IsOnline { get; set; }
+ public uint PowerOff { get; set; }
+ public uint InputSelect { get; set; }
+ public uint PowerOn { get; set; }
+ public uint SelectScene { get; set; }
+ public uint LightingSceneOffset { get; set; }
+ public uint ButtonVisibilityOffset { get; set; }
+ public uint IntegrationIdSet { get; set; }
+
+ public DisplayControllerJoinMap()
+ {
+ // Digital
+ IsOnline = 50;
+ PowerOff = 1;
+ PowerOn = 2;
+ InputSelect = 4;
+ IntegrationIdSet = 1;
+ LightingSceneOffset = 10;
+ ButtonVisibilityOffset = 40;
+ Name = 1;
+ InputNamesOffset = 10;
+ InputSelectOffset = 4;
+ // Analog
+ }
+
+ public override void OffsetJoinNumbers(uint joinStart)
+ {
+ var joinOffset = joinStart - 1;
+
+ IsOnline = IsOnline + joinOffset;
+ PowerOff = PowerOff + joinOffset;
+ PowerOn = PowerOn + joinOffset;
+ SelectScene = SelectScene + joinOffset;
+ LightingSceneOffset = LightingSceneOffset + joinOffset;
+ ButtonVisibilityOffset = ButtonVisibilityOffset + joinOffset;
+ Name = Name + joinOffset;
+ InputNamesOffset = InputNamesOffset + joinOffset;
+ InputSelectOffset = InputSelectOffset + joinOffset;
+
+ }
+ }
+}
\ No newline at end of file
diff --git a/PepperDashEssentials/Bridges/DmChassisControllerBridge.cs b/PepperDashEssentials/Bridges/DmChassisControllerBridge.cs
new file mode 100644
index 00000000..d864787c
--- /dev/null
+++ b/PepperDashEssentials/Bridges/DmChassisControllerBridge.cs
@@ -0,0 +1,130 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Crestron.SimplSharp;
+using Crestron.SimplSharpPro.DeviceSupport;
+
+using PepperDash.Core;
+using PepperDash.Essentials.Core;
+using PepperDash.Essentials.DM;
+
+namespace PepperDash.Essentials.Bridges
+{
+ public static class DmChassisControllerApiExtentions
+ {
+ public static void LinkToApi(this DmChassisController dmChassis, BasicTriList trilist, uint joinStart, string joinMapKey)
+ {
+ var joinMap = JoinMapHelper.GetJoinMapForDevice(joinMapKey) as DmChassisControllerJoinMap;
+
+ if (joinMap == null)
+ joinMap = new DmChassisControllerJoinMap();
+
+ joinMap.OffsetJoinNumbers(joinStart);
+
+ Debug.Console(1, dmChassis, "Linking to Trilist '{0}'", trilist.ID.ToString("X"));
+
+ dmChassis.IsOnline.LinkInputSig(trilist.BooleanInput[joinMap.IsOnline]);
+
+ // Link up outputs
+ for (uint i = 1; i <= dmChassis.Chassis.NumberOfOutputs - 1; i++)
+ {
+ var ioSlot = i;
+
+ // Control
+ trilist.SetUShortSigAction(joinMap.OutputVideo + ioSlot, new Action(o => dmChassis.ExecuteSwitch(o, ioSlot, eRoutingSignalType.Video)));
+ trilist.SetUShortSigAction(joinMap.OutputAudio + ioSlot, new Action(o => dmChassis.ExecuteSwitch(o, ioSlot, eRoutingSignalType.Audio)));
+ if (dmChassis.TxDictionary.ContainsKey(ioSlot))
+ {
+ Debug.Console(2, "Creating Tx Feedbacks {0}", ioSlot);
+ var TxKey = dmChassis.TxDictionary[ioSlot];
+ var TxDevice = DeviceManager.GetDeviceForKey(TxKey) as DmTxControllerBase;
+ TxDevice.IsOnline.LinkInputSig(trilist.BooleanInput[joinMap.InputEndpointOnline + ioSlot]);
+ // TxDevice.AnyVideoInput.VideoStatus.VideoSyncFeedback.LinkInputSig(trilist.BooleanInput[joinMap.InputEndpointOnline + ioSlot]);
+ // trilist.SetUShortSigAction((ApiMap.HdcpSupport[ioSlot]), u => TxDevice.SetHdcpSupportAll((ePdtHdcpSupport)(u)));
+ // TxDevice.HdcpSupportAllFeedback.LinkInputSig(trilist.UShortInput[joinMap. + ioSlot]);
+ // trilist.UShortInput[ApiMap.HdcpSupportCapability[ioSlot]].UShortValue = TxDevice.HdcpSupportCapability;
+ }
+ else
+ {
+ // dmChassis.VideoInputSyncFeedbacks[ioSlot].LinkInputSig(trilist.BooleanInput[ApiMap.TxVideoSyncStatus[ioSlot]]);
+ }
+ if (dmChassis.RxDictionary.ContainsKey(ioSlot))
+ {
+ Debug.Console(2, "Creating Rx Feedbacks {0}", ioSlot);
+ var RxKey = dmChassis.RxDictionary[ioSlot];
+ var RxDevice = DeviceManager.GetDeviceForKey(RxKey) as DmRmcControllerBase;
+ RxDevice.IsOnline.LinkInputSig(trilist.BooleanInput[joinMap.OutputEndpointOnline + ioSlot]);
+ }
+ // Feedback
+ dmChassis.VideoOutputFeedbacks[ioSlot].LinkInputSig(trilist.UShortInput[joinMap.OutputVideo + ioSlot]);
+ dmChassis.AudioOutputFeedbacks[ioSlot].LinkInputSig(trilist.UShortInput[joinMap.OutputAudio + ioSlot]);
+
+ dmChassis.VideoInputSyncFeedbacks[ioSlot].LinkInputSig(trilist.BooleanInput[joinMap.VideoSyncStatus + ioSlot]);
+
+ dmChassis.OutputNameFeedbacks[ioSlot].LinkInputSig(trilist.StringInput[joinMap.OutputNames + ioSlot]);
+ dmChassis.InputNameFeedbacks[ioSlot].LinkInputSig(trilist.StringInput[joinMap.InputNames + ioSlot]);
+ dmChassis.OutputVideoRouteNameFeedbacks[ioSlot].LinkInputSig(trilist.StringInput[joinMap.OutputCurrentVideoInputNames + ioSlot]);
+ dmChassis.OutputAudioRouteNameFeedbacks[ioSlot].LinkInputSig(trilist.StringInput[joinMap.OutputCurrentAudioInputNames + ioSlot]);
+ // dmChassis.InputEndpointOnlineFeedbacks[ioSlot].LinkInputSig(trilist.BooleanInput[joinMap.InputEndpointOnline + ioSlot]);
+ // dmChassis.OutputEndpointOnlineFeedbacks[ioSlot].LinkInputSig(trilist.BooleanInput[joinMap.OutputEndpointOnline + ioSlot]);
+ }
+ }
+
+
+ public class DmChassisControllerJoinMap : JoinMapBase
+ {
+ public uint IsOnline { get; set; }
+ public uint OutputVideo { get; set; }
+ public uint OutputAudio { get; set; }
+ public uint VideoSyncStatus { get; set; }
+ public uint InputNames { get; set; }
+ public uint OutputNames { get; set; }
+ public uint OutputCurrentVideoInputNames { get; set; }
+ public uint OutputCurrentAudioInputNames { get; set; }
+ public uint InputCurrentResolution { get; set; }
+ public uint InputEndpointOnline { get; set; }
+ public uint OutputEndpointOnline { get; set; }
+ //public uint HdcpSupport { get; set; }
+ //public uint HdcpSupportCapability { get; set; }
+
+
+ public DmChassisControllerJoinMap()
+ {
+ IsOnline = 11;
+ OutputVideo = 100; //101-299
+ OutputAudio = 300; //301-499
+ VideoSyncStatus = 100; //101-299
+ InputNames = 100; //101-299
+ OutputNames = 300; //301-499
+ OutputCurrentVideoInputNames = 2000; //2001-2199
+ OutputCurrentAudioInputNames = 2200; //2201-2399
+ InputCurrentResolution = 2400; // 2401-2599
+ InputEndpointOnline = 500;
+ OutputEndpointOnline = 700;
+ //HdcpSupport = 1000; //1001-1199
+ //HdcpSupportCapability = 1200; //1201-1399
+
+ }
+
+ public override void OffsetJoinNumbers(uint joinStart)
+ {
+ var joinOffset = joinStart - 1;
+
+ IsOnline = IsOnline + joinOffset;
+ OutputVideo = OutputVideo + joinOffset;
+ OutputAudio = OutputAudio + joinOffset;
+ VideoSyncStatus = VideoSyncStatus + joinOffset;
+ InputNames = InputNames + joinOffset;
+ OutputNames = OutputNames + joinOffset;
+ OutputCurrentVideoInputNames = OutputCurrentVideoInputNames + joinOffset;
+ OutputCurrentAudioInputNames = OutputCurrentAudioInputNames + joinOffset;
+ InputCurrentResolution = InputCurrentResolution + joinOffset;
+ InputEndpointOnline = InputEndpointOnline + joinOffset;
+ OutputEndpointOnline = OutputEndpointOnline + joinOffset;
+ //HdcpSupport = HdcpSupport + joinOffset;
+ //HdcpSupportCapability = HdcpSupportCapability + joinOffset;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/PepperDashEssentials/Bridges/DmRmcControllerBridge.cs b/PepperDashEssentials/Bridges/DmRmcControllerBridge.cs
new file mode 100644
index 00000000..318a3d0b
--- /dev/null
+++ b/PepperDashEssentials/Bridges/DmRmcControllerBridge.cs
@@ -0,0 +1,75 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Crestron.SimplSharp;
+using Crestron.SimplSharpPro.DeviceSupport;
+
+using PepperDash.Core;
+using PepperDash.Essentials.Core;
+using PepperDash.Essentials.DM;
+
+namespace PepperDash.Essentials.Bridges
+{
+ public static class DmRmcControllerApiExtensions
+ {
+ public static void LinkToApi(this DmRmcControllerBase rmc, BasicTriList trilist, uint joinStart, string joinMapKey)
+ {
+ var joinMap = JoinMapHelper.GetJoinMapForDevice(joinMapKey) as DmRmcControllerJoinMap;
+
+ if (joinMap == null)
+ joinMap = new DmRmcControllerJoinMap();
+
+ joinMap.OffsetJoinNumbers(joinStart);
+
+ Debug.Console(1, rmc, "Linking to Trilist '{0}'", trilist.ID.ToString("X"));
+
+ rmc.IsOnline.LinkInputSig(trilist.BooleanInput[joinMap.IsOnline]);
+ if(rmc.VideoOutputResolutionFeedback != null)
+ rmc.VideoOutputResolutionFeedback.LinkInputSig(trilist.StringInput[joinMap.CurrentOutputResolution]);
+ if(rmc.EdidManufacturerFeedback != null)
+ rmc.EdidManufacturerFeedback.LinkInputSig(trilist.StringInput[joinMap.EdidManufacturer]);
+ if(rmc.EdidNameFeedback != null)
+ rmc.EdidNameFeedback.LinkInputSig(trilist.StringInput[joinMap.EdidName]);
+ if(rmc.EdidPreferredTimingFeedback != null)
+ rmc.EdidPreferredTimingFeedback.LinkInputSig(trilist.StringInput[joinMap.EdidPrefferedTiming]);
+ if(rmc.EdidSerialNumberFeedback != null)
+ rmc.EdidSerialNumberFeedback.LinkInputSig(trilist.StringInput[joinMap.EdidSerialNumber]);
+ }
+
+ public class DmRmcControllerJoinMap : JoinMapBase
+ {
+ public uint IsOnline { get; set; }
+ public uint CurrentOutputResolution { get; set; }
+ public uint EdidManufacturer { get; set; }
+ public uint EdidName { get; set; }
+ public uint EdidPrefferedTiming { get; set; }
+ public uint EdidSerialNumber { get; set; }
+
+ public DmRmcControllerJoinMap()
+ {
+ // Digital
+ IsOnline = 1;
+
+ // Serial
+ CurrentOutputResolution = 1;
+ EdidManufacturer = 2;
+ EdidName = 3;
+ EdidPrefferedTiming = 4;
+ EdidSerialNumber = 5;
+ }
+
+ public override void OffsetJoinNumbers(uint joinStart)
+ {
+ var joinOffset = joinStart - 1;
+
+ IsOnline = IsOnline + joinOffset;
+ CurrentOutputResolution = CurrentOutputResolution + joinOffset;
+ EdidManufacturer = EdidManufacturer + joinOffset;
+ EdidName = EdidName + joinOffset;
+ EdidPrefferedTiming = EdidPrefferedTiming + joinOffset;
+ EdidSerialNumber = EdidSerialNumber + joinOffset;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/PepperDashEssentials/Bridges/DmTxControllerBridge.cs b/PepperDashEssentials/Bridges/DmTxControllerBridge.cs
new file mode 100644
index 00000000..26a6a939
--- /dev/null
+++ b/PepperDashEssentials/Bridges/DmTxControllerBridge.cs
@@ -0,0 +1,173 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Crestron.SimplSharp;
+using Crestron.SimplSharpPro.DM;
+using Crestron.SimplSharpPro.DM.Endpoints;
+using Crestron.SimplSharpPro.DM.Endpoints.Transmitters;
+using Crestron.SimplSharpPro.DeviceSupport;
+
+using PepperDash.Core;
+using PepperDash.Essentials.Core;
+using PepperDash.Essentials.DM;
+
+namespace PepperDash.Essentials.Bridges
+{
+ public static class DmTxControllerApiExtensions
+ {
+ public static void LinkToApi(this DmTxControllerBase tx, BasicTriList trilist, uint joinStart, string joinMapKey)
+ {
+ var joinMap = JoinMapHelper.GetJoinMapForDevice(joinMapKey) as DmTxControllerJoinMap;
+
+ if (joinMap == null)
+ joinMap = new DmTxControllerJoinMap();
+
+ joinMap.OffsetJoinNumbers(joinStart);
+
+ Debug.Console(1, tx, "Linking to Trilist '{0}'", trilist.ID.ToString("X"));
+
+ tx.IsOnline.LinkInputSig(trilist.BooleanInput[joinMap.IsOnline]);
+ tx.AnyVideoInput.VideoStatus.VideoSyncFeedback.LinkInputSig(trilist.BooleanInput[joinMap.VideoSyncStatus]);
+ tx.AnyVideoInput.VideoStatus.VideoResolutionFeedback.LinkInputSig(trilist.StringInput[joinMap.CurrentInputResolution]);
+ //tx.HdcpSupportAllFeedback.LinkInputSig(trilist.UShortInput[joinMap.HdcpSupportCapability]);
+
+ bool hdcpTypeSimple;
+
+ if (tx.Hardware is DmTx4kX02CBase || tx.Hardware is DmTx4kzX02CBase)
+ hdcpTypeSimple = false;
+ else
+ hdcpTypeSimple = true;
+
+ if (tx is ITxRouting)
+ {
+ var txR = tx as ITxRouting;
+
+ trilist.SetUShortSigAction(joinMap.VideoInput,
+ new Action(i => txR.ExecuteNumericSwitch(i, 0, eRoutingSignalType.Video)));
+ trilist.SetUShortSigAction(joinMap.AudioInput,
+ new Action(i => txR.ExecuteNumericSwitch(i, 0, eRoutingSignalType.Audio)));
+
+ txR.VideoSourceNumericFeedback.LinkInputSig(trilist.UShortInput[joinMap.VideoInput]);
+ txR.AudioSourceNumericFeedback.LinkInputSig(trilist.UShortInput[joinMap.AudioInput]);
+
+ trilist.UShortInput[joinMap.HdcpSupportCapability].UShortValue = (ushort)tx.HdcpSupportCapability;
+
+ if(txR.InputPorts[DmPortName.HdmiIn] != null)
+ {
+ var inputPort = txR.InputPorts[DmPortName.HdmiIn];
+
+ if (tx.Feedbacks["HdmiInHdcpCapability"] != null)
+ (tx.Feedbacks["HdmiInHdcpCapability"] as IntFeedback).LinkInputSig(trilist.UShortInput[joinMap.Port1HdcpState]);
+
+ if (inputPort.ConnectionType == eRoutingPortConnectionType.Hdmi && inputPort.Port != null)
+ {
+ var port = inputPort.Port as EndpointHdmiInput;
+
+ SetHdcpCapabilityAction(hdcpTypeSimple, port, joinMap.Port1HdcpState, trilist);
+ }
+ }
+
+ if (txR.InputPorts[DmPortName.HdmiIn1] != null)
+ {
+ var inputPort = txR.InputPorts[DmPortName.HdmiIn1];
+
+ if (tx.Feedbacks["HdmiIn1HdcpCapability"] != null)
+ (tx.Feedbacks["HdmiIn1HdcpCapability"] as IntFeedback).LinkInputSig(trilist.UShortInput[joinMap.Port1HdcpState]);
+
+ if (inputPort.ConnectionType == eRoutingPortConnectionType.Hdmi && inputPort.Port != null)
+ {
+ var port = inputPort.Port as EndpointHdmiInput;
+
+ SetHdcpCapabilityAction(hdcpTypeSimple, port, joinMap.Port1HdcpState, trilist);
+ }
+ }
+
+ if (txR.InputPorts[DmPortName.HdmiIn2] != null)
+ {
+ var inputPort = txR.InputPorts[DmPortName.HdmiIn2];
+
+ if (tx.Feedbacks["HdmiIn2HdcpCapability"] != null)
+ (tx.Feedbacks["HdmiIn2HdcpCapability"] as IntFeedback).LinkInputSig(trilist.UShortInput[joinMap.Port1HdcpState]);
+
+ if (inputPort.ConnectionType == eRoutingPortConnectionType.Hdmi && inputPort.Port != null)
+ {
+ var port = inputPort.Port as EndpointHdmiInput;
+
+ SetHdcpCapabilityAction(hdcpTypeSimple, port, joinMap.Port2HdcpState, trilist);
+ }
+ }
+
+ }
+ }
+
+ static void SetHdcpCapabilityAction(bool hdcpTypeSimple, EndpointHdmiInput port, uint join, BasicTriList trilist)
+ {
+ if (hdcpTypeSimple)
+ {
+ trilist.SetUShortSigAction(join,
+ new Action(s =>
+ {
+ if (s == 0)
+ {
+ port.HdcpSupportOff();
+ }
+ else if (s > 0)
+ {
+ port.HdcpSupportOn();
+ }
+ }));
+ }
+ else
+ {
+ trilist.SetUShortSigAction(join,
+ new Action(s =>
+ {
+ port.HdcpCapability = (eHdcpCapabilityType)s;
+ }));
+ }
+ }
+
+ public class DmTxControllerJoinMap : JoinMapBase
+ {
+ public uint IsOnline { get; set; }
+ public uint VideoSyncStatus { get; set; }
+ public uint CurrentInputResolution { get; set; }
+ public uint HdcpSupportCapability { get; set; }
+ public uint VideoInput { get; set; }
+ public uint AudioInput { get; set; }
+ public uint Port1HdcpState { get; set; }
+ public uint Port2HdcpState { get; set; }
+
+
+ public DmTxControllerJoinMap()
+ {
+ // Digital
+ IsOnline = 1;
+ VideoSyncStatus = 2;
+ // Serial
+ CurrentInputResolution = 1;
+ // Analog
+ VideoInput = 1;
+ AudioInput = 2;
+ HdcpSupportCapability = 3;
+ Port1HdcpState = 4;
+ Port2HdcpState = 5;
+ }
+
+ public override void OffsetJoinNumbers(uint joinStart)
+ {
+ var joinOffset = joinStart - 1;
+
+ IsOnline = IsOnline + joinOffset;
+ VideoSyncStatus = VideoSyncStatus + joinOffset;
+ CurrentInputResolution = CurrentInputResolution + joinOffset;
+ VideoInput = VideoInput + joinOffset;
+ AudioInput = AudioInput + joinOffset;
+ HdcpSupportCapability = HdcpSupportCapability + joinOffset;
+ Port1HdcpState = Port1HdcpState + joinOffset;
+ Port2HdcpState = Port2HdcpState + joinOffset;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/PepperDashEssentials/Bridges/DspControllerBridge.cs b/PepperDashEssentials/Bridges/DspControllerBridge.cs
new file mode 100644
index 00000000..9dd018fb
--- /dev/null
+++ b/PepperDashEssentials/Bridges/DspControllerBridge.cs
@@ -0,0 +1,120 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Crestron.SimplSharp;
+using Crestron.SimplSharpPro.DeviceSupport;
+using PepperDash.Core;
+using PepperDash.Essentials.Core;
+using PepperDash.Essentials.Devices.Common;
+
+namespace PepperDash.Essentials.Bridges
+{
+ public static class SamsungDisplayControllerApiExtensions
+ {
+ public static void LinkToApi(this PepperDash.Essentials.Core.TwoWayDisplayBase displayDevice, BasicTriList trilist, uint joinStart, string joinMapKey)
+ {
+ var joinMap = JoinMapHelper.GetJoinMapForDevice(joinMapKey) as DisplayControllerJoinMap;
+
+ if (joinMap == null)
+ {
+ joinMap = new DisplayControllerJoinMap();
+ }
+
+ joinMap.OffsetJoinNumbers(joinStart);
+ Debug.Console(1, "Linking to Trilist '{0}'", trilist.ID.ToString("X"));
+ Debug.Console(0, "Linking to lighting Type {0}", displayDevice.GetType().Name.ToString());
+
+ var commMonitor = displayDevice as ICommunicationMonitor;
+ commMonitor.CommunicationMonitor.IsOnlineFeedback.LinkInputSig(trilist.BooleanInput[joinMap.IsOnline]);
+
+
+ // Poewer Off
+ trilist.SetSigTrueAction(joinMap.PowerOff, () => displayDevice.PowerOff());
+ displayDevice.PowerIsOnFeedback.LinkComplementInputSig(trilist.BooleanInput[joinMap.PowerOff]);
+
+ // Poewer On
+ trilist.SetSigTrueAction(joinMap.PowerOn, () => displayDevice.PowerOn());
+ displayDevice.PowerIsOnFeedback.LinkInputSig(trilist.BooleanInput[joinMap.PowerOn]);
+
+ // GenericLighitng Actions & FeedBack
+
+ // int sceneIndex = 1;
+ /*
+ foreach (var scene in displayDevice.LightingScenes)
+ {
+ var tempIndex = sceneIndex - 1;
+ //trilist.SetSigTrueAction((uint)(joinMap.LightingSceneOffset + sceneIndex), () => displayDevice.SelectScene(displayDevice.LightingScenes[tempIndex]));
+ scene.IsActiveFeedback.LinkInputSig(trilist.BooleanInput[(uint)(joinMap.LightingSceneOffset + sceneIndex)]);
+ trilist.StringInput[(uint)(joinMap.LightingSceneOffset + sceneIndex)].StringValue = scene.Name;
+ trilist.BooleanInput[(uint)(joinMap.ButtonVisibilityOffset + sceneIndex)].BoolValue = true;
+ sceneIndex++;
+ }
+
+ if (displayDevice.GetType().Name.ToString() == "LutronQuantumArea")
+ {
+ var lutronDevice = displayDevice as PepperDash.Essentials.Devices.Common.Environment.Lutron.LutronQuantumArea;
+ lutronDevice.CommunicationMonitor.IsOnlineFeedback.LinkInputSig(trilist.BooleanInput[joinMap.IsOnline]);
+ trilist.SetStringSigAction(joinMap.IntegrationIdSet, s => lutronDevice.IntegrationId = s);
+ }
+ */
+ //ApiEisc.Eisc.SetStringSigAction(ApiMap.integrationID, (s) => { lutronLights.IntegrationId = s; });
+
+
+ /*
+ var lutronLights = displayDevice as PepperDash.Essentials.Devices.Common.Environment.Lutron.LutronQuantumArea;
+
+
+ for (uint i = 1; i <= lightingBase.CircuitCount; i++)
+ {
+ var circuit = i;
+ lightingBase.CircuitNameFeedbacks[circuit - 1].LinkInputSig(trilist.StringInput[joinMap.CircuitNames + circuit]);
+ lightingBase.CircuitIsCritical[circuit - 1].LinkInputSig(trilist.BooleanInput[joinMap.CircuitIsCritical + circuit]);
+ lightingBase.CircuitState[circuit - 1].LinkInputSig(trilist.BooleanInput[joinMap.CircuitState + circuit]);
+ trilist.SetSigTrueAction(joinMap.CircuitCycle + circuit, () => lightingBase.CycleCircuit(circuit - 1));
+ trilist.SetSigTrueAction(joinMap.CircuitOnCmd + circuit, () => lightingBase.TurnOnCircuit(circuit - 1));
+ trilist.SetSigTrueAction(joinMap.CircuitOffCmd + circuit, () => lightingBase.TurnOffCircuit(circuit - 1));
+
+ }
+ */
+ }
+ }
+ public class DisplayControllerJoinMap : JoinMapBase
+ {
+ public uint IsOnline { get; set; }
+ public uint PowerOff { get; set; }
+ public uint PowerOn { get; set; }
+ public uint SelectScene { get; set; }
+ public uint LightingSceneOffset { get; set; }
+ public uint ButtonVisibilityOffset { get; set; }
+ public uint IntegrationIdSet { get; set; }
+
+ public DisplayControllerJoinMap()
+ {
+ // Digital
+ IsOnline = 1;
+ PowerOff = 1;
+ PowerOn = 2;
+ SelectScene = 1;
+ IntegrationIdSet = 1;
+ LightingSceneOffset = 10;
+ ButtonVisibilityOffset = 40;
+ // Analog
+ }
+
+ public override void OffsetJoinNumbers(uint joinStart)
+ {
+ var joinOffset = joinStart - 1;
+
+ IsOnline = IsOnline + joinOffset;
+ PowerOff = PowerOff + joinOffset;
+ PowerOn = PowerOn + joinOffset;
+ SelectScene = SelectScene + joinOffset;
+ LightingSceneOffset = LightingSceneOffset + joinOffset;
+ ButtonVisibilityOffset = ButtonVisibilityOffset + joinOffset;
+
+
+
+ }
+ }
+}
\ No newline at end of file
diff --git a/PepperDashEssentials/Bridges/EssentialComms.cs b/PepperDashEssentials/Bridges/EssentialComms.cs
new file mode 100644
index 00000000..461715fc
--- /dev/null
+++ b/PepperDashEssentials/Bridges/EssentialComms.cs
@@ -0,0 +1,144 @@
+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;
+using Crestron.SimplSharpPro.CrestronThread;
+
+namespace PepperDash.Essentials {
+ public class EssentialCommConfig {
+ public string[] EiscApiIpids;
+ public EssentialCommCommConnectionConfigs[] CommConnections;
+ }
+ public class EssentialCommCommConnectionConfigs {
+ public uint joinNumber {get; set; }
+ public EssentialsControlPropertiesConfig control { get; set; }
+ }
+
+ public class EssentialCommsPort {
+ public IBasicCommunication Comm;
+ public IntFeedback StatusFeedback;
+ public BoolFeedback ConnectedFeedback;
+ public List Outputs = new List();
+ public String RxBuffer;
+ public EssentialCommsPort(EssentialsControlPropertiesConfig config, string keyPrefix) {
+ Comm = CommFactory.CreateCommForConfig(config, keyPrefix);
+ // var PortGather = new CommunicationGather(Comm, config.EndOfLineChar);
+ Comm.TextReceived += new EventHandler(Communication_TextReceived);
+
+ var socket = Comm as ISocketStatus;
+ StatusFeedback = new IntFeedback(() => { return (int)socket.ClientStatus; });
+ ConnectedFeedback = new BoolFeedback(() => { return Comm.IsConnected; });
+
+ if (socket != null) {
+ socket.ConnectionChange += new EventHandler(socket_ConnectionChange);
+ } else {
+ }
+
+ }
+ void socket_ConnectionChange(object sender, GenericSocketStatusChageEventArgs e) {
+ StatusFeedback.FireUpdate();
+ ConnectedFeedback.FireUpdate();
+ if (e.Client.IsConnected) {
+ // Tasks on connect
+ } else {
+ // Cleanup items from this session
+ }
+ }
+ void Communication_TextReceived(object sender, GenericCommMethodReceiveTextArgs args) {
+ try {
+ foreach (var Output in Outputs) {
+ Output.Api.Eisc.StringInput[Output.Join].StringValue = args.Text;
+ }
+
+ }
+ catch (Exception) {
+ throw new FormatException(string.Format("ERROR:{0}"));
+ }
+ }
+ }
+
+ public class EssentialComm : Device {
+ public EssentialCommConfig Properties;
+
+ public CommunicationGather PortGather { get; private set; }
+ public List Apis {get; set;}
+ public Dictionary CommFeedbacks {get; private set; }
+ public StatusMonitorBase CommunicationMonitor { get; private set; }
+ public Dictionary CommDictionary { get; private set; }
+
+ public EssentialComm(string key, string name, JToken properties) : base(key, name) {
+ Properties = JsonConvert.DeserializeObject(properties.ToString());
+ CommFeedbacks = new Dictionary();
+ CommDictionary = new Dictionary();
+ Apis = new List();
+ int commNumber = 1;
+ foreach (var commConfig in Properties.CommConnections) {
+ var commPort = new EssentialCommsPort(commConfig.control, string.Format("{0}-{1}", this.Key, commConfig.joinNumber));
+ CommDictionary.Add(commConfig.joinNumber, commPort);
+
+ commNumber++;
+ }
+
+ foreach (var Ipid in Properties.EiscApiIpids) {
+ var ApiEisc = new BridgeApiEisc(Ipid);
+ Apis.Add(ApiEisc);
+ foreach (var commConnection in CommDictionary) {
+ Debug.Console(2, "Joining Api{0} to comm {1}", Ipid, commConnection.Key);
+ var tempComm = commConnection.Value;
+ var tempJoin = (uint)commConnection.Key;
+ EssentialComApiMap ApiMap = new EssentialComApiMap(ApiEisc, (uint)tempJoin);
+
+ tempComm.Outputs.Add(ApiMap);
+ // Check for ApiMap Overide Values here
+
+ ApiEisc.Eisc.SetBoolSigAction(tempJoin, b => {if (b) { tempComm.Comm.Connect(); } else { tempComm.Comm.Disconnect(); }});
+ ApiEisc.Eisc.SetStringSigAction(tempJoin, s => tempComm.Comm.SendText(s));
+
+ tempComm.StatusFeedback.LinkInputSig(ApiEisc.Eisc.UShortInput[tempJoin]);
+ tempComm.ConnectedFeedback.LinkInputSig(ApiEisc.Eisc.BooleanInput[tempJoin]);
+
+
+
+ }
+ ApiEisc.Eisc.Register();
+ }
+ }
+
+
+
+ public override bool CustomActivate()
+ {
+ try {
+
+
+
+ Debug.Console(2, "Name {0} Activated", this.Name);
+ return true;
+ }
+ catch (Exception e) {
+ Debug.Console(0, "Bridge {0}", e);
+ return false;
+ }
+ }
+
+
+ }
+ public class EssentialComApiMap {
+ public uint Join;
+ public BridgeApiEisc Api;
+ public uint connectJoin;
+ public EssentialComApiMap(BridgeApiEisc api, uint join) {
+ Join = join;
+ Api = api;
+ }
+ }
+ }
\ No newline at end of file
diff --git a/PepperDashEssentials/Bridges/EssentialDM.cs b/PepperDashEssentials/Bridges/EssentialDM.cs
new file mode 100644
index 00000000..f03f174b
--- /dev/null
+++ b/PepperDashEssentials/Bridges/EssentialDM.cs
@@ -0,0 +1,150 @@
+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.Essentials.DM;
+using PepperDash.Core;
+using PepperDash.Essentials.Core.Routing;
+using Crestron.SimplSharpPro;
+using Crestron.SimplSharpPro.EthernetCommunication;
+using Crestron.SimplSharpPro.DM;
+
+namespace PepperDash.Essentials {
+ public class EssentialDM : PepperDash.Core.Device {
+ public EssentialDMProperties Properties;
+ public List BridgeApiEiscs;
+ private PepperDash.Essentials.DM.DmChassisController DmSwitch;
+ private EssentialDMApiMap ApiMap = new EssentialDMApiMap();
+ public EssentialDM(string key, string name, JToken properties)
+ : base(key, name) {
+ Properties = JsonConvert.DeserializeObject(properties.ToString());
+
+
+ }
+ public override bool CustomActivate() {
+ // Create EiscApis
+ try {
+ foreach (var device in DeviceManager.AllDevices) {
+ if (device.Key == this.Properties.connectionDeviceKey) {
+ Debug.Console(2, "deviceKey {0} Matches", device.Key);
+ DmSwitch = DeviceManager.GetDeviceForKey(device.Key) as PepperDash.Essentials.DM.DmChassisController;
+
+ }
+
+
+ else {
+ Debug.Console(2, "deviceKey {0} doesn't match", device.Key);
+ }
+ }
+ if (Properties.EiscApiIpids != null) {
+
+
+ foreach (string Ipid in Properties.EiscApiIpids) {
+ var ApiEisc = new BridgeApiEisc(Ipid);
+ for (uint x = 1; x <= DmSwitch.Chassis.NumberOfInputs;x++ ) {
+ uint tempX = x;
+ Debug.Console(2, "Creating EiscActions {0}", tempX);
+
+
+ ApiEisc.Eisc.SetUShortSigAction(ApiMap.OutputVideoRoutes[tempX], u => DmSwitch.ExecuteSwitch(u, tempX, eRoutingSignalType.Video));
+ ApiEisc.Eisc.SetUShortSigAction(ApiMap.OutputAudioRoutes[tempX], u => DmSwitch.ExecuteSwitch(u, tempX, eRoutingSignalType.Audio));
+
+
+ if (DmSwitch.TxDictionary.ContainsKey(tempX)) {
+ Debug.Console(2, "Creating Tx Feedbacks {0}", tempX);
+ var TxKey = DmSwitch.TxDictionary[tempX];
+ var TxDevice = DeviceManager.GetDeviceForKey(TxKey) as DmTxControllerBase;
+ TxDevice.IsOnline.LinkInputSig(ApiEisc.Eisc.BooleanInput[ApiMap.TxOnlineStatus[tempX]]);
+ TxDevice.AnyVideoInput.VideoStatus.VideoSyncFeedback.LinkInputSig(ApiEisc.Eisc.BooleanInput[ApiMap.TxVideoSyncStatus[tempX]]);
+ ApiEisc.Eisc.SetUShortSigAction((ApiMap.HdcpSupport[tempX]), u => TxDevice.SetHdcpSupportAll((ePdtHdcpSupport)(u)));
+ TxDevice.HdcpSupportAllFeedback.LinkInputSig(ApiEisc.Eisc.UShortInput[ApiMap.HdcpSupport[tempX]]);
+ ApiEisc.Eisc.UShortInput[ApiMap.HdcpSupportCapability[tempX]].UShortValue = TxDevice.HdcpSupportCapability;
+ }
+ else {
+ DmSwitch.VideoInputSyncFeedbacks[tempX].LinkInputSig(ApiEisc.Eisc.BooleanInput[ApiMap.TxVideoSyncStatus[tempX]]);
+ }
+ if (DmSwitch.RxDictionary.ContainsKey(tempX)) {
+ Debug.Console(2, "Creating Rx Feedbacks {0}", tempX);
+ var RxKey = DmSwitch.RxDictionary[tempX];
+ var RxDevice = DeviceManager.GetDeviceForKey(RxKey) as DmRmcControllerBase;
+ RxDevice.IsOnline.LinkInputSig(ApiEisc.Eisc.BooleanInput[ApiMap.RxOnlineStatus[tempX]]);
+ }
+ // DmSwitch.InputEndpointOnlineFeedbacks[(ushort)tempOutputNum].LinkInputSig(ApiEisc.Eisc.BooleanInput[ApiMap.OutputVideoRoutes[tempOutputNum]]);
+ DmSwitch.VideoOutputFeedbacks[(ushort)tempX].LinkInputSig(ApiEisc.Eisc.UShortInput[ApiMap.OutputVideoRoutes[tempX]]);
+ DmSwitch.AudioOutputFeedbacks[(ushort)tempX].LinkInputSig(ApiEisc.Eisc.UShortInput[ApiMap.OutputAudioRoutes[tempX]]);
+ DmSwitch.InputNameFeedbacks[(ushort)tempX].LinkInputSig(ApiEisc.Eisc.StringInput[ApiMap.InputNames[tempX]]);
+ DmSwitch.OutputNameFeedbacks[(ushort)tempX].LinkInputSig(ApiEisc.Eisc.StringInput[ApiMap.OutputNames[tempX]]);
+ DmSwitch.OutputRouteNameFeedbacks[(ushort)tempX].LinkInputSig(ApiEisc.Eisc.StringInput[ApiMap.OutputRouteNames[tempX]]);
+ }
+ DmSwitch.IsOnline.LinkInputSig(ApiEisc.Eisc.BooleanInput[ApiMap.ChassisOnline]);
+ ApiEisc.Eisc.Register();
+ }
+ }
+
+
+
+ Debug.Console(2, "Name {0} Activated", this.Name);
+ return true;
+ }
+ catch (Exception e) {
+ Debug.Console(2, "BRidge {0}", e);
+ return false;
+ }
+ }
+ }
+ public class EssentialDMProperties {
+ public string connectionDeviceKey;
+ public string[] EiscApiIpids;
+
+
+ }
+
+
+ public class EssentialDMApiMap {
+ public ushort ChassisOnline = 11;
+ public Dictionary OutputVideoRoutes;
+ public Dictionary OutputAudioRoutes;
+ public Dictionary TxOnlineStatus;
+ public Dictionary RxOnlineStatus;
+ public Dictionary TxVideoSyncStatus;
+ public Dictionary InputNames;
+ public Dictionary OutputNames;
+ public Dictionary OutputRouteNames;
+ public Dictionary HdcpSupport;
+ public Dictionary HdcpSupportCapability;
+
+ public EssentialDMApiMap() {
+ OutputVideoRoutes = new Dictionary();
+ OutputAudioRoutes = new Dictionary();
+ TxOnlineStatus = new Dictionary();
+ RxOnlineStatus = new Dictionary();
+ TxVideoSyncStatus = new Dictionary();
+ InputNames = new Dictionary();
+ OutputNames = new Dictionary();
+ OutputRouteNames = new Dictionary();
+ HdcpSupport = new Dictionary();
+ HdcpSupportCapability = new Dictionary();
+
+ for (uint x = 1; x <= 200; x++) {
+ // Debug.Console(0, "Init Value {0}", x);
+ uint tempNum = x;
+ HdcpSupportCapability[tempNum] = (ushort)(tempNum + 1200);
+ HdcpSupport[tempNum] = (ushort)(tempNum + 1000);
+ OutputVideoRoutes[tempNum] = (ushort)(tempNum + 100);
+ OutputAudioRoutes[tempNum] = (ushort)(tempNum + 300);
+ TxOnlineStatus[tempNum] = (ushort)(tempNum + 500);
+ RxOnlineStatus[tempNum] = (ushort)(tempNum + 700);
+ TxVideoSyncStatus[tempNum] = (ushort)(tempNum + 100);
+ InputNames[tempNum] = (ushort)(tempNum + 100);
+ OutputNames[tempNum] = (ushort)(tempNum + 300);
+ OutputRouteNames[tempNum] = (ushort)(tempNum + 2000);
+ }
+ }
+ }
+ }
+
\ No newline at end of file
diff --git a/PepperDashEssentials/Bridges/EssentialDsp.cs b/PepperDashEssentials/Bridges/EssentialDsp.cs
new file mode 100644
index 00000000..d8aed442
--- /dev/null
+++ b/PepperDashEssentials/Bridges/EssentialDsp.cs
@@ -0,0 +1,217 @@
+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.Essentials.DM;
+using PepperDash.Core;
+using PepperDash.Essentials.Core.Routing;
+using Crestron.SimplSharpPro;
+using Crestron.SimplSharpPro.EthernetCommunication;
+using Crestron.SimplSharpPro.DM;
+
+namespace PepperDash.Essentials {
+ public class EssentialDsp : PepperDash.Core.Device {
+ public EssentialDspProperties Properties;
+ public List BridgeApiEiscs;
+ private PepperDash.Essentials.Devices.Common.DSP.QscDsp Dsp;
+ private EssentialDspApiMap ApiMap = new EssentialDspApiMap();
+ public EssentialDsp(string key, string name, JToken properties)
+ : base(key, name) {
+ Properties = JsonConvert.DeserializeObject(properties.ToString());
+
+
+ }
+ public override bool CustomActivate() {
+ // Create EiscApis
+
+ try
+ {
+ ICommunicationMonitor comm = null;
+ foreach (var device in DeviceManager.AllDevices)
+ {
+ if (device.Key == this.Properties.connectionDeviceKey)
+ {
+ if (!(device is ICommunicationMonitor))
+ {
+ comm = device as ICommunicationMonitor;
+ }
+ Debug.Console(2, "deviceKey {0} Matches", device.Key);
+ Dsp = DeviceManager.GetDeviceForKey(device.Key) as PepperDash.Essentials.Devices.Common.DSP.QscDsp;
+ break;
+ }
+ else
+ {
+ Debug.Console(2, "deviceKey {0} doesn't match", device.Key);
+
+ }
+ }
+ if (Properties.EiscApiIpids != null && Dsp != null)
+ {
+ foreach (string Ipid in Properties.EiscApiIpids)
+ {
+ var ApiEisc = new BridgeApiEisc(Ipid);
+ Debug.Console(2, "Connecting EiscApi {0} to {1}", ApiEisc.Ipid, Dsp.Name);
+ ushort x = 1;
+ if (comm != null)
+ {
+ comm.CommunicationMonitor.IsOnlineFeedback.LinkInputSig(ApiEisc.Eisc.BooleanInput[ApiMap.Online]);
+ }
+ foreach (var channel in Dsp.LevelControlPoints)
+ {
+ //var QscChannel = channel.Value as PepperDash.Essentials.Devices.Common.DSP.QscDspLevelControl;
+ Debug.Console(2, "QscChannel {0} connect", x);
+
+ var genericChannel = channel.Value as IBasicVolumeWithFeedback;
+ if (channel.Value.Enabled)
+ {
+ ApiEisc.Eisc.StringInput[ApiMap.channelName[x]].StringValue = channel.Value.LevelCustomName;
+ ApiEisc.Eisc.UShortInput[ApiMap.channelType[x]].UShortValue = (ushort)channel.Value.Type;
+
+ genericChannel.MuteFeedback.LinkInputSig(ApiEisc.Eisc.BooleanInput[ApiMap.channelMuteToggle[x]]);
+ genericChannel.VolumeLevelFeedback.LinkInputSig(ApiEisc.Eisc.UShortInput[ApiMap.channelVolume[x]]);
+
+ ApiEisc.Eisc.SetSigTrueAction(ApiMap.channelMuteToggle[x], () => genericChannel.MuteToggle());
+ ApiEisc.Eisc.SetSigTrueAction(ApiMap.channelMuteOn[x], () => genericChannel.MuteOn());
+ ApiEisc.Eisc.SetSigTrueAction(ApiMap.channelMuteOff[x], () => genericChannel.MuteOff());
+
+ ApiEisc.Eisc.SetBoolSigAction(ApiMap.channelVolumeUp[x], b => genericChannel.VolumeUp(b));
+ ApiEisc.Eisc.SetBoolSigAction(ApiMap.channelVolumeDown[x], b => genericChannel.VolumeDown(b));
+
+ ApiEisc.Eisc.SetUShortSigAction(ApiMap.channelVolume[x], u => genericChannel.SetVolume(u));
+ ApiEisc.Eisc.SetStringSigAction(ApiMap.presetString, s => Dsp.RunPreset(s));
+ }
+ x++;
+
+ }
+ x = 1;
+ foreach (var preset in Dsp.PresetList)
+ {
+ ApiEisc.Eisc.StringInput[ApiMap.presets[x]].StringValue = preset.label;
+ ApiEisc.Eisc.SetSigTrueAction(ApiMap.presets[x], () => Dsp.RunPresetNumber(x));
+ x++;
+ }
+ foreach (var dialer in Dsp.Dialers)
+ {
+ ApiEisc.Eisc.SetSigTrueAction(ApiMap.Keypad0, () => dialer.Value.SendKeypad(PepperDash.Essentials.Devices.Common.DSP.QscDspDialer.eKeypadKeys.Num0));
+ ApiEisc.Eisc.SetSigTrueAction(ApiMap.Keypad1, () => dialer.Value.SendKeypad(PepperDash.Essentials.Devices.Common.DSP.QscDspDialer.eKeypadKeys.Num1));
+ ApiEisc.Eisc.SetSigTrueAction(ApiMap.Keypad2, () => dialer.Value.SendKeypad(PepperDash.Essentials.Devices.Common.DSP.QscDspDialer.eKeypadKeys.Num2));
+ ApiEisc.Eisc.SetSigTrueAction(ApiMap.Keypad3, () => dialer.Value.SendKeypad(PepperDash.Essentials.Devices.Common.DSP.QscDspDialer.eKeypadKeys.Num3));
+ ApiEisc.Eisc.SetSigTrueAction(ApiMap.Keypad4, () => dialer.Value.SendKeypad(PepperDash.Essentials.Devices.Common.DSP.QscDspDialer.eKeypadKeys.Num4));
+ ApiEisc.Eisc.SetSigTrueAction(ApiMap.Keypad5, () => dialer.Value.SendKeypad(PepperDash.Essentials.Devices.Common.DSP.QscDspDialer.eKeypadKeys.Num5));
+ ApiEisc.Eisc.SetSigTrueAction(ApiMap.Keypad6, () => dialer.Value.SendKeypad(PepperDash.Essentials.Devices.Common.DSP.QscDspDialer.eKeypadKeys.Num6));
+ ApiEisc.Eisc.SetSigTrueAction(ApiMap.Keypad7, () => dialer.Value.SendKeypad(PepperDash.Essentials.Devices.Common.DSP.QscDspDialer.eKeypadKeys.Num7));
+ ApiEisc.Eisc.SetSigTrueAction(ApiMap.Keypad8, () => dialer.Value.SendKeypad(PepperDash.Essentials.Devices.Common.DSP.QscDspDialer.eKeypadKeys.Num8));
+ ApiEisc.Eisc.SetSigTrueAction(ApiMap.Keypad9, () => dialer.Value.SendKeypad(PepperDash.Essentials.Devices.Common.DSP.QscDspDialer.eKeypadKeys.Num9));
+ ApiEisc.Eisc.SetSigTrueAction(ApiMap.KeypadStar, () => dialer.Value.SendKeypad(PepperDash.Essentials.Devices.Common.DSP.QscDspDialer.eKeypadKeys.Star));
+ ApiEisc.Eisc.SetSigTrueAction(ApiMap.KeypadPound, () => dialer.Value.SendKeypad(PepperDash.Essentials.Devices.Common.DSP.QscDspDialer.eKeypadKeys.Pound));
+ ApiEisc.Eisc.SetSigTrueAction(ApiMap.KeypadClear, () => dialer.Value.SendKeypad(PepperDash.Essentials.Devices.Common.DSP.QscDspDialer.eKeypadKeys.Clear));
+ ApiEisc.Eisc.SetSigTrueAction(ApiMap.KeypadBackspace, () => dialer.Value.SendKeypad(PepperDash.Essentials.Devices.Common.DSP.QscDspDialer.eKeypadKeys.Backspace));
+
+ ApiEisc.Eisc.SetSigTrueAction(ApiMap.Dial, () => dialer.Value.Dial());
+ ApiEisc.Eisc.SetSigTrueAction(ApiMap.DoNotDisturbToggle, () => dialer.Value.DoNotDisturbToggle());
+ ApiEisc.Eisc.SetSigTrueAction(ApiMap.DoNotDisturbOn, () => dialer.Value.DoNotDisturbOn());
+ ApiEisc.Eisc.SetSigTrueAction(ApiMap.DoNotDisturbOff, () => dialer.Value.DoNotDisturbOff());
+ ApiEisc.Eisc.SetSigTrueAction(ApiMap.AutoAnswerToggle, () => dialer.Value.AutoAnswerToggle());
+ ApiEisc.Eisc.SetSigTrueAction(ApiMap.AutoAnswerOn, () => dialer.Value.AutoAnswerOn());
+ ApiEisc.Eisc.SetSigTrueAction(ApiMap.AutoAnswerOff, () => dialer.Value.AutoAnswerOff());
+
+ dialer.Value.DoNotDisturbFeedback.LinkInputSig(ApiEisc.Eisc.BooleanInput[ApiMap.DoNotDisturbToggle]);
+ dialer.Value.DoNotDisturbFeedback.LinkInputSig(ApiEisc.Eisc.BooleanInput[ApiMap.DoNotDisturbOn]);
+ dialer.Value.DoNotDisturbFeedback.LinkComplementInputSig(ApiEisc.Eisc.BooleanInput[ApiMap.DoNotDisturbOff]);
+
+ dialer.Value.AutoAnswerFeedback.LinkInputSig(ApiEisc.Eisc.BooleanInput[ApiMap.AutoAnswerToggle]);
+ dialer.Value.AutoAnswerFeedback.LinkInputSig(ApiEisc.Eisc.BooleanInput[ApiMap.AutoAnswerOn]);
+ dialer.Value.AutoAnswerFeedback.LinkComplementInputSig(ApiEisc.Eisc.BooleanInput[ApiMap.AutoAnswerOff]);
+
+ dialer.Value.OffHookFeedback.LinkInputSig(ApiEisc.Eisc.BooleanInput[ApiMap.Dial]);
+ dialer.Value.DialStringFeedback.LinkInputSig(ApiEisc.Eisc.StringInput[ApiMap.DialString]);
+ }
+ }
+ }
+
+
+
+
+ Debug.Console(2, "Name {0} Activated", this.Name);
+ return true;
+ }
+ catch (Exception e) {
+ Debug.Console(0, "Bridge {0}", e);
+ return false;
+ }
+ }
+ }
+ public class EssentialDspProperties {
+ public string connectionDeviceKey;
+ public string[] EiscApiIpids;
+
+
+ }
+
+
+ public class EssentialDspApiMap {
+ public ushort Online = 1;
+ public ushort presetString = 2000;
+ public Dictionary channelMuteToggle;
+ public Dictionary channelMuteOn;
+ public Dictionary channelMuteOff;
+ public Dictionary channelVolume;
+ public Dictionary channelType;
+ public Dictionary channelName;
+ public Dictionary channelVolumeUp;
+ public Dictionary channelVolumeDown;
+ public Dictionary presets;
+ public ushort DialString = 3100;
+ public ushort Keypad0 = 3110;
+ public ushort Keypad1 = 3111;
+ public ushort Keypad2 = 3112;
+ public ushort Keypad3 = 3113;
+ public ushort Keypad4 = 3114;
+ public ushort Keypad5 = 3115;
+ public ushort Keypad6 = 3116;
+ public ushort Keypad7 = 3117;
+ public ushort Keypad8 = 3118;
+ public ushort Keypad9 = 3119;
+ public ushort KeypadStar = 3120;
+ public ushort KeypadPound = 3121;
+ public ushort KeypadClear = 3122;
+ public ushort KeypadBackspace = 3123;
+ public ushort Dial = 3124;
+ public ushort DoNotDisturbToggle = 3132;
+ public ushort DoNotDisturbOn = 3133;
+ public ushort DoNotDisturbOff = 3134;
+ public ushort AutoAnswerToggle = 3127;
+ public ushort AutoAnswerOn = 3125;
+ public ushort AutoAnswerOff = 3126;
+
+ public EssentialDspApiMap() {
+ channelMuteToggle = new Dictionary();
+ channelMuteOn = new Dictionary();
+ channelMuteOff = new Dictionary();
+ channelVolume = new Dictionary();
+ channelName = new Dictionary();
+ channelType = new Dictionary();
+ presets = new Dictionary();
+ channelVolumeUp = new Dictionary();
+ channelVolumeDown = new Dictionary();
+ for (uint x = 1; x <= 100; x++) {
+ uint tempNum = x;
+ presets[tempNum] = (ushort)(tempNum + 100);
+ channelMuteToggle[tempNum] = (ushort)(tempNum + 400);
+ channelMuteOn[tempNum] = (ushort)(tempNum + 600);
+ channelMuteOff[tempNum] = (ushort)(tempNum + 800);
+ channelVolume[tempNum] = (ushort)(tempNum + 200);
+ channelName[tempNum] = (ushort)(tempNum + 200);
+ channelType[tempNum] = (ushort)(tempNum + 400);
+ channelVolumeUp[tempNum] = (ushort)(tempNum + 1000);
+ channelVolumeDown[tempNum] = (ushort)(tempNum + 1200);
+ }
+ }
+ }
+ }
+
\ No newline at end of file
diff --git a/PepperDashEssentials/Bridges/EssentialTVOne.cs b/PepperDashEssentials/Bridges/EssentialTVOne.cs
new file mode 100644
index 00000000..12fb0c49
--- /dev/null
+++ b/PepperDashEssentials/Bridges/EssentialTVOne.cs
@@ -0,0 +1,98 @@
+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.Essentials.DM;
+using PepperDash.Core;
+using PepperDash.Essentials.Core.Routing;
+using Crestron.SimplSharpPro;
+using Crestron.SimplSharpPro.EthernetCommunication;
+using Crestron.SimplSharpPro.DM;
+
+namespace PepperDash.Essentials
+{
+ public class EssentialsTVOne : PepperDash.Core.Device
+ {
+ public EssentialTVOneProperties Properties;
+ public List BridgeApiEiscs;
+ private PepperDash.Essentials.Devices.Common.TVOneCorio TVOneCorio;
+ private EssentialsTVOneApiMap ApiMap = new EssentialsTVOneApiMap();
+ public EssentialsTVOne(string key, string name, JToken properties)
+ : base(key, name)
+ {
+ Properties = JsonConvert.DeserializeObject(properties.ToString());
+
+
+ }
+ public override bool CustomActivate() {
+ // Create EiscApis
+ try
+ {
+ foreach (var device in DeviceManager.AllDevices)
+ {
+ if (device.Key == this.Properties.connectionDeviceKey)
+ {
+ Debug.Console(2, "deviceKey {0} Matches", device.Key);
+ TVOneCorio = DeviceManager.GetDeviceForKey(device.Key) as PepperDash.Essentials.Devices.Common.TVOneCorio;
+ break;
+ }
+ else
+ {
+ Debug.Console(2, "deviceKey {0} doesn't match", device.Key);
+
+ }
+ }
+ if (Properties.EiscApiIpids != null && TVOneCorio != null)
+ {
+ foreach (string Ipid in Properties.EiscApiIpids)
+ {
+ var ApiEisc = new BridgeApiEisc(Ipid);
+ Debug.Console(2, "Connecting EiscApi {0} to {1}", ApiEisc.Ipid, TVOneCorio.Name);
+ ushort x = 1;
+ TVOneCorio.OnlineFeedback.LinkInputSig(ApiEisc.Eisc.BooleanInput[ApiMap.Online]);
+ ApiEisc.Eisc.SetUShortSigAction(ApiMap.CallPreset, u => TVOneCorio.CallPreset(u));
+ TVOneCorio.PresetFeedback.LinkInputSig(ApiEisc.Eisc.UShortInput[ApiMap.PresetFeedback]);
+
+ }
+ }
+
+
+
+
+ Debug.Console(2, "Name {0} Activated", this.Name);
+ return true;
+ }
+ catch (Exception e) {
+ Debug.Console(0, "Bridge {0}", e);
+ return false;
+ }
+ }
+ }
+ public class EssentialTVOneProperties
+ {
+ public string connectionDeviceKey;
+ public string[] EiscApiIpids;
+
+
+ }
+
+
+ public class EssentialsTVOneApiMap
+ {
+ public ushort CallPreset = 1;
+ public ushort PresetFeedback = 1;
+ public ushort Online = 1;
+
+ public EssentialsTVOneApiMap()
+ {
+
+
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/PepperDashEssentials/Bridges/GenericLightingBridge.cs b/PepperDashEssentials/Bridges/GenericLightingBridge.cs
new file mode 100644
index 00000000..c93a14fd
--- /dev/null
+++ b/PepperDashEssentials/Bridges/GenericLightingBridge.cs
@@ -0,0 +1,103 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Crestron.SimplSharp;
+using Crestron.SimplSharpPro.DeviceSupport;
+using PepperDash.Core;
+using PepperDash.Essentials.Core;
+using PepperDash.Essentials.Devices.Common;
+
+namespace PepperDash.Essentials.Bridges
+{
+ public static class GenericLightingApiExtensions
+ {
+ public static void LinkToApi(this PepperDash.Essentials.Core.Lighting.LightingBase lightingDevice, BasicTriList trilist, uint joinStart, string joinMapKey)
+ {
+ var joinMap = JoinMapHelper.GetJoinMapForDevice(joinMapKey) as GenericLightingJoinMap;
+
+ if (joinMap == null)
+ joinMap = new GenericLightingJoinMap();
+
+ joinMap.OffsetJoinNumbers(joinStart);
+ Debug.Console(1, "Linking to Trilist '{0}'", trilist.ID.ToString("X"));
+
+
+
+ Debug.Console(0, "Linking to lighting Type {0}", lightingDevice.GetType().Name.ToString());
+
+ // GenericLighitng Actions & FeedBack
+ trilist.SetUShortSigAction(joinMap.SelectScene, u => lightingDevice.SelectScene(lightingDevice.LightingScenes[u]));
+
+ int sceneIndex = 1;
+ foreach (var scene in lightingDevice.LightingScenes)
+ {
+ var tempIndex = sceneIndex - 1;
+ trilist.SetSigTrueAction((uint)(joinMap.LightingSceneOffset + sceneIndex), () => lightingDevice.SelectScene(lightingDevice.LightingScenes[tempIndex]));
+ scene.IsActiveFeedback.LinkInputSig(trilist.BooleanInput[(uint)(joinMap.LightingSceneOffset + sceneIndex)]);
+ trilist.StringInput[(uint)(joinMap.LightingSceneOffset + sceneIndex)].StringValue = scene.Name;
+ trilist.BooleanInput[(uint)(joinMap.ButtonVisibilityOffset + sceneIndex)].BoolValue = true;
+ sceneIndex++;
+ }
+
+ if (lightingDevice.GetType().Name.ToString() == "LutronQuantumArea")
+ {
+ var lutronDevice = lightingDevice as PepperDash.Essentials.Devices.Common.Environment.Lutron.LutronQuantumArea;
+ lutronDevice.CommunicationMonitor.IsOnlineFeedback.LinkInputSig(trilist.BooleanInput[joinMap.IsOnline]);
+ trilist.SetStringSigAction(joinMap.IntegrationIdSet, s => lutronDevice.IntegrationId = s);
+ }
+
+ //ApiEisc.Eisc.SetStringSigAction(ApiMap.integrationID, (s) => { lutronLights.IntegrationId = s; });
+
+
+ /*
+ var lutronLights = lightingDevice as PepperDash.Essentials.Devices.Common.Environment.Lutron.LutronQuantumArea;
+
+
+ for (uint i = 1; i <= lightingBase.CircuitCount; i++)
+ {
+ var circuit = i;
+ lightingBase.CircuitNameFeedbacks[circuit - 1].LinkInputSig(trilist.StringInput[joinMap.CircuitNames + circuit]);
+ lightingBase.CircuitIsCritical[circuit - 1].LinkInputSig(trilist.BooleanInput[joinMap.CircuitIsCritical + circuit]);
+ lightingBase.CircuitState[circuit - 1].LinkInputSig(trilist.BooleanInput[joinMap.CircuitState + circuit]);
+ trilist.SetSigTrueAction(joinMap.CircuitCycle + circuit, () => lightingBase.CycleCircuit(circuit - 1));
+ trilist.SetSigTrueAction(joinMap.CircuitOnCmd + circuit, () => lightingBase.TurnOnCircuit(circuit - 1));
+ trilist.SetSigTrueAction(joinMap.CircuitOffCmd + circuit, () => lightingBase.TurnOffCircuit(circuit - 1));
+
+ }
+ */
+ }
+ }
+ public class GenericLightingJoinMap : JoinMapBase
+ {
+ public uint IsOnline { get; set; }
+ public uint SelectScene { get; set; }
+ public uint LightingSceneOffset { get; set; }
+ public uint ButtonVisibilityOffset { get; set; }
+ public uint IntegrationIdSet { get; set; }
+
+ public GenericLightingJoinMap()
+ {
+ // Digital
+ IsOnline = 1;
+ SelectScene = 1;
+ IntegrationIdSet = 1;
+ LightingSceneOffset = 10;
+ ButtonVisibilityOffset = 40;
+ // Analog
+ }
+
+ public override void OffsetJoinNumbers(uint joinStart)
+ {
+ var joinOffset = joinStart - 1;
+
+ IsOnline = IsOnline + joinOffset;
+ SelectScene = SelectScene + joinOffset;
+ LightingSceneOffset = LightingSceneOffset + joinOffset;
+ ButtonVisibilityOffset = ButtonVisibilityOffset + joinOffset;
+
+
+
+ }
+ }
+}
\ No newline at end of file
diff --git a/PepperDashEssentials/Bridges/GenericRelayDeviceBridge.cs b/PepperDashEssentials/Bridges/GenericRelayDeviceBridge.cs
new file mode 100644
index 00000000..3871a80e
--- /dev/null
+++ b/PepperDashEssentials/Bridges/GenericRelayDeviceBridge.cs
@@ -0,0 +1,66 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Crestron.SimplSharp;
+using Crestron.SimplSharpPro.DeviceSupport;
+
+
+using PepperDash.Core;
+using PepperDash.Essentials.Core;
+using PepperDash.Essentials.Core.CrestronIO;
+
+namespace PepperDash.Essentials.Bridges
+{
+ public static class GenericRelayDeviceApiExtensions
+ {
+ public static void LinkToApi(this GenericRelayDevice relay, BasicTriList trilist, uint joinStart, string joinMapKey)
+ {
+ var joinMap = JoinMapHelper.GetJoinMapForDevice(joinMapKey) as GenericRelayControllerJoinMap;
+
+ if (joinMap == null)
+ joinMap = new GenericRelayControllerJoinMap();
+
+ joinMap.OffsetJoinNumbers(joinStart);
+
+ if (relay.RelayOutput == null)
+ {
+ Debug.Console(1, relay, "Unable to link device '{0}'. Relay is null", relay.Key);
+ return;
+ }
+
+ Debug.Console(1, relay, "Linking to Trilist '{0}'", trilist.ID.ToString("X"));
+
+ trilist.SetBoolSigAction(joinMap.Relay, new Action(b =>
+ {
+ if (b)
+ relay.CloseRelay();
+ else
+ relay.OpenRelay();
+ }));
+
+ // feedback for relay state
+
+ relay.OutputIsOnFeedback.LinkInputSig(trilist.BooleanInput[joinMap.Relay]);
+ }
+
+ }
+
+ public class GenericRelayControllerJoinMap : JoinMapBase
+ {
+ //Digital
+ public uint Relay { get; set; }
+
+ public GenericRelayControllerJoinMap()
+ {
+ Relay = 1;
+ }
+
+ public override void OffsetJoinNumbers(uint joinStart)
+ {
+ var joinOffset = joinStart - 1;
+
+ Relay = Relay + joinOffset;
+ }
+ }
+}
\ No newline at end of file
diff --git a/PepperDashEssentials/Bridges/IBasicCommunicationBridge.cs b/PepperDashEssentials/Bridges/IBasicCommunicationBridge.cs
new file mode 100644
index 00000000..8eeed161
--- /dev/null
+++ b/PepperDashEssentials/Bridges/IBasicCommunicationBridge.cs
@@ -0,0 +1,103 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Crestron.SimplSharp;
+using Crestron.SimplSharpPro.DeviceSupport;
+
+using PepperDash.Core;
+using PepperDash.Essentials.Core;
+
+namespace PepperDash.Essentials.Bridges
+{
+ public static class IBasicCommunicationApiExtensions
+ {
+ public static void LinkToApi(this GenericComm comm, BasicTriList trilist, uint joinStart, string joinMapKey)
+ {
+ var joinMap = JoinMapHelper.GetJoinMapForDevice(joinMapKey) as IBasicCommunicationJoinMap;
+
+ if (joinMap == null)
+ joinMap = new IBasicCommunicationJoinMap();
+
+ joinMap.OffsetJoinNumbers(joinStart);
+
+ if (comm.CommPort == null)
+ {
+ Debug.Console(1, comm, "Unable to link device '{0}'. CommPort is null", comm.Key);
+ return;
+ }
+
+ Debug.Console(1, comm, "Linking to Trilist '{0}'", trilist.ID.ToString("X"));
+
+ // this is a permanent event handler. This cannot be -= from event
+ comm.CommPort.TextReceived += (s, a) => trilist.SetString(joinMap.TextReceived, a.Text);
+ trilist.SetStringSigAction(joinMap.SendText, new Action(s => comm.CommPort.SendText(s)));
+ trilist.SetStringSigAction(joinMap.SetPortConfig + 1, new Action(s => comm.SetPortConfig(s)));
+
+
+ var sComm = comm.CommPort as ISocketStatus;
+ if (sComm != null)
+ {
+ sComm.ConnectionChange += (s, a) =>
+ {
+ trilist.SetUshort(joinMap.Status, (ushort)(a.Client.ClientStatus));
+ trilist.SetBool(joinMap.Connected, a.Client.ClientStatus ==
+ Crestron.SimplSharp.CrestronSockets.SocketStatus.SOCKET_STATUS_CONNECTED);
+ };
+
+ trilist.SetBoolSigAction(joinMap.Connect, new Action(b =>
+ {
+ if (b)
+ {
+ sComm.Connect();
+ }
+ else
+ {
+ sComm.Disconnect();
+ }
+ }));
+ }
+ }
+
+
+
+ public class IBasicCommunicationJoinMap : JoinMapBase
+ {
+ //Digital
+ public uint Connect { get; set; }
+ public uint Connected { get; set; }
+
+ //Analog
+ public uint Status { get; set; }
+
+ // Serial
+ public uint TextReceived { get; set; }
+ public uint SendText { get; set; }
+ public uint SetPortConfig { get; set; }
+
+
+ public IBasicCommunicationJoinMap()
+ {
+ TextReceived = 1;
+ SendText = 1;
+ SetPortConfig = 2;
+ Connect = 1;
+ Connected = 1;
+ Status = 1;
+ }
+
+ public override void OffsetJoinNumbers(uint joinStart)
+ {
+ var joinOffset = joinStart - 1;
+
+ TextReceived = TextReceived + joinOffset;
+ SendText = SendText + joinOffset;
+ SetPortConfig = SetPortConfig + joinOffset;
+ Connect = Connect + joinOffset;
+ Connected = Connected + joinOffset;
+ Status = Status + joinOffset;
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/PepperDashEssentials/Bridges/IDigitalInputBridge.cs b/PepperDashEssentials/Bridges/IDigitalInputBridge.cs
new file mode 100644
index 00000000..c33329dd
--- /dev/null
+++ b/PepperDashEssentials/Bridges/IDigitalInputBridge.cs
@@ -0,0 +1,59 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Crestron.SimplSharp;
+using Crestron.SimplSharpPro.DeviceSupport;
+
+using PepperDash.Core;
+using PepperDash.Essentials.Core.CrestronIO;
+
+namespace PepperDash.Essentials.Bridges
+{
+ public static class IDigitalInputApiExtenstions
+ {
+ public static void LinkToApi(this IDigitalInput input, BasicTriList trilist, uint joinStart, string joinMapKey)
+ {
+ var joinMap = JoinMapHelper.GetJoinMapForDevice(joinMapKey) as IDigitalInputApiJoinMap;
+
+ if (joinMap == null)
+ joinMap = new IDigitalInputApiJoinMap();
+
+ joinMap.OffsetJoinNumbers(joinStart);
+
+ try
+ {
+ Debug.Console(1, input as Device, "Linking to Trilist '{0}'", trilist.ID.ToString("X"));
+
+ // Link feedback for input state
+ input.InputStateFeedback.LinkInputSig(trilist.BooleanInput[joinMap.InputState]);
+ }
+ catch (Exception e)
+ {
+ Debug.Console(1, input as Device, "Unable to link device '{0}'. Input is null", (input as Device).Key);
+ Debug.Console(1, input as Device, "Error: {0}", e);
+ return;
+ }
+ }
+
+
+ }
+
+ public class IDigitalInputApiJoinMap : JoinMapBase
+ {
+ //Digital
+ public uint InputState { get; set; }
+
+ public IDigitalInputApiJoinMap()
+ {
+ InputState = 1;
+ }
+
+ public override void OffsetJoinNumbers(uint joinStart)
+ {
+ var joinOffset = joinStart - 1;
+
+ InputState = InputState + joinOffset;
+ }
+ }
+}
\ No newline at end of file
diff --git a/PepperDashEssentials/Bridges/JoinMapBase.cs b/PepperDashEssentials/Bridges/JoinMapBase.cs
new file mode 100644
index 00000000..13d08a3f
--- /dev/null
+++ b/PepperDashEssentials/Bridges/JoinMapBase.cs
@@ -0,0 +1,40 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Crestron.SimplSharp;
+
+namespace PepperDash.Essentials.Bridges
+{
+ public static class JoinMapHelper
+ {
+ ///
+ /// Attempts to get the join map from config
+ ///
+ ///
+ ///
+ public static JoinMapBase GetJoinMapForDevice(string joinMapKey)
+ {
+ if (!string.IsNullOrEmpty(joinMapKey))
+ return null;
+
+ // FUTURE TODO: Get the join map from the ConfigReader.ConfigObject
+
+ return null;
+ }
+ }
+
+
+ public abstract class JoinMapBase
+ {
+ ///
+ /// Modifies all the join numbers by adding the offset. This should never be called twice
+ ///
+ ///
+ public abstract void OffsetJoinNumbers(uint joinStart);
+
+
+ }
+
+
+}
\ No newline at end of file
diff --git a/PepperDashEssentials/Bridges/SystemMonitorBridge.cs b/PepperDashEssentials/Bridges/SystemMonitorBridge.cs
new file mode 100644
index 00000000..4b8829b2
--- /dev/null
+++ b/PepperDashEssentials/Bridges/SystemMonitorBridge.cs
@@ -0,0 +1,150 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Crestron.SimplSharp;
+using Crestron.SimplSharpPro.DeviceSupport;
+using Crestron.SimplSharpPro.Diagnostics;
+using PepperDash.Core;
+using PepperDash.Essentials.Core;
+using PepperDash.Essentials.Core.Monitoring;
+
+namespace PepperDash.Essentials.Bridges
+{
+ public static class SystemMonitorBridge
+ {
+ public static void LinkToApi(this SystemMonitorController systemMonitorController, BasicTriList trilist, uint joinStart, string joinMapKey)
+ {
+ var joinMap = JoinMapHelper.GetJoinMapForDevice(joinMapKey) as SystemMonitorJoinMap;
+
+ if (joinMap == null)
+ joinMap = new SystemMonitorJoinMap();
+
+ joinMap.OffsetJoinNumbers(joinStart);
+
+ //Debug.Console(1, systemMonitorController, "Linking API starting at join: {0}", joinStart);
+
+ systemMonitorController.TimeZoneFeedback.LinkInputSig(trilist.UShortInput[joinMap.TimeZone]);
+ //trilist.SetUShortSigAction(joinMap.TimeZone, new Action(u => systemMonitorController.SetTimeZone(u)));
+ systemMonitorController.TimeZoneTextFeedback.LinkInputSig(trilist.StringInput[joinMap.TimeZoneName]);
+
+ systemMonitorController.IOControllerVersionFeedback.LinkInputSig(trilist.StringInput[joinMap.IOControllerVersion]);
+ systemMonitorController.SnmpVersionFeedback.LinkInputSig(trilist.StringInput[joinMap.SnmpAppVersion]);
+ systemMonitorController.BACnetAppVersionFeedback.LinkInputSig(trilist.StringInput[joinMap.BACnetAppVersion]);
+ systemMonitorController.ControllerVersionFeedback.LinkInputSig(trilist.StringInput[joinMap.ControllerVersion]);
+
+
+ // iterate the program status feedback collection and map all the joins
+ var programSlotJoinStart = joinMap.ProgramStartJoin;
+
+ foreach (var p in systemMonitorController.ProgramStatusFeedbackCollection)
+ {
+
+ // TODO: link feedbacks for each program slot
+ var programNumber = p.Value.Program.Number;
+
+ //Debug.Console(1, systemMonitorController, "Linking API for Program Slot: {0} starting at join: {1}", programNumber, programSlotJoinStart);
+
+ trilist.SetBoolSigAction(programSlotJoinStart + joinMap.ProgramStart, new Action
+ (b => SystemMonitor.ProgramCollection[programNumber].OperatingState = eProgramOperatingState.Start));
+ p.Value.ProgramStartedFeedback.LinkInputSig(trilist.BooleanInput[programSlotJoinStart + joinMap.ProgramStart]);
+
+ trilist.SetBoolSigAction(programSlotJoinStart + joinMap.ProgramStop, new Action
+ (b => SystemMonitor.ProgramCollection[programNumber].OperatingState = eProgramOperatingState.Stop));
+ p.Value.ProgramStoppedFeedback.LinkInputSig(trilist.BooleanInput[programSlotJoinStart + joinMap.ProgramStop]);
+
+ trilist.SetBoolSigAction(programSlotJoinStart + joinMap.ProgramRegister, new Action
+ (b => SystemMonitor.ProgramCollection[programNumber].RegistrationState = eProgramRegistrationState.Register));
+ p.Value.ProgramRegisteredFeedback.LinkInputSig(trilist.BooleanInput[programSlotJoinStart + joinMap.ProgramRegister]);
+
+ trilist.SetBoolSigAction(programSlotJoinStart + joinMap.ProgramUnregister, new Action
+ (b => SystemMonitor.ProgramCollection[programNumber].RegistrationState = eProgramRegistrationState.Unregister));
+ p.Value.ProgramUnregisteredFeedback.LinkInputSig(trilist.BooleanInput[programSlotJoinStart + joinMap.ProgramUnregister]);
+
+ programSlotJoinStart = programSlotJoinStart + joinMap.ProgramOffsetJoin;
+ }
+
+ }
+
+
+ }
+
+ public class SystemMonitorJoinMap : JoinMapBase
+ {
+ ///
+ /// Offset to indicate where the range of iterated program joins will start
+ ///
+ public uint ProgramStartJoin { get; set; }
+
+ ///
+ /// Offset between each program join set
+ ///
+ public uint ProgramOffsetJoin { get; set; }
+
+ //Digital
+ public uint ProgramStart { get; set; }
+ public uint ProgramStop { get; set; }
+ public uint ProgramRegister { get; set; }
+ public uint ProgramUnregister { get; set; }
+
+ //Analog
+ public uint TimeZone { get; set; }
+
+ //Serial
+ public uint TimeZoneName { get; set; }
+ public uint IOControllerVersion { get; set; }
+ public uint SnmpAppVersion { get; set; }
+ public uint BACnetAppVersion { get; set; }
+ public uint ControllerVersion { get; set; }
+
+ public uint ProgramName { get; set; }
+ public uint ProgramCompiledTime { get; set; }
+ public uint ProgramCrestronDatabaseVersion { get; set; }
+ public uint ProgramEnvironmentVersion { get; set; }
+ public uint AggregatedProgramInfo { get; set; }
+
+ public SystemMonitorJoinMap()
+ {
+ TimeZone = 1;
+
+ TimeZoneName = 1;
+ IOControllerVersion = 2;
+ SnmpAppVersion = 3;
+ BACnetAppVersion = 4;
+ ControllerVersion = 5;
+
+
+ ProgramStartJoin = 10;
+
+ ProgramOffsetJoin = 5;
+
+ // Offset in groups of 5 joins
+ ProgramStart = 1;
+ ProgramStop = 2;
+ ProgramRegister = 3;
+ ProgramUnregister = 4;
+
+ ProgramName = 1;
+ ProgramCompiledTime = 2;
+ ProgramCrestronDatabaseVersion = 3;
+ ProgramEnvironmentVersion = 4;
+ AggregatedProgramInfo = 5;
+ }
+
+ public override void OffsetJoinNumbers(uint joinStart)
+ {
+ var joinOffset = joinStart - 1;
+
+ TimeZone = TimeZone + joinOffset;
+
+ TimeZoneName = TimeZoneName + joinOffset;
+ IOControllerVersion = IOControllerVersion + joinOffset;
+ SnmpAppVersion = SnmpAppVersion + joinOffset;
+ BACnetAppVersion = BACnetAppVersion + joinOffset;
+ ControllerVersion = ControllerVersion + joinOffset;
+
+ // Sets the initial join value where the iterated program joins will begin
+ ProgramStartJoin = ProgramStartJoin + joinOffset;
+ }
+ }
+}
\ No newline at end of file
diff --git a/PepperDashEssentials/Configuration Original/Factories/DmFactory.cs b/PepperDashEssentials/Configuration Original/Factories/DmFactory.cs
index 646cf1f3..ef2d1a03 100644
--- a/PepperDashEssentials/Configuration Original/Factories/DmFactory.cs
+++ b/PepperDashEssentials/Configuration Original/Factories/DmFactory.cs
@@ -80,6 +80,16 @@ namespace PepperDash.Essentials
var dm = new DmMd8x8(ipId, Global.ControlSystem);
//dev = new DmChassisController(devKey, devName, dm);
}
+ else if (devType.Equals("dmmd16x16", StringComparison.OrdinalIgnoreCase))
+ {
+ var dm = new DmMd16x16(ipId, Global.ControlSystem);
+ //dev = new DmChassisController(devKey, devName, dm);
+ }
+ else if (devType.Equals("dmmd32x32", StringComparison.OrdinalIgnoreCase))
+ {
+ var dm = new DmMd32x32(ipId, Global.ControlSystem);
+ //dev = new DmChassisController(devKey, devName, dm);
+ }
}
catch (Exception e)
{
diff --git a/PepperDashEssentials/ControlSystem.cs b/PepperDashEssentials/ControlSystem.cs
index f33537ae..bd33a276 100644
--- a/PepperDashEssentials/ControlSystem.cs
+++ b/PepperDashEssentials/ControlSystem.cs
@@ -4,6 +4,8 @@ using Crestron.SimplSharp;
using Crestron.SimplSharp.CrestronIO;
using Crestron.SimplSharpPro;
using Crestron.SimplSharpPro.CrestronThread;
+using Crestron.SimplSharpPro.Diagnostics;
+
using PepperDash.Core;
using PepperDash.Essentials.Core;
using PepperDash.Essentials.Core.Config;
@@ -32,12 +34,18 @@ namespace PepperDash.Essentials
///
public override void InitializeSystem()
{
+ SystemMonitor.ProgramInitialization.ProgramInitializationUnderUserControl = true;
+
DeterminePlatform();
//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 =>
+ {
+ Debug.Console(0, Debug.ErrorLogLevel.Notice, "CONSOLE MESSAGE: {0}", s);
+ }, "appdebugmessage", "Writes message to log", ConsoleAccessLevelEnum.AccessOperator);
CrestronConsole.AddNewConsoleCommand(s =>
{
@@ -141,12 +149,18 @@ namespace PepperDash.Essentials
"------------------------------------------------\r" +
"------------------------------------------------");
}
+
}
catch (Exception e)
{
Debug.Console(0, "FATAL INITIALIZE ERROR. System is in an inconsistent state:\r{0}", e);
+
+
}
+ // Notify the
+ SystemMonitor.ProgramInitialization.ProgramInitializationComplete = true;
+
}
///
@@ -203,16 +217,40 @@ namespace PepperDash.Essentials
LoadLogoServer();
DeviceManager.ActivateAll();
+
+ LinkSystemMonitorToAppServer();
}
+ void LinkSystemMonitorToAppServer()
+ {
+ var sysMon = DeviceManager.GetDeviceForKey("systemMonitor") as PepperDash.Essentials.Core.Monitoring.SystemMonitorController;
+
+ var appServer = DeviceManager.GetDeviceForKey("appServer") as CotijaSystemController;
+
+
+ if (sysMon != null && appServer != null)
+ {
+ var key = sysMon.Key + "-" + appServer.Key;
+ var messenger = new PepperDash.Essentials.AppServer.Messengers.SystemMonitorMessenger
+ (key, sysMon, "/device/systemMonitor");
+
+ messenger.RegisterWithAppServer(appServer);
+
+ DeviceManager.AddDevice(messenger);
+ }
+ }
///
/// Reads all devices from config and adds them to DeviceManager
///
public void LoadDevices()
{
- // Build the processor wrapper class
- DeviceManager.AddDevice(new PepperDash.Essentials.Core.Devices.CrestronProcessor("processor"));
+# warning Missing PepperDash.Essentials.Core.Devices.CrestronProcessor("processor"));
+ // Build the processor wrapper class
+ // DeviceManager.AddDevice(new PepperDash.Essentials.Core.Devices.CrestronProcessor("processor"));
+
+ // Add global System Monitor device
+ DeviceManager.AddDevice(new PepperDash.Essentials.Core.Monitoring.SystemMonitorController("systemMonitor"));
foreach (var devConf in ConfigReader.ConfigObject.Devices)
{
@@ -231,17 +269,23 @@ namespace PepperDash.Essentials
continue;
}
- // Try local factory first
+ // Try local factories first
var newDev = DeviceFactory.GetDevice(devConf);
+ if (newDev == null)
+ newDev = BridgeFactory.GetDevice(devConf);
+
// Then associated library factories
+ if (newDev == null)
+ newDev = PepperDash.Essentials.Core.DeviceFactory.GetDevice(devConf);
if (newDev == null)
newDev = PepperDash.Essentials.Devices.Common.DeviceFactory.GetDevice(devConf);
if (newDev == null)
newDev = PepperDash.Essentials.DM.DeviceFactory.GetDevice(devConf);
if (newDev == null)
newDev = PepperDash.Essentials.Devices.Displays.DisplayDeviceFactory.GetDevice(devConf);
-
+ if (newDev == null)
+ newDev = PepperDash.Essentials.BridgeFactory.GetDevice(devConf);
if (newDev != null)
DeviceManager.AddDevice(newDev);
else
@@ -256,6 +300,7 @@ namespace PepperDash.Essentials
}
+
///
/// Helper method to load tie lines. This should run after devices have loaded
///
diff --git a/PepperDashEssentials/ControlSystem.cs.orig b/PepperDashEssentials/ControlSystem.cs.orig
deleted file mode 100644
index bd9590c4..00000000
--- a/PepperDashEssentials/ControlSystem.cs.orig
+++ /dev/null
@@ -1,367 +0,0 @@
-using System;
-using System.Linq;
-using Crestron.SimplSharp;
-using Crestron.SimplSharp.CrestronIO;
-using Crestron.SimplSharpPro;
-using Crestron.SimplSharpPro.CrestronThread;
-using PepperDash.Core;
-using PepperDash.Core.PortalSync;
-using PepperDash.Essentials.Core;
-using PepperDash.Essentials.Devices.Common;
-using PepperDash.Essentials.DM;
-using PepperDash.Essentials.Fusion;
-using PepperDash.Essentials.Room.Cotija;
-
-namespace PepperDash.Essentials
-{
- public class ControlSystem : CrestronControlSystem
- {
- PepperDashPortalSyncClient PortalSync;
- HttpLogoServer LogoServer;
-
- public ControlSystem()
- : base()
- {
- Thread.MaxNumberOfUserThreads = 400;
- Global.ControlSystem = this;
- DeviceManager.Initialize(this);
- }
-
- ///
- /// Git 'er goin'
- ///
- public override void InitializeSystem()
- {
-<<<<<<< HEAD
- DeterminePlatform();
-
- //CrestronConsole.AddNewConsoleCommand(s => GoWithLoad(), "go", "Loads configuration file",
- // ConsoleAccessLevelEnum.AccessOperator);
-=======
- CrestronConsole.AddNewConsoleCommand(s => GoWithLoad(), "go", "Loads configuration file",
- ConsoleAccessLevelEnum.AccessOperator);
->>>>>>> 600b9f11ff1bbc186f7c2a2945955731b3523b3c
-
- CrestronConsole.AddNewConsoleCommand(s =>
- {
- foreach (var tl in TieLineCollection.Default)
- CrestronConsole.ConsoleCommandResponse(" {0}\r", tl);
- },
- "listtielines", "Prints out all tie lines", ConsoleAccessLevelEnum.AccessOperator);
-
- CrestronConsole.AddNewConsoleCommand(s =>
- {
- CrestronConsole.ConsoleCommandResponse
- ("Current running configuration. This is the merged system and template configuration");
- CrestronConsole.ConsoleCommandResponse(Newtonsoft.Json.JsonConvert.SerializeObject
- (ConfigReader.ConfigObject, Newtonsoft.Json.Formatting.Indented));
- }, "showconfig", "Shows the current running merged config", ConsoleAccessLevelEnum.AccessOperator);
-
- CrestronConsole.AddNewConsoleCommand(s =>
- {
- CrestronConsole.ConsoleCommandResponse("This system can be found at the following URLs:\r" +
- "System URL: {0}\r" +
- "Template URL: {1}", ConfigReader.ConfigObject.SystemUrl, ConfigReader.ConfigObject.TemplateUrl);
- }, "portalinfo", "Shows portal URLS from configuration", ConsoleAccessLevelEnum.AccessOperator);
-
- //GoWithLoad();
- }
-
- ///
- /// Determines if the program is running on a processor (appliance) or server (XiO Edge).
- ///
- /// Sets Global.FilePathPrefix based on platform
- ///
- public void DeterminePlatform()
- {
- Debug.Console(0, Debug.ErrorLogLevel.Notice, "Determining Platform....");
-
- string filePathPrefix;
-
- var dirSeparator = Global.DirectorySeparator;
-
- var version = Crestron.SimplSharp.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
-
- var versionString = string.Format("{0}.{1}.{2}", version.Major, version.Minor, version.Build);
-
- string directoryPrefix;
-
- //directoryPrefix = Crestron.SimplSharp.CrestronIO.Directory.GetApplicationRootDirectory();
-#warning ^ For use with beta Include4.dat for XiO Edge
- directoryPrefix = "";
-
- if (CrestronEnvironment.DevicePlatform != eDevicePlatform.Server)
- {
- filePathPrefix = directoryPrefix + dirSeparator + "NVRAM"
- + dirSeparator + string.Format("program{0}", InitialParametersClass.ApplicationNumber) + dirSeparator;
-
- Debug.Console(0, Debug.ErrorLogLevel.Notice, "Starting Essentials v{0} on 3-series Appliance", versionString);
- }
- else
- {
- filePathPrefix = directoryPrefix + dirSeparator + "User" + dirSeparator;
-
- Debug.Console(0, Debug.ErrorLogLevel.Notice, "Starting Essentials v{0} on XiO Edge Server", versionString);
- }
-
- Global.SetFilePathPrefix(filePathPrefix);
- }
-
- ///
- /// Do it, yo
- ///
- public void GoWithLoad()
- {
- try
- {
- CrestronConsole.AddNewConsoleCommand(EnablePortalSync, "portalsync", "Loads Portal Sync",
- ConsoleAccessLevelEnum.AccessOperator);
-
- //PortalSync = new PepperDashPortalSyncClient();
- Debug.Console(0, Debug.ErrorLogLevel.Notice, "Starting Essentials load from configuration");
-
- var filesReady = SetupFilesystem();
- if (filesReady)
- {
- Debug.Console(0, Debug.ErrorLogLevel.Notice, "Folder structure verified. Loading config...");
- if (!ConfigReader.LoadConfig2())
- return;
-
- Load();
- Debug.Console(0, Debug.ErrorLogLevel.Notice, "Essentials load complete\r" +
- "-------------------------------------------------------------");
- }
- else
- {
- Debug.Console(0,
- "------------------------------------------------\r" +
- "------------------------------------------------\r" +
- "------------------------------------------------\r" +
- "Essentials file structure setup completed.\r" +
- "Please load config, sgd and ir files and\r" +
- "restart program.\r" +
- "------------------------------------------------\r" +
- "------------------------------------------------\r" +
- "------------------------------------------------");
- }
- }
- catch (Exception e)
- {
- Debug.Console(0, "FATAL INITIALIZE ERROR. System is in an inconsistent state:\r{0}", e);
- }
-
- }
-
- ///
- /// Verifies filesystem is set up. IR, SGD, and program1 folders
- ///
- bool SetupFilesystem()
- {
- Debug.Console(0, "Verifying and/or creating folder structure");
- var configDir = Global.FilePathPrefix;
- var configExists = Directory.Exists(configDir);
- if (!configExists)
- Directory.Create(configDir);
-
- var irDir = Global.FilePathPrefix + "ir";
- if (!Directory.Exists(irDir))
- Directory.Create(irDir);
-
- var sgdDir = Global.FilePathPrefix + "sgd";
- if (!Directory.Exists(sgdDir))
- Directory.Create(sgdDir);
-
- return configExists;
- }
-
- public void EnablePortalSync(string s)
- {
- if (s.ToLower() == "enable")
- {
- CrestronConsole.ConsoleCommandResponse("Portal Sync features enabled");
- PortalSync = new PepperDashPortalSyncClient();
- }
- }
-
- public void TearDown()
- {
- Debug.Console(0, "Tearing down existing system");
- DeviceManager.DeactivateAll();
-
- TieLineCollection.Default.Clear();
-
- foreach (var key in DeviceManager.GetDevices())
- DeviceManager.RemoveDevice(key);
-
- Debug.Console(0, "Tear down COMPLETE");
- }
-
- ///
- ///
- ///
- void Load()
- {
- LoadDevices();
- LoadTieLines();
- LoadRooms();
- LoadLogoServer();
-
- DeviceManager.ActivateAll();
- }
-
-
- ///
- /// Reads all devices from config and adds them to DeviceManager
- ///
- public void LoadDevices()
- {
- foreach (var devConf in ConfigReader.ConfigObject.Devices)
- {
-
- try
- {
- Debug.Console(0, Debug.ErrorLogLevel.Notice, "Creating device '{0}'", devConf.Key);
- // Skip this to prevent unnecessary warnings
- if (devConf.Key == "processor")
- continue;
-
- // Try local factory first
- var newDev = DeviceFactory.GetDevice(devConf);
-
- // Then associated library factories
- if (newDev == null)
- newDev = PepperDash.Essentials.Devices.Common.DeviceFactory.GetDevice(devConf);
- if (newDev == null)
- newDev = PepperDash.Essentials.DM.DeviceFactory.GetDevice(devConf);
- if (newDev == null)
- newDev = PepperDash.Essentials.Devices.Displays.DisplayDeviceFactory.GetDevice(devConf);
-
- if (newDev != null)
- DeviceManager.AddDevice(newDev);
- else
- Debug.Console(0, Debug.ErrorLogLevel.Notice, "ERROR: Cannot load unknown device type '{0}', key '{1}'.", devConf.Type, devConf.Key);
- }
- catch (Exception e)
- {
- Debug.Console(0, Debug.ErrorLogLevel.Notice, "ERROR: Creating device {0}. Skipping device. \r{1}", devConf.Key, e);
- }
- }
- Debug.Console(0, Debug.ErrorLogLevel.Notice, "All Devices Loaded.");
-
- }
-
- ///
- /// Helper method to load tie lines. This should run after devices have loaded
- ///
- public void LoadTieLines()
- {
- // In the future, we can't necessarily just clear here because devices
- // might be making their own internal sources/tie lines
-
- var tlc = TieLineCollection.Default;
- //tlc.Clear();
- if (ConfigReader.ConfigObject.TieLines == null)
- {
- return;
- }
-
- foreach (var tieLineConfig in ConfigReader.ConfigObject.TieLines)
- {
- var newTL = tieLineConfig.GetTieLine();
- if (newTL != null)
- tlc.Add(newTL);
- }
-
- Debug.Console(0, Debug.ErrorLogLevel.Notice, "All Tie Lines Loaded.");
-
- }
-
- ///
- /// Reads all rooms from config and adds them to DeviceManager
- ///
- public void LoadRooms()
- {
- if (ConfigReader.ConfigObject.Rooms == null)
- {
- Debug.Console(0, Debug.ErrorLogLevel.Warning, "WARNING: Configuration contains no rooms");
- return;
- }
-
- foreach (var roomConfig in ConfigReader.ConfigObject.Rooms)
- {
- var room = roomConfig.GetRoomObject();
- if (room != null)
- {
- if (room is EssentialsHuddleSpaceRoom)
- {
- DeviceManager.AddDevice(room);
-
- Debug.Console(0, Debug.ErrorLogLevel.Notice, "Room is EssentialsHuddleSpaceRoom, attempting to add to DeviceManager with Fusion");
- DeviceManager.AddDevice(new EssentialsHuddleSpaceFusionSystemControllerBase((EssentialsHuddleSpaceRoom)room, 0xf1));
-
-
- Debug.Console(0, Debug.ErrorLogLevel.Notice, "Attempting to build Cotija Bridge...");
- // Cotija bridge
- var bridge = new CotijaEssentialsHuddleSpaceRoomBridge(room as EssentialsHuddleSpaceRoom);
- AddBridgePostActivationHelper(bridge); // Lets things happen later when all devices are present
- DeviceManager.AddDevice(bridge);
-
- Debug.Console(0, Debug.ErrorLogLevel.Notice, "Cotija Bridge Added...");
- }
- else if (room is EssentialsHuddleVtc1Room)
- {
- DeviceManager.AddDevice(room);
-
- Debug.Console(0, Debug.ErrorLogLevel.Notice, "Room is EssentialsHuddleVtc1Room, attempting to add to DeviceManager with Fusion");
- DeviceManager.AddDevice(new EssentialsHuddleVtc1FusionController((EssentialsHuddleVtc1Room)room, 0xf1));
- }
- else
- {
- Debug.Console(0, Debug.ErrorLogLevel.Notice, "Room is NOT EssentialsHuddleSpaceRoom, attempting to add to DeviceManager w/o Fusion");
- DeviceManager.AddDevice(room);
- }
-
- }
- else
- Debug.Console(0, Debug.ErrorLogLevel.Notice, "WARNING: Cannot create room from config, key '{0}'", roomConfig.Key);
- }
-
- Debug.Console(0, Debug.ErrorLogLevel.Notice, "All Rooms Loaded.");
-
- }
-
- ///
- /// Helps add the post activation steps that link bridges to main controller
- ///
- ///
- void AddBridgePostActivationHelper(CotijaBridgeBase bridge)
- {
- bridge.AddPostActivationAction(() =>
- {
- 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);
- });
- }
-
- ///
- /// Fires up a logo server if not already running
- ///
- void LoadLogoServer()
- {
- try
- {
- LogoServer = new HttpLogoServer(8080, Global.FilePathPrefix + "html" + Global.DirectorySeparator + "logo");
- }
- catch (Exception)
- {
- Debug.Console(0, Debug.ErrorLogLevel.Notice, "NOTICE: Logo server cannot be started. Likely already running in another program");
- }
- }
- }
-}
diff --git a/PepperDashEssentials/OTHER/Fusion/EssentialsHuddleSpaceFusionSystemControllerBase.cs b/PepperDashEssentials/FOR REFERENCE UI/OTHER/Fusion/EssentialsHuddleSpaceFusionSystemControllerBase.cs
similarity index 97%
rename from PepperDashEssentials/OTHER/Fusion/EssentialsHuddleSpaceFusionSystemControllerBase.cs
rename to PepperDashEssentials/FOR REFERENCE UI/OTHER/Fusion/EssentialsHuddleSpaceFusionSystemControllerBase.cs
index 609597d8..e7623975 100644
--- a/PepperDashEssentials/OTHER/Fusion/EssentialsHuddleSpaceFusionSystemControllerBase.cs
+++ b/PepperDashEssentials/FOR REFERENCE UI/OTHER/Fusion/EssentialsHuddleSpaceFusionSystemControllerBase.cs
@@ -1,1595 +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.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;
- }
- }
+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(2, 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/FOR REFERENCE UI/OTHER/Fusion/EssentialsHuddleVtc1FusionController.cs
similarity index 97%
rename from PepperDashEssentials/OTHER/Fusion/EssentialsHuddleVtc1FusionController.cs
rename to PepperDashEssentials/FOR REFERENCE UI/OTHER/Fusion/EssentialsHuddleVtc1FusionController.cs
index 14148a12..c79d49d7 100644
--- a/PepperDashEssentials/OTHER/Fusion/EssentialsHuddleVtc1FusionController.cs
+++ b/PepperDashEssentials/FOR REFERENCE UI/OTHER/Fusion/EssentialsHuddleVtc1FusionController.cs
@@ -1,348 +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.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"); }); ;
- }
- }
- }
+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/FOR REFERENCE UI/OTHER/Fusion/FusionCustomPropertiesBridge.cs
similarity index 52%
rename from PepperDashEssentials/OTHER/Fusion/FusionCustomPropertiesBridge.cs
rename to PepperDashEssentials/FOR REFERENCE UI/OTHER/Fusion/FusionCustomPropertiesBridge.cs
index 6dface95..b5704b40 100644
--- a/PepperDashEssentials/OTHER/Fusion/FusionCustomPropertiesBridge.cs
+++ b/PepperDashEssentials/FOR REFERENCE UI/OTHER/Fusion/FusionCustomPropertiesBridge.cs
@@ -1,102 +1,119 @@
-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);
- }
- }
- }
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Crestron.SimplSharp;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+
+using PepperDash.Core;
+using PepperDash.Essentials.Core;
+using PepperDash.Essentials.Core.Config;
+using PepperDash.Essentials.Core.Devices;
+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)
+ {
+ try
+ {
+ var reconfigurableDevices = DeviceManager.AllDevices.Where(d => d is ReconfigurableDevice);
+
+ foreach (var device in reconfigurableDevices)
+ {
+ // Get the current device config so new values can be overwritten over existing
+ var deviceConfig = (device as ReconfigurableDevice).Config;
+
+ if (device is RoomOnToDefaultSourceWhenOccupied)
+ {
+ Debug.Console(1, "Mapping Room on via Occupancy values from Fusion");
+
+ var devProps = JsonConvert.DeserializeObject(deviceConfig.Properties.ToString());
+
+ var enableFeature = roomInfo.FusionCustomProperties.FirstOrDefault(p => p.ID.Equals("EnRoomOnWhenOccupied"));
+ if (enableFeature != null)
+ devProps.EnableRoomOnWhenOccupied = bool.Parse(enableFeature.CustomFieldValue);
+
+ var enableTime = roomInfo.FusionCustomProperties.FirstOrDefault(p => p.ID.Equals("RoomOnWhenOccupiedStartTime"));
+ if (enableTime != null)
+ devProps.OccupancyStartTime = enableTime.CustomFieldValue;
+
+ var disableTime = roomInfo.FusionCustomProperties.FirstOrDefault(p => p.ID.Equals("RoomOnWhenOccupiedEndTime"));
+ if (disableTime != null)
+ devProps.OccupancyEndTime = disableTime.CustomFieldValue;
+
+ var enableSunday = roomInfo.FusionCustomProperties.FirstOrDefault(p => p.ID.Equals("EnRoomOnWhenOccupiedSun"));
+ if (enableSunday != null)
+ devProps.EnableSunday = bool.Parse(enableSunday.CustomFieldValue);
+
+ var enableMonday = roomInfo.FusionCustomProperties.FirstOrDefault(p => p.ID.Equals("EnRoomOnWhenOccupiedMon"));
+ if (enableMonday != null)
+ devProps.EnableMonday = bool.Parse(enableMonday.CustomFieldValue);
+
+ var enableTuesday = roomInfo.FusionCustomProperties.FirstOrDefault(p => p.ID.Equals("EnRoomOnWhenOccupiedTue"));
+ if (enableTuesday != null)
+ devProps.EnableTuesday = bool.Parse(enableTuesday.CustomFieldValue);
+
+ var enableWednesday = roomInfo.FusionCustomProperties.FirstOrDefault(p => p.ID.Equals("EnRoomOnWhenOccupiedWed"));
+ if (enableWednesday != null)
+ devProps.EnableWednesday = bool.Parse(enableWednesday.CustomFieldValue);
+
+ var enableThursday = roomInfo.FusionCustomProperties.FirstOrDefault(p => p.ID.Equals("EnRoomOnWhenOccupiedThu"));
+ if (enableThursday != null)
+ devProps.EnableThursday = bool.Parse(enableThursday.CustomFieldValue);
+
+ var enableFriday = roomInfo.FusionCustomProperties.FirstOrDefault(p => p.ID.Equals("EnRoomOnWhenOccupiedFri"));
+ if (enableFriday != null)
+ devProps.EnableFriday = bool.Parse(enableFriday.CustomFieldValue);
+
+ var enableSaturday = roomInfo.FusionCustomProperties.FirstOrDefault(p => p.ID.Equals("EnRoomOnWhenOccupiedSat"));
+ if (enableSaturday != null)
+ devProps.EnableSaturday = bool.Parse(enableSaturday.CustomFieldValue);
+
+ deviceConfig.Properties = JToken.FromObject(devProps);
+ }
+ else if (device is EssentialsRoomBase)
+ {
+ // Set the room name
+ if (!string.IsNullOrEmpty(roomInfo.Name))
+ {
+ Debug.Console(1, "Current Room Name: {0}. New Room Name: {1}", deviceConfig.Name, roomInfo.Name);
+ // Set the name in config
+ deviceConfig.Name = roomInfo.Name;
+
+ Debug.Console(1, "Room Name Successfully Changed.");
+ }
+
+ // Set the help message
+ var helpMessage = roomInfo.FusionCustomProperties.FirstOrDefault(p => p.ID.Equals("RoomHelpMessage"));
+ if (helpMessage != null)
+ {
+ //Debug.Console(1, "Current Help Message: {0}. New Help Message: {1}", deviceConfig.Properties["help"]["message"].Value(ToString()), helpMessage.CustomFieldValue);
+ deviceConfig.Properties["helpMessage"] = (string)helpMessage.CustomFieldValue;
+ }
+ }
+
+ // Set the config on the device
+ (device as ReconfigurableDevice).SetConfig(deviceConfig);
+ }
+
+
+ }
+ catch (Exception e)
+ {
+ Debug.Console(1, "FusionCustomPropetiesBridge: Error mapping properties: {0}", e);
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/PepperDashEssentials/OTHER/Fusion/FusionEventHandlers.cs b/PepperDashEssentials/FOR REFERENCE UI/OTHER/Fusion/FusionEventHandlers.cs
similarity index 100%
rename from PepperDashEssentials/OTHER/Fusion/FusionEventHandlers.cs
rename to PepperDashEssentials/FOR REFERENCE UI/OTHER/Fusion/FusionEventHandlers.cs
diff --git a/PepperDashEssentials/OTHER/Fusion/FusionProcessorQueries.cs b/PepperDashEssentials/FOR REFERENCE UI/OTHER/Fusion/FusionProcessorQueries.cs
similarity index 100%
rename from PepperDashEssentials/OTHER/Fusion/FusionProcessorQueries.cs
rename to PepperDashEssentials/FOR REFERENCE UI/OTHER/Fusion/FusionProcessorQueries.cs
diff --git a/PepperDashEssentials/OTHER/Fusion/FusionRviDataClasses.cs b/PepperDashEssentials/FOR REFERENCE UI/OTHER/Fusion/FusionRviDataClasses.cs
similarity index 100%
rename from PepperDashEssentials/OTHER/Fusion/FusionRviDataClasses.cs
rename to PepperDashEssentials/FOR REFERENCE UI/OTHER/Fusion/FusionRviDataClasses.cs
diff --git a/PepperDashEssentials/OTHER/Fusion/FusionSystemController.cs.orig b/PepperDashEssentials/FOR REFERENCE UI/OTHER/Fusion/FusionSystemController.cs.orig
similarity index 100%
rename from PepperDashEssentials/OTHER/Fusion/FusionSystemController.cs.orig
rename to PepperDashEssentials/FOR REFERENCE UI/OTHER/Fusion/FusionSystemController.cs.orig
diff --git a/PepperDashEssentials/Factory/DeviceFactory.cs b/PepperDashEssentials/Factory/DeviceFactory.cs
index 432802d0..86612747 100644
--- a/PepperDashEssentials/Factory/DeviceFactory.cs
+++ b/PepperDashEssentials/Factory/DeviceFactory.cs
@@ -87,9 +87,7 @@ namespace PepperDash.Essentials
else if (typeName == "roomonwhenoccupancydetectedfeature")
{
- var props = JsonConvert.DeserializeObject(properties.ToString());
-
- return new Room.Behaviours.RoomOnToDefaultSourceWhenOccupied(key, props);
+ return new Room.Behaviours.RoomOnToDefaultSourceWhenOccupied(dc);
}
return null;
diff --git a/PepperDashEssentials/Factory/UiDeviceFactory.cs b/PepperDashEssentials/Factory/UiDeviceFactory.cs
index a4387d1a..5499aa0c 100644
--- a/PepperDashEssentials/Factory/UiDeviceFactory.cs
+++ b/PepperDashEssentials/Factory/UiDeviceFactory.cs
@@ -49,12 +49,12 @@ namespace PepperDash.Essentials
avDriver.CurrentRoom = room as EssentialsHuddleSpaceRoom;
// Environment Driver
- if (avDriver.CurrentRoom.Config.Environment != null && avDriver.CurrentRoom.Config.Environment.DeviceKeys.Count > 0)
+ if (avDriver.CurrentRoom.PropertiesConfig.Environment != null && avDriver.CurrentRoom.PropertiesConfig.Environment.DeviceKeys.Count > 0)
{
Debug.Console(0, panelController, "Adding environment driver");
mainDriver.EnvironmentDriver = new EssentialsEnvironmentDriver(mainDriver, props);
- mainDriver.EnvironmentDriver.GetDevicesFromConfig(avDriver.CurrentRoom.Config.Environment);
+ mainDriver.EnvironmentDriver.GetDevicesFromConfig(avDriver.CurrentRoom.PropertiesConfig.Environment);
}
mainDriver.HeaderDriver.SetupHeaderButtons(avDriver, avDriver.CurrentRoom);
@@ -118,12 +118,12 @@ namespace PepperDash.Essentials
avDriver.CurrentRoom = room as EssentialsHuddleVtc1Room;
// Environment Driver
- if (avDriver.CurrentRoom.Config.Environment != null && avDriver.CurrentRoom.Config.Environment.DeviceKeys.Count > 0)
+ if (avDriver.CurrentRoom.PropertiesConfig.Environment != null && avDriver.CurrentRoom.PropertiesConfig.Environment.DeviceKeys.Count > 0)
{
Debug.Console(0, panelController, "Adding environment driver");
mainDriver.EnvironmentDriver = new EssentialsEnvironmentDriver(mainDriver, props);
- mainDriver.EnvironmentDriver.GetDevicesFromConfig(avDriver.CurrentRoom.Config.Environment);
+ mainDriver.EnvironmentDriver.GetDevicesFromConfig(avDriver.CurrentRoom.PropertiesConfig.Environment);
}
mainDriver.HeaderDriver.SetupHeaderButtons(avDriver, avDriver.CurrentRoom);
diff --git a/PepperDashEssentials/PepperDashEssentials.csproj b/PepperDashEssentials/PepperDashEssentials.csproj
index 59c180ee..ac74c6c0 100644
--- a/PepperDashEssentials/PepperDashEssentials.csproj
+++ b/PepperDashEssentials/PepperDashEssentials.csproj
@@ -101,17 +101,33 @@
False
- ..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpTimerEventInterface.dll
+ ..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpTimerEventInterface.dll
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -132,13 +148,13 @@
-
-
-
-
-
+
+
+
+
+
-
+
diff --git a/PepperDashEssentials/Properties/AssemblyInfo.cs b/PepperDashEssentials/Properties/AssemblyInfo.cs
index 36c70ef4..1f037bfb 100644
--- a/PepperDashEssentials/Properties/AssemblyInfo.cs
+++ b/PepperDashEssentials/Properties/AssemblyInfo.cs
@@ -4,5 +4,5 @@
[assembly: AssemblyCompany("PepperDash Technology Corp")]
[assembly: AssemblyProduct("PepperDashEssentials")]
[assembly: AssemblyCopyright("Copyright © PepperDash Technology Corp 2018")]
-[assembly: AssemblyVersion("1.2.6.*")]
+[assembly: AssemblyVersion("1.3.1.*")]
diff --git a/PepperDashEssentials/Properties/ControlSystem.cfg b/PepperDashEssentials/Properties/ControlSystem.cfg
index 4772f8fd..d2c3b2c7 100644
--- a/PepperDashEssentials/Properties/ControlSystem.cfg
+++ b/PepperDashEssentials/Properties/ControlSystem.cfg
@@ -1,7 +1,7 @@
-
-
- Test RMC3
- auto 192.168.1.40
- Program01
- Internal Flash
+
+
+ 192.168.10.1
+ auto 192.168.10.1
+ Program01
+ Internal Flash
\ No newline at end of file
diff --git a/PepperDashEssentials/Room/Behaviours/RoomOnToDefaultSourceWhenOccupied.cs b/PepperDashEssentials/Room/Behaviours/RoomOnToDefaultSourceWhenOccupied.cs
index 823ed2b7..b12fd2e5 100644
--- a/PepperDashEssentials/Room/Behaviours/RoomOnToDefaultSourceWhenOccupied.cs
+++ b/PepperDashEssentials/Room/Behaviours/RoomOnToDefaultSourceWhenOccupied.cs
@@ -11,6 +11,7 @@ using Newtonsoft.Json.Linq;
using PepperDash.Core;
using PepperDash.Essentials.Core;
using PepperDash.Essentials.Core.Config;
+using PepperDash.Essentials.Core.Devices;
using PepperDash.Essentials.Devices.Common.Occupancy;
namespace PepperDash.Essentials.Room.Behaviours
@@ -18,9 +19,9 @@ 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
+ public class RoomOnToDefaultSourceWhenOccupied : ReconfigurableDevice
{
- RoomOnToDefaultSourceWhenOccupiedConfig Config;
+ RoomOnToDefaultSourceWhenOccupiedConfig PropertiesConfig;
public bool FeatureEnabled { get; private set; }
@@ -42,10 +43,10 @@ namespace PepperDash.Essentials.Room.Behaviours
private Fusion.EssentialsHuddleSpaceFusionSystemControllerBase FusionRoom;
- public RoomOnToDefaultSourceWhenOccupied(string key, RoomOnToDefaultSourceWhenOccupiedConfig config)
- : base(key)
+ public RoomOnToDefaultSourceWhenOccupied(DeviceConfig config) :
+ base (config)
{
- Config = config;
+ PropertiesConfig = JsonConvert.DeserializeObject(config.Properties.ToString());
FeatureEventGroup = new ScheduledEventGroup(this.Key);
@@ -63,7 +64,7 @@ namespace PepperDash.Essentials.Room.Behaviours
else
Debug.Console(1, this, "Room has no RoomOccupancy object set");
- var fusionRoomKey = Config.RoomKey + "-fusion";
+ var fusionRoomKey = PropertiesConfig.RoomKey + "-fusion";
FusionRoom = DeviceManager.GetDeviceForKey(fusionRoomKey) as Fusion.EssentialsHuddleSpaceFusionSystemControllerBase;
@@ -79,45 +80,48 @@ namespace PepperDash.Essentials.Room.Behaviours
return base.CustomActivate();
}
+ ///
+ /// Sets up device based on config values
+ ///
void SetUpDevice()
{
- Room = DeviceManager.GetDeviceForKey(Config.RoomKey) as EssentialsRoomBase;
+ Room = DeviceManager.GetDeviceForKey(PropertiesConfig.RoomKey) as EssentialsRoomBase;
if (Room != null)
{
try
{
- FeatureEnabledTime = DateTime.Parse(Config.OccupancyStartTime);
+ FeatureEnabledTime = DateTime.Parse(PropertiesConfig.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);
+ Debug.Console(1, this, "Unable to parse {0} to DateTime", PropertiesConfig.OccupancyStartTime);
}
catch (Exception e)
{
- Debug.Console(1, this, "Unable to parse OccupancyStartTime property: {0} \n Error: {1}", Config.OccupancyStartTime, e);
+ Debug.Console(1, this, "Unable to parse OccupancyStartTime property: {0} \n Error: {1}", PropertiesConfig.OccupancyStartTime, e);
}
try
{
- FeatureDisabledTime = DateTime.Parse(Config.OccupancyEndTime);
+ FeatureDisabledTime = DateTime.Parse(PropertiesConfig.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);
+ Debug.Console(1, this, "Unable to parse {0} to DateTime", PropertiesConfig.OccupancyEndTime);
}
catch (Exception e)
{
Debug.Console(1, this, "Unable to parse a DateTime config value \n Error: {1}", e);
}
- if (!Config.EnableRoomOnWhenOccupied)
+ if (!PropertiesConfig.EnableRoomOnWhenOccupied)
FeatureEventGroup.ClearAllEvents();
else
{
@@ -133,30 +137,18 @@ namespace PepperDash.Essentials.Room.Behaviours
FeatureEnabled = CheckIfFeatureShouldBeEnabled();
}
else
- Debug.Console(1, this, "Unable to get room from Device Manager with key: {0}", Config.RoomKey);
+ Debug.Console(1, this, "Unable to get room from Device Manager with key: {0}", PropertiesConfig.RoomKey);
}
- ///
- /// Returns a JObject of the device config properties as they currently exist at runtime
- ///
- ///
- public JToken GetLocalConfigProperties()
+
+ protected override void CustomSetConfig(DeviceConfig config)
{
- return JToken.FromObject(Config);
- }
+ var newPropertiesConfig = JsonConvert.DeserializeObject(config.Properties.ToString());
- public object GetDeviceConfig()
- {
- return Config;
- }
+ if(newPropertiesConfig != null)
+ PropertiesConfig = newPropertiesConfig;
- public void SetDeviceConfig(object config)
- {
- var newConfig = config as RoomOnToDefaultSourceWhenOccupiedConfig;
-
- Config = newConfig;
-
- ConfigWriter.UpdateDeviceProperties(this.Key, GetLocalConfigProperties());
+ ConfigWriter.UpdateDeviceConfig(config);
SetUpDevice();
}
@@ -182,7 +174,7 @@ namespace PepperDash.Essentials.Room.Behaviours
{
if (SchEvent.Name == FeatureEnableEventName)
{
- if (Config.EnableRoomOnWhenOccupied)
+ if (PropertiesConfig.EnableRoomOnWhenOccupied)
FeatureEnabled = true;
Debug.Console(1, this, "*****Feature Enabled by event.*****");
@@ -204,7 +196,7 @@ namespace PepperDash.Essentials.Room.Behaviours
{
bool enabled = false;
- if(Config.EnableRoomOnWhenOccupied)
+ if(PropertiesConfig.EnableRoomOnWhenOccupied)
{
Debug.Console(1, this, "Current Time: {0} \n FeatureEnabledTime: {1} \n FeatureDisabledTime: {2}", DateTime.Now, FeatureEnabledTime, FeatureDisabledTime);
@@ -446,19 +438,19 @@ namespace PepperDash.Essentials.Room.Behaviours
{
ScheduledEventCommon.eWeekDays value = new ScheduledEventCommon.eWeekDays();
- if (Config.EnableSunday)
+ if (PropertiesConfig.EnableSunday)
value = value | ScheduledEventCommon.eWeekDays.Sunday;
- if (Config.EnableMonday)
+ if (PropertiesConfig.EnableMonday)
value = value | ScheduledEventCommon.eWeekDays.Monday;
- if (Config.EnableTuesday)
+ if (PropertiesConfig.EnableTuesday)
value = value | ScheduledEventCommon.eWeekDays.Tuesday;
- if (Config.EnableWednesday)
+ if (PropertiesConfig.EnableWednesday)
value = value | ScheduledEventCommon.eWeekDays.Wednesday;
- if (Config.EnableThursday)
+ if (PropertiesConfig.EnableThursday)
value = value | ScheduledEventCommon.eWeekDays.Thursday;
- if (Config.EnableFriday)
+ if (PropertiesConfig.EnableFriday)
value = value | ScheduledEventCommon.eWeekDays.Friday;
- if (Config.EnableSaturday)
+ if (PropertiesConfig.EnableSaturday)
value = value | ScheduledEventCommon.eWeekDays.Saturday;
return value;
@@ -473,7 +465,7 @@ namespace PepperDash.Essentials.Room.Behaviours
{
if (type == ScheduledEventCommon.eCallbackReason.NormalExpiration)
{
- if(Config.EnableRoomOnWhenOccupied)
+ if(PropertiesConfig.EnableRoomOnWhenOccupied)
FeatureEnabled = true;
Debug.Console(1, this, "RoomOnToDefaultSourceWhenOccupied Feature Enabled.");
diff --git a/PepperDashEssentials/Room/Config/EssentialsHuddleVtc1PropertiesConfig.cs b/PepperDashEssentials/Room/Config/EssentialsHuddleVtc1PropertiesConfig.cs
index 83303582..16e49933 100644
--- a/PepperDashEssentials/Room/Config/EssentialsHuddleVtc1PropertiesConfig.cs
+++ b/PepperDashEssentials/Room/Config/EssentialsHuddleVtc1PropertiesConfig.cs
@@ -20,5 +20,7 @@ namespace PepperDash.Essentials.Room.Config
public string DefaultSourceItem { get; set; }
[JsonProperty("videoCodecKey")]
public string VideoCodecKey { get; set; }
+ [JsonProperty("audioCodecKey")]
+ public string AudioCodecKey { get; set; }
}
}
\ No newline at end of file
diff --git a/PepperDashEssentials/Room/Config/EssentialsRoomConfig.cs b/PepperDashEssentials/Room/Config/EssentialsRoomConfig.cs
index 049c15dc..4ceb481d 100644
--- a/PepperDashEssentials/Room/Config/EssentialsRoomConfig.cs
+++ b/PepperDashEssentials/Room/Config/EssentialsRoomConfig.cs
@@ -24,64 +24,14 @@ namespace PepperDash.Essentials.Room.Config
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);
+ var huddle = new EssentialsHuddleSpaceRoom(roomConfig);
- 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
-
+ var rm = new EssentialsHuddleVtc1Room(roomConfig);
+
return rm;
}
else if (typeName == "ddvc01Bridge")
@@ -96,7 +46,7 @@ namespace PepperDash.Essentials.Room.Config
/// 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)
+ public static EssentialsRoomEmergencyBase GetEmergency(EssentialsRoomPropertiesConfig props, EssentialsRoomBase room)
{
// This emergency
var emergency = props.Emergency;
@@ -115,7 +65,7 @@ namespace PepperDash.Essentials.Room.Config
///
///
///
- static PepperDash.Essentials.Devices.Common.Microphones.MicrophonePrivacyController GetMicrophonePrivacy(
+ public static PepperDash.Essentials.Devices.Common.Microphones.MicrophonePrivacyController GetMicrophonePrivacy(
EssentialsRoomPropertiesConfig props, EssentialsHuddleVtc1Room room)
{
var microphonePrivacy = props.MicrophonePrivacy;
@@ -322,8 +272,8 @@ namespace PepperDash.Essentials.Room.Config
[JsonProperty("deviceKey")]
public string DeviceKey { get; set; }
- [JsonProperty("timoutMinutes")]
- public int TimoutMinutes { get; set; }
+ [JsonProperty("timeoutMinutes")]
+ public int TimeoutMinutes { get; set; }
}
public class EssentialsRoomTechConfig
diff --git a/PepperDashEssentials/Room/Types/EssentialsHuddleSpaceRoom.cs b/PepperDashEssentials/Room/Types/EssentialsHuddleSpaceRoom.cs
index a1a821d4..c1db1bee 100644
--- a/PepperDashEssentials/Room/Types/EssentialsHuddleSpaceRoom.cs
+++ b/PepperDashEssentials/Room/Types/EssentialsHuddleSpaceRoom.cs
@@ -4,6 +4,8 @@ using System.Linq;
using System.Text;
using Crestron.SimplSharp;
+using Newtonsoft.Json;
+
using PepperDash.Core;
using PepperDash.Essentials.Core;
using PepperDash.Essentials.Core.Config;
@@ -66,7 +68,7 @@ namespace PepperDash.Essentials
}
}
- public EssentialsRoomPropertiesConfig Config { get; private set; }
+ public EssentialsHuddleRoomPropertiesConfig PropertiesConfig { get; private set; }
public IRoutingSinkWithSwitching DefaultDisplay { get; private set; }
public IRoutingSinkNoSwitching DefaultAudioDevice { get; private set; }
@@ -147,22 +149,32 @@ namespace PepperDash.Essentials
public string CurrentSourceInfoKey { get; private set; }
- ///
- ///
- ///
- ///
- ///
- public EssentialsHuddleSpaceRoom(string key, string name, IRoutingSinkWithSwitching defaultDisplay,
- IRoutingSinkNoSwitching defaultAudio, EssentialsRoomPropertiesConfig config)
- : base(key, name)
+ public EssentialsHuddleSpaceRoom(DeviceConfig config)
+ : base(config)
+ {
+ try
+ {
+ PropertiesConfig = JsonConvert.DeserializeObject
+ (config.Properties.ToString());
+ DefaultDisplay = DeviceManager.GetDeviceForKey(PropertiesConfig.DefaultDisplayKey) as IRoutingSinkWithSwitching;
+
+
+ DefaultAudioDevice = DeviceManager.GetDeviceForKey(PropertiesConfig.DefaultAudioKey) as IRoutingSinkWithSwitching;
+
+ Initialize();
+ }
+ catch (Exception e)
+ {
+ Debug.Console(1, this, "Error building room: \n{0}", e);
+ }
+ }
+
+ void Initialize()
{
- Config = config;
- DefaultDisplay = defaultDisplay;
- DefaultAudioDevice = defaultAudio;
- if (defaultAudio is IBasicVolumeControls)
- DefaultVolumeControls = defaultAudio as IBasicVolumeControls;
- else if (defaultAudio is IHasVolumeDevice)
- DefaultVolumeControls = (defaultAudio as IHasVolumeDevice).VolumeDevice;
+ if (DefaultAudioDevice is IBasicVolumeControls)
+ DefaultVolumeControls = DefaultAudioDevice as IBasicVolumeControls;
+ else if (DefaultAudioDevice is IHasVolumeDevice)
+ DefaultVolumeControls = (DefaultAudioDevice as IHasVolumeDevice).VolumeDevice;
CurrentVolumeControls = DefaultVolumeControls;
var disp = DefaultDisplay as DisplayBase;
@@ -195,6 +207,15 @@ namespace PepperDash.Essentials
EnablePowerOnToLastSource = true;
}
+ protected override void CustomSetConfig(DeviceConfig config)
+ {
+ var newPropertiesConfig = JsonConvert.DeserializeObject(config.Properties.ToString());
+
+ if (newPropertiesConfig != null)
+ PropertiesConfig = newPropertiesConfig;
+
+ ConfigWriter.UpdateRoomConfig(config);
+ }
///
///
@@ -225,6 +246,21 @@ namespace PepperDash.Essentials
return true;
}
+ public override bool CustomActivate()
+ {
+ // Add Occupancy object from config
+ if (PropertiesConfig.Occupancy != null)
+ this.SetRoomOccupancy(DeviceManager.GetDeviceForKey(PropertiesConfig.Occupancy.DeviceKey) as
+ PepperDash.Essentials.Devices.Common.Occupancy.IOccupancyStatusProvider, PropertiesConfig.Occupancy.TimeoutMinutes);
+
+ this.LogoUrl = PropertiesConfig.Logo.GetUrl();
+ this.SourceListKey = PropertiesConfig.SourceListKey;
+ this.DefaultSourceItem = PropertiesConfig.DefaultSourceItem;
+ this.DefaultVolume = (ushort)(PropertiesConfig.Volumes.Master.Level * 65535 / 100);
+
+ return base.CustomActivate();
+ }
+
///
///
///
diff --git a/PepperDashEssentials/Room/Types/EssentialsHuddleVtc1Room.cs b/PepperDashEssentials/Room/Types/EssentialsHuddleVtc1Room.cs
index 12a54592..62b11e49 100644
--- a/PepperDashEssentials/Room/Types/EssentialsHuddleVtc1Room.cs
+++ b/PepperDashEssentials/Room/Types/EssentialsHuddleVtc1Room.cs
@@ -1,575 +1,647 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using Crestron.SimplSharp;
-
-using PepperDash.Core;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Crestron.SimplSharp;
+
+using Newtonsoft.Json;
+
+using PepperDash.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, IRunRouteAction, IRunDefaultCallRoute, IHasVideoCodec
- {
- public event EventHandler CurrentVolumeDeviceChange;
- public event SourceInfoChangeHandler CurrentSingleSourceChange;
-
-
- //************************
- // Call-related stuff
-
- public BoolFeedback InCallFeedback { get; private set; }
-
- ///
- /// Make this more specific
- ///
- public List ActiveCalls { get; private set; }
-
- ///
- /// States: 0 for on hook, 1 for video, 2 for audio, 3 for telekenesis
- ///
- public IntFeedback CallTypeFeedback { get; private set; }
-
- ///
- ///
- ///
- public BoolFeedback PrivacyModeIsOnFeedback { get; private set; }
-
- ///
- /// When something in the room is sharing with the far end or through other means
- ///
- public BoolFeedback IsSharingFeedback { get; private set; }
-
- //************************
-
-
- 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 EssentialsHuddleVtc1PropertiesConfig Config { get; private set; }
-
- public IRoutingSinkWithSwitching DefaultDisplay { get; private set; }
- public IBasicVolumeControls DefaultAudioDevice { get; private set; }
- public IBasicVolumeControls DefaultVolumeControls { get; private set; }
-
- public VideoCodecBase VideoCodec { 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;
-
- ///
- /// Sets the volume control device, and attaches/removes InUseTrackers with "audio"
- /// tag to device.
- ///
- 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; }
-
- ///
- /// "codecOsd"
- ///
- public string DefaultCodecRouteString { get { return "codecOsd"; } }
-
- ///
- /// Temporary implementation. Returns the schedule-ready object or null if none. Fow now,
- /// always returns the VideoCodec if it is capable
- ///
- public IHasScheduleAwareness ScheduleSource { get { return VideoCodec as IHasScheduleAwareness; } }
-
- CCriticalSection SourceSelectLock = new CCriticalSection();
-
- ///
- ///
- ///
- ///
- ///
- public EssentialsHuddleVtc1Room(string key, string name, IRoutingSinkWithSwitching defaultDisplay,
- IBasicVolumeControls defaultAudio, VideoCodecBase codec, EssentialsHuddleVtc1PropertiesConfig config)
- : base(key, name)
- {
- if (codec == null)
- throw new ArgumentNullException("codec cannot be null");
- Config = config;
- DefaultDisplay = defaultDisplay;
- VideoCodec = codec;
- 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();
- }
- if (disp.PowerIsOnFeedback.BoolValue)
- {
- SetDefaultLevels();
- }
- };
-
- disp.IsWarmingUpFeedback.OutputChange += (o, a) =>
- {
- IsWarmingUpFeedback.FireUpdate();
- if (!IsWarmingUpFeedback.BoolValue)
- (CurrentVolumeControls as IBasicVolumeWithFeedback).SetVolume(DefaultVolume);
- };
- disp.IsCoolingDownFeedback.OutputChange += (o, a) =>
- {
- IsCoolingDownFeedback.FireUpdate();
- };
- }
-
- InCallFeedback = new BoolFeedback(() => VideoCodec.IsInCall);
- VideoCodec.CallStatusChange += (o, a) => this.InCallFeedback.FireUpdate();
-
- IsSharingFeedback = new BoolFeedback(() => VideoCodec.SharingContentIsOnFeedback.BoolValue);
- VideoCodec.SharingContentIsOnFeedback.OutputChange += (o, a) => this.IsSharingFeedback.FireUpdate();
-
- // link privacy to VC (for now?)
- PrivacyModeIsOnFeedback = new BoolFeedback(() => VideoCodec.PrivacyModeIsOnFeedback.BoolValue);
- VideoCodec.PrivacyModeIsOnFeedback.OutputChange += (o, a) => this.PrivacyModeIsOnFeedback.FireUpdate();
-
- CallTypeFeedback = new IntFeedback(() => 0);
-
- SourceListKey = "default";
- EnablePowerOnToLastSource = true;
- }
-
-
- ///
- ///
- ///
- protected override void EndShutdown()
- {
- VideoCodec.EndAllCalls();
-
- SetDefaultLevels();
-
- RunDefaultPresentRoute();
-
- CrestronEnvironment.Sleep(1000);
-
- RunRouteAction("roomOff");
- }
-
- ///
- /// Routes the default source item, if any. Returns true when default route exists
- ///
- public override bool RunDefaultPresentRoute()
- {
- if (DefaultSourceItem != null)
- RunRouteAction(DefaultSourceItem);
-
- return DefaultSourceItem != null;
- }
-
- ///
- /// Sets up the room when started into call mode without presenting a source
- ///
- ///
- public bool RunDefaultCallRoute()
- {
- RunRouteAction(DefaultCodecRouteString);
- 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 =>
- {
- // try to prevent multiple simultaneous selections
- SourceSelectLock.TryEnter();
-
- try
- {
-
- 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;
- }
-
- // End usage timer on last source
- if (!string.IsNullOrEmpty(LastSourceKey))
- {
- var usageLastSource = dict[LastSourceKey].SourceDevice as IUsageTracking;
- if (usageLastSource != null && usageLastSource.UsageTracker != null)
- {
- try
- {
- // There MAY have been failures in here. Protect
- usageLastSource.UsageTracker.EndDeviceUsage();
- }
- catch (Exception e)
- {
- Debug.Console(1, this, "*#* EXCEPTION in end usage tracking:\r{0}", e);
- }
- }
- }
-
- // Let's run it
- var item = dict[routeKey];
- if (routeKey.ToLower() != "roomoff")
- {
-
- LastSourceKey = routeKey;
- }
- else
- CurrentSourceInfoKey = null;
-
- // hand off the individual routes to this helper
- foreach (var route in item.RouteList)
- DoRouteItem(route);
-
- // Start usage timer on routed source
- var usageNewSource = item.SourceDevice as IUsageTracking;
- if (usageNewSource != null && usageNewSource.UsageTracker != null) // Have to make sure there is a usage tracker!
- {
- (item.SourceDevice as IUsageTracking).UsageTracker.StartDeviceUsage();
- }
-
- // See if this can be moved into common, base-class method -------------
-
-
- // 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;
- }
-
- OnFeedback.FireUpdate();
-
- // report back when done
- if (successCallback != null)
- successCallback();
- }
- catch (Exception e)
- {
- Debug.Console(1, this, "ERROR in routing: {0}", e);
- }
-
- SourceSelectLock.Leave();
- }, 0); // end of CTimer
- }
-
- ///
- ///
- ///
- ///
- void DoRouteItem(SourceRouteListItem route)
- {
- // 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);
- }
- else
- DoRoute(route);
- }
-
- ///
- ///
- ///
- ///
- ///
- bool DoRoute(SourceRouteListItem route)
- {
- IRoutingSinkNoSwitching dest = null;
-
- if (route.DestinationKey.Equals("$defaultaudio", StringComparison.OrdinalIgnoreCase))
- dest = DefaultAudioDevice as IRoutingSinkNoSwitching;
- 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
- }
-
- ///
- /// 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);
- }
- ///
- /// Will power the room on with the last-used source
- ///
- public override void PowerOnToDefaultOrLastSource()
- {
- if (!EnablePowerOnToLastSource || LastSourceKey == null)
- return;
- RunRouteAction(LastSourceKey);
- }
-
- ///
- /// 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");
- }
-
- #region IPrivacy Members
-
-
- public void PrivacyModeOff()
- {
- VideoCodec.PrivacyModeOff();
- }
-
- public void PrivacyModeOn()
- {
- VideoCodec.PrivacyModeOn();
- }
-
- public void PrivacyModeToggle()
- {
- VideoCodec.PrivacyModeToggle();
- }
-
- #endregion
- }
+using PepperDash.Essentials.Core.Config;
+using PepperDash.Essentials.Room.Config;
+using PepperDash.Essentials.Devices.Common.Codec;
+using PepperDash.Essentials.Devices.Common.VideoCodec;
+using PepperDash.Essentials.Devices.Common.AudioCodec;
+
+namespace PepperDash.Essentials
+{
+ public class EssentialsHuddleVtc1Room : EssentialsRoomBase, IHasCurrentSourceInfoChange,
+ IPrivacy, IHasCurrentVolumeControls, IRunRouteAction, IRunDefaultCallRoute, IHasVideoCodec, IHasAudioCodec
+ {
+ public event EventHandler CurrentVolumeDeviceChange;
+ public event SourceInfoChangeHandler CurrentSingleSourceChange;
+
+
+ //************************
+ // Call-related stuff
+
+ public BoolFeedback InCallFeedback { get; private set; }
+
+ /////
+ ///// Make this more specific
+ /////
+ //public List ActiveCalls { get; private set; }
+
+ ///
+ /// States: 0 for on hook, 1 for video, 2 for audio, 3 for telekenesis
+ ///
+ public IntFeedback CallTypeFeedback { get; private set; }
+
+ ///
+ ///
+ ///
+ public BoolFeedback PrivacyModeIsOnFeedback { get; private set; }
+
+ ///
+ /// When something in the room is sharing with the far end or through other means
+ ///
+ public BoolFeedback IsSharingFeedback { get; private set; }
+
+ //************************
+
+
+ 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 EssentialsHuddleVtc1PropertiesConfig PropertiesConfig { get; private set; }
+
+ public IRoutingSinkWithSwitching DefaultDisplay { get; private set; }
+ public IBasicVolumeControls DefaultAudioDevice { get; private set; }
+ public IBasicVolumeControls DefaultVolumeControls { get; private set; }
+
+ public VideoCodecBase VideoCodec { get; private set; }
+
+ public AudioCodecBase AudioCodec { 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;
+
+ ///
+ /// Sets the volume control device, and attaches/removes InUseTrackers with "audio"
+ /// tag to device.
+ ///
+ 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; }
+
+ ///
+ /// "codecOsd"
+ ///
+ public string DefaultCodecRouteString { get { return "codecOsd"; } }
+
+ ///
+ /// Temporary implementation. Returns the schedule-ready object or null if none. Fow now,
+ /// always returns the VideoCodec if it is capable
+ ///
+ public IHasScheduleAwareness ScheduleSource { get { return VideoCodec as IHasScheduleAwareness; } }
+
+ CCriticalSection SourceSelectLock = new CCriticalSection();
+
+ public EssentialsHuddleVtc1Room(DeviceConfig config)
+ : base(config)
+ {
+ try
+ {
+ PropertiesConfig = JsonConvert.DeserializeObject
+ (config.Properties.ToString());
+ DefaultDisplay = DeviceManager.GetDeviceForKey(PropertiesConfig.DefaultDisplayKey) as IRoutingSinkWithSwitching;
+
+ VideoCodec = DeviceManager.GetDeviceForKey(PropertiesConfig.VideoCodecKey) as
+ PepperDash.Essentials.Devices.Common.VideoCodec.VideoCodecBase;
+ if (VideoCodec == null)
+ throw new ArgumentNullException("codec cannot be null");
+
+ AudioCodec = DeviceManager.GetDeviceForKey(PropertiesConfig.AudioCodecKey) as
+ PepperDash.Essentials.Devices.Common.AudioCodec.AudioCodecBase;
+ if (AudioCodec == null)
+ Debug.Console(0, this, "No Audio Codec Found");
+
+ DefaultAudioDevice = DeviceManager.GetDeviceForKey(PropertiesConfig.DefaultAudioKey) as IBasicVolumeControls;
+
+ Initialize();
+ }
+ catch (Exception e)
+ {
+ Debug.Console(1, this, "Error building room: \n{0}", e);
+ }
+ }
+
+ void Initialize()
+ {
+ if (DefaultAudioDevice is IBasicVolumeControls)
+ DefaultVolumeControls = DefaultAudioDevice as IBasicVolumeControls;
+ else if (DefaultAudioDevice is IHasVolumeDevice)
+ DefaultVolumeControls = (DefaultAudioDevice 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();
+ }
+ if (disp.PowerIsOnFeedback.BoolValue)
+ {
+ SetDefaultLevels();
+ }
+ };
+
+ disp.IsWarmingUpFeedback.OutputChange += (o, a) =>
+ {
+ IsWarmingUpFeedback.FireUpdate();
+ if (!IsWarmingUpFeedback.BoolValue)
+ (CurrentVolumeControls as IBasicVolumeWithFeedback).SetVolume(DefaultVolume);
+ };
+ disp.IsCoolingDownFeedback.OutputChange += (o, a) =>
+ {
+ IsCoolingDownFeedback.FireUpdate();
+ };
+ }
+
+
+ // Combines call feedback from both codecs if available
+ InCallFeedback = new BoolFeedback(() =>
+ {
+ bool inAudioCall = false;
+ bool inVideoCall = false;
+
+ if(AudioCodec != null)
+ inAudioCall = AudioCodec.IsInCall;
+
+ if(VideoCodec != null)
+ inVideoCall = AudioCodec.IsInCall;
+
+ if (inAudioCall || inVideoCall)
+ return true;
+ else
+ return false;
+ });
+
+ VideoCodec.CallStatusChange += (o, a) => this.InCallFeedback.FireUpdate();
+
+ if (AudioCodec != null)
+ AudioCodec.CallStatusChange += (o, a) => this.InCallFeedback.FireUpdate();
+
+ IsSharingFeedback = new BoolFeedback(() => VideoCodec.SharingContentIsOnFeedback.BoolValue);
+ VideoCodec.SharingContentIsOnFeedback.OutputChange += (o, a) => this.IsSharingFeedback.FireUpdate();
+
+ // link privacy to VC (for now?)
+ PrivacyModeIsOnFeedback = new BoolFeedback(() => VideoCodec.PrivacyModeIsOnFeedback.BoolValue);
+ VideoCodec.PrivacyModeIsOnFeedback.OutputChange += (o, a) => this.PrivacyModeIsOnFeedback.FireUpdate();
+
+ CallTypeFeedback = new IntFeedback(() => 0);
+
+ SourceListKey = "default";
+ EnablePowerOnToLastSource = true;
+ }
+
+ protected override void CustomSetConfig(DeviceConfig config)
+ {
+ var newPropertiesConfig = JsonConvert.DeserializeObject(config.Properties.ToString());
+
+ if (newPropertiesConfig != null)
+ PropertiesConfig = newPropertiesConfig;
+
+ ConfigWriter.UpdateRoomConfig(config);
+ }
+
+ public override bool CustomActivate()
+ {
+ // Add Occupancy object from config
+ if (PropertiesConfig.Occupancy != null)
+ this.SetRoomOccupancy(DeviceManager.GetDeviceForKey(PropertiesConfig.Occupancy.DeviceKey) as
+ PepperDash.Essentials.Devices.Common.Occupancy.IOccupancyStatusProvider, PropertiesConfig.Occupancy.TimeoutMinutes);
+
+ this.LogoUrl = PropertiesConfig.Logo.GetUrl();
+ this.SourceListKey = PropertiesConfig.SourceListKey;
+ this.DefaultSourceItem = PropertiesConfig.DefaultSourceItem;
+ this.DefaultVolume = (ushort)(PropertiesConfig.Volumes.Master.Level * 65535 / 100);
+
+ // Get Microphone Privacy object, if any
+ this.MicrophonePrivacy = EssentialsRoomConfigHelper.GetMicrophonePrivacy(PropertiesConfig, this);
+
+ // Get emergency object, if any
+ this.Emergency = EssentialsRoomConfigHelper.GetEmergency(PropertiesConfig, this);
+
+ return base.CustomActivate();
+ }
+
+
+ ///
+ ///
+ ///
+ protected override void EndShutdown()
+ {
+ VideoCodec.EndAllCalls();
+
+ SetDefaultLevels();
+
+ RunDefaultPresentRoute();
+
+ CrestronEnvironment.Sleep(1000);
+
+ RunRouteAction("roomOff");
+ }
+
+ ///
+ /// Routes the default source item, if any. Returns true when default route exists
+ ///
+ public override bool RunDefaultPresentRoute()
+ {
+ if (DefaultSourceItem != null)
+ RunRouteAction(DefaultSourceItem);
+
+ return DefaultSourceItem != null;
+ }
+
+ ///
+ /// Sets up the room when started into call mode without presenting a source
+ ///
+ ///
+ public bool RunDefaultCallRoute()
+ {
+ RunRouteAction(DefaultCodecRouteString);
+ 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 =>
+ {
+ // try to prevent multiple simultaneous selections
+ SourceSelectLock.TryEnter();
+
+ try
+ {
+
+ 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;
+ }
+
+ // End usage timer on last source
+ if (!string.IsNullOrEmpty(LastSourceKey))
+ {
+ var usageLastSource = dict[LastSourceKey].SourceDevice as IUsageTracking;
+ if (usageLastSource != null && usageLastSource.UsageTracker != null)
+ {
+ try
+ {
+ // There MAY have been failures in here. Protect
+ usageLastSource.UsageTracker.EndDeviceUsage();
+ }
+ catch (Exception e)
+ {
+ Debug.Console(1, this, "*#* EXCEPTION in end usage tracking:\r{0}", e);
+ }
+ }
+ }
+
+ // Let's run it
+ var item = dict[routeKey];
+ if (routeKey.ToLower() != "roomoff")
+ {
+
+ LastSourceKey = routeKey;
+ }
+ else
+ CurrentSourceInfoKey = null;
+
+ // hand off the individual routes to this helper
+ foreach (var route in item.RouteList)
+ DoRouteItem(route);
+
+ // Start usage timer on routed source
+ var usageNewSource = item.SourceDevice as IUsageTracking;
+ if (usageNewSource != null && usageNewSource.UsageTracker != null) // Have to make sure there is a usage tracker!
+ {
+ (item.SourceDevice as IUsageTracking).UsageTracker.StartDeviceUsage();
+ }
+
+ // See if this can be moved into common, base-class method -------------
+
+
+ // 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;
+ }
+
+ OnFeedback.FireUpdate();
+
+ // report back when done
+ if (successCallback != null)
+ successCallback();
+ }
+ catch (Exception e)
+ {
+ Debug.Console(1, this, "ERROR in routing: {0}", e);
+ }
+
+ SourceSelectLock.Leave();
+ }, 0); // end of CTimer
+ }
+
+ ///
+ ///
+ ///
+ ///
+ void DoRouteItem(SourceRouteListItem route)
+ {
+ // 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);
+ }
+ else
+ DoRoute(route);
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ bool DoRoute(SourceRouteListItem route)
+ {
+ IRoutingSinkNoSwitching dest = null;
+
+ if (route.DestinationKey.Equals("$defaultaudio", StringComparison.OrdinalIgnoreCase))
+ dest = DefaultAudioDevice as IRoutingSinkNoSwitching;
+ 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
+ }
+
+ ///
+ /// 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);
+ }
+ ///
+ /// Will power the room on with the last-used source
+ ///
+ public override void PowerOnToDefaultOrLastSource()
+ {
+ if (!EnablePowerOnToLastSource || LastSourceKey == null)
+ return;
+ RunRouteAction(LastSourceKey);
+ }
+
+ ///
+ /// 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");
+ }
+
+ #region IPrivacy Members
+
+
+ public void PrivacyModeOff()
+ {
+ VideoCodec.PrivacyModeOff();
+ }
+
+ public void PrivacyModeOn()
+ {
+ VideoCodec.PrivacyModeOn();
+ }
+
+ public void PrivacyModeToggle()
+ {
+ VideoCodec.PrivacyModeToggle();
+ }
+
+ #endregion
+ }
}
\ No newline at end of file
diff --git a/PepperDashEssentials/Room/Types/EssentialsRoomBase.cs b/PepperDashEssentials/Room/Types/EssentialsRoomBase.cs
index a364fa23..fc177b51 100644
--- a/PepperDashEssentials/Room/Types/EssentialsRoomBase.cs
+++ b/PepperDashEssentials/Room/Types/EssentialsRoomBase.cs
@@ -7,6 +7,8 @@ using Crestron.SimplSharp.Scheduler;
using PepperDash.Core;
using PepperDash.Essentials.Core;
+using PepperDash.Essentials.Core.Config;
+using PepperDash.Essentials.Core.Devices;
using PepperDash.Essentials.Devices.Common.Occupancy;
namespace PepperDash.Essentials
@@ -14,7 +16,7 @@ namespace PepperDash.Essentials
///
///
///
- public abstract class EssentialsRoomBase : Device
+ public abstract class EssentialsRoomBase : ReconfigurableDevice
{
///
///
@@ -80,12 +82,9 @@ namespace PepperDash.Essentials
///
public bool ZeroVolumeWhenSwtichingVolumeDevices { get; private set; }
- ///
- ///
- ///
- ///
- ///
- public EssentialsRoomBase(string key, string name) : base(key, name)
+
+ public EssentialsRoomBase(DeviceConfig config)
+ : base(config)
{
// Setup the ShutdownPromptTimer
ShutdownPromptTimer = new SecondsCountdownTimer(Key + "-offTimer");
@@ -159,6 +158,8 @@ namespace PepperDash.Essentials
ShutdownPromptTimer.SecondsToCount = ShutdownVacancySeconds;
ShutdownType = type;
ShutdownPromptTimer.Start();
+
+ Debug.Console(0, this, "ShutdwonPromptTimer Started. Type: {0}. Seconds: {1}", ShutdownType, ShutdownPromptTimer.SecondsToCount);
}
public void StartRoomVacancyTimer(eVacancyMode mode)
@@ -170,7 +171,7 @@ namespace PepperDash.Essentials
VacancyMode = mode;
RoomVacancyShutdownTimer.Start();
- Debug.Console(0, this, "Vacancy Timer Started.");
+ Debug.Console(0, this, "Vacancy Timer Started. Mode: {0}. Seconds: {1}", VacancyMode, RoomVacancyShutdownTimer.SecondsToCount);
}
///
@@ -199,7 +200,7 @@ namespace PepperDash.Essentials
///
///
public void SetRoomOccupancy(IOccupancyStatusProvider statusProvider, int timeoutMinutes)
- {
+ {
if (statusProvider == null)
{
Debug.Console(0, this, "ERROR: Occupancy sensor device is null");
@@ -213,6 +214,8 @@ namespace PepperDash.Essentials
if(timeoutMinutes > 0)
RoomVacancyShutdownSeconds = timeoutMinutes * 60;
+ Debug.Console(1, this, "RoomVacancyShutdownSeconds set to {0}", RoomVacancyShutdownSeconds);
+
RoomOccupancy = statusProvider;
OnRoomOccupancyIsSet();
diff --git a/PepperDashEssentials/UIDrivers/Essentials/EssentialsHeaderDriver.cs b/PepperDashEssentials/UIDrivers/Essentials/EssentialsHeaderDriver.cs
index 1a51fb44..68d4cbcb 100644
--- a/PepperDashEssentials/UIDrivers/Essentials/EssentialsHeaderDriver.cs
+++ b/PepperDashEssentials/UIDrivers/Essentials/EssentialsHeaderDriver.cs
@@ -76,7 +76,7 @@ namespace PepperDash.Essentials
avDriver.PopupInterlock.HideAndClear());
}
- void SetUpHelpButton(EssentialsRoomPropertiesConfig roomConf)
+ public void SetUpHelpButton(EssentialsRoomPropertiesConfig roomConf)
{
// Help roomConf and popup
if (roomConf.Help != null)
@@ -107,7 +107,7 @@ namespace PepperDash.Essentials
var room = DeviceManager.GetDeviceForKey(Config.DefaultRoomKey)
as EssentialsHuddleSpaceRoom;
if (room != null)
- message = room.Config.HelpMessage;
+ message = room.PropertiesConfig.HelpMessage;
else
message = "Sorry, no help message available. No room connected.";
//TriList.StringInput[UIStringJoin.HelpMessage].StringValue = message;
@@ -227,7 +227,7 @@ namespace PepperDash.Essentials
TriList.SetBool(UIBoolJoin.TopBarHabaneroDynamicVisible, true);
- var roomConf = currentRoom.Config;
+ var roomConf = currentRoom.PropertiesConfig;
// Register for the PopupInterlock IsShowsFeedback event to tie the header carets subpage visiblity to it
Parent.AvDriver.PopupInterlock.StatusChanged -= PopupInterlock_StatusChanged;
@@ -289,7 +289,7 @@ namespace PepperDash.Essentials
TriList.SetBool(UIBoolJoin.TopBarHabaneroDynamicVisible, true);
- var roomConf = currentRoom.Config;
+ var roomConf = currentRoom.PropertiesConfig;
// Register for the PopupInterlock IsShowsFeedback event to tie the header carets subpage visiblity to it
Parent.AvDriver.PopupInterlock.StatusChanged -= PopupInterlock_StatusChanged;
diff --git a/PepperDashEssentials/UIDrivers/EssentialsHuddle/EssentialsHuddlePanelAvFunctionsDriver.cs b/PepperDashEssentials/UIDrivers/EssentialsHuddle/EssentialsHuddlePanelAvFunctionsDriver.cs
index 48d81997..9a2b7a31 100644
--- a/PepperDashEssentials/UIDrivers/EssentialsHuddle/EssentialsHuddlePanelAvFunctionsDriver.cs
+++ b/PepperDashEssentials/UIDrivers/EssentialsHuddle/EssentialsHuddlePanelAvFunctionsDriver.cs
@@ -1,1136 +1,1076 @@
-using System;
-using System.Collections.Generic;
-using Crestron.SimplSharp;
-using Crestron.SimplSharpPro;
-using Crestron.SimplSharpPro.UI;
-
-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 EssentialsHuddlePanelAvFunctionsDriver : PanelDriverBase, IAVDriver
- {
- CrestronTouchpanelPropertiesConfig Config;
-
- public enum UiDisplayMode
- {
- PresentationMode, AudioSetup
- }
-
- ///
- /// 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 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 EssentialsHuddleSpaceRoom;
- }
- }
- string _DefaultRoomKey;
-
- ///
- /// Indicates that the SetHeaderButtons method has completed successfully
- ///
- public bool HeaderButtonsAreSetUp { get; private set; }
-
- ///
- ///
- ///
- public EssentialsHuddleSpaceRoom CurrentRoom
- {
- get { return _CurrentRoom; }
- set
- {
- SetCurrentRoom(value);
- }
- }
- EssentialsHuddleSpaceRoom _CurrentRoom;
-
- ///
- ///
- ///
- //uint CurrentInterlockedModalJoin;
-
- ///
- /// 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;
-
- ///
- /// All children attached to this driver. For hiding and showing as a group.
- ///
- List ChildDrivers = new List();
-
- List CurrentDisplayModeSigsInUse = new List();
-
- //// Important smart objects
-
- ///
- /// Smart Object 3200
- ///
- SubpageReferenceList SourcesSrl;
-
- ///
- /// 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