From a89502a2e229842b54604cd84e4848874c15caaf Mon Sep 17 00:00:00 2001 From: Cesare Giannetti Date: Thu, 19 Jun 2025 19:32:20 +0200 Subject: [PATCH 1/4] Use gpiozero active_high for inverted output Built-in active_high property of LED (and Output in general) handles the inverted output already, simplifying the access. --- octoprint_octorelay/driver.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/octoprint_octorelay/driver.py b/octoprint_octorelay/driver.py index e6b02d44..80d52e6e 100755 --- a/octoprint_octorelay/driver.py +++ b/octoprint_octorelay/driver.py @@ -11,7 +11,7 @@ class Driver(): def __init__(self, pin: int, inverted: bool, pin_factory=None): self.pin = pin # GPIO pin self.inverted = inverted # marks the relay as normally closed - self.handle = LED(pin, pin_factory=pin_factory, initial_value=inverted) + self.handle = LED(pin, pin_factory=pin_factory, active_high=not inverted) # release immediately, avoid lock, allow physical buttons to operate same relays: self.handle.pin_factory.release_pins(self.handle, self.pin) @@ -28,7 +28,7 @@ def open(self): def is_closed(self) -> bool: """Returns the logical state of the relay.""" - return xor(self.inverted, self.handle.is_lit) + return self.handle.is_lit def toggle(self, desired_state: Optional[bool] = None) -> bool: """ @@ -38,7 +38,7 @@ def toggle(self, desired_state: Optional[bool] = None) -> bool: """ if desired_state is None: desired_state = not self.is_closed() - (self.handle.on if xor(self.inverted, desired_state) else self.handle.off)() + (self.handle.on if desired_state else self.handle.off)() return desired_state @classmethod From 35d6f2dfa81a9a80a0164e574d86f9abef2b65a8 Mon Sep 17 00:00:00 2001 From: Cesare Giannetti Date: Fri, 20 Jun 2025 16:56:49 +0200 Subject: [PATCH 2/4] expose inverted as a property derived from the value stored in gpiozero LED class --- octoprint_octorelay/driver.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/octoprint_octorelay/driver.py b/octoprint_octorelay/driver.py index 80d52e6e..5c2a5b3c 100755 --- a/octoprint_octorelay/driver.py +++ b/octoprint_octorelay/driver.py @@ -10,7 +10,6 @@ class Driver(): def __init__(self, pin: int, inverted: bool, pin_factory=None): self.pin = pin # GPIO pin - self.inverted = inverted # marks the relay as normally closed self.handle = LED(pin, pin_factory=pin_factory, active_high=not inverted) # release immediately, avoid lock, allow physical buttons to operate same relays: self.handle.pin_factory.release_pins(self.handle, self.pin) @@ -18,6 +17,16 @@ def __init__(self, pin: int, inverted: bool, pin_factory=None): def __repr__(self) -> str: return f"{type(self).__name__}(pin={self.pin},inverted={self.inverted},closed={self.is_closed()})" + @property + def inverted(self): + """Returns true if the relay is normally closed.""" + return not self.handle.active_high + + @inverted.setter + def inverted(self, value): + """Changing this will invert is_closed value without inverting the pin""" + self.handle.active_high = not value + def close(self): """Activates the current flow through the relay.""" self.toggle(True) From 36c74a0f5fb53626f616307321c44176b40b104e Mon Sep 17 00:00:00 2001 From: Cesare Giannetti Date: Fri, 20 Jun 2025 15:25:35 +0200 Subject: [PATCH 3/4] fix test --- tests/test_driver.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_driver.py b/tests/test_driver.py index b2aeeb9e..f18b4543 100644 --- a/tests/test_driver.py +++ b/tests/test_driver.py @@ -75,7 +75,7 @@ def test_ensure(self): self.assertFalse(relay1.inverted) # Test retrieving the existing relay with the same pin and inversion - relay2 = Driver.ensure(17, True, MockFactory()) + relay2 = Driver.ensure(17, False, MockFactory()) self.assertIs(relay1, relay2) self.assertEqual(len(Driver.cache), 1) # Should still be 1 From d653bc1f9a730169162a539a77816bc51d44ed86 Mon Sep 17 00:00:00 2001 From: Cesare Giannetti Date: Sat, 21 Jun 2025 17:46:32 +0200 Subject: [PATCH 4/4] adjust driver test relay state now reflects pin state. inversion is now managed by gpiozero. --- tests/test_driver.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/test_driver.py b/tests/test_driver.py index f18b4543..142fe4e2 100644 --- a/tests/test_driver.py +++ b/tests/test_driver.py @@ -27,7 +27,7 @@ def test_serialization(self): def test_close(self): cases = [ { "relay": Driver(18, False, MockFactory()), "expected_pin_state": True }, - { "relay": Driver(18, True, MockFactory()), "expected_pin_state": False } + { "relay": Driver(18, True, MockFactory()), "expected_pin_state": True } ] for case in cases: case["relay"].close() @@ -36,7 +36,7 @@ def test_close(self): def test_open(self): cases = [ { "relay": Driver(18, False, MockFactory()), "expected_pin_state": False }, - { "relay": Driver(18, True, MockFactory()), "expected_pin_state": True } + { "relay": Driver(18, True, MockFactory()), "expected_pin_state": False } ] for case in cases: case["relay"].open() @@ -46,8 +46,8 @@ def test_is_closed(self): cases = [ { "mocked_state": 1, "inverted": False, "expected_relay_state": True }, { "mocked_state": 0, "inverted": False, "expected_relay_state": False }, - { "mocked_state": 1, "inverted": True, "expected_relay_state": False }, - { "mocked_state": 0, "inverted": True, "expected_relay_state": True }, + { "mocked_state": 1, "inverted": True, "expected_relay_state": True }, + { "mocked_state": 0, "inverted": True, "expected_relay_state": False }, ] for case in cases: relay = Driver(18, case["inverted"], MockFactory()) @@ -58,8 +58,8 @@ def test_toggle__no_argument(self): cases = [ { "mocked_state": 1, "inverted": False, "expected_pin_state": False, "expected_relay_state": False }, { "mocked_state": 0, "inverted": False, "expected_pin_state": True, "expected_relay_state": True }, - { "mocked_state": 1, "inverted": True, "expected_pin_state": False, "expected_relay_state": True }, - { "mocked_state": 0, "inverted": True, "expected_pin_state": True, "expected_relay_state": False }, + { "mocked_state": 1, "inverted": True, "expected_pin_state": False, "expected_relay_state": False }, + { "mocked_state": 0, "inverted": True, "expected_pin_state": True, "expected_relay_state": True }, ] for case in cases: relay = Driver(18, case["inverted"], MockFactory())