Compare commits

...

32 Commits

Author SHA1 Message Date
Andrew Welker
af82ba2351 feat: all fullStatus messages are now client-specific 2025-07-10 12:44:56 -05:00
Andrew Welker
505067f38f docs: add XML comments for messengers 2025-07-10 11:28:22 -05:00
Andrew Welker
8b098aac2c feat: unique client IDs for direct server
When multiple UI applications were connecting using the same token, the actual websocket connection was getting lost, and could eventually be garbage-collected, leading to odd behavior from the UI. This is due to an existing client getting replaced and a reference to it lost. This has now been remedied, with each client getting a unique instance with a unique client ID.
2025-07-10 10:36:47 -05:00
Neil Dorin
5ff587a8c9 Merge pull request #1285 from PepperDash/feature/add-isMic-support
feat: Update .gitignore and enhance routing-related classes
2025-07-08 10:55:05 -06:00
Neil Dorin
26c1baa1f8 Merge pull request #1284 from PepperDash/network-port 2025-07-07 09:19:42 -06:00
Andrew Welker
2b15c2a56f docs: remove extra space
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-07-07 10:17:37 -05:00
Andrew Welker
a076d531bc chore: remove BOM
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-07-07 10:17:08 -05:00
Andrew Welker
5e880f0111 chore: add missing brace 2025-07-07 10:06:23 -05:00
Andrew Welker
8f1fb86d37 fix: add NVX network port info interface 2025-07-07 09:49:01 -05:00
Neil Dorin
471d5b701b Merge pull request #1281 from PepperDash/combiner-auto-mode-enable 2025-06-27 08:48:11 -06:00
Andrew Welker
96ac266d24 fix: room combiner messenger sends disableAutoMode property 2025-06-27 10:45:42 -04:00
Andrew Welker
c1809459a6 chore: format property name correctly for JSON 2025-06-27 10:37:49 -04:00
Andrew Welker
1a9e1087de fix: add property to disable auto mode 2025-06-27 10:36:34 -04:00
Neil Dorin
8d55615837 Merge pull request #1280 from PepperDash/device-info-messenger 2025-06-26 16:08:49 -06:00
Andrew Welker
19e799f11d fix: debounce device info events
In some cases, multiple device info update events are triggering,
causing the queue to be flooded with multiple unneccessary messages containing the same info.
This clogs the queue and makes it harder for UIs to come online when Essentials restarts,
especially in systems with a lot of NVX devices.

The events are now debounced. If there are no new messages for 1 second, then the MC message
is sent out.
2025-06-26 17:12:36 -04:00
Neil Dorin
a3c1c444b7 Merge pull request #1279 from PepperDash/device-status-console 2025-06-26 12:27:43 -06:00
Andrew Welker
c9b3205736 fix: return --- if the device was created without a name 2025-06-26 14:14:04 -04:00
Andrew Welker
253b2cddaf fix: print device key & name instead of type 2025-06-26 13:54:24 -04:00
Andrew Welker
d96edfa8d0 fix: end devcommstatus with cr-lf instead of just -lf 2025-06-26 13:50:42 -04:00
Neil Dorin
2fa297a204 feat: Update .gitignore and enhance routing-related classes
- Updated `.gitignore` to include additional files and directories.
- Added summary comments and new properties in `LevelControlListItem.cs` for better clarity and functionality.
- Enhanced documentation in `SourceListItem.cs` and introduced new properties, including `Destinations` and a `ToString` method.
- Introduced `SourceRouteListItem` class with routing properties and expanded `eSourceListItemDestinationTypes` enum.
- Added `IRoutingSinkWithInputPort` interface in `IRoutingSink.cs` to support input port functionality.
2025-06-26 10:10:09 -06:00
aknous
95c1c01396 Merge pull request #1278 from PepperDash/feature/add-interfaces
Feature/add interfaces
2025-06-18 15:22:33 -04:00
Neil Dorin
9c94806e4f Update src/PepperDash.Essentials.Devices.Common/Codec/Cisco/IPresenterTrack.cs
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-06-18 13:21:32 -06:00
Neil Dorin
183879f1c4 feat: Add ApplyLayout method to IHasScreensWithLayouts
Introduced a new method `ApplyLayout` in the `IHasScreensWithLayouts` interface. This method enables the application of a specific layout to a screen using the provided screen ID and layout index. XML documentation has been added to clarify its purpose and parameters.
2025-06-18 11:11:12 -06:00
Neil Dorin
f3159738ce feat: Enhance layout and window configuration classes
Added `LayoutType` and `Windows` properties to the `LayoutInfo` class. Introduced a new `WindowConfig` class with `Label` and `Input` properties to represent window configurations within a layout.
2025-06-17 20:33:25 -06:00
Neil Dorin
2c5cae9f41 fix: Rename PresenterTrackMode to ePresenterTrackMode
Updated the enum name to reflect new naming conventions and potentially broader categorization within the codebase.
2025-06-17 19:18:30 -06:00
Neil Dorin
7178d8e284 featr: Add PresenterTrackMode enum to IPresenterTrack.cs
Introduces a new enumeration `PresenterTrackMode` that defines four tracking modes for the Cisco codec's Presenter Track feature: `Off`, `Follow`, `Background`, and `Persistent`. Each mode includes a summary comment for clarity.
2025-06-17 19:17:34 -06:00
Neil Dorin
af98a92f8c (force-patch): generate new build to test updated workflow nuget push issues 2025-06-17 19:01:13 -06:00
Neil Dorin
0a6896910d feat: Add screen/layout management and codec tracking features
Introduced new interfaces and classes for screen and layout management, including `IHasScreensWithLayouts`, `ScreenInfo`, and `LayoutInfo`. Enhanced `IPresenterTrack` and `ISpeakerTrack` interfaces with additional properties and methods for managing presenter and speaker tracking. Added `IHasCodecRoomPresets` interface for room preset management and updated `CodecRoomPreset` class with a new constructor.
2025-06-17 18:35:36 -06:00
Neil Dorin
9b1dd099f6 feat: Add IPresenterTrack and ISpeakerTrack interfaces
Introduced two new interfaces, `IPresenterTrack` and `ISpeakerTrack`, in the `PepperDash.Essentials.Devices.Common.Codec.Cisco` namespace. These interfaces provide properties and methods for managing presenter and speaker tracking functionalities in Cisco codecs, including availability, status feedback, and control methods.
2025-06-17 16:47:09 -06:00
aknous
3f5269de2f Merge pull request #1275 from PepperDash/mobile-control-direct-cs
Access MC from CS LAN
2025-06-06 17:32:15 -04:00
Andrew Welker
60f1adcd35 docs: fix spelling error
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-06-06 15:02:42 -05:00
Andrew Welker
12c8660015 feat: return correct config for CS processors
When MC is running on a processor with a control subnet and a user
attempts to access the UI via the control subnet, the return _config.local.json
needs to reflect the CS LAN IP address for the processor rather than the LAN IP Address.

In order to accomplish this, there will now be 2 config files written to disk on startup:
1. the original file, /user/programX/mcUserApp/_local-config/_config.local.json
2. A file with the correct CS IP Address /user/programX/mcUserApp/_local-config/_config.cs.json

When a user requests the _config.local.json, the processor will compare the remote IP address
with the CS LAN IP Address and determine if they're in the same subnet, the assumption being
that if the IP Addresses are in the same subnet, then the app or end user is on the CS LAN and needs the CS config.
If the addresses are in the same subnet, then the contents of the _config.cs.json will be returned.
2025-06-06 14:58:26 -05:00
76 changed files with 3914 additions and 1862 deletions

3
.gitignore vendored
View File

@@ -393,4 +393,5 @@ essentials-framework/Essentials Interfaces/PepperDash_Essentials_Interfaces/Pepp
/._PepperDash.Essentials.sln
.vscode/settings.json
_site/
api/
api/
*.DS_Store

View File

@@ -11,35 +11,35 @@ namespace PepperDash.Core
public class Device : IKeyName
{
/// <summary>
/// Unique Key
/// </summary>
/// <summary>
/// Unique Key
/// </summary>
public string Key { get; protected set; }
/// <summary>
/// Name of the devie
/// </summary>
public string Name { get; protected set; }
/// <summary>
///
/// </summary>
public string Name { get; protected set; }
/// <summary>
///
/// </summary>
public bool Enabled { get; protected set; }
///// <summary>
///// 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.
///// </summary>
//public DeviceConfig Config { get; private set; }
///// <summary>
///// Helper method to check if Config exists
///// </summary>
//public bool HasConfig { get { return Config != null; } }
///// <summary>
///// 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.
///// </summary>
//public DeviceConfig Config { get; private set; }
///// <summary>
///// Helper method to check if Config exists
///// </summary>
//public bool HasConfig { get { return Config != null; } }
List<Action> _PreActivationActions;
List<Action> _PostActivationActions;
/// <summary>
///
/// </summary>
/// <summary>
///
/// </summary>
public static Device DefaultDevice { get { return _DefaultDevice; } }
static Device _DefaultDevice = new Device("Default", "Default");
@@ -54,27 +54,27 @@ namespace PepperDash.Core
Name = "";
}
/// <summary>
/// Constructor with key and name
/// </summary>
/// <param name="key"></param>
/// <param name="name"></param>
/// <summary>
/// Constructor with key and name
/// </summary>
/// <param name="key"></param>
/// <param name="name"></param>
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;
//}
/// <summary>
/// Adds a pre activation action
/// </summary>
/// <param name="act"></param>
/// <summary>
/// Adds a pre activation action
/// </summary>
/// <param name="act"></param>
public void AddPreActivationAction(Action act)
{
if (_PreActivationActions == null)
@@ -82,10 +82,10 @@ namespace PepperDash.Core
_PreActivationActions.Add(act);
}
/// <summary>
/// Adds a post activation action
/// </summary>
/// <param name="act"></param>
/// <summary>
/// Adds a post activation action
/// </summary>
/// <param name="act"></param>
public void AddPostActivationAction(Action act)
{
if (_PostActivationActions == null)
@@ -93,55 +93,58 @@ namespace PepperDash.Core
_PostActivationActions.Add(act);
}
/// <summary>
/// Executes the preactivation actions
/// </summary>
public void PreActivate()
{
if (_PreActivationActions != null)
_PreActivationActions.ForEach(a => {
/// <summary>
/// Executes the preactivation actions
/// </summary>
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);
}
});
}
});
}
/// <summary>
/// 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()
/// </summary>
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;
}
/// <summary>
/// Executes the postactivation actions
/// </summary>
public void PostActivate()
{
if (_PostActivationActions != null)
_PostActivationActions.ForEach(a => {
try
{
a.Invoke();
}
catch (Exception e)
{
Debug.LogMessage(e, "Error in PostActivationAction: " + e.Message, this);
}
});
}
/// <summary>
/// Executes the postactivation actions
/// </summary>
public void PostActivate()
{
if (_PostActivationActions != null)
_PostActivationActions.ForEach(a =>
{
try
{
a.Invoke();
}
catch (Exception e)
{
Debug.LogMessage(e, "Error in PostActivationAction: " + e.Message, this);
}
});
}
/// <summary>
/// Called in between Pre and PostActivationActions when Activate() is called.
@@ -158,14 +161,14 @@ namespace PepperDash.Core
/// <returns></returns>
public virtual bool Deactivate() { return true; }
/// <summary>
/// Call this method to start communications with a device. Overriding classes do not need to call base.Initialize()
/// </summary>
public virtual void Initialize()
{
}
/// <summary>
/// Call this method to start communications with a device. Overriding classes do not need to call base.Initialize()
/// </summary>
public virtual void Initialize()
{
}
/// <summary>
/// <summary>
/// Helper method to check object for bool value false and fire an Action method
/// </summary>
/// <param name="o">Should be of type bool, others will be ignored</param>
@@ -175,5 +178,15 @@ namespace PepperDash.Core
if (o is bool && !(bool)o) a();
}
/// <summary>
/// Returns a string representation of the object, including its key and name.
/// </summary>
/// <remarks>The returned string is formatted as "{Key} - {Name}". If the <c>Name</c> property is
/// null or empty, "---" is used in place of the name.</remarks>
/// <returns>A string that represents the object, containing the key and name in the format "{Key} - {Name}".</returns>
public override string ToString()
{
return string.Format("{0} - {1}", Key, string.IsNullOrEmpty(Name) ? "---" : Name);
}
}
}

View File

@@ -0,0 +1,107 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PepperDash.Essentials.Core.DeviceTypeInterfaces
{
/// <summary>
/// This defines a device that has screens with layouts
/// Simply decorative
/// </summary>
public interface IHasScreensWithLayouts
{
/// <summary>
/// A dictionary of screens, keyed by screen ID, that contains information about each screen and its layouts.
/// </summary>
Dictionary<uint, ScreenInfo> Screens { get; }
/// <summary>
/// Applies a specific layout to a screen based on the provided screen ID and layout index.
/// </summary>
/// <param name="screenId"></param>
/// <param name="layoutIndex"></param>
void ApplyLayout(uint screenId, uint layoutIndex);
}
/// <summary>
/// Represents information about a screen and its layouts.
/// </summary>
public class ScreenInfo
{
/// <summary>
/// Indicates whether the screen is enabled or not.
/// </summary>
[JsonProperty("enabled")]
public bool Enabled { get; set; }
/// <summary>
/// The name of the screen.
/// </summary>
[JsonProperty("name")]
public string Name { get; set; }
/// <summary>
/// The index of the screen.
/// </summary>
[JsonProperty("screenIndex")]
public int ScreenIndex { get; set; }
/// <summary>
/// A dictionary of layout information for the screen, keyed by layout ID.
/// </summary>
[JsonProperty("layouts")]
public Dictionary<uint, LayoutInfo> Layouts { get; set; }
}
/// <summary>
/// Represents information about a layout on a screen.
/// </summary>
public class LayoutInfo
{
/// <summary>
/// The name of the layout.
/// </summary>
[JsonProperty("layoutName")]
public string LayoutName { get; set; }
/// <summary>
/// The index of the layout.
/// </summary>
[JsonProperty("layoutIndex")]
public int LayoutIndex { get; set; }
/// <summary>
/// The type of the layout, which can be "single", "double", "triple", or "quad".
/// </summary>
[JsonProperty("layoutType")]
public string LayoutType { get; set; }
/// <summary>
/// A dictionary of window configurations for the layout, keyed by window ID.
/// </summary>
[JsonProperty("windows")]
public Dictionary<uint, WindowConfig> Windows { get; set; }
}
/// <summary>
/// Represents the configuration of a window within a layout on a screen.
/// </summary>
public class WindowConfig
{
/// <summary>
/// The display label for the window
/// </summary>
[JsonProperty("label")]
public string Label { get; set; }
/// <summary>
/// The input for the window
/// </summary>
[JsonProperty("input")]
public string Input { get; set; }
}
}

View File

@@ -0,0 +1,90 @@
using Crestron.SimplSharpPro.DM.Streaming;
using System;
using System.Collections.Generic;
namespace PepperDash.Essentials.Core
{
/// <summary>
/// Represents a collection of network port information and provides notifications when the information changes.
/// </summary>
/// <remarks>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 <see
/// cref="PortInformationChanged"/> event is raised whenever the <see cref="NetworkPorts"/> collection
/// changes.</remarks>
public interface INvxNetworkPortInformation
{
/// <summary>
/// Occurs when the port information changes.
/// </summary>
/// <remarks>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.</remarks>
event EventHandler PortInformationChanged;
/// <summary>
/// Gets the collection of network port information associated with the current instance.
/// </summary>
/// <remarks>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.</remarks>
List<NvxNetworkPortInformation> NetworkPorts { get; }
}
/// <summary>
/// Represents information about a network port, including its configuration and associated system details.
/// </summary>
/// <remarks>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.</remarks>
public class NvxNetworkPortInformation
{
private readonly DmNvxBaseClass.DmNvx35xNetwork.DmNvxNetworkLldpPort port;
/// <summary>
/// Gets or sets the index of the device port.
/// </summary>
public uint DevicePortIndex { get; }
/// <summary>
/// Gets or sets the name of the port used for communication.
/// </summary>
public string PortName => port.PortNameFeedback.StringValue;
/// <summary>
/// Gets or sets the description of the port.
/// </summary>
public string PortDescription => port.PortNameDescriptionFeedback.StringValue;
/// <summary>
/// Gets or sets the name of the VLAN (Virtual Local Area Network).
/// </summary>
public string VlanName => port.VlanNameFeedback.StringValue;
/// <summary>
/// Gets the IP management address associated with the port.
/// </summary>
public string IpManagementAddress => port.IpManagementAddressFeedback.StringValue;
/// <summary>
/// Gets the name of the system as reported by the associated port.
/// </summary>
public string SystemName => port.SystemNameFeedback.StringValue;
/// <summary>
/// Gets the description of the system name.
/// </summary>
public string SystemNameDescription => port.SystemNameDescriptionFeedback.StringValue;
/// <summary>
/// Initializes a new instance of the <see cref="NvxNetworkPortInformation"/> class with the specified network port
/// and device port index.
/// </summary>
/// <param name="port">The network port associated with the device. Cannot be <see langword="null"/>.</param>
/// <param name="devicePortIndex">The index of the device port.</param>
/// <exception cref="ArgumentNullException">Thrown if <paramref name="port"/> is <see langword="null"/>.</exception>
public NvxNetworkPortInformation(DmNvxBaseClass.DmNvx35xNetwork.DmNvxNetworkLldpPort port, uint devicePortIndex)
{
this.port = port ?? throw new ArgumentNullException(nameof(port), "Port cannot be null");
DevicePortIndex = devicePortIndex;
}
}
}

View File

@@ -248,7 +248,7 @@ namespace PepperDash.Essentials.Core
foreach (var dev in Devices.Values.OfType<ICommunicationMonitor>())
{
CrestronConsole.ConsoleCommandResponse($"{dev}: {dev.CommunicationMonitor.Status}{Environment.NewLine}");
CrestronConsole.ConsoleCommandResponse($"{dev}: {dev.CommunicationMonitor.Status}\r\n");
}
}

View File

@@ -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
/// <summary>
/// Override this method to build and create custom Mobile Control Messengers during the Activation phase
/// </summary>
protected virtual void CreateMobileControlMessengers() {
protected virtual void CreateMobileControlMessengers()
{
}
}

View File

@@ -9,10 +9,15 @@ using PepperDash.Essentials.Core.Devices;
namespace PepperDash.Essentials.Core
{
/// <summary>
/// Represents a level control item in a list, which can be used to control volume or mute functionality.
/// </summary>
public class LevelControlListItem : AudioControlListItemBase
{
/// <summary>
/// A reference to the IBasicVolumeWithFeedback device for control.
/// </summary>
[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; }
/// <summary>
/// Indicates if the item is a mic or not.
/// </summary>
[JsonProperty("isMic", NullValueHandling = NullValueHandling.Ignore)]
public bool? IsMic { get; set; }
/// <summary>
/// Indicates if the item should show the raw level in the UI.
/// </summary>
[JsonProperty("showRawLevel", NullValueHandling = NullValueHandling.Ignore)]
public bool? ShowRawLevel { get; set; }
}
/// <summary>
/// Indicates the type of level control item.
/// </summary>
[Flags]
public enum eLevelControlType
{
/// <summary>
/// Indicates that the item is a level control only
/// </summary>
Level = 1,
/// <summary>
/// Indicates that the item is a mute control only
/// </summary>
Mute = 2,
/// <summary>
/// Indicates that the item is both a level and mute control
/// </summary>
LevelAndMute = Level | Mute,
}
}

View File

@@ -10,7 +10,18 @@ namespace PepperDash.Essentials.Core
/// </summary>
public enum eSourceListItemType
{
Route, Off, Other, SomethingAwesomerThanThese
/// <summary>
/// Represents a typical route.
/// </summary>
Route,
/// <summary>
/// Represents an off route.
/// </summary>
Off,
/// <summary>
/// Represents some other type of route
/// </summary>
Other,
}
/// <summary>
@@ -18,6 +29,9 @@ namespace PepperDash.Essentials.Core
/// </summary>
public class SourceListItem
{
/// <summary>
/// The key of the source item, which is used to identify it in the DeviceManager
/// </summary>
[JsonProperty("sourceKey")]
public string SourceKey { get; set; }
@@ -117,6 +131,9 @@ namespace PepperDash.Essentials.Core
[JsonProperty("disableRoutedSharing")]
public bool DisableRoutedSharing { get; set; }
/// <summary>
///
/// </summary>
[JsonProperty("destinations")]
public List<eSourceListItemDestinationTypes> Destinations { get; set; }
/// <summary>
@@ -149,31 +166,56 @@ namespace PepperDash.Essentials.Core
[JsonProperty("disableSimpleRouting")]
public bool DisableSimpleRouting { get; set; }
/// <summary>
/// Default constructor for SourceListItem, initializes the Icon to "Blank"
/// </summary>
public SourceListItem()
{
Icon = "Blank";
}
/// <summary>
/// Returns a string representation of the SourceListItem, including the SourceKey and Name
/// </summary>
/// <returns></returns>
public override string ToString()
{
return $"{SourceKey}:{Name}";
}
}
/// <summary>
/// Represents a route in a source list item, which defines the source and destination keys and the type of signal being routed
/// </summary>
public class SourceRouteListItem
{
/// <summary>
/// The key of the source device to route from
/// </summary>
[JsonProperty("sourceKey")]
public string SourceKey { get; set; }
/// <summary>
/// The key of the source port to route from
/// </summary>
[JsonProperty("sourcePortKey")]
public string SourcePortKey { get; set; }
/// <summary>
/// The key of the destination device to route to
/// </summary>
[JsonProperty("destinationKey")]
public string DestinationKey { get; set; }
/// <summary>
/// The key of the destination port to route to
/// </summary>
[JsonProperty("destinationPortKey")]
public string DestinationPortKey { get; set; }
/// <summary>
/// The type of signal being routed, such as audio or video
/// </summary>
[JsonProperty("type")]
public eRoutingSignalType Type { get; set; }
}
@@ -183,15 +225,85 @@ namespace PepperDash.Essentials.Core
/// </summary>
public enum eSourceListItemDestinationTypes
{
/// <summary>
/// Default display, used for the main video output in a room
/// </summary>
defaultDisplay,
/// <summary>
/// Left display
/// </summary>
leftDisplay,
/// <summary>
/// Right display
/// </summary>
rightDisplay,
/// <summary>
/// Center display
/// </summary>
centerDisplay,
/// <summary>
/// Program audio, used for the main audio output in a room
/// </summary>
programAudio,
/// <summary>
/// Codec content, used for sharing content to the far end in a video call
/// </summary>
codecContent,
/// <summary>
/// Front left display, used for rooms with multiple displays
/// </summary>
frontLeftDisplay,
/// <summary>
/// Front right display, used for rooms with multiple displays
/// </summary>
frontRightDisplay,
/// <summary>
/// Rear left display, used for rooms with multiple displays
/// </summary>
rearLeftDisplay,
/// <summary>
/// Rear right display, used for rooms with multiple displays
/// </summary>
rearRightDisplay,
/// <summary>
/// Auxiliary display 1, used for additional displays in a room
/// </summary>
auxDisplay1,
/// <summary>
/// Auxiliary display 2, used for additional displays in a room
/// </summary>
auxDisplay2,
/// <summary>
/// Auxiliary display 3, used for additional displays in a room
/// </summary>
auxDisplay3,
/// <summary>
/// Auxiliary display 4, used for additional displays in a room
/// </summary>
auxDisplay4,
/// <summary>
/// Auxiliary display 5, used for additional displays in a room
/// </summary>
auxDisplay5,
/// <summary>
/// Auxiliary display 6, used for additional displays in a room
/// </summary>
auxDisplay6,
/// <summary>
/// Auxiliary display 7, used for additional displays in a room
/// </summary>
auxDisplay7,
/// <summary>
/// Auxiliary display 8, used for additional displays in a room
/// </summary>
auxDisplay8,
/// <summary>
/// Auxiliary display 9, used for additional displays in a room
/// </summary>
auxDisplay9,
/// <summary>
/// Auxiliary display 10, used for additional displays in a room
/// </summary>
auxDisplay10,
}
}

