feat: refactor messaging interfaces and remove subscription support from IMobileControlMessengerWithSubscriptions

This commit is contained in:
Neil Dorin 2026-06-26 15:53:51 -06:00
parent b945ecb470
commit aae41b5947
10 changed files with 60 additions and 121 deletions

View file

@ -27,5 +27,11 @@ namespace PepperDash.Essentials.Core.DeviceTypeInterfaces
/// </summary>
/// <param name="appServerController"></param>
void RegisterWithAppServer(IMobileControl appServerController);
/// <summary>
/// Unsubscribe a client from this messenger
/// </summary>
/// <param name="clientId">Client ID to remove from subscription list</param>
void UnsubscribeClient(string clientId);
}
}

View file

@ -1,23 +1,14 @@
using System;
using PepperDash.Core;
namespace PepperDash.Essentials.Core.DeviceTypeInterfaces
{
/// <summary>
/// Defines the contract for IMobileControlMessenger
/// Obsolete. Use <see cref="IMobileControlMessenger"/> directly.
/// Subscriptions are now always enabled in MessengerBase.
/// </summary>
[Obsolete("Use IMobileControlMessenger directly. Subscriptions are always enabled.")]
public interface IMobileControlMessengerWithSubscriptions : IMobileControlMessenger
{
/// <summary>
/// Unsubscribe a client from this messenger
/// </summary>
/// <param name="clientId"></param>
void UnsubscribeClient(string clientId);
/// <summary>
/// Register this messenger with the AppServerController
/// </summary>
/// <param name="appServerController">parent for this messenger</param>
/// <param name="enableMessengerSubscriptions">Enable messenger subscriptions</param>
void RegisterWithAppServer(IMobileControl appServerController, bool enableMessengerSubscriptions);
}
}

View file

@ -30,10 +30,10 @@ namespace PepperDash.Essentials.AppServer.Messengers
{
base.RegisterActions();
AddAction("/fullStatus", (id, content) => PostCallHistory());
AddAction("/callHistoryStatus", (id, content) => PostCallHistory());
AddAction("/fullStatus", (id, content) => PostCallHistory(id));
AddAction("/callHistoryStatus", (id, content) => PostCallHistory(id));
AddAction("/getCallHistory", (id, content) => PostCallHistory());
AddAction("/getCallHistory", (id, content) => PostCallHistory(id));
}
private void CallHistory_RecentCallsListHasChanged(object sender, EventArgs e)
@ -57,7 +57,7 @@ namespace PepperDash.Essentials.AppServer.Messengers
}
}
private void PostCallHistory()
private void PostCallHistory(string id = null)
{
try
{
@ -67,7 +67,7 @@ namespace PepperDash.Essentials.AppServer.Messengers
PostStatusMessage(new IHasCallHistoryStateMessage
{
RecentCalls = recents,
});
}, id);
}
}
catch (Exception ex)

View file

