Compare commits

..

3 Commits

Author SHA1 Message Date
copilot-swe-agent[bot]
94909d2c7c Finalize CallStatusMessenger implementation - remove test files and verify clean implementation
Co-authored-by: ngenovese11 <23391587+ngenovese11@users.noreply.github.com>
2025-08-13 21:16:35 +00:00
copilot-swe-agent[bot]
9946e9a9ae Implement CallStatusMessenger for interface-based devices and update MobileControlSystemController
Co-authored-by: ngenovese11 <23391587+ngenovese11@users.noreply.github.com>
2025-08-13 21:14:29 +00:00
copilot-swe-agent[bot]
db80b7b501 Initial plan 2025-08-13 20:58:29 +00:00
30 changed files with 866 additions and 1188 deletions

1
.gitignore vendored
View File

@@ -395,4 +395,3 @@ essentials-framework/Essentials Interfaces/PepperDash_Essentials_Interfaces/Pepp
_site/ _site/
api/ api/
*.DS_Store *.DS_Store
/._PepperDash.Essentials.4Series.sln

View File

@@ -1,6 +1,6 @@
<Project> <Project>
<PropertyGroup> <PropertyGroup>
<Version>2.15.1-local</Version> <Version>2.12.1-local</Version>
<InformationalVersion>$(Version)</InformationalVersion> <InformationalVersion>$(Version)</InformationalVersion>
<Authors>PepperDash Technology</Authors> <Authors>PepperDash Technology</Authors>
<Company>PepperDash Technology</Company> <Company>PepperDash Technology</Company>

View File

@@ -74,10 +74,6 @@ namespace PepperDash.Core
/// <summary> /// <summary>
/// Secure TCP/IP /// Secure TCP/IP
/// </summary> /// </summary>
SecureTcpIp, SecureTcpIp
/// <summary>
/// Used when comms needs to be handled in SIMPL and bridged opposite the normal direction
/// </summary>
ComBridge
} }
} }

View File

@@ -43,7 +43,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="BouncyCastle.Cryptography" Version="2.4.0" /> <PackageReference Include="BouncyCastle.Cryptography" Version="2.4.0" />
<PackageReference Include="Crestron.SimplSharp.SDK.Library" Version="2.21.157" /> <PackageReference Include="Crestron.SimplSharp.SDK.Library" Version="2.21.90" />
<PackageReference Include="Serilog" Version="3.1.1" /> <PackageReference Include="Serilog" Version="3.1.1" />
<PackageReference Include="Serilog.Expressions" Version="4.0.0" /> <PackageReference Include="Serilog.Expressions" Version="4.0.0" />
<PackageReference Include="Serilog.Formatting.Compact" Version="2.0.0" /> <PackageReference Include="Serilog.Formatting.Compact" Version="2.0.0" />

View File

@@ -2,12 +2,14 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Reflection;
using Crestron.SimplSharp; using Crestron.SimplSharp;
using System.Reflection;
using Crestron.SimplSharpPro; using Crestron.SimplSharpPro;
using Crestron.SimplSharpPro.DeviceSupport; using Crestron.SimplSharpPro.DeviceSupport;
using Crestron.SimplSharpPro.EthernetCommunication; using Crestron.SimplSharpPro.EthernetCommunication;
using Newtonsoft.Json; using Newtonsoft.Json;
using PepperDash.Core; using PepperDash.Core;
using PepperDash.Essentials.Core.Config; using PepperDash.Essentials.Core.Config;
using Serilog.Events; using Serilog.Events;
@@ -353,22 +355,22 @@ namespace PepperDash.Essentials.Core.Bridges
/// </summary> /// </summary>
public class EiscApiPropertiesConfig public class EiscApiPropertiesConfig
{ {
[JsonProperty("control")]
/// <summary> /// <summary>
/// Gets or sets the Control /// Gets or sets the Control
/// </summary> /// </summary>
[JsonProperty("control")]
public EssentialsControlPropertiesConfig Control { get; set; } public EssentialsControlPropertiesConfig Control { get; set; }
[JsonProperty("devices")]
/// <summary> /// <summary>
/// Gets or sets the Devices /// Gets or sets the Devices
/// </summary> /// </summary>
[JsonProperty("devices")]
public List<ApiDevicePropertiesConfig> Devices { get; set; } public List<ApiDevicePropertiesConfig> Devices { get; set; }
[JsonProperty("rooms")]
/// <summary> /// <summary>
/// Gets or sets the Rooms /// Gets or sets the Rooms
/// </summary> /// </summary>
[JsonProperty("rooms")]
public List<ApiRoomPropertiesConfig> Rooms { get; set; } public List<ApiRoomPropertiesConfig> Rooms { get; set; }
@@ -377,22 +379,22 @@ namespace PepperDash.Essentials.Core.Bridges
/// </summary> /// </summary>
public class ApiDevicePropertiesConfig public class ApiDevicePropertiesConfig
{ {
[JsonProperty("deviceKey")]
/// <summary> /// <summary>
/// Gets or sets the DeviceKey /// Gets or sets the DeviceKey
/// </summary> /// </summary>
[JsonProperty("deviceKey")]
public string DeviceKey { get; set; } public string DeviceKey { get; set; }
[JsonProperty("joinStart")]
/// <summary> /// <summary>
/// Gets or sets the JoinStart /// Gets or sets the JoinStart
/// </summary> /// </summary>
[JsonProperty("joinStart")]
public uint JoinStart { get; set; } public uint JoinStart { get; set; }
[JsonProperty("joinMapKey")]
/// <summary> /// <summary>
/// Gets or sets the JoinMapKey /// Gets or sets the JoinMapKey
/// </summary> /// </summary>
[JsonProperty("joinMapKey")]
public string JoinMapKey { get; set; } public string JoinMapKey { get; set; }
} }
@@ -401,22 +403,22 @@ namespace PepperDash.Essentials.Core.Bridges
/// </summary> /// </summary>
public class ApiRoomPropertiesConfig public class ApiRoomPropertiesConfig
{ {
[JsonProperty("roomKey")]
/// <summary> /// <summary>
/// Gets or sets the RoomKey /// Gets or sets the RoomKey
/// </summary> /// </summary>
[JsonProperty("roomKey")]
public string RoomKey { get; set; } public string RoomKey { get; set; }
[JsonProperty("joinStart")]
/// <summary> /// <summary>
/// Gets or sets the JoinStart /// Gets or sets the JoinStart
/// </summary> /// </summary>
[JsonProperty("joinStart")]
public uint JoinStart { get; set; } public uint JoinStart { get; set; }
[JsonProperty("joinMapKey")]
/// <summary> /// <summary>
/// Gets or sets the JoinMapKey /// Gets or sets the JoinMapKey
/// </summary> /// </summary>
[JsonProperty("joinMapKey")]
public string JoinMapKey { get; set; } public string JoinMapKey { get; set; }
} }

View File

@@ -7,13 +7,6 @@ namespace PepperDash.Essentials.Core.Bridges
/// </summary> /// </summary>
public interface IBridgeAdvanced public interface IBridgeAdvanced
{ {
/// <summary>
/// Links the bridge to the API using the provided trilist, join start, join map key, and bridge.
/// </summary>
/// <param name="trilist">The trilist to link to.</param>
/// <param name="joinStart">The starting join number.</param>
/// <param name="joinMapKey">The key for the join map.</param>
/// <param name="bridge">The EISC API bridge.</param>
void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge); void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge);
} }
} }

View File

@@ -1,138 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;
using Crestron.SimplSharp.CrestronSockets;
using Crestron.SimplSharpPro.DeviceSupport;
using Newtonsoft.Json;
using PepperDash.Core;
using PepperDash.Core.Logging;
using PepperDash.Essentials.Core.Bridges;
using PepperDash.Essentials.Core.Config;
using PepperDash.Essentials.Core.Devices;
namespace PepperDash.Essentials.Core
{
/// <summary>
/// Implements IBasicCommunication and sends all communication through an EISC
/// </summary>
[Description("Generic communication wrapper class for any IBasicCommunication type")]
public class CommBridge : EssentialsBridgeableDevice, IBasicCommunication
{
private EiscApiAdvanced eisc;
private IBasicCommunicationJoinMap joinMap;
/// <summary>
/// Event triggered when text is received through the communication bridge.
/// </summary>
public event EventHandler<GenericCommMethodReceiveTextArgs> TextReceived;
/// <summary>
/// Event triggered when bytes are received through the communication bridge.
/// </summary>
public event EventHandler<GenericCommMethodReceiveBytesArgs> BytesReceived;
/// <summary>
/// Indicates whether the communication bridge is currently connected.
/// </summary>
public bool IsConnected { get; private set; }
/// <summary>
/// Initializes a new instance of the <see cref="CommBridge"/> class.
/// </summary>
/// <param name="key">The unique key for the communication bridge.</param>
/// <param name="name">The display name for the communication bridge.</param>
public CommBridge(string key, string name)
: base(key, name)
{
}
/// <summary>
/// Sends a byte array through the communication bridge.
/// </summary>
/// <param name="bytes">The byte array to send.</param>
public void SendBytes(byte[] bytes)
{
if (eisc == null)
{
this.LogWarning("EISC is null, cannot send bytes.");
return;
}
eisc.Eisc.SetString(joinMap.SendText.JoinNumber, Encoding.ASCII.GetString(bytes, 0, bytes.Length));
}
/// <summary>
/// Sends a text string through the communication bridge.
/// </summary>
/// <param name="text">The text string to send.</param>
public void SendText(string text)
{
if (eisc == null)
{
this.LogWarning("EISC is null, cannot send text.");
return;
}
eisc.Eisc.SetString(joinMap.SendText.JoinNumber, text);
}
/// <summary>
/// Initiates a connection through the communication bridge.
/// </summary>
public void Connect()
{
if (eisc == null)
{
this.LogWarning("EISC is null, cannot connect.");
return;
}
eisc.Eisc.SetBool(joinMap.Connect.JoinNumber, true);
}
/// <summary>
/// Terminates the connection through the communication bridge.
/// </summary>
public void Disconnect()
{
if (eisc == null)
{
this.LogWarning("EISC is null, cannot disconnect.");
return;
}
eisc.Eisc.SetBool(joinMap.Connect.JoinNumber, false);
}
/// <inheritdoc />
public override void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge)
{
joinMap = new IBasicCommunicationJoinMap(joinStart);
var joinMapSerialized = JoinMapHelper.GetSerializedJoinMapForDevice(joinMapKey);
if (!string.IsNullOrEmpty(joinMapSerialized))
joinMap = JsonConvert.DeserializeObject<IBasicCommunicationJoinMap>(joinMapSerialized);
if (bridge != null)
{
bridge.AddJoinMap(Key, joinMap);
}
else
{
this.LogWarning("Please update config to use 'eiscapiadvanced' to get all join map features for this device.");
}
this.LogDebug("Linking to Trilist '{0}'", trilist.ID.ToString("X"));
eisc = bridge;
trilist.SetBoolSigAction(joinMap.Connected.JoinNumber, (b) => IsConnected = b);
trilist.SetStringSigAction(joinMap.TextReceived.JoinNumber, (s) =>
{
TextReceived?.Invoke(this, new GenericCommMethodReceiveTextArgs(s));
BytesReceived?.Invoke(this, new GenericCommMethodReceiveBytesArgs(Encoding.ASCII.GetBytes(s)));
});
}
}
}

View File

