diff --git a/Essentials Core/PepperDashEssentialsBase/SmartObjects/SmartObjectHelperBase.cs b/Essentials Core/PepperDashEssentialsBase/SmartObjects/SmartObjectHelperBase.cs index 741eec45..6fadf491 100644 --- a/Essentials Core/PepperDashEssentialsBase/SmartObjects/SmartObjectHelperBase.cs +++ b/Essentials Core/PepperDashEssentialsBase/SmartObjects/SmartObjectHelperBase.cs @@ -6,6 +6,8 @@ using Crestron.SimplSharp; using Crestron.SimplSharpPro; using Crestron.SimplSharpPro.DeviceSupport; +using PepperDash.Core; + namespace PepperDash.Essentials.Core.SmartObjects { public class SmartObjectHelperBase @@ -33,13 +35,37 @@ namespace PepperDash.Essentials.Core.SmartObjects SmartObject.SigChange -= this.SmartObject_SigChange; } + /// + /// Helper to get a sig name with debugging when fail + /// + /// + /// public BoolOutputSig GetBoolOutputNamed(string name) { if (SmartObject.BooleanOutput.Contains(name)) return SmartObject.BooleanOutput[name]; + else + Debug.Console(0, "WARNING: Cannot get signal. Smart object {0} on trilist {1:x2} does not contain signal '{2}'", + SmartObject.ID, SmartObject.Device.ID, name); return null; } + /// + /// Sets action on signal after checking for existence. + /// + /// + /// + public void SetBoolAction(string name, Action a) + { + if (SmartObject.BooleanOutput.Contains(name)) + SmartObject.BooleanOutput[name].UserObject = a; + else + { + Debug.Console(0, "WARNING: Cannot set action. Smart object {0} on trilist {1:x2} does not contain signal '{2}'", + SmartObject.ID, SmartObject.Device.ID, name); + } + } + /// /// Standard Action listener /// diff --git a/Essentials Core/PepperDashEssentialsBase/SmartObjects/SmartObjectNumeric.cs b/Essentials Core/PepperDashEssentialsBase/SmartObjects/SmartObjectNumeric.cs index 5d234634..7e574242 100644 --- a/Essentials Core/PepperDashEssentialsBase/SmartObjects/SmartObjectNumeric.cs +++ b/Essentials Core/PepperDashEssentialsBase/SmartObjects/SmartObjectNumeric.cs @@ -10,6 +10,14 @@ namespace PepperDash.Essentials.Core.SmartObjects { public class SmartObjectNumeric : SmartObjectHelperBase { + /// + /// Defaults to "Misc_1". The name of the button in VTPro (Usually the text) + /// + public string Misc1SigName { get; set; } + /// + /// Defaults to "Misc_2". The name of the button in VTPro (Usually the text) + /// + public string Misc2SigName { get; set; } public BoolOutputSig Digit1 { get { return GetBoolOutputNamed("1"); } } public BoolOutputSig Digit2 { get { return GetBoolOutputNamed("2"); } } @@ -21,11 +29,13 @@ namespace PepperDash.Essentials.Core.SmartObjects public BoolOutputSig Digit8 { get { return GetBoolOutputNamed("8"); } } public BoolOutputSig Digit9 { get { return GetBoolOutputNamed("9"); } } public BoolOutputSig Digit0 { get { return GetBoolOutputNamed("0"); } } - public BoolOutputSig Misc1 { get { return GetBoolOutputNamed("Misc_1"); } } - public BoolOutputSig Misc2 { get { return GetBoolOutputNamed("Misc_2"); } } + public BoolOutputSig Misc1 { get { return GetBoolOutputNamed(Misc1SigName); } } + public BoolOutputSig Misc2 { get { return GetBoolOutputNamed(Misc2SigName); } } public SmartObjectNumeric(SmartObject so, bool useUserObjectHandler) : base(so, useUserObjectHandler) { + Misc1SigName = "Misc_1"; + Misc2SigName = "Misc_2"; } } } \ No newline at end of file diff --git a/Essentials Core/PepperDashEssentialsBase/SubpageReferencList/SubpageReferenceList.cs b/Essentials Core/PepperDashEssentialsBase/SubpageReferencList/SubpageReferenceList.cs index 83b0934d..65499990 100644 --- a/Essentials Core/PepperDashEssentialsBase/SubpageReferencList/SubpageReferenceList.cs +++ b/Essentials Core/PepperDashEssentialsBase/SubpageReferencList/SubpageReferenceList.cs @@ -53,7 +53,7 @@ namespace PepperDash.Essentials.Core // Fail cleanly if not defined if (triList.SmartObjects == null || triList.SmartObjects.Count == 0) { - Debug.Console(0, "TriList {0:X2} Smart objects not loaded", triList.ID, smartObjectId); + Debug.Console(0, "TriList {0:X2} Smart objects have not been loaded", triList.ID, smartObjectId); return; } if (triList.SmartObjects.TryGetValue(smartObjectId, out obj)) @@ -74,7 +74,8 @@ namespace PepperDash.Essentials.Core SRL.SigChange += new SmartObjectSigChangeEventHandler(SRL_SigChange); } else - Debug.Console(0, "TriList 0x{0:X2} Cannot load smart object {1}", triList.ID, smartObjectId); + Debug.Console(0, "ERROR: TriList 0x{0:X2} Cannot load smart object {1}. Verify correct SGD file is loaded", + triList.ID, smartObjectId); } /// diff --git a/Essentials Core/PepperDashEssentialsBase/Touchpanels/TriListExtensions.cs b/Essentials Core/PepperDashEssentialsBase/Touchpanels/TriListExtensions.cs index 690bdacf..be666c5f 100644 --- a/Essentials Core/PepperDashEssentialsBase/Touchpanels/TriListExtensions.cs +++ b/Essentials Core/PepperDashEssentialsBase/Touchpanels/TriListExtensions.cs @@ -17,7 +17,8 @@ namespace PepperDash.Essentials.Core public static class SigAndTriListExtensions { /// - /// Attaches Action to Sig's user object and returns the same Sig. + /// Attaches Action to Sig's user object and returns the same Sig. This provides no protection + /// from null sigs /// /// The BoolOutputSig to attach the Action to /// An action to run when sig is pressed and when released @@ -64,13 +65,20 @@ namespace PepperDash.Essentials.Core } /// - /// + /// Sets an action to a held sig /// - /// - /// - /// - /// + /// The sig public static BoolOutputSig SetSigHeldAction(this BasicTriList tl, uint sigNum, uint heldMs, Action heldAction) + { + return SetSigHeldAction(tl, sigNum, heldMs, heldAction, null); + } + + + /// + /// Sets an action to a held sig as well as a released-without-hold action + /// + /// The sig + public static BoolOutputSig SetSigHeldAction(this BasicTriList tl, uint sigNum, uint heldMs, Action heldAction, Action releaseAction) { CTimer heldTimer = null; return tl.SetBoolSigAction(sigNum, press => @@ -87,10 +95,12 @@ namespace PepperDash.Essentials.Core heldAction(); }, heldMs); } - else if (heldTimer != null) // released + { heldTimer.Stop(); - // could also revise this else to fire a released action as well as cancel the timer + if (releaseAction != null) + releaseAction(); + } }); } @@ -143,5 +153,21 @@ namespace PepperDash.Essentials.Core { return ClearSigAction(tl.StringOutput[sigNum]) as StringOutputSig; } - } + + /// + /// Helper method to set the value of a bool Sig on TriList + /// + public static void SetBool(this BasicTriList tl, uint sigNum, bool value) + { + tl.BooleanInput[sigNum].BoolValue = value; + } + + /// + /// Helper method to set the value of a string Sig on TriList + /// + public static void SetString(this BasicTriList tl, uint sigNum, string value) + { + tl.StringInput[sigNum].StringValue = value; + } + } } \ No newline at end of file diff --git a/Essentials Devices Common/Essentials Devices Common/Essentials Devices Common.csproj b/Essentials Devices Common/Essentials Devices Common/Essentials Devices Common.csproj index adf18e16..f7a4fab5 100644 --- a/Essentials Devices Common/Essentials Devices Common/Essentials Devices Common.csproj +++ b/Essentials Devices Common/Essentials Devices Common/Essentials Devices Common.csproj @@ -127,10 +127,7 @@ - - - - + diff --git a/Essentials Devices Common/Essentials Devices Common/VC/MockVC/MockVC.cs b/Essentials Devices Common/Essentials Devices Common/VC/MockVC/MockVC.cs new file mode 100644 index 00000000..d8f11150 --- /dev/null +++ b/Essentials Devices Common/Essentials Devices Common/VC/MockVC/MockVC.cs @@ -0,0 +1,191 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using PepperDash.Core; +using PepperDash.Essentials.Core; + +namespace PepperDash.Essentials.Devices.Common.VideoCodec +{ + public class MockVC : VideoCodecBase + { + public MockVC(string key, string name) + : base(key, name) + { + + } + + protected override Func InCallFeedbackFunc + { + get { return () => _InCall; } + } + bool _InCall; + + protected override Func IncomingCallFeedbackFunc + { + get { return () => _IncomingCall; } + } + bool _IncomingCall; + + protected override Func TransmitMuteFeedbackFunc + { + get { return () => _TransmitMute; } + } + bool _TransmitMute; + + protected override Func ReceiveMuteFeedbackFunc + { + get { return () => _ReceiveMute; } + } + bool _ReceiveMute; + + protected override Func PrivacyModeFeedbackFunc + { + get { return () => _PrivacyModeIsOn; } + } + bool _PrivacyModeIsOn; + + /// + /// Dials, yo! + /// + public override void Dial(string s) + { + + _InCall = true; + InCallFeedback.FireUpdate(); + } + + /// + /// Makes horrible tones go out on the wire! + /// + /// + public void SendDTMF(string s) + { + + } + + + public override void EndCall() + { + _InCall = false; + InCallFeedback.FireUpdate(); + } + + /// + /// For a call from the test methods below + /// + public override void AcceptCall() + { + + } + + /// + /// For a call from the test methods below + /// + public override void RejectCall() + { + + } + + + + public override void ExecuteSwitch(object selector) + { + + } + + public override void ReceiveMuteOff() + { + if (!_ReceiveMute) + return; + _ReceiveMute = false; + ReceiveMuteIsOnFeedback.FireUpdate(); + } + + public override void ReceiveMuteOn() + { + if (_ReceiveMute) + return; + ReceiveMuteIsOnFeedback.FireUpdate(); + } + + public override void ReceiveMuteToggle() + { + _ReceiveMute = !_ReceiveMute; + ReceiveMuteIsOnFeedback.FireUpdate(); + } + + public override void SetReceiveVolume(ushort level) + { + + } + + public override void TransmitMuteOff() + { + if (!_TransmitMute) + return; + _TransmitMute = false; + TransmitMuteIsOnFeedback.FireUpdate(); + } + + public override void TransmitMuteOn() + { + if (_TransmitMute) + return; + TransmitMuteIsOnFeedback.FireUpdate(); + } + + public override void TransmitMuteToggle() + { + _TransmitMute = !_TransmitMute; + TransmitMuteIsOnFeedback.FireUpdate(); + } + + public override void SetTransmitVolume(ushort level) + { + + } + + public override void PrivacyModeOn() + { + if (_PrivacyModeIsOn) + return; + _PrivacyModeIsOn = true; + PrivacyModeIsOnFeedback.FireUpdate(); + + } + + public override void PrivacyModeOff() + { + if (!_PrivacyModeIsOn) + return; + _PrivacyModeIsOn = false; + PrivacyModeIsOnFeedback.FireUpdate(); + } + + public override void PrivacyModeToggle() + { + _PrivacyModeIsOn = !_PrivacyModeIsOn; + PrivacyModeIsOnFeedback.FireUpdate(); + } + + //******************************************************** + // SIMULATION METHODS + + public void TestIncomingCall(string url) + { + _IncomingCall = true; + IncomingCallFeedback.FireUpdate(); + } + + public void TestFarEndHangup() + { + + } + + + + } +} \ No newline at end of file diff --git a/Essentials/PepperDashEssentials/Config/EssentialsConfig.cs b/Essentials/PepperDashEssentials/Config/EssentialsConfig.cs index b9ba2bc8..34d2f516 100644 --- a/Essentials/PepperDashEssentials/Config/EssentialsConfig.cs +++ b/Essentials/PepperDashEssentials/Config/EssentialsConfig.cs @@ -1,11 +1,13 @@ using System; using System.Collections.Generic; -using Crestron.SimplSharp.CrestronIO; +using System.Text.RegularExpressions; +using Crestron.SimplSharp.CrestronIO; using Newtonsoft.Json; + using PepperDash.Core; using PepperDash.Essentials.Core.Config; -using System.Text.RegularExpressions; +using PepperDash.Essentials.Room.Config; namespace PepperDash.Essentials { diff --git a/Essentials/PepperDashEssentials/ControlSystem.cs b/Essentials/PepperDashEssentials/ControlSystem.cs index 16451812..9e995b5e 100644 --- a/Essentials/PepperDashEssentials/ControlSystem.cs +++ b/Essentials/PepperDashEssentials/ControlSystem.cs @@ -59,12 +59,12 @@ namespace PepperDash.Essentials // CODEC TESTING - GenericSshClient TestCodecClient = new GenericSshClient("TestCodec-1--SshClient", "10.11.50.135", 22, "crestron", "2H3Zu&OvgXp6"); + //GenericSshClient TestCodecClient = new GenericSshClient("TestCodec-1--SshClient", "10.11.50.135", 22, "crestron", "2H3Zu&OvgXp6"); - PepperDash.Essentials.Devices.VideoCodec.Cisco.CiscoCodec TestCodec = - new PepperDash.Essentials.Devices.VideoCodec.Cisco.CiscoCodec("TestCodec-1", "Cisco Spark Room Kit", TestCodecClient, 8080); + //PepperDash.Essentials.Devices.VideoCodec.Cisco.CiscoCodec TestCodec = + // new PepperDash.Essentials.Devices.VideoCodec.Cisco.CiscoCodec("TestCodec-1", "Cisco Spark Room Kit", TestCodecClient, 8080); - TestCodec.CustomActivate(); + //TestCodec.CustomActivate(); // CODEC TESTING diff --git a/Essentials/PepperDashEssentials/Fusion/FusionSystemController.cs b/Essentials/PepperDashEssentials/Fusion/FusionSystemController.cs index 31cdc74a..f2568b8c 100644 --- a/Essentials/PepperDashEssentials/Fusion/FusionSystemController.cs +++ b/Essentials/PepperDashEssentials/Fusion/FusionSystemController.cs @@ -41,10 +41,10 @@ namespace PepperDash.Essentials.Fusion StringSigData CurrentRoomSourceNameSig; #region System Info Sigs - StringSigData SystemName; - StringSigData Model; - StringSigData SerialNumber; - StringSigData Uptime; + //StringSigData SystemName; + //StringSigData Model; + //StringSigData SerialNumber; + //StringSigData Uptime; #endregion diff --git a/Essentials/PepperDashEssentials/Fusion/FusionSystemController.cs.orig b/Essentials/PepperDashEssentials/Fusion/FusionSystemController.cs.orig index b90a99d5..f783b3c7 100644 --- a/Essentials/PepperDashEssentials/Fusion/FusionSystemController.cs.orig +++ b/Essentials/PepperDashEssentials/Fusion/FusionSystemController.cs.orig @@ -25,9 +25,9 @@ namespace PepperDash.Essentials.Fusion { public class EssentialsHuddleSpaceFusionSystemController : Device { - //public event EventHandler ScheduleChange; - //public event EventHandler MeetingEndWarning; - //public event EventHandler NextMeetingBeginWarning; + public event EventHandler ScheduleChange; + public event EventHandler MeetingEndWarning; + public event EventHandler NextMeetingBeginWarning; FusionRoom FusionRoom; EssentialsHuddleSpaceRoom Room; @@ -41,10 +41,10 @@ namespace PepperDash.Essentials.Fusion StringSigData CurrentRoomSourceNameSig; #region System Info Sigs - StringSigData SystemName; - StringSigData Model; - StringSigData SerialNumber; - StringSigData Uptime; + //StringSigData SystemName; + //StringSigData Model; + //StringSigData SerialNumber; + //StringSigData Uptime; #endregion @@ -788,6 +788,15 @@ namespace PepperDash.Essentials.Fusion if (!IsRegisteredForSchedulePushNotifications) PollTimer.Reset(SchedulePollInterval, SchedulePollInterval); + + // Fire Schedule Change Event + var handler = ScheduleChange; + + if (handler != null) + { + handler(this, new ScheduleChangeEventArgs() { Schedule = CurrentSchedule }); + } + } } @@ -806,20 +815,26 @@ namespace PepperDash.Essentials.Fusion } + /// + /// Prints today's schedule to console for debugging + /// void PrintTodaysSchedule() { - if (CurrentSchedule.Meetings.Count > 0) + if (Debug.Level > 1) { - Debug.Console(1, this, "Today's Schedule for '{0}'\n", Room.Name); - - foreach (Event e in CurrentSchedule.Meetings) + if (CurrentSchedule.Meetings.Count > 0) { - Debug.Console(1, this, "Subject: {0}", e.Subject); - Debug.Console(1, this, "Organizer: {0}", e.Organizer); - Debug.Console(1, this, "MeetingID: {0}", e.MeetingID); - Debug.Console(1, this, "Start Time: {0}", e.dtStart); - Debug.Console(1, this, "End Time: {0}", e.dtEnd); - Debug.Console(1, this, "Duration: {0}\n", e.DurationInMinutes); + Debug.Console(1, this, "Today's Schedule for '{0}'\n", Room.Name); + + foreach (Event e in CurrentSchedule.Meetings) + { + Debug.Console(1, this, "Subject: {0}", e.Subject); + Debug.Console(1, this, "Organizer: {0}", e.Organizer); + Debug.Console(1, this, "MeetingID: {0}", e.MeetingID); + Debug.Console(1, this, "Start Time: {0}", e.dtStart); + Debug.Console(1, this, "End Time: {0}", e.dtEnd); + Debug.Console(1, this, "Duration: {0}\n", e.DurationInMinutes); + } } } } @@ -888,13 +903,8 @@ namespace PepperDash.Essentials.Fusion /// /// void UsageTracker_DeviceUsageEnded(object sender, DeviceUsageEventArgs e) -<<<<<<< HEAD - { - var device = sender as Device; -======= { var deviceTracker = sender as UsageTracking; ->>>>>>> origin/feature/fusion-nyu var configDevice = ConfigReader.ConfigObject.Devices.Where(d => d.Key.Equals(deviceTracker.Parent)); @@ -960,6 +970,7 @@ namespace PepperDash.Essentials.Fusion string attrName = null; uint attrNum = Convert.ToUInt32(keyNum); +<<<<<<< HEAD if (dev is BasicTriListWithSmartObject) @@ -971,13 +982,32 @@ namespace PepperDash.Essentials.Fusion } // add xpanel here - if (dev is Crestron.SimplSharpPro.UI.XpanelForSmartGraphics) + //if (dev is Crestron.SimplSharpPro.UI.XpanelForSmartGraphics) + //{ + // if (attrNum > 10) + // continue; + // attrName = "Online - XPanel " + attrNum; + // attrNum += 160; + //} +======= + if (dev is EssentialsTouchpanelController) { - if (attrNum > 10) - continue; - attrName = "Online - XPanel " + attrNum; - attrNum += 160; - } + if ((dev as EssentialsTouchpanelController).Panel is Crestron.SimplSharpPro.DeviceSupport.TswFt5Button) + { + if (attrNum > 10) + continue; + attrName = "Online - Touch Panel " + attrNum; + attrNum += 150; + } + else if ((dev as EssentialsTouchpanelController).Panel is Crestron.SimplSharpPro.UI.XpanelForSmartGraphics) + { + if (attrNum > 10) + continue; + attrName = "Online - XPanel " + attrNum; + attrNum += 160; + } + } +>>>>>>> origin/feature/cisco-spark //else if (dev is DisplayBase) diff --git a/Essentials/PepperDashEssentials/PepperDashEssentials.csproj b/Essentials/PepperDashEssentials/PepperDashEssentials.csproj index 4e062dea..cafe9b6b 100644 --- a/Essentials/PepperDashEssentials/PepperDashEssentials.csproj +++ b/Essentials/PepperDashEssentials/PepperDashEssentials.csproj @@ -141,6 +141,9 @@ + + + @@ -151,37 +154,42 @@ - - - + + + + - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - + + + - - - + + + diff --git a/Essentials/PepperDashEssentials/Room/Config/EssentialsHuddleRoomPropertiesConfig.cs b/Essentials/PepperDashEssentials/Room/Config/EssentialsHuddleRoomPropertiesConfig.cs new file mode 100644 index 00000000..2044484d --- /dev/null +++ b/Essentials/PepperDashEssentials/Room/Config/EssentialsHuddleRoomPropertiesConfig.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +namespace PepperDash.Essentials.Room.Config +{ + /// + /// + /// + public class EssentialsHuddleRoomPropertiesConfig : EssentialsRoomPropertiesConfig + { + public string DefaultDisplayKey { get; set; } + public string DefaultAudioKey { get; set; } + public string SourceListKey { get; set; } + public string DefaultSourceItem { get; set; } + } +} \ No newline at end of file diff --git a/Essentials/PepperDashEssentials/Room/Config/EssentialsHuddleVtc1PropertiesConfig.cs b/Essentials/PepperDashEssentials/Room/Config/EssentialsHuddleVtc1PropertiesConfig.cs new file mode 100644 index 00000000..1d9051d5 --- /dev/null +++ b/Essentials/PepperDashEssentials/Room/Config/EssentialsHuddleVtc1PropertiesConfig.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +namespace PepperDash.Essentials.Room.Config +{ + + public class EssentialsHuddleVtc1PropertiesConfig : EssentialsRoomPropertiesConfig + { + public string DefaultDisplayKey { get; set; } + public string DefaultAudioKey { get; set; } + public string SourceListKey { get; set; } + public string DefaultSourceItem { get; set; } + } +} \ No newline at end of file diff --git a/Essentials/PepperDashEssentials/Room/Config/EssentialsPresentationPropertiesConfig.cs b/Essentials/PepperDashEssentials/Room/Config/EssentialsPresentationPropertiesConfig.cs new file mode 100644 index 00000000..a40e0fdc --- /dev/null +++ b/Essentials/PepperDashEssentials/Room/Config/EssentialsPresentationPropertiesConfig.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +namespace PepperDash.Essentials.Room.Config +{ + /// + /// + /// + public class EssentialsPresentationRoomPropertiesConfig : EssentialsRoomPropertiesConfig + { + public string DefaultAudioBehavior { get; set; } + public string DefaultAudioKey { get; set; } + public string DefaultVideoBehavior { get; set; } + public List DisplayKeys { get; set; } + public string SourceListKey { get; set; } + public bool HasDsp { get; set; } + + public EssentialsPresentationRoomPropertiesConfig() + { + DisplayKeys = new List(); + } + } +} \ No newline at end of file diff --git a/Essentials/PepperDashEssentials/Room/EssentialsRoomConfig.cs b/Essentials/PepperDashEssentials/Room/Config/EssentialsRoomConfig.cs similarity index 67% rename from Essentials/PepperDashEssentials/Room/EssentialsRoomConfig.cs rename to Essentials/PepperDashEssentials/Room/Config/EssentialsRoomConfig.cs index 89525d9e..7761d367 100644 --- a/Essentials/PepperDashEssentials/Room/EssentialsRoomConfig.cs +++ b/Essentials/PepperDashEssentials/Room/Config/EssentialsRoomConfig.cs @@ -2,16 +2,16 @@ using System.Collections.Generic; using System.Linq; using System.Text; -using System.Text.RegularExpressions; + using Crestron.SimplSharp; using Newtonsoft.Json; using PepperDash.Core; +using PepperDash.Essentials; using PepperDash.Essentials.Core; using PepperDash.Essentials.Core.Config; -using PepperDash.Essentials.DM; -namespace PepperDash.Essentials +namespace PepperDash.Essentials.Room.Config { public class EssentialsRoomConfig : DeviceConfig { @@ -54,6 +54,20 @@ namespace PepperDash.Essentials var presRoom = new EssentialsPresentationRoom(Key, Name, displaysDict, masterVolumeControlDev, props); return presRoom; } + else if (typeName == "huddlevtc1") + { + var props = JsonConvert.DeserializeObject + (this.Properties.ToString()); + var disp = DeviceManager.GetDeviceForKey(props.DefaultDisplayKey) as IRoutingSinkWithSwitching; + var rm = new EssentialsHuddleVtc1Room(Key, Name, disp, disp, props); + rm.LogoUrl = props.Logo.GetUrl(); + rm.SourceListKey = props.SourceListKey; + rm.DefaultSourceItem = props.DefaultSourceItem; + rm.DefaultVolume = (ushort)(props.Volumes.Master.Level * 65535 / 100); + + return rm; + } + return null; } } @@ -67,12 +81,50 @@ namespace PepperDash.Essentials public string Description { get; set; } public int ShutdownVacancySeconds { get; set; } public int ShutdownPromptSeconds { get; set; } + public EssentialsHelpPropertiesConfig Help { get; set; } + public EssentialsOneButtonMeetingPropertiesConfig OneButtonMeeting { get; set; } + public EssentialsRoomAddressPropertiesConfig Addresses { get; set; } public EssentialsRoomOccSensorConfig OccupancySensors { get; set; } public EssentialsLogoPropertiesConfig Logo { get; set; } public EssentialsRoomVolumesConfig Volumes { get; set; } } + /// + /// Properties for the help text box + /// + public class EssentialsHelpPropertiesConfig + { + public string Message { get; set; } + public bool ShowCallButton { get; set; } + /// + /// Defaults to "Call Help Desk" + /// + public string CallButtonText { get; set; } + public EssentialsHelpPropertiesConfig() + { + CallButtonText = "Call Help Desk"; + } + } + + /// + /// + /// + public class EssentialsOneButtonMeetingPropertiesConfig + { + public bool Enable { get; set; } + } + + public class EssentialsRoomAddressPropertiesConfig + { + public string PhoneNumber { get; set; } + public string SipAddress { get; set; } + } + + + /// + /// Properties for the room's logo on panels + /// public class EssentialsLogoPropertiesConfig { public string Type { get; set; } @@ -100,32 +152,4 @@ namespace PepperDash.Essentials public List Types { get; set; } } - /// - /// - /// - public class EssentialsHuddleRoomPropertiesConfig : EssentialsRoomPropertiesConfig - { - public string DefaultDisplayKey { get; set; } - public string DefaultAudioKey { get; set; } - public string SourceListKey { get; set; } - public string DefaultSourceItem { get; set; } - } - - /// - /// - /// - public class EssentialsPresentationRoomPropertiesConfig : EssentialsRoomPropertiesConfig - { - public string DefaultAudioBehavior { get; set; } - public string DefaultAudioKey { get; set; } - public string DefaultVideoBehavior { get; set; } - public List DisplayKeys { get; set; } - public string SourceListKey { get; set; } - public bool HasDsp { get; set; } - - public EssentialsPresentationRoomPropertiesConfig() - { - DisplayKeys = new List(); - } - } } \ No newline at end of file diff --git a/Essentials/PepperDashEssentials/Room/EssentialsHuddleSpaceRoom.cs b/Essentials/PepperDashEssentials/Room/Types/EssentialsHuddleSpaceRoom.cs similarity index 97% rename from Essentials/PepperDashEssentials/Room/EssentialsHuddleSpaceRoom.cs rename to Essentials/PepperDashEssentials/Room/Types/EssentialsHuddleSpaceRoom.cs index 455fcd9b..7685de18 100644 --- a/Essentials/PepperDashEssentials/Room/EssentialsHuddleSpaceRoom.cs +++ b/Essentials/PepperDashEssentials/Room/Types/EssentialsHuddleSpaceRoom.cs @@ -1,419 +1,420 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Crestron.SimplSharp; - -using PepperDash.Core; -using PepperDash.Essentials.Core; - -namespace PepperDash.Essentials -{ - public class EssentialsHuddleSpaceRoom : EssentialsRoomBase, IHasCurrentSourceInfoChange - { - public event EventHandler CurrentVolumeDeviceChange; - public event SourceInfoChangeHandler CurrentSingleSourceChange; - - protected override Func OnFeedbackFunc - { - get - { - return () => - { - var disp = DefaultDisplay as DisplayBase; - var val = CurrentSourceInfo != null - && CurrentSourceInfo.Type == eSourceListItemType.Route - && disp != null - && disp.PowerIsOnFeedback.BoolValue; - return val; - }; - } - } - /// - /// - /// - protected override Func IsWarmingFeedbackFunc - { - get - { - return () => - { - var disp = DefaultDisplay as DisplayBase; - if (disp != null) - return disp.IsWarmingUpFeedback.BoolValue; - else - return false; - }; - } - } - /// - /// - /// - protected override Func IsCoolingFeedbackFunc - { - get - { - return () => - { - var disp = DefaultDisplay as DisplayBase; - if (disp != null) - return disp.IsCoolingDownFeedback.BoolValue; - else - return false; - }; - } - } - - public EssentialsRoomPropertiesConfig Config { get; private set; } - - public IRoutingSinkWithSwitching DefaultDisplay { get; private set; } - public IRoutingSinkNoSwitching DefaultAudioDevice { get; private set; } - public IBasicVolumeControls DefaultVolumeControls { get; private set; } - - public bool ExcludeFromGlobalFunctions { get; set; } - - /// - /// The config name of the source list - /// - public string SourceListKey { get; set; } - - public string DefaultSourceItem { get; set; } - - public ushort DefaultVolume { get; set; } - - /// - /// If room is off, enables power on to last source. Default true - /// - public bool EnablePowerOnToLastSource { get; set; } - string LastSourceKey; - - /// - /// - /// - public IBasicVolumeControls CurrentVolumeControls - { - get { return _CurrentAudioDevice; } - set - { - if (value == _CurrentAudioDevice) return; - - var oldDev = _CurrentAudioDevice; - // derigister this room from the device, if it can - if (oldDev is IInUseTracking) - (oldDev as IInUseTracking).InUseTracker.RemoveUser(this, "audio"); - var handler = CurrentVolumeDeviceChange; - if (handler != null) - CurrentVolumeDeviceChange(this, new VolumeDeviceChangeEventArgs(oldDev, value, ChangeType.WillChange)); - _CurrentAudioDevice = value; - if (handler != null) - CurrentVolumeDeviceChange(this, new VolumeDeviceChangeEventArgs(oldDev, value, ChangeType.DidChange)); - // register this room with new device, if it can - if (_CurrentAudioDevice is IInUseTracking) - (_CurrentAudioDevice as IInUseTracking).InUseTracker.AddUser(this, "audio"); - } - } - IBasicVolumeControls _CurrentAudioDevice; - - /// - /// The SourceListItem last run - containing names and icons - /// - public SourceListItem CurrentSourceInfo - { - get { return _CurrentSourceInfo; } - private set - { - if (value == _CurrentSourceInfo) return; - - var handler = CurrentSingleSourceChange; - // remove from in-use tracker, if so equipped - if(_CurrentSourceInfo != null && _CurrentSourceInfo.SourceDevice is IInUseTracking) - (_CurrentSourceInfo.SourceDevice as IInUseTracking).InUseTracker.RemoveUser(this, "control"); - - if (handler != null) - handler(this, _CurrentSourceInfo, ChangeType.WillChange); - - _CurrentSourceInfo = value; - - // add to in-use tracking - if (_CurrentSourceInfo != null && _CurrentSourceInfo.SourceDevice is IInUseTracking) - (_CurrentSourceInfo.SourceDevice as IInUseTracking).InUseTracker.AddUser(this, "control"); - if (handler != null) - handler(this, _CurrentSourceInfo, ChangeType.DidChange); - } - } - SourceListItem _CurrentSourceInfo; - - public string CurrentSourceInfoKey { get; private set; } - - /// - /// - /// - /// - /// - public EssentialsHuddleSpaceRoom(string key, string name, IRoutingSinkWithSwitching defaultDisplay, - IRoutingSinkNoSwitching defaultAudio, EssentialsRoomPropertiesConfig config) - : base(key, name) - { - Config = config; - DefaultDisplay = defaultDisplay; - DefaultAudioDevice = defaultAudio; - if (defaultAudio is IBasicVolumeControls) - DefaultVolumeControls = defaultAudio as IBasicVolumeControls; - else if (defaultAudio is IHasVolumeDevice) - DefaultVolumeControls = (defaultAudio as IHasVolumeDevice).VolumeDevice; - CurrentVolumeControls = DefaultVolumeControls; - - var disp = DefaultDisplay as DisplayBase; - if (disp != null) - { - // Link power, warming, cooling to display - disp.PowerIsOnFeedback.OutputChange += (o, a) => - { - if (disp.PowerIsOnFeedback.BoolValue != OnFeedback.BoolValue) - { - if (!disp.PowerIsOnFeedback.BoolValue) - CurrentSourceInfo = null; - OnFeedback.FireUpdate(); - } - }; - - disp.IsWarmingUpFeedback.OutputChange += (o, a) => - { - IsWarmingUpFeedback.FireUpdate(); - if (!IsWarmingUpFeedback.BoolValue) - (DefaultDisplay as IBasicVolumeWithFeedback).SetVolume(DefaultVolume); - }; - disp.IsCoolingDownFeedback.OutputChange += (o, a) => - { - IsCoolingDownFeedback.FireUpdate(); - if (IsCoolingDownFeedback.BoolValue) - (DefaultDisplay as IBasicVolumeWithFeedback).SetVolume(DefaultVolume); - }; - } - - SourceListKey = "default"; - EnablePowerOnToLastSource = true; - } - - - /// - /// - /// - public override void Shutdown() - { - RunRouteAction("roomOff"); - } - - /// - /// Routes the default source item, if any - /// - public void RunDefaultRoute() - { - if (DefaultSourceItem != null) - RunRouteAction(DefaultSourceItem); - } - - /// - /// - /// - /// - public void RunRouteAction(string routeKey) - { - RunRouteAction(routeKey, null); - } - - /// - /// Gets a source from config list SourceListKey and dynamically build and executes the - /// route or commands - /// - /// - public void RunRouteAction(string routeKey, Action successCallback) - { - // Run this on a separate thread - new CTimer(o => - { - Debug.Console(1, this, "Run route action '{0}'", routeKey); - var dict = ConfigReader.ConfigObject.GetSourceListForKey(SourceListKey); - if(dict == null) - { - Debug.Console(1, this, "WARNING: Config source list '{0}' not found", SourceListKey); - return; - } - - // Try to get the list item by it's string key - if (!dict.ContainsKey(routeKey)) - { - Debug.Console(1, this, "WARNING: No item '{0}' found on config list '{1}'", - routeKey, SourceListKey); - return; - } - - var item = dict[routeKey]; - //Debug.Console(2, this, "Action {0} has {1} steps", - // item.SourceKey, item.RouteList.Count); - - // End usage timer on last source - if (!string.IsNullOrEmpty(LastSourceKey)) - { - var lastSource = dict[LastSourceKey].SourceDevice; - - try - { - if (lastSource != null && lastSource is IUsageTracking) - (lastSource as IUsageTracking).UsageTracker.EndDeviceUsage(); - } - catch (Exception e) - { - Debug.Console(1, this, "*#* EXCEPTION in end usage tracking (257):\r{0}", e); - } - } - - // Let's run it - if (routeKey.ToLower() != "roomoff") - { - LastSourceKey = routeKey; - } - else - { - CurrentSourceInfoKey = null; - } - - foreach (var route in item.RouteList) - { - // if there is a $defaultAll on route, run two separate - if (route.DestinationKey.Equals("$defaultAll", StringComparison.OrdinalIgnoreCase)) - { - // Going to assume a single-path route for now - var tempVideo = new SourceRouteListItem - { - DestinationKey = "$defaultDisplay", - SourceKey = route.SourceKey, - Type = eRoutingSignalType.Video - }; - DoRoute(tempVideo); - - //var tempAudio = new SourceRouteListItem - //{ - // DestinationKey = "$defaultAudio", - // SourceKey = route.SourceKey, - // Type = eRoutingSignalType.Audio - //}; - //DoRoute(tempAudio); - //continue; -- not sure why this was here - } - else - DoRoute(route); - } - - // Start usage timer on routed source - if (item.SourceDevice is IUsageTracking) - { - (item.SourceDevice as IUsageTracking).UsageTracker.StartDeviceUsage(); - } - - - // Set volume control on room, using default if non provided - IBasicVolumeControls volDev = null; - // Handle special cases for volume control - if (string.IsNullOrEmpty(item.VolumeControlKey) - || item.VolumeControlKey.Equals("$defaultAudio", StringComparison.OrdinalIgnoreCase)) - volDev = DefaultVolumeControls; - else if (item.VolumeControlKey.Equals("$defaultDisplay", StringComparison.OrdinalIgnoreCase)) - volDev = DefaultDisplay as IBasicVolumeControls; - // Or a specific device, probably rarely used. - else - { - var dev = DeviceManager.GetDeviceForKey(item.VolumeControlKey); - if (dev is IBasicVolumeControls) - volDev = dev as IBasicVolumeControls; - else if (dev is IHasVolumeDevice) - volDev = (dev as IHasVolumeDevice).VolumeDevice; - } - CurrentVolumeControls = volDev; - - // store the name and UI info for routes - if (item.SourceKey == "$off") - { - CurrentSourceInfoKey = routeKey; - CurrentSourceInfo = null; - } - else if (item.SourceKey != null) - { - CurrentSourceInfoKey = routeKey; - CurrentSourceInfo = item; - } - // And finally, set the "control". This will trigger event - //CurrentControlDevice = DeviceManager.GetDeviceForKey(item.SourceKey) as Device; - - OnFeedback.FireUpdate(); - - // report back when done - if (successCallback != null) - successCallback(); - - }, 0); // end of CTimer - } - - /// - /// Will power the room on with the last-used source - /// - public void PowerOnToDefaultOrLastSource() - { - if (!EnablePowerOnToLastSource || LastSourceKey == null) - return; - RunRouteAction(LastSourceKey); - } - - /// - /// - /// - /// - /// - bool DoRoute(SourceRouteListItem route) - { - IRoutingSinkNoSwitching dest = null; - - if (route.DestinationKey.Equals("$defaultaudio", StringComparison.OrdinalIgnoreCase)) - dest = DefaultAudioDevice; - else if (route.DestinationKey.Equals("$defaultDisplay", StringComparison.OrdinalIgnoreCase)) - dest = DefaultDisplay; - else - dest = DeviceManager.GetDeviceForKey(route.DestinationKey) as IRoutingSinkNoSwitching; - - if (dest == null) - { - Debug.Console(1, this, "Cannot route, unknown destination '{0}'", route.DestinationKey); - return false; - } - - if (route.SourceKey.Equals("$off", StringComparison.OrdinalIgnoreCase)) - { - dest.ReleaseRoute(); - if (dest is IPower) - (dest as IPower).PowerOff(); - } - else - { - var source = DeviceManager.GetDeviceForKey(route.SourceKey) as IRoutingOutputs; - if (source == null) - { - Debug.Console(1, this, "Cannot route unknown source '{0}' to {1}", route.SourceKey, route.DestinationKey); - return false; - } - dest.ReleaseAndMakeRoute(source, route.Type); - } - return true; - } - - /// - /// Runs "roomOff" action on all rooms not set to ExcludeFromGlobalFunctions - /// - public static void AllRoomsOff() - { - var allRooms = DeviceManager.AllDevices.Where(d => - d is EssentialsHuddleSpaceRoom && !(d as EssentialsHuddleSpaceRoom).ExcludeFromGlobalFunctions); - foreach (var room in allRooms) - (room as EssentialsHuddleSpaceRoom).RunRouteAction("roomOff"); - } - } +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using PepperDash.Core; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Room.Config; + +namespace PepperDash.Essentials +{ + public class EssentialsHuddleSpaceRoom : EssentialsRoomBase, IHasCurrentSourceInfoChange + { + public event EventHandler CurrentVolumeDeviceChange; + public event SourceInfoChangeHandler CurrentSingleSourceChange; + + protected override Func OnFeedbackFunc + { + get + { + return () => + { + var disp = DefaultDisplay as DisplayBase; + var val = CurrentSourceInfo != null + && CurrentSourceInfo.Type == eSourceListItemType.Route + && disp != null + && disp.PowerIsOnFeedback.BoolValue; + return val; + }; + } + } + /// + /// + /// + protected override Func IsWarmingFeedbackFunc + { + get + { + return () => + { + var disp = DefaultDisplay as DisplayBase; + if (disp != null) + return disp.IsWarmingUpFeedback.BoolValue; + else + return false; + }; + } + } + /// + /// + /// + protected override Func IsCoolingFeedbackFunc + { + get + { + return () => + { + var disp = DefaultDisplay as DisplayBase; + if (disp != null) + return disp.IsCoolingDownFeedback.BoolValue; + else + return false; + }; + } + } + + public EssentialsRoomPropertiesConfig Config { get; private set; } + + public IRoutingSinkWithSwitching DefaultDisplay { get; private set; } + public IRoutingSinkNoSwitching DefaultAudioDevice { get; private set; } + public IBasicVolumeControls DefaultVolumeControls { get; private set; } + + public bool ExcludeFromGlobalFunctions { get; set; } + + /// + /// The config name of the source list + /// + public string SourceListKey { get; set; } + + public string DefaultSourceItem { get; set; } + + public ushort DefaultVolume { get; set; } + + /// + /// If room is off, enables power on to last source. Default true + /// + public bool EnablePowerOnToLastSource { get; set; } + string LastSourceKey; + + /// + /// + /// + public IBasicVolumeControls CurrentVolumeControls + { + get { return _CurrentAudioDevice; } + set + { + if (value == _CurrentAudioDevice) return; + + var oldDev = _CurrentAudioDevice; + // derigister this room from the device, if it can + if (oldDev is IInUseTracking) + (oldDev as IInUseTracking).InUseTracker.RemoveUser(this, "audio"); + var handler = CurrentVolumeDeviceChange; + if (handler != null) + CurrentVolumeDeviceChange(this, new VolumeDeviceChangeEventArgs(oldDev, value, ChangeType.WillChange)); + _CurrentAudioDevice = value; + if (handler != null) + CurrentVolumeDeviceChange(this, new VolumeDeviceChangeEventArgs(oldDev, value, ChangeType.DidChange)); + // register this room with new device, if it can + if (_CurrentAudioDevice is IInUseTracking) + (_CurrentAudioDevice as IInUseTracking).InUseTracker.AddUser(this, "audio"); + } + } + IBasicVolumeControls _CurrentAudioDevice; + + /// + /// The SourceListItem last run - containing names and icons + /// + public SourceListItem CurrentSourceInfo + { + get { return _CurrentSourceInfo; } + private set + { + if (value == _CurrentSourceInfo) return; + + var handler = CurrentSingleSourceChange; + // remove from in-use tracker, if so equipped + if(_CurrentSourceInfo != null && _CurrentSourceInfo.SourceDevice is IInUseTracking) + (_CurrentSourceInfo.SourceDevice as IInUseTracking).InUseTracker.RemoveUser(this, "control"); + + if (handler != null) + handler(this, _CurrentSourceInfo, ChangeType.WillChange); + + _CurrentSourceInfo = value; + + // add to in-use tracking + if (_CurrentSourceInfo != null && _CurrentSourceInfo.SourceDevice is IInUseTracking) + (_CurrentSourceInfo.SourceDevice as IInUseTracking).InUseTracker.AddUser(this, "control"); + if (handler != null) + handler(this, _CurrentSourceInfo, ChangeType.DidChange); + } + } + SourceListItem _CurrentSourceInfo; + + public string CurrentSourceInfoKey { get; private set; } + + /// + /// + /// + /// + /// + public EssentialsHuddleSpaceRoom(string key, string name, IRoutingSinkWithSwitching defaultDisplay, + IRoutingSinkNoSwitching defaultAudio, EssentialsRoomPropertiesConfig config) + : base(key, name) + { + Config = config; + DefaultDisplay = defaultDisplay; + DefaultAudioDevice = defaultAudio; + if (defaultAudio is IBasicVolumeControls) + DefaultVolumeControls = defaultAudio as IBasicVolumeControls; + else if (defaultAudio is IHasVolumeDevice) + DefaultVolumeControls = (defaultAudio as IHasVolumeDevice).VolumeDevice; + CurrentVolumeControls = DefaultVolumeControls; + + var disp = DefaultDisplay as DisplayBase; + if (disp != null) + { + // Link power, warming, cooling to display + disp.PowerIsOnFeedback.OutputChange += (o, a) => + { + if (disp.PowerIsOnFeedback.BoolValue != OnFeedback.BoolValue) + { + if (!disp.PowerIsOnFeedback.BoolValue) + CurrentSourceInfo = null; + OnFeedback.FireUpdate(); + } + }; + + disp.IsWarmingUpFeedback.OutputChange += (o, a) => + { + IsWarmingUpFeedback.FireUpdate(); + if (!IsWarmingUpFeedback.BoolValue) + (DefaultDisplay as IBasicVolumeWithFeedback).SetVolume(DefaultVolume); + }; + disp.IsCoolingDownFeedback.OutputChange += (o, a) => + { + IsCoolingDownFeedback.FireUpdate(); + if (IsCoolingDownFeedback.BoolValue) + (DefaultDisplay as IBasicVolumeWithFeedback).SetVolume(DefaultVolume); + }; + } + + SourceListKey = "default"; + EnablePowerOnToLastSource = true; + } + + + /// + /// + /// + public override void Shutdown() + { + RunRouteAction("roomOff"); + } + + /// + /// Routes the default source item, if any + /// + public void RunDefaultRoute() + { + if (DefaultSourceItem != null) + RunRouteAction(DefaultSourceItem); + } + + /// + /// + /// + /// + public void RunRouteAction(string routeKey) + { + RunRouteAction(routeKey, null); + } + + /// + /// Gets a source from config list SourceListKey and dynamically build and executes the + /// route or commands + /// + /// + public void RunRouteAction(string routeKey, Action successCallback) + { + // Run this on a separate thread + new CTimer(o => + { + Debug.Console(1, this, "Run route action '{0}'", routeKey); + var dict = ConfigReader.ConfigObject.GetSourceListForKey(SourceListKey); + if(dict == null) + { + Debug.Console(1, this, "WARNING: Config source list '{0}' not found", SourceListKey); + return; + } + + // Try to get the list item by it's string key + if (!dict.ContainsKey(routeKey)) + { + Debug.Console(1, this, "WARNING: No item '{0}' found on config list '{1}'", + routeKey, SourceListKey); + return; + } + + var item = dict[routeKey]; + //Debug.Console(2, this, "Action {0} has {1} steps", + // item.SourceKey, item.RouteList.Count); + + // End usage timer on last source + if (!string.IsNullOrEmpty(LastSourceKey)) + { + var lastSource = dict[LastSourceKey].SourceDevice; + + try + { + if (lastSource != null && lastSource is IUsageTracking) + (lastSource as IUsageTracking).UsageTracker.EndDeviceUsage(); + } + catch (Exception e) + { + Debug.Console(1, this, "*#* EXCEPTION in end usage tracking (257):\r{0}", e); + } + } + + // Let's run it + if (routeKey.ToLower() != "roomoff") + { + LastSourceKey = routeKey; + } + else + { + CurrentSourceInfoKey = null; + } + + foreach (var route in item.RouteList) + { + // if there is a $defaultAll on route, run two separate + if (route.DestinationKey.Equals("$defaultAll", StringComparison.OrdinalIgnoreCase)) + { + // Going to assume a single-path route for now + var tempVideo = new SourceRouteListItem + { + DestinationKey = "$defaultDisplay", + SourceKey = route.SourceKey, + Type = eRoutingSignalType.Video + }; + DoRoute(tempVideo); + + //var tempAudio = new SourceRouteListItem + //{ + // DestinationKey = "$defaultAudio", + // SourceKey = route.SourceKey, + // Type = eRoutingSignalType.Audio + //}; + //DoRoute(tempAudio); + //continue; -- not sure why this was here + } + else + DoRoute(route); + } + + // Start usage timer on routed source + if (item.SourceDevice is IUsageTracking) + { + (item.SourceDevice as IUsageTracking).UsageTracker.StartDeviceUsage(); + } + + + // Set volume control on room, using default if non provided + IBasicVolumeControls volDev = null; + // Handle special cases for volume control + if (string.IsNullOrEmpty(item.VolumeControlKey) + || item.VolumeControlKey.Equals("$defaultAudio", StringComparison.OrdinalIgnoreCase)) + volDev = DefaultVolumeControls; + else if (item.VolumeControlKey.Equals("$defaultDisplay", StringComparison.OrdinalIgnoreCase)) + volDev = DefaultDisplay as IBasicVolumeControls; + // Or a specific device, probably rarely used. + else + { + var dev = DeviceManager.GetDeviceForKey(item.VolumeControlKey); + if (dev is IBasicVolumeControls) + volDev = dev as IBasicVolumeControls; + else if (dev is IHasVolumeDevice) + volDev = (dev as IHasVolumeDevice).VolumeDevice; + } + CurrentVolumeControls = volDev; + + // store the name and UI info for routes + if (item.SourceKey == "$off") + { + CurrentSourceInfoKey = routeKey; + CurrentSourceInfo = null; + } + else if (item.SourceKey != null) + { + CurrentSourceInfoKey = routeKey; + CurrentSourceInfo = item; + } + // And finally, set the "control". This will trigger event + //CurrentControlDevice = DeviceManager.GetDeviceForKey(item.SourceKey) as Device; + + OnFeedback.FireUpdate(); + + // report back when done + if (successCallback != null) + successCallback(); + + }, 0); // end of CTimer + } + + /// + /// Will power the room on with the last-used source + /// + public void PowerOnToDefaultOrLastSource() + { + if (!EnablePowerOnToLastSource || LastSourceKey == null) + return; + RunRouteAction(LastSourceKey); + } + + /// + /// + /// + /// + /// + bool DoRoute(SourceRouteListItem route) + { + IRoutingSinkNoSwitching dest = null; + + if (route.DestinationKey.Equals("$defaultaudio", StringComparison.OrdinalIgnoreCase)) + dest = DefaultAudioDevice; + else if (route.DestinationKey.Equals("$defaultDisplay", StringComparison.OrdinalIgnoreCase)) + dest = DefaultDisplay; + else + dest = DeviceManager.GetDeviceForKey(route.DestinationKey) as IRoutingSinkNoSwitching; + + if (dest == null) + { + Debug.Console(1, this, "Cannot route, unknown destination '{0}'", route.DestinationKey); + return false; + } + + if (route.SourceKey.Equals("$off", StringComparison.OrdinalIgnoreCase)) + { + dest.ReleaseRoute(); + if (dest is IPower) + (dest as IPower).PowerOff(); + } + else + { + var source = DeviceManager.GetDeviceForKey(route.SourceKey) as IRoutingOutputs; + if (source == null) + { + Debug.Console(1, this, "Cannot route unknown source '{0}' to {1}", route.SourceKey, route.DestinationKey); + return false; + } + dest.ReleaseAndMakeRoute(source, route.Type); + } + return true; + } + + /// + /// Runs "roomOff" action on all rooms not set to ExcludeFromGlobalFunctions + /// + public static void AllRoomsOff() + { + var allRooms = DeviceManager.AllDevices.Where(d => + d is EssentialsHuddleSpaceRoom && !(d as EssentialsHuddleSpaceRoom).ExcludeFromGlobalFunctions); + foreach (var room in allRooms) + (room as EssentialsHuddleSpaceRoom).RunRouteAction("roomOff"); + } + } } \ No newline at end of file diff --git a/Essentials/PepperDashEssentials/Room/Types/EssentialsHuddleVtc1Room.cs b/Essentials/PepperDashEssentials/Room/Types/EssentialsHuddleVtc1Room.cs new file mode 100644 index 00000000..60bed37e --- /dev/null +++ b/Essentials/PepperDashEssentials/Room/Types/EssentialsHuddleVtc1Room.cs @@ -0,0 +1,407 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using PepperDash.Core; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Room.Config; + +namespace PepperDash.Essentials +{ + public class EssentialsHuddleVtc1Room : EssentialsRoomBase, IHasCurrentSourceInfoChange + { + public event EventHandler CurrentVolumeDeviceChange; + public event SourceInfoChangeHandler CurrentSingleSourceChange; + + protected override Func OnFeedbackFunc + { + get + { + return () => + { + var disp = DefaultDisplay as DisplayBase; + var val = CurrentSourceInfo != null + && CurrentSourceInfo.Type == eSourceListItemType.Route + && disp != null + && disp.PowerIsOnFeedback.BoolValue; + return val; + }; + } + } + /// + /// + /// + protected override Func IsWarmingFeedbackFunc + { + get + { + return () => + { + var disp = DefaultDisplay as DisplayBase; + if (disp != null) + return disp.IsWarmingUpFeedback.BoolValue; + else + return false; + }; + } + } + /// + /// + /// + protected override Func IsCoolingFeedbackFunc + { + get + { + return () => + { + var disp = DefaultDisplay as DisplayBase; + if (disp != null) + return disp.IsCoolingDownFeedback.BoolValue; + else + return false; + }; + } + } + + public EssentialsHuddleVtc1PropertiesConfig Config { get; private set; } + + public IRoutingSinkWithSwitching DefaultDisplay { get; private set; } + public IRoutingSinkNoSwitching DefaultAudioDevice { get; private set; } + public IBasicVolumeControls DefaultVolumeControls { get; private set; } + + public bool ExcludeFromGlobalFunctions { get; set; } + + /// + /// The config name of the source list + /// + public string SourceListKey { get; set; } + + public string DefaultSourceItem { get; set; } + + public ushort DefaultVolume { get; set; } + + /// + /// If room is off, enables power on to last source. Default true + /// + public bool EnablePowerOnToLastSource { get; set; } + string LastSourceKey; + + /// + /// + /// + public IBasicVolumeControls CurrentVolumeControls + { + get { return _CurrentAudioDevice; } + set + { + if (value == _CurrentAudioDevice) return; + + var oldDev = _CurrentAudioDevice; + // derigister this room from the device, if it can + if (oldDev is IInUseTracking) + (oldDev as IInUseTracking).InUseTracker.RemoveUser(this, "audio"); + var handler = CurrentVolumeDeviceChange; + if (handler != null) + CurrentVolumeDeviceChange(this, new VolumeDeviceChangeEventArgs(oldDev, value, ChangeType.WillChange)); + _CurrentAudioDevice = value; + if (handler != null) + CurrentVolumeDeviceChange(this, new VolumeDeviceChangeEventArgs(oldDev, value, ChangeType.DidChange)); + // register this room with new device, if it can + if (_CurrentAudioDevice is IInUseTracking) + (_CurrentAudioDevice as IInUseTracking).InUseTracker.AddUser(this, "audio"); + } + } + IBasicVolumeControls _CurrentAudioDevice; + + /// + /// The SourceListItem last run - containing names and icons + /// + public SourceListItem CurrentSourceInfo + { + get { return _CurrentSourceInfo; } + private set + { + if (value == _CurrentSourceInfo) return; + + var handler = CurrentSingleSourceChange; + // remove from in-use tracker, if so equipped + if(_CurrentSourceInfo != null && _CurrentSourceInfo.SourceDevice is IInUseTracking) + (_CurrentSourceInfo.SourceDevice as IInUseTracking).InUseTracker.RemoveUser(this, "control"); + + if (handler != null) + handler(this, _CurrentSourceInfo, ChangeType.WillChange); + + _CurrentSourceInfo = value; + + // add to in-use tracking + if (_CurrentSourceInfo != null && _CurrentSourceInfo.SourceDevice is IInUseTracking) + (_CurrentSourceInfo.SourceDevice as IInUseTracking).InUseTracker.AddUser(this, "control"); + if (handler != null) + handler(this, _CurrentSourceInfo, ChangeType.DidChange); + } + } + SourceListItem _CurrentSourceInfo; + + public string CurrentSourceInfoKey { get; private set; } + + /// + /// + /// + /// + /// + public EssentialsHuddleVtc1Room(string key, string name, IRoutingSinkWithSwitching defaultDisplay, + IRoutingSinkNoSwitching defaultAudio, EssentialsHuddleVtc1PropertiesConfig config) + : base(key, name) + { + Config = config; + DefaultDisplay = defaultDisplay; + DefaultAudioDevice = defaultAudio; + if (defaultAudio is IBasicVolumeControls) + DefaultVolumeControls = defaultAudio as IBasicVolumeControls; + else if (defaultAudio is IHasVolumeDevice) + DefaultVolumeControls = (defaultAudio as IHasVolumeDevice).VolumeDevice; + CurrentVolumeControls = DefaultVolumeControls; + + var disp = DefaultDisplay as DisplayBase; + if (disp != null) + { + // Link power, warming, cooling to display + disp.PowerIsOnFeedback.OutputChange += (o, a) => + { + if (disp.PowerIsOnFeedback.BoolValue != OnFeedback.BoolValue) + { + if (!disp.PowerIsOnFeedback.BoolValue) + CurrentSourceInfo = null; + OnFeedback.FireUpdate(); + } + }; + + disp.IsWarmingUpFeedback.OutputChange += (o, a) => + { + IsWarmingUpFeedback.FireUpdate(); + if (!IsWarmingUpFeedback.BoolValue) + (DefaultDisplay as IBasicVolumeWithFeedback).SetVolume(DefaultVolume); + }; + disp.IsCoolingDownFeedback.OutputChange += (o, a) => + { + IsCoolingDownFeedback.FireUpdate(); + if (IsCoolingDownFeedback.BoolValue) + (DefaultDisplay as IBasicVolumeWithFeedback).SetVolume(DefaultVolume); + }; + } + + SourceListKey = "default"; + EnablePowerOnToLastSource = true; + } + + + /// + /// + /// + public override void Shutdown() + { + RunRouteAction("roomOff"); + } + + /// + /// Routes the default source item, if any + /// + public void RunDefaultRoute() + { + if (DefaultSourceItem != null) + RunRouteAction(DefaultSourceItem); + } + + /// + /// + /// + /// + public void RunRouteAction(string routeKey) + { + RunRouteAction(routeKey, null); + } + + /// + /// Gets a source from config list SourceListKey and dynamically build and executes the + /// route or commands + /// + /// + public void RunRouteAction(string routeKey, Action successCallback) + { + // Run this on a separate thread + new CTimer(o => + { + Debug.Console(1, this, "Run route action '{0}'", routeKey); + var dict = ConfigReader.ConfigObject.GetSourceListForKey(SourceListKey); + if(dict == null) + { + Debug.Console(1, this, "WARNING: Config source list '{0}' not found", SourceListKey); + return; + } + + // Try to get the list item by it's string key + if (!dict.ContainsKey(routeKey)) + { + Debug.Console(1, this, "WARNING: No item '{0}' found on config list '{1}'", + routeKey, SourceListKey); + return; + } + + var item = dict[routeKey]; + + // End usage timer on last source + if (!string.IsNullOrEmpty(LastSourceKey)) + { + var lastSource = dict[LastSourceKey].SourceDevice; + + try + { + if (lastSource != null && lastSource is IUsageTracking) + (lastSource as IUsageTracking).UsageTracker.EndDeviceUsage(); + } + catch (Exception e) + { + Debug.Console(1, this, "*#* EXCEPTION in end usage tracking (257):\r{0}", e); + } + } + + // Let's run it + if (routeKey.ToLower() != "roomoff") + { + LastSourceKey = routeKey; + } + else + { + CurrentSourceInfoKey = null; + } + + foreach (var route in item.RouteList) + { + // if there is a $defaultAll on route, run two separate + if (route.DestinationKey.Equals("$defaultAll", StringComparison.OrdinalIgnoreCase)) + { + // Going to assume a single-path route for now + var tempVideo = new SourceRouteListItem + { + DestinationKey = "$defaultDisplay", + SourceKey = route.SourceKey, + Type = eRoutingSignalType.Video + }; + DoRoute(tempVideo); + } + else + DoRoute(route); + } + + // Start usage timer on routed source + if (item.SourceDevice is IUsageTracking) + { + (item.SourceDevice as IUsageTracking).UsageTracker.StartDeviceUsage(); + } + + + // Set volume control on room, using default if non provided + IBasicVolumeControls volDev = null; + // Handle special cases for volume control + if (string.IsNullOrEmpty(item.VolumeControlKey) + || item.VolumeControlKey.Equals("$defaultAudio", StringComparison.OrdinalIgnoreCase)) + volDev = DefaultVolumeControls; + else if (item.VolumeControlKey.Equals("$defaultDisplay", StringComparison.OrdinalIgnoreCase)) + volDev = DefaultDisplay as IBasicVolumeControls; + // Or a specific device, probably rarely used. + else + { + var dev = DeviceManager.GetDeviceForKey(item.VolumeControlKey); + if (dev is IBasicVolumeControls) + volDev = dev as IBasicVolumeControls; + else if (dev is IHasVolumeDevice) + volDev = (dev as IHasVolumeDevice).VolumeDevice; + } + CurrentVolumeControls = volDev; + + // store the name and UI info for routes + if (item.SourceKey == "$off") + { + CurrentSourceInfoKey = routeKey; + CurrentSourceInfo = null; + } + else if (item.SourceKey != null) + { + CurrentSourceInfoKey = routeKey; + CurrentSourceInfo = item; + } + + OnFeedback.FireUpdate(); + + // report back when done + if (successCallback != null) + successCallback(); + + }, 0); // end of CTimer + } + + /// + /// Will power the room on with the last-used source + /// + public void PowerOnToDefaultOrLastSource() + { + if (!EnablePowerOnToLastSource || LastSourceKey == null) + return; + RunRouteAction(LastSourceKey); + } + + /// + /// + /// + /// + /// + bool DoRoute(SourceRouteListItem route) + { + IRoutingSinkNoSwitching dest = null; + + if (route.DestinationKey.Equals("$defaultaudio", StringComparison.OrdinalIgnoreCase)) + dest = DefaultAudioDevice; + else if (route.DestinationKey.Equals("$defaultDisplay", StringComparison.OrdinalIgnoreCase)) + dest = DefaultDisplay; + else + dest = DeviceManager.GetDeviceForKey(route.DestinationKey) as IRoutingSinkNoSwitching; + + if (dest == null) + { + Debug.Console(1, this, "Cannot route, unknown destination '{0}'", route.DestinationKey); + return false; + } + + if (route.SourceKey.Equals("$off", StringComparison.OrdinalIgnoreCase)) + { + dest.ReleaseRoute(); + if (dest is IPower) + (dest as IPower).PowerOff(); + } + else + { + var source = DeviceManager.GetDeviceForKey(route.SourceKey) as IRoutingOutputs; + if (source == null) + { + Debug.Console(1, this, "Cannot route unknown source '{0}' to {1}", route.SourceKey, route.DestinationKey); + return false; + } + dest.ReleaseAndMakeRoute(source, route.Type); + } + return true; + } + + /// + /// Runs "roomOff" action on all rooms not set to ExcludeFromGlobalFunctions + /// + public static void AllRoomsOff() + { + var allRooms = DeviceManager.AllDevices.Where(d => + d is EssentialsHuddleSpaceRoom && !(d as EssentialsHuddleSpaceRoom).ExcludeFromGlobalFunctions); + foreach (var room in allRooms) + (room as EssentialsHuddleSpaceRoom).RunRouteAction("roomOff"); + } + } +} \ No newline at end of file diff --git a/Essentials/PepperDashEssentials/Room/EssentialsPresentationRoom.cs b/Essentials/PepperDashEssentials/Room/Types/EssentialsPresentationRoom.cs similarity index 99% rename from Essentials/PepperDashEssentials/Room/EssentialsPresentationRoom.cs rename to Essentials/PepperDashEssentials/Room/Types/EssentialsPresentationRoom.cs index 698013be..4cc4d197 100644 --- a/Essentials/PepperDashEssentials/Room/EssentialsPresentationRoom.cs +++ b/Essentials/PepperDashEssentials/Room/Types/EssentialsPresentationRoom.cs @@ -6,6 +6,7 @@ using Crestron.SimplSharp; using PepperDash.Core; using PepperDash.Essentials.Core; +using PepperDash.Essentials.Room.Config; namespace PepperDash.Essentials { diff --git a/Essentials/PepperDashEssentials/Room/EssentialsRoomBase.cs b/Essentials/PepperDashEssentials/Room/Types/EssentialsRoomBase.cs similarity index 100% rename from Essentials/PepperDashEssentials/Room/EssentialsRoomBase.cs rename to Essentials/PepperDashEssentials/Room/Types/EssentialsRoomBase.cs diff --git a/Essentials/PepperDashEssentials/Room/UI/CrestronTouchpanelPropertiesConfig.cs b/Essentials/PepperDashEssentials/UI - FILES ORPHANED-DELETE/CrestronTouchpanelPropertiesConfig.cs similarity index 100% rename from Essentials/PepperDashEssentials/Room/UI/CrestronTouchpanelPropertiesConfig.cs rename to Essentials/PepperDashEssentials/UI - FILES ORPHANED-DELETE/CrestronTouchpanelPropertiesConfig.cs diff --git a/Essentials/PepperDashEssentials/Room/UI/DualDisplaySourceSRLController.cs b/Essentials/PepperDashEssentials/UI - FILES ORPHANED-DELETE/DualDisplaySourceSRLController.cs similarity index 100% rename from Essentials/PepperDashEssentials/Room/UI/DualDisplaySourceSRLController.cs rename to Essentials/PepperDashEssentials/UI - FILES ORPHANED-DELETE/DualDisplaySourceSRLController.cs diff --git a/Essentials/PepperDashEssentials/Room/UI/EssentialsTouchpanelController.cs b/Essentials/PepperDashEssentials/UI - FILES ORPHANED-DELETE/EssentialsTouchpanelController.cs similarity index 100% rename from Essentials/PepperDashEssentials/Room/UI/EssentialsTouchpanelController.cs rename to Essentials/PepperDashEssentials/UI - FILES ORPHANED-DELETE/EssentialsTouchpanelController.cs diff --git a/Essentials/PepperDashEssentials/Room/UI/SubpageReferenceListActivityItem.cs b/Essentials/PepperDashEssentials/UI - FILES ORPHANED-DELETE/SubpageReferenceListActivityItem.cs similarity index 100% rename from Essentials/PepperDashEssentials/Room/UI/SubpageReferenceListActivityItem.cs rename to Essentials/PepperDashEssentials/UI - FILES ORPHANED-DELETE/SubpageReferenceListActivityItem.cs diff --git a/Essentials/PepperDashEssentials/Room/UI/SubpageReferenceListSourceItem.cs b/Essentials/PepperDashEssentials/UI - FILES ORPHANED-DELETE/SubpageReferenceListSourceItem.cs similarity index 100% rename from Essentials/PepperDashEssentials/Room/UI/SubpageReferenceListSourceItem.cs rename to Essentials/PepperDashEssentials/UI - FILES ORPHANED-DELETE/SubpageReferenceListSourceItem.cs diff --git a/Essentials/PepperDashEssentials/UI Drivers/UISmartObjectJoin.cs b/Essentials/PepperDashEssentials/UI Drivers/UISmartObjectJoin.cs deleted file mode 100644 index cc2e83c9..00000000 --- a/Essentials/PepperDashEssentials/UI Drivers/UISmartObjectJoin.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace PepperDash.Essentials -{ - public class UISmartObjectJoin - { - /// - /// 3200 The staging, source-select list - /// - public const uint StagingListSRL = 3200; - /// - /// 15022 The main activity footer - /// - public const uint ActivityFooterSRL = 15022; - } -} \ No newline at end of file diff --git a/Essentials/PepperDashEssentials/UI/CrestronTouchpanelPropertiesConfig.cs b/Essentials/PepperDashEssentials/UI/CrestronTouchpanelPropertiesConfig.cs new file mode 100644 index 00000000..a79ef0b9 --- /dev/null +++ b/Essentials/PepperDashEssentials/UI/CrestronTouchpanelPropertiesConfig.cs @@ -0,0 +1,47 @@ +namespace PepperDash.Essentials +{ + public class CrestronTouchpanelPropertiesConfig + { + public string IpId { get; set; } + public string DefaultRoomKey { get; set; } + public string RoomListKey { get; set; } + public string SgdFile { get; set; } + public string ProjectName { get; set; } + public bool ShowVolumeGauge { get; set; } + public bool UsesSplashPage { get; set; } + public bool ShowDate { get; set; } + public bool ShowTime { get; set; } + public UiSetupPropertiesConfig Setup { get; set; } + public UiHeaderStyle HeaderStyle { get; set; } + + /// + /// The count of sources that will trigger the "additional" arrows to show on the SRL. + /// Defaults to 5 + /// + public int SourcesOverflowCount { get; set; } + + public CrestronTouchpanelPropertiesConfig() + { + SourcesOverflowCount = 5; + HeaderStyle = UiHeaderStyle.Habanero; + } + } + + /// + /// + /// + public class UiSetupPropertiesConfig + { + public bool IsVisible { get; set; } + } + + /// + /// + /// + public enum UiHeaderStyle + { + Habanero = 0, + Verbose + } + +} \ No newline at end of file diff --git a/Essentials/PepperDashEssentials/UI/DualDisplaySourceSRLController.cs b/Essentials/PepperDashEssentials/UI/DualDisplaySourceSRLController.cs new file mode 100644 index 00000000..c91d72f1 --- /dev/null +++ b/Essentials/PepperDashEssentials/UI/DualDisplaySourceSRLController.cs @@ -0,0 +1,28 @@ +//using System; +//using System.Collections.Generic; +//using System.Linq; +//using System.Text; +//using Crestron.SimplSharp; +//using Crestron.SimplSharpPro; +//using Crestron.SimplSharpPro.DeviceSupport; +//using Crestron.SimplSharpPro.UI; + +//using PepperDash.Essentials.Core; + +//namespace PepperDash.Essentials +//{ +// public class DualDisplaySourceSRLController : SubpageReferenceList +// { +// public DualDisplaySourceSRLController(BasicTriListWithSmartObject triList, +// uint smartObjectId, EssentialsPresentationRoom room) +// : base(triList, smartObjectId, 3, 3, 3) +// { +// var srcList = room.s items.Values.ToList().OrderBy(s => s.Order); +// foreach (var item in srcList) +// { +// GetBoolFeedbackSig(index, 1).UserObject = new Action(routeAction); + +// } +// } +// } +//} \ No newline at end of file diff --git a/Essentials/PepperDashEssentials/UI/EssentialsTouchpanelController.cs b/Essentials/PepperDashEssentials/UI/EssentialsTouchpanelController.cs new file mode 100644 index 00000000..6398a2c5 --- /dev/null +++ b/Essentials/PepperDashEssentials/UI/EssentialsTouchpanelController.cs @@ -0,0 +1,253 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharp.CrestronIO; +using Crestron.SimplSharpPro; +using Crestron.SimplSharpPro.DeviceSupport; +using Crestron.SimplSharpPro.UI; +using PepperDash.Core; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.PageManagers; + +namespace PepperDash.Essentials +{ + public class EssentialsTouchpanelController : Device + { + public BasicTriListWithSmartObject Panel { get; private set; } + + public PanelDriverBase PanelDriver { get; private set; } + + CTimer BacklightTransitionedOnTimer; + + public EssentialsTouchpanelController(string key, string name, Tswx52ButtonVoiceControl tsw, + string projectName, string sgdPath) + : base(key, name) + { + Panel = tsw; + tsw.LoadSmartObjects(sgdPath); + tsw.SigChange += new Crestron.SimplSharpPro.DeviceSupport.SigEventHandler(Tsw_SigChange); + } + + /// + /// Config constructor + /// + public EssentialsTouchpanelController(string key, string name, string type, CrestronTouchpanelPropertiesConfig props, uint id) + : base(key, name) + { + AddPostActivationAction(() => + { + Debug.Console(0, this, "Creating hardware..."); + type = type.ToLower(); + try + { + if (type == "crestronapp") + { + var app = new CrestronApp(id, Global.ControlSystem); + app.ParameterProjectName.Value = props.ProjectName; + Panel = app; + } + else if (type == "tsw550") + Panel = new Tsw550(id, Global.ControlSystem); + else if (type == "tsw552") + Panel = new Tsw552(id, Global.ControlSystem); + else if (type == "tsw560") + Panel = new Tsw560(id, Global.ControlSystem); + else if (type == "tsw750") + Panel = new Tsw750(id, Global.ControlSystem); + else if (type == "tsw752") + Panel = new Tsw752(id, Global.ControlSystem); + else if (type == "tsw760") + Panel = new Tsw760(id, Global.ControlSystem); + else if (type == "tsw1050") + Panel = new Tsw1050(id, Global.ControlSystem); + else if (type == "tsw1052") + Panel = new Tsw1052(id, Global.ControlSystem); + else if (type == "tsw1060") + Panel = new Tsw1060(id, Global.ControlSystem); + else + { + Debug.Console(0, this, "WARNING: Cannot create TSW controller with type '{0}'", type); + return; + } + } + catch (Exception e) + { + Debug.Console(0, this, "WARNING: Cannot create TSW base class. Panel will not function: {0}", e.Message); + return; + } + + // Reserved sigs + if (Panel is TswFt5ButtonSystem) + { + var tsw = Panel as TswFt5ButtonSystem; + tsw.ExtenderSystemReservedSigs.Use(); + tsw.ExtenderSystemReservedSigs.DeviceExtenderSigChange + += ExtenderSystemReservedSigs_DeviceExtenderSigChange; + } + + new CTimer(o => + { + var regSuccess = Panel.Register(); + if (regSuccess != eDeviceRegistrationUnRegistrationResponse.Success) + Debug.Console(0, this, "WARNING: Registration failed. Continuing, but panel may not function: {0}", regSuccess); + + // Give up cleanly if SGD is not present. + var sgdName = @"\NVRAM\Program" + InitialParametersClass.ApplicationNumber + + @"\sgd\" + props.SgdFile; + if (!File.Exists(sgdName)) + { + Debug.Console(0, this, "WARNING: Smart object file '{0}' not present. Exiting TSW load", sgdName); + return; + } + + Panel.LoadSmartObjects(sgdName); + Panel.SigChange += Tsw_SigChange; + + var mainDriver = new EssentialsPanelMainInterfaceDriver(Panel, props); + // Then the AV driver + + // spin up different room drivers depending on room type + var room = DeviceManager.GetDeviceForKey(props.DefaultRoomKey); + if (room is EssentialsHuddleSpaceRoom) + { + Debug.Console(0, this, "Adding huddle space driver"); + var avDriver = new EssentialsHuddlePanelAvFunctionsDriver(mainDriver, props); + avDriver.CurrentRoom = room as EssentialsHuddleSpaceRoom; + avDriver.DefaultRoomKey = props.DefaultRoomKey; + mainDriver.AvDriver = avDriver; + LoadAndShowDriver(mainDriver); // This is a little convoluted. + + if (Panel is TswFt5ButtonSystem) + { + var tsw = Panel as TswFt5ButtonSystem; + // Wire up hard keys + tsw.Power.UserObject = new Action(b => { if (!b) avDriver.PowerButtonPressed(); }); + //tsw.Home.UserObject = new Action(b => { if (!b) HomePressed(); }); + tsw.Up.UserObject = new Action(avDriver.VolumeUpPress); + tsw.Down.UserObject = new Action(avDriver.VolumeDownPress); + tsw.ButtonStateChange += new ButtonEventHandler(Tsw_ButtonStateChange); + } + } + else if (room is EssentialsPresentationRoom) + { + Debug.Console(0, this, "Adding presentation room driver"); + var avDriver = new EssentialsPresentationPanelAvFunctionsDriver(mainDriver, props); + avDriver.CurrentRoom = room as EssentialsPresentationRoom; + avDriver.DefaultRoomKey = props.DefaultRoomKey; + mainDriver.AvDriver = avDriver; + LoadAndShowDriver(mainDriver); + + if (Panel is TswFt5ButtonSystem) + { + var tsw = Panel as TswFt5ButtonSystem; + // Wire up hard keys + tsw.Power.UserObject = new Action(b => { if (!b) avDriver.PowerButtonPressed(); }); + //tsw.Home.UserObject = new Action(b => { if (!b) HomePressed(); }); + tsw.Up.UserObject = new Action(avDriver.VolumeUpPress); + tsw.Down.UserObject = new Action(avDriver.VolumeDownPress); + tsw.ButtonStateChange += new ButtonEventHandler(Tsw_ButtonStateChange); + } + } + else if (room is EssentialsHuddleVtc1Room) + { + Debug.Console(0, this, "Adding huddle space driver"); + var avDriver = new EssentialsHuddleVtc1PanelAvFunctionsDriver(mainDriver, props); + avDriver.CurrentRoom = room as EssentialsHuddleVtc1Room; + avDriver.DefaultRoomKey = props.DefaultRoomKey; + mainDriver.AvDriver = avDriver; + LoadAndShowDriver(mainDriver); // This is a little convoluted. + + if (Panel is TswFt5ButtonSystem) + { + var tsw = Panel as TswFt5ButtonSystem; + // Wire up hard keys + tsw.Power.UserObject = new Action(b => { if (!b) avDriver.PowerButtonPressed(); }); + //tsw.Home.UserObject = new Action(b => { if (!b) HomePressed(); }); + tsw.Up.UserObject = new Action(avDriver.VolumeUpPress); + tsw.Down.UserObject = new Action(avDriver.VolumeDownPress); + tsw.ButtonStateChange += new ButtonEventHandler(Tsw_ButtonStateChange); + } + } + else + { + Debug.Console(0, this, "ERROR: Cannot load AvFunctionsDriver for room '{0}'", props.DefaultRoomKey); + } + }, 0); + }); + } + + public void LoadAndShowDriver(PanelDriverBase driver) + { + PanelDriver = driver; + driver.Show(); + } + + void HomePressed() + { + if (BacklightTransitionedOnTimer == null) + PanelDriver.BackButtonPressed(); + } + + + void ExtenderSystemReservedSigs_DeviceExtenderSigChange(DeviceExtender currentDeviceExtender, SigEventArgs args) + { + // If the sig is transitioning on, mark it in case it was home button that transitioned it + var blOnSig = (Panel as TswFt5ButtonSystem).ExtenderSystemReservedSigs.BacklightOnFeedback; + if (args.Sig == blOnSig && blOnSig.BoolValue) + { + BacklightTransitionedOnTimer = new CTimer(o => + { + BacklightTransitionedOnTimer = null; + }, 200); + } + } + + public void PulseBool(uint join) + { + var act = Panel.BooleanInput[join].UserObject as Action; + if (act != null) + { + act(true); + act(false); + } + } + + public void SetBoolSig(uint join, bool value) + { + var act = Panel.BooleanInput[join].UserObject as Action; + if (act != null) + act(value); + } + + public void SetIntSig(uint join, ushort value) + { + var act = Panel.BooleanInput[join].UserObject as Action; + if (act != null) + { + act(value); + } + } + + void Tsw_SigChange(object currentDevice, Crestron.SimplSharpPro.SigEventArgs args) + { + if (Debug.Level == 2) + Debug.Console(2, this, "Sig change: {0} {1}={2}", args.Sig.Type, args.Sig.Number, args.Sig.StringValue); + var uo = args.Sig.UserObject; + if (uo is Action) + (uo as Action)(args.Sig.BoolValue); + else if (uo is Action) + (uo as Action)(args.Sig.UShortValue); + else if (uo is Action) + (uo as Action)(args.Sig.StringValue); + } + + void Tsw_ButtonStateChange(GenericBase device, ButtonEventArgs args) + { + var uo = args.Button.UserObject; + if(uo is Action) + (uo as Action)(args.Button.State == eButtonState.Pressed); + } + } +} \ No newline at end of file diff --git a/Essentials/PepperDashEssentials/UI Drivers/UIBoolJoin.cs b/Essentials/PepperDashEssentials/UI/JoinConstants/UIBoolJoin.cs similarity index 68% rename from Essentials/PepperDashEssentials/UI Drivers/UIBoolJoin.cs rename to Essentials/PepperDashEssentials/UI/JoinConstants/UIBoolJoin.cs index 8ebaf2ed..b54e40f6 100644 --- a/Essentials/PepperDashEssentials/UI Drivers/UIBoolJoin.cs +++ b/Essentials/PepperDashEssentials/UI/JoinConstants/UIBoolJoin.cs @@ -22,6 +22,143 @@ namespace PepperDash.Essentials /// public const uint VolumeDownPress = 902; + //****************************************************** + // Audio Conference + /// + /// 1001 + /// + public const uint ACKeypadVisible = 1001; + /// + /// 1002 + /// + public const uint ACStagingPopoverVisible = 1002; + /// + /// 1011 + /// + public const uint ACSpeedDial1Press = 1011; + /// + /// 1012 + /// + public const uint ACSpeedDial2Press = 1012; + /// + /// 1013 + /// + public const uint ACSpeedDial3Press = 1013; + /// + /// 1014 + /// + public const uint ACSpeedDial4Press = 1014; + /// + /// 1021 + /// + public const uint ACSpeedDial1Visible = 1021; + /// + /// 1022 + /// + public const uint ACSpeedDial2Visible = 1022; + /// + /// 1023 + /// + public const uint ACSpeedDial3Visible = 1023; + /// + /// 1024 + /// + public const uint ACSpeedDial4Visible = 1024; + + //****************************************************** + // Video Conference + /// + /// 1201 + /// + public const uint VCKeypadVisible = 1201; + /// + /// 1202 + /// + public const uint VCStagingInactivePopoverVisible = 1202; + /// + /// + /// + public const uint VCStagingActivePopoverVisible = 1203; + /// + /// 1205 + /// + public const uint VCDirectoryVisible = 1205; + /// + /// 1206 + /// + public const uint VCRecentsVisible = 1206; + /// + /// 1207 + /// + public const uint VCCameraVisible = 1207; + /// + /// 1211 + /// + public const uint VCSpeedDial1Press = 1211; + /// + /// 1212 + /// + public const uint VCSpeedDial2Press = 1212; + /// + /// 1213 + /// + public const uint VCSpeedDial3Press = 1213; + /// + /// 1214 + /// + public const uint VCSpeedDial4Press = 1214; + /// + /// 1221 + /// + public const uint VCSpeedDial1Visible = 1221; + /// + /// 1222 + /// + public const uint VCSpeedDial2Visible = 1222; + /// + /// 1223 + /// + public const uint VCSpeedDial3Visible = 1223; + /// + /// 1224 + /// + public const uint VCSpeedDial4Visible = 1224; + /// + /// 1231 + /// + public const uint VCStagingRecentsPress = 1231; + /// + /// 1232 + /// + public const uint VCStagingDirectoryPress = 1232; + /// + /// 1233 + /// + public const uint VCStagingKeypadPress = 1233; + /// + /// 1234 + /// + public const uint VCStagingConnectPress = 1234; + /// + /// 1235 + /// + public const uint VCStagingCameraPress = 1235; + + //****************************************************** + // Keyboard + /// + /// 2901 + /// + public const uint KeyboardVisible = 2901; + /// + /// 2910 + /// + public const uint KeyboardClearPress = 2910; + /// + /// 2911 + /// + public const uint KeyboardClearVisible = 2911; + /// /// 3811 /// @@ -34,19 +171,6 @@ namespace PepperDash.Essentials /// 3813 /// public const uint Volume1ProgramMutePressAndFB = 3813; - /// - /// 3871 - /// - public const uint VolumeDualMute1Visible = 3871; - /// - /// 3874 - /// - public const uint Volume1SpeechMutePressAndFB = 3874; - /// - /// 3875 - /// - public const uint Volume1BackerVisibility = 3875; - /// /// 3821 /// @@ -113,6 +237,22 @@ namespace PepperDash.Essentials /// public const uint VolumesPageVisible = 3870; /// + /// 3871 + /// + public const uint VolumeDualMute1Visible = 3871; + /// + /// 3874 + /// + public const uint Volume1SpeechMutePressAndFB = 3874; + /// + /// 3875 + /// + public const uint Volume1BackerVisibility = 3875; + /// + /// 3891 + /// + public const uint VolumeDefaultPress = 3891; + /// /// 3901 /// public const uint TechPagesExitButton = 3901; @@ -157,13 +297,13 @@ namespace PepperDash.Essentials /// public const uint ShowPanelSetupPress = 15010; /// - /// 15011 + /// 15011 - Top bar with room name and button that pops up dialog with room data /// - public const uint TopBarVisible = 15011; + public const uint TopBarHabaneroVisible = 15011; /// /// 15012 /// - public const uint StagingPageVisible = 15012; + public const uint SourceStagingBarVisible = 15012; /// /// 15013 /// @@ -212,7 +352,7 @@ namespace PepperDash.Essentials /// 15026 /// public const uint LightsHeaderButtonPress = 15026; - /// + /// [- /// 15027 /// public const uint CallHeaderButtonPress = 15027; @@ -261,6 +401,14 @@ namespace PepperDash.Essentials /// public const uint GearButtonVisible = 15037; /// + /// 15038 + /// + public const uint CalendarHeaderButtonVisible = 15038; + /// + /// 15039 + /// + public const uint CalendarHeaderButtonPress = 15039; + /// /// 15040 /// public const uint CallStatusPageVisible = 15040; @@ -280,8 +428,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; /// /// 15051 /// @@ -347,17 +497,22 @@ namespace PepperDash.Essentials /// 15065 /// public const uint LogoUrlVisible = 15065; - + /// + /// 15083 - Press for Call help desk on AC/VC + /// + public const uint HelpPageShowCallButtonPress = 15083; + /// + /// 15084 - Show the "call help desk" button on help page + /// + public const uint HelpPageShowCallButtonVisible = 15084; /// /// 15085 Visibility join for help subpage /// public const uint HelpPageVisible = 15085; - /// /// 15086 Press for help header button /// public const uint HelpPress = 15086; - /// /// 15088 /// diff --git a/Essentials/PepperDashEssentials/UI/JoinConstants/UISmartObjectJoin.cs b/Essentials/PepperDashEssentials/UI/JoinConstants/UISmartObjectJoin.cs new file mode 100644 index 00000000..3fdd58be --- /dev/null +++ b/Essentials/PepperDashEssentials/UI/JoinConstants/UISmartObjectJoin.cs @@ -0,0 +1,28 @@ +namespace PepperDash.Essentials +{ + public class UISmartObjectJoin + { + //****************************************************** + // Video Conference + + /// + /// 1201 + /// + public const uint VCDialKeypad = 1201; + + public const uint VCDirectoryList = 1202; + + //****************************************************** + // General + + /// + /// 3200 The staging, source-select list + /// + public const uint SourceStagingSRL = 3200; + /// + /// 15022 The main activity footer + /// + public const uint ActivityFooterSRL = 15022; + + } +} \ No newline at end of file diff --git a/Essentials/PepperDashEssentials/UI Drivers/UIStringlJoin.cs b/Essentials/PepperDashEssentials/UI/JoinConstants/UIStringlJoin.cs similarity index 75% rename from Essentials/PepperDashEssentials/UI Drivers/UIStringlJoin.cs rename to Essentials/PepperDashEssentials/UI/JoinConstants/UIStringlJoin.cs index 28118f1e..bb709aad 100644 --- a/Essentials/PepperDashEssentials/UI Drivers/UIStringlJoin.cs +++ b/Essentials/PepperDashEssentials/UI/JoinConstants/UIStringlJoin.cs @@ -9,10 +9,17 @@ using Crestron.SimplSharpPro.DeviceSupport; namespace PepperDash.Essentials { /// - /// + /// Common string join number constants /// public class UIStringJoin { + //****************************************************** + // Keyboard + /// + /// 1901 + /// + public const uint KeyboardText = 2901; + /// /// 3812 /// @@ -51,6 +58,18 @@ namespace PepperDash.Essentials /// public const uint CurrentSourceIcon = 3903; /// + /// 3904 - Phone number for room header + /// + public const uint RoomPhoneText = 3904; + /// + /// 3905 - SIP address for room header + /// + public const uint RoomSipText = 3905; + /// + /// 3906 - The separator for verbose-header text on addresses + /// + public const uint RoomAddressPipeText = 3906; + /// /// 3911 /// public const uint PowerOffMessage = 3911; @@ -71,11 +90,14 @@ namespace PepperDash.Essentials /// 3922 /// public const uint HelpMessage = 3922; - /// /// 3923 /// public const uint LogoUrl = 3923; + /// + /// 3924 - the text on the "call help desk" button + /// + public const uint HelpPageCallButtonText = 3924; /// /// 3961 Name of source on display 1 diff --git a/Essentials/PepperDashEssentials/UI Drivers/UIUshortJoin.cs b/Essentials/PepperDashEssentials/UI/JoinConstants/UIUshortJoin.cs similarity index 100% rename from Essentials/PepperDashEssentials/UI Drivers/UIUshortJoin.cs rename to Essentials/PepperDashEssentials/UI/JoinConstants/UIUshortJoin.cs diff --git a/Essentials/PepperDashEssentials/UI/SubpageReferenceListActivityItem.cs b/Essentials/PepperDashEssentials/UI/SubpageReferenceListActivityItem.cs new file mode 100644 index 00000000..4747a61a --- /dev/null +++ b/Essentials/PepperDashEssentials/UI/SubpageReferenceListActivityItem.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; +using Crestron.SimplSharpPro.UI; + +using PepperDash.Essentials.Core; + +namespace PepperDash.Essentials +{ + public class SubpageReferenceListActivityItem : SubpageReferenceListItem + { + /// + /// + /// + /// + /// + /// 0=Share, 1=Phone Call, 2=Video Call, 3=End Meeting + /// + public SubpageReferenceListActivityItem(uint index, SubpageReferenceList owner, + ushort buttonMode, Action pressAction) + : base(index, owner) + { + Owner.GetBoolFeedbackSig(Index, 1).UserObject = pressAction; + Owner.UShortInputSig(Index, 1).UShortValue = buttonMode; + } + + /// + /// Called by SRL to release all referenced objects + /// + public override void Clear() + { + Owner.BoolInputSig(Index, 1).UserObject = null; + Owner.UShortInputSig(Index, 1).UShortValue = 0; + } + } +} \ No newline at end of file diff --git a/Essentials/PepperDashEssentials/UI/SubpageReferenceListCallStagingItem.cs b/Essentials/PepperDashEssentials/UI/SubpageReferenceListCallStagingItem.cs new file mode 100644 index 00000000..4c64bdd6 --- /dev/null +++ b/Essentials/PepperDashEssentials/UI/SubpageReferenceListCallStagingItem.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; +using Crestron.SimplSharpPro.UI; + +using PepperDash.Essentials.Core; + +namespace PepperDash.Essentials +{ + public class SubpageReferenceListButtonAndModeItem : SubpageReferenceListItem + { + /// + /// + /// + /// + /// + /// 0=Share, 1=Phone Call, 2=Video Call, 3=End Meeting + /// + public SubpageReferenceListButtonAndModeItem(uint index, SubpageReferenceList owner, + ushort buttonMode, Action pressAction) + : base(index, owner) + { + Owner.GetBoolFeedbackSig(Index, 1).UserObject = pressAction; + Owner.UShortInputSig(Index, 1).UShortValue = buttonMode; + } + + /// + /// Called by SRL to release all referenced objects + /// + public override void Clear() + { + Owner.BoolInputSig(Index, 1).UserObject = null; + Owner.UShortInputSig(Index, 1).UShortValue = 0; + } + } +} \ No newline at end of file diff --git a/Essentials/PepperDashEssentials/UI/SubpageReferenceListSourceItem.cs b/Essentials/PepperDashEssentials/UI/SubpageReferenceListSourceItem.cs new file mode 100644 index 00000000..73cc5e71 --- /dev/null +++ b/Essentials/PepperDashEssentials/UI/SubpageReferenceListSourceItem.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; +using Crestron.SimplSharpPro.UI; + +using PepperDash.Essentials.Core; + +namespace PepperDash.Essentials +{ + public class SubpageReferenceListSourceItem : SubpageReferenceListItem + { + public SourceListItem SourceItem { get; private set; } + + public SubpageReferenceListSourceItem(uint index, SubpageReferenceList owner, + SourceListItem sourceItem, Action routeAction) + : base(index, owner) + { + SourceItem = sourceItem; + owner.GetBoolFeedbackSig(index, 1).UserObject = new Action(routeAction); + owner.StringInputSig(index, 1).StringValue = SourceItem.PreferredName; + } + + public void RegisterForSourceChange(IHasCurrentSourceInfoChange room) + { + room.CurrentSingleSourceChange -= room_CurrentSourceInfoChange; + room.CurrentSingleSourceChange += room_CurrentSourceInfoChange; + } + + void room_CurrentSourceInfoChange(EssentialsRoomBase room, SourceListItem info, ChangeType type) + { + if (type == ChangeType.WillChange && info == SourceItem) + ClearFeedback(); + else if (type == ChangeType.DidChange && info == SourceItem) + SetFeedback(); + } + + /// + /// Called by SRL to release all referenced objects + /// + public override void Clear() + { + Owner.BoolInputSig(Index, 1).UserObject = null; + Owner.StringInputSig(Index, 1).StringValue = ""; + } + + /// + /// Sets the selected feedback on the button + /// + public void SetFeedback() + { + Owner.BoolInputSig(Index, 1).BoolValue = true; + } + + /// + /// Clears the selected feedback on the button + /// + public void ClearFeedback() + { + Owner.BoolInputSig(Index, 1).BoolValue = false; + } + } +} \ No newline at end of file diff --git a/Essentials/PepperDashEssentials/UI Drivers/DualDisplayRouting.cs b/Essentials/PepperDashEssentials/UIDrivers/DualDisplayRouting.cs similarity index 100% rename from Essentials/PepperDashEssentials/UI Drivers/DualDisplayRouting.cs rename to Essentials/PepperDashEssentials/UIDrivers/DualDisplayRouting.cs diff --git a/Essentials/PepperDashEssentials/UI Drivers/EssentialsHuddlePanelAvFunctionsDriver.cs b/Essentials/PepperDashEssentials/UIDrivers/Essentials/EssentialsHuddlePanelAvFunctionsDriver.cs similarity index 91% rename from Essentials/PepperDashEssentials/UI Drivers/EssentialsHuddlePanelAvFunctionsDriver.cs rename to Essentials/PepperDashEssentials/UIDrivers/Essentials/EssentialsHuddlePanelAvFunctionsDriver.cs index 6c599488..dc5447d6 100644 --- a/Essentials/PepperDashEssentials/UI Drivers/EssentialsHuddlePanelAvFunctionsDriver.cs +++ b/Essentials/PepperDashEssentials/UIDrivers/Essentials/EssentialsHuddlePanelAvFunctionsDriver.cs @@ -1,970 +1,973 @@ -using System; -using System.Collections.Generic; -using Crestron.SimplSharp; -using Crestron.SimplSharpPro; -using Crestron.SimplSharpPro.UI; - -using PepperDash.Core; -using PepperDash.Essentials.Core; -using PepperDash.Essentials.Core.SmartObjects; -using PepperDash.Essentials.Core.PageManagers; - -namespace PepperDash.Essentials -{ - /// - /// - /// - public class EssentialsHuddlePanelAvFunctionsDriver : PanelDriverBase - { - CrestronTouchpanelPropertiesConfig Config; - - public enum UiDisplayMode - { - PresentationMode, AudioSetup - } - - /// - /// Whether volume ramping from this panel will show the volume - /// gauge popup. - /// - public bool ShowVolumeGauge { get; set; } - - /// - /// The amount of time that the volume buttons stays on screen, in ms - /// - public uint VolumeButtonPopupTimeout - { - get { return VolumeButtonsPopupFeedback.TimeoutMs; } - set { VolumeButtonsPopupFeedback.TimeoutMs = value; } - } - - /// - /// The amount of time that the volume gauge stays on screen, in ms - /// - public uint VolumeGaugePopupTimeout - { - get { return VolumeGaugeFeedback.TimeoutMs; } - set { VolumeGaugeFeedback.TimeoutMs = value; } - } - - /// - /// - /// - public uint PowerOffTimeout { get; set; } - - /// - /// - /// - public string DefaultRoomKey - { - get { return _DefaultRoomKey; } - set - { - _DefaultRoomKey = value; - //CurrentRoom = DeviceManager.GetDeviceForKey(value) as EssentialsHuddleSpaceRoom; - } - } - string _DefaultRoomKey; - - /// - /// - /// - public EssentialsHuddleSpaceRoom CurrentRoom - { - get { return _CurrentRoom; } - set - { - SetCurrentRoom(value); - } - } - EssentialsHuddleSpaceRoom _CurrentRoom; - - /// - /// - /// - uint CurrentInterlockedModalJoin; - - /// - /// For hitting feedback - /// - BoolInputSig ShareButtonSig; - BoolInputSig EndMeetingButtonSig; - - /// - /// Controls the extended period that the volume gauge shows on-screen, - /// as triggered by Volume up/down operations - /// - BoolFeedbackPulseExtender VolumeGaugeFeedback; - - /// - /// Controls the period that the volume buttons show on non-hard-button - /// interfaces - /// - BoolFeedbackPulseExtender VolumeButtonsPopupFeedback; - - /// - /// The parent driver for this - /// - PanelDriverBase Parent; - - /// - /// All children attached to this driver. For hiding and showing as a group. - /// - List ChildDrivers = new List(); - - List CurrentDisplayModeSigsInUse = new List(); - - //// Important smart objects - - /// - /// Smart Object 3200 - /// - SubpageReferenceList SourcesSrl; - - /// - /// Smart Object 15022 - /// - SubpageReferenceList ActivityFooterSrl; - - /// - /// Tracks which audio page group the UI is in - /// - UiDisplayMode CurrentDisplayMode; - - /// - /// The AV page mangagers that have been used, to keep them alive for later - /// - Dictionary PageManagers = new Dictionary(); - - /// - /// Current page manager running for a source - /// - PageManager CurrentSourcePageManager; - - /// - /// Will auto-timeout a power off - /// - CTimer PowerOffTimer; - - ModalDialog PowerDownModal; - - ModalDialog WarmingCoolingModal; - - /// - /// Constructor - /// - public EssentialsHuddlePanelAvFunctionsDriver(PanelDriverBase parent, CrestronTouchpanelPropertiesConfig config) - : base(parent.TriList) - { - Config = config; - Parent = parent; - - SourcesSrl = new SubpageReferenceList(TriList, 3200, 3, 3, 3); - ActivityFooterSrl = new SubpageReferenceList(TriList, 15022, 3, 3, 3); - ShareButtonSig = ActivityFooterSrl.BoolInputSig(1, 1); - - SetupActivityFooterWhenRoomOff(); - - ShowVolumeGauge = true; - - // One-second pulse extender for volume gauge - VolumeGaugeFeedback = new BoolFeedbackPulseExtender(1500); - VolumeGaugeFeedback.Feedback - .LinkInputSig(TriList.BooleanInput[UIBoolJoin.VolumeGaugePopupVisible]); - - VolumeButtonsPopupFeedback = new BoolFeedbackPulseExtender(4000); - VolumeButtonsPopupFeedback.Feedback - .LinkInputSig(TriList.BooleanInput[UIBoolJoin.VolumeButtonPopupVisible]); - - PowerOffTimeout = 30000; - - TriList.StringInput[UIStringJoin.StartActivityText].StringValue = - "Tap Share to begin"; - } - - /// - /// - /// - public override void Show() - { - TriList.BooleanInput[UIBoolJoin.TopBarVisible].BoolValue = true; - TriList.BooleanInput[UIBoolJoin.ActivityFooterVisible].BoolValue = true; - - // Default to showing rooms/sources now. - ShowMode(UiDisplayMode.PresentationMode); - - // Attach actions - TriList.SetSigFalseAction(UIBoolJoin.VolumeButtonPopupPress, VolumeButtonsTogglePress); - - //Interlocked modals - TriList.SetSigFalseAction(UIBoolJoin.InterlockedModalClosePress, HideCurrentInterlockedModal); - TriList.SetSigFalseAction(UIBoolJoin.HelpPress, () => - { - string message = null; - var room = DeviceManager.GetDeviceForKey(Config.DefaultRoomKey) - as EssentialsHuddleSpaceRoom; - if (room != null) - message = room.Config.HelpMessage; - else - message = "Sorry, no help message available. No room connected."; - TriList.StringInput[UIStringJoin.HelpMessage].StringValue = message; - ShowInterlockedModal(UIBoolJoin.HelpPageVisible); - }); - - //TriList.SetSigFalseAction(UIBoolJoin.RoomHeaderButtonPress, () => - // ShowInterlockedModal(UIBoolJoin.RoomHeaderPageVisible)); - - // Setup button - TriList.SetSigHeldAction(UIBoolJoin.GearHeaderButtonPress, 2000, - () => ShowInterlockedModal(UIBoolJoin.TechPanelSetupVisible)); - TriList.SetSigFalseAction(UIBoolJoin.TechPagesExitButton, () => - HideCurrentInterlockedModal()); -#warning This gets overridden by config after NYU demo - if(TriList is CrestronApp) - TriList.BooleanInput[UIBoolJoin.GearButtonVisible].BoolValue = false; - else - TriList.BooleanInput[UIBoolJoin.GearButtonVisible].BoolValue = true; - - // power-related functions - // Note: some of these are not directly-related to the huddle space UI, but are held over - // in case - TriList.SetSigFalseAction(UIBoolJoin.ShowPowerOffPress, PowerButtonPressed); - TriList.SetSigFalseAction(UIBoolJoin.PowerOffMorePress, () => - { - CancelPowerOffTimer(); - TriList.BooleanInput[UIBoolJoin.PowerOffStep1Visible].BoolValue = false; - TriList.BooleanInput[UIBoolJoin.PowerOffStep2Visible].BoolValue = true; - }); - TriList.SetSigFalseAction(UIBoolJoin.DisplayPowerTogglePress, () => - { - if (CurrentRoom != null && CurrentRoom.DefaultDisplay is IPower) - (CurrentRoom.DefaultDisplay as IPower).PowerToggle(); - }); - - base.Show(); - } - - /// - /// Handler for room on/off feedback - /// - /// - /// - //void OnFeedback_OutputChange(object sender, EventArgs e) - //{ - - //} - - public override void Hide() - { - HideAndClearCurrentDisplayModeSigsInUse(); - TriList.BooleanInput[UIBoolJoin.TopBarVisible].BoolValue = false; - TriList.BooleanInput[UIBoolJoin.ActivityFooterVisible].BoolValue = false; - TriList.BooleanInput[UIBoolJoin.StartPageVisible].BoolValue = false; - TriList.BooleanInput[UIBoolJoin.TapToBeginVisible].BoolValue = false; - TriList.BooleanInput[UIBoolJoin.SelectASourceVisible].BoolValue = false; - //TriList.BooleanInput[UIBoolJoin.StagingPageVisible].BoolValue = false; - VolumeButtonsPopupFeedback.ClearNow(); - //CancelPowerOff(); - - base.Hide(); - } - - /// - /// Shows the various "modes" that this driver controls. Presentation, Setup page - /// - /// - public void ShowMode(UiDisplayMode mode) - { - //Clear whatever is showing now. - HideAndClearCurrentDisplayModeSigsInUse(); - CurrentDisplayMode = mode; - switch (mode) - { - case UiDisplayMode.PresentationMode: - // show start page or staging... - if (CurrentRoom.OnFeedback.BoolValue) - { - TriList.BooleanInput[UIBoolJoin.StagingPageVisible].BoolValue = true; - TriList.BooleanInput[UIBoolJoin.TapToBeginVisible].BoolValue = false; - TriList.BooleanInput[UIBoolJoin.SelectASourceVisible].BoolValue = false; - } - else - { - TriList.BooleanInput[UIBoolJoin.StartPageVisible].BoolValue = true; - TriList.BooleanInput[UIBoolJoin.TapToBeginVisible].BoolValue = true; - TriList.BooleanInput[UIBoolJoin.SelectASourceVisible].BoolValue = false; - } - // Date/time - if (Config.ShowDate && Config.ShowTime) - { - TriList.BooleanInput[UIBoolJoin.DateAndTimeVisible].BoolValue = true; - TriList.BooleanInput[UIBoolJoin.DateOnlyVisible].BoolValue = false; - TriList.BooleanInput[UIBoolJoin.TimeOnlyVisible].BoolValue = false; - } - else - { - TriList.BooleanInput[UIBoolJoin.DateAndTimeVisible].BoolValue = false; - TriList.BooleanInput[UIBoolJoin.DateOnlyVisible].BoolValue = Config.ShowDate; - TriList.BooleanInput[UIBoolJoin.TimeOnlyVisible].BoolValue = Config.ShowTime; - } - - ShowCurrentDisplayModeSigsInUse(); - break; - } - } - - /// - /// When the room is off, set the footer SRL - /// - void SetupActivityFooterWhenRoomOff() - { - ActivityFooterSrl.Clear(); - ActivityFooterSrl.AddItem(new SubpageReferenceListActivityItem(1, ActivityFooterSrl, 0, - b => { if (!b) ShareButtonPressed(); })); - ActivityFooterSrl.Count = 1; - TriList.UShortInput[UIUshortJoin.PresentationListCaretMode].UShortValue = 0; - ShareButtonSig.BoolValue = false; - } - - /// - /// Sets up the footer SRL for when the room is on - /// - void SetupActivityFooterWhenRoomOn() - { - ActivityFooterSrl.Clear(); - ActivityFooterSrl.AddItem(new SubpageReferenceListActivityItem(1, ActivityFooterSrl, - 0, null)); - ActivityFooterSrl.AddItem(new SubpageReferenceListActivityItem(2, ActivityFooterSrl, - 3, b => { if (!b) PowerButtonPressed(); })); - ActivityFooterSrl.Count = 2; - TriList.UShortInput[UIUshortJoin.PresentationListCaretMode].UShortValue = 1; - EndMeetingButtonSig = ActivityFooterSrl.BoolInputSig(2, 1); - ShareButtonSig.BoolValue = CurrentRoom.OnFeedback.BoolValue; - } - - /// - /// Attached to activity list share button - /// - void ShareButtonPressed() - { - ShareButtonSig.BoolValue = true; - TriList.BooleanInput[UIBoolJoin.StartPageVisible].BoolValue = false; - TriList.BooleanInput[UIBoolJoin.StagingPageVisible].BoolValue = true; - TriList.BooleanInput[UIBoolJoin.SelectASourceVisible].BoolValue = true; - // Run default source when room is off and share is pressed - if (!CurrentRoom.OnFeedback.BoolValue) - CurrentRoom.RunDefaultRoute(); - } - - void ShowInterlockedModal(uint join) - { - if (CurrentInterlockedModalJoin == join) - HideCurrentInterlockedModal(); - else - { - // sets the sig true if the join is right - TriList.BooleanInput[UIBoolJoin.HelpPageVisible].BoolValue = join == UIBoolJoin.HelpPageVisible; - TriList.BooleanInput[UIBoolJoin.RoomHeaderPageVisible].BoolValue = join == UIBoolJoin.RoomHeaderPageVisible; - TriList.BooleanInput[UIBoolJoin.VolumesPageVisible].BoolValue = join == UIBoolJoin.VolumesPageVisible; - TriList.BooleanInput[UIBoolJoin.TechPanelSetupVisible].BoolValue = join == UIBoolJoin.TechPanelSetupVisible; - CurrentInterlockedModalJoin = join; - } - } - - void HideCurrentInterlockedModal() - { - TriList.BooleanInput[CurrentInterlockedModalJoin].BoolValue = false; - CurrentInterlockedModalJoin = 0; - } - - - /// - /// Shows all sigs that are in CurrentDisplayModeSigsInUse - /// - void ShowCurrentDisplayModeSigsInUse() - { - foreach (var sig in CurrentDisplayModeSigsInUse) - sig.BoolValue = true; - } - - /// - /// Hides all CurrentDisplayModeSigsInUse sigs and clears the array - /// - void HideAndClearCurrentDisplayModeSigsInUse() - { - foreach (var sig in CurrentDisplayModeSigsInUse) - sig.BoolValue = false; - CurrentDisplayModeSigsInUse.Clear(); - } - - /// - /// Send the UI back depending on location, not used in huddle UI - /// - public override void BackButtonPressed() - { - switch (CurrentDisplayMode) - { - case UiDisplayMode.PresentationMode: - //CancelReturnToSourceTimer(); - BackToHome(); - break; - } - } - - /// - /// - /// - void BackToHome() - { - Hide(); - Parent.Show(); - } - - /// - /// Loads the appropriate Sigs into CurrentDisplayModeSigsInUse and shows them - /// - void ShowCurrentSource() - { - if (CurrentRoom.CurrentSourceInfo == null) - return; - - var uiDev = CurrentRoom.CurrentSourceInfo.SourceDevice as IUiDisplayInfo; - PageManager pm = null; - // If we need a page manager, get an appropriate one - if (uiDev != null) - { - TriList.BooleanInput[UIBoolJoin.SelectASourceVisible].BoolValue = false; - // Got an existing page manager, get it - if (PageManagers.ContainsKey(uiDev)) - pm = PageManagers[uiDev]; - // Otherwise make an apporiate one - else if (uiDev is ISetTopBoxControls) - //pm = new SetTopBoxMediumPageManager(uiDev as ISetTopBoxControls, TriList); - pm = new SetTopBoxThreePanelPageManager(uiDev as ISetTopBoxControls, TriList); - else if (uiDev is IDiscPlayerControls) - pm = new DiscPlayerMediumPageManager(uiDev as IDiscPlayerControls, TriList); - else - pm = new DefaultPageManager(uiDev, TriList); - PageManagers[uiDev] = pm; - CurrentSourcePageManager = pm; - pm.Show(); - } - } - - /// - /// Called from button presses on source, where We can assume we want - /// to change to the proper screen. - /// - /// The key name of the route to run - void UiSelectSource(string key) - { - // Run the route and when it calls back, show the source - CurrentRoom.RunRouteAction(key, null); - } - - /// - /// - /// - public void PowerButtonPressed() - { - if (!CurrentRoom.OnFeedback.BoolValue - || CurrentRoom.ShutdownPromptTimer.IsRunningFeedback.BoolValue) - return; - - CurrentRoom.StartShutdown(ShutdownType.Manual); - } - - /// - /// - /// - /// - /// - void ShutdownPromptTimer_HasStarted(object sender, EventArgs e) - { - // Do we need to check where the UI is? No? - var timer = CurrentRoom.ShutdownPromptTimer; - EndMeetingButtonSig.BoolValue = true; - ShareButtonSig.BoolValue = false; - - if (CurrentRoom.ShutdownType == ShutdownType.Manual) - { - PowerDownModal = new ModalDialog(TriList); - var message = string.Format("Meeting will end in {0} seconds", CurrentRoom.ShutdownPromptSeconds); - - //// figure out a cleaner way to update gauge - //var gauge = CurrentRoom.ShutdownPromptTimer.PercentFeedback; - //EventHandler gaugeHandler = null; - //gaugeHandler = (o, a) => TriList.UShortInput[ModalDialog.TimerGaugeJoin].UShortValue = - // (ushort)(gauge.UShortValue * 65535 / 100); - //gauge.OutputChange += gaugeHandler; - - // Attach timer things to modal - CurrentRoom.ShutdownPromptTimer.TimeRemainingFeedback.OutputChange += ShutdownPromptTimer_TimeRemainingFeedback_OutputChange; - CurrentRoom.ShutdownPromptTimer.PercentFeedback.OutputChange += ShutdownPromptTimer_PercentFeedback_OutputChange; - - // respond to offs by cancelling dialog - var onFb = CurrentRoom.OnFeedback; - EventHandler offHandler = null; - offHandler = (o, a) => - { - if (!onFb.BoolValue) - { - EndMeetingButtonSig.BoolValue = false; - PowerDownModal.HideDialog(); - onFb.OutputChange -= offHandler; - //gauge.OutputChange -= gaugeHandler; - } - }; - onFb.OutputChange += offHandler; - - PowerDownModal.PresentModalDialog(2, "End Meeting", "Power", message, "Cancel", "End Meeting Now", true, true, - but => - { - if (but != 2) // any button except for End cancels - timer.Cancel(); - else - timer.Finish(); - }); - } - } - - /// - /// - /// - /// - /// - void ShutdownPromptTimer_HasFinished(object sender, EventArgs e) - { - //Debug.Console(2, "*#*UI shutdown prompt finished"); - EndMeetingButtonSig.BoolValue = false; - CurrentRoom.ShutdownPromptTimer.TimeRemainingFeedback.OutputChange -= ShutdownPromptTimer_TimeRemainingFeedback_OutputChange; - CurrentRoom.ShutdownPromptTimer.PercentFeedback.OutputChange -= ShutdownPromptTimer_PercentFeedback_OutputChange; - - } - - /// - /// - /// - /// - /// - void ShutdownPromptTimer_WasCancelled(object sender, EventArgs e) - { - //Debug.Console(2, "*#*UI shutdown prompt cancelled"); - if (PowerDownModal != null) - PowerDownModal.HideDialog(); - EndMeetingButtonSig.BoolValue = false; - ShareButtonSig.BoolValue = CurrentRoom.OnFeedback.BoolValue; - - CurrentRoom.ShutdownPromptTimer.TimeRemainingFeedback.OutputChange += ShutdownPromptTimer_TimeRemainingFeedback_OutputChange; - CurrentRoom.ShutdownPromptTimer.PercentFeedback.OutputChange -= ShutdownPromptTimer_PercentFeedback_OutputChange; - } - - void ShutdownPromptTimer_TimeRemainingFeedback_OutputChange(object sender, EventArgs e) - { - - var message = string.Format("Meeting will end in {0} seconds", (sender as StringFeedback).StringValue); - TriList.StringInput[ModalDialog.MessageTextJoin].StringValue = message; - } - - void ShutdownPromptTimer_PercentFeedback_OutputChange(object sender, EventArgs e) - { - var value = (ushort)((sender as IntFeedback).UShortValue * 65535 / 100); - TriList.UShortInput[ModalDialog.TimerGaugeJoin].UShortValue = value; - } - - /// - /// - /// - void CancelPowerOffTimer() - { - if (PowerOffTimer != null) - { - PowerOffTimer.Stop(); - PowerOffTimer = null; - } - } - - /// - /// - /// - void VolumeButtonsTogglePress() - { - if (VolumeButtonsPopupFeedback.BoolValue) - VolumeButtonsPopupFeedback.ClearNow(); - else - { - // Trigger the popup - VolumeButtonsPopupFeedback.BoolValue = true; - VolumeButtonsPopupFeedback.BoolValue = false; - } - } - - /// - /// - /// - /// - public void VolumeUpPress(bool state) - { - // extend timeouts - if (ShowVolumeGauge) - VolumeGaugeFeedback.BoolValue = state; - VolumeButtonsPopupFeedback.BoolValue = state; - if (CurrentRoom.CurrentVolumeControls != null) - CurrentRoom.CurrentVolumeControls.VolumeUp(state); - } - - /// - /// - /// - /// - public void VolumeDownPress(bool state) - { - // extend timeouts - if (ShowVolumeGauge) - VolumeGaugeFeedback.BoolValue = state; - VolumeButtonsPopupFeedback.BoolValue = state; - if (CurrentRoom.CurrentVolumeControls != null) - CurrentRoom.CurrentVolumeControls.VolumeDown(state); - } - - /// - /// Helper for property setter. Sets the panel to the given room, latching up all functionality - /// - void SetCurrentRoom(EssentialsHuddleSpaceRoom room) - { - if (_CurrentRoom == room) return; - // Disconnect current (probably never called) - if (_CurrentRoom != null) - { - // Disconnect current room - _CurrentRoom.CurrentVolumeDeviceChange -= this.CurrentRoom_CurrentAudioDeviceChange; - ClearAudioDeviceConnections(); - _CurrentRoom.CurrentSingleSourceChange -= this.CurrentRoom_SourceInfoChange; - DisconnectSource(_CurrentRoom.CurrentSourceInfo); - _CurrentRoom.ShutdownPromptTimer.HasStarted -= ShutdownPromptTimer_HasStarted; - _CurrentRoom.ShutdownPromptTimer.HasFinished -= ShutdownPromptTimer_HasFinished; - _CurrentRoom.ShutdownPromptTimer.WasCancelled -= ShutdownPromptTimer_WasCancelled; - - _CurrentRoom.OnFeedback.OutputChange += CurrentRoom_OnFeedback_OutputChange; - _CurrentRoom.IsWarmingUpFeedback.OutputChange -= CurrentRoom_IsWarmingFeedback_OutputChange; - _CurrentRoom.IsCoolingDownFeedback.OutputChange -= IsCoolingDownFeedback_OutputChange; - } - - _CurrentRoom = room; - - if (_CurrentRoom != null) - { - // get the source list config and set up the source list - var config = ConfigReader.ConfigObject.SourceLists; - if (config.ContainsKey(_CurrentRoom.SourceListKey)) - { - var srcList = config[_CurrentRoom.SourceListKey]; - // Setup sources list - uint i = 1; // counter for UI list - foreach (var kvp in srcList) - { - var srcConfig = kvp.Value; - if (!srcConfig.IncludeInSourceList) // Skip sources marked this way - continue; - - var actualSource = DeviceManager.GetDeviceForKey(srcConfig.SourceKey) as Device; - if (actualSource == null) - { - Debug.Console(1, "Cannot assign missing source '{0}' to source UI list", - srcConfig.SourceKey); - continue; - } - var routeKey = kvp.Key; - var item = new SubpageReferenceListSourceItem(i++, SourcesSrl, srcConfig, - b => { if (!b) UiSelectSource(routeKey); }); - SourcesSrl.AddItem(item); // add to the SRL - item.RegisterForSourceChange(_CurrentRoom); - } - SourcesSrl.Count = (ushort)(i - 1); - } - // Name and logo - TriList.StringInput[UIStringJoin.CurrentRoomName].StringValue = _CurrentRoom.Name; - if (_CurrentRoom.LogoUrl == null) - { - TriList.BooleanInput[UIBoolJoin.LogoDefaultVisible].BoolValue = true; - TriList.BooleanInput[UIBoolJoin.LogoUrlVisible].BoolValue = false; - } - else - { - TriList.BooleanInput[UIBoolJoin.LogoDefaultVisible].BoolValue = false; - TriList.BooleanInput[UIBoolJoin.LogoUrlVisible].BoolValue = true; - TriList.StringInput[UIStringJoin.LogoUrl].StringValue = _CurrentRoom.LogoUrl; - } - - // Shutdown timer - _CurrentRoom.ShutdownPromptTimer.HasStarted += ShutdownPromptTimer_HasStarted; - _CurrentRoom.ShutdownPromptTimer.HasFinished += ShutdownPromptTimer_HasFinished; - _CurrentRoom.ShutdownPromptTimer.WasCancelled += ShutdownPromptTimer_WasCancelled; - - // Link up all the change events from the room - _CurrentRoom.OnFeedback.OutputChange += CurrentRoom_OnFeedback_OutputChange; - CurrentRoom_SyncOnFeedback(); - _CurrentRoom.IsWarmingUpFeedback.OutputChange += CurrentRoom_IsWarmingFeedback_OutputChange; - _CurrentRoom.IsCoolingDownFeedback.OutputChange += IsCoolingDownFeedback_OutputChange; - - _CurrentRoom.CurrentVolumeDeviceChange += CurrentRoom_CurrentAudioDeviceChange; - RefreshAudioDeviceConnections(); - _CurrentRoom.CurrentSingleSourceChange += CurrentRoom_SourceInfoChange; - RefreshSourceInfo(); - } - else - { - // Clear sigs that need to be - TriList.StringInput[UIStringJoin.CurrentRoomName].StringValue = "Select a room"; - } - } - - /// - /// For room on/off changes - /// - void CurrentRoom_OnFeedback_OutputChange(object sender, EventArgs e) - { - CurrentRoom_SyncOnFeedback(); - } - - void CurrentRoom_SyncOnFeedback() - { - var value = _CurrentRoom.OnFeedback.BoolValue; - //Debug.Console(2, CurrentRoom, "UI: Is on event={0}", value); - TriList.BooleanInput[UIBoolJoin.RoomIsOn].BoolValue = value; - - if (value) //ON - { - SetupActivityFooterWhenRoomOn(); - TriList.BooleanInput[UIBoolJoin.SelectASourceVisible].BoolValue = false; - TriList.BooleanInput[UIBoolJoin.StagingPageVisible].BoolValue = true; - TriList.BooleanInput[UIBoolJoin.StartPageVisible].BoolValue = false; - TriList.BooleanInput[UIBoolJoin.VolumeSingleMute1Visible].BoolValue = true; - - } - else - { - SetupActivityFooterWhenRoomOff(); - TriList.BooleanInput[UIBoolJoin.StartPageVisible].BoolValue = true; - TriList.BooleanInput[UIBoolJoin.VolumeSingleMute1Visible].BoolValue = false; - TriList.BooleanInput[UIBoolJoin.StagingPageVisible].BoolValue = false; - } - } - - /// - /// - /// - void CurrentRoom_IsWarmingFeedback_OutputChange(object sender, EventArgs e) - { - var value = CurrentRoom.IsWarmingUpFeedback.BoolValue; - //Debug.Console(2, CurrentRoom, "UI: WARMING event={0}", value); - - if (value) - { - WarmingCoolingModal = new ModalDialog(TriList); - WarmingCoolingModal.PresentModalDialog(0, "Powering Up", "Power", "

