Skip to content

Commit 1993cfc

Browse files
CIclaude
andcommitted
fix(gateway): detect coordinator staging before charge ownership checks
A coordinator whose charge end entity was not (yet) owned by the gateway was refused with an ownership error, so its charge window was never written, instead of keeping its staged separate-endpoint sequence. Identify coordinators first, as that decision only needs the entity names and discovery record. Same ordering as the discharge side in #5532. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent fc0ed1c commit 1993cfc

2 files changed

Lines changed: 15 additions & 2 deletions

File tree

‎apps/predbat/inverter.py‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3638,13 +3638,13 @@ def write_and_poll_gateway_charge_window(self, new_start, new_end):
36383638
gateway = components.get_component("gateway") if components else None
36393639
names = ("charge_start_time", "charge_end_time")
36403640
target = (new_start, new_end)
3641+
if gateway is not None and gateway.charge_window_uses_staging(*entities):
3642+
return None # Coordinator slot staging must precede its enable write.
36413643
if owners != ["gateway", "gateway"] or gateway is None:
36423644
self.base.record_status(f"Warn: Inverter {self.id} Hub charge window has inconsistent entity ownership", had_errors=True)
36433645
return False
36443646
if not all(self.check_write_entity("write_and_poll_gateway_charge_window", name, entity, value) for name, entity, value in zip(names, entities, target)):
36453647
return False
3646-
if gateway.charge_window_uses_staging(*entities):
3647-
return None # Coordinator slot staging must precede its enable write.
36483648

36493649
def read_pair():
36503650
"""Read both endpoint entities without substituting desired values."""

‎apps/predbat/tests/test_gateway_charge_window.py‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,19 @@ def test_coordinator_slot_staging_keeps_existing_enable_sequence(self):
203203
self.assertEqual(inverter.write_and_poll_option.call_count, 2)
204204
self.assertEqual(base.errors, [])
205205

206+
def test_coordinator_with_partial_ownership_keeps_staged_sequence(self):
207+
"""A coordinator is identified before ownership checks, so it is never refused by them."""
208+
from gateway import GATEWAY_COORDINATOR_TYPES
209+
210+
for kind in GATEWAY_COORDINATOR_TYPES:
211+
inverter, gateway, base, wire = self.fixture()
212+
gateway._discovery = {"CE123456789": {"type": kind}}
213+
base.dashboard_index_app[TestCommandAck.SLOT_END] = None
214+
self.apply(inverter)
215+
self.assertFalse(any("inconsistent entity ownership" in error for error in base.errors), kind)
216+
self.assertEqual(wire, [], kind)
217+
self.assertEqual(inverter.write_and_poll_option.call_count, 2, kind)
218+
206219
def test_invalid_time_or_pair_is_rejected_before_command(self):
207220
"""The public paired method validates inputs before calling the command sink."""
208221
inverter, gateway, base, wire = self.fixture()

0 commit comments

Comments
 (0)