diff --git a/src/PepperDash.Essentials.Core/Routing/IHasNamedRoutingSlots.cs b/src/PepperDash.Essentials.Core/Routing/IHasNamedRoutingSlots.cs
new file mode 100644
index 00000000..84b5983e
--- /dev/null
+++ b/src/PepperDash.Essentials.Core/Routing/IHasNamedRoutingSlots.cs
@@ -0,0 +1,49 @@
+using System;
+using System.Collections.Generic;
+using PepperDash.Core;
+
+namespace PepperDash.Essentials.Core;
+
+///
+/// Describes a single named routing slot (input or output) on a device implementing
+/// .
+///
+public interface IRoutingSlotInfo : IKeyName
+{
+ /// Matrix slot number.
+ int SlotNumber { get; }
+
+ /// Signal types this slot can carry.
+ eRoutingSignalType SupportedSignalTypes { get; }
+}
+
+///
+/// Describes a named output slot, adding per-signal-type current-route feedback (keyed by the
+/// routed input slot's key) that does not
+/// carry.
+///
+public interface IRoutingOutputSlotInfo : IRoutingSlotInfo
+{
+ /// The key of the input slot currently routed to this output, per signal type.
+ IReadOnlyDictionary CurrentRouteInputKeys { get; }
+
+ /// Raised when the routed input on this output changes.
+ event EventHandler OutputSlotChanged;
+}
+
+///
+/// Optional extension to for matrix-style routing
+/// devices that track named input/output slots (with per-signal-type routing feedback) internally,
+/// e.g. plugin-local slot abstractions that replaced the removed core IRoutingInputSlot/
+/// IRoutingOutputSlot during the v3 routing refactor. Devices implementing this get a richer
+/// mobile-control matrix routing message (names + per-signal-type feedback) instead of the bare
+/// key-only ports from alone.
+///
+public interface IHasNamedRoutingSlots : IRoutingMidpointWithFeedback
+{
+ /// Named input slots, keyed by slot key.
+ IReadOnlyDictionary InputSlots { get; }
+
+ /// Named output slots, keyed by slot key.
+ IReadOnlyDictionary OutputSlots { get; }
+}
diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHasNamedRoutingSlotsMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHasNamedRoutingSlotsMessenger.cs
new file mode 100644
index 00000000..855a4aa7
--- /dev/null
+++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHasNamedRoutingSlotsMessenger.cs
@@ -0,0 +1,85 @@
+using System.Collections.Generic;
+using System.Linq;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using PepperDash.Core;
+using PepperDash.Essentials.Core;
+
+namespace PepperDash.Essentials.AppServer.Messengers
+{
+ ///
+ /// Messenger for devices implementing — sends named
+ /// input/output slots with per-signal-type current-route feedback, which the bare
+ /// (registered for every
+ /// device, including this one) cannot provide.
+ ///
+ public class IHasNamedRoutingSlotsMessenger : MessengerBase
+ {
+ private readonly IHasNamedRoutingSlots _device;
+
+ public IHasNamedRoutingSlotsMessenger(string key, string messagePath, IHasNamedRoutingSlots device)
+ : base(key, messagePath, device as IKeyName)
+ {
+ _device = device;
+ }
+
+ protected override void RegisterActions()
+ {
+ base.RegisterActions();
+
+ AddAction("/fullStatus", (id, content) => SendFullStatus(id));
+
+ foreach (var output in _device.OutputSlots.Values)
+ {
+ output.OutputSlotChanged += (sender, args) => SendFullStatus();
+ }
+ }
+
+ private static RoutingSlotMessage BuildSlotMessage(IRoutingSlotInfo slot) => new RoutingSlotMessage
+ {
+ Key = slot.Key,
+ Name = slot.Name,
+ SlotNumber = slot.SlotNumber,
+ SupportedSignalTypes = slot.SupportedSignalTypes.ToString()
+ };
+
+ private void SendFullStatus(string id = null)
+ {
+ var inputSlots = _device.InputSlots.ToDictionary(kvp => kvp.Key, kvp => BuildSlotMessage(kvp.Value));
+
+ var outputSlots = _device.OutputSlots.ToDictionary(kvp => kvp.Key, kvp =>
+ {
+ var message = BuildSlotMessage(kvp.Value);
+ message.CurrentRouteInputKeys = kvp.Value.CurrentRouteInputKeys
+ .ToDictionary(r => r.Key.ToString(), r => r.Value);
+ return message;
+ });
+
+ var content = JToken.FromObject(new
+ {
+ inputSlots,
+ outputSlots
+ });
+
+ PostStatusMessage(content, MessagePath, id);
+ }
+ }
+
+ public class RoutingSlotMessage
+ {
+ [JsonProperty("key")]
+ public string Key { get; set; }
+
+ [JsonProperty("name")]
+ public string Name { get; set; }
+
+ [JsonProperty("slotNumber")]
+ public int SlotNumber { get; set; }
+
+ [JsonProperty("supportedSignalTypes")]
+ public string SupportedSignalTypes { get; set; }
+
+ [JsonProperty("currentRouteInputKeys", NullValueHandling = NullValueHandling.Ignore)]
+ public Dictionary CurrentRouteInputKeys { get; set; }
+ }
+}
diff --git a/src/PepperDash.Essentials.MobileControl/MessengerFactoryRegistry.cs b/src/PepperDash.Essentials.MobileControl/MessengerFactoryRegistry.cs
index d2b5c73e..29ede86d 100644
--- a/src/PepperDash.Essentials.MobileControl/MessengerFactoryRegistry.cs
+++ b/src/PepperDash.Essentials.MobileControl/MessengerFactoryRegistry.cs
@@ -374,6 +374,14 @@ namespace PepperDash.Essentials
(d, mp, _) => new IRoutingMidpointWithFeedbackMessenger(
$"{d.Key}-matrixRouting", mp, (IRoutingMidpointWithFeedback)d)
),
+ // Devices that also expose named slots (plugin-local IDmInputSlot/INvxInputSlot-
+ // style abstractions) get a second, richer message with names + per-signal-type
+ // route feedback the bare messenger above cannot provide.
+ new MessengerFactoryEntry(
+ typeof(IHasNamedRoutingSlots),
+ (d, mp, _) => new IHasNamedRoutingSlotsMessenger(
+ $"{d.Key}-namedRoutingSlots", mp, (IHasNamedRoutingSlots)d)
+ ),
// ── Environmental sensors ─────────────────────────────────────────────────
// Preserving original key format (no controller key suffix)