homeview.maintenance_runs() ignored backoff_until(); it drew a cycle every
night of the wait. Now no run and a plain line (EN, NL). test_maint25.py.
Co-Authored-By: Claude <noreply@anthropic.com>
The mode trade still lets a SAFETY-73 probe (PROBE_W alone) go on while a pack
is in its maintenance CHARGE phase; test_opt20.py g7 pins it. ch_ch_maint
digest moved (B no longer grid-charges beside A's charge).
Co-Authored-By: Claude <noreply@anthropic.com>
While a pack is in its maintenance CHARGE phase the mode trade files no grid
charge for the other packs (why "cycle", mirror of SAFETY-78). The maintenance
shaping check and charge floor aim at peakguard.quarter_target_w, month peak
included (SAFETY-80). Tests through cycle(): test_maint24.py.
Co-Authored-By: Claude <noreply@anthropic.com>
With battery modes on, a grid charge the peak guard (or a measured headroom)
stops in the first 60 s of a period no longer latches off for the period: it
may start again once the period is 60 s old, if the guard is clear and the
headroom is over START_W. A stop after 60 s latches as before. In a young
period no charge starts while the batteries' give is still falling (over
START_W within 15 s), since the meter may not show that drop yet. The guard
itself is unchanged, so a real over-aim load is cut on the same cycle.
DECISIONS/SAFETY-135-young-period.md, test_safety135.py.
Co-Authored-By: Claude <noreply@anthropic.com>
F1 the on-change case is tested as slow, F2 override and direction checks,
F3 the 3.75 s rule in the texts, F4 the quarter figures and the live note.
No behaviour change.
Co-Authored-By: Claude <noreply@anthropic.com>
On a meter whose readings come 4 s or more apart, the loop runs at half its
gain after a move of 150 W or more, until the meter has fallen by half of the
move or 2 cycles have passed. With readings 10 s late on a 5 s meter the export
falls 44 % over oven, kettle and hob, owner shape, steps; 1-2 s meters are
untouched.
Co-Authored-By: Claude <noreply@anthropic.com>
test_safety118's "size rule is gone" and test_validate's "no numeric
maintenance_* fallback" read app/main.py only; the code they guard can now
sit in app/ctl_*.py, where a regression passed unseen. Both read
main_files.source() for main.py now. Tests only; no app change.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Router T241: the comments commit had put new rationale into
MAIN-orchestration.md and cut comment lines that the note at 56fc41f did not
hold.
- DECISIONS/MAIN-orchestration.md is the 56fc41f text plus the one sentence
that the Controller is split into app/main.py and app/ctl_*.py.
- Comments whose content is not in a DECISIONS note at 56fc41f are back in the
code verbatim: MAINT-05 one config, the SAFETY-02, SAFETY-42/34,
SAFETY-30 site half, SAFETY-31 and SAFETY-18 latch blocks in __init__, the
_grade_store_migration docstring and comment, the trade_power_w docstring,
the rows.py docstrings.
- Kept: cuts of text already in the note, the SMA-10 / SAFETY-137 / SMA-2
pointers, the SAFETY-32 line fix.
No code change: maint21_same_ast OK, maint20_same_code -OO same.
Co-Authored-By: Claude <noreply@anthropic.com>
No code change: tools/maint21_same_ast.py and tools/maint20_same_code.py HEAD~
both print OK, and the -OO code objects are the same.
- Trims 56 comment and docstring lines (2088 -> 2032) in app/main.py and the
new files that only restated their MAIN-orchestration.md bullet, each cut to
its first sentence and a pointer. What the cut comments said and the bullet
did not is now in the bullet: why a SAFETY-02 latch is restored, why the
SAFETY-30 site check is not armed at one inverter, the SAFETY-18 re-derive,
the MAINT-05 one-config rule, the OPT-09 sizing rules, the "no plan" row,
the 5 400 s age bound, and the guard being the only latch repair.
- Adds the missing DECISIONS pointers on the SMA-10, SAFETY-137 and SMA-2
comments (FALLBACK_BUDGET_S, write_setpoint, _await_inflight,
_release_last, the retry wait, the failed-release branch, release_zero_sent,
the in-flight write, the stop_event hand-back, the SMA-2 read and driver).
- DECISIONS/SAFETY-32: the stale `main.py:932-936` now names
Controller.pack_floor_soc.
- main.py's docstring and import block say where the Controller's parts are.
Co-Authored-By: Claude <noreply@anthropic.com>
Moves the methods named in the card and the constants only they read out of app/main.py unchanged. TasksMixin: run_control, run_tariff_refresh, fresh_tariff_plan and the store-migration grade. tools/maint21_same_ast.py: OK.
Co-Authored-By: Claude <noreply@anthropic.com>
Moves the methods named in the card and the constants only they read out of app/main.py unchanged. StatusMixin: checks, status_payload, forecast_payload, publish, mqtt_command, handle_action, why_words. tools/maint21_same_ast.py: OK.
Co-Authored-By: Claude <noreply@anthropic.com>
Moves the methods named in the card and the constants only they read out of app/main.py unchanged. AlarmsMixin: the check_* alarms and the loop-alarm helpers. tools/maint21_same_ast.py: OK.
Co-Authored-By: Claude <noreply@anthropic.com>
Moves the methods named in the card and the constants only they read out of app/main.py unchanged. CycleMixin: cycle() and _shadow_settle(). cycle() is not cut up. tools/maint21_same_ast.py: OK.
Co-Authored-By: Claude <noreply@anthropic.com>
Moves the methods named in the card and the constants only they read out of app/main.py unchanged. TradeMixin: trade sizing, the mode runs, the day plan, the peak floors, drain and charge limits, pack actions. tools/maint21_same_ast.py: OK.
Co-Authored-By: Claude <noreply@anthropic.com>
Moves the methods named in the card and the constants only they read out of app/main.py unchanged. WritesMixin: write_setpoint, write_refusal, _write_unit, _hand_back, the refusal evidence, heartbeats and the site release/zero commands. tools/maint21_same_ast.py: OK.
Co-Authored-By: Claude <noreply@anthropic.com>
Moves the methods named in the card and the constants only they read out of app/main.py unchanged. InputsMixin: read_inputs, _read_unit, live_units and the other readers of the meter, packs, PV and forecast. tools/maint21_same_ast.py: OK.
Co-Authored-By: Claude <noreply@anthropic.com>
Moves why_raised, why_key, tariff_plan_row and arbitrage_row out of app/main.py unchanged. main.py imports them, so app.main still exports them. tools/maint21_same_ast.py: OK.
Co-Authored-By: Claude <noreply@anthropic.com>
Prepares the split of app/main.py's Controller into files by job, with no app
change. tools/main_files.py gives a test the text and the AST of main.py plus
the files split from it, and `live`, which sets a global in every file whose
functions read it (and raises when none does). Tests that read main.py's
source, and tests and replay tools that patch a main global (datetime,
compute, resolve, _mono, WRITE_BUDGET_S, ...), go through it, so they pass on
this layout and on the split one. test_split_patches.py fails when a test or
tool patches an app.main attribute that main.py no longer reads, or that
another file also reads. tools/maint21_same_ast.py proves every function,
method and module-level assignment of main.py at the base is found unchanged
in its new file.
Co-Authored-By: Claude <noreply@anthropic.com>
- check_peak_floor_limited: restore the SAFETY-25 line (the reserve sentence is
in messages.peak_reserve() on every branch, unreadable SoC included).
- Three status-page comments that no longer said anything (SAFETY-20 row,
AC 5 delivery row, banner-vs-text) now say their rule in one line.
- tariff_refresh_wanted docstring: no orphan "TARIFF-24)" line.
- MAIN-orchestration.md: blank line before the first heading.
- MAIN-orchestration-archive.md: refilled intro; says how to find a section
from a main.py pointer.
No code change: maint20_same_code.py 16029d7 -> SAME CODE, -OO same.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Nine conflicts in app/main.py, all in comments around new code: every code
line is release/1.0's, every comment follows the part 2 rule.
Co-Authored-By: Claude <noreply@anthropic.com>
SAFETY-136: the line said 10 to 15 s late is not caught, then that 15 s late
is caught after about 4 minutes. It now says 10 s is not caught and 15 s only
after about 4 minutes, as the card says.
SAFETY-138: the 30 s timer that zeroes a late write is the ESPHome board's
watchdog (README), not the inverter's own.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Minor bump, no option changes. Folds "Not in a release yet" into a new
0.21.0 section, adds the contents table for the nine merges on release/1.0
since master 0.20.1, updates the known issues, and rewrites the 0.20.1
SAFETY-128 known issue to say it ships in 0.21.0.
Co-Authored-By: Claude <noreply@anthropic.com>
The 0 W of shutdown and of the Stop button now waits (bounded, 2 s) for a
write already on its way to the same unit, for every driver, GoodWe included.
Once shutdown has begun, cycle, heartbeat and safety-stop writes are dropped
right before the write is created. One shared wait (_await_inflight) serves
the 0 W and SMA-10's _release_last.
Co-Authored-By: Claude <noreply@anthropic.com>
The NL default for a hand-back driver without words (handback._BACK_NL) was
changed to "de eigen sturing" in round 2 at the PR #239 review's request
(comment 2495: the article as in the generic clause). The brief does not name
it, so DECISIONS now says where it came from and a test checks the exact Dutch
sentence. No app code change.
Co-Authored-By: Claude <noreply@anthropic.com>