diff --git a/.github/scripts/ZipBuildOutput.ps1 b/.github/scripts/ZipBuildOutput.ps1 index bf3c9d94..dfc6480d 100644 --- a/.github/scripts/ZipBuildOutput.ps1 +++ b/.github/scripts/ZipBuildOutput.ps1 @@ -8,6 +8,7 @@ $destination = "$($Env:GITHUB_HOME)\output" New-Item -ItemType Directory -Force -Path ($destination) Get-ChildItem ($destination) $exclusions = @(git submodule foreach --quiet 'echo $name') +$exclusions += "Newtonsoft.Compact.Json.dll" # Trying to get any .json schema files (not currently working) # Gets any files with the listed extensions. Get-ChildItem -recurse -Path "$($Env:GITHUB_WORKSPACE)" -include "*.clz", "*.cpz", "*.cplz", "*.dll", "*.nuspec" | ForEach-Object { diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index a74ca03e..94c45937 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -8,12 +8,9 @@ on: - bugfix/* - release/* - development - pull_request: - branches: - - development env: - # solution path doesn't need slashes unless there it is multiple folders deep + # solution path doesn't need slashes unless it is multiple folders deep # solution name does not include extension. .sln is assumed SOLUTION_PATH: PepperDashEssentials SOLUTION_FILE: PepperDashEssentials diff --git a/PepperDashEssentials/Room/Types/EssentialsDualDisplayRoom.cs b/PepperDashEssentials/Room/Types/EssentialsDualDisplayRoom.cs index d58f6ac7..d3812feb 100644 --- a/PepperDashEssentials/Room/Types/EssentialsDualDisplayRoom.cs +++ b/PepperDashEssentials/Room/Types/EssentialsDualDisplayRoom.cs @@ -207,7 +207,7 @@ namespace PepperDash.Essentials DefaultAudioDevice = DeviceManager.GetDeviceForKey(PropertiesConfig.DefaultAudioKey) as IBasicVolumeControls; - Initialize(); + InitializeRoom(); } catch (Exception e) { @@ -215,7 +215,7 @@ namespace PepperDash.Essentials } } - void Initialize() + void InitializeRoom() { if (DefaultAudioDevice is IBasicVolumeControls) DefaultVolumeControls = DefaultAudioDevice as IBasicVolumeControls; diff --git a/PepperDashEssentials/Room/Types/EssentialsHuddleSpaceRoom.cs b/PepperDashEssentials/Room/Types/EssentialsHuddleSpaceRoom.cs index 7a57e9c1..3123f20f 100644 --- a/PepperDashEssentials/Room/Types/EssentialsHuddleSpaceRoom.cs +++ b/PepperDashEssentials/Room/Types/EssentialsHuddleSpaceRoom.cs @@ -156,7 +156,7 @@ namespace PepperDash.Essentials DefaultAudioDevice = DeviceManager.GetDeviceForKey(PropertiesConfig.DefaultAudioKey) as IRoutingSinkWithSwitching; - Initialize(); + InitializeRoom(); } catch (Exception e) { @@ -164,7 +164,7 @@ namespace PepperDash.Essentials } } - void Initialize() + void InitializeRoom() { if (DefaultAudioDevice is IBasicVolumeControls) DefaultVolumeControls = DefaultAudioDevice as IBasicVolumeControls; diff --git a/PepperDashEssentials/Room/Types/EssentialsHuddleVtc1Room.cs b/PepperDashEssentials/Room/Types/EssentialsHuddleVtc1Room.cs index 906136b4..a5fda4bd 100644 --- a/PepperDashEssentials/Room/Types/EssentialsHuddleVtc1Room.cs +++ b/PepperDashEssentials/Room/Types/EssentialsHuddleVtc1Room.cs @@ -226,7 +226,7 @@ namespace PepperDash.Essentials DefaultAudioDevice = DeviceManager.GetDeviceForKey(PropertiesConfig.DefaultAudioKey) as IBasicVolumeControls; - Initialize(); + InitializeRoom(); } catch (Exception e) { @@ -234,7 +234,7 @@ namespace PepperDash.Essentials } } - void Initialize() + void InitializeRoom() { try { diff --git a/PepperDashEssentials/Room/Types/IEssentialsHuddleSpaceRoom.cs b/PepperDashEssentials/Room/Types/IEssentialsHuddleSpaceRoom.cs new file mode 100644 index 00000000..45074fb7 --- /dev/null +++ b/PepperDashEssentials/Room/Types/IEssentialsHuddleSpaceRoom.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.DeviceTypeInterfaces; +using PepperDash.Essentials.Room.Config; +using PepperDash.Essentials.Core.Devices; +using PepperDash.Essentials.Devices.Common.Codec; +using PepperDash.Essentials.Devices.Common.VideoCodec; +using PepperDash.Essentials.Devices.Common.AudioCodec; + + +using PepperDash.Core; + +namespace PepperDash.Essentials +{ + public interface IEssentialsHuddleSpaceRoom : IEssentialsRoom, IHasCurrentSourceInfoChange, IRunRouteAction, IHasDefaultDisplay + { + bool ExcludeFromGlobalFunctions { get; } + + void RunRouteAction(string routeKey); + + EssentialsHuddleRoomPropertiesConfig PropertiesConfig { get; } + + IBasicVolumeControls CurrentVolumeControls { get; } + + event EventHandler CurrentVolumeDeviceChange; + } + + public interface IEssentialsHuddleVtc1Room : IEssentialsRoom, IHasCurrentSourceInfoChange, + IHasCurrentVolumeControls, IRunRouteAction, IRunDefaultCallRoute, IHasVideoCodec, IHasAudioCodec, IHasDefaultDisplay + { + EssentialsHuddleVtc1PropertiesConfig PropertiesConfig { get; } + + void RunRouteAction(string routeKey); + + IHasScheduleAwareness ScheduleSource { get; } + + string DefaultCodecRouteString { get; } + } +} \ No newline at end of file diff --git a/PepperDashEssentials/UI/JoinConstants/UIBoolJoin.cs b/PepperDashEssentials/UI/JoinConstants/UIBoolJoin.cs index 81be9002..1a127000 100644 --- a/PepperDashEssentials/UI/JoinConstants/UIBoolJoin.cs +++ b/PepperDashEssentials/UI/JoinConstants/UIBoolJoin.cs @@ -49,6 +49,16 @@ namespace PepperDash.Essentials /// 1006 /// public const uint CallEndAllConfirmVisible = 1006; + /// + /// 1007 + /// + public const uint MeetingPasswordVisible = 1007; + /// + /// 1008 + /// + public const uint MeetingLeavePress = 1008; + + @@ -153,6 +163,11 @@ namespace PepperDash.Essentials public const uint VCFavoriteVisibleStart = 1221; // RANGE IN USE public const uint VCFavoriteVisibleEnd = 1225; + + /// + /// 1230 + /// + public const uint VCStagingMeetNowPress = 1230; /// /// 1231 /// @@ -752,10 +767,10 @@ namespace PepperDash.Essentials /// 15044 Close button for source modal overlay /// public const uint SourceBackgroundOverlayClosePress = 15044; - ///// - ///// 15045 - Visibility for the bar containing call navigation button list - ///// - //public const uint CallStagingBarVisible = 15045; + /// + /// 15045 + /// + public const uint ZoomRoomContentSharingVisible = 15045; /// /// 15046 /// @@ -844,6 +859,11 @@ namespace PepperDash.Essentials /// 15067 /// public const uint NotificationRibbonVisible = 15067; + /// + /// 15068 + /// + public const uint HeaderMeetingInfoVisible = 15068; + /// /// 15083 - Press for Call help desk on AC/VC /// @@ -948,5 +968,24 @@ namespace PepperDash.Essentials /// 15214 /// public const uint PinDialogDot4 = 15214; + + // Password Prompt Dialog ************************** + + /// + /// 15301 + /// + public const uint PasswordPromptDialogVisible = 15301; + /// + /// 15302 + /// + public const uint PasswordPromptTextPress = 15302; + /// + /// 15306 + /// + public const uint PasswordPromptCancelPress = 15306; + /// + /// 15307 + /// + public const uint PasswordPromptErrorVisible = 15307; } } \ No newline at end of file diff --git a/PepperDashEssentials/UI/JoinConstants/UIStringlJoin.cs b/PepperDashEssentials/UI/JoinConstants/UIStringlJoin.cs index 9b2b8a95..6741be35 100644 --- a/PepperDashEssentials/UI/JoinConstants/UIStringlJoin.cs +++ b/PepperDashEssentials/UI/JoinConstants/UIStringlJoin.cs @@ -27,6 +27,33 @@ namespace PepperDash.Essentials /// 1004 /// public const uint CallSharedSourceNameText = 1004; + /// + /// 1005 + /// + public const uint MeetingIdText = 1005; + /// + /// 1006 + /// + public const uint MeetingHostText = 1006; + /// + /// 1007 + /// + public const uint MeetingPasswordText = 1007; + /// + /// 1008 + /// + public const uint MeetingLeaveText = 1008; + /// + /// 1009 + /// + public const uint MeetingNameText = 1009; + + /// + /// 1240 - Used to determine text for meeting start button + /// + public const uint MeetingStartButtonText = 1240; + + /// @@ -118,6 +145,14 @@ namespace PepperDash.Essentials //----- through 3120 + /// + /// 3201 + /// + public const uint PasswordPromptMessageText = 3201; + /// + /// 3202 + /// + public const uint PasswordPromptPasswordText = 3202; /// /// 3812 diff --git a/PepperDashEssentials/UI/JoinConstants/UIUshortJoin.cs b/PepperDashEssentials/UI/JoinConstants/UIUshortJoin.cs index ffc7f19e..73356bf3 100644 --- a/PepperDashEssentials/UI/JoinConstants/UIUshortJoin.cs +++ b/PepperDashEssentials/UI/JoinConstants/UIUshortJoin.cs @@ -7,7 +7,7 @@ { // Video Codec /// - /// 1234: values 0 = Connect, 1 = End + /// 1234: values 0 = Connect, 1 = End, 2 = Start Meeting /// public const uint VCStagingConnectButtonMode = 1234; diff --git a/PepperDashEssentials/UIDrivers/Essentials/EssentialsHeaderDriver.cs b/PepperDashEssentials/UIDrivers/Essentials/EssentialsHeaderDriver.cs index 818498d7..c63af9d5 100644 --- a/PepperDashEssentials/UIDrivers/Essentials/EssentialsHeaderDriver.cs +++ b/PepperDashEssentials/UIDrivers/Essentials/EssentialsHeaderDriver.cs @@ -12,6 +12,7 @@ using PepperDash.Core; using PepperDash.Essentials.Core; using PepperDash.Essentials.Core.SmartObjects; using PepperDash.Essentials.Core.PageManagers; +using PepperDash.Essentials.Devices.Common.VideoCodec.Interfaces; using PepperDash.Essentials.Room.Config; using PepperDash.Essentials.Devices.Common.Codec; using PepperDash.Essentials.Devices.Common.VideoCodec; @@ -164,7 +165,7 @@ namespace PepperDash.Essentials CallCaretVisible = tempJoin + 10; TriList.SetSigFalseAction(tempJoin, () => { - avDriver.ShowActiveCallsList(); + avDriver.ShowActiveCallsListOrMeetingInfo(); if(avDriver.CurrentRoom.InCallFeedback.BoolValue) CaretInterlock.ShowInterlocked(CallCaretVisible); }); @@ -191,32 +192,56 @@ namespace PepperDash.Essentials return; } + var meetingInfoCodec = codec as IHasMeetingInfo; + // Set mode of header button + SetHeaderCallIcon(codec); + + // Set the call status text + Debug.Console(1, "Active Call Count: {0}", codec.ActiveCalls.Count); + + if (codec.ActiveCalls.Count > 0) + { + if (codec.ActiveCalls.Count == 1 && meetingInfoCodec == null) + TriList.SetString(UIStringJoin.HeaderCallStatusLabel, "1 Active Call"); + else if (codec.ActiveCalls.Count == 1 && meetingInfoCodec != null) + { + var headerCallStatusLabel = meetingInfoCodec.MeetingInfo.IsSharingMeeting + ? "Sharing-Only Meeting" + : "Active Meeting"; + + headerCallStatusLabel = meetingInfoCodec.MeetingInfo.WaitingForHost + ? "Waiting For Host" + : headerCallStatusLabel; + + TriList.SetString(UIStringJoin.HeaderCallStatusLabel, headerCallStatusLabel); + } + else if (codec.ActiveCalls.Count > 1) + TriList.SetString(UIStringJoin.HeaderCallStatusLabel, string.Format("{0} Active Calls", codec.ActiveCalls.Count)); + } + else + TriList.SetString(UIStringJoin.HeaderCallStatusLabel, "No Active Calls"); + } + + private void SetHeaderCallIcon(VideoCodecBase codec) + { if (!codec.IsInCall) { HeaderCallButtonIconSig.StringValue = "DND"; //HeaderCallButton.SetIcon(HeaderListButton.OnHook); } else if (codec.ActiveCalls.Any(c => c.Type == eCodecCallType.Video)) + { HeaderCallButtonIconSig.StringValue = "Misc-06_Dark"; - //HeaderCallButton.SetIcon(HeaderListButton.Camera); - //TriList.SetUshort(UIUshortJoin.CallHeaderButtonMode, 2); + } + //HeaderCallButton.SetIcon(HeaderListButton.Camera); + //TriList.SetUshort(UIUshortJoin.CallHeaderButtonMode, 2); else + { HeaderCallButtonIconSig.StringValue = "Misc-09_Dark"; + } //HeaderCallButton.SetIcon(HeaderListButton.Phone); //TriList.SetUshort(UIUshortJoin.CallHeaderButtonMode, 1); - - // Set the call status text - Debug.Console(1, "Active Call Count: {0}", codec.ActiveCalls.Count); - if (codec.ActiveCalls.Count > 0) - { - if (codec.ActiveCalls.Count == 1) - TriList.SetString(UIStringJoin.HeaderCallStatusLabel, "1 Active Call"); - else if (codec.ActiveCalls.Count > 1) - TriList.SetString(UIStringJoin.HeaderCallStatusLabel, string.Format("{0} Active Calls", codec.ActiveCalls.Count)); - } - else - TriList.SetString(UIStringJoin.HeaderCallStatusLabel, "No Active Calls"); } /// @@ -256,7 +281,7 @@ namespace PepperDash.Essentials TriList.SetSigFalseAction(UIBoolJoin.HeaderCallStatusLabelPress, () => { - avDriver.ShowActiveCallsList(); + avDriver.ShowActiveCallsListOrMeetingInfo(); if (avDriver.CurrentRoom.InCallFeedback.BoolValue) CaretInterlock.ShowInterlocked(CallCaretVisible); }); @@ -354,6 +379,8 @@ namespace PepperDash.Essentials headerPopupShown = true; else if (e.NewJoin == UIBoolJoin.HeaderActiveCallsListVisible) headerPopupShown = true; + else if (e.NewJoin == UIBoolJoin.HeaderMeetingInfoVisible) + headerPopupShown = true; else if (e.NewJoin == UIBoolJoin.HelpPageVisible) headerPopupShown = true; else if (e.NewJoin == UIBoolJoin.MeetingsOrContacMethodsListVisible) diff --git a/PepperDashEssentials/UIDrivers/EssentialsHuddleVTC/EssentialsHuddleVtc1PanelAvFunctionsDriver.cs b/PepperDashEssentials/UIDrivers/EssentialsHuddleVTC/EssentialsHuddleVtc1PanelAvFunctionsDriver.cs index 5852526e..44091362 100644 --- a/PepperDashEssentials/UIDrivers/EssentialsHuddleVTC/EssentialsHuddleVtc1PanelAvFunctionsDriver.cs +++ b/PepperDashEssentials/UIDrivers/EssentialsHuddleVTC/EssentialsHuddleVtc1PanelAvFunctionsDriver.cs @@ -1,6 +1,7 @@ using System; using System.Linq; using System.Collections.Generic; +using System.Globalization; using Crestron.SimplSharp; using Crestron.SimplSharpPro; using Crestron.SimplSharpPro.UI; @@ -8,11 +9,13 @@ using Crestron.SimplSharpPro.UI; using PepperDash.Core; using PepperDash.Essentials.Core; using PepperDash.Essentials.Core.Config; +using PepperDash.Essentials.Core.DeviceTypeInterfaces; using PepperDash.Essentials.Core.SmartObjects; using PepperDash.Essentials.Core.PageManagers; using PepperDash.Essentials.Room.Config; using PepperDash.Essentials.Devices.Common.Codec; using PepperDash.Essentials.Devices.Common.VideoCodec; +using PepperDash.Essentials.Devices.Common.VideoCodec.Interfaces; namespace PepperDash.Essentials { @@ -98,6 +101,9 @@ namespace PepperDash.Essentials /// public SubpageReferenceList MeetingOrContactMethodModalSrl { get; set; } + public uint CallListOrMeetingInfoPopoverVisibilityJoin { get; private set; } + + /// /// The list of buttons on the header. Managed with visibility only /// @@ -176,6 +182,16 @@ namespace PepperDash.Essentials private UiDisplayMode _currentMode; + private bool _isZoomRoomWithNoExternalSources + { + get + { + return CurrentRoom.VideoCodec is Essentials.Devices.Common.VideoCodec.ZoomRoom.ZoomRoom && _sourceListCount <= 1; + } + } + + private uint _sourceListCount; + /// /// The mode showing. Presentation or call. /// @@ -350,15 +366,17 @@ namespace PepperDash.Essentials /// /// Allows PopupInterlock to be toggled if the calls list is already visible, or if the codec is in a call /// - public void ShowActiveCallsList() + public void ShowActiveCallsListOrMeetingInfo() { TriList.SetBool(UIBoolJoin.CallEndAllConfirmVisible, true); - if(PopupInterlock.CurrentJoin == UIBoolJoin.HeaderActiveCallsListVisible) - PopupInterlock.ShowInterlockedWithToggle(UIBoolJoin.HeaderActiveCallsListVisible); + + + if(PopupInterlock.CurrentJoin == CallListOrMeetingInfoPopoverVisibilityJoin) + PopupInterlock.ShowInterlockedWithToggle(CallListOrMeetingInfoPopoverVisibilityJoin); else { - if((CurrentRoom.ScheduleSource as VideoCodecBase).IsInCall) - PopupInterlock.ShowInterlockedWithToggle(UIBoolJoin.HeaderActiveCallsListVisible); + if(CurrentRoom.VideoCodec.IsInCall) + PopupInterlock.ShowInterlockedWithToggle(CallListOrMeetingInfoPopoverVisibilityJoin); } } @@ -454,79 +472,92 @@ namespace PepperDash.Essentials /// void ShowNextMeetingTimerCallback() { + //Update calendar for Zoom. Zoom doesn't automatically update when meetings are in the past + if (_isZoomRoomWithNoExternalSources) + { + CurrentRoom.ScheduleSource.GetSchedule(); + } + // Every 60 seconds, refresh the calendar RefreshMeetingsList(); // check meetings list for the closest, joinable meeting var ss = CurrentRoom.ScheduleSource; var meetings = ss.CodecSchedule.Meetings; - if (meetings.Count > 0) - { - // If the room is off pester the user - // If the room is on, and the meeting is joinable - // and the LastMeetingDismissed != this meeting + if (meetings.Count <= 0) + { + return; + } + // If the room is off pester the user + // If the room is on, and the meeting is joinable + // and the LastMeetingDismissed != this meeting - var lastMeetingDismissed = meetings.FirstOrDefault(m => m.Id == LastMeetingDismissedId); - //Debug.Console(1, "*#* Room on: {0}, lastMeetingDismissedId: {1} {2} *#*", - // CurrentRoom.OnFeedback.BoolValue, - // LastMeetingDismissedId, - // lastMeetingDismissed != null ? lastMeetingDismissed.StartTime.ToShortTimeString() : ""); + var lastMeetingDismissed = meetings.FirstOrDefault(m => m.Id == LastMeetingDismissedId); + Debug.Console(0, "*#* Room on: {0}, lastMeetingDismissedId: {1} {2} *#*", + CurrentRoom.OnFeedback.BoolValue, + LastMeetingDismissedId, + lastMeetingDismissed != null ? lastMeetingDismissed.StartTime.ToString("t", Global.Culture) : ""); - var meeting = meetings.LastOrDefault(m => m.Joinable); - if (CurrentRoom.OnFeedback.BoolValue - && lastMeetingDismissed == meeting) - { - return; - } + var meeting = meetings.LastOrDefault(m => m.Joinable); + if (CurrentRoom.OnFeedback.BoolValue + && lastMeetingDismissed == meeting) + { + // meeting no longer joinable, hide popup + if(meeting == null) + HideNextMeetingPopup(); - LastMeetingDismissedId = null; - // Clear the popup when we run out of meetings - if (meeting == null) - { - HideNextMeetingPopup(); - } - else - { - TriList.SetString(UIStringJoin.MeetingsOrContactMethodListTitleText, "Upcoming meeting"); - TriList.SetString(UIStringJoin.NextMeetingStartTimeText, meeting.StartTime.ToShortTimeString()); - TriList.SetString(UIStringJoin.NextMeetingEndTimeText, meeting.EndTime.ToShortTimeString()); - TriList.SetString(UIStringJoin.NextMeetingTitleText, meeting.Title); - TriList.SetString(UIStringJoin.NextMeetingNameText, meeting.Organizer); - TriList.SetString(UIStringJoin.NextMeetingButtonLabel, "Join"); - TriList.SetSigFalseAction(UIBoolJoin.NextMeetingJoinPress, () => - { - HideNextMeetingPopup(); - PopupInterlock.Hide(); - RoomOnAndDialMeeting(meeting); - }); - TriList.SetString(UIStringJoin.NextMeetingSecondaryButtonLabel, "Show Schedule"); - TriList.SetSigFalseAction(UIBoolJoin.CalendarHeaderButtonPress, () => - { - HideNextMeetingPopup(); - //CalendarPress(); - RefreshMeetingsList(); - PopupInterlock.ShowInterlocked(UIBoolJoin.MeetingsOrContacMethodsListVisible); - }); - var indexOfNext = meetings.IndexOf(meeting) + 1; + return; + } - // indexOf = 3, 4 meetings : - if (indexOfNext < meetings.Count) - TriList.SetString(UIStringJoin.NextMeetingFollowingMeetingText, - meetings[indexOfNext].StartTime.ToShortTimeString()); - else - TriList.SetString(UIStringJoin.NextMeetingFollowingMeetingText, "No more meetings today"); + LastMeetingDismissedId = null; + // Clear the popup when we run out of meetings + if (meeting == null) + { + HideNextMeetingPopup(); + } + else + { - TriList.SetSigFalseAction(UIBoolJoin.NextMeetingModalClosePress, () => - { - // Mark the meeting to not re-harass the user - if(CurrentRoom.OnFeedback.BoolValue) - LastMeetingDismissedId = meeting.Id; - HideNextMeetingPopup(); - }); - TriList.SetBool(UIBoolJoin.NextMeetingModalVisible, true); - } - } + TriList.SetString(UIStringJoin.MeetingsOrContactMethodListTitleText, "Upcoming meeting"); + TriList.SetString(UIStringJoin.NextMeetingStartTimeText, meeting.StartTime.ToString("t", Global.Culture)); + TriList.SetString(UIStringJoin.NextMeetingEndTimeText, meeting.EndTime.ToString("t", Global.Culture)); + TriList.SetString(UIStringJoin.NextMeetingTitleText, meeting.Title); + TriList.SetString(UIStringJoin.NextMeetingNameText, meeting.Organizer); + TriList.SetString(UIStringJoin.NextMeetingButtonLabel, "Join"); + TriList.SetSigFalseAction(UIBoolJoin.NextMeetingJoinPress, () => + { + HideNextMeetingPopup(); + PopupInterlock.Hide(); + RoomOnAndDialMeeting(meeting); + }); + TriList.SetString(UIStringJoin.NextMeetingSecondaryButtonLabel, "Show Schedule"); + TriList.SetSigFalseAction(UIBoolJoin.CalendarHeaderButtonPress, () => + { + HideNextMeetingPopup(); + //CalendarPress(); + RefreshMeetingsList(); + PopupInterlock.ShowInterlocked(UIBoolJoin.MeetingsOrContacMethodsListVisible); + }); + var indexOfNext = meetings.IndexOf(meeting) + 1; + + // indexOf = 3, 4 meetings : + if (indexOfNext < meetings.Count) + TriList.SetString(UIStringJoin.NextMeetingFollowingMeetingText, + meetings[indexOfNext].StartTime.ToString("t", Global.Culture)); + else + TriList.SetString(UIStringJoin.NextMeetingFollowingMeetingText, "No more meetings today"); + + TriList.SetSigFalseAction(UIBoolJoin.NextMeetingModalClosePress, () => + { + // Mark the meeting to not re-harass the user + if(CurrentRoom.OnFeedback.BoolValue) + LastMeetingDismissedId = meeting.Id; + HideNextMeetingPopup(); + }); + + TriList.SetBool(UIBoolJoin.NextMeetingModalVisible, true); + } } /// @@ -552,19 +583,26 @@ namespace PepperDash.Essentials /// void RoomOnAndDialMeeting(Meeting meeting) { + Debug.Console(1, "[RoomOnAndDialMeeting] Joining meeting [{0}]", meeting); Action dialAction = () => { var d = CurrentRoom.ScheduleSource as VideoCodecBase; if (d != null) { + Debug.Console(1, + "[RoomOnAndDialMeeting] [dialAction] Sending command to codec to join meeting {0}", meeting); d.Dial(meeting); LastMeetingDismissedId = meeting.Id; // To prevent prompts for already-joined call } }; - if (CurrentRoom.OnFeedback.BoolValue) - dialAction(); + if (CurrentRoom.OnFeedback.BoolValue) + { + Debug.Console(1, "[RoomOnAndDialMeeting] Room is on."); + dialAction(); + } else - { + { + Debug.Console(1, "RoomOnAndDialMeeting] Room is off or warming. Registering for Warming Feedback"); // Rig a one-time handler to catch when the room is warmed and then dial call EventHandler oneTimeHandler = null; oneTimeHandler = (o, a) => @@ -634,9 +672,24 @@ namespace PepperDash.Essentials TriList.SetBool(StartPageVisibleJoin, startMode ? true : false); - TriList.SetBool(UIBoolJoin.SourceStagingBarVisible, presentationMode ? true : false); + if (presentationMode &&_isZoomRoomWithNoExternalSources) + { + // For now, if this is a Zoom Room and there are no shareable sources just display the informational subpage + TriList.SetBool(UIBoolJoin.SourceStagingBarVisible, false); + TriList.SetBool(UIBoolJoin.ZoomRoomContentSharingVisible, true); + } + else + { + // Otherwise, show the staging bar + TriList.SetBool(UIBoolJoin.ZoomRoomContentSharingVisible, false); + TriList.SetBool(UIBoolJoin.SourceStagingBarVisible, presentationMode ? true : false); + + } if (!presentationMode) + { + TriList.SetBool(UIBoolJoin.ZoomRoomContentSharingVisible, false); TriList.SetBool(UIBoolJoin.SelectASourceVisible, false); + } CallButtonSig.BoolValue = callMode && CurrentRoom.ShutdownType == eShutdownType.None; @@ -674,25 +727,63 @@ namespace PepperDash.Essentials if (VCDriver.IsVisible) VCDriver.Hide(); HideNextMeetingPopup(); - // Run default source when room is off and share is pressed - if (!CurrentRoom.OnFeedback.BoolValue) - { - // If there's no default, show UI elements - if (!(CurrentRoom as IRunDefaultPresentRoute).RunDefaultPresentRoute()) - TriList.SetBool(UIBoolJoin.SelectASourceVisible, true); - } - else // room is on show what's active or select a source if nothing is yet active + + + if (_isZoomRoomWithNoExternalSources) { - if(CurrentRoom.CurrentSourceInfo == null || (CurrentRoom.VideoCodec != null && CurrentRoom.CurrentSourceInfo.SourceDevice.Key == CurrentRoom.VideoCodec.OsdSource.Key)) - TriList.SetBool(UIBoolJoin.SelectASourceVisible, true); - else if (CurrentSourcePageManager != null) + if (!CurrentRoom.OnFeedback.BoolValue) { - TriList.SetBool(UIBoolJoin.SelectASourceVisible, false); - CurrentSourcePageManager.Show(); + CurrentRoom.RunDefaultPresentRoute(); } + // For now, if this is a Zoom Room and there are no shareable sources just display the informational subpage + TriList.SetBool(UIBoolJoin.ZoomRoomContentSharingVisible, true); + + var presentationMeetingCodec = CurrentRoom.VideoCodec as IHasPresentationOnlyMeeting; + var farEndContentStatusCodec = CurrentRoom.VideoCodec as IHasFarEndContentStatus; + var receivingContent = false; + + if (farEndContentStatusCodec != null) + { + receivingContent = farEndContentStatusCodec.ReceivingContent.BoolValue; + } + + if (presentationMeetingCodec != null && !CurrentRoom.VideoCodec.IsInCall) + { + presentationMeetingCodec.StartSharingOnlyMeeting(eSharingMeetingMode.Laptop); + } + else if (CurrentRoom.VideoCodec.IsInCall && !CurrentRoom.VideoCodec.SharingContentIsOnFeedback.BoolValue && + !receivingContent) + { + CurrentRoom.VideoCodec.StartSharing(); + } + + if (CurrentSourcePageManager != null) + CurrentSourcePageManager.Hide(); + } + else + { + // Run default source when room is off and share is pressed + if (!CurrentRoom.OnFeedback.BoolValue) + { + // If there's no default, show UI elements + if (!(CurrentRoom as IRunDefaultPresentRoute).RunDefaultPresentRoute()) + TriList.SetBool(UIBoolJoin.SelectASourceVisible, true); + } + else // room is on show what's active or select a source if nothing is yet active + { + if (CurrentRoom.CurrentSourceInfo == null || + (CurrentRoom.VideoCodec != null && + CurrentRoom.CurrentSourceInfo.SourceDevice.Key == CurrentRoom.VideoCodec.OsdSource.Key)) + TriList.SetBool(UIBoolJoin.SelectASourceVisible, true); + else if (CurrentSourcePageManager != null) + { + TriList.SetBool(UIBoolJoin.SelectASourceVisible, false); + CurrentSourcePageManager.Show(); + } + } + SetupSourceList(); } CurrentMode = UiDisplayMode.Presentation; - SetupSourceList(); } /// @@ -731,7 +822,7 @@ namespace PepperDash.Essentials /// void ShowCurrentSource() { - if (CurrentRoom.CurrentSourceInfo == null) + if (CurrentRoom.CurrentSourceInfo == null || _isZoomRoomWithNoExternalSources) return; CurrentMode = UiDisplayMode.Presentation; @@ -941,6 +1032,18 @@ namespace PepperDash.Essentials _CurrentRoom.IsWarmingUpFeedback.OutputChange -= CurrentRoom_IsWarmingFeedback_OutputChange; _CurrentRoom.IsCoolingDownFeedback.OutputChange -= CurrentRoom_IsCoolingDownFeedback_OutputChange; _CurrentRoom.InCallFeedback.OutputChange -= CurrentRoom_InCallFeedback_OutputChange; + + var scheduleAwareCodec = _CurrentRoom.VideoCodec as IHasScheduleAwareness; + if (scheduleAwareCodec != null) + { + scheduleAwareCodec.CodecSchedule.MeetingsListHasChanged -= CodecSchedule_MeetingsListHasChanged; + } + + var meetingInfoCodec = _CurrentRoom.VideoCodec as IHasMeetingInfo; + if (meetingInfoCodec != null) + { + meetingInfoCodec.MeetingInfoChanged -= meetingInfoCodec_MeetingInfoChanged; + } } _CurrentRoom = room; @@ -973,9 +1076,23 @@ namespace PepperDash.Essentials _CurrentRoom.CurrentSourceChange += CurrentRoom_SourceInfoChange; RefreshSourceInfo(); - if (_CurrentRoom.VideoCodec is IHasScheduleAwareness) + + var scheduleAwareCodec = _CurrentRoom.VideoCodec as IHasScheduleAwareness; + if (scheduleAwareCodec != null) { - (_CurrentRoom.VideoCodec as IHasScheduleAwareness).CodecSchedule.MeetingsListHasChanged += CodecSchedule_MeetingsListHasChanged; + scheduleAwareCodec.CodecSchedule.MeetingsListHasChanged += CodecSchedule_MeetingsListHasChanged; + } + + var meetingInfoCodec = _CurrentRoom.VideoCodec as IHasMeetingInfo; + if (meetingInfoCodec != null) + { + meetingInfoCodec.MeetingInfoChanged += new EventHandler(meetingInfoCodec_MeetingInfoChanged); + + CallListOrMeetingInfoPopoverVisibilityJoin = UIBoolJoin.HeaderMeetingInfoVisible; + } + else + { + CallListOrMeetingInfoPopoverVisibilityJoin = UIBoolJoin.HeaderActiveCallsListVisible; } CallSharingInfoVisibleFeedback = new BoolFeedback(() => _CurrentRoom.VideoCodec.SharingContentIsOnFeedback.BoolValue); @@ -987,7 +1104,8 @@ namespace PepperDash.Essentials if (_CurrentRoom != null) _CurrentRoom.CurrentSourceChange += new SourceInfoChangeHandler(CurrentRoom_CurrentSingleSourceChange); - TriList.SetSigFalseAction(UIBoolJoin.CallStopSharingPress, () => _CurrentRoom.RunRouteAction("codecOsd", _CurrentRoom.SourceListKey)); + // Moved to EssentialsVideoCodecUiDriver + //TriList.SetSigFalseAction(UIBoolJoin.CallStopSharingPress, () => _CurrentRoom.RunRouteAction("codecOsd", _CurrentRoom.SourceListKey)); (Parent as EssentialsPanelMainInterfaceDriver).HeaderDriver.SetupHeaderButtons(this, CurrentRoom); } @@ -998,6 +1116,26 @@ namespace PepperDash.Essentials } } + void meetingInfoCodec_MeetingInfoChanged(object sender, MeetingInfoEventArgs e) + { + if (e.Info == null) + { + return; + } + + TriList.SetString(UIStringJoin.MeetingIdText, e.Info.Id); + TriList.SetString(UIStringJoin.MeetingHostText, e.Info.Host); + TriList.SetString(UIStringJoin.MeetingNameText, e.Info.Name); + + TriList.SetString(UIStringJoin.MeetingPasswordText, e.Info.Password); + // Show the password fields if one is present + TriList.SetBool(UIBoolJoin.MeetingPasswordVisible, !string.IsNullOrEmpty(e.Info.Password)); + + TriList.SetString(UIStringJoin.CallSharedSourceNameText, e.Info.ShareStatus); + + TriList.SetString(UIStringJoin.MeetingLeaveText, e.Info.IsHost ? "End Meeting" : "Leave Meeting"); + } + void SetCurrentRoom(IEssentialsHuddleVtc1Room room) { if (_CurrentRoom == room) return; @@ -1111,7 +1249,8 @@ namespace PepperDash.Essentials Debug.Console(1, "**** KEY {0}", kvp.Key); } - SourceStagingSrl.Count = (ushort)(i - 1); + _sourceListCount = (i - 1); + SourceStagingSrl.Count = (ushort)_sourceListCount; } } @@ -1200,17 +1339,16 @@ namespace PepperDash.Essentials { // See if this is helpful or if the callback response in the codec class maybe doesn't come it time? // Let's build list from event - // CurrentRoom.ScheduleSource.GetSchedule(); - TriList.SetString(UIStringJoin.MeetingsOrContactMethodListIcon, "Calendar"); + TriList.SetString(UIStringJoin.MeetingsOrContactMethodListIcon, "Calendar"); TriList.SetString(UIStringJoin.MeetingsOrContactMethodListTitleText, "Today's Meetings"); ushort i = 0; foreach (var m in CurrentRoom.ScheduleSource.CodecSchedule.Meetings) { i++; - MeetingOrContactMethodModalSrl.StringInputSig(i, 1).StringValue = m.StartTime.ToShortTimeString(); - MeetingOrContactMethodModalSrl.StringInputSig(i, 2).StringValue = m.EndTime.ToShortTimeString(); + MeetingOrContactMethodModalSrl.StringInputSig(i, 1).StringValue = m.StartTime.ToString("t", Global.Culture); + MeetingOrContactMethodModalSrl.StringInputSig(i, 2).StringValue = m.EndTime.ToString("t", Global.Culture); MeetingOrContactMethodModalSrl.StringInputSig(i, 3).StringValue = m.Title; MeetingOrContactMethodModalSrl.StringInputSig(i, 4).StringValue = string.Format("
{0}",m.Organizer); MeetingOrContactMethodModalSrl.StringInputSig(i, 5).StringValue = "Join"; @@ -1507,6 +1645,8 @@ namespace PepperDash.Essentials ///
void PrepareForCodecIncomingCall(); + uint CallListOrMeetingInfoPopoverVisibilityJoin { get; } + SubpageReferenceList MeetingOrContactMethodModalSrl { get; } } } diff --git a/PepperDashEssentials/UIDrivers/VC/EssentialsVideoCodecUiDriver.cs b/PepperDashEssentials/UIDrivers/VC/EssentialsVideoCodecUiDriver.cs index e6aa1b7d..40671238 100644 --- a/PepperDashEssentials/UIDrivers/VC/EssentialsVideoCodecUiDriver.cs +++ b/PepperDashEssentials/UIDrivers/VC/EssentialsVideoCodecUiDriver.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using System.Globalization; using System.Text.RegularExpressions; using Crestron.SimplSharp; using Crestron.SimplSharpPro; @@ -14,6 +15,7 @@ using PepperDash.Essentials.Core.SmartObjects; using PepperDash.Essentials.Core.Touchpanels.Keyboards; using PepperDash.Essentials.Devices.Common.Codec; using PepperDash.Essentials.Devices.Common.VideoCodec; +using PepperDash.Essentials.Devices.Common.VideoCodec.Interfaces; using PepperDash.Essentials.Devices.Common.Cameras; namespace PepperDash.Essentials.UIDrivers.VC @@ -83,6 +85,9 @@ namespace PepperDash.Essentials.UIDrivers.VC StringBuilder SearchStringBuilder = new StringBuilder(); BoolFeedback SearchStringBackspaceVisibleFeedback; + StringFeedback PasswordStringFeedback; + StringBuilder PasswordStringBuilder = new StringBuilder(); + ModalDialog IncomingCallModal; eKeypadMode KeypadMode; @@ -129,11 +134,12 @@ namespace PepperDash.Essentials.UIDrivers.VC VCControlsInterlock = new JoinedSigInterlock(triList); VCCameraControlModeInterlock = new JoinedSigInterlock(triList); + VCControlsInterlock.HideAndClear(); - if (CodecHasFavorites) + /* if (CodecHasFavorites || codec is IHasZoomRoomLayouts) //Checking for Zoom Room...picked a ZoomRoom specific interface to check for VCControlsInterlock.SetButDontShow(UIBoolJoin.VCKeypadWithFavoritesVisible); else - VCControlsInterlock.SetButDontShow(UIBoolJoin.VCKeypadVisible); + VCControlsInterlock.SetButDontShow(UIBoolJoin.VCKeypadVisible); */ StagingBarsInterlock = new JoinedSigInterlock(triList); if(Codec is IHasCallHistory) @@ -147,7 +153,8 @@ namespace PepperDash.Essentials.UIDrivers.VC // Return formatted when dialing, straight digits when in call DialStringFeedback = new StringFeedback(() => { - if (KeypadMode == eKeypadMode.Dial) + // Format the number feedback if in dial mode and the codec is not IHasStartMeeting (ZoomRoom) + if (KeypadMode == eKeypadMode.Dial && !(Codec is IHasStartMeeting)) return GetFormattedDialString(DialStringBuilder.ToString()); else return DialStringBuilder.ToString(); @@ -174,8 +181,22 @@ namespace PepperDash.Essentials.UIDrivers.VC }); SearchStringFeedback.LinkInputSig(triList.StringInput[UIStringJoin.CodecDirectorySearchEntryText]); - SetupDirectoryList(); + PasswordStringFeedback = new StringFeedback(() => + { + if (PasswordStringBuilder.Length > 0) + { + Parent.Keyboard.EnableGoButton(); + return PasswordStringBuilder.ToString(); + } + else + { + Parent.Keyboard.DisableGoButton(); + return ""; + } + }); + PasswordStringFeedback.LinkInputSig(triList.StringInput[UIStringJoin.PasswordPromptPasswordText]); + SetupDirectoryList(); SearchStringBackspaceVisibleFeedback = new BoolFeedback(() => SearchStringBuilder.Length > 0); SearchStringBackspaceVisibleFeedback.LinkInputSig(triList.BooleanInput[UIBoolJoin.VCDirectoryBackspaceVisible]); @@ -193,14 +214,18 @@ namespace PepperDash.Essentials.UIDrivers.VC triList.SetSigHeldAction(UIBoolJoin.VCDirectoryBackspacePress, 500, StartSearchBackspaceRepeat, StopSearchBackspaceRepeat, SearchKeypadBackspacePress); + + if (Codec is IPasswordPrompt) + { + SetupPasswordPrompt(); + } + + // If the codec is ready, then get the values we want, otherwise wait if (Codec.IsReady) Codec_IsReady(); else codec.IsReadyChange += (o, a) => Codec_IsReady(); - - - } catch (Exception e) { @@ -296,24 +321,20 @@ namespace PepperDash.Essentials.UIDrivers.VC void Codec_CallStatusChange(object sender, CodecCallStatusItemChangeEventArgs e) { var call = e.CallItem; + var meetingInfoSender = sender as IHasMeetingInfo; switch (e.CallItem.Status) { case eCodecCallStatus.Connected: // fire at SRL item - KeypadMode = eKeypadMode.DTMF; - DialStringBuilder.Remove(0, DialStringBuilder.Length); - DialStringFeedback.FireUpdate(); - DialStringTextCheckEnables(); Parent.ShowNotificationRibbon("Connected", 2000); - StagingButtonsFeedbackInterlock.ShowInterlocked(UIBoolJoin.VCStagingKeypadPress); - ShowKeypad(); - ((Parent.CurrentRoom as IHasCurrentVolumeControls).CurrentVolumeControls as IBasicVolumeWithFeedback).MuteOff(); + OnCallConnected(); //VCControlsInterlock.ShowInterlocked(UIBoolJoin.VCKeypadVisible); break; case eCodecCallStatus.Connecting: // fire at SRL item Parent.ShowNotificationRibbon("Connecting", 0); + OnCallConnected(); break; case eCodecCallStatus.Dialing: Parent.ShowNotificationRibbon("Connecting", 0); @@ -329,7 +350,10 @@ namespace PepperDash.Essentials.UIDrivers.VC DialStringBuilder.Remove(0, DialStringBuilder.Length); DialStringFeedback.FireUpdate(); Parent.ShowNotificationRibbon("Disconnected", 2000); + Debug.Console(0, "Setting Connect Button mode to 0"); } + + break; case eCodecCallStatus.Disconnecting: break; @@ -350,12 +374,23 @@ namespace PepperDash.Essentials.UIDrivers.VC ShowIncomingModal(call); break; } - default: - break; } - TriList.UShortInput[UIUshortJoin.VCStagingConnectButtonMode].UShortValue = (ushort)(Codec.IsInCall ? 1 : 0); - - uint stageJoin; + + if (meetingInfoSender != null && Codec.IsInCall) + { + var meetingInfo = meetingInfoSender.MeetingInfo; + + TriList.UShortInput[UIUshortJoin.VCStagingConnectButtonMode].UShortValue = + (ushort) (meetingInfo.IsSharingMeeting ? 2 : 1); + } + else + { + + TriList.UShortInput[UIUshortJoin.VCStagingConnectButtonMode].UShortValue = + (ushort) (Codec.IsInCall ? 1 : 0); + } + + uint stageJoin; if (Codec.IsInCall) stageJoin = UIBoolJoin.VCStagingActivePopoverVisible; else @@ -376,6 +411,36 @@ namespace PepperDash.Essentials.UIDrivers.VC UpdateHeaderActiveCallList(); } + private void OnCallConnected() + { + HidePasswordPrompt(); + KeypadMode = eKeypadMode.DTMF; + DialStringBuilder.Remove(0, DialStringBuilder.Length); + DialStringFeedback.FireUpdate(); + DialStringTextCheckEnables(); + + StagingButtonsFeedbackInterlock.ShowInterlocked(UIBoolJoin.VCStagingKeypadPress); + ShowKeypad(); + + UnmuteRoomOnCallConnect(); + } + + private void UnmuteRoomOnCallConnect() + { + var volControl = Parent.CurrentRoom as IHasCurrentVolumeControls; + + if (volControl == null) + { + return; + } + var currentVolControls = volControl.CurrentVolumeControls as IBasicVolumeWithFeedback; + + if (currentVolControls != null) + { + currentVolControls.MuteOff(); + } + } + /// /// Redraws the calls list on the header /// @@ -399,8 +464,8 @@ namespace PepperDash.Essentials.UIDrivers.VC ActiveCallsSRL.Count = (ushort)activeList.Count; // If Active Calls list is visible and codec is not in a call, hide the list - if (!Codec.IsInCall && Parent.PopupInterlock.CurrentJoin == UIBoolJoin.HeaderActiveCallsListVisible) - Parent.PopupInterlock.ShowInterlockedWithToggle(UIBoolJoin.HeaderActiveCallsListVisible); + if (!Codec.IsInCall && Parent.PopupInterlock.CurrentJoin == Parent.CallListOrMeetingInfoPopoverVisibilityJoin) + Parent.PopupInterlock.ShowInterlockedWithToggle(Parent.CallListOrMeetingInfoPopoverVisibilityJoin); } /// @@ -491,20 +556,67 @@ namespace PepperDash.Essentials.UIDrivers.VC TriList.SetSigFalseAction(UIBoolJoin.VCStagingRecentsPress, ShowRecents); TriList.SetSigFalseAction(UIBoolJoin.VCStagingCameraPress, ShowCameraControls); TriList.SetSigFalseAction(UIBoolJoin.VCStagingConnectPress, ConnectPress); + TriList.SetSigFalseAction(UIBoolJoin.VCStagingMeetNowPress, MeetNowPress); + TriList.SetSigFalseAction(UIBoolJoin.CallStopSharingPress, CallStopSharingPress); + + var meetingInfoCodec = Codec as IHasMeetingInfo; + TriList.SetSigFalseAction(UIBoolJoin.CallEndPress, () => { if (Codec.ActiveCalls.Count > 1) { - Parent.PopupInterlock.ShowInterlocked(UIBoolJoin.HeaderActiveCallsListVisible); + Parent.PopupInterlock.ShowInterlocked(Parent.CallListOrMeetingInfoPopoverVisibilityJoin); + } + else if (meetingInfoCodec != null && Codec.ActiveCalls.Count == 1) + { + var meetingInfo = meetingInfoCodec.MeetingInfo; + + if (meetingInfo != null && meetingInfo.IsSharingMeeting) + { + var presentationMeetingCodec = Codec as IHasPresentationOnlyMeeting; + if (presentationMeetingCodec != null) + { + presentationMeetingCodec.StartNormalMeetingFromSharingOnlyMeeting(); + } + } + else + { + Codec.EndAllCalls(); + } } else + { Codec.EndAllCalls(); + } }); + TriList.SetSigFalseAction(UIBoolJoin.CallEndAllConfirmPress, () => { Parent.PopupInterlock.HideAndClear(); Codec.EndAllCalls(); }); + + + if (meetingInfoCodec != null) + { + TriList.SetSigFalseAction(UIBoolJoin.MeetingLeavePress, () => + { + Parent.PopupInterlock.HideAndClear(); + + if (meetingInfoCodec.MeetingInfo.IsHost) + { + Codec.EndAllCalls(); + } + else + { + var startMeetingCodec = Codec as IHasStartMeeting; + if (startMeetingCodec != null) + { + startMeetingCodec.LeaveMeeting(); + } + } + }); + } } void SetupCameraControls() @@ -989,7 +1101,7 @@ namespace PepperDash.Essentials.UIDrivers.VC // if it's today, show a simpler string string timeText = null; if (c.StartTime.Date == DateTime.Now.Date) - timeText = c.StartTime.ToShortTimeString(); + timeText = c.StartTime.ToString("t", Global.Culture); else if (c.StartTime == DateTime.MinValue) timeText = ""; else @@ -1333,7 +1445,21 @@ namespace PepperDash.Essentials.UIDrivers.VC /// void RevealKeyboard() { - if (VCControlsInterlock.CurrentJoin == UIBoolJoin.VCKeypadWithFavoritesVisible && KeypadMode == eKeypadMode.Dial) + if (_passwordPromptDialogVisible) + { + Debug.Console(2, "Attaching Keyboard to PasswordPromptDialog"); + DetachDialKeyboard(); + DetachSearchKeyboard(); + var kb = Parent.Keyboard; + kb.KeyPress -= Keyboard_PasswordKeyPress; + kb.KeyPress += Keyboard_PasswordKeyPress; + kb.HideAction = this.DetachPasswordKeyboard; + kb.GoButtonText = "Submit"; + kb.GoButtonVisible = true; + PasswordStringCheckEnables(); + kb.Show(); + } + else if (VCControlsInterlock.CurrentJoin == UIBoolJoin.VCKeypadWithFavoritesVisible && KeypadMode == eKeypadMode.Dial) { var kb = Parent.Keyboard; kb.KeyPress -= Keyboard_DialKeyPress; @@ -1355,6 +1481,7 @@ namespace PepperDash.Essentials.UIDrivers.VC SearchStringKeypadCheckEnables(); kb.Show(); } + } /// @@ -1410,6 +1537,32 @@ namespace PepperDash.Essentials.UIDrivers.VC } } + /// + /// Event handler for keyboard dialing + /// + void Keyboard_PasswordKeyPress(object sender, PepperDash.Essentials.Core.Touchpanels.Keyboards.KeyboardControllerPressEventArgs e) + { + if (_passwordPromptDialogVisible) + { + if (e.Text != null) + PasswordStringBuilder.Append(e.Text); + else + { + if (e.SpecialKey == KeyboardSpecialKey.Backspace) + PasswordKeypadBackspacePress(); + else if (e.SpecialKey == KeyboardSpecialKey.Clear) + PasswordKeypadClear(); + else if (e.SpecialKey == KeyboardSpecialKey.GoButton) + { + (Codec as IPasswordPrompt).SubmitPassword(PasswordStringBuilder.ToString()); + HidePasswordPrompt(); + } + } + PasswordStringFeedback.FireUpdate(); + PasswordStringCheckEnables(); + } + } + /// /// Call /// @@ -1423,6 +1576,11 @@ namespace PepperDash.Essentials.UIDrivers.VC Parent.Keyboard.KeyPress -= Keyboard_SearchKeyPress; } + void DetachPasswordKeyboard() + { + Parent.Keyboard.KeyPress -= Keyboard_PasswordKeyPress; + } + /// /// Shows the camera controls subpage /// @@ -1500,6 +1658,22 @@ namespace PepperDash.Essentials.UIDrivers.VC StagingButtonsFeedbackInterlock.ShowInterlocked(UIBoolJoin.VCStagingRecentsPress); } + /// + /// Meet Now button + /// + void MeetNowPress() + { + var startMeetingCodec = Codec as IHasStartMeeting; + if (startMeetingCodec != null) + { + startMeetingCodec.StartMeeting(startMeetingCodec.DefaultMeetingDurationMin); + } + else + { + Debug.Console(2, "Codce does not implment IHasStartMeeting. Cannot meet now"); + } + } + /// /// Connect call button /// @@ -1510,6 +1684,16 @@ namespace PepperDash.Essentials.UIDrivers.VC Codec.Dial(DialStringBuilder.ToString()); } + /// + /// Stop Sharing button + /// + void CallStopSharingPress() + { + Codec.StopSharing(); + Parent.CurrentRoom.RunRouteAction("codecOsd", Parent.CurrentRoom.SourceListKey); + } + + /// /// /// @@ -1676,6 +1860,40 @@ namespace PepperDash.Essentials.UIDrivers.VC Parent.Keyboard.DisableGoButton(); } + /// + /// Clears the Password keypad + /// + void PasswordKeypadClear() + { + PasswordStringBuilder.Remove(0, PasswordStringBuilder.Length); + PasswordStringFeedback.FireUpdate(); + PasswordStringCheckEnables(); + + } + + /// + /// + /// + void PasswordKeypadBackspacePress() + { + PasswordStringBuilder.Remove(PasswordStringBuilder.Length - 1, 1); + + PasswordStringFeedback.FireUpdate(); + PasswordStringCheckEnables(); + } + + /// + /// Checks the enabled states of various elements around the keypad + /// + void PasswordStringCheckEnables() + { + var textIsEntered = PasswordStringBuilder.Length > 0; + if (textIsEntered) + Parent.Keyboard.EnableGoButton(); + else + Parent.Keyboard.DisableGoButton(); + } + /// /// Returns the text value for the keypad dial entry field @@ -1721,5 +1939,62 @@ namespace PepperDash.Essentials.UIDrivers.VC Dial = 0, DTMF } + + void SetupPasswordPrompt() + { + var passwordPromptCodec = Codec as IPasswordPrompt; + + passwordPromptCodec.PasswordRequired += new EventHandler(passwordPromptCodec_PasswordRequired); + + TriList.SetSigFalseAction(UIBoolJoin.PasswordPromptCancelPress, HidePasswordPrompt); + TriList.SetSigFalseAction(UIBoolJoin.PasswordPromptTextPress, RevealKeyboard); + } + + void passwordPromptCodec_PasswordRequired(object sender, PasswordPromptEventArgs e) + { + if (e.LoginAttemptCancelled) + { + HidePasswordPrompt(); + return; + } + + if (!string.IsNullOrEmpty(e.Message)) + { + TriList.SetString(UIStringJoin.PasswordPromptMessageText, e.Message); + } + + if (e.LoginAttemptFailed) + { + // TODO: Show a message modal to indicate the login attempt failed + return; + } + + TriList.SetBool(UIBoolJoin.PasswordPromptErrorVisible, e.LastAttemptWasIncorrect); + + ShowPasswordPrompt(); + } + + private bool _passwordPromptDialogVisible; + + void ShowPasswordPrompt() + { + // Clear out any previous data + PasswordKeypadClear(); + + _passwordPromptDialogVisible = true; + TriList.SetBool(UIBoolJoin.PasswordPromptDialogVisible, _passwordPromptDialogVisible); + RevealKeyboard(); + } + + void HidePasswordPrompt() + { + if (_passwordPromptDialogVisible) + { + _passwordPromptDialogVisible = false; + Parent.Keyboard.Hide(); + TriList.SetBool(UIBoolJoin.PasswordPromptDialogVisible, _passwordPromptDialogVisible); + PasswordKeypadClear(); + } + } } } \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/DeviceTypeInterfaces/IPasswordPrompt.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/DeviceTypeInterfaces/IPasswordPrompt.cs new file mode 100644 index 00000000..6ecdd775 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/DeviceTypeInterfaces/IPasswordPrompt.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +namespace PepperDash.Essentials.Core +{ + /// + /// Describes the functionality required to prompt a user to enter a password + /// + public interface IPasswordPrompt + { + /// + /// Notifies when a password is required or is entered incorrectly + /// + event EventHandler PasswordRequired; + + /// + /// Submits the password + /// + /// + void SubmitPassword(string password); + } + + public class PasswordPromptEventArgs : EventArgs + { + /// + /// Indicates if the last submitted password was incorrect + /// + public bool LastAttemptWasIncorrect { get; private set; } + + /// + /// Indicates that the login attempt has failed + /// + public bool LoginAttemptFailed { get; private set; } + + /// + /// Indicates that the process was cancelled and the prompt should be dismissed + /// + public bool LoginAttemptCancelled { get; private set; } + + /// + /// A message to be displayed to the user + /// + public string Message { get; private set; } + + public PasswordPromptEventArgs(bool lastAttemptIncorrect, bool loginFailed, bool loginCancelled, string message) + { + LastAttemptWasIncorrect = lastAttemptIncorrect; + LoginAttemptFailed = loginFailed; + LoginAttemptCancelled = loginCancelled; + Message = message; + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/EssentialsDevice.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/EssentialsDevice.cs index 46fa819b..a72bd282 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/EssentialsDevice.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/EssentialsDevice.cs @@ -57,7 +57,7 @@ namespace PepperDash.Essentials.Core public DescriptionAttribute(string description) { - Debug.Console(2, "Setting Description: {0}", description); + //Debug.Console(2, "Setting Description: {0}", description); _Description = description; } @@ -74,7 +74,7 @@ namespace PepperDash.Essentials.Core public ConfigSnippetAttribute(string configSnippet) { - Debug.Console(2, "Setting Config Snippet {0}", configSnippet); + //Debug.Console(2, "Setting Config Snippet {0}", configSnippet); _ConfigSnippet = configSnippet; } @@ -103,7 +103,7 @@ namespace PepperDash.Essentials.Core { foreach (var typeName in TypeNames) { - Debug.Console(2, "Getting Description Attribute from class: '{0}'", typeof(T).FullName); + //Debug.Console(2, "Getting Description Attribute from class: '{0}'", typeof(T).FullName); var descriptionAttribute = typeof(T).GetCustomAttributes(typeof(DescriptionAttribute), true) as DescriptionAttribute[]; string description = descriptionAttribute[0].Description; var snippetAttribute = typeof(T).GetCustomAttributes(typeof(ConfigSnippetAttribute), true) as ConfigSnippetAttribute[]; diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Factory/DeviceFactory.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Factory/DeviceFactory.cs index a17e0d91..ebdc87b1 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Factory/DeviceFactory.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Factory/DeviceFactory.cs @@ -69,13 +69,13 @@ namespace PepperDash.Essentials.Core /// public static void AddFactoryForType(string typeName, Func method) { - Debug.Console(1, Debug.ErrorLogLevel.Notice, "Adding factory method for type '{0}'", typeName); + //Debug.Console(1, Debug.ErrorLogLevel.Notice, "Adding factory method for type '{0}'", typeName); DeviceFactory.FactoryMethods.Add(typeName, new DeviceFactoryWrapper() { FactoryMethod = method}); } public static void AddFactoryForType(string typeName, string description, CType cType, Func method) { - Debug.Console(1, Debug.ErrorLogLevel.Notice, "Adding factory method for type '{0}'", typeName); + //Debug.Console(1, Debug.ErrorLogLevel.Notice, "Adding factory method for type '{0}'", typeName); if(FactoryMethods.ContainsKey(typeName)) { diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Global/Global.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Global/Global.cs index aa71c1f7..121bc9b7 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Global/Global.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Global/Global.cs @@ -1,5 +1,6 @@ using System; using System.Text.RegularExpressions; +using System.Globalization; using Crestron.SimplSharp; using System.Collections.Generic; using Crestron.SimplSharp.CrestronIO; @@ -28,6 +29,10 @@ namespace PepperDash.Essentials.Core public static eCrestronSeries ProcessorSeries { get { return CrestronEnvironment.ProgramCompatibility; } } + // TODO: consider making this configurable later + public static IFormatProvider Culture = CultureInfo.CreateSpecificCulture("en-US"); + + /// /// The file path prefix to the folder containing configuration files /// diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/PartitionSensor/GlsPartitionSensorController.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/PartitionSensor/GlsPartitionSensorController.cs index 26cade95..5f40bf7e 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/PartitionSensor/GlsPartitionSensorController.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/PartitionSensor/GlsPartitionSensorController.cs @@ -135,6 +135,7 @@ namespace PepperDash.Essentials.Core public void SetEnableState(bool state) { + Debug.Console(2, this, "Sensor is {0}, SetEnableState: {1}", _partitionSensor == null ? "null" : "not null", state); if (_partitionSensor == null) return; @@ -143,6 +144,7 @@ namespace PepperDash.Essentials.Core public void IncreaseSensitivity() { + Debug.Console(2, this, "Sensor is {0}, IncreaseSensitivity", _partitionSensor == null ? "null" : "not null"); if (_partitionSensor == null) return; @@ -151,6 +153,7 @@ namespace PepperDash.Essentials.Core public void DecreaseSensitivity() { + Debug.Console(2, this, "Sensor is {0}, DecreaseSensitivity", _partitionSensor == null ? "null" : "not null"); if (_partitionSensor == null) return; @@ -159,6 +162,7 @@ namespace PepperDash.Essentials.Core public void SetSensitivity(ushort value) { + Debug.Console(2, this, "Sensor is {0}, SetSensitivity: {1}", _partitionSensor == null ? "null" : "not null", value); if (_partitionSensor == null) return; @@ -186,8 +190,7 @@ namespace PepperDash.Essentials.Core Debug.Console(0, this, "Linking to Bridge Type {0}", GetType().Name); // link input from simpl - trilist.SetSigTrueAction(joinMap.Enable.JoinNumber, () => SetEnableState(true)); - trilist.SetSigFalseAction(joinMap.Enable.JoinNumber, () => SetEnableState(false)); + trilist.SetBoolSigAction(joinMap.Enable.JoinNumber, SetEnableState); trilist.SetSigTrueAction(joinMap.IncreaseSensitivity.JoinNumber, IncreaseSensitivity); trilist.SetSigTrueAction(joinMap.DecreaseSensitivity.JoinNumber, DecreaseSensitivity); trilist.SetUShortSigAction(joinMap.Sensitivity.JoinNumber, SetSensitivity); diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.csproj b/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.csproj index bde91605..abd7c498 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.csproj +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.csproj @@ -203,6 +203,7 @@ + diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.nuspec b/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.nuspec index e736dcbf..c2394428 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.nuspec +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.nuspec @@ -14,7 +14,7 @@ crestron 3series 4series - + diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Plugins/PluginLoader.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Plugins/PluginLoader.cs index 7437d75a..136303e3 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Plugins/PluginLoader.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Plugins/PluginLoader.cs @@ -123,7 +123,7 @@ namespace PepperDash.Essentials /// static LoadedAssembly LoadAssembly(string filePath) { - Debug.Console(2, "Attempting to load {0}", filePath); + //Debug.Console(2, "Attempting to load {0}", filePath); var assembly = Assembly.LoadFrom(filePath); if (assembly != null) { diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/IEssentialsRoom.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/IEssentialsRoom.cs index e83c4b2f..5f94a5cd 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/IEssentialsRoom.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/IEssentialsRoom.cs @@ -15,7 +15,7 @@ namespace PepperDash.Essentials.Core /// /// Describes the basic functionality of an EssentialsRoom /// - public interface IEssentialsRoom : IKeyName, IReconfigurableDevice + public interface IEssentialsRoom : IKeyName, IReconfigurableDevice, IRunDefaultPresentRoute { BoolFeedback OnFeedback { get; } @@ -56,7 +56,6 @@ namespace PepperDash.Essentials.Core void SetRoomOccupancy(IOccupancyStatusProvider statusProvider, int timeoutMinutes); void PowerOnToDefaultOrLastSource(); - bool RunDefaultPresentRoute(); void SetDefaultLevels(); diff --git a/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmChassisController.cs b/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmChassisController.cs index 8c6b2ced..c74aea2b 100644 --- a/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmChassisController.cs +++ b/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmChassisController.cs @@ -8,6 +8,7 @@ using Crestron.SimplSharpPro.DeviceSupport; using Crestron.SimplSharpPro.DM; using Crestron.SimplSharpPro.DM.Cards; using Crestron.SimplSharpPro.DM.Endpoints; +using Crestron.SimplSharpProInternal; using Newtonsoft.Json; using PepperDash.Core; using PepperDash.Essentials.Core; @@ -482,6 +483,24 @@ namespace PepperDash.Essentials.DM } + private void RegisterForInputResolutionFeedback(IVideoAttributesBasic input, uint number, RoutingInputPortWithVideoStatuses inputPort) + { + if (input == null) + { + return; + } + + Debug.Console(1, this, "Registering for resolution feedback for input {0} using Routing Port {1}", number, inputPort.Key); + + input.VideoAttributes.AttributeChange += (sender, args) => + { + Debug.Console(1, this, "Input {0} resolution updated", number); + + Debug.Console(1, this, "Updating resolution feedback for input {0}", number); + inputPort.VideoStatus.VideoResolutionFeedback.FireUpdate(); + }; + } + /// /// /// @@ -497,166 +516,189 @@ namespace PepperDash.Essentials.DM { case "dmchd": { - var inputCard = new DmcHd(number, this.Chassis); - var cecPort = inputCard.HdmiInput as ICec; - AddHdmiInCardPorts(number, cecPort); + var inputCard = new DmcHd(number, Chassis); + AddHdmiInCardPorts(number, inputCard.HdmiInput, inputCard.HdmiInput); } break; case "dmchddsp": { - var inputCard = new DmcHdDsp(number, this.Chassis); - var cecPort = inputCard.HdmiInput as ICec; - AddHdmiInCardPorts(number, cecPort); + var inputCard = new DmcHdDsp(number, Chassis); + AddHdmiInCardPorts(number, inputCard.HdmiInput, inputCard.HdmiInput); } break; case "dmc4khd": { - var inputCard = new Dmc4kHd(number, this.Chassis); - var cecPort = inputCard.HdmiInput as ICec; - AddHdmiInCardPorts(number, cecPort); + var inputCard = new Dmc4kHd(number, Chassis); + AddHdmiInCardPorts(number, inputCard.HdmiInput, inputCard.HdmiInput); } break; case "dmc4khddsp": { - var inputCard = new Dmc4kHdDsp(number, this.Chassis); - var cecPort = inputCard.HdmiInput as ICec; - AddHdmiInCardPorts(number, cecPort); + var inputCard = new Dmc4kHdDsp(number, Chassis); + AddHdmiInCardPorts(number, inputCard.HdmiInput, inputCard.HdmiInput); } break; case "dmc4kzhd": { - var inputCard = new Dmc4kzHd(number, this.Chassis); - var cecPort = inputCard.HdmiInput as ICec; - AddHdmiInCardPorts(number, cecPort); - } + var inputCard = new Dmc4kzHd(number, Chassis); + AddHdmiInCardPorts(number, inputCard.HdmiInput, inputCard.HdmiInput); break; + } case "dmc4kzhddsp": { - var inputCard = new Dmc4kzHdDsp(number, this.Chassis); - var cecPort = inputCard.HdmiInput as ICec; - AddHdmiInCardPorts(number, cecPort); - } + var inputCard = new Dmc4kzHdDsp(number, Chassis); + AddHdmiInCardPorts(number, inputCard.HdmiInput, inputCard.HdmiInput); break; + } case "dmcc": { - var inputCard = new DmcC(number, this.Chassis); - var cecPort = inputCard.DmInput as ICec; - AddDmInCardPorts(number, cecPort); + var inputCard = new DmcC(number, Chassis); + //DmInput doesn't implement ICec...cast was resulting in null anyway + AddDmInCardPorts(number, null, inputCard.DmInput); } break; case "dmccdsp": { - var inputCard = new DmcCDsp(number, this.Chassis); - var cecPort = inputCard.DmInput as ICec; - AddDmInCardPorts(number, cecPort); - } + var inputCard = new DmcCDsp(number, Chassis); + //DmInput doesn't implement ICec...cast was resulting in null anyway + AddDmInCardPorts(number, null, inputCard.DmInput); break; + } + case "dmc4kc": { - var inputCard = new Dmc4kC(number, this.Chassis); - var cecPort = inputCard.DmInput as ICec; - AddDmInCardPorts(number, cecPort); - } + var inputCard = new Dmc4kC(number, Chassis); + AddDmInCardPorts(number, inputCard.DmInput,inputCard.DmInput); break; + } + case "dmc4kcdsp": { - var inputCard = new Dmc4kCDsp(number, this.Chassis); - var cecPort = inputCard.DmInput as ICec; - AddDmInCardPorts(number, cecPort); - } + var inputCard = new Dmc4kCDsp(number, Chassis); + AddDmInCardPorts(number, inputCard.DmInput,inputCard.DmInput); break; + } + case "dmc4kzc": { - var inputCard = new Dmc4kzC(number, this.Chassis); - var cecPort = inputCard.DmInput as ICec; - AddDmInCardPorts(number, cecPort); - } + var inputCard = new Dmc4kzC(number, Chassis); + AddDmInCardPorts(number, inputCard.DmInput,inputCard.DmInput); break; + } + case "dmc4kzcdsp": { - var inputCard = new Dmc4kzCDsp(number, this.Chassis); - var cecPort = inputCard.DmInput as ICec; - AddDmInCardPorts(number, cecPort); + var inputCard = new Dmc4kzCDsp(number, Chassis); + AddDmInCardPorts(number, inputCard.DmInput, inputCard.DmInput); + break; } - break; + case "dmccat": - new DmcCat(number, this.Chassis); - AddDmInCardPorts(number); + { + var inputCard = new DmcCat(number, Chassis); + AddDmInCardPorts(number, null, inputCard.DmInput); break; + } case "dmccatdsp": - new DmcCatDsp(number, this.Chassis); - AddDmInCardPorts(number); + { + var inputCard = new DmcCatDsp(number, Chassis); + AddDmInCardPorts(number, null, inputCard.DmInput); break; + } case "dmcs": - new DmcS(number, Chassis); - AddInputPortWithDebug(number, "dmIn", eRoutingSignalType.Audio | eRoutingSignalType.Video, eRoutingPortConnectionType.DmMmFiber); + { + var inputCard = new DmcS(number, Chassis); + AddInputPortWithDebug(number, "dmIn", eRoutingSignalType.Audio | eRoutingSignalType.Video, eRoutingPortConnectionType.DmMmFiber, null, inputCard.DmInput); AddInCardHdmiAndAudioLoopPorts(number); break; + } case "dmcsdsp": - new DmcSDsp(number, Chassis); - AddInputPortWithDebug(number, "dmIn", eRoutingSignalType.Audio | eRoutingSignalType.Video, eRoutingPortConnectionType.DmMmFiber); + { + var inputCard = new DmcSDsp(number, Chassis); + AddInputPortWithDebug(number, "dmIn", eRoutingSignalType.Audio | eRoutingSignalType.Video, eRoutingPortConnectionType.DmMmFiber, null, inputCard.DmInput); AddInCardHdmiAndAudioLoopPorts(number); break; + } case "dmcs2": - new DmcS2(number, Chassis); - AddInputPortWithDebug(number, "dmIn", eRoutingSignalType.Audio | eRoutingSignalType.Video, eRoutingPortConnectionType.DmSmFiber); + { + var inputCard = new DmcS2(number, Chassis); + AddInputPortWithDebug(number, "dmIn", eRoutingSignalType.Audio | eRoutingSignalType.Video, eRoutingPortConnectionType.DmSmFiber, null, inputCard.DmInput); AddInCardHdmiAndAudioLoopPorts(number); break; + } case "dmcs2dsp": - new DmcS2Dsp(number, Chassis); - AddInputPortWithDebug(number, "dmIn", eRoutingSignalType.Audio | eRoutingSignalType.Video, eRoutingPortConnectionType.DmSmFiber); + { + var inputCard = new DmcS2Dsp(number, Chassis); + AddInputPortWithDebug(number, "dmIn", eRoutingSignalType.Audio | eRoutingSignalType.Video, eRoutingPortConnectionType.DmSmFiber, null, inputCard.DmInput); AddInCardHdmiAndAudioLoopPorts(number); break; + } case "dmcsdi": - new DmcSdi(number, Chassis); - AddInputPortWithDebug(number, "sdiIn", eRoutingSignalType.Audio | eRoutingSignalType.Video, eRoutingPortConnectionType.Sdi); + { + var inputCard = new DmcSdi(number, Chassis); + AddInputPortWithDebug(number, "sdiIn", eRoutingSignalType.Audio | eRoutingSignalType.Video, eRoutingPortConnectionType.Sdi, null, inputCard.SdiInput); AddOutputPortWithDebug(string.Format("inputCard{0}", number), "sdiOut", eRoutingSignalType.Audio | eRoutingSignalType.Video, eRoutingPortConnectionType.Sdi, null); AddInCardHdmiAndAudioLoopPorts(number); break; + } case "dmcdvi": - new DmcDvi(number, Chassis); - AddInputPortWithDebug(number, "dviIn", eRoutingSignalType.Audio | eRoutingSignalType.Video, eRoutingPortConnectionType.Dvi); + { + var inputCard = new DmcDvi(number, Chassis); + AddInputPortWithDebug(number, "dviIn", eRoutingSignalType.Audio | eRoutingSignalType.Video, eRoutingPortConnectionType.Dvi, null, inputCard.DviInput); AddInputPortWithDebug(number, "audioIn", eRoutingSignalType.Audio, eRoutingPortConnectionType.LineAudio); AddInCardHdmiLoopPort(number); break; + } case "dmcvga": - new DmcVga(number, Chassis); - AddInputPortWithDebug(number, "vgaIn", eRoutingSignalType.Video, eRoutingPortConnectionType.Vga); + { + var inputCard = new DmcVga(number, Chassis); + AddInputPortWithDebug(number, "vgaIn", eRoutingSignalType.Video, eRoutingPortConnectionType.Vga, null, inputCard.VgaInput); AddInputPortWithDebug(number, "audioIn", eRoutingSignalType.Audio, eRoutingPortConnectionType.LineAudio); AddInCardHdmiLoopPort(number); break; + } case "dmcvidbnc": - new DmcVidBnc(number, Chassis); - AddInputPortWithDebug(number, "componentIn", eRoutingSignalType.Video, eRoutingPortConnectionType.Component); + { + var inputCard = new DmcVidBnc(number, Chassis); + AddInputPortWithDebug(number, "componentIn", eRoutingSignalType.Video, eRoutingPortConnectionType.Component, null, inputCard.VideoInput); AddInputPortWithDebug(number, "audioIn", eRoutingSignalType.Audio, eRoutingPortConnectionType.LineAudio); AddInCardHdmiLoopPort(number); break; + } case "dmcvidrcaa": - new DmcVidRcaA(number, Chassis); - AddInputPortWithDebug(number, "componentIn", eRoutingSignalType.Video, eRoutingPortConnectionType.Component); + { + var inputCard = new DmcVidRcaA(number, Chassis); + AddInputPortWithDebug(number, "componentIn", eRoutingSignalType.Video, eRoutingPortConnectionType.Component, null, inputCard.VideoInput); AddInputPortWithDebug(number, "audioIn", eRoutingSignalType.Audio, eRoutingPortConnectionType.LineAudio); AddInCardHdmiLoopPort(number); break; + } case "dmcvidrcad": - new DmcVidRcaD(number, Chassis); - AddInputPortWithDebug(number, "componentIn", eRoutingSignalType.Video, eRoutingPortConnectionType.Component); + { + var inputCard = new DmcVidRcaD(number, Chassis); + AddInputPortWithDebug(number, "componentIn", eRoutingSignalType.Video, eRoutingPortConnectionType.Component, null, inputCard.VideoInput); AddInputPortWithDebug(number, "audioIn", eRoutingSignalType.Audio, eRoutingPortConnectionType.DigitalAudio); AddInCardHdmiLoopPort(number); break; + } case "dmcvid4": - new DmcVid4(number, Chassis); + { + var inputCard = new DmcVid4(number, Chassis); AddInputPortWithDebug(number, "compositeIn1", eRoutingSignalType.Video, eRoutingPortConnectionType.Composite); AddInputPortWithDebug(number, "compositeIn2", eRoutingSignalType.Video, eRoutingPortConnectionType.Composite); AddInputPortWithDebug(number, "compositeIn3", eRoutingSignalType.Video, eRoutingPortConnectionType.Composite); AddInputPortWithDebug(number, "compositeIn4", eRoutingSignalType.Video, eRoutingPortConnectionType.Composite); AddInCardHdmiLoopPort(number); break; + } case "dmcstr": - new DmcStr(number, Chassis); - AddInputPortWithDebug(number, "streamIn", eRoutingSignalType.Audio | eRoutingSignalType.Video, eRoutingPortConnectionType.Streaming); + { + var inputCard = new DmcStr(number, Chassis); + AddInputPortWithDebug(number, "streamIn", eRoutingSignalType.Audio | eRoutingSignalType.Video, eRoutingPortConnectionType.Streaming, null, inputCard.Source); AddInCardHdmiAndAudioLoopPorts(number); break; + } } } @@ -672,12 +714,24 @@ namespace PepperDash.Essentials.DM AddInCardHdmiAndAudioLoopPorts(number); } + void AddDmInCardPorts(uint number, ICec cecPort, IVideoAttributesBasic videoAttributes) + { + AddInputPortWithDebug(number, "dmIn", eRoutingSignalType.Audio | eRoutingSignalType.Video, eRoutingPortConnectionType.DmCat, cecPort, videoAttributes); + AddInCardHdmiAndAudioLoopPorts(number); + } + void AddHdmiInCardPorts(uint number, ICec cecPort) { AddInputPortWithDebug(number, "hdmiIn", eRoutingSignalType.Audio | eRoutingSignalType.Video, eRoutingPortConnectionType.Hdmi, cecPort); AddInCardHdmiAndAudioLoopPorts(number); } + void AddHdmiInCardPorts(uint number, ICec cecPort, IVideoAttributesBasic videoAttributes) + { + AddInputPortWithDebug(number, "hdmiIn", eRoutingSignalType.Audio | eRoutingSignalType.Video, eRoutingPortConnectionType.Hdmi, cecPort, videoAttributes); + AddInCardHdmiAndAudioLoopPorts(number); + } + void AddInCardHdmiAndAudioLoopPorts(uint number) { AddOutputPortWithDebug(string.Format("inputCard{0}", number), "hdmiLoopOut", eRoutingSignalType.Audio | eRoutingSignalType.Video, @@ -816,20 +870,55 @@ namespace PepperDash.Essentials.DM /// void AddInputPortWithDebug(uint cardNum, string portName, eRoutingSignalType sigType, eRoutingPortConnectionType portType) { - AddInputPortWithDebug(cardNum, portName, sigType, portType, null); + //Cast is necessary here to determine the correct overload + AddInputPortWithDebug(cardNum, portName, sigType, portType, null, null); + } + + private void AddInputPortWithDebug(uint cardNum, string portName, eRoutingSignalType sigType, + eRoutingPortConnectionType portType, ICec cecPort) + { + //Cast is necessary here to determine the correct overload + AddInputPortWithDebug(cardNum, portName, sigType, portType, cecPort, null); } /// - /// Adds InputPort and sets Port as ICec object + /// Adds InputPort and sets Port as ICec object. If videoAttributesBasic is defined, RoutingPort will be RoutingInputPortWithVideoStatuses /// - void AddInputPortWithDebug(uint cardNum, string portName, eRoutingSignalType sigType, eRoutingPortConnectionType portType, ICec cecPort) + void AddInputPortWithDebug(uint cardNum, string portName, eRoutingSignalType sigType, eRoutingPortConnectionType portType, ICec cecPort, IVideoAttributesBasic videoAttributesBasic) { var portKey = string.Format("inputCard{0}--{1}", cardNum, portName); Debug.Console(2, this, "Adding input port '{0}'", portKey); - var inputPort = new RoutingInputPort(portKey, sigType, portType, Chassis.Inputs[cardNum], this) + + RoutingInputPort inputPort; + + if (videoAttributesBasic != null) { - FeedbackMatchObject = Chassis.Inputs[cardNum] - }; ; + Debug.Console(1, this, "card {0} supports IVideoAttributesBasic", cardNum); + var statusFuncs = new VideoStatusFuncsWrapper + { + VideoResolutionFeedbackFunc = () => + { + var resolution = videoAttributesBasic.VideoAttributes.GetVideoResolutionString(); + Debug.Console(1, this, "Updating resolution for input {0}. New resolution: {1}", cardNum, resolution); + return resolution; + } + }; + inputPort = new RoutingInputPortWithVideoStatuses(portKey, sigType, portType, + Chassis.Inputs[cardNum], this, statusFuncs) + { + FeedbackMatchObject = Chassis.Inputs[cardNum] + }; + + RegisterForInputResolutionFeedback(videoAttributesBasic, cardNum, inputPort as RoutingInputPortWithVideoStatuses); + } + else + { + inputPort = new RoutingInputPort(portKey, sigType, portType, + Chassis.Inputs[cardNum], this) + { + FeedbackMatchObject = Chassis.Inputs[cardNum] + }; + } if (cecPort != null) inputPort.Port = cecPort; @@ -989,6 +1078,23 @@ namespace PepperDash.Essentials.DM Debug.Console(2, this, "No index of {0} found in InputStreamCardStateFeedbacks"); break; } + case DMInputEventIds.HorizontalResolutionFeedbackEventId: + case DMInputEventIds.VerticalResolutionFeedbackEventId: + case DMInputEventIds.FramesPerSecondFeedbackEventId: + case DMInputEventIds.ResolutionEventId: + { + Debug.Console(1, this, "Input {0} resolution updated", args.Number); + var inputPort = + InputPorts.Cast() + .FirstOrDefault((ip) => ip.Key.Contains(String.Format("inputCard{0}", args.Number))); + + if (inputPort != null) + { + Debug.Console(1, this, "Updating resolution feedback for input {0}", args.Number); + inputPort.VideoStatus.VideoResolutionFeedback.FireUpdate(); + } + break; + } default: { Debug.Console(2, this, "DMInputChange fired for Input {0} with Unhandled EventId: {1}", args.Number, args.EventId); @@ -1050,7 +1156,7 @@ namespace PepperDash.Essentials.DM var inputNumber = Chassis.Outputs[output].VideoOutFeedback == null ? 0 : Chassis. Outputs[output].VideoOutFeedback.Number; - Debug.Console(2, this, "DMSwitchVideo:{0} Routed Input:{1} Output:{2}'", this.Name, inputNumber, output); + Debug.Console(2, this, "DMSwitchVideo:{0} Routed Input:{1} Output:{2}'", Name, inputNumber, output); if (VideoOutputFeedbacks.ContainsKey(output)) { @@ -1077,7 +1183,7 @@ namespace PepperDash.Essentials.DM var inputNumber = Chassis.Outputs[output].AudioOutFeedback == null ? 0 : Chassis. Outputs[output].AudioOutFeedback.Number; - Debug.Console(2, this, "DMSwitchAudio:{0} Routed Input:{1} Output:{2}'", this.Name, inputNumber, output); + Debug.Console(2, this, "DMSwitchAudio:{0} Routed Input:{1} Output:{2}'", Name, inputNumber, output); if (AudioOutputFeedbacks.ContainsKey(output)) { @@ -1388,6 +1494,16 @@ namespace PepperDash.Essentials.DM { trilist.UShortInput[joinMap.HdcpSupportCapability.JoinNumber + ioSlotJoin].UShortValue = 1; } + + var videoStatus = inputPort as RoutingInputPortWithVideoStatuses; + + if (videoStatus == null) + { + return; + } + + Debug.Console(1, this, "Linking {0} to join {1} for resolution feedback.", videoStatus.Key, joinMap.InputCurrentResolution.JoinNumber + ioSlotJoin); + videoStatus.VideoStatus.VideoResolutionFeedback.LinkInputSig(trilist.StringInput[joinMap.InputCurrentResolution.JoinNumber + ioSlotJoin]); } private void LinkStreamInputToApi(BasicTriList trilist, uint ioSlot, DmChassisControllerJoinMap joinMap, uint ioSlotJoin) @@ -1428,6 +1544,13 @@ namespace PepperDash.Essentials.DM InputStreamCardStateFeedbacks[ioSlot].LinkInputSig(trilist.UShortInput[join]); trilist.UShortInput[join].UShortValue = InputStreamCardStateFeedbacks[ioSlot].UShortValue; + + var videoStatus = inputPort as RoutingInputPortWithVideoStatuses; + + if (videoStatus != null) + { + videoStatus.VideoStatus.VideoResolutionFeedback.LinkInputSig(trilist.StringInput[joinMap.InputCurrentResolution.JoinNumber + ioSlotJoin]); + } } private void LinkStreamOutputToApi(BasicTriList trilist, uint ioSlot, DmChassisControllerJoinMap joinMap, uint ioSlotJoin) @@ -1564,10 +1687,19 @@ namespace PepperDash.Essentials.DM { trilist.UShortInput[joinMap.HdcpSupportCapability.JoinNumber + ioSlotJoin].UShortValue = 1; } + + var videoStatus = inputPort as RoutingInputPortWithVideoStatuses; + + if (videoStatus == null) + { + return; + } + Debug.Console(1, this, "Linking {0} to join {1} for resolution feedback.", videoStatus.Key, joinMap.InputCurrentResolution.JoinNumber + ioSlotJoin); + videoStatus.VideoStatus.VideoResolutionFeedback.LinkInputSig(trilist.StringInput[joinMap.InputCurrentResolution.JoinNumber + ioSlotJoin]); } private void LinkAdvancedTxToApi(BasicTriList trilist, DmChassisControllerJoinMap joinMap, - uint ioSlot, uint ioSlotJoin, BasicDmTxControllerBase basicTransmitter) + uint ioSlot, uint ioSlotJoin, BasicDmTxControllerBase basicTransmitter) { var transmitter = basicTransmitter as DmTxControllerBase; if (transmitter == null) return; @@ -1581,7 +1713,9 @@ namespace PepperDash.Essentials.DM if (txRoutingInputs == null) return; - var inputPorts = txRoutingInputs.InputPorts.Where((p) => p.Port is EndpointHdmiInput || p.Port is EndpointDisplayPortInput).ToList(); + var inputPorts = + txRoutingInputs.InputPorts.Where( + (p) => p.Port is EndpointHdmiInput || p.Port is EndpointDisplayPortInput).ToList(); if (inputPorts.Count == 0) { @@ -1614,6 +1748,19 @@ namespace PepperDash.Essentials.DM trilist.UShortInput[joinMap.HdcpSupportCapability.JoinNumber + ioSlotJoin].UShortValue = (ushort) transmitter.HdcpSupportCapability; + + + var videoStatus = + InputPorts[string.Format("inputCard{0}--dmIn", ioSlot)] as RoutingInputPortWithVideoStatuses; + + if (videoStatus == null) + { + return; + } + Debug.Console(1, this, "Linking {0} to join {1} for resolution feedback.", videoStatus.Key, + joinMap.InputCurrentResolution.JoinNumber + ioSlotJoin); + videoStatus.VideoStatus.VideoResolutionFeedback.LinkInputSig( + trilist.StringInput[joinMap.InputCurrentResolution.JoinNumber + ioSlotJoin]); } private void LinkTxOnlineFeedbackToApi(BasicTriList trilist, uint ioSlot, DmChassisControllerJoinMap joinMap, @@ -1896,15 +2043,15 @@ namespace PepperDash.Essentials.DM { var props = JsonConvert.DeserializeObject - (dc.Properties.ToString()); - return PepperDash.Essentials.DM.DmChassisController. + (dc.Properties.ToString()); + return DmChassisController. GetDmChassisController(dc.Key, dc.Name, type, props); } else if (type.StartsWith("dmmd128x") || type.StartsWith("dmmd64x")) { var props = JsonConvert.DeserializeObject - (dc.Properties.ToString()); - return PepperDash.Essentials.DM.DmBladeChassisController. + (dc.Properties.ToString()); + return DmBladeChassisController. GetDmChassisController(dc.Key, dc.Name, type, props); } diff --git a/essentials-framework/Essentials DM/Essentials_DM/Chassis/HdMdNxM4kEBridgeableController.cs b/essentials-framework/Essentials DM/Essentials_DM/Chassis/HdMdNxM4kEBridgeableController.cs index b766ac3c..3d41af3a 100644 --- a/essentials-framework/Essentials DM/Essentials_DM/Chassis/HdMdNxM4kEBridgeableController.cs +++ b/essentials-framework/Essentials DM/Essentials_DM/Chassis/HdMdNxM4kEBridgeableController.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using System.Text.RegularExpressions; using Newtonsoft.Json; using Crestron.SimplSharp; using Crestron.SimplSharpPro.DeviceSupport; @@ -15,421 +16,489 @@ using PepperDash.Essentials.Core.Config; namespace PepperDash.Essentials.DM.Chassis { - [Description("Wrapper class for all HdMdNxM4E switchers")] - public class HdMdNxM4kEBridgeableController : CrestronGenericBridgeableBaseDevice, IRoutingNumericWithFeedback, IHasFeedback - { - private HdMdNxM _Chassis; - private HdMd4x14kE _Chassis4x1; + [Description("Wrapper class for all HdMdNxM4E switchers")] + public class HdMdNxM4kEBridgeableController : CrestronGenericBridgeableBaseDevice, IRoutingNumericWithFeedback, IHasFeedback + { + private HdMdNxM _Chassis; + private HdMd4x14kE _Chassis4x1; - //IroutingNumericEvent - public event EventHandler NumericSwitchChange; + //IroutingNumericEvent + public event EventHandler NumericSwitchChange; - public Dictionary InputNames { get; set; } - public Dictionary OutputNames { get; set; } + public Dictionary InputNames { get; set; } + public Dictionary OutputNames { get; set; } - public RoutingPortCollection InputPorts { get; private set; } - public RoutingPortCollection OutputPorts { get; private set; } + public RoutingPortCollection InputPorts { get; private set; } + public RoutingPortCollection OutputPorts { get; private set; } - public FeedbackCollection VideoInputSyncFeedbacks { get; private set; } - public FeedbackCollection VideoOutputRouteFeedbacks { get; private set; } - public FeedbackCollection InputNameFeedbacks { get; private set; } - public FeedbackCollection OutputNameFeedbacks { get; private set; } - public FeedbackCollection OutputRouteNameFeedbacks { get; private set; } - public FeedbackCollection InputHdcpEnableFeedback { get; private set; } - public FeedbackCollection DeviceNameFeedback { get; private set; } - public FeedbackCollection AutoRouteFeedback { get; private set; } + public FeedbackCollection VideoInputSyncFeedbacks { get; private set; } + public FeedbackCollection VideoOutputRouteFeedbacks { get; private set; } + public FeedbackCollection InputNameFeedbacks { get; private set; } + public FeedbackCollection OutputNameFeedbacks { get; private set; } + public FeedbackCollection OutputRouteNameFeedbacks { get; private set; } + public FeedbackCollection InputHdcpEnableFeedback { get; private set; } + public StringFeedback DeviceNameFeedback { get; private set; } + public BoolFeedback AutoRouteFeedback { get; private set; } - #region Constructor + #region Constructor - public HdMdNxM4kEBridgeableController(string key, string name, HdMdNxM chassis, - HdMdNxM4kEBridgeablePropertiesConfig props) - : base(key, name, chassis) - { - _Chassis = chassis; - var _props = props; + public HdMdNxM4kEBridgeableController(string key, string name, HdMdNxM chassis, + HdMdNxM4kEBridgeablePropertiesConfig props) + : base(key, name, chassis) + { + _Chassis = chassis; + Name = name; - InputNames = props.Inputs; - OutputNames = props.Outputs; + if (props == null) + { + Debug.Console(1, this, "HdMdNx4keBridgeableController properties are null, failed to build the device"); + return; + } - VideoInputSyncFeedbacks = new FeedbackCollection(); - VideoOutputRouteFeedbacks = new FeedbackCollection(); - InputNameFeedbacks = new FeedbackCollection(); - OutputNameFeedbacks = new FeedbackCollection(); - OutputRouteNameFeedbacks = new FeedbackCollection(); - InputHdcpEnableFeedback = new FeedbackCollection(); - DeviceNameFeedback = new FeedbackCollection(); - AutoRouteFeedback = new FeedbackCollection(); - InputPorts = new RoutingPortCollection(); - OutputPorts = new RoutingPortCollection(); + if (props.Inputs != null) + { + foreach (var kvp in props.Inputs) + { + Debug.Console(1, this, "props.Inputs: {0}-{1}", kvp.Key, kvp.Value); + } + InputNames = props.Inputs; + } + if (props.Outputs != null) + { + foreach (var kvp in props.Outputs) + { + Debug.Console(1, this, "props.Outputs: {0}-{1}", kvp.Key, kvp.Value); + } + OutputNames = props.Outputs; + } - DeviceNameFeedback.Add(new StringFeedback(this.Name, () => this.Name)); + DeviceNameFeedback = new StringFeedback(()=>Name); - if (_Chassis.NumberOfInputs == 1) - { - _Chassis4x1 = _Chassis as HdMd4x14kE; - AutoRouteFeedback.Add(new BoolFeedback(this.Name + "-" + InputNames[1], () => _Chassis4x1.AutoModeOnFeedback.BoolValue)); - } + VideoInputSyncFeedbacks = new FeedbackCollection(); + VideoOutputRouteFeedbacks = new FeedbackCollection(); + InputNameFeedbacks = new FeedbackCollection(); + OutputNameFeedbacks = new FeedbackCollection(); + OutputRouteNameFeedbacks = new FeedbackCollection(); + InputHdcpEnableFeedback = new FeedbackCollection(); + + InputPorts = new RoutingPortCollection(); + OutputPorts = new RoutingPortCollection(); - for (uint i = 1; i <= _Chassis.NumberOfInputs; i++) - { - var index = i; - var inputName = InputNames[index]; - _Chassis.Inputs[index].Name.StringValue = inputName; + if (_Chassis.NumberOfInputs == 1) + { + _Chassis4x1 = _Chassis as HdMd4x14kE; + AutoRouteFeedback = new BoolFeedback(() => _Chassis4x1.AutoModeOnFeedback.BoolValue); + } - InputPorts.Add(new RoutingInputPort(inputName, eRoutingSignalType.AudioVideo, - eRoutingPortConnectionType.Hdmi, index, this) - { - FeedbackMatchObject = _Chassis.HdmiInputs[index] - }); - VideoInputSyncFeedbacks.Add(new BoolFeedback(inputName, () => _Chassis.Inputs[index].VideoDetectedFeedback.BoolValue)); - InputNameFeedbacks.Add(new StringFeedback(inputName, () => _Chassis.Inputs[index].Name.StringValue)); - InputHdcpEnableFeedback.Add(new BoolFeedback(inputName, () => _Chassis.HdmiInputs[index].HdmiInputPort.HdcpSupportOnFeedback.BoolValue)); - } + for (uint i = 1; i <= _Chassis.NumberOfInputs; i++) + { + var index = i; + var inputName = InputNames[index]; + //_Chassis.Inputs[index].Name.StringValue = inputName; + _Chassis.HdmiInputs[index].Name.StringValue = inputName; - for (uint i = 1; i <= _Chassis.NumberOfOutputs; i++) - { - var index = i; - var outputName = OutputNames[index]; - _Chassis.Outputs[i].Name.StringValue = outputName; + InputPorts.Add(new RoutingInputPort(inputName, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.Hdmi, _Chassis.HdmiInputs[index], this) + { + FeedbackMatchObject = _Chassis.HdmiInputs[index] + }); + VideoInputSyncFeedbacks.Add(new BoolFeedback(inputName, () => _Chassis.Inputs[index].VideoDetectedFeedback.BoolValue)); + //InputNameFeedbacks.Add(new StringFeedback(inputName, () => _Chassis.Inputs[index].NameFeedback.StringValue)); + InputNameFeedbacks.Add(new StringFeedback(inputName, () => InputNames[index])); + InputHdcpEnableFeedback.Add(new BoolFeedback(inputName, () => _Chassis.HdmiInputs[index].HdmiInputPort.HdcpSupportOnFeedback.BoolValue)); + } - OutputPorts.Add(new RoutingOutputPort(outputName, eRoutingSignalType.AudioVideo, - eRoutingPortConnectionType.Hdmi, index, this) - { - FeedbackMatchObject = _Chassis.HdmiOutputs[index] - }); - VideoOutputRouteFeedbacks.Add(new IntFeedback(outputName, () => (int)_Chassis.Outputs[index].VideoOutFeedback.Number)); - OutputNameFeedbacks.Add(new StringFeedback(outputName, () => _Chassis.Outputs[index].Name.StringValue)); - OutputRouteNameFeedbacks.Add(new StringFeedback(outputName, () => _Chassis.Outputs[index].VideoOutFeedback.NameFeedback.StringValue)); - } + for (uint i = 1; i <= _Chassis.NumberOfOutputs; i++) + { + var index = i; + var outputName = OutputNames[index]; + //_Chassis.Outputs[index].Name.StringValue = outputName; + //_Chassis.HdmiOutputs[index].Name.StringValue = outputName; - _Chassis.DMInputChange += new DMInputEventHandler(Chassis_DMInputChange); - _Chassis.DMOutputChange += new DMOutputEventHandler(Chassis_DMOutputChange); + OutputPorts.Add(new RoutingOutputPort(outputName, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.Hdmi, _Chassis.HdmiOutputs[index], this) + { + FeedbackMatchObject = _Chassis.HdmiOutputs[index] + }); + VideoOutputRouteFeedbacks.Add(new IntFeedback(outputName, () => _Chassis.Outputs[index].VideoOutFeedback == null ? 0 : (int)_Chassis.Outputs[index].VideoOutFeedback.Number)); + OutputNameFeedbacks.Add(new StringFeedback(outputName, () => OutputNames[index])); + OutputRouteNameFeedbacks.Add(new StringFeedback(outputName, () => _Chassis.Outputs[index].VideoOutFeedback.NameFeedback.StringValue)); + } - AddPostActivationAction(AddFeedbackCollections); - } + _Chassis.DMInputChange += Chassis_DMInputChange; + _Chassis.DMOutputChange += Chassis_DMOutputChange; - #endregion + AddPostActivationAction(AddFeedbackCollections); + } - #region Methods + #endregion - /// - /// Raise an event when the status of a switch object changes. - /// - /// Arguments defined as IKeyName sender, output, input, and eRoutingSignalType - private void OnSwitchChange(RoutingNumericEventArgs e) - { - var newEvent = NumericSwitchChange; - if (newEvent != null) newEvent(this, e); - } + #region Methods - public void EnableHdcp(uint port) - { - if (port > _Chassis.NumberOfInputs) return; - if (port <= 0) return; + /// + /// Raise an event when the status of a switch object changes. + /// + /// Arguments defined as IKeyName sender, output, input, and eRoutingSignalType + private void OnSwitchChange(RoutingNumericEventArgs e) + { + var newEvent = NumericSwitchChange; + if (newEvent != null) newEvent(this, e); + } - _Chassis.HdmiInputs[port].HdmiInputPort.HdcpSupportOn(); - InputHdcpEnableFeedback[InputNames[port]].FireUpdate(); - } + public void EnableHdcp(uint port) + { + if (port > _Chassis.NumberOfInputs) return; + if (port <= 0) return; - public void DisableHdcp(uint port) - { - if (port > _Chassis.NumberOfInputs) return; - if (port <= 0) return; + _Chassis.HdmiInputs[port].HdmiInputPort.HdcpSupportOn(); + InputHdcpEnableFeedback[InputNames[port]].FireUpdate(); + } - _Chassis.HdmiInputs[port].HdmiInputPort.HdcpSupportOff(); - InputHdcpEnableFeedback[InputNames[port]].FireUpdate(); - } + public void DisableHdcp(uint port) + { + if (port > _Chassis.NumberOfInputs) return; + if (port <= 0) return; - public void EnableAutoRoute() - { - if (_Chassis.NumberOfInputs != 1) return; + _Chassis.HdmiInputs[port].HdmiInputPort.HdcpSupportOff(); + InputHdcpEnableFeedback[InputNames[port]].FireUpdate(); + } - if (_Chassis4x1 == null) return; + public void EnableAutoRoute() + { + if (_Chassis.NumberOfInputs != 1) return; - _Chassis4x1.AutoModeOn(); - } + if (_Chassis4x1 == null) return; - public void DisableAutoRoute() - { - if (_Chassis.NumberOfInputs != 1) return; + _Chassis4x1.AutoModeOn(); + } - if (_Chassis4x1 == null) return; + public void DisableAutoRoute() + { + if (_Chassis.NumberOfInputs != 1) return; - _Chassis4x1.AutoModeOff(); - } + if (_Chassis4x1 == null) return; - #region PostActivate + _Chassis4x1.AutoModeOff(); + } - public void AddFeedbackCollections() - { - AddCollectionsToList(VideoInputSyncFeedbacks, InputHdcpEnableFeedback); - AddCollectionsToList(VideoOutputRouteFeedbacks); - AddCollectionsToList(InputNameFeedbacks, OutputNameFeedbacks, OutputRouteNameFeedbacks, DeviceNameFeedback); - } + #region PostActivate - #endregion + public void AddFeedbackCollections() + { + AddFeedbackToList(DeviceNameFeedback); + AddCollectionsToList(VideoInputSyncFeedbacks, InputHdcpEnableFeedback); + AddCollectionsToList(VideoOutputRouteFeedbacks); + AddCollectionsToList(InputNameFeedbacks, OutputNameFeedbacks, OutputRouteNameFeedbacks); + } - #region FeedbackCollection Methods + #endregion - //Add arrays of collections - public void AddCollectionsToList(params FeedbackCollection[] newFbs) - { - foreach (FeedbackCollection fbCollection in newFbs) - { - foreach (var item in newFbs) - { - AddCollectionToList(item); - } - } - } - public void AddCollectionsToList(params FeedbackCollection[] newFbs) - { - foreach (FeedbackCollection fbCollection in newFbs) - { - foreach (var item in newFbs) - { - AddCollectionToList(item); - } - } - } + #region FeedbackCollection Methods - public void AddCollectionsToList(params FeedbackCollection[] newFbs) - { - foreach (FeedbackCollection fbCollection in newFbs) - { - foreach (var item in newFbs) - { - AddCollectionToList(item); - } - } - } + //Add arrays of collections + public void AddCollectionsToList(params FeedbackCollection[] newFbs) + { + foreach (FeedbackCollection fbCollection in newFbs) + { + foreach (var item in newFbs) + { + AddCollectionToList(item); + } + } + } + public void AddCollectionsToList(params FeedbackCollection[] newFbs) + { + foreach (FeedbackCollection fbCollection in newFbs) + { + foreach (var item in newFbs) + { + AddCollectionToList(item); + } + } + } - //Add Collections - public void AddCollectionToList(FeedbackCollection newFbs) - { - foreach (var f in newFbs) - { - if (f == null) continue; + public void AddCollectionsToList(params FeedbackCollection[] newFbs) + { + foreach (FeedbackCollection fbCollection in newFbs) + { + foreach (var item in newFbs) + { + AddCollectionToList(item); + } + } + } - AddFeedbackToList(f); - } - } + //Add Collections + public void AddCollectionToList(FeedbackCollection newFbs) + { + foreach (var f in newFbs) + { + if (f == null) continue; - public void AddCollectionToList(FeedbackCollection newFbs) - { - foreach (var f in newFbs) - { - if (f == null) continue; + AddFeedbackToList(f); + } + } - AddFeedbackToList(f); - } - } + public void AddCollectionToList(FeedbackCollection newFbs) + { + foreach (var f in newFbs) + { + if (f == null) continue; - public void AddCollectionToList(FeedbackCollection newFbs) - { - foreach (var f in newFbs) - { - if (f == null) continue; + AddFeedbackToList(f); + } + } - AddFeedbackToList(f); - } - } + public void AddCollectionToList(FeedbackCollection newFbs) + { + foreach (var f in newFbs) + { + if (f == null) continue; - //Add Individual Feedbacks - public void AddFeedbackToList(PepperDash.Essentials.Core.Feedback newFb) - { - if (newFb == null) return; + AddFeedbackToList(f); + } + } - if (!Feedbacks.Contains(newFb)) - { - Feedbacks.Add(newFb); - } - } + //Add Individual Feedbacks + public void AddFeedbackToList(PepperDash.Essentials.Core.Feedback newFb) + { + if (newFb == null) return; - #endregion + if (!Feedbacks.Contains(newFb)) + { + Feedbacks.Add(newFb); + } + } - #region IRouting Members + #endregion - public void ExecuteSwitch(object inputSelector, object outputSelector, eRoutingSignalType signalType) - { - // Try to make switch only when necessary. The unit appears to toggle when already selected. - var current = _Chassis.HdmiOutputs[(uint)outputSelector].VideoOut; - if (current != _Chassis.HdmiInputs[(uint)inputSelector]) - _Chassis.HdmiOutputs[(uint)outputSelector].VideoOut = _Chassis.HdmiInputs[(uint)inputSelector]; - } + #region IRouting Members - #endregion + public void ExecuteSwitch(object inputSelector, object outputSelector, eRoutingSignalType signalType) + { + var input = inputSelector as HdMdNxMHdmiInput; //changed from HdMdNxM4kzEHdmiInput; + var output = outputSelector as HdMdNxMHdmiOutput; + Debug.Console(2, this, "ExecuteSwitch: input={0} output={1}", input, output); - #region IRoutingNumeric Members + if (output == null) + { + Debug.Console(0, this, "Unable to make switch. output selector is not HdMdNxMHdmiOutput"); + return; + } - public void ExecuteNumericSwitch(ushort inputSelector, ushort outputSelector, eRoutingSignalType signalType) - { - ExecuteSwitch(inputSelector, outputSelector, signalType); - } + // Try to make switch only when necessary. The unit appears to toggle when already selected. + var current = output.VideoOut; + if (current != input) + output.VideoOut = input; + } - #endregion + #endregion - #endregion + #region IRoutingNumeric Members - #region Bridge Linking + public void ExecuteNumericSwitch(ushort inputSelector, ushort outputSelector, eRoutingSignalType signalType) + { + var input = inputSelector == 0 ? null : _Chassis.HdmiInputs[inputSelector]; + var output = _Chassis.HdmiOutputs[outputSelector]; - public override void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge) - { - var joinMap = new HdMdNxM4kEControllerJoinMap(joinStart); + Debug.Console(2, this, "ExecuteNumericSwitch: input={0} output={1}", input, output); - var joinMapSerialized = JoinMapHelper.GetSerializedJoinMapForDevice(joinMapKey); + ExecuteSwitch(input, output, signalType); + } - if (!string.IsNullOrEmpty(joinMapSerialized)) - joinMap = JsonConvert.DeserializeObject(joinMapSerialized); + #endregion - if (bridge != null) - { - bridge.AddJoinMap(Key, joinMap); - } - else - { - Debug.Console(0, this, "Please update config to use 'eiscapiadvanced' to get all join map features for this device."); - } + #endregion - IsOnline.LinkInputSig(trilist.BooleanInput[joinMap.IsOnline.JoinNumber]); - DeviceNameFeedback[this.Name].LinkInputSig(trilist.StringInput[joinMap.Name.JoinNumber]); + #region Bridge Linking + + public override void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge) + { + var joinMap = new HdMdNxM4kEControllerJoinMap(joinStart); + + var joinMapSerialized = JoinMapHelper.GetSerializedJoinMapForDevice(joinMapKey); + + if (!string.IsNullOrEmpty(joinMapSerialized)) + joinMap = JsonConvert.DeserializeObject(joinMapSerialized); + + if (bridge != null) + { + bridge.AddJoinMap(Key, joinMap); + } + else + { + Debug.Console(0, this, "Please update config to use 'eiscapiadvanced' to get all join map features for this device."); + } + + IsOnline.LinkInputSig(trilist.BooleanInput[joinMap.IsOnline.JoinNumber]); + DeviceNameFeedback.LinkInputSig(trilist.StringInput[joinMap.Name.JoinNumber]); + + if (_Chassis4x1 != null) + { + trilist.SetSigTrueAction(joinMap.EnableAutoRoute.JoinNumber, () => _Chassis4x1.AutoModeOn()); + trilist.SetSigFalseAction(joinMap.EnableAutoRoute.JoinNumber, () => _Chassis4x1.AutoModeOff()); + AutoRouteFeedback.LinkInputSig(trilist.BooleanInput[joinMap.EnableAutoRoute.JoinNumber]); + } + + for (uint i = 1; i <= _Chassis.NumberOfInputs; i++) + { + var joinIndex = i - 1; + var input = i; + //Digital + VideoInputSyncFeedbacks[InputNames[input]].LinkInputSig(trilist.BooleanInput[joinMap.InputSync.JoinNumber + joinIndex]); + InputHdcpEnableFeedback[InputNames[input]].LinkInputSig(trilist.BooleanInput[joinMap.EnableInputHdcp.JoinNumber + joinIndex]); + InputHdcpEnableFeedback[InputNames[input]].LinkComplementInputSig(trilist.BooleanInput[joinMap.DisableInputHdcp.JoinNumber + joinIndex]); + trilist.SetSigTrueAction(joinMap.EnableInputHdcp.JoinNumber + joinIndex, () => EnableHdcp(input)); + trilist.SetSigTrueAction(joinMap.DisableInputHdcp.JoinNumber + joinIndex, () => DisableHdcp(input)); + + //Serial + InputNameFeedbacks[InputNames[input]].LinkInputSig(trilist.StringInput[joinMap.InputName.JoinNumber + joinIndex]); + } + + for (uint i = 1; i <= _Chassis.NumberOfOutputs; i++) + { + var joinIndex = i - 1; + var output = i; + //Analog + VideoOutputRouteFeedbacks[OutputNames[output]].LinkInputSig(trilist.UShortInput[joinMap.OutputRoute.JoinNumber + joinIndex]); + trilist.SetUShortSigAction(joinMap.OutputRoute.JoinNumber + joinIndex, (a) => ExecuteNumericSwitch(a, (ushort) output, eRoutingSignalType.AudioVideo)); + + //Serial + OutputNameFeedbacks[OutputNames[output]].LinkInputSig(trilist.StringInput[joinMap.OutputName.JoinNumber + joinIndex]); + OutputRouteNameFeedbacks[OutputNames[output]].LinkInputSig(trilist.StringInput[joinMap.OutputRoutedName.JoinNumber + joinIndex]); + } + + _Chassis.OnlineStatusChange += Chassis_OnlineStatusChange; + + trilist.OnlineStatusChange += (d, args) => + { + if (!args.DeviceOnLine) return; + + // feedback updates was moved to the Chassis_OnlineStatusChange + // due to the amount of time it takes for the device to come online + }; + } + + + #endregion + + #region Events + + void Chassis_OnlineStatusChange(Crestron.SimplSharpPro.GenericBase currentDevice, Crestron.SimplSharpPro.OnlineOfflineEventArgs args) + { + IsOnline.FireUpdate(); + + if (!args.DeviceOnLine) return; + + foreach (var feedback in Feedbacks) + { + feedback.FireUpdate(); + } if (_Chassis4x1 != null) - { - trilist.SetSigTrueAction(joinMap.EnableAutoRoute.JoinNumber, () => _Chassis4x1.AutoModeOn()); - trilist.SetSigFalseAction(joinMap.EnableAutoRoute.JoinNumber, () => _Chassis4x1.AutoModeOff()); - AutoRouteFeedback[this.Name + "-" + InputNames[1]].LinkInputSig(trilist.BooleanInput[joinMap.EnableAutoRoute.JoinNumber]); - } + AutoRouteFeedback.FireUpdate(); + } - for (uint i = 1; i <= _Chassis.NumberOfInputs; i++) - { - var joinIndex = i - 1; - //Digital - VideoInputSyncFeedbacks[InputNames[i]].LinkInputSig(trilist.BooleanInput[joinMap.InputSync.JoinNumber + joinIndex]); - InputHdcpEnableFeedback[InputNames[i]].LinkInputSig(trilist.BooleanInput[joinMap.EnableInputHdcp.JoinNumber + joinIndex]); - InputHdcpEnableFeedback[InputNames[i]].LinkComplementInputSig(trilist.BooleanInput[joinMap.DisableInputHdcp.JoinNumber + joinIndex]); - trilist.SetSigTrueAction(joinMap.EnableInputHdcp.JoinNumber + joinIndex, () => EnableHdcp(i)); - trilist.SetSigTrueAction(joinMap.DisableInputHdcp.JoinNumber + joinIndex, () => DisableHdcp(i)); + void Chassis_DMOutputChange(Switch device, DMOutputEventArgs args) + { + if (args.EventId != DMOutputEventIds.VideoOutEventId) return; - //Serial - InputNameFeedbacks[InputNames[i]].LinkInputSig(trilist.StringInput[joinMap.InputName.JoinNumber + joinIndex]); - } + var output = args.Number; - for (uint i = 1; i <= _Chassis.NumberOfOutputs; i++) - { - var joinIndex = i - 1; - //Analog - VideoOutputRouteFeedbacks[OutputNames[i]].LinkInputSig(trilist.UShortInput[joinMap.OutputRoute.JoinNumber + joinIndex]); - trilist.SetUShortSigAction(joinMap.OutputRoute.JoinNumber + joinIndex, (a) => ExecuteSwitch(a, i, eRoutingSignalType.AudioVideo)); + var inputNumber = _Chassis.HdmiOutputs[output].VideoOutFeedback == null + ? 0 + : _Chassis.HdmiOutputs[output].VideoOutFeedback.Number; - //Serial - OutputNameFeedbacks[OutputNames[i]].LinkInputSig(trilist.StringInput[joinMap.OutputName.JoinNumber + joinIndex]); - OutputRouteNameFeedbacks[OutputNames[i]].LinkInputSig(trilist.StringInput[joinMap.OutputRoutedName.JoinNumber + joinIndex]); - } + var outputName = OutputNames[output]; - _Chassis.OnlineStatusChange += new Crestron.SimplSharpPro.OnlineStatusChangeEventHandler(Chassis_OnlineStatusChange); + var feedback = VideoOutputRouteFeedbacks[outputName]; - trilist.OnlineStatusChange += new Crestron.SimplSharpPro.OnlineStatusChangeEventHandler((d, args) => - { - if (args.DeviceOnLine) - { - foreach (var feedback in Feedbacks) + if (feedback == null) + { + return; + } + var inPort = + InputPorts.FirstOrDefault(p => p.FeedbackMatchObject == _Chassis.HdmiOutputs[output].VideoOutFeedback); + var outPort = OutputPorts.FirstOrDefault(p => p.FeedbackMatchObject == _Chassis.HdmiOutputs[output]); + + feedback.FireUpdate(); + OnSwitchChange(new RoutingNumericEventArgs(output, inputNumber, outPort, inPort, eRoutingSignalType.AudioVideo)); + } + + void Chassis_DMInputChange(Switch device, DMInputEventArgs args) + { + switch (args.EventId) + { + case DMInputEventIds.VideoDetectedEventId: + { + Debug.Console(1, this, "Event ID {0}: Updating VideoInputSyncFeedbacks", args.EventId); + foreach (var item in VideoInputSyncFeedbacks) { - feedback.FireUpdate(); + item.FireUpdate(); } - } - }); - } + break; + } + case DMInputEventIds.InputNameFeedbackEventId: + case DMInputEventIds.InputNameEventId: + case DMInputEventIds.NameFeedbackEventId: + { + Debug.Console(1, this, "Event ID {0}: Updating name feedbacks.", args.EventId); + Debug.Console(1, this, "Input {0} Name {1}", args.Number, + _Chassis.HdmiInputs[args.Number].NameFeedback.StringValue); + foreach (var item in InputNameFeedbacks) + { + item.FireUpdate(); + } + break; + } + default: + { + Debug.Console(1, this, "Unhandled DM Input Event ID {0}", args.EventId); + break; + } + } + } + + #endregion + + #region Factory + + public class HdMdNxM4kEControllerFactory : EssentialsDeviceFactory + { + public HdMdNxM4kEControllerFactory() + { + TypeNames = new List() { "hdmd4x14ke-bridgeable", "hdmd4x24ke", "hdmd6x24ke" }; + } + + public override EssentialsDevice BuildDevice(DeviceConfig dc) + { + Debug.Console(1, "Factory Attempting to create new HD-MD-NxM-4K-E Device"); + + var props = JsonConvert.DeserializeObject(dc.Properties.ToString()); + + var type = dc.Type.ToLower(); + var control = props.Control; + var ipid = control.IpIdInt; + var address = control.TcpSshProperties.Address; + + switch (type) + { + case ("hdmd4x14ke-bridgeable"): + return new HdMdNxM4kEBridgeableController(dc.Key, dc.Name, new HdMd4x14kE(ipid, address, Global.ControlSystem), props); + case ("hdmd4x24ke"): + return new HdMdNxM4kEBridgeableController(dc.Key, dc.Name, new HdMd4x24kE(ipid, address, Global.ControlSystem), props); + case ("hdmd6x24ke"): + return new HdMdNxM4kEBridgeableController(dc.Key, dc.Name, new HdMd6x24kE(ipid, address, Global.ControlSystem), props); + default: + return null; + } + } + } + + #endregion - #endregion - #region Events - - void Chassis_OnlineStatusChange(Crestron.SimplSharpPro.GenericBase currentDevice, Crestron.SimplSharpPro.OnlineOfflineEventArgs args) - { - if (!args.DeviceOnLine) return; - for (uint i = 1; i <= _Chassis.NumberOfInputs; i++) - { - _Chassis.Inputs[i].Name.StringValue = InputNames[i]; - } - for (uint i = 1; i <= _Chassis.NumberOfOutputs; i++) - { - _Chassis.Outputs[i].Name.StringValue = OutputNames[i]; - } - - foreach (var feedback in Feedbacks) - { - feedback.FireUpdate(); - } - } - - void Chassis_DMOutputChange(Switch device, DMOutputEventArgs args) - { - if (args.EventId != DMOutputEventIds.VideoOutEventId) return; - - for (var i = 0; i < VideoOutputRouteFeedbacks.Count; i++) - { - var index = i; - var localInputPort = InputPorts.FirstOrDefault(p => (DMInput)p.FeedbackMatchObject == _Chassis.HdmiOutputs[(uint)index + 1].VideoOutFeedback); - var localOutputPort = - OutputPorts.FirstOrDefault(p => (DMOutput)p.FeedbackMatchObject == _Chassis.HdmiOutputs[(uint)index + 1]); - - - VideoOutputRouteFeedbacks[i].FireUpdate(); - OnSwitchChange(new RoutingNumericEventArgs((ushort)i, VideoOutputRouteFeedbacks[i].UShortValue, localOutputPort, localInputPort, eRoutingSignalType.AudioVideo)); - } - } - - void Chassis_DMInputChange(Switch device, DMInputEventArgs args) - { - if (args.EventId != DMInputEventIds.VideoDetectedEventId) return; - foreach (var item in VideoInputSyncFeedbacks) - { - item.FireUpdate(); - } - } - - #endregion - - #region Factory - - public class HdMdNxM4kEControllerFactory : EssentialsDeviceFactory - { - public HdMdNxM4kEControllerFactory() - { - TypeNames = new List() { "hdmd4x14ke-bridgeable", "hdmd4x24ke", "hdmd6x24ke" }; - } - - public override EssentialsDevice BuildDevice(DeviceConfig dc) - { - Debug.Console(1, "Factory Attempting to create new HD-MD-NxM-4K-E Device"); - - var props = JsonConvert.DeserializeObject(dc.Properties.ToString()); - - var type = dc.Type.ToLower(); - var control = props.Control; - var ipid = control.IpIdInt; - var address = control.TcpSshProperties.Address; - - switch (type) - { - case ("hdmd4x14ke-bridgeable"): - return new HdMdNxM4kEBridgeableController(dc.Key, dc.Name, new HdMd4x14kE(ipid, address, Global.ControlSystem), props); - case ("hdmd4x24ke"): - return new HdMdNxM4kEBridgeableController(dc.Key, dc.Name, new HdMd4x24kE(ipid, address, Global.ControlSystem), props); - case ("hdmd6x24ke"): - return new HdMdNxM4kEBridgeableController(dc.Key, dc.Name, new HdMd6x24kE(ipid, address, Global.ControlSystem), props); - default: - return null; - } - } - } - - #endregion - - - - } + } } \ No newline at end of file diff --git a/essentials-framework/Essentials DM/Essentials_DM/Chassis/HdMdNxM4kEController.cs b/essentials-framework/Essentials DM/Essentials_DM/Chassis/HdMdNxM4kEController.cs index 57c52627..b47139e2 100644 --- a/essentials-framework/Essentials DM/Essentials_DM/Chassis/HdMdNxM4kEController.cs +++ b/essentials-framework/Essentials DM/Essentials_DM/Chassis/HdMdNxM4kEController.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using System.Text.RegularExpressions; using Crestron.SimplSharp; using Crestron.SimplSharpPro.DM; using Newtonsoft.Json; @@ -13,6 +14,7 @@ using PepperDash.Essentials.DM.Config; namespace PepperDash.Essentials.DM.Chassis { + [Obsolete("Please use HdMdNxM4kEBridgeable Controller")] public class HdMdNxM4kEController : CrestronGenericBaseDevice, IRoutingInputsOutputs, IRouting { public HdMdNxM Chassis { get; private set; } @@ -31,6 +33,7 @@ namespace PepperDash.Essentials.DM.Chassis HdMdNxM4kEPropertiesConfig props) : base(key, name, chassis) { + Debug.Console(0, this, "Type hdmd4x14ke is obsolete. Please use hdmd4x14ke-bridgeable"); Chassis = chassis; // logical ports @@ -43,14 +46,19 @@ namespace PepperDash.Essentials.DM.Chassis OutputPorts = new RoutingPortCollection(); OutputPorts.Add(new RoutingOutputPort(DmPortName.HdmiOut, eRoutingSignalType.Audio | eRoutingSignalType.Video, eRoutingPortConnectionType.Hdmi, null, this)); - + // physical settings if (props != null && props.Inputs != null) { + var inputRegex = new Regex(@"(?\d)", RegexOptions.IgnoreCase); foreach (var kvp in props.Inputs) { - // strip "hdmiIn" - var inputNum = Convert.ToUInt32(kvp.Key.Substring(6)); + // get numnbers from key and convert to int + //var inputNum = Convert.ToUInt32(kvp.Key.Substring(6)); + var inputMatch = inputRegex.Match(kvp.Key); + if (inputMatch == null) continue; + + var inputNum = Convert.ToUInt32(inputMatch.Groups["InputNum"].Value); var port = chassis.HdmiInputs[inputNum].HdmiInputPort; // set hdcp disables diff --git a/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Transmitters/DmTx4kz202CController.cs b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Transmitters/DmTx4kz202CController.cs index da1685cc..a241d60d 100644 --- a/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Transmitters/DmTx4kz202CController.cs +++ b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Transmitters/DmTx4kz202CController.cs @@ -1,40 +1,40 @@ -using Crestron.SimplSharpPro; -using System; -using System.Linq; -//using Crestron.SimplSharpPro.DeviceSupport; -using Crestron.SimplSharpPro.DeviceSupport; -using Crestron.SimplSharpPro.DM; -using Crestron.SimplSharpPro.DM.Endpoints; -using Crestron.SimplSharpPro.DM.Endpoints.Transmitters; - -using PepperDash.Core; -using PepperDash.Essentials.Core; -using PepperDash.Essentials.Core.Bridges; - -namespace PepperDash.Essentials.DM -{ - using eVst = eX02VideoSourceType; +using Crestron.SimplSharpPro; +using System; +using System.Linq; +//using Crestron.SimplSharpPro.DeviceSupport; +using Crestron.SimplSharpPro.DeviceSupport; +using Crestron.SimplSharpPro.DM; +using Crestron.SimplSharpPro.DM.Endpoints; +using Crestron.SimplSharpPro.DM.Endpoints.Transmitters; + +using PepperDash.Core; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.Bridges; + +namespace PepperDash.Essentials.DM +{ + using eVst = eX02VideoSourceType; using eAst = eX02AudioSourceType; - public class DmTx4kz202CController : DmTxControllerBase, ITxRoutingWithFeedback, - IIROutputPorts, IComPorts - { - public DmTx4kz202C Tx { get; private set; } - - public RoutingInputPortWithVideoStatuses HdmiIn1 { get; private set; } - public RoutingInputPortWithVideoStatuses HdmiIn2 { get; private set; } - public RoutingOutputPort DmOut { get; private set; } - public RoutingOutputPort HdmiLoopOut { get; private set; } - - public override StringFeedback ActiveVideoInputFeedback { get; protected set; } - public IntFeedback VideoSourceNumericFeedback { get; protected set; } - public IntFeedback AudioSourceNumericFeedback { get; protected set; } - public IntFeedback HdmiIn1HdcpCapabilityFeedback { get; protected set; } - public IntFeedback HdmiIn2HdcpCapabilityFeedback { get; protected set; } - public BoolFeedback Hdmi1VideoSyncFeedback { get; protected set; } - public BoolFeedback Hdmi2VideoSyncFeedback { get; protected set; } - - //public override IntFeedback HdcpSupportAllFeedback { get; protected set; } + public class DmTx4kz202CController : DmTxControllerBase, ITxRoutingWithFeedback, + IIROutputPorts, IComPorts + { + public DmTx4kz202C Tx { get; private set; } + + public RoutingInputPortWithVideoStatuses HdmiIn1 { get; private set; } + public RoutingInputPortWithVideoStatuses HdmiIn2 { get; private set; } + public RoutingOutputPort DmOut { get; private set; } + public RoutingOutputPort HdmiLoopOut { get; private set; } + + public override StringFeedback ActiveVideoInputFeedback { get; protected set; } + public IntFeedback VideoSourceNumericFeedback { get; protected set; } + public IntFeedback AudioSourceNumericFeedback { get; protected set; } + public IntFeedback HdmiIn1HdcpCapabilityFeedback { get; protected set; } + public IntFeedback HdmiIn2HdcpCapabilityFeedback { get; protected set; } + public BoolFeedback Hdmi1VideoSyncFeedback { get; protected set; } + public BoolFeedback Hdmi2VideoSyncFeedback { get; protected set; } + + //public override IntFeedback HdcpSupportAllFeedback { get; protected set; } //public override ushort HdcpSupportCapability { get; protected set; } //IroutingNumericEvent public event EventHandler NumericSwitchChange; @@ -47,48 +47,48 @@ namespace PepperDash.Essentials.DM { var newEvent = NumericSwitchChange; if (newEvent != null) newEvent(this, e); - } - - - - /// - /// Helps get the "real" inputs, including when in Auto - /// - public eX02VideoSourceType ActualActiveVideoInput - { - get - { - if (Tx.VideoSourceFeedback != eVst.Auto) - return Tx.VideoSourceFeedback; - - if (Tx.HdmiInputs[1].SyncDetectedFeedback.BoolValue) - return eVst.Hdmi1; - - return Tx.HdmiInputs[2].SyncDetectedFeedback.BoolValue ? eVst.Hdmi2 : eVst.AllDisabled; - } - } - public RoutingPortCollection InputPorts - { - get - { - return new RoutingPortCollection - { - HdmiIn1, - HdmiIn2, - AnyVideoInput - }; - } - } - public RoutingPortCollection OutputPorts - { - get - { - return new RoutingPortCollection { DmOut, HdmiLoopOut }; - } - } - public DmTx4kz202CController(string key, string name, DmTx4kz202C tx) - : base(key, name, tx) - { + } + + + + /// + /// Helps get the "real" inputs, including when in Auto + /// + public eX02VideoSourceType ActualActiveVideoInput + { + get + { + if (Tx.VideoSourceFeedback != eVst.Auto) + return Tx.VideoSourceFeedback; + + if (Tx.HdmiInputs[1].SyncDetectedFeedback.BoolValue) + return eVst.Hdmi1; + + return Tx.HdmiInputs[2].SyncDetectedFeedback.BoolValue ? eVst.Hdmi2 : eVst.AllDisabled; + } + } + public RoutingPortCollection InputPorts + { + get + { + return new RoutingPortCollection + { + HdmiIn1, + HdmiIn2, + AnyVideoInput + }; + } + } + public RoutingPortCollection OutputPorts + { + get + { + return new RoutingPortCollection { DmOut, HdmiLoopOut }; + } + } + public DmTx4kz202CController(string key, string name, DmTx4kz202C tx) + : base(key, name, tx) + { Tx = tx; HdmiIn1 = new RoutingInputPortWithVideoStatuses(DmPortName.HdmiIn1, @@ -104,208 +104,208 @@ namespace PepperDash.Essentials.DM FeedbackMatchObject = eVst.Hdmi2 }; - ActiveVideoInputFeedback = new StringFeedback("ActiveVideoInput", - () => ActualActiveVideoInput.ToString()); - - - - Tx.HdmiInputs[1].InputStreamChange += InputStreamChangeEvent; - Tx.HdmiInputs[2].InputStreamChange += InputStreamChangeEvent; - Tx.BaseEvent += Tx_BaseEvent; - Tx.OnlineStatusChange += Tx_OnlineStatusChange; - - VideoSourceNumericFeedback = new IntFeedback(() => (int)Tx.VideoSourceFeedback); - - //Return VideoSourceFeedback here as DM-TX-4KZ-202-C does not support audio breakaway - AudioSourceNumericFeedback = new IntFeedback(() => (int)Tx.VideoSourceFeedback); - - HdmiIn1HdcpCapabilityFeedback = new IntFeedback("HdmiIn1HdcpCapability", () => (int)tx.HdmiInputs[1].HdcpCapabilityFeedback); - - HdmiIn2HdcpCapabilityFeedback = new IntFeedback("HdmiIn2HdcpCapability", () => (int)tx.HdmiInputs[2].HdcpCapabilityFeedback); - - HdcpStateFeedback = - new IntFeedback( - () => - tx.HdmiInputs[1].HdcpCapabilityFeedback > tx.HdmiInputs[2].HdcpCapabilityFeedback - ? (int)tx.HdmiInputs[1].HdcpCapabilityFeedback - : (int)tx.HdmiInputs[2].HdcpCapabilityFeedback); - - HdcpSupportCapability = eHdcpCapabilityType.Hdcp2_2Support; - - Hdmi1VideoSyncFeedback = new BoolFeedback(() => (bool)tx.HdmiInputs[1].SyncDetectedFeedback.BoolValue); - - Hdmi2VideoSyncFeedback = new BoolFeedback(() => (bool)tx.HdmiInputs[2].SyncDetectedFeedback.BoolValue); - - var combinedFuncs = new VideoStatusFuncsWrapper - { - HdcpActiveFeedbackFunc = () => - (ActualActiveVideoInput == eVst.Hdmi1 - && tx.HdmiInputs[1].VideoAttributes.HdcpActiveFeedback.BoolValue) - || (ActualActiveVideoInput == eVst.Hdmi2 - && tx.HdmiInputs[2].VideoAttributes.HdcpActiveFeedback.BoolValue), - - HdcpStateFeedbackFunc = () => - { - if (ActualActiveVideoInput == eVst.Hdmi1) - return tx.HdmiInputs[1].VideoAttributes.HdcpStateFeedback.ToString(); - if (ActualActiveVideoInput == eVst.Hdmi2) - return tx.HdmiInputs[2].VideoAttributes.HdcpStateFeedback.ToString(); - return ""; - }, - - VideoResolutionFeedbackFunc = () => - { - if (ActualActiveVideoInput == eVst.Hdmi1) - return tx.HdmiInputs[1].VideoAttributes.GetVideoResolutionString(); - if (ActualActiveVideoInput == eVst.Hdmi2) - return tx.HdmiInputs[2].VideoAttributes.GetVideoResolutionString(); - return ""; - }, - VideoSyncFeedbackFunc = () => - (ActualActiveVideoInput == eVst.Hdmi1 - && tx.HdmiInputs[1].SyncDetectedFeedback.BoolValue) - || (ActualActiveVideoInput == eVst.Hdmi2 - && tx.HdmiInputs[2].SyncDetectedFeedback.BoolValue) - - }; - - AnyVideoInput = new RoutingInputPortWithVideoStatuses(DmPortName.AnyVideoIn, - eRoutingSignalType.Audio | eRoutingSignalType.Video, eRoutingPortConnectionType.None, 0, this, combinedFuncs); - - DmOut = new RoutingOutputPort(DmPortName.DmOut, eRoutingSignalType.Audio | eRoutingSignalType.Video, - eRoutingPortConnectionType.DmCat, null, this); - HdmiLoopOut = new RoutingOutputPort(DmPortName.HdmiLoopOut, eRoutingSignalType.Audio | eRoutingSignalType.Video, - eRoutingPortConnectionType.Hdmi, null, this); - - - AddToFeedbackList(ActiveVideoInputFeedback, VideoSourceNumericFeedback, AudioSourceNumericFeedback, - AnyVideoInput.VideoStatus.HasVideoStatusFeedback, AnyVideoInput.VideoStatus.HdcpActiveFeedback, - AnyVideoInput.VideoStatus.HdcpStateFeedback, AnyVideoInput.VideoStatus.VideoResolutionFeedback, - AnyVideoInput.VideoStatus.VideoSyncFeedback, HdmiIn1HdcpCapabilityFeedback, HdmiIn2HdcpCapabilityFeedback, - Hdmi1VideoSyncFeedback, Hdmi2VideoSyncFeedback); - - // Set Ports for CEC - HdmiIn1.Port = Tx.HdmiInputs[1]; - HdmiIn2.Port = Tx.HdmiInputs[2]; - HdmiLoopOut.Port = Tx.HdmiOutput; - DmOut.Port = Tx.DmOutput; - } - - - - public override bool CustomActivate() - { - // Link up all of these damned events to the various RoutingPorts via a helper handler - Tx.HdmiInputs[1].InputStreamChange += (o, a) => FowardInputStreamChange(HdmiIn1, a.EventId); - Tx.HdmiInputs[1].VideoAttributes.AttributeChange += (o, a) => ForwardVideoAttributeChange(HdmiIn1, a.EventId); - - Tx.HdmiInputs[2].InputStreamChange += (o, a) => FowardInputStreamChange(HdmiIn2, a.EventId); - Tx.HdmiInputs[2].VideoAttributes.AttributeChange += (o, a) => ForwardVideoAttributeChange(HdmiIn2, a.EventId); - - // Base does register and sets up comm monitoring. - return base.CustomActivate(); - } - - public override void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge) - { - var joinMap = GetDmTxJoinMap(joinStart, joinMapKey); - - if (Hdmi1VideoSyncFeedback != null) - { - Hdmi1VideoSyncFeedback.LinkInputSig(trilist.BooleanInput[joinMap.Input1VideoSyncStatus.JoinNumber]); - } - if (Hdmi2VideoSyncFeedback != null) - { - Hdmi2VideoSyncFeedback.LinkInputSig(trilist.BooleanInput[joinMap.Input2VideoSyncStatus.JoinNumber]); - } - - LinkDmTxToApi(this, trilist, joinMap, bridge); - } - - public void ExecuteNumericSwitch(ushort input, ushort output, eRoutingSignalType type) - { - Debug.Console(2, this, "Executing Numeric Switch to input {0}.", input); - - switch (type) - { - case eRoutingSignalType.Video: - switch (input) - { - case 0: - { - ExecuteSwitch(eVst.Auto, null, type); - break; - } - case 1: - { - ExecuteSwitch(HdmiIn1.Selector, null, type); - break; - } - case 2: - { - ExecuteSwitch(HdmiIn2.Selector, null, type); - break; - } - case 3: - { - ExecuteSwitch(eVst.AllDisabled, null, type); - break; - } - } - break; - case eRoutingSignalType.Audio: - switch (input) - { - case 0: - { - ExecuteSwitch(eAst.Auto, null, type); - break; - } - case 1: - { - ExecuteSwitch(eAst.Hdmi1, null, type); - break; - } - case 2: - { - ExecuteSwitch(eAst.Hdmi2, null, type); - break; - } - case 3: - { - ExecuteSwitch(eAst.AllDisabled, null, type); - break; - } - } - break; - } - } - - public void ExecuteSwitch(object inputSelector, object outputSelector, eRoutingSignalType signalType) - { - if ((signalType & eRoutingSignalType.Video) == eRoutingSignalType.Video) - Tx.VideoSource = (eVst)inputSelector; - if(((signalType & eRoutingSignalType.Audio) == eRoutingSignalType.Audio)) - Debug.Console(2, this, "Unable to execute audio-only switch for tx {0}", Key); - } - - void InputStreamChangeEvent(EndpointInputStream inputStream, EndpointInputStreamEventArgs args) - { - Debug.Console(2, "{0} event {1} stream {2}", Tx.ToString(), inputStream.ToString(), args.EventId.ToString()); - - switch (args.EventId) - { - case EndpointInputStreamEventIds.HdcpSupportOffFeedbackEventId: - case EndpointInputStreamEventIds.HdcpSupportOnFeedbackEventId: - case EndpointInputStreamEventIds.HdcpCapabilityFeedbackEventId: - if (inputStream == Tx.HdmiInputs[1]) HdmiIn1HdcpCapabilityFeedback.FireUpdate(); - if (inputStream == Tx.HdmiInputs[2]) HdmiIn2HdcpCapabilityFeedback.FireUpdate(); - break; - case EndpointInputStreamEventIds.SyncDetectedFeedbackEventId: - if (inputStream == Tx.HdmiInputs[1]) Hdmi1VideoSyncFeedback.FireUpdate(); - if (inputStream == Tx.HdmiInputs[2]) Hdmi2VideoSyncFeedback.FireUpdate(); - break; - } + ActiveVideoInputFeedback = new StringFeedback("ActiveVideoInput", + () => ActualActiveVideoInput.ToString()); + + + + Tx.HdmiInputs[1].InputStreamChange += InputStreamChangeEvent; + Tx.HdmiInputs[2].InputStreamChange += InputStreamChangeEvent; + Tx.BaseEvent += Tx_BaseEvent; + Tx.OnlineStatusChange += Tx_OnlineStatusChange; + + VideoSourceNumericFeedback = new IntFeedback(() => (int)Tx.VideoSourceFeedback); + + //Return VideoSourceFeedback here as DM-TX-4KZ-202-C does not support audio breakaway + AudioSourceNumericFeedback = new IntFeedback(() => (int)Tx.VideoSourceFeedback); + + HdmiIn1HdcpCapabilityFeedback = new IntFeedback("HdmiIn1HdcpCapability", () => (int)tx.HdmiInputs[1].HdcpCapabilityFeedback); + + HdmiIn2HdcpCapabilityFeedback = new IntFeedback("HdmiIn2HdcpCapability", () => (int)tx.HdmiInputs[2].HdcpCapabilityFeedback); + + HdcpStateFeedback = + new IntFeedback( + () => + tx.HdmiInputs[1].HdcpCapabilityFeedback > tx.HdmiInputs[2].HdcpCapabilityFeedback + ? (int)tx.HdmiInputs[1].HdcpCapabilityFeedback + : (int)tx.HdmiInputs[2].HdcpCapabilityFeedback); + + HdcpSupportCapability = eHdcpCapabilityType.Hdcp2_2Support; + + Hdmi1VideoSyncFeedback = new BoolFeedback(() => (bool)tx.HdmiInputs[1].SyncDetectedFeedback.BoolValue); + + Hdmi2VideoSyncFeedback = new BoolFeedback(() => (bool)tx.HdmiInputs[2].SyncDetectedFeedback.BoolValue); + + var combinedFuncs = new VideoStatusFuncsWrapper + { + HdcpActiveFeedbackFunc = () => + (ActualActiveVideoInput == eVst.Hdmi1 + && tx.HdmiInputs[1].VideoAttributes.HdcpActiveFeedback.BoolValue) + || (ActualActiveVideoInput == eVst.Hdmi2 + && tx.HdmiInputs[2].VideoAttributes.HdcpActiveFeedback.BoolValue), + + HdcpStateFeedbackFunc = () => + { + if (ActualActiveVideoInput == eVst.Hdmi1) + return tx.HdmiInputs[1].VideoAttributes.HdcpStateFeedback.ToString(); + if (ActualActiveVideoInput == eVst.Hdmi2) + return tx.HdmiInputs[2].VideoAttributes.HdcpStateFeedback.ToString(); + return ""; + }, + + VideoResolutionFeedbackFunc = () => + { + if (ActualActiveVideoInput == eVst.Hdmi1) + return tx.HdmiInputs[1].VideoAttributes.GetVideoResolutionString(); + if (ActualActiveVideoInput == eVst.Hdmi2) + return tx.HdmiInputs[2].VideoAttributes.GetVideoResolutionString(); + return ""; + }, + VideoSyncFeedbackFunc = () => + (ActualActiveVideoInput == eVst.Hdmi1 + && tx.HdmiInputs[1].SyncDetectedFeedback.BoolValue) + || (ActualActiveVideoInput == eVst.Hdmi2 + && tx.HdmiInputs[2].SyncDetectedFeedback.BoolValue) + + }; + + AnyVideoInput = new RoutingInputPortWithVideoStatuses(DmPortName.AnyVideoIn, + eRoutingSignalType.Audio | eRoutingSignalType.Video, eRoutingPortConnectionType.None, 0, this, combinedFuncs); + + DmOut = new RoutingOutputPort(DmPortName.DmOut, eRoutingSignalType.Audio | eRoutingSignalType.Video, + eRoutingPortConnectionType.DmCat, null, this); + HdmiLoopOut = new RoutingOutputPort(DmPortName.HdmiLoopOut, eRoutingSignalType.Audio | eRoutingSignalType.Video, + eRoutingPortConnectionType.Hdmi, null, this); + + + AddToFeedbackList(ActiveVideoInputFeedback, VideoSourceNumericFeedback, AudioSourceNumericFeedback, + AnyVideoInput.VideoStatus.HasVideoStatusFeedback, AnyVideoInput.VideoStatus.HdcpActiveFeedback, + AnyVideoInput.VideoStatus.HdcpStateFeedback, AnyVideoInput.VideoStatus.VideoResolutionFeedback, + AnyVideoInput.VideoStatus.VideoSyncFeedback, HdmiIn1HdcpCapabilityFeedback, HdmiIn2HdcpCapabilityFeedback, + Hdmi1VideoSyncFeedback, Hdmi2VideoSyncFeedback); + + // Set Ports for CEC + HdmiIn1.Port = Tx.HdmiInputs[1]; + HdmiIn2.Port = Tx.HdmiInputs[2]; + HdmiLoopOut.Port = Tx.HdmiOutput; + DmOut.Port = Tx.DmOutput; + } + + + + public override bool CustomActivate() + { + // Link up all of these damned events to the various RoutingPorts via a helper handler + Tx.HdmiInputs[1].InputStreamChange += (o, a) => FowardInputStreamChange(HdmiIn1, a.EventId); + Tx.HdmiInputs[1].VideoAttributes.AttributeChange += (o, a) => ForwardVideoAttributeChange(HdmiIn1, a.EventId); + + Tx.HdmiInputs[2].InputStreamChange += (o, a) => FowardInputStreamChange(HdmiIn2, a.EventId); + Tx.HdmiInputs[2].VideoAttributes.AttributeChange += (o, a) => ForwardVideoAttributeChange(HdmiIn2, a.EventId); + + // Base does register and sets up comm monitoring. + return base.CustomActivate(); + } + + public override void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge) + { + var joinMap = GetDmTxJoinMap(joinStart, joinMapKey); + + if (Hdmi1VideoSyncFeedback != null) + { + Hdmi1VideoSyncFeedback.LinkInputSig(trilist.BooleanInput[joinMap.Input1VideoSyncStatus.JoinNumber]); + } + if (Hdmi2VideoSyncFeedback != null) + { + Hdmi2VideoSyncFeedback.LinkInputSig(trilist.BooleanInput[joinMap.Input2VideoSyncStatus.JoinNumber]); + } + + LinkDmTxToApi(this, trilist, joinMap, bridge); + } + + public void ExecuteNumericSwitch(ushort input, ushort output, eRoutingSignalType type) + { + Debug.Console(2, this, "Executing Numeric Switch to input {0}.", input); + + switch (type) + { + case eRoutingSignalType.Video: + switch (input) + { + case 0: + { + ExecuteSwitch(eVst.Auto, null, type); + break; + } + case 1: + { + ExecuteSwitch(HdmiIn1.Selector, null, type); + break; + } + case 2: + { + ExecuteSwitch(HdmiIn2.Selector, null, type); + break; + } + case 3: + { + ExecuteSwitch(eVst.AllDisabled, null, type); + break; + } + } + break; + case eRoutingSignalType.Audio: + switch (input) + { + case 0: + { + ExecuteSwitch(eAst.Auto, null, type); + break; + } + case 1: + { + ExecuteSwitch(eAst.Hdmi1, null, type); + break; + } + case 2: + { + ExecuteSwitch(eAst.Hdmi2, null, type); + break; + } + case 3: + { + ExecuteSwitch(eAst.AllDisabled, null, type); + break; + } + } + break; + } + } + + public void ExecuteSwitch(object inputSelector, object outputSelector, eRoutingSignalType signalType) + { + if ((signalType & eRoutingSignalType.Video) == eRoutingSignalType.Video) + Tx.VideoSource = (eVst)inputSelector; + if(((signalType & eRoutingSignalType.Audio) == eRoutingSignalType.Audio)) + Debug.Console(2, this, "Unable to execute audio-only switch for tx {0}", Key); + } + + void InputStreamChangeEvent(EndpointInputStream inputStream, EndpointInputStreamEventArgs args) + { + Debug.Console(2, "{0} event {1} stream {2}", Tx.ToString(), inputStream.ToString(), args.EventId.ToString()); + + switch (args.EventId) + { + case EndpointInputStreamEventIds.HdcpSupportOffFeedbackEventId: + case EndpointInputStreamEventIds.HdcpSupportOnFeedbackEventId: + case EndpointInputStreamEventIds.HdcpCapabilityFeedbackEventId: + if (inputStream == Tx.HdmiInputs[1]) HdmiIn1HdcpCapabilityFeedback.FireUpdate(); + if (inputStream == Tx.HdmiInputs[2]) HdmiIn2HdcpCapabilityFeedback.FireUpdate(); + break; + case EndpointInputStreamEventIds.SyncDetectedFeedbackEventId: + if (inputStream == Tx.HdmiInputs[1]) Hdmi1VideoSyncFeedback.FireUpdate(); + if (inputStream == Tx.HdmiInputs[2]) Hdmi2VideoSyncFeedback.FireUpdate(); + break; + } } void Tx_OnlineStatusChange(GenericBase currentDevice, OnlineOfflineEventArgs args) @@ -313,7 +313,7 @@ namespace PepperDash.Essentials.DM var localVideoInputPort = InputPorts.FirstOrDefault(p => (eVst)p.Selector == Tx.VideoSourceFeedback); var localAudioInputPort = - InputPorts.FirstOrDefault(p => (eAst)p.Selector == Tx.AudioSourceFeedback); + InputPorts.FirstOrDefault(p => (eVst)p.Selector == Tx.VideoSourceFeedback); ActiveVideoInputFeedback.FireUpdate(); VideoSourceNumericFeedback.FireUpdate(); @@ -338,68 +338,68 @@ namespace PepperDash.Essentials.DM OnSwitchChange(new RoutingNumericEventArgs(1, VideoSourceNumericFeedback.UShortValue, OutputPorts.First(), localVideoInputPort, eRoutingSignalType.Video)); break; case EndpointTransmitterBase.AudioSourceFeedbackEventId: - var localInputAudioPort = InputPorts.FirstOrDefault(p => (eAst)p.Selector == Tx.AudioSourceFeedback); - Debug.Console(2, this, " Audio Source: {0}", Tx.AudioSourceFeedback); + var localInputAudioPort = InputPorts.FirstOrDefault(p => (eVst)p.Selector == Tx.VideoSourceFeedback); + Debug.Console(2, this, " Audio Source: {0}", Tx.VideoSourceFeedback); AudioSourceNumericFeedback.FireUpdate(); OnSwitchChange(new RoutingNumericEventArgs(1, AudioSourceNumericFeedback.UShortValue, OutputPorts.First(), localInputAudioPort, eRoutingSignalType.Audio)); break; } - } - - /// - /// Relays the input stream change to the appropriate RoutingInputPort. - /// - void FowardInputStreamChange(RoutingInputPortWithVideoStatuses inputPort, int eventId) - { - if (eventId != EndpointInputStreamEventIds.SyncDetectedFeedbackEventId) - { - return; - } - inputPort.VideoStatus.VideoSyncFeedback.FireUpdate(); - AnyVideoInput.VideoStatus.VideoSyncFeedback.FireUpdate(); - } - - /// - /// Relays the VideoAttributes change to a RoutingInputPort - /// - void ForwardVideoAttributeChange(RoutingInputPortWithVideoStatuses inputPort, int eventId) - { - //// LOCATION: Crestron.SimplSharpPro.DM.VideoAttributeEventIds - //Debug.Console(2, this, "VideoAttributes_AttributeChange event id={0} from {1}", - // args.EventId, (sender as VideoAttributesEnhanced).Owner.GetType()); - switch (eventId) - { - case VideoAttributeEventIds.HdcpActiveFeedbackEventId: - inputPort.VideoStatus.HdcpActiveFeedback.FireUpdate(); - AnyVideoInput.VideoStatus.HdcpActiveFeedback.FireUpdate(); - break; - case VideoAttributeEventIds.HdcpStateFeedbackEventId: - inputPort.VideoStatus.HdcpStateFeedback.FireUpdate(); - AnyVideoInput.VideoStatus.HdcpStateFeedback.FireUpdate(); - break; - case VideoAttributeEventIds.HorizontalResolutionFeedbackEventId: - case VideoAttributeEventIds.VerticalResolutionFeedbackEventId: - inputPort.VideoStatus.VideoResolutionFeedback.FireUpdate(); - AnyVideoInput.VideoStatus.VideoResolutionFeedback.FireUpdate(); - break; - case VideoAttributeEventIds.FramesPerSecondFeedbackEventId: - inputPort.VideoStatus.VideoResolutionFeedback.FireUpdate(); - AnyVideoInput.VideoStatus.VideoResolutionFeedback.FireUpdate(); - break; - } - } - - - - - #region IIROutputPorts Members - public CrestronCollection IROutputPorts { get { return Tx.IROutputPorts; } } - public int NumberOfIROutputPorts { get { return Tx.NumberOfIROutputPorts; } } - #endregion - - #region IComPorts Members - public CrestronCollection ComPorts { get { return Tx.ComPorts; } } - public int NumberOfComPorts { get { return Tx.NumberOfComPorts; } } - #endregion - } + } + + /// + /// Relays the input stream change to the appropriate RoutingInputPort. + /// + void FowardInputStreamChange(RoutingInputPortWithVideoStatuses inputPort, int eventId) + { + if (eventId != EndpointInputStreamEventIds.SyncDetectedFeedbackEventId) + { + return; + } + inputPort.VideoStatus.VideoSyncFeedback.FireUpdate(); + AnyVideoInput.VideoStatus.VideoSyncFeedback.FireUpdate(); + } + + /// + /// Relays the VideoAttributes change to a RoutingInputPort + /// + void ForwardVideoAttributeChange(RoutingInputPortWithVideoStatuses inputPort, int eventId) + { + //// LOCATION: Crestron.SimplSharpPro.DM.VideoAttributeEventIds + //Debug.Console(2, this, "VideoAttributes_AttributeChange event id={0} from {1}", + // args.EventId, (sender as VideoAttributesEnhanced).Owner.GetType()); + switch (eventId) + { + case VideoAttributeEventIds.HdcpActiveFeedbackEventId: + inputPort.VideoStatus.HdcpActiveFeedback.FireUpdate(); + AnyVideoInput.VideoStatus.HdcpActiveFeedback.FireUpdate(); + break; + case VideoAttributeEventIds.HdcpStateFeedbackEventId: + inputPort.VideoStatus.HdcpStateFeedback.FireUpdate(); + AnyVideoInput.VideoStatus.HdcpStateFeedback.FireUpdate(); + break; + case VideoAttributeEventIds.HorizontalResolutionFeedbackEventId: + case VideoAttributeEventIds.VerticalResolutionFeedbackEventId: + inputPort.VideoStatus.VideoResolutionFeedback.FireUpdate(); + AnyVideoInput.VideoStatus.VideoResolutionFeedback.FireUpdate(); + break; + case VideoAttributeEventIds.FramesPerSecondFeedbackEventId: + inputPort.VideoStatus.VideoResolutionFeedback.FireUpdate(); + AnyVideoInput.VideoStatus.VideoResolutionFeedback.FireUpdate(); + break; + } + } + + + + + #region IIROutputPorts Members + public CrestronCollection IROutputPorts { get { return Tx.IROutputPorts; } } + public int NumberOfIROutputPorts { get { return Tx.NumberOfIROutputPorts; } } + #endregion + + #region IComPorts Members + public CrestronCollection ComPorts { get { return Tx.ComPorts; } } + public int NumberOfComPorts { get { return Tx.NumberOfComPorts; } } + #endregion + } } \ No newline at end of file diff --git a/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Transmitters/DmTx4kz302CController.cs b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Transmitters/DmTx4kz302CController.cs index fb8f3ac5..70df8259 100644 --- a/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Transmitters/DmTx4kz302CController.cs +++ b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Transmitters/DmTx4kz302CController.cs @@ -339,7 +339,7 @@ namespace PepperDash.Essentials.DM var localVideoInputPort = InputPorts.FirstOrDefault(p => (eVst)p.Selector == Tx.VideoSourceFeedback); var localAudioInputPort = - InputPorts.FirstOrDefault(p => (eAst)p.Selector == Tx.AudioSourceFeedback); + InputPorts.FirstOrDefault(p => (eVst)p.Selector == Tx.VideoSourceFeedback); ActiveVideoInputFeedback.FireUpdate(); VideoSourceNumericFeedback.FireUpdate(); @@ -364,8 +364,8 @@ namespace PepperDash.Essentials.DM OnSwitchChange(new RoutingNumericEventArgs(1, VideoSourceNumericFeedback.UShortValue, OutputPorts.First(), localVideoInputPort, eRoutingSignalType.Video)); break; case EndpointTransmitterBase.AudioSourceFeedbackEventId: - var localInputAudioPort = InputPorts.FirstOrDefault(p => (eAst)p.Selector == Tx.AudioSourceFeedback); - Debug.Console(2, this, " Audio Source: {0}", Tx.AudioSourceFeedback); + var localInputAudioPort = InputPorts.FirstOrDefault(p => (eVst)p.Selector == Tx.VideoSourceFeedback); + Debug.Console(2, this, " Audio Source: {0}", Tx.VideoSourceFeedback); AudioSourceNumericFeedback.FireUpdate(); OnSwitchChange(new RoutingNumericEventArgs(1, AudioSourceNumericFeedback.UShortValue, OutputPorts.First(), localInputAudioPort, eRoutingSignalType.Audio)); break; diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/AudioCodec/Interfaces/IHasAudioCodec.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/AudioCodec/Interfaces/IHasAudioCodec.cs index e4cb5ee4..b6fa079d 100644 --- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/AudioCodec/Interfaces/IHasAudioCodec.cs +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/AudioCodec/Interfaces/IHasAudioCodec.cs @@ -11,10 +11,9 @@ namespace PepperDash.Essentials.Devices.Common.AudioCodec /// /// For rooms that have audio codec /// - public interface IHasAudioCodec + public interface IHasAudioCodec:IHasInCallFeedback { AudioCodecBase AudioCodec { get; } - BoolFeedback InCallFeedback { get; } ///// ///// Make this more specific diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/IHasDoNotDisturb.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/IHasDoNotDisturb.cs new file mode 100644 index 00000000..7bb9b924 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/IHasDoNotDisturb.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using PepperDash.Essentials.Core; + +namespace PepperDash.Essentials.Devices.Common.Codec +{ + /// + /// Describes a device that has Do Not Disturb mode capability + /// + public interface IHasDoNotDisturbMode + { + /// + /// Indictes whether Do Not Disturb mode is on (Activated) + /// + BoolFeedback DoNotDisturbModeIsOnFeedback { get; } + + /// + /// Activates Do Not Disturb mode + /// + void ActivateDoNotDisturbMode(); + + /// + /// Deactivates Do Not Disturb mode + /// + void DeactivateDoNotDisturbMode(); + + /// + /// Toggles Do Not Disturb mode + /// + void ToggleDoNotDisturbMode(); + } + + public interface IHasDoNotDisturbModeWithTimeout : IHasDoNotDisturbMode + { + /// + /// Activates Do Not Disturb mode with a timeout + /// + /// + void ActivateDoNotDisturbMode(int timeout); + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/iHasDirectory.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/iHasDirectory.cs index 29a46466..b2ccb29c 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 @@ -160,7 +160,19 @@ namespace PepperDash.Essentials.Devices.Common.Codec /// public interface IInvitableContact { + bool IsInvitableContact { get; } + } + public class InvitableDirectoryContact : DirectoryContact, IInvitableContact + { + [JsonProperty("isInvitableContact")] + public bool IsInvitableContact + { + get + { + return this is IInvitableContact; + } + } } /// @@ -209,8 +221,6 @@ 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 24f1968b..06edf7bf 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 @@ -235,6 +235,15 @@ namespace PepperDash.Essentials.Devices.Common.Codec { Calls = new List(); } + + #region Overrides of Object + + public override string ToString() + { + return String.Format("{0}:{1}: {2}-{3}", Title, Agenda, StartTime, EndTime); + } + + #endregion } public class Call 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 21821d22..1fcffff2 100644 --- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Essentials Devices Common.csproj +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Essentials Devices Common.csproj @@ -108,6 +108,7 @@ + @@ -117,12 +118,17 @@ + + + + + diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoCodecJoinMap.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoCodecJoinMap.cs new file mode 100644 index 00000000..8caeaf3e --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoCodecJoinMap.cs @@ -0,0 +1,120 @@ +using System; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.Bridges.JoinMaps; + + +namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco +{ + public class CiscoCodecJoinMap : VideoCodecControllerJoinMap + { + #region Digital + + [JoinName("ActivateDoNotDisturbMode")] + public JoinDataComplete ActivateDoNotDisturbMode = new JoinDataComplete( + new JoinData + { + JoinNumber = 221, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Activates Do Not Disturb Mode. FB High if active.", + JoinCapabilities = eJoinCapabilities.ToFromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("DeactivateDoNotDisturbMode")] + public JoinDataComplete DeactivateDoNotDisturbMode = new JoinDataComplete( + new JoinData + { + JoinNumber = 222, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Deactivates Do Not Disturb Mode. FB High if deactivated.", + JoinCapabilities = eJoinCapabilities.ToFromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("ToggleDoNotDisturbMode")] + public JoinDataComplete ToggleDoNotDisturbMode = new JoinDataComplete( + new JoinData + { + JoinNumber = 223, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Toggles Do Not Disturb Mode.", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("ActivateStandby")] + public JoinDataComplete ActivateStandby = new JoinDataComplete( + new JoinData + { + JoinNumber = 226, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Activates Standby Mode. FB High if active.", + JoinCapabilities = eJoinCapabilities.ToFromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("DeactivateStandby")] + public JoinDataComplete DeactivateStandby = new JoinDataComplete( + new JoinData + { + JoinNumber = 227, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Deactivates Standby Mode. FB High if deactivated.", + JoinCapabilities = eJoinCapabilities.ToFromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("ActivateHalfWakeMode")] + public JoinDataComplete ActivateHalfWakeMode = new JoinDataComplete( + new JoinData + { + JoinNumber = 228, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Activates Half Wake Mode. FB High if active.", + JoinCapabilities = eJoinCapabilities.ToFromSIMPL, + JoinType = eJoinType.Digital + }); + + #endregion + + + #region Analog + + + #endregion + + + #region Serials + + + #endregion + + public CiscoCodecJoinMap(uint joinStart) + : base(joinStart, typeof(CiscoCodecJoinMap)) + { + } + + public CiscoCodecJoinMap(uint joinStart, Type type) + : base(joinStart, type) + { + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoSparkCodec.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoSparkCodec.cs index 893d9b0d..d54a0b2e 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 @@ -28,7 +28,9 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco public class CiscoSparkCodec : VideoCodecBase, IHasCallHistory, IHasCallFavorites, IHasDirectoryHistoryStack, IHasScheduleAwareness, IOccupancyStatusProvider, IHasCodecLayouts, IHasCodecSelfView, - ICommunicationMonitor, IRouting, IHasCodecCameras, IHasCameraAutoMode, IHasCodecRoomPresets, IHasExternalSourceSwitching, IHasBranding, IHasCameraOff, IHasCameraMute + ICommunicationMonitor, IRouting, IHasCodecCameras, IHasCameraAutoMode, IHasCodecRoomPresets, + IHasExternalSourceSwitching, IHasBranding, IHasCameraOff, IHasCameraMute, IHasDoNotDisturbMode, + IHasHalfWakeMode { private bool _externalSourceChangeRequested; @@ -326,6 +328,9 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco CameraIsMutedFeedback = CameraIsOffFeedback; SupportsCameraOff = true; + DoNotDisturbModeIsOnFeedback = new BoolFeedback(() => CodecStatus.Status.Conference.DoNotDisturb.BoolValue); + HalfWakeModeIsOnFeedback = new BoolFeedback(() => CodecStatus.Status.Standby.State.Value == "Halfwake"); + PresentationViewMaximizedFeedback = new BoolFeedback(() => CurrentPresentationView == "Maximized"); Communication = comm; @@ -422,7 +427,11 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco CodecStatus.Status.Audio.Volume.ValueChangedAction = VolumeLevelFeedback.FireUpdate; CodecStatus.Status.Audio.VolumeMute.ValueChangedAction = MuteFeedback.FireUpdate; CodecStatus.Status.Audio.Microphones.Mute.ValueChangedAction = PrivacyModeIsOnFeedback.FireUpdate; - CodecStatus.Status.Standby.State.ValueChangedAction = StandbyIsOnFeedback.FireUpdate; + CodecStatus.Status.Standby.State.ValueChangedAction = new Action(() => + { + StandbyIsOnFeedback.FireUpdate(); + HalfWakeModeIsOnFeedback.FireUpdate(); + }); CodecStatus.Status.RoomAnalytics.PeoplePresence.ValueChangedAction = RoomIsOccupiedFeedback.FireUpdate; CodecStatus.Status.RoomAnalytics.PeopleCount.Current.ValueChangedAction = PeopleCountFeedback.FireUpdate; CodecStatus.Status.Cameras.SpeakerTrack.Status.ValueChangedAction = CameraAutoModeIsOnFeedback.FireUpdate; @@ -432,6 +441,8 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco CodecStatus.Status.Video.Layout.LayoutFamily.Local.ValueChangedAction = ComputeLocalLayout; CodecStatus.Status.Conference.Presentation.Mode.ValueChangedAction = SharingContentIsOnFeedback.FireUpdate; CodecStatus.Status.Conference.Presentation.Mode.ValueChangedAction = FarEndIsSharingContentFeedback.FireUpdate; + CodecStatus.Status.Conference.DoNotDisturb.ValueChangedAction = DoNotDisturbModeIsOnFeedback.FireUpdate; + try { CodecStatus.Status.Video.Input.MainVideoMute.ValueChangedAction = CameraIsOffFeedback.FireUpdate; @@ -584,6 +595,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco prefix + "/Status/Audio" + Delimiter + prefix + "/Status/Call" + Delimiter + prefix + "/Status/Conference/Presentation" + Delimiter + + prefix + "/Status/Conference/DoNotDisturb" + Delimiter + prefix + "/Status/Cameras/SpeakerTrack" + Delimiter + prefix + "/Status/RoomAnalytics" + Delimiter + prefix + "/Status/RoomPreset" + Delimiter + @@ -592,10 +604,11 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco prefix + "/Status/Video/Layout" + Delimiter + prefix + "/Status/Video/Input/MainVideoMute" + Delimiter + prefix + "/Bookings" + Delimiter + - prefix + "/Event/CallDisconnect" + Delimiter + prefix + "/Event/Bookings" + Delimiter + prefix + "/Event/CameraPresetListUpdated" + Delimiter + - prefix + "/Event/UserInterface/Presentation/ExternalSource/Selected/SourceIdentifier" + Delimiter; + prefix + "/Event/UserInterface/Presentation/ExternalSource/Selected/SourceIdentifier" + Delimiter + + prefix + "/Event/CallDisconnect" + Delimiter; // Keep CallDisconnect last to detect when feedback registration completes correctly + } #endregion @@ -752,6 +765,10 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco } + /// + /// Appends the delimiter and send the command to the codec + /// + /// public void SendText(string command) { if (CommDebuggingIsOn) @@ -1490,7 +1507,49 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco public override void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge) { + var joinMap = new CiscoCodecJoinMap(joinStart); + + var customJoins = JoinMapHelper.TryGetJoinMapAdvancedForDevice(joinMapKey); + + if (customJoins != null) + { + joinMap.SetCustomJoinData(customJoins); + } + + if (bridge != null) + { + bridge.AddJoinMap(Key, joinMap); + } + LinkVideoCodecToApi(this, trilist, joinStart, joinMapKey, bridge); + + LinkCiscoCodecToApi(trilist, joinMap); + } + + public void LinkCiscoCodecToApi(BasicTriList trilist, CiscoCodecJoinMap joinMap) + { + var dndCodec = this as IHasDoNotDisturbMode; + if (dndCodec != null) + { + dndCodec.DoNotDisturbModeIsOnFeedback.LinkInputSig(trilist.BooleanInput[joinMap.ActivateDoNotDisturbMode.JoinNumber]); + dndCodec.DoNotDisturbModeIsOnFeedback.LinkComplementInputSig(trilist.BooleanInput[joinMap.DeactivateDoNotDisturbMode.JoinNumber]); + + trilist.SetSigFalseAction(joinMap.ActivateDoNotDisturbMode.JoinNumber, () => dndCodec.ActivateDoNotDisturbMode()); + trilist.SetSigFalseAction(joinMap.DeactivateDoNotDisturbMode.JoinNumber, () => dndCodec.DeactivateDoNotDisturbMode()); + trilist.SetSigFalseAction(joinMap.ToggleDoNotDisturbMode.JoinNumber, () => dndCodec.ToggleDoNotDisturbMode()); + } + + var halfwakeCodec = this as IHasHalfWakeMode; + if (halfwakeCodec != null) + { + halfwakeCodec.StandbyIsOnFeedback.LinkInputSig(trilist.BooleanInput[joinMap.ActivateStandby.JoinNumber]); + halfwakeCodec.StandbyIsOnFeedback.LinkComplementInputSig(trilist.BooleanInput[joinMap.DeactivateStandby.JoinNumber]); + halfwakeCodec.HalfWakeModeIsOnFeedback.LinkInputSig(trilist.BooleanInput[joinMap.ActivateHalfWakeMode.JoinNumber]); + + trilist.SetSigFalseAction(joinMap.ActivateStandby.JoinNumber, () => halfwakeCodec.StandbyActivate()); + trilist.SetSigFalseAction(joinMap.DeactivateStandby.JoinNumber, () => halfwakeCodec.StandbyDeactivate()); + trilist.SetSigFalseAction(joinMap.ActivateHalfWakeMode.JoinNumber, () => halfwakeCodec.HalfwakeActivate()); + } } /// @@ -2074,6 +2133,45 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco else CameraMuteOn(); } + + #region IHasDoNotDisturbMode Members + + public BoolFeedback DoNotDisturbModeIsOnFeedback { get; private set; } + + public void ActivateDoNotDisturbMode() + { + SendText("xCommand Conference DoNotDisturb Activate"); + } + + public void DeactivateDoNotDisturbMode() + { + SendText("xCommand Conference DoNotDisturb Deactivate"); + } + + public void ToggleDoNotDisturbMode() + { + if (DoNotDisturbModeIsOnFeedback.BoolValue) + { + DeactivateDoNotDisturbMode(); + } + else + { + ActivateDoNotDisturbMode(); + } + } + + #endregion + + #region IHasHalfWakeMode Members + + public BoolFeedback HalfWakeModeIsOnFeedback { get; private set; } + + public void HalfwakeActivate() + { + SendText("xCommand Standby Halfwake"); + } + + #endregion } diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/xStatus.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/xStatus.cs index c1cd92cf..29b9f260 100644 --- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/xStatus.cs +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/xStatus.cs @@ -440,9 +440,26 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco public CallId CallId { get; set; } } - public class DoNotDisturb + public class DoNotDisturb : ValueProperty { - public string Value { get; set; } + string _Value; + + public bool BoolValue { get; private set; } + + public string Value + { + get + { + return _Value; + } + set + { + _Value = value; + // If the incoming value is "On" it sets the BoolValue true, otherwise sets it false + BoolValue = value == "On" || value == "Active"; + OnValueChanged(); + } + } } public class Mode @@ -600,6 +617,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco public Conference2() { Presentation = new Presentation(); + DoNotDisturb = new DoNotDisturb(); } } @@ -1380,12 +1398,16 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco public class State : ValueProperty { + string _value; + public bool BoolValue { get; private set; } public string Value // Valid values are Standby/EnteringStandby/Halfwake/Off { + get { return _value; } set { + _value = value; // If the incoming value is "On" it sets the BoolValue true, otherwise sets it false BoolValue = value == "On" || value == "Standby"; OnValueChanged(); @@ -2091,6 +2113,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco Conference = new Conference2(); SystemUnit = new SystemUnit(); Video = new Video(); + Conference = new Conference2(); } } diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasMeetingInfo.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasMeetingInfo.cs new file mode 100644 index 00000000..0720b319 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasMeetingInfo.cs @@ -0,0 +1,67 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using Newtonsoft.Json; + +namespace PepperDash.Essentials.Devices.Common.VideoCodec.Interfaces +{ + /// + /// Describes a device that provides meeting information (like a ZoomRoom) + /// + public interface IHasMeetingInfo + { + event EventHandler MeetingInfoChanged; + + MeetingInfo MeetingInfo { get; } + } + + /// + /// Represents the information about a meeting in progress + /// Currently used for Zoom meetings + /// + public class MeetingInfo + { + [JsonProperty("id")] + public string Id { get; private set; } + [JsonProperty("name")] + public string Name { get; private set; } + [JsonProperty("host")] + public string Host { get; private set; } + [JsonProperty("password")] + public string Password { get; private set; } + [JsonProperty("shareStatus")] + public string ShareStatus { get; private set; } + [JsonProperty("isHost")] + public Boolean IsHost { get; private set; } + [JsonProperty("isSharingMeeting")] + public Boolean IsSharingMeeting { get; private set; } + [JsonProperty("waitingForHost")] + public Boolean WaitingForHost { get; private set; } + + public MeetingInfo(string id, string name, string host, string password, string shareStatus, bool isHost, bool isSharingMeeting, bool waitingForHost) + { + Id = id; + Name = name; + Host = host; + Password = password; + ShareStatus = shareStatus; + IsHost = isHost; + IsSharingMeeting = isSharingMeeting; + WaitingForHost = waitingForHost; + } + } + + public class MeetingInfoEventArgs : EventArgs + { + public MeetingInfo Info { get; private set; } + + public MeetingInfoEventArgs(MeetingInfo info) + { + Info = info; + } + + } +} \ No newline at end of file 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 index f6d32149..409ccd89 100644 --- 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 @@ -1,100 +1,112 @@ using System; +using System.Linq; using System.Collections.Generic; +using PepperDash.Core; using PepperDash.Essentials.Core; namespace PepperDash.Essentials.Devices.Common.VideoCodec.Interfaces { - /// - /// Describes a device that has call participants - /// - public interface IHasParticipants - { - CodecParticipants Participants { get; } - } + /// + /// Describes a device that has call participants + /// + public interface IHasParticipants + { + CodecParticipants Participants { get; } + } - /// - /// Describes the ability to mute and unmute a participant's video in a meeting - /// - public interface IHasParticipantVideoMute:IHasParticipants - { - void MuteVideoForParticipant(int userId); - void UnmuteVideoForParticipant(int userId); - void ToggleVideoForParticipant(int userId); - } + /// + /// Describes the ability to mute and unmute a participant's video in a meeting + /// + public interface IHasParticipantVideoMute : IHasParticipants + { + void MuteVideoForParticipant(int userId); + void UnmuteVideoForParticipant(int userId); + void ToggleVideoForParticipant(int userId); + } - /// - /// Describes the ability to mute and unmute a participant's audio in a meeting - /// - public interface IHasParticipantAudioMute : IHasParticipantVideoMute - { - void MuteAudioForParticipant(int userId); - void UnmuteAudioForParticipant(int userId); - void ToggleAudioForParticipant(int userId); - } + /// + /// Describes the ability to mute and unmute a participant's audio in a meeting + /// + public interface IHasParticipantAudioMute : IHasParticipantVideoMute + { + void MuteAudioForParticipant(int userId); + void UnmuteAudioForParticipant(int userId); + void ToggleAudioForParticipant(int userId); + } - /// - /// Describes the ability to pin and unpin a participant in a meeting - /// - public interface IHasParticipantPinUnpin : IHasParticipants - { - IntFeedback NumberOfScreensFeedback { get; } - int ScreenIndexToPinUserTo { get; } + /// + /// Describes the ability to pin and unpin a participant in a meeting + /// + public interface IHasParticipantPinUnpin : IHasParticipants + { + IntFeedback NumberOfScreensFeedback { get; } + int ScreenIndexToPinUserTo { get; } - void PinParticipant(int userId, int screenIndex); - void UnPinParticipant(int userId); - void ToggleParticipantPinState(int userId, int screenIndex); - } + void PinParticipant(int userId, int screenIndex); + void UnPinParticipant(int userId); + void ToggleParticipantPinState(int userId, int screenIndex); + } - public class CodecParticipants - { - private List _currentParticipants; - - public List CurrentParticipants { - get { return _currentParticipants; } - set + public class CodecParticipants + { + private List _currentParticipants; + + public List CurrentParticipants + { + get { return _currentParticipants; } + set + { + _currentParticipants = value; + OnParticipantsChanged(); + } + } + + public Participant Host + { + get { - _currentParticipants = value; - OnParticipantsChanged(); + return _currentParticipants.FirstOrDefault(p => p.IsHost); } } - public event EventHandler ParticipantsListHasChanged; + public event EventHandler ParticipantsListHasChanged; - public CodecParticipants() - { - _currentParticipants = new List(); - } + public CodecParticipants() + { + _currentParticipants = new List(); + } - public void OnParticipantsChanged() - { - var handler = ParticipantsListHasChanged; + public void OnParticipantsChanged() + { + var handler = ParticipantsListHasChanged; - if (handler == null) return; + if (handler == null) return; - handler(this, new EventArgs()); - } - } + handler(this, new EventArgs()); + } + } - /// - /// Represents a call participant - /// - public class Participant - { - public int UserId { get; set; } - 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; } - public bool HandIsRaisedFb { get; set; } - public bool IsPinnedFb { get; set; } - public int ScreenIndexIsPinnedToFb { get; set; } + /// + /// Represents a call participant + /// + public class Participant + { + public int UserId { get; set; } + public bool IsHost { get; set; } + public bool IsMyself { 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; } + public bool HandIsRaisedFb { get; set; } + public bool IsPinnedFb { get; set; } + public int ScreenIndexIsPinnedToFb { get; set; } - public Participant() - { - // Initialize to -1 (no screen) - ScreenIndexIsPinnedToFb = -1; - } - } + public Participant() + { + // Initialize to -1 (no screen) + ScreenIndexIsPinnedToFb = -1; + } + } } \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasPresentationOnlyMeeting.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasPresentationOnlyMeeting.cs new file mode 100644 index 00000000..06730d2a --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasPresentationOnlyMeeting.cs @@ -0,0 +1,18 @@ +namespace PepperDash.Essentials.Devices.Common.VideoCodec.Interfaces +{ + public interface IHasPresentationOnlyMeeting + { + void StartSharingOnlyMeeting(); + void StartSharingOnlyMeeting(eSharingMeetingMode mode); + void StartSharingOnlyMeeting(eSharingMeetingMode mode, ushort duration); + void StartSharingOnlyMeeting(eSharingMeetingMode mode, ushort duration, string password); + void StartNormalMeetingFromSharingOnlyMeeting(); + } + + public enum eSharingMeetingMode + { + None, + Laptop, + Ios, + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasStandbyMode.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasStandbyMode.cs new file mode 100644 index 00000000..55dcd081 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasStandbyMode.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using PepperDash.Essentials.Core; + +namespace PepperDash.Essentials.Devices.Common.VideoCodec +{ + /// + /// Describes a device that has Standby Mode capability + /// + public interface IHasStandbyMode + { + BoolFeedback StandbyIsOnFeedback { get; } + + void StandbyActivate(); + + void StandbyDeactivate(); + } + + /// + /// Describes a device that has Half Waek Mode capability + /// + public interface IHasHalfWakeMode : IHasStandbyMode + { + BoolFeedback HalfWakeModeIsOnFeedback { get; } + + void HalfwakeActivate(); + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasStartMeeting.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasStartMeeting.cs new file mode 100644 index 00000000..6af59534 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasStartMeeting.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +namespace PepperDash.Essentials.Devices.Common.VideoCodec.Interfaces +{ + /// + /// Describes the ability to start an ad-hoc meeting + /// + public interface IHasStartMeeting + { + /// + /// The default meeting duration in minutes + /// + uint DefaultMeetingDurationMin { get; } + + /// + /// Start an ad-hoc meeting for the specified duration + /// + /// + void StartMeeting(uint duration); + + /// + /// Leaves a meeting without ending it + /// + void LeaveMeeting(); + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasVideoCodec.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasVideoCodec.cs index 1d0f4850..d8494334 100644 --- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasVideoCodec.cs +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasVideoCodec.cs @@ -11,10 +11,9 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec /// /// For rooms that have video codec /// - public interface IHasVideoCodec + public interface IHasVideoCodec:IHasInCallFeedback,IPrivacy { VideoCodecBase VideoCodec { get; } - BoolFeedback InCallFeedback { get; } ///// ///// Make this more specific @@ -26,11 +25,6 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec /// IntFeedback CallTypeFeedback { get; } - /// - /// - /// - BoolFeedback PrivacyModeIsOnFeedback { get; } - /// /// When something in the room is sharing with the far end or through other means /// 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 e8988ad2..ac7514e0 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 @@ -123,12 +123,14 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec /// 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); var tl = new TieLine(OsdSource.AudioVideoOutputPort, CodecOsdIn); TieLineCollection.Default.Add(tl); + + //foreach(var input in Status.Video. } /// @@ -580,6 +582,10 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec void SetupCameras() { + SupportsCameraAutoMode = true; + + SupportsCameraOff = false; + Cameras = new List(); var internalCamera = new MockVCCamera(Key + "-camera1", "Near End", this); 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 c5cff74e..26ed54bb 100644 --- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/VideoCodecBase.cs +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/VideoCodecBase.cs @@ -25,7 +25,7 @@ using Feedback = PepperDash.Essentials.Core.Feedback; namespace PepperDash.Essentials.Devices.Common.VideoCodec { public abstract class VideoCodecBase : ReconfigurableDevice, IRoutingInputsOutputs, - IUsageTracking, IHasDialer, IHasContentSharing, ICodecAudio, iVideoCodecInfo, IBridgeAdvanced + IUsageTracking, IHasDialer, IHasContentSharing, ICodecAudio, iVideoCodecInfo, IBridgeAdvanced, IHasStandbyMode { private const int XSigEncoding = 28591; protected const int MaxParticipants = 50; @@ -836,10 +836,10 @@ ScreenIndexIsPinnedTo: {8} (a{17}) //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 + 2] = new XSigSerialToken(stringIndex + 3, meeting.StartTime.ToString("t", Global.Culture)); + tokenArray[stringIndex + 3] = new XSigSerialToken(stringIndex + 4, meeting.StartTime.ToString("t", Global.Culture)); + tokenArray[stringIndex + 4] = new XSigSerialToken(stringIndex + 5, meeting.EndTime.ToString("t", Global.Culture)); + tokenArray[stringIndex + 5] = new XSigSerialToken(stringIndex + 6, meeting.EndTime.ToString("t", Global.Culture)); tokenArray[stringIndex + 6] = new XSigSerialToken(stringIndex + 7, meeting.Id); 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 c116a4a8..e6efd063 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 @@ -59,6 +59,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom public List AudioOuputs { get; set; } public List Cameras { get; set; } public zEvent.PhoneCallStatus PhoneCall { get; set; } + public zEvent.NeedWaitForHost NeedWaitForHost { get; set; } public ZoomRoomStatus() { @@ -76,6 +77,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom AudioOuputs = new List(); Cameras = new List(); PhoneCall = new zEvent.PhoneCallStatus(); + NeedWaitForHost = new zEvent.NeedWaitForHost(); } } @@ -236,14 +238,6 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom } } - /// - /// Used to be able to inplement IInvitableContact on DirectoryContact - /// - public class ZoomDirectoryContact : DirectoryContact, IInvitableContact - { - - } - public class Phonebook { [JsonProperty("Contacts")] @@ -302,7 +296,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom foreach (Contact c in zoomContacts) { - var contact = new ZoomDirectoryContact { Name = c.ScreenName, ContactId = c.Jid }; + var contact = new InvitableDirectoryContact { Name = c.ScreenName, ContactId = c.Jid }; contact.ContactMethods.Add(new ContactMethod() { Number = c.Jid, Device = eContactMethodDevice.Video, CallType = eContactMethodCallType.Video, ContactMethodId = c.Jid }); @@ -439,6 +433,10 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom { private string _dispState; private string _password; + private bool _isAirHostClientConnected; + private bool _isSharingBlackMagic; + private bool _isDirectPresentationConnected; + public string directPresentationPairingCode { get; set; } /// @@ -460,11 +458,51 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom } } } - public bool isAirHostClientConnected { get; set; } + + public bool isAirHostClientConnected + { + get { return _isAirHostClientConnected; } + set + { + if (value != _isAirHostClientConnected) + { + _isAirHostClientConnected = value; + NotifyPropertyChanged("isAirHostClientConnected"); + } + } + } + public bool isBlackMagicConnected { get; set; } public bool isBlackMagicDataAvailable { get; set; } - public bool isDirectPresentationConnected { get; set; } - public bool isSharingBlackMagic { get; set; } + + public bool isDirectPresentationConnected + { + get { return _isDirectPresentationConnected; } + set + { + if (value != _isDirectPresentationConnected) + { + _isDirectPresentationConnected = value; + NotifyPropertyChanged("isDirectPresentationConnected"); + } + } + } + + public bool isSharingBlackMagic + { + get { return _isSharingBlackMagic; } + set + { + if (value != _isSharingBlackMagic) + { + _isSharingBlackMagic = value; + NotifyPropertyChanged("isSharingBlackMagic"); + } + } + } + + + /// /// IOS Airplay code /// @@ -720,6 +758,10 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom /// public class zEvent { + public class StartLocalPresentMeeting + { + public bool Success { get; set; } + } public class NeedWaitForHost { public bool Wait { get; set; } @@ -787,7 +829,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom private bool _paused; private eSharingState _state; - public bool IsSharing; + public bool IsSharing { get; private set; } [JsonProperty("paused")] public bool Paused @@ -927,6 +969,15 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom PhoneCallStatus_InCall, PhoneCallStatus_Init, } + + public class MeetingNeedsPassword + { + [JsonProperty("needsPassword")] + public bool NeedsPassword { get; set; } + + [JsonProperty("wrongAndRetry")] + public bool WrongAndRetry { get; set; } + } } /// @@ -1426,6 +1477,10 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom public static List GetGenericParticipantListFromParticipantsResult( List participants) { + if (participants.Count == 0) + { + return new List(); + } //return participants.Select(p => new Participant // { // UserId = p.UserId, @@ -1444,6 +1499,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom UserId = p.UserId, Name = p.UserName, IsHost = p.IsHost, + IsMyself = p.IsMyself, CanMuteVideo = p.IsVideoCanMuteByHost, CanUnmuteVideo = p.IsVideoCanUnmuteByHost, AudioMuteFb = p.AudioStatusState == "AUDIO_MUTED", 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 5df53635..32a24a28 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 @@ -1,15 +1,14 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.Linq; using System.Text; using Crestron.SimplSharp; -using Crestron.SimplSharp.Reflection; using Crestron.SimplSharpPro.CrestronThread; using Crestron.SimplSharpPro.DeviceSupport; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using PepperDash.Core; -using PepperDash.Core.Intersystem.Tokens; using PepperDash.Essentials.Core; using PepperDash.Essentials.Core.Bridges; using PepperDash.Essentials.Core.Config; @@ -26,17 +25,19 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom public class ZoomRoom : VideoCodecBase, IHasCodecSelfView, IHasDirectoryHistoryStack, ICommunicationMonitor, IRouting, IHasScheduleAwareness, IHasCodecCameras, IHasParticipants, IHasCameraOff, IHasCameraMute, IHasCameraAutoMode, - IHasFarEndContentStatus, IHasSelfviewPosition, IHasPhoneDialing, IHasZoomRoomLayouts, IHasParticipantPinUnpin, IHasParticipantAudioMute, IHasSelfviewSize + IHasFarEndContentStatus, IHasSelfviewPosition, IHasPhoneDialing, IHasZoomRoomLayouts, IHasParticipantPinUnpin, + IHasParticipantAudioMute, IHasSelfviewSize, IPasswordPrompt, IHasStartMeeting, IHasMeetingInfo, IHasPresentationOnlyMeeting { private const long MeetingRefreshTimer = 60000; - private const uint DefaultMeetingDurationMin = 30; + public uint DefaultMeetingDurationMin { get; private set; } + private const string Delimiter = "\x0D\x0A"; - private readonly GenericQueue _receiveQueue; - //private readonly CrestronQueue _receiveQueue; + private readonly GenericQueue _receiveQueue; + //private readonly CrestronQueue _receiveQueue; - //private readonly Thread _receiveThread; + //private readonly Thread _receiveThread; private readonly ZoomRoomSyncState _syncState; public bool CommDebuggingIsOn; @@ -46,15 +47,18 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom private StringBuilder _jsonMessage; private int _previousVolumeLevel; private CameraBase _selectedCamera; + private string _lastDialedMeetingNumber; private readonly ZoomRoomPropertiesConfig _props; public ZoomRoom(DeviceConfig config, IBasicCommunication comm) : base(config) { + DefaultMeetingDurationMin = 30; + _props = JsonConvert.DeserializeObject(config.Properties.ToString()); - _receiveQueue = new GenericQueue(Key + "-rxQueue", Thread.eThreadPriority.MediumPriority, 512); + _receiveQueue = new GenericQueue(Key + "-rxQueue", Thread.eThreadPriority.MediumPriority, 512); Communication = comm; @@ -83,7 +87,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom PhonebookSyncState = new CodecPhonebookSyncState(Key + "--PhonebookSync"); - PortGather = new CommunicationGather(Communication, "\x0A") { IncludeDelimiter = true }; + PortGather = new CommunicationGather(Communication, "\x0A") {IncludeDelimiter = true}; PortGather.LineReceived += Port_LineReceived; CodecOsdIn = new RoutingInputPort(RoutingPortNames.CodecOsd, @@ -94,9 +98,9 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom eRoutingSignalType.Audio | eRoutingSignalType.Video, eRoutingPortConnectionType.Hdmi, null, this); - Output2 = new RoutingOutputPort(RoutingPortNames.AnyVideoOut, - eRoutingSignalType.Video, - eRoutingPortConnectionType.DisplayPort, null, this); + Output2 = new RoutingOutputPort(RoutingPortNames.AnyVideoOut, + eRoutingSignalType.Video, + eRoutingPortConnectionType.DisplayPort, null, this); SelfviewIsOnFeedback = new BoolFeedback(SelfViewIsOnFeedbackFunc); @@ -108,13 +112,18 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom CodecSchedule = new CodecScheduleAwareness(MeetingRefreshTimer); + if (_props.MinutesBeforeMeetingStart > 0) + { + CodecSchedule.MeetingWarningMinutes = _props.MinutesBeforeMeetingStart; + } + ReceivingContent = new BoolFeedback(FarEndIsSharingContentFeedbackFunc); SelfviewPipPositionFeedback = new StringFeedback(SelfviewPipPositionFeedbackFunc); // TODO: #714 [ ] SelfviewPipSizeFeedback SelfviewPipSizeFeedback = new StringFeedback(SelfviewPipSizeFeedbackFunc); - + SetUpFeedbackActions(); Cameras = new List(); @@ -123,7 +132,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom Participants = new CodecParticipants(); - SupportsCameraOff = true; // Always allow turning off the camera for zoom calls? + SupportsCameraOff = true; // Always allow turning off the camera for zoom calls? SupportsCameraAutoMode = _props.SupportsCameraAutoMode; PhoneOffHookFeedback = new BoolFeedback(PhoneOffHookFeedbackFunc); @@ -138,7 +147,6 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom ContentSwappedWithThumbnailFeedback = new BoolFeedback(ContentSwappedWithThumbnailFeedbackFunc); NumberOfScreensFeedback = new IntFeedback(NumberOfScreensFeedbackFunc); - } public CommunicationGather PortGather { get; private set; } @@ -157,18 +165,19 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom { get { - return () => - { - var scaledVol = CrestronEnvironment.ScaleWithLimits(Configuration.Audio.Output.Volume, 100, 0, 65535, 0); + return () => + { + var scaledVol = CrestronEnvironment.ScaleWithLimits(Configuration.Audio.Output.Volume, 100, 0, 65535, 0); - if (Configuration.Audio.Output.Volume != 0) - { - Debug.Console(2, this, "Storing previous volume level as: {0}, scaled: {1}", Configuration.Audio.Output.Volume, scaledVol); - _previousVolumeLevel = scaledVol; // Store the previous level for recall - } + if (Configuration.Audio.Output.Volume != 0) + { + Debug.Console(2, this, "Storing previous volume level as: {0}, scaled: {1}", Configuration.Audio.Output.Volume, + scaledVol); + _previousVolumeLevel = scaledVol; // Store the previous level for recall + } - return scaledVol; - }; + return scaledVol; + }; } } @@ -266,7 +275,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom public RoutingInputPort CodecOsdIn { get; private set; } public RoutingOutputPort Output1 { get; private set; } - public RoutingOutputPort Output2 { get; private set; } + public RoutingOutputPort Output2 { get; private set; } #region ICommunicationMonitor Members @@ -366,16 +375,17 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom public CodecDirectory CurrentDirectoryResult { get { return _currentDirectoryResult; } - private set - { - _currentDirectoryResult = value; + private set + { + _currentDirectoryResult = value; - Debug.Console(2, this, "CurrentDirectoryResult Updated. ResultsFolderId: {0}", _currentDirectoryResult.ResultsFolderId); + Debug.Console(2, this, "CurrentDirectoryResult Updated. ResultsFolderId: {0}", + _currentDirectoryResult.ResultsFolderId); - CurrentDirectoryResultIsNotDirectoryRoot.FireUpdate(); + CurrentDirectoryResultIsNotDirectoryRoot.FireUpdate(); - OnDirectoryResultReturned(_currentDirectoryResult); - } + OnDirectoryResultReturned(_currentDirectoryResult); + } } public CodecPhonebookSyncState PhonebookSyncState { get; private set; } @@ -390,12 +400,11 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom DirectoryBrowseHistoryStack.Clear(); CurrentDirectoryResult = directoryResults; - } public void GetDirectoryFolderContents(string folderId) { - var directoryResults = new CodecDirectory { ResultsFolderId = folderId }; + var directoryResults = new CodecDirectory {ResultsFolderId = folderId}; directoryResults.AddContactsToDirectory( DirectoryRoot.CurrentDirectoryResults.FindAll(c => c.ParentFolderId.Equals(folderId))); @@ -403,15 +412,14 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom DirectoryBrowseHistoryStack.Push(_currentDirectoryResult); CurrentDirectoryResult = directoryResults; - } public void SetCurrentDirectoryToRoot() { DirectoryBrowseHistoryStack.Clear(); - CurrentDirectoryResult = DirectoryRoot; - } + CurrentDirectoryResult = DirectoryRoot; + } public void GetDirectoryParentFolderContents() { @@ -422,8 +430,8 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom var currentDirectory = DirectoryBrowseHistoryStack.Pop(); - CurrentDirectoryResult = currentDirectory; - } + CurrentDirectoryResult = currentDirectory; + } public BoolFeedback CurrentDirectoryResultIsNotDirectoryRoot { get; private set; } @@ -462,14 +470,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom private void SetUpCallFeedbackActions() { - Status.Call.Sharing.PropertyChanged += (o, a) => - { - if (a.PropertyName == "State") - { - SharingContentIsOnFeedback.FireUpdate(); - ReceivingContent.FireUpdate(); - } - }; + Status.Call.Sharing.PropertyChanged += HandleSharingStateUpdate; Status.Call.PropertyChanged += (o, a) => { @@ -481,6 +482,39 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom }; } + private void HandleSharingStateUpdate(object sender, PropertyChangedEventArgs a) + { + if (a.PropertyName != "State") + { + return; + } + + SharingContentIsOnFeedback.FireUpdate(); + ReceivingContent.FireUpdate(); + try + { + + // Update the share status of the meeting info + if (MeetingInfo == null) + { + var sharingStatus = GetSharingStatus(); + + MeetingInfo = new MeetingInfo("", "", "", "", sharingStatus, GetIsHostMyself(), true, false); + return; + } + + var meetingInfo = new MeetingInfo(MeetingInfo.Id, MeetingInfo.Name, Participants.Host != null ? Participants.Host.Name : "None", + MeetingInfo.Password, GetSharingStatus(), GetIsHostMyself(), MeetingInfo.IsSharingMeeting, MeetingInfo.WaitingForHost); + MeetingInfo = meetingInfo; + } + catch (Exception e) + { + Debug.Console(1, this, "Error processing state property update. {0}", e.Message); + Debug.Console(2, this, e.StackTrace); + MeetingInfo = new MeetingInfo("", "", "", "", "None", false, false, false); + } + } + /// /// Subscribes to the PropertyChanged events on the state objects and fires the corresponding feedbacks. /// @@ -534,23 +568,23 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom switch (a.PropertyName) { case "Position": - { - ComputeSelfviewPipPositionStatus(); + { + ComputeSelfviewPipPositionStatus(); - SelfviewPipPositionFeedback.FireUpdate(); + SelfviewPipPositionFeedback.FireUpdate(); - break; - } + break; + } case "ShareThumb": - { - ContentSwappedWithThumbnailFeedback.FireUpdate(); - break; - } + { + ContentSwappedWithThumbnailFeedback.FireUpdate(); + break; + } case "Style": - { - LocalLayoutFeedback.FireUpdate(); - break; - } + { + LocalLayoutFeedback.FireUpdate(); + break; + } case "Size": { // TODO: #714 [ ] SetupFeedbackActions >> Size @@ -560,36 +594,34 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom break; } - } }; - // This is to deal with incorrect object structure coming back from the Zoom Room on v 5.6.3 - Configuration.Client.Call.Layout.PropertyChanged += (o,a) => - { - switch (a.PropertyName) - { - case "Position": - { - ComputeSelfviewPipPositionStatus(); + // This is to deal with incorrect object structure coming back from the Zoom Room on v 5.6.3 + Configuration.Client.Call.Layout.PropertyChanged += (o, a) => + { + switch (a.PropertyName) + { + case "Position": + { + ComputeSelfviewPipPositionStatus(); - SelfviewPipPositionFeedback.FireUpdate(); + SelfviewPipPositionFeedback.FireUpdate(); - break; - } - case "ShareThumb": - { - ContentSwappedWithThumbnailFeedback.FireUpdate(); - break; - } - case "Style": - { - LocalLayoutFeedback.FireUpdate(); - break; - } - - } - }; + break; + } + case "ShareThumb": + { + ContentSwappedWithThumbnailFeedback.FireUpdate(); + break; + } + case "Style": + { + LocalLayoutFeedback.FireUpdate(); + break; + } + } + }; Status.Call.Sharing.PropertyChanged += (o, a) => { @@ -618,6 +650,23 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom break; case "password": break; + case "isAirHostClientConnected": + case "isDirectPresentationConnected": + case "isSharingBlackMagic": + { + Debug.Console(2, this, "Updating sharing status: {0}", a.PropertyName); + + SharingContentIsOnFeedback.FireUpdate(); + if (MeetingInfo == null) + { + //Ignoring for now, as the CallInfo return will create the appropriate value + return; + } + // Update the share status of the meeting info + var meetingInfo = new MeetingInfo(MeetingInfo.Id, MeetingInfo.Name, MeetingInfo.Host, MeetingInfo.Password, GetSharingStatus(), GetIsHostMyself(), MeetingInfo.IsSharingMeeting, MeetingInfo.WaitingForHost); + MeetingInfo = meetingInfo; + break; + } } }; @@ -651,53 +700,52 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom case "can_switch_speaker_view": case "can_switch_wall_view": case "can_switch_share_on_all_screens": - { - ComputeAvailableLayouts(); - break; - } + { + ComputeAvailableLayouts(); + break; + } case "is_in_first_page": - { - LayoutViewIsOnFirstPageFeedback.FireUpdate(); - break; - } + { + LayoutViewIsOnFirstPageFeedback.FireUpdate(); + break; + } case "is_in_last_page": - { - LayoutViewIsOnLastPageFeedback.FireUpdate(); - break; - } - //case "video_type": - // { - // It appears as though the actual value we want to watch is Configuration.Call.Layout.Style - // LocalLayoutFeedback.FireUpdate(); - // break; - // } + { + LayoutViewIsOnLastPageFeedback.FireUpdate(); + break; + } + //case "video_type": + // { + // It appears as though the actual value we want to watch is Configuration.Call.Layout.Style + // LocalLayoutFeedback.FireUpdate(); + // break; + // } } }; Status.NumberOfScreens.PropertyChanged += (o, a) => + { + switch (a.PropertyName) { - switch (a.PropertyName) + case "NumberOfScreens": { - case "NumberOfScreens": - { - NumberOfScreensFeedback.FireUpdate(); - break; - } + NumberOfScreensFeedback.FireUpdate(); + break; } - }; + } + }; } private void SetUpDirectory() { - DirectoryRoot = new CodecDirectory() { ResultsFolderId = "root" }; + DirectoryRoot = new CodecDirectory() {ResultsFolderId = "root"}; - CurrentDirectoryResultIsNotDirectoryRoot = new BoolFeedback(() => CurrentDirectoryResult.ResultsFolderId != "root"); + CurrentDirectoryResultIsNotDirectoryRoot = new BoolFeedback(() => CurrentDirectoryResult.ResultsFolderId != "root"); - CurrentDirectoryResult = DirectoryRoot; + CurrentDirectoryResult = DirectoryRoot; DirectoryBrowseHistory = new List(); DirectoryBrowseHistoryStack = new Stack(); - } private void SetUpRouting() @@ -708,7 +756,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom // Set up output ports OutputPorts.Add(Output1); - OutputPorts.Add(Output2); + OutputPorts.Add(Output2); } /// @@ -735,7 +783,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom ConsoleAccessLevelEnum.AccessOperator); if (!_props.DisablePhonebookAutoDownload) { - CrestronConsole.AddNewConsoleCommand(s => SendText("zCommand Phonebook List Offset: 0 Limit: 512"), + CrestronConsole.AddNewConsoleCommand(s => SendText("zCommand Phonebook List Offset: 0 Limit: 10000"), "GetZoomRoomContacts", "Triggers a refresh of the codec phonebook", ConsoleAccessLevelEnum.AccessOperator); } @@ -772,12 +820,12 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom if (s == "1") { CommDebuggingIsOn = true; - Debug.Console(0, this, "Comm Debug Enabled."); + Debug.Console(1, this, "Comm Debug Enabled."); } else { CommDebuggingIsOn = false; - Debug.Console(0, this, "Comm Debug Disabled."); + Debug.Console(1, this, "Comm Debug Disabled."); } } @@ -865,7 +913,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom if (!_props.DisablePhonebookAutoDownload) { - _syncState.AddQueryToQueue("zCommand Phonebook List Offset: 0 Limit: 512"); + _syncState.AddQueryToQueue("zCommand Phonebook List Offset: 0 Limit: 10000"); } _syncState.AddQueryToQueue("zCommand Bookings List"); @@ -885,7 +933,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom // Counts the curly braces if (message.Contains("client_loop: send disconnect: Broken pipe")) { - Debug.Console(0, this, Debug.ErrorLogLevel.Error, + Debug.Console(1, this, Debug.ErrorLogLevel.Error, "Zoom Room Controller or App connected. Essentials will NOT control the Zoom Room until it is disconnected."); return; @@ -904,7 +952,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom //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 + // Check for the beginning of a new JSON message { _jsonFeedbackMessageIsIncoming = true; _jsonCurlyBraceCounter = 1; // reset the counter for each new message @@ -921,7 +969,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom return; } if (_jsonFeedbackMessageIsIncoming && message.Trim('\x20') == "}" + Delimiter) - // Check for the end of a JSON message + // Check for the end of a JSON message { _jsonMessage.Append(message); @@ -964,34 +1012,39 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom 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(); + // Currently the feedback exclusions don't work when using the API in JSON response mode + // But leave these here in case the API gets updated in the future - // 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); + SendText("zFeedback Register Op: ex Path: /Event/InfoResult/Info/toll_free_callinLlist"); + Thread.Sleep(100); + + if (!_props.DisablePhonebookAutoDownload) { - 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); - SendText("zFeedback Register Op: ex Path: /Event/InfoResult/Info/toll_free_callinLlist"); - Thread.Sleep(100); + SendText("zFeedback Register Op: ex Path: /Event/Phonebook/AddedContact"); + } + // 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; + } } } } @@ -1015,7 +1068,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom var eType = (eZoomRoomResponseType) - Enum.Parse(typeof(eZoomRoomResponseType), message["type"].Value(), true); + Enum.Parse(typeof (eZoomRoomResponseType), message["type"].Value(), true); var topKey = message["topKey"].Value(); @@ -1026,494 +1079,612 @@ 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 "inforesult": - { - JsonConvert.PopulateObject(responseObj.ToString(), Status.Call.Info); + 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; + } + case eZoomRoomResponseType.zCommand: + { + switch (topKey.ToLower()) + { + case "inforesult": + { + JsonConvert.PopulateObject(responseObj.ToString(), Status.Call.Info); + break; + } + case "phonebooklistresult": + { + // This result will always be the complete contents of the directory and never + // A subset of the results via a search + + JsonConvert.PopulateObject(responseObj.ToString(), Status.Phonebook); + + var directoryResults = + zStatus.Phonebook.ConvertZoomContactsToGeneric(Status.Phonebook.Contacts); + + if (!PhonebookSyncState.InitialSyncComplete) + { + PhonebookSyncState.InitialPhonebookFoldersReceived(); + PhonebookSyncState.PhonebookRootEntriesReceived(); + PhonebookSyncState.SetPhonebookHasFolders(true); + PhonebookSyncState.SetNumberOfContacts(Status.Phonebook.Contacts.Count); + } + + if (directoryResults.ResultsFolderId != "root") + { + directoryResults.ResultsFolderId = "root"; + } + + DirectoryRoot = directoryResults; + + CurrentDirectoryResult = 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 "phonebooklistresult": + case JTokenType.Object: { - // This result will always be the complete contents of the directory and never - // A subset of the results via a search + // this is a single participant event notification - JsonConvert.PopulateObject(responseObj.ToString(), Status.Phonebook); + var participant = + JsonConvert.DeserializeObject( + responseObj.ToString()); - var directoryResults = - zStatus.Phonebook.ConvertZoomContactsToGeneric(Status.Phonebook.Contacts); - - if (!PhonebookSyncState.InitialSyncComplete) + if (participant != null) { - PhonebookSyncState.InitialPhonebookFoldersReceived(); - PhonebookSyncState.PhonebookRootEntriesReceived(); - PhonebookSyncState.SetPhonebookHasFolders(true); - PhonebookSyncState.SetNumberOfContacts(Status.Phonebook.Contacts.Count); - } + Debug.Console(1, this, + "[DeserializeResponse] zCommands.listparticipantresult - participant.event: {0} **********************************", + participant.Event); + Debug.Console(1, this, + "[DeserializeResponse] zCommands.listparticipantresult - participant.event: {0} - UserId: {1} Name: {2} IsHost: {3}", + participant.Event, participant.UserId, participant.UserName, participant.IsHost); - if (directoryResults.ResultsFolderId != "root") - { - directoryResults.ResultsFolderId = "root"; - } - - DirectoryRoot = directoryResults; - - CurrentDirectoryResult = 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 JTokenType.Object: + switch (participant.Event) + { + case "ZRCUserChangedEventUserInfoUpdated": + case "ZRCUserChangedEventLeftMeeting": { - // this is a single participant event notification + var existingParticipant = + Status.Call.Participants.FirstOrDefault( + p => p.UserId.Equals(participant.UserId)); - var participant = - JsonConvert.DeserializeObject( - responseObj.ToString()); - - if (participant != null) + if (existingParticipant != null) { switch (participant.Event) { - case "ZRCUserChangedEventUserInfoUpdated": case "ZRCUserChangedEventLeftMeeting": - { - var existingParticipant = - Status.Call.Participants.FirstOrDefault( - p => p.UserId.Equals(participant.UserId)); - - if (existingParticipant != null) - { - switch (participant.Event) - { - case "ZRCUserChangedEventLeftMeeting": - Status.Call.Participants.Remove(existingParticipant); - break; - case "ZRCUserChangedEventUserInfoUpdated": - JsonConvert.PopulateObject(responseObj.ToString(), - existingParticipant); - break; - } - } - } + Status.Call.Participants.Remove(existingParticipant); break; - case "ZRCUserChangedEventJoinedMeeting": - Status.Call.Participants.Add(participant); + case "ZRCUserChangedEventUserInfoUpdated": + JsonConvert.PopulateObject(responseObj.ToString(), + existingParticipant); break; } } } - break; - } - - var participants = - zCommand.ListParticipant.GetGenericParticipantListFromParticipantsResult( - Status.Call.Participants); - - Participants.CurrentParticipants = participants; - - PrintCurrentCallParticipants(); - - break; - } - default: - { - break; - } - } - break; - } - case eZoomRoomResponseType.zEvent: - { - switch (topKey.ToLower()) - { - 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) - { - // Update existing contact - JsonConvert.PopulateObject(responseObj["Updated Contact"].ToString(), - existingContact); - } - } - else if (responseObj["Added Contact"] != null) - { - var jToken = responseObj["Updated Contact"]; - if (jToken != null) - { - var newContact = - JsonConvert.DeserializeObject( - jToken.ToString()); - - // Add a new contact - Status.Phonebook.Contacts.Add(newContact); - } - } - - 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.Video, - 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) + break; + case "ZRCUserChangedEventJoinedMeeting": { - activeCall.Status = eCodecCallStatus.Disconnected; + var existingParticipant = + Status.Call.Participants.FirstOrDefault(p => p.UserId.Equals(participant.UserId)); - OnCallStatusChange(activeCall); + if (existingParticipant != null) + { + Debug.Console(1, this, + "[DeserializeResponse] zCommands.listparticipantresult - participant.event: {0} ...updating matching UserId participant with UserId: {1} UserName: {2}", + participant.Event, participant.UserId, participant.UserName); + + JsonConvert.PopulateObject(responseObj.ToString(), existingParticipant); + } + else + { + Debug.Console(1, this, + "[DeserializeResponse] zCommands.listparticipantresult - participant.event: {0} ...adding participant with UserId: {1} UserName: {2}", + participant.Event, participant.UserId, participant.UserName); + + Status.Call.Participants.Add(participant); + } + + break; } } - var emptyList = new List(); - Participants.CurrentParticipants = emptyList; + + Debug.Console(1, this, + "[DeserializeResponse] zCommands.listparticipantresult - participant.event: {0} ***********************************", + participant.Event); } - - 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 - } + var participants = + zCommand.ListParticipant.GetGenericParticipantListFromParticipantsResult( + Status.Call.Participants); - break; - } - case "openvideofailforhoststop": - { - // TODO: notify user that host has disabled unmuting video - break; - } - case "updatedcallrecordinfo": - { - JsonConvert.PopulateObject(responseObj.ToString(), Status.Call.CallRecordInfo); + Participants.CurrentParticipants = participants; - break; - } - case "phonecallstatus": - { - JsonConvert.PopulateObject(responseObj.ToString(), Status.PhoneCall); - break; - } - case "pinstatusofscreennotification": - { - var status = responseObj.ToObject(); + // Update the share status of the meeting info + var meetingInfo = new MeetingInfo(MeetingInfo.Id, MeetingInfo.Name, Participants.Host.Name, MeetingInfo.Password, MeetingInfo.ShareStatus, GetIsHostMyself(), MeetingInfo.IsSharingMeeting, MeetingInfo.WaitingForHost); + MeetingInfo = meetingInfo; - Debug.Console(1, this, "Pin Status notification for UserId: {0}, ScreenIndex: {1}", status.PinnedUserId, status.ScreenIndex); + PrintCurrentCallParticipants(); - Participant alreadyPinnedParticipant = null; - - // Check for a participant already pinned to the same screen index. - if (status.PinnedUserId > 0) - { - alreadyPinnedParticipant = Participants.CurrentParticipants.FirstOrDefault(p => p.ScreenIndexIsPinnedToFb.Equals(status.ScreenIndex)); - - // Make sure that the already pinned participant isn't the same ID as for this message. If true, clear the pinned fb. - if (alreadyPinnedParticipant != null && alreadyPinnedParticipant.UserId != status.PinnedUserId) - { - Debug.Console(1, this, "Participant: {0} with id: {1} already pinned to screenIndex {2}. Clearing pinned fb.", - alreadyPinnedParticipant.Name, alreadyPinnedParticipant.UserId, alreadyPinnedParticipant.ScreenIndexIsPinnedToFb); - alreadyPinnedParticipant.IsPinnedFb = false; - alreadyPinnedParticipant.ScreenIndexIsPinnedToFb = -1; - } - } - - var participant = Participants.CurrentParticipants.FirstOrDefault(p => p.UserId.Equals(status.PinnedUserId)); - - if (participant != null) - { - participant.IsPinnedFb = true; - participant.ScreenIndexIsPinnedToFb = status.ScreenIndex; - } - else - { - participant = Participants.CurrentParticipants.FirstOrDefault(p => p.ScreenIndexIsPinnedToFb.Equals(status.ScreenIndex)); - - if (participant == null && alreadyPinnedParticipant == null) - { - Debug.Console(1, this, "no matching participant found by pinned_user_id: {0} or screen_index: {1}", status.PinnedUserId, status.ScreenIndex); - return; - } - else if (participant != null) - { - Debug.Console(2, this, "Unpinning {0} with id: {1} from screen index: {2}", participant.Name, participant.UserId, status.ScreenIndex); - participant.IsPinnedFb = false; - participant.ScreenIndexIsPinnedToFb = -1; - } - } - - // fire the event as we've modified the participants list - Participants.OnParticipantsChanged(); - - break; - } - default: - { - break; - } + break; } - break; - } - case eZoomRoomResponseType.zStatus: - { - switch (topKey.ToLower()) + default: { - case "login": - { - _syncState.LoginMessageReceived(); - - if (!_syncState.InitialQueryMessagesWereSent) - { - SetUpSyncQueries(); - } - - 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; } - - break; } - default: + break; + } + case eZoomRoomResponseType.zEvent: + { + switch (topKey.ToLower()) { - Debug.Console(1, "Unknown Response Type:"); - break; + 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) + { + // Update existing contact + JsonConvert.PopulateObject(responseObj["Updated Contact"].ToString(), + existingContact); + } + } + else if (responseObj["Added Contact"] != null) + { + var jToken = responseObj["Updated Contact"]; + if (jToken != null) + { + var newContact = + JsonConvert.DeserializeObject( + jToken.ToString()); + + // Add a new contact + Status.Phonebook.Contacts.Add(newContact); + } + } + + 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.Video, + 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()); + + Debug.Console(1, this, + "[DeserializeResponse] zEvent.calldisconnect ********************************************"); + Debug.Console(1, this, "[DeserializeResponse] zEvent.calldisconnect - disconnectEvent.Successful: {0}", + disconnectEvent.Successful); + + if (disconnectEvent.Successful) + { + if (ActiveCalls.Count > 0) + { + var activeCall = ActiveCalls.FirstOrDefault(c => c.IsActiveCall); + + if (activeCall != null) + { + Debug.Console(1, this, + "[DeserializeResponse] zEvent.calldisconnect - ActiveCalls.Count: {0} activeCall.Id: {1}, activeCall.Number: {2} activeCall.Name: {3}, activeCall.IsActive: {4}", + ActiveCalls.Count, activeCall.Id, activeCall.Number, activeCall.Name, activeCall.IsActiveCall); + activeCall.Status = eCodecCallStatus.Disconnected; + + OnCallStatusChange(activeCall); + } + } + } + + Debug.Console(1, this, + "[DeserializeResponse] zEvent.calldisconnect ********************************************"); + + UpdateCallStatus(); + break; + } + case "callconnecterror": + { + UpdateCallStatus(); + break; + } + case "videounmuterequest": + { + // TODO: notify room of a request to unmute video + break; + } + case "meetingneedspassword": + { + var meetingNeedsPassword = + responseObj.ToObject(); + + if (meetingNeedsPassword.NeedsPassword) + { + var prompt = "Password required to join this meeting. Please enter the meeting password."; + + OnPasswordRequired(meetingNeedsPassword.WrongAndRetry, false, false, prompt); + } + else + { + OnPasswordRequired(false, false, true, ""); + } + + break; + } + case "needwaitforhost": + { + Status.NeedWaitForHost = JsonConvert.DeserializeObject(responseObj.ToString()); + + Debug.Console(1, this, "NeedWaitForHost: {0}", Status.NeedWaitForHost.Wait); + + if (Status.NeedWaitForHost.Wait) + { + if (MeetingInfo == null) + { + MeetingInfo = new MeetingInfo("Waiting For Host", "Waiting For Host", "Waiting For Host", "", + GetSharingStatus(), false, false, true); + + UpdateCallStatus(); + break; + } + + MeetingInfo = new MeetingInfo("Waiting For Host", "Waiting For Host", "Waiting For Host", "", + GetSharingStatus(), false, false, true); + + UpdateCallStatus(); + + break; + } + + if (MeetingInfo == null) + { + MeetingInfo = new MeetingInfo("Waiting For Host", "Waiting For Host", "Waiting For Host", "", + GetSharingStatus(), false, false, false); + break; + } + + MeetingInfo = new MeetingInfo(MeetingInfo.Id, MeetingInfo.Name, MeetingInfo.Host, MeetingInfo.Password, + GetSharingStatus(), GetIsHostMyself(), false, false); + + 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; + } + case "pinstatusofscreennotification": + { + var status = responseObj.ToObject(); + + Debug.Console(1, this, "Pin Status notification for UserId: {0}, ScreenIndex: {1}", status.PinnedUserId, + status.ScreenIndex); + + Participant alreadyPinnedParticipant = null; + + // Check for a participant already pinned to the same screen index. + if (status.PinnedUserId > 0) + { + alreadyPinnedParticipant = + Participants.CurrentParticipants.FirstOrDefault(p => p.ScreenIndexIsPinnedToFb.Equals(status.ScreenIndex)); + + // Make sure that the already pinned participant isn't the same ID as for this message. If true, clear the pinned fb. + if (alreadyPinnedParticipant != null && alreadyPinnedParticipant.UserId != status.PinnedUserId) + { + Debug.Console(1, this, "Participant: {0} with id: {1} already pinned to screenIndex {2}. Clearing pinned fb.", + alreadyPinnedParticipant.Name, alreadyPinnedParticipant.UserId, + alreadyPinnedParticipant.ScreenIndexIsPinnedToFb); + alreadyPinnedParticipant.IsPinnedFb = false; + alreadyPinnedParticipant.ScreenIndexIsPinnedToFb = -1; + } + } + + var participant = Participants.CurrentParticipants.FirstOrDefault(p => p.UserId.Equals(status.PinnedUserId)); + + if (participant != null) + { + participant.IsPinnedFb = true; + participant.ScreenIndexIsPinnedToFb = status.ScreenIndex; + } + else + { + participant = + Participants.CurrentParticipants.FirstOrDefault(p => p.ScreenIndexIsPinnedToFb.Equals(status.ScreenIndex)); + + if (participant == null && alreadyPinnedParticipant == null) + { + Debug.Console(1, this, "no matching participant found by pinned_user_id: {0} or screen_index: {1}", + status.PinnedUserId, status.ScreenIndex); + return; + } + else if (participant != null) + { + Debug.Console(2, this, "Unpinning {0} with id: {1} from screen index: {2}", participant.Name, + participant.UserId, status.ScreenIndex); + participant.IsPinnedFb = false; + participant.ScreenIndexIsPinnedToFb = -1; + } + } + + // fire the event as we've modified the participants list + Participants.OnParticipantsChanged(); + + break; + } + case "startlocalpresentmeeting": + { + var result = JsonConvert.DeserializeObject(responseObj.ToString()); + + if (result.Success) + { + MeetingInfo = new MeetingInfo("", "", "", "", "", true, true, MeetingInfo.WaitingForHost); + break; + } + + break; + } + default: + { + break; + } } + break; + } + case eZoomRoomResponseType.zStatus: + { + switch (topKey.ToLower()) + { + case "login": + { + _syncState.LoginMessageReceived(); + + if (!_syncState.InitialQueryMessagesWereSent) + { + SetUpSyncQueries(); + } + + JsonConvert.PopulateObject(responseObj.ToString(), Status.Login); + + break; + } + case "systemunit": + { + JsonConvert.PopulateObject(responseObj.ToString(), Status.SystemUnit); + + break; + } + case "call": + { + JsonConvert.PopulateObject(responseObj.ToString(), Status.Call); + + Debug.Console(1, this, + "[DeserializeResponse] zStatus.call - Status.Call.Info.meeting_id: {0} Status.Call.Info.meeting_list_item.meetingName: {1}", + Status.Call.Info.meeting_id, Status.Call.Info.meeting_list_item.meetingName); + foreach (var participant in Status.Call.Participants) + { + Debug.Console(1, this, + "[DeserializeResponse] zStatus.call - Status.Call.Participants participant.UserId: {0} participant.UserName: {1}", + participant.UserId, participant.UserName); + } + + 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; + } } } catch (Exception ex) { - Debug.Console(1, this, "Error Deserializing feedback: {0}", ex); + Debug.Console(1, this, "Error Deserializing feedback: {0}", ex.Message); + Debug.Console(2, this, "{0}", ex); + + if (ex.InnerException != null) + { + Debug.Console(1, this,"Error Deserializing feedback inner exception: {0}", ex.InnerException.Message); + Debug.Console(2, this, "{0}", ex.InnerException.StackTrace); + } } } @@ -1535,18 +1706,26 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom } } + /// + /// Retrieves the current call participants list + /// + public void GetCurrentCallParticipants() + { + SendText("zCommand Call ListParticipants"); + } + + /// + /// Prints the current call particiapnts list + /// public void PrintCurrentCallParticipants() { - if (Debug.Level <= 0) - { - return; - } + if (Debug.Level <= 0) 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, "UserId: {3} Name: {0} Audio: {1} IsHost: {2}", + participant.Name, participant.AudioMuteFb, participant.IsHost, participant.UserId); } Debug.Console(1, this, "************************************************************************"); } @@ -1565,20 +1744,21 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom /// private void UpdateCallStatus() { - Debug.Console(1, this, "[UpdateCallStatus] Current Call Status: {0}", - Status.Call != null ? Status.Call.Sharing.State.ToString() : "no call"); + Debug.Console(1, this, + "[UpdateCallStatus] Current Call Status: {0} Active Call Count: {1} Need Wait For Host: {2}", + Status.Call != null ? Status.Call.Status.ToString() : "no call", ActiveCalls.Count, Status.NeedWaitForHost.Wait); if (Status.Call != null) { var callStatus = Status.Call.Status; - // If not currently in a meeting, intialize the call object + // If not crrently in a meeting, intialize the call object if (callStatus != zStatus.eCallStatus.IN_MEETING && callStatus != zStatus.eCallStatus.CONNECTING_MEETING) { - Debug.Console(1, this, "Creating new Status.Call object"); - Status.Call = new zStatus.Call { Status = callStatus }; + //Debug.Console(1, this, "[UpdateCallStatus] Creating new Status.Call object"); + Status.Call = new zStatus.Call {Status = callStatus}; - OnCallStatusChange( new CodecActiveCallItem() { Status = eCodecCallStatus.Disconnected }); + OnCallStatusChange(new CodecActiveCallItem() {Status = eCodecCallStatus.Disconnected}); SetUpCallFeedbackActions(); } @@ -1586,7 +1766,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom if (ActiveCalls.Count == 0) { if (callStatus == zStatus.eCallStatus.CONNECTING_MEETING || - callStatus == zStatus.eCallStatus.IN_MEETING) + callStatus == zStatus.eCallStatus.IN_MEETING) { var newStatus = eCodecCallStatus.Unknown; @@ -1600,24 +1780,45 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom break; } - var newCall = new CodecActiveCallItem - { - Name = Status.Call.Info.meeting_list_item.meetingName, - Number = Status.Call.Info.meeting_id, - Id = Status.Call.Info.meeting_id, - Status = newStatus, - Type = eCodecCallType.Video, - }; + if (!string.IsNullOrEmpty(Status.Call.Info.meeting_id)) + { + var newCall = new CodecActiveCallItem + { + Name = Status.Call.Info.meeting_list_item.meetingName, + Number = Status.Call.Info.meeting_list_item.meetingNumber, + Id = Status.Call.Info.meeting_id, + Status = newStatus, + Type = eCodecCallType.Video, + }; - ActiveCalls.Add(newCall); + if (!String.IsNullOrEmpty(_lastDialedMeetingNumber)) + { + _lastDialedMeetingNumber = String.Empty; + } + ActiveCalls.Add(newCall); - Debug.Console(1, this, "[UpdateCallStatus] Current Call Status: {0}", - Status.Call != null ? Status.Call.Sharing.State.ToString() : "no call"); + OnCallStatusChange(newCall); + } else if (String.IsNullOrEmpty(Status.Call.Info.meeting_id) && Status.NeedWaitForHost.Wait) + { + var newCall = new CodecActiveCallItem + { + Name = "Waiting For Host", + Number = "Waiting For Host", + Id = "Waiting For Host", + Status = newStatus, + Type = eCodecCallType.Video, + }; - OnCallStatusChange(newCall); + if (!String.IsNullOrEmpty(_lastDialedMeetingNumber)) + { + _lastDialedMeetingNumber = String.Empty; + } + + ActiveCalls.Add(newCall); + + OnCallStatusChange(newCall); + } } - - } else { @@ -1626,21 +1827,30 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom switch (callStatus) { case zStatus.eCallStatus.IN_MEETING: + if (Status.NeedWaitForHost.Wait) + { + Status.NeedWaitForHost.Wait = false; + } existingCall.Status = eCodecCallStatus.Connected; break; case zStatus.eCallStatus.NOT_IN_MEETING: + if (Status.NeedWaitForHost.Wait) + { + Status.NeedWaitForHost.Wait = false; + } existingCall.Status = eCodecCallStatus.Disconnected; break; } - Debug.Console(1, this, "[UpdateCallStatus] Current Call Status: {0}", - Status.Call != null ? Status.Call.Sharing.State.ToString() : "no call"); + Debug.Console(1, this, "[UpdateCallStatus] ELSE ActiveCalls.Count == {1} - Current Call Status: {0}", + Status.Call != null ? Status.Call.Status.ToString() : "no call", ActiveCalls.Count); + OnCallStatusChange(existingCall); } } - Debug.Console(1, this, "*************************** Active Calls ********************************"); + Debug.Console(1, this, "[UpdateCallStatus] Active Calls ------------------------------"); // Clean up any disconnected calls left in the list for (int i = 0; i < ActiveCalls.Count; i++) @@ -1648,34 +1858,55 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom var call = ActiveCalls[i]; Debug.Console(1, this, - @"Name: {0} - ID: {1} + @"ID: {1} + Number: {5} + Name: {0} IsActive: {2} Status: {3} - Direction: {4}", call.Name, call.Id, call.IsActiveCall, call.Status, call.Direction); + Direction: {4} + IsActiveCall: {6}", call.Name, call.Id, call.IsActiveCall, call.Status, call.Direction, call.Number, + call.IsActiveCall); if (!call.IsActiveCall) { - Debug.Console(1, this, "***** Removing Inactive Call: {0} *****", call.Name); + Debug.Console(1, this, "[UpdateCallStatus] Removing Inactive call.Id: {1} call.Name: {0}", call.Name, call.Id); ActiveCalls.Remove(call); } } - Debug.Console(1, this, "**************************************************************************"); + Debug.Console(1, this, "[UpdateCallStatus] Active Calls ------------------------------"); //clear participants list after call cleanup - if (ActiveCalls.Count == 0) - { - var emptyList = new List(); - Participants.CurrentParticipants = emptyList; - } + var emptyList = new List(); + Participants.CurrentParticipants = emptyList; + if (ActiveCalls.Count > 0) GetCurrentCallParticipants(); } protected override void OnCallStatusChange(CodecActiveCallItem item) { - base.OnCallStatusChange(item); + if (item.Status == eCodecCallStatus.Connected) + { + + var host = ""; + + if (Participants.Host != null) + host = Participants.Host.Name; + + MeetingInfo = new MeetingInfo( + Status.Call.Info.meeting_id, + Status.Call.Info.meeting_list_item.meetingName, + host, + Status.Call.Info.meeting_password, + GetSharingStatus(), + GetIsHostMyself(), + !String.Equals(Status.Call.Info.meeting_type,"NORMAL"), + false + ); + } + + base.OnCallStatusChange(item); Debug.Console(1, this, "[OnCallStatusChange] Current Call Status: {0}", - Status.Call != null ? Status.Call.Sharing.State.ToString() : "no call"); + Status.Call != null ? Status.Call.Status.ToString() : "no call"); if (_props.AutoDefaultLayouts) { @@ -1683,6 +1914,71 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom } } + private string GetSharingStatus() + { + string sharingState = "None"; + + try + { + if (Status.Call.Sharing.State == zEvent.eSharingState.Receiving) + { + sharingState = "Receiving Content"; + } + if (Status.Sharing.isAirHostClientConnected) + { + sharingState = "Sharing AirPlay"; + } + if (Status.Sharing.isDirectPresentationConnected) + { + sharingState = "Sharing Laptop"; + } + if (Status.Sharing.isSharingBlackMagic) + { + sharingState = "Sharing HDMI Source"; + } + + return sharingState; + } + catch (Exception e) + { + Debug.Console(1, this, "Exception getting sharing status: {0}", e.Message); + Debug.Console(2, this, "{0}", e.StackTrace); + return sharingState; + } + } + + /// + /// Will return true if the host is myself (this zoom room) + /// + /// + private bool GetIsHostMyself() + { + try + { + if (Participants.CurrentParticipants.Count == 0) + { + Debug.Console(2, this, "No current participants"); + return false; + } + + var host = Participants.Host; + + if(host == null) + { + Debug.Console(2, this, "Host is currently null"); + return false; + } + Debug.Console(2, this, "Host is: '{0}' IsMyself?: {1}", host.Name, host.IsMyself); + return host.IsMyself; + } + catch (Exception e) + { + Debug.Console(1, "Exception getting isHost: {0}", e.Message); + Debug.Console(2, "{0}", e.StackTrace); + return false; + } + } + public override void StartSharing() { SendText("zCommand Call Sharing HDMI Start"); @@ -1693,7 +1989,14 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom /// public override void StopSharing() { - SendText("zCommand Call Sharing Disconnect"); + if (Status.Sharing.isSharingBlackMagic) + { + SendText("zCommand Call Sharing HDMI Stop"); + } + else + { + SendText("zCommand Call Sharing Disconnect"); + } } public override void PrivacyModeOn() @@ -1720,13 +2023,12 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom public override void MuteOff() { - Debug.Console(2, this, "Unmuting to previous level: {0}", _previousVolumeLevel); - SetVolume((ushort)_previousVolumeLevel); + Debug.Console(2, this, "Unmuting to previous level: {0}", _previousVolumeLevel); + SetVolume((ushort) _previousVolumeLevel); } public override void MuteOn() { - SetVolume(0); } @@ -1812,7 +2114,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom LinkVideoCodecToApi(this, trilist, joinMap); - LinkZoomRoomToApi(trilist, joinMap); + LinkZoomRoomToApi(trilist, joinMap); } /// @@ -1828,13 +2130,20 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom layoutsCodec.AvailableLayoutsChanged += (o, a) => { trilist.SetBool(joinMap.LayoutGalleryIsAvailable.JoinNumber, zConfiguration.eLayoutStyle.Gallery - == (a.AvailableLayouts & zConfiguration.eLayoutStyle.Gallery)); + == + (a.AvailableLayouts & + zConfiguration.eLayoutStyle.Gallery)); trilist.SetBool(joinMap.LayoutSpeakerIsAvailable.JoinNumber, zConfiguration.eLayoutStyle.Speaker - == (a.AvailableLayouts & zConfiguration.eLayoutStyle.Speaker)); + == + (a.AvailableLayouts & + zConfiguration.eLayoutStyle.Speaker)); trilist.SetBool(joinMap.LayoutStripIsAvailable.JoinNumber, zConfiguration.eLayoutStyle.Strip - == (a.AvailableLayouts & zConfiguration.eLayoutStyle.Strip)); + == + (a.AvailableLayouts & zConfiguration.eLayoutStyle.Strip)); trilist.SetBool(joinMap.LayoutShareAllIsAvailable.JoinNumber, zConfiguration.eLayoutStyle.ShareAll - == (a.AvailableLayouts & zConfiguration.eLayoutStyle.ShareAll)); + == + (a.AvailableLayouts & + zConfiguration.eLayoutStyle.ShareAll)); // pass the names used to set the layout through the bridge trilist.SetString(joinMap.LayoutGalleryIsAvailable.JoinNumber, zConfiguration.eLayoutStyle.Gallery.ToString()); @@ -1842,32 +2151,35 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom trilist.SetString(joinMap.LayoutStripIsAvailable.JoinNumber, zConfiguration.eLayoutStyle.Strip.ToString()); trilist.SetString(joinMap.LayoutShareAllIsAvailable.JoinNumber, zConfiguration.eLayoutStyle.ShareAll.ToString()); }; - + trilist.SetSigFalseAction(joinMap.SwapContentWithThumbnail.JoinNumber, () => layoutsCodec.SwapContentWithThumbnail()); - layoutsCodec.CanSwapContentWithThumbnailFeedback.LinkInputSig(trilist.BooleanInput[joinMap.CanSwapContentWithThumbnail.JoinNumber]); - layoutsCodec.ContentSwappedWithThumbnailFeedback.LinkInputSig(trilist.BooleanInput[joinMap.SwapContentWithThumbnail.JoinNumber]); + layoutsCodec.CanSwapContentWithThumbnailFeedback.LinkInputSig( + trilist.BooleanInput[joinMap.CanSwapContentWithThumbnail.JoinNumber]); + layoutsCodec.ContentSwappedWithThumbnailFeedback.LinkInputSig( + trilist.BooleanInput[joinMap.SwapContentWithThumbnail.JoinNumber]); - layoutsCodec.LayoutViewIsOnFirstPageFeedback.LinkInputSig(trilist.BooleanInput[joinMap.LayoutIsOnFirstPage.JoinNumber]); + layoutsCodec.LayoutViewIsOnFirstPageFeedback.LinkInputSig( + trilist.BooleanInput[joinMap.LayoutIsOnFirstPage.JoinNumber]); layoutsCodec.LayoutViewIsOnLastPageFeedback.LinkInputSig(trilist.BooleanInput[joinMap.LayoutIsOnLastPage.JoinNumber]); trilist.SetSigFalseAction(joinMap.LayoutTurnToNextPage.JoinNumber, () => layoutsCodec.LayoutTurnNextPage()); trilist.SetSigFalseAction(joinMap.LayoutTurnToPreviousPage.JoinNumber, () => layoutsCodec.LayoutTurnPreviousPage()); trilist.SetSigFalseAction(joinMap.GetAvailableLayouts.JoinNumber, () => layoutsCodec.GetAvailableLayouts()); trilist.SetStringSigAction(joinMap.GetSetCurrentLayout.JoinNumber, (s) => + { + try { - try - { - var style = (zConfiguration.eLayoutStyle)Enum.Parse(typeof(zConfiguration.eLayoutStyle), s, true); - SetLayout(style); - } - catch (Exception e) - { - Debug.Console(1, this, "Unable to parse '{0}' to zConfiguration.eLayoutStyle: {1}", s, e); - } - }); + var style = (zConfiguration.eLayoutStyle) Enum.Parse(typeof (zConfiguration.eLayoutStyle), s, true); + SetLayout(style); + } + catch (Exception e) + { + Debug.Console(1, this, "Unable to parse '{0}' to zConfiguration.eLayoutStyle: {1}", s, e); + } + }); - layoutsCodec.LocalLayoutFeedback.LinkInputSig(trilist.StringInput[joinMap.GetSetCurrentLayout.JoinNumber]); + layoutsCodec.LocalLayoutFeedback.LinkInputSig(trilist.StringInput[joinMap.GetSetCurrentLayout.JoinNumber]); } var pinCodec = this as IHasParticipantPinUnpin; @@ -1888,7 +2200,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom { try { - var size = (zConfiguration.eLayoutSize)Enum.Parse(typeof(zConfiguration.eLayoutSize), s, true); + var size = (zConfiguration.eLayoutSize) Enum.Parse(typeof (zConfiguration.eLayoutSize), s, true); var cmd = SelfviewPipSizes.FirstOrDefault(c => c.Command.Equals(size.ToString())); SelfviewPipSizeSet(cmd); } @@ -1906,7 +2218,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom if (!args.DeviceOnLine) return; ComputeAvailableLayouts(); - layoutsCodec.LocalLayoutFeedback.FireUpdate(); + layoutsCodec.LocalLayoutFeedback.FireUpdate(); layoutsCodec.CanSwapContentWithThumbnailFeedback.FireUpdate(); layoutsCodec.ContentSwappedWithThumbnailFeedback.FireUpdate(); layoutsCodec.LayoutViewIsOnFirstPageFeedback.FireUpdate(); @@ -1914,7 +2226,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom pinCodec.NumberOfScreensFeedback.FireUpdate(); layoutSizeCodec.SelfviewPipSizeFeedback.FireUpdate(); }; - } + } public override void ExecuteSwitch(object selector) { @@ -1970,14 +2282,28 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom public override void Dial(Meeting meeting) { Debug.Console(1, this, "Dialing meeting.Id: {0} Title: {1}", meeting.Id, meeting.Title); + _lastDialedMeetingNumber = meeting.Id; SendText(string.Format("zCommand Dial Start meetingNumber: {0}", meeting.Id)); } public override void Dial(string number) { + Debug.Console(2, this, "Dialing number: {0}", number); + _lastDialedMeetingNumber = number; SendText(string.Format("zCommand Dial Join meetingNumber: {0}", number)); } + /// + /// Dials a meeting with a password + /// + /// + /// + public void Dial(string number, string password) + { + Debug.Console(2, this, "Dialing meeting number: {0} with password: {1}", number, password); + SendText(string.Format("zCommand Dial Join meetingNumber: {0} password: {1}", number, password)); + } + /// /// Invites a contact to either a new meeting (if not already in a meeting) or the current meeting. /// Currently only invites a single user @@ -1985,7 +2311,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom /// public override void Dial(IInvitableContact contact) { - var ic = contact as zStatus.ZoomDirectoryContact; + var ic = contact as InvitableDirectoryContact; if (ic != null) { @@ -2003,6 +2329,26 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom } } + + /// + /// Starts a PMI Meeting for the specified duration (or default meeting duration if 0 is specified) + /// + /// duration of meeting + public void StartMeeting(uint duration) + { + uint dur = DefaultMeetingDurationMin; + + if (duration > 0) + dur = duration; + + SendText(string.Format("zCommand Dial StartPmi Duration: {0}", dur)); + } + + public void LeaveMeeting() + { + SendText("zCommand Call Leave"); + } + public override void EndCall(CodecActiveCallItem call) { SendText("zCommand Call Disconnect"); @@ -2024,43 +2370,45 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom /// private void OnDirectoryResultReturned(CodecDirectory result) { - try - { - Debug.Console(2, this, "OnDirectoryResultReturned"); + try + { + Debug.Console(2, this, "OnDirectoryResultReturned"); - var directoryResult = new CodecDirectory(); + var directoryResult = new CodecDirectory(); - // If result is Root, create a copy and filter out contacts whose parent folder is not root - if (!CurrentDirectoryResultIsNotDirectoryRoot.BoolValue) - { - Debug.Console(2, this, "Filtering DirectoryRoot to remove contacts for display"); + // If result is Root, create a copy and filter out contacts whose parent folder is not root + if (!CurrentDirectoryResultIsNotDirectoryRoot.BoolValue) + { + Debug.Console(2, this, "Filtering DirectoryRoot to remove contacts for display"); - directoryResult.ResultsFolderId = result.ResultsFolderId; - directoryResult.AddFoldersToDirectory(result.Folders); - directoryResult.AddContactsToDirectory(result.Contacts.Where((c) => c.ParentFolderId == result.ResultsFolderId).ToList()); - } - else - { - directoryResult = result; - } + directoryResult.ResultsFolderId = result.ResultsFolderId; + directoryResult.AddFoldersToDirectory(result.Folders); + directoryResult.AddContactsToDirectory( + result.Contacts.Where((c) => c.ParentFolderId == result.ResultsFolderId).ToList()); + } + else + { + directoryResult = result; + } - Debug.Console(2, this, "Updating directoryResult. IsOnRoot: {0}", !CurrentDirectoryResultIsNotDirectoryRoot.BoolValue); + Debug.Console(2, this, "Updating directoryResult. IsOnRoot: {0}", + !CurrentDirectoryResultIsNotDirectoryRoot.BoolValue); - // This will return the latest results to all UIs. Multiple indendent UI Directory browsing will require a different methodology - var handler = DirectoryResultReturned; - if (handler != null) - { - handler(this, new DirectoryEventArgs - { - Directory = directoryResult, - DirectoryIsOnRoot = !CurrentDirectoryResultIsNotDirectoryRoot.BoolValue - }); - } - } - catch (Exception e) - { - Debug.Console(2, this, "Error: {0}", e); - } + // This will return the latest results to all UIs. Multiple indendent UI Directory browsing will require a different methodology + var handler = DirectoryResultReturned; + if (handler != null) + { + handler(this, new DirectoryEventArgs + { + Directory = directoryResult, + DirectoryIsOnRoot = !CurrentDirectoryResultIsNotDirectoryRoot.BoolValue + }); + } + } + catch (Exception e) + { + Debug.Console(2, this, "Error: {0}", e); + } //PrintDirectory(result); } @@ -2076,15 +2424,15 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom foreach (var cam in Status.Cameras) { - // Known Issue: - // Crestron UC engine systems seem to report an item in the cameras list that represnts the USB bridge device. - // If we know the name and it's reliably consistent, we could ignore it here... + // Known Issue: + // Crestron UC engine systems seem to report an item in the cameras list that represnts the USB bridge device. + // If we know the name and it's reliably consistent, we could ignore it here... - if (cam.Name.IndexOf("HD-CONV-USB") > -1) - { - // Skip this as it's the Crestron USB box, not a real camera - continue; - } + if (cam.Name.IndexOf("HD-CONV-USB") > -1) + { + // Skip this as it's the Crestron USB box, not a real camera + continue; + } var camera = new ZoomRoomCamera(cam.id, cam.Name, this); @@ -2188,7 +2536,10 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom #region IHasParticipantPinUnpin Members - private Func NumberOfScreensFeedbackFunc { get { return () => Status.NumberOfScreens.NumOfScreens; } } + private Func NumberOfScreensFeedbackFunc + { + get { return () => Status.NumberOfScreens.NumOfScreens; } + } public IntFeedback NumberOfScreensFeedback { get; private set; } @@ -2313,12 +2664,12 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom } 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") - }; + { + new CodecCommandWithLabel("UpLeft", "Center Left"), + new CodecCommandWithLabel("UpRight", "Center Right"), + new CodecCommandWithLabel("DownRight", "Lower Right"), + new CodecCommandWithLabel("DownLeft", "Lower Left") + }; private void ComputeSelfviewPipPositionStatus() { @@ -2330,6 +2681,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom #endregion // TODO: #714 [ ] Implementation of IHasSelfviewPipSize + #region Implementation of IHasSelfviewPipSize private CodecCommandWithLabel _currentSelfviewPipSize; @@ -2356,13 +2708,13 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom } public List SelfviewPipSizes = new List() - { - new CodecCommandWithLabel("Off", "Off"), - new CodecCommandWithLabel("Size1", "Size 1"), - new CodecCommandWithLabel("Size2", "Size 2"), - new CodecCommandWithLabel("Size3", "Size 3"), + { + new CodecCommandWithLabel("Off", "Off"), + new CodecCommandWithLabel("Size1", "Size 1"), + new CodecCommandWithLabel("Size2", "Size 2"), + new CodecCommandWithLabel("Size3", "Size 3"), new CodecCommandWithLabel("Strip", "Strip") - }; + }; private void ComputeSelfviewPipSizeStatus() { @@ -2371,14 +2723,24 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom p => p.Command.ToLower().Equals(Configuration.Call.Layout.Size.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; } } + 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; } @@ -2405,10 +2767,25 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom public event EventHandler AvailableLayoutsChanged; - private Func LayoutViewIsOnFirstPageFeedbackFunc { get { return () => Status.Layout.is_In_First_Page; } } - private Func LayoutViewIsOnLastPageFeedbackFunc { get { return () => Status.Layout.is_In_Last_Page; } } - private Func CanSwapContentWithThumbnailFeedbackFunc { get { return () => Status.Layout.can_Switch_Floating_Share_Content; } } - private Func ContentSwappedWithThumbnailFeedbackFunc { get { return () => Configuration.Call.Layout.ShareThumb; } } + private Func LayoutViewIsOnFirstPageFeedbackFunc + { + get { return () => Status.Layout.is_In_First_Page; } + } + + private Func LayoutViewIsOnLastPageFeedbackFunc + { + get { return () => Status.Layout.is_In_Last_Page; } + } + + private Func CanSwapContentWithThumbnailFeedbackFunc + { + get { return () => Status.Layout.can_Switch_Floating_Share_Content; } + } + + private Func ContentSwappedWithThumbnailFeedbackFunc + { + get { return () => Configuration.Call.Layout.ShareThumb; } + } public BoolFeedback LayoutViewIsOnFirstPageFeedback { get; private set; } @@ -2452,12 +2829,12 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom availableLayouts |= zConfiguration.eLayoutStyle.Strip; } - Debug.Console(1, this, "availablelayouts: {0}", availableLayouts); + Debug.Console(1, this, "availablelayouts: {0}", availableLayouts); var handler = AvailableLayoutsChanged; if (handler != null) { - handler(this, new LayoutInfoChangedEventArgs() { AvailableLayouts = availableLayouts }); + handler(this, new LayoutInfoChangedEventArgs() {AvailableLayouts = availableLayouts}); } AvailableLayouts = availableLayouts; @@ -2478,7 +2855,8 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom { if (CanSwapContentWithThumbnailFeedback.BoolValue) { - var oppositeValue = ContentSwappedWithThumbnailFeedback.BoolValue ? "on" : "off"; // Get the value based on the opposite of the current state + var oppositeValue = ContentSwappedWithThumbnailFeedback.BoolValue ? "on" : "off"; + // Get the value based on the opposite of the current state // TODO: #697 [*] Need to verify the ternary above and make sure that the correct on/off value is being send based on the true/false value of the feedback // to toggle the state SendText(String.Format("zConfiguration Call Layout ShareThumb: {0}", oppositeValue)); @@ -2499,68 +2877,69 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom #region IHasCodecLayouts Members - private Func LocalLayoutFeedbackFunc - { - get - { - return () => - { - if (Configuration.Call.Layout.Style != zConfiguration.eLayoutStyle.None) - return Configuration.Call.Layout.Style.ToString(); - else - return Configuration.Client.Call.Layout.Style.ToString(); - }; - } - } + private Func LocalLayoutFeedbackFunc + { + get + { + return () => + { + if (Configuration.Call.Layout.Style != zConfiguration.eLayoutStyle.None) + return Configuration.Call.Layout.Style.ToString(); + else + return Configuration.Client.Call.Layout.Style.ToString(); + }; + } + } public StringFeedback LocalLayoutFeedback { get; private set; } public void LocalLayoutToggle() { - var currentLayout = LocalLayoutFeedback.StringValue; + var currentLayout = LocalLayoutFeedback.StringValue; - var eCurrentLayout = (int)Enum.Parse(typeof(zConfiguration.eLayoutStyle), currentLayout, true); + var eCurrentLayout = (int) Enum.Parse(typeof (zConfiguration.eLayoutStyle), currentLayout, true); - var nextLayout = GetNextLayout(eCurrentLayout); + var nextLayout = GetNextLayout(eCurrentLayout); - if (nextLayout != zConfiguration.eLayoutStyle.None) - { - SetLayout(nextLayout); - } + if (nextLayout != zConfiguration.eLayoutStyle.None) + { + SetLayout(nextLayout); + } } - /// - /// Tries to get the next available layout - /// - /// - /// - private zConfiguration.eLayoutStyle GetNextLayout(int currentLayout) - { - if (AvailableLayouts == zConfiguration.eLayoutStyle.None) - { - return zConfiguration.eLayoutStyle.None; - } + /// + /// Tries to get the next available layout + /// + /// + /// + private zConfiguration.eLayoutStyle GetNextLayout(int currentLayout) + { + if (AvailableLayouts == zConfiguration.eLayoutStyle.None) + { + return zConfiguration.eLayoutStyle.None; + } - zConfiguration.eLayoutStyle nextLayout; + zConfiguration.eLayoutStyle nextLayout; - if (((zConfiguration.eLayoutStyle)currentLayout & zConfiguration.eLayoutStyle.ShareAll) == zConfiguration.eLayoutStyle.ShareAll) - { - nextLayout = zConfiguration.eLayoutStyle.Gallery; - } - else - { - nextLayout = (zConfiguration.eLayoutStyle)(currentLayout << 1); - } + if (((zConfiguration.eLayoutStyle) currentLayout & zConfiguration.eLayoutStyle.ShareAll) == + zConfiguration.eLayoutStyle.ShareAll) + { + nextLayout = zConfiguration.eLayoutStyle.Gallery; + } + else + { + nextLayout = (zConfiguration.eLayoutStyle) (currentLayout << 1); + } - if ((AvailableLayouts & nextLayout) == nextLayout) - { - return nextLayout; - } - else - { - return GetNextLayout((int)nextLayout); - } - } + if ((AvailableLayouts & nextLayout) == nextLayout) + { + return nextLayout; + } + else + { + return GetNextLayout((int) nextLayout); + } + } public void LocalLayoutToggleSingleProminent() { @@ -2570,10 +2949,86 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom public void MinMaxLayoutToggle() { throw new NotImplementedException(); - } + } #endregion + #region IPasswordPrompt Members + + public event EventHandler PasswordRequired; + + public void SubmitPassword(string password) + { + Debug.Console(2, this, "Password Submitted: {0}", password); + Dial(_lastDialedMeetingNumber, password); + } + + void OnPasswordRequired(bool lastAttemptIncorrect, bool loginFailed, bool loginCancelled, string message) + { + var handler = PasswordRequired; + if (handler != null) + { + handler(this, new PasswordPromptEventArgs(lastAttemptIncorrect, loginFailed, loginCancelled, message)); + } + } + + #endregion + + #region IHasMeetingInfo Members + + public event EventHandler MeetingInfoChanged; + + private MeetingInfo _meetingInfo; + + public MeetingInfo MeetingInfo + { + get { return _meetingInfo; } + private set + { + if (value != _meetingInfo) + { + _meetingInfo = value; + + var handler = MeetingInfoChanged; + if (handler != null) + { + handler(this, new MeetingInfoEventArgs(_meetingInfo)); + } + } + } + } + + #endregion + + #region Implementation of IHasPresentationOnlyMeeting + + public void StartSharingOnlyMeeting() + { + StartSharingOnlyMeeting(eSharingMeetingMode.None, 30, String.Empty); + } + + public void StartSharingOnlyMeeting(eSharingMeetingMode mode) + { + StartSharingOnlyMeeting(mode, 30, String.Empty); + } + + public void StartSharingOnlyMeeting(eSharingMeetingMode mode, ushort duration) + { + StartSharingOnlyMeeting(mode, duration, String.Empty); + } + + public void StartSharingOnlyMeeting(eSharingMeetingMode mode, ushort duration, string password) + { + SendText(String.Format("zCommand Dial Sharing Duration: {0} DisplayState: {1} Password: {2}", duration, mode, password)); + } + + public void StartNormalMeetingFromSharingOnlyMeeting() + { + Debug.Console(2, this, "Converting Sharing Meeting to Normal Meeting"); + SendText("zCommand call sharing ToNormal"); + } + + #endregion } /// @@ -2774,7 +3229,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom private void CheckSyncStatus() { if (LoginMessageWasReceived && InitialQueryMessagesWereSent && LastQueryResponseWasReceived && - CamerasHaveBeenSetUp) + CamerasHaveBeenSetUp) { InitialSyncComplete = true; Debug.Console(1, this, "Initial Codec Sync Complete!"); @@ -2790,7 +3245,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom { public ZoomRoomFactory() { - TypeNames = new List { "zoomroom" }; + TypeNames = new List {"zoomroom"}; } public override EssentialsDevice BuildDevice(DeviceConfig dc) 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 38238023..4c9b08c2 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 @@ -25,5 +25,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom //This layout will be selected when a call is connected and no content is being shared public string DefaultCallLayout { get; set; } + + public int MinutesBeforeMeetingStart { get; set; } } } \ No newline at end of file