@@ -1,9 +1,11 @@
using System; using System;
using Crestron.SimplSharp;
using Crestron.SimplSharpPro; using Crestron.SimplSharpPro;
using Crestron.SimplSharpPro.DM; using Crestron.SimplSharpPro.DM;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using PepperDash.Core; using PepperDash.Core;
using PepperDash.Essentials.Core.Config; using PepperDash.Essentials.Core.Config;
using Serilog.Events; using Serilog.Events;
@@ -36,9 +38,9 @@ namespace PepperDash.Essentials.Core
/// Returns a comm method of either com port, TCP, SSH, and puts this into the DeviceManager /// Returns a comm method of either com port, TCP, SSH, and puts this into the DeviceManager
/// </summary> /// </summary>
/// <param name="deviceConfig">The Device config object</param> /// <param name="deviceConfig">The Device config object</param>
/// <summary> /// <summary>
/// CreateCommForDevice method /// CreateCommForDevice method
/// </summary> /// </summary>
public static IBasicCommunication CreateCommForDevice(DeviceConfig deviceConfig) public static IBasicCommunication CreateCommForDevice(DeviceConfig deviceConfig)
{ {
EssentialsControlPropertiesConfig controlConfig = GetControlPropertiesConfig(deviceConfig); EssentialsControlPropertiesConfig controlConfig = GetControlPropertiesConfig(deviceConfig);
@@ -54,38 +56,35 @@ namespace PepperDash.Essentials.Core
case eControlMethod.Com: case eControlMethod.Com:
comm = new ComPortController(deviceConfig.Key + "-com", GetComPort, controlConfig.ComParams.Value, controlConfig); comm = new ComPortController(deviceConfig.Key + "-com", GetComPort, controlConfig.ComParams.Value, controlConfig);
break; break;
case eControlMethod.ComBridge: case eControlMethod.Cec:
comm = new CommBridge(deviceConfig.Key + "-simpl", deviceConfig.Name + " Simpl"); comm = new CecPortController(deviceConfig.Key + "-cec", GetCecPort, controlConfig);
break; break;
case eControlMethod.Cec:
comm = new CecPortController(deviceConfig.Key + "-cec", GetCecPort, controlConfig);
break;
case eControlMethod.IR: case eControlMethod.IR:
break; break;
case eControlMethod.Ssh: case eControlMethod.Ssh:
{ {
var ssh = new GenericSshClient(deviceConfig.Key + "-ssh", c.Address, c.Port, c.Username, c.Password); var ssh = new GenericSshClient(deviceConfig.Key + "-ssh", c.Address, c.Port, c.Username, c.Password);
ssh.AutoReconnect = c.AutoReconnect; ssh.AutoReconnect = c.AutoReconnect;
if (ssh.AutoReconnect) if(ssh.AutoReconnect)
ssh.AutoReconnectIntervalMs = c.AutoReconnectIntervalMs; ssh.AutoReconnectIntervalMs = c.AutoReconnectIntervalMs;
comm = ssh; comm = ssh;
break; break;
} }
case eControlMethod.Tcpip: case eControlMethod.Tcpip:
{ {
var tcp = new GenericTcpIpClient(deviceConfig.Key + "-tcp", c.Address, c.Port, c.BufferSize); var tcp = new GenericTcpIpClient(deviceConfig.Key + "-tcp", c.Address, c.Port, c.BufferSize);
tcp.AutoReconnect = c.AutoReconnect; tcp.AutoReconnect = c.AutoReconnect;
if (tcp.AutoReconnect) if (tcp.AutoReconnect)
tcp.AutoReconnectIntervalMs = c.AutoReconnectIntervalMs; tcp.AutoReconnectIntervalMs = c.AutoReconnectIntervalMs;
comm = tcp; comm = tcp;
break; break;
} }
case eControlMethod.Udp: case eControlMethod.Udp:
{ {
var udp = new GenericUdpServer(deviceConfig.Key + "-udp", c.Address, c.Port, c.BufferSize); var udp = new GenericUdpServer(deviceConfig.Key + "-udp", c.Address, c.Port, c.BufferSize);
comm = udp; comm = udp;
break; break;
} }
case eControlMethod.Telnet: case eControlMethod.Telnet:
break; break;
case eControlMethod.SecureTcpIp: case eControlMethod.SecureTcpIp:
@@ -108,14 +107,15 @@ namespace PepperDash.Essentials.Core
} }
// put it in the device manager if it's the right flavor // put it in the device manager if it's the right flavor
if (comm is Device comDev) var comDev = comm as Device;
if (comDev != null)
DeviceManager.AddDevice(comDev); DeviceManager.AddDevice(comDev);
return comm; return comm;
} }
/// <summary> /// <summary>
/// GetComPort method /// GetComPort method
/// </summary> /// </summary>
public static ComPort GetComPort(EssentialsControlPropertiesConfig config) public static ComPort GetComPort(EssentialsControlPropertiesConfig config)
{ {
var comPar = config.ComParams; var comPar = config.ComParams;
@@ -126,74 +126,74 @@ namespace PepperDash.Essentials.Core
return null; return null;
} }
/// <summary> /// <summary>
/// Gets an ICec port from a RoutingInput or RoutingOutput on a device /// Gets an ICec port from a RoutingInput or RoutingOutput on a device
/// </summary> /// </summary>
/// <param name="config"></param> /// <param name="config"></param>
/// <returns></returns> /// <returns></returns>
/// <summary> /// <summary>
/// GetCecPort method /// GetCecPort method
/// </summary> /// </summary>
public static ICec GetCecPort(ControlPropertiesConfig config) public static ICec GetCecPort(ControlPropertiesConfig config)
{ {
try try
{ {
var dev = DeviceManager.GetDeviceForKey(config.ControlPortDevKey); var dev = DeviceManager.GetDeviceForKey(config.ControlPortDevKey);
Debug.LogMessage(LogEventLevel.Information, "GetCecPort: device '{0}' {1}", config.ControlPortDevKey, dev == null Debug.LogMessage(LogEventLevel.Information, "GetCecPort: device '{0}' {1}", config.ControlPortDevKey, dev == null
? "is not valid, failed to get cec port" ? "is not valid, failed to get cec port"
: "found in device manager, attempting to get cec port"); : "found in device manager, attempting to get cec port");
if (dev == null) if (dev == null)
return null; return null;
if (String.IsNullOrEmpty(config.ControlPortName)) if (String.IsNullOrEmpty(config.ControlPortName))
{ {
Debug.LogMessage(LogEventLevel.Information, "GetCecPort: '{0}' - Configuration missing 'ControlPortName'", config.ControlPortDevKey); Debug.LogMessage(LogEventLevel.Information, "GetCecPort: '{0}' - Configuration missing 'ControlPortName'", config.ControlPortDevKey);
return null; return null;
} }
var inputsOutputs = dev as IRoutingInputsOutputs; var inputsOutputs = dev as IRoutingInputsOutputs;
if (inputsOutputs == null) if (inputsOutputs == null)
{ {
Debug.LogMessage(LogEventLevel.Information, "GetCecPort: Device '{0}' does not support IRoutingInputsOutputs, failed to get CEC port called '{1}'", Debug.LogMessage(LogEventLevel.Information, "GetCecPort: Device '{0}' does not support IRoutingInputsOutputs, failed to get CEC port called '{1}'",
config.ControlPortDevKey, config.ControlPortName); config.ControlPortDevKey, config.ControlPortName);
return null; return null;
} }
var inputPort = inputsOutputs.InputPorts[config.ControlPortName]; var inputPort = inputsOutputs.InputPorts[config.ControlPortName];
if (inputPort != null && inputPort.Port is ICec) if (inputPort != null && inputPort.Port is ICec)
return inputPort.Port as ICec; return inputPort.Port as ICec;
var outputPort = inputsOutputs.OutputPorts[config.ControlPortName]; var outputPort = inputsOutputs.OutputPorts[config.ControlPortName];
if (outputPort != null && outputPort.Port is ICec) if (outputPort != null && outputPort.Port is ICec)
return outputPort.Port as ICec; return outputPort.Port as ICec;
} }
catch (Exception ex) catch (Exception ex)
{ {
Debug.LogMessage(LogEventLevel.Debug, "GetCecPort Exception Message: {0}", ex.Message); Debug.LogMessage(LogEventLevel.Debug, "GetCecPort Exception Message: {0}", ex.Message);
Debug.LogMessage(LogEventLevel.Verbose, "GetCecPort Exception StackTrace: {0}", ex.StackTrace); Debug.LogMessage(LogEventLevel.Verbose, "GetCecPort Exception StackTrace: {0}", ex.StackTrace);
if (ex.InnerException != null) if (ex.InnerException != null)
Debug.LogMessage(LogEventLevel.Information, "GetCecPort Exception InnerException: {0}", ex.InnerException); Debug.LogMessage(LogEventLevel.Information, "GetCecPort Exception InnerException: {0}", ex.InnerException);
} }
Debug.LogMessage(LogEventLevel.Information, "GetCecPort: Device '{0}' does not have a CEC port called '{1}'", Debug.LogMessage(LogEventLevel.Information, "GetCecPort: Device '{0}' does not have a CEC port called '{1}'",
config.ControlPortDevKey, config.ControlPortName); config.ControlPortDevKey, config.ControlPortName);
return null; return null;
} }
/// <summary> /// <summary>
/// Helper to grab the IComPorts device for this PortDeviceKey. Key "controlSystem" will /// Helper to grab the IComPorts device for this PortDeviceKey. Key "controlSystem" will
/// return the ControlSystem object from the Global class. /// return the ControlSystem object from the Global class.
/// </summary> /// </summary>
/// <returns>IComPorts device or null if the device is not found or does not implement IComPorts</returns> /// <returns>IComPorts device or null if the device is not found or does not implement IComPorts</returns>
/// <summary> /// <summary>
/// GetIComPortsDeviceFromManagedDevice method /// GetIComPortsDeviceFromManagedDevice method
/// </summary> /// </summary>
public static IComPorts GetIComPortsDeviceFromManagedDevice(string ComPortDevKey) public static IComPorts GetIComPortsDeviceFromManagedDevice(string ComPortDevKey)
{ {
if ((ComPortDevKey.Equals("controlSystem", System.StringComparison.OrdinalIgnoreCase) if ((ComPortDevKey.Equals("controlSystem", System.StringComparison.OrdinalIgnoreCase)
@@ -210,81 +210,81 @@ namespace PepperDash.Essentials.Core
} }
} }
/// <summary> /// <summary>
/// Represents a EssentialsControlPropertiesConfig /// Represents a EssentialsControlPropertiesConfig
/// </summary> /// </summary>
public class EssentialsControlPropertiesConfig : public class EssentialsControlPropertiesConfig :
ControlPropertiesConfig ControlPropertiesConfig
{ {
[JsonProperty("comParams", NullValueHandling = NullValueHandling.Ignore)] [JsonProperty("comParams", NullValueHandling = NullValueHandling.Ignore)]
[JsonConverter(typeof(ComSpecJsonConverter))] [JsonConverter(typeof(ComSpecJsonConverter))]
public ComPort.ComPortSpec? ComParams { get; set; } public ComPort.ComPortSpec? ComParams { get; set; }
[JsonProperty("cresnetId", NullValueHandling = NullValueHandling.Ignore)] [JsonProperty("cresnetId", NullValueHandling = NullValueHandling.Ignore)]
public string CresnetId { get; set; } public string CresnetId { get; set; }
/// <summary> /// <summary>
/// Attempts to provide uint conversion of string CresnetId /// Attempts to provide uint conversion of string CresnetId
/// </summary> /// </summary>
[JsonIgnore] [JsonIgnore]
public uint CresnetIdInt public uint CresnetIdInt
{ {
get get
{ {
try try
{ {
return Convert.ToUInt32(CresnetId, 16); return Convert.ToUInt32(CresnetId, 16);
} }
catch (Exception) catch (Exception)
{ {
throw new FormatException(string.Format("ERROR:Unable to convert Cresnet ID: {0} to hex. Error:\n{1}", CresnetId)); throw new FormatException(string.Format("ERROR:Unable to convert Cresnet ID: {0} to hex. Error:\n{1}", CresnetId));
} }
} }
} }
[JsonProperty("infinetId", NullValueHandling = NullValueHandling.Ignore)] [JsonProperty("infinetId", NullValueHandling = NullValueHandling.Ignore)]
/// <summary> /// <summary>
/// Gets or sets the InfinetId /// Gets or sets the InfinetId
/// </summary> /// </summary>
public string InfinetId { get; set; } public string InfinetId { get; set; }
/// <summary> /// <summary>
/// Attepmts to provide uiont conversion of string InifinetId /// Attepmts to provide uiont conversion of string InifinetId
/// </summary> /// </summary>
[JsonIgnore] [JsonIgnore]
public uint InfinetIdInt public uint InfinetIdInt
{ {
get get
{ {
try try
{ {
return Convert.ToUInt32(InfinetId, 16); return Convert.ToUInt32(InfinetId, 16);
} }
catch (Exception) catch (Exception)
{ {
throw new FormatException(string.Format("ERROR:Unable to conver Infinet ID: {0} to hex. Error:\n{1}", InfinetId)); throw new FormatException(string.Format("ERROR:Unable to conver Infinet ID: {0} to hex. Error:\n{1}", InfinetId));
} }
} }
} }
} }
/// <summary> /// <summary>
/// Represents a IrControlSpec /// Represents a IrControlSpec
/// </summary> /// </summary>
public class IrControlSpec public class IrControlSpec
{ {
/// <summary> /// <summary>
/// Gets or sets the PortDeviceKey /// Gets or sets the PortDeviceKey
/// </summary> /// </summary>
public string PortDeviceKey { get; set; } public string PortDeviceKey { get; set; }
/// <summary> /// <summary>
/// Gets or sets the PortNumber /// Gets or sets the PortNumber
/// </summary> /// </summary>
public uint PortNumber { get; set; } public uint PortNumber { get; set; }
/// <summary> /// <summary>
/// Gets or sets the File /// Gets or sets the File
/// </summary> /// </summary>
public string File { get; set; } public string File { get; set; }
} }
} }

View File

@@ -19,11 +19,5 @@ namespace PepperDash.Essentials.Core.Config
/// </summary> /// </summary>
[JsonProperty("multicastAudioAddress", NullValueHandling = NullValueHandling.Ignore)] [JsonProperty("multicastAudioAddress", NullValueHandling = NullValueHandling.Ignore)]
public string MulticastAudioAddress { get; set; } public string MulticastAudioAddress { get; set; }
/// <summary>
/// The URL for the streaming device's media stream.
/// </summary>
[JsonProperty("streamUrl", NullValueHandling = NullValueHandling.Ignore)]
public string StreamUrl { get; set; }
} }
} }

View File

@@ -18,41 +18,41 @@ namespace PepperDash.Essentials.Core.Config
/// </summary> /// </summary>
public class DeviceConfig public class DeviceConfig
{ {
[JsonProperty("key")]
/// <summary> /// <summary>
/// Gets or sets the Key /// Gets or sets the Key
/// </summary> /// </summary>
[JsonProperty("key")]
public string Key { get; set; } public string Key { get; set; }
[JsonProperty("uid")]
/// <summary> /// <summary>
/// Gets or sets the Uid /// Gets or sets the Uid
/// </summary> /// </summary>
[JsonProperty("uid")]
public int Uid { get; set; } public int Uid { get; set; }
[JsonProperty("name")]
/// <summary> /// <summary>
/// Gets or sets the Name /// Gets or sets the Name
/// </summary> /// </summary>
[JsonProperty("name")]
public string Name { get; set; } public string Name { get; set; }
[JsonProperty("group")]
/// <summary> /// <summary>
/// Gets or sets the Group /// Gets or sets the Group
/// </summary> /// </summary>
[JsonProperty("group")]
public string Group { get; set; } public string Group { get; set; }
[JsonProperty("type")]
/// <summary> /// <summary>
/// Gets or sets the Type /// Gets or sets the Type
/// </summary> /// </summary>
[JsonProperty("type")]
public string Type { get; set; } public string Type { get; set; }
[JsonProperty("properties")]
[JsonConverter(typeof(DevicePropertiesConverter))]
/// <summary> /// <summary>
/// Gets or sets the Properties /// Gets or sets the Properties
/// </summary> /// </summary>
[JsonProperty("properties")]
[JsonConverter(typeof(DevicePropertiesConverter))]
public JToken Properties { get; set; } public JToken Properties { get; set; }
public DeviceConfig(DeviceConfig dc) public DeviceConfig(DeviceConfig dc)
@@ -68,7 +68,7 @@ namespace PepperDash.Essentials.Core.Config
//Properties = JToken.FromObject(dc.Properties); //Properties = JToken.FromObject(dc.Properties);
} }
public DeviceConfig() { } public DeviceConfig() {}
} }
/// <summary> /// <summary>

