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;