Compare commits

..

5 Commits

Author SHA1 Message Date
Neil Dorin
258699fbcd fix: Enhance AppUrl change logging
Updated logging to include the new AppUrl value when it changes. This provides better context in the logs, making it easier to track the specific URL that was set.
2025-09-18 18:23:12 -06:00
Neil Dorin
738504e9fc fix: Add error handling for network parameter retrieval
Introduced a try-catch block to handle exceptions when fetching the Crestron Ethernet adapter's ID, subnet mask, and IP address. Added logging for cases where the processor lacks a CS LAN. Also included a new using directive for Serilog.Events.
2025-09-18 14:48:21 -06:00
erikdred
cae1bbd6e6 Merge pull request #1332 from PepperDash/feature/add-hide-property-to-room-combine-scenario
feat: Add HideInUi property to room combiner classes
2025-09-17 18:38:21 -04:00
Neil Dorin
dea4407e3e Update src/PepperDash.Essentials.Core/Room/Combining/IEssentialsRoomCombiner.cs
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-09-17 16:36:33 -06:00
Neil Dorin
ab08a546f7 feat: Add HideInUi property to room combiner classes
- Introduced `HideInUi` property in `EssentialsRoomCombinerPropertiesConfig` to control UI visibility.
- Added `HideInUi` interface property in `IEssentialsRoomCombiner`.
- Implemented `HideInUi` in `RoomCombinationScenario`, along with new `Key` and `Name` properties for improved data representation.
2025-09-17 15:25:23 -06:00
10 changed files with 376 additions and 448 deletions

View File

@@ -466,14 +466,14 @@ namespace PepperDash.Core
/// </summary>
public static void SetFileMinimumDebugLevel(LogEventLevel level)
{
_fileLevelSwitch.MinimumLevel = level;
_errorLogLevelSwitch.MinimumLevel = level;
var err = CrestronDataStoreStatic.SetLocalUintValue(FileLevelStoreKey, (uint)level);
var err = CrestronDataStoreStatic.SetLocalUintValue(ErrorLogLevelStoreKey, (uint)level);
if (err != CrestronDataStore.CDS_ERROR.CDS_SUCCESS)
LogMessage(LogEventLevel.Information, "Error saving File debug level setting: {error}", err);
LogMessage(LogEventLevel.Information, "File debug level set to {0}", _fileLevelSwitch.MinimumLevel);
LogMessage(LogEventLevel.Information, "File debug level set to {0}", _websocketLoggingLevelSwitch.MinimumLevel);
}
/// <summary>

View File

@@ -103,6 +103,12 @@ namespace PepperDash.Essentials.Core
[JsonProperty("name")]
public string Name { get; set; }
/// <summary>
/// Gets or sets a value indicating whether to hide this scenario in the UI.
/// </summary>
[JsonProperty("hideInUi", NullValueHandling = NullValueHandling.Ignore)]
public bool HideInUi { get; set; }
/// <summary>
/// Gets or sets the collection of partition states.
/// </summary>

View File

@@ -109,6 +109,12 @@ namespace PepperDash.Essentials.Core
[JsonProperty("isActive")]
bool IsActive { get; }
/// <summary>
/// Gets a value indicating whether this scenario should be hidden in the UI.
/// </summary>
[JsonProperty("hideInUi")]
bool HideInUi { get; }
/// <summary>
/// Activates this room combination scenario
/// </summary>

View File

@@ -14,18 +14,40 @@ namespace PepperDash.Essentials.Core
{
private RoomCombinationScenarioConfig _config;
/// <summary>
/// Gets or sets the key associated with the object.
/// </summary>
[JsonProperty("key")]
public string Key { get; set; }
/// <summary>
/// Gets or sets the name associated with the object.
/// </summary>
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("partitionStates")]
/// <summary>
/// Gets a value indicating whether to hide this scenario in the UI.
/// </summary>
///
[JsonProperty("hideInUi")]
public bool HideInUi
{
get { return _config.HideInUi; }
}
/// <summary>
/// Gets or sets the PartitionStates
/// </summary>
///
[JsonProperty("partitionStates")]
public List<PartitionState> PartitionStates { get; private set; }
/// <summary>
/// Determines which UI devices get mapped to which room in this scenario. The Key should be the key of the UI device and the Value should be the key of the room to map to
/// </summary>
[JsonProperty("uiMap")]
public Dictionary<string, string> UiMap { get; set; }

View File

