From d96edfa8d0e457f1c35a9faf85366606eb8e5964 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Thu, 26 Jun 2025 13:50:42 -0400 Subject: [PATCH 01/28] fix: end devcommstatus with cr-lf instead of just -lf --- src/PepperDash.Essentials.Core/Devices/DeviceManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PepperDash.Essentials.Core/Devices/DeviceManager.cs b/src/PepperDash.Essentials.Core/Devices/DeviceManager.cs index ae8eba03..5cd921b8 100644 --- a/src/PepperDash.Essentials.Core/Devices/DeviceManager.cs +++ b/src/PepperDash.Essentials.Core/Devices/DeviceManager.cs @@ -248,7 +248,7 @@ namespace PepperDash.Essentials.Core foreach (var dev in Devices.Values.OfType()) { - CrestronConsole.ConsoleCommandResponse($"{dev}: {dev.CommunicationMonitor.Status}{Environment.NewLine}"); + CrestronConsole.ConsoleCommandResponse($"{dev}: {dev.CommunicationMonitor.Status}\r\n"); } } From 253b2cddaf0b10bbf6522a9d609b015d21a7fa14 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Thu, 26 Jun 2025 13:54:24 -0400 Subject: [PATCH 02/28] fix: print device key & name instead of type --- src/PepperDash.Essentials.Core/Devices/DeviceManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PepperDash.Essentials.Core/Devices/DeviceManager.cs b/src/PepperDash.Essentials.Core/Devices/DeviceManager.cs index 5cd921b8..eaf696ba 100644 --- a/src/PepperDash.Essentials.Core/Devices/DeviceManager.cs +++ b/src/PepperDash.Essentials.Core/Devices/DeviceManager.cs @@ -248,7 +248,7 @@ namespace PepperDash.Essentials.Core foreach (var dev in Devices.Values.OfType()) { - CrestronConsole.ConsoleCommandResponse($"{dev}: {dev.CommunicationMonitor.Status}\r\n"); + CrestronConsole.ConsoleCommandResponse($"{dev.Key}|{dev.Name}: {dev.CommunicationMonitor.Status}\r\n"); } } From c9b32057364a011d8896f38e49ec2749765d368e Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Thu, 26 Jun 2025 14:14:04 -0400 Subject: [PATCH 03/28] fix: return --- if the device was created without a name --- src/PepperDash.Core/Device.cs | 169 ++++++++++-------- .../Devices/DeviceManager.cs | 2 +- .../Devices/EssentialsDevice.cs | 16 +- 3 files changed, 101 insertions(+), 86 deletions(-) diff --git a/src/PepperDash.Core/Device.cs b/src/PepperDash.Core/Device.cs index fda30c4c..ef9f6111 100644 --- a/src/PepperDash.Core/Device.cs +++ b/src/PepperDash.Core/Device.cs @@ -11,35 +11,35 @@ namespace PepperDash.Core public class Device : IKeyName { - /// - /// Unique Key - /// + /// + /// Unique Key + /// public string Key { get; protected set; } /// /// Name of the devie /// - public string Name { get; protected set; } - /// - /// - /// + public string Name { get; protected set; } + /// + /// + /// public bool Enabled { get; protected set; } - ///// - ///// A place to store reference to the original config object, if any. These values should - ///// NOT be used as properties on the device as they are all publicly-settable values. - ///// - //public DeviceConfig Config { get; private set; } - ///// - ///// Helper method to check if Config exists - ///// - //public bool HasConfig { get { return Config != null; } } + ///// + ///// A place to store reference to the original config object, if any. These values should + ///// NOT be used as properties on the device as they are all publicly-settable values. + ///// + //public DeviceConfig Config { get; private set; } + ///// + ///// Helper method to check if Config exists + ///// + //public bool HasConfig { get { return Config != null; } } List _PreActivationActions; List _PostActivationActions; - /// - /// - /// + /// + /// + /// public static Device DefaultDevice { get { return _DefaultDevice; } } static Device _DefaultDevice = new Device("Default", "Default"); @@ -54,27 +54,27 @@ namespace PepperDash.Core Name = ""; } - /// - /// Constructor with key and name - /// - /// - /// + /// + /// Constructor with key and name + /// + /// + /// public Device(string key, string name) : this(key) { Name = name; } - //public Device(DeviceConfig config) - // : this(config.Key, config.Name) - //{ - // Config = config; - //} + //public Device(DeviceConfig config) + // : this(config.Key, config.Name) + //{ + // Config = config; + //} - /// - /// Adds a pre activation action - /// - /// + /// + /// Adds a pre activation action + /// + /// public void AddPreActivationAction(Action act) { if (_PreActivationActions == null) @@ -82,10 +82,10 @@ namespace PepperDash.Core _PreActivationActions.Add(act); } - /// - /// Adds a post activation action - /// - /// + /// + /// Adds a post activation action + /// + /// public void AddPostActivationAction(Action act) { if (_PostActivationActions == null) @@ -93,55 +93,58 @@ namespace PepperDash.Core _PostActivationActions.Add(act); } - /// - /// Executes the preactivation actions - /// - public void PreActivate() - { - if (_PreActivationActions != null) - _PreActivationActions.ForEach(a => { + /// + /// Executes the preactivation actions + /// + public void PreActivate() + { + if (_PreActivationActions != null) + _PreActivationActions.ForEach(a => + { try { a.Invoke(); - } catch (Exception e) - { + } + catch (Exception e) + { Debug.LogMessage(e, "Error in PreActivationAction: " + e.Message, this); } - }); - } + }); + } /// /// Gets this device ready to be used in the system. Runs any added pre-activation items, and /// all post-activation at end. Classes needing additional logic to /// run should override CustomActivate() /// - public bool Activate() + public bool Activate() { - //if (_PreActivationActions != null) - // _PreActivationActions.ForEach(a => a.Invoke()); + //if (_PreActivationActions != null) + // _PreActivationActions.ForEach(a => a.Invoke()); var result = CustomActivate(); - //if(result && _PostActivationActions != null) - // _PostActivationActions.ForEach(a => a.Invoke()); - return result; + //if(result && _PostActivationActions != null) + // _PostActivationActions.ForEach(a => a.Invoke()); + return result; } - /// - /// Executes the postactivation actions - /// - public void PostActivate() - { - if (_PostActivationActions != null) - _PostActivationActions.ForEach(a => { - try - { - a.Invoke(); - } - catch (Exception e) - { - Debug.LogMessage(e, "Error in PostActivationAction: " + e.Message, this); - } - }); - } + /// + /// Executes the postactivation actions + /// + public void PostActivate() + { + if (_PostActivationActions != null) + _PostActivationActions.ForEach(a => + { + try + { + a.Invoke(); + } + catch (Exception e) + { + Debug.LogMessage(e, "Error in PostActivationAction: " + e.Message, this); + } + }); + } /// /// Called in between Pre and PostActivationActions when Activate() is called. @@ -158,14 +161,14 @@ namespace PepperDash.Core /// public virtual bool Deactivate() { return true; } - /// - /// Call this method to start communications with a device. Overriding classes do not need to call base.Initialize() - /// - public virtual void Initialize() - { - } + /// + /// Call this method to start communications with a device. Overriding classes do not need to call base.Initialize() + /// + public virtual void Initialize() + { + } - /// + /// /// Helper method to check object for bool value false and fire an Action method /// /// Should be of type bool, others will be ignored @@ -175,5 +178,15 @@ namespace PepperDash.Core if (o is bool && !(bool)o) a(); } + /// + /// Returns a string representation of the object, including its key and name. + /// + /// The returned string is formatted as "{Key} - {Name}". If the Name property is + /// null or empty, "---" is used in place of the name. + /// A string that represents the object, containing the key and name in the format "{Key} - {Name}". + public override string ToString() + { + return string.Format("{0} - {1}", Key, string.IsNullOrEmpty(Name) ? "---" : Name); + } } } \ No newline at end of file diff --git a/src/PepperDash.Essentials.Core/Devices/DeviceManager.cs b/src/PepperDash.Essentials.Core/Devices/DeviceManager.cs index eaf696ba..5cd921b8 100644 --- a/src/PepperDash.Essentials.Core/Devices/DeviceManager.cs +++ b/src/PepperDash.Essentials.Core/Devices/DeviceManager.cs @@ -248,7 +248,7 @@ namespace PepperDash.Essentials.Core foreach (var dev in Devices.Values.OfType()) { - CrestronConsole.ConsoleCommandResponse($"{dev.Key}|{dev.Name}: {dev.CommunicationMonitor.Status}\r\n"); + CrestronConsole.ConsoleCommandResponse($"{dev}: {dev.CommunicationMonitor.Status}\r\n"); } } diff --git a/src/PepperDash.Essentials.Core/Devices/EssentialsDevice.cs b/src/PepperDash.Essentials.Core/Devices/EssentialsDevice.cs index bdb8b45a..778f9226 100644 --- a/src/PepperDash.Essentials.Core/Devices/EssentialsDevice.cs +++ b/src/PepperDash.Essentials.Core/Devices/EssentialsDevice.cs @@ -20,19 +20,20 @@ namespace PepperDash.Essentials.Core public event EventHandler Initialized; private bool _isInitialized; - public bool IsInitialized { + public bool IsInitialized + { get { return _isInitialized; } - private set - { + private set + { if (_isInitialized == value) return; - + _isInitialized = value; if (_isInitialized) { Initialized?.Invoke(this, new EventArgs()); } - } + } } protected EssentialsDevice(string key) @@ -80,8 +81,9 @@ namespace PepperDash.Essentials.Core /// /// Override this method to build and create custom Mobile Control Messengers during the Activation phase /// - protected virtual void CreateMobileControlMessengers() { - + protected virtual void CreateMobileControlMessengers() + { + } } From 19e799f11d2d104b74f05e5d83b76f58d262afc6 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Thu, 26 Jun 2025 17:12:36 -0400 Subject: [PATCH 04/28] fix: debounce device info events In some cases, multiple device info update events are triggering, causing the queue to be flooded with multiple unneccessary messages containing the same info. This clogs the queue and makes it harder for UIs to come online when Essentials restarts, especially in systems with a lot of NVX devices. The events are now debounced. If there are no new messages for 1 second, then the MC message is sent out. --- .../Messengers/DeviceInfoMessenger.cs | 58 +++++++++++++++++-- 1 file changed, 53 insertions(+), 5 deletions(-) diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/DeviceInfoMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/DeviceInfoMessenger.cs index ffd58948..c588195e 100644 --- a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/DeviceInfoMessenger.cs +++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/DeviceInfoMessenger.cs @@ -2,27 +2,69 @@ using Newtonsoft.Json.Linq; using PepperDash.Core; using PepperDash.Essentials.Core.DeviceInfo; +using System.Timers; namespace PepperDash.Essentials.AppServer.Messengers { + /// + /// Facilitates communication of device information by providing mechanisms for status updates and device + /// information reporting. + /// + /// The class integrates with an to manage device-specific information. It uses a debounce timer to limit the + /// frequency of updates, ensuring efficient communication. The timer is initialized with a 1-second interval and + /// is disabled by default. This class also subscribes to device information change events and provides actions for + /// reporting full device status and triggering updates. public class DeviceInfoMessenger : MessengerBase { private readonly IDeviceInfoProvider _deviceInfoProvider; + + private readonly Timer debounceTimer; + + /// + /// Initializes a new instance of the class, which facilitates communication + /// of device information. + /// + /// The messenger uses a debounce timer to limit the frequency of certain operations. The + /// timer is initialized with a 1-second interval and is disabled by default. + /// A unique identifier for the messenger instance. + /// The path used for sending and receiving messages. + /// An implementation of that provides device-specific information. public DeviceInfoMessenger(string key, string messagePath, IDeviceInfoProvider device) : base(key, messagePath, device as Device) { _deviceInfoProvider = device; - } + debounceTimer = new Timer(1000) + { + Enabled = false, + AutoReset = false + }; + + debounceTimer.Elapsed += DebounceTimer_Elapsed; + } + + private void DebounceTimer_Elapsed(object sender, ElapsedEventArgs e) + { + PostStatusMessage(JToken.FromObject(new + { + deviceInfo = _deviceInfoProvider.DeviceInfo + })); + } + + /// + /// Registers actions and event handlers for device information updates and status reporting. + /// + /// This method sets up actions for handling device status updates and reporting full + /// device status. It also subscribes to the event to + /// trigger debounced updates when the device information changes. protected override void RegisterActions() { base.RegisterActions(); _deviceInfoProvider.DeviceInfoChanged += (o, a) => { - PostStatusMessage(JToken.FromObject(new - { - deviceInfo = a.DeviceInfo - })); + debounceTimer.Stop(); + debounceTimer.Start(); }; AddAction("/fullStatus", (id, context) => PostStatusMessage(new DeviceInfoStateMessage @@ -34,6 +76,12 @@ namespace PepperDash.Essentials.AppServer.Messengers } } + /// + /// Represents a message containing the state information of a device, including detailed device information. + /// + /// This class is used to encapsulate the state of a device along with its associated + /// information. It extends to provide additional details about the + /// device. public class DeviceInfoStateMessage : DeviceStateMessageBase { [JsonProperty("deviceInfo")] From 1a9e1087de4a58a3ac59cd833c534faa9ad45e13 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Fri, 27 Jun 2025 10:32:21 -0400 Subject: [PATCH 05/28] fix: add property to disable auto mode --- .../Room/Combining/EssentialsRoomCombiner.cs | 105 ++++++++++++++++++ .../EssentialsRoomCombinerPropertiesConfig.cs | 45 ++++++-- 2 files changed, 143 insertions(+), 7 deletions(-) diff --git a/src/PepperDash.Essentials.Core/Room/Combining/EssentialsRoomCombiner.cs b/src/PepperDash.Essentials.Core/Room/Combining/EssentialsRoomCombiner.cs index f0fb5e98..681f8810 100644 --- a/src/PepperDash.Essentials.Core/Room/Combining/EssentialsRoomCombiner.cs +++ b/src/PepperDash.Essentials.Core/Room/Combining/EssentialsRoomCombiner.cs @@ -10,6 +10,13 @@ using System.Threading.Tasks; namespace PepperDash.Essentials.Core { + /// + /// Represents a device that manages room combinations by controlling partitions and scenarios. + /// + /// The allows for dynamic configuration of room + /// combinations based on partition states and predefined scenarios. It supports both automatic and manual modes + /// for managing room combinations. In automatic mode, the device determines the current room combination scenario + /// based on partition sensor states. In manual mode, scenarios can be set explicitly by the user. public class EssentialsRoomCombiner : EssentialsDevice, IEssentialsRoomCombiner { private EssentialsRoomCombinerPropertiesConfig _propertiesConfig; @@ -18,6 +25,9 @@ namespace PepperDash.Essentials.Core private List _rooms; + /// + /// Gets a list of rooms represented as key-name pairs. + /// public List Rooms { get @@ -28,6 +38,12 @@ namespace PepperDash.Essentials.Core private bool _isInAutoMode; + /// + /// Gets or sets a value indicating whether the system is operating in automatic mode. + /// + /// Changing this property triggers an update event via + /// IsInAutoModeFeedback.FireUpdate(). Ensure that any event listeners are properly configured to handle + /// this update. public bool IsInAutoMode { get @@ -52,6 +68,19 @@ namespace PepperDash.Essentials.Core private Mutex _scenarioChange = new Mutex(); + /// + /// Initializes a new instance of the class, which manages room combination + /// scenarios and partition states. + /// + /// The class is designed to handle dynamic room + /// combination scenarios based on partition states. It supports both automatic and manual modes for managing + /// room combinations. By default, the instance starts in automatic mode unless the + /// specifies otherwise. After activation, the room combiner initializes partition state providers and sets up + /// the initial room configuration. Additionally, it subscribes to the event to ensure proper initialization of dependent devices + /// before determining or setting the room combination scenario. + /// The unique identifier for the room combiner instance. + /// The configuration properties for the room combiner, including default settings and debounce times. public EssentialsRoomCombiner(string key, EssentialsRoomCombinerPropertiesConfig props) : base(key) { @@ -246,8 +275,16 @@ namespace PepperDash.Essentials.Core #region IEssentialsRoomCombiner Members + /// + /// Occurs when the room combination scenario changes. + /// + /// This event is triggered whenever the configuration or state of the room combination + /// changes. Subscribers can use this event to update their logic or UI based on the new scenario. public event EventHandler RoomCombinationScenarioChanged; + /// + /// Gets the current room combination scenario. + /// public IRoomCombinationScenario CurrentScenario { get @@ -256,10 +293,25 @@ namespace PepperDash.Essentials.Core } } + /// + /// Gets the feedback indicating whether the system is currently in auto mode. + /// public BoolFeedback IsInAutoModeFeedback { get; private set; } + /// + /// Enables auto mode for the room combiner and its partitions, allowing automatic room combination scenarios to + /// be determined. + /// + /// Auto mode allows the room combiner to automatically adjust its configuration based on + /// the state of its partitions. If auto mode is disabled in the configuration, this method logs a warning and + /// does not enable auto mode. public void SetAutoMode() { + if(_propertiesConfig.DisableAutoMode) + { + this.LogWarning("Auto mode is disabled for this room combiner. Cannot set to auto mode."); + return; + } IsInAutoMode = true; foreach (var partition in Partitions) @@ -270,6 +322,12 @@ namespace PepperDash.Essentials.Core DetermineRoomCombinationScenario(); } + /// + /// Switches the system to manual mode, disabling automatic operations. + /// + /// This method sets the system to manual mode by updating the mode state and propagates + /// the change to all partitions. Once in manual mode, automatic operations are disabled for the system and its + /// partitions. public void SetManualMode() { IsInAutoMode = false; @@ -280,6 +338,11 @@ namespace PepperDash.Essentials.Core } } + /// + /// Toggles the current mode between automatic and manual. + /// + /// If the current mode is automatic, this method switches to manual mode. If the + /// current mode is manual, it switches to automatic mode. public void ToggleMode() { if (IsInAutoMode) @@ -292,10 +355,22 @@ namespace PepperDash.Essentials.Core } } + /// + /// Gets the collection of room combination scenarios. + /// public List RoomCombinationScenarios { get; private set; } + /// + /// Gets the collection of partition controllers managed by this instance. + /// public List Partitions { get; private set; } + /// + /// Toggles the state of the partition identified by the specified partition key. + /// + /// If no partition with the specified key exists, the method performs no + /// action. + /// The key of the partition whose state is to be toggled. This value cannot be null or empty. public void TogglePartitionState(string partitionKey) { var partition = Partitions.FirstOrDefault((p) => p.Key.Equals(partitionKey)); @@ -306,6 +381,17 @@ namespace PepperDash.Essentials.Core } } + /// + /// Sets the room combination scenario based on the specified scenario key. + /// + /// This method manually adjusts the partition states according to the specified + /// scenario. If the application is in auto mode, the operation will not proceed, and a log message will be + /// generated indicating that the mode must be set to manual first. If the specified scenario key does not + /// match any existing scenario, a debug log message will be generated. For each partition state in the + /// scenario, the corresponding partition will be updated to either "Present" or "Not Present" based on the + /// scenario's configuration. If a partition key in the scenario cannot be found, a debug log message will be + /// generated. + /// The key identifying the room combination scenario to apply. This must match the key of an existing scenario. public void SetRoomCombinationScenario(string scenarioKey) { if (IsInAutoMode) @@ -354,13 +440,32 @@ namespace PepperDash.Essentials.Core #endregion } + /// + /// Provides a factory for creating instances of devices. + /// + /// This factory is responsible for constructing devices + /// based on the provided configuration. It supports the type name "essentialsroomcombiner" for device + /// creation. public class EssentialsRoomCombinerFactory : EssentialsDeviceFactory { + /// + /// Initializes a new instance of the class. + /// + /// This factory is used to create instances of room combiners with the specified type + /// names. By default, the factory includes the type name "essentialsroomcombiner". public EssentialsRoomCombinerFactory() { TypeNames = new List { "essentialsroomcombiner" }; } + /// + /// Creates and initializes a new instance of the device. + /// + /// This method uses the provided device configuration to extract the properties and + /// create an device. Ensure that the configuration contains valid + /// properties for the device to be created successfully. + /// The device configuration containing the key and properties required to build the device. + /// A new instance of initialized with the specified configuration. public override EssentialsDevice BuildDevice(PepperDash.Essentials.Core.Config.DeviceConfig dc) { Debug.LogMessage(LogEventLevel.Debug, "Factory Attempting to create new EssentialsRoomCombiner Device"); diff --git a/src/PepperDash.Essentials.Core/Room/Combining/EssentialsRoomCombinerPropertiesConfig.cs b/src/PepperDash.Essentials.Core/Room/Combining/EssentialsRoomCombinerPropertiesConfig.cs index 745f303f..cd6f3f17 100644 --- a/src/PepperDash.Essentials.Core/Room/Combining/EssentialsRoomCombinerPropertiesConfig.cs +++ b/src/PepperDash.Essentials.Core/Room/Combining/EssentialsRoomCombinerPropertiesConfig.cs @@ -1,10 +1,4 @@ - - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Crestron.SimplSharp; +using System.Collections.Generic; using PepperDash.Core; @@ -17,6 +11,13 @@ namespace PepperDash.Essentials.Core /// public class EssentialsRoomCombinerPropertiesConfig { + /// + /// Gets or sets a value indicating whether the system operates in automatic mode. + /// Some systems don't have partitions sensors, and show shouldn't allow auto mode to be turned on. When this is true in the configuration, + /// auto mode won't be allowed to be turned on. + /// + public bool DisableAutoMode { get; set; } + /// /// The list of partitions that device the rooms /// @@ -47,6 +48,9 @@ namespace PepperDash.Essentials.Core [JsonProperty("defaultScenarioKey")] public string defaultScenarioKey { get; set; } + /// + /// Gets or sets the debounce time, in seconds, for scenario changes. + /// [JsonProperty("scenarioChangeDebounceTimeSeconds")] public int ScenarioChangeDebounceTimeSeconds { get; set; } } @@ -56,9 +60,15 @@ namespace PepperDash.Essentials.Core /// public class PartitionConfig : IKeyName { + /// + /// Gets or sets the unique key associated with the object. + /// [JsonProperty("key")] public string Key { get; set; } + /// + /// Gets or sets the name associated with the object. + /// [JsonProperty("name")] public string Name { get; set; } @@ -80,12 +90,21 @@ namespace PepperDash.Essentials.Core /// public class RoomCombinationScenarioConfig : IKeyName { + /// + /// Gets or sets the key associated with the object. + /// [JsonProperty("key")] public string Key { get; set; } + /// + /// Gets or sets the name associated with the object. + /// [JsonProperty("name")] public string Name { get; set; } + /// + /// Gets or sets the collection of partition states. + /// [JsonProperty("partitionStates")] public List PartitionStates { get; set; } @@ -95,9 +114,15 @@ namespace PepperDash.Essentials.Core [JsonProperty("uiMap")] public Dictionary UiMap { get; set; } + /// + /// Gets or sets the list of actions to be performed during device activation. + /// [JsonProperty("activationActions")] public List ActivationActions { get; set; } + /// + /// Gets or sets the list of actions to be performed when a device is deactivated. + /// [JsonProperty("deactivationActions")] public List DeactivationActions { get; set; } } @@ -107,9 +132,15 @@ namespace PepperDash.Essentials.Core /// public class PartitionState { + /// + /// Gets or sets the partition key used to group and organize data within a storage system. + /// [JsonProperty("partitionKey")] public string PartitionKey { get; set; } + /// + /// Gets or sets a value indicating whether a partition is currently present. + /// [JsonProperty("partitionSensedState")] public bool PartitionPresent { get; set; } } From c1809459a62f2ef6a8457d0969ffe89ae5896665 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Fri, 27 Jun 2025 10:37:49 -0400 Subject: [PATCH 06/28] chore: format property name correctly for JSON --- .../Room/Combining/EssentialsRoomCombinerPropertiesConfig.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/PepperDash.Essentials.Core/Room/Combining/EssentialsRoomCombinerPropertiesConfig.cs b/src/PepperDash.Essentials.Core/Room/Combining/EssentialsRoomCombinerPropertiesConfig.cs index cd6f3f17..fec7e380 100644 --- a/src/PepperDash.Essentials.Core/Room/Combining/EssentialsRoomCombinerPropertiesConfig.cs +++ b/src/PepperDash.Essentials.Core/Room/Combining/EssentialsRoomCombinerPropertiesConfig.cs @@ -16,6 +16,7 @@ namespace PepperDash.Essentials.Core /// Some systems don't have partitions sensors, and show shouldn't allow auto mode to be turned on. When this is true in the configuration, /// auto mode won't be allowed to be turned on. /// + [JsonProperty("disableAutoMode")] public bool DisableAutoMode { get; set; } /// From 96ac266d24e9dd1102408bc2206d4b3f69280b87 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Fri, 27 Jun 2025 10:45:42 -0400 Subject: [PATCH 07/28] fix: room combiner messenger sends disableAutoMode property --- .../Room/Combining/EssentialsRoomCombiner.cs | 11 ++++ .../Room/Combining/IEssentialsRoomCombiner.cs | 21 +++++++ .../IEssentialsRoomCombinerMessenger.cs | 55 +++++++++++++++++++ 3 files changed, 87 insertions(+) diff --git a/src/PepperDash.Essentials.Core/Room/Combining/EssentialsRoomCombiner.cs b/src/PepperDash.Essentials.Core/Room/Combining/EssentialsRoomCombiner.cs index 681f8810..d2a16739 100644 --- a/src/PepperDash.Essentials.Core/Room/Combining/EssentialsRoomCombiner.cs +++ b/src/PepperDash.Essentials.Core/Room/Combining/EssentialsRoomCombiner.cs @@ -62,6 +62,17 @@ namespace PepperDash.Essentials.Core } } + /// + /// Gets a value indicating whether automatic mode is disabled. + /// + public bool DisableAutoMode + { + get + { + return _propertiesConfig.DisableAutoMode; + } + } + private CTimer _scenarioChangeDebounceTimer; private int _scenarioChangeDebounceTimeSeconds = 10; // default to 10s diff --git a/src/PepperDash.Essentials.Core/Room/Combining/IEssentialsRoomCombiner.cs b/src/PepperDash.Essentials.Core/Room/Combining/IEssentialsRoomCombiner.cs index fefdc2da..04dfd16f 100644 --- a/src/PepperDash.Essentials.Core/Room/Combining/IEssentialsRoomCombiner.cs +++ b/src/PepperDash.Essentials.Core/Room/Combining/IEssentialsRoomCombiner.cs @@ -28,9 +28,20 @@ namespace PepperDash.Essentials.Core [JsonIgnore] BoolFeedback IsInAutoModeFeedback {get;} + /// + /// Gets a value indicating whether the automatic mode is disabled. + /// + [JsonProperty("disableAutoMode")] + bool DisableAutoMode { get; } + /// + /// Gets a value indicating whether the system is operating in automatic mode. + /// [JsonProperty("isInAutoMode")] bool IsInAutoMode { get; } + /// + /// Gets the collection of rooms associated with the current object. + /// [JsonProperty("rooms")] List Rooms { get; } @@ -74,6 +85,13 @@ namespace PepperDash.Essentials.Core void SetRoomCombinationScenario(string scenarioKey); } + /// + /// Represents a scenario for combining rooms, including activation, deactivation, and associated state. + /// + /// This interface defines the behavior for managing room combination scenarios, including + /// activation and deactivation, tracking the active state, and managing related partition states and UI mappings. + /// Implementations of this interface are expected to handle the logic for room combinations based on the provided + /// partition states and UI mappings. public interface IRoomCombinationScenario : IKeyName { /// @@ -82,6 +100,9 @@ namespace PepperDash.Essentials.Core [JsonIgnore] BoolFeedback IsActiveFeedback { get; } + /// + /// Gets a value indicating whether the entity is active. + /// [JsonProperty("isActive")] bool IsActive { get; } diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IEssentialsRoomCombinerMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IEssentialsRoomCombinerMessenger.cs index a29d7b9e..1752b567 100644 --- a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IEssentialsRoomCombinerMessenger.cs +++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IEssentialsRoomCombinerMessenger.cs @@ -8,16 +8,42 @@ using System.Collections.Generic; namespace PepperDash.Essentials.AppServer.Messengers { + /// + /// Provides messaging functionality for managing room combination scenarios and partition states in an instance. Enables external systems to interact with the room combiner via + /// predefined actions and status updates. + /// + /// This class facilitates communication with an by + /// exposing actions for toggling modes, managing partitions, and setting room combination scenarios. It also + /// listens for feedback changes and broadcasts status updates to connected systems. Typical usage involves + /// registering actions for external commands and handling feedback events to synchronize state changes. public class IEssentialsRoomCombinerMessenger : MessengerBase { private readonly IEssentialsRoomCombiner _roomCombiner; + /// + /// Initializes a new instance of the class, which facilitates + /// messaging for an instance. + /// + /// This class is designed to enable communication and interaction with an through the specified messaging path. Ensure that the parameter is not null when creating an instance. + /// The unique key identifying this messenger instance. + /// The path used for messaging operations. + /// The instance associated with this messenger. public IEssentialsRoomCombinerMessenger(string key, string messagePath, IEssentialsRoomCombiner roomCombiner) : base(key, messagePath, roomCombiner as IKeyName) { _roomCombiner = roomCombiner; } + /// + /// Registers actions and event handlers for managing room combination scenarios and partition states. + /// + /// This method sets up various actions that can be triggered via specific endpoints, + /// such as toggling modes, setting room combination scenarios, and managing partition states. It also + /// subscribes to feedback events to update the status when changes occur in room combination scenarios or + /// partition states. protected override void RegisterActions() { AddAction("/fullStatus", (id, content) => SendFullStatus()); @@ -107,6 +133,7 @@ namespace PepperDash.Essentials.AppServer.Messengers var message = new IEssentialsRoomCombinerStateMessage { + DisableAutoMode = _roomCombiner.DisableAutoMode, IsInAutoMode = _roomCombiner.IsInAutoMode, CurrentScenario = _roomCombiner.CurrentScenario, Rooms = rooms, @@ -132,20 +159,48 @@ namespace PepperDash.Essentials.AppServer.Messengers } } + /// + /// Represents the state message for a room combiner system, providing information about the current configuration, + /// operational mode, and associated rooms, partitions, and scenarios. + /// + /// This class is used to encapsulate the state of a room combiner system, including its current + /// mode of operation, active room combination scenario, and the list of rooms and partitions involved. It is + /// typically serialized and transmitted to communicate the state of the system. public class IEssentialsRoomCombinerStateMessage : DeviceStateMessageBase { + /// + /// Gets or sets a value indicating whether automatic mode is disabled. + /// + [JsonProperty("disableAutoMode", NullValueHandling = NullValueHandling.Ignore)] + public bool DisableAutoMode { get; set; } + + /// + /// Gets or sets a value indicating whether the system is operating in automatic mode. + /// [JsonProperty("isInAutoMode", NullValueHandling = NullValueHandling.Ignore)] public bool IsInAutoMode { get; set; } + /// + /// Gets or sets the current room combination scenario. + /// [JsonProperty("currentScenario", NullValueHandling = NullValueHandling.Ignore)] public IRoomCombinationScenario CurrentScenario { get; set; } + /// + /// Gets or sets the collection of rooms associated with the entity. + /// [JsonProperty("rooms", NullValueHandling = NullValueHandling.Ignore)] public List Rooms { get; set; } + /// + /// Gets or sets the collection of room combination scenarios. + /// [JsonProperty("roomCombinationScenarios", NullValueHandling = NullValueHandling.Ignore)] public List RoomCombinationScenarios { get; set; } + /// + /// Gets or sets the collection of partition controllers. + /// [JsonProperty("partitions", NullValueHandling = NullValueHandling.Ignore)] public List Partitions { get; set; } } From 8f1fb86d37cd3b387172a51fff2c920351b167aa Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Mon, 7 Jul 2025 09:49:01 -0500 Subject: [PATCH 08/28] fix: add NVX network port info interface --- .../INvxNetworkPortInformation.cs | 89 +++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 src/PepperDash.Essentials.Core/DeviceTypeInterfaces/INvxNetworkPortInformation.cs diff --git a/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/INvxNetworkPortInformation.cs b/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/INvxNetworkPortInformation.cs new file mode 100644 index 00000000..3fcb7e26 --- /dev/null +++ b/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/INvxNetworkPortInformation.cs @@ -0,0 +1,89 @@ +using Crestron.SimplSharpPro.DM.Streaming; +using System; +using System.Collections.Generic; + +namespace PepperDash.Essentials.Core +{ + /// + /// Represents a collection of network port information and provides notifications when the information changes. + /// + /// This interface is designed to provide access to a list of network port details and to notify + /// subscribers when the port information is updated. Implementations of this interface should ensure that the event is raised whenever the collection + /// changes. + public interface INvxNetworkPortInformation + { + /// + /// Occurs when the port information changes. + /// + /// This event is triggered whenever there is a change in the port information, such as + /// updates to port settings or status. Subscribers can handle this event to respond to such changes. + event EventHandler PortInformationChanged; + + /// + /// Gets the collection of network port information associated with the current instance. + /// + /// The collection provides information about the network ports, such as their status, + /// configuration, or other relevant details. The returned list is read-only and cannot be modified + /// directly. + List NetworkPorts { get; } + } + + /// + /// Represents information about a network port, including its configuration and associated system details. + /// + /// This class provides properties to describe various attributes of a network port, such as its + /// name, description, VLAN configuration, and management IP address. It is typically used to store and retrieve + /// metadata about network ports in a managed environment. + public class NvxNetworkPortInformation + { + private readonly DmNvxBaseClass.DmNvx35xNetwork.DmNvxNetworkLldpPort port; + + /// + /// Gets or sets the index of the device port. + /// + public uint DevicePortIndex { get; } + + /// + /// Gets or sets the name of the port used for communication. + /// + public string PortName => port.PortNameFeedback.StringValue; + + /// + /// Gets or sets the description of the port. + /// + public string PortDescription => port.PortNameDescriptionFeedback.StringValue; + + /// + /// Gets or sets the name of the VLAN (Virtual Local Area Network). + /// + public string VlanName => port.VlanNameFeedback.StringValue; + + /// + /// Gets the IP management address associated with the port. + /// + public string IpManagementAddress => port.IpManagementAddressFeedback.StringValue; + + /// + /// Gets the name of the system as reported by the associated port. + /// + public string SystemName => port.SystemNameFeedback.StringValue; + + /// + /// Gets the description of the system name. + /// + public string SystemNameDescription => port.SystemNameDescriptionFeedback.StringValue; + + /// + /// Initializes a new instance of the class with the specified network port + /// and device port index. + /// + /// The network port associated with the device. Cannot be . + /// The index of the device port. + /// Thrown if is . + public NvxNetworkPortInformation(DmNvxBaseClass.DmNvx35xNetwork.DmNvxNetworkLldpPort port, uint devicePortIndex) + { + this.port = port ?? throw new ArgumentNullException(nameof(port), "Port cannot be null"); + DevicePortIndex = devicePortIndex; + } +} From 5e880f01116c466b1bbfef07339fd20bf6fd75a0 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Mon, 7 Jul 2025 10:06:23 -0500 Subject: [PATCH 09/28] chore: add missing brace --- .../DeviceTypeInterfaces/INvxNetworkPortInformation.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/INvxNetworkPortInformation.cs b/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/INvxNetworkPortInformation.cs index 3fcb7e26..7894eca1 100644 --- a/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/INvxNetworkPortInformation.cs +++ b/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/INvxNetworkPortInformation.cs @@ -86,4 +86,5 @@ namespace PepperDash.Essentials.Core this.port = port ?? throw new ArgumentNullException(nameof(port), "Port cannot be null"); DevicePortIndex = devicePortIndex; } + } } From a076d531bc4200f40f9a736b09880099bb5b5376 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Mon, 7 Jul 2025 10:17:08 -0500 Subject: [PATCH 10/28] chore: remove BOM Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../DeviceTypeInterfaces/INvxNetworkPortInformation.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/INvxNetworkPortInformation.cs b/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/INvxNetworkPortInformation.cs index 7894eca1..549dd3f5 100644 --- a/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/INvxNetworkPortInformation.cs +++ b/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/INvxNetworkPortInformation.cs @@ -1,4 +1,4 @@ -using Crestron.SimplSharpPro.DM.Streaming; +using Crestron.SimplSharpPro.DM.Streaming; using System; using System.Collections.Generic; From 2b15c2a56f4ad7ad0916d92faeae071af10291dc Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Mon, 7 Jul 2025 10:17:37 -0500 Subject: [PATCH 11/28] docs: remove extra space Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../DeviceTypeInterfaces/INvxNetworkPortInformation.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/INvxNetworkPortInformation.cs b/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/INvxNetworkPortInformation.cs index 549dd3f5..c1592c94 100644 --- a/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/INvxNetworkPortInformation.cs +++ b/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/INvxNetworkPortInformation.cs @@ -33,7 +33,7 @@ namespace PepperDash.Essentials.Core /// Represents information about a network port, including its configuration and associated system details. /// /// This class provides properties to describe various attributes of a network port, such as its - /// name, description, VLAN configuration, and management IP address. It is typically used to store and retrieve + /// name, description, VLAN configuration, and management IP address. It is typically used to store and retrieve /// metadata about network ports in a managed environment. public class NvxNetworkPortInformation { From 056614cba111396fa79477d650f5863a0d3751c8 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Wed, 9 Jul 2025 14:32:01 -0500 Subject: [PATCH 12/28] fix: add IMeterFeedback interface --- .vscode/extensions.json | 9 +++++++++ .../DeviceTypeInterfaces/IMeterFeedback.cs | 18 ++++++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 .vscode/extensions.json create mode 100644 src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IMeterFeedback.cs diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 00000000..48c5715a --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "ms-dotnettools.vscode-dotnet-runtime", + "ms-dotnettools.csharp", + "ms-dotnettools.csdevkit", + "vivaxy.vscode-conventional-commits", + "mhutchie.git-graph" + ] +} \ No newline at end of file diff --git a/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IMeterFeedback.cs b/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IMeterFeedback.cs new file mode 100644 index 00000000..a4b935a5 --- /dev/null +++ b/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IMeterFeedback.cs @@ -0,0 +1,18 @@ +using System; + +namespace PepperDash.Essentials.Core.DeviceTypeInterfaces +{ + + /// + /// Interface for devices that provide audio meter feedback. + /// This interface is used to standardize access to meter feedback across different devices. + /// + public interface IMeterFeedback + { + /// + /// Gets the meter feedback for the device. + /// This property provides an IntFeedback that represents the current audio level or meter value. + /// + IntFeedback MeterFeedback { get; } + } +} From 2a70fc678e0394eca70489abdf33672c5d934344 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Fri, 11 Jul 2025 13:13:52 -0500 Subject: [PATCH 13/28] fix: add IStateFeedback interface --- .../DeviceTypeInterfaces/IStateFeedback.cs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IStateFeedback.cs diff --git a/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IStateFeedback.cs b/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IStateFeedback.cs new file mode 100644 index 00000000..e4070594 --- /dev/null +++ b/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IStateFeedback.cs @@ -0,0 +1,18 @@ +using System; + +namespace PepperDash.Essentials.Core.DeviceTypeInterfaces +{ + + /// + /// Interface for devices that provide audio meter feedback. + /// This interface is used to standardize access to meter feedback across different devices. + /// + public interface IStateFeedback + { + /// + /// Gets the state feedback for the device. + /// This property provides a BoolFeedback that represents the current state (on/off) of the device. + /// + BoolFeedback StateFeedback { get; } + } +} From ddbcc13c50613ad752588df42d8e49d540043a1d Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Wed, 16 Jul 2025 10:41:46 -0500 Subject: [PATCH 14/28] fix: add property for sync device association --- .../Devices/SourceListItem.cs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/PepperDash.Essentials.Core/Devices/SourceListItem.cs b/src/PepperDash.Essentials.Core/Devices/SourceListItem.cs index ae223005..0c7f3cf0 100644 --- a/src/PepperDash.Essentials.Core/Devices/SourceListItem.cs +++ b/src/PepperDash.Essentials.Core/Devices/SourceListItem.cs @@ -1,12 +1,14 @@ -using Newtonsoft.Json; +using System.Collections.Generic; +using Newtonsoft.Json; using Newtonsoft.Json.Converters; using PepperDash.Core; -using System.Collections.Generic; namespace PepperDash.Essentials.Core { /// - /// + /// Defines the type of source list item, which can be a route, off, or other. + /// This is used to categorize the source list items in a room. + /// The type is serialized to JSON and can be used to determine how the item should be displayed or handled in the UI. /// public enum eSourceListItemType { @@ -166,6 +168,12 @@ namespace PepperDash.Essentials.Core [JsonProperty("disableSimpleRouting")] public bool DisableSimpleRouting { get; set; } + /// + /// The key of the device that provides video sync for this source item + /// + [JsonProperty("syncProviderDeviceKey")] + public string SyncProviderDeviceKey { get; set; } + /// /// Default constructor for SourceListItem, initializes the Icon to "Blank" /// @@ -177,7 +185,7 @@ namespace PepperDash.Essentials.Core /// /// Returns a string representation of the SourceListItem, including the SourceKey and Name /// - /// + /// A string representation of the SourceListItem public override string ToString() { return $"{SourceKey}:{Name}"; From 9813673b663d465cf0420b19233f13180fd108af Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Thu, 17 Jul 2025 09:15:25 -0500 Subject: [PATCH 15/28] feat: ICurrentSources interface to allow for tracking breakaway routing --- .../DeviceTypeInterfaces/IDisplay.cs | 9 +- .../Devices/SourceListItem.cs | 7 + .../Routing/ICurrentSources.cs | 28 ++ .../Routing/IHasCurrentSourceInfoChange.cs | 16 + .../Routing/IRoutingSink.cs | 22 +- .../Displays/DisplayBase.cs | 341 ++++++++++++------ 6 files changed, 309 insertions(+), 114 deletions(-) create mode 100644 src/PepperDash.Essentials.Core/Routing/ICurrentSources.cs diff --git a/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IDisplay.cs b/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IDisplay.cs index 9b82cacd..af3428f6 100644 --- a/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IDisplay.cs +++ b/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IDisplay.cs @@ -2,7 +2,14 @@ namespace PepperDash.Essentials.Core.DeviceTypeInterfaces { - public interface IDisplay: IHasFeedback, IRoutingSinkWithSwitching, IHasPowerControl, IWarmingCooling, IUsageTracking, IKeyName + /// + /// Interface for display devices that can be controlled and monitored. + /// This interface combines functionality for feedback, routing, power control, + /// warming/cooling, usage tracking, and key name management. + /// It is designed to be implemented by devices that require these capabilities, + /// such as projectors, displays, and other visual output devices. + /// + public interface IDisplay : IHasFeedback, IRoutingSinkWithSwitching, IHasPowerControl, IWarmingCooling, IUsageTracking, IKeyName { } } diff --git a/src/PepperDash.Essentials.Core/Devices/SourceListItem.cs b/src/PepperDash.Essentials.Core/Devices/SourceListItem.cs index 0c7f3cf0..bfba4b4c 100644 --- a/src/PepperDash.Essentials.Core/Devices/SourceListItem.cs +++ b/src/PepperDash.Essentials.Core/Devices/SourceListItem.cs @@ -174,6 +174,13 @@ namespace PepperDash.Essentials.Core [JsonProperty("syncProviderDeviceKey")] public string SyncProviderDeviceKey { get; set; } + /// + /// Indicates if the source supports USB connections + /// + [JsonProperty("supportsUsb")] + public bool SupportsUsb { get; set; } + + /// /// Default constructor for SourceListItem, initializes the Icon to "Blank" /// diff --git a/src/PepperDash.Essentials.Core/Routing/ICurrentSources.cs b/src/PepperDash.Essentials.Core/Routing/ICurrentSources.cs new file mode 100644 index 00000000..d3f51483 --- /dev/null +++ b/src/PepperDash.Essentials.Core/Routing/ICurrentSources.cs @@ -0,0 +1,28 @@ +using System.Collections.Generic; + +namespace PepperDash.Essentials.Core.Routing +{ + /// + /// The current sources for the room, keyed by eRoutingSignalType. + /// This allows for multiple sources to be tracked, such as audio and video. + /// + /// + /// This interface is used to provide access to the current sources in a room, + /// allowing for more complex routing scenarios where multiple signal types are involved. + /// + public interface ICurrentSources + { + /// + /// Gets the current sources for the room, keyed by eRoutingSignalType. + /// This dictionary contains the current source for each signal type, such as audio, video, + /// + Dictionary CurrentSources { get; } + + /// + /// Gets the current source keys for the room, keyed by eRoutingSignalType. + /// This dictionary contains the keys for the current source for each signal type, such as audio, + /// + Dictionary CurrentSourceKeys { get; } + + } +} diff --git a/src/PepperDash.Essentials.Core/Routing/IHasCurrentSourceInfoChange.cs b/src/PepperDash.Essentials.Core/Routing/IHasCurrentSourceInfoChange.cs index 505a8652..a8dbc594 100644 --- a/src/PepperDash.Essentials.Core/Routing/IHasCurrentSourceInfoChange.cs +++ b/src/PepperDash.Essentials.Core/Routing/IHasCurrentSourceInfoChange.cs @@ -9,6 +9,8 @@ using PepperDash.Essentials.Core.Routing; using PepperDash.Essentials.Core.Routing; using PepperDash.Essentials.Core.Routing.Interfaces */ +using System; + namespace PepperDash.Essentials.Core { /// @@ -21,10 +23,24 @@ namespace PepperDash.Essentials.Core /// /// For rooms with a single presentation source, change event /// + [Obsolete("Use ICurrentSources instead")] public interface IHasCurrentSourceInfoChange { + /// + /// The key for the current source info, used to look up the source in the SourceList + /// string CurrentSourceInfoKey { get; set; } + + /// + /// The current source info for the room, used to look up the source in the SourceList + /// SourceListItem CurrentSourceInfo { get; set; } + + /// + /// Event that is raised when the current source info changes. + /// This is used to notify the system of changes to the current source info. + /// The event handler receives the new source info and the type of change that occurred. + /// event SourceInfoChangeHandler CurrentSourceChange; } } \ No newline at end of file diff --git a/src/PepperDash.Essentials.Core/Routing/IRoutingSink.cs b/src/PepperDash.Essentials.Core/Routing/IRoutingSink.cs index 59bcd65b..dd1d004e 100644 --- a/src/PepperDash.Essentials.Core/Routing/IRoutingSink.cs +++ b/src/PepperDash.Essentials.Core/Routing/IRoutingSink.cs @@ -1,29 +1,29 @@ -namespace PepperDash.Essentials.Core +using PepperDash.Essentials.Core.Routing; + +namespace PepperDash.Essentials.Core { /// /// For fixed-source endpoint devices /// public interface IRoutingSink : IRoutingInputs, IHasCurrentSourceInfoChange - { + { } /// /// For fixed-source endpoint devices with an input port /// - public interface IRoutingSinkWithInputPort :IRoutingSink + public interface IRoutingSinkWithInputPort : IRoutingSink { /// /// Gets the current input port for this routing sink. /// RoutingInputPort CurrentInputPort { get; } } - /*/// - /// For fixed-source endpoint devices - /// - public interface IRoutingSink : IRoutingInputs, IHasCurrentSourceInfoChange - { - void UpdateRouteRequest(RouteRequest request); - RouteRequest GetRouteRequest(); - }*/ + /// + /// Interface for routing sinks that have access to the current source information. + /// + public interface IRoutingSinkWithCurrentSources : IRoutingSink, ICurrentSources + { + } } \ No newline at end of file diff --git a/src/PepperDash.Essentials.Devices.Common/Displays/DisplayBase.cs b/src/PepperDash.Essentials.Devices.Common/Displays/DisplayBase.cs index 24d55c2e..926fca04 100644 --- a/src/PepperDash.Essentials.Devices.Common/Displays/DisplayBase.cs +++ b/src/PepperDash.Essentials.Devices.Common/Displays/DisplayBase.cs @@ -1,110 +1,199 @@ -using Crestron.SimplSharp; +using System; +using System.Collections.Generic; +using System.Linq; +using Crestron.SimplSharp; using Crestron.SimplSharpPro.DeviceSupport; using Newtonsoft.Json; using PepperDash.Core; using PepperDash.Essentials.Core; using PepperDash.Essentials.Core.Bridges; using PepperDash.Essentials.Core.DeviceTypeInterfaces; +using PepperDash.Essentials.Core.Routing; using Serilog.Events; -using System; -using System.Collections.Generic; -using System.Linq; using Feedback = PepperDash.Essentials.Core.Feedback; namespace PepperDash.Essentials.Devices.Common.Displays { - public abstract class DisplayBase : EssentialsDevice, IDisplay + /// + /// Abstract base class for display devices that provides common display functionality + /// including power control, input switching, and routing capabilities. + /// + public abstract class DisplayBase : EssentialsDevice, IDisplay, ICurrentSources { - private RoutingInputPort _currentInputPort; - public RoutingInputPort CurrentInputPort - { - get - { - return _currentInputPort; - } + private RoutingInputPort _currentInputPort; - protected set - { - if (_currentInputPort == value) return; + /// + /// Gets or sets the current input port that is selected on the display. + /// + public RoutingInputPort CurrentInputPort + { + get + { + return _currentInputPort; + } - _currentInputPort = value; + protected set + { + if (_currentInputPort == value) return; - InputChanged?.Invoke(this, _currentInputPort); - } - } + _currentInputPort = value; - public event InputChangedEventHandler InputChanged; + InputChanged?.Invoke(this, _currentInputPort); + } + } - public event SourceInfoChangeHandler CurrentSourceChange; + /// + /// Event that is raised when the input changes on the display. + /// + public event InputChangedEventHandler InputChanged; - public string CurrentSourceInfoKey { get; set; } - public SourceListItem CurrentSourceInfo - { - get - { - return _CurrentSourceInfo; - } - set - { - if (value == _CurrentSourceInfo) return; + /// + /// Event that is raised when the current source information changes. + /// + public event SourceInfoChangeHandler CurrentSourceChange; - var handler = CurrentSourceChange; + /// + /// Gets or sets the key of the current source information. + /// + public string CurrentSourceInfoKey { get; set; } - if (handler != null) - handler(_CurrentSourceInfo, ChangeType.WillChange); + /// + /// Gets or sets the current source information for the display. + /// + public SourceListItem CurrentSourceInfo + { + get + { + return _CurrentSourceInfo; + } + set + { + if (value == _CurrentSourceInfo) return; - _CurrentSourceInfo = value; + var handler = CurrentSourceChange; - if (handler != null) - handler(_CurrentSourceInfo, ChangeType.DidChange); - } - } - SourceListItem _CurrentSourceInfo; + if (handler != null) + handler(_CurrentSourceInfo, ChangeType.WillChange); + _CurrentSourceInfo = value; + + if (handler != null) + handler(_CurrentSourceInfo, ChangeType.DidChange); + } + } + SourceListItem _CurrentSourceInfo; + + /// + public Dictionary CurrentSources { get; private set; } + + /// + public Dictionary CurrentSourceKeys { get; private set; } + + /// + /// Gets feedback indicating whether the display is currently cooling down after being powered off. + /// public BoolFeedback IsCoolingDownFeedback { get; protected set; } + + /// + /// Gets feedback indicating whether the display is currently warming up after being powered on. + /// public BoolFeedback IsWarmingUpFeedback { get; private set; } - public UsageTracking UsageTracker { get; set; } + /// + /// Gets or sets the usage tracking instance for monitoring display usage statistics. + /// + public UsageTracking UsageTracker { get; set; } + /// + /// Gets or sets the warmup time in milliseconds for the display to become ready after power on. + /// public uint WarmupTime { get; set; } + + /// + /// Gets or sets the cooldown time in milliseconds for the display to fully power down. + /// public uint CooldownTime { get; set; } /// - /// Bool Func that will provide a value for the PowerIsOn Output. Must be implemented - /// by concrete sub-classes + /// Abstract function that must be implemented by derived classes to provide the cooling down feedback value. + /// Must be implemented by concrete sub-classes. /// abstract protected Func IsCoolingDownFeedbackFunc { get; } - abstract protected Func IsWarmingUpFeedbackFunc { get; } - + /// + /// Abstract function that must be implemented by derived classes to provide the warming up feedback value. + /// Must be implemented by concrete sub-classes. + /// + abstract protected Func IsWarmingUpFeedbackFunc { get; } + + /// + /// Timer used for managing display warmup timing. + /// protected CTimer WarmupTimer; + + /// + /// Timer used for managing display cooldown timing. + /// protected CTimer CooldownTimer; #region IRoutingInputs Members + /// + /// Gets the collection of input ports available on this display device. + /// public RoutingPortCollection InputPorts { get; private set; } #endregion - protected DisplayBase(string key, string name) - : base(key, name) + /// + /// Initializes a new instance of the DisplayBase class. + /// + /// The unique key identifier for this display device. + /// The friendly name for this display device. + protected DisplayBase(string key, string name) + : base(key, name) { IsCoolingDownFeedback = new BoolFeedback("IsCoolingDown", IsCoolingDownFeedbackFunc); IsWarmingUpFeedback = new BoolFeedback("IsWarmingUp", IsWarmingUpFeedbackFunc); InputPorts = new RoutingPortCollection(); + CurrentSources = new Dictionary + { + { eRoutingSignalType.Audio, null }, + { eRoutingSignalType.Video, null }, + }; + + CurrentSourceKeys = new Dictionary + { + { eRoutingSignalType.Audio, string.Empty }, + { eRoutingSignalType.Video, string.Empty }, + }; } + /// + /// Powers on the display device. Must be implemented by derived classes. + /// public abstract void PowerOn(); + + /// + /// Powers off the display device. Must be implemented by derived classes. + /// public abstract void PowerOff(); + + /// + /// Toggles the power state of the display device. Must be implemented by derived classes. + /// public abstract void PowerToggle(); - public virtual FeedbackCollection Feedbacks + /// + /// Gets the collection of feedback objects for this display device. + /// + public virtual FeedbackCollection Feedbacks { get { - return new FeedbackCollection + return new FeedbackCollection { IsCoolingDownFeedback, IsWarmingUpFeedback @@ -112,30 +201,50 @@ namespace PepperDash.Essentials.Devices.Common.Displays } } - public abstract void ExecuteSwitch(object selector); + /// + /// Executes a switch to the specified input on the display device. Must be implemented by derived classes. + /// + /// The selector object that identifies which input to switch to. + public abstract void ExecuteSwitch(object selector); - protected void LinkDisplayToApi(DisplayBase displayDevice, BasicTriList trilist, uint joinStart, string joinMapKey, - EiscApiAdvanced bridge) - { - var joinMap = new DisplayControllerJoinMap(joinStart); + /// + /// Links the display device to an API using a trilist, join start, join map key, and bridge. + /// This overload uses serialized join map configuration. + /// + /// The display device to link. + /// The BasicTriList for communication. + /// The starting join number for the device. + /// The key for the join map configuration. + /// The EISC API bridge instance. + protected void LinkDisplayToApi(DisplayBase displayDevice, BasicTriList trilist, uint joinStart, string joinMapKey, + EiscApiAdvanced bridge) + { + var joinMap = new DisplayControllerJoinMap(joinStart); - var joinMapSerialized = JoinMapHelper.GetSerializedJoinMapForDevice(joinMapKey); + var joinMapSerialized = JoinMapHelper.GetSerializedJoinMapForDevice(joinMapKey); - if (!string.IsNullOrEmpty(joinMapSerialized)) - joinMap = JsonConvert.DeserializeObject(joinMapSerialized); + if (!string.IsNullOrEmpty(joinMapSerialized)) + joinMap = JsonConvert.DeserializeObject(joinMapSerialized); - if (bridge != null) - { - bridge.AddJoinMap(Key, joinMap); - } - else - { - Debug.LogMessage(LogEventLevel.Information,this,"Please update config to use 'eiscapiadvanced' to get all join map features for this device."); - } + if (bridge != null) + { + bridge.AddJoinMap(Key, joinMap); + } + else + { + Debug.LogMessage(LogEventLevel.Information, this, "Please update config to use 'eiscapiadvanced' to get all join map features for this device."); + } LinkDisplayToApi(displayDevice, trilist, joinMap); - } + } + /// + /// Links the display device to an API using a trilist and join map. + /// This overload uses a pre-configured join map instance. + /// + /// The display device to link. + /// The BasicTriList for communication. + /// The join map configuration for the device. protected void LinkDisplayToApi(DisplayBase displayDevice, BasicTriList trilist, DisplayControllerJoinMap joinMap) { Debug.LogMessage(LogEventLevel.Debug, "Linking to Trilist '{0}'", trilist.ID.ToString("X")); @@ -268,68 +377,96 @@ namespace PepperDash.Essentials.Devices.Common.Displays volumeDisplayWithFeedback.MuteFeedback.LinkComplementInputSig(trilist.BooleanInput[joinMap.VolumeMuteOff.JoinNumber]); } - } + } - public abstract class TwoWayDisplayBase : DisplayBase, IRoutingFeedback, IHasPowerControlWithFeedback + /// + /// Abstract base class for two-way display devices that provide feedback capabilities. + /// Extends DisplayBase with routing feedback and power control feedback functionality. + /// + public abstract class TwoWayDisplayBase : DisplayBase, IRoutingFeedback, IHasPowerControlWithFeedback { - public StringFeedback CurrentInputFeedback { get; private set; } + /// + /// Gets feedback for the current input selection on the display. + /// + public StringFeedback CurrentInputFeedback { get; private set; } - abstract protected Func CurrentInputFeedbackFunc { get; } + /// + /// Abstract function that must be implemented by derived classes to provide the current input feedback value. + /// Must be implemented by concrete sub-classes. + /// + abstract protected Func CurrentInputFeedbackFunc { get; } - public BoolFeedback PowerIsOnFeedback { get; protected set; } + /// + /// Gets feedback indicating whether the display is currently powered on. + /// + public BoolFeedback PowerIsOnFeedback { get; protected set; } - abstract protected Func PowerIsOnFeedbackFunc { get; } + /// + /// Abstract function that must be implemented by derived classes to provide the power state feedback value. + /// Must be implemented by concrete sub-classes. + /// + abstract protected Func PowerIsOnFeedbackFunc { get; } - - public static MockDisplay DefaultDisplay - { - get + /// + /// Gets the default mock display instance for testing and development purposes. + /// + public static MockDisplay DefaultDisplay + { + get { if (_DefaultDisplay == null) _DefaultDisplay = new MockDisplay("default", "Default Display"); return _DefaultDisplay; - } + } } static MockDisplay _DefaultDisplay; + /// + /// Initializes a new instance of the TwoWayDisplayBase class. + /// + /// The unique key identifier for this display device. + /// The friendly name for this display device. public TwoWayDisplayBase(string key, string name) : base(key, name) { - CurrentInputFeedback = new StringFeedback(CurrentInputFeedbackFunc); + CurrentInputFeedback = new StringFeedback(CurrentInputFeedbackFunc); WarmupTime = 7000; CooldownTime = 15000; - PowerIsOnFeedback = new BoolFeedback("PowerOnFeedback", PowerIsOnFeedbackFunc); + PowerIsOnFeedback = new BoolFeedback("PowerOnFeedback", PowerIsOnFeedbackFunc); - Feedbacks.Add(CurrentInputFeedback); - Feedbacks.Add(PowerIsOnFeedback); + Feedbacks.Add(CurrentInputFeedback); + Feedbacks.Add(PowerIsOnFeedback); - PowerIsOnFeedback.OutputChange += PowerIsOnFeedback_OutputChange; + PowerIsOnFeedback.OutputChange += PowerIsOnFeedback_OutputChange; } - void PowerIsOnFeedback_OutputChange(object sender, EventArgs e) - { - if (UsageTracker != null) - { - if (PowerIsOnFeedback.BoolValue) - UsageTracker.StartDeviceUsage(); - else - UsageTracker.EndDeviceUsage(); - } - } + void PowerIsOnFeedback_OutputChange(object sender, EventArgs e) + { + if (UsageTracker != null) + { + if (PowerIsOnFeedback.BoolValue) + UsageTracker.StartDeviceUsage(); + else + UsageTracker.EndDeviceUsage(); + } + } - public event EventHandler NumericSwitchChange; + /// + /// Event that is raised when a numeric switch change occurs on the display. + /// + public event EventHandler NumericSwitchChange; - /// - /// Raise an event when the status of a switch object changes. - /// - /// Arguments defined as IKeyName sender, output, input, and eRoutingSignalType - protected void OnSwitchChange(RoutingNumericEventArgs e) - { - var newEvent = NumericSwitchChange; - if (newEvent != null) newEvent(this, e); - } + /// + /// Raise an event when the status of a switch object changes. + /// + /// Arguments defined as IKeyName sender, output, input, and eRoutingSignalType + protected void OnSwitchChange(RoutingNumericEventArgs e) + { + var newEvent = NumericSwitchChange; + if (newEvent != null) newEvent(this, e); + } } } \ No newline at end of file From 9d313d8c7ca3041eca6429d7ddb419027f97e89a Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Thu, 17 Jul 2025 09:54:08 -0500 Subject: [PATCH 16/28] fix: use Control Subnet IP if MC TP devices are on the CS Lan --- .../DeviceTypeInterfaces/IMobileControl.cs | 18 ++- .../MobileControlTouchpanelController.cs | 125 +++++++++++++++--- .../MobileControlWebsocketServer.cs | 48 +++---- 3 files changed, 145 insertions(+), 46 deletions(-) diff --git a/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IMobileControl.cs b/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IMobileControl.cs index c30edebd..1a7fab25 100644 --- a/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IMobileControl.cs +++ b/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IMobileControl.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.ObjectModel; +using Crestron.SimplSharpPro; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using PepperDash.Core; @@ -33,11 +35,11 @@ namespace PepperDash.Essentials.Core.DeviceTypeInterfaces string SystemUuid { get; } - BoolFeedback ApiOnlineAndAuthorized { get;} + BoolFeedback ApiOnlineAndAuthorized { get; } void SendMessageObject(IMobileControlMessage o); - void AddAction(T messenger, Action action) where T:IMobileControlMessenger; + void AddAction(T messenger, Action action) where T : IMobileControlMessenger; void RemoveAction(string key); @@ -45,14 +47,14 @@ namespace PepperDash.Essentials.Core.DeviceTypeInterfaces bool CheckForDeviceMessenger(string key); - IMobileControlRoomMessenger GetRoomMessenger(string key); + IMobileControlRoomMessenger GetRoomMessenger(string key); - } + } /// /// Describes a mobile control messenger /// - public interface IMobileControlMessenger: IKeyed + public interface IMobileControlMessenger : IKeyed { IMobileControl AppServerController { get; } string MessagePath { get; } @@ -104,9 +106,9 @@ namespace PepperDash.Essentials.Core.DeviceTypeInterfaces public interface IMobileControlAction { - IMobileControlMessenger Messenger { get; } + IMobileControlMessenger Messenger { get; } - Action Action { get; } + Action Action { get; } } public interface IMobileControlTouchpanelController : IKeyed @@ -115,5 +117,7 @@ namespace PepperDash.Essentials.Core.DeviceTypeInterfaces void SetAppUrl(string url); bool UseDirectServer { get; } bool ZoomRoomController { get; } + + ReadOnlyCollection ConnectedIps { get; } } } \ No newline at end of file diff --git a/src/PepperDash.Essentials.MobileControl/Touchpanel/MobileControlTouchpanelController.cs b/src/PepperDash.Essentials.MobileControl/Touchpanel/MobileControlTouchpanelController.cs index 9acd04f4..24f5f8e5 100644 --- a/src/PepperDash.Essentials.MobileControl/Touchpanel/MobileControlTouchpanelController.cs +++ b/src/PepperDash.Essentials.MobileControl/Touchpanel/MobileControlTouchpanelController.cs @@ -1,4 +1,8 @@ -using Crestron.SimplSharpPro; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using Crestron.SimplSharpPro; using Crestron.SimplSharpPro.DeviceSupport; using Crestron.SimplSharpPro.UI; using Newtonsoft.Json; @@ -10,21 +14,14 @@ using PepperDash.Essentials.Core.Config; using PepperDash.Essentials.Core.DeviceInfo; using PepperDash.Essentials.Core.DeviceTypeInterfaces; using PepperDash.Essentials.Core.UI; -using System; -using System.Collections.Generic; -using System.Linq; using Feedback = PepperDash.Essentials.Core.Feedback; namespace PepperDash.Essentials.Touchpanel { - //public interface IMobileControlTouchpanelController - //{ - // StringFeedback AppUrlFeedback { get; } - // string DefaultRoomKey { get; } - // string DeviceKey { get; } - //} - - + /// + /// Mobile Control touchpanel controller that provides app control, Zoom integration, + /// and mobile control functionality for Crestron touchpanels. + /// public class MobileControlTouchpanelController : TouchpanelBase, IHasFeedback, ITswAppControl, ITswZoomControl, IDeviceInfoProvider, IMobileControlTouchpanelController, ITheme { private readonly MobileControlTouchpanelProperties localConfig; @@ -32,42 +29,90 @@ namespace PepperDash.Essentials.Touchpanel private string _appUrl; + /// + /// Gets feedback for the current application URL. + /// public StringFeedback AppUrlFeedback { get; private set; } + private readonly StringFeedback QrCodeUrlFeedback; private readonly StringFeedback McServerUrlFeedback; private readonly StringFeedback UserCodeFeedback; private readonly BoolFeedback _appOpenFeedback; + /// + /// Gets feedback indicating whether an application is currently open on the touchpanel. + /// public BoolFeedback AppOpenFeedback => _appOpenFeedback; private readonly BoolFeedback _zoomIncomingCallFeedback; + /// + /// Gets feedback indicating whether there is an incoming Zoom call. + /// public BoolFeedback ZoomIncomingCallFeedback => _zoomIncomingCallFeedback; private readonly BoolFeedback _zoomInCallFeedback; + /// + /// Event that is raised when device information changes. + /// public event DeviceInfoChangeHandler DeviceInfoChanged; + /// + /// Gets feedback indicating whether a Zoom call is currently active. + /// public BoolFeedback ZoomInCallFeedback => _zoomInCallFeedback; - + /// + /// Gets the collection of feedback objects for this touchpanel controller. + /// public FeedbackCollection Feedbacks { get; private set; } + /// + /// Gets the collection of Zoom-related feedback objects. + /// public FeedbackCollection ZoomFeedbacks { get; private set; } + /// + /// Gets the default room key for this touchpanel controller. + /// public string DefaultRoomKey => _config.DefaultRoomKey; + /// + /// Gets a value indicating whether to use direct server communication. + /// public bool UseDirectServer => localConfig.UseDirectServer; + /// + /// Gets a value indicating whether this touchpanel acts as a Zoom Room controller. + /// public bool ZoomRoomController => localConfig.ZoomRoomController; + /// + /// Gets the current theme for the touchpanel interface. + /// public string Theme => localConfig.Theme; + /// + /// Gets feedback for the current theme setting. + /// public StringFeedback ThemeFeedback { get; private set; } + /// + /// Gets device information including MAC address and IP address. + /// public DeviceInfo DeviceInfo => new DeviceInfo(); + public ReadOnlyCollection ConnectedIps => Panel.ConnectedIpList; + + /// + /// Initializes a new instance of the MobileControlTouchpanelController class. + /// + /// The unique key identifier for this touchpanel controller. + /// The friendly name for this touchpanel controller. + /// The touchpanel hardware device. + /// The configuration properties for this controller. public MobileControlTouchpanelController(string key, string name, BasicTriListWithSmartObject panel, MobileControlTouchpanelProperties config) : base(key, name, panel, config) { localConfig = config; @@ -139,6 +184,10 @@ namespace PepperDash.Essentials.Touchpanel RegisterForExtenders(); } + /// + /// Updates the theme setting for this touchpanel controller and persists the change to configuration. + /// + /// The new theme identifier to apply. public void UpdateTheme(string theme) { localConfig.Theme = theme; @@ -271,6 +320,11 @@ namespace PepperDash.Essentials.Touchpanel } } + /// + /// Performs custom activation setup for the touchpanel controller, including + /// registering messengers and linking to mobile control. + /// + /// True if activation was successful; otherwise, false. public override bool CustomActivate() { var appMessenger = new ITswAppControlMessenger($"appControlMessenger-{Key}", $"/device/{Key}", this); @@ -300,12 +354,20 @@ namespace PepperDash.Essentials.Touchpanel return base.CustomActivate(); } - + /// + /// Handles device extender signal changes for system reserved signals. + /// + /// The device extender that generated the signal change. + /// 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}"); } + /// + /// Sets up the panel drivers and signal mappings for the specified room. + /// + /// The room key to configure the panel drivers for. protected override void SetupPanelDrivers(string roomKey) { AppUrlFeedback.LinkInputSig(Panel.StringInput[1]); @@ -366,6 +428,10 @@ namespace PepperDash.Essentials.Touchpanel SetAppUrl(_bridge.AppUrl); } + /// + /// Sets the application URL and updates the corresponding feedback. + /// + /// The new application URL to set. public void SetAppUrl(string url) { _appUrl = url; @@ -391,6 +457,9 @@ namespace PepperDash.Essentials.Touchpanel } } + /// + /// Hides the currently open application on the touchpanel. + /// public void HideOpenApp() { if (Panel is TswX70Base x70Panel) @@ -406,6 +475,9 @@ namespace PepperDash.Essentials.Touchpanel } } + /// + /// Opens an application on the touchpanel. Note: X60 panels do not support Zoom app opening. + /// public void OpenApp() { if (Panel is TswX70Base x70Panel) @@ -421,6 +493,9 @@ namespace PepperDash.Essentials.Touchpanel } } + /// + /// Closes the currently open application on the touchpanel. + /// public void CloseOpenApp() { if (Panel is TswX70Base x70Panel) @@ -436,6 +511,9 @@ namespace PepperDash.Essentials.Touchpanel } } + /// + /// Ends the current Zoom call on the touchpanel. + /// public void EndZoomCall() { if (Panel is TswX70Base x70Panel) @@ -451,6 +529,10 @@ namespace PepperDash.Essentials.Touchpanel } } + /// + /// Updates the device information (MAC address and IP address) from the touchpanel + /// and raises the DeviceInfoChanged event. + /// public void UpdateDeviceInfo() { if (Panel is TswXX70Base x70Panel) @@ -487,14 +569,27 @@ namespace PepperDash.Essentials.Touchpanel } } + /// + /// Factory class for creating MobileControlTouchpanelController instances from device configuration. + /// Supports various Crestron touchpanel models including TSW, TS, CrestronApp, XPanel, and DGE series. + /// public class MobileControlTouchpanelControllerFactory : EssentialsPluginDeviceFactory { + /// + /// Initializes a new instance of the MobileControlTouchpanelControllerFactory class. + /// Sets up supported device type names and minimum framework version requirements. + /// public MobileControlTouchpanelControllerFactory() { TypeNames = new List() { "mccrestronapp", "mctsw550", "mctsw750", "mctsw1050", "mctsw560", "mctsw760", "mctsw1060", "mctsw570", "mctsw770", "mcts770", "mctsw1070", "mcts1070", "mcxpanel", "mcdge1000" }; MinimumEssentialsFrameworkVersion = "2.0.0"; } + /// + /// Builds a MobileControlTouchpanelController device from the provided device configuration. + /// + /// The device configuration containing the device properties and settings. + /// A configured MobileControlTouchpanelController instance. public override EssentialsDevice BuildDevice(DeviceConfig dc) { var comm = CommFactory.GetControlPropertiesConfig(dc); @@ -557,7 +652,7 @@ namespace PepperDash.Essentials.Touchpanel return new Ts1070(id, Global.ControlSystem); else if (type == "dge1000") return new Dge1000(id, Global.ControlSystem); - else + else { Debug.LogMessage(Serilog.Events.LogEventLevel.Warning, "WARNING: Cannot create TSW controller with type '{0}'", type); diff --git a/src/PepperDash.Essentials.MobileControl/WebSocketServer/MobileControlWebsocketServer.cs b/src/PepperDash.Essentials.MobileControl/WebSocketServer/MobileControlWebsocketServer.cs index 859a7c7b..846a674a 100644 --- a/src/PepperDash.Essentials.MobileControl/WebSocketServer/MobileControlWebsocketServer.cs +++ b/src/PepperDash.Essentials.MobileControl/WebSocketServer/MobileControlWebsocketServer.cs @@ -1,4 +1,10 @@ -using Crestron.SimplSharp; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net.Http; +using System.Text; +using Crestron.SimplSharp; using Crestron.SimplSharp.WebScripting; using Newtonsoft.Json; using PepperDash.Core; @@ -9,12 +15,6 @@ using PepperDash.Essentials.Core.Web; using PepperDash.Essentials.RoomBridges; using PepperDash.Essentials.WebApiHandlers; using Serilog.Events; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Net.Http; -using System.Text; using WebSocketSharp; using WebSocketSharp.Net; using WebSocketSharp.Server; @@ -60,7 +60,7 @@ namespace PepperDash.Essentials.WebSocketServer private string lanIpAddress => CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, CrestronEthernetHelper.GetAdapterdIdForSpecifiedAdapterType(EthernetAdapterType.EthernetLANAdapter)); - private System.Net.IPAddress csIpAddress; + private System.Net.IPAddress csIpAddress; private System.Net.IPAddress csSubnetMask; @@ -122,7 +122,7 @@ namespace PepperDash.Essentials.WebSocketServer _parent = parent; // Set the default port to be 50000 plus the slot number of the program - Port = 50000 + (int)Global.ControlSystem.ProgramNumber; + Port = 50000 + (int)Global.ControlSystem.ProgramNumber; if (customPort != 0) { @@ -156,9 +156,9 @@ namespace PepperDash.Essentials.WebSocketServer } try - { + { var csAdapterId = CrestronEthernetHelper.GetAdapterdIdForSpecifiedAdapterType(EthernetAdapterType.EthernetCSAdapter); - var csSubnetMask = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_MASK, csAdapterId); + 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); @@ -298,8 +298,6 @@ namespace PepperDash.Essentials.WebSocketServer var processorIp = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, lanAdapterId); - this.LogVerbose("Processor IP: {processorIp}", processorIp); - foreach (var touchpanel in touchpanels.Select(tp => { var token = _secret.Tokens.FirstOrDefault((t) => t.Value.TouchpanelKey.Equals(tp.Key, StringComparison.InvariantCultureIgnoreCase)); @@ -321,11 +319,13 @@ namespace PepperDash.Essentials.WebSocketServer continue; } - var appUrl = $"http://{processorIp}:{_parent.Config.DirectServer.Port}/mc/app?token={touchpanel.Key}"; + var ip = touchpanel.Touchpanel.ConnectedIps.Any(ipInfo => csIpAddress.IsInSameSubnet(System.Net.IPAddress.Parse(ipInfo.DeviceIpAddress), csSubnetMask)) ? csIpAddress.ToString() : processorIp; + + var appUrl = $"http://{ip}:{_parent.Config.DirectServer.Port}/mc/app?token={touchpanel.Key}"; this.LogVerbose("Sending URL {appUrl}", appUrl); - touchpanel.Messenger.UpdateAppUrl($"http://{processorIp}:{_parent.Config.DirectServer.Port}/mc/app?token={touchpanel.Key}"); + touchpanel.Messenger.UpdateAppUrl($"http://{ip}:{_parent.Config.DirectServer.Port}/mc/app?token={touchpanel.Key}"); } } @@ -349,7 +349,7 @@ namespace PepperDash.Essentials.WebSocketServer if (!Directory.Exists($"{userAppPath}{localConfigFolderName}")) { Directory.CreateDirectory($"{userAppPath}{localConfigFolderName}"); - } + } using (var sw = new StreamWriter(File.Open($"{userAppPath}{localConfigFolderName}{Global.DirectorySeparator}{appConfigFileName}", FileMode.Create, FileAccess.ReadWrite))) { @@ -358,7 +358,7 @@ namespace PepperDash.Essentials.WebSocketServer this.LogDebug("LAN Adapter ID: {lanAdapterId}", lanAdapterId); - var processorIp = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, lanAdapterId); + var processorIp = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, lanAdapterId); var config = GetApplicationConfig(processorIp); @@ -378,7 +378,7 @@ namespace PepperDash.Essentials.WebSocketServer return; } - if(csAdapterId == -1) + if (csAdapterId == -1) { this.LogDebug("CS LAN Adapter not found"); return; @@ -389,8 +389,8 @@ namespace PepperDash.Essentials.WebSocketServer using (var sw = new StreamWriter(File.Open($"{userAppPath}{localConfigFolderName}{Global.DirectorySeparator}{appConfigCsFileName}", FileMode.Create, FileAccess.ReadWrite))) { // Write the CS application configuration file. Used when a request comes in for the application config from the CS - var processorIp = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, csAdapterId); - + var processorIp = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, csAdapterId); + var config = GetApplicationConfig(processorIp); var contents = JsonConvert.SerializeObject(config, Formatting.Indented); @@ -400,7 +400,7 @@ namespace PepperDash.Essentials.WebSocketServer } private MobileControlApplicationConfig GetApplicationConfig(string processorIp) - { + { try { var config = new MobileControlApplicationConfig @@ -430,10 +430,10 @@ namespace PepperDash.Essentials.WebSocketServer } catch (Exception ex) { - this.LogError(ex, "Error getting application configuration"); + this.LogError(ex, "Error getting application configuration"); return null; - } + } } /// @@ -572,7 +572,7 @@ namespace PepperDash.Essentials.WebSocketServer var values = s.Split(' '); - if(values.Length < 2) + if (values.Length < 2) { CrestronConsole.ConsoleCommandResponse("Invalid number of arguments. Please provide a room key and a grant code"); return; From e59c50d0aac1341e4bfda303d67cb4a3ff362740 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Thu, 17 Jul 2025 10:15:19 -0500 Subject: [PATCH 17/28] refactor: use tryParse for IP Address parsing Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../WebSocketServer/MobileControlWebsocketServer.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/PepperDash.Essentials.MobileControl/WebSocketServer/MobileControlWebsocketServer.cs b/src/PepperDash.Essentials.MobileControl/WebSocketServer/MobileControlWebsocketServer.cs index 846a674a..012f948f 100644 --- a/src/PepperDash.Essentials.MobileControl/WebSocketServer/MobileControlWebsocketServer.cs +++ b/src/PepperDash.Essentials.MobileControl/WebSocketServer/MobileControlWebsocketServer.cs @@ -319,7 +319,15 @@ namespace PepperDash.Essentials.WebSocketServer continue; } - var ip = touchpanel.Touchpanel.ConnectedIps.Any(ipInfo => csIpAddress.IsInSameSubnet(System.Net.IPAddress.Parse(ipInfo.DeviceIpAddress), csSubnetMask)) ? csIpAddress.ToString() : processorIp; + var ip = touchpanel.Touchpanel.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; var appUrl = $"http://{ip}:{_parent.Config.DirectServer.Port}/mc/app?token={touchpanel.Key}"; From c1eccfd790633770f0f8ffb03e3c33f677d64f2b Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Thu, 17 Jul 2025 12:13:08 -0500 Subject: [PATCH 18/28] fix: refactor interfaces for backwards compatibility --- .../DeviceTypeInterfaces/IMobileControl.cs | 35 +++++++++++++++++-- .../MobileControlTouchpanelController.cs | 2 +- 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IMobileControl.cs b/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IMobileControl.cs index 1a7fab25..edc2e627 100644 --- a/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IMobileControl.cs +++ b/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IMobileControl.cs @@ -111,13 +111,42 @@ namespace PepperDash.Essentials.Core.DeviceTypeInterfaces Action Action { get; } } + /// + /// Describes a MobileControl Touchpanel Controller + /// public interface IMobileControlTouchpanelController : IKeyed { + /// + /// The default room key for the controller + /// string DefaultRoomKey { get; } - void SetAppUrl(string url); - bool UseDirectServer { get; } - bool ZoomRoomController { 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.MobileControl/Touchpanel/MobileControlTouchpanelController.cs b/src/PepperDash.Essentials.MobileControl/Touchpanel/MobileControlTouchpanelController.cs index 24f5f8e5..53f600ef 100644 --- a/src/PepperDash.Essentials.MobileControl/Touchpanel/MobileControlTouchpanelController.cs +++ b/src/PepperDash.Essentials.MobileControl/Touchpanel/MobileControlTouchpanelController.cs @@ -22,7 +22,7 @@ namespace PepperDash.Essentials.Touchpanel /// Mobile Control touchpanel controller that provides app control, Zoom integration, /// and mobile control functionality for Crestron touchpanels. /// - public class MobileControlTouchpanelController : TouchpanelBase, IHasFeedback, ITswAppControl, ITswZoomControl, IDeviceInfoProvider, IMobileControlTouchpanelController, ITheme + public class MobileControlTouchpanelController : TouchpanelBase, IHasFeedback, ITswAppControl, ITswZoomControl, IDeviceInfoProvider, IMobileControlCrestronTouchpanelController, ITheme { private readonly MobileControlTouchpanelProperties localConfig; private IMobileControlRoomMessenger _bridge; From 2bf0f2092bb5f2d588482c2fc87c47ae44b4f6bd Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Thu, 17 Jul 2025 12:16:32 -0500 Subject: [PATCH 19/28] fix: use new interface in direct server --- .../MobileControlWebsocketServer.cs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/PepperDash.Essentials.MobileControl/WebSocketServer/MobileControlWebsocketServer.cs b/src/PepperDash.Essentials.MobileControl/WebSocketServer/MobileControlWebsocketServer.cs index 012f948f..2b0d5cbd 100644 --- a/src/PepperDash.Essentials.MobileControl/WebSocketServer/MobileControlWebsocketServer.cs +++ b/src/PepperDash.Essentials.MobileControl/WebSocketServer/MobileControlWebsocketServer.cs @@ -319,15 +319,19 @@ namespace PepperDash.Essentials.WebSocketServer continue; } - var ip = touchpanel.Touchpanel.ConnectedIps.Any(ipInfo => + string ip = processorIp; + if (touchpanel.Touchpanel is IMobileControlCrestronTouchpanelController crestronTouchpanel) { - if (System.Net.IPAddress.TryParse(ipInfo.DeviceIpAddress, out var parsedIp)) + ip = crestronTouchpanel.ConnectedIps.Any(ipInfo => { - return csIpAddress.IsInSameSubnet(parsedIp, csSubnetMask); - } - this.LogWarning("Invalid IP address: {deviceIpAddress}", ipInfo.DeviceIpAddress); - return false; - }) ? csIpAddress.ToString() : processorIp; + 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; + } var appUrl = $"http://{ip}:{_parent.Config.DirectServer.Port}/mc/app?token={touchpanel.Key}"; From e76369726d58e897e47904fdc77460a0d4ef890c Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Thu, 17 Jul 2025 12:25:52 -0500 Subject: [PATCH 20/28] docs: XML comments for DestinationListItem --- .../Devices/DestinationListItem.cs | 49 ++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/src/PepperDash.Essentials.Core/Devices/DestinationListItem.cs b/src/PepperDash.Essentials.Core/Devices/DestinationListItem.cs index 0c21216a..19c40adb 100644 --- a/src/PepperDash.Essentials.Core/Devices/DestinationListItem.cs +++ b/src/PepperDash.Essentials.Core/Devices/DestinationListItem.cs @@ -5,19 +5,34 @@ using PepperDash.Essentials.Core; namespace PepperDash.Essentials.Core { + /// + /// Represents a destination item in a routing system that can receive audio/video signals. + /// Contains information about the destination device, its properties, and location settings. + /// public class DestinationListItem { + /// + /// Gets or sets the key identifier for the sink device that this destination represents. + /// [JsonProperty("sinkKey")] public string SinkKey { get; set; } private EssentialsDevice _sinkDevice; + /// + /// Gets the actual device instance for this destination. + /// Lazily loads the device from the DeviceManager using the SinkKey. + /// [JsonIgnore] public EssentialsDevice SinkDevice { get { return _sinkDevice ?? (_sinkDevice = DeviceManager.GetDeviceForKey(SinkKey) as EssentialsDevice); } } + /// + /// Gets the preferred display name for this destination. + /// Returns the custom Name if set, otherwise returns the SinkDevice name, or "---" if no device is found. + /// [JsonProperty("preferredName")] public string PreferredName { @@ -32,30 +47,62 @@ namespace PepperDash.Essentials.Core } } + /// + /// Gets or sets the custom name for this destination. + /// If set, this name will be used as the PreferredName instead of the device name. + /// [JsonProperty("name")] public string Name { get; set; } + /// + /// Gets or sets a value indicating whether this destination should be included in destination lists. + /// [JsonProperty("includeInDestinationList")] public bool IncludeInDestinationList { get; set; } + /// + /// Gets or sets the display order for this destination in lists. + /// Lower values appear first in sorted lists. + /// [JsonProperty("order")] public int Order { get; set; } + /// + /// Gets or sets the surface location identifier for this destination. + /// Used to specify which surface or screen this destination is located on. + /// [JsonProperty("surfaceLocation")] public int SurfaceLocation { get; set; } + /// + /// Gets or sets the vertical location position for this destination. + /// Used for spatial positioning in multi-display configurations. + /// [JsonProperty("verticalLocation")] public int VerticalLocation { get; set; } - + + /// + /// Gets or sets the horizontal location position for this destination. + /// Used for spatial positioning in multi-display configurations. + /// [JsonProperty("horizontalLocation")] public int HorizontalLocation { get; set; } + /// + /// Gets or sets the signal type that this destination can receive (Audio, Video, AudioVideo, etc.). + /// [JsonProperty("sinkType")] public eRoutingSignalType SinkType { get; set; } + /// + /// Gets or sets a value indicating whether this destination is used for codec content sharing. + /// [JsonProperty("isCodecContentDestination")] public bool isCodecContentDestination { get; set; } + /// + /// Gets or sets a value indicating whether this destination is used for program audio output. + /// [JsonProperty("isProgramAudioDestination")] public bool isProgramAudioDestination { get; set; } } From 1dcd4e328c8f84795e85c8ce197496632c701599 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Thu, 17 Jul 2025 12:32:26 -0500 Subject: [PATCH 21/28] fix: Destination support for USB --- .../Devices/DestinationListItem.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/PepperDash.Essentials.Core/Devices/DestinationListItem.cs b/src/PepperDash.Essentials.Core/Devices/DestinationListItem.cs index 19c40adb..41043b82 100644 --- a/src/PepperDash.Essentials.Core/Devices/DestinationListItem.cs +++ b/src/PepperDash.Essentials.Core/Devices/DestinationListItem.cs @@ -105,5 +105,12 @@ namespace PepperDash.Essentials.Core /// [JsonProperty("isProgramAudioDestination")] public bool isProgramAudioDestination { get; set; } + + /// + /// Gets or sets a value indicating whether this destination supports USB connections. + /// This is used to determine if the destination can handle USB. + /// + [JsonProperty("supportsUsb")] + public bool SupportsUsb { get; set; } } } \ No newline at end of file From 2bbefa062d26db30578a169399f83c7e7648898b Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Mon, 21 Jul 2025 13:28:10 -0500 Subject: [PATCH 22/28] docs: fix comments Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../DeviceTypeInterfaces/IStateFeedback.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IStateFeedback.cs b/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IStateFeedback.cs index e4070594..a4f6c2bb 100644 --- a/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IStateFeedback.cs +++ b/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IStateFeedback.cs @@ -4,8 +4,8 @@ namespace PepperDash.Essentials.Core.DeviceTypeInterfaces { /// - /// Interface for devices that provide audio meter feedback. - /// This interface is used to standardize access to meter feedback across different devices. + /// Interface for devices that provide state feedback. + /// This interface is used to standardize access to state feedback across different devices. /// public interface IStateFeedback { From 97b2ffed9c07ee3a9e012041a33538ee4b65cb11 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Mon, 21 Jul 2025 13:28:37 -0500 Subject: [PATCH 23/28] docs: fix comment Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/PepperDash.Essentials.Core/Routing/ICurrentSources.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/PepperDash.Essentials.Core/Routing/ICurrentSources.cs b/src/PepperDash.Essentials.Core/Routing/ICurrentSources.cs index d3f51483..8c6323d4 100644 --- a/src/PepperDash.Essentials.Core/Routing/ICurrentSources.cs +++ b/src/PepperDash.Essentials.Core/Routing/ICurrentSources.cs @@ -14,13 +14,13 @@ namespace PepperDash.Essentials.Core.Routing { /// /// Gets the current sources for the room, keyed by eRoutingSignalType. - /// This dictionary contains the current source for each signal type, such as audio, video, + /// This dictionary contains the current source for each signal type, such as audio, video, and control signals. /// Dictionary CurrentSources { get; } /// /// Gets the current source keys for the room, keyed by eRoutingSignalType. - /// This dictionary contains the keys for the current source for each signal type, such as audio, + /// This dictionary contains the keys for the current source for each signal type, such as audio, video, and control signals. /// Dictionary CurrentSourceKeys { get; } From 660836bd5a3c19149b9322188f736c6dc2684aa2 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Mon, 21 Jul 2025 13:28:59 -0500 Subject: [PATCH 24/28] docs: remove spaces Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/PepperDash.Essentials.Core/Devices/SourceListItem.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PepperDash.Essentials.Core/Devices/SourceListItem.cs b/src/PepperDash.Essentials.Core/Devices/SourceListItem.cs index bfba4b4c..3d08a218 100644 --- a/src/PepperDash.Essentials.Core/Devices/SourceListItem.cs +++ b/src/PepperDash.Essentials.Core/Devices/SourceListItem.cs @@ -169,7 +169,7 @@ namespace PepperDash.Essentials.Core public bool DisableSimpleRouting { get; set; } /// - /// The key of the device that provides video sync for this source item + /// The key of the device that provides video sync for this source item /// [JsonProperty("syncProviderDeviceKey")] public string SyncProviderDeviceKey { get; set; } From 789113008ea553dab398a2e49b554c9db77e6c50 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Mon, 21 Jul 2025 13:29:11 -0500 Subject: [PATCH 25/28] docs: update comments Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/PepperDash.Essentials.Core/Devices/DestinationListItem.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/PepperDash.Essentials.Core/Devices/DestinationListItem.cs b/src/PepperDash.Essentials.Core/Devices/DestinationListItem.cs index 41043b82..c0837401 100644 --- a/src/PepperDash.Essentials.Core/Devices/DestinationListItem.cs +++ b/src/PepperDash.Essentials.Core/Devices/DestinationListItem.cs @@ -108,7 +108,8 @@ namespace PepperDash.Essentials.Core /// /// Gets or sets a value indicating whether this destination supports USB connections. - /// This is used to determine if the destination can handle USB. + /// Indicates if the destination can handle USB functionality, such as USB signal routing or device connections. + /// This property is used to determine compatibility with USB-based devices or systems. /// [JsonProperty("supportsUsb")] public bool SupportsUsb { get; set; } From 311452beaccc18f6c28a7acdfb9a077148d2cf1a Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Mon, 21 Jul 2025 13:30:11 -0500 Subject: [PATCH 26/28] fix: use correct namespaces Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/PepperDash.Essentials.Core/Routing/ICurrentSources.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/PepperDash.Essentials.Core/Routing/ICurrentSources.cs b/src/PepperDash.Essentials.Core/Routing/ICurrentSources.cs index 8c6323d4..bef0fd95 100644 --- a/src/PepperDash.Essentials.Core/Routing/ICurrentSources.cs +++ b/src/PepperDash.Essentials.Core/Routing/ICurrentSources.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using PepperDash.Essentials.Core; namespace PepperDash.Essentials.Core.Routing { From a6cd9a0571fb15e50a4f4129934c57208dd43bf7 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Tue, 22 Jul 2025 14:56:28 -0500 Subject: [PATCH 27/28] feat: add destination and source port key properties for advanced routing --- .../Devices/DestinationListItem.cs | 7 +++++++ src/PepperDash.Essentials.Core/Devices/SourceListItem.cs | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/src/PepperDash.Essentials.Core/Devices/DestinationListItem.cs b/src/PepperDash.Essentials.Core/Devices/DestinationListItem.cs index c0837401..788714b7 100644 --- a/src/PepperDash.Essentials.Core/Devices/DestinationListItem.cs +++ b/src/PepperDash.Essentials.Core/Devices/DestinationListItem.cs @@ -113,5 +113,12 @@ namespace PepperDash.Essentials.Core /// [JsonProperty("supportsUsb")] public bool SupportsUsb { get; set; } + + /// + /// The key of the destination port associated with this source item + /// This is used to identify the specific port on the destination device that this item refers to for advanced routing + /// + [JsonProperty("destinationPortKey")] + public string DestinationPortKey { get; set; } } } \ No newline at end of file diff --git a/src/PepperDash.Essentials.Core/Devices/SourceListItem.cs b/src/PepperDash.Essentials.Core/Devices/SourceListItem.cs index 3d08a218..fe0bde9d 100644 --- a/src/PepperDash.Essentials.Core/Devices/SourceListItem.cs +++ b/src/PepperDash.Essentials.Core/Devices/SourceListItem.cs @@ -180,6 +180,13 @@ namespace PepperDash.Essentials.Core [JsonProperty("supportsUsb")] public bool SupportsUsb { get; set; } + /// + /// The key of the source port associated with this source item + /// This is used to identify the specific port on the source device that this item refers to for advanced routing + /// + [JsonProperty("sourcePortKey")] + public string SourcePortKey { get; set; } + /// /// Default constructor for SourceListItem, initializes the Icon to "Blank" From 799d4c127cce5c8c416db72ee0be981844331cdc Mon Sep 17 00:00:00 2001 From: Neil Dorin Date: Tue, 22 Jul 2025 14:02:01 -0600 Subject: [PATCH 28/28] Update src/PepperDash.Essentials.Core/Devices/DestinationListItem.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/PepperDash.Essentials.Core/Devices/DestinationListItem.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PepperDash.Essentials.Core/Devices/DestinationListItem.cs b/src/PepperDash.Essentials.Core/Devices/DestinationListItem.cs index 788714b7..5a66c0b3 100644 --- a/src/PepperDash.Essentials.Core/Devices/DestinationListItem.cs +++ b/src/PepperDash.Essentials.Core/Devices/DestinationListItem.cs @@ -115,7 +115,7 @@ namespace PepperDash.Essentials.Core public bool SupportsUsb { get; set; } /// - /// The key of the destination port associated with this source item + /// The key of the destination port associated with this destination item /// This is used to identify the specific port on the destination device that this item refers to for advanced routing /// [JsonProperty("destinationPortKey")]