Deploy 0.16.0 to master #179

Merged
admin merged 21 commits from deploy-0.16.0 into master 2026-10-08 01:43:38 +02:00
Owner

Deploy add-on 0.16.0 (release/1.0 merge 451729c, PR #178) to master.

Contents: SAFETY-90, MAINT-10, SAFETY-96, SAFETY-95. See goodwe_controller/CHANGELOG.md ## 0.16.0.

  • Owner go for deploy: 2026-10-08.
  • predeploy_check (--deployed-version 0.15.0) on fresh live options: CLEARED; also CLEARED with maintenance_start_hour 17 -> 19 (set right after the update, MAINT-10 makes it a local hour).
  • Merge only after checks / unit (pull_request) is green on this head, pinning head_commit_id.

🤖 Generated with Claude Code

Deploy add-on 0.16.0 (release/1.0 merge 451729c, PR #178) to `master`. Contents: SAFETY-90, MAINT-10, SAFETY-96, SAFETY-95. See `goodwe_controller/CHANGELOG.md` `## 0.16.0`. - Owner go for deploy: 2026-10-08. - predeploy_check (`--deployed-version 0.15.0`) on fresh live options: CLEARED; also CLEARED with `maintenance_start_hour` 17 -> 19 (set right after the update, MAINT-10 makes it a local hour). - Merge only after `checks / unit (pull_request)` is green on this head, pinning `head_commit_id`. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
admin added 21 commits 2026-10-08 01:10:07 +02:00
The start hour was compared with the UTC hour, so the shipped 10 started at
12:00 in a Belgian summer. It is now read in Home Assistant's time zone
(fallback: the container's TZ, then UTC, one log line says which), and the
default moves to 19. DST: a missing hour starts at the first hour after the
jump; a repeated hour starts only in its first pass. Stored options are not
rewritten: a saved 10 now means 10:00 local (DOCS, CHANGELOG).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MAINT-10: DECISIONS says where the release note lives
checks / unit (pull_request) Successful in 23m8s
infra-pin / fresh (pull_request) Successful in 1s
2f5ab1f378
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
SAFETY-90: the peak guard goes on after a P1 meter gap
checks / unit (pull_request) Successful in 31m10s
infra-pin / fresh (pull_request) Successful in 1s
9d3fbe1be9
A meter gap past meter_max_age_s left seconds the quarter average never
counted, so the guard was blind for the rest of the quarter (rig case 7b:
3964.8 W against a 3450 W aim). Once readings are back, the missed seconds
now count at the higher of the limit and the import seen just before and
just after the gap with the batteries at 0 W (peakguard.gap_w/fill_gaps);
the gap's first meter_max_age_s is topped up to that too. Seconds before
the add-on's first reading count at the aim, and the page says part of the
period was not measured (EN/NL). The bill record is unchanged; no option.

DECISIONS/SAFETY-90-peak-guard-after-a-gap.md, test_safety90.py.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
While the add-on is down the batteries are at 0 W (it sets 0 W when it
stops; the ESP32 watchdog after 30 s), so the import before its first
reading was the house, not the aim. Those seconds now count at
gap_w(limit, the import seen first), and after a restart the restored
reading's held 30 s is topped up the same way. Status row: "counted as
high use". Tests take the pre-start import as the house (batteries at
0 W) and add restarts of 10/60/150 s with a restored averager.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
SAFETY-90 review: test that a second gap does not price the first one down
checks / unit (pull_request) Successful in 31m12s
infra-pin / fresh (pull_request) Successful in 1s
24dbbd18ad
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
SAFETY-90: the peak guard goes on after a P1 meter gap (#175)
Conflicts: main.py's maintenance import (base added CHARGE, HOLD; branch added
site_zone) - both kept. DOCS maintenance table: base's longer drain-rate row
kept, start hour row at 19 / local.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MAINT-10: every pack at N=2, the next DST days, plainer upgrade note
checks / unit (pull_request) Successful in 31m20s
infra-pin / fresh (pull_request) Successful in 1s
0b6b34f563
test_maint10.py part 10: at two inverters both packs get the site zone, the
real Controller is identical for 11 UTC and 13 Brussels, and two due packs
across 24/25 Oct 2026 and 27/28 Mar 2027 start at the local hour on
successive days; a pack freed in the repeated hour does not start there.
Part 3 adds 28 Mar 2027. DOCS: an existing install keeps its saved hour, now
read as local time; an hour set to make up for UTC goes back to 19.
DECISIONS: the SAFETY-83 gate is met; section on two inverters.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MAINT-10 review: warn when HA did not answer and TZ is UTC; test the 3 s budget
checks / unit (pull_request) Successful in 31m18s
infra-pin / fresh (pull_request) Successful in 1s
1d28daf8a0
HA can take minutes to start (2026-10-05, 502 from the proxy). Then TZ holds,
which the Supervisor sets from HA's zone, so the hour stays right; only a TZ
of UTC there now logs a WARNING asking for a restart. A test pins the 3 s
budget on the zone request (a mutant without it survived).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
SAFETY-96: a battery the loop cannot move does not freeze it
checks / unit (pull_request) Successful in 31m44s
infra-pin / fresh (pull_request) Successful in 2s
88b9cd470a
At N>=2 a battery out of the split (stale link), with refused writes, or
settling after a step the loop did not make (a charge end, a rejoin) now
counts in the freeze test at its command, not its reading. Its real power
still reaches the loop through the meter. At a drop its share leaves the
loop's start point (the others take over one slew step a cycle); at a
rejoin it enters at its reading. No refusal is learned while a battery's
writes are refused. N=1 and the 46 identity digests are byte-identical.

test_safety78 part 2: the dropped-drain case now rests at B 1510 W (grid at
its -10 W aim) instead of 1500 W; the drop no longer sends B to 2500 W.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
SAFETY-96 review: keep the refusal count over a refused write; test a fast board
checks / unit (pull_request) Successful in 31m53s
infra-pin / fresh (pull_request) Successful in 2s
956711b801
F1: resetting the count on each refused write hid a battery that really
refuses behind a link refusing 1 write in 3 (never learned; learned at
358 s with the count kept). F2: a board at 0 W before its link is stale
learned no refusal untested (mutation R7 survived); now a 1 s-lag case.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
When a battery's drain ended (done, Off, timeout, abort, stale link,
refusal), its drain floor stayed in the site target and landed on the
others: B 0 -> +1600 W in one cycle at a 1200 W house, exporting; under a
solar surplus B gave into the export.

Now, on that cycle, the loop starts from what the others stood at plus the
battery's new command, raised to the house's own point when the house needs
more (never above last cycle's start). While the battery settles, the loop
counts the batteries at its start instead of chasing the drain's tail on
the meter. Then one slew step a cycle. N=1 is unchanged.

test_safety95.py; DECISIONS/SAFETY-95-drain-end.md; one DOCS line.
test_modes.py: opt11 n2_maint re-pinned on purpose (its drain ends by a
refusal at 1655 s; B now stays at the house's 1420 W instead of 2500 W).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
SAFETY-95: N=1 identity for every drain end; plainer DOCS line
checks / unit (pull_request) Successful in 32m35s
infra-pin / fresh (pull_request) Successful in 2s
9c0314a826
test_safety95.py: the N=1 byte-identity now covers all six drain ends
(Off and stale link were missing); end_at()/measure() read through a
default, so test_named_absence.py passes (it failed on bdec66e); a check
that a dropped battery's start is 0 W even under a pinned charge (it
kills a mutation that survived).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
SAFETY-95 review: test the stale-link and contradiction legs; refusal +1 only
checks / unit (pull_request) Successful in 32m34s
infra-pin / fresh (pull_request) Successful in 1s
4707d37019
F1: test_safety95 checks that a dropped draining battery is this rule's
(stale link, N=3, 4000 W: grid import 676 W vs base 786 W); kills R2.
F2: _charge_track tested directly with a contradicting drain (SAFETY-91's
pin as a floor against a closed pack); kills R3. Today a drain contradicts
only through a refusal; the clause stays, as a contradiction sends 0 W.
F3: a drain ends on a refusal only when the battery will not give
(refused > 0, as _held_back()); one that will not take still drains.
Tested both signs; kills R8.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Release 0.16.0
checks / unit (pull_request) Successful in 32m41s
infra-pin / fresh (pull_request) Successful in 1s
756b50ddbb
version 0.15.0 -> 0.16.0 and a closed ## 0.16.0 CHANGELOG section.
No code change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
REL-0.16.0 review: CHANGELOG wording fixes
checks / unit (pull_request) Successful in 32m48s
infra-pin / fresh (pull_request) Successful in 1s
1c607a961a
SAFETY-103 swing 'by about 800 W for about 5 s'; SAFETY-99 'late, by less
than 30 s'; MAINT-10 row: one-at-a-time is not new; SAFETY-96: drop the
test-only 800 W; SAFETY-90 and MAINT-10 rows trimmed to two sentences.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Release 0.16.0 (#178)
infra-pin / fresh (push) Successful in 1s
checks / unit (pull_request) Successful in 32m56s
infra-pin / fresh (pull_request) Successful in 2s
451729c9b5
admin merged commit df154a26d1 into master 2026-10-08 01:43:38 +02:00
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: admin/goodwe-addon#179