@ -40,8 +40,8 @@ namespace PepperDash.Essentials.AppServer.Messengers
_cameraCodec.CameraSelected += CameraCodec_CameraSelected;
AddAction("/fullStatus", (id, content) => SendFullStatus());
AddAction("/codecCamerasStatus", (id, content) => SendFullStatus());
AddAction("/fullStatus", (id, content) => SendFullStatus(id));
AddAction("/codecCamerasStatus", (id, content) => SendFullStatus(id));
AddAction("/cameraSelect", (id, content) =>
{
@ -327,7 +327,7 @@ namespace PepperDash.Essentials.AppServer.Messengers
}
}
private void SendFullStatus()
private void SendFullStatus(string id = null)
{
try
{
@ -344,7 +344,7 @@ namespace PepperDash.Essentials.AppServer.Messengers
SelectedCamera = GetSelectedCamera(_cameraCodec)
},
Presets = GetCurrentPresets()
});
}, id);
}
catch (Exception ex)
{

View file

@ -28,12 +28,12 @@ namespace PepperDash.Essentials.AppServer.Messengers
SendFullStatus();
}
private void SendFullStatus()
private void SendFullStatus(string id = null)
{
PostStatusMessage(new IHasCodecLayoutsStateMessage
{
CurrentLayout = _layouts.LocalLayoutFeedback.StringValue
});
}, id);
}
/// <inheritdoc />
@ -41,9 +41,9 @@ namespace PepperDash.Essentials.AppServer.Messengers
{
base.RegisterActions();
AddAction("/fullStatus", (id, content) => SendFullStatus());
AddAction("/fullStatus", (id, content) => SendFullStatus(id));
AddAction("/layoutStatus", (id, content) => SendFullStatus());
AddAction("/layoutStatus", (id, content) => SendFullStatus(id));
AddAction("/cameraRemoteView", (id, content) => _layouts.LocalLayoutToggle());
AddAction("/cameraLayout", (id, content) => _layouts.LocalLayoutToggle());

View file

@ -28,9 +28,9 @@ namespace PepperDash.Essentials.AppServer.Messengers
{
base.RegisterActions();
AddAction("/fullStatus", (id, content) => SendFullStatus());
AddAction("/fullStatus", (id, content) => SendFullStatus(id));
AddAction("/cameraSelfViewStatus", (id, content) => SendFullStatus());
AddAction("/cameraSelfViewStatus", (id, content) => SendFullStatus(id));
AddAction("/cameraSelfView", (id, content) => _selfView.SelfViewModeToggle());
@ -42,12 +42,12 @@ namespace PepperDash.Essentials.AppServer.Messengers
PostCameraSelfView();
}
private void SendFullStatus()
private void SendFullStatus(string id = null)
{
PostCameraSelfView();
PostCameraSelfView(id);
}
private void PostCameraSelfView()
private void PostCameraSelfView(string id = null)
{
try
{
@ -55,7 +55,7 @@ namespace PepperDash.Essentials.AppServer.Messengers
{
CameraSelfViewIsOn = _selfView.SelfviewIsOnFeedback.BoolValue,
ShowSelfViewByDefault = _selfView.ShowSelfViewByDefault
});
}, id);
}
catch (Exception ex)
{

View file

@ -30,9 +30,9 @@ namespace PepperDash.Essentials.AppServer.Messengers
{
base.RegisterActions();
AddAction("/fullStatus", (id, content) => SendFullStatus());
AddAction("/fullStatus", (id, content) => SendFullStatus(id));
AddAction("/directoryStatus", (id, content) => SendFullStatus());
AddAction("/directoryStatus", (id, content) => SendFullStatus(id));
AddAction("/getDirectory", (id, content) => GetDirectoryRoot());
@ -49,7 +49,7 @@ namespace PepperDash.Essentials.AppServer.Messengers
AddAction("/directorySearch", (id, content) =>
{
var msg = content.ToObject<MobileControlSimpleContent<string>>();
GetDirectory(msg.Value);
_directory.SearchDirectory(msg.Value);
});
AddAction("/directoryBack", (id, content) => GetPreviousDirectory());
@ -133,7 +133,7 @@ namespace PepperDash.Essentials.AppServer.Messengers
_directory.GetDirectoryParentFolderContents();
}
private void SendFullStatus()
private void SendFullStatus(string id = null)
{
PostStatusMessage(new IHasDirectoryStateMessage
{
@ -144,7 +144,7 @@ namespace PepperDash.Essentials.AppServer.Messengers
HasDirectorySearch = true,
DirectorySelectedFolderName = _directory.CurrentDirectoryResult?.CurrentDirectoryResults?.Count > 0 ? _directory.CurrentDirectoryResult.CurrentDirectoryResults[0].Name : null,
DirectorySelectedFolderIsNotRoot = _directory.CurrentDirectoryResultIsNotDirectoryRoot?.BoolValue
});
}, id);
}
}

View file

