diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/VideoCodecControllerJoinMap.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/VideoCodecControllerJoinMap.cs
index 21b88ed3..07c5c4bc 100644
--- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/VideoCodecControllerJoinMap.cs
+++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/VideoCodecControllerJoinMap.cs
@@ -20,7 +20,21 @@ namespace PepperDash.Essentials.Core.Bridges.JoinMaps
JoinType = eJoinType.Digital
});
- [JoinName("1")]
+ [JoinName("SendDtmfToSpecificCallIndex")]
+ public JoinDataComplete SendDtmfToSpecificCallIndex = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 10,
+ JoinSpan = 1
+ },
+ new JoinMetadata
+ {
+ Description = "If High, will send DTMF tones to the call set by SelectCall analog. If low sends DTMF tones to last connected call.",
+ JoinCapabilities = eJoinCapabilities.FromSIMPL,
+ JoinType = eJoinType.Digital
+ });
+
+ [JoinName("Dtmf1")]
public JoinDataComplete Dtmf1 = new JoinDataComplete(
new JoinData
{
@@ -34,7 +48,7 @@ namespace PepperDash.Essentials.Core.Bridges.JoinMaps
JoinType = eJoinType.Digital
});
- [JoinName("2")]
+ [JoinName("Dtmf2")]
public JoinDataComplete Dtmf2 = new JoinDataComplete(
new JoinData
{
@@ -48,7 +62,7 @@ namespace PepperDash.Essentials.Core.Bridges.JoinMaps
JoinType = eJoinType.Digital
});
- [JoinName("3")]
+ [JoinName("Dtmf3")]
public JoinDataComplete Dtmf3 = new JoinDataComplete(
new JoinData
{
@@ -62,7 +76,7 @@ namespace PepperDash.Essentials.Core.Bridges.JoinMaps
JoinType = eJoinType.Digital
});
- [JoinName("4")]
+ [JoinName("Dtmf4")]
public JoinDataComplete Dtmf4 = new JoinDataComplete(
new JoinData
{
@@ -76,7 +90,7 @@ namespace PepperDash.Essentials.Core.Bridges.JoinMaps
JoinType = eJoinType.Digital
});
- [JoinName("5")]
+ [JoinName("Dtmf5")]
public JoinDataComplete Dtmf5 = new JoinDataComplete(
new JoinData
{
@@ -90,7 +104,7 @@ namespace PepperDash.Essentials.Core.Bridges.JoinMaps
JoinType = eJoinType.Digital
});
- [JoinName("6")]
+ [JoinName("Dtmf6")]
public JoinDataComplete Dtmf6 = new JoinDataComplete(
new JoinData
{
@@ -104,7 +118,7 @@ namespace PepperDash.Essentials.Core.Bridges.JoinMaps
JoinType = eJoinType.Digital
});
- [JoinName("7")]
+ [JoinName("Dtmf7")]
public JoinDataComplete Dtmf7 = new JoinDataComplete(
new JoinData
{
@@ -118,7 +132,7 @@ namespace PepperDash.Essentials.Core.Bridges.JoinMaps
JoinType = eJoinType.Digital
});
- [JoinName("8")]
+ [JoinName("Dtmf8")]
public JoinDataComplete Dtmf8 = new JoinDataComplete(
new JoinData
{
@@ -132,7 +146,7 @@ namespace PepperDash.Essentials.Core.Bridges.JoinMaps
JoinType = eJoinType.Digital
});
- [JoinName("9")]
+ [JoinName("Dtmf9")]
public JoinDataComplete Dtmf9 = new JoinDataComplete(
new JoinData
{
@@ -146,7 +160,7 @@ namespace PepperDash.Essentials.Core.Bridges.JoinMaps
JoinType = eJoinType.Digital
});
- [JoinName("0")]
+ [JoinName("Dtmf0")]
public JoinDataComplete Dtmf0 = new JoinDataComplete(
new JoinData
{
@@ -160,7 +174,7 @@ namespace PepperDash.Essentials.Core.Bridges.JoinMaps
JoinType = eJoinType.Digital
});
- [JoinName("*")]
+ [JoinName("DtmfStar")]
public JoinDataComplete DtmfStar = new JoinDataComplete(
new JoinData
{
@@ -174,7 +188,7 @@ namespace PepperDash.Essentials.Core.Bridges.JoinMaps
JoinType = eJoinType.Digital
});
- [JoinName("#")]
+ [JoinName("DtmfPound")]
public JoinDataComplete DtmfPound = new JoinDataComplete(
new JoinData
{
@@ -188,8 +202,8 @@ namespace PepperDash.Essentials.Core.Bridges.JoinMaps
JoinType = eJoinType.Digital
});
- [JoinName("EndCall")]
- public JoinDataComplete EndCall = new JoinDataComplete(
+ [JoinName("EndAllCalls")]
+ public JoinDataComplete EndAllCalls = new JoinDataComplete(
new JoinData
{
JoinNumber = 24,
@@ -197,7 +211,7 @@ namespace PepperDash.Essentials.Core.Bridges.JoinMaps
},
new JoinMetadata
{
- Description = "Hang Up",
+ Description = "End All Calls",
JoinCapabilities = eJoinCapabilities.FromSIMPL,
JoinType = eJoinType.Digital
});
@@ -226,7 +240,7 @@ namespace PepperDash.Essentials.Core.Bridges.JoinMaps
new JoinMetadata
{
Description = "Speed Dial",
- JoinCapabilities = eJoinCapabilities.ToSIMPL,
+ JoinCapabilities = eJoinCapabilities.FromSIMPL,
JoinType = eJoinType.Digital
});
@@ -281,12 +295,12 @@ namespace PepperDash.Essentials.Core.Bridges.JoinMaps
},
new JoinMetadata
{
- Description = "Dial manual string",
+ Description = "Dial manual string specified by CurrentDialString serial join",
JoinCapabilities = eJoinCapabilities.FromSIMPL,
JoinType = eJoinType.Digital
});
- [JoinName("DialPhoneCall")]
+ [JoinName("DialPhone")]
public JoinDataComplete DialPhone = new JoinDataComplete(
new JoinData
{
@@ -314,7 +328,7 @@ namespace PepperDash.Essentials.Core.Bridges.JoinMaps
JoinType = eJoinType.Digital
});
- [JoinName("EndPhoneCall")]
+ [JoinName("HangUpPhone")]
public JoinDataComplete HangUpPhone = new JoinDataComplete(
new JoinData
{
@@ -323,11 +337,53 @@ namespace PepperDash.Essentials.Core.Bridges.JoinMaps
},
new JoinMetadata
{
- Description = "Hang Up PHone",
+ Description = "Hang Up Phone",
JoinCapabilities = eJoinCapabilities.FromSIMPL,
JoinType = eJoinType.Digital
});
+ [JoinName("EndCallStart")]
+ public JoinDataComplete EndCallStart = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 81,
+ JoinSpan = 8
+ },
+ new JoinMetadata
+ {
+ Description = "End a specific call by call index. ",
+ JoinCapabilities = eJoinCapabilities.FromSIMPL,
+ JoinType = eJoinType.Digital
+ });
+
+ [JoinName("JoinAllCalls")]
+ public JoinDataComplete JoinAllCalls = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 90,
+ JoinSpan = 1
+ },
+ new JoinMetadata
+ {
+ Description = "Join all calls",
+ JoinCapabilities = eJoinCapabilities.FromSIMPL,
+ JoinType = eJoinType.Digital
+ });
+
+ [JoinName("JoinCallStart")]
+ public JoinDataComplete JoinCallStart = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 91,
+ JoinSpan = 8
+ },
+ new JoinMetadata
+ {
+ Description = "Join a specific call by call index. ",
+ JoinCapabilities = eJoinCapabilities.FromSIMPL,
+ JoinType = eJoinType.Digital
+ });
+
[JoinName("DirectorySearchBusy")]
public JoinDataComplete DirectorySearchBusy = new JoinDataComplete(
new JoinData
@@ -408,7 +464,7 @@ namespace PepperDash.Essentials.Core.Bridges.JoinMaps
new JoinMetadata
{
Description = "Go to Directory Root",
- JoinCapabilities = eJoinCapabilities.ToSIMPL,
+ JoinCapabilities = eJoinCapabilities.FromSIMPL,
JoinType = eJoinType.Digital
});
@@ -440,6 +496,33 @@ namespace PepperDash.Essentials.Core.Bridges.JoinMaps
JoinType = eJoinType.Digital
});
+ [JoinName("DirectoryDisableAutoDialSelectedLine")]
+ public JoinDataComplete DirectoryDisableAutoDialSelectedLine = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 107,
+ JoinSpan = 1
+ },
+ new JoinMetadata
+ {
+ Description = "Set high to disable automatic dialing of a contact when selected",
+ JoinCapabilities = eJoinCapabilities.FromSIMPL,
+ JoinType = eJoinType.Digital
+ });
+
+ [JoinName("DirectoryDialSelectedContactMethod")]
+ public JoinDataComplete DirectoryDialSelectedContactMethod = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 108,
+ JoinSpan = 1
+ },
+ new JoinMetadata
+ {
+ Description = "Pulse to dial the selected contact method",
+ JoinCapabilities = eJoinCapabilities.FromSIMPL,
+ JoinType = eJoinType.Digital
+ });
[JoinName("CameraTiltUp")]
public JoinDataComplete CameraTiltUp = new JoinDataComplete(
@@ -525,6 +608,48 @@ namespace PepperDash.Essentials.Core.Bridges.JoinMaps
JoinType = eJoinType.Digital
});
+ [JoinName("CameraFocusNear")]
+ public JoinDataComplete CameraFocusNear = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 117,
+ JoinSpan = 1
+ },
+ new JoinMetadata
+ {
+ Description = "Camera Focus Near",
+ JoinCapabilities = eJoinCapabilities.FromSIMPL,
+ JoinType = eJoinType.Digital
+ });
+
+ [JoinName("CameraFocusFar")]
+ public JoinDataComplete CameraFocusFar = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 118,
+ JoinSpan = 1
+ },
+ new JoinMetadata
+ {
+ Description = "Camera Focus Far",
+ JoinCapabilities = eJoinCapabilities.FromSIMPL,
+ JoinType = eJoinType.Digital
+ });
+
+ [JoinName("CameraFocusAuto")]
+ public JoinDataComplete CameraFocusAuto = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 119,
+ JoinSpan = 1
+ },
+ new JoinMetadata
+ {
+ Description = "Camera Auto Focus Trigger",
+ JoinCapabilities = eJoinCapabilities.FromSIMPL,
+ JoinType = eJoinType.Digital
+ });
+
[JoinName("CameraPresetSave")]
public JoinDataComplete CameraPresetSave = new JoinDataComplete(
new JoinData
@@ -534,7 +659,7 @@ namespace PepperDash.Essentials.Core.Bridges.JoinMaps
},
new JoinMetadata
{
- Description = "Save Selected Preset",
+ Description = "Pulse to save selected preset spcified by CameraPresetSelect analog join. FB will pulse for 3s when preset saved.",
JoinCapabilities = eJoinCapabilities.ToFromSIMPL,
JoinType = eJoinType.Digital
});
@@ -548,7 +673,7 @@ namespace PepperDash.Essentials.Core.Bridges.JoinMaps
},
new JoinMetadata
{
- Description = "Camera Mode Auto",
+ Description = "Camera Mode Auto. Enables camera auto tracking mode, with feedback",
JoinCapabilities = eJoinCapabilities.ToFromSIMPL,
JoinType = eJoinType.Digital
});
@@ -562,7 +687,7 @@ namespace PepperDash.Essentials.Core.Bridges.JoinMaps
},
new JoinMetadata
{
- Description = "Camera Mode Manual",
+ Description = "Camera Mode Manual. Disables camera auto tracking mode, with feedback",
JoinCapabilities = eJoinCapabilities.ToFromSIMPL,
JoinType = eJoinType.Digital
});
@@ -576,7 +701,7 @@ namespace PepperDash.Essentials.Core.Bridges.JoinMaps
},
new JoinMetadata
{
- Description = "Camera Mode Off",
+ Description = "Camera Mode Off. Disables camera video, with feedback. Works like video mute.",
JoinCapabilities = eJoinCapabilities.ToFromSIMPL,
JoinType = eJoinType.Digital
});
@@ -805,6 +930,34 @@ namespace PepperDash.Essentials.Core.Bridges.JoinMaps
JoinType = eJoinType.Digital
});
+ [JoinName("RemoveSelectedRecentCallItem")]
+ public JoinDataComplete RemoveSelectedRecentCallItem = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 181,
+ JoinSpan = 1
+ },
+ new JoinMetadata
+ {
+ Description = "Pulse to remove the selected recent call item specified by the SelectRecentCallItem analog join",
+ JoinCapabilities = eJoinCapabilities.FromSIMPL,
+ JoinType = eJoinType.Digital
+ });
+
+ [JoinName("DialSelectedRecentCallItem")]
+ public JoinDataComplete DialSelectedRecentCallItem = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 182,
+ JoinSpan = 1
+ },
+ new JoinMetadata
+ {
+ Description = "Pulse to dial the selected recent call item specified by the SelectRecentCallItem analog join",
+ JoinCapabilities = eJoinCapabilities.FromSIMPL,
+ JoinType = eJoinType.Digital
+ });
+
[JoinName("SourceShareStart")]
public JoinDataComplete SourceShareStart = new JoinDataComplete(
new JoinData
@@ -870,11 +1023,81 @@ namespace PepperDash.Essentials.Core.Bridges.JoinMaps
},
new JoinMetadata
{
- Description = "advance selfview position",
+ Description = "Toggles selfview position",
JoinCapabilities = eJoinCapabilities.FromSIMPL,
JoinType = eJoinType.Digital
});
+ [JoinName("HoldAllCalls")]
+ public JoinDataComplete HoldAllCalls = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 220,
+ JoinSpan = 1
+ },
+ new JoinMetadata
+ {
+ Description = "Holds all calls",
+ JoinCapabilities = eJoinCapabilities.FromSIMPL,
+ JoinType = eJoinType.Digital
+ });
+
+ [JoinName("HoldCallsStart")]
+ public JoinDataComplete HoldCallsStart = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 221,
+ JoinSpan = 8
+ },
+ new JoinMetadata
+ {
+ Description = "Holds Call at specified index. FB reported on Call Status XSIG",
+ JoinCapabilities = eJoinCapabilities.FromSIMPL,
+ JoinType = eJoinType.Digital
+ });
+
+ [JoinName("ResumeCallsStart")]
+ public JoinDataComplete ResumeCallsStart = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 231,
+ JoinSpan = 8
+ },
+ new JoinMetadata
+ {
+ Description = "Resume Call at specified index",
+ JoinCapabilities = eJoinCapabilities.FromSIMPL,
+ JoinType = eJoinType.Digital
+ });
+
+ [JoinName("MultiSiteOptionIsEnabled")]
+ public JoinDataComplete MultiSiteOptionIsEnabled = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 301,
+ JoinSpan = 1
+ },
+ new JoinMetadata
+ {
+ Description = "Multi site option is enabled FB",
+ JoinCapabilities = eJoinCapabilities.ToSIMPL,
+ JoinType = eJoinType.Digital
+ });
+
+ [JoinName("AutoAnswerEnabled")]
+ public JoinDataComplete AutoAnswerEnabled = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 302,
+ JoinSpan = 1
+ },
+ new JoinMetadata
+ {
+ Description = "Auto Answer is enabled FB",
+ JoinCapabilities = eJoinCapabilities.ToSIMPL,
+ JoinType = eJoinType.Digital
+ });
+
[JoinName("ParticipantAudioMuteToggleStart")]
public JoinDataComplete ParticipantAudioMuteToggleStart = new JoinDataComplete(
new JoinData
@@ -939,6 +1162,35 @@ namespace PepperDash.Essentials.Core.Bridges.JoinMaps
JoinType = eJoinType.Analog
});
+ [JoinName("SelectCall")]
+ public JoinDataComplete SelectCall = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 24,
+ JoinSpan = 1
+ },
+ new JoinMetadata
+ {
+ Description = "Sets the selected Call for DTMF commands. Valid values 1-8",
+ JoinCapabilities = eJoinCapabilities.FromSIMPL,
+ JoinType = eJoinType.Analog
+ });
+
+
+ [JoinName("ConnectedCallCount")]
+ public JoinDataComplete ConnectedCallCount = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 25,
+ JoinSpan = 1
+ },
+ new JoinMetadata
+ {
+ Description = "Reports the number of currently connected calls",
+ JoinCapabilities = eJoinCapabilities.ToSIMPL,
+ JoinType = eJoinType.Analog
+ });
+
[JoinName("MinutesBeforeMeetingStart")]
public JoinDataComplete MinutesBeforeMeetingStart = new JoinDataComplete(
new JoinData
@@ -962,11 +1214,25 @@ namespace PepperDash.Essentials.Core.Bridges.JoinMaps
},
new JoinMetadata
{
- Description = "Camera Number Select/FB",
+ Description = "Camera Number Select/FB. 1 based index. Valid range is 1 to the value reported by CameraCount.",
JoinCapabilities = eJoinCapabilities.ToFromSIMPL,
JoinType = eJoinType.Analog
});
+ [JoinName("CameraCount")]
+ public JoinDataComplete CameraCount = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 61,
+ JoinSpan = 1
+ },
+ new JoinMetadata
+ {
+ Description = "Reports the number of cameras",
+ JoinCapabilities = eJoinCapabilities.ToSIMPL,
+ JoinType = eJoinType.Analog
+ });
+
[JoinName("DirectoryRowCount")]
public JoinDataComplete DirectoryRowCount = new JoinDataComplete(
new JoinData
@@ -995,6 +1261,34 @@ namespace PepperDash.Essentials.Core.Bridges.JoinMaps
JoinType = eJoinType.Analog
});
+ [JoinName("SelectedContactMethodCount")]
+ public JoinDataComplete SelectedContactMethodCount = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 102,
+ JoinSpan = 1
+ },
+ new JoinMetadata
+ {
+ Description = "Reports the number of contact methods for the selected contact",
+ JoinCapabilities = eJoinCapabilities.FromSIMPL,
+ JoinType = eJoinType.Analog
+ });
+
+ [JoinName("SelectContactMethod")]
+ public JoinDataComplete SelectContactMethod = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 103,
+ JoinSpan = 1
+ },
+ new JoinMetadata
+ {
+ Description = "Selects a contact method by index",
+ JoinCapabilities = eJoinCapabilities.FromSIMPL,
+ JoinType = eJoinType.Analog
+ });
+
[JoinName("CameraPresetSelect")]
public JoinDataComplete CameraPresetSelect = new JoinDataComplete(
new JoinData
@@ -1005,10 +1299,24 @@ namespace PepperDash.Essentials.Core.Bridges.JoinMaps
new JoinMetadata
{
Description = "Camera Preset Select",
- JoinCapabilities = eJoinCapabilities.ToFromSIMPL,
+ JoinCapabilities = eJoinCapabilities.ToSIMPL,
JoinType = eJoinType.Analog
});
+ [JoinName("FarEndPresetSelect")]
+ public JoinDataComplete FarEndPresetSelect = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 122,
+ JoinSpan = 1
+ },
+ new JoinMetadata
+ {
+ Description = "Far End Preset Preset Select",
+ JoinCapabilities = eJoinCapabilities.ToSIMPL,
+ JoinType = eJoinType.Analog
+ });
+
[JoinName("ParticipantCount")]
public JoinDataComplete ParticipantCount = new JoinDataComplete(
new JoinData
@@ -1051,6 +1359,48 @@ namespace PepperDash.Essentials.Core.Bridges.JoinMaps
JoinType = eJoinType.Analog
});
+ [JoinName("SelectRecentCallItem")]
+ public JoinDataComplete SelectRecentCallItem = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 180,
+ JoinSpan = 1
+ },
+ new JoinMetadata
+ {
+ Description = "Select/FB for Recent Call Item. Valid values 1 - 10",
+ JoinCapabilities = eJoinCapabilities.ToFromSIMPL,
+ JoinType = eJoinType.Analog
+ });
+
+ [JoinName("RecentCallOccurrenceType")]
+ public JoinDataComplete RecentCallOccurrenceType = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 181,
+ JoinSpan = 10
+ },
+ new JoinMetadata
+ {
+ Description = "Recent Call Occurrence Type. [0-3] 0 = Unknown, 1 = Placed, 2 = Received, 3 = NoAnswer",
+ JoinCapabilities = eJoinCapabilities.ToSIMPL,
+ JoinType = eJoinType.Analog
+ });
+
+ [JoinName("RecentCallCount")]
+ public JoinDataComplete RecentCallCount = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 191,
+ JoinSpan = 1
+ },
+ new JoinMetadata
+ {
+ Description = "Recent Call Count",
+ JoinCapabilities = eJoinCapabilities.ToSIMPL,
+ JoinType = eJoinType.Analog
+ });
+
#endregion
@@ -1066,12 +1416,12 @@ namespace PepperDash.Essentials.Core.Bridges.JoinMaps
},
new JoinMetadata
{
- Description = "Current Dial String",
- JoinCapabilities = eJoinCapabilities.ToFromSIMPL,
+ Description = "Value to dial when ManualDial digital join is pulsed",
+ JoinCapabilities = eJoinCapabilities.ToSIMPL,
JoinType = eJoinType.Serial
});
- [JoinName("PhoneString")]
+ [JoinName("PhoneDialString")]
public JoinDataComplete PhoneDialString = new JoinDataComplete(
new JoinData
{
@@ -1085,7 +1435,7 @@ namespace PepperDash.Essentials.Core.Bridges.JoinMaps
JoinType = eJoinType.Serial
});
- [JoinName("CurrentCallName")]
+ [JoinName("CurrentCallData")]
public JoinDataComplete CurrentCallData = new JoinDataComplete(
new JoinData
{
@@ -1184,6 +1534,20 @@ namespace PepperDash.Essentials.Core.Bridges.JoinMaps
JoinType = eJoinType.Serial
});
+ [JoinName("ContactMethods")]
+ public JoinDataComplete ContactMethods = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 103,
+ JoinSpan = 1
+ },
+ new JoinMetadata
+ {
+ Description = "Contact Methods - XSig, 10 entries",
+ JoinCapabilities = eJoinCapabilities.ToSIMPL,
+ JoinType = eJoinType.Serial
+ });
+
[JoinName("CameraPresetNames")]
public JoinDataComplete CameraPresetNames = new JoinDataComplete(
new JoinData
@@ -1198,8 +1562,8 @@ namespace PepperDash.Essentials.Core.Bridges.JoinMaps
JoinType = eJoinType.Serial
});
- [JoinName("CameraLayoutStringFb")]
- public JoinDataComplete CameraLayoutStringFb = new JoinDataComplete(
+ [JoinName("CurrentLayoutStringFb")]
+ public JoinDataComplete CurrentLayoutStringFb = new JoinDataComplete(
new JoinData
{
JoinNumber = 141,
@@ -1226,6 +1590,76 @@ namespace PepperDash.Essentials.Core.Bridges.JoinMaps
JoinType = eJoinType.Serial
});
+ [JoinName("CameraNamesFb")]
+ public JoinDataComplete CameraNamesFb = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 161,
+ JoinSpan = 10
+ },
+ new JoinMetadata
+ {
+ Description = "Camera Name Fb",
+ JoinCapabilities = eJoinCapabilities.ToSIMPL,
+ JoinType = eJoinType.Serial
+ });
+
+ [JoinName("SelectedRecentCallName")]
+ public JoinDataComplete SelectedRecentCallName = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 171,
+ JoinSpan = 1
+ },
+ new JoinMetadata
+ {
+ Description = "Selected Recent Call Name",
+ JoinCapabilities = eJoinCapabilities.ToSIMPL,
+ JoinType = eJoinType.Serial
+ });
+
+ [JoinName("SelectedRecentCallNumber")]
+ public JoinDataComplete SelectedRecentCallNumber = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 172,
+ JoinSpan = 1
+ },
+ new JoinMetadata
+ {
+ Description = "Selected Recent Call Number",
+ JoinCapabilities = eJoinCapabilities.ToSIMPL,
+ JoinType = eJoinType.Serial
+ });
+
+ [JoinName("RecentCallNamesStart")]
+ public JoinDataComplete RecentCallNamesStart = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 181,
+ JoinSpan = 10
+ },
+ new JoinMetadata
+ {
+ Description = "Recent Call Names",
+ JoinCapabilities = eJoinCapabilities.ToSIMPL,
+ JoinType = eJoinType.Serial
+ });
+
+ [JoinName("RecentCallTimesStart")]
+ public JoinDataComplete RecentCallTimesStart = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 191,
+ JoinSpan = 10
+ },
+ new JoinMetadata
+ {
+ Description = "Recent Calls Times",
+ JoinCapabilities = eJoinCapabilities.ToSIMPL,
+ JoinType = eJoinType.Serial
+ });
+
[JoinName("CurrentSource")]
public JoinDataComplete CurrentSource = new JoinDataComplete(
new JoinData
@@ -1252,7 +1686,77 @@ namespace PepperDash.Essentials.Core.Bridges.JoinMaps
Description = "advance selfview position",
JoinCapabilities = eJoinCapabilities.ToSIMPL,
JoinType = eJoinType.Serial
- });
+ });
+
+ [JoinName("DeviceIpAddresss")]
+ public JoinDataComplete DeviceIpAddresss = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 301,
+ JoinSpan = 1
+ },
+ new JoinMetadata
+ {
+ Description = "IP Address of device",
+ JoinCapabilities = eJoinCapabilities.ToSIMPL,
+ JoinType = eJoinType.Serial
+ });
+
+ [JoinName("SipPhoneNumber")]
+ public JoinDataComplete SipPhoneNumber = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 302,
+ JoinSpan = 1
+ },
+ new JoinMetadata
+ {
+ Description = "SIP phone number of device",
+ JoinCapabilities = eJoinCapabilities.ToSIMPL,
+ JoinType = eJoinType.Serial
+ });
+
+ [JoinName("E164Alias")]
+ public JoinDataComplete E164Alias = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 303,
+ JoinSpan = 1
+ },
+ new JoinMetadata
+ {
+ Description = "E164 alias of device",
+ JoinCapabilities = eJoinCapabilities.ToSIMPL,
+ JoinType = eJoinType.Serial
+ });
+
+ [JoinName("H323Id")]
+ public JoinDataComplete H323Id = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 304,
+ JoinSpan = 1
+ },
+ new JoinMetadata
+ {
+ Description = "H323 ID of device",
+ JoinCapabilities = eJoinCapabilities.ToSIMPL,
+ JoinType = eJoinType.Serial
+ });
+
+ [JoinName("SipUri")]
+ public JoinDataComplete SipUri = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 305,
+ JoinSpan = 1
+ },
+ new JoinMetadata
+ {
+ Description = "SIP URI of device",
+ JoinCapabilities = eJoinCapabilities.ToSIMPL,
+ JoinType = eJoinType.Serial
+ });
[JoinName("DirectoryEntrySelectedName")]
public JoinDataComplete DirectoryEntrySelectedName = new JoinDataComplete(
diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Queues/GenericQueue.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Queues/GenericQueue.cs
index f6b0794c..9080435e 100644
--- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Queues/GenericQueue.cs
+++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Queues/GenericQueue.cs
@@ -118,7 +118,7 @@ namespace PepperDash.Essentials.Core.Queues
///
public GenericQueue(string key, int pacing, Thread.eThreadPriority priority, int capacity)
: this(key, priority, capacity, pacing)
- {
+ {
}
///
diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/CodecActiveCallItem.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/CodecActiveCallItem.cs
index 75c4fc1a..d1dbdf5f 100644
--- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/CodecActiveCallItem.cs
+++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/CodecActiveCallItem.cs
@@ -12,34 +12,40 @@ namespace PepperDash.Essentials.Devices.Common.Codec
{
public class CodecActiveCallItem
{
- [JsonProperty("name")]
+ [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)]
public string Name { get; set; }
- [JsonProperty("number")]
+ [JsonProperty("number", NullValueHandling = NullValueHandling.Ignore)]
public string Number { get; set; }
- [JsonProperty("type")]
+ [JsonProperty("type", NullValueHandling = NullValueHandling.Ignore)]
[JsonConverter(typeof(StringEnumConverter))]
public eCodecCallType Type { get; set; }
- [JsonProperty("status")]
+ [JsonProperty("status", NullValueHandling = NullValueHandling.Ignore)]
[JsonConverter(typeof(StringEnumConverter))]
public eCodecCallStatus Status { get; set; }
- [JsonProperty("direction")]
+ [JsonProperty("direction", NullValueHandling = NullValueHandling.Ignore)]
[JsonConverter(typeof(StringEnumConverter))]
public eCodecCallDirection Direction { get; set; }
- [JsonProperty("id")]
+ [JsonProperty("id", NullValueHandling = NullValueHandling.Ignore)]
public string Id { get; set; }
+ [JsonProperty("isOnHold", NullValueHandling = NullValueHandling.Ignore)]
+ public bool IsOnHold { get; set; }
+
+ [JsonProperty("duration", NullValueHandling = NullValueHandling.Ignore)]
+ public TimeSpan Duration { get; set; }
+
//public object CallMetaData { get; set; }
///
/// Returns true when this call is any status other than
/// Unknown, Disconnected, Disconnecting
///
- [JsonProperty("isActiveCall")]
+ [JsonProperty("isActiveCall", NullValueHandling = NullValueHandling.Ignore)]
public bool IsActiveCall
{
get
diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/IHasCallHold.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/IHasCallHold.cs
new file mode 100644
index 00000000..78211841
--- /dev/null
+++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/IHasCallHold.cs
@@ -0,0 +1,23 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Crestron.SimplSharp;
+
+namespace PepperDash.Essentials.Devices.Common.Codec
+{
+ public interface IHasCallHold
+ {
+ ///
+ /// Put the specified call on hold
+ ///
+ ///
+ void HoldCall(CodecActiveCallItem activeCall);
+
+ ///
+ /// Resume the specified call
+ ///
+ ///
+ void ResumeCall(CodecActiveCallItem activeCall);
+ }
+}
\ No newline at end of file
diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/iHasCallHistory.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/iHasCallHistory.cs
index 256938d1..83a74022 100644
--- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/iHasCallHistory.cs
+++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/iHasCallHistory.cs
@@ -23,9 +23,9 @@ namespace PepperDash.Essentials.Devices.Common.Codec
public enum eCodecOccurrenceType
{
Unknown = 0,
- Placed,
- Received,
- NoAnswer
+ Placed = 1,
+ Received = 2,
+ NoAnswer = 3,
}
///
diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Essentials Devices Common.csproj b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Essentials Devices Common.csproj
index c1873d26..fe4d5f61 100644
--- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Essentials Devices Common.csproj
+++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Essentials Devices Common.csproj
@@ -108,6 +108,7 @@
+
@@ -131,6 +132,7 @@
+
diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoCamera.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoCamera.cs
index 6f68b369..9e8b0554 100644
--- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoCamera.cs
+++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoCamera.cs
@@ -41,12 +41,12 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
public void PanLeft()
{
- ParentCodec.SendText(string.Format("xCommand Call FarEndControl Camera Move Value: Left CallId: {0}", CallId));
+ ParentCodec.EnqueueCommand(string.Format("xCommand Call FarEndControl Camera Move Value: Left CallId: {0}", CallId));
}
public void PanRight()
{
- ParentCodec.SendText(string.Format("xCommand Call FarEndControl Camera Move Value: Right CallId: {0}", CallId));
+ ParentCodec.EnqueueCommand(string.Format("xCommand Call FarEndControl Camera Move Value: Right CallId: {0}", CallId));
}
public void PanStop()
@@ -60,12 +60,12 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
public void TiltDown()
{
- ParentCodec.SendText(string.Format("xCommand Call FarEndControl Camera Move Value: Down CallId: {0}", CallId));
+ ParentCodec.EnqueueCommand(string.Format("xCommand Call FarEndControl Camera Move Value: Down CallId: {0}", CallId));
}
public void TiltUp()
{
- ParentCodec.SendText(string.Format("xCommand Call FarEndControl Camera Move Value: Up CallId: {0}", CallId));
+ ParentCodec.EnqueueCommand(string.Format("xCommand Call FarEndControl Camera Move Value: Up CallId: {0}", CallId));
}
public void TiltStop()
@@ -79,12 +79,12 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
public void ZoomIn()
{
- ParentCodec.SendText(string.Format("xCommand Call FarEndControl Camera Move Value: ZoomIn CallId: {0}", CallId));
+ ParentCodec.EnqueueCommand(string.Format("xCommand Call FarEndControl Camera Move Value: ZoomIn CallId: {0}", CallId));
}
public void ZoomOut()
{
- ParentCodec.SendText(string.Format("xCommand Call FarEndControl Camera Move Value: ZoomOut CallId: {0}", CallId));
+ ParentCodec.EnqueueCommand(string.Format("xCommand Call FarEndControl Camera Move Value: ZoomOut CallId: {0}", CallId));
}
public void ZoomStop()
@@ -97,7 +97,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
void Stop()
{
- ParentCodec.SendText(string.Format("xCommand Call FarEndControl Camera Stop CallId: {0}", CallId));
+ ParentCodec.EnqueueCommand(string.Format("xCommand Call FarEndControl Camera Stop CallId: {0}", CallId));
}
public void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge)
@@ -116,7 +116,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
///
/// The ID of the camera on the codec
///
- protected uint CameraId { get; private set; }
+ public uint CameraId { get; private set; }
///
/// Valid range 1-15
@@ -202,7 +202,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
{
if (!isMoving)
{
- ParentCodec.SendText(string.Format("xCommand Camera Ramp CameraId: {0} Pan: Left PanSpeed: {1}", CameraId, PanSpeed));
+ ParentCodec.EnqueueCommand(string.Format("xCommand Camera Ramp CameraId: {0} Pan: Left PanSpeed: {1}", CameraId, PanSpeed));
isPanning = true;
}
}
@@ -211,14 +211,14 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
{
if (!isMoving)
{
- ParentCodec.SendText(string.Format("xCommand Camera Ramp CameraId: {0} Pan: Right PanSpeed: {1}", CameraId, PanSpeed));
+ ParentCodec.EnqueueCommand(string.Format("xCommand Camera Ramp CameraId: {0} Pan: Right PanSpeed: {1}", CameraId, PanSpeed));
isPanning = true;
}
}
public void PanStop()
{
- ParentCodec.SendText(string.Format("xCommand Camera Ramp CameraId: {0} Pan: Stop", CameraId));
+ ParentCodec.EnqueueCommand(string.Format("xCommand Camera Ramp CameraId: {0} Pan: Stop", CameraId));
isPanning = false;
}
@@ -232,7 +232,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
{
if (!isMoving)
{
- ParentCodec.SendText(string.Format("xCommand Camera Ramp CameraId: {0} Tilt: Down TiltSpeed: {1}", CameraId, TiltSpeed));
+ ParentCodec.EnqueueCommand(string.Format("xCommand Camera Ramp CameraId: {0} Tilt: Down TiltSpeed: {1}", CameraId, TiltSpeed));
isTilting = true;
}
}
@@ -241,14 +241,14 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
{
if (!isMoving)
{
- ParentCodec.SendText(string.Format("xCommand Camera Ramp CameraId: {0} Tilt: Up TiltSpeed: {1}", CameraId, TiltSpeed));
+ ParentCodec.EnqueueCommand(string.Format("xCommand Camera Ramp CameraId: {0} Tilt: Up TiltSpeed: {1}", CameraId, TiltSpeed));
isTilting = true;
}
}
public void TiltStop()
{
- ParentCodec.SendText(string.Format("xCommand Camera Ramp CameraId: {0} Tilt: Stop", CameraId));
+ ParentCodec.EnqueueCommand(string.Format("xCommand Camera Ramp CameraId: {0} Tilt: Stop", CameraId));
isTilting = false;
}
@@ -260,7 +260,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
{
if (!isMoving)
{
- ParentCodec.SendText(string.Format("xCommand Camera Ramp CameraId: {0} Zoom: In ZoomSpeed: {1}", CameraId, ZoomSpeed));
+ ParentCodec.EnqueueCommand(string.Format("xCommand Camera Ramp CameraId: {0} Zoom: In ZoomSpeed: {1}", CameraId, ZoomSpeed));
isZooming = true;
}
}
@@ -269,14 +269,14 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
{
if (!isMoving)
{
- ParentCodec.SendText(string.Format("xCommand Camera Ramp CameraId: {0} Zoom: Out ZoomSpeed: {1}", CameraId, ZoomSpeed));
+ ParentCodec.EnqueueCommand(string.Format("xCommand Camera Ramp CameraId: {0} Zoom: Out ZoomSpeed: {1}", CameraId, ZoomSpeed));
isZooming = true;
}
}
public void ZoomStop()
{
- ParentCodec.SendText(string.Format("xCommand Camera Ramp CameraId: {0} Zoom: Stop", CameraId));
+ ParentCodec.EnqueueCommand(string.Format("xCommand Camera Ramp CameraId: {0} Zoom: Stop", CameraId));
isZooming = false;
}
@@ -288,7 +288,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
{
if (!isMoving)
{
- ParentCodec.SendText(string.Format("xCommand Camera Ramp CameraId: {0} Focus: Near", CameraId));
+ ParentCodec.EnqueueCommand(string.Format("xCommand Camera Ramp CameraId: {0} Focus: Near", CameraId));
isFocusing = true;
}
}
@@ -297,20 +297,20 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
{
if (!isMoving)
{
- ParentCodec.SendText(string.Format("xCommand Camera Ramp CameraId: {0} Focus: Far", CameraId));
+ ParentCodec.EnqueueCommand(string.Format("xCommand Camera Ramp CameraId: {0} Focus: Far", CameraId));
isFocusing = true;
}
}
public void FocusStop()
{
- ParentCodec.SendText(string.Format("xCommand Camera Ramp CameraId: {0} Focus: Stop", CameraId));
+ ParentCodec.EnqueueCommand(string.Format("xCommand Camera Ramp CameraId: {0} Focus: Stop", CameraId));
isFocusing = false;
}
public void TriggerAutoFocus()
{
- ParentCodec.SendText(string.Format("xCommand Camera TriggerAutofocus CameraId: {0}", CameraId));
+ ParentCodec.EnqueueCommand(string.Format("xCommand Camera TriggerAutofocus CameraId: {0}", CameraId));
}
#endregion
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 e4945ce8..2396afdc 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
@@ -9,11 +9,39 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
{
#region Digital
+ [JoinName("PresentationLocalOnly")]
+ public JoinDataComplete PresentationLocalOnly = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 205,
+ JoinSpan = 1
+ },
+ new JoinMetadata
+ {
+ Description = "Presentation Local Only Feedback",
+ JoinCapabilities = eJoinCapabilities.ToSIMPL,
+ JoinType = eJoinType.Digital
+ });
+
+ [JoinName("PresentationLocalRemote")]
+ public JoinDataComplete PresentationLocalRemote = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 206,
+ JoinSpan = 1
+ },
+ new JoinMetadata
+ {
+ Description = "Presentation Local and Remote Feedback",
+ JoinCapabilities = eJoinCapabilities.ToSIMPL,
+ JoinType = eJoinType.Digital
+ });
+
[JoinName("ActivateDoNotDisturbMode")]
public JoinDataComplete ActivateDoNotDisturbMode = new JoinDataComplete(
new JoinData
{
- JoinNumber = 221,
+ JoinNumber = 241,
JoinSpan = 1
},
new JoinMetadata
@@ -27,7 +55,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
public JoinDataComplete DeactivateDoNotDisturbMode = new JoinDataComplete(
new JoinData
{
- JoinNumber = 222,
+ JoinNumber = 242,
JoinSpan = 1
},
new JoinMetadata
@@ -41,7 +69,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
public JoinDataComplete ToggleDoNotDisturbMode = new JoinDataComplete(
new JoinData
{
- JoinNumber = 223,
+ JoinNumber = 243,
JoinSpan = 1
},
new JoinMetadata
@@ -55,7 +83,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
public JoinDataComplete ActivateStandby = new JoinDataComplete(
new JoinData
{
- JoinNumber = 226,
+ JoinNumber = 246,
JoinSpan = 1
},
new JoinMetadata
@@ -69,7 +97,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
public JoinDataComplete DeactivateStandby = new JoinDataComplete(
new JoinData
{
- JoinNumber = 227,
+ JoinNumber = 247,
JoinSpan = 1
},
new JoinMetadata
@@ -83,7 +111,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
public JoinDataComplete ActivateHalfWakeMode = new JoinDataComplete(
new JoinData
{
- JoinNumber = 228,
+ JoinNumber = 248,
JoinSpan = 1
},
new JoinMetadata
@@ -97,7 +125,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
public JoinDataComplete EnteringStandbyMode = new JoinDataComplete(
new JoinData
{
- JoinNumber = 229,
+ JoinNumber = 249,
JoinSpan = 1
},
new JoinMetadata
@@ -112,12 +140,55 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
#region Analog
+ [JoinName("RingtoneVolume")]
+ public JoinDataComplete RingtoneVolume = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 21,
+ JoinSpan = 1
+ },
+ new JoinMetadata
+ {
+ Description = "Ringtone volume set/FB. Valid values are 0 - 100 in increments of 5 (5, 10, 15, 20, etc.)",
+ JoinCapabilities = eJoinCapabilities.ToFromSIMPL,
+ JoinType = eJoinType.Analog
+ });
+
+ [JoinName("PresentationSource")]
+ public JoinDataComplete PresentationSource = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 201,
+ JoinSpan = 1
+ },
+ new JoinMetadata
+ {
+ Description = "Presentation set/FB. Valid values are 0 - 6 depending on the codec model.",
+ JoinCapabilities = eJoinCapabilities.ToFromSIMPL,
+ JoinType = eJoinType.Analog
+ });
+
#endregion
#region Serials
+ [JoinName("CommandToDevice")]
+ public JoinDataComplete CommandToDevice = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 5,
+ JoinSpan = 1
+ },
+ new JoinMetadata
+ {
+ Description = "Sends a serial command to the device. Do not include the delimiter, it will be added automatically.",
+ JoinCapabilities = eJoinCapabilities.FromSIMPL,
+ JoinType = eJoinType.Serial
+ });
+
+
#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 0a13434d..edcc61ec 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,2198 +1,2623 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Text.RegularExpressions;
-using Crestron.SimplSharp;
-using Crestron.SimplSharpPro.CrestronThread;
-using Crestron.SimplSharpPro.DeviceSupport;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
-
-using PepperDash.Core;
-using PepperDash.Essentials.Core;
-using PepperDash.Essentials.Core.Bridges;
-using PepperDash.Essentials.Core.Config;
-using PepperDash.Essentials.Core.DeviceTypeInterfaces;
-using PepperDash.Essentials.Core.Routing;
-using PepperDash.Essentials.Devices.Common.Cameras;
-using PepperDash.Essentials.Devices.Common.Codec;
-using PepperDash.Essentials.Devices.Common.VideoCodec;
-using PepperDash.Essentials.Core.Queues;
-
-namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
-{
- enum eCommandType { SessionStart, SessionEnd, Command, GetStatus, GetConfiguration };
- public enum eExternalSourceType {camera, desktop, document_camera, mediaplayer, PC, whiteboard, other}
- public enum eExternalSourceMode {Ready, NotReady, Hidden, Error}
-
- public class CiscoSparkCodec : VideoCodecBase, IHasCallHistory, IHasCallFavorites, IHasDirectory,
- IHasScheduleAwareness, IOccupancyStatusProvider, IHasCodecLayouts, IHasCodecSelfView,
- ICommunicationMonitor, IRouting, IHasCodecCameras, IHasCameraAutoMode, IHasCodecRoomPresets, IHasExternalSourceSwitching, IHasBranding, IHasCameraOff, IHasCameraMute
- {
- private bool _externalSourceChangeRequested;
-
- public event EventHandler DirectoryResultReturned;
-
- private CTimer _brandingTimer;
-
- public CommunicationGather PortGather { get; private set; }
-
- public StatusMonitorBase CommunicationMonitor { get; private set; }
-
- private GenericQueue ReceiveQueue;
-
- public BoolFeedback PresentationViewMaximizedFeedback { get; private set; }
-
- string CurrentPresentationView;
-
- public BoolFeedback RoomIsOccupiedFeedback { get; private set; }
-
- public IntFeedback PeopleCountFeedback { get; private set; }
-
- public BoolFeedback CameraAutoModeIsOnFeedback { get; private set; }
-
- public BoolFeedback SelfviewIsOnFeedback { get; private set; }
-
- public StringFeedback SelfviewPipPositionFeedback { get; private set; }
-
- public StringFeedback LocalLayoutFeedback { get; private set; }
-
- public BoolFeedback LocalLayoutIsProminentFeedback { get; private set; }
-
- public BoolFeedback FarEndIsSharingContentFeedback { get; private set; }
-
- private CodecCommandWithLabel CurrentSelfviewPipPosition;
-
- private CodecCommandWithLabel CurrentLocalLayout;
-
- ///
- /// List the available positions for the selfview PIP window
- ///
- public List SelfviewPipPositions = new List()
- {
- new CodecCommandWithLabel("CenterLeft", "Center Left"),
- new CodecCommandWithLabel("CenterRight", "Center Right"),
- new CodecCommandWithLabel("LowerLeft", "Lower Left"),
- new CodecCommandWithLabel("LowerRight", "Lower Right"),
- new CodecCommandWithLabel("UpperCenter", "Upper Center"),
- new CodecCommandWithLabel("UpperLeft", "Upper Left"),
- new CodecCommandWithLabel("UpperRight", "Upper Right"),
- };
-
- ///
- /// Lists the available options for local layout
- ///
- public List LocalLayouts = new List()
- {
- //new CodecCommandWithLabel("auto", "Auto"),
- //new CiscoCodecLocalLayout("custom", "Custom"), // Left out for now
- new CodecCommandWithLabel("equal","Equal"),
- new CodecCommandWithLabel("overlay","Overlay"),
- new CodecCommandWithLabel("prominent","Prominent"),
- new CodecCommandWithLabel("single","Single")
- };
-
- private CiscoCodecConfiguration.RootObject CodecConfiguration = new CiscoCodecConfiguration.RootObject();
-
- private CiscoCodecStatus.RootObject CodecStatus = new CiscoCodecStatus.RootObject();
-
- public CodecCallHistory CallHistory { get; private set; }
-
- public CodecCallFavorites CallFavorites { get; private set; }
-
- ///
- /// The root level of the directory
- ///
- public CodecDirectory DirectoryRoot { get; private set; }
-
- ///
- /// Represents the current state of the directory and is computed on get
- ///
- public CodecDirectory CurrentDirectoryResult
- {
- get
- {
- if (DirectoryBrowseHistory.Count > 0)
- return DirectoryBrowseHistory[DirectoryBrowseHistory.Count - 1];
- else
- return DirectoryRoot;
- }
- }
-
- public BoolFeedback CurrentDirectoryResultIsNotDirectoryRoot { get; private set; }
-
- ///
- /// Tracks the directory browse history when browsing beyond the root directory
- ///
- public List DirectoryBrowseHistory { get; private set; }
-
- public CodecScheduleAwareness CodecSchedule { get; private set; }
-
- ///
- /// Gets and returns the scaled volume of the codec
- ///
- protected override Func VolumeLevelFeedbackFunc
- {
- get
- {
- return () => CrestronEnvironment.ScaleWithLimits(CodecStatus.Status.Audio.Volume.IntValue, 100, 0, 65535, 0);
- }
- }
-
- protected override Func PrivacyModeIsOnFeedbackFunc
- {
- get
- {
- return () => CodecStatus.Status.Audio.Microphones.Mute.BoolValue;
- }
- }
-
- protected override Func StandbyIsOnFeedbackFunc
- {
- get
- {
- return () => CodecStatus.Status.Standby.State.BoolValue;
- }
- }
-
- ///
- /// Gets the value of the currently shared source, or returns null
- ///
- protected override Func SharingSourceFeedbackFunc
- {
- get
- {
- return () => PresentationSourceKey;
- }
- }
-
- protected override Func SharingContentIsOnFeedbackFunc
- {
- get
- {
- return () => CodecStatus.Status.Conference.Presentation.Mode.BoolValue;
- }
- }
-
- protected Func FarEndIsSharingContentFeedbackFunc
- {
- get
- {
- return () => CodecStatus.Status.Conference.Presentation.Mode.Value == "Receiving";
- }
- }
-
- protected override Func MuteFeedbackFunc
- {
- get
- {
- return () => CodecStatus.Status.Audio.VolumeMute.BoolValue;
- }
- }
-
- protected Func RoomIsOccupiedFeedbackFunc
- {
- get
- {
- return () => CodecStatus.Status.RoomAnalytics.PeoplePresence.BoolValue;
- }
- }
-
- protected Func PeopleCountFeedbackFunc
- {
- get
- {
- return () => CodecStatus.Status.RoomAnalytics.PeopleCount.Current.IntValue;
- }
- }
-
- protected Func SpeakerTrackIsOnFeedbackFunc
- {
- get
- {
- return () => CodecStatus.Status.Cameras.SpeakerTrack.Status.BoolValue;
- }
- }
-
- protected Func SelfViewIsOnFeedbackFunc
- {
- get
- {
- return () => CodecStatus.Status.Video.Selfview.Mode.BoolValue;
- }
- }
-
- protected Func SelfviewPipPositionFeedbackFunc
- {
- get
- {
- return () => CurrentSelfviewPipPosition.Label;
- }
- }
-
- protected Func LocalLayoutFeedbackFunc
- {
- get
- {
- return () => CurrentLocalLayout.Label;
- }
- }
-
- protected Func LocalLayoutIsProminentFeedbackFunc
- {
- get
- {
- return () => CurrentLocalLayout.Label == "Prominent";
- }
- }
-
-
- private string CliFeedbackRegistrationExpression;
-
- private CodecSyncState SyncState;
-
- public CodecPhonebookSyncState PhonebookSyncState { get; private set; }
-
- private StringBuilder JsonMessage;
-
- private bool JsonFeedbackMessageIsIncoming;
-
- public bool CommDebuggingIsOn;
-
- string Delimiter = "\r\n";
-
- ///
- /// Used to track the current connector used for the presentation source
- ///
- int PresentationSource;
-
- string PresentationSourceKey;
-
- string PhonebookMode = "Local"; // Default to Local
-
- uint PhonebookResultsLimit = 255; // Could be set later by config.
-
- CTimer LoginMessageReceivedTimer;
- CTimer RetryConnectionTimer;
-
- // **___________________________________________________________________**
- // Timers to be moved to the global system timer at a later point....
- CTimer BookingsRefreshTimer;
- CTimer PhonebookRefreshTimer;
- // **___________________________________________________________________**
-
- public RoutingInputPort CodecOsdIn { get; private set; }
- public RoutingInputPort HdmiIn2 { get; private set; }
- public RoutingInputPort HdmiIn3 { get; private set; }
- public RoutingOutputPort HdmiOut1 { get; private set; }
- public RoutingOutputPort HdmiOut2 { get; private set; }
-
-
- // Constructor for IBasicCommunication
- public CiscoSparkCodec(DeviceConfig config, IBasicCommunication comm)
- : base(config)
- {
- var props = JsonConvert.DeserializeObject(config.Properties.ToString());
-
- // Use the configured phonebook results limit if present
- if (props.PhonebookResultsLimit > 0)
- {
- PhonebookResultsLimit = props.PhonebookResultsLimit;
- }
-
- // The queue that will collect the repsonses in the order they are received
- ReceiveQueue = new GenericQueue(this.Key + "-rxQueue", 25);
-
- RoomIsOccupiedFeedback = new BoolFeedback(RoomIsOccupiedFeedbackFunc);
- PeopleCountFeedback = new IntFeedback(PeopleCountFeedbackFunc);
- CameraAutoModeIsOnFeedback = new BoolFeedback(SpeakerTrackIsOnFeedbackFunc);
- SelfviewIsOnFeedback = new BoolFeedback(SelfViewIsOnFeedbackFunc);
- SelfviewPipPositionFeedback = new StringFeedback(SelfviewPipPositionFeedbackFunc);
- LocalLayoutFeedback = new StringFeedback(LocalLayoutFeedbackFunc);
- LocalLayoutIsProminentFeedback = new BoolFeedback(LocalLayoutIsProminentFeedbackFunc);
- FarEndIsSharingContentFeedback = new BoolFeedback(FarEndIsSharingContentFeedbackFunc);
- CameraIsOffFeedback = new BoolFeedback(() => CodecStatus.Status.Video.Input.MainVideoMute.BoolValue);
- CameraIsMutedFeedback = CameraIsOffFeedback;
- SupportsCameraOff = true;
-
- PresentationViewMaximizedFeedback = new BoolFeedback(() => CurrentPresentationView == "Maximized");
-
- Communication = comm;
-
- if (props.CommunicationMonitorProperties != null)
- {
- CommunicationMonitor = new GenericCommunicationMonitor(this, Communication, props.CommunicationMonitorProperties);
- }
- else
- {
- CommunicationMonitor = new GenericCommunicationMonitor(this, Communication, 30000, 120000, 300000, "xStatus SystemUnit Software Version\r");
- }
-
- if (props.Sharing != null)
- AutoShareContentWhileInCall = props.Sharing.AutoShareContentWhileInCall;
-
- ShowSelfViewByDefault = props.ShowSelfViewByDefault;
-
- DeviceManager.AddDevice(CommunicationMonitor);
-
- PhonebookMode = props.PhonebookMode;
-
- SyncState = new CodecSyncState(Key + "--Sync");
-
- PhonebookSyncState = new CodecPhonebookSyncState(Key + "--PhonebookSync");
-
- SyncState.InitialSyncCompleted += new EventHandler(SyncState_InitialSyncCompleted);
-
- PortGather = new CommunicationGather(Communication, Delimiter);
- PortGather.IncludeDelimiter = true;
- PortGather.LineReceived += this.Port_LineReceived;
-
- CodecInfo = new CiscoCodecInfo(CodecStatus, CodecConfiguration);
-
- CallHistory = new CodecCallHistory();
-
-
- if (props.Favorites != null)
- {
- CallFavorites = new CodecCallFavorites();
- CallFavorites.Favorites = props.Favorites;
- }
-
- DirectoryRoot = new CodecDirectory();
-
- DirectoryBrowseHistory = new List();
-
- CurrentDirectoryResultIsNotDirectoryRoot = new BoolFeedback(() => DirectoryBrowseHistory.Count > 0);
-
- CurrentDirectoryResultIsNotDirectoryRoot.FireUpdate();
-
- CodecSchedule = new CodecScheduleAwareness();
-
- //Set Feedback Actions
- SetFeedbackActions();
-
- CodecOsdIn = new RoutingInputPort(RoutingPortNames.CodecOsd, eRoutingSignalType.Audio | eRoutingSignalType.Video,
- eRoutingPortConnectionType.Hdmi, new Action(StopSharing), this);
- HdmiIn2 = new RoutingInputPort(RoutingPortNames.HdmiIn2, eRoutingSignalType.Audio | eRoutingSignalType.Video,
- eRoutingPortConnectionType.Hdmi, new Action(SelectPresentationSource1), this);
- HdmiIn3 = new RoutingInputPort(RoutingPortNames.HdmiIn3, eRoutingSignalType.Audio | eRoutingSignalType.Video,
- eRoutingPortConnectionType.Hdmi, new Action(SelectPresentationSource2), this);
-
- HdmiOut1 = new RoutingOutputPort(RoutingPortNames.HdmiOut1, eRoutingSignalType.Audio | eRoutingSignalType.Video,
- eRoutingPortConnectionType.Hdmi, null, this);
- HdmiOut2 = new RoutingOutputPort(RoutingPortNames.HdmiOut2, eRoutingSignalType.Audio | eRoutingSignalType.Video,
- eRoutingPortConnectionType.Hdmi, null, this);
-
- InputPorts.Add(CodecOsdIn);
- InputPorts.Add(HdmiIn2);
- InputPorts.Add(HdmiIn3);
- OutputPorts.Add(HdmiOut1);
-
- SetUpCameras();
-
- CreateOsdSource();
-
- ExternalSourceListEnabled = props.ExternalSourceListEnabled;
- ExternalSourceInputPort = props.ExternalSourceInputPort;
-
- if (props.UiBranding == null)
- {
- return;
- }
- Debug.Console(2, this, "Setting branding properties enable: {0} _brandingUrl {1}", props.UiBranding.Enable,
- props.UiBranding.BrandingUrl);
-
- BrandingEnabled = props.UiBranding.Enable;
-
- _brandingUrl = props.UiBranding.BrandingUrl;
- }
-
- private void SetFeedbackActions()
- {
- CodecStatus.Status.Audio.Volume.ValueChangedAction = VolumeLevelFeedback.FireUpdate;
- CodecStatus.Status.Audio.VolumeMute.ValueChangedAction = MuteFeedback.FireUpdate;
- CodecStatus.Status.Audio.Microphones.Mute.ValueChangedAction = PrivacyModeIsOnFeedback.FireUpdate;
- CodecStatus.Status.Standby.State.ValueChangedAction = StandbyIsOnFeedback.FireUpdate;
- CodecStatus.Status.RoomAnalytics.PeoplePresence.ValueChangedAction = RoomIsOccupiedFeedback.FireUpdate;
- CodecStatus.Status.RoomAnalytics.PeopleCount.Current.ValueChangedAction = PeopleCountFeedback.FireUpdate;
- CodecStatus.Status.Cameras.SpeakerTrack.Status.ValueChangedAction = CameraAutoModeIsOnFeedback.FireUpdate;
- CodecStatus.Status.Cameras.SpeakerTrack.Availability.ValueChangedAction = () => { SupportsCameraAutoMode = CodecStatus.Status.Cameras.SpeakerTrack.Availability.BoolValue; };
- CodecStatus.Status.Video.Selfview.Mode.ValueChangedAction = SelfviewIsOnFeedback.FireUpdate;
- CodecStatus.Status.Video.Selfview.PIPPosition.ValueChangedAction = ComputeSelfviewPipStatus;
- CodecStatus.Status.Video.Layout.LayoutFamily.Local.ValueChangedAction = ComputeLocalLayout;
- CodecStatus.Status.Conference.Presentation.Mode.ValueChangedAction = () =>
- {
- SharingContentIsOnFeedback.FireUpdate();
- FarEndIsSharingContentFeedback.FireUpdate();
- };
-
- try
- {
- CodecStatus.Status.Video.Input.MainVideoMute.ValueChangedAction = CameraIsOffFeedback.FireUpdate;
- }
- catch (Exception ex)
- {
- Debug.Console(0, this, "Error setting MainVideuMute Action: {0}", ex);
-
- if (ex.InnerException != null)
- {
- Debug.Console(0, this, "Error setting MainVideuMute Action: {0}", ex);
- }
- }
- }
-
- ///
- /// Creates the fake OSD source, and connects it's AudioVideo output to the CodecOsdIn input
- /// to enable routing
- ///
- void CreateOsdSource()
- {
- OsdSource = new DummyRoutingInputsDevice(Key + "[osd]");
- DeviceManager.AddDevice(OsdSource);
- var tl = new TieLine(OsdSource.AudioVideoOutputPort, CodecOsdIn);
- TieLineCollection.Default.Add(tl);
- }
-
- public void InitializeBranding(string roomKey)
- {
- Debug.Console(1, this, "Initializing Branding for room {0}", roomKey);
-
- if (!BrandingEnabled)
- {
- return;
- }
-
- var mcBridgeKey = String.Format("mobileControlBridge-{0}", roomKey);
-
- var mcBridge = DeviceManager.GetDeviceForKey(mcBridgeKey) as IMobileControlRoomBridge;
-
- if (!String.IsNullOrEmpty(_brandingUrl))
- {
- Debug.Console(1, this, "Branding URL found: {0}", _brandingUrl);
- if (_brandingTimer != null)
- {
- _brandingTimer.Stop();
- _brandingTimer.Dispose();
- }
-
- _brandingTimer = new CTimer((o) =>
- {
- if (_sendMcUrl)
- {
- SendMcBrandingUrl(mcBridge);
- _sendMcUrl = false;
- }
- else
- {
- SendBrandingUrl();
- _sendMcUrl = true;
- }
- }, 0, 15000);
- } else if (String.IsNullOrEmpty(_brandingUrl))
- {
- Debug.Console(1, this, "No Branding URL found");
- if (mcBridge == null) return;
-
- Debug.Console(2, this, "Setting QR code URL: {0}", mcBridge.QrCodeUrl);
-
- mcBridge.UserCodeChanged += (o, a) => SendMcBrandingUrl(mcBridge);
- mcBridge.UserPromptedForCode += (o, a) => DisplayUserCode(mcBridge.UserCode);
-
- SendMcBrandingUrl(mcBridge);
- }
- }
-
- ///
- /// Displays the code for the specified duration
- ///
- /// Mobile Control user code
- private void DisplayUserCode(string code)
- {
- SendText(string.Format("xcommand userinterface message alert display title:\"Mobile Control User Code:\" text:\"{0}\" duration: 30", code));
- }
-
- private void SendMcBrandingUrl(IMobileControlRoomBridge mcBridge)
- {
- if (mcBridge == null)
- {
- return;
- }
-
- Debug.Console(1, this, "Sending url: {0}", mcBridge.QrCodeUrl);
-
- SendText("xconfiguration userinterface custommessage: \"Scan the QR code with a mobile phone to get started\"");
- SendText("xconfiguration userinterface osd halfwakemessage: \"Tap the touch panel or scan the QR code with a mobile phone to get started\"");
-
- var checksum = !String.IsNullOrEmpty(mcBridge.QrCodeChecksum)
- ? String.Format("checksum: {0} ", mcBridge.QrCodeChecksum)
- : String.Empty;
-
- SendText(String.Format(
- "xcommand userinterface branding fetch {1}type: branding url: {0}",
- mcBridge.QrCodeUrl, checksum));
- SendText(String.Format(
- "xcommand userinterface branding fetch {1}type: halfwakebranding url: {0}",
- mcBridge.QrCodeUrl, checksum));
- }
-
- private void SendBrandingUrl()
- {
- Debug.Console(1, this, "Sending url: {0}", _brandingUrl);
-
- SendText(String.Format("xcommand userinterface branding fetch type: branding url: {0}",
- _brandingUrl));
- SendText(String.Format("xcommand userinterface branding fetch type: halfwakebranding url: {0}",
- _brandingUrl));
- }
- ///
- /// Starts the HTTP feedback server and syncronizes state of codec
- ///
- ///
- public override bool CustomActivate()
- {
- CrestronConsole.AddNewConsoleCommand(SetCommDebug, "SetCodecCommDebug", "0 for Off, 1 for on", ConsoleAccessLevelEnum.AccessOperator);
- CrestronConsole.AddNewConsoleCommand(GetPhonebook, "GetCodecPhonebook", "Triggers a refresh of the codec phonebook", ConsoleAccessLevelEnum.AccessOperator);
- CrestronConsole.AddNewConsoleCommand(GetBookings, "GetCodecBookings", "Triggers a refresh of the booking data for today", ConsoleAccessLevelEnum.AccessOperator);
-
- return base.CustomActivate();
- }
-
- #region Overrides of Device
-
- public override void Initialize()
- {
- var socket = Communication as ISocketStatus;
- if (socket != null)
- {
- socket.ConnectionChange += new EventHandler(socket_ConnectionChange);
- }
-
- Communication.Connect();
-
- CommunicationMonitor.Start();
-
- const string prefix = "xFeedback register ";
-
- CliFeedbackRegistrationExpression =
- prefix + "/Configuration" + Delimiter +
- prefix + "/Status/Audio" + Delimiter +
- prefix + "/Status/Call" + Delimiter +
- prefix + "/Status/Conference/Presentation" + Delimiter +
- prefix + "/Status/Cameras/SpeakerTrack" + Delimiter +
- prefix + "/Status/RoomAnalytics" + Delimiter +
- prefix + "/Status/RoomPreset" + Delimiter +
- prefix + "/Status/Standby" + Delimiter +
- prefix + "/Status/Video/Selfview" + Delimiter +
- prefix + "/Status/Video/Layout" + Delimiter +
- prefix + "/Status/Video/Input/MainVideoMute" + Delimiter +
- prefix + "/Bookings" + Delimiter +
- prefix + "/Event/CallDisconnect" + Delimiter +
- prefix + "/Event/Bookings" + Delimiter +
- prefix + "/Event/CameraPresetListUpdated" + Delimiter +
- prefix + "/Event/UserInterface/Presentation/ExternalSource/Selected/SourceIdentifier" + Delimiter;
- }
-
- #endregion
-
- ///
- /// Fires when initial codec sync is completed. Used to then send commands to get call history, phonebook, bookings, etc.
- ///
- ///
- ///
- void SyncState_InitialSyncCompleted(object sender, EventArgs e)
- {
- // Fire the ready event
- SetIsReady();
- //CommDebuggingIsOn = false;
-
- GetCallHistory();
-
- PhonebookRefreshTimer = new CTimer(CheckCurrentHour, 3600000, 3600000); // check each hour to see if the phonebook should be downloaded
- GetPhonebook(null);
-
- BookingsRefreshTimer = new CTimer(GetBookings, 900000, 900000); // 15 minute timer to check for new booking info
- GetBookings(null);
- }
-
- public void SetCommDebug(string s)
- {
- if (s == "1")
- {
- CommDebuggingIsOn = true;
- Debug.Console(0, this, "Comm Debug Enabled.");
- }
- else
- {
- CommDebuggingIsOn = false;
- Debug.Console(0, this, "Comm Debug Disabled.");
- }
- }
-
- void socket_ConnectionChange(object sender, GenericSocketStatusChageEventArgs e)
- {
- Debug.Console(1, this, "Socket status change {0}", e.Client.ClientStatus);
- if (e.Client.IsConnected)
- {
- if(!SyncState.LoginMessageWasReceived)
- LoginMessageReceivedTimer = new CTimer(o => DisconnectClientAndReconnect(), 5000);
- }
- else
- {
- SyncState.CodecDisconnected();
- PhonebookSyncState.CodecDisconnected();
-
- if (PhonebookRefreshTimer != null)
- {
- PhonebookRefreshTimer.Stop();
- PhonebookRefreshTimer = null;
- }
-
- if (BookingsRefreshTimer != null)
- {
- BookingsRefreshTimer.Stop();
- BookingsRefreshTimer = null;
- }
- }
- }
-
- void DisconnectClientAndReconnect()
- {
- Debug.Console(1, this, "Retrying connection to codec.");
-
- Communication.Disconnect();
-
- RetryConnectionTimer = new CTimer(o => Communication.Connect(), 2000);
-
- //CrestronEnvironment.Sleep(2000);
-
- //Communication.Connect();
- }
-
- ///
- /// Gathers responses from the codec (including the delimiter. Responses are checked to see if they contain JSON data and if so, the data is collected until a complete JSON
- /// message is received before forwarding the message to be deserialized.
- ///
- ///
- ///
- void Port_LineReceived(object dev, GenericCommMethodReceiveTextArgs args)
- {
- if (CommDebuggingIsOn)
- {
- if (!JsonFeedbackMessageIsIncoming)
- Debug.Console(1, this, "RX: '{0}'", args.Text);
- }
-
- if (args.Text == "{" + Delimiter) // Check for the beginning of a new JSON message
- {
- JsonFeedbackMessageIsIncoming = true;
-
- if (CommDebuggingIsOn)
- Debug.Console(1, this, "Incoming JSON message...");
-
- JsonMessage = new StringBuilder();
- }
- else if (args.Text == "}" + Delimiter) // Check for the end of a JSON message
- {
- JsonFeedbackMessageIsIncoming = false;
-
- JsonMessage.Append(args.Text);
-
- if (CommDebuggingIsOn)
- Debug.Console(1, this, "Complete JSON Received:\n{0}", JsonMessage.ToString());
-
- // Enqueue the complete message to be deserialized
-
- ReceiveQueue.Enqueue(new ProcessStringMessage(JsonMessage.ToString(), DeserializeResponse));
-
- return;
- }
-
- if(JsonFeedbackMessageIsIncoming)
- {
- JsonMessage.Append(args.Text);
-
- //Debug.Console(1, this, "Building JSON:\n{0}", JsonMessage.ToString());
- return;
- }
-
- if (!SyncState.InitialSyncComplete)
- {
- switch (args.Text.Trim().ToLower()) // remove the whitespace
- {
- case "*r login successful":
- {
- SyncState.LoginMessageReceived();
-
- if(LoginMessageReceivedTimer != null)
- LoginMessageReceivedTimer.Stop();
-
- SendText("xPreferences outputmode json");
- break;
- }
- case "xpreferences outputmode json":
- {
- if (!SyncState.InitialStatusMessageWasReceived)
- SendText("xStatus");
- break;
- }
- case "xfeedback register /event/calldisconnect":
- {
- SyncState.FeedbackRegistered();
- break;
- }
- }
- }
-
-
- }
-
- ///
- /// Appends the delimiter and send the command to the codec
- ///
- ///
- public void SendText(string command)
- {
- if (CommDebuggingIsOn)
- Debug.Console(1, this, "Sending: '{0}'", command);
-
- Communication.SendText(command + Delimiter);
- }
-
- void DeserializeResponse(string response)
- {
- try
- {
- //// Serializer settings. We want to ignore null values and missing members
- //JsonSerializerSettings settings = new JsonSerializerSettings();
- //settings.NullValueHandling = NullValueHandling.Ignore;
- //settings.MissingMemberHandling = MissingMemberHandling.Ignore;
- //settings.ObjectCreationHandling = ObjectCreationHandling.Auto;
-
- if (response.IndexOf("\"Status\":{") > -1 || response.IndexOf("\"Status\": {") > -1)
- {
- // Status Message
-
- // Temp object so we can inpsect for call data before simply deserializing
- CiscoCodecStatus.RootObject tempCodecStatus = new CiscoCodecStatus.RootObject();
-
- JsonConvert.PopulateObject(response, tempCodecStatus);
-
- // Check to see if the message contains /Status/Conference/Presentation/LocalInstance and extract source value
- var conference = tempCodecStatus.Status.Conference;
-
- if (conference.Presentation.LocalInstance.Count > 0)
- {
- if (!string.IsNullOrEmpty(conference.Presentation.LocalInstance[0].ghost))
- PresentationSource = 0;
- else if (conference.Presentation.LocalInstance[0].Source != null)
- {
- PresentationSource = conference.Presentation.LocalInstance[0].Source.IntValue;
- }
- }
-
- // Check to see if this is a call status message received after the initial status message
- if (tempCodecStatus.Status.Call.Count > 0)
- {
- // Iterate through the call objects in the response
- foreach (CiscoCodecStatus.Call call in tempCodecStatus.Status.Call)
- {
- var tempActiveCall = ActiveCalls.FirstOrDefault(c => c.Id.Equals(call.id));
-
- if (tempActiveCall != null)
- {
- bool changeDetected = false;
-
- eCodecCallStatus newStatus = eCodecCallStatus.Unknown;
-
- // Update properties of ActiveCallItem
- if(call.Status != null)
- if (!string.IsNullOrEmpty(call.Status.Value))
- {
- tempActiveCall.Status = CodecCallStatus.ConvertToStatusEnum(call.Status.Value);
-
- if (newStatus == eCodecCallStatus.Connected)
- GetCallHistory();
-
- changeDetected = true;
- }
- if (call.CallType != null)
- if (!string.IsNullOrEmpty(call.CallType.Value))
- {
- tempActiveCall.Type = CodecCallType.ConvertToTypeEnum(call.CallType.Value);
- changeDetected = true;
- }
- if (call.DisplayName != null)
- if (!string.IsNullOrEmpty(call.DisplayName.Value))
- {
- tempActiveCall.Name = call.DisplayName.Value;
- changeDetected = true;
- }
- if (call.Direction != null)
- {
- if (!string.IsNullOrEmpty(call.Direction.Value))
- {
- tempActiveCall.Direction = CodecCallDirection.ConvertToDirectionEnum(call.Direction.Value);
- changeDetected = true;
- }
- }
-
- if (changeDetected)
- {
- SetSelfViewMode();
- OnCallStatusChange(tempActiveCall);
- ListCalls();
- }
- }
- else if( call.ghost == null ) // if the ghost value is present the call has ended already
- {
- // Create a new call item
- var newCallItem = new CodecActiveCallItem()
- {
- Id = call.id,
- Status = CodecCallStatus.ConvertToStatusEnum(call.Status.Value),
- Name = call.DisplayName.Value,
- Number = call.RemoteNumber.Value,
- Type = CodecCallType.ConvertToTypeEnum(call.CallType.Value),
- Direction = CodecCallDirection.ConvertToDirectionEnum(call.Direction.Value)
- };
-
- // Add it to the ActiveCalls List
- ActiveCalls.Add(newCallItem);
-
- ListCalls();
-
- SetSelfViewMode();
- OnCallStatusChange(newCallItem);
- }
-
- }
-
- }
-
- // Check for Room Preset data (comes in partial, so we need to handle these responses differently to prevent appending duplicate items
- var tempPresets = tempCodecStatus.Status.RoomPreset;
-
- if (tempPresets.Count > 0)
- {
- // Create temporary list to store the existing items from the CiscoCodecStatus.RoomPreset collection
- List existingRoomPresets = new List();
- // Add the existing items to the temporary list
- existingRoomPresets.AddRange(CodecStatus.Status.RoomPreset);
- // Populate the CodecStatus object (this will append new values to the RoomPreset collection
- JsonConvert.PopulateObject(response, CodecStatus);
-
- JObject jResponse = JObject.Parse(response);
-
- IList roomPresets = jResponse["Status"]["RoomPreset"].Children().ToList();
- // Iterate the new items in this response agains the temporary list. Overwrite any existing items and add new ones.
- foreach (var preset in tempPresets)
- {
- // First fine the existing preset that matches the id
- var existingPreset = existingRoomPresets.FirstOrDefault(p => p.id.Equals(preset.id));
- if (existingPreset != null)
- {
- Debug.Console(1, this, "Existing Room Preset with ID: {0} found. Updating.", existingPreset.id);
-
- JToken updatedPreset = null;
-
- // Find the JToken from the response with the matching id
- foreach (var jPreset in roomPresets)
- {
- if (jPreset["id"].Value() == existingPreset.id)
- updatedPreset = jPreset;
- }
-
- if (updatedPreset != null)
- {
- // use PopulateObject to overlay the partial data onto the existing object
- JsonConvert.PopulateObject(updatedPreset.ToString(), existingPreset);
- }
-
- }
- else
- {
- Debug.Console(1, this, "New Room Preset with ID: {0}. Adding.", preset.id);
- existingRoomPresets.Add(preset);
- }
- }
-
- // Replace the list in the CodecStatus object with the processed list
- CodecStatus.Status.RoomPreset = existingRoomPresets;
-
- // Generecise the list
- NearEndPresets = RoomPresets.GetGenericPresets(CodecStatus.Status.RoomPreset);
-
- var handler = CodecRoomPresetsListHasChanged;
- if (handler != null)
- {
- handler(this, new EventArgs());
- }
- }
- else
- {
- JsonConvert.PopulateObject(response, CodecStatus);
- }
-
- if (!SyncState.InitialStatusMessageWasReceived)
- {
- SyncState.InitialStatusMessageReceived();
-
- if (!SyncState.InitialConfigurationMessageWasReceived)
- SendText("xConfiguration");
- }
- }
- else if (response.IndexOf("\"Configuration\":{") > -1 || response.IndexOf("\"Configuration\": {") > -1)
- {
- // Configuration Message
-
- JsonConvert.PopulateObject(response, CodecConfiguration);
-
- if (!SyncState.InitialConfigurationMessageWasReceived)
- {
- SyncState.InitialConfigurationMessageReceived();
- if (!SyncState.FeedbackWasRegistered)
- {
- SendText(CliFeedbackRegistrationExpression);
- }
- }
-
- }
- else if (response.IndexOf("\"Event\":{") > -1 || response.IndexOf("\"Event\": {") > -1)
- {
- if (response.IndexOf("\"CallDisconnect\":{") > -1 || response.IndexOf("\"CallDisconnect\": {") > -1)
- {
- CiscoCodecEvents.RootObject eventReceived = new CiscoCodecEvents.RootObject();
-
- JsonConvert.PopulateObject(response, eventReceived);
-
- EvalutateDisconnectEvent(eventReceived);
- }
- else if (response.IndexOf("\"Bookings\":{") > -1 || response.IndexOf("\"Bookings\": {") > -1) // The list has changed, reload it
- {
- GetBookings(null);
- }
-
- else if (response.IndexOf("\"UserInterface\":{") > -1 || response.IndexOf("\"UserInterface\": {") > -1) // External Source Trigger
- {
- CiscoCodecEvents.RootObject eventReceived = new CiscoCodecEvents.RootObject();
- JsonConvert.PopulateObject(response, eventReceived);
- Debug.Console(2, this, "*** Got an External Source Selection {0} {1}", eventReceived, eventReceived.Event.UserInterface, eventReceived.Event.UserInterface.Presentation.ExternalSource.Selected.SourceIdentifier.Value);
-
- if (RunRouteAction != null && !_externalSourceChangeRequested)
- {
- RunRouteAction(eventReceived.Event.UserInterface.Presentation.ExternalSource.Selected.SourceIdentifier.Value, null);
- }
-
- _externalSourceChangeRequested = false;
- }
- }
- else if (response.IndexOf("\"CommandResponse\":{") > -1 || response.IndexOf("\"CommandResponse\": {") > -1)
- {
- // CommandResponse Message
-
- if (response.IndexOf("\"CallHistoryRecentsResult\":{") > -1 || response.IndexOf("\"CallHistoryRecentsResult\": {") > -1)
- {
- var codecCallHistory = new CiscoCallHistory.RootObject();
-
- JsonConvert.PopulateObject(response, codecCallHistory);
-
- CallHistory.ConvertCiscoCallHistoryToGeneric(codecCallHistory.CommandResponse.CallHistoryRecentsResult.Entry);
- }
- else if (response.IndexOf("\"CallHistoryDeleteEntryResult\":{") > -1 || response.IndexOf("\"CallHistoryDeleteEntryResult\": {") > -1)
- {
- GetCallHistory();
- }
- else if (response.IndexOf("\"PhonebookSearchResult\":{") > -1 || response.IndexOf("\"PhonebookSearchResult\": {") > -1)
- {
- var codecPhonebookResponse = new CiscoCodecPhonebook.RootObject();
-
- JsonConvert.PopulateObject(response, codecPhonebookResponse);
-
- if (!PhonebookSyncState.InitialPhonebookFoldersWasReceived)
- {
- // Check if the phonebook has any folders
- PhonebookSyncState.InitialPhonebookFoldersReceived();
-
- PhonebookSyncState.SetPhonebookHasFolders(codecPhonebookResponse.CommandResponse.PhonebookSearchResult.Folder.Count > 0);
-
- if (PhonebookSyncState.PhonebookHasFolders)
- {
- DirectoryRoot.AddFoldersToDirectory(CiscoCodecPhonebook.GetRootFoldersFromSearchResult(codecPhonebookResponse.CommandResponse.PhonebookSearchResult));
- }
-
- // Get the number of contacts in the phonebook
- GetPhonebookContacts();
- }
- else if (!PhonebookSyncState.NumberOfContactsWasReceived)
- {
- // Store the total number of contacts in the phonebook
- PhonebookSyncState.SetNumberOfContacts(Int32.Parse(codecPhonebookResponse.CommandResponse.PhonebookSearchResult.ResultInfo.TotalRows.Value));
-
- DirectoryRoot.AddContactsToDirectory(CiscoCodecPhonebook.GetRootContactsFromSearchResult(codecPhonebookResponse.CommandResponse.PhonebookSearchResult));
-
- PhonebookSyncState.PhonebookRootEntriesReceived();
-
- PrintDirectory(DirectoryRoot);
- }
- else if (PhonebookSyncState.InitialSyncComplete)
- {
- var directoryResults = new CodecDirectory();
-
- if(codecPhonebookResponse.CommandResponse.PhonebookSearchResult.ResultInfo.TotalRows.Value != "0")
- directoryResults = CiscoCodecPhonebook.ConvertCiscoPhonebookToGeneric(codecPhonebookResponse.CommandResponse.PhonebookSearchResult);
-
- PrintDirectory(directoryResults);
-
- DirectoryBrowseHistory.Add(directoryResults);
-
- OnDirectoryResultReturned(directoryResults);
-
- }
- }
- else if (response.IndexOf("\"BookingsListResult\":{") > -1)
- {
- var codecBookings = new CiscoCodecBookings.RootObject();
-
- JsonConvert.PopulateObject(response, codecBookings);
-
- if(codecBookings.CommandResponse.BookingsListResult.ResultInfo.TotalRows.Value != "0")
- CodecSchedule.Meetings = CiscoCodecBookings.GetGenericMeetingsFromBookingResult(codecBookings.CommandResponse.BookingsListResult.Booking);
-
- BookingsRefreshTimer.Reset(900000, 900000);
- }
-
- }
-
- }
- catch (Exception ex)
- {
- Debug.Console(1, this, "Error Deserializing feedback from codec: {0}", ex);
- }
- }
-
- ///
- /// Call when directory results are updated
- ///
- ///
- void OnDirectoryResultReturned(CodecDirectory result)
- {
- CurrentDirectoryResultIsNotDirectoryRoot.FireUpdate();
-
- // This will return the latest results to all UIs. Multiple indendent UI Directory browsing will require a different methodology
- var handler = DirectoryResultReturned;
- if (handler != null)
- {
- handler(this, new DirectoryEventArgs()
- {
- Directory = result,
- DirectoryIsOnRoot = !CurrentDirectoryResultIsNotDirectoryRoot.BoolValue
- });
- }
-
- PrintDirectory(result);
- }
-
- ///
- /// Evaluates an event received from the codec
- ///
- ///
- void EvalutateDisconnectEvent(CiscoCodecEvents.RootObject eventReceived)
- {
- if (eventReceived.Event.CallDisconnect != null)
- {
- var tempActiveCall = ActiveCalls.FirstOrDefault(c => c.Id.Equals(eventReceived.Event.CallDisconnect.CallId.Value));
-
- // Remove the call from the Active calls list
- if (tempActiveCall != null)
- {
- ActiveCalls.Remove(tempActiveCall);
-
- ListCalls();
-
- SetSelfViewMode();
- // Notify of the call disconnection
- SetNewCallStatusAndFireCallStatusChange(eCodecCallStatus.Disconnected, tempActiveCall);
-
- GetCallHistory();
- }
- }
- }
-
- ///
- ///
- ///
- ///
- public override void ExecuteSwitch(object selector)
- {
- (selector as Action)();
- PresentationSourceKey = selector.ToString();
- }
-
- ///
- /// This is necessary for devices that are "routers" in the middle of the path, even though it only has one output and
- /// may only have one input.
- ///
- public void ExecuteSwitch(object inputSelector, object outputSelector, eRoutingSignalType signalType)
- {
- ExecuteSwitch(inputSelector);
- PresentationSourceKey = inputSelector.ToString();
- }
-
-
- ///
- /// Gets the ID of the last connected call
- ///
- ///
- public string GetCallId()
- {
- string callId = null;
-
- if (ActiveCalls.Count > 1)
- {
- var lastCallIndex = ActiveCalls.Count - 1;
- callId = ActiveCalls[lastCallIndex].Id;
- }
- else if (ActiveCalls.Count == 1)
- callId = ActiveCalls[0].Id;
-
- return callId;
-
- }
-
- public void GetCallHistory()
- {
- SendText("xCommand CallHistory Recents Limit: 20 Order: OccurrenceTime");
- }
-
- ///
- /// Required for IHasScheduleAwareness
- ///
- public void GetSchedule()
- {
- GetBookings(null);
- }
-
- ///
- /// Gets the bookings for today
- ///
- ///
- public void GetBookings(object command)
- {
- Debug.Console(1, this, "Retrieving Booking Info from Codec. Current Time: {0}", DateTime.Now.ToLocalTime());
-
- SendText("xCommand Bookings List Days: 1 DayOffset: 0");
- }
-
- ///
- /// Checks to see if it is 2am (or within that hour) and triggers a download of the phonebook
- ///
- ///
- public void CheckCurrentHour(object o)
- {
- if (DateTime.Now.Hour == 2)
- {
- Debug.Console(1, this, "Checking hour to see if phonebook should be downloaded. Current hour is {0}", DateTime.Now.Hour);
-
- GetPhonebook(null);
- PhonebookRefreshTimer.Reset(3600000, 3600000);
- }
- }
-
- ///
- /// Triggers a refresh of the codec phonebook
- ///
- /// Just to allow this method to be called from a console command
- public void GetPhonebook(string command)
- {
- PhonebookSyncState.CodecDisconnected();
-
- DirectoryRoot = new CodecDirectory();
-
- GetPhonebookFolders();
- }
-
- private void GetPhonebookFolders()
- {
- // Get Phonebook Folders (determine local/corporate from config, and set results limit)
- SendText(string.Format("xCommand Phonebook Search PhonebookType: {0} ContactType: Folder", PhonebookMode));
- }
-
- private void GetPhonebookContacts()
- {
- // Get Phonebook Folders (determine local/corporate from config, and set results limit)
- SendText(string.Format("xCommand Phonebook Search PhonebookType: {0} ContactType: Contact Limit: {1}", PhonebookMode, PhonebookResultsLimit));
- }
-
- ///
- /// Searches the codec phonebook for all contacts matching the search string
- ///
- ///
- public void SearchDirectory(string searchString)
- {
- SendText(string.Format("xCommand Phonebook Search SearchString: \"{0}\" PhonebookType: {1} ContactType: Contact Limit: {2}", searchString, PhonebookMode, PhonebookResultsLimit));
- }
-
- ///
- /// // Get contents of a specific folder in the phonebook
- ///
- ///
- public void GetDirectoryFolderContents(string folderId)
- {
- SendText(string.Format("xCommand Phonebook Search FolderId: {0} PhonebookType: {1} ContactType: Any Limit: {2}", folderId, PhonebookMode, PhonebookResultsLimit));
- }
-
- ///
- /// Sets the parent folder contents or the directory root as teh current directory and fires the event. Used to browse up a level
- ///
- ///
- public void GetDirectoryParentFolderContents()
- {
- var currentDirectory = new CodecDirectory();
-
- if (DirectoryBrowseHistory.Count > 0)
- {
- var lastItemIndex = DirectoryBrowseHistory.Count - 1;
- var parentDirectoryContents = DirectoryBrowseHistory[lastItemIndex];
-
- DirectoryBrowseHistory.Remove(DirectoryBrowseHistory[lastItemIndex]);
-
- currentDirectory = parentDirectoryContents;
-
- }
- else
- {
- currentDirectory = DirectoryRoot;
- }
-
- OnDirectoryResultReturned(currentDirectory);
- }
-
- ///
- /// Clears the session browse history and fires the event with the directory root
- ///
- public void SetCurrentDirectoryToRoot()
- {
- DirectoryBrowseHistory.Clear();
-
- OnDirectoryResultReturned(DirectoryRoot);
- }
-
- ///
- /// Prints the directory to console
- ///
- ///
- void PrintDirectory(CodecDirectory directory)
- {
- if (Debug.Level > 0)
- {
- Debug.Console(1, this, "Directory Results:\n");
-
- foreach (DirectoryItem item in directory.CurrentDirectoryResults)
- {
- if (item is DirectoryFolder)
- {
- Debug.Console(1, this, "[+] {0}", item.Name);
- }
- else if (item is DirectoryContact)
- {
- Debug.Console(1, this, "{0}", item.Name);
- }
- }
- Debug.Console(1, this, "Directory is on Root Level: {0}", !CurrentDirectoryResultIsNotDirectoryRoot.BoolValue);
- }
-
- }
-
- ///
- /// Simple dial method
- ///
- ///
- public override void Dial(string number)
- {
- SendText(string.Format("xCommand Dial Number: \"{0}\"", number));
- }
-
- ///
- /// Dials a specific meeting
- ///
- ///
- public override void Dial(Meeting meeting)
- {
- foreach (Call c in meeting.Calls)
- {
- Dial(c.Number, c.Protocol, c.CallRate, c.CallType, meeting.Id);
- }
- }
-
- ///
- /// Detailed dial method
- ///
- ///
- ///
- ///
- ///
- ///
- public void Dial(string number, string protocol, string callRate, string callType, string meetingId)
- {
- SendText(string.Format("xCommand Dial Number: \"{0}\" Protocol: {1} CallRate: {2} CallType: {3} BookingId: {4}", number, protocol, callRate, callType, meetingId));
- }
-
- public override void EndCall(CodecActiveCallItem activeCall)
- {
- SendText(string.Format("xCommand Call Disconnect CallId: {0}", activeCall.Id));
- }
-
- public override void EndAllCalls()
- {
- foreach (CodecActiveCallItem activeCall in ActiveCalls)
- {
- SendText(string.Format("xCommand Call Disconnect CallId: {0}", activeCall.Id));
- }
- }
-
- public override void AcceptCall(CodecActiveCallItem item)
- {
- SendText("xCommand Call Accept");
- }
-
- public override void RejectCall(CodecActiveCallItem item)
- {
- SendText("xCommand Call Reject");
- }
-
- public override void SendDtmf(string s)
- {
- SendText(string.Format("xCommand Call DTMFSend CallId: {0} DTMFString: \"{1}\"", GetCallId(), s));
- }
-
- public void SelectPresentationSource(int source)
- {
- PresentationSource = source;
-
- StartSharing();
- }
-
- ///
- /// Select source 1 as the presetnation source
- ///
- public void SelectPresentationSource1()
- {
- SelectPresentationSource(2);
- }
-
- ///
- /// Select source 2 as the presetnation source
- ///
- public void SelectPresentationSource2()
- {
- SelectPresentationSource(3);
- }
-
- ///
- /// Starts presentation sharing
- ///
- public override void StartSharing()
- {
- string sendingMode = string.Empty;
-
- if (IsInCall)
- sendingMode = "LocalRemote";
- else
- sendingMode = "LocalOnly";
-
- if(PresentationSource > 0)
- SendText(string.Format("xCommand Presentation Start PresentationSource: {0} SendingMode: {1}", PresentationSource, sendingMode));
- }
-
- ///
- /// Stops sharing the current presentation
- ///
- public override void StopSharing()
- {
- PresentationSource = 0;
-
- SendText("xCommand Presentation Stop");
- }
-
- public override void PrivacyModeOn()
- {
- SendText("xCommand Audio Microphones Mute");
- }
-
- public override void PrivacyModeOff()
- {
- SendText("xCommand Audio Microphones Unmute");
- }
-
- public override void PrivacyModeToggle()
- {
- SendText("xCommand Audio Microphones ToggleMute");
- }
-
- public override void MuteOff()
- {
- SendText("xCommand Audio Volume Unmute");
- }
-
- public override void MuteOn()
- {
- SendText("xCommand Audio Volume Mute");
- }
-
- public override void MuteToggle()
- {
- SendText("xCommand Audio Volume ToggleMute");
- }
-
- ///
- /// Increments the voluem
- ///
- ///
- public override void VolumeUp(bool pressRelease)
- {
- SendText("xCommand Audio Volume Increase");
- }
-
- ///
- /// Decrements the volume
- ///
- ///
- public override void VolumeDown(bool pressRelease)
- {
- SendText("xCommand Audio Volume Decrease");
- }
-
- ///
- /// Scales the level and sets the codec to the specified level within its range
- ///
- /// level from slider (0-65535 range)
- public override void SetVolume(ushort level)
- {
- var scaledLevel = CrestronEnvironment.ScaleWithLimits(level, 65535, 0, 100, 0);
- SendText(string.Format("xCommand Audio Volume Set Level: {0}", scaledLevel));
- }
-
- ///
- /// Recalls the default volume on the codec
- ///
- public void VolumeSetToDefault()
- {
- SendText("xCommand Audio Volume SetToDefault");
- }
-
- ///
- /// Puts the codec in standby mode
- ///
- public override void StandbyActivate()
- {
- SendText("xCommand Standby Activate");
- }
-
- ///
- /// Wakes the codec from standby
- ///
- public override void StandbyDeactivate()
- {
- SendText("xCommand Standby Deactivate");
- }
-
- public override void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge)
- {
- LinkVideoCodecToApi(this, trilist, joinStart, joinMapKey, bridge);
- }
-
- ///
- /// Reboots the codec
- ///
- public void Reboot()
- {
- SendText("xCommand SystemUnit Boot Action: Restart");
- }
-
- ///
- /// Sets SelfView Mode based on config
- ///
- void SetSelfViewMode()
- {
- if (!IsInCall)
- {
- SelfViewModeOff();
- }
- else
- {
- if (ShowSelfViewByDefault)
- SelfViewModeOn();
- else
- SelfViewModeOff();
- }
- }
-
- ///
- /// Turns on Selfview Mode
- ///
- public void SelfViewModeOn()
- {
- SendText("xCommand Video Selfview Set Mode: On");
- }
-
- ///
- /// Turns off Selfview Mode
- ///
- public void SelfViewModeOff()
- {
- SendText("xCommand Video Selfview Set Mode: Off");
- }
-
- ///
- /// Toggles Selfview mode on/off
- ///
- public void SelfViewModeToggle()
- {
- string mode = string.Empty;
-
- if (CodecStatus.Status.Video.Selfview.Mode.BoolValue)
- mode = "Off";
- else
- mode = "On";
-
- SendText(string.Format("xCommand Video Selfview Set Mode: {0}", mode));
- }
-
- ///
- /// Sets a specified position for the selfview PIP window
- ///
- ///
- public void SelfviewPipPositionSet(CodecCommandWithLabel position)
- {
- SendText(string.Format("xCommand Video Selfview Set Mode: On PIPPosition: {0}", position.Command));
- }
-
- ///
- /// Toggles to the next selfview PIP position
- ///
- public void SelfviewPipPositionToggle()
- {
- if (CurrentSelfviewPipPosition != null)
- {
- var nextPipPositionIndex = SelfviewPipPositions.IndexOf(CurrentSelfviewPipPosition) + 1;
-
- if (nextPipPositionIndex >= SelfviewPipPositions.Count) // Check if we need to loop back to the first item in the list
- nextPipPositionIndex = 0;
-
- SelfviewPipPositionSet(SelfviewPipPositions[nextPipPositionIndex]);
- }
- }
-
- ///
- /// Sets a specific local layout
- ///
- ///
- public void LocalLayoutSet(CodecCommandWithLabel layout)
- {
- SendText(string.Format("xCommand Video Layout LayoutFamily Set Target: local LayoutFamily: {0}", layout.Command));
- }
-
- ///
- /// Toggles to the next local layout
- ///
- public void LocalLayoutToggle()
- {
- if(CurrentLocalLayout != null)
- {
- var nextLocalLayoutIndex = LocalLayouts.IndexOf(CurrentLocalLayout) + 1;
-
- if (nextLocalLayoutIndex >= LocalLayouts.Count) // Check if we need to loop back to the first item in the list
- nextLocalLayoutIndex = 0;
-
- LocalLayoutSet(LocalLayouts[nextLocalLayoutIndex]);
- }
- }
-
- ///
- /// Toggles between single/prominent layouts
- ///
- public void LocalLayoutToggleSingleProminent()
- {
- if (CurrentLocalLayout != null)
- {
- if (CurrentLocalLayout.Label != "Prominent")
- LocalLayoutSet(LocalLayouts.FirstOrDefault(l => l.Label.Equals("Prominent")));
- else
- LocalLayoutSet(LocalLayouts.FirstOrDefault(l => l.Label.Equals("Single")));
- }
-
- }
-
- ///
- ///
- ///
- public void MinMaxLayoutToggle()
- {
- if (PresentationViewMaximizedFeedback.BoolValue)
- CurrentPresentationView = "Minimized";
- else
- CurrentPresentationView = "Maximized";
-
- SendText(string.Format("xCommand Video PresentationView Set View: {0}", CurrentPresentationView));
- PresentationViewMaximizedFeedback.FireUpdate();
- }
-
- ///
- /// Calculates the current selfview PIP position
- ///
- void ComputeSelfviewPipStatus()
- {
- CurrentSelfviewPipPosition = SelfviewPipPositions.FirstOrDefault(p => p.Command.ToLower().Equals(CodecStatus.Status.Video.Selfview.PIPPosition.Value.ToLower()));
-
- if(CurrentSelfviewPipPosition != null)
- SelfviewIsOnFeedback.FireUpdate();
- }
-
- ///
- /// Calculates the current local Layout
- ///
- void ComputeLocalLayout()
- {
- CurrentLocalLayout = LocalLayouts.FirstOrDefault(l => l.Command.ToLower().Equals(CodecStatus.Status.Video.Layout.LayoutFamily.Local.Value.ToLower()));
-
- if (CurrentLocalLayout != null)
- LocalLayoutFeedback.FireUpdate();
- }
-
- public void RemoveCallHistoryEntry(CodecCallHistory.CallHistoryEntry entry)
- {
- SendText(string.Format("xCommand CallHistory DeleteEntry CallHistoryId: {0} AcknowledgeConsecutiveDuplicates: True", entry.OccurrenceHistoryId));
- }
-
- #region IHasCameraSpeakerTrack
-
- public void CameraAutoModeToggle()
- {
- if (!CameraAutoModeIsOnFeedback.BoolValue)
- {
- SendText("xCommand Cameras SpeakerTrack Activate");
- }
- else
- {
- SendText("xCommand Cameras SpeakerTrack Deactivate");
- }
- }
-
- public void CameraAutoModeOn()
- {
- if (CameraIsOffFeedback.BoolValue)
- {
- CameraMuteOff();
- }
-
- SendText("xCommand Cameras SpeakerTrack Activate");
- }
-
- public void CameraAutoModeOff()
- {
- if (CameraIsOffFeedback.BoolValue)
- {
- CameraMuteOff();
- }
-
- SendText("xCommand Cameras SpeakerTrack Deactivate");
- }
-
- #endregion
-
- ///
- /// Builds the cameras List. Could later be modified to build from config data
- ///
- void SetUpCameras()
- {
- // Add the internal camera
- Cameras = new List();
-
- var internalCamera = new CiscoSparkCamera(Key + "-camera1", "Near End", this, 1);
-
- if(CodecStatus.Status.Cameras.Camera.Count > 0)
- internalCamera.SetCapabilites(CodecStatus.Status.Cameras.Camera[0].Capabilities.Options.Value);
- else
- // Somehow subscribe to the event on the Options.Value property and update when it changes.
-
- Cameras.Add(internalCamera);
-
- // Add the far end camera
- var farEndCamera = new CiscoFarEndCamera(Key + "-cameraFar", "Far End", this);
- Cameras.Add(farEndCamera);
-
- SelectedCameraFeedback = new StringFeedback(() => SelectedCamera.Key);
-
- ControllingFarEndCameraFeedback = new BoolFeedback(() => SelectedCamera is IAmFarEndCamera);
-
- DeviceManager.AddDevice(internalCamera);
- DeviceManager.AddDevice(farEndCamera);
-
- NearEndPresets = new List(15);
-
- FarEndRoomPresets = new List(15);
-
- // Add the far end presets
- for (int i = 1; i <= FarEndRoomPresets.Capacity; i++)
- {
- var label = string.Format("Far End Preset {0}", i);
- FarEndRoomPresets.Add(new CodecRoomPreset(i, label, true, false));
- }
-
- SelectedCamera = internalCamera; ; // call the method to select the camera and ensure the feedbacks get updated.
- }
-
- #region IHasCodecCameras Members
-
- public event EventHandler CameraSelected;
-
- public List Cameras { get; private set; }
-
- public StringFeedback SelectedCameraFeedback { get; private set; }
-
- private CameraBase _selectedCamera;
-
- ///
- /// Returns the selected camera
- ///
- public CameraBase SelectedCamera
- {
- get
- {
- return _selectedCamera;
- }
- private set
- {
- _selectedCamera = value;
- SelectedCameraFeedback.FireUpdate();
- ControllingFarEndCameraFeedback.FireUpdate();
- if (CameraIsOffFeedback.BoolValue)
- CameraMuteOff();
-
- var handler = CameraSelected;
- if (handler != null)
- {
- handler(this, new CameraSelectedEventArgs(SelectedCamera));
- }
- }
- }
-
- public void SelectCamera(string key)
- {
- var camera = Cameras.FirstOrDefault(c => c.Key.IndexOf(key, StringComparison.OrdinalIgnoreCase) > -1);
- if (camera != null)
- {
- Debug.Console(2, this, "Selected Camera with key: '{0}'", camera.Key);
- SelectedCamera = camera;
- }
- else
- Debug.Console(2, this, "Unable to select camera with key: '{0}'", key);
- }
-
- public CameraBase FarEndCamera { get; private set; }
-
- public BoolFeedback ControllingFarEndCameraFeedback { get; private set; }
-
- #endregion
-
- ///
- ///
- ///
- public class CiscoCodecInfo : VideoCodecInfo
- {
- public CiscoCodecStatus.RootObject CodecStatus { get; private set; }
-
- public CiscoCodecConfiguration.RootObject CodecConfiguration { get; private set; }
-
- public override bool MultiSiteOptionIsEnabled
- {
- get
- {
- if (!string.IsNullOrEmpty(CodecStatus.Status.SystemUnit.Software.OptionKeys.MultiSite.Value) && CodecStatus.Status.SystemUnit.Software.OptionKeys.MultiSite.Value.ToLower() == "true")
- return true;
- else
- return false;
- }
-
- }
- public override string IpAddress
- {
- get
- {
- if (CodecConfiguration.Configuration.Network != null)
- {
- if (CodecConfiguration.Configuration.Network.Count > 0)
- return CodecConfiguration.Configuration.Network[0].IPv4.Address.Value;
- }
- return string.Empty;
- }
- }
- public override string E164Alias
- {
- get
- {
- if (CodecConfiguration.Configuration.H323 != null && CodecConfiguration.Configuration.H323.H323Alias.E164 != null)
- {
- return CodecConfiguration.Configuration.H323.H323Alias.E164.Value;
- }
- else
- {
- return string.Empty;
- }
- }
- }
- public override string H323Id
- {
- get
- {
- if (CodecConfiguration.Configuration.H323 != null && CodecConfiguration.Configuration.H323.H323Alias != null
- && CodecConfiguration.Configuration.H323.H323Alias.ID != null)
- {
- return CodecConfiguration.Configuration.H323.H323Alias.ID.Value;
- }
- else
- {
- return string.Empty;
- }
- }
- }
- public override string SipPhoneNumber
- {
- get
- {
- if (CodecStatus.Status.SIP != null && CodecStatus.Status.SIP.Registration.Count > 0)
- {
- var match = Regex.Match(CodecStatus.Status.SIP.Registration[0].URI.Value, @"(\d+)"); // extract numbers only
- if (match.Success)
- {
- Debug.Console(1, "Extracted phone number as '{0}' from string '{1}'", match.Groups[1].Value, CodecStatus.Status.SIP.Registration[0].URI.Value);
- return match.Groups[1].Value;
- }
- else
- {
- Debug.Console(1, "Unable to extract phone number from string: '{0}'", CodecStatus.Status.SIP.Registration[0].URI.Value);
- return string.Empty;
- }
- }
- else
- {
- Debug.Console(1, "Unable to extract phone number. No SIP Registration items found");
- return string.Empty;
- }
- }
- }
-
- public override string SipUri
- {
- get
- {
- if (CodecStatus.Status.SIP != null && CodecStatus.Status.SIP.AlternateURI.Primary.URI.Value != null)
- {
- return CodecStatus.Status.SIP.AlternateURI.Primary.URI.Value;
- }
- else if (CodecStatus.Status.UserInterface != null &&
- CodecStatus.Status.UserInterface.ContactInfo.ContactMethod[0].Number.Value != null)
- {
- return CodecStatus.Status.UserInterface.ContactInfo.ContactMethod[0].Number.Value;
- }
- else
- return string.Empty;
- }
- }
-
- public override bool AutoAnswerEnabled
- {
- get
- {
- if (CodecConfiguration.Configuration.Conference.AutoAnswer.Mode.Value.ToLower() == "on")
- return true;
- else
- return false;
- }
- }
-
- public CiscoCodecInfo(CiscoCodecStatus.RootObject status, CiscoCodecConfiguration.RootObject configuration)
- {
- CodecStatus = status;
- CodecConfiguration = configuration;
- }
- }
-
-
- #region IHasCameraPresets Members
-
- public event EventHandler CodecRoomPresetsListHasChanged;
-
- public List NearEndPresets { get; private set; }
-
- public List FarEndRoomPresets { get; private set; }
-
- public void CodecRoomPresetSelect(int preset)
- {
- Debug.Console(1, this, "Selecting Preset: {0}", preset);
- if (SelectedCamera is IAmFarEndCamera)
- SelectFarEndPreset(preset);
- else
- SendText(string.Format("xCommand RoomPreset Activate PresetId: {0}", preset));
- }
-
- public void CodecRoomPresetStore(int preset, string description)
- {
- SendText(string.Format("xCommand RoomPreset Store PresetId: {0} Description: \"{1}\" Type: All", preset, description));
- }
-
- #endregion
-
- public void SelectFarEndPreset(int preset)
- {
- SendText(string.Format("xCommand Call FarEndControl RoomPreset Activate CallId: {0} PresetId: {1}", GetCallId(), preset));
- }
-
-
- #region IHasExternalSourceSwitching Members
-
- ///
- /// Wheather the Cisco supports External Source Lists or not
- ///
- public bool ExternalSourceListEnabled
- {
- get;
- private set;
- }
-
- ///
- /// The name of the RoutingInputPort to which the upstream external switcher is connected
- ///
- public string ExternalSourceInputPort { get; private set; }
-
- public bool BrandingEnabled { get; private set; }
- private string _brandingUrl;
- private bool _sendMcUrl;
-
- ///
- /// Adds an external source to the Cisco
- ///
- ///
- ///
- ///
- public void AddExternalSource(string connectorId, string key, string name, eExternalSourceType type)
- {
- int id = 2;
- if (connectorId.ToLower() == "hdmiin3")
- {
- id = 3;
- }
- SendText(string.Format("xCommand UserInterface Presentation ExternalSource Add ConnectorId: {0} SourceIdentifier: \"{1}\" Name: \"{2}\" Type: {3}", id, key, name, type.ToString()));
- // SendText(string.Format("xCommand UserInterface Presentation ExternalSource State Set SourceIdentifier: \"{0}\" State: Ready", key));
- Debug.Console(2, this, "Adding ExternalSource {0} {1}", connectorId, name);
-
- }
-
-
- ///
- /// Sets the state of the External Source
- ///
- ///
- ///
- public void SetExternalSourceState(string key, eExternalSourceMode mode)
- {
- SendText(string.Format("xCommand UserInterface Presentation ExternalSource State Set SourceIdentifier: \"{0}\" State: {1}", key, mode.ToString()));
- }
- ///
- /// Clears all external sources on the codec
- ///
- public void ClearExternalSources()
- {
- SendText("xCommand UserInterface Presentation ExternalSource RemoveAll");
-
- }
-
- ///
- /// Sets the selected source of the available external sources on teh Touch10 UI
- ///
- public void SetSelectedSource(string key)
- {
- SendText(string.Format("xCommand UserInterface Presentation ExternalSource Select SourceIdentifier: {0}", key));
- _externalSourceChangeRequested = true;
- }
-
- ///
- /// Action that will run when the External Source is selected.
- ///
- public Action RunRouteAction { private get; set; }
-
-
-
-
-
-
- #endregion
- #region ExternalDevices
-
-
-
- #endregion
-
- #region IHasCameraOff Members
-
- public BoolFeedback CameraIsOffFeedback { get; private set; }
-
- public void CameraOff()
- {
- CameraMuteOn();
- }
-
- #endregion
-
- public BoolFeedback CameraIsMutedFeedback { get; private set; }
-
- ///
- /// Mutes the outgoing camera video
- ///
- public void CameraMuteOn()
- {
- SendText("xCommand Video Input MainVideo Mute");
- }
-
- ///
- /// Unmutes the outgoing camera video
- ///
- public void CameraMuteOff()
- {
- SendText("xCommand Video Input MainVideo Unmute");
- }
-
- ///
- /// Toggles the camera mute state
- ///
- public void CameraMuteToggle()
- {
- if (CameraIsMutedFeedback.BoolValue)
- CameraMuteOff();
- else
- CameraMuteOn();
- }
- }
-
-
- ///
- /// Represents a codec command that might need to have a friendly label applied for UI feedback purposes
- ///
- public class CodecCommandWithLabel
- {
- public string Command { get; set; }
- public string Label { get; set; }
-
- public CodecCommandWithLabel(string command, string label)
- {
- Command = command;
- Label = label;
- }
- }
-
- ///
- /// Tracks the initial sycnronization state of the codec when making a connection
- ///
- public class CodecSyncState : IKeyed
- {
- bool _InitialSyncComplete;
-
- public event EventHandler InitialSyncCompleted;
-
- public string Key { get; private set; }
-
- public bool InitialSyncComplete
- {
- get { return _InitialSyncComplete; }
- private set
- {
- if (value == true)
- {
- var handler = InitialSyncCompleted;
- if (handler != null)
- handler(this, new EventArgs());
- }
- _InitialSyncComplete = value;
- }
- }
-
- public bool LoginMessageWasReceived { get; private set; }
-
- public bool InitialStatusMessageWasReceived { get; private set; }
-
- public bool InitialConfigurationMessageWasReceived { get; private set; }
-
- public bool FeedbackWasRegistered { get; private set; }
-
- public CodecSyncState(string key)
- {
- Key = key;
- CodecDisconnected();
- }
-
- public void LoginMessageReceived()
- {
- LoginMessageWasReceived = true;
- Debug.Console(1, this, "Login Message Received.");
- CheckSyncStatus();
- }
-
- public void InitialStatusMessageReceived()
- {
- InitialStatusMessageWasReceived = true;
- Debug.Console(1, this, "Initial Codec Status Message Received.");
- CheckSyncStatus();
- }
-
- public void InitialConfigurationMessageReceived()
- {
- InitialConfigurationMessageWasReceived = true;
- Debug.Console(1, this, "Initial Codec Configuration Message Received.");
- CheckSyncStatus();
- }
-
- public void FeedbackRegistered()
- {
- FeedbackWasRegistered = true;
- Debug.Console(1, this, "Initial Codec Feedback Registration Successful.");
- CheckSyncStatus();
- }
-
- public void CodecDisconnected()
- {
- LoginMessageWasReceived = false;
- InitialConfigurationMessageWasReceived = false;
- InitialStatusMessageWasReceived = false;
- FeedbackWasRegistered = false;
- InitialSyncComplete = false;
- }
-
- void CheckSyncStatus()
- {
- if (LoginMessageWasReceived && InitialConfigurationMessageWasReceived && InitialStatusMessageWasReceived && FeedbackWasRegistered)
- {
- InitialSyncComplete = true;
- Debug.Console(1, this, "Initial Codec Sync Complete!");
- }
- else
- InitialSyncComplete = false;
- }
- }
-
- public class CiscoSparkCodecFactory : EssentialsDeviceFactory
- {
- public CiscoSparkCodecFactory()
- {
- TypeNames = new List() { "ciscospark", "ciscowebex", "ciscowebexpro", "ciscoroomkit", "ciscosparkpluscodec" };
- }
-
- public override EssentialsDevice BuildDevice(DeviceConfig dc)
- {
- Debug.Console(1, "Factory Attempting to create new Cisco Codec Device");
-
- var comm = CommFactory.CreateCommForDevice(dc);
- return new VideoCodec.Cisco.CiscoSparkCodec(dc, comm);
- }
- }
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Text.RegularExpressions;
+using Crestron.SimplSharp;
+using Crestron.SimplSharpPro.CrestronThread;
+using Crestron.SimplSharpPro.DeviceSupport;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+
+using PepperDash.Core;
+using PepperDash.Essentials.Core;
+using PepperDash.Essentials.Core.Bridges;
+using PepperDash.Essentials.Core.Config;
+using PepperDash.Essentials.Core.DeviceTypeInterfaces;
+using PepperDash.Essentials.Core.Routing;
+using PepperDash.Essentials.Devices.Common.Cameras;
+using PepperDash.Essentials.Devices.Common.Codec;
+using PepperDash.Essentials.Devices.Common.VideoCodec;
+using PepperDash.Essentials.Core.Queues;
+
+namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
+{
+ enum eCommandType { SessionStart, SessionEnd, Command, GetStatus, GetConfiguration };
+ public enum eExternalSourceType {camera, desktop, document_camera, mediaplayer, PC, whiteboard, other}
+ public enum eExternalSourceMode {Ready, NotReady, Hidden, Error}
+
+ public class CiscoSparkCodec : VideoCodecBase, IHasCallHistory, IHasCallFavorites, IHasDirectory,
+ IHasScheduleAwareness, IOccupancyStatusProvider, IHasCodecLayouts, IHasCodecSelfView,
+ ICommunicationMonitor, IRouting, IHasCodecCameras, IHasCameraAutoMode, IHasCodecRoomPresets,
+ IHasExternalSourceSwitching, IHasBranding, IHasCameraOff, IHasCameraMute, IHasDoNotDisturbMode,
+ IHasHalfWakeMode, IHasCallHold, IJoinCalls
+ {
+ private CiscoSparkCodecPropertiesConfig _config;
+
+ 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; }
+
+ private 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; }
+
+ public IntFeedback RingtoneVolumeFeedback { 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";
+
+ public IntFeedback PresentationSourceFeedback { get; private set; }
+
+ public BoolFeedback PresentationSendingLocalOnlyFeedback { get; private set; }
+
+ public BoolFeedback PresentationSendingLocalRemoteFeedback { get; private set; }
+
+ ///
+ /// Used to track the current connector used for the presentation source
+ ///
+ private int _presentationSource;
+
+ ///
+ /// Used to track the connector that is desired to be the current presentation source (until the command is send)
+ ///
+ private int _desiredPresentationSource;
+
+ private string _presentationSourceKey;
+
+ private bool _presentationLocalOnly;
+
+ private bool _presentationLocalRemote;
+
+ private string _phonebookMode = "Local"; // Default to Local
+
+ private uint _phonebookResultsLimit = 255; // Could be set later by config.
+
+ private CTimer _loginMessageReceivedTimer;
+ private CTimer _retryConnectionTimer;
+
+ // **___________________________________________________________________**
+ // Timers to be moved to the global system timer at a later point....
+ private CTimer BookingsRefreshTimer;
+ private 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());
+
+ _config = props;
+
+ // 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");
+
+ RingtoneVolumeFeedback = new IntFeedback(() => CodecConfiguration.Configuration.Audio.SoundsAndAlerts.RingVolume.Volume);
+
+ PresentationSourceFeedback = new IntFeedback(() => _presentationSource);
+ PresentationSendingLocalOnlyFeedback = new BoolFeedback(() => _presentationLocalOnly);
+ PresentationSendingLocalRemoteFeedback = new BoolFeedback(() => _presentationLocalRemote);
+
+ 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\n");
+ }
+
+ if (props.Sharing != null)
+ AutoShareContentWhileInCall = props.Sharing.AutoShareContentWhileInCall;
+
+ ShowSelfViewByDefault = props.ShowSelfViewByDefault;
+
+ DeviceManager.AddDevice(CommunicationMonitor);
+
+ _phonebookMode = props.PhonebookMode;
+
+ _syncState = new CodecSyncState(Key + "--Sync", this);
+
+ 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);
+ 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();
+ FarEndIsSharingContentFeedback.FireUpdate();
+ };
+ CodecStatus.Status.Conference.DoNotDisturb.ValueChangedAction = DoNotDisturbModeIsOnFeedback.FireUpdate;
+
+ CodecConfiguration.Configuration.Audio.SoundsAndAlerts.RingVolume.ValueChangedAction = RingtoneVolumeFeedback.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)
+ {
+ EnqueueCommand(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);
+
+ EnqueueCommand("xconfiguration userinterface custommessage: \"Scan the QR code with a mobile phone to get started\"");
+ EnqueueCommand("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;
+
+ EnqueueCommand(String.Format(
+ "xcommand userinterface branding fetch {1}type: branding url: {0}",
+ mcBridge.QrCodeUrl, checksum));
+ EnqueueCommand(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);
+
+ EnqueueCommand(String.Format("xcommand userinterface branding fetch type: branding url: {0}",
+ _brandingUrl));
+ EnqueueCommand(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);
+
+ PhonebookSyncState.InitialSyncCompleted += new EventHandler(PhonebookSyncState_InitialSyncCompleted);
+
+ return base.CustomActivate();
+ }
+
+ void PhonebookSyncState_InitialSyncCompleted(object sender, EventArgs e)
+ {
+ OnDirectoryResultReturned(DirectoryRoot);
+ }
+
+ #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)
+ {
+ // Check for camera config info first
+ if (_config.CameraInfo.Count > 0)
+ {
+ Debug.Console(0, this, "Reading codec cameraInfo from config properties.");
+ SetUpCameras(_config.CameraInfo);
+ }
+ else
+ {
+ Debug.Console(0, this, "No cameraInfo defined in video codec config. Attempting to get camera info from codec status data");
+ try
+ {
+ var cameraInfo = new List();
+
+ Debug.Console(0, this, "Codec reports {0} cameras", CodecStatus.Status.Cameras.Camera.Count);
+
+ foreach (var camera in CodecStatus.Status.Cameras.Camera)
+ {
+ Debug.Console(0, this,
+@"Camera id: {0}
+Name: {1}
+ConnectorID: {2}"
+, camera.id
+, camera.Manufacturer.Value
+, camera.Model.Value);
+
+ var id = Convert.ToUInt16(camera.id);
+ var info = new CameraInfo() { CameraNumber = id, Name = string.Format("{0} {1}", camera.Manufacturer.Value, camera.Model.Value), SourceId = camera.DetectedConnector.ConnectorId };
+ cameraInfo.Add(info);
+ }
+
+ Debug.Console(0, this, "Successfully got cameraInfo for {0} cameras from codec.", cameraInfo.Count);
+
+ SetUpCameras(cameraInfo);
+ }
+ catch (Exception ex)
+ {
+ Debug.Console(2, this, "Error generating camera info from codec status data: {0}", ex);
+ }
+ }
+
+ //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);
+
+ // Fire the ready event
+ SetIsReady();
+ }
+
+ 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}'", ComTextHelper.GetDebugText(args.Text));
+ }
+
+ if(args.Text.ToLower().Contains("xcommand"))
+ {
+ Debug.Console(1, this, "Received command echo response. Ignoring");
+ return;
+ }
+
+ 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("echo off");
+ SendText("xPreferences outputmode json");
+ break;
+ }
+ case "xpreferences outputmode json":
+ {
+ if (_syncState.JsonResponseModeSet)
+ return;
+
+ _syncState.JsonResponseModeMessageReceived();
+
+ if (!_syncState.InitialStatusMessageWasReceived)
+ SendText("xStatus");
+ break;
+ }
+ case "xfeedback register /event/calldisconnect":
+ {
+ _syncState.FeedbackRegistered();
+ break;
+ }
+ }
+ }
+ }
+
+ ///
+ /// Enqueues a command to be sent to the codec.
+ ///
+ ///
+ public void EnqueueCommand(string command)
+ {
+ _syncState.AddCommandToQueue(command);
+ }
+
+ ///
+ /// Appends the delimiter and send the command to the codec.
+ /// Should not be used for sending general commands to the codec. Use EnqueueCommand instead.
+ /// Should be used to get initial Status and Configuration as well as set up Feedback Registration
+ ///
+ ///
+ public void SendText(string command)
+ {
+ if (CommDebuggingIsOn)
+ Debug.Console(1, this, "Sending: '{0}'", ComTextHelper.GetDebugText(command + Delimiter));
+
+ 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 != null && conference.Presentation.LocalInstance == null)
+ {
+ // Handles an empty presentation object response
+ return;
+ }
+
+ if (conference.Presentation.LocalInstance.Count > 0)
+ {
+ if (!string.IsNullOrEmpty(conference.Presentation.LocalInstance[0].ghost))
+ {
+ _presentationSource = 0;
+ _presentationLocalOnly = false;
+ _presentationLocalRemote = false;
+ }
+ else if (conference.Presentation.LocalInstance[0].Source != null)
+ {
+ _presentationSource = conference.Presentation.LocalInstance[0].Source.IntValue;
+
+ // Check for any values in the SendingMode property
+ if (conference.Presentation.LocalInstance.Any((i) => !string.IsNullOrEmpty(i.SendingMode.Value)))
+ {
+ _presentationLocalOnly = conference.Presentation.LocalInstance.Any((i) => i.SendingMode.LocalOnly);
+ _presentationLocalRemote = conference.Presentation.LocalInstance.Any((i) => i.SendingMode.LocalRemote);
+ }
+ }
+
+ PresentationSourceFeedback.FireUpdate();
+ PresentationSendingLocalOnlyFeedback.FireUpdate();
+ PresentationSendingLocalRemoteFeedback.FireUpdate();
+ }
+
+ // 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);
+ tempActiveCall.IsOnHold = tempActiveCall.Status == eCodecCallStatus.OnHold;
+
+ 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(call.Duration != null)
+ {
+ if(!string.IsNullOrEmpty(call.Duration.Value))
+ {
+ tempActiveCall.Duration = call.Duration.DurationValue;
+ changeDetected = true;
+ }
+ }
+ if(call.PlacedOnHold != null)
+ {
+ tempActiveCall.IsOnHold = call.PlacedOnHold.BoolValue;
+ 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),
+ Duration = call.Duration.DurationValue,
+ IsOnHold = call.PlacedOnHold.BoolValue,
+ };
+
+ // 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);
+
+ if (ex is Newtonsoft.Json.JsonReaderException)
+ {
+ Debug.Console(1, this, "Received malformed response from codec.");
+
+ //Communication.Disconnect();
+
+ //Initialize();
+ }
+
+ }
+ }
+
+ ///
+ /// 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)
+ {
+ Debug.Console(2, this, "Directory result returned");
+ 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()
+ {
+ EnqueueCommand("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());
+
+ EnqueueCommand("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)
+ EnqueueCommand(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)
+ EnqueueCommand(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)
+ {
+ EnqueueCommand(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)
+ {
+ EnqueueCommand(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)
+ {
+ EnqueueCommand(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)
+ {
+ EnqueueCommand(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)
+ {
+ EnqueueCommand(string.Format("xCommand Call Disconnect CallId: {0}", activeCall.Id));
+ }
+
+ public override void EndAllCalls()
+ {
+ foreach (CodecActiveCallItem activeCall in ActiveCalls)
+ {
+ EnqueueCommand(string.Format("xCommand Call Disconnect CallId: {0}", activeCall.Id));
+ }
+ }
+
+ public override void AcceptCall(CodecActiveCallItem item)
+ {
+ EnqueueCommand("xCommand Call Accept");
+ }
+
+ public override void RejectCall(CodecActiveCallItem item)
+ {
+ EnqueueCommand("xCommand Call Reject");
+ }
+
+ #region IHasCallHold Members
+
+ public void HoldCall(CodecActiveCallItem activeCall)
+ {
+ EnqueueCommand(string.Format("xCommand Call Hold CallId: {0}", activeCall.Id));
+ }
+
+ public void ResumeCall(CodecActiveCallItem activeCall)
+ {
+ EnqueueCommand(string.Format("xCommand Call Resume CallId: {0}", activeCall.Id));
+ }
+
+ #endregion
+
+ #region IJoinCalls
+
+ public void JoinCall(CodecActiveCallItem activeCall)
+ {
+ EnqueueCommand(string.Format("xCommand Call Join CallId: {0}", activeCall.Id));
+ }
+
+ public void JoinAllCalls()
+ {
+ StringBuilder ids = new StringBuilder();
+
+ foreach (var call in ActiveCalls)
+ {
+ if (call.IsActiveCall)
+ {
+ ids.Append(string.Format(" CallId: {0}", call.Id));
+ }
+ }
+
+ if (ids.Length > 0)
+ {
+ EnqueueCommand(string.Format("xCommand Call Join {0}", ids.ToString()));
+ }
+ }
+
+ #endregion
+
+ ///
+ /// Sends tones to the last connected call
+ ///
+ ///
+ public override void SendDtmf(string s)
+ {
+ EnqueueCommand(string.Format("xCommand Call DTMFSend CallId: {0} DTMFString: \"{1}\"", GetCallId(), s));
+ }
+
+ ///
+ /// Sends tones to a specific call
+ ///
+ ///
+ ///
+ public override void SendDtmf(string s, CodecActiveCallItem activeCall)
+ {
+ EnqueueCommand(string.Format("xCommand Call DTMFSend CallId: {0} DTMFString: \"{1}\"", activeCall.Id, s));
+ }
+
+ public void SelectPresentationSource(int source)
+ {
+ _desiredPresentationSource = source;
+
+ StartSharing();
+ }
+
+ ///
+ /// Sets the ringtone volume level
+ ///
+ /// level from 0 - 100 in increments of 5
+ public void SetRingtoneVolume(int volume)
+ {
+ if (volume < 0 || volume > 100)
+ {
+ Debug.Console(0, this, "Cannot set ringtone volume to '{0}'. Value must be between 0 - 100", volume);
+ return;
+ }
+
+ if (volume % 5 != 0)
+ {
+ Debug.Console(0, this, "Cannot set ringtone volume to '{0}'. Value must be between 0 - 100 and a multiple of 5", volume);
+ return;
+ }
+
+ EnqueueCommand(string.Format("xConfiguration Audio SoundsAndAlerts RingVolume: {0}", volume));
+ }
+
+ ///
+ /// 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 (_desiredPresentationSource > 0)
+ EnqueueCommand(string.Format("xCommand Presentation Start PresentationSource: {0} SendingMode: {1}", _desiredPresentationSource, sendingMode));
+ }
+
+ ///
+ /// Stops sharing the current presentation
+ ///
+ public override void StopSharing()
+ {
+ _desiredPresentationSource = 0;
+
+ EnqueueCommand("xCommand Presentation Stop");
+ }
+
+
+
+ public override void PrivacyModeOn()
+ {
+ EnqueueCommand("xCommand Audio Microphones Mute");
+ }
+
+ public override void PrivacyModeOff()
+ {
+ EnqueueCommand("xCommand Audio Microphones Unmute");
+ }
+
+ public override void PrivacyModeToggle()
+ {
+ EnqueueCommand("xCommand Audio Microphones ToggleMute");
+ }
+
+ public override void MuteOff()
+ {
+ EnqueueCommand("xCommand Audio Volume Unmute");
+ }
+
+ public override void MuteOn()
+ {
+ EnqueueCommand("xCommand Audio Volume Mute");
+ }
+
+ public override void MuteToggle()
+ {
+ EnqueueCommand("xCommand Audio Volume ToggleMute");
+ }
+
+ ///
+ /// Increments the voluem
+ ///
+ ///
+ public override void VolumeUp(bool pressRelease)
+ {
+ EnqueueCommand("xCommand Audio Volume Increase");
+ }
+
+ ///
+ /// Decrements the volume
+ ///
+ ///
+ public override void VolumeDown(bool pressRelease)
+ {
+ EnqueueCommand("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);
+ EnqueueCommand(string.Format("xCommand Audio Volume Set Level: {0}", scaledLevel));
+ }
+
+ ///
+ /// Recalls the default volume on the codec
+ ///
+ public void VolumeSetToDefault()
+ {
+ EnqueueCommand("xCommand Audio Volume SetToDefault");
+ }
+
+ ///
+ /// Puts the codec in standby mode
+ ///
+ public override void StandbyActivate()
+ {
+ EnqueueCommand("xCommand Standby Activate");
+ }
+
+ ///
+ /// Wakes the codec from standby
+ ///
+ public override void StandbyDeactivate()
+ {
+ EnqueueCommand("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)
+ {
+ // Custom commands to codec
+ trilist.SetStringSigAction(joinMap.CommandToDevice.JoinNumber, (s) => this.EnqueueCommand(s));
+
+
+ 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());
+ }
+
+ // Ringtone volume
+ trilist.SetUShortSigAction(joinMap.RingtoneVolume.JoinNumber, (u) => SetRingtoneVolume(u));
+ RingtoneVolumeFeedback.LinkInputSig(trilist.UShortInput[joinMap.RingtoneVolume.JoinNumber]);
+
+ // Presentation Source
+ trilist.SetUShortSigAction(joinMap.PresentationSource.JoinNumber, (u) => SelectPresentationSource(u));
+ PresentationSourceFeedback.LinkInputSig(trilist.UShortInput[joinMap.PresentationSource.JoinNumber]);
+
+ PresentationSendingLocalOnlyFeedback.LinkInputSig(trilist.BooleanInput[joinMap.PresentationLocalOnly.JoinNumber]);
+ PresentationSendingLocalRemoteFeedback.LinkInputSig(trilist.BooleanInput[joinMap.PresentationLocalRemote.JoinNumber]);
+ }
+
+ ///
+ /// Reboots the codec
+ ///
+ public void Reboot()
+ {
+ EnqueueCommand("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()
+ {
+ EnqueueCommand("xCommand Video Selfview Set Mode: On");
+ }
+
+ ///
+ /// Turns off Selfview Mode
+ ///
+ public void SelfViewModeOff()
+ {
+ EnqueueCommand("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";
+
+ EnqueueCommand(string.Format("xCommand Video Selfview Set Mode: {0}", mode));
+ }
+
+ ///
+ /// Sets a specified position for the selfview PIP window
+ ///
+ ///
+ public void SelfviewPipPositionSet(CodecCommandWithLabel position)
+ {
+ EnqueueCommand(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)
+ {
+ EnqueueCommand(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";
+
+ EnqueueCommand(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)
+ {
+ EnqueueCommand(string.Format("xCommand CallHistory DeleteEntry CallHistoryId: {0} AcknowledgeConsecutiveDuplicates: True", entry.OccurrenceHistoryId));
+ }
+
+ #region IHasCameraSpeakerTrack
+
+ public void CameraAutoModeToggle()
+ {
+ if (!CameraAutoModeIsOnFeedback.BoolValue)
+ {
+ EnqueueCommand("xCommand Cameras SpeakerTrack Activate");
+ }
+ else
+ {
+ EnqueueCommand("xCommand Cameras SpeakerTrack Deactivate");
+ }
+ }
+
+ public void CameraAutoModeOn()
+ {
+ if (CameraIsOffFeedback.BoolValue)
+ {
+ CameraMuteOff();
+ }
+
+ EnqueueCommand("xCommand Cameras SpeakerTrack Activate");
+ }
+
+ public void CameraAutoModeOff()
+ {
+ if (CameraIsOffFeedback.BoolValue)
+ {
+ CameraMuteOff();
+ }
+
+ EnqueueCommand("xCommand Cameras SpeakerTrack Deactivate");
+ }
+
+ #endregion
+
+ ///
+ /// Builds the cameras List. Could later be modified to build from config data
+ ///
+ void SetUpCameras(List cameraInfo)
+ {
+ // Add the internal camera
+ Cameras = new List();
+
+ var camCount = CodecStatus.Status.Cameras.Camera.Count;
+
+ // Deal with the case of 1 or no reported cameras
+ if (camCount <= 1)
+ {
+ var internalCamera = new CiscoSparkCamera(Key + "-camera1", "Near End", this, 1);
+
+ if (camCount > 0)
+ {
+ // Try to get the capabilities from the codec
+ if (CodecStatus.Status.Cameras.Camera[0] != null && CodecStatus.Status.Cameras.Camera[0].Capabilities != null)
+ {
+ internalCamera.SetCapabilites(CodecStatus.Status.Cameras.Camera[0].Capabilities.Options.Value);
+ }
+ }
+
+ Cameras.Add(internalCamera);
+ //DeviceManager.AddDevice(internalCamera);
+ }
+ else
+ {
+ // Setup all the cameras
+ for (int i = 0; i < camCount; i++)
+ {
+ var cam = CodecStatus.Status.Cameras.Camera[i];
+
+ var id = (uint)i;
+ var name = string.Format("Camera {0}", id);
+
+ // Check for a config object that matches the camera number
+ var camInfo = cameraInfo.FirstOrDefault(c => c.CameraNumber == i + 1);
+ if (camInfo != null)
+ {
+ id = (uint)camInfo.SourceId;
+ name = camInfo.Name;
+ }
+
+ var key = string.Format("{0}-camera{1}", Key, id);
+ var camera = new CiscoSparkCamera(key, name, this, id);
+
+ if (cam.Capabilities != null)
+ {
+ camera.SetCapabilites(cam.Capabilities.Options.Value);
+ }
+
+ Cameras.Add(camera);
+ }
+ }
+
+ // 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);
+
+ 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 = Cameras[0]; ; // 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);
+
+ var ciscoCam = camera as CiscoSparkCamera;
+ if (ciscoCam != null)
+ {
+ EnqueueCommand(string.Format("xCommand Video Input SetMainVideoSource SourceId: {0}", ciscoCam.CameraId));
+ }
+ }
+
+ 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
+ {
+ var address = string.Empty;
+ if (CodecConfiguration.Configuration.Network.Count > 0)
+ {
+ if(!string.IsNullOrEmpty(CodecConfiguration.Configuration.Network[0].IPv4.Address.Value))
+ address = CodecConfiguration.Configuration.Network[0].IPv4.Address.Value;
+ }
+
+ if (string.IsNullOrEmpty(address) && CodecStatus.Status.Network.Count > 0)
+ {
+ if(!string.IsNullOrEmpty(CodecStatus.Status.Network[0].IPv4.Address.Value))
+ address = CodecStatus.Status.Network[0].IPv4.Address.Value;
+ }
+ return address;
+ }
+ }
+ 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 == null) return false;
+ return CodecConfiguration.Configuration.Conference.AutoAnswer.Mode.Value.ToLower() == "on";
+ }
+ }
+
+ 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
+ EnqueueCommand(string.Format("xCommand RoomPreset Activate PresetId: {0}", preset));
+ }
+
+ public void CodecRoomPresetStore(int preset, string description)
+ {
+ EnqueueCommand(string.Format("xCommand RoomPreset Store PresetId: {0} Description: \"{1}\" Type: All", preset, description));
+ }
+
+ #endregion
+
+ public void SelectFarEndPreset(int preset)
+ {
+ EnqueueCommand(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;
+ }
+ EnqueueCommand(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)
+ {
+ EnqueueCommand(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()
+ {
+ EnqueueCommand("xCommand UserInterface Presentation ExternalSource RemoveAll");
+
+ }
+
+ ///
+ /// Sets the selected source of the available external sources on teh Touch10 UI
+ ///
+ public void SetSelectedSource(string key)
+ {
+ EnqueueCommand(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()
+ {
+ EnqueueCommand("xCommand Video Input MainVideo Mute");
+ }
+
+ ///
+ /// Unmutes the outgoing camera video
+ ///
+ public void CameraMuteOff()
+ {
+ EnqueueCommand("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()
+ {
+ EnqueueCommand("xCommand Conference DoNotDisturb Activate");
+ }
+
+ public void DeactivateDoNotDisturbMode()
+ {
+ EnqueueCommand("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()
+ {
+ EnqueueCommand("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;
+ private readonly CiscoSparkCodec _parent;
+
+ public event EventHandler InitialSyncCompleted;
+ private readonly CrestronQueue _commandQueue;
+
+ 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 JsonResponseModeSet { 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, CiscoSparkCodec parent)
+ {
+ Key = key;
+ _parent = parent;
+ _commandQueue = new CrestronQueue(50);
+ CodecDisconnected();
+ }
+
+ private void ProcessQueuedCommands()
+ {
+ while (InitialSyncComplete)
+ {
+ var query = _commandQueue.Dequeue();
+
+ _parent.SendText(query);
+ }
+ }
+
+ public void AddCommandToQueue(string query)
+ {
+ _commandQueue.Enqueue(query);
+ }
+
+ public void LoginMessageReceived()
+ {
+ LoginMessageWasReceived = true;
+ Debug.Console(1, this, "Login Message Received.");
+ CheckSyncStatus();
+ }
+
+ public void JsonResponseModeMessageReceived()
+ {
+ JsonResponseModeSet = true;
+ Debug.Console(1, this, "Json Response Mode 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()
+ {
+ _commandQueue.Clear();
+ LoginMessageWasReceived = false;
+ JsonResponseModeSet = false;
+ InitialConfigurationMessageWasReceived = false;
+ InitialStatusMessageWasReceived = false;
+ FeedbackWasRegistered = false;
+ InitialSyncComplete = false;
+ }
+
+ void CheckSyncStatus()
+ {
+ if (LoginMessageWasReceived && JsonResponseModeSet && InitialConfigurationMessageWasReceived && InitialStatusMessageWasReceived && FeedbackWasRegistered)
+ {
+ InitialSyncComplete = true;
+ Debug.Console(1, this, "Initial Codec Sync Complete!");
+ Debug.Console(1, this, "{0} Command queued. Processing now...", _commandQueue.Count);
+
+ // Invoke a thread for the queue
+ CrestronInvoke.BeginInvoke((o) => {
+ ProcessQueuedCommands();
+ });
+ }
+ else
+ InitialSyncComplete = false;
+ }
+ }
+
+ public class CiscoSparkCodecFactory : EssentialsDeviceFactory
+ {
+ public CiscoSparkCodecFactory()
+ {
+ TypeNames = new List() { "ciscospark", "ciscowebex", "ciscowebexpro", "ciscoroomkit", "ciscosparkpluscodec" };
+ }
+
+ public override EssentialsDevice BuildDevice(DeviceConfig dc)
+ {
+ Debug.Console(1, "Factory Attempting to create new Cisco Codec Device");
+
+ var comm = CommFactory.CreateCommForDevice(dc);
+ return new VideoCodec.Cisco.CiscoSparkCodec(dc, comm);
+ }
+ }
+
}
\ No newline at end of file
diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoSparkCodecPropertiesConfig.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoSparkCodecPropertiesConfig.cs
index 1836bafb..7f36b0bf 100644
--- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoSparkCodecPropertiesConfig.cs
+++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoSparkCodecPropertiesConfig.cs
@@ -50,8 +50,16 @@ namespace PepperDash.Essentials.Devices.Common.Codec
public uint PhonebookResultsLimit { get; set; }
[JsonProperty("UiBranding")]
- public BrandingLogoProperties UiBranding { get; set; }
+ public BrandingLogoProperties UiBranding { get; set; }
+ [JsonProperty("cameraInfo")]
+ public List CameraInfo { get; set; }
+
+
+ public CiscoSparkCodecPropertiesConfig()
+ {
+ CameraInfo = new List();
+ }
}
public class SharingProperties
@@ -68,4 +76,14 @@ namespace PepperDash.Essentials.Devices.Common.Codec
[JsonProperty("brandingUrl")]
public string BrandingUrl { get; set; }
}
+
+ ///
+ /// Describes configuration information for the near end cameras
+ ///
+ public class CameraInfo
+ {
+ public int CameraNumber { get; set; }
+ public string Name { get; set; }
+ public int SourceId { get; set; }
+ }
}
\ No newline at end of file
diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/RoomPresets.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/RoomPresets.cs
index 1b456774..b6327d52 100644
--- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/RoomPresets.cs
+++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/RoomPresets.cs
@@ -26,6 +26,8 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec
void CodecRoomPresetSelect(int preset);
void CodecRoomPresetStore(int preset, string description);
+
+ void SelectFarEndPreset(int preset);
}
public static class RoomPresets
diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/xConfiguration.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/xConfiguration.cs
index 8ae9e643..05c07378 100644
--- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/xConfiguration.cs
+++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/xConfiguration.cs
@@ -112,16 +112,46 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
public string Value { get; set; }
}
- public class RingVolume
+ public class RingVolume : ValueProperty
{
public string valueSpaceRef { get; set; }
- public string Value { get; set; }
+
+ string _Value;
+
+ ///
+ /// Sets Value and triggers the action when set
+ ///
+ public string Value
+ {
+ get
+ {
+ return _Value;
+ }
+ set
+ {
+ _Value = value;
+ OnValueChanged();
+ }
+ }
+
+ public int Volume
+ {
+ get
+ {
+ return Int32.Parse(_Value);
+ }
+ }
}
public class SoundsAndAlerts
{
public RingTone RingTone { get; set; }
public RingVolume RingVolume { get; set; }
+
+ public SoundsAndAlerts()
+ {
+ RingVolume = new RingVolume();
+ }
}
public class Audio
@@ -131,6 +161,12 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
public Microphones Microphones { get; set; }
public Output Output { get; set; }
public SoundsAndAlerts SoundsAndAlerts { get; set; }
+
+
+ public Audio()
+ {
+ SoundsAndAlerts = new SoundsAndAlerts();
+ }
}
public class DefaultMode
@@ -340,6 +376,13 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
public Delay Delay { get; set; }
public Mode9 Mode { get; set; }
public Mute2 Mute { get; set; }
+
+ public AutoAnswer()
+ {
+ Mode = new Mode9();
+ Delay = new Delay();
+ Mute = new Mute2();
+ }
}
public class Protocol
@@ -440,6 +483,11 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
public MaxTotalTransmitCallRate MaxTotalTransmitCallRate { get; set; }
public MaxTransmitCallRate MaxTransmitCallRate { get; set; }
public MultiStream MultiStream { get; set; }
+
+ public Conference()
+ {
+ AutoAnswer = new AutoAnswer();
+ }
}
public class LoginName
@@ -690,6 +738,11 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
public Assignment Assignment { get; set; }
public Gateway Gateway { get; set; }
public SubnetMask SubnetMask { get; set; }
+
+ public IPv4()
+ {
+ Address = new Address4();
+ }
}
public class Address5
@@ -841,6 +894,11 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
public RemoteAccess RemoteAccess { get; set; }
public Speed Speed { get; set; }
public VLAN VLAN { get; set; }
+
+ public Network()
+ {
+ IPv4 = new IPv4();
+ }
}
public class Mode19
@@ -1797,11 +1855,23 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
public UserInterface UserInterface { get; set; }
public UserManagement UserManagement { get; set; }
public Video2 Video { get; set; }
+
+ public Configuration()
+ {
+ Audio = new Audio();
+ Conference = new Conference();
+ Network = new List();
+ }
}
public class RootObject
{
public Configuration Configuration { get; set; }
+
+ public RootObject()
+ {
+ Configuration = new Configuration();
+ }
}
}
}
\ No newline at end of file
diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/xStatus.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/xStatus.cs
index 29b9f260..5bde30ff 100644
--- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/xStatus.cs
+++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/xStatus.cs
@@ -12,28 +12,28 @@ using PepperDash.Essentials.Devices.Common.VideoCodec.CiscoCodec;
namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
{
+ // Helper Classes for Proerties
+ public abstract class ValueProperty
+ {
+ ///
+ /// Triggered when Value is set
+ ///
+ public Action ValueChangedAction { get; set; }
+
+ protected void OnValueChanged()
+ {
+ var a = ValueChangedAction;
+ if (a != null)
+ a();
+ }
+
+ }
+
///
/// This class exists to capture serialized data sent back by a Cisco codec in JSON output mode
///
public class CiscoCodecStatus
{
- // Helper Classes for Proerties
- public abstract class ValueProperty
- {
- ///
- /// Triggered when Value is set
- ///
- public Action ValueChangedAction { get; set; }
-
- protected void OnValueChanged()
- {
- var a = ValueChangedAction;
- if (a != null)
- a();
- }
-
- }
-
public class ConnectionStatus
{
@@ -262,11 +262,30 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
public string Value { get; set; }
}
+ public class DectectedConnector
+ {
+ public string Value { get; set; }
+
+ public int ConnectorId
+ {
+ get
+ {
+ if(!string.IsNullOrEmpty(Value))
+ {
+ return Convert.ToUInt16(Value);
+ }
+ else
+ return -1;
+ }
+ }
+ }
+
public class Camera
{
public string id { get; set; }
public Capabilities Capabilities { get; set; }
public Connected Connected { get; set; }
+ public DectectedConnector DetectedConnector { get; set; }
public Flip Flip { get; set; }
public HardwareID HardwareID { get; set; }
public MacAddress MacAddress { get; set; }
@@ -275,6 +294,13 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
public Position Position { get; set; }
public SerialNumber SerialNumber { get; set; }
public SoftwareID SoftwareID { get; set; }
+
+ public Camera()
+ {
+ Manufacturer = new Manufacturer();
+ Model = new Model();
+ DetectedConnector = new DectectedConnector();
+ }
}
public class Availability : ValueProperty
@@ -298,11 +324,34 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
}
}
+ public class CallStatus : ValueProperty
+ {
+ string _Value;
+ public bool BoolValue { get; private set; }
+
+
+ public string Value
+ {
+ get
+ {
+ return _Value;
+ }
+ set
+ {
+ // If the incoming value is "Active" it sets the BoolValue true, otherwise sets it false
+ _Value = value;
+ BoolValue = value == "Connected";
+ OnValueChanged();
+ }
+ }
+ }
+
public class Status2 : ValueProperty
{
string _Value;
public bool BoolValue { get; private set; }
+
public string Value
{
get
@@ -558,9 +607,47 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
}
}
- public class SendingMode
+ public class SendingMode : ValueProperty
{
- public string Value { get; set; }
+ string _Value;
+
+ ///
+ /// Sets Value and triggers the action when set
+ ///
+ public string Value
+ {
+ get
+ {
+ return _Value;
+ }
+ set
+ {
+ _Value = value;
+ OnValueChanged();
+ }
+ }
+
+ public bool LocalOnly
+ {
+ get
+ {
+ if(string.IsNullOrEmpty(_Value))
+ return false;
+
+ return _Value.ToLower() == "localonly";
+ }
+ }
+
+ public bool LocalRemote
+ {
+ get
+ {
+ if(string.IsNullOrEmpty(_Value))
+ return false;
+
+ return _Value.ToLower() == "localremote";
+ }
+ }
}
public class LocalInstance
@@ -573,6 +660,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
public LocalInstance()
{
Source = new Source2();
+ SendingMode = new SendingMode();
}
}
@@ -881,6 +969,11 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
public Address4 Address { get; set; }
public Gateway Gateway { get; set; }
public SubnetMask SubnetMask { get; set; }
+
+ public IPv4()
+ {
+ Address = new Address4();
+ }
}
public class Address5
@@ -923,6 +1016,11 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
public IPv4 IPv4 { get; set; }
public IPv6 IPv6 { get; set; }
public VLAN VLAN { get; set; }
+
+ public Network()
+ {
+ IPv4 = new IPv4();
+ }
}
public class CurrentAddress
@@ -1949,9 +2047,30 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
public string Value { get; set; }
}
- public class Duration
+ public class Duration : ValueProperty
{
- public string Value { get; set; }
+ private string _Value;
+
+ public string Value
+ {
+ get
+ {
+ return _Value;
+ }
+ set
+ {
+ _Value = value;
+ OnValueChanged();
+ }
+ }
+
+ public TimeSpan DurationValue
+ {
+ get
+ {
+ return new TimeSpan(0, 0, Int32.Parse(_Value));
+ }
+ }
}
public class FacilityServiceId
@@ -1964,9 +2083,19 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
public string Value { get; set; }
}
- public class PlacedOnHold
+ public class PlacedOnHold : ValueProperty
{
- public string Value { get; set; }
+ public bool BoolValue { get; private set; }
+
+ public string Value
+ {
+ set
+ {
+ // If the incoming value is "True" it sets the BoolValue true, otherwise sets it false
+ BoolValue = value == "True";
+ OnValueChanged();
+ }
+ }
}
public class Protocol
@@ -2007,13 +2136,14 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
public Protocol Protocol { get; set; }
public ReceiveCallRate ReceiveCallRate { get; set; }
public RemoteNumber RemoteNumber { get; set; }
- public Status2 Status { get; set; }
+ public CallStatus Status { get; set; }
public TransmitCallRate TransmitCallRate { get; set; }
public Call()
{
CallType = new CallType();
- Status = new Status2();
+ Status = new CallStatus();
+ Duration = new Duration();
}
}
@@ -2114,6 +2244,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
SystemUnit = new SystemUnit();
Video = new Video();
Conference = new Conference2();
+ Network = new List();
}
}
diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IJoinCalls.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IJoinCalls.cs
new file mode 100644
index 00000000..b84db1e9
--- /dev/null
+++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IJoinCalls.cs
@@ -0,0 +1,16 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Crestron.SimplSharp;
+
+using PepperDash.Essentials.Devices.Common.Codec;
+
+namespace PepperDash.Essentials.Devices.Common.VideoCodec
+{
+ public interface IJoinCalls
+ {
+ void JoinCall(CodecActiveCallItem activeCall);
+ void JoinAllCalls();
+ }
+}
\ No newline at end of file
diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/MockVC/MockVC.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/MockVC/MockVC.cs
index ac7514e0..1f79d936 100644
--- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/MockVC/MockVC.cs
+++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/MockVC/MockVC.cs
@@ -764,6 +764,11 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec
SetConfig(Config);
}
+ public void SelectFarEndPreset(int i)
+ {
+ Debug.Console(1, this, "Selecting Far End Preset: {0}", i);
+ }
+
#endregion
protected override void CustomSetConfig(DeviceConfig config)
diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/VideoCodecBase.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/VideoCodecBase.cs
index 438d6c56..593472e9 100644
--- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/VideoCodecBase.cs
+++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/VideoCodecBase.cs
@@ -152,6 +152,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec
public abstract void AcceptCall(CodecActiveCallItem call);
public abstract void RejectCall(CodecActiveCallItem call);
public abstract void SendDtmf(string s);
+ public virtual void SendDtmf(string s, CodecActiveCallItem call) { }
#endregion
@@ -259,10 +260,12 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec
///
public virtual void ListCalls()
{
+ Debug.Console(1, this, "Active Calls:");
+
var sb = new StringBuilder();
foreach (var c in ActiveCalls)
{
- sb.AppendFormat("{0} {1} -- {2} {3}\n", c.Id, c.Number, c.Name, c.Status);
+ sb.AppendFormat("id: {0} number: {1} -- name: {2} status: {3} onHold: {4}\r\n", c.Id, c.Number, c.Name, c.Status, c.IsOnHold);
}
Debug.Console(1, this, "\n{0}\n", sb.ToString());
}
@@ -328,15 +331,22 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec
LinkVideoCodecVolumeToApi(trilist, joinMap);
+ LinkVideoCodecInfoToApi(trilist, joinMap);
+
+ // Register for this event to link any functions that require the codec to be ready first
+ codec.IsReadyChange += (o, a) =>
+ {
+ if (codec is IHasCodecCameras)
+ {
+ LinkVideoCodecCameraToApi(codec as IHasCodecCameras, trilist, joinMap);
+ }
+ };
+
if (codec is ICommunicationMonitor)
{
LinkVideoCodecCommMonitorToApi(codec as ICommunicationMonitor, trilist, joinMap);
}
- if (codec is IHasCodecCameras)
- {
- LinkVideoCodecCameraToApi(codec as IHasCodecCameras, trilist, joinMap);
- }
if (codec is IHasCodecSelfView)
{
@@ -390,6 +400,11 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec
LinkVideoCodecPhoneToApi(codec as IHasPhoneDialing, trilist, joinMap);
}
+ if (codec is IHasCallHistory)
+ {
+ LinkVideoCodecCallHistoryToApi(codec as IHasCallHistory, trilist, joinMap);
+ }
+
trilist.OnlineStatusChange += (device, args) =>
{
if (!args.DeviceOnLine) return;
@@ -436,6 +451,11 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec
(codec as IHasPhoneDialing).PhoneOffHookFeedback.FireUpdate();
}
+ if (codec is IHasCallHistory)
+ {
+ UpdateCallHistory((codec as IHasCallHistory), trilist, joinMap);
+ }
+
SharingContentIsOnFeedback.FireUpdate();
trilist.SetBool(joinMap.HookState.JoinNumber, IsInCall);
@@ -444,6 +464,31 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec
};
}
+ private void LinkVideoCodecInfoToApi(BasicTriList trilist, VideoCodecControllerJoinMap joinMap)
+ {
+ trilist.SetBool(joinMap.MultiSiteOptionIsEnabled.JoinNumber, this.CodecInfo.MultiSiteOptionIsEnabled);
+ trilist.SetBool(joinMap.AutoAnswerEnabled.JoinNumber, this.CodecInfo.AutoAnswerEnabled);
+ trilist.SetString(joinMap.DeviceIpAddresss.JoinNumber, this.CodecInfo.IpAddress);
+ trilist.SetString(joinMap.SipPhoneNumber.JoinNumber, this.CodecInfo.SipPhoneNumber);
+ trilist.SetString(joinMap.E164Alias.JoinNumber, this.CodecInfo.E164Alias);
+ trilist.SetString(joinMap.H323Id.JoinNumber, this.CodecInfo.H323Id);
+ trilist.SetString(joinMap.SipUri.JoinNumber, this.CodecInfo.SipUri);
+
+ trilist.OnlineStatusChange += (o, a) =>
+ {
+ if (a.DeviceOnLine)
+ {
+ trilist.SetBool(joinMap.MultiSiteOptionIsEnabled.JoinNumber, this.CodecInfo.MultiSiteOptionIsEnabled);
+ trilist.SetBool(joinMap.AutoAnswerEnabled.JoinNumber, this.CodecInfo.AutoAnswerEnabled);
+ trilist.SetString(joinMap.DeviceIpAddresss.JoinNumber, this.CodecInfo.IpAddress);
+ trilist.SetString(joinMap.SipPhoneNumber.JoinNumber, this.CodecInfo.SipPhoneNumber);
+ trilist.SetString(joinMap.E164Alias.JoinNumber, this.CodecInfo.E164Alias);
+ trilist.SetString(joinMap.H323Id.JoinNumber, this.CodecInfo.H323Id);
+ trilist.SetString(joinMap.SipUri.JoinNumber, this.CodecInfo.SipUri);
+ }
+ };
+ }
+
private void LinkVideoCodecPhoneToApi(IHasPhoneDialing codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap)
{
codec.PhoneOffHookFeedback.LinkInputSig(trilist.BooleanInput[joinMap.PhoneHookState.JoinNumber]);
@@ -645,37 +690,37 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec
{
if (meetingIndex >= maxParticipants * offset) break;
- Debug.Console(2, this,
-@"Updating Participant on xsig:
-Name: {0} (s{9})
-AudioMute: {1} (d{10})
-VideoMute: {2} (d{11})
-CanMuteVideo: {3} (d{12})
-CanUMuteVideo: {4} (d{13})
-IsHost: {5} (d{14})
-HandIsRaised: {6} (d{15})
-IsPinned: {7} (d{16})
-ScreenIndexIsPinnedTo: {8} (a{17})
-",
- participant.Name,
- participant.AudioMuteFb,
- participant.VideoMuteFb,
- participant.CanMuteVideo,
- participant.CanUnmuteVideo,
- participant.IsHost,
- participant.HandIsRaisedFb,
- participant.IsPinnedFb,
- participant.ScreenIndexIsPinnedToFb,
- stringIndex + 1,
- digitalIndex + 1,
- digitalIndex + 2,
- digitalIndex + 3,
- digitalIndex + 4,
- digitalIndex + 5,
- digitalIndex + 6,
- digitalIndex + 7,
- analogIndex + 1
- );
+// Debug.Console(2, this,
+//@"Updating Participant on xsig:
+//Name: {0} (s{9})
+//AudioMute: {1} (d{10})
+//VideoMute: {2} (d{11})
+//CanMuteVideo: {3} (d{12})
+//CanUMuteVideo: {4} (d{13})
+//IsHost: {5} (d{14})
+//HandIsRaised: {6} (d{15})
+//IsPinned: {7} (d{16})
+//ScreenIndexIsPinnedTo: {8} (a{17})
+//",
+// participant.Name,
+// participant.AudioMuteFb,
+// participant.VideoMuteFb,
+// participant.CanMuteVideo,
+// participant.CanUnmuteVideo,
+// participant.IsHost,
+// participant.HandIsRaisedFb,
+// participant.IsPinnedFb,
+// participant.ScreenIndexIsPinnedToFb,
+// stringIndex + 1,
+// digitalIndex + 1,
+// digitalIndex + 2,
+// digitalIndex + 3,
+// digitalIndex + 4,
+// digitalIndex + 5,
+// digitalIndex + 6,
+// digitalIndex + 7,
+// analogIndex + 1
+// );
//digitals
@@ -945,18 +990,33 @@ ScreenIndexIsPinnedTo: {8} (a{17})
private void LinkVideoCodecDirectoryToApi(IHasDirectory codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap)
{
codec.CurrentDirectoryResultIsNotDirectoryRoot.LinkComplementInputSig(
- trilist.BooleanInput[joinMap.DirectoryIsRoot.JoinNumber]);
-
- trilist.SetSigFalseAction(joinMap.DirectoryRoot.JoinNumber, codec.SetCurrentDirectoryToRoot);
+ trilist.BooleanInput[joinMap.DirectoryIsRoot.JoinNumber]);
trilist.SetStringSigAction(joinMap.DirectorySearchString.JoinNumber, codec.SearchDirectory);
- trilist.SetUShortSigAction(joinMap.DirectorySelectRow.JoinNumber, (i) => SelectDirectoryEntry(codec, i));
+ trilist.SetUShortSigAction(joinMap.DirectorySelectRow.JoinNumber, (i) => SelectDirectoryEntry(codec, i, trilist, joinMap));
+
+ // Report feedback for number of contact methods for selected contact
trilist.SetSigFalseAction(joinMap.DirectoryRoot.JoinNumber, codec.SetCurrentDirectoryToRoot);
trilist.SetSigFalseAction(joinMap.DirectoryFolderBack.JoinNumber, codec.GetDirectoryParentFolderContents);
+ if (codec.DirectoryRoot != null)
+ {
+ trilist.SetUshort(joinMap.DirectoryRowCount.JoinNumber, (ushort)codec.DirectoryRoot.CurrentDirectoryResults.Count);
+
+ var clearBytes = XSigHelpers.ClearOutputs();
+
+ trilist.SetString(joinMap.DirectoryEntries.JoinNumber,
+ Encoding.GetEncoding(XSigEncoding).GetString(clearBytes, 0, clearBytes.Length));
+ var directoryXSig = UpdateDirectoryXSig(codec.DirectoryRoot, !codec.CurrentDirectoryResultIsNotDirectoryRoot.BoolValue);
+
+ Debug.Console(2, this, "Directory XSig Length: {0}", directoryXSig.Length);
+
+ trilist.SetString(joinMap.DirectoryEntries.JoinNumber, directoryXSig);
+ }
+
codec.DirectoryResultReturned += (sender, args) =>
{
trilist.SetUshort(joinMap.DirectoryRowCount.JoinNumber, (ushort)args.Directory.CurrentDirectoryResults.Count);
@@ -967,6 +1027,8 @@ ScreenIndexIsPinnedTo: {8} (a{17})
Encoding.GetEncoding(XSigEncoding).GetString(clearBytes, 0, clearBytes.Length));
var directoryXSig = UpdateDirectoryXSig(args.Directory, !codec.CurrentDirectoryResultIsNotDirectoryRoot.BoolValue);
+ Debug.Console(2, this, "Directory XSig Length: {0}", directoryXSig.Length);
+
trilist.SetString(joinMap.DirectoryEntries.JoinNumber, directoryXSig);
};
@@ -981,40 +1043,154 @@ ScreenIndexIsPinnedTo: {8} (a{17})
};
}
- private void SelectDirectoryEntry(IHasDirectory codec, ushort i)
+
+
+ private void SelectDirectoryEntry(IHasDirectory codec, ushort i, BasicTriList trilist, VideoCodecControllerJoinMap joinMap)
{
- var entry = codec.CurrentDirectoryResult.CurrentDirectoryResults[i - 1];
+ if (i < 1 || i > codec.CurrentDirectoryResult.CurrentDirectoryResults.Count) return;
- if (entry is DirectoryFolder)
+ _selectedDirectoryItem = codec.CurrentDirectoryResult.CurrentDirectoryResults[i - 1];
+
+
+ if (_selectedDirectoryItem is DirectoryFolder)
{
- codec.GetDirectoryFolderContents(entry.FolderId);
- return;
+ codec.GetDirectoryFolderContents(_selectedDirectoryItem.FolderId);
+ trilist.SetUshort(joinMap.SelectedContactMethodCount.JoinNumber, 0);
+ trilist.SetString(joinMap.DirectorySelectedFolderName.JoinNumber, _selectedDirectoryItem.Name);
+ trilist.SetString(joinMap.DirectoryEntrySelectedName.JoinNumber, string.Empty);
+ trilist.ClearUShortSigAction(joinMap.SelectContactMethod.JoinNumber);
+ trilist.ClearBoolSigAction(joinMap.DirectoryDialSelectedLine.JoinNumber);
+ trilist.ClearBoolSigAction(joinMap.DirectoryDialSelectedContactMethod.JoinNumber);
+ return;
}
- var dialableEntry = entry as IInvitableContact;
+ // not a folder. Clear this value
+ trilist.SetString(joinMap.DirectorySelectedFolderName.JoinNumber, string.Empty);
- if (dialableEntry != null)
- {
- Dial(dialableEntry);
- return;
- }
+ var selectedContact = _selectedDirectoryItem as DirectoryContact;
+ if (selectedContact != null)
+ {
+ trilist.SetString(joinMap.DirectoryEntrySelectedName.JoinNumber, selectedContact.Name);
+
+ }
- var entryToDial = entry as DirectoryContact;
+ // Allow auto dial of selected line. Always dials first contact method
+ if (!trilist.GetBool(joinMap.DirectoryDisableAutoDialSelectedLine.JoinNumber))
+ {
+ var invitableEntry = _selectedDirectoryItem as IInvitableContact;
- if (entryToDial == null) return;
+ if (invitableEntry != null)
+ {
+ Dial(invitableEntry);
+ return;
+ }
- Dial(entryToDial.ContactMethods[0].Number);
+ var entryToDial = _selectedDirectoryItem as DirectoryContact;
+
+ trilist.SetString(joinMap.DirectoryEntrySelectedNumber.JoinNumber, selectedContact.ContactMethods[0].Number);
+
+ if (entryToDial == null) return;
+
+ Dial(entryToDial.ContactMethods[0].Number);
+ return;
+ }
+ else
+ {
+ // If auto dial is disabled...
+ var entryToDial = _selectedDirectoryItem as DirectoryContact;
+
+ if (entryToDial == null)
+ {
+ // Clear out values and actions from last selected item
+ trilist.SetUshort(joinMap.SelectedContactMethodCount.JoinNumber, 0);
+ trilist.SetString(joinMap.DirectoryEntrySelectedName.JoinNumber, string.Empty);
+ trilist.ClearUShortSigAction(joinMap.SelectContactMethod.JoinNumber);
+ trilist.ClearBoolSigAction(joinMap.DirectoryDialSelectedLine.JoinNumber);
+ trilist.ClearBoolSigAction(joinMap.DirectoryDialSelectedContactMethod.JoinNumber);
+ return;
+ }
+
+ trilist.SetUshort(joinMap.SelectedContactMethodCount.JoinNumber, (ushort)entryToDial.ContactMethods.Count);
+
+ // Update the action to dial the selected contact method
+ trilist.SetUShortSigAction(joinMap.SelectContactMethod.JoinNumber, (u) =>
+ {
+ if (u < 1 || u > entryToDial.ContactMethods.Count) return;
+
+ trilist.SetSigFalseAction(joinMap.DirectoryDialSelectedContactMethod.JoinNumber, () => Dial(entryToDial.ContactMethods[u - 1].Number));
+ });
+
+ // Sets DirectoryDialSelectedLine join action to dial first contact method
+ trilist.SetSigFalseAction(joinMap.DirectoryDialSelectedLine.JoinNumber, () => Dial(entryToDial.ContactMethods[0].Number));
+
+ var clearBytes = XSigHelpers.ClearOutputs();
+
+ trilist.SetString(joinMap.ContactMethods.JoinNumber,
+ Encoding.GetEncoding(XSigEncoding).GetString(clearBytes, 0, clearBytes.Length));
+ var contactMethodsXSig = UpdateContactMethodsXSig(entryToDial);
+
+ trilist.SetString(joinMap.ContactMethods.JoinNumber, contactMethodsXSig);
+ }
}
+ ///
+ /// Generates the XSig data representing the available contact methods for the selected DirectoryContact
+ ///
+ ///
+ ///
+ private string UpdateContactMethodsXSig(DirectoryContact contact)
+ {
+ const int maxMethods = 10;
+ const int maxStrings = 3;
+ const int offset = maxStrings;
+ var stringIndex = 0;
+ var arrayIndex = 0;
+ // Create a new token array and set the size to the number of methods times the total number of signals
+ var tokenArray = new XSigToken[maxMethods * offset];
+
+ Debug.Console(2, this, "Creating XSIG token array with size {0}", maxMethods * offset);
+
+ // TODO: Add code to generate XSig data
+ foreach (var method in contact.ContactMethods)
+ {
+ if (arrayIndex >= maxMethods * offset)
+ break;
+
+ //serials
+ tokenArray[arrayIndex + 1] = new XSigSerialToken(stringIndex + 1, method.Number);
+ tokenArray[arrayIndex + 2] = new XSigSerialToken(stringIndex + 2, method.ContactMethodId.ToString());
+ tokenArray[arrayIndex + 3] = new XSigSerialToken(stringIndex + 3, method.Device.ToString());
+
+ arrayIndex += offset;
+ stringIndex += maxStrings;
+ }
+
+ while (arrayIndex < maxMethods)
+ {
+ tokenArray[arrayIndex + 1] = new XSigSerialToken(stringIndex + 1, String.Empty);
+ tokenArray[arrayIndex + 2] = new XSigSerialToken(stringIndex + 2, String.Empty);
+ tokenArray[arrayIndex + 3] = new XSigSerialToken(stringIndex + 3, String.Empty);
+
+ arrayIndex += offset;
+ stringIndex += maxStrings;
+ }
+
+ return GetXSigString(tokenArray);
+ }
+
private string UpdateDirectoryXSig(CodecDirectory directory, bool isRoot)
{
var contactIndex = 1;
var tokenArray = new XSigToken[directory.CurrentDirectoryResults.Count];
+ Debug.Console(2, this, "Is root {0} Directory Count: {1}", isRoot, directory.CurrentDirectoryResults.Count);
+
foreach (var entry in directory.CurrentDirectoryResults)
{
var arrayIndex = contactIndex - 1;
+ Debug.Console(2, this, "Entry Name: {0}, Folder ID: {1}", entry.Name, entry.FolderId);
+
if (entry is DirectoryFolder && entry.ParentFolderId == "root")
{
tokenArray[arrayIndex] = new XSigSerialToken(contactIndex, String.Format("[+] {0}", entry.Name));
@@ -1024,8 +1200,6 @@ ScreenIndexIsPinnedTo: {8} (a{17})
continue;
}
- if (isRoot && String.IsNullOrEmpty(entry.FolderId)) continue;
-
tokenArray[arrayIndex] = new XSigSerialToken(contactIndex, entry.Name);
contactIndex++;
@@ -1039,8 +1213,34 @@ ScreenIndexIsPinnedTo: {8} (a{17})
trilist.SetSigFalseAction(joinMap.ManualDial.JoinNumber,
() => Dial(trilist.StringOutput[joinMap.CurrentDialString.JoinNumber].StringValue));
- //End All calls for now
- trilist.SetSigFalseAction(joinMap.EndCall.JoinNumber, EndAllCalls);
+ //End All calls
+ trilist.SetSigFalseAction(joinMap.EndAllCalls.JoinNumber, EndAllCalls);
+
+ //End a specific call, specified by index. Maximum 8 calls supported
+ for (int i = 0; i < joinMap.EndCallStart.JoinSpan; i++)
+ {
+ var callIndex = i;
+
+ trilist.SetSigFalseAction((uint)(joinMap.EndCallStart.JoinNumber + i), () =>
+ {
+
+ if (callIndex < 0 || callIndex >= ActiveCalls.Count)
+ {
+ Debug.Console(2, this, "Cannot end call. No call found at index: {0}", callIndex);
+ return;
+ }
+
+ var call = ActiveCalls[callIndex];
+ if (call != null)
+ {
+ EndCall(call);
+ }
+ else
+ {
+ Debug.Console(0, this, "[End Call] Unable to find call at index '{0}'", i);
+ }
+ });
+ }
trilist.SetBool(joinMap.HookState.JoinNumber, IsInCall);
@@ -1052,30 +1252,112 @@ ScreenIndexIsPinnedTo: {8} (a{17})
Debug.Console(1, this, "Call is incoming: {0}", args.CallItem.Direction == eCodecCallDirection.Incoming);
trilist.SetBool(joinMap.IncomingCall.JoinNumber, args.CallItem.Direction == eCodecCallDirection.Incoming && args.CallItem.Status == eCodecCallStatus.Ringing);
- if (args.CallItem.Direction == eCodecCallDirection.Incoming)
- {
- trilist.SetSigFalseAction(joinMap.IncomingAnswer.JoinNumber, () => AcceptCall(args.CallItem));
- trilist.SetSigFalseAction(joinMap.IncomingReject.JoinNumber, () => RejectCall(args.CallItem));
- }
+ if (args.CallItem.Direction == eCodecCallDirection.Incoming)
+ {
+ trilist.SetSigFalseAction(joinMap.IncomingAnswer.JoinNumber, () => AcceptCall(args.CallItem));
+ trilist.SetSigFalseAction(joinMap.IncomingReject.JoinNumber, () => RejectCall(args.CallItem));
+ trilist.SetString(joinMap.IncomingCallName.JoinNumber, args.CallItem.Name);
+ trilist.SetString(joinMap.IncomingCallNumber.JoinNumber, args.CallItem.Number);
+ }
+ else
+ {
+ trilist.SetString(joinMap.IncomingCallName.JoinNumber, string.Empty);
+ trilist.SetString(joinMap.IncomingCallNumber.JoinNumber, string.Empty);
+ }
+
trilist.SetString(joinMap.CurrentCallData.JoinNumber, UpdateCallStatusXSig());
+
+ trilist.SetUshort(joinMap.ConnectedCallCount.JoinNumber, (ushort)ActiveCalls.Count);
};
- trilist.OnlineStatusChange += (device, args) =>
- {
- if (!args.DeviceOnLine) return;
+ var joinCodec = this as IJoinCalls;
+ if (joinCodec != null)
+ {
+ trilist.SetSigFalseAction(joinMap.JoinAllCalls.JoinNumber, () => joinCodec.JoinAllCalls());
- // TODO [ ] Issue #868
- trilist.SetString(joinMap.CurrentCallData.JoinNumber, "\xFC");
- UpdateCallStatusXSig();
- };
+ for (int i = 0; i < joinMap.JoinCallStart.JoinSpan; i++)
+ {
+ trilist.SetSigFalseAction((uint)(joinMap.JoinCallStart.JoinNumber + i), () =>
+ {
+ var call = ActiveCalls[i];
+ if (call != null)
+ {
+ joinCodec.JoinCall(call);
+ }
+ else
+ {
+ Debug.Console(0, this, "[Join Call] Unable to find call at index '{0}'", i);
+ }
+ });
+ }
+ }
+
+ var holdCodec = this as IHasCallHold;
+ if (holdCodec != null)
+ {
+ trilist.SetSigFalseAction(joinMap.HoldAllCalls.JoinNumber, () =>
+ {
+ foreach (var call in ActiveCalls)
+ {
+ holdCodec.HoldCall(call);
+ }
+ });
+
+ for (int i = 0; i < joinMap.HoldCallsStart.JoinSpan; i++)
+ {
+ var index = i;
+
+ trilist.SetSigFalseAction((uint)(joinMap.HoldCallsStart.JoinNumber + index), () =>
+ {
+ if (index < 0 || index >= ActiveCalls.Count) return;
+
+ var call = ActiveCalls[index];
+ if (call != null)
+ {
+ holdCodec.HoldCall(call);
+ }
+ else
+ {
+ Debug.Console(0, this, "[Hold Call] Unable to find call at index '{0}'", i);
+ }
+ });
+
+ trilist.SetSigFalseAction((uint)(joinMap.ResumeCallsStart.JoinNumber + index), () =>
+ {
+ if (index < 0 || index >= ActiveCalls.Count) return;
+
+ var call = ActiveCalls[index];
+ if (call != null)
+ {
+ holdCodec.ResumeCall(call);
+ }
+ else
+ {
+ Debug.Console(0, this, "[Resume Call] Unable to find call at index '{0}'", i);
+ }
+ });
+ }
+ }
+
+
+
+ trilist.OnlineStatusChange += (device, args) =>
+ {
+ if (!args.DeviceOnLine) return;
+
+ // TODO [ ] Issue #868
+ trilist.SetString(joinMap.CurrentCallData.JoinNumber, "\xFC");
+ UpdateCallStatusXSig();
+ };
}
private string UpdateCallStatusXSig()
{
const int maxCalls = 8;
- const int maxStrings = 5;
- const int offset = 6;
+ const int maxStrings = 6;
+ const int maxDigitals = 2;
+ const int offset = maxStrings + maxDigitals;
var stringIndex = 0;
var digitalIndex = maxStrings * maxCalls;
var arrayIndex = 0;
@@ -1087,7 +1369,8 @@ ScreenIndexIsPinnedTo: {8} (a{17})
if (arrayIndex >= maxCalls * offset)
break;
//digitals
- tokenArray[arrayIndex] = new XSigDigitalToken(digitalIndex + 1, call.IsActiveCall);
+ tokenArray[digitalIndex] = new XSigDigitalToken(digitalIndex + 1, call.IsActiveCall);
+ tokenArray[digitalIndex + 1] = new XSigDigitalToken(digitalIndex + 2, call.IsOnHold);
//serials
tokenArray[arrayIndex + 1] = new XSigSerialToken(stringIndex + 1, call.Name ?? String.Empty);
@@ -1095,6 +1378,12 @@ ScreenIndexIsPinnedTo: {8} (a{17})
tokenArray[arrayIndex + 3] = new XSigSerialToken(stringIndex + 3, call.Direction.ToString());
tokenArray[arrayIndex + 4] = new XSigSerialToken(stringIndex + 4, call.Type.ToString());
tokenArray[arrayIndex + 5] = new XSigSerialToken(stringIndex + 5, call.Status.ToString());
+ if(call.Duration != null)
+ {
+ // May need to verify correct string format here
+ var dur = string.Format("{0:c}", call.Duration);
+ tokenArray[arrayIndex + 6] = new XSigSerialToken(stringIndex + 6, dur);
+ }
arrayIndex += offset;
stringIndex += maxStrings;
@@ -1103,14 +1392,17 @@ ScreenIndexIsPinnedTo: {8} (a{17})
while (digitalIndex < maxCalls)
{
//digitals
- tokenArray[arrayIndex] = new XSigDigitalToken(digitalIndex + 1, false);
+ tokenArray[digitalIndex] = new XSigDigitalToken(digitalIndex + 1, false);
+ tokenArray[digitalIndex + 1] = new XSigDigitalToken(digitalIndex + 2, false);
- //serials
+
+ //serials
tokenArray[arrayIndex + 1] = new XSigSerialToken(stringIndex + 1, String.Empty);
tokenArray[arrayIndex + 2] = new XSigSerialToken(stringIndex + 2, String.Empty);
tokenArray[arrayIndex + 3] = new XSigSerialToken(stringIndex + 3, String.Empty);
tokenArray[arrayIndex + 4] = new XSigSerialToken(stringIndex + 4, String.Empty);
tokenArray[arrayIndex + 5] = new XSigSerialToken(stringIndex + 5, String.Empty);
+ tokenArray[arrayIndex + 6] = new XSigSerialToken(stringIndex + 6, String.Empty);
arrayIndex += offset;
stringIndex += maxStrings;
@@ -1122,25 +1414,61 @@ ScreenIndexIsPinnedTo: {8} (a{17})
private void LinkVideoCodecDtmfToApi(BasicTriList trilist, VideoCodecControllerJoinMap joinMap)
{
- trilist.SetSigFalseAction(joinMap.Dtmf0.JoinNumber, () => SendDtmf("0"));
- trilist.SetSigFalseAction(joinMap.Dtmf1.JoinNumber, () => SendDtmf("1"));
- trilist.SetSigFalseAction(joinMap.Dtmf2.JoinNumber, () => SendDtmf("2"));
- trilist.SetSigFalseAction(joinMap.Dtmf3.JoinNumber, () => SendDtmf("3"));
- trilist.SetSigFalseAction(joinMap.Dtmf4.JoinNumber, () => SendDtmf("4"));
- trilist.SetSigFalseAction(joinMap.Dtmf5.JoinNumber, () => SendDtmf("5"));
- trilist.SetSigFalseAction(joinMap.Dtmf6.JoinNumber, () => SendDtmf("6"));
- trilist.SetSigFalseAction(joinMap.Dtmf7.JoinNumber, () => SendDtmf("7"));
- trilist.SetSigFalseAction(joinMap.Dtmf8.JoinNumber, () => SendDtmf("8"));
- trilist.SetSigFalseAction(joinMap.Dtmf9.JoinNumber, () => SendDtmf("9"));
- trilist.SetSigFalseAction(joinMap.DtmfStar.JoinNumber, () => SendDtmf("*"));
- trilist.SetSigFalseAction(joinMap.DtmfPound.JoinNumber, () => SendDtmf("#"));
+ trilist.SetSigFalseAction(joinMap.Dtmf0.JoinNumber, () => SendDtmfAction("0", trilist, joinMap));
+ trilist.SetSigFalseAction(joinMap.Dtmf1.JoinNumber, () => SendDtmfAction("1", trilist, joinMap));
+ trilist.SetSigFalseAction(joinMap.Dtmf2.JoinNumber, () => SendDtmfAction("2", trilist, joinMap));
+ trilist.SetSigFalseAction(joinMap.Dtmf3.JoinNumber, () => SendDtmfAction("3", trilist, joinMap));
+ trilist.SetSigFalseAction(joinMap.Dtmf4.JoinNumber, () => SendDtmfAction("4", trilist, joinMap));
+ trilist.SetSigFalseAction(joinMap.Dtmf5.JoinNumber, () => SendDtmfAction("5", trilist, joinMap));
+ trilist.SetSigFalseAction(joinMap.Dtmf6.JoinNumber, () => SendDtmfAction("6", trilist, joinMap));
+ trilist.SetSigFalseAction(joinMap.Dtmf7.JoinNumber, () => SendDtmfAction("7", trilist, joinMap));
+ trilist.SetSigFalseAction(joinMap.Dtmf8.JoinNumber, () => SendDtmfAction("8", trilist, joinMap));
+ trilist.SetSigFalseAction(joinMap.Dtmf9.JoinNumber, () => SendDtmfAction("9", trilist, joinMap));
+ trilist.SetSigFalseAction(joinMap.DtmfStar.JoinNumber, () => SendDtmfAction("*", trilist, joinMap));
+ trilist.SetSigFalseAction(joinMap.DtmfPound.JoinNumber, () => SendDtmfAction("#", trilist, joinMap));
}
+ ///
+ /// Sends the specified string as a DTMF command.
+ /// Reads the value of the SendDtmfToSpecificCallInstance digital join and SelectCall analog join to determine
+ /// Whther to send to a specific call index or to the last connected call
+ ///
+ ///
+ ///
+ ///
+ private void SendDtmfAction(string s, BasicTriList trilist, VideoCodecControllerJoinMap joinMap)
+ {
+ if (!trilist.GetBool(joinMap.SendDtmfToSpecificCallIndex.JoinNumber))
+ {
+ SendDtmf(s);
+ }
+ else
+ {
+ var callIndex = trilist.GetUshort(joinMap.SelectCall.JoinNumber);
+ if (callIndex > 0 && callIndex <= 8)
+ {
+ var call = ActiveCalls[callIndex - 1];
+ if (call != null && call.IsActiveCall)
+ {
+ SendDtmf(s, call);
+ }
+ else
+ {
+ Debug.Console(0, this, "Warning: No call found at index {0} or call is not active.", callIndex);
+ }
+ }
+ else
+ {
+ Debug.Console(0, this, "Warning: Invalid call index specified. Please use a value of 1-8.");
+ }
+ }
+ }
+
private void LinkVideoCodecCameraLayoutsToApi(IHasCodecLayouts codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap)
{
trilist.SetSigFalseAction(joinMap.CameraLayout.JoinNumber, codec.LocalLayoutToggle);
- codec.LocalLayoutFeedback.LinkInputSig(trilist.StringInput[joinMap.CameraLayoutStringFb.JoinNumber]);
+ codec.LocalLayoutFeedback.LinkInputSig(trilist.StringInput[joinMap.CurrentLayoutStringFb.JoinNumber]);
}
private void LinkVideoCodecCameraModeToApi(IHasCameraAutoMode codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap)
@@ -1271,18 +1599,81 @@ ScreenIndexIsPinnedTo: {8} (a{17})
else camera.ZoomStop();
});
+
+ trilist.SetBoolSigAction(joinMap.CameraFocusNear.JoinNumber, (b) =>
+ {
+ if (codec.SelectedCamera == null) return;
+ var camera = codec.SelectedCamera as IHasCameraFocusControl;
+
+ if (camera == null) return;
+
+ if (b) camera.FocusNear();
+ else camera.FocusStop();
+ });
+
+ trilist.SetBoolSigAction(joinMap.CameraFocusFar.JoinNumber, (b) =>
+ {
+ if (codec.SelectedCamera == null) return;
+ var camera = codec.SelectedCamera as IHasCameraFocusControl;
+
+ if (camera == null) return;
+
+ if (b) camera.FocusFar();
+ else camera.FocusStop();
+ });
+
+ trilist.SetSigFalseAction(joinMap.CameraFocusAuto.JoinNumber, () =>
+ {
+ if (codec.SelectedCamera == null) return;
+ var camera = codec.SelectedCamera as IHasCameraFocusControl;
+
+ if (camera == null) return;
+
+ camera.TriggerAutoFocus();
+ });
+
+ // Camera count
+ trilist.SetUshort(joinMap.CameraCount.JoinNumber, (ushort)codec.Cameras.Count);
+
+ // Camera names
+ for (uint i = 0; i < joinMap.CameraNamesFb.JoinSpan; i++)
+ {
+ //Check the count first
+ if (i < codec.Cameras.Count && codec.Cameras[(int)i] != null)
+ {
+ trilist.SetString(joinMap.CameraNamesFb.JoinNumber + i, codec.Cameras[(int)i].Name);
+ }
+ else
+ {
+ trilist.SetString(joinMap.CameraNamesFb.JoinNumber + i, "");
+ }
+ }
+
//Camera Select
trilist.SetUShortSigAction(joinMap.CameraNumberSelect.JoinNumber, (i) =>
{
- if (codec.SelectedCamera == null) return;
-
- codec.SelectCamera(codec.Cameras[i].Key);
+ if (i > 0 && i <= codec.Cameras.Count)
+ {
+ codec.SelectCamera(codec.Cameras[i - 1].Key);
+ }
+ else
+ {
+ Debug.Console(0, this, "Unable to select. No camera found at index {0}", i);
+ }
});
+ // Set initial selected camera feedback
+ if (codec.SelectedCamera != null)
+ {
+ trilist.SetUshort(joinMap.CameraNumberSelect.JoinNumber, (ushort)codec.Cameras.FindIndex((c) => c.Key == codec.SelectedCamera.Key));
+ }
+
codec.CameraSelected += (sender, args) =>
{
var i = (ushort)codec.Cameras.FindIndex((c) => c.Key == args.SelectedCamera.Key);
+ trilist.SetUshort(joinMap.CameraNumberSelect.JoinNumber, (ushort)(i + 1));
+
if (codec is IHasCodecRoomPresets)
{
return;
@@ -1325,10 +1716,16 @@ ScreenIndexIsPinnedTo: {8} (a{17})
trilist.SetUShortSigAction(joinMap.CameraPresetSelect.JoinNumber, (i) =>
{
presetCodec.CodecRoomPresetSelect(i);
-
- trilist.SetUshort(joinMap.CameraPresetSelect.JoinNumber, i);
});
+
+ // Far End Presets
+ trilist.SetUShortSigAction(joinMap.FarEndPresetSelect.JoinNumber, (i) =>
+ {
+ presetCodec.SelectFarEndPreset(i);
+ });
+
+
trilist.SetSigFalseAction(joinMap.CameraPresetSave.JoinNumber,
() =>
{
@@ -1347,7 +1744,96 @@ ScreenIndexIsPinnedTo: {8} (a{17})
};
}
- private string SetCameraPresetNames(IEnumerable presets)
+ // Following fields only used for Bridging
+ private int _selectedRecentCallItemIndex;
+ private CodecCallHistory.CallHistoryEntry _selectedRecentCallItem;
+ private DirectoryItem _selectedDirectoryItem;
+
+ private void LinkVideoCodecCallHistoryToApi(IHasCallHistory codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap)
+ {
+ codec.CallHistory.RecentCallsListHasChanged += (o, a) =>
+ {
+ UpdateCallHistory(codec, trilist, joinMap);
+ };
+
+ // Selected item action and feedback
+ trilist.SetUShortSigAction(joinMap.SelectRecentCallItem.JoinNumber, (u) =>
+ {
+ if (u == 0 || u > codec.CallHistory.RecentCalls.Count)
+ {
+ Debug.Console(2, this, "Recent Call History index out of range");
+ return;
+ }
+
+ _selectedRecentCallItemIndex = (int)(u - 1);
+ trilist.SetUshort(joinMap.SelectRecentCallItem.JoinNumber, u);
+
+ var _selectedRecentCallItem = codec.CallHistory.RecentCalls[_selectedRecentCallItemIndex];
+
+ if (_selectedRecentCallItem != null)
+ {
+ trilist.SetString(joinMap.SelectedRecentCallName.JoinNumber, _selectedRecentCallItem.Name);
+ trilist.SetString(joinMap.SelectedRecentCallNumber.JoinNumber, _selectedRecentCallItem.Number);
+ trilist.SetSigFalseAction(joinMap.RemoveSelectedRecentCallItem.JoinNumber, () => codec.RemoveCallHistoryEntry(_selectedRecentCallItem));
+ trilist.SetSigFalseAction(joinMap.DialSelectedRecentCallItem.JoinNumber, () => this.Dial(_selectedRecentCallItem.Number));
+ }
+ else
+ {
+ trilist.SetString(joinMap.SelectedRecentCallName.JoinNumber, string.Empty);
+ trilist.SetString(joinMap.SelectedRecentCallNumber.JoinNumber, string.Empty);
+ trilist.ClearBoolSigAction(joinMap.RemoveSelectedRecentCallItem.JoinNumber);
+ trilist.ClearBoolSigAction(joinMap.DialSelectedRecentCallItem.JoinNumber);
+ }
+ });
+ }
+
+
+
+ private void UpdateCallHistory(IHasCallHistory codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap)
+ {
+ // Clear out selected item
+ _selectedRecentCallItemIndex = 0;
+ _selectedRecentCallItem = null;
+ trilist.SetUshort(joinMap.SelectRecentCallItem.JoinNumber, 0);
+ trilist.SetString(joinMap.SelectedRecentCallName.JoinNumber, string.Empty);
+ trilist.SetString(joinMap.SelectedRecentCallNumber.JoinNumber, string.Empty);
+ trilist.ClearBoolSigAction(joinMap.RemoveSelectedRecentCallItem.JoinNumber);
+ //
+
+ trilist.SetUshort(joinMap.RecentCallCount.JoinNumber, (ushort)codec.CallHistory.RecentCalls.Count);
+
+ // Update the call history joins
+ var maxItems = joinMap.RecentCallNamesStart.JoinSpan;
+
+ // Create history
+ uint index = 0;
+ for (uint i = 0; i < maxItems && i < codec.CallHistory.RecentCalls.Count; i++)
+ {
+ trilist.SetString(joinMap.RecentCallNamesStart.JoinNumber + i, codec.CallHistory.RecentCalls[(int)i].Name);
+ trilist.SetString(joinMap.RecentCallTimesStart.JoinNumber + i, codec.CallHistory.RecentCalls[(int)i].StartTime.ToShortTimeString());
+ trilist.SetUshort(joinMap.RecentCallOccurrenceType.JoinNumber + i, (ushort)codec.CallHistory.RecentCalls[(int)i].OccurrenceType);
+ //i++;
+ index = i;
+ }
+
+ //foreach(var item in codec.CallHistory.RecentCalls)
+ //{
+ // trilist.SetString(joinMap.RecentCallNamesStart.JoinNumber + i, item.Name);
+ // trilist.SetString(joinMap.RecentCallTimesStart.JoinNumber + i, item.StartTime.ToShortTimeString());
+ // trilist.SetUshort(joinMap.RecentCallOccurrenceType.JoinNumber + i, (ushort)item.OccurrenceType);
+ // i++;
+ //}
+
+ // Clears existing items
+ for (uint j = index; j < maxItems; j++)
+ {
+ trilist.SetString(joinMap.RecentCallNamesStart.JoinNumber + j, string.Empty);
+ trilist.SetString(joinMap.RecentCallTimesStart.JoinNumber + j, string.Empty);
+ trilist.SetUshort(joinMap.RecentCallOccurrenceType.JoinNumber + j, 0);
+ }
+ }
+
+ private string SetCameraPresetNames(IEnumerable presets)
{
return SetCameraPresetNames(presets.Select(p => p.Description).ToList());
}
diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ZoomRoom.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ZoomRoom.cs
index 1db04f0c..6ea90c80 100644
--- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ZoomRoom.cs
+++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ZoomRoom.cs
@@ -3560,7 +3560,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom
public bool LoginResponseWasReceived { get; private set; }
- public bool FirstJsonResponseWasReceived { get; private set; }
+ public bool FirstJsonResponseWasReceived { get; private set; }
public bool InitialQueryMessagesWereSent { get; private set; }
@@ -3576,7 +3576,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom
public event EventHandler InitialSyncCompleted;
- public event EventHandler FirstJsonResponseReceived;
+ public event EventHandler FirstJsonResponseReceived;
public void StartSync()
{
@@ -3607,18 +3607,18 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom
CheckSyncStatus();
}
- public void ReceivedFirstJsonResponse()
- {
- FirstJsonResponseWasReceived = true;
- Debug.Console(1, this, "First JSON Response Received.");
+ public void ReceivedFirstJsonResponse()
+ {
+ FirstJsonResponseWasReceived = true;
+ Debug.Console(1, this, "First JSON Response Received.");
- var handler = FirstJsonResponseReceived;
- if (handler != null)
- {
- handler(this, null);
- }
- CheckSyncStatus();
- }
+ var handler = FirstJsonResponseReceived;
+ if (handler != null)
+ {
+ handler(this, null);
+ }
+ CheckSyncStatus();
+ }
public void InitialQueryMessagesSent()
{
@@ -3645,7 +3645,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom
{
_syncQueries.Clear();
LoginResponseWasReceived = false;
- FirstJsonResponseWasReceived = false;
+ FirstJsonResponseWasReceived = false;
InitialQueryMessagesWereSent = false;
LastQueryResponseWasReceived = false;
CamerasHaveBeenSetUp = false;