From aea6942a1f965542fb4e535049ec483d06cc9230 Mon Sep 17 00:00:00 2001 From: Jason Alborough Date: Fri, 14 Aug 2020 15:51:37 -0400 Subject: [PATCH 1/4] Update to support Cisco Codec External Sources using Essentials Room logic. Adds new interface for Codecs IHasExternalSourceSwitching Adds ability to set and clear External Sources in Cisco Spark device class Adds ability to send sources from the source list to the Cisco Spark in the "EssentialsHuddleVtc1Room" room [ ] still needs parsing when source is selected on the Cisco touch 10 --- .../EssentialsHuddleVtc1FusionController.cs | 666 +++++++++--------- .../Room/Types/EssentialsHuddleVtc1Room.cs | 51 +- ...entialsHuddleVtc1PanelAvFunctionsDriver.cs | 7 +- .../Room/EssentialsRoomBase.cs | 14 +- .../Codec/IHasExternalSourceSwitching.cs | 17 + .../Essentials Devices Common.csproj | 1 + .../VideoCodec/CiscoCodec/CiscoSparkCodec.cs | 70 +- .../CiscoSparkCodecPropertiesConfig.cs | 95 +-- 8 files changed, 533 insertions(+), 388 deletions(-) create mode 100644 essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/IHasExternalSourceSwitching.cs diff --git a/PepperDashEssentials/Fusion/EssentialsHuddleVtc1FusionController.cs b/PepperDashEssentials/Fusion/EssentialsHuddleVtc1FusionController.cs index 4b0ef2b2..4159212c 100644 --- a/PepperDashEssentials/Fusion/EssentialsHuddleVtc1FusionController.cs +++ b/PepperDashEssentials/Fusion/EssentialsHuddleVtc1FusionController.cs @@ -2,340 +2,340 @@ using System.Linq; using Crestron.SimplSharp; using Crestron.SimplSharpPro; -using Crestron.SimplSharpPro.Fusion; - - +using Crestron.SimplSharpPro.Fusion; + + using PepperDash.Core; -using PepperDash.Essentials.Core; -using PepperDash.Essentials.Core.Config; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.Config; using PepperDash.Essentials.Core.Fusion; -namespace PepperDash.Essentials.Fusion -{ - public class EssentialsHuddleVtc1FusionController : EssentialsHuddleSpaceFusionSystemControllerBase - { - BooleanSigData CodecIsInCall; - - public EssentialsHuddleVtc1FusionController(EssentialsHuddleVtc1Room room, uint ipId) - : base(room, ipId) - { - - } - - /// - /// Called in base class constructor before RVI and GUID files are built - /// - protected override void ExecuteCustomSteps() - { - SetUpCodec(); - } - - /// - /// Creates a static asset for the codec and maps the joins to the main room symbol - /// - void SetUpCodec() - { - try - { - var codec = (Room as EssentialsHuddleVtc1Room).VideoCodec; - - if (codec == null) - { - Debug.Console(1, this, "Cannot link codec to Fusion because codec is null"); - return; - } - - codec.UsageTracker = new UsageTracking(codec); - codec.UsageTracker.UsageIsTracked = true; - codec.UsageTracker.DeviceUsageEnded += UsageTracker_DeviceUsageEnded; - - var codecPowerOnAction = new Action(b => { if (!b) codec.StandbyDeactivate(); }); - var codecPowerOffAction = new Action(b => { if (!b) codec.StandbyActivate(); }); - - // Map FusionRoom Attributes: - - // Codec volume - var codecVolume = FusionRoom.CreateOffsetUshortSig(50, "Volume - Fader01", eSigIoMask.InputOutputSig); - codecVolume.OutputSig.UserObject = new Action(b => (codec as IBasicVolumeWithFeedback).SetVolume(b)); - (codec as IBasicVolumeWithFeedback).VolumeLevelFeedback.LinkInputSig(codecVolume.InputSig); - - // In Call Status - CodecIsInCall = FusionRoom.CreateOffsetBoolSig(69, "Conf - VC 1 In Call", eSigIoMask.InputSigOnly); - codec.CallStatusChange += new EventHandler(codec_CallStatusChange); - - // Online status - if (codec is ICommunicationMonitor) - { - var c = codec as ICommunicationMonitor; - var codecOnline = FusionRoom.CreateOffsetBoolSig(122, "Online - VC 1", eSigIoMask.InputSigOnly); - codecOnline.InputSig.BoolValue = c.CommunicationMonitor.Status == MonitorStatus.IsOk; - c.CommunicationMonitor.StatusChange += (o, a) => - { - codecOnline.InputSig.BoolValue = a.Status == MonitorStatus.IsOk; - }; - Debug.Console(0, this, "Linking '{0}' communication monitor to Fusion '{1}'", codec.Key, "Online - VC 1"); - } - - // Codec IP Address - bool codecHasIpInfo = false; - var codecComm = codec.Communication; - - string codecIpAddress = string.Empty; - int codecIpPort = 0; - - StringSigData codecIpAddressSig; - StringSigData codecIpPortSig; - - if(codecComm is GenericSshClient) - { - codecIpAddress = (codecComm as GenericSshClient).Hostname; - codecIpPort = (codecComm as GenericSshClient).Port; - codecHasIpInfo = true; - } - else if (codecComm is GenericTcpIpClient) - { - codecIpAddress = (codecComm as GenericTcpIpClient).Hostname; - codecIpPort = (codecComm as GenericTcpIpClient).Port; - codecHasIpInfo = true; - } - - if (codecHasIpInfo) - { - codecIpAddressSig = FusionRoom.CreateOffsetStringSig(121, "IP Address - VC", eSigIoMask.InputSigOnly); - codecIpAddressSig.InputSig.StringValue = codecIpAddress; - - codecIpPortSig = FusionRoom.CreateOffsetStringSig(150, "IP Port - VC", eSigIoMask.InputSigOnly); - codecIpPortSig.InputSig.StringValue = codecIpPort.ToString(); - } - - var tempAsset = new FusionAsset(); - - var deviceConfig = ConfigReader.ConfigObject.Devices.FirstOrDefault(c => c.Key.Equals(codec.Key)); - - if (FusionStaticAssets.ContainsKey(deviceConfig.Uid)) - { - tempAsset = FusionStaticAssets[deviceConfig.Uid]; - } - else - { - // Create a new asset - tempAsset = new FusionAsset(FusionRoomGuids.GetNextAvailableAssetNumber(FusionRoom), codec.Name, "Codec", ""); - FusionStaticAssets.Add(deviceConfig.Uid, tempAsset); - } - - var codecAsset = FusionRoom.CreateStaticAsset(tempAsset.SlotNumber, tempAsset.Name, "Display", tempAsset.InstanceId); - codecAsset.PowerOn.OutputSig.UserObject = codecPowerOnAction; - codecAsset.PowerOff.OutputSig.UserObject = codecPowerOffAction; - codec.StandbyIsOnFeedback.LinkComplementInputSig(codecAsset.PowerOn.InputSig); - - // TODO: Map relevant attributes on asset symbol - - codecAsset.TrySetMakeModel(codec); - codecAsset.TryLinkAssetErrorToCommunication(codec); - } - catch (Exception e) - { - Debug.Console(1, this, "Error setting up codec in Fusion: {0}", e); - } - } - - void codec_CallStatusChange(object sender, PepperDash.Essentials.Devices.Common.Codec.CodecCallStatusItemChangeEventArgs e) - { - var codec = (Room as EssentialsHuddleVtc1Room).VideoCodec; - - CodecIsInCall.InputSig.BoolValue = codec.IsInCall; - } - - // These methods are overridden because they access the room class which is of a different type - - protected override void CreateSymbolAndBasicSigs(uint ipId) - { - Debug.Console(1, this, "Creating Fusion Room symbol with GUID: {0}", RoomGuid); - - FusionRoom = new FusionRoom(ipId, Global.ControlSystem, Room.Name, RoomGuid); - FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.Use(); - FusionRoom.ExtenderFusionRoomDataReservedSigs.Use(); - - FusionRoom.Register(); - - FusionRoom.FusionStateChange += FusionRoom_FusionStateChange; - - FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.DeviceExtenderSigChange += FusionRoomSchedule_DeviceExtenderSigChange; - FusionRoom.ExtenderFusionRoomDataReservedSigs.DeviceExtenderSigChange += ExtenderFusionRoomDataReservedSigs_DeviceExtenderSigChange; - FusionRoom.OnlineStatusChange += FusionRoom_OnlineStatusChange; - - CrestronConsole.AddNewConsoleCommand(RequestFullRoomSchedule, "FusReqRoomSchedule", "Requests schedule of the room for the next 24 hours", ConsoleAccessLevelEnum.AccessOperator); - CrestronConsole.AddNewConsoleCommand(ModifyMeetingEndTimeConsoleHelper, "FusReqRoomSchMod", "Ends or extends a meeting by the specified time", ConsoleAccessLevelEnum.AccessOperator); - CrestronConsole.AddNewConsoleCommand(CreateAsHocMeeting, "FusCreateMeeting", "Creates and Ad Hoc meeting for on hour or until the next meeting", ConsoleAccessLevelEnum.AccessOperator); - - // Room to fusion room - Room.OnFeedback.LinkInputSig(FusionRoom.SystemPowerOn.InputSig); - - // Moved to - CurrentRoomSourceNameSig = FusionRoom.CreateOffsetStringSig(84, "Display 1 - Current Source", eSigIoMask.InputSigOnly); - // Don't think we need to get current status of this as nothing should be alive yet. - (Room as EssentialsHuddleVtc1Room).CurrentSourceChange += Room_CurrentSourceInfoChange; - - - FusionRoom.SystemPowerOn.OutputSig.SetSigFalseAction((Room as EssentialsHuddleVtc1Room).PowerOnToDefaultOrLastSource); - FusionRoom.SystemPowerOff.OutputSig.SetSigFalseAction(() => (Room as EssentialsHuddleVtc1Room).RunRouteAction("roomOff", Room.SourceListKey)); - // NO!! room.RoomIsOn.LinkComplementInputSig(FusionRoom.SystemPowerOff.InputSig); - - - CrestronEnvironment.EthernetEventHandler += CrestronEnvironment_EthernetEventHandler; - } - - protected override void SetUpSources() - { - // Sources - var dict = ConfigReader.ConfigObject.GetSourceListForKey((Room as EssentialsHuddleVtc1Room).SourceListKey); - if (dict != null) - { - // NEW PROCESS: - // Make these lists and insert the fusion attributes by iterating these - var setTopBoxes = dict.Where(d => d.Value.SourceDevice is ISetTopBoxControls); - uint i = 1; - foreach (var kvp in setTopBoxes) - { - TryAddRouteActionSigs("Display 1 - Source TV " + i, 188 + i, kvp.Key, kvp.Value.SourceDevice); - i++; - if (i > 5) // We only have five spots - break; - } - - var discPlayers = dict.Where(d => d.Value.SourceDevice is IDiscPlayerControls); - i = 1; - foreach (var kvp in discPlayers) - { - TryAddRouteActionSigs("Display 1 - Source DVD " + i, 181 + i, kvp.Key, kvp.Value.SourceDevice); - i++; - if (i > 5) // We only have five spots - break; - } - - var laptops = dict.Where(d => d.Value.SourceDevice is Core.Devices.Laptop); - i = 1; - foreach (var kvp in laptops) - { - TryAddRouteActionSigs("Display 1 - Source Laptop " + i, 166 + i, kvp.Key, kvp.Value.SourceDevice); - i++; - if (i > 10) // We only have ten spots??? - break; - } - - foreach (var kvp in dict) - { - var usageDevice = kvp.Value.SourceDevice as IUsageTracking; - - if (usageDevice != null) - { - usageDevice.UsageTracker = new UsageTracking(usageDevice as Device); - usageDevice.UsageTracker.UsageIsTracked = true; - usageDevice.UsageTracker.DeviceUsageEnded += new EventHandler(UsageTracker_DeviceUsageEnded); - } - } - - } - else - { - Debug.Console(1, this, "WARNING: Config source list '{0}' not found for room '{1}'", - (Room as EssentialsHuddleVtc1Room).SourceListKey, Room.Key); - } - } - - protected override void SetUpDisplay() - { - try - { - //Setup Display Usage Monitoring - - var displays = DeviceManager.AllDevices.Where(d => d is DisplayBase); - - // Consider updating this in multiple display systems - - foreach (DisplayBase display in displays) - { - display.UsageTracker = new UsageTracking(display); - display.UsageTracker.UsageIsTracked = true; - display.UsageTracker.DeviceUsageEnded += new EventHandler(UsageTracker_DeviceUsageEnded); - } - - var defaultDisplay = (Room as EssentialsHuddleVtc1Room).DefaultDisplay as DisplayBase; - if (defaultDisplay == null) - { - Debug.Console(1, this, "Cannot link null display to Fusion because default display is null"); - return; - } - - var dispPowerOnAction = new Action(b => { if (!b) defaultDisplay.PowerOn(); }); - var dispPowerOffAction = new Action(b => { if (!b) defaultDisplay.PowerOff(); }); - - // Display to fusion room sigs - FusionRoom.DisplayPowerOn.OutputSig.UserObject = dispPowerOnAction; - FusionRoom.DisplayPowerOff.OutputSig.UserObject = dispPowerOffAction; - defaultDisplay.PowerIsOnFeedback.LinkInputSig(FusionRoom.DisplayPowerOn.InputSig); - if (defaultDisplay is IDisplayUsage) - (defaultDisplay as IDisplayUsage).LampHours.LinkInputSig(FusionRoom.DisplayUsage.InputSig); - - - - MapDisplayToRoomJoins(1, 158, defaultDisplay); - - - var deviceConfig = ConfigReader.ConfigObject.Devices.FirstOrDefault(d => d.Key.Equals(defaultDisplay.Key)); - - //Check for existing asset in GUIDs collection - - var tempAsset = new FusionAsset(); - - if (FusionStaticAssets.ContainsKey(deviceConfig.Uid)) - { - tempAsset = FusionStaticAssets[deviceConfig.Uid]; - } - else - { - // Create a new asset - tempAsset = new FusionAsset(FusionRoomGuids.GetNextAvailableAssetNumber(FusionRoom), defaultDisplay.Name, "Display", ""); - FusionStaticAssets.Add(deviceConfig.Uid, tempAsset); - } - - var dispAsset = FusionRoom.CreateStaticAsset(tempAsset.SlotNumber, tempAsset.Name, "Display", tempAsset.InstanceId); - dispAsset.PowerOn.OutputSig.UserObject = dispPowerOnAction; - dispAsset.PowerOff.OutputSig.UserObject = dispPowerOffAction; - defaultDisplay.PowerIsOnFeedback.LinkInputSig(dispAsset.PowerOn.InputSig); - // NO!! display.PowerIsOn.LinkComplementInputSig(dispAsset.PowerOff.InputSig); - // Use extension methods - dispAsset.TrySetMakeModel(defaultDisplay); - dispAsset.TryLinkAssetErrorToCommunication(defaultDisplay); - } - catch (Exception e) - { - Debug.Console(1, this, "Error setting up display in Fusion: {0}", e); - } - - } - - protected override void MapDisplayToRoomJoins(int displayIndex, int joinOffset, DisplayBase display) - { - string displayName = string.Format("Display {0} - ", displayIndex); - - - if (display == (Room as EssentialsHuddleVtc1Room).DefaultDisplay) - { - // Power on - var defaultDisplayPowerOn = FusionRoom.CreateOffsetBoolSig((uint)joinOffset, displayName + "Power On", eSigIoMask.InputOutputSig); - defaultDisplayPowerOn.OutputSig.UserObject = new Action(b => { if (!b) display.PowerOn(); }); - display.PowerIsOnFeedback.LinkInputSig(defaultDisplayPowerOn.InputSig); - - // Power Off - var defaultDisplayPowerOff = FusionRoom.CreateOffsetBoolSig((uint)joinOffset + 1, displayName + "Power Off", eSigIoMask.InputOutputSig); - defaultDisplayPowerOn.OutputSig.UserObject = new Action(b => { if (!b) display.PowerOff(); }); ; - display.PowerIsOnFeedback.LinkInputSig(defaultDisplayPowerOn.InputSig); - - // Current Source - var defaultDisplaySourceNone = FusionRoom.CreateOffsetBoolSig((uint)joinOffset + 8, displayName + "Source None", eSigIoMask.InputOutputSig); - defaultDisplaySourceNone.OutputSig.UserObject = new Action(b => { if (!b) (Room as EssentialsHuddleVtc1Room).RunRouteAction("roomOff", Room.SourceListKey); }); ; - } - } - } +namespace PepperDash.Essentials.Fusion +{ + public class EssentialsHuddleVtc1FusionController : EssentialsHuddleSpaceFusionSystemControllerBase + { + BooleanSigData CodecIsInCall; + + public EssentialsHuddleVtc1FusionController(EssentialsHuddleVtc1Room room, uint ipId) + : base(room, ipId) + { + + } + + /// + /// Called in base class constructor before RVI and GUID files are built + /// + protected override void ExecuteCustomSteps() + { + SetUpCodec(); + } + + /// + /// Creates a static asset for the codec and maps the joins to the main room symbol + /// + void SetUpCodec() + { + try + { + var codec = (Room as EssentialsHuddleVtc1Room).VideoCodec; + + if (codec == null) + { + Debug.Console(1, this, "Cannot link codec to Fusion because codec is null"); + return; + } + + codec.UsageTracker = new UsageTracking(codec); + codec.UsageTracker.UsageIsTracked = true; + codec.UsageTracker.DeviceUsageEnded += UsageTracker_DeviceUsageEnded; + + var codecPowerOnAction = new Action(b => { if (!b) codec.StandbyDeactivate(); }); + var codecPowerOffAction = new Action(b => { if (!b) codec.StandbyActivate(); }); + + // Map FusionRoom Attributes: + + // Codec volume + var codecVolume = FusionRoom.CreateOffsetUshortSig(50, "Volume - Fader01", eSigIoMask.InputOutputSig); + codecVolume.OutputSig.UserObject = new Action(b => (codec as IBasicVolumeWithFeedback).SetVolume(b)); + (codec as IBasicVolumeWithFeedback).VolumeLevelFeedback.LinkInputSig(codecVolume.InputSig); + + // In Call Status + CodecIsInCall = FusionRoom.CreateOffsetBoolSig(69, "Conf - VC 1 In Call", eSigIoMask.InputSigOnly); + codec.CallStatusChange += new EventHandler(codec_CallStatusChange); + + // Online status + if (codec is ICommunicationMonitor) + { + var c = codec as ICommunicationMonitor; + var codecOnline = FusionRoom.CreateOffsetBoolSig(122, "Online - VC 1", eSigIoMask.InputSigOnly); + codecOnline.InputSig.BoolValue = c.CommunicationMonitor.Status == MonitorStatus.IsOk; + c.CommunicationMonitor.StatusChange += (o, a) => + { + codecOnline.InputSig.BoolValue = a.Status == MonitorStatus.IsOk; + }; + Debug.Console(0, this, "Linking '{0}' communication monitor to Fusion '{1}'", codec.Key, "Online - VC 1"); + } + + // Codec IP Address + bool codecHasIpInfo = false; + var codecComm = codec.Communication; + + string codecIpAddress = string.Empty; + int codecIpPort = 0; + + StringSigData codecIpAddressSig; + StringSigData codecIpPortSig; + + if(codecComm is GenericSshClient) + { + codecIpAddress = (codecComm as GenericSshClient).Hostname; + codecIpPort = (codecComm as GenericSshClient).Port; + codecHasIpInfo = true; + } + else if (codecComm is GenericTcpIpClient) + { + codecIpAddress = (codecComm as GenericTcpIpClient).Hostname; + codecIpPort = (codecComm as GenericTcpIpClient).Port; + codecHasIpInfo = true; + } + + if (codecHasIpInfo) + { + codecIpAddressSig = FusionRoom.CreateOffsetStringSig(121, "IP Address - VC", eSigIoMask.InputSigOnly); + codecIpAddressSig.InputSig.StringValue = codecIpAddress; + + codecIpPortSig = FusionRoom.CreateOffsetStringSig(150, "IP Port - VC", eSigIoMask.InputSigOnly); + codecIpPortSig.InputSig.StringValue = codecIpPort.ToString(); + } + + var tempAsset = new FusionAsset(); + + var deviceConfig = ConfigReader.ConfigObject.Devices.FirstOrDefault(c => c.Key.Equals(codec.Key)); + + if (FusionStaticAssets.ContainsKey(deviceConfig.Uid)) + { + tempAsset = FusionStaticAssets[deviceConfig.Uid]; + } + else + { + // Create a new asset + tempAsset = new FusionAsset(FusionRoomGuids.GetNextAvailableAssetNumber(FusionRoom), codec.Name, "Codec", ""); + FusionStaticAssets.Add(deviceConfig.Uid, tempAsset); + } + + var codecAsset = FusionRoom.CreateStaticAsset(tempAsset.SlotNumber, tempAsset.Name, "Display", tempAsset.InstanceId); + codecAsset.PowerOn.OutputSig.UserObject = codecPowerOnAction; + codecAsset.PowerOff.OutputSig.UserObject = codecPowerOffAction; + codec.StandbyIsOnFeedback.LinkComplementInputSig(codecAsset.PowerOn.InputSig); + + // TODO: Map relevant attributes on asset symbol + + codecAsset.TrySetMakeModel(codec); + codecAsset.TryLinkAssetErrorToCommunication(codec); + } + catch (Exception e) + { + Debug.Console(1, this, "Error setting up codec in Fusion: {0}", e); + } + } + + void codec_CallStatusChange(object sender, PepperDash.Essentials.Devices.Common.Codec.CodecCallStatusItemChangeEventArgs e) + { + var codec = (Room as EssentialsHuddleVtc1Room).VideoCodec; + + CodecIsInCall.InputSig.BoolValue = codec.IsInCall; + } + + // These methods are overridden because they access the room class which is of a different type + + protected override void CreateSymbolAndBasicSigs(uint ipId) + { + Debug.Console(1, this, "Creating Fusion Room symbol with GUID: {0}", RoomGuid); + + FusionRoom = new FusionRoom(ipId, Global.ControlSystem, Room.Name, RoomGuid); + FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.Use(); + FusionRoom.ExtenderFusionRoomDataReservedSigs.Use(); + + FusionRoom.Register(); + + FusionRoom.FusionStateChange += FusionRoom_FusionStateChange; + + FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.DeviceExtenderSigChange += FusionRoomSchedule_DeviceExtenderSigChange; + FusionRoom.ExtenderFusionRoomDataReservedSigs.DeviceExtenderSigChange += ExtenderFusionRoomDataReservedSigs_DeviceExtenderSigChange; + FusionRoom.OnlineStatusChange += FusionRoom_OnlineStatusChange; + + CrestronConsole.AddNewConsoleCommand(RequestFullRoomSchedule, "FusReqRoomSchedule", "Requests schedule of the room for the next 24 hours", ConsoleAccessLevelEnum.AccessOperator); + CrestronConsole.AddNewConsoleCommand(ModifyMeetingEndTimeConsoleHelper, "FusReqRoomSchMod", "Ends or extends a meeting by the specified time", ConsoleAccessLevelEnum.AccessOperator); + CrestronConsole.AddNewConsoleCommand(CreateAsHocMeeting, "FusCreateMeeting", "Creates and Ad Hoc meeting for on hour or until the next meeting", ConsoleAccessLevelEnum.AccessOperator); + + // Room to fusion room + Room.OnFeedback.LinkInputSig(FusionRoom.SystemPowerOn.InputSig); + + // Moved to + CurrentRoomSourceNameSig = FusionRoom.CreateOffsetStringSig(84, "Display 1 - Current Source", eSigIoMask.InputSigOnly); + // Don't think we need to get current status of this as nothing should be alive yet. + (Room as EssentialsHuddleVtc1Room).CurrentSourceChange += Room_CurrentSourceInfoChange; + + + FusionRoom.SystemPowerOn.OutputSig.SetSigFalseAction((Room as EssentialsHuddleVtc1Room).PowerOnToDefaultOrLastSource); + FusionRoom.SystemPowerOff.OutputSig.SetSigFalseAction(() => (Room as EssentialsHuddleVtc1Room).RunRouteAction("roomOff", Room.SourceListKey)); + // NO!! room.RoomIsOn.LinkComplementInputSig(FusionRoom.SystemPowerOff.InputSig); + + + CrestronEnvironment.EthernetEventHandler += CrestronEnvironment_EthernetEventHandler; + } + + protected override void SetUpSources() + { + // Sources + var dict = ConfigReader.ConfigObject.GetSourceListForKey((Room as EssentialsHuddleVtc1Room).SourceListKey); + if (dict != null) + { + // NEW PROCESS: + // Make these lists and insert the fusion attributes by iterating these + var setTopBoxes = dict.Where(d => d.Value.SourceDevice is ISetTopBoxControls); + uint i = 1; + foreach (var kvp in setTopBoxes) + { + TryAddRouteActionSigs("Display 1 - Source TV " + i, 188 + i, kvp.Key, kvp.Value.SourceDevice); + i++; + if (i > 5) // We only have five spots + break; + } + + var discPlayers = dict.Where(d => d.Value.SourceDevice is IDiscPlayerControls); + i = 1; + foreach (var kvp in discPlayers) + { + TryAddRouteActionSigs("Display 1 - Source DVD " + i, 181 + i, kvp.Key, kvp.Value.SourceDevice); + i++; + if (i > 5) // We only have five spots + break; + } + + var laptops = dict.Where(d => d.Value.SourceDevice is Core.Devices.Laptop); + i = 1; + foreach (var kvp in laptops) + { + TryAddRouteActionSigs("Display 1 - Source Laptop " + i, 166 + i, kvp.Key, kvp.Value.SourceDevice); + i++; + if (i > 10) // We only have ten spots??? + break; + } + + foreach (var kvp in dict) + { + var usageDevice = kvp.Value.SourceDevice as IUsageTracking; + + if (usageDevice != null) + { + usageDevice.UsageTracker = new UsageTracking(usageDevice as Device); + usageDevice.UsageTracker.UsageIsTracked = true; + usageDevice.UsageTracker.DeviceUsageEnded += new EventHandler(UsageTracker_DeviceUsageEnded); + } + } + + } + else + { + Debug.Console(1, this, "WARNING: Config source list '{0}' not found for room '{1}'", + (Room as EssentialsHuddleVtc1Room).SourceListKey, Room.Key); + } + } + + protected override void SetUpDisplay() + { + try + { + //Setup Display Usage Monitoring + + var displays = DeviceManager.AllDevices.Where(d => d is DisplayBase); + + // Consider updating this in multiple display systems + + foreach (DisplayBase display in displays) + { + display.UsageTracker = new UsageTracking(display); + display.UsageTracker.UsageIsTracked = true; + display.UsageTracker.DeviceUsageEnded += new EventHandler(UsageTracker_DeviceUsageEnded); + } + + var defaultDisplay = (Room as EssentialsHuddleVtc1Room).DefaultDisplay as DisplayBase; + if (defaultDisplay == null) + { + Debug.Console(1, this, "Cannot link null display to Fusion because default display is null"); + return; + } + + var dispPowerOnAction = new Action(b => { if (!b) defaultDisplay.PowerOn(); }); + var dispPowerOffAction = new Action(b => { if (!b) defaultDisplay.PowerOff(); }); + + // Display to fusion room sigs + FusionRoom.DisplayPowerOn.OutputSig.UserObject = dispPowerOnAction; + FusionRoom.DisplayPowerOff.OutputSig.UserObject = dispPowerOffAction; + defaultDisplay.PowerIsOnFeedback.LinkInputSig(FusionRoom.DisplayPowerOn.InputSig); + if (defaultDisplay is IDisplayUsage) + (defaultDisplay as IDisplayUsage).LampHours.LinkInputSig(FusionRoom.DisplayUsage.InputSig); + + + + MapDisplayToRoomJoins(1, 158, defaultDisplay); + + + var deviceConfig = ConfigReader.ConfigObject.Devices.FirstOrDefault(d => d.Key.Equals(defaultDisplay.Key)); + + //Check for existing asset in GUIDs collection + + var tempAsset = new FusionAsset(); + + if (FusionStaticAssets.ContainsKey(deviceConfig.Uid)) + { + tempAsset = FusionStaticAssets[deviceConfig.Uid]; + } + else + { + // Create a new asset + tempAsset = new FusionAsset(FusionRoomGuids.GetNextAvailableAssetNumber(FusionRoom), defaultDisplay.Name, "Display", ""); + FusionStaticAssets.Add(deviceConfig.Uid, tempAsset); + } + + var dispAsset = FusionRoom.CreateStaticAsset(tempAsset.SlotNumber, tempAsset.Name, "Display", tempAsset.InstanceId); + dispAsset.PowerOn.OutputSig.UserObject = dispPowerOnAction; + dispAsset.PowerOff.OutputSig.UserObject = dispPowerOffAction; + defaultDisplay.PowerIsOnFeedback.LinkInputSig(dispAsset.PowerOn.InputSig); + // NO!! display.PowerIsOn.LinkComplementInputSig(dispAsset.PowerOff.InputSig); + // Use extension methods + dispAsset.TrySetMakeModel(defaultDisplay); + dispAsset.TryLinkAssetErrorToCommunication(defaultDisplay); + } + catch (Exception e) + { + Debug.Console(1, this, "Error setting up display in Fusion: {0}", e); + } + + } + + protected override void MapDisplayToRoomJoins(int displayIndex, int joinOffset, DisplayBase display) + { + string displayName = string.Format("Display {0} - ", displayIndex); + + + if (display == (Room as EssentialsHuddleVtc1Room).DefaultDisplay) + { + // Power on + var defaultDisplayPowerOn = FusionRoom.CreateOffsetBoolSig((uint)joinOffset, displayName + "Power On", eSigIoMask.InputOutputSig); + defaultDisplayPowerOn.OutputSig.UserObject = new Action(b => { if (!b) display.PowerOn(); }); + display.PowerIsOnFeedback.LinkInputSig(defaultDisplayPowerOn.InputSig); + + // Power Off + var defaultDisplayPowerOff = FusionRoom.CreateOffsetBoolSig((uint)joinOffset + 1, displayName + "Power Off", eSigIoMask.InputOutputSig); + defaultDisplayPowerOn.OutputSig.UserObject = new Action(b => { if (!b) display.PowerOff(); }); ; + display.PowerIsOnFeedback.LinkInputSig(defaultDisplayPowerOn.InputSig); + + // Current Source + var defaultDisplaySourceNone = FusionRoom.CreateOffsetBoolSig((uint)joinOffset + 8, displayName + "Source None", eSigIoMask.InputOutputSig); + defaultDisplaySourceNone.OutputSig.UserObject = new Action(b => { if (!b) (Room as EssentialsHuddleVtc1Room).RunRouteAction("roomOff", Room.SourceListKey); }); ; + } + } + } } \ No newline at end of file diff --git a/PepperDashEssentials/Room/Types/EssentialsHuddleVtc1Room.cs b/PepperDashEssentials/Room/Types/EssentialsHuddleVtc1Room.cs index 1440b82f..06ad71cd 100644 --- a/PepperDashEssentials/Room/Types/EssentialsHuddleVtc1Room.cs +++ b/PepperDashEssentials/Room/Types/EssentialsHuddleVtc1Room.cs @@ -50,6 +50,24 @@ namespace PepperDash.Essentials //************************ + public override string SourceListKey + { + get + { + return _SourceListKey; + } + set + { + _SourceListKey = value; + if(VideoCodec is IHasExternalSourceSwitching) + { + if((VideoCodec as IHasExternalSourceSwitching).ExternalSourceListEnabled) + { + SetCodecExternalSources(); + } + } + } + } protected override Func OnFeedbackFunc { @@ -206,9 +224,14 @@ namespace PepperDash.Essentials VideoCodec = DeviceManager.GetDeviceForKey(PropertiesConfig.VideoCodecKey) as PepperDash.Essentials.Devices.Common.VideoCodec.VideoCodecBase; + + if (VideoCodec == null) throw new ArgumentNullException("codec cannot be null"); + + //todo Do the thing here JTA + AudioCodec = DeviceManager.GetDeviceForKey(PropertiesConfig.AudioCodecKey) as PepperDash.Essentials.Devices.Common.AudioCodec.AudioCodecBase; if (AudioCodec == null) @@ -298,6 +321,7 @@ namespace PepperDash.Essentials VideoCodec.CallStatusChange += (o, a) => this.InCallFeedback.FireUpdate(); + VideoCodec.IsReadyChange += (o, a) => this.SetCodecExternalSources(); if (AudioCodec != null) AudioCodec.CallStatusChange += (o, a) => this.InCallFeedback.FireUpdate(); @@ -346,11 +370,10 @@ namespace PepperDash.Essentials this.SourceListKey = PropertiesConfig.SourceListKey; this.DefaultSourceItem = PropertiesConfig.DefaultSourceItem; this.DefaultVolume = (ushort)(PropertiesConfig.Volumes.Master.Level * 65535 / 100); - + return base.CustomActivate(); } - /// /// /// @@ -672,6 +695,30 @@ namespace PepperDash.Essentials (room as EssentialsHuddleSpaceRoom).RunRouteAction("roomOff"); } + private void SetCodecExternalSources() + { + + string codecTieLine = ""; + codecTieLine = ConfigReader.ConfigObject.TieLines.SingleOrDefault(x => x.DestinationKey == VideoCodec.Key).DestinationPort; + (VideoCodec as IHasExternalSourceSwitching).ClearExternalSources(); + (VideoCodec as IHasExternalSourceSwitching).RunRouteAction = RunRouteAction; + var srcList = ConfigReader.ConfigObject.SourceLists.SingleOrDefault(x => x.Key == SourceListKey).Value.OrderBy(kv => kv.Value.Order);; + + foreach (var kvp in srcList) + { + var srcConfig = kvp.Value; + Debug.Console(1, "**** KEY {0}", kvp.Key); + + if (kvp.Key != "codecOsd" && kvp.Key != "roomOff") + { + + (VideoCodec as IHasExternalSourceSwitching).AddExternalSource(codecTieLine, srcConfig.PreferredName); + + } + } + } + + #region IPrivacy Members diff --git a/PepperDashEssentials/UIDrivers/EssentialsHuddleVTC/EssentialsHuddleVtc1PanelAvFunctionsDriver.cs b/PepperDashEssentials/UIDrivers/EssentialsHuddleVTC/EssentialsHuddleVtc1PanelAvFunctionsDriver.cs index c74fd8e0..5a99f6e4 100644 --- a/PepperDashEssentials/UIDrivers/EssentialsHuddleVTC/EssentialsHuddleVtc1PanelAvFunctionsDriver.cs +++ b/PepperDashEssentials/UIDrivers/EssentialsHuddleVTC/EssentialsHuddleVtc1PanelAvFunctionsDriver.cs @@ -1047,12 +1047,15 @@ namespace PepperDash.Essentials /// void SetupSourceList() { - + var inCall = CurrentRoom.InCallFeedback.BoolValue; var config = ConfigReader.ConfigObject.SourceLists; + + if (config.ContainsKey(_CurrentRoom.SourceListKey)) { var srcList = config[_CurrentRoom.SourceListKey].OrderBy(kv => kv.Value.Order); + // Setup sources list SourceStagingSrl.Clear(); @@ -1076,6 +1079,8 @@ namespace PepperDash.Essentials b => { if (!b) UiSelectSource(routeKey); }); SourceStagingSrl.AddItem(item); // add to the SRL item.RegisterForSourceChange(_CurrentRoom); + Debug.Console(1, "**** KEY {0}", kvp.Key); + } SourceStagingSrl.Count = (ushort)(i - 1); } diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/EssentialsRoomBase.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/EssentialsRoomBase.cs index 624d072f..4cf36470 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/EssentialsRoomBase.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/EssentialsRoomBase.cs @@ -51,7 +51,19 @@ namespace PepperDash.Essentials.Core /// /// The config name of the source list /// - public string SourceListKey { get; set; } + /// + protected string _SourceListKey; + public virtual string SourceListKey { + get + { + return _SourceListKey; + } + set + { + _SourceListKey = value; + + } + } /// /// Timer used for informing the UIs of a shutdown diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/IHasExternalSourceSwitching.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/IHasExternalSourceSwitching.cs new file mode 100644 index 00000000..1870e756 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/IHasExternalSourceSwitching.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using PepperDash.Essentials.Core; +namespace PepperDash.Essentials.Devices.Common.Codec +{ + public interface IHasExternalSourceSwitching + { + bool ExternalSourceListEnabled { get; } + void AddExternalSource(string connectorId, string name); + void ClearExternalSources(); + Action RunRouteAction { set;} + } + +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Essentials Devices Common.csproj b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Essentials Devices Common.csproj index e33e54fd..6055f9ab 100644 --- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Essentials Devices Common.csproj +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Essentials Devices Common.csproj @@ -112,6 +112,7 @@ + diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoSparkCodec.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoSparkCodec.cs index b870fa78..b90ab896 100644 --- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoSparkCodec.cs +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoSparkCodec.cs @@ -15,16 +15,16 @@ using PepperDash.Essentials.Core.Config; using PepperDash.Essentials.Core.Routing; using PepperDash.Essentials.Devices.Common.Cameras; using PepperDash.Essentials.Devices.Common.Codec; -using PepperDash.Essentials.Core; using PepperDash.Essentials.Devices.Common.VideoCodec; namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco { enum eCommandType { SessionStart, SessionEnd, Command, GetStatus, GetConfiguration }; + public enum eExternalSourceType {camera, desktop, document_camera, mediaplayer, PC, whiteboard, other} public class CiscoSparkCodec : VideoCodecBase, IHasCallHistory, IHasCallFavorites, IHasDirectory, IHasScheduleAwareness, IOccupancyStatusProvider, IHasCodecLayouts, IHasCodecSelfView, - ICommunicationMonitor, IRouting, IHasCodecCameras, IHasCameraAutoMode, IHasCodecRoomPresets + ICommunicationMonitor, IRouting, IHasCodecCameras, IHasCameraAutoMode, IHasCodecRoomPresets, IHasExternalSourceSwitching { public event EventHandler DirectoryResultReturned; @@ -348,6 +348,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco CallHistory = new CodecCallHistory(); + if (props.Favorites != null) { CallFavorites = new CodecCallFavorites(); @@ -398,6 +399,11 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco SetUpCameras(); CreateOsdSource(); + + if (props.ExternalSourceListEnabled != null) + { + ExternalSourceListEnabled = props.ExternalSourceListEnabled; + } } /// @@ -472,7 +478,8 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco prefix + "/Bookings" + Delimiter + prefix + "/Event/CallDisconnect" + Delimiter + prefix + "/Event/Bookings" + Delimiter + - prefix + "/Event/CameraPresetListUpdated" + Delimiter; + prefix + "/Event/CameraPresetListUpdated" + Delimiter + + prefix + "/Event/UserInterface/Presentation/ExternalSource/Selected/SourceIdentifier" + Delimiter; return base.CustomActivate(); } @@ -629,6 +636,11 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco } } } + //TODO JTA FInish Parsing for External Sources + if (args.Text == "ExternalSource") + { + RunRouteAction("", ""); + } } @@ -1802,9 +1814,57 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco SendText(string.Format("xCommand Call FarEndControl RoomPreset Activate CallId: {0} PresetId: {1}", GetCallId(), preset)); } - } - /// + #region IHasExternalSourceSwitching Members + + /// + /// + /// + public bool ExternalSourceListEnabled + { + get; + private set; + } + + public void AddExternalSource(string connectorId, string name) + { + int id = 2; + if (connectorId.ToLower() == "hdmiin3") + { + id = 3; + } + SendText(string.Format("xCommand UserInterface Presentation ExternalSource Add ConnectorId: {0} SourceIdentifier: \"{1}\" Name: \"{2}\" Type: desktop", id, name, name)); + Debug.Console(2, this, "Adding ExternalSource {0} {1}", connectorId, name); + + } + + /// + /// + /// + public void ClearExternalSources() + { + SendText("xCommand UserInterface Presentation ExternalSource RemoveAll"); + + } + + + public Action RunRouteAction { private get; set; } + + + + + + + #endregion + #region ExternalDevices + + + + #endregion + } + + + /// /// Represents a codec command that might need to have a friendly label applied for UI feedback purposes /// public class CodecCommandWithLabel diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoSparkCodecPropertiesConfig.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoSparkCodecPropertiesConfig.cs index 521b9e5c..2f335780 100644 --- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoSparkCodecPropertiesConfig.cs +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoSparkCodecPropertiesConfig.cs @@ -1,47 +1,50 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Crestron.SimplSharp; - -using PepperDash.Core; -using PepperDash.Essentials.Core; - -using Newtonsoft.Json; - -namespace PepperDash.Essentials.Devices.Common.Codec -{ - public class CiscoSparkCodecPropertiesConfig - { - [JsonProperty("communicationMonitorProperties")] - public CommunicationMonitorConfig CommunicationMonitorProperties { get; set; } - - [JsonProperty("favorites")] - public List Favorites { get; set; } - - /// - /// Valid values: "Local" or "Corporate" - /// - [JsonProperty("phonebookMode")] - public string PhonebookMode { get; set; } - - [JsonProperty("showSelfViewByDefault")] - public bool ShowSelfViewByDefault { get; set; } - - [JsonProperty("sharing")] - public SharingProperties Sharing { get; set; } - - /// - /// Optionsal property to set the limit of any phonebook queries for directory or searching - /// - [JsonProperty("phonebookResultsLimit")] - public uint PhonebookResultsLimit { get; set; } - - } - - public class SharingProperties - { - [JsonProperty("autoShareContentWhileInCall")] - public bool AutoShareContentWhileInCall { get; set; } - } +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using PepperDash.Core; +using PepperDash.Essentials.Core; + +using Newtonsoft.Json; + +namespace PepperDash.Essentials.Devices.Common.Codec +{ + public class CiscoSparkCodecPropertiesConfig + { + [JsonProperty("communicationMonitorProperties")] + public CommunicationMonitorConfig CommunicationMonitorProperties { get; set; } + + [JsonProperty("favorites")] + public List Favorites { get; set; } + + /// + /// Valid values: "Local" or "Corporate" + /// + [JsonProperty("phonebookMode")] + public string PhonebookMode { get; set; } + + [JsonProperty("showSelfViewByDefault")] + public bool ShowSelfViewByDefault { get; set; } + + [JsonProperty("sharing")] + public SharingProperties Sharing { get; set; } + + [JsonProperty("externalSourceListEnabled")] + public bool ExternalSourceListEnabled { get; set; } + + /// + /// Optionsal property to set the limit of any phonebook queries for directory or searching + /// + [JsonProperty("phonebookResultsLimit")] + public uint PhonebookResultsLimit { get; set; } + + } + + public class SharingProperties + { + [JsonProperty("autoShareContentWhileInCall")] + public bool AutoShareContentWhileInCall { get; set; } + } } \ No newline at end of file From f0d10fb1f962f0ab3cdb8351781b55fcc4d153b7 Mon Sep 17 00:00:00 2001 From: Jason Alborough Date: Mon, 17 Aug 2020 14:52:49 -0400 Subject: [PATCH 2/4] Finalized IHasExternalSourceSwitching adding method for setting the ExternalSource State Adds arguments for External Source type it the AddExternalSource() Adds subscription and parsing for External Source events --- .../Room/Types/EssentialsHuddleVtc1Room.cs | 9 +- .../Devices/IrOutputPortController.cs | 240 +++++++++--------- .../Codec/IHasExternalSourceSwitching.cs | 5 +- .../VideoCodec/CiscoCodec/CiscoSparkCodec.cs | 44 +++- .../VideoCodec/CiscoCodec/xEvent.cs | 31 ++- 5 files changed, 193 insertions(+), 136 deletions(-) diff --git a/PepperDashEssentials/Room/Types/EssentialsHuddleVtc1Room.cs b/PepperDashEssentials/Room/Types/EssentialsHuddleVtc1Room.cs index 06ad71cd..0c048ed1 100644 --- a/PepperDashEssentials/Room/Types/EssentialsHuddleVtc1Room.cs +++ b/PepperDashEssentials/Room/Types/EssentialsHuddleVtc1Room.cs @@ -695,6 +695,10 @@ namespace PepperDash.Essentials (room as EssentialsHuddleSpaceRoom).RunRouteAction("roomOff"); } + + /// + /// Setup the external sources for the Cisco Touch 10 devices that support IHasExternalSourceSwitch + /// private void SetCodecExternalSources() { @@ -707,12 +711,13 @@ namespace PepperDash.Essentials foreach (var kvp in srcList) { var srcConfig = kvp.Value; - Debug.Console(1, "**** KEY {0}", kvp.Key); if (kvp.Key != "codecOsd" && kvp.Key != "roomOff") { - (VideoCodec as IHasExternalSourceSwitching).AddExternalSource(codecTieLine, srcConfig.PreferredName); + (VideoCodec as IHasExternalSourceSwitching).AddExternalSource(codecTieLine, kvp.Key, srcConfig.PreferredName, PepperDash.Essentials.Devices.Common.VideoCodec.Cisco.eExternalSourceType.desktop); + (VideoCodec as IHasExternalSourceSwitching).SetExternalSourceState(kvp.Key, PepperDash.Essentials.Devices.Common.VideoCodec.Cisco.eExternalSourceMode.Ready); + } } diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/IrOutputPortController.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/IrOutputPortController.cs index ecfca4f8..83241e79 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/IrOutputPortController.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/IrOutputPortController.cs @@ -1,45 +1,45 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Crestron.SimplSharp; -using Crestron.SimplSharpPro; +using System; +using System.Collections.Generic; +using System.Linq; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; using Crestron.SimplSharpPro.DeviceSupport; using Newtonsoft.Json.Linq; -using PepperDash.Essentials.Core.Config; - - -using PepperDash.Core; - -namespace PepperDash.Essentials.Core -{ - - /// - /// IR port wrapper. May act standalone - /// - public class IrOutputPortController : Device - { - uint IrPortUid; - IROutputPort IrPort; - - public ushort StandardIrPulseTime { get; set; } - public string DriverFilepath { get; private set; } - public bool DriverIsLoaded { get; private set; } - - /// - /// Constructor for IrDevice base class. If a null port is provided, this class will - /// still function without trying to talk to a port. - /// - public IrOutputPortController(string key, IROutputPort port, string irDriverFilepath) - : base(key) - { - //if (port == null) throw new ArgumentNullException("port"); - IrPort = port; - if (port == null) - { - Debug.Console(0, this, "WARNING No valid IR Port assigned to controller. IR will not function"); - return; - } - LoadDriver(irDriverFilepath); +using PepperDash.Essentials.Core.Config; + + +using PepperDash.Core; + +namespace PepperDash.Essentials.Core +{ + + /// + /// IR port wrapper. May act standalone + /// + public class IrOutputPortController : Device + { + uint IrPortUid; + IROutputPort IrPort; + + public ushort StandardIrPulseTime { get; set; } + public string DriverFilepath { get; private set; } + public bool DriverIsLoaded { get; private set; } + + /// + /// Constructor for IrDevice base class. If a null port is provided, this class will + /// still function without trying to talk to a port. + /// + public IrOutputPortController(string key, IROutputPort port, string irDriverFilepath) + : base(key) + { + //if (port == null) throw new ArgumentNullException("port"); + IrPort = port; + if (port == null) + { + Debug.Console(0, this, "WARNING No valid IR Port assigned to controller. IR will not function"); + return; + } + LoadDriver(irDriverFilepath); } public IrOutputPortController(string key, Func postActivationFunc, @@ -54,85 +54,85 @@ namespace PepperDash.Essentials.Core - /// - /// Loads the IR driver at path - /// - /// - public void LoadDriver(string path) - { - if (string.IsNullOrEmpty(path)) path = DriverFilepath; - try - { - IrPortUid = IrPort.LoadIRDriver(path); - DriverFilepath = path; - StandardIrPulseTime = 200; - DriverIsLoaded = true; - } - catch - { - DriverIsLoaded = false; - var message = string.Format("WARNING IR Driver '{0}' failed to load", path); - Debug.Console(0, this, message); - ErrorLog.Error(message); - } - } - - - /// - /// Starts and stops IR command on driver. Safe for missing commands - /// - public virtual void PressRelease(string command, bool state) - { - Debug.Console(2, this, "IR:'{0}'={1}", command, state); - if (IrPort == null) - { - Debug.Console(2, this, "WARNING No IR Port assigned to controller"); - return; - } - if (!DriverIsLoaded) - { - Debug.Console(2, this, "WARNING IR driver is not loaded"); - return; - } - if (state) - { - if (IrPort.IsIRCommandAvailable(IrPortUid, command)) - IrPort.Press(IrPortUid, command); - else - NoIrCommandError(command); - } - else - IrPort.Release(); - } - - /// - /// Pulses a command on driver. Safe for missing commands - /// - public virtual void Pulse(string command, ushort time) - { - if (IrPort == null) - { - Debug.Console(2, this, "WARNING No IR Port assigned to controller"); - return; - } - if (!DriverIsLoaded) - { - Debug.Console(2, this, "WARNING IR driver is not loaded"); - return; - } - if (IrPort.IsIRCommandAvailable(IrPortUid, command)) - IrPort.PressAndRelease(IrPortUid, command, time); - else - NoIrCommandError(command); - } - - /// - /// Notifies the console when a bad command is used. - /// - protected void NoIrCommandError(string command) - { - Debug.Console(2, this, "Device {0}: IR Driver {1} does not contain command {2}", - Key, IrPort.IRDriverFileNameByIRDriverId(IrPortUid), command); + /// + /// Loads the IR driver at path + /// + /// + public void LoadDriver(string path) + { + if (string.IsNullOrEmpty(path)) path = DriverFilepath; + try + { + IrPortUid = IrPort.LoadIRDriver(path); + DriverFilepath = path; + StandardIrPulseTime = 200; + DriverIsLoaded = true; + } + catch + { + DriverIsLoaded = false; + var message = string.Format("WARNING IR Driver '{0}' failed to load", path); + Debug.Console(0, this, message); + ErrorLog.Error(message); + } } - } + + + /// + /// Starts and stops IR command on driver. Safe for missing commands + /// + public virtual void PressRelease(string command, bool state) + { + Debug.Console(2, this, "IR:'{0}'={1}", command, state); + if (IrPort == null) + { + Debug.Console(2, this, "WARNING No IR Port assigned to controller"); + return; + } + if (!DriverIsLoaded) + { + Debug.Console(2, this, "WARNING IR driver is not loaded"); + return; + } + if (state) + { + if (IrPort.IsIRCommandAvailable(IrPortUid, command)) + IrPort.Press(IrPortUid, command); + else + NoIrCommandError(command); + } + else + IrPort.Release(); + } + + /// + /// Pulses a command on driver. Safe for missing commands + /// + public virtual void Pulse(string command, ushort time) + { + if (IrPort == null) + { + Debug.Console(2, this, "WARNING No IR Port assigned to controller"); + return; + } + if (!DriverIsLoaded) + { + Debug.Console(2, this, "WARNING IR driver is not loaded"); + return; + } + if (IrPort.IsIRCommandAvailable(IrPortUid, command)) + IrPort.PressAndRelease(IrPortUid, command, time); + else + NoIrCommandError(command); + } + + /// + /// Notifies the console when a bad command is used. + /// + protected void NoIrCommandError(string command) + { + Debug.Console(2, this, "Device {0}: IR Driver {1} does not contain command {2}", + Key, IrPort.IRDriverFileNameByIRDriverId(IrPortUid), command); + } + } } \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/IHasExternalSourceSwitching.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/IHasExternalSourceSwitching.cs index 1870e756..d86f628e 100644 --- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/IHasExternalSourceSwitching.cs +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/IHasExternalSourceSwitching.cs @@ -4,12 +4,15 @@ using System.Linq; using System.Text; using Crestron.SimplSharp; using PepperDash.Essentials.Core; +using PepperDash.Essentials.Devices.Common.VideoCodec.Cisco; + namespace PepperDash.Essentials.Devices.Common.Codec { public interface IHasExternalSourceSwitching { bool ExternalSourceListEnabled { get; } - void AddExternalSource(string connectorId, string name); + void AddExternalSource(string connectorId, string key, string name, eExternalSourceType type); + void SetExternalSourceState(string key, eExternalSourceMode mode); void ClearExternalSources(); Action RunRouteAction { set;} } diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoSparkCodec.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoSparkCodec.cs index b90ab896..108aab0b 100644 --- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoSparkCodec.cs +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoSparkCodec.cs @@ -21,6 +21,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco { enum eCommandType { SessionStart, SessionEnd, Command, GetStatus, GetConfiguration }; public enum eExternalSourceType {camera, desktop, document_camera, mediaplayer, PC, whiteboard, other} + public enum eExternalSourceMode {Ready, NotReady, Hiddon, Error} public class CiscoSparkCodec : VideoCodecBase, IHasCallHistory, IHasCallFavorites, IHasDirectory, IHasScheduleAwareness, IOccupancyStatusProvider, IHasCodecLayouts, IHasCodecSelfView, @@ -636,11 +637,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco } } } - //TODO JTA FInish Parsing for External Sources - if (args.Text == "ExternalSource") - { - RunRouteAction("", ""); - } + } @@ -865,6 +862,14 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco { GetBookings(null); } + + else if (response.IndexOf("\"UserInterface\":{") > -1 || response.IndexOf("\"UserInterface\": {") > -1) // External Source Trigger + { + CiscoCodecEvents.RootObject eventReceived = new CiscoCodecEvents.RootObject(); + JsonConvert.PopulateObject(response, eventReceived); + Debug.Console(2, this, "*** Got an External Source Selection {0} {1}", eventReceived, eventReceived.Event.UserInterface, eventReceived.Event.UserInterface.Presentation.ExternalSource.Selected.SourceIdentifier.Value); + RunRouteAction(eventReceived.Event.UserInterface.Presentation.ExternalSource.Selected.SourceIdentifier.Value, null); + } } else if (response.IndexOf("\"CommandResponse\":{") > -1 || response.IndexOf("\"CommandResponse\": {") > -1) { @@ -1818,7 +1823,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco #region IHasExternalSourceSwitching Members /// - /// + /// Weather the Cisco supports External Source Lists or not /// public bool ExternalSourceListEnabled { @@ -1826,20 +1831,37 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco private set; } - public void AddExternalSource(string connectorId, string name) + /// + /// Adds an external source to the Cisco + /// + /// + /// + /// + public void AddExternalSource(string connectorId, string key, string name, eExternalSourceType type) { int id = 2; if (connectorId.ToLower() == "hdmiin3") { id = 3; } - SendText(string.Format("xCommand UserInterface Presentation ExternalSource Add ConnectorId: {0} SourceIdentifier: \"{1}\" Name: \"{2}\" Type: desktop", id, name, name)); + SendText(string.Format("xCommand UserInterface Presentation ExternalSource Add ConnectorId: {0} SourceIdentifier: \"{1}\" Name: \"{2}\" Type: {3}", id, key, name, type.ToString())); + // SendText(string.Format("xCommand UserInterface Presentation ExternalSource State Set SourceIdentifier: \"{0}\" State: Ready", key)); Debug.Console(2, this, "Adding ExternalSource {0} {1}", connectorId, name); } + /// - /// + /// Sets the state of the External Source + /// + /// + /// + public void SetExternalSourceState(string key, eExternalSourceMode mode) + { + SendText(string.Format("xCommand UserInterface Presentation ExternalSource State Set SourceIdentifier: \"{0}\" State: {1}", key, mode.ToString())); + } + /// + /// Clears all external sources on the codec /// public void ClearExternalSources() { @@ -1847,7 +1869,9 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco } - + /// + /// Action that will run when the External Source is selected. + /// public Action RunRouteAction { private get; set; } diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/xEvent.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/xEvent.cs index 342a8848..23594ce2 100644 --- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/xEvent.cs +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/xEvent.cs @@ -126,11 +126,36 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco public Encryption Encryption { get; set; } public RequestedURI RequestedURI { get; set; } public PeopleCountAverage PeopleCountAverage { get; set; } - } - + } + public class UserInterface + { + public string id { get; set; } + public Presentation Presentation { get; set; } + } + public class Presentation + { + public string id { get; set; } + public ExternalSource ExternalSource { get; set; } + } + public class ExternalSource + { + public string id { get; set; } + public Selected Selected { get; set; } + } + public class Selected + { + public string id { get; set; } + public SourceIdentifier SourceIdentifier { get; set; } + } + public class SourceIdentifier + { + public string id { get; set; } + public string Value { get; set; } + } public class Event { - public CallDisconnect CallDisconnect { get; set; } + public CallDisconnect CallDisconnect { get; set; } + public UserInterface UserInterface { get; set; } } public class RootObject From 4e2683f8fca462ddc0ff5a0ecb04fcb2fb8930f7 Mon Sep 17 00:00:00 2001 From: Jason Alborough Date: Mon, 17 Aug 2020 14:57:33 -0400 Subject: [PATCH 3/4] Removes ToDo's --- PepperDashEssentials/Room/Types/EssentialsHuddleVtc1Room.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/PepperDashEssentials/Room/Types/EssentialsHuddleVtc1Room.cs b/PepperDashEssentials/Room/Types/EssentialsHuddleVtc1Room.cs index 0c048ed1..99278b32 100644 --- a/PepperDashEssentials/Room/Types/EssentialsHuddleVtc1Room.cs +++ b/PepperDashEssentials/Room/Types/EssentialsHuddleVtc1Room.cs @@ -228,9 +228,6 @@ namespace PepperDash.Essentials if (VideoCodec == null) throw new ArgumentNullException("codec cannot be null"); - - - //todo Do the thing here JTA AudioCodec = DeviceManager.GetDeviceForKey(PropertiesConfig.AudioCodecKey) as PepperDash.Essentials.Devices.Common.AudioCodec.AudioCodecBase; From 67f0ae92ef06860c6d4036862cfcff8b2b8e84f2 Mon Sep 17 00:00:00 2001 From: Jason Alborough Date: Tue, 18 Aug 2020 10:00:53 -0400 Subject: [PATCH 4/4] Adds null check before running RunRouteAction event in Cisco Spark class. Fixes spelling error and other minor cleanup. --- .../Room/Types/EssentialsHuddleVtc1Room.cs | 38 ++++++++++--------- .../VideoCodec/CiscoCodec/CiscoSparkCodec.cs | 8 +++- 2 files changed, 26 insertions(+), 20 deletions(-) diff --git a/PepperDashEssentials/Room/Types/EssentialsHuddleVtc1Room.cs b/PepperDashEssentials/Room/Types/EssentialsHuddleVtc1Room.cs index 99278b32..ca137a27 100644 --- a/PepperDashEssentials/Room/Types/EssentialsHuddleVtc1Room.cs +++ b/PepperDashEssentials/Room/Types/EssentialsHuddleVtc1Room.cs @@ -59,13 +59,8 @@ namespace PepperDash.Essentials set { _SourceListKey = value; - if(VideoCodec is IHasExternalSourceSwitching) - { - if((VideoCodec as IHasExternalSourceSwitching).ExternalSourceListEnabled) - { - SetCodecExternalSources(); - } - } + SetCodecExternalSources(); + } } @@ -698,28 +693,35 @@ namespace PepperDash.Essentials /// private void SetCodecExternalSources() { - - string codecTieLine = ""; - codecTieLine = ConfigReader.ConfigObject.TieLines.SingleOrDefault(x => x.DestinationKey == VideoCodec.Key).DestinationPort; - (VideoCodec as IHasExternalSourceSwitching).ClearExternalSources(); - (VideoCodec as IHasExternalSourceSwitching).RunRouteAction = RunRouteAction; - var srcList = ConfigReader.ConfigObject.SourceLists.SingleOrDefault(x => x.Key == SourceListKey).Value.OrderBy(kv => kv.Value.Order);; + var videoCodecWithExternalSwitching = VideoCodec as IHasExternalSourceSwitching; + + if (videoCodecWithExternalSwitching == null) + { + return; + } + else + { + string codecTieLine = ""; + codecTieLine = ConfigReader.ConfigObject.TieLines.SingleOrDefault(x => x.DestinationKey == VideoCodec.Key).DestinationPort; + videoCodecWithExternalSwitching.ClearExternalSources(); + videoCodecWithExternalSwitching.RunRouteAction = RunRouteAction; + var srcList = ConfigReader.ConfigObject.SourceLists.SingleOrDefault(x => x.Key == SourceListKey).Value.OrderBy(kv => kv.Value.Order); ; foreach (var kvp in srcList) { var srcConfig = kvp.Value; - - if (kvp.Key != "codecOsd" && kvp.Key != "roomOff") + + if (kvp.Key != DefaultCodecRouteString && kvp.Key != "roomOff") { - (VideoCodec as IHasExternalSourceSwitching).AddExternalSource(codecTieLine, kvp.Key, srcConfig.PreferredName, PepperDash.Essentials.Devices.Common.VideoCodec.Cisco.eExternalSourceType.desktop); - (VideoCodec as IHasExternalSourceSwitching).SetExternalSourceState(kvp.Key, PepperDash.Essentials.Devices.Common.VideoCodec.Cisco.eExternalSourceMode.Ready); + videoCodecWithExternalSwitching.AddExternalSource(codecTieLine, kvp.Key, srcConfig.PreferredName, PepperDash.Essentials.Devices.Common.VideoCodec.Cisco.eExternalSourceType.desktop); + videoCodecWithExternalSwitching.SetExternalSourceState(kvp.Key, PepperDash.Essentials.Devices.Common.VideoCodec.Cisco.eExternalSourceMode.Ready); } } } - + } #region IPrivacy Members diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoSparkCodec.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoSparkCodec.cs index 108aab0b..9759cfdd 100644 --- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoSparkCodec.cs +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoSparkCodec.cs @@ -21,7 +21,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco { enum eCommandType { SessionStart, SessionEnd, Command, GetStatus, GetConfiguration }; public enum eExternalSourceType {camera, desktop, document_camera, mediaplayer, PC, whiteboard, other} - public enum eExternalSourceMode {Ready, NotReady, Hiddon, Error} + public enum eExternalSourceMode {Ready, NotReady, Hidden, Error} public class CiscoSparkCodec : VideoCodecBase, IHasCallHistory, IHasCallFavorites, IHasDirectory, IHasScheduleAwareness, IOccupancyStatusProvider, IHasCodecLayouts, IHasCodecSelfView, @@ -868,7 +868,11 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco CiscoCodecEvents.RootObject eventReceived = new CiscoCodecEvents.RootObject(); JsonConvert.PopulateObject(response, eventReceived); Debug.Console(2, this, "*** Got an External Source Selection {0} {1}", eventReceived, eventReceived.Event.UserInterface, eventReceived.Event.UserInterface.Presentation.ExternalSource.Selected.SourceIdentifier.Value); - RunRouteAction(eventReceived.Event.UserInterface.Presentation.ExternalSource.Selected.SourceIdentifier.Value, null); + + if (RunRouteAction != null) + { + RunRouteAction(eventReceived.Event.UserInterface.Presentation.ExternalSource.Selected.SourceIdentifier.Value, null); + } } } else if (response.IndexOf("\"CommandResponse\":{") > -1 || response.IndexOf("\"CommandResponse\": {") > -1)