diff --git a/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoCodec.cs b/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoCodec.cs index 0d9947b4..0ee4034b 100644 --- a/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoCodec.cs +++ b/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoCodec.cs @@ -21,7 +21,8 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco { enum eCommandType { SessionStart, SessionEnd, Command, GetStatus, GetConfiguration }; - public class CiscoCodec : VideoCodecBase, IHasCallHistory, IHasCallFavorites, IHasDirectory, IHasScheduleAwareness, IOccupancyStatusProvider, IHasCodecLayouts, IHasCodecSelfview + public class CiscoCodec : VideoCodecBase, IHasCallHistory, IHasCallFavorites, IHasDirectory, + IHasScheduleAwareness, IOccupancyStatusProvider, IHasCodecLayouts, IHasCodecSelfview { public event EventHandler DirectoryResultReturned; @@ -283,6 +284,17 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco CodecStatus.Status.Video.Selfview.Mode.ValueChangedAction = SelfviewIsOnFeedback.FireUpdate; CodecStatus.Status.Video.Selfview.PIPPosition.ValueChangedAction = ComputeSelfviewPipStatus; CodecStatus.Status.Video.Layout.LayoutFamily.Local.ValueChangedAction = ComputeLocalLayout; + + CodecOsdIn = new RoutingInputPort(RoutingPortNames.CodecOsd, eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.Hdmi, new Action(StopSharing), this); + HdmiIn1 = new RoutingInputPort(RoutingPortNames.HdmiIn1, eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.Hdmi, new Action(SelectPresentationSource1), this); + HdmiIn2 = new RoutingInputPort(RoutingPortNames.HdmiIn2, eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.Hdmi, new Action(SelectPresentationSource2), this); + + HdmiOut = new RoutingOutputPort(RoutingPortNames.HdmiOut, eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.Hdmi, null, this); + + InputPorts.Add(CodecOsdIn); + InputPorts.Add(HdmiIn1); + InputPorts.Add(HdmiIn2); + OutputPorts.Add(HdmiOut); } /// @@ -304,19 +316,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco socket.ConnectionChange += new EventHandler(socket_ConnectionChange); } - CommunicationMonitor.Start(); - - CodecOsdIn = new RoutingInputPort(RoutingPortNames.CodecOsd, eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.Hdmi, new Action(StopSharing), this); - HdmiIn1 = new RoutingInputPort(RoutingPortNames.HdmiIn1, eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.Hdmi, new Action(SelectPresentationSource1), this); - HdmiIn2 = new RoutingInputPort(RoutingPortNames.HdmiIn2, eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.Hdmi, new Action(SelectPresentationSource2), this); - - HdmiOut = new RoutingOutputPort(RoutingPortNames.HdmiOut, eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.Hdmi, null, this); - - InputPorts.Add(CodecOsdIn); - InputPorts.Add(HdmiIn1); - InputPorts.Add(HdmiIn2); - OutputPorts.Add(HdmiOut); - + CommunicationMonitor.Start(); string prefix = "xFeedback register "; diff --git a/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoCodec.cs.orig b/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoCodec.cs.orig index 197e2315..3abc8d01 100644 --- a/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoCodec.cs.orig +++ b/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoCodec.cs.orig @@ -7,20 +7,27 @@ using Crestron.SimplSharp.Net.Https; using Crestron.SimplSharp.CrestronXml; using Crestron.SimplSharp.CrestronXml.Serialization; using Newtonsoft.Json; -using Cisco_One_Button_To_Push; -using Cisco_SX80_Corporate_Phone_Book; +<<<<<<< HEAD +//using Cisco_One_Button_To_Push; +//using Cisco_SX80_Corporate_Phone_Book; +======= +>>>>>>> origin/feature/ecs-342 using PepperDash.Core; using PepperDash.Essentials.Core; using PepperDash.Essentials.Core.Routing; using PepperDash.Essentials.Devices.Common.Codec; +using PepperDash.Essentials.Devices.Common.Occupancy; +using PepperDash.Essentials.Devices.Common.VideoCodec; namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco { enum eCommandType { SessionStart, SessionEnd, Command, GetStatus, GetConfiguration }; - public class CiscoCodec : VideoCodecBase + public class CiscoCodec : VideoCodecBase, IHasCallHistory, IHasCallFavorites, IHasDirectory, IHasScheduleAwareness, IOccupancyStatusProvider, IHasCodecLayouts, IHasCodecSelfview { + public event EventHandler DirectoryResultReturned; + public IBasicCommunication Communication { get; private set; } public CommunicationGather PortGather { get; private set; } public CommunicationGather JsonGather { get; private set; } @@ -31,23 +38,58 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco public BoolFeedback RoomIsOccupiedFeedback { get; private set; } - public BoolFeedback PeopleCountFeedback { get; private set; } + public IntFeedback PeopleCountFeedback { get; private set; } public BoolFeedback SpeakerTrackIsOnFeedback { get; private set; } - private CiscoOneButtonToPush CodecObtp; + public BoolFeedback SelfviewIsOnFeedback { get; private set; } - private Corporate_Phone_Book PhoneBook; + public StringFeedback SelfviewPipPositionFeedback { get; private set; } + public StringFeedback LocalLayoutFeedback { get; private set; } + + private CodecCommandWithLabel CurrentSelfviewPipPosition; + + private CodecCommandWithLabel CurrentLocalLayout; + + /// + /// List the available positions for the selfview PIP window + /// + public List SelfviewPipPositions = new List() + { + new CodecCommandWithLabel("CenterLeft", "Center Left"), + new CodecCommandWithLabel("CenterRight", "Center Right"), + new CodecCommandWithLabel("LowerLeft", "Lower Left"), + new CodecCommandWithLabel("LowerRight", "Lower Right"), + new CodecCommandWithLabel("UpperCenter", "Upper Center"), + new CodecCommandWithLabel("UpperLeft", "Upper Left"), + new CodecCommandWithLabel("UpperRight", "Upper Right"), + }; + + /// + /// Lists the available options for local layout + /// + public List LocalLayouts = new List() + { + new CodecCommandWithLabel("auto", "Auto"), + //new CiscoCodecLocalLayout("custom", "Custom"), // Left out for now + new CodecCommandWithLabel("equal","Equal"), + new CodecCommandWithLabel("overlay","Overlay"), + new CodecCommandWithLabel("prominent","Prominent"), + new CodecCommandWithLabel("single","Single") + }; + private CiscoCodecConfiguration.RootObject CodecConfiguration; private CiscoCodecStatus.RootObject CodecStatus; -<<<<<<< HEAD - private CiscoCodecEvents.RootObject CodecEvent; -======= - //private CiscoCodecEvents.RootObject CodecEvent; ->>>>>>> origin/feature/cisco-spark-2 + public CodecCallHistory CallHistory { get; private set; } + + public CodecCallFavorites CallFavorites { get; private set; } + + public CodecDirectory DirectoryRoot { get; private set; } + + public CodecScheduleAwareness CodecSchedule { get; private set; } /// /// Gets and returns the scaled volume of the codec @@ -76,11 +118,15 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco } } + /// + /// Gets the value of the currently shared source, or returns null + /// protected override Func SharingSourceFeedbackFunc { +#warning verify that source feedback to room works from codec get - { - return () => "Todo"; + { + return () => PresentationSourceKey; } } @@ -116,66 +162,130 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco } } - //private HttpsClient Client; + protected Func SelfViewIsOnFeedbackFunc + { + get + { + return () => CodecStatus.Status.Video.Selfview.Mode.BoolValue; + } + } - //private HttpApiServer Server; + protected Func SelfviewPipPositionFeedbackFunc + { + get + { + return () => CurrentSelfviewPipPosition.Label; + } + } - //private int ServerPort; - - //private string CodecUrl; - - //private string HttpSessionId; - - //private string FeedbackRegistrationExpression; + protected Func LocalLayoutFeedbackFunc + { + get + { + return () => CurrentLocalLayout.Label; + } + } private string CliFeedbackRegistrationExpression; private CodecSyncState SyncState; + private CodecPhonebookSyncState PhonebookSyncState; + private StringBuilder JsonMessage; private bool JsonFeedbackMessageIsIncoming; + public bool CommDebuggingIsOn; + string Delimiter = "\r\n"; int PresentationSource; - public bool CommDebuggingIsOn; + string PresentationSourceKey; + + string PhonebookMode = "Local"; // Default to Local + + int PhonebookResultsLimit = 255; // Could be set later by config. + + CTimer LoginMessageReceived; + + public RoutingInputPort CodecOsdIn { get; private set; } + public RoutingInputPort HdmiIn1 { get; private set; } + public RoutingInputPort HdmiIn2 { get; private set; } + public RoutingOutputPort HdmiOut { get; private set; } // Constructor for IBasicCommunication - public CiscoCodec(string key, string name, IBasicCommunication comm, int serverPort) + public CiscoCodec(string key, string name, IBasicCommunication comm, CiscoCodecPropertiesConfig props ) : base(key, name) { StandbyIsOnFeedback = new BoolFeedback(StandbyStateFeedbackFunc); + RoomIsOccupiedFeedback = new BoolFeedback(RoomIsOccupiedFeedbackFunc); + PeopleCountFeedback = new IntFeedback(PeopleCountFeedbackFunc); + SpeakerTrackIsOnFeedback = new BoolFeedback(SpeakerTrackIsOnFeedbackFunc); + SelfviewIsOnFeedback = new BoolFeedback(SelfViewIsOnFeedbackFunc); + SelfviewPipPositionFeedback = new StringFeedback(SelfviewPipPositionFeedbackFunc); + LocalLayoutFeedback = new StringFeedback(LocalLayoutFeedbackFunc); Communication = comm; - SyncState = new CodecSyncState(key + "--sync"); + LoginMessageReceived = new CTimer(DisconnectClientAndReconnect, 5000); + + if (props.CommunicationMonitorProperties != null) + { + CommunicationMonitor = new GenericCommunicationMonitor(this, Communication, props.CommunicationMonitorProperties); + } + else + { + CommunicationMonitor = new GenericCommunicationMonitor(this, Communication, 30000, 120000, 300000, "xStatus SystemUnit Software Version\r"); + } + + DeviceManager.AddDevice(CommunicationMonitor); + + PhonebookMode = props.PhonebookMode; + + SyncState = new CodecSyncState(key + "--Sync"); + + PhonebookSyncState = new CodecPhonebookSyncState(key + "--PhonebookSync"); + + SyncState.InitialSyncCompleted += new EventHandler(SyncState_InitialSyncCompleted); PortGather = new CommunicationGather(Communication, Delimiter); PortGather.IncludeDelimiter = true; PortGather.LineReceived += this.Port_LineReceived; - //ServerPort = serverPort; + //CodecObtp = new CiscoOneButtonToPush(); - CodecObtp = new CiscoOneButtonToPush(); - - PhoneBook = new Corporate_Phone_Book(); + //PhoneBook = new Corporate_Phone_Book(); CodecConfiguration = new CiscoCodecConfiguration.RootObject(); - CodecStatus = new CiscoCodecStatus.RootObject(); - //CodecEvent = new CiscoCodecEvents.RootObject(); + CodecInfo = new CiscoCodecInfo(CodecStatus, CodecConfiguration); + CallHistory = new CodecCallHistory(); + + if (props.Favorites != null) + { + CallFavorites = new CodecCallFavorites(); + CallFavorites.Favorites = props.Favorites; + } + + DirectoryRoot = new CodecDirectory(); + + CodecSchedule = new CodecScheduleAwareness(); + + //Set Feedback Actions CodecStatus.Status.Audio.Volume.ValueChangedAction = VolumeLevelFeedback.FireUpdate; CodecStatus.Status.Audio.VolumeMute.ValueChangedAction = MuteFeedback.FireUpdate; CodecStatus.Status.Audio.Microphones.Mute.ValueChangedAction = PrivacyModeIsOnFeedback.FireUpdate; CodecStatus.Status.Standby.State.ValueChangedAction = StandbyIsOnFeedback.FireUpdate; - - //Client = new HttpsClient(); - - //Server = new HttpApiServer(); + CodecStatus.Status.RoomAnalytics.PeoplePresence.ValueChangedAction = RoomIsOccupiedFeedback.FireUpdate; + CodecStatus.Status.RoomAnalytics.PeopleCount.Current.ValueChangedAction = PeopleCountFeedback.FireUpdate; + CodecStatus.Status.Cameras.SpeakerTrack.Status.ValueChangedAction = SpeakerTrackIsOnFeedback.FireUpdate; + CodecStatus.Status.Video.Selfview.Mode.ValueChangedAction = SelfviewIsOnFeedback.FireUpdate; + CodecStatus.Status.Video.Selfview.PIPPosition.ValueChangedAction = ComputeSelfviewPipStatus; + CodecStatus.Status.Video.Layout.LayoutFamily.Local.ValueChangedAction = ComputeLocalLayout; } /// @@ -185,10 +295,11 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco public override bool CustomActivate() { CrestronConsole.AddNewConsoleCommand(SendText, "send" + Key, "", ConsoleAccessLevelEnum.AccessOperator); - CrestronConsole.AddNewConsoleCommand(SetCommDebug, "SetCiscoCommDebug", "0 for Off, 1 for on", ConsoleAccessLevelEnum.AccessOperator); - - + CrestronConsole.AddNewConsoleCommand(SetCommDebug, "SetCodecCommDebug", "0 for Off, 1 for on", ConsoleAccessLevelEnum.AccessOperator); + CrestronConsole.AddNewConsoleCommand(GetPhonebook, "GetCodecPhonebook", "Triggers a refresh of the codec phonebook", ConsoleAccessLevelEnum.AccessOperator); + CrestronConsole.AddNewConsoleCommand(GetBookings, "GetCodecBookings", "Triggers a refresh of the booking data for today", ConsoleAccessLevelEnum.AccessOperator); + //CommDebuggingIsOn = true; Communication.Connect(); var socket = Communication as ISocketStatus; if (socket != null) @@ -196,62 +307,55 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco socket.ConnectionChange += new EventHandler(socket_ConnectionChange); } - InputPorts.Add(new RoutingInputPort(RoutingPortNames.HdmiIn1, eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.Hdmi, new Action(SelectPresentationSource1), this)); - InputPorts.Add(new RoutingInputPort(RoutingPortNames.HdmiIn2, eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.Hdmi, new Action(SelectPresentationSource2), this)); + CommunicationMonitor.Start(); - //Debug.Console(1, this, "Starting Cisco API Server"); + CodecOsdIn = new RoutingInputPort(RoutingPortNames.CodecOsd, eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.Hdmi, new Action(StopSharing), this); + HdmiIn1 = new RoutingInputPort(RoutingPortNames.HdmiIn1, eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.Hdmi, new Action(SelectPresentationSource1), this); + HdmiIn2 = new RoutingInputPort(RoutingPortNames.HdmiIn2, eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.Hdmi, new Action(SelectPresentationSource2), this); - //Server.Start(ServerPort); + HdmiOut = new RoutingOutputPort(RoutingPortNames.HdmiOut, eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.Hdmi, null, this); - //Server.ApiRequest += new EventHandler(Server_ApiRequest); - - //CodecUrl = string.Format("http://{0}", (Communication as GenericSshClient).Hostname); - - CommunicationMonitor = new GenericCommunicationMonitor(this, Communication, 2000, 120000, 300000, "xStatus SystemUnit Software Version\r"); - DeviceManager.AddDevice(CommunicationMonitor); - - //Client = new HttpsClient(); - - //Client.Verbose = true; - //Client.KeepAlive = true; + InputPorts.Add(CodecOsdIn); + InputPorts.Add(HdmiIn1); + InputPorts.Add(HdmiIn2); + OutputPorts.Add(HdmiOut); - // Temp feedback registration + string prefix = "xFeedback register "; - //FeedbackRegistrationExpression = - // "1" + - // string.Format("http://{0}:{1}/cisco/api", CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, 0), ServerPort) + - // "JSON" + - // "/Configuration" + - // "/Event/CallDisconnect" + - // "/Status/Call" + - // "" + - // "" + - // ""; - - string prefix = "xFeedback register "; CliFeedbackRegistrationExpression = prefix + "/Configuration" + Delimiter + prefix + "/Status/Audio" + Delimiter + prefix + "/Status/Call" + Delimiter + + prefix + "/Status/Conference/Presentation" + Delimiter + prefix + "/Status/Cameras/SpeakerTrack" + Delimiter + prefix + "/Status/RoomAnalytics" + Delimiter + prefix + "/Status/Standby" + Delimiter + prefix + "/Status/Video/Selfview" + Delimiter + prefix + "/Bookings" + Delimiter + - prefix + "/Event/CallDisconnect" + Delimiter; - - //StartHttpsSession(); - - //CodecObtp.Initialize(); - - //CodecObtp.GetMeetings(); - - //PhoneBook.DownloadPhoneBook(Corporate_Phone_Book.ePhoneBookLocation.Corporate); + prefix + "/Event/CallDisconnect" + Delimiter; return base.CustomActivate(); } + /// + /// Fires when initial codec sync is completed. Used to then send commands to get call history, phonebook, bookings, etc. + /// + /// + /// + void SyncState_InitialSyncCompleted(object sender, EventArgs e) + { + // Fire the ready event + SetIsReady(); + //CommDebuggingIsOn = false; + + GetCallHistory(); + + GetPhonebook(null); + + GetBookings(null); + } + public void SetCommDebug(string s) { if (s == "1") @@ -268,9 +372,26 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco void socket_ConnectionChange(object sender, GenericSocketStatusChageEventArgs e) { - // Reset sync status on disconnect - if (!e.Client.IsConnected) + if (e.Client.IsConnected) + { + //LoginMessageReceived.Reset(); + } + else + { SyncState.CodecDisconnected(); + PhonebookSyncState.CodecDisconnected(); + } + } + + void DisconnectClientAndReconnect(object o) + { + Debug.Console(0, this, "Disconnecting and Reconnecting to codec."); + + Communication.Disconnect(); + + CrestronEnvironment.Sleep(2000); + + Communication.Connect(); } /// @@ -324,22 +445,24 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco { case "*r login successful": { + LoginMessageReceived.Stop(); SendText("xPreferences outputmode json"); break; } case "xpreferences outputmode json": { - if(!SyncState.InitialStatusMessageWasReceived) + if (!SyncState.InitialStatusMessageWasReceived) SendText("xStatus"); break; } - case "xfeedback register": + case "xfeedback register /event/calldisconnect": { SyncState.FeedbackRegistered(); break; } } } + } public void SendText(string command) @@ -350,167 +473,6 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco Communication.SendText(command + Delimiter); } - //private void StartHttpsSession() - //{ - // SendHttpCommand("", eCommandType.SessionStart); - //} - - //private void EndHttpsSession() - //{ - // SendHttpCommand("", eCommandType.SessionEnd); - //} - - //private void SendHttpCommand(string command, eCommandType commandType) - //{ - // //HttpsClientRequest request = new HttpsClientRequest(); - - // //string urlSuffix = null; - - // //Client.UserName = null; - // //Client.Password = null; - - // //Client.PeerVerification = false; - // //Client.HostVerification = false; - - // //request.RequestType = RequestType.Post; - - // //if(!string.IsNullOrEmpty(HttpSessionId)) - // // request.Header.SetHeaderValue("Cookie", HttpSessionId); - - // //switch (commandType) - // //{ - // // case eCommandType.Command: - // // { - // // urlSuffix = "/putxml"; - // // request.ContentString = command; - // // request.Header.SetHeaderValue("Content-Type", "text/xml"); - // // break; - // // } - // // case eCommandType.SessionStart: - // // { - - // // urlSuffix = "/xmlapi/session/begin"; - - // // Client.UserName = (Communication as GenericSshClient).Username; - // // Client.Password = (Communication as GenericSshClient).Password; - - // // break; - // // } - // // case eCommandType.SessionEnd: - // // { - // // urlSuffix = "/xmlapi/session/end"; - // // request.Header.SetHeaderValue("Cookie", HttpSessionId); - // // break; - // // } - // // case eCommandType.GetStatus: - // // { - // // request.RequestType = RequestType.Get; - // // request.Header.SetHeaderValue("Content-Type", "text/xml"); - // // urlSuffix = "/getxml?location=/Status"; - // // break; - // // } - // // case eCommandType.GetConfiguration: - // // { - // // request.RequestType = RequestType.Get; - // // request.Header.SetHeaderValue("Content-Type", "text/xml"); - // // urlSuffix = "/getxml?location=/Configuration"; - // // break; - // // } - // //} - - // //var requestUrl = CodecUrl + urlSuffix; - // //request.Header.RequestVersion = "HTTP/1.1"; - // //request.Url.Parse(requestUrl); - - // //Debug.Console(1, this, "Sending HTTP request to Cisco Codec at {0}\nHeader:\n{1}\nContent:\n{2}", requestUrl, request.Header, request.ContentString); - - // //Client.DispatchAsync(request, PostConnectionCallback); - //} - - //void PostConnectionCallback(HttpsClientResponse resp, HTTPS_CALLBACK_ERROR err) - //{ - // //try - // //{ - // // if (resp != null) - // // { - // // if (resp.Code == 200) - // // { - // // Debug.Console(1, this, "Http Post to Cisco Codec Successful. Code: {0}\nContent: {1}", resp.Code, resp.ContentString); - - // // if (resp.ContentString.IndexOf("") > 1) - // // { - // // // Get the initial configruation for sync purposes - // // SendHttpCommand("", eCommandType.GetConfiguration); - // // } - // // else - // // { - // // try - // // { - // // if (resp.ContentString.IndexOf("") > -1) - // // { - // // XmlReaderSettings settings = new XmlReaderSettings(); - - // // XmlReader reader = new XmlReader(resp.ContentString, settings); - - // // CodecConfiguration = CrestronXMLSerialization.DeSerializeObject(reader); - - // // //Debug.Console(1, this, "Product Name: {0} Software Version: {1} ApiVersion: {2}", CodecConfiguration.Configuration.Product, CodecConfiguration.Version, CodecConfiguration.ApiVersion); - - // // // Get the initial status for sync purposes - // // SendHttpCommand("", eCommandType.GetStatus); - // // } - // // else if (resp.ContentString.IndexOf("") > -1) - // // { - // // XmlReaderSettings settings = new XmlReaderSettings(); - - // // XmlReader reader = new XmlReader(resp.ContentString, settings); - - // // CodecStatus = CrestronXMLSerialization.DeSerializeObject(reader); - // // //Debug.Console(1, this, "Product Name: {0} Software Version: {1} ApiVersion: {2} Volume: {3}", CodecStatus.Product, CodecStatus.Version, CodecStatus.ApiVersion, CodecStatus.Audio.Volume); - // // } - // // } - // // catch (Exception ex) - // // { - // // Debug.Console(1, this, "Error Deserializing XML document from codec: {0}", ex); - // // } - // // } - // // } - // // else if (resp.Code == 204) - // // { - // // Debug.Console(1, this, "Response Code: {0}\nHeader:\n{1}Content:\n{1}", resp.Code, resp.Header, resp.ContentString); - - // // HttpSessionId = resp.Header.GetHeaderValue("Set-Cookie"); - // // //var chunks = HttpSessionId.Split(';'); - // // //HttpSessionId = chunks[0]; - // // //HttpSessionId = HttpSessionId.Substring(HttpSessionId.IndexOf("=") + 1); - - - // // // Register for feedbacks once we have a valid session - // // SendHttpCommand(FeedbackRegistrationExpression, eCommandType.Command); - // // } - // // else - // // { - // // Debug.Console(1, this, "Response Code: {0}\nHeader:\n{1}Content:\n{1}Err:\n{2}", resp.Code, resp.Header, resp.ContentString, err); - // // } - // // } - // // else - // // Debug.Console(1, this, "Null response received from server"); - // //} - // //catch (Exception e) - // //{ - // // Debug.Console(1, this, "Error Initializing HTTPS Client: {0}", e); - // //} - //} - - //void Server_ApiRequest(object sender, Crestron.SimplSharp.Net.Http.OnHttpRequestArgs e) - //{ - // Debug.Console(1, this, "Api Reqeust from Codec: {0}", e.Request.ContentString); - // e.Response.Code = 200; - // e.Response.ContentString = "OK"; - - // DeserializeResponse(e.Request.ContentString); - //} - void DeserializeResponse(string response) { try @@ -523,36 +485,91 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco if (response.IndexOf("\"Status\":{") > -1) { + // Status Message + + // Temp object so we can inpsect for call data before simply deserializing + CiscoCodecStatus.RootObject tempCodecStatus = new CiscoCodecStatus.RootObject(); + + JsonConvert.PopulateObject(response, tempCodecStatus); + // Check to see if this is a call status message received after the initial status message - if(SyncState.InitialStatusMessageWasReceived && response.IndexOf("\"Call\":{") > -1) + if (tempCodecStatus.Status.Call.Count > 0) { - CiscoCodecStatus.RootObject callStatus = new CiscoCodecStatus.RootObject(); - - JsonConvert.PopulateObject(response, callStatus); - // Iterate through the call objects in the response - foreach (CiscoCodecStatus.Call call in callStatus.Status.Call) + foreach (CiscoCodecStatus.Call call in tempCodecStatus.Status.Call) { - // Look for a matching call id in the existing status structure - var existingCall = CodecStatus.Status.Call.FirstOrDefault(c => c.id.Equals(call.id)); + var tempActiveCall = ActiveCalls.FirstOrDefault(c => c.Id.Equals(call.id)); - if (existingCall != null) + if (tempActiveCall != null) { - // If an existing call object is found with a matching ID, populate the existing call with the new data. - // (must reserialize the object so that we can use PopulateObject() to overlay the new or updated properties on the existing object) - JsonConvert.PopulateObject(JsonConvert.SerializeObject(call), existingCall); + bool changeDetected = false; + + // Update properties of ActiveCallItem + if(call.Status != null) + if (!string.IsNullOrEmpty(call.Status.Value)) + { + eCodecCallStatus newStatus = eCodecCallStatus.Unknown; + + newStatus = CodecCallStatus.ConvertToStatusEnum(call.Status.Value); + + if (newStatus != eCodecCallStatus.Unknown) + { + changeDetected = true; + SetNewCallStatusAndFireCallStatusChange(newStatus, tempActiveCall); + } + + if (newStatus == eCodecCallStatus.Connected) + GetCallHistory(); + } + if (call.CallType != null) + if (!string.IsNullOrEmpty(call.CallType.Value)) + { + tempActiveCall.Type = CodecCallType.ConvertToTypeEnum(call.CallType.Value); + changeDetected = true; + } + if (call.DisplayName != null) + if (!string.IsNullOrEmpty(call.DisplayName.Value)) + { + tempActiveCall.Name = call.DisplayName.Value; + changeDetected = true; + } + if (call.Direction != null) + { + if (!string.IsNullOrEmpty(call.Direction.Value)) + { + tempActiveCall.Direction = CodecCallDirection.ConvertToDirectionEnum(call.Direction.Value); + changeDetected = true; + } + } + + if (changeDetected) + { + ListCalls(); + } } - else + else if( call.ghost == null ) // if the ghost value is present the call has ended already { - // Add the call - callStatus.Status.Call.Add(call); + // Create a new call item + var newCallItem = new CodecActiveCallItem() + { + Id = call.id, + Status = CodecCallStatus.ConvertToStatusEnum(call.Status.Value), + Name = call.DisplayName.Value, + Number = call.RemoteNumber.Value, + Type = CodecCallType.ConvertToTypeEnum(call.CallType.Value), + Direction = CodecCallDirection.ConvertToDirectionEnum(call.Direction.Value) + }; - // Add a call to the ActiveCalls List - //ActiveCalls.Add(new CodecActiveCallItem() { Id = call.id, Status = call.Status.Value, Name = call.DisplayName.Value, Number = call.RemoteNumber.Value, Type = call.Status.Value.ToString() + // Add it to the ActiveCalls List + ActiveCalls.Add(newCallItem); + + ListCalls(); + + SetNewCallStatusAndFireCallStatusChange(newCallItem.Status, newCallItem); } - // Handle call.status to determine if we need to fire an event notifying of an incoming call or a call disconnect } + } JsonConvert.PopulateObject(response, CodecStatus); @@ -560,12 +577,15 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco if (!SyncState.InitialStatusMessageWasReceived) { SyncState.InitialStatusMessageReceived(); - if(!SyncState.InitialConfigurationMessageWasReceived) + + if (!SyncState.InitialConfigurationMessageWasReceived) SendText("xConfiguration"); } } else if (response.IndexOf("\"Configuration\":{") > -1) { + // Configuration Message + JsonConvert.PopulateObject(response, CodecConfiguration); if (!SyncState.InitialConfigurationMessageWasReceived) @@ -580,12 +600,90 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco } else if (response.IndexOf("\"Event\":{") > -1) { + // Event Message + CiscoCodecEvents.RootObject eventReceived = new CiscoCodecEvents.RootObject(); JsonConvert.PopulateObject(response, eventReceived); EvalutateEvent(eventReceived); } + else if (response.IndexOf("\"CommandResponse\":{") > -1) + { + // CommandResponse Message + + if (response.IndexOf("\"CallHistoryRecentsResult\":{") > -1) + { + var codecCallHistory = new CiscoCallHistory.RootObject(); + + JsonConvert.PopulateObject(response, codecCallHistory); + + CallHistory.ConvertCiscoCallHistoryToGeneric(codecCallHistory.CommandResponse.CallHistoryRecentsResult.Entry); + } + else if (response.IndexOf("\"CallHistoryDeleteEntryResult\":{") > -1) + { + GetCallHistory(); + } + else if (response.IndexOf("\"PhonebookSearchResult\":{") > -1) + { + var codecPhonebookResponse = new CiscoCodecPhonebook.RootObject(); + + JsonConvert.PopulateObject(response, codecPhonebookResponse); + + if (!PhonebookSyncState.InitialPhonebookFoldersWasReceived) + { + // Check if the phonebook has any folders + PhonebookSyncState.InitialPhonebookFoldersReceived(); + + PhonebookSyncState.SetPhonebookHasFolders(codecPhonebookResponse.CommandResponse.PhonebookSearchResult.Folder.Count > 0); + + if (PhonebookSyncState.PhonebookHasFolders) + { + DirectoryRoot.AddFoldersToDirectory(CiscoCodecPhonebook.GetRootFoldersFromSearchResult(codecPhonebookResponse.CommandResponse.PhonebookSearchResult)); + } + + // Get the number of contacts in the phonebook + GetPhonebookContacts(); + } + else if (!PhonebookSyncState.NumberOfContactsWasReceived) + { + // Store the total number of contacts in the phonebook + PhonebookSyncState.SetNumberOfContacts(Int32.Parse(codecPhonebookResponse.CommandResponse.PhonebookSearchResult.ResultInfo.TotalRows.Value)); + + DirectoryRoot.AddContactsToDirectory(CiscoCodecPhonebook.GetRootContactsFromSearchResult(codecPhonebookResponse.CommandResponse.PhonebookSearchResult)); + + PhonebookSyncState.PhonebookRootEntriesReceived(); + + PrintPhonebook(DirectoryRoot); + } + else if (PhonebookSyncState.InitialSyncComplete) + { + var directoryResults = new CodecDirectory(); + + directoryResults = CiscoCodecPhonebook.ConvertCiscoPhonebookToGeneric(codecPhonebookResponse.CommandResponse.PhonebookSearchResult); + + PrintPhonebook(directoryResults); + + // This will return the latest results to all UIs. Multiple indendent UI Directory browsing will require a different methodology + var handler = DirectoryResultReturned; + if (handler != null) + handler(this, new DirectoryEventArgs() { Directory = directoryResults }); + + // Fire some sort of callback delegate to the UI that requested the directory search results + } + } + else if (response.IndexOf("\"BookingsListResult\":{") > -1) + { + var codecBookings = new CiscoCodecBookings.RootObject(); + + JsonConvert.PopulateObject(response, codecBookings); + + CodecSchedule.Meetings = CiscoCodecBookings.GetGenericMeetingsFromBookingResult(codecBookings.CommandResponse.BookingsListResult.Booking); + + + } + + } } catch (Exception ex) @@ -602,12 +700,19 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco { if (eventReceived.Event.CallDisconnect != null) { - var tempCall = CodecStatus.Status.Call.FirstOrDefault(c => c.id.Equals(eventReceived.Event.CallDisconnect.CallId)); + var tempActiveCall = ActiveCalls.FirstOrDefault(c => c.Id.Equals(eventReceived.Event.CallDisconnect.CallId.Value)); - if(tempCall != null) + // Remove the call from the Active calls list + if (tempActiveCall != null) { - CodecStatus.Status.Call.Remove(tempCall); - + ActiveCalls.Remove(tempActiveCall); + + ListCalls(); + + // Notify of the call disconnection + SetNewCallStatusAndFireCallStatusChange(eCodecCallStatus.Disconnected, tempActiveCall); + + GetCallHistory(); } } } @@ -615,10 +720,9 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco public override void ExecuteSwitch(object selector) { (selector as Action)(); + PresentationSourceKey = selector.ToString(); } - //protected Func InCallFeedbackFunc { get { return () => false; } } - protected override Func IncomingCallFeedbackFunc { get { return () => false; } } /// @@ -629,13 +733,94 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco { string callId = null; - if (CodecStatus.Status.Call.Count > 0) - callId = CodecStatus.Status.Call[0].id; + if (ActiveCalls.Count > 1) + { + foreach (CodecActiveCallItem call in ActiveCalls) ; + } + else if (ActiveCalls.Count == 1) + callId = ActiveCalls[0].Id; return callId; } + private void GetCallHistory() + { + SendText("xCommand CallHistory Recents Limit: 20 Order: OccurrenceTime"); + } + + /// + /// Gets the bookings for today + /// + /// + public void GetBookings(string command) + { + SendText("xCommand Bookings List Days: 1 DayOffset: 0"); + } + + /// + /// Triggers a refresh of the codec phonebook + /// + /// Just to allow this method to be called from a console command + public void GetPhonebook(string command) + { + PhonebookSyncState.CodecDisconnected(); + + DirectoryRoot = new CodecDirectory(); + + GetPhonebookFolders(); + } + + private void GetPhonebookFolders() + { + // Get Phonebook Folders (determine local/corporate from config, and set results limit) + SendText(string.Format("xCommand Phonebook Search PhonebookType: {0} ContactType: Folder", PhonebookMode)); + } + + private void GetPhonebookContacts() + { + // Get Phonebook Folders (determine local/corporate from config, and set results limit) + SendText(string.Format("xCommand Phonebook Search PhonebookType: {0} ContactType: Contact", PhonebookMode)); + } + + /// + /// Searches the codec phonebook for all contacts matching the search string + /// + /// + public void SearchDirectory(string searchString) + { + SendText(string.Format("xCommand Phonebook Search SearchString: \"{0}\" PhonebookType: {1} ContactType: Contact Limit: {2}", searchString, PhonebookMode, PhonebookResultsLimit)); + } + + /// + /// // Get contents of a specific folder in the phonebook + /// + /// + public void GetDirectoryFolderContents(string folderId) + { + SendText(string.Format("xCommand Phonebook Search FolderId: {0} PhonebookType: {1} ContactType: Contact Limit: {2}", folderId, PhonebookMode, PhonebookResultsLimit)); + } + + void PrintPhonebook(CodecDirectory directory) + { + if (Debug.Level > 0) + { + Debug.Console(1, this, "Directory Results:\n"); + + foreach (DirectoryItem item in directory.DirectoryResults) + { + if (item is DirectoryFolder) + { + Debug.Console(1, this, "+ {0}", item.Name); + } + else if (item is DirectoryContact) + { + Debug.Console(1, this, "{0}", item.Name); + } + } + } + } + public override void Dial(string s) { SendText(string.Format("xCommand Dial Number: \"{0}\"", s)); @@ -671,7 +856,10 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco public override void SendDtmf(string s) { - SendText(string.Format("xCommand Call DTMFSend CallId: {0} DTMFString: \"{1}\"", GetCallId(), s)); + if (CallFavorites != null) + { + SendText(string.Format("xCommand Call DTMFSend CallId: {0} DTMFString: \"{1}\"", GetCallId(), s)); + } } public void SelectPresentationSource(int source) @@ -804,9 +992,194 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco SendText("xCommand SystemUnit Boot Action: Restart"); } - protected override Func ActiveCallCountFeedbackFunc + /// + /// Turns on Selfview Mode + /// + public void SelfviewModeOn() { - get { return () => 0; } + SendText("xCommand Video Selfview Set Mode: On"); + } + + /// + /// Turns off Selfview Mode + /// + public void SelfviewModeOff() + { + SendText("xCommand Video Selfview Set Mode: Off"); + } + + /// + /// Toggles Selfview mode on/off + /// + public void SelfviewModeToggle() + { + string mode = string.Empty; + + if (CodecStatus.Status.Video.Selfview.Mode.BoolValue) + mode = "Off"; + else + mode = "On"; + + SendText(string.Format("xCommand Video Selfview Set Mode: {0}", mode)); + } + + /// + /// Sets a specified position for the selfview PIP window + /// + /// + public void SelfviewPipPositionSet(CodecCommandWithLabel position) + { + SendText(string.Format("xCommand Video Selfview Set Mode: On PIPPosition: {0}", position.Command)); + } + + /// + /// Toggles to the next selfview PIP position + /// + public void SelfviewPipPositionToggle() + { + if (CurrentSelfviewPipPosition != null) + { + var nextPipPositionIndex = SelfviewPipPositions.IndexOf(CurrentSelfviewPipPosition) + 1; + + if (nextPipPositionIndex >= SelfviewPipPositions.Count) // Check if we need to loop back to the first item in the list + nextPipPositionIndex = 0; + + SelfviewPipPositionSet(SelfviewPipPositions[nextPipPositionIndex]); + } + } + + /// + /// Sets a specific local layout + /// + /// + public void LocalLayoutSet(CodecCommandWithLabel layout) + { + SendText(string.Format("xCommand Video Layout LayoutFamily Set Target: local LayoutFamily: {0}", layout.Command)); + } + + /// + /// Toggles to the next local layout + /// + public void LocalLayoutToggle() + { + if(CurrentLocalLayout != null) + { + var nextLocalLayoutIndex = LocalLayouts.IndexOf(CurrentLocalLayout) + 1; + + if (nextLocalLayoutIndex >= LocalLayouts.Count) // Check if we need to loop back to the first item in the list + nextLocalLayoutIndex = 0; + + LocalLayoutSet(LocalLayouts[nextLocalLayoutIndex]); + } + } + + /// + /// Calculates the current selfview PIP position + /// + void ComputeSelfviewPipStatus() + { + CurrentSelfviewPipPosition = SelfviewPipPositions.FirstOrDefault(p => p.Command.ToLower().Equals(CodecStatus.Status.Video.Selfview.PIPPosition.Value.ToLower())); + + if(CurrentSelfviewPipPosition != null) + SelfviewIsOnFeedback.FireUpdate(); + } + + /// + /// Calculates the current local Layout + /// + void ComputeLocalLayout() + { + CurrentLocalLayout = LocalLayouts.FirstOrDefault(l => l.Command.ToLower().Equals(CodecStatus.Status.Video.Layout.LayoutFamily.Local.Value.ToLower())); + + if (CurrentLocalLayout != null) + LocalLayoutFeedback.FireUpdate(); + } + + public void RemoveCallHistoryEntry(CodecCallHistory.CallHistoryEntry entry) + { + SendText(string.Format("xCommand CallHistory DeleteEntry CallHistoryId: {0} AcknowledgeConsecutiveDuplicates: True", entry.OccurrenceHistoryId)); + } + + public class CiscoCodecInfo : VideoCodecInfo + { + public CiscoCodecStatus.RootObject CodecStatus { get; private set; } + + public CiscoCodecConfiguration.RootObject CodecConfiguration { get; private set; } + + public override bool MultiSiteOptionIsEnabled + { + get + { + if (CodecStatus.Status.SystemUnit.Software.OptionKeys.MultiSite.Value.ToLower() == "true") + return true; + else + return false; + } + + } + public override string IpAddress + { + get + { + if (CodecConfiguration.Configuration.Network != null) + { + if (CodecConfiguration.Configuration.Network.Count > 0) + return CodecConfiguration.Configuration.Network[0].IPv4.Address.Value; + } + return string.Empty; + } + } + public override string PhoneNumber + { + get + { + if (CodecConfiguration.Configuration.H323.H323Alias.E164 != null) + return CodecConfiguration.Configuration.H323.H323Alias.E164.Value; + else + return string.Empty; + } + } + public override string SipUri + { + get + { + if (CodecConfiguration.Configuration.H323.H323Alias.ID != null) + return CodecConfiguration.Configuration.H323.H323Alias.ID.Value; + else + return string.Empty; + } + } + public override bool AutoAnswerEnabled + { + get + { + if (CodecConfiguration.Configuration.Conference.AutoAnswer.Mode.Value.ToLower() == "on") + return true; + else + return false; + } + } + + public CiscoCodecInfo(CiscoCodecStatus.RootObject status, CiscoCodecConfiguration.RootObject configuration) + { + CodecStatus = status; + CodecConfiguration = configuration; + } + } + } + + /// + /// Represents a codec command that might need to have a friendly label applied for UI feedback purposes + /// + public class CodecCommandWithLabel + { + public string Command { get; set; } + public string Label { get; set; } + + public CodecCommandWithLabel(string command, string label) + { + Command = command; + Label = label; } } @@ -815,9 +1188,26 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco /// public class CodecSyncState : IKeyed { + bool _InitialSyncComplete; + + public event EventHandler InitialSyncCompleted; + public string Key { get; private set; } - public bool InitialSyncComplete { get; private set; } + public bool InitialSyncComplete + { + get { return _InitialSyncComplete; } + private set + { + if (value == true) + { + var handler = InitialSyncCompleted; + if (handler != null) + handler(this, new EventArgs()); + } + _InitialSyncComplete = value; + } + } public bool InitialStatusMessageWasReceived { get; private set; } @@ -871,4 +1261,99 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco InitialSyncComplete = false; } } + + /// + /// Used to track the status of syncronizing the phonebook values when connecting to a codec or refreshing the phonebook info + /// + public class CodecPhonebookSyncState : IKeyed + { + bool _InitialSyncComplete; + + public event EventHandler InitialSyncCompleted; + + public string Key { get; private set; } + + public bool InitialSyncComplete + { + get { return _InitialSyncComplete; } + private set + { + if (value == true) + { + var handler = InitialSyncCompleted; + if (handler != null) + handler(this, new EventArgs()); + } + _InitialSyncComplete = value; + } + } + + public bool InitialPhonebookFoldersWasReceived { get; private set; } + + public bool NumberOfContactsWasReceived { get; private set; } + + public bool PhonebookRootEntriesWasRecieved { get; private set; } + + public bool PhonebookHasFolders { get; private set; } + + public int NumberOfContacts { get; private set; } + + public CodecPhonebookSyncState(string key) + { + Key = key; + + CodecDisconnected(); + } + + public void InitialPhonebookFoldersReceived() + { + InitialPhonebookFoldersWasReceived = true; + + CheckSyncStatus(); + } + + public void PhonebookRootEntriesReceived() + { + PhonebookRootEntriesWasRecieved = true; + + CheckSyncStatus(); + } + + public void SetPhonebookHasFolders(bool value) + { + PhonebookHasFolders = value; + + Debug.Console(1, this, "Phonebook has folders: {0}", PhonebookHasFolders); + } + + public void SetNumberOfContacts(int contacts) + { + NumberOfContacts = contacts; + NumberOfContactsWasReceived = true; + + Debug.Console(1, this, "Phonebook contains {0} contacts.", NumberOfContacts); + + CheckSyncStatus(); + } + + public void CodecDisconnected() + { + InitialPhonebookFoldersWasReceived = false; + PhonebookHasFolders = false; + NumberOfContacts = 0; + NumberOfContactsWasReceived = false; + } + + void CheckSyncStatus() + { + if (InitialPhonebookFoldersWasReceived && NumberOfContactsWasReceived && PhonebookRootEntriesWasRecieved) + { + InitialSyncComplete = true; + Debug.Console(1, this, "Initial Phonebook Sync Complete!"); + } + else + InitialSyncComplete = false; + } + } + } \ No newline at end of file diff --git a/Essentials Devices Common/Essentials Devices Common/VideoCodec/VideoCodecBase.cs b/Essentials Devices Common/Essentials Devices Common/VideoCodec/VideoCodecBase.cs index deadefbd..feee31bc 100644 --- a/Essentials Devices Common/Essentials Devices Common/VideoCodec/VideoCodecBase.cs +++ b/Essentials Devices Common/Essentials Devices Common/VideoCodec/VideoCodecBase.cs @@ -11,7 +11,8 @@ using PepperDash.Essentials.Devices.Common.Codec; namespace PepperDash.Essentials.Devices.Common.VideoCodec { - public abstract class VideoCodecBase : Device, IRoutingInputsOutputs, IUsageTracking, IHasDialer, IHasSharing, ICodecAudio, iCodecInfo + public abstract class VideoCodecBase : Device, IRoutingInputsOutputs, + IUsageTracking, IHasDialer, IHasSharing, ICodecAudio, iCodecInfo { /// /// Fires when the status of any active, dialing, or incoming call changes or is new diff --git a/Essentials/PepperDashEssentials/Room/Types/EssentialsHuddleSpaceRoom.cs.orig b/Essentials/PepperDashEssentials/Room/Types/EssentialsHuddleSpaceRoom.cs.orig new file mode 100644 index 00000000..7c4e6c5d --- /dev/null +++ b/Essentials/PepperDashEssentials/Room/Types/EssentialsHuddleSpaceRoom.cs.orig @@ -0,0 +1,850 @@ +<<<<<<< HEAD +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); + } + + /// + /// Does what it says + /// + public override void SetDefaultLevels() + { + Debug.Console(0, this, "SetDefaultLevels not implemented"); + } + + /// + /// + /// + /// + /// + 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; +======= +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; + } + + + /// + /// + /// + protected override void EndShutdown() + { + 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); + } + + /// + /// Does what it says + /// + public override void SetDefaultLevels() + { + Debug.Console(0, this, "SetDefaultLevels not implemented"); + } + + /// + /// + /// + /// + /// + 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; +>>>>>>> origin/feature/ecs-342 + } + + public override void RoomVacatedForTimeoutPeriod(object o) + { + //Implement this + } + + /// + /// 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.orig b/Essentials/PepperDashEssentials/Room/Types/EssentialsHuddleVtc1Room.cs.orig new file mode 100644 index 00000000..f1b4b983 --- /dev/null +++ b/Essentials/PepperDashEssentials/Room/Types/EssentialsHuddleVtc1Room.cs.orig @@ -0,0 +1,967 @@ +<<<<<<< HEAD +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; +using PepperDash.Essentials.Devices.Common.Codec; +using PepperDash.Essentials.Devices.Common.VideoCodec; + +namespace PepperDash.Essentials +{ + public class EssentialsHuddleVtc1Room : EssentialsRoomBase, IHasCurrentSourceInfoChange, IPrivacy + { + public event EventHandler CurrentVolumeDeviceChange; + public event SourceInfoChangeHandler CurrentSingleSourceChange; + + + //************************ + // Call-related stuff + + public BoolFeedback InCallFeedback { get; private set; } + + /// + /// Make this more specific + /// + public List ActiveCalls { get; private set; } + + /// + /// States: 0 for on hook, 1 for video, 2 for audio, 3 for telekenesis + /// + public IntFeedback CallTypeFeedback { get; private set; } + + /// + /// + /// + public BoolFeedback PrivacyModeIsOnFeedback { get; private set; } + + /// + /// When something in the room is sharing with the far end or through other means + /// + public BoolFeedback IsSharingFeedback { get; private set; } + + //************************ + + + 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 IBasicVolumeControls DefaultAudioDevice { get; private set; } + public IBasicVolumeControls DefaultVolumeControls { get; private set; } + + public VideoCodecBase VideoCodec { 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; } + + /// + /// "codecOsd" + /// + public string DefaultCodecRouteString { get { return "codecOsd"; } } + + /// + /// Temporary implementation. Returns the schedule-ready object or null if none. Fow now, + /// always returns the VideoCodec if it is capable + /// + public IHasScheduleAwareness ScheduleSource { get { return VideoCodec as IHasScheduleAwareness; } } + + /// + /// + /// + /// + /// + public EssentialsHuddleVtc1Room(string key, string name, IRoutingSinkWithSwitching defaultDisplay, + IBasicVolumeControls defaultAudio, VideoCodecBase codec, EssentialsHuddleVtc1PropertiesConfig config) + : base(key, name) + { + if (codec == null) + throw new ArgumentNullException("codec cannot be null"); + Config = config; + DefaultDisplay = defaultDisplay; + VideoCodec = codec; + 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(); + } + if (disp.PowerIsOnFeedback.BoolValue) + { + SetDefaultLevels(); + } + }; + + 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); + }; + } + + InCallFeedback = new BoolFeedback(() => VideoCodec.IsInCall); + IsSharingFeedback = new BoolFeedback(() => VideoCodec.SharingSourceFeedback.StringValue != null); + + // link privacy to VC (for now?) + PrivacyModeIsOnFeedback = new BoolFeedback(() => VideoCodec.PrivacyModeIsOnFeedback.BoolValue); + VideoCodec.PrivacyModeIsOnFeedback.OutputChange += (o, a) => this.PrivacyModeIsOnFeedback.FireUpdate(); + + CallTypeFeedback = new IntFeedback(() => 0); + + SourceListKey = "default"; + EnablePowerOnToLastSource = true; + } + + + /// + /// + /// + public override void Shutdown() + { + RunRouteAction("roomOff"); + VideoCodec.EndAllCalls(); + } + + /// + /// Routes the default source item, if any. Returns true when default route exists + /// + public bool RunDefaultPresentRoute() + { + if (DefaultSourceItem != null) + RunRouteAction(DefaultSourceItem); + return DefaultSourceItem != null; + } + + /// + /// Sets up the room when started into call mode without presenting a source + /// + /// + public bool RunDefaultCallRoute() + { + RunRouteAction(DefaultCodecRouteString); + return true; + } + + /// + /// + /// + /// + 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 => + { + try + { + + 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; + } + + // End usage timer on last source + if (!string.IsNullOrEmpty(LastSourceKey)) + { + var usageLastSource = dict[LastSourceKey].SourceDevice as IUsageTracking; + if (usageLastSource != null && usageLastSource.UsageTracker != null) + { + try + { + // There MAY have been failures in here. Protect + usageLastSource.UsageTracker.EndDeviceUsage(); + } + catch (Exception e) + { + } + } + } + + // Let's run it + var item = dict[routeKey]; + if (routeKey.ToLower() != "roomoff") + { + + LastSourceKey = routeKey; + } + else + CurrentSourceInfoKey = null; + + // hand off the individual routes to this helper + foreach (var route in item.RouteList) + DoRouteItem(route); + + // Start usage timer on routed source + var usageNewSource = item.SourceDevice as IUsageTracking; + if (usageNewSource != null && usageNewSource.UsageTracker != null) // Have to make sure there is a usage tracker! + { + (item.SourceDevice as IUsageTracking).UsageTracker.StartDeviceUsage(); + } + + // 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(); + } + catch (Exception e) + { + Debug.Console(1, this, "ERROR in routing: {0}", e); + } + + }, 0); // end of CTimer + } + + /// + /// + /// + /// + void DoRouteItem(SourceRouteListItem route) + { + // 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); + } + + /// + /// + /// + /// + /// + bool DoRoute(SourceRouteListItem route) + { + IRoutingSinkNoSwitching dest = null; + + if (route.DestinationKey.Equals("$defaultaudio", StringComparison.OrdinalIgnoreCase)) + dest = DefaultAudioDevice as IRoutingSinkNoSwitching; + 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; +======= +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; +using PepperDash.Essentials.Devices.Common.Codec; +using PepperDash.Essentials.Devices.Common.VideoCodec; + +namespace PepperDash.Essentials +{ + public class EssentialsHuddleVtc1Room : EssentialsRoomBase, IHasCurrentSourceInfoChange, IPrivacy + { + public event EventHandler CurrentVolumeDeviceChange; + public event SourceInfoChangeHandler CurrentSingleSourceChange; + + + //************************ + // Call-related stuff + + public BoolFeedback InCallFeedback { get; private set; } + + /// + /// Make this more specific + /// + public List ActiveCalls { get; private set; } + + /// + /// States: 0 for on hook, 1 for video, 2 for audio, 3 for telekenesis + /// + public IntFeedback CallTypeFeedback { get; private set; } + + /// + /// + /// + public BoolFeedback PrivacyModeIsOnFeedback { get; private set; } + + /// + /// When something in the room is sharing with the far end or through other means + /// + public BoolFeedback IsSharingFeedback { get; private set; } + + //************************ + + + 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 IBasicVolumeControls DefaultAudioDevice { get; private set; } + public IBasicVolumeControls DefaultVolumeControls { get; private set; } + + public VideoCodecBase VideoCodec { 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; } + + /// + /// "codecOsd" + /// + public string DefaultCodecRouteString { get { return "codecOsd"; } } + + /// + /// Temporary implementation. Returns the schedule-ready object or null if none. Fow now, + /// always returns the VideoCodec if it is capable + /// + public IHasScheduleAwareness ScheduleSource { get { return VideoCodec as IHasScheduleAwareness; } } + + /// + /// + /// + /// + /// + public EssentialsHuddleVtc1Room(string key, string name, IRoutingSinkWithSwitching defaultDisplay, + IBasicVolumeControls defaultAudio, VideoCodecBase codec, EssentialsHuddleVtc1PropertiesConfig config) + : base(key, name) + { + if (codec == null) + throw new ArgumentNullException("codec cannot be null"); + Config = config; + DefaultDisplay = defaultDisplay; + VideoCodec = codec; + 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(); + } + if (disp.PowerIsOnFeedback.BoolValue) + { + SetDefaultLevels(); + } + }; + + 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); + }; + } + + InCallFeedback = new BoolFeedback(() => VideoCodec.IsInCall); + IsSharingFeedback = new BoolFeedback(() => VideoCodec.SharingSourceFeedback.StringValue != null); + + // link privacy to VC (for now?) + PrivacyModeIsOnFeedback = new BoolFeedback(() => VideoCodec.PrivacyModeIsOnFeedback.BoolValue); + VideoCodec.PrivacyModeIsOnFeedback.OutputChange += (o, a) => this.PrivacyModeIsOnFeedback.FireUpdate(); + + CallTypeFeedback = new IntFeedback(() => 0); + + SourceListKey = "default"; + EnablePowerOnToLastSource = true; + } + + + /// + /// + /// + protected override void EndShutdown() + { + RunRouteAction("roomOff"); + VideoCodec.EndAllCalls(); + } + + /// + /// Routes the default source item, if any. Returns true when default route exists + /// + public bool RunDefaultPresentRoute() + { + if (DefaultSourceItem != null) + RunRouteAction(DefaultSourceItem); + return DefaultSourceItem != null; + } + + /// + /// Sets up the room when started into call mode without presenting a source + /// + /// + public bool RunDefaultCallRoute() + { + RunRouteAction(DefaultCodecRouteString); + return true; + } + + /// + /// + /// + /// + 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 => + { + try + { + + 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; + } + + // End usage timer on last source + if (!string.IsNullOrEmpty(LastSourceKey)) + { + var usageLastSource = dict[LastSourceKey].SourceDevice as IUsageTracking; + if (usageLastSource != null && usageLastSource.UsageTracker != null) + { + try + { + // There MAY have been failures in here. Protect + usageLastSource.UsageTracker.EndDeviceUsage(); + } + catch (Exception e) + { + Debug.Console(1, this, "*#* EXCEPTION in end usage tracking:\r{0}", e); + } + } + } + + // Let's run it + var item = dict[routeKey]; + if (routeKey.ToLower() != "roomoff") + { + + LastSourceKey = routeKey; + } + else + CurrentSourceInfoKey = null; + + // hand off the individual routes to this helper + foreach (var route in item.RouteList) + DoRouteItem(route); + + // Start usage timer on routed source + var usageNewSource = item.SourceDevice as IUsageTracking; + if (usageNewSource != null && usageNewSource.UsageTracker != null) // Have to make sure there is a usage tracker! + { + (item.SourceDevice as IUsageTracking).UsageTracker.StartDeviceUsage(); + } + + // 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(); + } + catch (Exception e) + { + Debug.Console(1, this, "ERROR in routing: {0}", e); + } + + }, 0); // end of CTimer + } + + /// + /// + /// + /// + void DoRouteItem(SourceRouteListItem route) + { + // 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); + } + + /// + /// + /// + /// + /// + bool DoRoute(SourceRouteListItem route) + { + IRoutingSinkNoSwitching dest = null; + + if (route.DestinationKey.Equals("$defaultaudio", StringComparison.OrdinalIgnoreCase)) + dest = DefaultAudioDevice as IRoutingSinkNoSwitching; + 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; +>>>>>>> origin/feature/ecs-342 + } + + public override void RoomVacatedForTimeoutPeriod(object o) + { + //Implement this + } + + /// + /// Does what it says + /// + public override void SetDefaultLevels() + { + Debug.Console(1, this, "Restoring default levels"); + var vc = CurrentVolumeControls as IBasicVolumeWithFeedback; + if (vc != null) + vc.SetVolume(DefaultVolume); + } + /// + /// Will power the room on with the last-used source + /// + public void PowerOnToDefaultOrLastSource() + { + if (!EnablePowerOnToLastSource || LastSourceKey == null) + return; + RunRouteAction(LastSourceKey); + } + + /// + /// 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"); + } + + #region IPrivacy Members + + + public void PrivacyModeOff() + { + VideoCodec.PrivacyModeOff(); + } + + public void PrivacyModeOn() + { + VideoCodec.PrivacyModeOn(); + } + + public void PrivacyModeToggle() + { + VideoCodec.PrivacyModeToggle(); + } + + #endregion + } +} \ No newline at end of file diff --git a/Essentials/PepperDashEssentials/UI/JoinConstants/UIStringlJoin.cs b/Essentials/PepperDashEssentials/UI/JoinConstants/UIStringlJoin.cs index 7aeb3feb..f3d21d2c 100644 --- a/Essentials/PepperDashEssentials/UI/JoinConstants/UIStringlJoin.cs +++ b/Essentials/PepperDashEssentials/UI/JoinConstants/UIStringlJoin.cs @@ -27,9 +27,15 @@ namespace PepperDash.Essentials // RANGE IN USE public const uint VCRecentListTextEnd = 1230; /// - /// 1231 - the current layout mode + /// 1231 - 1261 range of joins for recent list time /// - public const uint VCLayoutModeText = 1231; + public const uint VCRecentListTimeTextStart = 1231; + // + public const uint VCRecentListTimeTextEnd = 1260; + /// + /// 1291 - the current layout mode + /// + public const uint VCLayoutModeText = 1291; /// /// 1301 - 1400 diff --git a/Essentials/PepperDashEssentials/UIDrivers/EssentialsHuddleVTC/EssentialsHuddleVtc1PanelAvFunctionsDriver.cs b/Essentials/PepperDashEssentials/UIDrivers/EssentialsHuddleVTC/EssentialsHuddleVtc1PanelAvFunctionsDriver.cs index 85fbef6c..17468457 100644 --- a/Essentials/PepperDashEssentials/UIDrivers/EssentialsHuddleVTC/EssentialsHuddleVtc1PanelAvFunctionsDriver.cs +++ b/Essentials/PepperDashEssentials/UIDrivers/EssentialsHuddleVTC/EssentialsHuddleVtc1PanelAvFunctionsDriver.cs @@ -1,399 +1,399 @@ -using System; -using System.Linq; -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; -using PepperDash.Essentials.Devices.Common.VideoCodec; - -namespace PepperDash.Essentials -{ - /// - /// - /// - public class EssentialsHuddleVtc1PanelAvFunctionsDriver : PanelDriverBase, IAVDriver - { - 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 feedbacks - /// - BoolInputSig CallButtonSig; - BoolInputSig ShareButtonSig; - BoolInputSig EndMeetingButtonSig; - - public HeaderListButton HeaderCallButton { get; private set; } - public HeaderListButton HeaderGearButton { get; private set; } - - /// - /// 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; - - /// - /// - /// - SubpageReferenceList MeetingsSrl; - - /// - /// The list of buttons on the header. Managed with visibility only - /// - SmartObjectHeaderButtonList HeaderButtonsList; - - /// - /// 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 - /// - public JoinedSigInterlock PopupInterlock { get; private set; } - - /// - /// Interlock for various source, camera, call control bars. The bar above the activity footer. This is also - /// used to show start page - /// - JoinedSigInterlock StagingBarInterlock; - - /// - /// Interlocks the various call-related subpages - /// - JoinedSigInterlock CallPagesInterlock; - - /// - /// The Video codec driver - /// - PepperDash.Essentials.UIDrivers.VC.EssentialsVideoCodecUiDriver VCDriver; - - /// - /// The driver for the tech page. Lazy getter for memory usage - /// - PepperDash.Essentials.UIDrivers.EssentialsHuddleTechPageDriver TechDriver - { - get - { - if (_TechDriver == null) - _TechDriver = new PepperDash.Essentials.UIDrivers.EssentialsHuddleTechPageDriver(TriList, this, CurrentRoom.Config.Tech); - return _TechDriver; - } - } - PepperDash.Essentials.UIDrivers.EssentialsHuddleTechPageDriver _TechDriver; - - /// - /// Controls timeout of notification ribbon timer - /// - CTimer RibbonTimer; - - /// - /// The keyboard - /// - public PepperDash.Essentials.Core.Touchpanels.Keyboards.HabaneroKeyboardController Keyboard { get; private set; } - - /// - /// The mode showing. Presentation or call. - /// - UiDisplayMode CurrentMode = UiDisplayMode.Start; - - CTimer NextMeetingTimer; - - - - /// - /// Constructor - /// - public EssentialsHuddleVtc1PanelAvFunctionsDriver(PanelDriverBase parent, CrestronTouchpanelPropertiesConfig config) - : base(parent.TriList) - { - Config = config; - Parent = parent; - - 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(2, 1); - ShareButtonSig = ActivityFooterSrl.BoolInputSig(1, 1); - EndMeetingButtonSig = ActivityFooterSrl.BoolInputSig(3, 1); - - MeetingsSrl = new SubpageReferenceList(TriList, UISmartObjectJoin.MeetingListSRL, 3, 3, 5); - - - // buttons are added in SetCurrentRoom - HeaderButtonsList = new SmartObjectHeaderButtonList(TriList.SmartObjects[UISmartObjectJoin.HeaderButtonList]); - - SetupActivityFooterWhenRoomOff(); - - ShowVolumeGauge = true; - Keyboard = new PepperDash.Essentials.Core.Touchpanels.Keyboards.HabaneroKeyboardController(TriList); - } - - /// - /// Add a video codec driver to this - /// - /// - public void SetVideoCodecDriver(PepperDash.Essentials.UIDrivers.VC.EssentialsVideoCodecUiDriver vcd) - { - VCDriver = vcd; - } - - /// - /// - /// - 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.HeaderRoomButtonPress, () => - 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.TopBarHabaneroDynamicVisible, true); - - TriList.SetBool(UIBoolJoin.ActivityFooterVisible, true); - - // Privacy mute button - TriList.SetSigFalseAction(UIBoolJoin.Volume1SpeechMutePressAndFB, CurrentRoom.PrivacyModeToggle); - CurrentRoom.PrivacyModeIsOnFeedback.LinkInputSig(TriList.BooleanInput[UIBoolJoin.Volume1SpeechMutePressAndFB]); - - // Default to showing rooms/sources now. - if (CurrentRoom.OnFeedback.BoolValue) - { - TriList.SetBool(UIBoolJoin.TapToBeginVisible, false); - } - else - { - TriList.SetBool(UIBoolJoin.StartPageVisible, true); - TriList.SetBool(UIBoolJoin.TapToBeginVisible, true); - } - ShowCurrentDisplayModeSigsInUse(); - - // *** Header Buttons *** - - // Generic "close" button for popup modals - TriList.SetSigFalseAction(UIBoolJoin.InterlockedModalClosePress, PopupInterlock.HideAndClear); - - // Volume related things - TriList.SetSigFalseAction(UIBoolJoin.VolumeDefaultPress, () => CurrentRoom.SetDefaultLevels()); - TriList.SetString(UIStringJoin.AdvancedVolumeSlider1Text, "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(); - }); - - SetupNextMeetingTimer(); - - base.Show(); - } - - /// - /// - /// - 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); - } - } - - /// - /// - /// - void HideLogo() - { - TriList.SetBool(UIBoolJoin.LogoDefaultVisible, false); - TriList.SetBool(UIBoolJoin.LogoUrlVisible, false); - } - - /// - /// - /// - public override void Hide() - { - HideAndClearCurrentDisplayModeSigsInUse(); - TriList.SetBool(UIBoolJoin.TopBarHabaneroDynamicVisible, 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; - if (NextMeetingTimer != null) - NextMeetingTimer.Stop(); - HideNextMeetingPopup(); - base.Hide(); - } - - /// - /// Reveals a message on the notification ribbon until cleared - /// - /// Text to display - /// Time in ms to display. 0 to keep on screen - public void ShowNotificationRibbon(string message, int timeout) - { - TriList.SetString(UIStringJoin.NotificationRibbonText, message); - TriList.SetBool(UIBoolJoin.NotificationRibbonVisible, true); - if (timeout > 0) - { - if (RibbonTimer != null) - RibbonTimer.Stop(); - RibbonTimer = new CTimer(o => { - TriList.SetBool(UIBoolJoin.NotificationRibbonVisible, false); - RibbonTimer = null; - }, timeout); - } - } - - /// - /// Hides the notification ribbon - /// - public void HideNotificationRibbon() - { - TriList.SetBool(UIBoolJoin.NotificationRibbonVisible, false); - if (RibbonTimer != null) - { - RibbonTimer.Stop(); - RibbonTimer = null; - } - } - - void SetupNextMeetingTimer() - { - var ss = CurrentRoom.ScheduleSource; - if (ss != null) - { - NextMeetingTimer = new CTimer(o => - { - if (CurrentRoom.OnFeedback.BoolValue) - return; - // Every 60 seconds, check meetings list for the closest, joinable meeting +using System; +using System.Linq; +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; +using PepperDash.Essentials.Devices.Common.VideoCodec; + +namespace PepperDash.Essentials +{ + /// + /// + /// + public class EssentialsHuddleVtc1PanelAvFunctionsDriver : PanelDriverBase, IAVDriver + { + 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 feedbacks + /// + BoolInputSig CallButtonSig; + BoolInputSig ShareButtonSig; + BoolInputSig EndMeetingButtonSig; + + public HeaderListButton HeaderCallButton { get; private set; } + public HeaderListButton HeaderGearButton { get; private set; } + + /// + /// 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; + + /// + /// + /// + SubpageReferenceList MeetingsSrl; + + /// + /// The list of buttons on the header. Managed with visibility only + /// + SmartObjectHeaderButtonList HeaderButtonsList; + + /// + /// 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 + /// + public JoinedSigInterlock PopupInterlock { get; private set; } + + /// + /// Interlock for various source, camera, call control bars. The bar above the activity footer. This is also + /// used to show start page + /// + JoinedSigInterlock StagingBarInterlock; + + /// + /// Interlocks the various call-related subpages + /// + JoinedSigInterlock CallPagesInterlock; + + /// + /// The Video codec driver + /// + PepperDash.Essentials.UIDrivers.VC.EssentialsVideoCodecUiDriver VCDriver; + + /// + /// The driver for the tech page. Lazy getter for memory usage + /// + PepperDash.Essentials.UIDrivers.EssentialsHuddleTechPageDriver TechDriver + { + get + { + if (_TechDriver == null) + _TechDriver = new PepperDash.Essentials.UIDrivers.EssentialsHuddleTechPageDriver(TriList, this, CurrentRoom.Config.Tech); + return _TechDriver; + } + } + PepperDash.Essentials.UIDrivers.EssentialsHuddleTechPageDriver _TechDriver; + + /// + /// Controls timeout of notification ribbon timer + /// + CTimer RibbonTimer; + + /// + /// The keyboard + /// + public PepperDash.Essentials.Core.Touchpanels.Keyboards.HabaneroKeyboardController Keyboard { get; private set; } + + /// + /// The mode showing. Presentation or call. + /// + UiDisplayMode CurrentMode = UiDisplayMode.Start; + + CTimer NextMeetingTimer; + + + + /// + /// Constructor + /// + public EssentialsHuddleVtc1PanelAvFunctionsDriver(PanelDriverBase parent, CrestronTouchpanelPropertiesConfig config) + : base(parent.TriList) + { + Config = config; + Parent = parent; + + 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(2, 1); + ShareButtonSig = ActivityFooterSrl.BoolInputSig(1, 1); + EndMeetingButtonSig = ActivityFooterSrl.BoolInputSig(3, 1); + + MeetingsSrl = new SubpageReferenceList(TriList, UISmartObjectJoin.MeetingListSRL, 3, 3, 5); + + + // buttons are added in SetCurrentRoom + HeaderButtonsList = new SmartObjectHeaderButtonList(TriList.SmartObjects[UISmartObjectJoin.HeaderButtonList]); + + SetupActivityFooterWhenRoomOff(); + + ShowVolumeGauge = true; + Keyboard = new PepperDash.Essentials.Core.Touchpanels.Keyboards.HabaneroKeyboardController(TriList); + } + + /// + /// Add a video codec driver to this + /// + /// + public void SetVideoCodecDriver(PepperDash.Essentials.UIDrivers.VC.EssentialsVideoCodecUiDriver vcd) + { + VCDriver = vcd; + } + + /// + /// + /// + 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.HeaderRoomButtonPress, () => + 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.TopBarHabaneroDynamicVisible, true); + + TriList.SetBool(UIBoolJoin.ActivityFooterVisible, true); + + // Privacy mute button + TriList.SetSigFalseAction(UIBoolJoin.Volume1SpeechMutePressAndFB, CurrentRoom.PrivacyModeToggle); + CurrentRoom.PrivacyModeIsOnFeedback.LinkInputSig(TriList.BooleanInput[UIBoolJoin.Volume1SpeechMutePressAndFB]); + + // Default to showing rooms/sources now. + if (CurrentRoom.OnFeedback.BoolValue) + { + TriList.SetBool(UIBoolJoin.TapToBeginVisible, false); + } + else + { + TriList.SetBool(UIBoolJoin.StartPageVisible, true); + TriList.SetBool(UIBoolJoin.TapToBeginVisible, true); + } + ShowCurrentDisplayModeSigsInUse(); + + // *** Header Buttons *** + + // Generic "close" button for popup modals + TriList.SetSigFalseAction(UIBoolJoin.InterlockedModalClosePress, PopupInterlock.HideAndClear); + + // Volume related things + TriList.SetSigFalseAction(UIBoolJoin.VolumeDefaultPress, () => CurrentRoom.SetDefaultLevels()); + TriList.SetString(UIStringJoin.AdvancedVolumeSlider1Text, "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(); + }); + + SetupNextMeetingTimer(); + + base.Show(); + } + + /// + /// + /// + 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); + } + } + + /// + /// + /// + void HideLogo() + { + TriList.SetBool(UIBoolJoin.LogoDefaultVisible, false); + TriList.SetBool(UIBoolJoin.LogoUrlVisible, false); + } + + /// + /// + /// + public override void Hide() + { + HideAndClearCurrentDisplayModeSigsInUse(); + TriList.SetBool(UIBoolJoin.TopBarHabaneroDynamicVisible, 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; + if (NextMeetingTimer != null) + NextMeetingTimer.Stop(); + HideNextMeetingPopup(); + base.Hide(); + } + + /// + /// Reveals a message on the notification ribbon until cleared + /// + /// Text to display + /// Time in ms to display. 0 to keep on screen + public void ShowNotificationRibbon(string message, int timeout) + { + TriList.SetString(UIStringJoin.NotificationRibbonText, message); + TriList.SetBool(UIBoolJoin.NotificationRibbonVisible, true); + if (timeout > 0) + { + if (RibbonTimer != null) + RibbonTimer.Stop(); + RibbonTimer = new CTimer(o => { + TriList.SetBool(UIBoolJoin.NotificationRibbonVisible, false); + RibbonTimer = null; + }, timeout); + } + } + + /// + /// Hides the notification ribbon + /// + public void HideNotificationRibbon() + { + TriList.SetBool(UIBoolJoin.NotificationRibbonVisible, false); + if (RibbonTimer != null) + { + RibbonTimer.Stop(); + RibbonTimer = null; + } + } + + void SetupNextMeetingTimer() + { + var ss = CurrentRoom.ScheduleSource; + if (ss != null) + { + NextMeetingTimer = new CTimer(o => + { + if (CurrentRoom.OnFeedback.BoolValue) + return; + // Every 60 seconds, check meetings list for the closest, joinable meeting var meetings = ss.CodecSchedule.Meetings; if (meetings.Count > 0) { @@ -425,824 +425,824 @@ namespace PepperDash.Essentials ShowNextMeetingPopup(); } - } - }, null, 0, 60000); - } - } - - /// - /// - /// - void ShowNextMeetingPopup() - { - TriList.SetSigFalseAction(UIBoolJoin.NextMeetingRibbonClosePress, HideNextMeetingPopup); - TriList.SetBool(UIBoolJoin.NextMeetingRibbonVisible, true); - } - - /// - /// - /// - void HideNextMeetingPopup() - { - TriList.SetBool(UIBoolJoin.NextMeetingRibbonVisible, false); - } - - /// - /// Calendar should only be visible when it's supposed to - /// - void CalendarPress() - { - RefreshMeetingsList(); - PopupInterlock.ShowInterlockedWithToggle(UIBoolJoin.MeetingsListVisible); - } - - /// - /// Dials a meeting after turning on room (if necessary) - /// - void RoomOnAndDialMeeting(string number) - { - Action dialAction = () => - { - var d = CurrentRoom.ScheduleSource as VideoCodecBase; - if (d != null) - d.Dial(number); - }; - if (CurrentRoom.OnFeedback.BoolValue) - dialAction(); - else - { - // Rig a one-time handler to catch when the room is warmed and then dial call - EventHandler oneTimeHandler = null; - oneTimeHandler = (o, a) => - { - if (!CurrentRoom.IsWarmingUpFeedback.BoolValue) - { - CurrentRoom.IsWarmingUpFeedback.OutputChange -= oneTimeHandler; - dialAction(); - } - }; - CurrentRoom.IsWarmingUpFeedback.OutputChange += oneTimeHandler; - ActivityCallButtonPressed(); - } - } - - /// - /// Reveals the tech page and puts away anything that's in the way. - /// - void ShowTech() - { - PopupInterlock.HideAndClear(); - TechDriver.Show(); - } - - /// - /// When the room is off, set the footer SRL - /// - void SetupActivityFooterWhenRoomOff() - { - ActivityFooterSrl.Clear(); - ActivityFooterSrl.AddItem(new SubpageReferenceListActivityItem(1, ActivityFooterSrl, 0, - b => { if (!b) ActivityShareButtonPressed(); })); - ActivityFooterSrl.AddItem(new SubpageReferenceListActivityItem(2, ActivityFooterSrl, 3, - b => { if (!b) ActivityCallButtonPressed(); })); - ActivityFooterSrl.Count = 2; - TriList.SetUshort(UIUshortJoin.PresentationStagingCaretMode, 1); // right one slot - TriList.SetUshort(UIUshortJoin.CallStagingCaretMode, 5); // left one slot - } - - /// - /// Sets up the footer SRL for when the room is on - /// - void SetupActivityFooterWhenRoomOn() - { - ActivityFooterSrl.Clear(); - ActivityFooterSrl.AddItem(new SubpageReferenceListActivityItem(1, ActivityFooterSrl, 0, - b => { if (!b) ActivityShareButtonPressed(); })); - ActivityFooterSrl.AddItem(new SubpageReferenceListActivityItem(2, ActivityFooterSrl, 3, - b => { if (!b) ActivityCallButtonPressed(); })); - ActivityFooterSrl.AddItem(new SubpageReferenceListActivityItem(3, ActivityFooterSrl, 4, - b => { if (!b) PowerButtonPressed(); })); - ActivityFooterSrl.Count = 3; - TriList.SetUshort(UIUshortJoin.PresentationStagingCaretMode, 2); // center - TriList.SetUshort(UIUshortJoin.CallStagingCaretMode, 0); // left -2 - } - - /// - /// Single point call for setting the feedbacks on the activity buttons - /// - void SetActivityFooterFeedbacks() - { - CallButtonSig.BoolValue = CurrentMode == UiDisplayMode.Call - && CurrentRoom.ShutdownType == eShutdownType.None; - ShareButtonSig.BoolValue = CurrentMode == UiDisplayMode.Presentation - && CurrentRoom.ShutdownType == eShutdownType.None; - EndMeetingButtonSig.BoolValue = CurrentRoom.ShutdownType != eShutdownType.None; - } - - /// - /// - /// - void ActivityCallButtonPressed() - { - if (VCDriver.IsVisible) - return; - HideLogo(); - TriList.SetBool(UIBoolJoin.StartPageVisible, false); - TriList.SetBool(UIBoolJoin.SourceStagingBarVisible, false); - TriList.SetBool(UIBoolJoin.SelectASourceVisible, false); - if (CurrentSourcePageManager != null) - CurrentSourcePageManager.Hide(); - if (!CurrentRoom.OnFeedback.BoolValue) - { - CurrentRoom.RunDefaultCallRoute(); - } - CurrentMode = UiDisplayMode.Call; - SetActivityFooterFeedbacks(); - VCDriver.Show(); - } - - /// - /// Attached to activity list share button - /// - void ActivityShareButtonPressed() - { - if (VCDriver.IsVisible) - VCDriver.Hide(); - TriList.SetBool(UIBoolJoin.StartPageVisible, false); - TriList.SetBool(UIBoolJoin.CallStagingBarVisible, false); - TriList.SetBool(UIBoolJoin.SourceStagingBarVisible, true); - // Run default source when room is off and share is pressed - if (!CurrentRoom.OnFeedback.BoolValue) - { - // If there's no default, show UI elements - if(!CurrentRoom.RunDefaultPresentRoute()) - TriList.SetBool(UIBoolJoin.SelectASourceVisible, true); - } - else // room is on show what's active or select a source if nothing is yet active - { - if(CurrentRoom.CurrentSourceInfo == null || CurrentRoom.CurrentSourceInfoKey == CurrentRoom.DefaultCodecRouteString) - TriList.SetBool(UIBoolJoin.SelectASourceVisible, true); - else if (CurrentSourcePageManager != null) - CurrentSourcePageManager.Show(); - } - CurrentMode = UiDisplayMode.Presentation; - SetActivityFooterFeedbacks(); - } - - /// - /// 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(eShutdownType.Manual); - } - - /// - /// - /// - /// - /// - void ShutdownPromptTimer_HasStarted(object sender, EventArgs e) - { - // Do we need to check where the UI is? No? - var timer = CurrentRoom.ShutdownPromptTimer; - SetActivityFooterFeedbacks(); - - if (CurrentRoom.ShutdownType == eShutdownType.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) - { - PowerDownModal.HideDialog(); - SetActivityFooterFeedbacks(); - 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) - { - SetActivityFooterFeedbacks(); - CurrentRoom.ShutdownPromptTimer.TimeRemainingFeedback.OutputChange -= ShutdownPromptTimer_TimeRemainingFeedback_OutputChange; - CurrentRoom.ShutdownPromptTimer.PercentFeedback.OutputChange -= ShutdownPromptTimer_PercentFeedback_OutputChange; - } - - /// - /// - /// - /// - /// - void ShutdownPromptTimer_WasCancelled(object sender, EventArgs e) - { - if (PowerDownModal != null) - PowerDownModal.HideDialog(); - SetActivityFooterFeedbacks(); - - CurrentRoom.ShutdownPromptTimer.TimeRemainingFeedback.OutputChange += ShutdownPromptTimer_TimeRemainingFeedback_OutputChange; - CurrentRoom.ShutdownPromptTimer.PercentFeedback.OutputChange -= ShutdownPromptTimer_PercentFeedback_OutputChange; - } - - /// - /// Event handler for countdown timer on power off modal - /// - 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; - } - - /// - /// Event handler for percentage on power off countdown - /// - 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 -= CurrentRoom_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 += CurrentRoom_IsCoolingDownFeedback_OutputChange; - - _CurrentRoom.CurrentVolumeDeviceChange += CurrentRoom_CurrentAudioDeviceChange; - RefreshAudioDeviceConnections(); - _CurrentRoom.CurrentSingleSourceChange += CurrentRoom_SourceInfoChange; - RefreshSourceInfo(); - - SetupHeaderButtons(); - } - else - { - // Clear sigs that need to be - TriList.StringInput[UIStringJoin.CurrentRoomName].StringValue = "Select a room"; - } - } - - /// - /// - /// - void SetupHeaderButtons() - { - TriList.SetBool(UIBoolJoin.TopBarHabaneroDynamicVisible, true); - - var roomConf = CurrentRoom.Config; - // - HeaderGearButton = new HeaderListButton(HeaderButtonsList, 5); - HeaderGearButton.SetIcon(HeaderListButton.Gear); - HeaderGearButton.OutputSig.SetSigHeldAction(2000, - ShowTech, - () => - { - if (CurrentRoom.OnFeedback.BoolValue) - PopupInterlock.ShowInterlockedWithToggle(UIBoolJoin.VolumesPageVisible); - else - PopupInterlock.ShowInterlockedWithToggle(UIBoolJoin.VolumesPagePowerOffVisible); - }); - - TriList.SetSigFalseAction(UIBoolJoin.TechExitButton, () => - 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); - } - var helpButton = new HeaderListButton(HeaderButtonsList, 4); - helpButton.SetIcon(HeaderListButton.Help); - helpButton.OutputSig.SetSigFalseAction(() => - { - 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); - }); - uint nextIndex = 3; - - // Lights button - //if (WHATEVER MAKES LIGHTS WORK) - //{ - // // do lights - // nextIndex--; - //} - - // Calendar button - if (_CurrentRoom.ScheduleSource != null) // ******************* Do we need a config option here as well? - { - var calBut = new HeaderListButton(HeaderButtonsList, nextIndex); - calBut.SetIcon(HeaderListButton.Calendar); - calBut.OutputSig.SetSigFalseAction(CalendarPress); - nextIndex--; - } - - // Call button - HeaderCallButton = new HeaderListButton(HeaderButtonsList, nextIndex); - HeaderCallButton.SetIcon(HeaderListButton.OnHook); - HeaderCallButton.OutputSig.SetSigFalseAction(() => - PopupInterlock.ShowInterlockedWithToggle(UIBoolJoin.HeaderActiveCallsListVisible)); - - nextIndex--; - - // blank any that remain - for (var i = nextIndex; i > 0; i--) - { - var blankBut = new HeaderListButton(HeaderButtonsList, i); - blankBut.ClearIcon(); - blankBut.OutputSig.SetSigFalseAction(() => { }); - } - } - - /// - /// - /// - void RefreshMeetingsList() - { - ushort i = 0; - foreach (var m in CurrentRoom.ScheduleSource.CodecSchedule.Meetings) - { - i++; - MeetingsSrl.StringInputSig(i, 1).StringValue = m.StartTime.ToShortTimeString(); - MeetingsSrl.StringInputSig(i, 2).StringValue = m.EndTime.ToShortTimeString(); - MeetingsSrl.StringInputSig(i, 3).StringValue = m.Title; - MeetingsSrl.StringInputSig(i, 4).StringValue = "Join"; - MeetingsSrl.BoolInputSig(i, 2).BoolValue = m.Joinable; - var mm = m; // lambda scope - MeetingsSrl.GetBoolFeedbackSig(i, 1).SetSigFalseAction(() => - { - PopupInterlock.Hide(); - var d = CurrentRoom.ScheduleSource as VideoCodecBase; - if (d != null) - RoomOnAndDialMeeting(mm.ConferenceNumberToDial); - }); - } - MeetingsSrl.Count = i; - } - - /// - /// For room on/off changes - /// - void CurrentRoom_OnFeedback_OutputChange(object sender, EventArgs e) - { - CurrentRoom_SyncOnFeedback(); - } - - /// - /// - /// - void CurrentRoom_SyncOnFeedback() - { - var value = _CurrentRoom.OnFeedback.BoolValue; - 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.VolumeDualMute1Visible].BoolValue = true; - - } - else - { - CurrentMode = UiDisplayMode.Start; - if (VCDriver.IsVisible) - VCDriver.Hide(); - SetupActivityFooterWhenRoomOff(); - ShowLogo(); - SetActivityFooterFeedbacks(); - TriList.BooleanInput[UIBoolJoin.StartPageVisible].BoolValue = true; - TriList.BooleanInput[UIBoolJoin.VolumeDualMute1Visible].BoolValue = false; - TriList.BooleanInput[UIBoolJoin.SourceStagingBarVisible].BoolValue = false; - } - } - - /// - /// - /// - void CurrentRoom_IsWarmingFeedback_OutputChange(object sender, EventArgs e) - { - if (CurrentRoom.IsWarmingUpFeedback.BoolValue) - { - 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 CurrentRoom_IsCoolingDownFeedback_OutputChange(object sender, EventArgs e) - { - if (CurrentRoom.IsCoolingDownFeedback.BoolValue) - { - 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(); - } - } - - /// - /// For hanging off various common things that child drivers might need from a parent AV driver - /// - public interface IAVDriver - { - PepperDash.Essentials.Core.Touchpanels.Keyboards.HabaneroKeyboardController Keyboard { get; } - JoinedSigInterlock PopupInterlock { get; } - void ShowNotificationRibbon(string message, int timeout); - void HideNotificationRibbon(); - HeaderListButton HeaderGearButton { get; } - HeaderListButton HeaderCallButton { get; } - } -} + } + }, null, 0, 60000); + } + } + + /// + /// + /// + void ShowNextMeetingPopup() + { + TriList.SetSigFalseAction(UIBoolJoin.NextMeetingRibbonClosePress, HideNextMeetingPopup); + TriList.SetBool(UIBoolJoin.NextMeetingRibbonVisible, true); + } + + /// + /// + /// + void HideNextMeetingPopup() + { + TriList.SetBool(UIBoolJoin.NextMeetingRibbonVisible, false); + } + + /// + /// Calendar should only be visible when it's supposed to + /// + void CalendarPress() + { + RefreshMeetingsList(); + PopupInterlock.ShowInterlockedWithToggle(UIBoolJoin.MeetingsListVisible); + } + + /// + /// Dials a meeting after turning on room (if necessary) + /// + void RoomOnAndDialMeeting(string number) + { + Action dialAction = () => + { + var d = CurrentRoom.ScheduleSource as VideoCodecBase; + if (d != null) + d.Dial(number); + }; + if (CurrentRoom.OnFeedback.BoolValue) + dialAction(); + else + { + // Rig a one-time handler to catch when the room is warmed and then dial call + EventHandler oneTimeHandler = null; + oneTimeHandler = (o, a) => + { + if (!CurrentRoom.IsWarmingUpFeedback.BoolValue) + { + CurrentRoom.IsWarmingUpFeedback.OutputChange -= oneTimeHandler; + dialAction(); + } + }; + CurrentRoom.IsWarmingUpFeedback.OutputChange += oneTimeHandler; + ActivityCallButtonPressed(); + } + } + + /// + /// Reveals the tech page and puts away anything that's in the way. + /// + void ShowTech() + { + PopupInterlock.HideAndClear(); + TechDriver.Show(); + } + + /// + /// When the room is off, set the footer SRL + /// + void SetupActivityFooterWhenRoomOff() + { + ActivityFooterSrl.Clear(); + ActivityFooterSrl.AddItem(new SubpageReferenceListActivityItem(1, ActivityFooterSrl, 0, + b => { if (!b) ActivityShareButtonPressed(); })); + ActivityFooterSrl.AddItem(new SubpageReferenceListActivityItem(2, ActivityFooterSrl, 3, + b => { if (!b) ActivityCallButtonPressed(); })); + ActivityFooterSrl.Count = 2; + TriList.SetUshort(UIUshortJoin.PresentationStagingCaretMode, 1); // right one slot + TriList.SetUshort(UIUshortJoin.CallStagingCaretMode, 5); // left one slot + } + + /// + /// Sets up the footer SRL for when the room is on + /// + void SetupActivityFooterWhenRoomOn() + { + ActivityFooterSrl.Clear(); + ActivityFooterSrl.AddItem(new SubpageReferenceListActivityItem(1, ActivityFooterSrl, 0, + b => { if (!b) ActivityShareButtonPressed(); })); + ActivityFooterSrl.AddItem(new SubpageReferenceListActivityItem(2, ActivityFooterSrl, 3, + b => { if (!b) ActivityCallButtonPressed(); })); + ActivityFooterSrl.AddItem(new SubpageReferenceListActivityItem(3, ActivityFooterSrl, 4, + b => { if (!b) PowerButtonPressed(); })); + ActivityFooterSrl.Count = 3; + TriList.SetUshort(UIUshortJoin.PresentationStagingCaretMode, 2); // center + TriList.SetUshort(UIUshortJoin.CallStagingCaretMode, 0); // left -2 + } + + /// + /// Single point call for setting the feedbacks on the activity buttons + /// + void SetActivityFooterFeedbacks() + { + CallButtonSig.BoolValue = CurrentMode == UiDisplayMode.Call + && CurrentRoom.ShutdownType == eShutdownType.None; + ShareButtonSig.BoolValue = CurrentMode == UiDisplayMode.Presentation + && CurrentRoom.ShutdownType == eShutdownType.None; + EndMeetingButtonSig.BoolValue = CurrentRoom.ShutdownType != eShutdownType.None; + } + + /// + /// + /// + void ActivityCallButtonPressed() + { + if (VCDriver.IsVisible) + return; + HideLogo(); + TriList.SetBool(UIBoolJoin.StartPageVisible, false); + TriList.SetBool(UIBoolJoin.SourceStagingBarVisible, false); + TriList.SetBool(UIBoolJoin.SelectASourceVisible, false); + if (CurrentSourcePageManager != null) + CurrentSourcePageManager.Hide(); + if (!CurrentRoom.OnFeedback.BoolValue) + { + CurrentRoom.RunDefaultCallRoute(); + } + CurrentMode = UiDisplayMode.Call; + SetActivityFooterFeedbacks(); + VCDriver.Show(); + } + + /// + /// Attached to activity list share button + /// + void ActivityShareButtonPressed() + { + if (VCDriver.IsVisible) + VCDriver.Hide(); + TriList.SetBool(UIBoolJoin.StartPageVisible, false); + TriList.SetBool(UIBoolJoin.CallStagingBarVisible, false); + TriList.SetBool(UIBoolJoin.SourceStagingBarVisible, true); + // Run default source when room is off and share is pressed + if (!CurrentRoom.OnFeedback.BoolValue) + { + // If there's no default, show UI elements + if(!CurrentRoom.RunDefaultPresentRoute()) + TriList.SetBool(UIBoolJoin.SelectASourceVisible, true); + } + else // room is on show what's active or select a source if nothing is yet active + { + if(CurrentRoom.CurrentSourceInfo == null || CurrentRoom.CurrentSourceInfoKey == CurrentRoom.DefaultCodecRouteString) + TriList.SetBool(UIBoolJoin.SelectASourceVisible, true); + else if (CurrentSourcePageManager != null) + CurrentSourcePageManager.Show(); + } + CurrentMode = UiDisplayMode.Presentation; + SetActivityFooterFeedbacks(); + } + + /// + /// 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(eShutdownType.Manual); + } + + /// + /// + /// + /// + /// + void ShutdownPromptTimer_HasStarted(object sender, EventArgs e) + { + // Do we need to check where the UI is? No? + var timer = CurrentRoom.ShutdownPromptTimer; + SetActivityFooterFeedbacks(); + + if (CurrentRoom.ShutdownType == eShutdownType.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) + { + PowerDownModal.HideDialog(); + SetActivityFooterFeedbacks(); + 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) + { + SetActivityFooterFeedbacks(); + CurrentRoom.ShutdownPromptTimer.TimeRemainingFeedback.OutputChange -= ShutdownPromptTimer_TimeRemainingFeedback_OutputChange; + CurrentRoom.ShutdownPromptTimer.PercentFeedback.OutputChange -= ShutdownPromptTimer_PercentFeedback_OutputChange; + } + + /// + /// + /// + /// + /// + void ShutdownPromptTimer_WasCancelled(object sender, EventArgs e) + { + if (PowerDownModal != null) + PowerDownModal.HideDialog(); + SetActivityFooterFeedbacks(); + + CurrentRoom.ShutdownPromptTimer.TimeRemainingFeedback.OutputChange += ShutdownPromptTimer_TimeRemainingFeedback_OutputChange; + CurrentRoom.ShutdownPromptTimer.PercentFeedback.OutputChange -= ShutdownPromptTimer_PercentFeedback_OutputChange; + } + + /// + /// Event handler for countdown timer on power off modal + /// + 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; + } + + /// + /// Event handler for percentage on power off countdown + /// + 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 -= CurrentRoom_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 += CurrentRoom_IsCoolingDownFeedback_OutputChange; + + _CurrentRoom.CurrentVolumeDeviceChange += CurrentRoom_CurrentAudioDeviceChange; + RefreshAudioDeviceConnections(); + _CurrentRoom.CurrentSingleSourceChange += CurrentRoom_SourceInfoChange; + RefreshSourceInfo(); + + SetupHeaderButtons(); + } + else + { + // Clear sigs that need to be + TriList.StringInput[UIStringJoin.CurrentRoomName].StringValue = "Select a room"; + } + } + + /// + /// + /// + void SetupHeaderButtons() + { + TriList.SetBool(UIBoolJoin.TopBarHabaneroDynamicVisible, true); + + var roomConf = CurrentRoom.Config; + // + HeaderGearButton = new HeaderListButton(HeaderButtonsList, 5); + HeaderGearButton.SetIcon(HeaderListButton.Gear); + HeaderGearButton.OutputSig.SetSigHeldAction(2000, + ShowTech, + () => + { + if (CurrentRoom.OnFeedback.BoolValue) + PopupInterlock.ShowInterlockedWithToggle(UIBoolJoin.VolumesPageVisible); + else + PopupInterlock.ShowInterlockedWithToggle(UIBoolJoin.VolumesPagePowerOffVisible); + }); + + TriList.SetSigFalseAction(UIBoolJoin.TechExitButton, () => + 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); + } + var helpButton = new HeaderListButton(HeaderButtonsList, 4); + helpButton.SetIcon(HeaderListButton.Help); + helpButton.OutputSig.SetSigFalseAction(() => + { + 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); + }); + uint nextIndex = 3; + + // Lights button + //if (WHATEVER MAKES LIGHTS WORK) + //{ + // // do lights + // nextIndex--; + //} + + // Calendar button + if (_CurrentRoom.ScheduleSource != null) // ******************* Do we need a config option here as well? + { + var calBut = new HeaderListButton(HeaderButtonsList, nextIndex); + calBut.SetIcon(HeaderListButton.Calendar); + calBut.OutputSig.SetSigFalseAction(CalendarPress); + nextIndex--; + } + + // Call button + HeaderCallButton = new HeaderListButton(HeaderButtonsList, nextIndex); + HeaderCallButton.SetIcon(HeaderListButton.OnHook); + HeaderCallButton.OutputSig.SetSigFalseAction(() => + PopupInterlock.ShowInterlockedWithToggle(UIBoolJoin.HeaderActiveCallsListVisible)); + + nextIndex--; + + // blank any that remain + for (var i = nextIndex; i > 0; i--) + { + var blankBut = new HeaderListButton(HeaderButtonsList, i); + blankBut.ClearIcon(); + blankBut.OutputSig.SetSigFalseAction(() => { }); + } + } + + /// + /// + /// + void RefreshMeetingsList() + { + ushort i = 0; + foreach (var m in CurrentRoom.ScheduleSource.CodecSchedule.Meetings) + { + i++; + MeetingsSrl.StringInputSig(i, 1).StringValue = m.StartTime.ToShortTimeString(); + MeetingsSrl.StringInputSig(i, 2).StringValue = m.EndTime.ToShortTimeString(); + MeetingsSrl.StringInputSig(i, 3).StringValue = m.Title; + MeetingsSrl.StringInputSig(i, 4).StringValue = "Join"; + MeetingsSrl.BoolInputSig(i, 2).BoolValue = m.Joinable; + var mm = m; // lambda scope + MeetingsSrl.GetBoolFeedbackSig(i, 1).SetSigFalseAction(() => + { + PopupInterlock.Hide(); + var d = CurrentRoom.ScheduleSource as VideoCodecBase; + if (d != null) + RoomOnAndDialMeeting(mm.ConferenceNumberToDial); + }); + } + MeetingsSrl.Count = i; + } + + /// + /// For room on/off changes + /// + void CurrentRoom_OnFeedback_OutputChange(object sender, EventArgs e) + { + CurrentRoom_SyncOnFeedback(); + } + + /// + /// + /// + void CurrentRoom_SyncOnFeedback() + { + var value = _CurrentRoom.OnFeedback.BoolValue; + 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.VolumeDualMute1Visible].BoolValue = true; + + } + else + { + CurrentMode = UiDisplayMode.Start; + if (VCDriver.IsVisible) + VCDriver.Hide(); + SetupActivityFooterWhenRoomOff(); + ShowLogo(); + SetActivityFooterFeedbacks(); + TriList.BooleanInput[UIBoolJoin.StartPageVisible].BoolValue = true; + TriList.BooleanInput[UIBoolJoin.VolumeDualMute1Visible].BoolValue = false; + TriList.BooleanInput[UIBoolJoin.SourceStagingBarVisible].BoolValue = false; + } + } + + /// + /// + /// + void CurrentRoom_IsWarmingFeedback_OutputChange(object sender, EventArgs e) + { + if (CurrentRoom.IsWarmingUpFeedback.BoolValue) + { + 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 CurrentRoom_IsCoolingDownFeedback_OutputChange(object sender, EventArgs e) + { + if (CurrentRoom.IsCoolingDownFeedback.BoolValue) + { + 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(); + } + } + + /// + /// For hanging off various common things that child drivers might need from a parent AV driver + /// + public interface IAVDriver + { + PepperDash.Essentials.Core.Touchpanels.Keyboards.HabaneroKeyboardController Keyboard { get; } + JoinedSigInterlock PopupInterlock { get; } + void ShowNotificationRibbon(string message, int timeout); + void HideNotificationRibbon(); + HeaderListButton HeaderGearButton { get; } + HeaderListButton HeaderCallButton { get; } + } +} diff --git a/Essentials/PepperDashEssentials/UIDrivers/VC/EssentialsVideoCodecUiDriver.cs b/Essentials/PepperDashEssentials/UIDrivers/VC/EssentialsVideoCodecUiDriver.cs index 03ecc292..6f7dc97d 100644 --- a/Essentials/PepperDashEssentials/UIDrivers/VC/EssentialsVideoCodecUiDriver.cs +++ b/Essentials/PepperDashEssentials/UIDrivers/VC/EssentialsVideoCodecUiDriver.cs @@ -59,6 +59,8 @@ namespace PepperDash.Essentials.UIDrivers.VC SmartObjectDynamicList DirectoryList; + CodecDirectory CurrentDirectoryResult; + // These are likely temp until we get a keyboard built StringFeedback DialStringFeedback; StringBuilder DialStringBuilder = new StringBuilder(); @@ -85,6 +87,7 @@ namespace PepperDash.Essentials.UIDrivers.VC SetupCallStagingPopover(); SetupDialKeypad(); ActiveCallsSRL = new SubpageReferenceList(TriList, UISmartObjectJoin.CodecActiveCallsHeaderList, 3, 3, 3); + SetupDirectoryList(); SetupRecentCallsList(); SetupFavorites(); SetupSelfViewControls(); @@ -368,13 +371,32 @@ namespace PepperDash.Essentials.UIDrivers.VC void RefreshRecentCallsList() { var codec = Codec as IHasCallHistory; + uint textOffset = 1200; + uint timeTextOffset = 1230; if (codec != null) { ushort i = 0; foreach (var c in codec.CallHistory.RecentCalls) { i++; - RecentCallsList.SetItemMainText(i, c.Name); + TriList.SetString(textOffset + i, c.Name); + // if it's today, show a simpler string + string timeText = null; + if (c.StartTime.Date == DateTime.Now.Date) + timeText = c.StartTime.ToShortTimeString(); + else + timeText = c.StartTime.ToString(); + TriList.SetString(timeTextOffset + i, timeText); + + string iconName = null; + if (c.Direction == eCodecCallDirection.Incoming) + iconName = "Left"; + else if (c.Direction == eCodecCallDirection.Outgoing) + iconName = "Right"; + else + iconName = "Blank"; + RecentCallsList.SetItemIcon(i, iconName); + var call = c; // for lambda scope RecentCallsList.SetItemButtonAction(i, b => { if(!b) Codec.Dial(call.Number); }); } @@ -406,7 +428,6 @@ namespace PepperDash.Essentials.UIDrivers.VC } else TriList.SetBool(1221 + i, false); - } } } @@ -419,17 +440,80 @@ namespace PepperDash.Essentials.UIDrivers.VC var codec = Codec as IHasDirectory; if (codec != null) { - //codec.CallHistory.RecentCallsListHasChanged += (o, a) => RefreshRecentCallsList(); - // EVENT??????????????? Pointed at refresh - DirectoryList = new SmartObjectDynamicList(TriList.SmartObjects[UISmartObjectJoin.VCDirectoryList], - true, 1300); - RefreshDirectory(); + var dir = Codec as IHasDirectory; + if (dir != null) + { + DirectoryList = new SmartObjectDynamicList(TriList.SmartObjects[UISmartObjectJoin.VCDirectoryList], + true, 1300); + dir.DirectoryResultReturned += new EventHandler(dir_DirectoryResultReturned); + CurrentDirectoryResult = dir.DirectoryRoot; + + if (CurrentDirectoryResult != null && dir.DirectoryRoot.DirectoryResults.Count > 0) + { + // populate it + } + else + { + // it will just show up?????? + } + } } } - void RefreshDirectory() - { + ///// + ///// + ///// + //void RefreshDirectory() + //{ + // (Codec as IHasDirectory).GetDirectoryFolderContents(CurrentDirectoryResult. + //} + /// + /// + /// + /// + /// + void dir_DirectoryResultReturned(object sender, DirectoryEventArgs e) + { + CurrentDirectoryResult = e.Directory; + ushort i = 0; + foreach (var r in CurrentDirectoryResult.DirectoryResults) + { + if (i == DirectoryList.MaxCount) + { + break; + } + + i++; + DirectoryList.SetItemMainText(i, r.Name); + if(r is DirectoryContact) + { + var dc = r as DirectoryContact; + // if more than one contact method, pop up modal to choose + // otherwiese dial 0 entry + if (dc.ContactMethods.Count == 1) + { + DirectoryList.SetItemButtonAction(i, b => { if (!b) Codec.Dial(dc.ContactMethods[0].Number); }); + } + else + { + + } + } + else + { + DirectoryList.SetItemButtonAction(i, b => + { + if (!b) + { + var id = (r as DirectoryFolder).FolderId; + (Codec as IHasDirectory).GetDirectoryFolderContents(id); + // will later call event handler + } + }); + } + } + DirectoryList.Count = i; } /// diff --git a/Release Package/PepperDashEssentials.cpz b/Release Package/PepperDashEssentials.cpz index fb4af4d7..6c1e5a80 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 9b086f12..497ad3c5 100644 Binary files a/Release Package/PepperDashEssentials.dll and b/Release Package/PepperDashEssentials.dll differ