@@ -244,15 +244,13 @@ namespace PepperDash.Essentials
CrestronEnvironment.ProgramStatusEventHandler +=
CrestronEnvironment_ProgramStatusEventHandler;
ApiOnlineAndAuthorized = new BoolFeedback("apiOnlineAndAuthorized", () =>
ApiOnlineAndAuthorized = new BoolFeedback(() =>
{
if (_wsClient2 == null)
return false;
return _wsClient2.IsAlive && IsAuthorized;
});
Debug.SetFileMinimumDebugLevel(Serilog.Events.LogEventLevel.Debug);
}
private void SetupDefaultRoomMessengers()
@@ -1919,8 +1917,7 @@ namespace PepperDash.Essentials
/// <param name="e"></param>
private void HandleError(object sender, ErrorEventArgs e)
{
this.LogError("Websocket error {message}", e.Message);
this.LogError(e.Exception, "Websocket error");
this.LogError("Websocket error {0}", e.Message);
IsAuthorized = false;
StartServerReconnectTimer();
@@ -1933,7 +1930,7 @@ namespace PepperDash.Essentials
/// <param name="e"></param>
private void HandleClose(object sender, CloseEventArgs e)
{
this.LogInformation(
this.LogDebug(
"Websocket close {code} {reason}, clean={wasClean}",
e.Code,
e.Reason,

View File

@@ -1,76 +0,0 @@
using Crestron.SimplSharpPro.UI;
using PepperDash.Core;
using PepperDash.Core.Logging;
namespace PepperDash.Essentials.Touchpanel
{
/// <summary>
/// Partial class containing app control functionality for managing applications on the touchpanel.
/// </summary>
public partial class MobileControlTouchpanelController
{
/// <summary>
/// Sets the application URL for mobile control access.
/// </summary>
/// <param name="url">The URL to set for the mobile control application.</param>
public void SetAppUrl(string url)
{
_appUrl = GetUrlWithCorrectIp(url);
AppUrlFeedback.FireUpdate();
}
/// <summary>
/// Hides the currently open application on the touchpanel.
/// </summary>
public void HideOpenApp()
{
if (Panel is TswX70Base x70Panel)
{
x70Panel.ExtenderApplicationControlReservedSigs.HideOpenedApplication();
return;
}
if (Panel is TswX60BaseClass x60Panel)
{
x60Panel.ExtenderApplicationControlReservedSigs.HideOpenApplication();
return;
}
}
/// <summary>
/// Opens an application on the touchpanel.
/// </summary>
public void OpenApp()
{
if (Panel is TswX70Base x70Panel)
{
x70Panel.ExtenderApplicationControlReservedSigs.OpenApplication();
return;
}
if (Panel is TswX60WithZoomRoomAppReservedSigs)
{
Debug.LogMessage(Serilog.Events.LogEventLevel.Information, this, $"X60 panel does not support zoom app");
return;
}
}
/// <summary>
/// Closes the currently open application on the touchpanel.
/// </summary>
public void CloseOpenApp()
{
if (Panel is TswX70Base x70Panel)
{
x70Panel.ExtenderApplicationControlReservedSigs.CloseOpenedApplication();
return;
}
if (Panel is TswX60WithZoomRoomAppReservedSigs x60Panel)
{
x60Panel.ExtenderApplicationControlReservedSigs.CloseOpenedApplication();
return;
}
}
}
}

View File

@@ -1,142 +0,0 @@
using System;
using System.Linq;
using System.Text.RegularExpressions;
using Crestron.SimplSharp;
using Crestron.SimplSharpPro;
using Crestron.SimplSharpPro.UI;
using PepperDash.Core;
using PepperDash.Core.Logging;
using PepperDash.Essentials.Core;
using PepperDash.Essentials.Core.DeviceInfo;
namespace PepperDash.Essentials.Touchpanel
{
/// <summary>
/// Partial class containing device information, IP address handling, and network configuration functionality.
/// </summary>
public partial class MobileControlTouchpanelController
{
/// <summary>
/// Updates device information including MAC address and IP address from panel extenders.
/// </summary>
public void UpdateDeviceInfo()
{
if (Panel is TswXX70Base x70Panel)
{
DeviceInfo.MacAddress = x70Panel.ExtenderEthernetReservedSigs.MacAddressFeedback.StringValue;
DeviceInfo.IpAddress = x70Panel.ExtenderEthernetReservedSigs.IpAddressFeedback.StringValue;
var handler = DeviceInfoChanged;
if (handler == null)
{
return;
}
handler(this, new DeviceInfoEventArgs(DeviceInfo));
}
if (Panel is TswX60WithZoomRoomAppReservedSigs x60Panel)
{
DeviceInfo.MacAddress = x60Panel.ExtenderEthernetReservedSigs.MacAddressFeedback.StringValue;
DeviceInfo.IpAddress = x60Panel.ExtenderEthernetReservedSigs.IpAddressFeedback.StringValue;
var handler = DeviceInfoChanged;
if (handler == null)
{
return;
}
handler(this, new DeviceInfoEventArgs(DeviceInfo));
}
Debug.LogMessage(Serilog.Events.LogEventLevel.Debug, this, $"MAC: {DeviceInfo.MacAddress} IP: {DeviceInfo.IpAddress}");
}
/// <summary>
/// Gets the URL with the correct IP address based on the connected devices and the Crestron processor's IP address.
/// </summary>
/// <param name="url">The original URL to process.</param>
/// <returns>The URL with the correct IP address for the panel's network connection.</returns>
private string GetUrlWithCorrectIp(string url)
{
var lanAdapterId = CrestronEthernetHelper.GetAdapterdIdForSpecifiedAdapterType(EthernetAdapterType.EthernetLANAdapter);
var processorIp = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, lanAdapterId);
if (csIpAddress == null || csSubnetMask == null || url == null)
{
this.LogWarning("CS IP Address Subnet Mask or url is null, cannot determine correct IP for URL");
return url;
}
this.LogVerbose("Processor IP: {processorIp}, CS IP: {csIpAddress}, CS Subnet Mask: {csSubnetMask}", processorIp, csIpAddress, csSubnetMask);
this.LogVerbose("Connected IP Count: {connectedIps}", ConnectedIps.Count);
var ip = ConnectedIps.Any(ipInfo =>
{
if (System.Net.IPAddress.TryParse(ipInfo.DeviceIpAddress, out var parsedIp))
{
return parsedIp.IsInSameSubnet(csIpAddress, csSubnetMask);
}
this.LogWarning("Invalid IP address: {deviceIpAddress}", ipInfo.DeviceIpAddress);
return false;
}) ? csIpAddress.ToString() : processorIp;
var match = Regex.Match(url, @"^http://([^:/]+):\d+/mc/app\?token=.+$");
if (match.Success)
{
string ipa = match.Groups[1].Value;
// ip will be "192.168.1.100"
}
// replace ipa with ip but leave the rest of the string intact
var updatedUrl = Regex.Replace(url, @"^http://[^:/]+", $"http://{ip}");
this.LogVerbose("Updated URL: {updatedUrl}", updatedUrl);
return updatedUrl;
}
/// <summary>
/// Subscribes to mobile control updates and bridge events for feedback updates.
/// </summary>
private void SubscribeForMobileControlUpdates()
{
foreach (var dev in DeviceManager.AllDevices)
{
Debug.LogMessage(Serilog.Events.LogEventLevel.Information, this, $"{dev.Key}:{dev.GetType().Name}");
}
var mcList = DeviceManager.AllDevices.OfType<MobileControlSystemController>().ToList();
if (mcList.Count == 0)
{
Debug.LogMessage(Serilog.Events.LogEventLevel.Information, this, $"No Mobile Control controller found");
return;
}
// use first in list, since there should only be one.
var mc = mcList[0];
var bridge = mc.GetRoomBridge(_config.DefaultRoomKey);
if (bridge == null)
{
Debug.LogMessage(Serilog.Events.LogEventLevel.Information, this, $"No Mobile Control bridge for {_config.DefaultRoomKey} found ");
return;
}
_bridge = bridge;
_bridge.UserCodeChanged += UpdateFeedbacks;
_bridge.AppUrlChanged += (s, a) =>
{
SetAppUrl(_bridge.AppUrl);
};
UpdateFeedbacks();
}
}
}

View File

@@ -1,160 +0,0 @@
using System;
using Crestron.SimplSharpPro.UI;
using PepperDash.Core;
using PepperDash.Core.Logging;
using PepperDash.Essentials.Core.DeviceInfo;
namespace PepperDash.Essentials.Touchpanel
{
/// <summary>
/// Partial class containing panel extender registration and event handling functionality.
/// </summary>
public partial class MobileControlTouchpanelController
{
/// <summary>
/// Registers for extender signals based on the panel type and sets up event handlers
/// for app control, Zoom room, and Ethernet functionality.
/// </summary>
private void RegisterForExtenders()
{
if (Panel is TswXX70Base x70Panel)
{
RegisterX70PanelExtenders(x70Panel);
return;
}
if (Panel is TswX60WithZoomRoomAppReservedSigs x60withZoomApp)
{
RegisterX60PanelExtenders(x60withZoomApp);
}
}
/// <summary>
/// Registers extender signals and event handlers for TSW X70 series panels.
/// </summary>
/// <param name="x70Panel">The X70 panel to register extenders for.</param>
private void RegisterX70PanelExtenders(TswXX70Base x70Panel)
{
// App Control Extender
x70Panel.ExtenderApplicationControlReservedSigs.DeviceExtenderSigChange += (e, a) =>
{
Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, this, $"X70 App Control Device Extender args: {a.Event}:{a.Sig}:{a.Sig.Type}:{a.Sig.BoolValue}:{a.Sig.UShortValue}:{a.Sig.StringValue}");
UpdateZoomFeedbacks();
if (!x70Panel.ExtenderApplicationControlReservedSigs.HideOpenedApplicationFeedback.BoolValue)
{
x70Panel.ExtenderButtonToolbarReservedSigs.ShowButtonToolbar();
x70Panel.ExtenderButtonToolbarReservedSigs.Button2On();
}
else
{
x70Panel.ExtenderButtonToolbarReservedSigs.HideButtonToolbar();
x70Panel.ExtenderButtonToolbarReservedSigs.Button2Off();
}
};
// Zoom Room App Extender
x70Panel.ExtenderZoomRoomAppReservedSigs.DeviceExtenderSigChange += (e, a) =>
{
Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, this, $"X70 Zoom Room App Device Extender args: {a.Event}:{a.Sig}:{a.Sig.Type}:{a.Sig.BoolValue}:{a.Sig.UShortValue}:{a.Sig.StringValue}");
if (a.Sig.Number == x70Panel.ExtenderZoomRoomAppReservedSigs.ZoomRoomIncomingCallFeedback.Number)
{
ZoomIncomingCallFeedback.FireUpdate();
}
else if (a.Sig.Number == x70Panel.ExtenderZoomRoomAppReservedSigs.ZoomRoomActiveFeedback.Number)
{
ZoomInCallFeedback.FireUpdate();
}
};
// Ethernet Extender
x70Panel.ExtenderEthernetReservedSigs.DeviceExtenderSigChange += (e, a) =>
{
DeviceInfo.MacAddress = x70Panel.ExtenderEthernetReservedSigs.MacAddressFeedback.StringValue;
DeviceInfo.IpAddress = x70Panel.ExtenderEthernetReservedSigs.IpAddressFeedback.StringValue;
Debug.LogMessage(Serilog.Events.LogEventLevel.Debug, this, $"MAC: {DeviceInfo.MacAddress} IP: {DeviceInfo.IpAddress}");
var handler = DeviceInfoChanged;
if (handler == null)
{
return;
}
handler(this, new DeviceInfoEventArgs(DeviceInfo));
};
// Initialize extenders
x70Panel.ExtenderApplicationControlReservedSigs.Use();
x70Panel.ExtenderZoomRoomAppReservedSigs.Use();
x70Panel.ExtenderEthernetReservedSigs.Use();
x70Panel.ExtenderButtonToolbarReservedSigs.Use();
// Initialize button toolbar
x70Panel.ExtenderButtonToolbarReservedSigs.Button1Off();
x70Panel.ExtenderButtonToolbarReservedSigs.Button3Off();
x70Panel.ExtenderButtonToolbarReservedSigs.Button4Off();
x70Panel.ExtenderButtonToolbarReservedSigs.Button5Off();
x70Panel.ExtenderButtonToolbarReservedSigs.Button6Off();
}
/// <summary>
/// Registers extender signals and event handlers for TSW X60 series panels with Zoom room app support.
/// </summary>
/// <param name="x60withZoomApp">The X60 panel with Zoom room app to register extenders for.</param>
private void RegisterX60PanelExtenders(TswX60WithZoomRoomAppReservedSigs x60withZoomApp)
{
// App Control Extender
x60withZoomApp.ExtenderApplicationControlReservedSigs.DeviceExtenderSigChange += (e, a) =>
{
Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, this, $"X60 App Control Device Extender args: {a.Event}:{a.Sig}:{a.Sig.Type}:{a.Sig.BoolValue}:{a.Sig.UShortValue}:{a.Sig.StringValue}");
if (a.Sig.Number == x60withZoomApp.ExtenderApplicationControlReservedSigs.HideOpenApplicationFeedback.Number)
{
AppOpenFeedback.FireUpdate();
}
};
// Zoom Room App Extender
x60withZoomApp.ExtenderZoomRoomAppReservedSigs.DeviceExtenderSigChange += (e, a) =>
{
Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, this, $"X60 Zoom Room App Device Extender args: {a.Event}:{a.Sig}:{a.Sig.Type}:{a.Sig.BoolValue}:{a.Sig.UShortValue}:{a.Sig.StringValue}");
if (a.Sig.Number == x60withZoomApp.ExtenderZoomRoomAppReservedSigs.ZoomRoomIncomingCallFeedback.Number)
{
ZoomIncomingCallFeedback.FireUpdate();
}
else if (a.Sig.Number == x60withZoomApp.ExtenderZoomRoomAppReservedSigs.ZoomRoomActiveFeedback.Number)
{
ZoomInCallFeedback.FireUpdate();
}
};
// Ethernet Extender
x60withZoomApp.ExtenderEthernetReservedSigs.DeviceExtenderSigChange += (e, a) =>
{
DeviceInfo.MacAddress = x60withZoomApp.ExtenderEthernetReservedSigs.MacAddressFeedback.StringValue;
DeviceInfo.IpAddress = x60withZoomApp.ExtenderEthernetReservedSigs.IpAddressFeedback.StringValue;
Debug.LogMessage(Serilog.Events.LogEventLevel.Debug, this, $"MAC: {DeviceInfo.MacAddress} IP: {DeviceInfo.IpAddress}");
var handler = DeviceInfoChanged;
if (handler == null)
{
return;
}
handler(this, new DeviceInfoEventArgs(DeviceInfo));
};
// Initialize extenders
x60withZoomApp.ExtenderZoomRoomAppReservedSigs.Use();
x60withZoomApp.ExtenderApplicationControlReservedSigs.Use();
x60withZoomApp.ExtenderEthernetReservedSigs.Use();
}
}
}