View File

@@ -1,7 +1,6 @@
using Crestron.SimplSharpPro.DM.Streaming;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Crestron.SimplSharpPro.DM.Streaming;
using PepperDash.Core;
namespace PepperDash.Essentials.Core namespace PepperDash.Essentials.Core
{ {
@@ -12,7 +11,7 @@ namespace PepperDash.Essentials.Core
/// subscribers when the port information is updated. Implementations of this interface should ensure that the <see /// 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 /// cref="PortInformationChanged"/> event is raised whenever the <see cref="NetworkPorts"/> collection
/// changes.</remarks> /// changes.</remarks>
public interface INvxNetworkPortInformation : IKeyed public interface INvxNetworkPortInformation
{ {
/// <summary> /// <summary>
/// Occurs when the port information changes. /// Occurs when the port information changes.

View File

@@ -202,15 +202,14 @@ namespace PepperDash.Essentials.Core
private static void ListDevices(string s) private static void ListDevices(string s)
{ {
CrestronConsole.ConsoleCommandResponse($"{Devices.Count} Devices registered with Device Manager:\r\n"); Debug.LogMessage(LogEventLevel.Information, "{0} Devices registered with Device Manager:", Devices.Count);
var sorted = Devices.Values.ToList(); var sorted = Devices.Values.ToList();
sorted.Sort((a, b) => a.Key.CompareTo(b.Key)); sorted.Sort((a, b) => a.Key.CompareTo(b.Key));
foreach (var d in sorted) foreach (var d in sorted)
{ {
var name = d is IKeyName ? (d as IKeyName).Name : "---"; var name = d is IKeyName ? (d as IKeyName).Name : "---";
CrestronConsole.ConsoleCommandResponse($" [{d.Key}] {name}\r\n"); Debug.LogMessage(LogEventLevel.Information, " [{0}] {1}", d.Key, name);
} }
} }
@@ -219,17 +218,28 @@ namespace PepperDash.Essentials.Core
var dev = GetDeviceForKey(devKey); var dev = GetDeviceForKey(devKey);
if (dev == null) if (dev == null)
{ {
CrestronConsole.ConsoleCommandResponse($"Device '{devKey}' not found\r\n"); Debug.LogMessage(LogEventLevel.Information, "Device '{0}' not found", devKey);
return; return;
} }
if (!(dev is IHasFeedback statusDev)) if (!(dev is IHasFeedback statusDev))
{ {
CrestronConsole.ConsoleCommandResponse($"Device '{devKey}' does not have visible feedbacks\r\n"); Debug.LogMessage(LogEventLevel.Information, "Device '{0}' does not have visible feedbacks", devKey);
return; return;
} }
statusDev.DumpFeedbacksToConsole(true); statusDev.DumpFeedbacksToConsole(true);
} }
//static void ListDeviceCommands(string devKey)
//{
// var dev = GetDeviceForKey(devKey);
// if (dev == null)
// {
// Debug.LogMessage(LogEventLevel.Information, "Device '{0}' not found", devKey);
// return;
// }
// Debug.LogMessage(LogEventLevel.Information, "This needs to be reworked. Stay tuned.", devKey);
//}
private static void ListDeviceCommStatuses(string input) private static void ListDeviceCommStatuses(string input)
{ {
@@ -239,6 +249,12 @@ namespace PepperDash.Essentials.Core
} }
} }
//static void DoDeviceCommand(string command)
//{
// Debug.LogMessage(LogEventLevel.Information, "Not yet implemented. Stay tuned");
//}
/// <summary> /// <summary>
/// AddDevice method /// AddDevice method
/// </summary> /// </summary>

View File

@@ -3,29 +3,16 @@ using PepperDash.Essentials.Core.Bridges;
namespace PepperDash.Essentials.Core namespace PepperDash.Essentials.Core
{ {
/// <summary> public abstract class EssentialsBridgeableDevice:EssentialsDevice, IBridgeAdvanced
/// Base class for devices that can be bridged to an EISC API.
/// </summary>
public abstract class EssentialsBridgeableDevice : EssentialsDevice, IBridgeAdvanced
{ {
/// <summary>
/// Initializes a new instance of the <see cref="EssentialsBridgeableDevice"/> class with the specified key.
/// </summary>
/// <param name="key">The unique key for the device.</param>
protected EssentialsBridgeableDevice(string key) : base(key) protected EssentialsBridgeableDevice(string key) : base(key)
{ {
} }
/// <summary>
/// Initializes a new instance of the <see cref="EssentialsBridgeableDevice"/> class with the specified key and name.
/// </summary>
/// <param name="key">The unique key for the device.</param>
/// <param name="name">The display name for the device.</param>
protected EssentialsBridgeableDevice(string key, string name) : base(key, name) protected EssentialsBridgeableDevice(string key, string name) : base(key, name)
{ {
} }
/// <inheritdoc />
public abstract void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge); public abstract void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge);
} }
} }

View File

@@ -1,14 +1,14 @@
using System; using System;
using System.Linq; using System.Linq;
using Crestron.SimplSharp;
using PepperDash.Core; using PepperDash.Core;
using Serilog.Events; using Serilog.Events;
namespace PepperDash.Essentials.Core namespace PepperDash.Essentials.Core
{ {
/// <summary> /// <summary>
/// Defines the contract for IHasFeedback /// Defines the contract for IHasFeedback
/// </summary> /// </summary>
public interface IHasFeedback : IKeyed public interface IHasFeedback : IKeyed
{ {
/// <summary> /// <summary>
@@ -19,72 +19,47 @@ namespace PepperDash.Essentials.Core
} }
/// <summary>
/// Extension methods for IHasFeedback
/// </summary>
public static class IHasFeedbackExtensions public static class IHasFeedbackExtensions
{ {
/// <summary>
/// Gets the feedback type name for sorting purposes
/// </summary>
/// <param name="feedback">The feedback to get the type name for</param>
/// <returns>A string representing the feedback type</returns>
private static string GetFeedbackTypeName(Feedback feedback)
{
if (feedback is BoolFeedback)
return "boolean";
else if (feedback is IntFeedback)
return "integer";
else if (feedback is StringFeedback)
return "string";
else
return feedback.GetType().Name;
}
/// <summary>
/// Dumps the feedbacks to the console
/// </summary>
/// <param name="source"></param>
/// <param name="getCurrentStates"></param>
public static void DumpFeedbacksToConsole(this IHasFeedback source, bool getCurrentStates) public static void DumpFeedbacksToConsole(this IHasFeedback source, bool getCurrentStates)
{ {
Type t = source.GetType();
// get the properties and set them into a new collection of NameType wrappers
var props = t.GetProperties().Select(p => new PropertyNameType(p, t));
var feedbacks = source.Feedbacks; var feedbacks = source.Feedbacks;
if (feedbacks != null)
if (feedbacks == null || feedbacks.Count == 0)
{ {
CrestronConsole.ConsoleCommandResponse("No available feedbacks\r\n"); Debug.LogMessage(LogEventLevel.Information, source, "\n\nAvailable feedbacks:");
return; foreach (var f in feedbacks)
}
CrestronConsole.ConsoleCommandResponse("Available feedbacks:\r\n");
// Sort feedbacks by type first, then by key
var sortedFeedbacks = feedbacks.OrderBy(f => GetFeedbackTypeName(f)).ThenBy(f => string.IsNullOrEmpty(f.Key) ? "" : f.Key);
foreach (var feedback in sortedFeedbacks)
{
string value = "";
string type = "";
if (getCurrentStates)
{ {
if (feedback is BoolFeedback) string val = "";
string type = "";
if (getCurrentStates)
{ {
value = feedback.BoolValue.ToString(); if (f is BoolFeedback)
type = "boolean"; {
} val = f.BoolValue.ToString();
else if (feedback is IntFeedback) type = "boolean";
{ }
value = feedback.IntValue.ToString(); else if (f is IntFeedback)
type = "integer"; {
} val = f.IntValue.ToString();
else if (feedback is StringFeedback) type = "integer";
{ }
value = feedback.StringValue; else if (f is StringFeedback)
type = "string"; {
val = f.StringValue;
type = "string";
}
} }
Debug.LogMessage(LogEventLevel.Information, "{0,-12} {1, -25} {2}", type,
(string.IsNullOrEmpty(f.Key) ? "-no key-" : f.Key), val);
} }
CrestronConsole.ConsoleCommandResponse($" {type,-12} {(string.IsNullOrEmpty(feedback.Key) ? "-no key-" : feedback.Key),-25} {value}\r\n");
} }
else
Debug.LogMessage(LogEventLevel.Information, source, "No available outputs:");
} }
} }
} }

View File

@@ -25,7 +25,7 @@
<DocumentationFile>bin\$(Configuration)\PepperDash_Essentials_Core.xml</DocumentationFile> <DocumentationFile>bin\$(Configuration)\PepperDash_Essentials_Core.xml</DocumentationFile>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Crestron.SimplSharp.SDK.ProgramLibrary" Version="2.21.157" /> <PackageReference Include="Crestron.SimplSharp.SDK.ProgramLibrary" Version="2.21.90" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="Crestron\CrestronGenericBaseDevice.cs.orig" /> <None Include="Crestron\CrestronGenericBaseDevice.cs.orig" />

View File

