diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/CallStatusMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/CallStatusMessenger.cs
new file mode 100644
index 000000000..cd7edeb3f
--- /dev/null
+++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/CallStatusMessenger.cs
@@ -0,0 +1,221 @@
+using System;
+using System.Linq;
+using System.Reflection;
+using Newtonsoft.Json.Linq;
+using PepperDash.Core;
+using PepperDash.Core.Logging;
+using PepperDash.Essentials.Core;
+using PepperDash.Essentials.Devices.Common.Codec;
+
+namespace PepperDash.Essentials.AppServer.Messengers
+{
+ ///
+ /// Provides a messaging bridge for devices that implement call status interfaces
+ /// without requiring VideoCodecBase inheritance
+ ///
+ public class CallStatusMessenger : MessengerBase
+ {
+ ///
+ /// Device with dialer capabilities
+ ///
+ protected IHasDialer Dialer { get; private set; }
+
+ ///
+ /// Device with content sharing capabilities (optional)
+ ///
+ protected IHasContentSharing ContentSharing { get; private set; }
+
+ ///
+ /// Constructor
+ ///
+ ///
+ ///
+ ///
+ public CallStatusMessenger(string key, IHasDialer dialer, string messagePath)
+ : base(key, messagePath, dialer as IKeyName)
+ {
+ Dialer = dialer ?? throw new ArgumentNullException(nameof(dialer));
+ dialer.CallStatusChange += Dialer_CallStatusChange;
+
+ // Check for optional content sharing interface
+ if (dialer is IHasContentSharing contentSharing)
+ {
+ ContentSharing = contentSharing;
+ contentSharing.SharingContentIsOnFeedback.OutputChange += SharingContentIsOnFeedback_OutputChange;
+ contentSharing.SharingSourceFeedback.OutputChange += SharingSourceFeedback_OutputChange;
+ }
+ }
+
+ ///
+ /// Handles call status changes
+ ///
+ ///
+ ///
+ private void Dialer_CallStatusChange(object sender, CodecCallStatusItemChangeEventArgs e)
+ {
+ try
+ {
+ SendFullStatus();
+ }
+ catch (Exception ex)
+ {
+ this.LogError(ex, "Error handling call status change: {error}", ex.Message);
+ }
+ }
+
+ ///
+ /// Handles content sharing status changes
+ ///
+ ///
+ ///
+ private void SharingContentIsOnFeedback_OutputChange(object sender, FeedbackEventArgs e)
+ {
+ PostStatusMessage(JToken.FromObject(new
+ {
+ sharingContentIsOn = e.BoolValue
+ }));
+ }
+
+ ///
+ /// Handles sharing source changes
+ ///
+ ///
+ ///
+ private void SharingSourceFeedback_OutputChange(object sender, FeedbackEventArgs e)
+ {
+ PostStatusMessage(JToken.FromObject(new
+ {
+ sharingSource = e.StringValue
+ }));
+ }
+
+ ///
+ /// Gets active calls from the dialer
+ ///
+ ///
+ private object GetActiveCalls()
+ {
+ // Try to get active calls if the dialer has an ActiveCalls property
+ var dialerType = Dialer.GetType();
+ var activeCallsProperty = dialerType.GetProperty("ActiveCalls");
+
+ if (activeCallsProperty != null && activeCallsProperty.PropertyType == typeof(System.Collections.Generic.List))
+ {
+ var activeCalls = activeCallsProperty.GetValue(Dialer) as System.Collections.Generic.List;
+ return activeCalls ?? new System.Collections.Generic.List();
+ }
+
+ // Return basic call status if no ActiveCalls property
+ return new { isInCall = Dialer.IsInCall };
+ }
+
+ ///
+ /// Sends full status message
+ ///
+ public void SendFullStatus()
+ {
+ var status = new
+ {
+ isInCall = Dialer.IsInCall,
+ calls = GetActiveCalls()
+ };
+
+ // Add content sharing status if available
+ if (ContentSharing != null)
+ {
+ var statusWithSharing = new
+ {
+ isInCall = Dialer.IsInCall,
+ calls = GetActiveCalls(),
+ sharingContentIsOn = ContentSharing.SharingContentIsOnFeedback.BoolValue,
+ sharingSource = ContentSharing.SharingSourceFeedback.StringValue
+ };
+ PostStatusMessage(JToken.FromObject(statusWithSharing));
+ }
+ else
+ {
+ PostStatusMessage(JToken.FromObject(status));
+ }
+ }
+
+ ///
+ /// Registers actions for call control
+ ///
+ protected override void RegisterActions()
+ {
+ base.RegisterActions();
+
+ AddAction("/fullStatus", (id, content) => SendFullStatus());
+
+ // Basic call control actions
+ AddAction("/dial", (id, content) =>
+ {
+ var msg = content.ToObject>();
+ Dialer.Dial(msg.Value);
+ });
+
+ AddAction("/endAllCalls", (id, content) => Dialer.EndAllCalls());
+
+ AddAction("/dtmf", (id, content) =>
+ {
+ var msg = content.ToObject>();
+ Dialer.SendDtmf(msg.Value);
+ });
+
+ // Call-specific actions (if active calls are available)
+ AddAction("/endCallById", (id, content) =>
+ {
+ var msg = content.ToObject>();
+ var call = GetCallWithId(msg.Value);
+ if (call != null)
+ Dialer.EndCall(call);
+ });
+
+ AddAction("/acceptById", (id, content) =>
+ {
+ var msg = content.ToObject>();
+ var call = GetCallWithId(msg.Value);
+ if (call != null)
+ Dialer.AcceptCall(call);
+ });
+
+ AddAction("/rejectById", (id, content) =>
+ {
+ var msg = content.ToObject>();
+ var call = GetCallWithId(msg.Value);
+ if (call != null)
+ Dialer.RejectCall(call);
+ });
+
+ // Content sharing actions if available
+ if (ContentSharing != null)
+ {
+ AddAction("/startSharing", (id, content) => ContentSharing.StartSharing());
+ AddAction("/stopSharing", (id, content) => ContentSharing.StopSharing());
+ }
+ }
+
+ ///
+ /// Finds a call by ID
+ ///
+ ///
+ ///
+ private CodecActiveCallItem GetCallWithId(string id)
+ {
+ // Try to get call using reflection for ActiveCalls property
+ var dialerType = Dialer.GetType();
+ var activeCallsProperty = dialerType.GetProperty("ActiveCalls");
+
+ if (activeCallsProperty != null && activeCallsProperty.PropertyType == typeof(System.Collections.Generic.List))
+ {
+ var activeCalls = activeCallsProperty.GetValue(Dialer) as System.Collections.Generic.List;
+ if (activeCalls != null)
+ {
+ return activeCalls.FirstOrDefault(c => c.Id.Equals(id));
+ }
+ }
+
+ return null;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.MobileControl/MobileControlSystemController.cs b/src/PepperDash.Essentials.MobileControl/MobileControlSystemController.cs
index f58545168..a18d4e40b 100644
--- a/src/PepperDash.Essentials.MobileControl/MobileControlSystemController.cs
+++ b/src/PepperDash.Essentials.MobileControl/MobileControlSystemController.cs
@@ -27,6 +27,7 @@
using PepperDash.Essentials.Core.Web;
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.Lighting;
using PepperDash.Essentials.Devices.Common.SoftCodec;
@@ -560,6 +561,21 @@ device as DisplayBase
messengerAdded = true;
}
+ else if (device is IHasDialer dialer && !messengerAdded)
+ {
+ this.LogVerbose(
+ "Adding CallStatusMessenger for {deviceKey}", device.Key);
+
+ var messenger = new CallStatusMessenger(
+ $"{device.Key}-callStatus-{Key}",
+ dialer,
+ $"/device/{device.Key}"
+ );
+
+ AddDefaultDeviceMessenger(messenger);
+
+ messengerAdded = true;
+ }
if (device is AudioCodecBase audioCodec)
{