View File

@@ -0,0 +1,86 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
namespace PepperDash.Essentials.Core
{
/// <summary>
/// Extensions for IPAddress to provide additional functionality such as getting broadcast address, network address, and checking if two addresses are in the same subnet.
/// </summary>
public static class IPAddressExtensions
{
/// <summary>
/// Get the broadcast address for a given IP address and subnet mask.
/// </summary>
/// <param name="address">Address to check</param>
/// <param name="subnetMask">Subnet mask in a.b.c.d format</param>
/// <returns>Broadcast address</returns>
/// <remarks>
/// If the input IP address is 192.168.1.100 and the subnet mask is 255.255.255.0, the broadcast address will be 192.168.1.255
/// </remarks>
/// <exception cref="ArgumentException"></exception>
public static IPAddress GetBroadcastAddress(this IPAddress address, IPAddress subnetMask)
{
byte[] ipAdressBytes = address.GetAddressBytes();
byte[] subnetMaskBytes = subnetMask.GetAddressBytes();
if (ipAdressBytes.Length != subnetMaskBytes.Length)
throw new ArgumentException("Lengths of IP address and subnet mask do not match.");
byte[] broadcastAddress = new byte[ipAdressBytes.Length];
for (int i = 0; i < broadcastAddress.Length; i++)
{
broadcastAddress[i] = (byte)(ipAdressBytes[i] | (subnetMaskBytes[i] ^ 255));
}
return new IPAddress(broadcastAddress);
}
/// <summary>
/// Get the network address for a given IP address and subnet mask.
/// </summary>
/// <param name="address">Address to check</param>
/// <param name="subnetMask">Subnet mask in a.b.c.d</param>
/// <returns>Network Address</returns>
/// /// <remarks>
/// If the input IP address is 192.168.1.100 and the subnet mask is 255.255.255.0, the network address will be 192.168.1.0
/// </remarks>
/// <exception cref="ArgumentException"></exception>
public static IPAddress GetNetworkAddress(this IPAddress address, IPAddress subnetMask)
{
byte[] ipAdressBytes = address.GetAddressBytes();
byte[] subnetMaskBytes = subnetMask.GetAddressBytes();
if (ipAdressBytes.Length != subnetMaskBytes.Length)
throw new ArgumentException("Lengths of IP address and subnet mask do not match.");
byte[] broadcastAddress = new byte[ipAdressBytes.Length];
for (int i = 0; i < broadcastAddress.Length; i++)
{
broadcastAddress[i] = (byte)(ipAdressBytes[i] & (subnetMaskBytes[i]));
}
return new IPAddress(broadcastAddress);
}
/// <summary>
/// Determine if two IP addresses are in the same subnet.
/// </summary>
/// <param name="address2">Address to check</param>
/// <param name="address">Second address to check</param>
/// <param name="subnetMask">Subnet mask to use to compare the 2 IP Address</param>
/// <returns>True if addresses are in the same subnet</returns>
/// <remarks>
/// If the input IP addresses are 192.168.1.100 and 192.168.1.200, and the subnet mask is 255.255.255.0, this will return true.
/// If the input IP addresses are 10.1.1.100 and 192.168.1.100, and the subnet mask is 255.255.255.0, this will return false.
/// </remarks>
public static bool IsInSameSubnet(this IPAddress address2, IPAddress address, IPAddress subnetMask)
{
IPAddress network1 = address.GetNetworkAddress(subnetMask);
IPAddress network2 = address2.GetNetworkAddress(subnetMask);
return network1.Equals(network2);
}
}
}

View File

