Refactor audio codec and display messaging interfaces

- Introduced IDialerCallStatus interface to streamline call status handling in audio codecs.
- Updated AudioCodecBase to implement IDialerCallStatus, providing a common structure for call status events.
- Added IDisplayCurrentInput interface for display devices to expose current input information.
- Created corresponding messenger classes for IDialerCallStatus and IDisplayCurrentInput to facilitate communication.
- Implemented CameraControlMessenger for camera devices to manage camera controls and presets.
- Added IWarmingCoolingMessenger for warming and cooling devices to handle status updates.
- Updated MessengerFactoryRegistry to register new messenger classes and ensure proper device messaging.
- Cleaned up CameraBase and removed unnecessary comments and whitespace in various files.

Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
Neil Dorin 2026-05-06 17:13:02 -06:00
parent dfc1d2bb21
commit f80aa95649
14 changed files with 185 additions and 60 deletions

View file

@ -6,11 +6,19 @@ using PepperDash.Essentials.Devices.Common.Codec;
namespace PepperDash.Essentials.Devices.Common.AudioCodec;
public abstract class AudioCodecBase : EssentialsDevice, IHasDialer, IUsageTracking, IAudioCodecInfo
/// <summary>
/// Base class for audio codecs. Provides common properties and methods for audio codecs,
/// as well as a common implementation of IDialerCallStatus to allow the AudioCodecBaseMessenger
/// to get call status information without requiring the full AudioCodecBase class.
/// This is useful for devices that have dialer call status information but do not need to implement
/// the full AudioCodecBase class.
/// </summary>
public abstract class AudioCodecBase : EssentialsDevice, IDialerCallStatus, IUsageTracking, IAudioCodecInfo
{
/// <inheritdoc />
public event EventHandler<CodecCallStatusItemChangeEventArgs> CallStatusChange;
/// <inheritdoc />
public AudioCodecInfo CodecInfo { get; protected set; }
#region IUsageTracking Members
@ -41,8 +49,14 @@ public abstract class AudioCodecBase : EssentialsDevice, IHasDialer, IUsageTrack
}
// In most cases only a single call can be active
/// <inheritdoc />
public List<CodecActiveCallItem> ActiveCalls { get; set; }
/// <summary>
/// Constructor
/// </summary>
/// <param name="key"></param>
/// <param name="name"></param>
public AudioCodecBase(string key, string name)
: base(key, name)
{
@ -83,16 +97,22 @@ public abstract class AudioCodecBase : EssentialsDevice, IHasDialer, IUsageTrack
#region IHasDialer Members
/// <inheritdoc />
public abstract void Dial(string number);
/// <inheritdoc />
public abstract void EndCall(CodecActiveCallItem activeCall);
/// <inheritdoc />
public abstract void EndAllCalls();
/// <inheritdoc />
public abstract void AcceptCall(CodecActiveCallItem item);
public abstract void RejectCall(CodecActiveCallItem item);
public abstract void SendDtmf(string digit);
#endregion

View file

@ -0,0 +1,20 @@
using System.Collections.Generic;
using PepperDash.Essentials.Devices.Common.Codec;
namespace PepperDash.Essentials.Devices.Common.AudioCodec;
/// <summary>
/// Defines the contract for a device that has dialer call status information. This is used to provide a common interface for the AudioCodecBaseMessenger to get call status information without requiring the full AudioCodecBase class
/// </summary>
public interface IDialerCallStatus : IHasDialer
{
/// <summary>
///
/// </summary>
AudioCodecInfo CodecInfo { get; }
/// <summary>
/// Gets or sets the list of active calls for the device.
/// </summary>
List<CodecActiveCallItem> ActiveCalls { get; set; }
}

View file

@ -1,6 +1,4 @@

using System;
using System;
using System.Collections.Generic;
using System.Linq;
using Crestron.SimplSharpPro.DeviceSupport;

View file

@ -0,0 +1,14 @@
using PepperDash.Core;
namespace PepperDash.Essentials.Core.DeviceTypeInterfaces
{
/// <summary>
/// Defines the contract for a display device that has current input information. This is used to provide a common interface for the TwoWayDisplayBaseMessenger to get current input information without requiring the full TwoWayDisplayBase class
/// </summary>
public interface IDisplayCurrentInput : IKeyName
{
/// <summary>
/// Gets the Current Input feedback for the display device.
/// </summary>
StringFeedback CurrentInputFeedback { get; }
}

View file

@ -11,7 +11,7 @@ namespace PepperDash.Essentials.AppServer.Messengers
/// <summary>
/// Messenger for a CameraBase device
/// </summary>
public class CameraBaseMessenger<T> : MessengerBase where T : IKeyed
public class CameraControlMessenger<T> : MessengerBase where T : IKeyed
{
/// <summary>
/// Gets or sets the Camera
@ -24,7 +24,7 @@ namespace PepperDash.Essentials.AppServer.Messengers
/// <param name="key"></param>
/// <param name="camera"></param>
/// <param name="messagePath"></param>
public CameraBaseMessenger(string key, T camera, string messagePath)
public CameraControlMessenger(string key, T camera, string messagePath)
: base(key, messagePath, camera as IKeyName)
{
if (camera == null)

View file

@ -1,20 +1,21 @@
using System;
using System.Linq;
using Newtonsoft.Json.Linq;
using PepperDash.Core;
using PepperDash.Essentials.Devices.Common.AudioCodec;
using PepperDash.Essentials.Devices.Common.Codec;
namespace PepperDash.Essentials.AppServer.Messengers
{
/// <summary>
/// Provides a messaging bridge for an AudioCodecBase device
/// Provides a messaging bridge for an IDialerCallStatus device
/// </summary>
public class AudioCodecBaseMessenger : MessengerBase
public class IDialerCallStatusMessenger : MessengerBase
{
/// <summary>
/// Device being bridged
/// </summary>
public AudioCodecBase Codec { get; private set; }
public IDialerCallStatus Codec { get; private set; }
/// <summary>
/// Constuctor
@ -22,8 +23,8 @@ namespace PepperDash.Essentials.AppServer.Messengers
/// <param name="key"></param>
/// <param name="codec"></param>
/// <param name="messagePath"></param>
public AudioCodecBaseMessenger(string key, AudioCodecBase codec, string messagePath)
: base(key, messagePath, codec)
public IDialerCallStatusMessenger(string key, IDialerCallStatus codec, string messagePath)
: base(key, messagePath, codec as IKeyName)
{
Codec = codec ?? throw new ArgumentNullException("codec");
codec.CallStatusChange += Codec_CallStatusChange;

View file

@ -1,25 +1,27 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using PepperDash.Core;
using PepperDash.Essentials.Core;
using PepperDash.Essentials.Core.DeviceTypeInterfaces;
using PepperDash.Essentials.Devices.Common.Displays;
namespace PepperDash.Essentials.AppServer.Messengers
{
/// <summary>
/// Represents a TwoWayDisplayBaseMessenger
/// Represents a messenger for a display device that has current input information.
/// </summary>
public class TwoWayDisplayBaseMessenger : MessengerBase
public class IDisplayCurrentInputMessenger : MessengerBase
{
private readonly TwoWayDisplayBase _display;
private readonly IDisplayCurrentInput _display;
/// <summary>
/// Initializes a new instance of the <see cref="TwoWayDisplayBaseMessenger"/> class.
/// Initializes a new instance of the <see cref="IDisplayCurrentInputMessenger"/> class.
/// </summary>
/// <param name="key"></param>
/// <param name="messagePath"></param>
/// <param name="display"></param>
public TwoWayDisplayBaseMessenger(string key, string messagePath, TwoWayDisplayBase display)
: base(key, messagePath, display)
public IDisplayCurrentInputMessenger(string key, string messagePath, IDisplayCurrentInput display)
: base(key, messagePath, display as IKeyName)
{
_display = display;
}
@ -31,9 +33,8 @@ namespace PepperDash.Essentials.AppServer.Messengers
/// </summary>
public void SendFullStatus(string id = null)
{
var messageObj = new TwoWayDisplayBaseStateMessage
var messageObj = new CurrentInputStateMessage
{
//PowerState = _display.PowerIsOnFeedback.BoolValue,
CurrentInput = _display.CurrentInputFeedback.StringValue
};
@ -47,11 +48,9 @@ namespace PepperDash.Essentials.AppServer.Messengers
AddAction("/fullStatus", (id, content) => SendFullStatus(id));
AddAction("/displayStatus", (id, content) => SendFullStatus(id));
AddAction("/currentInputStatus", (id, content) => SendFullStatus(id));
_display.CurrentInputFeedback.OutputChange += CurrentInputFeedbackOnOutputChange;
_display.IsCoolingDownFeedback.OutputChange += IsCoolingFeedbackOnOutputChange;
_display.IsWarmingUpFeedback.OutputChange += IsWarmingFeedbackOnOutputChange;
}
private void CurrentInputFeedbackOnOutputChange(object sender, FeedbackEventArgs feedbackEventArgs)
@ -61,24 +60,6 @@ namespace PepperDash.Essentials.AppServer.Messengers
currentInput = feedbackEventArgs.StringValue
})
);
}
private void IsWarmingFeedbackOnOutputChange(object sender, FeedbackEventArgs feedbackEventArgs)
{
PostStatusMessage(JToken.FromObject(new
{
isWarming = feedbackEventArgs.BoolValue
})
);
}
private void IsCoolingFeedbackOnOutputChange(object sender, FeedbackEventArgs feedbackEventArgs)
{
PostStatusMessage(JToken.FromObject(new
{
isCooling = feedbackEventArgs.BoolValue
})
);
}
@ -89,7 +70,7 @@ namespace PepperDash.Essentials.AppServer.Messengers
/// <summary>
/// Represents a TwoWayDisplayBaseStateMessage
/// </summary>
public class TwoWayDisplayBaseStateMessage : DeviceStateMessageBase
public class CurrentInputStateMessage : DeviceStateMessageBase
{
//[JsonProperty("powerState", NullValueHandling = NullValueHandling.Ignore)]
//public bool? PowerState { get; set; }

View file

@ -11,17 +11,17 @@ namespace PepperDash.Essentials.AppServer.Messengers;
/// <summary>
/// Represents a RoomEventScheduleMessenger
/// </summary>
public class RoomEventScheduleMessenger : MessengerBase
public class IRoomEventScheduleMessenger : MessengerBase
{
private readonly IRoomEventSchedule _room;
/// <summary>
/// Initializes a new instance of the <see cref="RoomEventScheduleMessenger"/> class.
/// Initializes a new instance of the <see cref="IRoomEventScheduleMessenger"/> class.
/// </summary>
/// <param name="key"></param>
/// <param name="messagePath"></param>
/// <param name="room"></param>
public RoomEventScheduleMessenger(string key, string messagePath, IRoomEventSchedule room)
public IRoomEventScheduleMessenger(string key, string messagePath, IRoomEventSchedule room)
: base(key, messagePath, room as IKeyName)
{
_room = room;

View file

@ -10,19 +10,19 @@ using PepperDash.Essentials.Core.Presets;
namespace PepperDash.Essentials.AppServer.Messengers
{
/// <summary>
/// Represents a DevicePresetsModelMessenger
/// Represents a ITvPresetsProviderMessenger
/// </summary>
public class DevicePresetsModelMessenger : MessengerBase
public class ITvPresetsProviderMessenger : MessengerBase
{
private readonly ITvPresetsProvider _presetsDevice;
/// <summary>
/// Constructor for DevicePresetsModelMessenger
/// Constructor for ITvPresetsProviderMessenger
/// </summary>
/// <param name="key">The key.</param>
/// <param name="messagePath">The message path.</param>
/// <param name="presetsDevice">The presets device.</param>
public DevicePresetsModelMessenger(string key, string messagePath, ITvPresetsProvider presetsDevice)
public ITvPresetsProviderMessenger(string key, string messagePath, ITvPresetsProvider presetsDevice)
: base(key, messagePath, presetsDevice as Device)
{
_presetsDevice = presetsDevice;

View file

@ -0,0 +1,86 @@
using System.Text.Json.Serialization;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using PepperDash.Essentials.Core;
namespace PepperDash.Essentials.AppServer.Messengers;
/// <summary>
/// Messenger for devices that implement <see cref="IWarmingCooling"/>
/// </summary>
public class IWarmingCoolingMessenger : MessengerBase
{
private readonly IWarmingCooling device;
/// <summary>
/// Initializes a new instance of the <see cref="IWarmingCoolingMessenger"/> class.
/// </summary>
/// <param name="key"></param>
/// <param name="messagePath"></param>
/// <param name="device"></param>
public IWarmingCoolingMessenger(string key, string messagePath, EssentialsDevice device)
: base(key, messagePath, device)
{
this.device = device as IWarmingCooling;
}
/// <inheritdoc />
protected override void RegisterActions()
{
base.RegisterActions();
AddAction("/fullStatus", (id, content) => SendFullStatus(id));
AddAction("/warmingCoolingStatus", (id, content) => SendFullStatus(id));
device.IsWarmingUpFeedback.OutputChange += IsWarmingFeedbackOnOutputChange;
device.IsCoolingDownFeedback.OutputChange += IsCoolingFeedbackOnOutputChange;
}
private void IsWarmingFeedbackOnOutputChange(object sender, FeedbackEventArgs feedbackEventArgs)
{
PostStatusMessage(JToken.FromObject(new
{
isWarming = feedbackEventArgs.BoolValue
}));
}
private void IsCoolingFeedbackOnOutputChange(object sender, FeedbackEventArgs feedbackEventArgs)
{
PostStatusMessage(JToken.FromObject(new
{
isCooling = feedbackEventArgs.BoolValue
}));
}
private void SendFullStatus(string id = null)
{
var messageObj = new IWarmingCoolingStateMessage
{
IsWarming = device.IsWarmingUpFeedback.BoolValue,
IsCooling = device.IsCoolingDownFeedback.BoolValue
};
PostStatusMessage(messageObj, id);
}
}
/// <summary>
/// Message object for warming/cooling status
/// </summary>
public class IWarmingCoolingStateMessage : DeviceStateMessageBase
{
/// <summary>
/// Indicates whether the device is currently warming up.
/// </summary>
[JsonProperty("isWarming", NullValueHandling = NullValueHandling.Ignore)]
public bool IsWarming { get; set; }
/// <summary>
/// Indicates whether the device is currently cooling down.
/// </summary>
[JsonProperty("isCooling", NullValueHandling = NullValueHandling.Ignore)]
public bool IsCooling { get; set; }
}

View file

@ -83,14 +83,14 @@ namespace PepperDash.Essentials
// CameraBase only when the device does NOT also implement IHasCameraControls
new MessengerFactoryEntry(
typeof(CameraBase),
(d, mp, ck) => new CameraBaseMessenger<CameraBase>(
(d, mp, ck) => new CameraControlMessenger<CameraBase>(
$"{d.Key}-cameraBase-{ck}", (CameraBase)d, mp),
predicate: d => d is CameraBase && !(d is IHasCameraControls)
),
new MessengerFactoryEntry(
typeof(IHasCameraControls),
(d, mp, ck) => new CameraBaseMessenger<IHasCameraControls>(
(d, mp, ck) => new CameraControlMessenger<IHasCameraControls>(
$"{d.Key}-hasCamerasWithControls-{ck}", (IHasCameraControls)d, mp)
),
new MessengerFactoryEntry(
@ -102,7 +102,7 @@ namespace PepperDash.Essentials
// ── Routing ──────────────────────────────────────────────────────────────
// BlueJeansPc implements IRunRouteAction
new MessengerFactoryEntry(
typeof(BlueJeansPc),
typeof(IRunRouteAction),
(d, mp, ck) => new RunRouteActionMessenger(
$"{d.Key}-runRouteAction-{ck}", (IRunRouteAction)d, mp)
),
@ -110,7 +110,7 @@ namespace PepperDash.Essentials
// ── Presets ──────────────────────────────────────────────────────────────
new MessengerFactoryEntry(
typeof(ITvPresetsProvider),
(d, mp, ck) => new DevicePresetsModelMessenger(
(d, mp, ck) => new ITvPresetsProviderMessenger(
$"{d.Key}-presets-{ck}", mp, (ITvPresetsProvider)d)
),
@ -121,9 +121,14 @@ namespace PepperDash.Essentials
$"{d.Key}-displayBase-{ck}", mp, (IRoutingSinkWithSwitching)d)
),
new MessengerFactoryEntry(
typeof(TwoWayDisplayBase),
(d, mp, ck) => new TwoWayDisplayBaseMessenger(
$"{d.Key}-twoWayDisplay-{ck}", mp, (TwoWayDisplayBase)d)
typeof(IDisplayCurrentInput),
(d, mp, ck) => new IDisplayCurrentInputMessenger(
$"{d.Key}-twoWayDisplay-{ck}", mp, (IDisplayCurrentInput)d)
),
new MessengerFactoryEntry(
typeof(IWarmingCooling),
(d, mp, ck) => new IWarmingCoolingMessenger(
$"{d.Key}-warmingCooling-{ck}", mp, d)
),
// ── Audio / Video ─────────────────────────────────────────────────────────
@ -160,9 +165,9 @@ namespace PepperDash.Essentials
$"{d.Key}-videoCodec-{ck}", (VideoCodecBase)d, mp)
),
new MessengerFactoryEntry(
typeof(AudioCodecBase),
(d, mp, ck) => new AudioCodecBaseMessenger(
$"{d.Key}-audioCodec-{ck}", (AudioCodecBase)d, mp)
typeof(IDialerCallStatus),
(d, mp, ck) => new IDialerCallStatusMessenger(
$"{d.Key}-audioCodec-{ck}", (IDialerCallStatus)d, mp)
),
// ── Set-top box controls ──────────────────────────────────────────────────
@ -294,7 +299,7 @@ namespace PepperDash.Essentials
// ── Event schedule ────────────────────────────────────────────────────────
new MessengerFactoryEntry(
typeof(IRoomEventSchedule),
(d, mp, ck) => new RoomEventScheduleMessenger(
(d, mp, ck) => new IRoomEventScheduleMessenger(
$"{d.Key}-schedule-{ck}", mp, (IRoomEventSchedule)d)
),