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.Core/Config/BaseStreamingDeviceProperties.cs b/src/PepperDash.Essentials.Core/Config/BaseStreamingDeviceProperties.cs
index fe77b6af..fd87710b 100644
--- a/src/PepperDash.Essentials.Core/Config/BaseStreamingDeviceProperties.cs
+++ b/src/PepperDash.Essentials.Core/Config/BaseStreamingDeviceProperties.cs
@@ -19,5 +19,11 @@ namespace PepperDash.Essentials.Core.Config
///
[JsonProperty("multicastAudioAddress", NullValueHandling = NullValueHandling.Ignore)]
public string MulticastAudioAddress { get; set; }
+
+ ///
+ /// The URL for the streaming device's media stream.
+ ///
+ [JsonProperty("streamUrl", NullValueHandling = NullValueHandling.Ignore)]
+ public string StreamUrl { get; set; }
}
}
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}";