@@ -9,11 +9,11 @@ using Serilog.Events;
namespace PepperDash.Essentials.Room.Config namespace PepperDash.Essentials.Room.Config
{ {
/// <summary> /// <summary>
/// Represents a EssentialsRoomConfigHelper /// Represents a EssentialsRoomConfigHelper
/// </summary> /// </summary>
public class EssentialsRoomConfigHelper public class EssentialsRoomConfigHelper
{ {
/// <summary> /// <summary>
/// GetEmergency method /// GetEmergency method
/// </summary> /// </summary>
@@ -31,100 +31,100 @@ namespace PepperDash.Essentials.Room.Config
return null; return null;
} }
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
/// <param name="props"></param> /// <param name="props"></param>
/// <param name="room"></param> /// <param name="room"></param>
/// <returns></returns> /// <returns></returns>
/// <summary> /// <summary>
/// GetMicrophonePrivacy method /// GetMicrophonePrivacy method
/// </summary> /// </summary>
public static MicrophonePrivacyController GetMicrophonePrivacy( public static MicrophonePrivacyController GetMicrophonePrivacy(
EssentialsRoomPropertiesConfig props, IPrivacy room) EssentialsRoomPropertiesConfig props, IPrivacy room)
{ {
var microphonePrivacy = props.MicrophonePrivacy; var microphonePrivacy = props.MicrophonePrivacy;
if (microphonePrivacy == null) if (microphonePrivacy == null)
{ {
Debug.LogMessage(LogEventLevel.Information, "Cannot create microphone privacy with null properties"); Debug.LogMessage(LogEventLevel.Information, "Cannot create microphone privacy with null properties");
return null; return null;
} }
// Get the MicrophonePrivacy device from the device manager // Get the MicrophonePrivacy device from the device manager
var mP = (DeviceManager.GetDeviceForKey(props.MicrophonePrivacy.DeviceKey) as MicrophonePrivacyController); var mP = (DeviceManager.GetDeviceForKey(props.MicrophonePrivacy.DeviceKey) as MicrophonePrivacyController);
// Set this room as the IPrivacy device // Set this room as the IPrivacy device
if (mP == null) if (mP == null)
{ {
Debug.LogMessage(LogEventLevel.Information, "ERROR: Selected device {0} is not MicrophonePrivacyController", props.MicrophonePrivacy.DeviceKey); Debug.LogMessage(LogEventLevel.Information, "ERROR: Selected device {0} is not MicrophonePrivacyController", props.MicrophonePrivacy.DeviceKey);
return null; return null;
} }
mP.SetPrivacyDevice(room); mP.SetPrivacyDevice(room);
var behaviour = props.MicrophonePrivacy.Behaviour.ToLower(); var behaviour = props.MicrophonePrivacy.Behaviour.ToLower();
if (behaviour == null) if (behaviour == null)
{ {
Debug.LogMessage(LogEventLevel.Information, "WARNING: No behaviour defined for MicrophonePrivacyController"); Debug.LogMessage(LogEventLevel.Information, "WARNING: No behaviour defined for MicrophonePrivacyController");
return null; return null;
} }
if (behaviour == "trackroomstate") if (behaviour == "trackroomstate")
{ {
// Tie LED enable to room power state // Tie LED enable to room power state
var essRoom = room as IEssentialsRoom; var essRoom = room as IEssentialsRoom;
essRoom.OnFeedback.OutputChange += (o, a) => essRoom.OnFeedback.OutputChange += (o, a) =>
{ {
if (essRoom.OnFeedback.BoolValue) if (essRoom.OnFeedback.BoolValue)
mP.EnableLeds = true; mP.EnableLeds = true;
else else
mP.EnableLeds = false; mP.EnableLeds = false;
}; };
mP.EnableLeds = essRoom.OnFeedback.BoolValue; mP.EnableLeds = essRoom.OnFeedback.BoolValue;
} }
else if (behaviour == "trackcallstate") else if (behaviour == "trackcallstate")
{ {
// Tie LED enable to room power state // Tie LED enable to room power state
var inCallRoom = room as IHasInCallFeedback; var inCallRoom = room as IHasInCallFeedback;
inCallRoom.InCallFeedback.OutputChange += (o, a) => inCallRoom.InCallFeedback.OutputChange += (o, a) =>
{ {
if (inCallRoom.InCallFeedback.BoolValue) if (inCallRoom.InCallFeedback.BoolValue)
mP.EnableLeds = true; mP.EnableLeds = true;
else else
mP.EnableLeds = false; mP.EnableLeds = false;
}; };
mP.EnableLeds = inCallRoom.InCallFeedback.BoolValue; mP.EnableLeds = inCallRoom.InCallFeedback.BoolValue;
} }
return mP; return mP;
} }
} }
/// <summary> /// <summary>
/// Represents a EssentialsRoomPropertiesConfig /// Represents a EssentialsRoomPropertiesConfig
/// </summary> /// </summary>
public class EssentialsRoomPropertiesConfig public class EssentialsRoomPropertiesConfig
{ {
[JsonProperty("addresses")] [JsonProperty("addresses")]
public EssentialsRoomAddressPropertiesConfig Addresses { get; set; } public EssentialsRoomAddressPropertiesConfig Addresses { get; set; }
[JsonProperty("description")] [JsonProperty("description")]
public string Description { get; set; } public string Description { get; set; }
[JsonProperty("emergency")] [JsonProperty("emergency")]
public EssentialsRoomEmergencyConfig Emergency { get; set; } public EssentialsRoomEmergencyConfig Emergency { get; set; }
/// <summary> [JsonProperty("help")]
/// Gets or sets the Help /// <summary>
/// </summary> /// Gets or sets the Help
[JsonProperty("help")] /// </summary>
public EssentialsHelpPropertiesConfig Help { get; set; } public EssentialsHelpPropertiesConfig Help { get; set; }
/// <summary> [JsonProperty("helpMessage")]
/// Gets or sets the HelpMessage /// <summary>
/// </summary> /// Gets or sets the HelpMessage
[JsonProperty("helpMessage")] /// </summary>
public string HelpMessage { get; set; } public string HelpMessage { get; set; }
/// <summary> /// <summary>
/// Read this value to get the help message. It checks for the old and new config format. /// Read this value to get the help message. It checks for the old and new config format.
@@ -133,7 +133,7 @@ namespace PepperDash.Essentials.Room.Config
{ {
get get
{ {
if (Help != null && !string.IsNullOrEmpty(Help.Message)) if(Help != null && !string.IsNullOrEmpty(Help.Message))
{ {
return Help.Message; return Help.Message;
} }
@@ -144,83 +144,83 @@ namespace PepperDash.Essentials.Room.Config
} }
} }
/// <summary> [JsonProperty("environment")]
/// Gets or sets the Environment /// <summary>
/// </summary> /// Gets or sets the Environment
[JsonProperty("environment")] /// </summary>
public EssentialsEnvironmentPropertiesConfig Environment { get; set; } public EssentialsEnvironmentPropertiesConfig Environment { get; set; }
/// <summary> [JsonProperty("logo")]
/// Gets or sets the LogoLight /// <summary>
/// </summary> /// Gets or sets the LogoLight
[JsonProperty("logo")] /// </summary>
public EssentialsLogoPropertiesConfig LogoLight { get; set; } public EssentialsLogoPropertiesConfig LogoLight { get; set; }
[JsonProperty("logoDark")]
/// <summary> /// <summary>
/// Gets or sets the LogoDark /// Gets or sets the LogoDark
/// </summary> /// </summary>
[JsonProperty("logoDark")]
public EssentialsLogoPropertiesConfig LogoDark { get; set; } public EssentialsLogoPropertiesConfig LogoDark { get; set; }
/// <summary> [JsonProperty("microphonePrivacy")]
/// Gets or sets the MicrophonePrivacy /// <summary>
/// </summary> /// Gets or sets the MicrophonePrivacy
[JsonProperty("microphonePrivacy")] /// </summary>
public EssentialsRoomMicrophonePrivacyConfig MicrophonePrivacy { get; set; } public EssentialsRoomMicrophonePrivacyConfig MicrophonePrivacy { get; set; }
/// <summary> [JsonProperty("occupancy")]
/// Gets or sets the Occupancy /// <summary>
/// </summary> /// Gets or sets the Occupancy
[JsonProperty("occupancy")] /// </summary>
public EssentialsRoomOccSensorConfig Occupancy { get; set; } public EssentialsRoomOccSensorConfig Occupancy { get; set; }
/// <summary> [JsonProperty("oneButtonMeeting")]
/// Gets or sets the OneButtonMeeting /// <summary>
/// </summary> /// Gets or sets the OneButtonMeeting
[JsonProperty("oneButtonMeeting")] /// </summary>
public EssentialsOneButtonMeetingPropertiesConfig OneButtonMeeting { get; set; } public EssentialsOneButtonMeetingPropertiesConfig OneButtonMeeting { get; set; }
/// <summary> [JsonProperty("shutdownVacancySeconds")]
/// Gets or sets the ShutdownVacancySeconds /// <summary>
/// </summary> /// Gets or sets the ShutdownVacancySeconds
[JsonProperty("shutdownVacancySeconds")] /// </summary>
public int ShutdownVacancySeconds { get; set; } public int ShutdownVacancySeconds { get; set; }
/// <summary> [JsonProperty("shutdownPromptSeconds")]
/// Gets or sets the ShutdownPromptSeconds /// <summary>
/// </summary> /// Gets or sets the ShutdownPromptSeconds
[JsonProperty("shutdownPromptSeconds")] /// </summary>
public int ShutdownPromptSeconds { get; set; } public int ShutdownPromptSeconds { get; set; }
/// <summary> [JsonProperty("tech")]
/// Gets or sets the Tech /// <summary>
/// </summary> /// Gets or sets the Tech
[JsonProperty("tech")] /// </summary>
public EssentialsRoomTechConfig Tech { get; set; } public EssentialsRoomTechConfig Tech { get; set; }
/// <summary> [JsonProperty("volumes")]
/// Gets or sets the Volumes /// <summary>
/// </summary> /// Gets or sets the Volumes
[JsonProperty("volumes")] /// </summary>
public EssentialsRoomVolumesConfig Volumes { get; set; } public EssentialsRoomVolumesConfig Volumes { get; set; }
[JsonProperty("fusion")]
/// <summary> /// <summary>
/// Gets or sets the Fusion /// Gets or sets the Fusion
/// </summary> /// </summary>
[JsonProperty("fusion")]
public EssentialsRoomFusionConfig Fusion { get; set; } public EssentialsRoomFusionConfig Fusion { get; set; }
[JsonProperty("essentialsRoomUiBehaviorConfig", NullValueHandling=NullValueHandling.Ignore)]
/// <summary> /// <summary>
/// Gets or sets the UiBehavior /// Gets or sets the UiBehavior
/// </summary> /// </summary>
[JsonProperty("essentialsRoomUiBehaviorConfig", NullValueHandling = NullValueHandling.Ignore)]
public EssentialsRoomUiBehaviorConfig UiBehavior { get; set; } public EssentialsRoomUiBehaviorConfig UiBehavior { get; set; }
/// <summary> [JsonProperty("zeroVolumeWhenSwtichingVolumeDevices")]
/// Gets or sets the ZeroVolumeWhenSwtichingVolumeDevices /// <summary>
/// </summary> /// Gets or sets the ZeroVolumeWhenSwtichingVolumeDevices
[JsonProperty("zeroVolumeWhenSwtichingVolumeDevices")] /// </summary>
public bool ZeroVolumeWhenSwtichingVolumeDevices { get; set; } public bool ZeroVolumeWhenSwtichingVolumeDevices { get; set; }
/// <summary> /// <summary>
/// Indicates if this room represents a combination of other rooms /// Indicates if this room represents a combination of other rooms
@@ -236,7 +236,7 @@ namespace PepperDash.Essentials.Room.Config
LogoLight = new EssentialsLogoPropertiesConfig(); LogoLight = new EssentialsLogoPropertiesConfig();
LogoDark = new EssentialsLogoPropertiesConfig(); LogoDark = new EssentialsLogoPropertiesConfig();
} }
} }
/// <summary> /// <summary>
/// Represents a EssentialsRoomUiBehaviorConfig /// Represents a EssentialsRoomUiBehaviorConfig
@@ -327,15 +327,15 @@ namespace PepperDash.Essentials.Room.Config
} }
/// <summary> /// <summary>
/// Represents a EssentialsEnvironmentPropertiesConfig /// Represents a EssentialsEnvironmentPropertiesConfig
/// </summary> /// </summary>
public class EssentialsEnvironmentPropertiesConfig public class EssentialsEnvironmentPropertiesConfig
{ {
/// <summary> /// <summary>
/// Gets or sets the Enabled /// Gets or sets the Enabled
/// </summary> /// </summary>
public bool Enabled { get; set; } public bool Enabled { get; set; }
[JsonProperty("deviceKeys")] [JsonProperty("deviceKeys")]
/// <summary> /// <summary>
@@ -348,7 +348,7 @@ namespace PepperDash.Essentials.Room.Config
DeviceKeys = new List<string>(); DeviceKeys = new List<string>();
} }
} }
/// <summary> /// <summary>
/// Represents a EssentialsRoomFusionConfig /// Represents a EssentialsRoomFusionConfig
@@ -390,17 +390,17 @@ namespace PepperDash.Essentials.Room.Config
/// </summary> /// </summary>
public class EssentialsRoomMicrophonePrivacyConfig public class EssentialsRoomMicrophonePrivacyConfig
{ {
[JsonProperty("deviceKey")] [JsonProperty("deviceKey")]
/// <summary> /// <summary>
/// Gets or sets the DeviceKey /// Gets or sets the DeviceKey
/// </summary> /// </summary>
public string DeviceKey { get; set; } public string DeviceKey { get; set; }
[JsonProperty("behaviour")] [JsonProperty("behaviour")]
/// <summary> /// <summary>
/// Gets or sets the Behaviour /// Gets or sets the Behaviour
/// </summary> /// </summary>
public string Behaviour { get; set; } public string Behaviour { get; set; }
} }
/// <summary> /// <summary>
@@ -408,23 +408,23 @@ namespace PepperDash.Essentials.Room.Config
/// </summary> /// </summary>
public class EssentialsHelpPropertiesConfig public class EssentialsHelpPropertiesConfig
{ {
[JsonProperty("message")] [JsonProperty("message")]
/// <summary> /// <summary>
/// Gets or sets the Message /// Gets or sets the Message
/// </summary> /// </summary>
public string Message { get; set; } public string Message { get; set; }
[JsonProperty("showCallButton")] [JsonProperty("showCallButton")]
public bool ShowCallButton { get; set; } public bool ShowCallButton { get; set; }
/// <summary> /// <summary>
/// Defaults to "Call Help Desk" /// Defaults to "Call Help Desk"
/// </summary> /// </summary>
[JsonProperty("callButtonText")] [JsonProperty("callButtonText")]
/// <summary> /// <summary>
/// Gets or sets the CallButtonText /// Gets or sets the CallButtonText
/// </summary> /// </summary>
public string CallButtonText { get; set; } public string CallButtonText { get; set; }
public EssentialsHelpPropertiesConfig() public EssentialsHelpPropertiesConfig()
{ {
@@ -437,23 +437,23 @@ namespace PepperDash.Essentials.Room.Config
/// </summary> /// </summary>
public class EssentialsOneButtonMeetingPropertiesConfig public class EssentialsOneButtonMeetingPropertiesConfig
{ {
[JsonProperty("enable")] [JsonProperty("enable")]
/// <summary> /// <summary>
/// Gets or sets the Enable /// Gets or sets the Enable
/// </summary> /// </summary>
public bool Enable { get; set; } public bool Enable { get; set; }
} }
public class EssentialsRoomAddressPropertiesConfig public class EssentialsRoomAddressPropertiesConfig
{ {
[JsonProperty("phoneNumber")] [JsonProperty("phoneNumber")]
public string PhoneNumber { get; set; } public string PhoneNumber { get; set; }
[JsonProperty("sipAddress")] [JsonProperty("sipAddress")]
/// <summary> /// <summary>
/// Gets or sets the SipAddress /// Gets or sets the SipAddress
/// </summary> /// </summary>
public string SipAddress { get; set; } public string SipAddress { get; set; }
} }
@@ -462,14 +462,14 @@ namespace PepperDash.Essentials.Room.Config
/// </summary> /// </summary>
public class EssentialsLogoPropertiesConfig public class EssentialsLogoPropertiesConfig
{ {
[JsonProperty("type")] [JsonProperty("type")]
/// <summary> /// <summary>
/// Gets or sets the Type /// Gets or sets the Type
/// </summary> /// </summary>
public string Type { get; set; } public string Type { get; set; }
[JsonProperty("url")] [JsonProperty("url")]
public string Url { get; set; } public string Url { get; set; }
/// <summary> /// <summary>
/// GetLogoUrlLight method /// GetLogoUrlLight method
/// </summary> /// </summary>
@@ -502,22 +502,22 @@ namespace PepperDash.Essentials.Room.Config
/// </summary> /// </summary>
public class EssentialsRoomOccSensorConfig public class EssentialsRoomOccSensorConfig
{ {
[JsonProperty("deviceKey")] [JsonProperty("deviceKey")]
/// <summary> /// <summary>
/// Gets or sets the DeviceKey /// Gets or sets the DeviceKey
/// </summary> /// </summary>
public string DeviceKey { get; set; } public string DeviceKey { get; set; }
[JsonProperty("timeoutMinutes")] [JsonProperty("timeoutMinutes")]
public int TimeoutMinutes { get; set; } public int TimeoutMinutes { get; set; }
} }
public class EssentialsRoomTechConfig public class EssentialsRoomTechConfig
{ {
[JsonProperty("password")] [JsonProperty("password")]
/// <summary> /// <summary>
/// Gets or sets the Password /// Gets or sets the Password
/// </summary> /// </summary>
public string Password { get; set; } public string Password { get; set; }
} }
} }

View File

@@ -14,14 +14,14 @@ using Serilog.Events;
namespace PepperDash.Essentials.Core namespace PepperDash.Essentials.Core
{ {
////***************************************************************************** ////*****************************************************************************
///// <summary> /// <summary>
///// Base class for all subpage reference list controllers /// Base class for all subpage reference list controllers
///// </summary> /// </summary>
//public class SubpageReferenceListController //public class SubpageReferenceListController
//{ //{
// public SubpageReferenceList TheList { get; protected set; } // public SubpageReferenceList TheList { get; protected set; }
//} //}
//***************************************************************************** //*****************************************************************************
/// <summary> /// <summary>
@@ -34,26 +34,26 @@ namespace PepperDash.Essentials.Core
public ushort Count public ushort Count
{ {
get { return SetNumberOfItemsSig.UShortValue; } get { return SetNumberOfItemsSig.UShortValue; }
set { SetNumberOfItemsSig.UShortValue = value; } set { SetNumberOfItemsSig.UShortValue = value; }
} }
public ushort MaxDefinedItems { get; private set; } public ushort MaxDefinedItems { get; private set; }
/// <summary> /// <summary>
/// Gets or sets the ScrollToItemSig /// Gets or sets the ScrollToItemSig
/// </summary> /// </summary>
public UShortInputSig ScrollToItemSig { get; private set; } public UShortInputSig ScrollToItemSig { get; private set; }
UShortInputSig SetNumberOfItemsSig; UShortInputSig SetNumberOfItemsSig;
/// <summary> /// <summary>
/// Gets or sets the BoolIncrement /// Gets or sets the BoolIncrement
/// </summary> /// </summary>
public uint BoolIncrement { get; protected set; } public uint BoolIncrement { get; protected set; }
/// <summary> /// <summary>
/// Gets or sets the UShortIncrement /// Gets or sets the UShortIncrement
/// </summary> /// </summary>
public uint UShortIncrement { get; protected set; } public uint UShortIncrement { get; protected set; }
/// <summary> /// <summary>
/// Gets or sets the StringIncrement /// Gets or sets the StringIncrement
/// </summary> /// </summary>
public uint StringIncrement { get; protected set; } public uint StringIncrement { get; protected set; }
protected readonly SmartObject SRL; protected readonly SmartObject SRL;
@@ -88,7 +88,7 @@ namespace PepperDash.Essentials.Core
} }
else else
Debug.LogMessage(LogEventLevel.Information, "ERROR: TriList 0x{0:X2} Cannot load smart object {1}. Verify correct SGD file is loaded", Debug.LogMessage(LogEventLevel.Information, "ERROR: TriList 0x{0:X2} Cannot load smart object {1}. Verify correct SGD file is loaded",
triList.ID, smartObjectId); triList.ID, smartObjectId);
} }
/// <summary> /// <summary>
@@ -96,17 +96,17 @@ namespace PepperDash.Essentials.Core
/// DOES NOT adjust Count /// DOES NOT adjust Count
/// </summary> /// </summary>
/// <param name="item"></param> /// <param name="item"></param>
/// <summary> /// <summary>
/// AddItem method /// AddItem method
/// </summary> /// </summary>
public void AddItem(SubpageReferenceListItem item) public void AddItem(SubpageReferenceListItem item)
{ {
Items.Add(item); Items.Add(item);
} }
/// <summary> /// <summary>
/// Clear method /// Clear method
/// </summary> /// </summary>
public void Clear() public void Clear()
{ {
// If a line item needs to disconnect an CueActionPair or do something to release RAM // If a line item needs to disconnect an CueActionPair or do something to release RAM
@@ -116,12 +116,12 @@ namespace PepperDash.Essentials.Core
// Clean up the SRL // Clean up the SRL
Count = 1; Count = 1;
ScrollToItemSig.UShortValue = 1; ScrollToItemSig.UShortValue = 1;
} }
/// <summary> /// <summary>
/// Refresh method /// Refresh method
/// </summary> /// </summary>
public void Refresh() public void Refresh()
{ {
foreach (var item in Items) item.Refresh(); foreach (var item in Items) item.Refresh();
@@ -157,7 +157,7 @@ namespace PepperDash.Essentials.Core
public UShortOutputSig GetUShortOutputSig(uint index, uint sigNum) public UShortOutputSig GetUShortOutputSig(uint index, uint sigNum)
{ {
if (sigNum > UShortIncrement) return null; if (sigNum > UShortIncrement) return null;
return SRL.UShortOutput.FirstOrDefault(s => s.Name.Equals(GetUShortOutputSigName(index, sigNum))); return SRL.UShortOutput.FirstOrDefault(s => s.Name.Equals(GetUShortOutputSigName(index, sigNum)));
} }
/// <summary> /// <summary>
@@ -172,7 +172,7 @@ namespace PepperDash.Essentials.Core
public StringOutputSig GetStringOutputSig(uint index, uint sigNum) public StringOutputSig GetStringOutputSig(uint index, uint sigNum)
{ {
if (sigNum > StringIncrement) return null; if (sigNum > StringIncrement) return null;
return SRL.StringOutput.FirstOrDefault(s => s.Name.Equals(GetStringOutputSigName(index, sigNum))); return SRL.StringOutput.FirstOrDefault(s => s.Name.Equals(GetStringOutputSigName(index, sigNum)));
} }
/// <summary> /// <summary>
@@ -263,9 +263,9 @@ namespace PepperDash.Essentials.Core
/// </summary> /// </summary>
/// <param name="currentDevice"></param> /// <param name="currentDevice"></param>
/// <param name="args"></param> /// <param name="args"></param>
/// <summary> /// <summary>
/// SRL_SigChange method /// SRL_SigChange method
/// </summary> /// </summary>
public static void SRL_SigChange(GenericBase currentDevice, SmartObjectEventArgs args) public static void SRL_SigChange(GenericBase currentDevice, SmartObjectEventArgs args)
{ {
var uo = args.Sig.UserObject; var uo = args.Sig.UserObject;

View File

@@ -6,9 +6,9 @@ using Serilog.Events;
namespace PepperDash.Essentials.Core namespace PepperDash.Essentials.Core
{ {
/// <summary> /// <summary>
/// Represents a ModalDialog /// Represents a ModalDialog
/// </summary> /// </summary>
public class ModalDialog public class ModalDialog
{ {
/// <summary> /// <summary>
@@ -19,10 +19,10 @@ namespace PepperDash.Essentials.Core
/// Bool press 3992 /// Bool press 3992
/// </summary> /// </summary>
public const uint Button2Join = 3992; public const uint Button2Join = 3992;
/// <summary> /// <summary>
/// 3993 /// 3993
/// </summary> /// </summary>
public const uint CancelButtonJoin = 3993; public const uint CancelButtonJoin = 3993;
/// <summary> /// <summary>
///For visibility of single button. Bool feedback 3994 ///For visibility of single button. Bool feedback 3994
/// </summary> /// </summary>
@@ -35,19 +35,19 @@ namespace PepperDash.Essentials.Core
/// Shows the timer guage if in use. Bool feedback 3996 /// Shows the timer guage if in use. Bool feedback 3996
/// </summary> /// </summary>
public const uint TimerVisibleJoin = 3996; public const uint TimerVisibleJoin = 3996;
/// <summary> /// <summary>
/// Visibility join to show "X" button 3997 /// Visibility join to show "X" button 3997
/// </summary> /// </summary>
public const uint CancelVisibleJoin = 3997; public const uint CancelVisibleJoin = 3997;
/// <summary> /// <summary>
/// Shows the modal subpage. Boolean feeback join 3999 /// Shows the modal subpage. Boolean feeback join 3999
/// </summary> /// </summary>
public const uint ModalVisibleJoin = 3999; public const uint ModalVisibleJoin = 3999;
///// <summary> /// <summary>
///// The seconds value of the countdown timer. Ushort join 3991 /// The seconds value of the countdown timer. Ushort join 3991
///// </summary> /// </summary>
//public const uint TimerSecondsJoin = 3991; //public const uint TimerSecondsJoin = 3991;
/// <summary> /// <summary>
/// The full ushort value of the countdown timer for a gauge. Ushort join 3992 /// The full ushort value of the countdown timer for a gauge. Ushort join 3992
/// </summary> /// </summary>
@@ -82,10 +82,10 @@ namespace PepperDash.Essentials.Core
get { return TriList.BooleanInput[ModalVisibleJoin].BoolValue; } get { return TriList.BooleanInput[ModalVisibleJoin].BoolValue; }
} }
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
public bool CanCancel { get; private set; } public bool CanCancel { get; private set; }
BasicTriList TriList; BasicTriList TriList;
@@ -103,10 +103,10 @@ namespace PepperDash.Essentials.Core
TriList = triList; TriList = triList;
// Attach actions to buttons // Attach actions to buttons
triList.SetSigFalseAction(Button1Join, () => OnModalComplete(1)); triList.SetSigFalseAction(Button1Join, () => OnModalComplete(1));
triList.SetSigFalseAction(Button2Join, () => OnModalComplete(2)); triList.SetSigFalseAction(Button2Join, () => OnModalComplete(2));
triList.SetSigFalseAction(CancelButtonJoin, () => { if (CanCancel) CancelDialog(); }); triList.SetSigFalseAction(CancelButtonJoin, () => { if (CanCancel) CancelDialog(); });
CanCancel = true; CanCancel = true;
} }
/// <summary> /// <summary>
@@ -151,15 +151,15 @@ namespace PepperDash.Essentials.Core
TriList.StringInput[Button2TextJoin].StringValue = button2Text; TriList.StringInput[Button2TextJoin].StringValue = button2Text;
} }
// Show/hide guage // Show/hide guage
TriList.BooleanInput[TimerVisibleJoin].BoolValue = showGauge; TriList.BooleanInput[TimerVisibleJoin].BoolValue = showGauge;
CanCancel = showCancel; CanCancel = showCancel;
TriList.BooleanInput[CancelVisibleJoin].BoolValue = showCancel; TriList.BooleanInput[CancelVisibleJoin].BoolValue = showCancel;
//Reveal and activate //Reveal and activate
TriList.BooleanInput[ModalVisibleJoin].BoolValue = true; TriList.BooleanInput[ModalVisibleJoin].BoolValue = true;
WakePanel(); WakePanel();
return true; return true;
} }
@@ -167,48 +167,48 @@ namespace PepperDash.Essentials.Core
return false; return false;
} }
/// <summary> /// <summary>
/// WakePanel method /// WakePanel method
/// </summary> /// </summary>
public void WakePanel() public void WakePanel()
{ {
try try
{ {
var panel = TriList as TswFt5Button; var panel = TriList as TswFt5Button;
if (panel != null && panel.ExtenderSystemReservedSigs.BacklightOffFeedback.BoolValue) if (panel != null && panel.ExtenderSystemReservedSigs.BacklightOffFeedback.BoolValue)
panel.ExtenderSystemReservedSigs.BacklightOn(); panel.ExtenderSystemReservedSigs.BacklightOn();
} }
catch catch
{ {
Debug.LogMessage(LogEventLevel.Debug, "Error Waking Panel. Maybe testing with Xpanel?"); Debug.LogMessage(LogEventLevel.Debug, "Error Waking Panel. Maybe testing with Xpanel?");
} }
} }
/// <summary> /// <summary>
/// CancelDialog method /// CancelDialog method
/// </summary> /// </summary>
public void CancelDialog() public void CancelDialog()
{ {
OnModalComplete(0); OnModalComplete(0);
} }
/// <summary> /// <summary>
/// Hides dialog. Fires no action /// Hides dialog. Fires no action
/// </summary> /// </summary>
public void HideDialog() public void HideDialog()
{ {
TriList.BooleanInput[ModalVisibleJoin].BoolValue = false; TriList.BooleanInput[ModalVisibleJoin].BoolValue = false;
} }
// When the modal is cleared or times out, clean up the various bits // When the modal is cleared or times out, clean up the various bits
void OnModalComplete(uint buttonNum) void OnModalComplete(uint buttonNum)
{ {
TriList.BooleanInput[ModalVisibleJoin].BoolValue = false; TriList.BooleanInput[ModalVisibleJoin].BoolValue = false;
var action = ModalCompleteAction; var action = ModalCompleteAction;
if (action != null) if (action != null)
action(buttonNum); action(buttonNum);
} }
} }

