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 | 227 +++++++++++------- .../ScreenLiftControllerConfigProperties.cs | 60 ++--- 2 files changed, 170 insertions(+), 117 deletions(-) diff --git a/src/PepperDash.Essentials.Devices.Common/Displays/ScreenLiftController.cs b/src/PepperDash.Essentials.Devices.Common/Displays/ScreenLiftController.cs index f0e57de2..80c1f8ee 100644 --- a/src/PepperDash.Essentials.Devices.Common/Displays/ScreenLiftController.cs +++ b/src/PepperDash.Essentials.Devices.Common/Displays/ScreenLiftController.cs @@ -19,7 +19,7 @@ namespace PepperDash.Essentials.Devices.Common.Shades { None, Raise, - Lower + Lower, } /// @@ -50,7 +50,8 @@ namespace PepperDash.Essentials.Devices.Common.Shades 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 @@ namespace PepperDash.Essentials.Devices.Common.Shades /// /// 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 @@ namespace PepperDash.Essentials.Devices.Common.Shades 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 @@ namespace PepperDash.Essentials.Devices.Common.Shades 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 @@ namespace PepperDash.Essentials.Devices.Common.Shades 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 @@ namespace PepperDash.Essentials.Devices.Common.Shades /// 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 @@ namespace PepperDash.Essentials.Devices.Common.Shades 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 - { - InUpPosition = true; - } - break; - } - case eScreenLiftControlMode.latched: + // Set moving flag and start timer if movement time is configured + if (RaiseRelayConfig.MoveTimeInMs > 0) { - LatchedRelay.Off(); - InUpPosition = true; - break; + _isMoving = true; + _currentMovement = RequestedState.Raise; + if (_movementTimer.Enabled) + { + _movementTimer.Stop(); + } + _movementTimer.Interval = RaiseRelayConfig.MoveTimeInMs; + _movementTimer.Start(); } + else + { + InUpPosition = true; + } + break; + } + case eScreenLiftControlMode.latched: + { + LatchedRelay.Off(); + InUpPosition = true; + break; + } } } @@ -235,7 +281,8 @@ namespace PepperDash.Essentials.Devices.Common.Shades /// 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 @@ namespace PepperDash.Essentials.Devices.Common.Shades 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 - { - InUpPosition = false; - } - break; - } - case eScreenLiftControlMode.latched: + // Set moving flag and start timer if movement time is configured + if (LowerRelayConfig.MoveTimeInMs > 0) { - LatchedRelay.On(); - InUpPosition = false; - break; + _isMoving = true; + _currentMovement = RequestedState.Lower; + if (_movementTimer.Enabled) + { + _movementTimer.Stop(); + } + _movementTimer.Interval = LowerRelayConfig.MoveTimeInMs; + _movementTimer.Start(); } + else + { + InUpPosition = false; + } + break; + } + case eScreenLiftControlMode.latched: + { + LatchedRelay.On(); + InUpPosition = false; + break; + } } } @@ -339,16 +386,13 @@ namespace PepperDash.Essentials.Devices.Common.Shades { 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 @@ namespace PepperDash.Essentials.Devices.Common.Shades } 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 @@ namespace PepperDash.Essentials.Devices.Common.Shades } 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 @@ namespace PepperDash.Essentials.Devices.Common.Shades /// public class ScreenLiftControllerFactory : EssentialsDeviceFactory { - /// + /// /// Constructor for ScreenLiftControllerFactory /// public ScreenLiftControllerFactory() @@ -404,4 +453,4 @@ namespace PepperDash.Essentials.Devices.Common.Shades 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 9de1faa0..1c4f9906 100644 --- a/src/PepperDash.Essentials.Devices.Common/Displays/ScreenLiftControllerConfigProperties.cs +++ b/src/PepperDash.Essentials.Devices.Common/Displays/ScreenLiftControllerConfigProperties.cs @@ -5,37 +5,41 @@ using PepperDash.Essentials.Core.DeviceTypeInterfaces; 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 a517e5d0..be7fbf68 100644 --- a/src/PepperDash.Essentials.Core/Monitoring/StatusMonitorCollection.cs +++ b/src/PepperDash.Essentials.Core/Monitoring/StatusMonitorCollection.cs @@ -14,7 +14,7 @@ using PepperDash.Core; namespace PepperDash.Essentials.Core { /// - /// + /// /// public class StatusMonitorCollection : IStatusMonitor { @@ -59,51 +59,61 @@ namespace PepperDash.Essentials.Core 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 6f858144..20ed56d6 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 @@ namespace PepperDash.Essentials.Core.CrestronIO IOPortConfig config) : base(key, name) { - InputStateFeedback = new BoolFeedback("inputState", () => 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 e2c4474a..43d97e9c 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 @@ namespace PepperDash.Essentials.Core.CrestronIO 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 8641df69..7b76958b 100644 --- a/src/PepperDash.Essentials.Core/CrestronIO/IOPortConfig.cs +++ b/src/PepperDash.Essentials.Core/CrestronIO/IOPortConfig.cs @@ -37,5 +37,12 @@ namespace PepperDash.Essentials.Core.CrestronIO /// [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 308e5c12..9364a58d 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 @@ namespace PepperDash.Essentials.Core.Fusion /// protected void FusionRoom_OnlineStatusChange(GenericBase currentDevice, OnlineOfflineEventArgs args) { - if (args.DeviceOnLine) + if (!args.DeviceOnLine) { - CrestronInvoke.BeginInvoke((o) => - { - CrestronEnvironment.Sleep(200); + return; + } - // Send Push Notification Action request: + if (!_config.EnableSchedulePushNotifications) + { + return; + } - const string requestId = "InitialPushRequest"; + Task.Run(() => + { + // CrestronEnvironment.Sleep(200); + + // Send Push Notification Action request: + + const string requestId = "InitialPushRequest"; - var actionRequest = + var actionRequest = string.Format("\n{0}\n", requestId) + "RegisterPushModel\n" + "\n" + @@ -734,27 +743,26 @@ namespace PepperDash.Essentials.Core.Fusion "\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 @@ namespace PepperDash.Essentials.Core.Fusion 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 @@ namespace PepperDash.Essentials.Core.Fusion 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 @@ namespace PepperDash.Essentials.Core.Fusion 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 98234e61..2be30b08 100644 --- a/src/PepperDash.Essentials.Core/Fusion/IEssentialsRoomFusionControllerPropertiesConfig.cs +++ b/src/PepperDash.Essentials.Core/Fusion/IEssentialsRoomFusionControllerPropertiesConfig.cs @@ -75,4 +75,11 @@ public class IEssentialsRoomFusionControllerPropertiesConfig /// [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 20ed56d6..3692d58d 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 @@ namespace PepperDash.Essentials.Core.CrestronIO IOPortConfig config) : base(key, name) { - invertState = !string.IsNullOrEmpty(config.CircuitType) && - config.CircuitType.Equals("NC", StringComparison.OrdinalIgnoreCase); - + invertState = string.Equals(config.CircuitType, "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 43d97e9c..47e8aef6 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 @@ namespace PepperDash.Essentials.Core.CrestronIO 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);