PKG-18: cut 0.12.0, so TEL-12's pv_entities can reach an installed 0.11.0 #117

Merged
admin merged 1 commits from PKG-18-cut-0.12.0 into release/1.0 2026-09-24 23:09:29 +02:00
Owner

Cuts 0.12.0 on release/1.0 so TEL-12 (#116) can reach an installed 0.11.0. Diff: config.yaml (version line) and CHANGELOG.md (new ## 0.12.0 section). Nothing else.

One runtime merge since 0.11.0 (cccb2fa): f863eca TEL-12, a new optional list option pv_entities beside pv_entity. Option surface 76 -> 77, so a minor bump. No validator rule added; an 0.11.0 options blob validates untouched (TEL-12's §G probe against Supervisor 2026.08.0).

The entry tells the operator:

  • pv_entities is for sites with more than one PV inverter;
  • it replaces pv_entity when set (it is not added to it). An operator upgrading with pv_entity set must include that inverter in the list, where it is counted once. The status page caution names which key won;
  • if any configured PV entity is absent, the published total is unknown (a recorder gap), never a partial sum;
  • it is instrumentation only, not a control input. Unset, and with a single entity, it behaves exactly as 0.11.0 (control hash 846a10a5… unmoved);
  • why it is a second key: with pv_entity declared as a list, Supervisor 2026.08.0 refuses an 0.11.0 install's stored string (Invalid list for option 'pv_entity');
  • the pre-0.10.0 crossing note (slew rule 10, the three withdrawn price_provider ids) points at ## 0.10.0, as ## 0.11.0 does;
  • the predeploy check must be given the bare data.options dict, not the wrapped ha apps info --raw-json export (SAFETY-60, open).

Verification

  • Version gate 11a–11d: pass. Exactly one contents table, ## 0.12.0 == config.yaml 0.12.0, all 3 RETIRED_PROVIDERS named, exactly 1 ## Unreleased below.
  • Full suite: ran in goodwe-addon-checks:1 with checks.yml's exact command, on a purged tree mounted :ro, one file at a time. 31/31 PASS.
  • predeploy_check.py --deployed-version 0.11.0: reports 0.11.0 -> 0.12.0, which is a real version change and no longer the NO-OP refusal it gave at f863eca. On a valid bare options blob the check clears.
  • SAFETY-60, reproduced locally so the changelog warning is based on a test: an options blob with slew_w: 5000 was REFUSED when passed bare, and printed CLEARED, exit 0 when passed wrapped as {"data":{"options":…}}.

Not deployed, not tagged. master was not touched. Shipping needs a separate release/1.0 -> master PR.

🤖 Generated with Claude Code

https://claude.ai/code/session_01BwRLw7eKYj2byVdwB6kbjU

Cuts **0.12.0** on `release/1.0` so TEL-12 (#116) can reach an installed 0.11.0. **Diff: `config.yaml` (version line) and `CHANGELOG.md` (new `## 0.12.0` section). Nothing else.** One runtime merge since 0.11.0 (`cccb2fa`): `f863eca` TEL-12, a new optional list option `pv_entities` beside `pv_entity`. Option surface 76 -> 77, so a **minor** bump. No validator rule added; an 0.11.0 options blob validates untouched (TEL-12's §G probe against Supervisor 2026.08.0). The entry tells the operator: - `pv_entities` is for sites with more than one PV inverter; - it **replaces** `pv_entity` when set (it is not added to it). An operator upgrading with `pv_entity` set must include that inverter in the list, where it is counted once. The status page caution names which key won; - if any configured PV entity is absent, the published total is `unknown` (a recorder gap), **never a partial sum**; - it is instrumentation only, not a control input. Unset, and with a single entity, it behaves exactly as 0.11.0 (control hash `846a10a5…` unmoved); - why it is a second key: with `pv_entity` declared as a list, Supervisor 2026.08.0 refuses an 0.11.0 install's stored string (`Invalid list for option 'pv_entity'`); - the pre-0.10.0 crossing note (slew rule 10, the three withdrawn `price_provider` ids) points at `## 0.10.0`, as `## 0.11.0` does; - the predeploy check must be given the **bare `data.options` dict**, not the wrapped `ha apps info --raw-json` export (SAFETY-60, open). ## Verification - **Version gate 11a–11d:** pass. Exactly one contents table, `## 0.12.0` == `config.yaml` `0.12.0`, all 3 `RETIRED_PROVIDERS` named, exactly 1 `## Unreleased` below. - **Full suite:** ran in `goodwe-addon-checks:1` with `checks.yml`'s exact command, on a purged tree mounted `:ro`, one file at a time. **31/31 PASS.** - **`predeploy_check.py --deployed-version 0.11.0`:** reports `0.11.0 -> 0.12.0`, which is a real version change and no longer the NO-OP refusal it gave at `f863eca`. On a valid bare options blob the check clears. - **SAFETY-60, reproduced locally so the changelog warning is based on a test:** an options blob with `slew_w: 5000` was **REFUSED** when passed bare, and printed **CLEARED, exit 0** when passed wrapped as `{"data":{"options":…}}`. Not deployed, not tagged. `master` was not touched. Shipping needs a separate `release/1.0` -> `master` PR. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01BwRLw7eKYj2byVdwB6kbjU
admin added 1 commit 2026-09-24 23:00:35 +02:00
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 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BwRLw7eKYj2byVdwB6kbjU
Author
Owner

Review of PKG-18 at head 1ad4d3a against base f863eca: sound, no blocking findings.

Identity. slug: goodwe_controller is unchanged, so this is the same add-on and its options and /data carry over. version moves "0.11.0" -> "0.12.0". The diff has two files: config.yaml (the version line only) and CHANGELOG.md (+132).

Gate 11a–11d. I ran test_version_gate.py in goodwe-addon-checks:1 on a fresh clone at 1ad4d3a, mounted :ro, and it printed all checks passed (exit 0). There is one contents table, ## 0.12.0 equals config.yaml, all three RETIRED_PROVIDERS (eneco_eddy, engie_dynamic, totalenergies_dynamic) are named, and one ## Unreleased remains below. 11c's text reads correctly for a 0.11.0 reader: it says the ids are "not new here" and that a site on 0.10.0 or later "has already met both". The 555.6 W figure matches validate.py:1137 (SLEW_RAIL_FRAC * 5000).

Operator claims, checked against the code at f863eca and DECISIONS/TEL-12-several-pv-inverters.md:

  • Replaces, not adds. pv.entities() returns the list when it is non-empty, and pv_entity is read only when the list is empty. The ids are de-duplicated in order with dict.fromkeys, so an inverter listed in both keys is counted once. The quoted caution text matches main.py:5113-5117 word for word. It is warn=True and never bad, so it cannot cause NOT READY.
  • An absent member makes the total unknown. pv.total() returns None whenever any member returns watts is None. main.py:4513 publishes None, and the status tile's w(None) renders —. A reading of 0 W still counts because the test is is None, not a falsiness check. The table in the changelog matches this, including 3 750 W (f"{v:,.0f} W".replace(",", " ")).
  • Instrumentation only. git diff cccb2fa..f863eca does not touch validate.py, control.py, allocator.py, arbiter.py or peakguard.py. The runtime files that change are exactly the five the entry lists. pv_w is read only by the MQTT value and by the status rows and tile. The control hash 846a10a5b89e… is the one recorded in DECISIONS/TEL-11-pv-production.md:185.
  • Why a second key. The Invalid list for option 'pv_entity' refusal, the deepmerge override detail, match(.*)? accepting the integer 417, and the rollback dropping the key with a warning all match §G of the decision doc. validate.py does not mention either key.

SAFETY-60 warning: correct, reproduced. I built an options blob from the shipped defaults with setpoint_entity set and slew_w: 5000 and ran predeploy_check.py --deployed-version 0.11.0 in the checks image. Passed as the bare dict, it was REFUSED, exit 1. Passed wrapped as {"result":"ok","data":{"options":…}}, it printed CLEARED for deploy., exit 0. The same bare blob with slew_w: 0 cleared, exit 0. So the warning is accurate, and jq '.data.options' is the right extraction. There is one placement note in the next comment, and it does not block.

CI. I traced the green to its run instead of trusting the status. Details are posted with the merge.

**Review of PKG-18 at head `1ad4d3a` against base `f863eca`: sound, no blocking findings.** **Identity.** `slug: goodwe_controller` is unchanged, so this is the same add-on and its options and /data carry over. `version` moves `"0.11.0"` -> `"0.12.0"`. The diff has two files: `config.yaml` (the version line only) and `CHANGELOG.md` (+132). **Gate 11a–11d.** I ran `test_version_gate.py` in `goodwe-addon-checks:1` on a fresh clone at `1ad4d3a`, mounted `:ro`, and it printed `all checks passed` (exit 0). There is one contents table, `## 0.12.0` equals `config.yaml`, all three `RETIRED_PROVIDERS` (`eneco_eddy`, `engie_dynamic`, `totalenergies_dynamic`) are named, and one `## Unreleased` remains below. 11c's text reads correctly for a 0.11.0 reader: it says the ids are "not new here" and that a site on 0.10.0 or later "has already met both". The `555.6 W` figure matches `validate.py:1137` (`SLEW_RAIL_FRAC * 5000`). **Operator claims, checked against the code at `f863eca` and `DECISIONS/TEL-12-several-pv-inverters.md`:** - *Replaces, not adds.* `pv.entities()` returns the list when it is non-empty, and `pv_entity` is read only when the list is empty. The ids are de-duplicated in order with `dict.fromkeys`, so an inverter listed in both keys is counted once. The quoted caution text matches `main.py:5113-5117` word for word. It is `warn=True` and never `bad`, so it cannot cause NOT READY. - *An absent member makes the total `unknown`.* `pv.total()` returns `None` whenever any member returns `watts is None`. `main.py:4513` publishes `None`, and the status tile's `w(None)` renders `—`. A reading of `0 W` still counts because the test is `is None`, not a falsiness check. The table in the changelog matches this, including `3 750 W` (`f"{v:,.0f} W".replace(",", " ")`). - *Instrumentation only.* `git diff cccb2fa..f863eca` does not touch `validate.py`, `control.py`, `allocator.py`, `arbiter.py` or `peakguard.py`. The runtime files that change are exactly the five the entry lists. `pv_w` is read only by the MQTT value and by the status rows and tile. The control hash `846a10a5b89e…` is the one recorded in `DECISIONS/TEL-11-pv-production.md:185`. - *Why a second key.* The `Invalid list for option 'pv_entity'` refusal, the deepmerge `override` detail, `match(.*)?` accepting the integer 417, and the rollback dropping the key with a warning all match §G of the decision doc. `validate.py` does not mention either key. **SAFETY-60 warning: correct, reproduced.** I built an options blob from the shipped defaults with `setpoint_entity` set and `slew_w: 5000` and ran `predeploy_check.py --deployed-version 0.11.0` in the checks image. Passed as the **bare** dict, it was `REFUSED`, exit 1. Passed **wrapped** as `{"result":"ok","data":{"options":…}}`, it printed `CLEARED for deploy.`, exit 0. The same bare blob with `slew_w: 0` cleared, exit 0. So the warning is accurate, and `jq '.data.options'` is the right extraction. There is one placement note in the next comment, and it does not block. **CI.** I traced the green to its run instead of trusting the status. Details are posted with the merge.
Author
Owner

Non-blocking: length and one placement issue. Suggested for the next release entry. This PR does not need to change for them.

1. The SAFETY-60 warning is under a heading that tells the actual reader to skip it. It is the last paragraph under ### If you are crossing from a version older than 0.10.0. The one operator waiting for this release is on 0.11.0, so that heading tells them the section does not apply to them. But the warning applies to every operator who runs the predeploy check, whatever version they come from. Why this does not block this hop: TEL-12 adds no validator rule (validate.py is not in cccb2fa..f863eca). An options blob that starts 0.11.0 therefore also passes 0.12.0's validator, so a false CLEARED cannot hide a new refusal on this upgrade. For the next cut, give it its own ### Before you press Update heading directly under the contents table, or put it in the top warning block.

2. Length. The entry is 132 lines for one optional telemetry key that is empty on every existing install. The owner has asked for shorter docs. An operator deciding whether to press Update needs about 35–45 lines:

  • Unset, nothing changes. No new validator rule, so no exit 78. Rolling back is safe. (~5 lines)
  • Instrumentation only, not a control input. One sentence plus the hash. (~3 lines)
  • What to do with several inverters: the YAML example, and the rule that it replaces pv_entity, so the inverter already in pv_entity goes in the list too and is counted once. (~12 lines)
  • An absent member makes the total unknown, never a partial sum. Keep the 4-row table. (~10 lines)
  • Why a second key: one sentence plus a pointer to DECISIONS/TEL-12… §1. (~3 lines)
  • The crossing note and the SAFETY-60 line. (~8 lines)

Candidates to cut or move to the DECISIONS doc: the match(.*)? / deepmerge argument, the template-sensor paragraph, the list of runtime files, the explanation of why replacement beats union, and "Why it matters now" (that is project context, not information for the operator).

Target for the next release entry: ≤ 50 lines per runtime feature.

**Non-blocking: length and one placement issue. Suggested for the next release entry. This PR does not need to change for them.** **1. The SAFETY-60 warning is under a heading that tells the actual reader to skip it.** It is the last paragraph under `### If you are crossing from a version older than 0.10.0`. The one operator waiting for this release is on 0.11.0, so that heading tells them the section does not apply to them. But the warning applies to **every** operator who runs the predeploy check, whatever version they come from. Why this does not block this hop: TEL-12 adds no validator rule (`validate.py` is not in `cccb2fa..f863eca`). An options blob that starts 0.11.0 therefore also passes 0.12.0's validator, so a false CLEARED cannot hide a new refusal on this upgrade. For the next cut, give it its own `### Before you press Update` heading directly under the contents table, or put it in the top warning block. **2. Length.** The entry is 132 lines for one optional telemetry key that is empty on every existing install. The owner has asked for shorter docs. An operator deciding whether to press Update needs about 35–45 lines: - Unset, nothing changes. No new validator rule, so no exit 78. Rolling back is safe. (~5 lines) - Instrumentation only, not a control input. One sentence plus the hash. (~3 lines) - What to do with several inverters: the YAML example, and the rule that it *replaces* `pv_entity`, so the inverter already in `pv_entity` goes in the list too and is counted once. (~12 lines) - An absent member makes the total `unknown`, never a partial sum. Keep the 4-row table. (~10 lines) - Why a second key: one sentence plus a pointer to `DECISIONS/TEL-12…` §1. (~3 lines) - The crossing note and the SAFETY-60 line. (~8 lines) Candidates to cut or move to the DECISIONS doc: the `match(.*)?` / deepmerge argument, the template-sensor paragraph, the list of runtime files, the explanation of why replacement beats union, and "Why it matters now" (that is project context, not information for the operator). Target for the next release entry: **≤ 50 lines per runtime feature.**
admin merged commit a45ed06c05 into release/1.0 2026-09-24 23:09:29 +02:00
Author
Owner

Merged. release/1.0 is now a45ed06, a merge commit with parents f863eca and 1ad4d3a. I confirmed it with git ls-remote.

How I traced the green. The combined status on 1ad4d3a came from run 830, job 1955. That run has event: pull_request, path: checks.yml@refs/pull/117/head, run_attempt: 1, and head_sha equal to this PR's head. Its pull_requests entry is #117, base release/1.0 @ f863eca. The job log shows PRNUM: 117 and BASEREF: release/1.0, then GRADED: refs/pull/117/head merged into release/1.0, then 31 test file(s) to run and 31 lines reading PASS, then Job succeeded (8m5s). So the run really executed; the green is not a laundered skip. The merge was pinned with head_commit_id to 1ad4d3a.

The merge is not a deploy. There is no tag, and master is untouched (78acb55). Shipping still needs a release/1.0 -> master PR and the owner's go.

**Merged. `release/1.0` is now `a45ed06`, a merge commit with parents `f863eca` and `1ad4d3a`. I confirmed it with `git ls-remote`.** How I traced the green. The combined status on `1ad4d3a` came from run **830**, job 1955. That run has `event: pull_request`, `path: checks.yml@refs/pull/117/head`, `run_attempt: 1`, and `head_sha` equal to this PR's head. Its `pull_requests` entry is #117, base `release/1.0` @ `f863eca`. The job log shows `PRNUM: 117` and `BASEREF: release/1.0`, then `GRADED: refs/pull/117/head merged into release/1.0`, then `31 test file(s) to run` and 31 lines reading `PASS`, then `Job succeeded` (8m5s). So the run really executed; the green is not a laundered skip. The merge was pinned with `head_commit_id` to `1ad4d3a`. The merge is not a deploy. There is no tag, and `master` is untouched (`78acb55`). Shipping still needs a `release/1.0` -> `master` PR and the owner's go.
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#117