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.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/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/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; } + } +} diff --git a/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IMobileControl.cs b/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IMobileControl.cs index c30edebd..edc2e627 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,16 +106,47 @@ namespace PepperDash.Essentials.Core.DeviceTypeInterfaces public interface IMobileControlAction { - IMobileControlMessenger Messenger { get; } + IMobileControlMessenger Messenger { get; } - Action Action { get; } + Action Action { get; } } + /// + /// Describes a MobileControl Touchpanel Controller + /// public interface IMobileControlTouchpanelController : IKeyed { + /// + /// The default room key for the controller + /// string DefaultRoomKey { get; } + + /// + /// Sets the application URL for the controller + /// + /// The application URL void SetAppUrl(string url); + + /// + /// Indicates whether the controller uses a direct server connection + /// bool UseDirectServer { get; } + + /// + /// Indicates whether the controller is a Zoom Room controller + /// bool ZoomRoomController { get; } } + + /// + /// Describes a MobileControl Crestron Touchpanel Controller + /// This interface extends the IMobileControlTouchpanelController to include connected IP information + /// + public interface IMobileControlCrestronTouchpanelController : IMobileControlTouchpanelController + { + /// + /// Gets a collection of connected IP information for the touchpanel controller + /// + ReadOnlyCollection ConnectedIps { get; } + } } \ No newline at end of file diff --git a/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/INvxNetworkPortInformation.cs b/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/INvxNetworkPortInformation.cs new file mode 100644 index 00000000..c1592c94 --- /dev/null +++ b/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/INvxNetworkPortInformation.cs @@ -0,0 +1,90 @@ +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; + } + } +} diff --git a/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IStateFeedback.cs b/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IStateFeedback.cs new file mode 100644 index 00000000..a4f6c2bb --- /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 state feedback. + /// This interface is used to standardize access to state 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; } + } +} diff --git a/src/PepperDash.Essentials.Core/Devices/DestinationListItem.cs b/src/PepperDash.Essentials.Core/Devices/DestinationListItem.cs index 0c21216a..5a66c0b3 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,31 +47,78 @@ 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; } + + /// + /// Gets or sets a value indicating whether this destination supports USB connections. + /// 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; } + + /// + /// 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")] + public string DestinationPortKey { get; set; } } } \ 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 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"); } } 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() + { + } } diff --git a/src/PepperDash.Essentials.Core/Devices/SourceListItem.cs b/src/PepperDash.Essentials.Core/Devices/SourceListItem.cs index ae223005..fe0bde9d 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,26 @@ 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; } + + /// + /// Indicates if the source supports USB connections + /// + [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" /// @@ -177,7 +199,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}"; diff --git a/src/PepperDash.Essentials.Core/Room/Combining/EssentialsRoomCombiner.cs b/src/PepperDash.Essentials.Core/Room/Combining/EssentialsRoomCombiner.cs index f0fb5e98..d2a16739 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 @@ -46,12 +62,36 @@ 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 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 +286,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 +304,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 +333,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 +349,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 +366,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 +392,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 +451,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..fec7e380 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,14 @@ 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. + /// + [JsonProperty("disableAutoMode")] + public bool DisableAutoMode { get; set; } + /// /// The list of partitions that device the rooms /// @@ -47,6 +49,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 +61,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 +91,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 +115,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 +133,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; } } 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.Core/Routing/ICurrentSources.cs b/src/PepperDash.Essentials.Core/Routing/ICurrentSources.cs new file mode 100644 index 00000000..bef0fd95 --- /dev/null +++ b/src/PepperDash.Essentials.Core/Routing/ICurrentSources.cs @@ -0,0 +1,29 @@ +using System.Collections.Generic; +using PepperDash.Essentials.Core; + +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, 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, video, and control signals. + /// + 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 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")] 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; } } diff --git a/src/PepperDash.Essentials.MobileControl/Touchpanel/MobileControlTouchpanelController.cs b/src/PepperDash.Essentials.MobileControl/Touchpanel/MobileControlTouchpanelController.cs index 9acd04f4..53f600ef 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,64 +14,105 @@ 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; } - //} - - - public class MobileControlTouchpanelController : TouchpanelBase, IHasFeedback, ITswAppControl, ITswZoomControl, IDeviceInfoProvider, IMobileControlTouchpanelController, ITheme + /// + /// 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, IMobileControlCrestronTouchpanelController, ITheme { private readonly MobileControlTouchpanelProperties localConfig; private IMobileControlRoomMessenger _bridge; 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..2b0d5cbd 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,25 @@ namespace PepperDash.Essentials.WebSocketServer continue; } - var appUrl = $"http://{processorIp}:{_parent.Config.DirectServer.Port}/mc/app?token={touchpanel.Key}"; + string ip = processorIp; + if (touchpanel.Touchpanel is IMobileControlCrestronTouchpanelController crestronTouchpanel) + { + ip = crestronTouchpanel.ConnectedIps.Any(ipInfo => + { + if (System.Net.IPAddress.TryParse(ipInfo.DeviceIpAddress, out var parsedIp)) + { + return csIpAddress.IsInSameSubnet(parsedIp, csSubnetMask); + } + this.LogWarning("Invalid IP address: {deviceIpAddress}", ipInfo.DeviceIpAddress); + return false; + }) ? csIpAddress.ToString() : processorIp; + } + + 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 +361,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 +370,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 +390,7 @@ namespace PepperDash.Essentials.WebSocketServer return; } - if(csAdapterId == -1) + if (csAdapterId == -1) { this.LogDebug("CS LAN Adapter not found"); return; @@ -389,8 +401,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 +412,7 @@ namespace PepperDash.Essentials.WebSocketServer } private MobileControlApplicationConfig GetApplicationConfig(string processorIp) - { + { try { var config = new MobileControlApplicationConfig @@ -430,10 +442,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 +584,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;