Room is powering up

Please wait

", - "", "", false, false, null); - } - else - { - if (WarmingCoolingModal != null) - WarmingCoolingModal.CancelDialog(); - } - } - - - void IsCoolingDownFeedback_OutputChange(object sender, EventArgs e) - { - var value = CurrentRoom.IsCoolingDownFeedback.BoolValue; - //Debug.Console(2, CurrentRoom, "UI: Cooldown event={0}", value); - - if (value) - { - WarmingCoolingModal = new ModalDialog(TriList); - WarmingCoolingModal.PresentModalDialog(0, "Shut Down", "Power", "

Room is shutting down

Please wait

", - "", "", false, false, null); - } - else - { - if (WarmingCoolingModal != null) - WarmingCoolingModal.CancelDialog(); - } - } - - /// - /// Hides source for provided source info - /// - /// - void DisconnectSource(SourceListItem previousInfo) - { - if (previousInfo == null) return; - - // Hide whatever is showing - if (IsVisible) - { - if (CurrentSourcePageManager != null) - { - CurrentSourcePageManager.Hide(); - CurrentSourcePageManager = null; - } - } - - if (previousInfo == null) return; - var previousDev = previousInfo.SourceDevice; - - // device type interfaces - if (previousDev is ISetTopBoxControls) - (previousDev as ISetTopBoxControls).UnlinkButtons(TriList); - // common interfaces - if (previousDev is IChannel) - (previousDev as IChannel).UnlinkButtons(TriList); - if (previousDev is IColor) - (previousDev as IColor).UnlinkButtons(TriList); - if (previousDev is IDPad) - (previousDev as IDPad).UnlinkButtons(TriList); - if (previousDev is IDvr) - (previousDev as IDvr).UnlinkButtons(TriList); - if (previousDev is INumericKeypad) - (previousDev as INumericKeypad).UnlinkButtons(TriList); - if (previousDev is IPower) - (previousDev as IPower).UnlinkButtons(TriList); - if (previousDev is ITransport) - (previousDev as ITransport).UnlinkButtons(TriList); - //if (previousDev is IRadio) - // (previousDev as IRadio).UnlinkButtons(this); - } - - /// - /// Refreshes and shows the room's current source - /// - void RefreshSourceInfo() - { - var routeInfo = CurrentRoom.CurrentSourceInfo; - // This will show off popup too - if (this.IsVisible) - ShowCurrentSource(); - - if (routeInfo == null)// || !CurrentRoom.OnFeedback.BoolValue) - { - // Check for power off and insert "Room is off" - TriList.StringInput[UIStringJoin.CurrentSourceName].StringValue = "Room is off"; - TriList.StringInput[UIStringJoin.CurrentSourceIcon].StringValue = "Power"; - this.Hide(); - Parent.Show(); - return; - } - else if (CurrentRoom.CurrentSourceInfo != null) - { - TriList.StringInput[UIStringJoin.CurrentSourceName].StringValue = routeInfo.PreferredName; - TriList.StringInput[UIStringJoin.CurrentSourceIcon].StringValue = routeInfo.Icon; // defaults to "blank" - } - else - { - TriList.StringInput[UIStringJoin.CurrentSourceName].StringValue = "---"; - TriList.StringInput[UIStringJoin.CurrentSourceIcon].StringValue = "Blank"; - } - - // Connect controls - if (routeInfo.SourceDevice != null) - ConnectControlDeviceMethods(routeInfo.SourceDevice); - } - - /// - /// Attach the source to the buttons and things - /// - void ConnectControlDeviceMethods(Device dev) - { - if(dev is ISetTopBoxControls) - (dev as ISetTopBoxControls).LinkButtons(TriList); - if (dev is IChannel) - (dev as IChannel).LinkButtons(TriList); - if (dev is IColor) - (dev as IColor).LinkButtons(TriList); - if (dev is IDPad) - (dev as IDPad).LinkButtons(TriList); - if (dev is IDvr) - (dev as IDvr).LinkButtons(TriList); - if (dev is INumericKeypad) - (dev as INumericKeypad).LinkButtons(TriList); - if (dev is IPower) - (dev as IPower).LinkButtons(TriList); - if (dev is ITransport) - (dev as ITransport).LinkButtons(TriList); - //if (dev is IRadio) - // (dev as IRadio).LinkButtons(this); // +++++++++++++ Make part of this into page manager - - //if (dev is ICustomFunctions) - //{ - // var custBridge = (dev as ICustomFunctions).GetCustomBridge(); - // custBridge.Link(this.Remote); - } - - /// - /// Detaches the buttons and feedback from the room's current audio device - /// - void ClearAudioDeviceConnections() - { - TriList.ClearBoolSigAction(UIBoolJoin.VolumeUpPress); - TriList.ClearBoolSigAction(UIBoolJoin.VolumeDownPress); - TriList.ClearBoolSigAction(UIBoolJoin.Volume1ProgramMutePressAndFB); - - var fDev = CurrentRoom.CurrentVolumeControls as IBasicVolumeWithFeedback; - if (fDev != null) - { - TriList.ClearUShortSigAction(UIUshortJoin.VolumeSlider1Value); - fDev.VolumeLevelFeedback.UnlinkInputSig( - TriList.UShortInput[UIUshortJoin.VolumeSlider1Value]); - } - } - - /// - /// Attaches the buttons and feedback to the room's current audio device - /// - void RefreshAudioDeviceConnections() - { - var dev = CurrentRoom.CurrentVolumeControls; - if (dev != null) // connect buttons - { - TriList.SetBoolSigAction(UIBoolJoin.VolumeUpPress, VolumeUpPress); - TriList.SetBoolSigAction(UIBoolJoin.VolumeDownPress, VolumeDownPress); - TriList.SetSigFalseAction(UIBoolJoin.Volume1ProgramMutePressAndFB, dev.MuteToggle); - } - - var fbDev = dev as IBasicVolumeWithFeedback; - if (fbDev == null) // this should catch both IBasicVolume and IBasicVolumeWithFeeback - TriList.UShortInput[UIUshortJoin.VolumeSlider1Value].UShortValue = 0; - else - { - // slider - TriList.SetUShortSigAction(UIUshortJoin.VolumeSlider1Value, fbDev.SetVolume); - // feedbacks - fbDev.MuteFeedback.LinkInputSig(TriList.BooleanInput[UIBoolJoin.Volume1ProgramMutePressAndFB]); - fbDev.VolumeLevelFeedback.LinkInputSig( - TriList.UShortInput[UIUshortJoin.VolumeSlider1Value]); - } - } - - /// - /// Handler for when the room's volume control device changes - /// - void CurrentRoom_CurrentAudioDeviceChange(object sender, VolumeDeviceChangeEventArgs args) - { - if (args.Type == ChangeType.WillChange) - ClearAudioDeviceConnections(); - else // did change - RefreshAudioDeviceConnections(); - } - - /// - /// Handles source change - /// - void CurrentRoom_SourceInfoChange(EssentialsRoomBase room, - SourceListItem info, ChangeType change) - { - if (change == ChangeType.WillChange) - DisconnectSource(info); - else - RefreshSourceInfo(); - } - } +using System; +using System.Collections.Generic; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; +using Crestron.SimplSharpPro.UI; + +using PepperDash.Core; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.SmartObjects; +using PepperDash.Essentials.Core.PageManagers; + +namespace PepperDash.Essentials +{ + /// + /// + /// + public class EssentialsHuddlePanelAvFunctionsDriver : PanelDriverBase + { + CrestronTouchpanelPropertiesConfig Config; + + public enum UiDisplayMode + { + PresentationMode, AudioSetup + } + + /// + /// Whether volume ramping from this panel will show the volume + /// gauge popup. + /// + public bool ShowVolumeGauge { get; set; } + + /// + /// The amount of time that the volume buttons stays on screen, in ms + /// + public uint VolumeButtonPopupTimeout + { + get { return VolumeButtonsPopupFeedback.TimeoutMs; } + set { VolumeButtonsPopupFeedback.TimeoutMs = value; } + } + + /// + /// The amount of time that the volume gauge stays on screen, in ms + /// + public uint VolumeGaugePopupTimeout + { + get { return VolumeGaugeFeedback.TimeoutMs; } + set { VolumeGaugeFeedback.TimeoutMs = value; } + } + + /// + /// + /// + public uint PowerOffTimeout { get; set; } + + /// + /// + /// + public string DefaultRoomKey + { + get { return _DefaultRoomKey; } + set + { + _DefaultRoomKey = value; + //CurrentRoom = DeviceManager.GetDeviceForKey(value) as EssentialsHuddleSpaceRoom; + } + } + string _DefaultRoomKey; + + /// + /// + /// + public EssentialsHuddleSpaceRoom CurrentRoom + { + get { return _CurrentRoom; } + set + { + SetCurrentRoom(value); + } + } + EssentialsHuddleSpaceRoom _CurrentRoom; + + /// + /// + /// + //uint CurrentInterlockedModalJoin; + + /// + /// For hitting feedback + /// + BoolInputSig ShareButtonSig; + BoolInputSig EndMeetingButtonSig; + + /// + /// Controls the extended period that the volume gauge shows on-screen, + /// as triggered by Volume up/down operations + /// + BoolFeedbackPulseExtender VolumeGaugeFeedback; + + /// + /// Controls the period that the volume buttons show on non-hard-button + /// interfaces + /// + BoolFeedbackPulseExtender VolumeButtonsPopupFeedback; + + /// + /// The parent driver for this + /// + PanelDriverBase Parent; + + /// + /// All children attached to this driver. For hiding and showing as a group. + /// + List ChildDrivers = new List(); + + List CurrentDisplayModeSigsInUse = new List(); + + //// Important smart objects + + /// + /// Smart Object 3200 + /// + SubpageReferenceList SourcesSrl; + + /// + /// Smart Object 15022 + /// + SubpageReferenceList ActivityFooterSrl; + + /// + /// Tracks which audio page group the UI is in + /// + UiDisplayMode CurrentDisplayMode; + + /// + /// The AV page mangagers that have been used, to keep them alive for later + /// + Dictionary PageManagers = new Dictionary(); + + /// + /// Current page manager running for a source + /// + PageManager CurrentSourcePageManager; + + /// + /// Will auto-timeout a power off + /// + CTimer PowerOffTimer; + + ModalDialog PowerDownModal; + + ModalDialog WarmingCoolingModal; + + JoinedSigInterlock PopupInterlock; + + /// + /// Constructor + /// + public EssentialsHuddlePanelAvFunctionsDriver(PanelDriverBase parent, CrestronTouchpanelPropertiesConfig config) + : base(parent.TriList) + { + Config = config; + Parent = parent; + PopupInterlock = new JoinedSigInterlock(TriList); + + SourcesSrl = new SubpageReferenceList(TriList, 3200, 3, 3, 3); + ActivityFooterSrl = new SubpageReferenceList(TriList, 15022, 3, 3, 3); + ShareButtonSig = ActivityFooterSrl.BoolInputSig(1, 1); + + SetupActivityFooterWhenRoomOff(); + + ShowVolumeGauge = true; + + // One-second pulse extender for volume gauge + VolumeGaugeFeedback = new BoolFeedbackPulseExtender(1500); + VolumeGaugeFeedback.Feedback + .LinkInputSig(TriList.BooleanInput[UIBoolJoin.VolumeGaugePopupVisible]); + + VolumeButtonsPopupFeedback = new BoolFeedbackPulseExtender(4000); + VolumeButtonsPopupFeedback.Feedback + .LinkInputSig(TriList.BooleanInput[UIBoolJoin.VolumeButtonPopupVisible]); + + PowerOffTimeout = 30000; + + TriList.StringInput[UIStringJoin.StartActivityText].StringValue = + "Tap Share to begin"; + } + + /// + /// + /// + public override void Show() + { + TriList.BooleanInput[UIBoolJoin.TopBarHabaneroVisible].BoolValue = true; + TriList.BooleanInput[UIBoolJoin.ActivityFooterVisible].BoolValue = true; + + // Default to showing rooms/sources now. + ShowMode(UiDisplayMode.PresentationMode); + + // Attach actions + TriList.SetSigFalseAction(UIBoolJoin.VolumeButtonPopupPress, VolumeButtonsTogglePress); + + //Interlocked modals + TriList.SetSigFalseAction(UIBoolJoin.InterlockedModalClosePress, PopupInterlock.HideAndClear);// HideCurrentInterlockedModal); + TriList.SetSigFalseAction(UIBoolJoin.HelpPress, () => + { + string message = null; + var room = DeviceManager.GetDeviceForKey(Config.DefaultRoomKey) + as EssentialsHuddleSpaceRoom; + if (room != null) + message = room.Config.HelpMessage; + else + message = "Sorry, no help message available. No room connected."; + TriList.StringInput[UIStringJoin.HelpMessage].StringValue = message; + PopupInterlock.ShowInterlockedWithToggle(UIBoolJoin.HelpPageVisible); // ShowInterlockedModal(UIBoolJoin.HelpPageVisible); + }); + + //TriList.SetSigFalseAction(UIBoolJoin.RoomHeaderButtonPress, () => + // ShowInterlockedModal(UIBoolJoin.RoomHeaderPageVisible)); + + // Setup button + TriList.SetSigHeldAction(UIBoolJoin.GearHeaderButtonPress, 2000, + () => PopupInterlock.ShowInterlockedWithToggle(UIBoolJoin.TechPanelSetupVisible));// ShowInterlockedModal(UIBoolJoin.TechPanelSetupVisible)); + TriList.SetSigFalseAction(UIBoolJoin.TechPagesExitButton, () => + PopupInterlock.HideAndClear()); // HideCurrentInterlockedModal()); +#warning This gets overridden by config after NYU demo + if(TriList is CrestronApp) + TriList.BooleanInput[UIBoolJoin.GearButtonVisible].BoolValue = false; + else + TriList.BooleanInput[UIBoolJoin.GearButtonVisible].BoolValue = true; + + // power-related functions + // Note: some of these are not directly-related to the huddle space UI, but are held over + // in case + TriList.SetSigFalseAction(UIBoolJoin.ShowPowerOffPress, PowerButtonPressed); + TriList.SetSigFalseAction(UIBoolJoin.PowerOffMorePress, () => + { + CancelPowerOffTimer(); + TriList.BooleanInput[UIBoolJoin.PowerOffStep1Visible].BoolValue = false; + TriList.BooleanInput[UIBoolJoin.PowerOffStep2Visible].BoolValue = true; + }); + TriList.SetSigFalseAction(UIBoolJoin.DisplayPowerTogglePress, () => + { + if (CurrentRoom != null && CurrentRoom.DefaultDisplay is IPower) + (CurrentRoom.DefaultDisplay as IPower).PowerToggle(); + }); + + base.Show(); + } + + /// + /// + /// + public override void Hide() + { + HideAndClearCurrentDisplayModeSigsInUse(); + TriList.BooleanInput[UIBoolJoin.TopBarHabaneroVisible].BoolValue = false; + TriList.BooleanInput[UIBoolJoin.ActivityFooterVisible].BoolValue = false; + TriList.BooleanInput[UIBoolJoin.StartPageVisible].BoolValue = false; + TriList.BooleanInput[UIBoolJoin.TapToBeginVisible].BoolValue = false; + TriList.BooleanInput[UIBoolJoin.SelectASourceVisible].BoolValue = false; + //TriList.BooleanInput[UIBoolJoin.StagingPageVisible].BoolValue = false; + VolumeButtonsPopupFeedback.ClearNow(); + //CancelPowerOff(); + + base.Hide(); + } + + /// + /// Shows the various "modes" that this driver controls. Presentation, Setup page + /// + /// + public void ShowMode(UiDisplayMode mode) + { + //Clear whatever is showing now. + HideAndClearCurrentDisplayModeSigsInUse(); + CurrentDisplayMode = mode; + switch (mode) + { + case UiDisplayMode.PresentationMode: + // show start page or staging... + if (CurrentRoom.OnFeedback.BoolValue) + { + TriList.BooleanInput[UIBoolJoin.SourceStagingBarVisible].BoolValue = true; + TriList.BooleanInput[UIBoolJoin.TapToBeginVisible].BoolValue = false; + TriList.BooleanInput[UIBoolJoin.SelectASourceVisible].BoolValue = false; + } + else + { + TriList.BooleanInput[UIBoolJoin.StartPageVisible].BoolValue = true; + TriList.BooleanInput[UIBoolJoin.TapToBeginVisible].BoolValue = true; + TriList.BooleanInput[UIBoolJoin.SelectASourceVisible].BoolValue = false; + } + // Date/time + if (Config.ShowDate && Config.ShowTime) + { + TriList.BooleanInput[UIBoolJoin.DateAndTimeVisible].BoolValue = true; + TriList.BooleanInput[UIBoolJoin.DateOnlyVisible].BoolValue = false; + TriList.BooleanInput[UIBoolJoin.TimeOnlyVisible].BoolValue = false; + } + else + { + TriList.BooleanInput[UIBoolJoin.DateAndTimeVisible].BoolValue = false; + TriList.BooleanInput[UIBoolJoin.DateOnlyVisible].BoolValue = Config.ShowDate; + TriList.BooleanInput[UIBoolJoin.TimeOnlyVisible].BoolValue = Config.ShowTime; + } + + ShowCurrentDisplayModeSigsInUse(); + break; + } + } + + /// + /// When the room is off, set the footer SRL + /// + void SetupActivityFooterWhenRoomOff() + { + ActivityFooterSrl.Clear(); + ActivityFooterSrl.AddItem(new SubpageReferenceListActivityItem(1, ActivityFooterSrl, 0, + b => { if (!b) ShareButtonPressed(); })); + ActivityFooterSrl.Count = 1; + TriList.UShortInput[UIUshortJoin.PresentationListCaretMode].UShortValue = 0; + ShareButtonSig.BoolValue = false; + } + + /// + /// Sets up the footer SRL for when the room is on + /// + void SetupActivityFooterWhenRoomOn() + { + ActivityFooterSrl.Clear(); + ActivityFooterSrl.AddItem(new SubpageReferenceListActivityItem(1, ActivityFooterSrl, + 0, null)); + ActivityFooterSrl.AddItem(new SubpageReferenceListActivityItem(2, ActivityFooterSrl, + 3, b => { if (!b) PowerButtonPressed(); })); + ActivityFooterSrl.Count = 2; + TriList.UShortInput[UIUshortJoin.PresentationListCaretMode].UShortValue = 1; + EndMeetingButtonSig = ActivityFooterSrl.BoolInputSig(2, 1); + ShareButtonSig.BoolValue = CurrentRoom.OnFeedback.BoolValue; + } + + /// + /// Attached to activity list share button + /// + void ShareButtonPressed() + { + ShareButtonSig.BoolValue = true; + TriList.BooleanInput[UIBoolJoin.StartPageVisible].BoolValue = false; + TriList.BooleanInput[UIBoolJoin.SourceStagingBarVisible].BoolValue = true; + TriList.BooleanInput[UIBoolJoin.SelectASourceVisible].BoolValue = true; + // Run default source when room is off and share is pressed + if (!CurrentRoom.OnFeedback.BoolValue) + CurrentRoom.RunDefaultRoute(); + } + + /// + /// Hides a current modal if showing and then shows a new. + /// + /// + //void ShowInterlockedModal(uint join) + //{ + // if (CurrentInterlockedModalJoin == join) + // HideCurrentInterlockedModal(); + // else + // { + // // sets the sig true if the join is right + // TriList.BooleanInput[UIBoolJoin.HelpPageVisible].BoolValue = join == UIBoolJoin.HelpPageVisible; + // TriList.BooleanInput[UIBoolJoin.RoomHeaderPageVisible].BoolValue = join == UIBoolJoin.RoomHeaderPageVisible; + // TriList.BooleanInput[UIBoolJoin.VolumesPageVisible].BoolValue = join == UIBoolJoin.VolumesPageVisible; + // TriList.BooleanInput[UIBoolJoin.TechPanelSetupVisible].BoolValue = join == UIBoolJoin.TechPanelSetupVisible; + // CurrentInterlockedModalJoin = join; + // } + //} + + ///// + ///// Helper to hide the current interlocked modal + ///// + //void HideCurrentInterlockedModal() + //{ + // TriList.BooleanInput[CurrentInterlockedModalJoin].BoolValue = false; + // CurrentInterlockedModalJoin = 0; + //} + + + /// + /// Shows all sigs that are in CurrentDisplayModeSigsInUse + /// + void ShowCurrentDisplayModeSigsInUse() + { + foreach (var sig in CurrentDisplayModeSigsInUse) + sig.BoolValue = true; + } + + /// + /// Hides all CurrentDisplayModeSigsInUse sigs and clears the array + /// + void HideAndClearCurrentDisplayModeSigsInUse() + { + foreach (var sig in CurrentDisplayModeSigsInUse) + sig.BoolValue = false; + CurrentDisplayModeSigsInUse.Clear(); + } + + /// + /// Send the UI back depending on location, not used in huddle UI + /// + public override void BackButtonPressed() + { + switch (CurrentDisplayMode) + { + case UiDisplayMode.PresentationMode: + //CancelReturnToSourceTimer(); + BackToHome(); + break; + } + } + + /// + /// + /// + void BackToHome() + { + Hide(); + Parent.Show(); + } + + /// + /// Loads the appropriate Sigs into CurrentDisplayModeSigsInUse and shows them + /// + void ShowCurrentSource() + { + if (CurrentRoom.CurrentSourceInfo == null) + return; + + var uiDev = CurrentRoom.CurrentSourceInfo.SourceDevice as IUiDisplayInfo; + PageManager pm = null; + // If we need a page manager, get an appropriate one + if (uiDev != null) + { + TriList.BooleanInput[UIBoolJoin.SelectASourceVisible].BoolValue = false; + // Got an existing page manager, get it + if (PageManagers.ContainsKey(uiDev)) + pm = PageManagers[uiDev]; + // Otherwise make an apporiate one + else if (uiDev is ISetTopBoxControls) + //pm = new SetTopBoxMediumPageManager(uiDev as ISetTopBoxControls, TriList); + pm = new SetTopBoxThreePanelPageManager(uiDev as ISetTopBoxControls, TriList); + else if (uiDev is IDiscPlayerControls) + pm = new DiscPlayerMediumPageManager(uiDev as IDiscPlayerControls, TriList); + else + pm = new DefaultPageManager(uiDev, TriList); + PageManagers[uiDev] = pm; + CurrentSourcePageManager = pm; + pm.Show(); + } + } + + /// + /// Called from button presses on source, where We can assume we want + /// to change to the proper screen. + /// + /// The key name of the route to run + void UiSelectSource(string key) + { + // Run the route and when it calls back, show the source + CurrentRoom.RunRouteAction(key, null); + } + + /// + /// + /// + public void PowerButtonPressed() + { + if (!CurrentRoom.OnFeedback.BoolValue + || CurrentRoom.ShutdownPromptTimer.IsRunningFeedback.BoolValue) + return; + + CurrentRoom.StartShutdown(ShutdownType.Manual); + } + + /// + /// + /// + /// + /// + void ShutdownPromptTimer_HasStarted(object sender, EventArgs e) + { + // Do we need to check where the UI is? No? + var timer = CurrentRoom.ShutdownPromptTimer; + EndMeetingButtonSig.BoolValue = true; + ShareButtonSig.BoolValue = false; + + if (CurrentRoom.ShutdownType == ShutdownType.Manual) + { + PowerDownModal = new ModalDialog(TriList); + var message = string.Format("Meeting will end in {0} seconds", CurrentRoom.ShutdownPromptSeconds); + + //// figure out a cleaner way to update gauge + //var gauge = CurrentRoom.ShutdownPromptTimer.PercentFeedback; + //EventHandler gaugeHandler = null; + //gaugeHandler = (o, a) => TriList.UShortInput[ModalDialog.TimerGaugeJoin].UShortValue = + // (ushort)(gauge.UShortValue * 65535 / 100); + //gauge.OutputChange += gaugeHandler; + + // Attach timer things to modal + CurrentRoom.ShutdownPromptTimer.TimeRemainingFeedback.OutputChange += ShutdownPromptTimer_TimeRemainingFeedback_OutputChange; + CurrentRoom.ShutdownPromptTimer.PercentFeedback.OutputChange += ShutdownPromptTimer_PercentFeedback_OutputChange; + + // respond to offs by cancelling dialog + var onFb = CurrentRoom.OnFeedback; + EventHandler offHandler = null; + offHandler = (o, a) => + { + if (!onFb.BoolValue) + { + EndMeetingButtonSig.BoolValue = false; + PowerDownModal.HideDialog(); + onFb.OutputChange -= offHandler; + //gauge.OutputChange -= gaugeHandler; + } + }; + onFb.OutputChange += offHandler; + + PowerDownModal.PresentModalDialog(2, "End Meeting", "Power", message, "Cancel", "End Meeting Now", true, true, + but => + { + if (but != 2) // any button except for End cancels + timer.Cancel(); + else + timer.Finish(); + }); + } + } + + /// + /// + /// + /// + /// + void ShutdownPromptTimer_HasFinished(object sender, EventArgs e) + { + //Debug.Console(2, "*#*UI shutdown prompt finished"); + EndMeetingButtonSig.BoolValue = false; + CurrentRoom.ShutdownPromptTimer.TimeRemainingFeedback.OutputChange -= ShutdownPromptTimer_TimeRemainingFeedback_OutputChange; + CurrentRoom.ShutdownPromptTimer.PercentFeedback.OutputChange -= ShutdownPromptTimer_PercentFeedback_OutputChange; + + } + + /// + /// + /// + /// + /// + void ShutdownPromptTimer_WasCancelled(object sender, EventArgs e) + { + //Debug.Console(2, "*#*UI shutdown prompt cancelled"); + if (PowerDownModal != null) + PowerDownModal.HideDialog(); + EndMeetingButtonSig.BoolValue = false; + ShareButtonSig.BoolValue = CurrentRoom.OnFeedback.BoolValue; + + CurrentRoom.ShutdownPromptTimer.TimeRemainingFeedback.OutputChange += ShutdownPromptTimer_TimeRemainingFeedback_OutputChange; + CurrentRoom.ShutdownPromptTimer.PercentFeedback.OutputChange -= ShutdownPromptTimer_PercentFeedback_OutputChange; + } + + void ShutdownPromptTimer_TimeRemainingFeedback_OutputChange(object sender, EventArgs e) + { + + var message = string.Format("Meeting will end in {0} seconds", (sender as StringFeedback).StringValue); + TriList.StringInput[ModalDialog.MessageTextJoin].StringValue = message; + } + + void ShutdownPromptTimer_PercentFeedback_OutputChange(object sender, EventArgs e) + { + var value = (ushort)((sender as IntFeedback).UShortValue * 65535 / 100); + TriList.UShortInput[ModalDialog.TimerGaugeJoin].UShortValue = value; + } + + /// + /// + /// + void CancelPowerOffTimer() + { + if (PowerOffTimer != null) + { + PowerOffTimer.Stop(); + PowerOffTimer = null; + } + } + + /// + /// + /// + void VolumeButtonsTogglePress() + { + if (VolumeButtonsPopupFeedback.BoolValue) + VolumeButtonsPopupFeedback.ClearNow(); + else + { + // Trigger the popup + VolumeButtonsPopupFeedback.BoolValue = true; + VolumeButtonsPopupFeedback.BoolValue = false; + } + } + + /// + /// + /// + /// + public void VolumeUpPress(bool state) + { + // extend timeouts + if (ShowVolumeGauge) + VolumeGaugeFeedback.BoolValue = state; + VolumeButtonsPopupFeedback.BoolValue = state; + if (CurrentRoom.CurrentVolumeControls != null) + CurrentRoom.CurrentVolumeControls.VolumeUp(state); + } + + /// + /// + /// + /// + public void VolumeDownPress(bool state) + { + // extend timeouts + if (ShowVolumeGauge) + VolumeGaugeFeedback.BoolValue = state; + VolumeButtonsPopupFeedback.BoolValue = state; + if (CurrentRoom.CurrentVolumeControls != null) + CurrentRoom.CurrentVolumeControls.VolumeDown(state); + } + + /// + /// Helper for property setter. Sets the panel to the given room, latching up all functionality + /// + void SetCurrentRoom(EssentialsHuddleSpaceRoom room) + { + if (_CurrentRoom == room) return; + // Disconnect current (probably never called) + if (_CurrentRoom != null) + { + // Disconnect current room + _CurrentRoom.CurrentVolumeDeviceChange -= this.CurrentRoom_CurrentAudioDeviceChange; + ClearAudioDeviceConnections(); + _CurrentRoom.CurrentSingleSourceChange -= this.CurrentRoom_SourceInfoChange; + DisconnectSource(_CurrentRoom.CurrentSourceInfo); + _CurrentRoom.ShutdownPromptTimer.HasStarted -= ShutdownPromptTimer_HasStarted; + _CurrentRoom.ShutdownPromptTimer.HasFinished -= ShutdownPromptTimer_HasFinished; + _CurrentRoom.ShutdownPromptTimer.WasCancelled -= ShutdownPromptTimer_WasCancelled; + + _CurrentRoom.OnFeedback.OutputChange += CurrentRoom_OnFeedback_OutputChange; + _CurrentRoom.IsWarmingUpFeedback.OutputChange -= CurrentRoom_IsWarmingFeedback_OutputChange; + _CurrentRoom.IsCoolingDownFeedback.OutputChange -= IsCoolingDownFeedback_OutputChange; + } + + _CurrentRoom = room; + + if (_CurrentRoom != null) + { + // get the source list config and set up the source list + var config = ConfigReader.ConfigObject.SourceLists; + if (config.ContainsKey(_CurrentRoom.SourceListKey)) + { + var srcList = config[_CurrentRoom.SourceListKey]; + // Setup sources list + uint i = 1; // counter for UI list + foreach (var kvp in srcList) + { + var srcConfig = kvp.Value; + if (!srcConfig.IncludeInSourceList) // Skip sources marked this way + continue; + + var actualSource = DeviceManager.GetDeviceForKey(srcConfig.SourceKey) as Device; + if (actualSource == null) + { + Debug.Console(1, "Cannot assign missing source '{0}' to source UI list", + srcConfig.SourceKey); + continue; + } + var routeKey = kvp.Key; + var item = new SubpageReferenceListSourceItem(i++, SourcesSrl, srcConfig, + b => { if (!b) UiSelectSource(routeKey); }); + SourcesSrl.AddItem(item); // add to the SRL + item.RegisterForSourceChange(_CurrentRoom); + } + SourcesSrl.Count = (ushort)(i - 1); + } + // Name and logo + TriList.StringInput[UIStringJoin.CurrentRoomName].StringValue = _CurrentRoom.Name; + if (_CurrentRoom.LogoUrl == null) + { + TriList.BooleanInput[UIBoolJoin.LogoDefaultVisible].BoolValue = true; + TriList.BooleanInput[UIBoolJoin.LogoUrlVisible].BoolValue = false; + } + else + { + TriList.BooleanInput[UIBoolJoin.LogoDefaultVisible].BoolValue = false; + TriList.BooleanInput[UIBoolJoin.LogoUrlVisible].BoolValue = true; + TriList.StringInput[UIStringJoin.LogoUrl].StringValue = _CurrentRoom.LogoUrl; + } + + // Shutdown timer + _CurrentRoom.ShutdownPromptTimer.HasStarted += ShutdownPromptTimer_HasStarted; + _CurrentRoom.ShutdownPromptTimer.HasFinished += ShutdownPromptTimer_HasFinished; + _CurrentRoom.ShutdownPromptTimer.WasCancelled += ShutdownPromptTimer_WasCancelled; + + // Link up all the change events from the room + _CurrentRoom.OnFeedback.OutputChange += CurrentRoom_OnFeedback_OutputChange; + CurrentRoom_SyncOnFeedback(); + _CurrentRoom.IsWarmingUpFeedback.OutputChange += CurrentRoom_IsWarmingFeedback_OutputChange; + _CurrentRoom.IsCoolingDownFeedback.OutputChange += IsCoolingDownFeedback_OutputChange; + + _CurrentRoom.CurrentVolumeDeviceChange += CurrentRoom_CurrentAudioDeviceChange; + RefreshAudioDeviceConnections(); + _CurrentRoom.CurrentSingleSourceChange += CurrentRoom_SourceInfoChange; + RefreshSourceInfo(); + } + else + { + // Clear sigs that need to be + TriList.StringInput[UIStringJoin.CurrentRoomName].StringValue = "Select a room"; + } + } + + /// + /// For room on/off changes + /// + void CurrentRoom_OnFeedback_OutputChange(object sender, EventArgs e) + { + CurrentRoom_SyncOnFeedback(); + } + + void CurrentRoom_SyncOnFeedback() + { + var value = _CurrentRoom.OnFeedback.BoolValue; + //Debug.Console(2, CurrentRoom, "UI: Is on event={0}", value); + TriList.BooleanInput[UIBoolJoin.RoomIsOn].BoolValue = value; + + if (value) //ON + { + SetupActivityFooterWhenRoomOn(); + TriList.BooleanInput[UIBoolJoin.SelectASourceVisible].BoolValue = false; + TriList.BooleanInput[UIBoolJoin.SourceStagingBarVisible].BoolValue = true; + TriList.BooleanInput[UIBoolJoin.StartPageVisible].BoolValue = false; + TriList.BooleanInput[UIBoolJoin.VolumeSingleMute1Visible].BoolValue = true; + + } + else + { + SetupActivityFooterWhenRoomOff(); + TriList.BooleanInput[UIBoolJoin.StartPageVisible].BoolValue = true; + TriList.BooleanInput[UIBoolJoin.VolumeSingleMute1Visible].BoolValue = false; + TriList.BooleanInput[UIBoolJoin.SourceStagingBarVisible].BoolValue = false; + } + } + + /// + /// + /// + void CurrentRoom_IsWarmingFeedback_OutputChange(object sender, EventArgs e) + { + var value = CurrentRoom.IsWarmingUpFeedback.BoolValue; + //Debug.Console(2, CurrentRoom, "UI: WARMING event={0}", value); + + if (value) + { + WarmingCoolingModal = new ModalDialog(TriList); + WarmingCoolingModal.PresentModalDialog(0, "Powering Up", "Power", "