@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Crestron.SimplSharp.Net;
using Newtonsoft.Json.Linq;
using PepperDash.Core;
using PepperDash.Core.Logging;
@ -13,18 +12,13 @@ namespace PepperDash.Essentials.AppServer.Messengers
/// <summary>
/// Provides a messaging bridge
/// </summary>
public abstract class MessengerBase : EssentialsDevice, IMobileControlMessengerWithSubscriptions
public abstract class MessengerBase : EssentialsDevice, IMobileControlMessenger
{
/// <summary>
/// The device this messenger is associated with
/// </summary>
protected IKeyName _device;
/// <summary>
/// Enable subscriptions
/// </summary>
protected bool enableMessengerSubscriptions;
/// <summary>
/// List of clients subscribed to this messenger
/// </summary>
@ -100,21 +94,6 @@ namespace PepperDash.Essentials.AppServer.Messengers
RegisterActions();
}
/// <summary>
/// Register this messenger with appserver controller
/// </summary>
/// <param name="appServerController">Parent controller for this messenger</param>
/// <param name="enableMessengerSubscriptions">Enable subscriptions</param>
public void RegisterWithAppServer(IMobileControl appServerController, bool enableMessengerSubscriptions)
{
this.enableMessengerSubscriptions = enableMessengerSubscriptions;
AppServerController = appServerController ?? throw new ArgumentNullException("appServerController");
AppServerController.AddAction(this, HandleMessage);
RegisterActions();
}
private void HandleMessage(string path, string id, JToken content)
{
// replace base path with empty string. Should leave something like /fullStatus
@ -125,6 +104,12 @@ namespace PepperDash.Essentials.AppServer.Messengers
return;
}
// Auto-subscribe the client on any incoming action
if (!string.IsNullOrEmpty(id))
{
SubscribeClient(id);
}
this.LogDebug("Executing action for path {path}", path);
action(id, content);
@ -176,21 +161,15 @@ namespace PepperDash.Essentials.AppServer.Messengers
}
/// <summary>
/// Add client to the susbscription list for unsolicited feedback
/// Add client to the subscription list for unsolicited feedback
/// </summary>
/// <param name="clientId">Client ID to add</param>
protected void SubscribeClient(string clientId)
private void SubscribeClient(string clientId)
{
if (!enableMessengerSubscriptions)
{
return;
}
lock (_subscriberLock)
{
if (!subscriberIds.Add(clientId))
{
this.LogVerbose("Client {clientId} already subscribed", clientId);
return;
}
}
@ -204,24 +183,14 @@ namespace PepperDash.Essentials.AppServer.Messengers
/// <param name="clientId">Client ID to remove</param>
public void UnsubscribeClient(string clientId)
{
if (!enableMessengerSubscriptions)
{
return;
}
bool wasSubscribed;
lock (_subscriberLock)
{
wasSubscribed = subscriberIds.Contains(clientId);
if (wasSubscribed)
{
subscriberIds.Remove(clientId);
}
wasSubscribed = subscriberIds.Remove(clientId);
}
if (!wasSubscribed)
{
this.LogVerbose("Client with ID {clientId} is not subscribed", clientId);
return;
}
@ -301,35 +270,26 @@ namespace PepperDash.Essentials.AppServer.Messengers
{
try
{
// Allow for legacy method to continue without subscriptions
if (!enableMessengerSubscriptions)
var messageType = !string.IsNullOrEmpty(type) ? type : MessagePath;
// If clientId is provided, send directly to that client
if (!string.IsNullOrEmpty(clientId))
{
AppServerController?.SendMessageObject(new MobileControlMessage { Type = !string.IsNullOrEmpty(type) ? type : MessagePath, ClientId = clientId, Content = content });
AppServerController?.SendMessageObject(new MobileControlMessage { Type = messageType, ClientId = clientId, Content = content });
return;
}
// handle subscription feedback
// If client is null or empty, this message is unsolicited feedback. Iterate through the subscriber list and send to all interested parties
if (string.IsNullOrEmpty(clientId))
// Unsolicited feedback: send to all subscribers
List<string> subscriberSnapshot;
lock (_subscriberLock)
{
// Create a snapshot of subscribers to avoid collection modification during iteration
List<string> subscriberSnapshot;
lock (_subscriberLock)
{
subscriberSnapshot = new List<string>(subscriberIds);
}
foreach (var client in subscriberSnapshot)
{
AppServerController?.SendMessageObject(new MobileControlMessage { Type = !string.IsNullOrEmpty(type) ? type : MessagePath, ClientId = client, Content = content });
}
return;
subscriberSnapshot = new List<string>(subscriberIds);
}
SubscribeClient(clientId);
AppServerController?.SendMessageObject(new MobileControlMessage { Type = !string.IsNullOrEmpty(type) ? type : MessagePath, ClientId = clientId, Content = content });
foreach (var client in subscriberSnapshot)
{
AppServerController?.SendMessageObject(new MobileControlMessage { Type = messageType, ClientId = client, Content = content });
}
}
catch (Exception ex)
{

View file

@ -38,13 +38,6 @@ namespace PepperDash.Essentials
/// </summary>
[JsonProperty("enableApiServer")]
public bool EnableApiServer { get; set; } = true;
/// <summary>
/// Enable subscriptions for Messengers.
/// Defaults to true for v3.x+
/// </summary>
[JsonProperty("enableMessengerSubscriptions")]
public bool EnableMessengerSubscriptions { get; set; } = true;
}
/// <summary>

View file

@ -59,14 +59,14 @@ namespace PepperDash.Essentials
new Dictionary<string, IMobileControlMessenger>();
/// <summary>
/// Get the custom messengers with subscriptions
/// Get the custom messengers
/// </summary>
public ReadOnlyDictionary<string, IMobileControlMessengerWithSubscriptions> Messengers => new ReadOnlyDictionary<string, IMobileControlMessengerWithSubscriptions>(_messengers.Values.OfType<IMobileControlMessengerWithSubscriptions>().ToDictionary(k => k.Key, v => v));
public ReadOnlyDictionary<string, IMobileControlMessenger> Messengers => new ReadOnlyDictionary<string, IMobileControlMessenger>(_messengers);
/// <summary>
/// Get the default messengers
/// </summary>
public ReadOnlyDictionary<string, IMobileControlMessengerWithSubscriptions> DefaultMessengers => new ReadOnlyDictionary<string, IMobileControlMessengerWithSubscriptions>(_defaultMessengers.Values.OfType<IMobileControlMessengerWithSubscriptions>().ToDictionary(k => k.Key, v => v));
public ReadOnlyDictionary<string, IMobileControlMessenger> DefaultMessengers => new ReadOnlyDictionary<string, IMobileControlMessenger>(_defaultMessengers);
private readonly GenericQueue _transmitToServerQueue;
@ -534,23 +534,12 @@ namespace PepperDash.Essentials
messenger.MessagePath
);
if (messenger is IMobileControlMessengerWithSubscriptions subMessenger)
{
subMessenger.RegisterWithAppServer(this, Config.EnableMessengerSubscriptions);
return;
}
messenger.RegisterWithAppServer(this);
}
/// <inheritdoc />
protected override void Initialize()
{
if (!Config.EnableMessengerSubscriptions)
{
this.LogWarning("Messenger subscriptions disabled. add \"enableMessengerSubscriptions\": true to config for {key} to enable.", Key);
}
foreach (var messenger in _messengers)
{
try