View File

@@ -29,6 +29,6 @@
<ProjectReference Include="..\PepperDash.Essentials.Core\PepperDash.Essentials.Core.csproj" /> <ProjectReference Include="..\PepperDash.Essentials.Core\PepperDash.Essentials.Core.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Crestron.SimplSharp.SDK.ProgramLibrary" Version="2.21.157" /> <PackageReference Include="Crestron.SimplSharp.SDK.ProgramLibrary" Version="2.21.90" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -0,0 +1,221 @@
using System;
using System.Linq;
using System.Reflection;
using Newtonsoft.Json.Linq;
using PepperDash.Core;
using PepperDash.Core.Logging;
using PepperDash.Essentials.Core;
using PepperDash.Essentials.Devices.Common.Codec;
namespace PepperDash.Essentials.AppServer.Messengers
{
/// <summary>
/// Provides a messaging bridge for devices that implement call status interfaces
/// without requiring VideoCodecBase inheritance
/// </summary>
public class CallStatusMessenger : MessengerBase
{
/// <summary>
/// Device with dialer capabilities
/// </summary>
protected IHasDialer Dialer { get; private set; }
/// <summary>
/// Device with content sharing capabilities (optional)
/// </summary>
protected IHasContentSharing ContentSharing { get; private set; }
/// <summary>
/// Constructor
/// </summary>
/// <param name="key"></param>
/// <param name="dialer"></param>
/// <param name="messagePath"></param>
public CallStatusMessenger(string key, IHasDialer dialer, string messagePath)
: base(key, messagePath, dialer as IKeyName)
{
Dialer = dialer ?? throw new ArgumentNullException(nameof(dialer));
dialer.CallStatusChange += Dialer_CallStatusChange;
// Check for optional content sharing interface
if (dialer is IHasContentSharing contentSharing)
{
ContentSharing = contentSharing;
contentSharing.SharingContentIsOnFeedback.OutputChange += SharingContentIsOnFeedback_OutputChange;
contentSharing.SharingSourceFeedback.OutputChange += SharingSourceFeedback_OutputChange;
}
}
/// <summary>
/// Handles call status changes
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Dialer_CallStatusChange(object sender, CodecCallStatusItemChangeEventArgs e)
{
try
{
SendFullStatus();
}
catch (Exception ex)
{
this.LogError(ex, "Error handling call status change: {error}", ex.Message);
}
}
/// <summary>
/// Handles content sharing status changes
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void SharingContentIsOnFeedback_OutputChange(object sender, FeedbackEventArgs e)
{
PostStatusMessage(JToken.FromObject(new
{
sharingContentIsOn = e.BoolValue
}));
}
/// <summary>
/// Handles sharing source changes
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void SharingSourceFeedback_OutputChange(object sender, FeedbackEventArgs e)
{
PostStatusMessage(JToken.FromObject(new
{
sharingSource = e.StringValue
}));
}
/// <summary>
/// Gets active calls from the dialer
/// </summary>
/// <returns></returns>
private object GetActiveCalls()
{
// Try to get active calls if the dialer has an ActiveCalls property
var dialerType = Dialer.GetType();
var activeCallsProperty = dialerType.GetProperty("ActiveCalls");
if (activeCallsProperty != null && activeCallsProperty.PropertyType == typeof(System.Collections.Generic.List<CodecActiveCallItem>))
{
var activeCalls = activeCallsProperty.GetValue(Dialer) as System.Collections.Generic.List<CodecActiveCallItem>;
return activeCalls ?? new System.Collections.Generic.List<CodecActiveCallItem>();
}
// Return basic call status if no ActiveCalls property
return new { isInCall = Dialer.IsInCall };
}
/// <summary>
/// Sends full status message
/// </summary>
public void SendFullStatus()
{
var status = new
{
isInCall = Dialer.IsInCall,
calls = GetActiveCalls()
};
// Add content sharing status if available
if (ContentSharing != null)
{
var statusWithSharing = new
{
isInCall = Dialer.IsInCall,
calls = GetActiveCalls(),
sharingContentIsOn = ContentSharing.SharingContentIsOnFeedback.BoolValue,
sharingSource = ContentSharing.SharingSourceFeedback.StringValue
};
PostStatusMessage(JToken.FromObject(statusWithSharing));
}
else
{
PostStatusMessage(JToken.FromObject(status));
}
}
/// <summary>
/// Registers actions for call control
/// </summary>
protected override void RegisterActions()
{
base.RegisterActions();
AddAction("/fullStatus", (id, content) => SendFullStatus());
// Basic call control actions
AddAction("/dial", (id, content) =>
{
var msg = content.ToObject<MobileControlSimpleContent<string>>();
Dialer.Dial(msg.Value);
});
AddAction("/endAllCalls", (id, content) => Dialer.EndAllCalls());
AddAction("/dtmf", (id, content) =>
{
var msg = content.ToObject<MobileControlSimpleContent<string>>();
Dialer.SendDtmf(msg.Value);
});
// Call-specific actions (if active calls are available)
AddAction("/endCallById", (id, content) =>
{
var msg = content.ToObject<MobileControlSimpleContent<string>>();
var call = GetCallWithId(msg.Value);
if (call != null)
Dialer.EndCall(call);
});
AddAction("/acceptById", (id, content) =>
{
var msg = content.ToObject<MobileControlSimpleContent<string>>();
var call = GetCallWithId(msg.Value);
if (call != null)
Dialer.AcceptCall(call);
});
AddAction("/rejectById", (id, content) =>
{
var msg = content.ToObject<MobileControlSimpleContent<string>>();
var call = GetCallWithId(msg.Value);
if (call != null)
Dialer.RejectCall(call);
});
// Content sharing actions if available
if (ContentSharing != null)
{
AddAction("/startSharing", (id, content) => ContentSharing.StartSharing());
AddAction("/stopSharing", (id, content) => ContentSharing.StopSharing());
}
}
/// <summary>
/// Finds a call by ID
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
private CodecActiveCallItem GetCallWithId(string id)
{
// Try to get call using reflection for ActiveCalls property
var dialerType = Dialer.GetType();
var activeCallsProperty = dialerType.GetProperty("ActiveCalls");
if (activeCallsProperty != null && activeCallsProperty.PropertyType == typeof(System.Collections.Generic.List<CodecActiveCallItem>))
{
var activeCalls = activeCallsProperty.GetValue(Dialer) as System.Collections.Generic.List<CodecActiveCallItem>;
if (activeCalls != null)
{
return activeCalls.FirstOrDefault(c => c.Id.Equals(id));
}
}
return null;
}
}
}

