diff --git a/.gitignore b/.gitignore index bd60ff8d..db1e92a3 100644 --- a/.gitignore +++ b/.gitignore @@ -395,3 +395,4 @@ essentials-framework/Essentials Interfaces/PepperDash_Essentials_Interfaces/Pepp _site/ api/ *.DS_Store +/._PepperDash.Essentials.4Series.sln diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/CameraBaseMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/CameraBaseMessenger.cs index e2e16b3e..cb3f77a5 100644 --- a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/CameraBaseMessenger.cs +++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/CameraBaseMessenger.cs @@ -140,17 +140,19 @@ namespace PepperDash.Essentials.AppServer.Messengers if (Camera is IHasCameraPresets presetsCamera) { - for (int i = 1; i <= 6; i++) + AddAction("/recallPreset", (id, content) => { - var preset = i; - AddAction("/cameraPreset" + i, (id, content) => - { - var msg = content.ToObject>(); + var msg = content.ToObject>(); - presetsCamera.PresetSelect(msg.Value); - }); + presetsCamera.PresetSelect(msg.Value); + }); - } + AddAction("/storePreset", (id, content) => + { + var msg = content.ToObject>(); + + presetsCamera.PresetStore(msg.Value, string.Empty); + }); } } @@ -164,9 +166,8 @@ namespace PepperDash.Essentials.AppServer.Messengers return; } - timerHandler(state.Value, cameraAction); + timerHandler(Camera.Key, cameraAction); - cameraAction(state.Value.Equals("true", StringComparison.InvariantCultureIgnoreCase)); } /// diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IBasicVideoMuteWithFeedbackMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IBasicVideoMuteWithFeedbackMessenger.cs new file mode 100644 index 00000000..70d59aa4 --- /dev/null +++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IBasicVideoMuteWithFeedbackMessenger.cs @@ -0,0 +1,77 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using PepperDash.Core; +using PepperDash.Essentials.Core; +using System.Collections.Generic; + +namespace PepperDash.Essentials.AppServer.Messengers +{ + /// + /// Represents a IBasicVideoMuteWithFeedbackMessenger + /// + public class IBasicVideoMuteWithFeedbackMessenger : MessengerBase + { + private readonly IBasicVideoMuteWithFeedback device; + + public IBasicVideoMuteWithFeedbackMessenger(string key, string messagePath, IBasicVideoMuteWithFeedback device) + : base(key, messagePath, device as IKeyName) + { + this.device = device; + } + + /// + /// SendFullStatus method + /// + public void SendFullStatus() + { + var messageObj = new IBasicVideoMuteWithFeedbackMessage + { + VideoMuteState = device.VideoMuteIsOn.BoolValue + }; + + PostStatusMessage(messageObj); + } + + protected override void RegisterActions() + { + base.RegisterActions(); + + AddAction("/fullStatus", (id, content) => SendFullStatus()); + + AddAction("/videoMuteToggle", (id, content) => + { + device.VideoMuteToggle(); + }); + + AddAction("/videoMuteOn", (id, content) => + { + device.VideoMuteOn(); + }); + + AddAction("/videoMuteOff", (id, content) => + { + device.VideoMuteOff(); + }); + + device.VideoMuteIsOn.OutputChange += VideoMuteIsOnFeedback_OutputChange; + } + + private void VideoMuteIsOnFeedback_OutputChange(object sender, FeedbackEventArgs args) + { + PostStatusMessage(JToken.FromObject(new + { + videoMuteState = args.BoolValue + }) + ); + } + } + + /// + /// Represents a IBasicVideoMuteWithFeedbackMessage + /// + public class IBasicVideoMuteWithFeedbackMessage : DeviceStateMessageBase + { + [JsonProperty("videoMuteState")] + public bool VideoMuteState { get; set; } + } +} diff --git a/src/PepperDash.Essentials.MobileControl/MobileControlSystemController.cs b/src/PepperDash.Essentials.MobileControl/MobileControlSystemController.cs index f5854516..49e71010 100644 --- a/src/PepperDash.Essentials.MobileControl/MobileControlSystemController.cs +++ b/src/PepperDash.Essentials.MobileControl/MobileControlSystemController.cs @@ -505,6 +505,25 @@ namespace PepperDash.Essentials messengerAdded = true; } + if (device is IBasicVideoMuteWithFeedback) + { + var deviceKey = device.Key; + this.LogVerbose( + "Adding IBasicVideoMuteWithFeedback for {deviceKey}", + deviceKey + ); + + var videoMuteControlDevice = device as IBasicVideoMuteWithFeedback; + var messenger = new IBasicVideoMuteWithFeedbackMessenger( + $"{device.Key}-videoMute-{Key}", + string.Format("/device/{0}", deviceKey), + videoMuteControlDevice + ); + AddDefaultDeviceMessenger(messenger); + + messengerAdded = true; + } + if (device is ILightingScenes || device is LightingBase) { var deviceKey = device.Key; diff --git a/src/PepperDash.Essentials.MobileControl/Touchpanel/MobileControlTouchpanelController.cs b/src/PepperDash.Essentials.MobileControl/Touchpanel/MobileControlTouchpanelController.cs index ab059244..a4476cd8 100644 --- a/src/PepperDash.Essentials.MobileControl/Touchpanel/MobileControlTouchpanelController.cs +++ b/src/PepperDash.Essentials.MobileControl/Touchpanel/MobileControlTouchpanelController.cs @@ -2,6 +2,8 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; +using System.Text.RegularExpressions; +using Crestron.SimplSharp; using Crestron.SimplSharpPro; using Crestron.SimplSharpPro.DeviceSupport; using Crestron.SimplSharpPro.UI; @@ -106,6 +108,11 @@ namespace PepperDash.Essentials.Touchpanel public ReadOnlyCollection ConnectedIps => Panel.ConnectedIpList; + private System.Net.IPAddress csIpAddress; + + private System.Net.IPAddress csSubnetMask; + + /// /// Initializes a new instance of the MobileControlTouchpanelController class. /// @@ -182,6 +189,13 @@ namespace PepperDash.Essentials.Touchpanel }; RegisterForExtenders(); + + var csAdapterId = CrestronEthernetHelper.GetAdapterdIdForSpecifiedAdapterType(EthernetAdapterType.EthernetCSAdapter); + var csSubnetMask = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_MASK, csAdapterId); + var csIpAddress = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, csAdapterId); + + this.csSubnetMask = System.Net.IPAddress.Parse(csSubnetMask); + this.csIpAddress = System.Net.IPAddress.Parse(csIpAddress); } /// @@ -381,19 +395,81 @@ namespace PepperDash.Essentials.Touchpanel McServerUrlFeedback.LinkInputSig(Panel.StringInput[3]); UserCodeFeedback.LinkInputSig(Panel.StringInput[4]); + Panel.IpInformationChange += (sender, args) => + { + if (args.Connected) + { + this.LogVerbose("Connection from IP: {ip}", args.DeviceIpAddress); + this.LogInformation("Sending {appUrl} on join 1", AppUrlFeedback.StringValue); + + var appUrl = GetUrlWithCorrectIp(_appUrl); + Panel.StringInput[1].StringValue = appUrl; + + SetAppUrl(appUrl); + } + else + { + this.LogVerbose("Disconnection from IP: {ip}", args.DeviceIpAddress); + } + }; + Panel.OnlineStatusChange += (sender, args) => { - UpdateFeedbacks(); - this.LogInformation("Sending {appUrl} on join 1", AppUrlFeedback.StringValue); - Panel.StringInput[1].StringValue = AppUrlFeedback.StringValue; + UpdateFeedbacks(); + Panel.StringInput[1].StringValue = _appUrl; Panel.StringInput[2].StringValue = QrCodeUrlFeedback.StringValue; Panel.StringInput[3].StringValue = McServerUrlFeedback.StringValue; Panel.StringInput[4].StringValue = UserCodeFeedback.StringValue; }; } + /// + /// Gets the URL with the correct IP address based on the connected devices and the Crestron processor's IP address. + /// + /// + /// + 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) @@ -443,7 +519,8 @@ namespace PepperDash.Essentials.Touchpanel /// public void SetAppUrl(string url) { - _appUrl = url; + _appUrl = GetUrlWithCorrectIp(url); + AppUrlFeedback.FireUpdate(); } diff --git a/src/PepperDash.Essentials.MobileControl/WebSocketServer/MobileControlWebsocketServer.cs b/src/PepperDash.Essentials.MobileControl/WebSocketServer/MobileControlWebsocketServer.cs index 0ebb0707..8fcfd11f 100644 --- a/src/PepperDash.Essentials.MobileControl/WebSocketServer/MobileControlWebsocketServer.cs +++ b/src/PepperDash.Essentials.MobileControl/WebSocketServer/MobileControlWebsocketServer.cs @@ -327,18 +327,22 @@ namespace PepperDash.Essentials.WebSocketServer } string ip = processorIp; - if (touchpanel.Touchpanel is IMobileControlCrestronTouchpanelController crestronTouchpanel && csIpAddress != null) - { - ip = crestronTouchpanel.ConnectedIps.Any(ipInfo => - { - if (System.Net.IPAddress.TryParse(ipInfo.DeviceIpAddress, out var parsedIp)) - { - return csIpAddress.IsInSameSubnet(parsedIp, csSubnetMask); - } - this.LogWarning("Invalid IP address: {deviceIpAddress}", ipInfo.DeviceIpAddress); - return false; - }) ? csIpAddress.ToString() : processorIp; - } + + // Moved to the MobileControlTouchpanelController class in the GetUrlWithCorrectIp method + // triggered by the Panel.IpInformationChange event so that we know we have the necessary info + // to make the determination of which IP to use. + //if (touchpanel.Touchpanel is IMobileControlCrestronTouchpanelController crestronTouchpanel && csIpAddress != null) + //{ + // ip = crestronTouchpanel.ConnectedIps.Any(ipInfo => + // { + // if (System.Net.IPAddress.TryParse(ipInfo.DeviceIpAddress, out var parsedIp)) + // { + // return csIpAddress.IsInSameSubnet(parsedIp, csSubnetMask); + // } + // this.LogWarning("Invalid IP address: {deviceIpAddress}", ipInfo.DeviceIpAddress); + // return false; + // }) ? csIpAddress.ToString() : processorIp; + //} var appUrl = $"http://{ip}:{_parent.Config.DirectServer.Port}/mc/app?token={touchpanel.Key}";