diff --git a/PepperDashEssentials/Room/Cotija/CotijaSystemController.cs b/PepperDashEssentials/Room/Cotija/CotijaSystemController.cs index ea217f28..54261370 100644 --- a/PepperDashEssentials/Room/Cotija/CotijaSystemController.cs +++ b/PepperDashEssentials/Room/Cotija/CotijaSystemController.cs @@ -1,89 +1,89 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Text.RegularExpressions; -using Crestron.SimplSharp; -using Crestron.SimplSharp.CrestronIO; -using Crestron.SimplSharp.Reflection; -using Crestron.SimplSharpPro.CrestronThread; -using Crestron.SimplSharp.CrestronWebSocketClient; -using Crestron.SimplSharpPro; -using Crestron.SimplSharp.Net.Http; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; - -using PepperDash.Core; -using PepperDash.Essentials.Core; -using PepperDash.Essentials.Room.Cotija; - -namespace PepperDash.Essentials -{ - public class CotijaSystemController : Device - { - WebSocketClient WSClient; - - /// - /// Prevents post operations from stomping on each other and getting lost - /// - CEvent PostLockEvent = new CEvent(true, true); - - CEvent RegisterLockEvent = new CEvent(true, true); - - public CotijaConfig Config { get; private set; } - - Dictionary ActionDictionary = new Dictionary(StringComparer.InvariantCultureIgnoreCase); - - Dictionary PushedActions = new Dictionary(); - - CTimer ServerHeartbeatCheckTimer; - - long ServerHeartbeatInterval = 20000; - - CTimer ServerReconnectTimer; - - long ServerReconnectInterval = 5000; - - string SystemUuid; - - List RoomBridges = new List(); - - long ButtonHeartbeatInterval = 1000; - - /// - /// Used for tracking HTTP debugging - /// - bool HttpDebugEnabled; - - /// - /// - /// - /// - /// - /// - public CotijaSystemController(string key, string name, CotijaConfig config) : base(key, name) - { - Config = config; - Debug.Console(0, this, "Mobile UI controller initializing for server:{0}", config.ServerUrl); - - CrestronConsole.AddNewConsoleCommand(AuthorizeSystem, - "mobileauth", "Authorizes system to talk to cotija server", ConsoleAccessLevelEnum.AccessOperator); - CrestronConsole.AddNewConsoleCommand(s => ShowInfo(), - "mobileinfo", "Shows information for current mobile control session", ConsoleAccessLevelEnum.AccessOperator); - CrestronConsole.AddNewConsoleCommand(s => { - s = s.Trim(); - if(!string.IsNullOrEmpty(s)) - { - HttpDebugEnabled = (s.Trim() != "0"); - } - CrestronConsole.ConsoleCommandResponse("HTTP Debug {0}", HttpDebugEnabled ? "Enabled" : "Disabled"); - }, - "mobilehttpdebug", "1 enables more verbose HTTP response debugging", ConsoleAccessLevelEnum.AccessOperator); - CrestronConsole.AddNewConsoleCommand(TestHttpRequest, +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using Crestron.SimplSharp; +using Crestron.SimplSharp.CrestronIO; +using Crestron.SimplSharp.Reflection; +using Crestron.SimplSharpPro.CrestronThread; +using Crestron.SimplSharp.CrestronWebSocketClient; +using Crestron.SimplSharpPro; +using Crestron.SimplSharp.Net.Http; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +using PepperDash.Core; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Room.Cotija; + +namespace PepperDash.Essentials +{ + public class CotijaSystemController : Device + { + WebSocketClient WSClient; + + /// + /// Prevents post operations from stomping on each other and getting lost + /// + CEvent PostLockEvent = new CEvent(true, true); + + CEvent RegisterLockEvent = new CEvent(true, true); + + public CotijaConfig Config { get; private set; } + + Dictionary ActionDictionary = new Dictionary(StringComparer.InvariantCultureIgnoreCase); + + Dictionary PushedActions = new Dictionary(); + + CTimer ServerHeartbeatCheckTimer; + + long ServerHeartbeatInterval = 20000; + + CTimer ServerReconnectTimer; + + long ServerReconnectInterval = 5000; + + string SystemUuid; + + List RoomBridges = new List(); + + long ButtonHeartbeatInterval = 1000; + + /// + /// Used for tracking HTTP debugging + /// + bool HttpDebugEnabled; + + /// + /// + /// + /// + /// + /// + public CotijaSystemController(string key, string name, CotijaConfig config) : base(key, name) + { + Config = config; + Debug.Console(0, this, "Mobile UI controller initializing for server:{0}", config.ServerUrl); + + CrestronConsole.AddNewConsoleCommand(AuthorizeSystem, + "mobileauth", "Authorizes system to talk to cotija server", ConsoleAccessLevelEnum.AccessOperator); + CrestronConsole.AddNewConsoleCommand(s => ShowInfo(), + "mobileinfo", "Shows information for current mobile control session", ConsoleAccessLevelEnum.AccessOperator); + CrestronConsole.AddNewConsoleCommand(s => { + s = s.Trim(); + if(!string.IsNullOrEmpty(s)) + { + HttpDebugEnabled = (s.Trim() != "0"); + } + CrestronConsole.ConsoleCommandResponse("HTTP Debug {0}", HttpDebugEnabled ? "Enabled" : "Disabled"); + }, + "mobilehttpdebug", "1 enables more verbose HTTP response debugging", ConsoleAccessLevelEnum.AccessOperator); + CrestronConsole.AddNewConsoleCommand(TestHttpRequest, "mobilehttprequest", "Tests an HTTP get to URL given", ConsoleAccessLevelEnum.AccessOperator); - CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler); + CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler); } /// @@ -100,614 +100,613 @@ namespace PepperDash.Essentials })); } - } - - /// - /// Adds an action to the dictionary - /// - /// The path of the API command - /// The action to be triggered by the commmand - public void AddAction(string key, object action) - { - if (!ActionDictionary.ContainsKey(key)) - { - ActionDictionary.Add(key, action); - } - else - { - Debug.Console(1, this, "Cannot add action with key '{0}' because key already exists in ActionDictionary.", key); - } - } - - /// - /// Removes an action from the dictionary - /// - /// - public void RemoveAction(string key) - { - if (ActionDictionary.ContainsKey(key)) - ActionDictionary.Remove(key); - } - - /// - /// - /// - /// - public void AddBridge(CotijaBridgeBase bridge) - { - RoomBridges.Add(bridge); - var b = bridge as IDelayedConfiguration; - if (b != null) - { - Debug.Console(0, this, "Adding room bridge with delayed configuration"); - b.ConfigurationIsReady += new EventHandler(bridge_ConfigurationIsReady); - } - else - { - Debug.Console(0, this, "Adding room bridge and sending configuration"); - RegisterSystemToServer(); - } - } - - /// - /// - /// - /// - /// - void bridge_ConfigurationIsReady(object sender, EventArgs e) - { - Debug.Console(1, this, "Bridge ready. Registering"); - // send the configuration object to the server - RegisterSystemToServer(); - } - - /// - /// - /// - /// - void ReconnectToServerTimerCallback(object o) - { - RegisterSystemToServer(); - } - - /// - /// Verifies system connection with servers - /// - /// - void AuthorizeSystem(string code) - { - if (string.IsNullOrEmpty(code)) - { - CrestronConsole.ConsoleCommandResponse("Please enter a user code to authorize a system"); - return; - } - - var req = new HttpClientRequest(); - string url = string.Format("http://{0}/api/system/grantcode/{1}/{2}", Config.ServerUrl, code, SystemUuid); - Debug.Console(0, this, "Authorizing to: {0}", url); - - if (string.IsNullOrEmpty(Config.ServerUrl)) - { - CrestronConsole.ConsoleCommandResponse("Config URL address is not set. Check portal configuration"); - return; - } - try - { - req.Url.Parse(url); - new HttpClient().DispatchAsync(req, (r, e) => - { - CheckHttpDebug(r, e); - if (e == HTTP_CALLBACK_ERROR.COMPLETED) - { - if (r.Code == 200) - { - Debug.Console(0, "System authorized, sending config."); - RegisterSystemToServer(); - } - else if (r.Code == 404) - { - if (r.ContentString.Contains("codeNotFound")) - { - Debug.Console(0, "Authorization failed, code not found for system UUID {0}", SystemUuid); - } - else if (r.ContentString.Contains("uuidNotFound")) - { - Debug.Console(0, "Authorization failed, uuid {0} not found. Check Essentials configuration is correct", - SystemUuid); - } - } - } - else - Debug.Console(0, this, "Error {0} in authorizing system", e); - }); - } - catch (Exception e) - { - Debug.Console(0, this, "Error in authorizing: {0}", e); - } - } - - /// - /// Dumps info in response to console command. - /// - void ShowInfo() - { - var url = Config != null ? Config.ServerUrl : "No config"; - string name; - string code; - if (RoomBridges != null && RoomBridges.Count > 0) - { - name = RoomBridges[0].RoomName; - code = RoomBridges[0].UserCode; - } - else - { - name = "No config"; - code = "Not available"; - } - var conn = WSClient == null ? "No client" : (WSClient.Connected ? "Yes" : "No"); - - CrestronConsole.ConsoleCommandResponse(@"Mobile Control Information: - Server address: {0} - System Name: {1} - System UUID: {2} - System User code: {3} - Connected?: {4}", url, name, SystemUuid, - code, conn); - } - - /// - /// Registers the room with the server - /// - /// URL of the server, including the port number, if not 80. Format: "serverUrlOrIp:port" - void RegisterSystemToServer() - { - var ready = RegisterLockEvent.Wait(20000); - if (!ready) - { - Debug.Console(1, this, "RegisterSystemToServer failed to enter after 20 seconds. Ignoring"); - return; - } - RegisterLockEvent.Reset(); - - try - { - var confObject = ConfigReader.ConfigObject; - confObject.Info.RuntimeInfo.AppName = Assembly.GetExecutingAssembly().GetName().Name; - var version = Assembly.GetExecutingAssembly().GetName().Version; - confObject.Info.RuntimeInfo.AssemblyVersion = string.Format("{0}.{1}.{2}", version.Major, version.Minor, version.Build); - confObject.Info.RuntimeInfo.OsVersion = Crestron.SimplSharp.CrestronEnvironment.OSVersion.Firmware; - - string postBody = JsonConvert.SerializeObject(confObject); - SystemUuid = confObject.SystemUuid; - - if (string.IsNullOrEmpty(postBody)) - { - Debug.Console(1, this, "ERROR: Config body is empty. Cannot register with server."); - } - else - { - var regClient = new HttpClient(); - regClient.Verbose = true; - regClient.KeepAlive = true; - - string url = string.Format("http://{0}/api/system/join/{1}", Config.ServerUrl, SystemUuid); - Debug.Console(1, this, "Joining server at {0}", url); - - HttpClientRequest request = new HttpClientRequest(); - request.Url.Parse(url); - request.RequestType = RequestType.Post; - request.Header.SetHeaderValue("Content-Type", "application/json"); - request.ContentString = postBody; - - var err = regClient.DispatchAsync(request, RegistrationConnectionCallback); - } - - } - catch (Exception e) - { - Debug.Console(0, this, "ERROR: Initilizing Room: {0}", e); - RegisterLockEvent.Set(); - StartReconnectTimer(); - } - - } - - /// - /// Sends a message to the server from a room - /// - /// room from which the message originates - /// object to be serialized and sent in post body - public void SendMessageToServer(JObject o) - { - - if (WSClient != null && WSClient.Connected) - { - string message = JsonConvert.SerializeObject(o, Formatting.None, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }); - Debug.Console(1, this, "Message TX: {0}", message); - var messageBytes = System.Text.Encoding.UTF8.GetBytes(message); - WSClient.Send(messageBytes, (uint)messageBytes.Length, WebSocketClient.WEBSOCKET_PACKET_TYPES.LWS_WS_OPCODE_07__TEXT_FRAME); - //WSClient.SendAsync(messageBytes, (uint)messageBytes.Length, WebSocketClient.WEBSOCKET_PACKET_TYPES.LWS_WS_OPCODE_07__TEXT_FRAME); - } - - } - - /// - /// 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) + } + + /// + /// Adds an action to the dictionary + /// + /// The path of the API command + /// The action to be triggered by the commmand + public void AddAction(string key, object action) + { + if (!ActionDictionary.ContainsKey(key)) + { + ActionDictionary.Add(key, action); + } + else + { + Debug.Console(1, this, "Cannot add action with key '{0}' because key already exists in ActionDictionary.", key); + } + } + + /// + /// Removes an action from the dictionary + /// + /// + public void RemoveAction(string key) + { + if (ActionDictionary.ContainsKey(key)) + ActionDictionary.Remove(key); + } + + /// + /// + /// + /// + public void AddBridge(CotijaBridgeBase bridge) + { + RoomBridges.Add(bridge); + var b = bridge as IDelayedConfiguration; + if (b != null) + { + Debug.Console(0, this, "Adding room bridge with delayed configuration"); + b.ConfigurationIsReady += new EventHandler(bridge_ConfigurationIsReady); + } + else + { + Debug.Console(0, this, "Adding room bridge and sending configuration"); + RegisterSystemToServer(); + } + } + + /// + /// + /// + /// + /// + void bridge_ConfigurationIsReady(object sender, EventArgs e) + { + Debug.Console(1, this, "Bridge ready. Registering"); + // send the configuration object to the server + RegisterSystemToServer(); + } + + /// + /// + /// + /// + void ReconnectToServerTimerCallback(object o) + { + RegisterSystemToServer(); + } + + /// + /// Verifies system connection with servers + /// + /// + void AuthorizeSystem(string code) + { + if (string.IsNullOrEmpty(code)) + { + CrestronConsole.ConsoleCommandResponse("Please enter a user code to authorize a system"); + return; + } + + var req = new HttpClientRequest(); + string url = string.Format("http://{0}/api/system/grantcode/{1}/{2}", Config.ServerUrl, code, SystemUuid); + Debug.Console(0, this, "Authorizing to: {0}", url); + + if (string.IsNullOrEmpty(Config.ServerUrl)) + { + CrestronConsole.ConsoleCommandResponse("Config URL address is not set. Check portal configuration"); + return; + } + try + { + req.Url.Parse(url); + new HttpClient().DispatchAsync(req, (r, e) => + { + CheckHttpDebug(r, e); + if (e == HTTP_CALLBACK_ERROR.COMPLETED) + { + if (r.Code == 200) + { + Debug.Console(0, "System authorized, sending config."); + RegisterSystemToServer(); + } + else if (r.Code == 404) + { + if (r.ContentString.Contains("codeNotFound")) + { + Debug.Console(0, "Authorization failed, code not found for system UUID {0}", SystemUuid); + } + else if (r.ContentString.Contains("uuidNotFound")) + { + Debug.Console(0, "Authorization failed, uuid {0} not found. Check Essentials configuration is correct", + SystemUuid); + } + } + } + else + Debug.Console(0, this, "Error {0} in authorizing system", e); + }); + } + catch (Exception e) + { + Debug.Console(0, this, "Error in authorizing: {0}", e); + } + } + + /// + /// Dumps info in response to console command. + /// + void ShowInfo() + { + var url = Config != null ? Config.ServerUrl : "No config"; + string name; + string code; + if (RoomBridges != null && RoomBridges.Count > 0) + { + name = RoomBridges[0].RoomName; + code = RoomBridges[0].UserCode; + } + else + { + name = "No config"; + code = "Not available"; + } + var conn = WSClient == null ? "No client" : (WSClient.Connected ? "Yes" : "No"); + + CrestronConsole.ConsoleCommandResponse(@"Mobile Control Information: + Server address: {0} + System Name: {1} + System UUID: {2} + System User code: {3} + Connected?: {4}", url, name, SystemUuid, + code, conn); + } + + /// + /// Registers the room with the server + /// + /// URL of the server, including the port number, if not 80. Format: "serverUrlOrIp:port" + void RegisterSystemToServer() + { + var ready = RegisterLockEvent.Wait(20000); + if (!ready) + { + Debug.Console(1, this, "RegisterSystemToServer failed to enter after 20 seconds. Ignoring"); + return; + } + RegisterLockEvent.Reset(); + + try + { + var confObject = ConfigReader.ConfigObject; + confObject.Info.RuntimeInfo.AppName = Assembly.GetExecutingAssembly().GetName().Name; + var version = Assembly.GetExecutingAssembly().GetName().Version; + confObject.Info.RuntimeInfo.AssemblyVersion = string.Format("{0}.{1}.{2}", version.Major, version.Minor, version.Build); + + 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"); - } - } + })); + + var code = content["userCode"]; + if(code != null) + { + foreach (var b in RoomBridges) + { + b.SetUserCode(code.Value()); + } + } + ResetOrStartHearbeatTimer(); + } + + /// + /// Outputs debug info when enabled + /// + /// + /// + /// + void CheckHttpDebug(HttpClientResponse r, HTTP_CALLBACK_ERROR e) + { + if (HttpDebugEnabled) + { + Debug.Console(0, this, "------ Begin HTTP Debug ---------------------------------------"); + Debug.Console(0, this, "HTTP Response URL: {0}", r.ResponseUrl.ToString()); + Debug.Console(0, this, "HTTP Response 'error' {0}", e); + Debug.Console(0, this, "HTTP Response code: {0}", r.Code); + Debug.Console(0, this, "HTTP Response content: \r{0}", r.ContentString); + Debug.Console(0, this, "------ End HTTP Debug -----------------------------------------"); + } + } + + /// + /// + /// + /// + /// + /// + /// + int WebsocketReceiveCallback(byte[] data, uint length, WebSocketClient.WEBSOCKET_PACKET_TYPES opcode, + WebSocketClient.WEBSOCKET_RESULT_CODES err) + { + var rx = System.Text.Encoding.UTF8.GetString(data, 0, (int)length); + if(rx.Length > 0) + ParseStreamRx(rx); + WSClient.ReceiveAsync(); + return 1; + } + + /// + /// Callback to catch possible errors in sending via the websocket + /// + /// + /// + int WebsocketSendCallback(Crestron.SimplSharp.CrestronWebSocketClient.WebSocketClient.WEBSOCKET_RESULT_CODES result) + { + Debug.Console(1, this, "SendCallback result: {0}", result); + + return 1; + } + + /// + /// + /// + /// + /// + void ParseStreamRx(string message) + { + if(string.IsNullOrEmpty(message)) + return; + + Debug.Console(1, this, "Message RX: '{0}'", message); + try + { + var messageObj = JObject.Parse(message); + + var type = messageObj["type"].Value(); + + if (type == "hello") + { + ResetOrStartHearbeatTimer(); + } + else if (type == "/system/heartbeat") + { + HandleHeartBeat(messageObj["content"]); + } + else if (type == "close") + { + WSClient.Disconnect(); + + ServerHeartbeatCheckTimer.Stop(); + // Start the reconnect timer + StartReconnectTimer(); + } + else + { + // Check path against Action dictionary + if (ActionDictionary.ContainsKey(type)) + { + var action = ActionDictionary[type]; + + if (action is Action) + { + (action as Action)(); + } + else if (action is PressAndHoldAction) + { + var stateString = messageObj["content"]["state"].Value(); + + // Look for a button press event + if (!string.IsNullOrEmpty(stateString)) + { + switch (stateString) + { + case "true": + { + if (!PushedActions.ContainsKey(type)) + { + PushedActions.Add(type, new CTimer(o => + { + (action as PressAndHoldAction)(false); + PushedActions.Remove(type); + }, null, ButtonHeartbeatInterval, ButtonHeartbeatInterval)); + } + // Maybe add an else to reset the timer + break; + } + case "held": + { + if (!PushedActions.ContainsKey(type)) + { + PushedActions[type].Reset(ButtonHeartbeatInterval, ButtonHeartbeatInterval); + } + return; + } + case "false": + { + if (PushedActions.ContainsKey(type)) + { + PushedActions[type].Stop(); + PushedActions.Remove(type); + } + break; + } + } + + (action as PressAndHoldAction)(stateString == "true"); + } + } + else if (action is Action) + { + var stateString = messageObj["content"]["state"].Value(); + + if (!string.IsNullOrEmpty(stateString)) + { + (action as Action)(stateString == "true"); + } + } + else if (action is Action) + { + (action as Action)(messageObj["content"]["value"].Value()); + } + else if (action is Action) + { + (action as Action)(messageObj["content"]["value"].Value()); + } + else if (action is Action) + { + (action as Action)(messageObj["content"] + .ToObject()); + } + } + else + { + Debug.Console(1, this, "-- Warning: Incoming message has no registered handler"); + } + } + } + catch (Exception err) + { + //Debug.Console(1, "SseMessageLengthBeforeFailureCount: {0}", SseMessageLengthBeforeFailureCount); + //SseMessageLengthBeforeFailureCount = 0; + Debug.Console(1, this, "Unable to parse message: {0}", err); + } + } + + void TestHttpRequest(string s) + { + { + s = s.Trim(); + if (string.IsNullOrEmpty(s)) + { + PrintTestHttpRequestUsage(); + return; + } + var tokens = s.Split(' '); + if (tokens.Length < 2) + { + CrestronConsole.ConsoleCommandResponse("Too few paramaters\r"); + PrintTestHttpRequestUsage(); + return; + } + + try + { + var url = tokens[1]; + if (tokens[0].ToLower() == "get") + { + var resp = new HttpClient().Get(url); + CrestronConsole.ConsoleCommandResponse("RESPONSE:\r{0}\r\r", resp); + } + else if (tokens[0].ToLower() == "post") + { + var resp = new HttpClient().Post(url, new byte[] { }); + CrestronConsole.ConsoleCommandResponse("RESPONSE:\r{0}\r\r", resp); + } + + else + { + CrestronConsole.ConsoleCommandResponse("Only get or post supported\r"); + PrintTestHttpRequestUsage(); + } + } + catch (HttpException e) + { + CrestronConsole.ConsoleCommandResponse("Exception in request:\r"); + CrestronConsole.ConsoleCommandResponse("Response URL: {0}\r", e.Response.ResponseUrl); + CrestronConsole.ConsoleCommandResponse("Response Error Code: {0}\r", e.Response.Code); + CrestronConsole.ConsoleCommandResponse("Response body: {0}\r", e.Response.ContentString); + } + + } + } + + void PrintTestHttpRequestUsage() + { + CrestronConsole.ConsoleCommandResponse("Usage: mobilehttprequest:N get/post url\r"); + } + } } \ No newline at end of file diff --git a/PepperDashEssentials/Room/Cotija/DeviceTypeInterfaces/IChannelExtensions.cs b/PepperDashEssentials/Room/Cotija/DeviceTypeInterfaces/IChannelExtensions.cs index b26a7f99..dd23d85a 100644 --- a/PepperDashEssentials/Room/Cotija/DeviceTypeInterfaces/IChannelExtensions.cs +++ b/PepperDashEssentials/Room/Cotija/DeviceTypeInterfaces/IChannelExtensions.cs @@ -14,9 +14,9 @@ namespace PepperDash.Essentials.Room.Cotija { var prefix = string.Format(@"/device/{0}/", (dev as IKeyed).Key); - controller.AddAction(prefix + "chanup", new PressAndHoldAction(dev.ChannelUp)); - controller.AddAction(prefix + "chandown", new PressAndHoldAction(dev.ChannelDown)); - controller.AddAction(prefix + "lastchan", new PressAndHoldAction(dev.LastChannel)); + controller.AddAction(prefix + "chanUp", new PressAndHoldAction(dev.ChannelUp)); + controller.AddAction(prefix + "chanDown", new PressAndHoldAction(dev.ChannelDown)); + controller.AddAction(prefix + "lastChan", new PressAndHoldAction(dev.LastChannel)); controller.AddAction(prefix + "guide", new PressAndHoldAction(dev.Guide)); controller.AddAction(prefix + "info", new PressAndHoldAction(dev.Info)); controller.AddAction(prefix + "exit", new PressAndHoldAction(dev.Exit)); @@ -26,9 +26,9 @@ namespace PepperDash.Essentials.Room.Cotija { var prefix = string.Format(@"/device/{0}/", (dev as IKeyed).Key); - controller.RemoveAction(prefix + "chanup"); - controller.RemoveAction(prefix + "chandown"); - controller.RemoveAction(prefix + "lastchan"); + controller.RemoveAction(prefix + "chanUp"); + controller.RemoveAction(prefix + "chanDown"); + controller.RemoveAction(prefix + "lastChan"); controller.RemoveAction(prefix + "guide"); controller.RemoveAction(prefix + "info"); controller.RemoveAction(prefix + "exit"); diff --git a/PepperDashEssentials/Room/Cotija/DeviceTypeInterfaces/INumericExtensions.cs b/PepperDashEssentials/Room/Cotija/DeviceTypeInterfaces/INumericExtensions.cs index 7246cb51..4f35e238 100644 --- a/PepperDashEssentials/Room/Cotija/DeviceTypeInterfaces/INumericExtensions.cs +++ b/PepperDashEssentials/Room/Cotija/DeviceTypeInterfaces/INumericExtensions.cs @@ -24,8 +24,8 @@ namespace PepperDash.Essentials.Room.Cotija controller.AddAction(prefix + "num7", new PressAndHoldAction(dev.Digit0)); controller.AddAction(prefix + "num8", new PressAndHoldAction(dev.Digit0)); controller.AddAction(prefix + "num9", new PressAndHoldAction(dev.Digit0)); - controller.AddAction(prefix + "dash", new PressAndHoldAction(dev.KeypadAccessoryButton1)); - controller.AddAction(prefix + "enter", new PressAndHoldAction(dev.KeypadAccessoryButton2)); + controller.AddAction(prefix + "numDash", new PressAndHoldAction(dev.KeypadAccessoryButton1)); + controller.AddAction(prefix + "numEnter", new PressAndHoldAction(dev.KeypadAccessoryButton2)); // Deal with the Accessory functions on the numpad later } @@ -42,9 +42,9 @@ namespace PepperDash.Essentials.Room.Cotija controller.RemoveAction(prefix + "num6"); controller.RemoveAction(prefix + "num7"); controller.RemoveAction(prefix + "num8"); - controller.RemoveAction(prefix + "num9"); - controller.RemoveAction(prefix + "dash"); - controller.RemoveAction(prefix + "enter"); + controller.RemoveAction(prefix + "num9"); + controller.RemoveAction(prefix + "numDash"); + controller.RemoveAction(prefix + "numEnter"); } } } \ No newline at end of file diff --git a/PepperDashEssentials/Room/Cotija/DeviceTypeInterfaces/IPowerExtensions.cs b/PepperDashEssentials/Room/Cotija/DeviceTypeInterfaces/IPowerExtensions.cs index 68b36675..732d2740 100644 --- a/PepperDashEssentials/Room/Cotija/DeviceTypeInterfaces/IPowerExtensions.cs +++ b/PepperDashEssentials/Room/Cotija/DeviceTypeInterfaces/IPowerExtensions.cs @@ -14,18 +14,18 @@ namespace PepperDash.Essentials.Room.Cotija { var prefix = string.Format(@"/device/{0}/", (dev as IKeyed).Key); - controller.AddAction(prefix + "poweron", new Action(dev.PowerOn)); - controller.AddAction(prefix + "poweroff", new Action(dev.PowerOff)); - controller.AddAction(prefix + "powertoggle", new Action(dev.PowerToggle)); + controller.AddAction(prefix + "powerOn", new Action(dev.PowerOn)); + controller.AddAction(prefix + "powerOff", new Action(dev.PowerOff)); + controller.AddAction(prefix + "powerToggle", new Action(dev.PowerToggle)); } public static void UnlinkActions(this IPower dev, CotijaSystemController controller) { var prefix = string.Format(@"/device/{0}/", (dev as IKeyed).Key); - controller.RemoveAction(prefix + "poweron"); - controller.RemoveAction(prefix + "poweroff"); - controller.RemoveAction(prefix + "powertoggle"); + controller.RemoveAction(prefix + "powerOn"); + controller.RemoveAction(prefix + "powerOff"); + controller.RemoveAction(prefix + "powerToggle"); } } diff --git a/PepperDashEssentials/Room/Cotija/DeviceTypeInterfaces/ISetTopBoxControlsExtensions.cs b/PepperDashEssentials/Room/Cotija/DeviceTypeInterfaces/ISetTopBoxControlsExtensions.cs index 7fa7d1b1..99198fa6 100644 --- a/PepperDashEssentials/Room/Cotija/DeviceTypeInterfaces/ISetTopBoxControlsExtensions.cs +++ b/PepperDashEssentials/Room/Cotija/DeviceTypeInterfaces/ISetTopBoxControlsExtensions.cs @@ -14,7 +14,7 @@ namespace PepperDash.Essentials.Room.Cotija { var prefix = string.Format(@"/device/{0}/", (dev as IKeyed).Key); - controller.AddAction(prefix + "dvrlist", new PressAndHoldAction(dev.DvrList)); + controller.AddAction(prefix + "dvrList", new PressAndHoldAction(dev.DvrList)); controller.AddAction(prefix + "replay", new PressAndHoldAction(dev.Replay)); } @@ -22,7 +22,7 @@ namespace PepperDash.Essentials.Room.Cotija { var prefix = string.Format(@"/device/{0}/", (dev as IKeyed).Key); - controller.RemoveAction(prefix + "dvrlist"); + controller.RemoveAction(prefix + "dvrList"); controller.RemoveAction(prefix + "replay"); } } diff --git a/PepperDashEssentials/Room/Cotija/DeviceTypeInterfaces/ITransportExtensions.cs b/PepperDashEssentials/Room/Cotija/DeviceTypeInterfaces/ITransportExtensions.cs index 34bda457..9463d95f 100644 --- a/PepperDashEssentials/Room/Cotija/DeviceTypeInterfaces/ITransportExtensions.cs +++ b/PepperDashEssentials/Room/Cotija/DeviceTypeInterfaces/ITransportExtensions.cs @@ -17,8 +17,8 @@ namespace PepperDash.Essentials.Room.Cotija controller.AddAction(prefix + "play", new PressAndHoldAction(dev.Play)); controller.AddAction(prefix + "pause", new PressAndHoldAction(dev.Pause)); controller.AddAction(prefix + "stop", new PressAndHoldAction(dev.Stop)); - controller.AddAction(prefix + "prevtrack", new PressAndHoldAction(dev.ChapPlus)); - controller.AddAction(prefix + "nexttrack", new PressAndHoldAction(dev.ChapMinus)); + controller.AddAction(prefix + "prevTrack", new PressAndHoldAction(dev.ChapPlus)); + controller.AddAction(prefix + "nextTrack", new PressAndHoldAction(dev.ChapMinus)); controller.AddAction(prefix + "rewind", new PressAndHoldAction(dev.Rewind)); controller.AddAction(prefix + "ffwd", new PressAndHoldAction(dev.FFwd)); controller.AddAction(prefix + "record", new PressAndHoldAction(dev.Record)); @@ -31,8 +31,8 @@ namespace PepperDash.Essentials.Room.Cotija controller.RemoveAction(prefix + "play"); controller.RemoveAction(prefix + "pause"); controller.RemoveAction(prefix + "stop"); - controller.RemoveAction(prefix + "prevtrack"); - controller.RemoveAction(prefix + "nexttrack"); + controller.RemoveAction(prefix + "prevTrack"); + controller.RemoveAction(prefix + "nextTrack"); controller.RemoveAction(prefix + "rewind"); controller.RemoveAction(prefix + "ffwd"); controller.RemoveAction(prefix + "record"); diff --git a/PepperDashEssentials/Room/Cotija/RoomBridges/CotijaDdvc01RoomBridge.cs b/PepperDashEssentials/Room/Cotija/RoomBridges/CotijaDdvc01RoomBridge.cs index 78512263..362777c3 100644 --- a/PepperDashEssentials/Room/Cotija/RoomBridges/CotijaDdvc01RoomBridge.cs +++ b/PepperDashEssentials/Room/Cotija/RoomBridges/CotijaDdvc01RoomBridge.cs @@ -1,336 +1,342 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Crestron.SimplSharp; -using Crestron.SimplSharpPro.EthernetCommunication; - -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; - -using PepperDash.Core; -using PepperDash.Essentials.Core; -using PepperDash.Essentials.Core.Config; -using PepperDash.Essentials.Room.Config; - - -namespace PepperDash.Essentials.Room.Cotija -{ - public class CotijaDdvc01RoomBridge : CotijaBridgeBase, IDelayedConfiguration - { - public class BoolJoin - { - /// - /// 301 - /// - public const uint RoomIsOn = 301; - - /// - /// 51 - /// - public const uint ActivitySharePress = 51; - /// - /// 52 - /// - public const uint ActivityPhoneCallPress = 52; - /// - /// 53 - /// - public const uint ActivityVideoCallPress = 53; - - /// - /// 1 - /// - public const uint MasterVolumeIsMuted = 1; - /// - /// 1 - /// - public const uint MasterVolumeMuteToggle = 1; - - /// - /// 61 - /// - public const uint ShutdownCancel = 61; - /// - /// 62 - /// - public const uint ShutdownEnd = 62; - /// - /// 63 - /// - public const uint ShutdownStart = 63; - - - - /// - /// 72 - /// - public const uint SourceHasChanged = 72; - /// - /// 501 - /// - public const uint ConfigIsReady = 501; - } - - public class UshortJoin - { - /// - /// 1 - /// - public const uint MasterVolumeLevel = 1; - - /// - /// 61 - /// - public const uint ShutdownPromptDuration = 61; - } - - public class StringJoin - { - /// - /// 71 - /// - public const uint SelectedSourceKey = 71; - - /// - /// 501 - /// - public const uint ConfigRoomName = 501; - /// - /// 502 - /// - public const uint ConfigHelpMessage = 502; - /// - /// 503 - /// - public const uint ConfigHelpNumber = 503; - /// - /// 504 - /// - public const uint ConfigRoomPhoneNumber = 504; - /// - /// 505 - /// - public const uint ConfigRoomURI = 505; - /// - /// 401 - /// - public const uint UserCodeToSystem = 401; - /// - /// 402 - /// - public const uint ServerUrl = 402; - } - - /// - /// Fires when config is ready to go - /// - public event EventHandler ConfigurationIsReady; - - public ThreeSeriesTcpIpEthernetIntersystemCommunications EISC { get; private set; } - - /// - /// - /// - public bool ConfigIsLoaded { get; private set; } - - public override string RoomName - { - get { - var name = EISC.StringOutput[StringJoin.ConfigRoomName].StringValue; - return string.IsNullOrEmpty(name) ? "Not Loaded" : name; - } - } - - CotijaDdvc01DeviceBridge SourceBridge; - - - /// - /// - /// - /// - /// - /// - public CotijaDdvc01RoomBridge(string key, string name, uint ipId) - : base(key, name) - { - try - { - EISC = new ThreeSeriesTcpIpEthernetIntersystemCommunications(ipId, "127.0.0.2", Global.ControlSystem); - var reg = EISC.Register(); - if (reg != Crestron.SimplSharpPro.eDeviceRegistrationUnRegistrationResponse.Success) - Debug.Console(0, this, "Cannot connect EISC at IPID {0}: \r{1}", ipId, reg); - - SourceBridge = new CotijaDdvc01DeviceBridge(key + "-sourceBridge", "DDVC01 source bridge", EISC); - DeviceManager.AddDevice(SourceBridge); - } - catch (Exception) - { - throw; - } - } - - /// - /// Finish wiring up everything after all devices are created. The base class will hunt down the related - /// parent controller and link them up. - /// - /// - public override bool CustomActivate() - { - Debug.Console(0, this, "Final activation. Setting up actions and feedbacks"); - SetupFunctions(); - SetupFeedbacks(); - - EISC.SigChange += EISC_SigChange; - EISC.OnlineStatusChange += (o, a) => - { - Debug.Console(1, this, "DDVC EISC online={0}. Config is ready={1}", a.DeviceOnLine, EISC.BooleanOutput[BoolJoin.ConfigIsReady].BoolValue); - if (a.DeviceOnLine && EISC.BooleanOutput[BoolJoin.ConfigIsReady].BoolValue) - LoadConfigValues(); - }; - // load config if it's already there - if (EISC.IsOnline && EISC.BooleanOutput[BoolJoin.ConfigIsReady].BoolValue) // || EISC.BooleanInput[BoolJoin.ConfigIsReady].BoolValue) - LoadConfigValues(); - - - CrestronConsole.AddNewConsoleCommand(s => - { - for (uint i = 1; i < 1000; i++) - { - if (s.ToLower().Equals("b")) - { - CrestronConsole.ConsoleCommandResponse("D{0,6} {1} - ", i, EISC.BooleanOutput[i].BoolValue); - } - else if (s.ToLower().Equals("u")) - { - CrestronConsole.ConsoleCommandResponse("U{0,6} {1,8} - ", i, EISC.UShortOutput[i].UShortValue); - } - else if (s.ToLower().Equals("s")) - { - var val = EISC.StringOutput[i].StringValue; - if(!string.IsNullOrEmpty(val)) - CrestronConsole.ConsoleCommandResponse("S{0,6} {1}\r", i, EISC.StringOutput[i].StringValue); - } - - } - }, "mobilebridgedump", "Dumps DDVC01 bridge EISC data b,u,s", ConsoleAccessLevelEnum.AccessOperator); - - CrestronConsole.AddNewConsoleCommand(s => LoadConfigValues(), "loadddvc", "", ConsoleAccessLevelEnum.AccessOperator); - - return base.CustomActivate(); - } - - - /// - /// Setup the actions to take place on various incoming API calls - /// - void SetupFunctions() - { - - Parent.AddAction(@"/room/room1/status", new Action(SendFullStatus)); - - Parent.AddAction(@"/room/room1/source", new Action(c => - { - EISC.SetString(StringJoin.SelectedSourceKey, c.SourceListItem); - EISC.PulseBool(BoolJoin.SourceHasChanged); - })); - -#warning CHANGE to activityshare. Perhaps - Parent.AddAction(@"/room/room1/defaultsource", new Action(() => - EISC.PulseBool(BoolJoin.ActivitySharePress))); - - Parent.AddAction(@"/room/room1/masterVolumeLevel", new Action(u => - EISC.SetUshort(UshortJoin.MasterVolumeLevel, u))); - Parent.AddAction(@"/room/room1/masterVolumeMuteToggle", new Action(() => - EISC.PulseBool(BoolJoin.MasterVolumeIsMuted))); - - Parent.AddAction(@"/room/room1/shutdownStart", new Action(() => - EISC.PulseBool(BoolJoin.ShutdownStart))); - Parent.AddAction(@"/room/room1/shutdownEnd", new Action(() => - EISC.PulseBool(BoolJoin.ShutdownEnd))); - Parent.AddAction(@"/room/room1/shutdownCancel", new Action(() => - EISC.PulseBool(BoolJoin.ShutdownCancel))); - - - // Source Device (Current Source)' - - SourceDeviceMapDictionary sourceJoinMap = new SourceDeviceMapDictionary(); - +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharp.Reflection; +using Crestron.SimplSharpPro.EthernetCommunication; + +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +using PepperDash.Core; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.Config; +using PepperDash.Essentials.Room.Config; + + +namespace PepperDash.Essentials.Room.Cotija +{ + public class CotijaDdvc01RoomBridge : CotijaBridgeBase, IDelayedConfiguration + { + public class BoolJoin + { + /// + /// 301 + /// + public const uint RoomIsOn = 301; + + /// + /// 51 + /// + public const uint ActivitySharePress = 51; + /// + /// 52 + /// + public const uint ActivityPhoneCallPress = 52; + /// + /// 53 + /// + public const uint ActivityVideoCallPress = 53; + + /// + /// 1 + /// + public const uint MasterVolumeIsMuted = 1; + /// + /// 1 + /// + public const uint MasterVolumeMuteToggle = 1; + + /// + /// 61 + /// + public const uint ShutdownCancel = 61; + /// + /// 62 + /// + public const uint ShutdownEnd = 62; + /// + /// 63 + /// + public const uint ShutdownStart = 63; + + + + /// + /// 72 + /// + public const uint SourceHasChanged = 72; + /// + /// 501 + /// + public const uint ConfigIsReady = 501; + } + + public class UshortJoin + { + /// + /// 1 + /// + public const uint MasterVolumeLevel = 1; + + /// + /// 61 + /// + public const uint ShutdownPromptDuration = 61; + } + + public class StringJoin + { + /// + /// 71 + /// + public const uint SelectedSourceKey = 71; + + /// + /// 501 + /// + public const uint ConfigRoomName = 501; + /// + /// 502 + /// + public const uint ConfigHelpMessage = 502; + /// + /// 503 + /// + public const uint ConfigHelpNumber = 503; + /// + /// 504 + /// + public const uint ConfigRoomPhoneNumber = 504; + /// + /// 505 + /// + public const uint ConfigRoomURI = 505; + /// + /// 401 + /// + public const uint UserCodeToSystem = 401; + /// + /// 402 + /// + public const uint ServerUrl = 402; + } + + /// + /// Fires when config is ready to go + /// + public event EventHandler ConfigurationIsReady; + + public ThreeSeriesTcpIpEthernetIntersystemCommunications EISC { get; private set; } + + /// + /// + /// + public bool ConfigIsLoaded { get; private set; } + + public override string RoomName + { + get { + var name = EISC.StringOutput[StringJoin.ConfigRoomName].StringValue; + return string.IsNullOrEmpty(name) ? "Not Loaded" : name; + } + } + + CotijaDdvc01DeviceBridge SourceBridge; + + + /// + /// + /// + /// + /// + /// + public CotijaDdvc01RoomBridge(string key, string name, uint ipId) + : base(key, name) + { + try + { + EISC = new ThreeSeriesTcpIpEthernetIntersystemCommunications(ipId, "127.0.0.2", Global.ControlSystem); + var reg = EISC.Register(); + if (reg != Crestron.SimplSharpPro.eDeviceRegistrationUnRegistrationResponse.Success) + Debug.Console(0, this, "Cannot connect EISC at IPID {0}: \r{1}", ipId, reg); + + SourceBridge = new CotijaDdvc01DeviceBridge(key + "-sourceBridge", "DDVC01 source bridge", EISC); + DeviceManager.AddDevice(SourceBridge); + } + catch (Exception) + { + throw; + } + } + + /// + /// Finish wiring up everything after all devices are created. The base class will hunt down the related + /// parent controller and link them up. + /// + /// + public override bool CustomActivate() + { + Debug.Console(0, this, "Final activation. Setting up actions and feedbacks"); + SetupFunctions(); + SetupFeedbacks(); + + EISC.SigChange += EISC_SigChange; + EISC.OnlineStatusChange += (o, a) => + { + Debug.Console(1, this, "DDVC EISC online={0}. Config is ready={1}", a.DeviceOnLine, EISC.BooleanOutput[BoolJoin.ConfigIsReady].BoolValue); + if (a.DeviceOnLine && EISC.BooleanOutput[BoolJoin.ConfigIsReady].BoolValue) + LoadConfigValues(); + }; + // load config if it's already there + if (EISC.IsOnline && EISC.BooleanOutput[BoolJoin.ConfigIsReady].BoolValue) // || EISC.BooleanInput[BoolJoin.ConfigIsReady].BoolValue) + LoadConfigValues(); + + + CrestronConsole.AddNewConsoleCommand(s => + { + for (uint i = 1; i < 1000; i++) + { + if (s.ToLower().Equals("b")) + { + CrestronConsole.ConsoleCommandResponse("D{0,6} {1} - ", i, EISC.BooleanOutput[i].BoolValue); + } + else if (s.ToLower().Equals("u")) + { + CrestronConsole.ConsoleCommandResponse("U{0,6} {1,8} - ", i, EISC.UShortOutput[i].UShortValue); + } + else if (s.ToLower().Equals("s")) + { + var val = EISC.StringOutput[i].StringValue; + if(!string.IsNullOrEmpty(val)) + CrestronConsole.ConsoleCommandResponse("S{0,6} {1}\r", i, EISC.StringOutput[i].StringValue); + } + + } + }, "mobilebridgedump", "Dumps DDVC01 bridge EISC data b,u,s", ConsoleAccessLevelEnum.AccessOperator); + + CrestronConsole.AddNewConsoleCommand(s => LoadConfigValues(), "loadddvc", "", ConsoleAccessLevelEnum.AccessOperator); + + return base.CustomActivate(); + } + + + /// + /// Setup the actions to take place on various incoming API calls + /// + void SetupFunctions() + { + + Parent.AddAction(@"/room/room1/status", new Action(SendFullStatus)); + + Parent.AddAction(@"/room/room1/source", new Action(c => + { + EISC.SetString(StringJoin.SelectedSourceKey, c.SourceListItem); + EISC.PulseBool(BoolJoin.SourceHasChanged); + })); + +#warning CHANGE to activityshare. Perhaps + Parent.AddAction(@"/room/room1/defaultsource", new Action(() => + EISC.PulseBool(BoolJoin.ActivitySharePress))); + + Parent.AddAction(@"/room/room1/masterVolumeLevel", new Action(u => + EISC.SetUshort(UshortJoin.MasterVolumeLevel, u))); + Parent.AddAction(@"/room/room1/masterVolumeMuteToggle", new Action(() => + EISC.PulseBool(BoolJoin.MasterVolumeIsMuted))); + + Parent.AddAction(@"/room/room1/shutdownStart", new Action(() => + EISC.PulseBool(BoolJoin.ShutdownStart))); + Parent.AddAction(@"/room/room1/shutdownEnd", new Action(() => + 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(prefix + item.Key, new PressAndHoldAction(b => EISC.SetBool(item.Value, b))); - } - } - - /// - /// Links feedbacks to whatever is gonna happen! - /// - void SetupFeedbacks() - { - // Power - EISC.SetBoolSigAction(BoolJoin.RoomIsOn, b => - PostStatusMessage(new - { - isOn = b - })); - - // Source change things - EISC.SetSigTrueAction(BoolJoin.SourceHasChanged, () => - PostStatusMessage(new - { - selectedSourceKey = EISC.StringOutput[StringJoin.SelectedSourceKey].StringValue - })); - - // Volume things - EISC.SetUShortSigAction(UshortJoin.MasterVolumeLevel, u => - PostStatusMessage(new - { - masterVolumeLevel = u - })); - - EISC.SetBoolSigAction(BoolJoin.MasterVolumeIsMuted, b => - PostStatusMessage(new - { - masterVolumeMuteState = b - })); - - // shutdown things - EISC.SetSigTrueAction(BoolJoin.ShutdownCancel, new Action(() => - PostMessage("/room/shutdown/", new - { - state = "wasCancelled" - }))); - EISC.SetSigTrueAction(BoolJoin.ShutdownEnd, new Action(() => - PostMessage("/room/shutdown/", new - { - state = "hasFinished" - }))); - EISC.SetSigTrueAction(BoolJoin.ShutdownStart, new Action(() => - PostMessage("/room/shutdown/", new - { - state = "hasStarted", - duration = EISC.UShortOutput[UshortJoin.ShutdownPromptDuration].UShortValue - }))); - - // Config things - EISC.SetSigTrueAction(BoolJoin.ConfigIsReady, LoadConfigValues); - } - - /// - /// Reads in config values when the Simpl program is ready - /// - void LoadConfigValues() - { - Debug.Console(1, this, "Loading configuration from DDVC01 EISC bridge"); - ConfigIsLoaded = false; - - var co = ConfigReader.ConfigObject; - - //Room - if (co.Rooms == null) - co.Rooms = new List(); + } + } + + /// + /// Links feedbacks to whatever is gonna happen! + /// + void SetupFeedbacks() + { + // Power + EISC.SetBoolSigAction(BoolJoin.RoomIsOn, b => + PostStatusMessage(new + { + isOn = b + })); + + // Source change things + EISC.SetSigTrueAction(BoolJoin.SourceHasChanged, () => + PostStatusMessage(new + { + selectedSourceKey = EISC.StringOutput[StringJoin.SelectedSourceKey].StringValue + })); + + // Volume things + EISC.SetUShortSigAction(UshortJoin.MasterVolumeLevel, u => + PostStatusMessage(new + { + masterVolumeLevel = u + })); + + EISC.SetBoolSigAction(BoolJoin.MasterVolumeIsMuted, b => + PostStatusMessage(new + { + masterVolumeMuteState = b + })); + + // shutdown things + EISC.SetSigTrueAction(BoolJoin.ShutdownCancel, new Action(() => + PostMessage("/room/shutdown/", new + { + state = "wasCancelled" + }))); + EISC.SetSigTrueAction(BoolJoin.ShutdownEnd, new Action(() => + PostMessage("/room/shutdown/", new + { + state = "hasFinished" + }))); + EISC.SetSigTrueAction(BoolJoin.ShutdownStart, new Action(() => + PostMessage("/room/shutdown/", new + { + state = "hasStarted", + duration = EISC.UShortOutput[UshortJoin.ShutdownPromptDuration].UShortValue + }))); + + // Config things + EISC.SetSigTrueAction(BoolJoin.ConfigIsReady, LoadConfigValues); + } + + /// + /// Reads in config values when the Simpl program is ready + /// + void LoadConfigValues() + { + Debug.Console(1, this, "Loading configuration from DDVC01 EISC bridge"); + ConfigIsLoaded = false; + + var co = ConfigReader.ConfigObject; + + co.Info.RuntimeInfo.AppName = Assembly.GetExecutingAssembly().GetName().Name; + var version = Assembly.GetExecutingAssembly().GetName().Version; + co.Info.RuntimeInfo.AssemblyVersion = string.Format("{0}.{1}.{2}", version.Major, version.Minor, version.Build); + + + //Room + if (co.Rooms == null) + co.Rooms = new List(); var rm = new EssentialsRoomConfig(); if (co.Rooms.Count == 0) { @@ -341,284 +347,284 @@ namespace PepperDash.Essentials.Room.Cotija { Debug.Console(0, this, "Replacing Room[0] in config"); co.Rooms[0] = rm; - } - rm.Name = EISC.StringOutput[501].StringValue; - rm.Key = "room1"; - rm.Type = "ddvc01"; - - DDVC01RoomPropertiesConfig rmProps; - if (rm.Properties == null) - rmProps = new DDVC01RoomPropertiesConfig(); - else - rmProps = JsonConvert.DeserializeObject(rm.Properties.ToString()); - - rmProps.Help = new EssentialsHelpPropertiesConfig(); - rmProps.Help.CallButtonText = EISC.StringOutput[503].StringValue; - rmProps.Help.Message = EISC.StringOutput[502].StringValue; - - rmProps.Environment = new EssentialsEnvironmentPropertiesConfig(); // enabled defaults to false - - rmProps.RoomPhoneNumber = EISC.StringOutput[504].StringValue; - rmProps.RoomURI = EISC.StringOutput[505].StringValue; - rmProps.SpeedDials = new List(); - // add speed dials as long as there are more - up to 4 - for (uint i = 512; i <= 519; i = i + 2) - { - var num = EISC.StringOutput[i].StringValue; - if (string.IsNullOrEmpty(num)) - break; - var name = EISC.StringOutput[i + 1].StringValue; - rmProps.SpeedDials.Add(new DDVC01SpeedDial { Number = num, Name = name}); - } - // volume control names - var volCount = EISC.UShortOutput[701].UShortValue; - - // use Volumes object or? - rmProps.VolumeSliderNames = new List(); - for(uint i = 701; i <= 700 + volCount; i++) - { - rmProps.VolumeSliderNames.Add(EISC.StringInput[i].StringValue); - } - - // There should be cotija devices in here, I think... - if(co.Devices == null) - co.Devices = new List(); - - // clear out previous DDVC devices - co.Devices.RemoveAll(d => d.Key.StartsWith("source-", StringComparison.OrdinalIgnoreCase)); - - rmProps.SourceListKey = "default"; - rm.Properties = JToken.FromObject(rmProps); - - // Source list! This might be brutal!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - - var groupMap = GetSourceGroupDictionary(); - - co.SourceLists = new Dictionary>(); - var newSl = new Dictionary(); - // add sources... - for (uint i = 0; i<= 19; i++) - { - var name = EISC.StringOutput[601 + i].StringValue; - if(string.IsNullOrEmpty(name)) - 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); - var newSLI = new SourceListItem{ - Icon = icon, - Name = name, - Order = (int)i + 1, - SourceKey = key, - }; - newSl.Add(key, newSLI); - - string group = "genericsource"; - if (groupMap.ContainsKey(type)) - { - group = groupMap[type]; - } - - // add dev to devices list - var devConf = new DeviceConfig { - Group = group, - Key = key, - Name = name, - Type = type - }; - co.Devices.Add(devConf); - } - - co.SourceLists.Add("default", newSl); - - Debug.Console(0, this, "******* CONFIG FROM DDVC: \r{0}", JsonConvert.SerializeObject(ConfigReader.ConfigObject, Formatting.Indented)); - - var handler = ConfigurationIsReady; - if (handler != null) - { - handler(this, new EventArgs()); - } - - ConfigIsLoaded = true; - } - - void SendFullStatus() - { - if (ConfigIsLoaded) - { - PostStatusMessage(new - { - isOn = EISC.BooleanOutput[BoolJoin.RoomIsOn].BoolValue, - selectedSourceKey = EISC.StringOutput[StringJoin.SelectedSourceKey].StringValue, - masterVolumeLevel = EISC.UShortOutput[UshortJoin.MasterVolumeLevel].UShortValue, - masterVolumeMuteState = EISC.BooleanOutput[BoolJoin.MasterVolumeIsMuted].BoolValue - }); - } - else - { - PostStatusMessage(new - { - error = "systemNotReady" - }); - } - } - - - - /// - /// Helper for posting status message - /// - /// The contents of the content object - void PostStatusMessage(object contentObject) - { - Parent.SendMessageToServer(JObject.FromObject(new - { - type = "/room/status/", - content = contentObject - })); - } - - /// - /// - /// - /// - /// - void PostMessage(string messageType, object contentObject) - { - Parent.SendMessageToServer(JObject.FromObject(new - { - type = messageType, - content = contentObject - })); - } - - - /// - /// - /// - /// - /// - void EISC_SigChange(object currentDevice, Crestron.SimplSharpPro.SigEventArgs args) - { - if (Debug.Level >= 1) - Debug.Console(1, this, "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); - } - - /// - /// Returns the mapping of types to groups, for setting up devices. - /// - /// - Dictionary GetSourceGroupDictionary() - { - //type, group - var d = new Dictionary(StringComparer.OrdinalIgnoreCase) - { - { "laptop", "pc" }, - { "wireless", "genericsource" }, - { "iptv", "settopbox" } - - }; - return d; - } - - /// - /// updates the usercode from server - /// - protected override void UserCodeChange() - { - Debug.Console(1, this, "Server user code changed: {0}", UserCode); - 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); - // } - //} - } - } + } + rm.Name = EISC.StringOutput[501].StringValue; + rm.Key = "room1"; + rm.Type = "ddvc01"; + + DDVC01RoomPropertiesConfig rmProps; + if (rm.Properties == null) + rmProps = new DDVC01RoomPropertiesConfig(); + else + rmProps = JsonConvert.DeserializeObject(rm.Properties.ToString()); + + rmProps.Help = new EssentialsHelpPropertiesConfig(); + rmProps.Help.CallButtonText = EISC.StringOutput[503].StringValue; + rmProps.Help.Message = EISC.StringOutput[502].StringValue; + + rmProps.Environment = new EssentialsEnvironmentPropertiesConfig(); // enabled defaults to false + + rmProps.RoomPhoneNumber = EISC.StringOutput[504].StringValue; + rmProps.RoomURI = EISC.StringOutput[505].StringValue; + rmProps.SpeedDials = new List(); + // add speed dials as long as there are more - up to 4 + for (uint i = 512; i <= 519; i = i + 2) + { + var num = EISC.StringOutput[i].StringValue; + if (string.IsNullOrEmpty(num)) + break; + var name = EISC.StringOutput[i + 1].StringValue; + rmProps.SpeedDials.Add(new DDVC01SpeedDial { Number = num, Name = name}); + } + // volume control names + var volCount = EISC.UShortOutput[701].UShortValue; + + // use Volumes object or? + rmProps.VolumeSliderNames = new List(); + for(uint i = 701; i <= 700 + volCount; i++) + { + rmProps.VolumeSliderNames.Add(EISC.StringInput[i].StringValue); + } + + // There should be cotija devices in here, I think... + if(co.Devices == null) + co.Devices = new List(); + + // clear out previous DDVC devices + co.Devices.RemoveAll(d => d.Key.StartsWith("source-", StringComparison.OrdinalIgnoreCase)); + + rmProps.SourceListKey = "default"; + rm.Properties = JToken.FromObject(rmProps); + + // Source list! This might be brutal!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + var groupMap = GetSourceGroupDictionary(); + + co.SourceLists = new Dictionary>(); + var newSl = new Dictionary(); + // add sources... + for (uint i = 0; i<= 19; i++) + { + var name = EISC.StringOutput[601 + i].StringValue; + if(string.IsNullOrEmpty(name)) + 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); + var newSLI = new SourceListItem{ + Icon = icon, + Name = name, + Order = (int)i + 1, + SourceKey = key, + }; + newSl.Add(key, newSLI); + + string group = "genericsource"; + if (groupMap.ContainsKey(type)) + { + group = groupMap[type]; + } + + // add dev to devices list + var devConf = new DeviceConfig { + Group = group, + Key = key, + Name = name, + Type = type + }; + co.Devices.Add(devConf); + } + + co.SourceLists.Add("default", newSl); + + Debug.Console(0, this, "******* CONFIG FROM DDVC: \r{0}", JsonConvert.SerializeObject(ConfigReader.ConfigObject, Formatting.Indented)); + + var handler = ConfigurationIsReady; + if (handler != null) + { + handler(this, new EventArgs()); + } + + ConfigIsLoaded = true; + } + + void SendFullStatus() + { + if (ConfigIsLoaded) + { + PostStatusMessage(new + { + isOn = EISC.BooleanOutput[BoolJoin.RoomIsOn].BoolValue, + selectedSourceKey = EISC.StringOutput[StringJoin.SelectedSourceKey].StringValue, + masterVolumeLevel = EISC.UShortOutput[UshortJoin.MasterVolumeLevel].UShortValue, + masterVolumeMuteState = EISC.BooleanOutput[BoolJoin.MasterVolumeIsMuted].BoolValue + }); + } + else + { + PostStatusMessage(new + { + error = "systemNotReady" + }); + } + } + + + + /// + /// Helper for posting status message + /// + /// The contents of the content object + void PostStatusMessage(object contentObject) + { + Parent.SendMessageToServer(JObject.FromObject(new + { + type = "/room/status/", + content = contentObject + })); + } + + /// + /// + /// + /// + /// + void PostMessage(string messageType, object contentObject) + { + Parent.SendMessageToServer(JObject.FromObject(new + { + type = messageType, + content = contentObject + })); + } + + + /// + /// + /// + /// + /// + void EISC_SigChange(object currentDevice, Crestron.SimplSharpPro.SigEventArgs args) + { + if (Debug.Level >= 1) + Debug.Console(1, this, "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); + } + + /// + /// Returns the mapping of types to groups, for setting up devices. + /// + /// + Dictionary GetSourceGroupDictionary() + { + //type, group + var d = new Dictionary(StringComparer.OrdinalIgnoreCase) + { + { "laptop", "pc" }, + { "wireless", "genericsource" }, + { "iptv", "settopbox" } + + }; + return d; + } + + /// + /// updates the usercode from server + /// + protected override void UserCodeChange() + { + Debug.Console(1, this, "Server user code changed: {0}", UserCode); + 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/Room/Cotija/RoomBridges/SourceDeviceMapDictionary.cs b/PepperDashEssentials/Room/Cotija/RoomBridges/SourceDeviceMapDictionary.cs index ecd29d59..06d64f94 100644 --- a/PepperDashEssentials/Room/Cotija/RoomBridges/SourceDeviceMapDictionary.cs +++ b/PepperDashEssentials/Room/Cotija/RoomBridges/SourceDeviceMapDictionary.cs @@ -42,23 +42,23 @@ namespace PepperDash.Essentials.Room.Cotija {"preset23", 123}, {"preset24", 124}, - {"key0", 130}, - {"key1", 131}, - {"key2", 132}, - {"key3", 133}, - {"key4", 134}, - {"key5", 135}, - {"key6", 136}, - {"key7", 137}, - {"key8", 138}, - {"key9", 139}, - {"keyDash", 140}, - {"keyEnter", 141}, - {"channelUp", 142}, - {"channelDown", 143}, - {"channelLast", 144}, + {"num0", 130}, + {"num1", 131}, + {"num2", 132}, + {"num3", 133}, + {"num4", 134}, + {"num5", 135}, + {"num6", 136}, + {"num7", 137}, + {"num8", 138}, + {"num9", 139}, + {"numDash", 140}, + {"numEnter", 141}, + {"chanUp", 142}, + {"chanDown", 143}, + {"lastChan", 144}, {"exit", 145}, - {"power", 146}, + {"powerToggle", 146}, {"red", 147}, {"green", 148}, {"yellow", 149}, @@ -71,21 +71,27 @@ namespace PepperDash.Essentials.Room.Cotija {"closedCaption", 156}, {"stop", 157}, {"pause", 158}, - {"cursorUp", 159}, - {"cursorDown", 160}, - {"cursorLeft", 161}, - {"cursorRight", 162}, + {"up", 159}, + {"down", 160}, + {"left", 161}, + {"right", 162}, {"settings", 163}, {"info", 164}, {"return", 165}, {"guide", 166}, {"reboot", 167}, - + {"dvrList", 168}, + {"replay", 169}, {"play", 170}, - {"cursorOk", 171}, + {"select", 171}, {"record", 172}, {"menu", 173}, - {"topMenu", 174} + {"topMenu", 174}, + {"prevTrack", 175}, + {"nextTrack", 176}, + {"powerOn", 177}, + {"powerOff", 178} + }; diff --git a/Release Package/PepperDashEssentials.cpz b/Release Package/PepperDashEssentials.cpz index 102c997c..f0921bce 100644 Binary files a/Release Package/PepperDashEssentials.cpz and b/Release Package/PepperDashEssentials.cpz differ diff --git a/Release Package/PepperDashEssentials.dll b/Release Package/PepperDashEssentials.dll index 7bb8d442..5a40fd8f 100644 Binary files a/Release Package/PepperDashEssentials.dll and b/Release Package/PepperDashEssentials.dll differ