From 37cea8a11c7e3e18eb2fba09f08d1d7b14643a04 Mon Sep 17 00:00:00 2001 From: Neil Dorin Date: Tue, 7 Oct 2025 11:52:07 -0600 Subject: [PATCH 1/7] fix: Refactor camera control interfaces and event arguments Significantly restructure camera control interfaces and event arguments. Removed obsolete interfaces like `IHasCameras` and `CameraSelectedEventArgs`, and introduced generic event argument classes for improved type safety. Added `IHasCamerasWithControls` for better management of camera controls. Corrected the `IHasCameraMuteWithUnmuteReqeust` interface name. Reintroduced the `eCameraControlMode` enum to define camera control modes. These changes enhance the organization, clarity, and functionality of the camera control system. --- .../Cameras/CameraControl.cs | 326 ------------------ .../Interfaces/CameraSelectedEventArgs.cs | 49 +++ .../Cameras/Interfaces/IAmFarEndCamera.cs | 12 + .../Cameras/Interfaces/IHasAutoFocusMode.cs | 24 ++ .../Cameras/Interfaces/IHasCameraAutoMode.cs | 31 ++ .../Cameras/Interfaces/IHasCameraControls.cs | 13 + .../Interfaces/IHasCameraFocusControl.cs | 29 ++ .../Cameras/Interfaces/IHasCameraMute.cs | 31 ++ .../IHasCameraMuteWithUnmuteRequest.cs | 16 + .../Cameras/Interfaces/IHasCameraOff.cs | 27 ++ .../Interfaces/IHasCameraPanControl.cs | 24 ++ .../Interfaces/IHasCameraPtzControl.cs | 14 + .../Interfaces/IHasCameraTiltControl.cs | 24 ++ .../Interfaces/IHasCameraZoomControl.cs | 24 ++ .../Cameras/Interfaces/IHasCameras.cs | 40 +++ .../Interfaces/IHasCamerasWithControls.cs | 39 +++ .../Cameras/Interfaces/IHasCodecCameras.cs | 13 + .../Interfaces/IHasFarEndCameraControl.cs | 23 ++ .../Cameras/Interfaces/eCameraControlMode.cs | 24 ++ 19 files changed, 457 insertions(+), 326 deletions(-) delete mode 100644 src/PepperDash.Essentials.Devices.Common/Cameras/CameraControl.cs create mode 100644 src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/CameraSelectedEventArgs.cs create mode 100644 src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/IAmFarEndCamera.cs create mode 100644 src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/IHasAutoFocusMode.cs create mode 100644 src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/IHasCameraAutoMode.cs create mode 100644 src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/IHasCameraControls.cs create mode 100644 src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/IHasCameraFocusControl.cs create mode 100644 src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/IHasCameraMute.cs create mode 100644 src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/IHasCameraMuteWithUnmuteRequest.cs create mode 100644 src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/IHasCameraOff.cs create mode 100644 src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/IHasCameraPanControl.cs create mode 100644 src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/IHasCameraPtzControl.cs create mode 100644 src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/IHasCameraTiltControl.cs create mode 100644 src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/IHasCameraZoomControl.cs create mode 100644 src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/IHasCameras.cs create mode 100644 src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/IHasCamerasWithControls.cs create mode 100644 src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/IHasCodecCameras.cs create mode 100644 src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/IHasFarEndCameraControl.cs create mode 100644 src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/eCameraControlMode.cs diff --git a/src/PepperDash.Essentials.Devices.Common/Cameras/CameraControl.cs b/src/PepperDash.Essentials.Devices.Common/Cameras/CameraControl.cs deleted file mode 100644 index cec2f58b..00000000 --- a/src/PepperDash.Essentials.Devices.Common/Cameras/CameraControl.cs +++ /dev/null @@ -1,326 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Crestron.SimplSharp; -using PepperDash.Core; -using PepperDash.Essentials.Core; - -namespace PepperDash.Essentials.Devices.Common.Cameras -{ - /// - /// Enum for camera control modes - /// - public enum eCameraControlMode - { - /// - /// Manual control mode, where the camera is controlled directly by the user or system - /// - Manual = 0, - /// - /// Off control mode, where the camera is turned off or disabled - /// - Off, - /// - /// Auto control mode, where the camera automatically adjusts settings based on the environment or conditions - /// - Auto - } - - - /// - /// Interface for devices that have cameras - /// - public interface IHasCameras : IKeyName - { - /// - /// Event that is raised when a camera is selected - /// - event EventHandler CameraSelected; - - /// - /// List of cameras on the device. This should be a list of CameraBase objects - /// - List Cameras { get; } - - /// - /// The currently selected camera. This should be a CameraBase object - /// - CameraBase SelectedCamera { get; } - - /// - /// Feedback that indicates the currently selected camera - /// - StringFeedback SelectedCameraFeedback { get; } - - /// - /// Selects a camera from the list of available cameras based on the provided key. - /// - /// The unique identifier or name of the camera to select. - void SelectCamera(string key); - } - - /// - /// Defines the contract for IHasCodecCameras - /// - public interface IHasCodecCameras : IHasCameras, IHasFarEndCameraControl - { - - } - - /// - /// To be implmented on codecs that can disable their camera(s) to blank the near end video - /// - public interface IHasCameraOff - { - /// - /// Feedback that indicates whether the camera is off - /// - BoolFeedback CameraIsOffFeedback { get; } - - /// - /// Turns the camera off, blanking the near end video - /// - void CameraOff(); - } - - /// - /// Describes the ability to mute and unmute camera video - /// - public interface IHasCameraMute - { - /// - /// Feedback that indicates whether the camera is muted - /// - BoolFeedback CameraIsMutedFeedback { get; } - - /// - /// Mutes the camera video, preventing it from being sent to the far end - /// - void CameraMuteOn(); - - /// - /// Unmutes the camera video, allowing it to be sent to the far end - /// - void CameraMuteOff(); - - /// - /// Toggles the camera mute state. If the camera is muted, it will be unmuted, and vice versa. - /// - void CameraMuteToggle(); - } - - /// - /// Interface for devices that can mute and unmute their camera video, with an event for unmute requests - /// - public interface IHasCameraMuteWithUnmuteReqeust : IHasCameraMute - { - /// - /// Event that is raised when a video unmute is requested, typically by the far end - /// - event EventHandler VideoUnmuteRequested; - } - - /// - /// Event arguments for the CameraSelected event - /// - public class CameraSelectedEventArgs : EventArgs - { - /// - /// Gets or sets the SelectedCamera - /// - public CameraBase SelectedCamera { get; private set; } - - /// - /// Constructor for CameraSelectedEventArgs - /// - /// - public CameraSelectedEventArgs(CameraBase camera) - { - SelectedCamera = camera; - } - } - - /// - /// Interface for devices that have a far end camera control - /// - public interface IHasFarEndCameraControl - { - /// - /// Gets the far end camera, which is typically a CameraBase object that represents the camera at the far end of a call - /// - CameraBase FarEndCamera { get; } - - /// - /// Feedback that indicates whether the far end camera is being controlled - /// - BoolFeedback ControllingFarEndCameraFeedback { get; } - - } - - /// - /// Defines the contract for IAmFarEndCamera - /// - public interface IAmFarEndCamera - { - - } - - /// - /// Interface for devices that have camera controls - /// - public interface IHasCameraControls - { - } - - /// - /// Defines the contract for IHasCameraPtzControl - /// - public interface IHasCameraPtzControl : IHasCameraPanControl, IHasCameraTiltControl, IHasCameraZoomControl - { - /// - /// Resets the camera position - /// - void PositionHome(); - } - - /// - /// Interface for camera pan control - /// - public interface IHasCameraPanControl : IHasCameraControls - { - /// - /// Pans the camera left - /// - void PanLeft(); - - /// - /// Pans the camera right - /// - void PanRight(); - - /// - /// Stops the camera pan movement - /// - void PanStop(); - } - - /// - /// Defines the contract for IHasCameraTiltControl - /// - public interface IHasCameraTiltControl : IHasCameraControls - { - /// - /// Tilts the camera down - /// - void TiltDown(); - - /// - /// Tilts the camera up - /// - void TiltUp(); - - /// - /// Stops the camera tilt movement - /// - void TiltStop(); - } - - /// - /// Defines the contract for IHasCameraZoomControl - /// - public interface IHasCameraZoomControl : IHasCameraControls - { - /// - /// Zooms the camera in - /// - void ZoomIn(); - - /// - /// Zooms the camera out - /// - void ZoomOut(); - - /// - /// Stops the camera zoom movement - /// - void ZoomStop(); - } - - /// - /// Defines the contract for IHasCameraFocusControl - /// - public interface IHasCameraFocusControl : IHasCameraControls - { - /// - /// Focuses the camera near - /// - void FocusNear(); - - /// - /// Focuses the camera far - /// - void FocusFar(); - - /// - /// Stops the camera focus movement - /// - void FocusStop(); - - /// - /// Triggers the camera's auto focus functionality, if available. - /// - void TriggerAutoFocus(); - } - - /// - /// Interface for devices that have auto focus mode control - /// - public interface IHasAutoFocusMode - { - /// - /// Sets the focus mode to auto or manual, or toggles between them. - /// - void SetFocusModeAuto(); - - /// - /// Sets the focus mode to manual, allowing for manual focus adjustments. - /// - void SetFocusModeManual(); - - /// - /// Toggles the focus mode between auto and manual. - /// - void ToggleFocusMode(); - } - - /// - /// Interface for devices that have camera auto mode control - /// - public interface IHasCameraAutoMode : IHasCameraControls - { - /// - /// Enables or disables the camera's auto mode, which may include automatic adjustments for focus, exposure, and other settings. - /// - void CameraAutoModeOn(); - - /// - /// Disables the camera's auto mode, allowing for manual control of camera settings. - /// - void CameraAutoModeOff(); - - /// - /// Toggles the camera's auto mode state. If the camera is in auto mode, it will switch to manual mode, and vice versa. - /// - void CameraAutoModeToggle(); - - /// - /// Feedback that indicates whether the camera's auto mode is currently enabled. - /// - BoolFeedback CameraAutoModeIsOnFeedback { get; } - } - - - - -} \ No newline at end of file diff --git a/src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/CameraSelectedEventArgs.cs b/src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/CameraSelectedEventArgs.cs new file mode 100644 index 00000000..bb2a85a2 --- /dev/null +++ b/src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/CameraSelectedEventArgs.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace PepperDash.Essentials.Devices.Common.Cameras +{ + /// + /// Event arguments for the CameraSelected event + /// + [Obsolete("Use CameraSelectedEventArgs instead. This class will be removed in a future version")] + public class CameraSelectedEventArgs : EventArgs + { + /// Gets or sets the SelectedCamera + /// + public CameraBase SelectedCamera { get; private set; } + + /// + /// Constructor for CameraSelectedEventArgs + /// + /// + public CameraSelectedEventArgs(CameraBase camera) + { + SelectedCamera = camera; + } + } + + /// + /// Event arguments for the CameraSelected event + /// + /// + public class CameraSelectedEventArgs : EventArgs + { + /// + /// Gets or sets the SelectedCamera + /// + public T SelectedCamera { get; private set; } + + /// + /// Constructor for CameraSelectedEventArgs + /// + /// + public CameraSelectedEventArgs(T camera) + { + SelectedCamera = camera; + } + } +} diff --git a/src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/IAmFarEndCamera.cs b/src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/IAmFarEndCamera.cs new file mode 100644 index 00000000..a8e96664 --- /dev/null +++ b/src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/IAmFarEndCamera.cs @@ -0,0 +1,12 @@ +using PepperDash.Core; + +namespace PepperDash.Essentials.Devices.Common.Cameras +{ + /// + /// Defines the contract for IAmFarEndCamera + /// + public interface IAmFarEndCamera : IKeyName + { + + } +} diff --git a/src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/IHasAutoFocusMode.cs b/src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/IHasAutoFocusMode.cs new file mode 100644 index 00000000..0dbd5f52 --- /dev/null +++ b/src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/IHasAutoFocusMode.cs @@ -0,0 +1,24 @@ + +namespace PepperDash.Essentials.Devices.Common.Cameras +{ + /// + /// Interface for devices that have auto focus mode control + /// + public interface IHasAutoFocusMode : IHasCameraControls + { + /// + /// Sets the focus mode to auto or manual, or toggles between them. + /// + void SetFocusModeAuto(); + + /// + /// Sets the focus mode to manual, allowing for manual focus adjustments. + /// + void SetFocusModeManual(); + + /// + /// Toggles the focus mode between auto and manual. + /// + void ToggleFocusMode(); + } +} diff --git a/src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/IHasCameraAutoMode.cs b/src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/IHasCameraAutoMode.cs new file mode 100644 index 00000000..9eeb1f40 --- /dev/null +++ b/src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/IHasCameraAutoMode.cs @@ -0,0 +1,31 @@ +using PepperDash.Essentials.Core; + +namespace PepperDash.Essentials.Devices.Common.Cameras +{ + + /// + /// Interface for devices that have camera auto mode control + /// + public interface IHasCameraAutoMode : IHasCameraControls + { + /// + /// Enables or disables the camera's auto mode, which may include automatic adjustments for focus, exposure, and other settings. + /// + void CameraAutoModeOn(); + + /// + /// Disables the camera's auto mode, allowing for manual control of camera settings. + /// + void CameraAutoModeOff(); + + /// + /// Toggles the camera's auto mode state. If the camera is in auto mode, it will switch to manual mode, and vice versa. + /// + void CameraAutoModeToggle(); + + /// + /// Feedback that indicates whether the camera's auto mode is currently enabled. + /// + BoolFeedback CameraAutoModeIsOnFeedback { get; } + } +} diff --git a/src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/IHasCameraControls.cs b/src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/IHasCameraControls.cs new file mode 100644 index 00000000..a4ed5937 --- /dev/null +++ b/src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/IHasCameraControls.cs @@ -0,0 +1,13 @@ +using PepperDash.Core; + + +namespace PepperDash.Essentials.Devices.Common.Cameras +{ + + /// + /// Interface for devices that have camera controls + /// + public interface IHasCameraControls : IKeyName + { + } +} diff --git a/src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/IHasCameraFocusControl.cs b/src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/IHasCameraFocusControl.cs new file mode 100644 index 00000000..bf266d56 --- /dev/null +++ b/src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/IHasCameraFocusControl.cs @@ -0,0 +1,29 @@ + +namespace PepperDash.Essentials.Devices.Common.Cameras +{ + /// + /// Defines the contract for IHasCameraFocusControl + /// + public interface IHasCameraFocusControl : IHasCameraControls + { + /// + /// Focuses the camera near + /// + void FocusNear(); + + /// + /// Focuses the camera far + /// + void FocusFar(); + + /// + /// Stops the camera focus movement + /// + void FocusStop(); + + /// + /// Triggers the camera's auto focus functionality, if available. + /// + void TriggerAutoFocus(); + } +} diff --git a/src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/IHasCameraMute.cs b/src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/IHasCameraMute.cs new file mode 100644 index 00000000..cae6f5ef --- /dev/null +++ b/src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/IHasCameraMute.cs @@ -0,0 +1,31 @@ +using PepperDash.Core; +using PepperDash.Essentials.Core; + +namespace PepperDash.Essentials.Devices.Common.Cameras +{ + /// + /// Describes the ability to mute and unmute camera video + /// + public interface IHasCameraMute : IKeyName + { + /// + /// Feedback that indicates whether the camera is muted + /// + BoolFeedback CameraIsMutedFeedback { get; } + + /// + /// Mutes the camera video, preventing it from being sent to the far end + /// + void CameraMuteOn(); + + /// + /// Unmutes the camera video, allowing it to be sent to the far end + /// + void CameraMuteOff(); + + /// + /// Toggles the camera mute state. If the camera is muted, it will be unmuted, and vice versa. + /// + void CameraMuteToggle(); + } +} diff --git a/src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/IHasCameraMuteWithUnmuteRequest.cs b/src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/IHasCameraMuteWithUnmuteRequest.cs new file mode 100644 index 00000000..5426ea2b --- /dev/null +++ b/src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/IHasCameraMuteWithUnmuteRequest.cs @@ -0,0 +1,16 @@ +using System; + + +namespace PepperDash.Essentials.Devices.Common.Cameras +{ + /// + /// Interface for devices that can mute and unmute their camera video, with an event for unmute requests + /// + public interface IHasCameraMuteWithUnmuteReqeust : IHasCameraMute + { + /// + /// Event that is raised when a video unmute is requested, typically by the far end + /// + event EventHandler VideoUnmuteRequested; + } +} diff --git a/src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/IHasCameraOff.cs b/src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/IHasCameraOff.cs new file mode 100644 index 00000000..6dc451c1 --- /dev/null +++ b/src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/IHasCameraOff.cs @@ -0,0 +1,27 @@ +using PepperDash.Essentials.Core; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace PepperDash.Essentials.Devices.Common.Cameras +{ + + + /// + /// To be implmented on codecs that can disable their camera(s) to blank the near end video + /// + public interface IHasCameraOff : IHasCameraControls + { + /// + /// Feedback that indicates whether the camera is off + /// + BoolFeedback CameraIsOffFeedback { get; } + + /// + /// Turns the camera off, blanking the near end video + /// + void CameraOff(); + } +} diff --git a/src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/IHasCameraPanControl.cs b/src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/IHasCameraPanControl.cs new file mode 100644 index 00000000..507dc555 --- /dev/null +++ b/src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/IHasCameraPanControl.cs @@ -0,0 +1,24 @@ + +namespace PepperDash.Essentials.Devices.Common.Cameras +{ + /// + /// Interface for camera pan control + /// + public interface IHasCameraPanControl : IHasCameraControls + { + /// + /// Pans the camera left + /// + void PanLeft(); + + /// + /// Pans the camera right + /// + void PanRight(); + + /// + /// Stops the camera pan movement + /// + void PanStop(); + } +} diff --git a/src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/IHasCameraPtzControl.cs b/src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/IHasCameraPtzControl.cs new file mode 100644 index 00000000..de2b52dd --- /dev/null +++ b/src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/IHasCameraPtzControl.cs @@ -0,0 +1,14 @@ + +namespace PepperDash.Essentials.Devices.Common.Cameras +{ + /// + /// Defines the contract for IHasCameraPtzControl + /// + public interface IHasCameraPtzControl : IHasCameraPanControl, IHasCameraTiltControl, IHasCameraZoomControl + { + /// + /// Resets the camera position + /// + void PositionHome(); + } +} diff --git a/src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/IHasCameraTiltControl.cs b/src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/IHasCameraTiltControl.cs new file mode 100644 index 00000000..87dc1751 --- /dev/null +++ b/src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/IHasCameraTiltControl.cs @@ -0,0 +1,24 @@ + +namespace PepperDash.Essentials.Devices.Common.Cameras +{ + /// + /// Defines the contract for IHasCameraTiltControl + /// + public interface IHasCameraTiltControl : IHasCameraControls + { + /// + /// Tilts the camera down + /// + void TiltDown(); + + /// + /// Tilts the camera up + /// + void TiltUp(); + + /// + /// Stops the camera tilt movement + /// + void TiltStop(); + } +} diff --git a/src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/IHasCameraZoomControl.cs b/src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/IHasCameraZoomControl.cs new file mode 100644 index 00000000..72bf16c4 --- /dev/null +++ b/src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/IHasCameraZoomControl.cs @@ -0,0 +1,24 @@ + +namespace PepperDash.Essentials.Devices.Common.Cameras +{ + /// + /// Defines the contract for IHasCameraZoomControl + /// + public interface IHasCameraZoomControl : IHasCameraControls + { + /// + /// Zooms the camera in + /// + void ZoomIn(); + + /// + /// Zooms the camera out + /// + void ZoomOut(); + + /// + /// Stops the camera zoom movement + /// + void ZoomStop(); + } +} diff --git a/src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/IHasCameras.cs b/src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/IHasCameras.cs new file mode 100644 index 00000000..f242f4f4 --- /dev/null +++ b/src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/IHasCameras.cs @@ -0,0 +1,40 @@ +using PepperDash.Core; +using PepperDash.Essentials.Core; +using System; +using System.Collections.Generic; + +namespace PepperDash.Essentials.Devices.Common.Cameras +{ + /// + /// Interface for devices that have cameras + /// + [Obsolete("Use IHasCamerasWithControls instead. This interface will be removed in a future version")] + public interface IHasCameras : IKeyName + { + /// + /// Event that is raised when a camera is selected + /// + event EventHandler CameraSelected; + + /// + /// List of cameras on the device. This should be a list of CameraBase objects + /// + List Cameras { get; } + + /// + /// The currently selected camera. This should be a CameraBase object + /// + CameraBase SelectedCamera { get; } + + /// + /// Feedback that indicates the currently selected camera + /// + StringFeedback SelectedCameraFeedback { get; } + + /// + /// Selects a camera from the list of available cameras based on the provided key. + /// + /// The unique identifier or name of the camera to select. + void SelectCamera(string key); + } +} diff --git a/src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/IHasCamerasWithControls.cs b/src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/IHasCamerasWithControls.cs new file mode 100644 index 00000000..a842364d --- /dev/null +++ b/src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/IHasCamerasWithControls.cs @@ -0,0 +1,39 @@ +using PepperDash.Core; +using PepperDash.Essentials.Core; +using System; +using System.Collections.Generic; + +namespace PepperDash.Essentials.Devices.Common.Cameras +{ + /// + /// Interface for devices that have cameras with controls + /// + public interface IHasCamerasWithControls : IKeyName, IKeyed + { + /// + /// List of cameras on the device. This should be a list of IHasCameraControls objects + /// + List Cameras { get; } + + /// + /// The currently selected camera. This should be an IHasCameraControls object + /// + IHasCameraControls SelectedCamera { get; } + + /// + /// Feedback that indicates the currently selected camera + /// + StringFeedback SelectedCameraFeedback { get; } + + /// + /// Event that is raised when a camera is selected + /// + event EventHandler> CameraSelected; + + /// + /// Selects a camera from the list of available cameras based on the provided key. + /// + /// + void SelectCamera(string key); + } +} diff --git a/src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/IHasCodecCameras.cs b/src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/IHasCodecCameras.cs new file mode 100644 index 00000000..01cdf24e --- /dev/null +++ b/src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/IHasCodecCameras.cs @@ -0,0 +1,13 @@ + +namespace PepperDash.Essentials.Devices.Common.Cameras +{ + + /// + /// Defines the contract for IHasCodecCameras + /// + public interface IHasCodecCameras : IHasCameras, IHasFarEndCameraControl + { + + } + +} diff --git a/src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/IHasFarEndCameraControl.cs b/src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/IHasFarEndCameraControl.cs new file mode 100644 index 00000000..10a4ba69 --- /dev/null +++ b/src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/IHasFarEndCameraControl.cs @@ -0,0 +1,23 @@ +using PepperDash.Core; +using PepperDash.Essentials.Core; + + +namespace PepperDash.Essentials.Devices.Common.Cameras +{ + /// + /// Interface for devices that have a far end camera control + /// + public interface IHasFarEndCameraControl : IKeyName + { + /// + /// Gets the far end camera, which is typically a CameraBase object that represents the camera at the far end of a call + /// + CameraBase FarEndCamera { get; } + + /// + /// Feedback that indicates whether the far end camera is being controlled + /// + BoolFeedback ControllingFarEndCameraFeedback { get; } + + } +} diff --git a/src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/eCameraControlMode.cs b/src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/eCameraControlMode.cs new file mode 100644 index 00000000..c966ac41 --- /dev/null +++ b/src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/eCameraControlMode.cs @@ -0,0 +1,24 @@ + + +namespace PepperDash.Essentials.Devices.Common.Cameras +{ + /// + /// Enum for camera control modes + /// + public enum eCameraControlMode + { + /// + /// Manual control mode, where the camera is controlled directly by the user or system + /// + Manual = 0, + /// + /// Off control mode, where the camera is turned off or disabled + /// + Off, + /// + /// Auto control mode, where the camera automatically adjusts settings based on the environment or conditions + /// + Auto + } + +} From 82b5dc96c13e1ab89659b4cd161ca5bd4b8ed46b Mon Sep 17 00:00:00 2001 From: Neil Dorin Date: Tue, 7 Oct 2025 12:00:01 -0600 Subject: [PATCH 2/7] feat: Deprecate IHasCamerasMessenger; introduce new controls Mark IHasCamerasMessenger as obsolete and replace it with IHasCamerasWithControlMessenger, which adds functionality for devices with camera controls. A new state message class, IHasCamerasWithControlsStateMessage, has been added to encapsulate camera state. Update MobileControlSystemController to use the new messenger implementation. --- .../Messengers/IHasCamerasMessenger.cs | 11 +-- .../IHasCamerasWithControlMessenger.cs | 99 +++++++++++++++++++ .../MobileControlSystemController.cs | 13 +++ 3 files changed, 116 insertions(+), 7 deletions(-) create mode 100644 src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHasCamerasWithControlMessenger.cs diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHasCamerasMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHasCamerasMessenger.cs index 7099e52c..00c2c2fe 100644 --- a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHasCamerasMessenger.cs +++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHasCamerasMessenger.cs @@ -1,17 +1,14 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; +using Newtonsoft.Json; using PepperDash.Essentials.Devices.Common.Cameras; +using System; +using System.Collections.Generic; namespace PepperDash.Essentials.AppServer.Messengers { /// /// Messenger for devices that implement the IHasCameras interface. /// + [Obsolete("Use IHasCamerasWithControlsMessenger instead. This class will be removed in a future version")] public class IHasCamerasMessenger : MessengerBase { /// diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHasCamerasWithControlMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHasCamerasWithControlMessenger.cs new file mode 100644 index 00000000..d94cc4c4 --- /dev/null +++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHasCamerasWithControlMessenger.cs @@ -0,0 +1,99 @@ +using Newtonsoft.Json; +using PepperDash.Essentials.Devices.Common.Cameras; +using System; +using System.Collections.Generic; + +namespace PepperDash.Essentials.AppServer.Messengers +{ + /// + /// Messenger for devices that implement the IHasCameras interface. + /// + public class IHasCamerasWithControlMessenger : MessengerBase + { + /// + /// Device being bridged that implements IHasCameras interface. + /// + public IHasCamerasWithControls CameraController { get; private set; } + + /// + /// Messenger for devices that implement IHasCameras interface. + /// + /// + /// + /// + /// + public IHasCamerasWithControlMessenger(string key, string messagePath, IHasCamerasWithControls cameraController) + : base(key, messagePath, cameraController) + { + CameraController = cameraController ?? throw new ArgumentNullException("cameraController"); + CameraController.CameraSelected += CameraController_CameraSelected; + } + + private void CameraController_CameraSelected(object sender, CameraSelectedEventArgs e) + { + PostStatusMessage(new IHasCamerasWithControlsStateMessage + { + SelectedCamera = e.SelectedCamera + }); + } + + /// + /// Registers the actions for this messenger. + /// + /// + protected override void RegisterActions() + { + base.RegisterActions(); + + AddAction("/fullStatus", (id, context) => SendFullStatus(id)); + + AddAction("/cameraListStatus", (id, content) => SendFullStatus(id)); + + AddAction("/selectCamera", (id, content) => + { + var cameraKey = content?.ToObject(); + + if (!string.IsNullOrEmpty(cameraKey)) + { + CameraController.SelectCamera(cameraKey); + } + else + { + throw new ArgumentException("Content must be a string representing the camera key"); + } + }); + } + + private void SendFullStatus(string clientId) + { + var state = new IHasCamerasWithControlsStateMessage + { + CameraList = CameraController.Cameras, + SelectedCamera = CameraController.SelectedCamera + }; + + PostStatusMessage(state, clientId); + } + + + } + + /// + /// State message for devices that implement the IHasCameras interface. + /// + public class IHasCamerasWithControlsStateMessage : DeviceStateMessageBase + { + /// + /// List of cameras available in the device. + /// + [JsonProperty("cameraList", NullValueHandling = NullValueHandling.Ignore)] + public List CameraList { get; set; } + + /// + /// The currently selected camera on the device. + /// + [JsonProperty("selectedCamera", NullValueHandling = NullValueHandling.Ignore)] + public IHasCameraControls SelectedCamera { get; set; } + + } +} diff --git a/src/PepperDash.Essentials.MobileControl/MobileControlSystemController.cs b/src/PepperDash.Essentials.MobileControl/MobileControlSystemController.cs index 0579c1f6..a4914c10 100644 --- a/src/PepperDash.Essentials.MobileControl/MobileControlSystemController.cs +++ b/src/PepperDash.Essentials.MobileControl/MobileControlSystemController.cs @@ -975,6 +975,19 @@ namespace PepperDash.Essentials messengerAdded = true; } + if (device is IHasCamerasWithControls cameras2) + { + this.LogVerbose("Adding IHasCamerasMessenger for {deviceKey}", device.Key + ); + var messenger = new IHasCamerasWithControlMessenger( + $"{device.Key}-cameras-{Key}", + $"/device/{device.Key}", + cameras2 + ); + AddDefaultDeviceMessenger(messenger); + messengerAdded = true; + } + this.LogVerbose("Trying to cast to generic device for device: {key}", device.Key); if (device is EssentialsDevice) From 2928c5cf94b25284352d81f83bdcd6b3c314ab07 Mon Sep 17 00:00:00 2001 From: Neil Dorin Date: Tue, 7 Oct 2025 15:37:31 -0600 Subject: [PATCH 3/7] feat: Enhance camera capabilities and messaging structure - Introduced `ICameraCapabilities` interface and `CameraCapabilities` class for defining camera features like pan, tilt, zoom, and focus. - Modified `IHasCameras` interface to include a list of `IHasCameraControls` objects for improved camera management. - Refactored `CameraBaseMessenger` to be generic, enhancing flexibility and type safety. - Updated `SendCameraFullMessageObject` to include detailed camera capabilities in status messages. - Added `CameraStateMessage` class to encapsulate camera state, including control support and capabilities. - Updated `IHasCamerasWithControlMessenger` to use `IKeyName` for camera list and selected camera properties, improving type consistency. - Enhanced `MobileControlSystemController` to manage devices implementing `IHasCameraControls`, creating appropriate messengers for different device types. --- .../Cameras/Interfaces/ICameraCapabilities.cs | 86 ++++++++++++++ .../Cameras/Interfaces/IHasCameras.cs | 3 +- .../Interfaces/IHasCamerasWithControls.cs | 1 + .../Messengers/CameraBaseMessenger.cs | 111 ++++++++++++++++-- .../IHasCamerasWithControlMessenger.cs | 15 +-- .../MobileControlSystemController.cs | 22 +++- 6 files changed, 214 insertions(+), 24 deletions(-) create mode 100644 src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/ICameraCapabilities.cs diff --git a/src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/ICameraCapabilities.cs b/src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/ICameraCapabilities.cs new file mode 100644 index 00000000..cf39a95c --- /dev/null +++ b/src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/ICameraCapabilities.cs @@ -0,0 +1,86 @@ +using Newtonsoft.Json; +using PepperDash.Core; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace PepperDash.Essentials.Devices.Common.Cameras +{ + /// + /// Interface for camera capabilities + /// + public interface ICameraCapabilities: IKeyName + { + /// + /// Indicates whether the camera can pan + /// + [JsonProperty("canPan", NullValueHandling = NullValueHandling.Ignore)] + bool CanPan { get; } + + /// + /// Indicates whether the camera can tilt + /// + [JsonProperty("canTilt", NullValueHandling = NullValueHandling.Ignore)] + bool CanTilt { get; } + + /// + /// Indicates whether the camera can zoom + /// + [JsonProperty("canZoom", NullValueHandling = NullValueHandling.Ignore)] + bool CanZoom { get; } + + + /// + /// Indicates whether the camera can focus + /// + [JsonProperty("canFocus", NullValueHandling = NullValueHandling.Ignore)] + bool CanFocus { get; } + } + + /// + /// Indicates the capabilities of a camera + /// + public class CameraCapabilities : ICameraCapabilities + { + + /// + /// Unique Key + /// + [JsonProperty("key", NullValueHandling = NullValueHandling.Ignore)] + public string Key { get; set; } + + /// + /// Isn't it obvious :) + /// + [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)] + public string Name { get; set; } + + + /// + /// Indicates whether the camera can pan + /// + [JsonProperty("canPan", NullValueHandling = NullValueHandling.Ignore)] + public bool CanPan { get; set; } + + /// + /// Indicates whether the camera can tilt + /// + [JsonProperty("canTilt", NullValueHandling = NullValueHandling.Ignore)] + public bool CanTilt { get; set; } + + /// + /// Indicates whether the camera can zoom + /// + [JsonProperty("canZoom", NullValueHandling = NullValueHandling.Ignore)] + public bool CanZoom { get; set; } + + + /// + /// Indicates whether the camera can focus + /// + [JsonProperty("canFocus", NullValueHandling = NullValueHandling.Ignore)] + public bool CanFocus { get; set; } + } +} diff --git a/src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/IHasCameras.cs b/src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/IHasCameras.cs index f242f4f4..5a5962f8 100644 --- a/src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/IHasCameras.cs +++ b/src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/IHasCameras.cs @@ -1,4 +1,5 @@ -using PepperDash.Core; +using Newtonsoft.Json; +using PepperDash.Core; using PepperDash.Essentials.Core; using System; using System.Collections.Generic; diff --git a/src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/IHasCamerasWithControls.cs b/src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/IHasCamerasWithControls.cs index a842364d..f7aff6d2 100644 --- a/src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/IHasCamerasWithControls.cs +++ b/src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/IHasCamerasWithControls.cs @@ -13,6 +13,7 @@ namespace PepperDash.Essentials.Devices.Common.Cameras /// /// List of cameras on the device. This should be a list of IHasCameraControls objects /// + List Cameras { get; } /// diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/CameraBaseMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/CameraBaseMessenger.cs index 7650b430..8c9f0451 100644 --- a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/CameraBaseMessenger.cs +++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/CameraBaseMessenger.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; +using Newtonsoft.Json; using Newtonsoft.Json.Linq; +using PepperDash.Core; using PepperDash.Essentials.Core; using PepperDash.Essentials.Devices.Common.Cameras; @@ -9,12 +11,12 @@ namespace PepperDash.Essentials.AppServer.Messengers /// /// Messenger for a CameraBase device /// - public class CameraBaseMessenger : MessengerBase + public class CameraBaseMessenger : MessengerBase where T : IKeyed { /// /// Gets or sets the Camera /// - public CameraBase Camera { get; set; } + public T Camera { get; set; } /// /// Constructor @@ -22,10 +24,13 @@ namespace PepperDash.Essentials.AppServer.Messengers /// /// /// - public CameraBaseMessenger(string key, CameraBase camera, string messagePath) - : base(key, messagePath, camera) + public CameraBaseMessenger(string key, T camera, string messagePath) + : base(key, messagePath, camera as IKeyName) { - Camera = camera ?? throw new ArgumentNullException("camera"); + if (camera == null) + throw new ArgumentNullException(nameof(camera)); + + Camera = camera; if (Camera is IHasCameraPresets presetsCamera) @@ -178,19 +183,44 @@ namespace PepperDash.Essentials.AppServer.Messengers private void SendCameraFullMessageObject(string id = null) { var presetList = new List(); + CameraCapabilities capabilities = null; if (Camera is IHasCameraPresets presetsCamera) presetList = presetsCamera.Presets; - PostStatusMessage(JToken.FromObject(new + if (Camera is ICameraCapabilities cameraCapabilities) + capabilities = new CameraCapabilities + { + CanPan = cameraCapabilities.CanPan, + CanTilt = cameraCapabilities.CanTilt, + CanZoom = cameraCapabilities.CanZoom, + CanFocus = cameraCapabilities.CanFocus + + }; + + if (Camera is CameraBase cameraBase) + capabilities = new CameraCapabilities + { + CanPan = cameraBase.CanPan, + CanTilt = cameraBase.CanTilt, + CanZoom = cameraBase.CanZoom, + CanFocus = cameraBase.CanFocus + + }; + + var message = new CameraStateMessage { - cameraManualSupported = Camera is IHasCameraControls, - cameraAutoSupported = Camera is IHasCameraAutoMode, - cameraOffSupported = Camera is IHasCameraOff, - cameraMode = GetCameraMode(), - hasPresets = Camera is IHasCameraPresets, - presets = presetList - }), id + CameraManualSupported = Camera is IHasCameraControls, + CameraAutoSupported = Camera is IHasCameraAutoMode, + CameraOffSupported = Camera is IHasCameraOff, + CameraMode = (eCameraControlMode)Enum.Parse(typeof(eCameraControlMode), GetCameraMode(), true), + HasPresets = Camera is IHasCameraPresets, + Presets = presetList, + Capabilities = capabilities, + IsFarEnd = Camera is IAmFarEndCamera + }; + + PostStatusMessage(message, id ); } @@ -210,4 +240,59 @@ namespace PepperDash.Essentials.AppServer.Messengers return m; } } + + /// + /// State message for a camera device + /// + public class CameraStateMessage : DeviceStateMessageBase + { + /// + /// Indicates whether the camera supports manual control + /// + [JsonProperty("cameraManualSupported", NullValueHandling = NullValueHandling.Ignore)] + public bool CameraManualSupported { get; set; } + + /// + /// Indicates whether the camera supports auto control + /// + [JsonProperty("cameraAutoSupported", NullValueHandling = NullValueHandling.Ignore)] + public bool CameraAutoSupported { get; set; } + + /// + /// Indicates whether the camera supports off control + /// + [JsonProperty("cameraOffSupported", NullValueHandling = NullValueHandling.Ignore)] + public bool CameraOffSupported { get; set; } + + /// + /// Indicates the current camera control mode + /// + [JsonProperty("cameraMode", NullValueHandling = NullValueHandling.Ignore)] + [JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))] + public eCameraControlMode CameraMode { get; set; } + + /// + /// Indicates whether the camera has presets + /// + [JsonProperty("hasPresets", NullValueHandling = NullValueHandling.Ignore)] + public bool HasPresets { get; set; } + + /// + /// List of presets if the camera supports them + /// + [JsonProperty("presets", NullValueHandling = NullValueHandling.Ignore)] + public List Presets { get; set; } + + /// + /// Indicates the capabilities of the camera + /// + [JsonProperty("capabilities", NullValueHandling = NullValueHandling.Ignore)] + public CameraCapabilities Capabilities { get; set; } + + /// + /// Indicates whether the camera is a far end camera + /// + [JsonProperty("isFarEnd", NullValueHandling = NullValueHandling.Ignore)] + public bool IsFarEnd { get; set; } + } } \ No newline at end of file diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHasCamerasWithControlMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHasCamerasWithControlMessenger.cs index d94cc4c4..e8c4a6a0 100644 --- a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHasCamerasWithControlMessenger.cs +++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHasCamerasWithControlMessenger.cs @@ -1,7 +1,9 @@ using Newtonsoft.Json; +using PepperDash.Core; using PepperDash.Essentials.Devices.Common.Cameras; using System; using System.Collections.Generic; +using System.Linq; namespace PepperDash.Essentials.AppServer.Messengers { @@ -68,14 +70,12 @@ namespace PepperDash.Essentials.AppServer.Messengers { var state = new IHasCamerasWithControlsStateMessage { - CameraList = CameraController.Cameras, - SelectedCamera = CameraController.SelectedCamera + CameraList = CameraController.Cameras.Cast().ToList(), + SelectedCamera = CameraController.SelectedCamera as IKeyName }; PostStatusMessage(state, clientId); } - - } /// @@ -87,13 +87,14 @@ namespace PepperDash.Essentials.AppServer.Messengers /// List of cameras available in the device. /// [JsonProperty("cameraList", NullValueHandling = NullValueHandling.Ignore)] - public List CameraList { get; set; } + public List CameraList { get; set; } /// /// The currently selected camera on the device. /// [JsonProperty("selectedCamera", NullValueHandling = NullValueHandling.Ignore)] - public IHasCameraControls SelectedCamera { get; set; } - + public IKeyName SelectedCamera { get; set; } } + + } diff --git a/src/PepperDash.Essentials.MobileControl/MobileControlSystemController.cs b/src/PepperDash.Essentials.MobileControl/MobileControlSystemController.cs index a4914c10..d6a40ede 100644 --- a/src/PepperDash.Essentials.MobileControl/MobileControlSystemController.cs +++ b/src/PepperDash.Essentials.MobileControl/MobileControlSystemController.cs @@ -405,14 +405,15 @@ namespace PepperDash.Essentials messengerAdded = true; } - if (device is CameraBase cameraDevice) + // Default to IHasCameraControls if CameraBase and IHasCameraControls + if (device is CameraBase cameraDevice && !(device is IHasCameraControls)) { this.LogVerbose( "Adding CameraBaseMessenger for {deviceKey}", device.Key ); - var cameraMessenger = new CameraBaseMessenger( + var cameraMessenger = new CameraBaseMessenger( $"{device.Key}-cameraBase-{Key}", cameraDevice, $"/device/{device.Key}" @@ -423,6 +424,21 @@ namespace PepperDash.Essentials messengerAdded = true; } + if (device is IHasCameraControls cameraControlDev) + { + this.LogVerbose( + "Adding IHasCamerasWithControlMessenger for {deviceKey}", + device.Key + ); + var cameraControlMessenger = new CameraBaseMessenger( + $"{device.Key}-hasCamerasWithControls-{Key}", + cameraControlDev, + $"/device/{device.Key}" + ); + AddDefaultDeviceMessenger(cameraControlMessenger); + messengerAdded = true; + } + if (device is BlueJeansPc) { this.LogVerbose( @@ -977,7 +993,7 @@ namespace PepperDash.Essentials if (device is IHasCamerasWithControls cameras2) { - this.LogVerbose("Adding IHasCamerasMessenger for {deviceKey}", device.Key + this.LogVerbose("Adding IHasCamerasWithControlsMessenger for {deviceKey}", device.Key ); var messenger = new IHasCamerasWithControlMessenger( $"{device.Key}-cameras-{Key}", From bd01e2bacce4df7af3165ebaa85da113bca11a4b Mon Sep 17 00:00:00 2001 From: Neil Dorin Date: Tue, 7 Oct 2025 17:10:11 -0600 Subject: [PATCH 4/7] fix: Add KeyName class and update camera messaging This commit introduces a new `KeyName` class implementing the `IKeyName` interface, enhancing the representation of camera data. The `CameraController_CameraSelected` and `SendFullStatus` methods are updated to utilize `KeyName` instances for selected and listed cameras, improving data encapsulation and consistency in the `IHasCamerasWithControlsStateMessage`. Additionally, new using directives for logging and core functionalities are added. --- .../IHasCamerasWithControlMessenger.cs | 45 +++++++++++++++++-- 1 file changed, 41 insertions(+), 4 deletions(-) diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHasCamerasWithControlMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHasCamerasWithControlMessenger.cs index e8c4a6a0..cce07dcd 100644 --- a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHasCamerasWithControlMessenger.cs +++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHasCamerasWithControlMessenger.cs @@ -1,5 +1,7 @@ using Newtonsoft.Json; using PepperDash.Core; +using PepperDash.Core.Logging; +using PepperDash.Essentials.Core; using PepperDash.Essentials.Devices.Common.Cameras; using System; using System.Collections.Generic; @@ -33,9 +35,15 @@ namespace PepperDash.Essentials.AppServer.Messengers private void CameraController_CameraSelected(object sender, CameraSelectedEventArgs e) { + var selectedCamera = new KeyName + { + Key = e.SelectedCamera.Key, + Name = e.SelectedCamera.Name + }; + PostStatusMessage(new IHasCamerasWithControlsStateMessage { - SelectedCamera = e.SelectedCamera + SelectedCamera = selectedCamera }); } @@ -68,10 +76,30 @@ namespace PepperDash.Essentials.AppServer.Messengers private void SendFullStatus(string clientId) { + var cameraList = new List(); + KeyName selectedCamera = null; + + foreach (var cam in CameraController.Cameras) + { + cameraList.Add(new KeyName{ + Key = cam.Key, + Name = cam.Name + }); + } + + if (CameraController.SelectedCamera != null) + { + selectedCamera = new KeyName + { + Key = CameraController.SelectedCamera.Key, + Name = CameraController.SelectedCamera.Name + }; + } + var state = new IHasCamerasWithControlsStateMessage { - CameraList = CameraController.Cameras.Cast().ToList(), - SelectedCamera = CameraController.SelectedCamera as IKeyName + CameraList = cameraList, + SelectedCamera = selectedCamera }; PostStatusMessage(state, clientId); @@ -96,5 +124,14 @@ namespace PepperDash.Essentials.AppServer.Messengers public IKeyName SelectedCamera { get; set; } } - + class KeyName : IKeyName + { + public string Key { get; set; } + public string Name { get; set; } + public KeyName() + { + Key = ""; + Name = ""; + } + } } From 419177ccd54f2b88cdeb0a74f105fac58d250ddd Mon Sep 17 00:00:00 2001 From: Neil Dorin Date: Wed, 8 Oct 2025 12:24:17 -0600 Subject: [PATCH 5/7] fix: Add item management and update handling in messenger Introduces a new private field `_itemKeys` to store item keys. Adds the `SetItems` method to manage item actions and update events, ensuring proper registration and cleanup. The `SendFullStatus` method is now invoked from a dedicated event handler `LocalItem_ItemUpdated`, improving the handling of item updates. --- .../Messengers/ISelectableItemsMessenger.cs | 33 ++++++++++++++++--- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/ISelectableItemsMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/ISelectableItemsMessenger.cs index 36d73a3e..c3b3715f 100644 --- a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/ISelectableItemsMessenger.cs +++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/ISelectableItemsMessenger.cs @@ -16,6 +16,8 @@ namespace PepperDash.Essentials.AppServer.Messengers private readonly string _propName; + private List _itemKeys = new List(); + /// /// Constructs a messenger for a device that implements ISelectableItems /// @@ -41,7 +43,7 @@ namespace PepperDash.Essentials.AppServer.Messengers itemDevice.ItemsUpdated += (sender, args) => { - SendFullStatus(); + SetItems(); }; itemDevice.CurrentItemChanged += (sender, args) => @@ -49,6 +51,22 @@ namespace PepperDash.Essentials.AppServer.Messengers SendFullStatus(); }; + SetItems(); + } + + /// + /// Sets the items and registers their update events + /// + private void SetItems() + { + /// Clear out any existing item actions + foreach (var item in _itemKeys) + { + RemoveAction($"/{item}"); + } + + _itemKeys.Clear(); + foreach (var input in itemDevice.Items) { var key = input.Key; @@ -59,13 +77,18 @@ namespace PepperDash.Essentials.AppServer.Messengers localItem.Select(); }); - localItem.ItemUpdated += (sender, args) => - { - SendFullStatus(); - }; + _itemKeys.Add(key.ToString()); + + localItem.ItemUpdated -= LocalItem_ItemUpdated; + localItem.ItemUpdated += LocalItem_ItemUpdated; } } + private void LocalItem_ItemUpdated(object sender, EventArgs e) + { + SendFullStatus(); + } + private void SendFullStatus(string id = null) { try From 0418f8a7ccc4498be8329a98745fd538493669c2 Mon Sep 17 00:00:00 2001 From: Neil Dorin Date: Thu, 9 Oct 2025 09:40:46 -0600 Subject: [PATCH 6/7] fix: Fix typos and enhance item selection handling Corrected parameter name in GenericUdpServer constructor. Added new action for item selection in ISelectableItemsMessenger with error handling for missing or invalid keys. Updated SetItems method to improve clarity and ensure proper clearing and re-adding of item actions. --- src/PepperDash.Core/Comm/GenericUdpServer.cs | 6 +-- .../Messengers/ISelectableItemsMessenger.cs | 45 +++++++++++++++---- 2 files changed, 40 insertions(+), 11 deletions(-) diff --git a/src/PepperDash.Core/Comm/GenericUdpServer.cs b/src/PepperDash.Core/Comm/GenericUdpServer.cs index 61bebf8d..1c747998 100644 --- a/src/PepperDash.Core/Comm/GenericUdpServer.cs +++ b/src/PepperDash.Core/Comm/GenericUdpServer.cs @@ -131,14 +131,14 @@ namespace PepperDash.Core /// /// /// - /// - public GenericUdpServer(string key, string address, int port, int buffefSize) + /// + public GenericUdpServer(string key, string address, int port, int bufferSize) : base(key) { StreamDebugging = new CommunicationStreamDebugging(key); Hostname = address; Port = port; - BufferSize = buffefSize; + BufferSize = bufferSize; CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler); CrestronEnvironment.EthernetEventHandler += new EthernetEventHandler(CrestronEnvironment_EthernetEventHandler); diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/ISelectableItemsMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/ISelectableItemsMessenger.cs index c3b3715f..8e2f8f3e 100644 --- a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/ISelectableItemsMessenger.cs +++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/ISelectableItemsMessenger.cs @@ -41,6 +41,32 @@ namespace PepperDash.Essentials.AppServer.Messengers AddAction("/itemsStatus", (id, content) => SendFullStatus(id)); + AddAction("/selectItem", (id, content) => + { + try + { + var key = content.ToObject(); + + if (key == null) + { + this.LogError("No key specified to select"); + return; + } + if (itemDevice.Items.ContainsKey((TKey)Convert.ChangeType(key, typeof(TKey)))) + { + itemDevice.Items[(TKey)Convert.ChangeType(key, typeof(TKey))].Select(); + } + else + { + this.LogError("Key {0} not found in items", key); + } + } + catch (Exception e) + { + this.LogError("Error selecting item: {0}", e.Message); + } + }); + itemDevice.ItemsUpdated += (sender, args) => { SetItems(); @@ -59,18 +85,21 @@ namespace PepperDash.Essentials.AppServer.Messengers /// private void SetItems() { - /// Clear out any existing item actions - foreach (var item in _itemKeys) + if (_itemKeys != null && _itemKeys.Count > 0) { - RemoveAction($"/{item}"); + /// Clear out any existing item actions + foreach (var item in _itemKeys) + { + RemoveAction($"/{item}"); + } + + _itemKeys.Clear(); } - _itemKeys.Clear(); - - foreach (var input in itemDevice.Items) + foreach (var item in itemDevice.Items) { - var key = input.Key; - var localItem = input.Value; + var key = item.Key; + var localItem = item.Value; AddAction($"/{key}", (id, content) => { From c284c4275f070d49ea0c779fee420863305ab3b8 Mon Sep 17 00:00:00 2001 From: Neil Dorin Date: Thu, 9 Oct 2025 11:31:38 -0600 Subject: [PATCH 7/7] feat: Enhance UDPServer initialization in Connect method Updated the `Connect` method in `GenericUdpServer.cs` to include error handling for hostname parsing. The method now attempts to create a `UDPServer` instance with specified parameters and falls back to default initialization if an error occurs. This improves flexibility and robustness in server setup. --- src/PepperDash.Core/Comm/GenericUdpServer.cs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/PepperDash.Core/Comm/GenericUdpServer.cs b/src/PepperDash.Core/Comm/GenericUdpServer.cs index 1c747998..52ac627a 100644 --- a/src/PepperDash.Core/Comm/GenericUdpServer.cs +++ b/src/PepperDash.Core/Comm/GenericUdpServer.cs @@ -194,7 +194,21 @@ namespace PepperDash.Core { if (Server == null) { - Server = new UDPServer(); + try + { + var address = IPAddress.Parse(Hostname); + + Server = new UDPServer(address, Port, BufferSize); + + } + catch (Exception ex) + { + this.LogError("Error parsing IP Address '{ipAddress}': message: {message}", Hostname, ex.Message); + this.LogInformation("Creating UDPServer with default buffersize"); + + Server = new UDPServer(); + } + } if (string.IsNullOrEmpty(Hostname))