From 9d6aaa2a0ec75304183198a84c3904925e5f749b Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 27 Dec 2025 20:02:52 +0000
Subject: [PATCH 1/5] Initial plan
From 7ea1efbabf25aba18e04a4479ab3e0fe05636582 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 27 Dec 2025 20:09:31 +0000
Subject: [PATCH 2/5] Add raise/lower movement time configuration and banked
command support
Co-authored-by: erikdred <88980320+erikdred@users.noreply.github.com>
---
.../Displays/ScreenLiftController.cs | 105 ++++++++++++++++++
.../Displays/ScreenLiftRelaysConfig.cs | 6 +
2 files changed, 111 insertions(+)
diff --git a/src/PepperDash.Essentials.Devices.Common/Displays/ScreenLiftController.cs b/src/PepperDash.Essentials.Devices.Common/Displays/ScreenLiftController.cs
index 9163c2a32..5d25a58ab 100644
--- a/src/PepperDash.Essentials.Devices.Common/Displays/ScreenLiftController.cs
+++ b/src/PepperDash.Essentials.Devices.Common/Displays/ScreenLiftController.cs
@@ -10,6 +10,16 @@
namespace PepperDash.Essentials.Devices.Common.Shades
{
+ ///
+ /// Enumeration for requested state
+ ///
+ enum RequestedState
+ {
+ None,
+ Raise,
+ Lower
+ }
+
///
/// Controls a single shade using three relays
///
@@ -25,6 +35,10 @@ public class ScreenLiftController : EssentialsDevice, IProjectorScreenLiftContro
ISwitchedOutput LowerRelay;
ISwitchedOutput LatchedRelay;
+ private bool _isMoving;
+ private RequestedState _requestedState;
+ private CTimer _movementTimer;
+
///
/// Gets or sets the InUpPosition
///
@@ -163,6 +177,16 @@ public void Raise()
{
if (RaiseRelay == null && LatchedRelay == null) return;
+ Debug.LogMessage(LogEventLevel.Debug, this, $"Raise called for {Type}");
+
+ // If device is moving, bank the command
+ if (_isMoving)
+ {
+ Debug.LogMessage(LogEventLevel.Debug, this, $"Device is moving, banking Raise command");
+ _requestedState = RequestedState.Raise;
+ return;
+ }
+
Debug.LogMessage(LogEventLevel.Debug, this, $"Raising {Type}");
switch (Mode)
@@ -170,11 +194,25 @@ public void Raise()
case eScreenLiftControlMode.momentary:
{
PulseOutput(RaiseRelay, RaiseRelayConfig.PulseTimeInMs);
+
+ // Set moving flag and start timer if movement time is configured
+ if (RaiseRelayConfig.MovementTimeInMs > 0)
+ {
+ _isMoving = true;
+ _movementTimer = new CTimer(OnMovementComplete, RaiseRelayConfig.MovementTimeInMs);
+ }
break;
}
case eScreenLiftControlMode.latched:
{
LatchedRelay.Off();
+
+ // Set moving flag and start timer if movement time is configured
+ if (LatchedRelayConfig.MovementTimeInMs > 0)
+ {
+ _isMoving = true;
+ _movementTimer = new CTimer(OnMovementComplete, LatchedRelayConfig.MovementTimeInMs);
+ }
break;
}
}
@@ -188,6 +226,16 @@ public void Lower()
{
if (LowerRelay == null && LatchedRelay == null) return;
+ Debug.LogMessage(LogEventLevel.Debug, this, $"Lower called for {Type}");
+
+ // If device is moving, bank the command
+ if (_isMoving)
+ {
+ Debug.LogMessage(LogEventLevel.Debug, this, $"Device is moving, banking Lower command");
+ _requestedState = RequestedState.Lower;
+ return;
+ }
+
Debug.LogMessage(LogEventLevel.Debug, this, $"Lowering {Type}");
switch (Mode)
@@ -195,17 +243,74 @@ public void Lower()
case eScreenLiftControlMode.momentary:
{
PulseOutput(LowerRelay, LowerRelayConfig.PulseTimeInMs);
+
+ // Set moving flag and start timer if movement time is configured
+ if (LowerRelayConfig.MovementTimeInMs > 0)
+ {
+ _isMoving = true;
+ _movementTimer = new CTimer(OnMovementComplete, LowerRelayConfig.MovementTimeInMs);
+ }
break;
}
case eScreenLiftControlMode.latched:
{
LatchedRelay.On();
+
+ // Set moving flag and start timer if movement time is configured
+ if (LatchedRelayConfig.MovementTimeInMs > 0)
+ {
+ _isMoving = true;
+ _movementTimer = new CTimer(OnMovementComplete, LatchedRelayConfig.MovementTimeInMs);
+ }
break;
}
}
InUpPosition = false;
}
+ ///
+ /// Called when movement timer completes
+ ///
+ void OnMovementComplete(object o)
+ {
+ Debug.LogMessage(LogEventLevel.Debug, this, $"Movement complete");
+
+ _isMoving = false;
+
+ // Execute banked command if one exists
+ if (_requestedState != RequestedState.None)
+ {
+ Debug.LogMessage(LogEventLevel.Debug, this, $"Executing banked command: {_requestedState}");
+
+ var commandToExecute = _requestedState;
+ _requestedState = RequestedState.None;
+
+ // Check if current state matches what the banked command would do
+ // If so, ignore it
+ if (commandToExecute == RequestedState.Raise && InUpPosition)
+ {
+ Debug.LogMessage(LogEventLevel.Debug, this, $"Already in up position, ignoring banked Raise command");
+ return;
+ }
+
+ if (commandToExecute == RequestedState.Lower && !InUpPosition)
+ {
+ Debug.LogMessage(LogEventLevel.Debug, this, $"Already in down position, ignoring banked Lower command");
+ return;
+ }
+
+ // Execute the banked command
+ if (commandToExecute == RequestedState.Raise)
+ {
+ Raise();
+ }
+ else if (commandToExecute == RequestedState.Lower)
+ {
+ Lower();
+ }
+ }
+ }
+
void PulseOutput(ISwitchedOutput output, int pulseTime)
{
output.On();
diff --git a/src/PepperDash.Essentials.Devices.Common/Displays/ScreenLiftRelaysConfig.cs b/src/PepperDash.Essentials.Devices.Common/Displays/ScreenLiftRelaysConfig.cs
index 4de9eb25f..a99f82d38 100644
--- a/src/PepperDash.Essentials.Devices.Common/Displays/ScreenLiftRelaysConfig.cs
+++ b/src/PepperDash.Essentials.Devices.Common/Displays/ScreenLiftRelaysConfig.cs
@@ -18,5 +18,11 @@ public class ScreenLiftRelaysConfig
///
[JsonProperty("pulseTimeInMs")]
public int PulseTimeInMs { get; set; }
+
+ ///
+ /// Gets or sets the MovementTimeInMs - time in milliseconds for the movement to complete
+ ///
+ [JsonProperty("movementTimeInMs")]
+ public int MovementTimeInMs { get; set; }
}
}
\ No newline at end of file
From bd11c827daef5f6938c5cb89681de3649ebea126 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 27 Dec 2025 20:14:39 +0000
Subject: [PATCH 3/5] Split movement time into separate raise/lower times and
remove timing from latched mode
Co-authored-by: erikdred <88980320+erikdred@users.noreply.github.com>
---
.../Displays/ScreenLiftController.cs | 40 ++++++++++---------
.../Displays/ScreenLiftRelaysConfig.cs | 12 ++++--
2 files changed, 30 insertions(+), 22 deletions(-)
diff --git a/src/PepperDash.Essentials.Devices.Common/Displays/ScreenLiftController.cs b/src/PepperDash.Essentials.Devices.Common/Displays/ScreenLiftController.cs
index 5d25a58ab..4494fdbfa 100644
--- a/src/PepperDash.Essentials.Devices.Common/Displays/ScreenLiftController.cs
+++ b/src/PepperDash.Essentials.Devices.Common/Displays/ScreenLiftController.cs
@@ -196,23 +196,24 @@ public void Raise()
PulseOutput(RaiseRelay, RaiseRelayConfig.PulseTimeInMs);
// Set moving flag and start timer if movement time is configured
- if (RaiseRelayConfig.MovementTimeInMs > 0)
+ if (RaiseRelayConfig.RaiseTimeInMs > 0)
{
_isMoving = true;
- _movementTimer = new CTimer(OnMovementComplete, RaiseRelayConfig.MovementTimeInMs);
+
+ // Dispose previous timer if exists
+ if (_movementTimer != null)
+ {
+ _movementTimer.Stop();
+ _movementTimer.Dispose();
+ }
+
+ _movementTimer = new CTimer(OnMovementComplete, RaiseRelayConfig.RaiseTimeInMs);
}
break;
}
case eScreenLiftControlMode.latched:
{
LatchedRelay.Off();
-
- // Set moving flag and start timer if movement time is configured
- if (LatchedRelayConfig.MovementTimeInMs > 0)
- {
- _isMoving = true;
- _movementTimer = new CTimer(OnMovementComplete, LatchedRelayConfig.MovementTimeInMs);
- }
break;
}
}
@@ -245,23 +246,24 @@ public void Lower()
PulseOutput(LowerRelay, LowerRelayConfig.PulseTimeInMs);
// Set moving flag and start timer if movement time is configured
- if (LowerRelayConfig.MovementTimeInMs > 0)
+ if (LowerRelayConfig.LowerTimeInMs > 0)
{
_isMoving = true;
- _movementTimer = new CTimer(OnMovementComplete, LowerRelayConfig.MovementTimeInMs);
+
+ // Dispose previous timer if exists
+ if (_movementTimer != null)
+ {
+ _movementTimer.Stop();
+ _movementTimer.Dispose();
+ }
+
+ _movementTimer = new CTimer(OnMovementComplete, LowerRelayConfig.LowerTimeInMs);
}
break;
}
case eScreenLiftControlMode.latched:
{
LatchedRelay.On();
-
- // Set moving flag and start timer if movement time is configured
- if (LatchedRelayConfig.MovementTimeInMs > 0)
- {
- _isMoving = true;
- _movementTimer = new CTimer(OnMovementComplete, LatchedRelayConfig.MovementTimeInMs);
- }
break;
}
}
@@ -271,7 +273,7 @@ public void Lower()
///
/// Called when movement timer completes
///
- void OnMovementComplete(object o)
+ private void OnMovementComplete(object o)
{
Debug.LogMessage(LogEventLevel.Debug, this, $"Movement complete");
diff --git a/src/PepperDash.Essentials.Devices.Common/Displays/ScreenLiftRelaysConfig.cs b/src/PepperDash.Essentials.Devices.Common/Displays/ScreenLiftRelaysConfig.cs
index a99f82d38..f39134a41 100644
--- a/src/PepperDash.Essentials.Devices.Common/Displays/ScreenLiftRelaysConfig.cs
+++ b/src/PepperDash.Essentials.Devices.Common/Displays/ScreenLiftRelaysConfig.cs
@@ -20,9 +20,15 @@ public class ScreenLiftRelaysConfig
public int PulseTimeInMs { get; set; }
///
- /// Gets or sets the MovementTimeInMs - time in milliseconds for the movement to complete
+ /// Gets or sets the RaiseTimeInMs - time in milliseconds for the raise movement to complete
///
- [JsonProperty("movementTimeInMs")]
- public int MovementTimeInMs { get; set; }
+ [JsonProperty("raiseTimeInMs")]
+ public int RaiseTimeInMs { get; set; }
+
+ ///
+ /// Gets or sets the LowerTimeInMs - time in milliseconds for the lower movement to complete
+ ///
+ [JsonProperty("lowerTimeInMs")]
+ public int LowerTimeInMs { get; set; }
}
}
\ No newline at end of file
From ae0b2fe0864692fd85a8c9fffce8f02fc4b3a105 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 27 Dec 2025 20:17:59 +0000
Subject: [PATCH 4/5] Refactor timer disposal and improve code readability
Co-authored-by: erikdred <88980320+erikdred@users.noreply.github.com>
---
.../Displays/ScreenLiftController.cs | 75 ++++++++++---------
1 file changed, 38 insertions(+), 37 deletions(-)
diff --git a/src/PepperDash.Essentials.Devices.Common/Displays/ScreenLiftController.cs b/src/PepperDash.Essentials.Devices.Common/Displays/ScreenLiftController.cs
index 4494fdbfa..459819aff 100644
--- a/src/PepperDash.Essentials.Devices.Common/Displays/ScreenLiftController.cs
+++ b/src/PepperDash.Essentials.Devices.Common/Displays/ScreenLiftController.cs
@@ -199,14 +199,7 @@ public void Raise()
if (RaiseRelayConfig.RaiseTimeInMs > 0)
{
_isMoving = true;
-
- // Dispose previous timer if exists
- if (_movementTimer != null)
- {
- _movementTimer.Stop();
- _movementTimer.Dispose();
- }
-
+ DisposeMovementTimer();
_movementTimer = new CTimer(OnMovementComplete, RaiseRelayConfig.RaiseTimeInMs);
}
break;
@@ -249,14 +242,7 @@ public void Lower()
if (LowerRelayConfig.LowerTimeInMs > 0)
{
_isMoving = true;
-
- // Dispose previous timer if exists
- if (_movementTimer != null)
- {
- _movementTimer.Stop();
- _movementTimer.Dispose();
- }
-
+ DisposeMovementTimer();
_movementTimer = new CTimer(OnMovementComplete, LowerRelayConfig.LowerTimeInMs);
}
break;
@@ -270,6 +256,19 @@ public void Lower()
InUpPosition = false;
}
+ ///
+ /// Disposes the current movement timer if it exists
+ ///
+ private void DisposeMovementTimer()
+ {
+ if (_movementTimer != null)
+ {
+ _movementTimer.Stop();
+ _movementTimer.Dispose();
+ _movementTimer = null;
+ }
+ }
+
///
/// Called when movement timer completes
///
@@ -287,28 +286,30 @@ private void OnMovementComplete(object o)
var commandToExecute = _requestedState;
_requestedState = RequestedState.None;
- // Check if current state matches what the banked command would do
- // If so, ignore it
- if (commandToExecute == RequestedState.Raise && InUpPosition)
- {
- Debug.LogMessage(LogEventLevel.Debug, this, $"Already in up position, ignoring banked Raise command");
- return;
- }
-
- if (commandToExecute == RequestedState.Lower && !InUpPosition)
- {
- Debug.LogMessage(LogEventLevel.Debug, this, $"Already in down position, ignoring banked Lower command");
- return;
- }
-
- // Execute the banked command
- if (commandToExecute == RequestedState.Raise)
+ // Check if current state matches what the banked command would do and execute if different
+ switch (commandToExecute)
{
- Raise();
- }
- else if (commandToExecute == RequestedState.Lower)
- {
- Lower();
+ case RequestedState.Raise:
+ if (InUpPosition)
+ {
+ Debug.LogMessage(LogEventLevel.Debug, this, $"Already in up position, ignoring banked Raise command");
+ }
+ else
+ {
+ Raise();
+ }
+ break;
+
+ case RequestedState.Lower:
+ if (!InUpPosition)
+ {
+ Debug.LogMessage(LogEventLevel.Debug, this, $"Already in down position, ignoring banked Lower command");
+ }
+ else
+ {
+ Lower();
+ }
+ break;
}
}
}
From a7ff2e8903487c3aea66f136f2184693b11219b3 Mon Sep 17 00:00:00 2001
From: Erik Meyer
Date: Sat, 27 Dec 2025 17:09:56 -0600
Subject: [PATCH 5/5] fix: move isInUpPosition to momvement complete method,
remove conditionlal logic on raise/lower commands
---
.../Displays/ScreenLiftController.cs | 56 +++++++++++--------
.../Displays/ScreenLiftRelaysConfig.cs | 12 +---
2 files changed, 35 insertions(+), 33 deletions(-)
diff --git a/src/PepperDash.Essentials.Devices.Common/Displays/ScreenLiftController.cs b/src/PepperDash.Essentials.Devices.Common/Displays/ScreenLiftController.cs
index 459819aff..aad8f54a8 100644
--- a/src/PepperDash.Essentials.Devices.Common/Displays/ScreenLiftController.cs
+++ b/src/PepperDash.Essentials.Devices.Common/Displays/ScreenLiftController.cs
@@ -37,6 +37,7 @@ public class ScreenLiftController : EssentialsDevice, IProjectorScreenLiftContro
private bool _isMoving;
private RequestedState _requestedState;
+ private RequestedState _currentMovement;
private CTimer _movementTimer;
///
@@ -188,7 +189,7 @@ public void Raise()
}
Debug.LogMessage(LogEventLevel.Debug, this, $"Raising {Type}");
-
+
switch (Mode)
{
case eScreenLiftControlMode.momentary:
@@ -196,21 +197,26 @@ public void Raise()
PulseOutput(RaiseRelay, RaiseRelayConfig.PulseTimeInMs);
// Set moving flag and start timer if movement time is configured
- if (RaiseRelayConfig.RaiseTimeInMs > 0)
+ if (RaiseRelayConfig.MoveTimeInMs > 0)
{
_isMoving = true;
+ _currentMovement = RequestedState.Raise;
DisposeMovementTimer();
- _movementTimer = new CTimer(OnMovementComplete, RaiseRelayConfig.RaiseTimeInMs);
+ _movementTimer = new CTimer(OnMovementComplete, RaiseRelayConfig.MoveTimeInMs);
+ }
+ else
+ {
+ InUpPosition = true;
}
break;
}
case eScreenLiftControlMode.latched:
{
LatchedRelay.Off();
+ InUpPosition = true;
break;
}
}
- InUpPosition = true;
}
///
@@ -221,7 +227,7 @@ public void Lower()
if (LowerRelay == null && LatchedRelay == null) return;
Debug.LogMessage(LogEventLevel.Debug, this, $"Lower called for {Type}");
-
+
// If device is moving, bank the command
if (_isMoving)
{
@@ -239,21 +245,26 @@ public void Lower()
PulseOutput(LowerRelay, LowerRelayConfig.PulseTimeInMs);
// Set moving flag and start timer if movement time is configured
- if (LowerRelayConfig.LowerTimeInMs > 0)
+ if (LowerRelayConfig.MoveTimeInMs > 0)
{
_isMoving = true;
+ _currentMovement = RequestedState.Lower;
DisposeMovementTimer();
- _movementTimer = new CTimer(OnMovementComplete, LowerRelayConfig.LowerTimeInMs);
+ _movementTimer = new CTimer(OnMovementComplete, LowerRelayConfig.MoveTimeInMs);
+ }
+ else
+ {
+ InUpPosition = false;
}
break;
}
case eScreenLiftControlMode.latched:
{
LatchedRelay.On();
+ InUpPosition = false;
break;
}
}
- InUpPosition = false;
}
///
@@ -276,7 +287,18 @@ private void OnMovementComplete(object o)
{
Debug.LogMessage(LogEventLevel.Debug, this, $"Movement complete");
+ // Update position based on completed movement
+ if (_currentMovement == RequestedState.Raise)
+ {
+ InUpPosition = true;
+ }
+ else if (_currentMovement == RequestedState.Lower)
+ {
+ InUpPosition = false;
+ }
+
_isMoving = false;
+ _currentMovement = RequestedState.None;
// Execute banked command if one exists
if (_requestedState != RequestedState.None)
@@ -290,25 +312,11 @@ private void OnMovementComplete(object o)
switch (commandToExecute)
{
case RequestedState.Raise:
- if (InUpPosition)
- {
- Debug.LogMessage(LogEventLevel.Debug, this, $"Already in up position, ignoring banked Raise command");
- }
- else
- {
- Raise();
- }
+ Raise();
break;
case RequestedState.Lower:
- if (!InUpPosition)
- {
- Debug.LogMessage(LogEventLevel.Debug, this, $"Already in down position, ignoring banked Lower command");
- }
- else
- {
- Lower();
- }
+ Lower();
break;
}
}
diff --git a/src/PepperDash.Essentials.Devices.Common/Displays/ScreenLiftRelaysConfig.cs b/src/PepperDash.Essentials.Devices.Common/Displays/ScreenLiftRelaysConfig.cs
index f39134a41..8890ac95d 100644
--- a/src/PepperDash.Essentials.Devices.Common/Displays/ScreenLiftRelaysConfig.cs
+++ b/src/PepperDash.Essentials.Devices.Common/Displays/ScreenLiftRelaysConfig.cs
@@ -20,15 +20,9 @@ public class ScreenLiftRelaysConfig
public int PulseTimeInMs { get; set; }
///
- /// Gets or sets the RaiseTimeInMs - time in milliseconds for the raise movement to complete
+ /// Gets or sets the MoveTimeInMs - time in milliseconds for the movement to complete
///
- [JsonProperty("raiseTimeInMs")]
- public int RaiseTimeInMs { get; set; }
-
- ///
- /// Gets or sets the LowerTimeInMs - time in milliseconds for the lower movement to complete
- ///
- [JsonProperty("lowerTimeInMs")]
- public int LowerTimeInMs { get; set; }
+ [JsonProperty("moveTimeInMs")]
+ public int MoveTimeInMs { get; set; }
}
}
\ No newline at end of file