diff --git a/PepperDashEssentials/AppServer/DeviceTypeInterfaces/IChannelExtensions.cs b/PepperDashEssentials/AppServer/DeviceTypeInterfaces/IChannelExtensions.cs
index dd23d85a..287a9ba1 100644
--- a/PepperDashEssentials/AppServer/DeviceTypeInterfaces/IChannelExtensions.cs
+++ b/PepperDashEssentials/AppServer/DeviceTypeInterfaces/IChannelExtensions.cs
@@ -6,11 +6,11 @@ using Crestron.SimplSharp;
using PepperDash.Essentials.Core;
using PepperDash.Core;
-namespace PepperDash.Essentials.Room.Cotija
+namespace PepperDash.Essentials.Room.MobileControl
{
public static class IChannelExtensions
{
- public static void LinkActions(this IChannel dev, CotijaSystemController controller)
+ public static void LinkActions(this IChannel dev, MobileControlSystemController controller)
{
var prefix = string.Format(@"/device/{0}/", (dev as IKeyed).Key);
@@ -22,7 +22,7 @@ namespace PepperDash.Essentials.Room.Cotija
controller.AddAction(prefix + "exit", new PressAndHoldAction(dev.Exit));
}
- public static void UnlinkActions(this IChannel dev, CotijaSystemController controller)
+ public static void UnlinkActions(this IChannel dev, MobileControlSystemController controller)
{
var prefix = string.Format(@"/device/{0}/", (dev as IKeyed).Key);
diff --git a/PepperDashEssentials/AppServer/DeviceTypeInterfaces/IColorExtensions.cs b/PepperDashEssentials/AppServer/DeviceTypeInterfaces/IColorExtensions.cs
index 21296550..9912433d 100644
--- a/PepperDashEssentials/AppServer/DeviceTypeInterfaces/IColorExtensions.cs
+++ b/PepperDashEssentials/AppServer/DeviceTypeInterfaces/IColorExtensions.cs
@@ -6,11 +6,11 @@ using Crestron.SimplSharp;
using PepperDash.Essentials.Core;
using PepperDash.Core;
-namespace PepperDash.Essentials.Room.Cotija
+namespace PepperDash.Essentials.Room.MobileControl
{
public static class IColorExtensions
{
- public static void LinkActions(this IColor dev, CotijaSystemController controller)
+ public static void LinkActions(this IColor dev, MobileControlSystemController controller)
{
var prefix = string.Format(@"/device/{0}/", (dev as IKeyed).Key);
@@ -20,7 +20,7 @@ namespace PepperDash.Essentials.Room.Cotija
controller.AddAction(prefix + "blue", new PressAndHoldAction(dev.Blue));
}
- public static void UnlinkActions(this IColor dev, CotijaSystemController controller)
+ public static void UnlinkActions(this IColor dev, MobileControlSystemController controller)
{
var prefix = string.Format(@"/device/{0}/", (dev as IKeyed).Key);
diff --git a/PepperDashEssentials/AppServer/DeviceTypeInterfaces/IDPadExtensions.cs b/PepperDashEssentials/AppServer/DeviceTypeInterfaces/IDPadExtensions.cs
index 3652ba68..b094dfe8 100644
--- a/PepperDashEssentials/AppServer/DeviceTypeInterfaces/IDPadExtensions.cs
+++ b/PepperDashEssentials/AppServer/DeviceTypeInterfaces/IDPadExtensions.cs
@@ -6,11 +6,11 @@ using Crestron.SimplSharp;
using PepperDash.Essentials.Core;
using PepperDash.Core;
-namespace PepperDash.Essentials.Room.Cotija
+namespace PepperDash.Essentials.Room.MobileControl
{
public static class IDPadExtensions
{
- public static void LinkActions(this IDPad dev, CotijaSystemController controller)
+ public static void LinkActions(this IDPad dev, MobileControlSystemController controller)
{
var prefix = string.Format(@"/device/{0}/", (dev as IKeyed).Key);
@@ -23,7 +23,7 @@ namespace PepperDash.Essentials.Room.Cotija
controller.AddAction(prefix + "exit", new PressAndHoldAction(dev.Exit));
}
- public static void UnlinkActions(this IDPad dev, CotijaSystemController controller)
+ public static void UnlinkActions(this IDPad dev, MobileControlSystemController controller)
{
var prefix = string.Format(@"/device/{0}/", (dev as IKeyed).Key);
diff --git a/PepperDashEssentials/AppServer/DeviceTypeInterfaces/IDvrExtensions.cs b/PepperDashEssentials/AppServer/DeviceTypeInterfaces/IDvrExtensions.cs
index 99aa1103..d4a9570b 100644
--- a/PepperDashEssentials/AppServer/DeviceTypeInterfaces/IDvrExtensions.cs
+++ b/PepperDashEssentials/AppServer/DeviceTypeInterfaces/IDvrExtensions.cs
@@ -6,11 +6,11 @@ using Crestron.SimplSharp;
using PepperDash.Essentials.Core;
using PepperDash.Core;
-namespace PepperDash.Essentials.Room.Cotija
+namespace PepperDash.Essentials.Room.MobileControl
{
public static class IDvrExtensions
{
- public static void LinkActions(this IDvr dev, CotijaSystemController controller)
+ public static void LinkActions(this IDvr dev, MobileControlSystemController controller)
{
var prefix = string.Format(@"/device/{0}/", (dev as IKeyed).Key);
@@ -18,7 +18,7 @@ namespace PepperDash.Essentials.Room.Cotija
controller.AddAction(prefix + "record", new PressAndHoldAction(dev.Record));
}
- public static void UnlinkActions(this IDvr dev, CotijaSystemController controller)
+ public static void UnlinkActions(this IDvr dev, MobileControlSystemController controller)
{
var prefix = string.Format(@"/device/{0}/", (dev as IKeyed).Key);
diff --git a/PepperDashEssentials/AppServer/DeviceTypeInterfaces/INumericExtensions.cs b/PepperDashEssentials/AppServer/DeviceTypeInterfaces/INumericExtensions.cs
index 376ed57b..2b61d775 100644
--- a/PepperDashEssentials/AppServer/DeviceTypeInterfaces/INumericExtensions.cs
+++ b/PepperDashEssentials/AppServer/DeviceTypeInterfaces/INumericExtensions.cs
@@ -6,11 +6,11 @@ using Crestron.SimplSharp;
using PepperDash.Essentials.Core;
using PepperDash.Core;
-namespace PepperDash.Essentials.Room.Cotija
+namespace PepperDash.Essentials.Room.MobileControl
{
public static class INumericExtensions
{
- public static void LinkActions(this INumericKeypad dev, CotijaSystemController controller)
+ public static void LinkActions(this INumericKeypad dev, MobileControlSystemController controller)
{
var prefix = string.Format(@"/device/{0}/", (dev as IKeyed).Key);
@@ -29,7 +29,7 @@ namespace PepperDash.Essentials.Room.Cotija
// Deal with the Accessory functions on the numpad later
}
- public static void UnlinkActions(this INumericKeypad dev, CotijaSystemController controller)
+ public static void UnlinkActions(this INumericKeypad dev, MobileControlSystemController controller)
{
var prefix = string.Format(@"/device/{0}/", (dev as IKeyed).Key);
diff --git a/PepperDashEssentials/AppServer/DeviceTypeInterfaces/IPowerExtensions.cs b/PepperDashEssentials/AppServer/DeviceTypeInterfaces/IPowerExtensions.cs
index 732d2740..515c2c7f 100644
--- a/PepperDashEssentials/AppServer/DeviceTypeInterfaces/IPowerExtensions.cs
+++ b/PepperDashEssentials/AppServer/DeviceTypeInterfaces/IPowerExtensions.cs
@@ -6,11 +6,11 @@ using Crestron.SimplSharp;
using PepperDash.Essentials.Core;
using PepperDash.Core;
-namespace PepperDash.Essentials.Room.Cotija
+namespace PepperDash.Essentials.Room.MobileControl
{
public static class IPowerExtensions
{
- public static void LinkActions(this IPower dev, CotijaSystemController controller)
+ public static void LinkActions(this IPower dev, MobileControlSystemController controller)
{
var prefix = string.Format(@"/device/{0}/", (dev as IKeyed).Key);
@@ -19,7 +19,7 @@ namespace PepperDash.Essentials.Room.Cotija
controller.AddAction(prefix + "powerToggle", new Action(dev.PowerToggle));
}
- public static void UnlinkActions(this IPower dev, CotijaSystemController controller)
+ public static void UnlinkActions(this IPower dev, MobileControlSystemController controller)
{
var prefix = string.Format(@"/device/{0}/", (dev as IKeyed).Key);
diff --git a/PepperDashEssentials/AppServer/DeviceTypeInterfaces/ISetTopBoxControlsExtensions.cs b/PepperDashEssentials/AppServer/DeviceTypeInterfaces/ISetTopBoxControlsExtensions.cs
index 99198fa6..2ea60545 100644
--- a/PepperDashEssentials/AppServer/DeviceTypeInterfaces/ISetTopBoxControlsExtensions.cs
+++ b/PepperDashEssentials/AppServer/DeviceTypeInterfaces/ISetTopBoxControlsExtensions.cs
@@ -6,11 +6,11 @@ using Crestron.SimplSharp;
using PepperDash.Essentials.Core;
using PepperDash.Core;
-namespace PepperDash.Essentials.Room.Cotija
+namespace PepperDash.Essentials.Room.MobileControl
{
public static class ISetTopBoxControlsExtensions
{
- public static void LinkActions(this ISetTopBoxControls dev, CotijaSystemController controller)
+ public static void LinkActions(this ISetTopBoxControls dev, MobileControlSystemController controller)
{
var prefix = string.Format(@"/device/{0}/", (dev as IKeyed).Key);
@@ -18,7 +18,7 @@ namespace PepperDash.Essentials.Room.Cotija
controller.AddAction(prefix + "replay", new PressAndHoldAction(dev.Replay));
}
- public static void UnlinkActions(this ISetTopBoxControls dev, CotijaSystemController controller)
+ public static void UnlinkActions(this ISetTopBoxControls dev, MobileControlSystemController controller)
{
var prefix = string.Format(@"/device/{0}/", (dev as IKeyed).Key);
diff --git a/PepperDashEssentials/AppServer/DeviceTypeInterfaces/ITransportExtensions.cs b/PepperDashEssentials/AppServer/DeviceTypeInterfaces/ITransportExtensions.cs
index 9463d95f..f34fb9e3 100644
--- a/PepperDashEssentials/AppServer/DeviceTypeInterfaces/ITransportExtensions.cs
+++ b/PepperDashEssentials/AppServer/DeviceTypeInterfaces/ITransportExtensions.cs
@@ -6,11 +6,11 @@ using Crestron.SimplSharp;
using PepperDash.Essentials.Core;
using PepperDash.Core;
-namespace PepperDash.Essentials.Room.Cotija
+namespace PepperDash.Essentials.Room.MobileControl
{
public static class ITransportExtensions
{
- public static void LinkActions(this ITransport dev, CotijaSystemController controller)
+ public static void LinkActions(this ITransport dev, MobileControlSystemController controller)
{
var prefix = string.Format(@"/device/{0}/", (dev as IKeyed).Key);
@@ -24,7 +24,7 @@ namespace PepperDash.Essentials.Room.Cotija
controller.AddAction(prefix + "record", new PressAndHoldAction(dev.Record));
}
- public static void UnlinkActions(this ITransport dev, CotijaSystemController controller)
+ public static void UnlinkActions(this ITransport dev, MobileControlSystemController controller)
{
var prefix = string.Format(@"/device/{0}/", (dev as IKeyed).Key);
diff --git a/PepperDashEssentials/AppServer/Interfaces.cs b/PepperDashEssentials/AppServer/Interfaces.cs
index df651ef8..8a1c20c8 100644
--- a/PepperDashEssentials/AppServer/Interfaces.cs
+++ b/PepperDashEssentials/AppServer/Interfaces.cs
@@ -6,7 +6,7 @@ using Crestron.SimplSharp;
using PepperDash.Essentials.Core;
-namespace PepperDash.Essentials.Room.Cotija
+namespace PepperDash.Essentials.Room.MobileControl
{
///
/// Represents a room whose configuration is derived from runtime data,
@@ -19,42 +19,3 @@ namespace PepperDash.Essentials.Room.Cotija
}
}
-namespace PepperDash.Essentials
-{
- ///
- /// For rooms with a single presentation source, change event
- ///
- public interface IHasCurrentSourceInfoChange
- {
- string CurrentSourceInfoKey { get; }
- SourceListItem CurrentSourceInfo { get; }
- event SourceInfoChangeHandler CurrentSingleSourceChange;
- }
-
-
- ///
- /// For rooms with routing
- ///
- public interface IRunRouteAction
- {
- void RunRouteAction(string routeKey);
-
- void RunRouteAction(string routeKey, Action successCallback);
- }
-
- ///
- /// For rooms that default presentation only routing
- ///
- public interface IRunDefaultPresentRoute
- {
- bool RunDefaultPresentRoute();
- }
-
- ///
- /// For rooms that have default presentation and calling routes
- ///
- public interface IRunDefaultCallRoute : IRunDefaultPresentRoute
- {
- bool RunDefaultCallRoute();
- }
-}
\ No newline at end of file
diff --git a/PepperDashEssentials/AppServer/Messengers/AudioCodecBaseMessenger.cs b/PepperDashEssentials/AppServer/Messengers/AudioCodecBaseMessenger.cs
index b37b80da..3d74b132 100644
--- a/PepperDashEssentials/AppServer/Messengers/AudioCodecBaseMessenger.cs
+++ b/PepperDashEssentials/AppServer/Messengers/AudioCodecBaseMessenger.cs
@@ -39,7 +39,7 @@ namespace PepperDash.Essentials.AppServer.Messengers
}
- protected override void CustomRegisterWithAppServer(CotijaSystemController appServerController)
+ protected override void CustomRegisterWithAppServer(MobileControlSystemController appServerController)
{
appServerController.AddAction(MessagePath + "/fullStatus", new Action(SendAtcFullMessageObject));
appServerController.AddAction(MessagePath + "/dial", new Action(s => Codec.Dial(s)));
diff --git a/PepperDashEssentials/AppServer/Messengers/ConfigMessenger.cs b/PepperDashEssentials/AppServer/Messengers/ConfigMessenger.cs
index 1e3cfa12..ae05c118 100644
--- a/PepperDashEssentials/AppServer/Messengers/ConfigMessenger.cs
+++ b/PepperDashEssentials/AppServer/Messengers/ConfigMessenger.cs
@@ -27,7 +27,7 @@ namespace PepperDash.Essentials.AppServer.Messengers
PostUpdateStatus(e.UpdateStatus.ToString());
}
- protected override void CustomRegisterWithAppServer(CotijaSystemController appServerController)
+ protected override void CustomRegisterWithAppServer(MobileControlSystemController appServerController)
{
appServerController.AddAction(MessagePath + "/updateConfig", new Action(s => GetConfigFile(s)));
}
diff --git a/PepperDashEssentials/AppServer/Messengers/Ddvc01AtcMessenger.cs b/PepperDashEssentials/AppServer/Messengers/Ddvc01AtcMessenger.cs
index 72e2f8d3..eee94350 100644
--- a/PepperDashEssentials/AppServer/Messengers/Ddvc01AtcMessenger.cs
+++ b/PepperDashEssentials/AppServer/Messengers/Ddvc01AtcMessenger.cs
@@ -127,7 +127,7 @@ namespace PepperDash.Essentials.AppServer.Messengers
///
///
///
- protected override void CustomRegisterWithAppServer(CotijaSystemController appServerController)
+ protected override void CustomRegisterWithAppServer(MobileControlSystemController appServerController)
{
//EISC.SetStringSigAction(SCurrentDialString, s => PostStatusMessage(new { currentDialString = s }));
diff --git a/PepperDashEssentials/AppServer/Messengers/Ddvc01VtcMessenger.cs b/PepperDashEssentials/AppServer/Messengers/Ddvc01VtcMessenger.cs
index 7fb53f96..e3de11d0 100644
--- a/PepperDashEssentials/AppServer/Messengers/Ddvc01VtcMessenger.cs
+++ b/PepperDashEssentials/AppServer/Messengers/Ddvc01VtcMessenger.cs
@@ -253,7 +253,7 @@ namespace PepperDash.Essentials.AppServer.Messengers
///
///
///
- protected override void CustomRegisterWithAppServer(CotijaSystemController appServerController)
+ protected override void CustomRegisterWithAppServer(MobileControlSystemController appServerController)
{
var asc = appServerController;
EISC.SetStringSigAction(SHookState, s =>
diff --git a/PepperDashEssentials/AppServer/Messengers/MessengerBase.cs b/PepperDashEssentials/AppServer/Messengers/MessengerBase.cs
index f9833afb..d4967eee 100644
--- a/PepperDashEssentials/AppServer/Messengers/MessengerBase.cs
+++ b/PepperDashEssentials/AppServer/Messengers/MessengerBase.cs
@@ -24,7 +24,7 @@ namespace PepperDash.Essentials.AppServer.Messengers
///
///
///
- public CotijaSystemController AppServerController { get; private set; }
+ public MobileControlSystemController AppServerController { get; private set; }
public string MessagePath { get; private set; }
@@ -47,7 +47,7 @@ namespace PepperDash.Essentials.AppServer.Messengers
/// Registers this messenger with appserver controller
///
///
- public void RegisterWithAppServer(CotijaSystemController appServerController)
+ public void RegisterWithAppServer(MobileControlSystemController appServerController)
{
if (appServerController == null)
throw new ArgumentNullException("appServerController");
@@ -60,7 +60,7 @@ namespace PepperDash.Essentials.AppServer.Messengers
/// Implemented in extending classes. Wire up API calls and feedback here
///
///
- abstract protected void CustomRegisterWithAppServer(CotijaSystemController appServerController);
+ abstract protected void CustomRegisterWithAppServer(MobileControlSystemController appServerController);
///
/// Helper for posting status message
diff --git a/PepperDashEssentials/AppServer/Messengers/SystemMonitorMessenger.cs b/PepperDashEssentials/AppServer/Messengers/SystemMonitorMessenger.cs
index 12107ff5..080fbd78 100644
--- a/PepperDashEssentials/AppServer/Messengers/SystemMonitorMessenger.cs
+++ b/PepperDashEssentials/AppServer/Messengers/SystemMonitorMessenger.cs
@@ -86,7 +86,7 @@ namespace PepperDash.Essentials.AppServer.Messengers
});
}
- protected override void CustomRegisterWithAppServer(CotijaSystemController appServerController)
+ protected override void CustomRegisterWithAppServer(MobileControlSystemController appServerController)
{
AppServerController.AddAction(MessagePath + "/fullStatus", new Action(SendFullStatusMessage));
}
diff --git a/PepperDashEssentials/AppServer/Messengers/VideoCodecBaseMessenger.cs b/PepperDashEssentials/AppServer/Messengers/VideoCodecBaseMessenger.cs
index 5c1c59b7..29c80085 100644
--- a/PepperDashEssentials/AppServer/Messengers/VideoCodecBaseMessenger.cs
+++ b/PepperDashEssentials/AppServer/Messengers/VideoCodecBaseMessenger.cs
@@ -160,7 +160,7 @@ namespace PepperDash.Essentials.AppServer.Messengers
/// Called from base's RegisterWithAppServer method
///
///
- protected override void CustomRegisterWithAppServer(CotijaSystemController appServerController)
+ protected override void CustomRegisterWithAppServer(MobileControlSystemController appServerController)
{
appServerController.AddAction("/device/videoCodec/isReady", new Action(SendIsReady));
appServerController.AddAction("/device/videoCodec/fullStatus", new Action(SendVtcFullMessageObject));
diff --git a/PepperDashEssentials/AppServer/CotijaConfig.cs b/PepperDashEssentials/AppServer/MobileControlConfig.cs
similarity index 84%
rename from PepperDashEssentials/AppServer/CotijaConfig.cs
rename to PepperDashEssentials/AppServer/MobileControlConfig.cs
index 22196956..6b775a0a 100644
--- a/PepperDashEssentials/AppServer/CotijaConfig.cs
+++ b/PepperDashEssentials/AppServer/MobileControlConfig.cs
@@ -13,7 +13,7 @@ namespace PepperDash.Essentials
///
///
///
- public class CotijaConfig
+ public class MobileControlConfig
{
[JsonProperty("serverUrl")]
public string ServerUrl { get; set; }
@@ -25,7 +25,7 @@ namespace PepperDash.Essentials
///
///
///
- public class CotijaDdvc01RoomBridgePropertiesConfig
+ public class MobileControlDdvc01RoomBridgePropertiesConfig
{
[JsonProperty("eiscId")]
public string EiscId { get; set; }
diff --git a/PepperDashEssentials/AppServer/CotijaDdvc01DeviceBridge.cs b/PepperDashEssentials/AppServer/MobileControlDdvc01DeviceBridge.cs
similarity index 91%
rename from PepperDashEssentials/AppServer/CotijaDdvc01DeviceBridge.cs
rename to PepperDashEssentials/AppServer/MobileControlDdvc01DeviceBridge.cs
index 51c0bb5b..49a75263 100644
--- a/PepperDashEssentials/AppServer/CotijaDdvc01DeviceBridge.cs
+++ b/PepperDashEssentials/AppServer/MobileControlDdvc01DeviceBridge.cs
@@ -8,19 +8,19 @@ using Crestron.SimplSharpPro.EthernetCommunication;
using PepperDash.Core;
using PepperDash.Essentials.Core;
-namespace PepperDash.Essentials.Room.Cotija
+namespace PepperDash.Essentials.Room.MobileControl
{
///
/// Represents a generic device connection through to and EISC for DDVC01
///
- public class CotijaDdvc01DeviceBridge : Device, IChannel, INumericKeypad
+ public class MobileControlDdvc01DeviceBridge : Device, IChannel, INumericKeypad
{
///
/// EISC used to talk to Simpl
///
ThreeSeriesTcpIpEthernetIntersystemCommunications EISC;
- public CotijaDdvc01DeviceBridge(string key, string name, ThreeSeriesTcpIpEthernetIntersystemCommunications eisc)
+ public MobileControlDdvc01DeviceBridge(string key, string name, ThreeSeriesTcpIpEthernetIntersystemCommunications eisc)
: base(key, name)
{
EISC = eisc;
diff --git a/PepperDashEssentials/AppServer/CotijaSystemController.cs b/PepperDashEssentials/AppServer/MobileControlSystemController.cs
similarity index 95%
rename from PepperDashEssentials/AppServer/CotijaSystemController.cs
rename to PepperDashEssentials/AppServer/MobileControlSystemController.cs
index 1e74efe0..08afb464 100644
--- a/PepperDashEssentials/AppServer/CotijaSystemController.cs
+++ b/PepperDashEssentials/AppServer/MobileControlSystemController.cs
@@ -1,864 +1,864 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Text.RegularExpressions;
-using Crestron.SimplSharp;
-using Crestron.SimplSharp.CrestronIO;
-using Crestron.SimplSharp.Reflection;
-using Crestron.SimplSharpPro.CrestronThread;
-using Crestron.SimplSharp.CrestronWebSocketClient;
-using Crestron.SimplSharpPro;
-using Crestron.SimplSharp.Net.Http;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
-
-using PepperDash.Core;
-using PepperDash.Essentials.Core;
-using PepperDash.Essentials.Core.Config;
-using PepperDash.Essentials.Room.Cotija;
-using PepperDash.Essentials.AppServer.Messengers;
-
-namespace PepperDash.Essentials
-{
- public class CotijaSystemController : Device
- {
- WebSocketClient WSClient;
-
- //bool LinkUp;
-
- ///
- /// Prevents post operations from stomping on each other and getting lost
- ///
- CEvent PostLockEvent = new CEvent(true, true);
-
- CEvent RegisterLockEvent = new CEvent(true, true);
-
- public CotijaConfig Config { get; private set; }
-
- Dictionary ActionDictionary = new Dictionary(StringComparer.InvariantCultureIgnoreCase);
-
- Dictionary PushedActions = new Dictionary();
-
- public ConfigMessenger ConfigMessenger { get; private set; }
-
- CTimer ServerHeartbeatCheckTimer;
-
- long ServerHeartbeatInterval = 20000;
-
- CTimer ServerReconnectTimer;
-
- long ServerReconnectInterval = 5000;
-
- DateTime LastAckMessage;
-
- public string SystemUuid;
-
- List RoomBridges = new List();
-
- long ButtonHeartbeatInterval = 1000;
-
- ///
- /// Used for tracking HTTP debugging
- ///
- bool HttpDebugEnabled;
-
- ///
- ///
- ///
- ///
- ///
- ///
- public CotijaSystemController(string key, string name, CotijaConfig config) : base(key, name)
- {
- Config = config;
-
- SystemUuid = ConfigReader.ConfigObject.SystemUuid;
-
- Debug.Console(0, this, "Mobile UI controller initializing for server:{0}", config.ServerUrl);
-
- CrestronConsole.AddNewConsoleCommand(AuthorizeSystem,
- "mobileauth", "Authorizes system to talk to cotija server", ConsoleAccessLevelEnum.AccessOperator);
- CrestronConsole.AddNewConsoleCommand(s => ShowInfo(),
- "mobileinfo", "Shows information for current mobile control session", ConsoleAccessLevelEnum.AccessOperator);
- CrestronConsole.AddNewConsoleCommand(s => {
- s = s.Trim();
- if(!string.IsNullOrEmpty(s))
- {
- HttpDebugEnabled = (s.Trim() != "0");
- }
- CrestronConsole.ConsoleCommandResponse("HTTP Debug {0}", HttpDebugEnabled ? "Enabled" : "Disabled");
- },
- "mobilehttpdebug", "1 enables more verbose HTTP response debugging", ConsoleAccessLevelEnum.AccessOperator);
- CrestronConsole.AddNewConsoleCommand(TestHttpRequest,
- "mobilehttprequest", "Tests an HTTP get to URL given", ConsoleAccessLevelEnum.AccessOperator);
-
- CrestronConsole.AddNewConsoleCommand(PrintActionDictionaryPaths, "mobileshowactionpaths",
- "Prints the paths in the Action Dictionary", ConsoleAccessLevelEnum.AccessOperator);
- CrestronConsole.AddNewConsoleCommand(s => ConnectWebsocketClient(), "mobileconnect",
- "Forces connect of websocket", ConsoleAccessLevelEnum.AccessOperator);
- CrestronConsole.AddNewConsoleCommand(s => CleanUpWebsocketClient(), "mobiledisco",
- "Disconnects websocket", ConsoleAccessLevelEnum.AccessOperator);
-
- CrestronConsole.AddNewConsoleCommand(s => ParseStreamRx(s), "mobilesimulateaction", "Simulates a message from the server", ConsoleAccessLevelEnum.AccessOperator);
-
- CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
- CrestronEnvironment.EthernetEventHandler += new EthernetEventHandler(CrestronEnvironment_EthernetEventHandler);
-
- // Config Messenger
- var cmKey = Key + "-config";
- ConfigMessenger = new ConfigMessenger(cmKey, "/config");
- ConfigMessenger.RegisterWithAppServer(this);
- }
-
- ///
- /// If config rooms is empty or null then go
- ///
- ///
- public override bool CustomActivate()
- {
- if (ConfigReader.ConfigObject.Rooms == null || ConfigReader.ConfigObject.Rooms.Count == 0)
- {
- Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Config contains no rooms. Registering with Server.");
- RegisterSystemToServer();
- }
-
- return base.CustomActivate();
- }
-
- ///
- ///
- ///
- ///
- void CrestronEnvironment_EthernetEventHandler(EthernetEventArgs args)
- {
- Debug.Console(1, this, Debug.ErrorLogLevel.Warning, "Ethernet status change, port {0}: {1}",
- args.EthernetAdapter, args.EthernetEventType);
-
- if (args.EthernetEventType == eEthernetEventType.LinkDown && WSClient != null && args.EthernetAdapter == WSClient.EthernetAdapter)
- {
- CleanUpWebsocketClient();
- }
- }
-
- ///
- /// Sends message to server to indicate the system is shutting down
- ///
- ///
- void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType)
- {
- if (programEventType == eProgramStatusEventType.Stopping
- && WSClient != null
- && WSClient.Connected)
- {
- CleanUpWebsocketClient();
- }
- }
-
- public void PrintActionDictionaryPaths(object o)
- {
- Debug.Console(0, this, "ActionDictionary Contents:");
-
- foreach (var item in ActionDictionary)
- {
- Debug.Console(0, this, "{0}", item.Key);
- }
- }
-
- ///
- /// Adds an action to the dictionary
- ///
- /// The path of the API command
- /// The action to be triggered by the commmand
- public void AddAction(string key, object action)
- {
- if (!ActionDictionary.ContainsKey(key))
- {
- ActionDictionary.Add(key, action);
- }
- else
- {
- Debug.Console(1, this, "Cannot add action with key '{0}' because key already exists in ActionDictionary.", key);
- }
- }
-
- ///
- /// Removes an action from the dictionary
- ///
- ///
- public void RemoveAction(string key)
- {
- if (ActionDictionary.ContainsKey(key))
- ActionDictionary.Remove(key);
- }
-
- ///
- ///
- ///
- ///
- public void AddBridge(CotijaBridgeBase bridge)
- {
- RoomBridges.Add(bridge);
- var b = bridge as IDelayedConfiguration;
- if (b != null)
- {
- Debug.Console(0, this, "Adding room bridge with delayed configuration");
- b.ConfigurationIsReady += new EventHandler(bridge_ConfigurationIsReady);
- }
- else
- {
- Debug.Console(0, this, "Adding room bridge and sending configuration");
- //SystemUuid = ConfigReader.ConfigObject.SystemUuid;
- RegisterSystemToServer();
- }
- }
-
- ///
- ///
- ///
- ///
- ///
- void bridge_ConfigurationIsReady(object sender, EventArgs e)
- {
- Debug.Console(1, this, "Bridge ready. Registering");
- //SystemUuid = ConfigReader.ConfigObject.SystemUuid;
- // send the configuration object to the server
- RegisterSystemToServer();
- }
-
- ///
- ///
- ///
- ///
- void ReconnectToServerTimerCallback(object o)
- {
- RegisterSystemToServer();
- }
-
- ///
- /// Verifies system connection with servers
- ///
- ///
- void AuthorizeSystem(string code)
- {
- //SystemUuid = ConfigReader.ConfigObject.SystemUuid;
-
- if (string.IsNullOrEmpty(SystemUuid))
- {
- CrestronConsole.ConsoleCommandResponse("System does not have a UUID. Please ensure proper portal-format configuration is loaded and restart.");
- return;
- }
-
- if (string.IsNullOrEmpty(code))
- {
- CrestronConsole.ConsoleCommandResponse("Please enter a user code to authorize a system");
- return;
- }
-
- var req = new HttpClientRequest();
- string url = string.Format("http://{0}/api/system/grantcode/{1}/{2}", Config.ServerUrl, code, SystemUuid);
- Debug.Console(0, this, "Authorizing to: {0}", url);
-
- if (string.IsNullOrEmpty(Config.ServerUrl))
- {
- CrestronConsole.ConsoleCommandResponse("Config URL address is not set. Check portal configuration");
- return;
- }
- try
- {
- req.Url.Parse(url);
- new HttpClient().DispatchAsync(req, (r, e) =>
- {
- CheckHttpDebug(r, e);
- if (e == HTTP_CALLBACK_ERROR.COMPLETED)
- {
- if (r.Code == 200)
- {
- Debug.Console(0, "System authorized, sending config.");
-#warning This registration may need to wait for config ready. Maybe.
- RegisterSystemToServer();
- }
- else if (r.Code == 404)
- {
- if (r.ContentString.Contains("codeNotFound"))
- {
- Debug.Console(0, "Authorization failed, code not found for system UUID {0}", SystemUuid);
- }
- else if (r.ContentString.Contains("uuidNotFound"))
- {
- Debug.Console(0, "Authorization failed, uuid {0} not found. Check Essentials configuration is correct",
- SystemUuid);
- }
- }
- }
- else
- Debug.Console(0, this, "Error {0} in authorizing system", e);
- });
- }
- catch (Exception e)
- {
- Debug.Console(0, this, "Error in authorizing: {0}", e);
- }
- }
-
- ///
- /// Dumps info in response to console command.
- ///
- void ShowInfo()
- {
- var url = Config != null ? Config.ServerUrl : "No config";
- string name;
- string code;
- if (RoomBridges != null && RoomBridges.Count > 0)
- {
- name = RoomBridges[0].RoomName;
- code = RoomBridges[0].UserCode;
- }
- else
- {
- name = "No config";
- code = "Not available";
- }
- var conn = WSClient == null ? "No client" : (WSClient.Connected ? "Yes" : "No");
- var secSinceLastAck = DateTime.Now - LastAckMessage;
-
-
- CrestronConsole.ConsoleCommandResponse(@"Mobile Control Information:
- Server address: {0}
- System Name: {1}
- System URL: {2}
- System UUID: {3}
- System User code: {4}
- Connected?: {5}
- Seconds Since Last Ack: {6}"
- , url, name, ConfigReader.ConfigObject.SystemUrl, SystemUuid,
- code, conn, secSinceLastAck.Seconds);
- }
-
- ///
- /// Registers the room with the server
- ///
- /// URL of the server, including the port number, if not 80. Format: "serverUrlOrIp:port"
- void RegisterSystemToServer()
- {
- ConnectWebsocketClient();
- }
-
- ///
- /// Connects the Websocket Client
- ///
- ///
- void ConnectWebsocketClient()
- {
-
- Debug.Console(1, this, "Initializing Stream client to server.");
-
- if (WSClient != null)
- {
- Debug.Console(1, this, "Cleaning up previous socket");
- CleanUpWebsocketClient();
- }
-
- WSClient = new WebSocketClient();
- WSClient.URL = string.Format("wss://{0}/system/join/{1}", Config.ServerUrl, this.SystemUuid);
- WSClient.ConnectionCallBack = Websocket_ConnectCallback;
- WSClient.ConnectAsync();
- }
-
- ///
- ///
- ///
- ///
- ///
- int Websocket_ConnectCallback(WebSocketClient.WEBSOCKET_RESULT_CODES code)
- {
- if (code == WebSocketClient.WEBSOCKET_RESULT_CODES.WEBSOCKET_CLIENT_SUCCESS)
- {
- StopServerReconnectTimer();
- Debug.Console(1, this, "Websocket connected");
- WSClient.DisconnectCallBack = Websocket_DisconnectCallback;
- WSClient.SendCallBack = Websocket_SendCallback;
- WSClient.ReceiveCallBack = Websocket_ReceiveCallback;
- WSClient.ReceiveAsync();
- SendMessageObjectToServer(new
- {
- type = "hello"
- });
- }
- else
- {
- if (code == WebSocketClient.WEBSOCKET_RESULT_CODES.WEBSOCKET_CLIENT_HTTP_HANDSHAKE_TOKEN_ERROR)
- {
- // This is the case when app server is running behind a websever and app server is down
- Debug.Console(1, this, Debug.ErrorLogLevel.Warning, "Web socket connection failed. Check that app server is running behind web server");
- }
- else if (code == WebSocketClient.WEBSOCKET_RESULT_CODES.WEBSOCKET_CLIENT_SOCKET_CONNECTION_FAILED)
- {
- // this will be the case when webserver is unreachable
- Debug.Console(1, this, Debug.ErrorLogLevel.Warning, "Web socket connection failed");
- }
- else
- {
- Debug.Console(1, this, Debug.ErrorLogLevel.Warning, "Web socket connection failure: {0}", code);
- }
- StartServerReconnectTimer();
- }
-
- return 0;
- }
-
- ///
- /// After a "hello" from the server, sends config and stuff
- ///
- void SendInitialMessage()
- {
- Debug.Console(1, this, "Sending initial join message");
- var confObject = ConfigReader.ConfigObject;
- confObject.Info.RuntimeInfo.AppName = Assembly.GetExecutingAssembly().GetName().Name;
- var version = Assembly.GetExecutingAssembly().GetName().Version;
- confObject.Info.RuntimeInfo.AssemblyVersion = string.Format("{0}.{1}.{2}", version.Major, version.Minor, version.Build);
-
- var msg = new
- {
- type = "join",
- content = new
- {
- config = confObject
- }
- };
- SendMessageObjectToServer(msg);
- }
-
- ///
- /// Sends any object type to server
- ///
- ///
- public void SendMessageObjectToServer(object o)
- {
- SendMessageToServer(JObject.FromObject(o));
- }
-
- ///
- /// Sends a message to the server from a room
- ///
- /// room from which the message originates
- /// object to be serialized and sent in post body
- public void SendMessageToServer(JObject o)
- {
- if (WSClient != null && WSClient.Connected)
- {
- string message = JsonConvert.SerializeObject(o, Formatting.None, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
-
- if (!message.Contains("/system/heartbeat"))
- Debug.Console(1, this, "Message TX: {0}", message);
- //else
- // Debug.Console(1, this, "TX messages contains /system/heartbeat");
-
- var messageBytes = System.Text.Encoding.UTF8.GetBytes(message);
- var result = WSClient.Send(messageBytes, (uint)messageBytes.Length, WebSocketClient.WEBSOCKET_PACKET_TYPES.LWS_WS_OPCODE_07__TEXT_FRAME);
- if (result != WebSocketClient.WEBSOCKET_RESULT_CODES.WEBSOCKET_CLIENT_SUCCESS)
- {
- Debug.Console(1, this, "Socket send result error: {0}", result);
- }
- }
- else if (WSClient == null)
- {
- Debug.Console(1, this, "Cannot send. Not connected.");
- }
- }
-
- ///
- /// Disconnects the SSE Client and stops the heartbeat timer
- ///
- ///
- void CleanUpWebsocketClient()
- {
- Debug.Console(1, this, "Disconnecting websocket");
- if (WSClient != null)
- {
- WSClient.SendCallBack = null;
- WSClient.ReceiveCallBack = null;
- WSClient.ConnectionCallBack = null;
- WSClient.DisconnectCallBack = null;
- if (WSClient.Connected)
- {
- WSClient.Disconnect();
- }
- WSClient = null;
- }
- }
-
- ///
- ///
- ///
- ///
- ///
- void StartServerReconnectTimer()
- {
- StopServerReconnectTimer();
- ServerReconnectTimer = new CTimer(ReconnectToServerTimerCallback, ServerReconnectInterval);
- Debug.Console(1, this, "Reconnect Timer Started.");
- }
-
- ///
- /// Does what it says
- ///
- void StopServerReconnectTimer()
- {
- if (ServerReconnectTimer != null)
- {
- ServerReconnectTimer.Stop();
- ServerReconnectTimer = null;
- }
- }
-
- ///
- /// Executes when we don't get a heartbeat message in time. Triggers reconnect.
- ///
- /// For CTimer callback. Not used
- void HeartbeatExpiredTimerCallback(object o)
- {
- Debug.Console(1, this, "Heartbeat Timer Expired.");
- if (ServerHeartbeatCheckTimer != null)
- {
- ServerHeartbeatCheckTimer.Stop();
- ServerHeartbeatCheckTimer = null;
- }
- CleanUpWebsocketClient();
- StartServerReconnectTimer();
- }
-
- ///
- ///
- ///
- ///
- ///
- void ResetOrStartHearbeatTimer()
- {
- if (ServerHeartbeatCheckTimer == null)
- {
- ServerHeartbeatCheckTimer = new CTimer(HeartbeatExpiredTimerCallback, null, ServerHeartbeatInterval, ServerHeartbeatInterval);
- Debug.Console(1, this, "Heartbeat Timer Started.");
- }
- else
- {
- ServerHeartbeatCheckTimer.Reset(ServerHeartbeatInterval, ServerHeartbeatInterval);
- }
- }
-
- ///
- /// Waits two and goes again
- ///
- void ReconnectStreamClient()
- {
- new CTimer(o => ConnectWebsocketClient(), 2000);
- }
-
- ///
- ///
- ///
- ///
- ///
- int Websocket_DisconnectCallback(WebSocketClient.WEBSOCKET_RESULT_CODES code, object o)
- {
- Debug.Console(1, this, Debug.ErrorLogLevel.Warning, "Websocket disconnected with code: {0}", code);
-
- if (ServerHeartbeatCheckTimer != null)
- ServerHeartbeatCheckTimer.Stop();
- // Start the reconnect timer
- StartServerReconnectTimer();
- return 0;
- }
-
-
- ///
- /// Resets reconnect timer and updates usercode
- ///
- ///
- void HandleHeartBeat(JToken content)
- {
- SendMessageToServer(JObject.FromObject(new
- {
- type = "/system/heartbeatAck"
- }));
-
- var code = content["userCode"];
- if(code != null)
- {
- foreach (var b in RoomBridges)
- {
- b.SetUserCode(code.Value());
- }
- }
- ResetOrStartHearbeatTimer();
- }
-
- ///
- /// Outputs debug info when enabled
- ///
- ///
- ///
- ///
- void CheckHttpDebug(HttpClientResponse r, HTTP_CALLBACK_ERROR e)
- {
- if (HttpDebugEnabled)
- {
- try
- {
- Debug.Console(0, this, "------ Begin HTTP Debug ---------------------------------------");
- if (r != null)
- {
- Debug.Console(0, this, "HTTP Response URL: {0}", r.ResponseUrl != null ? r.ResponseUrl.ToString() : "NONE");
- Debug.Console(0, this, "HTTP Response code: {0}", r.Code);
- Debug.Console(0, this, "HTTP Response content: \r{0}", r.ContentString);
- }
- else
- {
- Debug.Console(0, this, "No HTTP response");
- }
- Debug.Console(0, this, "HTTP Response 'error' {0}", e);
- Debug.Console(0, this, "------ End HTTP Debug -----------------------------------------");
- }
- catch (Exception ex)
- {
- Debug.Console(0, this, "HttpDebugError: {0}", ex);
- }
- }
- }
-
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- int Websocket_ReceiveCallback(byte[] data, uint length, WebSocketClient.WEBSOCKET_PACKET_TYPES opcode,
- WebSocketClient.WEBSOCKET_RESULT_CODES err)
- {
- if (opcode == WebSocketClient.WEBSOCKET_PACKET_TYPES.LWS_WS_OPCODE_07__TEXT_FRAME)
- {
- var rx = System.Text.Encoding.UTF8.GetString(data, 0, (int)length);
- if (rx.Length > 0)
- ParseStreamRx(rx);
- WSClient.ReceiveAsync();
- }
-
- else if (opcode == WebSocketClient.WEBSOCKET_PACKET_TYPES.LWS_WS_OPCODE_07__CLOSE)
- {
- Debug.Console(1, this, "Websocket disconnect received from remote");
- CleanUpWebsocketClient();
- }
- else
- {
- Debug.Console(1, this, "websocket rx opcode/err {0}/{1}", opcode, err);
- WSClient.ReceiveAsync();
- }
- return 0;
- }
-
- ///
- /// Callback to catch possible errors in sending via the websocket
- ///
- ///
- ///
- int Websocket_SendCallback(Crestron.SimplSharp.CrestronWebSocketClient.WebSocketClient.WEBSOCKET_RESULT_CODES result)
- {
- if(result != WebSocketClient.WEBSOCKET_RESULT_CODES.WEBSOCKET_CLIENT_SUCCESS)
- Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "SendCallback questionable result: {0}", result);
- return 1;
- }
-
- ///
- ///
- ///
- ///
- ///
- void ParseStreamRx(string message)
- {
- if(string.IsNullOrEmpty(message))
- return;
-
- if (!message.Contains("/system/heartbeat"))
- {
- Debug.Console(1, this, "Message RX: {0}", message);
- }
- else
- {
- LastAckMessage = DateTime.Now;
- }
-
- try
- {
- var messageObj = JObject.Parse(message);
-
- var type = messageObj["type"].Value();
-
- if (type == "hello")
- {
- SendInitialMessage();
- ResetOrStartHearbeatTimer();
- }
- else if (type == "/system/heartbeat")
- {
- HandleHeartBeat(messageObj["content"]);
- }
- else if (type == "raw")
- {
- var wrapper = messageObj["content"].ToObject();
- DeviceJsonApi.DoDeviceAction(wrapper);
- }
- else if (type == "close")
- {
- Debug.Console(1, this, "Received close message from server.");
- // DisconnectWebsocketClient();
-
- if (ServerHeartbeatCheckTimer != null)
- ServerHeartbeatCheckTimer.Stop();
- }
- else
- {
- // Check path against Action dictionary
- if (ActionDictionary.ContainsKey(type))
- {
- var action = ActionDictionary[type];
-
- if (action is Action)
- {
- (action as Action)();
- }
- else if (action is PressAndHoldAction)
- {
- var stateString = messageObj["content"]["state"].Value();
-
- // Look for a button press event
- if (!string.IsNullOrEmpty(stateString))
- {
- switch (stateString)
- {
- case "true":
- {
- if (!PushedActions.ContainsKey(type))
- {
- PushedActions.Add(type, new CTimer(o =>
- {
- (action as PressAndHoldAction)(false);
- PushedActions.Remove(type);
- }, null, ButtonHeartbeatInterval, ButtonHeartbeatInterval));
- }
- // Maybe add an else to reset the timer
- break;
- }
- case "held":
- {
- if (PushedActions.ContainsKey(type))
- {
- PushedActions[type].Reset(ButtonHeartbeatInterval, ButtonHeartbeatInterval);
- }
- return;
- }
- case "false":
- {
- if (PushedActions.ContainsKey(type))
- {
- PushedActions[type].Stop();
- PushedActions.Remove(type);
- }
- break;
- }
- }
-
- (action as PressAndHoldAction)(stateString == "true");
- }
- }
- else if (action is Action)
- {
- var stateString = messageObj["content"]["state"].Value();
-
- if (!string.IsNullOrEmpty(stateString))
- {
- (action as Action)(stateString == "true");
- }
- }
- else if (action is Action)
- {
- (action as Action)(messageObj["content"]["value"].Value());
- }
- else if (action is Action)
- {
- (action as Action)(messageObj["content"]["value"].Value());
- }
- else if (action is Action)
- {
- (action as Action)(messageObj["content"]
- .ToObject());
- }
- }
- else
- {
- Debug.Console(1, this, "-- Warning: Incoming message has no registered handler");
- }
- }
- }
- catch (Exception err)
- {
- //Debug.Console(1, "SseMessageLengthBeforeFailureCount: {0}", SseMessageLengthBeforeFailureCount);
- //SseMessageLengthBeforeFailureCount = 0;
- Debug.Console(1, this, "Unable to parse message: {0}", err);
- }
- }
-
- void TestHttpRequest(string s)
- {
- {
- s = s.Trim();
- if (string.IsNullOrEmpty(s))
- {
- PrintTestHttpRequestUsage();
- return;
- }
- var tokens = s.Split(' ');
- if (tokens.Length < 2)
- {
- CrestronConsole.ConsoleCommandResponse("Too few paramaters\r");
- PrintTestHttpRequestUsage();
- return;
- }
-
- try
- {
- var url = tokens[1];
- if (tokens[0].ToLower() == "get")
- {
- var resp = new HttpClient().Get(url);
- CrestronConsole.ConsoleCommandResponse("RESPONSE:\r{0}\r\r", resp);
- }
- else if (tokens[0].ToLower() == "post")
- {
- var resp = new HttpClient().Post(url, new byte[] { });
- CrestronConsole.ConsoleCommandResponse("RESPONSE:\r{0}\r\r", resp);
- }
-
- else
- {
- CrestronConsole.ConsoleCommandResponse("Only get or post supported\r");
- PrintTestHttpRequestUsage();
- }
- }
- catch (HttpException e)
- {
- CrestronConsole.ConsoleCommandResponse("Exception in request:\r");
- CrestronConsole.ConsoleCommandResponse("Response URL: {0}\r", e.Response.ResponseUrl);
- CrestronConsole.ConsoleCommandResponse("Response Error Code: {0}\r", e.Response.Code);
- CrestronConsole.ConsoleCommandResponse("Response body: {0}\r", e.Response.ContentString);
- }
-
- }
- }
-
- void PrintTestHttpRequestUsage()
- {
- CrestronConsole.ConsoleCommandResponse("Usage: mobilehttprequest:N get/post url\r");
- }
- }
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Text.RegularExpressions;
+using Crestron.SimplSharp;
+using Crestron.SimplSharp.CrestronIO;
+using Crestron.SimplSharp.Reflection;
+using Crestron.SimplSharpPro.CrestronThread;
+using Crestron.SimplSharp.CrestronWebSocketClient;
+using Crestron.SimplSharpPro;
+using Crestron.SimplSharp.Net.Http;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+
+using PepperDash.Core;
+using PepperDash.Essentials.Core;
+using PepperDash.Essentials.Core.Config;
+using PepperDash.Essentials.Room.MobileControl;
+using PepperDash.Essentials.AppServer.Messengers;
+
+namespace PepperDash.Essentials
+{
+ public class MobileControlSystemController : Device
+ {
+ WebSocketClient WSClient;
+
+ //bool LinkUp;
+
+ ///
+ /// Prevents post operations from stomping on each other and getting lost
+ ///
+ CEvent PostLockEvent = new CEvent(true, true);
+
+ CEvent RegisterLockEvent = new CEvent(true, true);
+
+ public MobileControlConfig Config { get; private set; }
+
+ Dictionary ActionDictionary = new Dictionary(StringComparer.InvariantCultureIgnoreCase);
+
+ Dictionary PushedActions = new Dictionary();
+
+ public ConfigMessenger ConfigMessenger { get; private set; }
+
+ CTimer ServerHeartbeatCheckTimer;
+
+ long ServerHeartbeatInterval = 20000;
+
+ CTimer ServerReconnectTimer;
+
+ long ServerReconnectInterval = 5000;
+
+ DateTime LastAckMessage;
+
+ public string SystemUuid;
+
+ List RoomBridges = new List();
+
+ long ButtonHeartbeatInterval = 1000;
+
+ ///
+ /// Used for tracking HTTP debugging
+ ///
+ bool HttpDebugEnabled;
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public MobileControlSystemController(string key, string name, MobileControlConfig config) : base(key, name)
+ {
+ Config = config;
+
+ SystemUuid = ConfigReader.ConfigObject.SystemUuid;
+
+ Debug.Console(0, this, "Mobile UI controller initializing for server:{0}", config.ServerUrl);
+
+ CrestronConsole.AddNewConsoleCommand(AuthorizeSystem,
+ "mobileauth", "Authorizes system to talk to Mobile Control server", ConsoleAccessLevelEnum.AccessOperator);
+ CrestronConsole.AddNewConsoleCommand(s => ShowInfo(),
+ "mobileinfo", "Shows information for current mobile control session", ConsoleAccessLevelEnum.AccessOperator);
+ CrestronConsole.AddNewConsoleCommand(s => {
+ s = s.Trim();
+ if(!string.IsNullOrEmpty(s))
+ {
+ HttpDebugEnabled = (s.Trim() != "0");
+ }
+ CrestronConsole.ConsoleCommandResponse("HTTP Debug {0}", HttpDebugEnabled ? "Enabled" : "Disabled");
+ },
+ "mobilehttpdebug", "1 enables more verbose HTTP response debugging", ConsoleAccessLevelEnum.AccessOperator);
+ CrestronConsole.AddNewConsoleCommand(TestHttpRequest,
+ "mobilehttprequest", "Tests an HTTP get to URL given", ConsoleAccessLevelEnum.AccessOperator);
+
+ CrestronConsole.AddNewConsoleCommand(PrintActionDictionaryPaths, "mobileshowactionpaths",
+ "Prints the paths in the Action Dictionary", ConsoleAccessLevelEnum.AccessOperator);
+ CrestronConsole.AddNewConsoleCommand(s => ConnectWebsocketClient(), "mobileconnect",
+ "Forces connect of websocket", ConsoleAccessLevelEnum.AccessOperator);
+ CrestronConsole.AddNewConsoleCommand(s => CleanUpWebsocketClient(), "mobiledisco",
+ "Disconnects websocket", ConsoleAccessLevelEnum.AccessOperator);
+
+ CrestronConsole.AddNewConsoleCommand(s => ParseStreamRx(s), "mobilesimulateaction", "Simulates a message from the server", ConsoleAccessLevelEnum.AccessOperator);
+
+ CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
+ CrestronEnvironment.EthernetEventHandler += new EthernetEventHandler(CrestronEnvironment_EthernetEventHandler);
+
+ // Config Messenger
+ var cmKey = Key + "-config";
+ ConfigMessenger = new ConfigMessenger(cmKey, "/config");
+ ConfigMessenger.RegisterWithAppServer(this);
+ }
+
+ ///
+ /// If config rooms is empty or null then go
+ ///
+ ///
+ public override bool CustomActivate()
+ {
+ if (ConfigReader.ConfigObject.Rooms == null || ConfigReader.ConfigObject.Rooms.Count == 0)
+ {
+ Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Config contains no rooms. Registering with Server.");
+ RegisterSystemToServer();
+ }
+
+ return base.CustomActivate();
+ }
+
+ ///
+ ///
+ ///
+ ///
+ void CrestronEnvironment_EthernetEventHandler(EthernetEventArgs args)
+ {
+ Debug.Console(1, this, Debug.ErrorLogLevel.Warning, "Ethernet status change, port {0}: {1}",
+ args.EthernetAdapter, args.EthernetEventType);
+
+ if (args.EthernetEventType == eEthernetEventType.LinkDown && WSClient != null && args.EthernetAdapter == WSClient.EthernetAdapter)
+ {
+ CleanUpWebsocketClient();
+ }
+ }
+
+ ///
+ /// Sends message to server to indicate the system is shutting down
+ ///
+ ///
+ void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType)
+ {
+ if (programEventType == eProgramStatusEventType.Stopping
+ && WSClient != null
+ && WSClient.Connected)
+ {
+ CleanUpWebsocketClient();
+ }
+ }
+
+ public void PrintActionDictionaryPaths(object o)
+ {
+ Debug.Console(0, this, "ActionDictionary Contents:");
+
+ foreach (var item in ActionDictionary)
+ {
+ Debug.Console(0, this, "{0}", item.Key);
+ }
+ }
+
+ ///
+ /// Adds an action to the dictionary
+ ///
+ /// The path of the API command
+ /// The action to be triggered by the commmand
+ public void AddAction(string key, object action)
+ {
+ if (!ActionDictionary.ContainsKey(key))
+ {
+ ActionDictionary.Add(key, action);
+ }
+ else
+ {
+ Debug.Console(1, this, "Cannot add action with key '{0}' because key already exists in ActionDictionary.", key);
+ }
+ }
+
+ ///
+ /// Removes an action from the dictionary
+ ///
+ ///
+ public void RemoveAction(string key)
+ {
+ if (ActionDictionary.ContainsKey(key))
+ ActionDictionary.Remove(key);
+ }
+
+ ///
+ ///
+ ///
+ ///
+ public void AddBridge(MobileControlBridgeBase bridge)
+ {
+ RoomBridges.Add(bridge);
+ var b = bridge as IDelayedConfiguration;
+ if (b != null)
+ {
+ Debug.Console(0, this, "Adding room bridge with delayed configuration");
+ b.ConfigurationIsReady += new EventHandler(bridge_ConfigurationIsReady);
+ }
+ else
+ {
+ Debug.Console(0, this, "Adding room bridge and sending configuration");
+ //SystemUuid = ConfigReader.ConfigObject.SystemUuid;
+ RegisterSystemToServer();
+ }
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ void bridge_ConfigurationIsReady(object sender, EventArgs e)
+ {
+ Debug.Console(1, this, "Bridge ready. Registering");
+ //SystemUuid = ConfigReader.ConfigObject.SystemUuid;
+ // send the configuration object to the server
+ RegisterSystemToServer();
+ }
+
+ ///
+ ///
+ ///
+ ///
+ void ReconnectToServerTimerCallback(object o)
+ {
+ RegisterSystemToServer();
+ }
+
+ ///
+ /// Verifies system connection with servers
+ ///
+ ///
+ void AuthorizeSystem(string code)
+ {
+ //SystemUuid = ConfigReader.ConfigObject.SystemUuid;
+
+ if (string.IsNullOrEmpty(SystemUuid))
+ {
+ CrestronConsole.ConsoleCommandResponse("System does not have a UUID. Please ensure proper portal-format configuration is loaded and restart.");
+ return;
+ }
+
+ if (string.IsNullOrEmpty(code))
+ {
+ CrestronConsole.ConsoleCommandResponse("Please enter a user code to authorize a system");
+ return;
+ }
+
+ var req = new HttpClientRequest();
+ string url = string.Format("http://{0}/api/system/grantcode/{1}/{2}", Config.ServerUrl, code, SystemUuid);
+ Debug.Console(0, this, "Authorizing to: {0}", url);
+
+ if (string.IsNullOrEmpty(Config.ServerUrl))
+ {
+ CrestronConsole.ConsoleCommandResponse("Config URL address is not set. Check portal configuration");
+ return;
+ }
+ try
+ {
+ req.Url.Parse(url);
+ new HttpClient().DispatchAsync(req, (r, e) =>
+ {
+ CheckHttpDebug(r, e);
+ if (e == HTTP_CALLBACK_ERROR.COMPLETED)
+ {
+ if (r.Code == 200)
+ {
+ Debug.Console(0, "System authorized, sending config.");
+#warning This registration may need to wait for config ready. Maybe.
+ RegisterSystemToServer();
+ }
+ else if (r.Code == 404)
+ {
+ if (r.ContentString.Contains("codeNotFound"))
+ {
+ Debug.Console(0, "Authorization failed, code not found for system UUID {0}", SystemUuid);
+ }
+ else if (r.ContentString.Contains("uuidNotFound"))
+ {
+ Debug.Console(0, "Authorization failed, uuid {0} not found. Check Essentials configuration is correct",
+ SystemUuid);
+ }
+ }
+ }
+ else
+ Debug.Console(0, this, "Error {0} in authorizing system", e);
+ });
+ }
+ catch (Exception e)
+ {
+ Debug.Console(0, this, "Error in authorizing: {0}", e);
+ }
+ }
+
+ ///
+ /// Dumps info in response to console command.
+ ///
+ void ShowInfo()
+ {
+ var url = Config != null ? Config.ServerUrl : "No config";
+ string name;
+ string code;
+ if (RoomBridges != null && RoomBridges.Count > 0)
+ {
+ name = RoomBridges[0].RoomName;
+ code = RoomBridges[0].UserCode;
+ }
+ else
+ {
+ name = "No config";
+ code = "Not available";
+ }
+ var conn = WSClient == null ? "No client" : (WSClient.Connected ? "Yes" : "No");
+ var secSinceLastAck = DateTime.Now - LastAckMessage;
+
+
+ CrestronConsole.ConsoleCommandResponse(@"Mobile Control Information:
+ Server address: {0}
+ System Name: {1}
+ System URL: {2}
+ System UUID: {3}
+ System User code: {4}
+ Connected?: {5}
+ Seconds Since Last Ack: {6}"
+ , url, name, ConfigReader.ConfigObject.SystemUrl, SystemUuid,
+ code, conn, secSinceLastAck.Seconds);
+ }
+
+ ///
+ /// Registers the room with the server
+ ///
+ /// URL of the server, including the port number, if not 80. Format: "serverUrlOrIp:port"
+ void RegisterSystemToServer()
+ {
+ ConnectWebsocketClient();
+ }
+
+ ///
+ /// Connects the Websocket Client
+ ///
+ ///
+ void ConnectWebsocketClient()
+ {
+
+ Debug.Console(1, this, "Initializing Stream client to server.");
+
+ if (WSClient != null)
+ {
+ Debug.Console(1, this, "Cleaning up previous socket");
+ CleanUpWebsocketClient();
+ }
+
+ WSClient = new WebSocketClient();
+ WSClient.URL = string.Format("wss://{0}/system/join/{1}", Config.ServerUrl, this.SystemUuid);
+ WSClient.ConnectionCallBack = Websocket_ConnectCallback;
+ WSClient.ConnectAsync();
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ int Websocket_ConnectCallback(WebSocketClient.WEBSOCKET_RESULT_CODES code)
+ {
+ if (code == WebSocketClient.WEBSOCKET_RESULT_CODES.WEBSOCKET_CLIENT_SUCCESS)
+ {
+ StopServerReconnectTimer();
+ Debug.Console(1, this, "Websocket connected");
+ WSClient.DisconnectCallBack = Websocket_DisconnectCallback;
+ WSClient.SendCallBack = Websocket_SendCallback;
+ WSClient.ReceiveCallBack = Websocket_ReceiveCallback;
+ WSClient.ReceiveAsync();
+ SendMessageObjectToServer(new
+ {
+ type = "hello"
+ });
+ }
+ else
+ {
+ if (code == WebSocketClient.WEBSOCKET_RESULT_CODES.WEBSOCKET_CLIENT_HTTP_HANDSHAKE_TOKEN_ERROR)
+ {
+ // This is the case when app server is running behind a websever and app server is down
+ Debug.Console(1, this, Debug.ErrorLogLevel.Warning, "Web socket connection failed. Check that app server is running behind web server");
+ }
+ else if (code == WebSocketClient.WEBSOCKET_RESULT_CODES.WEBSOCKET_CLIENT_SOCKET_CONNECTION_FAILED)
+ {
+ // this will be the case when webserver is unreachable
+ Debug.Console(1, this, Debug.ErrorLogLevel.Warning, "Web socket connection failed");
+ }
+ else
+ {
+ Debug.Console(1, this, Debug.ErrorLogLevel.Warning, "Web socket connection failure: {0}", code);
+ }
+ StartServerReconnectTimer();
+ }
+
+ return 0;
+ }
+
+ ///
+ /// After a "hello" from the server, sends config and stuff
+ ///
+ void SendInitialMessage()
+ {
+ Debug.Console(1, this, "Sending initial join message");
+ var confObject = ConfigReader.ConfigObject;
+ confObject.Info.RuntimeInfo.AppName = Assembly.GetExecutingAssembly().GetName().Name;
+ var version = Assembly.GetExecutingAssembly().GetName().Version;
+ confObject.Info.RuntimeInfo.AssemblyVersion = string.Format("{0}.{1}.{2}", version.Major, version.Minor, version.Build);
+
+ var msg = new
+ {
+ type = "join",
+ content = new
+ {
+ config = confObject
+ }
+ };
+ SendMessageObjectToServer(msg);
+ }
+
+ ///
+ /// Sends any object type to server
+ ///
+ ///
+ public void SendMessageObjectToServer(object o)
+ {
+ SendMessageToServer(JObject.FromObject(o));
+ }
+
+ ///
+ /// Sends a message to the server from a room
+ ///
+ /// room from which the message originates
+ /// object to be serialized and sent in post body
+ public void SendMessageToServer(JObject o)
+ {
+ if (WSClient != null && WSClient.Connected)
+ {
+ string message = JsonConvert.SerializeObject(o, Formatting.None, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
+
+ if (!message.Contains("/system/heartbeat"))
+ Debug.Console(1, this, "Message TX: {0}", message);
+ //else
+ // Debug.Console(1, this, "TX messages contains /system/heartbeat");
+
+ var messageBytes = System.Text.Encoding.UTF8.GetBytes(message);
+ var result = WSClient.Send(messageBytes, (uint)messageBytes.Length, WebSocketClient.WEBSOCKET_PACKET_TYPES.LWS_WS_OPCODE_07__TEXT_FRAME);
+ if (result != WebSocketClient.WEBSOCKET_RESULT_CODES.WEBSOCKET_CLIENT_SUCCESS)
+ {
+ Debug.Console(1, this, "Socket send result error: {0}", result);
+ }
+ }
+ else if (WSClient == null)
+ {
+ Debug.Console(1, this, "Cannot send. Not connected.");
+ }
+ }
+
+ ///
+ /// Disconnects the SSE Client and stops the heartbeat timer
+ ///
+ ///
+ void CleanUpWebsocketClient()
+ {
+ Debug.Console(1, this, "Disconnecting websocket");
+ if (WSClient != null)
+ {
+ WSClient.SendCallBack = null;
+ WSClient.ReceiveCallBack = null;
+ WSClient.ConnectionCallBack = null;
+ WSClient.DisconnectCallBack = null;
+ if (WSClient.Connected)
+ {
+ WSClient.Disconnect();
+ }
+ WSClient = null;
+ }
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ void StartServerReconnectTimer()
+ {
+ StopServerReconnectTimer();
+ ServerReconnectTimer = new CTimer(ReconnectToServerTimerCallback, ServerReconnectInterval);
+ Debug.Console(1, this, "Reconnect Timer Started.");
+ }
+
+ ///
+ /// Does what it says
+ ///
+ void StopServerReconnectTimer()
+ {
+ if (ServerReconnectTimer != null)
+ {
+ ServerReconnectTimer.Stop();
+ ServerReconnectTimer = null;
+ }
+ }
+
+ ///
+ /// Executes when we don't get a heartbeat message in time. Triggers reconnect.
+ ///
+ /// For CTimer callback. Not used
+ void HeartbeatExpiredTimerCallback(object o)
+ {
+ Debug.Console(1, this, "Heartbeat Timer Expired.");
+ if (ServerHeartbeatCheckTimer != null)
+ {
+ ServerHeartbeatCheckTimer.Stop();
+ ServerHeartbeatCheckTimer = null;
+ }
+ CleanUpWebsocketClient();
+ StartServerReconnectTimer();
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ void ResetOrStartHearbeatTimer()
+ {
+ if (ServerHeartbeatCheckTimer == null)
+ {
+ ServerHeartbeatCheckTimer = new CTimer(HeartbeatExpiredTimerCallback, null, ServerHeartbeatInterval, ServerHeartbeatInterval);
+ Debug.Console(1, this, "Heartbeat Timer Started.");
+ }
+ else
+ {
+ ServerHeartbeatCheckTimer.Reset(ServerHeartbeatInterval, ServerHeartbeatInterval);
+ }
+ }
+
+ ///
+ /// Waits two and goes again
+ ///
+ void ReconnectStreamClient()
+ {
+ new CTimer(o => ConnectWebsocketClient(), 2000);
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ int Websocket_DisconnectCallback(WebSocketClient.WEBSOCKET_RESULT_CODES code, object o)
+ {
+ Debug.Console(1, this, Debug.ErrorLogLevel.Warning, "Websocket disconnected with code: {0}", code);
+
+ if (ServerHeartbeatCheckTimer != null)
+ ServerHeartbeatCheckTimer.Stop();
+ // Start the reconnect timer
+ StartServerReconnectTimer();
+ return 0;
+ }
+
+
+ ///
+ /// Resets reconnect timer and updates usercode
+ ///
+ ///
+ void HandleHeartBeat(JToken content)
+ {
+ SendMessageToServer(JObject.FromObject(new
+ {
+ type = "/system/heartbeatAck"
+ }));
+
+ var code = content["userCode"];
+ if(code != null)
+ {
+ foreach (var b in RoomBridges)
+ {
+ b.SetUserCode(code.Value());
+ }
+ }
+ ResetOrStartHearbeatTimer();
+ }
+
+ ///
+ /// Outputs debug info when enabled
+ ///
+ ///
+ ///
+ ///
+ void CheckHttpDebug(HttpClientResponse r, HTTP_CALLBACK_ERROR e)
+ {
+ if (HttpDebugEnabled)
+ {
+ try
+ {
+ Debug.Console(0, this, "------ Begin HTTP Debug ---------------------------------------");
+ if (r != null)
+ {
+ Debug.Console(0, this, "HTTP Response URL: {0}", r.ResponseUrl != null ? r.ResponseUrl.ToString() : "NONE");
+ Debug.Console(0, this, "HTTP Response code: {0}", r.Code);
+ Debug.Console(0, this, "HTTP Response content: \r{0}", r.ContentString);
+ }
+ else
+ {
+ Debug.Console(0, this, "No HTTP response");
+ }
+ Debug.Console(0, this, "HTTP Response 'error' {0}", e);
+ Debug.Console(0, this, "------ End HTTP Debug -----------------------------------------");
+ }
+ catch (Exception ex)
+ {
+ Debug.Console(0, this, "HttpDebugError: {0}", ex);
+ }
+ }
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ int Websocket_ReceiveCallback(byte[] data, uint length, WebSocketClient.WEBSOCKET_PACKET_TYPES opcode,
+ WebSocketClient.WEBSOCKET_RESULT_CODES err)
+ {
+ if (opcode == WebSocketClient.WEBSOCKET_PACKET_TYPES.LWS_WS_OPCODE_07__TEXT_FRAME)
+ {
+ var rx = System.Text.Encoding.UTF8.GetString(data, 0, (int)length);
+ if (rx.Length > 0)
+ ParseStreamRx(rx);
+ WSClient.ReceiveAsync();
+ }
+
+ else if (opcode == WebSocketClient.WEBSOCKET_PACKET_TYPES.LWS_WS_OPCODE_07__CLOSE)
+ {
+ Debug.Console(1, this, "Websocket disconnect received from remote");
+ CleanUpWebsocketClient();
+ }
+ else
+ {
+ Debug.Console(1, this, "websocket rx opcode/err {0}/{1}", opcode, err);
+ WSClient.ReceiveAsync();
+ }
+ return 0;
+ }
+
+ ///
+ /// Callback to catch possible errors in sending via the websocket
+ ///
+ ///
+ ///
+ int Websocket_SendCallback(Crestron.SimplSharp.CrestronWebSocketClient.WebSocketClient.WEBSOCKET_RESULT_CODES result)
+ {
+ if(result != WebSocketClient.WEBSOCKET_RESULT_CODES.WEBSOCKET_CLIENT_SUCCESS)
+ Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "SendCallback questionable result: {0}", result);
+ return 1;
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ void ParseStreamRx(string message)
+ {
+ if(string.IsNullOrEmpty(message))
+ return;
+
+ if (!message.Contains("/system/heartbeat"))
+ {
+ Debug.Console(1, this, "Message RX: {0}", message);
+ }
+ else
+ {
+ LastAckMessage = DateTime.Now;
+ }
+
+ try
+ {
+ var messageObj = JObject.Parse(message);
+
+ var type = messageObj["type"].Value();
+
+ if (type == "hello")
+ {
+ SendInitialMessage();
+ ResetOrStartHearbeatTimer();
+ }
+ else if (type == "/system/heartbeat")
+ {
+ HandleHeartBeat(messageObj["content"]);
+ }
+ else if (type == "raw")
+ {
+ var wrapper = messageObj["content"].ToObject();
+ DeviceJsonApi.DoDeviceAction(wrapper);
+ }
+ else if (type == "close")
+ {
+ Debug.Console(1, this, "Received close message from server.");
+ // DisconnectWebsocketClient();
+
+ if (ServerHeartbeatCheckTimer != null)
+ ServerHeartbeatCheckTimer.Stop();
+ }
+ else
+ {
+ // Check path against Action dictionary
+ if (ActionDictionary.ContainsKey(type))
+ {
+ var action = ActionDictionary[type];
+
+ if (action is Action)
+ {
+ (action as Action)();
+ }
+ else if (action is PressAndHoldAction)
+ {
+ var stateString = messageObj["content"]["state"].Value();
+
+ // Look for a button press event
+ if (!string.IsNullOrEmpty(stateString))
+ {
+ switch (stateString)
+ {
+ case "true":
+ {
+ if (!PushedActions.ContainsKey(type))
+ {
+ PushedActions.Add(type, new CTimer(o =>
+ {
+ (action as PressAndHoldAction)(false);
+ PushedActions.Remove(type);
+ }, null, ButtonHeartbeatInterval, ButtonHeartbeatInterval));
+ }
+ // Maybe add an else to reset the timer
+ break;
+ }
+ case "held":
+ {
+ if (PushedActions.ContainsKey(type))
+ {
+ PushedActions[type].Reset(ButtonHeartbeatInterval, ButtonHeartbeatInterval);
+ }
+ return;
+ }
+ case "false":
+ {
+ if (PushedActions.ContainsKey(type))
+ {
+ PushedActions[type].Stop();
+ PushedActions.Remove(type);
+ }
+ break;
+ }
+ }
+
+ (action as PressAndHoldAction)(stateString == "true");
+ }
+ }
+ else if (action is Action)
+ {
+ var stateString = messageObj["content"]["state"].Value();
+
+ if (!string.IsNullOrEmpty(stateString))
+ {
+ (action as Action)(stateString == "true");
+ }
+ }
+ else if (action is Action)
+ {
+ (action as Action)(messageObj["content"]["value"].Value());
+ }
+ else if (action is Action)
+ {
+ (action as Action)(messageObj["content"]["value"].Value());
+ }
+ else if (action is Action)
+ {
+ (action as Action)(messageObj["content"]
+ .ToObject());
+ }
+ }
+ else
+ {
+ Debug.Console(1, this, "-- Warning: Incoming message has no registered handler");
+ }
+ }
+ }
+ catch (Exception err)
+ {
+ //Debug.Console(1, "SseMessageLengthBeforeFailureCount: {0}", SseMessageLengthBeforeFailureCount);
+ //SseMessageLengthBeforeFailureCount = 0;
+ Debug.Console(1, this, "Unable to parse message: {0}", err);
+ }
+ }
+
+ void TestHttpRequest(string s)
+ {
+ {
+ s = s.Trim();
+ if (string.IsNullOrEmpty(s))
+ {
+ PrintTestHttpRequestUsage();
+ return;
+ }
+ var tokens = s.Split(' ');
+ if (tokens.Length < 2)
+ {
+ CrestronConsole.ConsoleCommandResponse("Too few paramaters\r");
+ PrintTestHttpRequestUsage();
+ return;
+ }
+
+ try
+ {
+ var url = tokens[1];
+ if (tokens[0].ToLower() == "get")
+ {
+ var resp = new HttpClient().Get(url);
+ CrestronConsole.ConsoleCommandResponse("RESPONSE:\r{0}\r\r", resp);
+ }
+ else if (tokens[0].ToLower() == "post")
+ {
+ var resp = new HttpClient().Post(url, new byte[] { });
+ CrestronConsole.ConsoleCommandResponse("RESPONSE:\r{0}\r\r", resp);
+ }
+
+ else
+ {
+ CrestronConsole.ConsoleCommandResponse("Only get or post supported\r");
+ PrintTestHttpRequestUsage();
+ }
+ }
+ catch (HttpException e)
+ {
+ CrestronConsole.ConsoleCommandResponse("Exception in request:\r");
+ CrestronConsole.ConsoleCommandResponse("Response URL: {0}\r", e.Response.ResponseUrl);
+ CrestronConsole.ConsoleCommandResponse("Response Error Code: {0}\r", e.Response.Code);
+ CrestronConsole.ConsoleCommandResponse("Response body: {0}\r", e.Response.ContentString);
+ }
+
+ }
+ }
+
+ void PrintTestHttpRequestUsage()
+ {
+ CrestronConsole.ConsoleCommandResponse("Usage: mobilehttprequest:N get/post url\r");
+ }
+ }
}
\ No newline at end of file
diff --git a/PepperDashEssentials/AppServer/RoomBridges/CotijaBridgeBase.cs b/PepperDashEssentials/AppServer/RoomBridges/MobileControlBridgeBase.cs
similarity index 81%
rename from PepperDashEssentials/AppServer/RoomBridges/CotijaBridgeBase.cs
rename to PepperDashEssentials/AppServer/RoomBridges/MobileControlBridgeBase.cs
index a578a0ae..d7258070 100644
--- a/PepperDashEssentials/AppServer/RoomBridges/CotijaBridgeBase.cs
+++ b/PepperDashEssentials/AppServer/RoomBridges/MobileControlBridgeBase.cs
@@ -12,15 +12,15 @@ namespace PepperDash.Essentials
///
///
///
- public abstract class CotijaBridgeBase: Device
+ public abstract class MobileControlBridgeBase: Device
{
- public CotijaSystemController Parent { get; private set; }
+ public MobileControlSystemController Parent { get; private set; }
public string UserCode { get; private set; }
public abstract string RoomName { get; }
- public CotijaBridgeBase(string key, string name)
+ public MobileControlBridgeBase(string key, string name)
: base(key, name)
{
}
@@ -30,7 +30,7 @@ namespace PepperDash.Essentials
/// as adding actions to parent
///
///
- public virtual void AddParent(CotijaSystemController parent)
+ public virtual void AddParent(MobileControlSystemController parent)
{
Parent = parent;
}
diff --git a/PepperDashEssentials/AppServer/RoomBridges/CotijaDdvc01RoomBridge.cs b/PepperDashEssentials/AppServer/RoomBridges/MobileControlDdvc01RoomBridge.cs
similarity index 98%
rename from PepperDashEssentials/AppServer/RoomBridges/CotijaDdvc01RoomBridge.cs
rename to PepperDashEssentials/AppServer/RoomBridges/MobileControlDdvc01RoomBridge.cs
index 0ff3c80c..eefd8fbc 100644
--- a/PepperDashEssentials/AppServer/RoomBridges/CotijaDdvc01RoomBridge.cs
+++ b/PepperDashEssentials/AppServer/RoomBridges/MobileControlDdvc01RoomBridge.cs
@@ -16,9 +16,9 @@ using PepperDash.Essentials.Core.Config;
using PepperDash.Essentials.Room.Config;
-namespace PepperDash.Essentials.Room.Cotija
+namespace PepperDash.Essentials.Room.MobileControl
{
- public class CotijaDdvc01RoomBridge : CotijaBridgeBase, IDelayedConfiguration
+ public class MobileControlDdvc01RoomBridge : MobileControlBridgeBase, IDelayedConfiguration
{
public class BoolJoin
{
@@ -236,7 +236,7 @@ namespace PepperDash.Essentials.Room.Cotija
}
}
- CotijaDdvc01DeviceBridge SourceBridge;
+ MobileControlDdvc01DeviceBridge SourceBridge;
Ddvc01AtcMessenger AtcMessenger;
Ddvc01VtcMessenger VtcMessenger;
@@ -248,7 +248,7 @@ namespace PepperDash.Essentials.Room.Cotija
///
///
///
- public CotijaDdvc01RoomBridge(string key, string name, uint ipId)
+ public MobileControlDdvc01RoomBridge(string key, string name, uint ipId)
: base(key, name)
{
try
@@ -258,7 +258,7 @@ namespace PepperDash.Essentials.Room.Cotija
if (reg != Crestron.SimplSharpPro.eDeviceRegistrationUnRegistrationResponse.Success)
Debug.Console(0, this, "Cannot connect EISC at IPID {0}: \r{1}", ipId, reg);
- SourceBridge = new CotijaDdvc01DeviceBridge(key + "-sourceBridge", "DDVC01 source bridge", EISC);
+ SourceBridge = new MobileControlDdvc01DeviceBridge(key + "-sourceBridge", "DDVC01 source bridge", EISC);
DeviceManager.AddDevice(SourceBridge);
}
catch (Exception)
@@ -590,7 +590,7 @@ namespace PepperDash.Essentials.Room.Cotija
// rmProps.VolumeSliderNames.Add(EISC.StringInput[i].StringValue);
//}
- // There should be cotija devices in here, I think...
+ // There should be Mobile Control devices in here, I think...
if(co.Devices == null)
co.Devices = new List();
diff --git a/PepperDashEssentials/AppServer/RoomBridges/CotijaEssentialsHuddleSpaceRoomBridge.cs b/PepperDashEssentials/AppServer/RoomBridges/MobileControlEssentialsHuddleSpaceRoomBridge.cs
similarity index 85%
rename from PepperDashEssentials/AppServer/RoomBridges/CotijaEssentialsHuddleSpaceRoomBridge.cs
rename to PepperDashEssentials/AppServer/RoomBridges/MobileControlEssentialsHuddleSpaceRoomBridge.cs
index 4fd75a36..fd36389a 100644
--- a/PepperDashEssentials/AppServer/RoomBridges/CotijaEssentialsHuddleSpaceRoomBridge.cs
+++ b/PepperDashEssentials/AppServer/RoomBridges/MobileControlEssentialsHuddleSpaceRoomBridge.cs
@@ -9,14 +9,14 @@ using Newtonsoft.Json.Linq;
using PepperDash.Core;
using PepperDash.Essentials.AppServer.Messengers;
using PepperDash.Essentials.Core;
-using PepperDash.Essentials.Room.Cotija;
+using PepperDash.Essentials.Room.MobileControl;
using PepperDash.Essentials.Devices.Common.Codec;
using PepperDash.Essentials.Devices.Common.VideoCodec;
using PepperDash.Essentials.Devices.Common.AudioCodec;
namespace PepperDash.Essentials
{
- public class CotijaEssentialsHuddleSpaceRoomBridge : CotijaBridgeBase
+ public class MobileConrolEssentialsHuddleSpaceRoomBridge : MobileControlBridgeBase
{
public EssentialsRoomBase Room { get; private set; }
@@ -42,7 +42,7 @@ namespace PepperDash.Essentials
///
///
///
- public CotijaEssentialsHuddleSpaceRoomBridge(EssentialsRoomBase room):
+ public MobileConrolEssentialsHuddleSpaceRoomBridge(EssentialsRoomBase room):
base("mobileControlBridge-essentialsHuddle", "Essentials Mobile Control Bridge-Huddle")
{
Room = room;
@@ -52,7 +52,7 @@ namespace PepperDash.Essentials
/// Override of base: calls base to add parent and then registers actions and events.
///
///
- public override void AddParent(CotijaSystemController parent)
+ public override void AddParent(MobileControlSystemController parent)
{
base.AddParent(parent);
@@ -93,7 +93,7 @@ namespace PepperDash.Essentials
var sscRoom = Room as IHasCurrentSourceInfoChange;
if(sscRoom != null)
- sscRoom.CurrentSingleSourceChange += new SourceInfoChangeHandler(Room_CurrentSingleSourceChange);
+ sscRoom.CurrentSourceChange += new SourceInfoChangeHandler(Room_CurrentSingleSourceChange);
var vcRoom = Room as IHasVideoCodec;
if (vcRoom != null && vcRoom.VideoCodec != null)
@@ -169,19 +169,6 @@ namespace PepperDash.Essentials
});
}
- /////
- ///// Handler for codec changes
- /////
- //void codec_CallStatusChange(object sender, CodecCallStatusItemChangeEventArgs e)
- //{
- // PostStatusMessage(new
- // {
- // calls = GetCallsMessageObject(),
- // //vtc = GetVtcCallsMessageObject()
- // });
-
- //}
-
///
/// Helper for posting status message
///
@@ -334,7 +321,7 @@ namespace PepperDash.Essentials
}
- void Room_CurrentSingleSourceChange(EssentialsRoomBase room, PepperDash.Essentials.Core.SourceListItem info, ChangeType type)
+ void Room_CurrentSingleSourceChange(PepperDash.Essentials.Core.SourceListItem info, ChangeType type)
{
/* Example message
* {
@@ -395,11 +382,14 @@ namespace PepperDash.Essentials
if (dev is ITransport)
(dev as ITransport).LinkActions(Parent);
- var srcRm = room as IHasCurrentSourceInfoChange;
- PostStatusMessage(new
- {
- selectedSourceKey = srcRm.CurrentSourceInfoKey
- });
+ var srcRm = Room as IHasCurrentSourceInfoChange;
+ if (srcRm != null)
+ {
+ PostStatusMessage(new
+ {
+ selectedSourceKey = srcRm.CurrentSourceInfoKey
+ });
+ }
}
}
}
@@ -432,45 +422,6 @@ namespace PepperDash.Essentials
volumes = volumes
});
}
-
- /////
- ///// Helper to return a anonymous object with the call data for JSON message
- /////
- /////
- //object GetCallsMessageObject()
- //{
- // var callRm = Room as IHasVideoCodec;
- // if (callRm == null)
- // return null;
- // return new
- // {
- // activeCalls = callRm.VideoCodec.ActiveCalls,
- // callType = callRm.CallTypeFeedback.IntValue,
- // inCall = callRm.InCallFeedback.BoolValue,
- // isSharing = callRm.IsSharingFeedback.BoolValue,
- // privacyModeIsOn = callRm.PrivacyModeIsOnFeedback.BoolValue
- // };
- //}
-
- /////
- ///// Helper method to build call status for vtc
- /////
- /////
- //object GetVtcCallsMessageObject()
- //{
- // var callRm = Room as IHasVideoCodec;
- // object vtc = null;
- // if (callRm != null)
- // {
- // var codec = callRm.VideoCodec;
- // vtc = new
- // {
- // isInCall = codec.IsInCall,
- // calls = codec.ActiveCalls
- // };
- // }
- // return vtc;
- //}
}
///
diff --git a/PepperDashEssentials/AppServer/RoomBridges/SourceDeviceMapDictionary.cs b/PepperDashEssentials/AppServer/RoomBridges/SourceDeviceMapDictionary.cs
index 976c4121..b025de44 100644
--- a/PepperDashEssentials/AppServer/RoomBridges/SourceDeviceMapDictionary.cs
+++ b/PepperDashEssentials/AppServer/RoomBridges/SourceDeviceMapDictionary.cs
@@ -4,7 +4,7 @@ using System.Linq;
using System.Text;
using Crestron.SimplSharp;
-namespace PepperDash.Essentials.Room.Cotija
+namespace PepperDash.Essentials.Room.MobileControl
{
///
diff --git a/PepperDashEssentials/AppServer/Volumes.cs b/PepperDashEssentials/AppServer/Volumes.cs
index 5806f117..5b5096b0 100644
--- a/PepperDashEssentials/AppServer/Volumes.cs
+++ b/PepperDashEssentials/AppServer/Volumes.cs
@@ -6,7 +6,7 @@ using Crestron.SimplSharp;
using Newtonsoft.Json;
-namespace PepperDash.Essentials.Room.Cotija
+namespace PepperDash.Essentials.Room.MobileControl
{
public class Volumes
{
diff --git a/PepperDashEssentials/ControlSystem.cs b/PepperDashEssentials/ControlSystem.cs
index 65848f71..4cb58a24 100644
--- a/PepperDashEssentials/ControlSystem.cs
+++ b/PepperDashEssentials/ControlSystem.cs
@@ -15,7 +15,7 @@ using PepperDash.Essentials.Devices.Common;
using PepperDash.Essentials.DM;
using PepperDash.Essentials.Fusion;
using PepperDash.Essentials.Room.Config;
-using PepperDash.Essentials.Room.Cotija;
+using PepperDash.Essentials.Room.MobileControl;
using Newtonsoft.Json;
@@ -42,8 +42,11 @@ namespace PepperDash.Essentials
{
DeterminePlatform();
- //CrestronConsole.AddNewConsoleCommand(s => GoWithLoad(), "go", "Loads configuration file",
- // ConsoleAccessLevelEnum.AccessOperator);
+ if (Debug.DoNotLoadOnNextBoot)
+ {
+ CrestronConsole.AddNewConsoleCommand(s => GoWithLoad(), "go", "Loads configuration file",
+ ConsoleAccessLevelEnum.AccessOperator);
+ }
// CrestronConsole.AddNewConsoleCommand(S => { ConfigWriter.WriteConfigFile(null); }, "writeconfig", "writes the current config to a file", ConsoleAccessLevelEnum.AccessOperator);
CrestronConsole.AddNewConsoleCommand(s =>
@@ -73,7 +76,8 @@ namespace PepperDash.Essentials
"Template URL: {1}", ConfigReader.ConfigObject.SystemUrl, ConfigReader.ConfigObject.TemplateUrl);
}, "portalinfo", "Shows portal URLS from configuration", ConsoleAccessLevelEnum.AccessOperator);
- GoWithLoad();
+ if (!Debug.DoNotLoadOnNextBoot)
+ GoWithLoad();
}
///
@@ -83,57 +87,64 @@ namespace PepperDash.Essentials
///
public void DeterminePlatform()
{
- Debug.Console(0, Debug.ErrorLogLevel.Notice, "Determining Platform....");
-
- string filePathPrefix;
-
- var dirSeparator = Global.DirectorySeparator;
-
- var version = Crestron.SimplSharp.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
-
- var versionString = string.Format("{0}.{1}.{2}", version.Major, version.Minor, version.Build);
-
- string directoryPrefix;
-
- directoryPrefix = Crestron.SimplSharp.CrestronIO.Directory.GetApplicationRootDirectory();
-
- if (CrestronEnvironment.DevicePlatform != eDevicePlatform.Server) // Handles 3-series running Windows OS
+ try
{
- Debug.Console(0, Debug.ErrorLogLevel.Notice, "Starting Essentials v{0} on 3-series Appliance", versionString);
+ Debug.Console(0, Debug.ErrorLogLevel.Notice, "Determining Platform....");
- // Check if User/ProgramX exists
- if (Directory.Exists(directoryPrefix + dirSeparator + "User"
- + dirSeparator + string.Format("program{0}", InitialParametersClass.ApplicationNumber)))
+ string filePathPrefix;
+
+ var dirSeparator = Global.DirectorySeparator;
+
+ var version = Crestron.SimplSharp.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
+
+ var versionString = string.Format("{0}.{1}.{2}", version.Major, version.Minor, version.Build);
+
+ string directoryPrefix;
+
+ directoryPrefix = Crestron.SimplSharp.CrestronIO.Directory.GetApplicationRootDirectory();
+
+ if (CrestronEnvironment.DevicePlatform != eDevicePlatform.Server) // Handles 3-series running Windows OS
{
- Debug.Console(0, @"User/program{0} directory found", InitialParametersClass.ApplicationNumber);
- filePathPrefix = directoryPrefix + dirSeparator + "User"
- + dirSeparator + string.Format("program{0}", InitialParametersClass.ApplicationNumber) + dirSeparator;
+ Debug.Console(0, Debug.ErrorLogLevel.Notice, "Starting Essentials v{0} on 3-series Appliance", versionString);
+
+ // Check if User/ProgramX exists
+ if (Directory.Exists(directoryPrefix + dirSeparator + "User"
+ + dirSeparator + string.Format("program{0}", InitialParametersClass.ApplicationNumber)))
+ {
+ Debug.Console(0, @"User/program{0} directory found", InitialParametersClass.ApplicationNumber);
+ filePathPrefix = directoryPrefix + dirSeparator + "User"
+ + dirSeparator + string.Format("program{0}", InitialParametersClass.ApplicationNumber) + dirSeparator;
+ }
+ // Check if Nvram/Programx exists
+ else if (Directory.Exists(directoryPrefix + dirSeparator + "Nvram"
+ + dirSeparator + string.Format("program{0}", InitialParametersClass.ApplicationNumber)))
+ {
+ Debug.Console(0, @"Nvram/program{0} directory found", InitialParametersClass.ApplicationNumber);
+ filePathPrefix = directoryPrefix + dirSeparator + "Nvram"
+ + dirSeparator + string.Format("program{0}", InitialParametersClass.ApplicationNumber) + dirSeparator;
+ }
+ // If neither exists, set path to User/ProgramX
+ else
+ {
+ Debug.Console(0, @"No previous directory found. Using User/program{0}", InitialParametersClass.ApplicationNumber);
+ filePathPrefix = directoryPrefix + dirSeparator + "User"
+ + dirSeparator + string.Format("program{0}", InitialParametersClass.ApplicationNumber) + dirSeparator;
+ }
}
- // Check if Nvram/Programx exists
- else if (Directory.Exists(directoryPrefix + dirSeparator + "Nvram"
- + dirSeparator + string.Format("program{0}", InitialParametersClass.ApplicationNumber)))
+ else // Handles Linux OS (Virtual Control)
{
- Debug.Console(0, @"Nvram/program{0} directory found", InitialParametersClass.ApplicationNumber);
- filePathPrefix = directoryPrefix + dirSeparator + "Nvram"
- + dirSeparator + string.Format("program{0}", InitialParametersClass.ApplicationNumber) + dirSeparator;
- }
- // If neither exists, set path to User/ProgramX
- else
- {
- Debug.Console(0, @"No previous directory found. Using User/program{0}", InitialParametersClass.ApplicationNumber);
- filePathPrefix = directoryPrefix + dirSeparator + "User"
- + dirSeparator + string.Format("program{0}", InitialParametersClass.ApplicationNumber) + dirSeparator;
+ Debug.Console(0, Debug.ErrorLogLevel.Notice, "Starting Essentials v{0} on Virtual Control Server", versionString);
+
+ // Set path to User/
+ filePathPrefix = directoryPrefix + dirSeparator + "User" + dirSeparator;
}
+
+ Global.SetFilePathPrefix(filePathPrefix);
}
- else // Handles Linux OS (Virtual Control)
+ catch (Exception e)
{
- Debug.Console(0, Debug.ErrorLogLevel.Notice, "Starting Essentials v{0} on Virtual Control Server", versionString);
-
- // Set path to User/
- filePathPrefix = directoryPrefix + dirSeparator + "User" + dirSeparator;
+ Debug.Console(0, "Unable to Determine Platform due to Exception: {0}", e.Message);
}
-
- Global.SetFilePathPrefix(filePathPrefix);
}
///
@@ -143,6 +154,8 @@ namespace PepperDash.Essentials
{
try
{
+ Debug.SetDoNotLoadOnNextBoot(false);
+
Debug.Console(0, Debug.ErrorLogLevel.Notice, "Starting Essentials load from configuration");
var filesReady = SetupFilesystem();
@@ -337,7 +350,7 @@ namespace PepperDash.Essentials
{
var sysMon = DeviceManager.GetDeviceForKey("systemMonitor") as PepperDash.Essentials.Core.Monitoring.SystemMonitorController;
- var appServer = DeviceManager.GetDeviceForKey("appServer") as CotijaSystemController;
+ var appServer = DeviceManager.GetDeviceForKey("appServer") as MobileControlSystemController;
if (sysMon != null && appServer != null)
@@ -493,16 +506,16 @@ namespace PepperDash.Essentials
DeviceManager.AddDevice(room);
Debug.Console(0, Debug.ErrorLogLevel.Notice, "Room is EssentialsHuddleSpaceRoom, attempting to add to DeviceManager with Fusion");
- DeviceManager.AddDevice(new EssentialsHuddleSpaceFusionSystemControllerBase((EssentialsHuddleSpaceRoom)room, 0xf1));
+ DeviceManager.AddDevice(new Core.Fusion.EssentialsHuddleSpaceFusionSystemControllerBase((EssentialsHuddleSpaceRoom)room, 0xf1));
- Debug.Console(0, Debug.ErrorLogLevel.Notice, "Attempting to build Cotija Bridge...");
- // Cotija bridge
- var bridge = new CotijaEssentialsHuddleSpaceRoomBridge(room as EssentialsHuddleSpaceRoom);
+ Debug.Console(0, Debug.ErrorLogLevel.Notice, "Attempting to build Mobile Control Bridge...");
+ // Mobile Control bridge
+ var bridge = new MobileConrolEssentialsHuddleSpaceRoomBridge(room as EssentialsHuddleSpaceRoom);
AddBridgePostActivationHelper(bridge); // Lets things happen later when all devices are present
DeviceManager.AddDevice(bridge);
- Debug.Console(0, Debug.ErrorLogLevel.Notice, "Cotija Bridge Added...");
+ Debug.Console(0, Debug.ErrorLogLevel.Notice, "Mobile Control Bridge Added...");
}
else if (room is EssentialsHuddleVtc1Room)
{
@@ -511,9 +524,9 @@ namespace PepperDash.Essentials
Debug.Console(0, Debug.ErrorLogLevel.Notice, "Room is EssentialsHuddleVtc1Room, attempting to add to DeviceManager with Fusion");
DeviceManager.AddDevice(new EssentialsHuddleVtc1FusionController((EssentialsHuddleVtc1Room)room, 0xf1));
- Debug.Console(0, Debug.ErrorLogLevel.Notice, "Attempting to build Cotija Bridge...");
- // Cotija bridge
- var bridge = new CotijaEssentialsHuddleSpaceRoomBridge(room);
+ Debug.Console(0, Debug.ErrorLogLevel.Notice, "Attempting to build Mobile Control Bridge...");
+ // Mobile Control bridge
+ var bridge = new MobileConrolEssentialsHuddleSpaceRoomBridge(room);
AddBridgePostActivationHelper(bridge); // Lets things happen later when all devices are present
DeviceManager.AddDevice(bridge);
}
@@ -536,11 +549,11 @@ namespace PepperDash.Essentials
/// Helps add the post activation steps that link bridges to main controller
///
///
- void AddBridgePostActivationHelper(CotijaBridgeBase bridge)
+ void AddBridgePostActivationHelper(MobileControlBridgeBase bridge)
{
bridge.AddPostActivationAction(() =>
{
- var parent = DeviceManager.AllDevices.FirstOrDefault(d => d.Key == "appServer") as CotijaSystemController;
+ var parent = DeviceManager.AllDevices.FirstOrDefault(d => d.Key == "appServer") as MobileControlSystemController;
if (parent == null)
{
Debug.Console(0, bridge, "ERROR: Cannot connect app server room bridge. System controller not present");
diff --git a/PepperDashEssentials/Devices/Amplifier.cs b/PepperDashEssentials/Devices/Amplifier.cs
index bed85856..20d8efa0 100644
--- a/PepperDashEssentials/Devices/Amplifier.cs
+++ b/PepperDashEssentials/Devices/Amplifier.cs
@@ -12,6 +12,32 @@ namespace PepperDash.Essentials
{
public class Amplifier : Device, IRoutingSinkNoSwitching
{
+ public event SourceInfoChangeHandler CurrentSourceChange;
+
+ public string CurrentSourceInfoKey { get; set; }
+ public SourceListItem CurrentSourceInfo
+ {
+ get
+ {
+ return _CurrentSourceInfo;
+ }
+ set
+ {
+ if (value == _CurrentSourceInfo) return;
+
+ var handler = CurrentSourceChange;
+
+ if (handler != null)
+ handler(_CurrentSourceInfo, ChangeType.WillChange);
+
+ _CurrentSourceInfo = value;
+
+ if (handler != null)
+ handler(_CurrentSourceInfo, ChangeType.DidChange);
+ }
+ }
+ SourceListItem _CurrentSourceInfo;
+
public RoutingInputPort AudioIn { get; private set; }
public Amplifier(string key, string name)
diff --git a/PepperDashEssentials/Factory/DeviceFactory.cs b/PepperDashEssentials/Factory/DeviceFactory.cs
index 8e83fb9d..24c4bfe1 100644
--- a/PepperDashEssentials/Factory/DeviceFactory.cs
+++ b/PepperDashEssentials/Factory/DeviceFactory.cs
@@ -61,18 +61,18 @@ namespace PepperDash.Essentials
else if (typeName == "appserver")
{
- var props = JsonConvert.DeserializeObject(properties.ToString());
- return new CotijaSystemController(key, name, props);
+ var props = JsonConvert.DeserializeObject(properties.ToString());
+ return new MobileControlSystemController(key, name, props);
}
else if (typeName == "mobilecontrolbridge-ddvc01")
{
var comm = CommFactory.GetControlPropertiesConfig(dc);
- var bridge = new PepperDash.Essentials.Room.Cotija.CotijaDdvc01RoomBridge(key, name, comm.IpIdInt);
+ var bridge = new PepperDash.Essentials.Room.MobileControl.MobileControlDdvc01RoomBridge(key, name, comm.IpIdInt);
bridge.AddPreActivationAction(() =>
{
- var parent = DeviceManager.AllDevices.FirstOrDefault(d => d.Key == "appServer") as CotijaSystemController;
+ var parent = DeviceManager.AllDevices.FirstOrDefault(d => d.Key == "appServer") as MobileControlSystemController;
if (parent == null)
{
Debug.Console(0, bridge, "ERROR: Cannot connect bridge. System controller not present");
@@ -87,7 +87,7 @@ namespace PepperDash.Essentials
else if (typeName == "roomonwhenoccupancydetectedfeature")
{
- return new Room.Behaviours.RoomOnToDefaultSourceWhenOccupied(dc);
+ return new RoomOnToDefaultSourceWhenOccupied(dc);
}
return null;
diff --git a/PepperDashEssentials/FOR REFERENCE UI/OTHER/Fusion/EssentialsHuddleVtc1FusionController.cs b/PepperDashEssentials/Fusion/EssentialsHuddleVtc1FusionController.cs
similarity index 98%
rename from PepperDashEssentials/FOR REFERENCE UI/OTHER/Fusion/EssentialsHuddleVtc1FusionController.cs
rename to PepperDashEssentials/Fusion/EssentialsHuddleVtc1FusionController.cs
index c79d49d7..8ec55881 100644
--- a/PepperDashEssentials/FOR REFERENCE UI/OTHER/Fusion/EssentialsHuddleVtc1FusionController.cs
+++ b/PepperDashEssentials/Fusion/EssentialsHuddleVtc1FusionController.cs
@@ -14,6 +14,7 @@ using PepperDash.Core;
using PepperDash.Essentials;
using PepperDash.Essentials.Core;
using PepperDash.Essentials.Core.Config;
+using PepperDash.Essentials.Core.Fusion;
using PepperDash.Essentials.Devices.Common;
using PepperDash.Essentials.Devices.Common.Occupancy;
@@ -181,7 +182,7 @@ namespace PepperDash.Essentials.Fusion
// Moved to
CurrentRoomSourceNameSig = FusionRoom.CreateOffsetStringSig(84, "Display 1 - Current Source", eSigIoMask.InputSigOnly);
// Don't think we need to get current status of this as nothing should be alive yet.
- (Room as EssentialsHuddleVtc1Room).CurrentSingleSourceChange += Room_CurrentSourceInfoChange;
+ (Room as EssentialsHuddleVtc1Room).CurrentSourceChange += Room_CurrentSourceInfoChange;
FusionRoom.SystemPowerOn.OutputSig.SetSigFalseAction((Room as EssentialsHuddleVtc1Room).PowerOnToDefaultOrLastSource);
@@ -220,7 +221,7 @@ namespace PepperDash.Essentials.Fusion
break;
}
- var laptops = dict.Where(d => d.Value.SourceDevice is Laptop);
+ var laptops = dict.Where(d => d.Value.SourceDevice is Core.Devices.Laptop);
i = 1;
foreach (var kvp in laptops)
{
diff --git a/PepperDashEssentials/FOR REFERENCE UI/OTHER/Fusion/FusionSystemController.cs.orig b/PepperDashEssentials/Fusion/FusionSystemController.cs.orig
similarity index 100%
rename from PepperDashEssentials/FOR REFERENCE UI/OTHER/Fusion/FusionSystemController.cs.orig
rename to PepperDashEssentials/Fusion/FusionSystemController.cs.orig
diff --git a/PepperDashEssentials/PepperDashEssentials.csproj b/PepperDashEssentials/PepperDashEssentials.csproj
index 8ec13dc8..14725a1b 100644
--- a/PepperDashEssentials/PepperDashEssentials.csproj
+++ b/PepperDashEssentials/PepperDashEssentials.csproj
@@ -175,28 +175,23 @@
-
-
-
-
-
+
-
-
+
-
-
+
+
-
-
-
+
+
+
@@ -208,10 +203,10 @@
+
-
@@ -245,7 +240,7 @@
-
+
@@ -275,6 +270,9 @@
Essentials Devices Common
+
+
+
diff --git a/PepperDashEssentials/Room/Config/EssentialsDualDisplayRoomPropertiesConfig.cs b/PepperDashEssentials/Room/Config/EssentialsDualDisplayRoomPropertiesConfig.cs
new file mode 100644
index 00000000..949e1212
--- /dev/null
+++ b/PepperDashEssentials/Room/Config/EssentialsDualDisplayRoomPropertiesConfig.cs
@@ -0,0 +1,15 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Crestron.SimplSharp;
+
+using Newtonsoft.Json;
+
+namespace PepperDash.Essentials.Room.Config
+{
+ public class EssentialsDualDisplayRoomPropertiesConfig : EssentialsNDisplayRoomPropertiesConfig
+ {
+
+ }
+}
\ No newline at end of file
diff --git a/PepperDashEssentials/Room/Config/EssentialsHuddleVtc1PropertiesConfig.cs b/PepperDashEssentials/Room/Config/EssentialsHuddleVtc1PropertiesConfig.cs
index 16e49933..5ce13bfa 100644
--- a/PepperDashEssentials/Room/Config/EssentialsHuddleVtc1PropertiesConfig.cs
+++ b/PepperDashEssentials/Room/Config/EssentialsHuddleVtc1PropertiesConfig.cs
@@ -8,19 +8,10 @@ using Newtonsoft.Json;
namespace PepperDash.Essentials.Room.Config
{
- public class EssentialsHuddleVtc1PropertiesConfig : EssentialsRoomPropertiesConfig
+ public class EssentialsHuddleVtc1PropertiesConfig : EssentialsConferenceRoomPropertiesConfig
{
[JsonProperty("defaultDisplayKey")]
public string DefaultDisplayKey { get; set; }
- [JsonProperty("defaultAudioKey")]
- public string DefaultAudioKey { get; set; }
- [JsonProperty("sourceListKey")]
- public string SourceListKey { get; set; }
- [JsonProperty("defaultSourceItem")]
- public string DefaultSourceItem { get; set; }
- [JsonProperty("videoCodecKey")]
- public string VideoCodecKey { get; set; }
- [JsonProperty("audioCodecKey")]
- public string AudioCodecKey { get; set; }
+
}
}
\ No newline at end of file
diff --git a/PepperDashEssentials/Room/Config/EssentialsNDisplayRoomPropertiesConfig.cs b/PepperDashEssentials/Room/Config/EssentialsNDisplayRoomPropertiesConfig.cs
index 6bd2009e..bb6623aa 100644
--- a/PepperDashEssentials/Room/Config/EssentialsNDisplayRoomPropertiesConfig.cs
+++ b/PepperDashEssentials/Room/Config/EssentialsNDisplayRoomPropertiesConfig.cs
@@ -4,22 +4,36 @@ using System.Linq;
using System.Text;
using Crestron.SimplSharp;
+using PepperDash.Core;
+using PepperDash.Essentials.Core;
+
+using Newtonsoft.Json;
+
namespace PepperDash.Essentials.Room.Config
{
///
///
///
- public class EssentialsNDisplayRoomPropertiesConfig : EssentialsRoomPropertiesConfig
+ public class EssentialsNDisplayRoomPropertiesConfig : EssentialsConferenceRoomPropertiesConfig
{
+ [JsonProperty("defaultAudioBehavior")]
public string DefaultAudioBehavior { get; set; }
- public string DefaultAudioKey { get; set; }
+ [JsonProperty("defaultVideoBehavior")]
public string DefaultVideoBehavior { get; set; }
- public Dictionary Displays { get; set; }
- public string SourceListKey { get; set; }
+ [JsonProperty("displays")]
+ public Dictionary Displays { get; set; }
public EssentialsNDisplayRoomPropertiesConfig()
{
- Displays = new Dictionary();
+ Displays = new Dictionary();
}
+
}
+
+ public class DisplayItem : IKeyName
+ {
+ public string Key { get; set; }
+ public string Name { get; set; }
+ }
+
}
\ No newline at end of file
diff --git a/PepperDashEssentials/Room/Config/EssentialsRoomConfig.cs b/PepperDashEssentials/Room/Config/EssentialsRoomConfig.cs
index 7b7718eb..b34e923b 100644
--- a/PepperDashEssentials/Room/Config/EssentialsRoomConfig.cs
+++ b/PepperDashEssentials/Room/Config/EssentialsRoomConfig.cs
@@ -38,6 +38,12 @@ namespace PepperDash.Essentials.Room.Config
{
return new Device(roomConfig.Key, roomConfig.Name); // placeholder device that does nothing.
}
+ else if (typeName == "dualdisplay")
+ {
+ var rm = new EssentialsDualDisplayRoom(roomConfig);
+
+ return rm;
+ }
return null;
}
@@ -65,8 +71,8 @@ namespace PepperDash.Essentials.Room.Config
///
///
///
- public static PepperDash.Essentials.Devices.Common.Microphones.MicrophonePrivacyController GetMicrophonePrivacy(
- EssentialsRoomPropertiesConfig props, EssentialsHuddleVtc1Room room)
+ public static Core.Privacy.MicrophonePrivacyController GetMicrophonePrivacy(
+ EssentialsRoomPropertiesConfig props, IPrivacy room)
{
var microphonePrivacy = props.MicrophonePrivacy;
if (microphonePrivacy == null)
@@ -76,7 +82,7 @@ namespace PepperDash.Essentials.Room.Config
}
// Get the MicrophonePrivacy device from the device manager
var mP = (DeviceManager.GetDeviceForKey(props.MicrophonePrivacy.DeviceKey) as
- PepperDash.Essentials.Devices.Common.Microphones.MicrophonePrivacyController);
+ Core.Privacy.MicrophonePrivacyController);
// Set this room as the IPrivacy device
if (mP == null)
{
@@ -95,28 +101,30 @@ namespace PepperDash.Essentials.Room.Config
if (behaviour == "trackroomstate")
{
// Tie LED enable to room power state
- room.OnFeedback.OutputChange += (o, a) =>
+ var essRoom = room as EssentialsRoomBase;
+ essRoom.OnFeedback.OutputChange += (o, a) =>
{
- if (room.OnFeedback.BoolValue)
+ if (essRoom.OnFeedback.BoolValue)
mP.EnableLeds = true;
else
mP.EnableLeds = false;
};
- mP.EnableLeds = room.OnFeedback.BoolValue;
+ mP.EnableLeds = essRoom.OnFeedback.BoolValue;
}
else if (behaviour == "trackcallstate")
{
// Tie LED enable to room power state
- room.InCallFeedback.OutputChange += (o, a) =>
+ var inCallRoom = room as IHasInCallFeedback;
+ inCallRoom.InCallFeedback.OutputChange += (o, a) =>
{
- if (room.InCallFeedback.BoolValue)
+ if (inCallRoom.InCallFeedback.BoolValue)
mP.EnableLeds = true;
else
mP.EnableLeds = false;
};
- mP.EnableLeds = room.InCallFeedback.BoolValue;
+ mP.EnableLeds = inCallRoom.InCallFeedback.BoolValue;
}
return mP;
@@ -175,6 +183,25 @@ namespace PepperDash.Essentials.Room.Config
public bool ZeroVolumeWhenSwtichingVolumeDevices { get; set; }
}
+ public class EssentialsAvRoomPropertiesConfig : EssentialsRoomPropertiesConfig
+ {
+ [JsonProperty("defaultAudioKey")]
+ public string DefaultAudioKey { get; set; }
+ [JsonProperty("sourceListKey")]
+ public string SourceListKey { get; set; }
+ [JsonProperty("defaultSourceItem")]
+ public string DefaultSourceItem { get; set; }
+
+ }
+
+ public class EssentialsConferenceRoomPropertiesConfig : EssentialsAvRoomPropertiesConfig
+ {
+ [JsonProperty("videoCodecKey")]
+ public string VideoCodecKey { get; set; }
+ [JsonProperty("audioCodecKey")]
+ public string AudioCodecKey { get; set; }
+ }
+
public class EssentialsEnvironmentPropertiesConfig
{
public bool Enabled { get; set; }
diff --git a/PepperDashEssentials/Room/Emergency/EsentialsRoomEmergencyContactClosure.cs b/PepperDashEssentials/Room/Emergency/EsentialsRoomEmergencyContactClosure.cs
index 09e09bda..ab40d2fe 100644
--- a/PepperDashEssentials/Room/Emergency/EsentialsRoomEmergencyContactClosure.cs
+++ b/PepperDashEssentials/Room/Emergency/EsentialsRoomEmergencyContactClosure.cs
@@ -12,15 +12,7 @@ using PepperDash.Essentials.Room.Config;
namespace PepperDash.Essentials.Room
{
- public abstract class EssentialsRoomEmergencyBase : IKeyed
- {
- public string Key { get; private set; }
- public EssentialsRoomEmergencyBase(string key)
- {
- Key = key;
- }
- }
public class EssentialsRoomEmergencyContactClosure : EssentialsRoomEmergencyBase
diff --git a/PepperDashEssentials/Room/Types/EssentialsDualDisplayRoom.cs b/PepperDashEssentials/Room/Types/EssentialsDualDisplayRoom.cs
new file mode 100644
index 00000000..44c88724
--- /dev/null
+++ b/PepperDashEssentials/Room/Types/EssentialsDualDisplayRoom.cs
@@ -0,0 +1,656 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Crestron.SimplSharp;
+
+using Newtonsoft.Json;
+
+using PepperDash.Core;
+using PepperDash.Essentials.Core;
+using PepperDash.Essentials.Core.Devices;
+using PepperDash.Essentials.Core.Config;
+using PepperDash.Essentials.Room.Config;
+using PepperDash.Essentials.Devices.Common.Codec;
+using PepperDash.Essentials.Devices.Common.VideoCodec;
+using PepperDash.Essentials.Devices.Common.AudioCodec;
+
+namespace PepperDash.Essentials
+{
+ public class EssentialsDualDisplayRoom : EssentialsNDisplayRoomBase, IHasCurrentVolumeControls,
+ IRunRouteAction, IPrivacy, IRunDefaultCallRoute, IHasVideoCodec, IHasAudioCodec, IHasInCallFeedback
+ {
+ public event EventHandler CurrentVolumeDeviceChange;
+
+ public EssentialsDualDisplayRoomPropertiesConfig PropertiesConfig { get; private set; }
+
+ //************************
+ // Call-related stuff
+
+ public BoolFeedback InCallFeedback { get; private set; }
+
+ ///
+ /// States: 0 for on hook, 1 for video, 2 for audio, 3 for telekenesis
+ ///
+ public IntFeedback CallTypeFeedback { get; private set; }
+
+ ///
+ ///
+ ///
+ public BoolFeedback PrivacyModeIsOnFeedback { get; private set; }
+
+ ///
+ /// When something in the room is sharing with the far end or through other means
+ ///
+ public BoolFeedback IsSharingFeedback { get; private set; }
+
+ public IRoutingSinkWithSwitching LeftDisplay { get; private set; }
+ public IRoutingSinkWithSwitching RightDisplay { get; private set; }
+
+
+ protected override Func OnFeedbackFunc
+ {
+ get
+ {
+ return () =>
+ {
+ var leftDisp = LeftDisplay as DisplayBase;
+ var rightDisp = RightDisplay as DisplayBase;
+ var val = leftDisp != null && leftDisp.CurrentSourceInfo != null
+ && leftDisp.CurrentSourceInfo.Type == eSourceListItemType.Route
+ && rightDisp != null && rightDisp.CurrentSourceInfo != null
+ && rightDisp.CurrentSourceInfo.Type == eSourceListItemType.Route;
+ return val;
+ };
+ }
+ }
+
+ ///
+ ///
+ ///
+ protected override Func IsWarmingFeedbackFunc
+ {
+ get
+ {
+ return () =>
+ {
+ var leftDisp = LeftDisplay as DisplayBase;
+ var rightDisp = RightDisplay as DisplayBase;
+ if (leftDisp != null && RightDisplay != null)
+ return leftDisp.IsWarmingUpFeedback.BoolValue || rightDisp.IsWarmingUpFeedback.BoolValue;
+ else
+ return false;
+ };
+ }
+ }
+
+ ///
+ ///
+ ///
+ protected override Func IsCoolingFeedbackFunc
+ {
+ get
+ {
+ return () =>
+ {
+ var leftDisp = LeftDisplay as DisplayBase;
+ var rightDisp = RightDisplay as DisplayBase;
+ if (leftDisp != null && RightDisplay != null)
+ return leftDisp.IsCoolingDownFeedback.BoolValue || rightDisp.IsCoolingDownFeedback.BoolValue;
+ else
+ return false;
+ };
+ }
+ }
+
+ public IBasicVolumeControls DefaultAudioDevice { get; private set; }
+ public IBasicVolumeControls DefaultVolumeControls { get; private set; }
+
+ public VideoCodecBase VideoCodec { get; private set; }
+
+ public AudioCodecBase AudioCodec { get; private set; }
+
+ public bool ExcludeFromGlobalFunctions { get; set; }
+
+ public string DefaultSourceItem { get; set; }
+
+ public ushort DefaultVolume { get; set; }
+
+ ///
+ /// If room is off, enables power on to last source. Default true
+ ///
+ public bool EnablePowerOnToLastSource { get; set; }
+ string LastSourceKey;
+
+ ///
+ /// Sets the volume control device, and attaches/removes InUseTrackers with "audio"
+ /// tag to device.
+ ///
+ public IBasicVolumeControls CurrentVolumeControls
+ {
+ get { return _CurrentAudioDevice; }
+ set
+ {
+ if (value == _CurrentAudioDevice) return;
+
+ var oldDev = _CurrentAudioDevice;
+ // derigister this room from the device, if it can
+ if (oldDev is IInUseTracking)
+ (oldDev as IInUseTracking).InUseTracker.RemoveUser(this, "audio");
+ var handler = CurrentVolumeDeviceChange;
+ if (handler != null)
+ CurrentVolumeDeviceChange(this, new VolumeDeviceChangeEventArgs(oldDev, value, ChangeType.WillChange));
+ _CurrentAudioDevice = value;
+ if (handler != null)
+ CurrentVolumeDeviceChange(this, new VolumeDeviceChangeEventArgs(oldDev, value, ChangeType.DidChange));
+ // register this room with new device, if it can
+ if (_CurrentAudioDevice is IInUseTracking)
+ (_CurrentAudioDevice as IInUseTracking).InUseTracker.AddUser(this, "audio");
+ }
+ }
+ IBasicVolumeControls _CurrentAudioDevice;
+
+ ///
+ /// "codecOsd"
+ ///
+ public string DefaultCodecRouteString { get { return "codecOsd"; } }
+
+ ///
+ /// Temporary implementation. Returns the schedule-ready object or null if none. Fow now,
+ /// always returns the VideoCodec if it is capable
+ ///
+ public IHasScheduleAwareness ScheduleSource { get { return VideoCodec as IHasScheduleAwareness; } }
+
+ CCriticalSection SourceSelectLock = new CCriticalSection();
+
+ public EssentialsDualDisplayRoom(DeviceConfig config)
+ : base(config)
+ {
+ try
+ {
+ PropertiesConfig = JsonConvert.DeserializeObject
+ (config.Properties.ToString());
+
+ var leftDisp = PropertiesConfig.Displays[eSourceListItemDestinationTypes.leftDisplay];
+ if (leftDisp != null)
+ {
+ if (!string.IsNullOrEmpty(leftDisp.Key))
+ {
+ LeftDisplay = DeviceManager.GetDeviceForKey(leftDisp.Key) as IRoutingSinkWithSwitching;
+ Displays.Add(eSourceListItemDestinationTypes.leftDisplay, LeftDisplay);
+ }
+ else
+ Debug.Console(0, this, "Unable to get LeftDisplay for Room");
+ }
+
+ var rightDisp = PropertiesConfig.Displays[eSourceListItemDestinationTypes.rightDisplay];
+ if (rightDisp != null)
+ {
+ if (!string.IsNullOrEmpty(rightDisp.Key))
+ {
+ LeftDisplay = DeviceManager.GetDeviceForKey(rightDisp.Key) as IRoutingSinkWithSwitching;
+ Displays.Add(eSourceListItemDestinationTypes.rightDisplay, RightDisplay);
+ }
+ else
+ Debug.Console(0, this, "Unable to get LeftDisplay for Room");
+ }
+
+ VideoCodec = DeviceManager.GetDeviceForKey(PropertiesConfig.VideoCodecKey) as
+ PepperDash.Essentials.Devices.Common.VideoCodec.VideoCodecBase;
+ if (VideoCodec == null)
+ throw new ArgumentNullException("codec cannot be null");
+
+ AudioCodec = DeviceManager.GetDeviceForKey(PropertiesConfig.AudioCodecKey) as
+ PepperDash.Essentials.Devices.Common.AudioCodec.AudioCodecBase;
+ if (AudioCodec == null)
+ Debug.Console(0, this, "No Audio Codec Found");
+
+ DefaultAudioDevice = DeviceManager.GetDeviceForKey(PropertiesConfig.DefaultAudioKey) as IBasicVolumeControls;
+
+ Initialize();
+ }
+ catch (Exception e)
+ {
+ Debug.Console(1, this, "Error building room \n{0}", e);
+ }
+ }
+
+ void Initialize()
+ {
+ if (DefaultAudioDevice is IBasicVolumeControls)
+ DefaultVolumeControls = DefaultAudioDevice as IBasicVolumeControls;
+ else if (DefaultAudioDevice is IHasVolumeDevice)
+ DefaultVolumeControls = (DefaultAudioDevice as IHasVolumeDevice).VolumeDevice;
+ CurrentVolumeControls = DefaultVolumeControls;
+
+
+ var leftDisp = LeftDisplay as DisplayBase;
+ if (leftDisp != null)
+ InitializeDisplay(leftDisp);
+
+ var rightDisp = RightDisplay as DisplayBase;
+ if (rightDisp != null)
+ InitializeDisplay(rightDisp);
+
+ // Get Microphone Privacy object, if any
+ this.MicrophonePrivacy = EssentialsRoomConfigHelper.GetMicrophonePrivacy(PropertiesConfig, this);
+
+ Debug.Console(2, this, "Microphone Privacy Config evaluated.");
+
+ // Get emergency object, if any
+ this.Emergency = EssentialsRoomConfigHelper.GetEmergency(PropertiesConfig, this);
+
+ Debug.Console(2, this, "Emergency Config evaluated.");
+
+ // Combines call feedback from both codecs if available
+ InCallFeedback = new BoolFeedback(() =>
+ {
+ bool inAudioCall = false;
+ bool inVideoCall = false;
+
+ if (AudioCodec != null)
+ inAudioCall = AudioCodec.IsInCall;
+
+ if (VideoCodec != null)
+ inVideoCall = VideoCodec.IsInCall;
+
+ if (inAudioCall || inVideoCall)
+ return true;
+ else
+ return false;
+ });
+
+ VideoCodec.CallStatusChange += (o, a) => this.InCallFeedback.FireUpdate();
+
+ if (AudioCodec != null)
+ AudioCodec.CallStatusChange += (o, a) => this.InCallFeedback.FireUpdate();
+
+ IsSharingFeedback = new BoolFeedback(() => VideoCodec.SharingContentIsOnFeedback.BoolValue);
+ VideoCodec.SharingContentIsOnFeedback.OutputChange += (o, a) => this.IsSharingFeedback.FireUpdate();
+
+ // link privacy to VC (for now?)
+ PrivacyModeIsOnFeedback = new BoolFeedback(() => VideoCodec.PrivacyModeIsOnFeedback.BoolValue);
+ VideoCodec.PrivacyModeIsOnFeedback.OutputChange += (o, a) => this.PrivacyModeIsOnFeedback.FireUpdate();
+
+ CallTypeFeedback = new IntFeedback(() => 0);
+
+ SourceListKey = "default";
+ EnablePowerOnToLastSource = true;
+ }
+
+ void InitializeDisplay(DisplayBase disp)
+ {
+ if (disp != null)
+ {
+ // Link power, warming, cooling to display
+ disp.PowerIsOnFeedback.OutputChange += (o, a) =>
+ {
+ if (disp.PowerIsOnFeedback.BoolValue != OnFeedback.BoolValue)
+ {
+ if (!disp.PowerIsOnFeedback.BoolValue)
+ disp.CurrentSourceInfo = null;
+ OnFeedback.FireUpdate();
+ }
+ if (disp.PowerIsOnFeedback.BoolValue)
+ {
+ SetDefaultLevels();
+ }
+ };
+
+ disp.IsWarmingUpFeedback.OutputChange += (o, a) =>
+ {
+ IsWarmingUpFeedback.FireUpdate();
+ if (!IsWarmingUpFeedback.BoolValue)
+ (CurrentVolumeControls as IBasicVolumeWithFeedback).SetVolume(DefaultVolume);
+ };
+ disp.IsCoolingDownFeedback.OutputChange += (o, a) =>
+ {
+ IsCoolingDownFeedback.FireUpdate();
+ };
+ }
+ }
+
+ protected override void CustomSetConfig(DeviceConfig config)
+ {
+ var newPropertiesConfig = JsonConvert.DeserializeObject(config.Properties.ToString());
+
+ if (newPropertiesConfig != null)
+ PropertiesConfig = newPropertiesConfig;
+
+ ConfigWriter.UpdateRoomConfig(config);
+ }
+
+ public override bool CustomActivate()
+ {
+ // Add Occupancy object from config
+ if (PropertiesConfig.Occupancy != null)
+ this.SetRoomOccupancy(DeviceManager.GetDeviceForKey(PropertiesConfig.Occupancy.DeviceKey) as
+ IOccupancyStatusProvider, PropertiesConfig.Occupancy.TimeoutMinutes);
+
+ this.LogoUrl = PropertiesConfig.Logo.GetUrl();
+ this.SourceListKey = PropertiesConfig.SourceListKey;
+ this.DefaultSourceItem = PropertiesConfig.DefaultSourceItem;
+ this.DefaultVolume = (ushort)(PropertiesConfig.Volumes.Master.Level * 65535 / 100);
+
+ return base.CustomActivate();
+ }
+
+ ///
+ ///
+ ///
+ protected override void EndShutdown()
+ {
+ VideoCodec.EndAllCalls();
+
+ SetDefaultLevels();
+
+ RunDefaultPresentRoute();
+
+ CrestronEnvironment.Sleep(1000);
+
+ RunRouteAction("roomOff");
+ }
+
+ ///
+ /// Routes the default source item, if any. Returns true when default route exists
+ ///
+ public override bool RunDefaultPresentRoute()
+ {
+ if (DefaultSourceItem != null)
+ RunRouteAction(DefaultSourceItem);
+
+ return DefaultSourceItem != null;
+ }
+
+ ///
+ /// Sets up the room when started into call mode without presenting a source
+ ///
+ ///
+ public bool RunDefaultCallRoute()
+ {
+ RunRouteAction(DefaultCodecRouteString);
+ return true;
+ }
+
+ ///
+ ///
+ ///
+ ///
+ public void RunRouteAction(string routeKey)
+ {
+ RunRouteAction(routeKey, null);
+ }
+
+ ///
+ /// Gets a source from config list SourceListKey and dynamically build and executes the
+ /// route or commands
+ ///
+ ///
+ public void RunRouteAction(string routeKey, Action successCallback)
+ {
+ // Run this on a separate thread
+ new CTimer(o =>
+ {
+ // try to prevent multiple simultaneous selections
+ SourceSelectLock.TryEnter();
+
+ try
+ {
+
+ Debug.Console(1, this, "Run route action '{0}'", routeKey);
+ var dict = ConfigReader.ConfigObject.GetSourceListForKey(SourceListKey);
+ if (dict == null)
+ {
+ Debug.Console(1, this, "WARNING: Config source list '{0}' not found", SourceListKey);
+ return;
+ }
+
+ // Try to get the list item by it's string key
+ if (!dict.ContainsKey(routeKey))
+ {
+ Debug.Console(1, this, "WARNING: No item '{0}' found on config list '{1}'",
+ routeKey, SourceListKey);
+ return;
+ }
+
+ // End usage timer on last source
+ if (!string.IsNullOrEmpty(LastSourceKey))
+ {
+ var usageLastSource = dict[LastSourceKey].SourceDevice as IUsageTracking;
+ if (usageLastSource != null && usageLastSource.UsageTracker != null)
+ {
+ try
+ {
+ // There MAY have been failures in here. Protect
+ usageLastSource.UsageTracker.EndDeviceUsage();
+ }
+ catch (Exception e)
+ {
+ Debug.Console(1, this, "*#* EXCEPTION in end usage tracking:\r{0}", e);
+ }
+ }
+ }
+
+ // Let's run it
+ var item = dict[routeKey];
+ if (routeKey.ToLower() != "roomoff")
+ {
+
+ LastSourceKey = routeKey;
+ }
+ //else
+ // CurrentSourceInfoKey = null;
+
+ // hand off the individual routes to this helper
+ foreach (var route in item.RouteList)
+ DoRouteItem(route, item, routeKey);
+
+ // Start usage timer on routed source
+ var usageNewSource = item.SourceDevice as IUsageTracking;
+ if (usageNewSource != null && usageNewSource.UsageTracker != null) // Have to make sure there is a usage tracker!
+ {
+ (item.SourceDevice as IUsageTracking).UsageTracker.StartDeviceUsage();
+ }
+
+ // See if this can be moved into common, base-class method -------------
+
+
+ // Set volume control, using default if non provided
+ IBasicVolumeControls volDev = null;
+ // Handle special cases for volume control
+ if (string.IsNullOrEmpty(item.VolumeControlKey)
+ || item.VolumeControlKey.Equals("$defaultAudio", StringComparison.OrdinalIgnoreCase))
+ volDev = DefaultVolumeControls;
+
+ // Or a specific device, probably rarely used.
+ else
+ {
+ var dev = DeviceManager.GetDeviceForKey(item.VolumeControlKey);
+ if (dev is IBasicVolumeControls)
+ volDev = dev as IBasicVolumeControls;
+ else if (dev is IHasVolumeDevice)
+ volDev = (dev as IHasVolumeDevice).VolumeDevice;
+ }
+
+ if (volDev != CurrentVolumeControls)
+ {
+ // zero the volume on the device we are leaving.
+ // Set the volume to default on device we are entering
+ if (ZeroVolumeWhenSwtichingVolumeDevices && CurrentVolumeControls is IBasicVolumeWithFeedback)
+ {
+ var vd = CurrentVolumeControls as IBasicVolumeWithFeedback;
+ SavedVolumeLevels[vd] = (uint)vd.VolumeLevelFeedback.IntValue;
+ vd.SetVolume(0);
+ }
+
+ CurrentVolumeControls = volDev;
+ if (ZeroVolumeWhenSwtichingVolumeDevices && CurrentVolumeControls is IBasicVolumeWithFeedback)
+ {
+ var vd = CurrentVolumeControls as IBasicVolumeWithFeedback;
+ ushort vol = (SavedVolumeLevels.ContainsKey(vd) ? (ushort)SavedVolumeLevels[vd] : DefaultVolume);
+ vd.SetVolume(vol);
+ }
+ }
+ // -----------------------------------------------------------------------
+
+
+
+ // store the name and UI info for routes
+ if (item.SourceKey == "$off")
+ {
+ LeftDisplay.CurrentSourceInfoKey = routeKey;
+ LeftDisplay.CurrentSourceInfo = null;
+ RightDisplay.CurrentSourceInfoKey = routeKey;
+ RightDisplay.CurrentSourceInfo = null;
+ }
+ //else if (item.SourceKey != null)
+ //{
+ // if(item.RouteList
+ // CurrentSourceInfoKey = routeKey;
+ // CurrentSourceInfo = item;
+ //}
+
+ OnFeedback.FireUpdate();
+
+ // report back when done
+ if (successCallback != null)
+ successCallback();
+ }
+ catch (Exception e)
+ {
+ Debug.Console(1, this, "ERROR in routing: {0}", e);
+ }
+
+ SourceSelectLock.Leave();
+ }, 0); // end of CTimer
+ }
+
+
+ ///
+ ///
+ ///
+ ///
+ void DoRouteItem(SourceRouteListItem route, SourceListItem sourceItem, string sourceItemKey)
+ {
+ // if there is a $defaultAll on route, run two separate
+ if (route.DestinationKey.Equals("$defaultAll", StringComparison.OrdinalIgnoreCase))
+ {
+ // Going to assume a single-path route for now
+ var tempVideo = new SourceRouteListItem
+ {
+ DestinationKey = "$defaultDisplay",
+ SourceKey = route.SourceKey,
+ Type = eRoutingSignalType.Video
+ };
+ DoRoute(tempVideo, sourceItem, sourceItemKey);
+ }
+ else
+ DoRoute(route, sourceItem, sourceItemKey);
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ bool DoRoute(SourceRouteListItem route, SourceListItem sourceItem, string sourceItemKey)
+ {
+ IRoutingSinkNoSwitching dest = null;
+
+ if (route.DestinationKey.Equals("$defaultaudio", StringComparison.OrdinalIgnoreCase))
+ dest = DefaultAudioDevice as IRoutingSinkNoSwitching;
+ else if (route.DestinationKey.Equals(LeftDisplay.Key, StringComparison.OrdinalIgnoreCase))
+ dest = LeftDisplay;
+ else if (route.DestinationKey.Equals(RightDisplay.Key, StringComparison.OrdinalIgnoreCase))
+ dest = RightDisplay;
+ else
+ dest = DeviceManager.GetDeviceForKey(route.DestinationKey) as IRoutingSinkNoSwitching;
+
+ if (dest == null)
+ {
+ Debug.Console(1, this, "Cannot route, unknown destination '{0}'", route.DestinationKey);
+ return false;
+ }
+
+ if (route.SourceKey.Equals("$off", StringComparison.OrdinalIgnoreCase))
+ {
+ dest.ReleaseRoute();
+
+
+
+ if (dest is IPower)
+ (dest as IPower).PowerOff();
+ }
+ else
+ {
+ var source = DeviceManager.GetDeviceForKey(route.SourceKey) as IRoutingOutputs;
+ if (source == null)
+ {
+ Debug.Console(1, this, "Cannot route unknown source '{0}' to {1}", route.SourceKey, route.DestinationKey);
+ return false;
+ }
+ dest.ReleaseAndMakeRoute(source, route.Type);
+
+ dest.CurrentSourceInfoKey = sourceItemKey;
+ dest.CurrentSourceInfo = sourceItem;
+ }
+ return true;
+ }
+
+ public override void RoomVacatedForTimeoutPeriod(object o)
+ {
+ //Implement this
+ }
+
+ ///
+ /// Does what it says
+ ///
+ public override void SetDefaultLevels()
+ {
+ Debug.Console(1, this, "Restoring default levels");
+ var vc = CurrentVolumeControls as IBasicVolumeWithFeedback;
+ if (vc != null)
+ vc.SetVolume(DefaultVolume);
+ }
+ ///
+ /// Will power the room on with the last-used source
+ ///
+ public override void PowerOnToDefaultOrLastSource()
+ {
+ if (!EnablePowerOnToLastSource || LastSourceKey == null)
+ return;
+ RunRouteAction(LastSourceKey);
+ }
+
+ ///
+ /// Runs "roomOff" action on all rooms not set to ExcludeFromGlobalFunctions
+ ///
+ public static void AllRoomsOff()
+ {
+ var allRooms = DeviceManager.AllDevices.Where(d =>
+ d is EssentialsHuddleSpaceRoom && !(d as EssentialsHuddleSpaceRoom).ExcludeFromGlobalFunctions);
+ foreach (var room in allRooms)
+ (room as EssentialsHuddleSpaceRoom).RunRouteAction("roomOff");
+ }
+
+ #region IPrivacy Members
+
+
+ public void PrivacyModeOff()
+ {
+ VideoCodec.PrivacyModeOff();
+ }
+
+ public void PrivacyModeOn()
+ {
+ VideoCodec.PrivacyModeOn();
+ }
+
+ public void PrivacyModeToggle()
+ {
+ VideoCodec.PrivacyModeToggle();
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/PepperDashEssentials/Room/Types/EssentialsHuddleSpaceRoom.cs b/PepperDashEssentials/Room/Types/EssentialsHuddleSpaceRoom.cs
index 63f0bb29..e4b503c3 100644
--- a/PepperDashEssentials/Room/Types/EssentialsHuddleSpaceRoom.cs
+++ b/PepperDashEssentials/Room/Types/EssentialsHuddleSpaceRoom.cs
@@ -13,10 +13,10 @@ using PepperDash.Essentials.Room.Config;
namespace PepperDash.Essentials
{
- public class EssentialsHuddleSpaceRoom : EssentialsRoomBase, IHasCurrentSourceInfoChange, IRunRouteAction, IRunDefaultPresentRoute, IHasCurrentVolumeControls
+ public class EssentialsHuddleSpaceRoom : EssentialsRoomBase, IHasCurrentSourceInfoChange, IRunRouteAction, IRunDefaultPresentRoute, IHasCurrentVolumeControls, IHasDefaultDisplay
{
public event EventHandler CurrentVolumeDeviceChange;
- public event SourceInfoChangeHandler CurrentSingleSourceChange;
+ public event SourceInfoChangeHandler CurrentSourceChange;
protected override Func OnFeedbackFunc
{
@@ -76,11 +76,6 @@ namespace PepperDash.Essentials
public bool ExcludeFromGlobalFunctions { get; set; }
- ///
- /// The config name of the source list
- ///
- public string SourceListKey { get; set; }
-
public string DefaultSourceItem { get; set; }
public ushort DefaultVolume { get; set; }
@@ -124,17 +119,17 @@ namespace PepperDash.Essentials
public SourceListItem CurrentSourceInfo
{
get { return _CurrentSourceInfo; }
- private set
+ set
{
if (value == _CurrentSourceInfo) return;
- var handler = CurrentSingleSourceChange;
+ var handler = CurrentSourceChange;
// remove from in-use tracker, if so equipped
if(_CurrentSourceInfo != null && _CurrentSourceInfo.SourceDevice is IInUseTracking)
(_CurrentSourceInfo.SourceDevice as IInUseTracking).InUseTracker.RemoveUser(this, "control");
if (handler != null)
- handler(this, _CurrentSourceInfo, ChangeType.WillChange);
+ handler(_CurrentSourceInfo, ChangeType.WillChange);
_CurrentSourceInfo = value;
@@ -142,12 +137,12 @@ namespace PepperDash.Essentials
if (_CurrentSourceInfo != null && _CurrentSourceInfo.SourceDevice is IInUseTracking)
(_CurrentSourceInfo.SourceDevice as IInUseTracking).InUseTracker.AddUser(this, "control");
if (handler != null)
- handler(this, _CurrentSourceInfo, ChangeType.DidChange);
+ handler( _CurrentSourceInfo, ChangeType.DidChange);
}
}
SourceListItem _CurrentSourceInfo;
- public string CurrentSourceInfoKey { get; private set; }
+ public string CurrentSourceInfoKey { get; set; }
public EssentialsHuddleSpaceRoom(DeviceConfig config)
: base(config)
@@ -251,7 +246,7 @@ namespace PepperDash.Essentials
// Add Occupancy object from config
if (PropertiesConfig.Occupancy != null)
this.SetRoomOccupancy(DeviceManager.GetDeviceForKey(PropertiesConfig.Occupancy.DeviceKey) as
- PepperDash.Essentials.Devices.Common.Occupancy.IOccupancyStatusProvider, PropertiesConfig.Occupancy.TimeoutMinutes);
+ IOccupancyStatusProvider, PropertiesConfig.Occupancy.TimeoutMinutes);
this.LogoUrl = PropertiesConfig.Logo.GetUrl();
this.SourceListKey = PropertiesConfig.SourceListKey;
diff --git a/PepperDashEssentials/Room/Types/EssentialsHuddleVtc1Room.cs b/PepperDashEssentials/Room/Types/EssentialsHuddleVtc1Room.cs
index 991a3470..62fbe754 100644
--- a/PepperDashEssentials/Room/Types/EssentialsHuddleVtc1Room.cs
+++ b/PepperDashEssentials/Room/Types/EssentialsHuddleVtc1Room.cs
@@ -16,11 +16,11 @@ using PepperDash.Essentials.Devices.Common.AudioCodec;
namespace PepperDash.Essentials
{
- public class EssentialsHuddleVtc1Room : EssentialsRoomBase, IHasCurrentSourceInfoChange,
- IPrivacy, IHasCurrentVolumeControls, IRunRouteAction, IRunDefaultCallRoute, IHasVideoCodec, IHasAudioCodec
+ public class EssentialsHuddleVtc1Room : EssentialsRoomBase, IHasCurrentSourceInfoChange,
+ IPrivacy, IHasCurrentVolumeControls, IRunRouteAction, IRunDefaultCallRoute, IHasVideoCodec, IHasAudioCodec, IHasDefaultDisplay, IHasInCallFeedback
{
public event EventHandler CurrentVolumeDeviceChange;
- public event SourceInfoChangeHandler CurrentSingleSourceChange;
+ public event SourceInfoChangeHandler CurrentSourceChange;
//************************
@@ -112,11 +112,6 @@ namespace PepperDash.Essentials
public AudioCodecBase AudioCodec { get; private set; }
public bool ExcludeFromGlobalFunctions { get; set; }
-
- ///
- /// The config name of the source list
- ///
- public string SourceListKey { get; set; }
public string DefaultSourceItem { get; set; }
@@ -162,17 +157,17 @@ namespace PepperDash.Essentials
public SourceListItem CurrentSourceInfo
{
get { return _CurrentSourceInfo; }
- private set
+ set
{
if (value == _CurrentSourceInfo) return;
- var handler = CurrentSingleSourceChange;
+ var handler = CurrentSourceChange;
// remove from in-use tracker, if so equipped
if(_CurrentSourceInfo != null && _CurrentSourceInfo.SourceDevice is IInUseTracking)
(_CurrentSourceInfo.SourceDevice as IInUseTracking).InUseTracker.RemoveUser(this, "control");
if (handler != null)
- handler(this, _CurrentSourceInfo, ChangeType.WillChange);
+ handler(_CurrentSourceInfo, ChangeType.WillChange);
_CurrentSourceInfo = value;
@@ -180,12 +175,12 @@ namespace PepperDash.Essentials
if (_CurrentSourceInfo != null && _CurrentSourceInfo.SourceDevice is IInUseTracking)
(_CurrentSourceInfo.SourceDevice as IInUseTracking).InUseTracker.AddUser(this, "control");
if (handler != null)
- handler(this, _CurrentSourceInfo, ChangeType.DidChange);
+ handler(_CurrentSourceInfo, ChangeType.DidChange);
}
}
SourceListItem _CurrentSourceInfo;
- public string CurrentSourceInfoKey { get; private set; }
+ public string CurrentSourceInfoKey { get; set; }
///
/// "codecOsd"
@@ -340,7 +335,7 @@ namespace PepperDash.Essentials
// Add Occupancy object from config
if (PropertiesConfig.Occupancy != null)
this.SetRoomOccupancy(DeviceManager.GetDeviceForKey(PropertiesConfig.Occupancy.DeviceKey) as
- PepperDash.Essentials.Devices.Common.Occupancy.IOccupancyStatusProvider, PropertiesConfig.Occupancy.TimeoutMinutes);
+ IOccupancyStatusProvider, PropertiesConfig.Occupancy.TimeoutMinutes);
this.LogoUrl = PropertiesConfig.Logo.GetUrl();
this.SourceListKey = PropertiesConfig.SourceListKey;
diff --git a/PepperDashEssentials/Room/Types/EssentialsNDisplayRoomBase.cs b/PepperDashEssentials/Room/Types/EssentialsNDisplayRoomBase.cs
index 9cdb14c9..85f52c9f 100644
--- a/PepperDashEssentials/Room/Types/EssentialsNDisplayRoomBase.cs
+++ b/PepperDashEssentials/Room/Types/EssentialsNDisplayRoomBase.cs
@@ -12,34 +12,22 @@ using PepperDash.Essentials.Core.Devices;
using PepperDash.Essentials.Core.Config;
using PepperDash.Essentials.Room.Config;
-namespace PepperDash.Essentials.Room.Types
+namespace PepperDash.Essentials
{
///
/// Base class for rooms with more than a single display
///
- public abstract class EssentialsNDisplayRoomBase : EssentialsRoomBase
+ public abstract class EssentialsNDisplayRoomBase : EssentialsRoomBase, IHasMultipleDisplays
{
- public event SourceInfoChangeHandler CurrentSingleSourceChange;
+ //public event SourceInfoChangeHandler CurrentSingleSourceChange;
- public Dictionary Displays { get; protected set; }
-
- protected override Func IsWarmingFeedbackFunc { get { return () => false; ; } }
- protected override Func IsCoolingFeedbackFunc { get { return () => false; } }
+ public Dictionary Displays { get; protected set;}
public EssentialsNDisplayRoomBase(DeviceConfig config)
: base (config)
{
- Displays = new Dictionary();
+ Displays = new Dictionary();
- var propertiesConfig = JsonConvert.DeserializeObject(config.Properties.ToString());
-
- foreach (var display in propertiesConfig.Displays)
- {
- var displayDevice = DeviceManager.GetDeviceForKey(display.Value) as IRoutingSinkWithSwitching;
-
- if (displayDevice != null)
- Displays.Add(display.Key, displayDevice);
- }
}
}
}
\ No newline at end of file
diff --git a/PepperDashEssentials/UI/EssentialsTouchpanelController.cs b/PepperDashEssentials/UI/EssentialsTouchpanelController.cs
index e15f5b6c..678355e3 100644
--- a/PepperDashEssentials/UI/EssentialsTouchpanelController.cs
+++ b/PepperDashEssentials/UI/EssentialsTouchpanelController.cs
@@ -13,7 +13,7 @@ using PepperDash.Essentials.Core.PageManagers;
namespace PepperDash.Essentials
{
- public class EssentialsTouchpanelController : Device
+ public class EssentialsTouchpanelController : Device, IHasBasicTriListWithSmartObject
{
public BasicTriListWithSmartObject Panel { get; private set; }
diff --git a/PepperDashEssentials/UI/SubpageReferenceListSourceItem.cs b/PepperDashEssentials/UI/SubpageReferenceListSourceItem.cs
index 73cc5e71..3e1869cc 100644
--- a/PepperDashEssentials/UI/SubpageReferenceListSourceItem.cs
+++ b/PepperDashEssentials/UI/SubpageReferenceListSourceItem.cs
@@ -25,11 +25,11 @@ namespace PepperDash.Essentials
public void RegisterForSourceChange(IHasCurrentSourceInfoChange room)
{
- room.CurrentSingleSourceChange -= room_CurrentSourceInfoChange;
- room.CurrentSingleSourceChange += room_CurrentSourceInfoChange;
+ room.CurrentSourceChange -= room_CurrentSourceInfoChange;
+ room.CurrentSourceChange += room_CurrentSourceInfoChange;
}
- void room_CurrentSourceInfoChange(EssentialsRoomBase room, SourceListItem info, ChangeType type)
+ void room_CurrentSourceInfoChange(SourceListItem info, ChangeType type)
{
if (type == ChangeType.WillChange && info == SourceItem)
ClearFeedback();
diff --git a/PepperDashEssentials/UIDrivers/EssentialsHuddle/EssentialsHuddlePanelAvFunctionsDriver.cs b/PepperDashEssentials/UIDrivers/EssentialsHuddle/EssentialsHuddlePanelAvFunctionsDriver.cs
index 9a2b7a31..144fafb4 100644
--- a/PepperDashEssentials/UIDrivers/EssentialsHuddle/EssentialsHuddlePanelAvFunctionsDriver.cs
+++ b/PepperDashEssentials/UIDrivers/EssentialsHuddle/EssentialsHuddlePanelAvFunctionsDriver.cs
@@ -733,7 +733,7 @@ namespace PepperDash.Essentials
// Disconnect current room
_CurrentRoom.CurrentVolumeDeviceChange -= this.CurrentRoom_CurrentAudioDeviceChange;
ClearAudioDeviceConnections();
- _CurrentRoom.CurrentSingleSourceChange -= this.CurrentRoom_SourceInfoChange;
+ _CurrentRoom.CurrentSourceChange -= this.CurrentRoom_SourceInfoChange;
DisconnectSource(_CurrentRoom.CurrentSourceInfo);
_CurrentRoom.ShutdownPromptTimer.HasStarted -= ShutdownPromptTimer_HasStarted;
_CurrentRoom.ShutdownPromptTimer.HasFinished -= ShutdownPromptTimer_HasFinished;
@@ -803,7 +803,7 @@ namespace PepperDash.Essentials
_CurrentRoom.CurrentVolumeDeviceChange += CurrentRoom_CurrentAudioDeviceChange;
RefreshAudioDeviceConnections();
- _CurrentRoom.CurrentSingleSourceChange += CurrentRoom_SourceInfoChange;
+ _CurrentRoom.CurrentSourceChange += CurrentRoom_SourceInfoChange;
RefreshSourceInfo();
(Parent as EssentialsPanelMainInterfaceDriver).HeaderDriver.SetupHeaderButtons(this, CurrentRoom);
@@ -1064,8 +1064,7 @@ namespace PepperDash.Essentials
///
/// Handles source change
///
- void CurrentRoom_SourceInfoChange(EssentialsRoomBase room,
- SourceListItem info, ChangeType change)
+ void CurrentRoom_SourceInfoChange(SourceListItem info, ChangeType change)
{
if (change == ChangeType.WillChange)
DisconnectSource(info);
diff --git a/PepperDashEssentials/UIDrivers/EssentialsHuddleVTC/EssentialsHuddleVtc1PanelAvFunctionsDriver.cs b/PepperDashEssentials/UIDrivers/EssentialsHuddleVTC/EssentialsHuddleVtc1PanelAvFunctionsDriver.cs
index be51a4aa..57e68010 100644
--- a/PepperDashEssentials/UIDrivers/EssentialsHuddleVTC/EssentialsHuddleVtc1PanelAvFunctionsDriver.cs
+++ b/PepperDashEssentials/UIDrivers/EssentialsHuddleVTC/EssentialsHuddleVtc1PanelAvFunctionsDriver.cs
@@ -885,7 +885,7 @@ namespace PepperDash.Essentials
// Disconnect current room
_CurrentRoom.CurrentVolumeDeviceChange -= this.CurrentRoom_CurrentAudioDeviceChange;
ClearAudioDeviceConnections();
- _CurrentRoom.CurrentSingleSourceChange -= this.CurrentRoom_SourceInfoChange;
+ _CurrentRoom.CurrentSourceChange -= this.CurrentRoom_SourceInfoChange;
DisconnectSource(_CurrentRoom.CurrentSourceInfo);
_CurrentRoom.ShutdownPromptTimer.HasStarted -= ShutdownPromptTimer_HasStarted;
_CurrentRoom.ShutdownPromptTimer.HasFinished -= ShutdownPromptTimer_HasFinished;
@@ -924,7 +924,7 @@ namespace PepperDash.Essentials
_CurrentRoom.CurrentVolumeDeviceChange += CurrentRoom_CurrentAudioDeviceChange;
RefreshAudioDeviceConnections();
- _CurrentRoom.CurrentSingleSourceChange += CurrentRoom_SourceInfoChange;
+ _CurrentRoom.CurrentSourceChange += CurrentRoom_SourceInfoChange;
RefreshSourceInfo();
if (_CurrentRoom.VideoCodec is IHasScheduleAwareness)
@@ -939,7 +939,7 @@ namespace PepperDash.Essentials
SetActiveCallListSharingContentStatus();
if (_CurrentRoom != null)
- _CurrentRoom.CurrentSingleSourceChange += new SourceInfoChangeHandler(CurrentRoom_CurrentSingleSourceChange);
+ _CurrentRoom.CurrentSourceChange += new SourceInfoChangeHandler(CurrentRoom_CurrentSingleSourceChange);
TriList.SetSigFalseAction(UIBoolJoin.CallStopSharingPress, () => _CurrentRoom.RunRouteAction("codecOsd"));
@@ -1050,7 +1050,7 @@ namespace PepperDash.Essentials
///
///
///
- void CurrentRoom_CurrentSingleSourceChange(EssentialsRoomBase room, SourceListItem info, ChangeType type)
+ void CurrentRoom_CurrentSingleSourceChange(SourceListItem info, ChangeType type)
{
if (_CurrentRoom.VideoCodec.SharingContentIsOnFeedback.BoolValue && _CurrentRoom.CurrentSourceInfo != null)
TriList.StringInput[UIStringJoin.CallSharedSourceNameText].StringValue = _CurrentRoom.CurrentSourceInfo.PreferredName;
@@ -1363,8 +1363,7 @@ namespace PepperDash.Essentials
///
/// Handles source change
///
- void CurrentRoom_SourceInfoChange(EssentialsRoomBase room,
- SourceListItem info, ChangeType change)
+ void CurrentRoom_SourceInfoChange(SourceListItem info, ChangeType change)
{
if (change == ChangeType.WillChange)
DisconnectSource(info);
diff --git a/PepperDashEssentials/UIDrivers/SmartObjectRoomsList.cs b/PepperDashEssentials/UIDrivers/SmartObjectRoomsList.cs
index e942913a..e43abc09 100644
--- a/PepperDashEssentials/UIDrivers/SmartObjectRoomsList.cs
+++ b/PepperDashEssentials/UIDrivers/SmartObjectRoomsList.cs
@@ -64,11 +64,11 @@ namespace PepperDash.Essentials
parent.SetItemMainText(index, room.Name);
UpdateItem(room.CurrentSourceInfo);
// Watch for later changes
- room.CurrentSingleSourceChange += new SourceInfoChangeHandler(room_CurrentSourceInfoChange);
+ room.CurrentSourceChange += new SourceInfoChangeHandler(room_CurrentSourceInfoChange);
parent.SetItemButtonAction(index, buttonAction);
}
- void room_CurrentSourceInfoChange(EssentialsRoomBase room, SourceListItem info, ChangeType type)
+ void room_CurrentSourceInfoChange(SourceListItem info, ChangeType type)
{
UpdateItem(info);
}
diff --git a/PepperDashEssentials/UIDrivers/SourceChangeArgs.cs b/PepperDashEssentials/UIDrivers/SourceChangeArgs.cs
index 621ba257..c777c08e 100644
--- a/PepperDashEssentials/UIDrivers/SourceChangeArgs.cs
+++ b/PepperDashEssentials/UIDrivers/SourceChangeArgs.cs
@@ -9,8 +9,8 @@ using PepperDash.Essentials.Core;
namespace PepperDash.Essentials
{
- ///
- /// The handler type for a Room's SourceInfoChange
- ///
- public delegate void SourceInfoChangeHandler(EssentialsRoomBase room, SourceListItem info, ChangeType type);
+ /////
+ ///// The handler type for a Room's SourceInfoChange
+ /////
+ //public delegate void SourceInfoChangeHandler(EssentialsRoomBase room, SourceListItem info, ChangeType type);
}
\ No newline at end of file
diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Config/Essentials/EssentialsConfig.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Config/Essentials/EssentialsConfig.cs
index 69f162d0..cc3375e2 100644
--- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Config/Essentials/EssentialsConfig.cs
+++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Config/Essentials/EssentialsConfig.cs
@@ -21,8 +21,6 @@ namespace PepperDash.Essentials.Core.Config
public string TemplateUrl { get; set; }
- //public CotijaConfig Cotija { get; private set; }
-
[JsonProperty("systemUuid")]
public string SystemUuid
{
diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/PC/InRoomPc.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/PC/InRoomPc.cs
similarity index 97%
rename from essentials-framework/Essentials Devices Common/Essentials Devices Common/PC/InRoomPc.cs
rename to essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/PC/InRoomPc.cs
index f3c8ca0e..9a13106f 100644
--- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/PC/InRoomPc.cs
+++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/PC/InRoomPc.cs
@@ -7,7 +7,7 @@ using PepperDash.Essentials.Core;
using PepperDash.Essentials.Core.Routing;
using PepperDash.Core;
-namespace PepperDash.Essentials.Devices.Common
+namespace PepperDash.Essentials.Core.Devices
{
///
/// This DVD class should cover most IR, one-way DVD and Bluray fuctions
diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/PC/Laptop.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/PC/Laptop.cs
similarity index 97%
rename from essentials-framework/Essentials Devices Common/Essentials Devices Common/PC/Laptop.cs
rename to essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/PC/Laptop.cs
index 6ed08e58..a1a8b30b 100644
--- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/PC/Laptop.cs
+++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/PC/Laptop.cs
@@ -7,7 +7,7 @@ using PepperDash.Essentials.Core;
using PepperDash.Essentials.Core.Routing;
using PepperDash.Core;
-namespace PepperDash.Essentials.Devices.Common
+namespace PepperDash.Essentials.Core.Devices
{
///
/// This DVD class should cover most IR, one-way DVD and Bluray fuctions
diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/SourceListItem.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/SourceListItem.cs
index 86c940d3..efa2b2ad 100644
--- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/SourceListItem.cs
+++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/SourceListItem.cs
@@ -95,6 +95,9 @@ namespace PepperDash.Essentials.Core
[JsonProperty("disableRoutedSharing")]
public bool DisableRoutedSharing { get; set; }
+ [JsonProperty("destinations")]
+ public List Destinations { get; set; }
+
public SourceListItem()
{
Icon = "Blank";
@@ -112,4 +115,16 @@ namespace PepperDash.Essentials.Core
[JsonProperty("type")]
public eRoutingSignalType Type { get; set; }
}
+
+ ///
+ /// Defines the valid destination types for SourceListItems in a room
+ ///
+ public enum eSourceListItemDestinationTypes
+ {
+ defaultDisplay,
+ leftDisplay,
+ rightDisplay,
+ programAudio,
+ codecContent
+ }
}
\ No newline at end of file
diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Display/BasicIrDisplay.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Display/BasicIrDisplay.cs
index b724366f..3ffd0bf4 100644
--- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Display/BasicIrDisplay.cs
+++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Display/BasicIrDisplay.cs
@@ -12,7 +12,7 @@ using PepperDash.Essentials.Core.Routing;
namespace PepperDash.Essentials.Core
{
- public class BasicIrDisplay : DisplayBase, IBasicVolumeControls, IPower, IWarmingCooling, IRoutingSinkWithSwitching
+ public class BasicIrDisplay : DisplayBase, IBasicVolumeControls, IPower, IWarmingCooling
{
public IrOutputPortController IrPort { get; private set; }
public ushort IrPulseTime { get; set; }
diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Display/DisplayBase.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Display/DisplayBase.cs
index 9ef0ee86..98659ab2 100644
--- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Display/DisplayBase.cs
+++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Display/DisplayBase.cs
@@ -18,6 +18,32 @@ namespace PepperDash.Essentials.Core
///
public abstract class DisplayBase : Device, IHasFeedback, IRoutingSinkWithSwitching, IPower, IWarmingCooling, IUsageTracking
{
+ public event SourceInfoChangeHandler CurrentSourceChange;
+
+ public string CurrentSourceInfoKey { get; set; }
+ public SourceListItem CurrentSourceInfo
+ {
+ get
+ {
+ return _CurrentSourceInfo;
+ }
+ set
+ {
+ if (value == _CurrentSourceInfo) return;
+
+ var handler = CurrentSourceChange;
+
+ if (handler != null)
+ handler(_CurrentSourceInfo, ChangeType.WillChange);
+
+ _CurrentSourceInfo = value;
+
+ if (handler != null)
+ handler(_CurrentSourceInfo, ChangeType.DidChange);
+ }
+ }
+ SourceListItem _CurrentSourceInfo;
+
public BoolFeedback PowerIsOnFeedback { get; protected set; }
public BoolFeedback IsCoolingDownFeedback { get; protected set; }
public BoolFeedback IsWarmingUpFeedback { get; private set; }
diff --git a/PepperDashEssentials/FOR REFERENCE UI/OTHER/Fusion/EssentialsHuddleSpaceFusionSystemControllerBase.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/EssentialsHuddleSpaceFusionSystemControllerBase.cs
similarity index 98%
rename from PepperDashEssentials/FOR REFERENCE UI/OTHER/Fusion/EssentialsHuddleSpaceFusionSystemControllerBase.cs
rename to essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/EssentialsHuddleSpaceFusionSystemControllerBase.cs
index e7623975..ef5634aa 100644
--- a/PepperDashEssentials/FOR REFERENCE UI/OTHER/Fusion/EssentialsHuddleSpaceFusionSystemControllerBase.cs
+++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/EssentialsHuddleSpaceFusionSystemControllerBase.cs
@@ -18,12 +18,10 @@ using PepperDash.Core;
using PepperDash.Essentials;
using PepperDash.Essentials.Core;
using PepperDash.Essentials.Core.Config;
-using PepperDash.Essentials.Devices.Common;
-using PepperDash.Essentials.Devices.Common.Occupancy;
-namespace PepperDash.Essentials.Fusion
+namespace PepperDash.Essentials.Core.Fusion
{
public class EssentialsHuddleSpaceFusionSystemControllerBase : Device, IOccupancyStatusProvider
{
@@ -330,11 +328,11 @@ namespace PepperDash.Essentials.Fusion
// Moved to
CurrentRoomSourceNameSig = FusionRoom.CreateOffsetStringSig(84, "Display 1 - Current Source", eSigIoMask.InputSigOnly);
// Don't think we need to get current status of this as nothing should be alive yet.
- (Room as EssentialsHuddleSpaceRoom).CurrentSingleSourceChange += new SourceInfoChangeHandler(Room_CurrentSourceInfoChange);
+ (Room as IHasCurrentSourceInfoChange).CurrentSourceChange += new SourceInfoChangeHandler(Room_CurrentSourceInfoChange);
- FusionRoom.SystemPowerOn.OutputSig.SetSigFalseAction((Room as EssentialsHuddleSpaceRoom).PowerOnToDefaultOrLastSource);
- FusionRoom.SystemPowerOff.OutputSig.SetSigFalseAction(() => (Room as EssentialsHuddleSpaceRoom).RunRouteAction("roomOff"));
+ FusionRoom.SystemPowerOn.OutputSig.SetSigFalseAction((Room as EssentialsRoomBase).PowerOnToDefaultOrLastSource);
+ FusionRoom.SystemPowerOff.OutputSig.SetSigFalseAction(() => (Room as IRunRouteAction).RunRouteAction("roomOff"));
// NO!! room.RoomIsOn.LinkComplementInputSig(FusionRoom.SystemPowerOff.InputSig);
FusionRoom.ErrorMessage.InputSig.StringValue =
"3: 7 Errors: This is a really long error message;This is a really long error message;This is a really long error message;This is a really long error message;This is a really long error message;This is a really long error message;This is a really long error message;";
@@ -990,7 +988,7 @@ namespace PepperDash.Essentials.Fusion
protected virtual void SetUpSources()
{
// Sources
- var dict = ConfigReader.ConfigObject.GetSourceListForKey((Room as EssentialsHuddleSpaceRoom).SourceListKey);
+ var dict = ConfigReader.ConfigObject.GetSourceListForKey((Room as EssentialsRoomBase).SourceListKey);
if (dict != null)
{
// NEW PROCESS:
@@ -1015,7 +1013,7 @@ namespace PepperDash.Essentials.Fusion
break;
}
- var laptops = dict.Where(d => d.Value.SourceDevice is Laptop);
+ var laptops = dict.Where(d => d.Value.SourceDevice is Devices.Laptop);
i = 1;
foreach (var kvp in laptops)
{
@@ -1041,7 +1039,7 @@ namespace PepperDash.Essentials.Fusion
else
{
Debug.Console(1, this, "WARNING: Config source list '{0}' not found for room '{1}'",
- (Room as EssentialsHuddleSpaceRoom).SourceListKey, Room.Key);
+ (Room as EssentialsRoomBase).SourceListKey, Room.Key);
}
}
@@ -1088,7 +1086,7 @@ namespace PepperDash.Essentials.Fusion
SourceToFeedbackSigs.Add(pSrc, sigD.InputSig);
// And respond to selection in Fusion
- sigD.OutputSig.SetSigFalseAction(() => (Room as EssentialsHuddleSpaceRoom).RunRouteAction(routeKey));
+ sigD.OutputSig.SetSigFalseAction(() => (Room as IRunRouteAction).RunRouteAction(routeKey));
}
catch (Exception)
{
@@ -1125,7 +1123,7 @@ namespace PepperDash.Essentials.Fusion
//uint attrNum = Convert.ToUInt32(keyNum);
// Check for UI devices
- var uiDev = dev as EssentialsTouchpanelController;
+ var uiDev = dev as IHasBasicTriListWithSmartObject;
if (uiDev != null)
{
if (uiDev.Panel is Crestron.SimplSharpPro.UI.XpanelForSmartGraphics)
@@ -1206,7 +1204,7 @@ namespace PepperDash.Essentials.Fusion
display.UsageTracker.DeviceUsageEnded += new EventHandler(UsageTracker_DeviceUsageEnded);
}
- var defaultDisplay = (Room as EssentialsHuddleSpaceRoom).DefaultDisplay as DisplayBase;
+ var defaultDisplay = (Room as IHasDefaultDisplay).DefaultDisplay as DisplayBase;
if (defaultDisplay == null)
{
Debug.Console(1, this, "Cannot link null display to Fusion because default display is null");
@@ -1271,7 +1269,7 @@ namespace PepperDash.Essentials.Fusion
string displayName = string.Format("Display {0} - ", displayIndex);
- if (display == (Room as EssentialsHuddleSpaceRoom).DefaultDisplay)
+ if (display == (Room as IHasDefaultDisplay).DefaultDisplay)
{
// Display volume
var defaultDisplayVolume = FusionRoom.CreateOffsetUshortSig(50, "Volume - Fader01", eSigIoMask.InputOutputSig);
@@ -1290,7 +1288,7 @@ namespace PepperDash.Essentials.Fusion
// Current Source
var defaultDisplaySourceNone = FusionRoom.CreateOffsetBoolSig((uint)joinOffset + 8, displayName + "Source None", eSigIoMask.InputOutputSig);
- defaultDisplaySourceNone.OutputSig.UserObject = new Action(b => { if (!b) (Room as EssentialsHuddleSpaceRoom).RunRouteAction("roomOff"); }); ;
+ defaultDisplaySourceNone.OutputSig.UserObject = new Action(b => { if (!b) (Room as IRunRouteAction).RunRouteAction("roomOff"); }); ;
}
}
@@ -1389,7 +1387,7 @@ namespace PepperDash.Essentials.Fusion
///
/// Event handler for when room source changes
///
- protected void Room_CurrentSourceInfoChange(EssentialsRoomBase room, SourceListItem info, ChangeType type)
+ protected void Room_CurrentSourceInfoChange(SourceListItem info, ChangeType type)
{
// Handle null. Nothing to do when switching from or to null
if (info == null || info.SourceDevice == null)
@@ -1405,7 +1403,7 @@ namespace PepperDash.Essentials.Fusion
{
if (SourceToFeedbackSigs.ContainsKey(dev))
SourceToFeedbackSigs[dev].BoolValue = true;
- var name = (room == null ? "" : room.Name);
+ //var name = (room == null ? "" : room.Name);
CurrentRoomSourceNameSig.InputSig.StringValue = info.SourceDevice.Name;
}
}
diff --git a/PepperDashEssentials/FOR REFERENCE UI/OTHER/Fusion/FusionCustomPropertiesBridge.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/FusionCustomPropertiesBridge.cs
similarity index 98%
rename from PepperDashEssentials/FOR REFERENCE UI/OTHER/Fusion/FusionCustomPropertiesBridge.cs
rename to essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/FusionCustomPropertiesBridge.cs
index b5704b40..01b230f9 100644
--- a/PepperDashEssentials/FOR REFERENCE UI/OTHER/Fusion/FusionCustomPropertiesBridge.cs
+++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/FusionCustomPropertiesBridge.cs
@@ -10,9 +10,8 @@ using PepperDash.Core;
using PepperDash.Essentials.Core;
using PepperDash.Essentials.Core.Config;
using PepperDash.Essentials.Core.Devices;
-using PepperDash.Essentials.Room.Behaviours;
-namespace PepperDash.Essentials.Fusion
+namespace PepperDash.Essentials.Core.Fusion
{
///
/// Handles mapping Fusion Custom Property values to system properties
diff --git a/PepperDashEssentials/FOR REFERENCE UI/OTHER/Fusion/FusionEventHandlers.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/FusionEventHandlers.cs
similarity index 88%
rename from PepperDashEssentials/FOR REFERENCE UI/OTHER/Fusion/FusionEventHandlers.cs
rename to essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/FusionEventHandlers.cs
index 4987c047..26647a96 100644
--- a/PepperDashEssentials/FOR REFERENCE UI/OTHER/Fusion/FusionEventHandlers.cs
+++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/FusionEventHandlers.cs
@@ -4,7 +4,7 @@ using System.Linq;
using System.Text;
using Crestron.SimplSharp;
-namespace PepperDash.Essentials.Fusion
+namespace PepperDash.Essentials.Core.Fusion
{
public class ScheduleChangeEventArgs : EventArgs
{
diff --git a/PepperDashEssentials/FOR REFERENCE UI/OTHER/Fusion/FusionProcessorQueries.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/FusionProcessorQueries.cs
similarity index 97%
rename from PepperDashEssentials/FOR REFERENCE UI/OTHER/Fusion/FusionProcessorQueries.cs
rename to essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/FusionProcessorQueries.cs
index 8757be62..62109d97 100644
--- a/PepperDashEssentials/FOR REFERENCE UI/OTHER/Fusion/FusionProcessorQueries.cs
+++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/FusionProcessorQueries.cs
@@ -4,9 +4,8 @@ using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using PepperDash.Core;
-using PepperDash.Essentials.Core;
-namespace PepperDash.Essentials.Fusion
+namespace PepperDash.Essentials.Core.Fusion
{
///
/// When created, runs progcomments on every slot and stores the program names in a list
diff --git a/PepperDashEssentials/FOR REFERENCE UI/OTHER/Fusion/FusionRviDataClasses.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/FusionRviDataClasses.cs
similarity index 99%
rename from PepperDashEssentials/FOR REFERENCE UI/OTHER/Fusion/FusionRviDataClasses.cs
rename to essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/FusionRviDataClasses.cs
index 3752531a..99fc0abb 100644
--- a/PepperDashEssentials/FOR REFERENCE UI/OTHER/Fusion/FusionRviDataClasses.cs
+++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/FusionRviDataClasses.cs
@@ -7,7 +7,7 @@ using Crestron.SimplSharpPro.Fusion;
using PepperDash.Core;
-namespace PepperDash.Essentials.Fusion
+namespace PepperDash.Essentials.Core.Fusion
{
// Helper Classes for GUIDs
diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/MOVED FusionSystemController.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/MOVED FusionSystemController.cs
deleted file mode 100644
index 8e7e70c6..00000000
--- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/MOVED FusionSystemController.cs
+++ /dev/null
@@ -1,377 +0,0 @@
-//using System;
-//using System.Collections.Generic;
-//using System.Linq;
-//using System.Text;
-
-//using Crestron.SimplSharp;
-//using Crestron.SimplSharpPro;
-//using Crestron.SimplSharpPro.DeviceSupport;
-
-//using Crestron.SimplSharpPro.Fusion;
-//using PepperDash.Essentials.Core;
-
-//using PepperDash.Core;
-
-
-//namespace PepperDash.Essentials.Core.Fusion
-//{
-// public class EssentialsHuddleSpaceFusionSystemController : Device
-// {
-// FusionRoom FusionRoom;
-// Room Room;
-// Dictionary SourceToFeedbackSigs = new Dictionary();
-
-// StatusMonitorCollection ErrorMessageRollUp;
-
-// public EssentialsHuddleSpaceFusionSystemController(HuddleSpaceRoom room, uint ipId)
-// : base(room.Key + "-fusion")
-// {
-// Room = room;
-
-// FusionRoom = new FusionRoom(ipId, Global.ControlSystem, room.Name, "awesomeGuid-" + room.Key);
-// FusionRoom.Register();
-
-// FusionRoom.FusionStateChange += new FusionStateEventHandler(FusionRoom_FusionStateChange);
-
-// // Room to fusion room
-// room.RoomIsOn.LinkInputSig(FusionRoom.SystemPowerOn.InputSig);
-// var srcName = FusionRoom.CreateOffsetStringSig(50, "Source - Name", eSigIoMask.InputSigOnly);
-// room.CurrentSourceName.LinkInputSig(srcName.InputSig);
-
-// FusionRoom.SystemPowerOn.OutputSig.UserObject = new Action(b => { if (b) room.RoomOn(null); });
-// FusionRoom.SystemPowerOff.OutputSig.UserObject = new Action(b => { if (b) room.RoomOff(); });
-// // NO!! room.RoomIsOn.LinkComplementInputSig(FusionRoom.SystemPowerOff.InputSig);
-// FusionRoom.ErrorMessage.InputSig.StringValue = "3: 7 Errors: This is a really long error message;This is a really long error message;This is a really long error message;This is a really long error message;This is a really long error message;This is a really long error message;This is a really long error message;";
-
-// // Sources
-// foreach (var src in room.Sources)
-// {
-// var srcNum = src.Key;
-// var pSrc = src.Value as IPresentationSource;
-// var keyNum = ExtractNumberFromKey(pSrc.Key);
-// if (keyNum == -1)
-// {
-// Debug.Console(1, this, "WARNING: Cannot link source '{0}' to numbered Fusion attributes", pSrc.Key);
-// continue;
-// }
-// string attrName = null;
-// uint attrNum = Convert.ToUInt32(keyNum);
-// switch (pSrc.Type)
-// {
-// case PresentationSourceType.None:
-// break;
-// case PresentationSourceType.SetTopBox:
-// attrName = "Source - TV " + keyNum;
-// attrNum += 115; // TV starts at 116
-// break;
-// case PresentationSourceType.Dvd:
-// attrName = "Source - DVD " + keyNum;
-// attrNum += 120; // DVD starts at 121
-// break;
-// case PresentationSourceType.PC:
-// attrName = "Source - PC " + keyNum;
-// attrNum += 110; // PC starts at 111
-// break;
-// case PresentationSourceType.Laptop:
-// attrName = "Source - Laptop " + keyNum;
-// attrNum += 100; // Laptops start at 101
-// break;
-// case PresentationSourceType.VCR:
-// attrName = "Source - VCR " + keyNum;
-// attrNum += 125; // VCRs start at 126
-// break;
-// }
-// if (attrName == null)
-// {
-// Debug.Console(1, this, "Source type {0} does not have corresponsing Fusion attribute type, skipping", pSrc.Type);
-// continue;
-// }
-// Debug.Console(2, this, "Creating attribute '{0}' with join {1} for source {2}", attrName, attrNum, pSrc.Key);
-// var sigD = FusionRoom.CreateOffsetBoolSig(attrNum, attrName, eSigIoMask.InputOutputSig);
-// // Need feedback when this source is selected
-// // Event handler, added below, will compare source changes with this sig dict
-// SourceToFeedbackSigs.Add(pSrc, sigD.InputSig);
-
-// // And respond to selection in Fusion
-// sigD.OutputSig.UserObject = new Action(b => { if(b) room.SelectSource(pSrc); });
-// }
-
-// // Attach to all room's devices with monitors.
-// //foreach (var dev in DeviceManager.Devices)
-// foreach (var dev in DeviceManager.GetDevices())
-// {
-// if (!(dev is ICommunicationMonitor))
-// continue;
-
-// var keyNum = ExtractNumberFromKey(dev.Key);
-// if (keyNum == -1)
-// {
-// Debug.Console(1, this, "WARNING: Cannot link device '{0}' to numbered Fusion monitoring attributes", dev.Key);
-// continue;
-// }
-// string attrName = null;
-// uint attrNum = Convert.ToUInt32(keyNum);
-
-// //if (dev is SmartGraphicsTouchpanelControllerBase)
-// //{
-// // if (attrNum > 10)
-// // continue;
-// // attrName = "Device Ok - Touch Panel " + attrNum;
-// // attrNum += 200;
-// //}
-// //// add xpanel here
-
-// //else
-// if (dev is DisplayBase)
-// {
-// if (attrNum > 10)
-// continue;
-// attrName = "Device Ok - Display " + attrNum;
-// attrNum += 240;
-// }
-// //else if (dev is DvdDeviceBase)
-// //{
-// // if (attrNum > 5)
-// // continue;
-// // attrName = "Device Ok - DVD " + attrNum;
-// // attrNum += 260;
-// //}
-// // add set top box
-
-// // add Cresnet roll-up
-
-// // add DM-devices roll-up
-
-// if (attrName != null)
-// {
-// // Link comm status to sig and update
-// var sigD = FusionRoom.CreateOffsetBoolSig(attrNum, attrName, eSigIoMask.InputSigOnly);
-// var smd = dev as ICommunicationMonitor;
-// sigD.InputSig.BoolValue = smd.CommunicationMonitor.Status == MonitorStatus.IsOk;
-// smd.CommunicationMonitor.StatusChange += (o, a) => { sigD.InputSig.BoolValue = a.Status == MonitorStatus.IsOk; };
-// Debug.Console(0, this, "Linking '{0}' communication monitor to Fusion '{1}'", dev.Key, attrName);
-// }
-// }
-
-// // Don't think we need to get current status of this as nothing should be alive yet.
-// room.PresentationSourceChange += Room_PresentationSourceChange;
-
-// // these get used in multiple places
-// var display = room.Display;
-// var dispPowerOnAction = new Action(b => { if (!b) display.PowerOn(); });
-// var dispPowerOffAction = new Action(b => { if (!b) display.PowerOff(); });
-
-// // Display to fusion room sigs
-// FusionRoom.DisplayPowerOn.OutputSig.UserObject = dispPowerOnAction;
-// FusionRoom.DisplayPowerOff.OutputSig.UserObject = dispPowerOffAction;
-// display.PowerIsOnFeedback.LinkInputSig(FusionRoom.DisplayPowerOn.InputSig);
-// if (display is IDisplayUsage)
-// (display as IDisplayUsage).LampHours.LinkInputSig(FusionRoom.DisplayUsage.InputSig);
-
-// // Roll up ALL device errors
-// ErrorMessageRollUp = new StatusMonitorCollection(this);
-// foreach (var dev in DeviceManager.GetDevices())
-// {
-// var md = dev as ICommunicationMonitor;
-// if (md != null)
-// {
-// ErrorMessageRollUp.AddMonitor(md.CommunicationMonitor);
-// Debug.Console(2, this, "Adding '{0}' to room's overall error monitor", md.CommunicationMonitor.Parent.Key);
-// }
-// }
-// ErrorMessageRollUp.Start();
-// FusionRoom.ErrorMessage.InputSig.StringValue = ErrorMessageRollUp.Message;
-// ErrorMessageRollUp.StatusChange += (o, a) => {
-// FusionRoom.ErrorMessage.InputSig.StringValue = ErrorMessageRollUp.Message; };
-
-
-// // static assets --------------- testing
-
-// // test assets --- THESE ARE BOTH WIRED TO AssetUsage somewhere internally.
-// var ta1 = FusionRoom.CreateStaticAsset(1, "Test asset 1", "Awesome Asset", "Awesome123");
-// ta1.AssetError.InputSig.StringValue = "This should be error";
-
-
-// var ta2 = FusionRoom.CreateStaticAsset(2, "Test asset 2", "Awesome Asset", "Awesome1232");
-// ta2.AssetUsage.InputSig.StringValue = "This should be usage";
-
-
-// // Make a display asset
-// var dispAsset = FusionRoom.CreateStaticAsset(3, display.Name, "Display", "awesomeDisplayId" + room.Key);
-// dispAsset.PowerOn.OutputSig.UserObject = dispPowerOnAction;
-// dispAsset.PowerOff.OutputSig.UserObject = dispPowerOffAction;
-// display.PowerIsOnFeedback.LinkInputSig(dispAsset.PowerOn.InputSig);
-// // NO!! display.PowerIsOn.LinkComplementInputSig(dispAsset.PowerOff.InputSig);
-// // Use extension methods
-// dispAsset.TrySetMakeModel(display);
-// dispAsset.TryLinkAssetErrorToCommunication(display);
-
-
-// // Make it so!
-// FusionRVI.GenerateFileForAllFusionDevices();
-// }
-
-// ///
-// /// Helper to get the number from the end of a device's key string
-// ///
-// /// -1 if no number matched
-// int ExtractNumberFromKey(string key)
-// {
-// var capture = System.Text.RegularExpressions.Regex.Match(key, @"\D+(\d+)");
-// if (!capture.Success)
-// return -1;
-// else return Convert.ToInt32(capture.Groups[1].Value);
-// }
-
-// void Room_PresentationSourceChange(object sender, EssentialsRoomSourceChangeEventArgs e)
-// {
-// if (e.OldSource != null)
-// {
-// if (SourceToFeedbackSigs.ContainsKey(e.OldSource))
-// SourceToFeedbackSigs[e.OldSource].BoolValue = false;
-// }
-// if (e.NewSource != null)
-// {
-// if (SourceToFeedbackSigs.ContainsKey(e.NewSource))
-// SourceToFeedbackSigs[e.NewSource].BoolValue = true;
-// }
-// }
-
-// void FusionRoom_FusionStateChange(FusionBase device, FusionStateEventArgs args)
-// {
-
-// // The sig/UO method: Need separate handlers for fixed and user sigs, all flavors,
-// // even though they all contain sigs.
-
-// var sigData = (args.UserConfiguredSigDetail as BooleanSigDataFixedName);
-// if (sigData != null)
-// {
-// var outSig = sigData.OutputSig;
-// if (outSig.UserObject is Action)
-// (outSig.UserObject as Action).Invoke(outSig.BoolValue);
-// else if (outSig.UserObject is Action)
-// (outSig.UserObject as Action).Invoke(outSig.UShortValue);
-// else if (outSig.UserObject is Action)
-// (outSig.UserObject as Action).Invoke(outSig.StringValue);
-// return;
-// }
-
-// var attrData = (args.UserConfiguredSigDetail as BooleanSigData);
-// if (attrData != null)
-// {
-// var outSig = attrData.OutputSig;
-// if (outSig.UserObject is Action)
-// (outSig.UserObject as Action).Invoke(outSig.BoolValue);
-// else if (outSig.UserObject is Action)
-// (outSig.UserObject as Action).Invoke(outSig.UShortValue);
-// else if (outSig.UserObject is Action)
-// (outSig.UserObject as Action).Invoke(outSig.StringValue);
-// return;
-// }
-
-// }
-// }
-
-
-// public static class FusionRoomExtensions
-// {
-// ///
-// /// Creates and returns a fusion attribute. The join number will match the established Simpl
-// /// standard of 50+, and will generate a 50+ join in the RVI. It calls
-// /// FusionRoom.AddSig with join number - 49
-// ///
-// /// The new attribute
-// public static BooleanSigData CreateOffsetBoolSig(this FusionRoom fr, uint number, string name, eSigIoMask mask)
-// {
-// if (number < 50) throw new ArgumentOutOfRangeException("number", "Cannot be less than 50");
-// number -= 49;
-// fr.AddSig(eSigType.Bool, number, name, mask);
-// return fr.UserDefinedBooleanSigDetails[number];
-// }
-
-// ///
-// /// Creates and returns a fusion attribute. The join number will match the established Simpl
-// /// standard of 50+, and will generate a 50+ join in the RVI. It calls
-// /// FusionRoom.AddSig with join number - 49
-// ///
-// /// The new attribute
-// public static UShortSigData CreateOffsetUshortSig(this FusionRoom fr, uint number, string name, eSigIoMask mask)
-// {
-// if (number < 50) throw new ArgumentOutOfRangeException("number", "Cannot be less than 50");
-// number -= 49;
-// fr.AddSig(eSigType.UShort, number, name, mask);
-// return fr.UserDefinedUShortSigDetails[number];
-// }
-
-// ///
-// /// Creates and returns a fusion attribute. The join number will match the established Simpl
-// /// standard of 50+, and will generate a 50+ join in the RVI. It calls
-// /// FusionRoom.AddSig with join number - 49
-// ///
-// /// The new attribute
-// public static StringSigData CreateOffsetStringSig(this FusionRoom fr, uint number, string name, eSigIoMask mask)
-// {
-// if (number < 50) throw new ArgumentOutOfRangeException("number", "Cannot be less than 50");
-// number -= 49;
-// fr.AddSig(eSigType.String, number, name, mask);
-// return fr.UserDefinedStringSigDetails[number];
-// }
-
-// ///
-// /// Creates and returns a static asset
-// ///
-// /// the new asset
-// public static FusionStaticAsset CreateStaticAsset(this FusionRoom fr, uint number, string name, string type, string instanceId)
-// {
-// fr.AddAsset(eAssetType.StaticAsset, number, name, type, instanceId);
-// return fr.UserConfigurableAssetDetails[number].Asset as FusionStaticAsset;
-// }
-// }
-
-// //************************************************************************************************
-// ///
-// /// Extensions to enhance Fusion room, asset and signal creation.
-// ///
-// public static class FusionStaticAssetExtensions
-// {
-// ///
-// /// Tries to set a Fusion asset with the make and model of a device.
-// /// If the provided Device is IMakeModel, will set the corresponding parameters on the fusion static asset.
-// /// Otherwise, does nothing.
-// ///
-// public static void TrySetMakeModel(this FusionStaticAsset asset, Device device)
-// {
-// var mm = device as IMakeModel;
-// if (mm != null)
-// {
-// asset.ParamMake.Value = mm.DeviceMake;
-// asset.ParamModel.Value = mm.DeviceModel;
-// }
-// }
-
-// ///
-// /// Tries to attach the AssetError input on a Fusion asset to a Device's
-// /// CommunicationMonitor.StatusChange event. Does nothing if the device is not
-// /// IStatusMonitor
-// ///
-// ///
-// ///
-// public static void TryLinkAssetErrorToCommunication(this FusionStaticAsset asset, Device device)
-// {
-// if (device is ICommunicationMonitor)
-// {
-// var monitor = (device as ICommunicationMonitor).CommunicationMonitor;
-// monitor.StatusChange += (o, a) =>
-// {
-// // Link connected and error inputs on asset
-// asset.Connected.InputSig.BoolValue = a.Status == MonitorStatus.IsOk;
-// asset.AssetError.InputSig.StringValue = a.Status.ToString();
-// };
-// // set current value
-// asset.Connected.InputSig.BoolValue = monitor.Status == MonitorStatus.IsOk;
-// asset.AssetError.InputSig.StringValue = monitor.Status.ToString();
-// }
-// }
-// }
-
-//}
\ No newline at end of file
diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/MIcrophone/MicrophonePrivacyController.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Microphone Privacy/MicrophonePrivacyController.cs
similarity index 99%
rename from essentials-framework/Essentials Devices Common/Essentials Devices Common/MIcrophone/MicrophonePrivacyController.cs
rename to essentials-framework/Essentials Core/PepperDashEssentialsBase/Microphone Privacy/MicrophonePrivacyController.cs
index d8a7f543..e238c40e 100644
--- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/MIcrophone/MicrophonePrivacyController.cs
+++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Microphone Privacy/MicrophonePrivacyController.cs
@@ -9,7 +9,7 @@ using PepperDash.Essentials.Core;
using PepperDash.Essentials.Core.CrestronIO;
-namespace PepperDash.Essentials.Devices.Common.Microphones
+namespace PepperDash.Essentials.Core.Privacy
{
///
/// Used for applications where one or more microphones with momentary contact closure outputs are used to
diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/MIcrophone/MicrophonePrivacyControllerConfig.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Microphone Privacy/MicrophonePrivacyControllerConfig.cs
similarity index 89%
rename from essentials-framework/Essentials Devices Common/Essentials Devices Common/MIcrophone/MicrophonePrivacyControllerConfig.cs
rename to essentials-framework/Essentials Core/PepperDashEssentialsBase/Microphone Privacy/MicrophonePrivacyControllerConfig.cs
index 238fc7cf..1c172090 100644
--- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/MIcrophone/MicrophonePrivacyControllerConfig.cs
+++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Microphone Privacy/MicrophonePrivacyControllerConfig.cs
@@ -6,7 +6,7 @@ using Crestron.SimplSharp;
using PepperDash.Essentials.Core.CrestronIO;
-namespace PepperDash.Essentials.Devices.Common.Microphones
+namespace PepperDash.Essentials.Core.Privacy
{
public class MicrophonePrivacyControllerConfig
{
diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.csproj b/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.csproj
index 9eca4e25..7c3cd563 100644
--- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.csproj
+++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.csproj
@@ -123,6 +123,8 @@
+
+
@@ -132,12 +134,19 @@
+
+
+
+
+
+
+
@@ -173,6 +182,10 @@
+
+
+
+
@@ -191,7 +204,6 @@
-
@@ -217,6 +229,7 @@
+
@@ -238,7 +251,6 @@
-
diff --git a/PepperDashEssentials/Room/Behaviours/RoomOnToDefaultSourceWhenOccupied.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Behaviours/RoomOnToDefaultSourceWhenOccupied.cs
similarity index 96%
rename from PepperDashEssentials/Room/Behaviours/RoomOnToDefaultSourceWhenOccupied.cs
rename to essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Behaviours/RoomOnToDefaultSourceWhenOccupied.cs
index b12fd2e5..fcb04420 100644
--- a/PepperDashEssentials/Room/Behaviours/RoomOnToDefaultSourceWhenOccupied.cs
+++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Behaviours/RoomOnToDefaultSourceWhenOccupied.cs
@@ -1,526 +1,525 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-
-using Crestron.SimplSharp;
-using Crestron.SimplSharp.Scheduler;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
-
-using PepperDash.Core;
-using PepperDash.Essentials.Core;
-using PepperDash.Essentials.Core.Config;
-using PepperDash.Essentials.Core.Devices;
-using PepperDash.Essentials.Devices.Common.Occupancy;
-
-namespace PepperDash.Essentials.Room.Behaviours
-{
- ///
- /// A device that when linked to a room can power the room on when enabled during scheduled hours.
- ///
- public class RoomOnToDefaultSourceWhenOccupied : ReconfigurableDevice
- {
- RoomOnToDefaultSourceWhenOccupiedConfig PropertiesConfig;
-
- public bool FeatureEnabled { get; private set; }
-
- public DateTime FeatureEnabledTime { get; private set; }
-
- ScheduledEvent FeatureEnableEvent;
-
- const string FeatureEnableEventName = "EnableRoomOnToDefaultSourceWhenOccupied";
-
- public DateTime FeatureDisabledTime { get; private set; }
-
- ScheduledEvent FeatureDisableEvent;
-
- const string FeatureDisableEventName = "DisableRoomOnToDefaultSourceWhenOccupied";
-
- ScheduledEventGroup FeatureEventGroup;
-
- public EssentialsRoomBase Room { get; private set; }
-
- private Fusion.EssentialsHuddleSpaceFusionSystemControllerBase FusionRoom;
-
- public RoomOnToDefaultSourceWhenOccupied(DeviceConfig config) :
- base (config)
- {
- PropertiesConfig = JsonConvert.DeserializeObject(config.Properties.ToString());
-
- FeatureEventGroup = new ScheduledEventGroup(this.Key);
-
- FeatureEventGroup.RetrieveAllEvents();
-
- // Add to the global class for tracking
- Scheduler.AddEventGroup(FeatureEventGroup);
-
- AddPostActivationAction(() =>
- {
- // Subscribe to room event to know when RoomOccupancy is set and ready to be subscribed to
- if (Room != null)
- Room.RoomOccupancyIsSet += new EventHandler(RoomOccupancyIsSet);
-
- else
- Debug.Console(1, this, "Room has no RoomOccupancy object set");
-
- var fusionRoomKey = PropertiesConfig.RoomKey + "-fusion";
-
- FusionRoom = DeviceManager.GetDeviceForKey(fusionRoomKey) as Fusion.EssentialsHuddleSpaceFusionSystemControllerBase;
-
- if (FusionRoom == null)
- Debug.Console(1, this, "Unable to get Fusion Room from Device Manager with key: {0}", fusionRoomKey);
- });
- }
-
- public override bool CustomActivate()
- {
- SetUpDevice();
-
- return base.CustomActivate();
- }
-
- ///
- /// Sets up device based on config values
- ///
- void SetUpDevice()
- {
- Room = DeviceManager.GetDeviceForKey(PropertiesConfig.RoomKey) as EssentialsRoomBase;
-
- if (Room != null)
- {
- try
- {
- FeatureEnabledTime = DateTime.Parse(PropertiesConfig.OccupancyStartTime);
-
- if (FeatureEnabledTime != null)
- {
- Debug.Console(1, this, "Enabled Time: {0}", FeatureEnabledTime.ToString());
- }
- else
- Debug.Console(1, this, "Unable to parse {0} to DateTime", PropertiesConfig.OccupancyStartTime);
- }
- catch (Exception e)
- {
- Debug.Console(1, this, "Unable to parse OccupancyStartTime property: {0} \n Error: {1}", PropertiesConfig.OccupancyStartTime, e);
- }
-
- try
- {
- FeatureDisabledTime = DateTime.Parse(PropertiesConfig.OccupancyEndTime);
-
- if (FeatureDisabledTime != null)
- {
- Debug.Console(1, this, "Disabled Time: {0}", FeatureDisabledTime.ToString());
- }
- else
- Debug.Console(1, this, "Unable to parse {0} to DateTime", PropertiesConfig.OccupancyEndTime);
- }
- catch (Exception e)
- {
- Debug.Console(1, this, "Unable to parse a DateTime config value \n Error: {1}", e);
- }
-
- if (!PropertiesConfig.EnableRoomOnWhenOccupied)
- FeatureEventGroup.ClearAllEvents();
- else
- {
- AddEnableEventToGroup();
-
- AddDisableEventToGroup();
-
- FeatureEventGroup.UserGroupCallBack += new ScheduledEventGroup.UserEventGroupCallBack(FeatureEventGroup_UserGroupCallBack);
-
- FeatureEventGroup.EnableAllEvents();
- }
-
- FeatureEnabled = CheckIfFeatureShouldBeEnabled();
- }
- else
- Debug.Console(1, this, "Unable to get room from Device Manager with key: {0}", PropertiesConfig.RoomKey);
- }
-
-
- protected override void CustomSetConfig(DeviceConfig config)
- {
- var newPropertiesConfig = JsonConvert.DeserializeObject(config.Properties.ToString());
-
- if(newPropertiesConfig != null)
- PropertiesConfig = newPropertiesConfig;
-
- ConfigWriter.UpdateDeviceConfig(config);
-
- SetUpDevice();
- }
-
- ///
- /// Subscribe to feedback from RoomIsOccupiedFeedback on Room
- ///
- ///
- ///
- void RoomOccupancyIsSet(object sender, EventArgs e)
- {
- if (Room.RoomOccupancy != null)
- {
- Room.RoomOccupancy.RoomIsOccupiedFeedback.OutputChange -= RoomIsOccupiedFeedback_OutputChange;
- Room.RoomOccupancy.RoomIsOccupiedFeedback.OutputChange += new EventHandler(RoomIsOccupiedFeedback_OutputChange);
- Debug.Console(1, this, "Subscribed to RoomOccupancy status from: '{0}'", Room.Key);
- }
- }
-
- void FeatureEventGroup_UserGroupCallBack(ScheduledEvent SchEvent, ScheduledEventCommon.eCallbackReason type)
- {
- if (type == ScheduledEventCommon.eCallbackReason.NormalExpiration)
- {
- if (SchEvent.Name == FeatureEnableEventName)
- {
- if (PropertiesConfig.EnableRoomOnWhenOccupied)
- FeatureEnabled = true;
-
- Debug.Console(1, this, "*****Feature Enabled by event.*****");
- }
- else if (SchEvent.Name == FeatureDisableEventName)
- {
- FeatureEnabled = false;
-
- Debug.Console(1, this, "*****Feature Disabled by event.*****");
- }
- }
- }
-
- ///
- /// Checks if the feature should be currently enabled. Used on startup if processor starts after start time but before end time
- ///
- ///
- bool CheckIfFeatureShouldBeEnabled()
- {
- bool enabled = false;
-
- if(PropertiesConfig.EnableRoomOnWhenOccupied)
- {
- Debug.Console(1, this, "Current Time: {0} \n FeatureEnabledTime: {1} \n FeatureDisabledTime: {2}", DateTime.Now, FeatureEnabledTime, FeatureDisabledTime);
-
- if (DateTime.Now.TimeOfDay.CompareTo(FeatureEnabledTime.TimeOfDay) >= 0 && FeatureDisabledTime.TimeOfDay.CompareTo(DateTime.Now.TimeOfDay) > 0)
- {
- if (SchedulerUtilities.CheckIfDayOfWeekMatchesRecurrenceDays(DateTime.Now, CalculateDaysOfWeekRecurrence()))
- {
- enabled = true;
- }
- }
- }
-
- if(enabled)
- Debug.Console(1, this, "*****Feature Enabled*****");
- else
- Debug.Console(1, this, "*****Feature Disabled*****");
-
- return enabled;
- }
-
- ///
- /// Respond to Occupancy status event
- ///
- ///
- ///
- void RoomIsOccupiedFeedback_OutputChange(object sender, FeedbackEventArgs e)
- {
- Debug.Console(1, this, "RoomIsOccupiedFeeback.OutputChange event fired. e.BoolValue: {0}", e.BoolValue);
- if(e.BoolValue)
- {
- // Occupancy detected
-
- if (FeatureEnabled)
- {
- // Check room power state first
- if (!Room.OnFeedback.BoolValue)
- {
- Debug.Console(1, this, "Powering Room on to default source");
- Room.RunDefaultPresentRoute();
- }
- }
- }
- }
-
- void CreateEvent(ScheduledEvent schEvent, string name)
- {
- Debug.Console(1, this, "Adding Event: '{0}'", name);
- // Create the event
- if (schEvent == null)
- schEvent = new ScheduledEvent(name, FeatureEventGroup);
-
- // Set up its initial properties
-
- if(!schEvent.Acknowledgeable)
- schEvent.Acknowledgeable = true;
-
- if(!schEvent.Persistent)
- schEvent.Persistent = true;
-
- schEvent.DateAndTime.SetFirstDayOfWeek(ScheduledEventCommon.eFirstDayOfWeek.Sunday);
-
- // Set config driven properties
-
- if (schEvent.Name == FeatureEnableEventName)
- {
- schEvent.Description = "Enables the RoomOnToDefaultSourceWhenOccupiedFeature";
-
- var eventRecurrennce = CalculateDaysOfWeekRecurrence();
-
- var eventTime = new DateTime();
-
- // Check to make sure the date for this event is in the future
- if (DateTime.Now.CompareTo(FeatureEnabledTime) > 0)
- eventTime = FeatureEnabledTime.AddDays(1);
- else
- eventTime = FeatureEnabledTime;
-
- Debug.Console(1, this, "eventTime (before recurrence check): {0}", eventTime);
-
- // Check day of week against recurrence days and move date ahead as necessary to avoid throwing an exception by trying to set the event
- // start date on a day of the week that doesn't match teh recurrence values
- while(!SchedulerUtilities.CheckIfDayOfWeekMatchesRecurrenceDays(eventTime, eventRecurrennce))
- {
- eventTime = eventTime.AddDays(1);
- Debug.Console(1, this, "eventTime does not fall on a recurrence weekday. eventTime: {0}", eventTime);
- }
-
- schEvent.DateAndTime.SetAbsoluteEventTime(eventTime);
-
- Debug.Console(1, this, "Event '{0}' Absolute time set to {1}", schEvent.Name, schEvent.DateAndTime.ToString());
-
- CalculateAndSetAcknowledgeExpirationTimeout(schEvent, FeatureEnabledTime, FeatureDisabledTime);
-
- schEvent.Recurrence.Weekly(eventRecurrennce);
-
- }
- else if (schEvent.Name == FeatureDisableEventName)
- {
- schEvent.Description = "Disables the RoomOnToDefaultSourceWhenOccupiedFeature";
-
- // Check to make sure the date for this event is in the future
- if (DateTime.Now.CompareTo(FeatureDisabledTime) > 0)
- schEvent.DateAndTime.SetAbsoluteEventTime(FeatureDisabledTime.AddDays(1));
- else
- schEvent.DateAndTime.SetAbsoluteEventTime(FeatureDisabledTime);
-
- Debug.Console(1, this, "Event '{0}' Absolute time set to {1}", schEvent.Name, schEvent.DateAndTime.ToString());
-
- CalculateAndSetAcknowledgeExpirationTimeout(schEvent, FeatureDisabledTime, FeatureEnabledTime);
-
- schEvent.Recurrence.Daily();
- }
- }
-
- void CalculateAndSetAcknowledgeExpirationTimeout(ScheduledEvent schEvent, DateTime time1, DateTime time2)
- {
- Debug.Console(1, this, "time1.Hour = {0}", time1.Hour);
- Debug.Console(1, this, "time2.Hour = {0}", time2.Hour);
- Debug.Console(1, this, "time1.Minute = {0}", time1.Minute);
- Debug.Console(1, this, "time2.Minute = {0}", time2.Minute);
-
- // Calculate the Acknowledge Expiration timer to be the time between the enable and dispable events, less one minute
- var ackHours = time2.Hour - time1.Hour;
- if(ackHours < 0)
- ackHours = ackHours + 24;
- var ackMinutes = time2.Minute - time1.Minute;
-
- Debug.Console(1, this, "ackHours = {0}, ackMinutes = {1}", ackHours, ackMinutes);
-
- var ackTotalMinutes = ((ackHours * 60) + ackMinutes) - 1;
-
- var ackExpHour = ackTotalMinutes / 60;
- var ackExpMinutes = ackTotalMinutes % 60;
-
- Debug.Console(1, this, "Acknowledge Expiration Timeout: {0} hours, {1} minutes", ackExpHour, ackExpMinutes);
-
- schEvent.AcknowledgeExpirationTimeout.Hour = (ushort)(ackHours);
- schEvent.AcknowledgeExpirationTimeout.Minute = (ushort)(ackExpMinutes);
- }
-
- ///
- /// Checks existing event to see if it matches the execution time
- ///
- ///
- ///
- bool CheckExistingEventTimeForMatch(ScheduledEvent existingEvent, DateTime newTime)
- {
- bool isMatch = true;
-
- // Check to see if hour and minute match
- if (existingEvent.DateAndTime.Hour != newTime.Hour || existingEvent.DateAndTime.Minute != newTime.Minute)
- return false;
-
-
- return isMatch;
- }
-
- ///
- /// Checks existing event to see if it matches the recurrence days
- ///
- ///
- ///
- ///
- bool CheckExistingEventRecurrenceForMatch(ScheduledEvent existingEvent, ScheduledEventCommon.eWeekDays eWeekdays)
- {
- bool isMatch = true;
-
- // Check to see if recurrence matches
- if (eWeekdays != existingEvent.Recurrence.RecurrenceDays)
- return false;
-
- return isMatch;
- }
-
- ///
- /// Adds the Enable event to the local event group and sets its properties based on config
- ///
- void AddEnableEventToGroup()
- {
- if (!FeatureEventGroup.ScheduledEvents.ContainsKey(FeatureEnableEventName))
- {
- CreateEvent(FeatureEnableEvent, FeatureEnableEventName);
- }
- else
- {
- // Check if existing event has same time and recurrence as config values
-
- FeatureEnableEvent = FeatureEventGroup.ScheduledEvents[FeatureEnableEventName];
- Debug.Console(1, this, "Enable event already found in group");
-
- // Check config times and days against DateAndTime of existing event. If different, delete existing event and create new event
- if(!CheckExistingEventTimeForMatch(FeatureEnableEvent, FeatureEnabledTime) || !CheckExistingEventRecurrenceForMatch(FeatureEnableEvent, CalculateDaysOfWeekRecurrence()))
- {
- Debug.Console(1, this, "Existing event does not match new config properties. Deleting exisiting event: '{0}'", FeatureEnableEvent.Name);
- FeatureEventGroup.DeleteEvent(FeatureEnableEvent);
-
- FeatureEnableEvent = null;
-
- CreateEvent(FeatureEnableEvent, FeatureEnableEventName);
- }
- }
-
- }
-
- ///
- /// Adds the Enable event to the local event group and sets its properties based on config
- ///
- void AddDisableEventToGroup()
- {
- if (!FeatureEventGroup.ScheduledEvents.ContainsKey(FeatureDisableEventName))
- {
- CreateEvent(FeatureDisableEvent, FeatureDisableEventName);
- }
- else
- {
- FeatureDisableEvent = FeatureEventGroup.ScheduledEvents[FeatureDisableEventName];
- Debug.Console(1, this, "Disable event already found in group");
-
- // Check config times against DateAndTime of existing event. If different, delete existing event and create new event
- if(!CheckExistingEventTimeForMatch(FeatureDisableEvent, FeatureDisabledTime))
- {
- Debug.Console(1, this, "Existing event does not match new config properties. Deleting exisiting event: '{0}'", FeatureDisableEvent.Name);
-
- FeatureEventGroup.DeleteEvent(FeatureDisableEvent);
-
- FeatureDisableEvent = null;
-
- CreateEvent(FeatureDisableEvent, FeatureDisableEventName);
- }
- }
- }
-
-
- ///
- /// Calculates the correct bitfield enum value for the event recurrence based on the config values
- ///
- ///
- ScheduledEventCommon.eWeekDays CalculateDaysOfWeekRecurrence()
- {
- ScheduledEventCommon.eWeekDays value = new ScheduledEventCommon.eWeekDays();
-
- if (PropertiesConfig.EnableSunday)
- value = value | ScheduledEventCommon.eWeekDays.Sunday;
- if (PropertiesConfig.EnableMonday)
- value = value | ScheduledEventCommon.eWeekDays.Monday;
- if (PropertiesConfig.EnableTuesday)
- value = value | ScheduledEventCommon.eWeekDays.Tuesday;
- if (PropertiesConfig.EnableWednesday)
- value = value | ScheduledEventCommon.eWeekDays.Wednesday;
- if (PropertiesConfig.EnableThursday)
- value = value | ScheduledEventCommon.eWeekDays.Thursday;
- if (PropertiesConfig.EnableFriday)
- value = value | ScheduledEventCommon.eWeekDays.Friday;
- if (PropertiesConfig.EnableSaturday)
- value = value | ScheduledEventCommon.eWeekDays.Saturday;
-
- return value;
- }
-
- ///
- /// Callback for event that enables feature. Enables feature if config property is true
- ///
- ///
- ///
- void FeatureEnableEvent_UserCallBack(ScheduledEvent SchEvent, ScheduledEventCommon.eCallbackReason type)
- {
- if (type == ScheduledEventCommon.eCallbackReason.NormalExpiration)
- {
- if(PropertiesConfig.EnableRoomOnWhenOccupied)
- FeatureEnabled = true;
-
- Debug.Console(1, this, "RoomOnToDefaultSourceWhenOccupied Feature Enabled.");
- }
- }
-
- ///
- /// Callback for event that enables feature. Disables feature
- ///
- ///
- ///
- void FeatureDisableEvent_UserCallBack(ScheduledEvent SchEvent, ScheduledEventCommon.eCallbackReason type)
- {
- if (type == ScheduledEventCommon.eCallbackReason.NormalExpiration)
- {
- FeatureEnabled = false;
-
- Debug.Console(1, this, "RoomOnToDefaultSourceWhenOccupied Feature Disabled.");
- }
- }
- }
-
- public class RoomOnToDefaultSourceWhenOccupiedConfig
- {
- [JsonProperty("roomKey")]
- public string RoomKey { get; set; }
-
- [JsonProperty("enableRoomOnWhenOccupied")]
- public bool EnableRoomOnWhenOccupied { get; set; }
-
- [JsonProperty("occupancyStartTime")]
- public string OccupancyStartTime { get; set; }
-
- [JsonProperty("occupancyEndTime")]
- public string OccupancyEndTime { get; set; }
-
- [JsonProperty("enableSunday")]
- public bool EnableSunday { get; set; }
-
- [JsonProperty("enableMonday")]
- public bool EnableMonday { get; set; }
-
- [JsonProperty("enableTuesday")]
- public bool EnableTuesday { get; set; }
-
- [JsonProperty("enableWednesday")]
- public bool EnableWednesday { get; set; }
-
- [JsonProperty("enableThursday")]
- public bool EnableThursday { get; set; }
-
- [JsonProperty("enableFriday")]
- public bool EnableFriday { get; set; }
-
- [JsonProperty("enableSaturday")]
- public bool EnableSaturday { get; set; }
- }
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+using Crestron.SimplSharp;
+using Crestron.SimplSharp.Scheduler;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+
+using PepperDash.Core;
+using PepperDash.Essentials.Core;
+using PepperDash.Essentials.Core.Config;
+using PepperDash.Essentials.Core.Devices;
+
+namespace PepperDash.Essentials.Core
+{
+ ///
+ /// A device that when linked to a room can power the room on when enabled during scheduled hours.
+ ///
+ public class RoomOnToDefaultSourceWhenOccupied : ReconfigurableDevice
+ {
+ RoomOnToDefaultSourceWhenOccupiedConfig PropertiesConfig;
+
+ public bool FeatureEnabled { get; private set; }
+
+ public DateTime FeatureEnabledTime { get; private set; }
+
+ ScheduledEvent FeatureEnableEvent;
+
+ const string FeatureEnableEventName = "EnableRoomOnToDefaultSourceWhenOccupied";
+
+ public DateTime FeatureDisabledTime { get; private set; }
+
+ ScheduledEvent FeatureDisableEvent;
+
+ const string FeatureDisableEventName = "DisableRoomOnToDefaultSourceWhenOccupied";
+
+ ScheduledEventGroup FeatureEventGroup;
+
+ public EssentialsRoomBase Room { get; private set; }
+
+ private Fusion.EssentialsHuddleSpaceFusionSystemControllerBase FusionRoom;
+
+ public RoomOnToDefaultSourceWhenOccupied(DeviceConfig config) :
+ base (config)
+ {
+ PropertiesConfig = JsonConvert.DeserializeObject(config.Properties.ToString());
+
+ FeatureEventGroup = new ScheduledEventGroup(this.Key);
+
+ FeatureEventGroup.RetrieveAllEvents();
+
+ // Add to the global class for tracking
+ Scheduler.AddEventGroup(FeatureEventGroup);
+
+ AddPostActivationAction(() =>
+ {
+ // Subscribe to room event to know when RoomOccupancy is set and ready to be subscribed to
+ if (Room != null)
+ Room.RoomOccupancyIsSet += new EventHandler(RoomOccupancyIsSet);
+
+ else
+ Debug.Console(1, this, "Room has no RoomOccupancy object set");
+
+ var fusionRoomKey = PropertiesConfig.RoomKey + "-fusion";
+
+ FusionRoom = DeviceManager.GetDeviceForKey(fusionRoomKey) as Core.Fusion.EssentialsHuddleSpaceFusionSystemControllerBase;
+
+ if (FusionRoom == null)
+ Debug.Console(1, this, "Unable to get Fusion Room from Device Manager with key: {0}", fusionRoomKey);
+ });
+ }
+
+ public override bool CustomActivate()
+ {
+ SetUpDevice();
+
+ return base.CustomActivate();
+ }
+
+ ///
+ /// Sets up device based on config values
+ ///
+ void SetUpDevice()
+ {
+ Room = DeviceManager.GetDeviceForKey(PropertiesConfig.RoomKey) as EssentialsRoomBase;
+
+ if (Room != null)
+ {
+ try
+ {
+ FeatureEnabledTime = DateTime.Parse(PropertiesConfig.OccupancyStartTime);
+
+ if (FeatureEnabledTime != null)
+ {
+ Debug.Console(1, this, "Enabled Time: {0}", FeatureEnabledTime.ToString());
+ }
+ else
+ Debug.Console(1, this, "Unable to parse {0} to DateTime", PropertiesConfig.OccupancyStartTime);
+ }
+ catch (Exception e)
+ {
+ Debug.Console(1, this, "Unable to parse OccupancyStartTime property: {0} \n Error: {1}", PropertiesConfig.OccupancyStartTime, e);
+ }
+
+ try
+ {
+ FeatureDisabledTime = DateTime.Parse(PropertiesConfig.OccupancyEndTime);
+
+ if (FeatureDisabledTime != null)
+ {
+ Debug.Console(1, this, "Disabled Time: {0}", FeatureDisabledTime.ToString());
+ }
+ else
+ Debug.Console(1, this, "Unable to parse {0} to DateTime", PropertiesConfig.OccupancyEndTime);
+ }
+ catch (Exception e)
+ {
+ Debug.Console(1, this, "Unable to parse a DateTime config value \n Error: {1}", e);
+ }
+
+ if (!PropertiesConfig.EnableRoomOnWhenOccupied)
+ FeatureEventGroup.ClearAllEvents();
+ else
+ {
+ AddEnableEventToGroup();
+
+ AddDisableEventToGroup();
+
+ FeatureEventGroup.UserGroupCallBack += new ScheduledEventGroup.UserEventGroupCallBack(FeatureEventGroup_UserGroupCallBack);
+
+ FeatureEventGroup.EnableAllEvents();
+ }
+
+ FeatureEnabled = CheckIfFeatureShouldBeEnabled();
+ }
+ else
+ Debug.Console(1, this, "Unable to get room from Device Manager with key: {0}", PropertiesConfig.RoomKey);
+ }
+
+
+ protected override void CustomSetConfig(DeviceConfig config)
+ {
+ var newPropertiesConfig = JsonConvert.DeserializeObject(config.Properties.ToString());
+
+ if(newPropertiesConfig != null)
+ PropertiesConfig = newPropertiesConfig;
+
+ ConfigWriter.UpdateDeviceConfig(config);
+
+ SetUpDevice();
+ }
+
+ ///
+ /// Subscribe to feedback from RoomIsOccupiedFeedback on Room
+ ///
+ ///
+ ///
+ void RoomOccupancyIsSet(object sender, EventArgs e)
+ {
+ if (Room.RoomOccupancy != null)
+ {
+ Room.RoomOccupancy.RoomIsOccupiedFeedback.OutputChange -= RoomIsOccupiedFeedback_OutputChange;
+ Room.RoomOccupancy.RoomIsOccupiedFeedback.OutputChange += new EventHandler(RoomIsOccupiedFeedback_OutputChange);
+ Debug.Console(1, this, "Subscribed to RoomOccupancy status from: '{0}'", Room.Key);
+ }
+ }
+
+ void FeatureEventGroup_UserGroupCallBack(ScheduledEvent SchEvent, ScheduledEventCommon.eCallbackReason type)
+ {
+ if (type == ScheduledEventCommon.eCallbackReason.NormalExpiration)
+ {
+ if (SchEvent.Name == FeatureEnableEventName)
+ {
+ if (PropertiesConfig.EnableRoomOnWhenOccupied)
+ FeatureEnabled = true;
+
+ Debug.Console(1, this, "*****Feature Enabled by event.*****");
+ }
+ else if (SchEvent.Name == FeatureDisableEventName)
+ {
+ FeatureEnabled = false;
+
+ Debug.Console(1, this, "*****Feature Disabled by event.*****");
+ }
+ }
+ }
+
+ ///
+ /// Checks if the feature should be currently enabled. Used on startup if processor starts after start time but before end time
+ ///
+ ///
+ bool CheckIfFeatureShouldBeEnabled()
+ {
+ bool enabled = false;
+
+ if(PropertiesConfig.EnableRoomOnWhenOccupied)
+ {
+ Debug.Console(1, this, "Current Time: {0} \n FeatureEnabledTime: {1} \n FeatureDisabledTime: {2}", DateTime.Now, FeatureEnabledTime, FeatureDisabledTime);
+
+ if (DateTime.Now.TimeOfDay.CompareTo(FeatureEnabledTime.TimeOfDay) >= 0 && FeatureDisabledTime.TimeOfDay.CompareTo(DateTime.Now.TimeOfDay) > 0)
+ {
+ if (SchedulerUtilities.CheckIfDayOfWeekMatchesRecurrenceDays(DateTime.Now, CalculateDaysOfWeekRecurrence()))
+ {
+ enabled = true;
+ }
+ }
+ }
+
+ if(enabled)
+ Debug.Console(1, this, "*****Feature Enabled*****");
+ else
+ Debug.Console(1, this, "*****Feature Disabled*****");
+
+ return enabled;
+ }
+
+ ///
+ /// Respond to Occupancy status event
+ ///
+ ///
+ ///
+ void RoomIsOccupiedFeedback_OutputChange(object sender, FeedbackEventArgs e)
+ {
+ Debug.Console(1, this, "RoomIsOccupiedFeeback.OutputChange event fired. e.BoolValue: {0}", e.BoolValue);
+ if(e.BoolValue)
+ {
+ // Occupancy detected
+
+ if (FeatureEnabled)
+ {
+ // Check room power state first
+ if (!Room.OnFeedback.BoolValue)
+ {
+ Debug.Console(1, this, "Powering Room on to default source");
+ Room.RunDefaultPresentRoute();
+ }
+ }
+ }
+ }
+
+ void CreateEvent(ScheduledEvent schEvent, string name)
+ {
+ Debug.Console(1, this, "Adding Event: '{0}'", name);
+ // Create the event
+ if (schEvent == null)
+ schEvent = new ScheduledEvent(name, FeatureEventGroup);
+
+ // Set up its initial properties
+
+ if(!schEvent.Acknowledgeable)
+ schEvent.Acknowledgeable = true;
+
+ if(!schEvent.Persistent)
+ schEvent.Persistent = true;
+
+ schEvent.DateAndTime.SetFirstDayOfWeek(ScheduledEventCommon.eFirstDayOfWeek.Sunday);
+
+ // Set config driven properties
+
+ if (schEvent.Name == FeatureEnableEventName)
+ {
+ schEvent.Description = "Enables the RoomOnToDefaultSourceWhenOccupiedFeature";
+
+ var eventRecurrennce = CalculateDaysOfWeekRecurrence();
+
+ var eventTime = new DateTime();
+
+ // Check to make sure the date for this event is in the future
+ if (DateTime.Now.CompareTo(FeatureEnabledTime) > 0)
+ eventTime = FeatureEnabledTime.AddDays(1);
+ else
+ eventTime = FeatureEnabledTime;
+
+ Debug.Console(1, this, "eventTime (before recurrence check): {0}", eventTime);
+
+ // Check day of week against recurrence days and move date ahead as necessary to avoid throwing an exception by trying to set the event
+ // start date on a day of the week that doesn't match teh recurrence values
+ while(!SchedulerUtilities.CheckIfDayOfWeekMatchesRecurrenceDays(eventTime, eventRecurrennce))
+ {
+ eventTime = eventTime.AddDays(1);
+ Debug.Console(1, this, "eventTime does not fall on a recurrence weekday. eventTime: {0}", eventTime);
+ }
+
+ schEvent.DateAndTime.SetAbsoluteEventTime(eventTime);
+
+ Debug.Console(1, this, "Event '{0}' Absolute time set to {1}", schEvent.Name, schEvent.DateAndTime.ToString());
+
+ CalculateAndSetAcknowledgeExpirationTimeout(schEvent, FeatureEnabledTime, FeatureDisabledTime);
+
+ schEvent.Recurrence.Weekly(eventRecurrennce);
+
+ }
+ else if (schEvent.Name == FeatureDisableEventName)
+ {
+ schEvent.Description = "Disables the RoomOnToDefaultSourceWhenOccupiedFeature";
+
+ // Check to make sure the date for this event is in the future
+ if (DateTime.Now.CompareTo(FeatureDisabledTime) > 0)
+ schEvent.DateAndTime.SetAbsoluteEventTime(FeatureDisabledTime.AddDays(1));
+ else
+ schEvent.DateAndTime.SetAbsoluteEventTime(FeatureDisabledTime);
+
+ Debug.Console(1, this, "Event '{0}' Absolute time set to {1}", schEvent.Name, schEvent.DateAndTime.ToString());
+
+ CalculateAndSetAcknowledgeExpirationTimeout(schEvent, FeatureDisabledTime, FeatureEnabledTime);
+
+ schEvent.Recurrence.Daily();
+ }
+ }
+
+ void CalculateAndSetAcknowledgeExpirationTimeout(ScheduledEvent schEvent, DateTime time1, DateTime time2)
+ {
+ Debug.Console(1, this, "time1.Hour = {0}", time1.Hour);
+ Debug.Console(1, this, "time2.Hour = {0}", time2.Hour);
+ Debug.Console(1, this, "time1.Minute = {0}", time1.Minute);
+ Debug.Console(1, this, "time2.Minute = {0}", time2.Minute);
+
+ // Calculate the Acknowledge Expiration timer to be the time between the enable and dispable events, less one minute
+ var ackHours = time2.Hour - time1.Hour;
+ if(ackHours < 0)
+ ackHours = ackHours + 24;
+ var ackMinutes = time2.Minute - time1.Minute;
+
+ Debug.Console(1, this, "ackHours = {0}, ackMinutes = {1}", ackHours, ackMinutes);
+
+ var ackTotalMinutes = ((ackHours * 60) + ackMinutes) - 1;
+
+ var ackExpHour = ackTotalMinutes / 60;
+ var ackExpMinutes = ackTotalMinutes % 60;
+
+ Debug.Console(1, this, "Acknowledge Expiration Timeout: {0} hours, {1} minutes", ackExpHour, ackExpMinutes);
+
+ schEvent.AcknowledgeExpirationTimeout.Hour = (ushort)(ackHours);
+ schEvent.AcknowledgeExpirationTimeout.Minute = (ushort)(ackExpMinutes);
+ }
+
+ ///
+ /// Checks existing event to see if it matches the execution time
+ ///
+ ///
+ ///
+ bool CheckExistingEventTimeForMatch(ScheduledEvent existingEvent, DateTime newTime)
+ {
+ bool isMatch = true;
+
+ // Check to see if hour and minute match
+ if (existingEvent.DateAndTime.Hour != newTime.Hour || existingEvent.DateAndTime.Minute != newTime.Minute)
+ return false;
+
+
+ return isMatch;
+ }
+
+ ///
+ /// Checks existing event to see if it matches the recurrence days
+ ///
+ ///
+ ///
+ ///
+ bool CheckExistingEventRecurrenceForMatch(ScheduledEvent existingEvent, ScheduledEventCommon.eWeekDays eWeekdays)
+ {
+ bool isMatch = true;
+
+ // Check to see if recurrence matches
+ if (eWeekdays != existingEvent.Recurrence.RecurrenceDays)
+ return false;
+
+ return isMatch;
+ }
+
+ ///
+ /// Adds the Enable event to the local event group and sets its properties based on config
+ ///
+ void AddEnableEventToGroup()
+ {
+ if (!FeatureEventGroup.ScheduledEvents.ContainsKey(FeatureEnableEventName))
+ {
+ CreateEvent(FeatureEnableEvent, FeatureEnableEventName);
+ }
+ else
+ {
+ // Check if existing event has same time and recurrence as config values
+
+ FeatureEnableEvent = FeatureEventGroup.ScheduledEvents[FeatureEnableEventName];
+ Debug.Console(1, this, "Enable event already found in group");
+
+ // Check config times and days against DateAndTime of existing event. If different, delete existing event and create new event
+ if(!CheckExistingEventTimeForMatch(FeatureEnableEvent, FeatureEnabledTime) || !CheckExistingEventRecurrenceForMatch(FeatureEnableEvent, CalculateDaysOfWeekRecurrence()))
+ {
+ Debug.Console(1, this, "Existing event does not match new config properties. Deleting exisiting event: '{0}'", FeatureEnableEvent.Name);
+ FeatureEventGroup.DeleteEvent(FeatureEnableEvent);
+
+ FeatureEnableEvent = null;
+
+ CreateEvent(FeatureEnableEvent, FeatureEnableEventName);
+ }
+ }
+
+ }
+
+ ///
+ /// Adds the Enable event to the local event group and sets its properties based on config
+ ///
+ void AddDisableEventToGroup()
+ {
+ if (!FeatureEventGroup.ScheduledEvents.ContainsKey(FeatureDisableEventName))
+ {
+ CreateEvent(FeatureDisableEvent, FeatureDisableEventName);
+ }
+ else
+ {
+ FeatureDisableEvent = FeatureEventGroup.ScheduledEvents[FeatureDisableEventName];
+ Debug.Console(1, this, "Disable event already found in group");
+
+ // Check config times against DateAndTime of existing event. If different, delete existing event and create new event
+ if(!CheckExistingEventTimeForMatch(FeatureDisableEvent, FeatureDisabledTime))
+ {
+ Debug.Console(1, this, "Existing event does not match new config properties. Deleting exisiting event: '{0}'", FeatureDisableEvent.Name);
+
+ FeatureEventGroup.DeleteEvent(FeatureDisableEvent);
+
+ FeatureDisableEvent = null;
+
+ CreateEvent(FeatureDisableEvent, FeatureDisableEventName);
+ }
+ }
+ }
+
+
+ ///
+ /// Calculates the correct bitfield enum value for the event recurrence based on the config values
+ ///
+ ///
+ ScheduledEventCommon.eWeekDays CalculateDaysOfWeekRecurrence()
+ {
+ ScheduledEventCommon.eWeekDays value = new ScheduledEventCommon.eWeekDays();
+
+ if (PropertiesConfig.EnableSunday)
+ value = value | ScheduledEventCommon.eWeekDays.Sunday;
+ if (PropertiesConfig.EnableMonday)
+ value = value | ScheduledEventCommon.eWeekDays.Monday;
+ if (PropertiesConfig.EnableTuesday)
+ value = value | ScheduledEventCommon.eWeekDays.Tuesday;
+ if (PropertiesConfig.EnableWednesday)
+ value = value | ScheduledEventCommon.eWeekDays.Wednesday;
+ if (PropertiesConfig.EnableThursday)
+ value = value | ScheduledEventCommon.eWeekDays.Thursday;
+ if (PropertiesConfig.EnableFriday)
+ value = value | ScheduledEventCommon.eWeekDays.Friday;
+ if (PropertiesConfig.EnableSaturday)
+ value = value | ScheduledEventCommon.eWeekDays.Saturday;
+
+ return value;
+ }
+
+ ///
+ /// Callback for event that enables feature. Enables feature if config property is true
+ ///
+ ///
+ ///
+ void FeatureEnableEvent_UserCallBack(ScheduledEvent SchEvent, ScheduledEventCommon.eCallbackReason type)
+ {
+ if (type == ScheduledEventCommon.eCallbackReason.NormalExpiration)
+ {
+ if(PropertiesConfig.EnableRoomOnWhenOccupied)
+ FeatureEnabled = true;
+
+ Debug.Console(1, this, "RoomOnToDefaultSourceWhenOccupied Feature Enabled.");
+ }
+ }
+
+ ///
+ /// Callback for event that enables feature. Disables feature
+ ///
+ ///
+ ///
+ void FeatureDisableEvent_UserCallBack(ScheduledEvent SchEvent, ScheduledEventCommon.eCallbackReason type)
+ {
+ if (type == ScheduledEventCommon.eCallbackReason.NormalExpiration)
+ {
+ FeatureEnabled = false;
+
+ Debug.Console(1, this, "RoomOnToDefaultSourceWhenOccupied Feature Disabled.");
+ }
+ }
+ }
+
+ public class RoomOnToDefaultSourceWhenOccupiedConfig
+ {
+ [JsonProperty("roomKey")]
+ public string RoomKey { get; set; }
+
+ [JsonProperty("enableRoomOnWhenOccupied")]
+ public bool EnableRoomOnWhenOccupied { get; set; }
+
+ [JsonProperty("occupancyStartTime")]
+ public string OccupancyStartTime { get; set; }
+
+ [JsonProperty("occupancyEndTime")]
+ public string OccupancyEndTime { get; set; }
+
+ [JsonProperty("enableSunday")]
+ public bool EnableSunday { get; set; }
+
+ [JsonProperty("enableMonday")]
+ public bool EnableMonday { get; set; }
+
+ [JsonProperty("enableTuesday")]
+ public bool EnableTuesday { get; set; }
+
+ [JsonProperty("enableWednesday")]
+ public bool EnableWednesday { get; set; }
+
+ [JsonProperty("enableThursday")]
+ public bool EnableThursday { get; set; }
+
+ [JsonProperty("enableFriday")]
+ public bool EnableFriday { get; set; }
+
+ [JsonProperty("enableSaturday")]
+ public bool EnableSaturday { get; set; }
+ }
}
\ No newline at end of file
diff --git a/PepperDashEssentials/Room/Types/EssentialsRoomBase.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/EssentialsRoomBase.cs
similarity index 93%
rename from PepperDashEssentials/Room/Types/EssentialsRoomBase.cs
rename to essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/EssentialsRoomBase.cs
index fc177b51..e8193837 100644
--- a/PepperDashEssentials/Room/Types/EssentialsRoomBase.cs
+++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/EssentialsRoomBase.cs
@@ -1,297 +1,311 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using Crestron.SimplSharp;
-using Crestron.SimplSharp.Scheduler;
-
-using PepperDash.Core;
-using PepperDash.Essentials.Core;
-using PepperDash.Essentials.Core.Config;
-using PepperDash.Essentials.Core.Devices;
-using PepperDash.Essentials.Devices.Common.Occupancy;
-
-namespace PepperDash.Essentials
-{
- ///
- ///
- ///
- public abstract class EssentialsRoomBase : ReconfigurableDevice
- {
- ///
- ///
- ///
- public BoolFeedback OnFeedback { get; private set; }
-
- ///
- /// Fires when the RoomOccupancy object is set
- ///
- public event EventHandler RoomOccupancyIsSet;
-
- public BoolFeedback IsWarmingUpFeedback { get; private set; }
- public BoolFeedback IsCoolingDownFeedback { get; private set; }
-
- public IOccupancyStatusProvider RoomOccupancy { get; private set; }
-
- public bool OccupancyStatusProviderIsRemote { get; private set; }
-
- protected abstract Func IsWarmingFeedbackFunc { get; }
- protected abstract Func IsCoolingFeedbackFunc { get; }
-
- ///
- /// Timer used for informing the UIs of a shutdown
- ///
- public SecondsCountdownTimer ShutdownPromptTimer { get; private set; }
-
- ///
- ///
- ///
- public int ShutdownPromptSeconds { get; set; }
- public int ShutdownVacancySeconds { get; set; }
- public eShutdownType ShutdownType { get; private set; }
-
- public PepperDash.Essentials.Room.EssentialsRoomEmergencyBase Emergency { get; set; }
-
- public PepperDash.Essentials.Devices.Common.Microphones.MicrophonePrivacyController MicrophonePrivacy { get; set; }
-
- public string LogoUrl { get; set; }
-
- protected SecondsCountdownTimer RoomVacancyShutdownTimer { get; private set; }
-
- public eVacancyMode VacancyMode { get; private set; }
-
- ///
- /// Seconds after vacancy prompt is displayed until shutdown
- ///
- protected int RoomVacancyShutdownSeconds;
-
- ///
- /// Seconds after vacancy detected until prompt is displayed
- ///
- protected int RoomVacancyShutdownPromptSeconds;
-
- ///
- ///
- ///
- protected abstract Func OnFeedbackFunc { get; }
-
- protected Dictionary SavedVolumeLevels = new Dictionary();
-
- ///
- /// When volume control devices change, should we zero the one that we are leaving?
- ///
- public bool ZeroVolumeWhenSwtichingVolumeDevices { get; private set; }
-
-
- public EssentialsRoomBase(DeviceConfig config)
- : base(config)
- {
- // Setup the ShutdownPromptTimer
- ShutdownPromptTimer = new SecondsCountdownTimer(Key + "-offTimer");
- ShutdownPromptTimer.IsRunningFeedback.OutputChange += (o, a) =>
- {
- if (!ShutdownPromptTimer.IsRunningFeedback.BoolValue)
- ShutdownType = eShutdownType.None;
- };
- ShutdownPromptTimer.HasFinished += (o, a) => Shutdown(); // Shutdown is triggered
-
- ShutdownPromptSeconds = 60;
- ShutdownVacancySeconds = 120;
- ShutdownType = eShutdownType.None;
-
- RoomVacancyShutdownTimer = new SecondsCountdownTimer(Key + "-vacancyOffTimer");
- //RoomVacancyShutdownTimer.IsRunningFeedback.OutputChange += (o, a) =>
- //{
- // if (!RoomVacancyShutdownTimer.IsRunningFeedback.BoolValue)
- // ShutdownType = ShutdownType.Vacancy;
- //};
- RoomVacancyShutdownTimer.HasFinished += new EventHandler(RoomVacancyShutdownPromptTimer_HasFinished); // Shutdown is triggered
-
- RoomVacancyShutdownPromptSeconds = 1500; // 25 min to prompt warning
- RoomVacancyShutdownSeconds = 240; // 4 min after prompt will trigger shutdown prompt
- VacancyMode = eVacancyMode.None;
-
- OnFeedback = new BoolFeedback(OnFeedbackFunc);
-
- IsWarmingUpFeedback = new BoolFeedback(IsWarmingFeedbackFunc);
- IsCoolingDownFeedback = new BoolFeedback(IsCoolingFeedbackFunc);
-
- AddPostActivationAction(() =>
- {
- if (RoomOccupancy != null)
- OnRoomOccupancyIsSet();
- });
- }
-
- void RoomVacancyShutdownPromptTimer_HasFinished(object sender, EventArgs e)
- {
- switch (VacancyMode)
- {
- case eVacancyMode.None:
- StartRoomVacancyTimer(eVacancyMode.InInitialVacancy);
- break;
- case eVacancyMode.InInitialVacancy:
- StartRoomVacancyTimer(eVacancyMode.InShutdownWarning);
- break;
- case eVacancyMode.InShutdownWarning:
- {
- StartShutdown(eShutdownType.Vacancy);
- Debug.Console(0, this, "Shutting Down due to vacancy.");
- break;
- }
- default:
- break;
- }
- }
-
- ///
- ///
- ///
- ///
- public void StartShutdown(eShutdownType type)
- {
- // Check for shutdowns running. Manual should override other shutdowns
-
- if (type == eShutdownType.Manual)
- ShutdownPromptTimer.SecondsToCount = ShutdownPromptSeconds;
- else if (type == eShutdownType.Vacancy)
- ShutdownPromptTimer.SecondsToCount = ShutdownVacancySeconds;
- ShutdownType = type;
- ShutdownPromptTimer.Start();
-
- Debug.Console(0, this, "ShutdwonPromptTimer Started. Type: {0}. Seconds: {1}", ShutdownType, ShutdownPromptTimer.SecondsToCount);
- }
-
- public void StartRoomVacancyTimer(eVacancyMode mode)
- {
- if (mode == eVacancyMode.None)
- RoomVacancyShutdownTimer.SecondsToCount = RoomVacancyShutdownPromptSeconds;
- else if (mode == eVacancyMode.InInitialVacancy)
- RoomVacancyShutdownTimer.SecondsToCount = RoomVacancyShutdownSeconds;
- VacancyMode = mode;
- RoomVacancyShutdownTimer.Start();
-
- Debug.Console(0, this, "Vacancy Timer Started. Mode: {0}. Seconds: {1}", VacancyMode, RoomVacancyShutdownTimer.SecondsToCount);
- }
-
- ///
- /// Resets the vacancy mode and shutsdwon the room
- ///
- public void Shutdown()
- {
- VacancyMode = eVacancyMode.None;
- EndShutdown();
- }
-
- ///
- /// This method is for the derived class to define it's specific shutdown
- /// requirements but should not be called directly. It is called by Shutdown()
- ///
- protected abstract void EndShutdown();
-
-
- ///
- /// Override this to implement a default volume level(s) method
- ///
- public abstract void SetDefaultLevels();
-
- ///
- /// Sets the object to be used as the IOccupancyStatusProvider for the room. Can be an Occupancy Aggregator or a specific device
- ///
- ///
- public void SetRoomOccupancy(IOccupancyStatusProvider statusProvider, int timeoutMinutes)
- {
- if (statusProvider == null)
- {
- Debug.Console(0, this, "ERROR: Occupancy sensor device is null");
- return;
- }
-
- // If status provider is fusion, set flag to remote
- if (statusProvider is PepperDash.Essentials.Fusion.EssentialsHuddleSpaceFusionSystemControllerBase)
- OccupancyStatusProviderIsRemote = true;
-
- if(timeoutMinutes > 0)
- RoomVacancyShutdownSeconds = timeoutMinutes * 60;
-
- Debug.Console(1, this, "RoomVacancyShutdownSeconds set to {0}", RoomVacancyShutdownSeconds);
-
- RoomOccupancy = statusProvider;
-
- OnRoomOccupancyIsSet();
-
- RoomOccupancy.RoomIsOccupiedFeedback.OutputChange -= RoomIsOccupiedFeedback_OutputChange;
- RoomOccupancy.RoomIsOccupiedFeedback.OutputChange += RoomIsOccupiedFeedback_OutputChange;
-
- Debug.Console(0, this, "Room Occupancy set to device: '{0}'", (statusProvider as Device).Key);
- }
-
- void OnRoomOccupancyIsSet()
- {
- var handler = RoomOccupancyIsSet;
- if (handler != null)
- handler(this, new EventArgs());
- }
-
- ///
- /// To allow base class to power room on to last source
- ///
- public abstract void PowerOnToDefaultOrLastSource();
-
- ///
- /// To allow base class to power room on to default source
- ///
- ///
- public abstract bool RunDefaultPresentRoute();
-
- void RoomIsOccupiedFeedback_OutputChange(object sender, EventArgs e)
- {
- if (RoomOccupancy.RoomIsOccupiedFeedback.BoolValue == false)
- {
- Debug.Console(1, this, "Notice: Vacancy Detected");
- // Trigger the timer when the room is vacant
- StartRoomVacancyTimer(eVacancyMode.InInitialVacancy);
- }
- else
- {
- Debug.Console(1, this, "Notice: Occupancy Detected");
- // Reset the timer when the room is occupied
- RoomVacancyShutdownTimer.Cancel();
- }
- }
-
- ///
- /// Executes when RoomVacancyShutdownTimer expires. Used to trigger specific room actions as needed. Must nullify the timer object when executed
- ///
- ///
- public abstract void RoomVacatedForTimeoutPeriod(object o);
- }
-
- ///
- /// To describe the various ways a room may be shutting down
- ///
- public enum eShutdownType
- {
- None = 0,
- External,
- Manual,
- Vacancy
- }
-
- public enum eVacancyMode
- {
- None = 0,
- InInitialVacancy,
- InShutdownWarning
- }
-
- ///
- ///
- ///
- public enum eWarmingCoolingMode
- {
- None,
- Warming,
- Cooling
- }
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Crestron.SimplSharp;
+using Crestron.SimplSharp.Scheduler;
+
+using PepperDash.Core;
+using PepperDash.Essentials.Core;
+using PepperDash.Essentials.Core.Config;
+using PepperDash.Essentials.Core.Devices;
+
+namespace PepperDash.Essentials.Core
+{
+ ///
+ ///
+ ///
+ public abstract class EssentialsRoomBase : ReconfigurableDevice
+ {
+ ///
+ ///
+ ///
+ public BoolFeedback OnFeedback { get; private set; }
+
+ ///
+ /// Fires when the RoomOccupancy object is set
+ ///
+ public event EventHandler RoomOccupancyIsSet;
+
+ public BoolFeedback IsWarmingUpFeedback { get; private set; }
+ public BoolFeedback IsCoolingDownFeedback { get; private set; }
+
+ public IOccupancyStatusProvider RoomOccupancy { get; private set; }
+
+ public bool OccupancyStatusProviderIsRemote { get; private set; }
+
+ protected abstract Func IsWarmingFeedbackFunc { get; }
+ protected abstract Func IsCoolingFeedbackFunc { get; }
+
+ ///
+ /// The config name of the source list
+ ///
+ public string SourceListKey { get; set; }
+
+ ///
+ /// Timer used for informing the UIs of a shutdown
+ ///
+ public SecondsCountdownTimer ShutdownPromptTimer { get; private set; }
+
+ ///
+ ///
+ ///
+ public int ShutdownPromptSeconds { get; set; }
+ public int ShutdownVacancySeconds { get; set; }
+ public eShutdownType ShutdownType { get; private set; }
+
+ public EssentialsRoomEmergencyBase Emergency { get; set; }
+
+ public Core.Privacy.MicrophonePrivacyController MicrophonePrivacy { get; set; }
+
+ public string LogoUrl { get; set; }
+
+ protected SecondsCountdownTimer RoomVacancyShutdownTimer { get; private set; }
+
+ public eVacancyMode VacancyMode { get; private set; }
+
+ ///
+ /// Seconds after vacancy prompt is displayed until shutdown
+ ///
+ protected int RoomVacancyShutdownSeconds;
+
+ ///
+ /// Seconds after vacancy detected until prompt is displayed
+ ///
+ protected int RoomVacancyShutdownPromptSeconds;
+
+ ///
+ ///
+ ///
+ protected abstract Func OnFeedbackFunc { get; }
+
+ protected Dictionary SavedVolumeLevels = new Dictionary();
+
+ ///
+ /// When volume control devices change, should we zero the one that we are leaving?
+ ///
+ public bool ZeroVolumeWhenSwtichingVolumeDevices { get; private set; }
+
+
+ public EssentialsRoomBase(DeviceConfig config)
+ : base(config)
+ {
+ // Setup the ShutdownPromptTimer
+ ShutdownPromptTimer = new SecondsCountdownTimer(Key + "-offTimer");
+ ShutdownPromptTimer.IsRunningFeedback.OutputChange += (o, a) =>
+ {
+ if (!ShutdownPromptTimer.IsRunningFeedback.BoolValue)
+ ShutdownType = eShutdownType.None;
+ };
+ ShutdownPromptTimer.HasFinished += (o, a) => Shutdown(); // Shutdown is triggered
+
+ ShutdownPromptSeconds = 60;
+ ShutdownVacancySeconds = 120;
+ ShutdownType = eShutdownType.None;
+
+ RoomVacancyShutdownTimer = new SecondsCountdownTimer(Key + "-vacancyOffTimer");
+ //RoomVacancyShutdownTimer.IsRunningFeedback.OutputChange += (o, a) =>
+ //{
+ // if (!RoomVacancyShutdownTimer.IsRunningFeedback.BoolValue)
+ // ShutdownType = ShutdownType.Vacancy;
+ //};
+ RoomVacancyShutdownTimer.HasFinished += new EventHandler(RoomVacancyShutdownPromptTimer_HasFinished); // Shutdown is triggered
+
+ RoomVacancyShutdownPromptSeconds = 1500; // 25 min to prompt warning
+ RoomVacancyShutdownSeconds = 240; // 4 min after prompt will trigger shutdown prompt
+ VacancyMode = eVacancyMode.None;
+
+ OnFeedback = new BoolFeedback(OnFeedbackFunc);
+
+ IsWarmingUpFeedback = new BoolFeedback(IsWarmingFeedbackFunc);
+ IsCoolingDownFeedback = new BoolFeedback(IsCoolingFeedbackFunc);
+
+ AddPostActivationAction(() =>
+ {
+ if (RoomOccupancy != null)
+ OnRoomOccupancyIsSet();
+ });
+ }
+
+ void RoomVacancyShutdownPromptTimer_HasFinished(object sender, EventArgs e)
+ {
+ switch (VacancyMode)
+ {
+ case eVacancyMode.None:
+ StartRoomVacancyTimer(eVacancyMode.InInitialVacancy);
+ break;
+ case eVacancyMode.InInitialVacancy:
+ StartRoomVacancyTimer(eVacancyMode.InShutdownWarning);
+ break;
+ case eVacancyMode.InShutdownWarning:
+ {
+ StartShutdown(eShutdownType.Vacancy);
+ Debug.Console(0, this, "Shutting Down due to vacancy.");
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ ///
+ ///
+ ///
+ ///
+ public void StartShutdown(eShutdownType type)
+ {
+ // Check for shutdowns running. Manual should override other shutdowns
+
+ if (type == eShutdownType.Manual)
+ ShutdownPromptTimer.SecondsToCount = ShutdownPromptSeconds;
+ else if (type == eShutdownType.Vacancy)
+ ShutdownPromptTimer.SecondsToCount = ShutdownVacancySeconds;
+ ShutdownType = type;
+ ShutdownPromptTimer.Start();
+
+ Debug.Console(0, this, "ShutdwonPromptTimer Started. Type: {0}. Seconds: {1}", ShutdownType, ShutdownPromptTimer.SecondsToCount);
+ }
+
+ public void StartRoomVacancyTimer(eVacancyMode mode)
+ {
+ if (mode == eVacancyMode.None)
+ RoomVacancyShutdownTimer.SecondsToCount = RoomVacancyShutdownPromptSeconds;
+ else if (mode == eVacancyMode.InInitialVacancy)
+ RoomVacancyShutdownTimer.SecondsToCount = RoomVacancyShutdownSeconds;
+ VacancyMode = mode;
+ RoomVacancyShutdownTimer.Start();
+
+ Debug.Console(0, this, "Vacancy Timer Started. Mode: {0}. Seconds: {1}", VacancyMode, RoomVacancyShutdownTimer.SecondsToCount);
+ }
+
+ ///
+ /// Resets the vacancy mode and shutsdwon the room
+ ///
+ public void Shutdown()
+ {
+ VacancyMode = eVacancyMode.None;
+ EndShutdown();
+ }
+
+ ///
+ /// This method is for the derived class to define it's specific shutdown
+ /// requirements but should not be called directly. It is called by Shutdown()
+ ///
+ protected abstract void EndShutdown();
+
+
+ ///
+ /// Override this to implement a default volume level(s) method
+ ///
+ public abstract void SetDefaultLevels();
+
+ ///
+ /// Sets the object to be used as the IOccupancyStatusProvider for the room. Can be an Occupancy Aggregator or a specific device
+ ///
+ ///
+ public void SetRoomOccupancy(IOccupancyStatusProvider statusProvider, int timeoutMinutes)
+ {
+ if (statusProvider == null)
+ {
+ Debug.Console(0, this, "ERROR: Occupancy sensor device is null");
+ return;
+ }
+
+ // If status provider is fusion, set flag to remote
+ if (statusProvider is Core.Fusion.EssentialsHuddleSpaceFusionSystemControllerBase)
+ OccupancyStatusProviderIsRemote = true;
+
+ if(timeoutMinutes > 0)
+ RoomVacancyShutdownSeconds = timeoutMinutes * 60;
+
+ Debug.Console(1, this, "RoomVacancyShutdownSeconds set to {0}", RoomVacancyShutdownSeconds);
+
+ RoomOccupancy = statusProvider;
+
+ OnRoomOccupancyIsSet();
+
+ RoomOccupancy.RoomIsOccupiedFeedback.OutputChange -= RoomIsOccupiedFeedback_OutputChange;
+ RoomOccupancy.RoomIsOccupiedFeedback.OutputChange += RoomIsOccupiedFeedback_OutputChange;
+
+ Debug.Console(0, this, "Room Occupancy set to device: '{0}'", (statusProvider as Device).Key);
+ }
+
+ void OnRoomOccupancyIsSet()
+ {
+ var handler = RoomOccupancyIsSet;
+ if (handler != null)
+ handler(this, new EventArgs());
+ }
+
+ ///
+ /// To allow base class to power room on to last source
+ ///
+ public abstract void PowerOnToDefaultOrLastSource();
+
+ ///
+ /// To allow base class to power room on to default source
+ ///
+ ///
+ public abstract bool RunDefaultPresentRoute();
+
+ void RoomIsOccupiedFeedback_OutputChange(object sender, EventArgs e)
+ {
+ if (RoomOccupancy.RoomIsOccupiedFeedback.BoolValue == false)
+ {
+ Debug.Console(1, this, "Notice: Vacancy Detected");
+ // Trigger the timer when the room is vacant
+ StartRoomVacancyTimer(eVacancyMode.InInitialVacancy);
+ }
+ else
+ {
+ Debug.Console(1, this, "Notice: Occupancy Detected");
+ // Reset the timer when the room is occupied
+ RoomVacancyShutdownTimer.Cancel();
+ }
+ }
+
+ ///
+ /// Executes when RoomVacancyShutdownTimer expires. Used to trigger specific room actions as needed. Must nullify the timer object when executed
+ ///
+ ///
+ public abstract void RoomVacatedForTimeoutPeriod(object o);
+ }
+
+ ///
+ /// To describe the various ways a room may be shutting down
+ ///
+ public enum eShutdownType
+ {
+ None = 0,
+ External,
+ Manual,
+ Vacancy
+ }
+
+ public enum eVacancyMode
+ {
+ None = 0,
+ InInitialVacancy,
+ InShutdownWarning
+ }
+
+ ///
+ ///
+ ///
+ public enum eWarmingCoolingMode
+ {
+ None,
+ Warming,
+ Cooling
+ }
+
+ public abstract class EssentialsRoomEmergencyBase : IKeyed
+ {
+ public string Key { get; private set; }
+
+ public EssentialsRoomEmergencyBase(string key)
+ {
+ Key = key;
+ }
+ }
}
\ No newline at end of file
diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Interfaces.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Interfaces.cs
new file mode 100644
index 00000000..36f390c3
--- /dev/null
+++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Interfaces.cs
@@ -0,0 +1,59 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Crestron.SimplSharp;
+
+namespace PepperDash.Essentials.Core
+{
+ ///
+ /// For rooms with in call feedback
+ ///
+ public interface IHasInCallFeedback
+ {
+ BoolFeedback InCallFeedback { get; }
+ }
+
+ ///
+ /// For rooms with a single display
+ ///
+ public interface IHasDefaultDisplay
+ {
+ IRoutingSinkWithSwitching DefaultDisplay { get; }
+ }
+
+ ///
+ /// For rooms with multiple displays
+ ///
+ public interface IHasMultipleDisplays
+ {
+ Dictionary Displays { get; }
+ }
+
+ ///
+ /// For rooms with routing
+ ///
+ public interface IRunRouteAction
+ {
+ void RunRouteAction(string routeKey);
+
+ void RunRouteAction(string routeKey, Action successCallback);
+ }
+
+ ///
+ /// For rooms that default presentation only routing
+ ///
+ public interface IRunDefaultPresentRoute
+ {
+ bool RunDefaultPresentRoute();
+ }
+
+ ///
+ /// For rooms that have default presentation and calling routes
+ ///
+ public interface IRunDefaultCallRoute : IRunDefaultPresentRoute
+ {
+ bool RunDefaultCallRoute();
+ }
+
+}
\ No newline at end of file
diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/MOVED RoomEventArgs.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/MOVED RoomEventArgs.cs
deleted file mode 100644
index bca9bb62..00000000
--- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/MOVED RoomEventArgs.cs
+++ /dev/null
@@ -1,41 +0,0 @@
-//using System;
-//using System.Collections.Generic;
-//using System.Linq;
-//using System.Text;
-//using Crestron.SimplSharp;
-
-//namespace PepperDash.Essentials.Core
-//{
-// public class EssentialsRoomSourceChangeEventArgs : EventArgs
-// {
-// public EssentialsRoom Room { get; private set; }
-// public IPresentationSource OldSource { get; private set; }
-// public IPresentationSource NewSource { get; private set; }
-
-// public EssentialsRoomSourceChangeEventArgs(EssentialsRoom room,
-// IPresentationSource oldSource, IPresentationSource newSource)
-// {
-// Room = room;
-// OldSource = oldSource;
-// NewSource = newSource;
-// }
-// }
-
-
-
-// public class EssentialsRoomAudioDeviceChangeEventArgs : EventArgs
-// {
-// public EssentialsRoom Room { get; private set; }
-// public IBasicVolumeControls OldDevice { get; private set; }
-// public IBasicVolumeControls NewDevice { get; private set; }
-
-// public EssentialsRoomAudioDeviceChangeEventArgs(EssentialsRoom room,
-// IBasicVolumeControls oldDevice, IBasicVolumeControls newDevice)
-// {
-// Room = room;
-// OldDevice = oldDevice;
-// NewDevice = newDevice;
-// }
-// }
-
-//}
\ No newline at end of file
diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Occupancy/iOccupancyStatusProvider.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/iOccupancyStatusProvider.cs
similarity index 82%
rename from essentials-framework/Essentials Devices Common/Essentials Devices Common/Occupancy/iOccupancyStatusProvider.cs
rename to essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/iOccupancyStatusProvider.cs
index 02054535..f46f10b1 100644
--- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Occupancy/iOccupancyStatusProvider.cs
+++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/iOccupancyStatusProvider.cs
@@ -6,7 +6,7 @@ using Crestron.SimplSharp;
using PepperDash.Essentials.Core;
-namespace PepperDash.Essentials.Devices.Common.Occupancy
+namespace PepperDash.Essentials.Core
{
public interface IOccupancyStatusProvider
{
diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Routing/RoutingInterfaces.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Routing/RoutingInterfaces.cs
index 76745f22..0c642d5c 100644
--- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Routing/RoutingInterfaces.cs
+++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Routing/RoutingInterfaces.cs
@@ -11,9 +11,25 @@ using PepperDash.Core;
namespace PepperDash.Essentials.Core
{
+
+ ///
+ /// The handler type for a Room's SourceInfoChange
+ ///
+ public delegate void SourceInfoChangeHandler(/*EssentialsRoomBase room,*/ SourceListItem info, ChangeType type);
+
+
//*******************************************************************************************
// Interfaces
+ ///
+ /// For rooms with a single presentation source, change event
+ ///
+ public interface IHasCurrentSourceInfoChange
+ {
+ string CurrentSourceInfoKey { get; set; }
+ SourceListItem CurrentSourceInfo { get; set; }
+ event SourceInfoChangeHandler CurrentSourceChange;
+ }
///
/// Defines a class that has a collection of RoutingInputPorts
@@ -35,7 +51,7 @@ namespace PepperDash.Essentials.Core
///
/// For fixed-source endpoint devices
///
- public interface IRoutingSinkNoSwitching : IRoutingInputs
+ public interface IRoutingSinkNoSwitching : IRoutingInputs, IHasCurrentSourceInfoChange
{
}
@@ -43,7 +59,7 @@ namespace PepperDash.Essentials.Core
///
/// Endpoint device like a display, that selects inputs
///
- public interface IRoutingSinkWithSwitching : IRoutingSinkNoSwitching
+ public interface IRoutingSinkWithSwitching : IRoutingSinkNoSwitching, IHasCurrentSourceInfoChange
{
//void ClearRoute();
void ExecuteSwitch(object inputSelector);
diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Touchpanels/Interfaces.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Touchpanels/Interfaces.cs
new file mode 100644
index 00000000..946f4f9d
--- /dev/null
+++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Touchpanels/Interfaces.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Crestron.SimplSharp;
+using Crestron.SimplSharpPro.DeviceSupport;
+
+namespace PepperDash.Essentials.Core
+{
+ public interface IHasBasicTriListWithSmartObject
+ {
+ BasicTriListWithSmartObject Panel { get; }
+ }
+}
\ No newline at end of file
diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Audio/GenericAudioOut.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Audio/GenericAudioOut.cs
index d9ba06cf..d89a36fc 100644
--- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Audio/GenericAudioOut.cs
+++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Audio/GenericAudioOut.cs
@@ -15,6 +15,32 @@ namespace PepperDash.Essentials.Devices.Common
///
public class GenericAudioOut : Device, IRoutingSinkNoSwitching
{
+ public event SourceInfoChangeHandler CurrentSourceChange;
+
+ public string CurrentSourceInfoKey { get; set; }
+ public SourceListItem CurrentSourceInfo
+ {
+ get
+ {
+ return _CurrentSourceInfo;
+ }
+ set
+ {
+ if (value == _CurrentSourceInfo) return;
+
+ var handler = CurrentSourceChange;
+
+ if (handler != null)
+ handler(_CurrentSourceInfo, ChangeType.WillChange);
+
+ _CurrentSourceInfo = value;
+
+ if (handler != null)
+ handler(_CurrentSourceInfo, ChangeType.DidChange);
+ }
+ }
+ SourceListItem _CurrentSourceInfo;
+
public RoutingInputPort AnyAudioIn { get; private set; }
public GenericAudioOut(string key, string name)
diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Essentials Devices Common.csproj b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Essentials Devices Common.csproj
index ec276366..bed83feb 100644
--- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Essentials Devices Common.csproj
+++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Essentials Devices Common.csproj
@@ -150,13 +150,8 @@
-
-
-
-
-
diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Factory/DeviceFactory.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Factory/DeviceFactory.cs
index 6c2a86f3..671dec12 100644
--- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Factory/DeviceFactory.cs
+++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Factory/DeviceFactory.cs
@@ -136,12 +136,12 @@ namespace PepperDash.Essentials.Devices.Common
else if (typeName == "inroompc")
{
- return new InRoomPc(key, name);
+ return new Core.Devices.InRoomPc(key, name);
}
else if (typeName == "laptop")
{
- return new Laptop(key, name);
+ return new Core.Devices.Laptop(key, name);
}
else if (typeName == "mockvc")
@@ -299,9 +299,9 @@ namespace PepperDash.Essentials.Devices.Common
else if (typeName == "microphoneprivacycontroller")
{
- var props = JsonConvert.DeserializeObject(properties.ToString());
+ var props = JsonConvert.DeserializeObject(properties.ToString());
- return new Microphones.MicrophonePrivacyController(key, props);
+ return new Core.Privacy.MicrophonePrivacyController(key, props);
}
else if (typeName == "roku")
{