From 1ad4d3a5bbc638926f11254ccbb529ae430f5c67 Mon Sep 17 00:00:00 2001 From: glenn schrooyen Date: Thu, 24 Sep 2026 22:59:44 +0200 Subject: [PATCH] PKG-18: cut 0.12.0, so TEL-12's pv_entities can reach an installed 0.11.0 One runtime merge since 0.11.0 (cccb2fa): TEL-12 (#116), a new optional list option `pv_entities` beside `pv_entity`. Option surface 76 -> 77, so a minor bump. config.yaml version and a closed `## 0.12.0` section only. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01BwRLw7eKYj2byVdwB6kbjU --- goodwe_controller/CHANGELOG.md | 132 +++++++++++++++++++++++++++++++++ goodwe_controller/config.yaml | 2 +- 2 files changed, 133 insertions(+), 1 deletion(-) diff --git a/goodwe_controller/CHANGELOG.md b/goodwe_controller/CHANGELOG.md index 02a046e..6e298b6 100644 --- a/goodwe_controller/CHANGELOG.md +++ b/goodwe_controller/CHANGELOG.md @@ -1,5 +1,137 @@ # Changelog +## 0.12.0 + +**A MINOR BUMP FOR ONE NEW OPTION: `pv_entities` ([[TEL-12]]), for sites with +more than one PV inverter.** The option surface moves **76 keys -> 77**, +`options:` and `schema:` in step. 0.11.0's `pv_entity` takes one entity; a roof +with two or three inverters, each publishing its own power sensor into Home +Assistant, could only record one of them. `pv_entities` is a list, and the +Configuration tab renders it as a repeatable field, so you add your second and +third inverter there rather than in YAML. + +⚠️ **UNSET, NOTHING CHANGES.** `pv_entities` ships as `[]`, which is what every +install that presses *Update* lands on, and empty means "use `pv_entity`" - so +an install with no PV entity still has no PV channel, and an install with one +`pv_entity` keeps exactly the single-entity channel 0.11.0 gave it. **No rule was +added to `validate.py`**, which mentions neither key, so **a stored options blob +that starts the add-on on 0.11.0 starts it on 0.12.0**. There is no +`SystemExit(78)` to plan around on this hop. + +⚠️ **IT IS INSTRUMENTATION ONLY, NOT A CONTROL INPUT.** `app/control.py`, +`allocator.py`, `arbiter.py`, `peakguard.py` and the watchdogs are not in this +release's diff; the runtime files that move are `app/pv.py`, `app/main.py`, +`app/mqtt.py`, `config.yaml` and `translations/`. `tools/tel11_pv_trace.py`, +extended for this card, hashes the controller's decisions on 0.11.0 and on this +tree: the control document is **`846a10a5b89e…` with PV unset and with one +entity, on both trees** - the same hash TEL-11 recorded - and with a +three-entity roster it equals each grid world's unset document, including every +cell where one inverter is dead. Neither the total nor any partial total reaches +a decision. With PV unset or with one entity, the **telemetry** is byte-identical +to 0.11.0's as well. + +Contents of this release, in merge order since 0.11.0 (`cccb2fa`): + +| | | +|---|---| +| `f863eca` | TEL-12 - several PV inverters, and one absent member is an absent TOTAL **⚠️ NEW OPTIONAL OPTION, EMPTY ON EVERY EXISTING INSTALL** | + +### TEL-12 — `pv_entities`: the PV channel for a roof with several inverters + +**WHAT TO DO ON A SITE WITH MORE THAN ONE PV INVERTER.** List every inverter's +power sensor under `pv_entities`: + +```yaml +pv_entities: + - sensor.pv_east_power + - sensor.pv_west_power + - sensor.pv_garage_power +``` + +`sensor.goodwe_pv_power` then carries the **site total**. Each entity's own +`unit_of_measurement` is read, so a kW inverter beside a W one sums correctly; +a sensor with no unit or a non-power unit is refused by name on the status +page, as in 0.11.0. + +⚠️ **`pv_entities` REPLACES `pv_entity` - IT DOES NOT ADD TO IT.** When the list +is set, `pv_entity` is not read at all. **If you are upgrading from 0.11.0 with +`pv_entity` already set, the inverter it names must also be in the list**, or it +drops out of the total. Put it in the list and it is **counted once**, not +twice - that is why the rule is replacement: under a union, the natural action +(put all your inverters in the new field) would double-count the one the old +key already names. While both keys are set the status page shows a caution +saying which one won - *"`pv_entities` is set, so `pv_entity` is IGNORED - the N +entities in the list are what is summed. Clear `pv_entity` to remove this +caution"*. It is a caution, never a refusal to start and never a NOT READY row. +A repeated id inside the list is also counted once. + +⚠️ **IF ANY CONFIGURED PV ENTITY IS ABSENT, THE PUBLISHED TOTAL IS `unknown` - +NOT THE SUM OF THE ONES THAT ANSWERED.** Three inverters configured and one +`unavailable`: `sensor.goodwe_pv_power` goes to `unknown` and the recorder +stores a **gap**. The sum of the other two would be a smaller number +indistinguishable from a real one, and a week later it would read as *"there +was less sun than there was"* - the conclusion this channel exists to test +rather than to manufacture. **A gap in the history, never an understated +number.** The status page, read live, still gives you the diagnostic: `PV —` +plus a caution that says there is **no site total**, names the missing entity +and its reason, and lists what the others reported. An inverter honestly +reporting **0 W** is summed, not treated as absent - shading and nightfall are +real zeros. + +| what happened | `sensor.goodwe_pv_power` | the status page | +|---|---|---| +| neither key set | the entity does not exist | no row | +| all three answer, 1200 W + 2.1 kW + 450 W | `3750.0` | `PV 3 750 W` | +| all three answer, one of them `0 W` | the sum of the two others | the sum | +| one (or more) `unavailable` / missing / bad unit | `unknown` - a recorder gap | `PV —` + a caution naming what is missing | + +⚠️ **THE FIX FOR A MISSING INVERTER IS NOT A TEMPLATE SENSOR THAT SUMS THEM.** +You can still point `pv_entity` at one if you prefer, but a template sensor +summing an `unavailable` source yields the partial total silently - the same +defect one layer down, where this add-on cannot see it or report it. + +**WHY A SECOND KEY, RATHER THAN MAKING `pv_entity` A LIST.** Because an 0.11.0 +install that has `pv_entity` set would not start. Measured against Supervisor +**2026.08.0** by `tools/supervisor_shape_probe.py` §G: with `pv_entity` declared +as a list, the string an 0.11.0 install has persisted is refused - +`Invalid list for option 'pv_entity'` - and a shipped `[]` default does not +rescue it, because the Supervisor's merge keeps your stored string. The one +single-key spelling that accepts both shapes, `match(.*)?`, validates nothing +(it accepts the integer `417`) and renders as a plain text box, so you could not +add a second inverter in the UI. So `pv_entity` stays a string, and the same +probe shows an 0.11.0 options blob - `pv_entity` set, no `pv_entities` key at +all - validating against this version untouched. ⚠️ **Rolling back to 0.11.0 is +also safe**: a schema without `pv_entities` drops the key with a warning and +leaves `pv_entity` standing. + +**Why it matters now:** [[OPT-10]] needs a week of PV history to attribute the +surplus that left the reference site while the pack had room, and that site has +several PV inverters. The week starts when the site total is recorded. + +**Full account: `DECISIONS/TEL-12-several-pv-inverters.md`, with the operator's +version under "Several PV inverters" in `DOCS.md`.** + +### If you are crossing from a version older than 0.10.0 + +⚠️ **THIS SECTION IS NOT THE ONE THAT CAN REFUSE YOU - `## 0.10.0` BELOW IS.** +Updating installs 0.12.0 in one step, so every validator rule added in +0.8.0, 0.9.0 and 0.10.0 arrives at the same moment. Two of them can refuse a +config nobody has edited: [[SAFETY-39]]'s **rule 10** (added in 0.8.0) against +0.7.0's shipped default `slew_w: 1000` - the ceiling is **555.6 W** on a 5000 W +rail - and [[TARIFF-07]]'s three withdrawn `price_provider` ids, +**`engie_dynamic`, `totalenergies_dynamic` and `eneco_eddy`**, each still +refused by name at startup on 0.12.0. Neither is new here, and a site already on +0.10.0 or later has already met both. **The full account of both, including +what to set instead of each withdrawn id, is in `## 0.10.0`**, which withdrew +them and is not superseded by this section. + +`tools/predeploy_check.py` checks your live options against this version's +validator before you press *Update*, and on this tree reports a real version +change from 0.11.0. ⚠️ **Pass it the bare options dict - the `data.options` +object - NOT the whole `ha apps info --raw-json` document.** Handed the wrapped +export it currently prints CLEARED while having checked nothing ([[SAFETY-60]], +open). Extract the options first, e.g. `jq '.data.options'`. + ## 0.11.0 **A MINOR BUMP, AND ONE NEW OPTION IS THE WHOLE REASON FOR THE SECOND DIGIT: diff --git a/goodwe_controller/config.yaml b/goodwe_controller/config.yaml index 72b6ebb..2e301b3 100644 --- a/goodwe_controller/config.yaml +++ b/goodwe_controller/config.yaml @@ -1,5 +1,5 @@ name: GoodWe RS485 Controller -version: "0.11.0" +version: "0.12.0" slug: goodwe_controller description: >- Drives a GoodWe ES/BP battery inverter over RS485 by emulating its smart -- 2.54.0