fix(alphaess): let a same-cycle target SoC correction past the write pacer - #4776
Merged
Merged
Conversation
…pacer Predbat commits a schedule in stages - charge window, then the enable switch, then the target SoC - pressing the schedule write button after each one. The first commit of a cycle therefore carried whatever target SoC the control entity still held from the previous cycle, and the corrected value that arrived seconds later was held for the whole alphaess_min_write_interval. On a manual charge for the live slot that meant AlphaESS ran a "charge to 10%" schedule for five minutes with the house on the grid. A correction landing within ALPHAESS_WRITE_SETTLE_SECONDS of a successful write is now treated as the rest of the same schedule update and allowed through, capped at ALPHAESS_WRITE_BURST_MAX writes so the 24-hour write budget stays bounded. Only successful writes open a burst, so rejected-write pacing (6053, 6008) is unchanged. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
🟢 Approval recommended
The pacing change is narrowly scoped to AlphaESS, is explicitly bounded, and is backed by focused regression and behavioral tests covering the new exemption and its limits.
Pull request overview
This PR fixes an AlphaESS-specific pacing edge case where Predbat’s staged schedule commits (window → enable → target SoC) could leave the inverter running a stale target SoC for the full alphaess_min_write_interval, by allowing a small bounded “correction burst” shortly after a successful write.
Changes:
- Add a settle-window exemption to the AlphaESS write pacer so same-cycle corrections can pass within a bounded burst after a successful write.
- Introduce
ALPHAESS_WRITE_SETTLE_SECONDSandALPHAESS_WRITE_BURST_MAXconstants and track per-(serial,direction) burst state in-memory (not persisted). - Update/extend AlphaESS control tests and adjust two existing tests to ensure they remain outside the settle window; update documentation to describe the correction behavior.
File summaries
| File | Description |
|---|---|
| docs/components.md | Documents the new “same-cycle correction” behavior and why it exists for AlphaESS staged commits. |
| docs/apps-yaml.md | Expands alphaess_min_write_interval documentation to explain the capped correction window after writes. |
| apps/predbat/tests/test_alphaess_control.py | Adds targeted regression tests for GH#4769 and burst behavior; shifts two timing-based tests to avoid the settle window. |
| apps/predbat/alphaess.py | Implements the correction-burst pacing exemption and burst accounting on successful writes only; keeps burst state non-persisted. |
| apps/predbat/alphaess_const.py | Adds constants controlling settle window duration and burst cap. |
Review details
- Files reviewed: 5/5 changed files
- Comments generated: 0
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
springfall2008
added a commit
that referenced
this pull request
Aug 27, 2026
Read through the daemon's per-issue logs at ~/predbat-triage-bot/logs (both the pending and reviewed/ directories) and added confirmed, working-tree-verified findings the file didn't already have: - create_debug_yaml() masks only the "args" key; args_from_apps_yaml (added v8.48.1) is a second, unmasked deepcopy with real secrets.yaml values, and is still present on main - a live credential-leak trap for anyone quoting a reporter's debug yaml in a comment. - Solis: adjust_force_export()/adjust_inverter_mode() press the update button every cycle on H-M-format inverters regardless of whether anything changed, distinct from the existing CID 636 TOU-bit notes. - AlphaESS: the one-write-behind schedule pacer bug (now fixed in PR #4776) and the legacy batUseCap floor (errno 10001 signature). - Ohme's vendored ohmepy client was several versions behind upstream with withdrawn v1 control routes - also since fixed on main. - New rows: Load ML CPU-spike mechanism, the savings_total vs savings_yesterday metric mismatch, and the components.py auto-config gate's false-positive "skipping interface" warning. - Two new traps: SoC-quantisation false positives on "stuck charge" reports, and the Companion app ignoring Content-Disposition. Each entry either cites the issue it came from or is marked as already fixed on main, per the file's own "confirm before you write it down" rule. Added the small handful of new dictionary words (BLAS, threadpoolctl, unredacted, unreproduced) cspell flagged.
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.
This is an automated draft PR generated from issue #4769 — a maintainer should review it before merging.
Fixes #4769
Summary
Predbat commits an AlphaESS schedule in stages — charge window, then the enable switch, then the target SoC — pressing the schedule write button after each one (
INVERTER_DEF time_button_press). The first commit of a cycle therefore carries whatever target SoC the control entity still holds from the previous cycle, andalphaess_min_write_intervalthen held the corrected value that arrived ~3 seconds later for the full 300s. On the reporter's manual charge for the already-live slot that meant AlphaESS ran achargeLimit: 10schedule while the battery sat at 69%, with the house on the grid._write_allowed()now exempts one thing: a correction landing withinALPHAESS_WRITE_SETTLE_SECONDS(60s, and never longer than the user's ownalphaess_min_write_interval) of a successful write is treated as the rest of the same schedule update rather than a new one. The burst is capped atALPHAESS_WRITE_BURST_MAX(3) writes, so the cost against the documented 24-hour write budget stays a small constant per pacing interval instead of becoming one write per tick.Two properties are deliberately preserved:
save_control(). A restart falls back to full pacing (the safe direction) rather than handing a restart loop a fresh allowance each time.Two existing tests moved their second write from t+10s to t+100s. Their intent — "a genuinely new change inside the interval is held, not dropped" — is unchanged; they just now sit outside the settle window instead of accidentally inside it.
Testing
python3 -m pre_commit run --all-files— all hooks pass (run_pre_commit's own invocation is blocked by this environment's permission mode, so its two halves were run separately).unit_test.py --quick, whatrun_pre_commitruns after the hooks) — all tests passed, 4 slow tests skipped, 104.59s.tools/triage_test.sh alphaess_control— passes, including four new cases: the GH#4769 repro (asserting thechargeLimitvalues that actually reach the POST body are[10, 100], not just the call count), the burst cap and its return to normal pacing, a rejected write opening no burst, and the settle window never outlasting a shortenedalphaess_min_write_interval.alphaess_const,alphaess_api,alphaess_config,alphaess_publish,alphaess_storage) — all pass.Notes
Two contributing factors were identified during triage; this PR fixes the second. The first — that
adjust_charge_window()presses the commit button beforeadjust_battery_target()writes the target SoC (inverter.py:3226vsinverter.py:2024) — is structural inexecute.py, which writes every charge window before it reaches theset_soc_enableblock, and affects Solis and Fox as well. Predbat's design already tolerates it by committing a second time with the corrected value; only AlphaESS's pacer turned that intermediate state into a five-minute one. Fixing the pacer reduces the exposure from ~300s to ~3s without touching the shared inverter write ordering. If you would rather the stale commit never went out at all, that is a larger change and worth doing separately.Worth flagging from the same log:
inverter_soc_reset: truewould normally parkcharge_limitat 100 between windows and mask this entirely, but it is gated onnot self.inverter_hybrid(execute.py:646, 682, 703, 715) and the reporter's system reportsinverter_hybrid([True]), so it is inert for them.🤖 Generated with Claude Code