Room is powering up

Please wait

", + "", "", false, false, null); + } + else + { + if (WarmingCoolingModal != null) + WarmingCoolingModal.CancelDialog(); + } + } + + + void IsCoolingDownFeedback_OutputChange(object sender, EventArgs e) + { + var value = CurrentRoom.IsCoolingDownFeedback.BoolValue; + //Debug.Console(2, CurrentRoom, "UI: Cooldown event={0}", value); + + if (value) + { + WarmingCoolingModal = new ModalDialog(TriList); + WarmingCoolingModal.PresentModalDialog(0, "Shut Down", "Power", "

Room is shutting down

Please wait

", + "", "", false, false, null); + } + else + { + if (WarmingCoolingModal != null) + WarmingCoolingModal.CancelDialog(); + } + } + + /// + /// Hides source for provided source info + /// + /// + void DisconnectSource(SourceListItem previousInfo) + { + if (previousInfo == null) return; + + // Hide whatever is showing + if (IsVisible) + { + if (CurrentSourcePageManager != null) + { + CurrentSourcePageManager.Hide(); + CurrentSourcePageManager = null; + } + } + + if (previousInfo == null) return; + var previousDev = previousInfo.SourceDevice; + + // device type interfaces + if (previousDev is ISetTopBoxControls) + (previousDev as ISetTopBoxControls).UnlinkButtons(TriList); + // common interfaces + if (previousDev is IChannel) + (previousDev as IChannel).UnlinkButtons(TriList); + if (previousDev is IColor) + (previousDev as IColor).UnlinkButtons(TriList); + if (previousDev is IDPad) + (previousDev as IDPad).UnlinkButtons(TriList); + if (previousDev is IDvr) + (previousDev as IDvr).UnlinkButtons(TriList); + if (previousDev is INumericKeypad) + (previousDev as INumericKeypad).UnlinkButtons(TriList); + if (previousDev is IPower) + (previousDev as IPower).UnlinkButtons(TriList); + if (previousDev is ITransport) + (previousDev as ITransport).UnlinkButtons(TriList); + //if (previousDev is IRadio) + // (previousDev as IRadio).UnlinkButtons(this); + } + + /// + /// Refreshes and shows the room's current source + /// + void RefreshSourceInfo() + { + var routeInfo = CurrentRoom.CurrentSourceInfo; + // This will show off popup too + if (this.IsVisible) + ShowCurrentSource(); + + if (routeInfo == null)// || !CurrentRoom.OnFeedback.BoolValue) + { + // Check for power off and insert "Room is off" + TriList.StringInput[UIStringJoin.CurrentSourceName].StringValue = "Room is off"; + TriList.StringInput[UIStringJoin.CurrentSourceIcon].StringValue = "Power"; + this.Hide(); + Parent.Show(); + return; + } + else if (CurrentRoom.CurrentSourceInfo != null) + { + TriList.StringInput[UIStringJoin.CurrentSourceName].StringValue = routeInfo.PreferredName; + TriList.StringInput[UIStringJoin.CurrentSourceIcon].StringValue = routeInfo.Icon; // defaults to "blank" + } + else + { + TriList.StringInput[UIStringJoin.CurrentSourceName].StringValue = "---"; + TriList.StringInput[UIStringJoin.CurrentSourceIcon].StringValue = "Blank"; + } + + // Connect controls + if (routeInfo.SourceDevice != null) + ConnectControlDeviceMethods(routeInfo.SourceDevice); + } + + /// + /// Attach the source to the buttons and things + /// + void ConnectControlDeviceMethods(Device dev) + { + if(dev is ISetTopBoxControls) + (dev as ISetTopBoxControls).LinkButtons(TriList); + if (dev is IChannel) + (dev as IChannel).LinkButtons(TriList); + if (dev is IColor) + (dev as IColor).LinkButtons(TriList); + if (dev is IDPad) + (dev as IDPad).LinkButtons(TriList); + if (dev is IDvr) + (dev as IDvr).LinkButtons(TriList); + if (dev is INumericKeypad) + (dev as INumericKeypad).LinkButtons(TriList); + if (dev is IPower) + (dev as IPower).LinkButtons(TriList); + if (dev is ITransport) + (dev as ITransport).LinkButtons(TriList); + //if (dev is IRadio) + // (dev as IRadio).LinkButtons(this); // +++++++++++++ Make part of this into page manager + + //if (dev is ICustomFunctions) + //{ + // var custBridge = (dev as ICustomFunctions).GetCustomBridge(); + // custBridge.Link(this.Remote); + } + + /// + /// Detaches the buttons and feedback from the room's current audio device + /// + void ClearAudioDeviceConnections() + { + TriList.ClearBoolSigAction(UIBoolJoin.VolumeUpPress); + TriList.ClearBoolSigAction(UIBoolJoin.VolumeDownPress); + TriList.ClearBoolSigAction(UIBoolJoin.Volume1ProgramMutePressAndFB); + + var fDev = CurrentRoom.CurrentVolumeControls as IBasicVolumeWithFeedback; + if (fDev != null) + { + TriList.ClearUShortSigAction(UIUshortJoin.VolumeSlider1Value); + fDev.VolumeLevelFeedback.UnlinkInputSig( + TriList.UShortInput[UIUshortJoin.VolumeSlider1Value]); + } + } + + /// + /// Attaches the buttons and feedback to the room's current audio device + /// + void RefreshAudioDeviceConnections() + { + var dev = CurrentRoom.CurrentVolumeControls; + if (dev != null) // connect buttons + { + TriList.SetBoolSigAction(UIBoolJoin.VolumeUpPress, VolumeUpPress); + TriList.SetBoolSigAction(UIBoolJoin.VolumeDownPress, VolumeDownPress); + TriList.SetSigFalseAction(UIBoolJoin.Volume1ProgramMutePressAndFB, dev.MuteToggle); + } + + var fbDev = dev as IBasicVolumeWithFeedback; + if (fbDev == null) // this should catch both IBasicVolume and IBasicVolumeWithFeeback + TriList.UShortInput[UIUshortJoin.VolumeSlider1Value].UShortValue = 0; + else + { + // slider + TriList.SetUShortSigAction(UIUshortJoin.VolumeSlider1Value, fbDev.SetVolume); + // feedbacks + fbDev.MuteFeedback.LinkInputSig(TriList.BooleanInput[UIBoolJoin.Volume1ProgramMutePressAndFB]); + fbDev.VolumeLevelFeedback.LinkInputSig( + TriList.UShortInput[UIUshortJoin.VolumeSlider1Value]); + } + } + + /// + /// Handler for when the room's volume control device changes + /// + void CurrentRoom_CurrentAudioDeviceChange(object sender, VolumeDeviceChangeEventArgs args) + { + if (args.Type == ChangeType.WillChange) + ClearAudioDeviceConnections(); + else // did change + RefreshAudioDeviceConnections(); + } + + /// + /// Handles source change + /// + void CurrentRoom_SourceInfoChange(EssentialsRoomBase room, + SourceListItem info, ChangeType change) + { + if (change == ChangeType.WillChange) + DisconnectSource(info); + else + RefreshSourceInfo(); + } + } } \ No newline at end of file diff --git a/Essentials/PepperDashEssentials/UI Drivers/EssentialsPanelMainInterfaceDriver.cs b/Essentials/PepperDashEssentials/UIDrivers/Essentials/EssentialsPanelMainInterfaceDriver.cs similarity index 100% rename from Essentials/PepperDashEssentials/UI Drivers/EssentialsPanelMainInterfaceDriver.cs rename to Essentials/PepperDashEssentials/UIDrivers/Essentials/EssentialsPanelMainInterfaceDriver.cs diff --git a/Essentials/PepperDashEssentials/UI Drivers/EssentialsPresentationPanelAvFunctionsDriver.cs b/Essentials/PepperDashEssentials/UIDrivers/Essentials/EssentialsPresentationPanelAvFunctionsDriver.cs similarity index 99% rename from Essentials/PepperDashEssentials/UI Drivers/EssentialsPresentationPanelAvFunctionsDriver.cs rename to Essentials/PepperDashEssentials/UIDrivers/Essentials/EssentialsPresentationPanelAvFunctionsDriver.cs index 3d422804..c817ce5a 100644 --- a/Essentials/PepperDashEssentials/UI Drivers/EssentialsPresentationPanelAvFunctionsDriver.cs +++ b/Essentials/PepperDashEssentials/UIDrivers/Essentials/EssentialsPresentationPanelAvFunctionsDriver.cs @@ -205,7 +205,7 @@ namespace PepperDash.Essentials ///
public override void Show() { - TriList.BooleanInput[UIBoolJoin.TopBarVisible].BoolValue = true; + TriList.BooleanInput[UIBoolJoin.TopBarHabaneroVisible].BoolValue = true; TriList.BooleanInput[UIBoolJoin.ActivityFooterVisible].BoolValue = true; // Default to showing rooms/sources now. @@ -266,12 +266,12 @@ namespace PepperDash.Essentials { var tl = TriList.BooleanInput; HideAndClearCurrentDisplayModeSigsInUse(); - tl[UIBoolJoin.TopBarVisible].BoolValue = false; + tl[UIBoolJoin.TopBarHabaneroVisible].BoolValue = false; tl[UIBoolJoin.ActivityFooterVisible].BoolValue = false; tl[UIBoolJoin.StartPageVisible].BoolValue = false; tl[UIBoolJoin.TapToBeginVisible].BoolValue = false; tl[UIBoolJoin.ToggleSharingModeVisible].BoolValue = false; - tl[UIBoolJoin.StagingPageVisible].BoolValue = false; + tl[UIBoolJoin.SourceStagingBarVisible].BoolValue = false; if (IsSharingModeAdvanced) tl[UIBoolJoin.DualDisplayPageVisible].BoolValue = false; else @@ -290,7 +290,7 @@ namespace PepperDash.Essentials { var tlb = TriList.BooleanInput; tlb[UIBoolJoin.ToggleSharingModeVisible].BoolValue = true; - tlb[UIBoolJoin.StagingPageVisible].BoolValue = true; + tlb[UIBoolJoin.SourceStagingBarVisible].BoolValue = true; if (IsSharingModeAdvanced) { tlb[UIBoolJoin.DualDisplayPageVisible].BoolValue = true; @@ -310,7 +310,7 @@ namespace PepperDash.Essentials { var tl = TriList.BooleanInput; tl[UIBoolJoin.ToggleSharingModeVisible].BoolValue = false; - tl[UIBoolJoin.StagingPageVisible].BoolValue = false; + tl[UIBoolJoin.SourceStagingBarVisible].BoolValue = false; tl[UIBoolJoin.DualDisplayPageVisible].BoolValue = false; tl[UIBoolJoin.SelectASourceVisible].BoolValue = false; } diff --git a/Essentials/PepperDashEssentials/UIDrivers/EssentialsHuddleVTC/HuddleVTCPanelAvFunctionsDriver.cs b/Essentials/PepperDashEssentials/UIDrivers/EssentialsHuddleVTC/HuddleVTCPanelAvFunctionsDriver.cs new file mode 100644 index 00000000..cb05d1b5 --- /dev/null +++ b/Essentials/PepperDashEssentials/UIDrivers/EssentialsHuddleVTC/HuddleVTCPanelAvFunctionsDriver.cs @@ -0,0 +1,940 @@ +using System; +using System.Collections.Generic; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; +using Crestron.SimplSharpPro.UI; + +using PepperDash.Core; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.SmartObjects; +using PepperDash.Essentials.Core.PageManagers; +using PepperDash.Essentials.Room.Config; + +namespace PepperDash.Essentials +{ + /// + /// + /// + public class EssentialsHuddleVtc1PanelAvFunctionsDriver : PanelDriverBase + { + CrestronTouchpanelPropertiesConfig Config; + + public enum UiDisplayMode + { + Presentation, AudioSetup, Call, Start + } + + /// + /// Whether volume ramping from this panel will show the volume + /// gauge popup. + /// + public bool ShowVolumeGauge { get; set; } + + /// + /// + /// + public uint PowerOffTimeout { get; set; } + + /// + /// + /// + public string DefaultRoomKey { get; set; } + + /// + /// + /// + public EssentialsHuddleVtc1Room CurrentRoom + { + get { return _CurrentRoom; } + set + { + SetCurrentRoom(value); + } + } + EssentialsHuddleVtc1Room _CurrentRoom; + + /// + /// For hitting feedback + /// + BoolInputSig CallButtonSig; + BoolInputSig ShareButtonSig; + BoolInputSig EndMeetingButtonSig; + + /// + /// The parent driver for this + /// + PanelDriverBase Parent; + + /// + /// All children attached to this driver. For hiding and showing as a group. + /// + List ChildDrivers = new List(); + + List CurrentDisplayModeSigsInUse = new List(); + + //// Important smart objects + + /// + /// Smart Object 3200 + /// + SubpageReferenceList SourceStagingSrl; + + /// + /// Smart Object 15022 + /// + SubpageReferenceList ActivityFooterSrl; + + /// + /// Tracks which audio page group the UI is in + /// + UiDisplayMode CurrentDisplayMode; + + /// + /// The AV page mangagers that have been used, to keep them alive for later + /// + Dictionary PageManagers = new Dictionary(); + + /// + /// Current page manager running for a source + /// + PageManager CurrentSourcePageManager; + + /// + /// Will auto-timeout a power off + /// + CTimer PowerOffTimer; + + /// + /// + /// + ModalDialog PowerDownModal; + + /// + /// + /// + ModalDialog WarmingCoolingModal; + + /// + /// Represents + /// + JoinedSigInterlock PopupInterlock; + + /// + /// Interlock for various source, camera, call control bars. The bar above the activity footer. This is also + /// used to show start page + /// + JoinedSigInterlock StagingBarInterlock; + + JoinedSigInterlock CallPagesInterlock; + + PepperDash.Essentials.UIDrivers.VC.EssentialsCiscoSparkUiDriver VCDriver; + + /// + /// Constructor + /// + public EssentialsHuddleVtc1PanelAvFunctionsDriver(PanelDriverBase parent, CrestronTouchpanelPropertiesConfig config) + : base(parent.TriList) + { + Config = config; + Parent = parent; + + VCDriver = new PepperDash.Essentials.UIDrivers.VC.EssentialsCiscoSparkUiDriver(Parent.TriList, null); + + PopupInterlock = new JoinedSigInterlock(TriList); + StagingBarInterlock = new JoinedSigInterlock(TriList); + CallPagesInterlock = new JoinedSigInterlock(TriList); + + SourceStagingSrl = new SubpageReferenceList(TriList, UISmartObjectJoin.SourceStagingSRL, 3, 3, 3); + + ActivityFooterSrl = new SubpageReferenceList(TriList, UISmartObjectJoin.ActivityFooterSRL, 3, 3, 3); + CallButtonSig = ActivityFooterSrl.BoolInputSig(1, 1); + ShareButtonSig = ActivityFooterSrl.BoolInputSig(2, 1); + + SetupActivityFooterWhenRoomOff(); + + ShowVolumeGauge = true; + //PowerOffTimeout = 30000; + + //TriList.StringInput[UIStringJoin.StartActivityText].StringValue = "Tap an activity below"; + } + + /// + /// + /// + public override void Show() + { + if (CurrentRoom == null) + { + Debug.Console(1, "ERROR: AVUIFunctionsDriver, Cannot show. No room assigned"); + return; + } + + var roomConf = CurrentRoom.Config; + + if (Config.HeaderStyle == UiHeaderStyle.Habanero) + { + TriList.SetString(UIStringJoin.CurrentRoomName, CurrentRoom.Name); + TriList.SetSigFalseAction(UIBoolJoin.RoomHeaderButtonPress, () => + PopupInterlock.ShowInterlockedWithToggle(UIBoolJoin.RoomHeaderPageVisible)); + } + else if (Config.HeaderStyle == UiHeaderStyle.Verbose) + { + // room name on join 1, concat phone and sip on join 2, no button method + TriList.SetString(UIStringJoin.CurrentRoomName, CurrentRoom.Name); + var addr = roomConf.Addresses; + if (addr == null) // protect from missing values by using default empties + addr = new EssentialsRoomAddressPropertiesConfig(); + // empty string when either missing, pipe when both showing + TriList.SetString(UIStringJoin.RoomAddressPipeText, + (string.IsNullOrEmpty(addr.PhoneNumber.Trim()) + || string.IsNullOrEmpty(addr.SipAddress.Trim())) ? "" : " | "); + TriList.SetString(UIStringJoin.RoomPhoneText, addr.PhoneNumber); + TriList.SetString(UIStringJoin.RoomSipText, addr.SipAddress); + } + + TriList.SetBool(UIBoolJoin.DateAndTimeVisible, Config.ShowDate && Config.ShowTime); + TriList.SetBool(UIBoolJoin.DateOnlyVisible, Config.ShowDate && !Config.ShowTime); + TriList.SetBool(UIBoolJoin.TimeOnlyVisible, !Config.ShowDate && Config.ShowTime); + TriList.SetBool(UIBoolJoin.TopBarHabaneroVisible, true); + TriList.SetBool(UIBoolJoin.ActivityFooterVisible, true); + + // Default to showing rooms/sources now. + //ShowMode(UiDisplayMode.PresentationMode); + if (CurrentRoom.OnFeedback.BoolValue) + { + TriList.SetBool(UIBoolJoin.SourceStagingBarVisible, true); + TriList.SetBool(UIBoolJoin.TapToBeginVisible, false); + TriList.SetBool(UIBoolJoin.SelectASourceVisible, false); + } + else + { + TriList.SetBool(UIBoolJoin.StartPageVisible, true); + TriList.SetBool(UIBoolJoin.TapToBeginVisible, true); + TriList.SetBool(UIBoolJoin.SelectASourceVisible, false); + } + ShowCurrentDisplayModeSigsInUse(); + + // *** Header Buttons *** + + // Generic "close" button for these modals + TriList.SetSigFalseAction(UIBoolJoin.InterlockedModalClosePress, PopupInterlock.HideAndClear); + + // Help button and popup + if (CurrentRoom.Config.Help != null) + { + TriList.SetString(UIStringJoin.HelpMessage, roomConf.Help.Message); + TriList.SetBool(UIBoolJoin.HelpPageShowCallButtonVisible, roomConf.Help.ShowCallButton); + TriList.SetString(UIStringJoin.HelpPageCallButtonText, roomConf.Help.CallButtonText); + if(roomConf.Help.ShowCallButton) + TriList.SetSigFalseAction(UIBoolJoin.HelpPageShowCallButtonPress, () => { }); // ************ FILL IN + else + TriList.ClearBoolSigAction(UIBoolJoin.HelpPageShowCallButtonPress); + } + else // older config + { + TriList.SetString(UIStringJoin.HelpMessage, CurrentRoom.Config.HelpMessage); + TriList.SetBool(UIBoolJoin.HelpPageShowCallButtonVisible, false); + TriList.SetString(UIStringJoin.HelpPageCallButtonText, null); + TriList.ClearBoolSigAction(UIBoolJoin.HelpPageShowCallButtonPress); + } + TriList.SetSigFalseAction(UIBoolJoin.HelpPress, () => + { + string message = null; + var room = DeviceManager.GetDeviceForKey(Config.DefaultRoomKey) + as EssentialsHuddleSpaceRoom; + if (room != null) + message = room.Config.HelpMessage; + else + message = "Sorry, no help message available. No room connected."; + //TriList.StringInput[UIStringJoin.HelpMessage].StringValue = message; + PopupInterlock.ShowInterlockedWithToggle(UIBoolJoin.HelpPageVisible); + }); + + // Lights button + TriList.SetSigFalseAction(UIBoolJoin.LightsHeaderButtonPress, () => // ******************** FILL IN + { }); + + // Call header button + if(roomConf.OneButtonMeeting != null && roomConf.OneButtonMeeting.Enable) + { + TriList.SetBool(UIBoolJoin.CalendarHeaderButtonVisible, true); + TriList.SetSigFalseAction(UIBoolJoin.CallHeaderButtonPress, () => + { }); + } + + // Setup button - shows volumes with default button OR hold for tech page + TriList.SetSigHeldAction(UIBoolJoin.GearHeaderButtonPress, 2000, + () => PopupInterlock.ShowInterlockedWithToggle(UIBoolJoin.TechPanelSetupVisible), + () => PopupInterlock.ShowInterlockedWithToggle(UIBoolJoin.VolumesPageVisible)); + TriList.SetSigFalseAction(UIBoolJoin.TechPagesExitButton, () => + PopupInterlock.HideAndClear()); + + // Default Volume button + TriList.SetSigFalseAction(UIBoolJoin.VolumeDefaultPress, () => // Set default volume method on room + { }); + + + if (TriList is CrestronApp) + TriList.BooleanInput[UIBoolJoin.GearButtonVisible].BoolValue = false; + else + TriList.BooleanInput[UIBoolJoin.GearButtonVisible].BoolValue = true; + + // power-related functions + // Note: some of these are not directly-related to the huddle space UI, but are held over + // in case + TriList.SetSigFalseAction(UIBoolJoin.ShowPowerOffPress, PowerButtonPressed); + + TriList.SetSigFalseAction(UIBoolJoin.DisplayPowerTogglePress, () => + { + if (CurrentRoom != null && CurrentRoom.DefaultDisplay is IPower) + (CurrentRoom.DefaultDisplay as IPower).PowerToggle(); + }); + + base.Show(); + } + + /// + /// Puts the UI into the "start" mode. System is off. Logo shows. Activity SRL is clear + /// + void ShowStartMode() + { + SetupActivityFooterWhenRoomOff(); + + ShareButtonSig.BoolValue = false; + CallButtonSig.BoolValue = false; + ShowLogo(); + StagingBarInterlock.ShowInterlocked(UIBoolJoin.StartPageVisible); + StagingBarInterlock.HideAndClear(); + } + + void ShowShareMode() + { + ShareButtonSig.BoolValue = true; + CallButtonSig.BoolValue = false; + StagingBarInterlock.ShowInterlocked(UIBoolJoin.SourceStagingBarVisible); + } + + void ShowVideoCallMode() + { + ShareButtonSig.BoolValue = false; + CallButtonSig.BoolValue = true; + StagingBarInterlock.ShowInterlocked(UIBoolJoin.CallStagingBarVisible); + } + + /// + /// + /// + void ShowLogo() + { + if (CurrentRoom.LogoUrl == null) + { + TriList.SetBool(UIBoolJoin.LogoDefaultVisible, true); + TriList.SetBool(UIBoolJoin.LogoUrlVisible, false); + } + else + { + TriList.SetBool(UIBoolJoin.LogoDefaultVisible, false); + TriList.SetBool(UIBoolJoin.LogoUrlVisible, true); + TriList.SetString(UIStringJoin.LogoUrl, _CurrentRoom.LogoUrl); + } + } + + /// + /// + /// + public override void Hide() + { + HideAndClearCurrentDisplayModeSigsInUse(); + TriList.BooleanInput[UIBoolJoin.TopBarHabaneroVisible].BoolValue = false; + TriList.BooleanInput[UIBoolJoin.ActivityFooterVisible].BoolValue = false; + TriList.BooleanInput[UIBoolJoin.StartPageVisible].BoolValue = false; + TriList.BooleanInput[UIBoolJoin.TapToBeginVisible].BoolValue = false; + TriList.BooleanInput[UIBoolJoin.SelectASourceVisible].BoolValue = false; + base.Hide(); + } + + /// + /// When the room is off, set the footer SRL + /// + void SetupActivityFooterWhenRoomOff() + { + ActivityFooterSrl.Clear(); + ActivityFooterSrl.AddItem(new SubpageReferenceListActivityItem(1, ActivityFooterSrl, 1, + b => { if (!b) ActivityCallButtonPressed(); })); + ActivityFooterSrl.AddItem(new SubpageReferenceListActivityItem(2, ActivityFooterSrl, 0, + b => { if (!b) ActivityShareButtonPressed(); })); + ActivityFooterSrl.Count = 2; + TriList.UShortInput[UIUshortJoin.PresentationListCaretMode].UShortValue = 0; + ShareButtonSig.BoolValue = false; + } + + /// + /// Sets up the footer SRL for when the room is on + /// + void SetupActivityFooterWhenRoomOn() + { + ActivityFooterSrl.Clear(); + ActivityFooterSrl.AddItem(new SubpageReferenceListActivityItem(1, ActivityFooterSrl, 1, + b => { if (!b) ActivityCallButtonPressed(); })); + ActivityFooterSrl.AddItem(new SubpageReferenceListActivityItem(2, ActivityFooterSrl, 0, + b => { if (!b) ActivityShareButtonPressed(); })); + ActivityFooterSrl.AddItem(new SubpageReferenceListActivityItem(3, ActivityFooterSrl, + 3, b => { if (!b) PowerButtonPressed(); })); + ActivityFooterSrl.Count = 2; + TriList.UShortInput[UIUshortJoin.PresentationListCaretMode].UShortValue = 1; + EndMeetingButtonSig = ActivityFooterSrl.BoolInputSig(3, 1); + + ShareButtonSig.BoolValue = CurrentRoom.OnFeedback.BoolValue; + } + + /// + /// + /// + void ActivityCallButtonPressed() + { + if (VCDriver.IsVisible) + return; + CallButtonSig.BoolValue = true; + ShareButtonSig.BoolValue = false; + TriList.SetBool(UIBoolJoin.StartPageVisible, false); + TriList.SetBool(UIBoolJoin.SourceStagingBarVisible, false); + TriList.SetBool(UIBoolJoin.SelectASourceVisible, false); + VCDriver.Show(); + } + + /// + /// Attached to activity list share button + /// + void ActivityShareButtonPressed() + { + if (VCDriver.IsVisible) + VCDriver.Hide(); + ShareButtonSig.BoolValue = true; + CallButtonSig.BoolValue = false; + TriList.SetBool(UIBoolJoin.StartPageVisible, false); + TriList.SetBool(UIBoolJoin.SourceStagingBarVisible, true); + TriList.SetBool(UIBoolJoin.SelectASourceVisible, true); + // Run default source when room is off and share is pressed + if (!CurrentRoom.OnFeedback.BoolValue) + CurrentRoom.RunDefaultRoute(); + } + + /// + /// Shows all sigs that are in CurrentDisplayModeSigsInUse + /// + void ShowCurrentDisplayModeSigsInUse() + { + foreach (var sig in CurrentDisplayModeSigsInUse) + sig.BoolValue = true; + } + + /// + /// Hides all CurrentDisplayModeSigsInUse sigs and clears the array + /// + void HideAndClearCurrentDisplayModeSigsInUse() + { + foreach (var sig in CurrentDisplayModeSigsInUse) + sig.BoolValue = false; + CurrentDisplayModeSigsInUse.Clear(); + } + + + /// + /// Loads the appropriate Sigs into CurrentDisplayModeSigsInUse and shows them + /// + void ShowCurrentSource() + { + if (CurrentRoom.CurrentSourceInfo == null) + return; + + var uiDev = CurrentRoom.CurrentSourceInfo.SourceDevice as IUiDisplayInfo; + PageManager pm = null; + // If we need a page manager, get an appropriate one + if (uiDev != null) + { + TriList.BooleanInput[UIBoolJoin.SelectASourceVisible].BoolValue = false; + // Got an existing page manager, get it + if (PageManagers.ContainsKey(uiDev)) + pm = PageManagers[uiDev]; + // Otherwise make an apporiate one + else if (uiDev is ISetTopBoxControls) + pm = new SetTopBoxThreePanelPageManager(uiDev as ISetTopBoxControls, TriList); + else if (uiDev is IDiscPlayerControls) + pm = new DiscPlayerMediumPageManager(uiDev as IDiscPlayerControls, TriList); + else + pm = new DefaultPageManager(uiDev, TriList); + PageManagers[uiDev] = pm; + CurrentSourcePageManager = pm; + pm.Show(); + } + } + + /// + /// Called from button presses on source, where We can assume we want + /// to change to the proper screen. + /// + /// The key name of the route to run + void UiSelectSource(string key) + { + // Run the route and when it calls back, show the source + CurrentRoom.RunRouteAction(key, null); + } + + /// + /// + /// + public void PowerButtonPressed() + { + if (!CurrentRoom.OnFeedback.BoolValue + || CurrentRoom.ShutdownPromptTimer.IsRunningFeedback.BoolValue) + return; + + CurrentRoom.StartShutdown(ShutdownType.Manual); + } + + /// + /// + /// + /// + /// + void ShutdownPromptTimer_HasStarted(object sender, EventArgs e) + { + // Do we need to check where the UI is? No? + var timer = CurrentRoom.ShutdownPromptTimer; + EndMeetingButtonSig.BoolValue = true; + ShareButtonSig.BoolValue = false; + + if (CurrentRoom.ShutdownType == ShutdownType.Manual) + { + PowerDownModal = new ModalDialog(TriList); + var message = string.Format("Meeting will end in {0} seconds", CurrentRoom.ShutdownPromptSeconds); + + // Attach timer things to modal + CurrentRoom.ShutdownPromptTimer.TimeRemainingFeedback.OutputChange += ShutdownPromptTimer_TimeRemainingFeedback_OutputChange; + CurrentRoom.ShutdownPromptTimer.PercentFeedback.OutputChange += ShutdownPromptTimer_PercentFeedback_OutputChange; + + // respond to offs by cancelling dialog + var onFb = CurrentRoom.OnFeedback; + EventHandler offHandler = null; + offHandler = (o, a) => + { + if (!onFb.BoolValue) + { + EndMeetingButtonSig.BoolValue = false; + PowerDownModal.HideDialog(); + onFb.OutputChange -= offHandler; + } + }; + onFb.OutputChange += offHandler; + + PowerDownModal.PresentModalDialog(2, "End Meeting", "Power", message, "Cancel", "End Meeting Now", true, true, + but => + { + if (but != 2) // any button except for End cancels + timer.Cancel(); + else + timer.Finish(); + }); + } + } + + /// + /// + /// + /// + /// + void ShutdownPromptTimer_HasFinished(object sender, EventArgs e) + { + //Debug.Console(2, "*#*UI shutdown prompt finished"); + EndMeetingButtonSig.BoolValue = false; + CurrentRoom.ShutdownPromptTimer.TimeRemainingFeedback.OutputChange -= ShutdownPromptTimer_TimeRemainingFeedback_OutputChange; + CurrentRoom.ShutdownPromptTimer.PercentFeedback.OutputChange -= ShutdownPromptTimer_PercentFeedback_OutputChange; + + } + + /// + /// + /// + /// + /// + void ShutdownPromptTimer_WasCancelled(object sender, EventArgs e) + { + //Debug.Console(2, "*#*UI shutdown prompt cancelled"); + if (PowerDownModal != null) + PowerDownModal.HideDialog(); + EndMeetingButtonSig.BoolValue = false; + ShareButtonSig.BoolValue = CurrentRoom.OnFeedback.BoolValue; + + CurrentRoom.ShutdownPromptTimer.TimeRemainingFeedback.OutputChange += ShutdownPromptTimer_TimeRemainingFeedback_OutputChange; + CurrentRoom.ShutdownPromptTimer.PercentFeedback.OutputChange -= ShutdownPromptTimer_PercentFeedback_OutputChange; + } + + void ShutdownPromptTimer_TimeRemainingFeedback_OutputChange(object sender, EventArgs e) + { + + var message = string.Format("Meeting will end in {0} seconds", (sender as StringFeedback).StringValue); + TriList.StringInput[ModalDialog.MessageTextJoin].StringValue = message; + } + + void ShutdownPromptTimer_PercentFeedback_OutputChange(object sender, EventArgs e) + { + var value = (ushort)((sender as IntFeedback).UShortValue * 65535 / 100); + TriList.UShortInput[ModalDialog.TimerGaugeJoin].UShortValue = value; + } + + /// + /// + /// + void CancelPowerOffTimer() + { + if (PowerOffTimer != null) + { + PowerOffTimer.Stop(); + PowerOffTimer = null; + } + } + + /// + /// + /// + /// + public void VolumeUpPress(bool state) + { + if (CurrentRoom.CurrentVolumeControls != null) + CurrentRoom.CurrentVolumeControls.VolumeUp(state); + } + + /// + /// + /// + /// + public void VolumeDownPress(bool state) + { + if (CurrentRoom.CurrentVolumeControls != null) + CurrentRoom.CurrentVolumeControls.VolumeDown(state); + } + + /// + /// Helper for property setter. Sets the panel to the given room, latching up all functionality + /// + void SetCurrentRoom(EssentialsHuddleVtc1Room room) + { + if (_CurrentRoom == room) return; + // Disconnect current (probably never called) + if (_CurrentRoom != null) + { + // Disconnect current room + _CurrentRoom.CurrentVolumeDeviceChange -= this.CurrentRoom_CurrentAudioDeviceChange; + ClearAudioDeviceConnections(); + _CurrentRoom.CurrentSingleSourceChange -= this.CurrentRoom_SourceInfoChange; + DisconnectSource(_CurrentRoom.CurrentSourceInfo); + _CurrentRoom.ShutdownPromptTimer.HasStarted -= ShutdownPromptTimer_HasStarted; + _CurrentRoom.ShutdownPromptTimer.HasFinished -= ShutdownPromptTimer_HasFinished; + _CurrentRoom.ShutdownPromptTimer.WasCancelled -= ShutdownPromptTimer_WasCancelled; + + _CurrentRoom.OnFeedback.OutputChange += CurrentRoom_OnFeedback_OutputChange; + _CurrentRoom.IsWarmingUpFeedback.OutputChange -= CurrentRoom_IsWarmingFeedback_OutputChange; + _CurrentRoom.IsCoolingDownFeedback.OutputChange -= IsCoolingDownFeedback_OutputChange; + } + + _CurrentRoom = room; + + if (_CurrentRoom != null) + { + // get the source list config and set up the source list + var config = ConfigReader.ConfigObject.SourceLists; + if (config.ContainsKey(_CurrentRoom.SourceListKey)) + { + var srcList = config[_CurrentRoom.SourceListKey]; + // Setup sources list + uint i = 1; // counter for UI list + foreach (var kvp in srcList) + { + var srcConfig = kvp.Value; + if (!srcConfig.IncludeInSourceList) // Skip sources marked this way + continue; + + var actualSource = DeviceManager.GetDeviceForKey(srcConfig.SourceKey) as Device; + if (actualSource == null) + { + Debug.Console(1, "Cannot assign missing source '{0}' to source UI list", + srcConfig.SourceKey); + continue; + } + var routeKey = kvp.Key; + var item = new SubpageReferenceListSourceItem(i++, SourceStagingSrl, srcConfig, + b => { if (!b) UiSelectSource(routeKey); }); + SourceStagingSrl.AddItem(item); // add to the SRL + item.RegisterForSourceChange(_CurrentRoom); + } + SourceStagingSrl.Count = (ushort)(i - 1); + } + // Name and logo + TriList.StringInput[UIStringJoin.CurrentRoomName].StringValue = _CurrentRoom.Name; + ShowLogo(); + + // Shutdown timer + _CurrentRoom.ShutdownPromptTimer.HasStarted += ShutdownPromptTimer_HasStarted; + _CurrentRoom.ShutdownPromptTimer.HasFinished += ShutdownPromptTimer_HasFinished; + _CurrentRoom.ShutdownPromptTimer.WasCancelled += ShutdownPromptTimer_WasCancelled; + + // Link up all the change events from the room + _CurrentRoom.OnFeedback.OutputChange += CurrentRoom_OnFeedback_OutputChange; + CurrentRoom_SyncOnFeedback(); + _CurrentRoom.IsWarmingUpFeedback.OutputChange += CurrentRoom_IsWarmingFeedback_OutputChange; + _CurrentRoom.IsCoolingDownFeedback.OutputChange += IsCoolingDownFeedback_OutputChange; + + _CurrentRoom.CurrentVolumeDeviceChange += CurrentRoom_CurrentAudioDeviceChange; + RefreshAudioDeviceConnections(); + _CurrentRoom.CurrentSingleSourceChange += CurrentRoom_SourceInfoChange; + RefreshSourceInfo(); + } + else + { + // Clear sigs that need to be + TriList.StringInput[UIStringJoin.CurrentRoomName].StringValue = "Select a room"; + } + } + + /// + /// For room on/off changes + /// + void CurrentRoom_OnFeedback_OutputChange(object sender, EventArgs e) + { + CurrentRoom_SyncOnFeedback(); + } + + void CurrentRoom_SyncOnFeedback() + { + var value = _CurrentRoom.OnFeedback.BoolValue; + //Debug.Console(2, CurrentRoom, "UI: Is on event={0}", value); + TriList.BooleanInput[UIBoolJoin.RoomIsOn].BoolValue = value; + + if (value) //ON + { + SetupActivityFooterWhenRoomOn(); + TriList.BooleanInput[UIBoolJoin.SelectASourceVisible].BoolValue = false; + TriList.BooleanInput[UIBoolJoin.SourceStagingBarVisible].BoolValue = true; + TriList.BooleanInput[UIBoolJoin.StartPageVisible].BoolValue = false; + TriList.BooleanInput[UIBoolJoin.VolumeSingleMute1Visible].BoolValue = true; + + } + else + { + SetupActivityFooterWhenRoomOff(); + TriList.BooleanInput[UIBoolJoin.StartPageVisible].BoolValue = true; + TriList.BooleanInput[UIBoolJoin.VolumeSingleMute1Visible].BoolValue = false; + TriList.BooleanInput[UIBoolJoin.SourceStagingBarVisible].BoolValue = false; + } + } + + /// + /// + /// + void CurrentRoom_IsWarmingFeedback_OutputChange(object sender, EventArgs e) + { + var value = CurrentRoom.IsWarmingUpFeedback.BoolValue; + //Debug.Console(2, CurrentRoom, "UI: WARMING event={0}", value); + + if (value) + { + WarmingCoolingModal = new ModalDialog(TriList); + WarmingCoolingModal.PresentModalDialog(0, "Powering Up", "Power", "

Room is powering up

Please wait

", + "", "", false, false, null); + } + else + { + if (WarmingCoolingModal != null) + WarmingCoolingModal.CancelDialog(); + } + } + + + void IsCoolingDownFeedback_OutputChange(object sender, EventArgs e) + { + var value = CurrentRoom.IsCoolingDownFeedback.BoolValue; + //Debug.Console(2, CurrentRoom, "UI: Cooldown event={0}", value); + + if (value) + { + WarmingCoolingModal = new ModalDialog(TriList); + WarmingCoolingModal.PresentModalDialog(0, "Shut Down", "Power", "

Room is shutting down

Please wait

", + "", "", false, false, null); + } + else + { + if (WarmingCoolingModal != null) + WarmingCoolingModal.CancelDialog(); + } + } + + /// + /// Hides source for provided source info + /// + /// + void DisconnectSource(SourceListItem previousInfo) + { + if (previousInfo == null) return; + + // Hide whatever is showing + if (IsVisible) + { + if (CurrentSourcePageManager != null) + { + CurrentSourcePageManager.Hide(); + CurrentSourcePageManager = null; + } + } + + if (previousInfo == null) return; + var previousDev = previousInfo.SourceDevice; + + // device type interfaces + if (previousDev is ISetTopBoxControls) + (previousDev as ISetTopBoxControls).UnlinkButtons(TriList); + // common interfaces + if (previousDev is IChannel) + (previousDev as IChannel).UnlinkButtons(TriList); + if (previousDev is IColor) + (previousDev as IColor).UnlinkButtons(TriList); + if (previousDev is IDPad) + (previousDev as IDPad).UnlinkButtons(TriList); + if (previousDev is IDvr) + (previousDev as IDvr).UnlinkButtons(TriList); + if (previousDev is INumericKeypad) + (previousDev as INumericKeypad).UnlinkButtons(TriList); + if (previousDev is IPower) + (previousDev as IPower).UnlinkButtons(TriList); + if (previousDev is ITransport) + (previousDev as ITransport).UnlinkButtons(TriList); + //if (previousDev is IRadio) + // (previousDev as IRadio).UnlinkButtons(this); + } + + /// + /// Refreshes and shows the room's current source + /// + void RefreshSourceInfo() + { + var routeInfo = CurrentRoom.CurrentSourceInfo; + // This will show off popup too + if (this.IsVisible) + ShowCurrentSource(); + + if (routeInfo == null)// || !CurrentRoom.OnFeedback.BoolValue) + { + // Check for power off and insert "Room is off" + TriList.StringInput[UIStringJoin.CurrentSourceName].StringValue = "Room is off"; + TriList.StringInput[UIStringJoin.CurrentSourceIcon].StringValue = "Power"; + this.Hide(); + Parent.Show(); + return; + } + else if (CurrentRoom.CurrentSourceInfo != null) + { + TriList.StringInput[UIStringJoin.CurrentSourceName].StringValue = routeInfo.PreferredName; + TriList.StringInput[UIStringJoin.CurrentSourceIcon].StringValue = routeInfo.Icon; // defaults to "blank" + } + else + { + TriList.StringInput[UIStringJoin.CurrentSourceName].StringValue = "---"; + TriList.StringInput[UIStringJoin.CurrentSourceIcon].StringValue = "Blank"; + } + + // Connect controls + if (routeInfo.SourceDevice != null) + ConnectControlDeviceMethods(routeInfo.SourceDevice); + } + + /// + /// Attach the source to the buttons and things + /// + void ConnectControlDeviceMethods(Device dev) + { + if (dev is ISetTopBoxControls) + (dev as ISetTopBoxControls).LinkButtons(TriList); + if (dev is IChannel) + (dev as IChannel).LinkButtons(TriList); + if (dev is IColor) + (dev as IColor).LinkButtons(TriList); + if (dev is IDPad) + (dev as IDPad).LinkButtons(TriList); + if (dev is IDvr) + (dev as IDvr).LinkButtons(TriList); + if (dev is INumericKeypad) + (dev as INumericKeypad).LinkButtons(TriList); + if (dev is IPower) + (dev as IPower).LinkButtons(TriList); + if (dev is ITransport) + (dev as ITransport).LinkButtons(TriList); + } + + /// + /// Detaches the buttons and feedback from the room's current audio device + /// + void ClearAudioDeviceConnections() + { + TriList.ClearBoolSigAction(UIBoolJoin.VolumeUpPress); + TriList.ClearBoolSigAction(UIBoolJoin.VolumeDownPress); + TriList.ClearBoolSigAction(UIBoolJoin.Volume1ProgramMutePressAndFB); + + var fDev = CurrentRoom.CurrentVolumeControls as IBasicVolumeWithFeedback; + if (fDev != null) + { + TriList.ClearUShortSigAction(UIUshortJoin.VolumeSlider1Value); + fDev.VolumeLevelFeedback.UnlinkInputSig( + TriList.UShortInput[UIUshortJoin.VolumeSlider1Value]); + } + } + + /// + /// Attaches the buttons and feedback to the room's current audio device + /// + void RefreshAudioDeviceConnections() + { + var dev = CurrentRoom.CurrentVolumeControls; + if (dev != null) // connect buttons + { + TriList.SetBoolSigAction(UIBoolJoin.VolumeUpPress, VolumeUpPress); + TriList.SetBoolSigAction(UIBoolJoin.VolumeDownPress, VolumeDownPress); + TriList.SetSigFalseAction(UIBoolJoin.Volume1ProgramMutePressAndFB, dev.MuteToggle); + } + + var fbDev = dev as IBasicVolumeWithFeedback; + if (fbDev == null) // this should catch both IBasicVolume and IBasicVolumeWithFeeback + TriList.UShortInput[UIUshortJoin.VolumeSlider1Value].UShortValue = 0; + else + { + // slider + TriList.SetUShortSigAction(UIUshortJoin.VolumeSlider1Value, fbDev.SetVolume); + // feedbacks + fbDev.MuteFeedback.LinkInputSig(TriList.BooleanInput[UIBoolJoin.Volume1ProgramMutePressAndFB]); + fbDev.VolumeLevelFeedback.LinkInputSig( + TriList.UShortInput[UIUshortJoin.VolumeSlider1Value]); + } + } + + /// + /// Handler for when the room's volume control device changes + /// + void CurrentRoom_CurrentAudioDeviceChange(object sender, VolumeDeviceChangeEventArgs args) + { + if (args.Type == ChangeType.WillChange) + ClearAudioDeviceConnections(); + else // did change + RefreshAudioDeviceConnections(); + } + + /// + /// Handles source change + /// + void CurrentRoom_SourceInfoChange(EssentialsRoomBase room, + SourceListItem info, ChangeType change) + { + if (change == ChangeType.WillChange) + DisconnectSource(info); + else + RefreshSourceInfo(); + } + } +} diff --git a/Essentials/PepperDashEssentials/UIDrivers/JoinedSigInterlock.cs b/Essentials/PepperDashEssentials/UIDrivers/JoinedSigInterlock.cs new file mode 100644 index 00000000..c383c7d3 --- /dev/null +++ b/Essentials/PepperDashEssentials/UIDrivers/JoinedSigInterlock.cs @@ -0,0 +1,88 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro.DeviceSupport; + +namespace PepperDash.Essentials +{ + public class JoinedSigInterlock + { + public uint CurrentJoin { get; private set; } + + BasicTriList TriList; + + public JoinedSigInterlock(BasicTriList triList) + { + TriList = triList; + } + + /// + /// Hides CurrentJoin and shows join. Does nothing when resending CurrentJoin + /// + public void ShowInterlocked(uint join) + { + if (CurrentJoin == join) + return; + SetButDontShow(join); + TriList.BooleanInput[CurrentJoin].BoolValue = true; + } + + /// + /// + /// + /// + public void ShowInterlockedWithToggle(uint join) + { + if (CurrentJoin == join) + HideAndClear(); + else + { + if (CurrentJoin > 0) + TriList.BooleanInput[CurrentJoin].BoolValue = false; + CurrentJoin = join; + TriList.BooleanInput[CurrentJoin].BoolValue = true; + } + } + /// + /// Hides current join and clears CurrentJoin + /// + public void HideAndClear() + { + Hide(); + CurrentJoin = 0; + } + + /// + /// Hides the current join but does not clear the selected join in case + /// it needs to be reshown + /// + public void Hide() + { + if (CurrentJoin > 0) + TriList.BooleanInput[CurrentJoin].BoolValue = false; + } + + /// + /// If CurrentJoin is set, it restores that join + /// + public void Show() + { + if (CurrentJoin > 0) + TriList.BooleanInput[CurrentJoin].BoolValue = true; + } + + /// + /// Useful for pre-setting the interlock but not enabling it. + /// + /// + public void SetButDontShow(uint join) + { + if (CurrentJoin > 0) + TriList.BooleanInput[CurrentJoin].BoolValue = false; + CurrentJoin = join; + } + + } +} \ No newline at end of file diff --git a/Essentials/PepperDashEssentials/UI Drivers/SingleSubpageModalAndBackDriver.cs b/Essentials/PepperDashEssentials/UIDrivers/Page Drivers/SingleSubpageModalAndBackDriver.cs similarity index 100% rename from Essentials/PepperDashEssentials/UI Drivers/SingleSubpageModalAndBackDriver.cs rename to Essentials/PepperDashEssentials/UIDrivers/Page Drivers/SingleSubpageModalAndBackDriver.cs diff --git a/Essentials/PepperDashEssentials/UI Drivers/SingleSubpageModalDriver.cs b/Essentials/PepperDashEssentials/UIDrivers/Page Drivers/SingleSubpageModalDriver.cs similarity index 100% rename from Essentials/PepperDashEssentials/UI Drivers/SingleSubpageModalDriver.cs rename to Essentials/PepperDashEssentials/UIDrivers/Page Drivers/SingleSubpageModalDriver.cs diff --git a/Essentials/PepperDashEssentials/UI Drivers/SmartObjectRoomsList.cs b/Essentials/PepperDashEssentials/UIDrivers/SmartObjectRoomsList.cs similarity index 100% rename from Essentials/PepperDashEssentials/UI Drivers/SmartObjectRoomsList.cs rename to Essentials/PepperDashEssentials/UIDrivers/SmartObjectRoomsList.cs diff --git a/Essentials/PepperDashEssentials/UIDrivers/VC/EssentialsCiscoSparkUiDriver.cs b/Essentials/PepperDashEssentials/UIDrivers/VC/EssentialsCiscoSparkUiDriver.cs new file mode 100644 index 00000000..b61f39f6 --- /dev/null +++ b/Essentials/PepperDashEssentials/UIDrivers/VC/EssentialsCiscoSparkUiDriver.cs @@ -0,0 +1,235 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro.DeviceSupport; + +using PepperDash.Core; +using PepperDash.Essentials; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.SmartObjects; +using PepperDash.Essentials.Devices.Common.VideoCodec; + +namespace PepperDash.Essentials.UIDrivers.VC +{ + + /// + /// This fella will likely need to interact with the room's source, although that is routed via the spark... + /// Probably needs event or FB to feed AV driver - to show two-mute volume when appropriate. + /// + /// + public class EssentialsCiscoSparkUiDriver : PanelDriverBase + { + VideoCodecBase Codec; + + /// + /// + /// + SmartObjectDynamicList DirectorySrl; // ***************** SRL ??? + + + /// + /// To drive UI elements outside of this driver that may be dependent on this. + /// + BoolFeedback InCall; + BoolFeedback LocalPrivacyIsMuted; + + /// + /// For the subpages above the bar + /// + JoinedSigInterlock VCControlsInterlock; + + /// + /// For the different staging bars: Active, inactive + /// + JoinedSigInterlock StagingBarInterlock; + + /// + /// For the staging button feedbacks + /// + JoinedSigInterlock StagingButtonFeedbackInterlock; + + SmartObjectNumeric DialKeypad; + + // These are likely temp until we get a keyboard built + StringFeedback DialStringFeedback; + StringBuilder DialStringBuilder = new StringBuilder(); + BoolFeedback DialStringBackspaceVisibleFeedback; + + /// + /// + /// + /// + /// + public EssentialsCiscoSparkUiDriver(BasicTriListWithSmartObject triList, VideoCodecBase codec) + : base(triList) + { + Codec = codec; + SetupCallStagingPopover(); + SetupDialKeypad(); + + InCall = new BoolFeedback(() => false); + LocalPrivacyIsMuted = new BoolFeedback(() => false); + + //DirectorySrl = new SubpageReferenceList(triList, UISmartObjectJoin.VCDirectoryList, 3, 3, 3); + + VCControlsInterlock = new JoinedSigInterlock(triList); + VCControlsInterlock.SetButDontShow(UIBoolJoin.VCDirectoryVisible); + + StagingBarInterlock = new JoinedSigInterlock(triList); + StagingBarInterlock.SetButDontShow(UIBoolJoin.VCStagingInactivePopoverVisible); + + StagingButtonFeedbackInterlock = new JoinedSigInterlock(triList); + StagingButtonFeedbackInterlock.ShowInterlocked(UIBoolJoin.VCRecentsVisible); + + DialStringFeedback = new StringFeedback(() => DialStringBuilder.ToString()); + DialStringFeedback.LinkInputSig(triList.StringInput[UIStringJoin.KeyboardText]); + + DialStringBackspaceVisibleFeedback = new BoolFeedback(() => DialStringBuilder.Length > 0); + DialStringBackspaceVisibleFeedback + .LinkInputSig(TriList.BooleanInput[UIBoolJoin.KeyboardClearVisible]); + + Codec.InCallFeedback.OutputChange += new EventHandler(InCallFeedback_OutputChange); + } + + /// + /// + /// + public override void Show() + { + VCControlsInterlock.Show(); + StagingBarInterlock.Show(); + base.Show(); + } + + /// + /// + /// + public override void Hide() + { + VCControlsInterlock.Hide(); + StagingBarInterlock.Hide(); + base.Hide(); + } + + /// + /// Builds the call stage + /// + void SetupCallStagingPopover() + { + TriList.SetSigFalseAction(UIBoolJoin.VCStagingDirectoryPress, ShowDirectory); + TriList.SetSigFalseAction(UIBoolJoin.VCStagingConnectPress, ConnectPress); + TriList.SetSigFalseAction(UIBoolJoin.VCStagingKeypadPress, ShowKeypad); + TriList.SetSigFalseAction(UIBoolJoin.VCStagingRecentsPress, ShowRecents); + } + + /// + /// + /// + void SetupDialKeypad() + { + if(TriList.SmartObjects.Contains(UISmartObjectJoin.VCDialKeypad)) + { + DialKeypad = new SmartObjectNumeric(TriList.SmartObjects[UISmartObjectJoin.VCDialKeypad], true); + DialKeypad.Digit0.SetSigFalseAction(() => DialKeypadPress("0")); + DialKeypad.Digit1.SetSigFalseAction(() => DialKeypadPress("1")); + DialKeypad.Digit2.SetSigFalseAction(() => DialKeypadPress("2")); + DialKeypad.Digit3.SetSigFalseAction(() => DialKeypadPress("3")); + DialKeypad.Digit4.SetSigFalseAction(() => DialKeypadPress("4")); + DialKeypad.Digit5.SetSigFalseAction(() => DialKeypadPress("5")); + DialKeypad.Digit6.SetSigFalseAction(() => DialKeypadPress("6")); + DialKeypad.Digit7.SetSigFalseAction(() => DialKeypadPress("7")); + DialKeypad.Digit8.SetSigFalseAction(() => DialKeypadPress("8")); + DialKeypad.Digit9.SetSigFalseAction(() => DialKeypadPress("9")); + DialKeypad.Misc1SigName = "*"; + DialKeypad.Misc1.SetSigFalseAction(() => DialKeypadPress("*")); + DialKeypad.Misc2SigName = "#"; + DialKeypad.Misc2.SetSigFalseAction(() => DialKeypadPress("#")); + } + else + Debug.Console(0, "Trilist {0:x2}, VC dial keypad object {1} not found. Check SGD file or VTP", + TriList.ID, UISmartObjectJoin.VCDialKeypad); + } + + /// + /// + /// + void ShowCameraControls() + { + VCControlsInterlock.ShowInterlocked(UIBoolJoin.VCCameraVisible); + StagingButtonFeedbackInterlock.ShowInterlocked(UIBoolJoin.VCStagingCameraPress); + } + + void ShowKeypad() + { + VCControlsInterlock.ShowInterlocked(UIBoolJoin.VCKeypadVisible); + StagingButtonFeedbackInterlock.ShowInterlocked(UIBoolJoin.VCStagingKeypadPress); + } + + void ShowDirectory() + { + // populate directory + VCControlsInterlock.ShowInterlocked(UIBoolJoin.VCDirectoryVisible); + StagingButtonFeedbackInterlock.ShowInterlocked(UIBoolJoin.VCStagingDirectoryPress); + } + + void ShowRecents() + { + //populate recents + VCControlsInterlock.ShowInterlocked(UIBoolJoin.VCDirectoryVisible); + StagingButtonFeedbackInterlock.ShowInterlocked(UIBoolJoin.VCStagingRecentsPress); + } + + /// + /// + /// + void ConnectPress() + { + if (Codec.InCallFeedback.BoolValue) + Codec.EndCall(); + else + Codec.Dial(DialStringBuilder.ToString()); + } + + /// + /// + /// + + void InCallFeedback_OutputChange(object sender, EventArgs e) + { + if (Codec.InCallFeedback.BoolValue) // Call is starting + { + // Header icon + // Add end call button to stage + // Volume bar needs to have mic mute + } + else // ending + { + // Header icon + // Remove end call + // Volume bar no mic mute (or hidden if no source?) + } + } + + /// + /// + /// + /// + void DialKeypadPress(string i) + { + DialStringBuilder.Append(i); + DialStringFeedback.FireUpdate(); + TriList.BooleanInput[UIBoolJoin.KeyboardClearVisible].BoolValue = + DialStringBuilder.Length > 0; + } + + void DialKeypadBackspacePress() + { + DialStringBuilder.Remove(DialStringBuilder.Length - 1, 1); + DialStringFeedback.FireUpdate(); + TriList.BooleanInput[UIBoolJoin.KeyboardClearVisible].BoolValue = + DialStringBuilder.Length > 0; + } + } +} \ No newline at end of file diff --git a/Essentials/PepperDashEssentials/UI Drivers/VolumeAndSourceChangeArgs.cs b/Essentials/PepperDashEssentials/UIDrivers/VolumeAndSourceChangeArgs.cs similarity index 100% rename from Essentials/PepperDashEssentials/UI Drivers/VolumeAndSourceChangeArgs.cs rename to Essentials/PepperDashEssentials/UIDrivers/VolumeAndSourceChangeArgs.cs diff --git a/Essentials/PepperDashEssentials/UI Drivers/enums and base.cs b/Essentials/PepperDashEssentials/UIDrivers/enums and base.cs similarity index 100% rename from Essentials/PepperDashEssentials/UI Drivers/enums and base.cs rename to Essentials/PepperDashEssentials/UIDrivers/enums and base.cs diff --git a/EssentialsHuddleWorkflow/EssentialsHuddleWorkflow/EssentialsHuddleWorkflow.csproj b/EssentialsHuddleWorkflow/EssentialsHuddleWorkflow/EssentialsHuddleWorkflow.csproj new file mode 100644 index 00000000..33405f12 --- /dev/null +++ b/EssentialsHuddleWorkflow/EssentialsHuddleWorkflow/EssentialsHuddleWorkflow.csproj @@ -0,0 +1,98 @@ + + + Release + AnyCPU + 9.0.30729 + 2.0 + {225C9CD0-1AA9-464C-A3A2-2117EE45A87E} + Library + Properties + EssentialsHuddleWorkflow + EssentialsHuddleWorkflow + {0B4745B0-194B-4BB6-8E21-E9057CA92300};{4D628B5B-2FBC-4AA6-8C16-197242AEB884};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + WindowsCE + E2BECB1F-8C8C-41ba-B736-9BE7D946A398 + 5.0 + SmartDeviceProject1 + v3.5 + Windows CE + + + + + .allowedReferenceRelatedFileExtensions + true + full + false + bin\Debug\ + DEBUG;TRACE; + prompt + 4 + 512 + true + true + off + + + .allowedReferenceRelatedFileExtensions + none + true + bin\Release\ + prompt + 4 + 512 + true + true + off + + + + + False + ..\..\Essentials\PepperDashEssentials\bin\PepperDashEssentials.dll + + + False + ..\..\..\pepperdash-simplsharp-core\Pepperdash Core\CLZ Builds\PepperDash_Core.dll + + + False + ..\..\Essentials Core\PepperDashEssentialsBase\bin\PepperDash_Essentials_Core.dll + + + False + ..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpCustomAttributesInterface.dll + False + + + False + ..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpHelperInterface.dll + False + + + False + ..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpPro.exe + False + + + + + + + + + + + + + + + + + + + + + rem S# Pro preparation will execute after these operations + + \ No newline at end of file diff --git a/EssentialsHuddleWorkflow/EssentialsHuddleWorkflow/Properties/AssemblyInfo.cs b/EssentialsHuddleWorkflow/EssentialsHuddleWorkflow/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..0c28f7e6 --- /dev/null +++ b/EssentialsHuddleWorkflow/EssentialsHuddleWorkflow/Properties/AssemblyInfo.cs @@ -0,0 +1,8 @@ +using System.Reflection; + +[assembly: AssemblyTitle("EssentialsHuddleWorkflow")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("EssentialsHuddleWorkflow")] +[assembly: AssemblyCopyright("Copyright © 2017")] +[assembly: AssemblyVersion("1.0.0.*")] + diff --git a/EssentialsHuddleWorkflow/EssentialsHuddleWorkflow/Properties/ControlSystem.cfg b/EssentialsHuddleWorkflow/EssentialsHuddleWorkflow/Properties/ControlSystem.cfg new file mode 100644 index 00000000..e69de29b diff --git a/EssentialsHuddleWorkflow/EssentialsHuddleWorkflow/Room/EssentialsHuddleSpaceRoom.cs b/EssentialsHuddleWorkflow/EssentialsHuddleWorkflow/Room/EssentialsHuddleSpaceRoom.cs new file mode 100644 index 00000000..bc6749fe --- /dev/null +++ b/EssentialsHuddleWorkflow/EssentialsHuddleWorkflow/Room/EssentialsHuddleSpaceRoom.cs @@ -0,0 +1,420 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using PepperDash.Core; +using PepperDash.Essentials; +using PepperDash.Essentials.Core; + +namespace PepperDash.Essentials.EssentialsHuddleWorkflow +{ + public class EssentialsHuddleSpaceRoom : EssentialsRoomBase, IHasCurrentSourceInfoChange + { + public event EventHandler CurrentVolumeDeviceChange; + public event SourceInfoChangeHandler CurrentSingleSourceChange; + + protected override Func OnFeedbackFunc + { + get + { + return () => + { + var disp = DefaultDisplay as DisplayBase; + var val = CurrentSourceInfo != null + && CurrentSourceInfo.Type == eSourceListItemType.Route + && disp != null + && disp.PowerIsOnFeedback.BoolValue; + return val; + }; + } + } + /// + /// + /// + protected override Func IsWarmingFeedbackFunc + { + get + { + return () => + { + var disp = DefaultDisplay as DisplayBase; + if (disp != null) + return disp.IsWarmingUpFeedback.BoolValue; + else + return false; + }; + } + } + /// + /// + /// + protected override Func IsCoolingFeedbackFunc + { + get + { + return () => + { + var disp = DefaultDisplay as DisplayBase; + if (disp != null) + return disp.IsCoolingDownFeedback.BoolValue; + else + return false; + }; + } + } + + public EssentialsRoomPropertiesConfig Config { get; private set; } + + public IRoutingSinkWithSwitching DefaultDisplay { get; private set; } + public IRoutingSinkNoSwitching DefaultAudioDevice { get; private set; } + public IBasicVolumeControls DefaultVolumeControls { get; private set; } + + public bool ExcludeFromGlobalFunctions { get; set; } + + /// + /// The config name of the source list + /// + public string SourceListKey { get; set; } + + public string DefaultSourceItem { get; set; } + + public ushort DefaultVolume { get; set; } + + /// + /// If room is off, enables power on to last source. Default true + /// + public bool EnablePowerOnToLastSource { get; set; } + string LastSourceKey; + + /// + /// + /// + public IBasicVolumeControls CurrentVolumeControls + { + get { return _CurrentAudioDevice; } + set + { + if (value == _CurrentAudioDevice) return; + + var oldDev = _CurrentAudioDevice; + // derigister this room from the device, if it can + if (oldDev is IInUseTracking) + (oldDev as IInUseTracking).InUseTracker.RemoveUser(this, "audio"); + var handler = CurrentVolumeDeviceChange; + if (handler != null) + CurrentVolumeDeviceChange(this, new VolumeDeviceChangeEventArgs(oldDev, value, ChangeType.WillChange)); + _CurrentAudioDevice = value; + if (handler != null) + CurrentVolumeDeviceChange(this, new VolumeDeviceChangeEventArgs(oldDev, value, ChangeType.DidChange)); + // register this room with new device, if it can + if (_CurrentAudioDevice is IInUseTracking) + (_CurrentAudioDevice as IInUseTracking).InUseTracker.AddUser(this, "audio"); + } + } + IBasicVolumeControls _CurrentAudioDevice; + + /// + /// The SourceListItem last run - containing names and icons + /// + public SourceListItem CurrentSourceInfo + { + get { return _CurrentSourceInfo; } + private set + { + if (value == _CurrentSourceInfo) return; + + var handler = CurrentSingleSourceChange; + // remove from in-use tracker, if so equipped + if(_CurrentSourceInfo != null && _CurrentSourceInfo.SourceDevice is IInUseTracking) + (_CurrentSourceInfo.SourceDevice as IInUseTracking).InUseTracker.RemoveUser(this, "control"); + + if (handler != null) + handler(this, _CurrentSourceInfo, ChangeType.WillChange); + + _CurrentSourceInfo = value; + + // add to in-use tracking + if (_CurrentSourceInfo != null && _CurrentSourceInfo.SourceDevice is IInUseTracking) + (_CurrentSourceInfo.SourceDevice as IInUseTracking).InUseTracker.AddUser(this, "control"); + if (handler != null) + handler(this, _CurrentSourceInfo, ChangeType.DidChange); + } + } + SourceListItem _CurrentSourceInfo; + + public string CurrentSourceInfoKey { get; private set; } + + /// + /// + /// + /// + /// + public EssentialsHuddleSpaceRoom(string key, string name, IRoutingSinkWithSwitching defaultDisplay, + IRoutingSinkNoSwitching defaultAudio, EssentialsRoomPropertiesConfig config) + : base(key, name) + { + Config = config; + DefaultDisplay = defaultDisplay; + DefaultAudioDevice = defaultAudio; + if (defaultAudio is IBasicVolumeControls) + DefaultVolumeControls = defaultAudio as IBasicVolumeControls; + else if (defaultAudio is IHasVolumeDevice) + DefaultVolumeControls = (defaultAudio as IHasVolumeDevice).VolumeDevice; + CurrentVolumeControls = DefaultVolumeControls; + + var disp = DefaultDisplay as DisplayBase; + if (disp != null) + { + // Link power, warming, cooling to display + disp.PowerIsOnFeedback.OutputChange += (o, a) => + { + if (disp.PowerIsOnFeedback.BoolValue != OnFeedback.BoolValue) + { + if (!disp.PowerIsOnFeedback.BoolValue) + CurrentSourceInfo = null; + OnFeedback.FireUpdate(); + } + }; + + disp.IsWarmingUpFeedback.OutputChange += (o, a) => + { + IsWarmingUpFeedback.FireUpdate(); + if (!IsWarmingUpFeedback.BoolValue) + (DefaultDisplay as IBasicVolumeWithFeedback).SetVolume(DefaultVolume); + }; + disp.IsCoolingDownFeedback.OutputChange += (o, a) => + { + IsCoolingDownFeedback.FireUpdate(); + if (IsCoolingDownFeedback.BoolValue) + (DefaultDisplay as IBasicVolumeWithFeedback).SetVolume(DefaultVolume); + }; + } + + SourceListKey = "default"; + EnablePowerOnToLastSource = true; + } + + + /// + /// + /// + public override void Shutdown() + { + RunRouteAction("roomOff"); + } + + /// + /// Routes the default source item, if any + /// + public void RunDefaultRoute() + { + if (DefaultSourceItem != null) + RunRouteAction(DefaultSourceItem); + } + + /// + /// + /// + /// + public void RunRouteAction(string routeKey) + { + RunRouteAction(routeKey, null); + } + + /// + /// Gets a source from config list SourceListKey and dynamically build and executes the + /// route or commands + /// + /// + public void RunRouteAction(string routeKey, Action successCallback) + { + // Run this on a separate thread + new CTimer(o => + { + Debug.Console(1, this, "Run route action '{0}'", routeKey); + var dict = ConfigReader.ConfigObject.GetSourceListForKey(SourceListKey); + if(dict == null) + { + Debug.Console(1, this, "WARNING: Config source list '{0}' not found", SourceListKey); + return; + } + + // Try to get the list item by it's string key + if (!dict.ContainsKey(routeKey)) + { + Debug.Console(1, this, "WARNING: No item '{0}' found on config list '{1}'", + routeKey, SourceListKey); + return; + } + + var item = dict[routeKey]; + //Debug.Console(2, this, "Action {0} has {1} steps", + // item.SourceKey, item.RouteList.Count); + + // End usage timer on last source + if (!string.IsNullOrEmpty(LastSourceKey)) + { + var lastSource = dict[LastSourceKey].SourceDevice; + + try + { + if (lastSource != null && lastSource is IUsageTracking) + (lastSource as IUsageTracking).UsageTracker.EndDeviceUsage(); + } + catch (Exception e) + { + Debug.Console(1, this, "*#* EXCEPTION in end usage tracking (257):\r{0}", e); + } + } + + // Let's run it + if (routeKey.ToLower() != "roomoff") + { + LastSourceKey = routeKey; + } + else + { + CurrentSourceInfoKey = null; + } + + foreach (var route in item.RouteList) + { + // if there is a $defaultAll on route, run two separate + if (route.DestinationKey.Equals("$defaultAll", StringComparison.OrdinalIgnoreCase)) + { + // Going to assume a single-path route for now + var tempVideo = new SourceRouteListItem + { + DestinationKey = "$defaultDisplay", + SourceKey = route.SourceKey, + Type = eRoutingSignalType.Video + }; + DoRoute(tempVideo); + + //var tempAudio = new SourceRouteListItem + //{ + // DestinationKey = "$defaultAudio", + // SourceKey = route.SourceKey, + // Type = eRoutingSignalType.Audio + //}; + //DoRoute(tempAudio); + //continue; -- not sure why this was here + } + else + DoRoute(route); + } + + // Start usage timer on routed source + if (item.SourceDevice is IUsageTracking) + { + (item.SourceDevice as IUsageTracking).UsageTracker.StartDeviceUsage(); + } + + + // Set volume control on room, using default if non provided + IBasicVolumeControls volDev = null; + // Handle special cases for volume control + if (string.IsNullOrEmpty(item.VolumeControlKey) + || item.VolumeControlKey.Equals("$defaultAudio", StringComparison.OrdinalIgnoreCase)) + volDev = DefaultVolumeControls; + else if (item.VolumeControlKey.Equals("$defaultDisplay", StringComparison.OrdinalIgnoreCase)) + volDev = DefaultDisplay as IBasicVolumeControls; + // Or a specific device, probably rarely used. + else + { + var dev = DeviceManager.GetDeviceForKey(item.VolumeControlKey); + if (dev is IBasicVolumeControls) + volDev = dev as IBasicVolumeControls; + else if (dev is IHasVolumeDevice) + volDev = (dev as IHasVolumeDevice).VolumeDevice; + } + CurrentVolumeControls = volDev; + + // store the name and UI info for routes + if (item.SourceKey == "$off") + { + CurrentSourceInfoKey = routeKey; + CurrentSourceInfo = null; + } + else if (item.SourceKey != null) + { + CurrentSourceInfoKey = routeKey; + CurrentSourceInfo = item; + } + // And finally, set the "control". This will trigger event + //CurrentControlDevice = DeviceManager.GetDeviceForKey(item.SourceKey) as Device; + + OnFeedback.FireUpdate(); + + // report back when done + if (successCallback != null) + successCallback(); + + }, 0); // end of CTimer + } + + /// + /// Will power the room on with the last-used source + /// + public void PowerOnToDefaultOrLastSource() + { + if (!EnablePowerOnToLastSource || LastSourceKey == null) + return; + RunRouteAction(LastSourceKey); + } + + /// + /// + /// + /// + /// + bool DoRoute(SourceRouteListItem route) + { + IRoutingSinkNoSwitching dest = null; + + if (route.DestinationKey.Equals("$defaultaudio", StringComparison.OrdinalIgnoreCase)) + dest = DefaultAudioDevice; + else if (route.DestinationKey.Equals("$defaultDisplay", StringComparison.OrdinalIgnoreCase)) + dest = DefaultDisplay; + else + dest = DeviceManager.GetDeviceForKey(route.DestinationKey) as IRoutingSinkNoSwitching; + + if (dest == null) + { + Debug.Console(1, this, "Cannot route, unknown destination '{0}'", route.DestinationKey); + return false; + } + + if (route.SourceKey.Equals("$off", StringComparison.OrdinalIgnoreCase)) + { + dest.ReleaseRoute(); + if (dest is IPower) + (dest as IPower).PowerOff(); + } + else + { + var source = DeviceManager.GetDeviceForKey(route.SourceKey) as IRoutingOutputs; + if (source == null) + { + Debug.Console(1, this, "Cannot route unknown source '{0}' to {1}", route.SourceKey, route.DestinationKey); + return false; + } + dest.ReleaseAndMakeRoute(source, route.Type); + } + return true; + } + + /// + /// Runs "roomOff" action on all rooms not set to ExcludeFromGlobalFunctions + /// + public static void AllRoomsOff() + { + var allRooms = DeviceManager.AllDevices.Where(d => + d is EssentialsHuddleSpaceRoom && !(d as EssentialsHuddleSpaceRoom).ExcludeFromGlobalFunctions); + foreach (var room in allRooms) + (room as EssentialsHuddleSpaceRoom).RunRouteAction("roomOff"); + } + } +} \ No newline at end of file diff --git a/EssentialsHuddleWorkflow/EssentialsHuddleWorkflow/UI/DualDisplaySourceSRLController.cs b/EssentialsHuddleWorkflow/EssentialsHuddleWorkflow/UI/DualDisplaySourceSRLController.cs new file mode 100644 index 00000000..c91d72f1 --- /dev/null +++ b/EssentialsHuddleWorkflow/EssentialsHuddleWorkflow/UI/DualDisplaySourceSRLController.cs @@ -0,0 +1,28 @@ +//using System; +//using System.Collections.Generic; +//using System.Linq; +//using System.Text; +//using Crestron.SimplSharp; +//using Crestron.SimplSharpPro; +//using Crestron.SimplSharpPro.DeviceSupport; +//using Crestron.SimplSharpPro.UI; + +//using PepperDash.Essentials.Core; + +//namespace PepperDash.Essentials +//{ +// public class DualDisplaySourceSRLController : SubpageReferenceList +// { +// public DualDisplaySourceSRLController(BasicTriListWithSmartObject triList, +// uint smartObjectId, EssentialsPresentationRoom room) +// : base(triList, smartObjectId, 3, 3, 3) +// { +// var srcList = room.s items.Values.ToList().OrderBy(s => s.Order); +// foreach (var item in srcList) +// { +// GetBoolFeedbackSig(index, 1).UserObject = new Action(routeAction); + +// } +// } +// } +//} \ No newline at end of file diff --git a/EssentialsHuddleWorkflow/EssentialsHuddleWorkflow/UI/EssentialsTouchpanelController.cs b/EssentialsHuddleWorkflow/EssentialsHuddleWorkflow/UI/EssentialsTouchpanelController.cs new file mode 100644 index 00000000..8ad6ab1a --- /dev/null +++ b/EssentialsHuddleWorkflow/EssentialsHuddleWorkflow/UI/EssentialsTouchpanelController.cs @@ -0,0 +1,221 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharp.CrestronIO; +using Crestron.SimplSharpPro; +using Crestron.SimplSharpPro.DeviceSupport; +using Crestron.SimplSharpPro.UI; +using PepperDash.Core; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.PageManagers; + +namespace PepperDash.Essentials +{ + public class EssentialsTouchpanelController : Device + { + public BasicTriListWithSmartObject Panel { get; private set; } + + public PanelDriverBase PanelDriver { get; private set; } + + CTimer BacklightTransitionedOnTimer; + + public EssentialsTouchpanelController(string key, string name, Tswx52ButtonVoiceControl tsw, + string projectName, string sgdPath) + : base(key, name) + { + Panel = tsw; + tsw.LoadSmartObjects(sgdPath); + tsw.SigChange += new Crestron.SimplSharpPro.DeviceSupport.SigEventHandler(Tsw_SigChange); + } + + /// + /// Config constructor + /// + public EssentialsTouchpanelController(string key, string name, string type, CrestronTouchpanelPropertiesConfig props, uint id) + : base(key, name) + { + AddPostActivationAction(() => + { + Debug.Console(0, this, "post-activation linking"); + type = type.ToLower(); + try + { + if (type == "crestronapp") + { + var app = new CrestronApp(id, Global.ControlSystem); + app.ParameterProjectName.Value = props.ProjectName; + Panel = app; + } + else if (type == "tsw560") + Panel = new Tsw560(id, Global.ControlSystem); + else if (type == "tsw752") + Panel = new Tsw752(id, Global.ControlSystem); + else if (type == "tsw1052") + Panel = new Tsw1052(id, Global.ControlSystem); + else + { + Debug.Console(0, this, "WARNING: Cannot create TSW controller with type '{0}'", type); + return; + } + } + catch (Exception e) + { + Debug.Console(0, this, "WARNING: Cannot create TSW base class. Panel will not function: {0}", e.Message); + return; + } + + // Reserved sigs + if (Panel is TswFt5ButtonSystem) + { + var tsw = Panel as TswFt5ButtonSystem; + tsw.ExtenderSystemReservedSigs.Use(); + tsw.ExtenderSystemReservedSigs.DeviceExtenderSigChange + += ExtenderSystemReservedSigs_DeviceExtenderSigChange; + } + + new CTimer(o => + { + var regSuccess = Panel.Register(); + if (regSuccess != eDeviceRegistrationUnRegistrationResponse.Success) + Debug.Console(0, this, "WARNING: Registration failed. Continuing, but panel may not function: {0}", regSuccess); + + // Give up cleanly if SGD is not present. + var sgdName = @"\NVRAM\Program" + InitialParametersClass.ApplicationNumber + + @"\sgd\" + props.SgdFile; + if (!File.Exists(sgdName)) + { + Debug.Console(0, this, "WARNING: Smart object file '{0}' not present. Exiting TSW load", sgdName); + return; + } + + Panel.LoadSmartObjects(sgdName); + Panel.SigChange += Tsw_SigChange; + + var mainDriver = new EssentialsPanelMainInterfaceDriver(Panel, props); + // Then the AV driver + + // spin up different room drivers depending on room type + var room = DeviceManager.GetDeviceForKey(props.DefaultRoomKey); + if (room is EssentialsHuddleSpaceRoom) + { + Debug.Console(0, this, "Adding huddle space driver"); + var avDriver = new EssentialsHuddlePanelAvFunctionsDriver(mainDriver, props); + avDriver.CurrentRoom = room as EssentialsHuddleSpaceRoom; + avDriver.DefaultRoomKey = props.DefaultRoomKey; + mainDriver.AvDriver = avDriver; + LoadAndShowDriver(mainDriver); // This is a little convoluted. + + if (Panel is TswFt5ButtonSystem) + { + var tsw = Panel as TswFt5ButtonSystem; + // Wire up hard keys + tsw.Power.UserObject = new Action(b => { if (!b) avDriver.PowerButtonPressed(); }); + //tsw.Home.UserObject = new Action(b => { if (!b) HomePressed(); }); + tsw.Up.UserObject = new Action(avDriver.VolumeUpPress); + tsw.Down.UserObject = new Action(avDriver.VolumeDownPress); + tsw.ButtonStateChange += new ButtonEventHandler(Tsw_ButtonStateChange); + } + } + else if (room is EssentialsPresentationRoom) + { + Debug.Console(0, this, "Adding presentation room driver"); + var avDriver = new EssentialsPresentationPanelAvFunctionsDriver(mainDriver, props); + avDriver.CurrentRoom = room as EssentialsPresentationRoom; + avDriver.DefaultRoomKey = props.DefaultRoomKey; + mainDriver.AvDriver = avDriver; + LoadAndShowDriver(mainDriver); + + if (Panel is TswFt5ButtonSystem) + { + var tsw = Panel as TswFt5ButtonSystem; + // Wire up hard keys + tsw.Power.UserObject = new Action(b => { if (!b) avDriver.PowerButtonPressed(); }); + //tsw.Home.UserObject = new Action(b => { if (!b) HomePressed(); }); + tsw.Up.UserObject = new Action(avDriver.VolumeUpPress); + tsw.Down.UserObject = new Action(avDriver.VolumeDownPress); + tsw.ButtonStateChange += new ButtonEventHandler(Tsw_ButtonStateChange); + } + } + else + { + Debug.Console(0, this, "ERROR: Cannot load AvFunctionsDriver for room '{0}'", props.DefaultRoomKey); + } + }, 0); + }); + } + + public void LoadAndShowDriver(PanelDriverBase driver) + { + PanelDriver = driver; + driver.Show(); + } + + void HomePressed() + { + if (BacklightTransitionedOnTimer == null) + PanelDriver.BackButtonPressed(); + } + + + void ExtenderSystemReservedSigs_DeviceExtenderSigChange(DeviceExtender currentDeviceExtender, SigEventArgs args) + { + // If the sig is transitioning on, mark it in case it was home button that transitioned it + var blOnSig = (Panel as TswFt5ButtonSystem).ExtenderSystemReservedSigs.BacklightOnFeedback; + if (args.Sig == blOnSig && blOnSig.BoolValue) + { + BacklightTransitionedOnTimer = new CTimer(o => + { + BacklightTransitionedOnTimer = null; + }, 200); + } + } + + public void PulseBool(uint join) + { + var act = Panel.BooleanInput[join].UserObject as Action; + if (act != null) + { + act(true); + act(false); + } + } + + public void SetBoolSig(uint join, bool value) + { + var act = Panel.BooleanInput[join].UserObject as Action; + if (act != null) + act(value); + } + + public void SetIntSig(uint join, ushort value) + { + var act = Panel.BooleanInput[join].UserObject as Action; + if (act != null) + { + act(value); + } + } + + void Tsw_SigChange(object currentDevice, Crestron.SimplSharpPro.SigEventArgs args) + { + if (Debug.Level == 2) + Debug.Console(2, this, "Sig change: {0} {1}={2}", args.Sig.Type, args.Sig.Number, args.Sig.StringValue); + var uo = args.Sig.UserObject; + if (uo is Action) + (uo as Action)(args.Sig.BoolValue); + else if (uo is Action) + (uo as Action)(args.Sig.UShortValue); + else if (uo is Action) + (uo as Action)(args.Sig.StringValue); + } + + void Tsw_ButtonStateChange(GenericBase device, ButtonEventArgs args) + { + var uo = args.Button.UserObject; + if(uo is Action) + (uo as Action)(args.Button.State == eButtonState.Pressed); + } + } +} \ No newline at end of file diff --git a/EssentialsHuddleWorkflow/EssentialsHuddleWorkflow/UI/SubpageReferenceListActivityItem.cs b/EssentialsHuddleWorkflow/EssentialsHuddleWorkflow/UI/SubpageReferenceListActivityItem.cs new file mode 100644 index 00000000..4747a61a --- /dev/null +++ b/EssentialsHuddleWorkflow/EssentialsHuddleWorkflow/UI/SubpageReferenceListActivityItem.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; +using Crestron.SimplSharpPro.UI; + +using PepperDash.Essentials.Core; + +namespace PepperDash.Essentials +{ + public class SubpageReferenceListActivityItem : SubpageReferenceListItem + { + /// + /// + /// + /// + /// + /// 0=Share, 1=Phone Call, 2=Video Call, 3=End Meeting + /// + public SubpageReferenceListActivityItem(uint index, SubpageReferenceList owner, + ushort buttonMode, Action pressAction) + : base(index, owner) + { + Owner.GetBoolFeedbackSig(Index, 1).UserObject = pressAction; + Owner.UShortInputSig(Index, 1).UShortValue = buttonMode; + } + + /// + /// Called by SRL to release all referenced objects + /// + public override void Clear() + { + Owner.BoolInputSig(Index, 1).UserObject = null; + Owner.UShortInputSig(Index, 1).UShortValue = 0; + } + } +} \ No newline at end of file diff --git a/EssentialsHuddleWorkflow/EssentialsHuddleWorkflow/UI/SubpageReferenceListSourceItem.cs b/EssentialsHuddleWorkflow/EssentialsHuddleWorkflow/UI/SubpageReferenceListSourceItem.cs new file mode 100644 index 00000000..73cc5e71 --- /dev/null +++ b/EssentialsHuddleWorkflow/EssentialsHuddleWorkflow/UI/SubpageReferenceListSourceItem.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; +using Crestron.SimplSharpPro.UI; + +using PepperDash.Essentials.Core; + +namespace PepperDash.Essentials +{ + public class SubpageReferenceListSourceItem : SubpageReferenceListItem + { + public SourceListItem SourceItem { get; private set; } + + public SubpageReferenceListSourceItem(uint index, SubpageReferenceList owner, + SourceListItem sourceItem, Action routeAction) + : base(index, owner) + { + SourceItem = sourceItem; + owner.GetBoolFeedbackSig(index, 1).UserObject = new Action(routeAction); + owner.StringInputSig(index, 1).StringValue = SourceItem.PreferredName; + } + + public void RegisterForSourceChange(IHasCurrentSourceInfoChange room) + { + room.CurrentSingleSourceChange -= room_CurrentSourceInfoChange; + room.CurrentSingleSourceChange += room_CurrentSourceInfoChange; + } + + void room_CurrentSourceInfoChange(EssentialsRoomBase room, SourceListItem info, ChangeType type) + { + if (type == ChangeType.WillChange && info == SourceItem) + ClearFeedback(); + else if (type == ChangeType.DidChange && info == SourceItem) + SetFeedback(); + } + + /// + /// Called by SRL to release all referenced objects + /// + public override void Clear() + { + Owner.BoolInputSig(Index, 1).UserObject = null; + Owner.StringInputSig(Index, 1).StringValue = ""; + } + + /// + /// Sets the selected feedback on the button + /// + public void SetFeedback() + { + Owner.BoolInputSig(Index, 1).BoolValue = true; + } + + /// + /// Clears the selected feedback on the button + /// + public void ClearFeedback() + { + Owner.BoolInputSig(Index, 1).BoolValue = false; + } + } +} \ No newline at end of file diff --git a/Release Package/PepperDashEssentials.cpz b/Release Package/PepperDashEssentials.cpz index e9daf0be..7451c8ca 100644 Binary files a/Release Package/PepperDashEssentials.cpz and b/Release Package/PepperDashEssentials.cpz differ diff --git a/Release Package/PepperDashEssentials.dll b/Release Package/PepperDashEssentials.dll index c8f15a7c..679cdf05 100644 Binary files a/Release Package/PepperDashEssentials.dll and b/Release Package/PepperDashEssentials.dll differ