diff --git a/PepperDashEssentials/ControlSystem.cs b/PepperDashEssentials/ControlSystem.cs
index 84018a15..b454cec9 100644
--- a/PepperDashEssentials/ControlSystem.cs
+++ b/PepperDashEssentials/ControlSystem.cs
@@ -28,6 +28,7 @@ namespace PepperDash.Essentials
HttpLogoServer LogoServer;
private CTimer _startTimer;
+ private CEvent _initializeEvent;
private const long StartupTime = 500;
public ControlSystem()
@@ -46,6 +47,24 @@ namespace PepperDash.Essentials
public override void InitializeSystem()
{
_startTimer = new CTimer(StartSystem,StartupTime);
+
+
+ // If the control system is a DMPS type, we need to wait to exit this method until all devices have had time to activate
+ // to allow any HD-BaseT DM endpoints to register first.
+ if (Global.ControlSystemIsDmpsType)
+ {
+ Debug.Console(2, "******************* InitializeSystem() Entering **********************");
+
+ _initializeEvent = new CEvent();
+
+ DeviceManager.AllDevicesActivated += (o, a) =>
+ {
+ _initializeEvent.Set();
+ Debug.Console(2, "******************* InitializeSystem() Exiting **********************");
+ };
+
+ _initializeEvent.Wait(30000);
+ }
}
private void StartSystem(object obj)
@@ -361,9 +380,7 @@ namespace PepperDash.Essentials
if(propertiesConfig == null)
propertiesConfig = new DM.Config.DmpsRoutingPropertiesConfig();
- var dmpsRoutingController = DmpsRoutingController.GetDmpsRoutingController("processor-avRouting", this.ControllerPrompt, propertiesConfig);
-
- DeviceManager.AddDevice(dmpsRoutingController);
+ DeviceManager.AddDevice(DmpsRoutingController.GetDmpsRoutingController("processor-avRouting", this.ControllerPrompt, propertiesConfig));
}
else if (this.ControllerPrompt.IndexOf("mpc3", StringComparison.OrdinalIgnoreCase) > -1)
{
@@ -450,14 +467,13 @@ namespace PepperDash.Essentials
return;
}
+ uint fusionIpId = 0xf1;
+
foreach (var roomConfig in ConfigReader.ConfigObject.Rooms)
{
var room = EssentialsRoomConfigHelper.GetRoomObject(roomConfig) as IEssentialsRoom;
if (room != null)
{
- // default IPID
- uint fusionIpId = 0xf1;
-
// default to no join map key
string fusionJoinMapKey = string.Empty;
@@ -478,7 +494,7 @@ namespace PepperDash.Essentials
{
DeviceManager.AddDevice(room);
- Debug.Console(0, Debug.ErrorLogLevel.Notice, "Room is EssentialsHuddleSpaceRoom, attempting to add to DeviceManager with Fusion");
+ Debug.Console(0, Debug.ErrorLogLevel.Notice, "Room is EssentialsHuddleSpaceRoom, attempting to add to DeviceManager with Fusion with IP-ID {0:X2}", fusionIpId);
DeviceManager.AddDevice(new Core.Fusion.EssentialsHuddleSpaceFusionSystemControllerBase(room, fusionIpId, fusionJoinMapKey));
@@ -490,7 +506,7 @@ namespace PepperDash.Essentials
{
DeviceManager.AddDevice(room);
- Debug.Console(0, Debug.ErrorLogLevel.Notice, "Room is EssentialsHuddleVtc1Room, attempting to add to DeviceManager with Fusion");
+ Debug.Console(0, Debug.ErrorLogLevel.Notice, "Room is EssentialsHuddleVtc1Room, attempting to add to DeviceManager with Fusion with IP-ID {0:X2}", fusionIpId);
DeviceManager.AddDevice(new EssentialsHuddleVtc1FusionController((IEssentialsHuddleVtc1Room)room, fusionIpId, fusionJoinMapKey));
Debug.Console(0, Debug.ErrorLogLevel.Notice, "Attempting to build Mobile Control Bridge...");
@@ -502,7 +518,7 @@ namespace PepperDash.Essentials
DeviceManager.AddDevice(room);
Debug.Console(0, Debug.ErrorLogLevel.Notice,
- "Room is EssentialsTechRoom, Attempting to add to DeviceManager with Fusion");
+ "Room is EssentialsTechRoom, Attempting to add to DeviceManager with Fusion with IP-ID {0:X2}", fusionIpId);
DeviceManager.AddDevice(new EssentialsTechRoomFusionSystemController((EssentialsTechRoom)room, fusionIpId, fusionJoinMapKey));
Debug.Console(0, Debug.ErrorLogLevel.Notice, "Attempting to build Mobile Control Bridge");
@@ -515,9 +531,13 @@ namespace PepperDash.Essentials
DeviceManager.AddDevice(room);
}
+ fusionIpId += 1;
}
else
+ {
Debug.Console(0, Debug.ErrorLogLevel.Notice, "Notice: Cannot create room from config, key '{0}' - Is this intentional? This may be a valid configuration.", roomConfig.Key);
+
+ }
}
Debug.Console(0, Debug.ErrorLogLevel.Notice, "All Rooms Loaded.");
diff --git a/PepperDashEssentials/Fusion/EssentialsHuddleVtc1FusionController.cs b/PepperDashEssentials/Fusion/EssentialsHuddleVtc1FusionController.cs
index 54d936f0..c3877706 100644
--- a/PepperDashEssentials/Fusion/EssentialsHuddleVtc1FusionController.cs
+++ b/PepperDashEssentials/Fusion/EssentialsHuddleVtc1FusionController.cs
@@ -150,7 +150,7 @@ namespace PepperDash.Essentials.Fusion
protected override void CreateSymbolAndBasicSigs(uint ipId)
{
- Debug.Console(1, this, "Creating Fusion Room symbol with GUID: {0}", RoomGuid);
+ Debug.Console(0, this, "Creating Fusion Room symbol with GUID: {0} and IP-ID {1:X2}", RoomGuid, ipId);
FusionRoom = new FusionRoom(ipId, Global.ControlSystem, Room.Name, RoomGuid);
FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.Use();
diff --git a/PepperDashEssentials/PepperDashEssentials.csproj b/PepperDashEssentials/PepperDashEssentials.csproj
index 14bd450a..d172076a 100644
--- a/PepperDashEssentials/PepperDashEssentials.csproj
+++ b/PepperDashEssentials/PepperDashEssentials.csproj
@@ -149,7 +149,8 @@
-
+
+
diff --git a/PepperDashEssentials/Room/Types/Interfaces/IEssentialsHuddleSpaceRoom.cs b/PepperDashEssentials/Room/Types/Interfaces/IEssentialsHuddleSpaceRoom.cs
new file mode 100644
index 00000000..c7f68e8b
--- /dev/null
+++ b/PepperDashEssentials/Room/Types/Interfaces/IEssentialsHuddleSpaceRoom.cs
@@ -0,0 +1,24 @@
+using System;
+
+using PepperDash.Essentials.Core;
+using PepperDash.Essentials.Room.Config;
+
+
+
+namespace PepperDash.Essentials
+{
+ public interface IEssentialsHuddleSpaceRoom : IEssentialsRoom, IHasCurrentSourceInfoChange, IRunRouteAction, IRunDefaultPresentRoute, IHasDefaultDisplay
+ {
+ bool ExcludeFromGlobalFunctions { get; }
+
+ void RunRouteAction(string routeKey);
+
+ EssentialsHuddleRoomPropertiesConfig PropertiesConfig { get; }
+
+ IBasicVolumeControls CurrentVolumeControls { get; }
+
+ event EventHandler CurrentVolumeDeviceChange;
+ }
+
+
+}
\ No newline at end of file
diff --git a/PepperDashEssentials/Room/Types/Interfaces/IEssentialsHuddleVtc1Room.cs b/PepperDashEssentials/Room/Types/Interfaces/IEssentialsHuddleVtc1Room.cs
new file mode 100644
index 00000000..5a6e9f59
--- /dev/null
+++ b/PepperDashEssentials/Room/Types/Interfaces/IEssentialsHuddleVtc1Room.cs
@@ -0,0 +1,25 @@
+
+using PepperDash.Essentials.Core;
+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 interface IEssentialsHuddleVtc1Room : IEssentialsRoom, IHasCurrentSourceInfoChange,
+ IPrivacy, IHasCurrentVolumeControls, IRunRouteAction, IRunDefaultCallRoute, IHasVideoCodec, IHasAudioCodec, IHasDefaultDisplay, IHasInCallFeedback
+ {
+ EssentialsHuddleVtc1PropertiesConfig PropertiesConfig { get; }
+
+ void RunRouteAction(string routeKey);
+
+ IHasScheduleAwareness ScheduleSource { get; }
+
+ new BoolFeedback InCallFeedback { get; }
+
+ new BoolFeedback PrivacyModeIsOnFeedback { get; }
+
+ string DefaultCodecRouteString { get; }
+ }
+}
\ No newline at end of file
diff --git a/PepperDashEssentials/UIDrivers/EssentialsHuddle/EssentialsHuddleTechPageDriver.cs b/PepperDashEssentials/UIDrivers/EssentialsHuddle/EssentialsHuddleTechPageDriver.cs
index 32d8ebe4..57aed03e 100644
--- a/PepperDashEssentials/UIDrivers/EssentialsHuddle/EssentialsHuddleTechPageDriver.cs
+++ b/PepperDashEssentials/UIDrivers/EssentialsHuddle/EssentialsHuddleTechPageDriver.cs
@@ -1,326 +1,326 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Text.RegularExpressions;
-using Crestron.SimplSharp;
-using Crestron.SimplSharpPro.DeviceSupport;
-
-using PepperDash.Core;
-using PepperDash.Essentials;
-using PepperDash.Essentials.Core;
-using PepperDash.Essentials.Core.Config;
-using PepperDash.Essentials.Core.SmartObjects;
-using PepperDash.Essentials.Core.Touchpanels.Keyboards;
-using PepperDash.Essentials.Devices.Displays;
-using PepperDash.Essentials.Room.Config;
-
-namespace PepperDash.Essentials.UIDrivers
-{
- public class EssentialsHuddleTechPageDriver : PanelDriverBase
- {
- ///
- ///
- ///
- SmartObjectDynamicList MenuList;
- ///
- ///
- ///
- SubpageReferenceList StatusList;
- ///
- /// The list of display controls
- ///
- SubpageReferenceList DisplayList;
- ///
- /// References lines in the list against device instances
- ///
- Dictionary StatusListDeviceIndexes;
- ///
- ///
- ///
- JoinedSigInterlock PagesInterlock;
-
- ///
- /// 1
- ///
- public const uint JoinText = 1;
-
- CTimer PinAuthorizedTimer;
-
- EssentialsRoomTechConfig Config;
-
- StringBuilder PinEntryBuilder = new StringBuilder(4);
-
- bool IsAuthorized;
-
- SmartObjectNumeric PinKeypad;
-
- ///
- ///
- ///
- ///
- ///
- public EssentialsHuddleTechPageDriver(BasicTriListWithSmartObject trilist, EssentialsRoomTechConfig config)
- : base(trilist)
- {
- Config = config;
-
- PagesInterlock = new JoinedSigInterlock(trilist);
- PagesInterlock.SetButDontShow(UIBoolJoin.TechSystemStatusVisible);
-
- trilist.SetSigFalseAction(UIBoolJoin.TechExitButton, Hide);
-
- MenuList = new SmartObjectDynamicList(trilist.SmartObjects[UISmartObjectJoin.TechMenuList],
- true, 3100);
-
- MenuList.SetFeedback(1, true); // initial fb
- ushort count = 0;
-
- MenuList.SetItemMainText(1, "System Status");
- MenuList.SetItemButtonAction(1, b => {
- if (b) PagesInterlock.ShowInterlocked(UIBoolJoin.TechSystemStatusVisible);
- MenuList.SetFeedback(1, true);
- });
-
- MenuList.SetItemMainText(2, "Display Controls");
- MenuList.SetItemButtonAction(2, b => {
- if (b) PagesInterlock.ShowInterlocked(UIBoolJoin.TechDisplayControlsVisible);
- MenuList.SetFeedback(2, true);
- });
-
- count = 2;
-
- // Don't show panel setup on iPad or xpanel
- if (TriList is Crestron.SimplSharpPro.DeviceSupport.TswFt5Button)
- {
- count++;
- MenuList.SetItemMainText(count, "Panel Setup");
- MenuList.SetItemButtonAction(count, b =>
- {
- if (b) PagesInterlock.ShowInterlocked(UIBoolJoin.TechPanelSetupVisible);
- MenuList.SetFeedback(count, true);
- });
- }
-
- MenuList.Count = count;
- BuildStatusList();
- BuildDisplayList();
- SetupPinModal();
- }
-
- ///
- ///
- ///
- public override void Show()
- {
- // divert to PIN if we need auth
- if (IsAuthorized)
- {
- // Cancel the auth timer so we don't deauth after coming back in
- if (PinAuthorizedTimer != null)
- PinAuthorizedTimer.Stop();
-
- TriList.SetBool(UIBoolJoin.TechCommonItemsVisbible, true);
- PagesInterlock.Show();
- base.Show();
- }
- else
- {
- TriList.SetBool(UIBoolJoin.PinDialog4DigitVisible, true);
- }
- }
-
- ///
- ///
- ///
- public override void Hide()
- {
- // Leave it authorized for 60 seconds.
- if (IsAuthorized)
- PinAuthorizedTimer = new CTimer(o => {
- IsAuthorized = false;
- PinAuthorizedTimer = null;
- }, 60000);
- TriList.SetBool(UIBoolJoin.TechCommonItemsVisbible, false);
- PagesInterlock.Hide();
- base.Hide();
- }
-
- ///
- /// Wire up the keypad and buttons
- ///
- void SetupPinModal()
- {
- TriList.SetSigFalseAction(UIBoolJoin.PinDialogCancelPress, CancelPinDialog);
- PinKeypad = new SmartObjectNumeric(TriList.SmartObjects[UISmartObjectJoin.TechPinDialogKeypad], true);
- PinKeypad.Digit0.UserObject = new Action(b => { if (b)DialPinDigit('0'); });
- PinKeypad.Digit1.UserObject = new Action(b => { if (b)DialPinDigit('1'); });
- PinKeypad.Digit2.UserObject = new Action(b => { if (b)DialPinDigit('2'); });
- PinKeypad.Digit3.UserObject = new Action(b => { if (b)DialPinDigit('3'); });
- PinKeypad.Digit4.UserObject = new Action(b => { if (b)DialPinDigit('4'); });
- PinKeypad.Digit5.UserObject = new Action(b => { if (b)DialPinDigit('5'); });
- PinKeypad.Digit6.UserObject = new Action(b => { if (b)DialPinDigit('6'); });
- PinKeypad.Digit7.UserObject = new Action(b => { if (b)DialPinDigit('7'); });
- PinKeypad.Digit8.UserObject = new Action(b => { if (b)DialPinDigit('8'); });
- PinKeypad.Digit9.UserObject = new Action(b => { if (b)DialPinDigit('9'); });
- }
-
- ///
- ///
- ///
- ///
- void DialPinDigit(char d)
- {
- PinEntryBuilder.Append(d);
- var len = PinEntryBuilder.Length;
- SetPinDotsFeedback(len);
-
- // check it!
- if (len == 4)
- {
- if (Config.Password == PinEntryBuilder.ToString())
- {
- IsAuthorized = true;
- SetPinDotsFeedback(0);
- TriList.SetBool(UIBoolJoin.PinDialog4DigitVisible, false);
- Show();
- }
- else
- {
- SetPinDotsFeedback(0);
- TriList.SetBool(UIBoolJoin.PinDialogErrorVisible, true);
- new CTimer(o =>
- {
- TriList.SetBool(UIBoolJoin.PinDialogErrorVisible, false);
- }, 1500);
- }
-
- PinEntryBuilder.Remove(0, len); // clear it either way
- }
- }
-
- ///
- /// Draws the dots as pin is entered
- ///
- ///
- void SetPinDotsFeedback(int len)
- {
- TriList.SetBool(UIBoolJoin.PinDialogDot1, len >= 1);
- TriList.SetBool(UIBoolJoin.PinDialogDot2, len >= 2);
- TriList.SetBool(UIBoolJoin.PinDialogDot3, len >= 3);
- TriList.SetBool(UIBoolJoin.PinDialogDot4, len == 4);
-
- }
-
- ///
- /// Does what it says
- ///
- void CancelPinDialog()
- {
- PinEntryBuilder.Remove(0, PinEntryBuilder.Length);
- TriList.SetBool(UIBoolJoin.PinDialog4DigitVisible, false);
- }
-
-
- ///
- ///
- ///
- void BuildStatusList()
- {
- StatusList = new SubpageReferenceList(TriList, UISmartObjectJoin.TechStatusList, 3, 3, 3);
- StatusListDeviceIndexes = new Dictionary();
- uint i = 0;
- foreach (var d in DeviceManager.AllDevices)
- {
- // make sure it is both ICommunicationMonitor and a Device
- var sd = d as ICommunicationMonitor;
- if (sd == null)
- continue;
- var dd = sd as Device;
- if(dd == null)
- continue;
- i++;
- StatusList.StringInputSig(i, 1).StringValue = dd.Name;
- StatusList.UShortInputSig(i, 1).UShortValue = (ushort)sd.CommunicationMonitor.Status;
- StatusListDeviceIndexes.Add(sd, i);
- sd.CommunicationMonitor.StatusChange += CommunicationMonitor_StatusChange ;
- }
- StatusList.Count = (ushort)i;
- }
-
- ///
- /// Builds the list of display controls
- ///
- void BuildDisplayList()
- {
- DisplayList = new SubpageReferenceList(TriList, UISmartObjectJoin.TechDisplayControlsList, 10, 3, 3);
-
- var devKeys = ConfigReader.ConfigObject.Devices.Where(d =>
- d.Group.Equals("display", StringComparison.OrdinalIgnoreCase)
- || d.Group.Equals("projector", StringComparison.OrdinalIgnoreCase))
- .Select(dd => dd.Key);
- var disps = DeviceManager.AllDevices.Where(d =>
- devKeys.Contains(d.Key));
- ushort i = 0;
- foreach (var disp in disps)
- {
- var display = disp as DisplayBase;
- if (display != null)
- {
- i++;
- DisplayList.StringInputSig(i, 1).StringValue = display.Name;
- DisplayList.GetBoolFeedbackSig(i, 1).SetSigFalseAction(display.PowerOn);
- DisplayList.GetBoolFeedbackSig(i, 2).SetSigFalseAction(display.PowerOff);
- if (display is TwoWayDisplayBase)
- {
- var powerOnSig = DisplayList.BoolInputSig(i, 1);
- (display as TwoWayDisplayBase).PowerIsOnFeedback.LinkInputSig(powerOnSig);
-
- var powerOffSig = DisplayList.BoolInputSig(1, 2);
- (display as TwoWayDisplayBase).PowerIsOnFeedback.LinkComplementInputSig(powerOffSig);
- }
- DisplayList.GetBoolFeedbackSig(i, 3).SetSigFalseAction(() =>
- { if (display is IInputHdmi1) (display as IInputHdmi1).InputHdmi1(); });
- DisplayList.GetBoolFeedbackSig(i, 4).SetSigFalseAction(() =>
- { if (display is IInputHdmi2) (display as IInputHdmi2).InputHdmi2(); });
- DisplayList.GetBoolFeedbackSig(i, 5).SetSigFalseAction(() =>
- { if (display is IInputHdmi3) (display as IInputHdmi3).InputHdmi3(); });
- //DisplayList.GetBoolFeedbackSig(i, 6).SetSigFalseAction(() =>
- //{ if (display is IInputHdmi4) (display as IInputHdmi4).InputHdmi4(); });
- DisplayList.GetBoolFeedbackSig(i, 6).SetSigFalseAction(() =>
- { if (display is IInputDisplayPort1) (display as IInputDisplayPort1).InputDisplayPort1(); });
-
-
- // Figure out some way to provide current input feedback
- if (display is TwoWayDisplayBase)
- {
- (display as TwoWayDisplayBase).CurrentInputFeedback.OutputChange += CurrentInputFeedback_OutputChange;
- }
- }
-
-
- }
-
- DisplayList.Count = i;
- }
-
-
- void CurrentInputFeedback_OutputChange(object sender, EventArgs e)
- {
-
- }
-
- ///
- ///
- ///
- void CommunicationMonitor_StatusChange(object sender, MonitorStatusChangeEventArgs e)
- {
- var c = sender as ICommunicationMonitor;
- if (c != null && StatusListDeviceIndexes.ContainsKey(c))
- {
- var i = StatusListDeviceIndexes[c];
- StatusList.UShortInputSig(i, 1).UShortValue = (ushort)e.Status;
- }
- }
- }
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Text.RegularExpressions;
+using Crestron.SimplSharp;
+using Crestron.SimplSharpPro.DeviceSupport;
+
+using PepperDash.Core;
+using PepperDash.Essentials;
+using PepperDash.Essentials.Core;
+using PepperDash.Essentials.Core.Config;
+using PepperDash.Essentials.Core.SmartObjects;
+using PepperDash.Essentials.Core.Touchpanels.Keyboards;
+using PepperDash.Essentials.Devices.Displays;
+using PepperDash.Essentials.Room.Config;
+
+namespace PepperDash.Essentials.UIDrivers
+{
+ public class EssentialsHuddleTechPageDriver : PanelDriverBase
+ {
+ ///
+ ///
+ ///
+ SmartObjectDynamicList MenuList;
+ ///
+ ///
+ ///
+ SubpageReferenceList StatusList;
+ ///
+ /// The list of display controls
+ ///
+ SubpageReferenceList DisplayList;
+ ///
+ /// References lines in the list against device instances
+ ///
+ Dictionary StatusListDeviceIndexes;
+ ///
+ ///
+ ///
+ JoinedSigInterlock PagesInterlock;
+
+ ///
+ /// 1
+ ///
+ public const uint JoinText = 1;
+
+ CTimer PinAuthorizedTimer;
+
+ EssentialsRoomTechConfig Config;
+
+ StringBuilder PinEntryBuilder = new StringBuilder(4);
+
+ bool IsAuthorized;
+
+ SmartObjectNumeric PinKeypad;
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ public EssentialsHuddleTechPageDriver(BasicTriListWithSmartObject trilist, EssentialsRoomTechConfig config)
+ : base(trilist)
+ {
+ Config = config;
+
+ PagesInterlock = new JoinedSigInterlock(trilist);
+ PagesInterlock.SetButDontShow(UIBoolJoin.TechSystemStatusVisible);
+
+ trilist.SetSigFalseAction(UIBoolJoin.TechExitButton, Hide);
+
+ MenuList = new SmartObjectDynamicList(trilist.SmartObjects[UISmartObjectJoin.TechMenuList],
+ true, 3100);
+
+ MenuList.SetFeedback(1, true); // initial fb
+ ushort count = 0;
+
+ MenuList.SetItemMainText(1, "System Status");
+ MenuList.SetItemButtonAction(1, b => {
+ if (b) PagesInterlock.ShowInterlocked(UIBoolJoin.TechSystemStatusVisible);
+ MenuList.SetFeedback(1, true);
+ });
+
+ MenuList.SetItemMainText(2, "Display Controls");
+ MenuList.SetItemButtonAction(2, b => {
+ if (b) PagesInterlock.ShowInterlocked(UIBoolJoin.TechDisplayControlsVisible);
+ MenuList.SetFeedback(2, true);
+ });
+
+ count = 2;
+
+ // Don't show panel setup on iPad or xpanel
+ if (TriList is Crestron.SimplSharpPro.DeviceSupport.TswFt5Button)
+ {
+ count++;
+ MenuList.SetItemMainText(count, "Panel Setup");
+ MenuList.SetItemButtonAction(count, b =>
+ {
+ if (b) PagesInterlock.ShowInterlocked(UIBoolJoin.TechPanelSetupVisible);
+ MenuList.SetFeedback(count, true);
+ });
+ }
+
+ MenuList.Count = count;
+ BuildStatusList();
+ BuildDisplayList();
+ SetupPinModal();
+ }
+
+ ///
+ ///
+ ///
+ public override void Show()
+ {
+ // divert to PIN if we need auth
+ if (IsAuthorized)
+ {
+ // Cancel the auth timer so we don't deauth after coming back in
+ if (PinAuthorizedTimer != null)
+ PinAuthorizedTimer.Stop();
+
+ TriList.SetBool(UIBoolJoin.TechCommonItemsVisbible, true);
+ PagesInterlock.Show();
+ base.Show();
+ }
+ else
+ {
+ TriList.SetBool(UIBoolJoin.PinDialog4DigitVisible, true);
+ }
+ }
+
+ ///
+ ///
+ ///
+ public override void Hide()
+ {
+ // Leave it authorized for 60 seconds.
+ if (IsAuthorized)
+ PinAuthorizedTimer = new CTimer(o => {
+ IsAuthorized = false;
+ PinAuthorizedTimer = null;
+ }, 60000);
+ TriList.SetBool(UIBoolJoin.TechCommonItemsVisbible, false);
+ PagesInterlock.Hide();
+ base.Hide();
+ }
+
+ ///
+ /// Wire up the keypad and buttons
+ ///
+ void SetupPinModal()
+ {
+ TriList.SetSigFalseAction(UIBoolJoin.PinDialogCancelPress, CancelPinDialog);
+ PinKeypad = new SmartObjectNumeric(TriList.SmartObjects[UISmartObjectJoin.TechPinDialogKeypad], true);
+ PinKeypad.Digit0.UserObject = new Action(b => { if (b)DialPinDigit('0'); });
+ PinKeypad.Digit1.UserObject = new Action(b => { if (b)DialPinDigit('1'); });
+ PinKeypad.Digit2.UserObject = new Action(b => { if (b)DialPinDigit('2'); });
+ PinKeypad.Digit3.UserObject = new Action(b => { if (b)DialPinDigit('3'); });
+ PinKeypad.Digit4.UserObject = new Action(b => { if (b)DialPinDigit('4'); });
+ PinKeypad.Digit5.UserObject = new Action(b => { if (b)DialPinDigit('5'); });
+ PinKeypad.Digit6.UserObject = new Action(b => { if (b)DialPinDigit('6'); });
+ PinKeypad.Digit7.UserObject = new Action(b => { if (b)DialPinDigit('7'); });
+ PinKeypad.Digit8.UserObject = new Action(b => { if (b)DialPinDigit('8'); });
+ PinKeypad.Digit9.UserObject = new Action(b => { if (b)DialPinDigit('9'); });
+ }
+
+ ///
+ ///
+ ///
+ ///
+ void DialPinDigit(char d)
+ {
+ PinEntryBuilder.Append(d);
+ var len = PinEntryBuilder.Length;
+ SetPinDotsFeedback(len);
+
+ // check it!
+ if (len == 4)
+ {
+ if (Config.Password == PinEntryBuilder.ToString())
+ {
+ IsAuthorized = true;
+ SetPinDotsFeedback(0);
+ TriList.SetBool(UIBoolJoin.PinDialog4DigitVisible, false);
+ Show();
+ }
+ else
+ {
+ SetPinDotsFeedback(0);
+ TriList.SetBool(UIBoolJoin.PinDialogErrorVisible, true);
+ new CTimer(o =>
+ {
+ TriList.SetBool(UIBoolJoin.PinDialogErrorVisible, false);
+ }, 1500);
+ }
+
+ PinEntryBuilder.Remove(0, len); // clear it either way
+ }
+ }
+
+ ///
+ /// Draws the dots as pin is entered
+ ///
+ ///
+ void SetPinDotsFeedback(int len)
+ {
+ TriList.SetBool(UIBoolJoin.PinDialogDot1, len >= 1);
+ TriList.SetBool(UIBoolJoin.PinDialogDot2, len >= 2);
+ TriList.SetBool(UIBoolJoin.PinDialogDot3, len >= 3);
+ TriList.SetBool(UIBoolJoin.PinDialogDot4, len == 4);
+
+ }
+
+ ///
+ /// Does what it says
+ ///
+ void CancelPinDialog()
+ {
+ PinEntryBuilder.Remove(0, PinEntryBuilder.Length);
+ TriList.SetBool(UIBoolJoin.PinDialog4DigitVisible, false);
+ }
+
+
+ ///
+ ///
+ ///
+ void BuildStatusList()
+ {
+ StatusList = new SubpageReferenceList(TriList, UISmartObjectJoin.TechStatusList, 3, 3, 3);
+ StatusListDeviceIndexes = new Dictionary();
+ uint i = 0;
+ foreach (var d in DeviceManager.AllDevices)
+ {
+ // make sure it is both ICommunicationMonitor and a Device
+ var sd = d as ICommunicationMonitor;
+ if (sd == null)
+ continue;
+ var dd = sd as Device;
+ if(dd == null)
+ continue;
+ i++;
+ StatusList.StringInputSig(i, 1).StringValue = dd.Name;
+ StatusList.UShortInputSig(i, 1).UShortValue = (ushort)sd.CommunicationMonitor.Status;
+ StatusListDeviceIndexes.Add(sd, i);
+ sd.CommunicationMonitor.StatusChange += CommunicationMonitor_StatusChange ;
+ }
+ StatusList.Count = (ushort)i;
+ }
+
+ ///
+ /// Builds the list of display controls
+ ///
+ void BuildDisplayList()
+ {
+ DisplayList = new SubpageReferenceList(TriList, UISmartObjectJoin.TechDisplayControlsList, 10, 3, 3);
+
+ var devKeys = ConfigReader.ConfigObject.Devices.Where(d =>
+ d.Group.Equals("display", StringComparison.OrdinalIgnoreCase)
+ || d.Group.Equals("projector", StringComparison.OrdinalIgnoreCase))
+ .Select(dd => dd.Key);
+ var disps = DeviceManager.AllDevices.Where(d =>
+ devKeys.Contains(d.Key));
+ ushort i = 0;
+ foreach (var disp in disps)
+ {
+ var display = disp as DisplayBase;
+ if (display != null)
+ {
+ i++;
+ DisplayList.StringInputSig(i, 1).StringValue = display.Name;
+ DisplayList.GetBoolFeedbackSig(i, 1).SetSigFalseAction(display.PowerOn);
+ DisplayList.GetBoolFeedbackSig(i, 2).SetSigFalseAction(display.PowerOff);
+ if (display is TwoWayDisplayBase)
+ {
+ var powerOnSig = DisplayList.BoolInputSig(i, 1);
+ (display as TwoWayDisplayBase).PowerIsOnFeedback.LinkInputSig(powerOnSig);
+
+ var powerOffSig = DisplayList.BoolInputSig(1, 2);
+ (display as TwoWayDisplayBase).PowerIsOnFeedback.LinkComplementInputSig(powerOffSig);
+ }
+ DisplayList.GetBoolFeedbackSig(i, 3).SetSigFalseAction(() =>
+ { if (display is IInputHdmi1) (display as IInputHdmi1).InputHdmi1(); });
+ DisplayList.GetBoolFeedbackSig(i, 4).SetSigFalseAction(() =>
+ { if (display is IInputHdmi2) (display as IInputHdmi2).InputHdmi2(); });
+ DisplayList.GetBoolFeedbackSig(i, 5).SetSigFalseAction(() =>
+ { if (display is IInputHdmi3) (display as IInputHdmi3).InputHdmi3(); });
+ //DisplayList.GetBoolFeedbackSig(i, 6).SetSigFalseAction(() =>
+ //{ if (display is IInputHdmi4) (display as IInputHdmi4).InputHdmi4(); });
+ DisplayList.GetBoolFeedbackSig(i, 6).SetSigFalseAction(() =>
+ { if (display is IInputDisplayPort1) (display as IInputDisplayPort1).InputDisplayPort1(); });
+
+
+ // Figure out some way to provide current input feedback
+ if (display is TwoWayDisplayBase)
+ {
+ (display as TwoWayDisplayBase).CurrentInputFeedback.OutputChange += CurrentInputFeedback_OutputChange;
+ }
+ }
+
+
+ }
+
+ DisplayList.Count = i;
+ }
+
+
+ void CurrentInputFeedback_OutputChange(object sender, EventArgs e)
+ {
+
+ }
+
+ ///
+ ///
+ ///
+ void CommunicationMonitor_StatusChange(object sender, MonitorStatusChangeEventArgs e)
+ {
+ var c = sender as ICommunicationMonitor;
+ if (c != null && StatusListDeviceIndexes.ContainsKey(c))
+ {
+ var i = StatusListDeviceIndexes[c];
+ StatusList.UShortInputSig(i, 1).UShortValue = (ushort)e.Status;
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/CenOdtOccupancySensorBaseJoinMap.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/CenOdtOccupancySensorBaseJoinMap.cs
index 4e731f41..63684837 100644
--- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/CenOdtOccupancySensorBaseJoinMap.cs
+++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/CenOdtOccupancySensorBaseJoinMap.cs
@@ -46,6 +46,14 @@ namespace PepperDash.Essentials.Core.Bridges
public JoinDataComplete RawOccupancyUsFeedback = new JoinDataComplete(new JoinData { JoinNumber = 7, JoinSpan = 1 },
new JoinMetadata { Description = "Raw Occupancy Us Feedback", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+ [JoinName("IdentityModeOn")]
+ public JoinDataComplete IdentityMode = new JoinDataComplete(new JoinData { JoinNumber = 8, JoinSpan = 1 },
+ new JoinMetadata { Description = "Enable Identity Mode", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ [JoinName("IdentityModeFeedback")]
+ public JoinDataComplete IdentityModeFeedback = new JoinDataComplete(new JoinData { JoinNumber = 8, JoinSpan = 1 },
+ new JoinMetadata { Description = "Identity Mode Feedback", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
[JoinName("EnableLedFlash")]
public JoinDataComplete EnableLedFlash = new JoinDataComplete(new JoinData { JoinNumber = 11, JoinSpan = 1 },
new JoinMetadata { Description = "Enable Led Flash", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/DmChassisControllerJoinMap.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/DmChassisControllerJoinMap.cs
index ee04bd45..60973801 100644
--- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/DmChassisControllerJoinMap.cs
+++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/DmChassisControllerJoinMap.cs
@@ -10,7 +10,7 @@ namespace PepperDash.Essentials.Core.Bridges
new JoinMetadata
{
Description = "DM Chassis enable audio breakaway routing",
- JoinCapabilities = eJoinCapabilities.ToSIMPL,
+ JoinCapabilities = eJoinCapabilities.FromSIMPL,
JoinType = eJoinType.Digital
});
@@ -20,7 +20,7 @@ namespace PepperDash.Essentials.Core.Bridges
new JoinMetadata
{
Description = "DM Chassis enable USB breakaway routing",
- JoinCapabilities = eJoinCapabilities.ToSIMPL,
+ JoinCapabilities = eJoinCapabilities.FromSIMPL,
JoinType = eJoinType.Digital
});
diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/DmpsRoutingControllerJoinMap.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/DmpsRoutingControllerJoinMap.cs
index 11385916..80975338 100644
--- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/DmpsRoutingControllerJoinMap.cs
+++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/DmpsRoutingControllerJoinMap.cs
@@ -1,9 +1,17 @@
using System;
-namespace PepperDash.Essentials.Core.Bridges
-{
- public class DmpsRoutingControllerJoinMap : JoinMapBaseAdvanced
- {
+namespace PepperDash.Essentials.Core.Bridges
+{
+ public class DmpsRoutingControllerJoinMap : JoinMapBaseAdvanced
+ {
+ [JoinName("SystemPowerOn")]
+ public JoinDataComplete SystemPowerOn = new JoinDataComplete(new JoinData { JoinNumber = 12, JoinSpan = 1 },
+ new JoinMetadata { Description = "DMPS System Power On Get/Set", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
+
+ [JoinName("SystemPowerOff")]
+ public JoinDataComplete SystemPowerOff = new JoinDataComplete(new JoinData { JoinNumber = 13, JoinSpan = 1 },
+ new JoinMetadata { Description = "DMPS System Power Off Get/Set", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
+
[JoinName("VideoSyncStatus")]
public JoinDataComplete VideoSyncStatus = new JoinDataComplete(new JoinData { JoinNumber = 101, JoinSpan = 32 },
new JoinMetadata { Description = "DM Input Video Sync", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
@@ -61,5 +69,5 @@ namespace PepperDash.Essentials.Core.Bridges
protected DmpsRoutingControllerJoinMap(uint joinStart, Type type) : base(joinStart, type)
{
}
- }
+ }
}
\ No newline at end of file
diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/GlsPartitionSensorJoinMap.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/GlsPartitionSensorJoinMap.cs
index 3854a4fb..5a583d69 100644
--- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/GlsPartitionSensorJoinMap.cs
+++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/GlsPartitionSensorJoinMap.cs
@@ -5,6 +5,9 @@ namespace PepperDash.Essentials.Core.Bridges.JoinMaps
{
public class GlsPartitionSensorJoinMap : JoinMapBaseAdvanced
{
+
+ #region Digital
+
[JoinName("IsOnline")]
public JoinDataComplete IsOnline = new JoinDataComplete(
new JoinData
@@ -19,20 +22,7 @@ namespace PepperDash.Essentials.Core.Bridges.JoinMaps
JoinType = eJoinType.Digital
});
- [JoinName("Name")]
- public JoinDataComplete Name = new JoinDataComplete(
- new JoinData
- {
- JoinNumber = 1,
- JoinSpan = 1
- },
- new JoinMetadata
- {
- Description = "Sensor Name",
- JoinCapabilities = eJoinCapabilities.ToSIMPL,
- JoinType = eJoinType.Serial
- });
-
+
[JoinName("Enable")]
public JoinDataComplete Enable = new JoinDataComplete(
new JoinData
@@ -101,7 +91,11 @@ namespace PepperDash.Essentials.Core.Bridges.JoinMaps
Description = "Sensor Decrease Sensitivity",
JoinCapabilities = eJoinCapabilities.FromSIMPL,
JoinType = eJoinType.Digital
- });
+ });
+
+ #endregion
+
+ #region Analog
[JoinName("Sensitivity")]
public JoinDataComplete Sensitivity = new JoinDataComplete(
@@ -117,6 +111,28 @@ namespace PepperDash.Essentials.Core.Bridges.JoinMaps
JoinType = eJoinType.Analog
});
+ #endregion
+
+
+ #region Serial
+
+ [JoinName("Name")]
+ public JoinDataComplete Name = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 1,
+ JoinSpan = 1
+ },
+ new JoinMetadata
+ {
+ Description = "Sensor Name",
+ JoinCapabilities = eJoinCapabilities.ToSIMPL,
+ JoinType = eJoinType.Serial
+ });
+
+ #endregion
+
+
///
/// Constructor to use when instantiating this Join Map without inheriting from it
///
diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Crestron IO/Relay/CenIoRy104Controller.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Crestron IO/Relay/CenIoRy104Controller.cs
new file mode 100644
index 00000000..19ca8438
--- /dev/null
+++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Crestron IO/Relay/CenIoRy104Controller.cs
@@ -0,0 +1,77 @@
+using System.Collections.Generic;
+using Crestron.SimplSharpPro;
+using Crestron.SimplSharpPro.GeneralIO;
+using PepperDash.Core;
+using PepperDash.Essentials.Core.Config;
+
+namespace PepperDash.Essentials.Core
+{
+ ///
+ /// Wrapper class for CEN-IO-RY-104 relay module
+ ///
+ [Description("Wrapper class for the CEN-IO-RY-104 relay module")]
+ public class CenIoRy104Controller : EssentialsDevice, IRelayPorts
+ {
+ private readonly CenIoRy104 _ry104;
+
+ ///
+ /// Constructor
+ ///
+ ///
+ ///
+ ///
+ public CenIoRy104Controller(string key, string name, CenIoRy104 ry104)
+ : base(key, name)
+ {
+ _ry104 = ry104;
+ }
+
+ ///
+ /// Relay port collection
+ ///
+ public CrestronCollection RelayPorts
+ {
+ get { return _ry104.RelayPorts; }
+ }
+
+ ///
+ /// Number of relay ports property
+ ///
+ public int NumberOfRelayPorts
+ {
+ get { return _ry104.NumberOfRelayPorts; }
+ }
+ }
+
+ ///
+ /// CEN-IO-RY Controller factory
+ ///
+ public class CenIoRy104ControllerFactory : EssentialsDeviceFactory
+ {
+ ///
+ /// Constructor
+ ///
+ public CenIoRy104ControllerFactory()
+ {
+ TypeNames = new List() { "ceniory104" };
+ }
+
+ public override EssentialsDevice BuildDevice(DeviceConfig dc)
+ {
+ Debug.Console(1, "Factory Attempting to create a new CEN-IO-RY-104 Device");
+
+ var controlPropertiesConfig = CommFactory.GetControlPropertiesConfig(dc);
+ if (controlPropertiesConfig == null)
+ {
+ Debug.Console(1, "Factory failed to create a new CEN-IO-RY-104 Device");
+ return null;
+ }
+
+ var ipid = controlPropertiesConfig.IpIdInt;
+ if (ipid != 0) return new CenIoRy104Controller(dc.Key, dc.Name, new CenIoRy104(ipid, Global.ControlSystem));
+
+ Debug.Console(1, "Factory failed to create a new CEN-IO-RY-104 Device using IP-ID-{0}", ipid);
+ return null;
+ }
+ }
+}
\ No newline at end of file
diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Crestron/CrestronGenericBaseDevice.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Crestron/CrestronGenericBaseDevice.cs
index df2c3c56..b7534087 100644
--- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Crestron/CrestronGenericBaseDevice.cs
+++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Crestron/CrestronGenericBaseDevice.cs
@@ -69,19 +69,28 @@ namespace PepperDash.Essentials.Core
public override bool CustomActivate()
{
Debug.Console(0, this, "Activating");
- if (!PreventRegistration)
- {
+ if (!PreventRegistration)
+ {
//Debug.Console(1, this, " Does not require registration. Skipping");
- var response = Hardware.RegisterWithLogging(Key);
- if (response != eDeviceRegistrationUnRegistrationResponse.Success)
- {
- //Debug.Console(0, this, "ERROR: Cannot register Crestron device: {0}", response);
- return false;
- }
+ var response = Hardware.RegisterWithLogging(Key);
+ if (response != eDeviceRegistrationUnRegistrationResponse.Success)
+ {
+ //Debug.Console(0, this, "ERROR: Cannot register Crestron device: {0}", response);
+ return false;
+ }
IsRegistered.FireUpdate();
- }
+ }
+ else
+ {
+ AddPostActivationAction(() =>
+ {
+ var response = Hardware.RegisterWithLogging(Key);
+
+ IsRegistered.FireUpdate();
+ });
+ }
foreach (var f in Feedbacks)
{
diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/DeviceManager.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/DeviceManager.cs
index aac38dfa..df864550 100644
--- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/DeviceManager.cs
+++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/DeviceManager.cs
@@ -60,6 +60,7 @@ namespace PepperDash.Essentials.Core
DeviceCriticalSection.Enter();
AddDeviceEnabled = false;
// PreActivate all devices
+ Debug.Console(0,"****PreActivation starting...****");
foreach (var d in Devices.Values)
{
try
@@ -69,9 +70,12 @@ namespace PepperDash.Essentials.Core
}
catch (Exception e)
{
- Debug.Console(0, d, "ERROR: Device PreActivation failure:\r{0}", e);
+ Debug.Console(0, d, "ERROR: Device {1} PreActivation failure: {0}", e.Message, d.Key);
+ Debug.Console(1, d, "Stack Trace: {0}", e.StackTrace);
}
}
+ Debug.Console(0, "****PreActivation complete****");
+ Debug.Console(0, "****Activation starting...****");
// Activate all devices
foreach (var d in Devices.Values)
@@ -83,10 +87,14 @@ namespace PepperDash.Essentials.Core
}
catch (Exception e)
{
- Debug.Console(0, d, "ERROR: Device Activation failure:\r{0}", e);
+ Debug.Console(0, d, "ERROR: Device {1} Activation failure: {0}", e.Message, d.Key);
+ Debug.Console(1, d, "Stack Trace: {0}", e.StackTrace);
}
}
+ Debug.Console(0, "****Activation complete****");
+ Debug.Console(0, "****PostActivation starting...****");
+
// PostActivate all devices
foreach (var d in Devices.Values)
{
@@ -97,10 +105,13 @@ namespace PepperDash.Essentials.Core
}
catch (Exception e)
{
- Debug.Console(0, d, "ERROR: Device PostActivation failure:\r{0}", e);
+ Debug.Console(0, d, "ERROR: Device {1} PostActivation failure: {0}", e.Message, d.Key);
+ Debug.Console(1, d, "Stack Trace: {0}", e.StackTrace);
}
}
+ Debug.Console(0, "****PostActivation complete****");
+
OnAllDevicesActivated();
}
finally
diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/EssentialsDevice.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/EssentialsDevice.cs
index 46fa819b..a72bd282 100644
--- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/EssentialsDevice.cs
+++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/EssentialsDevice.cs
@@ -57,7 +57,7 @@ namespace PepperDash.Essentials.Core
public DescriptionAttribute(string description)
{
- Debug.Console(2, "Setting Description: {0}", description);
+ //Debug.Console(2, "Setting Description: {0}", description);
_Description = description;
}
@@ -74,7 +74,7 @@ namespace PepperDash.Essentials.Core
public ConfigSnippetAttribute(string configSnippet)
{
- Debug.Console(2, "Setting Config Snippet {0}", configSnippet);
+ //Debug.Console(2, "Setting Config Snippet {0}", configSnippet);
_ConfigSnippet = configSnippet;
}
@@ -103,7 +103,7 @@ namespace PepperDash.Essentials.Core
{
foreach (var typeName in TypeNames)
{
- Debug.Console(2, "Getting Description Attribute from class: '{0}'", typeof(T).FullName);
+ //Debug.Console(2, "Getting Description Attribute from class: '{0}'", typeof(T).FullName);
var descriptionAttribute = typeof(T).GetCustomAttributes(typeof(DescriptionAttribute), true) as DescriptionAttribute[];
string description = descriptionAttribute[0].Description;
var snippetAttribute = typeof(T).GetCustomAttributes(typeof(ConfigSnippetAttribute), true) as ConfigSnippetAttribute[];
diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Factory/DeviceFactory.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Factory/DeviceFactory.cs
index a17e0d91..ebdc87b1 100644
--- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Factory/DeviceFactory.cs
+++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Factory/DeviceFactory.cs
@@ -69,13 +69,13 @@ namespace PepperDash.Essentials.Core
///
public static void AddFactoryForType(string typeName, Func method)
{
- Debug.Console(1, Debug.ErrorLogLevel.Notice, "Adding factory method for type '{0}'", typeName);
+ //Debug.Console(1, Debug.ErrorLogLevel.Notice, "Adding factory method for type '{0}'", typeName);
DeviceFactory.FactoryMethods.Add(typeName, new DeviceFactoryWrapper() { FactoryMethod = method});
}
public static void AddFactoryForType(string typeName, string description, CType cType, Func method)
{
- Debug.Console(1, Debug.ErrorLogLevel.Notice, "Adding factory method for type '{0}'", typeName);
+ //Debug.Console(1, Debug.ErrorLogLevel.Notice, "Adding factory method for type '{0}'", typeName);
if(FactoryMethods.ContainsKey(typeName))
{
diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Feedbacks/BoolFeedback.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Feedbacks/BoolFeedback.cs
index f46b4767..055ed5b6 100644
--- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Feedbacks/BoolFeedback.cs
+++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Feedbacks/BoolFeedback.cs
@@ -62,6 +62,11 @@ namespace PepperDash.Essentials.Core
ValueFunc = valueFunc;
}
+ public void SetValueFunc(Func newFunc)
+ {
+ ValueFunc = newFunc;
+ }
+
public override void FireUpdate()
{
bool newValue = InTestMode ? TestValue : ValueFunc.Invoke();
diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Feedbacks/IntFeedback.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Feedbacks/IntFeedback.cs
index 25390c2c..53bae09a 100644
--- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Feedbacks/IntFeedback.cs
+++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Feedbacks/IntFeedback.cs
@@ -51,6 +51,12 @@ namespace PepperDash.Essentials.Core
ValueFunc = valueFunc;
}
+ public void SetValueFunc(Func newFunc)
+ {
+ ValueFunc = newFunc;
+ }
+
+
public override void FireUpdate()
{
var newValue = InTestMode ? TestValue : ValueFunc.Invoke();
diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Feedbacks/StringFeedback.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Feedbacks/StringFeedback.cs
index 56251a2e..fb5cccb5 100644
--- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Feedbacks/StringFeedback.cs
+++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Feedbacks/StringFeedback.cs
@@ -52,7 +52,10 @@ namespace PepperDash.Essentials.Core
ValueFunc = valueFunc;
}
-
+ public void SetValueFunc(Func newFunc)
+ {
+ ValueFunc = newFunc;
+ }
public override void FireUpdate()
{
diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/EssentialsHuddleSpaceFusionSystemControllerBase.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/EssentialsHuddleSpaceFusionSystemControllerBase.cs
index a172ab49..1bf925d6 100644
--- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/EssentialsHuddleSpaceFusionSystemControllerBase.cs
+++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/EssentialsHuddleSpaceFusionSystemControllerBase.cs
@@ -119,9 +119,21 @@ namespace PepperDash.Essentials.Core.Fusion
var slot = Global.ControlSystem.ProgramNumber;
var guidFilePath = Global.FilePathPrefix +
- string.Format(@"{0}-FusionGuids.json", InitialParametersClass.ProgramIDTag);
+ string.Format(@"{0}-FusionGuids-{1:X2}.json", InitialParametersClass.ProgramIDTag, _ipId);
- _guidFileExists = File.Exists(guidFilePath);
+ var oldGuidFilePath = Global.FilePathPrefix +
+ string.Format(@"{0}-FusionGuids.json", InitialParametersClass.ProgramIDTag);
+
+ if (File.Exists(oldGuidFilePath))
+ {
+ Debug.Console(0, this, "Migrating from old Fusion GUID file to new Fusion GUID File");
+
+ File.Copy(oldGuidFilePath, guidFilePath);
+
+ File.Delete(oldGuidFilePath);
+ }
+
+ _guidFileExists = File.Exists(guidFilePath);
// Check if file exists
if (!_guidFileExists)
@@ -149,19 +161,7 @@ namespace PepperDash.Essentials.Core.Fusion
}
- AddPostActivationAction(() =>
- {
- CreateSymbolAndBasicSigs(_ipId);
- SetUpSources();
- SetUpCommunitcationMonitors();
- SetUpDisplay();
- SetUpError();
- ExecuteCustomSteps();
-
- FusionRVI.GenerateFileForAllFusionDevices();
-
- GenerateGuidFile(guidFilePath);
- });
+ AddPostActivationAction(() => PostActivate(guidFilePath));
}
catch (Exception e)
{
@@ -169,6 +169,20 @@ namespace PepperDash.Essentials.Core.Fusion
}
}
+ private void PostActivate(string guidFilePath)
+ {
+ CreateSymbolAndBasicSigs(_ipId);
+ SetUpSources();
+ SetUpCommunitcationMonitors();
+ SetUpDisplay();
+ SetUpError();
+ ExecuteCustomSteps();
+
+ FusionRVI.GenerateFileForAllFusionDevices();
+
+ GenerateGuidFile(guidFilePath);
+ }
+
protected string RoomGuid
{
get { return _guiDs.RoomGuid; }
@@ -314,7 +328,7 @@ namespace PepperDash.Essentials.Core.Fusion
protected virtual void CreateSymbolAndBasicSigs(uint ipId)
{
- Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Creating Fusion Room symbol with GUID: {0}", RoomGuid);
+ Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Creating Fusion Room symbol with GUID: {0} and IP-ID {1:X2}", RoomGuid, ipId);
FusionRoom = new FusionRoom(ipId, Global.ControlSystem, Room.Name, RoomGuid);
FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.Use();
diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Global/Global.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Global/Global.cs
index 121bc9b7..9d792437 100644
--- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Global/Global.cs
+++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Global/Global.cs
@@ -32,6 +32,27 @@ namespace PepperDash.Essentials.Core
// TODO: consider making this configurable later
public static IFormatProvider Culture = CultureInfo.CreateSpecificCulture("en-US");
+ ///
+ /// True when the processor type is a DMPS variant
+ ///
+ public static bool ControlSystemIsDmpsType
+ {
+ get
+ {
+ return ControlSystem.ControllerPrompt.ToLower().IndexOf("dmps") > -1;
+ }
+ }
+
+ ///
+ /// True when the processor type is a DMPS 4K variant
+ ///
+ public static bool ControlSystemIsDmps4kType
+ {
+ get
+ {
+ return ControlSystemIsDmpsType && ControlSystem.ControllerPrompt.ToLower().IndexOf("4k") > -1;
+ }
+ }
///
/// The file path prefix to the folder containing configuration files
diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Occupancy/CenOdtOccupancySensorBaseController.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Occupancy/CenOdtOccupancySensorBaseController.cs
index dcf0b193..eae2f993 100644
--- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Occupancy/CenOdtOccupancySensorBaseController.cs
+++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Occupancy/CenOdtOccupancySensorBaseController.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Resources;
using System.Text;
using Crestron.SimplSharp;
using Crestron.SimplSharpPro.DeviceSupport;
@@ -61,6 +62,8 @@ namespace PepperDash.Essentials.Core
public BoolFeedback RawOccupancyUsFeedback { get; private set; }
+ public BoolFeedback IdentityModeFeedback { get; private set; }
+
// Debug properties
public bool InTestMode { get; private set; }
@@ -117,6 +120,8 @@ namespace PepperDash.Essentials.Core
RawOccupancyUsFeedback = new BoolFeedback(() => OccSensor.RawOccupancyDetectedByUltrasonicSensorFeedback.BoolValue);
+ IdentityModeFeedback = new BoolFeedback(()=>OccSensor.IdentityModeOnFeedback.BoolValue);
+
UltrasonicSensitivityInVacantStateFeedback = new IntFeedback(() => (int)OccSensor.UltrasonicSensorSensitivityInVacantStateFeedback);
UltrasonicSensitivityInOccupiedStateFeedback = new IntFeedback(() => (int)OccSensor.UltrasonicSensorSensitivityInOccupiedStateFeedback);
@@ -199,6 +204,27 @@ namespace PepperDash.Essentials.Core
{
SetAndWhenVacatedState((bool)PropertiesConfig.AndWhenVacatedState);
}
+
+ // TODO [ ] feature/cenoodtcpoe-sensor-sensitivity-configuration
+ if (PropertiesConfig.UsSensitivityOccupied != null)
+ {
+ SetUsSensitivityOccupied((ushort)PropertiesConfig.UsSensitivityOccupied);
+ }
+
+ if (PropertiesConfig.UsSensitivityVacant != null)
+ {
+ SetUsSensitivityVacant((ushort)PropertiesConfig.UsSensitivityVacant);
+ }
+
+ if (PropertiesConfig.PirSensitivityOccupied != null)
+ {
+ SetPirSensitivityOccupied((ushort)PropertiesConfig.PirSensitivityOccupied);
+ }
+
+ if (PropertiesConfig.PirSensitivityVacant != null)
+ {
+ SetPirSensitivityVacant((ushort)PropertiesConfig.PirSensitivityVacant);
+ }
}
///
@@ -279,7 +305,21 @@ namespace PepperDash.Essentials.Core
}
}
- ///
+ ///
+ /// Sets the identity mode on or off
+ ///
+ ///
+ public void SetIdentityMode(bool state)
+ {
+ if (state)
+ OccSensor.IdentityModeOn();
+ else
+ OccSensor.IdentityModeOff();
+
+ Debug.Console(1, this, "Identity Mode: {0}", OccSensor.IdentityModeOnFeedback.BoolValue ? "On" : "Off");
+ }
+
+ ///
/// Enables or disables the PIR sensor
///
///
@@ -506,6 +546,54 @@ namespace PepperDash.Essentials.Core
}
}
+ ///
+ /// Sets the US sensor sensitivity for occupied state
+ ///
+ ///
+ public void SetUsSensitivityOccupied(ushort sensitivity)
+ {
+ var level = (eSensitivityLevel) sensitivity;
+ if (level == 0) return;
+
+ OccSensor.UltrasonicSensorSensitivityInOccupiedState = level;
+ }
+
+ ///
+ /// Sets the US sensor sensitivity for vacant state
+ ///
+ ///
+ public void SetUsSensitivityVacant(ushort sensitivity)
+ {
+ var level = (eSensitivityLevel)sensitivity;
+ if (level == 0) return;
+
+ OccSensor.UltrasonicSensorSensitivityInVacantState = level;
+ }
+
+ ///
+ /// Sets the PIR sensor sensitivity for occupied state
+ ///
+ ///
+ public void SetPirSensitivityOccupied(ushort sensitivity)
+ {
+ var level = (eSensitivityLevel)sensitivity;
+ if (level == 0) return;
+
+ OccSensor.PassiveInfraredSensorSensitivityInOccupiedState = level;
+ }
+
+ ///
+ /// Sets the PIR sensor sensitivity for vacant state
+ ///
+ ///
+ public void SetPirSensitivityVacant(ushort sensitivity)
+ {
+ var level = (eSensitivityLevel)sensitivity;
+ if (level == 0) return;
+
+ OccSensor.PassiveInfraredSensorSensitivityInVacantState = level;
+ }
+
///
/// Method to print current settings to console
///
@@ -647,8 +735,11 @@ namespace PepperDash.Essentials.Core
//Sensor Raw States
occController.RawOccupancyPirFeedback.LinkInputSig(trilist.BooleanInput[joinMap.RawOccupancyPirFeedback.JoinNumber]);
- occController.RawOccupancyUsFeedback.LinkInputSig(trilist.BooleanInput[joinMap.RawOccupancyUsFeedback.JoinNumber]);
-
+ occController.RawOccupancyUsFeedback.LinkInputSig(trilist.BooleanInput[joinMap.RawOccupancyUsFeedback.JoinNumber]);
+
+ // Identity mode
+ trilist.SetBoolSigAction(joinMap.IdentityMode.JoinNumber, occController.SetIdentityMode);
+ occController.IdentityModeFeedback.LinkInputSig(trilist.BooleanInput[joinMap.IdentityModeFeedback.JoinNumber]);
}
public class CenOdtOccupancySensorBaseControllerFactory : EssentialsDeviceFactory
diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Occupancy/GlsOccupancySensorPropertiesConfig.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Occupancy/GlsOccupancySensorPropertiesConfig.cs
index 1f21b2a8..392c05b0 100644
--- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Occupancy/GlsOccupancySensorPropertiesConfig.cs
+++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Occupancy/GlsOccupancySensorPropertiesConfig.cs
@@ -47,5 +47,35 @@ namespace PepperDash.Essentials.Core
[JsonProperty("andWhenVacatedState")]
public bool? AndWhenVacatedState { get; set; }
+
+ // PoE Sensors: CenOdtCPoe
+
+ ///
+ /// Sets the sensitivity level for US while sensor is in occupied state
+ /// 1 = low; 2 = medium; 3 = high; 4 = xlow; 5 = 2xlow; 6 = 3xlow
+ ///
+ [JsonProperty("usSensitivityOccupied")]
+ public ushort? UsSensitivityOccupied { get; set; }
+
+ ///
+ /// Sets the sensitivity level for US while sensor is in vacant state
+ /// 1 = low; 2 = medium; 3 = high; 4 = xlow; 5 = 2xlow; 6 = 3xlow
+ ///
+ [JsonProperty("usSensitivityVacant")]
+ public ushort? UsSensitivityVacant { get; set; }
+
+ ///
+ /// Sets the sensitivity level for PIR while sensor is in occupied state
+ /// 1 = low; 2 = medium; 3 = high
+ ///
+ [JsonProperty("pirSensitivityOccupied")]
+ public ushort? PirSensitivityOccupied { get; set; }
+
+ ///
+ /// Sets the sensitivity level for PIR while sensor is in vacant state
+ /// 1 = low; 2 = medium; 3 = high
+ ///
+ [JsonProperty("pirSensitivityVacant")]
+ public ushort? PirSensitivityVacant { get; set; }
}
}
\ No newline at end of file
diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/PartitionSensor/EssentialsPartitionController.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/PartitionSensor/EssentialsPartitionController.cs
new file mode 100644
index 00000000..7066be0e
--- /dev/null
+++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/PartitionSensor/EssentialsPartitionController.cs
@@ -0,0 +1,149 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Crestron.SimplSharp;
+
+namespace PepperDash.Essentials.Core
+{
+ ///
+ /// Represents an abstract controller device for a partition dividing rooms that are combinable
+ ///
+ /// In Auto mode, it can use a partition sensor to automatically determine whether the partition is present.
+ ///
+ /// In Manual mode it accepts user input to tell it whether the partition is present.
+ ///
+ public class EssentialsPartitionController : IPartitionController
+ {
+ private IPartitionStateProvider _partitionSensor;
+
+ private bool isInAutoMode;
+
+ private bool partitionPresent;
+
+ public EssentialsPartitionController(string key, string name, IPartitionStateProvider sensor, bool defaultToManualMode, List adjacentRoomKeys)
+ {
+ Key = key;
+
+ Name = name;
+
+ AdjacentRoomKeys = adjacentRoomKeys;
+
+ if (sensor != null)
+ {
+ _partitionSensor = sensor;
+
+ if (!defaultToManualMode)
+ {
+ SetAutoMode();
+ }
+ else
+ {
+ SetManualMode();
+ }
+ }
+ else
+ {
+ SetManualMode();
+ }
+
+ PartitionPresentFeedback.FireUpdate();
+ }
+
+ void PartitionPresentFeedback_OutputChange(object sender, FeedbackEventArgs e)
+ {
+ if (isInAutoMode)
+ {
+ PartitionPresentFeedback.FireUpdate();
+ }
+ }
+
+ #region IPartitionController Members
+
+ public List AdjacentRoomKeys { get; private set; }
+
+ public void SetAutoMode()
+ {
+ isInAutoMode = true;
+ if (PartitionPresentFeedback != null)
+ {
+ PartitionPresentFeedback.SetValueFunc(() => _partitionSensor.PartitionPresentFeedback.BoolValue);
+ }
+ else
+ {
+ PartitionPresentFeedback = new BoolFeedback(() => _partitionSensor.PartitionPresentFeedback.BoolValue);
+ }
+
+ if (_partitionSensor != null)
+ {
+ _partitionSensor.PartitionPresentFeedback.OutputChange += PartitionPresentFeedback_OutputChange;
+ }
+ }
+
+ public void SetManualMode()
+ {
+ isInAutoMode = false;
+ if (PartitionPresentFeedback != null)
+ {
+ PartitionPresentFeedback.SetValueFunc(() => partitionPresent);
+ }
+ else
+ {
+ PartitionPresentFeedback = new BoolFeedback(() => partitionPresent);
+ }
+
+ if (_partitionSensor != null)
+ {
+ _partitionSensor.PartitionPresentFeedback.OutputChange -= PartitionPresentFeedback_OutputChange;
+ }
+ }
+
+
+ public void SetPartitionStatePresent()
+ {
+ if (!isInAutoMode)
+ {
+ partitionPresent = true;
+ PartitionPresentFeedback.FireUpdate();
+ }
+ }
+
+ public void SetPartitionStateNotPresent()
+ {
+ if (!isInAutoMode)
+ {
+ partitionPresent = false;
+ PartitionPresentFeedback.FireUpdate();
+ }
+ }
+
+ public void ToggglePartitionState()
+ {
+ if (!isInAutoMode)
+ {
+ partitionPresent = !partitionPresent;
+ PartitionPresentFeedback.FireUpdate();
+ }
+ }
+
+ #endregion
+
+ #region IPartitionStateProvider Members
+
+ public BoolFeedback PartitionPresentFeedback { get; private set; }
+
+ #endregion
+
+ #region IKeyName Members
+
+ public string Name { get; private set; }
+
+ #endregion
+
+ #region IKeyed Members
+
+ public string Key { get; private set; }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/PartitionSensor/GlsPartitionSensorController.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/PartitionSensor/GlsPartitionSensorController.cs
index bd67dfec..16b2f265 100644
--- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/PartitionSensor/GlsPartitionSensorController.cs
+++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/PartitionSensor/GlsPartitionSensorController.cs
@@ -1,4 +1,5 @@
-using Crestron.SimplSharpPro;
+using Crestron.SimplSharp;
+using Crestron.SimplSharpPro;
using Crestron.SimplSharpPro.DeviceSupport;
using Crestron.SimplSharpPro.GeneralIO;
using Newtonsoft.Json;
@@ -9,17 +10,20 @@ using PepperDash.Essentials.Core.Bridges.JoinMaps;
using System;
using System.Collections.Generic;
using PepperDash.Essentials.Core.Config;
+using PepperDash_Essentials_Core.PartitionSensor;
namespace PepperDash.Essentials.Core
{
[Description("Wrapper class for GLS Cresnet Partition Sensor")]
- public class GlsPartitionSensorController : CrestronGenericBridgeableBaseDevice
+ public class GlsPartitionSensorController : CrestronGenericBridgeableBaseDevice, IPartitionStateProvider
{
- private GlsPartCn _partitionSensor;
- public StringFeedback NameFeedback { get; private set; }
+ public GlsPartitionSensorPropertiesConfig PropertiesConfig { get; private set; }
+
+ private GlsPartCn _partitionSensor;
+
public BoolFeedback EnableFeedback { get; private set; }
- public BoolFeedback PartitionSensedFeedback { get; private set; }
+ public BoolFeedback PartitionPresentFeedback { get; private set; }
public BoolFeedback PartitionNotSensedFeedback { get; private set; }
public IntFeedback SensitivityFeedback { get; private set; }
@@ -32,23 +36,71 @@ namespace PepperDash.Essentials.Core
public GlsPartitionSensorController(string key, Func preActivationFunc, DeviceConfig config)
: base(key, config.Name)
{
+
+ var props = config.Properties.ToObject();
+ if (props != null)
+ {
+ PropertiesConfig = props;
+ }
+ else
+ {
+ Debug.Console(1, this, "props are null. Unable to deserialize into GlsPartSensorPropertiesConfig");
+ }
+
AddPreActivationAction(() =>
{
_partitionSensor = preActivationFunc(config);
-
+
RegisterCrestronGenericBase(_partitionSensor);
+
+ EnableFeedback = new BoolFeedback(() => InTestMode ? TestEnableFeedback : _partitionSensor.EnableFeedback.BoolValue);
+ PartitionPresentFeedback = new BoolFeedback(() => InTestMode ? TestPartitionSensedFeedback : _partitionSensor.PartitionSensedFeedback.BoolValue);
+ PartitionNotSensedFeedback = new BoolFeedback(() => InTestMode ? !TestPartitionSensedFeedback : _partitionSensor.PartitionNotSensedFeedback.BoolValue);
+ SensitivityFeedback = new IntFeedback(() => InTestMode ? TestSensitivityFeedback : _partitionSensor.SensitivityFeedback.UShortValue);
- NameFeedback = new StringFeedback(() => Name);
- EnableFeedback = new BoolFeedback(() => _partitionSensor.EnableFeedback.BoolValue);
- PartitionSensedFeedback = new BoolFeedback(() => _partitionSensor.PartitionSensedFeedback.BoolValue);
- PartitionNotSensedFeedback = new BoolFeedback(() => _partitionSensor.PartitionNotSensedFeedback.BoolValue);
- SensitivityFeedback = new IntFeedback(() => _partitionSensor.SensitivityFeedback.UShortValue);
-
- if (_partitionSensor != null) _partitionSensor.BaseEvent += PartitionSensor_BaseEvent;
+ if (_partitionSensor != null)
+ {
+ _partitionSensor.BaseEvent += PartitionSensor_BaseEvent;
+ }
});
- }
- private void PartitionSensor_BaseEvent(GenericBase device, BaseEventArgs args)
+ AddPostActivationAction(() =>
+ {
+ _partitionSensor.OnlineStatusChange += (o, a) =>
+ {
+ if (a.DeviceOnLine)
+ {
+ ApplySettingsToSensorFromConfig();
+ }
+ };
+
+ if (_partitionSensor.IsOnline)
+ {
+ ApplySettingsToSensorFromConfig();
+ }
+ });
+ }
+
+ private void ApplySettingsToSensorFromConfig()
+ {
+ if (_partitionSensor.IsOnline == false) return;
+
+ Debug.Console(1, this, "Attempting to apply settings to sensor from config");
+
+ if (PropertiesConfig.Sensitivity != null)
+ {
+ Debug.Console(1, this, "Sensitivity found, attempting to set value '{0}' from config",
+ PropertiesConfig.Sensitivity);
+ _partitionSensor.Sensitivity.UShortValue = (ushort) PropertiesConfig.Sensitivity;
+ }
+ else
+ {
+ Debug.Console(1, this, "Sensitivity null, no value specified in config");
+ }
+
+ }
+
+ private void PartitionSensor_BaseEvent(GenericBase device, BaseEventArgs args)
{
Debug.Console(2, this, "EventId: {0}, Index: {1}", args.EventId, args.Index);
@@ -61,11 +113,13 @@ namespace PepperDash.Essentials.Core
}
case (GlsPartCn.PartitionSensedFeedbackEventId):
{
- PartitionSensedFeedback.FireUpdate();
+ Debug.Console(1, this, "Partition Sensed State: {0}", _partitionSensor.PartitionSensedFeedback.BoolValue);
+ PartitionPresentFeedback.FireUpdate();
break;
}
case (GlsPartCn.PartitionNotSensedFeedbackEventId):
{
+ Debug.Console(1, this, "Partition Not Sensed State: {0}", _partitionSensor.PartitionNotSensedFeedback.BoolValue);
PartitionNotSensedFeedback.FireUpdate();
break;
}
@@ -73,7 +127,7 @@ namespace PepperDash.Essentials.Core
{
SensitivityFeedback.FireUpdate();
break;
- }
+ }
default:
{
Debug.Console(2, this, "Unhandled args.EventId: {0}", args.EventId);
@@ -93,6 +147,9 @@ namespace PepperDash.Essentials.Core
if (InTestMode)
{
TestEnableFeedback = state;
+
+ EnableFeedback.FireUpdate();
+
Debug.Console(1, this, "TestEnableFeedback: {0}", TestEnableFeedback.ToString());
return;
}
@@ -105,6 +162,10 @@ namespace PepperDash.Essentials.Core
if (InTestMode)
{
TestPartitionSensedFeedback = state;
+
+ PartitionPresentFeedback.FireUpdate();
+ PartitionNotSensedFeedback.FireUpdate();
+
Debug.Console(1, this, "TestPartitionSensedFeedback: {0}", TestPartitionSensedFeedback.ToString());
return;
}
@@ -117,6 +178,8 @@ namespace PepperDash.Essentials.Core
if (InTestMode)
{
TestSensitivityFeedback = value;
+
+ SensitivityFeedback.FireUpdate();
Debug.Console(1, this, "TestSensitivityFeedback: {0}", TestSensitivityFeedback);
return;
}
@@ -124,7 +187,22 @@ namespace PepperDash.Essentials.Core
Debug.Console(1, this, "InTestMode: {0}, unable to set sensitivity value: {1}", InTestMode.ToString(), value);
}
- public void SetEnableState(bool state)
+ public void GetSettings()
+ {
+ var dash = new string('*', 50);
+ CrestronConsole.PrintLine(string.Format("{0}\n", dash));
+
+ Debug.Console(0, this, "Enabled State: {0}", _partitionSensor.EnableFeedback.BoolValue);
+
+ Debug.Console(0, this, "Partition Sensed State: {0}", _partitionSensor.PartitionSensedFeedback.BoolValue);
+ Debug.Console(0, this, "Partition Not Sensed State: {0}", _partitionSensor.PartitionNotSensedFeedback.BoolValue);
+
+ Debug.Console(0, this, "Sensitivity Value: {0}", _partitionSensor.SensitivityFeedback.UShortValue);
+
+ CrestronConsole.PrintLine(string.Format("{0}\n", dash));
+ }
+
+ public void SetEnableState(bool state)
{
Debug.Console(2, this, "Sensor is {0}, SetEnableState: {1}", _partitionSensor == null ? "null" : "not null", state);
if (_partitionSensor == null)
@@ -180,18 +258,20 @@ namespace PepperDash.Essentials.Core
Debug.Console(1, this, "Linking to Trilist '{0}'", trilist.ID.ToString("X"));
Debug.Console(0, this, "Linking to Bridge Type {0}", GetType().Name);
- // link input from simpl
+ IsOnline.LinkInputSig(trilist.BooleanInput[joinMap.IsOnline.JoinNumber]);
+ trilist.StringInput[joinMap.Name.JoinNumber].StringValue = _partitionSensor.Name;
+
trilist.SetBoolSigAction(joinMap.Enable.JoinNumber, SetEnableState);
+ EnableFeedback.LinkInputSig(trilist.BooleanInput[joinMap.Enable.JoinNumber]);
+
+ PartitionPresentFeedback.LinkInputSig(trilist.BooleanInput[joinMap.PartitionSensed.JoinNumber]);
+ PartitionNotSensedFeedback.LinkInputSig(trilist.BooleanInput[joinMap.PartitionNotSensed.JoinNumber]);
+
trilist.SetSigTrueAction(joinMap.IncreaseSensitivity.JoinNumber, IncreaseSensitivity);
trilist.SetSigTrueAction(joinMap.DecreaseSensitivity.JoinNumber, DecreaseSensitivity);
- trilist.SetUShortSigAction(joinMap.Sensitivity.JoinNumber, SetSensitivity);
- // link output to simpl
- IsOnline.LinkInputSig(trilist.BooleanInput[joinMap.IsOnline.JoinNumber]);
- EnableFeedback.LinkInputSig(trilist.BooleanInput[joinMap.Enable.JoinNumber]);
- PartitionSensedFeedback.LinkInputSig(trilist.BooleanInput[joinMap.PartitionSensed.JoinNumber]);
- PartitionNotSensedFeedback.LinkInputSig(trilist.BooleanInput[joinMap.PartitionNotSensed.JoinNumber]);
- SensitivityFeedback.LinkInputSig(trilist.UShortInput[joinMap.Sensitivity.JoinNumber]);
+ SensitivityFeedback.LinkInputSig(trilist.UShortInput[joinMap.Sensitivity.JoinNumber]);
+ trilist.SetUShortSigAction(joinMap.Sensitivity.JoinNumber, SetSensitivity);
FeedbacksFireUpdates();
@@ -209,6 +289,7 @@ namespace PepperDash.Essentials.Core
{
if (a.DeviceOnLine)
{
+ trilist.StringInput[joinMap.Name.JoinNumber].StringValue = _partitionSensor.Name;
FeedbacksFireUpdates();
}
};
@@ -216,10 +297,9 @@ namespace PepperDash.Essentials.Core
private void FeedbacksFireUpdates()
{
- IsOnline.FireUpdate();
- NameFeedback.FireUpdate();
+ IsOnline.FireUpdate();
EnableFeedback.FireUpdate();
- PartitionSensedFeedback.FireUpdate();
+ PartitionPresentFeedback.FireUpdate();
PartitionNotSensedFeedback.FireUpdate();
SensitivityFeedback.FireUpdate();
}
@@ -260,7 +340,7 @@ namespace PepperDash.Essentials.Core
public override EssentialsDevice BuildDevice(DeviceConfig dc)
{
- Debug.Console(1, "Factory Attempting to create new C2N-RTHS Device");
+ Debug.Console(1, "Factory Attempting to create new GlsPartitionSensorController Device");
return new GlsPartitionSensorController(dc.Key, GetGlsPartCnDevice, dc);
}
diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/PartitionSensor/GlsPartitionSensorPropertiesConfig.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/PartitionSensor/GlsPartitionSensorPropertiesConfig.cs
new file mode 100644
index 00000000..8a303662
--- /dev/null
+++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/PartitionSensor/GlsPartitionSensorPropertiesConfig.cs
@@ -0,0 +1,21 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Crestron.SimplSharp;
+using Newtonsoft.Json;
+
+namespace PepperDash_Essentials_Core.PartitionSensor
+{
+ public class GlsPartitionSensorPropertiesConfig
+ {
+ ///
+ /// Sets the sensor sensitivity
+ ///
+ ///
+ /// The sensitivity range shall be between 1(lowest) to 10 (highest).
+ ///
+ [JsonProperty("sensitivity")]
+ public ushort? Sensitivity { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/PartitionSensor/IPartitionStateProvider.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/PartitionSensor/IPartitionStateProvider.cs
new file mode 100644
index 00000000..adb420b7
--- /dev/null
+++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/PartitionSensor/IPartitionStateProvider.cs
@@ -0,0 +1,36 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Crestron.SimplSharp;
+
+using PepperDash.Core;
+
+namespace PepperDash.Essentials.Core
+{
+ ///
+ /// Describes the functionality of a device that senses and provides partition state
+ ///
+ public interface IPartitionStateProvider : IKeyName
+ {
+ BoolFeedback PartitionPresentFeedback { get; }
+ }
+
+ ///
+ /// Describes the functionality of a device that can provide partition state either manually via user input or optionally via a sensor state
+ ///
+ public interface IPartitionController : IPartitionStateProvider
+ {
+ List AdjacentRoomKeys { get; }
+
+ void SetPartitionStatePresent();
+
+ void SetPartitionStateNotPresent();
+
+ void ToggglePartitionState();
+
+ void SetManualMode();
+
+ void SetAutoMode();
+ }
+}
\ No newline at end of file
diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.csproj b/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.csproj
index 85c66a13..3c7a6c31 100644
--- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.csproj
+++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.csproj
@@ -182,6 +182,7 @@
+
@@ -235,6 +236,9 @@
+
+
+
@@ -290,6 +294,10 @@
+
+
+
+
diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Plugins/PluginLoader.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Plugins/PluginLoader.cs
index 7437d75a..136303e3 100644
--- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Plugins/PluginLoader.cs
+++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Plugins/PluginLoader.cs
@@ -123,7 +123,7 @@ namespace PepperDash.Essentials
///
static LoadedAssembly LoadAssembly(string filePath)
{
- Debug.Console(2, "Attempting to load {0}", filePath);
+ //Debug.Console(2, "Attempting to load {0}", filePath);
var assembly = Assembly.LoadFrom(filePath);
if (assembly != null)
{
diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Combining/EssentialsRoomCombiner.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Combining/EssentialsRoomCombiner.cs
new file mode 100644
index 00000000..6d80913f
--- /dev/null
+++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Combining/EssentialsRoomCombiner.cs
@@ -0,0 +1,264 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Crestron.SimplSharp;
+
+using PepperDash.Core;
+
+namespace PepperDash.Essentials.Core
+{
+ public class EssentialsRoomCombiner : EssentialsDevice, IEssentialsRoomCombiner
+ {
+ private EssentialsRoomCombinerPropertiesConfig _propertiesConfig;
+
+ private IRoomCombinationScenario _currentScenario;
+
+ private List _rooms;
+
+ private bool isInAutoMode;
+
+ private CTimer _scenarioChangeDebounceTimer;
+
+ private int _scenarioChangeDebounceTimeSeconds = 10; // default to 10s
+
+ public EssentialsRoomCombiner(string key, EssentialsRoomCombinerPropertiesConfig props)
+ : base(key)
+ {
+ _propertiesConfig = props;
+
+ Partitions = new List();
+ RoomCombinationScenarios = new List();
+
+ if (_propertiesConfig.ScenarioChangeDebounceTimeSeconds > 0)
+ {
+ _scenarioChangeDebounceTimeSeconds = _propertiesConfig.ScenarioChangeDebounceTimeSeconds;
+ }
+
+ IsInAutoModeFeedback = new BoolFeedback(() => isInAutoMode);
+
+ // default to auto mode
+ isInAutoMode = true;
+
+ if (_propertiesConfig.defaultToManualMode)
+ {
+ isInAutoMode = false;
+ }
+
+ IsInAutoModeFeedback.FireUpdate();
+
+ CreateScenarios();
+
+ AddPostActivationAction(() =>
+ {
+ SetupPartitionStateProviders();
+
+ SetRooms();
+ });
+ }
+
+ void CreateScenarios()
+ {
+ RoomCombinationScenarios = new List();
+
+ foreach (var scenarioConfig in _propertiesConfig.Scenarios)
+ {
+ var scenario = new RoomCombinationScenario(scenarioConfig);
+ RoomCombinationScenarios.Add(scenario);
+ }
+ }
+
+ void SetRooms()
+ {
+ _rooms = new List();
+
+ foreach (var roomKey in _propertiesConfig.RoomKeys)
+ {
+ var room = DeviceManager.GetDeviceForKey(roomKey) as IEssentialsRoom;
+ if (room != null)
+ {
+ _rooms.Add(room);
+ }
+ }
+ }
+
+ void SetupPartitionStateProviders()
+ {
+ foreach (var pConfig in _propertiesConfig.Partitions)
+ {
+ var sensor = DeviceManager.GetDeviceForKey(pConfig.DeviceKey) as IPartitionStateProvider;
+
+ var partition = new EssentialsPartitionController(pConfig.Key, pConfig.Name, sensor, _propertiesConfig.defaultToManualMode, pConfig.AdjacentRoomKeys);
+
+ partition.PartitionPresentFeedback.OutputChange += PartitionPresentFeedback_OutputChange;
+
+ Partitions.Add(partition);
+ }
+ }
+
+ void PartitionPresentFeedback_OutputChange(object sender, FeedbackEventArgs e)
+ {
+ StartDebounceTimer();
+ }
+
+ void StartDebounceTimer()
+ {
+ var time = _scenarioChangeDebounceTimeSeconds * 1000;
+
+ if (_scenarioChangeDebounceTimer == null)
+ {
+ _scenarioChangeDebounceTimer = new CTimer((o) => DetermineRoomCombinationScenario(), time);
+ }
+ else
+ {
+ _scenarioChangeDebounceTimer.Reset(time);
+ }
+ }
+
+ ///
+ /// Determines the current room combination scenario based on the state of the partition sensors
+ ///
+ void DetermineRoomCombinationScenario()
+ {
+ if (_scenarioChangeDebounceTimer != null)
+ {
+ _scenarioChangeDebounceTimer.Dispose();
+ _scenarioChangeDebounceTimer = null;
+ }
+
+ var currentScenario = RoomCombinationScenarios.FirstOrDefault((s) =>
+ {
+ // iterate the partition states
+ foreach (var partitionState in s.PartitionStates)
+ {
+ // get the partition by key
+ var partition = Partitions.FirstOrDefault((p) => p.Key.Equals(partitionState.PartitionKey));
+
+ if (partition != null && partitionState.PartitionPresent != partition.PartitionPresentFeedback.BoolValue)
+ {
+ // the partition can't be found or the state doesn't match
+ return false;
+ }
+ }
+ // if it hasn't returned false by now we have the matching scenario
+ return true;
+ });
+
+ if (currentScenario != null)
+ {
+ CurrentScenario = currentScenario;
+ }
+ }
+
+ #region IEssentialsRoomCombiner Members
+
+ public event EventHandler RoomCombinationScenarioChanged;
+
+ public IRoomCombinationScenario CurrentScenario
+ {
+ get
+ {
+ return _currentScenario;
+ }
+ set
+ {
+ if (value != _currentScenario)
+ {
+ _currentScenario = value;
+ Debug.Console(1, this, "Current Scenario: {0}", _currentScenario.Name);
+ var handler = RoomCombinationScenarioChanged;
+ if (handler != null)
+ {
+ handler(this, new EventArgs());
+ }
+ }
+ }
+ }
+
+ public BoolFeedback IsInAutoModeFeedback { get; private set; }
+
+ public void SetAutoMode()
+ {
+ isInAutoMode = true;
+ IsInAutoModeFeedback.FireUpdate();
+ }
+
+ public void SetManualMode()
+ {
+ isInAutoMode = false;
+ IsInAutoModeFeedback.FireUpdate();
+ }
+
+ public void ToggleMode()
+ {
+ isInAutoMode = !isInAutoMode;
+ IsInAutoModeFeedback.FireUpdate();
+ }
+
+ public List RoomCombinationScenarios { get; private set; }
+
+ public List Partitions { get; private set; }
+
+ public void TogglePartitionState(string partitionKey)
+ {
+ var partition = Partitions.FirstOrDefault((p) => p.Key.Equals(partitionKey)) as IPartitionController;
+
+ if (partition != null)
+ {
+ partition.ToggglePartitionState();
+ }
+ }
+
+ public void SetRoomCombinationScenario(string scenarioKey)
+ {
+ if (isInAutoMode)
+ {
+ Debug.Console(0, this, "Cannot set room combination scenario when in auto mode. Set to auto mode first.");
+ return;
+ }
+
+ // Get the scenario
+ var scenario = RoomCombinationScenarios.FirstOrDefault((s) => s.Key.Equals(scenarioKey));
+
+ // Set the parition states from the scenario manually
+ if (scenario != null)
+ {
+ foreach (var partitionState in scenario.PartitionStates)
+ {
+ var partition = Partitions.FirstOrDefault((p) => p.Key.Equals(partitionState.PartitionKey));
+
+ if (partition != null)
+ {
+ if (partitionState.PartitionPresent)
+ {
+ partition.SetPartitionStatePresent();
+ }
+ else
+ {
+ partition.SetPartitionStateNotPresent();
+ }
+ }
+ }
+ }
+ }
+
+ #endregion
+ }
+
+ public class EssentialsRoomCombinerFactory : EssentialsDeviceFactory
+ {
+ public EssentialsRoomCombinerFactory()
+ {
+ TypeNames = new List { "essentialsroomcombiner" };
+ }
+
+ public override EssentialsDevice BuildDevice(PepperDash.Essentials.Core.Config.DeviceConfig dc)
+ {
+ Debug.Console(1, "Factory Attempting to create new EssentialsRoomCombiner Device");
+
+ var props = dc.Properties.ToObject();
+
+ return new EssentialsRoomCombiner(dc.Key, props);
+ }
+ }
+}
\ No newline at end of file
diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Combining/EssentialsRoomCombinerPropertiesConfig.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Combining/EssentialsRoomCombinerPropertiesConfig.cs
new file mode 100644
index 00000000..05295f42
--- /dev/null
+++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Combining/EssentialsRoomCombinerPropertiesConfig.cs
@@ -0,0 +1,111 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Crestron.SimplSharp;
+
+using PepperDash.Core;
+
+using Newtonsoft.Json;
+
+namespace PepperDash.Essentials.Core
+{
+ ///
+ /// Config properties for an EssentialsRoomCombiner device
+ ///
+ public class EssentialsRoomCombinerPropertiesConfig
+ {
+ ///
+ /// The list of partitions that device the rooms
+ ///
+ [JsonProperty("partitions")]
+ public List Partitions {get; set;}
+
+ ///
+ /// The list of combinations scenarios for the rooms
+ ///
+ [JsonProperty("scenarios")]
+ public List Scenarios { get; set; }
+
+ ///
+ /// The list of rooms keys that can be combined
+ ///
+ [JsonProperty("roomMap")]
+ public List RoomKeys {get; set;}
+
+ ///
+ /// Set to true to default to manual mode
+ ///
+ [JsonProperty("defaultToManualMode")]
+ public bool defaultToManualMode { get; set; }
+
+ ///
+ /// The key of the scenario to default to at system startup if in manual mode
+ ///
+ [JsonProperty("defaultScenarioKey")]
+ public string defaultScenarioKey { get; set; }
+
+ [JsonProperty("scenarioChangeDebounceTimeSeconds")]
+ public int ScenarioChangeDebounceTimeSeconds { get; set; }
+ }
+
+ ///
+ /// Config properties for a partition that separates rooms
+ ///
+ public class PartitionConfig : IKeyName
+ {
+ [JsonProperty("key")]
+ public string Key { get; set; }
+
+ [JsonProperty("name")]
+ public string Name { get; set; }
+
+ ///
+ /// Key of the device that implements IPartitionStateProvider to provide the state of the partition
+ ///
+ [JsonProperty("deviceKey")]
+ public string DeviceKey { get; set; }
+
+ ///
+ /// Keys of the rooms that this partion would be located between
+ ///
+ [JsonProperty("adjacentRoomKeys")]
+ public List AdjacentRoomKeys { get; set; }
+ }
+
+ ///
+ /// Config propeties for a room combination scenario
+ ///
+ public class RoomCombinationScenarioConfig : IKeyName
+ {
+ [JsonProperty("key")]
+ public string Key { get; set; }
+
+ [JsonProperty("name")]
+ public string Name { get; set; }
+
+ [JsonProperty("partitionStates")]
+ public List PartitionStates { get; set; }
+
+ [JsonProperty("uiMap")]
+ public Dictionary UiMap { get; set; }
+
+ [JsonProperty("activationActions")]
+ public List ActivationActions { get; set; }
+
+ [JsonProperty("deactivationActions")]
+ public List DeactivationActions { get; set; }
+ }
+
+ ///
+ /// Config properties to represent the state of a partition sensor in a RoomCombinationScenario
+ ///
+ public class PartitionState
+ {
+ [JsonProperty("partitionKey")]
+ public string PartitionKey { get; set; }
+
+ [JsonProperty("partitionSensedState")]
+ public bool PartitionPresent { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Combining/IEssentialsRoomCombiner.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Combining/IEssentialsRoomCombiner.cs
new file mode 100644
index 00000000..c0c8101b
--- /dev/null
+++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Combining/IEssentialsRoomCombiner.cs
@@ -0,0 +1,92 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Crestron.SimplSharp;
+
+using PepperDash.Core;
+
+namespace PepperDash.Essentials.Core
+{
+ ///
+ /// Describes the functionality for an EssentailsRoomCombiner device
+ ///
+ public interface IEssentialsRoomCombiner : IKeyed
+ {
+ ///
+ /// Indicates that the room combination scenario has changed
+ ///
+ event EventHandler RoomCombinationScenarioChanged;
+
+ ///
+ /// The current room combination scenario
+ ///
+ IRoomCombinationScenario CurrentScenario { get; }
+
+ ///
+ /// When true, indicates the current mode is auto mode
+ ///
+ BoolFeedback IsInAutoModeFeedback {get;}
+
+ ///
+ /// Sets auto mode
+ ///
+ void SetAutoMode();
+
+ ///
+ /// Sets manual mode
+ ///
+ void SetManualMode();
+
+ ///
+ /// Toggles the current mode between auto and manual
+ ///
+ void ToggleMode();
+
+ ///
+ /// The available room combinatino scenarios
+ ///
+ List RoomCombinationScenarios { get; }
+
+ ///
+ /// The partition
+ ///
+ List Partitions { get; }
+
+ ///
+ /// Toggles the state of a manual partition sensor
+ ///
+ ///
+ void TogglePartitionState(string partitionKey);
+
+ ///
+ /// Sets the room combination scenario (if in manual mode)
+ ///
+ ///
+ void SetRoomCombinationScenario(string scenarioKey);
+ }
+
+ public interface IRoomCombinationScenario : IKeyName
+ {
+ ///
+ /// When true, indicates that this room combination scenario is active
+ ///
+ BoolFeedback IsActiveFeedback { get; }
+
+ ///
+ /// Activates this room combination scenario
+ ///
+ void Activate();
+
+ ///
+ /// The state of the partitions that would activate this scenario
+ ///
+ List PartitionStates { get; }
+
+ ///
+ /// The mapping of UIs by key to rooms by key
+ ///
+ Dictionary UiMap { get; set; }
+ }
+
+}
\ No newline at end of file
diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Combining/RoomCombinationScenario.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Combining/RoomCombinationScenario.cs
new file mode 100644
index 00000000..a5534edc
--- /dev/null
+++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Combining/RoomCombinationScenario.cs
@@ -0,0 +1,83 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Crestron.SimplSharp;
+
+using Newtonsoft.Json;
+
+namespace PepperDash.Essentials.Core
+{
+ ///
+ /// Represents a room combination scenario
+ ///
+ public class RoomCombinationScenario: IRoomCombinationScenario
+ {
+ private RoomCombinationScenarioConfig _config;
+
+ public string Key { get; set; }
+
+ public string Name { get; set; }
+
+ public List PartitionStates { get; private set; }
+
+ public Dictionary UiMap { get; set; }
+
+ private bool _isActive;
+
+ public BoolFeedback IsActiveFeedback { get; private set; }
+
+ List activationActions;
+
+ List deactivationActions;
+
+ public RoomCombinationScenario(RoomCombinationScenarioConfig config)
+ {
+ Key = config.Key;
+
+ Name = config.Name;
+
+ PartitionStates = config.PartitionStates;
+
+ UiMap = config.UiMap;
+
+ activationActions = config.ActivationActions;
+
+ deactivationActions = config.DeactivationActions;
+
+ _config = config;
+
+ IsActiveFeedback = new BoolFeedback(() => _isActive);
+ }
+
+ public void Activate()
+ {
+ if (activationActions != null)
+ {
+ foreach (var action in activationActions)
+ {
+ DeviceJsonApi.DoDeviceAction(action);
+ }
+ }
+
+ _isActive = true;
+ IsActiveFeedback.FireUpdate();
+ }
+
+ public void Deactivate()
+ {
+ if (deactivationActions != null)
+ {
+ foreach (var action in deactivationActions)
+ {
+ DeviceJsonApi.DoDeviceAction(action);
+ }
+ }
+
+ _isActive = false;
+ IsActiveFeedback.FireUpdate();
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmBladeChassisController.cs b/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmBladeChassisController.cs
index 22e792b5..412dee6b 100644
--- a/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmBladeChassisController.cs
+++ b/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmBladeChassisController.cs
@@ -16,7 +16,8 @@ using PepperDash.Essentials.Core;
using PepperDash.Essentials.Core.Bridges;
using PepperDash.Essentials.DM.Config;
-namespace PepperDash.Essentials.DM {
+namespace PepperDash.Essentials.DM
+{
///
/// Builds a controller for basic DM-RMCs with Com and IR ports and no control functions
///
@@ -75,8 +76,10 @@ namespace PepperDash.Essentials.DM {
/// Factory method to create a new chassis controller from config data. Limited to 8x8 right now
///
public static DmBladeChassisController GetDmChassisController(string key, string name,
- string type, DMChassisPropertiesConfig properties) {
- try {
+ string type, DMChassisPropertiesConfig properties)
+ {
+ try
+ {
type = type.ToLower();
uint ipid = properties.Control.IpIdInt;
@@ -85,7 +88,8 @@ namespace PepperDash.Essentials.DM {
else if (type == "dmmd128x128") { chassis = new DmMd128x128(ipid, Global.ControlSystem); }
- if (chassis == null) {
+ if (chassis == null)
+ {
return null;
}
@@ -93,11 +97,13 @@ namespace PepperDash.Essentials.DM {
// add the cards and port names
foreach (var kvp in properties.InputSlots)
controller.AddInputBlade(kvp.Value, kvp.Key);
- foreach (var kvp in properties.OutputSlots) {
+ foreach (var kvp in properties.OutputSlots)
+ {
controller.AddOutputBlade(kvp.Value, kvp.Key);
}
- foreach (var kvp in properties.VolumeControls) {
+ foreach (var kvp in properties.VolumeControls)
+ {
// get the card
// check it for an audio-compatible type
// make a something-something that will make it work
@@ -123,7 +129,8 @@ namespace PepperDash.Essentials.DM {
controller.PropertiesConfig = properties;
return controller;
}
- catch (System.Exception e) {
+ catch (System.Exception e)
+ {
Debug.Console(0, "Error creating DM chassis:\r{0}", e);
}
return null;
@@ -137,7 +144,8 @@ namespace PepperDash.Essentials.DM {
///
///
public DmBladeChassisController(string key, string name, BladeSwitch chassis)
- : base(key, name, chassis) {
+ : base(key, name, chassis)
+ {
Chassis = chassis;
InputPorts = new RoutingPortCollection();
OutputPorts = new RoutingPortCollection();
@@ -161,68 +169,87 @@ namespace PepperDash.Essentials.DM {
InputCardHdcpCapabilityFeedbacks = new Dictionary();
InputCardHdcpCapabilityTypes = new Dictionary();
- for (uint x = 1; x <= Chassis.NumberOfOutputs; x++) {
+ for (uint x = 1; x <= Chassis.NumberOfOutputs; x++)
+ {
var tempX = x;
- if (Chassis.Outputs[tempX] != null) {
- VideoOutputFeedbacks[tempX] = new IntFeedback(() => {
+ if (Chassis.Outputs[tempX] != null)
+ {
+ VideoOutputFeedbacks[tempX] = new IntFeedback(() =>
+ {
if (Chassis.Outputs[tempX].VideoOutFeedback != null) { return (ushort)Chassis.Outputs[tempX].VideoOutFeedback.Number; }
else { return 0; };
});
- OutputNameFeedbacks[tempX] = new StringFeedback(() => {
- if (Chassis.Outputs[tempX].NameFeedback != null) {
+ OutputNameFeedbacks[tempX] = new StringFeedback(() =>
+ {
+ if (Chassis.Outputs[tempX].NameFeedback != null)
+ {
return Chassis.Outputs[tempX].NameFeedback.StringValue;
}
- else {
+ else
+ {
return "";
}
});
- OutputVideoRouteNameFeedbacks[tempX] = new StringFeedback(() => {
- if (Chassis.Outputs[tempX].VideoOutFeedback != null) {
+ OutputVideoRouteNameFeedbacks[tempX] = new StringFeedback(() =>
+ {
+ if (Chassis.Outputs[tempX].VideoOutFeedback != null)
+ {
return Chassis.Outputs[tempX].VideoOutFeedback.NameFeedback.StringValue;
}
- else {
+ else
+ {
return "";
}
});
- OutputEndpointOnlineFeedbacks[tempX] = new BoolFeedback(() => {
+ OutputEndpointOnlineFeedbacks[tempX] = new BoolFeedback(() =>
+ {
//if (Chassis.Outputs[tempX].Endpoint != null)
// return Chassis.Outputs[tempX].Endpoint.IsOnline;
//else
- return Chassis.Outputs[tempX].EndpointOnlineFeedback;
+ return Chassis.Outputs[tempX].EndpointOnlineFeedback;
});
}
- if (Chassis.Inputs[tempX] != null) {
- UsbInputRoutedToFeebacks[tempX] = new IntFeedback(() => {
+ if (Chassis.Inputs[tempX] != null)
+ {
+ UsbInputRoutedToFeebacks[tempX] = new IntFeedback(() =>
+ {
if (Chassis.Inputs[tempX].USBRoutedToFeedback != null) { return (ushort)Chassis.Inputs[tempX].USBRoutedToFeedback.Number; }
else { return 0; };
});
- VideoInputSyncFeedbacks[tempX] = new BoolFeedback(() => {
+ VideoInputSyncFeedbacks[tempX] = new BoolFeedback(() =>
+ {
if (Chassis.Inputs[tempX].VideoDetectedFeedback != null)
return Chassis.Inputs[tempX].VideoDetectedFeedback.BoolValue;
else
return false;
});
- InputNameFeedbacks[tempX] = new StringFeedback(() => {
- if (Chassis.Inputs[tempX].NameFeedback != null) {
+ InputNameFeedbacks[tempX] = new StringFeedback(() =>
+ {
+ if (Chassis.Inputs[tempX].NameFeedback != null)
+ {
return Chassis.Inputs[tempX].NameFeedback.StringValue;
}
- else {
+ else
+ {
return "";
}
});
- InputEndpointOnlineFeedbacks[tempX] = new BoolFeedback(() => {
+ InputEndpointOnlineFeedbacks[tempX] = new BoolFeedback(() =>
+ {
return Chassis.Inputs[tempX].EndpointOnlineFeedback;
});
- InputCardHdcpCapabilityFeedbacks[tempX] = new IntFeedback(() => {
+ InputCardHdcpCapabilityFeedbacks[tempX] = new IntFeedback(() =>
+ {
var inputCard = Chassis.Inputs[tempX];
- if (inputCard.Card is DmHdmi4kInputBladeCard) {
+ if (inputCard.Card is DmHdmi4kInputBladeCard)
+ {
InputCardHdcpCapabilityTypes[tempX] = eHdcpCapabilityType.Hdcp2_2Support;
if ((inputCard.Card as DmHdmi4kInputBladeCard).Hdmi4kInput.HdcpSupportOnFeedback.BoolValue)
@@ -231,7 +258,8 @@ namespace PepperDash.Essentials.DM {
return 0;
}
- if (inputCard.Card is DmC4kInputBladeCard) {
+ if (inputCard.Card is DmC4kInputBladeCard)
+ {
InputCardHdcpCapabilityTypes[tempX] = eHdcpCapabilityType.Hdcp2_2Support;
if ((inputCard.Card as DmC4kInputBladeCard).DmInput.HdcpCapabilityFeedback.Equals(eHdcpCapabilityType.HdcpSupportOff))
@@ -252,45 +280,56 @@ namespace PepperDash.Essentials.DM {
///
///
///
- public void AddInputBlade(string type, uint number) {
+ public void AddInputBlade(string type, uint number)
+ {
Debug.Console(2, this, "Adding input blade '{0}', slot {1}", type, number);
type = type.ToLower();
- if (type == "dmb4kihd") {
+ if (type == "dmb4kihd")
+ {
var inputBlade = new Dmb4kIHd(number, this.Chassis);
- foreach (var item in inputBlade.Inputs) {
+ foreach (var item in inputBlade.Inputs)
+ {
var card = (item.Card as DmHdmi4kInputBladeCard).Hdmi4kInput;
var cecPort = card as ICec;
AddHdmiInBladePorts(item.Number, cecPort);
}
}
- else if (type == "dmb4kihddnt") {
+ else if (type == "dmb4kihddnt")
+ {
var inputBlade = new Dmb4kIHd(number, this.Chassis);
- foreach (var item in inputBlade.Inputs) {
+ foreach (var item in inputBlade.Inputs)
+ {
var card = (item.Card as DmHdmi4kInputBladeCard).Hdmi4kInput;
var cecPort = card as ICec;
AddHdmiInBladePorts(item.Number, cecPort);
}
}
- else if (type == "dmb4kic") {
+ else if (type == "dmb4kic")
+ {
var inputBlade = new Dmb4kIC(number, this.Chassis);
- foreach (var item in inputBlade.Inputs) {
+ foreach (var item in inputBlade.Inputs)
+ {
AddDmInBladePorts(item.Number);
}
}
- else if (type == "dmbis") {
+ else if (type == "dmbis")
+ {
var inputBlade = new DmbIS(number, this.Chassis);
- foreach (var item in inputBlade.Inputs) {
+ foreach (var item in inputBlade.Inputs)
+ {
AddDmInMmFiberPorts(item.Number);
}
}
- else if (type == "dmbis2") {
+ else if (type == "dmbis2")
+ {
var inputBlade = new DmbIS2(number, this.Chassis);
- foreach (var item in inputBlade.Inputs) {
+ foreach (var item in inputBlade.Inputs)
+ {
AddDmInSmFiberPorts(item.Number);
}
}
@@ -307,19 +346,23 @@ namespace PepperDash.Essentials.DM {
}
- void AddHdmiInBladePorts(uint number, ICec cecPort) {
+ void AddHdmiInBladePorts(uint number, ICec cecPort)
+ {
AddInputPortWithDebug(number, "hdmiIn", eRoutingSignalType.Audio | eRoutingSignalType.Video, eRoutingPortConnectionType.DmCat, cecPort);
}
- void AddDmInBladePorts(uint number) {
+ void AddDmInBladePorts(uint number)
+ {
AddInputPortWithDebug(number, "dmCIn", eRoutingSignalType.Video, eRoutingPortConnectionType.DmCat);
}
- void AddDmInMmFiberPorts(uint number) {
+ void AddDmInMmFiberPorts(uint number)
+ {
AddInputPortWithDebug(number, "dmMmIn", eRoutingSignalType.Video, eRoutingPortConnectionType.DmMmFiber);
}
- void AddDmInSmFiberPorts(uint number) {
+ void AddDmInSmFiberPorts(uint number)
+ {
AddInputPortWithDebug(number, "dmSmIn", eRoutingSignalType.Video, eRoutingPortConnectionType.DmSmFiber);
}
@@ -328,64 +371,81 @@ namespace PepperDash.Essentials.DM {
///
///
///
- public void AddOutputBlade(string type, uint number) {
+ public void AddOutputBlade(string type, uint number)
+ {
type = type.ToLower();
Debug.Console(2, this, "Adding output blade '{0}', slot {1}", type, number);
- if (type == "dmb4kohd") {
+ if (type == "dmb4kohd")
+ {
var outputBlade = new Dmb4KOHD(number, Chassis);
- foreach (var item in outputBlade.Outputs) {
+ foreach (var item in outputBlade.Outputs)
+ {
AddHdmiOutBladePorts(item.Number);
}
}
- else if (type == "dmb4kohddnt") {
+ else if (type == "dmb4kohddnt")
+ {
var outputBlade = new Dmb4KOHD(number, Chassis);
- foreach (var item in outputBlade.Outputs) {
+ foreach (var item in outputBlade.Outputs)
+ {
AddHdmiOutBladePorts(item.Number);
}
}
- else if (type == "dmb4koc") {
+ else if (type == "dmb4koc")
+ {
var outputBlade = new Dmb4KOC(number, Chassis);
- foreach (var item in outputBlade.Outputs) {
+ foreach (var item in outputBlade.Outputs)
+ {
AddDmOutBladePorts(item.Number);
}
}
- else if (type == "dmb4koc") {
+ else if (type == "dmb4koc")
+ {
var outputBlade = new Dmb4KOC(number, Chassis);
- foreach (var item in outputBlade.Outputs) {
+ foreach (var item in outputBlade.Outputs)
+ {
AddDmOutBladePorts(item.Number);
}
}
- else if (type == "dmbos") {
+ else if (type == "dmbos")
+ {
var outputBlade = new DmbOS(number, Chassis);
- foreach (var item in outputBlade.Outputs) {
+ foreach (var item in outputBlade.Outputs)
+ {
AddDmOutMmFiberBladePorts(item.Number);
}
}
- else if (type == "dmbos2") {
+ else if (type == "dmbos2")
+ {
var outputBlade = new DmbOS2(number, Chassis);
- foreach (var item in outputBlade.Outputs) {
+ foreach (var item in outputBlade.Outputs)
+ {
AddDmOutSmFiberBladePorts(item.Number);
}
}
}
- void AddHdmiOutBladePorts(uint number) {
- AddOutputPortWithDebug(String.Format("outputBlade{0}", (number / 8 > 0 ? 1 : number / 8)), String.Format("hdmiOut{0}", number) , eRoutingSignalType.Video, eRoutingPortConnectionType.Hdmi, Chassis.Outputs[number]);
+ void AddHdmiOutBladePorts(uint number)
+ {
+ AddOutputPortWithDebug(number, "hdmiOut", eRoutingSignalType.Video, eRoutingPortConnectionType.Hdmi, Chassis.Outputs[number]);
}
- void AddDmOutBladePorts(uint number) {
- AddOutputPortWithDebug(String.Format("outputBlade{0}", (number / 8 > 0 ? 1 : number / 8)), String.Format("dmOut{0}", number), eRoutingSignalType.Video, eRoutingPortConnectionType.DmCat, Chassis.Outputs[number]);
+ void AddDmOutBladePorts(uint number)
+ {
+ AddOutputPortWithDebug(number, "dmOut", eRoutingSignalType.Video, eRoutingPortConnectionType.DmCat, Chassis.Outputs[number]);
}
- void AddDmOutMmFiberBladePorts(uint number) {
- AddOutputPortWithDebug(String.Format("outputBlade{0}", (number / 8 > 0 ? 1 : number / 8)), String.Format("dmOut{0}", number), eRoutingSignalType.Video, eRoutingPortConnectionType.DmMmFiber, Chassis.Outputs[number]);
+ void AddDmOutMmFiberBladePorts(uint number)
+ {
+ AddOutputPortWithDebug(number, "dmMmOut", eRoutingSignalType.Video, eRoutingPortConnectionType.DmMmFiber, Chassis.Outputs[number]);
}
- void AddDmOutSmFiberBladePorts(uint number) {
- AddOutputPortWithDebug(String.Format("outputBlade{0}", (number / 8 > 0 ? 1 : number / 8)), String.Format("dmOut{0}", number), eRoutingSignalType.Video, eRoutingPortConnectionType.DmSmFiber, Chassis.Outputs[number]);
+ void AddDmOutSmFiberBladePorts(uint number)
+ {
+ AddOutputPortWithDebug(number, "dmSmOut", eRoutingSignalType.Video, eRoutingPortConnectionType.DmSmFiber, Chassis.Outputs[number]);
}
@@ -417,23 +477,44 @@ namespace PepperDash.Essentials.DM {
}
- ///
- /// Adds OutputPort
- ///
- void AddOutputPortWithDebug(string cardName, string portName, eRoutingSignalType sigType, eRoutingPortConnectionType portType, object selector) {
+
+ /*void AddOutputPortWithDebug(string cardName, string portName, eRoutingSignalType sigType, eRoutingPortConnectionType portType, object selector) {
var portKey = string.Format("{0}--{1}", cardName, portName);
Debug.Console(2, this, "Adding output port '{0}'", portKey);
OutputPorts.Add(new RoutingOutputPort(portKey, sigType, portType, selector, this)
{
FeedbackMatchObject = selector
});
+ }*/
+
+ ///
+ /// Adds OutputPort
+ ///
+ void AddOutputPortWithDebug(uint cardNum, string portName, eRoutingSignalType sigType, eRoutingPortConnectionType portType, object selector)
+ {
+ try
+ {
+ var portKey = string.Format("outputCard{0}--{1}", cardNum, portName);
+ Debug.Console(2, this, "Adding output port '{0}'", portKey);
+ var outputPort = new RoutingOutputPort(portKey, sigType, portType, selector, this)
+ {
+ FeedbackMatchObject = Chassis.Outputs[cardNum]
+ };
+ OutputPorts.Add(outputPort);
+ }
+ catch (Exception ex)
+ {
+ Debug.Console(0, this, "Exception : {0}", ex);
+ }
+
}
///
///
///
- void AddVolumeControl(uint number, Audio.Output audio) {
+ void AddVolumeControl(uint number, Audio.Output audio)
+ {
VolumeControls.Add(number, new DmCardAudioOutputController(audio));
}
@@ -443,35 +524,43 @@ namespace PepperDash.Essentials.DM {
//}
- void Chassis_DMInputChange(Switch device, DMInputEventArgs args) {
+ void Chassis_DMInputChange(Switch device, DMInputEventArgs args)
+ {
- switch (args.EventId) {
- case DMInputEventIds.EndpointOnlineEventId: {
+ switch (args.EventId)
+ {
+ case DMInputEventIds.EndpointOnlineEventId:
+ {
Debug.Console(2, this, "DM Input EndpointOnlineEventId for input: {0}. State: {1}", args.Number, device.Inputs[args.Number].EndpointOnlineFeedback);
InputEndpointOnlineFeedbacks[args.Number].FireUpdate();
break;
}
- case DMInputEventIds.OnlineFeedbackEventId: {
+ case DMInputEventIds.OnlineFeedbackEventId:
+ {
Debug.Console(2, this, "DM Input OnlineFeedbackEventId for input: {0}. State: {1}", args.Number, device.Inputs[args.Number].EndpointOnlineFeedback);
InputEndpointOnlineFeedbacks[args.Number].FireUpdate();
break;
}
- case DMInputEventIds.VideoDetectedEventId: {
+ case DMInputEventIds.VideoDetectedEventId:
+ {
Debug.Console(2, this, "DM Input {0} VideoDetectedEventId", args.Number);
VideoInputSyncFeedbacks[args.Number].FireUpdate();
break;
}
- case DMInputEventIds.InputNameEventId: {
+ case DMInputEventIds.InputNameEventId:
+ {
Debug.Console(2, this, "DM Input {0} NameFeedbackEventId", args.Number);
InputNameFeedbacks[args.Number].FireUpdate();
break;
}
- case DMInputEventIds.HdcpCapabilityFeedbackEventId: {
+ case DMInputEventIds.HdcpCapabilityFeedbackEventId:
+ {
Debug.Console(2, this, "DM Input {0} HdcpCapabilityFeedbackEventId", args.Number);
InputCardHdcpCapabilityFeedbacks[args.Number].FireUpdate();
break;
}
- default: {
+ default:
+ {
Debug.Console(2, this, "DMInputChange fired for Input {0} with Unhandled EventId: {1}", args.Number, args.EventId);
break;
}
@@ -487,74 +576,74 @@ namespace PepperDash.Essentials.DM {
switch (args.EventId)
{
case DMOutputEventIds.VolumeEventId:
- {
- if (VolumeControls.ContainsKey(output))
{
- VolumeControls[args.Number].VolumeEventFromChassis();
+ if (VolumeControls.ContainsKey(output))
+ {
+ VolumeControls[args.Number].VolumeEventFromChassis();
+ }
+ break;
}
- break;
- }
case DMOutputEventIds.EndpointOnlineEventId:
- {
- Debug.Console(2, this,
- "Output {0} DMOutputEventIds.EndpointOnlineEventId fired. EndpointOnlineFeedback State: {1}",
- args.Number, Chassis.Outputs[output].EndpointOnlineFeedback);
- if (Chassis.Outputs[output].Endpoint != null)
+ {
Debug.Console(2, this,
- "Output {0} DMOutputEventIds.EndpointOnlineEventId fired. Endpoint.IsOnline State: {1}",
- args.Number, Chassis.Outputs[output].Endpoint.IsOnline);
+ "Output {0} DMOutputEventIds.EndpointOnlineEventId fired. EndpointOnlineFeedback State: {1}",
+ args.Number, Chassis.Outputs[output].EndpointOnlineFeedback);
+ if (Chassis.Outputs[output].Endpoint != null)
+ Debug.Console(2, this,
+ "Output {0} DMOutputEventIds.EndpointOnlineEventId fired. Endpoint.IsOnline State: {1}",
+ args.Number, Chassis.Outputs[output].Endpoint.IsOnline);
- OutputEndpointOnlineFeedbacks[output].FireUpdate();
- break;
- }
+ OutputEndpointOnlineFeedbacks[output].FireUpdate();
+ break;
+ }
case DMOutputEventIds.OnlineFeedbackEventId:
- {
- Debug.Console(2, this, "Output {0} DMInputEventIds.OnlineFeedbackEventId fired. State: {1}",
- args.Number, Chassis.Outputs[output].EndpointOnlineFeedback);
- OutputEndpointOnlineFeedbacks[output].FireUpdate();
- break;
- }
+ {
+ Debug.Console(2, this, "Output {0} DMInputEventIds.OnlineFeedbackEventId fired. State: {1}",
+ args.Number, Chassis.Outputs[output].EndpointOnlineFeedback);
+ OutputEndpointOnlineFeedbacks[output].FireUpdate();
+ break;
+ }
case DMOutputEventIds.VideoOutEventId:
- {
-
- var inputNumber = Chassis.Outputs[output].VideoOutFeedback == null ? 0 : Chassis.Outputs[output].VideoOutFeedback.Number;
-
- Debug.Console(2, this, "DMSwitchAudioVideo:{0} Routed Input:{1} Output:{2}'", this.Name,
- inputNumber, output);
-
- if (VideoOutputFeedbacks.ContainsKey(output))
{
- var localInputPort = InputPorts.FirstOrDefault(p => (DMInput)p.FeedbackMatchObject == Chassis.Outputs[output].VideoOutFeedback);
- var localOutputPort =
- OutputPorts.FirstOrDefault(p => (DMOutput) p.FeedbackMatchObject == Chassis.Outputs[output]);
+
+ var inputNumber = Chassis.Outputs[output].VideoOutFeedback == null ? 0 : Chassis.Outputs[output].VideoOutFeedback.Number;
+
+ Debug.Console(2, this, "DMSwitchAudioVideo:{0} Routed Input:{1} Output:{2}'", this.Name,
+ inputNumber, output);
+
+ if (VideoOutputFeedbacks.ContainsKey(output))
+ {
+ var localInputPort = InputPorts.FirstOrDefault(p => (DMInput)p.FeedbackMatchObject == Chassis.Outputs[output].VideoOutFeedback);
+ var localOutputPort =
+ OutputPorts.FirstOrDefault(p => (DMOutput)p.FeedbackMatchObject == Chassis.Outputs[output]);
- VideoOutputFeedbacks[output].FireUpdate();
- OnSwitchChange(new RoutingNumericEventArgs(output,
- inputNumber,
- localOutputPort,
- localInputPort,
- eRoutingSignalType.AudioVideo));
+ VideoOutputFeedbacks[output].FireUpdate();
+ OnSwitchChange(new RoutingNumericEventArgs(output,
+ inputNumber,
+ localOutputPort,
+ localInputPort,
+ eRoutingSignalType.AudioVideo));
+ }
+ if (OutputVideoRouteNameFeedbacks.ContainsKey(output))
+ {
+ OutputVideoRouteNameFeedbacks[output].FireUpdate();
+ }
+ break;
}
- if (OutputVideoRouteNameFeedbacks.ContainsKey(output))
- {
- OutputVideoRouteNameFeedbacks[output].FireUpdate();
- }
- break;
- }
case DMOutputEventIds.OutputNameEventId:
- {
- Debug.Console(2, this, "DM Output {0} NameFeedbackEventId", output);
- OutputNameFeedbacks[output].FireUpdate();
- break;
- }
+ {
+ Debug.Console(2, this, "DM Output {0} NameFeedbackEventId", output);
+ OutputNameFeedbacks[output].FireUpdate();
+ break;
+ }
default:
- {
- Debug.Console(2, this, "DMOutputChange fired for Output {0} with Unhandled EventId: {1}",
- args.Number, args.EventId);
- break;
- }
+ {
+ Debug.Console(2, this, "DMOutputChange fired for Output {0} with Unhandled EventId: {1}",
+ args.Number, args.EventId);
+ break;
+ }
}
}
@@ -564,7 +653,8 @@ namespace PepperDash.Essentials.DM {
///
///
///
- void StartOffTimer(PortNumberType pnt) {
+ void StartOffTimer(PortNumberType pnt)
+ {
if (RouteOffTimers.ContainsKey(pnt))
return;
RouteOffTimers[pnt] = new CTimer(o => ExecuteSwitch(null, pnt.Selector, pnt.Type), RouteOffTime);
@@ -572,8 +662,10 @@ namespace PepperDash.Essentials.DM {
// Send out sigs when coming online
- void IsOnline_OutputChange(object sender, EventArgs e) {
- if (IsOnline.BoolValue) {
+ void IsOnline_OutputChange(object sender, EventArgs e)
+ {
+ if (IsOnline.BoolValue)
+ {
Chassis.EnableUSBBreakaway.BoolValue = true;
if (InputNames != null)
@@ -587,12 +679,13 @@ namespace PepperDash.Essentials.DM {
#region IRouting Members
- public void ExecuteSwitch(object inputSelector, object outputSelector, eRoutingSignalType sigType) {
+ public void ExecuteSwitch(object inputSelector, object outputSelector, eRoutingSignalType sigType)
+ {
Debug.Console(2, this, "Making an awesome DM route from {0} to {1} {2}", inputSelector, outputSelector, sigType);
var input = inputSelector as DMInput; // Cast can sometimes fail
var output = outputSelector as DMOutput;
-
+
if (output == null)
{
@@ -605,11 +698,14 @@ namespace PepperDash.Essentials.DM {
// Check to see if there's an off timer waiting on this and if so, cancel
var key = new PortNumberType(output, sigType);
- if (input == null) {
+ if (input == null)
+ {
StartOffTimer(key);
}
- else {
- if (RouteOffTimers.ContainsKey(key)) {
+ else
+ {
+ if (RouteOffTimers.ContainsKey(key))
+ {
Debug.Console(2, this, "{0} cancelling route off due to new source", output);
RouteOffTimers[key].Stop();
RouteOffTimers.Remove(key);
@@ -671,7 +767,7 @@ namespace PepperDash.Essentials.DM {
var ioSlotJoin = ioSlot - 1;
// Control
- trilist.SetUShortSigAction(joinMap.OutputVideo.JoinNumber + ioSlotJoin, o => ExecuteNumericSwitch(o, (ushort) ioSlot, eRoutingSignalType.Video));
+ trilist.SetUShortSigAction(joinMap.OutputVideo.JoinNumber + ioSlotJoin, o => ExecuteNumericSwitch(o, (ushort)ioSlot, eRoutingSignalType.Video));
if (TxDictionary.ContainsKey(ioSlot))
{
diff --git a/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmpsRoutingController.cs b/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmpsRoutingController.cs
index 484293eb..9a8742a4 100644
--- a/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmpsRoutingController.cs
+++ b/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmpsRoutingController.cs
@@ -25,9 +25,16 @@ namespace PepperDash.Essentials.DM
public CrestronControlSystem Dmps { get; set; }
public ISystemControl SystemControl { get; private set; }
+
+ //Check if DMPS is a DMPS3-4K type for endpoint creation
+ public bool Dmps4kType { get; private set; }
//IroutingNumericEvent
public event EventHandler NumericSwitchChange;
+
+ //Feedback for DMPS System Power
+ public BoolFeedback SystemPowerOnFeedback { get; private set; }
+ public BoolFeedback SystemPowerOffFeedback { get; private set; }
// Feedbacks for EssentialDM
public Dictionary VideoOutputFeedbacks { get; private set; }
@@ -112,10 +119,29 @@ namespace PepperDash.Essentials.DM
///
public DmpsRoutingController(string key, string name, ISystemControl systemControl)
: base(key, name)
- {
-
+ {
Dmps = Global.ControlSystem;
- SystemControl = systemControl;
+
+ switch (name.Replace("-", "").Replace("c", "").Replace("C", ""))
+ {
+ case "dmps34k50":
+ case "dmps34k100":
+ case "dmps34k150":
+ SystemControl = systemControl as Dmps34K150CSystemControl;
+ Dmps4kType = true;
+ break;
+ case "dmps34k200":
+ case "dmps34k250":
+ case "dmps34k300":
+ case "dmps34k350":
+ SystemControl = systemControl as Dmps34K300CSystemControl;
+ Dmps4kType = true;
+ break;
+ default:
+ SystemControl = systemControl as Dmps3SystemControl;
+ Dmps4kType = false;
+ break;
+ }
InputPorts = new RoutingPortCollection();
OutputPorts = new RoutingPortCollection();
@@ -123,6 +149,29 @@ namespace PepperDash.Essentials.DM
TxDictionary = new Dictionary();
RxDictionary = new Dictionary();
+ SystemPowerOnFeedback = new BoolFeedback(() =>
+ {
+ if (SystemControl is Dmps3SystemControl)
+ {
+ return ((Dmps3SystemControl)SystemControl).SystemPowerOnFeedBack.BoolValue;
+ }
+ else
+ {
+ return false;
+ }
+ });
+ SystemPowerOffFeedback = new BoolFeedback(() =>
+ {
+ if (SystemControl is Dmps3SystemControl)
+ {
+ return ((Dmps3SystemControl)SystemControl).SystemPowerOffFeedBack.BoolValue;
+ }
+ else
+ {
+ return false;
+ }
+ });
+
VideoOutputFeedbacks = new Dictionary();
AudioOutputFeedbacks = new Dictionary();
VideoInputSyncFeedbacks = new Dictionary();
@@ -154,6 +203,7 @@ namespace PepperDash.Essentials.DM
// Subscribe to events
Dmps.DMInputChange += Dmps_DMInputChange;
Dmps.DMOutputChange += Dmps_DMOutputChange;
+ Dmps.DMSystemChange += Dmps_DMSystemChange;
return base.CustomActivate();
}
@@ -191,6 +241,22 @@ namespace PepperDash.Essentials.DM
}
}
+ public void SetPowerOn(bool a)
+ {
+ if (SystemControl is Dmps3SystemControl)
+ {
+ ((Dmps3SystemControl)SystemControl).SystemPowerOn();
+ }
+ }
+
+ public void SetPowerOff(bool a)
+ {
+ if (SystemControl is Dmps3SystemControl)
+ {
+ ((Dmps3SystemControl)SystemControl).SystemPowerOff();
+ }
+ }
+
public override void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge)
{
var joinMap = new DmpsRoutingControllerJoinMap(joinStart);
@@ -211,9 +277,22 @@ namespace PepperDash.Essentials.DM
Debug.Console(1, this, "Linking to Trilist '{0}'", trilist.ID.ToString("X"));
+ //Link up system
+ trilist.SetBoolSigAction(joinMap.SystemPowerOn.JoinNumber, SetPowerOn);
+ trilist.SetBoolSigAction(joinMap.SystemPowerOff.JoinNumber, SetPowerOff);
+ if (SystemPowerOnFeedback != null)
+ {
+ SystemPowerOnFeedback.LinkInputSig(
+ trilist.BooleanInput[joinMap.SystemPowerOn.JoinNumber]);
+ }
+ if (SystemPowerOffFeedback != null)
+ {
+ SystemPowerOffFeedback.LinkInputSig(
+ trilist.BooleanInput[joinMap.SystemPowerOff.JoinNumber]);
+ }
+
// Link up outputs
LinkInputsToApi(trilist, joinMap);
-
LinkOutputsToApi(trilist, joinMap);
}
@@ -719,28 +798,36 @@ namespace PepperDash.Essentials.DM
void Dmps_DMInputChange(Switch device, DMInputEventArgs args)
{
- //Debug.Console(2, this, "DMSwitch:{0} Input:{1} Event:{2}'", this.Name, args.Number, args.EventId.ToString());
-
- switch (args.EventId)
+ try
{
- case (DMInputEventIds.OnlineFeedbackEventId):
- {
- Debug.Console(2, this, "DM Input OnlineFeedbackEventId for input: {0}. State: {1}", args.Number, device.Inputs[args.Number].EndpointOnlineFeedback);
- InputEndpointOnlineFeedbacks[args.Number].FireUpdate();
- break;
- }
- case (DMInputEventIds.VideoDetectedEventId):
- {
- Debug.Console(2, this, "DM Input {0} VideoDetectedEventId", args.Number);
- VideoInputSyncFeedbacks[args.Number].FireUpdate();
- break;
- }
- case (DMInputEventIds.InputNameEventId):
- {
- Debug.Console(2, this, "DM Input {0} NameFeedbackEventId", args.Number);
- InputNameFeedbacks[args.Number].FireUpdate();
- break;
- }
+ switch (args.EventId)
+ {
+ case (DMInputEventIds.OnlineFeedbackEventId):
+ {
+ Debug.Console(2, this, "DM Input OnlineFeedbackEventId for input: {0}. State: {1}", args.Number, device.Inputs[args.Number].EndpointOnlineFeedback);
+ InputEndpointOnlineFeedbacks[args.Number].FireUpdate();
+ break;
+ }
+ case (DMInputEventIds.VideoDetectedEventId):
+ {
+ Debug.Console(2, this, "DM Input {0} VideoDetectedEventId", args.Number);
+ VideoInputSyncFeedbacks[args.Number].FireUpdate();
+ break;
+ }
+ case (DMInputEventIds.InputNameEventId):
+ {
+ Debug.Console(2, this, "DM Input {0} NameFeedbackEventId", args.Number);
+ if(InputNameFeedbacks.ContainsKey(args.Number))
+ {
+ InputNameFeedbacks[args.Number].FireUpdate();
+ }
+ break;
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ Debug.Console(0, Debug.ErrorLogLevel.Notice, "DMSwitch Input Change:{0} Input:{1} Event:{2}\rException: {3}", this.Name, args.Number, args.EventId.ToString(), e.ToString());
}
}
void Dmps_DMOutputChange(Switch device, DMOutputEventArgs args)
@@ -812,6 +899,23 @@ namespace PepperDash.Essentials.DM
}
+ void Dmps_DMSystemChange(Switch device, DMSystemEventArgs args)
+ {
+ switch (args.EventId)
+ {
+ case DMSystemEventIds.SystemPowerOnEventId:
+ {
+ SystemPowerOnFeedback.FireUpdate();
+ break;
+ }
+ case DMSystemEventIds.SystemPowerOffEventId:
+ {
+ SystemPowerOffFeedback.FireUpdate();
+ break;
+ }
+ }
+ }
+
///
///
///
@@ -879,7 +983,6 @@ namespace PepperDash.Essentials.DM
// NOTE THAT BITWISE COMPARISONS - TO CATCH ALL ROUTING TYPES
if ((sigType & eRoutingSignalType.Video) == eRoutingSignalType.Video)
{
-
output.VideoOut = input;
}
@@ -903,7 +1006,6 @@ namespace PepperDash.Essentials.DM
if ((sigType & eRoutingSignalType.UsbOutput) == eRoutingSignalType.UsbOutput)
{
-
output.USBRoutedTo = input;
}
diff --git a/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Receivers/DmRmcHelper.cs b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Receivers/DmRmcHelper.cs
index 83c386bc..9043d514 100644
--- a/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Receivers/DmRmcHelper.cs
+++ b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Receivers/DmRmcHelper.cs
@@ -329,7 +329,14 @@ namespace PepperDash.Essentials.DM
var parentDev = DeviceManager.GetDeviceForKey(pKey);
if (parentDev is DmpsRoutingController)
{
- return GetDmRmcControllerForDmps(key, name, typeName, parentDev as DmpsRoutingController, props.ParentOutputNumber);
+ if ((parentDev as DmpsRoutingController).Dmps4kType)
+ {
+ return GetDmRmcControllerForDmps4k(key, name, typeName, parentDev as DmpsRoutingController, props.ParentOutputNumber);
+ }
+ else
+ {
+ return GetDmRmcControllerForDmps(key, name, typeName, ipid, parentDev as DmpsRoutingController, props.ParentOutputNumber);
+ }
}
if (!(parentDev is IDmSwitch))
{
@@ -395,25 +402,47 @@ namespace PepperDash.Essentials.DM
return null;
}
- private static CrestronGenericBaseDevice GetDmRmcControllerForDmps(string key, string name, string typeName,
+ private static CrestronGenericBaseDevice GetDmRmcControllerForDmps(string key, string name, string typeName,
+ uint ipid, DmpsRoutingController controller, uint num)
+ {
+ Func dmpsHandler;
+ if (ChassisDict.TryGetValue(typeName.ToLower(), out dmpsHandler))
+ {
+ var output = controller.Dmps.SwitcherOutputs[num] as DMOutput;
+
+ if (output != null)
+ {
+ return dmpsHandler(key, name, ipid, output);
+ }
+ Debug.Console(0, Debug.ErrorLogLevel.Error,
+ "Cannot attach DM-RMC of type '{0}' to output {1} on DMPS chassis. Output is not a DM Output.",
+ typeName, num);
+ return null;
+ }
+
+ Debug.Console(0, Debug.ErrorLogLevel.Error, "Cannot create DM-RMC of type '{0}' to output {1} on DMPS chassis", typeName, num);
+ return null;
+ }
+
+ private static CrestronGenericBaseDevice GetDmRmcControllerForDmps4k(string key, string name, string typeName,
DmpsRoutingController controller, uint num)
{
- Func dmpsHandler;
- if (ChassisCpu3Dict.TryGetValue(typeName.ToLower(), out dmpsHandler))
+ Func dmps4kHandler;
+ if (ChassisCpu3Dict.TryGetValue(typeName.ToLower(), out dmps4kHandler))
{
var output = controller.Dmps.SwitcherOutputs[num] as DMOutput;
if (output != null)
{
- return dmpsHandler(key, name, output);
+ return dmps4kHandler(key, name, output);
}
Debug.Console(0, Debug.ErrorLogLevel.Error,
- "Cannot attach DM-RMC of type '{0}' to output {1} on DMPS chassis. Output is not a DM Output.",
+ "Cannot attach DM-RMC of type '{0}' to output {1} on DMPS-4K chassis. Output is not a DM Output.",
typeName, num);
return null;
}
- Debug.Console(0, Debug.ErrorLogLevel.Error, "Cannot create DM-RMC of type '{0}' to output {1} on DMPS chassis", typeName, num);
+ Debug.Console(0, Debug.ErrorLogLevel.Error, "Cannot create DM-RMC of type '{0}' to output {1} on DMPS-4K chassis", typeName, num);
return null;
}
diff --git a/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Transmitters/DmTxHelpers.cs b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Transmitters/DmTxHelpers.cs
index 4d8e41f6..fcec7fa4 100644
--- a/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Transmitters/DmTxHelpers.cs
+++ b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Transmitters/DmTxHelpers.cs
@@ -36,7 +36,7 @@ namespace PepperDash.Essentials.DM
var ipid = props.Control.IpIdInt;
var pKey = props.ParentDeviceKey.ToLower();
- if (pKey == "processor")
+ if (pKey == "processor")
{
// Catch constructor failures, mainly dues to IPID
try
@@ -65,99 +65,135 @@ namespace PepperDash.Essentials.DM
{
Debug.Console(0, "[{0}] WARNING: Cannot create DM-TX device: {1}", key, e);
}
+ return null;
}
- else
- {
- var parentDev = DeviceManager.GetDeviceForKey(pKey);
- if (!(parentDev is IDmSwitch))
- {
- Debug.Console(0, "Cannot create DM device '{0}'. '{1}' is not a DM Chassis.",
- key, pKey);
- return null;
- }
+ var parentDev = DeviceManager.GetDeviceForKey(pKey);
+ DMInput dmInput;
+ bool isCpu3 = false;
+
+ if (parentDev is IDmSwitch)
+ {
// Get the Crestron chassis and link stuff up
- var switchDev = (parentDev as IDmSwitch);
- var chassis = switchDev.Chassis;
+ var switchDev = (parentDev as IDmSwitch);
+ var chassis = switchDev.Chassis;
- var num = props.ParentInputNumber;
- if (num <= 0 || num > chassis.NumberOfInputs)
- {
- Debug.Console(0, "Cannot create DM device '{0}'. Input number '{1}' is out of range",
- key, num);
- return null;
- }
- else
+ //Check that the input is within range of this chassis' possible inputs
+ var num = props.ParentInputNumber;
+ if (num <= 0 || num > chassis.NumberOfInputs)
+ {
+ Debug.Console(0, "Cannot create DM device '{0}'. Input number '{1}' is out of range",
+ key, num);
+ return null;
+ }
+
+ switchDev.TxDictionary.Add(num, key);
+ dmInput = chassis.Inputs[num];
+
+ //Determine if IpId is needed for this chassis type
+ if (chassis is DmMd8x8Cpu3 || chassis is DmMd16x16Cpu3 ||
+ chassis is DmMd32x32Cpu3 || chassis is DmMd8x8Cpu3rps ||
+ chassis is DmMd16x16Cpu3rps || chassis is DmMd32x32Cpu3rps ||
+ chassis is DmMd128x128 || chassis is DmMd64x64)
{
- var controller = (parentDev as IDmSwitch);
- controller.TxDictionary.Add(num, key);
+ isCpu3 = true;
}
- // Catch constructor failures, mainly dues to IPID
- try
- {
- // Must use different constructor for CPU3 chassis types. No IPID
- if (chassis is DmMd8x8Cpu3 || chassis is DmMd16x16Cpu3 ||
- chassis is DmMd32x32Cpu3 || chassis is DmMd8x8Cpu3rps ||
- chassis is DmMd16x16Cpu3rps || chassis is DmMd32x32Cpu3rps||
- chassis is DmMd128x128 || chassis is DmMd64x64)
- {
- if (typeName.StartsWith("dmtx200"))
- return new DmTx200Controller(key, name, new DmTx200C2G(chassis.Inputs[num]));
- if (typeName.StartsWith("dmtx201c"))
- return new DmTx201CController(key, name, new DmTx201C(chassis.Inputs[num]));
- if (typeName.StartsWith("dmtx201s"))
- return new DmTx201SController(key, name, new DmTx201S(chassis.Inputs[num]));
- if (typeName.StartsWith("dmtx4k100"))
- return new DmTx4k100Controller(key, name, new DmTx4K100C1G(chassis.Inputs[num]));
- if (typeName.StartsWith("dmtx4kz100"))
- return new DmTx4kz100Controller(key, name, new DmTx4kz100C1G(chassis.Inputs[num]));
- if (typeName.StartsWith("dmtx4k202"))
- return new DmTx4k202CController(key, name, new DmTx4k202C(chassis.Inputs[num]));
- if (typeName.StartsWith("dmtx4kz202"))
- return new DmTx4kz202CController(key, name, new DmTx4kz202C(chassis.Inputs[num]));
- if (typeName.StartsWith("dmtx4k302"))
- return new DmTx4k302CController(key, name, new DmTx4k302C(chassis.Inputs[num]));
- if (typeName.StartsWith("dmtx4kz302"))
- return new DmTx4kz302CController(key, name, new DmTx4kz302C(chassis.Inputs[num]));
- if (typeName.StartsWith("dmtx401"))
- return new DmTx401CController(key, name, new DmTx401C(chassis.Inputs[num]));
- if (typeName.StartsWith("hdbasettx"))
- return new HDBaseTTxController(key, name, new HDTx3CB(chassis.Inputs[num]));
- }
- else
- {
- if (typeName.StartsWith("dmtx200"))
- return new DmTx200Controller(key, name, new DmTx200C2G(ipid, chassis.Inputs[num]));
- if (typeName.StartsWith("dmtx201c"))
- return new DmTx201CController(key, name, new DmTx201C(ipid, chassis.Inputs[num]));
- if (typeName.StartsWith("dmtx201s"))
- return new DmTx201SController(key, name, new DmTx201S(ipid, chassis.Inputs[num]));
- if (typeName.StartsWith("dmtx4k100"))
- return new DmTx4k100Controller(key, name, new DmTx4K100C1G(ipid, chassis.Inputs[num]));
- if (typeName.StartsWith("dmtx4kz100"))
- return new DmTx4kz100Controller(key, name, new DmTx4kz100C1G(ipid, chassis.Inputs[num]));
- if (typeName.StartsWith("dmtx4k202"))
- return new DmTx4k202CController(key, name, new DmTx4k202C(ipid, chassis.Inputs[num]));
- if (typeName.StartsWith("dmtx4kz202"))
- return new DmTx4kz202CController(key, name, new DmTx4kz202C(ipid, chassis.Inputs[num]));
- if (typeName.StartsWith("dmtx4k302"))
- return new DmTx4k302CController(key, name, new DmTx4k302C(ipid, chassis.Inputs[num]));
- if (typeName.StartsWith("dmtx4kz302"))
- return new DmTx4kz302CController(key, name, new DmTx4kz302C(ipid, chassis.Inputs[num]));
- if (typeName.StartsWith("dmtx401"))
- return new DmTx401CController(key, name, new DmTx401C(ipid, chassis.Inputs[num]));
- if (typeName.StartsWith("hdbasettx"))
- return new HDBaseTTxController(key, name, new HDTx3CB(ipid, chassis.Inputs[num]));
- }
- }
- catch (Exception e)
- {
- Debug.Console(0, "[{0}] WARNING: Cannot create DM-TX device: {1}", key, e);
- }
+ }
+ else if(parentDev is DmpsRoutingController)
+ {
+ // Get the DMPS chassis and link stuff up
+ var dmpsDev = (parentDev as DmpsRoutingController);
+ var chassis = dmpsDev.Dmps;
+
+ //Check that the input is within range of this chassis' possible inputs
+ var num = props.ParentInputNumber;
+ if (num <= 0 || num > chassis.SwitcherInputs.Count)
+ {
+ Debug.Console(0, "Cannot create DMPS device '{0}'. Input number '{1}' is out of range",
+ key, num);
+ return null;
+ }
+
+ dmpsDev.TxDictionary.Add(num, key);
+
+ try
+ {
+ dmInput = chassis.SwitcherInputs[num] as DMInput;
+ }
+ catch
+ {
+ Debug.Console(0, "Cannot create DMPS device '{0}'. Input number '{1}' is not a DM input", key, num);
+ return null;
+ }
+ }
+
+ else
+ {
+ Debug.Console(0, "Cannot create DM device '{0}'. '{1}' is not a processor, DM Chassis or DMPS.", key, pKey);
+ return null;
}
-
- return null;
+
+ try
+ {
+ // Must use different constructor for CPU3 or DMPS3-4K types. No IPID
+ if (isCpu3 || Global.ControlSystemIsDmps4kType)
+ {
+ if (typeName.StartsWith("dmtx200"))
+ return new DmTx200Controller(key, name, new DmTx200C2G(dmInput));
+ if (typeName.StartsWith("dmtx201c"))
+ return new DmTx201CController(key, name, new DmTx201C(dmInput));
+ if (typeName.StartsWith("dmtx201s"))
+ return new DmTx201SController(key, name, new DmTx201S(dmInput));
+ if (typeName.StartsWith("dmtx4k100"))
+ return new DmTx4k100Controller(key, name, new DmTx4K100C1G(dmInput));
+ if (typeName.StartsWith("dmtx4kz100"))
+ return new DmTx4kz100Controller(key, name, new DmTx4kz100C1G(dmInput));
+ if (typeName.StartsWith("dmtx4k202"))
+ return new DmTx4k202CController(key, name, new DmTx4k202C(dmInput));
+ if (typeName.StartsWith("dmtx4kz202"))
+ return new DmTx4kz202CController(key, name, new DmTx4kz202C(dmInput));
+ if (typeName.StartsWith("dmtx4k302"))
+ return new DmTx4k302CController(key, name, new DmTx4k302C(dmInput));
+ if (typeName.StartsWith("dmtx4kz302"))
+ return new DmTx4kz302CController(key, name, new DmTx4kz302C(dmInput));
+ if (typeName.StartsWith("dmtx401"))
+ return new DmTx401CController(key, name, new DmTx401C(dmInput));
+ if (typeName.StartsWith("hdbasettx"))
+ return new HDBaseTTxController(key, name, new HDTx3CB(dmInput));
+ }
+ else
+ {
+ if (typeName.StartsWith("dmtx200"))
+ return new DmTx200Controller(key, name, new DmTx200C2G(ipid, dmInput));
+ if (typeName.StartsWith("dmtx201c"))
+ return new DmTx201CController(key, name, new DmTx201C(ipid, dmInput));
+ if (typeName.StartsWith("dmtx201s"))
+ return new DmTx201SController(key, name, new DmTx201S(ipid, dmInput));
+ if (typeName.StartsWith("dmtx4k100"))
+ return new DmTx4k100Controller(key, name, new DmTx4K100C1G(ipid, dmInput));
+ if (typeName.StartsWith("dmtx4kz100"))
+ return new DmTx4kz100Controller(key, name, new DmTx4kz100C1G(ipid, dmInput));
+ if (typeName.StartsWith("dmtx4k202"))
+ return new DmTx4k202CController(key, name, new DmTx4k202C(ipid, dmInput));
+ if (typeName.StartsWith("dmtx4kz202"))
+ return new DmTx4kz202CController(key, name, new DmTx4kz202C(ipid, dmInput));
+ if (typeName.StartsWith("dmtx4k302"))
+ return new DmTx4k302CController(key, name, new DmTx4k302C(ipid, dmInput));
+ if (typeName.StartsWith("dmtx4kz302"))
+ return new DmTx4kz302CController(key, name, new DmTx4kz302C(ipid, dmInput));
+ if (typeName.StartsWith("dmtx401"))
+ return new DmTx401CController(key, name, new DmTx401C(ipid, dmInput));
+ if (typeName.StartsWith("hdbasettx"))
+ return new HDBaseTTxController(key, name, new HDTx3CB(ipid, dmInput));
+ }
+ }
+ catch (Exception e)
+ {
+ Debug.Console(0, "[{0}] WARNING: Cannot create DM-TX device: {1}", key, e);
+ }
+
+ return null;
}
}
@@ -185,11 +221,12 @@ namespace PepperDash.Essentials.DM
protected DmTxControllerBase(string key, string name, EndpointTransmitterBase hardware)
: base(key, name, hardware)
{
- // if wired to a chassis, skip registration step in base class
- if (hardware.DMInput != null)
- {
- this.PreventRegistration = true;
- }
+ // if wired to a chassis or DMPS, skip registration step in base class
+ if (hardware.DMInput != null || (Global.ControlSystemIsDmpsType && hardware.DMInput != null))
+ {
+ this.PreventRegistration = true;
+ }
+
AddToFeedbackList(ActiveVideoInputFeedback);
}
diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Cameras/CameraBase.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Cameras/CameraBase.cs
index def22069..18a1ad36 100644
--- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Cameras/CameraBase.cs
+++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Cameras/CameraBase.cs
@@ -216,21 +216,11 @@ namespace PepperDash.Essentials.Devices.Common.Cameras
var presetsCamera = cameraDevice as IHasCameraPresets;
presetsCamera.PresetsListHasChanged += new EventHandler((o, a) =>
{
- for (int i = 1; i <= joinMap.NumberOfPresets.JoinNumber; i++)
- {
- int tempNum = i - 1;
-
- string label = "";
-
- var preset = presetsCamera.Presets.FirstOrDefault(p => p.ID.Equals(i));
-
- if (preset != null)
- label = preset.Description;
-
- trilist.SetString((ushort) (joinMap.PresetLabelStart.JoinNumber + tempNum), label);
- }
+ SendCameraPresetNamesToApi(presetsCamera, joinMap, trilist);
});
+ SendCameraPresetNamesToApi(presetsCamera, joinMap, trilist);
+
for (int i = 0; i < joinMap.NumberOfPresets.JoinNumber; i++)
{
int tempNum = i;
@@ -246,10 +236,35 @@ namespace PepperDash.Essentials.Devices.Common.Cameras
presetsCamera.PresetStore(tempNum, label);
});
}
+ trilist.OnlineStatusChange += (sender, args) =>
+ {
+ if (!args.DeviceOnLine)
+ { return; }
+
+ SendCameraPresetNamesToApi(presetsCamera, joinMap, trilist);
+ };
+
+ }
+ }
+ private void SendCameraPresetNamesToApi(IHasCameraPresets presetsCamera, CameraControllerJoinMap joinMap, BasicTriList trilist)
+ {
+ for (int i = 1; i <= joinMap.NumberOfPresets.JoinNumber; i++)
+ {
+ int tempNum = i - 1;
+
+ string label = "";
+
+ var preset = presetsCamera.Presets.FirstOrDefault(p => p.ID.Equals(i));
+
+ if (preset != null)
+ label = preset.Description;
+
+ trilist.SetString((ushort)(joinMap.PresetLabelStart.JoinNumber + tempNum), label);
}
}
}
+
public class CameraPreset : PresetBase
{
public CameraPreset(int id, string description, bool isDefined, bool isDefinable)
diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Cameras/CameraVisca.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Cameras/CameraVisca.cs
index fb8ae79c..6acf5850 100644
--- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Cameras/CameraVisca.cs
+++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Cameras/CameraVisca.cs
@@ -525,6 +525,15 @@ namespace PepperDash.Essentials.Devices.Common.Cameras
public event EventHandler PresetsListHasChanged;
+ protected void OnPresetsListHasChanged()
+ {
+ var handler = PresetsListHasChanged;
+ if (handler == null)
+ return;
+
+ handler.Invoke(this, EventArgs.Empty);
+ }
+
public List Presets { get; private set; }
public void PresetSelect(int preset)
@@ -537,6 +546,7 @@ namespace PepperDash.Essentials.Devices.Common.Cameras
SavePreset(preset);
}
+
#endregion
#region IHasCameraFocusControl Members
diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/IHasDoNotDisturb.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/IHasDoNotDisturb.cs
new file mode 100644
index 00000000..7bb9b924
--- /dev/null
+++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/IHasDoNotDisturb.cs
@@ -0,0 +1,45 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Crestron.SimplSharp;
+
+using PepperDash.Essentials.Core;
+
+namespace PepperDash.Essentials.Devices.Common.Codec
+{
+ ///
+ /// Describes a device that has Do Not Disturb mode capability
+ ///
+ public interface IHasDoNotDisturbMode
+ {
+ ///
+ /// Indictes whether Do Not Disturb mode is on (Activated)
+ ///
+ BoolFeedback DoNotDisturbModeIsOnFeedback { get; }
+
+ ///
+ /// Activates Do Not Disturb mode
+ ///
+ void ActivateDoNotDisturbMode();
+
+ ///
+ /// Deactivates Do Not Disturb mode
+ ///
+ void DeactivateDoNotDisturbMode();
+
+ ///
+ /// Toggles Do Not Disturb mode
+ ///
+ void ToggleDoNotDisturbMode();
+ }
+
+ public interface IHasDoNotDisturbModeWithTimeout : IHasDoNotDisturbMode
+ {
+ ///
+ /// Activates Do Not Disturb mode with a timeout
+ ///
+ ///
+ void ActivateDoNotDisturbMode(int timeout);
+ }
+}
\ No newline at end of file
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 5a1a501b..c1873d26 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
@@ -108,6 +108,7 @@
+
@@ -117,6 +118,7 @@
+
@@ -127,6 +129,7 @@
+
diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoCodecJoinMap.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoCodecJoinMap.cs
new file mode 100644
index 00000000..e4945ce8
--- /dev/null
+++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoCodecJoinMap.cs
@@ -0,0 +1,134 @@
+using System;
+using PepperDash.Essentials.Core;
+using PepperDash.Essentials.Core.Bridges.JoinMaps;
+
+
+namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
+{
+ public class CiscoCodecJoinMap : VideoCodecControllerJoinMap
+ {
+ #region Digital
+
+ [JoinName("ActivateDoNotDisturbMode")]
+ public JoinDataComplete ActivateDoNotDisturbMode = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 221,
+ JoinSpan = 1
+ },
+ new JoinMetadata
+ {
+ Description = "Activates Do Not Disturb Mode. FB High if active.",
+ JoinCapabilities = eJoinCapabilities.ToFromSIMPL,
+ JoinType = eJoinType.Digital
+ });
+
+ [JoinName("DeactivateDoNotDisturbMode")]
+ public JoinDataComplete DeactivateDoNotDisturbMode = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 222,
+ JoinSpan = 1
+ },
+ new JoinMetadata
+ {
+ Description = "Deactivates Do Not Disturb Mode. FB High if deactivated.",
+ JoinCapabilities = eJoinCapabilities.ToFromSIMPL,
+ JoinType = eJoinType.Digital
+ });
+
+ [JoinName("ToggleDoNotDisturbMode")]
+ public JoinDataComplete ToggleDoNotDisturbMode = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 223,
+ JoinSpan = 1
+ },
+ new JoinMetadata
+ {
+ Description = "Toggles Do Not Disturb Mode.",
+ JoinCapabilities = eJoinCapabilities.ToSIMPL,
+ JoinType = eJoinType.Digital
+ });
+
+ [JoinName("ActivateStandby")]
+ public JoinDataComplete ActivateStandby = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 226,
+ JoinSpan = 1
+ },
+ new JoinMetadata
+ {
+ Description = "Activates Standby Mode. FB High if active.",
+ JoinCapabilities = eJoinCapabilities.ToFromSIMPL,
+ JoinType = eJoinType.Digital
+ });
+
+ [JoinName("DeactivateStandby")]
+ public JoinDataComplete DeactivateStandby = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 227,
+ JoinSpan = 1
+ },
+ new JoinMetadata
+ {
+ Description = "Deactivates Standby Mode. FB High if deactivated.",
+ JoinCapabilities = eJoinCapabilities.ToFromSIMPL,
+ JoinType = eJoinType.Digital
+ });
+
+ [JoinName("ActivateHalfWakeMode")]
+ public JoinDataComplete ActivateHalfWakeMode = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 228,
+ JoinSpan = 1
+ },
+ new JoinMetadata
+ {
+ Description = "Activates Half Wake Mode. FB High if active.",
+ JoinCapabilities = eJoinCapabilities.ToFromSIMPL,
+ JoinType = eJoinType.Digital
+ });
+
+ [JoinName("EnteringStandbyMode")]
+ public JoinDataComplete EnteringStandbyMode = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 229,
+ JoinSpan = 1
+ },
+ new JoinMetadata
+ {
+ Description = "High to indicate that the codec is entering standby mode",
+ JoinCapabilities = eJoinCapabilities.ToSIMPL,
+ JoinType = eJoinType.Digital
+ });
+
+ #endregion
+
+
+ #region Analog
+
+
+ #endregion
+
+
+ #region Serials
+
+
+ #endregion
+
+ public CiscoCodecJoinMap(uint joinStart)
+ : base(joinStart, typeof(CiscoCodecJoinMap))
+ {
+ }
+
+ public CiscoCodecJoinMap(uint joinStart, Type type)
+ : base(joinStart, type)
+ {
+ }
+ }
+}
\ No newline at end of file
diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoSparkCodec.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoSparkCodec.cs
index 8dac777c..cf48ba1d 100644
--- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoSparkCodec.cs
+++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoSparkCodec.cs
@@ -1,2199 +1,2198 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Text.RegularExpressions;
-using Crestron.SimplSharp;
-using Crestron.SimplSharpPro.CrestronThread;
-using Crestron.SimplSharpPro.DeviceSupport;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
-
-using PepperDash.Core;
-using PepperDash.Essentials.Core;
-using PepperDash.Essentials.Core.Bridges;
-using PepperDash.Essentials.Core.Config;
-using PepperDash.Essentials.Core.DeviceTypeInterfaces;
-using PepperDash.Essentials.Core.Routing;
-using PepperDash.Essentials.Devices.Common.Cameras;
-using PepperDash.Essentials.Devices.Common.Codec;
-using PepperDash.Essentials.Devices.Common.VideoCodec;
-using PepperDash.Essentials.Core.Queues;
-
-namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
-{
- enum eCommandType { SessionStart, SessionEnd, Command, GetStatus, GetConfiguration };
- public enum eExternalSourceType {camera, desktop, document_camera, mediaplayer, PC, whiteboard, other}
- public enum eExternalSourceMode {Ready, NotReady, Hidden, Error}
-
- public class CiscoSparkCodec : VideoCodecBase, IHasCallHistory, IHasCallFavorites, IHasDirectory,
- IHasScheduleAwareness, IOccupancyStatusProvider, IHasCodecLayouts, IHasCodecSelfView,
- ICommunicationMonitor, IRouting, IHasCodecCameras, IHasCameraAutoMode, IHasCodecRoomPresets, IHasExternalSourceSwitching, IHasBranding, IHasCameraOff, IHasCameraMute
- {
- private bool _externalSourceChangeRequested;
-
- public event EventHandler DirectoryResultReturned;
-
- private CTimer _brandingTimer;
-
- public CommunicationGather PortGather { get; private set; }
-
- public StatusMonitorBase CommunicationMonitor { get; private set; }
-
- private GenericQueue ReceiveQueue;
-
- public BoolFeedback PresentationViewMaximizedFeedback { get; private set; }
-
- string CurrentPresentationView;
-
- public BoolFeedback RoomIsOccupiedFeedback { get; private set; }
-
- public IntFeedback PeopleCountFeedback { get; private set; }
-
- public BoolFeedback CameraAutoModeIsOnFeedback { get; private set; }
-
- public BoolFeedback SelfviewIsOnFeedback { get; private set; }
-
- public StringFeedback SelfviewPipPositionFeedback { get; private set; }
-
- public StringFeedback LocalLayoutFeedback { get; private set; }
-
- public BoolFeedback LocalLayoutIsProminentFeedback { get; private set; }
-
- public BoolFeedback FarEndIsSharingContentFeedback { get; private set; }
-
- private CodecCommandWithLabel CurrentSelfviewPipPosition;
-
- private CodecCommandWithLabel CurrentLocalLayout;
-
- ///
- /// List the available positions for the selfview PIP window
- ///
- public List SelfviewPipPositions = new List()
- {
- new CodecCommandWithLabel("CenterLeft", "Center Left"),
- new CodecCommandWithLabel("CenterRight", "Center Right"),
- new CodecCommandWithLabel("LowerLeft", "Lower Left"),
- new CodecCommandWithLabel("LowerRight", "Lower Right"),
- new CodecCommandWithLabel("UpperCenter", "Upper Center"),
- new CodecCommandWithLabel("UpperLeft", "Upper Left"),
- new CodecCommandWithLabel("UpperRight", "Upper Right"),
- };
-
- ///
- /// Lists the available options for local layout
- ///
- public List LocalLayouts = new List()
- {
- //new CodecCommandWithLabel("auto", "Auto"),
- //new CiscoCodecLocalLayout("custom", "Custom"), // Left out for now
- new CodecCommandWithLabel("equal","Equal"),
- new CodecCommandWithLabel("overlay","Overlay"),
- new CodecCommandWithLabel("prominent","Prominent"),
- new CodecCommandWithLabel("single","Single")
- };
-
- private CiscoCodecConfiguration.RootObject CodecConfiguration = new CiscoCodecConfiguration.RootObject();
-
- private CiscoCodecStatus.RootObject CodecStatus = new CiscoCodecStatus.RootObject();
-
- public CodecCallHistory CallHistory { get; private set; }
-
- public CodecCallFavorites CallFavorites { get; private set; }
-
- ///
- /// The root level of the directory
- ///
- public CodecDirectory DirectoryRoot { get; private set; }
-
- ///
- /// Represents the current state of the directory and is computed on get
- ///
- public CodecDirectory CurrentDirectoryResult
- {
- get
- {
- if (DirectoryBrowseHistory.Count > 0)
- return DirectoryBrowseHistory[DirectoryBrowseHistory.Count - 1];
- else
- return DirectoryRoot;
- }
- }
-
- public BoolFeedback CurrentDirectoryResultIsNotDirectoryRoot { get; private set; }
-
- ///
- /// Tracks the directory browse history when browsing beyond the root directory
- ///
- public List DirectoryBrowseHistory { get; private set; }
-
- public CodecScheduleAwareness CodecSchedule { get; private set; }
-
- ///
- /// Gets and returns the scaled volume of the codec
- ///
- protected override Func VolumeLevelFeedbackFunc
- {
- get
- {
- return () => CrestronEnvironment.ScaleWithLimits(CodecStatus.Status.Audio.Volume.IntValue, 100, 0, 65535, 0);
- }
- }
-
- protected override Func PrivacyModeIsOnFeedbackFunc
- {
- get
- {
- return () => CodecStatus.Status.Audio.Microphones.Mute.BoolValue;
- }
- }
-
- protected override Func StandbyIsOnFeedbackFunc
- {
- get
- {
- return () => CodecStatus.Status.Standby.State.BoolValue;
- }
- }
-
- ///
- /// Gets the value of the currently shared source, or returns null
- ///
- protected override Func SharingSourceFeedbackFunc
- {
- get
- {
- return () => PresentationSourceKey;
- }
- }
-
- protected override Func SharingContentIsOnFeedbackFunc
- {
- get
- {
- return () => CodecStatus.Status.Conference.Presentation.Mode.BoolValue;
- }
- }
-
- protected Func FarEndIsSharingContentFeedbackFunc
- {
- get
- {
- return () => CodecStatus.Status.Conference.Presentation.Mode.Value == "Receiving";
- }
- }
-
- protected override Func MuteFeedbackFunc
- {
- get
- {
- return () => CodecStatus.Status.Audio.VolumeMute.BoolValue;
- }
- }
-
- protected Func RoomIsOccupiedFeedbackFunc
- {
- get
- {
- return () => CodecStatus.Status.RoomAnalytics.PeoplePresence.BoolValue;
- }
- }
-
- protected Func PeopleCountFeedbackFunc
- {
- get
- {
- return () => CodecStatus.Status.RoomAnalytics.PeopleCount.Current.IntValue;
- }
- }
-
- protected Func SpeakerTrackIsOnFeedbackFunc
- {
- get
- {
- return () => CodecStatus.Status.Cameras.SpeakerTrack.Status.BoolValue;
- }
- }
-
- protected Func SelfViewIsOnFeedbackFunc
- {
- get
- {
- return () => CodecStatus.Status.Video.Selfview.Mode.BoolValue;
- }
- }
-
- protected Func SelfviewPipPositionFeedbackFunc
- {
- get
- {
- return () => CurrentSelfviewPipPosition.Label;
- }
- }
-
- protected Func LocalLayoutFeedbackFunc
- {
- get
- {
- return () => CurrentLocalLayout.Label;
- }
- }
-
- protected Func LocalLayoutIsProminentFeedbackFunc
- {
- get
- {
- return () => CurrentLocalLayout.Label == "Prominent";
- }
- }
-
-
- private string CliFeedbackRegistrationExpression;
-
- private CodecSyncState SyncState;
-
- public CodecPhonebookSyncState PhonebookSyncState { get; private set; }
-
- private StringBuilder JsonMessage;
-
- private bool JsonFeedbackMessageIsIncoming;
-
- public bool CommDebuggingIsOn;
-
- string Delimiter = "\r\n";
-
- ///
- /// Used to track the current connector used for the presentation source
- ///
- int PresentationSource;
-
- string PresentationSourceKey;
-
- string PhonebookMode = "Local"; // Default to Local
-
- uint PhonebookResultsLimit = 255; // Could be set later by config.
-
- CTimer LoginMessageReceivedTimer;
- CTimer RetryConnectionTimer;
-
- // **___________________________________________________________________**
- // Timers to be moved to the global system timer at a later point....
- CTimer BookingsRefreshTimer;
- CTimer PhonebookRefreshTimer;
- // **___________________________________________________________________**
-
- public RoutingInputPort CodecOsdIn { get; private set; }
- public RoutingInputPort HdmiIn2 { get; private set; }
- public RoutingInputPort HdmiIn3 { get; private set; }
- public RoutingOutputPort HdmiOut1 { get; private set; }
- public RoutingOutputPort HdmiOut2 { get; private set; }
-
-
- // Constructor for IBasicCommunication
- public CiscoSparkCodec(DeviceConfig config, IBasicCommunication comm)
- : base(config)
- {
- var props = JsonConvert.DeserializeObject(config.Properties.ToString());
-
- // Use the configured phonebook results limit if present
- if (props.PhonebookResultsLimit > 0)
- {
- PhonebookResultsLimit = props.PhonebookResultsLimit;
- }
-
- // The queue that will collect the repsonses in the order they are received
- ReceiveQueue = new GenericQueue(this.Key + "-rxQueue", 25);
-
- RoomIsOccupiedFeedback = new BoolFeedback(RoomIsOccupiedFeedbackFunc);
- PeopleCountFeedback = new IntFeedback(PeopleCountFeedbackFunc);
- CameraAutoModeIsOnFeedback = new BoolFeedback(SpeakerTrackIsOnFeedbackFunc);
- SelfviewIsOnFeedback = new BoolFeedback(SelfViewIsOnFeedbackFunc);
- SelfviewPipPositionFeedback = new StringFeedback(SelfviewPipPositionFeedbackFunc);
- LocalLayoutFeedback = new StringFeedback(LocalLayoutFeedbackFunc);
- LocalLayoutIsProminentFeedback = new BoolFeedback(LocalLayoutIsProminentFeedbackFunc);
- FarEndIsSharingContentFeedback = new BoolFeedback(FarEndIsSharingContentFeedbackFunc);
- CameraIsOffFeedback = new BoolFeedback(() => CodecStatus.Status.Video.Input.MainVideoMute.BoolValue);
- CameraIsMutedFeedback = CameraIsOffFeedback;
- SupportsCameraOff = true;
-
- PresentationViewMaximizedFeedback = new BoolFeedback(() => CurrentPresentationView == "Maximized");
-
- Communication = comm;
-
- if (props.CommunicationMonitorProperties != null)
- {
- CommunicationMonitor = new GenericCommunicationMonitor(this, Communication, props.CommunicationMonitorProperties);
- }
- else
- {
- CommunicationMonitor = new GenericCommunicationMonitor(this, Communication, 30000, 120000, 300000, "xStatus SystemUnit Software Version\r");
- }
-
- if (props.Sharing != null)
- AutoShareContentWhileInCall = props.Sharing.AutoShareContentWhileInCall;
-
- ShowSelfViewByDefault = props.ShowSelfViewByDefault;
-
- DeviceManager.AddDevice(CommunicationMonitor);
-
- PhonebookMode = props.PhonebookMode;
-
- SyncState = new CodecSyncState(Key + "--Sync");
-
- PhonebookSyncState = new CodecPhonebookSyncState(Key + "--PhonebookSync");
-
- SyncState.InitialSyncCompleted += new EventHandler(SyncState_InitialSyncCompleted);
-
- PortGather = new CommunicationGather(Communication, Delimiter);
- PortGather.IncludeDelimiter = true;
- PortGather.LineReceived += this.Port_LineReceived;
-
- CodecInfo = new CiscoCodecInfo(CodecStatus, CodecConfiguration);
-
- CallHistory = new CodecCallHistory();
-
-
- if (props.Favorites != null)
- {
- CallFavorites = new CodecCallFavorites();
- CallFavorites.Favorites = props.Favorites;
- }
-
- DirectoryRoot = new CodecDirectory();
-
- DirectoryBrowseHistory = new List();
-
- CurrentDirectoryResultIsNotDirectoryRoot = new BoolFeedback(() => DirectoryBrowseHistory.Count > 0);
-
- CurrentDirectoryResultIsNotDirectoryRoot.FireUpdate();
-
- CodecSchedule = new CodecScheduleAwareness();
-
- //Set Feedback Actions
- SetFeedbackActions();
-
- CodecOsdIn = new RoutingInputPort(RoutingPortNames.CodecOsd, eRoutingSignalType.Audio | eRoutingSignalType.Video,
- eRoutingPortConnectionType.Hdmi, new Action(StopSharing), this);
- HdmiIn2 = new RoutingInputPort(RoutingPortNames.HdmiIn2, eRoutingSignalType.Audio | eRoutingSignalType.Video,
- eRoutingPortConnectionType.Hdmi, new Action(SelectPresentationSource1), this);
- HdmiIn3 = new RoutingInputPort(RoutingPortNames.HdmiIn3, eRoutingSignalType.Audio | eRoutingSignalType.Video,
- eRoutingPortConnectionType.Hdmi, new Action(SelectPresentationSource2), this);
-
- HdmiOut1 = new RoutingOutputPort(RoutingPortNames.HdmiOut1, eRoutingSignalType.Audio | eRoutingSignalType.Video,
- eRoutingPortConnectionType.Hdmi, null, this);
- HdmiOut2 = new RoutingOutputPort(RoutingPortNames.HdmiOut2, eRoutingSignalType.Audio | eRoutingSignalType.Video,
- eRoutingPortConnectionType.Hdmi, null, this);
-
- InputPorts.Add(CodecOsdIn);
- InputPorts.Add(HdmiIn2);
- InputPorts.Add(HdmiIn3);
- OutputPorts.Add(HdmiOut1);
-
- SetUpCameras();
-
- CreateOsdSource();
-
- ExternalSourceListEnabled = props.ExternalSourceListEnabled;
- ExternalSourceInputPort = props.ExternalSourceInputPort;
-
- if (props.UiBranding == null)
- {
- return;
- }
- Debug.Console(2, this, "Setting branding properties enable: {0} _brandingUrl {1}", props.UiBranding.Enable,
- props.UiBranding.BrandingUrl);
-
- BrandingEnabled = props.UiBranding.Enable;
-
- _brandingUrl = props.UiBranding.BrandingUrl;
- }
-
- private void SetFeedbackActions()
- {
- CodecStatus.Status.Audio.Volume.ValueChangedAction = VolumeLevelFeedback.FireUpdate;
- CodecStatus.Status.Audio.VolumeMute.ValueChangedAction = MuteFeedback.FireUpdate;
- CodecStatus.Status.Audio.Microphones.Mute.ValueChangedAction = PrivacyModeIsOnFeedback.FireUpdate;
- CodecStatus.Status.Standby.State.ValueChangedAction = StandbyIsOnFeedback.FireUpdate;
- CodecStatus.Status.RoomAnalytics.PeoplePresence.ValueChangedAction = RoomIsOccupiedFeedback.FireUpdate;
- CodecStatus.Status.RoomAnalytics.PeopleCount.Current.ValueChangedAction = PeopleCountFeedback.FireUpdate;
- CodecStatus.Status.Cameras.SpeakerTrack.Status.ValueChangedAction = CameraAutoModeIsOnFeedback.FireUpdate;
- CodecStatus.Status.Cameras.SpeakerTrack.Availability.ValueChangedAction = () => { SupportsCameraAutoMode = CodecStatus.Status.Cameras.SpeakerTrack.Availability.BoolValue; };
- CodecStatus.Status.Video.Selfview.Mode.ValueChangedAction = SelfviewIsOnFeedback.FireUpdate;
- CodecStatus.Status.Video.Selfview.PIPPosition.ValueChangedAction = ComputeSelfviewPipStatus;
- CodecStatus.Status.Video.Layout.LayoutFamily.Local.ValueChangedAction = ComputeLocalLayout;
- CodecStatus.Status.Conference.Presentation.Mode.ValueChangedAction = () =>
- {
- SharingContentIsOnFeedback.FireUpdate();
- FarEndIsSharingContentFeedback.FireUpdate();
- };
-
- try
- {
- CodecStatus.Status.Video.Input.MainVideoMute.ValueChangedAction = CameraIsOffFeedback.FireUpdate;
- }
- catch (Exception ex)
- {
- Debug.Console(0, this, "Error setting MainVideuMute Action: {0}", ex);
-
- if (ex.InnerException != null)
- {
- Debug.Console(0, this, "Error setting MainVideuMute Action: {0}", ex);
- }
- }
- }
-
- ///
- /// Creates the fake OSD source, and connects it's AudioVideo output to the CodecOsdIn input
- /// to enable routing
- ///
- void CreateOsdSource()
- {
- OsdSource = new DummyRoutingInputsDevice(Key + "[osd]");
- DeviceManager.AddDevice(OsdSource);
- var tl = new TieLine(OsdSource.AudioVideoOutputPort, CodecOsdIn);
- TieLineCollection.Default.Add(tl);
- }
-
- public void InitializeBranding(string roomKey)
- {
- Debug.Console(1, this, "Initializing Branding for room {0}", roomKey);
-
- if (!BrandingEnabled)
- {
- return;
- }
-
- var mcBridgeKey = String.Format("mobileControlBridge-{0}", roomKey);
-
- var mcBridge = DeviceManager.GetDeviceForKey(mcBridgeKey) as IMobileControlRoomBridge;
-
- if (!String.IsNullOrEmpty(_brandingUrl))
- {
- Debug.Console(1, this, "Branding URL found: {0}", _brandingUrl);
- if (_brandingTimer != null)
- {
- _brandingTimer.Stop();
- _brandingTimer.Dispose();
- }
-
- _brandingTimer = new CTimer((o) =>
- {
- if (_sendMcUrl)
- {
- SendMcBrandingUrl(mcBridge);
- _sendMcUrl = false;
- }
- else
- {
- SendBrandingUrl();
- _sendMcUrl = true;
- }
- }, 0, 15000);
- } else if (String.IsNullOrEmpty(_brandingUrl))
- {
- Debug.Console(1, this, "No Branding URL found");
- if (mcBridge == null) return;
-
- Debug.Console(2, this, "Setting QR code URL: {0}", mcBridge.QrCodeUrl);
-
- mcBridge.UserCodeChanged += (o, a) => SendMcBrandingUrl(mcBridge);
- mcBridge.UserPromptedForCode += (o, a) => DisplayUserCode(mcBridge.UserCode);
-
- SendMcBrandingUrl(mcBridge);
- }
- }
-
- ///
- /// Displays the code for the specified duration
- ///
- /// Mobile Control user code
- private void DisplayUserCode(string code)
- {
- SendText(string.Format("xcommand userinterface message alert display title:\"Mobile Control User Code:\" text:\"{0}\" duration: 30", code));
- }
-
- private void SendMcBrandingUrl(IMobileControlRoomBridge mcBridge)
- {
- if (mcBridge == null)
- {
- return;
- }
-
- Debug.Console(1, this, "Sending url: {0}", mcBridge.QrCodeUrl);
-
- SendText("xconfiguration userinterface custommessage: \"Scan the QR code with a mobile phone to get started\"");
- SendText("xconfiguration userinterface osd halfwakemessage: \"Tap the touch panel or scan the QR code with a mobile phone to get started\"");
-
- var checksum = !String.IsNullOrEmpty(mcBridge.QrCodeChecksum)
- ? String.Format("checksum: {0} ", mcBridge.QrCodeChecksum)
- : String.Empty;
-
- SendText(String.Format(
- "xcommand userinterface branding fetch {1}type: branding url: {0}",
- mcBridge.QrCodeUrl, checksum));
- SendText(String.Format(
- "xcommand userinterface branding fetch {1}type: halfwakebranding url: {0}",
- mcBridge.QrCodeUrl, checksum));
- }
-
- private void SendBrandingUrl()
- {
- Debug.Console(1, this, "Sending url: {0}", _brandingUrl);
-
- SendText(String.Format("xcommand userinterface branding fetch type: branding url: {0}",
- _brandingUrl));
- SendText(String.Format("xcommand userinterface branding fetch type: halfwakebranding url: {0}",
- _brandingUrl));
- }
- ///
- /// Starts the HTTP feedback server and syncronizes state of codec
- ///
- ///
- public override bool CustomActivate()
- {
- CrestronConsole.AddNewConsoleCommand(SetCommDebug, "SetCodecCommDebug", "0 for Off, 1 for on", ConsoleAccessLevelEnum.AccessOperator);
- CrestronConsole.AddNewConsoleCommand(GetPhonebook, "GetCodecPhonebook", "Triggers a refresh of the codec phonebook", ConsoleAccessLevelEnum.AccessOperator);
- CrestronConsole.AddNewConsoleCommand(GetBookings, "GetCodecBookings", "Triggers a refresh of the booking data for today", ConsoleAccessLevelEnum.AccessOperator);
-
- return base.CustomActivate();
- }
-
- #region Overrides of Device
-
- public override void Initialize()
- {
- var socket = Communication as ISocketStatus;
- if (socket != null)
- {
- socket.ConnectionChange += new EventHandler(socket_ConnectionChange);
- }
-
- Communication.Connect();
-
- CommunicationMonitor.Start();
-
- const string prefix = "xFeedback register ";
-
- CliFeedbackRegistrationExpression =
- prefix + "/Configuration" + Delimiter +
- prefix + "/Status/Audio" + Delimiter +
- prefix + "/Status/Call" + Delimiter +
- prefix + "/Status/Conference/Presentation" + Delimiter +
- prefix + "/Status/Cameras/SpeakerTrack" + Delimiter +
- prefix + "/Status/RoomAnalytics" + Delimiter +
- prefix + "/Status/RoomPreset" + Delimiter +
- prefix + "/Status/Standby" + Delimiter +
- prefix + "/Status/Video/Selfview" + Delimiter +
- prefix + "/Status/Video/Layout" + Delimiter +
- prefix + "/Status/Video/Input/MainVideoMute" + Delimiter +
- prefix + "/Bookings" + Delimiter +
- prefix + "/Event/CallDisconnect" + Delimiter +
- prefix + "/Event/Bookings" + Delimiter +
- prefix + "/Event/CameraPresetListUpdated" + Delimiter +
- prefix + "/Event/UserInterface/Presentation/ExternalSource/Selected/SourceIdentifier" + Delimiter;
- }
-
- #endregion
-
- ///
- /// Fires when initial codec sync is completed. Used to then send commands to get call history, phonebook, bookings, etc.
- ///
- ///
- ///
- void SyncState_InitialSyncCompleted(object sender, EventArgs e)
- {
- // Fire the ready event
- SetIsReady();
- //CommDebuggingIsOn = false;
-
- GetCallHistory();
-
- PhonebookRefreshTimer = new CTimer(CheckCurrentHour, 3600000, 3600000); // check each hour to see if the phonebook should be downloaded
- GetPhonebook(null);
-
- BookingsRefreshTimer = new CTimer(GetBookings, 900000, 900000); // 15 minute timer to check for new booking info
- GetBookings(null);
- }
-
- public void SetCommDebug(string s)
- {
- if (s == "1")
- {
- CommDebuggingIsOn = true;
- Debug.Console(0, this, "Comm Debug Enabled.");
- }
- else
- {
- CommDebuggingIsOn = false;
- Debug.Console(0, this, "Comm Debug Disabled.");
- }
- }
-
- void socket_ConnectionChange(object sender, GenericSocketStatusChageEventArgs e)
- {
- Debug.Console(1, this, "Socket status change {0}", e.Client.ClientStatus);
- if (e.Client.IsConnected)
- {
- if(!SyncState.LoginMessageWasReceived)
- LoginMessageReceivedTimer = new CTimer(o => DisconnectClientAndReconnect(), 5000);
- }
- else
- {
- SyncState.CodecDisconnected();
- PhonebookSyncState.CodecDisconnected();
-
- if (PhonebookRefreshTimer != null)
- {
- PhonebookRefreshTimer.Stop();
- PhonebookRefreshTimer = null;
- }
-
- if (BookingsRefreshTimer != null)
- {
- BookingsRefreshTimer.Stop();
- BookingsRefreshTimer = null;
- }
- }
- }
-
- void DisconnectClientAndReconnect()
- {
- Debug.Console(1, this, "Retrying connection to codec.");
-
- Communication.Disconnect();
-
- RetryConnectionTimer = new CTimer(o => Communication.Connect(), 2000);
-
- //CrestronEnvironment.Sleep(2000);
-
- //Communication.Connect();
- }
-
- ///
- /// Gathers responses from the codec (including the delimiter. Responses are checked to see if they contain JSON data and if so, the data is collected until a complete JSON
- /// message is received before forwarding the message to be deserialized.
- ///
- ///
- ///
- void Port_LineReceived(object dev, GenericCommMethodReceiveTextArgs args)
- {
- if (CommDebuggingIsOn)
- {
- if (!JsonFeedbackMessageIsIncoming)
- Debug.Console(1, this, "RX: '{0}'", args.Text);
- }
-
- if (args.Text == "{" + Delimiter) // Check for the beginning of a new JSON message
- {
- JsonFeedbackMessageIsIncoming = true;
-
- if (CommDebuggingIsOn)
- Debug.Console(1, this, "Incoming JSON message...");
-
- JsonMessage = new StringBuilder();
- }
- else if (args.Text == "}" + Delimiter) // Check for the end of a JSON message
- {
- JsonFeedbackMessageIsIncoming = false;
-
- JsonMessage.Append(args.Text);
-
- if (CommDebuggingIsOn)
- Debug.Console(1, this, "Complete JSON Received:\n{0}", JsonMessage.ToString());
-
- // Enqueue the complete message to be deserialized
-
- ReceiveQueue.Enqueue(new ProcessStringMessage(JsonMessage.ToString(), DeserializeResponse));
-
- return;
- }
-
- if(JsonFeedbackMessageIsIncoming)
- {
- JsonMessage.Append(args.Text);
-
- //Debug.Console(1, this, "Building JSON:\n{0}", JsonMessage.ToString());
- return;
- }
-
- if (!SyncState.InitialSyncComplete)
- {
- switch (args.Text.Trim().ToLower()) // remove the whitespace
- {
- case "*r login successful":
- {
- SyncState.LoginMessageReceived();
-
- if(LoginMessageReceivedTimer != null)
- LoginMessageReceivedTimer.Stop();
-
- SendText("xPreferences outputmode json");
- break;
- }
- case "xpreferences outputmode json":
- {
- if (!SyncState.InitialStatusMessageWasReceived)
- SendText("xStatus");
- break;
- }
- case "xfeedback register /event/calldisconnect":
- {
- SyncState.FeedbackRegistered();
- break;
- }
- }
- }
-
-
- }
-
- ///
- /// Appends the delimiter and send the command to the codec
- ///
- ///
- public void SendText(string command)
- {
- if (CommDebuggingIsOn)
- Debug.Console(1, this, "Sending: '{0}'", command);
-
- Communication.SendText(command + Delimiter);
- }
-
- void DeserializeResponse(string response)
- {
- try
- {
- //// Serializer settings. We want to ignore null values and missing members
- //JsonSerializerSettings settings = new JsonSerializerSettings();
- //settings.NullValueHandling = NullValueHandling.Ignore;
- //settings.MissingMemberHandling = MissingMemberHandling.Ignore;
- //settings.ObjectCreationHandling = ObjectCreationHandling.Auto;
-
- if (response.IndexOf("\"Status\":{") > -1 || response.IndexOf("\"Status\": {") > -1)
- {
- // Status Message
-
- // Temp object so we can inpsect for call data before simply deserializing
- CiscoCodecStatus.RootObject tempCodecStatus = new CiscoCodecStatus.RootObject();
-
- JsonConvert.PopulateObject(response, tempCodecStatus);
-
- // Check to see if the message contains /Status/Conference/Presentation/LocalInstance and extract source value
- var conference = tempCodecStatus.Status.Conference;
-
- if (conference.Presentation.LocalInstance.Count > 0)
- {
- if (!string.IsNullOrEmpty(conference.Presentation.LocalInstance[0].ghost))
- PresentationSource = 0;
- else if (conference.Presentation.LocalInstance[0].Source != null)
- {
- PresentationSource = conference.Presentation.LocalInstance[0].Source.IntValue;
- }
- }
-
- // Check to see if this is a call status message received after the initial status message
- if (tempCodecStatus.Status.Call.Count > 0)
- {
- // Iterate through the call objects in the response
- foreach (CiscoCodecStatus.Call call in tempCodecStatus.Status.Call)
- {
- var tempActiveCall = ActiveCalls.FirstOrDefault(c => c.Id.Equals(call.id));
-
- if (tempActiveCall != null)
- {
- bool changeDetected = false;
-
- eCodecCallStatus newStatus = eCodecCallStatus.Unknown;
-
- // Update properties of ActiveCallItem
- if(call.Status != null)
- if (!string.IsNullOrEmpty(call.Status.Value))
- {
- tempActiveCall.Status = CodecCallStatus.ConvertToStatusEnum(call.Status.Value);
-
- if (newStatus == eCodecCallStatus.Connected)
- GetCallHistory();
-
- changeDetected = true;
- }
- if (call.CallType != null)
- if (!string.IsNullOrEmpty(call.CallType.Value))
- {
- tempActiveCall.Type = CodecCallType.ConvertToTypeEnum(call.CallType.Value);
- changeDetected = true;
- }
- if (call.DisplayName != null)
- if (!string.IsNullOrEmpty(call.DisplayName.Value))
- {
- tempActiveCall.Name = call.DisplayName.Value;
- changeDetected = true;
- }
- if (call.Direction != null)
- {
- if (!string.IsNullOrEmpty(call.Direction.Value))
- {
- tempActiveCall.Direction = CodecCallDirection.ConvertToDirectionEnum(call.Direction.Value);
- changeDetected = true;
- }
- }
-
- if (changeDetected)
- {
- SetSelfViewMode();
- OnCallStatusChange(tempActiveCall);
- ListCalls();
- }
- }
- else if( call.ghost == null ) // if the ghost value is present the call has ended already
- {
- // Create a new call item
- var newCallItem = new CodecActiveCallItem()
- {
- Id = call.id,
- Status = CodecCallStatus.ConvertToStatusEnum(call.Status.Value),
- Name = call.DisplayName.Value,
- Number = call.RemoteNumber.Value,
- Type = CodecCallType.ConvertToTypeEnum(call.CallType.Value),
- Direction = CodecCallDirection.ConvertToDirectionEnum(call.Direction.Value)
- };
-
- // Add it to the ActiveCalls List
- ActiveCalls.Add(newCallItem);
-
- ListCalls();
-
- SetSelfViewMode();
- OnCallStatusChange(newCallItem);
- }
-
- }
-
- }
-
- // Check for Room Preset data (comes in partial, so we need to handle these responses differently to prevent appending duplicate items
- var tempPresets = tempCodecStatus.Status.RoomPreset;
-
- if (tempPresets.Count > 0)
- {
- // Create temporary list to store the existing items from the CiscoCodecStatus.RoomPreset collection
- List existingRoomPresets = new List();
- // Add the existing items to the temporary list
- existingRoomPresets.AddRange(CodecStatus.Status.RoomPreset);
- // Populate the CodecStatus object (this will append new values to the RoomPreset collection
- JsonConvert.PopulateObject(response, CodecStatus);
-
- JObject jResponse = JObject.Parse(response);
-
- IList roomPresets = jResponse["Status"]["RoomPreset"].Children().ToList();
- // Iterate the new items in this response agains the temporary list. Overwrite any existing items and add new ones.
- foreach (var preset in tempPresets)
- {
- // First fine the existing preset that matches the id
- var existingPreset = existingRoomPresets.FirstOrDefault(p => p.id.Equals(preset.id));
- if (existingPreset != null)
- {
- Debug.Console(1, this, "Existing Room Preset with ID: {0} found. Updating.", existingPreset.id);
-
- JToken updatedPreset = null;
-
- // Find the JToken from the response with the matching id
- foreach (var jPreset in roomPresets)
- {
- if (jPreset["id"].Value() == existingPreset.id)
- updatedPreset = jPreset;
- }
-
- if (updatedPreset != null)
- {
- // use PopulateObject to overlay the partial data onto the existing object
- JsonConvert.PopulateObject(updatedPreset.ToString(), existingPreset);
- }
-
- }
- else
- {
- Debug.Console(1, this, "New Room Preset with ID: {0}. Adding.", preset.id);
- existingRoomPresets.Add(preset);
- }
- }
-
- // Replace the list in the CodecStatus object with the processed list
- CodecStatus.Status.RoomPreset = existingRoomPresets;
-
- // Generecise the list
- NearEndPresets = RoomPresets.GetGenericPresets(CodecStatus.Status.RoomPreset);
-
- var handler = CodecRoomPresetsListHasChanged;
- if (handler != null)
- {
- handler(this, new EventArgs());
- }
- }
- else
- {
- JsonConvert.PopulateObject(response, CodecStatus);
- }
-
- if (!SyncState.InitialStatusMessageWasReceived)
- {
- SyncState.InitialStatusMessageReceived();
-
- if (!SyncState.InitialConfigurationMessageWasReceived)
- SendText("xConfiguration");
- }
- }
- else if (response.IndexOf("\"Configuration\":{") > -1 || response.IndexOf("\"Configuration\": {") > -1)
- {
- // Configuration Message
-
- JsonConvert.PopulateObject(response, CodecConfiguration);
-
- if (!SyncState.InitialConfigurationMessageWasReceived)
- {
- SyncState.InitialConfigurationMessageReceived();
- if (!SyncState.FeedbackWasRegistered)
- {
- SendText(CliFeedbackRegistrationExpression);
- }
- }
-
- }
- else if (response.IndexOf("\"Event\":{") > -1 || response.IndexOf("\"Event\": {") > -1)
- {
- if (response.IndexOf("\"CallDisconnect\":{") > -1 || response.IndexOf("\"CallDisconnect\": {") > -1)
- {
- CiscoCodecEvents.RootObject eventReceived = new CiscoCodecEvents.RootObject();
-
- JsonConvert.PopulateObject(response, eventReceived);
-
- EvalutateDisconnectEvent(eventReceived);
- }
- else if (response.IndexOf("\"Bookings\":{") > -1 || response.IndexOf("\"Bookings\": {") > -1) // The list has changed, reload it
- {
- GetBookings(null);
- }
-
- else if (response.IndexOf("\"UserInterface\":{") > -1 || response.IndexOf("\"UserInterface\": {") > -1) // External Source Trigger
- {
- CiscoCodecEvents.RootObject eventReceived = new CiscoCodecEvents.RootObject();
- JsonConvert.PopulateObject(response, eventReceived);
- Debug.Console(2, this, "*** Got an External Source Selection {0} {1}", eventReceived, eventReceived.Event.UserInterface, eventReceived.Event.UserInterface.Presentation.ExternalSource.Selected.SourceIdentifier.Value);
-
- if (RunRouteAction != null && !_externalSourceChangeRequested)
- {
- RunRouteAction(eventReceived.Event.UserInterface.Presentation.ExternalSource.Selected.SourceIdentifier.Value, null);
- }
-
- _externalSourceChangeRequested = false;
- }
- }
- else if (response.IndexOf("\"CommandResponse\":{") > -1 || response.IndexOf("\"CommandResponse\": {") > -1)
- {
- // CommandResponse Message
-
- if (response.IndexOf("\"CallHistoryRecentsResult\":{") > -1 || response.IndexOf("\"CallHistoryRecentsResult\": {") > -1)
- {
- var codecCallHistory = new CiscoCallHistory.RootObject();
-
- JsonConvert.PopulateObject(response, codecCallHistory);
-
- CallHistory.ConvertCiscoCallHistoryToGeneric(codecCallHistory.CommandResponse.CallHistoryRecentsResult.Entry);
- }
- else if (response.IndexOf("\"CallHistoryDeleteEntryResult\":{") > -1 || response.IndexOf("\"CallHistoryDeleteEntryResult\": {") > -1)
- {
- GetCallHistory();
- }
- else if (response.IndexOf("\"PhonebookSearchResult\":{") > -1 || response.IndexOf("\"PhonebookSearchResult\": {") > -1)
- {
- var codecPhonebookResponse = new CiscoCodecPhonebook.RootObject();
-
- JsonConvert.PopulateObject(response, codecPhonebookResponse);
-
- if (!PhonebookSyncState.InitialPhonebookFoldersWasReceived)
- {
- // Check if the phonebook has any folders
- PhonebookSyncState.InitialPhonebookFoldersReceived();
-
- PhonebookSyncState.SetPhonebookHasFolders(codecPhonebookResponse.CommandResponse.PhonebookSearchResult.Folder.Count > 0);
-
- if (PhonebookSyncState.PhonebookHasFolders)
- {
- DirectoryRoot.AddFoldersToDirectory(CiscoCodecPhonebook.GetRootFoldersFromSearchResult(codecPhonebookResponse.CommandResponse.PhonebookSearchResult));
- }
-
- // Get the number of contacts in the phonebook
- GetPhonebookContacts();
- }
- else if (!PhonebookSyncState.NumberOfContactsWasReceived)
- {
- // Store the total number of contacts in the phonebook
- PhonebookSyncState.SetNumberOfContacts(Int32.Parse(codecPhonebookResponse.CommandResponse.PhonebookSearchResult.ResultInfo.TotalRows.Value));
-
- DirectoryRoot.AddContactsToDirectory(CiscoCodecPhonebook.GetRootContactsFromSearchResult(codecPhonebookResponse.CommandResponse.PhonebookSearchResult));
-
- PhonebookSyncState.PhonebookRootEntriesReceived();
-
- PrintDirectory(DirectoryRoot);
- }
- else if (PhonebookSyncState.InitialSyncComplete)
- {
- var directoryResults = new CodecDirectory();
-
- if(codecPhonebookResponse.CommandResponse.PhonebookSearchResult.ResultInfo.TotalRows.Value != "0")
- directoryResults = CiscoCodecPhonebook.ConvertCiscoPhonebookToGeneric(codecPhonebookResponse.CommandResponse.PhonebookSearchResult);
-
- PrintDirectory(directoryResults);
-
- DirectoryBrowseHistory.Add(directoryResults);
-
- OnDirectoryResultReturned(directoryResults);
-
- }
- }
- else if (response.IndexOf("\"BookingsListResult\":{") > -1)
- {
- var codecBookings = new CiscoCodecBookings.RootObject();
-
- JsonConvert.PopulateObject(response, codecBookings);
-
- if(codecBookings.CommandResponse.BookingsListResult.ResultInfo.TotalRows.Value != "0")
- CodecSchedule.Meetings = CiscoCodecBookings.GetGenericMeetingsFromBookingResult(codecBookings.CommandResponse.BookingsListResult.Booking);
-
- BookingsRefreshTimer.Reset(900000, 900000);
- }
-
- }
-
- }
- catch (Exception ex)
- {
- Debug.Console(1, this, "Error Deserializing feedback from codec: {0}", ex);
- }
- }
-
- ///
- /// Call when directory results are updated
- ///
- ///
- void OnDirectoryResultReturned(CodecDirectory result)
- {
- CurrentDirectoryResultIsNotDirectoryRoot.FireUpdate();
-
- // This will return the latest results to all UIs. Multiple indendent UI Directory browsing will require a different methodology
- var handler = DirectoryResultReturned;
- if (handler != null)
- {
- handler(this, new DirectoryEventArgs()
- {
- Directory = result,
- DirectoryIsOnRoot = !CurrentDirectoryResultIsNotDirectoryRoot.BoolValue
- });
- }
-
- PrintDirectory(result);
- }
-
- ///
- /// Evaluates an event received from the codec
- ///
- ///
- void EvalutateDisconnectEvent(CiscoCodecEvents.RootObject eventReceived)
- {
- if (eventReceived.Event.CallDisconnect != null)
- {
- var tempActiveCall = ActiveCalls.FirstOrDefault(c => c.Id.Equals(eventReceived.Event.CallDisconnect.CallId.Value));
-
- // Remove the call from the Active calls list
- if (tempActiveCall != null)
- {
- ActiveCalls.Remove(tempActiveCall);
-
- ListCalls();
-
- SetSelfViewMode();
- // Notify of the call disconnection
- SetNewCallStatusAndFireCallStatusChange(eCodecCallStatus.Disconnected, tempActiveCall);
-
- GetCallHistory();
- }
- }
- }
-
- ///
- ///
- ///
- ///
- public override void ExecuteSwitch(object selector)
- {
- (selector as Action)();
- PresentationSourceKey = selector.ToString();
- }
-
- ///
- /// This is necessary for devices that are "routers" in the middle of the path, even though it only has one output and
- /// may only have one input.
- ///
- public void ExecuteSwitch(object inputSelector, object outputSelector, eRoutingSignalType signalType)
- {
- ExecuteSwitch(inputSelector);
- PresentationSourceKey = inputSelector.ToString();
- }
-
-
- ///
- /// Gets the ID of the last connected call
- ///
- ///
- public string GetCallId()
- {
- string callId = null;
-
- if (ActiveCalls.Count > 1)
- {
- var lastCallIndex = ActiveCalls.Count - 1;
- callId = ActiveCalls[lastCallIndex].Id;
- }
- else if (ActiveCalls.Count == 1)
- callId = ActiveCalls[0].Id;
-
- return callId;
-
- }
-
- public void GetCallHistory()
- {
- SendText("xCommand CallHistory Recents Limit: 20 Order: OccurrenceTime");
- }
-
- ///
- /// Required for IHasScheduleAwareness
- ///
- public void GetSchedule()
- {
- GetBookings(null);
- }
-
- ///
- /// Gets the bookings for today
- ///
- ///
- public void GetBookings(object command)
- {
- Debug.Console(1, this, "Retrieving Booking Info from Codec. Current Time: {0}", DateTime.Now.ToLocalTime());
-
- SendText("xCommand Bookings List Days: 1 DayOffset: 0");
- }
-
- ///
- /// Checks to see if it is 2am (or within that hour) and triggers a download of the phonebook
- ///
- ///
- public void CheckCurrentHour(object o)
- {
- if (DateTime.Now.Hour == 2)
- {
- Debug.Console(1, this, "Checking hour to see if phonebook should be downloaded. Current hour is {0}", DateTime.Now.Hour);
-
- GetPhonebook(null);
- PhonebookRefreshTimer.Reset(3600000, 3600000);
- }
- }
-
- ///
- /// Triggers a refresh of the codec phonebook
- ///
- /// Just to allow this method to be called from a console command
- public void GetPhonebook(string command)
- {
- PhonebookSyncState.CodecDisconnected();
-
- DirectoryRoot = new CodecDirectory();
-
- GetPhonebookFolders();
- }
-
- private void GetPhonebookFolders()
- {
- // Get Phonebook Folders (determine local/corporate from config, and set results limit)
- SendText(string.Format("xCommand Phonebook Search PhonebookType: {0} ContactType: Folder", PhonebookMode));
- }
-
- private void GetPhonebookContacts()
- {
- // Get Phonebook Folders (determine local/corporate from config, and set results limit)
- SendText(string.Format("xCommand Phonebook Search PhonebookType: {0} ContactType: Contact Limit: {1}", PhonebookMode, PhonebookResultsLimit));
- }
-
- ///
- /// Searches the codec phonebook for all contacts matching the search string
- ///
- ///
- public void SearchDirectory(string searchString)
- {
- SendText(string.Format("xCommand Phonebook Search SearchString: \"{0}\" PhonebookType: {1} ContactType: Contact Limit: {2}", searchString, PhonebookMode, PhonebookResultsLimit));
- }
-
- ///
- /// // Get contents of a specific folder in the phonebook
- ///
- ///
- public void GetDirectoryFolderContents(string folderId)
- {
- SendText(string.Format("xCommand Phonebook Search FolderId: {0} PhonebookType: {1} ContactType: Any Limit: {2}", folderId, PhonebookMode, PhonebookResultsLimit));
- }
-
- ///
- /// Sets the parent folder contents or the directory root as teh current directory and fires the event. Used to browse up a level
- ///
- ///
- public void GetDirectoryParentFolderContents()
- {
- var currentDirectory = new CodecDirectory();
-
- if (DirectoryBrowseHistory.Count > 0)
- {
- var lastItemIndex = DirectoryBrowseHistory.Count - 1;
- var parentDirectoryContents = DirectoryBrowseHistory[lastItemIndex];
-
- DirectoryBrowseHistory.Remove(DirectoryBrowseHistory[lastItemIndex]);
-
- currentDirectory = parentDirectoryContents;
-
- }
- else
- {
- currentDirectory = DirectoryRoot;
- }
-
- OnDirectoryResultReturned(currentDirectory);
- }
-
- ///
- /// Clears the session browse history and fires the event with the directory root
- ///
- public void SetCurrentDirectoryToRoot()
- {
- DirectoryBrowseHistory.Clear();
-
- OnDirectoryResultReturned(DirectoryRoot);
- }
-
- ///
- /// Prints the directory to console
- ///
- ///
- void PrintDirectory(CodecDirectory directory)
- {
- if (Debug.Level > 0)
- {
- Debug.Console(1, this, "Directory Results:\n");
-
- foreach (DirectoryItem item in directory.CurrentDirectoryResults)
- {
- if (item is DirectoryFolder)
- {
- Debug.Console(1, this, "[+] {0}", item.Name);
- }
- else if (item is DirectoryContact)
- {
- Debug.Console(1, this, "{0}", item.Name);
- }
- }
- Debug.Console(1, this, "Directory is on Root Level: {0}", !CurrentDirectoryResultIsNotDirectoryRoot.BoolValue);
- }
-
- }
-
- ///
- /// Simple dial method
- ///
- ///
- public override void Dial(string number)
- {
- SendText(string.Format("xCommand Dial Number: \"{0}\"", number));
- }
-
- ///
- /// Dials a specific meeting
- ///
- ///
- public override void Dial(Meeting meeting)
- {
- foreach (Call c in meeting.Calls)
- {
- Dial(c.Number, c.Protocol, c.CallRate, c.CallType, meeting.Id);
- }
- }
-
- ///
- /// Detailed dial method
- ///
- ///
- ///
- ///
- ///
- ///
- public void Dial(string number, string protocol, string callRate, string callType, string meetingId)
- {
- SendText(string.Format("xCommand Dial Number: \"{0}\" Protocol: {1} CallRate: {2} CallType: {3} BookingId: {4}", number, protocol, callRate, callType, meetingId));
- }
-
- public override void EndCall(CodecActiveCallItem activeCall)
- {
- SendText(string.Format("xCommand Call Disconnect CallId: {0}", activeCall.Id));
- }
-
- public override void EndAllCalls()
- {
- foreach (CodecActiveCallItem activeCall in ActiveCalls)
- {
- SendText(string.Format("xCommand Call Disconnect CallId: {0}", activeCall.Id));
- }
- }
-
- public override void AcceptCall(CodecActiveCallItem item)
- {
- SendText("xCommand Call Accept");
- }
-
- public override void RejectCall(CodecActiveCallItem item)
- {
- SendText("xCommand Call Reject");
- }
-
- public override void SendDtmf(string s)
- {
- SendText(string.Format("xCommand Call DTMFSend CallId: {0} DTMFString: \"{1}\"", GetCallId(), s));
- }
-
- public void SelectPresentationSource(int source)
- {
- PresentationSource = source;
-
- StartSharing();
- }
-
- ///
- /// Select source 1 as the presetnation source
- ///
- public void SelectPresentationSource1()
- {
- SelectPresentationSource(2);
- }
-
- ///
- /// Select source 2 as the presetnation source
- ///
- public void SelectPresentationSource2()
- {
- SelectPresentationSource(3);
- }
-
- ///
- /// Starts presentation sharing
- ///
- public override void StartSharing()
- {
- string sendingMode = string.Empty;
-
- if (IsInCall)
- sendingMode = "LocalRemote";
- else
- sendingMode = "LocalOnly";
-
- if(PresentationSource > 0)
- SendText(string.Format("xCommand Presentation Start PresentationSource: {0} SendingMode: {1}", PresentationSource, sendingMode));
- }
-
- ///
- /// Stops sharing the current presentation
- ///
- public override void StopSharing()
- {
- PresentationSource = 0;
-
- SendText("xCommand Presentation Stop");
- }
-
- public override void PrivacyModeOn()
- {
- SendText("xCommand Audio Microphones Mute");
- }
-
- public override void PrivacyModeOff()
- {
- SendText("xCommand Audio Microphones Unmute");
- }
-
- public override void PrivacyModeToggle()
- {
- SendText("xCommand Audio Microphones ToggleMute");
- }
-
- public override void MuteOff()
- {
- SendText("xCommand Audio Volume Unmute");
- }
-
- public override void MuteOn()
- {
- SendText("xCommand Audio Volume Mute");
- }
-
- public override void MuteToggle()
- {
- SendText("xCommand Audio Volume ToggleMute");
- }
-
- ///
- /// Increments the voluem
- ///
- ///
- public override void VolumeUp(bool pressRelease)
- {
- SendText("xCommand Audio Volume Increase");
- }
-
- ///
- /// Decrements the volume
- ///
- ///
- public override void VolumeDown(bool pressRelease)
- {
- SendText("xCommand Audio Volume Decrease");
- }
-
- ///
- /// Scales the level and sets the codec to the specified level within its range
- ///
- /// level from slider (0-65535 range)
- public override void SetVolume(ushort level)
- {
- var scaledLevel = CrestronEnvironment.ScaleWithLimits(level, 65535, 0, 100, 0);
- SendText(string.Format("xCommand Audio Volume Set Level: {0}", scaledLevel));
- }
-
- ///
- /// Recalls the default volume on the codec
- ///
- public void VolumeSetToDefault()
- {
- SendText("xCommand Audio Volume SetToDefault");
- }
-
- ///
- /// Puts the codec in standby mode
- ///
- public override void StandbyActivate()
- {
- SendText("xCommand Standby Activate");
- }
-
- ///
- /// Wakes the codec from standby
- ///
- public override void StandbyDeactivate()
- {
- SendText("xCommand Standby Deactivate");
- }
-
- public override void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge)
- {
- LinkVideoCodecToApi(this, trilist, joinStart, joinMapKey, bridge);
- }
-
- ///
- /// Reboots the codec
- ///
- public void Reboot()
- {
- SendText("xCommand SystemUnit Boot Action: Restart");
- }
-
- ///
- /// Sets SelfView Mode based on config
- ///
- void SetSelfViewMode()
- {
- if (!IsInCall)
- {
- SelfViewModeOff();
- }
- else
- {
- if (ShowSelfViewByDefault)
- SelfViewModeOn();
- else
- SelfViewModeOff();
- }
- }
-
- ///
- /// Turns on Selfview Mode
- ///
- public void SelfViewModeOn()
- {
- SendText("xCommand Video Selfview Set Mode: On");
- }
-
- ///
- /// Turns off Selfview Mode
- ///
- public void SelfViewModeOff()
- {
- SendText("xCommand Video Selfview Set Mode: Off");
- }
-
- ///
- /// Toggles Selfview mode on/off
- ///
- public void SelfViewModeToggle()
- {
- string mode = string.Empty;
-
- if (CodecStatus.Status.Video.Selfview.Mode.BoolValue)
- mode = "Off";
- else
- mode = "On";
-
- SendText(string.Format("xCommand Video Selfview Set Mode: {0}", mode));
- }
-
- ///
- /// Sets a specified position for the selfview PIP window
- ///
- ///
- public void SelfviewPipPositionSet(CodecCommandWithLabel position)
- {
- SendText(string.Format("xCommand Video Selfview Set Mode: On PIPPosition: {0}", position.Command));
- }
-
- ///
- /// Toggles to the next selfview PIP position
- ///
- public void SelfviewPipPositionToggle()
- {
- if (CurrentSelfviewPipPosition != null)
- {
- var nextPipPositionIndex = SelfviewPipPositions.IndexOf(CurrentSelfviewPipPosition) + 1;
-
- if (nextPipPositionIndex >= SelfviewPipPositions.Count) // Check if we need to loop back to the first item in the list
- nextPipPositionIndex = 0;
-
- SelfviewPipPositionSet(SelfviewPipPositions[nextPipPositionIndex]);
- }
- }
-
- ///
- /// Sets a specific local layout
- ///
- ///
- public void LocalLayoutSet(CodecCommandWithLabel layout)
- {
- SendText(string.Format("xCommand Video Layout LayoutFamily Set Target: local LayoutFamily: {0}", layout.Command));
- }
-
- ///
- /// Toggles to the next local layout
- ///
- public void LocalLayoutToggle()
- {
- if(CurrentLocalLayout != null)
- {
- var nextLocalLayoutIndex = LocalLayouts.IndexOf(CurrentLocalLayout) + 1;
-
- if (nextLocalLayoutIndex >= LocalLayouts.Count) // Check if we need to loop back to the first item in the list
- nextLocalLayoutIndex = 0;
-
- LocalLayoutSet(LocalLayouts[nextLocalLayoutIndex]);
- }
- }
-
- ///
- /// Toggles between single/prominent layouts
- ///
- public void LocalLayoutToggleSingleProminent()
- {
- if (CurrentLocalLayout != null)
- {
- if (CurrentLocalLayout.Label != "Prominent")
- LocalLayoutSet(LocalLayouts.FirstOrDefault(l => l.Label.Equals("Prominent")));
- else
- LocalLayoutSet(LocalLayouts.FirstOrDefault(l => l.Label.Equals("Single")));
- }
-
- }
-
- ///
- ///
- ///
- public void MinMaxLayoutToggle()
- {
- if (PresentationViewMaximizedFeedback.BoolValue)
- CurrentPresentationView = "Minimized";
- else
- CurrentPresentationView = "Maximized";
-
- SendText(string.Format("xCommand Video PresentationView Set View: {0}", CurrentPresentationView));
- PresentationViewMaximizedFeedback.FireUpdate();
- }
-
- ///
- /// Calculates the current selfview PIP position
- ///
- void ComputeSelfviewPipStatus()
- {
- CurrentSelfviewPipPosition = SelfviewPipPositions.FirstOrDefault(p => p.Command.ToLower().Equals(CodecStatus.Status.Video.Selfview.PIPPosition.Value.ToLower()));
-
- if(CurrentSelfviewPipPosition != null)
- SelfviewIsOnFeedback.FireUpdate();
- }
-
- ///
- /// Calculates the current local Layout
- ///
- void ComputeLocalLayout()
- {
- CurrentLocalLayout = LocalLayouts.FirstOrDefault(l => l.Command.ToLower().Equals(CodecStatus.Status.Video.Layout.LayoutFamily.Local.Value.ToLower()));
-
- if (CurrentLocalLayout != null)
- LocalLayoutFeedback.FireUpdate();
- }
-
- public void RemoveCallHistoryEntry(CodecCallHistory.CallHistoryEntry entry)
- {
- SendText(string.Format("xCommand CallHistory DeleteEntry CallHistoryId: {0} AcknowledgeConsecutiveDuplicates: True", entry.OccurrenceHistoryId));
- }
-
- #region IHasCameraSpeakerTrack
-
- public void CameraAutoModeToggle()
- {
- if (!CameraAutoModeIsOnFeedback.BoolValue)
- {
- SendText("xCommand Cameras SpeakerTrack Activate");
- }
- else
- {
- SendText("xCommand Cameras SpeakerTrack Deactivate");
- }
- }
-
- public void CameraAutoModeOn()
- {
- if (CameraIsOffFeedback.BoolValue)
- {
- CameraMuteOff();
- }
-
- SendText("xCommand Cameras SpeakerTrack Activate");
- }
-
- public void CameraAutoModeOff()
- {
- if (CameraIsOffFeedback.BoolValue)
- {
- CameraMuteOff();
- }
-
- SendText("xCommand Cameras SpeakerTrack Deactivate");
- }
-
- #endregion
-
- ///
- /// Builds the cameras List. Could later be modified to build from config data
- ///
- void SetUpCameras()
- {
- // Add the internal camera
- Cameras = new List();
-
- var internalCamera = new CiscoSparkCamera(Key + "-camera1", "Near End", this, 1);
-
- if(CodecStatus.Status.Cameras.Camera.Count > 0)
- internalCamera.SetCapabilites(CodecStatus.Status.Cameras.Camera[0].Capabilities.Options.Value);
- else
- // Somehow subscribe to the event on the Options.Value property and update when it changes.
-
- Cameras.Add(internalCamera);
-
- // Add the far end camera
- var farEndCamera = new CiscoFarEndCamera(Key + "-cameraFar", "Far End", this);
- Cameras.Add(farEndCamera);
-
- SelectedCameraFeedback = new StringFeedback(() => SelectedCamera.Key);
-
- ControllingFarEndCameraFeedback = new BoolFeedback(() => SelectedCamera is IAmFarEndCamera);
-
- DeviceManager.AddDevice(internalCamera);
- DeviceManager.AddDevice(farEndCamera);
-
- NearEndPresets = new List(15);
-
- FarEndRoomPresets = new List(15);
-
- // Add the far end presets
- for (int i = 1; i <= FarEndRoomPresets.Capacity; i++)
- {
- var label = string.Format("Far End Preset {0}", i);
- FarEndRoomPresets.Add(new CodecRoomPreset(i, label, true, false));
- }
-
- SelectedCamera = internalCamera; ; // call the method to select the camera and ensure the feedbacks get updated.
- }
-
- #region IHasCodecCameras Members
-
- public event EventHandler CameraSelected;
-
- public List Cameras { get; private set; }
-
- public StringFeedback SelectedCameraFeedback { get; private set; }
-
- private CameraBase _selectedCamera;
-
- ///
- /// Returns the selected camera
- ///
- public CameraBase SelectedCamera
- {
- get
- {
- return _selectedCamera;
- }
- private set
- {
- _selectedCamera = value;
- SelectedCameraFeedback.FireUpdate();
- ControllingFarEndCameraFeedback.FireUpdate();
- if (CameraIsOffFeedback.BoolValue)
- CameraMuteOff();
-
- var handler = CameraSelected;
- if (handler != null)
- {
- handler(this, new CameraSelectedEventArgs(SelectedCamera));
- }
- }
- }
-
- public void SelectCamera(string key)
- {
- var camera = Cameras.FirstOrDefault(c => c.Key.IndexOf(key, StringComparison.OrdinalIgnoreCase) > -1);
- if (camera != null)
- {
- Debug.Console(2, this, "Selected Camera with key: '{0}'", camera.Key);
- SelectedCamera = camera;
- }
- else
- Debug.Console(2, this, "Unable to select camera with key: '{0}'", key);
- }
-
- public CameraBase FarEndCamera { get; private set; }
-
- public BoolFeedback ControllingFarEndCameraFeedback { get; private set; }
-
- #endregion
-
- ///
- ///
- ///
- public class CiscoCodecInfo : VideoCodecInfo
- {
- public CiscoCodecStatus.RootObject CodecStatus { get; private set; }
-
- public CiscoCodecConfiguration.RootObject CodecConfiguration { get; private set; }
-
- public override bool MultiSiteOptionIsEnabled
- {
- get
- {
- if (!string.IsNullOrEmpty(CodecStatus.Status.SystemUnit.Software.OptionKeys.MultiSite.Value) && CodecStatus.Status.SystemUnit.Software.OptionKeys.MultiSite.Value.ToLower() == "true")
- return true;
- else
- return false;
- }
-
- }
- public override string IpAddress
- {
- get
- {
- if (CodecConfiguration.Configuration.Network != null)
- {
- if (CodecConfiguration.Configuration.Network.Count > 0)
- return CodecConfiguration.Configuration.Network[0].IPv4.Address.Value;
- }
- return string.Empty;
- }
- }
- public override string E164Alias
- {
- get
- {
- if (CodecConfiguration.Configuration.H323 != null && CodecConfiguration.Configuration.H323.H323Alias.E164 != null)
- {
- return CodecConfiguration.Configuration.H323.H323Alias.E164.Value;
- }
- else
- {
- return string.Empty;
- }
- }
- }
- public override string H323Id
- {
- get
- {
- if (CodecConfiguration.Configuration.H323 != null && CodecConfiguration.Configuration.H323.H323Alias != null
- && CodecConfiguration.Configuration.H323.H323Alias.ID != null)
- {
- return CodecConfiguration.Configuration.H323.H323Alias.ID.Value;
- }
- else
- {
- return string.Empty;
- }
- }
- }
- public override string SipPhoneNumber
- {
- get
- {
- if (CodecStatus.Status.SIP != null && CodecStatus.Status.SIP.Registration.Count > 0)
- {
- var match = Regex.Match(CodecStatus.Status.SIP.Registration[0].URI.Value, @"(\d+)"); // extract numbers only
- if (match.Success)
- {
- Debug.Console(1, "Extracted phone number as '{0}' from string '{1}'", match.Groups[1].Value, CodecStatus.Status.SIP.Registration[0].URI.Value);
- return match.Groups[1].Value;
- }
- else
- {
- Debug.Console(1, "Unable to extract phone number from string: '{0}'", CodecStatus.Status.SIP.Registration[0].URI.Value);
- return string.Empty;
- }
- }
- else
- {
- Debug.Console(1, "Unable to extract phone number. No SIP Registration items found");
- return string.Empty;
- }
- }
- }
-
- public override string SipUri
- {
- get
- {
- if (CodecStatus.Status.SIP != null && CodecStatus.Status.SIP.AlternateURI.Primary.URI.Value != null)
- {
- return CodecStatus.Status.SIP.AlternateURI.Primary.URI.Value;
- }
- else if (CodecStatus.Status.UserInterface != null &&
- CodecStatus.Status.UserInterface.ContactInfo.ContactMethod[0].Number.Value != null)
- {
- return CodecStatus.Status.UserInterface.ContactInfo.ContactMethod[0].Number.Value;
- }
- else
- return string.Empty;
- }
- }
-
- public override bool AutoAnswerEnabled
- {
- get
- {
- if (CodecConfiguration.Configuration.Conference.AutoAnswer.Mode.Value.ToLower() == "on")
- return true;
- else
- return false;
- }
- }
-
- public CiscoCodecInfo(CiscoCodecStatus.RootObject status, CiscoCodecConfiguration.RootObject configuration)
- {
- CodecStatus = status;
- CodecConfiguration = configuration;
- }
- }
-
-
- #region IHasCameraPresets Members
-
- public event EventHandler CodecRoomPresetsListHasChanged;
-
- public List NearEndPresets { get; private set; }
-
- public List FarEndRoomPresets { get; private set; }
-
- public void CodecRoomPresetSelect(int preset)
- {
- Debug.Console(1, this, "Selecting Preset: {0}", preset);
- if (SelectedCamera is IAmFarEndCamera)
- SelectFarEndPreset(preset);
- else
- SendText(string.Format("xCommand RoomPreset Activate PresetId: {0}", preset));
- }
-
- public void CodecRoomPresetStore(int preset, string description)
- {
- SendText(string.Format("xCommand RoomPreset Store PresetId: {0} Description: \"{1}\" Type: All", preset, description));
- }
-
- #endregion
-
- public void SelectFarEndPreset(int preset)
- {
- SendText(string.Format("xCommand Call FarEndControl RoomPreset Activate CallId: {0} PresetId: {1}", GetCallId(), preset));
- }
-
-
- #region IHasExternalSourceSwitching Members
-
- ///
- /// Wheather the Cisco supports External Source Lists or not
- ///
- public bool ExternalSourceListEnabled
- {
- get;
- private set;
- }
-
- ///
- /// The name of the RoutingInputPort to which the upstream external switcher is connected
- ///
- public string ExternalSourceInputPort { get; private set; }
-
- public bool BrandingEnabled { get; private set; }
- private string _brandingUrl;
- private bool _sendMcUrl;
-
- ///
- /// Adds an external source to the Cisco
- ///
- ///
- ///
- ///
- public void AddExternalSource(string connectorId, string key, string name, eExternalSourceType type)
- {
- int id = 2;
- if (connectorId.ToLower() == "hdmiin3")
- {
- id = 3;
- }
- SendText(string.Format("xCommand UserInterface Presentation ExternalSource Add ConnectorId: {0} SourceIdentifier: \"{1}\" Name: \"{2}\" Type: {3}", id, key, name, type.ToString()));
- // SendText(string.Format("xCommand UserInterface Presentation ExternalSource State Set SourceIdentifier: \"{0}\" State: Ready", key));
- Debug.Console(2, this, "Adding ExternalSource {0} {1}", connectorId, name);
-
- }
-
-
- ///
- /// Sets the state of the External Source
- ///
- ///
- ///
- public void SetExternalSourceState(string key, eExternalSourceMode mode)
- {
- SendText(string.Format("xCommand UserInterface Presentation ExternalSource State Set SourceIdentifier: \"{0}\" State: {1}", key, mode.ToString()));
- }
- ///
- /// Clears all external sources on the codec
- ///
- public void ClearExternalSources()
- {
- SendText("xCommand UserInterface Presentation ExternalSource RemoveAll");
-
- }
-
- ///
- /// Sets the selected source of the available external sources on teh Touch10 UI
- ///
- public void SetSelectedSource(string key)
- {
- SendText(string.Format("xCommand UserInterface Presentation ExternalSource Select SourceIdentifier: {0}", key));
- _externalSourceChangeRequested = true;
- }
-
- ///
- /// Action that will run when the External Source is selected.
- ///
- public Action RunRouteAction { private get; set; }
-
-
-
-
-
-
- #endregion
- #region ExternalDevices
-
-
-
- #endregion
-
- #region IHasCameraOff Members
-
- public BoolFeedback CameraIsOffFeedback { get; private set; }
-
- public void CameraOff()
- {
- CameraMuteOn();
- }
-
- #endregion
-
- public BoolFeedback CameraIsMutedFeedback { get; private set; }
-
- ///
- /// Mutes the outgoing camera video
- ///
- public void CameraMuteOn()
- {
- SendText("xCommand Video Input MainVideo Mute");
- }
-
- ///
- /// Unmutes the outgoing camera video
- ///
- public void CameraMuteOff()
- {
- SendText("xCommand Video Input MainVideo Unmute");
- }
-
- ///
- /// Toggles the camera mute state
- ///
- public void CameraMuteToggle()
- {
- if (CameraIsMutedFeedback.BoolValue)
- CameraMuteOff();
- else
- CameraMuteOn();
- }
- }
-
-
- ///
- /// Represents a codec command that might need to have a friendly label applied for UI feedback purposes
- ///
- public class CodecCommandWithLabel
- {
- public string Command { get; set; }
- public string Label { get; set; }
-
- public CodecCommandWithLabel(string command, string label)
- {
- Command = command;
- Label = label;
- }
- }
-
- ///
- /// Tracks the initial sycnronization state of the codec when making a connection
- ///
- public class CodecSyncState : IKeyed
- {
- bool _InitialSyncComplete;
-
- public event EventHandler InitialSyncCompleted;
-
- public string Key { get; private set; }
-
- public bool InitialSyncComplete
- {
- get { return _InitialSyncComplete; }
- private set
- {
- if (value == true)
- {
- var handler = InitialSyncCompleted;
- if (handler != null)
- handler(this, new EventArgs());
- }
- _InitialSyncComplete = value;
- }
- }
-
- public bool LoginMessageWasReceived { get; private set; }
-
- public bool InitialStatusMessageWasReceived { get; private set; }
-
- public bool InitialConfigurationMessageWasReceived { get; private set; }
-
- public bool FeedbackWasRegistered { get; private set; }
-
- public CodecSyncState(string key)
- {
- Key = key;
- CodecDisconnected();
- }
-
- public void LoginMessageReceived()
- {
- LoginMessageWasReceived = true;
- Debug.Console(1, this, "Login Message Received.");
- CheckSyncStatus();
- }
-
- public void InitialStatusMessageReceived()
- {
- InitialStatusMessageWasReceived = true;
- Debug.Console(1, this, "Initial Codec Status Message Received.");
- CheckSyncStatus();
- }
-
- public void InitialConfigurationMessageReceived()
- {
- InitialConfigurationMessageWasReceived = true;
- Debug.Console(1, this, "Initial Codec Configuration Message Received.");
- CheckSyncStatus();
- }
-
- public void FeedbackRegistered()
- {
- FeedbackWasRegistered = true;
- Debug.Console(1, this, "Initial Codec Feedback Registration Successful.");
- CheckSyncStatus();
- }
-
- public void CodecDisconnected()
- {
- LoginMessageWasReceived = false;
- InitialConfigurationMessageWasReceived = false;
- InitialStatusMessageWasReceived = false;
- FeedbackWasRegistered = false;
- InitialSyncComplete = false;
- }
-
- void CheckSyncStatus()
- {
- if (LoginMessageWasReceived && InitialConfigurationMessageWasReceived && InitialStatusMessageWasReceived && FeedbackWasRegistered)
- {
- InitialSyncComplete = true;
- Debug.Console(1, this, "Initial Codec Sync Complete!");
- }
- else
- InitialSyncComplete = false;
- }
- }
-
- public class CiscoSparkCodecFactory : EssentialsDeviceFactory
- {
- public CiscoSparkCodecFactory()
- {
- TypeNames = new List() { "ciscospark", "ciscowebex", "ciscowebexpro", "ciscoroomkit", "ciscosparkpluscodec" };
- }
-
- public override EssentialsDevice BuildDevice(DeviceConfig dc)
- {
- Debug.Console(1, "Factory Attempting to create new Cisco Codec Device");
-
- var comm = CommFactory.CreateCommForDevice(dc);
- return new VideoCodec.Cisco.CiscoSparkCodec(dc, comm);
- }
- }
-
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Text.RegularExpressions;
+using Crestron.SimplSharp;
+using Crestron.SimplSharpPro.CrestronThread;
+using Crestron.SimplSharpPro.DeviceSupport;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+
+using PepperDash.Core;
+using PepperDash.Essentials.Core;
+using PepperDash.Essentials.Core.Bridges;
+using PepperDash.Essentials.Core.Config;
+using PepperDash.Essentials.Core.DeviceTypeInterfaces;
+using PepperDash.Essentials.Core.Routing;
+using PepperDash.Essentials.Devices.Common.Cameras;
+using PepperDash.Essentials.Devices.Common.Codec;
+using PepperDash.Essentials.Devices.Common.VideoCodec;
+using PepperDash.Essentials.Core.Queues;
+
+namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
+{
+ enum eCommandType { SessionStart, SessionEnd, Command, GetStatus, GetConfiguration };
+ public enum eExternalSourceType {camera, desktop, document_camera, mediaplayer, PC, whiteboard, other}
+ public enum eExternalSourceMode {Ready, NotReady, Hidden, Error}
+
+ public class CiscoSparkCodec : VideoCodecBase, IHasCallHistory, IHasCallFavorites, IHasDirectory,
+ IHasScheduleAwareness, IOccupancyStatusProvider, IHasCodecLayouts, IHasCodecSelfView,
+ ICommunicationMonitor, IRouting, IHasCodecCameras, IHasCameraAutoMode, IHasCodecRoomPresets, IHasExternalSourceSwitching, IHasBranding, IHasCameraOff, IHasCameraMute
+ {
+ private bool _externalSourceChangeRequested;
+
+ public event EventHandler DirectoryResultReturned;
+
+ private CTimer _brandingTimer;
+
+ public CommunicationGather PortGather { get; private set; }
+
+ public StatusMonitorBase CommunicationMonitor { get; private set; }
+
+ private GenericQueue ReceiveQueue;
+
+ public BoolFeedback PresentationViewMaximizedFeedback { get; private set; }
+
+ string CurrentPresentationView;
+
+ public BoolFeedback RoomIsOccupiedFeedback { get; private set; }
+
+ public IntFeedback PeopleCountFeedback { get; private set; }
+
+ public BoolFeedback CameraAutoModeIsOnFeedback { get; private set; }
+
+ public BoolFeedback SelfviewIsOnFeedback { get; private set; }
+
+ public StringFeedback SelfviewPipPositionFeedback { get; private set; }
+
+ public StringFeedback LocalLayoutFeedback { get; private set; }
+
+ public BoolFeedback LocalLayoutIsProminentFeedback { get; private set; }
+
+ public BoolFeedback FarEndIsSharingContentFeedback { get; private set; }
+
+ private CodecCommandWithLabel CurrentSelfviewPipPosition;
+
+ private CodecCommandWithLabel CurrentLocalLayout;
+
+ ///
+ /// List the available positions for the selfview PIP window
+ ///
+ public List SelfviewPipPositions = new List()
+ {
+ new CodecCommandWithLabel("CenterLeft", "Center Left"),
+ new CodecCommandWithLabel("CenterRight", "Center Right"),
+ new CodecCommandWithLabel("LowerLeft", "Lower Left"),
+ new CodecCommandWithLabel("LowerRight", "Lower Right"),
+ new CodecCommandWithLabel("UpperCenter", "Upper Center"),
+ new CodecCommandWithLabel("UpperLeft", "Upper Left"),
+ new CodecCommandWithLabel("UpperRight", "Upper Right"),
+ };
+
+ ///
+ /// Lists the available options for local layout
+ ///
+ public List LocalLayouts = new List()
+ {
+ //new CodecCommandWithLabel("auto", "Auto"),
+ //new CiscoCodecLocalLayout("custom", "Custom"), // Left out for now
+ new CodecCommandWithLabel("equal","Equal"),
+ new CodecCommandWithLabel("overlay","Overlay"),
+ new CodecCommandWithLabel("prominent","Prominent"),
+ new CodecCommandWithLabel("single","Single")
+ };
+
+ private CiscoCodecConfiguration.RootObject CodecConfiguration = new CiscoCodecConfiguration.RootObject();
+
+ private CiscoCodecStatus.RootObject CodecStatus = new CiscoCodecStatus.RootObject();
+
+ public CodecCallHistory CallHistory { get; private set; }
+
+ public CodecCallFavorites CallFavorites { get; private set; }
+
+ ///
+ /// The root level of the directory
+ ///
+ public CodecDirectory DirectoryRoot { get; private set; }
+
+ ///
+ /// Represents the current state of the directory and is computed on get
+ ///
+ public CodecDirectory CurrentDirectoryResult
+ {
+ get
+ {
+ if (DirectoryBrowseHistory.Count > 0)
+ return DirectoryBrowseHistory[DirectoryBrowseHistory.Count - 1];
+ else
+ return DirectoryRoot;
+ }
+ }
+
+ public BoolFeedback CurrentDirectoryResultIsNotDirectoryRoot { get; private set; }
+
+ ///
+ /// Tracks the directory browse history when browsing beyond the root directory
+ ///
+ public List DirectoryBrowseHistory { get; private set; }
+
+ public CodecScheduleAwareness CodecSchedule { get; private set; }
+
+ ///
+ /// Gets and returns the scaled volume of the codec
+ ///
+ protected override Func VolumeLevelFeedbackFunc
+ {
+ get
+ {
+ return () => CrestronEnvironment.ScaleWithLimits(CodecStatus.Status.Audio.Volume.IntValue, 100, 0, 65535, 0);
+ }
+ }
+
+ protected override Func PrivacyModeIsOnFeedbackFunc
+ {
+ get
+ {
+ return () => CodecStatus.Status.Audio.Microphones.Mute.BoolValue;
+ }
+ }
+
+ protected override Func StandbyIsOnFeedbackFunc
+ {
+ get
+ {
+ return () => CodecStatus.Status.Standby.State.BoolValue;
+ }
+ }
+
+ ///
+ /// Gets the value of the currently shared source, or returns null
+ ///
+ protected override Func SharingSourceFeedbackFunc
+ {
+ get
+ {
+ return () => PresentationSourceKey;
+ }
+ }
+
+ protected override Func SharingContentIsOnFeedbackFunc
+ {
+ get
+ {
+ return () => CodecStatus.Status.Conference.Presentation.Mode.BoolValue;
+ }
+ }
+
+ protected Func FarEndIsSharingContentFeedbackFunc
+ {
+ get
+ {
+ return () => CodecStatus.Status.Conference.Presentation.Mode.Value == "Receiving";
+ }
+ }
+
+ protected override Func MuteFeedbackFunc
+ {
+ get
+ {
+ return () => CodecStatus.Status.Audio.VolumeMute.BoolValue;
+ }
+ }
+
+ protected Func RoomIsOccupiedFeedbackFunc
+ {
+ get
+ {
+ return () => CodecStatus.Status.RoomAnalytics.PeoplePresence.BoolValue;
+ }
+ }
+
+ protected Func PeopleCountFeedbackFunc
+ {
+ get
+ {
+ return () => CodecStatus.Status.RoomAnalytics.PeopleCount.Current.IntValue;
+ }
+ }
+
+ protected Func SpeakerTrackIsOnFeedbackFunc
+ {
+ get
+ {
+ return () => CodecStatus.Status.Cameras.SpeakerTrack.Status.BoolValue;
+ }
+ }
+
+ protected Func SelfViewIsOnFeedbackFunc
+ {
+ get
+ {
+ return () => CodecStatus.Status.Video.Selfview.Mode.BoolValue;
+ }
+ }
+
+ protected Func SelfviewPipPositionFeedbackFunc
+ {
+ get
+ {
+ return () => CurrentSelfviewPipPosition.Label;
+ }
+ }
+
+ protected Func LocalLayoutFeedbackFunc
+ {
+ get
+ {
+ return () => CurrentLocalLayout.Label;
+ }
+ }
+
+ protected Func LocalLayoutIsProminentFeedbackFunc
+ {
+ get
+ {
+ return () => CurrentLocalLayout.Label == "Prominent";
+ }
+ }
+
+
+ private string CliFeedbackRegistrationExpression;
+
+ private CodecSyncState SyncState;
+
+ public CodecPhonebookSyncState PhonebookSyncState { get; private set; }
+
+ private StringBuilder JsonMessage;
+
+ private bool JsonFeedbackMessageIsIncoming;
+
+ public bool CommDebuggingIsOn;
+
+ string Delimiter = "\r\n";
+
+ ///
+ /// Used to track the current connector used for the presentation source
+ ///
+ int PresentationSource;
+
+ string PresentationSourceKey;
+
+ string PhonebookMode = "Local"; // Default to Local
+
+ uint PhonebookResultsLimit = 255; // Could be set later by config.
+
+ CTimer LoginMessageReceivedTimer;
+ CTimer RetryConnectionTimer;
+
+ // **___________________________________________________________________**
+ // Timers to be moved to the global system timer at a later point....
+ CTimer BookingsRefreshTimer;
+ CTimer PhonebookRefreshTimer;
+ // **___________________________________________________________________**
+
+ public RoutingInputPort CodecOsdIn { get; private set; }
+ public RoutingInputPort HdmiIn2 { get; private set; }
+ public RoutingInputPort HdmiIn3 { get; private set; }
+ public RoutingOutputPort HdmiOut1 { get; private set; }
+ public RoutingOutputPort HdmiOut2 { get; private set; }
+
+
+ // Constructor for IBasicCommunication
+ public CiscoSparkCodec(DeviceConfig config, IBasicCommunication comm)
+ : base(config)
+ {
+ var props = JsonConvert.DeserializeObject(config.Properties.ToString());
+
+ // Use the configured phonebook results limit if present
+ if (props.PhonebookResultsLimit > 0)
+ {
+ PhonebookResultsLimit = props.PhonebookResultsLimit;
+ }
+
+ // The queue that will collect the repsonses in the order they are received
+ ReceiveQueue = new GenericQueue(this.Key + "-rxQueue", 25);
+
+ RoomIsOccupiedFeedback = new BoolFeedback(RoomIsOccupiedFeedbackFunc);
+ PeopleCountFeedback = new IntFeedback(PeopleCountFeedbackFunc);
+ CameraAutoModeIsOnFeedback = new BoolFeedback(SpeakerTrackIsOnFeedbackFunc);
+ SelfviewIsOnFeedback = new BoolFeedback(SelfViewIsOnFeedbackFunc);
+ SelfviewPipPositionFeedback = new StringFeedback(SelfviewPipPositionFeedbackFunc);
+ LocalLayoutFeedback = new StringFeedback(LocalLayoutFeedbackFunc);
+ LocalLayoutIsProminentFeedback = new BoolFeedback(LocalLayoutIsProminentFeedbackFunc);
+ FarEndIsSharingContentFeedback = new BoolFeedback(FarEndIsSharingContentFeedbackFunc);
+ CameraIsOffFeedback = new BoolFeedback(() => CodecStatus.Status.Video.Input.MainVideoMute.BoolValue);
+ CameraIsMutedFeedback = CameraIsOffFeedback;
+ SupportsCameraOff = true;
+
+ PresentationViewMaximizedFeedback = new BoolFeedback(() => CurrentPresentationView == "Maximized");
+
+ Communication = comm;
+
+ if (props.CommunicationMonitorProperties != null)
+ {
+ CommunicationMonitor = new GenericCommunicationMonitor(this, Communication, props.CommunicationMonitorProperties);
+ }
+ else
+ {
+ CommunicationMonitor = new GenericCommunicationMonitor(this, Communication, 30000, 120000, 300000, "xStatus SystemUnit Software Version\r");
+ }
+
+ if (props.Sharing != null)
+ AutoShareContentWhileInCall = props.Sharing.AutoShareContentWhileInCall;
+
+ ShowSelfViewByDefault = props.ShowSelfViewByDefault;
+
+ DeviceManager.AddDevice(CommunicationMonitor);
+
+ PhonebookMode = props.PhonebookMode;
+
+ SyncState = new CodecSyncState(Key + "--Sync");
+
+ PhonebookSyncState = new CodecPhonebookSyncState(Key + "--PhonebookSync");
+
+ SyncState.InitialSyncCompleted += new EventHandler(SyncState_InitialSyncCompleted);
+
+ PortGather = new CommunicationGather(Communication, Delimiter);
+ PortGather.IncludeDelimiter = true;
+ PortGather.LineReceived += this.Port_LineReceived;
+
+ CodecInfo = new CiscoCodecInfo(CodecStatus, CodecConfiguration);
+
+ CallHistory = new CodecCallHistory();
+
+
+ if (props.Favorites != null)
+ {
+ CallFavorites = new CodecCallFavorites();
+ CallFavorites.Favorites = props.Favorites;
+ }
+
+ DirectoryRoot = new CodecDirectory();
+
+ DirectoryBrowseHistory = new List();
+
+ CurrentDirectoryResultIsNotDirectoryRoot = new BoolFeedback(() => DirectoryBrowseHistory.Count > 0);
+
+ CurrentDirectoryResultIsNotDirectoryRoot.FireUpdate();
+
+ CodecSchedule = new CodecScheduleAwareness();
+
+ //Set Feedback Actions
+ SetFeedbackActions();
+
+ CodecOsdIn = new RoutingInputPort(RoutingPortNames.CodecOsd, eRoutingSignalType.Audio | eRoutingSignalType.Video,
+ eRoutingPortConnectionType.Hdmi, new Action(StopSharing), this);
+ HdmiIn2 = new RoutingInputPort(RoutingPortNames.HdmiIn2, eRoutingSignalType.Audio | eRoutingSignalType.Video,
+ eRoutingPortConnectionType.Hdmi, new Action(SelectPresentationSource1), this);
+ HdmiIn3 = new RoutingInputPort(RoutingPortNames.HdmiIn3, eRoutingSignalType.Audio | eRoutingSignalType.Video,
+ eRoutingPortConnectionType.Hdmi, new Action(SelectPresentationSource2), this);
+
+ HdmiOut1 = new RoutingOutputPort(RoutingPortNames.HdmiOut1, eRoutingSignalType.Audio | eRoutingSignalType.Video,
+ eRoutingPortConnectionType.Hdmi, null, this);
+ HdmiOut2 = new RoutingOutputPort(RoutingPortNames.HdmiOut2, eRoutingSignalType.Audio | eRoutingSignalType.Video,
+ eRoutingPortConnectionType.Hdmi, null, this);
+
+ InputPorts.Add(CodecOsdIn);
+ InputPorts.Add(HdmiIn2);
+ InputPorts.Add(HdmiIn3);
+ OutputPorts.Add(HdmiOut1);
+
+ SetUpCameras();
+
+ CreateOsdSource();
+
+ ExternalSourceListEnabled = props.ExternalSourceListEnabled;
+ ExternalSourceInputPort = props.ExternalSourceInputPort;
+
+ if (props.UiBranding == null)
+ {
+ return;
+ }
+ Debug.Console(2, this, "Setting branding properties enable: {0} _brandingUrl {1}", props.UiBranding.Enable,
+ props.UiBranding.BrandingUrl);
+
+ BrandingEnabled = props.UiBranding.Enable;
+
+ _brandingUrl = props.UiBranding.BrandingUrl;
+ }
+
+ private void SetFeedbackActions()
+ {
+ CodecStatus.Status.Audio.Volume.ValueChangedAction = VolumeLevelFeedback.FireUpdate;
+ CodecStatus.Status.Audio.VolumeMute.ValueChangedAction = MuteFeedback.FireUpdate;
+ CodecStatus.Status.Audio.Microphones.Mute.ValueChangedAction = PrivacyModeIsOnFeedback.FireUpdate;
+ CodecStatus.Status.Standby.State.ValueChangedAction = StandbyIsOnFeedback.FireUpdate;
+ CodecStatus.Status.RoomAnalytics.PeoplePresence.ValueChangedAction = RoomIsOccupiedFeedback.FireUpdate;
+ CodecStatus.Status.RoomAnalytics.PeopleCount.Current.ValueChangedAction = PeopleCountFeedback.FireUpdate;
+ CodecStatus.Status.Cameras.SpeakerTrack.Status.ValueChangedAction = CameraAutoModeIsOnFeedback.FireUpdate;
+ CodecStatus.Status.Cameras.SpeakerTrack.Availability.ValueChangedAction = () => { SupportsCameraAutoMode = CodecStatus.Status.Cameras.SpeakerTrack.Availability.BoolValue; };
+ CodecStatus.Status.Video.Selfview.Mode.ValueChangedAction = SelfviewIsOnFeedback.FireUpdate;
+ CodecStatus.Status.Video.Selfview.PIPPosition.ValueChangedAction = ComputeSelfviewPipStatus;
+ CodecStatus.Status.Video.Layout.LayoutFamily.Local.ValueChangedAction = ComputeLocalLayout;
+ CodecStatus.Status.Conference.Presentation.Mode.ValueChangedAction = () =>
+ {
+ SharingContentIsOnFeedback.FireUpdate();
+ FarEndIsSharingContentFeedback.FireUpdate();
+ };
+
+ try
+ {
+ CodecStatus.Status.Video.Input.MainVideoMute.ValueChangedAction = CameraIsOffFeedback.FireUpdate;
+ }
+ catch (Exception ex)
+ {
+ Debug.Console(0, this, "Error setting MainVideuMute Action: {0}", ex);
+
+ if (ex.InnerException != null)
+ {
+ Debug.Console(0, this, "Error setting MainVideuMute Action: {0}", ex);
+ }
+ }
+ }
+
+ ///
+ /// Creates the fake OSD source, and connects it's AudioVideo output to the CodecOsdIn input
+ /// to enable routing
+ ///
+ void CreateOsdSource()
+ {
+ OsdSource = new DummyRoutingInputsDevice(Key + "[osd]");
+ DeviceManager.AddDevice(OsdSource);
+ var tl = new TieLine(OsdSource.AudioVideoOutputPort, CodecOsdIn);
+ TieLineCollection.Default.Add(tl);
+ }
+
+ public void InitializeBranding(string roomKey)
+ {
+ Debug.Console(1, this, "Initializing Branding for room {0}", roomKey);
+
+ if (!BrandingEnabled)
+ {
+ return;
+ }
+
+ var mcBridgeKey = String.Format("mobileControlBridge-{0}", roomKey);
+
+ var mcBridge = DeviceManager.GetDeviceForKey(mcBridgeKey) as IMobileControlRoomBridge;
+
+ if (!String.IsNullOrEmpty(_brandingUrl))
+ {
+ Debug.Console(1, this, "Branding URL found: {0}", _brandingUrl);
+ if (_brandingTimer != null)
+ {
+ _brandingTimer.Stop();
+ _brandingTimer.Dispose();
+ }
+
+ _brandingTimer = new CTimer((o) =>
+ {
+ if (_sendMcUrl)
+ {
+ SendMcBrandingUrl(mcBridge);
+ _sendMcUrl = false;
+ }
+ else
+ {
+ SendBrandingUrl();
+ _sendMcUrl = true;
+ }
+ }, 0, 15000);
+ } else if (String.IsNullOrEmpty(_brandingUrl))
+ {
+ Debug.Console(1, this, "No Branding URL found");
+ if (mcBridge == null) return;
+
+ Debug.Console(2, this, "Setting QR code URL: {0}", mcBridge.QrCodeUrl);
+
+ mcBridge.UserCodeChanged += (o, a) => SendMcBrandingUrl(mcBridge);
+ mcBridge.UserPromptedForCode += (o, a) => DisplayUserCode(mcBridge.UserCode);
+
+ SendMcBrandingUrl(mcBridge);
+ }
+ }
+
+ ///
+ /// Displays the code for the specified duration
+ ///
+ /// Mobile Control user code
+ private void DisplayUserCode(string code)
+ {
+ SendText(string.Format("xcommand userinterface message alert display title:\"Mobile Control User Code:\" text:\"{0}\" duration: 30", code));
+ }
+
+ private void SendMcBrandingUrl(IMobileControlRoomBridge mcBridge)
+ {
+ if (mcBridge == null)
+ {
+ return;
+ }
+
+ Debug.Console(1, this, "Sending url: {0}", mcBridge.QrCodeUrl);
+
+ SendText("xconfiguration userinterface custommessage: \"Scan the QR code with a mobile phone to get started\"");
+ SendText("xconfiguration userinterface osd halfwakemessage: \"Tap the touch panel or scan the QR code with a mobile phone to get started\"");
+
+ var checksum = !String.IsNullOrEmpty(mcBridge.QrCodeChecksum)
+ ? String.Format("checksum: {0} ", mcBridge.QrCodeChecksum)
+ : String.Empty;
+
+ SendText(String.Format(
+ "xcommand userinterface branding fetch {1}type: branding url: {0}",
+ mcBridge.QrCodeUrl, checksum));
+ SendText(String.Format(
+ "xcommand userinterface branding fetch {1}type: halfwakebranding url: {0}",
+ mcBridge.QrCodeUrl, checksum));
+ }
+
+ private void SendBrandingUrl()
+ {
+ Debug.Console(1, this, "Sending url: {0}", _brandingUrl);
+
+ SendText(String.Format("xcommand userinterface branding fetch type: branding url: {0}",
+ _brandingUrl));
+ SendText(String.Format("xcommand userinterface branding fetch type: halfwakebranding url: {0}",
+ _brandingUrl));
+ }
+ ///
+ /// Starts the HTTP feedback server and syncronizes state of codec
+ ///
+ ///
+ public override bool CustomActivate()
+ {
+ CrestronConsole.AddNewConsoleCommand(SetCommDebug, "SetCodecCommDebug", "0 for Off, 1 for on", ConsoleAccessLevelEnum.AccessOperator);
+ CrestronConsole.AddNewConsoleCommand(GetPhonebook, "GetCodecPhonebook", "Triggers a refresh of the codec phonebook", ConsoleAccessLevelEnum.AccessOperator);
+ CrestronConsole.AddNewConsoleCommand(GetBookings, "GetCodecBookings", "Triggers a refresh of the booking data for today", ConsoleAccessLevelEnum.AccessOperator);
+
+ return base.CustomActivate();
+ }
+
+ #region Overrides of Device
+
+ public override void Initialize()
+ {
+ var socket = Communication as ISocketStatus;
+ if (socket != null)
+ {
+ socket.ConnectionChange += new EventHandler(socket_ConnectionChange);
+ }
+
+ Communication.Connect();
+
+ CommunicationMonitor.Start();
+
+ const string prefix = "xFeedback register ";
+
+ CliFeedbackRegistrationExpression =
+ prefix + "/Configuration" + Delimiter +
+ prefix + "/Status/Audio" + Delimiter +
+ prefix + "/Status/Call" + Delimiter +
+ prefix + "/Status/Conference/Presentation" + Delimiter +
+ prefix + "/Status/Cameras/SpeakerTrack" + Delimiter +
+ prefix + "/Status/RoomAnalytics" + Delimiter +
+ prefix + "/Status/RoomPreset" + Delimiter +
+ prefix + "/Status/Standby" + Delimiter +
+ prefix + "/Status/Video/Selfview" + Delimiter +
+ prefix + "/Status/Video/Layout" + Delimiter +
+ prefix + "/Status/Video/Input/MainVideoMute" + Delimiter +
+ prefix + "/Bookings" + Delimiter +
+ prefix + "/Event/CallDisconnect" + Delimiter +
+ prefix + "/Event/Bookings" + Delimiter +
+ prefix + "/Event/CameraPresetListUpdated" + Delimiter +
+ prefix + "/Event/UserInterface/Presentation/ExternalSource/Selected/SourceIdentifier" + Delimiter;
+ }
+
+ #endregion
+
+ ///
+ /// Fires when initial codec sync is completed. Used to then send commands to get call history, phonebook, bookings, etc.
+ ///
+ ///
+ ///
+ void SyncState_InitialSyncCompleted(object sender, EventArgs e)
+ {
+ // Fire the ready event
+ SetIsReady();
+ //CommDebuggingIsOn = false;
+
+ GetCallHistory();
+
+ PhonebookRefreshTimer = new CTimer(CheckCurrentHour, 3600000, 3600000); // check each hour to see if the phonebook should be downloaded
+ GetPhonebook(null);
+
+ BookingsRefreshTimer = new CTimer(GetBookings, 900000, 900000); // 15 minute timer to check for new booking info
+ GetBookings(null);
+ }
+
+ public void SetCommDebug(string s)
+ {
+ if (s == "1")
+ {
+ CommDebuggingIsOn = true;
+ Debug.Console(0, this, "Comm Debug Enabled.");
+ }
+ else
+ {
+ CommDebuggingIsOn = false;
+ Debug.Console(0, this, "Comm Debug Disabled.");
+ }
+ }
+
+ void socket_ConnectionChange(object sender, GenericSocketStatusChageEventArgs e)
+ {
+ Debug.Console(1, this, "Socket status change {0}", e.Client.ClientStatus);
+ if (e.Client.IsConnected)
+ {
+ if(!SyncState.LoginMessageWasReceived)
+ LoginMessageReceivedTimer = new CTimer(o => DisconnectClientAndReconnect(), 5000);
+ }
+ else
+ {
+ SyncState.CodecDisconnected();
+ PhonebookSyncState.CodecDisconnected();
+
+ if (PhonebookRefreshTimer != null)
+ {
+ PhonebookRefreshTimer.Stop();
+ PhonebookRefreshTimer = null;
+ }
+
+ if (BookingsRefreshTimer != null)
+ {
+ BookingsRefreshTimer.Stop();
+ BookingsRefreshTimer = null;
+ }
+ }
+ }
+
+ void DisconnectClientAndReconnect()
+ {
+ Debug.Console(1, this, "Retrying connection to codec.");
+
+ Communication.Disconnect();
+
+ RetryConnectionTimer = new CTimer(o => Communication.Connect(), 2000);
+
+ //CrestronEnvironment.Sleep(2000);
+
+ //Communication.Connect();
+ }
+
+ ///
+ /// Gathers responses from the codec (including the delimiter. Responses are checked to see if they contain JSON data and if so, the data is collected until a complete JSON
+ /// message is received before forwarding the message to be deserialized.
+ ///
+ ///
+ ///
+ void Port_LineReceived(object dev, GenericCommMethodReceiveTextArgs args)
+ {
+ if (CommDebuggingIsOn)
+ {
+ if (!JsonFeedbackMessageIsIncoming)
+ Debug.Console(1, this, "RX: '{0}'", args.Text);
+ }
+
+ if (args.Text == "{" + Delimiter) // Check for the beginning of a new JSON message
+ {
+ JsonFeedbackMessageIsIncoming = true;
+
+ if (CommDebuggingIsOn)
+ Debug.Console(1, this, "Incoming JSON message...");
+
+ JsonMessage = new StringBuilder();
+ }
+ else if (args.Text == "}" + Delimiter) // Check for the end of a JSON message
+ {
+ JsonFeedbackMessageIsIncoming = false;
+
+ JsonMessage.Append(args.Text);
+
+ if (CommDebuggingIsOn)
+ Debug.Console(1, this, "Complete JSON Received:\n{0}", JsonMessage.ToString());
+
+ // Enqueue the complete message to be deserialized
+
+ ReceiveQueue.Enqueue(new ProcessStringMessage(JsonMessage.ToString(), DeserializeResponse));
+
+ return;
+ }
+
+ if(JsonFeedbackMessageIsIncoming)
+ {
+ JsonMessage.Append(args.Text);
+
+ //Debug.Console(1, this, "Building JSON:\n{0}", JsonMessage.ToString());
+ return;
+ }
+
+ if (!SyncState.InitialSyncComplete)
+ {
+ switch (args.Text.Trim().ToLower()) // remove the whitespace
+ {
+ case "*r login successful":
+ {
+ SyncState.LoginMessageReceived();
+
+ if(LoginMessageReceivedTimer != null)
+ LoginMessageReceivedTimer.Stop();
+
+ SendText("xPreferences outputmode json");
+ break;
+ }
+ case "xpreferences outputmode json":
+ {
+ if (!SyncState.InitialStatusMessageWasReceived)
+ SendText("xStatus");
+ break;
+ }
+ case "xfeedback register /event/calldisconnect":
+ {
+ SyncState.FeedbackRegistered();
+ break;
+ }
+ }
+ }
+
+
+ }
+
+ ///
+ /// Appends the delimiter and send the command to the codec
+ ///
+ ///
+ public void SendText(string command)
+ {
+ if (CommDebuggingIsOn)
+ Debug.Console(1, this, "Sending: '{0}'", command);
+
+ Communication.SendText(command + Delimiter);
+ }
+
+ void DeserializeResponse(string response)
+ {
+ try
+ {
+ //// Serializer settings. We want to ignore null values and missing members
+ //JsonSerializerSettings settings = new JsonSerializerSettings();
+ //settings.NullValueHandling = NullValueHandling.Ignore;
+ //settings.MissingMemberHandling = MissingMemberHandling.Ignore;
+ //settings.ObjectCreationHandling = ObjectCreationHandling.Auto;
+
+ if (response.IndexOf("\"Status\":{") > -1 || response.IndexOf("\"Status\": {") > -1)
+ {
+ // Status Message
+
+ // Temp object so we can inpsect for call data before simply deserializing
+ CiscoCodecStatus.RootObject tempCodecStatus = new CiscoCodecStatus.RootObject();
+
+ JsonConvert.PopulateObject(response, tempCodecStatus);
+
+ // Check to see if the message contains /Status/Conference/Presentation/LocalInstance and extract source value
+ var conference = tempCodecStatus.Status.Conference;
+
+ if (conference.Presentation.LocalInstance.Count > 0)
+ {
+ if (!string.IsNullOrEmpty(conference.Presentation.LocalInstance[0].ghost))
+ PresentationSource = 0;
+ else if (conference.Presentation.LocalInstance[0].Source != null)
+ {
+ PresentationSource = conference.Presentation.LocalInstance[0].Source.IntValue;
+ }
+ }
+
+ // Check to see if this is a call status message received after the initial status message
+ if (tempCodecStatus.Status.Call.Count > 0)
+ {
+ // Iterate through the call objects in the response
+ foreach (CiscoCodecStatus.Call call in tempCodecStatus.Status.Call)
+ {
+ var tempActiveCall = ActiveCalls.FirstOrDefault(c => c.Id.Equals(call.id));
+
+ if (tempActiveCall != null)
+ {
+ bool changeDetected = false;
+
+ eCodecCallStatus newStatus = eCodecCallStatus.Unknown;
+
+ // Update properties of ActiveCallItem
+ if(call.Status != null)
+ if (!string.IsNullOrEmpty(call.Status.Value))
+ {
+ tempActiveCall.Status = CodecCallStatus.ConvertToStatusEnum(call.Status.Value);
+
+ if (newStatus == eCodecCallStatus.Connected)
+ GetCallHistory();
+
+ changeDetected = true;
+ }
+ if (call.CallType != null)
+ if (!string.IsNullOrEmpty(call.CallType.Value))
+ {
+ tempActiveCall.Type = CodecCallType.ConvertToTypeEnum(call.CallType.Value);
+ changeDetected = true;
+ }
+ if (call.DisplayName != null)
+ if (!string.IsNullOrEmpty(call.DisplayName.Value))
+ {
+ tempActiveCall.Name = call.DisplayName.Value;
+ changeDetected = true;
+ }
+ if (call.Direction != null)
+ {
+ if (!string.IsNullOrEmpty(call.Direction.Value))
+ {
+ tempActiveCall.Direction = CodecCallDirection.ConvertToDirectionEnum(call.Direction.Value);
+ changeDetected = true;
+ }
+ }
+
+ if (changeDetected)
+ {
+ SetSelfViewMode();
+ OnCallStatusChange(tempActiveCall);
+ ListCalls();
+ }
+ }
+ else if( call.ghost == null ) // if the ghost value is present the call has ended already
+ {
+ // Create a new call item
+ var newCallItem = new CodecActiveCallItem()
+ {
+ Id = call.id,
+ Status = CodecCallStatus.ConvertToStatusEnum(call.Status.Value),
+ Name = call.DisplayName.Value,
+ Number = call.RemoteNumber.Value,
+ Type = CodecCallType.ConvertToTypeEnum(call.CallType.Value),
+ Direction = CodecCallDirection.ConvertToDirectionEnum(call.Direction.Value)
+ };
+
+ // Add it to the ActiveCalls List
+ ActiveCalls.Add(newCallItem);
+
+ ListCalls();
+
+ SetSelfViewMode();
+ OnCallStatusChange(newCallItem);
+ }
+
+ }
+
+ }
+
+ // Check for Room Preset data (comes in partial, so we need to handle these responses differently to prevent appending duplicate items
+ var tempPresets = tempCodecStatus.Status.RoomPreset;
+
+ if (tempPresets.Count > 0)
+ {
+ // Create temporary list to store the existing items from the CiscoCodecStatus.RoomPreset collection
+ List existingRoomPresets = new List();
+ // Add the existing items to the temporary list
+ existingRoomPresets.AddRange(CodecStatus.Status.RoomPreset);
+ // Populate the CodecStatus object (this will append new values to the RoomPreset collection
+ JsonConvert.PopulateObject(response, CodecStatus);
+
+ JObject jResponse = JObject.Parse(response);
+
+ IList roomPresets = jResponse["Status"]["RoomPreset"].Children().ToList();
+ // Iterate the new items in this response agains the temporary list. Overwrite any existing items and add new ones.
+ foreach (var preset in tempPresets)
+ {
+ // First fine the existing preset that matches the id
+ var existingPreset = existingRoomPresets.FirstOrDefault(p => p.id.Equals(preset.id));
+ if (existingPreset != null)
+ {
+ Debug.Console(1, this, "Existing Room Preset with ID: {0} found. Updating.", existingPreset.id);
+
+ JToken updatedPreset = null;
+
+ // Find the JToken from the response with the matching id
+ foreach (var jPreset in roomPresets)
+ {
+ if (jPreset["id"].Value() == existingPreset.id)
+ updatedPreset = jPreset;
+ }
+
+ if (updatedPreset != null)
+ {
+ // use PopulateObject to overlay the partial data onto the existing object
+ JsonConvert.PopulateObject(updatedPreset.ToString(), existingPreset);
+ }
+
+ }
+ else
+ {
+ Debug.Console(1, this, "New Room Preset with ID: {0}. Adding.", preset.id);
+ existingRoomPresets.Add(preset);
+ }
+ }
+
+ // Replace the list in the CodecStatus object with the processed list
+ CodecStatus.Status.RoomPreset = existingRoomPresets;
+
+ // Generecise the list
+ NearEndPresets = RoomPresets.GetGenericPresets(CodecStatus.Status.RoomPreset);
+
+ var handler = CodecRoomPresetsListHasChanged;
+ if (handler != null)
+ {
+ handler(this, new EventArgs());
+ }
+ }
+ else
+ {
+ JsonConvert.PopulateObject(response, CodecStatus);
+ }
+
+ if (!SyncState.InitialStatusMessageWasReceived)
+ {
+ SyncState.InitialStatusMessageReceived();
+
+ if (!SyncState.InitialConfigurationMessageWasReceived)
+ SendText("xConfiguration");
+ }
+ }
+ else if (response.IndexOf("\"Configuration\":{") > -1 || response.IndexOf("\"Configuration\": {") > -1)
+ {
+ // Configuration Message
+
+ JsonConvert.PopulateObject(response, CodecConfiguration);
+
+ if (!SyncState.InitialConfigurationMessageWasReceived)
+ {
+ SyncState.InitialConfigurationMessageReceived();
+ if (!SyncState.FeedbackWasRegistered)
+ {
+ SendText(CliFeedbackRegistrationExpression);
+ }
+ }
+
+ }
+ else if (response.IndexOf("\"Event\":{") > -1 || response.IndexOf("\"Event\": {") > -1)
+ {
+ if (response.IndexOf("\"CallDisconnect\":{") > -1 || response.IndexOf("\"CallDisconnect\": {") > -1)
+ {
+ CiscoCodecEvents.RootObject eventReceived = new CiscoCodecEvents.RootObject();
+
+ JsonConvert.PopulateObject(response, eventReceived);
+
+ EvalutateDisconnectEvent(eventReceived);
+ }
+ else if (response.IndexOf("\"Bookings\":{") > -1 || response.IndexOf("\"Bookings\": {") > -1) // The list has changed, reload it
+ {
+ GetBookings(null);
+ }
+
+ else if (response.IndexOf("\"UserInterface\":{") > -1 || response.IndexOf("\"UserInterface\": {") > -1) // External Source Trigger
+ {
+ CiscoCodecEvents.RootObject eventReceived = new CiscoCodecEvents.RootObject();
+ JsonConvert.PopulateObject(response, eventReceived);
+ Debug.Console(2, this, "*** Got an External Source Selection {0} {1}", eventReceived, eventReceived.Event.UserInterface, eventReceived.Event.UserInterface.Presentation.ExternalSource.Selected.SourceIdentifier.Value);
+
+ if (RunRouteAction != null && !_externalSourceChangeRequested)
+ {
+ RunRouteAction(eventReceived.Event.UserInterface.Presentation.ExternalSource.Selected.SourceIdentifier.Value, null);
+ }
+
+ _externalSourceChangeRequested = false;
+ }
+ }
+ else if (response.IndexOf("\"CommandResponse\":{") > -1 || response.IndexOf("\"CommandResponse\": {") > -1)
+ {
+ // CommandResponse Message
+
+ if (response.IndexOf("\"CallHistoryRecentsResult\":{") > -1 || response.IndexOf("\"CallHistoryRecentsResult\": {") > -1)
+ {
+ var codecCallHistory = new CiscoCallHistory.RootObject();
+
+ JsonConvert.PopulateObject(response, codecCallHistory);
+
+ CallHistory.ConvertCiscoCallHistoryToGeneric(codecCallHistory.CommandResponse.CallHistoryRecentsResult.Entry);
+ }
+ else if (response.IndexOf("\"CallHistoryDeleteEntryResult\":{") > -1 || response.IndexOf("\"CallHistoryDeleteEntryResult\": {") > -1)
+ {
+ GetCallHistory();
+ }
+ else if (response.IndexOf("\"PhonebookSearchResult\":{") > -1 || response.IndexOf("\"PhonebookSearchResult\": {") > -1)
+ {
+ var codecPhonebookResponse = new CiscoCodecPhonebook.RootObject();
+
+ JsonConvert.PopulateObject(response, codecPhonebookResponse);
+
+ if (!PhonebookSyncState.InitialPhonebookFoldersWasReceived)
+ {
+ // Check if the phonebook has any folders
+ PhonebookSyncState.InitialPhonebookFoldersReceived();
+
+ PhonebookSyncState.SetPhonebookHasFolders(codecPhonebookResponse.CommandResponse.PhonebookSearchResult.Folder.Count > 0);
+
+ if (PhonebookSyncState.PhonebookHasFolders)
+ {
+ DirectoryRoot.AddFoldersToDirectory(CiscoCodecPhonebook.GetRootFoldersFromSearchResult(codecPhonebookResponse.CommandResponse.PhonebookSearchResult));
+ }
+
+ // Get the number of contacts in the phonebook
+ GetPhonebookContacts();
+ }
+ else if (!PhonebookSyncState.NumberOfContactsWasReceived)
+ {
+ // Store the total number of contacts in the phonebook
+ PhonebookSyncState.SetNumberOfContacts(Int32.Parse(codecPhonebookResponse.CommandResponse.PhonebookSearchResult.ResultInfo.TotalRows.Value));
+
+ DirectoryRoot.AddContactsToDirectory(CiscoCodecPhonebook.GetRootContactsFromSearchResult(codecPhonebookResponse.CommandResponse.PhonebookSearchResult));
+
+ PhonebookSyncState.PhonebookRootEntriesReceived();
+
+ PrintDirectory(DirectoryRoot);
+ }
+ else if (PhonebookSyncState.InitialSyncComplete)
+ {
+ var directoryResults = new CodecDirectory();
+
+ if(codecPhonebookResponse.CommandResponse.PhonebookSearchResult.ResultInfo.TotalRows.Value != "0")
+ directoryResults = CiscoCodecPhonebook.ConvertCiscoPhonebookToGeneric(codecPhonebookResponse.CommandResponse.PhonebookSearchResult);
+
+ PrintDirectory(directoryResults);
+
+ DirectoryBrowseHistory.Add(directoryResults);
+
+ OnDirectoryResultReturned(directoryResults);
+
+ }
+ }
+ else if (response.IndexOf("\"BookingsListResult\":{") > -1)
+ {
+ var codecBookings = new CiscoCodecBookings.RootObject();
+
+ JsonConvert.PopulateObject(response, codecBookings);
+
+ if(codecBookings.CommandResponse.BookingsListResult.ResultInfo.TotalRows.Value != "0")
+ CodecSchedule.Meetings = CiscoCodecBookings.GetGenericMeetingsFromBookingResult(codecBookings.CommandResponse.BookingsListResult.Booking);
+
+ BookingsRefreshTimer.Reset(900000, 900000);
+ }
+
+ }
+
+ }
+ catch (Exception ex)
+ {
+ Debug.Console(1, this, "Error Deserializing feedback from codec: {0}", ex);
+ }
+ }
+
+ ///
+ /// Call when directory results are updated
+ ///
+ ///
+ void OnDirectoryResultReturned(CodecDirectory result)
+ {
+ CurrentDirectoryResultIsNotDirectoryRoot.FireUpdate();
+
+ // This will return the latest results to all UIs. Multiple indendent UI Directory browsing will require a different methodology
+ var handler = DirectoryResultReturned;
+ if (handler != null)
+ {
+ handler(this, new DirectoryEventArgs()
+ {
+ Directory = result,
+ DirectoryIsOnRoot = !CurrentDirectoryResultIsNotDirectoryRoot.BoolValue
+ });
+ }
+
+ PrintDirectory(result);
+ }
+
+ ///
+ /// Evaluates an event received from the codec
+ ///
+ ///
+ void EvalutateDisconnectEvent(CiscoCodecEvents.RootObject eventReceived)
+ {
+ if (eventReceived.Event.CallDisconnect != null)
+ {
+ var tempActiveCall = ActiveCalls.FirstOrDefault(c => c.Id.Equals(eventReceived.Event.CallDisconnect.CallId.Value));
+
+ // Remove the call from the Active calls list
+ if (tempActiveCall != null)
+ {
+ ActiveCalls.Remove(tempActiveCall);
+
+ ListCalls();
+
+ SetSelfViewMode();
+ // Notify of the call disconnection
+ SetNewCallStatusAndFireCallStatusChange(eCodecCallStatus.Disconnected, tempActiveCall);
+
+ GetCallHistory();
+ }
+ }
+ }
+
+ ///
+ ///
+ ///
+ ///
+ public override void ExecuteSwitch(object selector)
+ {
+ (selector as Action)();
+ PresentationSourceKey = selector.ToString();
+ }
+
+ ///
+ /// This is necessary for devices that are "routers" in the middle of the path, even though it only has one output and
+ /// may only have one input.
+ ///
+ public void ExecuteSwitch(object inputSelector, object outputSelector, eRoutingSignalType signalType)
+ {
+ ExecuteSwitch(inputSelector);
+ PresentationSourceKey = inputSelector.ToString();
+ }
+
+
+ ///
+ /// Gets the ID of the last connected call
+ ///
+ ///
+ public string GetCallId()
+ {
+ string callId = null;
+
+ if (ActiveCalls.Count > 1)
+ {
+ var lastCallIndex = ActiveCalls.Count - 1;
+ callId = ActiveCalls[lastCallIndex].Id;
+ }
+ else if (ActiveCalls.Count == 1)
+ callId = ActiveCalls[0].Id;
+
+ return callId;
+
+ }
+
+ public void GetCallHistory()
+ {
+ SendText("xCommand CallHistory Recents Limit: 20 Order: OccurrenceTime");
+ }
+
+ ///
+ /// Required for IHasScheduleAwareness
+ ///
+ public void GetSchedule()
+ {
+ GetBookings(null);
+ }
+
+ ///
+ /// Gets the bookings for today
+ ///
+ ///
+ public void GetBookings(object command)
+ {
+ Debug.Console(1, this, "Retrieving Booking Info from Codec. Current Time: {0}", DateTime.Now.ToLocalTime());
+
+ SendText("xCommand Bookings List Days: 1 DayOffset: 0");
+ }
+
+ ///
+ /// Checks to see if it is 2am (or within that hour) and triggers a download of the phonebook
+ ///
+ ///
+ public void CheckCurrentHour(object o)
+ {
+ if (DateTime.Now.Hour == 2)
+ {
+ Debug.Console(1, this, "Checking hour to see if phonebook should be downloaded. Current hour is {0}", DateTime.Now.Hour);
+
+ GetPhonebook(null);
+ PhonebookRefreshTimer.Reset(3600000, 3600000);
+ }
+ }
+
+ ///
+ /// Triggers a refresh of the codec phonebook
+ ///
+ /// Just to allow this method to be called from a console command
+ public void GetPhonebook(string command)
+ {
+ PhonebookSyncState.CodecDisconnected();
+
+ DirectoryRoot = new CodecDirectory();
+
+ GetPhonebookFolders();
+ }
+
+ private void GetPhonebookFolders()
+ {
+ // Get Phonebook Folders (determine local/corporate from config, and set results limit)
+ SendText(string.Format("xCommand Phonebook Search PhonebookType: {0} ContactType: Folder", PhonebookMode));
+ }
+
+ private void GetPhonebookContacts()
+ {
+ // Get Phonebook Folders (determine local/corporate from config, and set results limit)
+ SendText(string.Format("xCommand Phonebook Search PhonebookType: {0} ContactType: Contact Limit: {1}", PhonebookMode, PhonebookResultsLimit));
+ }
+
+ ///
+ /// Searches the codec phonebook for all contacts matching the search string
+ ///
+ ///
+ public void SearchDirectory(string searchString)
+ {
+ SendText(string.Format("xCommand Phonebook Search SearchString: \"{0}\" PhonebookType: {1} ContactType: Contact Limit: {2}", searchString, PhonebookMode, PhonebookResultsLimit));
+ }
+
+ ///
+ /// // Get contents of a specific folder in the phonebook
+ ///
+ ///
+ public void GetDirectoryFolderContents(string folderId)
+ {
+ SendText(string.Format("xCommand Phonebook Search FolderId: {0} PhonebookType: {1} ContactType: Any Limit: {2}", folderId, PhonebookMode, PhonebookResultsLimit));
+ }
+
+ ///
+ /// Sets the parent folder contents or the directory root as teh current directory and fires the event. Used to browse up a level
+ ///
+ ///
+ public void GetDirectoryParentFolderContents()
+ {
+ var currentDirectory = new CodecDirectory();
+
+ if (DirectoryBrowseHistory.Count > 0)
+ {
+ var lastItemIndex = DirectoryBrowseHistory.Count - 1;
+ var parentDirectoryContents = DirectoryBrowseHistory[lastItemIndex];
+
+ DirectoryBrowseHistory.Remove(DirectoryBrowseHistory[lastItemIndex]);
+
+ currentDirectory = parentDirectoryContents;
+
+ }
+ else
+ {
+ currentDirectory = DirectoryRoot;
+ }
+
+ OnDirectoryResultReturned(currentDirectory);
+ }
+
+ ///
+ /// Clears the session browse history and fires the event with the directory root
+ ///
+ public void SetCurrentDirectoryToRoot()
+ {
+ DirectoryBrowseHistory.Clear();
+
+ OnDirectoryResultReturned(DirectoryRoot);
+ }
+
+ ///
+ /// Prints the directory to console
+ ///
+ ///
+ void PrintDirectory(CodecDirectory directory)
+ {
+ if (Debug.Level > 0)
+ {
+ Debug.Console(1, this, "Directory Results:\n");
+
+ foreach (DirectoryItem item in directory.CurrentDirectoryResults)
+ {
+ if (item is DirectoryFolder)
+ {
+ Debug.Console(1, this, "[+] {0}", item.Name);
+ }
+ else if (item is DirectoryContact)
+ {
+ Debug.Console(1, this, "{0}", item.Name);
+ }
+ }
+ Debug.Console(1, this, "Directory is on Root Level: {0}", !CurrentDirectoryResultIsNotDirectoryRoot.BoolValue);
+ }
+
+ }
+
+ ///
+ /// Simple dial method
+ ///
+ ///
+ public override void Dial(string number)
+ {
+ SendText(string.Format("xCommand Dial Number: \"{0}\"", number));
+ }
+
+ ///
+ /// Dials a specific meeting
+ ///
+ ///
+ public override void Dial(Meeting meeting)
+ {
+ foreach (Call c in meeting.Calls)
+ {
+ Dial(c.Number, c.Protocol, c.CallRate, c.CallType, meeting.Id);
+ }
+ }
+
+ ///
+ /// Detailed dial method
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public void Dial(string number, string protocol, string callRate, string callType, string meetingId)
+ {
+ SendText(string.Format("xCommand Dial Number: \"{0}\" Protocol: {1} CallRate: {2} CallType: {3} BookingId: {4}", number, protocol, callRate, callType, meetingId));
+ }
+
+ public override void EndCall(CodecActiveCallItem activeCall)
+ {
+ SendText(string.Format("xCommand Call Disconnect CallId: {0}", activeCall.Id));
+ }
+
+ public override void EndAllCalls()
+ {
+ foreach (CodecActiveCallItem activeCall in ActiveCalls)
+ {
+ SendText(string.Format("xCommand Call Disconnect CallId: {0}", activeCall.Id));
+ }
+ }
+
+ public override void AcceptCall(CodecActiveCallItem item)
+ {
+ SendText("xCommand Call Accept");
+ }
+
+ public override void RejectCall(CodecActiveCallItem item)
+ {
+ SendText("xCommand Call Reject");
+ }
+
+ public override void SendDtmf(string s)
+ {
+ SendText(string.Format("xCommand Call DTMFSend CallId: {0} DTMFString: \"{1}\"", GetCallId(), s));
+ }
+
+ public void SelectPresentationSource(int source)
+ {
+ PresentationSource = source;
+
+ StartSharing();
+ }
+
+ ///
+ /// Select source 1 as the presetnation source
+ ///
+ public void SelectPresentationSource1()
+ {
+ SelectPresentationSource(2);
+ }
+
+ ///
+ /// Select source 2 as the presetnation source
+ ///
+ public void SelectPresentationSource2()
+ {
+ SelectPresentationSource(3);
+ }
+
+ ///
+ /// Starts presentation sharing
+ ///
+ public override void StartSharing()
+ {
+ string sendingMode = string.Empty;
+
+ if (IsInCall)
+ sendingMode = "LocalRemote";
+ else
+ sendingMode = "LocalOnly";
+
+ if(PresentationSource > 0)
+ SendText(string.Format("xCommand Presentation Start PresentationSource: {0} SendingMode: {1}", PresentationSource, sendingMode));
+ }
+
+ ///
+ /// Stops sharing the current presentation
+ ///
+ public override void StopSharing()
+ {
+ PresentationSource = 0;
+
+ SendText("xCommand Presentation Stop");
+ }
+
+ public override void PrivacyModeOn()
+ {
+ SendText("xCommand Audio Microphones Mute");
+ }
+
+ public override void PrivacyModeOff()
+ {
+ SendText("xCommand Audio Microphones Unmute");
+ }
+
+ public override void PrivacyModeToggle()
+ {
+ SendText("xCommand Audio Microphones ToggleMute");
+ }
+
+ public override void MuteOff()
+ {
+ SendText("xCommand Audio Volume Unmute");
+ }
+
+ public override void MuteOn()
+ {
+ SendText("xCommand Audio Volume Mute");
+ }
+
+ public override void MuteToggle()
+ {
+ SendText("xCommand Audio Volume ToggleMute");
+ }
+
+ ///
+ /// Increments the voluem
+ ///
+ ///
+ public override void VolumeUp(bool pressRelease)
+ {
+ SendText("xCommand Audio Volume Increase");
+ }
+
+ ///
+ /// Decrements the volume
+ ///
+ ///
+ public override void VolumeDown(bool pressRelease)
+ {
+ SendText("xCommand Audio Volume Decrease");
+ }
+
+ ///
+ /// Scales the level and sets the codec to the specified level within its range
+ ///
+ /// level from slider (0-65535 range)
+ public override void SetVolume(ushort level)
+ {
+ var scaledLevel = CrestronEnvironment.ScaleWithLimits(level, 65535, 0, 100, 0);
+ SendText(string.Format("xCommand Audio Volume Set Level: {0}", scaledLevel));
+ }
+
+ ///
+ /// Recalls the default volume on the codec
+ ///
+ public void VolumeSetToDefault()
+ {
+ SendText("xCommand Audio Volume SetToDefault");
+ }
+
+ ///
+ /// Puts the codec in standby mode
+ ///
+ public override void StandbyActivate()
+ {
+ SendText("xCommand Standby Activate");
+ }
+
+ ///
+ /// Wakes the codec from standby
+ ///
+ public override void StandbyDeactivate()
+ {
+ SendText("xCommand Standby Deactivate");
+ }
+
+ public override void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge)
+ {
+ LinkVideoCodecToApi(this, trilist, joinStart, joinMapKey, bridge);
+ }
+
+ ///
+ /// Reboots the codec
+ ///
+ public void Reboot()
+ {
+ SendText("xCommand SystemUnit Boot Action: Restart");
+ }
+
+ ///
+ /// Sets SelfView Mode based on config
+ ///
+ void SetSelfViewMode()
+ {
+ if (!IsInCall)
+ {
+ SelfViewModeOff();
+ }
+ else
+ {
+ if (ShowSelfViewByDefault)
+ SelfViewModeOn();
+ else
+ SelfViewModeOff();
+ }
+ }
+
+ ///
+ /// Turns on Selfview Mode
+ ///
+ public void SelfViewModeOn()
+ {
+ SendText("xCommand Video Selfview Set Mode: On");
+ }
+
+ ///
+ /// Turns off Selfview Mode
+ ///
+ public void SelfViewModeOff()
+ {
+ SendText("xCommand Video Selfview Set Mode: Off");
+ }
+
+ ///
+ /// Toggles Selfview mode on/off
+ ///
+ public void SelfViewModeToggle()
+ {
+ string mode = string.Empty;
+
+ if (CodecStatus.Status.Video.Selfview.Mode.BoolValue)
+ mode = "Off";
+ else
+ mode = "On";
+
+ SendText(string.Format("xCommand Video Selfview Set Mode: {0}", mode));
+ }
+
+ ///
+ /// Sets a specified position for the selfview PIP window
+ ///
+ ///
+ public void SelfviewPipPositionSet(CodecCommandWithLabel position)
+ {
+ SendText(string.Format("xCommand Video Selfview Set Mode: On PIPPosition: {0}", position.Command));
+ }
+
+ ///
+ /// Toggles to the next selfview PIP position
+ ///
+ public void SelfviewPipPositionToggle()
+ {
+ if (CurrentSelfviewPipPosition != null)
+ {
+ var nextPipPositionIndex = SelfviewPipPositions.IndexOf(CurrentSelfviewPipPosition) + 1;
+
+ if (nextPipPositionIndex >= SelfviewPipPositions.Count) // Check if we need to loop back to the first item in the list
+ nextPipPositionIndex = 0;
+
+ SelfviewPipPositionSet(SelfviewPipPositions[nextPipPositionIndex]);
+ }
+ }
+
+ ///
+ /// Sets a specific local layout
+ ///
+ ///
+ public void LocalLayoutSet(CodecCommandWithLabel layout)
+ {
+ SendText(string.Format("xCommand Video Layout LayoutFamily Set Target: local LayoutFamily: {0}", layout.Command));
+ }
+
+ ///
+ /// Toggles to the next local layout
+ ///
+ public void LocalLayoutToggle()
+ {
+ if(CurrentLocalLayout != null)
+ {
+ var nextLocalLayoutIndex = LocalLayouts.IndexOf(CurrentLocalLayout) + 1;
+
+ if (nextLocalLayoutIndex >= LocalLayouts.Count) // Check if we need to loop back to the first item in the list
+ nextLocalLayoutIndex = 0;
+
+ LocalLayoutSet(LocalLayouts[nextLocalLayoutIndex]);
+ }
+ }
+
+ ///
+ /// Toggles between single/prominent layouts
+ ///
+ public void LocalLayoutToggleSingleProminent()
+ {
+ if (CurrentLocalLayout != null)
+ {
+ if (CurrentLocalLayout.Label != "Prominent")
+ LocalLayoutSet(LocalLayouts.FirstOrDefault(l => l.Label.Equals("Prominent")));
+ else
+ LocalLayoutSet(LocalLayouts.FirstOrDefault(l => l.Label.Equals("Single")));
+ }
+
+ }
+
+ ///
+ ///
+ ///
+ public void MinMaxLayoutToggle()
+ {
+ if (PresentationViewMaximizedFeedback.BoolValue)
+ CurrentPresentationView = "Minimized";
+ else
+ CurrentPresentationView = "Maximized";
+
+ SendText(string.Format("xCommand Video PresentationView Set View: {0}", CurrentPresentationView));
+ PresentationViewMaximizedFeedback.FireUpdate();
+ }
+
+ ///
+ /// Calculates the current selfview PIP position
+ ///
+ void ComputeSelfviewPipStatus()
+ {
+ CurrentSelfviewPipPosition = SelfviewPipPositions.FirstOrDefault(p => p.Command.ToLower().Equals(CodecStatus.Status.Video.Selfview.PIPPosition.Value.ToLower()));
+
+ if(CurrentSelfviewPipPosition != null)
+ SelfviewIsOnFeedback.FireUpdate();
+ }
+
+ ///
+ /// Calculates the current local Layout
+ ///
+ void ComputeLocalLayout()
+ {
+ CurrentLocalLayout = LocalLayouts.FirstOrDefault(l => l.Command.ToLower().Equals(CodecStatus.Status.Video.Layout.LayoutFamily.Local.Value.ToLower()));
+
+ if (CurrentLocalLayout != null)
+ LocalLayoutFeedback.FireUpdate();
+ }
+
+ public void RemoveCallHistoryEntry(CodecCallHistory.CallHistoryEntry entry)
+ {
+ SendText(string.Format("xCommand CallHistory DeleteEntry CallHistoryId: {0} AcknowledgeConsecutiveDuplicates: True", entry.OccurrenceHistoryId));
+ }
+
+ #region IHasCameraSpeakerTrack
+
+ public void CameraAutoModeToggle()
+ {
+ if (!CameraAutoModeIsOnFeedback.BoolValue)
+ {
+ SendText("xCommand Cameras SpeakerTrack Activate");
+ }
+ else
+ {
+ SendText("xCommand Cameras SpeakerTrack Deactivate");
+ }
+ }
+
+ public void CameraAutoModeOn()
+ {
+ if (CameraIsOffFeedback.BoolValue)
+ {
+ CameraMuteOff();
+ }
+
+ SendText("xCommand Cameras SpeakerTrack Activate");
+ }
+
+ public void CameraAutoModeOff()
+ {
+ if (CameraIsOffFeedback.BoolValue)
+ {
+ CameraMuteOff();
+ }
+
+ SendText("xCommand Cameras SpeakerTrack Deactivate");
+ }
+
+ #endregion
+
+ ///
+ /// Builds the cameras List. Could later be modified to build from config data
+ ///
+ void SetUpCameras()
+ {
+ // Add the internal camera
+ Cameras = new List();
+
+ var internalCamera = new CiscoSparkCamera(Key + "-camera1", "Near End", this, 1);
+
+ if(CodecStatus.Status.Cameras.Camera.Count > 0)
+ internalCamera.SetCapabilites(CodecStatus.Status.Cameras.Camera[0].Capabilities.Options.Value);
+ else
+ // Somehow subscribe to the event on the Options.Value property and update when it changes.
+
+ Cameras.Add(internalCamera);
+
+ // Add the far end camera
+ var farEndCamera = new CiscoFarEndCamera(Key + "-cameraFar", "Far End", this);
+ Cameras.Add(farEndCamera);
+
+ SelectedCameraFeedback = new StringFeedback(() => SelectedCamera.Key);
+
+ ControllingFarEndCameraFeedback = new BoolFeedback(() => SelectedCamera is IAmFarEndCamera);
+
+ DeviceManager.AddDevice(internalCamera);
+ DeviceManager.AddDevice(farEndCamera);
+
+ NearEndPresets = new List(15);
+
+ FarEndRoomPresets = new List(15);
+
+ // Add the far end presets
+ for (int i = 1; i <= FarEndRoomPresets.Capacity; i++)
+ {
+ var label = string.Format("Far End Preset {0}", i);
+ FarEndRoomPresets.Add(new CodecRoomPreset(i, label, true, false));
+ }
+
+ SelectedCamera = internalCamera; ; // call the method to select the camera and ensure the feedbacks get updated.
+ }
+
+ #region IHasCodecCameras Members
+
+ public event EventHandler CameraSelected;
+
+ public List Cameras { get; private set; }
+
+ public StringFeedback SelectedCameraFeedback { get; private set; }
+
+ private CameraBase _selectedCamera;
+
+ ///
+ /// Returns the selected camera
+ ///
+ public CameraBase SelectedCamera
+ {
+ get
+ {
+ return _selectedCamera;
+ }
+ private set
+ {
+ _selectedCamera = value;
+ SelectedCameraFeedback.FireUpdate();
+ ControllingFarEndCameraFeedback.FireUpdate();
+ if (CameraIsOffFeedback.BoolValue)
+ CameraMuteOff();
+
+ var handler = CameraSelected;
+ if (handler != null)
+ {
+ handler(this, new CameraSelectedEventArgs(SelectedCamera));
+ }
+ }
+ }
+
+ public void SelectCamera(string key)
+ {
+ var camera = Cameras.FirstOrDefault(c => c.Key.IndexOf(key, StringComparison.OrdinalIgnoreCase) > -1);
+ if (camera != null)
+ {
+ Debug.Console(2, this, "Selected Camera with key: '{0}'", camera.Key);
+ SelectedCamera = camera;
+ }
+ else
+ Debug.Console(2, this, "Unable to select camera with key: '{0}'", key);
+ }
+
+ public CameraBase FarEndCamera { get; private set; }
+
+ public BoolFeedback ControllingFarEndCameraFeedback { get; private set; }
+
+ #endregion
+
+ ///
+ ///
+ ///
+ public class CiscoCodecInfo : VideoCodecInfo
+ {
+ public CiscoCodecStatus.RootObject CodecStatus { get; private set; }
+
+ public CiscoCodecConfiguration.RootObject CodecConfiguration { get; private set; }
+
+ public override bool MultiSiteOptionIsEnabled
+ {
+ get
+ {
+ if (!string.IsNullOrEmpty(CodecStatus.Status.SystemUnit.Software.OptionKeys.MultiSite.Value) && CodecStatus.Status.SystemUnit.Software.OptionKeys.MultiSite.Value.ToLower() == "true")
+ return true;
+ else
+ return false;
+ }
+
+ }
+ public override string IpAddress
+ {
+ get
+ {
+ if (CodecConfiguration.Configuration.Network != null)
+ {
+ if (CodecConfiguration.Configuration.Network.Count > 0)
+ return CodecConfiguration.Configuration.Network[0].IPv4.Address.Value;
+ }
+ return string.Empty;
+ }
+ }
+ public override string E164Alias
+ {
+ get
+ {
+ if (CodecConfiguration.Configuration.H323 != null && CodecConfiguration.Configuration.H323.H323Alias.E164 != null)
+ {
+ return CodecConfiguration.Configuration.H323.H323Alias.E164.Value;
+ }
+ else
+ {
+ return string.Empty;
+ }
+ }
+ }
+ public override string H323Id
+ {
+ get
+ {
+ if (CodecConfiguration.Configuration.H323 != null && CodecConfiguration.Configuration.H323.H323Alias != null
+ && CodecConfiguration.Configuration.H323.H323Alias.ID != null)
+ {
+ return CodecConfiguration.Configuration.H323.H323Alias.ID.Value;
+ }
+ else
+ {
+ return string.Empty;
+ }
+ }
+ }
+ public override string SipPhoneNumber
+ {
+ get
+ {
+ if (CodecStatus.Status.SIP != null && CodecStatus.Status.SIP.Registration.Count > 0)
+ {
+ var match = Regex.Match(CodecStatus.Status.SIP.Registration[0].URI.Value, @"(\d+)"); // extract numbers only
+ if (match.Success)
+ {
+ Debug.Console(1, "Extracted phone number as '{0}' from string '{1}'", match.Groups[1].Value, CodecStatus.Status.SIP.Registration[0].URI.Value);
+ return match.Groups[1].Value;
+ }
+ else
+ {
+ Debug.Console(1, "Unable to extract phone number from string: '{0}'", CodecStatus.Status.SIP.Registration[0].URI.Value);
+ return string.Empty;
+ }
+ }
+ else
+ {
+ Debug.Console(1, "Unable to extract phone number. No SIP Registration items found");
+ return string.Empty;
+ }
+ }
+ }
+
+ public override string SipUri
+ {
+ get
+ {
+ if (CodecStatus.Status.SIP != null && CodecStatus.Status.SIP.AlternateURI.Primary.URI.Value != null)
+ {
+ return CodecStatus.Status.SIP.AlternateURI.Primary.URI.Value;
+ }
+ else if (CodecStatus.Status.UserInterface != null &&
+ CodecStatus.Status.UserInterface.ContactInfo.ContactMethod[0].Number.Value != null)
+ {
+ return CodecStatus.Status.UserInterface.ContactInfo.ContactMethod[0].Number.Value;
+ }
+ else
+ return string.Empty;
+ }
+ }
+
+ public override bool AutoAnswerEnabled
+ {
+ get
+ {
+ if (CodecConfiguration.Configuration.Conference.AutoAnswer.Mode.Value.ToLower() == "on")
+ return true;
+ else
+ return false;
+ }
+ }
+
+ public CiscoCodecInfo(CiscoCodecStatus.RootObject status, CiscoCodecConfiguration.RootObject configuration)
+ {
+ CodecStatus = status;
+ CodecConfiguration = configuration;
+ }
+ }
+
+
+ #region IHasCameraPresets Members
+
+ public event EventHandler CodecRoomPresetsListHasChanged;
+
+ public List NearEndPresets { get; private set; }
+
+ public List FarEndRoomPresets { get; private set; }
+
+ public void CodecRoomPresetSelect(int preset)
+ {
+ Debug.Console(1, this, "Selecting Preset: {0}", preset);
+ if (SelectedCamera is IAmFarEndCamera)
+ SelectFarEndPreset(preset);
+ else
+ SendText(string.Format("xCommand RoomPreset Activate PresetId: {0}", preset));
+ }
+
+ public void CodecRoomPresetStore(int preset, string description)
+ {
+ SendText(string.Format("xCommand RoomPreset Store PresetId: {0} Description: \"{1}\" Type: All", preset, description));
+ }
+
+ #endregion
+
+ public void SelectFarEndPreset(int preset)
+ {
+ SendText(string.Format("xCommand Call FarEndControl RoomPreset Activate CallId: {0} PresetId: {1}", GetCallId(), preset));
+ }
+
+
+ #region IHasExternalSourceSwitching Members
+
+ ///
+ /// Wheather the Cisco supports External Source Lists or not
+ ///
+ public bool ExternalSourceListEnabled
+ {
+ get;
+ private set;
+ }
+
+ ///
+ /// The name of the RoutingInputPort to which the upstream external switcher is connected
+ ///
+ public string ExternalSourceInputPort { get; private set; }
+
+ public bool BrandingEnabled { get; private set; }
+ private string _brandingUrl;
+ private bool _sendMcUrl;
+
+ ///
+ /// Adds an external source to the Cisco
+ ///
+ ///
+ ///
+ ///
+ public void AddExternalSource(string connectorId, string key, string name, eExternalSourceType type)
+ {
+ int id = 2;
+ if (connectorId.ToLower() == "hdmiin3")
+ {
+ id = 3;
+ }
+ SendText(string.Format("xCommand UserInterface Presentation ExternalSource Add ConnectorId: {0} SourceIdentifier: \"{1}\" Name: \"{2}\" Type: {3}", id, key, name, type.ToString()));
+ // SendText(string.Format("xCommand UserInterface Presentation ExternalSource State Set SourceIdentifier: \"{0}\" State: Ready", key));
+ Debug.Console(2, this, "Adding ExternalSource {0} {1}", connectorId, name);
+
+ }
+
+
+ ///
+ /// Sets the state of the External Source
+ ///
+ ///
+ ///
+ public void SetExternalSourceState(string key, eExternalSourceMode mode)
+ {
+ SendText(string.Format("xCommand UserInterface Presentation ExternalSource State Set SourceIdentifier: \"{0}\" State: {1}", key, mode.ToString()));
+ }
+ ///
+ /// Clears all external sources on the codec
+ ///
+ public void ClearExternalSources()
+ {
+ SendText("xCommand UserInterface Presentation ExternalSource RemoveAll");
+
+ }
+
+ ///
+ /// Sets the selected source of the available external sources on teh Touch10 UI
+ ///
+ public void SetSelectedSource(string key)
+ {
+ SendText(string.Format("xCommand UserInterface Presentation ExternalSource Select SourceIdentifier: {0}", key));
+ _externalSourceChangeRequested = true;
+ }
+
+ ///
+ /// Action that will run when the External Source is selected.
+ ///
+ public Action RunRouteAction { private get; set; }
+
+
+
+
+
+
+ #endregion
+ #region ExternalDevices
+
+
+
+ #endregion
+
+ #region IHasCameraOff Members
+
+ public BoolFeedback CameraIsOffFeedback { get; private set; }
+
+ public void CameraOff()
+ {
+ CameraMuteOn();
+ }
+
+ #endregion
+
+ public BoolFeedback CameraIsMutedFeedback { get; private set; }
+
+ ///
+ /// Mutes the outgoing camera video
+ ///
+ public void CameraMuteOn()
+ {
+ SendText("xCommand Video Input MainVideo Mute");
+ }
+
+ ///
+ /// Unmutes the outgoing camera video
+ ///
+ public void CameraMuteOff()
+ {
+ SendText("xCommand Video Input MainVideo Unmute");
+ }
+
+ ///
+ /// Toggles the camera mute state
+ ///
+ public void CameraMuteToggle()
+ {
+ if (CameraIsMutedFeedback.BoolValue)
+ CameraMuteOff();
+ else
+ CameraMuteOn();
+ }
+ }
+
+
+ ///
+ /// Represents a codec command that might need to have a friendly label applied for UI feedback purposes
+ ///
+ public class CodecCommandWithLabel
+ {
+ public string Command { get; set; }
+ public string Label { get; set; }
+
+ public CodecCommandWithLabel(string command, string label)
+ {
+ Command = command;
+ Label = label;
+ }
+ }
+
+ ///
+ /// Tracks the initial sycnronization state of the codec when making a connection
+ ///
+ public class CodecSyncState : IKeyed
+ {
+ bool _InitialSyncComplete;
+
+ public event EventHandler InitialSyncCompleted;
+
+ public string Key { get; private set; }
+
+ public bool InitialSyncComplete
+ {
+ get { return _InitialSyncComplete; }
+ private set
+ {
+ if (value == true)
+ {
+ var handler = InitialSyncCompleted;
+ if (handler != null)
+ handler(this, new EventArgs());
+ }
+ _InitialSyncComplete = value;
+ }
+ }
+
+ public bool LoginMessageWasReceived { get; private set; }
+
+ public bool InitialStatusMessageWasReceived { get; private set; }
+
+ public bool InitialConfigurationMessageWasReceived { get; private set; }
+
+ public bool FeedbackWasRegistered { get; private set; }
+
+ public CodecSyncState(string key)
+ {
+ Key = key;
+ CodecDisconnected();
+ }
+
+ public void LoginMessageReceived()
+ {
+ LoginMessageWasReceived = true;
+ Debug.Console(1, this, "Login Message Received.");
+ CheckSyncStatus();
+ }
+
+ public void InitialStatusMessageReceived()
+ {
+ InitialStatusMessageWasReceived = true;
+ Debug.Console(1, this, "Initial Codec Status Message Received.");
+ CheckSyncStatus();
+ }
+
+ public void InitialConfigurationMessageReceived()
+ {
+ InitialConfigurationMessageWasReceived = true;
+ Debug.Console(1, this, "Initial Codec Configuration Message Received.");
+ CheckSyncStatus();
+ }
+
+ public void FeedbackRegistered()
+ {
+ FeedbackWasRegistered = true;
+ Debug.Console(1, this, "Initial Codec Feedback Registration Successful.");
+ CheckSyncStatus();
+ }
+
+ public void CodecDisconnected()
+ {
+ LoginMessageWasReceived = false;
+ InitialConfigurationMessageWasReceived = false;
+ InitialStatusMessageWasReceived = false;
+ FeedbackWasRegistered = false;
+ InitialSyncComplete = false;
+ }
+
+ void CheckSyncStatus()
+ {
+ if (LoginMessageWasReceived && InitialConfigurationMessageWasReceived && InitialStatusMessageWasReceived && FeedbackWasRegistered)
+ {
+ InitialSyncComplete = true;
+ Debug.Console(1, this, "Initial Codec Sync Complete!");
+ }
+ else
+ InitialSyncComplete = false;
+ }
+ }
+
+ public class CiscoSparkCodecFactory : EssentialsDeviceFactory
+ {
+ public CiscoSparkCodecFactory()
+ {
+ TypeNames = new List() { "ciscospark", "ciscowebex", "ciscowebexpro", "ciscoroomkit", "ciscosparkpluscodec" };
+ }
+
+ public override EssentialsDevice BuildDevice(DeviceConfig dc)
+ {
+ Debug.Console(1, "Factory Attempting to create new Cisco Codec Device");
+
+ var comm = CommFactory.CreateCommForDevice(dc);
+ return new VideoCodec.Cisco.CiscoSparkCodec(dc, comm);
+ }
+ }
}
\ No newline at end of file
diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/xStatus.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/xStatus.cs
index c1cd92cf..29b9f260 100644
--- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/xStatus.cs
+++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/xStatus.cs
@@ -440,9 +440,26 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
public CallId CallId { get; set; }
}
- public class DoNotDisturb
+ public class DoNotDisturb : ValueProperty
{
- public string Value { get; set; }
+ string _Value;
+
+ public bool BoolValue { get; private set; }
+
+ public string Value
+ {
+ get
+ {
+ return _Value;
+ }
+ set
+ {
+ _Value = value;
+ // If the incoming value is "On" it sets the BoolValue true, otherwise sets it false
+ BoolValue = value == "On" || value == "Active";
+ OnValueChanged();
+ }
+ }
}
public class Mode
@@ -600,6 +617,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
public Conference2()
{
Presentation = new Presentation();
+ DoNotDisturb = new DoNotDisturb();
}
}
@@ -1380,12 +1398,16 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
public class State : ValueProperty
{
+ string _value;
+
public bool BoolValue { get; private set; }
public string Value // Valid values are Standby/EnteringStandby/Halfwake/Off
{
+ get { return _value; }
set
{
+ _value = value;
// If the incoming value is "On" it sets the BoolValue true, otherwise sets it false
BoolValue = value == "On" || value == "Standby";
OnValueChanged();
@@ -2091,6 +2113,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
Conference = new Conference2();
SystemUnit = new SystemUnit();
Video = new Video();
+ Conference = new Conference2();
}
}
diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasStandbyMode.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasStandbyMode.cs
new file mode 100644
index 00000000..cc9dcd3d
--- /dev/null
+++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasStandbyMode.cs
@@ -0,0 +1,34 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Crestron.SimplSharp;
+
+using PepperDash.Essentials.Core;
+
+namespace PepperDash.Essentials.Devices.Common.VideoCodec
+{
+ ///
+ /// Describes a device that has Standby Mode capability
+ ///
+ public interface IHasStandbyMode
+ {
+ BoolFeedback StandbyIsOnFeedback { get; }
+
+ void StandbyActivate();
+
+ void StandbyDeactivate();
+ }
+
+ ///
+ /// Describes a device that has Half Waek Mode capability
+ ///
+ public interface IHasHalfWakeMode : IHasStandbyMode
+ {
+ BoolFeedback HalfWakeModeIsOnFeedback { get; }
+
+ BoolFeedback EnteringStandbyModeFeedback { get; }
+
+ void HalfwakeActivate();
+ }
+}
\ No newline at end of file
diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/VideoCodecBase.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/VideoCodecBase.cs
index 7db58b1e..cd70119f 100644
--- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/VideoCodecBase.cs
+++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/VideoCodecBase.cs
@@ -25,7 +25,7 @@ using Feedback = PepperDash.Essentials.Core.Feedback;
namespace PepperDash.Essentials.Devices.Common.VideoCodec
{
public abstract class VideoCodecBase : ReconfigurableDevice, IRoutingInputsOutputs,
- IUsageTracking, IHasDialer, IHasContentSharing, ICodecAudio, iVideoCodecInfo, IBridgeAdvanced
+ IUsageTracking, IHasDialer, IHasContentSharing, ICodecAudio, iVideoCodecInfo, IBridgeAdvanced, IHasStandbyMode
{
private const int XSigEncoding = 28591;
protected const int MaxParticipants = 50;