mirror of
https://github.com/PepperDash/Essentials.git
synced 2026-02-11 02:35:00 +00:00
Compare commits
1 Commits
v2.8.2
...
appdebug-c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
185410b802 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -394,4 +394,3 @@ essentials-framework/Essentials Interfaces/PepperDash_Essentials_Interfaces/Pepp
|
||||
.vscode/settings.json
|
||||
_site/
|
||||
api/
|
||||
*.DS_Store
|
||||
|
||||
@@ -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,24 +93,22 @@ 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
|
||||
@@ -119,32 +117,31 @@ namespace PepperDash.Core
|
||||
/// </summary>
|
||||
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());
|
||||
//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.
|
||||
@@ -161,14 +158,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>
|
||||
@@ -178,15 +175,5 @@ 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -353,7 +353,7 @@ namespace PepperDash.Core
|
||||
return;
|
||||
}
|
||||
|
||||
if(Enum.TryParse<LogEventLevel>(levelString, out var levelEnum))
|
||||
if(Enum.TryParse<LogEventLevel>(levelString, true, out var levelEnum))
|
||||
{
|
||||
SetDebugLevel(levelEnum);
|
||||
return;
|
||||
|
||||
@@ -1,107 +0,0 @@
|
||||
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; }
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using Crestron.SimplSharpPro;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using PepperDash.Core;
|
||||
@@ -35,11 +33,11 @@ namespace PepperDash.Essentials.Core.DeviceTypeInterfaces
|
||||
|
||||
string SystemUuid { get; }
|
||||
|
||||
BoolFeedback ApiOnlineAndAuthorized { get; }
|
||||
BoolFeedback ApiOnlineAndAuthorized { get;}
|
||||
|
||||
void SendMessageObject(IMobileControlMessage o);
|
||||
|
||||
void AddAction<T>(T messenger, Action<string, string, JToken> action) where T : IMobileControlMessenger;
|
||||
void AddAction<T>(T messenger, Action<string, string, JToken> action) where T:IMobileControlMessenger;
|
||||
|
||||
void RemoveAction(string key);
|
||||
|
||||
@@ -47,14 +45,14 @@ namespace PepperDash.Essentials.Core.DeviceTypeInterfaces
|
||||
|
||||
bool CheckForDeviceMessenger(string key);
|
||||
|
||||
IMobileControlRoomMessenger GetRoomMessenger(string key);
|
||||
IMobileControlRoomMessenger GetRoomMessenger(string key);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Describes a mobile control messenger
|
||||
/// </summary>
|
||||
public interface IMobileControlMessenger : IKeyed
|
||||
public interface IMobileControlMessenger: IKeyed
|
||||
{
|
||||
IMobileControl AppServerController { get; }
|
||||
string MessagePath { get; }
|
||||
@@ -106,47 +104,16 @@ namespace PepperDash.Essentials.Core.DeviceTypeInterfaces
|
||||
|
||||
public interface IMobileControlAction
|
||||
{
|
||||
IMobileControlMessenger Messenger { get; }
|
||||
IMobileControlMessenger Messenger { get; }
|
||||
|
||||
Action<string, string, JToken> Action { get; }
|
||||
Action<string,string, JToken> Action { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Describes a MobileControl Touchpanel Controller
|
||||
/// </summary>
|
||||
public interface IMobileControlTouchpanelController : IKeyed
|
||||
{
|
||||
/// <summary>
|
||||
/// The default room key for the controller
|
||||
/// </summary>
|
||||
string DefaultRoomKey { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Sets the application URL for the controller
|
||||
/// </summary>
|
||||
/// <param name="url">The application URL</param>
|
||||
void SetAppUrl(string url);
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the controller uses a direct server connection
|
||||
/// </summary>
|
||||
bool UseDirectServer { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the controller is a Zoom Room controller
|
||||
/// </summary>
|
||||
bool ZoomRoomController { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Describes a MobileControl Crestron Touchpanel Controller
|
||||
/// This interface extends the IMobileControlTouchpanelController to include connected IP information
|
||||
/// </summary>
|
||||
public interface IMobileControlCrestronTouchpanelController : IMobileControlTouchpanelController
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a collection of connected IP information for the touchpanel controller
|
||||
/// </summary>
|
||||
ReadOnlyCollection<ConnectedIpInformation> ConnectedIps { get; }
|
||||
}
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -248,7 +248,7 @@ namespace PepperDash.Essentials.Core
|
||||
|
||||
foreach (var dev in Devices.Values.OfType<ICommunicationMonitor>())
|
||||
{
|
||||
CrestronConsole.ConsoleCommandResponse($"{dev}: {dev.CommunicationMonitor.Status}\r\n");
|
||||
CrestronConsole.ConsoleCommandResponse($"{dev}: {dev.CommunicationMonitor.Status}{Environment.NewLine}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,8 +20,7 @@ namespace PepperDash.Essentials.Core
|
||||
public event EventHandler Initialized;
|
||||
|
||||
private bool _isInitialized;
|
||||
public bool IsInitialized
|
||||
{
|
||||
public bool IsInitialized {
|
||||
get { return _isInitialized; }
|
||||
private set
|
||||
{
|
||||
@@ -81,8 +80,7 @@ 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() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,15 +9,10 @@ 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
|
||||
{
|
||||
@@ -60,7 +55,7 @@ namespace PepperDash.Essentials.Core
|
||||
{
|
||||
get
|
||||
{
|
||||
if (string.IsNullOrEmpty(ItemKey)) return ParentDeviceKey;
|
||||
if(string.IsNullOrEmpty(ItemKey)) return ParentDeviceKey;
|
||||
else
|
||||
{
|
||||
return DeviceManager.AllDevices.
|
||||
@@ -75,39 +70,13 @@ 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,
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -10,18 +10,7 @@ namespace PepperDash.Essentials.Core
|
||||
/// </summary>
|
||||
public enum eSourceListItemType
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a typical route.
|
||||
/// </summary>
|
||||
Route,
|
||||
/// <summary>
|
||||
/// Represents an off route.
|
||||
/// </summary>
|
||||
Off,
|
||||
/// <summary>
|
||||
/// Represents some other type of route
|
||||
/// </summary>
|
||||
Other,
|
||||
Route, Off, Other, SomethingAwesomerThanThese
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -29,9 +18,6 @@ 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; }
|
||||
|
||||
@@ -131,9 +117,6 @@ namespace PepperDash.Essentials.Core
|
||||
[JsonProperty("disableRoutedSharing")]
|
||||
public bool DisableRoutedSharing { get; set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
[JsonProperty("destinations")]
|
||||
public List<eSourceListItemDestinationTypes> Destinations { get; set; }
|
||||
/// <summary>
|
||||
@@ -166,56 +149,31 @@ 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; }
|
||||
}
|
||||
@@ -225,85 +183,15 @@ 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,
|
||||
}
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,13 +10,6 @@ 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;
|
||||
@@ -25,9 +18,6 @@ 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
|
||||
@@ -38,12 +28,6 @@ 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
|
||||
@@ -62,36 +46,12 @@ 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)
|
||||
{
|
||||
@@ -286,16 +246,8 @@ 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
|
||||
@@ -304,25 +256,10 @@ 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)
|
||||
@@ -333,12 +270,6 @@ 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;
|
||||
@@ -349,11 +280,6 @@ 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)
|
||||
@@ -366,22 +292,10 @@ 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));
|
||||
@@ -392,17 +306,6 @@ 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)
|
||||
@@ -451,32 +354,13 @@ 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");
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Crestron.SimplSharp;
|
||||
|
||||
using PepperDash.Core;
|
||||
|
||||
@@ -11,14 +17,6 @@ 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>
|
||||
@@ -49,9 +47,6 @@ 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; }
|
||||
}
|
||||
@@ -61,15 +56,9 @@ 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; }
|
||||
|
||||
@@ -91,21 +80,12 @@ 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; }
|
||||
|
||||
@@ -115,15 +95,9 @@ 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; }
|
||||
}
|
||||
@@ -133,15 +107,9 @@ 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; }
|
||||
}
|
||||
|
||||
@@ -28,20 +28,9 @@ 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; }
|
||||
|
||||
@@ -85,13 +74,6 @@ 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>
|
||||
@@ -100,9 +82,6 @@ namespace PepperDash.Essentials.Core
|
||||
[JsonIgnore]
|
||||
BoolFeedback IsActiveFeedback { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the entity is active.
|
||||
/// </summary>
|
||||
[JsonProperty("isActive")]
|
||||
bool IsActive { get; }
|
||||
|
||||
|
||||
@@ -7,14 +7,8 @@
|
||||
{
|
||||
}
|
||||
|
||||
/// <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>
|
||||
|
||||
@@ -227,7 +227,7 @@ namespace PepperDash.Essentials.Devices.Common.Cameras
|
||||
|
||||
SendCameraPresetNamesToApi(presetsCamera, joinMap, trilist);
|
||||
|
||||
for (int i = 0; i < joinMap.PresetRecallStart.JoinSpan; i++)
|
||||
for (int i = 0; i < joinMap.NumberOfPresets.JoinSpan; i++)
|
||||
{
|
||||
int tempNum = i;
|
||||
|
||||
|
||||
@@ -1,95 +0,0 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -18,13 +18,9 @@ namespace PepperDash.Essentials.Devices.Common.DSP
|
||||
|
||||
public Dictionary<string, DspControlPoint> SwitcherControlPoints { get; private set; }
|
||||
|
||||
public DspBase(string key, string name) :
|
||||
base(key, name)
|
||||
{
|
||||
|
||||
LevelControlPoints = new Dictionary<string, IBasicVolumeWithFeedback>();
|
||||
DialerControlPoints = new Dictionary<string, DspControlPoint>();
|
||||
SwitcherControlPoints = new Dictionary<string, DspControlPoint>();
|
||||
public DspBase(string key, string name) :
|
||||
base(key, name)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -12,44 +12,19 @@ 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);
|
||||
|
||||
/// <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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Static class for converting non-generic RoomPresets to generic CameraPresets.
|
||||
/// </summary>
|
||||
public static class RoomPresets
|
||||
{
|
||||
/// <summary>
|
||||
@@ -72,13 +47,6 @@ 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)
|
||||
{
|
||||
|
||||
@@ -2,69 +2,27 @@
|
||||
using Newtonsoft.Json.Linq;
|
||||
using PepperDash.Core;
|
||||
using PepperDash.Essentials.Core.DeviceInfo;
|
||||
using System.Timers;
|
||||
|
||||
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) =>
|
||||
{
|
||||
debounceTimer.Stop();
|
||||
debounceTimer.Start();
|
||||
PostStatusMessage(JToken.FromObject(new
|
||||
{
|
||||
deviceInfo = a.DeviceInfo
|
||||
}));
|
||||
};
|
||||
|
||||
AddAction("/fullStatus", (id, context) => PostStatusMessage(new DeviceInfoStateMessage
|
||||
@@ -76,12 +34,6 @@ namespace PepperDash.Essentials.AppServer.Messengers
|
||||
}
|
||||
}
|
||||
|
||||
/// <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
|
||||
{
|
||||
[JsonProperty("deviceInfo")]
|
||||
|
||||
@@ -8,42 +8,16 @@ 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());
|
||||
@@ -133,7 +107,6 @@ namespace PepperDash.Essentials.AppServer.Messengers
|
||||
|
||||
var message = new IEssentialsRoomCombinerStateMessage
|
||||
{
|
||||
DisableAutoMode = _roomCombiner.DisableAutoMode,
|
||||
IsInAutoMode = _roomCombiner.IsInAutoMode,
|
||||
CurrentScenario = _roomCombiner.CurrentScenario,
|
||||
Rooms = rooms,
|
||||
@@ -159,48 +132,20 @@ 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; }
|
||||
}
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using Crestron.SimplSharpPro;
|
||||
using Crestron.SimplSharpPro;
|
||||
using Crestron.SimplSharpPro.DeviceSupport;
|
||||
using Crestron.SimplSharpPro.UI;
|
||||
using Newtonsoft.Json;
|
||||
@@ -14,105 +10,64 @@ using PepperDash.Essentials.Core.Config;
|
||||
using PepperDash.Essentials.Core.DeviceInfo;
|
||||
using PepperDash.Essentials.Core.DeviceTypeInterfaces;
|
||||
using PepperDash.Essentials.Core.UI;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Feedback = PepperDash.Essentials.Core.Feedback;
|
||||
|
||||
namespace PepperDash.Essentials.Touchpanel
|
||||
{
|
||||
/// <summary>
|
||||
/// Mobile Control touchpanel controller that provides app control, Zoom integration,
|
||||
/// and mobile control functionality for Crestron touchpanels.
|
||||
/// </summary>
|
||||
public class MobileControlTouchpanelController : TouchpanelBase, IHasFeedback, ITswAppControl, ITswZoomControl, IDeviceInfoProvider, IMobileControlCrestronTouchpanelController, ITheme
|
||||
//public interface IMobileControlTouchpanelController
|
||||
//{
|
||||
// StringFeedback AppUrlFeedback { get; }
|
||||
// string DefaultRoomKey { get; }
|
||||
// string DeviceKey { get; }
|
||||
//}
|
||||
|
||||
|
||||
public class MobileControlTouchpanelController : TouchpanelBase, IHasFeedback, ITswAppControl, ITswZoomControl, IDeviceInfoProvider, IMobileControlTouchpanelController, ITheme
|
||||
{
|
||||
private readonly MobileControlTouchpanelProperties localConfig;
|
||||
private IMobileControlRoomMessenger _bridge;
|
||||
|
||||
private string _appUrl;
|
||||
|
||||
/// <summary>
|
||||
/// Gets feedback for the current application URL.
|
||||
/// </summary>
|
||||
public StringFeedback AppUrlFeedback { get; private set; }
|
||||
|
||||
private readonly StringFeedback QrCodeUrlFeedback;
|
||||
private readonly StringFeedback McServerUrlFeedback;
|
||||
private readonly StringFeedback UserCodeFeedback;
|
||||
|
||||
private readonly BoolFeedback _appOpenFeedback;
|
||||
|
||||
/// <summary>
|
||||
/// Gets feedback indicating whether an application is currently open on the touchpanel.
|
||||
/// </summary>
|
||||
public BoolFeedback AppOpenFeedback => _appOpenFeedback;
|
||||
|
||||
private readonly BoolFeedback _zoomIncomingCallFeedback;
|
||||
|
||||
/// <summary>
|
||||
/// Gets feedback indicating whether there is an incoming Zoom call.
|
||||
/// </summary>
|
||||
public BoolFeedback ZoomIncomingCallFeedback => _zoomIncomingCallFeedback;
|
||||
|
||||
private readonly BoolFeedback _zoomInCallFeedback;
|
||||
|
||||
/// <summary>
|
||||
/// Event that is raised when device information changes.
|
||||
/// </summary>
|
||||
public event DeviceInfoChangeHandler DeviceInfoChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Gets feedback indicating whether a Zoom call is currently active.
|
||||
/// </summary>
|
||||
public BoolFeedback ZoomInCallFeedback => _zoomInCallFeedback;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the collection of feedback objects for this touchpanel controller.
|
||||
/// </summary>
|
||||
|
||||
public FeedbackCollection<Feedback> Feedbacks { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the collection of Zoom-related feedback objects.
|
||||
/// </summary>
|
||||
public FeedbackCollection<Feedback> ZoomFeedbacks { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the default room key for this touchpanel controller.
|
||||
/// </summary>
|
||||
public string DefaultRoomKey => _config.DefaultRoomKey;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether to use direct server communication.
|
||||
/// </summary>
|
||||
public bool UseDirectServer => localConfig.UseDirectServer;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this touchpanel acts as a Zoom Room controller.
|
||||
/// </summary>
|
||||
public bool ZoomRoomController => localConfig.ZoomRoomController;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current theme for the touchpanel interface.
|
||||
/// </summary>
|
||||
public string Theme => localConfig.Theme;
|
||||
|
||||
/// <summary>
|
||||
/// Gets feedback for the current theme setting.
|
||||
/// </summary>
|
||||
public StringFeedback ThemeFeedback { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets device information including MAC address and IP address.
|
||||
/// </summary>
|
||||
public DeviceInfo DeviceInfo => new DeviceInfo();
|
||||
|
||||
public ReadOnlyCollection<ConnectedIpInformation> ConnectedIps => Panel.ConnectedIpList;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the MobileControlTouchpanelController class.
|
||||
/// </summary>
|
||||
/// <param name="key">The unique key identifier for this touchpanel controller.</param>
|
||||
/// <param name="name">The friendly name for this touchpanel controller.</param>
|
||||
/// <param name="panel">The touchpanel hardware device.</param>
|
||||
/// <param name="config">The configuration properties for this controller.</param>
|
||||
public MobileControlTouchpanelController(string key, string name, BasicTriListWithSmartObject panel, MobileControlTouchpanelProperties config) : base(key, name, panel, config)
|
||||
{
|
||||
localConfig = config;
|
||||
@@ -184,10 +139,6 @@ namespace PepperDash.Essentials.Touchpanel
|
||||
RegisterForExtenders();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the theme setting for this touchpanel controller and persists the change to configuration.
|
||||
/// </summary>
|
||||
/// <param name="theme">The new theme identifier to apply.</param>
|
||||
public void UpdateTheme(string theme)
|
||||
{
|
||||
localConfig.Theme = theme;
|
||||
@@ -320,11 +271,6 @@ namespace PepperDash.Essentials.Touchpanel
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs custom activation setup for the touchpanel controller, including
|
||||
/// registering messengers and linking to mobile control.
|
||||
/// </summary>
|
||||
/// <returns>True if activation was successful; otherwise, false.</returns>
|
||||
public override bool CustomActivate()
|
||||
{
|
||||
var appMessenger = new ITswAppControlMessenger($"appControlMessenger-{Key}", $"/device/{Key}", this);
|
||||
@@ -354,20 +300,12 @@ namespace PepperDash.Essentials.Touchpanel
|
||||
return base.CustomActivate();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles device extender signal changes for system reserved signals.
|
||||
/// </summary>
|
||||
/// <param name="currentDeviceExtender">The device extender that generated the signal change.</param>
|
||||
/// <param name="args">The signal event arguments containing the changed signal information.</param>
|
||||
|
||||
protected override void ExtenderSystemReservedSigs_DeviceExtenderSigChange(DeviceExtender currentDeviceExtender, SigEventArgs args)
|
||||
{
|
||||
Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, this, $"System Device Extender args: ${args.Event}:${args.Sig}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets up the panel drivers and signal mappings for the specified room.
|
||||
/// </summary>
|
||||
/// <param name="roomKey">The room key to configure the panel drivers for.</param>
|
||||
protected override void SetupPanelDrivers(string roomKey)
|
||||
{
|
||||
AppUrlFeedback.LinkInputSig(Panel.StringInput[1]);
|
||||
@@ -428,10 +366,6 @@ namespace PepperDash.Essentials.Touchpanel
|
||||
SetAppUrl(_bridge.AppUrl);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the application URL and updates the corresponding feedback.
|
||||
/// </summary>
|
||||
/// <param name="url">The new application URL to set.</param>
|
||||
public void SetAppUrl(string url)
|
||||
{
|
||||
_appUrl = url;
|
||||
@@ -457,9 +391,6 @@ namespace PepperDash.Essentials.Touchpanel
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Hides the currently open application on the touchpanel.
|
||||
/// </summary>
|
||||
public void HideOpenApp()
|
||||
{
|
||||
if (Panel is TswX70Base x70Panel)
|
||||
@@ -475,9 +406,6 @@ namespace PepperDash.Essentials.Touchpanel
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens an application on the touchpanel. Note: X60 panels do not support Zoom app opening.
|
||||
/// </summary>
|
||||
public void OpenApp()
|
||||
{
|
||||
if (Panel is TswX70Base x70Panel)
|
||||
@@ -493,9 +421,6 @@ namespace PepperDash.Essentials.Touchpanel
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Closes the currently open application on the touchpanel.
|
||||
/// </summary>
|
||||
public void CloseOpenApp()
|
||||
{
|
||||
if (Panel is TswX70Base x70Panel)
|
||||
@@ -511,9 +436,6 @@ namespace PepperDash.Essentials.Touchpanel
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ends the current Zoom call on the touchpanel.
|
||||
/// </summary>
|
||||
public void EndZoomCall()
|
||||
{
|
||||
if (Panel is TswX70Base x70Panel)
|
||||
@@ -529,10 +451,6 @@ namespace PepperDash.Essentials.Touchpanel
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the device information (MAC address and IP address) from the touchpanel
|
||||
/// and raises the DeviceInfoChanged event.
|
||||
/// </summary>
|
||||
public void UpdateDeviceInfo()
|
||||
{
|
||||
if (Panel is TswXX70Base x70Panel)
|
||||
@@ -569,27 +487,14 @@ namespace PepperDash.Essentials.Touchpanel
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Factory class for creating MobileControlTouchpanelController instances from device configuration.
|
||||
/// Supports various Crestron touchpanel models including TSW, TS, CrestronApp, XPanel, and DGE series.
|
||||
/// </summary>
|
||||
public class MobileControlTouchpanelControllerFactory : EssentialsPluginDeviceFactory<MobileControlTouchpanelController>
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the MobileControlTouchpanelControllerFactory class.
|
||||
/// Sets up supported device type names and minimum framework version requirements.
|
||||
/// </summary>
|
||||
public MobileControlTouchpanelControllerFactory()
|
||||
{
|
||||
TypeNames = new List<string>() { "mccrestronapp", "mctsw550", "mctsw750", "mctsw1050", "mctsw560", "mctsw760", "mctsw1060", "mctsw570", "mctsw770", "mcts770", "mctsw1070", "mcts1070", "mcxpanel", "mcdge1000" };
|
||||
TypeNames = new List<string>() { "mccrestronapp", "mctsw550", "mctsw750", "mctsw1050", "mctsw560", "mctsw760", "mctsw1060", "mctsw570", "mctsw770", "mcts770", "mctsw1070", "mcts1070", "mcxpanel" };
|
||||
MinimumEssentialsFrameworkVersion = "2.0.0";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds a MobileControlTouchpanelController device from the provided device configuration.
|
||||
/// </summary>
|
||||
/// <param name="dc">The device configuration containing the device properties and settings.</param>
|
||||
/// <returns>A configured MobileControlTouchpanelController instance.</returns>
|
||||
public override EssentialsDevice BuildDevice(DeviceConfig dc)
|
||||
{
|
||||
var comm = CommFactory.GetControlPropertiesConfig(dc);
|
||||
@@ -650,10 +555,7 @@ namespace PepperDash.Essentials.Touchpanel
|
||||
return new Tsw1070(id, Global.ControlSystem);
|
||||
else if (type == "ts1070")
|
||||
return new Ts1070(id, Global.ControlSystem);
|
||||
else if (type == "dge1000")
|
||||
return new Dge1000(id, Global.ControlSystem);
|
||||
else
|
||||
|
||||
{
|
||||
Debug.LogMessage(Serilog.Events.LogEventLevel.Warning, "WARNING: Cannot create TSW controller with type '{0}'", type);
|
||||
return null;
|
||||
|
||||
@@ -1,27 +1,161 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using Crestron.SimplSharp;
|
||||
using Crestron.SimplSharp;
|
||||
using Crestron.SimplSharp.WebScripting;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using PepperDash.Core;
|
||||
using PepperDash.Core.Logging;
|
||||
using PepperDash.Essentials.AppServer.Messengers;
|
||||
using PepperDash.Essentials.Core;
|
||||
using PepperDash.Essentials.Core.DeviceTypeInterfaces;
|
||||
using PepperDash.Essentials.Core.Web;
|
||||
using PepperDash.Essentials.RoomBridges;
|
||||
using PepperDash.Essentials.WebApiHandlers;
|
||||
using Serilog.Events;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using WebSocketSharp;
|
||||
using WebSocketSharp.Net;
|
||||
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; }
|
||||
|
||||
private string _clientId;
|
||||
|
||||
private DateTime _connectionTime;
|
||||
|
||||
public TimeSpan ConnectedDuration
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Context.WebSocket.IsAlive)
|
||||
{
|
||||
return DateTime.Now - _connectionTime;
|
||||
}
|
||||
else
|
||||
{
|
||||
return new TimeSpan(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public UiClient()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
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 = match.Groups[1].Value;
|
||||
_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);
|
||||
}
|
||||
}
|
||||
|
||||
public class MobileControlWebsocketServer : EssentialsDevice
|
||||
{
|
||||
private readonly string userAppPath = Global.FilePathPrefix + "mcUserApp" + Global.DirectorySeparator;
|
||||
@@ -29,7 +163,6 @@ namespace PepperDash.Essentials.WebSocketServer
|
||||
private readonly string localConfigFolderName = "_local-config";
|
||||
|
||||
private readonly string appConfigFileName = "_config.local.json";
|
||||
private readonly string appConfigCsFileName = "_config.cs.json";
|
||||
|
||||
/// <summary>
|
||||
/// Where the key is the join token and the value is the room key
|
||||
@@ -58,12 +191,6 @@ namespace PepperDash.Essentials.WebSocketServer
|
||||
}
|
||||
}
|
||||
|
||||
private string lanIpAddress => CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, CrestronEthernetHelper.GetAdapterdIdForSpecifiedAdapterType(EthernetAdapterType.EthernetLANAdapter));
|
||||
|
||||
private System.Net.IPAddress csIpAddress;
|
||||
|
||||
private System.Net.IPAddress csSubnetMask;
|
||||
|
||||
/// <summary>
|
||||
/// The path for the WebSocket messaging
|
||||
/// </summary>
|
||||
@@ -82,7 +209,7 @@ namespace PepperDash.Essentials.WebSocketServer
|
||||
private string _userAppBaseHref = "/mc/app";
|
||||
|
||||
/// <summary>
|
||||
/// The port the server will run on
|
||||
/// The prot the server will run on
|
||||
/// </summary>
|
||||
public int Port { get; private set; }
|
||||
|
||||
@@ -133,6 +260,9 @@ namespace PepperDash.Essentials.WebSocketServer
|
||||
{
|
||||
try
|
||||
{
|
||||
CrestronEthernetHelper.GetAdapterdIdForSpecifiedAdapterType(EthernetAdapterType.EthernetCSAdapter);
|
||||
|
||||
|
||||
Debug.LogMessage(LogEventLevel.Information, "Automatically forwarding port {0} to CS LAN", Port);
|
||||
|
||||
var csAdapterId = CrestronEthernetHelper.GetAdapterdIdForSpecifiedAdapterType(EthernetAdapterType.EthernetCSAdapter);
|
||||
@@ -155,23 +285,6 @@ namespace PepperDash.Essentials.WebSocketServer
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var csAdapterId = CrestronEthernetHelper.GetAdapterdIdForSpecifiedAdapterType(EthernetAdapterType.EthernetCSAdapter);
|
||||
var csSubnetMask = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_MASK, csAdapterId);
|
||||
var csIpAddress = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, csAdapterId);
|
||||
|
||||
this.csSubnetMask = System.Net.IPAddress.Parse(csSubnetMask);
|
||||
this.csIpAddress = System.Net.IPAddress.Parse(csIpAddress);
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
if (parent.Config.DirectServer.AutomaticallyForwardPortToCSLAN == false)
|
||||
{
|
||||
Debug.LogMessage(LogEventLevel.Information, "This processor does not have a CS LAN", this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
UiClients = new Dictionary<string, UiClientContext>();
|
||||
|
||||
@@ -298,6 +411,8 @@ namespace PepperDash.Essentials.WebSocketServer
|
||||
|
||||
var processorIp = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, lanAdapterId);
|
||||
|
||||
this.LogVerbose("Processor IP: {processorIp}", processorIp);
|
||||
|
||||
foreach (var touchpanel in touchpanels.Select(tp =>
|
||||
{
|
||||
var token = _secret.Tokens.FirstOrDefault((t) => t.Value.TouchpanelKey.Equals(tp.Key, StringComparison.InvariantCultureIgnoreCase));
|
||||
@@ -319,25 +434,11 @@ namespace PepperDash.Essentials.WebSocketServer
|
||||
continue;
|
||||
}
|
||||
|
||||
string ip = processorIp;
|
||||
if (touchpanel.Touchpanel is IMobileControlCrestronTouchpanelController crestronTouchpanel)
|
||||
{
|
||||
ip = crestronTouchpanel.ConnectedIps.Any(ipInfo =>
|
||||
{
|
||||
if (System.Net.IPAddress.TryParse(ipInfo.DeviceIpAddress, out var parsedIp))
|
||||
{
|
||||
return csIpAddress.IsInSameSubnet(parsedIp, csSubnetMask);
|
||||
}
|
||||
this.LogWarning("Invalid IP address: {deviceIpAddress}", ipInfo.DeviceIpAddress);
|
||||
return false;
|
||||
}) ? csIpAddress.ToString() : processorIp;
|
||||
}
|
||||
|
||||
var appUrl = $"http://{ip}:{_parent.Config.DirectServer.Port}/mc/app?token={touchpanel.Key}";
|
||||
var appUrl = $"http://{processorIp}:{_parent.Config.DirectServer.Port}/mc/app?token={touchpanel.Key}";
|
||||
|
||||
this.LogVerbose("Sending URL {appUrl}", appUrl);
|
||||
|
||||
touchpanel.Messenger.UpdateAppUrl($"http://{ip}:{_parent.Config.DirectServer.Port}/mc/app?token={touchpanel.Key}");
|
||||
touchpanel.Messenger.UpdateAppUrl($"http://{processorIp}:{_parent.Config.DirectServer.Port}/mc/app?token={touchpanel.Key}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -365,45 +466,7 @@ namespace PepperDash.Essentials.WebSocketServer
|
||||
|
||||
using (var sw = new StreamWriter(File.Open($"{userAppPath}{localConfigFolderName}{Global.DirectorySeparator}{appConfigFileName}", FileMode.Create, FileAccess.ReadWrite)))
|
||||
{
|
||||
// Write the LAN application configuration file. Used when a request comes in for the application config from the LAN
|
||||
var lanAdapterId = CrestronEthernetHelper.GetAdapterdIdForSpecifiedAdapterType(EthernetAdapterType.EthernetLANAdapter);
|
||||
|
||||
this.LogDebug("LAN Adapter ID: {lanAdapterId}", lanAdapterId);
|
||||
|
||||
var processorIp = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, lanAdapterId);
|
||||
|
||||
var config = GetApplicationConfig(processorIp);
|
||||
|
||||
var contents = JsonConvert.SerializeObject(config, Formatting.Indented);
|
||||
|
||||
sw.Write(contents);
|
||||
}
|
||||
|
||||
short csAdapterId;
|
||||
try
|
||||
{
|
||||
csAdapterId = CrestronEthernetHelper.GetAdapterdIdForSpecifiedAdapterType(EthernetAdapterType.EthernetCSAdapter);
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
this.LogDebug("This processor does not have a CS LAN");
|
||||
return;
|
||||
}
|
||||
|
||||
if (csAdapterId == -1)
|
||||
{
|
||||
this.LogDebug("CS LAN Adapter not found");
|
||||
return;
|
||||
}
|
||||
|
||||
this.LogDebug("CS LAN Adapter ID: {csAdapterId}. Adding CS Config", csAdapterId);
|
||||
|
||||
using (var sw = new StreamWriter(File.Open($"{userAppPath}{localConfigFolderName}{Global.DirectorySeparator}{appConfigCsFileName}", FileMode.Create, FileAccess.ReadWrite)))
|
||||
{
|
||||
// Write the CS application configuration file. Used when a request comes in for the application config from the CS
|
||||
var processorIp = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, csAdapterId);
|
||||
|
||||
var config = GetApplicationConfig(processorIp);
|
||||
var config = GetApplicationConfig();
|
||||
|
||||
var contents = JsonConvert.SerializeObject(config, Formatting.Indented);
|
||||
|
||||
@@ -411,41 +474,74 @@ namespace PepperDash.Essentials.WebSocketServer
|
||||
}
|
||||
}
|
||||
|
||||
private MobileControlApplicationConfig GetApplicationConfig(string processorIp)
|
||||
private MobileControlApplicationConfig GetApplicationConfig()
|
||||
{
|
||||
MobileControlApplicationConfig config = null;
|
||||
|
||||
var lanAdapterId = CrestronEthernetHelper.GetAdapterdIdForSpecifiedAdapterType(EthernetAdapterType.EthernetLANAdapter);
|
||||
|
||||
var processorIp = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, lanAdapterId);
|
||||
|
||||
try
|
||||
{
|
||||
var config = new MobileControlApplicationConfig
|
||||
if (_parent.Config.ApplicationConfig == null)
|
||||
{
|
||||
ApiPath = string.Format("http://{0}:{1}/mc/api", processorIp, _parent.Config.DirectServer.Port),
|
||||
GatewayAppPath = "",
|
||||
LogoPath = _parent.Config.ApplicationConfig?.LogoPath ?? "logo/logo.png",
|
||||
EnableDev = _parent.Config.ApplicationConfig?.EnableDev ?? false,
|
||||
IconSet = _parent.Config.ApplicationConfig?.IconSet ?? MCIconSet.GOOGLE,
|
||||
LoginMode = _parent.Config.ApplicationConfig?.LoginMode ?? "room-list",
|
||||
Modes = _parent.Config.ApplicationConfig?.Modes ?? new Dictionary<string, McMode>
|
||||
config = new MobileControlApplicationConfig
|
||||
{
|
||||
ApiPath = string.Format("http://{0}:{1}/mc/api", processorIp, _parent.Config.DirectServer.Port),
|
||||
GatewayAppPath = "",
|
||||
LogoPath = "logo/logo.png",
|
||||
EnableDev = false,
|
||||
IconSet = MCIconSet.GOOGLE,
|
||||
LoginMode = "room-list",
|
||||
Modes = new Dictionary<string, McMode>
|
||||
{
|
||||
"room-list",
|
||||
new McMode {
|
||||
ListPageText = "Please select your room",
|
||||
LoginHelpText = "Please select your room from the list, then enter the code shown on the display.",
|
||||
PasscodePageText = "Please enter the code shown on this room's display"
|
||||
{
|
||||
"room-list",
|
||||
new McMode{
|
||||
ListPageText= "Please select your room",
|
||||
LoginHelpText = "Please select your room from the list, then enter the code shown on the display.",
|
||||
PasscodePageText = "Please enter the code shown on this room's display"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
Logging = _parent.Config.ApplicationConfig?.Logging ?? false,
|
||||
PartnerMetadata = _parent.Config.ApplicationConfig?.PartnerMetadata ?? new List<MobileControlPartnerMetadata>()
|
||||
};
|
||||
|
||||
return config;
|
||||
},
|
||||
Logging = _parent.Config.DirectServer.Logging.EnableRemoteLogging,
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
config = new MobileControlApplicationConfig
|
||||
{
|
||||
ApiPath = string.Format("http://{0}:{1}/mc/api", processorIp, _parent.Config.DirectServer.Port),
|
||||
GatewayAppPath = "",
|
||||
LogoPath = _parent.Config.ApplicationConfig.LogoPath ?? "logo/logo.png",
|
||||
EnableDev = _parent.Config.ApplicationConfig.EnableDev ?? false,
|
||||
IconSet = _parent.Config.ApplicationConfig.IconSet ?? MCIconSet.GOOGLE,
|
||||
LoginMode = _parent.Config.ApplicationConfig.LoginMode ?? "room-list",
|
||||
Modes = _parent.Config.ApplicationConfig.Modes ?? new Dictionary<string, McMode>
|
||||
{
|
||||
{
|
||||
"room-list",
|
||||
new McMode {
|
||||
ListPageText = "Please select your room",
|
||||
LoginHelpText = "Please select your room from the list, then enter the code shown on the display.",
|
||||
PasscodePageText = "Please enter the code shown on this room's display"
|
||||
}
|
||||
}
|
||||
},
|
||||
Logging = _parent.Config.ApplicationConfig.Logging,
|
||||
PartnerMetadata = _parent.Config.ApplicationConfig.PartnerMetadata ?? new List<MobileControlPartnerMetadata>()
|
||||
};
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
this.LogError(ex, "Error getting application configuration");
|
||||
Debug.LogMessage(ex, "Error getting application configuration", this);
|
||||
|
||||
return null;
|
||||
Debug.LogMessage(LogEventLevel.Verbose, "Config Object: {config} from {parentConfig}", this, config, _parent.Config);
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -584,7 +680,7 @@ namespace PepperDash.Essentials.WebSocketServer
|
||||
|
||||
var values = s.Split(' ');
|
||||
|
||||
if (values.Length < 2)
|
||||
if(values.Length < 2)
|
||||
{
|
||||
CrestronConsole.ConsoleCommandResponse("Invalid number of arguments. Please provide a room key and a grant code");
|
||||
return;
|
||||
@@ -1111,14 +1207,6 @@ namespace PepperDash.Essentials.WebSocketServer
|
||||
|
||||
this.LogVerbose("Attempting to serve file: {filePath}", filePath);
|
||||
|
||||
var remoteIp = req.RemoteEndPoint.Address;
|
||||
|
||||
// Check if the request is coming from the CS LAN and if so, send the CS config instead of the LAN config
|
||||
if (csSubnetMask != null && csIpAddress != null && remoteIp.IsInSameSubnet(csIpAddress, csSubnetMask) && filePath.Contains(appConfigFileName))
|
||||
{
|
||||
filePath = filePath.Replace(appConfigFileName, appConfigCsFileName);
|
||||
}
|
||||
|
||||
byte[] contents;
|
||||
if (File.Exists(filePath))
|
||||
{
|
||||
|
||||
@@ -1,145 +0,0 @@
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using PepperDash.Core;
|
||||
using PepperDash.Essentials.AppServer.Messengers;
|
||||
using PepperDash.Essentials.RoomBridges;
|
||||
using Serilog.Events;
|
||||
using System;
|
||||
using System.Text.RegularExpressions;
|
||||
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; }
|
||||
|
||||
private string _clientId;
|
||||
|
||||
private DateTime _connectionTime;
|
||||
|
||||
public TimeSpan ConnectedDuration
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Context.WebSocket.IsAlive)
|
||||
{
|
||||
return DateTime.Now - _connectionTime;
|
||||
}
|
||||
else
|
||||
{
|
||||
return new TimeSpan(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public UiClient()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
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 = match.Groups[1].Value;
|
||||
_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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user