diff --git a/PepperDashEssentials/Room/Cotija/CotijaConfig.cs b/PepperDashEssentials/AppServer/CotijaConfig.cs
similarity index 100%
rename from PepperDashEssentials/Room/Cotija/CotijaConfig.cs
rename to PepperDashEssentials/AppServer/CotijaConfig.cs
diff --git a/PepperDashEssentials/Room/Cotija/CotijaDdvc01DeviceBridge.cs b/PepperDashEssentials/AppServer/CotijaDdvc01DeviceBridge.cs
similarity index 100%
rename from PepperDashEssentials/Room/Cotija/CotijaDdvc01DeviceBridge.cs
rename to PepperDashEssentials/AppServer/CotijaDdvc01DeviceBridge.cs
diff --git a/PepperDashEssentials/Room/Cotija/CotijaSystemController.cs b/PepperDashEssentials/AppServer/CotijaSystemController.cs
similarity index 59%
rename from PepperDashEssentials/Room/Cotija/CotijaSystemController.cs
rename to PepperDashEssentials/AppServer/CotijaSystemController.cs
index fc7d8212..b2e3c9dd 100644
--- a/PepperDashEssentials/Room/Cotija/CotijaSystemController.cs
+++ b/PepperDashEssentials/AppServer/CotijaSystemController.cs
@@ -1,698 +1,828 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Text.RegularExpressions;
-using Crestron.SimplSharp;
-using Crestron.SimplSharp.CrestronIO;
-using Crestron.SimplSharp.Reflection;
-using Crestron.SimplSharpPro.CrestronThread;
-using Crestron.SimplSharp.CrestronWebSocketClient;
-using Crestron.SimplSharpPro;
-using Crestron.SimplSharp.Net.Http;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
-
-using PepperDash.Core;
-using PepperDash.Essentials.Core;
-using PepperDash.Essentials.Room.Cotija;
-
-namespace PepperDash.Essentials
-{
- public class CotijaSystemController : Device
- {
- WebSocketClient WSClient;
-
- ///
- /// Prevents post operations from stomping on each other and getting lost
- ///
- CEvent PostLockEvent = new CEvent(true, true);
-
- CEvent RegisterLockEvent = new CEvent(true, true);
-
- public CotijaConfig Config { get; private set; }
-
- Dictionary ActionDictionary = new Dictionary(StringComparer.InvariantCultureIgnoreCase);
-
- Dictionary PushedActions = new Dictionary();
-
- CTimer ServerHeartbeatCheckTimer;
-
- long ServerHeartbeatInterval = 20000;
-
- CTimer ServerReconnectTimer;
-
- long ServerReconnectInterval = 5000;
-
- string SystemUuid;
-
- List RoomBridges = new List();
-
- long ButtonHeartbeatInterval = 1000;
-
- ///
- /// Used for tracking HTTP debugging
- ///
- bool HttpDebugEnabled;
-
- ///
- ///
- ///
- ///
- ///
- ///
- public CotijaSystemController(string key, string name, CotijaConfig config) : base(key, name)
- {
- Config = config;
- Debug.Console(0, this, "Mobile UI controller initializing for server:{0}", config.ServerUrl);
-
- CrestronConsole.AddNewConsoleCommand(AuthorizeSystem,
- "mobileauth", "Authorizes system to talk to cotija server", ConsoleAccessLevelEnum.AccessOperator);
- CrestronConsole.AddNewConsoleCommand(s => ShowInfo(),
- "mobileinfo", "Shows information for current mobile control session", ConsoleAccessLevelEnum.AccessOperator);
- CrestronConsole.AddNewConsoleCommand(s => {
- s = s.Trim();
- if(!string.IsNullOrEmpty(s))
- {
- HttpDebugEnabled = (s.Trim() != "0");
- }
- CrestronConsole.ConsoleCommandResponse("HTTP Debug {0}", HttpDebugEnabled ? "Enabled" : "Disabled");
- },
- "mobilehttpdebug", "1 enables more verbose HTTP response debugging", ConsoleAccessLevelEnum.AccessOperator);
- CrestronConsole.AddNewConsoleCommand(TestHttpRequest,
- "mobilehttprequest", "Tests an HTTP get to URL given", ConsoleAccessLevelEnum.AccessOperator);
-
- }
-
- ///
- /// Adds an action to the dictionary
- ///
- /// The path of the API command
- /// The action to be triggered by the commmand
- public void AddAction(string key, object action)
- {
- if (!ActionDictionary.ContainsKey(key))
- {
- ActionDictionary.Add(key, action);
- }
- else
- {
- Debug.Console(1, this, "Cannot add action with key '{0}' because key already exists in ActionDictionary.", key);
- }
- }
-
- ///
- /// Removes an action from the dictionary
- ///
- ///
- public void RemoveAction(string key)
- {
- if (ActionDictionary.ContainsKey(key))
- ActionDictionary.Remove(key);
- }
-
- ///
- ///
- ///
- ///
- public void AddBridge(CotijaBridgeBase bridge)
- {
- RoomBridges.Add(bridge);
- var b = bridge as IDelayedConfiguration;
- if (b != null)
- {
- Debug.Console(0, this, "Adding room bridge with delayed configuration");
- b.ConfigurationIsReady += new EventHandler(bridge_ConfigurationIsReady);
- }
- else
- {
- Debug.Console(0, this, "Adding room bridge and sending configuration");
- RegisterSystemToServer();
- }
- }
-
- ///
- ///
- ///
- ///
- ///
- void bridge_ConfigurationIsReady(object sender, EventArgs e)
- {
- Debug.Console(1, this, "Bridge ready. Registering");
- // send the configuration object to the server
- RegisterSystemToServer();
- }
-
- ///
- ///
- ///
- ///
- void ReconnectToServerTimerCallback(object o)
- {
- RegisterSystemToServer();
- }
-
- ///
- /// Verifies system connection with servers
- ///
- ///
- void AuthorizeSystem(string code)
- {
- if (string.IsNullOrEmpty(code))
- {
- CrestronConsole.ConsoleCommandResponse("Please enter a user code to authorize a system");
- return;
- }
-
- var req = new HttpClientRequest();
- string url = string.Format("http://{0}/api/system/grantcode/{1}/{2}", Config.ServerUrl, code, SystemUuid);
- Debug.Console(0, this, "Authorizing to: {0}", url);
-
- if (string.IsNullOrEmpty(Config.ServerUrl))
- {
- CrestronConsole.ConsoleCommandResponse("Config URL address is not set. Check portal configuration");
- return;
- }
- try
- {
- req.Url.Parse(url);
- new HttpClient().DispatchAsync(req, (r, e) =>
- {
- CheckHttpDebug(r, e);
- if (e == HTTP_CALLBACK_ERROR.COMPLETED)
- {
- if (r.Code == 200)
- {
- Debug.Console(0, "System authorized, sending config.");
- RegisterSystemToServer();
- }
- else if (r.Code == 404)
- {
- if (r.ContentString.Contains("codeNotFound"))
- {
- Debug.Console(0, "Authorization failed, code not found for system UUID {0}", SystemUuid);
- }
- else if (r.ContentString.Contains("uuidNotFound"))
- {
- Debug.Console(0, "Authorization failed, uuid {0} not found. Check Essentials configuration is correct",
- SystemUuid);
- }
- }
- }
- else
- Debug.Console(0, this, "Error {0} in authorizing system", e);
- });
- }
- catch (Exception e)
- {
- Debug.Console(0, this, "Error in authorizing: {0}", e);
- }
- }
-
- ///
- /// Dumps info in response to console command.
- ///
- void ShowInfo()
- {
- var url = Config != null ? Config.ServerUrl : "No config";
- string name;
- string code;
- if (RoomBridges != null && RoomBridges.Count > 0)
- {
- name = RoomBridges[0].RoomName;
- code = RoomBridges[0].UserCode;
- }
- else
- {
- name = "No config";
- code = "Not available";
- }
- var conn = WSClient == null ? "No client" : (WSClient.Connected ? "Yes" : "No");
-
- CrestronConsole.ConsoleCommandResponse(@"Mobile Control Information:
- Server address: {0}
- System Name: {1}
- System UUID: {2}
- System User code: {3}
- Connected?: {4}", url, name, SystemUuid,
- code, conn);
- }
-
- ///
- /// Registers the room with the server
- ///
- /// URL of the server, including the port number, if not 80. Format: "serverUrlOrIp:port"
- void RegisterSystemToServer()
- {
- var ready = RegisterLockEvent.Wait(20000);
- if (!ready)
- {
- Debug.Console(1, this, "RegisterSystemToServer failed to enter after 20 seconds. Ignoring");
- return;
- }
- RegisterLockEvent.Reset();
-
- try
- {
- var confObject = ConfigReader.ConfigObject;
- confObject.Info.RuntimeInfo.AppName = Assembly.GetExecutingAssembly().GetName().Name;
- var version = Assembly.GetExecutingAssembly().GetName().Version;
- confObject.Info.RuntimeInfo.AssemblyVersion = string.Format("{0}.{1}.{2}", version.Major, version.Minor, version.Build);
-
-
-# warning I commented this out...read only property...Bad merge? JTA 2018-08-13 ;
- //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)
- {
- SendMessageToServer(JObject.FromObject(new
- {
- type = "/heartbeat-ack"
- }));
-
- 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 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;
+
+ //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;
+
+ 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");
+
+ 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()
+ {
+ 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 });
+ Debug.Console(1, this, "Message TX: {0}", message);
+ 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;
+
+ Debug.Console(1, this, "Message RX: {0}", message);
+ 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/Room/Cotija/DeviceTypeInterfaces/IChannelExtensions.cs b/PepperDashEssentials/AppServer/DeviceTypeInterfaces/IChannelExtensions.cs
similarity index 76%
rename from PepperDashEssentials/Room/Cotija/DeviceTypeInterfaces/IChannelExtensions.cs
rename to PepperDashEssentials/AppServer/DeviceTypeInterfaces/IChannelExtensions.cs
index b26a7f99..dd23d85a 100644
--- a/PepperDashEssentials/Room/Cotija/DeviceTypeInterfaces/IChannelExtensions.cs
+++ b/PepperDashEssentials/AppServer/DeviceTypeInterfaces/IChannelExtensions.cs
@@ -14,9 +14,9 @@ namespace PepperDash.Essentials.Room.Cotija
{
var prefix = string.Format(@"/device/{0}/", (dev as IKeyed).Key);
- controller.AddAction(prefix + "chanup", new PressAndHoldAction(dev.ChannelUp));
- controller.AddAction(prefix + "chandown", new PressAndHoldAction(dev.ChannelDown));
- controller.AddAction(prefix + "lastchan", new PressAndHoldAction(dev.LastChannel));
+ controller.AddAction(prefix + "chanUp", new PressAndHoldAction(dev.ChannelUp));
+ controller.AddAction(prefix + "chanDown", new PressAndHoldAction(dev.ChannelDown));
+ controller.AddAction(prefix + "lastChan", new PressAndHoldAction(dev.LastChannel));
controller.AddAction(prefix + "guide", new PressAndHoldAction(dev.Guide));
controller.AddAction(prefix + "info", new PressAndHoldAction(dev.Info));
controller.AddAction(prefix + "exit", new PressAndHoldAction(dev.Exit));
@@ -26,9 +26,9 @@ namespace PepperDash.Essentials.Room.Cotija
{
var prefix = string.Format(@"/device/{0}/", (dev as IKeyed).Key);
- controller.RemoveAction(prefix + "chanup");
- controller.RemoveAction(prefix + "chandown");
- controller.RemoveAction(prefix + "lastchan");
+ controller.RemoveAction(prefix + "chanUp");
+ controller.RemoveAction(prefix + "chanDown");
+ controller.RemoveAction(prefix + "lastChan");
controller.RemoveAction(prefix + "guide");
controller.RemoveAction(prefix + "info");
controller.RemoveAction(prefix + "exit");
diff --git a/PepperDashEssentials/Room/Cotija/DeviceTypeInterfaces/IColorExtensions.cs b/PepperDashEssentials/AppServer/DeviceTypeInterfaces/IColorExtensions.cs
similarity index 100%
rename from PepperDashEssentials/Room/Cotija/DeviceTypeInterfaces/IColorExtensions.cs
rename to PepperDashEssentials/AppServer/DeviceTypeInterfaces/IColorExtensions.cs
diff --git a/PepperDashEssentials/Room/Cotija/DeviceTypeInterfaces/IDPadExtensions.cs b/PepperDashEssentials/AppServer/DeviceTypeInterfaces/IDPadExtensions.cs
similarity index 100%
rename from PepperDashEssentials/Room/Cotija/DeviceTypeInterfaces/IDPadExtensions.cs
rename to PepperDashEssentials/AppServer/DeviceTypeInterfaces/IDPadExtensions.cs
diff --git a/PepperDashEssentials/Room/Cotija/DeviceTypeInterfaces/IDvrExtensions.cs b/PepperDashEssentials/AppServer/DeviceTypeInterfaces/IDvrExtensions.cs
similarity index 100%
rename from PepperDashEssentials/Room/Cotija/DeviceTypeInterfaces/IDvrExtensions.cs
rename to PepperDashEssentials/AppServer/DeviceTypeInterfaces/IDvrExtensions.cs
diff --git a/PepperDashEssentials/Room/Cotija/DeviceTypeInterfaces/INumericExtensions.cs b/PepperDashEssentials/AppServer/DeviceTypeInterfaces/INumericExtensions.cs
similarity index 86%
rename from PepperDashEssentials/Room/Cotija/DeviceTypeInterfaces/INumericExtensions.cs
rename to PepperDashEssentials/AppServer/DeviceTypeInterfaces/INumericExtensions.cs
index 7246cb51..376ed57b 100644
--- a/PepperDashEssentials/Room/Cotija/DeviceTypeInterfaces/INumericExtensions.cs
+++ b/PepperDashEssentials/AppServer/DeviceTypeInterfaces/INumericExtensions.cs
@@ -24,8 +24,8 @@ namespace PepperDash.Essentials.Room.Cotija
controller.AddAction(prefix + "num7", new PressAndHoldAction(dev.Digit0));
controller.AddAction(prefix + "num8", new PressAndHoldAction(dev.Digit0));
controller.AddAction(prefix + "num9", new PressAndHoldAction(dev.Digit0));
- controller.AddAction(prefix + "dash", new PressAndHoldAction(dev.KeypadAccessoryButton1));
- controller.AddAction(prefix + "enter", new PressAndHoldAction(dev.KeypadAccessoryButton2));
+ controller.AddAction(prefix + "numDash", new PressAndHoldAction(dev.KeypadAccessoryButton1));
+ controller.AddAction(prefix + "numEnter", new PressAndHoldAction(dev.KeypadAccessoryButton2));
// Deal with the Accessory functions on the numpad later
}
@@ -43,8 +43,8 @@ namespace PepperDash.Essentials.Room.Cotija
controller.RemoveAction(prefix + "num7");
controller.RemoveAction(prefix + "num8");
controller.RemoveAction(prefix + "num9");
- controller.RemoveAction(prefix + "dash");
- controller.RemoveAction(prefix + "enter");
+ controller.RemoveAction(prefix + "numDash");
+ controller.RemoveAction(prefix + "numEnter");
}
}
}
\ No newline at end of file
diff --git a/PepperDashEssentials/Room/Cotija/DeviceTypeInterfaces/IPowerExtensions.cs b/PepperDashEssentials/AppServer/DeviceTypeInterfaces/IPowerExtensions.cs
similarity index 66%
rename from PepperDashEssentials/Room/Cotija/DeviceTypeInterfaces/IPowerExtensions.cs
rename to PepperDashEssentials/AppServer/DeviceTypeInterfaces/IPowerExtensions.cs
index 68b36675..732d2740 100644
--- a/PepperDashEssentials/Room/Cotija/DeviceTypeInterfaces/IPowerExtensions.cs
+++ b/PepperDashEssentials/AppServer/DeviceTypeInterfaces/IPowerExtensions.cs
@@ -14,18 +14,18 @@ namespace PepperDash.Essentials.Room.Cotija
{
var prefix = string.Format(@"/device/{0}/", (dev as IKeyed).Key);
- controller.AddAction(prefix + "poweron", new Action(dev.PowerOn));
- controller.AddAction(prefix + "poweroff", new Action(dev.PowerOff));
- controller.AddAction(prefix + "powertoggle", new Action(dev.PowerToggle));
+ controller.AddAction(prefix + "powerOn", new Action(dev.PowerOn));
+ controller.AddAction(prefix + "powerOff", new Action(dev.PowerOff));
+ controller.AddAction(prefix + "powerToggle", new Action(dev.PowerToggle));
}
public static void UnlinkActions(this IPower dev, CotijaSystemController controller)
{
var prefix = string.Format(@"/device/{0}/", (dev as IKeyed).Key);
- controller.RemoveAction(prefix + "poweron");
- controller.RemoveAction(prefix + "poweroff");
- controller.RemoveAction(prefix + "powertoggle");
+ controller.RemoveAction(prefix + "powerOn");
+ controller.RemoveAction(prefix + "powerOff");
+ controller.RemoveAction(prefix + "powerToggle");
}
}
diff --git a/PepperDashEssentials/Room/Cotija/DeviceTypeInterfaces/ISetTopBoxControlsExtensions.cs b/PepperDashEssentials/AppServer/DeviceTypeInterfaces/ISetTopBoxControlsExtensions.cs
similarity index 87%
rename from PepperDashEssentials/Room/Cotija/DeviceTypeInterfaces/ISetTopBoxControlsExtensions.cs
rename to PepperDashEssentials/AppServer/DeviceTypeInterfaces/ISetTopBoxControlsExtensions.cs
index 7fa7d1b1..99198fa6 100644
--- a/PepperDashEssentials/Room/Cotija/DeviceTypeInterfaces/ISetTopBoxControlsExtensions.cs
+++ b/PepperDashEssentials/AppServer/DeviceTypeInterfaces/ISetTopBoxControlsExtensions.cs
@@ -14,7 +14,7 @@ namespace PepperDash.Essentials.Room.Cotija
{
var prefix = string.Format(@"/device/{0}/", (dev as IKeyed).Key);
- controller.AddAction(prefix + "dvrlist", new PressAndHoldAction(dev.DvrList));
+ controller.AddAction(prefix + "dvrList", new PressAndHoldAction(dev.DvrList));
controller.AddAction(prefix + "replay", new PressAndHoldAction(dev.Replay));
}
@@ -22,7 +22,7 @@ namespace PepperDash.Essentials.Room.Cotija
{
var prefix = string.Format(@"/device/{0}/", (dev as IKeyed).Key);
- controller.RemoveAction(prefix + "dvrlist");
+ controller.RemoveAction(prefix + "dvrList");
controller.RemoveAction(prefix + "replay");
}
}
diff --git a/PepperDashEssentials/Room/Cotija/DeviceTypeInterfaces/ITransportExtensions.cs b/PepperDashEssentials/AppServer/DeviceTypeInterfaces/ITransportExtensions.cs
similarity index 86%
rename from PepperDashEssentials/Room/Cotija/DeviceTypeInterfaces/ITransportExtensions.cs
rename to PepperDashEssentials/AppServer/DeviceTypeInterfaces/ITransportExtensions.cs
index 34bda457..9463d95f 100644
--- a/PepperDashEssentials/Room/Cotija/DeviceTypeInterfaces/ITransportExtensions.cs
+++ b/PepperDashEssentials/AppServer/DeviceTypeInterfaces/ITransportExtensions.cs
@@ -17,8 +17,8 @@ namespace PepperDash.Essentials.Room.Cotija
controller.AddAction(prefix + "play", new PressAndHoldAction(dev.Play));
controller.AddAction(prefix + "pause", new PressAndHoldAction(dev.Pause));
controller.AddAction(prefix + "stop", new PressAndHoldAction(dev.Stop));
- controller.AddAction(prefix + "prevtrack", new PressAndHoldAction(dev.ChapPlus));
- controller.AddAction(prefix + "nexttrack", new PressAndHoldAction(dev.ChapMinus));
+ controller.AddAction(prefix + "prevTrack", new PressAndHoldAction(dev.ChapPlus));
+ controller.AddAction(prefix + "nextTrack", new PressAndHoldAction(dev.ChapMinus));
controller.AddAction(prefix + "rewind", new PressAndHoldAction(dev.Rewind));
controller.AddAction(prefix + "ffwd", new PressAndHoldAction(dev.FFwd));
controller.AddAction(prefix + "record", new PressAndHoldAction(dev.Record));
@@ -31,8 +31,8 @@ namespace PepperDash.Essentials.Room.Cotija
controller.RemoveAction(prefix + "play");
controller.RemoveAction(prefix + "pause");
controller.RemoveAction(prefix + "stop");
- controller.RemoveAction(prefix + "prevtrack");
- controller.RemoveAction(prefix + "nexttrack");
+ controller.RemoveAction(prefix + "prevTrack");
+ controller.RemoveAction(prefix + "nextTrack");
controller.RemoveAction(prefix + "rewind");
controller.RemoveAction(prefix + "ffwd");
controller.RemoveAction(prefix + "record");
diff --git a/PepperDashEssentials/AppServer/Interfaces.cs b/PepperDashEssentials/AppServer/Interfaces.cs
new file mode 100644
index 00000000..df651ef8
--- /dev/null
+++ b/PepperDashEssentials/AppServer/Interfaces.cs
@@ -0,0 +1,60 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Crestron.SimplSharp;
+
+using PepperDash.Essentials.Core;
+
+namespace PepperDash.Essentials.Room.Cotija
+{
+ ///
+ /// Represents a room whose configuration is derived from runtime data,
+ /// perhaps from another program, and that the data may not be fully
+ /// available at startup.
+ ///
+ public interface IDelayedConfiguration
+ {
+ event EventHandler ConfigurationIsReady;
+ }
+}
+
+namespace PepperDash.Essentials
+{
+ ///
+ /// For rooms with a single presentation source, change event
+ ///
+ public interface IHasCurrentSourceInfoChange
+ {
+ string CurrentSourceInfoKey { get; }
+ SourceListItem CurrentSourceInfo { get; }
+ event SourceInfoChangeHandler CurrentSingleSourceChange;
+ }
+
+
+ ///
+ /// For rooms with routing
+ ///
+ public interface IRunRouteAction
+ {
+ void RunRouteAction(string routeKey);
+
+ void RunRouteAction(string routeKey, Action successCallback);
+ }
+
+ ///
+ /// For rooms that default presentation only routing
+ ///
+ public interface IRunDefaultPresentRoute
+ {
+ bool RunDefaultPresentRoute();
+ }
+
+ ///
+ /// For rooms that have default presentation and calling routes
+ ///
+ public interface IRunDefaultCallRoute : IRunDefaultPresentRoute
+ {
+ bool RunDefaultCallRoute();
+ }
+}
\ No newline at end of file
diff --git a/PepperDashEssentials/AppServer/Messengers/AtcDdvc01Messenger.cs b/PepperDashEssentials/AppServer/Messengers/AtcDdvc01Messenger.cs
new file mode 100644
index 00000000..c1491f83
--- /dev/null
+++ b/PepperDashEssentials/AppServer/Messengers/AtcDdvc01Messenger.cs
@@ -0,0 +1,167 @@
+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 SCurrentCallString = 211;
+ ///
+ /// 221
+ ///
+ const uint SHookState = 221;
+
+ ///
+ ///
+ ///
+ 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(),
+ callStatus = EISC.GetString(SHookState),
+ currentCallString = EISC.GetString(SCurrentCallString),
+ currentDialString = EISC.GetString(SCurrentDialString),
+ });
+ }
+
+ ///
+ ///
+ ///
+ ///
+ protected override void CustomRegisterWithAppServer(CotijaSystemController appServerController)
+ {
+ Action