diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/BridgeBase.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/BridgeBase.cs index 13d1948a..151b5461 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/BridgeBase.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/BridgeBase.cs @@ -316,9 +316,9 @@ namespace PepperDash.Essentials.Core.Bridges { Debug.Console(1, "Factory Attempting to create new EiscApiAdvanced Device"); - var controlProperties = dc.Properties["control"].ToObject(); + var controlProperties = CommFactory.GetControlPropertiesConfig(dc); - switch (dc.Type) + switch (dc.Type.ToLower()) { case "eiscapiadv": case "eiscapiadvanced": diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/VideoCodecControllerJoinMap.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/VideoCodecControllerJoinMap.cs new file mode 100644 index 00000000..8054a077 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/VideoCodecControllerJoinMap.cs @@ -0,0 +1,811 @@ +using System; +using PepperDash.Essentials.Core; + +namespace PepperDash_Essentials_Core.Bridges.JoinMaps +{ + public class VideoCodecControllerJoinMap : JoinMapBaseAdvanced + { + #region Status + + [JoinName("IsOnline")] public JoinDataComplete IsOnline = + new JoinDataComplete(new JoinData {JoinNumber = 1, JoinSpan = 1}, + new JoinMetadata + { + Description = "Device is Online", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Digital + }); + + #endregion + + [JoinName("CallDirection")] public JoinDataComplete CallDirection = + new JoinDataComplete(new JoinData {JoinNumber = 22, JoinSpan = 1}, + new JoinMetadata + { + Description = "Current Call Direction", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Serial + }); + + [JoinName("CameraLayout")] public JoinDataComplete CameraLayout = + new JoinDataComplete(new JoinData {JoinNumber = 142, JoinSpan = 1}, + new JoinMetadata + { + Description = "Camera Layout Toggle", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("CameraLayoutStringFb")] public JoinDataComplete CameraLayoutStringFb = + new JoinDataComplete(new JoinData {JoinNumber = 141, JoinSpan = 1}, + new JoinMetadata + { + Description = "Current Layout Fb", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Analog + }); + + [JoinName("CameraModeAuto")] public JoinDataComplete CameraModeAuto = + new JoinDataComplete(new JoinData {JoinNumber = 131, JoinSpan = 1}, + new JoinMetadata + { + Description = "Camera Mode Auto", + JoinCapabilities = eJoinCapabilities.ToFromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("CameraModeManual")] public JoinDataComplete CameraModeManual = + new JoinDataComplete(new JoinData {JoinNumber = 132, JoinSpan = 1}, + new JoinMetadata + { + Description = "Camera Mode Manual", + JoinCapabilities = eJoinCapabilities.ToFromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("CameraModeOff")] public JoinDataComplete CameraModeOff = + new JoinDataComplete(new JoinData {JoinNumber = 133, JoinSpan = 1}, + new JoinMetadata + { + Description = "Camera Mode Off", + JoinCapabilities = eJoinCapabilities.ToFromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("CameraNumberSelect")] public JoinDataComplete CameraNumberSelect = + new JoinDataComplete(new JoinData {JoinNumber = 60, JoinSpan = 1}, + new JoinMetadata + { + Description = "Camera Number Select/FB", + JoinCapabilities = eJoinCapabilities.ToFromSIMPL, + JoinType = eJoinType.Analog + }); + + [JoinName("CameraPanLeft")] public JoinDataComplete CameraPanLeft = + new JoinDataComplete(new JoinData {JoinNumber = 113, JoinSpan = 1}, + new JoinMetadata + { + Description = "Camera Pan Left", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("CameraPanRight")] public JoinDataComplete CameraPanRight = + new JoinDataComplete(new JoinData {JoinNumber = 114, JoinSpan = 1}, + new JoinMetadata + { + Description = "Camera Pan Right", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("CameraPresetNames")] public JoinDataComplete CameraPresetNames = + new JoinDataComplete(new JoinData {JoinNumber = 121, JoinSpan = 1}, + new JoinMetadata + { + Description = "Camera Preset Names - XSIG, max of 15", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Serial + }); + + [JoinName("CameraPresetSelect")] public JoinDataComplete CameraPresetSelect = + new JoinDataComplete(new JoinData {JoinNumber = 121, JoinSpan = 1}, + new JoinMetadata + { + Description = "Camera Preset Select", + JoinCapabilities = eJoinCapabilities.ToFromSIMPL, + JoinType = eJoinType.Analog + }); + + [JoinName("CameraPresetSave")] public JoinDataComplete CameraPresetSave = + new JoinDataComplete(new JoinData {JoinNumber = 121, JoinSpan = 1}, + new JoinMetadata + { + Description = "Save Selected Preset", + JoinCapabilities = eJoinCapabilities.ToFromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("CameraSelfView")] public JoinDataComplete CameraSelfView = + new JoinDataComplete(new JoinData {JoinNumber = 141, JoinSpan = 1}, + new JoinMetadata + { + Description = "Camera Self View Toggle/FB", + JoinCapabilities = eJoinCapabilities.ToFromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("CameraSupportsAutoMode")] public JoinDataComplete CameraSupportsAutoMode = + new JoinDataComplete(new JoinData {JoinNumber = 143, JoinSpan = 1}, + new JoinMetadata + { + Description = "Camera Supports Auto Mode FB", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("CameraSupportsOffMode")] public JoinDataComplete CameraSupportsOffMode = + new JoinDataComplete(new JoinData {JoinNumber = 144, JoinSpan = 1}, + new JoinMetadata + { + Description = "Camera Supports Off Mode FB", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("CameraTiltDown")] public JoinDataComplete CameraTiltDown = + new JoinDataComplete(new JoinData {JoinNumber = 112, JoinSpan = 1}, + new JoinMetadata + { + Description = "Camera Tilt Down", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("CameraTiltUp")] public JoinDataComplete CameraTiltUp = + new JoinDataComplete(new JoinData {JoinNumber = 111, JoinSpan = 1}, + new JoinMetadata + { + Description = "Camera Tilt Up", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("CameraZoomIn")] public JoinDataComplete CameraZoomIn = + new JoinDataComplete(new JoinData {JoinNumber = 115, JoinSpan = 1}, + new JoinMetadata + { + Description = "Camera Zoom In", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("CameraZoomOut")] public JoinDataComplete CameraZoomOut = + new JoinDataComplete(new JoinData {JoinNumber = 116, JoinSpan = 1}, + new JoinMetadata + { + Description = "Camera Zoom Out", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("CurrentCallName")] public JoinDataComplete CurrentCallData = + new JoinDataComplete(new JoinData {JoinNumber = 2, JoinSpan = 1}, + new JoinMetadata + { + Description = "Current Call Data - XSIG", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Serial + }); + + [JoinName("CurrentDialString")] public JoinDataComplete CurrentDialString = + new JoinDataComplete(new JoinData {JoinNumber = 1, JoinSpan = 1}, + new JoinMetadata + { + Description = "Current Dial String", + JoinCapabilities = eJoinCapabilities.ToFromSIMPL, + JoinType = eJoinType.Serial + }); + + [JoinName("CurrentParticipants")] public JoinDataComplete CurrentParticipants = + new JoinDataComplete(new JoinData {JoinNumber = 151, JoinSpan = 1}, + new JoinMetadata() + { + Description = "Current Participants XSig", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Serial + }); + + [JoinName("CurrentSource")] public JoinDataComplete CurrentSource = + new JoinDataComplete(new JoinData {JoinNumber = 201, JoinSpan = 1}, + new JoinMetadata + { + Description = "Current Source", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Serial + }); + + [JoinName("DialMeeting1")] public JoinDataComplete DialMeeting1 = + new JoinDataComplete(new JoinData {JoinNumber = 161, JoinSpan = 1}, + new JoinMetadata + { + Description = "Join first meeting", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("DialMeeting2")] + public JoinDataComplete DialMeeting2 = + new JoinDataComplete(new JoinData { JoinNumber = 162, JoinSpan = 1 }, + new JoinMetadata + { + Description = "Join second meeting", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("DialMeeting3")] + public JoinDataComplete DialMeeting3 = + new JoinDataComplete(new JoinData { JoinNumber = 163, JoinSpan = 1 }, + new JoinMetadata + { + Description = "Join third meeting", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("DirectoryDialSelectedLine")] public JoinDataComplete DirectoryDialSelectedLine = + new JoinDataComplete(new JoinData {JoinNumber = 106, JoinSpan = 1}, + new JoinMetadata + { + Description = "Dial selected directory line", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("DirectoryEntries")] public JoinDataComplete DirectoryEntries = + new JoinDataComplete(new JoinData {JoinNumber = 101, JoinSpan = 1}, + new JoinMetadata + { + Description = "Directory Entries - XSig, 255 entries", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Serial + }); + + [JoinName("DirectoryEntryIsContact")] public JoinDataComplete DirectoryEntryIsContact = + new JoinDataComplete(new JoinData {JoinNumber = 101, JoinSpan = 1}, + new JoinMetadata + { + Description = "Directory Selected Entry Is Contact FB", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("DirectoryEntrySelectedName")] public JoinDataComplete DirectoryEntrySelectedName = + new JoinDataComplete(new JoinData {JoinNumber = 356, JoinSpan = 1}, + new JoinMetadata + { + Description = "Selected Directory Entry Name", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Serial + }); + + [JoinName("DirectoryEntrySelectedNumber")] public JoinDataComplete DirectoryEntrySelectedNumber = + new JoinDataComplete(new JoinData {JoinNumber = 357, JoinSpan = 1}, + new JoinMetadata + { + Description = "Selected Directory Entry Number", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Serial + }); + + [JoinName("DirectoryFolderBack")] public JoinDataComplete DirectoryFolderBack = + new JoinDataComplete(new JoinData {JoinNumber = 105, JoinSpan = 1}, + new JoinMetadata + { + Description = "Go back one directory level", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("DirectoryHasChanged")] public JoinDataComplete DirectoryHasChanged = + new JoinDataComplete(new JoinData {JoinNumber = 103, JoinSpan = 1}, + new JoinMetadata + { + Description = "Directory has changed FB", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("DirectoryIsRoot")] public JoinDataComplete DirectoryIsRoot = + new JoinDataComplete(new JoinData {JoinNumber = 102, JoinSpan = 1}, + new JoinMetadata + { + Description = "Directory is on Root FB", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("DirectoryLineSelected")] public JoinDataComplete DirectoryLineSelected = + new JoinDataComplete(new JoinData {JoinNumber = 101, JoinSpan = 1}, + new JoinMetadata + { + Description = "Directory Line Selected FB", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("DirectoryRoot")] public JoinDataComplete DirectoryRoot = + new JoinDataComplete(new JoinData {JoinNumber = 104, JoinSpan = 1}, + new JoinMetadata + { + Description = "Go to Directory Root", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("DirectoryRowCount")] public JoinDataComplete DirectoryRowCount = + new JoinDataComplete(new JoinData {JoinNumber = 101, JoinSpan = 1}, + new JoinMetadata + { + Description = "Directory Row Count FB", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Analog + }); + + [JoinName("DirectorySearchBusy")] public JoinDataComplete DirectorySearchBusy = + new JoinDataComplete(new JoinData {JoinNumber = 100, JoinSpan = 1}, + new JoinMetadata + { + Description = "Directory Search Busy FB", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("DirectorySearchString")] public JoinDataComplete DirectorySearchString = + new JoinDataComplete(new JoinData {JoinNumber = 100, JoinSpan = 1}, + new JoinMetadata + { + Description = "Directory Search String", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Serial + }); + + [JoinName("DirectorySelectRow")] public JoinDataComplete DirectorySelectRow = + new JoinDataComplete(new JoinData {JoinNumber = 101, JoinSpan = 1}, + new JoinMetadata + { + Description = "Directory Select Row", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Analog + }); + + [JoinName("DirectorySelectedFolderName")] public JoinDataComplete DirectorySelectedFolderName = + new JoinDataComplete(new JoinData {JoinNumber = 358, JoinSpan = 1}, + new JoinMetadata + { + Description = "Selected Directory Folder Name", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Serial + }); + + [JoinName("0")] public JoinDataComplete Dtmf0 = + new JoinDataComplete(new JoinData {JoinNumber = 20, JoinSpan = 1}, + new JoinMetadata + { + Description = "DTMF 0", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("1")] public JoinDataComplete Dtmf1 = + new JoinDataComplete(new JoinData {JoinNumber = 11, JoinSpan = 1}, + new JoinMetadata + { + Description = "DTMF 1", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("2")] public JoinDataComplete Dtmf2 = + new JoinDataComplete(new JoinData {JoinNumber = 12, JoinSpan = 1}, + new JoinMetadata + { + Description = "DTMF 2", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("3")] public JoinDataComplete Dtmf3 = + new JoinDataComplete(new JoinData {JoinNumber = 13, JoinSpan = 1}, + new JoinMetadata + { + Description = "DTMF 3", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("4")] public JoinDataComplete Dtmf4 = + new JoinDataComplete(new JoinData {JoinNumber = 14, JoinSpan = 1}, + new JoinMetadata + { + Description = "DTMF 4", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("5")] public JoinDataComplete Dtmf5 = + new JoinDataComplete(new JoinData {JoinNumber = 15, JoinSpan = 1}, + new JoinMetadata + { + Description = "DTMF 5", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("6")] public JoinDataComplete Dtmf6 = + new JoinDataComplete(new JoinData {JoinNumber = 16, JoinSpan = 1}, + new JoinMetadata + { + Description = "DTMF 6", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("7")] public JoinDataComplete Dtmf7 = + new JoinDataComplete(new JoinData {JoinNumber = 17, JoinSpan = 1}, + new JoinMetadata + { + Description = "DTMF 7", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("8")] public JoinDataComplete Dtmf8 = + new JoinDataComplete(new JoinData {JoinNumber = 18, JoinSpan = 1}, + new JoinMetadata + { + Description = "DTMF 8", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("9")] public JoinDataComplete Dtmf9 = + new JoinDataComplete(new JoinData {JoinNumber = 19, JoinSpan = 1}, + new JoinMetadata + { + Description = "DTMF 9", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("#")] public JoinDataComplete DtmfPound = + new JoinDataComplete(new JoinData {JoinNumber = 22, JoinSpan = 1}, + new JoinMetadata + { + Description = "DTMF #", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("*")] public JoinDataComplete DtmfStar = + new JoinDataComplete(new JoinData {JoinNumber = 21, JoinSpan = 1}, + new JoinMetadata + { + Description = "DTMF *", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("EndCall")] public JoinDataComplete EndCall = + new JoinDataComplete(new JoinData {JoinNumber = 24, JoinSpan = 1}, + new JoinMetadata + { + Description = "Hang Up", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("HookState")] public JoinDataComplete HookState = + new JoinDataComplete(new JoinData {JoinNumber = 31, JoinSpan = 1}, + new JoinMetadata + { + Description = "Current Hook State", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("IncomingAnswer")] public JoinDataComplete IncomingAnswer = + new JoinDataComplete(new JoinData {JoinNumber = 51, JoinSpan = 1}, + new JoinMetadata + { + Description = "Answer Incoming Call", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("IncomingCall")] public JoinDataComplete IncomingCall = + new JoinDataComplete(new JoinData {JoinNumber = 50, JoinSpan = 1}, + new JoinMetadata + { + Description = "Incoming Call", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("IncomingCallName")] public JoinDataComplete IncomingCallName = + new JoinDataComplete(new JoinData {JoinNumber = 51, JoinSpan = 1}, + new JoinMetadata + { + Description = "Incoming Call Name", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Serial + }); + + [JoinName("IncomingCallNumber")] public JoinDataComplete IncomingCallNumber = + new JoinDataComplete(new JoinData {JoinNumber = 52, JoinSpan = 1}, + new JoinMetadata + { + Description = "Incoming Call Number", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Serial + }); + + [JoinName("IncomingReject")] public JoinDataComplete IncomingReject = + new JoinDataComplete(new JoinData {JoinNumber = 52, JoinSpan = 1}, + new JoinMetadata + { + Description = "Reject Incoming Call", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + + [JoinName("ManualDial")] public JoinDataComplete ManualDial = + new JoinDataComplete(new JoinData {JoinNumber = 71, JoinSpan = 1}, + new JoinMetadata + { + Description = "Dial manual string", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("Meeting Count Fb")] public JoinDataComplete MeetingCount = + new JoinDataComplete(new JoinData {JoinNumber = 161, JoinSpan = 1}, + new JoinMetadata + { + Description = "Meeting Count", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Analog + }); + + [JoinName("MicMuteOff")] public JoinDataComplete MicMuteOff = + new JoinDataComplete(new JoinData {JoinNumber = 172, JoinSpan = 1}, + new JoinMetadata + { + Description = "Mic Mute Off", + JoinCapabilities = eJoinCapabilities.ToFromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("MicMuteOn")] public JoinDataComplete MicMuteOn = + new JoinDataComplete(new JoinData {JoinNumber = 171, JoinSpan = 1}, + new JoinMetadata + { + Description = "Mic Mute On", + JoinCapabilities = eJoinCapabilities.ToFromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("MicMuteToggle")] public JoinDataComplete MicMuteToggle = + new JoinDataComplete(new JoinData {JoinNumber = 173, JoinSpan = 1}, + new JoinMetadata + { + Description = "Mic Mute Toggle", + JoinCapabilities = eJoinCapabilities.ToFromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("MinutesBeforeMeetingStart")] public JoinDataComplete MinutesBeforeMeetingStart = + new JoinDataComplete(new JoinData {JoinNumber = 41, JoinSpan = 1}, + new JoinMetadata + { + Description = "Minutes before meeting start that a meeting is joinable", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Analog + }); + + [JoinName("ParticipantCount")] public JoinDataComplete ParticipantCount = + new JoinDataComplete(new JoinData {JoinNumber = 151, JoinSpan = 1}, + new JoinMetadata + { + Description = "Current Participant Count", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Analog + }); + + [JoinName("Schedule")] public JoinDataComplete Schedule = + new JoinDataComplete(new JoinData {JoinNumber = 102, JoinSpan = 1}, + new JoinMetadata + { + Description = "Schedule Data - XSIG", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Serial + }); + + [JoinName("AutoShareWhileInCall")] public JoinDataComplete SourceShareAutoStart = + new JoinDataComplete(new JoinData {JoinNumber = 203, JoinSpan = 1}, + new JoinMetadata + { + Description = "When high, will autostart sharing when a call is joined", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("SourceShareEnd")] public JoinDataComplete SourceShareEnd = + new JoinDataComplete(new JoinData {JoinNumber = 202, JoinSpan = 1}, + new JoinMetadata + { + Description = "Stop Sharing & Feedback", + JoinCapabilities = eJoinCapabilities.ToFromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("SourceShareStart")] public JoinDataComplete SourceShareStart = + new JoinDataComplete(new JoinData {JoinNumber = 201, JoinSpan = 1}, + new JoinMetadata + { + Description = "Start Sharing & Feedback", + JoinCapabilities = eJoinCapabilities.ToFromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("RecievingContent")] public JoinDataComplete RecievingContent = + new JoinDataComplete(new JoinData {JoinNumber = 204, JoinSpan = 1}, + new JoinMetadata + { + Description = "Recieving content from the far end", + JoinType = eJoinType.Digital, + JoinCapabilities = eJoinCapabilities.ToSIMPL + }); + + [JoinName("SelfviewPosition")] public JoinDataComplete SelfviewPosition = + new JoinDataComplete(new JoinData {JoinNumber = 211, JoinSpan = 1}, + new JoinMetadata + { + Description = "advance selfview position", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("SelfviewPositionFb")] + public JoinDataComplete SelfviewPositionFb = + new JoinDataComplete(new JoinData { JoinNumber = 211, JoinSpan = 1 }, + new JoinMetadata + { + Description = "advance selfview position", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Serial + }); + + [JoinName("SpeedDialStart")] public JoinDataComplete SpeedDialStart = + new JoinDataComplete(new JoinData {JoinNumber = 41, JoinSpan = 4}, + new JoinMetadata + { + Description = "Speed Dial", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("UpdateMeetings")] public JoinDataComplete UpdateMeetings = + new JoinDataComplete(new JoinData {JoinNumber = 160, JoinSpan = 1}, + new JoinMetadata + { + Description = "Update Meetings", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("VolumeDown")] public JoinDataComplete VolumeDown = + new JoinDataComplete(new JoinData {JoinNumber = 175, JoinSpan = 1}, + new JoinMetadata + { + Description = "Volume Down", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("VolumeLevel")] public JoinDataComplete VolumeLevel = + new JoinDataComplete(new JoinData {JoinNumber = 174, JoinSpan = 1}, + new JoinMetadata + { + Description = "Volume Level", + JoinCapabilities = eJoinCapabilities.ToFromSIMPL, + JoinType = eJoinType.Analog + }); + + [JoinName("VolumeMuteOff")] public JoinDataComplete VolumeMuteOff = + new JoinDataComplete(new JoinData {JoinNumber = 177, JoinSpan = 1}, + new JoinMetadata + { + Description = "Volume Mute Off", + JoinCapabilities = eJoinCapabilities.ToFromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("VolumeMuteOn")] public JoinDataComplete VolumeMuteOn = + new JoinDataComplete(new JoinData {JoinNumber = 176, JoinSpan = 1}, + new JoinMetadata + { + Description = "Volume Mute On", + JoinCapabilities = eJoinCapabilities.ToFromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("VolumeMuteToggle")] public JoinDataComplete VolumeMuteToggle = + new JoinDataComplete(new JoinData {JoinNumber = 178, JoinSpan = 1}, + new JoinMetadata + { + Description = "Volume Mute Toggle", + JoinCapabilities = eJoinCapabilities.ToFromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("VolumeUp")] public JoinDataComplete VolumeUp = + new JoinDataComplete(new JoinData {JoinNumber = 174, JoinSpan = 1}, + new JoinMetadata + { + Description = "Volume Up", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("DialPhoneCall")] + public JoinDataComplete DialPhone = + new JoinDataComplete(new JoinData { JoinNumber = 72, JoinSpan = 1 }, + new JoinMetadata + { + Description = "Dial Phone", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("PhoneHookState")] + public JoinDataComplete PhoneHookState = + new JoinDataComplete(new JoinData { JoinNumber = 72, JoinSpan = 1 }, + new JoinMetadata + { + Description = "Dial Phone", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("EndPhoneCall")] + public JoinDataComplete HangUpPhone = + new JoinDataComplete(new JoinData { JoinNumber = 73, JoinSpan = 1 }, + new JoinMetadata + { + Description = "Hang Up PHone", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("PhoneString")] + public JoinDataComplete PhoneDialString = + new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 }, + new JoinMetadata + { + Description = "Phone Dial String", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Serial + }); + + public VideoCodecControllerJoinMap(uint joinStart) : base(joinStart, typeof (VideoCodecControllerJoinMap)) + { + } + + public VideoCodecControllerJoinMap(uint joinStart, Type type) : base(joinStart, type) + { + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/DeviceTypeInterfaces/IHasFarEndContentStatus.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/DeviceTypeInterfaces/IHasFarEndContentStatus.cs new file mode 100644 index 00000000..21bde91f --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/DeviceTypeInterfaces/IHasFarEndContentStatus.cs @@ -0,0 +1,7 @@ +namespace PepperDash.Essentials.Core.DeviceTypeInterfaces +{ + public interface IHasFarEndContentStatus + { + BoolFeedback ReceivingContent { get; } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/DeviceTypeInterfaces/IHasPhoneDialing.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/DeviceTypeInterfaces/IHasPhoneDialing.cs new file mode 100644 index 00000000..2b7af8ad --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/DeviceTypeInterfaces/IHasPhoneDialing.cs @@ -0,0 +1,14 @@ +using PepperDash.Essentials.Core; + +namespace PepperDash_Essentials_Core.DeviceTypeInterfaces +{ + public interface IHasPhoneDialing + { + BoolFeedback PhoneOffHookFeedback { get; } + StringFeedback CallerIdNameFeedback { get; } + StringFeedback CallerIdNumberFeedback { get; } + void DialPhoneCall(string number); + void EndPhoneCall(); + void SendDtmfToPhone(string digit); + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.csproj b/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.csproj index 36afcac4..9cc4365f 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.csproj +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.csproj @@ -48,48 +48,39 @@ False - ..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.DeviceSupport.dll - True + ..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.DeviceSupport.dll False - ..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.DM.dll - True + ..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.DM.dll False - ..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.EthernetCommunications.dll - True + ..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.EthernetCommunications.dll False - ..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.Fusion.dll - True + ..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.Fusion.dll False - ..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.Gateways.dll - True + ..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.Gateways.dll False - ..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.GeneralIO.dll - True + ..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.GeneralIO.dll False - ..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.Remotes.dll - True + ..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.Remotes.dll False - ..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.ThreeSeriesCards.dll - True + ..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.ThreeSeriesCards.dll False - ..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.UI.dll - True + ..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.UI.dll @@ -108,8 +99,7 @@ False - ..\..\..\..\ProgramData\Crestron\SDK\SimplSharpNewtonsoft.dll - True + ..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpNewtonsoft.dll False @@ -118,13 +108,11 @@ False - ..\..\..\..\ProgramData\Crestron\SDK\SimplSharpReflectionInterface.dll - True + ..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpReflectionInterface.dll False - ..\..\..\..\ProgramData\Crestron\SDK\SimplSharpTimerEventInterface.dll - True + ..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpTimerEventInterface.dll @@ -158,6 +146,7 @@ + @@ -207,6 +196,8 @@ + + diff --git a/essentials-framework/Essentials DM/Essentials_DM/PepperDash_Essentials_DM.csproj b/essentials-framework/Essentials DM/Essentials_DM/PepperDash_Essentials_DM.csproj index 7c8b163a..7d6b7bde 100644 --- a/essentials-framework/Essentials DM/Essentials_DM/PepperDash_Essentials_DM.csproj +++ b/essentials-framework/Essentials DM/Essentials_DM/PepperDash_Essentials_DM.csproj @@ -48,18 +48,15 @@ False - ..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.DeviceSupport.dll - True + ..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.DeviceSupport.dll False - ..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.DM.dll - True + ..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.DM.dll False - ..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.UI.dll - True + ..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.UI.dll @@ -78,8 +75,7 @@ False - ..\..\..\..\ProgramData\Crestron\SDK\SimplSharpNewtonsoft.dll - True + ..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpNewtonsoft.dll False @@ -88,8 +84,7 @@ False - ..\..\..\..\ProgramData\Crestron\SDK\SimplSharpReflectionInterface.dll - True + ..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpReflectionInterface.dll diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/iHasDirectory.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/iHasDirectory.cs index 3a984ab2..3a59b62d 100644 --- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/iHasDirectory.cs +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/iHasDirectory.cs @@ -40,9 +40,15 @@ namespace PepperDash.Essentials.Devices.Common.Codec /// /// Tracks the directory browse history when browsing beyond the root directory /// + [Obsolete("Please use the Stack-based history instead")] List DirectoryBrowseHistory { get; } } + public interface IHasDirectoryHistoryStack : IHasDirectory + { + Stack DirectoryBrowseHistoryStack { get; } + } + /// /// /// @@ -147,6 +153,9 @@ namespace PepperDash.Essentials.Devices.Common.Codec [JsonProperty("name")] public string Name { get; set; } + + [JsonProperty("parentFolderId")] + public string ParentFolderId { get; set; } } /// @@ -157,8 +166,6 @@ namespace PepperDash.Essentials.Devices.Common.Codec [JsonProperty("contacts")] public List Contacts { get; set; } - [JsonProperty("parentFolderId")] - public string ParentFolderId { get; set; } public DirectoryFolder() { @@ -177,6 +184,8 @@ namespace PepperDash.Essentials.Devices.Common.Codec [JsonProperty("title")] public string Title { get; set; } + + [JsonProperty("contactMethods")] public List ContactMethods { get; set; } diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/iHasScheduleAwareness.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/iHasScheduleAwareness.cs index 57839471..7d6acdc2 100644 --- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/iHasScheduleAwareness.cs +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/iHasScheduleAwareness.cs @@ -24,24 +24,32 @@ namespace PepperDash.Essentials.Devices.Common.Codec public class CodecScheduleAwareness { - List _Meetings; + List _meetings; public event EventHandler MeetingEventChange; public event EventHandler MeetingsListHasChanged; - /// + private int _meetingWarningMinutes = 5; + + public int MeetingWarningMinutes + { + get { return _meetingWarningMinutes; } + set { _meetingWarningMinutes = value; } + } + + /// /// Setter triggers MeetingsListHasChanged event /// public List Meetings { get { - return _Meetings; + return _meetings; } set { - _Meetings = value; + _meetings = value; var handler = MeetingsListHasChanged; if (handler != null) @@ -51,13 +59,20 @@ namespace PepperDash.Essentials.Devices.Common.Codec } } - private CTimer ScheduleChecker; + private CTimer _scheduleChecker; public CodecScheduleAwareness() { Meetings = new List(); - ScheduleChecker = new CTimer(CheckSchedule, null, 1000, 1000); + _scheduleChecker = new CTimer(CheckSchedule, null, 1000, 1000); + } + + public CodecScheduleAwareness(long pollTime) + { + Meetings = new List(); + + _scheduleChecker = new CTimer(CheckSchedule, null, pollTime, pollTime); } private void OnMeetingChange(eMeetingEventChangeType changeType, Meeting meeting) @@ -74,9 +89,9 @@ namespace PepperDash.Essentials.Devices.Common.Codec // Iterate the meeting list and check if any meeting need to do anythingk const double meetingTimeEpsilon = 0.0001; - foreach (Meeting m in Meetings) + foreach (var m in Meetings) { - eMeetingEventChangeType changeType = eMeetingEventChangeType.Unkown; + var changeType = eMeetingEventChangeType.Unkown; if (m.TimeToMeetingStart.TotalMinutes <= m.MeetingWarningMinutes.TotalMinutes) // Meeting is about to start changeType = eMeetingEventChangeType.MeetingStartWarning; @@ -100,12 +115,17 @@ namespace PepperDash.Essentials.Devices.Common.Codec /// public class Meeting { - public TimeSpan MeetingWarningMinutes = TimeSpan.FromMinutes(5); + public int MinutesBeforeMeeting; public string Id { get; set; } public string Organizer { get; set; } public string Title { get; set; } public string Agenda { get; set; } + + public TimeSpan MeetingWarningMinutes + { + get { return TimeSpan.FromMinutes(MinutesBeforeMeeting); } + } public TimeSpan TimeToMeetingStart { get @@ -134,7 +154,7 @@ namespace PepperDash.Essentials.Devices.Common.Codec { get { - return StartTime.AddMinutes(-5) <= DateTime.Now + return StartTime.AddMinutes(-MinutesBeforeMeeting) <= DateTime.Now && DateTime.Now <= EndTime; //.AddMinutes(-5); } } 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 d18cb6ce..f115f401 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 @@ -48,23 +48,19 @@ False - ..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.DeviceSupport.dll - True + ..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.DeviceSupport.dll False - ..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.Gateways.dll - True + ..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.Gateways.dll False - ..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.GeneralIO.dll - True + ..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.GeneralIO.dll False - ..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.Lighting.dll - True + ..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.Lighting.dll @@ -83,8 +79,7 @@ False - ..\..\..\..\ProgramData\Crestron\SDK\SimplSharpNewtonsoft.dll - True + ..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpNewtonsoft.dll False @@ -93,8 +88,7 @@ False - ..\..\..\..\ProgramData\Crestron\SDK\SimplSharpReflectionInterface.dll - True + ..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpReflectionInterface.dll @@ -126,6 +120,8 @@ + + diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Factory/DeviceFactory.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Factory/DeviceFactory.cs index dd99fe34..a18d3b65 100644 --- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Factory/DeviceFactory.cs +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Factory/DeviceFactory.cs @@ -17,7 +17,6 @@ using PepperDash.Essentials.Core.CrestronIO; using PepperDash.Essentials.Devices.Common; using PepperDash.Essentials.Devices.Common.DSP; using PepperDash.Essentials.Devices.Common.VideoCodec; -using PepperDash.Essentials.Core; using PepperDash.Essentials.Devices.Common.Environment; namespace PepperDash.Essentials.Devices.Common 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 643e8b83..55e6bf11 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 @@ -5,12 +5,13 @@ 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; @@ -1475,6 +1476,11 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco SendText("xCommand Standby Deactivate"); } + public override void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge) + { + throw new NotImplementedException(); + } + /// /// Reboots the codec /// diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasParticipants.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasParticipants.cs new file mode 100644 index 00000000..e0f1d1a3 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasParticipants.cs @@ -0,0 +1,59 @@ +using System; +using System.Collections.Generic; + +namespace PepperDash.Essentials.Devices.Common.VideoCodec.Interfaces +{ + public interface IHasParticipants + { + CodecParticipants Participants { get; } + } + + public interface IHasParticipantVideoMute:IHasParticipants + { + void MuteVideoForParticipant(int userId); + void UnmuteVideoForParticipant(int userId); + void ToggleVideoForParticipant(int userId); + } + + public interface IHasParticipantAudioMute:IHasParticipantVideoMute + { + void MuteAudioForParticipant(int userId); + void UnmuteAudioForParticipant(int userId); + void ToggleAudioForParticipant(int userId); + } + + public class CodecParticipants + { + private List _currentParticipants; + + public List CurrentParticipants { + get { return _currentParticipants; } + set + { + _currentParticipants = value; + var handler = ParticipantsListHasChanged; + + if(handler == null) return; + + handler(this, new EventArgs()); + } + } + + public event EventHandler ParticipantsListHasChanged; + + public CodecParticipants() + { + _currentParticipants = new List(); + } + } + + public class Participant + { + public bool IsHost { get; set; } + public string Name { get; set; } + public bool CanMuteVideo { get; set; } + public bool CanUnmuteVideo { get; set; } + public bool VideoMuteFb { get; set; } + public bool AudioMuteFb { get; set; } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasSelfviewPosition.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasSelfviewPosition.cs new file mode 100644 index 00000000..5360b80a --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasSelfviewPosition.cs @@ -0,0 +1,14 @@ +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Devices.Common.VideoCodec.Cisco; + +namespace PepperDash.Essentials.Core.DeviceTypeInterfaces +{ + public interface IHasSelfviewPosition + { + StringFeedback SelfviewPipPositionFeedback { get; } + + void SelfviewPipPositionSet(CodecCommandWithLabel position); + + void SelfviewPipPositionToggle(); + } +} \ 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 dc32ecd8..c4bfa298 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 @@ -3,9 +3,10 @@ using System.Collections.Generic; using System.Linq; using System.Text; using Crestron.SimplSharp; - +using Crestron.SimplSharpPro.DeviceSupport; using PepperDash.Core; using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.Bridges; using PepperDash.Essentials.Core.Config; using PepperDash.Essentials.Core.Routing; using PepperDash.Essentials.Devices.Common.Codec; @@ -226,6 +227,11 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec _StandbyIsOn = false; } + public override void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge) + { + throw new NotImplementedException(); + } + /// /// Called by routing to make it happen /// 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 1f8938f6..8b7b3bce 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 @@ -2,85 +2,37 @@ using System.Collections.Generic; using System.Linq; using System.Text; -using Crestron.SimplSharp; - +using Crestron.SimplSharp.CrestronIO; +using Crestron.SimplSharp.Ssh; +using Crestron.SimplSharpPro.DeviceSupport; using PepperDash.Core; +using PepperDash.Core.Intersystem; +using PepperDash.Core.Intersystem.Tokens; +using PepperDash.Core.WebApi.Presets; using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.Bridges; using PepperDash.Essentials.Core.Config; using PepperDash.Essentials.Core.Devices; +using PepperDash.Essentials.Core.DeviceTypeInterfaces; using PepperDash.Essentials.Core.Routing; -using PepperDash.Essentials.Devices.Common; +using PepperDash.Essentials.Devices.Common.Cameras; using PepperDash.Essentials.Devices.Common.Codec; +using PepperDash.Essentials.Devices.Common.VideoCodec.Interfaces; +using PepperDash_Essentials_Core.Bridges.JoinMaps; +using PepperDash_Essentials_Core.DeviceTypeInterfaces; +using Feedback = PepperDash.Essentials.Core.Feedback; namespace PepperDash.Essentials.Devices.Common.VideoCodec { public abstract class VideoCodecBase : ReconfigurableDevice, IRoutingInputsOutputs, - IUsageTracking, IHasDialer, IHasContentSharing, ICodecAudio, iVideoCodecInfo + IUsageTracking, IHasDialer, IHasContentSharing, ICodecAudio, iVideoCodecInfo, IBridgeAdvanced { - /// - /// Fires when the status of any active, dialing, or incoming call changes or is new - /// - public event EventHandler CallStatusChange; - - public event EventHandler IsReadyChange; - - public IBasicCommunication Communication { get; protected set; } - - #region IUsageTracking Members - - /// - /// This object can be added by outside users of this class to provide usage tracking - /// for various services - /// - public UsageTracking UsageTracker { get; set; } - - #endregion - - /// - /// An internal pseudo-source that is routable and connected to the osd input - /// - public DummyRoutingInputsDevice OsdSource { get; protected set; } - - public RoutingPortCollection InputPorts { get; private set; } - - public RoutingPortCollection OutputPorts { get; private set; } - - /// - /// Returns true when any call is not in state Unknown, Disconnecting, Disconnected - /// - public bool IsInCall - { - get - { - bool value; - - if (ActiveCalls != null) - value = ActiveCalls.Any(c => c.IsActiveCall); - else - value = false; - return value; - } - } - - public BoolFeedback StandbyIsOnFeedback { get; private set; } - - abstract protected Func PrivacyModeIsOnFeedbackFunc { get; } - abstract protected Func VolumeLevelFeedbackFunc { get; } - abstract protected Func MuteFeedbackFunc { get; } - abstract protected Func StandbyIsOnFeedbackFunc { get; } - - public List ActiveCalls { get; set; } - - public VideoCodecInfo CodecInfo { get; protected set; } - - public bool ShowSelfViewByDefault { get; protected set; } - - - public bool IsReady { get; protected set; } - - public VideoCodecBase(DeviceConfig config) + private const int XSigEncoding = 28591; + private readonly byte[] _clearBytes = XSigHelpers.ClearOutputs(); + protected VideoCodecBase(DeviceConfig config) : base(config) { + StandbyIsOnFeedback = new BoolFeedback(StandbyIsOnFeedbackFunc); PrivacyModeIsOnFeedback = new BoolFeedback(PrivacyModeIsOnFeedbackFunc); VolumeLevelFeedback = new IntFeedback(VolumeLevelFeedbackFunc); @@ -90,85 +42,47 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec InputPorts = new RoutingPortCollection(); OutputPorts = new RoutingPortCollection(); - + ActiveCalls = new List(); } - #region IHasDialer Members + public IBasicCommunication Communication { get; protected set; } - public abstract void Dial(string number); - public abstract void Dial(Meeting meeting); - public virtual void Dial(IInvitableContact contact) - { + /// + /// An internal pseudo-source that is routable and connected to the osd input + /// + public DummyRoutingInputsDevice OsdSource { get; protected set; } - } - public abstract void EndCall(CodecActiveCallItem call); - public abstract void EndAllCalls(); - public abstract void AcceptCall(CodecActiveCallItem call); - public abstract void RejectCall(CodecActiveCallItem call); - public abstract void SendDtmf(string s); + public BoolFeedback StandbyIsOnFeedback { get; private set; } - #endregion + protected abstract Func PrivacyModeIsOnFeedbackFunc { get; } + protected abstract Func VolumeLevelFeedbackFunc { get; } + protected abstract Func MuteFeedbackFunc { get; } + protected abstract Func StandbyIsOnFeedbackFunc { get; } + + public List ActiveCalls { get; set; } + + public bool ShowSelfViewByDefault { get; protected set; } + + protected bool SupportsCameraOff; + protected bool SupportsCameraAutoMode; + + public bool IsReady { get; protected set; } public virtual List Feedbacks { get { return new List - { + { PrivacyModeIsOnFeedback, SharingSourceFeedback - }; + }; } } - public abstract void ExecuteSwitch(object selector); - - /// - /// Helper method to fire CallStatusChange event with old and new status - /// - protected void SetNewCallStatusAndFireCallStatusChange(eCodecCallStatus newStatus, CodecActiveCallItem call) - { - call.Status = newStatus; - - OnCallStatusChange(call); - - } - - /// - /// - /// - /// - /// - /// - protected void OnCallStatusChange(CodecActiveCallItem item) - { - var handler = CallStatusChange; - if (handler != null) - handler(this, new CodecCallStatusItemChangeEventArgs(item)); - - if (AutoShareContentWhileInCall) - StartSharing(); - - if (UsageTracker != null) - { - if (IsInCall && !UsageTracker.UsageTrackingStarted) - UsageTracker.StartDeviceUsage(); - else if (UsageTracker.UsageTrackingStarted && !IsInCall) - UsageTracker.EndDeviceUsage(); - } - } - - /// - /// Sets IsReady property and fires the event. Used for dependent classes to sync up their data. - /// - protected void SetIsReady() - { - IsReady = true; - var h = IsReadyChange; - if(h != null) - h(this, new EventArgs()); - } + protected abstract Func SharingSourceFeedbackFunc { get; } + protected abstract Func SharingContentIsOnFeedbackFunc { get; } #region ICodecAudio Members @@ -197,7 +111,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec #endregion - #region IHasSharing Members + #region IHasContentSharing Members public abstract void StartSharing(); public abstract void StopSharing(); @@ -207,12 +121,124 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec public StringFeedback SharingSourceFeedback { get; private set; } public BoolFeedback SharingContentIsOnFeedback { get; private set; } - abstract protected Func SharingSourceFeedbackFunc { get; } - abstract protected Func SharingContentIsOnFeedbackFunc { get; } + #endregion + #region IHasDialer Members + + /// + /// Fires when the status of any active, dialing, or incoming call changes or is new + /// + public event EventHandler CallStatusChange; + + /// + /// Returns true when any call is not in state Unknown, Disconnecting, Disconnected + /// + public bool IsInCall + { + get + { + var value = ActiveCalls != null && ActiveCalls.Any(c => c.IsActiveCall); + return value; + } + } + + public abstract void Dial(string number); + public abstract void EndCall(CodecActiveCallItem call); + public abstract void EndAllCalls(); + public abstract void AcceptCall(CodecActiveCallItem call); + public abstract void RejectCall(CodecActiveCallItem call); + public abstract void SendDtmf(string s); #endregion + #region IRoutingInputsOutputs Members + + public RoutingPortCollection InputPorts { get; private set; } + + public RoutingPortCollection OutputPorts { get; private set; } + + #endregion + + #region IUsageTracking Members + + /// + /// This object can be added by outside users of this class to provide usage tracking + /// for various services + /// + public UsageTracking UsageTracker { get; set; } + + #endregion + + #region iVideoCodecInfo Members + + public VideoCodecInfo CodecInfo { get; protected set; } + + #endregion + + public event EventHandler IsReadyChange; + public abstract void Dial(Meeting meeting); + + public virtual void Dial(IInvitableContact contact) + { + } + + public abstract void ExecuteSwitch(object selector); + + /// + /// Helper method to fire CallStatusChange event with old and new status + /// + protected void SetNewCallStatusAndFireCallStatusChange(eCodecCallStatus newStatus, CodecActiveCallItem call) + { + call.Status = newStatus; + + OnCallStatusChange(call); + } + + /// + /// + /// + /// + /// + /// + protected virtual void OnCallStatusChange(CodecActiveCallItem item) + { + var handler = CallStatusChange; + if (handler != null) + { + handler(this, new CodecCallStatusItemChangeEventArgs(item)); + } + + if (AutoShareContentWhileInCall) + { + StartSharing(); + } + + if (UsageTracker != null) + { + if (IsInCall && !UsageTracker.UsageTrackingStarted) + { + UsageTracker.StartDeviceUsage(); + } + else if (UsageTracker.UsageTrackingStarted && !IsInCall) + { + UsageTracker.EndDeviceUsage(); + } + } + } + + /// + /// Sets IsReady property and fires the event. Used for dependent classes to sync up their data. + /// + protected void SetIsReady() + { + IsReady = true; + var h = IsReadyChange; + if (h != null) + { + h(this, new EventArgs()); + } + } + // **** DEBUGGING THINGS **** /// /// @@ -221,7 +247,9 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec { var sb = new StringBuilder(); foreach (var c in ActiveCalls) + { sb.AppendFormat("{0} {1} -- {2} {3}\n", c.Id, c.Number, c.Name, c.Status); + } Debug.Console(1, this, "\n{0}\n", sb.ToString()); } @@ -229,6 +257,886 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec public abstract void StandbyDeactivate(); + #region Implementation of IBridgeAdvanced + + public abstract void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge); + + protected void LinkVideoCodecToApi(VideoCodecBase codec, BasicTriList trilist, uint joinStart, string joinMapKey, + EiscApiAdvanced bridge) + { + var joinMap = new VideoCodecControllerJoinMap(joinStart); + + var customJoins = JoinMapHelper.TryGetJoinMapAdvancedForDevice(joinMapKey); + + if (customJoins != null) + { + joinMap.SetCustomJoinData(customJoins); + } + + if (bridge != null) + { + bridge.AddJoinMap(Key, joinMap); + } + + Debug.Console(1, this, "Linking to Trilist {0}", trilist.ID.ToString("X")); + + + + LinkVideoCodecDtmfToApi(trilist, joinMap); + + LinkVideoCodecCallControlsToApi(trilist, joinMap); + + LinkVideoCodecContentSharingToApi(trilist, joinMap); + + LinkVideoCodecPrivacyToApi(trilist, joinMap); + + LinkVideoCodecVolumeToApi(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) + { + LinkVideoCodecSelfviewToApi(codec as IHasCodecSelfView, trilist, joinMap); + } + + if (codec is IHasCameraAutoMode) + { + trilist.SetBool(joinMap.CameraSupportsAutoMode.JoinNumber, SupportsCameraAutoMode); + LinkVideoCodecCameraModeToApi(codec as IHasCameraAutoMode, trilist, joinMap); + } + + if (codec is IHasCameraOff) + { + trilist.SetBool(joinMap.CameraSupportsOffMode.JoinNumber, SupportsCameraOff); + LinkVideoCodecCameraOffToApi(codec as IHasCameraOff, trilist, joinMap); + } + + if (codec is IHasCodecLayouts) + { + LinkVideoCodecCameraLayoutsToApi(codec as IHasCodecLayouts, trilist, joinMap); + } + + if (codec is IHasSelfviewPosition) + { + LinkVideoCodecSelfviewPositionToApi(codec as IHasSelfviewPosition, trilist, joinMap); + } + + if (codec is IHasDirectory) + { + LinkVideoCodecDirectoryToApi(codec as IHasDirectory, trilist, joinMap); + } + + if (codec is IHasScheduleAwareness) + { + LinkVideoCodecScheduleToApi(codec as IHasScheduleAwareness, trilist, joinMap); + } + + if (codec is IHasParticipants) + { + LinkVideoCodecParticipantsToApi(codec as IHasParticipants, trilist, joinMap); + } + + if (codec is IHasFarEndContentStatus) + { + (codec as IHasFarEndContentStatus).ReceivingContent.LinkInputSig(trilist.BooleanInput[joinMap.RecievingContent.JoinNumber]); + } + + if (codec is IHasPhoneDialing) + { + LinkVideoCodecPhoneToApi(codec as IHasPhoneDialing, trilist, joinMap); + } + + trilist.OnlineStatusChange += (device, args) => + { + if (!args.DeviceOnLine) return; + + if (codec is IHasDirectory) + { + (codec as IHasDirectory).SetCurrentDirectoryToRoot(); + } + + if (codec is IHasScheduleAwareness) + { + (codec as IHasScheduleAwareness).GetSchedule(); + } + + if (codec is IHasParticipants) + { + UpdateParticipantsXSig((codec as IHasParticipants).Participants.CurrentParticipants); + } + + if (codec is IHasCameraAutoMode) + { + trilist.SetBool(joinMap.CameraSupportsAutoMode.JoinNumber, true); + + (codec as IHasCameraAutoMode).CameraAutoModeIsOnFeedback.FireUpdate(); + } + + if (codec is IHasCodecSelfView) + { + (codec as IHasCodecSelfView).SelfviewIsOnFeedback.FireUpdate(); + } + + if (codec is IHasCameraAutoMode) + { + (codec as IHasCameraAutoMode).CameraAutoModeIsOnFeedback.FireUpdate(); + } + + if (codec is IHasCameraOff) + { + (codec as IHasCameraOff).CameraIsOffFeedback.FireUpdate(); + } + + if (codec is IHasPhoneDialing) + { + (codec as IHasPhoneDialing).PhoneOffHookFeedback.FireUpdate(); + } + + SharingContentIsOnFeedback.FireUpdate(); + + trilist.SetBool(joinMap.HookState.JoinNumber, IsInCall); + + trilist.SetString(joinMap.CurrentCallData.JoinNumber, UpdateCallStatusXSig()); + }; + } + + private void LinkVideoCodecPhoneToApi(IHasPhoneDialing codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) + { + codec.PhoneOffHookFeedback.LinkInputSig(trilist.BooleanInput[joinMap.PhoneHookState.JoinNumber]); + + trilist.SetSigFalseAction(joinMap.DialPhone.JoinNumber, + () => codec.DialPhoneCall(trilist.StringOutput[joinMap.PhoneDialString.JoinNumber].StringValue)); + + trilist.SetSigFalseAction(joinMap.HangUpPhone.JoinNumber, codec.EndPhoneCall); + } + + private void LinkVideoCodecSelfviewPositionToApi(IHasSelfviewPosition codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) + { + trilist.SetSigFalseAction(joinMap.SelfviewPosition.JoinNumber, codec.SelfviewPipPositionToggle); + + codec.SelfviewPipPositionFeedback.LinkInputSig(trilist.StringInput[joinMap.SelfviewPositionFb.JoinNumber]); + } + + private void LinkVideoCodecCameraOffToApi(IHasCameraOff codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) + { + trilist.SetSigFalseAction(joinMap.CameraModeOff.JoinNumber, codec.CameraOff); + + codec.CameraIsOffFeedback.OutputChange += (o, a) => + { + if (a.BoolValue) + { + trilist.SetBool(joinMap.CameraModeOff.JoinNumber, true); + trilist.SetBool(joinMap.CameraModeManual.JoinNumber, false); + trilist.SetBool(joinMap.CameraModeAuto.JoinNumber, false); + return; + } + + trilist.SetBool(joinMap.CameraModeOff.JoinNumber, false); + + var autoCodec = codec as IHasCameraAutoMode; + + if (autoCodec == null) return; + + trilist.SetBool(joinMap.CameraModeAuto.JoinNumber, autoCodec.CameraAutoModeIsOnFeedback.BoolValue); + trilist.SetBool(joinMap.CameraModeManual.JoinNumber, !autoCodec.CameraAutoModeIsOnFeedback.BoolValue); + }; + + if (codec.CameraIsOffFeedback.BoolValue) + { + trilist.SetBool(joinMap.CameraModeOff.JoinNumber, true); + trilist.SetBool(joinMap.CameraModeManual.JoinNumber, false); + trilist.SetBool(joinMap.CameraModeAuto.JoinNumber, false); + return; + } + + trilist.SetBool(joinMap.CameraModeOff.JoinNumber, false); + + var autoModeCodec = codec as IHasCameraAutoMode; + + if (autoModeCodec == null) return; + + trilist.SetBool(joinMap.CameraModeAuto.JoinNumber, autoModeCodec.CameraAutoModeIsOnFeedback.BoolValue); + trilist.SetBool(joinMap.CameraModeManual.JoinNumber, !autoModeCodec.CameraAutoModeIsOnFeedback.BoolValue); + } + + private void LinkVideoCodecVolumeToApi(BasicTriList trilist, VideoCodecControllerJoinMap joinMap) + { + MuteFeedback.LinkInputSig(trilist.BooleanInput[joinMap.VolumeMuteOn.JoinNumber]); + MuteFeedback.LinkComplementInputSig(trilist.BooleanInput[joinMap.VolumeMuteOff.JoinNumber]); + + trilist.SetSigFalseAction(joinMap.VolumeMuteOn.JoinNumber, MuteOn); + trilist.SetSigFalseAction(joinMap.VolumeMuteOff.JoinNumber, MuteOff); + trilist.SetSigFalseAction(joinMap.VolumeMuteToggle.JoinNumber, MuteToggle); + + VolumeLevelFeedback.LinkInputSig(trilist.UShortInput[joinMap.VolumeLevel.JoinNumber]); + + trilist.SetBoolSigAction(joinMap.VolumeUp.JoinNumber, VolumeUp); + trilist.SetBoolSigAction(joinMap.VolumeDown.JoinNumber, VolumeDown); + + trilist.SetUShortSigAction(joinMap.VolumeLevel.JoinNumber, SetVolume); + + } + + private void LinkVideoCodecPrivacyToApi(BasicTriList trilist, VideoCodecControllerJoinMap joinMap) + { + PrivacyModeIsOnFeedback.LinkInputSig(trilist.BooleanInput[joinMap.MicMuteOn.JoinNumber]); + PrivacyModeIsOnFeedback.LinkComplementInputSig(trilist.BooleanInput[joinMap.MicMuteOff.JoinNumber]); + + trilist.SetSigFalseAction(joinMap.MicMuteOn.JoinNumber, PrivacyModeOn); + trilist.SetSigFalseAction(joinMap.MicMuteOff.JoinNumber, PrivacyModeOff); + trilist.SetSigFalseAction(joinMap.MicMuteToggle.JoinNumber, PrivacyModeToggle); + } + + private void LinkVideoCodecCommMonitorToApi(ICommunicationMonitor codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) + { + codec.CommunicationMonitor.IsOnlineFeedback.LinkInputSig(trilist.BooleanInput[joinMap.IsOnline.JoinNumber]); + } + + private void LinkVideoCodecParticipantsToApi(IHasParticipants codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) + { + codec.Participants.ParticipantsListHasChanged += (sender, args) => + { + string participantsXSig; + + if (codec.Participants.CurrentParticipants.Count == 0) + { + participantsXSig = Encoding.GetEncoding(XSigEncoding).GetString(_clearBytes, 0, _clearBytes.Length); + trilist.SetString(joinMap.CurrentParticipants.JoinNumber, participantsXSig); + trilist.SetUshort(joinMap.ParticipantCount.JoinNumber, (ushort)codec.Participants.CurrentParticipants.Count); + return; + } + + participantsXSig = UpdateParticipantsXSig(codec.Participants.CurrentParticipants); + + trilist.SetString(joinMap.CurrentParticipants.JoinNumber, participantsXSig); + + trilist.SetUshort(joinMap.ParticipantCount.JoinNumber, (ushort) codec.Participants.CurrentParticipants.Count); + }; + } + + private string UpdateParticipantsXSig(List currentParticipants) + { + const int maxParticipants = 50; + const int maxDigitals = 5; + const int maxStrings = 1; + const int offset = maxDigitals + maxStrings; + var digitalIndex = maxStrings * maxParticipants; //15 + var stringIndex = 0; + var meetingIndex = 0; + + var tokenArray = new XSigToken[maxParticipants * offset]; + + foreach (var participant in currentParticipants) + { + if (meetingIndex >= maxParticipants * offset) break; + + //digitals + tokenArray[digitalIndex] = new XSigDigitalToken(digitalIndex + 1, participant.AudioMuteFb); + tokenArray[digitalIndex + 1] = new XSigDigitalToken(digitalIndex + 2, participant.VideoMuteFb); + tokenArray[digitalIndex + 2] = new XSigDigitalToken(digitalIndex + 3, participant.CanMuteVideo); + tokenArray[digitalIndex + 3] = new XSigDigitalToken(digitalIndex + 4, participant.CanUnmuteVideo); + tokenArray[digitalIndex + 4] = new XSigDigitalToken(digitalIndex + 5, participant.IsHost); + + //serials + tokenArray[stringIndex] = new XSigSerialToken(stringIndex + 1, participant.Name); + + digitalIndex += maxDigitals; + meetingIndex += offset; + stringIndex += maxStrings; + } + + while (meetingIndex < maxParticipants*offset) + { + tokenArray[digitalIndex] = new XSigDigitalToken(digitalIndex + 1, false); + tokenArray[digitalIndex + 1] = new XSigDigitalToken(digitalIndex + 2, false); + tokenArray[digitalIndex + 2] = new XSigDigitalToken(digitalIndex + 3, false); + tokenArray[digitalIndex + 3] = new XSigDigitalToken(digitalIndex + 4, false); + tokenArray[digitalIndex + 4] = new XSigDigitalToken(digitalIndex + 5, false); + + //serials + tokenArray[stringIndex] = new XSigSerialToken(stringIndex + 1, String.Empty); + + digitalIndex += maxDigitals; + meetingIndex += offset; + stringIndex += maxStrings; + } + + return GetXSigString(tokenArray); + } + + private void LinkVideoCodecContentSharingToApi(BasicTriList trilist, VideoCodecControllerJoinMap joinMap) + { + SharingContentIsOnFeedback.LinkInputSig(trilist.BooleanInput[joinMap.SourceShareStart.JoinNumber]); + SharingContentIsOnFeedback.LinkComplementInputSig(trilist.BooleanInput[joinMap.SourceShareEnd.JoinNumber]); + + SharingSourceFeedback.LinkInputSig(trilist.StringInput[joinMap.CurrentSource.JoinNumber]); + + trilist.SetSigFalseAction(joinMap.SourceShareStart.JoinNumber, StartSharing); + trilist.SetSigFalseAction(joinMap.SourceShareEnd.JoinNumber, StopSharing); + + trilist.SetBoolSigAction(joinMap.SourceShareAutoStart.JoinNumber, (b) => AutoShareContentWhileInCall = b); + } + + private void LinkVideoCodecScheduleToApi(IHasScheduleAwareness codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) + { + trilist.SetSigFalseAction(joinMap.UpdateMeetings.JoinNumber, codec.GetSchedule); + + trilist.SetUShortSigAction(joinMap.MinutesBeforeMeetingStart.JoinNumber, (i) => + { + codec.CodecSchedule.MeetingWarningMinutes = i; + }); + + codec.CodecSchedule.MeetingsListHasChanged += (sender, args) => UpdateMeetingsList(codec, trilist, joinMap); + + codec.CodecSchedule.MeetingEventChange += + (sender, args) => + { + if (args.ChangeType == eMeetingEventChangeType.MeetingStartWarning) + { + UpdateMeetingsList(codec, trilist, joinMap); + } + }; + } + + private void UpdateMeetingsList(IHasScheduleAwareness codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) + { + var currentTime = DateTime.Now; + var currentMeetings = + codec.CodecSchedule.Meetings.Where(m => m.StartTime >= currentTime || m.EndTime >= currentTime).ToList(); + + var meetingsData = UpdateMeetingsListXSig(currentMeetings); + + trilist.SetString(joinMap.Schedule.JoinNumber, meetingsData); + + trilist.SetSigFalseAction(joinMap.DialMeeting1.JoinNumber, () => + { + if(codec.CodecSchedule.Meetings[0] != null) + Dial(codec.CodecSchedule.Meetings[0]); + }); + trilist.SetSigFalseAction(joinMap.DialMeeting2.JoinNumber, () => + { + if (codec.CodecSchedule.Meetings[1] != null) + Dial(codec.CodecSchedule.Meetings[1]); + }); + trilist.SetSigFalseAction(joinMap.DialMeeting3.JoinNumber, () => + { + if (codec.CodecSchedule.Meetings[2] != null) + Dial(codec.CodecSchedule.Meetings[2]); + }); + + trilist.SetUshort(joinMap.MeetingCount.JoinNumber, (ushort)currentMeetings.Count); + } + + private string UpdateMeetingsListXSig(List meetings) + { + const int maxMeetings = 3; + const int maxDigitals = 2; + const int maxStrings = 7; + const int offset = maxDigitals + maxStrings; + var digitalIndex = maxStrings*maxMeetings; //15 + var stringIndex = 0; + var meetingIndex = 0; + + var tokenArray = new XSigToken[maxMeetings*offset]; + /* + * Digitals + * IsJoinable - 1 + * IsDialable - 2 + * + * Serials + * Organizer - 1 + * Title - 2 + * Start Date - 3 + * Start Time - 4 + * End Date - 5 + * End Time - 6 + */ + + + foreach(var meeting in meetings) + { + var currentTime = DateTime.Now; + + if(meeting.StartTime < currentTime && meeting.EndTime < currentTime) continue; + + if (meetingIndex >= maxMeetings*offset) + { + Debug.Console(2, this, "Max Meetings reached"); + break;} + + //digitals + tokenArray[digitalIndex] = new XSigDigitalToken(digitalIndex + 1, meeting.Joinable); + tokenArray[digitalIndex + 1] = new XSigDigitalToken(digitalIndex + 2, meeting.Id != "0"); + + //serials + tokenArray[stringIndex] = new XSigSerialToken(stringIndex + 1, meeting.Organizer); + tokenArray[stringIndex + 1] = new XSigSerialToken(stringIndex + 2, meeting.Title); + tokenArray[stringIndex + 2] = new XSigSerialToken(stringIndex + 3, meeting.StartTime.ToShortDateString()); + tokenArray[stringIndex + 3] = new XSigSerialToken(stringIndex + 4, meeting.StartTime.ToShortTimeString()); + tokenArray[stringIndex + 4] = new XSigSerialToken(stringIndex + 5, meeting.EndTime.ToShortDateString()); + tokenArray[stringIndex + 5] = new XSigSerialToken(stringIndex + 6, meeting.EndTime.ToShortTimeString()); + tokenArray[stringIndex + 6] = new XSigSerialToken(stringIndex + 7, meeting.Id); + + + digitalIndex += maxDigitals; + meetingIndex += offset; + stringIndex += maxStrings; + } + + while (meetingIndex < maxMeetings*offset) + { + Debug.Console(2, this, "Clearing unused data. Meeting Index: {0} MaxMeetings * Offset: {1}", + meetingIndex, maxMeetings*offset); + + //digitals + tokenArray[digitalIndex] = new XSigDigitalToken(digitalIndex + 1, false); + tokenArray[digitalIndex + 1] = new XSigDigitalToken(digitalIndex + 2, false); + + //serials + tokenArray[stringIndex] = new XSigSerialToken(stringIndex + 1, String.Empty); + tokenArray[stringIndex + 1] = new XSigSerialToken(stringIndex + 2, String.Empty); + tokenArray[stringIndex + 2] = new XSigSerialToken(stringIndex + 3, String.Empty); + tokenArray[stringIndex + 3] = new XSigSerialToken(stringIndex + 4, String.Empty); + tokenArray[stringIndex + 4] = new XSigSerialToken(stringIndex + 5, String.Empty); + tokenArray[stringIndex + 5] = new XSigSerialToken(stringIndex + 6, String.Empty); + tokenArray[stringIndex + 6] = new XSigSerialToken(stringIndex + 7, String.Empty); + + digitalIndex += maxDigitals; + meetingIndex += offset; + stringIndex += maxStrings; + } + + return GetXSigString(tokenArray); + } + + 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.SetStringSigAction(joinMap.DirectorySearchString.JoinNumber, codec.SearchDirectory); + + trilist.SetUShortSigAction(joinMap.DirectorySelectRow.JoinNumber, (i) => SelectDirectoryEntry(codec, i)); + + trilist.SetSigFalseAction(joinMap.DirectoryRoot.JoinNumber, codec.SetCurrentDirectoryToRoot); + + trilist.SetSigFalseAction(joinMap.DirectoryFolderBack.JoinNumber, codec.GetDirectoryParentFolderContents); + + codec.DirectoryResultReturned += (sender, args) => + { + trilist.SetUshort(joinMap.DirectoryRowCount.JoinNumber, (ushort) args.Directory.CurrentDirectoryResults.Count); + + var clearBytes = XSigHelpers.ClearOutputs(); + + trilist.SetString(joinMap.DirectoryEntries.JoinNumber, + Encoding.GetEncoding(XSigEncoding).GetString(clearBytes, 0, clearBytes.Length)); + var directoryXSig = UpdateDirectoryXSig(args.Directory, !codec.CurrentDirectoryResultIsNotDirectoryRoot.BoolValue); + + trilist.SetString(joinMap.DirectoryEntries.JoinNumber, directoryXSig); + }; + } + + private void SelectDirectoryEntry(IHasDirectory codec, ushort i) + { + var entry = codec.CurrentDirectoryResult.CurrentDirectoryResults[i - 1]; + + if (entry is DirectoryFolder) + { + codec.GetDirectoryFolderContents(entry.FolderId); + return; + } + + var dialableEntry = entry as IInvitableContact; + + if (dialableEntry != null) + { + Dial(dialableEntry); + return; + } + + var entryToDial = entry as DirectoryContact; + + if (entryToDial == null) return; + + Dial(entryToDial.ContactMethods[0].Number); + } + + private string UpdateDirectoryXSig(CodecDirectory directory, bool isRoot) + { + var contactIndex = 1; + var tokenArray = new XSigToken[directory.CurrentDirectoryResults.Count]; + + foreach(var entry in directory.CurrentDirectoryResults) + { + var arrayIndex = contactIndex - 1; + + if (entry is DirectoryFolder && entry.ParentFolderId == "root") + { + tokenArray[arrayIndex] = new XSigSerialToken(contactIndex, String.Format("[+] {0}", entry.Name)); + + contactIndex++; + + continue; + } + + if(isRoot && String.IsNullOrEmpty(entry.FolderId)) continue; + + tokenArray[arrayIndex] = new XSigSerialToken(contactIndex, entry.Name); + + contactIndex++; + } + + return GetXSigString(tokenArray); + } + + private void LinkVideoCodecCallControlsToApi(BasicTriList trilist, VideoCodecControllerJoinMap joinMap) + { + trilist.SetSigFalseAction(joinMap.ManualDial.JoinNumber, + () => Dial(trilist.StringOutput[joinMap.CurrentDialString.JoinNumber].StringValue)); + + //End All calls for now + trilist.SetSigFalseAction(joinMap.EndCall.JoinNumber, EndAllCalls); + + trilist.SetBool(joinMap.HookState.JoinNumber, IsInCall); + + CallStatusChange += (sender, args) => + { + trilist.SetBool(joinMap.HookState.JoinNumber, IsInCall); + + trilist.SetBool(joinMap.IncomingCall.JoinNumber, args.CallItem.Direction == eCodecCallDirection.Incoming); + + 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.CurrentCallData.JoinNumber, UpdateCallStatusXSig()); + }; + } + + private string UpdateCallStatusXSig() + { + const int maxCalls = 8; + const int maxStrings = 5; + const int offset = 6; + var stringIndex = 0; + var digitalIndex = maxStrings * maxCalls; + var arrayIndex = 0; + + var tokenArray = new XSigToken[maxCalls*offset]; //set array size for number of calls * pieces of info + + foreach (var call in ActiveCalls) + { + if (arrayIndex >= maxCalls * offset) + break; + //digitals + tokenArray[arrayIndex] = new XSigDigitalToken(digitalIndex + 1, call.IsActiveCall); + + //serials + tokenArray[arrayIndex + 1] = new XSigSerialToken(stringIndex + 1, call.Name ?? String.Empty); + tokenArray[arrayIndex + 2] = new XSigSerialToken(stringIndex + 2, call.Number ?? String.Empty); + 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()); + + arrayIndex += offset; + stringIndex += maxStrings; + digitalIndex++; + } + while (digitalIndex < maxCalls) + { + //digitals + tokenArray[arrayIndex] = new XSigDigitalToken(digitalIndex + 1, false); + + //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); + + arrayIndex += offset; + stringIndex += maxStrings; + digitalIndex++; + } + + return GetXSigString(tokenArray); + } + + 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("#")); + } + + private void LinkVideoCodecCameraLayoutsToApi(IHasCodecLayouts codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) + { + trilist.SetSigFalseAction(joinMap.CameraLayout.JoinNumber, codec.LocalLayoutToggle); + + codec.LocalLayoutFeedback.LinkInputSig(trilist.StringInput[joinMap.CameraLayoutStringFb.JoinNumber]); + } + + private void LinkVideoCodecCameraModeToApi(IHasCameraAutoMode codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) + { + trilist.SetSigFalseAction(joinMap.CameraModeAuto.JoinNumber, codec.CameraAutoModeOn); + trilist.SetSigFalseAction(joinMap.CameraModeManual.JoinNumber, codec.CameraAutoModeOff); + + codec.CameraAutoModeIsOnFeedback.OutputChange += (o, a) => + { + var offCodec = codec as IHasCameraOff; + + if (offCodec != null) + { + if (offCodec.CameraIsOffFeedback.BoolValue) + { + trilist.SetBool(joinMap.CameraModeAuto.JoinNumber, false); + trilist.SetBool(joinMap.CameraModeManual.JoinNumber, false); + trilist.SetBool(joinMap.CameraModeOff.JoinNumber, true); + return; + } + + trilist.SetBool(joinMap.CameraModeAuto.JoinNumber, a.BoolValue); + trilist.SetBool(joinMap.CameraModeManual.JoinNumber, !a.BoolValue); + trilist.SetBool(joinMap.CameraModeOff.JoinNumber, false); + return; + } + + trilist.SetBool(joinMap.CameraModeAuto.JoinNumber, a.BoolValue); + trilist.SetBool(joinMap.CameraModeManual.JoinNumber, !a.BoolValue); + trilist.SetBool(joinMap.CameraModeOff.JoinNumber, false); + }; + + var offModeCodec = codec as IHasCameraOff; + + if (offModeCodec != null) + { + if (offModeCodec.CameraIsOffFeedback.BoolValue) + { + trilist.SetBool(joinMap.CameraModeAuto.JoinNumber, false); + trilist.SetBool(joinMap.CameraModeManual.JoinNumber, false); + trilist.SetBool(joinMap.CameraModeOff.JoinNumber, true); + return; + } + + trilist.SetBool(joinMap.CameraModeAuto.JoinNumber, codec.CameraAutoModeIsOnFeedback.BoolValue); + trilist.SetBool(joinMap.CameraModeManual.JoinNumber, !codec.CameraAutoModeIsOnFeedback.BoolValue); + trilist.SetBool(joinMap.CameraModeOff.JoinNumber, false); + return; + } + + trilist.SetBool(joinMap.CameraModeAuto.JoinNumber, codec.CameraAutoModeIsOnFeedback.BoolValue); + trilist.SetBool(joinMap.CameraModeManual.JoinNumber, !codec.CameraAutoModeIsOnFeedback.BoolValue); + trilist.SetBool(joinMap.CameraModeOff.JoinNumber, false); + } + + private void LinkVideoCodecSelfviewToApi(IHasCodecSelfView codec, BasicTriList trilist, + VideoCodecControllerJoinMap joinMap) + { + trilist.SetSigFalseAction(joinMap.CameraSelfView.JoinNumber, codec.SelfViewModeToggle); + + codec.SelfviewIsOnFeedback.LinkInputSig(trilist.BooleanInput[joinMap.CameraSelfView.JoinNumber]); + } + + private void LinkVideoCodecCameraToApi(IHasCodecCameras codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) + { + //Camera PTZ + trilist.SetBoolSigAction(joinMap.CameraTiltUp.JoinNumber, (b) => + { + if (codec.SelectedCamera == null) return; + var camera = codec.SelectedCamera as IHasCameraPtzControl; + + if (camera == null) return; + + if (b) camera.TiltUp(); + else camera.TiltStop(); + }); + + trilist.SetBoolSigAction(joinMap.CameraTiltDown.JoinNumber, (b) => + { + if (codec.SelectedCamera == null) return; + var camera = codec.SelectedCamera as IHasCameraPtzControl; + + if (camera == null) return; + + if (b) camera.TiltDown(); + else camera.TiltStop(); + }); + trilist.SetBoolSigAction(joinMap.CameraPanLeft.JoinNumber, (b) => + { + if (codec.SelectedCamera == null) return; + var camera = codec.SelectedCamera as IHasCameraPtzControl; + + if (camera == null) return; + + if (b) camera.PanLeft(); + else camera.PanStop(); + }); + trilist.SetBoolSigAction(joinMap.CameraPanRight.JoinNumber, (b) => + { + if (codec.SelectedCamera == null) return; + var camera = codec.SelectedCamera as IHasCameraPtzControl; + + if (camera == null) return; + + if (b) camera.PanRight(); + else camera.PanStop(); + }); + + trilist.SetBoolSigAction(joinMap.CameraZoomIn.JoinNumber, (b) => + { + if (codec.SelectedCamera == null) return; + var camera = codec.SelectedCamera as IHasCameraPtzControl; + + if (camera == null) return; + + if (b) camera.ZoomIn(); + else camera.ZoomStop(); + }); + + trilist.SetBoolSigAction(joinMap.CameraZoomOut.JoinNumber, (b) => + { + if (codec.SelectedCamera == null) return; + var camera = codec.SelectedCamera as IHasCameraPtzControl; + + if (camera == null) return; + + if (b) camera.ZoomOut(); + else camera.ZoomStop(); + }); + + //Camera Select + trilist.SetUShortSigAction(joinMap.CameraNumberSelect.JoinNumber, (i) => + { + if (codec.SelectedCamera == null) return; + + codec.SelectCamera(codec.Cameras[i].Key); + }); + + codec.CameraSelected += (sender, args) => + { + var i = (ushort) codec.Cameras.FindIndex((c) => c.Key == args.SelectedCamera.Key); + + if (codec is IHasCodecRoomPresets) + { + return; + } + + if (!(args.SelectedCamera is IHasCameraPresets)) + { + return; + } + + var cam = args.SelectedCamera as IHasCameraPresets; + SetCameraPresetNames(cam.Presets); + + (args.SelectedCamera as IHasCameraPresets).PresetsListHasChanged += (o, eventArgs) => SetCameraPresetNames(cam.Presets); + + trilist.SetUShortSigAction(joinMap.CameraPresetSelect.JoinNumber, + (a) => + { + cam.PresetSelect(a); + trilist.SetUshort(joinMap.CameraPresetSelect.JoinNumber, a); + }); + + trilist.SetSigFalseAction(joinMap.CameraPresetSave.JoinNumber, + () => + { + cam.PresetStore(trilist.UShortOutput[joinMap.CameraPresetSelect.JoinNumber].UShortValue, + String.Empty); + trilist.PulseBool(joinMap.CameraPresetSave.JoinNumber, 3000); + }); + }; + + if (!(codec is IHasCodecRoomPresets)) return; + + var presetCodec = codec as IHasCodecRoomPresets; + + presetCodec.CodecRoomPresetsListHasChanged += + (sender, args) => SetCameraPresetNames(presetCodec.NearEndPresets); + + //Camera Presets + trilist.SetUShortSigAction(joinMap.CameraPresetSelect.JoinNumber, (i) => + { + presetCodec.CodecRoomPresetSelect(i); + + trilist.SetUshort(joinMap.CameraPresetSelect.JoinNumber, i); + }); + + trilist.SetSigFalseAction(joinMap.CameraPresetSave.JoinNumber, + () => + { + presetCodec.CodecRoomPresetStore( + trilist.UShortOutput[joinMap.CameraPresetSelect.JoinNumber].UShortValue, String.Empty); + trilist.PulseBool(joinMap.CameraPresetSave.JoinNumber, 3000); + }); + } + + private string SetCameraPresetNames(IEnumerable presets) + { + return SetCameraPresetNames(presets.Select(p => p.Description).ToList()); + } + + private string SetCameraPresetNames(IEnumerable presets) + { + return SetCameraPresetNames(presets.Select(p => p.Description).ToList()); + } + + private string SetCameraPresetNames(ICollection presets) + { + var i = 1; //start index for xsig; + + var tokenArray = new XSigToken[presets.Count]; + + foreach (var preset in presets) + { + var cameraPreset = new XSigSerialToken(i, preset); + tokenArray[i - 1] = cameraPreset; + i++; + } + + return GetXSigString(tokenArray); + } + + private string GetXSigString(XSigToken[] tokenArray) + { + string returnString; + using (var s = new MemoryStream()) + { + using (var tw = new XSigTokenStreamWriter(s, true)) + { + tw.WriteXSigData(tokenArray); + } + + var xSig = s.ToArray(); + + returnString = Encoding.GetEncoding(XSigEncoding).GetString(xSig, 0, xSig.Length); + } + + return returnString; + } + + #endregion } @@ -237,11 +1145,14 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec /// public class CodecPhonebookSyncState : IKeyed { - bool _InitialSyncComplete; + private bool _InitialSyncComplete; - public event EventHandler InitialSyncCompleted; + public CodecPhonebookSyncState(string key) + { + Key = key; - public string Key { get; private set; } + CodecDisconnected(); + } public bool InitialSyncComplete { @@ -252,7 +1163,9 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec { var handler = InitialSyncCompleted; if (handler != null) + { handler(this, new EventArgs()); + } } _InitialSyncComplete = value; } @@ -268,12 +1181,13 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec public int NumberOfContacts { get; private set; } - public CodecPhonebookSyncState(string key) - { - Key = key; + #region IKeyed Members - CodecDisconnected(); - } + public string Key { get; private set; } + + #endregion + + public event EventHandler InitialSyncCompleted; public void InitialPhonebookFoldersReceived() { @@ -314,7 +1228,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec NumberOfContactsWasReceived = false; } - void CheckSyncStatus() + private void CheckSyncStatus() { if (InitialPhonebookFoldersWasReceived && NumberOfContactsWasReceived && PhonebookRootEntriesWasRecieved) { @@ -322,7 +1236,9 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec Debug.Console(1, this, "Initial Phonebook Sync Complete!"); } else + { InitialSyncComplete = false; + } } } } \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ResponseObjects.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ResponseObjects.cs index c603ca08..4f4eddbc 100644 --- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ResponseObjects.cs +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ResponseObjects.cs @@ -11,6 +11,7 @@ using PepperDash.Essentials.Devices.Common.Codec; using Newtonsoft.Json; using Newtonsoft.Json.Linq; +using PepperDash.Essentials.Devices.Common.VideoCodec.Interfaces; namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom { @@ -57,6 +58,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom public List AudioInputs { get; set; } public List AudioOuputs { get; set; } public List Cameras { get; set; } + public zEvent.PhoneCallStatus PhoneCall { get; set; } public ZoomRoomStatus() { @@ -73,6 +75,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom AudioInputs = new List(); AudioOuputs = new List(); Cameras = new List(); + PhoneCall = new zEvent.PhoneCallStatus(); } } @@ -85,6 +88,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom public zConfiguration.Audio Audio { get; set; } public zConfiguration.Video Video { get; set; } public zConfiguration.Client Client { get; set; } + public zConfiguration.Camera Camera { get; set; } public ZoomRoomConfiguration() { @@ -92,6 +96,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom Audio = new zConfiguration.Audio(); Video = new zConfiguration.Video(); Client = new zConfiguration.Client(); + Camera = new zConfiguration.Camera(); } } @@ -255,9 +260,9 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom /// public static CodecDirectory ConvertZoomContactsToGeneric(List zoomContacts) { - var directory = new Codec.CodecDirectory(); + var directory = new CodecDirectory(); - var folders = new List(); + var folders = new List(); var roomFolder = new DirectoryFolder(); @@ -272,9 +277,11 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom { // If so, setup a rooms and contacts folder and add them. roomFolder.Name = "Rooms"; + roomFolder.ParentFolderId = "root"; roomFolder.FolderId = "rooms"; contactFolder.Name = "Contacts"; + contactFolder.ParentFolderId = "root"; contactFolder.FolderId = "contacts"; folders.Add(roomFolder); @@ -285,21 +292,15 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom try { - if (zoomContacts.Count > 0) + if (zoomContacts.Count == 0) return directory; { foreach (Contact c in zoomContacts) { - var contact = new ZoomDirectoryContact(); - - contact.Name = c.ScreenName; - contact.ContactId = c.Jid; + var contact = new ZoomDirectoryContact {Name = c.ScreenName, ContactId = c.Jid}; if (folders.Count > 0) { - if (c.IsZoomRoom) - contact.FolderId = roomFolder.FolderId; - else - contact.FolderId = contactFolder.FolderId; + contact.ParentFolderId = c.IsZoomRoom ? "rooms" : "contacts"; } contacts.Add(contact); @@ -371,7 +372,20 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom public CallRecordInfo CallRecordInfo { get; set; } - public zCommand.InfoResult Info { get; set; } + private zCommand.InfoResult _info; + + public zCommand.InfoResult Info + { + get + { + return _info; + } + set + { + _info = value; + NotifyPropertyChanged("Info"); + } + } public Call() { @@ -577,7 +591,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom public string meetingID { get; set; } public string password { get; set; } public string meetingOption { get; set; } - public int MeetingNumber { get; set; } + public long MeetingNumber { get; set; } public string callerName { get; set; } public string avatarURL { get; set; } public int lifeTime { get; set; } @@ -688,6 +702,86 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom [JsonProperty("why_cannot_pin_share")] public string WhyCannotPinShare { get; set; } } + + public class PhoneCallStatus:NotifiableObject + { + private bool _isIncomingCall; + private string _peerDisplayName; + private string _peerNumber; + + private bool _offHook; + + public string CallId { get; set; } + public bool IsIncomingCall { + get { return _isIncomingCall; } + set + { + if(value == _isIncomingCall) return; + + _isIncomingCall = value; + NotifyPropertyChanged("IsIncomingCall"); + } } + + public string PeerDisplayName + { + get { return _peerDisplayName; } + set + { + if (value == _peerDisplayName) return; + _peerDisplayName = value; + NotifyPropertyChanged("PeerDisplayName"); + } + } + + public string PeerNumber + { + get { return _peerNumber; } + set + { + if (value == _peerNumber) return; + + _peerNumber = value; + NotifyPropertyChanged("PeerNumber"); + } + } + + public string PeerUri { get; set; } + + private ePhoneCallStatus _status; + public ePhoneCallStatus Status + { + get { return _status; } + set + { + _status = value; + OffHook = _status == ePhoneCallStatus.PhoneCallStatus_Accepted || + _status == ePhoneCallStatus.PhoneCallStatus_InCall || + _status == ePhoneCallStatus.PhoneCallStatus_Init || + _status == ePhoneCallStatus.PhoneCallStatus_Ringing; + } + } + + public bool OffHook + { + get { return _offHook; } + set + { + if (value == _offHook) return; + + _offHook = value; + NotifyPropertyChanged("OffHook"); + } + } + } + + public enum ePhoneCallStatus + { + PhoneCallStatus_Ringing, + PhoneCallStatus_Terminated, + PhoneCallStatus_Accepted, + PhoneCallStatus_InCall, + PhoneCallStatus_Init, + } } /// @@ -701,9 +795,23 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom public bool OptimizeVideoSharing { get; set; } } - public class Camera + public class Camera : NotifiableObject { - public bool Mute { get; set; } + private bool _mute; + + public bool Mute + { + get { return _mute; } + set + { + Debug.Console(1, "Camera Mute response received: {0}", value); + + if (value == _mute) return; + + _mute = value; + NotifyPropertyChanged("Mute"); + } + } } public class Microphone : NotifiableObject @@ -757,12 +865,20 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom DownLeft } - public class Layout + public class Layout:NotifiableObject { public bool ShareThumb { get; set; } public eLayoutStyle Style { get; set; } public eLayoutSize Size { get; set; } - public eLayoutPosition Position { get; set; } + + private eLayoutPosition _position; + public eLayoutPosition Position { + get { return _position; } + set + { + _position = value; + NotifyPropertyChanged("Position"); + } } } public class Lock @@ -831,13 +947,13 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom { get { - return this._volume; + return _volume; } set { if (value != _volume) { - this._volume = value; + _volume = value; NotifyPropertyChanged("Volume"); } } @@ -913,7 +1029,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom /// public class zCommand { - public partial class BookingsListResult + public class BookingsListResult { [JsonProperty("accessRole")] public string AccessRole { get; set; } @@ -949,8 +1065,20 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom public ThirdParty ThirdParty { get; set; } } + public static List GetGenericMeetingsFromBookingResult(List bookings, + int minutesBeforeMeetingStart) + { + var rv = GetGenericMeetingsFromBookingResult(bookings); + + foreach (var meeting in rv) + { + meeting.MinutesBeforeMeeting = minutesBeforeMeetingStart; + } + + return rv; + } /// - /// Extracts the necessary meeting values from the Cisco bookings response ans converts them to the generic class + /// Extracts the necessary meeting values from the Zoom bookings response and converts them to the generic class /// /// /// @@ -983,6 +1111,12 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom meeting.Privacy = b.IsPrivate ? eMeetingPrivacy.Private : eMeetingPrivacy.Public; // No meeting.Calls data exists for Zoom Rooms. Leaving out for now. + var now = DateTime.Now; + if (meeting.StartTime < now && meeting.EndTime < now) + { + Debug.Console(1, "Skipping meeting {0}. Meeting is in the past.", meeting.Title); + continue; + } meetings.Add(meeting); @@ -1070,6 +1204,23 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom { HandStatus = new HandStatus(); } + + public static List GetGenericParticipantListFromParticipantsResult( + List participants) + { + return + participants.Select( + p => + new Participant + { + Name = p.UserName, + IsHost = p.IsHost, + CanMuteVideo = p.IsVideoCanMuteByHost, + CanUnmuteVideo = p.IsVideoCanUnmuteByHost, + AudioMuteFb = p.AudioStatusState == "AUDIO_MUTED", + VideoMuteFb = p.VideoStatusIsSending + }).ToList(); + } } public class CallinCountryList 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 2f834c36..b8d29077 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 @@ -4,45 +4,130 @@ using System.Linq; using System.Text; 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.Devices.Common.VideoCodec.Cisco; +using PepperDash.Essentials.Devices.Common.VideoCodec.Interfaces; +using PepperDash_Essentials_Core.DeviceTypeInterfaces; namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom { - public class ZoomRoom : VideoCodecBase, IHasCodecSelfView, IHasDirectory, ICommunicationMonitor, IRouting, IHasScheduleAwareness, IHasCodecCameras + public class ZoomRoom : VideoCodecBase, IHasCodecSelfView, IHasDirectoryHistoryStack, ICommunicationMonitor, + IRouting, + IHasScheduleAwareness, IHasCodecCameras, IHasParticipants, IHasCameraOff, IHasCameraAutoMode, + IHasFarEndContentStatus, IHasSelfviewPosition, IHasPhoneDialing { + private const long MeetingRefreshTimer = 60000; + private const uint DefaultMeetingDurationMin = 30; + private const string Delimiter = "\x0D\x0A"; + private readonly CrestronQueue _receiveQueue; + + + private readonly Thread _receiveThread; + + private readonly ZoomRoomSyncState _syncState; + public bool CommDebuggingIsOn; + private CodecDirectory _currentDirectoryResult; + private uint _jsonCurlyBraceCounter; + private bool _jsonFeedbackMessageIsIncoming; + private StringBuilder _jsonMessage; + private int _previousVolumeLevel; + private CameraBase _selectedCamera; + + private readonly ZoomRoomPropertiesConfig _props; + + public ZoomRoom(DeviceConfig config, IBasicCommunication comm) + : base(config) + { + _props = JsonConvert.DeserializeObject(config.Properties.ToString()); + + // The queue that will collect the repsonses in the order they are received + _receiveQueue = new CrestronQueue(1024); + + // The thread responsible for dequeuing and processing the messages + _receiveThread = new Thread(o => ProcessQueue(), null) {Priority = Thread.eThreadPriority.MediumPriority}; + + Communication = comm; + + if (_props.CommunicationMonitorProperties != null) + { + CommunicationMonitor = new GenericCommunicationMonitor(this, Communication, + _props.CommunicationMonitorProperties); + } + else + { + CommunicationMonitor = new GenericCommunicationMonitor(this, Communication, 30000, 120000, 300000, + "zStatus SystemUnit\r"); + } + + DeviceManager.AddDevice(CommunicationMonitor); + + Status = new ZoomRoomStatus(); + + Configuration = new ZoomRoomConfiguration(); + + CodecInfo = new ZoomRoomInfo(Status, Configuration); + + _syncState = new ZoomRoomSyncState(Key + "--Sync", this); + + _syncState.InitialSyncCompleted += SyncState_InitialSyncCompleted; + + PhonebookSyncState = new CodecPhonebookSyncState(Key + "--PhonebookSync"); + + PortGather = new CommunicationGather(Communication, "\x0A") {IncludeDelimiter = true}; + PortGather.LineReceived += Port_LineReceived; + + CodecOsdIn = new RoutingInputPort(RoutingPortNames.CodecOsd, + eRoutingSignalType.Audio | eRoutingSignalType.Video, + eRoutingPortConnectionType.Hdmi, new Action(StopSharing), this); + + Output1 = new RoutingOutputPort(RoutingPortNames.AnyVideoOut, + eRoutingSignalType.Audio | eRoutingSignalType.Video, + eRoutingPortConnectionType.Hdmi, null, this); + + SelfviewIsOnFeedback = new BoolFeedback(SelfViewIsOnFeedbackFunc); + + CameraIsOffFeedback = new BoolFeedback(CameraIsOffFeedbackFunc); + + CameraAutoModeIsOnFeedback = new BoolFeedback(CameraAutoModeIsOnFeedbackFunc); + + CodecSchedule = new CodecScheduleAwareness(MeetingRefreshTimer); + + ReceivingContent = new BoolFeedback(FarEndIsSharingContentFeedbackFunc); + + SelfviewPipPositionFeedback = new StringFeedback(SelfviewPipPositionFeedbackFunc); + + SetUpFeedbackActions(); + + Cameras = new List(); + + SetUpDirectory(); + + Participants = new CodecParticipants(); + + SupportsCameraOff = _props.SupportsCameraOff; + SupportsCameraAutoMode = _props.SupportsCameraAutoMode; + + PhoneOffHookFeedback = new BoolFeedback(PhoneOffHookFeedbackFunc); + CallerIdNameFeedback = new StringFeedback(CallerIdNameFeedbackFunc); + CallerIdNumberFeedback = new StringFeedback(CallerIdNumberFeedbackFunc); + } + public CommunicationGather PortGather { get; private set; } - public StatusMonitorBase CommunicationMonitor { get; private set; } - - private CrestronQueue ReceiveQueue; - - private Thread ReceiveThread; - - string Delimiter = "\x0D\x0A"; - - private ZoomRoomSyncState SyncState; - public ZoomRoomStatus Status { get; private set; } public ZoomRoomConfiguration Configuration { get; private set; } - private StringBuilder JsonMessage; - - private bool JsonFeedbackMessageIsIncoming; - private uint JsonCurlyBraceCounter = 0; - - public bool CommDebuggingIsOn; - //CTimer LoginMessageReceivedTimer; //CTimer RetryConnectionTimer; @@ -59,50 +144,32 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom protected override Func PrivacyModeIsOnFeedbackFunc { - get - { - return () => Configuration.Call.Microphone.Mute; - } + get { return () => Configuration.Call.Microphone.Mute; } } protected override Func StandbyIsOnFeedbackFunc { - get - { - return () => false; - } + get { return () => false; } } protected override Func SharingSourceFeedbackFunc { - get - { - return () => Status.Sharing.dispState; - } + get { return () => Status.Sharing.dispState; } } protected override Func SharingContentIsOnFeedbackFunc { - get - { - return () => Status.Call.Sharing.IsSharing; - } + get { return () => Status.Call.Sharing.IsSharing; } } protected Func FarEndIsSharingContentFeedbackFunc { - get - { - return () => false; - } + get { return () => Status.Call.Sharing.State == zEvent.eSharingState.Receiving; } } protected override Func MuteFeedbackFunc { - get - { - return () => Configuration.Audio.Output.Volume == 0; - } + get { return () => Configuration.Audio.Output.Volume == 0; } } //protected Func RoomIsOccupiedFeedbackFunc @@ -123,186 +190,365 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom protected Func SelfViewIsOnFeedbackFunc { - get - { - return () => !Configuration.Video.HideConfSelfVideo; - } + get { return () => !Configuration.Video.HideConfSelfVideo; } + } + + protected Func CameraIsOffFeedbackFunc + { + get { return () => Configuration.Call.Camera.Mute; } + } + + protected Func CameraAutoModeIsOnFeedbackFunc + { + get { return () => false; } } protected Func SelfviewPipPositionFeedbackFunc { - get - { - return () => ""; - } + get { return () => _currentSelfviewPipPosition.Command; } } protected Func LocalLayoutFeedbackFunc { - get - { - return () => ""; - } + get { return () => ""; } } protected Func LocalLayoutIsProminentFeedbackFunc { - get - { - return () => false; - } + get { return () => false; } } public RoutingInputPort CodecOsdIn { get; private set; } public RoutingOutputPort Output1 { get; private set; } - uint DefaultMeetingDurationMin = 30; + #region ICommunicationMonitor Members - int PreviousVolumeLevel = 0; + public StatusMonitorBase CommunicationMonitor { get; private set; } - public ZoomRoom(DeviceConfig config, IBasicCommunication comm) - : base(config) + #endregion + + #region IHasCodecCameras Members + + public event EventHandler CameraSelected; + + public List Cameras { get; private set; } + + public CameraBase SelectedCamera { - var props = JsonConvert.DeserializeObject(config.Properties.ToString()); - - // The queue that will collect the repsonses in the order they are received - ReceiveQueue = new CrestronQueue(25); - - // The thread responsible for dequeuing and processing the messages - ReceiveThread = new Thread((o) => ProcessQueue(), null); - ReceiveThread.Priority = Thread.eThreadPriority.MediumPriority; - - Communication = comm; - - if (props.CommunicationMonitorProperties != null) + get { return _selectedCamera; } + private set { - CommunicationMonitor = new GenericCommunicationMonitor(this, Communication, props.CommunicationMonitorProperties); + _selectedCamera = value; + SelectedCameraFeedback.FireUpdate(); + ControllingFarEndCameraFeedback.FireUpdate(); + + var handler = CameraSelected; + if (handler != null) + { + handler(this, new CameraSelectedEventArgs(SelectedCamera)); + } + } + } + + + public StringFeedback SelectedCameraFeedback { get; private set; } + + public void SelectCamera(string key) + { + if (Cameras == null) + { + return; + } + + var camera = Cameras.FirstOrDefault(c => c.Key.IndexOf(key, StringComparison.OrdinalIgnoreCase) > -1); + if (camera != null) + { + Debug.Console(1, this, "Selected Camera with key: '{0}'", camera.Key); + SelectedCamera = camera; } else { - CommunicationMonitor = new GenericCommunicationMonitor(this, Communication, 30000, 120000, 300000, "zStatus SystemUnit\r"); + Debug.Console(1, this, "Unable to select camera with key: '{0}'", key); } - - DeviceManager.AddDevice(CommunicationMonitor); - - Status = new ZoomRoomStatus(); - - Configuration = new ZoomRoomConfiguration(); - - CodecInfo = new ZoomRoomInfo(Status, Configuration); - - SyncState = new ZoomRoomSyncState(Key + "--Sync", this); - - SyncState.InitialSyncCompleted += new EventHandler(SyncState_InitialSyncCompleted); - - PhonebookSyncState = new CodecPhonebookSyncState(Key + "--PhonebookSync"); - - PortGather = new CommunicationGather(Communication, "\x0A"); - PortGather.IncludeDelimiter = true; - PortGather.LineReceived += this.Port_LineReceived; - - CodecOsdIn = new RoutingInputPort(RoutingPortNames.CodecOsd, eRoutingSignalType.Audio | eRoutingSignalType.Video, - eRoutingPortConnectionType.Hdmi, new Action(StopSharing), this); - - Output1 = new RoutingOutputPort(RoutingPortNames.AnyVideoOut, eRoutingSignalType.Audio | eRoutingSignalType.Video, - eRoutingPortConnectionType.Hdmi, null, this); - - SelfviewIsOnFeedback = new BoolFeedback(SelfViewIsOnFeedbackFunc); - - CodecSchedule = new CodecScheduleAwareness(); - - SetUpFeedbackActions(); - - Cameras = new List(); - - SetUpDirectory(); } - void SyncState_InitialSyncCompleted(object sender, EventArgs e) + public CameraBase FarEndCamera { get; private set; } + + public BoolFeedback ControllingFarEndCameraFeedback { get; private set; } + + #endregion + + #region IHasCodecSelfView Members + + public BoolFeedback SelfviewIsOnFeedback { get; private set; } + + public void SelfViewModeOn() + { + SendText("zConfiguration Video hide_conf_self_video: off"); + } + + public void SelfViewModeOff() + { + SendText("zConfiguration Video hide_conf_self_video: on"); + } + + public void SelfViewModeToggle() + { + if (SelfviewIsOnFeedback.BoolValue) + { + SelfViewModeOff(); + } + else + { + SelfViewModeOn(); + } + } + + #endregion + + #region IHasDirectoryHistoryStack Members + + public event EventHandler DirectoryResultReturned; + public CodecDirectory DirectoryRoot { get; private set; } + + public CodecDirectory CurrentDirectoryResult + { + get { return _currentDirectoryResult; } + } + + public CodecPhonebookSyncState PhonebookSyncState { get; private set; } + + public void SearchDirectory(string searchString) + { + var directoryResults = new CodecDirectory(); + + directoryResults.AddContactsToDirectory( + DirectoryRoot.CurrentDirectoryResults.FindAll( + c => c.Name.IndexOf(searchString, 0, StringComparison.OrdinalIgnoreCase) > -1)); + + DirectoryBrowseHistoryStack.Clear(); + _currentDirectoryResult = directoryResults; + + OnDirectoryResultReturned(directoryResults); + } + + public void GetDirectoryFolderContents(string folderId) + { + var directoryResults = new CodecDirectory {ResultsFolderId = folderId}; + + directoryResults.AddContactsToDirectory( + DirectoryRoot.CurrentDirectoryResults.FindAll(c => c.ParentFolderId.Equals(folderId))); + + DirectoryBrowseHistoryStack.Push(_currentDirectoryResult); + + _currentDirectoryResult = directoryResults; + + OnDirectoryResultReturned(directoryResults); + } + + public void SetCurrentDirectoryToRoot() + { + DirectoryBrowseHistoryStack.Clear(); + + _currentDirectoryResult = DirectoryRoot; + + OnDirectoryResultReturned(DirectoryRoot); + } + + public void GetDirectoryParentFolderContents() + { + if (DirectoryBrowseHistoryStack.Count == 0) + { + return; + } + + var currentDirectory = DirectoryBrowseHistoryStack.Pop(); + + _currentDirectoryResult = currentDirectory; + + OnDirectoryResultReturned(currentDirectory); + } + + public BoolFeedback CurrentDirectoryResultIsNotDirectoryRoot { get; private set; } + + public List DirectoryBrowseHistory { get; private set; } + + public Stack DirectoryBrowseHistoryStack { get; private set; } + + #endregion + + #region IHasScheduleAwareness Members + + public CodecScheduleAwareness CodecSchedule { get; private set; } + + public void GetSchedule() + { + GetBookings(); + } + + #endregion + + #region IRouting Members + + public void ExecuteSwitch(object inputSelector, object outputSelector, eRoutingSignalType signalType) + { + ExecuteSwitch(inputSelector); + } + + #endregion + + private void SyncState_InitialSyncCompleted(object sender, EventArgs e) { SetUpRouting(); SetIsReady(); } + private void SetUpCallFeedbackActions() + { + Status.Call.Sharing.PropertyChanged += (o, a) => + { + if (a.PropertyName == "State") + { + SharingContentIsOnFeedback.FireUpdate(); + ReceivingContent.FireUpdate(); + } + }; + + Status.Call.PropertyChanged += (o, a) => + { + if (a.PropertyName == "Info") + { + Debug.Console(1, this, "Updating Call Status"); + UpdateCallStatus(); + } + }; + } /// /// Subscribes to the PropertyChanged events on the state objects and fires the corresponding feedbacks. /// - void SetUpFeedbackActions() + private void SetUpFeedbackActions() { - Configuration.Audio.Output.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler( - (o, a) => + Configuration.Audio.Output.PropertyChanged += (o, a) => + { + if (a.PropertyName == "Volume") { - if (a.PropertyName == "Volume") - { - VolumeLevelFeedback.FireUpdate(); - MuteFeedback.FireUpdate(); - } - }); + VolumeLevelFeedback.FireUpdate(); + MuteFeedback.FireUpdate(); + } + }; - Configuration.Call.Microphone.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler( - (o, a) => + Configuration.Call.Microphone.PropertyChanged += (o, a) => + { + if (a.PropertyName == "Mute") { - if (a.PropertyName == "Mute") - { - PrivacyModeIsOnFeedback.FireUpdate(); - } - }); + PrivacyModeIsOnFeedback.FireUpdate(); + } + }; - Configuration.Video.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler( - (o, a) => + Configuration.Video.PropertyChanged += (o, a) => + { + if (a.PropertyName == "HideConfSelfVideo") { - if (a.PropertyName == "HideConfSelfVideo") - { - SelfviewIsOnFeedback.FireUpdate(); - } - }); - Configuration.Video.Camera.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler( - (o, a) => + SelfviewIsOnFeedback.FireUpdate(); + } + }; + Configuration.Video.Camera.PropertyChanged += (o, a) => + { + if (a.PropertyName == "SelectedId") { - if (a.PropertyName == "SelectedId") - { - SelectCamera(Configuration.Video.Camera.SelectedId); // this will in turn fire the affected feedbacks - } - }); + SelectCamera(Configuration.Video.Camera.SelectedId); + // this will in turn fire the affected feedbacks + } + }; - Status.Call.Sharing.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler( - (o, a) => - { - if (a.PropertyName == "State") - { - SharingContentIsOnFeedback.FireUpdate(); - } - }); + Configuration.Call.Camera.PropertyChanged += (o, a) => + { + Debug.Console(1, this, "Configuration.Call.Camera.PropertyChanged: {0}", a.PropertyName); - Status.Sharing.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler( - (o, a) => + if (a.PropertyName != "Mute") return; + + CameraIsOffFeedback.FireUpdate(); + CameraAutoModeIsOnFeedback.FireUpdate(); + }; + + Configuration.Call.Layout.PropertyChanged += (o, a) => + { + if (a.PropertyName != "Position") return; + + ComputeSelfviewPipStatus(); + + SelfviewPipPositionFeedback.FireUpdate(); + }; + + Status.Call.Sharing.PropertyChanged += (o, a) => + { + if (a.PropertyName == "State") { - if (a.PropertyName == "dispState") - { + SharingContentIsOnFeedback.FireUpdate(); + ReceivingContent.FireUpdate(); + } + }; + + Status.Call.PropertyChanged += (o, a) => + { + if (a.PropertyName == "Info") + { + Debug.Console(1, this, "Updating Call Status"); + UpdateCallStatus(); + } + }; + + Status.Sharing.PropertyChanged += (o, a) => + { + switch (a.PropertyName) + { + case "dispState": SharingSourceFeedback.FireUpdate(); - } - else if (a.PropertyName == "password") - { - //TODO: Fire Sharing Password Update - } - }); + break; + case "password": + break; + } + }; + + Status.PhoneCall.PropertyChanged += (o, a) => + { + switch (a.PropertyName) + { + case "IsIncomingCall": + Debug.Console(1, this, "Incoming Phone Call: {0}", Status.PhoneCall.IsIncomingCall); + break; + case "PeerDisplayName": + Debug.Console(1, this, "Peer Display Name: {0}", Status.PhoneCall.PeerDisplayName); + CallerIdNameFeedback.FireUpdate(); + break; + case "PeerNumber": + Debug.Console(1, this, "Peer Number: {0}", Status.PhoneCall.PeerNumber); + CallerIdNumberFeedback.FireUpdate(); + break; + case "OffHook": + Debug.Console(1, this, "Phone is OffHook: {0}", Status.PhoneCall.OffHook); + PhoneOffHookFeedback.FireUpdate(); + break; + } + }; } - void SetUpDirectory() + private void SetUpDirectory() { DirectoryRoot = new CodecDirectory(); DirectoryBrowseHistory = new List(); + DirectoryBrowseHistoryStack = new Stack(); - CurrentDirectoryResultIsNotDirectoryRoot = new BoolFeedback(() => DirectoryBrowseHistory.Count > 0); + CurrentDirectoryResultIsNotDirectoryRoot = new BoolFeedback(() => _currentDirectoryResult != DirectoryRoot); CurrentDirectoryResultIsNotDirectoryRoot.FireUpdate(); } - void SetUpRouting() + private void SetUpRouting() { // Set up input ports CreateOsdSource(); @@ -316,7 +562,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom /// Creates the fake OSD source, and connects it's AudioVideo output to the CodecOsdIn input /// to enable routing /// - void CreateOsdSource() + private void CreateOsdSource() { OsdSource = new DummyRoutingInputsDevice(Key + "[osd]"); DeviceManager.AddDevice(OsdSource); @@ -332,18 +578,25 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom /// public override bool CustomActivate() { - CrestronConsole.AddNewConsoleCommand(SetCommDebug, "SetCodecCommDebug", "0 for Off, 1 for on", ConsoleAccessLevelEnum.AccessOperator); - CrestronConsole.AddNewConsoleCommand((s) => SendText("zCommand Phonebook List Offset: 0 Limit: 512"), "GetZoomRoomContacts", "Triggers a refresh of the codec phonebook", ConsoleAccessLevelEnum.AccessOperator); - CrestronConsole.AddNewConsoleCommand((s) => GetBookings(), "GetZoomRoomBookings", "Triggers a refresh of the booking data for today", ConsoleAccessLevelEnum.AccessOperator); + CrestronConsole.AddNewConsoleCommand(SetCommDebug, "SetCodecCommDebug", "0 for Off, 1 for on", + ConsoleAccessLevelEnum.AccessOperator); + if (!_props.DisablePhonebookAutoDownload) + { + CrestronConsole.AddNewConsoleCommand(s => SendText("zCommand Phonebook List Offset: 0 Limit: 512"), + "GetZoomRoomContacts", "Triggers a refresh of the codec phonebook", + ConsoleAccessLevelEnum.AccessOperator); + } + + CrestronConsole.AddNewConsoleCommand(s => GetBookings(), "GetZoomRoomBookings", + "Triggers a refresh of the booking data for today", ConsoleAccessLevelEnum.AccessOperator); var socket = Communication as ISocketStatus; if (socket != null) { - socket.ConnectionChange += new EventHandler(socket_ConnectionChange); + socket.ConnectionChange += socket_ConnectionChange; } - // TODO: Turn this off when done initial development - CommDebuggingIsOn = true; + CommDebuggingIsOn = false; Communication.Connect(); @@ -366,16 +619,15 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom } } - void socket_ConnectionChange(object sender, GenericSocketStatusChageEventArgs e) + private void socket_ConnectionChange(object sender, GenericSocketStatusChageEventArgs e) { Debug.Console(1, this, "Socket status change {0}", e.Client.ClientStatus); if (e.Client.IsConnected) { - } else { - SyncState.CodecDisconnected(); + _syncState.CodecDisconnected(); PhonebookSyncState.CodecDisconnected(); } } @@ -383,7 +635,9 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom public void SendText(string command) { if (CommDebuggingIsOn) + { Debug.Console(1, this, "Sending: '{0}'", command); + } Communication.SendText(command + Delimiter); } @@ -393,17 +647,17 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom /// /// /// - void Port_LineReceived(object dev, GenericCommMethodReceiveTextArgs args) + private void Port_LineReceived(object dev, GenericCommMethodReceiveTextArgs args) { //if (CommDebuggingIsOn) // Debug.Console(1, this, "Gathered: '{0}'", args.Text); - ReceiveQueue.Enqueue(args.Text); + _receiveQueue.Enqueue(args.Text); // If the receive thread has for some reason stopped, this will restart it - if (ReceiveThread.ThreadState != Thread.eThreadStates.ThreadRunning) + if (_receiveThread.ThreadState != Thread.eThreadStates.ThreadRunning) { - ReceiveThread.Start(); + _receiveThread.Start(); } } @@ -412,13 +666,13 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom /// Runs in it's own thread to dequeue messages in the order they were received to be processed /// /// - object ProcessQueue() + private object ProcessQueue() { try { while (true) { - var message = ReceiveQueue.Dequeue(); + var message = _receiveQueue.Dequeue(); ProcessMessage(message); } @@ -435,97 +689,121 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom /// /// Queues the initial queries to be sent upon connection /// - void SetUpSyncQueries() + private void SetUpSyncQueries() { // zStatus - SyncState.AddQueryToQueue("zStatus Call Status"); - SyncState.AddQueryToQueue("zStatus Audio Input Line"); - SyncState.AddQueryToQueue("zStatus Audio Output Line"); - SyncState.AddQueryToQueue("zStatus Video Camera Line"); - SyncState.AddQueryToQueue("zStatus Video Optimizable"); - SyncState.AddQueryToQueue("zStatus Capabilities"); - SyncState.AddQueryToQueue("zStatus Sharing"); - SyncState.AddQueryToQueue("zStatus CameraShare"); - SyncState.AddQueryToQueue("zStatus Call Layout"); - SyncState.AddQueryToQueue("zStatus Call ClosedCaption Available"); - SyncState.AddQueryToQueue("zStatus NumberOfScreens"); + _syncState.AddQueryToQueue("zStatus Call Status"); + _syncState.AddQueryToQueue("zStatus Audio Input Line"); + _syncState.AddQueryToQueue("zStatus Audio Output Line"); + _syncState.AddQueryToQueue("zStatus Video Camera Line"); + _syncState.AddQueryToQueue("zStatus Video Optimizable"); + _syncState.AddQueryToQueue("zStatus Capabilities"); + _syncState.AddQueryToQueue("zStatus Sharing"); + _syncState.AddQueryToQueue("zStatus CameraShare"); + _syncState.AddQueryToQueue("zStatus Call Layout"); + _syncState.AddQueryToQueue("zStatus Call ClosedCaption Available"); + _syncState.AddQueryToQueue("zStatus NumberOfScreens"); // zConfiguration - SyncState.AddQueryToQueue("zConfiguration Call Sharing optimize_video_sharing"); - SyncState.AddQueryToQueue("zConfiguration Call Microphone Mute"); - SyncState.AddQueryToQueue("zConfiguration Call Camera Mute"); - SyncState.AddQueryToQueue("zConfiguration Audio Input SelectedId"); - SyncState.AddQueryToQueue("zConfiguration Audio Input is_sap_disabled"); - SyncState.AddQueryToQueue("zConfiguration Audio Input reduce_reverb"); - SyncState.AddQueryToQueue("zConfiguration Audio Input volume"); - SyncState.AddQueryToQueue("zConfiguration Audio Output selectedId"); - SyncState.AddQueryToQueue("zConfiguration Audio Output volume"); - SyncState.AddQueryToQueue("zConfiguration Video hide_conf_self_video"); - SyncState.AddQueryToQueue("zConfiguration Video Camera selectedId"); - SyncState.AddQueryToQueue("zConfiguration Video Camera Mirror"); - SyncState.AddQueryToQueue("zConfiguration Client appVersion"); - SyncState.AddQueryToQueue("zConfiguration Client deviceSystem"); - SyncState.AddQueryToQueue("zConfiguration Call Layout ShareThumb"); - SyncState.AddQueryToQueue("zConfiguration Call Layout Style"); - SyncState.AddQueryToQueue("zConfiguration Call Layout Size"); - SyncState.AddQueryToQueue("zConfiguration Call Layout Position"); - SyncState.AddQueryToQueue("zConfiguration Call Lock Enable"); - SyncState.AddQueryToQueue("zConfiguration Call MuteUserOnEntry Enable"); - SyncState.AddQueryToQueue("zConfiguration Call ClosedCaption FontSize "); - SyncState.AddQueryToQueue("zConfiguration Call ClosedCaption Visible"); + _syncState.AddQueryToQueue("zConfiguration Call Sharing optimize_video_sharing"); + _syncState.AddQueryToQueue("zConfiguration Call Microphone Mute"); + _syncState.AddQueryToQueue("zConfiguration Call Camera Mute"); + _syncState.AddQueryToQueue("zConfiguration Audio Input SelectedId"); + _syncState.AddQueryToQueue("zConfiguration Audio Input is_sap_disabled"); + _syncState.AddQueryToQueue("zConfiguration Audio Input reduce_reverb"); + _syncState.AddQueryToQueue("zConfiguration Audio Input volume"); + _syncState.AddQueryToQueue("zConfiguration Audio Output selectedId"); + _syncState.AddQueryToQueue("zConfiguration Audio Output volume"); + _syncState.AddQueryToQueue("zConfiguration Video hide_conf_self_video"); + _syncState.AddQueryToQueue("zConfiguration Video Camera selectedId"); + _syncState.AddQueryToQueue("zConfiguration Video Camera Mirror"); + _syncState.AddQueryToQueue("zConfiguration Client appVersion"); + _syncState.AddQueryToQueue("zConfiguration Client deviceSystem"); + _syncState.AddQueryToQueue("zConfiguration Call Layout ShareThumb"); + _syncState.AddQueryToQueue("zConfiguration Call Layout Style"); + _syncState.AddQueryToQueue("zConfiguration Call Layout Size"); + _syncState.AddQueryToQueue("zConfiguration Call Layout Position"); + _syncState.AddQueryToQueue("zConfiguration Call Lock Enable"); + _syncState.AddQueryToQueue("zConfiguration Call MuteUserOnEntry Enable"); + _syncState.AddQueryToQueue("zConfiguration Call ClosedCaption FontSize "); + _syncState.AddQueryToQueue("zConfiguration Call ClosedCaption Visible"); // zCommand - SyncState.AddQueryToQueue("zCommand Phonebook List Offset: 0 Limit: 512"); - SyncState.AddQueryToQueue("zCommand Bookings List"); + if (!_props.DisablePhonebookAutoDownload) + { + _syncState.AddQueryToQueue("zCommand Phonebook List Offset: 0 Limit: 512"); + } + + _syncState.AddQueryToQueue("zCommand Bookings List"); + _syncState.AddQueryToQueue("zCommand Call ListParticipants"); + _syncState.AddQueryToQueue("zCommand Call Info"); - SyncState.StartSync(); + _syncState.StartSync(); } /// /// Processes messages as they are dequeued /// /// - void ProcessMessage(string message) - { + private void ProcessMessage(string message) + { // Counts the curly braces - if(message.Contains('{')) - JsonCurlyBraceCounter++; - - if (message.Contains('}')) - JsonCurlyBraceCounter--; - - Debug.Console(2, this, "JSON Curly Brace Count: {0}", JsonCurlyBraceCounter); - - if (!JsonFeedbackMessageIsIncoming && message.Trim('\x20') == "{" + Delimiter) // Check for the beginning of a new JSON message + if (message.Contains("client_loop: send disconnect: Broken pipe")) { - JsonFeedbackMessageIsIncoming = true; - JsonCurlyBraceCounter = 1; // reset the counter for each new message - - JsonMessage = new StringBuilder(); - - JsonMessage.Append(message); - - if (CommDebuggingIsOn) - Debug.Console(2, this, "Incoming JSON message..."); + Debug.Console(0, this, Debug.ErrorLogLevel.Error, + "Zoom Room Controller or App connected. Essentials will NOT control the Zoom Room until it is disconnected."); return; } - else if (JsonFeedbackMessageIsIncoming && message.Trim('\x20') == "}" + Delimiter) // Check for the end of a JSON message - { - JsonMessage.Append(message); - if(JsonCurlyBraceCounter == 0) + if (message.Contains('{')) + { + _jsonCurlyBraceCounter++; + } + + if (message.Contains('}')) + { + _jsonCurlyBraceCounter--; + } + + Debug.Console(2, this, "JSON Curly Brace Count: {0}", _jsonCurlyBraceCounter); + + if (!_jsonFeedbackMessageIsIncoming && message.Trim('\x20') == "{" + Delimiter) + // Check for the beginning of a new JSON message + { + _jsonFeedbackMessageIsIncoming = true; + _jsonCurlyBraceCounter = 1; // reset the counter for each new message + + _jsonMessage = new StringBuilder(); + + _jsonMessage.Append(message); + + if (CommDebuggingIsOn) { - JsonFeedbackMessageIsIncoming = false; + Debug.Console(2, this, "Incoming JSON message..."); + } + + return; + } + if (_jsonFeedbackMessageIsIncoming && message.Trim('\x20') == "}" + Delimiter) + // Check for the end of a JSON message + { + _jsonMessage.Append(message); + + if (_jsonCurlyBraceCounter == 0) + { + _jsonFeedbackMessageIsIncoming = false; if (CommDebuggingIsOn) - Debug.Console(2, this, "Complete JSON Received:\n{0}", JsonMessage.ToString()); + { + Debug.Console(2, this, "Complete JSON Received:\n{0}", _jsonMessage.ToString()); + } // Forward the complete message to be deserialized - DeserializeResponse(JsonMessage.ToString()); + DeserializeResponse(_jsonMessage.ToString()); } //JsonMessage = new StringBuilder(); @@ -534,45 +812,52 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom // NOTE: This must happen after the above conditions have been checked // Append subsequent partial JSON fragments to the string builder - if (JsonFeedbackMessageIsIncoming) + if (_jsonFeedbackMessageIsIncoming) { - JsonMessage.Append(message); + _jsonMessage.Append(message); //Debug.Console(1, this, "Building JSON:\n{0}", JsonMessage.ToString()); return; } if (CommDebuggingIsOn) + { Debug.Console(1, this, "Non-JSON response: '{0}'", message); + } - JsonCurlyBraceCounter = 0; // reset on non-JSON response + _jsonCurlyBraceCounter = 0; // reset on non-JSON response - if (!SyncState.InitialSyncComplete) + if (!_syncState.InitialSyncComplete) { switch (message.Trim().ToLower()) // remove the whitespace { case "*r login successful": + { + _syncState.LoginMessageReceived(); + + // Fire up a thread to send the intial commands. + CrestronInvoke.BeginInvoke(o => { - SyncState.LoginMessageReceived(); + Thread.Sleep(100); + // disable echo of commands + SendText("echo off"); + Thread.Sleep(100); + // set feedback exclusions + SendText("zFeedback Register Op: ex Path: /Event/InfoResult/info/callin_country_list"); + Thread.Sleep(100); + SendText("zFeedback Register Op: ex Path: /Event/InfoResult/info/callout_country_list"); + Thread.Sleep(100); - // Fire up a thread to send the intial commands. - CrestronInvoke.BeginInvoke((o) => - { - Thread.Sleep(100); - // disable echo of commands - SendText("echo off"); - Thread.Sleep(100); - // set feedback exclusions - SendText("zFeedback Register Op: ex Path: /Event/InfoResult/info/callin_country_list"); - Thread.Sleep(100); - SendText("zFeedback Register Op: ex Path: /Event/InfoResult/info/callout_country_list"); - Thread.Sleep(100); - // switch to json format - SendText("format json"); - }); + if (!_props.DisablePhonebookAutoDownload) + { + SendText("zFeedback Register Op: ex Path: /Event/Phonebook/AddedContact"); + } + // switch to json format + SendText("format json"); + }); - break; - } + break; + } } } } @@ -581,18 +866,22 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom /// Deserializes a JSON formatted response /// /// - void DeserializeResponse(string response) + private void DeserializeResponse(string response) { try { var trimmedResponse = response.Trim(); if (trimmedResponse.Length <= 0) + { return; + } var message = JObject.Parse(trimmedResponse); - eZoomRoomResponseType eType = (eZoomRoomResponseType)Enum.Parse(typeof(eZoomRoomResponseType), message["type"].Value(), true); + var eType = + (eZoomRoomResponseType) + Enum.Parse(typeof (eZoomRoomResponseType), message["type"].Value(), true); var topKey = message["topKey"].Value(); @@ -603,385 +892,432 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom switch (eType) { case eZoomRoomResponseType.zConfiguration: + { + switch (topKey.ToLower()) { - switch (topKey.ToLower()) + case "call": { - case "call": - { - JsonConvert.PopulateObject(responseObj.ToString(), Configuration.Call); + JsonConvert.PopulateObject(responseObj.ToString(), Configuration.Call); - break; - } - case "audio": - { - JsonConvert.PopulateObject(responseObj.ToString(), Configuration.Audio); - - break; - } - case "video": - { - JsonConvert.PopulateObject(responseObj.ToString(), Configuration.Video); - - break; - } - case "client": - { - JsonConvert.PopulateObject(responseObj.ToString(), Configuration.Client); - - break; - } - default: - { - break; - } + break; } - break; - } - case eZoomRoomResponseType.zCommand: - { - switch (topKey.ToLower()) + case "audio": { - case "phonebooklistresult": - { - JsonConvert.PopulateObject(responseObj.ToString(), Status.Phonebook); + JsonConvert.PopulateObject(responseObj.ToString(), Configuration.Audio); - if(!PhonebookSyncState.InitialSyncComplete) - { - PhonebookSyncState.InitialPhonebookFoldersReceived(); - PhonebookSyncState.PhonebookRootEntriesReceived(); - PhonebookSyncState.SetPhonebookHasFolders(false); - PhonebookSyncState.SetNumberOfContacts(Status.Phonebook.Contacts.Count); - } + break; + } + case "video": + { + JsonConvert.PopulateObject(responseObj.ToString(), Configuration.Video); - var directoryResults = new CodecDirectory(); + break; + } + case "client": + { + JsonConvert.PopulateObject(responseObj.ToString(), Configuration.Client); - directoryResults = zStatus.Phonebook.ConvertZoomContactsToGeneric(Status.Phonebook.Contacts); + break; + } + default: + { + break; + } + } + break; + } + case eZoomRoomResponseType.zCommand: + { + switch (topKey.ToLower()) + { + case "inforesult": + { + JsonConvert.PopulateObject(responseObj.ToString(), Status.Call.Info); + break; + } + case "phonebooklistresult": + { + JsonConvert.PopulateObject(responseObj.ToString(), Status.Phonebook); - DirectoryRoot = directoryResults; + if (!PhonebookSyncState.InitialSyncComplete) + { + PhonebookSyncState.InitialPhonebookFoldersReceived(); + PhonebookSyncState.PhonebookRootEntriesReceived(); + PhonebookSyncState.SetPhonebookHasFolders(false); + PhonebookSyncState.SetNumberOfContacts(Status.Phonebook.Contacts.Count); + } - OnDirectoryResultReturned(directoryResults); + var directoryResults = + zStatus.Phonebook.ConvertZoomContactsToGeneric(Status.Phonebook.Contacts); + DirectoryRoot = directoryResults; + + _currentDirectoryResult = DirectoryRoot; + + OnDirectoryResultReturned(directoryResults); + + break; + } + case "listparticipantsresult": + { + Debug.Console(1, this, "JTokenType: {0}", responseObj.Type); + + switch (responseObj.Type) + { + case JTokenType.Array: + Status.Call.Participants = + JsonConvert.DeserializeObject>( + responseObj.ToString()); break; - } - case "listparticipantsresult": + case JTokenType.Object: { - Debug.Console(1, this, "JTokenType: {0}", responseObj.Type); + // this is a single participant event notification - if (responseObj.Type == JTokenType.Array) + var participant = + JsonConvert.DeserializeObject( + responseObj.ToString()); + + if (participant != null) { - // if the type is array this must be the complete list - Status.Call.Participants = JsonConvert.DeserializeObject>(responseObj.ToString()); - } - else if (responseObj.Type == JTokenType.Object) - { - // this is a single participant event notification - - var participant = JsonConvert.DeserializeObject(responseObj.ToString()); - - if (participant != null) + switch (participant.Event) { - if (participant.Event == "ZRCUserChangedEventLeftMeeting" || participant.Event == "ZRCUserChangedEventUserInfoUpdated") + case "ZRCUserChangedEventUserInfoUpdated": + case "ZRCUserChangedEventLeftMeeting": { - var existingParticipant = Status.Call.Participants.FirstOrDefault(p => p.UserId.Equals(participant.UserId)); + var existingParticipant = + Status.Call.Participants.FirstOrDefault( + p => p.UserId.Equals(participant.UserId)); if (existingParticipant != null) { - if (participant.Event == "ZRCUserChangedEventLeftMeeting") + switch (participant.Event) { - // Remove participant - Status.Call.Participants.Remove(existingParticipant); - } - else if (participant.Event == "ZRCUserChangedEventUserInfoUpdated") - { - // Update participant - JsonConvert.PopulateObject(responseObj.ToString(), existingParticipant); + case "ZRCUserChangedEventLeftMeeting": + Status.Call.Participants.Remove(existingParticipant); + break; + case "ZRCUserChangedEventUserInfoUpdated": + JsonConvert.PopulateObject(responseObj.ToString(), + existingParticipant); + break; } } } - else if(participant.Event == "ZRCUserChangedEventJoinedMeeting") - { + break; + case "ZRCUserChangedEventJoinedMeeting": Status.Call.Participants.Add(participant); - } + break; } } - - PrintCurrentCallParticipants(); - - break; } - default: - { break; - } + } + + var participants = + zCommand.ListParticipant.GetGenericParticipantListFromParticipantsResult( + Status.Call.Participants); + + Participants.CurrentParticipants = participants; + + PrintCurrentCallParticipants(); + + break; + } + default: + { + break; } - break; } + break; + } case eZoomRoomResponseType.zEvent: + { + switch (topKey.ToLower()) { - switch (topKey.ToLower()) + case "phonebook": { - case "phonebook": + if (responseObj["Updated Contact"] != null) + { + var updatedContact = + JsonConvert.DeserializeObject( + responseObj["Updated Contact"].ToString()); + + var existingContact = + Status.Phonebook.Contacts.FirstOrDefault(c => c.Jid.Equals(updatedContact.Jid)); + + if (existingContact != null) { - if (responseObj["Updated Contact"] != null) - { - var updatedContact = JsonConvert.DeserializeObject(responseObj["Updated Contact"].ToString()); - - var existingContact = Status.Phonebook.Contacts.FirstOrDefault(c => c.Jid.Equals(updatedContact.Jid)); - - if (existingContact != null) - { - // Update existing contact - JsonConvert.PopulateObject(responseObj["Updated Contact"].ToString(), existingContact); - } - } - else if (responseObj["Added Contact"] != null) - { - var newContact = JsonConvert.DeserializeObject(responseObj["Updated Contact"].ToString()); - - // Add a new contact - Status.Phonebook.Contacts.Add(newContact); - } - - break; + // Update existing contact + JsonConvert.PopulateObject(responseObj["Updated Contact"].ToString(), + existingContact); } - case "bookingslistresult": + } + else if (responseObj["Added Contact"] != null) + { + var jToken = responseObj["Updated Contact"]; + if (jToken != null) { - if (!SyncState.InitialSyncComplete) - SyncState.LastQueryResponseReceived(); + var newContact = + JsonConvert.DeserializeObject( + jToken.ToString()); - var codecBookings = new List(); - - codecBookings = JsonConvert.DeserializeObject < List>(responseObj.ToString()); - - if (codecBookings != null && codecBookings.Count > 0) - { - CodecSchedule.Meetings = zCommand.GetGenericMeetingsFromBookingResult(codecBookings); - } - - break; + // Add a new contact + Status.Phonebook.Contacts.Add(newContact); } - case "bookings": - { - // Bookings have been updated, trigger a query to retreive the new bookings - if (responseObj["Updated"] != null) - GetBookings(); + } - break; - } - case "sharingstate": - { - JsonConvert.PopulateObject(responseObj.ToString(), Status.Call.Sharing); - - break; - } - case "incomingcallindication": - { - var incomingCall = JsonConvert.DeserializeObject(responseObj.ToString()); - - if (incomingCall != null) - { - var newCall = new CodecActiveCallItem(); - - newCall.Direction = eCodecCallDirection.Incoming; - newCall.Status = eCodecCallStatus.Ringing; - newCall.Type = eCodecCallType.Unknown; - newCall.Name = incomingCall.callerName; - newCall.Id = incomingCall.callerJID; - - ActiveCalls.Add(newCall); - - OnCallStatusChange(newCall); - } - - break; - } - case "treatedincomingcallindication": - { - var incomingCall = JsonConvert.DeserializeObject(responseObj.ToString()); - - if (incomingCall != null) - { - var existingCall = ActiveCalls.FirstOrDefault(c => c.Id.Equals(incomingCall.callerJID)); - - if (existingCall != null) - { - if (!incomingCall.accepted) - { - existingCall.Status = eCodecCallStatus.Disconnected; - } - else - { - existingCall.Status = eCodecCallStatus.Connecting; - } - - OnCallStatusChange(existingCall); - } - - UpdateCallStatus(); - } - - break; - } - case "calldisconnect": - { - var disconnectEvent = JsonConvert.DeserializeObject(responseObj.ToString()); - - if (disconnectEvent.Successful) - { - if (ActiveCalls.Count > 0) - { - var activeCall = ActiveCalls.FirstOrDefault(c => c.IsActiveCall); - - if (activeCall != null) - { - activeCall.Status = eCodecCallStatus.Disconnected; - - OnCallStatusChange(activeCall); - } - } - } - - UpdateCallStatus(); - break; - } - case "callconnecterror": - { - UpdateCallStatus(); - break; - } - case "videounmuterequest": - { - // TODO: notify room of a request to unmute video - break; - } - case "meetingneedspassword": - { - // TODO: notify user to enter a password - break; - } - case "needwaitforhost": - { - var needWait = JsonConvert.DeserializeObject(responseObj.ToString()); - - if (needWait.Wait) - { - // TODO: notify user to wait for host - } - - break; - } - case "openvideofailforhoststop": - { - // TODO: notify user that host has disabled unmuting video - break; - } - case "updatedcallrecordinfo": - { - JsonConvert.PopulateObject(responseObj.ToString(), Status.Call.CallRecordInfo); - - break; - } - default: - { - break; - } + break; + } + case "bookingslistresult": + { + if (!_syncState.InitialSyncComplete) + { + _syncState.LastQueryResponseReceived(); + } + + var codecBookings = JsonConvert.DeserializeObject>( + responseObj.ToString()); + + if (codecBookings != null && codecBookings.Count > 0) + { + CodecSchedule.Meetings = zCommand.GetGenericMeetingsFromBookingResult( + codecBookings, CodecSchedule.MeetingWarningMinutes); + } + + break; + } + case "bookings updated": + { + GetBookings(); + + break; + } + case "sharingstate": + { + JsonConvert.PopulateObject(responseObj.ToString(), Status.Call.Sharing); + + SetLayout(); + + break; + } + case "incomingcallindication": + { + var incomingCall = + JsonConvert.DeserializeObject(responseObj.ToString()); + + if (incomingCall != null) + { + var newCall = new CodecActiveCallItem + { + Direction = eCodecCallDirection.Incoming, + Status = eCodecCallStatus.Ringing, + Type = eCodecCallType.Unknown, + Name = incomingCall.callerName, + Id = incomingCall.callerJID + }; + + ActiveCalls.Add(newCall); + + OnCallStatusChange(newCall); + } + + break; + } + case "treatedincomingcallindication": + { + var incomingCall = + JsonConvert.DeserializeObject(responseObj.ToString()); + + if (incomingCall != null) + { + var existingCall = + ActiveCalls.FirstOrDefault(c => c.Id.Equals(incomingCall.callerJID)); + + if (existingCall != null) + { + existingCall.Status = !incomingCall.accepted + ? eCodecCallStatus.Disconnected + : eCodecCallStatus.Connecting; + + OnCallStatusChange(existingCall); + } + + UpdateCallStatus(); + } + + break; + } + case "calldisconnect": + { + var disconnectEvent = + JsonConvert.DeserializeObject(responseObj.ToString()); + + if (disconnectEvent.Successful) + { + if (ActiveCalls.Count > 0) + { + var activeCall = ActiveCalls.FirstOrDefault(c => c.IsActiveCall); + + if (activeCall != null) + { + activeCall.Status = eCodecCallStatus.Disconnected; + + OnCallStatusChange(activeCall); + } + } + var emptyList = new List(); + Participants.CurrentParticipants = emptyList; + } + + UpdateCallStatus(); + break; + } + case "callconnecterror": + { + UpdateCallStatus(); + break; + } + case "videounmuterequest": + { + // TODO: notify room of a request to unmute video + break; + } + case "meetingneedspassword": + { + // TODO: notify user to enter a password + break; + } + case "needwaitforhost": + { + var needWait = + JsonConvert.DeserializeObject(responseObj.ToString()); + + if (needWait.Wait) + { + // TODO: notify user to wait for host + } + + break; + } + case "openvideofailforhoststop": + { + // TODO: notify user that host has disabled unmuting video + break; + } + case "updatedcallrecordinfo": + { + JsonConvert.PopulateObject(responseObj.ToString(), Status.Call.CallRecordInfo); + + break; + } + case "phonecallstatus": + { + JsonConvert.PopulateObject(responseObj.ToString(), Status.PhoneCall); + break; + } + default: + { + break; } - break; } + break; + } case eZoomRoomResponseType.zStatus: + { + switch (topKey.ToLower()) { - switch (topKey.ToLower()) + case "login": { - case "login": - { - SyncState.LoginMessageReceived(); + _syncState.LoginMessageReceived(); - if (!SyncState.InitialQueryMessagesWereSent) - SetUpSyncQueries(); + if (!_syncState.InitialQueryMessagesWereSent) + { + SetUpSyncQueries(); + } - JsonConvert.PopulateObject(responseObj.ToString(), Status.Login); + JsonConvert.PopulateObject(responseObj.ToString(), Status.Login); - break; - } - case "systemunit": - { - JsonConvert.PopulateObject(responseObj.ToString(), Status.SystemUnit); - - break; - } - case "call": - { - JsonConvert.PopulateObject(responseObj.ToString(), Status.Call); - - UpdateCallStatus(); - - break; - } - case "capabilities": - { - JsonConvert.PopulateObject(responseObj.ToString(), Status.Capabilities); - break; - } - case "sharing": - { - JsonConvert.PopulateObject(responseObj.ToString(), Status.Sharing); - - break; - } - case "numberofscreens": - { - JsonConvert.PopulateObject(responseObj.ToString(), Status.NumberOfScreens); - break; - } - case "video": - { - JsonConvert.PopulateObject(responseObj.ToString(), Status.Video); - break; - } - case "camerashare": - { - JsonConvert.PopulateObject(responseObj.ToString(), Status.CameraShare); - break; - } - case "layout": - { - JsonConvert.PopulateObject(responseObj.ToString(), Status.Layout); - break; - } - case "audio input line": - { - JsonConvert.PopulateObject(responseObj.ToString(), Status.AudioInputs); - break; - } - case "audio output line": - { - JsonConvert.PopulateObject(responseObj.ToString(), Status.AudioOuputs); - break; - } - case "video camera line": - { - JsonConvert.PopulateObject(responseObj.ToString(), Status.Cameras); - - if(!SyncState.CamerasHaveBeenSetUp) - SetUpCameras(); - - break; - } - default: - { - break; - } + break; } + case "systemunit": + { + JsonConvert.PopulateObject(responseObj.ToString(), Status.SystemUnit); - break; + break; + } + case "call": + { + JsonConvert.PopulateObject(responseObj.ToString(), Status.Call); + + UpdateCallStatus(); + + break; + } + case "capabilities": + { + JsonConvert.PopulateObject(responseObj.ToString(), Status.Capabilities); + break; + } + case "sharing": + { + JsonConvert.PopulateObject(responseObj.ToString(), Status.Sharing); + + break; + } + case "numberofscreens": + { + JsonConvert.PopulateObject(responseObj.ToString(), Status.NumberOfScreens); + break; + } + case "video": + { + JsonConvert.PopulateObject(responseObj.ToString(), Status.Video); + break; + } + case "camerashare": + { + JsonConvert.PopulateObject(responseObj.ToString(), Status.CameraShare); + break; + } + case "layout": + { + JsonConvert.PopulateObject(responseObj.ToString(), Status.Layout); + break; + } + case "audio input line": + { + JsonConvert.PopulateObject(responseObj.ToString(), Status.AudioInputs); + break; + } + case "audio output line": + { + JsonConvert.PopulateObject(responseObj.ToString(), Status.AudioOuputs); + break; + } + case "video camera line": + { + JsonConvert.PopulateObject(responseObj.ToString(), Status.Cameras); + + if (!_syncState.CamerasHaveBeenSetUp) + { + SetUpCameras(); + } + + break; + } + default: + { + break; + } } + + break; + } default: - { - Debug.Console(1, "Unknown Response Type:"); - break; - } + { + Debug.Console(1, "Unknown Response Type:"); + break; + } } - } catch (Exception ex) { @@ -989,23 +1325,44 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom } } + private void SetLayout() + { + if (!_props.AutoDefaultLayouts) return; + + if ( + (Status.Call.Sharing.State == zEvent.eSharingState.Receiving || + Status.Call.Sharing.State == zEvent.eSharingState.Sending)) + { + SendText(String.Format("zconfiguration call layout style: {0}", + _props.DefaultSharingLayout)); + } + else + { + SendText(String.Format("zconfiguration call layout style: {0}", + _props.DefaultCallLayout)); + } + } + public void PrintCurrentCallParticipants() { - if (Debug.Level > 0) + if (Debug.Level <= 0) { - Debug.Console(1, this, "****************************Call Participants***************************"); - foreach (var participant in Status.Call.Participants) - { - Debug.Console(1, this, "Name: {0} Audio: {1} IsHost: {2}", participant.UserName, participant.AudioStatusState, participant.IsHost); - } - Debug.Console(1, this, "************************************************************************"); + return; } + + Debug.Console(1, this, "****************************Call Participants***************************"); + foreach (var participant in Participants.CurrentParticipants) + { + Debug.Console(1, this, "Name: {0} Audio: {1} IsHost: {2}", participant.Name, + participant.AudioMuteFb, participant.IsHost); + } + Debug.Console(1, this, "************************************************************************"); } /// /// Retrieves bookings list /// - void GetBookings() + private void GetBookings() { SendText("zCommand Bookings List"); } @@ -1014,28 +1371,38 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom /// /// Updates the current call status /// - void UpdateCallStatus() + private void UpdateCallStatus() { - zStatus.eCallStatus callStatus; - if (Status.Call != null) { - callStatus = Status.Call.Status; + var callStatus = Status.Call.Status; // If not currently in a meeting, intialize the call object if (callStatus != zStatus.eCallStatus.IN_MEETING || callStatus != zStatus.eCallStatus.CONNECTING_MEETING) { - Status.Call = new zStatus.Call(); - Status.Call.Status = callStatus; // set the status after initializing the object + Status.Call = new zStatus.Call {Status = callStatus}; + + SetUpCallFeedbackActions(); } if (ActiveCalls.Count == 0) { - if(callStatus == zStatus.eCallStatus.CONNECTING_MEETING) + if (callStatus == zStatus.eCallStatus.CONNECTING_MEETING || + callStatus == zStatus.eCallStatus.IN_MEETING) { - var newCall = new CodecActiveCallItem(); + var newStatus = eCodecCallStatus.Unknown; - newCall.Status = eCodecCallStatus.Connecting; + switch (callStatus) + { + case zStatus.eCallStatus.CONNECTING_MEETING: + newStatus = eCodecCallStatus.Connecting; + break; + case zStatus.eCallStatus.IN_MEETING: + newStatus = eCodecCallStatus.Connected; + break; + } + + var newCall = new CodecActiveCallItem {Status = newStatus}; ActiveCalls.Add(newCall); @@ -1046,18 +1413,18 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom { var existingCall = ActiveCalls.FirstOrDefault(c => !c.Status.Equals(eCodecCallStatus.Ringing)); - if (callStatus == zStatus.eCallStatus.IN_MEETING) + switch (callStatus) { - existingCall.Status = eCodecCallStatus.Connected; - } - else if (callStatus == zStatus.eCallStatus.NOT_IN_MEETING) - { - existingCall.Status = eCodecCallStatus.Disconnected; + case zStatus.eCallStatus.IN_MEETING: + existingCall.Status = eCodecCallStatus.Connected; + break; + case zStatus.eCallStatus.NOT_IN_MEETING: + existingCall.Status = eCodecCallStatus.Disconnected; + break; } OnCallStatusChange(existingCall); } - } Debug.Console(1, this, "****************************Active Calls*********************************"); @@ -1067,7 +1434,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom { var call = ActiveCalls[i]; - Debug.Console(1, this, + Debug.Console(1, this, @"Name: {0} ID: {1} IsActive: {2} @@ -1080,14 +1447,28 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom ActiveCalls.Remove(call); } } - Debug.Console(1, this, "**************************************************************************"); + //clear participants list after call cleanup + if (ActiveCalls.Count == 0) + { + Participants.CurrentParticipants = new List(); + } + } + + protected override void OnCallStatusChange(CodecActiveCallItem item) + { + base.OnCallStatusChange(item); + + if (_props.AutoDefaultLayouts) + { + SetLayout(); + } } public override void StartSharing() { - throw new NotImplementedException(); + SendText("zCommand Call Sharing HDMI Start"); } /// @@ -1111,19 +1492,23 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom public override void PrivacyModeToggle() { if (PrivacyModeIsOnFeedback.BoolValue) + { PrivacyModeOff(); + } else + { PrivacyModeOn(); + } } public override void MuteOff() { - SetVolume((ushort)PreviousVolumeLevel); + SetVolume((ushort) _previousVolumeLevel); } public override void MuteOn() { - PreviousVolumeLevel = Configuration.Audio.Output.Volume; // Store the previous level for recall + _previousVolumeLevel = Configuration.Audio.Output.Volume; // Store the previous level for recall SetVolume(0); } @@ -1131,18 +1516,23 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom public override void MuteToggle() { if (MuteFeedback.BoolValue) + { MuteOff(); + } else + { MuteOn(); + } } + /// /// Increments the voluem /// /// public override void VolumeUp(bool pressRelease) { - // TODO: Implment volume increment that calls SetVolume() + // TODO: Implment volume decrement that calls SetVolume() } /// @@ -1169,7 +1559,6 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom /// public void VolumeSetToDefault() { - } /// @@ -1188,26 +1577,48 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom // No corresponding function on device } - public override void ExecuteSwitch(object selector) + public override void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge) { - (selector as Action)(); + LinkVideoCodecToApi(this, trilist, joinStart, joinMapKey, bridge); } - public void ExecuteSwitch(object inputSelector, object outputSelector, eRoutingSignalType signalType) + public override void ExecuteSwitch(object selector) { - ExecuteSwitch(inputSelector); + var action = selector as Action; + if (action == null) + { + return; + } + + action(); + } + + public void AcceptCall() + { + var incomingCall = + ActiveCalls.FirstOrDefault( + c => c.Status.Equals(eCodecCallStatus.Ringing) && c.Direction.Equals(eCodecCallDirection.Incoming)); + + AcceptCall(incomingCall); } public override void AcceptCall(CodecActiveCallItem call) { - var incomingCall = ActiveCalls.FirstOrDefault(c => c.Status.Equals(eCodecCallStatus.Ringing) && c.Direction.Equals(eCodecCallDirection.Incoming)); - SendText(string.Format("zCommand Call Accept callerJID: {0}", incomingCall.Id)); + SendText(string.Format("zCommand Call Accept callerJID: {0}", call.Id)); + } + + public void RejectCall() + { + var incomingCall = + ActiveCalls.FirstOrDefault( + c => c.Status.Equals(eCodecCallStatus.Ringing) && c.Direction.Equals(eCodecCallDirection.Incoming)); + + RejectCall(incomingCall); } public override void RejectCall(CodecActiveCallItem call) { - var incomingCall = ActiveCalls.FirstOrDefault(c => c.Status.Equals(eCodecCallStatus.Ringing) && c.Direction.Equals(eCodecCallDirection.Incoming)); - SendText(string.Format("zCommand Call Reject callerJID: {0}", incomingCall.Id)); + SendText(string.Format("zCommand Call Reject callerJID: {0}", call.Id)); } public override void Dial(Meeting meeting) @@ -1234,9 +1645,14 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom Debug.Console(1, this, "Attempting to Dial (Invite): {0}", ic.Name); if (!IsInCall) - SendText(string.Format("zCommand Invite Duration: {0} user: {1}", DefaultMeetingDurationMin, ic.ContactId)); + { + SendText(string.Format("zCommand Invite Duration: {0} user: {1}", DefaultMeetingDurationMin, + ic.ContactId)); + } else + { SendText(string.Format("zCommand Call invite user: {0}", ic.ContactId)); + } } } @@ -1252,42 +1668,14 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom public override void SendDtmf(string s) { - throw new NotImplementedException(); + SendDtmfToPhone(s); } - - #region IHasCodecSelfView Members - - public BoolFeedback SelfviewIsOnFeedback { get; private set; } - - public void SelfViewModeOn() - { - SendText("zConfiguration Video hide_conf_self_video: off"); - } - - public void SelfViewModeOff() - { - SendText("zConfiguration Video hide_conf_self_video: on"); - } - - public void SelfViewModeToggle() - { - if (SelfviewIsOnFeedback.BoolValue) - SelfViewModeOff(); - else - SelfViewModeOn(); - } - - #endregion - - #region IHasDirectory Members - - public event EventHandler DirectoryResultReturned; - + /// /// Call when directory results are updated /// /// - void OnDirectoryResultReturned(CodecDirectory result) + private void OnDirectoryResultReturned(CodecDirectory result) { CurrentDirectoryResultIsNotDirectoryRoot.FireUpdate(); @@ -1295,7 +1683,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom var handler = DirectoryResultReturned; if (handler != null) { - handler(this, new DirectoryEventArgs() + handler(this, new DirectoryEventArgs { Directory = result, DirectoryIsOnRoot = !CurrentDirectoryResultIsNotDirectoryRoot.BoolValue @@ -1305,94 +1693,10 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom //PrintDirectory(result); } - public CodecDirectory DirectoryRoot { get; private set; } - - public CodecDirectory CurrentDirectoryResult - { - get - { - if (DirectoryBrowseHistory.Count > 0) - return DirectoryBrowseHistory[DirectoryBrowseHistory.Count - 1]; - else - return DirectoryRoot; - } - } - - public CodecPhonebookSyncState PhonebookSyncState { get; private set; } - - public void SearchDirectory(string searchString) - { - var directoryResults = new CodecDirectory(); - - directoryResults.AddContactsToDirectory(DirectoryRoot.CurrentDirectoryResults.FindAll(c => c.Name.IndexOf(searchString, 0, StringComparison.OrdinalIgnoreCase) > -1)); - - DirectoryBrowseHistory.Add(directoryResults); - - OnDirectoryResultReturned(directoryResults); - } - - public void GetDirectoryFolderContents(string folderId) - { - var directoryResults = new CodecDirectory(); - - directoryResults.ResultsFolderId = folderId; - directoryResults.AddContactsToDirectory(DirectoryRoot.CurrentDirectoryResults.FindAll(c => c.FolderId.Equals(folderId))); - - DirectoryBrowseHistory.Add(directoryResults); - - OnDirectoryResultReturned(directoryResults); - } - - public void SetCurrentDirectoryToRoot() - { - DirectoryBrowseHistory.Clear(); - - OnDirectoryResultReturned(DirectoryRoot); - } - - 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); - } - - public BoolFeedback CurrentDirectoryResultIsNotDirectoryRoot { get; private set; } - - public List DirectoryBrowseHistory { get; private set; } - - #endregion - - #region IHasScheduleAwareness Members - - public CodecScheduleAwareness CodecSchedule { get; private set; } - - public void GetSchedule() - { - GetBookings(); - } - - #endregion - /// /// Builds the cameras List by using the Zoom Room zStatus.Cameras data. Could later be modified to build from config data /// - void SetUpCameras() + private void SetUpCameras() { SelectedCameraFeedback = new StringFeedback(() => Configuration.Video.Camera.SelectedId); @@ -1405,76 +1709,139 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom Cameras.Add(camera); if (cam.Selected) - SelectedCamera = camera; + { + SelectedCamera = camera; + } } if (IsInCall) + { UpdateFarEndCameras(); + } - SyncState.CamerasSetUp(); + _syncState.CamerasSetUp(); } /// /// Dynamically creates far end cameras for call participants who have far end control enabled. /// - void UpdateFarEndCameras() + private void UpdateFarEndCameras() { // TODO: set up far end cameras for the current call } - #region IHasCameras Members + #region Implementation of IHasParticipants - public event EventHandler CameraSelected; + public CodecParticipants Participants { get; private set; } - public List Cameras { get; private set; } + #endregion - private CameraBase _selectedCamera; + #region Implementation of IHasCameraOff - public CameraBase SelectedCamera + public BoolFeedback CameraIsOffFeedback { get; private set; } + + public void CameraOff() { - get - { - return _selectedCamera; - } - private set - { - _selectedCamera = value; - SelectedCameraFeedback.FireUpdate(); - ControllingFarEndCameraFeedback.FireUpdate(); - - var handler = CameraSelected; - if (handler != null) - { - handler(this, new CameraSelectedEventArgs(SelectedCamera)); - } - } - } - - - public StringFeedback SelectedCameraFeedback { get; private set; } - - public void SelectCamera(string key) - { - if(Cameras != null) - { - var camera = Cameras.FirstOrDefault(c => c.Key.IndexOf(key, StringComparison.OrdinalIgnoreCase) > -1); - if (camera != null) - { - Debug.Console(1, this, "Selected Camera with key: '{0}'", camera.Key); - SelectedCamera = camera; - } - else - Debug.Console(1, this, "Unable to select camera with key: '{0}'", key); - } + SendText("zConfiguration Call Camera Mute: On"); } #endregion - #region IHasFarEndCameraControl Members + #region Implementation of IHasCameraAutoMode - public CameraBase FarEndCamera { get; private set; } + //Zoom doesn't support camera auto modes. Setting this to just unmute video + public void CameraAutoModeOn() + { + throw new NotImplementedException("Zoom Room Doesn't support camera auto mode"); + } - public BoolFeedback ControllingFarEndCameraFeedback { get; private set; } + //Zoom doesn't support camera auto modes. Setting this to just unmute video + public void CameraAutoModeOff() + { + SendText("zConfiguration Call Camera Mute: Off"); + } + + public void CameraAutoModeToggle() + { + throw new NotImplementedException("Zoom Room doesn't support camera auto mode"); + } + + public BoolFeedback CameraAutoModeIsOnFeedback { get; private set; } + + #endregion + + #region Implementation of IHasFarEndContentStatus + + public BoolFeedback ReceivingContent { get; private set; } + + #endregion + + #region Implementation of IHasSelfviewPosition + + private CodecCommandWithLabel _currentSelfviewPipPosition; + + public StringFeedback SelfviewPipPositionFeedback { get; private set; } + + public void SelfviewPipPositionSet(CodecCommandWithLabel position) + { + SendText(String.Format("zConfiguration Call Layout Position: {0}", position.Command)); + } + + 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]); + } + } + + public List SelfviewPipPositions = new List() + { + new CodecCommandWithLabel("UpLeft", "Center Left"), + new CodecCommandWithLabel("UpRight", "Center Right"), + new CodecCommandWithLabel("DownRight", "Lower Right"), + new CodecCommandWithLabel("DownLeft", "Lower Left") + }; + + private void ComputeSelfviewPipStatus() + { + _currentSelfviewPipPosition = + SelfviewPipPositions.FirstOrDefault( + p => p.Command.ToLower().Equals(Configuration.Call.Layout.Position.ToString().ToLower())); + } + + #endregion + + #region Implementation of IHasPhoneDialing + + private Func PhoneOffHookFeedbackFunc {get {return () => Status.PhoneCall.OffHook; }} + private Func CallerIdNameFeedbackFunc { get { return () => Status.PhoneCall.PeerDisplayName; } } + private Func CallerIdNumberFeedbackFunc { get { return () => Status.PhoneCall.PeerNumber; } } + + public BoolFeedback PhoneOffHookFeedback { get; private set; } + public StringFeedback CallerIdNameFeedback { get; private set; } + public StringFeedback CallerIdNumberFeedback { get; private set; } + + public void DialPhoneCall(string number) + { + SendText(String.Format("zCommand Dial PhoneCallOut Number: {0}", number)); + } + + public void EndPhoneCall() + { + SendText(String.Format("zCommand Dial PhoneHangUp CallId: {0}", Status.PhoneCall.CallId)); + } + + public void SendDtmfToPhone(string digit) + { + SendText(String.Format("zCommand SendSipDTMF CallId: {0} Key: {1}", Status.PhoneCall.CallId, digit)); + } #endregion } @@ -1484,15 +1851,18 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom /// public class ZoomRoomInfo : VideoCodecInfo { + public ZoomRoomInfo(ZoomRoomStatus status, ZoomRoomConfiguration configuration) + { + Status = status; + Configuration = configuration; + } + public ZoomRoomStatus Status { get; private set; } public ZoomRoomConfiguration Configuration { get; private set; } public override bool AutoAnswerEnabled { - get - { - return Status.SystemUnit.RoomInfo.AutoAnswerIsEnabled; - } + get { return Status.SystemUnit.RoomInfo.AutoAnswerIsEnabled; } } public override string E164Alias @@ -1500,9 +1870,10 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom get { if (!string.IsNullOrEmpty(Status.SystemUnit.MeetingNumber)) + { return Status.SystemUnit.MeetingNumber; - else - return string.Empty; + } + return string.Empty; } } @@ -1511,9 +1882,10 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom get { if (!string.IsNullOrEmpty(Status.Call.Info.meeting_list_item.third_party.h323_address)) + { return Status.Call.Info.meeting_list_item.third_party.h323_address; - else - return string.Empty; + } + return string.Empty; } } @@ -1522,9 +1894,10 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom get { if (!string.IsNullOrEmpty(Status.SystemUnit.RoomInfo.AccountEmail)) + { return Status.SystemUnit.RoomInfo.AccountEmail; - else - return string.Empty; + } + return string.Empty; } } @@ -1538,9 +1911,10 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom get { if (!string.IsNullOrEmpty(Status.Call.Info.dialIn)) + { return Status.Call.Info.dialIn; - else - return string.Empty; + } + return string.Empty; } } @@ -1549,17 +1923,12 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom get { if (!string.IsNullOrEmpty(Status.Call.Info.meeting_list_item.third_party.sip_address)) + { return Status.Call.Info.meeting_list_item.third_party.sip_address; - else - return string.Empty; + } + return string.Empty; } } - - public ZoomRoomInfo(ZoomRoomStatus status, ZoomRoomConfiguration configuration) - { - Status = status; - Configuration = configuration; - } } /// @@ -1567,28 +1936,32 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom /// public class ZoomRoomSyncState : IKeyed { - bool _InitialSyncComplete; + private readonly ZoomRoom _parent; + private readonly CrestronQueue _syncQueries; + private bool _initialSyncComplete; - public event EventHandler InitialSyncCompleted; - - private CrestronQueue SyncQueries; - - private ZoomRoom Parent; - - public string Key { get; private set; } + public ZoomRoomSyncState(string key, ZoomRoom parent) + { + _parent = parent; + Key = key; + _syncQueries = new CrestronQueue(50); + CodecDisconnected(); + } public bool InitialSyncComplete { - get { return _InitialSyncComplete; } + get { return _initialSyncComplete; } private set { - if (value == true) + if (value) { var handler = InitialSyncCompleted; if (handler != null) + { handler(this, new EventArgs()); + } } - _InitialSyncComplete = value; + _initialSyncComplete = value; } } @@ -1598,28 +1971,28 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom public bool LastQueryResponseWasReceived { get; private set; } - public bool CamerasHaveBeenSetUp { get; private set;} + public bool CamerasHaveBeenSetUp { get; private set; } - public ZoomRoomSyncState(string key, ZoomRoom parent) - { - Parent = parent; - Key = key; - SyncQueries = new CrestronQueue(50); - CodecDisconnected(); - } + #region IKeyed Members + + public string Key { get; private set; } + + #endregion + + public event EventHandler InitialSyncCompleted; public void StartSync() { DequeueQueries(); } - void DequeueQueries() + private void DequeueQueries() { - while (!SyncQueries.IsEmpty) + while (!_syncQueries.IsEmpty) { - var query = SyncQueries.Dequeue(); + var query = _syncQueries.Dequeue(); - Parent.SendText(query); + _parent.SendText(query); } InitialQueryMessagesSent(); @@ -1627,7 +2000,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom public void AddQueryToQueue(string query) { - SyncQueries.Enqueue(query); + _syncQueries.Enqueue(query); } public void LoginMessageReceived() @@ -1660,7 +2033,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom public void CodecDisconnected() { - SyncQueries.Clear(); + _syncQueries.Clear(); LoginMessageWasReceived = false; InitialQueryMessagesWereSent = false; LastQueryResponseWasReceived = false; @@ -1668,15 +2041,18 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom InitialSyncComplete = false; } - void CheckSyncStatus() + private void CheckSyncStatus() { - if (LoginMessageWasReceived && InitialQueryMessagesWereSent && LastQueryResponseWasReceived && CamerasHaveBeenSetUp) + if (LoginMessageWasReceived && InitialQueryMessagesWereSent && LastQueryResponseWasReceived && + CamerasHaveBeenSetUp) { InitialSyncComplete = true; Debug.Console(1, this, "Initial Codec Sync Complete!"); } else + { InitialSyncComplete = false; + } } } @@ -1684,15 +2060,14 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom { public ZoomRoomFactory() { - TypeNames = new List() { "zoomroom" }; + TypeNames = new List {"zoomroom"}; } public override EssentialsDevice BuildDevice(DeviceConfig dc) { Debug.Console(1, "Factory Attempting to create new ZoomRoom Device"); var comm = CommFactory.CreateCommForDevice(dc); - return new VideoCodec.ZoomRoom.ZoomRoom(dc, comm); + return new ZoomRoom(dc, comm); } } - } \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ZoomRoomCamera.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ZoomRoomCamera.cs index 4fa377ae..7a3abdaa 100644 --- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ZoomRoomCamera.cs +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ZoomRoomCamera.cs @@ -68,7 +68,6 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom /// /// Builds the command and triggers the parent ZoomRoom to send it /// - /// /// /// void SendCommand(eZoomRoomCameraState state, eZoomRoomCameraAction action) @@ -79,23 +78,25 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom void StartContinueTimer() { - if(ContinueTimer == null) - ContinueTimer = new CTimer((o) => SendContinueAction(LastAction), ContinueTime); + if (ContinueTimer == null) + ContinueTimer = new CTimer((o) => SendContinueAction(LastAction), null, ContinueTime, ContinueTime); } void SendContinueAction(eZoomRoomCameraAction action) { SendCommand(eZoomRoomCameraState.Continue, action); - ContinueTimer.Reset(); } void StopContinueTimer() { - if (ContinueTimer != null) + if (ContinueTimer == null) { - ContinueTimer.Stop(); - ContinueTimer.Dispose(); + return; } + + ContinueTimer.Stop(); + ContinueTimer.Dispose(); + ContinueTimer = null; } #region IHasCameraPtzControl Members @@ -111,22 +112,26 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom public void PanLeft() { - if (!isMoving) + if (isMoving) { - SendCommand(eZoomRoomCameraState.Start, eZoomRoomCameraAction.Left); - StartContinueTimer(); - isPanning = true; + return; } + + SendCommand(eZoomRoomCameraState.Start, eZoomRoomCameraAction.Left); + StartContinueTimer(); + isPanning = true; } public void PanRight() { - if (!isMoving) + if (isMoving) { - SendCommand(eZoomRoomCameraState.Start, eZoomRoomCameraAction.Right); - StartContinueTimer(); - isPanning = true; + return; } + + SendCommand(eZoomRoomCameraState.Start, eZoomRoomCameraAction.Right); + StartContinueTimer(); + isPanning = true; } public void PanStop() diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ZoomRoomPropertiesConfig.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ZoomRoomPropertiesConfig.cs index d60b63c2..38238023 100644 --- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ZoomRoomPropertiesConfig.cs +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ZoomRoomPropertiesConfig.cs @@ -12,5 +12,18 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom public class ZoomRoomPropertiesConfig { public CommunicationMonitorConfig CommunicationMonitorProperties { get; set; } + + public bool DisablePhonebookAutoDownload { get; set; } + public bool SupportsCameraAutoMode { get; set; } + public bool SupportsCameraOff { get; set; } + + //if true, the layouts will be set automatically when sharing starts/ends or a call is joined + public bool AutoDefaultLayouts { get; set; } + + /* This layout will be selected when Sharing starts (either from Far end or locally)*/ + public string DefaultSharingLayout { get; set; } + + //This layout will be selected when a call is connected and no content is being shared + public string DefaultCallLayout { get; set; } } } \ No newline at end of file diff --git a/packages.config b/packages.config index 11087174..296413b6 100644 --- a/packages.config +++ b/packages.config @@ -1,3 +1,3 @@ - + \ No newline at end of file