Repository navigation
fix(gateway): apply direct battery charge windows as paired slots - #5531
Merged
Merged
Conversation
mgazza
marked this pull request as ready for review
October 13, 2026 01:28
springfall2008
pushed a commit
that referenced
this pull request
Oct 14, 2026
…te (#5532) * fix(gateway): never enable discharge/export after a failed window write For a direct Hub battery, adjust_force_export wrote the discharge start and end as two separate set_discharge_slot commands, then enabled the scheduled discharge and switched to export mode regardless of whether the endpoint writes were applied. schedule_write_ok only withheld the later commit record. When the end write was refused after the start was accepted, the Hub was left enabled with a stale wrapping window (e.g. 02:55-00:01) and actively exporting. Send both endpoints in one full-serial set_discharge_slot for direct Hub batteries and verify both endpoint read-backs before continuing. Any refusal, staged or partial ACK, timeout, publication error or swallowed async-bridge exception returns before the enable write, export mode and commit, leaving the commit pending for the next cycle to retry. Both endpoints are tracked in the control ledger and share one backoff key. Read-only, entity ownership and serial checks refuse before publishing. Coordinators keep their staged separate-endpoint sequence; other providers, disable and midnight windows are unchanged. Mirrors the charge-window fix in #5531. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018BaJjLpuMEtQYfvmTRcFJf * fix(gateway): detect coordinator staging before discharge ownership checks A coordinator whose discharge end entity was not (yet) owned by the gateway was refused with an ownership error instead of keeping its staged separate-endpoint sequence. Identify coordinators first, as that decision only needs the entity names and discovery record. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018BaJjLpuMEtQYfvmTRcFJf * fix(gateway): stop an armed export when its window move is refused; no re-poll of a refusal - When the paired window write fails while scheduled export is already enabled, turn the schedule off and return to Eco instead of leaving the previous window exporting on a plan that has moved away from it (skipped in read-only mode). - A refusal of the exact paired command ends the attempt immediately: the poll stops when it arrives, and a retry inside the ack window is not published (_send_control would suppress it anyway), so a refused write no longer holds the engine for the full poll timeout per retry. - Drop the unreachable end-serial comparison (the end entity name is derived from the start, so it carries the same serial) and name the test case after what it exercises. - Document that an exception inside a running event loop reaches the caller as None via run_async's thread, not as an exception. - Test the write/moved counters for the paired path. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018BaJjLpuMEtQYfvmTRcFJf * test(gateway): a successful move of an exporting schedule stays enabled The fail-safe disable applies only to a failed window write. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018BaJjLpuMEtQYfvmTRcFJf * fix(gateway): only an explicit Hub refusal turns off an exporting schedule The fail-safe disable fired on any unverified window write, including a slow but successful one (ACK outside the poll, older firmware without an applied block, telemetry not yet landed). That stopped a correctly re-windowed export for a cycle and toggled enable/mode. Take the disable path only when the Hub refused the exact paired command; an unconfirmed write leaves the running export alone and keeps the commit pending for the next cycle. Document that execute.py still reports "Exporting" for a cycle whose window was refused. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018BaJjLpuMEtQYfvmTRcFJf --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: CI <ci@example.com>
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
When a direct Hub battery's new charge start equals the old charge end, separate start/end commands briefly form a zero-length slot. The Hub refuses the first command, then accepts the new end with the old start. A midnight-spanning window can consequently retain an earlier start while the engine logs the intended pair.
Send both endpoints in one existing
set_charge_slotcommand for direct battery inverters and verify both endpoint entities through the normal applied-ACK/telemetry path. Refusal, staged or incomplete ACKs, timeout and publication errors stop the update before a success log or enable write. Track both endpoints in the control ledger, preserve read-only and entity ownership checks, and retain the existing EMS/Gateway coordinator staging sequence and other provider paths.Validation: Gateway, inverter and inverter-write-poll suites pass, including nine joined engine/JSON/ACK regressions covering the observed failure shapes, endpoint swap, errors, read-only, ownership, ledger, repeat-cycle and coordinator behavior. Changed-file pre-commit checks pass. Fresh GitNexus analysis reports four expected files, medium risk and two indexed affected flows.
Focused review found and verified the correction for async bridge exceptions: select coordinator staging synchronously and require explicit publication success, so a swallowed coroutine exception cannot trigger individual fallback writes. Deeper independent review traced scheduling, entity ownership, full-serial routing, ACK cache, retry/backoff, ledger and coordinator compatibility and found no additional confident regression. All nine new tests independently pass. Normal CI38331810602 passes at08d96daf1a071e67eda05f2283e7fa67d00eab3d. Ready for review; physical validation remains outstanding. MQTT transport, inverter state and ACK delivery are synthetic in these tests. No physical hardware validation, deployment, firmware release or customer configuration change.