diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/ICurrentSourcesMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/ICurrentSourcesMessenger.cs index c8de380c..12d1dea6 100644 --- a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/ICurrentSourcesMessenger.cs +++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/ICurrentSourcesMessenger.cs @@ -50,7 +50,7 @@ namespace PepperDash.Essentials.AppServer.Messengers }; } - private void SendCurrentSourceStatus(string id = null) + private void SendCurrentSourceStatus(string id) { var message = new CurrentSourcesStateMessage { diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHasDirectoryMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHasDirectoryMessenger.cs index a19a4ae8..31d61da6 100644 --- a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHasDirectoryMessenger.cs +++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHasDirectoryMessenger.cs @@ -36,12 +36,16 @@ namespace PepperDash.Essentials.AppServer.Messengers AddAction("/getDirectory", (id, content) => GetDirectoryRoot()); + AddAction("/setCurrentDirectoryToRoot", (id, content) => _directory.SetCurrentDirectoryToRoot()); + AddAction("/directoryById", (id, content) => { var msg = content.ToObject>(); GetDirectory(msg.Value); }); + AddAction("/getDirectoryPareentFolderContents", (id, content) => _directory.GetDirectoryParentFolderContents()); + AddAction("/directorySearch", (id, content) => { var msg = content.ToObject>(); @@ -52,6 +56,14 @@ namespace PepperDash.Essentials.AppServer.Messengers _directory.DirectoryResultReturned += DirectoryResultReturned; _directory.PhonebookSyncState.InitialSyncCompleted += PhonebookSyncState_InitialSyncCompleted; + + _directory.CurrentDirectoryResultIsNotDirectoryRoot.OutputChange += (s, e) => + { + PostStatusMessage(new IHasDirectoryStateMessage + { + DirectorySelectedFolderIsNotRoot = _directory.CurrentDirectoryResultIsNotDirectoryRoot?.BoolValue + }); + }; } private void DirectoryResultReturned(object sender, DirectoryEventArgs e) @@ -125,16 +137,22 @@ namespace PepperDash.Essentials.AppServer.Messengers { PostStatusMessage(new IHasDirectoryStateMessage { + DirectoryRoot = _directory.DirectoryRoot, CurrentDirectory = _directory.CurrentDirectoryResult, InitialPhonebookSyncComplete = _directory.PhonebookSyncState.InitialSyncComplete, HasDirectory = true, HasDirectorySearch = true, + DirectorySelectedFolderName = _directory.CurrentDirectoryResult?.CurrentDirectoryResults?.Count > 0 ? _directory.CurrentDirectoryResult.CurrentDirectoryResults[0].Name : null, + DirectorySelectedFolderIsNotRoot = _directory.CurrentDirectorResultIsNotDirectoryRoot?.BoolValue }); } } public class IHasDirectoryStateMessage : DeviceStateMessageBase { + [JsonProperty("directoryRoot", NullValueHandling = NullValueHandling.Ignore)] + public CodecDirectory DirectoryRoot { get; set; } + [JsonProperty("currentDirectory", NullValueHandling = NullValueHandling.Ignore)] public CodecDirectory CurrentDirectory { get; set; } @@ -153,5 +171,8 @@ namespace PepperDash.Essentials.AppServer.Messengers [JsonProperty("directorySelectedFolderName", NullValueHandling = NullValueHandling.Ignore)] public string DirectorySelectedFolderName { get; set; } + [JsonProperty("directorySelectedFolderIsNotRoot", NullValueHandling = NullValueHandling.Ignore)] + public bool? DirectorySelectedFolderIsNotRoot { get; set; } + } } diff --git a/src/PepperDash.Essentials.MobileControl/MobileControlConfig.cs b/src/PepperDash.Essentials.MobileControl/MobileControlConfig.cs index ec7219a3..b74e30cf 100644 --- a/src/PepperDash.Essentials.MobileControl/MobileControlConfig.cs +++ b/src/PepperDash.Essentials.MobileControl/MobileControlConfig.cs @@ -40,10 +40,11 @@ namespace PepperDash.Essentials public bool EnableApiServer { get; set; } = true; /// - /// Enable subscriptions for Messengers + /// Enable subscriptions for Messengers. + /// Defaults to true for v3.x+ /// [JsonProperty("enableMessengerSubscriptions")] - public bool EnableMessengerSubscriptions { get; set; } + public bool EnableMessengerSubscriptions { get; set; } = true; } /// diff --git a/src/PepperDash.Essentials.MobileControl/MobileControlSystemController.cs b/src/PepperDash.Essentials.MobileControl/MobileControlSystemController.cs index e81c53d4..129af42f 100644 --- a/src/PepperDash.Essentials.MobileControl/MobileControlSystemController.cs +++ b/src/PepperDash.Essentials.MobileControl/MobileControlSystemController.cs @@ -1501,8 +1501,14 @@ namespace PepperDash.Essentials } /// - /// Handles a batch request for full status of multiple devices. - /// Triggers all registered messengers for each device key in parallel, + /// Handles a batch request for device status. Supports two content formats: + /// + /// Granular format: { "devices": { "deviceKey": ["/fullStatus", "/layoutStatus"], ... } } + /// + /// + /// Legacy format: { "deviceKeys": ["deviceKey1", "deviceKey2"] } (equivalent to /fullStatus for each) + /// + /// Triggers the specified action paths for each device in parallel, /// then sends an /system/initialSyncComplete message after all have been processed. /// private void HandleBatchDeviceFullStatus(string clientId, JToken content) @@ -1513,11 +1519,37 @@ namespace PepperDash.Essentials return; } - var deviceKeys = content.SelectToken("deviceKeys")?.ToObject>(); + // Build a dictionary of deviceKey -> list of action paths + Dictionary> deviceActionPaths; - if (deviceKeys == null || deviceKeys.Count == 0) + var devicesToken = content.SelectToken("devices"); + if (devicesToken != null) { - this.LogWarning("BatchDeviceFullStatus: No device keys provided"); + // Granular format: { "devices": { "key": ["/path1", "/path2"], ... } } + deviceActionPaths = devicesToken.ToObject>>(); + } + else + { + // Legacy format: { "deviceKeys": ["key1", "key2"] } -> each gets /fullStatus + var deviceKeys = content.SelectToken("deviceKeys")?.ToObject>(); + + if (deviceKeys == null || deviceKeys.Count == 0) + { + this.LogWarning("BatchDeviceFullStatus: No device keys or devices provided"); + SendMessageObject(new MobileControlMessage + { + Type = "/system/initialSyncComplete", + ClientId = clientId + }); + return; + } + + deviceActionPaths = deviceKeys.ToDictionary(k => k, _ => new List { "/fullStatus" }); + } + + if (deviceActionPaths == null || deviceActionPaths.Count == 0) + { + this.LogWarning("BatchDeviceFullStatus: Empty devices dictionary"); SendMessageObject(new MobileControlMessage { Type = "/system/initialSyncComplete", @@ -1526,38 +1558,49 @@ namespace PepperDash.Essentials return; } - this.LogInformation("BatchDeviceFullStatus: Processing {count} device keys", deviceKeys.Count); + this.LogInformation("BatchDeviceFullStatus: Processing {count} devices", deviceActionPaths.Count); var tasks = new List(); - foreach (var deviceKey in deviceKeys) + foreach (var kvp in deviceActionPaths) { - var fullStatusPath = $"/device/{deviceKey}/fullStatus"; + var deviceKey = kvp.Key; + var actionPaths = kvp.Value; - var handlers = _actionDictionary - .Where(kv => fullStatusPath.StartsWith(kv.Key + "/")) - .SelectMany(kv => kv.Value) - .ToList(); - - if (handlers.Count == 0) + if (actionPaths == null || actionPaths.Count == 0) { - this.LogDebug("BatchDeviceFullStatus: No handlers for {deviceKey}", deviceKey); continue; } - foreach (var handler in handlers) + foreach (var actionPath in actionPaths) { - tasks.Add(Task.Run(() => + var fullPath = $"/device/{deviceKey}{actionPath}"; + + var handlers = _actionDictionary + .Where(kv => fullPath.StartsWith(kv.Key + "/")) + .SelectMany(kv => kv.Value) + .ToList(); + + if (handlers.Count == 0) { - try + this.LogDebug("BatchDeviceFullStatus: No handlers for {deviceKey} at path {actionPath}", deviceKey, actionPath); + continue; + } + + foreach (var handler in handlers) + { + tasks.Add(Task.Run(() => { - handler.Action(fullStatusPath, clientId, JToken.FromObject(new { deviceKey })); - } - catch (Exception ex) - { - this.LogError("BatchDeviceFullStatus: Exception in handler for {deviceKey}: {message}", deviceKey, ex.Message); - } - })); + try + { + handler.Action(fullPath, clientId, JToken.FromObject(new { deviceKey })); + } + catch (Exception ex) + { + this.LogError("BatchDeviceFullStatus: Exception in handler for {deviceKey} at {actionPath}: {message}", deviceKey, actionPath, ex.Message); + } + })); + } } }