diff --git a/.gitignore b/.gitignore index c5e1142f..bd60ff8d 100644 --- a/.gitignore +++ b/.gitignore @@ -393,4 +393,5 @@ essentials-framework/Essentials Interfaces/PepperDash_Essentials_Interfaces/Pepp /._PepperDash.Essentials.sln .vscode/settings.json _site/ -api/ \ No newline at end of file +api/ +*.DS_Store 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/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/LevelControlListItem.cs b/src/PepperDash.Essentials.Core/Devices/LevelControlListItem.cs index 4c2e83e4..821a0c2b 100644 --- a/src/PepperDash.Essentials.Core/Devices/LevelControlListItem.cs +++ b/src/PepperDash.Essentials.Core/Devices/LevelControlListItem.cs @@ -9,10 +9,15 @@ using PepperDash.Essentials.Core.Devices; namespace PepperDash.Essentials.Core { + /// + /// Represents a level control item in a list, which can be used to control volume or mute functionality. + /// public class LevelControlListItem : AudioControlListItemBase { - + /// + /// A reference to the IBasicVolumeWithFeedback device for control. + /// [JsonIgnore] public IBasicVolumeWithFeedback LevelControl { @@ -55,7 +60,7 @@ namespace PepperDash.Essentials.Core { get { - if(string.IsNullOrEmpty(ItemKey)) return ParentDeviceKey; + if (string.IsNullOrEmpty(ItemKey)) return ParentDeviceKey; else { return DeviceManager.AllDevices. @@ -70,13 +75,39 @@ namespace PepperDash.Essentials.Core [JsonProperty("type")] [JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))] public eLevelControlType Type { get; set; } + + + /// + /// Indicates if the item is a mic or not. + /// + [JsonProperty("isMic", NullValueHandling = NullValueHandling.Ignore)] + public bool? IsMic { get; set; } + + /// + /// Indicates if the item should show the raw level in the UI. + /// + [JsonProperty("showRawLevel", NullValueHandling = NullValueHandling.Ignore)] + public bool? ShowRawLevel { get; set; } } + /// + /// Indicates the type of level control item. + /// [Flags] public enum eLevelControlType { + /// + /// Indicates that the item is a level control only + /// Level = 1, + /// + /// Indicates that the item is a mute control only + /// Mute = 2, + /// + /// Indicates that the item is both a level and mute control + /// LevelAndMute = Level | Mute, } + } diff --git a/src/PepperDash.Essentials.Core/Devices/SourceListItem.cs b/src/PepperDash.Essentials.Core/Devices/SourceListItem.cs index 6de2f35d..fe0bde9d 100644 --- a/src/PepperDash.Essentials.Core/Devices/SourceListItem.cs +++ b/src/PepperDash.Essentials.Core/Devices/SourceListItem.cs @@ -1,16 +1,29 @@ -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 { - Route, Off, Other, SomethingAwesomerThanThese + /// + /// Represents a typical route. + /// + Route, + /// + /// Represents an off route. + /// + Off, + /// + /// Represents some other type of route + /// + Other, } /// @@ -18,6 +31,9 @@ namespace PepperDash.Essentials.Core /// public class SourceListItem { + /// + /// The key of the source item, which is used to identify it in the DeviceManager + /// [JsonProperty("sourceKey")] public string SourceKey { get; set; } @@ -117,6 +133,9 @@ namespace PepperDash.Essentials.Core [JsonProperty("disableRoutedSharing")] public bool DisableRoutedSharing { get; set; } + /// + /// + /// [JsonProperty("destinations")] public List Destinations { get; set; } /// @@ -149,31 +168,76 @@ 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" + /// public SourceListItem() { Icon = "Blank"; } + /// + /// 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}"; } } + /// + /// Represents a route in a source list item, which defines the source and destination keys and the type of signal being routed + /// public class SourceRouteListItem { + /// + /// The key of the source device to route from + /// [JsonProperty("sourceKey")] public string SourceKey { get; set; } + /// + /// The key of the source port to route from + /// [JsonProperty("sourcePortKey")] public string SourcePortKey { get; set; } + /// + /// The key of the destination device to route to + /// [JsonProperty("destinationKey")] public string DestinationKey { get; set; } + /// + /// The key of the destination port to route to + /// [JsonProperty("destinationPortKey")] public string DestinationPortKey { get; set; } + /// + /// The type of signal being routed, such as audio or video + /// [JsonProperty("type")] public eRoutingSignalType Type { get; set; } } @@ -183,15 +247,85 @@ namespace PepperDash.Essentials.Core /// public enum eSourceListItemDestinationTypes { + /// + /// Default display, used for the main video output in a room + /// defaultDisplay, + /// + /// Left display + /// leftDisplay, + /// + /// Right display + /// rightDisplay, + /// + /// Center display + /// centerDisplay, + /// + /// Program audio, used for the main audio output in a room + /// programAudio, + /// + /// Codec content, used for sharing content to the far end in a video call + /// codecContent, + /// + /// Front left display, used for rooms with multiple displays + /// frontLeftDisplay, + /// + /// Front right display, used for rooms with multiple displays + /// frontRightDisplay, + /// + /// Rear left display, used for rooms with multiple displays + /// rearLeftDisplay, + /// + /// Rear right display, used for rooms with multiple displays + /// rearRightDisplay, + /// + /// Auxiliary display 1, used for additional displays in a room + /// + auxDisplay1, + /// + /// Auxiliary display 2, used for additional displays in a room + /// + auxDisplay2, + /// + /// Auxiliary display 3, used for additional displays in a room + /// + auxDisplay3, + /// + /// Auxiliary display 4, used for additional displays in a room + /// + auxDisplay4, + /// + /// Auxiliary display 5, used for additional displays in a room + /// + auxDisplay5, + /// + /// Auxiliary display 6, used for additional displays in a room + /// + auxDisplay6, + /// + /// Auxiliary display 7, used for additional displays in a room + /// + auxDisplay7, + /// + /// Auxiliary display 8, used for additional displays in a room + /// + auxDisplay8, + /// + /// Auxiliary display 9, used for additional displays in a room + /// + auxDisplay9, + /// + /// Auxiliary display 10, used for additional displays in a room + /// + auxDisplay10, } } \ No newline at end of file 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 b05c7744..dd1d004e 100644 --- a/src/PepperDash.Essentials.Core/Routing/IRoutingSink.cs +++ b/src/PepperDash.Essentials.Core/Routing/IRoutingSink.cs @@ -1,23 +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 - { + { } - public interface IRoutingSinkWithInputPort :IRoutingSink + /// + /// For fixed-source endpoint devices with an input port + /// + 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/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;