diff --git a/docs/docs/usage/GenericComm.md b/docs/docs/usage/GenericComm.md
index 16f467e2..243536e5 100644
--- a/docs/docs/usage/GenericComm.md
+++ b/docs/docs/usage/GenericComm.md
@@ -183,12 +183,11 @@ namespace PepperDash.Core
Cresnet = 8,
Cec = 9,
Udp = 10,
- UdpClient = 11,
}
}
```
-These enumerations are not case sensitive. Not all methods are valid for a ```genericComm``` device. For a comport, the only valid type would be ```Com```. For a direct network socket, valid options are ```Ssh```, ```Tcpip```, ```Telnet```, ```UdpClient```, and ```Udp```.
+These enumerations are not case sensitive. Not all methods are valid for a ```genericComm``` device. For a comport, the only valid type would be ```Com```. For a direct network socket, valid options are ```Ssh```, ```Tcpip```, ```Telnet```, and ```Udp```.
##### ComParams
@@ -288,7 +287,7 @@ This property maps to the number of the port on the device you have mapped the r
##### TcpSshParams
-A ```Ssh```, ```TcpIp```, ```UdpClient```, or ```Udp``` device requires a ```tcpSshProperties``` object to set the propeties of the socket.
+A ```Ssh```, ```TcpIp```, or ```Udp``` device requires a ```tcpSshProperties``` object to set the propeties of the socket.
```Json
{
@@ -305,7 +304,7 @@ A ```Ssh```, ```TcpIp```, ```UdpClient```, or ```Udp``` device requires a ```tcp
**```address```**
-This is the IP address, hostname, or FQDN of the resource you wish to open a socket to. Use ```UdpClient``` for outbound UDP to a remote endpoint. Use ```Udp``` when you need Essentials to bind a local UDP listener.
+This is the IP address, hostname, or FQDN of the resource you wish to open a socket to. In the case of a UDP device, you can set either a single whitelist address with this data, or an appropriate broadcast address.
**```port```**
diff --git a/src/Directory.Build.props b/src/Directory.Build.props
index 235fee48..7435df6f 100644
--- a/src/Directory.Build.props
+++ b/src/Directory.Build.props
@@ -1,11 +1,11 @@
- 2.42.1-local
+ 2.29.0-local
$(Version)
PepperDash Technology
PepperDash Technology
PepperDash Essentials
- Copyright © 2026
+ Copyright © 2025
https://github.com/PepperDash/Essentials
git
Crestron; 4series
@@ -20,7 +20,4 @@
-
-
-
diff --git a/src/PepperDash.Core/Comm/GenericSshClient.cs b/src/PepperDash.Core/Comm/GenericSshClient.cs
index 546a2a67..df44ab51 100644
--- a/src/PepperDash.Core/Comm/GenericSshClient.cs
+++ b/src/PepperDash.Core/Comm/GenericSshClient.cs
@@ -151,8 +151,6 @@ namespace PepperDash.Core
// Thread-safety lock for state changes
private readonly object _stateLock = new object();
- private volatile bool _isProgramStopping;
-
private bool disconnectLogged = false;
///
@@ -209,9 +207,11 @@ namespace PepperDash.Core
{
if (programEventType == eProgramStatusEventType.Stopping)
{
- _isProgramStopping = true;
- this.LogDebug("Program stopping. Closing connection");
- Disconnect();
+ if (client != null)
+ {
+ this.LogDebug("Program stopping. Closing connection");
+ Disconnect();
+ }
}
}
@@ -228,12 +228,6 @@ namespace PepperDash.Core
return;
}
- if (_isProgramStopping)
- {
- this.LogDebug("Skipping connect because program is stopping");
- return;
- }
-
ConnectEnabled = true;
try
@@ -293,7 +287,13 @@ namespace PepperDash.Core
}
catch (SshConnectionException e)
{
- var ie = e.InnerException; // The details are inside, when present - remote can close the connection with no inner exception at all
+ var ie = e.InnerException; // The details are inside!!
+
+ if (ie is SocketException)
+ {
+ this.LogError("CONNECTION failure: Cannot reach host");
+ this.LogVerbose(ie, "Exception details: ");
+ }
if (ie is System.Net.Sockets.SocketException socketException)
{
@@ -301,20 +301,20 @@ namespace PepperDash.Core
Hostname, Port);
this.LogVerbose(socketException, "SocketException details: ");
}
- else if (ie is SshAuthenticationException)
+ if (ie is SshAuthenticationException)
{
this.LogError("Authentication failure for username {userName}", Username);
this.LogVerbose(ie, "AuthenticationException details: ");
}
else
{
- this.LogError("Error on connect: {error}", ie?.Message ?? e.Message);
- this.LogVerbose(ie ?? e, "Exception details: ");
+ this.LogError("Error on connect: {error}", ie.Message);
+ this.LogVerbose(ie, "Exception details: ");
}
disconnectLogged = true;
KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED);
- if (AutoReconnect && ConnectEnabled && !_isProgramStopping)
+ if (AutoReconnect)
{
this.LogDebug("Checking autoreconnect: {autoReconnect}, {autoReconnectInterval}ms", AutoReconnect, AutoReconnectIntervalMs);
StartReconnectTimer();
@@ -326,7 +326,7 @@ namespace PepperDash.Core
disconnectLogged = true;
KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED);
- if (AutoReconnect && ConnectEnabled && !_isProgramStopping)
+ if (AutoReconnect)
{
this.LogDebug("Checking autoreconnect: {0}, {1}ms", AutoReconnect, AutoReconnectIntervalMs);
StartReconnectTimer();
@@ -338,7 +338,7 @@ namespace PepperDash.Core
this.LogVerbose(e, "Exception details: ");
disconnectLogged = true;
KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED);
- if (AutoReconnect && ConnectEnabled && !_isProgramStopping)
+ if (AutoReconnect)
{
this.LogDebug("Checking autoreconnect: {0}, {1}ms", AutoReconnect, AutoReconnectIntervalMs);
StartReconnectTimer();
@@ -473,7 +473,7 @@ namespace PepperDash.Core
{
connectLock.Release();
}
- if (AutoReconnect && ConnectEnabled && !_isProgramStopping)
+ if (AutoReconnect && ConnectEnabled)
{
this.LogDebug("Checking autoreconnect: {0}, {1}ms", AutoReconnect, AutoReconnectIntervalMs);
StartReconnectTimer();
@@ -516,10 +516,7 @@ namespace PepperDash.Core
this.LogError("ObjectDisposedException sending '{message}'. Restarting connection...", text.Trim());
KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED);
- if (AutoReconnect && ConnectEnabled && !_isProgramStopping)
- {
- StartReconnectTimer();
- }
+ StartReconnectTimer();
}
catch (Exception ex)
{
@@ -552,10 +549,7 @@ namespace PepperDash.Core
this.LogException(ex, "ObjectDisposedException sending {message}", ComTextHelper.GetEscapedText(bytes));
KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED);
- if (AutoReconnect && ConnectEnabled && !_isProgramStopping)
- {
- StartReconnectTimer();
- }
+ StartReconnectTimer();
}
catch (Exception ex)
{
diff --git a/src/PepperDash.Core/Comm/GenericUdpClient.cs b/src/PepperDash.Core/Comm/GenericUdpClient.cs
deleted file mode 100644
index 41792537..00000000
--- a/src/PepperDash.Core/Comm/GenericUdpClient.cs
+++ /dev/null
@@ -1,463 +0,0 @@
-using System;
-using System.Net.Sockets;
-using System.Text;
-using System.Threading;
-using System.Threading.Tasks;
-using Crestron.SimplSharp;
-using Crestron.SimplSharp.CrestronSockets;
-using ThreadingTimeout = System.Threading.Timeout;
-using NetSocketException = System.Net.Sockets.SocketException;
-
-namespace PepperDash.Core
-{
- ///
- /// A class to handle basic UDP communications to a remote endpoint
- ///
- public class GenericUdpClient : Device, ISocketStatusWithStreamDebugging, IAutoReconnect
- {
- private const string SplusKey = "Uninitialized UdpClient";
-
- private readonly object stateLock = new object();
- private readonly Timer reconnectTimer;
-
- private UdpClient client;
- private CancellationTokenSource receiveCancellationTokenSource;
- private bool connectEnabled;
- private bool connectionRefusedLogged;
- private SocketStatus clientStatus = SocketStatus.SOCKET_STATUS_NO_CONNECT;
-
- ///
- /// Object to enable stream debugging
- ///
- public CommunicationStreamDebugging StreamDebugging { get; private set; }
-
- ///
- /// Fires when data is received from the remote endpoint and returns it as a byte array
- ///
- public event EventHandler BytesReceived;
-
- ///
- /// Fires when data is received from the remote endpoint and returns it as text
- ///
- public event EventHandler TextReceived;
-
- ///
- /// Fires when the socket status changes
- ///
- public event EventHandler ConnectionChange;
-
- ///
- /// Address of remote endpoint
- ///
- public string Hostname { get; set; }
-
- ///
- /// Port on remote endpoint
- ///
- public int Port { get; set; }
-
- ///
- /// Another S+ helper because large port numbers can be treated as signed ints
- ///
- public ushort UPort
- {
- get { return Convert.ToUInt16(Port); }
- set { Port = Convert.ToInt32(value); }
- }
-
- ///
- /// Defaults to 2000
- ///
- public int BufferSize { get; set; }
-
- ///
- /// True when the local socket is created and associated with the configured remote endpoint
- ///
- public bool IsConnected
- {
- get { return ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED; }
- }
-
- ///
- /// S+ helper for IsConnected
- ///
- public ushort UIsConnected
- {
- get { return (ushort)(IsConnected ? 1 : 0); }
- }
-
- ///
- /// The current socket status of the client
- ///
- public SocketStatus ClientStatus
- {
- get
- {
- lock (stateLock)
- {
- return clientStatus;
- }
- }
- private set
- {
- var shouldFireEvent = false;
-
- lock (stateLock)
- {
- if (clientStatus != value)
- {
- clientStatus = value;
- shouldFireEvent = true;
- }
- }
-
- if (shouldFireEvent)
- ConnectionChange?.Invoke(this, new GenericSocketStatusChageEventArgs(this));
- }
- }
-
- ///
- /// Ushort representation of client status
- ///
- public ushort UStatus
- {
- get { return (ushort)ClientStatus; }
- }
-
- ///
- /// Gets or sets the AutoReconnect
- ///
- public bool AutoReconnect { get; set; }
-
- ///
- /// S+ helper for AutoReconnect
- ///
- public ushort UAutoReconnect
- {
- get { return (ushort)(AutoReconnect ? 1 : 0); }
- set { AutoReconnect = value == 1; }
- }
-
- ///
- /// Milliseconds to wait before attempting to reconnect. Defaults to 5000
- ///
- public int AutoReconnectIntervalMs { get; set; }
-
- ///
- /// Constructor
- ///
- public GenericUdpClient(string key, string address, int port, int bufferSize)
- : base(key)
- {
- StreamDebugging = new CommunicationStreamDebugging(key);
- CrestronEnvironment.ProgramStatusEventHandler += CrestronEnvironment_ProgramStatusEventHandler;
- AutoReconnectIntervalMs = 5000;
- Hostname = address;
- Port = port;
- BufferSize = bufferSize;
-
- reconnectTimer = new Timer(o =>
- {
- if (connectEnabled)
- Connect();
- }, null, ThreadingTimeout.Infinite, ThreadingTimeout.Infinite);
- }
-
- ///
- /// Constructor for S+
- ///
- public GenericUdpClient()
- : base(SplusKey)
- {
- StreamDebugging = new CommunicationStreamDebugging(SplusKey);
- CrestronEnvironment.ProgramStatusEventHandler += CrestronEnvironment_ProgramStatusEventHandler;
- AutoReconnectIntervalMs = 5000;
- BufferSize = 2000;
-
- reconnectTimer = new Timer(o =>
- {
- if (connectEnabled)
- Connect();
- }, null, ThreadingTimeout.Infinite, ThreadingTimeout.Infinite);
- }
-
- ///
- /// Initialize method
- ///
- public void Initialize(string key)
- {
- Key = key;
- }
-
- private void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType)
- {
- if (programEventType == eProgramStatusEventType.Stopping)
- {
- Debug.Console(1, this, "Program stopping. Closing connection");
- Deactivate();
- }
- }
-
- ///
- /// Deactivate method
- ///
- public override bool Deactivate()
- {
- Disconnect();
- return true;
- }
-
- ///
- /// Connect method
- ///
- public void Connect()
- {
- if (string.IsNullOrEmpty(Hostname))
- {
- Debug.Console(1, Debug.ErrorLogLevel.Warning, "GenericUdpClient '{0}': No address set", Key);
- ClientStatus = SocketStatus.SOCKET_STATUS_NO_CONNECT;
- return;
- }
-
- if (Port < 1 || Port > 65535)
- {
- Debug.Console(1, Debug.ErrorLogLevel.Warning, "GenericUdpClient '{0}': Invalid port", Key);
- ClientStatus = SocketStatus.SOCKET_STATUS_NO_CONNECT;
- return;
- }
-
- var hostname = Hostname;
- var port = Port;
- var bufferSize = BufferSize;
- UdpClient newClient = null;
- CancellationTokenSource newReceiveCancellationTokenSource = null;
- CancellationToken startReceiveToken = default(CancellationToken);
- var shouldStartReceive = false;
-
- lock (stateLock)
- {
- connectEnabled = true;
-
- if (client != null)
- return;
- }
-
- try
- {
- newReceiveCancellationTokenSource = new CancellationTokenSource();
- newClient = new UdpClient();
- newClient.Client.ReceiveBufferSize = bufferSize;
- newClient.Client.SendBufferSize = bufferSize;
- newClient.Connect(hostname, port);
-
- lock (stateLock)
- {
- if (!connectEnabled || client != null)
- {
- newClient.Close();
- newReceiveCancellationTokenSource.Cancel();
- newReceiveCancellationTokenSource.Dispose();
- return;
- }
-
- receiveCancellationTokenSource = newReceiveCancellationTokenSource;
- client = newClient;
- ClientStatus = SocketStatus.SOCKET_STATUS_CONNECTED;
- reconnectTimer.Change(ThreadingTimeout.Infinite, ThreadingTimeout.Infinite);
- startReceiveToken = receiveCancellationTokenSource.Token;
- shouldStartReceive = true;
- }
-
- if (shouldStartReceive)
- StartReceive(startReceiveToken);
- }
- catch (Exception ex)
- {
- Debug.LogMessage(ex, "Error connecting UDP client {0}", this, Key);
-
- if (newClient != null)
- newClient.Close();
-
- if (newReceiveCancellationTokenSource != null)
- {
- newReceiveCancellationTokenSource.Cancel();
- newReceiveCancellationTokenSource.Dispose();
- }
-
- lock (stateLock)
- {
- if (connectEnabled && client == null)
- {
- ClientStatus = SocketStatus.SOCKET_STATUS_NO_CONNECT;
- StartReconnectTimer();
- }
- }
- }
- }
-
- ///
- /// Disconnect method
- ///
- public void Disconnect()
- {
- lock (stateLock)
- {
- connectEnabled = false;
- reconnectTimer.Change(ThreadingTimeout.Infinite, ThreadingTimeout.Infinite);
- CleanupClient();
- ClientStatus = SocketStatus.SOCKET_STATUS_NO_CONNECT;
- }
- }
-
- ///
- /// SendText method
- ///
- public void SendText(string text)
- {
- this.PrintSentText(text);
-
- var bytes = Encoding.GetEncoding(28591).GetBytes(text);
- SendBytes(bytes);
- }
-
- ///
- /// SendBytes method
- ///
- public void SendBytes(byte[] bytes)
- {
- if (bytes == null)
- return;
-
- try
- {
- this.PrintSentBytes(bytes);
-
- if (!IsConnected || client == null)
- Connect();
-
- var udpClient = client;
- if (!IsConnected || udpClient == null)
- {
- Debug.Console(1, Debug.ErrorLogLevel.Warning, "GenericUdpClient '{0}': Cannot send bytes because the client is not connected", Key);
- return;
- }
-
- udpClient.Send(bytes, bytes.Length);
- }
- catch (Exception ex)
- {
- Debug.LogMessage(ex, "Error sending UDP bytes for {0}", this, Key);
- HandleDisconnected();
- }
- }
-
- private void StartReceive(CancellationToken token)
- {
- Task.Run(async () =>
- {
- while (!token.IsCancellationRequested)
- {
- try
- {
- var udpClient = client;
- if (udpClient == null)
- return;
-
- var result = await udpClient.ReceiveAsync().ConfigureAwait(false);
- var bytes = result.Buffer;
- if (bytes == null || bytes.Length == 0)
- continue;
-
- connectionRefusedLogged = false;
-
- var text = Encoding.GetEncoding(28591).GetString(bytes, 0, bytes.Length);
-
- this.PrintReceivedBytes(bytes);
- this.PrintReceivedText(text);
-
- BytesReceived?.Invoke(this, new GenericCommMethodReceiveBytesArgs(bytes));
- TextReceived?.Invoke(this, new GenericCommMethodReceiveTextArgs(text));
- }
- catch (ObjectDisposedException)
- {
- return;
- }
- catch (InvalidOperationException)
- {
- return;
- }
- catch (NetSocketException ex)
- {
- if (ex.SocketErrorCode == SocketError.ConnectionRefused)
- {
- if (!connectionRefusedLogged)
- {
- Debug.Console(1, Debug.ErrorLogLevel.Warning,
- "GenericUdpClient '{0}': Remote endpoint refused UDP traffic or is no longer listening",
- Key);
- connectionRefusedLogged = true;
- }
-
- HandleDisconnected();
- return;
- }
-
- Debug.LogMessage(ex, "UDP receive error for {0}", this, Key);
-
- if (AutoReconnect)
- {
- HandleDisconnected();
- return;
- }
-
- continue;
- }
- catch (Exception ex)
- {
- Debug.LogMessage(ex, "Unexpected UDP receive error for {0}", this, Key);
-
- if (AutoReconnect)
- {
- HandleDisconnected();
- return;
- }
-
- continue;
- }
- }
- }, token);
- }
-
- private void HandleDisconnected()
- {
- lock (stateLock)
- {
- CleanupClient();
- ClientStatus = SocketStatus.SOCKET_STATUS_NO_CONNECT;
- StartReconnectTimer();
- }
- }
-
- private void StartReconnectTimer()
- {
- if (AutoReconnect && connectEnabled)
- reconnectTimer.Change(AutoReconnectIntervalMs, ThreadingTimeout.Infinite);
- }
-
- private void CleanupClient()
- {
- if (receiveCancellationTokenSource != null)
- {
- receiveCancellationTokenSource.Cancel();
- receiveCancellationTokenSource.Dispose();
- receiveCancellationTokenSource = null;
- }
-
- if (client != null)
- {
- client.Close();
- client = null;
- }
- }
- }
-}
diff --git a/src/PepperDash.Core/Comm/eControlMethods.cs b/src/PepperDash.Core/Comm/eControlMethods.cs
index a1a30734..b807fdc5 100644
--- a/src/PepperDash.Core/Comm/eControlMethods.cs
+++ b/src/PepperDash.Core/Comm/eControlMethods.cs
@@ -18,75 +18,70 @@ namespace PepperDash.Core
///
/// RS232/422/485
///
- Com = 1,
+ Com,
///
/// Crestron IpId (most Crestron ethernet devices)
///
- IpId = 2,
+ IpId,
///
/// Crestron IpIdTcp (HD-MD series, etc.)
///
- IpidTcp = 3,
+ IpidTcp,
///
/// Crestron IR control
///
- IR = 4,
+ IR,
///
/// SSH client
///
- Ssh = 5,
+ Ssh,
///
/// TCP/IP client
///
- Tcpip = 6,
+ Tcpip,
///
/// Telnet
///
- Telnet = 7,
+ Telnet,
///
/// Crestnet device
///
- Cresnet = 8,
+ Cresnet,
///
/// CEC Control, via a DM HDMI port
///
- Cec = 9,
+ Cec,
///
/// UDP Server
///
- Udp = 10,
-
+ Udp,
///
/// HTTP client
///
- Http = 11,
+ Http,
///
/// HTTPS client
///
- Https = 12,
+ Https,
///
/// Websocket client
///
- Ws = 13,
+ Ws,
///
/// Secure Websocket client
///
- Wss = 14,
+ Wss,
///
/// Secure TCP/IP
///
- SecureTcpIp = 15,
+ SecureTcpIp,
///
/// Used when comms needs to be handled in SIMPL and bridged opposite the normal direction
///
- ComBridge = 16,
+ ComBridge,
///
/// InfinetEX control
///
- InfinetEx = 17,
- ///
- /// UDP client
- ///
- UdpClient = 18,
+ InfinetEx
}
}
\ No newline at end of file
diff --git a/src/PepperDash.Core/Logging/DebugContext.cs b/src/PepperDash.Core/Logging/DebugContext.cs
index 66e63634..e90ec86a 100644
--- a/src/PepperDash.Core/Logging/DebugContext.cs
+++ b/src/PepperDash.Core/Logging/DebugContext.cs
@@ -11,7 +11,6 @@ namespace PepperDash.Core
///
/// Represents a debugging context
///
- [Obsolete("DebugContext is no longer supported and will be removed in a future release.")]
public class DebugContext
{
///
diff --git a/src/PepperDash.Core/Logging/DebugWebsocketSink.cs b/src/PepperDash.Core/Logging/DebugWebsocketSink.cs
index cfbf5785..fc3b0247 100644
--- a/src/PepperDash.Core/Logging/DebugWebsocketSink.cs
+++ b/src/PepperDash.Core/Logging/DebugWebsocketSink.cs
@@ -8,17 +8,10 @@ using Crestron.SimplSharp;
using WebSocketSharp;
using System.Security.Authentication;
using WebSocketSharp.Net;
+using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.IO;
-using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Asn1.X509;
-using Org.BouncyCastle.Crypto;
-using Org.BouncyCastle.Crypto.Generators;
-using Org.BouncyCastle.Crypto.Operators;
-using Org.BouncyCastle.Math;
-using Org.BouncyCastle.Pkcs;
-using Org.BouncyCastle.Security;
-using Org.BouncyCastle.X509;
using Serilog.Formatting;
using Serilog.Formatting.Json;
@@ -72,20 +65,6 @@ namespace PepperDash.Core
///
public bool IsRunning { get => _httpsServer?.IsListening ?? false; }
- ///
- /// Gets a value indicating whether there are active WebSocket connections.
- ///
- public bool HasActiveConnections
- {
- get
- {
- if (_httpsServer == null || !_httpsServer.IsListening) return false;
- var service = _httpsServer.WebSocketServices[_path];
- if (service == null) return false;
- return service.Sessions.Count > 0;
- }
- }
-
private readonly ITextFormatter _textFormatter;
@@ -135,70 +114,51 @@ namespace PepperDash.Core
var subjectName = string.Format("CN={0}.{1}", hostName, domainName);
var fqdn = string.Format("{0}.{1}", hostName, domainName);
- var random = new SecureRandom();
-
- // Generate RSA 2048 key pair
- var keyPairGenerator = new RsaKeyPairGenerator();
- keyPairGenerator.Init(new KeyGenerationParameters(random, 2048));
- var keyPair = keyPairGenerator.GenerateKeyPair();
-
- // Build certificate
- var certGenerator = new X509V3CertificateGenerator();
- certGenerator.SetSerialNumber(BigInteger.ValueOf(Math.Abs(DateTime.UtcNow.Ticks)));
- certGenerator.SetIssuerDN(new X509Name(subjectName));
- certGenerator.SetSubjectDN(new X509Name(subjectName));
- certGenerator.SetNotBefore(DateTime.UtcNow);
- certGenerator.SetNotAfter(DateTime.UtcNow.AddYears(2));
- certGenerator.SetPublicKey(keyPair.Public);
-
- // Extended Key Usage: server + client auth
- certGenerator.AddExtension(X509Extensions.ExtendedKeyUsage, false,
- new ExtendedKeyUsage(new[] { KeyPurposeID.id_kp_serverAuth, KeyPurposeID.id_kp_clientAuth }));
-
- // Subject Alternative Names: DNS + IP
- System.Net.IPAddress parsedIp;
- if (System.Net.IPAddress.TryParse(ipAddress, out parsedIp))
+ using (var rsa = RSA.Create(2048))
{
- certGenerator.AddExtension(X509Extensions.SubjectAlternativeName, false,
- new GeneralNames(new GeneralName[] {
- new GeneralName(GeneralName.DnsName, fqdn),
- new GeneralName(GeneralName.IPAddress, ipAddress)
- }));
- }
- else
- {
- certGenerator.AddExtension(X509Extensions.SubjectAlternativeName, false,
- new GeneralNames(new GeneralName(GeneralName.DnsName, fqdn)));
- }
- // Sign with SHA256withRSA
- var signatureFactory = new Asn1SignatureFactory("SHA256WITHRSA", keyPair.Private, random);
- var certificate = certGenerator.Generate(signatureFactory);
+ var request = new CertificateRequest(
+ subjectName,
+ rsa,
+ HashAlgorithmName.SHA256,
+ RSASignaturePadding.Pkcs1);
- // Export as PKCS12/PFX
- var pkcs12Store = new Pkcs12StoreBuilder().Build();
- var certEntry = new X509CertificateEntry(certificate);
- pkcs12Store.SetCertificateEntry(_certificateName, certEntry);
- pkcs12Store.SetKeyEntry(_certificateName, new AsymmetricKeyEntry(keyPair.Private), new[] { certEntry });
+ // Subject Key Identifier
+ request.CertificateExtensions.Add(
+ new X509SubjectKeyIdentifierExtension(request.PublicKey, false));
- var separator = Path.DirectorySeparatorChar;
- var outputPath = string.Format("{0}user{1}{2}.pfx", separator, separator, _certificateName);
+ // Extended Key Usage: server + client auth
+ request.CertificateExtensions.Add(
+ new X509EnhancedKeyUsageExtension(
+ new OidCollection
+ {
+ new Oid("1.3.6.1.5.5.7.3.1"), // id-kp-serverAuth
+ new Oid("1.3.6.1.5.5.7.3.2") // id-kp-clientAuth
+ },
+ false));
- using (var ms = new MemoryStream())
- {
- var passwordChars = _certificatePassword.ToCharArray();
- try
+ // Subject Alternative Names: DNS + IP
+ var sanBuilder = new SubjectAlternativeNameBuilder();
+ sanBuilder.AddDnsName(fqdn);
+ if (System.Net.IPAddress.TryParse(ipAddress, out var ip))
+ sanBuilder.AddIpAddress(ip);
+ request.CertificateExtensions.Add(sanBuilder.Build());
+
+ var notBefore = DateTimeOffset.UtcNow;
+ var notAfter = notBefore.AddYears(2);
+
+ using (var cert = request.CreateSelfSigned(notBefore, notAfter))
{
- pkcs12Store.Save(ms, passwordChars, random);
- }
- finally
- {
- Array.Clear(passwordChars, 0, passwordChars.Length);
- }
- File.WriteAllBytes(outputPath, ms.ToArray());
- }
- CrestronConsole.PrintLine(string.Format("CreateCert: Certificate written to {0}", outputPath));
+ var separator = Path.DirectorySeparatorChar;
+ var outputPath = string.Format("{0}user{1}{2}.pfx", separator, separator, _certificateName);
+
+ var pfxBytes = cert.Export(X509ContentType.Pfx, _certificatePassword);
+ File.WriteAllBytes(outputPath, pfxBytes);
+
+ CrestronConsole.PrintLine(string.Format("CreateCert: Certificate written to {0}", outputPath));
+ }
+ }
}
catch (Exception ex)
{
@@ -231,66 +191,26 @@ namespace PepperDash.Core
{
Debug.LogInformation("Starting Websocket Server on port: {0}", port);
-
-
Start(port, CertPath, _certificatePassword);
}
private static X509Certificate2 LoadOrRecreateCert(string certPath, string certPassword)
{
- if (!File.Exists(certPath))
- CreateCert();
-
try
{
- return LoadCertFromBouncyCastle(certPath, certPassword);
+ // EphemeralKeySet is required on Linux/OpenSSL (Crestron 4-series) to avoid
+ // key-container persistence failures, and avoids the private key export restriction.
+ return new X509Certificate2(certPath, certPassword, X509KeyStorageFlags.EphemeralKeySet);
}
catch (Exception ex)
{
- // Cert is corrupt or was written by an incompatible tool — delete and regenerate once.
+ // Cert is stale or was generated by an incompatible library (e.g. old BouncyCastle output).
+ // Delete it, regenerate with the BCL path, and retry once.
CrestronConsole.PrintLine(string.Format("SSL cert load failed ({0}); regenerating...", ex.Message));
try { File.Delete(certPath); } catch { }
CreateCert();
- return LoadCertFromBouncyCastle(certPath, certPassword);
- }
- }
-
- ///
- /// Loads a PKCS#12 file written by BouncyCastle and returns an with
- /// private key attached.
- /// The PFX is parsed and re-encoded by BouncyCastle (ensuring format compatibility), then passed as
- /// raw bytes to so neither RSACryptoServiceProvider nor the
- /// EphemeralKeySet flag (unsupported on the Crestron/Mono runtime) is needed.
- ///
- private static X509Certificate2 LoadCertFromBouncyCastle(string certPath, string certPassword)
- {
- var passwordChars = certPassword.ToCharArray();
- try
- {
- using (var stream = File.OpenRead(certPath))
- {
- var store = new Pkcs12StoreBuilder().Build();
- store.Load(stream, passwordChars);
-
- // Re-encode through BouncyCastle to guarantee PKCS#12 format compatibility,
- // then hand raw bytes to X509Certificate2 — no RSACryptoServiceProvider needed.
- using (var ms = new MemoryStream())
- {
- store.Save(ms, passwordChars, new SecureRandom());
- var cert = new X509Certificate2(ms.ToArray(), certPassword);
-
- if (!cert.HasPrivateKey)
- throw new InvalidOperationException(
- string.Format("Certificate loaded from '{0}' does not contain a private key and cannot be used as a server certificate.", certPath));
-
- return cert;
- }
- }
- }
- finally
- {
- Array.Clear(passwordChars, 0, passwordChars.Length);
+ return new X509Certificate2(certPath, certPassword, X509KeyStorageFlags.EphemeralKeySet);
}
}
diff --git a/src/PepperDash.Core/PepperDash.Core.csproj b/src/PepperDash.Core/PepperDash.Core.csproj
index 747aefb6..daa5c6da 100644
--- a/src/PepperDash.Core/PepperDash.Core.csproj
+++ b/src/PepperDash.Core/PepperDash.Core.csproj
@@ -43,7 +43,7 @@
-
+
diff --git a/src/PepperDash.Core/WebApi/Presets/WebApiPasscodeClient.cs b/src/PepperDash.Core/WebApi/Presets/WebApiPasscodeClient.cs
index 131430a2..f8c815fd 100644
--- a/src/PepperDash.Core/WebApi/Presets/WebApiPasscodeClient.cs
+++ b/src/PepperDash.Core/WebApi/Presets/WebApiPasscodeClient.cs
@@ -1,5 +1,5 @@
using System;
-using Crestron.SimplSharp; // For Basic SIMPL# Classes
+using Crestron.SimplSharp; // For Basic SIMPL# Classes
using Crestron.SimplSharp.CrestronIO;
using Crestron.SimplSharp.Net.Http;
using Crestron.SimplSharp.Net.Https;
@@ -10,25 +10,24 @@ using PepperDash.Core.JsonToSimpl;
namespace PepperDash.Core.WebApi.Presets
{
- ///
- /// Passcode client for the WebApi
- ///
- [Obsolete("WebApiPasscodeClient is no longer supported and will be removed in a future release.")]
+ ///
+ /// Passcode client for the WebApi
+ ///
public class WebApiPasscodeClient : IKeyed
{
- ///
- /// Notifies when user received
- ///
+ ///
+ /// Notifies when user received
+ ///
public event EventHandler UserReceived;
- ///
- /// Notifies when Preset received
- ///
+ ///
+ /// Notifies when Preset received
+ ///
public event EventHandler PresetReceived;
- ///
- /// Gets or sets the Key
- ///
+ ///
+ /// Gets or sets the Key
+ ///
public string Key { get; private set; }
//string JsonMasterKey;
@@ -55,13 +54,13 @@ namespace PepperDash.Core.WebApi.Presets
{
}
- ///
- /// Initializes the instance
- ///
- ///
- ///
- ///
- ///
+ ///
+ /// Initializes the instance
+ ///
+ ///
+ ///
+ ///
+ ///
public void Initialize(string key, string jsonMasterKey, string urlBase, string defaultPresetJsonFilePath)
{
Key = key;
@@ -74,44 +73,44 @@ namespace PepperDash.Core.WebApi.Presets
J2SMaster.Initialize(jsonMasterKey);
}
- ///
- /// Gets the user for a passcode
- ///
- ///
- ///
- /// GetUserForPasscode method
- ///
+ ///
+ /// Gets the user for a passcode
+ ///
+ ///
+ ///
+ /// GetUserForPasscode method
+ ///
public void GetUserForPasscode(string passcode)
{
- // Bullshit duplicate code here... These two cases should be the same
- // except for https/http and the certificate ignores
- if (!UrlBase.StartsWith("https"))
- return;
- var req = new HttpsClientRequest();
- req.Url = new UrlParser(UrlBase + "/api/users/dopin");
- req.RequestType = Crestron.SimplSharp.Net.Https.RequestType.Post;
- req.Header.AddHeader(new HttpsHeader("Content-Type", "application/json"));
- req.Header.AddHeader(new HttpsHeader("Accept", "application/json"));
- var jo = new JObject();
- jo.Add("pin", passcode);
- req.ContentString = jo.ToString();
+ // Bullshit duplicate code here... These two cases should be the same
+ // except for https/http and the certificate ignores
+ if (!UrlBase.StartsWith("https"))
+ return;
+ var req = new HttpsClientRequest();
+ req.Url = new UrlParser(UrlBase + "/api/users/dopin");
+ req.RequestType = Crestron.SimplSharp.Net.Https.RequestType.Post;
+ req.Header.AddHeader(new HttpsHeader("Content-Type", "application/json"));
+ req.Header.AddHeader(new HttpsHeader("Accept", "application/json"));
+ var jo = new JObject();
+ jo.Add("pin", passcode);
+ req.ContentString = jo.ToString();
- var client = new HttpsClient();
- client.HostVerification = false;
- client.PeerVerification = false;
- var resp = client.Dispatch(req);
- var handler = UserReceived;
- if (resp.Code == 200)
- {
- //CrestronConsole.PrintLine("Received: {0}", resp.ContentString);
- var user = JsonConvert.DeserializeObject(resp.ContentString);
- CurrentUser = user;
- if (handler != null)
- handler(this, new UserReceivedEventArgs(user, true));
- }
- else
- if (handler != null)
- handler(this, new UserReceivedEventArgs(null, false));
+ var client = new HttpsClient();
+ client.HostVerification = false;
+ client.PeerVerification = false;
+ var resp = client.Dispatch(req);
+ var handler = UserReceived;
+ if (resp.Code == 200)
+ {
+ //CrestronConsole.PrintLine("Received: {0}", resp.ContentString);
+ var user = JsonConvert.DeserializeObject(resp.ContentString);
+ CurrentUser = user;
+ if (handler != null)
+ UserReceived(this, new UserReceivedEventArgs(user, true));
+ }
+ else
+ if (handler != null)
+ UserReceived(this, new UserReceivedEventArgs(null, false));
}
///
@@ -119,9 +118,9 @@ namespace PepperDash.Core.WebApi.Presets
///
///
///
- ///
- /// GetPresetForThisUser method
- ///
+ ///
+ /// GetPresetForThisUser method
+ ///
public void GetPresetForThisUser(int roomTypeId, int presetNumber)
{
if (CurrentUser == null)
@@ -137,57 +136,57 @@ namespace PepperDash.Core.WebApi.Presets
PresetNumber = presetNumber
};
- var handler = PresetReceived;
+ var handler = PresetReceived;
try
{
- if (!UrlBase.StartsWith("https"))
- return;
- var req = new HttpsClientRequest();
- req.Url = new UrlParser(UrlBase + "/api/presets/userandroom");
- req.RequestType = Crestron.SimplSharp.Net.Https.RequestType.Post;
- req.Header.AddHeader(new HttpsHeader("Content-Type", "application/json"));
- req.Header.AddHeader(new HttpsHeader("Accept", "application/json"));
- req.ContentString = JsonConvert.SerializeObject(msg);
+ if (!UrlBase.StartsWith("https"))
+ return;
+ var req = new HttpsClientRequest();
+ req.Url = new UrlParser(UrlBase + "/api/presets/userandroom");
+ req.RequestType = Crestron.SimplSharp.Net.Https.RequestType.Post;
+ req.Header.AddHeader(new HttpsHeader("Content-Type", "application/json"));
+ req.Header.AddHeader(new HttpsHeader("Accept", "application/json"));
+ req.ContentString = JsonConvert.SerializeObject(msg);
- var client = new HttpsClient();
- client.HostVerification = false;
- client.PeerVerification = false;
+ var client = new HttpsClient();
+ client.HostVerification = false;
+ client.PeerVerification = false;
- // ask for the preset
- var resp = client.Dispatch(req);
- if (resp.Code == 200) // got it
- {
- //Debug.Console(1, this, "Received: {0}", resp.ContentString);
- var preset = JsonConvert.DeserializeObject(resp.ContentString);
- CurrentPreset = preset;
+ // ask for the preset
+ var resp = client.Dispatch(req);
+ if (resp.Code == 200) // got it
+ {
+ //Debug.Console(1, this, "Received: {0}", resp.ContentString);
+ var preset = JsonConvert.DeserializeObject(resp.ContentString);
+ CurrentPreset = preset;
- //if there's no preset data, load the template
- if (preset.Data == null || preset.Data.Trim() == string.Empty || JObject.Parse(preset.Data).Count == 0)
- {
- //Debug.Console(1, this, "Loaded preset has no data. Loading default template.");
- LoadDefaultPresetData();
- return;
- }
+ //if there's no preset data, load the template
+ if (preset.Data == null || preset.Data.Trim() == string.Empty || JObject.Parse(preset.Data).Count == 0)
+ {
+ //Debug.Console(1, this, "Loaded preset has no data. Loading default template.");
+ LoadDefaultPresetData();
+ return;
+ }
- J2SMaster.LoadWithJson(preset.Data);
- if (handler != null)
- handler(this, new PresetReceivedEventArgs(preset, true));
- }
- else // no existing preset
- {
- CurrentPreset = new Preset();
- LoadDefaultPresetData();
- if (handler != null)
- handler(this, new PresetReceivedEventArgs(null, false));
- }
+ J2SMaster.LoadWithJson(preset.Data);
+ if (handler != null)
+ PresetReceived(this, new PresetReceivedEventArgs(preset, true));
+ }
+ else // no existing preset
+ {
+ CurrentPreset = new Preset();
+ LoadDefaultPresetData();
+ if (handler != null)
+ PresetReceived(this, new PresetReceivedEventArgs(null, false));
+ }
}
catch (HttpException e)
{
var resp = e.Response;
Debug.Console(1, this, "No preset received (code {0}). Loading default template", resp.Code);
LoadDefaultPresetData();
- if (handler != null)
- PresetReceived(this, new PresetReceivedEventArgs(null, false));
+ if (handler != null)
+ PresetReceived(this, new PresetReceivedEventArgs(null, false));
}
}
@@ -219,14 +218,14 @@ namespace PepperDash.Core.WebApi.Presets
///
///
///
- ///
- /// SavePresetForThisUser method
- ///
+ ///
+ /// SavePresetForThisUser method
+ ///
public void SavePresetForThisUser(int roomTypeId, int presetNumber)
{
if (CurrentPreset == null)
LoadDefaultPresetData();
- //return;
+ //return;
//// A new preset needs to have its numbers set
//if (CurrentPreset.IsNewPreset)
@@ -246,8 +245,8 @@ namespace PepperDash.Core.WebApi.Presets
{
CurrentPreset.Data = json;
- if (!UrlBase.StartsWith("https"))
- return;
+ if (!UrlBase.StartsWith("https"))
+ return;
var req = new HttpsClientRequest();
req.RequestType = Crestron.SimplSharp.Net.Https.RequestType.Post;
req.Url = new UrlParser(string.Format("{0}/api/presets/addorchange", UrlBase));
@@ -256,8 +255,8 @@ namespace PepperDash.Core.WebApi.Presets
req.ContentString = JsonConvert.SerializeObject(CurrentPreset);
var client = new HttpsClient();
- client.HostVerification = false;
- client.PeerVerification = false;
+ client.HostVerification = false;
+ client.PeerVerification = false;
try
{
var resp = client.Dispatch(req);
diff --git a/src/PepperDash.Essentials.Core/Comm and IR/CecPortController.cs b/src/PepperDash.Essentials.Core/Comm and IR/CecPortController.cs
index a5aa0d13..298bbc9b 100644
--- a/src/PepperDash.Essentials.Core/Comm and IR/CecPortController.cs
+++ b/src/PepperDash.Essentials.Core/Comm and IR/CecPortController.cs
@@ -39,8 +39,6 @@ namespace PepperDash.Essentials.Core
ICec Port;
- bool _cecSubscribed;
-
///
/// Constructor
///
@@ -56,7 +54,7 @@ namespace PepperDash.Essentials.Core
{
Port = postActivationFunc(config);
- TryEnsureCecSubscription();
+ Port.StreamCec.CecChange += StreamCec_CecChange;
});
}
@@ -70,29 +68,7 @@ namespace PepperDash.Essentials.Core
{
Port = port;
- TryEnsureCecSubscription();
- }
-
- ///
- /// Subscribes to the CEC change event once is available.
- /// Safe to call repeatedly; the subscription is only wired a single time.
- /// If StreamCec is null during construction, this is retried when send methods invoke
- /// this method later.
- ///
- void TryEnsureCecSubscription()
- {
- if (_cecSubscribed)
- return;
-
- if (Port?.StreamCec == null)
- {
- Debug.LogMessage(LogEventLevel.Warning, this, "StreamCec is not available; CEC feedback is deferred until the device is ready");
- return;
- }
-
Port.StreamCec.CecChange += new CecChangeEventHandler(StreamCec_CecChange);
- _cecSubscribed = true;
- Debug.LogMessage(LogEventLevel.Information, this, "Subscribed to CEC feedback");
}
void StreamCec_CecChange(Cec cecDevice, CecEventArgs args)
@@ -128,9 +104,8 @@ namespace PepperDash.Essentials.Core
///
public void SendText(string text)
{
- if (Port?.StreamCec == null)
+ if (Port == null)
return;
- TryEnsureCecSubscription();
this.PrintSentText(text);
Port.StreamCec.Send.StringValue = text;
}
@@ -140,9 +115,8 @@ namespace PepperDash.Essentials.Core
///
public void SendBytes(byte[] bytes)
{
- if (Port?.StreamCec == null)
+ if (Port == null)
return;
- TryEnsureCecSubscription();
var text = Encoding.GetEncoding(28591).GetString(bytes, 0, bytes.Length);
this.PrintSentBytes(bytes);
Debug.LogMessage(LogEventLevel.Information, this, "Sending {0} bytes: '{1}'", bytes.Length, ComTextHelper.GetEscapedText(bytes));
diff --git a/src/PepperDash.Essentials.Core/Comm and IR/CommFactory.cs b/src/PepperDash.Essentials.Core/Comm and IR/CommFactory.cs
index b28331a0..9318e04b 100644
--- a/src/PepperDash.Essentials.Core/Comm and IR/CommFactory.cs
+++ b/src/PepperDash.Essentials.Core/Comm and IR/CommFactory.cs
@@ -96,17 +96,6 @@ namespace PepperDash.Essentials.Core
comm = udp;
break;
}
- case eControlMethod.UdpClient:
- {
- var udpClient = new GenericUdpClient(deviceConfig.Key + "-udpClient", c.Address, c.Port, c.BufferSize)
- {
- AutoReconnect = c.AutoReconnect
- };
- if (udpClient.AutoReconnect)
- udpClient.AutoReconnectIntervalMs = c.AutoReconnectIntervalMs;
- comm = udpClient;
- break;
- }
case eControlMethod.Telnet:
break;
case eControlMethod.SecureTcpIp:
diff --git a/src/PepperDash.Essentials.Core/Config/Essentials/ConfigReader.cs b/src/PepperDash.Essentials.Core/Config/Essentials/ConfigReader.cs
index 37cd4eba..40c6c0cf 100644
--- a/src/PepperDash.Essentials.Core/Config/Essentials/ConfigReader.cs
+++ b/src/PepperDash.Essentials.Core/Config/Essentials/ConfigReader.cs
@@ -135,14 +135,12 @@ namespace PepperDash.Essentials.Core.Config
{
var parsedConfig = JObject.Parse(fs.ReadToEnd());
- // A config is v1 if it has separate "system" and "template" nodes that
- // need to be merged. A v2 config is already merged by the Portal API and
- // will not have "system"/"template" nodes. This is independent of whether
- // a "versions" node is present, which only carries version metadata and
- // can appear on either a v1 or v2 config.
- var isV1Config = parsedConfig["system"] != null && parsedConfig["template"] != null;
-
- if (!isV1Config)
+ // Check if it's a v2 config (check for "version" node)
+ // this means it's already merged by the Portal API
+ // from the v2 config tool
+ var isV2Config = parsedConfig["versions"] != null;
+
+ if (isV2Config)
{
Debug.LogMessage(LogEventLevel.Information, "Config file is a v2 format, no merge necessary.");
ConfigObject = parsedConfig.ToObject();
@@ -150,8 +148,6 @@ namespace PepperDash.Essentials.Core.Config
return true;
}
- Debug.LogMessage(LogEventLevel.Information, "Config file is a v1 format, merging system and template.");
-
// Extract SystemUrl and TemplateUrl into final config output
ConfigObject = PortalConfigReader.MergeConfigs(parsedConfig).ToObject();
@@ -164,13 +160,6 @@ namespace PepperDash.Essentials.Core.Config
{
ConfigObject.TemplateUrl = parsedConfig["template_url"].Value();
}
-
- // MergeConfigs does not carry the "versions" node forward, so it must be
- // applied separately to ensure it's preserved in the merged config.
- if (parsedConfig["versions"] != null)
- {
- ConfigObject.Versions = parsedConfig["versions"].ToObject();
- }
}
Debug.LogMessage(LogEventLevel.Information, "Successfully Loaded Merged Config");
diff --git a/src/PepperDash.Essentials.Core/Config/Essentials/ConfigUpdater.cs b/src/PepperDash.Essentials.Core/Config/Essentials/ConfigUpdater.cs
index 2ba33df0..9df673bf 100644
--- a/src/PepperDash.Essentials.Core/Config/Essentials/ConfigUpdater.cs
+++ b/src/PepperDash.Essentials.Core/Config/Essentials/ConfigUpdater.cs
@@ -19,7 +19,6 @@ namespace PepperDash.Essentials.Core.Config
///
/// ConfigUpdater class
///
- [Obsolete("ConfigUpdater is no longer supported and will be removed in a future release.")]
public static class ConfigUpdater
{
///
@@ -82,7 +81,7 @@ namespace PepperDash.Essentials.Core.Config
{
var handler = ConfigStatusChanged;
- if (handler != null)
+ if(handler != null)
{
handler(typeof(ConfigUpdater), new ConfigStatusEventArgs(status));
}
@@ -90,7 +89,7 @@ namespace PepperDash.Essentials.Core.Config
static void WriteConfigToFile(string configData)
{
- var filePath = Global.FilePathPrefix + "configurationFile-updated.json";
+ var filePath = Global.FilePathPrefix+ "configurationFile-updated.json";
try
{
@@ -105,7 +104,7 @@ namespace PepperDash.Essentials.Core.Config
Debug.LogMessage(LogEventLevel.Debug, "Error parsing new config: {0}", e);
OnStatusUpdate(eUpdateStatus.UpdateFailed);
- }
+ }
}
///
@@ -150,11 +149,11 @@ namespace PepperDash.Essentials.Core.Config
// Directory exists, first clear any contents
var archivedConfigFiles = ConfigReader.GetConfigFiles(archiveDirectoryPath + Global.DirectorySeparator + Global.ConfigFileName + ".bak");
- if (archivedConfigFiles != null && archivedConfigFiles.Length > 0)
+ if(archivedConfigFiles != null || archivedConfigFiles.Length > 0)
{
Debug.LogMessage(LogEventLevel.Information, "{0} Existing files found in archive folder. Deleting.", archivedConfigFiles.Length);
- for (int i = 0; i < archivedConfigFiles.Length; i++)
+ for (int i = 0; i < archivedConfigFiles.Length; i++ )
{
var file = archivedConfigFiles[i];
Debug.LogMessage(LogEventLevel.Information, "Deleting archived file: '{0}'", file.FullName);
@@ -171,9 +170,9 @@ namespace PepperDash.Essentials.Core.Config
// Moves the file and appends the .bak extension
var fileDest = archiveDirectoryPath + "/" + file.Name + ".bak";
- if (!File.Exists(fileDest))
+ if(!File.Exists(fileDest))
{
- file.MoveTo(fileDest);
+ file.MoveTo(fileDest);
}
else
Debug.LogMessage(LogEventLevel.Information, "Cannot move file to archive folder. Existing file already exists with same name: '{0}'", fileDest);
@@ -208,15 +207,15 @@ namespace PepperDash.Essentials.Core.Config
CrestronConsole.SendControlSystemCommand(string.Format("progreset -p:{0}", InitialParametersClass.ApplicationNumber), ref response);
- Debug.LogMessage(LogEventLevel.Debug, "Console Response: {0}", response);
+ Debug.LogMessage(LogEventLevel.Debug, "Console Response: {0}", response);
}
}
- ///
- /// Enumeration of eUpdateStatus values
- ///
- public enum eUpdateStatus
+ ///
+ /// Enumeration of eUpdateStatus values
+ ///
+ public enum eUpdateStatus
{
///
/// UpdateStarted status
diff --git a/src/PepperDash.Essentials.Core/Config/Essentials/EssentialsConfig.cs b/src/PepperDash.Essentials.Core/Config/Essentials/EssentialsConfig.cs
index 44275ef0..6ffe07d2 100644
--- a/src/PepperDash.Essentials.Core/Config/Essentials/EssentialsConfig.cs
+++ b/src/PepperDash.Essentials.Core/Config/Essentials/EssentialsConfig.cs
@@ -105,7 +105,6 @@ namespace PepperDash.Essentials.Core.Config
///
/// Gets or sets the Versions
///
- [JsonProperty("versions")]
public VersionData Versions { get; set; }
///
@@ -135,25 +134,12 @@ namespace PepperDash.Essentials.Core.Config
[JsonProperty("packages")]
public List Packages { get; set; }
- ///
- /// Gets or sets the touchpanel wrapper app version
- ///
- [JsonProperty("touchpanelWrapperApp")]
- public NugetVersion TouchpanelWrapperApp { get; set; }
-
- ///
- /// Gets or sets the list of user interface packages
- ///
- [JsonProperty("userInterfaces")]
- public List UserInterfaces { get; set; }
-
///
/// Initializes a new instance of the class.
///
public VersionData()
{
Packages = new List();
- UserInterfaces = new List();
}
}
@@ -171,20 +157,8 @@ namespace PepperDash.Essentials.Core.Config
///
/// Gets or sets the PackageId
///
- [JsonProperty("packageId", NullValueHandling = NullValueHandling.Ignore)]
+ [JsonProperty("packageId")]
public string PackageId { get; set; }
-
- ///
- /// Gets or sets the RepoUrl
- ///
- [JsonProperty("repoUrl", NullValueHandling = NullValueHandling.Ignore)]
- public string RepoUrl { get; set; }
-
- ///
- /// Gets or sets the human-readable name
- ///
- [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)]
- public string Name { get; set; }
}
///
diff --git a/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IHasWirelessSharing.cs b/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IHasWirelessSharing.cs
deleted file mode 100644
index 6cd3835e..00000000
--- a/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IHasWirelessSharing.cs
+++ /dev/null
@@ -1,48 +0,0 @@
-using System;
-
-namespace PepperDash.Essentials.Core.DeviceTypeInterfaces
-{
- ///
- /// Defines the contract for a wireless presentation endpoint that reports whether a wireless
- /// sharing session is currently active. Implemented by platforms such as Crestron AirMedia,
- /// Mersive Solstice, Barco ClickShare, Miracast/Teams receivers, etc. Allows consumers (e.g.
- /// room plugins) to react to wireless sharing activity without taking a dependency on any
- /// concrete device implementation.
- ///
- ///
- /// This refers specifically to wireless screen/device mirroring, as distinct from in-call
- /// content sharing on a video conference.
- ///
- public interface IHasWirelessSharing
- {
- ///
- /// Reports whether a wireless sharing session is currently active (content is being presented).
- ///
- BoolFeedback IsSharingFeedback { get; }
-
- ///
- /// Raised when wireless sharing starts or stops. The event args carry the new sharing state.
- ///
- event EventHandler SharingChanged;
- }
-
- ///
- /// Event arguments describing a change in wireless sharing state.
- ///
- public class WirelessSharingEventArgs : EventArgs
- {
- ///
- /// True if a wireless sharing session is active (content is being presented), false otherwise.
- ///
- public bool IsSharing { get; private set; }
-
- ///
- /// Creates a new .
- ///
- /// True if a wireless sharing session is active, false otherwise.
- public WirelessSharingEventArgs(bool isSharing)
- {
- IsSharing = isSharing;
- }
- }
-}
diff --git a/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IMobileControlMessengerWithSubscriptions.cs b/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IMobileControlMessengerWithSubscriptions.cs
index 8603d5b6..887f1789 100644
--- a/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IMobileControlMessengerWithSubscriptions.cs
+++ b/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IMobileControlMessengerWithSubscriptions.cs
@@ -1,12 +1,10 @@
-using System;
using PepperDash.Core;
namespace PepperDash.Essentials.Core.DeviceTypeInterfaces
{
///
- /// Obsolete: messengers are subscription based by default; use IMobileControlMessenger instead.
+ /// Defines the contract for IMobileControlMessenger
///
- [Obsolete("This interface is obsolete and will be removed in a future version. All messengers are now subscription based.")]
public interface IMobileControlMessengerWithSubscriptions : IMobileControlMessenger
{
///
diff --git a/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/INetworkSwitchControl.cs b/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/INetworkSwitchControl.cs
deleted file mode 100644
index 6c6e5598..00000000
--- a/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/INetworkSwitchControl.cs
+++ /dev/null
@@ -1,122 +0,0 @@
-using System;
-
-namespace PepperDash.Essentials.Core.DeviceTypeInterfaces
-{
- ///
- /// Interface for network switches that support VLAN assignment on individual ports.
- ///
- public interface INetworkSwitchVlanManager
- {
- ///
- /// Returns the current access VLAN ID configured on the port.
- /// Return -1 when the value is unavailable (e.g. the switch has not been polled yet
- /// or the implementation does not support VLAN queries).
- ///
- /// Switch port identifier
- /// VLAN ID or -1 when unavailable
- int GetPortCurrentVlan(string port);
-
- ///
- /// Changes the access VLAN of a single switch port.
- /// The implementation is responsible for entering/exiting privileged/config mode.
- ///
- /// Switch port identifier (e.g. "1/0/3" for Netgear, "gi1/0/3" for Cisco)
- /// Target VLAN ID (1-4093)
- void SetPortVlan(string port, uint vlanId);
- }
-
- ///
- /// Interface for network switches that support Power over Ethernet (PoE) control on individual ports.
- ///
- public interface INetworkSwitchPoeManager
- {
- ///
- /// Enables or disables PoE power delivery on a single switch port.
- /// The implementation is responsible for entering/exiting privileged/config mode.
- ///
- /// Switch port identifier
- /// True to enable PoE; false to disable PoE
- void SetPortPoeState(string port, bool enabled);
- }
-
- ///
- /// Standardized interface for network switch devices that support per-port PoE control
- /// and VLAN assignment.
- ///
- public interface INetworkSwitchPoeVlanManager : INetworkSwitchVlanManager, INetworkSwitchPoeManager
- {
- ///
- /// Event that is raised when the state of a switch port changes, such as a VLAN change or PoE state change.
- ///
- event EventHandler PortStateChanged;
-
- }
-
- ///
- /// Event arguments for port state changes on a network switch, such as VLAN changes or PoE state changes.
- ///
- public class NetworkSwitchPortEventArgs : EventArgs
- {
- ///
- /// The identifier of the port that changed state (e.g. "1/0/3" for Netgear, "gi1/0/3" for Cisco).
- ///
- public string Port { get; private set; }
-
- ///
- /// The type of event that occurred on the port (e.g. VLAN change, PoE enabled/disabled).
- ///
- public NetworkSwitchPortEventType EventType { get; private set; }
-
- ///
- /// Constructor for NetworkSwitchPortEventArgs
- ///
- /// The identifier of the port that changed state
- /// The type of event that occurred on the port
- public NetworkSwitchPortEventArgs(string port, NetworkSwitchPortEventType eventType)
- {
- Port = port;
- EventType = eventType;
- }
- }
-
- ///
- /// Enumeration of network switch port state change event types (e.g. VLAN changes or PoE state changes).
- ///
- public enum NetworkSwitchPortEventType
- {
- ///
- /// Indicates that the type of event is unknown or cannot be determined.
- ///
- Unknown,
-
- ///
- /// Indicates that a VLAN change is in progress on the port, either through a call to SetPortVlan or an external change detected by polling.
- ///
- VlanChangeInProgress,
-
- ///
- /// Indicates that the access VLAN on a port has changed, either through a successful call to SetPortVlan or an external change detected by polling.
- ///
- VlanChanged,
-
- ///
- /// Indicates that PoE is being disabled on the port, either through a call to SetPortPoeState or an external change detected by polling.
- ///
- PoeDisableInProgress,
-
- ///
- /// Indicates that PoE has been disabled on the port, either through a successful call to SetPortPoeState or an external change detected by polling.
- ///
- PoEDisabled,
-
- ///
- /// Indicates that PoE is being enabled on the port, either through a call to SetPortPoeState or an external change detected by polling.
- ///
- PoeEnableInProgress,
-
- ///
- /// Indicates that PoE has been enabled on the port, either through a successful call to SetPortPoeState or an external change detected by polling.
- ///
- PoEEnabled
- }
-}
diff --git a/src/PepperDash.Essentials.Core/Devices/DeviceJsonApi.cs b/src/PepperDash.Essentials.Core/Devices/DeviceJsonApi.cs
index c01d5e81..c4ec2ca9 100644
--- a/src/PepperDash.Essentials.Core/Devices/DeviceJsonApi.cs
+++ b/src/PepperDash.Essentials.Core/Devices/DeviceJsonApi.cs
@@ -7,7 +7,6 @@ using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
-using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace PepperDash.Essentials.Core
@@ -177,20 +176,7 @@ namespace PepperDash.Essentials.Core
{
if (!conversionType.IsEnum)
{
- if (conversionType == typeof(byte[]) && value is string byteString)
- {
- var unescaped = UnescapeString(byteString);
- return System.Text.Encoding.GetEncoding(28591).GetBytes(unescaped);
- }
-
- var converted = Convert.ChangeType(value, conversionType, System.Globalization.CultureInfo.InvariantCulture);
-
- if (conversionType == typeof(string) && converted is string s)
- {
- return UnescapeString(s);
- }
-
- return converted;
+ return Convert.ChangeType(value, conversionType, System.Globalization.CultureInfo.InvariantCulture);
}
var stringValue = Convert.ToString(value);
@@ -203,32 +189,6 @@ namespace PepperDash.Essentials.Core
return Enum.Parse(conversionType, stringValue, true);
}
- ///
- /// Processes escape sequences in a string, converting sequences like \r, \n, \t, \xHH
- /// to their corresponding non-printable ASCII characters.
- ///
- private static string UnescapeString(string input)
- {
- if (string.IsNullOrEmpty(input))
- return input;
-
- return Regex.Replace(input, @"\\(r|n|t|\\|x[0-9A-Fa-f]{2})", match =>
- {
- var seq = match.Groups[1].Value;
- switch (seq)
- {
- case "r": return "\r";
- case "n": return "\n";
- case "t": return "\t";
- case "\\": return "\\";
- default:
- // \xHH hex escape
- var hex = seq.Substring(1);
- return ((char)Convert.ToInt32(hex, 16)).ToString();
- }
- });
- }
-
///
/// Gets the properties on a device
///
diff --git a/src/PepperDash.Essentials.Core/Devices/IHasDspPresetSave.cs b/src/PepperDash.Essentials.Core/Devices/IHasDspPresetSave.cs
deleted file mode 100644
index c628129e..00000000
--- a/src/PepperDash.Essentials.Core/Devices/IHasDspPresetSave.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-namespace PepperDash.Essentials.Core
-{
- ///
- /// Defines the contract for IHasDspPresetSave
- ///
- public interface IHasDspPresetSave : IDspPresets
- {
- ///
- /// Saves the preset by key
- ///
- /// key of preset to save
- void SavePreset(string key);
- }
-}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/Interfaces/ILogStrings.cs b/src/PepperDash.Essentials.Core/Interfaces/ILogStrings.cs
index b41a9020..d55194fd 100644
--- a/src/PepperDash.Essentials.Core/Interfaces/ILogStrings.cs
+++ b/src/PepperDash.Essentials.Core/Interfaces/ILogStrings.cs
@@ -7,10 +7,9 @@ using PepperDash.Core;
namespace PepperDash.Essentials.Core.Interfaces
{
- ///
- /// Defines the contract for ILogStrings
- ///
- [Obsolete("ILogStrings is no longer supported and will be removed in a future release.")]
+ ///
+ /// Defines the contract for ILogStrings
+ ///
public interface ILogStrings : IKeyed
{
///
diff --git a/src/PepperDash.Essentials.Core/Interfaces/ILogStringsWithLevel.cs b/src/PepperDash.Essentials.Core/Interfaces/ILogStringsWithLevel.cs
index 32ef104a..bb835919 100644
--- a/src/PepperDash.Essentials.Core/Interfaces/ILogStringsWithLevel.cs
+++ b/src/PepperDash.Essentials.Core/Interfaces/ILogStringsWithLevel.cs
@@ -7,16 +7,15 @@ using PepperDash.Core;
namespace PepperDash.Essentials.Core.Interfaces
{
- ///
- /// Defines the contract for ILogStringsWithLevel
- ///
- [Obsolete("ILogStringsWithLevel is no longer supported and will be removed in a future release.")]
+ ///
+ /// Defines the contract for ILogStringsWithLevel
+ ///
public interface ILogStringsWithLevel : IKeyed
{
///
/// Defines a class that is capable of logging a string with an int level
///
- void SendToLog(IKeyed device, Debug.ErrorLogLevel level, string logMessage);
+ void SendToLog(IKeyed device, Debug.ErrorLogLevel level,string logMessage);
}
}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/PepperDash.Essentials.Core.csproj b/src/PepperDash.Essentials.Core/PepperDash.Essentials.Core.csproj
index b7f333bd..251ba316 100644
--- a/src/PepperDash.Essentials.Core/PepperDash.Essentials.Core.csproj
+++ b/src/PepperDash.Essentials.Core/PepperDash.Essentials.Core.csproj
@@ -25,7 +25,7 @@
bin\$(Configuration)\PepperDash_Essentials_Core.xml
-
+
diff --git a/src/PepperDash.Essentials.Core/Routing/Extensions.cs b/src/PepperDash.Essentials.Core/Routing/Extensions.cs
index 303a0194..631ebd99 100644
--- a/src/PepperDash.Essentials.Core/Routing/Extensions.cs
+++ b/src/PepperDash.Essentials.Core/Routing/Extensions.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
@@ -51,6 +52,13 @@ namespace PepperDash.Essentials.Core
///
private static Dictionary> _tieLinesBySource;
+ ///
+ /// Cache of failed route attempts to avoid re-checking impossible paths.
+ /// Format: "sourceKey|destKey|signalType"
+ /// Uses ConcurrentDictionary as a thread-safe set (byte value is unused).
+ ///
+ private static readonly ConcurrentDictionary _impossibleRoutes = new ConcurrentDictionary();
+
///
/// Indexes all TieLines by source and destination device keys for faster lookups.
/// Should be called once at system startup after all TieLines are created.
@@ -113,6 +121,29 @@ namespace PepperDash.Essentials.Core
return TieLineCollection.Default.Where(t => t.SourcePort.ParentDevice.Key == sourceKey);
}
+ ///
+ /// Creates a cache key for route impossibility tracking.
+ ///
+ /// Source device key
+ /// Destination device key
+ /// Source port key
+ /// Destination port key
+ /// Signal type
+ /// Cache key string
+ private static string GetRouteKey(string sourceKey, string destKey, string sourcePortKey, string destinationPortKey, eRoutingSignalType type)
+ {
+ return $"{sourceKey}|{destKey}|{sourcePortKey}|{destinationPortKey}|{type}";
+ }
+
+ ///
+ /// Clears the impossible routes cache. Should be called if TieLines are added/removed at runtime.
+ ///
+ public static void ClearImpossibleRoutesCache()
+ {
+ _impossibleRoutes.Clear();
+ Debug.LogInformation("Impossible routes cache cleared");
+ }
+
///
/// Gets any existing RouteDescriptor for a destination, clears it using ReleaseRoute
/// and then attempts a new Route and if sucessful, stores that RouteDescriptor
@@ -218,7 +249,7 @@ namespace PepperDash.Essentials.Core
}
// otherwise, audioVideo needs to be handled as two steps.
- Debug.LogDebug(destination, "Attempting to build source route from {destinationKey} to {sourceKey} of type {type}", destination.Key, source.Key, signalType);
+ Debug.LogDebug(destination, "Attempting to build source route from {destinationKey} to {sourceKey} of type {type}", source.Key, signalType);
RouteDescriptor audioRouteDescriptor;
@@ -343,10 +374,8 @@ namespace PepperDash.Essentials.Core
IndexTieLines();
}
- var sinks = DeviceManager.AllDevices.OfType()
- .Where(d => !(d is IRoutingInputsOutputs)).ToList();
- var sources = DeviceManager.AllDevices.OfType()
- .Where(d => !(d is IRoutingInputsOutputs)).ToList();
+ var sinks = DeviceManager.AllDevices.OfType().Where(d => !(d is IRoutingInputsOutputs));
+ var sources = DeviceManager.AllDevices.OfType().Where(d => !(d is IRoutingInputsOutputs));
foreach (var sink in sinks)
{
@@ -356,17 +385,13 @@ namespace PepperDash.Essentials.Core
{
foreach (var outputPort in source.OutputPorts)
{
- var (audioOrSingleRoute, videoRoute) = sink.GetRouteToSource(source, outputPort.Type, inputPort, outputPort);
+ var (audioOrSingleRoute, videoRoute) = sink.GetRouteToSource(source, inputPort.Type, inputPort, outputPort);
if (audioOrSingleRoute == null && videoRoute == null)
{
continue;
}
- Debug.LogVerbose("AudioOrSingleRoute Found: {audioRoute}", audioOrSingleRoute);
-
- Debug.LogVerbose("VideoRoute Found: {videoRoute}", videoRoute);
-
if (audioOrSingleRoute != null)
{
// Only add routes that have actual switching steps
@@ -375,10 +400,6 @@ namespace PepperDash.Essentials.Core
continue;
}
- Debug.LogVerbose("Route mapped: {source} -> {sink} via {input}/{output}, type {type}",
- source.Key, sink.Key,
- inputPort.Key, outputPort.Key, audioOrSingleRoute.SignalType);
-
// Add to the appropriate collection(s) based on signal type
// Note: A single route descriptor with combined flags (e.g., AudioVideo) will be added once per matching signal type
if (audioOrSingleRoute.SignalType.HasFlag(eRoutingSignalType.Audio))
@@ -410,10 +431,6 @@ namespace PepperDash.Essentials.Core
continue;
}
- Debug.LogVerbose("Video route mapped: {source} -> {sink} via {input}/{output}",
- source.Key, sink.Key,
- inputPort.Key, outputPort.Key);
-
RouteDescriptors[eRoutingSignalType.Video].AddRouteDescriptor(videoRoute);
}
}
@@ -567,6 +584,14 @@ namespace PepperDash.Essentials.Core
{
cycle++;
+ // Check if this route has already been determined to be impossible
+ var routeKey = GetRouteKey(source.Key, destination.Key, sourcePort?.Key ?? "auto", destinationPort?.Key ?? "auto", signalType);
+ if (_impossibleRoutes.ContainsKey(routeKey))
+ {
+ Debug.LogVerbose("Route {0} is cached as impossible, skipping", routeKey);
+ return false;
+ }
+
Debug.LogVerbose("GetRouteToSource: {cycle} {sourceKey}:{sourcePortKey}--> {destinationKey}:{destinationPortKey} {type}", null, cycle, source.Key, sourcePort?.Key ?? "auto", destination.Key, destinationPort?.Key ?? "auto", signalType.ToString());
RoutingInputPort goodInputPort = null;
@@ -619,12 +644,8 @@ namespace PepperDash.Essentials.Core
// No direct tie? Run back out on the inputs' attached devices...
// Only the ones that are routing devices
- var midpointTieLines = destinationTieLines
- .Where(t => t.SourcePort.ParentDevice is IRoutingInputsOutputs)
- .ToList();
+ var midpointTieLines = destinationTieLines.Where(t => t.SourcePort.ParentDevice is IRoutingInputsOutputs);
- Debug.LogVerbose(destination, "Found {tieLineCount} tie lines to walk for {destinationKey}", midpointTieLines.Count, destination.Key);
-
//Create a list for tracking already checked devices to avoid loops, if it doesn't already exist from previous iteration
if (alreadyCheckedDevices == null)
alreadyCheckedDevices = new List();
@@ -664,7 +685,10 @@ namespace PepperDash.Essentials.Core
if (goodInputPort == null)
{
- Debug.LogVerbose(destination, "No route found to {0} from destination {1} for type {2}", source.Key, destination.Key, signalType);
+ Debug.LogVerbose(destination, "No route found to {0}", source.Key);
+
+ // Cache this as an impossible route
+ _impossibleRoutes.TryAdd(routeKey, 0);
return false;
}
diff --git a/src/PepperDash.Essentials.Core/Routing/RoutingFeedbackManager.cs b/src/PepperDash.Essentials.Core/Routing/RoutingFeedbackManager.cs
index 7fe41c5c..16822ff2 100644
--- a/src/PepperDash.Essentials.Core/Routing/RoutingFeedbackManager.cs
+++ b/src/PepperDash.Essentials.Core/Routing/RoutingFeedbackManager.cs
@@ -66,9 +66,6 @@ namespace PepperDash.Essentials.Core.Routing
foreach (var midpointKey in upstreamMidpoints)
{
- if (string.IsNullOrEmpty(midpointKey))
- continue;
-
if (!midpointToSinksMap.ContainsKey(midpointKey))
midpointToSinksMap[midpointKey] = new HashSet();
@@ -118,8 +115,7 @@ namespace PepperDash.Essentials.Core.Routing
if (tieLine.SourcePort.ParentDevice is IRoutingWithFeedback midpoint)
{
- if (!string.IsNullOrEmpty(midpoint.Key))
- midpoints.Add(midpoint.Key);
+ midpoints.Add(midpoint.Key);
// Find upstream TieLines connected to this midpoint's inputs
var midpointInputs = (midpoint as IRoutingInputs)?.InputPorts;
@@ -248,9 +244,6 @@ namespace PepperDash.Essentials.Core.Routing
var upstreamMidpoints = GetUpstreamMidpoints(sink);
foreach (var midpointKey in upstreamMidpoints)
{
- if (string.IsNullOrEmpty(midpointKey))
- continue;
-
if (!midpointToSinksMap.ContainsKey(midpointKey))
midpointToSinksMap[midpointKey] = new HashSet();
diff --git a/src/PepperDash.Essentials.Core/Routing/eRoutingSignalType.cs b/src/PepperDash.Essentials.Core/Routing/eRoutingSignalType.cs
index 708b7448..9fce9c5b 100644
--- a/src/PepperDash.Essentials.Core/Routing/eRoutingSignalType.cs
+++ b/src/PepperDash.Essentials.Core/Routing/eRoutingSignalType.cs
@@ -27,19 +27,16 @@ namespace PepperDash.Essentials.Core
///
/// Control signal type
///
- [Obsolete("UsbOutput is no longer supported and will be removed in a future release.")]
UsbOutput = 8,
///
/// Control signal type
///
- [Obsolete("UsbInput is no longer supported and will be removed in a future release.")]
UsbInput = 16,
///
/// Secondary audio signal type
///
- [Obsolete("SecondaryAudio is no longer supported and will be removed in a future release.")]
SecondaryAudio = 32
}
}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/Secrets/SecretsManager.cs b/src/PepperDash.Essentials.Core/Secrets/SecretsManager.cs
index ac4e80d8..382f5d55 100644
--- a/src/PepperDash.Essentials.Core/Secrets/SecretsManager.cs
+++ b/src/PepperDash.Essentials.Core/Secrets/SecretsManager.cs
@@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.Linq;
using Crestron.SimplSharp;
using PepperDash.Core;
-using PepperDash.Core.Logging;
using Serilog.Events;
namespace PepperDash.Essentials.Core
@@ -297,14 +296,19 @@ namespace PepperDash.Essentials.Core
{
var secretPresent = provider.TestSecret(key);
- provider.LogVerbose("SecretsProvider {0} {1} contain a secret entry for {2}", provider.Key, secretPresent ? "does" : "does not", key);
+ Debug.LogMessage(LogEventLevel.Verbose, provider, "SecretsProvider {0} {1} contain a secret entry for {2}", provider.Key, secretPresent ? "does" : "does not", key);
if (!secretPresent)
return
- $"Unable to update secret for {provider.Key}:{key} - Please use the 'SetSecret' command to modify it";
+ String.Format(
+ "Unable to update secret for {0}:{1} - Please use the 'SetSecret' command to modify it");
var response = provider.SetSecret(key, secret)
- ? $"Secret successfully set for {provider.Key}:{key}"
- : $"Unable to set secret for {provider.Key}:{key}";
+ ? String.Format(
+ "Secret successfully set for {0}:{1}",
+ provider.Key, key)
+ : String.Format(
+ "Unable to set secret for {0}:{1}",
+ provider.Key, key);
return response;
}
@@ -312,14 +316,19 @@ namespace PepperDash.Essentials.Core
{
var secretPresent = provider.TestSecret(key);
- provider.LogVerbose("SecretsProvider {0} {1} contain a secret entry for {2}", provider.Key, secretPresent ? "does" : "does not", key);
+ Debug.LogMessage(LogEventLevel.Verbose, provider, "SecretsProvider {0} {1} contain a secret entry for {2}", provider.Key, secretPresent ? "does" : "does not", key);
if (secretPresent)
return
- $"Unable to set secret for {provider.Key}:{key} - Please use the 'UpdateSecret' command to modify it";
+ String.Format(
+ "Unable to set secret for {0}:{1} - Please use the 'UpdateSecret' command to modify it");
var response = provider.SetSecret(key, secret)
- ? $"Secret successfully set for {provider.Key}:{key}"
- : $"Unable to set secret for {provider.Key}:{key}";
+ ? String.Format(
+ "Secret successfully set for {0}:{1}",
+ provider.Key, key)
+ : String.Format(
+ "Unable to set secret for {0}:{1}",
+ provider.Key, key);
return response;
}
@@ -368,10 +377,15 @@ namespace PepperDash.Essentials.Core
var key = args[1];
+
provider.SetSecret(key, "");
response = provider.SetSecret(key, "")
- ? $"Secret successfully deleted for {provider.Key}:{key}"
- : $"Unable to delete secret for {provider.Key}:{key}";
+ ? String.Format(
+ "Secret successfully deleted for {0}:{1}",
+ provider.Key, key)
+ : String.Format(
+ "Unable to delete secret for {0}:{1}",
+ provider.Key, key);
CrestronConsole.ConsoleCommandResponse(response);
return;
diff --git a/src/PepperDash.Essentials.Core/Web/EssentialsWebApi.cs b/src/PepperDash.Essentials.Core/Web/EssentialsWebApi.cs
index cfbaa1df..9e781883 100644
--- a/src/PepperDash.Essentials.Core/Web/EssentialsWebApi.cs
+++ b/src/PepperDash.Essentials.Core/Web/EssentialsWebApi.cs
@@ -20,8 +20,6 @@ namespace PepperDash.Essentials.Core.Web
private readonly WebApiServer _debugServer;
-
-
///
/// http(s)://{ipaddress}/cws/{basePath}
/// http(s)://{ipaddress}/VirtualControl/Rooms/{roomId}/cws/{basePath}
@@ -95,11 +93,6 @@ namespace PepperDash.Essentials.Core.Web
Name = "ReportVersions",
RouteHandler = new ReportVersionsRequestHandler()
},
- new HttpCwsRoute("packageManifest")
- {
- Name = "GetPackageManifest",
- RouteHandler = new GetPackageManifestRequestHandler()
- },
new HttpCwsRoute("appdebug")
{
Name = "AppDebug",
@@ -267,7 +260,7 @@ namespace PepperDash.Essentials.Core.Web
_server.Start();
_debugServer.Start();
-
+
GetPaths();
}
@@ -308,15 +301,7 @@ namespace PepperDash.Essentials.Core.Web
{
Debug.LogMessage(LogEventLevel.Information, this, "{routeName:l}: {routePath:l}/{routeUrl:l}", route.Name, path, route.Url);
}
- Debug.LogInformation(this, "Web API initialized and ready to accept requests");
-
Debug.LogMessage(LogEventLevel.Information, this, new string('-', 50));
-
- var debugAppUrl = CrestronEnvironment.DevicePlatform == eDevicePlatform.Server
- ? $"https://{hostname}/VirtualControl/Rooms/{InitialParametersClass.RoomId}/cws/debug"
- : $"https://{currentIp}/cws/debug";
-
- Debug.LogMessage(LogEventLevel.Information, this, "Developer Tools Web App available at: {debugAppUrl:l}", debugAppUrl);
}
}
}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/Web/RequestHandlers/DebugSessionRequestHandler.cs b/src/PepperDash.Essentials.Core/Web/RequestHandlers/DebugSessionRequestHandler.cs
index 59c662dc..01e38834 100644
--- a/src/PepperDash.Essentials.Core/Web/RequestHandlers/DebugSessionRequestHandler.cs
+++ b/src/PepperDash.Essentials.Core/Web/RequestHandlers/DebugSessionRequestHandler.cs
@@ -17,10 +17,7 @@ namespace PepperDash.Essentials.Core.Web.RequestHandlers
/// Represents a DebugSessionRequestHandler
///
public class DebugSessionRequestHandler : WebApiBaseRequestHandler
- {
- private CTimer _portForwardTimeoutTimer;
- private readonly object _timerLock = new object();
-
+ {
///
/// Constructor
///
@@ -51,7 +48,6 @@ namespace PepperDash.Essentials.Core.Web.RequestHandlers
CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, 0);
var port = 0;
- string csIp = null;
if (!Debug.WebsocketSink.IsRunning)
{
@@ -63,50 +59,14 @@ namespace PepperDash.Essentials.Core.Web.RequestHandlers
Debug.SetWebSocketMinimumDebugLevel(Serilog.Events.LogEventLevel.Verbose);
}
- // Attempt to get the CS LAN IP and forward the port
- try
- {
- var csAdapterId = CrestronEthernetHelper.GetAdapterdIdForSpecifiedAdapterType(
- EthernetAdapterType.EthernetCSAdapter);
- csIp = CrestronEthernetHelper.GetEthernetParameter(
- CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, csAdapterId);
-
- if (port > 0)
- {
- var result = CrestronEthernetHelper.AddPortForwarding(
- (ushort)port, (ushort)port, csIp,
- CrestronEthernetHelper.ePortMapTransport.TCP);
-
- if (result != CrestronEthernetHelper.PortForwardingUserPatRetCodes.NoErr)
- {
- Debug.LogMessage(LogEventLevel.Warning, "Error adding port forwarding for debug websocket: {0}", result);
- }
- else
- {
- Debug.LogMessage(LogEventLevel.Information, "Port {0} forwarded to CS LAN for debug websocket", port);
- StartPortForwardTimeout(port, csIp);
- }
- }
- }
- catch (ArgumentException)
- {
- Debug.LogMessage(LogEventLevel.Debug, "This processor does not have a CS LAN adapter; skipping port forwarding");
- }
- catch (Exception ex)
- {
- Debug.LogMessage(LogEventLevel.Warning, "Error automatically forwarding debug websocket port to CS LAN: {0}", ex.Message);
- }
-
var url = Debug.WebsocketSink.Url;
- var data = new
+ object data = new
{
- url = Debug.WebsocketSink.Url,
- fallbackUrl = csIp != null ? url.Replace(csIp, ip) : null
+ url = Debug.WebsocketSink.Url
};
Debug.LogMessage(LogEventLevel.Information, "Debug Session URL: {0}", url);
- Debug.LogMessage(LogEventLevel.Information, "Fallback Debug Session URL: {0}", data.fallbackUrl);
// Return the port number with the full url of the WS Server
var res = JsonConvert.SerializeObject(data);
@@ -130,49 +90,8 @@ namespace PepperDash.Essentials.Core.Web.RequestHandlers
///
protected override void HandlePost(HttpCwsContext context)
{
- CancelPortForwardTimeout();
-
- var port = Debug.WebsocketSink.Port;
-
Debug.WebsocketSink.StopServer();
- // Remove the port forwarding entry
- try
- {
- var csAdapterId = CrestronEthernetHelper.GetAdapterdIdForSpecifiedAdapterType(
- EthernetAdapterType.EthernetCSAdapter);
- var csIp = CrestronEthernetHelper.GetEthernetParameter(
- CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, csAdapterId);
-
- if (port <= 0)
- {
- Debug.LogMessage(LogEventLevel.Debug, "Debug websocket port is not set; skipping port forwarding removal");
- }
- else
- {
- var result = CrestronEthernetHelper.RemovePortForwarding(
- (ushort)port, (ushort)port, csIp,
- CrestronEthernetHelper.ePortMapTransport.TCP);
-
- if (result != CrestronEthernetHelper.PortForwardingUserPatRetCodes.NoErr)
- {
- Debug.LogMessage(LogEventLevel.Warning, "Error removing port forwarding for debug websocket: {0}", result);
- }
- else
- {
- Debug.LogMessage(LogEventLevel.Information, "Port forwarding for port {0} removed", port);
- }
- }
- }
- catch (ArgumentException)
- {
- Debug.LogMessage(LogEventLevel.Debug, "This processor does not have a CS LAN adapter; skipping port forwarding removal");
- }
- catch (Exception ex)
- {
- Debug.LogMessage(LogEventLevel.Warning, "Error removing debug websocket port forwarding: {0}", ex.Message);
- }
-
context.Response.StatusCode = 200;
context.Response.StatusDescription = "OK";
context.Response.End();
@@ -180,55 +99,5 @@ namespace PepperDash.Essentials.Core.Web.RequestHandlers
Debug.LogMessage(LogEventLevel.Information, "Websocket Debug Session Stopped");
}
- private void StartPortForwardTimeout(int port, string csIp)
- {
- lock (_timerLock)
- {
- _portForwardTimeoutTimer?.Dispose();
- _portForwardTimeoutTimer = new CTimer(_ =>
- {
- if (Debug.WebsocketSink.HasActiveConnections)
- {
- Debug.LogMessage(LogEventLevel.Debug, "Debug websocket has active connections; keeping port forward");
- return;
- }
-
- Debug.LogMessage(LogEventLevel.Information, "No debug websocket connection within 30 seconds; removing port forward for port {0}", port);
-
- try
- {
- var result = CrestronEthernetHelper.RemovePortForwarding(
- (ushort)port, (ushort)port, csIp,
- CrestronEthernetHelper.ePortMapTransport.TCP);
-
- if (result != CrestronEthernetHelper.PortForwardingUserPatRetCodes.NoErr)
- {
- Debug.LogMessage(LogEventLevel.Warning, "Error removing port forwarding on timeout: {0}", result);
- }
- else
- {
- Debug.LogMessage(LogEventLevel.Information, "Port forwarding for port {0} removed due to timeout", port);
- }
- }
- catch (Exception ex)
- {
- Debug.LogMessage(LogEventLevel.Warning, "Error removing port forwarding on timeout: {0}", ex.Message);
- }
- }, 30000);
- }
- }
-
- ///
- /// Cancels the port forward timeout timer if a session is being explicitly stopped.
- ///
- private void CancelPortForwardTimeout()
- {
- lock (_timerLock)
- {
- _portForwardTimeoutTimer?.Dispose();
- _portForwardTimeoutTimer = null;
- }
- }
-
}
}
diff --git a/src/PepperDash.Essentials.Core/Web/RequestHandlers/GetFeedbacksForDeviceRequestHandler.cs b/src/PepperDash.Essentials.Core/Web/RequestHandlers/GetFeedbacksForDeviceRequestHandler.cs
index 9831fa91..ca9eeb81 100644
--- a/src/PepperDash.Essentials.Core/Web/RequestHandlers/GetFeedbacksForDeviceRequestHandler.cs
+++ b/src/PepperDash.Essentials.Core/Web/RequestHandlers/GetFeedbacksForDeviceRequestHandler.cs
@@ -1,14 +1,13 @@
-using System;
-using System.Linq;
+using System.Linq;
using Crestron.SimplSharp.WebScripting;
using Newtonsoft.Json;
using PepperDash.Core.Web.RequestHandlers;
namespace PepperDash.Essentials.Core.Web.RequestHandlers
{
- ///
- /// Represents a GetFeedbacksForDeviceRequestHandler
- ///
+ ///
+ /// Represents a GetFeedbacksForDeviceRequestHandler
+ ///
public class GetFeedbacksForDeviceRequestHandler : WebApiBaseRequestHandler
{
///
@@ -77,7 +76,7 @@ namespace PepperDash.Essentials.Core.Web.RequestHandlers
Value = feedback.IntValue
};
- var stringFeedback =
+ var stringFeedback =
from feedback in device.Feedbacks.OfType()
where !string.IsNullOrEmpty(feedback.Key)
select new
diff --git a/src/PepperDash.Essentials.Core/Web/RequestHandlers/GetPackageManifestRequestHandler.cs b/src/PepperDash.Essentials.Core/Web/RequestHandlers/GetPackageManifestRequestHandler.cs
deleted file mode 100644
index eeea75cd..00000000
--- a/src/PepperDash.Essentials.Core/Web/RequestHandlers/GetPackageManifestRequestHandler.cs
+++ /dev/null
@@ -1,259 +0,0 @@
-using System;
-using System.Linq;
-using System.Reflection;
-using Crestron.SimplSharp.WebScripting;
-using Newtonsoft.Json;
-using PepperDash.Core.Web.RequestHandlers;
-using PepperDash.Essentials.Core.Config;
-
-namespace PepperDash.Essentials.Core.Web.RequestHandlers
-{
- ///
- /// Represents a GetPackageManifestRequestHandler
- ///
- public class GetPackageManifestRequestHandler : WebApiBaseRequestHandler
- {
- ///
- /// Constructor
- ///
- ///
- /// base(true) enables CORS support by default
- ///
- public GetPackageManifestRequestHandler()
- : base(true)
- {
- }
-
- ///
- /// Handles GET method requests
- ///
- ///
- protected override void HandleGet(HttpCwsContext context)
- {
- try
- {
- var result = CloneVersionData(ConfigReader.ConfigObject?.Versions) ?? new VersionData();
-
- PopulateEssentials(result);
- PopulatePackages(result);
-
- var js = JsonConvert.SerializeObject(result, Formatting.Indented);
-
- context.Response.StatusCode = 200;
- context.Response.StatusDescription = "OK";
- context.Response.ContentType = "application/json";
- context.Response.ContentEncoding = System.Text.Encoding.UTF8;
- context.Response.Write(js, false);
- context.Response.End();
- }
- catch (Exception ex)
- {
- PepperDash.Core.Debug.LogMessage(ex, "Exception handling GET /packageManifest request");
- context.Response.StatusCode = 500;
- context.Response.StatusDescription = "Internal Server Error";
- context.Response.End();
- }
- }
-
- ///
- /// Deep-copies the config's VersionData so the live config object is never mutated
- ///
- private static VersionData CloneVersionData(VersionData source)
- {
- if (source == null)
- {
- return null;
- }
-
- var json = JsonConvert.SerializeObject(source);
- return JsonConvert.DeserializeObject(json);
- }
-
- ///
- /// Enriches (or creates) the essentials entry from the loaded PepperDash.Essentials.Core assembly
- ///
- private static void PopulateEssentials(VersionData result)
- {
- var essentials = result.Essentials ?? new NugetVersion();
-
- essentials.Version = Global.AssemblyVersion;
-
- // The main program assembly (PackageId "PepperDashEssentials") is what's actually published
- // to NuGet, but this handler lives in PepperDash.Essentials.Core, which can't reference that
- // project's types directly (Essentials -> Core, not the reverse). PluginLoader.EssentialsAssembly.Assembly
- // is unreliable (often left null - see PluginLoader.SetEssentialsAssembly), so look it up
- // directly from the loaded AppDomain by its Directory.Build.props-embedded PackageId metadata
- // (every project's .csproj sets its own PackageId explicitly), falling back to this handler's
- // own (Core) assembly if it can't be found.
- var essentialsAssembly = AppDomain.CurrentDomain.GetAssemblies()
- .FirstOrDefault(a => string.Equals(GetAssemblyMetadataValue(a, "PackageId"), "PepperDashEssentials", StringComparison.OrdinalIgnoreCase))
- ?? typeof(GetPackageManifestRequestHandler).Assembly;
-
- var repoUrl = TrimTrailingGit(GetAssemblyMetadataValue(essentialsAssembly, "RepositoryUrl"));
- if (!string.IsNullOrEmpty(repoUrl))
- {
- essentials.RepoUrl = repoUrl;
- }
-
- var name = GetAssemblyProduct(essentialsAssembly);
- if (!string.IsNullOrEmpty(name))
- {
- essentials.Name = name;
- }
-
- // Prefer the PackageId embedded via Directory.Build.props' AssemblyMetadata item (the
- // authoritative source, matching the actual published PackageId) over any config-supplied
- // or hardcoded value.
- var reflectedPackageId = GetAssemblyMetadataValue(essentialsAssembly, "PackageId");
- if (!string.IsNullOrEmpty(reflectedPackageId))
- {
- essentials.PackageId = reflectedPackageId;
- }
- else if (string.IsNullOrEmpty(essentials.PackageId))
- {
- essentials.PackageId = "PepperDashEssentials";
- }
-
- result.Essentials = essentials;
- }
-
- ///
- /// Merges reflection data from loaded plugin assemblies with the config's packages list
- ///
- private static void PopulatePackages(VersionData result)
- {
- // Filter out null entries defensively - the packages list is deserialized from user-editable
- // config JSON, so a malformed "packages": [null, ...] shouldn't throw and 500 the endpoint.
- var configPackages = (result.Packages ?? new System.Collections.Generic.List())
- .Where(p => p != null)
- .ToList();
- var matchedConfigPackages = new System.Collections.Generic.HashSet();
- var mergedPackages = new System.Collections.Generic.List();
-
- foreach (var loaded in PluginLoader.EssentialsPluginAssemblies.Where(a => a.Assembly != null))
- {
- var reflectedVersion = loaded.Version;
- if (string.IsNullOrEmpty(reflectedVersion))
- {
- // Never emit an entry with no version - the extension's parser drops entries
- // whose version isn't a string.
- continue;
- }
-
- var reflectedRepoUrl = TrimTrailingGit(GetAssemblyMetadataValue(loaded.Assembly, "RepositoryUrl"));
- var reflectedName = GetAssemblyProduct(loaded.Assembly);
-
- // Plugins built from a Directory.Build.props that embeds
- // carry their PackageId
- // directly - this is authoritative and should be preferred over the title/name fallback chain.
- var reflectedPackageId = GetAssemblyMetadataValue(loaded.Assembly, "PackageId");
-
- var assemblyTitle = GetAssemblyTitle(loaded.Assembly);
- var assemblyName = loaded.Assembly.GetName().Name;
- var assemblyNameNoSeriesSuffix = StripTrailingSeriesSuffix(assemblyName);
-
- var match = configPackages.FirstOrDefault(p =>
- !matchedConfigPackages.Contains(p) &&
- !string.IsNullOrEmpty(p.PackageId) &&
- (string.Equals(p.PackageId, reflectedPackageId, StringComparison.OrdinalIgnoreCase) ||
- string.Equals(p.PackageId, assemblyTitle, StringComparison.OrdinalIgnoreCase) ||
- string.Equals(p.PackageId, assemblyName, StringComparison.OrdinalIgnoreCase) ||
- string.Equals(p.PackageId, assemblyNameNoSeriesSuffix, StringComparison.OrdinalIgnoreCase)));
-
- if (match != null)
- {
- matchedConfigPackages.Add(match);
-
- mergedPackages.Add(new NugetVersion
- {
- Name = !string.IsNullOrEmpty(match.Name) ? match.Name : reflectedName,
- RepoUrl = !string.IsNullOrEmpty(match.RepoUrl) ? match.RepoUrl : reflectedRepoUrl,
- PackageId = !string.IsNullOrEmpty(reflectedPackageId) ? reflectedPackageId : match.PackageId,
- Version = reflectedVersion
- });
- }
- else
- {
- // Loaded but not present (or not matched) in config - emit the reflected PackageId
- // when the assembly carries one, otherwise leave it null as before.
- mergedPackages.Add(new NugetVersion
- {
- Name = reflectedName,
- RepoUrl = reflectedRepoUrl,
- PackageId = reflectedPackageId,
- Version = reflectedVersion,
- });
- }
- }
-
- // Configured but not currently loaded - pass through unchanged
- mergedPackages.AddRange(configPackages.Where(p => !matchedConfigPackages.Contains(p)));
-
- result.Packages = mergedPackages;
- }
-
- private static string GetAssemblyMetadataValue(Assembly assembly, string key)
- {
- if (assembly == null)
- {
- return null;
- }
-
- var match = assembly.GetCustomAttributes(typeof(AssemblyMetadataAttribute), false)
- .Cast()
- .FirstOrDefault(a => string.Equals(a.Key, key, StringComparison.OrdinalIgnoreCase));
-
- return match?.Value;
- }
-
- private static string GetAssemblyProduct(Assembly assembly)
- {
- if (assembly == null)
- {
- return null;
- }
-
- var attribute = assembly.GetCustomAttributes(typeof(AssemblyProductAttribute), false)
- .FirstOrDefault() as AssemblyProductAttribute;
-
- return attribute?.Product;
- }
-
- private static string GetAssemblyTitle(Assembly assembly)
- {
- if (assembly == null)
- {
- return null;
- }
-
- var attribute = assembly.GetCustomAttributes(typeof(AssemblyTitleAttribute), false)
- .FirstOrDefault() as AssemblyTitleAttribute;
-
- return attribute?.Title;
- }
-
- private static string StripTrailingSeriesSuffix(string assemblyName)
- {
- const string suffix = ".4Series";
-
- if (string.IsNullOrEmpty(assemblyName) || !assemblyName.EndsWith(suffix, StringComparison.OrdinalIgnoreCase))
- {
- return assemblyName;
- }
-
- return assemblyName.Substring(0, assemblyName.Length - suffix.Length);
- }
-
- private static string TrimTrailingGit(string repoUrl)
- {
- const string suffix = ".git";
-
- if (string.IsNullOrEmpty(repoUrl) || !repoUrl.EndsWith(suffix, StringComparison.OrdinalIgnoreCase))
- {
- return repoUrl;
- }
-
- return repoUrl.Substring(0, repoUrl.Length - suffix.Length);
- }
- }
-}
diff --git a/src/PepperDash.Essentials.Core/Web/RequestHandlers/LoginRequestHandler.cs b/src/PepperDash.Essentials.Core/Web/RequestHandlers/LoginRequestHandler.cs
index 949f8bd5..5a4b7df7 100644
--- a/src/PepperDash.Essentials.Core/Web/RequestHandlers/LoginRequestHandler.cs
+++ b/src/PepperDash.Essentials.Core/Web/RequestHandlers/LoginRequestHandler.cs
@@ -1,6 +1,5 @@
using System;
-using System.Collections.Generic;
using Crestron.SimplSharp.CrestronAuthentication;
using Crestron.SimplSharp.WebScripting;
using Newtonsoft.Json;
@@ -92,19 +91,7 @@ namespace PepperDash.Essentials.Core.Web.RequestHandlers
context.Response.StatusDescription = "OK";
context.Response.ContentType = "application/json";
context.Response.ContentEncoding = System.Text.Encoding.UTF8;
- context.Response.Write(JsonConvert.SerializeObject(
- new
- {
- Token = new LoginResponse
- {
- UserName = token.UserName,
- Access = token.Access,
- State = token.State,
- Groups = token.Groups,
- ADConnect = token.ADConnect,
- Valid = token.Valid
- }
- }, Formatting.Indented), false);
+ context.Response.Write(JsonConvert.SerializeObject(new { Token = token }, Formatting.Indented), false);
context.Response.End();
}
catch (System.Exception ex)
@@ -134,40 +121,4 @@ namespace PepperDash.Essentials.Core.Web.RequestHandlers
///
public string Password { get; set; }
}
-
- ///
- /// Represents a LoginResponse
- ///
- internal class LoginResponse
- {
- ///
- /// Gets or sets the username.
- ///
- public string UserName { get; set; }
-
- ///
- /// Gets or sets the access level.
- ///
- public Authentication.UserAuthenticationLevelEnum Access { get; set; }
-
- ///
- /// Gets or sets the token authenticated state.
- ///
- public Authentication.eTokenAuthenticatedState State { get; set; }
-
- ///
- /// Gets or sets the list of groups.
- ///
- public List Groups { get; set; }
-
- ///
- /// Gets or sets the active directory connection flag.
- ///
- public int ADConnect { get; set; }
-
- ///
- /// Gets or sets the valid flag indicating whether the token is valid.
- ///
- public bool Valid { get; set; }
- }
}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Devices.Common/Cameras/CameraVisca.cs b/src/PepperDash.Essentials.Devices.Common/Cameras/CameraVisca.cs
index f383dd0a..126bcd27 100644
--- a/src/PepperDash.Essentials.Devices.Common/Cameras/CameraVisca.cs
+++ b/src/PepperDash.Essentials.Devices.Common/Cameras/CameraVisca.cs
@@ -17,8 +17,6 @@ namespace PepperDash.Essentials.Devices.Common.Cameras
///
/// Represents a CameraVisca
///
- [Obsolete("CameraVisca is no longer supported and will be removed in a future release. Use the CameraVisca plugin instead.")]
-
public class CameraVisca : CameraBase, IHasCameraPtzControl, ICommunicationMonitor, IHasCameraPresets, IHasPowerControlWithFeedback, IBridgeAdvanced, IHasCameraFocusControl, IHasAutoFocusMode
{
private readonly CameraViscaPropertiesConfig PropertiesConfig;
diff --git a/src/PepperDash.Essentials.Devices.Common/Displays/ScreenLiftController.cs b/src/PepperDash.Essentials.Devices.Common/Displays/ScreenLiftController.cs
index d542f52a..80c1f8ee 100644
--- a/src/PepperDash.Essentials.Devices.Common/Displays/ScreenLiftController.cs
+++ b/src/PepperDash.Essentials.Devices.Common/Displays/ScreenLiftController.cs
@@ -152,15 +152,6 @@ namespace PepperDash.Essentials.Devices.Common.Shades
private void IsCoolingDownFeedback_OutputChange(object sender, FeedbackEventArgs e)
{
- if (Config.DisableAutoRaiseOnPowerOff)
- {
- this.LogDebug(
- "Auto-raise on power-off disabled for {type}; leaving position unchanged (manual control only)",
- Type
- );
- return;
- }
-
if (
!DisplayDevice.IsCoolingDownFeedback.BoolValue
&& Type == eScreenLiftControlType.lift
@@ -183,15 +174,6 @@ namespace PepperDash.Essentials.Devices.Common.Shades
{
if (DisplayDevice.IsWarmingUpFeedback.BoolValue)
{
- if (Config.DisableAutoLowerOnPowerOn)
- {
- this.LogDebug(
- "Auto-lower on power-on disabled for {type}; leaving position unchanged (manual control only)",
- Type
- );
- return;
- }
-
Lower();
}
}
diff --git a/src/PepperDash.Essentials.Devices.Common/Displays/ScreenLiftControllerConfigProperties.cs b/src/PepperDash.Essentials.Devices.Common/Displays/ScreenLiftControllerConfigProperties.cs
index 88113a08..1c4f9906 100644
--- a/src/PepperDash.Essentials.Devices.Common/Displays/ScreenLiftControllerConfigProperties.cs
+++ b/src/PepperDash.Essentials.Devices.Common/Displays/ScreenLiftControllerConfigProperties.cs
@@ -41,23 +41,5 @@ namespace PepperDash.Essentials.Devices.Common.Shades
///
[JsonProperty("muteOnScreenUp")]
public bool MuteOnScreenUp { get; set; }
-
- ///
- /// When true, this controller does NOT automatically lower when its assigned display powers on
- /// (warms up). Manual Raise/Lower still work, and the power-off auto-raise is unaffected. Intended
- /// for a projector screen that must not auto-drop in a public space for safety, while the projector
- /// lift (a separate controller) can still drop automatically.
- ///
- [JsonProperty("disableAutoLowerOnPowerOn")]
- public bool DisableAutoLowerOnPowerOn { get; set; }
-
- ///
- /// When true, this controller does NOT automatically raise when its assigned display powers off
- /// (cools down). Manual Raise/Lower still work, and the power-on auto-lower is unaffected. The
- /// companion to ; together they make a controller fully
- /// manual while leaving other controllers (e.g. the lift) on their default automatic behavior.
- ///
- [JsonProperty("disableAutoRaiseOnPowerOff")]
- public bool DisableAutoRaiseOnPowerOff { get; set; }
}
}
diff --git a/src/PepperDash.Essentials.Devices.Common/PepperDash.Essentials.Devices.Common.csproj b/src/PepperDash.Essentials.Devices.Common/PepperDash.Essentials.Devices.Common.csproj
index 317fa2d3..7be4372d 100644
--- a/src/PepperDash.Essentials.Devices.Common/PepperDash.Essentials.Devices.Common.csproj
+++ b/src/PepperDash.Essentials.Devices.Common/PepperDash.Essentials.Devices.Common.csproj
@@ -29,6 +29,6 @@
-
+
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Devices.Common/VideoCodec/MockVC/MockVC.cs b/src/PepperDash.Essentials.Devices.Common/VideoCodec/MockVC/MockVC.cs
index bdefa09d..e9f7908c 100644
--- a/src/PepperDash.Essentials.Devices.Common/VideoCodec/MockVC/MockVC.cs
+++ b/src/PepperDash.Essentials.Devices.Common/VideoCodec/MockVC/MockVC.cs
@@ -21,7 +21,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec
///
/// Represents a MockVC
///
- public class MockVC : VideoCodecBase, IRoutingSource, IHasCallHistory, IHasScheduleAwareness, IHasCallFavorites, IHasDirectory, IHasCodecCameras, IHasCameraAutoMode, IHasCodecRoomPresets, IRoutingInputs
+ public class MockVC : VideoCodecBase, IRoutingSource, IHasCallHistory, IHasScheduleAwareness, IHasCallFavorites, IHasDirectory, IHasCodecCameras, IHasCameraAutoMode, IHasCodecRoomPresets
{
///
/// Gets or sets the PropertiesConfig
diff --git a/src/PepperDash.Essentials.Devices.Common/VideoCodec/VideoCodecBase.cs b/src/PepperDash.Essentials.Devices.Common/VideoCodec/VideoCodecBase.cs
index 04c7e64d..b0b5ef55 100644
--- a/src/PepperDash.Essentials.Devices.Common/VideoCodec/VideoCodecBase.cs
+++ b/src/PepperDash.Essentials.Devices.Common/VideoCodec/VideoCodecBase.cs
@@ -26,7 +26,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec
///
/// Base class for video codec devices
///
- public abstract class VideoCodecBase : ReconfigurableDevice,
+ public abstract class VideoCodecBase : ReconfigurableDevice, IRoutingInputsOutputs,
IUsageTracking, IHasDialer, IHasContentSharing, ICodecAudio, iVideoCodecInfo, IBridgeAdvanced, IHasStandbyMode
{
private const int XSigEncoding = 28591;
diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/DeviceMessageBase.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/DeviceMessageBase.cs
index 0198df2f..54a6ec36 100644
--- a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/DeviceMessageBase.cs
+++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/DeviceMessageBase.cs
@@ -10,7 +10,7 @@ namespace PepperDash.Essentials.AppServer.Messengers
///
/// The device key
///
- [JsonProperty("key", NullValueHandling = NullValueHandling.Ignore)]
+ [JsonProperty("key")]
///
/// Gets or sets the Key
///
@@ -19,19 +19,19 @@ namespace PepperDash.Essentials.AppServer.Messengers
///
/// The device name
///
- [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)]
+ [JsonProperty("name")]
public string Name { get; set; }
///
/// The type of the message class
///
- [JsonProperty("messageType", NullValueHandling = NullValueHandling.Ignore)]
+ [JsonProperty("messageType")]
public string MessageType => GetType().Name;
///
/// Gets or sets the MessageBasePath
///
- [JsonProperty("messageBasePath", NullValueHandling = NullValueHandling.Ignore)]
+ [JsonProperty("messageBasePath")]
public string MessageBasePath { get; set; }
}
diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/DeviceStateMessageBase.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/DeviceStateMessageBase.cs
index c5c0ab65..87f19e3f 100644
--- a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/DeviceStateMessageBase.cs
+++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/DeviceStateMessageBase.cs
@@ -1,4 +1,3 @@
-using System;
using System.Collections.Generic;
using Newtonsoft.Json;
@@ -12,15 +11,13 @@ namespace PepperDash.Essentials.AppServer.Messengers
///
/// The interfaces implmented by the device sending the messsage
///
- [JsonProperty("interfaces", NullValueHandling = NullValueHandling.Ignore)]
- [Obsolete("Interfaces are no longer supported and will be removed in a future release. Interfaces for all devices are now retrieved via the /joinroom endpoint in the MobileControlWebsocketServer")]
+ [JsonProperty("interfaces")]
public List Interfaces { get; private set; }
///
/// Sets the interfaces implemented by the device sending the message
///
///
- [Obsolete("SetInterfaces is no longer supported and will be removed in a future release. Interfaces for all devices are now retrieved via the /joinroom endpoint in the MobileControlWebsocketServer")]
public void SetInterfaces(List interfaces)
{
Interfaces = interfaces;
diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/MessengerBase.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/MessengerBase.cs
index eb3afec3..3031f4ba 100644
--- a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/MessengerBase.cs
+++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/MessengerBase.cs
@@ -264,9 +264,6 @@ namespace PepperDash.Essentials.AppServer.Messengers
message.Name = _device.Name;
- message.MessageBasePath = MessagePath;
-
-
var token = JToken.FromObject(message);
PostStatusMessage(token, MessagePath, clientId);
diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/PepperDash.Essentials.MobileControl.Messengers.csproj b/src/PepperDash.Essentials.MobileControl.Messengers/PepperDash.Essentials.MobileControl.Messengers.csproj
index a9223061..d13d1a09 100644
--- a/src/PepperDash.Essentials.MobileControl.Messengers/PepperDash.Essentials.MobileControl.Messengers.csproj
+++ b/src/PepperDash.Essentials.MobileControl.Messengers/PepperDash.Essentials.MobileControl.Messengers.csproj
@@ -33,7 +33,7 @@
-
+
diff --git a/src/PepperDash.Essentials.MobileControl/ConnectedClientVersionInfo.cs b/src/PepperDash.Essentials.MobileControl/ConnectedClientVersionInfo.cs
deleted file mode 100644
index 68f5e36e..00000000
--- a/src/PepperDash.Essentials.MobileControl/ConnectedClientVersionInfo.cs
+++ /dev/null
@@ -1,61 +0,0 @@
-using System;
-using Newtonsoft.Json;
-
-namespace PepperDash.Essentials
-{
- ///
- /// Represents the version information reported by a connected Mobile Control UI client
- ///
- public class ConnectedClientVersionInfo
- {
- ///
- /// Gets or sets the client id
- ///
- [JsonProperty("clientId")]
- public string ClientId { get; set; }
-
- ///
- /// Gets or sets the room key the client joined
- ///
- [JsonProperty("roomKey")]
- public string RoomKey { get; set; }
-
- ///
- /// Gets or sets the touchpanel key the client joined as, if any
- ///
- [JsonProperty("touchpanelKey")]
- public string TouchpanelKey { get; set; }
-
- ///
- /// Gets or sets the app version reported by the client (e.g. the React app's build-time APP_VERSION)
- ///
- [JsonProperty("appVersion")]
- public string AppVersion { get; set; }
-
- ///
- /// Gets or sets the expected app version from the system config's versions.touchpanelWrapperApp, if configured
- ///
- [JsonProperty("expectedAppVersion")]
- public string ExpectedAppVersion { get; set; }
-
- ///
- /// Gets or sets the UTC time the client last reported this version
- ///
- [JsonProperty("lastSeen")]
- public DateTime LastSeen { get; set; }
-
- ///
- /// Returns a copy of this instance, safe for callers outside the owning lock to hold/mutate
- /// without affecting the internally tracked instance
- ///
- public ConnectedClientVersionInfo Clone() => new ConnectedClientVersionInfo
- {
- ClientId = ClientId,
- RoomKey = RoomKey,
- TouchpanelKey = TouchpanelKey,
- AppVersion = AppVersion,
- ExpectedAppVersion = ExpectedAppVersion,
- LastSeen = LastSeen
- };
- }
-}
diff --git a/src/PepperDash.Essentials.MobileControl/MobileControlConfig.cs b/src/PepperDash.Essentials.MobileControl/MobileControlConfig.cs
index 963e7fd5..ec7219a3 100644
--- a/src/PepperDash.Essentials.MobileControl/MobileControlConfig.cs
+++ b/src/PepperDash.Essentials.MobileControl/MobileControlConfig.cs
@@ -1,5 +1,4 @@
-using System;
-using System.Collections.Generic;
+using System.Collections.Generic;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
@@ -41,10 +40,9 @@ namespace PepperDash.Essentials
public bool EnableApiServer { get; set; } = true;
///
- /// Enables subscriptions for messengers
+ /// Enable subscriptions for Messengers
///
[JsonProperty("enableMessengerSubscriptions")]
- [Obsolete("This property is obsolete and will be removed in a future version. All messengers are now subscription based.")]
public bool EnableMessengerSubscriptions { get; set; }
}
@@ -290,4 +288,4 @@ namespace PepperDash.Essentials
///
NEO
}
-}
+}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.MobileControl/MobileControlSystemController.cs b/src/PepperDash.Essentials.MobileControl/MobileControlSystemController.cs
index c17cf3b9..3adcaf87 100644
--- a/src/PepperDash.Essentials.MobileControl/MobileControlSystemController.cs
+++ b/src/PepperDash.Essentials.MobileControl/MobileControlSystemController.cs
@@ -69,32 +69,11 @@ namespace PepperDash.Essentials
private readonly Dictionary _defaultMessengers =
new Dictionary();
- private readonly Dictionary _connectedClientVersions =
- new Dictionary(StringComparer.InvariantCultureIgnoreCase);
-
- private readonly object _connectedClientVersionsLock = new object();
-
///
/// Get the custom messengers with subscriptions
///
public ReadOnlyDictionary Messengers => new ReadOnlyDictionary(_messengers.Values.OfType().ToDictionary(k => k.Key, v => v));
- ///
- /// Gets the most recently reported UI app version for each connected client, keyed by clientId
- ///
- public ReadOnlyDictionary ConnectedClientVersions
- {
- get
- {
- lock (_connectedClientVersionsLock)
- {
- return new ReadOnlyDictionary(
- _connectedClientVersions.ToDictionary(kv => kv.Key, kv => kv.Value.Clone())
- );
- }
- }
- }
-
///
/// Get the default messengers
///
@@ -158,7 +137,7 @@ namespace PepperDash.Essentials
"No system_url value defined in config. Checking for value from SIMPL Bridge."
);
- if (string.IsNullOrEmpty(SystemUrl))
+ if (!string.IsNullOrEmpty(SystemUrl))
{
this.LogError(
"No system_url value defined in config or SIMPL Bridge. Unable to connect to Mobile Control."
@@ -1803,28 +1782,6 @@ namespace PepperDash.Essentials
" Not Enabled in Config.\r\n"
);
}
-
- var connectedClientVersions = ConnectedClientVersions;
-
- if (connectedClientVersions.Count == 0)
- {
- CrestronConsole.ConsoleCommandResponse("\r\nUI Client App Versions: None reported yet\r\n");
- }
- else
- {
- CrestronConsole.ConsoleCommandResponse("\r\nUI Client App Versions:\r\n");
- foreach (var kv in connectedClientVersions)
- {
- var v = kv.Value;
- var match = string.IsNullOrEmpty(v.ExpectedAppVersion) || string.Equals(v.ExpectedAppVersion, v.AppVersion, StringComparison.OrdinalIgnoreCase);
-
- CrestronConsole.ConsoleCommandResponse(
- $" Client: {v.ClientId} Touchpanel: {v.TouchpanelKey} Room: {v.RoomKey}\r\n" +
- $" Reported: {v.AppVersion} Expected: {(string.IsNullOrEmpty(v.ExpectedAppVersion) ? "(not configured)" : v.ExpectedAppVersion)} Match: {(match ? "Yes" : "NO - MISMATCH")}\r\n" +
- $" Last Seen (UTC): {v.LastSeen:yyyy-MM-dd HH:mm:ss}\r\n"
- );
- }
- }
}
///
@@ -2224,8 +2181,6 @@ namespace PepperDash.Essentials
var roomKey = content["roomKey"].Value();
var touchpanelKey = content.SelectToken("touchpanelKey");
- TrackClientAppVersion(clientId, roomKey, touchpanelKey?.Value(), content.SelectToken("appVersion")?.Value());
-
if (_roomCombiner == null)
{
var message = new MobileControlMessage
@@ -2297,50 +2252,6 @@ namespace PepperDash.Essentials
SendTouchpanelKey(clientId, touchpanelKey);
}
- ///
- /// Records the app version reported by a connecting UI client (e.g. the mobile control React app's
- /// build-time APP_VERSION) and compares it against the configured versions.touchpanelWrapperApp version.
- ///
- private void TrackClientAppVersion(string clientId, string roomKey, string touchpanelKey, string appVersion)
- {
- if (string.IsNullOrEmpty(appVersion))
- {
- return;
- }
-
- var expectedVersion = ConfigReader.ConfigObject?.Versions?.TouchpanelWrapperApp?.Version;
-
- var info = new ConnectedClientVersionInfo
- {
- ClientId = clientId,
- RoomKey = roomKey,
- TouchpanelKey = touchpanelKey,
- AppVersion = appVersion,
- ExpectedAppVersion = expectedVersion,
- LastSeen = DateTime.UtcNow
- };
-
- lock (_connectedClientVersionsLock)
- {
- _connectedClientVersions[clientId] = info;
- }
-
- if (!string.IsNullOrEmpty(expectedVersion) && !string.Equals(expectedVersion, appVersion, StringComparison.OrdinalIgnoreCase))
- {
- this.LogWarning(
- "Client {clientId} (touchpanel {touchpanelKey}) reported UI app version {appVersion}, which does not match configured versions.touchpanelWrapperApp version {expectedVersion}",
- clientId, touchpanelKey, appVersion, expectedVersion
- );
- }
- else
- {
- this.LogVerbose(
- "Client {clientId} (touchpanel {touchpanelKey}) reported UI app version {appVersion}",
- clientId, touchpanelKey, appVersion
- );
- }
- }
-
private void SendTouchpanelKey(string clientId, JToken touchpanelKeyToken)
{
if (touchpanelKeyToken == null)
diff --git a/src/PepperDash.Essentials.MobileControl/PepperDash.Essentials.MobileControl.csproj b/src/PepperDash.Essentials.MobileControl/PepperDash.Essentials.MobileControl.csproj
index b0e2dd9f..235e0899 100644
--- a/src/PepperDash.Essentials.MobileControl/PepperDash.Essentials.MobileControl.csproj
+++ b/src/PepperDash.Essentials.MobileControl/PepperDash.Essentials.MobileControl.csproj
@@ -38,7 +38,7 @@
-
+
diff --git a/src/PepperDash.Essentials.MobileControl/Touchpanel/MobileControlTouchpanelController.cs b/src/PepperDash.Essentials.MobileControl/Touchpanel/MobileControlTouchpanelController.cs
index 262cb34e..5830782d 100644
--- a/src/PepperDash.Essentials.MobileControl/Touchpanel/MobileControlTouchpanelController.cs
+++ b/src/PepperDash.Essentials.MobileControl/Touchpanel/MobileControlTouchpanelController.cs
@@ -25,28 +25,11 @@ namespace PepperDash.Essentials.Touchpanel
/// Mobile Control touchpanel controller that provides app control, Zoom integration,
/// and mobile control functionality for Crestron touchpanels.
///
- public class MobileControlTouchpanelController : TouchpanelBase, IHasFeedback, ITswAppControl, ITswZoomControl, IDeviceInfoProvider, IMobileControlCrestronTouchpanelController, ITheme, ICommunicationMonitor
+ public class MobileControlTouchpanelController : TouchpanelBase, IHasFeedback, ITswAppControl, ITswZoomControl, IDeviceInfoProvider, IMobileControlCrestronTouchpanelController, ITheme
{
private readonly MobileControlTouchpanelProperties localConfig;
private IMobileControlRoomMessenger _bridge;
- ///
- /// Gets the CommunicationMonitor tracking the panel's online/offline state
- ///
- public StatusMonitorBase CommunicationMonitor { get; private set; }
-
- private sealed class NullCommunicationMonitor : StatusMonitorBase
- {
- public NullCommunicationMonitor(IKeyed parent) : base(parent, 120000, 300000)
- {
- Status = MonitorStatus.InError;
- Message = "Panel is not initialized";
- }
-
- public override void Start() { }
- public override void Stop() { }
- }
-
private string _appUrl;
///
@@ -145,11 +128,6 @@ namespace PepperDash.Essentials.Touchpanel
{
localConfig = config;
- if (panel != null)
- {
- CommunicationMonitor = new CrestronGenericBaseCommunicationMonitor(this, panel, 120000, 300000);
- }
-
AddPostActivationAction(SubscribeForMobileControlUpdates);
ThemeFeedback = new StringFeedback($"{Key}-theme", () => Theme);
@@ -388,8 +366,6 @@ namespace PepperDash.Essentials.Touchpanel
///
public override bool CustomActivate()
{
- CommunicationMonitor?.Start();
-
var appMessenger = new ITswAppControlMessenger($"appControlMessenger-{Key}", $"/device/{Key}", this);
var zoomMessenger = new ITswZoomControlMessenger($"zoomControlMessenger-{Key}", $"/device/{Key}", this);
@@ -417,17 +393,6 @@ namespace PepperDash.Essentials.Touchpanel
return base.CustomActivate();
}
- ///
- /// Stops the CommunicationMonitor on deactivation.
- ///
- /// True if deactivation was successful; otherwise, false.
- public override bool Deactivate()
- {
- CommunicationMonitor?.Stop();
-
- return base.Deactivate();
- }
-
///
/// Handles device extender signal changes for system reserved signals.
///
@@ -554,8 +519,15 @@ namespace PepperDash.Essentials.Touchpanel
return false;
}) ? csIpAddress.ToString() : processorIp;
- // replace the host but preserve whatever scheme (http/https) is already present in the URL
- var updatedUrl = Regex.Replace(url, @"^(https?)://[^:/]+", $"$1://{ip}");
+ 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);
@@ -768,7 +740,7 @@ namespace PepperDash.Essentials.Touchpanel
///
public MobileControlTouchpanelControllerFactory()
{
- TypeNames = new List() { "mccrestronapp", "mctsw550", "mctsw750", "mctsw1050", "mctsw560", "mctsw760", "mctsw1060", "mctsw570", "mctsw770", "mcts770", "mctsw1070", "mcts1070", "mctsw1080", "mcts1080", "mcxpanel", "mcdge1000" };
+ TypeNames = new List() { "mccrestronapp", "mctsw550", "mctsw750", "mctsw1050", "mctsw560", "mctsw760", "mctsw1060", "mctsw570", "mctsw770", "mcts770", "mctsw1070", "mcts1070", "mcxpanel", "mcdge1000" };
MinimumEssentialsFrameworkVersion = "2.0.0";
factories = new Dictionary>
@@ -793,8 +765,6 @@ namespace PepperDash.Essentials.Touchpanel
{"ts770", (id, controlSystem, projectName) => new Ts770(id, controlSystem)},
{"tsw1070", (id, controlSystem, projectName) => new Tsw1070(id, controlSystem)},
{"ts1070", (id, controlSystem, projectName) => new Ts1070(id, controlSystem)},
- {"tsw1080", (id, controlSystem, projectName) => new Tsw1080(id, controlSystem)},
- {"ts1080", (id, controlSystem, projectName) => new Ts1080(id, controlSystem)},
{"dge1000", (id, controlSystem, projectName) => new Dge1000(id, controlSystem)}
};
}
diff --git a/src/PepperDash.Essentials.MobileControl/WebSocketServer/MobileControlWebsocketServer.cs b/src/PepperDash.Essentials.MobileControl/WebSocketServer/MobileControlWebsocketServer.cs
index c96dc116..1c9ed37a 100644
--- a/src/PepperDash.Essentials.MobileControl/WebSocketServer/MobileControlWebsocketServer.cs
+++ b/src/PepperDash.Essentials.MobileControl/WebSocketServer/MobileControlWebsocketServer.cs
@@ -127,16 +127,6 @@ namespace PepperDash.Essentials.WebSocketServer
///
public int Port { get; private set; }
- ///
- /// Gets the HTTP scheme to use for generated URLs, based on whether the direct server is configured as secure
- ///
- private string HttpScheme => _parent.Config.DirectServer.Secure ? "https" : "http";
-
- ///
- /// Gets the WebSocket scheme to use for generated URLs, based on whether the direct server is configured as secure
- ///
- private string WsScheme => _parent.Config.DirectServer.Secure ? "wss" : "ws";
-
///
/// Gets the user app URL prefix
///
@@ -144,8 +134,7 @@ namespace PepperDash.Essentials.WebSocketServer
{
get
{
- return string.Format("{0}://{1}:{2}{3}?token=",
- HttpScheme,
+ return string.Format("http://{0}:{1}{2}?token=",
CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, 0),
Port,
_userAppBaseHref);
@@ -284,7 +273,7 @@ namespace PepperDash.Essentials.WebSocketServer
{
base.Initialize();
- _server = new HttpServer(Port, _parent.Config.DirectServer.Secure);
+ _server = new HttpServer(Port, false);
_server.OnGet += Server_OnGet;
@@ -302,7 +291,7 @@ namespace PepperDash.Essentials.WebSocketServer
{
ClientCertificateRequired = false,
CheckCertificateRevocation = false,
- EnabledSslProtocols = SslProtocols.Tls12
+ EnabledSslProtocols = SslProtocols.Tls12 | SslProtocols.Tls11
};
}
@@ -414,11 +403,11 @@ namespace PepperDash.Essentials.WebSocketServer
ip = csIpAddress.ToString();
}
- var appUrl = $"{HttpScheme}://{ip}:{Port}/mc/app?token={touchpanel.Key}";
+ var appUrl = $"http://{ip}:{_parent.Config.DirectServer.Port}/mc/app?token={touchpanel.Key}";
this.LogVerbose("Sending URL {appUrl} to touchpanel {touchpanelKey}", appUrl, touchpanel.Touchpanel.Key);
- touchpanel.Touchpanel.SetAppUrl(appUrl);
+ touchpanel.Touchpanel.SetAppUrl($"http://{ip}:{_parent.Config.DirectServer.Port}/mc/app?token={touchpanel.Key}");
}
}
@@ -498,7 +487,7 @@ namespace PepperDash.Essentials.WebSocketServer
{
var config = new MobileControlApplicationConfig
{
- ApiPath = string.Format("{0}://{1}:{2}/mc/api", HttpScheme, processorIp, Port),
+ ApiPath = string.Format("http://{0}:{1}/mc/api", processorIp, _parent.Config.DirectServer.Port),
GatewayAppPath = "",
LogoPath = _parent.Config.ApplicationConfig?.LogoPath ?? "logo/logo.png",
EnableDev = _parent.Config.ApplicationConfig?.EnableDev ?? false,
@@ -1109,7 +1098,6 @@ namespace PepperDash.Essentials.WebSocketServer
res.StatusCode = 200;
res.Close();
- // remote log collector has no dedicated secure flag; keep it on http regardless of DirectServer.Secure
var logRequest = new HttpRequestMessage(HttpMethod.Post, $"http://{_parent.Config.DirectServer.Logging.Host}:{_parent.Config.DirectServer.Logging.Port}/logs")
{
Content = new StringContent(body, Encoding.UTF8, "application/json"),
@@ -1162,11 +1150,6 @@ namespace PepperDash.Essentials.WebSocketServer
var qp = req.QueryString;
var token = qp["token"];
- // Each join mints a single-use clientId; the panel webview must never replay a cached
- // response, or it reconnects forever with an already-consumed id (1008 loop).
- res.Headers.Add("Cache-Control", "no-store");
- res.Headers.Add("Pragma", "no-cache");
-
this.LogVerbose("Join Room Request with token: {token}", token);
byte[] body;
@@ -1230,7 +1213,8 @@ namespace PepperDash.Essentials.WebSocketServer
this.LogVerbose("Assigning ClientId: {clientId} for token: {token} at {timestamp}", clientId, token, now);
// Construct WebSocket URL with clientId query parameter
- var wsUrl = $"{WsScheme}://{CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, 0)}:{Port}{_wsPath}{token}?clientId={clientId}";
+ var wsProtocol = "ws";
+ var wsUrl = $"{wsProtocol}://{CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, 0)}:{Port}{_wsPath}{token}?clientId={clientId}";
// Construct the response object
JoinResponse jRes = new JoinResponse
@@ -1242,8 +1226,7 @@ namespace PepperDash.Essentials.WebSocketServer
Config = _parent.GetConfigWithPluginVersion(),
CodeExpires = new DateTime().AddYears(1),
UserCode = bridge.UserCode,
- UserAppUrl = string.Format("{0}://{1}:{2}/mc/app",
- HttpScheme,
+ UserAppUrl = string.Format("http://{0}:{1}/mc/app",
CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, 0),
Port),
WebSocketUrl = wsUrl,
@@ -1268,8 +1251,6 @@ namespace PepperDash.Essentials.WebSocketServer
{
res.StatusCode = 200;
res.ContentType = "application/json";
- res.Headers.Add("Cache-Control", "no-store");
- res.Headers.Add("Pragma", "no-cache");
var version = new Version() { ServerVersion = _parent.GetConfigWithPluginVersion().RuntimeInfo.PluginVersion };
var message = JsonConvert.SerializeObject(version);
this.LogVerbose("{message}", message);
diff --git a/src/PepperDash.Essentials/ControlSystem.cs b/src/PepperDash.Essentials/ControlSystem.cs
index 47de4d3e..e6c61c60 100644
--- a/src/PepperDash.Essentials/ControlSystem.cs
+++ b/src/PepperDash.Essentials/ControlSystem.cs
@@ -27,7 +27,7 @@ namespace PepperDash.Essentials
private CEvent _initializeEvent;
private const long StartupTime = 500;
- // private const string minimumFirmwareVersion = "2.8006.00110";
+ private const string minimumFirmwareVersion = "2.8006.00110";
///
/// Initializes a new instance of the ControlSystem class
@@ -50,21 +50,21 @@ namespace PepperDash.Essentials
{
// Get FW version and stop if it's too low to run this version of Essentials. Must be greater than v2.8006.00110
- // var fwVersion = InitialParametersClass.FirmwareVersion;
+ var fwVersion = InitialParametersClass.FirmwareVersion;
- // Debug.LogInformation("Control System Hardware Version: {fwVersion}", fwVersion);
+ Debug.LogInformation("Control System Hardware Version: {fwVersion}", fwVersion);
- // // split the version into parts and compare against minimumFirmwareVersion
- // var versionParts = fwVersion.Split('.').Select(int.Parse).ToArray();
- // var minParts = minimumFirmwareVersion.Split('.').Select(int.Parse).ToArray();
- // if (versionParts.Length < minParts.Length
- // || versionParts[0] < minParts[0]
- // || (versionParts[0] == minParts[0] && versionParts[1] < minParts[1])
- // || (versionParts[0] == minParts[0] && versionParts[1] == minParts[1] && versionParts[2] <= minParts[2]))
- // {
- // Debug.LogFatal("Firmware version {fwVersion} is too low to run this version of Essentials. Please upgrade to greater than v{minimumFirmwareVersion}.", fwVersion, minimumFirmwareVersion);
- // return;
- // }
+ // split the version into parts and compare against minimumFirmwareVersion
+ var versionParts = fwVersion.Split('.').Select(int.Parse).ToArray();
+ var minParts = minimumFirmwareVersion.Split('.').Select(int.Parse).ToArray();
+ if (versionParts.Length < minParts.Length
+ || versionParts[0] < minParts[0]
+ || (versionParts[0] == minParts[0] && versionParts[1] < minParts[1])
+ || (versionParts[0] == minParts[0] && versionParts[1] == minParts[1] && versionParts[2] <= minParts[2]))
+ {
+ Debug.LogFatal("Firmware version {fwVersion} is too low to run this version of Essentials. Please upgrade to greater than v{minimumFirmwareVersion}.", fwVersion, minimumFirmwareVersion);
+ return;
+ }
// If the control system is a DMPS type, we need to wait to exit this method until all devices have had time to activate
// to allow any HD-BaseT DM endpoints to register first.
@@ -130,8 +130,14 @@ namespace PepperDash.Essentials
(ConfigReader.ConfigObject, Newtonsoft.Json.Formatting.Indented).Replace(Environment.NewLine, "\r\n"));
}, "showconfig", "Shows the current running merged config", ConsoleAccessLevelEnum.AccessOperator);
- CrestronConsole.AddNewConsoleCommand(
- PrintPortalInfo,
+ CrestronConsole.AddNewConsoleCommand(s =>
+ CrestronConsole.ConsoleCommandResponse(
+ "This system can be found at the following URLs:{2}" +
+ "System URL: {0}{2}" +
+ "Template URL: {1}{2}",
+ ConfigReader.ConfigObject.SystemUrl,
+ ConfigReader.ConfigObject.TemplateUrl,
+ CrestronEnvironment.NewLine),
"portalinfo",
"Shows portal URLS from configuration",
ConsoleAccessLevelEnum.AccessOperator);
@@ -154,29 +160,6 @@ namespace PepperDash.Essentials
}
}
- private void PrintPortalInfo(string args)
- {
- if(ConfigReader.ConfigObject == null)
- {
- CrestronConsole.ConsoleCommandResponse("No configuration loaded. Cannot show portal URLs.");
- return;
- }
-
- if (string.IsNullOrEmpty(ConfigReader.ConfigObject.SystemUrl) && string.IsNullOrEmpty(ConfigReader.ConfigObject.TemplateUrl))
- {
- CrestronConsole.ConsoleCommandResponse("No portal URLs defined in config.");
- return;
- }
-
- CrestronConsole.ConsoleCommandResponse(
- "This system can be found at the following URLs:{2}" +
- "System URL: {0}{2}" +
- "Template URL: {1}{2}",
- ConfigReader.ConfigObject?.SystemUrl,
- ConfigReader.ConfigObject?.TemplateUrl,
- CrestronEnvironment.NewLine);
- }
-
///
/// DeterminePlatform method
///
@@ -274,6 +257,11 @@ namespace PepperDash.Essentials
PluginLoader.AddProgramAssemblies();
_ = new Core.DeviceFactory();
+ // _ = new Devices.Common.DeviceFactory();
+ // _ = new DeviceFactory();
+
+ // _ = new ProcessorExtensionDeviceFactory();
+ // _ = new MobileControlFactory();
LoadAssets(Global.ApplicationDirectoryPathPrefix, Global.FilePathPrefix);
@@ -286,9 +274,10 @@ namespace PepperDash.Essentials
PluginLoader.LoadPlugins();
Debug.LogMessage(LogEventLevel.Information, "Folder structure verified. Loading config...");
- if (!ConfigReader.LoadConfig2() || ConfigReader.ConfigObject == null)
+ if (!ConfigReader.LoadConfig2())
{
- Debug.LogMessage(LogEventLevel.Warning, "Unable to load config file.");
+ Debug.LogMessage(LogEventLevel.Information, "Essentials Load complete with errors");
+ return;
}
Load();
@@ -410,12 +399,6 @@ namespace PepperDash.Essentials
new Core.Monitoring.SystemMonitorController("systemMonitor"));
}
- if (ConfigReader.ConfigObject is null)
- {
- Debug.LogMessage(LogEventLevel.Warning, "LoadDevices: ConfigObject is null. Cannot load devices.");
- return;
- }
-
foreach (var devConf in ConfigReader.ConfigObject.Devices)
{
IKeyed newDev = null;
@@ -469,7 +452,7 @@ namespace PepperDash.Essentials
var tlc = TieLineCollection.Default;
- if (ConfigReader.ConfigObject?.TieLines == null)
+ if (ConfigReader.ConfigObject.TieLines == null)
{
return;
}
@@ -766,7 +749,7 @@ namespace PepperDash.Essentials
///
public void LoadRooms()
{
- if (ConfigReader.ConfigObject?.Rooms == null)
+ if (ConfigReader.ConfigObject.Rooms == null)
{
Debug.LogMessage(LogEventLevel.Information, "Notice: Configuration contains no rooms - Is this intentional? This may be a valid configuration.");
return;
@@ -801,16 +784,15 @@ namespace PepperDash.Essentials
///
/// Fires up a logo server if not already running
///
- [Obsolete("Logo server is no longer supported and will be removed in a future release.")]
void LoadLogoServer()
{
- if (ConfigReader.ConfigObject?.Rooms == null)
+ if (ConfigReader.ConfigObject.Rooms == null)
{
Debug.LogMessage(LogEventLevel.Information, "No rooms configured. Bypassing Logo server startup.");
return;
}
- if (ConfigReader.ConfigObject?.Rooms == null ||
+ if (
!ConfigReader.ConfigObject.Rooms.Any(
CheckRoomConfig))
{
diff --git a/src/PepperDash.Essentials/PepperDash.Essentials.csproj b/src/PepperDash.Essentials/PepperDash.Essentials.csproj
index cb83bf84..20a42ffd 100644
--- a/src/PepperDash.Essentials/PepperDash.Essentials.csproj
+++ b/src/PepperDash.Essentials/PepperDash.Essentials.csproj
@@ -48,7 +48,7 @@
-
+