diff --git a/src/PepperDash.Essentials.Core/Devices/SourceListItem.cs b/src/PepperDash.Essentials.Core/Devices/SourceListItem.cs
index 3f770f91..afe3ef24 100644
--- a/src/PepperDash.Essentials.Core/Devices/SourceListItem.cs
+++ b/src/PepperDash.Essentials.Core/Devices/SourceListItem.cs
@@ -169,9 +169,15 @@ namespace PepperDash.Essentials.Core
[JsonProperty("sourceKey")]
public string SourceKey { get; set; }
+ [JsonProperty("sourcePortKey")]
+ public string SourcePortKey { get; set; }
+
[JsonProperty("destinationKey")]
public string DestinationKey { get; set; }
+ [JsonProperty("destinationPortKey")]
+ public string DestinationPortKey { get; set; }
+
[JsonProperty("type")]
public eRoutingSignalType Type { get; set; }
}
diff --git a/src/PepperDash.Essentials.Core/PepperDash.Essentials.Core.csproj b/src/PepperDash.Essentials.Core/PepperDash.Essentials.Core.csproj
index c38c0fbd..79891444 100644
--- a/src/PepperDash.Essentials.Core/PepperDash.Essentials.Core.csproj
+++ b/src/PepperDash.Essentials.Core/PepperDash.Essentials.Core.csproj
@@ -23,6 +23,7 @@
+
diff --git a/src/PepperDash.Essentials.Core/Room/Interfaces.cs b/src/PepperDash.Essentials.Core/Room/Interfaces.cs
index 640dc91f..eb0b6f25 100644
--- a/src/PepperDash.Essentials.Core/Room/Interfaces.cs
+++ b/src/PepperDash.Essentials.Core/Room/Interfaces.cs
@@ -5,6 +5,7 @@ using System.Text;
using Crestron.SimplSharp;
using PepperDash.Core;
+using PepperDash.Essentials.Core.Routing;
namespace PepperDash.Essentials.Core
@@ -41,6 +42,8 @@ namespace PepperDash.Essentials.Core
void RunRouteAction(string routeKey, string sourceListKey);
void RunRouteAction(string routeKey, string sourceListKey, Action successCallback);
+
+ RoutingFeedbackManager RoutingFeedbackManager { get; }
}
///
@@ -49,6 +52,8 @@ namespace PepperDash.Essentials.Core
public interface IRunDirectRouteAction
{
void RunDirectRoute(string sourceKey, string destinationKey, eRoutingSignalType type = eRoutingSignalType.AudioVideo);
+
+ RoutingFeedbackManager RoutingFeedbackManager { get; }
}
///
diff --git a/src/PepperDash.Essentials.Core/Routing/Extensions.cs b/src/PepperDash.Essentials.Core/Routing/Extensions.cs
index 4d33ed4a..03c59bf0 100644
--- a/src/PepperDash.Essentials.Core/Routing/Extensions.cs
+++ b/src/PepperDash.Essentials.Core/Routing/Extensions.cs
@@ -169,18 +169,17 @@ namespace PepperDash.Essentials.Core
RoutingOutputPort outputPortToUse, List alreadyCheckedDevices,
eRoutingSignalType signalType, int cycle, RouteDescriptor routeTable)
{
-
cycle++;
Debug.LogMessage(LogEventLevel.Verbose, "GetRouteToSource: {0} {1}--> {2}", null, cycle, source.Key, destination.Key);
RoutingInputPort goodInputPort = null;
- var destDevInputTies = TieLineCollection.Default.Where(t =>
+ var destinationTieLines = TieLineCollection.Default.Where(t =>
t.DestinationPort.ParentDevice == destination && (t.Type == signalType || t.Type.HasFlag(eRoutingSignalType.AudioVideo)));
// find a direct tie
- var directTie = destDevInputTies.FirstOrDefault(
+ var directTie = destinationTieLines.FirstOrDefault(
t => t.DestinationPort.ParentDevice == destination
&& t.SourcePort.ParentDevice == source);
if (directTie != null) // Found a tie directly to the source
@@ -193,7 +192,7 @@ namespace PepperDash.Essentials.Core
// No direct tie? Run back out on the inputs' attached devices...
// Only the ones that are routing devices
- var attachedMidpoints = destDevInputTies.Where(t => t.SourcePort.ParentDevice is IRoutingInputsOutputs);
+ var attachedMidpoints = destinationTieLines.Where(t => t.SourcePort.ParentDevice is IRoutingInputsOutputs);
//Create a list for tracking already checked devices to avoid loops, if it doesn't already exist from previous iteration
if (alreadyCheckedDevices == null)
diff --git a/src/PepperDash.Essentials.Core/Routing/IRoutingSink.cs b/src/PepperDash.Essentials.Core/Routing/IRoutingSink.cs
index c51ed12f..773fab77 100644
--- a/src/PepperDash.Essentials.Core/Routing/IRoutingSink.cs
+++ b/src/PepperDash.Essentials.Core/Routing/IRoutingSink.cs
@@ -7,7 +7,7 @@ namespace PepperDash.Essentials.Core
///
public interface IRoutingSink : IRoutingInputs, IHasCurrentSourceInfoChange
{
-
+ RoutingInputPort CurrentInputPort { get; }
}
/*///
diff --git a/src/PepperDash.Essentials.Core/Routing/IRoutingSinkWithFeedback.cs b/src/PepperDash.Essentials.Core/Routing/IRoutingSinkWithFeedback.cs
index c7e99746..d3d3e600 100644
--- a/src/PepperDash.Essentials.Core/Routing/IRoutingSinkWithFeedback.cs
+++ b/src/PepperDash.Essentials.Core/Routing/IRoutingSinkWithFeedback.cs
@@ -4,14 +4,13 @@ using System.Collections.Generic;
namespace PepperDash.Essentials.Core
{
+
///
/// For fixed-source endpoint devices
///
public interface IRoutingSinkWithFeedback : IRoutingSinkWithSwitching
{
- RouteSwitchDescriptor CurrentRoute { get; }
-
- event EventHandler InputChanged;
+
}
/* ///
diff --git a/src/PepperDash.Essentials.Core/Routing/IRoutingSinkWithSwitching.cs b/src/PepperDash.Essentials.Core/Routing/IRoutingSinkWithSwitching.cs
index b31bd973..68e4632c 100644
--- a/src/PepperDash.Essentials.Core/Routing/IRoutingSinkWithSwitching.cs
+++ b/src/PepperDash.Essentials.Core/Routing/IRoutingSinkWithSwitching.cs
@@ -2,13 +2,17 @@
namespace PepperDash.Essentials.Core
{
+ public delegate void InputChangedEventHandler(IRoutingSinkWithSwitching destination, RoutingInputPort currentPort);
+
///
/// Endpoint device like a display, that selects inputs
///
public interface IRoutingSinkWithSwitching : IRoutingSink
{
void ExecuteSwitch(object inputSelector);
- }
+
+ event InputChangedEventHandler InputChanged;
+ }
/* ///
/// Endpoint device like a display, that selects inputs
diff --git a/src/PepperDash.Essentials.Core/Routing/IRoutingWithFeedback.cs b/src/PepperDash.Essentials.Core/Routing/IRoutingWithFeedback.cs
index c4abf0c6..dcf4e423 100644
--- a/src/PepperDash.Essentials.Core/Routing/IRoutingWithFeedback.cs
+++ b/src/PepperDash.Essentials.Core/Routing/IRoutingWithFeedback.cs
@@ -3,6 +3,7 @@ using System;
namespace PepperDash.Essentials.Core
{
+ public delegate void RouteChangedEventHandler(IRoutingWithFeedback midpoint, RouteSwitchDescriptor newRoute);
///
/// Defines an IRouting with a feedback event
///
@@ -10,6 +11,6 @@ namespace PepperDash.Essentials.Core
{
List CurrentRoutes { get; }
- event EventHandler RoutingChanged;
+ event RouteChangedEventHandler RouteChanged;
}
}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/Routing/RoutingFeedbackManager.cs b/src/PepperDash.Essentials.Core/Routing/RoutingFeedbackManager.cs
new file mode 100644
index 00000000..bf796d5b
--- /dev/null
+++ b/src/PepperDash.Essentials.Core/Routing/RoutingFeedbackManager.cs
@@ -0,0 +1,165 @@
+using Org.BouncyCastle.Crypto.Prng;
+using PepperDash.Core;
+using PepperDash.Essentials.Core.Config;
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace PepperDash.Essentials.Core.Routing
+{
+ public class RoutingFeedbackManager:EssentialsDevice
+ {
+ public RoutingFeedbackManager(string key, string name): base(key, name)
+ {
+ AddPostActivationAction(SubscribeForMidpointFeedback);
+ AddPostActivationAction(SubscribeForSinkFeedback);
+ }
+
+ private void SubscribeForMidpointFeedback()
+ {
+ var midpointDevices = DeviceManager.AllDevices.OfType();
+
+ foreach (var device in midpointDevices)
+ {
+ device.RouteChanged += HandleMidpointUpdate;
+ }
+ }
+
+ private void SubscribeForSinkFeedback()
+ {
+ var sinkDevices = DeviceManager.AllDevices.OfType();
+
+ foreach (var device in sinkDevices)
+ {
+ device.InputChanged += HandleSinkUpdate;
+ }
+ }
+
+ private void HandleMidpointUpdate(IRoutingWithFeedback midpoint, RouteSwitchDescriptor newRoute)
+ {
+ var devices = DeviceManager.AllDevices.OfType();
+
+ foreach(var device in devices)
+ {
+ UpdateDestination(device, device.CurrentInputPort);
+ }
+ }
+
+ private void HandleSinkUpdate(IRoutingSinkWithSwitching sender, RoutingInputPort currentInputPort)
+ {
+ UpdateDestination(sender, currentInputPort);
+ }
+
+ private void UpdateDestination(IRoutingSinkWithSwitching destination, RoutingInputPort inputPort)
+ {
+ var tieLines = TieLineCollection.Default;
+
+ var firstTieLine = tieLines.FirstOrDefault(tl => tl.DestinationPort.Key == inputPort.Key);
+
+ if (firstTieLine == null)
+ {
+ Debug.LogMessage(Serilog.Events.LogEventLevel.Information, "No tieline found for inputPort {inputPort}. Clearing current source", this, inputPort);
+
+ destination.CurrentSourceInfo = null;
+ destination.CurrentSourceInfoKey = string.Empty;
+ return;
+ }
+
+ var sourceTieLine = GetRootTieLine(firstTieLine);
+
+ if (sourceTieLine == null)
+ {
+ Debug.LogMessage(Serilog.Events.LogEventLevel.Information, "No route found to source for inputPort {inputPort}. Clearing current source", this, inputPort);
+
+ destination.CurrentSourceInfo = null;
+ destination.CurrentSourceInfoKey = string.Empty;
+ }
+
+
+ // Does not handle combinable scenarios or other scenarios where a display might be part of multiple rooms yet.
+ var room = DeviceManager.AllDevices.OfType().FirstOrDefault((r) => {
+ if(r is IHasMultipleDisplays roomMultipleDisplays)
+ {
+ return roomMultipleDisplays.Displays.Any(d => d.Value.Key == destination.Key);
+ }
+
+ if(r is IHasDefaultDisplay roomDefaultDisplay)
+ {
+ return roomDefaultDisplay.DefaultDisplay.Key == destination.Key;
+ }
+
+ return false;
+ }) ;
+
+ if(room == null)
+ {
+ Debug.LogMessage(Serilog.Events.LogEventLevel.Warning, "No room found for display {destination}", this, destination.Key);
+ return;
+ }
+
+ var sourceList = ConfigReader.ConfigObject.GetSourceListForKey(room.SourceListKey);
+
+ if (sourceList == null)
+ {
+ Debug.LogMessage(Serilog.Events.LogEventLevel.Warning, "No source list found for source list key {key}. Unable to find source for tieLine {sourceTieLine}", this, room.SourceListKey, sourceTieLine);
+ return;
+ }
+
+ var sourceListItem = sourceList.FirstOrDefault(sli => sli.Value.SourceKey == sourceTieLine.SourcePort.ParentDevice.Key);
+
+ var source = sourceListItem.Value;
+
+ if (source == null)
+ {
+ Debug.LogMessage(Serilog.Events.LogEventLevel.Information, "No source found for device {key}. Clearing current source on {destination}", this, sourceTieLine.SourcePort.ParentDevice.Key, destination);
+
+ destination.CurrentSourceInfo = null;
+ destination.CurrentSourceInfoKey = string.Empty;
+ return;
+ }
+
+ destination.CurrentSourceInfo = source;
+ destination.CurrentSourceInfoKey = source.SourceKey;
+ }
+
+ private TieLine GetRootTieLine(TieLine tieLine)
+ {
+ TieLine nextTieLine = null;
+
+ if(tieLine.SourcePort.ParentDevice is IRoutingWithFeedback midpoint)
+ {
+ var currentRoute = midpoint.CurrentRoutes.FirstOrDefault(route => route.OutputPort.Key == tieLine.SourcePort.Key);
+
+ if(currentRoute == null)
+ {
+ Debug.LogMessage(Serilog.Events.LogEventLevel.Information, "No route through midpoint {midpoint} for outputPort {outputPort}", this, midpoint.Key, tieLine.SourcePort);
+ return null;
+ }
+
+ nextTieLine = TieLineCollection.Default.FirstOrDefault(tl => tl.DestinationPort.Key == currentRoute.InputPort.Key);
+
+ if(tieLine != null)
+ {
+ return GetRootTieLine(nextTieLine);
+ }
+
+ return tieLine;
+ }
+
+ if(tieLine.SourcePort.ParentDevice is IRoutingSource) //end of the chain
+ {
+ return tieLine;
+ }
+
+ nextTieLine = TieLineCollection.Default.FirstOrDefault(tl => tl.SourcePort.Key == tieLine.SourcePort.Key);
+
+ if(nextTieLine != null)
+ {
+ return GetRootTieLine(nextTieLine);
+ }
+
+ return nextTieLine;
+ }
+ }
+}
diff --git a/src/PepperDash.Essentials.Core/Routing/TieLine.cs b/src/PepperDash.Essentials.Core/Routing/TieLine.cs
index 25bf4ea3..65804eb3 100644
--- a/src/PepperDash.Essentials.Core/Routing/TieLine.cs
+++ b/src/PepperDash.Essentials.Core/Routing/TieLine.cs
@@ -88,7 +88,7 @@ namespace PepperDash.Essentials.Core
public override string ToString()
{
- return string.Format("Tie line: [{0}]{1} --> [{2}]{3}", SourcePort.ParentDevice.Key, SourcePort.Key,
+ return string.Format("Tie line: {0}:{1} --> {2}:{3}", SourcePort.ParentDevice.Key, SourcePort.Key,
DestinationPort.ParentDevice.Key, DestinationPort.Key);
}
}
diff --git a/src/PepperDash.Essentials.Core/Routing/TieLineConfig.cs b/src/PepperDash.Essentials.Core/Routing/TieLineConfig.cs
index 1728c20f..922cc61d 100644
--- a/src/PepperDash.Essentials.Core/Routing/TieLineConfig.cs
+++ b/src/PepperDash.Essentials.Core/Routing/TieLineConfig.cs
@@ -30,7 +30,7 @@ namespace PepperDash.Essentials.Core.Config
/// null if config data does not match ports, cards or devices
public TieLine GetTieLine()
{
- Debug.LogMessage(LogEventLevel.Information, "Build TieLine: {0}", this);
+ Debug.LogMessage(LogEventLevel.Information, "Build TieLine: {0}",null, this);
// Get the source device
var sourceDev = DeviceManager.GetDeviceForKey(SourceKey) as IRoutingOutputs;
if (sourceDev == null)
@@ -48,68 +48,29 @@ namespace PepperDash.Essentials.Core.Config
}
//Get the source port
- RoutingOutputPort sourceOutputPort = null;
- //// If it's a card-based device, get the card and then the source port
- //if (sourceDev is ICardPortsDevice)
- //{
- // if (SourceCard == null)
- // {
- // LogError("Card missing from source device config");
- // return null;
- // }
- // sourceOutputPort = (sourceDev as ICardPortsDevice).GetChildOutputPort(SourceCard, SourcePort);
- // if (sourceOutputPort == null)
- // {
- // LogError("Source card does not contain port");
- // return null;
- // }
- //}
- //// otherwise it's a normal port device, get the source port
- //else
- //{
- sourceOutputPort = sourceDev.OutputPorts[SourcePort];
- if (sourceOutputPort == null)
- {
- LogError("Source does not contain port");
- return null;
- }
- //}
+ var sourceOutputPort = sourceDev.OutputPorts[SourcePort];
+ if (sourceOutputPort == null)
+ {
+ LogError("Source does not contain port");
+ return null;
+ }
- //Get the Destination port
- RoutingInputPort destinationInputPort = null;
- //// If it's a card-based device, get the card and then the Destination port
- //if (destDev is ICardPortsDevice)
- //{
- // if (DestinationCard == null)
- // {
- // LogError("Card missing from destination device config");
- // return null;
- // }
- // destinationInputPort = (destDev as ICardPortsDevice).GetChildInputPort(DestinationCard, DestinationPort);
- // if (destinationInputPort == null)
- // {
- // LogError("Destination card does not contain port");
- // return null;
- // }
- //}
- //// otherwise it's a normal port device, get the Destination port
- //else
- //{
- destinationInputPort = destDev.InputPorts[DestinationPort];
- if (destinationInputPort == null)
+ //Get the Destination port
+ var destinationInputPort = destDev.InputPorts[DestinationPort];
+
+ if (destinationInputPort == null)
{
LogError("Destination does not contain port");
return null;
- }
- //}
+ }
return new TieLine(sourceOutputPort, destinationInputPort);
}
void LogError(string msg)
{
- Debug.LogMessage(LogEventLevel.Debug, "WARNING: Cannot create tie line: {0}:\r {1}", msg, this);
+ Debug.LogMessage(LogEventLevel.Error, "WARNING: Cannot create tie line: {message}:\r {tieLineConfig}",null, msg, this);
}
public override string ToString()
diff --git a/src/PepperDash.Essentials.Devices.Common/Displays/DisplayBase.cs b/src/PepperDash.Essentials.Devices.Common/Displays/DisplayBase.cs
index 8dfa2f2b..33d5632a 100644
--- a/src/PepperDash.Essentials.Devices.Common/Displays/DisplayBase.cs
+++ b/src/PepperDash.Essentials.Devices.Common/Displays/DisplayBase.cs
@@ -20,6 +20,24 @@ namespace PepperDash.Essentials.Devices.Common.Displays
, IWarmingCooling
, IUsageTracking
{
+ private RoutingInputPort _currentInputPort;
+ public RoutingInputPort CurrentInputPort
+ {
+ get
+ {
+ return _currentInputPort;
+ }
+
+ private set
+ {
+ _currentInputPort = value;
+
+ InputChanged?.Invoke(this, _currentInputPort);
+ }
+ }
+
+ public event InputChangedEventHandler InputChanged;
+
public event SourceInfoChangeHandler CurrentSourceChange;
public string CurrentSourceInfoKey { get; set; }
diff --git a/src/PepperDash.Essentials.Devices.Common/Generic/GenericSource.cs b/src/PepperDash.Essentials.Devices.Common/Generic/GenericSource.cs
index f4062c98..c34f7d12 100644
--- a/src/PepperDash.Essentials.Devices.Common/Generic/GenericSource.cs
+++ b/src/PepperDash.Essentials.Devices.Common/Generic/GenericSource.cs
@@ -14,7 +14,7 @@ using Serilog.Events;
namespace PepperDash.Essentials.Devices.Common
{
- public class GenericSource : EssentialsDevice, IUiDisplayInfo, IRoutingOutputs, IUsageTracking
+ public class GenericSource : EssentialsDevice, IUiDisplayInfo, IRoutingSource, IUsageTracking
{
public uint DisplayUiType { get { return DisplayUiConstants.TypeNoControls; } }
diff --git a/src/PepperDash.Essentials.Devices.Common/SetTopBox/IRSetTopBoxBase.cs b/src/PepperDash.Essentials.Devices.Common/SetTopBox/IRSetTopBoxBase.cs
index 1728939d..9e334244 100644
--- a/src/PepperDash.Essentials.Devices.Common/SetTopBox/IRSetTopBoxBase.cs
+++ b/src/PepperDash.Essentials.Devices.Common/SetTopBox/IRSetTopBoxBase.cs
@@ -20,7 +20,7 @@ using Serilog.Events;
namespace PepperDash.Essentials.Devices.Common
{
[Description("Wrapper class for an IR Set Top Box")]
- public class IRSetTopBoxBase : EssentialsBridgeableDevice, ISetTopBoxControls, IRoutingOutputs, IUsageTracking, IHasPowerControl, ITvPresetsProvider
+ public class IRSetTopBoxBase : EssentialsBridgeableDevice, ISetTopBoxControls, IRoutingSource, IUsageTracking, IHasPowerControl, ITvPresetsProvider
{
public IrOutputPortController IrPort { get; private set; }
diff --git a/src/PepperDash.Essentials.Devices.Common/Sources/InRoomPc.cs b/src/PepperDash.Essentials.Devices.Common/Sources/InRoomPc.cs
index 565956f4..b3d8fbee 100644
--- a/src/PepperDash.Essentials.Devices.Common/Sources/InRoomPc.cs
+++ b/src/PepperDash.Essentials.Devices.Common/Sources/InRoomPc.cs
@@ -8,7 +8,7 @@ using Serilog.Events;
namespace PepperDash.Essentials.Devices.Common.Sources
{
- public class InRoomPc : EssentialsDevice, IHasFeedback, IRoutingOutputs, IAttachVideoStatus, IUiDisplayInfo, IUsageTracking
+ public class InRoomPc : EssentialsDevice, IHasFeedback, IRoutingSource, IAttachVideoStatus, IUiDisplayInfo, IUsageTracking
{
public uint DisplayUiType { get { return DisplayUiConstants.TypeLaptop; } }
public string IconName { get; set; }
diff --git a/src/PepperDash.Essentials.Devices.Common/Sources/Laptop.cs b/src/PepperDash.Essentials.Devices.Common/Sources/Laptop.cs
index 7aac6267..c2ffb726 100644
--- a/src/PepperDash.Essentials.Devices.Common/Sources/Laptop.cs
+++ b/src/PepperDash.Essentials.Devices.Common/Sources/Laptop.cs
@@ -8,7 +8,7 @@ using Serilog.Events;
namespace PepperDash.Essentials.Devices.Common.Sources
{
- public class Laptop : EssentialsDevice, IHasFeedback, IRoutingOutputs, IAttachVideoStatus, IUiDisplayInfo, IUsageTracking
+ public class Laptop : EssentialsDevice, IHasFeedback, IRoutingSource, IAttachVideoStatus, IUiDisplayInfo, IUsageTracking
{
public uint DisplayUiType { get { return DisplayUiConstants.TypeLaptop; } }
public string IconName { get; set; }
diff --git a/src/PepperDash.Essentials.Devices.Common/Streaming/AppleTV.cs b/src/PepperDash.Essentials.Devices.Common/Streaming/AppleTV.cs
index 5c8f9163..c4a9b5a7 100644
--- a/src/PepperDash.Essentials.Devices.Common/Streaming/AppleTV.cs
+++ b/src/PepperDash.Essentials.Devices.Common/Streaming/AppleTV.cs
@@ -19,7 +19,7 @@ using Serilog.Events;
namespace PepperDash.Essentials.Devices.Common
{
[Description("Wrapper class for an IR-Controlled AppleTV")]
- public class AppleTV : EssentialsBridgeableDevice, IDPad, ITransport, IUiDisplayInfo, IRoutingOutputs
+ public class AppleTV : EssentialsBridgeableDevice, IDPad, ITransport, IUiDisplayInfo, IRoutingSource
{
public IrOutputPortController IrPort { get; private set; }
public const string StandardDriverName = "Apple_AppleTV_4th_Gen_Essentials.ir";
diff --git a/src/PepperDash.Essentials.Devices.Common/Streaming/Roku.cs b/src/PepperDash.Essentials.Devices.Common/Streaming/Roku.cs
index 4364610b..9d08f53a 100644
--- a/src/PepperDash.Essentials.Devices.Common/Streaming/Roku.cs
+++ b/src/PepperDash.Essentials.Devices.Common/Streaming/Roku.cs
@@ -15,7 +15,7 @@ using Serilog.Events;
namespace PepperDash.Essentials.Devices.Common
{
[Description("Wrapper class for an IR-Controlled Roku")]
- public class Roku2 : EssentialsDevice, IDPad, ITransport, IUiDisplayInfo, IRoutingOutputs
+ public class Roku2 : EssentialsDevice, IDPad, ITransport, IUiDisplayInfo, IRoutingSource
{
[Api]
public IrOutputPortController IrPort { get; private set; }
diff --git a/src/PepperDash.Essentials/ControlSystem.cs b/src/PepperDash.Essentials/ControlSystem.cs
index af87158b..d2b7a2b9 100644
--- a/src/PepperDash.Essentials/ControlSystem.cs
+++ b/src/PepperDash.Essentials/ControlSystem.cs
@@ -14,6 +14,7 @@ using PepperDash.Essentials.Core.Web;
using System;
using System.Linq;
using Serilog.Events;
+using PepperDash.Essentials.Core.Routing;
namespace PepperDash.Essentials
{
@@ -388,6 +389,8 @@ namespace PepperDash.Essentials
// Build the processor wrapper class
DeviceManager.AddDevice(new Core.Devices.CrestronProcessor("processor"));
+ DeviceManager.AddDevice(new RoutingFeedbackManager($"routingFeedbackManager", "Routing Feedback Manager"));
+
// Add global System Monitor device
if (CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance)
{