diff --git a/PepperDashEssentials/UI/JoinConstants/UIBoolJoin.cs b/PepperDashEssentials/UI/JoinConstants/UIBoolJoin.cs index 81be9002..dd454d97 100644 --- a/PepperDashEssentials/UI/JoinConstants/UIBoolJoin.cs +++ b/PepperDashEssentials/UI/JoinConstants/UIBoolJoin.cs @@ -948,5 +948,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..67a5c6e2 100644 --- a/PepperDashEssentials/UI/JoinConstants/UIStringlJoin.cs +++ b/PepperDashEssentials/UI/JoinConstants/UIStringlJoin.cs @@ -118,6 +118,14 @@ namespace PepperDash.Essentials //----- through 3120 + /// + /// 3201 + /// + public const uint PasswordPromptMessageText = 3201; + /// + /// 3202 + /// + public const uint PasswordPromptPasswordText = 3202; /// /// 3812 diff --git a/PepperDashEssentials/UIDrivers/EssentialsHuddleVTC/EssentialsHuddleVtc1PanelAvFunctionsDriver.cs b/PepperDashEssentials/UIDrivers/EssentialsHuddleVTC/EssentialsHuddleVtc1PanelAvFunctionsDriver.cs index 8082a6cb..2d5bd1dd 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; @@ -470,12 +471,16 @@ namespace PepperDash.Essentials Debug.Console(0, "*#* Room on: {0}, lastMeetingDismissedId: {1} {2} *#*", CurrentRoom.OnFeedback.BoolValue, LastMeetingDismissedId, - lastMeetingDismissed != null ? lastMeetingDismissed.StartTime.ToShortTimeString() : ""); + lastMeetingDismissed != null ? lastMeetingDismissed.StartTime.ToString("t", Global.Culture) : ""); var meeting = meetings.LastOrDefault(m => m.Joinable); if (CurrentRoom.OnFeedback.BoolValue && lastMeetingDismissed == meeting) { + // meeting no longer joinable, hide popup + if(meeting == null) + HideNextMeetingPopup(); + return; } @@ -487,9 +492,11 @@ namespace PepperDash.Essentials } else { + + TriList.SetString(UIStringJoin.MeetingsOrContactMethodListTitleText, "Upcoming meeting"); - TriList.SetString(UIStringJoin.NextMeetingStartTimeText, meeting.StartTime.ToShortTimeString()); - TriList.SetString(UIStringJoin.NextMeetingEndTimeText, meeting.EndTime.ToShortTimeString()); + 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"); @@ -512,7 +519,7 @@ namespace PepperDash.Essentials // indexOf = 3, 4 meetings : if (indexOfNext < meetings.Count) TriList.SetString(UIStringJoin.NextMeetingFollowingMeetingText, - meetings[indexOfNext].StartTime.ToShortTimeString()); + meetings[indexOfNext].StartTime.ToString("t", Global.Culture)); else TriList.SetString(UIStringJoin.NextMeetingFollowingMeetingText, "No more meetings today"); @@ -1209,8 +1216,8 @@ namespace PepperDash.Essentials 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"; diff --git a/PepperDashEssentials/UIDrivers/VC/EssentialsVideoCodecUiDriver.cs b/PepperDashEssentials/UIDrivers/VC/EssentialsVideoCodecUiDriver.cs index 612f7eeb..f3f707c3 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; @@ -83,6 +84,9 @@ namespace PepperDash.Essentials.UIDrivers.VC StringBuilder SearchStringBuilder = new StringBuilder(); BoolFeedback SearchStringBackspaceVisibleFeedback; + StringFeedback PasswordStringFeedback; + StringBuilder PasswordStringBuilder = new StringBuilder(); + ModalDialog IncomingCallModal; eKeypadMode KeypadMode; @@ -180,8 +184,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]); @@ -199,6 +217,12 @@ namespace PepperDash.Essentials.UIDrivers.VC triList.SetSigHeldAction(UIBoolJoin.VCDirectoryBackspacePress, 500, StartSearchBackspaceRepeat, StopSearchBackspaceRepeat, SearchKeypadBackspacePress); + + if (Codec is IPasswordPrompt) + { + SetupPasswordPrompt(); + } + } catch (Exception e) { @@ -299,6 +323,7 @@ namespace PepperDash.Essentials.UIDrivers.VC { case eCodecCallStatus.Connected: // fire at SRL item + HidePasswordPrompt(); KeypadMode = eKeypadMode.DTMF; DialStringBuilder.Remove(0, DialStringBuilder.Length); DialStringFeedback.FireUpdate(); @@ -984,7 +1009,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 @@ -1328,7 +1353,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; @@ -1350,6 +1389,7 @@ namespace PepperDash.Essentials.UIDrivers.VC SearchStringKeypadCheckEnables(); kb.Show(); } + } /// @@ -1405,6 +1445,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 /// @@ -1418,6 +1484,11 @@ namespace PepperDash.Essentials.UIDrivers.VC Parent.Keyboard.KeyPress -= Keyboard_SearchKeyPress; } + void DetachPasswordKeyboard() + { + Parent.Keyboard.KeyPress -= Keyboard_PasswordKeyPress; + } + /// /// Shows the camera controls subpage /// @@ -1671,6 +1742,40 @@ namespace PepperDash.Essentials.UIDrivers.VC Parent.Keyboard.DisableGoButton(); } + /// + /// Clears the Password keypad + /// + void PasswordKeypadClear() + { + PasswordStringBuilder.Remove(0, SearchStringBuilder.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 @@ -1716,5 +1821,61 @@ 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); + } + } } } \ 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/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/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 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/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/Essentials Devices Common.csproj b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Essentials Devices Common.csproj index 21821d22..75d01b0f 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 @@ -123,6 +123,7 @@ + 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 bce321b9..f894141c 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 @@ -743,6 +743,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) 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..a01ec099 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasStartMeeting.cs @@ -0,0 +1,25 @@ +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); + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/VideoCodecBase.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/VideoCodecBase.cs index c5cff74e..d34c9e3b 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 @@ -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..28073eb8 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 @@ -236,14 +236,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 +294,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 }); @@ -927,6 +919,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; } + } } /// 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 5473cbb8..1b1a9629 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 @@ -25,10 +25,11 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom IRouting, IHasScheduleAwareness, IHasCodecCameras, IHasParticipants, IHasCameraOff, IHasCameraMute, IHasCameraAutoMode, IHasFarEndContentStatus, IHasSelfviewPosition, IHasPhoneDialing, IHasZoomRoomLayouts, IHasParticipantPinUnpin, - IHasParticipantAudioMute, IHasSelfviewSize + IHasParticipantAudioMute, IHasSelfviewSize, IPasswordPrompt, IHasStartMeeting { 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; @@ -45,12 +46,15 @@ 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); @@ -730,7 +734,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); } @@ -860,7 +864,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"); @@ -1358,7 +1362,20 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom } case "meetingneedspassword": { - // TODO: notify user to enter a password + 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": @@ -2050,9 +2067,21 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom public override void Dial(string 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 @@ -2060,7 +2089,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) { @@ -2078,6 +2107,21 @@ 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 override void EndCall(CodecActiveCallItem call) { SendText("zCommand Call Disconnect"); @@ -2681,7 +2725,28 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom } #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 + } /// /// Zoom Room specific info object