View File

@@ -1,42 +0,0 @@
using Crestron.SimplSharpPro.UI;
using PepperDash.Core;
using PepperDash.Core.Logging;
namespace PepperDash.Essentials.Touchpanel
{
/// <summary>
/// Partial class containing Zoom integration functionality for managing Zoom calls and room control.
/// </summary>
public partial class MobileControlTouchpanelController
{
/// <summary>
/// Updates all Zoom-related feedback values to reflect current state.
/// </summary>
private void UpdateZoomFeedbacks()
{
foreach (var feedback in ZoomFeedbacks)
{
Debug.LogMessage(Serilog.Events.LogEventLevel.Debug, this, $"Updating {feedback.Key}");
feedback.FireUpdate();
}
}
/// <summary>
/// Ends the current Zoom call on the touchpanel.
/// </summary>
public void EndZoomCall()
{
if (Panel is TswX70Base x70Panel)
{
x70Panel.ExtenderZoomRoomAppReservedSigs.ZoomRoomEndCall();
return;
}
if (Panel is TswX60WithZoomRoomAppReservedSigs x60Panel)
{
x60Panel.ExtenderZoomRoomAppReservedSigs.ZoomRoomEndCall();
return;
}
}
}
}

View File

@@ -16,6 +16,7 @@ using PepperDash.Essentials.Core.Config;
using PepperDash.Essentials.Core.DeviceInfo;
using PepperDash.Essentials.Core.DeviceTypeInterfaces;
using PepperDash.Essentials.Core.UI;
using Serilog.Events;
using Feedback = PepperDash.Essentials.Core.Feedback;
namespace PepperDash.Essentials.Touchpanel
@@ -23,9 +24,8 @@ namespace PepperDash.Essentials.Touchpanel
/// <summary>
/// Mobile Control touchpanel controller that provides app control, Zoom integration,
/// and mobile control functionality for Crestron touchpanels.
/// This is the main partial class containing core properties, constructor, and basic functionality.
/// </summary>
public partial class MobileControlTouchpanelController : TouchpanelBase, IHasFeedback, ITswAppControl, ITswZoomControl, IDeviceInfoProvider, IMobileControlCrestronTouchpanelController, ITheme
public class MobileControlTouchpanelController : TouchpanelBase, IHasFeedback, ITswAppControl, ITswZoomControl, IDeviceInfoProvider, IMobileControlCrestronTouchpanelController, ITheme
{
private readonly MobileControlTouchpanelProperties localConfig;
private IMobileControlRoomMessenger _bridge;
@@ -191,12 +191,6 @@ namespace PepperDash.Essentials.Touchpanel
RegisterForExtenders();
if (CrestronEnvironment.DevicePlatform != eDevicePlatform.Appliance)
{
this.LogInformation("Not running on processor. Skipping CS LAN Configuration");
return;
}
try
{
var csAdapterId = CrestronEthernetHelper.GetAdapterdIdForSpecifiedAdapterType(EthernetAdapterType.EthernetCSAdapter);
@@ -206,9 +200,9 @@ namespace PepperDash.Essentials.Touchpanel
this.csSubnetMask = System.Net.IPAddress.Parse(csSubnetMask);
this.csIpAddress = System.Net.IPAddress.Parse(csIpAddress);
}
catch (ArgumentException)
catch
{
this.LogInformation("This processor does not have a CS LAN");
Debug.LogInformation("This processor does not have a CS LAN", this);
}
}
@@ -234,6 +228,123 @@ namespace PepperDash.Essentials.Touchpanel
ConfigWriter.UpdateDeviceConfig(deviceConfig);
}
private void RegisterForExtenders()
{
if (Panel is TswXX70Base x70Panel)
{
x70Panel.ExtenderApplicationControlReservedSigs.DeviceExtenderSigChange += (e, a) =>
{
Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, this, $"X70 App Control Device Extender args: {a.Event}:{a.Sig}:{a.Sig.Type}:{a.Sig.BoolValue}:{a.Sig.UShortValue}:{a.Sig.StringValue}");
UpdateZoomFeedbacks();
if (!x70Panel.ExtenderApplicationControlReservedSigs.HideOpenedApplicationFeedback.BoolValue)
{
x70Panel.ExtenderButtonToolbarReservedSigs.ShowButtonToolbar();
x70Panel.ExtenderButtonToolbarReservedSigs.Button2On();
}
else
{
x70Panel.ExtenderButtonToolbarReservedSigs.HideButtonToolbar();
x70Panel.ExtenderButtonToolbarReservedSigs.Button2Off();
}
};
x70Panel.ExtenderZoomRoomAppReservedSigs.DeviceExtenderSigChange += (e, a) =>
{
Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, this, $"X70 Zoom Room Ap Device Extender args: {a.Event}:{a.Sig}:{a.Sig.Type}:{a.Sig.BoolValue}:{a.Sig.UShortValue}:{a.Sig.StringValue}");
if (a.Sig.Number == x70Panel.ExtenderZoomRoomAppReservedSigs.ZoomRoomIncomingCallFeedback.Number)
{
ZoomIncomingCallFeedback.FireUpdate();
}
else if (a.Sig.Number == x70Panel.ExtenderZoomRoomAppReservedSigs.ZoomRoomActiveFeedback.Number)
{
ZoomInCallFeedback.FireUpdate();
}
};
x70Panel.ExtenderEthernetReservedSigs.DeviceExtenderSigChange += (e, a) =>
{
DeviceInfo.MacAddress = x70Panel.ExtenderEthernetReservedSigs.MacAddressFeedback.StringValue;
DeviceInfo.IpAddress = x70Panel.ExtenderEthernetReservedSigs.IpAddressFeedback.StringValue;
Debug.LogMessage(Serilog.Events.LogEventLevel.Debug, this, $"MAC: {DeviceInfo.MacAddress} IP: {DeviceInfo.IpAddress}");
var handler = DeviceInfoChanged;
if (handler == null)
{
return;
}
handler(this, new DeviceInfoEventArgs(DeviceInfo));
};
x70Panel.ExtenderApplicationControlReservedSigs.Use();
x70Panel.ExtenderZoomRoomAppReservedSigs.Use();
x70Panel.ExtenderEthernetReservedSigs.Use();
x70Panel.ExtenderButtonToolbarReservedSigs.Use();
x70Panel.ExtenderButtonToolbarReservedSigs.Button1Off();
x70Panel.ExtenderButtonToolbarReservedSigs.Button3Off();
x70Panel.ExtenderButtonToolbarReservedSigs.Button4Off();
x70Panel.ExtenderButtonToolbarReservedSigs.Button5Off();
x70Panel.ExtenderButtonToolbarReservedSigs.Button6Off();
return;
}
if (Panel is TswX60WithZoomRoomAppReservedSigs x60withZoomApp)
{
x60withZoomApp.ExtenderApplicationControlReservedSigs.DeviceExtenderSigChange += (e, a) =>
{
Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, this, $"X60 App Control Device Extender args: {a.Event}:{a.Sig}:{a.Sig.Type}:{a.Sig.BoolValue}:{a.Sig.UShortValue}:{a.Sig.StringValue}");
if (a.Sig.Number == x60withZoomApp.ExtenderApplicationControlReservedSigs.HideOpenApplicationFeedback.Number)
{
AppOpenFeedback.FireUpdate();
}
};
x60withZoomApp.ExtenderZoomRoomAppReservedSigs.DeviceExtenderSigChange += (e, a) =>
{
Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, this, $"X60 Zoom Room App Device Extender args: {a.Event}:{a.Sig}:{a.Sig.Type}:{a.Sig.BoolValue}:{a.Sig.UShortValue}:{a.Sig.StringValue}");
if (a.Sig.Number == x60withZoomApp.ExtenderZoomRoomAppReservedSigs.ZoomRoomIncomingCallFeedback.Number)
{
ZoomIncomingCallFeedback.FireUpdate();
}
else if (a.Sig.Number == x60withZoomApp.ExtenderZoomRoomAppReservedSigs.ZoomRoomActiveFeedback.Number)
{
ZoomInCallFeedback.FireUpdate();
}
};
x60withZoomApp.ExtenderEthernetReservedSigs.DeviceExtenderSigChange += (e, a) =>
{
DeviceInfo.MacAddress = x60withZoomApp.ExtenderEthernetReservedSigs.MacAddressFeedback.StringValue;
DeviceInfo.IpAddress = x60withZoomApp.ExtenderEthernetReservedSigs.IpAddressFeedback.StringValue;
Debug.LogMessage(Serilog.Events.LogEventLevel.Debug, this, $"MAC: {DeviceInfo.MacAddress} IP: {DeviceInfo.IpAddress}");
var handler = DeviceInfoChanged;
if (handler == null)
{
return;
}
handler(this, new DeviceInfoEventArgs(DeviceInfo));
};
x60withZoomApp.ExtenderZoomRoomAppReservedSigs.Use();
x60withZoomApp.ExtenderApplicationControlReservedSigs.Use();
x60withZoomApp.ExtenderEthernetReservedSigs.Use();
}
}
/// <summary>
/// Performs custom activation setup for the touchpanel controller, including
/// registering messengers and linking to mobile control.
@@ -323,26 +434,232 @@ namespace PepperDash.Essentials.Touchpanel
}
/// <summary>
/// Updates feedbacks in response to state changes.
/// Gets the URL with the correct IP address based on the connected devices and the Crestron processor's IP address.
/// </summary>
/// <param name="sender">The event sender.</param>
/// <param name="args">The event arguments.</param>
/// <param name="url"></param>
/// <returns></returns>
private string GetUrlWithCorrectIp(string url)
{
var lanAdapterId = CrestronEthernetHelper.GetAdapterdIdForSpecifiedAdapterType(EthernetAdapterType.EthernetLANAdapter);
var processorIp = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, lanAdapterId);
if(csIpAddress == null || csSubnetMask == null || url == null)
{
this.LogWarning("CS IP Address Subnet Mask or url is null, cannot determine correct IP for URL");
return url;
}
this.LogVerbose("Processor IP: {processorIp}, CS IP: {csIpAddress}, CS Subnet Mask: {csSubnetMask}", processorIp, csIpAddress, csSubnetMask);
this.LogVerbose("Connected IP Count: {connectedIps}", ConnectedIps.Count);
var ip = ConnectedIps.Any(ipInfo =>
{
if (System.Net.IPAddress.TryParse(ipInfo.DeviceIpAddress, out var parsedIp))
{
return csIpAddress.IsInSameSubnet(parsedIp, csSubnetMask);
}
this.LogWarning("Invalid IP address: {deviceIpAddress}", ipInfo.DeviceIpAddress);
return false;
}) ? csIpAddress.ToString() : processorIp;
var match = Regex.Match(url, @"^http://([^:/]+):\d+/mc/app\?token=.+$");
if (match.Success)
{
string ipa = match.Groups[1].Value;
// ip will be "192.168.1.100"
}
// replace ipa with ip but leave the rest of the string intact
var updatedUrl = Regex.Replace(url, @"^http://[^:/]+", $"http://{ip}");
this.LogVerbose("Updated URL: {updatedUrl}", updatedUrl);
return updatedUrl;
}
private void SubscribeForMobileControlUpdates()
{
foreach (var dev in DeviceManager.AllDevices)
{
Debug.LogMessage(Serilog.Events.LogEventLevel.Information, this, $"{dev.Key}:{dev.GetType().Name}");
}
var mcList = DeviceManager.AllDevices.OfType<MobileControlSystemController>().ToList();
if (mcList.Count == 0)
{
Debug.LogMessage(Serilog.Events.LogEventLevel.Information, this, $"No Mobile Control controller found");
return;
}
// use first in list, since there should only be one.
var mc = mcList[0];
var bridge = mc.GetRoomBridge(_config.DefaultRoomKey);
if (bridge == null)
{
Debug.LogMessage(Serilog.Events.LogEventLevel.Information, this, $"No Mobile Control bridge for {_config.DefaultRoomKey} found ");
return;
}
_bridge = bridge;
_bridge.UserCodeChanged += UpdateFeedbacks;
_bridge.AppUrlChanged += (s, a) =>
{
this.LogInformation("AppURL changed: {appURL}", _bridge.AppUrl);
SetAppUrl(_bridge.AppUrl);
UpdateFeedbacks(s, a);
};
SetAppUrl(_bridge.AppUrl);
}
/// <summary>
/// Sets the application URL and updates the corresponding feedback.
/// </summary>
/// <param name="url">The new application URL to set.</param>
/// <summary>
/// SetAppUrl method
/// </summary>
public void SetAppUrl(string url)
{
_appUrl = GetUrlWithCorrectIp(url);
AppUrlFeedback.FireUpdate();
}
private void UpdateFeedbacks(object sender, EventArgs args)
{
UpdateFeedbacks();
}
/// <summary>
/// Updates all feedback values to reflect current state.
/// </summary>
private void UpdateFeedbacks()
{
foreach (var feedback in Feedbacks)
foreach (var feedback in Feedbacks) { this.LogDebug("Updating {feedbackKey}", feedback.Key); feedback.FireUpdate(); }
}
private void UpdateZoomFeedbacks()
{
foreach (var feedback in ZoomFeedbacks)
{
this.LogDebug("Updating {feedbackKey}", feedback.Key);
Debug.LogMessage(Serilog.Events.LogEventLevel.Debug, this, $"Updating {feedback.Key}");
feedback.FireUpdate();
}
}
/// <summary>
/// HideOpenApp method
/// </summary>
public void HideOpenApp()
{
if (Panel is TswX70Base x70Panel)
{
x70Panel.ExtenderApplicationControlReservedSigs.HideOpenedApplication();
return;
}
if (Panel is TswX60BaseClass x60Panel)
{
x60Panel.ExtenderApplicationControlReservedSigs.HideOpenApplication();
return;
}
}
/// <summary>
/// OpenApp method
/// </summary>
public void OpenApp()
{
if (Panel is TswX70Base x70Panel)
{
x70Panel.ExtenderApplicationControlReservedSigs.OpenApplication();
return;
}
if (Panel is TswX60WithZoomRoomAppReservedSigs)
{
Debug.LogMessage(Serilog.Events.LogEventLevel.Information, this, $"X60 panel does not support zoom app");
return;
}
}
/// <summary>
/// CloseOpenApp method
/// </summary>
public void CloseOpenApp()
{
if (Panel is TswX70Base x70Panel)
{
x70Panel.ExtenderApplicationControlReservedSigs.CloseOpenedApplication();
return;
}
if (Panel is TswX60WithZoomRoomAppReservedSigs x60Panel)
{
x60Panel.ExtenderApplicationControlReservedSigs.CloseOpenedApplication();
return;
}
}
/// <summary>
/// EndZoomCall method
/// </summary>
public void EndZoomCall()
{
if (Panel is TswX70Base x70Panel)
{
x70Panel.ExtenderZoomRoomAppReservedSigs.ZoomRoomEndCall();
return;
}
if (Panel is TswX60WithZoomRoomAppReservedSigs x60Panel)
{
x60Panel.ExtenderZoomRoomAppReservedSigs.ZoomRoomEndCall();
return;
}
}
/// <summary>
/// UpdateDeviceInfo method
/// </summary>
public void UpdateDeviceInfo()
{
if (Panel is TswXX70Base x70Panel)
{
DeviceInfo.MacAddress = x70Panel.ExtenderEthernetReservedSigs.MacAddressFeedback.StringValue;
DeviceInfo.IpAddress = x70Panel.ExtenderEthernetReservedSigs.IpAddressFeedback.StringValue;
var handler = DeviceInfoChanged;
if (handler == null)
{
return;
}
handler(this, new DeviceInfoEventArgs(DeviceInfo));
}
if (Panel is TswX60WithZoomRoomAppReservedSigs x60Panel)
{
DeviceInfo.MacAddress = x60Panel.ExtenderEthernetReservedSigs.MacAddressFeedback.StringValue;
DeviceInfo.IpAddress = x60Panel.ExtenderEthernetReservedSigs.IpAddressFeedback.StringValue;
var handler = DeviceInfoChanged;
if (handler == null)
{
return;
}
handler(this, new DeviceInfoEventArgs(DeviceInfo));
}
Debug.LogMessage(Serilog.Events.LogEventLevel.Debug, this, $"MAC: {DeviceInfo.MacAddress} IP: {DeviceInfo.IpAddress}");
}
}
/// <summary>