Commit Graph
798 Commits
Author SHA1 Message Date
admin 8508ac33f0 Merge pull request 'MAINT-21: split app/main.py Controller into ctl_*.py mixins' (#243) from MAINT-21 into release/1.0
infra-pin / fresh (push) Successful in 2s
2026-11-09 08:06:31 +01:00
glenn schrooyenandClaude Opus 5.5 8f0b8477e6 MAINT-21 review: two source scans read the split files too
checks / unit (pull_request) Successful in 31m44s
infra-pin / fresh (pull_request) Successful in 1s
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>
2026-11-09 07:15:14 +01:00
glenn schrooyenandClaude 58b6fa35f5 MAINT-21 round 2: keep the move pure - revert the added DECISIONS text, restore cut comments
checks / unit (pull_request) Successful in 26m32s
infra-pin / fresh (pull_request) Successful in 1s
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>
2026-11-09 05:48:00 +01:00
glenn schrooyenandClaude 902da3f723 MAINT-21: comments only - trim restated notes, add DECISIONS pointers
checks / unit (pull_request) Successful in 25m17s
infra-pin / fresh (pull_request) Successful in 1s
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>
2026-11-09 00:41:34 +01:00
glenn schrooyenandClaude 27a867f34b MAINT-21: app/ctl_tasks.py - Controller methods that run as tasks
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>
2026-11-09 00:30:04 +01:00
glenn schrooyenandClaude 66952beb53 MAINT-21: app/ctl_status.py - Controller methods that build the status and MQTT output
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>
2026-11-09 00:30:00 +01:00
glenn schrooyenandClaude d3504fbbe7 MAINT-21: app/ctl_alarms.py - Controller methods that raise and clear alarms
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>
2026-11-09 00:29:56 +01:00
glenn schrooyenandClaude 3939211176 MAINT-21: app/ctl_cycle.py - Controller.cycle, whole
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>
2026-11-09 00:29:52 +01:00
glenn schrooyenandClaude f7c2d45957 MAINT-21: app/ctl_trade.py - Controller methods for trading
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>
2026-11-09 00:29:48 +01:00
glenn schrooyenandClaude 058ecb7caa MAINT-21: app/ctl_writes.py - Controller methods that write, refuse and hand back
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>
2026-11-09 00:29:44 +01:00
glenn schrooyenandClaude fb0730fe6f MAINT-21: app/ctl_inputs.py - Controller methods that read the inputs
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>
2026-11-09 00:29:40 +01:00
glenn schrooyenandClaude 1b6bb05fb4 MAINT-21: app/rows.py - the status-row and Why-line module functions
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>
2026-11-09 00:29:37 +01:00
glenn schrooyenandClaude 0d49027daa MAINT-21: tests read and patch the Controller through tools/main_files.py
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>
2026-11-09 00:27:07 +01:00
admin 56fc41fffa Merge pull request 'MAINT-20 part 2: comments and docstrings in main.py' (#224) from MAINT-20-2 into release/1.0
infra-pin / fresh (push) Successful in 1s
2026-11-08 22:16:27 +01:00
glenn schrooyenandClaude Opus 5.5 bbbc89b235 MAINT-20 part 2 review: comment-only fixes in main.py and the MAIN notes
checks / unit (pull_request) Successful in 30m13s
infra-pin / fresh (pull_request) Successful in 1s
- 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>
2026-11-08 19:25:37 +01:00
glenn schrooyenandClaude f8df7cd860 MAINT-20 part 2 r2: DECISIONS MAIN-orchestration-archive intro
checks / unit (pull_request) Successful in 25m14s
infra-pin / fresh (pull_request) Successful in 1s
Co-Authored-By: Claude <noreply@anthropic.com>
2026-11-08 18:06:01 +01:00
glenn schrooyenandClaude f5714248f6 MAINT-20 part 2 r2: app/main.py comments, lines 6001-6185 (amain .. main)
Co-Authored-By: Claude <noreply@anthropic.com>
2026-11-08 18:06:01 +01:00
glenn schrooyenandClaude 40ef27c367 MAINT-20 part 2 r2: app/main.py comments, lines 5501-6000 (status_payload .. maint_site_tz)
Co-Authored-By: Claude <noreply@anthropic.com>
2026-11-08 18:06:01 +01:00
glenn schrooyenandClaude b05ad2b273 MAINT-20 part 2 r2: app/main.py comments, lines 5001-5500 (shutdown .. checks)
Co-Authored-By: Claude <noreply@anthropic.com>
2026-11-08 18:06:01 +01:00
glenn schrooyenandClaude 31d13ee8bb MAINT-20 part 2 r2: app/main.py comments, lines 4501-5000 (check_peak_saturated .. publish)
Co-Authored-By: Claude <noreply@anthropic.com>
2026-11-08 18:06:00 +01:00
glenn schrooyenandClaude 47e99a37bc MAINT-20 part 2 r2: app/main.py comments, lines 4001-4500 (_shadow_settle .. check_output_clamp)
Co-Authored-By: Claude <noreply@anthropic.com>
2026-11-08 18:06:00 +01:00
glenn schrooyenandClaude d298f8aa4f MAINT-20 part 2 r2: app/main.py comments, lines 3501-4000 (module level)
Co-Authored-By: Claude <noreply@anthropic.com>
2026-11-08 18:06:00 +01:00
glenn schrooyenandClaude 391c4b4411 MAINT-20 part 2 r2: app/main.py comments, lines 3001-3500 (shadow .. cycle)
Co-Authored-By: Claude <noreply@anthropic.com>
2026-11-08 18:06:00 +01:00
glenn schrooyenandClaude 6f94944e44 MAINT-20 part 2 r2: app/main.py comments, lines 2501-3000 (_peak_over_drain .. _read_unit)
Co-Authored-By: Claude <noreply@anthropic.com>
2026-11-08 18:06:00 +01:00
glenn schrooyenandClaude 56d5532b83 MAINT-20 part 2 r2: app/main.py comments, lines 2001-2500 (_reseed_drain_end .. _fill_peak_gaps)
Co-Authored-By: Claude <noreply@anthropic.com>
2026-11-08 18:06:00 +01:00
glenn schrooyenandClaude 9ec0a84129 MAINT-20 part 2 r2: app/main.py comments, lines 1501-2000 (pack_floor_soc .. _reseed_charge_end)
Co-Authored-By: Claude <noreply@anthropic.com>
2026-11-08 18:06:00 +01:00
glenn schrooyenandClaude 6492d179d6 MAINT-20 part 2 r2: app/main.py comments, lines 1001-1500 (_refusal_from_stale .. site_circulating_w)
Co-Authored-By: Claude <noreply@anthropic.com>
2026-11-08 18:06:00 +01:00
glenn schrooyenandClaude f114a19dcc MAINT-20 part 2 r2: app/main.py comments, lines 501-1000 (_refusal_probe .. _refusal_update)
Co-Authored-By: Claude <noreply@anthropic.com>
2026-11-08 18:06:00 +01:00
glenn schrooyenandClaude a5fec377ad MAINT-20 part 2 r2: app/main.py comments, lines 1-500 (tariff_refresh_wanted .. __init__)
Co-Authored-By: Claude <noreply@anthropic.com>
2026-11-08 18:06:00 +01:00
glenn schrooyenandClaude 8fc5f15b03 MAINT-20 part 2 r2: cut the comments that came in with SMA-2, SMA-10, SAFETY-128 and SAFETY-137
Word for word in MAIN-orchestration-archive.md, keyed by the line at 16029d7.
No code change.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-11-08 17:29:27 +01:00
glenn schrooyenandClaude c2b4302dcf Merge release/1.0 (16029d7) into MAINT-20-2
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>
2026-11-08 17:28:03 +01:00
admin 16029d7cb8 Merge pull request 'Release 0.21.0' (#241) from release-0.21.0 into release/1.0
infra-pin / fresh (push) Successful in 2s
checks / unit (pull_request) Successful in 23m41s
infra-pin / fresh (pull_request) Successful in 2s
2026-11-08 10:46:27 +01:00
Claude review 8e26574b03 Release 0.21.0: CHANGELOG wording (SAFETY-136, SAFETY-138 known issues)
checks / unit (pull_request) Successful in 24m11s
infra-pin / fresh (pull_request) Successful in 2s
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>
2026-11-08 10:20:46 +01:00
Claude 0f208a0500 Release 0.21.0: version bump and CHANGELOG
checks / unit (pull_request) Successful in 33m53s
infra-pin / fresh (pull_request) Successful in 2s
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>
2026-11-08 09:08:59 +01:00
admin b3cfcf2df4 Merge pull request 'SAFETY-137: the shutdown 0 W must be the last write every unit hears' (#240) from SAFETY-137 into release/1.0
infra-pin / fresh (push) Successful in 2s
2026-11-08 04:20:04 +01:00
Claude 8953be0362 SAFETY-137: the shutdown and Stop 0 W is the last write every unit hears
checks / unit (pull_request) Successful in 22m59s
infra-pin / fresh (pull_request) Successful in 1s
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>
2026-11-08 02:39:38 +01:00
admin dbdc37b959 Merge pull request 'SMA-10: two idle states, hold 0 W or hand the inverter back' (#239) from SMA-10 into release/1.0
infra-pin / fresh (push) Successful in 1s
2026-11-07 23:43:14 +01:00
glenn schrooyenandClaude 6cae5a2f8f SMA-10 round 4: pin the Dutch default "de eigen sturing"
checks / unit (pull_request) Successful in 30m41s
infra-pin / fresh (pull_request) Successful in 2s
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>
2026-11-07 22:45:08 +01:00
glenn schrooyenandClaude 313503724d SMA-10 round 3: Start clears a failed hand-back streak; the hook is tested
checks / unit (pull_request) Successful in 23m23s
infra-pin / fresh (pull_request) Successful in 2s
When control takes the unit again, release_fails, release_retry_at and
release_zero_sent are reset, so the next Stop is a new streak with its own one
0 W, error line and event. Tests: a streak that Start interrupts without a
recovery; a driver that overrides release_fallback(). _BACK_NL "de eigen
sturing" (round 2) is kept.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-11-07 21:31:28 +01:00
glenn schrooyenandClaude d26cc573fe SMA-10 round 2: a failed release sends 0 W once, retries at heartbeat pace
checks / unit (pull_request) Successful in 23m37s
infra-pin / fresh (pull_request) Successful in 2s
When release() fails (False, raises or hangs past RELEASE_BUDGET_S) the unit
gets one bounded write(0) through the new Driver.release_fallback() hook
(default write(0)), then nothing more, so the inverter's own fallback timer is
not reset. The release is retried once per heartbeat, not on every idle
write. One error line and one action-log event per failure streak, one line on
recovery. Dutch default "terug naar de eigen sturing". DECISIONS says what a
unit is left at.

test_messages.py: the RELEASE_BUDGET_S data entry is gone again (the line
that needed it is gone).

Co-Authored-By: Claude <noreply@anthropic.com>
2026-11-07 20:17:01 +01:00
glenn schrooyenandClaude a1be9c48b3 SMA-10: two idle states, hold 0 W or hand the inverter back
checks / unit (pull_request) Successful in 24m9s
infra-pin / fresh (pull_request) Successful in 1s
A driver now says whether its release() hands the inverter back
(Driver.hands_back; GoodWe is False, so release() stays write(0) and the
Controller never calls it). command_site_release() sits next to
command_site_zero(): the Stop button and shutdown() (so SIGTERM, restart,
control task death and a late config refusal) hand a hand-back unit back,
once, and zero every other unit as before. Safety stops and refusals still
hold 0 W for every unit. Shadow mode releases a hand-back unit. After a
release nothing is written to the unit (heartbeat and cycle included) until
control starts again. release() is bounded by RELEASE_BUDGET_S (= the write
budget), runs concurrently per unit, and a raising or hanging release on one
unit does not stop the others.

The "set to 0 W" sentences for Stop, restart, shadow mode and the stopped
banner are built from the roster (app/handback.py, EN and NL, words from the
driver); a GoodWe-only roster keeps every old literal. A handed-back unit
shows "Back under <its control>" on Home.

Existing test edited: test_messages.py (two classification entries for the
CRITICAL line that moved into a roster builder, and the new release bound).

Co-Authored-By: Claude <noreply@anthropic.com>
2026-11-07 18:21:28 +01:00
admin 57c8d1b912 Merge pull request 'SMA-2: each inverter is reached through a driver; GoodWe behaviour unchanged' (#238) from SMA-2 into release/1.0
infra-pin / fresh (push) Successful in 2s
2026-11-07 14:46:58 +01:00
glenn schrooyenandClaude c015230452 SMA-2 round 2: keep the driver keys out of the schema, translations and labels
checks / unit (pull_request) Successful in 23m13s
infra-pin / fresh (pull_request) Successful in 1s
Owner decision 2026-11-07: clients see nothing new until SMA-4. config.yaml,
translations, labels and the page layout are back to 23ff9d0's. The validation
of the keys stays for callers that skip the schema.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-11-07 13:09:31 +01:00
glenn schrooyenandClaude 0b20f53cf8 SMA-2 round 2: census keeps ACTUATOR_WRITE_KEYS, setters, NaN/inf port refused, tests
Co-Authored-By: Claude <noreply@anthropic.com>
2026-11-07 12:20:56 +01:00
glenn schrooyenandClaude e56eeef8f0 SMA-2: each inverter is reached through a driver; GoodWe behaviour unchanged
checks / unit (pull_request) Successful in 23m14s
infra-pin / fresh (pull_request) Successful in 2s
app/drivers.py holds one Driver per roster entry (configured, actuator_id,
limits, read, write, release, probe, link_stale_s, batt_bound, settle_s, brand,
words). GoodWeEsphomeDriver makes the calls the code it replaced made, in the
same order. Inverter gets driver; has_writer, is_live and link_note read it. The
SoC/power read, the write, the device range (run_control, amain, the settings
panel), rule 2 per unit and the SAFETY-28 census go through it.

setpoint_entity is optional per entry beside driver, host, port, unit_id and
maintenance; only goodwe_esphome is selectable, the rest are checked and unused.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-11-07 11:28:47 +01:00
admin 23ff9d0920 Merge pull request 'OPT-38: keep tomorrow's solar forecast when one sensor goes stale in the afternoon' (#237) from OPT-38 into release/1.0
infra-pin / fresh (push) Successful in 1s
2026-11-07 06:56:10 +01:00
glenn schrooyenandClaude 0bacafadf0 OPT-38 round 2: a kept tomorrow reading is flagged kept only from 20:00
checks / unit (pull_request) Successful in 23m11s
infra-pin / fresh (pull_request) Successful in 1s
Before the bell ends it replaced today's Home line and the event log with
the kept-for-tonight wording. stand_in() docstring covers the tomorrow case.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-11-07 06:02:00 +01:00
glenn schrooyenandClaude c43873b142 OPT-38: a kept tomorrow forecast reading stands in at any hour
checks / unit (pull_request) Successful in 22m54s
infra-pin / fresh (pull_request) Successful in 1s
One stale Forecast.Solar tomorrow sensor dropped the whole OPT-17 sum from
17:54 to 20:00. Today readings and the OPT-34 morning rules are unchanged.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-11-07 04:24:01 +01:00
admin b291262ee1 Merge pull request 'SAFETY-129: find late Home Assistant readings sooner at a 5 s meter' (#236) from SAFETY-129 into release/1.0
infra-pin / fresh (push) Successful in 2s
2026-11-07 01:24:39 +01:00
glenn schrooyenandClaude c70eeb6916 SAFETY-129 round 3: the real reason for the 16 s limit, a slow-start check, the row's tail
checks / unit (pull_request) Successful in 23m1s
infra-pin / fresh (pull_request) Successful in 1s
Co-Authored-By: Claude <noreply@anthropic.com>
2026-11-07 00:12:33 +01:00