From 7910b7931e8123f5fa2f5d59bb3333c5785a1e15 Mon Sep 17 00:00:00 2001 From: Nick Genovese Date: Thu, 1 Jan 2026 09:02:04 -0600 Subject: [PATCH 1/5] feat: Add mute logic to ScreenLiftController - adds a config value that mutes the display when the screen is in the up position - screens will now mute/unmute based on their position if the config is set --- .../Displays/ScreenLiftController.cs | 211 +++++++++++------- .../ScreenLiftControllerConfigProperties.cs | 60 ++--- 2 files changed, 162 insertions(+), 109 deletions(-) diff --git a/src/PepperDash.Essentials.Devices.Common/Displays/ScreenLiftController.cs b/src/PepperDash.Essentials.Devices.Common/Displays/ScreenLiftController.cs index f0e57de28..80c1f8ee6 100644 --- a/src/PepperDash.Essentials.Devices.Common/Displays/ScreenLiftController.cs +++ b/src/PepperDash.Essentials.Devices.Common/Displays/ScreenLiftController.cs @@ -19,7 +19,7 @@ enum RequestedState { None, Raise, - Lower + Lower, } /// @@ -50,7 +50,8 @@ public bool InUpPosition get { return _isInUpPosition; } set { - if (value == _isInUpPosition) return; + if (value == _isInUpPosition) + return; _isInUpPosition = value; IsInUpPosition.FireUpdate(); PositionChanged?.Invoke(this, new EventArgs()); @@ -87,7 +88,11 @@ public bool InUpPosition /// /// Constructor for ScreenLiftController /// - public ScreenLiftController(string key, string name, ScreenLiftControllerConfigProperties config) + public ScreenLiftController( + string key, + string name, + ScreenLiftControllerConfigProperties config + ) : base(key, name) { Config = config; @@ -105,27 +110,60 @@ public ScreenLiftController(string key, string name, ScreenLiftControllerConfigP switch (Mode) { case eScreenLiftControlMode.momentary: - { - RaiseRelayConfig = Config.Relays["raise"]; - LowerRelayConfig = Config.Relays["lower"]; - break; - } + { + RaiseRelayConfig = Config.Relays["raise"]; + LowerRelayConfig = Config.Relays["lower"]; + break; + } case eScreenLiftControlMode.latched: - { - LatchedRelayConfig = Config.Relays["latched"]; - break; - } + { + LatchedRelayConfig = Config.Relays["latched"]; + break; + } } + + IsInUpPosition.OutputChange += (sender, args) => + { + this.LogDebug( + "ScreenLiftController '{name}' IsInUpPosition changed to {position}", + Name, + IsInUpPosition.BoolValue ? "Up" : "Down" + ); + + if (!Config.MuteOnScreenUp) + { + return; + } + + if (args.BoolValue) + { + return; + } + + if (DisplayDevice is IBasicVideoMuteWithFeedback videoMute) + { + this.LogInformation("Unmuting video because screen is down"); + videoMute.VideoMuteOff(); + } + }; + + IsInUpPosition.FireUpdate(); } private void IsCoolingDownFeedback_OutputChange(object sender, FeedbackEventArgs e) { - if (!DisplayDevice.IsCoolingDownFeedback.BoolValue && Type == eScreenLiftControlType.lift) + if ( + !DisplayDevice.IsCoolingDownFeedback.BoolValue + && Type == eScreenLiftControlType.lift + ) { Raise(); return; } - if (DisplayDevice.IsCoolingDownFeedback.BoolValue && Type == eScreenLiftControlType.screen) + if ( + DisplayDevice.IsCoolingDownFeedback.BoolValue + && Type == eScreenLiftControlType.screen + ) { Raise(); return; @@ -150,18 +188,18 @@ public override bool CustomActivate() switch (Mode) { case eScreenLiftControlMode.momentary: - { - this.LogDebug("Getting relays for {mode}", Mode); - RaiseRelay = GetSwitchedOutputFromDevice(RaiseRelayConfig.DeviceKey); - LowerRelay = GetSwitchedOutputFromDevice(LowerRelayConfig.DeviceKey); - break; - } + { + this.LogDebug("Getting relays for {mode}", Mode); + RaiseRelay = GetSwitchedOutputFromDevice(RaiseRelayConfig.DeviceKey); + LowerRelay = GetSwitchedOutputFromDevice(LowerRelayConfig.DeviceKey); + break; + } case eScreenLiftControlMode.latched: - { - this.LogDebug("Getting relays for {mode}", Mode); - LatchedRelay = GetSwitchedOutputFromDevice(LatchedRelayConfig.DeviceKey); - break; - } + { + this.LogDebug("Getting relays for {mode}", Mode); + LatchedRelay = GetSwitchedOutputFromDevice(LatchedRelayConfig.DeviceKey); + break; + } } this.LogDebug("Getting display with key {displayKey}", DisplayDeviceKey); @@ -172,7 +210,8 @@ public override bool CustomActivate() this.LogDebug("Subscribing to {displayKey} feedbacks", DisplayDeviceKey); DisplayDevice.IsWarmingUpFeedback.OutputChange += IsWarmingUpFeedback_OutputChange; - DisplayDevice.IsCoolingDownFeedback.OutputChange += IsCoolingDownFeedback_OutputChange; + DisplayDevice.IsCoolingDownFeedback.OutputChange += + IsCoolingDownFeedback_OutputChange; } return base.CustomActivate(); @@ -183,10 +222,17 @@ public override bool CustomActivate() /// public void Raise() { - if (RaiseRelay == null && LatchedRelay == null) return; + if (RaiseRelay == null && LatchedRelay == null) + return; this.LogDebug("Raise called for {type}", Type); + if (Config.MuteOnScreenUp && DisplayDevice is IBasicVideoMuteWithFeedback videoMute) + { + this.LogInformation("Muting video because screen is going up"); + videoMute.VideoMuteOn(); + } + // If device is moving, bank the command if (_isMoving) { @@ -200,33 +246,33 @@ public void Raise() switch (Mode) { case eScreenLiftControlMode.momentary: - { - PulseOutput(RaiseRelay, RaiseRelayConfig.PulseTimeInMs); + { + PulseOutput(RaiseRelay, RaiseRelayConfig.PulseTimeInMs); - // Set moving flag and start timer if movement time is configured - if (RaiseRelayConfig.MoveTimeInMs > 0) - { - _isMoving = true; - _currentMovement = RequestedState.Raise; - if (_movementTimer.Enabled) - { - _movementTimer.Stop(); - } - _movementTimer.Interval = RaiseRelayConfig.MoveTimeInMs; - _movementTimer.Start(); - } - else + // Set moving flag and start timer if movement time is configured + if (RaiseRelayConfig.MoveTimeInMs > 0) + { + _isMoving = true; + _currentMovement = RequestedState.Raise; + if (_movementTimer.Enabled) { - InUpPosition = true; + _movementTimer.Stop(); } - break; + _movementTimer.Interval = RaiseRelayConfig.MoveTimeInMs; + _movementTimer.Start(); } - case eScreenLiftControlMode.latched: + else { - LatchedRelay.Off(); InUpPosition = true; - break; } + break; + } + case eScreenLiftControlMode.latched: + { + LatchedRelay.Off(); + InUpPosition = true; + break; + } } } @@ -235,7 +281,8 @@ public void Raise() /// public void Lower() { - if (LowerRelay == null && LatchedRelay == null) return; + if (LowerRelay == null && LatchedRelay == null) + return; this.LogDebug("Lower called for {type}", Type); @@ -252,33 +299,33 @@ public void Lower() switch (Mode) { case eScreenLiftControlMode.momentary: - { - PulseOutput(LowerRelay, LowerRelayConfig.PulseTimeInMs); + { + PulseOutput(LowerRelay, LowerRelayConfig.PulseTimeInMs); - // Set moving flag and start timer if movement time is configured - if (LowerRelayConfig.MoveTimeInMs > 0) - { - _isMoving = true; - _currentMovement = RequestedState.Lower; - if (_movementTimer.Enabled) - { - _movementTimer.Stop(); - } - _movementTimer.Interval = LowerRelayConfig.MoveTimeInMs; - _movementTimer.Start(); - } - else + // Set moving flag and start timer if movement time is configured + if (LowerRelayConfig.MoveTimeInMs > 0) + { + _isMoving = true; + _currentMovement = RequestedState.Lower; + if (_movementTimer.Enabled) { - InUpPosition = false; + _movementTimer.Stop(); } - break; + _movementTimer.Interval = LowerRelayConfig.MoveTimeInMs; + _movementTimer.Start(); } - case eScreenLiftControlMode.latched: + else { - LatchedRelay.On(); InUpPosition = false; - break; } + break; + } + case eScreenLiftControlMode.latched: + { + LatchedRelay.On(); + InUpPosition = false; + break; + } } } @@ -339,16 +386,13 @@ private void PulseOutput(ISwitchedOutput output, int pulseTime) { output.On(); - var timer = new Timer(pulseTime) - { - AutoReset = false - }; + var timer = new Timer(pulseTime) { AutoReset = false }; timer.Elapsed += (sender, e) => - { - output.Off(); - timer.Dispose(); - }; + { + output.Off(); + timer.Dispose(); + }; timer.Start(); } @@ -361,7 +405,10 @@ private ISwitchedOutput GetSwitchedOutputFromDevice(string relayKey) } else { - this.LogWarning("Error: Unable to get relay device with key '{relayKey}'", relayKey); + this.LogWarning( + "Error: Unable to get relay device with key '{relayKey}'", + relayKey + ); return null; } } @@ -375,11 +422,13 @@ private DisplayBase GetDisplayBaseFromDevice(string displayKey) } else { - this.LogWarning("Error: Unable to get display device with key '{displayKey}'", displayKey); + this.LogWarning( + "Error: Unable to get display device with key '{displayKey}'", + displayKey + ); return null; } } - } /// @@ -387,7 +436,7 @@ private DisplayBase GetDisplayBaseFromDevice(string displayKey) /// public class ScreenLiftControllerFactory : EssentialsDeviceFactory { - /// + /// /// Constructor for ScreenLiftControllerFactory /// public ScreenLiftControllerFactory() @@ -404,4 +453,4 @@ public override EssentialsDevice BuildDevice(DeviceConfig dc) return new ScreenLiftController(dc.Key, dc.Name, props); } } -} \ No newline at end of file +} diff --git a/src/PepperDash.Essentials.Devices.Common/Displays/ScreenLiftControllerConfigProperties.cs b/src/PepperDash.Essentials.Devices.Common/Displays/ScreenLiftControllerConfigProperties.cs index 9de1faa0c..1c4f9906a 100644 --- a/src/PepperDash.Essentials.Devices.Common/Displays/ScreenLiftControllerConfigProperties.cs +++ b/src/PepperDash.Essentials.Devices.Common/Displays/ScreenLiftControllerConfigProperties.cs @@ -5,37 +5,41 @@ namespace PepperDash.Essentials.Devices.Common.Shades { - /// - /// Represents a ScreenLiftControllerConfigProperties - /// - public class ScreenLiftControllerConfigProperties - { - /// - /// Gets or sets the DisplayDeviceKey + /// Represents a ScreenLiftControllerConfigProperties /// - [JsonProperty("displayDeviceKey")] - public string DisplayDeviceKey { get; set; } + public class ScreenLiftControllerConfigProperties + { + /// + /// Gets or sets the DisplayDeviceKey + /// + [JsonProperty("displayDeviceKey")] + public string DisplayDeviceKey { get; set; } - /// - /// Gets or sets the Type - /// - [JsonProperty("type")] - [JsonConverter(typeof(StringEnumConverter))] - public eScreenLiftControlType Type { get; set; } + /// + /// Gets or sets the Type + /// + [JsonProperty("type")] + [JsonConverter(typeof(StringEnumConverter))] + public eScreenLiftControlType Type { get; set; } - /// - /// Gets or sets the Mode - /// - [JsonProperty("mode")] - [JsonConverter(typeof(StringEnumConverter))] - public eScreenLiftControlMode Mode { get; set; } + /// + /// Gets or sets the Mode + /// + [JsonProperty("mode")] + [JsonConverter(typeof(StringEnumConverter))] + public eScreenLiftControlMode Mode { get; set; } - /// - /// Gets or sets the Relays - /// - [JsonProperty("relays")] - public Dictionary Relays { get; set; } + /// + /// Gets or sets the Relays + /// + [JsonProperty("relays")] + public Dictionary Relays { get; set; } - } -} \ No newline at end of file + /// + /// Mutes the display when the screen is in the up position + /// + [JsonProperty("muteOnScreenUp")] + public bool MuteOnScreenUp { get; set; } + } +} From f49901d3faa37eed3353638bca8a5e265ffba5f7 Mon Sep 17 00:00:00 2001 From: Nick Genovese Date: Thu, 1 Jan 2026 18:01:37 -0600 Subject: [PATCH 2/5] fix: Improve status messages in StatusMonitorCollection Enhanced error and warning message generation to use monitor names when available, include counts, proper pluralization, and append "Offline" when issues are present. Avoided multiple enumerations by converting to lists. "Room Ok." is shown when no issues are detected. --- .../Monitoring/StatusMonitorCollection.cs | 68 +++++++++++-------- 1 file changed, 39 insertions(+), 29 deletions(-) diff --git a/src/PepperDash.Essentials.Core/Monitoring/StatusMonitorCollection.cs b/src/PepperDash.Essentials.Core/Monitoring/StatusMonitorCollection.cs index a517e5d08..be7fbf684 100644 --- a/src/PepperDash.Essentials.Core/Monitoring/StatusMonitorCollection.cs +++ b/src/PepperDash.Essentials.Core/Monitoring/StatusMonitorCollection.cs @@ -14,7 +14,7 @@ namespace PepperDash.Essentials.Core { /// - /// + /// /// public class StatusMonitorCollection : IStatusMonitor { @@ -59,51 +59,61 @@ public void Start() void ProcessStatuses() { - var InError = Monitors.Where(m => m.Status == MonitorStatus.InError); - var InWarning = Monitors.Where(m => m.Status == MonitorStatus.InWarning); - var IsOk = Monitors.Where(m => m.Status == MonitorStatus.IsOk); + var InError = Monitors.Where(m => m.Status == MonitorStatus.InError).ToList(); + var InWarning = Monitors.Where(m => m.Status == MonitorStatus.InWarning).ToList(); + var IsOk = Monitors.Where(m => m.Status == MonitorStatus.IsOk).ToList(); MonitorStatus initialStatus; string prefix = "0:"; - if (InError.Count() > 0) + if (InError.Any()) { initialStatus = MonitorStatus.InError; prefix = "3:"; } - else if (InWarning.Count() > 0) + else if (InWarning.Any()) { initialStatus = MonitorStatus.InWarning; prefix = "2:"; } - else if (IsOk.Count() > 0) + else if (IsOk.Any()) initialStatus = MonitorStatus.IsOk; else initialStatus = MonitorStatus.StatusUnknown; // Build the error message string - if (InError.Count() > 0 || InWarning.Count() > 0) - { - StringBuilder sb = new StringBuilder(prefix); - if (InError.Count() > 0) - { - // Do string splits and joins - sb.Append(string.Format("{0} Errors:", InError.Count())); - foreach (var mon in InError) - sb.Append(string.Format("{0}, ", mon.Parent.Key)); - } - if (InWarning.Count() > 0) - { - sb.Append(string.Format("{0} Warnings:", InWarning.Count())); - foreach (var mon in InWarning) - sb.Append(string.Format("{0}, ", mon.Parent.Key)); - } - Message = sb.ToString(); - } - else - { - Message = "Room Ok."; - } + if (InError.Any() || InWarning.Any()) + { + var errorNames = InError + .Select(mon => mon.Parent is IKeyName keyName ? keyName.Name : mon.Parent.Key) + .ToList(); + var warningNames = InWarning + .Select(mon => mon.Parent is IKeyName keyName ? keyName.Name : mon.Parent.Key) + .ToList(); + + var sb = new StringBuilder(prefix); + + if (errorNames.Count > 0) + { + sb.Append($"{errorNames.Count} Error{(errorNames.Count > 1 ? "s" : "")}: "); + sb.Append(string.Join(", ", errorNames)); + } + if (warningNames.Count > 0) + { + if (errorNames.Count > 0) + sb.Append("; "); + + sb.Append($"{warningNames.Count} Warning{(warningNames.Count > 1 ? "s" : "")}: "); + sb.Append(string.Join(", ", warningNames)); + } + + sb.Append(" Offline"); + Message = sb.ToString(); + } + else + { + Message = "Room Ok."; + } // Want to fire even if status doesn't change because the message may. Status = initialStatus; From 8bc6d4392ba2279a90a3f77c8d923aa5b15bca65 Mon Sep 17 00:00:00 2001 From: equinoy Date: Thu, 22 Jan 2026 15:04:25 -0600 Subject: [PATCH 3/5] feat: add circuitType property to IOPortConfig and implement state inversion in digital input devices --- .../CrestronIO/GenericDigitalInputDevice.cs | 6 +++++- .../CrestronIO/GenericVersiportInputDevice.cs | 6 +++++- src/PepperDash.Essentials.Core/CrestronIO/IOPortConfig.cs | 7 +++++++ 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/PepperDash.Essentials.Core/CrestronIO/GenericDigitalInputDevice.cs b/src/PepperDash.Essentials.Core/CrestronIO/GenericDigitalInputDevice.cs index 6f8581441..20ed56d66 100644 --- a/src/PepperDash.Essentials.Core/CrestronIO/GenericDigitalInputDevice.cs +++ b/src/PepperDash.Essentials.Core/CrestronIO/GenericDigitalInputDevice.cs @@ -21,6 +21,7 @@ namespace PepperDash.Essentials.Core.CrestronIO public class GenericDigitalInputDevice : EssentialsBridgeableDevice, IDigitalInput, IHasFeedback { private DigitalInput inputPort; + private bool invertState; /// /// Gets or sets the InputStateFeedback @@ -41,7 +42,10 @@ public GenericDigitalInputDevice(string key, string name, Func inputPort.State); + invertState = !string.IsNullOrEmpty(config.CircuitType) && + config.CircuitType.Equals("NC", StringComparison.OrdinalIgnoreCase); + + InputStateFeedback = new BoolFeedback("inputState", () => invertState ? !inputPort.State : inputPort.State); AddPostActivationAction(() => { diff --git a/src/PepperDash.Essentials.Core/CrestronIO/GenericVersiportInputDevice.cs b/src/PepperDash.Essentials.Core/CrestronIO/GenericVersiportInputDevice.cs index e2c4474ac..43d97e9c1 100644 --- a/src/PepperDash.Essentials.Core/CrestronIO/GenericVersiportInputDevice.cs +++ b/src/PepperDash.Essentials.Core/CrestronIO/GenericVersiportInputDevice.cs @@ -18,6 +18,7 @@ namespace PepperDash.Essentials.Core.CrestronIO public class GenericVersiportDigitalInputDevice : EssentialsBridgeableDevice, IDigitalInput, IPartitionStateProvider, IHasFeedback { private Versiport inputPort; + private bool invertState; /// /// Gets or sets the InputStateFeedback @@ -47,7 +48,10 @@ public class GenericVersiportDigitalInputDevice : EssentialsBridgeableDevice, ID public GenericVersiportDigitalInputDevice(string key, string name, Func postActivationFunc, IOPortConfig config) : base(key, name) { - InputStateFeedback = new BoolFeedback("inputState", () => inputPort.DigitalIn); + invertState = !string.IsNullOrEmpty(config.CircuitType) && + config.CircuitType.Equals("NC", StringComparison.OrdinalIgnoreCase); + + InputStateFeedback = new BoolFeedback("inputState", () => invertState ? !inputPort.DigitalIn : inputPort.DigitalIn); PartitionPresentFeedback = new BoolFeedback("partitionPresent", () => !inputPort.DigitalIn); AddPostActivationAction(() => diff --git a/src/PepperDash.Essentials.Core/CrestronIO/IOPortConfig.cs b/src/PepperDash.Essentials.Core/CrestronIO/IOPortConfig.cs index 8641df69e..7b76958b6 100644 --- a/src/PepperDash.Essentials.Core/CrestronIO/IOPortConfig.cs +++ b/src/PepperDash.Essentials.Core/CrestronIO/IOPortConfig.cs @@ -37,5 +37,12 @@ public class IOPortConfig /// [JsonProperty("minimumChange")] public int MinimumChange { get; set; } + + /// + /// Gets or sets the circuit type: "NO" (Normally Open) or "NC" (Normally Closed) + /// If set to "NC", the input state will be inverted. Defaults to "NO" if not specified. + /// + [JsonProperty("circuitType")] + public string CircuitType { get; set; } = "NO"; } } \ No newline at end of file From 1fb5d3e5ee77a266f12a5a807ed9ae9733d4e0b1 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Mon, 9 Feb 2026 08:09:18 -0600 Subject: [PATCH 4/5] fix: make registering for push schedule updates optional The default config for a Fusion room will now NOT subscribe for Fusion schedule push updates unless explicitly requested. --- .../Fusion/IEssentialsRoomFusionController.cs | 58 +++++++++++-------- ...alsRoomFusionControllerPropertiesConfig.cs | 7 +++ 2 files changed, 40 insertions(+), 25 deletions(-) diff --git a/src/PepperDash.Essentials.Core/Fusion/IEssentialsRoomFusionController.cs b/src/PepperDash.Essentials.Core/Fusion/IEssentialsRoomFusionController.cs index 308e5c123..9364a58d1 100644 --- a/src/PepperDash.Essentials.Core/Fusion/IEssentialsRoomFusionController.cs +++ b/src/PepperDash.Essentials.Core/Fusion/IEssentialsRoomFusionController.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using System.Threading.Tasks; using System.Timers; using Crestron.SimplSharp; using Crestron.SimplSharp.CrestronIO; @@ -698,18 +699,26 @@ private void GetTouchpanelInfo() /// protected void FusionRoom_OnlineStatusChange(GenericBase currentDevice, OnlineOfflineEventArgs args) { - if (args.DeviceOnLine) + if (!args.DeviceOnLine) { - CrestronInvoke.BeginInvoke((o) => - { - CrestronEnvironment.Sleep(200); + return; + } + + if (!_config.EnableSchedulePushNotifications) + { + return; + } + + Task.Run(() => + { + // CrestronEnvironment.Sleep(200); - // Send Push Notification Action request: + // Send Push Notification Action request: - const string requestId = "InitialPushRequest"; + const string requestId = "InitialPushRequest"; - var actionRequest = + var actionRequest = string.Format("\n{0}\n", requestId) + "RegisterPushModel\n" + "\n" + @@ -734,27 +743,26 @@ protected void FusionRoom_OnlineStatusChange(GenericBase currentDevice, OnlineOf "\n" + "\n"; - Debug.LogMessage(LogEventLevel.Verbose, this, "Sending Fusion ActionRequest: \n{0}", actionRequest); + Debug.LogMessage(LogEventLevel.Verbose, this, "Sending Fusion ActionRequest: \n{0}", actionRequest); - FusionRoom.ExtenderFusionRoomDataReservedSigs.ActionQuery.StringValue = actionRequest; + FusionRoom.ExtenderFusionRoomDataReservedSigs.ActionQuery.StringValue = actionRequest; - GetCustomProperties(); + GetCustomProperties(); - // Request current Fusion Server Time - RequestLocalDateTime(null); + // Request current Fusion Server Time + RequestLocalDateTime(null); - // Setup timer to request time daily - if (_dailyTimeRequestTimer != null && !_dailyTimeRequestTimer.Disposed) - { - _dailyTimeRequestTimer.Stop(); - _dailyTimeRequestTimer.Dispose(); - } + // Setup timer to request time daily + if (_dailyTimeRequestTimer != null && !_dailyTimeRequestTimer.Disposed) + { + _dailyTimeRequestTimer.Stop(); + _dailyTimeRequestTimer.Dispose(); + } - _dailyTimeRequestTimer = new CTimer(RequestLocalDateTime, null, 86400000, 86400000); + _dailyTimeRequestTimer = new CTimer(RequestLocalDateTime, null, 86400000, 86400000); - _dailyTimeRequestTimer.Reset(86400000, 86400000); - }); - } + _dailyTimeRequestTimer.Reset(86400000, 86400000); + }); } /// @@ -785,7 +793,7 @@ public void RequestFullRoomSchedule(object callbackObject) var requestTest = string.Format( - "FullSchedleRequest{0}{1}24", + "FullScheduleRequest{0}{1}24", RoomGuid, currentTime); Debug.LogMessage(LogEventLevel.Verbose, this, "Sending Fusion ScheduleQuery: \n{0}", requestTest); @@ -960,7 +968,7 @@ where parameter.HasAttributes select parameter.Attributes into attributes where attributes["ID"].Value == "Registered" - select Int32.Parse(attributes["Value"].Value)) + select int.Parse(attributes["Value"].Value)) { switch (isRegistered) { @@ -1112,7 +1120,7 @@ select Int32.Parse(attributes["Value"].Value)) protected void FusionRoomSchedule_DeviceExtenderSigChange(DeviceExtender currentDeviceExtender, SigEventArgs args) { - Debug.LogMessage(LogEventLevel.Verbose, this, "Scehdule Response Event: {0}\n Sig: {1}\nFusionResponse:\n{2}", args.Event, + Debug.LogMessage(LogEventLevel.Verbose, this, "Schedule Response Event: {0}\n Sig: {1}\nFusionResponse:\n{2}", args.Event, args.Sig.Name, args.Sig.StringValue); diff --git a/src/PepperDash.Essentials.Core/Fusion/IEssentialsRoomFusionControllerPropertiesConfig.cs b/src/PepperDash.Essentials.Core/Fusion/IEssentialsRoomFusionControllerPropertiesConfig.cs index 98234e61b..2be30b08f 100644 --- a/src/PepperDash.Essentials.Core/Fusion/IEssentialsRoomFusionControllerPropertiesConfig.cs +++ b/src/PepperDash.Essentials.Core/Fusion/IEssentialsRoomFusionControllerPropertiesConfig.cs @@ -75,4 +75,11 @@ public uint IpIdInt /// [JsonProperty("helpRequestTimeoutMs")] public int HelpRequestTimeoutMs { get; set; } = 30000; + + /// + /// Gets or sets whether to enable schedule push notifications + /// + /// Defaults to false to skip getting schedule unless required + [JsonProperty("enableSchedulePushNotifications")] + public bool EnableSchedulePushNotifications { get; set; } = false; } \ No newline at end of file From 4437074f0798fe38ca1727346eea8a8e0a8c84d6 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Mon, 9 Feb 2026 08:45:28 -0600 Subject: [PATCH 5/5] fix: implement copilot suggestions --- .../CrestronIO/GenericDigitalInputDevice.cs | 7 +++---- .../CrestronIO/GenericVersiportInputDevice.cs | 8 ++++---- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/PepperDash.Essentials.Core/CrestronIO/GenericDigitalInputDevice.cs b/src/PepperDash.Essentials.Core/CrestronIO/GenericDigitalInputDevice.cs index 20ed56d66..3692d58d2 100644 --- a/src/PepperDash.Essentials.Core/CrestronIO/GenericDigitalInputDevice.cs +++ b/src/PepperDash.Essentials.Core/CrestronIO/GenericDigitalInputDevice.cs @@ -21,7 +21,7 @@ namespace PepperDash.Essentials.Core.CrestronIO public class GenericDigitalInputDevice : EssentialsBridgeableDevice, IDigitalInput, IHasFeedback { private DigitalInput inputPort; - private bool invertState; + private readonly bool invertState; /// /// Gets or sets the InputStateFeedback @@ -42,9 +42,8 @@ public GenericDigitalInputDevice(string key, string name, Func invertState ? !inputPort.State : inputPort.State); AddPostActivationAction(() => diff --git a/src/PepperDash.Essentials.Core/CrestronIO/GenericVersiportInputDevice.cs b/src/PepperDash.Essentials.Core/CrestronIO/GenericVersiportInputDevice.cs index 43d97e9c1..47e8aef6c 100644 --- a/src/PepperDash.Essentials.Core/CrestronIO/GenericVersiportInputDevice.cs +++ b/src/PepperDash.Essentials.Core/CrestronIO/GenericVersiportInputDevice.cs @@ -18,7 +18,7 @@ namespace PepperDash.Essentials.Core.CrestronIO public class GenericVersiportDigitalInputDevice : EssentialsBridgeableDevice, IDigitalInput, IPartitionStateProvider, IHasFeedback { private Versiport inputPort; - private bool invertState; + private readonly bool invertState; /// /// Gets or sets the InputStateFeedback @@ -48,9 +48,9 @@ public class GenericVersiportDigitalInputDevice : EssentialsBridgeableDevice, ID public GenericVersiportDigitalInputDevice(string key, string name, Func postActivationFunc, IOPortConfig config) : base(key, name) { - invertState = !string.IsNullOrEmpty(config.CircuitType) && - config.CircuitType.Equals("NC", StringComparison.OrdinalIgnoreCase); - + var circuitType = string.IsNullOrEmpty(config.CircuitType) ? "NO" : config.CircuitType; + invertState = circuitType.Equals("NC", StringComparison.OrdinalIgnoreCase); + InputStateFeedback = new BoolFeedback("inputState", () => invertState ? !inputPort.DigitalIn : inputPort.DigitalIn); PartitionPresentFeedback = new BoolFeedback("partitionPresent", () => !inputPort.DigitalIn);