diff --git a/PepperDash.Essentials.4Series.sln b/PepperDash.Essentials.4Series.sln
index e2db852a..a1e57f5e 100644
--- a/PepperDash.Essentials.4Series.sln
+++ b/PepperDash.Essentials.4Series.sln
@@ -9,6 +9,14 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PepperDash.Essentials", "sr
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PepperDash.Essentials.Core", "src\PepperDash.Essentials.Core\PepperDash.Essentials.Core.csproj", "{3D192FED-8FFC-4CB5-B5F7-BA307ABA254B}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Mobile Control", "Mobile Control", "{B24989D7-32B5-48D5-9AE1-5F3B17D25206}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PepperDash.Essentials.MobileControl", "src\PepperDash.Essentials.MobileControl\PepperDash.Essentials.MobileControl.csproj", "{F6D362DE-2256-44B1-927A-8CE4705D839A}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PepperDash.Essentials.MobileControl.Messengers", "src\PepperDash.Essentials.MobileControl.Messengers\PepperDash.Essentials.MobileControl.Messengers.csproj", "{B438694F-8FF7-464A-9EC8-10427374471F}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Essentials", "Essentials", "{AD98B742-8D85-481C-A69D-D8D8ABED39EA}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug 4.7.2|Any CPU = Debug 4.7.2|Any CPU
@@ -34,10 +42,29 @@ Global
{3D192FED-8FFC-4CB5-B5F7-BA307ABA254B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3D192FED-8FFC-4CB5-B5F7-BA307ABA254B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3D192FED-8FFC-4CB5-B5F7-BA307ABA254B}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F6D362DE-2256-44B1-927A-8CE4705D839A}.Debug 4.7.2|Any CPU.ActiveCfg = Debug|Any CPU
+ {F6D362DE-2256-44B1-927A-8CE4705D839A}.Debug 4.7.2|Any CPU.Build.0 = Debug|Any CPU
+ {F6D362DE-2256-44B1-927A-8CE4705D839A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F6D362DE-2256-44B1-927A-8CE4705D839A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F6D362DE-2256-44B1-927A-8CE4705D839A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F6D362DE-2256-44B1-927A-8CE4705D839A}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B438694F-8FF7-464A-9EC8-10427374471F}.Debug 4.7.2|Any CPU.ActiveCfg = Debug|Any CPU
+ {B438694F-8FF7-464A-9EC8-10427374471F}.Debug 4.7.2|Any CPU.Build.0 = Debug|Any CPU
+ {B438694F-8FF7-464A-9EC8-10427374471F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B438694F-8FF7-464A-9EC8-10427374471F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B438694F-8FF7-464A-9EC8-10427374471F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B438694F-8FF7-464A-9EC8-10427374471F}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {53E204B7-97DD-441D-A96C-721DF014DF82} = {AD98B742-8D85-481C-A69D-D8D8ABED39EA}
+ {CB3B11BA-625C-4D35-B663-FDC5BE9A230E} = {AD98B742-8D85-481C-A69D-D8D8ABED39EA}
+ {3D192FED-8FFC-4CB5-B5F7-BA307ABA254B} = {AD98B742-8D85-481C-A69D-D8D8ABED39EA}
+ {F6D362DE-2256-44B1-927A-8CE4705D839A} = {B24989D7-32B5-48D5-9AE1-5F3B17D25206}
+ {B438694F-8FF7-464A-9EC8-10427374471F} = {B24989D7-32B5-48D5-9AE1-5F3B17D25206}
+ EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {6907A4BF-7201-47CF-AAB1-3597F3B8E1C3}
EndGlobalSection
diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/ContentTypes.cs b/src/PepperDash.Essentials.MobileControl.Messengers/ContentTypes.cs
new file mode 100644
index 00000000..476747b0
--- /dev/null
+++ b/src/PepperDash.Essentials.MobileControl.Messengers/ContentTypes.cs
@@ -0,0 +1,31 @@
+using Newtonsoft.Json;
+using PepperDash.Essentials.Core;
+
+namespace PepperDash.Essentials.AppServer
+{
+ public class SourceSelectMessageContent
+ {
+
+ [JsonProperty("sourceListItemKey")]
+ public string SourceListItemKey { get; set; }
+ [JsonProperty("sourceListKey")]
+ public string SourceListKey { get; set; }
+ }
+
+ public class DirectRoute
+ {
+
+ [JsonProperty("sourceKey")]
+ public string SourceKey { get; set; }
+ [JsonProperty("destinationKey")]
+ public string DestinationKey { get; set; }
+ [JsonProperty("signalType")]
+ public eRoutingSignalType SignalType { get; set; }
+ }
+
+ ///
+ ///
+ ///
+ ///
+ public delegate void PressAndHoldAction(bool b);
+}
diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/DeviceTypeExtenstions/CoreDisplayBaseMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/DeviceTypeExtenstions/CoreDisplayBaseMessenger.cs
new file mode 100644
index 00000000..bf782710
--- /dev/null
+++ b/src/PepperDash.Essentials.MobileControl.Messengers/DeviceTypeExtenstions/CoreDisplayBaseMessenger.cs
@@ -0,0 +1,61 @@
+using Newtonsoft.Json.Linq;
+using PepperDash.Core;
+using PepperDash.Essentials.AppServer;
+using PepperDash.Essentials.AppServer.Messengers;
+using PepperDash.Essentials.Core.DeviceTypeInterfaces;
+using System;
+using System.Linq;
+using DisplayBase = PepperDash.Essentials.Core.DisplayBase;
+
+namespace PepperDash.Essentials.Room.MobileControl
+{
+ public class CoreDisplayBaseMessenger: MessengerBase
+ {
+ private readonly DisplayBase display;
+
+ public CoreDisplayBaseMessenger(string key, string messagePath, DisplayBase device) : base(key, messagePath, device)
+ {
+ display = device;
+ }
+
+ protected override void RegisterActions()
+ {
+ base.RegisterActions();
+
+ /* AddAction("/powerOn", (id, content) => display.PowerOn());
+ AddAction("/powerOff", (id, content) => display.PowerOff());
+ AddAction("/powerToggle", (id, content) => display.PowerToggle());*/
+
+ AddAction("/inputSelect", (id, content) =>
+ {
+ var s = content.ToObject>();
+
+ var inputPort = display.InputPorts.FirstOrDefault(i => i.Key == s.Value);
+
+ if (inputPort == null)
+ {
+ Debug.Console(1, "No input named {0} found for device {1}", s, display.Key);
+ return;
+ }
+
+ display.ExecuteSwitch(inputPort.Selector);
+ });
+
+ AddAction("/inputs", (id, content) =>
+ {
+ var inputsList = display.InputPorts.Select(p => p.Key).ToList();
+
+ var messageObject = new MobileControlMessage
+ {
+ Type = MessagePath + "/inputs",
+ Content = JToken.FromObject(new
+ {
+ inputKeys = inputsList,
+ })
+ };
+
+ AppServerController.SendMessageObject(messageObject);
+ });
+ }
+ }
+}
diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/DeviceTypeExtenstions/DisplayBaseMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/DeviceTypeExtenstions/DisplayBaseMessenger.cs
new file mode 100644
index 00000000..659c62e8
--- /dev/null
+++ b/src/PepperDash.Essentials.MobileControl.Messengers/DeviceTypeExtenstions/DisplayBaseMessenger.cs
@@ -0,0 +1,61 @@
+using Newtonsoft.Json.Linq;
+using PepperDash.Core;
+using PepperDash.Essentials.AppServer;
+using PepperDash.Essentials.AppServer.Messengers;
+using PepperDash.Essentials.Core.DeviceTypeInterfaces;
+using System;
+using System.Linq;
+using DisplayBase = PepperDash.Essentials.Devices.Common.Displays.DisplayBase;
+
+namespace PepperDash.Essentials.Room.MobileControl
+{
+ public class DisplayBaseMessenger: MessengerBase
+ {
+ private readonly DisplayBase display;
+
+ public DisplayBaseMessenger(string key, string messagePath, DisplayBase device) : base(key, messagePath, device)
+ {
+ display = device;
+ }
+
+ protected override void RegisterActions()
+ {
+ base.RegisterActions();
+
+ /*AddAction("/powerOn", (id, content) => display.PowerOn());
+ AddAction("/powerOff", (id, content) => display.PowerOff());
+ AddAction("/powerToggle", (id, content) => display.PowerToggle());*/
+
+ AddAction("/inputSelect", (id, content) =>
+ {
+ var s = content.ToObject>();
+
+ var inputPort = display.InputPorts.FirstOrDefault(i => i.Key == s.Value);
+
+ if (inputPort == null)
+ {
+ Debug.Console(1, "No input named {0} found for device {1}", s, display.Key);
+ return;
+ }
+
+ display.ExecuteSwitch(inputPort.Selector);
+ });
+
+ AddAction("/inputs", (id, content) =>
+ {
+ var inputsList = display.InputPorts.Select(p => p.Key).ToList();
+
+ var messageObject = new MobileControlMessage
+ {
+ Type = MessagePath + "/inputs",
+ Content = JToken.FromObject(new
+ {
+ inputKeys = inputsList,
+ })
+ };
+
+ AppServerController.SendMessageObject(messageObject);
+ });
+ }
+ }
+}
diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/DeviceTypeExtenstions/IChannelMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/DeviceTypeExtenstions/IChannelMessenger.cs
new file mode 100644
index 00000000..33d5ecd7
--- /dev/null
+++ b/src/PepperDash.Essentials.MobileControl.Messengers/DeviceTypeExtenstions/IChannelMessenger.cs
@@ -0,0 +1,31 @@
+using PepperDash.Core;
+using PepperDash.Essentials.AppServer.Messengers;
+using PepperDash.Essentials.Core;
+using PepperDash.Essentials.Core.DeviceTypeInterfaces;
+#if SERIES4
+#endif
+namespace PepperDash.Essentials.Room.MobileControl
+{
+ public class IChannelMessenger:MessengerBase
+ {
+ private readonly IChannel channelDevice;
+
+ public IChannelMessenger(string key, string messagePath, Device device) : base(key, messagePath, device)
+ {
+ channelDevice = device as IChannel;
+ }
+
+ protected override void RegisterActions()
+ {
+ base.RegisterActions();
+
+ AddAction("/chanUp", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) => channelDevice?.ChannelUp(b)));
+
+ AddAction("/chanDown", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) => channelDevice?.ChannelDown(b)));
+ AddAction("/lastChan", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) => channelDevice?.LastChannel(b)));
+ AddAction("/guide", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) => channelDevice?.Guide(b)));
+ AddAction("/info", (id, content) => PressAndHoldHandler.HandlePressAndHold (DeviceKey, content, (b) => channelDevice?.Info(b)));
+ AddAction("/exit", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) => channelDevice?.Exit(b)));
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/DeviceTypeExtenstions/IColorMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/DeviceTypeExtenstions/IColorMessenger.cs
new file mode 100644
index 00000000..d23fbf2b
--- /dev/null
+++ b/src/PepperDash.Essentials.MobileControl.Messengers/DeviceTypeExtenstions/IColorMessenger.cs
@@ -0,0 +1,26 @@
+using PepperDash.Core;
+using PepperDash.Essentials.AppServer.Messengers;
+using PepperDash.Essentials.Core;
+using PepperDash.Essentials.Core.DeviceTypeInterfaces;
+
+namespace PepperDash.Essentials.Room.MobileControl
+{
+ public class IColorMessenger:MessengerBase
+ {
+ private readonly IColor colorDevice;
+ public IColorMessenger(string key, string messagePath, Device device) : base(key, messagePath, device)
+ {
+ colorDevice = device as IColor;
+ }
+
+ protected override void RegisterActions()
+ {
+ base.RegisterActions();
+
+ AddAction("/red", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) => colorDevice?.Red(b)));
+ AddAction("/green", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) => colorDevice?.Green(b)));
+ AddAction("/yellow", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) => colorDevice?.Yellow(b)));
+ AddAction("/blue", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) => colorDevice?.Blue(b)));
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/DeviceTypeExtenstions/IDPadMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/DeviceTypeExtenstions/IDPadMessenger.cs
new file mode 100644
index 00000000..6f72856c
--- /dev/null
+++ b/src/PepperDash.Essentials.MobileControl.Messengers/DeviceTypeExtenstions/IDPadMessenger.cs
@@ -0,0 +1,31 @@
+using PepperDash.Core;
+using PepperDash.Essentials.AppServer.Messengers;
+using PepperDash.Essentials.Core;
+using PepperDash.Essentials.Core.DeviceTypeInterfaces;
+#if SERIES4
+#endif
+namespace PepperDash.Essentials.Room.MobileControl
+{
+ public class IDPadMessenger:MessengerBase
+ {
+ private readonly IDPad dpadDevice;
+ public IDPadMessenger(string key, string messagePath, Device device) : base(key, messagePath, device)
+ {
+ dpadDevice = device as IDPad;
+ }
+
+
+ protected override void RegisterActions()
+ {
+ base.RegisterActions();
+
+ AddAction("/up", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) => dpadDevice?.Up(b)));
+ AddAction("/down", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) => dpadDevice?.Down(b)));
+ AddAction("/left", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) => dpadDevice?.Left(b)));
+ AddAction("/right", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) => dpadDevice?.Right(b)));
+ AddAction("/select", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) => dpadDevice?.Select(b)));
+ AddAction("/menu", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) => dpadDevice?.Menu(b)));
+ AddAction("/exit", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) => dpadDevice?.Exit(b)));
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/DeviceTypeExtenstions/IDvrMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/DeviceTypeExtenstions/IDvrMessenger.cs
new file mode 100644
index 00000000..4692aaf0
--- /dev/null
+++ b/src/PepperDash.Essentials.MobileControl.Messengers/DeviceTypeExtenstions/IDvrMessenger.cs
@@ -0,0 +1,26 @@
+using PepperDash.Core;
+using PepperDash.Essentials.AppServer.Messengers;
+using PepperDash.Essentials.Core;
+using PepperDash.Essentials.Core.DeviceTypeInterfaces;
+#if SERIES4
+#endif
+namespace PepperDash.Essentials.Room.MobileControl
+{
+ public class IDvrMessenger: MessengerBase
+ {
+ private readonly IDvr dvrDevice;
+ public IDvrMessenger(string key, string messagePath, Device device) : base(key, messagePath, device)
+ {
+ dvrDevice = device as IDvr;
+ }
+
+ protected override void RegisterActions()
+ {
+ base.RegisterActions();
+
+ AddAction("/dvrlist", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) => dvrDevice?.DvrList(b)));
+ AddAction("/record", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) => dvrDevice?.Record(b)));
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/DeviceTypeExtenstions/IHasPowerMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/DeviceTypeExtenstions/IHasPowerMessenger.cs
new file mode 100644
index 00000000..33ce7ea1
--- /dev/null
+++ b/src/PepperDash.Essentials.MobileControl.Messengers/DeviceTypeExtenstions/IHasPowerMessenger.cs
@@ -0,0 +1,25 @@
+using PepperDash.Core;
+using PepperDash.Essentials.AppServer.Messengers;
+using PepperDash.Essentials.Core;
+using PepperDash.Essentials.Core.DeviceTypeInterfaces;
+
+namespace PepperDash.Essentials.Room.MobileControl
+{
+ public class IHasPowerMessenger:MessengerBase
+ {
+ private readonly IHasPowerControl powerDevice;
+ public IHasPowerMessenger(string key, string messagePath, Device device) : base(key, messagePath, device)
+ {
+ powerDevice = device as IHasPowerControl;
+ }
+
+ protected override void RegisterActions()
+ {
+ base.RegisterActions();
+
+ AddAction("/powerOn", (id, content) => powerDevice?.PowerOn());
+ AddAction("/powerOff", (id, content) => powerDevice?.PowerOff());
+ AddAction("/powerToggle", (id, content) => powerDevice?.PowerToggle());
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/DeviceTypeExtenstions/INumericMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/DeviceTypeExtenstions/INumericMessenger.cs
new file mode 100644
index 00000000..dc3290c9
--- /dev/null
+++ b/src/PepperDash.Essentials.MobileControl.Messengers/DeviceTypeExtenstions/INumericMessenger.cs
@@ -0,0 +1,36 @@
+using PepperDash.Core;
+using PepperDash.Essentials.AppServer.Messengers;
+using PepperDash.Essentials.Core;
+using PepperDash.Essentials.Core.DeviceTypeInterfaces;
+#if SERIES4
+#endif
+namespace PepperDash.Essentials.Room.MobileControl
+{
+ public class INumericKeypadMessenger:MessengerBase
+ {
+ private readonly INumericKeypad keypadDevice;
+ public INumericKeypadMessenger(string key, string messagePath, Device device) : base(key, messagePath, device)
+ {
+ keypadDevice = device as INumericKeypad;
+ }
+
+ protected override void RegisterActions()
+ {
+ base.RegisterActions();
+
+ AddAction("/num0", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) => keypadDevice?.Digit0(b)));
+ AddAction("/num1", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) => keypadDevice?.Digit1(b)));
+ AddAction("/num2", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) => keypadDevice?.Digit2(b)));
+ AddAction("/num3", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) => keypadDevice?.Digit3(b)));
+ AddAction("/num4", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) => keypadDevice?.Digit4(b)));
+ AddAction("/num5", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) => keypadDevice?.Digit5(b)));
+ AddAction("/num6", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) => keypadDevice?.Digit6(b)));
+ AddAction("/num7", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) => keypadDevice?.Digit7(b)));
+ AddAction("/num8", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) => keypadDevice?.Digit8(b)));
+ AddAction("/num9", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) => keypadDevice?.Digit9(b)));
+ AddAction("/numDash", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) => keypadDevice?.KeypadAccessoryButton1(b)));
+ AddAction("/numEnter", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) => keypadDevice?.KeypadAccessoryButton2(b)));
+ // Deal with the Accessory functions on the numpad later
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/DeviceTypeExtenstions/ISetTopBoxControlsMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/DeviceTypeExtenstions/ISetTopBoxControlsMessenger.cs
new file mode 100644
index 00000000..9b88b035
--- /dev/null
+++ b/src/PepperDash.Essentials.MobileControl.Messengers/DeviceTypeExtenstions/ISetTopBoxControlsMessenger.cs
@@ -0,0 +1,41 @@
+using PepperDash.Core;
+using PepperDash.Essentials.AppServer.Messengers;
+using PepperDash.Essentials.Core;
+using PepperDash.Essentials.Core.DeviceTypeInterfaces;
+#if SERIES4
+#endif
+namespace PepperDash.Essentials.Room.MobileControl
+{
+ public class ISetTopBoxControlsMessenger:MessengerBase
+ {
+ private readonly ISetTopBoxControls stbDevice;
+ public ISetTopBoxControlsMessenger(string key, string messagePath, IKeyName device) : base(key, messagePath, device)
+ {
+ stbDevice = device as ISetTopBoxControls;
+ }
+
+ protected override void RegisterActions()
+ {
+ base.RegisterActions();
+ AddAction("/fullStatus", (id, content) => SendISetTopBoxControlsFullMessageObject());
+ AddAction("/dvrList", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) => stbDevice?.DvrList(b)));
+ AddAction("/replay", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) => stbDevice?.Replay(b)));
+ }
+ ///
+ /// Helper method to build call status for vtc
+ ///
+ ///
+ private void SendISetTopBoxControlsFullMessageObject()
+ {
+
+ PostStatusMessage( new SetTopBoxControlsState());
+
+
+ }
+ }
+
+ public class SetTopBoxControlsState : DeviceStateMessageBase
+ {
+
+ }
+}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/DeviceTypeExtenstions/ITransportMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/DeviceTypeExtenstions/ITransportMessenger.cs
new file mode 100644
index 00000000..bc2f770e
--- /dev/null
+++ b/src/PepperDash.Essentials.MobileControl.Messengers/DeviceTypeExtenstions/ITransportMessenger.cs
@@ -0,0 +1,32 @@
+using PepperDash.Core;
+using PepperDash.Essentials.AppServer.Messengers;
+using PepperDash.Essentials.Core;
+using PepperDash.Essentials.Core.DeviceTypeInterfaces;
+#if SERIES4
+#endif
+namespace PepperDash.Essentials.Room.MobileControl
+{
+ public class ITransportMessenger:MessengerBase
+ {
+ private readonly ITransport transportDevice;
+ public ITransportMessenger(string key, string messagePath, Device device) : base(key, messagePath, device)
+ {
+ transportDevice = device as ITransport;
+ }
+
+ protected override void RegisterActions()
+ {
+ base.RegisterActions();
+
+ AddAction("/play", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) => transportDevice?.Play(b)));
+ AddAction("/pause", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) => transportDevice?.Pause(b)));
+ AddAction("/stop", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) => transportDevice?.Stop(b)));
+ AddAction("/prevTrack", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) => transportDevice?.ChapPlus(b)));
+ AddAction("/nextTrack", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) => transportDevice?.ChapMinus(b)));
+ AddAction("/rewind", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) => transportDevice?.Rewind(b)));
+ AddAction("/ffwd", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) => transportDevice?.FFwd(b)));
+ AddAction("/record", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) => transportDevice?.Record(b)));
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/AudioCodecBaseMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/AudioCodecBaseMessenger.cs
new file mode 100644
index 00000000..0472241d
--- /dev/null
+++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/AudioCodecBaseMessenger.cs
@@ -0,0 +1,120 @@
+using Newtonsoft.Json.Linq;
+using PepperDash.Essentials.Core.DeviceTypeInterfaces;
+using PepperDash.Essentials.Devices.Common.AudioCodec;
+using PepperDash.Essentials.Devices.Common.Codec;
+using System;
+using System.Linq;
+
+namespace PepperDash.Essentials.AppServer.Messengers
+{
+ ///
+ /// Provides a messaging bridge for an AudioCodecBase device
+ ///
+ public class AudioCodecBaseMessenger : MessengerBase
+ {
+ ///
+ /// Device being bridged
+ ///
+ public AudioCodecBase Codec { get; private set; }
+
+ ///
+ /// Constuctor
+ ///
+ ///
+ ///
+ ///
+ public AudioCodecBaseMessenger(string key, AudioCodecBase codec, string messagePath)
+ : base(key, messagePath, codec)
+ {
+ Codec = codec ?? throw new ArgumentNullException("codec");
+ codec.CallStatusChange += Codec_CallStatusChange;
+ }
+
+#if SERIES4
+ protected override void RegisterActions()
+#else
+ protected override void CustomRegisterWithAppServer(MobileControlSystemController appServerController)
+#endif
+ {
+ base.RegisterActions();
+
+ AddAction("/fullStatus", (id, content) => SendAtcFullMessageObject());
+ AddAction("/dial", (id, content) =>
+ {
+ var msg = content.ToObject>();
+
+ Codec.Dial(msg.Value);
+ });
+
+ AddAction("/endCallById", (id, content) =>
+ {
+ var msg = content.ToObject>();
+
+ var call = GetCallWithId(msg.Value);
+ if (call != null)
+ Codec.EndCall(call);
+ });
+
+ AddAction("/endAllCalls", (id, content) => Codec.EndAllCalls());
+ AddAction("/dtmf", (id, content) =>
+ {
+ var msg = content.ToObject>();
+
+ Codec.SendDtmf(msg.Value);
+ });
+
+ AddAction("/rejectById", (id, content) =>
+ {
+ var msg = content.ToObject>();
+
+ var call = GetCallWithId(msg.Value);
+
+ if (call != null)
+ Codec.RejectCall(call);
+ });
+
+ AddAction("/acceptById", (id, content) =>
+ {
+ var msg = content.ToObject>();
+ var call = GetCallWithId(msg.Value);
+ if (call != null)
+ Codec.AcceptCall(call);
+ });
+ }
+
+ ///
+ /// Helper to grab a call with string ID
+ ///
+ ///
+ ///
+ private CodecActiveCallItem GetCallWithId(string id)
+ {
+ return Codec.ActiveCalls.FirstOrDefault(c => c.Id == id);
+ }
+
+ private void Codec_CallStatusChange(object sender, CodecCallStatusItemChangeEventArgs e)
+ {
+ SendAtcFullMessageObject();
+ }
+
+ ///
+ /// Helper method to build call status for vtc
+ ///
+ ///
+ private void SendAtcFullMessageObject()
+ {
+ var info = Codec.CodecInfo;
+
+ PostStatusMessage(JToken.FromObject(new
+ {
+ isInCall = Codec.IsInCall,
+ calls = Codec.ActiveCalls,
+ info = new
+ {
+ phoneNumber = info.PhoneNumber
+ }
+ })
+ );
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/CameraBaseMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/CameraBaseMessenger.cs
new file mode 100644
index 00000000..1a8f0706
--- /dev/null
+++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/CameraBaseMessenger.cs
@@ -0,0 +1,209 @@
+using Newtonsoft.Json.Linq;
+using PepperDash.Essentials.Core;
+using PepperDash.Essentials.Core.DeviceTypeInterfaces;
+using PepperDash.Essentials.Devices.Common.Cameras;
+using System;
+using System.Collections.Generic;
+
+namespace PepperDash.Essentials.AppServer.Messengers
+{
+ public class CameraBaseMessenger : MessengerBase
+ {
+ ///
+ /// Device being bridged
+ ///
+ public CameraBase Camera { get; set; }
+
+ ///
+ /// Constructor
+ ///
+ ///
+ ///
+ ///
+ public CameraBaseMessenger(string key, CameraBase camera, string messagePath)
+ : base(key, messagePath, camera)
+ {
+ Camera = camera ?? throw new ArgumentNullException("camera");
+
+
+ if (Camera is IHasCameraPresets presetsCamera)
+ {
+ presetsCamera.PresetsListHasChanged += PresetsCamera_PresetsListHasChanged;
+ }
+ }
+
+ private void PresetsCamera_PresetsListHasChanged(object sender, EventArgs e)
+ {
+ var presetList = new List();
+
+ if (Camera is IHasCameraPresets presetsCamera)
+ presetList = presetsCamera.Presets;
+
+ PostStatusMessage(JToken.FromObject(new
+ {
+ presets = presetList
+ })
+ );
+ }
+
+#if SERIES4
+ protected override void RegisterActions()
+#else
+ protected override void CustomRegisterWithAppServer(MobileControlSystemController appServerController)
+#endif
+ {
+ base.RegisterActions();
+
+ AddAction("/fullStatus", (id, content) => SendCameraFullMessageObject());
+
+
+ if (Camera is IHasCameraPtzControl ptzCamera)
+ {
+ // Need to evaluate how to pass through these P&H actions. Need a method that takes a bool maybe?
+ AddAction("/cameraUp", (id, content) => HandleCameraPressAndHold(content, (b) =>
+ {
+ if (b)
+ {
+ ptzCamera.TiltUp();
+ return;
+ }
+
+ ptzCamera.TiltStop();
+ }));
+ AddAction("/cameraDown", (id, content) => HandleCameraPressAndHold(content, (b) =>
+ {
+ if (b)
+ {
+ ptzCamera.TiltDown();
+ return;
+ }
+
+ ptzCamera.TiltStop();
+ }));
+ AddAction("/cameraLeft", (id, content) => HandleCameraPressAndHold(content, (b) =>
+ {
+ if (b)
+ {
+ ptzCamera.PanLeft();
+ return;
+ }
+
+ ptzCamera.PanStop();
+ }));
+ AddAction("/cameraRight", (id, content) => HandleCameraPressAndHold(content, (b) =>
+ {
+ if (b)
+ {
+ ptzCamera.PanRight();
+ return;
+ }
+
+ ptzCamera.PanStop();
+ }));
+ AddAction("/cameraZoomIn", (id, content) => HandleCameraPressAndHold(content, (b) =>
+ {
+ if (b)
+ {
+ ptzCamera.ZoomIn();
+ return;
+ }
+
+ ptzCamera.ZoomStop();
+ }));
+ AddAction("/cameraZoomOut", (id, content) => HandleCameraPressAndHold(content, (b) =>
+ {
+ if (b)
+ {
+ ptzCamera.ZoomOut();
+ return;
+ }
+
+ ptzCamera.ZoomStop();
+ }));
+ }
+
+ if (Camera is IHasCameraAutoMode)
+ {
+ AddAction("/cameraModeAuto", (id, content) => (Camera as IHasCameraAutoMode).CameraAutoModeOn());
+
+ AddAction("/cameraModeManual", (id, content) => (Camera as IHasCameraAutoMode).CameraAutoModeOff());
+
+ }
+
+ if (Camera is IHasPowerControl)
+ {
+ AddAction("/cameraModeOff", (id, content) => (Camera as IHasPowerControl).PowerOff());
+ AddAction("/cameraModeManual", (id, content) => (Camera as IHasPowerControl).PowerOn());
+ }
+
+
+ if (Camera is IHasCameraPresets presetsCamera)
+ {
+ for (int i = 1; i <= 6; i++)
+ {
+ var preset = i;
+ AddAction("/cameraPreset" + i, (id, content) =>
+ {
+ var msg = content.ToObject>();
+
+ presetsCamera.PresetSelect(msg.Value);
+ });
+
+ }
+ }
+ }
+
+ private void HandleCameraPressAndHold(JToken content, Action cameraAction)
+ {
+ var state = content.ToObject>();
+
+ var timerHandler = PressAndHoldHandler.GetPressAndHoldHandler(state.Value);
+ if (timerHandler == null)
+ {
+ return;
+ }
+
+ timerHandler(state.Value, cameraAction);
+
+ cameraAction(state.Value.Equals("true", StringComparison.InvariantCultureIgnoreCase));
+ }
+
+ ///
+ /// Helper method to update the full status of the camera
+ ///
+ private void SendCameraFullMessageObject()
+ {
+ var presetList = new List();
+
+ if (Camera is IHasCameraPresets presetsCamera)
+ presetList = presetsCamera.Presets;
+
+ PostStatusMessage(JToken.FromObject(new
+ {
+ cameraManualSupported = Camera is IHasCameraControls,
+ cameraAutoSupported = Camera is IHasCameraAutoMode,
+ cameraOffSupported = Camera is IHasCameraOff,
+ cameraMode = GetCameraMode(),
+ hasPresets = Camera is IHasCameraPresets,
+ presets = presetList
+ })
+ );
+ }
+
+ ///
+ /// Computes the current camera mode
+ ///
+ ///
+ private string GetCameraMode()
+ {
+ string m;
+ if (Camera is IHasCameraAutoMode && (Camera as IHasCameraAutoMode).CameraAutoModeIsOnFeedback.BoolValue)
+ m = eCameraControlMode.Auto.ToString().ToLower();
+ else if (Camera is IHasPowerControlWithFeedback && !(Camera as IHasPowerControlWithFeedback).PowerIsOnFeedback.BoolValue)
+ m = eCameraControlMode.Off.ToString().ToLower();
+ else
+ m = eCameraControlMode.Manual.ToString().ToLower();
+ return m;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/CoreTwoWayDisplayBaseMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/CoreTwoWayDisplayBaseMessenger.cs
new file mode 100644
index 00000000..8bce8a4e
--- /dev/null
+++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/CoreTwoWayDisplayBaseMessenger.cs
@@ -0,0 +1,91 @@
+using Newtonsoft.Json.Linq;
+using PepperDash.Core;
+using PepperDash.Essentials.Core;
+using PepperDash.Essentials.Core.DeviceTypeInterfaces;
+
+namespace PepperDash.Essentials.AppServer.Messengers
+{
+ public class CoreTwoWayDisplayBaseMessenger : MessengerBase
+ {
+ private readonly TwoWayDisplayBase _display;
+
+ public CoreTwoWayDisplayBaseMessenger(string key, string messagePath, Device display)
+ : base(key, messagePath, display)
+ {
+ _display = display as TwoWayDisplayBase;
+ }
+
+ #region Overrides of MessengerBase
+
+ public void SendFullStatus()
+ {
+ var messageObj = new TwoWayDisplayBaseStateMessage
+ {
+ //PowerState = _display.PowerIsOnFeedback.BoolValue,
+ CurrentInput = _display.CurrentInputFeedback.StringValue
+ };
+
+ PostStatusMessage(messageObj);
+ }
+
+#if SERIES4
+ protected override void RegisterActions()
+#else
+ protected override void CustomRegisterWithAppServer(MobileControlSystemController appServerController)
+#endif
+ {
+ base.RegisterActions();
+ if (_display == null)
+ {
+ Debug.Console(0, this, $"Unable to register TwoWayDisplayBase messenger {Key}");
+ return;
+ }
+
+ AddAction("/fullStatus", (id, content) => SendFullStatus());
+
+ _display.PowerIsOnFeedback.OutputChange += PowerIsOnFeedbackOnOutputChange;
+ _display.CurrentInputFeedback.OutputChange += CurrentInputFeedbackOnOutputChange;
+ _display.IsCoolingDownFeedback.OutputChange += IsCoolingFeedbackOnOutputChange;
+ _display.IsWarmingUpFeedback.OutputChange += IsWarmingFeedbackOnOutputChange;
+ }
+
+ private void CurrentInputFeedbackOnOutputChange(object sender, FeedbackEventArgs feedbackEventArgs)
+ {
+ PostStatusMessage(JToken.FromObject(new
+ {
+ currentInput = feedbackEventArgs.StringValue
+ }));
+ }
+
+
+ private void PowerIsOnFeedbackOnOutputChange(object sender, FeedbackEventArgs feedbackEventArgs)
+ {
+ PostStatusMessage(JToken.FromObject(new
+ {
+ powerState = feedbackEventArgs.BoolValue
+ })
+ );
+ }
+
+ private void IsWarmingFeedbackOnOutputChange(object sender, FeedbackEventArgs feedbackEventArgs)
+ {
+ PostStatusMessage(JToken.FromObject(new
+ {
+ isWarming = feedbackEventArgs.BoolValue
+ })
+ );
+
+ }
+
+ private void IsCoolingFeedbackOnOutputChange(object sender, FeedbackEventArgs feedbackEventArgs)
+ {
+ PostStatusMessage(JToken.FromObject(new
+ {
+ isCooling = feedbackEventArgs.BoolValue
+ })
+ );
+ }
+
+ #endregion
+ }
+}
diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/DeviceInfoMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/DeviceInfoMessenger.cs
new file mode 100644
index 00000000..8a17ce01
--- /dev/null
+++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/DeviceInfoMessenger.cs
@@ -0,0 +1,47 @@
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using PepperDash.Core;
+using PepperDash.Essentials.Core.DeviceInfo;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace PepperDash.Essentials.AppServer.Messengers
+{
+ public class DeviceInfoMessenger : MessengerBase
+ {
+ private readonly IDeviceInfoProvider _deviceInfoProvider;
+ public DeviceInfoMessenger(string key, string messagePath, IDeviceInfoProvider device) : base(key, messagePath, device as Device)
+ {
+ _deviceInfoProvider = device;
+ }
+
+ protected override void RegisterActions()
+ {
+ base.RegisterActions();
+
+ _deviceInfoProvider.DeviceInfoChanged += (o, a) =>
+ {
+ PostStatusMessage(JToken.FromObject(new
+ {
+ deviceInfo = a.DeviceInfo
+ }));
+ };
+
+ AddAction("/fullStatus", (id, context) => PostStatusMessage(new DeviceInfoStateMessage
+ {
+ DeviceInfo = _deviceInfoProvider.DeviceInfo
+ }));
+
+ AddAction("/update", (id, context) => _deviceInfoProvider.UpdateDeviceInfo());
+ }
+ }
+
+ public class DeviceInfoStateMessage : DeviceStateMessageBase
+ {
+ [JsonProperty("deviceInfo")]
+ public DeviceInfo DeviceInfo { get; set; }
+ }
+}
diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/DevicePresetsModelMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/DevicePresetsModelMessenger.cs
new file mode 100644
index 00000000..36c6fd62
--- /dev/null
+++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/DevicePresetsModelMessenger.cs
@@ -0,0 +1,101 @@
+using Newtonsoft.Json;
+using PepperDash.Core;
+using PepperDash.Core.Logging;
+using PepperDash.Essentials.Core;
+using PepperDash.Essentials.Core.DeviceTypeInterfaces;
+using PepperDash.Essentials.Core.Presets;
+using System;
+using System.Collections.Generic;
+
+namespace PepperDash.Essentials.AppServer.Messengers
+{
+ public class DevicePresetsModelMessenger : MessengerBase
+ {
+ private readonly ITvPresetsProvider _presetsDevice;
+
+ public DevicePresetsModelMessenger(string key, string messagePath, ITvPresetsProvider presetsDevice)
+ : base(key, messagePath, presetsDevice as Device)
+ {
+ _presetsDevice = presetsDevice;
+ }
+
+ private void SendPresets()
+ {
+ PostStatusMessage(new PresetStateMessage
+ {
+ Favorites = _presetsDevice.TvPresets.PresetsList
+ });
+ }
+
+ private void RecallPreset(ISetTopBoxNumericKeypad device, string channel)
+ {
+ _presetsDevice.TvPresets.Dial(channel, device);
+ }
+
+ private void SavePresets(List presets)
+ {
+ _presetsDevice.TvPresets.UpdatePresets(presets);
+ }
+
+
+ #region Overrides of MessengerBase
+
+#if SERIES4
+ protected override void RegisterActions()
+#else
+ protected override void CustomRegisterWithAppServer(MobileControlSystemController appServerController)
+#endif
+ {
+ AddAction("/presets/fullStatus", (id, content) => {
+ this.LogInformation("getting full status for client {id}", id);
+ try
+ {
+ SendPresets();
+ } catch(Exception ex)
+ {
+ Debug.LogMessage(ex, "Exception sending preset full status", this);
+ }
+ });
+
+ AddAction("/presets/recall", (id, content) =>
+ {
+ var p = content.ToObject();
+
+
+ if (!(DeviceManager.GetDeviceForKey(p.DeviceKey) is ISetTopBoxNumericKeypad dev))
+ {
+ this.LogDebug("Unable to find device with key {0}", p.DeviceKey);
+ return;
+ }
+
+ RecallPreset(dev, p.Preset.Channel);
+ });
+
+ AddAction("/presets/save", (id, content) =>
+ {
+ var presets = content.ToObject>();
+
+ SavePresets(presets);
+ });
+
+ _presetsDevice.TvPresets.PresetsSaved += (p) => SendPresets();
+ }
+
+ #endregion
+ }
+
+ public class PresetChannelMessage
+ {
+ [JsonProperty("preset")]
+ public PresetChannel Preset;
+
+ [JsonProperty("deviceKey")]
+ public string DeviceKey;
+ }
+
+ public class PresetStateMessage : DeviceStateMessageBase
+ {
+ [JsonProperty("favorites", NullValueHandling = NullValueHandling.Ignore)]
+ public List Favorites { get; set; } = new List();
+ }
+}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/DeviceVolumeMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/DeviceVolumeMessenger.cs
new file mode 100644
index 00000000..aeeb3af0
--- /dev/null
+++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/DeviceVolumeMessenger.cs
@@ -0,0 +1,174 @@
+using Newtonsoft.Json;
+using Newtonsoft.Json.Converters;
+using Newtonsoft.Json.Linq;
+using PepperDash.Core;
+using PepperDash.Core.Logging;
+using PepperDash.Essentials.Core;
+using System;
+
+namespace PepperDash.Essentials.AppServer.Messengers
+{
+ public class DeviceVolumeMessenger : MessengerBase
+ {
+ private readonly IBasicVolumeWithFeedback _localDevice;
+
+ public DeviceVolumeMessenger(string key, string messagePath, IBasicVolumeWithFeedback device)
+ : base(key, messagePath, device as IKeyName)
+ {
+ _localDevice = device;
+ }
+
+ private void SendStatus()
+ {
+ try
+ {
+ var messageObj = new VolumeStateMessage
+ {
+ Volume = new Volume
+ {
+ Level = _localDevice?.VolumeLevelFeedback.IntValue ?? -1,
+ Muted = _localDevice?.MuteFeedback.BoolValue ?? false,
+ HasMute = true, // assume all devices have mute for now
+ }
+ };
+
+ if (_localDevice is IBasicVolumeWithFeedbackAdvanced volumeAdvanced)
+ {
+ messageObj.Volume.RawValue = volumeAdvanced.RawVolumeLevel.ToString();
+ messageObj.Volume.Units = volumeAdvanced.Units;
+ }
+
+ PostStatusMessage(messageObj);
+ } catch(Exception ex)
+ {
+ Debug.LogMessage(ex, "Exception sending full status", this);
+ }
+ }
+
+ #region Overrides of MessengerBase
+
+#if SERIES4
+ protected override void RegisterActions()
+#else
+ protected override void CustomRegisterWithAppServer(MobileControlSystemController appServerController)
+#endif
+ {
+ AddAction("/fullStatus", (id, content) => SendStatus());
+
+ AddAction("/level", (id, content) =>
+ {
+ var volume = content.ToObject>();
+
+ _localDevice.SetVolume(volume.Value);
+ });
+
+ AddAction("/muteToggle", (id, content) =>
+ {
+ _localDevice.MuteToggle();
+ });
+
+ AddAction("/muteOn", (id, content) =>
+ {
+ _localDevice.MuteOn();
+ });
+
+ AddAction("/muteOff", (id, content) =>
+ {
+ _localDevice.MuteOff();
+ });
+
+ AddAction("/volumeUp", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) =>
+ {
+ Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "Calling {localDevice} volume up with {value}", DeviceKey, b);
+ try
+ {
+ _localDevice.VolumeUp(b);
+ } catch (Exception ex)
+ {
+ Debug.LogMessage(ex, "Got exception during volume up: {Exception}", null, ex);
+ }
+ }));
+
+
+
+ AddAction("/volumeDown", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) =>
+ {
+ Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "Calling {localDevice} volume down with {value}", DeviceKey, b);
+
+ try
+ {
+ _localDevice.VolumeDown(b);
+ }
+ catch (Exception ex)
+ {
+ Debug.LogMessage(ex, "Got exception during volume down: {Exception}", null, ex);
+ }
+ }));
+
+ _localDevice.MuteFeedback.OutputChange += (sender, args) =>
+ {
+ PostStatusMessage(JToken.FromObject(
+ new
+ {
+ volume = new
+ {
+ muted = args.BoolValue
+ }
+ })
+ );
+ };
+
+ _localDevice.VolumeLevelFeedback.OutputChange += (sender, args) =>
+ {
+ var rawValue = "";
+ if (_localDevice is IBasicVolumeWithFeedbackAdvanced volumeAdvanced)
+ {
+ rawValue = volumeAdvanced.RawVolumeLevel.ToString();
+ }
+
+ var message = new
+ {
+ volume = new
+ {
+ level = args.IntValue,
+ rawValue
+ }
+ };
+
+ PostStatusMessage(JToken.FromObject(message));
+ };
+
+
+ }
+
+ #endregion
+ }
+
+ public class VolumeStateMessage : DeviceStateMessageBase
+ {
+ [JsonProperty("volume", NullValueHandling = NullValueHandling.Ignore)]
+ public Volume Volume { get; set; }
+ }
+
+ public class Volume
+ {
+ [JsonProperty("level", NullValueHandling = NullValueHandling.Ignore)]
+ public int? Level { get; set; }
+
+ [JsonProperty("hasMute", NullValueHandling = NullValueHandling.Ignore)]
+ public bool? HasMute { get; set; }
+
+ [JsonProperty("muted", NullValueHandling = NullValueHandling.Ignore)]
+ public bool? Muted { get; set; }
+
+ [JsonProperty("label", NullValueHandling = NullValueHandling.Ignore)]
+ public string Label { get; set; }
+
+ [JsonProperty("rawValue", NullValueHandling = NullValueHandling.Ignore)]
+ public string RawValue { get; set; }
+
+ [JsonConverter(typeof(StringEnumConverter))]
+ [JsonProperty("units", NullValueHandling = NullValueHandling.Ignore)]
+ public eVolumeLevelUnits? Units { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/GenericMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/GenericMessenger.cs
new file mode 100644
index 00000000..2a52db13
--- /dev/null
+++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/GenericMessenger.cs
@@ -0,0 +1,31 @@
+using PepperDash.Essentials.Core;
+using PepperDash.Essentials.Core.DeviceTypeInterfaces;
+using System;
+
+namespace PepperDash.Essentials.AppServer.Messengers
+{
+ public class GenericMessenger : MessengerBase
+ {
+ public GenericMessenger(string key, EssentialsDevice device, string messagePath) : base(key, messagePath, device)
+ {
+ }
+
+#if SERIES4
+ protected override void RegisterActions()
+#else
+ protected override void CustomRegisterWithAppServer(MobileControlSystemController appServerController)
+#endif
+ {
+ base.RegisterActions();
+
+ AddAction("/fullStatus", (id, content) => SendFullStatus());
+ }
+
+ private void SendFullStatus()
+ {
+ var state = new DeviceStateMessageBase();
+
+ PostStatusMessage(state);
+ }
+ }
+}
diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/ICommunicationMonitorMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/ICommunicationMonitorMessenger.cs
new file mode 100644
index 00000000..7e4d03f6
--- /dev/null
+++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/ICommunicationMonitorMessenger.cs
@@ -0,0 +1,79 @@
+using Newtonsoft.Json;
+using Newtonsoft.Json.Converters;
+using Newtonsoft.Json.Linq;
+using PepperDash.Core;
+using PepperDash.Essentials.Core;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace PepperDash.Essentials.AppServer.Messengers
+{
+ public class ICommunicationMonitorMessenger : MessengerBase
+ {
+ private readonly ICommunicationMonitor _communicationMonitor;
+
+ public ICommunicationMonitorMessenger(string key, string messagePath, ICommunicationMonitor device) : base(key, messagePath, device as IKeyName)
+ {
+ _communicationMonitor = device;
+ }
+
+ protected override void RegisterActions()
+ {
+ base.RegisterActions();
+
+ AddAction("/fullStatus", (id, content) =>
+ {
+ PostStatusMessage(new CommunicationMonitorState
+ {
+ CommunicationMonitor = new CommunicationMonitorProps
+ {
+ IsOnline = _communicationMonitor.CommunicationMonitor.IsOnline,
+ Status = _communicationMonitor.CommunicationMonitor.Status
+ }
+ });
+ });
+
+ _communicationMonitor.CommunicationMonitor.StatusChange += (sender, args) =>
+ {
+ PostStatusMessage(JToken.FromObject(new
+ {
+ commMonitor = new CommunicationMonitorProps
+ {
+ IsOnline = _communicationMonitor.CommunicationMonitor.IsOnline,
+ Status = _communicationMonitor.CommunicationMonitor.Status
+ }
+ }));
+ };
+ }
+ }
+
+ ///
+ /// Represents the state of the communication monitor
+ ///
+ public class CommunicationMonitorState : DeviceStateMessageBase
+ {
+ [JsonProperty("commMonitor", NullValueHandling = NullValueHandling.Ignore)]
+ public CommunicationMonitorProps CommunicationMonitor { get; set; }
+
+ }
+
+ public class CommunicationMonitorProps
+ { ///
+ /// For devices that implement ICommunicationMonitor, reports the online status of the device
+ ///
+ [JsonProperty("isOnline", NullValueHandling = NullValueHandling.Ignore)]
+ public bool? IsOnline { get; set; }
+
+ ///
+ /// For devices that implement ICommunicationMonitor, reports the online status of the device
+ ///
+ [JsonProperty("status", NullValueHandling = NullValueHandling.Ignore)]
+ [JsonConverter(typeof(StringEnumConverter))]
+ public MonitorStatus Status { get; set; }
+
+ }
+
+}
diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IDspPresetsMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IDspPresetsMessenger.cs
new file mode 100644
index 00000000..31529566
--- /dev/null
+++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IDspPresetsMessenger.cs
@@ -0,0 +1,50 @@
+using Newtonsoft.Json;
+using PepperDash.Core;
+using PepperDash.Essentials.Core;
+using System.Collections.Generic;
+
+namespace PepperDash.Essentials.AppServer.Messengers
+{
+ public class IDspPresetsMessenger : MessengerBase
+ {
+ private IDspPresets _device;
+
+ public IDspPresetsMessenger(string key, string messagePath, IDspPresets device)
+ : base(key, messagePath, device as Device)
+ {
+ _device = device;
+ }
+
+ protected override void RegisterActions()
+ {
+ base.RegisterActions();
+
+ AddAction("/fullStatus", (id, content) =>
+ {
+ var message = new IHasDspPresetsStateMessage
+ {
+ Presets = _device.Presets
+ };
+
+ PostStatusMessage(message);
+ });
+
+ AddAction("/recallPreset", (id, content) =>
+ {
+ var presetKey = content.ToObject();
+
+
+ if (!string.IsNullOrEmpty(presetKey))
+ {
+ _device.RecallPreset(presetKey);
+ }
+ });
+ }
+ }
+
+ public class IHasDspPresetsStateMessage : DeviceStateMessageBase
+ {
+ [JsonProperty("presets")]
+ public Dictionary Presets { get; set; }
+ }
+}
diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IEssentialsRoomCombinerMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IEssentialsRoomCombinerMessenger.cs
new file mode 100644
index 00000000..9601bad6
--- /dev/null
+++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IEssentialsRoomCombinerMessenger.cs
@@ -0,0 +1,153 @@
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using PepperDash.Core;
+using PepperDash.Essentials.Core;
+using System;
+using System.Collections.Generic;
+
+namespace PepperDash.Essentials.AppServer.Messengers
+{
+ public class IEssentialsRoomCombinerMessenger : MessengerBase
+ {
+ private readonly IEssentialsRoomCombiner _roomCombiner;
+
+ public IEssentialsRoomCombinerMessenger(string key, string messagePath, IEssentialsRoomCombiner roomCombiner)
+ : base(key, messagePath, roomCombiner as Device)
+ {
+ _roomCombiner = roomCombiner;
+ }
+
+ protected override void RegisterActions()
+ {
+ AddAction("/fullStatus", (id, content) => SendFullStatus());
+
+ AddAction("/setAutoMode", (id, content) =>
+ {
+ _roomCombiner.SetAutoMode();
+ });
+
+ AddAction("/setManualMode", (id, content) =>
+ {
+ _roomCombiner.SetManualMode();
+ });
+
+ AddAction("/toggleMode", (id, content) =>
+ {
+ _roomCombiner.ToggleMode();
+ });
+
+ AddAction("/togglePartitionState", (id, content) =>
+ {
+ try
+ {
+ var partitionKey = content.ToObject();
+
+ _roomCombiner.TogglePartitionState(partitionKey);
+ }
+ catch (Exception e)
+ {
+ Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, $"Error toggling partition state: {e}", this);
+ }
+ });
+
+ AddAction("/setRoomCombinationScenario", (id, content) =>
+ {
+ try
+ {
+ var scenarioKey = content.ToObject();
+
+ _roomCombiner.SetRoomCombinationScenario(scenarioKey);
+ }
+ catch (Exception e)
+ {
+ Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, $"Error toggling partition state: {e}", this);
+ }
+ });
+
+ _roomCombiner.RoomCombinationScenarioChanged += (sender, args) =>
+ {
+ SendFullStatus();
+ };
+
+ _roomCombiner.IsInAutoModeFeedback.OutputChange += (sender, args) =>
+ {
+ var message = new
+ {
+ isInAutoMode = _roomCombiner.IsInAutoModeFeedback.BoolValue
+ };
+
+ PostStatusMessage(JToken.FromObject(message));
+ };
+
+ foreach(var partition in _roomCombiner.Partitions)
+ {
+ partition.PartitionPresentFeedback.OutputChange += (sender, args) =>
+ {
+ var message = new
+ {
+ partitions = _roomCombiner.Partitions
+ };
+
+ PostStatusMessage(JToken.FromObject(message));
+ };
+ }
+ }
+
+ private void SendFullStatus()
+ {
+ try
+ {
+ var rooms = new List();
+
+ foreach (var room in _roomCombiner.Rooms)
+ {
+ rooms.Add(new RoomCombinerRoom{ Key = room.Key, Name = room.Name });
+ }
+
+ var message = new IEssentialsRoomCombinerStateMessage
+ {
+ IsInAutoMode = _roomCombiner.IsInAutoMode,
+ CurrentScenario = _roomCombiner.CurrentScenario,
+ Rooms = rooms,
+ RoomCombinationScenarios = _roomCombiner.RoomCombinationScenarios,
+ Partitions = _roomCombiner.Partitions
+ };
+
+ PostStatusMessage(message);
+ }
+ catch (Exception e)
+ {
+ Debug.Console(0, this, "Error sending full status: {0}", e);
+ }
+ }
+
+ private class RoomCombinerRoom : IKeyName
+ {
+ [JsonProperty("key")]
+ public string Key { get; set; }
+
+ [JsonProperty("name")]
+ public string Name { get; set; }
+ }
+ }
+
+ public class IEssentialsRoomCombinerStateMessage : DeviceStateMessageBase
+ {
+ [JsonProperty("isInAutoMode", NullValueHandling = NullValueHandling.Ignore)]
+ public bool IsInAutoMode { get; set; }
+
+ [JsonProperty("currentScenario", NullValueHandling = NullValueHandling.Ignore)]
+ public IRoomCombinationScenario CurrentScenario { get; set; }
+
+ [JsonProperty("rooms", NullValueHandling = NullValueHandling.Ignore)]
+ public List Rooms { get; set; }
+
+ [JsonProperty("roomCombinationScenarios", NullValueHandling = NullValueHandling.Ignore)]
+ public List RoomCombinationScenarios { get; set; }
+
+ [JsonProperty("partitions", NullValueHandling = NullValueHandling.Ignore)]
+ public List Partitions { get; set; }
+ }
+
+
+}
diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHasCurrentSourceInfoMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHasCurrentSourceInfoMessenger.cs
new file mode 100644
index 00000000..65bc67bd
--- /dev/null
+++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHasCurrentSourceInfoMessenger.cs
@@ -0,0 +1,57 @@
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using PepperDash.Core;
+using PepperDash.Essentials.Core;
+using PepperDash.Essentials.Core.Routing;
+
+namespace PepperDash.Essentials.AppServer.Messengers
+{
+ public class IHasCurrentSourceInfoMessenger : MessengerBase
+ {
+ private readonly IHasCurrentSourceInfoChange sourceDevice;
+ public IHasCurrentSourceInfoMessenger(string key, string messagePath, IHasCurrentSourceInfoChange device) : base(key, messagePath, device as IKeyName)
+ {
+ sourceDevice = device;
+ }
+
+ protected override void RegisterActions()
+ {
+ base.RegisterActions();
+
+ AddAction("/fullStatus", (id, content) =>
+ {
+ var message = new CurrentSourceStateMessage
+ {
+ CurrentSourceKey = sourceDevice.CurrentSourceInfoKey,
+ CurrentSource = sourceDevice.CurrentSourceInfo
+ };
+
+ PostStatusMessage(message);
+ });
+
+ sourceDevice.CurrentSourceChange += (sender, e) => {
+ switch (e)
+ {
+ case ChangeType.DidChange:
+ {
+ PostStatusMessage(JToken.FromObject(new
+ {
+ currentSourceKey = string.IsNullOrEmpty(sourceDevice.CurrentSourceInfoKey) ? string.Empty : sourceDevice.CurrentSourceInfoKey,
+ currentSource = sourceDevice.CurrentSourceInfo
+ }));
+ break;
+ }
+ }
+ };
+ }
+ }
+
+ public class CurrentSourceStateMessage: DeviceStateMessageBase
+ {
+ [JsonProperty("currentSourceKey", NullValueHandling = NullValueHandling.Ignore)]
+ public string CurrentSourceKey { get; set; }
+
+ [JsonProperty("currentSource")]
+ public SourceListItem CurrentSource { get; set; }
+ }
+}
diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHasPowerControlWithFeedbackMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHasPowerControlWithFeedbackMessenger.cs
new file mode 100644
index 00000000..bf838e92
--- /dev/null
+++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHasPowerControlWithFeedbackMessenger.cs
@@ -0,0 +1,57 @@
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using PepperDash.Essentials.Core;
+using PepperDash.Core;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace PepperDash.Essentials.AppServer.Messengers
+{
+ public class IHasPowerControlWithFeedbackMessenger: MessengerBase
+ {
+ private readonly IHasPowerControlWithFeedback _powerControl;
+
+ public IHasPowerControlWithFeedbackMessenger(string key, string messagePath, IHasPowerControlWithFeedback powerControl)
+ : base(key, messagePath, powerControl as Device)
+ {
+ _powerControl = powerControl;
+ }
+
+ public void SendFullStatus()
+ {
+ var messageObj = new PowerControlWithFeedbackStateMessage
+ {
+ PowerState = _powerControl.PowerIsOnFeedback.BoolValue
+ };
+
+ PostStatusMessage(messageObj);
+ }
+
+ protected override void RegisterActions()
+ {
+ base.RegisterActions();
+
+ AddAction("/fullStatus", (id, content) => SendFullStatus());
+
+ _powerControl.PowerIsOnFeedback.OutputChange += PowerIsOnFeedback_OutputChange; ;
+ }
+
+ private void PowerIsOnFeedback_OutputChange(object sender, FeedbackEventArgs args)
+ {
+ PostStatusMessage(JToken.FromObject(new
+ {
+ powerState = args.BoolValue
+ })
+ );
+ }
+ }
+
+ public class PowerControlWithFeedbackStateMessage : DeviceStateMessageBase
+ {
+ [JsonProperty("powerState", NullValueHandling = NullValueHandling.Ignore)]
+ public bool? PowerState { get; set; }
+ }
+}
diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHasScheduleAwarenessMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHasScheduleAwarenessMessenger.cs
new file mode 100644
index 00000000..056f2f22
--- /dev/null
+++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHasScheduleAwarenessMessenger.cs
@@ -0,0 +1,86 @@
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using PepperDash.Core;
+using PepperDash.Essentials.Core.DeviceTypeInterfaces;
+using PepperDash.Essentials.Devices.Common.Codec;
+using System;
+using System.Collections.Generic;
+
+namespace PepperDash.Essentials.AppServer.Messengers
+{
+ public class IHasScheduleAwarenessMessenger : MessengerBase
+ {
+ public IHasScheduleAwareness ScheduleSource { get; private set; }
+
+ public IHasScheduleAwarenessMessenger(string key, IHasScheduleAwareness scheduleSource, string messagePath)
+ : base(key, messagePath, scheduleSource as Device)
+ {
+ ScheduleSource = scheduleSource ?? throw new ArgumentNullException("scheduleSource");
+ ScheduleSource.CodecSchedule.MeetingsListHasChanged += new EventHandler(CodecSchedule_MeetingsListHasChanged);
+ ScheduleSource.CodecSchedule.MeetingEventChange += new EventHandler(CodecSchedule_MeetingEventChange);
+ }
+
+#if SERIES4
+ protected override void RegisterActions()
+#else
+ protected override void CustomRegisterWithAppServer(MobileControlSystemController appServerController)
+#endif
+ {
+ AddAction("/schedule/fullStatus", (id, content) => SendFullScheduleObject());
+ }
+
+ private void CodecSchedule_MeetingEventChange(object sender, MeetingEventArgs e)
+ {
+ PostStatusMessage(JToken.FromObject(new MeetingChangeMessage
+ {
+ MeetingChange = new MeetingChange
+ {
+ ChangeType = e.ChangeType.ToString(),
+ Meeting = e.Meeting
+ }
+ })
+ );
+ }
+
+ private void CodecSchedule_MeetingsListHasChanged(object sender, EventArgs e)
+ {
+ SendFullScheduleObject();
+ }
+
+ ///
+ /// Helper method to send the full schedule data
+ ///
+ private void SendFullScheduleObject()
+ {
+ PostStatusMessage(new FullScheduleMessage
+ {
+ Meetings = ScheduleSource.CodecSchedule.Meetings,
+ MeetingWarningMinutes = ScheduleSource.CodecSchedule.MeetingWarningMinutes
+ });
+ }
+ }
+
+ public class FullScheduleMessage : DeviceStateMessageBase
+ {
+ [JsonProperty("meetings", NullValueHandling = NullValueHandling.Ignore)]
+ public List Meetings { get; set; }
+
+ [JsonProperty("meetingWarningMinutes", NullValueHandling = NullValueHandling.Ignore)]
+ public int MeetingWarningMinutes { get; set; }
+ }
+
+ public class MeetingChangeMessage
+ {
+ [JsonProperty("meetingChange", NullValueHandling = NullValueHandling.Ignore)]
+ public MeetingChange MeetingChange { get; set; }
+ }
+
+ public class MeetingChange
+ {
+ [JsonProperty("changeType", NullValueHandling = NullValueHandling.Ignore)]
+ public string ChangeType { get; set; }
+
+ [JsonProperty("meeting", NullValueHandling = NullValueHandling.Ignore)]
+ public Meeting Meeting { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHumiditySensor.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHumiditySensor.cs
new file mode 100644
index 00000000..0e500250
--- /dev/null
+++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHumiditySensor.cs
@@ -0,0 +1,43 @@
+using Newtonsoft.Json;
+using PepperDash.Core;
+using PepperDash.Essentials.Core.DeviceTypeInterfaces;
+using System;
+
+namespace PepperDash.Essentials.AppServer.Messengers
+{
+ public class IHumiditySensorMessenger : MessengerBase
+ {
+ private readonly IHumiditySensor device;
+
+ public IHumiditySensorMessenger(string key, IHumiditySensor device, string messagePath)
+ : base(key, messagePath, device as Device)
+ {
+ this.device = device;
+ }
+
+ protected override void RegisterActions()
+ {
+ base.RegisterActions();
+
+ AddAction("/fullStatus", (id, content) => SendFullStatus());
+
+ device.HumidityFeedback.OutputChange += new EventHandler((o, a) => SendFullStatus());
+ }
+
+ private void SendFullStatus()
+ {
+ var state = new IHumiditySensorStateMessage
+ {
+ Humidity = string.Format("{0}%", device.HumidityFeedback.UShortValue)
+ };
+
+ PostStatusMessage(state);
+ }
+ }
+
+ public class IHumiditySensorStateMessage : DeviceStateMessageBase
+ {
+ [JsonProperty("humidity")]
+ public string Humidity { get; set; }
+ }
+}
diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/ILevelControlsMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/ILevelControlsMessenger.cs
new file mode 100644
index 00000000..6410d314
--- /dev/null
+++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/ILevelControlsMessenger.cs
@@ -0,0 +1,95 @@
+using Independentsoft.Exchange;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using PepperDash.Core;
+using PepperDash.Essentials.Core.DeviceTypeInterfaces;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace PepperDash.Essentials.AppServer.Messengers
+{
+ public class ILevelControlsMessenger : MessengerBase
+ {
+ private ILevelControls levelControlsDevice;
+ public ILevelControlsMessenger(string key, string messagePath, ILevelControls device) : base(key, messagePath, device as Device)
+ {
+ levelControlsDevice = device;
+ }
+
+ protected override void RegisterActions()
+ {
+ base.RegisterActions();
+
+ AddAction("/fullStatus", (id, context) =>
+ {
+ var message = new LevelControlStateMessage
+ {
+ Levels = levelControlsDevice.LevelControlPoints.ToDictionary(kv => kv.Key, kv => new Volume { Level = kv.Value.VolumeLevelFeedback.IntValue, Muted = kv.Value.MuteFeedback.BoolValue })
+ };
+
+ PostStatusMessage(message);
+ });
+
+ foreach(var levelControl in levelControlsDevice.LevelControlPoints)
+ {
+ // reassigning here just in case of lambda closure issues
+ var key = levelControl.Key;
+ var control = levelControl.Value;
+
+ AddAction($"/{key}/level", (id, content) =>
+ {
+ var request = content.ToObject>();
+
+ control.SetVolume(request.Value);
+ });
+
+ AddAction($"/{key}/muteToggle", (id, content) =>
+ {
+ control.MuteToggle();
+ });
+
+ AddAction($"/{key}/muteOn", (id, content) => control.MuteOn());
+
+ AddAction($"/{key}/muteOff", (id, content) => control.MuteOff());
+
+ AddAction($"/{key}/volumeUp", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) => control.VolumeUp(b)));
+
+ AddAction($"/{key}/volumeDown", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) => control.VolumeDown(b)));
+
+ control.VolumeLevelFeedback.OutputChange += (o, a) => PostStatusMessage(JToken.FromObject(new
+ {
+ levelControls = new Dictionary
+ {
+ {key, new Volume{Level = a.IntValue} }
+ }
+ }));
+
+ control.MuteFeedback.OutputChange += (o, a) => PostStatusMessage(JToken.FromObject(new
+ {
+ levelControls = new Dictionary
+ {
+ {key, new Volume{Muted = a.BoolValue} }
+ }
+ }));
+ }
+ }
+ }
+
+ public class LevelControlStateMessage:DeviceStateMessageBase
+ {
+ [JsonProperty("levelControls")]
+ public Dictionary Levels { get; set; }
+ }
+
+ public class LevelControlRequestMessage
+ {
+ [JsonProperty("key")]
+ public string Key { get; set; }
+
+ [JsonProperty("level", NullValueHandling = NullValueHandling.Ignore)]
+ public ushort? Level { get; set; }
+ }
+}
diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IMatrixRoutingMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IMatrixRoutingMessenger.cs
new file mode 100644
index 00000000..8bacab8f
--- /dev/null
+++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IMatrixRoutingMessenger.cs
@@ -0,0 +1,168 @@
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using PepperDash.Core;
+using PepperDash.Essentials.Core;
+using PepperDash.Essentials.Core.Routing;
+using System.Collections.Generic;
+using System.Linq;
+using Serilog.Events;
+using System;
+
+namespace PepperDash.Essentials.AppServer.Messengers
+{
+ ///
+ /// Messenger for devices that implment IMatrixRouting
+ ///
+ public class IMatrixRoutingMessenger : MessengerBase
+ {
+ private readonly IMatrixRouting matrixDevice;
+ public IMatrixRoutingMessenger(string key, string messagePath, IMatrixRouting device) : base(key, messagePath, device as Device)
+ {
+ matrixDevice = device;
+ }
+
+ protected override void RegisterActions()
+ {
+ base.RegisterActions();
+
+ AddAction("/fullStatus", (id, content) =>
+ {
+ try
+ {
+ Debug.LogMessage(LogEventLevel.Verbose, "InputCount: {inputCount}, OutputCount: {outputCount}", this, matrixDevice.InputSlots.Count, matrixDevice.OutputSlots.Count);
+ var message = new MatrixStateMessage
+ {
+ Outputs = matrixDevice.OutputSlots.ToDictionary(kvp => kvp.Key, kvp => new RoutingOutput(kvp.Value)),
+ Inputs = matrixDevice.InputSlots.ToDictionary(kvp => kvp.Key, kvp => new RoutingInput(kvp.Value)),
+ };
+
+
+ PostStatusMessage(message);
+ }
+ catch (Exception e)
+ {
+ Debug.LogMessage(e, "Exception Getting full status: {@exception}", this, e);
+ }
+ });
+
+ AddAction("/route", (id, content) =>
+ {
+ var request = content.ToObject();
+
+ matrixDevice.Route(request.InputKey, request.OutputKey, request.RouteType);
+ });
+
+ foreach(var output in matrixDevice.OutputSlots)
+ {
+ var key = output.Key;
+ var outputSlot = output.Value;
+
+ outputSlot.OutputSlotChanged += (sender, args) =>
+ {
+ PostStatusMessage(JToken.FromObject(new
+ {
+ outputs = matrixDevice.OutputSlots.ToDictionary(kvp => kvp.Key, kvp => new RoutingOutput(kvp.Value))
+ }));
+ };
+ }
+
+ foreach(var input in matrixDevice.InputSlots)
+ {
+ var key = input.Key;
+ var inputSlot = input.Value;
+
+ inputSlot.VideoSyncChanged += (sender, args) =>
+ {
+ PostStatusMessage(JToken.FromObject(new
+ {
+ inputs = matrixDevice.InputSlots.ToDictionary(kvp => kvp.Key, kvp => new RoutingInput(kvp.Value))
+ }));
+ };
+ }
+ }
+ }
+
+ public class MatrixStateMessage : DeviceStateMessageBase
+ {
+ [JsonProperty("outputs")]
+ public Dictionary Outputs;
+
+ [JsonProperty("inputs")]
+ public Dictionary Inputs;
+ }
+
+ public class RoutingInput
+ {
+ private IRoutingInputSlot _input;
+
+ [JsonProperty("txDeviceKey", NullValueHandling = NullValueHandling.Ignore)]
+ public string TxDeviceKey => _input?.TxDeviceKey;
+
+ [JsonProperty("slotNumber", NullValueHandling = NullValueHandling.Ignore)]
+ public int? SlotNumber => _input?.SlotNumber;
+
+ [JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))]
+ [JsonProperty("supportedSignalTypes", NullValueHandling = NullValueHandling.Ignore)]
+ public eRoutingSignalType? SupportedSignalTypes => _input?.SupportedSignalTypes;
+
+ [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)]
+ public string Name => _input?.Name;
+
+ [JsonProperty("isOnline", NullValueHandling = NullValueHandling.Ignore)]
+ public bool? IsOnline => _input?.IsOnline.BoolValue;
+
+ [JsonProperty("videoSyncDetected", NullValueHandling = NullValueHandling.Ignore)]
+
+ public bool? VideoSyncDetected => _input?.VideoSyncDetected;
+
+ [JsonProperty("key", NullValueHandling = NullValueHandling.Ignore)]
+ public string Key => _input?.Key;
+
+ public RoutingInput(IRoutingInputSlot input)
+ {
+ _input = input;
+ }
+ }
+
+ public class RoutingOutput
+ {
+ private IRoutingOutputSlot _output;
+
+
+ public RoutingOutput(IRoutingOutputSlot output)
+ {
+ _output = output;
+ }
+
+ [JsonProperty("rxDeviceKey")]
+ public string RxDeviceKey => _output.RxDeviceKey;
+
+ [JsonProperty("currentRoutes")]
+ public Dictionary CurrentRoutes => _output.CurrentRoutes.ToDictionary(kvp => kvp.Key.ToString(), kvp => new RoutingInput(kvp.Value));
+
+ [JsonProperty("slotNumber")]
+ public int SlotNumber => _output.SlotNumber;
+
+ [JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))]
+ [JsonProperty("supportedSignalTypes")]
+ public eRoutingSignalType SupportedSignalTypes => _output.SupportedSignalTypes;
+
+ [JsonProperty("name")]
+ public string Name => _output.Name;
+
+ [JsonProperty("key")]
+ public string Key => _output.Key;
+ }
+
+ public class MatrixRouteRequest
+ {
+ [JsonProperty("outputKey")]
+ public string OutputKey { get; set; }
+
+ [JsonProperty("inputKey")]
+ public string InputKey { get; set; }
+
+ [JsonProperty("routeType")]
+ public eRoutingSignalType RouteType { get; set; }
+ }
+}
diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IProjectorScreenLiftControlMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IProjectorScreenLiftControlMessenger.cs
new file mode 100644
index 00000000..9d4a3804
--- /dev/null
+++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IProjectorScreenLiftControlMessenger.cs
@@ -0,0 +1,78 @@
+using Newtonsoft.Json;
+using Newtonsoft.Json.Converters;
+using Newtonsoft.Json.Linq;
+using PepperDash.Core;
+using PepperDash.Essentials.Core.DeviceTypeInterfaces;
+using System;
+
+namespace PepperDash.Essentials.AppServer.Messengers
+{
+ public class IProjectorScreenLiftControlMessenger: MessengerBase
+ {
+ private readonly IProjectorScreenLiftControl device;
+
+ public IProjectorScreenLiftControlMessenger(string key, string messagePath, IProjectorScreenLiftControl screenLiftDevice)
+ : base(key, messagePath, screenLiftDevice as Device)
+ {
+ device = screenLiftDevice;
+ }
+
+ protected override void RegisterActions()
+ {
+ base.RegisterActions();
+
+ AddAction("/fullStatus", (id, content) => SendFullStatus());
+
+ AddAction("/raise", (id, content) =>
+ {
+
+ device.Raise();
+
+ });
+
+ AddAction("/lower", (id, content) =>
+ {
+
+ device.Lower();
+
+ });
+
+ device.PositionChanged += Device_PositionChanged;
+
+ }
+
+ private void Device_PositionChanged(object sender, EventArgs e)
+ {
+ var state = new
+ {
+ inUpPosition = device.InUpPosition
+ };
+ PostStatusMessage(JToken.FromObject(state));
+ }
+
+ private void SendFullStatus()
+ {
+ var state = new ScreenLiftStateMessage
+ {
+ InUpPosition = device.InUpPosition,
+ Type = device.Type,
+ DisplayDeviceKey = device.DisplayDeviceKey
+ };
+
+ PostStatusMessage(state);
+ }
+ }
+
+ public class ScreenLiftStateMessage : DeviceStateMessageBase
+ {
+ [JsonProperty("inUpPosition", NullValueHandling = NullValueHandling.Ignore)]
+ public bool? InUpPosition { get; set; }
+
+ [JsonProperty("displayDeviceKey", NullValueHandling = NullValueHandling.Ignore)]
+ public string DisplayDeviceKey { get; set; }
+
+ [JsonConverter(typeof(StringEnumConverter))]
+ [JsonProperty("type", NullValueHandling = NullValueHandling.Ignore)]
+ public eScreenLiftControlType Type { get; set; }
+ }
+}
diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IRunRouteActionMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IRunRouteActionMessenger.cs
new file mode 100644
index 00000000..88d0a5bf
--- /dev/null
+++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IRunRouteActionMessenger.cs
@@ -0,0 +1,89 @@
+using Newtonsoft.Json;
+using PepperDash.Core;
+using PepperDash.Essentials.Core;
+using PepperDash.Essentials.Core.DeviceTypeInterfaces;
+using System;
+
+
+namespace PepperDash.Essentials.AppServer.Messengers
+{
+ public class RunRouteActionMessenger : MessengerBase
+ {
+ ///
+ /// Device being bridged
+ ///
+ public IRunRouteAction RoutingDevice { get; private set; }
+
+ public RunRouteActionMessenger(string key, IRunRouteAction routingDevice, string messagePath)
+ : base(key, messagePath, routingDevice as Device)
+ {
+ RoutingDevice = routingDevice ?? throw new ArgumentNullException("routingDevice");
+
+
+ if (RoutingDevice is IRoutingSink routingSink)
+ {
+ routingSink.CurrentSourceChange += RoutingSink_CurrentSourceChange;
+ }
+ }
+
+ private void RoutingSink_CurrentSourceChange(SourceListItem info, ChangeType type)
+ {
+ SendRoutingFullMessageObject();
+ }
+
+#if SERIES4
+ protected override void RegisterActions()
+#else
+ protected override void CustomRegisterWithAppServer(MobileControlSystemController appServerController)
+#endif
+ {
+ AddAction("/fullStatus", (id, content) => SendRoutingFullMessageObject());
+
+ AddAction("/source", (id, content) =>
+ {
+ var c = content.ToObject();
+ // assume no sourceListKey
+ var sourceListKey = string.Empty;
+
+ if (!string.IsNullOrEmpty(c.SourceListKey))
+ {
+ // Check for source list in content of message
+ Debug.Console(1, this, "sourceListKey found in message");
+ sourceListKey = c.SourceListKey;
+ }
+
+ RoutingDevice.RunRouteAction(c.SourceListItemKey, sourceListKey);
+ });
+
+ if (RoutingDevice is IRoutingSink sinkDevice)
+ {
+ sinkDevice.CurrentSourceChange += (o, a) => SendRoutingFullMessageObject();
+ }
+ }
+
+ ///
+ /// Helper method to update full status of the routing device
+ ///
+ private void SendRoutingFullMessageObject()
+ {
+ if (RoutingDevice is IRoutingSink sinkDevice)
+ {
+ var sourceKey = sinkDevice.CurrentSourceInfoKey;
+
+ if (string.IsNullOrEmpty(sourceKey))
+ sourceKey = "none";
+
+ PostStatusMessage(new RoutingStateMessage
+ {
+ SelectedSourceKey = sourceKey
+ });
+ }
+ }
+ }
+
+ public class RoutingStateMessage : DeviceStateMessageBase
+ {
+ [JsonProperty("selectedSourceKey")]
+ public string SelectedSourceKey { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/ISelectableItemsMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/ISelectableItemsMessenger.cs
new file mode 100644
index 00000000..88441526
--- /dev/null
+++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/ISelectableItemsMessenger.cs
@@ -0,0 +1,70 @@
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using PepperDash.Core;
+using PepperDash.Essentials.Core.DeviceTypeInterfaces;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Newtonsoft.Json.Converters;
+
+namespace PepperDash.Essentials.AppServer.Messengers
+{
+ public class ISelectableItemsMessenger : MessengerBase
+ {
+ private static readonly JsonSerializer serializer = new JsonSerializer { Converters = { new StringEnumConverter() } };
+ private ISelectableItems itemDevice;
+
+ private readonly string _propName;
+ public ISelectableItemsMessenger(string key, string messagePath, ISelectableItems device, string propName) : base(key, messagePath, device as Device)
+ {
+ itemDevice = device;
+ _propName = propName;
+ }
+
+ protected override void RegisterActions()
+ {
+ base.RegisterActions();
+
+ AddAction("/fullStatus", (id, context) =>
+ {
+ SendFullStatus();
+ });
+
+ itemDevice.ItemsUpdated += (sender, args) =>
+ {
+ SendFullStatus();
+ };
+
+ itemDevice.CurrentItemChanged += (sender, args) =>
+ {
+ SendFullStatus();
+ };
+
+ foreach (var input in itemDevice.Items)
+ {
+ var key = input.Key;
+ var localItem = input.Value;
+
+ AddAction($"/{key}", (id, content) =>
+ {
+ localItem.Select();
+ });
+
+ localItem.ItemUpdated += (sender, args) =>
+ {
+ SendFullStatus();
+ };
+ }
+ }
+
+ private void SendFullStatus()
+ {
+ var stateObject = new JObject();
+ stateObject[_propName] = JToken.FromObject(itemDevice, serializer);
+ PostStatusMessage(stateObject);
+ }
+ }
+
+}
diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IShutdownPromptTimerMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IShutdownPromptTimerMessenger.cs
new file mode 100644
index 00000000..a2e6a6f8
--- /dev/null
+++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IShutdownPromptTimerMessenger.cs
@@ -0,0 +1,93 @@
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using PepperDash.Core;
+using PepperDash.Essentials.Core;
+
+namespace PepperDash.Essentials.AppServer.Messengers
+{
+ public class IShutdownPromptTimerMessenger : MessengerBase
+ {
+ private readonly IShutdownPromptTimer _room;
+
+ public IShutdownPromptTimerMessenger(string key, string messagePath, IShutdownPromptTimer room)
+ : base(key, messagePath, room as Device)
+ {
+ _room = room;
+ }
+
+ protected override void RegisterActions()
+ {
+ AddAction("/status", (id, content) =>
+ {
+ SendFullStatus();
+ });
+
+ AddAction("/setShutdownPromptSeconds", (id, content) =>
+ {
+ var response = content.ToObject();
+
+ _room.SetShutdownPromptSeconds(response);
+
+ SendFullStatus();
+ });
+
+ AddAction("/shutdownStart", (id, content) => _room.StartShutdown(eShutdownType.Manual));
+
+ AddAction("/shutdownEnd", (id, content) => _room.ShutdownPromptTimer.Finish());
+
+ AddAction("/shutdownCancel", (id, content) => _room.ShutdownPromptTimer.Cancel());
+
+
+ _room.ShutdownPromptTimer.HasStarted += (sender, args) =>
+ {
+ PostEventMessage("timerStarted");
+ };
+
+ _room.ShutdownPromptTimer.HasFinished += (sender, args) =>
+ {
+ PostEventMessage("timerFinished");
+ };
+
+ _room.ShutdownPromptTimer.WasCancelled += (sender, args) =>
+ {
+ PostEventMessage("timerCancelled");
+ };
+
+ _room.ShutdownPromptTimer.SecondsRemainingFeedback.OutputChange += (sender, args) =>
+ {
+ var status = new
+ {
+ secondsRemaining = _room.ShutdownPromptTimer.SecondsRemainingFeedback.IntValue,
+ percentageRemaining = _room.ShutdownPromptTimer.PercentFeedback.UShortValue
+ };
+
+ PostStatusMessage(JToken.FromObject(status));
+ };
+ }
+
+ private void SendFullStatus()
+ {
+ var status = new IShutdownPromptTimerStateMessage
+ {
+ ShutdownPromptSeconds = _room.ShutdownPromptTimer.SecondsToCount,
+ SecondsRemaining = _room.ShutdownPromptTimer.SecondsRemainingFeedback.IntValue,
+ PercentageRemaining = _room.ShutdownPromptTimer.PercentFeedback.UShortValue
+ };
+
+ PostStatusMessage(status);
+ }
+ }
+
+
+ public class IShutdownPromptTimerStateMessage : DeviceStateMessageBase
+ {
+ [JsonProperty("secondsRemaining")]
+ public int SecondsRemaining { get; set; }
+
+ [JsonProperty("percentageRemaining")]
+ public int PercentageRemaining { get; set; }
+
+ [JsonProperty("shutdownPromptSeconds")]
+ public int ShutdownPromptSeconds { get; set; }
+ }
+}
diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/ISwitchedOutputMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/ISwitchedOutputMessenger.cs
new file mode 100644
index 00000000..6d3bceb6
--- /dev/null
+++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/ISwitchedOutputMessenger.cs
@@ -0,0 +1,67 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using PepperDash.Essentials.Core.CrestronIO;
+using PepperDash.Essentials.Core.Shades;
+using Newtonsoft.Json;
+using PepperDash.Core;
+
+namespace PepperDash.Essentials.AppServer.Messengers
+{
+ public class ISwitchedOutputMessenger : MessengerBase
+ {
+
+ private readonly ISwitchedOutput device;
+
+ public ISwitchedOutputMessenger(string key, ISwitchedOutput device, string messagePath)
+ : base(key, messagePath, device as Device)
+ {
+ this.device = device;
+ }
+
+#if SERIES4
+ protected override void RegisterActions()
+#else
+ protected override void CustomRegisterWithAppServer(MobileControlSystemController appServerController)
+#endif
+ {
+ base.RegisterActions();
+
+ AddAction("/fullStatus", (id, content) => SendFullStatus());
+
+ AddAction("/on", (id, content) =>
+ {
+
+ device.On();
+
+ });
+
+ AddAction("/off", (id, content) =>
+ {
+
+ device.Off();
+
+ });
+
+ device.OutputIsOnFeedback.OutputChange += new EventHandler((o, a) => SendFullStatus());
+ }
+
+ private void SendFullStatus()
+ {
+ var state = new ISwitchedOutputStateMessage
+ {
+ IsOn = device.OutputIsOnFeedback.BoolValue
+ };
+
+ PostStatusMessage(state);
+ }
+ }
+
+ public class ISwitchedOutputStateMessage : DeviceStateMessageBase
+ {
+ [JsonProperty("isOn")]
+ public bool IsOn { get; set; }
+ }
+}
diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/ITechPasswordMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/ITechPasswordMessenger.cs
new file mode 100644
index 00000000..46e2a5a7
--- /dev/null
+++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/ITechPasswordMessenger.cs
@@ -0,0 +1,95 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics.Contracts;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Independentsoft.Json.Parser;
+using Newtonsoft.Json;
+using PepperDash.Core;
+using PepperDash.Essentials.Core;
+
+namespace PepperDash.Essentials.AppServer.Messengers
+{
+ public class ITechPasswordMessenger : MessengerBase
+ {
+ private readonly ITechPassword _room;
+
+ public ITechPasswordMessenger(string key, string messagePath, ITechPassword room)
+ : base(key, messagePath, room as Device)
+ {
+ _room = room;
+ }
+
+ protected override void RegisterActions()
+ {
+
+ AddAction("/status", (id, content) =>
+ {
+ SendFullStatus();
+ });
+
+ AddAction("/validateTechPassword", (id, content) =>
+ {
+ var password = content.Value("password");
+
+ _room.ValidateTechPassword(password);
+ });
+
+ AddAction("/setTechPassword", (id, content) =>
+ {
+ var response = content.ToObject();
+
+ _room.SetTechPassword(response.OldPassword, response.NewPassword);
+ });
+
+ _room.TechPasswordChanged += (sender, args) =>
+ {
+ PostEventMessage("passwordChangedSuccessfully");
+ };
+
+ _room.TechPasswordValidateResult += (sender, args) =>
+ {
+ var evt = new ITechPasswordEventMessage
+ {
+ IsValid = args.IsValid
+ };
+
+ PostEventMessage(evt, "passwordValidationResult");
+ };
+ }
+
+ private void SendFullStatus()
+ {
+ var status = new ITechPasswordStateMessage
+ {
+ TechPasswordLength = _room.TechPasswordLength
+ };
+
+ PostStatusMessage(status);
+ }
+
+ }
+
+ public class ITechPasswordStateMessage : DeviceStateMessageBase
+ {
+ [JsonProperty("techPasswordLength", NullValueHandling = NullValueHandling.Ignore)]
+ public int? TechPasswordLength { get; set; }
+ }
+
+ public class ITechPasswordEventMessage : DeviceEventMessageBase
+ {
+ [JsonProperty("isValid", NullValueHandling = NullValueHandling.Ignore)]
+ public bool? IsValid { get; set; }
+ }
+
+ class SetTechPasswordContent
+ {
+ [JsonProperty("oldPassword")]
+ public string OldPassword { get; set; }
+
+ [JsonProperty("newPassword")]
+ public string NewPassword { get; set; }
+ }
+
+}
diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/ITemperatureSensorMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/ITemperatureSensorMessenger.cs
new file mode 100644
index 00000000..8d1d5771
--- /dev/null
+++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/ITemperatureSensorMessenger.cs
@@ -0,0 +1,61 @@
+using Newtonsoft.Json;
+using PepperDash.Core;
+using PepperDash.Essentials.Core.DeviceTypeInterfaces;
+using System;
+
+namespace PepperDash.Essentials.AppServer.Messengers
+{
+ public class ITemperatureSensorMessenger : MessengerBase
+ {
+ private readonly ITemperatureSensor device;
+
+ public ITemperatureSensorMessenger(string key, ITemperatureSensor device, string messagePath)
+ : base(key, messagePath, device as Device)
+ {
+ this.device = device;
+ }
+
+ protected override void RegisterActions()
+ {
+ base.RegisterActions();
+
+ AddAction("/fullStatus", (id, content) => SendFullStatus());
+
+ AddAction("/setTemperatureUnitsToCelcius", (id, content) =>
+ {
+ device.SetTemperatureFormat(true);
+ });
+
+ AddAction("/setTemperatureUnitsToFahrenheit", (id, content) =>
+ {
+ device.SetTemperatureFormat(false);
+ });
+
+ device.TemperatureFeedback.OutputChange += new EventHandler((o, a) => SendFullStatus());
+ device.TemperatureInCFeedback.OutputChange += new EventHandler((o, a) => SendFullStatus());
+ }
+
+ private void SendFullStatus()
+ {
+ // format the temperature to a string with one decimal place
+ var tempString = string.Format("{0}.{1}", device.TemperatureFeedback.UShortValue / 10, device.TemperatureFeedback.UShortValue % 10);
+
+ var state = new ITemperatureSensorStateMessage
+ {
+ Temperature = tempString,
+ TemperatureInCelsius = device.TemperatureInCFeedback.BoolValue
+ };
+
+ PostStatusMessage(state);
+ }
+ }
+
+ public class ITemperatureSensorStateMessage : DeviceStateMessageBase
+ {
+ [JsonProperty("temperature")]
+ public string Temperature { get; set; }
+
+ [JsonProperty("temperatureInCelsius")]
+ public bool TemperatureInCelsius { get; set; }
+ }
+}
diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/LightingBaseMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/LightingBaseMessenger.cs
new file mode 100644
index 00000000..4058c3a7
--- /dev/null
+++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/LightingBaseMessenger.cs
@@ -0,0 +1,73 @@
+using Newtonsoft.Json;
+using PepperDash.Core;
+using PepperDash.Essentials.Core.DeviceTypeInterfaces;
+using PepperDash.Essentials.Core.Lighting;
+using System;
+using System.Collections.Generic;
+
+namespace PepperDash.Essentials.AppServer.Messengers
+{
+ public class ILightingScenesMessenger : MessengerBase
+ {
+ protected ILightingScenes Device { get; private set; }
+
+ public ILightingScenesMessenger(string key, ILightingScenes device, string messagePath)
+ : base(key, messagePath, device as Device)
+ {
+ Device = device ?? throw new ArgumentNullException("device");
+ Device.LightingSceneChange += new EventHandler(LightingDevice_LightingSceneChange);
+
+
+ }
+
+ private void LightingDevice_LightingSceneChange(object sender, LightingSceneChangeEventArgs e)
+ {
+ var state = new LightingBaseStateMessage
+ {
+ CurrentLightingScene = e.CurrentLightingScene
+ };
+
+ PostStatusMessage(state);
+ }
+
+#if SERIES4
+ protected override void RegisterActions()
+#else
+ protected override void CustomRegisterWithAppServer(MobileControlSystemController appServerController)
+#endif
+ {
+ base.RegisterActions();
+
+ AddAction("/fullStatus", (id, content) => SendFullStatus());
+
+ AddAction("/selectScene", (id, content) =>
+ {
+ var s = content.ToObject();
+ Device.SelectScene(s);
+ });
+ }
+
+
+ private void SendFullStatus()
+ {
+ Debug.Console(2, "LightingBaseMessenger GetFullStatus");
+
+ var state = new LightingBaseStateMessage
+ {
+ Scenes = Device.LightingScenes,
+ CurrentLightingScene = Device.CurrentLightingScene
+ };
+
+ PostStatusMessage(state);
+ }
+ }
+
+ public class LightingBaseStateMessage : DeviceStateMessageBase
+ {
+ [JsonProperty("scenes", NullValueHandling = NullValueHandling.Ignore)]
+ public List Scenes { get; set; }
+
+ [JsonProperty("currentLightingScene", NullValueHandling = NullValueHandling.Ignore)]
+ public LightingScene CurrentLightingScene { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/MessengerBase.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/MessengerBase.cs
new file mode 100644
index 00000000..e73a4f0a
--- /dev/null
+++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/MessengerBase.cs
@@ -0,0 +1,303 @@
+using Newtonsoft.Json;
+using Newtonsoft.Json.Converters;
+using Newtonsoft.Json.Linq;
+using PepperDash.Core;
+using PepperDash.Essentials.Core;
+using PepperDash.Essentials.Core.DeviceTypeInterfaces;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace PepperDash.Essentials.AppServer.Messengers
+{
+ ///
+ /// Provides a messaging bridge
+ ///
+#if SERIES4
+ public abstract class MessengerBase : EssentialsDevice, IMobileControlMessenger
+#else
+ public abstract class MessengerBase: EssentialsDevice
+#endif
+ {
+ protected IKeyName _device;
+
+ private readonly List _deviceInterfaces;
+
+ private readonly Dictionary> _actions = new Dictionary>();
+
+ public string DeviceKey => _device?.Key ?? "";
+
+ ///
+ ///
+ ///
+#if SERIES4
+ public IMobileControl AppServerController { get; private set; }
+#else
+ public MobileControlSystemController AppServerController { get; private set; }
+#endif
+
+ public string MessagePath { get; private set; }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ protected MessengerBase(string key, string messagePath)
+ : base(key)
+ {
+ Key = key;
+
+ if (string.IsNullOrEmpty(messagePath))
+ throw new ArgumentException("messagePath must not be empty or null");
+
+ MessagePath = messagePath;
+ }
+
+ protected MessengerBase(string key, string messagePath, IKeyName device)
+ : this(key, messagePath)
+ {
+ _device = device;
+
+ _deviceInterfaces = GetInterfaces(_device as Device);
+ }
+
+ ///
+ /// Gets the interfaces implmented on the device
+ ///
+ ///
+ ///
+ private List GetInterfaces(Device device)
+ {
+ return device?.GetType().GetInterfaces().Select((i) => i.Name).ToList() ?? new List();
+ }
+
+ ///
+ /// Registers this messenger with appserver controller
+ ///
+ ///
+#if SERIES4
+ public void RegisterWithAppServer(IMobileControl appServerController)
+#else
+ public void RegisterWithAppServer(MobileControlSystemController appServerController)
+#endif
+ {
+ AppServerController = appServerController ?? throw new ArgumentNullException("appServerController");
+
+ AppServerController.AddAction(this, HandleMessage);
+
+ RegisterActions();
+ }
+
+ private void HandleMessage(string path, string id, JToken content)
+ {
+ // replace base path with empty string. Should leave something like /fullStatus
+ var route = path.Replace(MessagePath, string.Empty);
+
+ if(!_actions.TryGetValue(route, out var action)) {
+ return;
+ }
+
+ Debug.LogMessage(Serilog.Events.LogEventLevel.Debug, "Executing action for path {path}", this, path);
+
+ action(id, content);
+ }
+
+ protected void AddAction(string path, Action action)
+ {
+ if (_actions.ContainsKey(path))
+ {
+ //Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, $"Messenger {Key} already has action registered at {path}", this);
+ return;
+ }
+
+ _actions.Add(path, action);
+ }
+
+ public List GetActionPaths()
+ {
+ return _actions.Keys.ToList();
+ }
+
+ protected void RemoveAction(string path)
+ {
+ if (!_actions.ContainsKey(path))
+ {
+ return;
+ }
+
+ _actions.Remove(path);
+ }
+
+ ///
+ /// Implemented in extending classes. Wire up API calls and feedback here
+ ///
+ ///
+#if SERIES4
+ protected virtual void RegisterActions()
+#else
+ protected virtual void CustomRegisterWithAppServer(MobileControlSystemController appServerController)
+#endif
+ {
+
+ }
+
+ ///
+ /// Helper for posting status message
+ ///
+ ///
+ ///
+ protected void PostStatusMessage(DeviceStateMessageBase message, string clientId = null)
+ {
+ try
+ {
+ if(message == null)
+ {
+ throw new ArgumentNullException("message");
+ }
+
+ if(_device == null)
+ {
+ throw new ArgumentNullException("device");
+ }
+
+ message.SetInterfaces(_deviceInterfaces);
+
+ message.Key = _device.Key;
+
+ message.Name = _device.Name;
+
+ PostStatusMessage(JToken.FromObject(message), MessagePath, clientId);
+ }
+ catch (Exception ex) {
+ Debug.LogMessage(ex, "Exception posting status message", this);
+ }
+ }
+
+#if SERIES4
+ protected void PostStatusMessage(string type, DeviceStateMessageBase deviceState, string clientId = null)
+ {
+ try
+ {
+ //Debug.Console(2, this, "*********************Setting DeviceStateMessageProperties on MobileControlResponseMessage");
+ deviceState.SetInterfaces(_deviceInterfaces);
+
+ deviceState.Key = _device.Key;
+
+ deviceState.Name = _device.Name;
+
+ deviceState.MessageBasePath = MessagePath;
+
+ PostStatusMessage(JToken.FromObject(deviceState), type, clientId);
+ }
+ catch (Exception ex)
+ {
+ Debug.LogMessage(ex, "Exception posting status message", this);
+ }
+ }
+#endif
+ protected void PostStatusMessage(JToken content, string type = "", string clientId = null)
+ {
+ try
+ {
+ AppServerController?.SendMessageObject(new MobileControlMessage { Type = !string.IsNullOrEmpty(type) ? type : MessagePath, ClientId = clientId, Content = content });
+ }
+ catch (Exception ex)
+ {
+ Debug.LogMessage(ex, "Exception posting status message", this);
+ }
+ }
+
+ protected void PostEventMessage(DeviceEventMessageBase message)
+ {
+ message.Key = _device.Key;
+
+ message.Name = _device.Name;
+
+ AppServerController?.SendMessageObject(new MobileControlMessage
+ {
+ Type = $"/event{MessagePath}/{message.EventType}",
+ Content = JToken.FromObject(message),
+ });
+ }
+
+ protected void PostEventMessage(DeviceEventMessageBase message, string eventType)
+ {
+ message.Key = _device.Key;
+
+ message.Name = _device.Name;
+
+ message.EventType = eventType;
+
+ AppServerController?.SendMessageObject(new MobileControlMessage
+ {
+ Type = $"/event{MessagePath}/{eventType}",
+ Content = JToken.FromObject(message),
+ });
+ }
+
+ protected void PostEventMessage(string eventType)
+ {
+ AppServerController?.SendMessageObject(new MobileControlMessage
+ {
+ Type = $"/event{MessagePath}/{eventType}",
+ Content = JToken.FromObject(new { }),
+ });
+ }
+
+ }
+
+ public abstract class DeviceMessageBase
+ {
+ ///
+ /// The device key
+ ///
+ [JsonProperty("key")]
+ public string Key { get; set; }
+
+ ///
+ /// The device name
+ ///
+ [JsonProperty("name")]
+ public string Name { get; set; }
+
+ ///
+ /// The type of the message class
+ ///
+ [JsonProperty("messageType")]
+ public string MessageType => GetType().Name;
+
+ [JsonProperty("messageBasePath")]
+ public string MessageBasePath { get; set; }
+ }
+
+ ///
+ /// Base class for state messages that includes the type of message and the implmented interfaces
+ ///
+ public class DeviceStateMessageBase : DeviceMessageBase
+ {
+ ///
+ /// The interfaces implmented by the device sending the messsage
+ ///
+ [JsonProperty("interfaces")]
+ public List Interfaces { get; private set; }
+
+ public void SetInterfaces(List interfaces)
+ {
+ Interfaces = interfaces;
+ }
+ }
+
+ ///
+ /// Base class for event messages that include the type of message and an event type
+ ///
+ public abstract class DeviceEventMessageBase : DeviceMessageBase
+ {
+ ///
+ /// The event type
+ ///
+ [JsonProperty("eventType")]
+ public string EventType { get; set; }
+ }
+
+}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/PressAndHoldHandler.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/PressAndHoldHandler.cs
new file mode 100644
index 00000000..2bf213ac
--- /dev/null
+++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/PressAndHoldHandler.cs
@@ -0,0 +1,116 @@
+using Crestron.SimplSharp;
+using Newtonsoft.Json.Linq;
+using PepperDash.Core;
+using System;
+using System.Collections.Generic;
+using System.Runtime.CompilerServices;
+
+namespace PepperDash.Essentials.AppServer.Messengers
+{
+ public static class PressAndHoldHandler
+ {
+ private const long ButtonHeartbeatInterval = 1000;
+
+ private static readonly Dictionary _pushedActions = new Dictionary();
+
+ private static readonly Dictionary>> _pushedActionHandlers;
+
+ static PressAndHoldHandler()
+ {
+ _pushedActionHandlers = new Dictionary>>
+ {
+ {"pressed", AddTimer },
+ {"held", ResetTimer },
+ {"released", StopTimer }
+ };
+ }
+
+ private static void AddTimer(string deviceKey, Action action)
+ {
+ Debug.LogMessage(Serilog.Events.LogEventLevel.Debug, "Attempting to add timer for {deviceKey}", deviceKey);
+
+ if (_pushedActions.TryGetValue(deviceKey, out CTimer cancelTimer))
+ {
+ Debug.LogMessage(Serilog.Events.LogEventLevel.Debug, "Timer for {deviceKey} already exists", deviceKey);
+ return;
+ }
+
+ Debug.LogMessage(Serilog.Events.LogEventLevel.Debug, "Adding timer for {deviceKey} with due time {dueTime}", deviceKey, ButtonHeartbeatInterval);
+
+ action(true);
+
+ cancelTimer = new CTimer(o =>
+ {
+ Debug.LogMessage(Serilog.Events.LogEventLevel.Debug, "Timer expired for {deviceKey}", deviceKey);
+
+ action(false);
+
+ _pushedActions.Remove(deviceKey);
+ }, ButtonHeartbeatInterval);
+
+ _pushedActions.Add(deviceKey, cancelTimer);
+ }
+
+ private static void ResetTimer(string deviceKey, Action action)
+ {
+ Debug.LogMessage(Serilog.Events.LogEventLevel.Debug, "Attempting to reset timer for {deviceKey}", deviceKey);
+
+ if (!_pushedActions.TryGetValue(deviceKey, out CTimer cancelTimer))
+ {
+ Debug.LogMessage(Serilog.Events.LogEventLevel.Debug, "Timer for {deviceKey} not found", deviceKey);
+ return;
+ }
+
+ Debug.LogMessage(Serilog.Events.LogEventLevel.Debug, "Resetting timer for {deviceKey} with due time {dueTime}", deviceKey, ButtonHeartbeatInterval);
+
+ cancelTimer.Reset(ButtonHeartbeatInterval);
+ }
+
+ private static void StopTimer(string deviceKey, Action action)
+ {
+ Debug.LogMessage(Serilog.Events.LogEventLevel.Debug, "Attempting to stop timer for {deviceKey}", deviceKey);
+
+ if (!_pushedActions.TryGetValue(deviceKey, out CTimer cancelTimer)) {
+ Debug.LogMessage(Serilog.Events.LogEventLevel.Debug, "Timer for {deviceKey} not found", deviceKey);
+ return;
+ }
+
+ Debug.LogMessage(Serilog.Events.LogEventLevel.Debug, "Stopping timer for {deviceKey} with due time {dueTime}", deviceKey, ButtonHeartbeatInterval);
+
+ action(false);
+ cancelTimer.Stop();
+ _pushedActions.Remove(deviceKey);
+ }
+
+ public static Action> GetPressAndHoldHandler(string value)
+ {
+ Debug.LogMessage(Serilog.Events.LogEventLevel.Debug, "Getting press and hold handler for {value}", value);
+
+ if (!_pushedActionHandlers.TryGetValue(value, out Action> handler))
+ {
+ Debug.LogMessage(Serilog.Events.LogEventLevel.Debug, "Press and hold handler for {value} not found", value);
+ return null;
+ }
+
+ Debug.LogMessage(Serilog.Events.LogEventLevel.Debug, "Got handler for {value}", value);
+
+ return handler;
+ }
+
+ public static void HandlePressAndHold(string deviceKey, JToken content, Action action)
+ {
+ var msg = content.ToObject>();
+
+ Debug.LogMessage(Serilog.Events.LogEventLevel.Debug, "Handling press and hold message of {type} for {deviceKey}", msg.Value, deviceKey);
+
+ var timerHandler = GetPressAndHoldHandler(msg.Value);
+
+ if (timerHandler == null)
+ {
+ return;
+ }
+
+ timerHandler(deviceKey, action);
+ }
+ }
+}
diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/RoomEventScheduleMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/RoomEventScheduleMessenger.cs
new file mode 100644
index 00000000..a32eb5a6
--- /dev/null
+++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/RoomEventScheduleMessenger.cs
@@ -0,0 +1,80 @@
+using Newtonsoft.Json;
+using PepperDash.Core;
+using PepperDash.Essentials.Core;
+using PepperDash.Essentials.Core.DeviceTypeInterfaces;
+using PepperDash.Essentials.Room.Config;
+using System;
+using System.Collections.Generic;
+
+namespace PepperDash.Essentials.AppServer.Messengers
+{
+ public class RoomEventScheduleMessenger : MessengerBase
+ {
+ private readonly IRoomEventSchedule _room;
+
+
+ public RoomEventScheduleMessenger(string key, string messagePath, IRoomEventSchedule room)
+ : base(key, messagePath, room as Device)
+ {
+ _room = room;
+ }
+
+ #region Overrides of MessengerBase
+
+#if SERIES4
+ protected override void RegisterActions()
+#else
+ protected override void CustomRegisterWithAppServer(MobileControlSystemController appServerController)
+#endif
+ {
+ AddAction("/saveScheduledEvents", (id, content) => SaveScheduledEvents(content.ToObject>()));
+ AddAction("/status", (id, content) =>
+ {
+ var events = _room.GetScheduledEvents();
+
+ SendFullStatus(events);
+ });
+
+ _room.ScheduledEventsChanged += (sender, args) => SendFullStatus(args.ScheduledEvents);
+ }
+
+ #endregion
+
+ private void SaveScheduledEvents(List events)
+ {
+ foreach (var evt in events)
+ {
+ SaveScheduledEvent(evt);
+ }
+ }
+
+ private void SaveScheduledEvent(ScheduledEventConfig eventConfig)
+ {
+ try
+ {
+ _room.AddOrUpdateScheduledEvent(eventConfig);
+ }
+ catch (Exception ex)
+ {
+ Debug.Console(0, this, "Exception saving event: {0}\r\n{1}", ex.Message, ex.StackTrace);
+ }
+ }
+
+ private void SendFullStatus(List events)
+ {
+
+ var message = new RoomEventScheduleStateMessage
+ {
+ ScheduleEvents = events,
+ };
+
+ PostStatusMessage(message);
+ }
+ }
+
+ public class RoomEventScheduleStateMessage : DeviceStateMessageBase
+ {
+ [JsonProperty("scheduleEvents")]
+ public List ScheduleEvents { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/SIMPLAtcMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/SIMPLAtcMessenger.cs
new file mode 100644
index 00000000..3f2ab694
--- /dev/null
+++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/SIMPLAtcMessenger.cs
@@ -0,0 +1,163 @@
+using Crestron.SimplSharpPro.DeviceSupport;
+using Newtonsoft.Json.Linq;
+using PepperDash.Essentials.Core;
+using PepperDash.Essentials.Core.DeviceTypeInterfaces;
+using PepperDash.Essentials.Devices.Common.Codec;
+using System;
+using System.Collections.Generic;
+
+namespace PepperDash.Essentials.AppServer.Messengers
+{
+ // ReSharper disable once InconsistentNaming
+ public class SIMPLAtcMessenger : MessengerBase
+ {
+ private readonly BasicTriList _eisc;
+
+ public SIMPLAtcJoinMap JoinMap { get; private set; }
+
+
+ ///
+ ///
+ ///
+ private readonly CodecActiveCallItem _currentCallItem;
+
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public SIMPLAtcMessenger(string key, BasicTriList eisc, string messagePath)
+ : base(key, messagePath)
+ {
+ _eisc = eisc;
+
+ JoinMap = new SIMPLAtcJoinMap(201);
+
+ _currentCallItem = new CodecActiveCallItem { Type = eCodecCallType.Audio, Id = "-audio-" };
+ }
+
+ ///
+ ///
+ ///
+ private void SendFullStatus()
+ {
+ PostStatusMessage(JToken.FromObject(new
+ {
+ calls = GetCurrentCallList(),
+ currentCallString = _eisc.GetString(JoinMap.CurrentCallName.JoinNumber),
+ currentDialString = _eisc.GetString(JoinMap.CurrentDialString.JoinNumber),
+ isInCall = _eisc.GetString(JoinMap.HookState.JoinNumber) == "Connected"
+ })
+ );
+ }
+
+ ///
+ ///
+ ///
+ ///
+#if SERIES4
+ protected override void RegisterActions()
+#else
+ protected override void CustomRegisterWithAppServer(MobileControlSystemController appServerController)
+#endif
+ {
+ //EISC.SetStringSigAction(SCurrentDialString, s => PostStatusMessage(new { currentDialString = s }));
+
+ _eisc.SetStringSigAction(JoinMap.HookState.JoinNumber, s =>
+ {
+ _currentCallItem.Status = (eCodecCallStatus)Enum.Parse(typeof(eCodecCallStatus), s, true);
+ //GetCurrentCallList();
+ SendFullStatus();
+ });
+
+ _eisc.SetStringSigAction(JoinMap.CurrentCallNumber.JoinNumber, s =>
+ {
+ _currentCallItem.Number = s;
+ SendCallsList();
+ });
+
+ _eisc.SetStringSigAction(JoinMap.CurrentCallName.JoinNumber, s =>
+ {
+ _currentCallItem.Name = s;
+ SendCallsList();
+ });
+
+ _eisc.SetStringSigAction(JoinMap.CallDirection.JoinNumber, s =>
+ {
+ _currentCallItem.Direction = (eCodecCallDirection)Enum.Parse(typeof(eCodecCallDirection), s, true);
+ SendCallsList();
+ });
+
+ // Add press and holds using helper
+ //Action addPhAction = (s, u) =>
+ // AppServerController.AddAction(MessagePath + s, new PressAndHoldAction(b => _eisc.SetBool(u, b)));
+
+ // Add straight pulse calls
+ void addAction(string s, uint u) =>
+ AddAction(s, (id, content) => _eisc.PulseBool(u, 100));
+ addAction("/endCallById", JoinMap.EndCall.JoinNumber);
+ addAction("/endAllCalls", JoinMap.EndCall.JoinNumber);
+ addAction("/acceptById", JoinMap.IncomingAnswer.JoinNumber);
+ addAction("/rejectById", JoinMap.IncomingReject.JoinNumber);
+
+ var speeddialStart = JoinMap.SpeedDialStart.JoinNumber;
+ var speeddialEnd = JoinMap.SpeedDialStart.JoinNumber + JoinMap.SpeedDialStart.JoinSpan;
+
+ var speedDialIndex = 1;
+ for (uint i = speeddialStart; i < speeddialEnd; i++)
+ {
+ addAction(string.Format("/speedDial{0}", speedDialIndex), i);
+ speedDialIndex++;
+ }
+
+ // Get status
+ AddAction("/fullStatus", (id, content) => SendFullStatus());
+ // Dial on string
+ AddAction("/dial",
+ (id, content) =>
+ {
+ var msg = content.ToObject>();
+ _eisc.SetString(JoinMap.CurrentDialString.JoinNumber, msg.Value);
+ });
+ // Pulse DTMF
+ AddAction("/dtmf", (id, content) =>
+ {
+ var s = content.ToObject>();
+
+ var join = JoinMap.Joins[s.Value];
+ if (join != null)
+ {
+ if (join.JoinNumber > 0)
+ {
+ _eisc.PulseBool(join.JoinNumber, 100);
+ }
+ }
+ });
+ }
+
+ ///
+ ///
+ ///
+ private void SendCallsList()
+ {
+ PostStatusMessage(JToken.FromObject(new
+ {
+ calls = GetCurrentCallList(),
+ })
+ );
+ }
+
+ ///
+ /// Turns the
+ ///
+ ///
+ private List GetCurrentCallList()
+ {
+ return _currentCallItem.Status == eCodecCallStatus.Disconnected
+ ? new List()
+ : new List { _currentCallItem };
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/SIMPLCameraMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/SIMPLCameraMessenger.cs
new file mode 100644
index 00000000..42111467
--- /dev/null
+++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/SIMPLCameraMessenger.cs
@@ -0,0 +1,169 @@
+using Crestron.SimplSharpPro.DeviceSupport;
+using Newtonsoft.Json.Linq;
+using PepperDash.Essentials.Core;
+using PepperDash.Essentials.Core.Bridges;
+using PepperDash.Essentials.Core.DeviceTypeInterfaces;
+using PepperDash.Essentials.Devices.Common.Cameras;
+using System;
+using System.Collections.Generic;
+
+namespace PepperDash.Essentials.AppServer.Messengers
+{
+ // ReSharper disable once InconsistentNaming
+ public class SIMPLCameraMessenger : MessengerBase
+ {
+ private readonly BasicTriList _eisc;
+
+ private readonly CameraControllerJoinMap _joinMap;
+
+
+ public SIMPLCameraMessenger(string key, BasicTriList eisc, string messagePath, uint joinStart)
+ : base(key, messagePath)
+ {
+ _eisc = eisc;
+
+ _joinMap = new CameraControllerJoinMap(joinStart);
+
+ _eisc.SetUShortSigAction(_joinMap.NumberOfPresets.JoinNumber, u => SendCameraFullMessageObject());
+
+ _eisc.SetBoolSigAction(_joinMap.CameraModeAuto.JoinNumber, b => PostCameraMode());
+ _eisc.SetBoolSigAction(_joinMap.CameraModeManual.JoinNumber, b => PostCameraMode());
+ _eisc.SetBoolSigAction(_joinMap.CameraModeOff.JoinNumber, b => PostCameraMode());
+ }
+
+
+#if SERIES4
+ protected override void RegisterActions()
+#else
+ protected override void CustomRegisterWithAppServer(MobileControlSystemController appServerController)
+#endif
+ {
+ AddAction("/fullStatus", (id, content) => SendCameraFullMessageObject());
+
+ // Add press and holds using helper action
+ void addPhAction(string s, uint u) =>
+ AddAction(s, (id, content) => HandleCameraPressAndHold(content, b => _eisc.SetBool(u, b)));
+ addPhAction("/cameraUp", _joinMap.TiltUp.JoinNumber);
+ addPhAction("/cameraDown", _joinMap.TiltDown.JoinNumber);
+ addPhAction("/cameraLeft", _joinMap.PanLeft.JoinNumber);
+ addPhAction("/cameraRight", _joinMap.PanRight.JoinNumber);
+ addPhAction("/cameraZoomIn", _joinMap.ZoomIn.JoinNumber);
+ addPhAction("/cameraZoomOut", _joinMap.ZoomOut.JoinNumber);
+
+ void addAction(string s, uint u) =>
+ AddAction(s, (id, content) => _eisc.PulseBool(u, 100));
+
+ addAction("/cameraModeAuto", _joinMap.CameraModeAuto.JoinNumber);
+ addAction("/cameraModeManual", _joinMap.CameraModeManual.JoinNumber);
+ addAction("/cameraModeOff", _joinMap.CameraModeOff.JoinNumber);
+
+ var presetStart = _joinMap.PresetRecallStart.JoinNumber;
+ var presetEnd = _joinMap.PresetRecallStart.JoinNumber + _joinMap.PresetRecallStart.JoinSpan;
+
+ int presetId = 1;
+ // camera presets
+ for (uint i = presetStart; i <= presetEnd; i++)
+ {
+ addAction("/cameraPreset" + (presetId), i);
+ presetId++;
+ }
+ }
+
+ private void HandleCameraPressAndHold(JToken content, Action cameraAction)
+ {
+ var state = content.ToObject>();
+
+ var timerHandler = PressAndHoldHandler.GetPressAndHoldHandler(state.Value);
+ if (timerHandler == null)
+ {
+ return;
+ }
+
+ timerHandler(state.Value, cameraAction);
+
+ cameraAction(state.Value.Equals("true", StringComparison.InvariantCultureIgnoreCase));
+ }
+
+#if SERIES4
+ public void CustomUnregsiterWithAppServer(IMobileControl appServerController)
+#else
+ public void CustomUnregsiterWithAppServer(MobileControlSystemController appServerController)
+#endif
+ {
+ appServerController.RemoveAction(MessagePath + "/fullStatus");
+
+ appServerController.RemoveAction(MessagePath + "/cameraUp");
+ appServerController.RemoveAction(MessagePath + "/cameraDown");
+ appServerController.RemoveAction(MessagePath + "/cameraLeft");
+ appServerController.RemoveAction(MessagePath + "/cameraRight");
+ appServerController.RemoveAction(MessagePath + "/cameraZoomIn");
+ appServerController.RemoveAction(MessagePath + "/cameraZoomOut");
+ appServerController.RemoveAction(MessagePath + "/cameraModeAuto");
+ appServerController.RemoveAction(MessagePath + "/cameraModeManual");
+ appServerController.RemoveAction(MessagePath + "/cameraModeOff");
+
+ _eisc.SetUShortSigAction(_joinMap.NumberOfPresets.JoinNumber, null);
+
+ _eisc.SetBoolSigAction(_joinMap.CameraModeAuto.JoinNumber, null);
+ _eisc.SetBoolSigAction(_joinMap.CameraModeManual.JoinNumber, null);
+ _eisc.SetBoolSigAction(_joinMap.CameraModeOff.JoinNumber, null);
+ }
+
+ ///
+ /// Helper method to update the full status of the camera
+ ///
+ private void SendCameraFullMessageObject()
+ {
+ var presetList = new List();
+
+ // Build a list of camera presets based on the names and count
+ if (_eisc.GetBool(_joinMap.SupportsPresets.JoinNumber))
+ {
+ var presetStart = _joinMap.PresetLabelStart.JoinNumber;
+ var presetEnd = _joinMap.PresetLabelStart.JoinNumber + _joinMap.NumberOfPresets.JoinNumber;
+
+ var presetId = 1;
+ for (uint i = presetStart; i < presetEnd; i++)
+ {
+ var presetName = _eisc.GetString(i);
+ var preset = new CameraPreset(presetId, presetName, string.IsNullOrEmpty(presetName), true);
+ presetList.Add(preset);
+ presetId++;
+ }
+ }
+
+ PostStatusMessage(JToken.FromObject(new
+ {
+ cameraMode = GetCameraMode(),
+ hasPresets = _eisc.GetBool(_joinMap.SupportsPresets.JoinNumber),
+ presets = presetList
+ })
+ );
+ }
+
+ ///
+ ///
+ ///
+ private void PostCameraMode()
+ {
+ PostStatusMessage(JToken.FromObject(new
+ {
+ cameraMode = GetCameraMode()
+ }));
+ }
+
+ ///
+ /// Computes the current camera mode
+ ///
+ ///
+ private string GetCameraMode()
+ {
+ string m;
+ if (_eisc.GetBool(_joinMap.CameraModeAuto.JoinNumber)) m = eCameraControlMode.Auto.ToString().ToLower();
+ else if (_eisc.GetBool(_joinMap.CameraModeManual.JoinNumber))
+ m = eCameraControlMode.Manual.ToString().ToLower();
+ else m = eCameraControlMode.Off.ToString().ToLower();
+ return m;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/SIMPLDirectRouteMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/SIMPLDirectRouteMessenger.cs
new file mode 100644
index 00000000..46899cd1
--- /dev/null
+++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/SIMPLDirectRouteMessenger.cs
@@ -0,0 +1,132 @@
+using Crestron.SimplSharpPro.DeviceSupport;
+using Newtonsoft.Json.Linq;
+using PepperDash.Core;
+using PepperDash.Essentials.Core;
+using PepperDash.Essentials.Core.DeviceTypeInterfaces;
+using System.Collections.Generic;
+
+namespace PepperDash.Essentials.AppServer.Messengers
+{
+ public class SimplDirectRouteMessenger : MessengerBase
+ {
+ private readonly BasicTriList _eisc;
+
+ public MobileControlSIMPLRunDirectRouteActionJoinMap JoinMap { get; private set; }
+
+ public Dictionary DestinationList { get; set; }
+
+ public SimplDirectRouteMessenger(string key, BasicTriList eisc, string messagePath) : base(key, messagePath)
+ {
+ _eisc = eisc;
+
+ JoinMap = new MobileControlSIMPLRunDirectRouteActionJoinMap(851);
+
+ DestinationList = new Dictionary();
+ }
+
+ #region Overrides of MessengerBase
+
+#if SERIES4
+ protected override void RegisterActions()
+#else
+ protected override void CustomRegisterWithAppServer(MobileControlSystemController controller)
+#endif
+ {
+ Debug.Console(2, "********** Direct Route Messenger CustomRegisterWithAppServer **********");
+
+
+ //Audio source
+ _eisc.SetStringSigAction(JoinMap.SourceForDestinationAudio.JoinNumber,
+ s => PostStatusMessage(JToken.FromObject(new
+ {
+ selectedSourceKey = s,
+ })
+ ));
+
+ AddAction("/programAudio/selectSource", (id, content) =>
+ {
+ var msg = content.ToObject>();
+
+ _eisc.StringInput[JoinMap.SourceForDestinationAudio.JoinNumber].StringValue = msg.Value;
+ });
+
+ AddAction("/fullStatus", (id, content) =>
+ {
+ foreach (var dest in DestinationList)
+ {
+ var key = dest.Key;
+ var item = dest.Value;
+
+ var source =
+ _eisc.StringOutput[(uint)(JoinMap.SourceForDestinationJoinStart.JoinNumber + item.Order)].StringValue;
+
+ UpdateSourceForDestination(source, key);
+ }
+
+ PostStatusMessage(JToken.FromObject(new
+ {
+ selectedSourceKey = _eisc.StringOutput[JoinMap.SourceForDestinationAudio.JoinNumber].StringValue
+ })
+ );
+
+ PostStatusMessage(JToken.FromObject(new
+ {
+ advancedSharingActive = _eisc.BooleanOutput[JoinMap.AdvancedSharingModeFb.JoinNumber].BoolValue
+ })
+ );
+ });
+
+ AddAction("/advancedSharingMode", (id, content) =>
+ {
+ var b = content.ToObject>();
+
+ Debug.Console(1, "Current Sharing Mode: {2}\r\nadvanced sharing mode: {0} join number: {1}", b.Value,
+ JoinMap.AdvancedSharingModeOn.JoinNumber,
+ _eisc.BooleanOutput[JoinMap.AdvancedSharingModeOn.JoinNumber].BoolValue);
+
+ _eisc.SetBool(JoinMap.AdvancedSharingModeOn.JoinNumber, b.Value);
+ _eisc.SetBool(JoinMap.AdvancedSharingModeOff.JoinNumber, !b.Value);
+ _eisc.PulseBool(JoinMap.AdvancedSharingModeToggle.JoinNumber);
+ });
+
+ _eisc.SetBoolSigAction(JoinMap.AdvancedSharingModeFb.JoinNumber,
+ (b) => PostStatusMessage(JToken.FromObject(new
+ {
+ advancedSharingActive = b
+ })
+ ));
+ }
+
+ public void RegisterForDestinationPaths()
+ {
+ //handle routing feedback from SIMPL
+ foreach (var destination in DestinationList)
+ {
+ var key = destination.Key;
+ var dest = destination.Value;
+
+ _eisc.SetStringSigAction((uint)(JoinMap.SourceForDestinationJoinStart.JoinNumber + dest.Order),
+ s => UpdateSourceForDestination(s, key));
+
+ AddAction($"/{key}/selectSource", (id, content) =>
+ {
+ var s = content.ToObject>();
+
+ _eisc.StringInput[(uint)(JoinMap.SourceForDestinationJoinStart.JoinNumber + dest.Order)].StringValue = s.Value;
+ });
+ }
+ }
+
+ #endregion
+
+ private void UpdateSourceForDestination(string sourceKey, string destKey)
+ {
+ PostStatusMessage(JToken.FromObject(new
+ {
+ selectedSourceKey = sourceKey
+ }), $"{MessagePath}/{destKey}/currentSource");
+ }
+ }
+
+
+}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/SIMPLRouteMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/SIMPLRouteMessenger.cs
new file mode 100644
index 00000000..ccdbb279
--- /dev/null
+++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/SIMPLRouteMessenger.cs
@@ -0,0 +1,77 @@
+using Crestron.SimplSharpPro.DeviceSupport;
+using Newtonsoft.Json.Linq;
+using PepperDash.Essentials.Core;
+using PepperDash.Essentials.Core.DeviceTypeInterfaces;
+
+
+namespace PepperDash.Essentials.AppServer.Messengers
+{
+
+ public class SIMPLRouteMessenger : MessengerBase
+ {
+ private readonly BasicTriList _eisc;
+
+ private readonly uint _joinStart;
+
+ public class StringJoin
+ {
+ ///
+ /// 1
+ ///
+ public const uint CurrentSource = 1;
+ }
+
+ public SIMPLRouteMessenger(string key, BasicTriList eisc, string messagePath, uint joinStart)
+ : base(key, messagePath)
+ {
+ _eisc = eisc;
+ _joinStart = joinStart - 1;
+
+ _eisc.SetStringSigAction(_joinStart + StringJoin.CurrentSource, SendRoutingFullMessageObject);
+ }
+
+#if SERIES4
+ protected override void RegisterActions()
+#else
+ protected override void CustomRegisterWithAppServer(MobileControlSystemController appServerController)
+#endif
+ {
+ AddAction("/fullStatus",
+ (id, content) => SendRoutingFullMessageObject(_eisc.GetString(_joinStart + StringJoin.CurrentSource)));
+
+ AddAction("/source", (id, content) =>
+ {
+ var c = content.ToObject();
+
+ _eisc.SetString(_joinStart + StringJoin.CurrentSource, c.SourceListItemKey);
+ });
+ }
+
+#if SERIES4
+ public void CustomUnregsiterWithAppServer(IMobileControl appServerController)
+#else
+ public void CustomUnregsiterWithAppServer(MobileControlSystemController appServerController)
+#endif
+ {
+ appServerController.RemoveAction(MessagePath + "/fullStatus");
+ appServerController.RemoveAction(MessagePath + "/source");
+
+ _eisc.SetStringSigAction(_joinStart + StringJoin.CurrentSource, null);
+ }
+
+ ///
+ /// Helper method to update full status of the routing device
+ ///
+ private void SendRoutingFullMessageObject(string sourceKey)
+ {
+ if (string.IsNullOrEmpty(sourceKey))
+ sourceKey = "none";
+
+ PostStatusMessage(JToken.FromObject(new
+ {
+ selectedSourceKey = sourceKey
+ })
+ );
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/SIMPLVtcMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/SIMPLVtcMessenger.cs
new file mode 100644
index 00000000..a421e068
--- /dev/null
+++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/SIMPLVtcMessenger.cs
@@ -0,0 +1,480 @@
+using Crestron.SimplSharpPro.DeviceSupport;
+using Newtonsoft.Json.Linq;
+using PepperDash.Core;
+using PepperDash.Essentials.Core;
+using PepperDash.Essentials.Core.DeviceTypeInterfaces;
+using PepperDash.Essentials.Devices.Common.Cameras;
+using PepperDash.Essentials.Devices.Common.Codec;
+using System;
+using System.Collections.Generic;
+
+namespace PepperDash.Essentials.AppServer.Messengers
+{
+ // ReSharper disable once InconsistentNaming
+ public class SIMPLVtcMessenger : MessengerBase
+ {
+ private readonly BasicTriList _eisc;
+
+ public SIMPLVtcJoinMap JoinMap { get; private set; }
+
+ private readonly CodecActiveCallItem _currentCallItem;
+
+ private CodecActiveCallItem _incomingCallItem;
+
+ private ushort _previousDirectoryLength = 701;
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public SIMPLVtcMessenger(string key, BasicTriList eisc, string messagePath)
+ : base(key, messagePath)
+ {
+ _eisc = eisc;
+
+ JoinMap = new SIMPLVtcJoinMap(1001);
+
+ _currentCallItem = new CodecActiveCallItem { Type = eCodecCallType.Video, Id = "-video-" };
+ }
+
+ ///
+ ///
+ ///
+ ///
+#if SERIES4
+ protected override void RegisterActions()
+#else
+ protected override void CustomRegisterWithAppServer(MobileControlSystemController appServerController)
+#endif
+ {
+ _eisc.SetStringSigAction(JoinMap.HookState.JoinNumber, s =>
+ {
+ _currentCallItem.Status = (eCodecCallStatus)Enum.Parse(typeof(eCodecCallStatus), s, true);
+ PostFullStatus(); // SendCallsList();
+ });
+
+ _eisc.SetStringSigAction(JoinMap.CurrentCallNumber.JoinNumber, s =>
+ {
+ _currentCallItem.Number = s;
+ PostCallsList();
+ });
+
+ _eisc.SetStringSigAction(JoinMap.CurrentCallName.JoinNumber, s =>
+ {
+ _currentCallItem.Name = s;
+ PostCallsList();
+ });
+
+ _eisc.SetStringSigAction(JoinMap.CallDirection.JoinNumber, s =>
+ {
+ _currentCallItem.Direction = (eCodecCallDirection)Enum.Parse(typeof(eCodecCallDirection), s, true);
+ PostCallsList();
+ });
+
+ _eisc.SetBoolSigAction(JoinMap.IncomingCall.JoinNumber, b =>
+ {
+ if (b)
+ {
+ var ica = new CodecActiveCallItem
+ {
+ Direction = eCodecCallDirection.Incoming,
+ Id = "-video-incoming",
+ Name = _eisc.GetString(JoinMap.IncomingCallName.JoinNumber),
+ Number = _eisc.GetString(JoinMap.IncomingCallNumber.JoinNumber),
+ Status = eCodecCallStatus.Ringing,
+ Type = eCodecCallType.Video
+ };
+ _incomingCallItem = ica;
+ }
+ else
+ {
+ _incomingCallItem = null;
+ }
+ PostCallsList();
+ });
+
+ _eisc.SetStringSigAction(JoinMap.IncomingCallName.JoinNumber, s =>
+ {
+ if (_incomingCallItem != null)
+ {
+ _incomingCallItem.Name = s;
+ PostCallsList();
+ }
+ });
+
+ _eisc.SetStringSigAction(JoinMap.IncomingCallNumber.JoinNumber, s =>
+ {
+ if (_incomingCallItem != null)
+ {
+ _incomingCallItem.Number = s;
+ PostCallsList();
+ }
+ });
+
+ _eisc.SetBoolSigAction(JoinMap.CameraSupportsAutoMode.JoinNumber, b => PostStatusMessage(JToken.FromObject(new
+ {
+ cameraSupportsAutoMode = b
+ })));
+ _eisc.SetBoolSigAction(JoinMap.CameraSupportsOffMode.JoinNumber, b => PostStatusMessage(JToken.FromObject(new
+ {
+ cameraSupportsOffMode = b
+ })));
+
+ // Directory insanity
+ _eisc.SetUShortSigAction(JoinMap.DirectoryRowCount.JoinNumber, u =>
+ {
+ // The length of the list comes in before the list does.
+ // Splice the sig change operation onto the last string sig that will be changing
+ // when the directory entries make it through.
+ if (_previousDirectoryLength > 0)
+ {
+ _eisc.ClearStringSigAction(JoinMap.DirectoryEntriesStart.JoinNumber + _previousDirectoryLength - 1);
+ }
+ _eisc.SetStringSigAction(JoinMap.DirectoryEntriesStart.JoinNumber + u - 1, s => PostDirectory());
+ _previousDirectoryLength = u;
+ });
+
+ _eisc.SetStringSigAction(JoinMap.DirectoryEntrySelectedName.JoinNumber, s => PostStatusMessage(JToken.FromObject(new
+ {
+ directoryContactSelected = new
+ {
+ name = _eisc.GetString(JoinMap.DirectoryEntrySelectedName.JoinNumber),
+ }
+ })));
+
+ _eisc.SetStringSigAction(JoinMap.DirectoryEntrySelectedNumber.JoinNumber, s => PostStatusMessage(JToken.FromObject(new
+ {
+ directoryContactSelected = new
+ {
+ number = _eisc.GetString(JoinMap.DirectoryEntrySelectedNumber.JoinNumber),
+ }
+ })));
+
+ _eisc.SetStringSigAction(JoinMap.DirectorySelectedFolderName.JoinNumber, s => PostStatusMessage(JToken.FromObject(new
+ {
+ directorySelectedFolderName = _eisc.GetString(JoinMap.DirectorySelectedFolderName.JoinNumber)
+ })));
+
+ _eisc.SetSigTrueAction(JoinMap.CameraModeAuto.JoinNumber, PostCameraMode);
+ _eisc.SetSigTrueAction(JoinMap.CameraModeManual.JoinNumber, PostCameraMode);
+ _eisc.SetSigTrueAction(JoinMap.CameraModeOff.JoinNumber, PostCameraMode);
+
+ _eisc.SetBoolSigAction(JoinMap.CameraSelfView.JoinNumber, b => PostStatusMessage(JToken.FromObject(new
+ {
+ cameraSelfView = b
+ })));
+
+ _eisc.SetUShortSigAction(JoinMap.CameraNumberSelect.JoinNumber, u => PostSelectedCamera());
+
+
+ // Add press and holds using helper action
+ void addPhAction(string s, uint u) =>
+ AddAction(s, (id, content) => HandleCameraPressAndHold(content, b => _eisc.SetBool(u, b)));
+ addPhAction("/cameraUp", JoinMap.CameraTiltUp.JoinNumber);
+ addPhAction("/cameraDown", JoinMap.CameraTiltDown.JoinNumber);
+ addPhAction("/cameraLeft", JoinMap.CameraPanLeft.JoinNumber);
+ addPhAction("/cameraRight", JoinMap.CameraPanRight.JoinNumber);
+ addPhAction("/cameraZoomIn", JoinMap.CameraZoomIn.JoinNumber);
+ addPhAction("/cameraZoomOut", JoinMap.CameraZoomOut.JoinNumber);
+
+ // Add straight pulse calls using helper action
+ void addAction(string s, uint u) =>
+ AddAction(s, (id, content) => _eisc.PulseBool(u, 100));
+ addAction("/endCallById", JoinMap.EndCall.JoinNumber);
+ addAction("/endAllCalls", JoinMap.EndCall.JoinNumber);
+ addAction("/acceptById", JoinMap.IncomingAnswer.JoinNumber);
+ addAction("/rejectById", JoinMap.IncomingReject.JoinNumber);
+
+ var speeddialStart = JoinMap.SpeedDialStart.JoinNumber;
+ var speeddialEnd = JoinMap.SpeedDialStart.JoinNumber + JoinMap.SpeedDialStart.JoinSpan;
+
+ var speedDialIndex = 1;
+ for (uint i = speeddialStart; i < speeddialEnd; i++)
+ {
+ addAction(string.Format("/speedDial{0}", speedDialIndex), i);
+ speedDialIndex++;
+ }
+
+ addAction("/cameraModeAuto", JoinMap.CameraModeAuto.JoinNumber);
+ addAction("/cameraModeManual", JoinMap.CameraModeManual.JoinNumber);
+ addAction("/cameraModeOff", JoinMap.CameraModeOff.JoinNumber);
+ addAction("/cameraSelfView", JoinMap.CameraSelfView.JoinNumber);
+ addAction("/cameraLayout", JoinMap.CameraLayout.JoinNumber);
+
+ AddAction("/cameraSelect", (id, content) =>
+ {
+ var s = content.ToObject>();
+ SelectCamera(s.Value);
+ });
+
+ // camera presets
+ for (uint i = 0; i < 6; i++)
+ {
+ addAction("/cameraPreset" + (i + 1), JoinMap.CameraPresetStart.JoinNumber + i);
+ }
+
+ AddAction("/isReady", (id, content) => PostIsReady());
+ // Get status
+ AddAction("/fullStatus", (id, content) => PostFullStatus());
+ // Dial on string
+ AddAction("/dial", (id, content) =>
+ {
+ var s = content.ToObject>();
+
+ _eisc.SetString(JoinMap.CurrentDialString.JoinNumber, s.Value);
+ });
+ // Pulse DTMF
+ AddAction("/dtmf", (id, content) =>
+ {
+ var s = content.ToObject>();
+ var join = JoinMap.Joins[s.Value];
+ if (join != null)
+ {
+ if (join.JoinNumber > 0)
+ {
+ _eisc.PulseBool(join.JoinNumber, 100);
+ }
+ }
+ });
+
+ // Directory madness
+ AddAction("/directoryRoot",
+ (id, content) => _eisc.PulseBool(JoinMap.DirectoryRoot.JoinNumber));
+ AddAction("/directoryBack",
+ (id, content) => _eisc.PulseBool(JoinMap.DirectoryFolderBack.JoinNumber));
+ AddAction("/directoryById", (id, content) =>
+ {
+ var s = content.ToObject>();
+ // the id should contain the line number to forward to simpl
+ try
+ {
+ var u = ushort.Parse(s.Value);
+ _eisc.SetUshort(JoinMap.DirectorySelectRow.JoinNumber, u);
+ _eisc.PulseBool(JoinMap.DirectoryLineSelected.JoinNumber);
+ }
+ catch (Exception)
+ {
+ Debug.Console(1, this, Debug.ErrorLogLevel.Warning,
+ "/directoryById request contains non-numeric ID incompatible with SIMPL bridge");
+ }
+ });
+ AddAction("/directorySelectContact", (id, content) =>
+ {
+ var s = content.ToObject>();
+ try
+ {
+ var u = ushort.Parse(s.Value);
+ _eisc.SetUshort(JoinMap.DirectorySelectRow.JoinNumber, u);
+ _eisc.PulseBool(JoinMap.DirectoryLineSelected.JoinNumber);
+ }
+ catch
+ {
+ Debug.Console(2, this, "Error parsing contact from {0} for path /directorySelectContact", s);
+ }
+ });
+ AddAction("/directoryDialContact",
+ (id, content) => _eisc.PulseBool(JoinMap.DirectoryDialSelectedLine.JoinNumber));
+ AddAction("/getDirectory", (id, content) =>
+ {
+ if (_eisc.GetUshort(JoinMap.DirectoryRowCount.JoinNumber) > 0)
+ {
+ PostDirectory();
+ }
+ else
+ {
+ _eisc.PulseBool(JoinMap.DirectoryRoot.JoinNumber);
+ }
+ });
+ }
+
+ private void HandleCameraPressAndHold(JToken content, Action cameraAction)
+ {
+ var state = content.ToObject>();
+
+ var timerHandler = PressAndHoldHandler.GetPressAndHoldHandler(state.Value);
+ if (timerHandler == null)
+ {
+ return;
+ }
+
+ timerHandler(state.Value, cameraAction);
+
+ cameraAction(state.Value.Equals("true", StringComparison.InvariantCultureIgnoreCase));
+ }
+
+ ///
+ ///
+ ///
+ ///
+ private void PostFullStatus()
+ {
+ PostStatusMessage(JToken.FromObject(new
+ {
+ calls = GetCurrentCallList(),
+ cameraMode = GetCameraMode(),
+ cameraSelfView = _eisc.GetBool(JoinMap.CameraSelfView.JoinNumber),
+ cameraSupportsAutoMode = _eisc.GetBool(JoinMap.CameraSupportsAutoMode.JoinNumber),
+ cameraSupportsOffMode = _eisc.GetBool(JoinMap.CameraSupportsOffMode.JoinNumber),
+ currentCallString = _eisc.GetString(JoinMap.CurrentCallNumber.JoinNumber),
+ currentDialString = _eisc.GetString(JoinMap.CurrentDialString.JoinNumber),
+ directoryContactSelected = new
+ {
+ name = _eisc.GetString(JoinMap.DirectoryEntrySelectedName.JoinNumber),
+ number = _eisc.GetString(JoinMap.DirectoryEntrySelectedNumber.JoinNumber)
+ },
+ directorySelectedFolderName = _eisc.GetString(JoinMap.DirectorySelectedFolderName.JoinNumber),
+ isInCall = _eisc.GetString(JoinMap.HookState.JoinNumber) == "Connected",
+ hasDirectory = true,
+ hasDirectorySearch = false,
+ hasRecents = !_eisc.BooleanOutput[502].BoolValue,
+ hasCameras = true,
+ showCamerasWhenNotInCall = _eisc.BooleanOutput[503].BoolValue,
+ selectedCamera = GetSelectedCamera(),
+ }));
+ }
+
+ ///
+ ///
+ ///
+ private void PostDirectory()
+ {
+ var u = _eisc.GetUshort(JoinMap.DirectoryRowCount.JoinNumber);
+ var items = new List