diff --git a/PepperDashEssentials/ControlSystem.cs b/PepperDashEssentials/ControlSystem.cs
index 0717bd80..9afa210d 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,21 @@ 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)
+ {
+ _initializeEvent = new CEvent();
+
+ DeviceManager.AllDevicesActivated += (o, a) =>
+ {
+ _initializeEvent.Set();
+ };
+
+ _initializeEvent.Wait(30000);
+ }
}
private void StartSystem(object obj)
@@ -343,7 +359,7 @@ namespace PepperDash.Essentials
{
var prompt = Global.ControlSystem.ControllerPrompt;
- var typeMatch = String.Equals(devConf.Type, prompt, StringComparison.OrdinalIgnoreCase) &&
+ var typeMatch = String.Equals(devConf.Type, prompt, StringComparison.OrdinalIgnoreCase) ||
String.Equals(devConf.Type, prompt.Replace("-", ""), StringComparison.OrdinalIgnoreCase);
if (!typeMatch)
@@ -361,9 +377,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)
{
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/Global/Global.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Global/Global.cs
index 121bc9b7..0365b209 100644
--- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Global/Global.cs
+++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Global/Global.cs
@@ -32,6 +32,16 @@ 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;
+ }
+ }
///
/// The file path prefix to the folder containing configuration files
diff --git a/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmpsRoutingController.cs b/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmpsRoutingController.cs
index 0b08500d..f6f6be22 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..3ff14dba 100644
--- a/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Transmitters/DmTxHelpers.cs
+++ b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Transmitters/DmTxHelpers.cs
@@ -65,99 +65,136 @@ 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 noIpId = 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);
+ noIpId = 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);
+ noIpId = dmpsDev.Dmps4kType;
+
+ 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 (noIpId)
+ {
+ 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;
}
}
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
index 8caeaf3e..e4945ce8 100644
--- 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
@@ -93,6 +93,20 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
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
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 d625637c..2d3645f0 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,2289 +1,2294 @@
-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, IHasDoNotDisturbMode,
- IHasHalfWakeMode
- {
- 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;
-
- DoNotDisturbModeIsOnFeedback = new BoolFeedback(() => CodecStatus.Status.Conference.DoNotDisturb.BoolValue);
- HalfWakeModeIsOnFeedback = new BoolFeedback(() => CodecStatus.Status.Standby.State.Value == "Halfwake");
-
- 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 = new Action(() =>
- {
- StandbyIsOnFeedback.FireUpdate();
- HalfWakeModeIsOnFeedback.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;
- CodecStatus.Status.Conference.Presentation.Mode.ValueChangedAction = FarEndIsSharingContentFeedback.FireUpdate;
- CodecStatus.Status.Conference.DoNotDisturb.ValueChangedAction = DoNotDisturbModeIsOnFeedback.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/Conference/DoNotDisturb" + 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/Bookings" + Delimiter +
- prefix + "/Event/CameraPresetListUpdated" + Delimiter +
- prefix + "/Event/UserInterface/Presentation/ExternalSource/Selected/SourceIdentifier" + Delimiter +
- prefix + "/Event/CallDisconnect" + Delimiter; // Keep CallDisconnect last to detect when feedback registration completes correctly
-
- }
-
- #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)
- {
- var joinMap = new CiscoCodecJoinMap(joinStart);
-
- var customJoins = JoinMapHelper.TryGetJoinMapAdvancedForDevice(joinMapKey);
-
- if (customJoins != null)
- {
- joinMap.SetCustomJoinData(customJoins);
- }
-
- if (bridge != null)
- {
- bridge.AddJoinMap(Key, joinMap);
- }
-
- LinkVideoCodecToApi(this, trilist, joinStart, joinMapKey, bridge);
-
- LinkCiscoCodecToApi(trilist, joinMap);
- }
-
- public void LinkCiscoCodecToApi(BasicTriList trilist, CiscoCodecJoinMap joinMap)
- {
- var dndCodec = this as IHasDoNotDisturbMode;
- if (dndCodec != null)
- {
- dndCodec.DoNotDisturbModeIsOnFeedback.LinkInputSig(trilist.BooleanInput[joinMap.ActivateDoNotDisturbMode.JoinNumber]);
- dndCodec.DoNotDisturbModeIsOnFeedback.LinkComplementInputSig(trilist.BooleanInput[joinMap.DeactivateDoNotDisturbMode.JoinNumber]);
-
- trilist.SetSigFalseAction(joinMap.ActivateDoNotDisturbMode.JoinNumber, () => dndCodec.ActivateDoNotDisturbMode());
- trilist.SetSigFalseAction(joinMap.DeactivateDoNotDisturbMode.JoinNumber, () => dndCodec.DeactivateDoNotDisturbMode());
- trilist.SetSigFalseAction(joinMap.ToggleDoNotDisturbMode.JoinNumber, () => dndCodec.ToggleDoNotDisturbMode());
- }
-
- var halfwakeCodec = this as IHasHalfWakeMode;
- if (halfwakeCodec != null)
- {
- halfwakeCodec.StandbyIsOnFeedback.LinkInputSig(trilist.BooleanInput[joinMap.ActivateStandby.JoinNumber]);
- halfwakeCodec.StandbyIsOnFeedback.LinkComplementInputSig(trilist.BooleanInput[joinMap.DeactivateStandby.JoinNumber]);
- halfwakeCodec.HalfWakeModeIsOnFeedback.LinkInputSig(trilist.BooleanInput[joinMap.ActivateHalfWakeMode.JoinNumber]);
-
- trilist.SetSigFalseAction(joinMap.ActivateStandby.JoinNumber, () => halfwakeCodec.StandbyActivate());
- trilist.SetSigFalseAction(joinMap.DeactivateStandby.JoinNumber, () => halfwakeCodec.StandbyDeactivate());
- trilist.SetSigFalseAction(joinMap.ActivateHalfWakeMode.JoinNumber, () => halfwakeCodec.HalfwakeActivate());
- }
- }
-
- ///
- /// 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();
- }
-
- #region IHasDoNotDisturbMode Members
-
- public BoolFeedback DoNotDisturbModeIsOnFeedback { get; private set; }
-
- public void ActivateDoNotDisturbMode()
- {
- SendText("xCommand Conference DoNotDisturb Activate");
- }
-
- public void DeactivateDoNotDisturbMode()
- {
- SendText("xCommand Conference DoNotDisturb Deactivate");
- }
-
- public void ToggleDoNotDisturbMode()
- {
- if (DoNotDisturbModeIsOnFeedback.BoolValue)
- {
- DeactivateDoNotDisturbMode();
- }
- else
- {
- ActivateDoNotDisturbMode();
- }
- }
-
- #endregion
-
- #region IHasHalfWakeMode Members
-
- public BoolFeedback HalfWakeModeIsOnFeedback { get; private set; }
-
- public void HalfwakeActivate()
- {
- SendText("xCommand Standby Halfwake");
- }
-
- #endregion
- }
-
-
- ///
- /// 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, IHasDoNotDisturbMode,
+ IHasHalfWakeMode
+ {
+ 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;
+
+ DoNotDisturbModeIsOnFeedback = new BoolFeedback(() => CodecStatus.Status.Conference.DoNotDisturb.BoolValue);
+ HalfWakeModeIsOnFeedback = new BoolFeedback(() => CodecStatus.Status.Standby.State.Value.ToLower() == "halfwake");
+ EnteringStandbyModeFeedback = new BoolFeedback(() => CodecStatus.Status.Standby.State.Value.ToLower() == "enteringstandby");
+
+ 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 = new Action(() =>
+ {
+ StandbyIsOnFeedback.FireUpdate();
+ HalfWakeModeIsOnFeedback.FireUpdate();
+ EnteringStandbyModeFeedback.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;
+ CodecStatus.Status.Conference.Presentation.Mode.ValueChangedAction = FarEndIsSharingContentFeedback.FireUpdate;
+ CodecStatus.Status.Conference.DoNotDisturb.ValueChangedAction = DoNotDisturbModeIsOnFeedback.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/Conference/DoNotDisturb" + 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/Bookings" + Delimiter +
+ prefix + "/Event/CameraPresetListUpdated" + Delimiter +
+ prefix + "/Event/UserInterface/Presentation/ExternalSource/Selected/SourceIdentifier" + Delimiter +
+ prefix + "/Event/CallDisconnect" + Delimiter; // Keep CallDisconnect last to detect when feedback registration completes correctly
+
+ }
+
+ #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)
+ {
+ var joinMap = new CiscoCodecJoinMap(joinStart);
+
+ var customJoins = JoinMapHelper.TryGetJoinMapAdvancedForDevice(joinMapKey);
+
+ if (customJoins != null)
+ {
+ joinMap.SetCustomJoinData(customJoins);
+ }
+
+ if (bridge != null)
+ {
+ bridge.AddJoinMap(Key, joinMap);
+ }
+
+ LinkVideoCodecToApi(this, trilist, joinStart, joinMapKey, bridge);
+
+ LinkCiscoCodecToApi(trilist, joinMap);
+ }
+
+ public void LinkCiscoCodecToApi(BasicTriList trilist, CiscoCodecJoinMap joinMap)
+ {
+ var dndCodec = this as IHasDoNotDisturbMode;
+ if (dndCodec != null)
+ {
+ dndCodec.DoNotDisturbModeIsOnFeedback.LinkInputSig(trilist.BooleanInput[joinMap.ActivateDoNotDisturbMode.JoinNumber]);
+ dndCodec.DoNotDisturbModeIsOnFeedback.LinkComplementInputSig(trilist.BooleanInput[joinMap.DeactivateDoNotDisturbMode.JoinNumber]);
+
+ trilist.SetSigFalseAction(joinMap.ActivateDoNotDisturbMode.JoinNumber, () => dndCodec.ActivateDoNotDisturbMode());
+ trilist.SetSigFalseAction(joinMap.DeactivateDoNotDisturbMode.JoinNumber, () => dndCodec.DeactivateDoNotDisturbMode());
+ trilist.SetSigFalseAction(joinMap.ToggleDoNotDisturbMode.JoinNumber, () => dndCodec.ToggleDoNotDisturbMode());
+ }
+
+ var halfwakeCodec = this as IHasHalfWakeMode;
+ if (halfwakeCodec != null)
+ {
+ halfwakeCodec.StandbyIsOnFeedback.LinkInputSig(trilist.BooleanInput[joinMap.ActivateStandby.JoinNumber]);
+ halfwakeCodec.StandbyIsOnFeedback.LinkComplementInputSig(trilist.BooleanInput[joinMap.DeactivateStandby.JoinNumber]);
+ halfwakeCodec.HalfWakeModeIsOnFeedback.LinkInputSig(trilist.BooleanInput[joinMap.ActivateHalfWakeMode.JoinNumber]);
+ halfwakeCodec.EnteringStandbyModeFeedback.LinkInputSig(trilist.BooleanInput[joinMap.EnteringStandbyMode.JoinNumber]);
+
+ trilist.SetSigFalseAction(joinMap.ActivateStandby.JoinNumber, () => halfwakeCodec.StandbyActivate());
+ trilist.SetSigFalseAction(joinMap.DeactivateStandby.JoinNumber, () => halfwakeCodec.StandbyDeactivate());
+ trilist.SetSigFalseAction(joinMap.ActivateHalfWakeMode.JoinNumber, () => halfwakeCodec.HalfwakeActivate());
+ }
+ }
+
+ ///
+ /// 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();
+ }
+
+ #region IHasDoNotDisturbMode Members
+
+ public BoolFeedback DoNotDisturbModeIsOnFeedback { get; private set; }
+
+ public void ActivateDoNotDisturbMode()
+ {
+ SendText("xCommand Conference DoNotDisturb Activate");
+ }
+
+ public void DeactivateDoNotDisturbMode()
+ {
+ SendText("xCommand Conference DoNotDisturb Deactivate");
+ }
+
+ public void ToggleDoNotDisturbMode()
+ {
+ if (DoNotDisturbModeIsOnFeedback.BoolValue)
+ {
+ DeactivateDoNotDisturbMode();
+ }
+ else
+ {
+ ActivateDoNotDisturbMode();
+ }
+ }
+
+ #endregion
+
+ #region IHasHalfWakeMode Members
+
+ public BoolFeedback HalfWakeModeIsOnFeedback { get; private set; }
+
+ public BoolFeedback EnteringStandbyModeFeedback { get; private set; }
+
+ public void HalfwakeActivate()
+ {
+ SendText("xCommand Standby Halfwake");
+ }
+
+ #endregion
+ }
+
+
+ ///
+ /// 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/Interfaces/IHasStandbyMode.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasStandbyMode.cs
index 55dcd081..cc9dcd3d 100644
--- 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
@@ -27,6 +27,8 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec
{
BoolFeedback HalfWakeModeIsOnFeedback { get; }
+ BoolFeedback EnteringStandbyModeFeedback { get; }
+
void HalfwakeActivate();
}
}
\ No newline at end of file
diff --git a/packages.config b/packages.config
index 3494f2eb..10124bbd 100644
--- a/packages.config
+++ b/packages.config
@@ -1,3 +1,3 @@
-
+
\ No newline at end of file