@@ -10,6 +10,13 @@ using System.Threading.Tasks;
namespace PepperDash.Essentials.Core
{
/// <summary>
/// Represents a device that manages room combinations by controlling partitions and scenarios.
/// </summary>
/// <remarks>The <see cref="EssentialsRoomCombiner"/> 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.</remarks>
public class EssentialsRoomCombiner : EssentialsDevice, IEssentialsRoomCombiner
{
private EssentialsRoomCombinerPropertiesConfig _propertiesConfig;
@@ -18,6 +25,9 @@ namespace PepperDash.Essentials.Core
private List<IEssentialsRoom> _rooms;
/// <summary>
/// Gets a list of rooms represented as key-name pairs.
/// </summary>
public List<IKeyName> Rooms
{
get
@@ -28,6 +38,12 @@ namespace PepperDash.Essentials.Core
private bool _isInAutoMode;
/// <summary>
/// Gets or sets a value indicating whether the system is operating in automatic mode.
/// </summary>
/// <remarks>Changing this property triggers an update event via
/// <c>IsInAutoModeFeedback.FireUpdate()</c>. Ensure that any event listeners are properly configured to handle
/// this update.</remarks>
public bool IsInAutoMode
{
get
@@ -46,12 +62,36 @@ namespace PepperDash.Essentials.Core
}
}
/// <summary>
/// Gets a value indicating whether automatic mode is disabled.
/// </summary>
public bool DisableAutoMode
{
get
{
return _propertiesConfig.DisableAutoMode;
}
}
private CTimer _scenarioChangeDebounceTimer;
private int _scenarioChangeDebounceTimeSeconds = 10; // default to 10s
private Mutex _scenarioChange = new Mutex();
/// <summary>
/// Initializes a new instance of the <see cref="EssentialsRoomCombiner"/> class, which manages room combination
/// scenarios and partition states.
/// </summary>
/// <remarks>The <see cref="EssentialsRoomCombiner"/> 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 <paramref name="props"/>
/// specifies otherwise. After activation, the room combiner initializes partition state providers and sets up
/// the initial room configuration. Additionally, it subscribes to the <see
/// cref="DeviceManager.AllDevicesInitialized"/> event to ensure proper initialization of dependent devices
/// before determining or setting the room combination scenario.</remarks>
/// <param name="key">The unique identifier for the room combiner instance.</param>
/// <param name="props">The configuration properties for the room combiner, including default settings and debounce times.</param>
public EssentialsRoomCombiner(string key, EssentialsRoomCombinerPropertiesConfig props)
: base(key)
{
@@ -246,8 +286,16 @@ namespace PepperDash.Essentials.Core
#region IEssentialsRoomCombiner Members
/// <summary>
/// Occurs when the room combination scenario changes.
/// </summary>
/// <remarks>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.</remarks>
public event EventHandler<EventArgs> RoomCombinationScenarioChanged;
/// <summary>
/// Gets the current room combination scenario.
/// </summary>
public IRoomCombinationScenario CurrentScenario
{
get
@@ -256,10 +304,25 @@ namespace PepperDash.Essentials.Core
}
}
/// <summary>
/// Gets the feedback indicating whether the system is currently in auto mode.
/// </summary>
public BoolFeedback IsInAutoModeFeedback { get; private set; }
/// <summary>
/// Enables auto mode for the room combiner and its partitions, allowing automatic room combination scenarios to
/// be determined.
/// </summary>
/// <remarks>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.</remarks>
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();
}
/// <summary>
/// Switches the system to manual mode, disabling automatic operations.
/// </summary>
/// <remarks>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.</remarks>
public void SetManualMode()
{
IsInAutoMode = false;
@@ -280,6 +349,11 @@ namespace PepperDash.Essentials.Core
}
}
/// <summary>
/// Toggles the current mode between automatic and manual.
/// </summary>
/// <remarks>If the current mode is automatic, this method switches to manual mode. If the
/// current mode is manual, it switches to automatic mode.</remarks>
public void ToggleMode()
{
if (IsInAutoMode)
@@ -292,10 +366,22 @@ namespace PepperDash.Essentials.Core
}
}
/// <summary>
/// Gets the collection of room combination scenarios.
/// </summary>
public List<IRoomCombinationScenario> RoomCombinationScenarios { get; private set; }
/// <summary>
/// Gets the collection of partition controllers managed by this instance.
/// </summary>
public List<IPartitionController> Partitions { get; private set; }
/// <summary>
/// Toggles the state of the partition identified by the specified partition key.
/// </summary>
/// <remarks>If no partition with the specified key exists, the method performs no
/// action.</remarks>
/// <param name="partitionKey">The key of the partition whose state is to be toggled. This value cannot be null or empty.</param>
public void TogglePartitionState(string partitionKey)
{
var partition = Partitions.FirstOrDefault((p) => p.Key.Equals(partitionKey));
@@ -306,6 +392,17 @@ namespace PepperDash.Essentials.Core
}
}
/// <summary>
/// Sets the room combination scenario based on the specified scenario key.
/// </summary>
/// <remarks>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.</remarks>
/// <param name="scenarioKey">The key identifying the room combination scenario to apply. This must match the key of an existing scenario.</param>
public void SetRoomCombinationScenario(string scenarioKey)
{
if (IsInAutoMode)
@@ -354,13 +451,32 @@ namespace PepperDash.Essentials.Core
#endregion
}
/// <summary>
/// Provides a factory for creating instances of <see cref="EssentialsRoomCombiner"/> devices.
/// </summary>
/// <remarks>This factory is responsible for constructing <see cref="EssentialsRoomCombiner"/> devices
/// based on the provided configuration. It supports the type name "essentialsroomcombiner" for device
/// creation.</remarks>
public class EssentialsRoomCombinerFactory : EssentialsDeviceFactory<EssentialsRoomCombiner>
{
/// <summary>
/// Initializes a new instance of the <see cref="EssentialsRoomCombinerFactory"/> class.
/// </summary>
/// <remarks>This factory is used to create instances of room combiners with the specified type
/// names. By default, the factory includes the type name "essentialsroomcombiner".</remarks>
public EssentialsRoomCombinerFactory()
{
TypeNames = new List<string> { "essentialsroomcombiner" };
}
/// <summary>
/// Creates and initializes a new instance of the <see cref="EssentialsRoomCombiner"/> device.
/// </summary>
/// <remarks>This method uses the provided device configuration to extract the properties and
/// create an <see cref="EssentialsRoomCombiner"/> device. Ensure that the configuration contains valid
/// properties for the device to be created successfully.</remarks>
/// <param name="dc">The device configuration containing the key and properties required to build the device.</param>
/// <returns>A new instance of <see cref="EssentialsRoomCombiner"/> initialized with the specified configuration.</returns>
public override EssentialsDevice BuildDevice(PepperDash.Essentials.Core.Config.DeviceConfig dc)
{
Debug.LogMessage(LogEventLevel.Debug, "Factory Attempting to create new EssentialsRoomCombiner Device");

View File

@@ -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
/// </summary>
public class EssentialsRoomCombinerPropertiesConfig
{
/// <summary>
/// Gets or sets a value indicating whether the system operates in automatic mode.
/// <remarks>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.</remarks>
/// </summary>
[JsonProperty("disableAutoMode")]
public bool DisableAutoMode { get; set; }
/// <summary>
/// The list of partitions that device the rooms
/// </summary>
@@ -47,6 +49,9 @@ namespace PepperDash.Essentials.Core
[JsonProperty("defaultScenarioKey")]
public string defaultScenarioKey { get; set; }
/// <summary>
/// Gets or sets the debounce time, in seconds, for scenario changes.
/// </summary>
[JsonProperty("scenarioChangeDebounceTimeSeconds")]
public int ScenarioChangeDebounceTimeSeconds { get; set; }
}
@@ -56,9 +61,15 @@ namespace PepperDash.Essentials.Core
/// </summary>
public class PartitionConfig : IKeyName
{
/// <summary>
/// Gets or sets the unique key associated with the object.
/// </summary>
[JsonProperty("key")]
public string Key { get; set; }
/// <summary>
/// Gets or sets the name associated with the object.
/// </summary>
[JsonProperty("name")]
public string Name { get; set; }
@@ -80,12 +91,21 @@ namespace PepperDash.Essentials.Core
/// </summary>
public class RoomCombinationScenarioConfig : IKeyName
{
/// <summary>
/// Gets or sets the key associated with the object.
/// </summary>
[JsonProperty("key")]
public string Key { get; set; }
/// <summary>
/// Gets or sets the name associated with the object.
/// </summary>
[JsonProperty("name")]
public string Name { get; set; }
/// <summary>
/// Gets or sets the collection of partition states.
/// </summary>
[JsonProperty("partitionStates")]
public List<PartitionState> PartitionStates { get; set; }
@@ -95,9 +115,15 @@ namespace PepperDash.Essentials.Core
[JsonProperty("uiMap")]
public Dictionary<string, string> UiMap { get; set; }
/// <summary>
/// Gets or sets the list of actions to be performed during device activation.
/// </summary>
[JsonProperty("activationActions")]
public List<DeviceActionWrapper> ActivationActions { get; set; }
/// <summary>
/// Gets or sets the list of actions to be performed when a device is deactivated.
/// </summary>
[JsonProperty("deactivationActions")]
public List<DeviceActionWrapper> DeactivationActions { get; set; }
}
@@ -107,9 +133,15 @@ namespace PepperDash.Essentials.Core
/// </summary>
public class PartitionState
{
/// <summary>
/// Gets or sets the partition key used to group and organize data within a storage system.
/// </summary>
[JsonProperty("partitionKey")]
public string PartitionKey { get; set; }
/// <summary>
/// Gets or sets a value indicating whether a partition is currently present.
/// </summary>
[JsonProperty("partitionSensedState")]
public bool PartitionPresent { get; set; }
}

View File

@@ -28,9 +28,20 @@ namespace PepperDash.Essentials.Core
[JsonIgnore]
BoolFeedback IsInAutoModeFeedback {get;}
/// <summary>
/// Gets a value indicating whether the automatic mode is disabled.
/// </summary>
[JsonProperty("disableAutoMode")]
bool DisableAutoMode { get; }
/// <summary>
/// Gets a value indicating whether the system is operating in automatic mode.
/// </summary>
[JsonProperty("isInAutoMode")]
bool IsInAutoMode { get; }
/// <summary>
/// Gets the collection of rooms associated with the current object.
/// </summary>
[JsonProperty("rooms")]
List<IKeyName> Rooms { get; }
@@ -74,6 +85,13 @@ namespace PepperDash.Essentials.Core
void SetRoomCombinationScenario(string scenarioKey);
}
/// <summary>
/// Represents a scenario for combining rooms, including activation, deactivation, and associated state.
/// </summary>
/// <remarks>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.</remarks>
public interface IRoomCombinationScenario : IKeyName
{
/// <summary>
@@ -82,6 +100,9 @@ namespace PepperDash.Essentials.Core
[JsonIgnore]
BoolFeedback IsActiveFeedback { get; }
/// <summary>
/// Gets a value indicating whether the entity is active.
/// </summary>
[JsonProperty("isActive")]
bool IsActive { get; }

View File

@@ -7,8 +7,14 @@
{
}
/// <summary>
/// For fixed-source endpoint devices with an input port
/// </summary>
public interface IRoutingSinkWithInputPort :IRoutingSink
{
/// <summary>
/// Gets the current input port for this routing sink.
/// </summary>
RoutingInputPort CurrentInputPort { get; }
}
/*/// <summary>

View File

@@ -0,0 +1,95 @@
using PepperDash.Core;
using PepperDash.Essentials.Core;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PepperDash.Essentials.Devices.Common.Codec.Cisco
{
/// <summary>
/// Describes the available tracking modes for a Cisco codec's Presenter Track feature.
/// </summary>
public enum ePresenterTrackMode
{
/// <summary>
/// Presenter Track is turned off.
/// </summary>
Off,
/// <summary>
/// Presenter Track follows the speaker's movements.
/// </summary>
Follow,
/// <summary>
/// Presenter Track is set to background mode, where it tracks the speaker but does not actively follow.
/// </summary>
Background,
/// <summary>
/// Presenter Track is set to persistent mode, where it maintains a fixed position or focus on the speaker.
/// </summary>
Persistent
}
/// <summary>
/// Describes the Presenter Track controls for a Cisco codec.
/// </summary>
public interface IPresenterTrack : IKeyed
{
/// <summary>
///
/// </summary>
bool PresenterTrackAvailability { get; }
/// <summary>
/// Feedback indicating whether Presenter Track is available.
/// </summary>
BoolFeedback PresenterTrackAvailableFeedback { get; }
/// <summary>
/// Feedback indicating the current status of Presenter Track is off
/// </summary>
BoolFeedback PresenterTrackStatusOffFeedback { get; }
/// <summary>
/// Feedback indicating the current status of Presenter Track is follow
/// </summary>
BoolFeedback PresenterTrackStatusFollowFeedback { get; }
/// <summary>
/// Feedback indicating the current status of Presenter Track is background
/// </summary>
BoolFeedback PresenterTrackStatusBackgroundFeedback { get; }
/// <summary>
/// Feedback indicating the current status of Presenter Track is persistent
/// </summary>
BoolFeedback PresenterTrackStatusPersistentFeedback { get; }
/// <summary>
/// Indicates the current status of Presenter Track.
/// </summary>
bool PresenterTrackStatus { get; }
/// <summary>
/// Turns off Presenter Track.
/// </summary>
void PresenterTrackOff();
/// <summary>
/// Turns on Presenter Track in follow mode.
/// </summary>
void PresenterTrackFollow();
/// <summary>
/// Turns on Presenter Track in background mode.
/// </summary>
void PresenterTrackBackground();
/// <summary>
/// Turns on Presenter Track in persistent mode.
/// </summary>
void PresenterTrackPersistent();
}
}

View File

@@ -0,0 +1,40 @@
using PepperDash.Core;
using PepperDash.Essentials.Core;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PepperDash.Essentials.Devices.Common.Codec.Cisco
{
/// <summary>
/// Describes the available tracking modes for a Cisco codec
/// </summary>
public interface ISpeakerTrack : IKeyed
{
/// <summary>
/// Indicates whether Speaker Track is available on the codec.
/// </summary>
bool SpeakerTrackAvailability { get; }
/// <summary>
///
/// </summary>
BoolFeedback SpeakerTrackAvailableFeedback { get; }
/// <summary>
/// Feedback indicating the current status of Speaker Track is off
/// </summary>
bool SpeakerTrackStatus { get; }
/// <summary>
/// Turns Speaker Track off
/// </summary>
void SpeakerTrackOff();
/// <summary>
/// Turns Speaker Track on
/// </summary>
void SpeakerTrackOn();
}
}

View File

@@ -12,20 +12,45 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec
/// </summary>
public interface IHasCodecRoomPresets
{
/// <summary>
/// Event that is raised when the list of room presets has changed.
/// </summary>
event EventHandler<EventArgs> CodecRoomPresetsListHasChanged;
/// <summary>
/// List of near end presets that can be recalled.
/// </summary>
List<CodecRoomPreset> NearEndPresets { get; }
/// <summary>
/// List of far end presets that can be recalled.
/// </summary>
List<CodecRoomPreset> FarEndRoomPresets { get; }
/// <summary>
/// Selects a near end preset by its ID.
/// </summary>
/// <param name="preset"></param>
void CodecRoomPresetSelect(int preset);
void CodecRoomPresetStore(int preset, string description);
/// <summary>
/// Stores a near end preset with the given ID and description.
/// </summary>
/// <param name="preset"></param>
/// <param name="description"></param>
void CodecRoomPresetStore(int preset, string description);
/// <summary>
/// Selects a far end preset by its ID. This is typically used to recall a preset that has been defined on the far end codec.
/// </summary>
/// <param name="preset"></param>
void SelectFarEndPreset(int preset);
}
public static class RoomPresets
/// <summary>
/// Static class for converting non-generic RoomPresets to generic CameraPresets.
/// </summary>
public static class RoomPresets
{
/// <summary>
/// Converts non-generic RoomPresets to generic CameraPresets
@@ -47,6 +72,13 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec
/// </summary>
public class CodecRoomPreset : PresetBase
{
/// <summary>
///
/// </summary>
/// <param name="id"></param>
/// <param name="description"></param>
/// <param name="def"></param>
/// <param name="isDef"></param>
public CodecRoomPreset(int id, string description, bool def, bool isDef)
: base(id, description, def, isDef)
{

View File

@@ -3,29 +3,51 @@ using PepperDash.Essentials.Core;
namespace PepperDash.Essentials.AppServer
{
/// <summary>
/// Represents the content of a source selection message
/// </summary>
public class SourceSelectMessageContent
{
/// <summary>
/// Gets or sets the key of the source list item to select
/// </summary>
[JsonProperty("sourceListItemKey")]
public string SourceListItemKey { get; set; }
/// <summary>
/// Gets or sets the key of the source list containing the item
/// </summary>
[JsonProperty("sourceListKey")]
public string SourceListKey { get; set; }
}
/// <summary>
/// Represents a direct routing operation between a source and destination
/// </summary>
public class DirectRoute
{
/// <summary>
/// Gets or sets the key of the source device
/// </summary>
[JsonProperty("sourceKey")]
public string SourceKey { get; set; }
/// <summary>
/// Gets or sets the key of the destination device
/// </summary>
[JsonProperty("destinationKey")]
public string DestinationKey { get; set; }
/// <summary>
/// Gets or sets the type of routing signal (Audio, Video, etc.)
/// </summary>
[JsonProperty("signalType")]
public eRoutingSignalType SignalType { get; set; }
}
/// <summary>
///
/// Delegate for press and hold actions with boolean state parameter
/// </summary>
/// <param name="b"></param>
/// <param name="b">The state of the press and hold action</param>
public delegate void PressAndHoldAction(bool b);
}

View File

@@ -1,22 +1,35 @@
using Newtonsoft.Json.Linq;
using System.Linq;
using Newtonsoft.Json.Linq;
using PepperDash.Core;
using PepperDash.Core.Logging;
using PepperDash.Essentials.AppServer;
using PepperDash.Essentials.AppServer.Messengers;
using System.Linq;
using DisplayBase = PepperDash.Essentials.Devices.Common.Displays.DisplayBase;
namespace PepperDash.Essentials.Room.MobileControl
{
/// <summary>
/// Messenger that provides mobile control interface for display devices
/// </summary>
public class DisplayBaseMessenger : MessengerBase
{
/// <summary>
/// The display device this messenger is associated with
/// </summary>
private readonly DisplayBase display;
/// <summary>
/// Initializes a new instance of the DisplayBaseMessenger class
/// </summary>
/// <param name="key">The unique key for this messenger</param>
/// <param name="messagePath">The message path for routing display control messages</param>
/// <param name="device">The display device to control</param>
public DisplayBaseMessenger(string key, string messagePath, DisplayBase device) : base(key, messagePath, device)
{
display = device;
}
/// <inheritdoc />
protected override void RegisterActions()
{
base.RegisterActions();

View File

@@ -4,15 +4,28 @@ using PepperDash.Essentials.Core;
namespace PepperDash.Essentials.Room.MobileControl
{
/// <summary>
/// Messenger that provides mobile control interface for devices with channel control functionality
/// </summary>
public class IChannelMessenger : MessengerBase
{
/// <summary>
/// The channel control device this messenger is associated with
/// </summary>
private readonly IChannel channelDevice;
/// <summary>
/// Initializes a new instance of the IChannelMessenger class
/// </summary>
/// <param name="key">The unique key for this messenger</param>
/// <param name="messagePath">The message path for routing channel control messages</param>
/// <param name="device">The device that implements channel control functionality</param>
public IChannelMessenger(string key, string messagePath, IChannel device) : base(key, messagePath, device as IKeyName)
{
channelDevice = device;
}
/// <inheritdoc />
protected override void RegisterActions()
{
base.RegisterActions();

View File

@@ -4,14 +4,25 @@ using PepperDash.Essentials.Core;
namespace PepperDash.Essentials.Room.MobileControl
{
/// <summary>
/// Messenger for devices that implement IColor interface
/// </summary>
public class IColorMessenger : MessengerBase
{
private readonly IColor colorDevice;
/// <summary>
/// Initializes a new instance of the IColorMessenger class
/// </summary>
/// <param name="key">Unique identifier for the messenger</param>
/// <param name="messagePath">Path for message routing</param>
/// <param name="device">Device that implements IColor</param>
public IColorMessenger(string key, string messagePath, IColor device) : base(key, messagePath, device as IKeyName)
{
colorDevice = device as IColor;
}
/// <inheritdoc />
protected override void RegisterActions()
{
base.RegisterActions();

View File

@@ -4,15 +4,28 @@ using PepperDash.Essentials.Core;
namespace PepperDash.Essentials.Room.MobileControl
{
/// <summary>
/// Messenger that provides mobile control interface for devices with directional pad functionality
/// </summary>
public class IDPadMessenger : MessengerBase
{
/// <summary>
/// The directional pad device this messenger is associated with
/// </summary>
private readonly IDPad dpadDevice;
/// <summary>
/// Initializes a new instance of the IDPadMessenger class
/// </summary>
/// <param name="key">The unique key for this messenger</param>
/// <param name="messagePath">The message path for routing directional pad messages</param>
/// <param name="device">The device that implements directional pad functionality</param>
public IDPadMessenger(string key, string messagePath, IDPad device) : base(key, messagePath, device as IKeyName)
{
dpadDevice = device;
}
/// <inheritdoc />
protected override void RegisterActions()
{
base.RegisterActions();

View File

@@ -4,14 +4,25 @@ using PepperDash.Essentials.Core;
namespace PepperDash.Essentials.Room.MobileControl
{
/// <summary>
/// Messenger for devices that implement IDvr interface
/// </summary>
public class IDvrMessenger : MessengerBase
{
private readonly IDvr dvrDevice;
/// <summary>
/// Initializes a new instance of the IDvrMessenger class
/// </summary>
/// <param name="key">Unique identifier for the messenger</param>
/// <param name="messagePath">Path for message routing</param>
/// <param name="device">Device that implements IDvr</param>
public IDvrMessenger(string key, string messagePath, IDvr device) : base(key, messagePath, device as IKeyName)
{
dvrDevice = device;
}
/// <inheritdoc />
protected override void RegisterActions()
{
base.RegisterActions();

View File

@@ -4,14 +4,28 @@ using PepperDash.Essentials.Core;
namespace PepperDash.Essentials.Room.MobileControl
{
/// <summary>
/// Messenger that provides mobile control interface for devices with power control functionality
/// </summary>
public class IHasPowerMessenger : MessengerBase
{
/// <summary>
/// The power control device this messenger is associated with
/// </summary>
private readonly IHasPowerControl powerDevice;
/// <summary>
/// Initializes a new instance of the IHasPowerMessenger class
/// </summary>
/// <param name="key">The unique key for this messenger</param>
/// <param name="messagePath">The message path for routing power control messages</param>
/// <param name="device">The device that implements power control functionality</param>
public IHasPowerMessenger(string key, string messagePath, IHasPowerControl device) : base(key, messagePath, device as IKeyName)
{
powerDevice = device;
}
/// <inheritdoc />
protected override void RegisterActions()
{
base.RegisterActions();

View File

@@ -4,14 +4,28 @@ using PepperDash.Essentials.Core;
namespace PepperDash.Essentials.Room.MobileControl
{
/// <summary>
/// Messenger that provides mobile control interface for devices with numeric keypad functionality
/// </summary>
public class INumericKeypadMessenger : MessengerBase
{
/// <summary>
/// The numeric keypad device this messenger is associated with
/// </summary>
private readonly INumericKeypad keypadDevice;
/// <summary>
/// Initializes a new instance of the INumericKeypadMessenger class
/// </summary>
/// <param name="key">The unique key for this messenger</param>
/// <param name="messagePath">The message path for routing numeric keypad messages</param>
/// <param name="device">The device that implements numeric keypad functionality</param>
public INumericKeypadMessenger(string key, string messagePath, INumericKeypad device) : base(key, messagePath, device as IKeyName)
{
keypadDevice = device;
}
/// <inheritdoc />
protected override void RegisterActions()
{
base.RegisterActions();

View File

@@ -4,14 +4,25 @@ using PepperDash.Essentials.Core;
namespace PepperDash.Essentials.Room.MobileControl
{
/// <summary>
/// Messenger for devices that implement ISetTopBoxControls interface
/// </summary>
public class ISetTopBoxControlsMessenger : MessengerBase
{
private readonly ISetTopBoxControls stbDevice;
/// <summary>
/// Initializes a new instance of the ISetTopBoxControlsMessenger class
/// </summary>
/// <param name="key">Unique identifier for the messenger</param>
/// <param name="messagePath">Path for message routing</param>
/// <param name="device">Device that implements ISetTopBoxControls</param>
public ISetTopBoxControlsMessenger(string key, string messagePath, ISetTopBoxControls device) : base(key, messagePath, device as IKeyName)
{
stbDevice = device;
}
/// <inheritdoc />
protected override void RegisterActions()
{
base.RegisterActions();
@@ -32,6 +43,9 @@ namespace PepperDash.Essentials.Room.MobileControl
}
}
/// <summary>
/// State message for set top box controls
/// </summary>
public class SetTopBoxControlsState : DeviceStateMessageBase
{

View File

@@ -4,14 +4,28 @@ using PepperDash.Essentials.Core;
namespace PepperDash.Essentials.Room.MobileControl
{
/// <summary>
/// Messenger that provides mobile control interface for devices with transport control functionality
/// </summary>
public class ITransportMessenger : MessengerBase
{
/// <summary>
/// The transport control device this messenger is associated with
/// </summary>
private readonly ITransport transportDevice;
/// <summary>
/// Initializes a new instance of the ITransportMessenger class
/// </summary>
/// <param name="key">The unique key for this messenger</param>
/// <param name="messagePath">The message path for routing transport control messages</param>
/// <param name="device">The device that implements transport control functionality</param>
public ITransportMessenger(string key, string messagePath, ITransport device) : base(key, messagePath, device as IKeyName)
{
transportDevice = device;
}
/// <inheritdoc />
protected override void RegisterActions()
{
base.RegisterActions();

View File

@@ -1,8 +1,8 @@
using Newtonsoft.Json.Linq;
using System;
using System.Linq;
using Newtonsoft.Json.Linq;
using PepperDash.Essentials.Devices.Common.AudioCodec;
using PepperDash.Essentials.Devices.Common.Codec;
using System;
using System.Linq;
namespace PepperDash.Essentials.AppServer.Messengers
{
@@ -29,12 +29,16 @@ namespace PepperDash.Essentials.AppServer.Messengers
codec.CallStatusChange += Codec_CallStatusChange;
}
/// <summary>
/// Registers actions for handling audio codec operations.
/// Includes call control, status reporting, and audio codec management.
/// </summary>
protected override void RegisterActions()
{
base.RegisterActions();
AddAction("/fullStatus", (id, content) => SendAtcFullMessageObject());
AddAction("/fullStatus", (id, content) => SendAtcFullMessageObject(id));
AddAction("/dial", (id, content) =>
{
var msg = content.ToObject<MobileControlSimpleContent<string>>();
@@ -97,7 +101,7 @@ namespace PepperDash.Essentials.AppServer.Messengers
/// Helper method to build call status for vtc
/// </summary>
/// <returns></returns>
private void SendAtcFullMessageObject()
private void SendAtcFullMessageObject(string id = null)
{
var info = Codec.CodecInfo;
@@ -109,7 +113,7 @@ namespace PepperDash.Essentials.AppServer.Messengers
{
phoneNumber = info.PhoneNumber
}
})
}), clientId: id
);
}
}

View File

@@ -1,11 +1,15 @@
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using Newtonsoft.Json.Linq;
using PepperDash.Essentials.Core;
using PepperDash.Essentials.Devices.Common.Cameras;
using System;
using System.Collections.Generic;
namespace PepperDash.Essentials.AppServer.Messengers
{
/// <summary>
/// Provides messaging capabilities for camera control operations.
/// Handles camera movement, zoom, preset management, and camera status reporting.
/// </summary>
public class CameraBaseMessenger : MessengerBase
{
/// <summary>
@@ -45,11 +49,15 @@ namespace PepperDash.Essentials.AppServer.Messengers
);
}
/// <summary>
/// Registers actions for handling camera control operations.
/// Includes camera movement, zoom, preset management, and full status reporting.
/// </summary>
protected override void RegisterActions()
{
base.RegisterActions();
AddAction("/fullStatus", (id, content) => SendCameraFullMessageObject());
AddAction("/fullStatus", (id, content) => SendCameraFullMessageObject(id));
if (Camera is IHasCameraPtzControl ptzCamera)
@@ -166,7 +174,7 @@ namespace PepperDash.Essentials.AppServer.Messengers
/// <summary>
/// Helper method to update the full status of the camera
/// </summary>
private void SendCameraFullMessageObject()
private void SendCameraFullMessageObject(string id = null)
{
var presetList = new List<CameraPreset>();
@@ -181,7 +189,7 @@ namespace PepperDash.Essentials.AppServer.Messengers
cameraMode = GetCameraMode(),
hasPresets = Camera is IHasCameraPresets,
presets = presetList
})
}), clientId: id
);
}

View File

@@ -1,41 +1,93 @@
using Newtonsoft.Json;
using System.Timers;
using Independentsoft.Exchange;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using PepperDash.Core;
using PepperDash.Essentials.Core.DeviceInfo;
namespace PepperDash.Essentials.AppServer.Messengers
{
/// <summary>
/// Facilitates communication of device information by providing mechanisms for status updates and device
/// information reporting.
/// </summary>
/// <remarks>The <see cref="DeviceInfoMessenger"/> class integrates with an <see
/// cref="IDeviceInfoProvider"/> 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.</remarks>
public class DeviceInfoMessenger : MessengerBase
{
private readonly IDeviceInfoProvider _deviceInfoProvider;
private readonly Timer debounceTimer;
/// <summary>
/// Initializes a new instance of the <see cref="DeviceInfoMessenger"/> class, which facilitates communication
/// of device information.
/// </summary>
/// <remarks>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.</remarks>
/// <param name="key">A unique identifier for the messenger instance.</param>
/// <param name="messagePath">The path used for sending and receiving messages.</param>
/// <param name="device">An implementation of <see cref="IDeviceInfoProvider"/> that provides device-specific information.</param>
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
}));
}
/// <summary>
/// Registers actions and event handlers for device information updates and status reporting.
/// </summary>
/// <remarks>This method sets up actions for handling device status updates and reporting full
/// device status. It also subscribes to the <see cref="IDeviceInfoProvider.DeviceInfoChanged"/> event to
/// trigger debounced updates when the device information changes.</remarks>
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
{
DeviceInfo = _deviceInfoProvider.DeviceInfo
}));
}, id));
AddAction("/update", (id, context) => _deviceInfoProvider.UpdateDeviceInfo());
}
}
/// <summary>
/// Represents a message containing the state information of a device, including detailed device information.
/// </summary>
/// <remarks>This class is used to encapsulate the state of a device along with its associated
/// information. It extends <see cref="DeviceStateMessageBase"/> to provide additional details about the
/// device.</remarks>
public class DeviceInfoStateMessage : DeviceStateMessageBase
{
/// <summary>
/// Gets or sets the device information.
/// </summary>
[JsonProperty("deviceInfo")]
public DeviceInfo DeviceInfo { get; set; }
}

View File

@@ -1,30 +1,40 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using PepperDash.Core;
using PepperDash.Core.Logging;
using PepperDash.Essentials.Core;
using PepperDash.Essentials.Core.DeviceTypeInterfaces;
using PepperDash.Essentials.Core.Presets;
using System;
using System.Collections.Generic;
namespace PepperDash.Essentials.AppServer.Messengers
{
/// <summary>
/// Provides messaging capabilities for device preset management operations.
/// Handles preset selection, recall, and preset list management.
/// </summary>
public class DevicePresetsModelMessenger : MessengerBase
{
private readonly ITvPresetsProvider _presetsDevice;
/// <summary>
/// Initializes a new instance of the <see cref="DevicePresetsModelMessenger"/> class.
/// </summary>
/// <param name="key">The unique identifier for this messenger instance.</param>
/// <param name="messagePath">The message path for preset control messages.</param>
/// <param name="presetsDevice">The device that provides preset functionality.</param>
public DevicePresetsModelMessenger(string key, string messagePath, ITvPresetsProvider presetsDevice)
: base(key, messagePath, presetsDevice as Device)
{
_presetsDevice = presetsDevice;
}
private void SendPresets()
private void SendPresets(string id = null)
{
PostStatusMessage(new PresetStateMessage
{
Favorites = _presetsDevice.TvPresets.PresetsList
});
}, id);
}
private void RecallPreset(ISetTopBoxNumericKeypad device, string channel)
@@ -40,6 +50,10 @@ namespace PepperDash.Essentials.AppServer.Messengers
#region Overrides of MessengerBase
/// <summary>
/// Registers actions for handling device preset operations.
/// Includes preset selection, recall, and full status reporting.
/// </summary>
protected override void RegisterActions()
{
@@ -48,7 +62,7 @@ namespace PepperDash.Essentials.AppServer.Messengers
this.LogInformation("getting full status for client {id}", id);
try
{
SendPresets();
SendPresets(id);
}
catch (Exception ex)
{
@@ -83,17 +97,32 @@ namespace PepperDash.Essentials.AppServer.Messengers
#endregion
}
/// <summary>
/// Represents a preset channel message for device preset operations.
/// </summary>
public class PresetChannelMessage
{
/// <summary>
/// Gets or sets the preset channel information.
/// </summary>
[JsonProperty("preset")]
public PresetChannel Preset;
/// <summary>
/// Gets or sets the device key associated with the preset.
/// </summary>
[JsonProperty("deviceKey")]
public string DeviceKey;
}
/// <summary>
/// Represents a preset state message containing favorite presets.
/// </summary>
public class PresetStateMessage : DeviceStateMessageBase
{
/// <summary>
/// Gets or sets the list of favorite preset channels.
/// </summary>
[JsonProperty("favorites", NullValueHandling = NullValueHandling.Ignore)]
public List<PresetChannel> Favorites { get; set; } = new List<PresetChannel>();
}

View File

@@ -1,23 +1,33 @@
using Newtonsoft.Json;
using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Linq;
using PepperDash.Core;
using PepperDash.Essentials.Core;
using System;
namespace PepperDash.Essentials.AppServer.Messengers
{
/// <summary>
/// Provides messaging capabilities for device volume control operations.
/// Handles volume level adjustment, mute control, and volume status reporting.
/// </summary>
public class DeviceVolumeMessenger : MessengerBase
{
private readonly IBasicVolumeWithFeedback _localDevice;
/// <summary>
/// Initializes a new instance of the <see cref="DeviceVolumeMessenger"/> class.
/// </summary>
/// <param name="key">The unique identifier for this messenger instance.</param>
/// <param name="messagePath">The message path for volume control messages.</param>
/// <param name="device">The device that provides volume control functionality.</param>
public DeviceVolumeMessenger(string key, string messagePath, IBasicVolumeWithFeedback device)
: base(key, messagePath, device as IKeyName)
{
_localDevice = device;
}
private void SendStatus()
private void SendStatus(string id = null)
{
try
{
@@ -37,7 +47,7 @@ namespace PepperDash.Essentials.AppServer.Messengers
messageObj.Volume.Units = volumeAdvanced.Units;
}
PostStatusMessage(messageObj);
PostStatusMessage(messageObj, id);
}
catch (Exception ex)
{
@@ -48,9 +58,13 @@ namespace PepperDash.Essentials.AppServer.Messengers
#region Overrides of MessengerBase
/// <summary>
/// Registers actions for handling volume control operations.
/// Includes volume level adjustment, mute control, and full status reporting.
/// </summary>
protected override void RegisterActions()
{
AddAction("/fullStatus", (id, content) => SendStatus());
AddAction("/fullStatus", (id, content) => SendStatus(id));
AddAction("/level", (id, content) =>
{
@@ -142,29 +156,56 @@ namespace PepperDash.Essentials.AppServer.Messengers
#endregion
}
/// <summary>
/// Represents a volume state message containing volume information.
/// </summary>
public class VolumeStateMessage : DeviceStateMessageBase
{
/// <summary>
/// Gets or sets the volume information.
/// </summary>
[JsonProperty("volume", NullValueHandling = NullValueHandling.Ignore)]
public Volume Volume { get; set; }
}
/// <summary>
/// Represents volume control information including level, mute status, and units.
/// </summary>
public class Volume
{
/// <summary>
/// Gets or sets the volume level.
/// </summary>
[JsonProperty("level", NullValueHandling = NullValueHandling.Ignore)]
public int? Level { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the device has mute capability.
/// </summary>
[JsonProperty("hasMute", NullValueHandling = NullValueHandling.Ignore)]
public bool? HasMute { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the device is currently muted.
/// </summary>
[JsonProperty("muted", NullValueHandling = NullValueHandling.Ignore)]
public bool? Muted { get; set; }
/// <summary>
/// Gets or sets the volume label for display purposes.
/// </summary>
[JsonProperty("label", NullValueHandling = NullValueHandling.Ignore)]
public string Label { get; set; }
/// <summary>
/// Gets or sets the raw volume value as a string.
/// </summary>
[JsonProperty("rawValue", NullValueHandling = NullValueHandling.Ignore)]
public string RawValue { get; set; }
/// <summary>
/// Gets or sets the volume level units.
/// </summary>
[JsonConverter(typeof(StringEnumConverter))]
[JsonProperty("units", NullValueHandling = NullValueHandling.Ignore)]
public eVolumeLevelUnits? Units { get; set; }

View File

@@ -2,24 +2,34 @@
namespace PepperDash.Essentials.AppServer.Messengers
{
/// <summary>
/// Generic messenger for basic device communication
/// </summary>
public class GenericMessenger : MessengerBase
{
/// <summary>
/// Initializes a new instance of the GenericMessenger class
/// </summary>
/// <param name="key">Unique identifier for the messenger</param>
/// <param name="device">Device to communicate with</param>
/// <param name="messagePath">Path for message routing</param>
public GenericMessenger(string key, EssentialsDevice device, string messagePath) : base(key, messagePath, device)
{
}
/// <inheritdoc />
protected override void RegisterActions()
{
base.RegisterActions();
AddAction("/fullStatus", (id, content) => SendFullStatus());
AddAction("/fullStatus", (id, content) => SendFullStatus(id));
}
private void SendFullStatus()
private void SendFullStatus(string id = null)
{
var state = new DeviceStateMessageBase();
PostStatusMessage(state);
PostStatusMessage(state, id);
}
}
}

View File

@@ -6,15 +6,29 @@ using PepperDash.Essentials.Core;
namespace PepperDash.Essentials.AppServer.Messengers
{
/// <summary>
/// Provides messaging capabilities for communication monitoring operations.
/// Handles communication status reporting and monitoring feedback.
/// </summary>
public class ICommunicationMonitorMessenger : MessengerBase
{
private readonly ICommunicationMonitor _communicationMonitor;
/// <summary>
/// Initializes a new instance of the <see cref="ICommunicationMonitorMessenger"/> class.
/// </summary>
/// <param name="key">The unique identifier for this messenger instance.</param>
/// <param name="messagePath">The message path for communication monitor messages.</param>
/// <param name="device">The device that provides communication monitoring functionality.</param>
public ICommunicationMonitorMessenger(string key, string messagePath, ICommunicationMonitor device) : base(key, messagePath, device as IKeyName)
{
_communicationMonitor = device;
}
/// <summary>
/// Registers actions for handling communication monitoring operations.
/// Includes full status reporting for communication monitoring data.
/// </summary>
protected override void RegisterActions()
{
base.RegisterActions();
@@ -28,7 +42,7 @@ namespace PepperDash.Essentials.AppServer.Messengers
IsOnline = _communicationMonitor.CommunicationMonitor.IsOnline,
Status = _communicationMonitor.CommunicationMonitor.Status
}
});
}, id);
});
_communicationMonitor.CommunicationMonitor.StatusChange += (sender, args) =>
@@ -50,11 +64,17 @@ namespace PepperDash.Essentials.AppServer.Messengers
/// </summary>
public class CommunicationMonitorState : DeviceStateMessageBase
{
/// <summary>
/// Gets or sets the communication monitor properties.
/// </summary>
[JsonProperty("commMonitor", NullValueHandling = NullValueHandling.Ignore)]
public CommunicationMonitorProps CommunicationMonitor { get; set; }
}
/// <summary>
/// Represents the properties of a communication monitor.
/// </summary>
public class CommunicationMonitorProps
{ /// <summary>
/// For devices that implement ICommunicationMonitor, reports the online status of the device

View File

@@ -1,20 +1,34 @@
using Newtonsoft.Json;
using System.Collections.Generic;
using Newtonsoft.Json;
using PepperDash.Core;
using PepperDash.Essentials.Core;
using System.Collections.Generic;
namespace PepperDash.Essentials.AppServer.Messengers
{
/// <summary>
/// Provides messaging capabilities for DSP preset management operations.
/// Handles DSP preset selection, recall, and preset list management.
/// </summary>
public class IDspPresetsMessenger : MessengerBase
{
private readonly IDspPresets device;
/// <summary>
/// Initializes a new instance of the <see cref="IDspPresetsMessenger"/> class.
/// </summary>
/// <param name="key">The unique identifier for this messenger instance.</param>
/// <param name="messagePath">The message path for DSP preset control messages.</param>
/// <param name="device">The device that provides DSP preset functionality.</param>
public IDspPresetsMessenger(string key, string messagePath, IDspPresets device)
: base(key, messagePath, device as IKeyName)
{
this.device = device;
}
/// <summary>
/// Registers actions for handling DSP preset operations.
/// Includes preset selection, recall, and full status reporting.
/// </summary>
protected override void RegisterActions()
{
base.RegisterActions();
@@ -26,7 +40,7 @@ namespace PepperDash.Essentials.AppServer.Messengers
Presets = device.Presets
};
PostStatusMessage(message);
PostStatusMessage(message, id);
});
AddAction("/recallPreset", (id, content) =>
@@ -42,8 +56,14 @@ namespace PepperDash.Essentials.AppServer.Messengers
}
}
/// <summary>
/// Represents a DSP presets state message containing available presets.
/// </summary>
public class IHasDspPresetsStateMessage : DeviceStateMessageBase
{
/// <summary>
/// Gets or sets the dictionary of available DSP presets.
/// </summary>
[JsonProperty("presets")]
public Dictionary<string, IKeyName> Presets { get; set; }
}

View File

@@ -1,26 +1,52 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using PepperDash.Core;
using PepperDash.Core.Logging;
using PepperDash.Essentials.Core;
using System;
using System.Collections.Generic;
namespace PepperDash.Essentials.AppServer.Messengers
{
/// <summary>
/// Provides messaging functionality for managing room combination scenarios and partition states in an <see
/// cref="IEssentialsRoomCombiner"/> instance. Enables external systems to interact with the room combiner via
/// predefined actions and status updates.
/// </summary>
/// <remarks>This class facilitates communication with an <see cref="IEssentialsRoomCombiner"/> 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.</remarks>
public class IEssentialsRoomCombinerMessenger : MessengerBase
{
private readonly IEssentialsRoomCombiner _roomCombiner;
/// <summary>
/// Initializes a new instance of the <see cref="IEssentialsRoomCombinerMessenger"/> class, which facilitates
/// messaging for an <see cref="IEssentialsRoomCombiner"/> instance.
/// </summary>
/// <remarks>This class is designed to enable communication and interaction with an <see
/// cref="IEssentialsRoomCombiner"/> through the specified messaging path. Ensure that the <paramref
/// name="roomCombiner"/> parameter is not null when creating an instance.</remarks>
/// <param name="key">The unique key identifying this messenger instance.</param>
/// <param name="messagePath">The path used for messaging operations.</param>
/// <param name="roomCombiner">The <see cref="IEssentialsRoomCombiner"/> instance associated with this messenger.</param>
public IEssentialsRoomCombinerMessenger(string key, string messagePath, IEssentialsRoomCombiner roomCombiner)
: base(key, messagePath, roomCombiner as IKeyName)
{
_roomCombiner = roomCombiner;
}
/// <summary>
/// Registers actions and event handlers for managing room combination scenarios and partition states.
/// </summary>
/// <remarks>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.</remarks>
protected override void RegisterActions()
{
AddAction("/fullStatus", (id, content) => SendFullStatus());
AddAction("/fullStatus", (id, content) => SendFullStatus(id));
AddAction("/setAutoMode", (id, content) =>
{
@@ -94,7 +120,7 @@ namespace PepperDash.Essentials.AppServer.Messengers
}
}
private void SendFullStatus()
private void SendFullStatus(string id = null)
{
try
{
@@ -107,6 +133,7 @@ namespace PepperDash.Essentials.AppServer.Messengers
var message = new IEssentialsRoomCombinerStateMessage
{
DisableAutoMode = _roomCombiner.DisableAutoMode,
IsInAutoMode = _roomCombiner.IsInAutoMode,
CurrentScenario = _roomCombiner.CurrentScenario,
Rooms = rooms,
@@ -114,7 +141,7 @@ namespace PepperDash.Essentials.AppServer.Messengers
Partitions = _roomCombiner.Partitions
};
PostStatusMessage(message);
PostStatusMessage(message, id);
}
catch (Exception e)
{
@@ -132,20 +159,48 @@ namespace PepperDash.Essentials.AppServer.Messengers
}
}
/// <summary>
/// Represents the state message for a room combiner system, providing information about the current configuration,
/// operational mode, and associated rooms, partitions, and scenarios.
/// </summary>
/// <remarks>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.</remarks>
public class IEssentialsRoomCombinerStateMessage : DeviceStateMessageBase
{
/// <summary>
/// Gets or sets a value indicating whether automatic mode is disabled.
/// </summary>
[JsonProperty("disableAutoMode", NullValueHandling = NullValueHandling.Ignore)]
public bool DisableAutoMode { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the system is operating in automatic mode.
/// </summary>
[JsonProperty("isInAutoMode", NullValueHandling = NullValueHandling.Ignore)]
public bool IsInAutoMode { get; set; }
/// <summary>
/// Gets or sets the current room combination scenario.
/// </summary>
[JsonProperty("currentScenario", NullValueHandling = NullValueHandling.Ignore)]
public IRoomCombinationScenario CurrentScenario { get; set; }
/// <summary>
/// Gets or sets the collection of rooms associated with the entity.
/// </summary>
[JsonProperty("rooms", NullValueHandling = NullValueHandling.Ignore)]
public List<IKeyName> Rooms { get; set; }
/// <summary>
/// Gets or sets the collection of room combination scenarios.
/// </summary>
[JsonProperty("roomCombinationScenarios", NullValueHandling = NullValueHandling.Ignore)]
public List<IRoomCombinationScenario> RoomCombinationScenarios { get; set; }
/// <summary>
/// Gets or sets the collection of partition controllers.
/// </summary>
[JsonProperty("partitions", NullValueHandling = NullValueHandling.Ignore)]
public List<IPartitionController> Partitions { get; set; }
}

View File

@@ -5,14 +5,25 @@ using PepperDash.Essentials.Core;
namespace PepperDash.Essentials.AppServer.Messengers
{
/// <summary>
/// Messenger for devices that implement IHasCurrentSourceInfoChange interface
/// </summary>
public class IHasCurrentSourceInfoMessenger : MessengerBase
{
private readonly IHasCurrentSourceInfoChange sourceDevice;
/// <summary>
/// Initializes a new instance of the IHasCurrentSourceInfoMessenger class
/// </summary>
/// <param name="key">Unique identifier for the messenger</param>
/// <param name="messagePath">Path for message routing</param>
/// <param name="device">Device that implements IHasCurrentSourceInfoChange</param>
public IHasCurrentSourceInfoMessenger(string key, string messagePath, IHasCurrentSourceInfoChange device) : base(key, messagePath, device as IKeyName)
{
sourceDevice = device;
}
/// <inheritdoc />
protected override void RegisterActions()
{
base.RegisterActions();
@@ -25,7 +36,7 @@ namespace PepperDash.Essentials.AppServer.Messengers
CurrentSource = sourceDevice.CurrentSourceInfo
};
PostStatusMessage(message);
PostStatusMessage(message, id);
});
sourceDevice.CurrentSourceChange += (sender, e) =>
@@ -46,11 +57,20 @@ namespace PepperDash.Essentials.AppServer.Messengers
}
}
/// <summary>
/// State message for current source information
/// </summary>
public class CurrentSourceStateMessage : DeviceStateMessageBase
{
/// <summary>
/// Gets or sets the current source key
/// </summary>
[JsonProperty("currentSourceKey", NullValueHandling = NullValueHandling.Ignore)]
public string CurrentSourceKey { get; set; }
/// <summary>
/// Gets or sets the current source information
/// </summary>
[JsonProperty("currentSource")]
public SourceListItem CurrentSource { get; set; }
}

View File

@@ -1,35 +1,40 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using PepperDash.Core;
using PepperDash.Core.Logging;
using PepperDash.Essentials.Core.DeviceTypeInterfaces;
using System;
using System.Collections.Generic;
namespace PepperDash.Essentials.AppServer.Messengers
{
/// <summary>
/// Messenger for devices that implement IHasInputs interface
/// </summary>
/// <typeparam name="TKey">Type of the key used for inputs</typeparam>
public class IHasInputsMessenger<TKey> : MessengerBase
{
private readonly IHasInputs<TKey> itemDevice;
{
private readonly IHasInputs<TKey> itemDevice;
/// <summary>
/// Constructs a messenger for a device that implements IHasInputs<typeparamref name="TKey"/>
/// </summary>
/// <param name="key"></param>
/// <param name="messagePath"></param>
/// <param name="device"></param>
/// <param name="key">Unique identifier for the messenger</param>
/// <param name="messagePath">Path for message routing</param>
/// <param name="device">Device that implements IHasInputs</param>
public IHasInputsMessenger(string key, string messagePath, IHasInputs<TKey> device) : base(key, messagePath, device)
{
itemDevice = device;
itemDevice = device;
}
/// <inheritdoc />
protected override void RegisterActions()
{
base.RegisterActions();
AddAction("/fullStatus", (id, context) =>
{
SendFullStatus();
SendFullStatus(id);
});
itemDevice.Inputs.ItemsUpdated += (sender, args) =>
@@ -59,7 +64,7 @@ namespace PepperDash.Essentials.AppServer.Messengers
}
}
private void SendFullStatus()
private void SendFullStatus(string id = null)
{
try
{
@@ -74,7 +79,7 @@ namespace PepperDash.Essentials.AppServer.Messengers
}
};
PostStatusMessage(stateObject);
PostStatusMessage(stateObject, id);
}
catch (Exception e)
{
@@ -83,17 +88,34 @@ namespace PepperDash.Essentials.AppServer.Messengers
}
}
/// <summary>
/// State message for devices with inputs
/// </summary>
/// <typeparam name="TKey">Type of the key used for inputs</typeparam>
public class IHasInputsStateMessage<TKey> : DeviceStateMessageBase
{
/// <summary>
/// Gets or sets the inputs
/// </summary>
[JsonProperty("inputs")]
public Inputs<TKey> Inputs { get; set; }
}
/// <summary>
/// Represents a collection of inputs
/// </summary>
/// <typeparam name="TKey">Type of the key used for inputs</typeparam>
public class Inputs<TKey>
{
/// <summary>
/// Gets or sets the items dictionary
/// </summary>
[JsonProperty("items")]
public Dictionary<TKey, ISelectableItem> Items { get; set; }
/// <summary>
/// Gets or sets the current item
/// </summary>
[JsonProperty("currentItem")]
public TKey CurrentItem { get; set; }
}

View File

@@ -5,31 +5,48 @@ using PepperDash.Essentials.Core;
namespace PepperDash.Essentials.AppServer.Messengers
{
/// <summary>
/// Provides messaging capabilities for power control operations with feedback.
/// Handles power on/off commands and power state feedback reporting.
/// </summary>
public class IHasPowerControlWithFeedbackMessenger : MessengerBase
{
private readonly IHasPowerControlWithFeedback _powerControl;
/// <summary>
/// Initializes a new instance of the <see cref="IHasPowerControlWithFeedbackMessenger"/> class.
/// </summary>
/// <param name="key">The unique identifier for this messenger instance.</param>
/// <param name="messagePath">The message path for power control messages.</param>
/// <param name="powerControl">The device that provides power control functionality.</param>
public IHasPowerControlWithFeedbackMessenger(string key, string messagePath, IHasPowerControlWithFeedback powerControl)
: base(key, messagePath, powerControl as IKeyName)
{
_powerControl = powerControl;
}
public void SendFullStatus()
/// <summary>
/// Sends the full power control status to connected clients.
/// </summary>
public void SendFullStatus(string id = null)
{
var messageObj = new PowerControlWithFeedbackStateMessage
{
PowerState = _powerControl.PowerIsOnFeedback.BoolValue
};
PostStatusMessage(messageObj);
PostStatusMessage(messageObj, id);
}
/// <summary>
/// Registers actions for handling power control operations.
/// Includes power on, power off, power toggle, and full status reporting.
/// </summary>
protected override void RegisterActions()
{
base.RegisterActions();
AddAction("/fullStatus", (id, content) => SendFullStatus());
AddAction("/fullStatus", (id, content) => SendFullStatus(id));
_powerControl.PowerIsOnFeedback.OutputChange += PowerIsOnFeedback_OutputChange; ;
}
@@ -44,8 +61,14 @@ namespace PepperDash.Essentials.AppServer.Messengers
}
}
/// <summary>
/// Represents a power control state message containing power state information.
/// </summary>
public class PowerControlWithFeedbackStateMessage : DeviceStateMessageBase
{
/// <summary>
/// Gets or sets the power state of the device.
/// </summary>
[JsonProperty("powerState", NullValueHandling = NullValueHandling.Ignore)]
public bool? PowerState { get; set; }
}

View File

@@ -1,16 +1,28 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using PepperDash.Core;
using PepperDash.Essentials.Devices.Common.Codec;
using System;
using System.Collections.Generic;
namespace PepperDash.Essentials.AppServer.Messengers
{
/// <summary>
/// Messenger for devices that implement IHasScheduleAwareness interface
/// </summary>
public class IHasScheduleAwarenessMessenger : MessengerBase
{
/// <summary>
/// Gets the schedule source device
/// </summary>
public IHasScheduleAwareness ScheduleSource { get; private set; }
/// <summary>
/// Initializes a new instance of the IHasScheduleAwarenessMessenger class
/// </summary>
/// <param name="key">Unique identifier for the messenger</param>
/// <param name="scheduleSource">Device that implements IHasScheduleAwareness</param>
/// <param name="messagePath">Path for message routing</param>
public IHasScheduleAwarenessMessenger(string key, IHasScheduleAwareness scheduleSource, string messagePath)
: base(key, messagePath, scheduleSource as IKeyName)
{
@@ -19,9 +31,10 @@ namespace PepperDash.Essentials.AppServer.Messengers
ScheduleSource.CodecSchedule.MeetingEventChange += new EventHandler<MeetingEventArgs>(CodecSchedule_MeetingEventChange);
}
/// <inheritdoc />
protected override void RegisterActions()
{
AddAction("/schedule/fullStatus", (id, content) => SendFullScheduleObject());
AddAction("/schedule/fullStatus", (id, content) => SendFullScheduleObject(id));
}
private void CodecSchedule_MeetingEventChange(object sender, MeetingEventArgs e)
@@ -45,36 +58,60 @@ namespace PepperDash.Essentials.AppServer.Messengers
/// <summary>
/// Helper method to send the full schedule data
/// </summary>
private void SendFullScheduleObject()
private void SendFullScheduleObject(string id = null)
{
PostStatusMessage(new FullScheduleMessage
{
Meetings = ScheduleSource.CodecSchedule.Meetings,
MeetingWarningMinutes = ScheduleSource.CodecSchedule.MeetingWarningMinutes
});
}, id);
}
}
/// <summary>
/// Full schedule message containing meetings and warning minutes
/// </summary>
public class FullScheduleMessage : DeviceStateMessageBase
{
/// <summary>
/// Gets or sets the list of meetings
/// </summary>
[JsonProperty("meetings", NullValueHandling = NullValueHandling.Ignore)]
public List<Meeting> Meetings { get; set; }
/// <summary>
/// Gets or sets the meeting warning minutes
/// </summary>
[JsonProperty("meetingWarningMinutes", NullValueHandling = NullValueHandling.Ignore)]
public int MeetingWarningMinutes { get; set; }
}
/// <summary>
/// Message containing meeting change information
/// </summary>
public class MeetingChangeMessage
{
/// <summary>
/// Gets or sets the meeting change details
/// </summary>
[JsonProperty("meetingChange", NullValueHandling = NullValueHandling.Ignore)]
public MeetingChange MeetingChange { get; set; }
}
/// <summary>
/// Represents a meeting change with type and meeting details
/// </summary>
public class MeetingChange
{
/// <summary>
/// Gets or sets the change type
/// </summary>
[JsonProperty("changeType", NullValueHandling = NullValueHandling.Ignore)]
public string ChangeType { get; set; }
/// <summary>
/// Gets or sets the meeting details
/// </summary>
[JsonProperty("meeting", NullValueHandling = NullValueHandling.Ignore)]
public Meeting Meeting { get; set; }
}

View File

@@ -1,7 +1,7 @@
using Newtonsoft.Json;
using System;
using Newtonsoft.Json;
using PepperDash.Core;
using PepperDash.Essentials.Core.DeviceTypeInterfaces;
using System;
namespace PepperDash.Essentials.AppServer.Messengers
{
@@ -19,19 +19,19 @@ namespace PepperDash.Essentials.AppServer.Messengers
{
base.RegisterActions();
AddAction("/fullStatus", (id, content) => SendFullStatus());
AddAction("/fullStatus", (id, content) => SendFullStatus(id));
device.HumidityFeedback.OutputChange += new EventHandler<Core.FeedbackEventArgs>((o, a) => SendFullStatus());
}
private void SendFullStatus()
private void SendFullStatus(string id = null)
{
var state = new IHumiditySensorStateMessage
{
Humidity = string.Format("{0}%", device.HumidityFeedback.UShortValue)
};
PostStatusMessage(state);
PostStatusMessage(state, id);
}
}

View File

@@ -1,20 +1,31 @@
using Newtonsoft.Json;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using PepperDash.Core;
using PepperDash.Essentials.Core.DeviceTypeInterfaces;
using System.Collections.Generic;
using System.Linq;
namespace PepperDash.Essentials.AppServer.Messengers
{
/// <summary>
/// Messenger for devices that implement ILevelControls interface
/// </summary>
public class ILevelControlsMessenger : MessengerBase
{
private ILevelControls levelControlsDevice;
/// <summary>
/// Initializes a new instance of the ILevelControlsMessenger class
/// </summary>
/// <param name="key">Unique identifier for the messenger</param>
/// <param name="messagePath">Path for message routing</param>
/// <param name="device">Device that implements ILevelControls</param>
public ILevelControlsMessenger(string key, string messagePath, ILevelControls device) : base(key, messagePath, device as IKeyName)
{
levelControlsDevice = device;
}
/// <inheritdoc />
protected override void RegisterActions()
{
base.RegisterActions();
@@ -26,7 +37,7 @@ namespace PepperDash.Essentials.AppServer.Messengers
Levels = levelControlsDevice.LevelControlPoints.ToDictionary(kv => kv.Key, kv => new Volume { Level = kv.Value.VolumeLevelFeedback.IntValue, Muted = kv.Value.MuteFeedback.BoolValue })
};
PostStatusMessage(message);
PostStatusMessage(message, id);
});
foreach (var levelControl in levelControlsDevice.LevelControlPoints)
@@ -74,17 +85,32 @@ namespace PepperDash.Essentials.AppServer.Messengers
}
}
/// <summary>
/// State message for level controls
/// </summary>
public class LevelControlStateMessage : DeviceStateMessageBase
{
/// <summary>
/// Gets or sets the level controls
/// </summary>
[JsonProperty("levelControls")]
public Dictionary<string, Volume> Levels { get; set; }
}
/// <summary>
/// Request message for level control operations
/// </summary>
public class LevelControlRequestMessage
{
/// <summary>
/// Gets or sets the control key
/// </summary>
[JsonProperty("key")]
public string Key { get; set; }
/// <summary>
/// Gets or sets the level
/// </summary>
[JsonProperty("level", NullValueHandling = NullValueHandling.Ignore)]
public ushort? Level { get; set; }
}

View File

@@ -1,12 +1,12 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using PepperDash.Core;
using PepperDash.Essentials.Core;
using PepperDash.Essentials.Core.Routing;
using Serilog.Events;
using System;
using System.Collections.Generic;
using System.Linq;
namespace PepperDash.Essentials.AppServer.Messengers
{
@@ -16,11 +16,19 @@ namespace PepperDash.Essentials.AppServer.Messengers
public class IMatrixRoutingMessenger : MessengerBase
{
private readonly IMatrixRouting matrixDevice;
/// <summary>
/// Initializes a new instance of the IMatrixRoutingMessenger class
/// </summary>
/// <param name="key">Unique identifier for the messenger</param>
/// <param name="messagePath">Path for message routing</param>
/// <param name="device">Device that implements IMatrixRouting</param>
public IMatrixRoutingMessenger(string key, string messagePath, IMatrixRouting device) : base(key, messagePath, device as IKeyName)
{
matrixDevice = device;
}
/// <inheritdoc />
protected override void RegisterActions()
{
base.RegisterActions();
@@ -37,7 +45,7 @@ namespace PepperDash.Essentials.AppServer.Messengers
};
PostStatusMessage(message);
PostStatusMessage(message, id);
}
catch (Exception e)
{
@@ -82,86 +90,159 @@ namespace PepperDash.Essentials.AppServer.Messengers
}
}
/// <summary>
/// State message for matrix routing information
/// </summary>
public class MatrixStateMessage : DeviceStateMessageBase
{
/// <summary>
/// Gets or sets the outputs dictionary
/// </summary>
[JsonProperty("outputs")]
public Dictionary<string, RoutingOutput> Outputs;
/// <summary>
/// Gets or sets the inputs dictionary
/// </summary>
[JsonProperty("inputs")]
public Dictionary<string, RoutingInput> Inputs;
}
/// <summary>
/// Represents a routing input slot
/// </summary>
public class RoutingInput
{
private IRoutingInputSlot _input;
/// <summary>
/// Gets the transmit device key
/// </summary>
[JsonProperty("txDeviceKey", NullValueHandling = NullValueHandling.Ignore)]
public string TxDeviceKey => _input?.TxDeviceKey;
/// <summary>
/// Gets the slot number
/// </summary>
[JsonProperty("slotNumber", NullValueHandling = NullValueHandling.Ignore)]
public int? SlotNumber => _input?.SlotNumber;
/// <summary>
/// Gets the supported signal types
/// </summary>
[JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))]
[JsonProperty("supportedSignalTypes", NullValueHandling = NullValueHandling.Ignore)]
public eRoutingSignalType? SupportedSignalTypes => _input?.SupportedSignalTypes;
/// <summary>
/// Gets the name
/// </summary>
[JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)]
public string Name => _input?.Name;
/// <summary>
/// Gets the online status
/// </summary>
[JsonProperty("isOnline", NullValueHandling = NullValueHandling.Ignore)]
public bool? IsOnline => _input?.IsOnline.BoolValue;
/// <summary>
/// Gets the video sync detected status
/// </summary>
[JsonProperty("videoSyncDetected", NullValueHandling = NullValueHandling.Ignore)]
public bool? VideoSyncDetected => _input?.VideoSyncDetected;
/// <summary>
/// Gets the key
/// </summary>
[JsonProperty("key", NullValueHandling = NullValueHandling.Ignore)]
public string Key => _input?.Key;
/// <summary>
/// Initializes a new instance of the RoutingInput class
/// </summary>
/// <param name="input">The input slot</param>
public RoutingInput(IRoutingInputSlot input)
{
_input = input;
}
}
/// <summary>
/// Represents a routing output slot
/// </summary>
public class RoutingOutput
{
private IRoutingOutputSlot _output;
/// <summary>
/// Initializes a new instance of the RoutingOutput class
/// </summary>
/// <param name="output">The output slot</param>
public RoutingOutput(IRoutingOutputSlot output)
{
_output = output;
}
/// <summary>
/// Gets the receive device key
/// </summary>
[JsonProperty("rxDeviceKey")]
public string RxDeviceKey => _output.RxDeviceKey;
/// <summary>
/// Gets the current routes
/// </summary>
[JsonProperty("currentRoutes")]
public Dictionary<string, RoutingInput> CurrentRoutes => _output.CurrentRoutes.ToDictionary(kvp => kvp.Key.ToString(), kvp => new RoutingInput(kvp.Value));
/// <summary>
/// Gets the slot number
/// </summary>
[JsonProperty("slotNumber")]
public int SlotNumber => _output.SlotNumber;
/// <summary>
/// Gets the supported signal types
/// </summary>
[JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))]
[JsonProperty("supportedSignalTypes")]
public eRoutingSignalType SupportedSignalTypes => _output.SupportedSignalTypes;
/// <summary>
/// Gets the name
/// </summary>
[JsonProperty("name")]
public string Name => _output.Name;
/// <summary>
/// Gets the key
/// </summary>
[JsonProperty("key")]
public string Key => _output.Key;
}
/// <summary>
/// Request for matrix routing
/// </summary>
public class MatrixRouteRequest
{
/// <summary>
/// Gets or sets the output key
/// </summary>
[JsonProperty("outputKey")]
public string OutputKey { get; set; }
/// <summary>
/// Gets or sets the input key
/// </summary>
[JsonProperty("inputKey")]
public string InputKey { get; set; }
/// <summary>
/// Gets or sets the route type
/// </summary>
[JsonProperty("routeType")]
public eRoutingSignalType RouteType { get; set; }
}

View File

@@ -1,9 +1,9 @@
using Newtonsoft.Json;
using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Linq;
using PepperDash.Core;
using PepperDash.Essentials.Core.DeviceTypeInterfaces;
using System;
namespace PepperDash.Essentials.AppServer.Messengers
{
@@ -21,7 +21,7 @@ namespace PepperDash.Essentials.AppServer.Messengers
{
base.RegisterActions();
AddAction("/fullStatus", (id, content) => SendFullStatus());
AddAction("/fullStatus", (id, content) => SendFullStatus(id));
AddAction("/raise", (id, content) =>
{
@@ -50,7 +50,7 @@ namespace PepperDash.Essentials.AppServer.Messengers
PostStatusMessage(JToken.FromObject(state));
}
private void SendFullStatus()
private void SendFullStatus(string id = null)
{
var state = new ScreenLiftStateMessage
{
@@ -59,7 +59,7 @@ namespace PepperDash.Essentials.AppServer.Messengers
DisplayDeviceKey = device.DisplayDeviceKey
};
PostStatusMessage(state);
PostStatusMessage(state, id);
}
}

View File

@@ -1,12 +1,15 @@
using Newtonsoft.Json;
using System;
using Newtonsoft.Json;
using PepperDash.Core;
using PepperDash.Core.Logging;
using PepperDash.Essentials.Core;
using System;
namespace PepperDash.Essentials.AppServer.Messengers
{
/// <summary>
/// Messenger for routing actions
/// </summary>
public class RunRouteActionMessenger : MessengerBase
{
/// <summary>
@@ -14,6 +17,13 @@ namespace PepperDash.Essentials.AppServer.Messengers
/// </summary>
public IRunRouteAction RoutingDevice { get; private set; }
/// <summary>
/// Initializes a new instance of the <see cref="RunRouteActionMessenger"/> class.
/// </summary>
/// <param name="key">Unique identifier for the messenger</param>
/// <param name="routingDevice">Device that implements IRunRouteAction</param>
/// <param name="messagePath">Path for message routing</param>
/// <exception cref="ArgumentNullException">Thrown when routingDevice is null</exception>
public RunRouteActionMessenger(string key, IRunRouteAction routingDevice, string messagePath)
: base(key, messagePath, routingDevice as IKeyName)
{
@@ -31,9 +41,10 @@ namespace PepperDash.Essentials.AppServer.Messengers
SendRoutingFullMessageObject();
}
/// <inheritdoc />
protected override void RegisterActions()
{
AddAction("/fullStatus", (id, content) => SendRoutingFullMessageObject());
AddAction("/fullStatus", (id, content) => SendRoutingFullMessageObject(id));
AddAction("/source", (id, content) =>
{
@@ -59,7 +70,7 @@ namespace PepperDash.Essentials.AppServer.Messengers
/// <summary>
/// Helper method to update full status of the routing device
/// </summary>
private void SendRoutingFullMessageObject()
private void SendRoutingFullMessageObject(string id = null)
{
if (RoutingDevice is IRoutingSink sinkDevice)
{
@@ -71,13 +82,19 @@ namespace PepperDash.Essentials.AppServer.Messengers
PostStatusMessage(new RoutingStateMessage
{
SelectedSourceKey = sourceKey
});
}, id);
}
}
}
/// <summary>
/// Message class for routing state
/// </summary>
public class RoutingStateMessage : DeviceStateMessageBase
{
/// <summary>
/// Gets or sets the selected source key
/// </summary>
[JsonProperty("selectedSourceKey")]
public string SelectedSourceKey { get; set; }
}

View File

@@ -1,14 +1,18 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using PepperDash.Core;
using PepperDash.Core.Logging;
using PepperDash.Essentials.Core.DeviceTypeInterfaces;
using System;
using System.Collections.Generic;
namespace PepperDash.Essentials.AppServer.Messengers
{
/// <summary>
/// Messenger for devices that implement ISelectableItems interface
/// </summary>
/// <typeparam name="TKey">Type of the key used for selectable items</typeparam>
public class ISelectableItemsMessenger<TKey> : MessengerBase
{
{
private readonly ISelectableItems<TKey> itemDevice;
private readonly string _propName;
@@ -16,23 +20,24 @@ namespace PepperDash.Essentials.AppServer.Messengers
/// <summary>
/// Constructs a messenger for a device that implements ISelectableItems<typeparamref name="TKey"/>
/// </summary>
/// <param name="key"></param>
/// <param name="messagePath"></param>
/// <param name="device"></param>
/// <param name="propName"></param>
/// <param name="key">Unique identifier for the messenger</param>
/// <param name="messagePath">Path for message routing</param>
/// <param name="device">Device that implements ISelectableItems</param>
/// <param name="propName">Property name for JSON serialization</param>
public ISelectableItemsMessenger(string key, string messagePath, ISelectableItems<TKey> device, string propName) : base(key, messagePath, device as IKeyName)
{
itemDevice = device;
_propName = propName;
}
/// <inheritdoc />
protected override void RegisterActions()
{
base.RegisterActions();
AddAction("/fullStatus", (id, context) =>
{
SendFullStatus();
SendFullStatus(id);
});
itemDevice.ItemsUpdated += (sender, args) =>
@@ -62,7 +67,7 @@ namespace PepperDash.Essentials.AppServer.Messengers
}
}
private void SendFullStatus()
private void SendFullStatus(string id = null)
{
try
{
@@ -74,7 +79,7 @@ namespace PepperDash.Essentials.AppServer.Messengers
CurrentItem = itemDevice.CurrentItem
};
PostStatusMessage(stateObject);
PostStatusMessage(stateObject, id);
}
catch (Exception e)
{
@@ -83,11 +88,21 @@ namespace PepperDash.Essentials.AppServer.Messengers
}
}
/// <summary>
/// State message for selectable items
/// </summary>
/// <typeparam name="TKey">Type of the key used for selectable items</typeparam>
public class ISelectableItemsStateMessage<TKey> : DeviceStateMessageBase
{
/// <summary>
/// Gets or sets the items dictionary
/// </summary>
[JsonProperty("items")]
public Dictionary<TKey, ISelectableItem> Items { get; set; }
/// <summary>
/// Gets or sets the current item
/// </summary>
[JsonProperty("currentItem")]
public TKey CurrentItem { get; set; }
}

View File

@@ -5,16 +5,26 @@ using PepperDash.Essentials.Core;
namespace PepperDash.Essentials.AppServer.Messengers
{
/// <summary>
/// Messenger for the shutdown prompt timer
/// </summary>
public class IShutdownPromptTimerMessenger : MessengerBase
{
private readonly IShutdownPromptTimer _room;
/// <summary>
/// Initializes a new instance of the <see cref="IShutdownPromptTimerMessenger"/> class.
/// </summary>
/// <param name="key">Unique identifier for the messenger</param>
/// <param name="messagePath">Path for message routing</param>
/// <param name="room">Room that implements IShutdownPromptTimer</param>
public IShutdownPromptTimerMessenger(string key, string messagePath, IShutdownPromptTimer room)
: base(key, messagePath, room as IKeyName)
{
_room = room;
}
/// <inheritdoc />
protected override void RegisterActions()
{
AddAction("/status", (id, content) =>
@@ -65,7 +75,7 @@ namespace PepperDash.Essentials.AppServer.Messengers
};
}
private void SendFullStatus()
private void SendFullStatus(string id = null)
{
var status = new IShutdownPromptTimerStateMessage
{
@@ -74,7 +84,7 @@ namespace PepperDash.Essentials.AppServer.Messengers
PercentageRemaining = _room.ShutdownPromptTimer.PercentFeedback.UShortValue
};
PostStatusMessage(status);
PostStatusMessage(status, id);
}
}

View File

@@ -1,26 +1,39 @@
using Newtonsoft.Json;
using System;
using Newtonsoft.Json;
using PepperDash.Core;
using PepperDash.Essentials.Core.CrestronIO;
using System;
namespace PepperDash.Essentials.AppServer.Messengers
{
/// <summary>
/// Messenger for ISwitchedOutput devices
/// </summary>
public class ISwitchedOutputMessenger : MessengerBase
{
private readonly ISwitchedOutput device;
/// <summary>
/// Initializes a new instance of the <see cref="ISwitchedOutputMessenger"/> class.
/// This messenger provides mobile control interface for switched output devices.
/// It allows sending commands to turn the output on or off, and provides feedback on the
/// current state of the output.
/// </summary>
/// <param name="key">Unique identifier for the messenger</param>
/// <param name="device">Device that implements ISwitchedOutput</param>
/// <param name="messagePath">Path for message routing</param>
public ISwitchedOutputMessenger(string key, ISwitchedOutput device, string messagePath)
: base(key, messagePath, device as IKeyName)
{
this.device = device;
}
/// <inheritdoc />
protected override void RegisterActions()
{
base.RegisterActions();
AddAction("/fullStatus", (id, content) => SendFullStatus());
AddAction("/fullStatus", (id, content) => SendFullStatus(id));
AddAction("/on", (id, content) =>
{
@@ -39,19 +52,29 @@ namespace PepperDash.Essentials.AppServer.Messengers
device.OutputIsOnFeedback.OutputChange += new EventHandler<Core.FeedbackEventArgs>((o, a) => SendFullStatus());
}
private void SendFullStatus()
private void SendFullStatus(string id = null)
{
var state = new ISwitchedOutputStateMessage
{
IsOn = device.OutputIsOnFeedback.BoolValue
};
PostStatusMessage(state);
PostStatusMessage(state, id);
}
}
/// <summary>
/// State message for ISwitchedOutput devices
/// This message contains the current state of the switched output, specifically whether it is on or off.
/// It is used to communicate the state of the output to clients that subscribe to this messenger.
/// </summary>
public class ISwitchedOutputStateMessage : DeviceStateMessageBase
{
/// <summary>
/// Gets or sets a value indicating whether the switched output is currently on.
/// This property is used to convey the current state of the output to clients.
/// A value of true indicates that the output is on, while false indicates it is off.
/// </summary>
[JsonProperty("isOn")]
public bool IsOn { get; set; }
}

View File

@@ -4,22 +4,32 @@ using PepperDash.Essentials.Core;
namespace PepperDash.Essentials.AppServer.Messengers
{
/// <summary>
/// Messenger for devices that implement ITechPassword interface
/// </summary>
public class ITechPasswordMessenger : MessengerBase
{
private readonly ITechPassword _room;
/// <summary>
/// Initializes a new instance of the ITechPasswordMessenger class
/// </summary>
/// <param name="key">Unique identifier for the messenger</param>
/// <param name="messagePath">Path for message routing</param>
/// <param name="room">Room that implements ITechPassword</param>
public ITechPasswordMessenger(string key, string messagePath, ITechPassword room)
: base(key, messagePath, room as IKeyName)
{
_room = room;
}
/// <inheritdoc />
protected override void RegisterActions()
{
AddAction("/status", (id, content) =>
{
SendFullStatus();
SendFullStatus(id);
});
AddAction("/validateTechPassword", (id, content) =>
@@ -52,35 +62,56 @@ namespace PepperDash.Essentials.AppServer.Messengers
};
}
private void SendFullStatus()
private void SendFullStatus(string id = null)
{
var status = new ITechPasswordStateMessage
{
TechPasswordLength = _room.TechPasswordLength
};
PostStatusMessage(status);
PostStatusMessage(status, id);
}
}
/// <summary>
/// State message for tech password information
/// </summary>
public class ITechPasswordStateMessage : DeviceStateMessageBase
{
/// <summary>
/// Gets or sets the tech password length
/// </summary>
[JsonProperty("techPasswordLength", NullValueHandling = NullValueHandling.Ignore)]
public int? TechPasswordLength { get; set; }
}
/// <summary>
/// Event message for tech password validation result
/// </summary>
public class ITechPasswordEventMessage : DeviceEventMessageBase
{
/// <summary>
/// Gets or sets whether the password is valid
/// </summary>
[JsonProperty("isValid", NullValueHandling = NullValueHandling.Ignore)]
public bool? IsValid { get; set; }
}
/// <summary>
/// Content for setting tech password
/// </summary>
internal class SetTechPasswordContent
{
/// <summary>
/// Gets or sets the old password
/// </summary>
[JsonProperty("oldPassword")]
public string OldPassword { get; set; }
/// <summary>
/// Gets or sets the new password
/// </summary>
[JsonProperty("newPassword")]
public string NewPassword { get; set; }
}

View File

@@ -1,25 +1,37 @@
using Newtonsoft.Json;
using System;
using Newtonsoft.Json;
using PepperDash.Core;
using PepperDash.Essentials.Core.DeviceTypeInterfaces;
using System;
namespace PepperDash.Essentials.AppServer.Messengers
{
/// <summary>
/// Messenger for temperature sensor devices
/// </summary>
public class ITemperatureSensorMessenger : MessengerBase
{
private readonly ITemperatureSensor device;
/// <summary>
/// Initializes a new instance of the ITemperatureSensorMessenger class
/// This messenger provides a mobile control interface for temperature sensor devices.
/// It allows clients to retrieve the current temperature and change the temperature format between Celsius and Fahrenheit.
/// </summary>
/// <param name="key">Unique identifier for the messenger</param>
/// <param name="device">Device that implements ITemperatureSensor</param>
/// <param name="messagePath">Path for message routing</param>
public ITemperatureSensorMessenger(string key, ITemperatureSensor device, string messagePath)
: base(key, messagePath, device as IKeyName)
{
this.device = device;
}
/// <inheritdoc />
protected override void RegisterActions()
{
base.RegisterActions();
AddAction("/fullStatus", (id, content) => SendFullStatus());
AddAction("/fullStatus", (id, content) => SendFullStatus(id));
AddAction("/setTemperatureUnitsToCelcius", (id, content) =>
{
@@ -35,7 +47,7 @@ namespace PepperDash.Essentials.AppServer.Messengers
device.TemperatureInCFeedback.OutputChange += new EventHandler<Core.FeedbackEventArgs>((o, a) => SendFullStatus());
}
private void SendFullStatus()
private void SendFullStatus(string id = null)
{
// format the temperature to a string with one decimal place
var tempString = string.Format("{0}.{1}", device.TemperatureFeedback.UShortValue / 10, device.TemperatureFeedback.UShortValue % 10);
@@ -46,15 +58,27 @@ namespace PepperDash.Essentials.AppServer.Messengers
TemperatureInCelsius = device.TemperatureInCFeedback.BoolValue
};
PostStatusMessage(state);
PostStatusMessage(state, id);
}
}
/// <summary>
/// State message for temperature sensor devices
/// </summary>
public class ITemperatureSensorStateMessage : DeviceStateMessageBase
{
/// <summary>
/// Gets or sets the current temperature reading from the sensor.
/// The temperature is represented as a string formatted to one decimal place.
/// For example, "22.5" for 22.5 degrees.
/// </summary>
[JsonProperty("temperature")]
public string Temperature { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the temperature is in Celsius.
/// This property is true if the temperature is in Celsius, and false if it is in Fahrenheit.
/// </summary>
[JsonProperty("temperatureInCelsius")]
public bool TemperatureInCelsius { get; set; }
}

View File

@@ -1,15 +1,25 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using PepperDash.Core;
using PepperDash.Essentials.Core.Lighting;
using System;
using System.Collections.Generic;
namespace PepperDash.Essentials.AppServer.Messengers
{
/// <summary>
/// Messenger for lighting scenes devices
/// </summary>
public class ILightingScenesMessenger : MessengerBase
{
private ILightingScenes lightingScenesDevice;
/// <summary>
/// Initializes a new instance of the <see cref="ILightingScenesMessenger"/> class.
/// </summary>
/// <param name="key">Unique identifier for the messenger</param>
/// <param name="device">Device that implements ILightingScenes</param>
/// <param name="messagePath">Path for message routing</param>
/// <exception cref="ArgumentNullException">Thrown when device is null</exception>
public ILightingScenesMessenger(string key, ILightingScenes device, string messagePath)
: base(key, messagePath, device as IKeyName)
{
@@ -28,11 +38,12 @@ namespace PepperDash.Essentials.AppServer.Messengers
PostStatusMessage(state);
}
/// <inheritdoc />
protected override void RegisterActions()
{
base.RegisterActions();
AddAction("/fullStatus", (id, content) => SendFullStatus());
AddAction("/fullStatus", (id, content) => SendFullStatus(id));
AddAction("/selectScene", (id, content) =>
{
@@ -40,14 +51,14 @@ namespace PepperDash.Essentials.AppServer.Messengers
lightingScenesDevice.SelectScene(s);
});
if(!(lightingScenesDevice is ILightingScenesDynamic lightingScenesDynamic))
if (!(lightingScenesDevice is ILightingScenesDynamic lightingScenesDynamic))
return;
lightingScenesDynamic.LightingScenesUpdated += (s, e) => SendFullStatus();
}
private void SendFullStatus()
private void SendFullStatus(string id = null)
{
var state = new LightingBaseStateMessage
{
@@ -55,7 +66,7 @@ namespace PepperDash.Essentials.AppServer.Messengers
CurrentLightingScene = lightingScenesDevice.CurrentLightingScene
};
PostStatusMessage(state);
PostStatusMessage(state, id);
}
}

View File

@@ -1,41 +1,57 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using PepperDash.Core;
using PepperDash.Core.Logging;
using PepperDash.Essentials.Core;
using PepperDash.Essentials.Core.DeviceTypeInterfaces;
using System;
using System.Collections.Generic;
using System.Linq;
namespace PepperDash.Essentials.AppServer.Messengers
{
/// <summary>
/// Provides a messaging bridge
/// Provides a messaging bridge between mobile control clients and Essentials devices.
/// This abstract base class handles message routing, action registration, and status updates.
/// </summary>
public abstract class MessengerBase : EssentialsDevice, IMobileControlMessenger
{
/// <summary>
/// The device that this messenger is associated with
/// </summary>
protected IKeyName _device;
/// <summary>
/// List of interfaces implemented by the associated device
/// </summary>
private readonly List<string> _deviceInterfaces;
/// <summary>
/// Dictionary of registered actions, keyed by path
/// </summary>
private readonly Dictionary<string, Action<string, JToken>> _actions = new Dictionary<string, Action<string, JToken>>();
/// <summary>
/// Gets the key of the associated device
/// </summary>
public string DeviceKey => _device?.Key ?? "";
/// <summary>
///
/// Gets the mobile control app server controller
/// </summary>
public IMobileControl AppServerController { get; private set; }
/// <summary>
/// Gets the message path for this messenger
/// </summary>
public string MessagePath { get; private set; }
/// <summary>
///
/// Initializes a new instance of the MessengerBase class
/// </summary>
/// <param name="key"></param>
/// <param name="messagePath"></param>
/// <param name="key">The unique key for this messenger</param>
/// <param name="messagePath">The message path for routing messages</param>
/// <exception cref="ArgumentException">Thrown when messagePath is null or empty</exception>
protected MessengerBase(string key, string messagePath)
: base(key)
{
@@ -47,6 +63,12 @@ namespace PepperDash.Essentials.AppServer.Messengers
MessagePath = messagePath;
}
/// <summary>
/// Initializes a new instance of the MessengerBase class with an associated device
/// </summary>
/// <param name="key">The unique key for this messenger</param>
/// <param name="messagePath">The message path for routing messages</param>
/// <param name="device">The device to associate with this messenger</param>
protected MessengerBase(string key, string messagePath, IKeyName device)
: this(key, messagePath)
{
@@ -56,7 +78,7 @@ namespace PepperDash.Essentials.AppServer.Messengers
}
/// <summary>
/// Gets the interfaces implmented on the device
/// Gets the interfaces implented on the device
/// </summary>
/// <param name="device"></param>
/// <returns></returns>
@@ -93,6 +115,11 @@ namespace PepperDash.Essentials.AppServer.Messengers
action(id, content);
}
/// <summary>
/// Adds an action to be executed when a message is received at the specified path
/// </summary>
/// <param name="path">The path to register the action for</param>
/// <param name="action">The action to execute when the path is matched</param>
protected void AddAction(string path, Action<string, JToken> action)
{
if (_actions.ContainsKey(path))
@@ -104,11 +131,19 @@ namespace PepperDash.Essentials.AppServer.Messengers
_actions.Add(path, action);
}
/// <summary>
/// Gets a list of all registered action paths
/// </summary>
/// <returns>A list of action paths</returns>
public List<string> GetActionPaths()
{
return _actions.Keys.ToList();
}
/// <summary>
/// Removes an action from the specified path
/// </summary>
/// <param name="path">The path to remove the action from</param>
protected void RemoveAction(string path)
{
if (!_actions.ContainsKey(path))
@@ -122,7 +157,6 @@ namespace PepperDash.Essentials.AppServer.Messengers
/// <summary>
/// Implemented in extending classes. Wire up API calls and feedback here
/// </summary>
/// <param name="appServerController"></param>
protected virtual void RegisterActions()
{
@@ -131,8 +165,8 @@ namespace PepperDash.Essentials.AppServer.Messengers
/// <summary>
/// Helper for posting status message
/// </summary>
/// <param name="type"></param>
/// <param name="message"></param>
/// <param name="message">The device state message to post</param>
/// <param name="clientId">Optional client ID to send the message to a specific client</param>
protected void PostStatusMessage(DeviceStateMessageBase message, string clientId = null)
{
try
@@ -153,16 +187,22 @@ namespace PepperDash.Essentials.AppServer.Messengers
message.Name = _device.Name;
var token = JToken.FromObject(message);
var token = JToken.FromObject(message);
PostStatusMessage(token, MessagePath, clientId);
}
catch (Exception ex)
{
this.LogError(ex, "Exception posting status message for {messagePath} to {clientId}", MessagePath, clientId ?? "all clients");
this.LogError(ex, "Exception posting status message for {messagePath} to {clientId}", MessagePath, clientId ?? "all clients");
}
}
/// <summary>
/// Posts a status message with a specific message type
/// </summary>
/// <param name="type">The message type to send</param>
/// <param name="deviceState">The device state message to post</param>
/// <param name="clientId">Optional client ID to send the message to a specific client</param>
protected void PostStatusMessage(string type, DeviceStateMessageBase deviceState, string clientId = null)
{
try
@@ -182,10 +222,16 @@ namespace PepperDash.Essentials.AppServer.Messengers
}
catch (Exception ex)
{
this.LogError(ex, "Exception posting status message for {type} to {clientId}", type, clientId ?? "all clients");
this.LogError(ex, "Exception posting status message for {type} to {clientId}", type, clientId ?? "all clients");
}
}
/// <summary>
/// Posts a status message with JSON content
/// </summary>
/// <param name="content">The JSON content to send</param>
/// <param name="type">The message type (defaults to MessagePath if empty)</param>
/// <param name="clientId">Optional client ID to send the message to a specific client</param>
protected void PostStatusMessage(JToken content, string type = "", string clientId = null)
{
try
@@ -198,6 +244,10 @@ namespace PepperDash.Essentials.AppServer.Messengers
}
}
/// <summary>
/// Posts an event message for the device
/// </summary>
/// <param name="message">The device event message to post</param>
protected void PostEventMessage(DeviceEventMessageBase message)
{
message.Key = _device.Key;
@@ -211,6 +261,11 @@ namespace PepperDash.Essentials.AppServer.Messengers
});
}
/// <summary>
/// Posts an event message with a specific event type
/// </summary>
/// <param name="message">The device event message to post</param>
/// <param name="eventType">The event type to use</param>
protected void PostEventMessage(DeviceEventMessageBase message, string eventType)
{
message.Key = _device.Key;
@@ -226,6 +281,10 @@ namespace PepperDash.Essentials.AppServer.Messengers
});
}
/// <summary>
/// Posts an event message with only an event type
/// </summary>
/// <param name="eventType">The event type to post</param>
protected void PostEventMessage(string eventType)
{
AppServerController?.SendMessageObject(new MobileControlMessage
@@ -237,6 +296,9 @@ namespace PepperDash.Essentials.AppServer.Messengers
}
/// <summary>
/// Base class for device messages containing common properties like key, name, and message type
/// </summary>
public abstract class DeviceMessageBase
{
/// <summary>
@@ -257,21 +319,28 @@ namespace PepperDash.Essentials.AppServer.Messengers
[JsonProperty("messageType")]
public string MessageType => GetType().Name;
/// <summary>
/// Gets or sets the base path for the message
/// </summary>
[JsonProperty("messageBasePath")]
public string MessageBasePath { get; set; }
}
/// <summary>
/// Base class for state messages that includes the type of message and the implmented interfaces
/// Base class for state messages that includes the type of message and the implemented interfaces
/// </summary>
public class DeviceStateMessageBase : DeviceMessageBase
{
/// <summary>
/// The interfaces implmented by the device sending the messsage
/// The interfaces implented by the device sending the messsage
/// </summary>
[JsonProperty("interfaces")]
public List<string> Interfaces { get; private set; }
/// <summary>
/// Sets the interfaces implemented by the device
/// </summary>
/// <param name="interfaces">List of interface names to set</param>
public void SetInterfaces(List<string> interfaces)
{
Interfaces = interfaces;

View File

@@ -1,19 +1,34 @@
using Crestron.SimplSharp;
using System;
using System.Collections.Generic;
using Crestron.SimplSharp;
using Newtonsoft.Json.Linq;
using PepperDash.Core;
using System;
using System.Collections.Generic;
namespace PepperDash.Essentials.AppServer.Messengers
{
/// <summary>
/// Static handler for managing press and hold button actions with automatic timeout functionality
/// </summary>
public static class PressAndHoldHandler
{
/// <summary>
/// The interval in milliseconds for button heartbeat timeout
/// </summary>
private const long ButtonHeartbeatInterval = 1000;
/// <summary>
/// Dictionary of active timers for pressed actions, keyed by device key
/// </summary>
private static readonly Dictionary<string, CTimer> _pushedActions = new Dictionary<string, CTimer>();
/// <summary>
/// Dictionary of action handlers for different button states
/// </summary>
private static readonly Dictionary<string, Action<string, Action<bool>>> _pushedActionHandlers;
/// <summary>
/// Static constructor that initializes the action handlers for different button states
/// </summary>
static PressAndHoldHandler()
{
_pushedActionHandlers = new Dictionary<string, Action<string, Action<bool>>>
@@ -24,6 +39,11 @@ namespace PepperDash.Essentials.AppServer.Messengers
};
}
/// <summary>
/// Adds a timer for a device key and executes the action with true state
/// </summary>
/// <param name="deviceKey">The unique key for the device</param>
/// <param name="action">The action to execute with boolean state</param>
private static void AddTimer(string deviceKey, Action<bool> action)
{
Debug.LogMessage(Serilog.Events.LogEventLevel.Debug, "Attempting to add timer for {deviceKey}", deviceKey);
@@ -50,6 +70,11 @@ namespace PepperDash.Essentials.AppServer.Messengers
_pushedActions.Add(deviceKey, cancelTimer);
}
/// <summary>
/// Resets an existing timer for the specified device key
/// </summary>
/// <param name="deviceKey">The unique key for the device</param>
/// <param name="action">The action associated with the timer</param>
private static void ResetTimer(string deviceKey, Action<bool> action)
{
Debug.LogMessage(Serilog.Events.LogEventLevel.Debug, "Attempting to reset timer for {deviceKey}", deviceKey);
@@ -65,6 +90,11 @@ namespace PepperDash.Essentials.AppServer.Messengers
cancelTimer.Reset(ButtonHeartbeatInterval);
}
/// <summary>
/// Stops and removes the timer for the specified device key
/// </summary>
/// <param name="deviceKey">The unique key for the device</param>
/// <param name="action">The action to execute with false state before stopping</param>
private static void StopTimer(string deviceKey, Action<bool> action)
{
Debug.LogMessage(Serilog.Events.LogEventLevel.Debug, "Attempting to stop timer for {deviceKey}", deviceKey);
@@ -82,6 +112,11 @@ namespace PepperDash.Essentials.AppServer.Messengers
_pushedActions.Remove(deviceKey);
}
/// <summary>
/// Gets the appropriate press and hold handler for the specified value
/// </summary>
/// <param name="value">The button state value (pressed, held, released)</param>
/// <returns>The handler action for the specified state, or null if not found</returns>
public static Action<string, Action<bool>> GetPressAndHoldHandler(string value)
{
Debug.LogMessage(Serilog.Events.LogEventLevel.Debug, "Getting press and hold handler for {value}", value);
@@ -97,6 +132,12 @@ namespace PepperDash.Essentials.AppServer.Messengers
return handler;
}
/// <summary>
/// Handles press and hold messages by parsing the content and executing the appropriate handler
/// </summary>
/// <param name="deviceKey">The unique key for the device</param>
/// <param name="content">The JSON content containing the button state</param>
/// <param name="action">The action to execute with boolean state</param>
public static void HandlePressAndHold(string deviceKey, JToken content, Action<bool> action)
{
var msg = content.ToObject<MobileControlSimpleContent<string>>();

View File

@@ -1,18 +1,30 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using PepperDash.Core;
using PepperDash.Core.Logging;
using PepperDash.Essentials.Core;
using PepperDash.Essentials.Room.Config;
using System;
using System.Collections.Generic;
namespace PepperDash.Essentials.AppServer.Messengers
{
/// <summary>
/// Messenger for managing scheduled events in a room.
/// This class handles saving scheduled events and sending the current schedule state to clients.
/// It listens for changes in the scheduled events and updates clients accordingly.
/// </summary>
public class RoomEventScheduleMessenger : MessengerBase
{
private readonly IRoomEventSchedule _room;
/// <summary>
/// Initializes a new instance of the <see cref="RoomEventScheduleMessenger"/> class.
/// This constructor sets up the messenger with a unique key, message path, and the room event schedule interface.
/// It registers actions for saving scheduled events and sending the current schedule state.
/// </summary>
/// <param name="key">Unique identifier for the messenger</param>
/// <param name="messagePath">Path for message routing</param>
/// <param name="room">Room event schedule interface</param>
public RoomEventScheduleMessenger(string key, string messagePath, IRoomEventSchedule room)
: base(key, messagePath, room as IKeyName)
{
@@ -21,6 +33,7 @@ namespace PepperDash.Essentials.AppServer.Messengers
#region Overrides of MessengerBase
/// <inheritdoc />
protected override void RegisterActions()
{
AddAction("/saveScheduledEvents", (id, content) => SaveScheduledEvents(content.ToObject<List<ScheduledEventConfig>>()));
@@ -28,7 +41,7 @@ namespace PepperDash.Essentials.AppServer.Messengers
{
var events = _room.GetScheduledEvents();
SendFullStatus(events);
SendFullStatus(events, id);
});
_room.ScheduledEventsChanged += (sender, args) => SendFullStatus(args.ScheduledEvents);
@@ -52,11 +65,11 @@ namespace PepperDash.Essentials.AppServer.Messengers
}
catch (Exception ex)
{
this.LogException(ex,"Exception saving event");
this.LogException(ex, "Exception saving event");
}
}
private void SendFullStatus(List<ScheduledEventConfig> events)
private void SendFullStatus(List<ScheduledEventConfig> events, string id = null)
{
var message = new RoomEventScheduleStateMessage
@@ -64,12 +77,22 @@ namespace PepperDash.Essentials.AppServer.Messengers
ScheduleEvents = events,
};
PostStatusMessage(message);
PostStatusMessage(message, id);
}
}
/// <summary>
/// Represents the state message for room event schedules.
/// This message contains a list of scheduled events configured for the room.
/// It is used to communicate the current schedule state to clients.
/// </summary>
public class RoomEventScheduleStateMessage : DeviceStateMessageBase
{
/// <summary>
/// Gets or sets the list of scheduled events for the room.
/// This property contains the configuration of scheduled events that are set up in the room.
/// Each event includes details such as the event name, start time, end time, and recurrence pattern.
/// </summary>
[JsonProperty("scheduleEvents")]
public List<ScheduledEventConfig> ScheduleEvents { get; set; }
}

View File

@@ -1,32 +1,39 @@
using Crestron.SimplSharpPro.DeviceSupport;
using System;
using System.Collections.Generic;
using Crestron.SimplSharpPro.DeviceSupport;
using Newtonsoft.Json.Linq;
using PepperDash.Essentials.Core;
using PepperDash.Essentials.Devices.Common.Codec;
using System;
using System.Collections.Generic;
namespace PepperDash.Essentials.AppServer.Messengers
{
/// <summary>
/// SIMPL messenger for audio-only conference devices that provides audio calling functionality
/// </summary>
// ReSharper disable once InconsistentNaming
public class SIMPLAtcMessenger : MessengerBase
{
/// <summary>
/// The EISC (Ethernet Intersystem Communication) device
/// </summary>
private readonly BasicTriList _eisc;
/// <summary>
/// Gets the join map for SIMPL ATC operations
/// </summary>
public SIMPLAtcJoinMap JoinMap { get; private set; }
/// <summary>
///
/// The current active call item for audio calls
/// </summary>
private readonly CodecActiveCallItem _currentCallItem;
/// <summary>
///
/// Initializes a new instance of the SIMPLAtcMessenger class
/// </summary>
/// <param name="key"></param>
/// <param name="eisc"></param>
/// <param name="messagePath"></param>
/// <param name="key">Unique identifier for the messenger</param>
/// <param name="eisc">The EISC device for communication</param>
/// <param name="messagePath">Path for message routing</param>
public SIMPLAtcMessenger(string key, BasicTriList eisc, string messagePath)
: base(key, messagePath)
{
@@ -38,7 +45,7 @@ namespace PepperDash.Essentials.AppServer.Messengers
}
/// <summary>
///
/// Sends the current status of the audio conference device to connected clients
/// </summary>
private void SendFullStatus()
{
@@ -53,9 +60,8 @@ namespace PepperDash.Essentials.AppServer.Messengers
}
/// <summary>
///
/// Registers actions and feedback handlers for the SIMPL ATC messenger
/// </summary>
/// <param name="appServerController"></param>
protected override void RegisterActions()
{
//EISC.SetStringSigAction(SCurrentDialString, s => PostStatusMessage(new { currentDialString = s }));
@@ -133,7 +139,7 @@ namespace PepperDash.Essentials.AppServer.Messengers
}
/// <summary>
///
/// Sends the current calls list to connected clients
/// </summary>
private void SendCallsList()
{
@@ -145,9 +151,9 @@ namespace PepperDash.Essentials.AppServer.Messengers
}
/// <summary>
/// Turns the
/// Gets the current call list based on the call status
/// </summary>
/// <returns></returns>
/// <returns>A list containing the current call item if connected, or an empty list if disconnected</returns>
private List<CodecActiveCallItem> GetCurrentCallList()
{
return _currentCallItem.Status == eCodecCallStatus.Disconnected

View File

@@ -1,14 +1,18 @@
using Crestron.SimplSharpPro.DeviceSupport;
using System;
using System.Collections.Generic;
using Crestron.SimplSharpPro.DeviceSupport;
using Newtonsoft.Json.Linq;
using PepperDash.Essentials.Core;
using PepperDash.Essentials.Core.Bridges;
using PepperDash.Essentials.Core.DeviceTypeInterfaces;
using PepperDash.Essentials.Devices.Common.Cameras;
using System;
using System.Collections.Generic;
namespace PepperDash.Essentials.AppServer.Messengers
{
/// <summary>
/// Provides messaging capabilities for camera control operations in SIMPL-based systems.
/// Handles camera movement, zoom, preset management, and mode control.
/// </summary>
// ReSharper disable once InconsistentNaming
public class SIMPLCameraMessenger : MessengerBase
{
@@ -16,7 +20,13 @@ namespace PepperDash.Essentials.AppServer.Messengers
private readonly CameraControllerJoinMap _joinMap;
/// <summary>
/// Initializes a new instance of the <see cref="SIMPLCameraMessenger"/> class.
/// </summary>
/// <param name="key">The unique identifier for this messenger instance.</param>
/// <param name="eisc">The basic tri-list for SIMPL communication.</param>
/// <param name="messagePath">The message path for camera control messages.</param>
/// <param name="joinStart">The starting join number for SIMPL signal mapping.</param>
public SIMPLCameraMessenger(string key, BasicTriList eisc, string messagePath, uint joinStart)
: base(key, messagePath)
{
@@ -32,6 +42,10 @@ namespace PepperDash.Essentials.AppServer.Messengers
}
/// <summary>
/// Registers actions for handling camera control operations.
/// Includes camera movement, zoom, preset management, and mode control actions.
/// </summary>
protected override void RegisterActions()
{
AddAction("/fullStatus", (id, content) => SendCameraFullMessageObject());

View File

@@ -1,19 +1,35 @@
using Crestron.SimplSharpPro.DeviceSupport;
using System.Collections.Generic;
using Crestron.SimplSharpPro.DeviceSupport;
using Newtonsoft.Json.Linq;
using PepperDash.Core;
using PepperDash.Essentials.Core;
using System.Collections.Generic;
namespace PepperDash.Essentials.AppServer.Messengers
{
/// <summary>
/// Provides messaging capabilities for direct routing operations in SIMPL-based systems.
/// Handles source selection, destination routing, and advanced sharing mode functionality.
/// </summary>
public class SimplDirectRouteMessenger : MessengerBase
{
private readonly BasicTriList _eisc;
/// <summary>
/// Gets the join map for SIMPL direct route actions.
/// </summary>
public MobileControlSIMPLRunDirectRouteActionJoinMap JoinMap { get; private set; }
/// <summary>
/// Gets or sets the dictionary of destination items for routing operations.
/// </summary>
public Dictionary<string, DestinationListItem> DestinationList { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="SimplDirectRouteMessenger"/> class.
/// </summary>
/// <param name="key">The unique identifier for this messenger instance.</param>
/// <param name="eisc">The basic tri-list for SIMPL communication.</param>
/// <param name="messagePath">The message path for routing messages.</param>
public SimplDirectRouteMessenger(string key, BasicTriList eisc, string messagePath) : base(key, messagePath)
{
_eisc = eisc;
@@ -25,6 +41,10 @@ namespace PepperDash.Essentials.AppServer.Messengers
#region Overrides of MessengerBase
/// <summary>
/// Registers actions for handling direct route messaging operations.
/// Includes audio source selection, full status reporting, and advanced sharing mode controls.
/// </summary>
protected override void RegisterActions()
{
Debug.Console(2, "********** Direct Route Messenger CustomRegisterWithAppServer **********");
@@ -92,6 +112,10 @@ namespace PepperDash.Essentials.AppServer.Messengers
));
}
/// <summary>
/// Registers routing actions for each destination in the destination list.
/// Sets up feedback handlers and source selection actions for individual destinations.
/// </summary>
public void RegisterForDestinationPaths()
{
//handle routing feedback from SIMPL
@@ -114,6 +138,11 @@ namespace PepperDash.Essentials.AppServer.Messengers
#endregion
/// <summary>
/// Updates the source selection for a specific destination and posts a status message.
/// </summary>
/// <param name="sourceKey">The key of the selected source.</param>
/// <param name="destKey">The key of the destination being updated.</param>
private void UpdateSourceForDestination(string sourceKey, string destKey)
{
PostStatusMessage(JToken.FromObject(new

View File

@@ -6,21 +6,34 @@ using PepperDash.Essentials.Core.DeviceTypeInterfaces;
namespace PepperDash.Essentials.AppServer.Messengers
{
/// <summary>
/// Provides messaging capabilities for routing operations in SIMPL-based systems.
/// Handles source selection and routing status feedback.
/// </summary>
public class SIMPLRouteMessenger : MessengerBase
{
private readonly BasicTriList _eisc;
private readonly uint _joinStart;
/// <summary>
/// Defines the string join mappings for SIMPL routing operations.
/// </summary>
public class StringJoin
{
/// <summary>
/// 1
/// Join number for current source information (1).
/// </summary>
public const uint CurrentSource = 1;
}
/// <summary>
/// Initializes a new instance of the <see cref="SIMPLRouteMessenger"/> class.
/// </summary>
/// <param name="key">The unique identifier for this messenger instance.</param>
/// <param name="eisc">The basic tri-list for SIMPL communication.</param>
/// <param name="messagePath">The message path for routing messages.</param>
/// <param name="joinStart">The starting join number for SIMPL signal mapping.</param>
public SIMPLRouteMessenger(string key, BasicTriList eisc, string messagePath, uint joinStart)
: base(key, messagePath)
{
@@ -30,6 +43,10 @@ namespace PepperDash.Essentials.AppServer.Messengers
_eisc.SetStringSigAction(_joinStart + StringJoin.CurrentSource, SendRoutingFullMessageObject);
}
/// <summary>
/// Registers actions for handling routing operations and status reporting.
/// Includes full status requests and source selection actions.
/// </summary>
protected override void RegisterActions()
{
AddAction("/fullStatus",
@@ -43,6 +60,11 @@ namespace PepperDash.Essentials.AppServer.Messengers
});
}
/// <summary>
/// Unregisters this messenger from the mobile control app server.
/// Removes all registered actions and clears SIMPL signal actions.
/// </summary>
/// <param name="appServerController">The mobile control app server controller.</param>
public void CustomUnregisterWithAppServer(IMobileControl appServerController)
{
appServerController.RemoveAction(MessagePath + "/fullStatus");

View File

@@ -1,33 +1,51 @@
using Crestron.SimplSharpPro.DeviceSupport;
using System;
using System.Collections.Generic;
using Crestron.SimplSharpPro.DeviceSupport;
using Newtonsoft.Json.Linq;
using PepperDash.Core;
using PepperDash.Essentials.Core;
using PepperDash.Essentials.Devices.Common.Cameras;
using PepperDash.Essentials.Devices.Common.Codec;
using System;
using System.Collections.Generic;
namespace PepperDash.Essentials.AppServer.Messengers
{
/// <summary>
/// SIMPL messenger for video conference devices that provides video calling and camera control functionality
/// </summary>
// ReSharper disable once InconsistentNaming
public class SIMPLVtcMessenger : MessengerBase
{
/// <summary>
/// The EISC (Ethernet Intersystem Communication) device
/// </summary>
private readonly BasicTriList _eisc;
/// <summary>
/// Gets the join map for SIMPL VTC operations
/// </summary>
public SIMPLVtcJoinMap JoinMap { get; private set; }
/// <summary>
/// The current active call item for video calls
/// </summary>
private readonly CodecActiveCallItem _currentCallItem;
/// <summary>
/// The incoming call item for video calls
/// </summary>
private CodecActiveCallItem _incomingCallItem;
/// <summary>
/// The previous directory length for tracking changes
/// </summary>
private ushort _previousDirectoryLength = 701;
/// <summary>
///
/// Initializes a new instance of the SIMPLVtcMessenger class
/// </summary>
/// <param name="key"></param>
/// <param name="eisc"></param>
/// <param name="messagePath"></param>
/// <param name="key">Unique identifier for the messenger</param>
/// <param name="eisc">The EISC device for communication</param>
/// <param name="messagePath">Path for message routing</param>
public SIMPLVtcMessenger(string key, BasicTriList eisc, string messagePath)
: base(key, messagePath)
{
@@ -39,9 +57,8 @@ namespace PepperDash.Essentials.AppServer.Messengers
}
/// <summary>
///
/// Registers actions and feedback handlers for the SIMPL VTC messenger
/// </summary>
/// <param name="appServerController"></param>
protected override void RegisterActions()
{
_eisc.SetStringSigAction(JoinMap.HookState.JoinNumber, s =>

View File

@@ -1,25 +1,39 @@
using Newtonsoft.Json;
using System;
using Newtonsoft.Json;
using PepperDash.Core;
using PepperDash.Essentials.Core.Shades;
using System;
namespace PepperDash.Essentials.AppServer.Messengers
{
/// <summary>
/// Provides messaging capabilities for shade control operations.
/// Handles shade open, close, and stop commands for shades that support these operations.
/// </summary>
public class IShadesOpenCloseStopMessenger : MessengerBase
{
private readonly IShadesOpenCloseStop device;
/// <summary>
/// Initializes a new instance of the <see cref="IShadesOpenCloseStopMessenger"/> class.
/// </summary>
/// <param name="key">The unique identifier for this messenger instance.</param>
/// <param name="shades">The shade device that provides open/close/stop functionality.</param>
/// <param name="messagePath">The message path for shade control messages.</param>
public IShadesOpenCloseStopMessenger(string key, IShadesOpenCloseStop shades, string messagePath)
: base(key, messagePath, shades as IKeyName)
{
device = shades;
}
/// <summary>
/// Registers actions for handling shade control operations.
/// Includes shade open, close, stop, and full status reporting.
/// </summary>
protected override void RegisterActions()
{
base.RegisterActions();
AddAction("/fullStatus", (id, content) => SendFullStatus());
AddAction("/fullStatus", (id, content) => SendFullStatus(id));
AddAction("/shadeUp", (id, content) =>
{
@@ -72,7 +86,7 @@ namespace PepperDash.Essentials.AppServer.Messengers
}
private void SendFullStatus()
private void SendFullStatus(string id = null)
{
var state = new ShadeBaseStateMessage();
@@ -82,18 +96,30 @@ namespace PepperDash.Essentials.AppServer.Messengers
state.IsClosed = feedbackDevice.ShadeIsClosedFeedback.BoolValue;
}
PostStatusMessage(state);
PostStatusMessage(state, id);
}
}
/// <summary>
/// Represents a shade state message containing shade status and control information.
/// </summary>
public class ShadeBaseStateMessage : DeviceStateMessageBase
{
/// <summary>
/// Gets or sets the label for the middle button control.
/// </summary>
[JsonProperty("middleButtonLabel", NullValueHandling = NullValueHandling.Ignore)]
public string MiddleButtonLabel { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the shade is open.
/// </summary>
[JsonProperty("isOpen", NullValueHandling = NullValueHandling.Ignore)]
public bool? IsOpen { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the shade is closed.
/// </summary>
[JsonProperty("isClosed", NullValueHandling = NullValueHandling.Ignore)]
public bool? IsClosed { get; set; }
}

View File

@@ -1,17 +1,28 @@
using Crestron.SimplSharp;
using System;
using System.Threading.Tasks;
using Crestron.SimplSharp;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using PepperDash.Core;
using PepperDash.Essentials.Core.Monitoring;
using System;
using System.Threading.Tasks;
namespace PepperDash.Essentials.AppServer.Messengers
{
/// <summary>
/// Provides messaging capabilities for system monitoring operations.
/// Handles system performance metrics, program status reporting, and monitoring data communication.
/// </summary>
public class SystemMonitorMessenger : MessengerBase
{
private readonly SystemMonitorController systemMonitor;
/// <summary>
/// Initializes a new instance of the <see cref="SystemMonitorMessenger"/> class.
/// </summary>
/// <param name="key">The unique identifier for this messenger instance.</param>
/// <param name="sysMon">The system monitor controller for monitoring operations.</param>
/// <param name="messagePath">The message path for system monitor messages.</param>
/// <exception cref="ArgumentNullException">Thrown when sysMon is null.</exception>
public SystemMonitorMessenger(string key, SystemMonitorController sysMon, string messagePath)
: base(key, messagePath, sysMon)
{
@@ -53,20 +64,20 @@ namespace PepperDash.Essentials.AppServer.Messengers
SendSystemMonitorStatusMessage();
}
private void SendFullStatusMessage()
private void SendFullStatusMessage(string id = null)
{
SendSystemMonitorStatusMessage();
SendSystemMonitorStatusMessage(id);
foreach (var p in systemMonitor.ProgramStatusFeedbackCollection)
{
PostStatusMessage(JToken.FromObject(p.Value.ProgramInfo));
PostStatusMessage(JToken.FromObject(p.Value.ProgramInfo), id);
}
}
private void SendSystemMonitorStatusMessage()
private void SendSystemMonitorStatusMessage(string id = null)
{
// This takes a while, launch a new thread
Task.Run(() => PostStatusMessage(JToken.FromObject(new SystemMonitorStateMessage
{
@@ -76,33 +87,58 @@ namespace PepperDash.Essentials.AppServer.Messengers
SnmpVersion = systemMonitor.SnmpVersionFeedback.StringValue,
BacnetVersion = systemMonitor.BaCnetAppVersionFeedback.StringValue,
ControllerVersion = systemMonitor.ControllerVersionFeedback.StringValue
})
}), id
));
}
/// <summary>
/// Registers actions for handling system monitor operations.
/// Includes full status reporting for system monitoring data.
/// </summary>
protected override void RegisterActions()
{
AddAction("/fullStatus", (id, content) => SendFullStatusMessage());
AddAction("/fullStatus", (id, content) => SendFullStatusMessage(id));
}
}
/// <summary>
/// Represents the system monitor state message containing system information and version details.
/// </summary>
public class SystemMonitorStateMessage
{
/// <summary>
/// Gets or sets the system time zone offset.
/// </summary>
[JsonProperty("timeZone", NullValueHandling = NullValueHandling.Ignore)]
public int TimeZone { get; set; }
/// <summary>
/// Gets or sets the system time zone name.
/// </summary>
[JsonProperty("timeZone", NullValueHandling = NullValueHandling.Ignore)]
public string TimeZoneName { get; set; }
/// <summary>
/// Gets or sets the IO controller version information.
/// </summary>
[JsonProperty("timeZone", NullValueHandling = NullValueHandling.Ignore)]
public string IoControllerVersion { get; set; }
/// <summary>
/// Gets or sets the SNMP version information.
/// </summary>
[JsonProperty("timeZone", NullValueHandling = NullValueHandling.Ignore)]
public string SnmpVersion { get; set; }
/// <summary>
/// Gets or sets the BACnet version information.
/// </summary>
[JsonProperty("timeZone", NullValueHandling = NullValueHandling.Ignore)]
public string BacnetVersion { get; set; }
/// <summary>
/// Gets or sets the controller version information.
/// </summary>
[JsonProperty("timeZone", NullValueHandling = NullValueHandling.Ignore)]
public string ControllerVersion { get; set; }
}

View File

@@ -5,10 +5,27 @@ using PepperDash.Essentials.Devices.Common.Displays;
namespace PepperDash.Essentials.AppServer.Messengers
{
/// <summary>
/// Provides messaging capabilities for two-way display control operations.
/// Handles display input changes, power state, and cooling/warming status.
/// This class extends the MessengerBase to facilitate communication between the display and the mobile control interface.
/// </summary>
public class TwoWayDisplayBaseMessenger : MessengerBase
{
private readonly TwoWayDisplayBase _display;
/// <summary>
/// Initializes a new instance of the <see cref="TwoWayDisplayBaseMessenger"/> class.
/// This constructor sets up the messenger with a key, message path, and the display instance
/// that it will control and monitor.
/// The display instance should implement the TwoWayDisplayBase interface to provide the necessary feedback and
/// control methods for the display device.
/// The messenger will listen for changes in the display's state and send updates to the mobile control interface.
/// It also allows for sending commands to the display, such as changing inputs or toggling power states.
/// </summary>
/// <param name="key">The unique identifier for this messenger instance.</param>
/// <param name="messagePath">The message path for display control messages.</param>
/// <param name="display">The display instance to control and monitor.</param>
public TwoWayDisplayBaseMessenger(string key, string messagePath, TwoWayDisplayBase display)
: base(key, messagePath, display)
{
@@ -17,7 +34,8 @@ namespace PepperDash.Essentials.AppServer.Messengers
#region Overrides of MessengerBase
public void SendFullStatus()
private void SendFullStatus(string id = null)
{
var messageObj = new TwoWayDisplayBaseStateMessage
{
@@ -25,16 +43,20 @@ namespace PepperDash.Essentials.AppServer.Messengers
CurrentInput = _display.CurrentInputFeedback.StringValue
};
PostStatusMessage(messageObj);
PostStatusMessage(messageObj, id);
}
/// <summary>
/// Registers actions for handling two-way display operations.
/// This includes sending full status updates and handling input changes, cooling, and warming feedback.
/// The actions are registered to respond to specific commands sent from the mobile control interface.
/// </summary>
protected override void RegisterActions()
{
base.RegisterActions();
AddAction("/fullStatus", (id, content) => SendFullStatus());
AddAction("/fullStatus", (id, content) => SendFullStatus(id));
//_display.PowerIsOnFeedback.OutputChange += PowerIsOnFeedbackOnOutputChange;
_display.CurrentInputFeedback.OutputChange += CurrentInputFeedbackOnOutputChange;
_display.IsCoolingDownFeedback.OutputChange += IsCoolingFeedbackOnOutputChange;
_display.IsWarmingUpFeedback.OutputChange += IsWarmingFeedbackOnOutputChange;
@@ -49,16 +71,6 @@ namespace PepperDash.Essentials.AppServer.Messengers
);
}
//private void PowerIsOnFeedbackOnOutputChange(object sender, FeedbackEventArgs feedbackEventArgs)
//{
// PostStatusMessage(JToken.FromObject(new
// {
// powerState = feedbackEventArgs.BoolValue
// })
// );
//}
private void IsWarmingFeedbackOnOutputChange(object sender, FeedbackEventArgs feedbackEventArgs)
{
PostStatusMessage(JToken.FromObject(new

View File

@@ -1,4 +1,8 @@
using Crestron.SimplSharp;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Crestron.SimplSharp;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using PepperDash.Core;
@@ -9,9 +13,6 @@ using PepperDash.Essentials.Devices.Common.Cameras;
using PepperDash.Essentials.Devices.Common.Codec;
using PepperDash.Essentials.Devices.Common.VideoCodec;
using PepperDash.Essentials.Devices.Common.VideoCodec.Interfaces;
using System;
using System.Collections.Generic;
using System.Linq;
namespace PepperDash.Essentials.AppServer.Messengers
{
@@ -21,18 +22,18 @@ namespace PepperDash.Essentials.AppServer.Messengers
public class VideoCodecBaseMessenger : MessengerBase
{
/// <summary>
///
/// The video codec device being managed by this messenger.
/// </summary>
protected VideoCodecBase Codec { get; private set; }
/// <summary>
///
/// Initializes a new instance of the <see cref="VideoCodecBaseMessenger"/> class.
/// </summary>
/// <param name="key"></param>
/// <param name="codec"></param>
/// <param name="messagePath"></param>
/// <param name="key">The unique identifier for the messenger.</param>
/// <param name="codec">The video codec device to be managed.</param>
/// <param name="messagePath">The message path for communication.</param>
/// <exception cref="ArgumentNullException">Thrown when codec is null</exception>
public VideoCodecBaseMessenger(string key, VideoCodecBase codec, string messagePath)
: base(key, messagePath, codec)
{
@@ -70,11 +71,6 @@ namespace PepperDash.Essentials.AppServer.Messengers
PostEventMessage(eventMsg);
}
/// <summary>
///
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void CallHistory_RecentCallsListHasChanged(object sender, EventArgs e)
{
try
@@ -98,10 +94,10 @@ namespace PepperDash.Essentials.AppServer.Messengers
}
/// <summary>
///
/// Handles the event when a directory result is returned from the codec.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The directory event arguments.</param>
protected virtual void DirCodec_DirectoryResultReturned(object sender, DirectoryEventArgs e)
{
if (Codec is IHasDirectory)
@@ -109,8 +105,9 @@ namespace PepperDash.Essentials.AppServer.Messengers
}
/// <summary>
/// Posts the current directory
/// Sends the current directory structure to the mobile control interface.
/// </summary>
/// <param name="directory">The directory structure to send.</param>
protected void SendDirectory(CodecDirectory directory)
{
try
@@ -134,11 +131,6 @@ namespace PepperDash.Essentials.AppServer.Messengers
}
}
/// <summary>
///
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Codec_IsReadyChange(object sender, EventArgs e)
{
try
@@ -151,16 +143,14 @@ namespace PepperDash.Essentials.AppServer.Messengers
PostStatusMessage(state);
SendFullStatus();
} catch (Exception ex)
}
catch (Exception ex)
{
this.LogError(ex, "Error sending codec ready status");
}
}
/// <summary>
/// Called from base's RegisterWithAppServer method
/// </summary>
/// <param name="appServerController"></param>
/// <inheritdoc />
protected override void RegisterActions()
{
try
@@ -169,7 +159,7 @@ namespace PepperDash.Essentials.AppServer.Messengers
AddAction("/isReady", (id, content) => SendIsReady());
AddAction("/fullStatus", (id, content) => SendFullStatus());
AddAction("/fullStatus", (id, content) => SendFullStatus(id));
AddAction("/dial", (id, content) =>
{
@@ -369,7 +359,8 @@ namespace PepperDash.Essentials.AppServer.Messengers
};
PostStatusMessage(state);
} catch (Exception ex)
}
catch (Exception ex)
{
this.LogError(ex, "Error posting sharing source");
}
@@ -385,7 +376,8 @@ namespace PepperDash.Essentials.AppServer.Messengers
};
PostStatusMessage(state);
} catch (Exception ex)
}
catch (Exception ex)
{
this.LogError(ex, "Error posting sharing content");
}
@@ -435,15 +427,13 @@ namespace PepperDash.Essentials.AppServer.Messengers
{
MapCameraActions();
PostSelectedCamera();
} catch(Exception ex)
}
catch (Exception ex)
{
this.LogError(ex, "Exception handling camera selected event");
}
}
/// <summary>
/// Maps the camera control actions to the current selected camera on the codec
/// </summary>
private void MapCameraActions()
{
if (Codec is IHasCameras cameraCodec && cameraCodec.SelectedCamera != null)
@@ -599,7 +589,7 @@ namespace PepperDash.Essentials.AppServer.Messengers
{
try
{
var codec = (Codec as IHasCallHistory);
var codec = Codec as IHasCallHistory;
if (codec != null)
{
@@ -621,20 +611,11 @@ namespace PepperDash.Essentials.AppServer.Messengers
}
}
/// <summary>
/// Helper to grab a call with string ID
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
private CodecActiveCallItem GetCallWithId(string id)
{
return Codec.ActiveCalls.FirstOrDefault(c => c.Id == id);
}
/// <summary>
///
/// </summary>
/// <param name="id"></param>
private void GetDirectory(string id)
{
if (!(Codec is IHasDirectory dirCodec))
@@ -644,10 +625,7 @@ namespace PepperDash.Essentials.AppServer.Messengers
dirCodec.GetDirectoryFolderContents(id);
}
/// <summary>
///
/// </summary>
private void GetDirectoryRoot()
private void GetDirectoryRoot(string id = null)
{
try
{
@@ -675,9 +653,6 @@ namespace PepperDash.Essentials.AppServer.Messengers
}
}
/// <summary>
/// Requests the parent folder contents
/// </summary>
private void GetPreviousDirectory()
{
if (!(Codec is IHasDirectory dirCodec))
@@ -688,17 +663,11 @@ namespace PepperDash.Essentials.AppServer.Messengers
dirCodec.GetDirectoryParentFolderContents();
}
/// <summary>
/// Handler for codec changes
/// </summary>
private void Codec_CallStatusChange(object sender, CodecCallStatusItemChangeEventArgs e)
{
SendFullStatus();
}
/// <summary>
///
/// </summary>
private void SendIsReady()
{
try
@@ -719,9 +688,9 @@ namespace PepperDash.Essentials.AppServer.Messengers
}
/// <summary>
/// Helper method to build call status for vtc
/// Gets the current status of the video codec.
/// </summary>
/// <returns></returns>
/// <returns> The current status of the video codec.</returns>
protected VideoCodecBaseStateMessage GetStatus()
{
try
@@ -780,14 +749,18 @@ namespace PepperDash.Essentials.AppServer.Messengers
}
}
protected virtual void SendFullStatus()
/// <summary>
/// Sends the full status of the codec.
/// </summary>
/// <param name="id">The unique identifier for the status message.</param>
protected virtual void SendFullStatus(string id = null)
{
if (!Codec.IsReady)
{
return;
}
CrestronInvoke.BeginInvoke((o) => PostStatusMessage(GetStatus()));
Task.Run(() => PostStatusMessage(GetStatus(), id));
}
private void PostReceivingContent(bool receivingContent)
@@ -800,7 +773,8 @@ namespace PepperDash.Essentials.AppServer.Messengers
};
PostStatusMessage(state);
} catch(Exception ex)
}
catch (Exception ex)
{
this.LogError(ex, "Error posting receiving content");
}
@@ -824,9 +798,6 @@ namespace PepperDash.Essentials.AppServer.Messengers
}
}
/// <summary>
///
/// </summary>
private void PostCameraMode()
{
try
@@ -928,164 +899,324 @@ namespace PepperDash.Essentials.AppServer.Messengers
public class VideoCodecBaseStateMessage : DeviceStateMessageBase
{
/// <summary>
/// The list of active calls on the codec.
/// </summary>
[JsonProperty("calls", NullValueHandling = NullValueHandling.Ignore)]
public List<CodecActiveCallItem> Calls { get; set; }
/// <summary>
/// The current mode of the camera.
/// </summary>
[JsonProperty("cameraMode", NullValueHandling = NullValueHandling.Ignore)]
public string CameraMode { get; set; }
/// <summary>
/// Indicates whether the camera self-view is enabled.
/// </summary>
[JsonProperty("cameraSelfView", NullValueHandling = NullValueHandling.Ignore)]
public bool? CameraSelfViewIsOn { get; set; }
/// <summary>
/// Gets the current status of the cameras.
/// </summary>
[JsonProperty("cameras", NullValueHandling = NullValueHandling.Ignore)]
public CameraStatus Cameras { get; set; }
/// <summary>
/// Indicates whether the camera supports auto mode.
/// </summary>
[JsonProperty("cameraSupportsAutoMode", NullValueHandling = NullValueHandling.Ignore)]
public bool? CameraSupportsAutoMode { get; set; }
/// <summary>
/// Indicates whether the camera supports off mode.
/// </summary>
[JsonProperty("cameraSupportsOffMode", NullValueHandling = NullValueHandling.Ignore)]
public bool? CameraSupportsOffMode { get; set; }
/// <summary>
/// The current dial string for the codec.
/// </summary>
[JsonProperty("currentDialString", NullValueHandling = NullValueHandling.Ignore)]
public string CurrentDialString { get; set; }
/// <summary>
/// Gets the current directory for the codec.
/// </summary>
[JsonProperty("currentDirectory", NullValueHandling = NullValueHandling.Ignore)]
public CodecDirectory CurrentDirectory { get; set; }
/// <summary>
/// Gets the selected folder name in the directory.
/// </summary>
[JsonProperty("directorySelectedFolderName", NullValueHandling = NullValueHandling.Ignore)]
public string DirectorySelectedFolderName { get; set; }
/// <summary>
/// Indicates whether the codec has active camera streams.
/// </summary>
[JsonProperty("hasCameras", NullValueHandling = NullValueHandling.Ignore)]
public bool? HasCameras { get; set; }
/// <summary>
/// Indicates whether the codec has a directory.
/// </summary>
[JsonProperty("hasDirectory", NullValueHandling = NullValueHandling.Ignore)]
public bool? HasDirectory { get; set; }
/// <summary>
/// Indicates whether the codec supports directory search functionality.
/// </summary>
[JsonProperty("hasDirectorySearch", NullValueHandling = NullValueHandling.Ignore)]
public bool? HasDirectorySearch { get; set; }
/// <summary>
/// Indicates whether the codec has presets.
/// </summary>
[JsonProperty("hasPresets", NullValueHandling = NullValueHandling.Ignore)]
public bool? HasPresets { get; set; }
/// <summary>
/// Indicates whether the codec has recent calls.
/// </summary>
[JsonProperty("hasRecents", NullValueHandling = NullValueHandling.Ignore)]
public bool? HasRecents { get; set; }
/// <summary>
/// Indicates whether the initial phonebook sync is complete.
/// </summary>
[JsonProperty("initialPhonebookSyncComplete", NullValueHandling = NullValueHandling.Ignore)]
public bool? InitialPhonebookSyncComplete { get; set; }
/// <summary>
/// Gets the information about the video codec.
/// </summary>
[JsonProperty("info", NullValueHandling = NullValueHandling.Ignore)]
public VideoCodecInfo Info { get; set; }
/// <summary>
/// Indicates whether the codec is currently in a call.
/// </summary>
[JsonProperty("isInCall", NullValueHandling = NullValueHandling.Ignore)]
public bool? IsInCall { get; set; }
/// <summary>
/// Indicates whether the codec is ready.
/// </summary>
[JsonProperty("isReady", NullValueHandling = NullValueHandling.Ignore)]
public bool? IsReady { get; set; }
/// <summary>
/// Indicates whether the codec is a Zoom Room.
/// </summary>
[JsonProperty("isZoomRoom", NullValueHandling = NullValueHandling.Ignore)]
public bool? IsZoomRoom { get; set; }
/// <summary>
/// Gets the meeting information for the codec, if available.
/// </summary>
[JsonProperty("meetingInfo", NullValueHandling = NullValueHandling.Ignore)]
public MeetingInfo MeetingInfo { get; set; }
/// <summary>
/// Gets the list of presets for the codec.
/// </summary>
[JsonProperty("presets", NullValueHandling = NullValueHandling.Ignore)]
public List<CodecRoomPreset> Presets { get; set; }
/// <summary>
/// Indicates whether the privacy mode is currently enabled.
/// </summary>
[JsonProperty("privacyModeIsOn", NullValueHandling = NullValueHandling.Ignore)]
public bool? PrivacyModeIsOn { get; set; }
/// <summary>
/// Indicates whether the codec is currently receiving content.
/// </summary>
[JsonProperty("receivingContent", NullValueHandling = NullValueHandling.Ignore)]
public bool? ReceivingContent { get; set; }
/// <summary>
/// Gets the list of recent calls for the codec, if available.
/// </summary>
[JsonProperty("recentCalls", NullValueHandling = NullValueHandling.Ignore)]
public List<CodecCallHistory.CallHistoryEntry> RecentCalls { get; set; }
/// <summary>
/// Indicates whether the codec is currently sharing content.
/// </summary>
[JsonProperty("sharingContentIsOn", NullValueHandling = NullValueHandling.Ignore)]
public bool? SharingContentIsOn { get; set; }
/// <summary>
/// Gets the source of the shared content, if available.
/// </summary>
[JsonProperty("sharingSource", NullValueHandling = NullValueHandling.Ignore)]
public string SharingSource { get; set; }
/// <summary>
/// Indicates whether the cameras should be shown when not in a call.
/// </summary>
[JsonProperty("showCamerasWhenNotInCall", NullValueHandling = NullValueHandling.Ignore)]
public bool? ShowCamerasWhenNotInCall { get; set; }
/// <summary>
/// Indicates whether the self-view is shown by default.
/// </summary>
[JsonProperty("showSelfViewByDefault", NullValueHandling = NullValueHandling.Ignore)]
public bool? ShowSelfViewByDefault { get; set; }
/// <summary>
/// Indicates whether the codec is currently in standby mode.
/// </summary>
[JsonProperty("standbyIsOn", NullValueHandling = NullValueHandling.Ignore)]
public bool? StandbyIsOn { get; set; }
/// <summary>
/// Indicates whether the codec supports ad-hoc meetings.
/// </summary>
[JsonProperty("supportsAdHocMeeting", NullValueHandling = NullValueHandling.Ignore)]
public bool? SupportsAdHocMeeting { get; set; }
}
/// <summary>
/// Represents the status of the camera.
/// </summary>
public class CameraStatus
{
/// <summary>
/// Indicates whether the camera manual control is supported.
/// </summary>
[JsonProperty("cameraManualSupported", NullValueHandling = NullValueHandling.Ignore)]
public bool? CameraManualIsSupported { get; set; }
/// <summary>
/// Indicates whether the camera auto control is supported.
/// </summary>
[JsonProperty("cameraAutoSupported", NullValueHandling = NullValueHandling.Ignore)]
public bool? CameraAutoIsSupported { get; set; }
/// <summary>
/// Indicates whether the camera off control is supported.
/// </summary>
[JsonProperty("cameraOffSupported", NullValueHandling = NullValueHandling.Ignore)]
public bool? CameraOffIsSupported { get; set; }
/// <summary>
/// Indicates the current mode of the camera.
/// </summary>
[JsonProperty("cameraMode", NullValueHandling = NullValueHandling.Ignore)]
public string CameraMode { get; set; }
/// <summary>
/// Represents the list of cameras available.
/// </summary>
[JsonProperty("cameraList", NullValueHandling = NullValueHandling.Ignore)]
public List<CameraBase> Cameras { get; set; }
/// <summary>
/// Represents the currently selected camera.
/// </summary>
[JsonProperty("selectedCamera", NullValueHandling = NullValueHandling.Ignore)]
public Camera SelectedCamera { get; set; }
public Camera SelectedCamera { get; set; }
}
/// <summary>
/// Represents a camera in the video codec system.
/// </summary>
public class Camera
{
/// <summary>
/// The unique identifier for the camera.
/// </summary>
[JsonProperty("key", NullValueHandling = NullValueHandling.Ignore)]
public string Key { get; set; }
/// <summary>
/// The name of the camera.
/// </summary>
[JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)]
public string Name { get; set; }
/// <summary>
/// Indicates whether the camera is a far-end camera.
/// </summary>
[JsonProperty("isFarEnd", NullValueHandling = NullValueHandling.Ignore)]
public bool? IsFarEnd { get; set; }
/// <summary>
/// Represents the capabilities of the camera.
/// </summary>
[JsonProperty("capabilities", NullValueHandling = NullValueHandling.Ignore)]
public CameraCapabilities Capabilities { get; set; }
}
/// <summary>
/// Represents the capabilities of the camera.
/// </summary>
public class CameraCapabilities
{
/// <summary>
/// Indicates whether the camera can pan.
/// </summary>
[JsonProperty("canPan", NullValueHandling = NullValueHandling.Ignore)]
public bool? CanPan { get; set; }
/// <summary>
/// Indicates whether the camera can tilt.
/// </summary>
[JsonProperty("canTilt", NullValueHandling = NullValueHandling.Ignore)]
public bool? CanTilt { get; set; }
/// <summary>
/// Indicates whether the camera can zoom.
/// </summary>
[JsonProperty("canZoom", NullValueHandling = NullValueHandling.Ignore)]
public bool? CanZoom { get; set; }
/// <summary>
/// Indicates whether the camera can focus.
/// </summary>
[JsonProperty("canFocus", NullValueHandling = NullValueHandling.Ignore)]
public bool? CanFocus { get; set; }
}
/// <summary>
/// Represents a video codec event message.
/// </summary>
public class VideoCodecBaseEventMessage : DeviceEventMessageBase
{
}
/// <summary>
/// Represents a password prompt event message.
/// </summary>
public class PasswordPromptEventMessage : VideoCodecBaseEventMessage
{
/// <summary>
/// The message to display in the password prompt.
/// </summary>
[JsonProperty("message", NullValueHandling = NullValueHandling.Ignore)]
public string Message { get; set; }
/// <summary>
/// Indicates whether the last password attempt was incorrect.
/// </summary>
[JsonProperty("lastAttemptWasIncorrect", NullValueHandling = NullValueHandling.Ignore)]
public bool LastAttemptWasIncorrect { get; set; }
/// <summary>
/// Indicates whether the login attempt failed.
/// </summary>
[JsonProperty("loginAttemptFailed", NullValueHandling = NullValueHandling.Ignore)]
public bool LoginAttemptFailed { get; set; }
/// <summary>
/// Indicates whether the login attempt was cancelled.
/// </summary>
[JsonProperty("loginAttemptCancelled", NullValueHandling = NullValueHandling.Ignore)]
public bool LoginAttemptCancelled { get; set; }
}

View File

@@ -4,14 +4,26 @@ using PepperDash.Essentials.Core.DeviceTypeInterfaces;
namespace PepperDash.Essentials.AppServer.Messengers
{
/// <summary>
/// Represents a mobile control message that can be sent between clients and the system
/// </summary>
public class MobileControlMessage : IMobileControlMessage
{
/// <summary>
/// Gets or sets the message type/path for routing
/// </summary>
[JsonProperty("type")]
public string Type { get; set; }
/// <summary>
/// Gets or sets the client ID this message is intended for (null for broadcast)
/// </summary>
[JsonProperty("clientId")]
public string ClientId { get; set; }
/// <summary>
/// Gets or sets the JSON content of the message
/// </summary>
[JsonProperty("content")]
public JToken Content { get; set; }
}

View File

@@ -2,8 +2,15 @@
namespace PepperDash.Essentials.AppServer
{
/// <summary>
/// Generic container for simple mobile control message content with a single value
/// </summary>
/// <typeparam name="T">The type of the value contained in the message</typeparam>
public class MobileControlSimpleContent<T>
{
/// <summary>
/// Gets or sets the value of the message content
/// </summary>
[JsonProperty("value", NullValueHandling = NullValueHandling.Ignore)]
public T Value { get; set; }
}

View File

@@ -0,0 +1,74 @@
using System;
using System.Threading;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using PepperDash.Core;
using PepperDash.Core.Logging;
using PepperDash.Essentials.AppServer.Messengers;
using PepperDash.Essentials.Core.Queues;
using PepperDash.Essentials.WebSocketServer;
using Serilog.Events;
namespace PepperDash.Essentials
{
public class MessageToClients : IQueueMessage
{
private readonly MobileControlWebsocketServer _server;
private readonly object msgToSend;
public MessageToClients(object msg, MobileControlWebsocketServer server)
{
_server = server;
msgToSend = msg;
}
public MessageToClients(DeviceStateMessageBase msg, MobileControlWebsocketServer server)
{
_server = server;
msgToSend = msg;
}
#region Implementation of IQueueMessage
public void Dispatch()
{
try
{
if (_server == null)
{
Debug.LogMessage(LogEventLevel.Warning, "Cannot send message. Server is null");
return;
}
var message = JsonConvert.SerializeObject(msgToSend, Formatting.None,
new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore, Converters = { new IsoDateTimeConverter() } });
var clientSpecificMessage = msgToSend as MobileControlMessage;
if (clientSpecificMessage.ClientId != null)
{
var clientId = clientSpecificMessage.ClientId;
_server.LogVerbose("Message TX To client {clientId} Message: {message}", clientId, message);
_server.SendMessageToClient(clientId, message);
return;
}
_server.SendMessageToAllClients(message);
_server.LogVerbose("Message TX To all clients: {message}", message);
}
catch (ThreadAbortException)
{
//Swallowing this exception, as it occurs on shutdown and there's no need to print out a scary stack trace
}
catch (Exception ex)
{
Debug.LogMessage(ex, "Caught an exception in the Transmit Processor");
}
}
#endregion
}
}

View File

@@ -1,4 +1,10 @@
using Crestron.SimplSharp;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Crestron.SimplSharp;
using Crestron.SimplSharp.CrestronIO;
using Crestron.SimplSharp.Net.Http;
using Crestron.SimplSharp.WebScripting;
@@ -30,12 +36,6 @@ using PepperDash.Essentials.RoomBridges;
using PepperDash.Essentials.Services;
using PepperDash.Essentials.WebApiHandlers;
using PepperDash.Essentials.WebSocketServer;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using WebSocketSharp;
namespace PepperDash.Essentials
@@ -570,7 +570,7 @@ namespace PepperDash.Essentials
{
this.LogVerbose(
"Adding ISetTopBoxControlMessenger for {deviceKey}"
);
);
var messenger = new ISetTopBoxControlsMessenger(
$"{device.Key}-stb-{Key}",
@@ -587,7 +587,7 @@ namespace PepperDash.Essentials
{
this.LogVerbose(
"Adding IChannelMessenger for {deviceKey}", device.Key
);
);
var messenger = new IChannelMessenger(
$"{device.Key}-channel-{Key}",
@@ -602,7 +602,7 @@ namespace PepperDash.Essentials
if (device is IColor colorDevice)
{
this.LogVerbose("Adding IColorMessenger for {deviceKey}", device.Key);
this.LogVerbose("Adding IColorMessenger for {deviceKey}", device.Key);
var messenger = new IColorMessenger(
$"{device.Key}-color-{Key}",
@@ -617,7 +617,7 @@ namespace PepperDash.Essentials
if (device is IDPad dPadDevice)
{
this.LogVerbose("Adding IDPadMessenger for {deviceKey}", device.Key);
this.LogVerbose("Adding IDPadMessenger for {deviceKey}", device.Key);
var messenger = new IDPadMessenger(
$"{device.Key}-dPad-{Key}",
@@ -632,7 +632,7 @@ namespace PepperDash.Essentials
if (device is INumericKeypad nkDevice)
{
this.LogVerbose("Adding INumericKeyapdMessenger for {deviceKey}", device.Key);
this.LogVerbose("Adding INumericKeyapdMessenger for {deviceKey}", device.Key);
var messenger = new INumericKeypadMessenger(
$"{device.Key}-numericKeypad-{Key}",
@@ -647,7 +647,7 @@ namespace PepperDash.Essentials
if (device is IHasPowerControl pcDevice)
{
this.LogVerbose("Adding IHasPowerControlMessenger for {deviceKey}", device.Key);
this.LogVerbose("Adding IHasPowerControlMessenger for {deviceKey}", device.Key);
var messenger = new IHasPowerMessenger(
$"{device.Key}-powerControl-{Key}",
@@ -681,7 +681,7 @@ namespace PepperDash.Essentials
{
this.LogVerbose(
"Adding ITransportMessenger for {deviceKey}", device.Key
);
);
var messenger = new ITransportMessenger(
$"{device.Key}-transport-{Key}",
@@ -1619,12 +1619,12 @@ Mobile Control Direct Server Information:
Tokens Defined: {0}
Clients Connected: {1}
",
_directServer.UiClients.Count,
_directServer.UiClientContexts.Count,
_directServer.ConnectedUiClientsCount
);
var clientNo = 1;
foreach (var clientContext in _directServer.UiClients)
foreach (var clientContext in _directServer.UiClientContexts)
{
var isAlive = false;
var duration = "Not Connected";
@@ -2238,7 +2238,7 @@ Mobile Control Direct Server Infromation:
{
this.LogInformation("-- Warning: Incoming message has no registered handler {type}", message.Type);
break;
}
}
foreach (var handler in handlers)
{

View File

@@ -1,13 +1,10 @@
using Newtonsoft.Json;
using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using PepperDash.Core;
using PepperDash.Core.Logging;
using PepperDash.Essentials.AppServer.Messengers;
using PepperDash.Essentials.Core.Queues;
using PepperDash.Essentials.WebSocketServer;
using Serilog.Events;
using System;
using System.Threading;
using WebSocketSharp;
namespace PepperDash.Essentials
@@ -65,66 +62,4 @@ namespace PepperDash.Essentials
#endregion
}
public class MessageToClients : IQueueMessage
{
private readonly MobileControlWebsocketServer _server;
private readonly object msgToSend;
public MessageToClients(object msg, MobileControlWebsocketServer server)
{
_server = server;
msgToSend = msg;
}
public MessageToClients(DeviceStateMessageBase msg, MobileControlWebsocketServer server)
{
_server = server;
msgToSend = msg;
}
#region Implementation of IQueueMessage
public void Dispatch()
{
try
{
if (_server == null)
{
Debug.LogMessage(LogEventLevel.Warning, "Cannot send message. Server is null");
return;
}
var message = JsonConvert.SerializeObject(msgToSend, Formatting.None,
new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore, Converters = { new IsoDateTimeConverter() } });
var clientSpecificMessage = msgToSend as MobileControlMessage;
if (clientSpecificMessage.ClientId != null)
{
var clientId = clientSpecificMessage.ClientId;
_server.LogVerbose("Message TX To client {clientId} Message: {message}", clientId, message);
_server.SendMessageToClient(clientId, message);
return;
}
_server.SendMessageToAllClients(message);
_server.LogVerbose("Message TX To all clients: {message}", message);
}
catch (ThreadAbortException)
{
//Swallowing this exception, as it occurs on shutdown and there's no need to print out a scary stack trace
}
catch (Exception ex)
{
Debug.LogMessage(ex, "Caught an exception in the Transmit Processor");
}
}
#endregion
}
}

View File

@@ -1,12 +1,12 @@
using Crestron.SimplSharp.WebScripting;
using System;
using System.Collections.Generic;
using System.Linq;
using Crestron.SimplSharp.WebScripting;
using Newtonsoft.Json;
using PepperDash.Core;
using PepperDash.Core.Web.RequestHandlers;
using PepperDash.Essentials.Core.Config;
using PepperDash.Essentials.WebSocketServer;
using System;
using System.Collections.Generic;
using System.Linq;
namespace PepperDash.Essentials.WebApiHandlers
{
@@ -99,13 +99,13 @@ namespace PepperDash.Essentials.WebApiHandlers
public int ServerPort => directServer.Port;
[JsonProperty("tokensDefined")]
public int TokensDefined => directServer.UiClients.Count;
public int TokensDefined => directServer.UiClientContexts.Count;
[JsonProperty("clientsConnected")]
public int ClientsConnected => directServer.ConnectedUiClientsCount;
[JsonProperty("clients")]
public List<MobileControlDirectClient> Clients => directServer.UiClients.Select((c, i) => { return new MobileControlDirectClient(c, i, directServer.UserAppUrlPrefix); }).ToList();
public List<MobileControlDirectClient> Clients => directServer.UiClientContexts.Select((c, i) => { return new MobileControlDirectClient(c, i, directServer.UserAppUrlPrefix); }).ToList();
public MobileControlDirectServer(MobileControlWebsocketServer server)
{

View File

@@ -90,7 +90,7 @@ namespace PepperDash.Essentials.WebApiHandlers
if (!server.UiClients.TryGetValue(request.Token, out UiClientContext clientContext))
if (!server.UiClientContexts.TryGetValue(request.Token, out UiClientContext clientContext))
{
var response = new ClientResponse
{
@@ -131,7 +131,7 @@ namespace PepperDash.Essentials.WebApiHandlers
return;
}
server.UiClients.Remove(request.Token);
server.UiClientContexts.Remove(request.Token);
server.UpdateSecret();

View File

@@ -0,0 +1,39 @@
using System;
using Newtonsoft.Json;
namespace PepperDash.Essentials.WebSocketServer
{
/// <summary>
/// Represents the structure of the join response
/// </summary>
public class JoinResponse
{
[JsonProperty("clientId")]
public string ClientId { get; set; }
[JsonProperty("roomKey")]
public string RoomKey { get; set; }
[JsonProperty("systemUUid")]
public string SystemUuid { get; set; }
[JsonProperty("roomUUid")]
public string RoomUuid { get; set; }
[JsonProperty("config")]
public object Config { get; set; }
[JsonProperty("codeExpires")]
public DateTime CodeExpires { get; set; }
[JsonProperty("userCode")]
public string UserCode { get; set; }
[JsonProperty("userAppUrl")]
public string UserAppUrl { get; set; }
[JsonProperty("enableDebug")]
public bool EnableDebug { get; set; }
}
}

View File

@@ -0,0 +1,22 @@
using Independentsoft.Exchange;
namespace PepperDash.Essentials.WebSocketServer
{
/// <summary>
/// Represents a join token with the associated properties
/// </summary>
public class JoinToken
{
public string Code { get; set; }
public string RoomKey { get; set; }
public string Uuid { get; set; }
public string TouchpanelKey { get; set; } = "";
public string Token { get; set; } = null;
public string Id { get; set; }
}
}

View File

@@ -0,0 +1,21 @@
using System.Collections.Generic;
namespace PepperDash.Essentials.WebSocketServer
{
/// <summary>
/// Represents the data structure for the grant code and UiClient tokens to be stored in the secrets manager
/// </summary>
public class ServerTokenSecrets
{
public string GrantCode { get; set; }
public Dictionary<string, JoinToken> Tokens { get; set; }
public ServerTokenSecrets(string grantCode)
{
GrantCode = grantCode;
Tokens = new Dictionary<string, JoinToken>();
}
}
}

View File

@@ -0,0 +1,147 @@
using System;
using System.Text.RegularExpressions;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using PepperDash.Core;
using PepperDash.Essentials.AppServer.Messengers;
using PepperDash.Essentials.RoomBridges;
using Serilog.Events;
using WebSocketSharp;
using WebSocketSharp.Server;
using ErrorEventArgs = WebSocketSharp.ErrorEventArgs;
namespace PepperDash.Essentials.WebSocketServer
{
/// <summary>
/// Represents the behaviour to associate with a UiClient for WebSocket communication
/// </summary>
public class UiClient : WebSocketBehavior
{
public MobileControlSystemController Controller { get; set; }
public string RoomKey { get; set; }
public string ClientId
{
get; private set;
}
private DateTime _connectionTime;
public TimeSpan ConnectedDuration
{
get
{
if (Context.WebSocket.IsAlive)
{
return DateTime.Now - _connectionTime;
}
else
{
return new TimeSpan(0);
}
}
}
public UiClient(string clientId)
{
ClientId = clientId;
}
protected override void OnOpen()
{
base.OnOpen();
var url = Context.WebSocket.Url;
Debug.LogMessage(LogEventLevel.Verbose, "New WebSocket Connection from: {0}", null, url);
var match = Regex.Match(url.AbsoluteUri, "(?:ws|wss):\\/\\/.*(?:\\/mc\\/api\\/ui\\/join\\/)(.*)");
if (!match.Success)
{
_connectionTime = DateTime.Now;
return;
}
var clientId = ClientId;
if (Controller == null)
{
Debug.LogMessage(LogEventLevel.Verbose, "WebSocket UiClient Controller is null");
_connectionTime = DateTime.Now;
}
var clientJoinedMessage = new MobileControlMessage
{
Type = "/system/clientJoined",
Content = JToken.FromObject(new
{
clientId,
roomKey = RoomKey,
})
};
Controller.HandleClientMessage(JsonConvert.SerializeObject(clientJoinedMessage));
var bridge = Controller.GetRoomBridge(RoomKey);
if (bridge == null) return;
SendUserCodeToClient(bridge, clientId);
bridge.UserCodeChanged -= Bridge_UserCodeChanged;
bridge.UserCodeChanged += Bridge_UserCodeChanged;
// TODO: Future: Check token to see if there's already an open session using that token and reject/close the session
}
private void Bridge_UserCodeChanged(object sender, EventArgs e)
{
SendUserCodeToClient((MobileControlEssentialsRoomBridge)sender, ClientId);
}
private void SendUserCodeToClient(MobileControlBridgeBase bridge, string clientId)
{
var content = new
{
userCode = bridge.UserCode,
qrUrl = bridge.QrCodeUrl,
};
var message = new MobileControlMessage
{
Type = "/system/userCodeChanged",
ClientId = clientId,
Content = JToken.FromObject(content)
};
Controller.SendMessageObjectToDirectClient(message);
}
protected override void OnMessage(MessageEventArgs e)
{
base.OnMessage(e);
if (e.IsText && e.Data.Length > 0 && Controller != null)
{
// Forward the message to the controller to be put on the receive queue
Controller.HandleClientMessage(e.Data);
}
}
protected override void OnClose(CloseEventArgs e)
{
base.OnClose(e);
Debug.LogMessage(LogEventLevel.Verbose, "WebSocket UiClient Closing: {0} reason: {1}", null, e.Code, e.Reason);
}
protected override void OnError(ErrorEventArgs e)
{
base.OnError(e);
Debug.LogMessage(LogEventLevel.Verbose, "WebSocket UiClient Error: {exception} message: {message}", e.Exception, e.Message);
}
}
}

View File

@@ -0,0 +1,22 @@
namespace PepperDash.Essentials.WebSocketServer
{
/// <summary>
/// Represents an instance of a UiClient and the associated Token
/// </summary>
public class UiClientContext
{
public UiClient Client { get; private set; }
public JoinToken Token { get; private set; }
public UiClientContext(JoinToken token)
{
Token = token;
}
public void SetClient(UiClient client)
{
Client = client;
}
}
}

View File

@@ -0,0 +1,22 @@
using Newtonsoft.Json;
namespace PepperDash.Essentials.WebSocketServer
{
/// <summary>
/// Class to describe the server version info
/// </summary>
public class Version
{
[JsonProperty("serverVersion")]
public string ServerVersion { get; set; }
[JsonProperty("serverIsRunningOnProcessorHardware")]
public bool ServerIsRunningOnProcessorHardware { get; private set; }
public Version()
{
ServerIsRunningOnProcessorHardware = true;
}
}
}