From 9c9eaea9281428b22d7a0145c3c5d35e2ff71f6a Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Tue, 23 Sep 2025 10:55:16 -0500 Subject: [PATCH 01/12] feat: unique status requests for messengers UI Applications can now request status for specific feature sets instead of full status for a device. This will hopefully cut down on the traffic and messages required to get the data for the UI. --- .../Messengers/AudioCodecBaseMessenger.cs | 16 +- .../Messengers/CameraBaseMessenger.cs | 8 +- .../Messengers/CurrentSourcesMessenger.cs | 22 +-- .../Messengers/DeviceInfoMessenger.cs | 19 ++- .../Messengers/DevicePresetsModelMessenger.cs | 28 ++-- .../Messengers/DeviceVolumeMessenger.cs | 20 ++- .../Messengers/GenericMessenger.cs | 13 +- .../IBasicVideoMuteWithFeedbackMessenger.cs | 19 ++- .../ICommunicationMonitorMessenger.cs | 26 +++- .../Messengers/IDspPresetsMessenger.cs | 24 +-- .../IEssentialsRoomCombinerMessenger.cs | 14 +- .../Messengers/IHasCamerasMessenger.cs | 17 +- .../IHasCurrentSourceInfoMessenger.cs | 23 +-- .../Messengers/IHasInputsMessenger.cs | 23 ++- .../IHasPowerControlWithFeedbackMessenger.cs | 16 +- .../IHasScheduleAwarenessMessenger.cs | 29 ++-- .../Messengers/IHumiditySensor.cs | 15 +- .../Messengers/ILevelControlsMessenger.cs | 27 ++-- .../Messengers/IMatrixRoutingMessenger.cs | 48 +++--- .../IProjectorScreenLiftControlMessenger.cs | 30 +++- .../Messengers/IRunRouteActionMessenger.cs | 12 +- .../Messengers/ISelectableItemsMessenger.cs | 25 +-- .../IShutdownPromptTimerMessenger.cs | 17 +- .../Messengers/ISwitchedOutputMessenger.cs | 14 +- .../Messengers/ITechPasswordMessenger.cs | 11 +- .../Messengers/ITemperatureSensorMessenger.cs | 16 +- .../Messengers/LightingBaseMessenger.cs | 21 +-- .../Messengers/MessengerBase.cs | 16 +- .../Messengers/PressAndHoldHandler.cs | 37 +++-- .../Messengers/RoomEventScheduleMessenger.cs | 21 ++- .../Messengers/ShadeBaseMessenger.cs | 14 +- .../Messengers/SystemMonitorMessenger.cs | 40 +++-- .../Messengers/TwoWayDisplayBaseMessenger.cs | 22 +-- .../Messengers/VideoCodecBaseMessenger.cs | 84 ++++++---- .../AuthorizationResponse.cs | 9 +- .../MobileControlEssentialsConfig.cs | 10 +- .../MobileControlSystemController.cs | 4 +- .../MobileControlEssentialsRoomBridge.cs | 146 +++++++++--------- .../MobileControlTouchpanelProperties.cs | 12 +- .../Touchpanel/ThemeMessenger.cs | 3 +- .../UserCodeChangedContent.cs | 6 +- .../Volumes.cs | 16 +- .../WebApiHandlers/ActionPathsHandler.cs | 12 +- .../WebApiHandlers/UiClientHandler.cs | 18 ++- .../MobileControlWebsocketServer.cs | 21 ++- 45 files changed, 619 insertions(+), 425 deletions(-) diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/AudioCodecBaseMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/AudioCodecBaseMessenger.cs index 9a42141e..a1b30341 100644 --- a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/AudioCodecBaseMessenger.cs +++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/AudioCodecBaseMessenger.cs @@ -1,8 +1,8 @@ -using Newtonsoft.Json.Linq; +using System; +using System.Linq; +using Newtonsoft.Json.Linq; using PepperDash.Essentials.Devices.Common.AudioCodec; using PepperDash.Essentials.Devices.Common.Codec; -using System; -using System.Linq; namespace PepperDash.Essentials.AppServer.Messengers { @@ -29,12 +29,16 @@ namespace PepperDash.Essentials.AppServer.Messengers codec.CallStatusChange += Codec_CallStatusChange; } + /// protected override void RegisterActions() { base.RegisterActions(); - AddAction("/fullStatus", (id, content) => SendAtcFullMessageObject()); + AddAction("/fullStatus", (id, content) => SendAtcFullMessageObject(id)); + + AddAction("/audioDialerStatus", (id, content) => SendAtcFullMessageObject(id)); + AddAction("/dial", (id, content) => { var msg = content.ToObject>(); @@ -97,7 +101,7 @@ namespace PepperDash.Essentials.AppServer.Messengers /// Helper method to build call status for vtc /// /// - private void SendAtcFullMessageObject() + private void SendAtcFullMessageObject(string id = null) { var info = Codec.CodecInfo; @@ -109,7 +113,7 @@ namespace PepperDash.Essentials.AppServer.Messengers { phoneNumber = info.PhoneNumber } - }) + }), id ); } } diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/CameraBaseMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/CameraBaseMessenger.cs index cb3f77a5..7650b430 100644 --- a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/CameraBaseMessenger.cs +++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/CameraBaseMessenger.cs @@ -55,7 +55,9 @@ namespace PepperDash.Essentials.AppServer.Messengers { base.RegisterActions(); - AddAction("/fullStatus", (id, content) => SendCameraFullMessageObject()); + AddAction("/fullStatus", (id, content) => SendCameraFullMessageObject(id)); + + AddAction("/cameraStatus", (id, content) => SendCameraFullMessageObject(id)); if (Camera is IHasCameraPtzControl ptzCamera) @@ -173,7 +175,7 @@ namespace PepperDash.Essentials.AppServer.Messengers /// /// Helper method to update the full status of the camera /// - private void SendCameraFullMessageObject() + private void SendCameraFullMessageObject(string id = null) { var presetList = new List(); @@ -188,7 +190,7 @@ namespace PepperDash.Essentials.AppServer.Messengers cameraMode = GetCameraMode(), hasPresets = Camera is IHasCameraPresets, presets = presetList - }) + }), id ); } diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/CurrentSourcesMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/CurrentSourcesMessenger.cs index 5b220256..9643a607 100644 --- a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/CurrentSourcesMessenger.cs +++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/CurrentSourcesMessenger.cs @@ -33,16 +33,9 @@ namespace PepperDash.Essentials.AppServer.Messengers { base.RegisterActions(); - AddAction("/fullStatus", (id, content) => - { - var message = new CurrentSourcesStateMessage - { - CurrentSourceKeys = sourceDevice.CurrentSourceKeys, - CurrentSources = sourceDevice.CurrentSources - }; + AddAction("/fullStatus", (id, content) => SendCurrentSourceStatus(id)); - PostStatusMessage(message); - }); + AddAction("/currentSourceStatus", (id, content) => SendCurrentSourceStatus(id)); sourceDevice.CurrentSourcesChanged += (sender, e) => { @@ -53,6 +46,17 @@ namespace PepperDash.Essentials.AppServer.Messengers })); }; } + + private void SendCurrentSourceStatus(string id = null) + { + var message = new CurrentSourcesStateMessage + { + CurrentSourceKeys = sourceDevice.CurrentSourceKeys, + CurrentSources = sourceDevice.CurrentSources + }; + + PostStatusMessage(message, id); + } } /// diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/DeviceInfoMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/DeviceInfoMessenger.cs index 6537b143..876bcafe 100644 --- a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/DeviceInfoMessenger.cs +++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/DeviceInfoMessenger.cs @@ -1,8 +1,8 @@ -using Newtonsoft.Json; +using System.Timers; +using Newtonsoft.Json; using Newtonsoft.Json.Linq; using PepperDash.Core; using PepperDash.Essentials.Core.DeviceInfo; -using System.Timers; namespace PepperDash.Essentials.AppServer.Messengers { @@ -67,13 +67,20 @@ namespace PepperDash.Essentials.AppServer.Messengers debounceTimer.Start(); }; - AddAction("/fullStatus", (id, context) => PostStatusMessage(new DeviceInfoStateMessage - { - DeviceInfo = _deviceInfoProvider.DeviceInfo - })); + AddAction("/fullStatus", (id, context) => SendFullStatus(id)); + + AddAction("/deviceInfo", (id, content) => SendFullStatus(id)); AddAction("/update", (id, context) => _deviceInfoProvider.UpdateDeviceInfo()); } + + private void SendFullStatus(string id = null) + { + PostStatusMessage(new DeviceInfoStateMessage + { + DeviceInfo = _deviceInfoProvider.DeviceInfo + }, id); + } } /// diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/DevicePresetsModelMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/DevicePresetsModelMessenger.cs index fb8ccef3..833a781b 100644 --- a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/DevicePresetsModelMessenger.cs +++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/DevicePresetsModelMessenger.cs @@ -1,11 +1,11 @@ -using Newtonsoft.Json; +using System; +using System.Collections.Generic; +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 { @@ -16,18 +16,24 @@ namespace PepperDash.Essentials.AppServer.Messengers { private readonly ITvPresetsProvider _presetsDevice; + /// + /// Constructor for DevicePresetsModelMessenger + /// + /// The key. + /// The message path. + /// The presets device. public DevicePresetsModelMessenger(string key, string messagePath, ITvPresetsProvider presetsDevice) : base(key, messagePath, presetsDevice as Device) { _presetsDevice = presetsDevice; } - private void SendPresets() + private void SendPresets(string id = null) { PostStatusMessage(new PresetStateMessage { Favorites = _presetsDevice.TvPresets.PresetsList - }); + }, id); } private void RecallPreset(ISetTopBoxNumericKeypad device, string channel) @@ -43,6 +49,7 @@ namespace PepperDash.Essentials.AppServer.Messengers #region Overrides of MessengerBase + /// protected override void RegisterActions() { @@ -51,7 +58,7 @@ namespace PepperDash.Essentials.AppServer.Messengers this.LogInformation("getting full status for client {id}", id); try { - SendPresets(); + SendPresets(id); } catch (Exception ex) { @@ -59,6 +66,8 @@ namespace PepperDash.Essentials.AppServer.Messengers } }); + AddAction("/presetsStatus", (id, content) => SendPresets(id)); + AddAction("/recall", (id, content) => { var p = content.ToObject(); @@ -91,16 +100,16 @@ namespace PepperDash.Essentials.AppServer.Messengers /// public class PresetChannelMessage { - [JsonProperty("preset")] /// /// Gets or sets the Preset /// + [JsonProperty("preset")] public PresetChannel Preset; - [JsonProperty("deviceKey")] /// /// Gets or sets the DeviceKey /// + [JsonProperty("deviceKey")] public string DeviceKey; } @@ -109,10 +118,11 @@ namespace PepperDash.Essentials.AppServer.Messengers /// public class PresetStateMessage : DeviceStateMessageBase { - [JsonProperty("favorites", NullValueHandling = NullValueHandling.Ignore)] + /// /// Gets or sets the Favorites /// + [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 index b6c9b18e..2eb965b9 100644 --- a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/DeviceVolumeMessenger.cs +++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/DeviceVolumeMessenger.cs @@ -1,9 +1,9 @@ -using Newtonsoft.Json; +using System; +using Newtonsoft.Json; using Newtonsoft.Json.Converters; using Newtonsoft.Json.Linq; using PepperDash.Core; using PepperDash.Essentials.Core; -using System; namespace PepperDash.Essentials.AppServer.Messengers { @@ -14,13 +14,19 @@ namespace PepperDash.Essentials.AppServer.Messengers { private readonly IBasicVolumeWithFeedback _localDevice; + /// + /// Initializes a new instance of the class. + /// + /// The key. + /// The message path. + /// The device. public DeviceVolumeMessenger(string key, string messagePath, IBasicVolumeWithFeedback device) : base(key, messagePath, device as IKeyName) { _localDevice = device; } - private void SendStatus() + private void SendStatus(string id = null) { try { @@ -40,7 +46,7 @@ namespace PepperDash.Essentials.AppServer.Messengers messageObj.Volume.Units = volumeAdvanced.Units; } - PostStatusMessage(messageObj); + PostStatusMessage(messageObj, id); } catch (Exception ex) { @@ -50,10 +56,12 @@ namespace PepperDash.Essentials.AppServer.Messengers #region Overrides of MessengerBase - + /// protected override void RegisterActions() { - AddAction("/fullStatus", (id, content) => SendStatus()); + AddAction("/fullStatus", (id, content) => SendStatus(id)); + + AddAction("/volumeStatus", (id, content) => SendStatus(id)); AddAction("/level", (id, content) => { diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/GenericMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/GenericMessenger.cs index eb0611c7..83def192 100644 --- a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/GenericMessenger.cs +++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/GenericMessenger.cs @@ -7,22 +7,29 @@ namespace PepperDash.Essentials.AppServer.Messengers /// public class GenericMessenger : MessengerBase { + /// + /// Initializes a new instance of the class. + /// + /// The key. + /// The device. + /// The message path. public GenericMessenger(string key, EssentialsDevice device, string messagePath) : base(key, messagePath, device) { } + /// protected override void RegisterActions() { base.RegisterActions(); - AddAction("/fullStatus", (id, content) => SendFullStatus()); + AddAction("/fullStatus", (id, content) => SendFullStatus(id)); } - private void SendFullStatus() + private void SendFullStatus(string id = null) { var state = new DeviceStateMessageBase(); - PostStatusMessage(state); + PostStatusMessage(state, id); } } } diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IBasicVideoMuteWithFeedbackMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IBasicVideoMuteWithFeedbackMessenger.cs index 70d59aa4..5977be6e 100644 --- a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IBasicVideoMuteWithFeedbackMessenger.cs +++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IBasicVideoMuteWithFeedbackMessenger.cs @@ -1,8 +1,8 @@ -using Newtonsoft.Json; +using System.Collections.Generic; +using Newtonsoft.Json; using Newtonsoft.Json.Linq; using PepperDash.Core; using PepperDash.Essentials.Core; -using System.Collections.Generic; namespace PepperDash.Essentials.AppServer.Messengers { @@ -13,6 +13,12 @@ namespace PepperDash.Essentials.AppServer.Messengers { private readonly IBasicVideoMuteWithFeedback device; + /// + /// Initializes a new instance of the class. + /// + /// + /// + /// public IBasicVideoMuteWithFeedbackMessenger(string key, string messagePath, IBasicVideoMuteWithFeedback device) : base(key, messagePath, device as IKeyName) { @@ -22,21 +28,24 @@ namespace PepperDash.Essentials.AppServer.Messengers /// /// SendFullStatus method /// - public void SendFullStatus() + public void SendFullStatus(string id = null) { var messageObj = new IBasicVideoMuteWithFeedbackMessage { VideoMuteState = device.VideoMuteIsOn.BoolValue }; - PostStatusMessage(messageObj); + PostStatusMessage(messageObj, id); } + /// protected override void RegisterActions() { base.RegisterActions(); - AddAction("/fullStatus", (id, content) => SendFullStatus()); + AddAction("/fullStatus", (id, content) => SendFullStatus(id)); + + AddAction("/videoMuteStatus", (id, content) => SendFullStatus(id)); AddAction("/videoMuteToggle", (id, content) => { diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/ICommunicationMonitorMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/ICommunicationMonitorMessenger.cs index 9399aacb..c3d2f1f0 100644 --- a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/ICommunicationMonitorMessenger.cs +++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/ICommunicationMonitorMessenger.cs @@ -24,14 +24,12 @@ namespace PepperDash.Essentials.AppServer.Messengers AddAction("/fullStatus", (id, content) => { - PostStatusMessage(new CommunicationMonitorState - { - CommunicationMonitor = new CommunicationMonitorProps - { - IsOnline = _communicationMonitor.CommunicationMonitor.IsOnline, - Status = _communicationMonitor.CommunicationMonitor.Status - } - }); + SendFullStatus(id); + }); + + AddAction("/commStatus", (id, content) => + { + SendFullStatus(id); }); _communicationMonitor.CommunicationMonitor.StatusChange += (sender, args) => @@ -46,6 +44,18 @@ namespace PepperDash.Essentials.AppServer.Messengers })); }; } + + private void SendFullStatus(string id = null) + { + PostStatusMessage(new CommunicationMonitorState + { + CommunicationMonitor = new CommunicationMonitorProps + { + IsOnline = _communicationMonitor.CommunicationMonitor.IsOnline, + Status = _communicationMonitor.CommunicationMonitor.Status + } + }); + } } /// diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IDspPresetsMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IDspPresetsMessenger.cs index 8096c4e9..92674574 100644 --- a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IDspPresetsMessenger.cs +++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IDspPresetsMessenger.cs @@ -1,7 +1,7 @@ -using Newtonsoft.Json; +using System.Collections.Generic; +using Newtonsoft.Json; using PepperDash.Core; using PepperDash.Essentials.Core; -using System.Collections.Generic; namespace PepperDash.Essentials.AppServer.Messengers { @@ -22,15 +22,9 @@ namespace PepperDash.Essentials.AppServer.Messengers { base.RegisterActions(); - AddAction("/fullStatus", (id, content) => - { - var message = new IHasDspPresetsStateMessage - { - Presets = device.Presets - }; + AddAction("/fullStatus", (id, content) => SendFullStatus(id)); - PostStatusMessage(message); - }); + AddAction("/dspPresetStatus", (id, content) => SendFullStatus(id)); AddAction("/recallPreset", (id, content) => { @@ -43,6 +37,16 @@ namespace PepperDash.Essentials.AppServer.Messengers } }); } + + private void SendFullStatus(string id = null) + { + var message = new IHasDspPresetsStateMessage + { + Presets = device.Presets + }; + + PostStatusMessage(message, id); + } } /// diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IEssentialsRoomCombinerMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IEssentialsRoomCombinerMessenger.cs index ab2ff259..966d8d77 100644 --- a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IEssentialsRoomCombinerMessenger.cs +++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IEssentialsRoomCombinerMessenger.cs @@ -1,10 +1,10 @@ -using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using Newtonsoft.Json; using Newtonsoft.Json.Linq; using PepperDash.Core; using PepperDash.Core.Logging; using PepperDash.Essentials.Core; -using System; -using System.Collections.Generic; namespace PepperDash.Essentials.AppServer.Messengers { @@ -46,7 +46,9 @@ namespace PepperDash.Essentials.AppServer.Messengers /// partition states. protected override void RegisterActions() { - AddAction("/fullStatus", (id, content) => SendFullStatus()); + AddAction("/fullStatus", (id, content) => SendFullStatus(id)); + + AddAction("/combinerStatus", (id, content) => SendFullStatus(id)); AddAction("/setAutoMode", (id, content) => { @@ -120,7 +122,7 @@ namespace PepperDash.Essentials.AppServer.Messengers } } - private void SendFullStatus() + private void SendFullStatus(string id = null) { try { @@ -141,7 +143,7 @@ namespace PepperDash.Essentials.AppServer.Messengers Partitions = _roomCombiner.Partitions }; - PostStatusMessage(message); + PostStatusMessage(message, id); } catch (Exception e) { diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHasCamerasMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHasCamerasMessenger.cs index 4fa0c5b1..7099e52c 100644 --- a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHasCamerasMessenger.cs +++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHasCamerasMessenger.cs @@ -1,11 +1,11 @@ -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using PepperDash.Essentials.Devices.Common.Cameras; -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using PepperDash.Essentials.Devices.Common.Cameras; namespace PepperDash.Essentials.AppServer.Messengers { @@ -26,7 +26,7 @@ namespace PepperDash.Essentials.AppServer.Messengers /// /// /// - public IHasCamerasMessenger(string key, string messagePath , IHasCameras cameraController) + public IHasCamerasMessenger(string key, string messagePath, IHasCameras cameraController) : base(key, messagePath, cameraController) { CameraController = cameraController ?? throw new ArgumentNullException("cameraController"); @@ -49,10 +49,9 @@ namespace PepperDash.Essentials.AppServer.Messengers { base.RegisterActions(); - AddAction("/fullStatus", (id, context) => - { - SendFullStatus(id); - }); + AddAction("/fullStatus", (id, context) => SendFullStatus(id)); + + AddAction("/cameraListStatus", (id, content) => SendFullStatus(id)); AddAction("/selectCamera", (id, content) => { diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHasCurrentSourceInfoMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHasCurrentSourceInfoMessenger.cs index 04130776..6c0f1a0a 100644 --- a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHasCurrentSourceInfoMessenger.cs +++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHasCurrentSourceInfoMessenger.cs @@ -11,6 +11,7 @@ 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; @@ -20,16 +21,9 @@ namespace PepperDash.Essentials.AppServer.Messengers { base.RegisterActions(); - AddAction("/fullStatus", (id, content) => - { - var message = new CurrentSourceStateMessage - { - CurrentSourceKey = sourceDevice.CurrentSourceInfoKey, - CurrentSource = sourceDevice.CurrentSourceInfo - }; + AddAction("/fullStatus", (id, content) => SendFullStatus(id)); - PostStatusMessage(message); - }); + AddAction("/currentSourceInfoStatus", (id, content) => SendFullStatus(id)); sourceDevice.CurrentSourceChange += (sender, e) => { @@ -47,6 +41,17 @@ namespace PepperDash.Essentials.AppServer.Messengers } }; } + + private void SendFullStatus(string id = null) + { + var message = new CurrentSourceStateMessage + { + CurrentSourceKey = sourceDevice.CurrentSourceInfoKey, + CurrentSource = sourceDevice.CurrentSourceInfo + }; + + PostStatusMessage(message, id); + } } /// diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHasInputsMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHasInputsMessenger.cs index bff3ca85..57ab2617 100644 --- a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHasInputsMessenger.cs +++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHasInputsMessenger.cs @@ -1,9 +1,9 @@ -using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using Newtonsoft.Json; using PepperDash.Core; using PepperDash.Core.Logging; using PepperDash.Essentials.Core.DeviceTypeInterfaces; -using System; -using System.Collections.Generic; namespace PepperDash.Essentials.AppServer.Messengers { @@ -11,8 +11,8 @@ namespace PepperDash.Essentials.AppServer.Messengers /// Represents a IHasInputsMessenger /// public class IHasInputsMessenger : MessengerBase - { - private readonly IHasInputs itemDevice; + { + private readonly IHasInputs itemDevice; /// @@ -23,17 +23,16 @@ namespace PepperDash.Essentials.AppServer.Messengers /// public IHasInputsMessenger(string key, string messagePath, IHasInputs device) : base(key, messagePath, device) { - itemDevice = device; + itemDevice = device; } protected override void RegisterActions() { base.RegisterActions(); - AddAction("/fullStatus", (id, context) => - { - SendFullStatus(); - }); + AddAction("/fullStatus", (id, context) => SendFullStatus(id)); + + AddAction("/inputStatus", (id, content) => SendFullStatus(id)); itemDevice.Inputs.ItemsUpdated += (sender, args) => { @@ -62,7 +61,7 @@ namespace PepperDash.Essentials.AppServer.Messengers } } - private void SendFullStatus() + private void SendFullStatus(string id = null) { try { @@ -77,7 +76,7 @@ namespace PepperDash.Essentials.AppServer.Messengers } }; - PostStatusMessage(stateObject); + PostStatusMessage(stateObject, id); } catch (Exception e) { diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHasPowerControlWithFeedbackMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHasPowerControlWithFeedbackMessenger.cs index 66e97352..44c29f86 100644 --- a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHasPowerControlWithFeedbackMessenger.cs +++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHasPowerControlWithFeedbackMessenger.cs @@ -12,6 +12,12 @@ namespace PepperDash.Essentials.AppServer.Messengers { private readonly IHasPowerControlWithFeedback _powerControl; + /// + /// Initializes a new instance of the class. + /// + /// The key. + /// The message path. + /// The power control device public IHasPowerControlWithFeedbackMessenger(string key, string messagePath, IHasPowerControlWithFeedback powerControl) : base(key, messagePath, powerControl as IKeyName) { @@ -21,7 +27,7 @@ namespace PepperDash.Essentials.AppServer.Messengers /// /// SendFullStatus method /// - public void SendFullStatus() + public void SendFullStatus(string id = null) { var messageObj = new PowerControlWithFeedbackStateMessage { @@ -31,11 +37,14 @@ namespace PepperDash.Essentials.AppServer.Messengers PostStatusMessage(messageObj); } + /// protected override void RegisterActions() { base.RegisterActions(); - AddAction("/fullStatus", (id, content) => SendFullStatus()); + AddAction("/fullStatus", (id, content) => SendFullStatus(id)); + + AddAction("/powerStatus", (id, content) => SendFullStatus(id)); _powerControl.PowerIsOnFeedback.OutputChange += PowerIsOnFeedback_OutputChange; ; } @@ -55,6 +64,9 @@ namespace PepperDash.Essentials.AppServer.Messengers /// public class PowerControlWithFeedbackStateMessage : DeviceStateMessageBase { + /// + /// Power State + /// [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 index 6329a25c..76598cee 100644 --- a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHasScheduleAwarenessMessenger.cs +++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHasScheduleAwarenessMessenger.cs @@ -1,9 +1,9 @@ -using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using Newtonsoft.Json; using Newtonsoft.Json.Linq; using PepperDash.Core; using PepperDash.Essentials.Devices.Common.Codec; -using System; -using System.Collections.Generic; namespace PepperDash.Essentials.AppServer.Messengers { @@ -27,7 +27,9 @@ namespace PepperDash.Essentials.AppServer.Messengers protected override void RegisterActions() { - AddAction("/schedule/fullStatus", (id, content) => SendFullScheduleObject()); + AddAction("/schedule/fullStatus", (id, content) => SendFullScheduleObject(id)); + + AddAction("/schedule/status", (id, content) => SendFullScheduleObject(id)); } private void CodecSchedule_MeetingEventChange(object sender, MeetingEventArgs e) @@ -51,13 +53,13 @@ namespace PepperDash.Essentials.AppServer.Messengers /// /// Helper method to send the full schedule data /// - private void SendFullScheduleObject() + private void SendFullScheduleObject(string id = null) { PostStatusMessage(new FullScheduleMessage { Meetings = ScheduleSource.CodecSchedule.Meetings, MeetingWarningMinutes = ScheduleSource.CodecSchedule.MeetingWarningMinutes - }); + }, id); } } @@ -66,16 +68,18 @@ namespace PepperDash.Essentials.AppServer.Messengers /// public class FullScheduleMessage : DeviceStateMessageBase { - [JsonProperty("meetings", NullValueHandling = NullValueHandling.Ignore)] + /// /// Gets or sets the Meetings /// + [JsonProperty("meetings", NullValueHandling = NullValueHandling.Ignore)] public List Meetings { get; set; } - [JsonProperty("meetingWarningMinutes", NullValueHandling = NullValueHandling.Ignore)] + /// /// Gets or sets the MeetingWarningMinutes /// + [JsonProperty("meetingWarningMinutes", NullValueHandling = NullValueHandling.Ignore)] public int MeetingWarningMinutes { get; set; } } @@ -84,10 +88,11 @@ namespace PepperDash.Essentials.AppServer.Messengers /// public class MeetingChangeMessage { - [JsonProperty("meetingChange", NullValueHandling = NullValueHandling.Ignore)] + /// /// Gets or sets the MeetingChange /// + [JsonProperty("meetingChange", NullValueHandling = NullValueHandling.Ignore)] public MeetingChange MeetingChange { get; set; } } @@ -96,16 +101,18 @@ namespace PepperDash.Essentials.AppServer.Messengers /// public class MeetingChange { - [JsonProperty("changeType", NullValueHandling = NullValueHandling.Ignore)] + /// /// Gets or sets the ChangeType /// + [JsonProperty("changeType", NullValueHandling = NullValueHandling.Ignore)] public string ChangeType { get; set; } - [JsonProperty("meeting", NullValueHandling = NullValueHandling.Ignore)] + /// /// Gets or sets the Meeting /// + [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 index 4b274270..6bd04a74 100644 --- a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHumiditySensor.cs +++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHumiditySensor.cs @@ -1,7 +1,7 @@ -using Newtonsoft.Json; +using System; +using Newtonsoft.Json; using PepperDash.Core; using PepperDash.Essentials.Core.DeviceTypeInterfaces; -using System; namespace PepperDash.Essentials.AppServer.Messengers { @@ -22,19 +22,21 @@ namespace PepperDash.Essentials.AppServer.Messengers { base.RegisterActions(); - AddAction("/fullStatus", (id, content) => SendFullStatus()); + AddAction("/fullStatus", (id, content) => SendFullStatus(id)); + + AddAction("/humidityStatus", (id, content) => SendFullStatus(id)); device.HumidityFeedback.OutputChange += new EventHandler((o, a) => SendFullStatus()); } - private void SendFullStatus() + private void SendFullStatus(string id = null) { var state = new IHumiditySensorStateMessage { Humidity = string.Format("{0}%", device.HumidityFeedback.UShortValue) }; - PostStatusMessage(state); + PostStatusMessage(state, id); } } @@ -43,10 +45,11 @@ namespace PepperDash.Essentials.AppServer.Messengers /// public class IHumiditySensorStateMessage : DeviceStateMessageBase { - [JsonProperty("humidity")] + /// /// Gets or sets the Humidity /// + [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 index 946c0bfd..03bfc80f 100644 --- a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/ILevelControlsMessenger.cs +++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/ILevelControlsMessenger.cs @@ -1,9 +1,9 @@ -using Newtonsoft.Json; +using System.Collections.Generic; +using System.Linq; +using Newtonsoft.Json; using Newtonsoft.Json.Linq; using PepperDash.Core; using PepperDash.Essentials.Core.DeviceTypeInterfaces; -using System.Collections.Generic; -using System.Linq; namespace PepperDash.Essentials.AppServer.Messengers { @@ -13,6 +13,7 @@ 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 IKeyName) { levelControlsDevice = device; @@ -22,15 +23,9 @@ namespace PepperDash.Essentials.AppServer.Messengers { 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 }) - }; + AddAction("/fullStatus", (id, context) => SendFullStatus(id)); - PostStatusMessage(message); - }); + AddAction("/levelStats", (id, content) => SendFullStatus(id)); foreach (var levelControl in levelControlsDevice.LevelControlPoints) { @@ -75,6 +70,16 @@ namespace PepperDash.Essentials.AppServer.Messengers })); } } + + private void SendFullStatus(string id = null) + { + 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, id); + } } /// diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IMatrixRoutingMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IMatrixRoutingMessenger.cs index c63bd0e4..3a1b6a27 100644 --- a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IMatrixRoutingMessenger.cs +++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IMatrixRoutingMessenger.cs @@ -1,12 +1,12 @@ -using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using Newtonsoft.Json; using Newtonsoft.Json.Linq; using PepperDash.Core; using PepperDash.Essentials.Core; using PepperDash.Essentials.Core.Routing; using Serilog.Events; -using System; -using System.Collections.Generic; -using System.Linq; namespace PepperDash.Essentials.AppServer.Messengers { @@ -25,25 +25,9 @@ namespace PepperDash.Essentials.AppServer.Messengers { 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)), - }; + AddAction("/fullStatus", (id, content) => SendFullStatus(id)); - - PostStatusMessage(message); - } - catch (Exception e) - { - Debug.LogMessage(e, "Exception Getting full status: {@exception}", this, e); - } - }); + AddAction("/matrixStatus", (id, content) => SendFullStatus(id)); AddAction("/route", (id, content) => { @@ -80,6 +64,26 @@ namespace PepperDash.Essentials.AppServer.Messengers }; } } + + private void SendFullStatus(string id = null) + { + 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, id); + } + catch (Exception e) + { + Debug.LogMessage(e, "Exception Getting full status: {@exception}", this, e); + } + } } /// diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IProjectorScreenLiftControlMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IProjectorScreenLiftControlMessenger.cs index f0840b5f..f63b4834 100644 --- a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IProjectorScreenLiftControlMessenger.cs +++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IProjectorScreenLiftControlMessenger.cs @@ -1,9 +1,9 @@ -using Newtonsoft.Json; +using System; +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 { @@ -14,17 +14,28 @@ namespace PepperDash.Essentials.AppServer.Messengers { private readonly IProjectorScreenLiftControl device; + /// + /// Initializes a new instance of the class. + /// + /// message key + /// message path + /// screen lift device public IProjectorScreenLiftControlMessenger(string key, string messagePath, IProjectorScreenLiftControl screenLiftDevice) : base(key, messagePath, screenLiftDevice as IKeyName) { device = screenLiftDevice; } + /// + /// Registers the actions for the messenger. + /// protected override void RegisterActions() { base.RegisterActions(); - AddAction("/fullStatus", (id, content) => SendFullStatus()); + AddAction("/fullStatus", (id, content) => SendFullStatus(id)); + + AddAction("/screenliftStatus", (id, content) => SendFullStatus(id)); AddAction("/raise", (id, content) => { @@ -53,7 +64,7 @@ namespace PepperDash.Essentials.AppServer.Messengers PostStatusMessage(JToken.FromObject(state)); } - private void SendFullStatus() + private void SendFullStatus(string id = null) { var state = new ScreenLiftStateMessage { @@ -62,7 +73,7 @@ namespace PepperDash.Essentials.AppServer.Messengers DisplayDeviceKey = device.DisplayDeviceKey }; - PostStatusMessage(state); + PostStatusMessage(state, id); } } @@ -71,20 +82,23 @@ namespace PepperDash.Essentials.AppServer.Messengers /// public class ScreenLiftStateMessage : DeviceStateMessageBase { + /// + /// Gets or sets the InUpPosition + /// [JsonProperty("inUpPosition", NullValueHandling = NullValueHandling.Ignore)] public bool? InUpPosition { get; set; } - [JsonProperty("displayDeviceKey", NullValueHandling = NullValueHandling.Ignore)] /// /// Gets or sets the DisplayDeviceKey /// + [JsonProperty("displayDeviceKey", NullValueHandling = NullValueHandling.Ignore)] public string DisplayDeviceKey { get; set; } - [JsonConverter(typeof(StringEnumConverter))] - [JsonProperty("type", NullValueHandling = NullValueHandling.Ignore)] /// /// Gets or sets the Type /// + [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 index 9bfb8b39..d5038d91 100644 --- a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IRunRouteActionMessenger.cs +++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IRunRouteActionMessenger.cs @@ -1,8 +1,8 @@ -using Newtonsoft.Json; +using System; +using Newtonsoft.Json; using PepperDash.Core; using PepperDash.Core.Logging; using PepperDash.Essentials.Core; -using System; namespace PepperDash.Essentials.AppServer.Messengers @@ -36,7 +36,9 @@ namespace PepperDash.Essentials.AppServer.Messengers protected override void RegisterActions() { - AddAction("/fullStatus", (id, content) => SendRoutingFullMessageObject()); + AddAction("/fullStatus", (id, content) => SendRoutingFullMessageObject(id)); + + AddAction("/routingStatus", (id, content) => SendRoutingFullMessageObject(id)); AddAction("/source", (id, content) => { @@ -62,7 +64,7 @@ namespace PepperDash.Essentials.AppServer.Messengers /// /// Helper method to update full status of the routing device /// - private void SendRoutingFullMessageObject() + private void SendRoutingFullMessageObject(string id = null) { if (RoutingDevice is IRoutingSink sinkDevice) { @@ -84,10 +86,10 @@ namespace PepperDash.Essentials.AppServer.Messengers /// public class RoutingStateMessage : DeviceStateMessageBase { - [JsonProperty("selectedSourceKey")] /// /// Gets or sets the SelectedSourceKey /// + [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 index 54f1e314..36d73a3e 100644 --- a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/ISelectableItemsMessenger.cs +++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/ISelectableItemsMessenger.cs @@ -1,9 +1,9 @@ -using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using Newtonsoft.Json; using PepperDash.Core; using PepperDash.Core.Logging; using PepperDash.Essentials.Core.DeviceTypeInterfaces; -using System; -using System.Collections.Generic; namespace PepperDash.Essentials.AppServer.Messengers { @@ -11,7 +11,7 @@ namespace PepperDash.Essentials.AppServer.Messengers /// Represents a ISelectableItemsMessenger /// public class ISelectableItemsMessenger : MessengerBase - { + { private readonly ISelectableItems itemDevice; private readonly string _propName; @@ -34,9 +34,10 @@ namespace PepperDash.Essentials.AppServer.Messengers base.RegisterActions(); AddAction("/fullStatus", (id, context) => - { - SendFullStatus(); - }); + SendFullStatus(id) + ); + + AddAction("/itemsStatus", (id, content) => SendFullStatus(id)); itemDevice.ItemsUpdated += (sender, args) => { @@ -65,7 +66,7 @@ namespace PepperDash.Essentials.AppServer.Messengers } } - private void SendFullStatus() + private void SendFullStatus(string id = null) { try { @@ -77,7 +78,7 @@ namespace PepperDash.Essentials.AppServer.Messengers CurrentItem = itemDevice.CurrentItem }; - PostStatusMessage(stateObject); + PostStatusMessage(stateObject, id); } catch (Exception e) { @@ -91,13 +92,17 @@ namespace PepperDash.Essentials.AppServer.Messengers /// public class ISelectableItemsStateMessage : DeviceStateMessageBase { + /// + /// Gets or sets the Items + /// [JsonProperty("items")] public Dictionary Items { get; set; } - [JsonProperty("currentItem")] + /// /// Gets or sets the CurrentItem /// + [JsonProperty("currentItem")] public TKey CurrentItem { get; set; } } diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IShutdownPromptTimerMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IShutdownPromptTimerMessenger.cs index 600779f9..30a24827 100644 --- a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IShutdownPromptTimerMessenger.cs +++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IShutdownPromptTimerMessenger.cs @@ -21,9 +21,10 @@ namespace PepperDash.Essentials.AppServer.Messengers protected override void RegisterActions() { AddAction("/status", (id, content) => - { - SendFullStatus(); - }); + SendFullStatus(id) + ); + + AddAction("/shutdownPromptStatus", (id, content) => SendFullStatus(id)); AddAction("/setShutdownPromptSeconds", (id, content) => { @@ -68,7 +69,7 @@ namespace PepperDash.Essentials.AppServer.Messengers }; } - private void SendFullStatus() + private void SendFullStatus(string id = null) { var status = new IShutdownPromptTimerStateMessage { @@ -77,7 +78,7 @@ namespace PepperDash.Essentials.AppServer.Messengers PercentageRemaining = _room.ShutdownPromptTimer.PercentFeedback.UShortValue }; - PostStatusMessage(status); + PostStatusMessage(status, id); } } @@ -87,22 +88,22 @@ namespace PepperDash.Essentials.AppServer.Messengers /// public class IShutdownPromptTimerStateMessage : DeviceStateMessageBase { - [JsonProperty("secondsRemaining")] /// /// Gets or sets the SecondsRemaining /// + [JsonProperty("secondsRemaining")] public int SecondsRemaining { get; set; } - [JsonProperty("percentageRemaining")] /// /// Gets or sets the PercentageRemaining /// + [JsonProperty("percentageRemaining")] public int PercentageRemaining { get; set; } - [JsonProperty("shutdownPromptSeconds")] /// /// Gets or sets the ShutdownPromptSeconds /// + [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 index 6efd182b..98223188 100644 --- a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/ISwitchedOutputMessenger.cs +++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/ISwitchedOutputMessenger.cs @@ -1,7 +1,7 @@ -using Newtonsoft.Json; +using System; +using Newtonsoft.Json; using PepperDash.Core; using PepperDash.Essentials.Core.CrestronIO; -using System; namespace PepperDash.Essentials.AppServer.Messengers { @@ -23,7 +23,9 @@ namespace PepperDash.Essentials.AppServer.Messengers { base.RegisterActions(); - AddAction("/fullStatus", (id, content) => SendFullStatus()); + AddAction("/fullStatus", (id, content) => SendFullStatus(id)); + + AddAction("/switchedOutputStatus", (id, content) => SendFullStatus(id)); AddAction("/on", (id, content) => { @@ -42,14 +44,14 @@ namespace PepperDash.Essentials.AppServer.Messengers device.OutputIsOnFeedback.OutputChange += new EventHandler((o, a) => SendFullStatus()); } - private void SendFullStatus() + private void SendFullStatus(string id = null) { var state = new ISwitchedOutputStateMessage { IsOn = device.OutputIsOnFeedback.BoolValue }; - PostStatusMessage(state); + PostStatusMessage(state, id); } } @@ -58,10 +60,10 @@ namespace PepperDash.Essentials.AppServer.Messengers /// public class ISwitchedOutputStateMessage : DeviceStateMessageBase { - [JsonProperty("isOn")] /// /// Gets or sets the IsOn /// + [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 index 783bee7c..283ef0c8 100644 --- a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/ITechPasswordMessenger.cs +++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/ITechPasswordMessenger.cs @@ -20,10 +20,9 @@ namespace PepperDash.Essentials.AppServer.Messengers protected override void RegisterActions() { - AddAction("/status", (id, content) => - { - SendFullStatus(); - }); + AddAction("/status", (id, content) => SendFullStatus(id)); + + AddAction("/techPasswordStatus", (id, content) => SendFullStatus(id)); AddAction("/validateTechPassword", (id, content) => { @@ -55,14 +54,14 @@ namespace PepperDash.Essentials.AppServer.Messengers }; } - private void SendFullStatus() + private void SendFullStatus(string id = null) { var status = new ITechPasswordStateMessage { TechPasswordLength = _room.TechPasswordLength }; - PostStatusMessage(status); + PostStatusMessage(status, id); } } diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/ITemperatureSensorMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/ITemperatureSensorMessenger.cs index 9f3b56eb..5963bba5 100644 --- a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/ITemperatureSensorMessenger.cs +++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/ITemperatureSensorMessenger.cs @@ -1,7 +1,7 @@ -using Newtonsoft.Json; +using System; +using Newtonsoft.Json; using PepperDash.Core; using PepperDash.Essentials.Core.DeviceTypeInterfaces; -using System; namespace PepperDash.Essentials.AppServer.Messengers { @@ -22,7 +22,9 @@ namespace PepperDash.Essentials.AppServer.Messengers { base.RegisterActions(); - AddAction("/fullStatus", (id, content) => SendFullStatus()); + AddAction("/fullStatus", (id, content) => SendFullStatus(id)); + + AddAction("/temperatureStatus", (id, content) => SendFullStatus(id)); AddAction("/setTemperatureUnitsToCelcius", (id, content) => { @@ -38,7 +40,7 @@ namespace PepperDash.Essentials.AppServer.Messengers device.TemperatureInCFeedback.OutputChange += new EventHandler((o, a) => SendFullStatus()); } - private void SendFullStatus() + private void SendFullStatus(string id = null) { // format the temperature to a string with one decimal place var tempString = string.Format("{0}.{1}", device.TemperatureFeedback.UShortValue / 10, device.TemperatureFeedback.UShortValue % 10); @@ -49,7 +51,7 @@ namespace PepperDash.Essentials.AppServer.Messengers TemperatureInCelsius = device.TemperatureInCFeedback.BoolValue }; - PostStatusMessage(state); + PostStatusMessage(state, id); } } @@ -58,16 +60,16 @@ namespace PepperDash.Essentials.AppServer.Messengers /// public class ITemperatureSensorStateMessage : DeviceStateMessageBase { - [JsonProperty("temperature")] /// /// Gets or sets the Temperature /// + [JsonProperty("temperature")] public string Temperature { get; set; } - [JsonProperty("temperatureInCelsius")] /// /// Gets or sets the TemperatureInCelsius /// + [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 index 9c0672fb..71c6349f 100644 --- a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/LightingBaseMessenger.cs +++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/LightingBaseMessenger.cs @@ -1,8 +1,8 @@ -using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using Newtonsoft.Json; using PepperDash.Core; using PepperDash.Essentials.Core.Lighting; -using System; -using System.Collections.Generic; namespace PepperDash.Essentials.AppServer.Messengers { @@ -35,7 +35,9 @@ namespace PepperDash.Essentials.AppServer.Messengers { base.RegisterActions(); - AddAction("/fullStatus", (id, content) => SendFullStatus()); + AddAction("/fullStatus", (id, content) => SendFullStatus(id)); + + AddAction("/lightingStatus", (id, content) => SendFullStatus(id)); AddAction("/selectScene", (id, content) => { @@ -43,14 +45,14 @@ namespace PepperDash.Essentials.AppServer.Messengers lightingScenesDevice.SelectScene(s); }); - if(!(lightingScenesDevice is ILightingScenesDynamic lightingScenesDynamic)) + if (!(lightingScenesDevice is ILightingScenesDynamic lightingScenesDynamic)) return; lightingScenesDynamic.LightingScenesUpdated += (s, e) => SendFullStatus(); } - private void SendFullStatus() + private void SendFullStatus(string id = null) { var state = new LightingBaseStateMessage { @@ -58,7 +60,7 @@ namespace PepperDash.Essentials.AppServer.Messengers CurrentLightingScene = lightingScenesDevice.CurrentLightingScene }; - PostStatusMessage(state); + PostStatusMessage(state, id); } } @@ -67,16 +69,17 @@ namespace PepperDash.Essentials.AppServer.Messengers /// public class LightingBaseStateMessage : DeviceStateMessageBase { - [JsonProperty("scenes", NullValueHandling = NullValueHandling.Ignore)] /// /// Gets or sets the Scenes /// + [JsonProperty("scenes", NullValueHandling = NullValueHandling.Ignore)] public List Scenes { get; set; } - [JsonProperty("currentLightingScene", NullValueHandling = NullValueHandling.Ignore)] + /// /// Gets or sets the CurrentLightingScene /// + [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 index 491ebb67..ad8e67a3 100644 --- a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/MessengerBase.cs +++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/MessengerBase.cs @@ -1,12 +1,12 @@ -using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using Newtonsoft.Json; using Newtonsoft.Json.Linq; using PepperDash.Core; using PepperDash.Core.Logging; using PepperDash.Essentials.Core; using PepperDash.Essentials.Core.DeviceTypeInterfaces; -using System; -using System.Collections.Generic; -using System.Linq; namespace PepperDash.Essentials.AppServer.Messengers { @@ -159,13 +159,13 @@ namespace PepperDash.Essentials.AppServer.Messengers message.Name = _device.Name; - var token = JToken.FromObject(message); - + var token = JToken.FromObject(message); + PostStatusMessage(token, MessagePath, clientId); } catch (Exception ex) { - this.LogError(ex, "Exception posting status message for {messagePath} to {clientId}", MessagePath, clientId ?? "all clients"); + this.LogError(ex, "Exception posting status message for {messagePath} to {clientId}", MessagePath, clientId ?? "all clients"); } } @@ -188,7 +188,7 @@ namespace PepperDash.Essentials.AppServer.Messengers } catch (Exception ex) { - this.LogError(ex, "Exception posting status message for {type} to {clientId}", type, clientId ?? "all clients"); + this.LogError(ex, "Exception posting status message for {type} to {clientId}", type, clientId ?? "all clients"); } } diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/PressAndHoldHandler.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/PressAndHoldHandler.cs index 46728dba..b076566b 100644 --- a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/PressAndHoldHandler.cs +++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/PressAndHoldHandler.cs @@ -1,11 +1,14 @@ -using Crestron.SimplSharp; +using System; +using System.Collections.Generic; +using Crestron.SimplSharp; using Newtonsoft.Json.Linq; using PepperDash.Core; -using System; -using System.Collections.Generic; namespace PepperDash.Essentials.AppServer.Messengers { + /// + /// Handler for press/hold/release messages + /// public static class PressAndHoldHandler { private const long ButtonHeartbeatInterval = 1000; @@ -26,21 +29,21 @@ namespace PepperDash.Essentials.AppServer.Messengers private static void AddTimer(string deviceKey, Action action) { - Debug.LogMessage(Serilog.Events.LogEventLevel.Debug, "Attempting to add timer for {deviceKey}", deviceKey); + Debug.LogDebug("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); + Debug.LogDebug("Timer for {deviceKey} already exists", deviceKey); return; } - Debug.LogMessage(Serilog.Events.LogEventLevel.Debug, "Adding timer for {deviceKey} with due time {dueTime}", deviceKey, ButtonHeartbeatInterval); + Debug.LogDebug("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); + Debug.LogDebug("Timer expired for {deviceKey}", deviceKey); action(false); @@ -52,30 +55,30 @@ namespace PepperDash.Essentials.AppServer.Messengers private static void ResetTimer(string deviceKey, Action action) { - Debug.LogMessage(Serilog.Events.LogEventLevel.Debug, "Attempting to reset timer for {deviceKey}", deviceKey); + Debug.LogDebug("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); + Debug.LogDebug("Timer for {deviceKey} not found", deviceKey); return; } - Debug.LogMessage(Serilog.Events.LogEventLevel.Debug, "Resetting timer for {deviceKey} with due time {dueTime}", deviceKey, ButtonHeartbeatInterval); + Debug.LogDebug("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); + Debug.LogDebug("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); + Debug.LogDebug("Timer for {deviceKey} not found", deviceKey); return; } - Debug.LogMessage(Serilog.Events.LogEventLevel.Debug, "Stopping timer for {deviceKey} with due time {dueTime}", deviceKey, ButtonHeartbeatInterval); + Debug.LogDebug("Stopping timer for {deviceKey} with due time {dueTime}", deviceKey, ButtonHeartbeatInterval); action(false); cancelTimer.Stop(); @@ -84,15 +87,15 @@ namespace PepperDash.Essentials.AppServer.Messengers public static Action> GetPressAndHoldHandler(string value) { - Debug.LogMessage(Serilog.Events.LogEventLevel.Debug, "Getting press and hold handler for {value}", value); + Debug.LogDebug("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); + Debug.LogDebug("Press and hold handler for {value} not found", value); return null; } - Debug.LogMessage(Serilog.Events.LogEventLevel.Debug, "Got handler for {value}", value); + Debug.LogDebug("Got handler for {value}", value); return handler; } @@ -104,7 +107,7 @@ namespace PepperDash.Essentials.AppServer.Messengers { var msg = content.ToObject>(); - Debug.LogMessage(Serilog.Events.LogEventLevel.Debug, "Handling press and hold message of {type} for {deviceKey}", msg.Value, deviceKey); + Debug.LogDebug("Handling press and hold message of {type} for {deviceKey}", msg.Value, deviceKey); var timerHandler = GetPressAndHoldHandler(msg.Value); diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/RoomEventScheduleMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/RoomEventScheduleMessenger.cs index f5c019d1..2e10d0f3 100644 --- a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/RoomEventScheduleMessenger.cs +++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/RoomEventScheduleMessenger.cs @@ -1,10 +1,10 @@ -using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using Newtonsoft.Json; using PepperDash.Core; using PepperDash.Core.Logging; using PepperDash.Essentials.Core; using PepperDash.Essentials.Room.Config; -using System; -using System.Collections.Generic; namespace PepperDash.Essentials.AppServer.Messengers { @@ -31,7 +31,14 @@ namespace PepperDash.Essentials.AppServer.Messengers { var events = _room.GetScheduledEvents(); - SendFullStatus(events); + SendFullStatus(events, id); + }); + + AddAction("/scheduledEventsStatus", (id, content) => + { + var events = _room.GetScheduledEvents(); + + SendFullStatus(events, id); }); _room.ScheduledEventsChanged += (sender, args) => SendFullStatus(args.ScheduledEvents); @@ -55,11 +62,11 @@ namespace PepperDash.Essentials.AppServer.Messengers } catch (Exception ex) { - this.LogException(ex,"Exception saving event"); + this.LogException(ex, "Exception saving event"); } } - private void SendFullStatus(List events) + private void SendFullStatus(List events, string id = null) { var message = new RoomEventScheduleStateMessage @@ -67,7 +74,7 @@ namespace PepperDash.Essentials.AppServer.Messengers ScheduleEvents = events, }; - PostStatusMessage(message); + PostStatusMessage(message, id); } } diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/ShadeBaseMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/ShadeBaseMessenger.cs index 8a071409..5492cc2b 100644 --- a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/ShadeBaseMessenger.cs +++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/ShadeBaseMessenger.cs @@ -1,7 +1,7 @@ -using Newtonsoft.Json; +using System; +using Newtonsoft.Json; using PepperDash.Core; using PepperDash.Essentials.Core.Shades; -using System; namespace PepperDash.Essentials.AppServer.Messengers { @@ -22,7 +22,8 @@ namespace PepperDash.Essentials.AppServer.Messengers { base.RegisterActions(); - AddAction("/fullStatus", (id, content) => SendFullStatus()); + AddAction("/fullStatus", (id, content) => SendFullStatus(id)); + AddAction("/shadesStatus", (id, content) => SendFullStatus(id)); AddAction("/shadeUp", (id, content) => { @@ -75,7 +76,7 @@ namespace PepperDash.Essentials.AppServer.Messengers } - private void SendFullStatus() + private void SendFullStatus(string id = null) { var state = new ShadeBaseStateMessage(); @@ -85,7 +86,7 @@ namespace PepperDash.Essentials.AppServer.Messengers state.IsClosed = feedbackDevice.ShadeIsClosedFeedback.BoolValue; } - PostStatusMessage(state); + PostStatusMessage(state, id); } } @@ -94,10 +95,11 @@ namespace PepperDash.Essentials.AppServer.Messengers /// public class ShadeBaseStateMessage : DeviceStateMessageBase { - [JsonProperty("middleButtonLabel", NullValueHandling = NullValueHandling.Ignore)] + /// /// Gets or sets the MiddleButtonLabel /// + [JsonProperty("middleButtonLabel", NullValueHandling = NullValueHandling.Ignore)] public string MiddleButtonLabel { get; set; } [JsonProperty("isOpen", NullValueHandling = NullValueHandling.Ignore)] diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/SystemMonitorMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/SystemMonitorMessenger.cs index 0080153c..fcc02930 100644 --- a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/SystemMonitorMessenger.cs +++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/SystemMonitorMessenger.cs @@ -1,10 +1,10 @@ -using Crestron.SimplSharp; +using System; +using System.Threading.Tasks; +using Crestron.SimplSharp; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using PepperDash.Core; using PepperDash.Essentials.Core.Monitoring; -using System; -using System.Threading.Tasks; namespace PepperDash.Essentials.AppServer.Messengers { @@ -56,36 +56,37 @@ namespace PepperDash.Essentials.AppServer.Messengers SendSystemMonitorStatusMessage(); } - private void SendFullStatusMessage() + private void SendFullStatusMessage(string id = null) { - SendSystemMonitorStatusMessage(); + SendSystemMonitorStatusMessage(id); foreach (var p in systemMonitor.ProgramStatusFeedbackCollection) { - PostStatusMessage(JToken.FromObject(p.Value.ProgramInfo)); + PostStatusMessage(JToken.FromObject(p.Value.ProgramInfo), id); } } - private void SendSystemMonitorStatusMessage() + private void SendSystemMonitorStatusMessage(string id = null) { // This takes a while, launch a new thread - + Task.Run(() => PostStatusMessage(JToken.FromObject(new SystemMonitorStateMessage { - TimeZone = systemMonitor.TimeZoneFeedback.IntValue, TimeZoneName = systemMonitor.TimeZoneTextFeedback.StringValue, IoControllerVersion = systemMonitor.IoControllerVersionFeedback.StringValue, SnmpVersion = systemMonitor.SnmpVersionFeedback.StringValue, BacnetVersion = systemMonitor.BaCnetAppVersionFeedback.StringValue, ControllerVersion = systemMonitor.ControllerVersionFeedback.StringValue - }) + }), id )); } protected override void RegisterActions() { - AddAction("/fullStatus", (id, content) => SendFullStatusMessage()); + AddAction("/fullStatus", (id, content) => SendFullStatusMessage(id)); + + AddAction("/systemStatus", (id, content) => SendFullStatusMessage(id)); } } @@ -94,40 +95,45 @@ namespace PepperDash.Essentials.AppServer.Messengers /// public class SystemMonitorStateMessage { - [JsonProperty("timeZone", NullValueHandling = NullValueHandling.Ignore)] /// /// Gets or sets the TimeZone /// + [JsonProperty("timeZone", NullValueHandling = NullValueHandling.Ignore)] public int TimeZone { get; set; } - [JsonProperty("timeZone", NullValueHandling = NullValueHandling.Ignore)] + /// /// Gets or sets the TimeZoneName /// + [JsonProperty("timeZone", NullValueHandling = NullValueHandling.Ignore)] public string TimeZoneName { get; set; } - [JsonProperty("timeZone", NullValueHandling = NullValueHandling.Ignore)] + /// /// Gets or sets the IoControllerVersion /// + [JsonProperty("timeZone", NullValueHandling = NullValueHandling.Ignore)] public string IoControllerVersion { get; set; } - [JsonProperty("timeZone", NullValueHandling = NullValueHandling.Ignore)] + /// /// Gets or sets the SnmpVersion /// + [JsonProperty("timeZone", NullValueHandling = NullValueHandling.Ignore)] public string SnmpVersion { get; set; } - [JsonProperty("timeZone", NullValueHandling = NullValueHandling.Ignore)] + /// /// Gets or sets the BacnetVersion /// + [JsonProperty("timeZone", NullValueHandling = NullValueHandling.Ignore)] public string BacnetVersion { get; set; } - [JsonProperty("timeZone", NullValueHandling = NullValueHandling.Ignore)] + /// /// Gets or sets the ControllerVersion /// + [JsonProperty("timeZone", NullValueHandling = NullValueHandling.Ignore)] public string ControllerVersion { get; set; } } } \ No newline at end of file diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/TwoWayDisplayBaseMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/TwoWayDisplayBaseMessenger.cs index 9bc4c12b..5147da4d 100644 --- a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/TwoWayDisplayBaseMessenger.cs +++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/TwoWayDisplayBaseMessenger.cs @@ -23,7 +23,7 @@ namespace PepperDash.Essentials.AppServer.Messengers /// /// SendFullStatus method /// - public void SendFullStatus() + public void SendFullStatus(string id = null) { var messageObj = new TwoWayDisplayBaseStateMessage { @@ -31,16 +31,17 @@ namespace PepperDash.Essentials.AppServer.Messengers CurrentInput = _display.CurrentInputFeedback.StringValue }; - PostStatusMessage(messageObj); + PostStatusMessage(messageObj, id); } protected override void RegisterActions() { base.RegisterActions(); - AddAction("/fullStatus", (id, content) => SendFullStatus()); + AddAction("/fullStatus", (id, content) => SendFullStatus(id)); + + AddAction("/displayStatus", (id, content) => SendFullStatus(id)); - //_display.PowerIsOnFeedback.OutputChange += PowerIsOnFeedbackOnOutputChange; _display.CurrentInputFeedback.OutputChange += CurrentInputFeedbackOnOutputChange; _display.IsCoolingDownFeedback.OutputChange += IsCoolingFeedbackOnOutputChange; _display.IsWarmingUpFeedback.OutputChange += IsWarmingFeedbackOnOutputChange; @@ -55,16 +56,6 @@ namespace PepperDash.Essentials.AppServer.Messengers ); } - - //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 @@ -96,10 +87,11 @@ namespace PepperDash.Essentials.AppServer.Messengers //[JsonProperty("powerState", NullValueHandling = NullValueHandling.Ignore)] //public bool? PowerState { get; set; } - [JsonProperty("currentInput", NullValueHandling = NullValueHandling.Ignore)] + /// /// Gets or sets the CurrentInput /// + [JsonProperty("currentInput", NullValueHandling = NullValueHandling.Ignore)] public string CurrentInput { get; set; } } } \ No newline at end of file diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/VideoCodecBaseMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/VideoCodecBaseMessenger.cs index 58300f55..071eaf27 100644 --- a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/VideoCodecBaseMessenger.cs +++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/VideoCodecBaseMessenger.cs @@ -1,4 +1,8 @@ -using Crestron.SimplSharp; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Crestron.SimplSharp; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using PepperDash.Core; @@ -9,9 +13,6 @@ using PepperDash.Essentials.Devices.Common.Cameras; using PepperDash.Essentials.Devices.Common.Codec; using PepperDash.Essentials.Devices.Common.VideoCodec; using PepperDash.Essentials.Devices.Common.VideoCodec.Interfaces; -using System; -using System.Collections.Generic; -using System.Linq; namespace PepperDash.Essentials.AppServer.Messengers { @@ -151,7 +152,8 @@ namespace PepperDash.Essentials.AppServer.Messengers PostStatusMessage(state); SendFullStatus(); - } catch (Exception ex) + } + catch (Exception ex) { this.LogError(ex, "Error sending codec ready status"); } @@ -169,7 +171,8 @@ namespace PepperDash.Essentials.AppServer.Messengers AddAction("/isReady", (id, content) => SendIsReady()); - AddAction("/fullStatus", (id, content) => SendFullStatus()); + AddAction("/fullStatus", (id, content) => SendFullStatus(id)); + AddAction("/codecStatus", (id, content) => SendFullStatus(id)); AddAction("/dial", (id, content) => { @@ -369,7 +372,8 @@ namespace PepperDash.Essentials.AppServer.Messengers }; PostStatusMessage(state); - } catch (Exception ex) + } + catch (Exception ex) { this.LogError(ex, "Error posting sharing source"); } @@ -385,7 +389,8 @@ namespace PepperDash.Essentials.AppServer.Messengers }; PostStatusMessage(state); - } catch (Exception ex) + } + catch (Exception ex) { this.LogError(ex, "Error posting sharing content"); } @@ -435,7 +440,8 @@ namespace PepperDash.Essentials.AppServer.Messengers { MapCameraActions(); PostSelectedCamera(); - } catch(Exception ex) + } + catch (Exception ex) { this.LogError(ex, "Exception handling camera selected event"); } @@ -780,14 +786,14 @@ namespace PepperDash.Essentials.AppServer.Messengers } } - protected virtual void SendFullStatus() + protected virtual void SendFullStatus(string id = null) { if (!Codec.IsReady) { return; } - CrestronInvoke.BeginInvoke((o) => PostStatusMessage(GetStatus())); + Task.Run(() => PostStatusMessage(GetStatus(), id)); } private void PostReceivingContent(bool receivingContent) @@ -800,7 +806,8 @@ namespace PepperDash.Essentials.AppServer.Messengers }; PostStatusMessage(state); - } catch(Exception ex) + } + catch (Exception ex) { this.LogError(ex, "Error posting receiving content"); } @@ -949,22 +956,25 @@ namespace PepperDash.Essentials.AppServer.Messengers [JsonProperty("cameraSupportsOffMode", NullValueHandling = NullValueHandling.Ignore)] public bool? CameraSupportsOffMode { get; set; } - [JsonProperty("currentDialString", NullValueHandling = NullValueHandling.Ignore)] + /// /// Gets or sets the CurrentDialString /// + [JsonProperty("currentDialString", NullValueHandling = NullValueHandling.Ignore)] public string CurrentDialString { get; set; } - [JsonProperty("currentDirectory", NullValueHandling = NullValueHandling.Ignore)] + /// /// Gets or sets the CurrentDirectory /// + [JsonProperty("currentDirectory", NullValueHandling = NullValueHandling.Ignore)] public CodecDirectory CurrentDirectory { get; set; } - [JsonProperty("directorySelectedFolderName", NullValueHandling = NullValueHandling.Ignore)] + /// /// Gets or sets the DirectorySelectedFolderName /// + [JsonProperty("directorySelectedFolderName", NullValueHandling = NullValueHandling.Ignore)] public string DirectorySelectedFolderName { get; set; } [JsonProperty("hasCameras", NullValueHandling = NullValueHandling.Ignore)] @@ -985,10 +995,11 @@ namespace PepperDash.Essentials.AppServer.Messengers [JsonProperty("initialPhonebookSyncComplete", NullValueHandling = NullValueHandling.Ignore)] public bool? InitialPhonebookSyncComplete { get; set; } - [JsonProperty("info", NullValueHandling = NullValueHandling.Ignore)] + /// /// Gets or sets the Info /// + [JsonProperty("info", NullValueHandling = NullValueHandling.Ignore)] public VideoCodecInfo Info { get; set; } [JsonProperty("isInCall", NullValueHandling = NullValueHandling.Ignore)] @@ -1000,16 +1011,18 @@ namespace PepperDash.Essentials.AppServer.Messengers [JsonProperty("isZoomRoom", NullValueHandling = NullValueHandling.Ignore)] public bool? IsZoomRoom { get; set; } - [JsonProperty("meetingInfo", NullValueHandling = NullValueHandling.Ignore)] + /// /// Gets or sets the MeetingInfo /// + [JsonProperty("meetingInfo", NullValueHandling = NullValueHandling.Ignore)] public MeetingInfo MeetingInfo { get; set; } - [JsonProperty("presets", NullValueHandling = NullValueHandling.Ignore)] + /// /// Gets or sets the Presets /// + [JsonProperty("presets", NullValueHandling = NullValueHandling.Ignore)] public List Presets { get; set; } [JsonProperty("privacyModeIsOn", NullValueHandling = NullValueHandling.Ignore)] @@ -1024,10 +1037,11 @@ namespace PepperDash.Essentials.AppServer.Messengers [JsonProperty("sharingContentIsOn", NullValueHandling = NullValueHandling.Ignore)] public bool? SharingContentIsOn { get; set; } - [JsonProperty("sharingSource", NullValueHandling = NullValueHandling.Ignore)] + /// /// Gets or sets the SharingSource /// + [JsonProperty("sharingSource", NullValueHandling = NullValueHandling.Ignore)] public string SharingSource { get; set; } [JsonProperty("showCamerasWhenNotInCall", NullValueHandling = NullValueHandling.Ignore)] @@ -1057,23 +1071,26 @@ namespace PepperDash.Essentials.AppServer.Messengers [JsonProperty("cameraOffSupported", NullValueHandling = NullValueHandling.Ignore)] public bool? CameraOffIsSupported { get; set; } - [JsonProperty("cameraMode", NullValueHandling = NullValueHandling.Ignore)] + /// /// Gets or sets the CameraMode /// + [JsonProperty("cameraMode", NullValueHandling = NullValueHandling.Ignore)] public string CameraMode { get; set; } - [JsonProperty("cameraList", NullValueHandling = NullValueHandling.Ignore)] + /// /// Gets or sets the Cameras /// + [JsonProperty("cameraList", NullValueHandling = NullValueHandling.Ignore)] public List Cameras { get; set; } - [JsonProperty("selectedCamera", NullValueHandling = NullValueHandling.Ignore)] + /// /// Gets or sets the SelectedCamera /// - public Camera SelectedCamera { get; set; } + [JsonProperty("selectedCamera", NullValueHandling = NullValueHandling.Ignore)] + public Camera SelectedCamera { get; set; } } /// @@ -1081,25 +1098,28 @@ namespace PepperDash.Essentials.AppServer.Messengers /// public class Camera { - [JsonProperty("key", NullValueHandling = NullValueHandling.Ignore)] + /// /// Gets or sets the Key /// + [JsonProperty("key", NullValueHandling = NullValueHandling.Ignore)] public string Key { get; set; } - [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)] + /// /// Gets or sets the Name /// + [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)] public string Name { get; set; } [JsonProperty("isFarEnd", NullValueHandling = NullValueHandling.Ignore)] public bool? IsFarEnd { get; set; } - [JsonProperty("capabilities", NullValueHandling = NullValueHandling.Ignore)] + /// /// Gets or sets the Capabilities /// + [JsonProperty("capabilities", NullValueHandling = NullValueHandling.Ignore)] public CameraCapabilities Capabilities { get; set; } } @@ -1135,27 +1155,31 @@ namespace PepperDash.Essentials.AppServer.Messengers /// public class PasswordPromptEventMessage : VideoCodecBaseEventMessage { - [JsonProperty("message", NullValueHandling = NullValueHandling.Ignore)] /// /// Gets or sets the Message /// + [JsonProperty("message", NullValueHandling = NullValueHandling.Ignore)] public string Message { get; set; } - [JsonProperty("lastAttemptWasIncorrect", NullValueHandling = NullValueHandling.Ignore)] + + /// /// Gets or sets the LastAttemptWasIncorrect /// + [JsonProperty("lastAttemptWasIncorrect", NullValueHandling = NullValueHandling.Ignore)] public bool LastAttemptWasIncorrect { get; set; } - [JsonProperty("loginAttemptFailed", NullValueHandling = NullValueHandling.Ignore)] + /// /// Gets or sets the LoginAttemptFailed /// + [JsonProperty("loginAttemptFailed", NullValueHandling = NullValueHandling.Ignore)] public bool LoginAttemptFailed { get; set; } - [JsonProperty("loginAttemptCancelled", NullValueHandling = NullValueHandling.Ignore)] + /// /// Gets or sets the LoginAttemptCancelled /// + [JsonProperty("loginAttemptCancelled", NullValueHandling = NullValueHandling.Ignore)] public bool LoginAttemptCancelled { get; set; } } } \ No newline at end of file diff --git a/src/PepperDash.Essentials.MobileControl/AuthorizationResponse.cs b/src/PepperDash.Essentials.MobileControl/AuthorizationResponse.cs index 69684f47..7ba130bc 100644 --- a/src/PepperDash.Essentials.MobileControl/AuthorizationResponse.cs +++ b/src/PepperDash.Essentials.MobileControl/AuthorizationResponse.cs @@ -7,16 +7,18 @@ namespace PepperDash.Essentials /// public class AuthorizationResponse { - [JsonProperty("authorized")] + /// /// Gets or sets the Authorized /// + [JsonProperty("authorized")] public bool Authorized { get; set; } - [JsonProperty("reason", NullValueHandling = NullValueHandling.Ignore)] + /// /// Gets or sets the Reason /// + [JsonProperty("reason", NullValueHandling = NullValueHandling.Ignore)] public string Reason { get; set; } = null; } @@ -25,10 +27,11 @@ namespace PepperDash.Essentials /// public class AuthorizationRequest { - [JsonProperty("grantCode")] + /// /// Gets or sets the GrantCode /// + [JsonProperty("grantCode")] public string GrantCode { get; set; } } } diff --git a/src/PepperDash.Essentials.MobileControl/MobileControlEssentialsConfig.cs b/src/PepperDash.Essentials.MobileControl/MobileControlEssentialsConfig.cs index d27a8449..7183ec84 100644 --- a/src/PepperDash.Essentials.MobileControl/MobileControlEssentialsConfig.cs +++ b/src/PepperDash.Essentials.MobileControl/MobileControlEssentialsConfig.cs @@ -1,6 +1,6 @@ -using Newtonsoft.Json; +using System.Collections.Generic; +using Newtonsoft.Json; using PepperDash.Essentials.Core.Config; -using System.Collections.Generic; namespace PepperDash.Essentials @@ -39,10 +39,11 @@ namespace PepperDash.Essentials /// public class MobileControlRuntimeInfo { - [JsonProperty("pluginVersion")] + /// /// Gets or sets the PluginVersion /// + [JsonProperty("pluginVersion")] public string PluginVersion { get; set; } [JsonProperty("essentialsVersion")] @@ -51,10 +52,11 @@ namespace PepperDash.Essentials [JsonProperty("pepperDashCoreVersion")] public string PepperDashCoreVersion { get; set; } - [JsonProperty("essentialsPlugins")] + /// /// Gets or sets the EssentialsPlugins /// + [JsonProperty("essentialsPlugins")] public List EssentialsPlugins { get; set; } } } \ No newline at end of file diff --git a/src/PepperDash.Essentials.MobileControl/MobileControlSystemController.cs b/src/PepperDash.Essentials.MobileControl/MobileControlSystemController.cs index 49e71010..640cbecf 100644 --- a/src/PepperDash.Essentials.MobileControl/MobileControlSystemController.cs +++ b/src/PepperDash.Essentials.MobileControl/MobileControlSystemController.cs @@ -244,7 +244,7 @@ namespace PepperDash.Essentials CrestronEnvironment.ProgramStatusEventHandler += CrestronEnvironment_ProgramStatusEventHandler; - ApiOnlineAndAuthorized = new BoolFeedback(() => + ApiOnlineAndAuthorized = new BoolFeedback("apiOnlineAndAuthorized", () => { if (_wsClient2 == null) return false; @@ -1484,7 +1484,7 @@ namespace PepperDash.Essentials /// /// Adds an action to the dictionary /// - /// The path of the API command + /// The messenger for the API command /// The action to be triggered by the commmand public void AddAction(T messenger, Action action) where T : IMobileControlMessenger diff --git a/src/PepperDash.Essentials.MobileControl/RoomBridges/MobileControlEssentialsRoomBridge.cs b/src/PepperDash.Essentials.MobileControl/RoomBridges/MobileControlEssentialsRoomBridge.cs index a3485aec..f0463f27 100644 --- a/src/PepperDash.Essentials.MobileControl/RoomBridges/MobileControlEssentialsRoomBridge.cs +++ b/src/PepperDash.Essentials.MobileControl/RoomBridges/MobileControlEssentialsRoomBridge.cs @@ -1,4 +1,7 @@ -using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using Newtonsoft.Json; using Newtonsoft.Json.Converters; using Newtonsoft.Json.Linq; using PepperDash.Core; @@ -17,9 +20,6 @@ using PepperDash.Essentials.Devices.Common.Room; using PepperDash.Essentials.Devices.Common.VideoCodec; using PepperDash.Essentials.Room.Config; using PepperDash.Essentials.WebSocketServer; -using System; -using System.Collections.Generic; -using System.Linq; using IShades = PepperDash.Essentials.Core.Shades.IShades; using ShadeBase = PepperDash.Essentials.Devices.Common.Shades.ShadeBase; @@ -485,6 +485,7 @@ namespace PepperDash.Essentials.RoomBridges /// Sends the full status of the room to the server /// /// + /// private void SendFullStatusForClientId(string id, IEssentialsRoom room) { //Parent.SendMessageObject(GetFullStatus(room)); @@ -554,6 +555,7 @@ namespace PepperDash.Essentials.RoomBridges /// /// Determines the configuration of the room and the details about the devices associated with the room + /// /// /// private RoomConfiguration GetRoomConfiguration(IEssentialsRoom room) @@ -798,31 +800,38 @@ namespace PepperDash.Essentials.RoomBridges /// public class RoomStateMessage : DeviceStateMessageBase { - [JsonProperty("configuration", NullValueHandling = NullValueHandling.Ignore)] + /// /// Gets or sets the Configuration /// + [JsonProperty("configuration", NullValueHandling = NullValueHandling.Ignore)] public RoomConfiguration Configuration { get; set; } [JsonProperty("activityMode", NullValueHandling = NullValueHandling.Ignore)] public int? ActivityMode { get; set; } + [JsonProperty("advancedSharingActive", NullValueHandling = NullValueHandling.Ignore)] public bool? AdvancedSharingActive { get; set; } + [JsonProperty("isOn", NullValueHandling = NullValueHandling.Ignore)] public bool? IsOn { get; set; } + [JsonProperty("isWarmingUp", NullValueHandling = NullValueHandling.Ignore)] public bool? IsWarmingUp { get; set; } + [JsonProperty("isCoolingDown", NullValueHandling = NullValueHandling.Ignore)] public bool? IsCoolingDown { get; set; } - [JsonProperty("selectedSourceKey", NullValueHandling = NullValueHandling.Ignore)] + /// /// Gets or sets the SelectedSourceKey /// + [JsonProperty("selectedSourceKey", NullValueHandling = NullValueHandling.Ignore)] public string SelectedSourceKey { get; set; } - [JsonProperty("share", NullValueHandling = NullValueHandling.Ignore)] + /// /// Gets or sets the Share /// + [JsonProperty("share", NullValueHandling = NullValueHandling.Ignore)] public ShareState Share { get; set; } [JsonProperty("volumes", NullValueHandling = NullValueHandling.Ignore)] @@ -837,13 +846,16 @@ namespace PepperDash.Essentials.RoomBridges /// public class ShareState { - [JsonProperty("currentShareText", NullValueHandling = NullValueHandling.Ignore)] + /// /// Gets or sets the CurrentShareText /// + [JsonProperty("currentShareText", NullValueHandling = NullValueHandling.Ignore)] public string CurrentShareText { get; set; } + [JsonProperty("enabled", NullValueHandling = NullValueHandling.Ignore)] public bool? Enabled { get; set; } + [JsonProperty("isSharing", NullValueHandling = NullValueHandling.Ignore)] public bool? IsSharing { get; set; } } @@ -853,131 +865,156 @@ namespace PepperDash.Essentials.RoomBridges /// public class RoomConfiguration { - //[JsonProperty("shutdownPromptSeconds", NullValueHandling = NullValueHandling.Ignore)] - //public int? ShutdownPromptSeconds { get; set; } - [JsonProperty("hasVideoConferencing", NullValueHandling = NullValueHandling.Ignore)] public bool? HasVideoConferencing { get; set; } + [JsonProperty("videoCodecIsZoomRoom", NullValueHandling = NullValueHandling.Ignore)] public bool? VideoCodecIsZoomRoom { get; set; } + [JsonProperty("hasAudioConferencing", NullValueHandling = NullValueHandling.Ignore)] public bool? HasAudioConferencing { get; set; } + [JsonProperty("hasEnvironmentalControls", NullValueHandling = NullValueHandling.Ignore)] public bool? HasEnvironmentalControls { get; set; } + [JsonProperty("hasCameraControls", NullValueHandling = NullValueHandling.Ignore)] public bool? HasCameraControls { get; set; } + [JsonProperty("hasSetTopBoxControls", NullValueHandling = NullValueHandling.Ignore)] public bool? HasSetTopBoxControls { get; set; } + [JsonProperty("hasRoutingControls", NullValueHandling = NullValueHandling.Ignore)] public bool? HasRoutingControls { get; set; } - [JsonProperty("touchpanelKeys", NullValueHandling = NullValueHandling.Ignore)] /// /// Gets or sets the TouchpanelKeys /// + [JsonProperty("touchpanelKeys", NullValueHandling = NullValueHandling.Ignore)] public List TouchpanelKeys { get; set; } - [JsonProperty("zoomRoomControllerKey", NullValueHandling = NullValueHandling.Ignore)] + /// /// Gets or sets the ZoomRoomControllerKey /// + [JsonProperty("zoomRoomControllerKey", NullValueHandling = NullValueHandling.Ignore)] public string ZoomRoomControllerKey { get; set; } - [JsonProperty("ciscoNavigatorKey", NullValueHandling = NullValueHandling.Ignore)] + /// /// Gets or sets the CiscoNavigatorKey /// + [JsonProperty("ciscoNavigatorKey", NullValueHandling = NullValueHandling.Ignore)] public string CiscoNavigatorKey { get; set; } - [JsonProperty("videoCodecKey", NullValueHandling = NullValueHandling.Ignore)] + /// /// Gets or sets the VideoCodecKey /// + [JsonProperty("videoCodecKey", NullValueHandling = NullValueHandling.Ignore)] public string VideoCodecKey { get; set; } - [JsonProperty("audioCodecKey", NullValueHandling = NullValueHandling.Ignore)] + + /// /// Gets or sets the AudioCodecKey /// + [JsonProperty("audioCodecKey", NullValueHandling = NullValueHandling.Ignore)] public string AudioCodecKey { get; set; } - [JsonProperty("matrixRoutingKey", NullValueHandling = NullValueHandling.Ignore)] + + /// /// Gets or sets the MatrixRoutingKey /// + [JsonProperty("matrixRoutingKey", NullValueHandling = NullValueHandling.Ignore)] public string MatrixRoutingKey { get; set; } - [JsonProperty("endpointKeys", NullValueHandling = NullValueHandling.Ignore)] + + /// /// Gets or sets the EndpointKeys /// + [JsonProperty("endpointKeys", NullValueHandling = NullValueHandling.Ignore)] public List EndpointKeys { get; set; } - [JsonProperty("accessoryDeviceKeys", NullValueHandling = NullValueHandling.Ignore)] + /// /// Gets or sets the AccessoryDeviceKeys /// + [JsonProperty("accessoryDeviceKeys", NullValueHandling = NullValueHandling.Ignore)] public List AccessoryDeviceKeys { get; set; } - [JsonProperty("defaultDisplayKey", NullValueHandling = NullValueHandling.Ignore)] + /// /// Gets or sets the DefaultDisplayKey /// + [JsonProperty("defaultDisplayKey", NullValueHandling = NullValueHandling.Ignore)] public string DefaultDisplayKey { get; set; } + [JsonProperty("destinations", NullValueHandling = NullValueHandling.Ignore)] public Dictionary Destinations { get; set; } - [JsonProperty("environmentalDevices", NullValueHandling = NullValueHandling.Ignore)] + + /// /// Gets or sets the EnvironmentalDevices /// + [JsonProperty("environmentalDevices", NullValueHandling = NullValueHandling.Ignore)] public List EnvironmentalDevices { get; set; } + [JsonProperty("sourceList", NullValueHandling = NullValueHandling.Ignore)] public Dictionary SourceList { get; set; } [JsonProperty("destinationList", NullValueHandling = NullValueHandling.Ignore)] public Dictionary DestinationList { get; set; } - [JsonProperty("audioControlPointList", NullValueHandling = NullValueHandling.Ignore)] + /// /// Gets or sets the AudioControlPointList /// + [JsonProperty("audioControlPointList", NullValueHandling = NullValueHandling.Ignore)] public AudioControlPointListItem AudioControlPointList { get; set; } [JsonProperty("cameraList", NullValueHandling = NullValueHandling.Ignore)] public Dictionary CameraList { get; set; } - [JsonProperty("defaultPresentationSourceKey", NullValueHandling = NullValueHandling.Ignore)] + /// /// Gets or sets the DefaultPresentationSourceKey /// + [JsonProperty("defaultPresentationSourceKey", NullValueHandling = NullValueHandling.Ignore)] public string DefaultPresentationSourceKey { get; set; } - [JsonProperty("helpMessage", NullValueHandling = NullValueHandling.Ignore)] + /// /// Gets or sets the HelpMessage /// + [JsonProperty("helpMessage", NullValueHandling = NullValueHandling.Ignore)] public string HelpMessage { get; set; } - [JsonProperty("techPassword", NullValueHandling = NullValueHandling.Ignore)] + /// /// Gets or sets the TechPassword /// + [JsonProperty("techPassword", NullValueHandling = NullValueHandling.Ignore)] public string TechPassword { get; set; } - [JsonProperty("uiBehavior", NullValueHandling = NullValueHandling.Ignore)] + /// /// Gets or sets the UiBehavior /// + [JsonProperty("uiBehavior", NullValueHandling = NullValueHandling.Ignore)] public EssentialsRoomUiBehaviorConfig UiBehavior { get; set; } [JsonProperty("supportsAdvancedSharing", NullValueHandling = NullValueHandling.Ignore)] public bool? SupportsAdvancedSharing { get; set; } + [JsonProperty("userCanChangeShareMode", NullValueHandling = NullValueHandling.Ignore)] public bool? UserCanChangeShareMode { get; set; } - [JsonProperty("roomCombinerKey", NullValueHandling = NullValueHandling.Ignore)] + /// /// Gets or sets the RoomCombinerKey /// + [JsonProperty("roomCombinerKey", NullValueHandling = NullValueHandling.Ignore)] public string RoomCombinerKey { get; set; } public RoomConfiguration() @@ -994,17 +1031,19 @@ namespace PepperDash.Essentials.RoomBridges /// public class EnvironmentalDeviceConfiguration { - [JsonProperty("deviceKey", NullValueHandling = NullValueHandling.Ignore)] + /// /// Gets or sets the DeviceKey /// + [JsonProperty("deviceKey", NullValueHandling = NullValueHandling.Ignore)] public string DeviceKey { get; private set; } - [JsonConverter(typeof(StringEnumConverter))] - [JsonProperty("deviceType", NullValueHandling = NullValueHandling.Ignore)] + /// /// Gets or sets the DeviceType /// + [JsonConverter(typeof(StringEnumConverter))] + [JsonProperty("deviceType", NullValueHandling = NullValueHandling.Ignore)] public eEnvironmentalDeviceTypes DeviceType { get; private set; } public EnvironmentalDeviceConfiguration(string key, eEnvironmentalDeviceTypes type) @@ -1031,57 +1070,18 @@ namespace PepperDash.Essentials.RoomBridges /// public class ApiTouchPanelToken { - [JsonProperty("touchPanels", NullValueHandling = NullValueHandling.Ignore)] + /// /// Gets or sets the TouchPanels /// + [JsonProperty("touchPanels", NullValueHandling = NullValueHandling.Ignore)] public List TouchPanels { get; set; } = new List(); - [JsonProperty("userAppUrl", NullValueHandling = NullValueHandling.Ignore)] + /// /// Gets or sets the UserAppUrl /// + [JsonProperty("userAppUrl", NullValueHandling = NullValueHandling.Ignore)] public string UserAppUrl { get; set; } = ""; } - -#if SERIES3 - /// - /// Represents a SourceSelectMessageContent - /// - public class SourceSelectMessageContent - { - /// - /// Gets or sets the SourceListItem - /// - public string SourceListItem { get; set; } - /// - /// Gets or sets the SourceListKey - /// - public string SourceListKey { get; set; } - } - - /// - /// Represents a DirectRoute - /// - public class DirectRoute - { - /// - /// Gets or sets the SourceKey - /// - public string SourceKey { get; set; } - /// - /// Gets or sets the DestinationKey - /// - public string DestinationKey { get; set; } - } - - /// - /// - /// - /// - /// - /// Delegate for PressAndHoldAction - /// - public delegate void PressAndHoldAction(bool b); -#endif } \ No newline at end of file diff --git a/src/PepperDash.Essentials.MobileControl/Touchpanel/MobileControlTouchpanelProperties.cs b/src/PepperDash.Essentials.MobileControl/Touchpanel/MobileControlTouchpanelProperties.cs index c99abe31..8156834b 100644 --- a/src/PepperDash.Essentials.MobileControl/Touchpanel/MobileControlTouchpanelProperties.cs +++ b/src/PepperDash.Essentials.MobileControl/Touchpanel/MobileControlTouchpanelProperties.cs @@ -8,28 +8,32 @@ namespace PepperDash.Essentials.Touchpanel /// public class MobileControlTouchpanelProperties : CrestronTouchpanelPropertiesConfig { - [JsonProperty("useDirectServer")] + /// /// Gets or sets the UseDirectServer /// + [JsonProperty("useDirectServer")] public bool UseDirectServer { get; set; } = false; - [JsonProperty("zoomRoomController")] + /// /// Gets or sets the ZoomRoomController /// + [JsonProperty("zoomRoomController")] public bool ZoomRoomController { get; set; } = false; - [JsonProperty("buttonToolbarTimeoutInS")] + /// /// Gets or sets the ButtonToolbarTimoutInS /// + [JsonProperty("buttonToolbarTimeoutInS")] public ushort ButtonToolbarTimoutInS { get; set; } = 0; - [JsonProperty("theme")] + /// /// Gets or sets the Theme /// + [JsonProperty("theme")] public string Theme { get; set; } = "light"; } } \ No newline at end of file diff --git a/src/PepperDash.Essentials.MobileControl/Touchpanel/ThemeMessenger.cs b/src/PepperDash.Essentials.MobileControl/Touchpanel/ThemeMessenger.cs index 133cf0c8..089665d9 100644 --- a/src/PepperDash.Essentials.MobileControl/Touchpanel/ThemeMessenger.cs +++ b/src/PepperDash.Essentials.MobileControl/Touchpanel/ThemeMessenger.cs @@ -42,10 +42,11 @@ namespace PepperDash.Essentials.Touchpanel /// public class ThemeUpdateMessage : DeviceStateMessageBase { - [JsonProperty("theme")] + /// /// Gets or sets the Theme /// + [JsonProperty("theme")] public string Theme { get; set; } } } diff --git a/src/PepperDash.Essentials.MobileControl/UserCodeChangedContent.cs b/src/PepperDash.Essentials.MobileControl/UserCodeChangedContent.cs index 1dd3ff97..ce31f766 100644 --- a/src/PepperDash.Essentials.MobileControl/UserCodeChangedContent.cs +++ b/src/PepperDash.Essentials.MobileControl/UserCodeChangedContent.cs @@ -7,16 +7,18 @@ namespace PepperDash.Essentials /// public class UserCodeChangedContent { - [JsonProperty("userCode")] + /// /// Gets or sets the UserCode /// + [JsonProperty("userCode")] public string UserCode { get; set; } - [JsonProperty("qrChecksum", NullValueHandling = NullValueHandling.Include)] + /// /// Gets or sets the QrChecksum /// + [JsonProperty("qrChecksum", NullValueHandling = NullValueHandling.Include)] public string QrChecksum { get; set; } } } diff --git a/src/PepperDash.Essentials.MobileControl/Volumes.cs b/src/PepperDash.Essentials.MobileControl/Volumes.cs index 1de5078e..84accd26 100644 --- a/src/PepperDash.Essentials.MobileControl/Volumes.cs +++ b/src/PepperDash.Essentials.MobileControl/Volumes.cs @@ -1,5 +1,5 @@ -using Newtonsoft.Json; -using System.Collections.Generic; +using System.Collections.Generic; +using Newtonsoft.Json; namespace PepperDash.Essentials { @@ -8,10 +8,11 @@ namespace PepperDash.Essentials /// public class Volumes { - [JsonProperty("master", NullValueHandling = NullValueHandling.Ignore)] + /// /// Gets or sets the Master /// + [JsonProperty("master", NullValueHandling = NullValueHandling.Ignore)] public Volume Master { get; set; } [JsonProperty("auxFaders", NullValueHandling = NullValueHandling.Ignore)] @@ -30,10 +31,11 @@ namespace PepperDash.Essentials /// public class Volume { - [JsonProperty("key", NullValueHandling = NullValueHandling.Ignore)] + /// /// Gets or sets the Key /// + [JsonProperty("key", NullValueHandling = NullValueHandling.Ignore)] public string Key { get; set; } [JsonProperty("level", NullValueHandling = NullValueHandling.Ignore)] @@ -42,10 +44,11 @@ namespace PepperDash.Essentials [JsonProperty("muted", NullValueHandling = NullValueHandling.Ignore)] public bool? Muted { get; set; } - [JsonProperty("label", NullValueHandling = NullValueHandling.Ignore)] + /// /// Gets or sets the Label /// + [JsonProperty("label", NullValueHandling = NullValueHandling.Ignore)] public string Label { get; set; } [JsonProperty("hasMute", NullValueHandling = NullValueHandling.Ignore)] @@ -58,10 +61,11 @@ namespace PepperDash.Essentials public bool? PrivacyMuted { get; set; } - [JsonProperty("muteIcon", NullValueHandling = NullValueHandling.Ignore)] + /// /// Gets or sets the MuteIcon /// + [JsonProperty("muteIcon", NullValueHandling = NullValueHandling.Ignore)] public string MuteIcon { get; set; } public Volume(string key, int level, bool muted, string label, bool hasMute, string muteIcon) diff --git a/src/PepperDash.Essentials.MobileControl/WebApiHandlers/ActionPathsHandler.cs b/src/PepperDash.Essentials.MobileControl/WebApiHandlers/ActionPathsHandler.cs index 14fa3568..84d0318a 100644 --- a/src/PepperDash.Essentials.MobileControl/WebApiHandlers/ActionPathsHandler.cs +++ b/src/PepperDash.Essentials.MobileControl/WebApiHandlers/ActionPathsHandler.cs @@ -1,8 +1,8 @@ -using Crestron.SimplSharp.WebScripting; +using System.Collections.Generic; +using System.Linq; +using Crestron.SimplSharp.WebScripting; using Newtonsoft.Json; using PepperDash.Core.Web.RequestHandlers; -using System.Collections.Generic; -using System.Linq; namespace PepperDash.Essentials.WebApiHandlers { @@ -51,16 +51,18 @@ namespace PepperDash.Essentials.WebApiHandlers /// public class ActionPath { - [JsonProperty("messengerKey")] + /// /// Gets or sets the MessengerKey /// + [JsonProperty("messengerKey")] public string MessengerKey { get; set; } - [JsonProperty("path")] + /// /// Gets or sets the Path /// + [JsonProperty("path")] public string Path { get; set; } } } diff --git a/src/PepperDash.Essentials.MobileControl/WebApiHandlers/UiClientHandler.cs b/src/PepperDash.Essentials.MobileControl/WebApiHandlers/UiClientHandler.cs index 6826092b..e45fcc39 100644 --- a/src/PepperDash.Essentials.MobileControl/WebApiHandlers/UiClientHandler.cs +++ b/src/PepperDash.Essentials.MobileControl/WebApiHandlers/UiClientHandler.cs @@ -148,22 +148,25 @@ namespace PepperDash.Essentials.WebApiHandlers /// public class ClientRequest { - [JsonProperty("roomKey", NullValueHandling = NullValueHandling.Ignore)] + /// /// Gets or sets the RoomKey /// + [JsonProperty("roomKey", NullValueHandling = NullValueHandling.Ignore)] public string RoomKey { get; set; } - [JsonProperty("grantCode", NullValueHandling = NullValueHandling.Ignore)] + /// /// Gets or sets the GrantCode /// + [JsonProperty("grantCode", NullValueHandling = NullValueHandling.Ignore)] public string GrantCode { get; set; } - [JsonProperty("token", NullValueHandling = NullValueHandling.Ignore)] + /// /// Gets or sets the Token /// + [JsonProperty("token", NullValueHandling = NullValueHandling.Ignore)] public string Token { get; set; } } @@ -172,22 +175,25 @@ namespace PepperDash.Essentials.WebApiHandlers /// public class ClientResponse { - [JsonProperty("error", NullValueHandling = NullValueHandling.Ignore)] + /// /// Gets or sets the Error /// + [JsonProperty("error", NullValueHandling = NullValueHandling.Ignore)] public string Error { get; set; } - [JsonProperty("token", NullValueHandling = NullValueHandling.Ignore)] + /// /// Gets or sets the Token /// + [JsonProperty("token", NullValueHandling = NullValueHandling.Ignore)] public string Token { get; set; } - [JsonProperty("path", NullValueHandling = NullValueHandling.Ignore)] + /// /// Gets or sets the Path /// + [JsonProperty("path", NullValueHandling = NullValueHandling.Ignore)] public string Path { get; set; } } } diff --git a/src/PepperDash.Essentials.MobileControl/WebSocketServer/MobileControlWebsocketServer.cs b/src/PepperDash.Essentials.MobileControl/WebSocketServer/MobileControlWebsocketServer.cs index ba241a04..db5cf06a 100644 --- a/src/PepperDash.Essentials.MobileControl/WebSocketServer/MobileControlWebsocketServer.cs +++ b/src/PepperDash.Essentials.MobileControl/WebSocketServer/MobileControlWebsocketServer.cs @@ -1334,10 +1334,11 @@ namespace PepperDash.Essentials.WebSocketServer /// public class JoinResponse { - [JsonProperty("clientId")] + /// /// Gets or sets the ClientId /// + [JsonProperty("clientId")] public string ClientId { get; set; } [JsonProperty("roomKey")] @@ -1346,40 +1347,46 @@ namespace PepperDash.Essentials.WebSocketServer [JsonProperty("systemUUid")] public string SystemUuid { get; set; } - [JsonProperty("roomUUid")] + /// /// Gets or sets the RoomUuid /// + [JsonProperty("roomUUid")] public string RoomUuid { get; set; } - [JsonProperty("config")] + /// /// Gets or sets the Config /// + [JsonProperty("config")] public object Config { get; set; } - [JsonProperty("codeExpires")] + /// /// Gets or sets the CodeExpires /// + [JsonProperty("codeExpires")] public DateTime CodeExpires { get; set; } - [JsonProperty("userCode")] + /// /// Gets or sets the UserCode /// + [JsonProperty("userCode")] public string UserCode { get; set; } - [JsonProperty("userAppUrl")] + /// /// Gets or sets the UserAppUrl /// + [JsonProperty("userAppUrl")] public string UserAppUrl { get; set; } - [JsonProperty("enableDebug")] + /// /// Gets or sets the EnableDebug /// + [JsonProperty("enableDebug")] public bool EnableDebug { get; set; } } } From c0af637108abf431ddf2017f03bcc5223186b41e Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Tue, 23 Sep 2025 11:07:24 -0500 Subject: [PATCH 02/12] fix: use correct property names --- .../Messengers/SystemMonitorMessenger.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/SystemMonitorMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/SystemMonitorMessenger.cs index fcc02930..63869ae0 100644 --- a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/SystemMonitorMessenger.cs +++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/SystemMonitorMessenger.cs @@ -105,35 +105,35 @@ namespace PepperDash.Essentials.AppServer.Messengers /// /// Gets or sets the TimeZoneName /// - [JsonProperty("timeZone", NullValueHandling = NullValueHandling.Ignore)] + [JsonProperty("timeZoneName", NullValueHandling = NullValueHandling.Ignore)] public string TimeZoneName { get; set; } /// /// Gets or sets the IoControllerVersion /// - [JsonProperty("timeZone", NullValueHandling = NullValueHandling.Ignore)] + [JsonProperty("ioControllerVersion", NullValueHandling = NullValueHandling.Ignore)] public string IoControllerVersion { get; set; } /// /// Gets or sets the SnmpVersion /// - [JsonProperty("timeZone", NullValueHandling = NullValueHandling.Ignore)] + [JsonProperty("snmpVersion", NullValueHandling = NullValueHandling.Ignore)] public string SnmpVersion { get; set; } /// /// Gets or sets the BacnetVersion /// - [JsonProperty("timeZone", NullValueHandling = NullValueHandling.Ignore)] + [JsonProperty("bacnetVersion", NullValueHandling = NullValueHandling.Ignore)] public string BacnetVersion { get; set; } /// /// Gets or sets the ControllerVersion /// - [JsonProperty("timeZone", NullValueHandling = NullValueHandling.Ignore)] + [JsonProperty("controllerVersion", NullValueHandling = NullValueHandling.Ignore)] public string ControllerVersion { get; set; } } } \ No newline at end of file From 9c0cab82184cbacc359f02fb3ef2fc6607e368db Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Tue, 23 Sep 2025 11:09:37 -0500 Subject: [PATCH 03/12] fix: use id parameter and fix some formatting --- .../Messengers/ICommunicationMonitorMessenger.cs | 4 ++-- .../Messengers/IHasPowerControlWithFeedbackMessenger.cs | 2 +- .../Messengers/IShutdownPromptTimerMessenger.cs | 4 +--- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/ICommunicationMonitorMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/ICommunicationMonitorMessenger.cs index c3d2f1f0..79622a41 100644 --- a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/ICommunicationMonitorMessenger.cs +++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/ICommunicationMonitorMessenger.cs @@ -53,8 +53,8 @@ namespace PepperDash.Essentials.AppServer.Messengers { IsOnline = _communicationMonitor.CommunicationMonitor.IsOnline, Status = _communicationMonitor.CommunicationMonitor.Status - } - }); + }, + }, id); } } diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHasPowerControlWithFeedbackMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHasPowerControlWithFeedbackMessenger.cs index 44c29f86..525a6d6d 100644 --- a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHasPowerControlWithFeedbackMessenger.cs +++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHasPowerControlWithFeedbackMessenger.cs @@ -34,7 +34,7 @@ namespace PepperDash.Essentials.AppServer.Messengers PowerState = _powerControl.PowerIsOnFeedback.BoolValue }; - PostStatusMessage(messageObj); + PostStatusMessage(messageObj, id); } /// diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IShutdownPromptTimerMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IShutdownPromptTimerMessenger.cs index 30a24827..516e77c6 100644 --- a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IShutdownPromptTimerMessenger.cs +++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IShutdownPromptTimerMessenger.cs @@ -20,9 +20,7 @@ namespace PepperDash.Essentials.AppServer.Messengers protected override void RegisterActions() { - AddAction("/status", (id, content) => - SendFullStatus(id) - ); + AddAction("/status", (id, content) => SendFullStatus(id)); AddAction("/shutdownPromptStatus", (id, content) => SendFullStatus(id)); From 06341b14f3022906d1913ca0f553e5c73c4ee1ee Mon Sep 17 00:00:00 2001 From: Neil Dorin Date: Wed, 24 Sep 2025 14:49:41 -0600 Subject: [PATCH 04/12] feat: Adds device interface support info to joinroom response in MC websocket server. Enhance MessengerBase and WebSocketServer functionality Updated MessengerBase with new methods for action management and message posting, along with improved documentation. Introduced DeviceMessageBase for better message representation. Enhanced MobileControlWebsocketServer to support device interfaces, adding DeviceInterfaceSupport to JoinResponse and a new DeviceInterfaceInfo class for detailed device information. --- .../Messengers/MessengerBase.cs | 59 ++++++++++++++++++- .../MobileControlWebsocketServer.cs | 47 ++++++++++++++- 2 files changed, 102 insertions(+), 4 deletions(-) diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/MessengerBase.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/MessengerBase.cs index ad8e67a3..4d96cf81 100644 --- a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/MessengerBase.cs +++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/MessengerBase.cs @@ -15,12 +15,18 @@ namespace PepperDash.Essentials.AppServer.Messengers /// public abstract class MessengerBase : EssentialsDevice, IMobileControlMessenger { + /// + /// The device this messenger is associated with + /// protected IKeyName _device; private readonly List _deviceInterfaces; private readonly Dictionary> _actions = new Dictionary>(); + /// + /// Gets the DeviceKey + /// public string DeviceKey => _device?.Key ?? ""; @@ -50,6 +56,12 @@ namespace PepperDash.Essentials.AppServer.Messengers MessagePath = messagePath; } + /// + /// Constructor for a messenger associated with a device + /// + /// + /// + /// protected MessengerBase(string key, string messagePath, IKeyName device) : this(key, messagePath) { @@ -96,6 +108,11 @@ namespace PepperDash.Essentials.AppServer.Messengers action(id, content); } + /// + /// Adds an action for a given path + /// + /// + /// protected void AddAction(string path, Action action) { if (_actions.ContainsKey(path)) @@ -115,6 +132,10 @@ namespace PepperDash.Essentials.AppServer.Messengers return _actions.Keys.ToList(); } + /// + /// Removes an action for a given path + /// + /// protected void RemoveAction(string path) { if (!_actions.ContainsKey(path)) @@ -128,7 +149,6 @@ namespace PepperDash.Essentials.AppServer.Messengers /// /// Implemented in extending classes. Wire up API calls and feedback here /// - /// protected virtual void RegisterActions() { @@ -137,8 +157,8 @@ namespace PepperDash.Essentials.AppServer.Messengers /// /// Helper for posting status message /// - /// /// + /// Optional client id that will direct the message back to only that client protected void PostStatusMessage(DeviceStateMessageBase message, string clientId = null) { try @@ -169,6 +189,12 @@ namespace PepperDash.Essentials.AppServer.Messengers } } + /// + /// Helper for posting status message + /// + /// + /// + /// Optional client id that will direct the message back to only that client protected void PostStatusMessage(string type, DeviceStateMessageBase deviceState, string clientId = null) { try @@ -192,6 +218,12 @@ namespace PepperDash.Essentials.AppServer.Messengers } } + /// + /// Helper for posting status message + /// + /// + /// + /// Optional client id that will direct the message back to only that client protected void PostStatusMessage(JToken content, string type = "", string clientId = null) { try @@ -204,6 +236,10 @@ namespace PepperDash.Essentials.AppServer.Messengers } } + /// + /// Helper for posting event message + /// + /// protected void PostEventMessage(DeviceEventMessageBase message) { message.Key = _device.Key; @@ -217,6 +253,11 @@ namespace PepperDash.Essentials.AppServer.Messengers }); } + /// + /// Helper for posting event message + /// + /// + /// protected void PostEventMessage(DeviceEventMessageBase message, string eventType) { message.Key = _device.Key; @@ -232,6 +273,10 @@ namespace PepperDash.Essentials.AppServer.Messengers }); } + /// + /// Helper for posting event message with no content + /// + /// protected void PostEventMessage(string eventType) { AppServerController?.SendMessageObject(new MobileControlMessage @@ -243,6 +288,9 @@ namespace PepperDash.Essentials.AppServer.Messengers } + /// + /// Base class for device messages that include the type of message + /// public abstract class DeviceMessageBase { /// @@ -266,10 +314,11 @@ namespace PepperDash.Essentials.AppServer.Messengers [JsonProperty("messageType")] public string MessageType => GetType().Name; - [JsonProperty("messageBasePath")] /// /// Gets or sets the MessageBasePath /// + [JsonProperty("messageBasePath")] + public string MessageBasePath { get; set; } } @@ -284,6 +333,10 @@ namespace PepperDash.Essentials.AppServer.Messengers [JsonProperty("interfaces")] public List Interfaces { get; private set; } + /// + /// Sets the interfaces implemented by the device sending the message + /// + /// public void SetInterfaces(List interfaces) { Interfaces = interfaces; diff --git a/src/PepperDash.Essentials.MobileControl/WebSocketServer/MobileControlWebsocketServer.cs b/src/PepperDash.Essentials.MobileControl/WebSocketServer/MobileControlWebsocketServer.cs index db5cf06a..4cb9de84 100644 --- a/src/PepperDash.Essentials.MobileControl/WebSocketServer/MobileControlWebsocketServer.cs +++ b/src/PepperDash.Essentials.MobileControl/WebSocketServer/MobileControlWebsocketServer.cs @@ -972,6 +972,20 @@ namespace PepperDash.Essentials.WebSocketServer res.StatusCode = 200; res.ContentType = "application/json"; + var devices = DeviceManager.GetDevices(); + Dictionary deviceInterfaces = new Dictionary(); + + foreach (var device in devices) + { + var interfaces = device?.GetType().GetInterfaces().Select((i) => i.Name).ToList() ?? new List(); + deviceInterfaces.Add(device.Key, new DeviceInterfaceInfo + { + Key = device.Key, + Name = device is IKeyName ? (device as IKeyName).Name : "", + Interfaces = interfaces + }); + } + // Construct the response object JoinResponse jRes = new JoinResponse { @@ -985,7 +999,8 @@ namespace PepperDash.Essentials.WebSocketServer UserAppUrl = string.Format("http://{0}:{1}/mc/app", CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, 0), Port), - EnableDebug = false + EnableDebug = false, + DeviceInterfaceSupport = deviceInterfaces }; // Serialize to JSON and convert to Byte[] @@ -1361,6 +1376,12 @@ namespace PepperDash.Essentials.WebSocketServer [JsonProperty("config")] public object Config { get; set; } + /// + /// Gets or sets the DeviceInterfaceSupport + /// + [JsonProperty("deviceInterfaceSupport")] + public Dictionary DeviceInterfaceSupport { get; set; } + /// /// Gets or sets the CodeExpires @@ -1389,4 +1410,28 @@ namespace PepperDash.Essentials.WebSocketServer [JsonProperty("enableDebug")] public bool EnableDebug { get; set; } } + + /// + /// Represents info about a device including supproted interfaces + /// + public class DeviceInterfaceInfo : IKeyName + { + /// + /// Gets or sets the Key + /// + [JsonProperty("key")] + public string Key { get; set; } + + /// + /// Gets or sets the Name + /// + [JsonProperty("name")] + public string Name { get; set; } + + /// + /// Gets or sets the Interfaces + /// + [JsonProperty("interfaces")] + public List Interfaces { get; set; } + } } From fd70377c7ff8a102753b8c53ef5c8ccc303c896f Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Thu, 25 Sep 2025 08:36:24 -0500 Subject: [PATCH 05/12] fix: log errors and disconnects for UI Clients --- .../WebSocketServer/DeviceInterfaceInfo.cs | 28 ++ .../WebSocketServer/JoinResponse.cs | 71 +++++ .../WebSocketServer/JoinToken.cs | 24 ++ .../MobileControlWebsocketServer.cs | 264 +++--------------- .../WebSocketServer/ServerTokenSecrets.cs | 24 ++ .../WebSocketServer/UiClient.cs | 29 +- .../WebSocketServer/UiClientContext.cs | 31 ++ .../WebSocketServer/Version.cs | 22 ++ 8 files changed, 253 insertions(+), 240 deletions(-) create mode 100644 src/PepperDash.Essentials.MobileControl/WebSocketServer/DeviceInterfaceInfo.cs create mode 100644 src/PepperDash.Essentials.MobileControl/WebSocketServer/JoinResponse.cs create mode 100644 src/PepperDash.Essentials.MobileControl/WebSocketServer/JoinToken.cs create mode 100644 src/PepperDash.Essentials.MobileControl/WebSocketServer/ServerTokenSecrets.cs create mode 100644 src/PepperDash.Essentials.MobileControl/WebSocketServer/UiClientContext.cs create mode 100644 src/PepperDash.Essentials.MobileControl/WebSocketServer/Version.cs diff --git a/src/PepperDash.Essentials.MobileControl/WebSocketServer/DeviceInterfaceInfo.cs b/src/PepperDash.Essentials.MobileControl/WebSocketServer/DeviceInterfaceInfo.cs new file mode 100644 index 00000000..09be4399 --- /dev/null +++ b/src/PepperDash.Essentials.MobileControl/WebSocketServer/DeviceInterfaceInfo.cs @@ -0,0 +1,28 @@ +using System.Collections.Generic; +using Newtonsoft.Json; +using PepperDash.Core; + + +/// +/// Represents info about a device including supproted interfaces +/// +public class DeviceInterfaceInfo : IKeyName +{ + /// + /// Gets or sets the Key + /// + [JsonProperty("key")] + public string Key { get; set; } + + /// + /// Gets or sets the Name + /// + [JsonProperty("name")] + public string Name { get; set; } + + /// + /// Gets or sets the Interfaces + /// + [JsonProperty("interfaces")] + public List Interfaces { get; set; } +} \ No newline at end of file diff --git a/src/PepperDash.Essentials.MobileControl/WebSocketServer/JoinResponse.cs b/src/PepperDash.Essentials.MobileControl/WebSocketServer/JoinResponse.cs new file mode 100644 index 00000000..ce0610e0 --- /dev/null +++ b/src/PepperDash.Essentials.MobileControl/WebSocketServer/JoinResponse.cs @@ -0,0 +1,71 @@ +using System; +using System.Collections.Generic; +using Newtonsoft.Json; + + +namespace PepperDash.Essentials.WebSocketServer +{ + /// + /// Represents a JoinResponse + /// + public class JoinResponse + { + + /// + /// Gets or sets the ClientId + /// + [JsonProperty("clientId")] + public string ClientId { get; set; } + + [JsonProperty("roomKey")] + public string RoomKey { get; set; } + + [JsonProperty("systemUUid")] + public string SystemUuid { get; set; } + + + /// + /// Gets or sets the RoomUuid + /// + [JsonProperty("roomUUid")] + public string RoomUuid { get; set; } + + + /// + /// Gets or sets the Config + /// + [JsonProperty("config")] + public object Config { get; set; } + + + /// + /// Gets or sets the CodeExpires + /// + [JsonProperty("codeExpires")] + public DateTime CodeExpires { get; set; } + + + /// + /// Gets or sets the UserCode + /// + [JsonProperty("userCode")] + public string UserCode { get; set; } + + + /// + /// Gets or sets the UserAppUrl + /// + [JsonProperty("userAppUrl")] + public string UserAppUrl { get; set; } + + + /// + /// Gets or sets the EnableDebug + /// + [JsonProperty("enableDebug")] + public bool EnableDebug { get; set; } + + /// + public Dictionary DeviceInterfaceSupport { get; set; } + } +} diff --git a/src/PepperDash.Essentials.MobileControl/WebSocketServer/JoinToken.cs b/src/PepperDash.Essentials.MobileControl/WebSocketServer/JoinToken.cs new file mode 100644 index 00000000..b3ea3c7c --- /dev/null +++ b/src/PepperDash.Essentials.MobileControl/WebSocketServer/JoinToken.cs @@ -0,0 +1,24 @@ +namespace PepperDash.Essentials.WebSocketServer +{ + /// + /// Represents a JoinToken + /// + public class JoinToken + { + /// + /// Gets or sets the Code + /// + public string Code { get; set; } + + public string RoomKey { get; set; } + + public string Uuid { get; set; } + + public string TouchpanelKey { get; set; } = ""; + + /// + /// Gets or sets the Token + /// + public string Token { get; set; } = null; + } +} diff --git a/src/PepperDash.Essentials.MobileControl/WebSocketServer/MobileControlWebsocketServer.cs b/src/PepperDash.Essentials.MobileControl/WebSocketServer/MobileControlWebsocketServer.cs index 4cb9de84..f2f5003a 100644 --- a/src/PepperDash.Essentials.MobileControl/WebSocketServer/MobileControlWebsocketServer.cs +++ b/src/PepperDash.Essentials.MobileControl/WebSocketServer/MobileControlWebsocketServer.cs @@ -155,7 +155,7 @@ namespace PepperDash.Essentials.WebSocketServer { try { - Debug.LogMessage(LogEventLevel.Information, "Automatically forwarding port {0} to CS LAN", Port); + this.LogInformation("Automatically forwarding port {port} to CS LAN", Port); var csAdapterId = CrestronEthernetHelper.GetAdapterdIdForSpecifiedAdapterType(EthernetAdapterType.EthernetCSAdapter); var csIp = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, csAdapterId); @@ -164,16 +164,17 @@ namespace PepperDash.Essentials.WebSocketServer if (result != CrestronEthernetHelper.PortForwardingUserPatRetCodes.NoErr) { - Debug.LogMessage(LogEventLevel.Error, "Error adding port forwarding: {0}", result); + this.LogError("Error adding port forwarding: {error}", result); } } catch (ArgumentException) { - Debug.LogMessage(LogEventLevel.Information, "This processor does not have a CS LAN", this); + this.LogInformation("This processor does not have a CS LAN", this); } catch (Exception ex) { - Debug.LogMessage(ex, "Error automatically forwarding port to CS LAN"); + this.LogError("Error automatically forwarding port to CS LAN: {message}", ex.Message); + this.LogDebug(ex, "Stack Trace"); } } @@ -190,7 +191,7 @@ namespace PepperDash.Essentials.WebSocketServer { if (parent.Config.DirectServer.AutomaticallyForwardPortToCSLAN == false) { - Debug.LogMessage(LogEventLevel.Information, "This processor does not have a CS LAN", this); + this.LogInformation("This processor does not have a CS LAN"); } } @@ -259,13 +260,15 @@ namespace PepperDash.Essentials.WebSocketServer _server.OnPost += Server_OnPost; } + _server.Log.Output = (data, level) => this.LogInformation("WebSocket Server Log [{level}]: {data}", level, data); + CrestronEnvironment.ProgramStatusEventHandler += CrestronEnvironment_ProgramStatusEventHandler; _server.Start(); if (_server.IsListening) { - Debug.LogMessage(LogEventLevel.Information, "Mobile Control WebSocket Server listening on port {port}", this, _server.Port); + this.LogInformation("Mobile Control WebSocket Server listening on port {port}", _server.Port); } CrestronEnvironment.ProgramStatusEventHandler += OnProgramStop; @@ -278,7 +281,8 @@ namespace PepperDash.Essentials.WebSocketServer } catch (Exception ex) { - Debug.LogMessage(ex, "Exception intializing websocket server", this); + this.LogError("Exception initializing direct server: {message}", ex.Message); + this.LogDebug(ex, "Stack Trace"); } } @@ -347,22 +351,6 @@ namespace PepperDash.Essentials.WebSocketServer string ip = processorIp; - // Moved to the MobileControlTouchpanelController class in the GetUrlWithCorrectIp method - // triggered by the Panel.IpInformationChange event so that we know we have the necessary info - // to make the determination of which IP to use. - //if (touchpanel.Touchpanel is IMobileControlCrestronTouchpanelController crestronTouchpanel && csIpAddress != null) - //{ - // ip = crestronTouchpanel.ConnectedIps.Any(ipInfo => - // { - // if (System.Net.IPAddress.TryParse(ipInfo.DeviceIpAddress, out var parsedIp)) - // { - // return csIpAddress.IsInSameSubnet(parsedIp, csSubnetMask); - // } - // this.LogWarning("Invalid IP address: {deviceIpAddress}", ipInfo.DeviceIpAddress); - // return false; - // }) ? csIpAddress.ToString() : processorIp; - //} - if (_parent.Config.DirectServer.CSLanUiDeviceKeys != null && _parent.Config.DirectServer.CSLanUiDeviceKeys.Any(k => k.Equals(touchpanel.Touchpanel.Key, StringComparison.InvariantCultureIgnoreCase)) && csIpAddress != null) { ip = csIpAddress.ToString(); @@ -477,7 +465,8 @@ namespace PepperDash.Essentials.WebSocketServer } catch (Exception ex) { - this.LogError(ex, "Error getting application configuration"); + this.LogError("Error getting application configuration: {message}", ex.Message); + this.LogDebug(ex, "Stack Trace"); return null; } @@ -513,15 +502,14 @@ namespace PepperDash.Essentials.WebSocketServer { if (token.Value == null) { - Debug.LogMessage(LogEventLevel.Warning, "Token value is null", this); + this.LogWarning("Token value is null"); continue; } - Debug.LogMessage(LogEventLevel.Information, "Adding token: {0} for room: {1}", this, token.Key, token.Value.RoomKey); + this.LogInformation("Adding token: {key} for room: {roomKey}", token.Key, token.Value.RoomKey); if (UiClients == null) { - Debug.LogMessage(LogEventLevel.Warning, "UiClients is null", this); UiClients = new Dictionary(); } @@ -531,7 +519,7 @@ namespace PepperDash.Essentials.WebSocketServer if (UiClients.Count > 0) { - Debug.LogMessage(LogEventLevel.Information, "Restored {uiClientCount} UiClients from secrets data", this, UiClients.Count); + this.LogInformation("Restored {uiClientCount} UiClients from secrets data", UiClients.Count); foreach (var client in UiClients) { @@ -541,36 +529,28 @@ namespace PepperDash.Essentials.WebSocketServer _server.AddWebSocketService(path, () => { - var c = new UiClient(); - Debug.LogMessage(LogEventLevel.Debug, "Constructing UiClient with id: {key}", this, key); + var c = new UiClient($"uiclient-{key}-{roomKey}"); + this.LogDebug("Constructing UiClient with id: {key}", key); c.Controller = _parent; c.RoomKey = roomKey; UiClients[key].SetClient(c); return c; }); - - - //_server.WebSocketServices.AddService(path, (c) => - //{ - // Debug.Console(2, this, "Constructing UiClient with id: {0}", key); - // c.Controller = _parent; - // c.RoomKey = roomKey; - // UiClients[key].SetClient(c); - //}); } } } else { - Debug.LogMessage(LogEventLevel.Warning, "No secret found"); + this.LogWarning("No secret found"); } - Debug.LogMessage(LogEventLevel.Debug, "{uiClientCount} UiClients restored from secrets data", this, UiClients.Count); + this.LogDebug("{uiClientCount} UiClients restored from secrets data", UiClients.Count); } catch (Exception ex) { - Debug.LogMessage(ex, "Exception retrieving secret", this); + this.LogError("Exception retrieving secret: {message}", ex.Message); + this.LogDebug(ex, "Stack Trace"); } } @@ -583,7 +563,7 @@ namespace PepperDash.Essentials.WebSocketServer { if (_secret == null) { - Debug.LogMessage(LogEventLevel.Error, "Secret is null", this); + this.LogError("Secret is null"); _secret = new ServerTokenSecrets(string.Empty); } @@ -601,7 +581,8 @@ namespace PepperDash.Essentials.WebSocketServer } catch (Exception ex) { - Debug.LogMessage(ex, "Exception updating secret", this); + this.LogError("Exception updating secret: {message}", ex.Message); + this.LogDebug(ex, "Stack Trace"); } } @@ -704,18 +685,18 @@ namespace PepperDash.Essentials.WebSocketServer _server.AddWebSocketService(path, () => { - var c = new UiClient(); - Debug.LogMessage(LogEventLevel.Verbose, "Constructing UiClient with id: {0}", this, key); + var c = new UiClient($"uiclient-{key}-{bridge.RoomKey}"); + this.LogVerbose("Constructing UiClient with id: {key}", key); c.Controller = _parent; c.RoomKey = bridge.RoomKey; UiClients[key].SetClient(c); return c; }); - Debug.LogMessage(LogEventLevel.Information, "Added new WebSocket UiClient service at path: {path}", this, path); - Debug.LogMessage(LogEventLevel.Information, "Token: {@token}", this, token); + this.LogInformation("Added new WebSocket UiClient service at path: {path}", path); + this.LogInformation("Token: {@token}", token); - Debug.LogMessage(LogEventLevel.Verbose, "{serviceCount} websocket services present", this, _server.WebSocketServices.Count); + this.LogVerbose("{serviceCount} websocket services present", _server.WebSocketServices.Count); UpdateSecret(); @@ -729,7 +710,7 @@ namespace PepperDash.Essentials.WebSocketServer { if (s == "?" || string.IsNullOrEmpty(s)) { - CrestronConsole.ConsoleCommandResponse(@"Removes all clients from the server. To execute add 'confirm' to command"); + CrestronConsole.ConsoleCommandResponse(@"Remove all clients from the server. To execute add 'confirm' to command"); return; } @@ -883,7 +864,8 @@ namespace PepperDash.Essentials.WebSocketServer } catch (Exception ex) { - Debug.LogMessage(ex, "Caught an exception in the OnGet handler", this); + this.LogError("Exception in OnGet handler: {message}", ex.Message); + this.LogDebug(ex, "Stack Trace"); } } @@ -1186,7 +1168,7 @@ namespace PepperDash.Essentials.WebSocketServer } else { - this.LogVerbose("File not found: {filePath}", filePath); + this.LogWarning("File not found: {filePath}", filePath); res.StatusCode = (int)HttpStatusCode.NotFound; res.Close(); return; @@ -1256,182 +1238,4 @@ namespace PepperDash.Essentials.WebSocketServer } } } - - /// - /// Represents a Version - /// - public class Version - { - [JsonProperty("serverVersion")] - public string ServerVersion { get; set; } - - [JsonProperty("serverIsRunningOnProcessorHardware")] - public bool ServerIsRunningOnProcessorHardware { get; private set; } - - public Version() - { - ServerIsRunningOnProcessorHardware = true; - } - } - - /// - /// Represents a UiClientContext - /// - public class UiClientContext - { - /// - /// Gets or sets the Client - /// - public UiClient Client { get; private set; } - /// - /// Gets or sets the Token - /// - public JoinToken Token { get; private set; } - - public UiClientContext(JoinToken token) - { - Token = token; - } - - /// - /// SetClient method - /// - public void SetClient(UiClient client) - { - Client = client; - } - - } - - /// - /// Represents a ServerTokenSecrets - /// - public class ServerTokenSecrets - { - /// - /// Gets or sets the GrantCode - /// - public string GrantCode { get; set; } - - public Dictionary Tokens { get; set; } - - public ServerTokenSecrets(string grantCode) - { - GrantCode = grantCode; - Tokens = new Dictionary(); - } - } - - /// - /// Represents a JoinToken - /// - public class JoinToken - { - /// - /// Gets or sets the Code - /// - public string Code { get; set; } - - public string RoomKey { get; set; } - - public string Uuid { get; set; } - - public string TouchpanelKey { get; set; } = ""; - - /// - /// Gets or sets the Token - /// - public string Token { get; set; } = null; - } - - /// - /// Represents a JoinResponse - /// - public class JoinResponse - { - - /// - /// Gets or sets the ClientId - /// - [JsonProperty("clientId")] - public string ClientId { get; set; } - - [JsonProperty("roomKey")] - public string RoomKey { get; set; } - - [JsonProperty("systemUUid")] - public string SystemUuid { get; set; } - - - /// - /// Gets or sets the RoomUuid - /// - [JsonProperty("roomUUid")] - public string RoomUuid { get; set; } - - - /// - /// Gets or sets the Config - /// - [JsonProperty("config")] - public object Config { get; set; } - - /// - /// Gets or sets the DeviceInterfaceSupport - /// - [JsonProperty("deviceInterfaceSupport")] - public Dictionary DeviceInterfaceSupport { get; set; } - - - /// - /// Gets or sets the CodeExpires - /// - [JsonProperty("codeExpires")] - public DateTime CodeExpires { get; set; } - - - /// - /// Gets or sets the UserCode - /// - [JsonProperty("userCode")] - public string UserCode { get; set; } - - - /// - /// Gets or sets the UserAppUrl - /// - [JsonProperty("userAppUrl")] - public string UserAppUrl { get; set; } - - - /// - /// Gets or sets the EnableDebug - /// - [JsonProperty("enableDebug")] - public bool EnableDebug { get; set; } - } - - /// - /// Represents info about a device including supproted interfaces - /// - public class DeviceInterfaceInfo : IKeyName - { - /// - /// Gets or sets the Key - /// - [JsonProperty("key")] - public string Key { get; set; } - - /// - /// Gets or sets the Name - /// - [JsonProperty("name")] - public string Name { get; set; } - - /// - /// Gets or sets the Interfaces - /// - [JsonProperty("interfaces")] - public List Interfaces { get; set; } - } } diff --git a/src/PepperDash.Essentials.MobileControl/WebSocketServer/ServerTokenSecrets.cs b/src/PepperDash.Essentials.MobileControl/WebSocketServer/ServerTokenSecrets.cs new file mode 100644 index 00000000..3fa2fb0c --- /dev/null +++ b/src/PepperDash.Essentials.MobileControl/WebSocketServer/ServerTokenSecrets.cs @@ -0,0 +1,24 @@ +using System.Collections.Generic; + + +namespace PepperDash.Essentials.WebSocketServer +{ + /// + /// Represents a ServerTokenSecrets + /// + public class ServerTokenSecrets + { + /// + /// Gets or sets the GrantCode + /// + public string GrantCode { get; set; } + + public Dictionary Tokens { get; set; } + + public ServerTokenSecrets(string grantCode) + { + GrantCode = grantCode; + Tokens = new Dictionary(); + } + } +} diff --git a/src/PepperDash.Essentials.MobileControl/WebSocketServer/UiClient.cs b/src/PepperDash.Essentials.MobileControl/WebSocketServer/UiClient.cs index eb1cf7a1..d1d4524d 100644 --- a/src/PepperDash.Essentials.MobileControl/WebSocketServer/UiClient.cs +++ b/src/PepperDash.Essentials.MobileControl/WebSocketServer/UiClient.cs @@ -1,23 +1,27 @@ -using Newtonsoft.Json; +using System; +using System.Text.RegularExpressions; +using Newtonsoft.Json; using Newtonsoft.Json.Linq; using PepperDash.Core; +using PepperDash.Core.Logging; using PepperDash.Essentials.AppServer.Messengers; using PepperDash.Essentials.RoomBridges; using Serilog.Events; -using System; -using System.Text.RegularExpressions; using WebSocketSharp; using WebSocketSharp.Server; using ErrorEventArgs = WebSocketSharp.ErrorEventArgs; namespace PepperDash.Essentials.WebSocketServer -{ +{ /// /// Represents the behaviour to associate with a UiClient for WebSocket communication /// - public class UiClient : WebSocketBehavior + public class UiClient : WebSocketBehavior, IKeyed { + /// + public string Key { get; private set; } + public MobileControlSystemController Controller { get; set; } public string RoomKey { get; set; } @@ -41,17 +45,18 @@ namespace PepperDash.Essentials.WebSocketServer } } - public UiClient() + public UiClient(string key) { - + Key = key; } + /// protected override void OnOpen() { base.OnOpen(); var url = Context.WebSocket.Url; - Debug.LogMessage(LogEventLevel.Verbose, "New WebSocket Connection from: {0}", null, url); + this.LogInformation("New WebSocket Connection from: {url}", url); var match = Regex.Match(url.AbsoluteUri, "(?:ws|wss):\\/\\/.*(?:\\/mc\\/api\\/ui\\/join\\/)(.*)"); @@ -117,6 +122,7 @@ namespace PepperDash.Essentials.WebSocketServer Controller.SendMessageObjectToDirectClient(message); } + /// protected override void OnMessage(MessageEventArgs e) { base.OnMessage(e); @@ -128,18 +134,21 @@ namespace PepperDash.Essentials.WebSocketServer } } + /// protected override void OnClose(CloseEventArgs e) { base.OnClose(e); - Debug.LogMessage(LogEventLevel.Verbose, "WebSocket UiClient Closing: {0} reason: {1}", null, e.Code, e.Reason); + this.LogInformation("WebSocket UiClient Closing: {code} reason: {reason}", e.Code, e.Reason); } + /// protected override void OnError(ErrorEventArgs e) { base.OnError(e); - Debug.LogMessage(LogEventLevel.Verbose, "WebSocket UiClient Error: {exception} message: {message}", e.Exception, e.Message); + this.LogError("WebSocket UiClient Error: {message}", e.Message); + this.LogDebug(e.Exception, "Stack Trace"); } } } diff --git a/src/PepperDash.Essentials.MobileControl/WebSocketServer/UiClientContext.cs b/src/PepperDash.Essentials.MobileControl/WebSocketServer/UiClientContext.cs new file mode 100644 index 00000000..6782306f --- /dev/null +++ b/src/PepperDash.Essentials.MobileControl/WebSocketServer/UiClientContext.cs @@ -0,0 +1,31 @@ +namespace PepperDash.Essentials.WebSocketServer +{ + /// + /// Represents a UiClientContext + /// + public class UiClientContext + { + /// + /// Gets or sets the Client + /// + public UiClient Client { get; private set; } + /// + /// Gets or sets the Token + /// + public JoinToken Token { get; private set; } + + public UiClientContext(JoinToken token) + { + Token = token; + } + + /// + /// SetClient method + /// + public void SetClient(UiClient client) + { + Client = client; + } + + } +} diff --git a/src/PepperDash.Essentials.MobileControl/WebSocketServer/Version.cs b/src/PepperDash.Essentials.MobileControl/WebSocketServer/Version.cs new file mode 100644 index 00000000..5552e29b --- /dev/null +++ b/src/PepperDash.Essentials.MobileControl/WebSocketServer/Version.cs @@ -0,0 +1,22 @@ +using Newtonsoft.Json; + + +namespace PepperDash.Essentials.WebSocketServer +{ + /// + /// Represents a Version + /// + public class Version + { + [JsonProperty("serverVersion")] + public string ServerVersion { get; set; } + + [JsonProperty("serverIsRunningOnProcessorHardware")] + public bool ServerIsRunningOnProcessorHardware { get; private set; } + + public Version() + { + ServerIsRunningOnProcessorHardware = true; + } + } +} From 8fc4d21f024747ec717e7cdc65a7d586b50c00fb Mon Sep 17 00:00:00 2001 From: Neil Dorin Date: Thu, 25 Sep 2025 11:50:42 -0600 Subject: [PATCH 06/12] fix: Add DeviceInterfaceSupport property to JoinResponse This commit introduces a new property, `DeviceInterfaceSupport`, to the `JoinResponse` class in the `PepperDash.Essentials.WebSocketServer` namespace. This property is a dictionary that maps strings to `DeviceInterfaceInfo` objects, enhancing support for device interfaces. A summary comment has also been added for clarity. --- .../WebSocketServer/JoinResponse.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/PepperDash.Essentials.MobileControl/WebSocketServer/JoinResponse.cs b/src/PepperDash.Essentials.MobileControl/WebSocketServer/JoinResponse.cs index ce0610e0..ba214db4 100644 --- a/src/PepperDash.Essentials.MobileControl/WebSocketServer/JoinResponse.cs +++ b/src/PepperDash.Essentials.MobileControl/WebSocketServer/JoinResponse.cs @@ -65,7 +65,10 @@ namespace PepperDash.Essentials.WebSocketServer [JsonProperty("enableDebug")] public bool EnableDebug { get; set; } - /// + /// + /// Gets or sets the DeviceInterfaceSupport + /// + [JsonProperty("deviceInterfaceSupport")] public Dictionary DeviceInterfaceSupport { get; set; } } } From d33fd56529262dcd405bc28c45012049fc4c167e Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Thu, 25 Sep 2025 14:47:11 -0500 Subject: [PATCH 07/12] feat: add method to force panel reload Other refactorings for factory method and log statements --- .../MobileControlTouchpanelController.cs | 130 +++++++++--------- 1 file changed, 67 insertions(+), 63 deletions(-) diff --git a/src/PepperDash.Essentials.MobileControl/Touchpanel/MobileControlTouchpanelController.cs b/src/PepperDash.Essentials.MobileControl/Touchpanel/MobileControlTouchpanelController.cs index 61f54832..aedd323e 100644 --- a/src/PepperDash.Essentials.MobileControl/Touchpanel/MobileControlTouchpanelController.cs +++ b/src/PepperDash.Essentials.MobileControl/Touchpanel/MobileControlTouchpanelController.cs @@ -16,8 +16,8 @@ using PepperDash.Essentials.Core.Config; using PepperDash.Essentials.Core.DeviceInfo; using PepperDash.Essentials.Core.DeviceTypeInterfaces; using PepperDash.Essentials.Core.UI; -using Serilog.Events; using Feedback = PepperDash.Essentials.Core.Feedback; +using IPAddress = System.Net.IPAddress; namespace PepperDash.Essentials.Touchpanel { @@ -107,11 +107,14 @@ namespace PepperDash.Essentials.Touchpanel /// public DeviceInfo DeviceInfo => new DeviceInfo(); + /// + /// Gets the list of connected IPs for this IpId + /// public ReadOnlyCollection ConnectedIps => Panel.ConnectedIpList; - private System.Net.IPAddress csIpAddress; + private readonly IPAddress csIpAddress; - private System.Net.IPAddress csSubnetMask; + private readonly IPAddress csSubnetMask; /// @@ -197,8 +200,8 @@ namespace PepperDash.Essentials.Touchpanel var csSubnetMask = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_MASK, csAdapterId); var csIpAddress = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, csAdapterId); - this.csSubnetMask = System.Net.IPAddress.Parse(csSubnetMask); - this.csIpAddress = System.Net.IPAddress.Parse(csIpAddress); + this.csSubnetMask = IPAddress.Parse(csSubnetMask); + this.csIpAddress = IPAddress.Parse(csIpAddress); } catch { @@ -234,7 +237,7 @@ namespace PepperDash.Essentials.Touchpanel { x70Panel.ExtenderApplicationControlReservedSigs.DeviceExtenderSigChange += (e, a) => { - Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, this, $"X70 App Control Device Extender args: {a.Event}:{a.Sig}:{a.Sig.Type}:{a.Sig.BoolValue}:{a.Sig.UShortValue}:{a.Sig.StringValue}"); + this.LogVerbose("X70 App Control Device Extender args: {event}:{sig}:{type}:{boolValue}:{ushortValue}:{stringValue}", a.Event, a.Sig, a.Sig.Type, a.Sig.BoolValue, a.Sig.UShortValue, a.Sig.StringValue); UpdateZoomFeedbacks(); @@ -253,7 +256,7 @@ namespace PepperDash.Essentials.Touchpanel x70Panel.ExtenderZoomRoomAppReservedSigs.DeviceExtenderSigChange += (e, a) => { - Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, this, $"X70 Zoom Room Ap Device Extender args: {a.Event}:{a.Sig}:{a.Sig.Type}:{a.Sig.BoolValue}:{a.Sig.UShortValue}:{a.Sig.StringValue}"); + this.LogVerbose("X70 Zoom Room App Device Extender args: {event}:{sig}:{type}:{boolValue}:{ushortValue}:{stringValue}", a.Event, a.Sig, a.Sig.Type, a.Sig.BoolValue, a.Sig.UShortValue, a.Sig.StringValue); if (a.Sig.Number == x70Panel.ExtenderZoomRoomAppReservedSigs.ZoomRoomIncomingCallFeedback.Number) { @@ -271,7 +274,7 @@ namespace PepperDash.Essentials.Touchpanel DeviceInfo.MacAddress = x70Panel.ExtenderEthernetReservedSigs.MacAddressFeedback.StringValue; DeviceInfo.IpAddress = x70Panel.ExtenderEthernetReservedSigs.IpAddressFeedback.StringValue; - Debug.LogMessage(Serilog.Events.LogEventLevel.Debug, this, $"MAC: {DeviceInfo.MacAddress} IP: {DeviceInfo.IpAddress}"); + this.LogDebug("MAC: {macAddress} IP: {ipAddress}", DeviceInfo.MacAddress, DeviceInfo.IpAddress); var handler = DeviceInfoChanged; @@ -301,7 +304,7 @@ namespace PepperDash.Essentials.Touchpanel { x60withZoomApp.ExtenderApplicationControlReservedSigs.DeviceExtenderSigChange += (e, a) => { - Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, this, $"X60 App Control Device Extender args: {a.Event}:{a.Sig}:{a.Sig.Type}:{a.Sig.BoolValue}:{a.Sig.UShortValue}:{a.Sig.StringValue}"); + this.LogVerbose("X60 App Control Device Extender args: {event}:{sig}:{type}:{boolValue}:{ushortValue}:{stringValue}", a.Event, a.Sig, a.Sig.Type, a.Sig.BoolValue, a.Sig.UShortValue, a.Sig.StringValue); if (a.Sig.Number == x60withZoomApp.ExtenderApplicationControlReservedSigs.HideOpenApplicationFeedback.Number) { @@ -310,7 +313,7 @@ namespace PepperDash.Essentials.Touchpanel }; x60withZoomApp.ExtenderZoomRoomAppReservedSigs.DeviceExtenderSigChange += (e, a) => { - Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, this, $"X60 Zoom Room App Device Extender args: {a.Event}:{a.Sig}:{a.Sig.Type}:{a.Sig.BoolValue}:{a.Sig.UShortValue}:{a.Sig.StringValue}"); + this.LogVerbose("X60 Zoom Room App Device Extender args: {event}:{sig}:{type}:{boolValue}:{ushortValue}:{stringValue}", a.Event, a.Sig, a.Sig.Type, a.Sig.BoolValue, a.Sig.UShortValue, a.Sig.StringValue); if (a.Sig.Number == x60withZoomApp.ExtenderZoomRoomAppReservedSigs.ZoomRoomIncomingCallFeedback.Number) { @@ -327,7 +330,7 @@ namespace PepperDash.Essentials.Touchpanel DeviceInfo.MacAddress = x60withZoomApp.ExtenderEthernetReservedSigs.MacAddressFeedback.StringValue; DeviceInfo.IpAddress = x60withZoomApp.ExtenderEthernetReservedSigs.IpAddressFeedback.StringValue; - Debug.LogMessage(Serilog.Events.LogEventLevel.Debug, this, $"MAC: {DeviceInfo.MacAddress} IP: {DeviceInfo.IpAddress}"); + this.LogDebug("MAC: {macAddress} IP: {ipAddress}", DeviceInfo.MacAddress, DeviceInfo.IpAddress); var handler = DeviceInfoChanged; @@ -389,7 +392,7 @@ namespace PepperDash.Essentials.Touchpanel /// The signal event arguments containing the changed signal information. protected override void ExtenderSystemReservedSigs_DeviceExtenderSigChange(DeviceExtender currentDeviceExtender, SigEventArgs args) { - Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, this, $"System Device Extender args: ${args.Event}:${args.Sig}"); + this.LogVerbose("System Device Extender args: {event}:{sig}", args.Event, args.Sig); } /// @@ -444,7 +447,7 @@ namespace PepperDash.Essentials.Touchpanel var processorIp = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, lanAdapterId); - if(csIpAddress == null || csSubnetMask == null || url == null) + if (csIpAddress == null || csSubnetMask == null || url == null) { this.LogWarning("CS IP Address Subnet Mask or url is null, cannot determine correct IP for URL"); return url; @@ -455,7 +458,7 @@ namespace PepperDash.Essentials.Touchpanel var ip = ConnectedIps.Any(ipInfo => { - if (System.Net.IPAddress.TryParse(ipInfo.DeviceIpAddress, out var parsedIp)) + if (IPAddress.TryParse(ipInfo.DeviceIpAddress, out var parsedIp)) { return csIpAddress.IsInSameSubnet(parsedIp, csSubnetMask); } @@ -489,7 +492,7 @@ namespace PepperDash.Essentials.Touchpanel if (mcList.Count == 0) { - Debug.LogMessage(Serilog.Events.LogEventLevel.Information, this, $"No Mobile Control controller found"); + this.LogError("No Mobile Control controller found"); return; } @@ -501,7 +504,7 @@ namespace PepperDash.Essentials.Touchpanel if (bridge == null) { - Debug.LogMessage(Serilog.Events.LogEventLevel.Information, this, $"No Mobile Control bridge for {_config.DefaultRoomKey} found "); + this.LogInformation("No Mobile Control bridge for {roomKey} found", _config.DefaultRoomKey); return; } @@ -546,7 +549,7 @@ namespace PepperDash.Essentials.Touchpanel { foreach (var feedback in ZoomFeedbacks) { - Debug.LogMessage(Serilog.Events.LogEventLevel.Debug, this, $"Updating {feedback.Key}"); + this.LogDebug("Updating {feedbackKey}", feedback.Key); feedback.FireUpdate(); } } @@ -582,7 +585,7 @@ namespace PepperDash.Essentials.Touchpanel if (Panel is TswX60WithZoomRoomAppReservedSigs) { - Debug.LogMessage(Serilog.Events.LogEventLevel.Information, this, $"X60 panel does not support zoom app"); + this.LogInformation("X60 panel does not support zoom app"); return; } } @@ -658,7 +661,16 @@ namespace PepperDash.Essentials.Touchpanel handler(this, new DeviceInfoEventArgs(DeviceInfo)); } - Debug.LogMessage(Serilog.Events.LogEventLevel.Debug, this, $"MAC: {DeviceInfo.MacAddress} IP: {DeviceInfo.IpAddress}"); + this.LogDebug("MAC: {macAddress} IP: {ipAddress}", DeviceInfo.MacAddress, DeviceInfo.IpAddress); + } + + /// + /// Force a reload of the iframe on the panel connected to this IP ID + /// + public void ReloadIframe() + { + this.LogInformation("Pulsing join 1"); + Panel.PulseBool(1, 100); } } @@ -667,6 +679,8 @@ namespace PepperDash.Essentials.Touchpanel /// public class MobileControlTouchpanelControllerFactory : EssentialsPluginDeviceFactory { + private Dictionary> factories; + /// /// Initializes a new instance of the MobileControlTouchpanelControllerFactory class. /// Sets up supported device type names and minimum framework version requirements. @@ -675,6 +689,31 @@ namespace PepperDash.Essentials.Touchpanel { TypeNames = new List() { "mccrestronapp", "mctsw550", "mctsw750", "mctsw1050", "mctsw560", "mctsw760", "mctsw1060", "mctsw570", "mctsw770", "mcts770", "mctsw1070", "mcts1070", "mcxpanel", "mcdge1000" }; MinimumEssentialsFrameworkVersion = "2.0.0"; + + factories = new Dictionary> + { + {"crestronapp", (id, controlSystem, projectName) => { + var app = new CrestronApp(id, Global.ControlSystem); + app.ParameterProjectName.Value = projectName; + return app; + }}, + {"xpanel", (id, controlSystem, projectName) => new XpanelForHtml5(id, controlSystem)}, + {"tsw550", (id, controlSystem, projectName) => new Tsw550(id, controlSystem)}, + {"tsw552", (id, controlSystem, projectName) => new Tsw552(id, controlSystem)}, + {"tsw560", (id, controlSystem, projectName) => new Tsw560(id, controlSystem)}, + {"tsw750", (id, controlSystem, projectName) => new Tsw750(id, controlSystem)}, + {"tsw752", (id, controlSystem, projectName) => new Tsw752(id, controlSystem)}, + {"tsw760", (id, controlSystem, projectName) => new Tsw760(id, controlSystem)}, + {"tsw1050", (id, controlSystem, projectName) => new Tsw1050(id, controlSystem)}, + {"tsw1052", (id, controlSystem, projectName) => new Tsw1052(id, controlSystem)}, + {"tsw1060", (id, controlSystem, projectName) => new Tsw1060(id, controlSystem)}, + {"tsw570", (id, controlSystem, projectName) => new Tsw570(id, controlSystem)}, + {"tsw770", (id, controlSystem, projectName) => new Tsw770(id, controlSystem)}, + {"ts770", (id, controlSystem, projectName) => new Ts770(id, controlSystem)}, + {"tsw1070", (id, controlSystem, projectName) => new Tsw1070(id, controlSystem)}, + {"ts1070", (id, controlSystem, projectName) => new Ts1070(id, controlSystem)}, + {"dge1000", (id, controlSystem, projectName) => new Dge1000(id, controlSystem)} + }; } /// @@ -694,10 +733,10 @@ namespace PepperDash.Essentials.Touchpanel if (panel == null) { - Debug.LogMessage(Serilog.Events.LogEventLevel.Information, "Unable to create Touchpanel for type {0}. Touchpanel Controller WILL NOT function correctly", dc.Type); + Debug.LogError("Unable to create Touchpanel for type {type}. Touchpanel Controller WILL NOT function correctly", dc.Type); } - Debug.LogMessage(Serilog.Events.LogEventLevel.Debug, "Factory Attempting to create new MobileControlTouchpanelController"); + Debug.LogDebug("Factory Attempting to create new MobileControlTouchpanelController"); var panelController = new MobileControlTouchpanelController(dc.Key, dc.Name, panel, props); @@ -707,56 +746,21 @@ namespace PepperDash.Essentials.Touchpanel private BasicTriListWithSmartObject GetPanelForType(string type, uint id, string projectName) { type = type.ToLower().Replace("mc", ""); + try { - if (type == "crestronapp") + if (!factories.TryGetValue(type, out var buildCrestronHardwareDevice)) { - var app = new CrestronApp(id, Global.ControlSystem); - app.ParameterProjectName.Value = projectName; - return app; - } - else if (type == "xpanel") - return new XpanelForHtml5(id, Global.ControlSystem); - else if (type == "tsw550") - return new Tsw550(id, Global.ControlSystem); - else if (type == "tsw552") - return new Tsw552(id, Global.ControlSystem); - else if (type == "tsw560") - return new Tsw560(id, Global.ControlSystem); - else if (type == "tsw750") - return new Tsw750(id, Global.ControlSystem); - else if (type == "tsw752") - return new Tsw752(id, Global.ControlSystem); - else if (type == "tsw760") - return new Tsw760(id, Global.ControlSystem); - else if (type == "tsw1050") - return new Tsw1050(id, Global.ControlSystem); - else if (type == "tsw1052") - return new Tsw1052(id, Global.ControlSystem); - else if (type == "tsw1060") - return new Tsw1060(id, Global.ControlSystem); - else if (type == "tsw570") - return new Tsw570(id, Global.ControlSystem); - else if (type == "tsw770") - return new Tsw770(id, Global.ControlSystem); - else if (type == "ts770") - return new Ts770(id, Global.ControlSystem); - else if (type == "tsw1070") - return new Tsw1070(id, Global.ControlSystem); - else if (type == "ts1070") - return new Ts1070(id, Global.ControlSystem); - else if (type == "dge1000") - return new Dge1000(id, Global.ControlSystem); - else - - { - Debug.LogMessage(Serilog.Events.LogEventLevel.Warning, "WARNING: Cannot create TSW controller with type '{0}'", type); + Debug.LogError("Cannot create TSW controller with type {type}", type); return null; } + + return buildCrestronHardwareDevice(id, Global.ControlSystem, projectName); } catch (Exception e) { - Debug.LogMessage(Serilog.Events.LogEventLevel.Warning, "WARNING: Cannot create TSW base class. Panel will not function: {0}", e.Message); + Debug.LogError("Cannot create TSW base class. Panel will not function: {message}", e.Message); + Debug.LogDebug(e, "Stack Trace: "); return null; } } From 4747c16b023e6fa37ef0b0cffddfaf2f94293611 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Fri, 26 Sep 2025 21:17:28 -0500 Subject: [PATCH 08/12] build: delete all local build clzs on build --- src/Directory.Build.targets | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/src/Directory.Build.targets b/src/Directory.Build.targets index 1e7c0909..ce6c0eed 100644 --- a/src/Directory.Build.targets +++ b/src/Directory.Build.targets @@ -23,23 +23,32 @@ $(TargetDir)$(TargetName).$(Version).$(TargetFramework).cpz - - + + + + + - + - - + + + + + - + - - + + + + + - + From 087d0a11493e7bc45c1ee246582a697662f8f062 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Fri, 26 Sep 2025 21:20:10 -0500 Subject: [PATCH 09/12] chore: move classes and interfaces to individual files --- .../ICustomMobileControl.cs | 11 ++ .../DeviceTypeInterfaces/IMobileControl.cs | 161 +++++------------- .../IMobileControlAction.cs | 15 ++ ...bileControlCrestronTouchpanelController.cs | 17 ++ .../IMobileControlMessage.cs | 18 ++ .../IMobileControlMessenger.cs | 31 ++++ .../IMobileControlRoomMessenger.cs | 33 ++++ .../IMobileControlTouchpanelController.cs | 31 ++++ .../Messengers/DeviceEventMessageBase.cs | 17 ++ .../Messengers/DeviceMessageBase.cs | 39 +++++ .../Messengers/DeviceStateMessageBase.cs | 27 +++ .../Messengers/MessengerBase.cs | 70 +------- .../ClientSpecificUpdateRequest.cs | 20 +++ ...Interfaces.cs => IDelayedConfiguration.cs} | 0 .../MobileControlSystemController.cs | 29 ---- .../UserCodeChanged.cs | 17 ++ 16 files changed, 316 insertions(+), 220 deletions(-) create mode 100644 src/PepperDash.Essentials.Core/DeviceTypeInterfaces/ICustomMobileControl.cs create mode 100644 src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IMobileControlAction.cs create mode 100644 src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IMobileControlCrestronTouchpanelController.cs create mode 100644 src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IMobileControlMessage.cs create mode 100644 src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IMobileControlMessenger.cs create mode 100644 src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IMobileControlRoomMessenger.cs create mode 100644 src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IMobileControlTouchpanelController.cs create mode 100644 src/PepperDash.Essentials.MobileControl.Messengers/Messengers/DeviceEventMessageBase.cs create mode 100644 src/PepperDash.Essentials.MobileControl.Messengers/Messengers/DeviceMessageBase.cs create mode 100644 src/PepperDash.Essentials.MobileControl.Messengers/Messengers/DeviceStateMessageBase.cs create mode 100644 src/PepperDash.Essentials.MobileControl/ClientSpecificUpdateRequest.cs rename src/PepperDash.Essentials.MobileControl/{Interfaces.cs => IDelayedConfiguration.cs} (100%) create mode 100644 src/PepperDash.Essentials.MobileControl/UserCodeChanged.cs diff --git a/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/ICustomMobileControl.cs b/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/ICustomMobileControl.cs new file mode 100644 index 00000000..3ae19216 --- /dev/null +++ b/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/ICustomMobileControl.cs @@ -0,0 +1,11 @@ +using PepperDash.Core; + +namespace PepperDash.Essentials.Core.DeviceTypeInterfaces +{ + /// + /// Use this interface on a device or room if it uses custom Mobile Control messengers + /// + public interface ICustomMobileControl : IKeyed + { + } +} \ No newline at end of file diff --git a/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IMobileControl.cs b/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IMobileControl.cs index 09420b7e..0b1760fa 100644 --- a/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IMobileControl.cs +++ b/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IMobileControl.cs @@ -1,155 +1,72 @@ using System; -using System.Collections.ObjectModel; -using Crestron.SimplSharpPro; -using Newtonsoft.Json; using Newtonsoft.Json.Linq; using PepperDash.Core; namespace PepperDash.Essentials.Core.DeviceTypeInterfaces { - /// - /// Use this interface on a device or room if it uses custom Mobile Control messengers - /// - public interface ICustomMobileControl : IKeyed - { - } - - /*/// - /// Describes a MobileControlSystemController - /// - public interface IMobileControl : IKeyed - { - void CreateMobileControlRoomBridge(IEssentialsRoom room, IMobileControl parent); - - void LinkSystemMonitorToAppServer(); - }*/ /// /// Defines the contract for IMobileControl /// public interface IMobileControl : IKeyed { + /// + /// Gets the Host + /// string Host { get; } + /// + /// Gets the Client App URL + /// string ClientAppUrl { get; } + /// + /// Gets the System UUID + /// string SystemUuid { get; } + /// + /// Gets the ApiOnlineAndAuthorized feedback + /// BoolFeedback ApiOnlineAndAuthorized { get; } + /// + /// Sends the message object to the AppServer + /// + /// Message to send void SendMessageObject(IMobileControlMessage o); + /// + /// Adds an action for a messenger + /// + /// Messenger type. Must implement IMobileControlMessenger + /// messenger to register + /// action to add void AddAction(T messenger, Action action) where T : IMobileControlMessenger; + /// + /// Removes an action for a messenger + /// + /// key for action void RemoveAction(string key); + /// + /// Adds a device messenger + /// + /// Messenger to add void AddDeviceMessenger(IMobileControlMessenger messenger); + /// + /// Check if a device messenger exists + /// + /// Messenger key to find bool CheckForDeviceMessenger(string key); + /// + /// Get a Room Messenger by key + /// + /// messenger key to find + /// Messenger if found, null otherwise IMobileControlRoomMessenger GetRoomMessenger(string key); - - } - - /// - /// Defines the contract for IMobileControlMessenger - /// - public interface IMobileControlMessenger : IKeyed - { - IMobileControl AppServerController { get; } - string MessagePath { get; } - - string DeviceKey { get; } - void RegisterWithAppServer(IMobileControl appServerController); - } - - public interface IMobileControlMessage - { - [JsonProperty("type")] - string Type { get; } - - [JsonProperty("clientId", NullValueHandling = NullValueHandling.Ignore)] - string ClientId { get; } - - [JsonProperty("content", NullValueHandling = NullValueHandling.Ignore)] - JToken Content { get; } - - } - - /// - /// Defines the contract for IMobileControlRoomMessenger - /// - public interface IMobileControlRoomMessenger : IKeyed - { - event EventHandler UserCodeChanged; - - event EventHandler UserPromptedForCode; - - event EventHandler ClientJoined; - - event EventHandler AppUrlChanged; - - string UserCode { get; } - - string QrCodeUrl { get; } - - string QrCodeChecksum { get; } - - string McServerUrl { get; } - - string RoomName { get; } - - string AppUrl { get; } - - void UpdateAppUrl(string url); - } - - /// - /// Defines the contract for IMobileControlAction - /// - public interface IMobileControlAction - { - IMobileControlMessenger Messenger { get; } - - Action Action { get; } - } - - /// - /// Defines the contract for IMobileControlTouchpanelController - /// - public interface IMobileControlTouchpanelController : IKeyed - { - /// - /// The default room key for the controller - /// - string DefaultRoomKey { get; } - - /// - /// Sets the application URL for the controller - /// - /// The application URL - void SetAppUrl(string url); - - /// - /// Indicates whether the controller uses a direct server connection - /// - bool UseDirectServer { get; } - - /// - /// Indicates whether the controller is a Zoom Room controller - /// - bool ZoomRoomController { get; } - } - - /// - /// Describes a MobileControl Crestron Touchpanel Controller - /// This interface extends the IMobileControlTouchpanelController to include connected IP information - /// - public interface IMobileControlCrestronTouchpanelController : IMobileControlTouchpanelController - { - /// - /// Gets a collection of connected IP information for the touchpanel controller - /// - ReadOnlyCollection ConnectedIps { get; } } } \ No newline at end of file diff --git a/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IMobileControlAction.cs b/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IMobileControlAction.cs new file mode 100644 index 00000000..982deaae --- /dev/null +++ b/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IMobileControlAction.cs @@ -0,0 +1,15 @@ +using System; +using Newtonsoft.Json.Linq; + +namespace PepperDash.Essentials.Core.DeviceTypeInterfaces +{ + /// + /// Defines the contract for IMobileControlAction + /// + public interface IMobileControlAction + { + IMobileControlMessenger Messenger { get; } + + Action Action { get; } + } +} \ No newline at end of file diff --git a/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IMobileControlCrestronTouchpanelController.cs b/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IMobileControlCrestronTouchpanelController.cs new file mode 100644 index 00000000..9878fcbd --- /dev/null +++ b/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IMobileControlCrestronTouchpanelController.cs @@ -0,0 +1,17 @@ +using System.Collections.ObjectModel; +using Crestron.SimplSharpPro; + +namespace PepperDash.Essentials.Core.DeviceTypeInterfaces +{ + /// + /// Describes a MobileControl Crestron Touchpanel Controller + /// This interface extends the IMobileControlTouchpanelController to include connected IP information + /// + public interface IMobileControlCrestronTouchpanelController : IMobileControlTouchpanelController + { + /// + /// Gets a collection of connected IP information for the touchpanel controller + /// + ReadOnlyCollection ConnectedIps { get; } + } +} \ No newline at end of file diff --git a/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IMobileControlMessage.cs b/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IMobileControlMessage.cs new file mode 100644 index 00000000..41645da2 --- /dev/null +++ b/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IMobileControlMessage.cs @@ -0,0 +1,18 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace PepperDash.Essentials.Core.DeviceTypeInterfaces +{ + public interface IMobileControlMessage + { + [JsonProperty("type")] + string Type { get; } + + [JsonProperty("clientId", NullValueHandling = NullValueHandling.Ignore)] + string ClientId { get; } + + [JsonProperty("content", NullValueHandling = NullValueHandling.Ignore)] + JToken Content { get; } + + } +} \ No newline at end of file diff --git a/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IMobileControlMessenger.cs b/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IMobileControlMessenger.cs new file mode 100644 index 00000000..178289e4 --- /dev/null +++ b/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IMobileControlMessenger.cs @@ -0,0 +1,31 @@ +using PepperDash.Core; + +namespace PepperDash.Essentials.Core.DeviceTypeInterfaces +{ + /// + /// Defines the contract for IMobileControlMessenger + /// + public interface IMobileControlMessenger : IKeyed + { + /// + /// Parent controller for this messenger + /// + IMobileControl AppServerController { get; } + + /// + /// Path to listen for messages + /// + string MessagePath { get; } + + /// + /// Key of the device this messenger is associated with + /// + string DeviceKey { get; } + + /// + /// Register this messenger with the AppServerController + /// + /// + void RegisterWithAppServer(IMobileControl appServerController); + } +} \ No newline at end of file diff --git a/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IMobileControlRoomMessenger.cs b/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IMobileControlRoomMessenger.cs new file mode 100644 index 00000000..6f4d9a17 --- /dev/null +++ b/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IMobileControlRoomMessenger.cs @@ -0,0 +1,33 @@ +using System; +using PepperDash.Core; + +namespace PepperDash.Essentials.Core.DeviceTypeInterfaces +{ + /// + /// Defines the contract for IMobileControlRoomMessenger + /// + public interface IMobileControlRoomMessenger : IKeyed + { + event EventHandler UserCodeChanged; + + event EventHandler UserPromptedForCode; + + event EventHandler ClientJoined; + + event EventHandler AppUrlChanged; + + string UserCode { get; } + + string QrCodeUrl { get; } + + string QrCodeChecksum { get; } + + string McServerUrl { get; } + + string RoomName { get; } + + string AppUrl { get; } + + void UpdateAppUrl(string url); + } +} \ No newline at end of file diff --git a/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IMobileControlTouchpanelController.cs b/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IMobileControlTouchpanelController.cs new file mode 100644 index 00000000..e0d5f05d --- /dev/null +++ b/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IMobileControlTouchpanelController.cs @@ -0,0 +1,31 @@ +using PepperDash.Core; + +namespace PepperDash.Essentials.Core.DeviceTypeInterfaces +{ + /// + /// Defines the contract for IMobileControlTouchpanelController + /// + public interface IMobileControlTouchpanelController : IKeyed + { + /// + /// The default room key for the controller + /// + string DefaultRoomKey { get; } + + /// + /// Sets the application URL for the controller + /// + /// The application URL + void SetAppUrl(string url); + + /// + /// Indicates whether the controller uses a direct server connection + /// + bool UseDirectServer { get; } + + /// + /// Indicates whether the controller is a Zoom Room controller + /// + bool ZoomRoomController { get; } + } +} \ No newline at end of file diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/DeviceEventMessageBase.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/DeviceEventMessageBase.cs new file mode 100644 index 00000000..0960758c --- /dev/null +++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/DeviceEventMessageBase.cs @@ -0,0 +1,17 @@ +using Newtonsoft.Json; + +namespace PepperDash.Essentials.AppServer.Messengers +{ + /// + /// 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/DeviceMessageBase.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/DeviceMessageBase.cs new file mode 100644 index 00000000..54a6ec36 --- /dev/null +++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/DeviceMessageBase.cs @@ -0,0 +1,39 @@ +using Newtonsoft.Json; + +namespace PepperDash.Essentials.AppServer.Messengers +{ + /// + /// Base class for device messages that include the type of message + /// + public abstract class DeviceMessageBase + { + /// + /// The device key + /// + [JsonProperty("key")] + /// + /// Gets or sets the 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; + + /// + /// Gets or sets the MessageBasePath + /// + [JsonProperty("messageBasePath")] + + public string MessageBasePath { get; set; } + } + +} \ No newline at end of file diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/DeviceStateMessageBase.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/DeviceStateMessageBase.cs new file mode 100644 index 00000000..87f19e3f --- /dev/null +++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/DeviceStateMessageBase.cs @@ -0,0 +1,27 @@ +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace PepperDash.Essentials.AppServer.Messengers +{ + /// + /// Represents a DeviceStateMessageBase + /// + public class DeviceStateMessageBase : DeviceMessageBase + { + /// + /// The interfaces implmented by the device sending the messsage + /// + [JsonProperty("interfaces")] + public List Interfaces { get; private set; } + + /// + /// Sets the interfaces implemented by the device sending the message + /// + /// + public void SetInterfaces(List interfaces) + { + Interfaces = interfaces; + } + } + +} \ 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 index 4d96cf81..9ed9029b 100644 --- a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/MessengerBase.cs +++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/MessengerBase.cs @@ -287,72 +287,4 @@ namespace PepperDash.Essentials.AppServer.Messengers } } - - /// - /// Base class for device messages that include the type of message - /// - public abstract class DeviceMessageBase - { - /// - /// The device key - /// - [JsonProperty("key")] - /// - /// Gets or sets the 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; - - /// - /// Gets or sets the MessageBasePath - /// - [JsonProperty("messageBasePath")] - - public string MessageBasePath { get; set; } - } - - /// - /// Represents a DeviceStateMessageBase - /// - public class DeviceStateMessageBase : DeviceMessageBase - { - /// - /// The interfaces implmented by the device sending the messsage - /// - [JsonProperty("interfaces")] - public List Interfaces { get; private set; } - - /// - /// Sets the interfaces implemented by the device sending the message - /// - /// - 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/ClientSpecificUpdateRequest.cs b/src/PepperDash.Essentials.MobileControl/ClientSpecificUpdateRequest.cs new file mode 100644 index 00000000..beddd2f6 --- /dev/null +++ b/src/PepperDash.Essentials.MobileControl/ClientSpecificUpdateRequest.cs @@ -0,0 +1,20 @@ +using System; + +namespace PepperDash.Essentials +{ + /// + /// Represents a ClientSpecificUpdateRequest + /// + public class ClientSpecificUpdateRequest + { + public ClientSpecificUpdateRequest(Action action) + { + ResponseMethod = action; + } + + /// + /// Gets or sets the ResponseMethod + /// + public Action ResponseMethod { get; private set; } + } +} diff --git a/src/PepperDash.Essentials.MobileControl/Interfaces.cs b/src/PepperDash.Essentials.MobileControl/IDelayedConfiguration.cs similarity index 100% rename from src/PepperDash.Essentials.MobileControl/Interfaces.cs rename to src/PepperDash.Essentials.MobileControl/IDelayedConfiguration.cs diff --git a/src/PepperDash.Essentials.MobileControl/MobileControlSystemController.cs b/src/PepperDash.Essentials.MobileControl/MobileControlSystemController.cs index 640cbecf..736bf69c 100644 --- a/src/PepperDash.Essentials.MobileControl/MobileControlSystemController.cs +++ b/src/PepperDash.Essentials.MobileControl/MobileControlSystemController.cs @@ -2420,33 +2420,4 @@ namespace PepperDash.Essentials CrestronConsole.ConsoleCommandResponse("Usage: mobilehttprequest:N get/post url\r"); } } - - /// - /// Represents a ClientSpecificUpdateRequest - /// - public class ClientSpecificUpdateRequest - { - public ClientSpecificUpdateRequest(Action action) - { - ResponseMethod = action; - } - - /// - /// Gets or sets the ResponseMethod - /// - public Action ResponseMethod { get; private set; } - } - - /// - /// Represents a UserCodeChanged - /// - public class UserCodeChanged - { - public Action UpdateUserCode { get; private set; } - - public UserCodeChanged(Action updateMethod) - { - UpdateUserCode = updateMethod; - } - } } diff --git a/src/PepperDash.Essentials.MobileControl/UserCodeChanged.cs b/src/PepperDash.Essentials.MobileControl/UserCodeChanged.cs new file mode 100644 index 00000000..ab899167 --- /dev/null +++ b/src/PepperDash.Essentials.MobileControl/UserCodeChanged.cs @@ -0,0 +1,17 @@ +using System; + +namespace PepperDash.Essentials +{ + /// + /// Represents a UserCodeChanged + /// + public class UserCodeChanged + { + public Action UpdateUserCode { get; private set; } + + public UserCodeChanged(Action updateMethod) + { + UpdateUserCode = updateMethod; + } + } +} From bb694b4200e0e4ffc1f7124200318684e0bc7c37 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Fri, 26 Sep 2025 21:31:54 -0500 Subject: [PATCH 10/12] feat: enable subscription logic for messengers In order to help control traffic over the websocket, a subscription feature has been added: * A config option, `enableMessengerSubscriptions` has been added * When true, the MessengerBase class will assume that any message sent using the `PostStatusMessage` that has a valid client ID wants to send any subsequent unsolicited updates to that same client * The client's ID will be added a list of subscribed client IDs * Any subsequent messages sent using the `PostStatusMessage` methods that have a null clientId will ONLY be sent to subscribed clients * When a client disconnects, it will be removed from the list of subscribed clients This should cut down drastically on the traffic to the UI, especially when combined with requesting partial status updates from a device rather than the entire state. --- ...MobileControlMessengerWithSubscriptions.cs | 23 ++++ .../Messengers/MessengerBase.cs | 105 +++++++++++++++++- .../MobileControlConfig.cs | 15 +++ .../MobileControlSystemController.cs | 24 +++- .../MobileControlWebsocketServer.cs | 42 ++++++- .../WebSocketServer/UiClient.cs | 39 +++++++ 6 files changed, 239 insertions(+), 9 deletions(-) create mode 100644 src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IMobileControlMessengerWithSubscriptions.cs diff --git a/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IMobileControlMessengerWithSubscriptions.cs b/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IMobileControlMessengerWithSubscriptions.cs new file mode 100644 index 00000000..887f1789 --- /dev/null +++ b/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IMobileControlMessengerWithSubscriptions.cs @@ -0,0 +1,23 @@ +using PepperDash.Core; + +namespace PepperDash.Essentials.Core.DeviceTypeInterfaces +{ + /// + /// Defines the contract for IMobileControlMessenger + /// + public interface IMobileControlMessengerWithSubscriptions : IMobileControlMessenger + { + /// + /// Unsubscribe a client from this messenger + /// + /// + void UnsubscribeClient(string clientId); + + /// + /// Register this messenger with the AppServerController + /// + /// parent for this messenger + /// Enable messenger subscriptions + void RegisterWithAppServer(IMobileControl appServerController, bool enableMessengerSubscriptions); + } +} \ 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 index 9ed9029b..a7210a13 100644 --- a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/MessengerBase.cs +++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/MessengerBase.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; -using Newtonsoft.Json; +using Crestron.SimplSharp.Net; using Newtonsoft.Json.Linq; using PepperDash.Core; using PepperDash.Core.Logging; @@ -13,13 +13,26 @@ namespace PepperDash.Essentials.AppServer.Messengers /// /// Provides a messaging bridge /// - public abstract class MessengerBase : EssentialsDevice, IMobileControlMessenger + public abstract class MessengerBase : EssentialsDevice, IMobileControlMessengerWithSubscriptions { /// /// The device this messenger is associated with /// protected IKeyName _device; + /// + /// Enable subscriptions + /// + protected bool enableMessengerSubscriptions; + + /// + /// List of clients subscribed to this messenger + /// + /// + /// Unsoliciited feedback from a device in a messenger will ONLY be sent to devices in this subscription list. When a client disconnects, it's ID will be removed from the collection. + /// + protected HashSet SubscriberIds = new HashSet(); + private readonly List _deviceInterfaces; private readonly Dictionary> _actions = new Dictionary>(); @@ -93,6 +106,21 @@ namespace PepperDash.Essentials.AppServer.Messengers RegisterActions(); } + /// + /// Register this messenger with appserver controller + /// + /// Parent controller for this messenger + /// Enable subscriptions + public void RegisterWithAppServer(IMobileControl appServerController, bool enableMessengerSubscriptions) + { + this.enableMessengerSubscriptions = enableMessengerSubscriptions; + 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 @@ -103,7 +131,7 @@ namespace PepperDash.Essentials.AppServer.Messengers return; } - Debug.LogMessage(Serilog.Events.LogEventLevel.Debug, "Executing action for path {path}", this, path); + this.LogDebug("Executing action for path {path}", path); action(id, content); } @@ -117,7 +145,6 @@ namespace PepperDash.Essentials.AppServer.Messengers { if (_actions.ContainsKey(path)) { - //Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, $"Messenger {Key} already has action registered at {path}", this); return; } @@ -154,6 +181,52 @@ namespace PepperDash.Essentials.AppServer.Messengers } + /// + /// Add client to the susbscription list for unsolicited feedback + /// + /// Client ID to add + protected void SubscribeClient(string clientId) + { + if (!enableMessengerSubscriptions) + { + this.LogWarning("Messenger subscriptions not enabled"); + return; + } + + if (SubscriberIds.Any(id => id == clientId)) + { + this.LogVerbose("Client {clientId} already subscribed", clientId); + return; + } + + SubscriberIds.Add(clientId); + + this.LogDebug("Client {clientId} subscribed", clientId); + } + + /// + /// Remove a client from the subscription list + /// + /// Client ID to remove + public void UnsubscribeClient(string clientId) + { + if (!enableMessengerSubscriptions) + { + this.LogWarning("Messenger subscriptions not enabled"); + return; + } + + if (!SubscriberIds.Any(i => i == clientId)) + { + this.LogVerbose("Client with ID {clientId} is not subscribed", clientId); + return; + } + + SubscriberIds.RemoveWhere((i) => i == clientId); + + this.LogInformation("Client with ID {clientId} unsubscribed", clientId); + } + /// /// Helper for posting status message /// @@ -228,11 +301,33 @@ namespace PepperDash.Essentials.AppServer.Messengers { try { + // Allow for legacy method to continue without subscriptions + if (!enableMessengerSubscriptions) + { + AppServerController?.SendMessageObject(new MobileControlMessage { Type = !string.IsNullOrEmpty(type) ? type : MessagePath, ClientId = clientId, Content = content }); + return; + } + + // handle subscription feedback + // If client is null or empty, this message is unsolicited feedback. Iterate through the subscriber list and send to all interested parties + if (string.IsNullOrEmpty(clientId)) + { + foreach (var client in SubscriberIds) + { + AppServerController?.SendMessageObject(new MobileControlMessage { Type = !string.IsNullOrEmpty(type) ? type : MessagePath, ClientId = client, Content = content }); + } + + return; + } + + SubscribeClient(clientId); + 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); + this.LogError("Exception posting status message: {message}", ex.Message); + this.LogDebug(ex, "Stack Trace: "); } } diff --git a/src/PepperDash.Essentials.MobileControl/MobileControlConfig.cs b/src/PepperDash.Essentials.MobileControl/MobileControlConfig.cs index 6c130f6d..ec7219a3 100644 --- a/src/PepperDash.Essentials.MobileControl/MobileControlConfig.cs +++ b/src/PepperDash.Essentials.MobileControl/MobileControlConfig.cs @@ -38,6 +38,12 @@ namespace PepperDash.Essentials /// [JsonProperty("enableApiServer")] public bool EnableApiServer { get; set; } = true; + + /// + /// Enable subscriptions for Messengers + /// + [JsonProperty("enableMessengerSubscriptions")] + public bool EnableMessengerSubscriptions { get; set; } } /// @@ -78,6 +84,15 @@ namespace PepperDash.Essentials [JsonProperty("csLanUiDeviceKeys")] public List CSLanUiDeviceKeys { get; set; } + /// + /// Get or set the Secure property + /// + /// + /// Indicates whether the connection is secure (HTTPS). + /// + [JsonProperty("Secure")] + public bool Secure { get; set; } + /// /// Initializes a new instance of the MobileControlDirectServerPropertiesConfig class. /// diff --git a/src/PepperDash.Essentials.MobileControl/MobileControlSystemController.cs b/src/PepperDash.Essentials.MobileControl/MobileControlSystemController.cs index 736bf69c..bd04b0e3 100644 --- a/src/PepperDash.Essentials.MobileControl/MobileControlSystemController.cs +++ b/src/PepperDash.Essentials.MobileControl/MobileControlSystemController.cs @@ -54,7 +54,10 @@ namespace PepperDash.Essentials StringComparer.InvariantCultureIgnoreCase ); - public Dictionary> ActionDictionary => _actionDictionary; + /// + /// Actions + /// + public ReadOnlyDictionary> ActionDictionary => new ReadOnlyDictionary>(_actionDictionary); private readonly GenericQueue _receiveQueue; private readonly List _roomBridges = @@ -66,6 +69,16 @@ namespace PepperDash.Essentials private readonly Dictionary _defaultMessengers = new Dictionary(); + /// + /// Get the custom messengers with subscriptions + /// + public ReadOnlyDictionary Messengers => new ReadOnlyDictionary(_messengers.Values.OfType().ToDictionary(k => k.Key, v => v)); + + /// + /// Get the default messengers + /// + public ReadOnlyDictionary DefaultMessengers => new ReadOnlyDictionary(_defaultMessengers.Values.OfType().ToDictionary(k => k.Key, v => v)); + private readonly GenericQueue _transmitToServerQueue; private readonly GenericQueue _transmitToClientsQueue; @@ -1199,8 +1212,7 @@ namespace PepperDash.Essentials if (_initialized) { - this.LogDebug("Registering messenger {messengerKey} AFTER initialization", messenger.Key); - messenger.RegisterWithAppServer(this); + RegisterMessengerWithServer(messenger); } } @@ -1241,6 +1253,12 @@ namespace PepperDash.Essentials messenger.MessagePath ); + if (messenger is IMobileControlMessengerWithSubscriptions subMessenger) + { + subMessenger.RegisterWithAppServer(this, Config.EnableMessengerSubscriptions); + return; + } + messenger.RegisterWithAppServer(this); } diff --git a/src/PepperDash.Essentials.MobileControl/WebSocketServer/MobileControlWebsocketServer.cs b/src/PepperDash.Essentials.MobileControl/WebSocketServer/MobileControlWebsocketServer.cs index f2f5003a..eb60857e 100644 --- a/src/PepperDash.Essentials.MobileControl/WebSocketServer/MobileControlWebsocketServer.cs +++ b/src/PepperDash.Essentials.MobileControl/WebSocketServer/MobileControlWebsocketServer.cs @@ -4,6 +4,8 @@ using System.ComponentModel; using System.IO; using System.Linq; using System.Net.Http; +using System.Security.Authentication; +using System.Security.Cryptography.X509Certificates; using System.Text; using Crestron.SimplSharp; using Crestron.SimplSharp.WebScripting; @@ -35,6 +37,10 @@ namespace PepperDash.Essentials.WebSocketServer private readonly string appConfigFileName = "_config.local.json"; private readonly string appConfigCsFileName = "_config.cs.json"; + private const string certificateName = "selfCres"; + + private const string certificatePassword = "cres12345"; + /// /// Where the key is the join token and the value is the room key /// @@ -260,7 +266,41 @@ namespace PepperDash.Essentials.WebSocketServer _server.OnPost += Server_OnPost; } - _server.Log.Output = (data, level) => this.LogInformation("WebSocket Server Log [{level}]: {data}", level, data); + if (_parent.Config.DirectServer.Secure) + { + this.LogInformation("Adding SSL Configuration to server"); + _server.SslConfiguration = new ServerSslConfiguration(new X509Certificate2($"\\user\\{certificateName}.pfx", certificatePassword)) + { + ClientCertificateRequired = false, + CheckCertificateRevocation = false, + EnabledSslProtocols = SslProtocols.Tls12 | SslProtocols.Tls11 + }; + } + + _server.Log.Output = (data, message) => + { + switch (data.Level) + { + case LogLevel.Trace: + this.LogVerbose(data.Message); + break; + case LogLevel.Debug: + this.LogDebug(data.Message); + break; + case LogLevel.Info: + this.LogInformation(data.Message); + break; + case LogLevel.Warn: + this.LogWarning(data.Message); + break; + case LogLevel.Error: + this.LogError(data.Message); + break; + case LogLevel.Fatal: + this.LogFatal(data.Message); + break; + } + }; CrestronEnvironment.ProgramStatusEventHandler += CrestronEnvironment_ProgramStatusEventHandler; diff --git a/src/PepperDash.Essentials.MobileControl/WebSocketServer/UiClient.cs b/src/PepperDash.Essentials.MobileControl/WebSocketServer/UiClient.cs index d1d4524d..3cdd183b 100644 --- a/src/PepperDash.Essentials.MobileControl/WebSocketServer/UiClient.cs +++ b/src/PepperDash.Essentials.MobileControl/WebSocketServer/UiClient.cs @@ -22,14 +22,29 @@ namespace PepperDash.Essentials.WebSocketServer /// public string Key { get; private set; } + /// + /// Gets or sets the mobile control system controller that handles this client's messages + /// public MobileControlSystemController Controller { get; set; } + /// + /// Gets or sets the room key that this client is associated with + /// public string RoomKey { get; set; } + /// + /// The unique identifier for this client instance + /// private string _clientId; + /// + /// The timestamp when this client connection was established + /// private DateTime _connectionTime; + /// + /// Gets the duration that this client has been connected. Returns zero if not currently connected. + /// public TimeSpan ConnectedDuration { get @@ -45,6 +60,10 @@ namespace PepperDash.Essentials.WebSocketServer } } + /// + /// Initializes a new instance of the UiClient class with the specified key + /// + /// The unique key to identify this client public UiClient(string key) { Key = key; @@ -99,11 +118,21 @@ namespace PepperDash.Essentials.WebSocketServer // TODO: Future: Check token to see if there's already an open session using that token and reject/close the session } + /// + /// Handles the UserCodeChanged event from a room bridge and sends the updated user code to the client + /// + /// The room bridge that raised the event + /// Event arguments private void Bridge_UserCodeChanged(object sender, EventArgs e) { SendUserCodeToClient((MobileControlEssentialsRoomBridge)sender, _clientId); } + /// + /// Sends the current user code and QR code URL to the specified client + /// + /// The room bridge containing the user code information + /// The ID of the client to send the information to private void SendUserCodeToClient(MobileControlBridgeBase bridge, string clientId) { var content = new @@ -140,6 +169,16 @@ namespace PepperDash.Essentials.WebSocketServer base.OnClose(e); this.LogInformation("WebSocket UiClient Closing: {code} reason: {reason}", e.Code, e.Reason); + + foreach (var messenger in Controller.Messengers) + { + messenger.Value.UnsubscribeClient(_clientId); + } + + foreach (var messenger in Controller.DefaultMessengers) + { + messenger.Value.UnsubscribeClient(_clientId); + } } /// From 7c90027578c271f4dd8d7106181406a499157ba8 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Fri, 26 Sep 2025 21:47:06 -0500 Subject: [PATCH 11/12] feat: set direct server debug level via console command The `mobilewsdebug` console command can now be used to set the internal websocket logging level for both the API client and the direct server at the same time. --- .../MobileControlSystemController.cs | 45 ++++++++++++++----- .../MobileControlWebsocketServer.cs | 6 +++ 2 files changed, 39 insertions(+), 12 deletions(-) diff --git a/src/PepperDash.Essentials.MobileControl/MobileControlSystemController.cs b/src/PepperDash.Essentials.MobileControl/MobileControlSystemController.cs index bd04b0e3..73861291 100644 --- a/src/PepperDash.Essentials.MobileControl/MobileControlSystemController.cs +++ b/src/PepperDash.Essentials.MobileControl/MobileControlSystemController.cs @@ -1353,11 +1353,30 @@ namespace PepperDash.Essentials { Log = { - Output = (data, s) => - this.LogDebug( - "Message from websocket: {message}", - data - ) + Output = (data, message) => + { + switch (data.Level) + { + case LogLevel.Trace: + this.LogVerbose(data.Message); + break; + case LogLevel.Debug: + this.LogDebug(data.Message); + break; + case LogLevel.Info: + this.LogInformation(data.Message); + break; + case LogLevel.Warn: + this.LogWarning(data.Message); + break; + case LogLevel.Error: + this.LogError(data.Message); + break; + case LogLevel.Fatal: + this.LogFatal(data.Message); + break; + } + } } }; @@ -1401,13 +1420,13 @@ namespace PepperDash.Essentials private void SetWebsocketDebugLevel(string cmdparameters) { - if (CrestronEnvironment.ProgramCompatibility == eCrestronSeries.Series4) - { - this.LogInformation( - "Setting websocket log level not currently allowed on 4 series." - ); - return; // Web socket log level not currently allowed in series4 - } + // if (CrestronEnvironment.ProgramCompatibility == eCrestronSeries.Series4) + // { + // this.LogInformation( + // "Setting websocket log level not currently allowed on 4 series." + // ); + // return; // Web socket log level not currently allowed in series4 + // } if (string.IsNullOrEmpty(cmdparameters)) { @@ -1433,6 +1452,8 @@ namespace PepperDash.Essentials _wsClient2.Log.Level = _wsLogLevel; } + _directServer?.SetWebsocketLogLevel(_wsLogLevel); + CrestronConsole.ConsoleCommandResponse($"Websocket log level set to {debugLevel}"); } catch diff --git a/src/PepperDash.Essentials.MobileControl/WebSocketServer/MobileControlWebsocketServer.cs b/src/PepperDash.Essentials.MobileControl/WebSocketServer/MobileControlWebsocketServer.cs index eb60857e..d15185b0 100644 --- a/src/PepperDash.Essentials.MobileControl/WebSocketServer/MobileControlWebsocketServer.cs +++ b/src/PepperDash.Essentials.MobileControl/WebSocketServer/MobileControlWebsocketServer.cs @@ -326,6 +326,12 @@ namespace PepperDash.Essentials.WebSocketServer } } + public void SetWebsocketLogLevel(LogLevel level) + { + CrestronConsole.ConsoleCommandResponse($"Setting direct server debug level to {level}", level.ToString()); + _server.Log.Level = level; + } + private void AddClientsForTouchpanels() { var touchpanels = DeviceManager.AllDevices From f91f43576828b6d8cbee941a7af8b1adf8761927 Mon Sep 17 00:00:00 2001 From: Neil Dorin Date: Tue, 30 Sep 2025 12:11:25 -0600 Subject: [PATCH 12/12] fix: Add null check for CurrentScenario in MobileControlSystem This change introduces a check for `_roomCombiner.CurrentScenario` being `null`. When it is `null`, a `MobileControlMessage` is created with the type `/system/roomKey`, `clientId`, and `roomKey`, which is then sent to the client. This improves the handling of scenarios without a current scenario by ensuring relevant room key information is communicated. --- .../MobileControlSystemController.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/PepperDash.Essentials.MobileControl/MobileControlSystemController.cs b/src/PepperDash.Essentials.MobileControl/MobileControlSystemController.cs index 7994afc3..0579c1f6 100644 --- a/src/PepperDash.Essentials.MobileControl/MobileControlSystemController.cs +++ b/src/PepperDash.Essentials.MobileControl/MobileControlSystemController.cs @@ -2233,8 +2233,21 @@ namespace PepperDash.Essentials return; } + if (_roomCombiner.CurrentScenario == null) + { + var message = new MobileControlMessage + { + Type = "/system/roomKey", + ClientId = clientId, + Content = roomKey + }; + SendMessageObject(message); + return; + } + if (!_roomCombiner.CurrentScenario.UiMap.ContainsKey(roomKey)) { + this.LogWarning( "Unable to find correct roomKey for {roomKey} in current scenario. Returning {roomKey} as roomKey", roomKey);