diff --git a/goodwe_controller/CHANGELOG.md b/goodwe_controller/CHANGELOG.md index e22c7d9..df0a58d 100644 --- a/goodwe_controller/CHANGELOG.md +++ b/goodwe_controller/CHANGELOG.md @@ -1,5 +1,86 @@ # Changelog +## 0.9.0 + +⚠️ **MINOR FOR NEW FUNCTIONALITY, NOT FOR A BREAK - AND THAT DISTINCTION IS THE +POINT.** 0.8.0 was minor because a config that started on 0.7.0 could be +REFUSED on it (rule 10 against 0.7.0's own default `slew_w: 1000`). Nothing of +that kind is true here, and it was checked rather than assumed: + +- `options:` and `schema:` both hold **71 top-level keys**, the same 71 as + 0.8.0. The one schema addition is a per-entry key, `capacity_kwh: + float(0.1,200)?` - **note the trailing `?`, it is OPTIONAL**. +- An unmodified 0.8.0-era config validates clean on 0.9.0. Measured on both the + flat single-inverter shape the reference install runs and an N=2 roster with + no `capacity_kwh` on any entry: **0 problems each**. +- The two refusals [[OPT-09]] adds fire only on configs that USE the new key - + an unparseable value, or the key stated on one roster entry and left empty on + another. Neither can reach a config written before the key existed. + +So: existing installs upgrade untouched, and the bump is minor because the +option surface and the behaviour grew. + +Contents, in merge order since 0.8.0 (`06661c5`): + +| | | +|---|---| +| `3d29993` | UX-02 - the validator's refusals, next to the field, before the save | +| `fdb3b69` | OPT-09 - the weights parameter nothing ever passed, and what it cannot do | +| `fcaa724` | PKG-09 - refuse a deploy that would be a no-op, or that the add-on would reject | + +**UX-02** builds the real configuration surface in the ingress panel, beside the +Supervisor Configuration tab that cannot be removed. The prize is not styling: +`validate.py` writes long cross-field refusals the Supervisor UI *structurally +cannot show*, and until now an operator met them as **exit 78 in a log**, after +saving and restarting. ⚠️ Two config surfaces now exist and the DECISIONS record +names the **stored options blob** as authoritative, neither UI. + +**OPT-09** makes the architecture's one free parameter real. `main.py` called +`resolve()` without weights, so the split was a fixed 50/50 regardless of what +either pack could deliver; it now passes SoC- and capacity-aware weights. + +⚠️ **OPT-09 SHIPS A DISCLOSED DETECTION REGRESSION AT N=2.** `delivery. +delinquent()` reads an ABSOLUTE 500 W gap, so weighting a low-headroom unit down +to a small share puts a unit delivering **none of what it was asked** under the +threshold. Measured: a wedged inverter goes from named on 199 of 200 cycles to +**0 of 200** at a load its sibling can carry alone. The alarm returns once the +healthy pack saturates - swept, that is a 5000 W load on the fixture, **below** +the proportional-regime formula's 6500 W and not above it. ⚠️ **N=1 IS +UNAFFECTED**: `allocator.plan()` returns before weights are read at all. The +closure is card SAFETY-43. + +**PKG-09** adds `tools/predeploy_check.py`. ⚠️ **RUN IT BEFORE DEPLOYING THIS +RELEASE** - it is what caught the fact that 0.8.0 had five runtime commits +behind it, which is why this section exists: + + docker run --rm -v :/w:ro goodwe-addon-checks:1 \ + python3 /w/goodwe_controller/tools/predeploy_check.py \ + --deployed-version 0.8.0 --options /w/live-options.json + +Exit `0` cleared, `1` REFUSED, `2` NOT CLEARED. ⚠️ **2 is not a pass.** + +✅ **RUN AGAINST THIS RELEASE, 2026-09-07: CLEARED, exit 0.** The reference +install's options were exported from the Supervisor (70 keys) and checked twice +- the stored blob as-is, and the **effective post-upgrade config**, being those +70 keys merged over 0.9.0's 71 defaults. Both pass. `max_w` 5000, `slew_w` 400 +against a rule-10 ceiling of 555.6 W. + +⚠️ **THE STORED BLOB HAS 70 KEYS, NOT 71, AND THAT IS THE INTERESTING PART.** +`delivery_hold_s` arrived with SAFETY-42 in 0.8.0, so an install running 0.7.0 +has never stored it - and it is REQUIRED in the schema, no trailing `?`, which +is the shape that becomes exit 78. It resolves to its `options:` default of +**185**, because the Supervisor sets `App.options = deepmerge(config.yaml +defaults, stored options)`; `tools/supervisor_selfapi_probe.py` §D pins that +against a real Supervisor image rather than assuming it. The second check above +exists because "your saved settings are valid" and "what the box will actually +run is valid" are different questions, and only the second one matters. + +⚠️ **THE VERSION WENT UNBUMPED FOR THE THIRD TIME IN THREE DAYS** - PKG-06 six +merges, PKG-07 six, this one five - and this time it was caught by PKG-09's own +tool during PKG-09's review rather than by a person. `ha apps update` against an +unchanged version installs nothing and reports success. PKG-10 carries the +post-merge alarm so that noticing stops being a human's job. + ## Unreleased ⚠️ **THIS HAS NO VERSION AND CANNOT REACH AN INSTALL UNTIL IT GETS ONE.** diff --git a/goodwe_controller/config.yaml b/goodwe_controller/config.yaml index 3d6a6b1..8a96dbe 100644 --- a/goodwe_controller/config.yaml +++ b/goodwe_controller/config.yaml @@ -1,5 +1,5 @@ name: GoodWe RS485 Controller -version: "0.8.0" +version: "0.9.0" slug: goodwe_controller description: >- Drives a GoodWe ES/BP battery inverter over RS485 by emulating its smart