diff --git a/src/PepperDash.Essentials.Core/Devices/DeviceJsonApi.cs b/src/PepperDash.Essentials.Core/Devices/DeviceJsonApi.cs
index c4ec2ca99..c01d5e815 100644
--- a/src/PepperDash.Essentials.Core/Devices/DeviceJsonApi.cs
+++ b/src/PepperDash.Essentials.Core/Devices/DeviceJsonApi.cs
@@ -7,6 +7,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
+using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace PepperDash.Essentials.Core
@@ -176,7 +177,20 @@ private static object ConvertType(object value, Type conversionType)
{
if (!conversionType.IsEnum)
{
- return Convert.ChangeType(value, conversionType, System.Globalization.CultureInfo.InvariantCulture);
+ 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;
}
var stringValue = Convert.ToString(value);
@@ -189,6 +203,32 @@ private static object ConvertType(object value, Type conversionType)
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/Web/RequestHandlers/DebugSessionRequestHandler.cs b/src/PepperDash.Essentials.Core/Web/RequestHandlers/DebugSessionRequestHandler.cs
index 01e388344..1eeb27809 100644
--- a/src/PepperDash.Essentials.Core/Web/RequestHandlers/DebugSessionRequestHandler.cs
+++ b/src/PepperDash.Essentials.Core/Web/RequestHandlers/DebugSessionRequestHandler.cs
@@ -57,6 +57,36 @@ protected override void HandleGet(Crestron.SimplSharp.WebScripting.HttpCwsContex
// Start the WS Server
Debug.WebsocketSink.StartServerAndSetPort(port);
Debug.SetWebSocketMinimumDebugLevel(Serilog.Events.LogEventLevel.Verbose);
+
+ // Attempt to forward the port to the CS LAN
+ try
+ {
+ var csAdapterId = CrestronEthernetHelper.GetAdapterdIdForSpecifiedAdapterType(
+ EthernetAdapterType.EthernetCSAdapter);
+ var csIp = CrestronEthernetHelper.GetEthernetParameter(
+ CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, csAdapterId);
+
+ 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);
+ }
+ }
+ 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;
@@ -90,8 +120,47 @@ protected override void HandleGet(Crestron.SimplSharp.WebScripting.HttpCwsContex
///
protected override void HandlePost(HttpCwsContext context)
{
+ 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();