diff --git a/.gitignore b/.gitignore index 94b7d400..739a60ab 100644 --- a/.gitignore +++ b/.gitignore @@ -389,3 +389,4 @@ MigrationBackup/ # Fody - auto-generated XML schema FodyWeavers.xsd essentials-framework/Essentials Interfaces/PepperDash_Essentials_Interfaces/PepperDash_Essentials_Interfaces.csproj +.DS_Store diff --git a/PepperDashEssentials/ControlSystem.cs b/PepperDashEssentials/ControlSystem.cs index f24cb828..1bafbbac 100644 --- a/PepperDashEssentials/ControlSystem.cs +++ b/PepperDashEssentials/ControlSystem.cs @@ -401,20 +401,21 @@ namespace PepperDash.Essentials } else if (this.ControllerPrompt.IndexOf("mpc3", StringComparison.OrdinalIgnoreCase) > -1) { - Debug.Console(2, "MPC3 processor type detected. Adding Mpc3TouchpanelController."); + Debug.Console(2, "MPC3 processor type detected. Adding Mpc3TouchpanelController."); - var butToken = devConf.Properties["buttons"]; - if (butToken != null) - { - var buttons = butToken.ToObject>(); - var tpController = new Essentials.Core.Touchpanels.Mpc3TouchpanelController(devConf.Key, devConf.Name, Global.ControlSystem, buttons); - DeviceManager.AddDevice(tpController); - } - else - { - Debug.Console(0, Debug.ErrorLogLevel.Error, "Error: Unable to deserialize buttons collection for device: {0}", devConf.Key); - } - + var butToken = devConf.Properties["buttons"]; + if (butToken == null) + { + Debug.Console(0, Debug.ErrorLogLevel.Error, + "Error: Unable to deserialize buttons collection for device: {0}", devConf.Key); + continue; + } + + var buttons = butToken.ToObject>(); + var tpController = new Core.Touchpanels.Mpc3TouchpanelController( + string.Format("{0}-keypadButtons", devConf.Key), devConf.Name, Global.ControlSystem, buttons); + + DeviceManager.AddDevice(tpController); } else { diff --git a/PepperDashEssentials/PepperDashEssentials.csproj b/PepperDashEssentials/PepperDashEssentials.csproj index e4a0cff6..66a20f72 100644 --- a/PepperDashEssentials/PepperDashEssentials.csproj +++ b/PepperDashEssentials/PepperDashEssentials.csproj @@ -71,7 +71,7 @@ ..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.UI.dll - + False ..\packages\PepperDashCore\lib\net35\PepperDash_Core.dll diff --git a/PepperDashEssentials/Room/Types/EssentialsCombinedHuddleVtc1Room.cs b/PepperDashEssentials/Room/Types/EssentialsCombinedHuddleVtc1Room.cs index 50186d0b..cbe1b579 100644 --- a/PepperDashEssentials/Room/Types/EssentialsCombinedHuddleVtc1Room.cs +++ b/PepperDashEssentials/Room/Types/EssentialsCombinedHuddleVtc1Room.cs @@ -226,66 +226,66 @@ namespace PepperDash.Essentials } } - void Initialize() + public override void Initialize() { try { - if (DefaultAudioDevice is IBasicVolumeControls) - DefaultVolumeControls = DefaultAudioDevice as IBasicVolumeControls; - else if (DefaultAudioDevice is IHasVolumeDevice) - DefaultVolumeControls = (DefaultAudioDevice as IHasVolumeDevice).VolumeDevice; - CurrentVolumeControls = DefaultVolumeControls; + //if (DefaultAudioDevice is IBasicVolumeControls) + // DefaultVolumeControls = DefaultAudioDevice as IBasicVolumeControls; + //else if (DefaultAudioDevice is IHasVolumeDevice) + // DefaultVolumeControls = (DefaultAudioDevice as IHasVolumeDevice).VolumeDevice; + //CurrentVolumeControls = DefaultVolumeControls; - // Combines call feedback from both codecs if available - InCallFeedback = new BoolFeedback(() => - { - bool inAudioCall = false; - bool inVideoCall = false; + //// Combines call feedback from both codecs if available + //InCallFeedback = new BoolFeedback(() => + //{ + // bool inAudioCall = false; + // bool inVideoCall = false; - if (AudioCodec != null) - inAudioCall = AudioCodec.IsInCall; + // if (AudioCodec != null) + // inAudioCall = AudioCodec.IsInCall; - if (VideoCodec != null) - inVideoCall = VideoCodec.IsInCall; + // if (VideoCodec != null) + // inVideoCall = VideoCodec.IsInCall; - if (inAudioCall || inVideoCall) - return true; - else - return false; - }); + // if (inAudioCall || inVideoCall) + // return true; + // else + // return false; + //}); - SetupDisplays(); + //SetupDisplays(); - // Get Microphone Privacy object, if any MUST HAPPEN AFTER setting InCallFeedback - this.MicrophonePrivacy = EssentialsRoomConfigHelper.GetMicrophonePrivacy(PropertiesConfig, this); + //// Get Microphone Privacy object, if any MUST HAPPEN AFTER setting InCallFeedback + //this.MicrophonePrivacy = EssentialsRoomConfigHelper.GetMicrophonePrivacy(PropertiesConfig, this); - Debug.Console(2, this, "Microphone Privacy Config evaluated."); + //Debug.Console(2, this, "Microphone Privacy Config evaluated."); - // Get emergency object, if any - this.Emergency = EssentialsRoomConfigHelper.GetEmergency(PropertiesConfig, this); + //// Get emergency object, if any + //this.Emergency = EssentialsRoomConfigHelper.GetEmergency(PropertiesConfig, this); - Debug.Console(2, this, "Emergency Config evaluated."); + //Debug.Console(2, this, "Emergency Config evaluated."); - VideoCodec.CallStatusChange += (o, a) => this.InCallFeedback.FireUpdate(); - VideoCodec.IsReadyChange += (o, a) => { this.SetCodecExternalSources(); SetCodecBranding(); }; + //VideoCodec.CallStatusChange += (o, a) => this.InCallFeedback.FireUpdate(); + //VideoCodec.IsReadyChange += (o, a) => { this.SetCodecExternalSources(); SetCodecBranding(); }; - if (AudioCodec != null) - AudioCodec.CallStatusChange += (o, a) => this.InCallFeedback.FireUpdate(); + //if (AudioCodec != null) + // AudioCodec.CallStatusChange += (o, a) => this.InCallFeedback.FireUpdate(); - IsSharingFeedback = new BoolFeedback(() => VideoCodec.SharingContentIsOnFeedback.BoolValue); - VideoCodec.SharingContentIsOnFeedback.OutputChange += (o, a) => this.IsSharingFeedback.FireUpdate(); + //IsSharingFeedback = new BoolFeedback(() => VideoCodec.SharingContentIsOnFeedback.BoolValue); + //VideoCodec.SharingContentIsOnFeedback.OutputChange += (o, a) => this.IsSharingFeedback.FireUpdate(); - // link privacy to VC (for now?) - PrivacyModeIsOnFeedback = new BoolFeedback(() => VideoCodec.PrivacyModeIsOnFeedback.BoolValue); - VideoCodec.PrivacyModeIsOnFeedback.OutputChange += (o, a) => this.PrivacyModeIsOnFeedback.FireUpdate(); + //// link privacy to VC (for now?) + //PrivacyModeIsOnFeedback = new BoolFeedback(() => VideoCodec.PrivacyModeIsOnFeedback.BoolValue); + //VideoCodec.PrivacyModeIsOnFeedback.OutputChange += (o, a) => this.PrivacyModeIsOnFeedback.FireUpdate(); - CallTypeFeedback = new IntFeedback(() => 0); + //CallTypeFeedback = new IntFeedback(() => 0); SetSourceListKey(); - EnablePowerOnToLastSource = true; + //EnablePowerOnToLastSource = true; } catch (Exception e) { @@ -297,7 +297,9 @@ namespace PepperDash.Essentials { //DefaultDisplay = DeviceManager.GetDeviceForKey(PropertiesConfig.DefaultDisplayKey) as IRoutingSinkWithSwitching; - var destinationList = ConfigReader.ConfigObject.DestinationLists[PropertiesConfig.DestinationListKey]; + var destinationList = ConfigReader.ConfigObject.DestinationLists[PropertiesConfig.DestinationListKey]; + + Displays.Clear(); foreach (var destination in destinationList) { @@ -314,37 +316,54 @@ namespace PepperDash.Essentials // Link power, warming, cooling to display var dispTwoWay = display as IHasPowerControlWithFeedback; if (dispTwoWay != null) - { - dispTwoWay.PowerIsOnFeedback.OutputChange += (o, a) => - { - if (dispTwoWay.PowerIsOnFeedback.BoolValue != OnFeedback.BoolValue) - { - //if (!dispTwoWay.PowerIsOnFeedback.BoolValue) - // CurrentSourceInfo = null; - OnFeedback.FireUpdate(); - } - if (dispTwoWay.PowerIsOnFeedback.BoolValue) - { - SetDefaultLevels(); - } - }; - } - - display.IsWarmingUpFeedback.OutputChange += (o, a) => - { - IsWarmingUpFeedback.FireUpdate(); - if (!IsWarmingUpFeedback.BoolValue) - (CurrentVolumeControls as IBasicVolumeWithFeedback).SetVolume(DefaultVolume); - }; - display.IsCoolingDownFeedback.OutputChange += (o, a) => - { - IsCoolingDownFeedback.FireUpdate(); - }; + { + dispTwoWay.PowerIsOnFeedback.OutputChange -= PowerIsOnFeedback_OutputChange; + dispTwoWay.PowerIsOnFeedback.OutputChange += PowerIsOnFeedback_OutputChange; + + if (dispTwoWay.PowerIsOnFeedback.BoolValue) + { + SetDefaultLevels(); + } + } + + display.IsWarmingUpFeedback.OutputChange -= IsWarmingUpFeedback_OutputChange; + display.IsWarmingUpFeedback.OutputChange += IsWarmingUpFeedback_OutputChange; + + display.IsCoolingDownFeedback.OutputChange -= IsCoolingDownFeedback_OutputChange; + display.IsCoolingDownFeedback.OutputChange += IsCoolingDownFeedback_OutputChange; } } + } + + void IsCoolingDownFeedback_OutputChange(object sender, FeedbackEventArgs e) + { + IsCoolingDownFeedback.FireUpdate(); + } + + void IsWarmingUpFeedback_OutputChange(object sender, FeedbackEventArgs e) + { + IsWarmingUpFeedback.FireUpdate(); + if (!IsWarmingUpFeedback.BoolValue) + (CurrentVolumeControls as IBasicVolumeWithFeedback).SetVolume(DefaultVolume); + } + + void PowerIsOnFeedback_OutputChange(object sender, FeedbackEventArgs e) + { + var dispTwoWay = sender as IHasPowerControlWithFeedback; + + if (dispTwoWay != null && dispTwoWay.PowerIsOnFeedback.BoolValue != OnFeedback.BoolValue) + { + //if (!dispTwoWay.PowerIsOnFeedback.BoolValue) + // CurrentSourceInfo = null; + OnFeedback.FireUpdate(); + } } + + + + private void SetSourceListKey() { if (!string.IsNullOrEmpty(PropertiesConfig.SourceListKey)) @@ -354,9 +373,9 @@ namespace PepperDash.Essentials else { SetSourceListKey(Key); - } - - SetCodecExternalSources(); + } + + SetUpVideoCodec(); } protected override void CustomSetConfig(DeviceConfig config) @@ -367,26 +386,150 @@ namespace PepperDash.Essentials PropertiesConfig = newPropertiesConfig; ConfigWriter.UpdateRoomConfig(config); + } + + public override bool Deactivate() + { + // Stop listining to this event when room deactivated + VideoCodec.IsReadyChange -= VideoCodec_IsReadyChange; + + // Clear occupancy + RoomOccupancy = null; + + Debug.Console(0, this, "Room '{0}' Deactivated", Name); + + return base.Deactivate(); } public override bool CustomActivate() - { - // Add Occupancy object from config - if (PropertiesConfig.Occupancy != null) - { - Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Setting Occupancy Provider for room"); - this.SetRoomOccupancy(DeviceManager.GetDeviceForKey(PropertiesConfig.Occupancy.DeviceKey) as - IOccupancyStatusProvider, PropertiesConfig.Occupancy.TimeoutMinutes); - } - - this.LogoUrlLightBkgnd = PropertiesConfig.LogoLight.GetLogoUrlLight(); - this.LogoUrlDarkBkgnd = PropertiesConfig.LogoDark.GetLogoUrlDark(); - - this.DefaultSourceItem = PropertiesConfig.DefaultSourceItem; - this.DefaultVolume = (ushort)(PropertiesConfig.Volumes.Master.Level * 65535 / 100); - + { + try + { + if (DefaultAudioDevice is IBasicVolumeControls) + DefaultVolumeControls = DefaultAudioDevice as IBasicVolumeControls; + else if (DefaultAudioDevice is IHasVolumeDevice) + DefaultVolumeControls = (DefaultAudioDevice as IHasVolumeDevice).VolumeDevice; + CurrentVolumeControls = DefaultVolumeControls; + + + // Combines call feedback from both codecs if available + InCallFeedback = new BoolFeedback(() => + { + bool inAudioCall = false; + bool inVideoCall = false; + + if (AudioCodec != null) + inAudioCall = AudioCodec.IsInCall; + + if (VideoCodec != null) + inVideoCall = VideoCodec.IsInCall; + + if (inAudioCall || inVideoCall) + return true; + else + return false; + }); + + SetupDisplays(); + + // Get Microphone Privacy object, if any MUST HAPPEN AFTER setting InCallFeedback + this.MicrophonePrivacy = EssentialsRoomConfigHelper.GetMicrophonePrivacy(PropertiesConfig, this); + + Debug.Console(2, this, "Microphone Privacy Config evaluated."); + + // Get emergency object, if any + this.Emergency = EssentialsRoomConfigHelper.GetEmergency(PropertiesConfig, this); + + Debug.Console(2, this, "Emergency Config evaluated."); + + if (AudioCodec != null) + { + AudioCodec.CallStatusChange -= AudioCodec_CallStatusChange; + AudioCodec.CallStatusChange += AudioCodec_CallStatusChange; + } + + VideoCodec.CallStatusChange -= VideoCodec_CallStatusChange; + VideoCodec.CallStatusChange += VideoCodec_CallStatusChange; + + VideoCodec.IsReadyChange -= VideoCodec_IsReadyChange; + VideoCodec.IsReadyChange += VideoCodec_IsReadyChange; + + VideoCodec.SharingContentIsOnFeedback.OutputChange -= SharingContentIsOnFeedback_OutputChange; + VideoCodec.SharingContentIsOnFeedback.OutputChange += SharingContentIsOnFeedback_OutputChange; + + + IsSharingFeedback = new BoolFeedback(() => VideoCodec.SharingContentIsOnFeedback.BoolValue); + + // link privacy to VC (for now?) + PrivacyModeIsOnFeedback = new BoolFeedback(() => VideoCodec.PrivacyModeIsOnFeedback.BoolValue); + + VideoCodec.PrivacyModeIsOnFeedback.OutputChange -= PrivacyModeIsOnFeedback_OutputChange; + VideoCodec.PrivacyModeIsOnFeedback.OutputChange += PrivacyModeIsOnFeedback_OutputChange; + + CallTypeFeedback = new IntFeedback(() => 0); + + SetSourceListKey(); + + EnablePowerOnToLastSource = true; + + + // Add Occupancy object from config + if (PropertiesConfig.Occupancy != null) + { + Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Setting Occupancy Provider for room"); + this.SetRoomOccupancy(DeviceManager.GetDeviceForKey(PropertiesConfig.Occupancy.DeviceKey) as + IOccupancyStatusProvider, PropertiesConfig.Occupancy.TimeoutMinutes); + } + + this.LogoUrlLightBkgnd = PropertiesConfig.LogoLight.GetLogoUrlLight(); + this.LogoUrlDarkBkgnd = PropertiesConfig.LogoDark.GetLogoUrlDark(); + + this.DefaultSourceItem = PropertiesConfig.DefaultSourceItem; + this.DefaultVolume = (ushort)(PropertiesConfig.Volumes.Master.Level * 65535 / 100); + + } + catch (Exception e) + { + Debug.Console(0, this, "Error Activiating Room: {0}", e); + } + + + Debug.Console(0, this, "Room '{0}' Activated", Name); return base.CustomActivate(); - } + } + + void AudioCodec_CallStatusChange(object sender, CodecCallStatusItemChangeEventArgs e) + { + InCallFeedback.FireUpdate(); + } + + void PrivacyModeIsOnFeedback_OutputChange(object sender, FeedbackEventArgs e) + { + PrivacyModeIsOnFeedback.FireUpdate(); + } + + void VideoCodec_IsReadyChange(object sender, EventArgs e) + { + SetUpVideoCodec(); + } + + void SetUpVideoCodec() + { + SetCodecExternalSources(); + SetCodecBranding(); + } + + void VideoCodec_CallStatusChange(object sender, CodecCallStatusItemChangeEventArgs e) + { + InCallFeedback.FireUpdate(); + } + + void SharingContentIsOnFeedback_OutputChange(object sender, FeedbackEventArgs e) + { + IsSharingFeedback.FireUpdate(); + } + + /// /// @@ -779,7 +922,9 @@ namespace PepperDash.Essentials videoCodecWithExternalSwitching.AddExternalSource(codecInputConnectorName, kvp.Key, srcConfig.PreferredName, PepperDash.Essentials.Devices.Common.VideoCodec.Cisco.eExternalSourceType.desktop); videoCodecWithExternalSwitching.SetExternalSourceState(kvp.Key, PepperDash.Essentials.Devices.Common.VideoCodec.Cisco.eExternalSourceMode.Ready); } - } + } + + Debug.Console(1, this, "Successfully set up codec external sources for room: {0}", Name); } catch (Exception e) { diff --git a/PepperDashEssentials/Room/Types/EssentialsHuddleVtc1Room.cs b/PepperDashEssentials/Room/Types/EssentialsHuddleVtc1Room.cs index d04de3cc..2aae2f75 100644 --- a/PepperDashEssentials/Room/Types/EssentialsHuddleVtc1Room.cs +++ b/PepperDashEssentials/Room/Types/EssentialsHuddleVtc1Room.cs @@ -19,6 +19,8 @@ namespace PepperDash.Essentials { public class EssentialsHuddleVtc1Room : EssentialsRoomBase, IEssentialsHuddleVtc1Room { + private IEssentialsRoomCombiner _roomCombiner; + private bool _codecExternalSourceChange; public event EventHandler CurrentVolumeDeviceChange; public event SourceInfoChangeHandler CurrentSourceChange; @@ -234,7 +236,7 @@ namespace PepperDash.Essentials throw new ArgumentNullException("DefaultAudioDevice cannot be null"); } - InitializeRoom(); + Initialize(); } catch (Exception e) { @@ -242,8 +244,65 @@ namespace PepperDash.Essentials } } - void InitializeRoom() - { + + private void SetupEnvironmentalControlDevices() + { + if (PropertiesConfig.Environment != null) + { + if (PropertiesConfig.Environment.Enabled) + { + EnvironmentalControlDevices.Clear(); + + foreach (var d in PropertiesConfig.Environment.DeviceKeys) + { + var envDevice = DeviceManager.GetDeviceForKey(d) as EssentialsDevice; + EnvironmentalControlDevices.Add(envDevice); + } + } + } + } + + + private void SetSourceListKey() + { + if (!string.IsNullOrEmpty(PropertiesConfig.SourceListKey)) + { + SetSourceListKey(PropertiesConfig.SourceListKey); + } + else + { + SetSourceListKey(Key); + } + + SetUpVideoCodec(); + } + + protected override void CustomSetConfig(DeviceConfig config) + { + var newPropertiesConfig = JsonConvert.DeserializeObject(config.Properties.ToString()); + + if (newPropertiesConfig != null) + PropertiesConfig = newPropertiesConfig; + + ConfigWriter.UpdateRoomConfig(config); + } + + public override bool Deactivate() + { + + // Stop listining to this event when room deactivated + VideoCodec.IsReadyChange -= VideoCodec_IsReadyChange; + + // Clear occupancy + RoomOccupancy = null; + + Debug.Console(0, this, "Room '{0}' Deactivated", Name); + + return base.Deactivate(); + } + + public override bool CustomActivate() + { try { if (DefaultAudioDevice is IBasicVolumeControls) @@ -278,32 +337,15 @@ namespace PepperDash.Essentials var dispTwoWay = disp as IHasPowerControlWithFeedback; if (dispTwoWay != null) { - dispTwoWay.PowerIsOnFeedback.OutputChange += (o, a) => - { - if (dispTwoWay.PowerIsOnFeedback.BoolValue != OnFeedback.BoolValue) - { - if (!dispTwoWay.PowerIsOnFeedback.BoolValue) - CurrentSourceInfo = null; - OnFeedback.FireUpdate(); - } - if (dispTwoWay.PowerIsOnFeedback.BoolValue) - { - SetDefaultLevels(); - } - }; + dispTwoWay.PowerIsOnFeedback.OutputChange -= PowerIsOnFeedback_OutputChange; + dispTwoWay.PowerIsOnFeedback.OutputChange += PowerIsOnFeedback_OutputChange; } - disp.IsWarmingUpFeedback.OutputChange += (o, a) => - { - IsWarmingUpFeedback.FireUpdate(); - if (!IsWarmingUpFeedback.BoolValue) - (CurrentVolumeControls as IBasicVolumeWithFeedback).SetVolume(DefaultVolume); - }; - disp.IsCoolingDownFeedback.OutputChange += (o, a) => - { - IsCoolingDownFeedback.FireUpdate(); - }; + disp.IsWarmingUpFeedback.OutputChange -= IsWarmingUpFeedback_OutputChange; + disp.IsWarmingUpFeedback.OutputChange += IsWarmingUpFeedback_OutputChange; + disp.IsCoolingDownFeedback.OutputChange -= IsCoolingDownFeedback_OutputChange; + disp.IsCoolingDownFeedback.OutputChange += IsCoolingDownFeedback_OutputChange; } @@ -317,20 +359,30 @@ namespace PepperDash.Essentials this.Emergency = EssentialsRoomConfigHelper.GetEmergency(PropertiesConfig, this); Debug.Console(2, this, "Emergency Config evaluated."); - - - VideoCodec.CallStatusChange += (o, a) => this.InCallFeedback.FireUpdate(); - VideoCodec.IsReadyChange += (o, a) => { this.SetCodecExternalSources(); SetCodecBranding(); }; - + if (AudioCodec != null) - AudioCodec.CallStatusChange += (o, a) => this.InCallFeedback.FireUpdate(); + { + AudioCodec.CallStatusChange -= AudioCodec_CallStatusChange; + AudioCodec.CallStatusChange += AudioCodec_CallStatusChange; + } + + VideoCodec.CallStatusChange -= VideoCodec_CallStatusChange; + VideoCodec.CallStatusChange += VideoCodec_CallStatusChange; + + VideoCodec.IsReadyChange -= VideoCodec_IsReadyChange; + VideoCodec.IsReadyChange += VideoCodec_IsReadyChange; + + VideoCodec.SharingContentIsOnFeedback.OutputChange -= SharingContentIsOnFeedback_OutputChange; + VideoCodec.SharingContentIsOnFeedback.OutputChange += SharingContentIsOnFeedback_OutputChange; + IsSharingFeedback = new BoolFeedback(() => VideoCodec.SharingContentIsOnFeedback.BoolValue); - VideoCodec.SharingContentIsOnFeedback.OutputChange += (o, a) => this.IsSharingFeedback.FireUpdate(); // link privacy to VC (for now?) PrivacyModeIsOnFeedback = new BoolFeedback(() => VideoCodec.PrivacyModeIsOnFeedback.BoolValue); - VideoCodec.PrivacyModeIsOnFeedback.OutputChange += (o, a) => this.PrivacyModeIsOnFeedback.FireUpdate(); + + VideoCodec.PrivacyModeIsOnFeedback.OutputChange -= PrivacyModeIsOnFeedback_OutputChange; + VideoCodec.PrivacyModeIsOnFeedback.OutputChange += PrivacyModeIsOnFeedback_OutputChange; CallTypeFeedback = new IntFeedback(() => 0); @@ -339,72 +391,92 @@ namespace PepperDash.Essentials SetSourceListKey(); EnablePowerOnToLastSource = true; + + + // Add Occupancy object from config + if (PropertiesConfig.Occupancy != null) + { + Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Setting Occupancy Provider for room"); + this.SetRoomOccupancy(DeviceManager.GetDeviceForKey(PropertiesConfig.Occupancy.DeviceKey) as + IOccupancyStatusProvider, PropertiesConfig.Occupancy.TimeoutMinutes); + } + + this.LogoUrlLightBkgnd = PropertiesConfig.LogoLight.GetLogoUrlLight(); + this.LogoUrlDarkBkgnd = PropertiesConfig.LogoDark.GetLogoUrlDark(); + + this.DefaultSourceItem = PropertiesConfig.DefaultSourceItem; + this.DefaultVolume = (ushort)(PropertiesConfig.Volumes.Master.Level * 65535 / 100); } catch (Exception e) { - Debug.Console(0, this, "Error Initializing Room: {0}", e); - } - } - - private void SetupEnvironmentalControlDevices() - { - if (PropertiesConfig.Environment != null) - { - if (PropertiesConfig.Environment.Enabled) - { - foreach (var d in PropertiesConfig.Environment.DeviceKeys) - { - var envDevice = DeviceManager.GetDeviceForKey(d) as EssentialsDevice; - EnvironmentalControlDevices.Add(envDevice); - } - } - } - } - - - private void SetSourceListKey() - { - if (!string.IsNullOrEmpty(PropertiesConfig.SourceListKey)) - { - SetSourceListKey(PropertiesConfig.SourceListKey); - } - else - { - SetSourceListKey(Key); + Debug.Console(0, this, "Error Activiating Room: {0}", e); } - SetCodecExternalSources(); - } - - protected override void CustomSetConfig(DeviceConfig config) - { - var newPropertiesConfig = JsonConvert.DeserializeObject(config.Properties.ToString()); - - if (newPropertiesConfig != null) - PropertiesConfig = newPropertiesConfig; - - ConfigWriter.UpdateRoomConfig(config); - } - - public override bool CustomActivate() - { - // Add Occupancy object from config - if (PropertiesConfig.Occupancy != null) - { - Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Setting Occupancy Provider for room"); - this.SetRoomOccupancy(DeviceManager.GetDeviceForKey(PropertiesConfig.Occupancy.DeviceKey) as - IOccupancyStatusProvider, PropertiesConfig.Occupancy.TimeoutMinutes); - } - - this.LogoUrlLightBkgnd = PropertiesConfig.LogoLight.GetLogoUrlLight(); - this.LogoUrlDarkBkgnd = PropertiesConfig.LogoDark.GetLogoUrlDark(); - - this.DefaultSourceItem = PropertiesConfig.DefaultSourceItem; - this.DefaultVolume = (ushort)(PropertiesConfig.Volumes.Master.Level * 65535 / 100); - + Debug.Console(0, this, "Room '{0}' Activated", Name); return base.CustomActivate(); } + void PrivacyModeIsOnFeedback_OutputChange(object sender, FeedbackEventArgs e) + { + PrivacyModeIsOnFeedback.FireUpdate(); + } + + void SharingContentIsOnFeedback_OutputChange(object sender, FeedbackEventArgs e) + { + IsSharingFeedback.FireUpdate(); + } + + void AudioCodec_CallStatusChange(object sender, CodecCallStatusItemChangeEventArgs e) + { + InCallFeedback.FireUpdate(); + } + + void VideoCodec_IsReadyChange(object sender, EventArgs e) + { + SetUpVideoCodec(); + } + + void SetUpVideoCodec() + { + SetCodecExternalSources(); + SetCodecBranding(); + } + + void VideoCodec_CallStatusChange(object sender, CodecCallStatusItemChangeEventArgs e) + { + InCallFeedback.FireUpdate(); + } + + void IsCoolingDownFeedback_OutputChange(object sender, FeedbackEventArgs e) + { + IsCoolingDownFeedback.FireUpdate(); + } + + void IsWarmingUpFeedback_OutputChange(object sender, FeedbackEventArgs e) + { + IsWarmingUpFeedback.FireUpdate(); + if (!IsWarmingUpFeedback.BoolValue) + (CurrentVolumeControls as IBasicVolumeWithFeedback).SetVolume(DefaultVolume); + + } + + void PowerIsOnFeedback_OutputChange(object sender, FeedbackEventArgs e) + { + var dispTwoWay = DefaultDisplay as IHasPowerControlWithFeedback; + + if (dispTwoWay.PowerIsOnFeedback.BoolValue != OnFeedback.BoolValue) + { + if (!dispTwoWay.PowerIsOnFeedback.BoolValue) + CurrentSourceInfo = null; + OnFeedback.FireUpdate(); + } + if (dispTwoWay.PowerIsOnFeedback.BoolValue) + { + SetDefaultLevels(); + } + + } + /// @@ -832,6 +904,8 @@ namespace PepperDash.Essentials videoCodecWithExternalSwitching.SetExternalSourceState(kvp.Key, PepperDash.Essentials.Devices.Common.VideoCodec.Cisco.eExternalSourceMode.Ready); } } + Debug.Console(1, this, "Successfully set up codec external sources for room: {0}", Name); + } catch (Exception e) { diff --git a/PepperDashEssentials/Room/Types/Interfaces/IEssentialsHuddleSpaceRoom.cs b/PepperDashEssentials/Room/Types/Interfaces/IEssentialsHuddleSpaceRoom.cs index 41616d96..dccae06a 100644 --- a/PepperDashEssentials/Room/Types/Interfaces/IEssentialsHuddleSpaceRoom.cs +++ b/PepperDashEssentials/Room/Types/Interfaces/IEssentialsHuddleSpaceRoom.cs @@ -7,7 +7,8 @@ using PepperDash.Essentials.Room.Config; namespace PepperDash.Essentials { - public interface IEssentialsHuddleSpaceRoom : IEssentialsRoom, IHasCurrentSourceInfoChange, IRunRouteAction, IRunDefaultPresentRoute, IHasDefaultDisplay, IHasCurrentVolumeControls + public interface IEssentialsHuddleSpaceRoom : IEssentialsRoom, IHasCurrentSourceInfoChange, IRunRouteAction, IRunDefaultPresentRoute, IHasDefaultDisplay, IHasCurrentVolumeControls, IRoomOccupancy, + IEmergency, IMicrophonePrivacy { bool ExcludeFromGlobalFunctions { get; } diff --git a/PepperDashEssentials/Room/Types/Interfaces/IEssentialsHuddleVtc1Room.cs b/PepperDashEssentials/Room/Types/Interfaces/IEssentialsHuddleVtc1Room.cs index 03f7340b..85937828 100644 --- a/PepperDashEssentials/Room/Types/Interfaces/IEssentialsHuddleVtc1Room.cs +++ b/PepperDashEssentials/Room/Types/Interfaces/IEssentialsHuddleVtc1Room.cs @@ -8,7 +8,8 @@ using PepperDash.Essentials.Devices.Common.AudioCodec; namespace PepperDash.Essentials { public interface IEssentialsHuddleVtc1Room : IEssentialsRoom, IHasCurrentSourceInfoChange, - IPrivacy, IHasCurrentVolumeControls, IRunRouteAction, IRunDefaultCallRoute, IHasVideoCodec, IHasAudioCodec, IHasDefaultDisplay, IHasInCallFeedback + IPrivacy, IHasCurrentVolumeControls, IRunRouteAction, IRunDefaultCallRoute, IHasVideoCodec, IHasAudioCodec, IHasDefaultDisplay, IHasInCallFeedback, + IRoomOccupancy, IEmergency, IMicrophonePrivacy { EssentialsHuddleVtc1PropertiesConfig PropertiesConfig { get; } diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/DmRmcControllerJoinMap.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/DmRmcControllerJoinMap.cs index cff5bffe..c0e7141f 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/DmRmcControllerJoinMap.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/DmRmcControllerJoinMap.cs @@ -8,6 +8,18 @@ namespace PepperDash.Essentials.Core.Bridges public JoinDataComplete IsOnline = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 }, new JoinMetadata { Description = "DM RMC Online", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital }); + [JoinName("VideoMuteOn")] + public JoinDataComplete VideoMuteOn = new JoinDataComplete(new JoinData { JoinNumber = 3, JoinSpan = 1 }, + new JoinMetadata { Description = "DM RMC Mute Video", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital }); + + [JoinName("VideoMuteOff")] + public JoinDataComplete VideoMuteOff = new JoinDataComplete(new JoinData { JoinNumber = 4, JoinSpan = 1 }, + new JoinMetadata { Description = "DM RMC UnMute Video", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital }); + + [JoinName("VideoMuteToggle")] + public JoinDataComplete VideoMuteToggle = new JoinDataComplete(new JoinData { JoinNumber = 5, JoinSpan = 1 }, + new JoinMetadata { Description = "DM RMC Mute Video Toggle", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital }); + [JoinName("CurrentOutputResolution")] public JoinDataComplete CurrentOutputResolution = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 }, new JoinMetadata { Description = "DM RMC Current Output Resolution", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial }); diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/HdPsXxxControllerJoinMap.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/HdPsXxxControllerJoinMap.cs new file mode 100644 index 00000000..3f2901c9 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/HdPsXxxControllerJoinMap.cs @@ -0,0 +1,190 @@ +using System; +using PepperDash.Essentials.Core; + +namespace PepperDash_Essentials_Core.Bridges +{ + public class HdPsXxxControllerJoinMap : JoinMapBaseAdvanced + { + + #region Digital + + [JoinName("EnableAutoRoute")] + public JoinDataComplete EnableAutoRoute = new JoinDataComplete( + new JoinData + { + JoinNumber = 1, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Enable Automatic Routing on Xx1 Switchers", + JoinCapabilities = eJoinCapabilities.ToFromSIMPL, + JoinType = eJoinType.Digital + }); + + + [JoinName("InputSync")] + public JoinDataComplete InputSync = new JoinDataComplete( + new JoinData + { + JoinNumber = 2, + JoinSpan = 8 + }, + new JoinMetadata + { + Description = "Device Input Sync", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Digital + }); + + + [JoinName("EnableInputHdcp")] + public JoinDataComplete EnableInputHdcp = new JoinDataComplete( + new JoinData + { + JoinNumber = 11, + JoinSpan = 8 + }, + new JoinMetadata + { + Description = "Device Enable Input Hdcp", + JoinCapabilities = eJoinCapabilities.ToFromSIMPL, + JoinType = eJoinType.Digital + }); + + + [JoinName("DisableInputHdcp")] + public JoinDataComplete DisableInputHdcp = new JoinDataComplete( + new JoinData + { + JoinNumber = 21, + JoinSpan = 8 + }, + new JoinMetadata + { + Description = "Device Disnable Input Hdcp", + JoinCapabilities = eJoinCapabilities.ToFromSIMPL, + JoinType = eJoinType.Digital + }); + + + [JoinName("IsOnline")] + public JoinDataComplete IsOnline = new JoinDataComplete( + new JoinData + { + JoinNumber = 30, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Device Onlne", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Digital + }); + + #endregion + + + #region Analog + + [JoinName("OutputRoute")] + public JoinDataComplete OutputRoute = new JoinDataComplete( + new JoinData + { + JoinNumber = 11, + JoinSpan = 2 + }, + new JoinMetadata + { + Description = "Device Output Route Set/Get", + JoinCapabilities = eJoinCapabilities.ToFromSIMPL, + JoinType = eJoinType.Analog + }); + + #endregion + + + #region Serial + + [JoinName("Name")] + public JoinDataComplete Name = new JoinDataComplete( + new JoinData + { + JoinNumber = 1, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Device Name", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Serial + }); + + + [JoinName("InputName")] + public JoinDataComplete InputName = new JoinDataComplete( + new JoinData + { + JoinNumber = 2, + JoinSpan = 8 + }, + new JoinMetadata + { + Description = "Device Input Name", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Serial + }); + + + [JoinName("OutputName")] + public JoinDataComplete OutputName = new JoinDataComplete( + new JoinData + { + JoinNumber = 11, + JoinSpan = 2 + }, + new JoinMetadata + { + Description = "Device Output Name", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Serial + }); + + + [JoinName("OutputRoutedName")] + public JoinDataComplete OutputRoutedName = new JoinDataComplete( + new JoinData + { + JoinNumber = 16, + JoinSpan = 2 + }, + new JoinMetadata + { + Description = "Device Output Route Name", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Serial + }); + + + #endregion + + /// + /// Constructor to use when instantiating this join map without inheriting from it + /// + /// Join this join map will start at + public HdPsXxxControllerJoinMap(uint joinStart) + : this(joinStart, typeof(HdPsXxxControllerJoinMap)) + { + } + + /// + /// Constructor to use when extending this Join map + /// + /// Join this join map will start at + /// Type of the child join map + protected HdPsXxxControllerJoinMap(uint joinStart, Type type) + : base(joinStart, type) + { + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/PduJoinMapBase.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/PduJoinMapBase.cs index 2ac56ff1..0c2e9ed9 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/PduJoinMapBase.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/PduJoinMapBase.cs @@ -10,7 +10,7 @@ namespace PepperDash.Essentials.Core.Bridges [JoinName("Online")] public JoinDataComplete Online = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 }, - new JoinMetadata { Description = "PDU Name", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital }); + new JoinMetadata { Description = "Online", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital }); [JoinName("OutletCount")] public JoinDataComplete OutletCount = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 }, diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Comm and IR/CommFactory.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Comm and IR/CommFactory.cs index 9667b5b9..d5bc56fe 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Comm and IR/CommFactory.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Comm and IR/CommFactory.cs @@ -81,6 +81,15 @@ namespace PepperDash.Essentials.Core } case eControlMethod.Telnet: break; + case eControlMethod.SecureTcpIp: + { + var secureTcp = new GenericSecureTcpIpClient(deviceConfig.Key + "-secureTcp", c.Address, c.Port, c.BufferSize); + secureTcp.AutoReconnect = c.AutoReconnect; + if (secureTcp.AutoReconnect) + secureTcp.AutoReconnectIntervalMs = c.AutoReconnectIntervalMs; + comm = secureTcp; + break; + } default: break; } @@ -115,41 +124,54 @@ namespace PepperDash.Essentials.Core /// public static ICec GetCecPort(ControlPropertiesConfig config) { - var dev = DeviceManager.GetDeviceForKey(config.ControlPortDevKey); + try + { + var dev = DeviceManager.GetDeviceForKey(config.ControlPortDevKey); - if (dev != null) - { - if (!String.IsNullOrEmpty(config.ControlPortName)) - { + Debug.Console(0, "GetCecPort: device '{0}' {1}", config.ControlPortDevKey, dev == null + ? "is not valid, failed to get cec port" + : "found in device manager, attempting to get cec port"); - var inputPort = (dev as IRoutingInputsOutputs).InputPorts[config.ControlPortName]; + if (dev == null) + return null; - if (inputPort != null) - { - if (inputPort.Port is ICec) - return inputPort.Port as ICec; - } + if (String.IsNullOrEmpty(config.ControlPortName)) + { + Debug.Console(0, "GetCecPort: '{0}' - Configuration missing 'ControlPortName'", config.ControlPortDevKey); + return null; + } - var outputPort = (dev as IRoutingInputsOutputs).OutputPorts[config.ControlPortName]; - if (outputPort != null) - { - if (outputPort.Port is ICec) - return outputPort.Port as ICec; - } + var inputsOutputs = dev as IRoutingInputsOutputs; + if (inputsOutputs == null) + { + Debug.Console(0, "GetCecPort: Device '{0}' does not support IRoutingInputsOutputs, failed to get CEC port called '{1}'", + config.ControlPortDevKey, config.ControlPortName); - else - Debug.Console(0, "GetCecPort: Device '{0}' does not have a CEC port called: '{1}'", - config.ControlPortDevKey, config.ControlPortName); - } - else - { - Debug.Console(0, "GetCecPort: '{0}' - Configuration missing 'ControlPortName'", config.ControlPortDevKey); - } - } - Debug.Console(0, "GetCecPort: Device '{0}' is not a valid device.", config.ControlPortDevKey); + return null; + } - return null; + var inputPort = inputsOutputs.InputPorts[config.ControlPortName]; + if (inputPort != null && inputPort.Port is ICec) + return inputPort.Port as ICec; + + + var outputPort = inputsOutputs.OutputPorts[config.ControlPortName]; + if (outputPort != null && outputPort.Port is ICec) + return outputPort.Port as ICec; + } + catch (Exception ex) + { + Debug.Console(1, "GetCecPort Exception Message: {0}", ex.Message); + Debug.Console(2, "GetCecPort Exception StackTrace: {0}", ex.StackTrace); + if (ex.InnerException != null) + Debug.Console(0, "GetCecPort Exception InnerException: {0}", ex.InnerException); + } + + Debug.Console(0, "GetCecPort: Device '{0}' does not have a CEC port called '{1}'", + config.ControlPortDevKey, config.ControlPortName); + + return null; } /// diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Device Info/NetworkDeviceHelpers.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Device Info/NetworkDeviceHelpers.cs new file mode 100644 index 00000000..04e697c9 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Device Info/NetworkDeviceHelpers.cs @@ -0,0 +1,218 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using PepperDash.Core; +using Crestron.SimplSharp; +using PepperDash.Essentials.Core; + +namespace PepperDash.Essentials.Core.DeviceInfo +{ + public static class NetworkDeviceHelpers + { + /// + /// Event raised when ArpTable changes + /// + public static event ArpTableEventHandler ArpTableUpdated; + + /// + /// Delegate called by ArpTableUpdated + /// + /// contains the entire ARP table and a bool to note if there was an error in retrieving the data + public delegate void ArpTableEventHandler(ArpTableEventArgs args); + + private static readonly char NewLineSplitter = CrestronEnvironment.NewLine.ToCharArray().First(); + private static readonly string NewLine = CrestronEnvironment.NewLine; + + private static readonly CCriticalSection Lock = new CCriticalSection(); + + /// + /// Last resolved ARP table - it is recommended to refresh the arp before using this. + /// + public static List ArpTable { get; private set; } + + /// + /// Force recheck of ARP table + /// + public static void RefreshArp() + { + var error = false; + try + { + Lock.Enter(); + var consoleResponse = string.Empty; + if (!CrestronConsole.SendControlSystemCommand("showarptable", ref consoleResponse)) return; + if (string.IsNullOrEmpty(consoleResponse)) + { + error = true; + return; + } + ArpTable.Clear(); + + Debug.Console(2, "ConsoleResponse of 'showarptable' : {0}{1}", NewLine, consoleResponse); + + var myLines = + consoleResponse.Split(NewLineSplitter) + .ToList() + .Where(o => (o.Contains(':') && !o.Contains("Type", StringComparison.OrdinalIgnoreCase))) + .ToList(); + foreach (var line in myLines) + { + var item = line; + var seperator = item.Contains('\t') ? '\t' : ' '; + var dataPoints = item.Split(seperator); + if (dataPoints == null || dataPoints.Length < 2) continue; + var ipAddress = SanitizeIpAddress(dataPoints.First().TrimAll()); + var macAddress = dataPoints.Last(); + ArpTable.Add(new ArpEntry(ipAddress, macAddress)); + } + } + catch (Exception ex) + { + Debug.Console(0, "Exception in \"RefreshArp\" : {0}", ex.Message); + error = true; + } + finally + { + Lock.Leave(); + OnArpTableUpdated(new ArpTableEventArgs(ArpTable, error)); + } + } + + + private static void OnArpTableUpdated(ArpTableEventArgs args) + { + if (args == null) return; + var handler = ArpTableUpdated; + if (handler == null) return; + handler.Invoke(args); + } + + static NetworkDeviceHelpers() + { + ArpTable = new List(); + } + + /// + /// Removes leading zeros, leading whitespace, and trailing whitespace from an IPAddress string + /// + /// Ip Address to Santitize + /// Sanitized Ip Address + public static string SanitizeIpAddress(string ipAddressIn) + { + try + { + var ipAddress = IPAddress.Parse(ipAddressIn.TrimStart('0')); + return ipAddress.ToString(); + } + catch (Exception ex) + { + Debug.Console(0, "Unable to Santize Ip : {0}", ex.Message); + return ipAddressIn; + } + } + + /// + /// Resolves a hostname by IP Address using DNS + /// + /// IP Address to resolve from + /// Resolved Hostname - on failure to determine hostname, will return IP Address + public static string ResolveHostnameFromIp(string ipAddress) + { + try + { + var santitizedIp = SanitizeIpAddress(ipAddress); + var hostEntry = Dns.GetHostEntry(santitizedIp); + return hostEntry == null ? ipAddress : hostEntry.HostName; + } + catch (Exception ex) + { + Debug.Console(0, "Exception Resolving Hostname from IP Address : {0}", ex.Message); + return ipAddress; + } + } + + /// + /// Resolves an IP Address by hostname using DNS + /// + /// Hostname to resolve from + /// Resolved IP Address - on a failure to determine IP Address, will return hostname + public static string ResolveIpFromHostname(string hostName) + { + try + { + var hostEntry = Dns.GetHostEntry(hostName); + return hostEntry == null ? hostName : hostEntry.AddressList.First().ToString(); + } + catch (Exception ex) + { + Debug.Console(0, "Exception Resolving IP Address from Hostname : {0}", ex.Message); + return hostName; + } + } + + } + + /// + /// Object to hold data about an arp entry + /// + public class ArpEntry + { + public readonly IPAddress IpAddress; + public readonly string MacAddress; + + /// + /// Constructs new ArpEntry object + /// + /// string formatted as ipv4 address + /// mac address string - format is unimportant + public ArpEntry(string ipAddress, string macAddress) + { + if (string.IsNullOrEmpty(ipAddress)) + { + throw new ArgumentException("\"ipAddress\" cannot be null or empty"); + } + if (string.IsNullOrEmpty(macAddress)) + { + throw new ArgumentException("\"macAddress\" cannot be null or empty"); + } + IpAddress = IPAddress.Parse(ipAddress.TrimStart().TrimStart('0').TrimEnd()); + MacAddress = macAddress; + } + } + + /// + /// Arguments passed by the ArpTableUpdated event + /// + public class ArpTableEventArgs : EventArgs + { + /// + /// The retrieved ARP Table + /// + public readonly List ArpTable; + /// + /// True if there was a problem retrieving the ARP Table + /// + public readonly bool Error; + + /// + /// Constructor for ArpTableEventArgs + /// + /// The entirety of the retrieved ARP table + /// True of an error was encountered updating the ARP table + public ArpTableEventArgs(List arpTable, bool error) + { + ArpTable = arpTable; + Error = error; + } + + /// + /// Constructor for ArpTableEventArgs - assumes no error encountered in retrieving ARP Table + /// + /// The entirety of the retrieved ARP table + public ArpTableEventArgs(List arpTable) + { + ArpTable = arpTable; + Error = false; + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/IVolumeAndAudioInterfaces.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/IVolumeAndAudioInterfaces.cs index c8a5df39..c8033b92 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/IVolumeAndAudioInterfaces.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/IVolumeAndAudioInterfaces.cs @@ -72,6 +72,10 @@ namespace PepperDash.Essentials.Core { IBasicVolumeControls CurrentVolumeControls { get; } event EventHandler CurrentVolumeDeviceChange; + + void SetDefaultLevels(); + + bool ZeroVolumeWhenSwtichingVolumeDevices { get; } } diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Extensions/StringExtensions.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Extensions/StringExtensions.cs index 39501387..7bf8d5a5 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Extensions/StringExtensions.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Extensions/StringExtensions.cs @@ -1,4 +1,5 @@ using System; +using System.ComponentModel; using System.Collections.Generic; using System.Linq; using System.Text; @@ -8,17 +9,70 @@ namespace PepperDash.Essentials.Core { public static class StringExtensions { + /// + /// Returns null if a string is empty, otherwise returns the string + /// + /// string input + /// null if the string is emtpy, otherwise returns the string public static string NullIfEmpty(this string s) { return string.IsNullOrEmpty(s) ? null : s; } + + /// + /// Returns null if a string is empty or made of only whitespace characters, otherwise returns the string + /// + /// string input + /// null if the string is wempty or made of only whitespace characters, otherwise returns the string public static string NullIfWhiteSpace(this string s) { return string.IsNullOrEmpty(s.Trim()) ? null : s; } + + /// + /// Returns a replacement string if the input string is empty or made of only whitespace characters, otherwise returns the input string + /// + /// input string + /// string to replace with if input string is empty or whitespace + /// returns newString if s is null, emtpy, or made of whitespace characters, otherwise returns s public static string ReplaceIfNullOrEmpty(this string s, string newString) { return string.IsNullOrEmpty(s) ? newString : s; } + + /// + /// Overload for Contains that allows setting an explicit String Comparison + /// + /// Source String + /// String to check in Source String + /// Comparison parameters + /// true of string contains "toCheck" + public static bool Contains(this string source, string toCheck, StringComparison comp) + { + if (string.IsNullOrEmpty(source)) return false; + return source.IndexOf(toCheck, comp) >= 0; + } + + /// + /// Performs TrimStart() and TrimEnd() on source string + /// + /// String to Trim + /// Trimmed String + public static string TrimAll(this string source) + { + return string.IsNullOrEmpty(source) ? string.Empty : source.TrimStart().TrimEnd(); + } + + /// + /// Performs TrimStart(chars char[]) and TrimEnd(chars char[]) on source string. + /// + /// String to Trim + /// Char Array to trim from string + /// Trimmed String + public static string TrimAll(this string source, char[] chars) + { + return string.IsNullOrEmpty(source) ? string.Empty : source.TrimStart(chars).TrimEnd(chars); + } + } } \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/EssentialsHuddleSpaceFusionSystemControllerBase.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/EssentialsHuddleSpaceFusionSystemControllerBase.cs index 1bf925d6..a675f765 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/EssentialsHuddleSpaceFusionSystemControllerBase.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/EssentialsHuddleSpaceFusionSystemControllerBase.cs @@ -148,15 +148,20 @@ namespace PepperDash.Essentials.Core.Fusion ReadGuidFile(guidFilePath); } - if (Room.RoomOccupancy != null) + var occupancyRoom = Room as IRoomOccupancy; + + if (occupancyRoom != null) { - if (Room.OccupancyStatusProviderIsRemote) + if (occupancyRoom.RoomOccupancy != null) { - SetUpRemoteOccupancy(); - } - else - { - SetUpLocalOccupancy(); + if (occupancyRoom.OccupancyStatusProviderIsRemote) + { + SetUpRemoteOccupancy(); + } + else + { + SetUpLocalOccupancy(); + } } } @@ -1523,10 +1528,15 @@ namespace PepperDash.Essentials.Core.Fusion // Tie to method on occupancy object //occSensorShutdownMinutes.OutputSig.UserObject(new Action(ushort)(b => Room.OccupancyObj.SetShutdownMinutes(b)); + var occRoom = Room as IRoomOccupancy; + if (occRoom != null) + { + occRoom.RoomOccupancy.RoomIsOccupiedFeedback.LinkInputSig(occSensorAsset.RoomOccupied.InputSig); + occRoom.RoomOccupancy.RoomIsOccupiedFeedback.OutputChange += RoomIsOccupiedFeedback_OutputChange; + } RoomOccupancyRemoteStringFeedback = new StringFeedback(() => _roomOccupancyRemoteString); - Room.RoomOccupancy.RoomIsOccupiedFeedback.LinkInputSig(occSensorAsset.RoomOccupied.InputSig); - Room.RoomOccupancy.RoomIsOccupiedFeedback.OutputChange += RoomIsOccupiedFeedback_OutputChange; + RoomOccupancyRemoteStringFeedback.LinkInputSig(occSensorAsset.RoomOccupancyInfo.InputSig); //} diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/JoinMaps/JoinMapBase.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/JoinMaps/JoinMapBase.cs index 60aa6275..12df7f14 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/JoinMaps/JoinMapBase.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/JoinMaps/JoinMapBase.cs @@ -356,16 +356,18 @@ namespace PepperDash.Essentials.Core { foreach (var customJoinData in joinData) { - var join = Joins[customJoinData.Key]; + JoinDataComplete join; + + if (!Joins.TryGetValue(customJoinData.Key, out join)) + { + Debug.Console(2, "No matching key found in join map for: '{0}'", customJoinData.Key); + continue; + } if (join != null) { join.SetCustomJoinData(customJoinData.Value); } - else - { - Debug.Console(2, "No matching key found in join map for: '{0}'", customJoinData.Key); - } } PrintJoinMapInfo(); diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/PartitionSensor/GlsPartitionSensorController.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/PartitionSensor/GlsPartitionSensorController.cs index 16b2f265..73f15cf2 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/PartitionSensor/GlsPartitionSensorController.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/PartitionSensor/GlsPartitionSensorController.cs @@ -85,18 +85,32 @@ namespace PepperDash.Essentials.Core { if (_partitionSensor.IsOnline == false) return; - Debug.Console(1, this, "Attempting to apply settings to sensor from config"); + // Default to enable + _partitionSensor.Enable.BoolValue = true; - if (PropertiesConfig.Sensitivity != null) - { - Debug.Console(1, this, "Sensitivity found, attempting to set value '{0}' from config", - PropertiesConfig.Sensitivity); - _partitionSensor.Sensitivity.UShortValue = (ushort) PropertiesConfig.Sensitivity; - } - else - { - Debug.Console(1, this, "Sensitivity null, no value specified in config"); - } + Debug.Console(1, this, "Attempting to apply settings to sensor from config"); + + if (PropertiesConfig.Sensitivity != null) + { + Debug.Console(1, this, "Sensitivity found, attempting to set value '{0}' from config", + PropertiesConfig.Sensitivity); + _partitionSensor.Sensitivity.UShortValue = (ushort)PropertiesConfig.Sensitivity; + } + else + { + Debug.Console(1, this, "Sensitivity null, no value specified in config"); + } + + if (PropertiesConfig.Enable != null) + { + Debug.Console(1, this, "Enable found, attempting to set value '{0}' from config", + PropertiesConfig.Enable); + _partitionSensor.Enable.BoolValue = (bool)PropertiesConfig.Enable; + } + else + { + Debug.Console(1, this, "Enable null, no value specified in config"); + } } diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/PartitionSensor/GlsPartitionSensorPropertiesConfig.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/PartitionSensor/GlsPartitionSensorPropertiesConfig.cs index 8a303662..c9f715b5 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/PartitionSensor/GlsPartitionSensorPropertiesConfig.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/PartitionSensor/GlsPartitionSensorPropertiesConfig.cs @@ -16,6 +16,9 @@ namespace PepperDash_Essentials_Core.PartitionSensor /// The sensitivity range shall be between 1(lowest) to 10 (highest). /// [JsonProperty("sensitivity")] - public ushort? Sensitivity { get; set; } + public ushort? Sensitivity { get; set; } + + [JsonProperty("enable")] + public bool? Enable { get; set; } } } \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.csproj b/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.csproj index 7930f49e..a0da04f9 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.csproj +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.csproj @@ -83,7 +83,7 @@ ..\..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.UI.dll - + False ..\..\..\packages\PepperDashCore\lib\net35\PepperDash_Core.dll @@ -127,6 +127,7 @@ + @@ -200,6 +201,7 @@ + diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Behaviours/RoomOnToDefaultSourceWhenOccupied.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Behaviours/RoomOnToDefaultSourceWhenOccupied.cs index 81cbff9e..b7440213 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Behaviours/RoomOnToDefaultSourceWhenOccupied.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Behaviours/RoomOnToDefaultSourceWhenOccupied.cs @@ -38,7 +38,7 @@ namespace PepperDash.Essentials.Core ScheduledEventGroup FeatureEventGroup; - public IEssentialsRoom Room { get; private set; } + public IRoomOccupancy Room { get; private set; } private Fusion.EssentialsHuddleSpaceFusionSystemControllerBase FusionRoom; @@ -84,7 +84,7 @@ namespace PepperDash.Essentials.Core /// void SetUpDevice() { - Room = DeviceManager.GetDeviceForKey(PropertiesConfig.RoomKey) as IEssentialsRoom; + Room = DeviceManager.GetDeviceForKey(PropertiesConfig.RoomKey) as IRoomOccupancy; if (Room != null) { @@ -235,12 +235,23 @@ namespace PepperDash.Essentials.Core if (FeatureEnabled) { - // Check room power state first - if (!Room.OnFeedback.BoolValue) - { - Debug.Console(1, this, "Powering Room on to default source"); - Room.RunDefaultPresentRoute(); + var essentialsRoom = Room as IEssentialsRoom; + + if (essentialsRoom != null) { + if (!essentialsRoom.OnFeedback.BoolValue) + { + Debug.Console(1, this, "Powering Room on to default source"); + + var defaultRouteRoom = Room as IRunDefaultPresentRoute; + + if (defaultRouteRoom != null) + { + defaultRouteRoom.RunDefaultPresentRoute(); + } + } } + // Check room power state first + } } } diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/EssentialsRoomBase.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/EssentialsRoomBase.cs index f5e3dee8..0c7e0de0 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/EssentialsRoomBase.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/EssentialsRoomBase.cs @@ -35,7 +35,7 @@ namespace PepperDash.Essentials.Core public BoolFeedback IsWarmingUpFeedback { get; private set; } public BoolFeedback IsCoolingDownFeedback { get; private set; } - public IOccupancyStatusProvider RoomOccupancy { get; private set; } + public IOccupancyStatusProvider RoomOccupancy { get; protected set; } public bool OccupancyStatusProviderIsRemote { get; private set; } diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/IEssentialsRoom.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/IEssentialsRoom.cs index 2273690f..9a70f980 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/IEssentialsRoom.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/IEssentialsRoom.cs @@ -17,15 +17,10 @@ namespace PepperDash.Essentials.Core /// public interface IEssentialsRoom : IKeyName, IReconfigurableDevice, IRunDefaultPresentRoute, IEnvironmentalControls { - BoolFeedback OnFeedback { get; } - - event EventHandler RoomOccupancyIsSet; + BoolFeedback OnFeedback { get; } BoolFeedback IsWarmingUpFeedback { get; } - BoolFeedback IsCoolingDownFeedback { get; } - - IOccupancyStatusProvider RoomOccupancy { get; } - bool OccupancyStatusProviderIsRemote { get; } + BoolFeedback IsCoolingDownFeedback { get; } bool IsMobileControlEnabled { get; } IMobileControlRoomBridge MobileControlRoomBridge { get; } @@ -35,31 +30,16 @@ namespace PepperDash.Essentials.Core SecondsCountdownTimer ShutdownPromptTimer { get; } int ShutdownPromptSeconds { get; } int ShutdownVacancySeconds { get; } - eShutdownType ShutdownType { get; } - - EssentialsRoomEmergencyBase Emergency { get; } - - Core.Privacy.MicrophonePrivacyController MicrophonePrivacy { get; } + eShutdownType ShutdownType { get; } string LogoUrlLightBkgnd { get; } string LogoUrlDarkBkgnd { get; } - eVacancyMode VacancyMode { get; } + void StartShutdown(eShutdownType type); - bool ZeroVolumeWhenSwtichingVolumeDevices { get; } + void Shutdown(); - void StartShutdown(eShutdownType type); - void StartRoomVacancyTimer(eVacancyMode mode); - - void Shutdown(); - - void SetRoomOccupancy(IOccupancyStatusProvider statusProvider, int timeoutMinutes); - - void PowerOnToDefaultOrLastSource(); - - void SetDefaultLevels(); - - void RoomVacatedForTimeoutPeriod(object o); + void PowerOnToDefaultOrLastSource(); } } \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Interfaces.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Interfaces.cs index b5121e9c..e962e604 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Interfaces.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Interfaces.cs @@ -41,7 +41,6 @@ namespace PepperDash.Essentials.Core void RunRouteAction(string routeKey, string sourceListKey); void RunRouteAction(string routeKey, string sourceListKey, Action successCallback); - } /// @@ -78,4 +77,30 @@ namespace PepperDash.Essentials.Core bool HasEnvironmentalControlDevices { get; } } + public interface IRoomOccupancy:IKeyed + { + IOccupancyStatusProvider RoomOccupancy { get; } + bool OccupancyStatusProviderIsRemote { get; } + + void SetRoomOccupancy(IOccupancyStatusProvider statusProvider, int timeoutMinutes); + + void RoomVacatedForTimeoutPeriod(object o); + + void StartRoomVacancyTimer(eVacancyMode mode); + + eVacancyMode VacancyMode { get; } + + event EventHandler RoomOccupancyIsSet; + } + + public interface IEmergency + { + EssentialsRoomEmergencyBase Emergency { get; } + } + + public interface IMicrophonePrivacy + { + Core.Privacy.MicrophonePrivacyController MicrophonePrivacy { get; } + } + } \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Routing/RoutingInterfaces.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Routing/RoutingInterfaces.cs index 467bf045..45245066 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Routing/RoutingInterfaces.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Routing/RoutingInterfaces.cs @@ -204,4 +204,9 @@ namespace PepperDash.Essentials.Core SigType = sigType; } } + + public interface IRoutingHasVideoInputSyncFeedbacks + { + FeedbackCollection VideoInputSyncFeedbacks { get; } + } } \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Routing/RoutingPort.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Routing/RoutingPort.cs index 79dd4eda..ab64f15e 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Routing/RoutingPort.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Routing/RoutingPort.cs @@ -42,7 +42,7 @@ namespace PepperDash.Essentials.Core public enum eRoutingPortConnectionType { None, BackplaneOnly, DisplayPort, Dvi, Hdmi, Rgb, Vga, LineAudio, DigitalAudio, Sdi, - Composite, Component, DmCat, DmMmFiber, DmSmFiber, Speaker, Streaming + Composite, Component, DmCat, DmMmFiber, DmSmFiber, Speaker, Streaming, UsbC, HdBaseT } /// diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Routing/RoutingPortNames.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Routing/RoutingPortNames.cs index 00e85191..7029443b 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Routing/RoutingPortNames.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Routing/RoutingPortNames.cs @@ -199,5 +199,45 @@ namespace PepperDash.Essentials.Core.Routing /// MediaPlayer /// public const string MediaPlayer = "mediaPlayer"; - } + /// + /// UsbCIn + /// + public const string UsbCIn = "usbCIn"; + /// + /// UsbCIn1 + /// + public const string UsbCIn1 = "usbCIn1"; + /// + /// UsbCIn2 + /// + public const string UsbCIn2 = "usbCIn2"; + /// + /// UsbCIn3 + /// + public const string UsbCIn3 = "usbCIn3"; + /// + /// UsbCOut + /// + public const string UsbCOut = "usbCOut"; + /// + /// UsbCOut1 + /// + public const string UsbCOut1 = "usbCOut1"; + /// + /// UsbCOut2 + /// + public const string UsbCOut2 = "usbCOut2"; + /// + /// UsbCOut3 + /// + public const string UsbCOut3 = "usbCOut3"; + /// + /// HdBaseTIn + /// + public const string HdBaseTIn = "hdBaseTIn"; + /// + /// HdBaseTOut + /// + public const string HdBaseTOut = "hdBaseTOut"; + } } \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Touchpanels/Mpc3Touchpanel.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Touchpanels/Mpc3Touchpanel.cs index c9a5f605..2f6074dd 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Touchpanels/Mpc3Touchpanel.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Touchpanels/Mpc3Touchpanel.cs @@ -1,144 +1,352 @@ using System; using System.Collections.Generic; -using System.Linq; -using System.Text; -using Crestron.SimplSharp; +using System.Globalization; using Crestron.SimplSharpPro; - +using Newtonsoft.Json; using PepperDash.Core; -using PepperDash.Essentials.Core; namespace PepperDash.Essentials.Core.Touchpanels { - /// - /// A wrapper class for the touchpanel portion of an MPC3 class process to allow for configurable - /// behavior of the keybad buttons - /// - public class Mpc3TouchpanelController : Device - { - MPC3Basic _Touchpanel; + /// + /// A wrapper class for the touchpanel portion of an MPC3 class process to allow for configurable + /// behavior of the keybad buttons + /// + public class Mpc3TouchpanelController : Device + { + readonly MPC3Basic _touchpanel; - Dictionary _Buttons; + readonly Dictionary _buttons; - public Mpc3TouchpanelController(string key, string name, CrestronControlSystem processor, Dictionary buttons) - : base(key, name) - { - _Touchpanel = processor.ControllerTouchScreenSlotDevice as MPC3Basic; - _Buttons = buttons; + public Mpc3TouchpanelController(string key, string name, CrestronControlSystem processor, Dictionary buttons) + : base(key, name) + { + _touchpanel = processor.ControllerTouchScreenSlotDevice as MPC3Basic; + if (_touchpanel == null) + { + Debug.Console(1, this, "Failed to construct MPC3 Touchpanel Controller with key {0}, check configuration", key); + return; + } - _Touchpanel.ButtonStateChange += new Crestron.SimplSharpPro.DeviceSupport.ButtonEventHandler(_Touchpanel_ButtonStateChange); + if (_touchpanel.Registerable) + { + var registrationResponse = _touchpanel.Register(); + Debug.Console(0, this, "touchpanel registration response: {0}", registrationResponse); + } - AddPostActivationAction(() => - { - // Link up the button feedbacks to the specified BoolFeedbacks - foreach (var button in _Buttons) - { - var feedbackConfig = button.Value.Feedback; - var device = DeviceManager.GetDeviceForKey(feedbackConfig.DeviceKey) as Device; - if (device != null) - { - var bKey = button.Key.ToLower(); + _touchpanel.BaseEvent += _touchpanel_BaseEvent; + _touchpanel.ButtonStateChange += _touchpanel_ButtonStateChange; + _touchpanel.PanelStateChange += _touchpanel_PanelStateChange; - var feedback = device.GetFeedbackProperty(feedbackConfig.FeedbackName); + _buttons = buttons; + if (_buttons == null) + { + Debug.Console(1, this, + "Button properties are null, failed to setup MPC3 Touch Controller, check configuration"); + return; + } - var bFeedback = feedback as BoolFeedback; - var iFeedback = feedback as IntFeedback; - if (bFeedback != null) - { + AddPostActivationAction(() => + { + foreach (var button in _buttons) + { + var buttonKey = button.Key.ToLower(); + var buttonConfig = button.Value; - if (bKey == "power") - { - bFeedback.LinkCrestronFeedback(_Touchpanel.FeedbackPower); - continue; - } - else if (bKey == "mute") - { - bFeedback.LinkCrestronFeedback(_Touchpanel.FeedbackMute); - continue; - } + InitializeButton(buttonKey, buttonConfig); + InitializeButtonFeedback(buttonKey, buttonConfig); + } + }); + } - // Link to the Crestron Feedback corresponding to the button number - bFeedback.LinkCrestronFeedback(_Touchpanel.Feedbacks[UInt16.Parse(button.Key)]); - } - else if (iFeedback != null) - { - if (bKey == "volumefeedback") - { - var volFeedback = feedback as IntFeedback; - // TODO: Figure out how to subsribe to a volume IntFeedback and link it to the voluem - volFeedback.LinkInputSig(_Touchpanel.VolumeBargraph); - } - } - else - { - Debug.Console(1, this, "Unable to get BoolFeedback with name: {0} from device: {1}", feedbackConfig.FeedbackName, device.Key); - } - } - else - { - Debug.Console(1, this, "Unable to get device with key: {0}", feedbackConfig.DeviceKey); - } - } - }); - } + /// + /// Enables/disables buttons based on event type configuration + /// + /// + /// + public void InitializeButton(string key, KeypadButton config) + { + if (config == null) + { + Debug.Console(1, this, "Button '{0}' config is null, unable to initialize", key); + return; + } - void _Touchpanel_ButtonStateChange(GenericBase device, Crestron.SimplSharpPro.DeviceSupport.ButtonEventArgs args) - { - Debug.Console(1, this, "Button {0} ({1}), {2}", args.Button.Number, args.Button.Name, args.NewButtonState); - var type = args.NewButtonState.ToString(); + int buttonNumber; + TryParseInt(key, out buttonNumber); - if (_Buttons.ContainsKey(args.Button.Number.ToString())) - { - Press(args.Button.Number.ToString(), type); - } - else if(_Buttons.ContainsKey(args.Button.Name.ToString())) - { - Press(args.Button.Name.ToString(), type); - } - } + var buttonEventTypes = config.EventTypes; + BoolOutputSig enabledFb = null; + BoolOutputSig disabledFb = null; - /// - /// Runs the function associated with this button/type. One of the following strings: - /// Pressed, Released, Tapped, DoubleTapped, Held, HeldReleased - /// - /// - /// - public void Press(string number, string type) - { - // TODO: In future, consider modifying this to generate actions at device activation time - // to prevent the need to dynamically call the method via reflection on each button press - if (!_Buttons.ContainsKey(number)) { return; } - var but = _Buttons[number]; - if (but.EventTypes.ContainsKey(type)) - { - foreach (var a in but.EventTypes[type]) { DeviceJsonApi.DoDeviceAction(a); } - } - } + switch (key) + { + case ("power"): + { + if (buttonEventTypes == null || buttonEventTypes.Keys == null) + _touchpanel.DisablePowerButton(); + else + _touchpanel.EnablePowerButton(); - } + enabledFb = _touchpanel.PowerButtonEnabledFeedBack; + disabledFb = _touchpanel.PowerButtonDisabledFeedBack; - /// - /// Represents the configuration of a keybad buggon - /// - public class KeypadButton - { - public Dictionary EventTypes { get; set; } - public KeypadButtonFeedback Feedback { get; set; } + break; + } + //case ("volumeup"): + // { + // break; + // } + //case ("volumedown"): + // { + // break; + // } + //case ("volumefeedback"): + // { + // break; + // } + case ("mute"): + { + if (buttonEventTypes == null || buttonEventTypes.Keys == null) + _touchpanel.DisableMuteButton(); + else + _touchpanel.EnableMuteButton(); - public KeypadButton() - { - EventTypes = new Dictionary(); - Feedback = new KeypadButtonFeedback(); - } - } - /// - /// - /// - public class KeypadButtonFeedback - { - public string DeviceKey { get; set; } - public string FeedbackName { get; set; } - } + enabledFb = _touchpanel.MuteButtonEnabledFeedBack; + disabledFb = _touchpanel.MuteButtonDisabledFeedBack; + + break; + } + default: + { + if (buttonNumber == 0 || buttonNumber > 9) + break; + + if (buttonEventTypes == null || buttonEventTypes.Keys == null) + _touchpanel.DisableNumericalButton((uint)buttonNumber); + else + _touchpanel.EnableNumericalButton((uint)buttonNumber); + + + if (_touchpanel.NumericalButtonEnabledFeedBack != null) + enabledFb = _touchpanel.NumericalButtonEnabledFeedBack[(uint)buttonNumber]; + + if (_touchpanel.NumericalButtonDisabledFeedBack != null) + disabledFb = _touchpanel.NumericalButtonDisabledFeedBack[(uint)buttonNumber]; + + break; + } + } + + Debug.Console(0, this, "InitializeButton: key-'{0}' enabledFb-'{1}', disabledFb-'{2}'", + key, enabledFb ?? (object)"null", disabledFb ?? (object)"null"); + } + + /// + /// Links button feedback if configured + /// + /// + /// + public void InitializeButtonFeedback(string key, KeypadButton config) + { + //Debug.Console(1, this, "Initializing button '{0}' feedback...", key); + + if (config == null) + { + Debug.Console(1, this, "Button '{0}' config is null, skipping.", key); + return; + } + + int buttonNumber; + TryParseInt(key, out buttonNumber); + + // Link up the button feedbacks to the specified device feedback + var buttonFeedback = config.Feedback; + if (buttonFeedback == null || string.IsNullOrEmpty(buttonFeedback.DeviceKey)) + { + Debug.Console(1, this, "Button '{0}' feedback not configured, skipping.", + key); + return; + } + + Feedback deviceFeedback; + + try + { + var device = DeviceManager.GetDeviceForKey(buttonFeedback.DeviceKey) as Device; + if (device == null) + { + Debug.Console(1, this, "Button '{0}' feedback deviceKey '{1}' not found.", + key, buttonFeedback.DeviceKey); + return; + } + + deviceFeedback = device.GetFeedbackProperty(buttonFeedback.FeedbackName); + if (deviceFeedback == null) + { + Debug.Console(1, this, "Button '{0}' feedbackName property '{1}' not found.", + key, buttonFeedback.FeedbackName); + return; + } + + // TODO [ ] verify if this can replace the current method + //Debug.Console(0, this, "deviceFeedback.GetType().Name: '{0}'", deviceFeedback.GetType().Name); + //switch (feedback.GetType().Name.ToLower()) + //{ + // case("boolfeedback"): + // { + // break; + // } + // case("intfeedback"): + // { + // break; + // } + // case("stringfeedback"): + // { + // break; + // } + //} + } + catch (Exception ex) + { + Debug.Console(1, this, "InitializeButtonFeedback (button '{1}', deviceKey '{2}') Exception Message: {0}", + ex.Message, key, buttonFeedback.DeviceKey); + Debug.Console(2, this, "InitializeButtonFeedback (button '{1}', deviceKey '{2}') Exception StackTrace: {0}", + ex.StackTrace, key, buttonFeedback.DeviceKey); + if (ex.InnerException != null) Debug.Console(2, this, "InitializeButtonFeedback (button '{1}', deviceKey '{2}') InnerException: {0}", + ex.InnerException, key, buttonFeedback.DeviceKey); + + return; + } + + var boolFeedback = deviceFeedback as BoolFeedback; + var intFeedback = deviceFeedback as IntFeedback; + + switch (key) + { + case ("power"): + { + if (boolFeedback != null) boolFeedback.LinkCrestronFeedback(_touchpanel.FeedbackPower); + break; + } + case ("volumeup"): + case ("volumedown"): + case ("volumefeedback"): + { + if (intFeedback != null) + { + var volumeFeedback = intFeedback; + volumeFeedback.LinkInputSig(_touchpanel.VolumeBargraph); + } + break; + } + case ("mute"): + { + if (boolFeedback != null) boolFeedback.LinkCrestronFeedback(_touchpanel.FeedbackMute); + break; + } + default: + { + if (boolFeedback != null) boolFeedback.LinkCrestronFeedback(_touchpanel.Feedbacks[(uint)buttonNumber]); + break; + } + } + } + + /// + /// Try parse int helper method + /// + /// + /// + /// + public bool TryParseInt(string str, out int result) + { + try + { + result = int.Parse(str); + return true; + } + catch + { + result = 0; + return false; + } + } + + private void _touchpanel_BaseEvent(GenericBase device, BaseEventArgs args) + { + Debug.Console(1, this, "BaseEvent: eventId-'{0}', index-'{1}'", args.EventId, args.Index); + } + + private void _touchpanel_ButtonStateChange(GenericBase device, Crestron.SimplSharpPro.DeviceSupport.ButtonEventArgs args) + { + Debug.Console(1, this, "ButtonStateChange: buttonNumber-'{0}' buttonName-'{1}', buttonState-'{2}'", args.Button.Number, args.Button.Name, args.NewButtonState); + var type = args.NewButtonState.ToString(); + + if (_buttons.ContainsKey(args.Button.Number.ToString(CultureInfo.InvariantCulture))) + { + Press(args.Button.Number.ToString(CultureInfo.InvariantCulture), type); + } + else if (_buttons.ContainsKey(args.Button.Name.ToString())) + { + Press(args.Button.Name.ToString(), type); + } + } + + private void _touchpanel_PanelStateChange(GenericBase device, BaseEventArgs args) + { + Debug.Console(1, this, "PanelStateChange: eventId-'{0}', index-'{1}'", args.EventId, args.Index); + } + + /// + /// Runs the function associated with this button/type. One of the following strings: + /// Pressed, Released, Tapped, DoubleTapped, Held, HeldReleased + /// + /// + /// + public void Press(string buttonKey, string type) + { + Debug.Console(2, this, "Press: buttonKey-'{0}', type-'{1}'", buttonKey, type); + + // TODO: In future, consider modifying this to generate actions at device activation time + // to prevent the need to dynamically call the method via reflection on each button press + if (!_buttons.ContainsKey(buttonKey)) return; + + var button = _buttons[buttonKey]; + if (!button.EventTypes.ContainsKey(type)) return; + + foreach (var eventType in button.EventTypes[type]) DeviceJsonApi.DoDeviceAction(eventType); + } + } + + /// + /// Represents the configuration of a keypad button + /// + public class KeypadButton + { + [JsonProperty("eventTypes")] + public Dictionary EventTypes { get; set; } + + [JsonProperty("feedback")] + public KeypadButtonFeedback Feedback { get; set; } + + public KeypadButton() + { + EventTypes = new Dictionary(); + Feedback = new KeypadButtonFeedback(); + } + } + + /// + /// Represents the configuration of a keypad button feedback + /// + public class KeypadButtonFeedback + { + [JsonProperty("deviceKey")] + public string DeviceKey { get; set; } + + [JsonProperty("feedbackName")] + public string FeedbackName { get; set; } + } } \ No newline at end of file diff --git a/essentials-framework/Essentials DM/Essentials_DM/AirMedia/AirMediaController.cs b/essentials-framework/Essentials DM/Essentials_DM/AirMedia/AirMediaController.cs index ff07a54b..b1fa3186 100644 --- a/essentials-framework/Essentials DM/Essentials_DM/AirMedia/AirMediaController.cs +++ b/essentials-framework/Essentials DM/Essentials_DM/AirMedia/AirMediaController.cs @@ -129,7 +129,7 @@ namespace PepperDash.Essentials.DM.AirMedia else AirMedia.DisplayControl.DisableAutomaticRouting(); - return true; + return base.CustomActivate(); } public override void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge) diff --git a/essentials-framework/Essentials DM/Essentials_DM/Chassis/HdPsXxxController.cs b/essentials-framework/Essentials DM/Essentials_DM/Chassis/HdPsXxxController.cs new file mode 100644 index 00000000..36e99bc6 --- /dev/null +++ b/essentials-framework/Essentials DM/Essentials_DM/Chassis/HdPsXxxController.cs @@ -0,0 +1,589 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using Crestron.SimplSharpPro; +using Crestron.SimplSharpPro.DeviceSupport; +using Crestron.SimplSharpPro.DM; +using Newtonsoft.Json; +using PepperDash.Core; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.Bridges; +using PepperDash.Essentials.Core.Config; +using PepperDash_Essentials_Core.Bridges; +using PepperDash_Essentials_DM.Config; + +namespace PepperDash_Essentials_DM.Chassis +{ + [Description("Wrapper class for all HdPsXxx switchers")] + public class HdPsXxxController : CrestronGenericBridgeableBaseDevice, IRoutingNumericWithFeedback, IRoutingHasVideoInputSyncFeedbacks + { + private readonly HdPsXxx _chassis; + + public RoutingPortCollection InputPorts { get; private set; } + public RoutingPortCollection OutputPorts { get; private set; } + + public Dictionary InputNames { get; set; } + public Dictionary OutputNames { get; set; } + + public FeedbackCollection InputNameFeedbacks { get; private set; } + public FeedbackCollection InputHdcpEnableFeedback { get; private set; } + + public FeedbackCollection OutputNameFeedbacks { get; private set; } + public FeedbackCollection OutputRouteNameFeedback { get; private set; } + + public FeedbackCollection VideoInputSyncFeedbacks { get; private set; } + public FeedbackCollection VideoOutputRouteFeedbacks { get; private set; } + + public StringFeedback DeviceNameFeedback { get; private set; } + public BoolFeedback AutoRouteFeedback { get; private set; } + + public event EventHandler NumericSwitchChange; + public event EventHandler DmInputChange; + + + /// + /// Constructor + /// + /// + /// + /// HdPs401 device instance + /// + public HdPsXxxController(string key, string name, HdPsXxx chassis, HdPsXxxPropertiesConfig props) + : base(key, name, chassis) + { + _chassis = chassis; + Name = name; + + if (props == null) + { + Debug.Console(1, this, "HdPsXxxController properties are null, failed to build device"); + return; + } + + InputPorts = new RoutingPortCollection(); + InputNameFeedbacks = new FeedbackCollection(); + InputHdcpEnableFeedback = new FeedbackCollection(); + InputNames = new Dictionary(); + + OutputPorts = new RoutingPortCollection(); + OutputNameFeedbacks = new FeedbackCollection(); + OutputRouteNameFeedback = new FeedbackCollection(); + OutputNames = new Dictionary(); + + VideoInputSyncFeedbacks = new FeedbackCollection(); + VideoOutputRouteFeedbacks = new FeedbackCollection(); + + if (_chassis.NumberOfOutputs == 1) + AutoRouteFeedback = new BoolFeedback(() => _chassis.PriorityRouteOnFeedback.BoolValue); + + InputNames = props.Inputs; + SetupInputs(InputNames); + + OutputNames = props.Outputs; + SetupOutputs(OutputNames); + } + + // get input priorities + private byte[] SetInputPriorities(HdPsXxxPropertiesConfig props) + { + throw new NotImplementedException(); + } + + // input setup + private void SetupInputs(Dictionary dict) + { + if (dict == null) + { + Debug.Console(1, this, "Failed to setup inputs, properties are null"); + return; + } + + // iterate through HDMI inputs + foreach (var item in _chassis.HdmiInputs) + { + var input = item; + var index = item.Number; + var key = string.Format("hdmiIn{0}", index); + var name = string.IsNullOrEmpty(InputNames[index]) + ? string.Format("HDMI Input {0}", index) + : InputNames[index]; + + input.Name.StringValue = name; + + InputNameFeedbacks.Add(new StringFeedback(index.ToString(CultureInfo.InvariantCulture), + () => InputNames[index])); + + var port = new RoutingInputPort(key, eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.Hdmi, input, this) + { + FeedbackMatchObject = input + }; + Debug.Console(1, this, "Adding Input port: {0} - {1}", port.Key, name); + InputPorts.Add(port); + + InputHdcpEnableFeedback.Add(new BoolFeedback(index.ToString(CultureInfo.InvariantCulture), + () => input.InputPort.HdcpSupportOnFeedback.BoolValue)); + + VideoInputSyncFeedbacks.Add(new BoolFeedback(index.ToString(CultureInfo.InvariantCulture), + () => input.VideoDetectedFeedback.BoolValue)); + } + + // iterate through DM Lite inputs + foreach (var item in _chassis.DmLiteInputs) + { + var input = item; + var index = item.Number; + var key = string.Format("dmLiteIn{0}", index); + var name = string.IsNullOrEmpty(InputNames[index]) + ? string.Format("DM Input {0}", index) + : InputNames[index]; + + input.Name.StringValue = name; + + InputNameFeedbacks.Add(new StringFeedback(index.ToString(CultureInfo.InvariantCulture), + () => InputNames[index])); + + var port = new RoutingInputPort(key, eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.Hdmi, input, this) + { + FeedbackMatchObject = input + }; + Debug.Console(0, this, "Adding Input port: {0} - {1}", port.Key, name); + InputPorts.Add(port); + + InputHdcpEnableFeedback.Add(new BoolFeedback(index.ToString(CultureInfo.InvariantCulture), + () => input.InputPort.HdcpSupportOnFeedback.BoolValue)); + + VideoInputSyncFeedbacks.Add(new BoolFeedback(index.ToString(CultureInfo.InvariantCulture), + () => input.VideoDetectedFeedback.BoolValue)); + } + + _chassis.DMInputChange += _chassis_InputChange; + } + + // output setup + private void SetupOutputs(Dictionary dict) + { + if (dict == null) + { + Debug.Console(1, this, "Failed to setup outputs, properties are null"); + return; + } + + foreach (var item in _chassis.HdmiDmLiteOutputs) + { + var output = item; + var index = item.Number; + var name = string.IsNullOrEmpty(OutputNames[index]) + ? string.Format("Output {0}", index) + : OutputNames[index]; + + output.Name.StringValue = name; + + var hdmiKey = string.Format("hdmiOut{0}", index); + var hdmiPort = new RoutingOutputPort(hdmiKey, eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.Hdmi, output, this) + { + FeedbackMatchObject = output, + Port = output.HdmiOutput.HdmiOutputPort + }; + Debug.Console(1, this, "Adding Output port: {0} - {1}", hdmiPort.Key, name); + OutputPorts.Add(hdmiPort); + + var dmLiteKey = string.Format("dmLiteOut{0}", index); + var dmLitePort = new RoutingOutputPort(dmLiteKey, eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.DmCat, output, this) + { + FeedbackMatchObject = output, + Port = output.DmLiteOutput.DmLiteOutputPort + }; + Debug.Console(1, this, "Adding Output port: {0} - {1}", dmLitePort.Key, name); + OutputPorts.Add(dmLitePort); + + OutputRouteNameFeedback.Add(new StringFeedback(index.ToString(CultureInfo.InvariantCulture), + () => output.VideoOutFeedback.NameFeedback.StringValue)); + + VideoOutputRouteFeedbacks.Add(new IntFeedback(index.ToString(CultureInfo.InvariantCulture), + () => output.VideoOutFeedback == null ? 0 : (int)output.VideoOutFeedback.Number)); + } + + _chassis.DMOutputChange += _chassis_OutputChange; + } + + + public void ListRoutingPorts() + { + try + { + foreach (var port in InputPorts) + { + Debug.Console(0, this, @"Input Port Key: {0} +Port: {1} +Type: {2} +ConnectionType: {3} +Selector: {4} +", port.Key, port.Port, port.Type, port.ConnectionType, port.Selector); + } + + foreach (var port in OutputPorts) + { + Debug.Console(0, this, @"Output Port Key: {0} +Port: {1} +Type: {2} +ConnectionType: {3} +Selector: {4} +", port.Key, port.Port, port.Type, port.ConnectionType, port.Selector); + } + } + catch (Exception ex) + { + Debug.Console(0, this, "ListRoutingPorts Exception Message: {0}", ex.Message); + Debug.Console(0, this, "ListRoutingPorts Exception StackTrace: {0}", ex.StackTrace); + if (ex.InnerException != null) Debug.Console(0, this, "ListRoutingPorts InnerException: {0}", ex.InnerException); + } + } + + #region BridgeLinking + + /// + /// Link device to API + /// + /// + /// + /// + /// + public override void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge) + { + var joinMap = new HdPsXxxControllerJoinMap(joinStart); + + if (bridge != null) + { + bridge.AddJoinMap(Key, joinMap); + } + else + { + Debug.Console(0, this, "Please update config to use 'eiscApiAdvanced' to get all join map features for this device"); + } + + IsOnline.LinkInputSig(trilist.BooleanInput[joinMap.IsOnline.JoinNumber]); + DeviceNameFeedback.LinkInputSig(trilist.StringInput[joinMap.Name.JoinNumber]); + + _chassis.OnlineStatusChange += _chassis_OnlineStatusChange; + + LinkChassisInputsToApi(trilist, joinMap); + LinkChassisOutputsToApi(trilist, joinMap); + + trilist.OnlineStatusChange += (sender, args) => + { + if (!args.DeviceOnLine) return; + }; + } + + + // links inputs to API + private void LinkChassisInputsToApi(BasicTriList trilist, HdPsXxxControllerJoinMap joinMap) + { + for (uint i = 1; i <= _chassis.NumberOfInputs; i++) + { + var input = i; + var inputName = InputNames[input]; + var indexWithOffset = input - 1; + + trilist.SetSigTrueAction(joinMap.EnableInputHdcp.JoinNumber + indexWithOffset, () => EnableHdcp(input)); + trilist.SetSigTrueAction(joinMap.DisableInputHdcp.JoinNumber + indexWithOffset, () => DisableHdcp(input)); + + InputHdcpEnableFeedback[inputName].LinkInputSig(trilist.BooleanInput[joinMap.EnableInputHdcp.JoinNumber + indexWithOffset]); + InputHdcpEnableFeedback[inputName].LinkComplementInputSig(trilist.BooleanInput[joinMap.EnableInputHdcp.JoinNumber + indexWithOffset]); + + VideoInputSyncFeedbacks[inputName].LinkInputSig(trilist.BooleanInput[joinMap.InputSync.JoinNumber + indexWithOffset]); + + InputNameFeedbacks[inputName].LinkInputSig(trilist.StringInput[joinMap.InputName.JoinNumber + indexWithOffset]); + } + } + + + // links outputs to API + private void LinkChassisOutputsToApi(BasicTriList trilist, HdPsXxxControllerJoinMap joinMap) + { + for (uint i = 1; i <= _chassis.NumberOfOutputs; i++) + { + var output = i; + var outputName = OutputNames[output]; + var indexWithOffset = output - 1; + + trilist.SetUShortSigAction(joinMap.OutputRoute.JoinNumber + indexWithOffset, (a) => + ExecuteNumericSwitch(a, (ushort)output, eRoutingSignalType.AudioVideo)); + + OutputNameFeedbacks[outputName].LinkInputSig(trilist.StringInput[joinMap.OutputName.JoinNumber + indexWithOffset]); + OutputRouteNameFeedback[outputName].LinkInputSig(trilist.StringInput[joinMap.OutputRoutedName.JoinNumber + indexWithOffset]); + + VideoOutputRouteFeedbacks[outputName].LinkInputSig(trilist.UShortInput[joinMap.OutputRoute.JoinNumber + indexWithOffset]); + } + + AutoRouteFeedback.LinkInputSig(trilist.BooleanInput[joinMap.EnableAutoRoute.JoinNumber]); + } + + #endregion + + + /// + /// Executes a device switch using objects + /// + /// + /// + /// + public void ExecuteSwitch(object inputSelector, object outputSelector, eRoutingSignalType signalType) + { + var input = inputSelector as HdPsXxxInput; + var output = outputSelector as HdPsXxxOutput; + + Debug.Console(2, this, "ExecuteSwitch: input={0}, output={1}", input, output); + + if (output == null) + { + Debug.Console(0, this, "Unable to make switch, output selector is not HdPsXxxHdmiOutput"); + return; + } + + // TODO [ ] Validate if sending the same input toggles the switch + var current = output.VideoOut; + if (current != input) + output.VideoOut = input; + } + + + /// + /// Executes a device switch using numeric values + /// + /// + /// + /// + public void ExecuteNumericSwitch(ushort inputSelector, ushort outputSelector, eRoutingSignalType signalType) + { + var input = inputSelector == 0 ? null : _chassis.Inputs[inputSelector]; + var output = _chassis.Outputs[outputSelector]; + + Debug.Console(2, this, "ExecuteNumericSwitch: input={0}, output={1}", input, output); + + ExecuteSwitch(input, output, signalType); + } + + + /// + /// Enables Hdcp on the provided port + /// + /// + public void EnableHdcp(uint port) + { + if (port <= 0 || port > _chassis.NumberOfInputs) return; + + _chassis.HdmiInputs[port].InputPort.HdcpSupportOn(); + InputHdcpEnableFeedback[InputNames[port]].FireUpdate(); + } + + + /// + /// Disables Hdcp on the provided port + /// + /// + public void DisableHdcp(uint port) + { + if (port <= 0 || port > _chassis.NumberOfInputs) return; + + _chassis.HdmiInputs[port].InputPort.HdcpSupportOff(); + InputHdcpEnableFeedback[InputNames[port]].FireUpdate(); + } + + + /// + /// Enables switcher auto route + /// + public void EnableAutoRoute() + { + if (_chassis.NumberOfInputs == 1) return; + + _chassis.AutoRouteOn(); + } + + + /// + /// Disables switcher auto route + /// + public void DisableAutoRoute() + { + if (_chassis.NumberOfInputs == 1) return; + + _chassis.AutoRouteOff(); + } + + #region Events + + + // _chassis online/offline event + private void _chassis_OnlineStatusChange(GenericBase currentDevice, + OnlineOfflineEventArgs args) + { + IsOnline.FireUpdate(); + + if (!args.DeviceOnLine) return; + + foreach (var feedback in Feedbacks) + { + feedback.FireUpdate(); + } + } + + + // _chassis input change event + private void _chassis_InputChange(Switch device, DMInputEventArgs args) + { + var eventId = args.EventId; + + switch (eventId) + { + case DMInputEventIds.VideoDetectedEventId: + { + Debug.Console(1, this, "Event ID {0}: Updating VideoInputSyncFeedbacks", eventId); + foreach (var item in VideoInputSyncFeedbacks) + { + item.FireUpdate(); + } + break; + } + case DMInputEventIds.InputNameFeedbackEventId: + case DMInputEventIds.InputNameEventId: + case DMInputEventIds.NameFeedbackEventId: + { + Debug.Console(1, this, "Event ID {0}: Updating name feedbacks", eventId); + + var input = args.Number; + var name = _chassis.HdmiInputs[input].NameFeedback.StringValue; + + Debug.Console(1, this, "Input {0} Name {1}", input, name); + break; + } + default: + { + Debug.Console(1, this, "Uhandled DM Input Event ID {0}", eventId); + break; + } + } + + OnDmInputChange(args); + } + + + // _chassis output change event + private void _chassis_OutputChange(Switch device, DMOutputEventArgs args) + { + if (args.EventId != DMOutputEventIds.VideoOutEventId) return; + + var output = args.Number; + + var input = _chassis.HdmiDmLiteOutputs[output].VideoOutFeedback == null + ? 0 + : _chassis.HdmiDmLiteOutputs[output].VideoOutFeedback.Number; + + var outputName = OutputNames[output]; + + var feedback = VideoOutputRouteFeedbacks[outputName]; + if (feedback == null) return; + + var inputPort = InputPorts.FirstOrDefault( + p => p.FeedbackMatchObject == _chassis.HdmiDmLiteOutputs[output].VideoOutFeedback); + + var outputPort = OutputPorts.FirstOrDefault( + p => p.FeedbackMatchObject == _chassis.HdmiDmLiteOutputs[output]); + + feedback.FireUpdate(); + + OnSwitchChange(new RoutingNumericEventArgs( + output, input, outputPort, inputPort, eRoutingSignalType.AudioVideo)); + } + + + // Raise an event when the status of a switch object changes. + private void OnSwitchChange(RoutingNumericEventArgs args) + { + var newEvent = NumericSwitchChange; + if (newEvent != null) newEvent(this, args); + } + + // Raise an event when the DM input changes. + private void OnDmInputChange(DMInputEventArgs args) + { + var newEvent = DmInputChange; + if (newEvent != null) newEvent(this, args); + } + + + #endregion + + + #region Factory + + + public class HdSp401ControllerFactory : EssentialsDeviceFactory + { + public HdSp401ControllerFactory() + { + TypeNames = new List() { "hdps401", "hdps402", "hdps621", "hdps622" }; + } + public override EssentialsDevice BuildDevice(DeviceConfig dc) + { + var key = dc.Key; + var name = dc.Name; + var type = dc.Type.ToLower(); + + Debug.Console(1, "Factory Attempting to create new {0} device", type); + + var props = JsonConvert.DeserializeObject(dc.Properties.ToString()); + if (props == null) + { + Debug.Console(1, "Factory failed to create new HD-PSXxx device, properties config was null"); + return null; + } + + var ipid = props.Control.IpIdInt; + + switch (type) + { + case ("hdps401"): + { + return new HdPsXxxController(key, name, new HdPs401(ipid, Global.ControlSystem), props); + } + case ("hdps402"): + { + return new HdPsXxxController(key, name, new HdPs402(ipid, Global.ControlSystem), props); + } + case ("hdps621"): + { + return new HdPsXxxController(key, name, new HdPs621(ipid, Global.ControlSystem), props); + } + case ("hdps622"): + { + return new HdPsXxxController(key, name, new HdPs622(ipid, Global.ControlSystem), props); + } + default: + { + Debug.Console(1, "Factory failed to create new {0} device", type); + return null; + } + } + } + } + + + #endregion + } + + + public class StreamCecWrapper : IKeyed, ICec + { + public string Key { get; private set; } + public Cec StreamCec { get; private set; } + + public StreamCecWrapper(string key, Cec streamCec) + { + Key = key; + StreamCec = streamCec; + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials DM/Essentials_DM/Config/HdPsXxxPropertiesConfig.cs b/essentials-framework/Essentials DM/Essentials_DM/Config/HdPsXxxPropertiesConfig.cs new file mode 100644 index 00000000..576b74f0 --- /dev/null +++ b/essentials-framework/Essentials DM/Essentials_DM/Config/HdPsXxxPropertiesConfig.cs @@ -0,0 +1,28 @@ +using System.Collections.Generic; +using Newtonsoft.Json; +using PepperDash.Core; + +namespace PepperDash_Essentials_DM.Config +{ + public class HdPsXxxPropertiesConfig + { + [JsonProperty("control")] + public ControlPropertiesConfig Control { get; set; } + + [JsonProperty("inputs")] + public Dictionary Inputs { get; set; } + + [JsonProperty("outputs")] + public Dictionary Outputs { get; set; } + + // "inputPriorities": "1,4,3,2" + [JsonProperty("inputPriorities")] + public string InputPriorities { get; set; } + + public HdPsXxxPropertiesConfig() + { + Inputs = new Dictionary(); + Outputs = new Dictionary(); + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Receivers/DmRmc4KScalerCController.cs b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Receivers/DmRmc4KScalerCController.cs index 79069430..79debd63 100644 --- a/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Receivers/DmRmc4KScalerCController.cs +++ b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Receivers/DmRmc4KScalerCController.cs @@ -7,6 +7,7 @@ using PepperDash.Core; using PepperDash.Essentials.Core; using PepperDash.Essentials.Core.Bridges; using PepperDash_Essentials_DM; +using System.Collections.Generic; namespace PepperDash.Essentials.DM { @@ -16,7 +17,7 @@ namespace PepperDash.Essentials.DM /// [Description("Wrapper Class for DM-RMC-4K-SCALER-C")] public class DmRmc4kScalerCController : DmRmcControllerBase, IRoutingInputsOutputs, IBasicVolumeWithFeedback, - IIROutputPorts, IComPorts, ICec, IRelayPorts, IHasDmInHdcp + IIROutputPorts, IComPorts, ICec, IRelayPorts, IHasDmInHdcp, IBasicVideoMuteWithFeedback { private readonly DmRmc4kScalerC _rmc; @@ -68,6 +69,7 @@ namespace PepperDash.Essentials.DM AddToFeedbackList(DmInHdcpStateFeedback); + VideoMuteIsOn = new BoolFeedback("HdmiOutputVideoMuteIsOn", () => _rmc.HdmiOutput.BlankEnabledFeedback.BoolValue); _rmc.HdmiOutput.OutputStreamChange += HdmiOutput_OutputStreamChange; _rmc.HdmiOutput.ConnectedDevice.DeviceInformationChange += ConnectedDevice_DeviceInformationChange; @@ -83,6 +85,10 @@ namespace PepperDash.Essentials.DM { VideoOutputResolutionFeedback.FireUpdate(); } + else if (args.EventId == EndpointOutputStreamEventIds.BlankEnabledFeedbackEventId) + { + VideoMuteIsOn.FireUpdate(); + } } void ConnectedDevice_DeviceInformationChange(ConnectedDeviceInformation connectedDevice, ConnectedDeviceEventArgs args) @@ -216,5 +222,40 @@ namespace PepperDash.Essentials.DM _rmc.DmInput.HdcpCapability = hdcpState; } + + #region IBasicVideoMuteWithFeedback Members + + public BoolFeedback VideoMuteIsOn + { + get; + private set; + } + + public void VideoMuteOn() + { + Debug.Console(2, this, "Video Mute On"); + _rmc.HdmiOutput.BlankEnabled(); + } + + public void VideoMuteOff() + { + Debug.Console(2, this, "Video Mute Off"); + _rmc.HdmiOutput.BlankDisabled(); + } + + #endregion + + #region IBasicVideoMute Members + + public void VideoMuteToggle() + { + Debug.Console(2, this, "Video Mute Toggle"); + if (_rmc.HdmiOutput.BlankEnabledFeedback.BoolValue == true) + VideoMuteOff(); + else + VideoMuteOn(); + } + + #endregion } } \ No newline at end of file diff --git a/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Receivers/DmRmcHelper.cs b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Receivers/DmRmcHelper.cs index 57f522fd..ee423436 100644 --- a/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Receivers/DmRmcHelper.cs +++ b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Receivers/DmRmcHelper.cs @@ -59,8 +59,13 @@ namespace PepperDash.Essentials.DM else { Debug.Console(0, this, "Please update config to use 'eiscapiadvanced' to get all join map features for this device."); - } + } + + LinkDmRmcToApi(rmc, trilist, joinMap); + } + protected void LinkDmRmcToApi(DmRmcControllerBase rmc, BasicTriList trilist, DmRmcControllerJoinMap joinMap) + { Debug.Console(1, rmc, "Linking to Trilist '{0}'", trilist.ID.ToString("X")); IsOnline.LinkInputSig(trilist.BooleanInput[joinMap.IsOnline.JoinNumber]); @@ -137,7 +142,19 @@ namespace PepperDash.Essentials.DM trilist.UShortInput[joinMap.HdcpSupportCapability.JoinNumber].UShortValue = (ushort)hdcpCapability; - trilist.UShortInput[joinMap.HdcpInputPortCount.JoinNumber].UShortValue = (ushort)routing.InputPorts.Count; + trilist.UShortInput[joinMap.HdcpInputPortCount.JoinNumber].UShortValue = (ushort)routing.InputPorts.Count; + + var dmRmcScalerCBasicVideoMuteWithFeedback = rmc as IBasicVideoMuteWithFeedback; + + if (dmRmcScalerCBasicVideoMuteWithFeedback != null) + { + Debug.Console(1, this, "Device is IBasicVideoMuteWithFeedback, linking video mute"); + trilist.SetSigTrueAction(joinMap.VideoMuteToggle.JoinNumber, () => dmRmcScalerCBasicVideoMuteWithFeedback.VideoMuteToggle()); + trilist.SetSigTrueAction(joinMap.VideoMuteOn.JoinNumber, () => dmRmcScalerCBasicVideoMuteWithFeedback.VideoMuteOn()); + trilist.SetSigTrueAction(joinMap.VideoMuteOff.JoinNumber, () => dmRmcScalerCBasicVideoMuteWithFeedback.VideoMuteOff()); + dmRmcScalerCBasicVideoMuteWithFeedback.VideoMuteIsOn.LinkInputSig(trilist.BooleanInput[joinMap.VideoMuteOn.JoinNumber]); + dmRmcScalerCBasicVideoMuteWithFeedback.VideoMuteIsOn.LinkComplementInputSig(trilist.BooleanInput[joinMap.VideoMuteOff.JoinNumber]); + } var routingWithFeedback = routing as IRmcRouting; if (routingWithFeedback == null) return; @@ -149,6 +166,7 @@ namespace PepperDash.Essentials.DM trilist.SetUShortSigAction(joinMap.AudioVideoSource.JoinNumber, a => routingWithFeedback.ExecuteNumericSwitch(a, 1, eRoutingSignalType.AudioVideo)); + } #region Implementation of IDeviceInfoProvider diff --git a/essentials-framework/Essentials DM/Essentials_DM/PepperDash_Essentials_DM.csproj b/essentials-framework/Essentials DM/Essentials_DM/PepperDash_Essentials_DM.csproj index 068d39cf..0011c305 100644 --- a/essentials-framework/Essentials DM/Essentials_DM/PepperDash_Essentials_DM.csproj +++ b/essentials-framework/Essentials DM/Essentials_DM/PepperDash_Essentials_DM.csproj @@ -59,7 +59,7 @@ ..\..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.UI.dll - + False ..\..\..\packages\PepperDashCore\lib\net35\PepperDash_Core.dll @@ -104,6 +104,8 @@ + + @@ -153,6 +155,7 @@ + diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/iHasScheduleAwareness.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/iHasScheduleAwareness.cs index 9169fd7c..c4473c67 100644 --- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/iHasScheduleAwareness.cs +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/iHasScheduleAwareness.cs @@ -216,6 +216,10 @@ namespace PepperDash.Essentials.Devices.Common.Codec return joinable; } } + + [JsonProperty("dialable")] + public bool Dialable { get; set; } + //public string ConferenceNumberToDial { get; set; } [JsonProperty("conferencePassword")] public string ConferencePassword { get; set; } diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Essentials Devices Common.csproj b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Essentials Devices Common.csproj index a1cc37f2..845b61ef 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 @@ -63,7 +63,7 @@ ..\..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.Lighting.dll - + False ..\..\..\packages\PepperDashCore\lib\net35\PepperDash_Core.dll diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/BookingsDataClasses.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/BookingsDataClasses.cs index 59823bb9..05456ad4 100644 --- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/BookingsDataClasses.cs +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/BookingsDataClasses.cs @@ -348,6 +348,8 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec if (b.DialInfo.ConnectMode.Value.ToLower() == "obtp" || b.DialInfo.ConnectMode.Value.ToLower() == "manual") meeting.IsOneButtonToPushMeeting = true; + meeting.Dialable = b.DialInfo.Calls.Call.Count > 0; + if (b.DialInfo.Calls.Call != null) { foreach (Call c in b.DialInfo.Calls.Call) diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/VideoCodecBase.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/VideoCodecBase.cs index c0048a68..187cc855 100644 --- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/VideoCodecBase.cs +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/VideoCodecBase.cs @@ -940,7 +940,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec //digitals tokenArray[digitalIndex] = new XSigDigitalToken(digitalIndex + 1, meeting.Joinable); - tokenArray[digitalIndex + 1] = new XSigDigitalToken(digitalIndex + 2, meeting.Id != "0"); + tokenArray[digitalIndex + 1] = new XSigDigitalToken(digitalIndex + 2, meeting.Dialable); //serials tokenArray[stringIndex] = new XSigSerialToken(stringIndex + 1, meeting.Organizer); diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ResponseObjects.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ResponseObjects.cs index 79a5395d..09069146 100644 --- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ResponseObjects.cs +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ResponseObjects.cs @@ -1510,6 +1510,8 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom meeting.Privacy = b.IsPrivate ? eMeetingPrivacy.Private : eMeetingPrivacy.Public; + meeting.Dialable = meeting.Id != "0"; + // No meeting.Calls data exists for Zoom Rooms. Leaving out for now. var now = DateTime.Now; if (meeting.StartTime < now && meeting.EndTime < now) diff --git a/packages.config b/packages.config index 35ed241d..761b11cb 100644 --- a/packages.config +++ b/packages.config @@ -1,3 +1,3 @@ - +