View File

@@ -140,19 +140,17 @@ namespace PepperDash.Essentials.AppServer.Messengers
if (Camera is IHasCameraPresets presetsCamera) if (Camera is IHasCameraPresets presetsCamera)
{ {
AddAction("/recallPreset", (id, content) => for (int i = 1; i <= 6; i++)
{ {
var msg = content.ToObject<MobileControlSimpleContent<int>>(); var preset = i;
AddAction("/cameraPreset" + i, (id, content) =>
{
var msg = content.ToObject<MobileControlSimpleContent<int>>();
presetsCamera.PresetSelect(msg.Value); presetsCamera.PresetSelect(msg.Value);
}); });
AddAction("/storePreset", (id, content) => }
{
var msg = content.ToObject<MobileControlSimpleContent<int>>();
presetsCamera.PresetStore(msg.Value, string.Empty);
});
} }
} }
@@ -166,8 +164,9 @@ namespace PepperDash.Essentials.AppServer.Messengers
return; return;
} }
timerHandler(Camera.Key, cameraAction); timerHandler(state.Value, cameraAction);
cameraAction(state.Value.Equals("true", StringComparison.InvariantCultureIgnoreCase));
} }
/// <summary> /// <summary>

View File

@@ -1,77 +0,0 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using PepperDash.Core;
using PepperDash.Essentials.Core;
using System.Collections.Generic;
namespace PepperDash.Essentials.AppServer.Messengers
{
/// <summary>
/// Represents a IBasicVideoMuteWithFeedbackMessenger
/// </summary>
public class IBasicVideoMuteWithFeedbackMessenger : MessengerBase
{
private readonly IBasicVideoMuteWithFeedback device;
public IBasicVideoMuteWithFeedbackMessenger(string key, string messagePath, IBasicVideoMuteWithFeedback device)
: base(key, messagePath, device as IKeyName)
{
this.device = device;
}
/// <summary>
/// SendFullStatus method
/// </summary>
public void SendFullStatus()
{
var messageObj = new IBasicVideoMuteWithFeedbackMessage
{
VideoMuteState = device.VideoMuteIsOn.BoolValue
};
PostStatusMessage(messageObj);
}
protected override void RegisterActions()
{
base.RegisterActions();
AddAction("/fullStatus", (id, content) => SendFullStatus());
AddAction("/videoMuteToggle", (id, content) =>
{
device.VideoMuteToggle();
});
AddAction("/videoMuteOn", (id, content) =>
{
device.VideoMuteOn();
});
AddAction("/videoMuteOff", (id, content) =>
{
device.VideoMuteOff();
});
device.VideoMuteIsOn.OutputChange += VideoMuteIsOnFeedback_OutputChange;
}
private void VideoMuteIsOnFeedback_OutputChange(object sender, FeedbackEventArgs args)
{
PostStatusMessage(JToken.FromObject(new
{
videoMuteState = args.BoolValue
})
);
}
}
/// <summary>
/// Represents a IBasicVideoMuteWithFeedbackMessage
/// </summary>
public class IBasicVideoMuteWithFeedbackMessage : DeviceStateMessageBase
{
[JsonProperty("videoMuteState")]
public bool VideoMuteState { get; set; }
}
}

View File

@@ -33,7 +33,7 @@
<Compile Remove="Messengers\SIMPLVtcMessenger.cs" /> <Compile Remove="Messengers\SIMPLVtcMessenger.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Crestron.SimplSharp.SDK.ProgramLibrary" Version="2.21.157" /> <PackageReference Include="Crestron.SimplSharp.SDK.ProgramLibrary" Version="2.21.90" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\PepperDash.Core\PepperDash.Core.csproj" /> <ProjectReference Include="..\PepperDash.Core\PepperDash.Core.csproj" />

View File

