diff --git a/src/PepperDash.Essentials.Core/Config/Essentials/ConfigReader.cs b/src/PepperDash.Essentials.Core/Config/Essentials/ConfigReader.cs index f1a79d3c..a1afba93 100644 --- a/src/PepperDash.Essentials.Core/Config/Essentials/ConfigReader.cs +++ b/src/PepperDash.Essentials.Core/Config/Essentials/ConfigReader.cs @@ -1,11 +1,8 @@ - - -using System; +using System; using System.Linq; using System.Text; using Crestron.SimplSharp; using Crestron.SimplSharp.CrestronIO; -using Newtonsoft.Json; using Newtonsoft.Json.Linq; using PepperDash.Core; using PepperDash.Core.Config; diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHasCallHistoryMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHasCallHistoryMessenger.cs new file mode 100644 index 00000000..10e2f8d6 --- /dev/null +++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHasCallHistoryMessenger.cs @@ -0,0 +1,84 @@ +using System; +using System.Collections.Generic; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using PepperDash.Core.Logging; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Devices.Common.Codec; + +namespace PepperDash.Essentials.AppServer.Messengers +{ + /// + /// Messenger for devices implementing + /// + public class IHasCallHistoryMessenger : MessengerBase + { + private readonly IHasCallHistory _callHistory; + + /// + /// Initializes a new instance of the class. + /// + public IHasCallHistoryMessenger(string key, string messagePath, EssentialsDevice device) + : base(key, messagePath, device) + { + _callHistory = device as IHasCallHistory ?? throw new ArgumentException("device must implement IHasCallHistory", nameof(device)); + _callHistory.CallHistory.RecentCallsListHasChanged += CallHistory_RecentCallsListHasChanged; + } + + /// + protected override void RegisterActions() + { + base.RegisterActions(); + + AddAction("/fullStatus", (id, content) => PostCallHistory()); + + AddAction("/getCallHistory", (id, content) => PostCallHistory()); + } + + private void CallHistory_RecentCallsListHasChanged(object sender, EventArgs e) + { + try + { + if (!(sender is CodecCallHistory codecCallHistory)) return; + + var recents = codecCallHistory.RecentCalls; + if (recents != null) + { + PostStatusMessage(new IHasCallHistoryStateMessage + { + RecentCalls = recents + }); + } + } + catch (Exception ex) + { + this.LogError(ex, "Error posting call history"); + } + } + + private void PostCallHistory() + { + try + { + var recents = _callHistory.CallHistory.RecentCalls; + if (recents != null) + { + PostStatusMessage(new IHasCallHistoryStateMessage + { + RecentCalls = recents + }); + } + } + catch (Exception ex) + { + this.LogError(ex, "Error posting call history"); + } + } + } + + public class IHasCallHistoryStateMessage : DeviceStateMessageBase + { + [JsonProperty("recentCalls", NullValueHandling = NullValueHandling.Ignore)] + public List RecentCalls { get; set; } + } +} diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHasCamerasWithControlMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHasCamerasWithControlsMessenger.cs similarity index 95% rename from src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHasCamerasWithControlMessenger.cs rename to src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHasCamerasWithControlsMessenger.cs index cce07dcd..e20eabdc 100644 --- a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHasCamerasWithControlMessenger.cs +++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHasCamerasWithControlsMessenger.cs @@ -12,7 +12,7 @@ namespace PepperDash.Essentials.AppServer.Messengers /// /// Messenger for devices that implement the IHasCameras interface. /// - public class IHasCamerasWithControlMessenger : MessengerBase + public class IHasCamerasWithControlsMessenger : MessengerBase { /// /// Device being bridged that implements IHasCameras interface. @@ -26,7 +26,7 @@ namespace PepperDash.Essentials.AppServer.Messengers /// /// /// - public IHasCamerasWithControlMessenger(string key, string messagePath, IHasCamerasWithControls cameraController) + public IHasCamerasWithControlsMessenger(string key, string messagePath, IHasCamerasWithControls cameraController) : base(key, messagePath, cameraController) { CameraController = cameraController ?? throw new ArgumentNullException("cameraController"); diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHasCodecCamerasMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHasCodecCamerasMessenger.cs new file mode 100644 index 00000000..c3f8d3fd --- /dev/null +++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHasCodecCamerasMessenger.cs @@ -0,0 +1,310 @@ +using System; +using System.Collections.Generic; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using PepperDash.Core.Logging; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Devices.Common.Cameras; +using PepperDash.Essentials.Devices.Common.VideoCodec; + +namespace PepperDash.Essentials.AppServer.Messengers +{ + /// + /// Messenger for devices implementing , including + /// sub-interface support for , + /// , and . + /// + public class IHasCodecCamerasMessenger : MessengerBase + { + private readonly VideoCodecBase _codec; + private readonly IHasCodecCameras _cameraCodec; + + /// + /// Initializes a new instance of the class. + /// + public IHasCodecCamerasMessenger(string key, string messagePath, VideoCodecBase codec) + : base(key, messagePath, codec) + { + _codec = codec ?? throw new ArgumentNullException(nameof(codec)); + _cameraCodec = codec as IHasCodecCameras ?? throw new ArgumentException("codec must implement IHasCodecCameras", nameof(codec)); + } + + /// + protected override void RegisterActions() + { + base.RegisterActions(); + + this.LogVerbose("Adding IHasCodecCameras Actions"); + + _cameraCodec.CameraSelected += CameraCodec_CameraSelected; + + AddAction("/fullStatus", (id, content) => PostSelectedCamera()); + + AddAction("/cameraSelect", (id, content) => + { + var msg = content.ToObject>(); + _cameraCodec.SelectCamera(msg.Value); + }); + + MapCameraActions(); + + if (_codec is IHasCodecRoomPresets presetsCodec) + { + this.LogVerbose("Adding IHasCodecRoomPresets Actions"); + + presetsCodec.CodecRoomPresetsListHasChanged += PresetsCodec_CameraPresetsListHasChanged; + + AddAction("/cameraPreset", (id, content) => + { + var msg = content.ToObject>(); + presetsCodec.CodecRoomPresetSelect(msg.Value); + }); + + AddAction("/cameraPresetStore", (id, content) => + { + var msg = content.ToObject(); + presetsCodec.CodecRoomPresetStore(msg.ID, msg.Description); + }); + } + + if (_codec is IHasCameraAutoMode speakerTrackCodec) + { + this.LogVerbose("Adding IHasCameraAutoMode Actions"); + + speakerTrackCodec.CameraAutoModeIsOnFeedback.OutputChange += CameraAutoModeIsOnFeedback_OutputChange; + + AddAction("/cameraModeAuto", (id, content) => speakerTrackCodec.CameraAutoModeOn()); + AddAction("/cameraModeManual", (id, content) => speakerTrackCodec.CameraAutoModeOff()); + } + + if (_codec is IHasCameraOff cameraOffCodec) + { + this.LogVerbose("Adding IHasCameraOff Actions"); + + cameraOffCodec.CameraIsOffFeedback.OutputChange += CameraIsOffFeedback_OutputChange; + + AddAction("/cameraModeOff", (id, content) => cameraOffCodec.CameraOff()); + } + } + + private void CameraCodec_CameraSelected(object sender, CameraSelectedEventArgs e) + { + try + { + MapCameraActions(); + PostSelectedCamera(); + } + catch (Exception ex) + { + this.LogError(ex, "Exception handling camera selected event"); + } + } + + private void MapCameraActions() + { + if (_cameraCodec.SelectedCamera == null) return; + + RemoveAction("/cameraUp"); + RemoveAction("/cameraDown"); + RemoveAction("/cameraLeft"); + RemoveAction("/cameraRight"); + RemoveAction("/cameraZoomIn"); + RemoveAction("/cameraZoomOut"); + RemoveAction("/cameraHome"); + + if (_cameraCodec.SelectedCamera is IHasCameraPtzControl camera) + { + AddAction("/cameraUp", (id, content) => HandleCameraPressAndHold(content, b => + { + if (b) camera.TiltUp(); else camera.TiltStop(); + })); + + AddAction("/cameraDown", (id, content) => HandleCameraPressAndHold(content, b => + { + if (b) camera.TiltDown(); else camera.TiltStop(); + })); + + AddAction("/cameraLeft", (id, content) => HandleCameraPressAndHold(content, b => + { + if (b) camera.PanLeft(); else camera.PanStop(); + })); + + AddAction("/cameraRight", (id, content) => HandleCameraPressAndHold(content, b => + { + if (b) camera.PanRight(); else camera.PanStop(); + })); + + AddAction("/cameraZoomIn", (id, content) => HandleCameraPressAndHold(content, b => + { + if (b) camera.ZoomIn(); else camera.ZoomStop(); + })); + + AddAction("/cameraZoomOut", (id, content) => HandleCameraPressAndHold(content, b => + { + if (b) camera.ZoomOut(); else camera.ZoomStop(); + })); + + AddAction("/cameraHome", (id, content) => camera.PositionHome()); + + RemoveAction("/cameraAutoFocus"); + RemoveAction("/cameraFocusNear"); + RemoveAction("/cameraFocusFar"); + + if (_cameraCodec.SelectedCamera is IHasCameraFocusControl focusCamera) + { + AddAction("/cameraAutoFocus", (id, content) => focusCamera.TriggerAutoFocus()); + + AddAction("/cameraFocusNear", (id, content) => HandleCameraPressAndHold(content, b => + { + if (b) focusCamera.FocusNear(); else focusCamera.FocusStop(); + })); + + AddAction("/cameraFocusFar", (id, content) => HandleCameraPressAndHold(content, b => + { + if (b) focusCamera.FocusFar(); else focusCamera.FocusStop(); + })); + } + } + } + + private void HandleCameraPressAndHold(JToken content, Action cameraAction) + { + var state = content.ToObject>(); + var timerHandler = PressAndHoldHandler.GetPressAndHoldHandler(state.Value); + if (timerHandler == null) return; + timerHandler(state.Value, cameraAction); + cameraAction(state.Value.Equals("true", StringComparison.InvariantCultureIgnoreCase)); + } + + private void CameraAutoModeIsOnFeedback_OutputChange(object sender, FeedbackEventArgs e) + { + PostCameraMode(); + } + + private void CameraIsOffFeedback_OutputChange(object sender, FeedbackEventArgs e) + { + PostCameraMode(); + } + + private void PresetsCodec_CameraPresetsListHasChanged(object sender, EventArgs e) + { + PostCameraPresets(); + } + + private string GetCameraMode() + { + string m = ""; + + if (_codec is IHasCameraAutoMode speakerTrackCodec) + { + m = speakerTrackCodec.CameraAutoModeIsOnFeedback.BoolValue + ? eCameraControlMode.Auto.ToString().ToLower() + : eCameraControlMode.Manual.ToString().ToLower(); + } + + if (_codec is IHasCameraOff cameraOffCodec && cameraOffCodec.CameraIsOffFeedback.BoolValue) + { + m = eCameraControlMode.Off.ToString().ToLower(); + } + + return m; + } + + private Camera GetSelectedCamera() + { + var camera = new Camera(); + + if (_cameraCodec.SelectedCameraFeedback != null) + camera.Key = _cameraCodec.SelectedCameraFeedback.StringValue; + + if (_cameraCodec.SelectedCamera != null) + { + camera.Name = _cameraCodec.SelectedCamera.Name; + + if (_cameraCodec.SelectedCamera is IHasCameraPtzControl ptz) + { + camera.Capabilities = new CameraCapabilities + { + CanPan = ptz is IHasCameraPanControl, + CanTilt = ptz is IHasCameraTiltControl, + CanZoom = ptz is IHasCameraZoomControl, + CanFocus = ptz is IHasCameraFocusControl, + }; + } + } + + if (_cameraCodec.ControllingFarEndCameraFeedback != null) + camera.IsFarEnd = _cameraCodec.ControllingFarEndCameraFeedback.BoolValue; + + return camera; + } + + private List GetCurrentPresets() + { + if (!(_codec is IHasCodecRoomPresets presetsCodec)) return null; + + if (_codec is IHasFarEndCameraControl farEnd && farEnd.ControllingFarEndCameraFeedback.BoolValue) + return presetsCodec.FarEndRoomPresets; + + return presetsCodec.NearEndPresets; + } + + private void PostCameraMode() + { + try + { + PostStatusMessage(new IHasCodecCamerasStateMessage + { + CameraMode = GetCameraMode() + }); + } + catch (Exception ex) + { + this.LogError(ex, "Error posting camera mode"); + } + } + + private void PostSelectedCamera() + { + try + { + PostStatusMessage(new IHasCodecCamerasStateMessage + { + Cameras = new CameraStatus { SelectedCamera = GetSelectedCamera() }, + Presets = GetCurrentPresets() + }); + } + catch (Exception ex) + { + this.LogError(ex, "Error posting selected camera"); + } + } + + private void PostCameraPresets() + { + try + { + PostStatusMessage(new IHasCodecCamerasStateMessage + { + Presets = GetCurrentPresets() + }); + } + catch (Exception ex) + { + this.LogError(ex, "Error posting camera presets"); + } + } + } + + public class IHasCodecCamerasStateMessage : DeviceStateMessageBase + { + [JsonProperty("cameraMode", NullValueHandling = NullValueHandling.Ignore)] + public string CameraMode { get; set; } + + [JsonProperty("cameras", NullValueHandling = NullValueHandling.Ignore)] + public CameraStatus Cameras { get; set; } + + [JsonProperty("presets", NullValueHandling = NullValueHandling.Ignore)] + public List Presets { get; set; } + } +} diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHasCodecLayoutsMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHasCodecLayoutsMessenger.cs new file mode 100644 index 00000000..db9cea4d --- /dev/null +++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHasCodecLayoutsMessenger.cs @@ -0,0 +1,64 @@ +using System; +using Newtonsoft.Json; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Devices.Common.VideoCodec; + +namespace PepperDash.Essentials.AppServer.Messengers +{ + /// + /// Messenger for devices implementing + /// + public class IHasCodecLayoutsMessenger : MessengerBase + { + private readonly IHasCodecLayouts _layouts; + + /// + /// Initializes a new instance of the class. + /// + public IHasCodecLayoutsMessenger(string key, string messagePath, EssentialsDevice device) + : base(key, messagePath, device) + { + _layouts = device as IHasCodecLayouts ?? throw new ArgumentException("device must implement IHasCodecLayouts", nameof(device)); + + _layouts.LocalLayoutFeedback.OutputChange += LocalLayoutFeedback_OutputChange; + } + + private void LocalLayoutFeedback_OutputChange(object sender, FeedbackEventArgs e) + { + SendFullStatus(); + } + + private void SendFullStatus() + { + PostStatusMessage(new IHasCodecLayoutsStateMessage + { + CurrentLayout = _layouts.LocalLayoutFeedback.StringValue + }); + } + + /// + protected override void RegisterActions() + { + base.RegisterActions(); + + AddAction("/fullStatus", (id, content) => SendFullStatus()); + + AddAction("/layoutStatus", (id, content) => SendFullStatus()); + + AddAction("/cameraRemoteView", (id, content) => _layouts.LocalLayoutToggle()); + AddAction("/cameraLayout", (id, content) => _layouts.LocalLayoutToggle()); + } + } + + /// + /// State message for + /// + public class IHasCodecLayoutsStateMessage : DeviceStateMessageBase + { + /// + /// Gets or sets the current layout of the codec + /// + [JsonProperty("currentLayout", NullValueHandling = NullValueHandling.Ignore)] + public string CurrentLayout { get; set; } + } +} diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHasCodecSelfViewMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHasCodecSelfViewMessenger.cs new file mode 100644 index 00000000..885c786d --- /dev/null +++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHasCodecSelfViewMessenger.cs @@ -0,0 +1,62 @@ +using System; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using PepperDash.Core.Logging; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Devices.Common.VideoCodec; + +namespace PepperDash.Essentials.AppServer.Messengers +{ + /// + /// Messenger for devices implementing + /// + public class IHasCodecSelfViewMessenger : MessengerBase + { + private readonly IHasCodecSelfView _selfView; + + /// + /// Initializes a new instance of the class. + /// + public IHasCodecSelfViewMessenger(string key, string messagePath, EssentialsDevice device) + : base(key, messagePath, device) + { + _selfView = device as IHasCodecSelfView ?? throw new ArgumentException("device must implement IHasCodecSelfView", nameof(device)); + } + + /// + protected override void RegisterActions() + { + base.RegisterActions(); + + AddAction("/cameraSelfView", (id, content) => _selfView.SelfViewModeToggle()); + + _selfView.SelfviewIsOnFeedback.OutputChange += SelfviewIsOnFeedback_OutputChange; + } + + private void SelfviewIsOnFeedback_OutputChange(object sender, FeedbackEventArgs e) + { + PostCameraSelfView(); + } + + private void PostCameraSelfView() + { + try + { + PostStatusMessage(new IHasCodecSelfViewStateMessage + { + CameraSelfViewIsOn = _selfView.SelfviewIsOnFeedback.BoolValue + }); + } + catch (Exception ex) + { + this.LogError(ex, "Error posting camera self view"); + } + } + } + + public class IHasCodecSelfViewStateMessage : DeviceStateMessageBase + { + [JsonProperty("cameraSelfView", NullValueHandling = NullValueHandling.Ignore)] + public bool? CameraSelfViewIsOn { get; set; } + } +} diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHasDirectoryMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHasDirectoryMessenger.cs new file mode 100644 index 00000000..677d31c1 --- /dev/null +++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHasDirectoryMessenger.cs @@ -0,0 +1,129 @@ +using System; +using System.Threading.Tasks; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using PepperDash.Core.Logging; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Devices.Common.Codec; +using PepperDash.Essentials.Devices.Common.VideoCodec; + +namespace PepperDash.Essentials.AppServer.Messengers +{ + /// + /// Messenger for devices implementing + /// + public class IHasDirectoryMessenger : MessengerBase + { + private readonly IHasDirectory _directory; + + /// + /// Initializes a new instance of the class. + /// + public IHasDirectoryMessenger(string key, string messagePath, EssentialsDevice device) + : base(key, messagePath, device) + { + _directory = device as IHasDirectory ?? throw new ArgumentException("device must implement IHasDirectory", nameof(device)); + } + + /// + protected override void RegisterActions() + { + base.RegisterActions(); + + AddAction("/getDirectory", (id, content) => GetDirectoryRoot()); + + AddAction("/directoryById", (id, content) => + { + var msg = content.ToObject>(); + GetDirectory(msg.Value); + }); + + AddAction("/directorySearch", (id, content) => + { + var msg = content.ToObject>(); + GetDirectory(msg.Value); + }); + + AddAction("/directoryBack", (id, content) => GetPreviousDirectory()); + + _directory.DirectoryResultReturned += DirectoryResultReturned; + _directory.PhonebookSyncState.InitialSyncCompleted += PhonebookSyncState_InitialSyncCompleted; + } + + private void DirectoryResultReturned(object sender, DirectoryEventArgs e) + { + SendDirectory(e.Directory); + } + + private void SendDirectory(CodecDirectory directory) + { + try + { + this.LogVerbose("Sending Directory. Directory Item Count: {directoryItemCount}", directory.CurrentDirectoryResults.Count); + Task.Run(() => PostStatusMessage(new IHasDirectoryStateMessage + { + CurrentDirectory = directory + })); + } + catch (Exception ex) + { + this.LogError(ex, "Error sending directory"); + } + } + + private void PhonebookSyncState_InitialSyncCompleted(object sender, EventArgs e) + { + try + { + PostStatusMessage(new IHasDirectoryStateMessage + { + InitialPhonebookSyncComplete = true + }); + } + catch (Exception ex) + { + this.LogError(ex, "Error posting phonebook sync state"); + } + } + + private void GetDirectory(string id) + { + _directory.GetDirectoryFolderContents(id); + } + + private void GetDirectoryRoot() + { + try + { + if (!_directory.PhonebookSyncState.InitialSyncComplete) + { + PostStatusMessage(new IHasDirectoryStateMessage + { + InitialPhonebookSyncComplete = false + }); + return; + } + + _directory.SetCurrentDirectoryToRoot(); + } + catch (Exception ex) + { + this.LogError(ex, "Error getting directory root"); + } + } + + private void GetPreviousDirectory() + { + _directory.GetDirectoryParentFolderContents(); + } + } + + public class IHasDirectoryStateMessage : DeviceStateMessageBase + { + [JsonProperty("currentDirectory", NullValueHandling = NullValueHandling.Ignore)] + public CodecDirectory CurrentDirectory { get; set; } + + [JsonProperty("initialPhonebookSyncComplete", NullValueHandling = NullValueHandling.Ignore)] + public bool? InitialPhonebookSyncComplete { get; set; } + } +} diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHasFarEndContentStatusMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHasFarEndContentStatusMessenger.cs new file mode 100644 index 00000000..0e307a6f --- /dev/null +++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHasFarEndContentStatusMessenger.cs @@ -0,0 +1,55 @@ +using System; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using PepperDash.Core.Logging; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.DeviceTypeInterfaces; + +namespace PepperDash.Essentials.AppServer.Messengers +{ + /// + /// Messenger for devices implementing + /// + public class IHasFarEndContentStatusMessenger : MessengerBase + { + private readonly IHasFarEndContentStatus _device; + + /// + /// Initializes a new instance of the class. + /// + public IHasFarEndContentStatusMessenger(string key, string messagePath, EssentialsDevice device) + : base(key, messagePath, device) + { + _device = device as IHasFarEndContentStatus ?? throw new ArgumentException("device must implement IHasFarEndContentStatus", nameof(device)); + } + + /// + protected override void RegisterActions() + { + base.RegisterActions(); + + _device.ReceivingContent.OutputChange += (sender, args) => PostReceivingContent(args.BoolValue); + } + + private void PostReceivingContent(bool receivingContent) + { + try + { + PostStatusMessage(new IHasFarEndContentStatusStateMessage + { + ReceivingContent = receivingContent + }); + } + catch (Exception ex) + { + this.LogError(ex, "Error posting receiving content"); + } + } + } + + public class IHasFarEndContentStatusStateMessage : DeviceStateMessageBase + { + [JsonProperty("receivingContent", NullValueHandling = NullValueHandling.Ignore)] + public bool? ReceivingContent { get; set; } + } +} diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IPasswordPromptMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IPasswordPromptMessenger.cs new file mode 100644 index 00000000..84ca2fe3 --- /dev/null +++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IPasswordPromptMessenger.cs @@ -0,0 +1,85 @@ +using System; +using Newtonsoft.Json; +using PepperDash.Essentials.Core; + +namespace PepperDash.Essentials.AppServer.Messengers +{ + /// + /// Messenger for devices implementing + /// + public class IPasswordPromptMessenger : MessengerBase + { + private readonly IPasswordPrompt _device; + + /// + /// Initializes a new instance of the class. + /// + public IPasswordPromptMessenger(string key, string messagePath, EssentialsDevice device) + : base(key, messagePath, device) + { + _device = device as IPasswordPrompt ?? throw new ArgumentException("device must implement IPasswordPrompt", nameof(device)); + _device.PasswordRequired += OnPasswordRequired; + } + + /// + protected override void RegisterActions() + { + base.RegisterActions(); + + AddAction("/password", (id, content) => + { + var msg = content.ToObject>(); + _device.SubmitPassword(msg.Value); + }); + } + + private void OnPasswordRequired(object sender, PasswordPromptEventArgs args) + { + PostEventMessage(new PasswordPromptEventMessage + { + Message = args.Message, + LastAttemptWasIncorrect = args.LastAttemptWasIncorrect, + LoginAttemptFailed = args.LoginAttemptFailed, + LoginAttemptCancelled = args.LoginAttemptCancelled, + EventType = "passwordPrompt" + }); + } + } + + /// + /// Base event message for video codec events + /// + public class VideoCodecBaseEventMessage : DeviceEventMessageBase + { + } + + /// + /// Event message sent when a password is required + /// + public class PasswordPromptEventMessage : VideoCodecBaseEventMessage + { + /// + /// Gets or sets the Message + /// + [JsonProperty("message", NullValueHandling = NullValueHandling.Ignore)] + public string Message { get; set; } + + /// + /// Gets or sets the LastAttemptWasIncorrect + /// + [JsonProperty("lastAttemptWasIncorrect", NullValueHandling = NullValueHandling.Ignore)] + public bool LastAttemptWasIncorrect { get; set; } + + /// + /// Gets or sets the LoginAttemptFailed + /// + [JsonProperty("loginAttemptFailed", NullValueHandling = NullValueHandling.Ignore)] + public bool LoginAttemptFailed { get; set; } + + /// + /// Gets or sets the LoginAttemptCancelled + /// + [JsonProperty("loginAttemptCancelled", NullValueHandling = NullValueHandling.Ignore)] + public bool LoginAttemptCancelled { get; set; } + } +} diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/VideoCodecBaseMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/VideoCodecBaseMessenger.cs index 28f1999f..285d3f3f 100644 --- a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/VideoCodecBaseMessenger.cs +++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/VideoCodecBaseMessenger.cs @@ -38,99 +38,6 @@ namespace PepperDash.Essentials.AppServer.Messengers Codec = codec ?? throw new ArgumentNullException("codec"); codec.CallStatusChange += Codec_CallStatusChange; codec.IsReadyChange += Codec_IsReadyChange; - - if (codec is IHasDirectory dirCodec) - { - dirCodec.DirectoryResultReturned += DirCodec_DirectoryResultReturned; - } - - if (codec is IHasCallHistory recCodec) - { - recCodec.CallHistory.RecentCallsListHasChanged += CallHistory_RecentCallsListHasChanged; - } - - if (codec is IPasswordPrompt pwPromptCodec) - { - pwPromptCodec.PasswordRequired += OnPasswordRequired; - } - } - - private void OnPasswordRequired(object sender, PasswordPromptEventArgs args) - { - var eventMsg = new PasswordPromptEventMessage - { - Message = args.Message, - LastAttemptWasIncorrect = args.LastAttemptWasIncorrect, - LoginAttemptFailed = args.LoginAttemptFailed, - LoginAttemptCancelled = args.LoginAttemptCancelled, - EventType = "passwordPrompt" - }; - - PostEventMessage(eventMsg); - } - - /// - /// - /// - /// - /// - private void CallHistory_RecentCallsListHasChanged(object sender, EventArgs e) - { - try - { - var state = new VideoCodecBaseStateMessage(); - - if (!(sender is CodecCallHistory codecCallHistory)) return; - var recents = codecCallHistory.RecentCalls; - - if (recents != null) - { - state.RecentCalls = recents; - - PostStatusMessage(state); - } - } - catch (Exception ex) - { - this.LogError(ex, "Error posting call history"); - } - } - - /// - /// - /// - /// - /// - protected virtual void DirCodec_DirectoryResultReturned(object sender, DirectoryEventArgs e) - { - if (Codec is IHasDirectory) - SendDirectory(e.Directory); - } - - /// - /// Posts the current directory - /// - protected void SendDirectory(CodecDirectory directory) - { - try - { - var state = new VideoCodecBaseStateMessage(); - - - if (Codec is IHasDirectory dirCodec) - { - this.LogVerbose("Sending Directory. Directory Item Count: {directoryItemCount}", directory.CurrentDirectoryResults.Count); - - //state.CurrentDirectory = PrefixDirectoryFolderItems(directory); - state.CurrentDirectory = directory; - - Task.Run(() => PostStatusMessage(state)); - } - } - catch (Exception ex) - { - this.LogError(ex, "Error sending directory"); - } } /// @@ -217,132 +124,6 @@ namespace PepperDash.Essentials.AppServer.Messengers Codec.SharingContentIsOnFeedback.OutputChange += SharingContentIsOnFeedback_OutputChange; Codec.SharingSourceFeedback.OutputChange += SharingSourceFeedback_OutputChange; - // Directory actions - if (Codec is IHasDirectory dirCodec) - { - AddAction("/getDirectory", (id, content) => GetDirectoryRoot()); - - AddAction("/directoryById", (id, content) => - { - var msg = content.ToObject>(); - GetDirectory(msg.Value); - }); - - AddAction("/directorySearch", (id, content) => - { - var msg = content.ToObject>(); - - GetDirectory(msg.Value); - }); - - AddAction("/directoryBack", (id, content) => GetPreviousDirectory()); - - dirCodec.PhonebookSyncState.InitialSyncCompleted += PhonebookSyncState_InitialSyncCompleted; - } - - // History actions - if (Codec is IHasCallHistory recCodec) - { - AddAction("/getCallHistory", (id, content) => PostCallHistory()); - } - if (Codec is IHasCodecCameras cameraCodec) - { - this.LogVerbose("Adding IHasCodecCameras Actions"); - - cameraCodec.CameraSelected += CameraCodec_CameraSelected; - - AddAction("/cameraSelect", (id, content) => - { - var msg = content.ToObject>(); - - cameraCodec.SelectCamera(msg.Value); - }); - - - MapCameraActions(); - - if (Codec is IHasCodecRoomPresets presetsCodec) - { - this.LogVerbose("Adding IHasCodecRoomPresets Actions"); - - presetsCodec.CodecRoomPresetsListHasChanged += PresetsCodec_CameraPresetsListHasChanged; - - AddAction("/cameraPreset", (id, content) => - { - var msg = content.ToObject>(); - - presetsCodec.CodecRoomPresetSelect(msg.Value); - }); - - AddAction("/cameraPresetStore", (id, content) => - { - var msg = content.ToObject(); - - presetsCodec.CodecRoomPresetStore(msg.ID, msg.Description); - }); - } - - if (Codec is IHasCameraAutoMode speakerTrackCodec) - { - this.LogVerbose("Adding IHasCameraAutoMode Actions"); - - speakerTrackCodec.CameraAutoModeIsOnFeedback.OutputChange += CameraAutoModeIsOnFeedback_OutputChange; - - AddAction("/cameraModeAuto", (id, content) => speakerTrackCodec.CameraAutoModeOn()); - - AddAction("/cameraModeManual", (id, content) => speakerTrackCodec.CameraAutoModeOff()); - } - - if (Codec is IHasCameraOff cameraOffCodec) - { - this.LogVerbose("Adding IHasCameraOff Actions"); - - cameraOffCodec.CameraIsOffFeedback.OutputChange += (CameraIsOffFeedback_OutputChange); - - AddAction("/cameraModeOff", (id, content) => cameraOffCodec.CameraOff()); - } - } - - - - if (Codec is IHasCodecSelfView selfViewCodec) - { - this.LogVerbose("Adding IHasCodecSelfView Actions"); - - AddAction("/cameraSelfView", (id, content) => selfViewCodec.SelfViewModeToggle()); - - selfViewCodec.SelfviewIsOnFeedback.OutputChange += new EventHandler(SelfviewIsOnFeedback_OutputChange); - } - - - if (Codec is IHasCodecLayouts layoutsCodec) - { - this.LogVerbose("Adding IHasCodecLayouts Actions"); - - AddAction("/cameraRemoteView", (id, content) => layoutsCodec.LocalLayoutToggle()); - - AddAction("/cameraLayout", (id, content) => layoutsCodec.LocalLayoutToggle()); - } - - if (Codec is IPasswordPrompt pwCodec) - { - this.LogVerbose("Adding IPasswordPrompt Actions"); - - AddAction("/password", (id, content) => - { - var msg = content.ToObject>(); - - pwCodec.SubmitPassword(msg.Value); - }); - } - - - if (Codec is IHasFarEndContentStatus farEndContentStatus) - { - farEndContentStatus.ReceivingContent.OutputChange += - (sender, args) => PostReceivingContent(args.BoolValue); - } - this.LogVerbose("Adding Privacy & Standby Actions"); AddAction("/privacyModeOn", (id, content) => Codec.PrivacyModeOn()); @@ -393,304 +174,6 @@ namespace PepperDash.Essentials.AppServer.Messengers } } - private void PhonebookSyncState_InitialSyncCompleted(object sender, EventArgs e) - { - try - { - var state = new VideoCodecBaseStateMessage - { - InitialPhonebookSyncComplete = true - }; - - PostStatusMessage(state); - } - catch (Exception ex) - { - this.LogError(ex, "Error posting phonebook sync state"); - } - } - - private void CameraIsOffFeedback_OutputChange(object sender, FeedbackEventArgs e) - { - PostCameraMode(); - } - - private void SelfviewIsOnFeedback_OutputChange(object sender, FeedbackEventArgs e) - { - PostCameraSelfView(); - } - - private void PresetsCodec_CameraPresetsListHasChanged(object sender, EventArgs e) - { - PostCameraPresets(); - } - - private void CameraAutoModeIsOnFeedback_OutputChange(object sender, FeedbackEventArgs e) - { - PostCameraMode(); - } - - - private void CameraCodec_CameraSelected(object sender, CameraSelectedEventArgs e) - { - try - { - MapCameraActions(); - PostSelectedCamera(); - } - catch (Exception ex) - { - this.LogError(ex, "Exception handling camera selected event"); - } - } - - /// - /// Maps the camera control actions to the current selected camera on the codec - /// - private void MapCameraActions() - { - if (Codec is IHasCamerasWithControls cameraCodec && cameraCodec.SelectedCamera != null) - { - RemoveAction("/cameraUp"); - RemoveAction("/cameraDown"); - RemoveAction("/cameraLeft"); - RemoveAction("/cameraRight"); - RemoveAction("/cameraZoomIn"); - RemoveAction("/cameraZoomOut"); - RemoveAction("/cameraHome"); - - if (cameraCodec.SelectedCamera is IHasCameraPtzControl camera) - { - AddAction("/cameraUp", (id, content) => HandleCameraPressAndHold(content, (b) => - { - if (b) - { - camera.TiltUp(); - return; - } - - camera.TiltStop(); - })); - - AddAction("/cameraDown", (id, content) => HandleCameraPressAndHold(content, (b) => - { - if (b) - { - camera.TiltDown(); - return; - } - - camera.TiltStop(); - })); - - AddAction("/cameraLeft", (id, content) => HandleCameraPressAndHold(content, (b) => - { - if (b) - { - camera.PanLeft(); - return; - } - - camera.PanStop(); - })); - - AddAction("/cameraRight", (id, content) => HandleCameraPressAndHold(content, (b) => - { - if (b) - { - camera.PanRight(); - return; - } - - camera.PanStop(); - })); - - AddAction("/cameraZoomIn", (id, content) => HandleCameraPressAndHold(content, (b) => - { - if (b) - { - camera.ZoomIn(); - return; - } - - camera.ZoomStop(); - })); - - AddAction("/cameraZoomOut", (id, content) => HandleCameraPressAndHold(content, (b) => - { - if (b) - { - camera.ZoomOut(); - return; - } - - camera.ZoomStop(); - })); - AddAction("/cameraHome", (id, content) => camera.PositionHome()); - - - RemoveAction("/cameraAutoFocus"); - RemoveAction("/cameraFocusNear"); - RemoveAction("/cameraFocusFar"); - - if (cameraCodec is IHasCameraFocusControl focusCamera) - { - AddAction("/cameraAutoFocus", (id, content) => focusCamera.TriggerAutoFocus()); - - AddAction("/cameraFocusNear", (id, content) => HandleCameraPressAndHold(content, (b) => - { - if (b) - { - focusCamera.FocusNear(); - return; - } - - focusCamera.FocusStop(); - })); - - AddAction("/cameraFocusFar", (id, content) => HandleCameraPressAndHold(content, (b) => - { - if (b) - { - focusCamera.FocusFar(); - return; - } - - focusCamera.FocusStop(); - })); - } - } - } - } - - private void HandleCameraPressAndHold(JToken content, Action cameraAction) - { - var state = content.ToObject>(); - - var timerHandler = PressAndHoldHandler.GetPressAndHoldHandler(state.Value); - if (timerHandler == null) - { - return; - } - - timerHandler(state.Value, cameraAction); - - cameraAction(state.Value.Equals("true", StringComparison.InvariantCultureIgnoreCase)); - } - - private string GetCameraMode() - { - string m = ""; - - if (Codec is IHasCameraAutoMode speakerTrackCodec) - { - m = speakerTrackCodec.CameraAutoModeIsOnFeedback.BoolValue - ? eCameraControlMode.Auto.ToString().ToLower() - : eCameraControlMode.Manual.ToString().ToLower(); - } - - if (Codec is IHasCameraOff cameraOffCodec) - { - if (cameraOffCodec.CameraIsOffFeedback.BoolValue) - m = eCameraControlMode.Off.ToString().ToLower(); - } - - return m; - } - - private void PostCallHistory() - { - try - { - var codec = (Codec as IHasCallHistory); - - if (codec != null) - { - var status = new VideoCodecBaseStateMessage(); - - var recents = codec.CallHistory.RecentCalls; - - if (recents != null) - { - status.RecentCalls = codec.CallHistory.RecentCalls; - - PostStatusMessage(status); - } - } - } - catch (Exception ex) - { - this.LogError(ex, "Error posting call history"); - } - } - - /// - /// Helper to grab a call with string ID - /// - /// - /// - private CodecActiveCallItem GetCallWithId(string id) - { - return Codec.ActiveCalls.FirstOrDefault(c => c.Id == id); - } - - /// - /// - /// - /// - private void GetDirectory(string id) - { - if (!(Codec is IHasDirectory dirCodec)) - { - return; - } - dirCodec.GetDirectoryFolderContents(id); - } - - /// - /// - /// - private void GetDirectoryRoot() - { - try - { - if (!(Codec is IHasDirectory dirCodec)) - { - // do something else? - return; - } - if (!dirCodec.PhonebookSyncState.InitialSyncComplete) - { - var state = new VideoCodecBaseStateMessage - { - InitialPhonebookSyncComplete = false - }; - - PostStatusMessage(state); - return; - } - - dirCodec.SetCurrentDirectoryToRoot(); - } - catch (Exception ex) - { - this.LogError(ex, "Error getting directory root"); - } - } - - /// - /// Requests the parent folder contents - /// - private void GetPreviousDirectory() - { - if (!(Codec is IHasDirectory dirCodec)) - { - return; - } - - dirCodec.GetDirectoryParentFolderContents(); - } - /// /// Handler for codec changes /// @@ -797,96 +280,32 @@ namespace PepperDash.Essentials.AppServer.Messengers Task.Run(() => PostStatusMessage(GetStatus(), id)); } - private void PostReceivingContent(bool receivingContent) - { - try - { - var state = new VideoCodecBaseStateMessage - { - ReceivingContent = receivingContent - }; - - PostStatusMessage(state); - } - catch (Exception ex) - { - this.LogError(ex, "Error posting receiving content"); - } - } - - private void PostCameraSelfView() - { - try - { - var status = new VideoCodecBaseStateMessage - { - CameraSelfViewIsOn = Codec is IHasCodecSelfView - && (Codec as IHasCodecSelfView).SelfviewIsOnFeedback.BoolValue - }; - - PostStatusMessage(status); - } - catch (Exception ex) - { - this.LogError(ex, "Error posting camera self view"); - } - } - /// - /// + /// Helper to grab a call with string ID /// - private void PostCameraMode() + private CodecActiveCallItem GetCallWithId(string id) { - try - { - var status = new VideoCodecBaseStateMessage - { - CameraMode = GetCameraMode() - }; - - PostStatusMessage(status); - } - catch (Exception ex) - { - this.LogError(ex, "Error posting camera mode"); - } + return Codec.ActiveCalls.FirstOrDefault(c => c.Id == id); } - private void PostSelectedCamera() + private string GetCameraMode() { - try - { - var camerasCodec = Codec as IHasCodecCameras; + string m = ""; - var status = new VideoCodecBaseStateMessage - { - Cameras = new CameraStatus() { SelectedCamera = GetSelectedCamera(camerasCodec) }, - Presets = GetCurrentPresets() - }; - - PostStatusMessage(status); - } - catch (Exception e) + if (Codec is IHasCameraAutoMode speakerTrackCodec) { - this.LogError(e, "Error posting selected camera"); + m = speakerTrackCodec.CameraAutoModeIsOnFeedback.BoolValue + ? eCameraControlMode.Auto.ToString().ToLower() + : eCameraControlMode.Manual.ToString().ToLower(); } - } - private void PostCameraPresets() - { - try + if (Codec is IHasCameraOff cameraOffCodec) { - var status = new VideoCodecBaseStateMessage - { - Presets = GetCurrentPresets() - }; + if (cameraOffCodec.CameraIsOffFeedback.BoolValue) + m = eCameraControlMode.Off.ToString().ToLower(); + } - PostStatusMessage(status); - } - catch (Exception e) - { - this.LogError(e, "Error posting camera presets"); - } + return m; } private Camera GetSelectedCamera(IHasCodecCameras camerasCodec) @@ -1145,45 +564,4 @@ namespace PepperDash.Essentials.AppServer.Messengers public bool? CanFocus { get; set; } } - - /// - /// Represents a VideoCodecBaseEventMessage - /// - public class VideoCodecBaseEventMessage : DeviceEventMessageBase - { - - } - - /// - /// Represents a PasswordPromptEventMessage - /// - public class PasswordPromptEventMessage : VideoCodecBaseEventMessage - { - /// - /// Gets or sets the Message - /// - [JsonProperty("message", NullValueHandling = NullValueHandling.Ignore)] - public string Message { get; set; } - - - /// - /// Gets or sets the LastAttemptWasIncorrect - /// - [JsonProperty("lastAttemptWasIncorrect", NullValueHandling = NullValueHandling.Ignore)] - public bool LastAttemptWasIncorrect { get; set; } - - - /// - /// Gets or sets the LoginAttemptFailed - /// - [JsonProperty("loginAttemptFailed", NullValueHandling = NullValueHandling.Ignore)] - public bool LoginAttemptFailed { get; set; } - - - /// - /// Gets or sets the LoginAttemptCancelled - /// - [JsonProperty("loginAttemptCancelled", NullValueHandling = NullValueHandling.Ignore)] - public bool LoginAttemptCancelled { get; set; } - } } \ No newline at end of file diff --git a/src/PepperDash.Essentials.MobileControl/MessengerFactoryRegistry.cs b/src/PepperDash.Essentials.MobileControl/MessengerFactoryRegistry.cs index 060e594f..96bc8ee5 100644 --- a/src/PepperDash.Essentials.MobileControl/MessengerFactoryRegistry.cs +++ b/src/PepperDash.Essentials.MobileControl/MessengerFactoryRegistry.cs @@ -10,6 +10,7 @@ using PepperDash.Essentials.Core.Routing; using PepperDash.Essentials.Core.Shades; using PepperDash.Essentials.Devices.Common.AudioCodec; using PepperDash.Essentials.Devices.Common.Cameras; +using PepperDash.Essentials.Devices.Common.Codec; using PepperDash.Essentials.Devices.Common.Displays; using PepperDash.Essentials.Devices.Common.SoftCodec; using PepperDash.Essentials.Devices.Common.VideoCodec; @@ -95,7 +96,7 @@ namespace PepperDash.Essentials ), new MessengerFactoryEntry( typeof(IHasCamerasWithControls), - (d, mp, ck) => new IHasCamerasWithControlMessenger( + (d, mp, ck) => new IHasCamerasWithControlsMessenger( $"{d.Key}-cameras-{ck}", mp, (IHasCamerasWithControls)d) ), @@ -164,6 +165,42 @@ namespace PepperDash.Essentials (d, mp, ck) => new VideoCodecBaseMessenger( $"{d.Key}-videoCodec-{ck}", (VideoCodecBase)d, mp) ), + new MessengerFactoryEntry( + typeof(IHasDirectory), + (d, mp, ck) => new IHasDirectoryMessenger( + $"{d.Key}-directory-{ck}", mp, d) + ), + new MessengerFactoryEntry( + typeof(IHasCallHistory), + (d, mp, ck) => new IHasCallHistoryMessenger( + $"{d.Key}-callHistory-{ck}", mp, d) + ), + new MessengerFactoryEntry( + typeof(IPasswordPrompt), + (d, mp, ck) => new IPasswordPromptMessenger( + $"{d.Key}-passwordPrompt-{ck}", mp, d) + ), + new MessengerFactoryEntry( + typeof(IHasCodecCameras), + (d, mp, ck) => new IHasCodecCamerasMessenger( + $"{d.Key}-codecCameras-{ck}", mp, (VideoCodecBase)d), + predicate: d => d is VideoCodecBase && d is IHasCodecCameras + ), + new MessengerFactoryEntry( + typeof(IHasCodecSelfView), + (d, mp, ck) => new IHasCodecSelfViewMessenger( + $"{d.Key}-selfView-{ck}", mp, d) + ), + new MessengerFactoryEntry( + typeof(IHasCodecLayouts), + (d, mp, ck) => new IHasCodecLayoutsMessenger( + $"{d.Key}-codecLayouts-{ck}", mp, d) + ), + new MessengerFactoryEntry( + typeof(IHasFarEndContentStatus), + (d, mp, ck) => new IHasFarEndContentStatusMessenger( + $"{d.Key}-farEndContent-{ck}", mp, d) + ), new MessengerFactoryEntry( typeof(IDialerCallStatus), (d, mp, ck) => new IDialerCallStatusMessenger( diff --git a/src/PepperDash.Essentials/ControlSystem.cs b/src/PepperDash.Essentials/ControlSystem.cs index 1539fcf0..701a0ec4 100644 --- a/src/PepperDash.Essentials/ControlSystem.cs +++ b/src/PepperDash.Essentials/ControlSystem.cs @@ -9,6 +9,7 @@ using Crestron.SimplSharp; using Crestron.SimplSharp.CrestronIO; using Crestron.SimplSharpPro; using Crestron.SimplSharpPro.Diagnostics; +using Crestron.SimplSharpPro.UC; using PepperDash.Core; using PepperDash.Core.Abstractions; using PepperDash.Core.Adapters;