@@ -1,6 +1,6 @@
using System.Collections.Generic; using Newtonsoft.Json;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters; using Newtonsoft.Json.Converters;
using System.Collections.Generic;
namespace PepperDash.Essentials namespace PepperDash.Essentials
{ {
@@ -9,34 +9,25 @@ namespace PepperDash.Essentials
/// </summary> /// </summary>
public class MobileControlConfig public class MobileControlConfig
{ {
/// <summary>
/// Gets or sets the ServerUrl
/// </summary>
[JsonProperty("serverUrl")] [JsonProperty("serverUrl")]
public string ServerUrl { get; set; } public string ServerUrl { get; set; }
/// <summary>
/// Gets or sets the ClientAppUrl
/// </summary>
[JsonProperty("clientAppUrl")] [JsonProperty("clientAppUrl")]
public string ClientAppUrl { get; set; } public string ClientAppUrl { get; set; }
/// <summary>
/// Gets or sets the DirectServer
/// </summary>
[JsonProperty("directServer")] [JsonProperty("directServer")]
public MobileControlDirectServerPropertiesConfig DirectServer { get; set; } public MobileControlDirectServerPropertiesConfig DirectServer { get; set; }
[JsonProperty("applicationConfig")]
/// <summary> /// <summary>
/// Gets or sets the ApplicationConfig /// Gets or sets the ApplicationConfig
/// </summary> /// </summary>
[JsonProperty("applicationConfig")]
public MobileControlApplicationConfig ApplicationConfig { get; set; } = null; public MobileControlApplicationConfig ApplicationConfig { get; set; } = null;
[JsonProperty("enableApiServer")]
/// <summary> /// <summary>
/// Gets or sets the EnableApiServer /// Gets or sets the EnableApiServer
/// </summary> /// </summary>
[JsonProperty("enableApiServer")]
public bool EnableApiServer { get; set; } = true; public bool EnableApiServer { get; set; } = true;
} }
@@ -45,42 +36,27 @@ namespace PepperDash.Essentials
/// </summary> /// </summary>
public class MobileControlDirectServerPropertiesConfig public class MobileControlDirectServerPropertiesConfig
{ {
[JsonProperty("enableDirectServer")]
/// <summary> /// <summary>
/// Gets or sets the EnableDirectServer /// Gets or sets the EnableDirectServer
/// </summary> /// </summary>
[JsonProperty("enableDirectServer")]
public bool EnableDirectServer { get; set; } public bool EnableDirectServer { get; set; }
[JsonProperty("port")]
/// <summary> /// <summary>
/// Gets or sets the Port /// Gets or sets the Port
/// </summary> /// </summary>
[JsonProperty("port")]
public int Port { get; set; } public int Port { get; set; }
[JsonProperty("logging")]
/// <summary> /// <summary>
/// Gets or sets the Logging /// Gets or sets the Logging
/// </summary> /// </summary>
[JsonProperty("logging")]
public MobileControlLoggingConfig Logging { get; set; } public MobileControlLoggingConfig Logging { get; set; }
/// <summary>
/// Gets or sets the AutomaticallyForwardPortToCSLAN
/// </summary>
[JsonProperty("automaticallyForwardPortToCSLAN")] [JsonProperty("automaticallyForwardPortToCSLAN")]
public bool? AutomaticallyForwardPortToCSLAN { get; set; } public bool? AutomaticallyForwardPortToCSLAN { get; set; }
/// <summary>
/// Gets or sets the CSLanUiDeviceKeys
/// </summary>
/// <remarks>
/// A list of device keys for the CS LAN UI. These devices will get the CS LAN IP address instead of the LAN IP Address
/// </remarks>
[JsonProperty("csLanUiDeviceKeys")]
public List<string> CSLanUiDeviceKeys { get; set; }
/// <summary>
/// Initializes a new instance of the MobileControlDirectServerPropertiesConfig class.
/// </summary>
public MobileControlDirectServerPropertiesConfig() public MobileControlDirectServerPropertiesConfig()
{ {
Logging = new MobileControlLoggingConfig(); Logging = new MobileControlLoggingConfig();
@@ -92,26 +68,26 @@ namespace PepperDash.Essentials
/// </summary> /// </summary>
public class MobileControlLoggingConfig public class MobileControlLoggingConfig
{ {
[JsonProperty("enableRemoteLogging")]
/// <summary> /// <summary>
/// Gets or sets the EnableRemoteLogging /// Gets or sets the EnableRemoteLogging
/// </summary> /// </summary>
[JsonProperty("enableRemoteLogging")]
public bool EnableRemoteLogging { get; set; } public bool EnableRemoteLogging { get; set; }
[JsonProperty("host")]
/// <summary> /// <summary>
/// Gets or sets the Host /// Gets or sets the Host
/// </summary> /// </summary>
[JsonProperty("host")]
public string Host { get; set; } public string Host { get; set; }
[JsonProperty("port")]
/// <summary> /// <summary>
/// Gets or sets the Port /// Gets or sets the Port
/// </summary> /// </summary>
[JsonProperty("port")]
public int Port { get; set; } public int Port { get; set; }
} }
/// <summary> /// <summary>
@@ -119,16 +95,16 @@ namespace PepperDash.Essentials
/// </summary> /// </summary>
public class MobileControlRoomBridgePropertiesConfig public class MobileControlRoomBridgePropertiesConfig
{ {
[JsonProperty("key")]
/// <summary> /// <summary>
/// Gets or sets the Key /// Gets or sets the Key
/// </summary> /// </summary>
[JsonProperty("key")]
public string Key { get; set; } public string Key { get; set; }
[JsonProperty("roomKey")]
/// <summary> /// <summary>
/// Gets or sets the RoomKey /// Gets or sets the RoomKey
/// </summary> /// </summary>
[JsonProperty("roomKey")]
public string RoomKey { get; set; } public string RoomKey { get; set; }
} }
@@ -137,71 +113,53 @@ namespace PepperDash.Essentials
/// </summary> /// </summary>
public class MobileControlSimplRoomBridgePropertiesConfig public class MobileControlSimplRoomBridgePropertiesConfig
{ {
[JsonProperty("eiscId")]
/// <summary> /// <summary>
/// Gets or sets the EiscId /// Gets or sets the EiscId
/// </summary> /// </summary>
[JsonProperty("eiscId")]
public string EiscId { get; set; } public string EiscId { get; set; }
} }
/// <summary>
/// Represents a MobileControlApplicationConfig
/// </summary>
public class MobileControlApplicationConfig public class MobileControlApplicationConfig
{ {
/// <summary>
/// Gets or sets the ApiPath
/// </summary>
[JsonProperty("apiPath")] [JsonProperty("apiPath")]
public string ApiPath { get; set; } public string ApiPath { get; set; }
[JsonProperty("gatewayAppPath")]
/// <summary> /// <summary>
/// Gets or sets the GatewayAppPath /// Gets or sets the GatewayAppPath
/// </summary> /// </summary>
[JsonProperty("gatewayAppPath")]
public string GatewayAppPath { get; set; } public string GatewayAppPath { get; set; }
/// <summary>
/// Gets or sets the EnableDev
/// </summary>
[JsonProperty("enableDev")] [JsonProperty("enableDev")]
public bool? EnableDev { get; set; } public bool? EnableDev { get; set; }
[JsonProperty("logoPath")]
/// <summary> /// <summary>
/// Gets or sets the LogoPath /// Gets or sets the LogoPath
/// </summary> /// </summary>
[JsonProperty("logoPath")]
public string LogoPath { get; set; } public string LogoPath { get; set; }
/// <summary>
/// Gets or sets the IconSet
/// </summary>
[JsonProperty("iconSet")] [JsonProperty("iconSet")]
[JsonConverter(typeof(StringEnumConverter))] [JsonConverter(typeof(StringEnumConverter))]
public MCIconSet? IconSet { get; set; } public MCIconSet? IconSet { get; set; }
/// <summary>
/// Gets or sets the LoginMode
/// </summary>
[JsonProperty("loginMode")] [JsonProperty("loginMode")]
public string LoginMode { get; set; } public string LoginMode { get; set; }
/// <summary>
/// Gets or sets the Modes
/// </summary>
[JsonProperty("modes")] [JsonProperty("modes")]
public Dictionary<string, McMode> Modes { get; set; } public Dictionary<string, McMode> Modes { get; set; }
[JsonProperty("enableRemoteLogging")]
/// <summary> /// <summary>
/// Gets or sets the Logging /// Gets or sets the Logging
/// </summary> /// </summary>
[JsonProperty("enableRemoteLogging")]
public bool Logging { get; set; } public bool Logging { get; set; }
[JsonProperty("partnerMetadata", NullValueHandling = NullValueHandling.Ignore)]
/// <summary> /// <summary>
/// Gets or sets the PartnerMetadata /// Gets or sets the PartnerMetadata
/// </summary> /// </summary>
[JsonProperty("partnerMetadata", NullValueHandling = NullValueHandling.Ignore)]
public List<MobileControlPartnerMetadata> PartnerMetadata { get; set; } public List<MobileControlPartnerMetadata> PartnerMetadata { get; set; }
} }
@@ -210,22 +168,22 @@ namespace PepperDash.Essentials
/// </summary> /// </summary>
public class MobileControlPartnerMetadata public class MobileControlPartnerMetadata
{ {
[JsonProperty("role")]
/// <summary> /// <summary>
/// Gets or sets the Role /// Gets or sets the Role
/// </summary> /// </summary>
[JsonProperty("role")]
public string Role { get; set; } public string Role { get; set; }
[JsonProperty("description")]
/// <summary> /// <summary>
/// Gets or sets the Description /// Gets or sets the Description
/// </summary> /// </summary>
[JsonProperty("description")]
public string Description { get; set; } public string Description { get; set; }
[JsonProperty("logoPath")]
/// <summary> /// <summary>
/// Gets or sets the LogoPath /// Gets or sets the LogoPath
/// </summary> /// </summary>
[JsonProperty("logoPath")]
public string LogoPath { get; set; } public string LogoPath { get; set; }
} }
@@ -234,22 +192,21 @@ namespace PepperDash.Essentials
/// </summary> /// </summary>
public class McMode public class McMode
{ {
[JsonProperty("listPageText")]
/// <summary> /// <summary>
/// Gets or sets the ListPageText /// Gets or sets the ListPageText
/// </summary> /// </summary>
[JsonProperty("listPageText")]
public string ListPageText { get; set; } public string ListPageText { get; set; }
[JsonProperty("loginHelpText")]
/// <summary> /// <summary>
/// Gets or sets the LoginHelpText /// Gets or sets the LoginHelpText
/// </summary> /// </summary>
[JsonProperty("loginHelpText")]
public string LoginHelpText { get; set; } public string LoginHelpText { get; set; }
[JsonProperty("passcodePageText")]
/// <summary> /// <summary>
/// Gets or sets the PasscodePageText /// Gets or sets the PasscodePageText
/// </summary> /// </summary>
[JsonProperty("passcodePageText")]
public string PasscodePageText { get; set; } public string PasscodePageText { get; set; }
} }
@@ -258,19 +215,8 @@ namespace PepperDash.Essentials
/// </summary> /// </summary>
public enum MCIconSet public enum MCIconSet
{ {
/// <summary>
/// Google icon set
/// </summary>
GOOGLE, GOOGLE,
/// <summary>
/// Habanero icon set
/// </summary>
HABANERO, HABANERO,
/// <summary>
/// Neo icon set
/// </summary>
NEO NEO
} }
} }

View File

@@ -27,6 +27,7 @@ using PepperDash.Essentials.Core.Shades;
using PepperDash.Essentials.Core.Web; using PepperDash.Essentials.Core.Web;
using PepperDash.Essentials.Devices.Common.AudioCodec; using PepperDash.Essentials.Devices.Common.AudioCodec;
using PepperDash.Essentials.Devices.Common.Cameras; using PepperDash.Essentials.Devices.Common.Cameras;
using PepperDash.Essentials.Devices.Common.Codec;
using PepperDash.Essentials.Devices.Common.Displays; using PepperDash.Essentials.Devices.Common.Displays;
using PepperDash.Essentials.Devices.Common.Lighting; using PepperDash.Essentials.Devices.Common.Lighting;
using PepperDash.Essentials.Devices.Common.SoftCodec; using PepperDash.Essentials.Devices.Common.SoftCodec;
@@ -505,25 +506,6 @@ namespace PepperDash.Essentials
messengerAdded = true; messengerAdded = true;
} }
if (device is IBasicVideoMuteWithFeedback)
{
var deviceKey = device.Key;
this.LogVerbose(
"Adding IBasicVideoMuteWithFeedback for {deviceKey}",
deviceKey
);
var videoMuteControlDevice = device as IBasicVideoMuteWithFeedback;
var messenger = new IBasicVideoMuteWithFeedbackMessenger(
$"{device.Key}-videoMute-{Key}",
string.Format("/device/{0}", deviceKey),
videoMuteControlDevice
);
AddDefaultDeviceMessenger(messenger);
messengerAdded = true;
}
if (device is ILightingScenes || device is LightingBase) if (device is ILightingScenes || device is LightingBase)
{ {
var deviceKey = device.Key; var deviceKey = device.Key;
@@ -579,6 +561,21 @@ namespace PepperDash.Essentials
messengerAdded = true; messengerAdded = true;
} }
else if (device is IHasDialer dialer && !messengerAdded)
{
this.LogVerbose(
"Adding CallStatusMessenger for {deviceKey}", device.Key);
var messenger = new CallStatusMessenger(
$"{device.Key}-callStatus-{Key}",
dialer,
$"/device/{device.Key}"
);
AddDefaultDeviceMessenger(messenger);
messengerAdded = true;
}
if (device is AudioCodecBase audioCodec) if (device is AudioCodecBase audioCodec)
{ {

View File

@@ -38,7 +38,7 @@
<Compile Remove="RoomBridges\SourceDeviceMapDictionary.cs" /> <Compile Remove="RoomBridges\SourceDeviceMapDictionary.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Crestron.SimplSharp.SDK.ProgramLibrary" Version="2.21.157" /> <PackageReference Include="Crestron.SimplSharp.SDK.ProgramLibrary" Version="2.21.90" />
<PackageReference Include="WebSocketSharp-netstandard" Version="1.0.1" /> <PackageReference Include="WebSocketSharp-netstandard" Version="1.0.1" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@@ -2,8 +2,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Linq; using System.Linq;
using System.Text.RegularExpressions;
using Crestron.SimplSharp;
using Crestron.SimplSharpPro; using Crestron.SimplSharpPro;
using Crestron.SimplSharpPro.DeviceSupport; using Crestron.SimplSharpPro.DeviceSupport;
using Crestron.SimplSharpPro.UI; using Crestron.SimplSharpPro.UI;
@@ -108,11 +106,6 @@ namespace PepperDash.Essentials.Touchpanel
public ReadOnlyCollection<ConnectedIpInformation> ConnectedIps => Panel.ConnectedIpList; public ReadOnlyCollection<ConnectedIpInformation> ConnectedIps => Panel.ConnectedIpList;
private System.Net.IPAddress csIpAddress;
private System.Net.IPAddress csSubnetMask;
/// <summary> /// <summary>
/// Initializes a new instance of the MobileControlTouchpanelController class. /// Initializes a new instance of the MobileControlTouchpanelController class.
/// </summary> /// </summary>
@@ -189,13 +182,6 @@ namespace PepperDash.Essentials.Touchpanel
}; };
RegisterForExtenders(); RegisterForExtenders();
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);
} }
/// <summary> /// <summary>
@@ -395,81 +381,19 @@ namespace PepperDash.Essentials.Touchpanel
McServerUrlFeedback.LinkInputSig(Panel.StringInput[3]); McServerUrlFeedback.LinkInputSig(Panel.StringInput[3]);
UserCodeFeedback.LinkInputSig(Panel.StringInput[4]); UserCodeFeedback.LinkInputSig(Panel.StringInput[4]);
Panel.IpInformationChange += (sender, args) =>
{
if (args.Connected)
{
this.LogVerbose("Connection from IP: {ip}", args.DeviceIpAddress);
this.LogInformation("Sending {appUrl} on join 1", AppUrlFeedback.StringValue);
var appUrl = GetUrlWithCorrectIp(_appUrl);
Panel.StringInput[1].StringValue = appUrl;
SetAppUrl(appUrl);
}
else
{
this.LogVerbose("Disconnection from IP: {ip}", args.DeviceIpAddress);
}
};
Panel.OnlineStatusChange += (sender, args) => Panel.OnlineStatusChange += (sender, args) =>
{ {
UpdateFeedbacks();
this.LogInformation("Sending {appUrl} on join 1", AppUrlFeedback.StringValue); this.LogInformation("Sending {appUrl} on join 1", AppUrlFeedback.StringValue);
UpdateFeedbacks(); Panel.StringInput[1].StringValue = AppUrlFeedback.StringValue;
Panel.StringInput[1].StringValue = _appUrl;
Panel.StringInput[2].StringValue = QrCodeUrlFeedback.StringValue; Panel.StringInput[2].StringValue = QrCodeUrlFeedback.StringValue;
Panel.StringInput[3].StringValue = McServerUrlFeedback.StringValue; Panel.StringInput[3].StringValue = McServerUrlFeedback.StringValue;
Panel.StringInput[4].StringValue = UserCodeFeedback.StringValue; Panel.StringInput[4].StringValue = UserCodeFeedback.StringValue;
}; };
} }
/// <summary>
/// Gets the URL with the correct IP address based on the connected devices and the Crestron processor's IP address.
/// </summary>
/// <param name="url"></param>
/// <returns></returns>
private string GetUrlWithCorrectIp(string url)
{
var lanAdapterId = CrestronEthernetHelper.GetAdapterdIdForSpecifiedAdapterType(EthernetAdapterType.EthernetLANAdapter);
var processorIp = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, lanAdapterId);
if(csIpAddress == null || csSubnetMask == null || url == null)
{
this.LogWarning("CS IP Address Subnet Mask or url is null, cannot determine correct IP for URL");
return url;
}
this.LogVerbose("Processor IP: {processorIp}, CS IP: {csIpAddress}, CS Subnet Mask: {csSubnetMask}", processorIp, csIpAddress, csSubnetMask);
this.LogVerbose("Connected IP Count: {connectedIps}", ConnectedIps.Count);
var ip = 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 match = Regex.Match(url, @"^http://([^:/]+):\d+/mc/app\?token=.+$");
if (match.Success)
{
string ipa = match.Groups[1].Value;
// ip will be "192.168.1.100"
}
// replace ipa with ip but leave the rest of the string intact
var updatedUrl = Regex.Replace(url, @"^http://[^:/]+", $"http://{ip}");
this.LogVerbose("Updated URL: {updatedUrl}", updatedUrl);
return updatedUrl;
}
private void SubscribeForMobileControlUpdates() private void SubscribeForMobileControlUpdates()
{ {
foreach (var dev in DeviceManager.AllDevices) foreach (var dev in DeviceManager.AllDevices)
@@ -519,8 +443,7 @@ namespace PepperDash.Essentials.Touchpanel
/// </summary> /// </summary>
public void SetAppUrl(string url) public void SetAppUrl(string url)
{ {
_appUrl = GetUrlWithCorrectIp(url); _appUrl = url;
AppUrlFeedback.FireUpdate(); AppUrlFeedback.FireUpdate();
} }

View File

@@ -1,6 +1,5 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Net.Http; using System.Net.Http;
@@ -42,14 +41,8 @@ namespace PepperDash.Essentials.WebSocketServer
private HttpServer _server; private HttpServer _server;
/// <summary>
/// Gets the HttpServer instance
/// </summary>
public HttpServer Server => _server; public HttpServer Server => _server;
/// <summary>
/// Gets the collection of UI client contexts
/// </summary>
public Dictionary<string, UiClientContext> UiClients { get; private set; } public Dictionary<string, UiClientContext> UiClients { get; private set; }
private readonly MobileControlSystemController _parent; private readonly MobileControlSystemController _parent;
@@ -68,20 +61,17 @@ namespace PepperDash.Essentials.WebSocketServer
} }
} }
private string LanIpAddress => CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, CrestronEthernetHelper.GetAdapterdIdForSpecifiedAdapterType(EthernetAdapterType.EthernetLANAdapter)); private string lanIpAddress => CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, CrestronEthernetHelper.GetAdapterdIdForSpecifiedAdapterType(EthernetAdapterType.EthernetLANAdapter));
private readonly System.Net.IPAddress csIpAddress; private System.Net.IPAddress csIpAddress;
private readonly System.Net.IPAddress csSubnetMask; private System.Net.IPAddress csSubnetMask;
/// <summary> /// <summary>
/// The path for the WebSocket messaging /// The path for the WebSocket messaging
/// </summary> /// </summary>
private readonly string _wsPath = "/mc/api/ui/join/"; private readonly string _wsPath = "/mc/api/ui/join/";
/// <summary>
/// Gets the WebSocket path
/// </summary>
public string WsPath => _wsPath; public string WsPath => _wsPath;
/// <summary> /// <summary>
@@ -99,9 +89,6 @@ namespace PepperDash.Essentials.WebSocketServer
/// </summary> /// </summary>
public int Port { get; private set; } public int Port { get; private set; }
/// <summary>
/// Gets the user app URL prefix
/// </summary>
public string UserAppUrlPrefix public string UserAppUrlPrefix
{ {
get get
@@ -114,9 +101,6 @@ namespace PepperDash.Essentials.WebSocketServer
} }
} }
/// <summary>
/// Gets the count of connected UI clients
/// </summary>
public int ConnectedUiClientsCount public int ConnectedUiClientsCount
{ {
get get
@@ -135,9 +119,6 @@ namespace PepperDash.Essentials.WebSocketServer
} }
} }
/// <summary>
/// Initializes a new instance of the MobileControlWebsocketServer class.
/// </summary>
public MobileControlWebsocketServer(string key, int customPort, MobileControlSystemController parent) public MobileControlWebsocketServer(string key, int customPort, MobileControlSystemController parent)
: base(key) : base(key)
{ {
@@ -346,26 +327,17 @@ namespace PepperDash.Essentials.WebSocketServer
} }
string ip = processorIp; string ip = processorIp;
if (touchpanel.Touchpanel is IMobileControlCrestronTouchpanelController crestronTouchpanel && csIpAddress != null)
// Moved to the MobileControlTouchpanelController class in the GetUrlWithCorrectIp method
// triggered by the Panel.IpInformationChange event so that we know we have the necessary info
// to make the determination of which IP to use.
//if (touchpanel.Touchpanel is IMobileControlCrestronTouchpanelController crestronTouchpanel && csIpAddress != null)
//{
// 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;
//}
if (_parent.Config.DirectServer.CSLanUiDeviceKeys != null && _parent.Config.DirectServer.CSLanUiDeviceKeys.Any(k => k.Equals(touchpanel.Touchpanel.Key, StringComparison.InvariantCultureIgnoreCase)) && csIpAddress != null)
{ {
ip = csIpAddress.ToString(); 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://{ip}:{_parent.Config.DirectServer.Port}/mc/app?token={touchpanel.Key}";
@@ -649,9 +621,6 @@ namespace PepperDash.Essentials.WebSocketServer
CrestronConsole.ConsoleCommandResponse($"Token: {token}"); CrestronConsole.ConsoleCommandResponse($"Token: {token}");
} }
/// <summary>
/// Validates the grant code against the room key
/// </summary>
public (string, string) ValidateGrantCode(string grantCode, string roomKey) public (string, string) ValidateGrantCode(string grantCode, string roomKey)
{ {
var bridge = _parent.GetRoomBridge(roomKey); var bridge = _parent.GetRoomBridge(roomKey);
@@ -665,9 +634,6 @@ namespace PepperDash.Essentials.WebSocketServer
return ValidateGrantCode(grantCode, bridge); return ValidateGrantCode(grantCode, bridge);
} }
/// <summary>
/// Validates the grant code against the room key
/// </summary>
public (string, string) ValidateGrantCode(string grantCode, MobileControlBridgeBase bridge) public (string, string) ValidateGrantCode(string grantCode, MobileControlBridgeBase bridge)
{ {
// TODO: Authenticate grant code passed in // TODO: Authenticate grant code passed in
@@ -689,9 +655,6 @@ namespace PepperDash.Essentials.WebSocketServer
} }
} }
/// <summary>
/// Generates a new client token for the specified bridge
/// </summary>
public (string, string) GenerateClientToken(MobileControlBridgeBase bridge, string touchPanelKey = "") public (string, string) GenerateClientToken(MobileControlBridgeBase bridge, string touchPanelKey = "")
{ {
var key = Guid.NewGuid().ToString(); var key = Guid.NewGuid().ToString();

View File

@@ -1,5 +1,5 @@
using System;
using System.IO.Compression; using System;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using Crestron.SimplSharp; using Crestron.SimplSharp;
@@ -41,6 +41,29 @@ namespace PepperDash.Essentials
SystemMonitor.ProgramInitialization.ProgramInitializationUnderUserControl = true; SystemMonitor.ProgramInitialization.ProgramInitializationUnderUserControl = true;
Debug.SetErrorLogMinimumDebugLevel(CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance ? LogEventLevel.Warning : LogEventLevel.Verbose); Debug.SetErrorLogMinimumDebugLevel(CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance ? LogEventLevel.Warning : LogEventLevel.Verbose);
// AppDomain.CurrentDomain.AssemblyResolve += CurrentDomainOnAssemblyResolve;
}
private Assembly CurrentDomainOnAssemblyResolve(object sender, ResolveEventArgs args)
{
var assemblyName = new AssemblyName(args.Name).Name;
if (assemblyName == "PepperDash_Core")
{
return Assembly.LoadFrom("PepperDashCore.dll");
}
if (assemblyName == "PepperDash_Essentials_Core")
{
return Assembly.LoadFrom("PepperDash.Essentials.Core.dll");
}
if (assemblyName == "Essentials Devices Common")
{
return Assembly.LoadFrom("PepperDash.Essentials.Devices.Common.dll");
}
return null;
} }
/// <summary> /// <summary>
@@ -244,8 +267,6 @@ namespace PepperDash.Essentials
// _ = new ProcessorExtensionDeviceFactory(); // _ = new ProcessorExtensionDeviceFactory();
// _ = new MobileControlFactory(); // _ = new MobileControlFactory();
LoadAssets();
Debug.LogMessage(LogEventLevel.Information, "Starting Essentials load from configuration"); Debug.LogMessage(LogEventLevel.Information, "Starting Essentials load from configuration");
var filesReady = SetupFilesystem(); var filesReady = SetupFilesystem();
@@ -547,142 +568,5 @@ namespace PepperDash.Essentials
return false; return false;
} }
} }
private static void LoadAssets()
{
var applicationDirectory = new DirectoryInfo(Global.ApplicationDirectoryPathPrefix);
Debug.LogMessage(LogEventLevel.Information, "Searching: {applicationDirectory:l} for embedded assets - {Destination}", applicationDirectory.FullName, Global.FilePathPrefix);
var zipFiles = applicationDirectory.GetFiles("assets*.zip");
if (zipFiles.Length > 1)
{
throw new Exception("Multiple assets zip files found. Cannot continue.");
}
if (zipFiles.Length == 1)
{
var zipFile = zipFiles[0];
var assetsRoot = System.IO.Path.GetFullPath(Global.FilePathPrefix);
if (!assetsRoot.EndsWith(Path.DirectorySeparatorChar.ToString()) && !assetsRoot.EndsWith(Path.AltDirectorySeparatorChar.ToString()))
{
assetsRoot += Path.DirectorySeparatorChar;
}
Debug.LogMessage(LogEventLevel.Information, "Found assets zip file: {zipFile:l}... Unzipping...", zipFile.FullName);
using (var archive = ZipFile.OpenRead(zipFile.FullName))
{
foreach (var entry in archive.Entries)
{
var destinationPath = Path.Combine(Global.FilePathPrefix, entry.FullName);
var fullDest = System.IO.Path.GetFullPath(destinationPath);
if (!fullDest.StartsWith(assetsRoot, StringComparison.OrdinalIgnoreCase))
throw new InvalidOperationException($"Entry '{entry.FullName}' is trying to extract outside of the target directory.");
if (string.IsNullOrEmpty(entry.Name))
{
Directory.CreateDirectory(destinationPath);
continue;
}
// If a directory exists where a file should go, delete it
if (Directory.Exists(destinationPath))
Directory.Delete(destinationPath, true);
Directory.CreateDirectory(Path.GetDirectoryName(destinationPath));
entry.ExtractToFile(destinationPath, true);
Debug.LogMessage(LogEventLevel.Information, "Extracted: {entry:l} to {Destination}", entry.FullName, destinationPath);
}
}
}
// cleaning up zip files
foreach (var file in zipFiles)
{
File.Delete(file.FullName);
}
var htmlZipFiles = applicationDirectory.GetFiles("htmlassets*.zip");
if (htmlZipFiles.Length > 1)
{
throw new Exception("Multiple htmlassets zip files found in application directory. Please ensure only one htmlassets*.zip file is present and retry.");
}
if (htmlZipFiles.Length == 1)
{
var htmlZipFile = htmlZipFiles[0];
var programDir = new DirectoryInfo(Global.FilePathPrefix.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar));
var userOrNvramDir = programDir.Parent;
var rootDir = userOrNvramDir?.Parent;
if (rootDir == null)
{
throw new Exception($"Unable to determine root directory for html extraction. Current path: {Global.FilePathPrefix}");
}
var htmlDir = Path.Combine(rootDir.FullName, "html");
var htmlRoot = System.IO.Path.GetFullPath(htmlDir);
if (!htmlRoot.EndsWith(Path.DirectorySeparatorChar.ToString()) &&
!htmlRoot.EndsWith(Path.AltDirectorySeparatorChar.ToString()))
{
htmlRoot += Path.DirectorySeparatorChar;
}
Debug.LogMessage(LogEventLevel.Information, "Found htmlassets zip file: {zipFile:l}... Unzipping...", htmlZipFile.FullName);
using (var archive = ZipFile.OpenRead(htmlZipFile.FullName))
{
foreach (var entry in archive.Entries)
{
var destinationPath = Path.Combine(htmlDir, entry.FullName);
var fullDest = System.IO.Path.GetFullPath(destinationPath);
if (!fullDest.StartsWith(htmlRoot, StringComparison.OrdinalIgnoreCase))
throw new InvalidOperationException($"Entry '{entry.FullName}' is trying to extract outside of the target directory.");
if (string.IsNullOrEmpty(entry.Name))
{
Directory.CreateDirectory(destinationPath);
continue;
}
// Only delete the file if it exists and is a file, not a directory
if (File.Exists(destinationPath))
File.Delete(destinationPath);
var parentDir = Path.GetDirectoryName(destinationPath);
if (!string.IsNullOrEmpty(parentDir))
Directory.CreateDirectory(parentDir);
entry.ExtractToFile(destinationPath, true);
Debug.LogMessage(LogEventLevel.Information, "Extracted: {entry:l} to {Destination}", entry.FullName, destinationPath);
}
}
}
// cleaning up html zip files
foreach (var file in htmlZipFiles)
{
File.Delete(file.FullName);
}
var jsonFiles = applicationDirectory.GetFiles("*configurationFile*.json");
if (jsonFiles.Length > 1)
{
throw new Exception("Multiple configuration files found. Cannot continue.");
}
if (jsonFiles.Length == 1)
{
var jsonFile = jsonFiles[0];
var finalPath = Path.Combine(Global.FilePathPrefix, jsonFile.Name);
Debug.LogMessage(LogEventLevel.Information, "Found configuration file: {jsonFile:l}... Moving to: {Destination}", jsonFile.FullName, finalPath);
if (File.Exists(finalPath))
{
Debug.LogMessage(LogEventLevel.Information, "Removing existing configuration file: {Destination}", finalPath);
File.Delete(finalPath);
}
jsonFile.MoveTo(finalPath);
}
}
} }
} }

View File

@@ -48,8 +48,7 @@
</None> </None>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Crestron.SimplSharp.SDK.Program" Version="2.21.157" /> <PackageReference Include="Crestron.SimplSharp.SDK.Program" Version="2.21.90" />
<PackageReference Include="System.IO.Compression" Version="4.0.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\PepperDash.Core\PepperDash.Core.csproj" /> <ProjectReference Include="..\PepperDash.Core\PepperDash.Core.csproj" />