PKG-17: cut 0.11.0, so SAFETY-56's wording fix and TEL-11's pv_entity can reach an installed 0.10.1 (#114)
checks / unit (pull_request) Successful in 8m1s
checks / unit (pull_request) Successful in 8m1s
Bumps config.yaml 0.10.1 -> 0.11.0 and adds the `## 0.11.0` section, so the Supervisor offers the update that carries SAFETY-56's wording fix and TEL-11's `pv_entity`.
Reviewed at head b11bea7. Green traced to this PR's own run (827, attempt 1, payload names PR 114 base release/1.0, 31/31 real). Version gate 11a-11d, SAFETY-56's behaviour hash and TEL-11's four trace rows all re-measured on this tree and matching. slug unchanged; option surface 75 -> 76 with no validator rule added.
Two non-blocking findings recorded on the PR.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BwRLw7eKYj2byVdwB6kbjU
This commit was merged in pull request #114.
This commit is contained in:
@@ -1,5 +1,307 @@
|
||||
# Changelog
|
||||
|
||||
## 0.11.0
|
||||
|
||||
**A MINOR BUMP, AND ONE NEW OPTION IS THE WHOLE REASON FOR THE SECOND DIGIT:
|
||||
`pv_entity` ([[TEL-11]]).** The option surface moves - **75 keys -> 76**,
|
||||
`options:` and `schema:` in step - which is the same test 0.8.0, 0.9.0 and
|
||||
0.10.0 were each cut on. ⚠️ **AND UNLIKE 0.10.0, NOTHING HERE CAN REFUSE A
|
||||
STORED OPTIONS BLOB.** No key was removed, no default moved, no enum widened,
|
||||
and **no rule was added to `validate.py`** - the validator does not mention
|
||||
`pv_entity` at all, because nothing else reads it and it cannot conflict with
|
||||
anything. The new key is `str?` - **note the trailing `?`, it is OPTIONAL** -
|
||||
with an `options:` default of `""`, so a blob written on 0.10.0 or 0.10.1 does
|
||||
not carry it, the Supervisor's merge supplies the empty string, and the empty
|
||||
string is *off*. ⚠️ **A stored options blob that starts the add-on on 0.10.1
|
||||
starts it on 0.11.0. There is no `SystemExit(78)` to plan around on this hop**,
|
||||
and a site that presses *Update* and changes nothing else gets an add-on that
|
||||
behaves exactly as its 0.10.1 did.
|
||||
|
||||
⚠️ **WHAT YOU ARE ACTUALLY DECIDING, IN ONE LINE EACH. TWO TICKETS HERE ARE
|
||||
OBSERVABLE ON AN INSTALLED ADD-ON, AND NEITHER CHANGES A WATT.**
|
||||
|
||||
* [[SAFETY-56]] - **three messages stop naming your hardware as the fault** in a
|
||||
state where the add-on cannot tell a dead register from a battery that is
|
||||
simply empty at its BMS floor. **Text only; the behaviour is measured
|
||||
byte-identical.** If you have ever opened Developer Tools because the add-on
|
||||
told you a sensor had failed, and found the sensor working, this is that
|
||||
defect. On the reference install the state it misdiagnoses arrives on most
|
||||
nights and on every cloudy morning after a deep discharge.
|
||||
* [[TEL-11]] - **one new optional key, `pv_entity`, and nothing whatever happens
|
||||
until you fill it in.** Set it and the add-on records what your solar is
|
||||
producing beside what the meter and the battery are doing. ⚠️ **It is not a
|
||||
control input and it does not change what the loop does.**
|
||||
|
||||
⚠️ **THERE IS NO CONTROL-LAW CHANGE IN THIS RELEASE, WHICH IS WORTH SAYING
|
||||
BECAUSE 0.10.0 HAD ONE AND NOTHING IN ITS PREAMBLE COULD WARN YOU.**
|
||||
`app/control.py` is not in this release's diff at all, and neither are
|
||||
`allocator.py`, `arbiter.py`, `arbitrage.py`, `clamp.py`, `peakguard.py`,
|
||||
`maintenance.py` or `watchdog.py`. The runtime files that move are
|
||||
`app/freshness.py` and `app/main.py` (SAFETY-56's three sentences), and
|
||||
`app/pv.py` - new - with `app/main.py`, `app/mqtt.py`, `config.yaml` and
|
||||
`translations/` (TEL-11). Contrast [[SAFETY-48]] in `## 0.10.0`, which altered
|
||||
what the inverter is asked to do while touching no option, no default and no
|
||||
validator rule, so that nothing a config check could see would mention it. This
|
||||
release has nothing of that shape in it.
|
||||
|
||||
⚠️ **IF YOU ARE CROSSING FROM A VERSION OLDER THAN 0.10.0, THIS SECTION IS
|
||||
STILL NOT THE ONE THAT CAN REFUSE YOU - `## 0.10.0` BELOW IS.** `ha addons
|
||||
update` does not walk the versions; it installs 0.11.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 as
|
||||
this section's nothing. Two of them can refuse a config that nobody has edited:
|
||||
|
||||
> [[SAFETY-39]]'s **rule 10**, added in 0.8.0, against 0.7.0's own shipped
|
||||
> default `slew_w: 1000` - the ceiling is 2/18 of the site rail, which is
|
||||
> **555.6 W** on a 5000 W rail.
|
||||
>
|
||||
> [[TARIFF-07]]'s three withdrawn `price_provider` ids - **`engie_dynamic`,
|
||||
> `totalenergies_dynamic` and `eneco_eddy`** - each of which is **still refused
|
||||
> by name at startup on 0.11.0**, with the same message and for the same
|
||||
> measured reasons: a 404 from a gateway with no route, a TLS handshake against
|
||||
> a hostname the certificate does not cover, and a 404 with an empty body.
|
||||
|
||||
⚠️ **NEITHER OF THOSE IS NEW IN 0.11.0, AND NEITHER IS NEWS TO A SITE ALREADY ON
|
||||
0.10.0 OR 0.10.1 - IT HAS ALREADY MET THEM.** They are restated here only
|
||||
because this is now the newest section, and a reader who opens the newest section
|
||||
and stops would otherwise never meet the one class of thing that can stop the
|
||||
add-on starting. **The full account of both is in `## 0.10.0` below**: what to
|
||||
set instead of each withdrawn id, which tariff a site left on one of them has
|
||||
really been running, why the refusal *is* the migration, and why all three ids
|
||||
stay in `config.yaml`'s `price_provider` enum even though `PROVIDERS` is down to
|
||||
three. **That is the section that withdrew them, it is where the evidence is,
|
||||
and it is not superseded by this one.** ⚠️ The reference install is on
|
||||
`price_provider: off` and at `slew_w: 400`, so neither applies to it.
|
||||
`tools/predeploy_check.py` checks both against your live options blob before you
|
||||
press *Update*, and on this tree it reports a real version change rather than
|
||||
0.10.1's no-op.
|
||||
|
||||
Contents of this release, in merge order since 0.10.1 (`b0b24e5`):
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| `c9aaf7f` | SAFETY-56 - the gate cannot tell a dead register from a pack that cannot comply, so stop saying it can **⚠️ TEXT ONLY, BEHAVIOUR BYTE-IDENTICAL** |
|
||||
| `94da37f` | TEL-11 - PV production stops being invisible, and stays out of the control law **⚠️ NEW OPTIONAL OPTION, UNSET ON EVERY EXISTING INSTALL** |
|
||||
|
||||
⚠️ **TWO MORE MERGES LANDED IN THIS WINDOW AND ARE DELIBERATELY NOT IN THE TABLE
|
||||
ABOVE: [[CI-9]]'S FOLLOW-UP (`9f6b9f1`) AND [[SEC-2]] (`239e120`). THE RULE IS
|
||||
[[PKG-15]]'S - AUDIENCE, NOT IMPORTANCE - AND BOTH FAIL IT THE SAME WAY.** This
|
||||
file is the **add-on's** release note: the text the Supervisor renders beside the
|
||||
version number, read by somebody deciding whether to press *Update* on a battery
|
||||
that is currently regulating. A row here has to be something that reader can act
|
||||
on.
|
||||
|
||||
* **CI-9's follow-up changes one file, `.gitea/workflows/checks.yml`**, exactly
|
||||
as CI-9 itself did, and it is comment-only within that file. It is not under
|
||||
`goodwe_controller/`, it is not in `predeploy_check.py`'s `RUNTIME` tuple, it
|
||||
is not in the built image, and **there is no version of this add-on on which an
|
||||
operator can observe it.** `## 0.10.0` excluded CI-9 on exactly that reasoning;
|
||||
excluding the follow-up on any other reasoning would make the rule mean
|
||||
nothing.
|
||||
* **SEC-2 adds `docs/RELEASE.md` and two lines to `README.md`, and this one is
|
||||
the closer call, so here is the whole of it.** SEC-2 is by some distance the
|
||||
more consequential of the two: it is the ticket that made `master` refuse a
|
||||
direct push, and it is *why this release reaches you as a pull request into
|
||||
`release/1.0` and then into `master`, rather than as a push straight to the
|
||||
branch the Supervisor clones* - which is how 0.10.1 got there. It is also
|
||||
**purely a procedure for whoever cuts a release**; both files sit outside
|
||||
`goodwe_controller/`, neither is in the image, and no install can tell whether
|
||||
that document exists or what it says. An operator reading it learns nothing
|
||||
about 0.11.0 that helps them decide whether to take it. So it is excluded, and
|
||||
it is excluded under the same sentence as CI-9: ⚠️ **importance to this
|
||||
repository is not the test. Observability on an installed add-on is.**
|
||||
|
||||
⚠️ **AND BOTH ARE WRITTEN DOWN, JUST NOT HERE.** CI-9's follow-up is PR #102 plus
|
||||
the comment block at the top of `.gitea/workflows/checks.yml`, which carries the
|
||||
measurements in full; SEC-2 is PR #108 plus `docs/RELEASE.md` itself, which is
|
||||
now the canonical account of the release path and is the procedure this release
|
||||
followed. ⚠️ **If a later CI or packaging ticket changes what the shipped image
|
||||
contains, it belongs in this table.** Neither of these does.
|
||||
|
||||
### SAFETY-56 — three messages stop blaming healthy hardware
|
||||
|
||||
⚠️ **NOTHING THE ADD-ON DOES CHANGES. THREE THINGS IT SAYS DO.** Same
|
||||
conjunction, same limits, same `MISSING`, same 0 W command, same NOT READY row,
|
||||
same [[SAFETY-57]] release at 32 s. `tools/safety56_behaviour_trace.py` is what
|
||||
says so rather than a commit message: it hashes a document with no sentence
|
||||
anywhere in it, built from **2390 one-second reads replayed out of the committed
|
||||
2026-09-21 bus capture**, a **66-cell command × register grid stepped 120 times
|
||||
each**, and **six driven controller worlds**. That document is
|
||||
`6030175ed2ab9a41…` on `release/1.0` and on this tree, byte for byte, while the
|
||||
sentences differ. ⚠️ **And the tool REFUSES to report "behaviour unchanged" when
|
||||
the sentence halves agree**, so an identical pair is a result rather than one
|
||||
tree compared with itself.
|
||||
|
||||
**THE DEFECT, AND IT IS NOT A TYPO.** [[SAFETY-37]]'s freshness gate fires when a
|
||||
battery-power register holds one value while the standing command moves. That
|
||||
conjunction is satisfied by **two disjoint worlds**:
|
||||
|
||||
1. the register has stopped reporting reality - which is what the gate was built
|
||||
to catch; or
|
||||
2. **reality is not moving**, because the source cannot comply with the command.
|
||||
A BMS refusing a discharge at its floor holds the pack still under any
|
||||
setpoint, and a register reporting that honestly publishes the same unchanged
|
||||
number a dead one does.
|
||||
|
||||
**Nothing the gate is handed separates them, and three sentences asserted the
|
||||
first.** ⚠️ **On the one episode this repository has measured, it was the
|
||||
second.** On 2026-09-21 the pack read **11 %** for the whole 850 s hold, **every
|
||||
command inside that hold was a discharge**, and the register **answered the first
|
||||
CHARGE command three seconds later**. All three numbers are read off the
|
||||
committed capture by `test_freshness.py` part 11a rather than typed there. The
|
||||
register was healthy and truthful throughout, and the add-on said it had failed.
|
||||
|
||||
**THE THREE SURFACES, so you can recognise what moved:**
|
||||
|
||||
| where you read it | what 0.10.1 said | what 0.11.0 says |
|
||||
|---|---|---|
|
||||
| the **action log** and the **status page** — both quote `freshness.py`'s `STALE_READING:` line verbatim, through `_read_unit()` and `stale_note()` | `… the reading is not following, so it is reported as MISSING` | `… TWO THINGS LOOK LIKE THIS AND THIS GATE CANNOT TELL THEM APART: the register may have stopped reporting reality, or the source may be unable to comply with the command …` |
|
||||
| the **add-on log**, at ERROR, from `main.py` | `SAFETY-37: <unit> battery-power register is not following its own command` | `SAFETY-37: <unit> battery-power reading did not move under a command that did, WHICH IS NOT BY ITSELF A VERDICT ON THE REGISTER` |
|
||||
| the **readiness row** on the diagnostics page | `battery power: <entity> is missing or not numeric` | `battery power: no usable value this cycle from <entity> - and this row cannot tell which of two things that is … Confirm the entity in Developer Tools before replacing it` |
|
||||
|
||||
⚠️ **THE THIRD ROW IS THE ONE THAT COST AN OPERATOR AN EVENING, AND IT IS THE
|
||||
WORST THING A DIAGNOSTIC CAN DO: IT ASSERTED A FALSE FACT ABOUT A NAMED
|
||||
ENTITY.** It read *"is missing or not numeric"* for **any** `None`, and one of
|
||||
the ways that value becomes `None` is the freshness gate withholding a reading
|
||||
that is present, numeric and updating - `Freshness.reading()` returns `None`
|
||||
there **by design**. So the operator opened Developer Tools on the entity the row
|
||||
named, found it working, and had every reason to conclude the diagnostics were
|
||||
broken. **It is fixed by saying less, not by looking harder:** the row does not
|
||||
branch on the gate's state, because the gate already has a surface of its own -
|
||||
`stale_note()` rides `liveness_notes()` a few rows below with the whole
|
||||
`STALE_READING` sentence in it - so your discriminator is *"is that row there?"*,
|
||||
and this row's only job is to stop pre-empting the answer.
|
||||
|
||||
⚠️ **WHAT IT DOES NOT GIVE YOU IS WHICH OF THE TWO IT WAS.** Naming which would
|
||||
need the SoC and the pack floor, and those are deliberately not `freshness.py`'s
|
||||
inputs - they are [[TEL-08]]'s and [[SAFETY-32]]'s numbers, on their own rows.
|
||||
**SoC is STATED in these sentences and never WIRED into them**: part 11b drives
|
||||
the same two worlds at 50 % and at 11 % and the sentence does not move one byte.
|
||||
What changed is that the add-on now tells you it cannot tell, instead of guessing
|
||||
and blaming your hardware. **The reading rule is unchanged from
|
||||
`### SAFETY-57` below**: if the stale/withdrawn pair keeps repeating, the unit
|
||||
really does want looking at; if it happens once at a low SoC and stops, the pack
|
||||
was empty and the site has resumed.
|
||||
|
||||
⚠️ **AND THE THREE WORDINGS SHARE NO HELPER, ON PURPOSE.** Two of them are log
|
||||
lines about a **behaviour** and the third is a banner about an **entity**; they
|
||||
answer different questions ("what did the register do" against "can I trust this
|
||||
entity"), and one helper serving both would have to drop whichever half the other
|
||||
caller does not need - and the half it dropped would be the operator's next step.
|
||||
|
||||
**Full account: `DECISIONS/SAFETY-56-what-the-gate-cannot-tell-apart.md`.**
|
||||
|
||||
### TEL-11 — `pv_entity`: PV production gets recorded, and stays out of the control law
|
||||
|
||||
⚠️ **UNSET, THIS IS NOT A CHANGE AT ALL - AND THAT IS MEASURED RATHER THAN
|
||||
ASSERTED.** `pv_entity` ships empty, which is what **every** install that
|
||||
upgrades into this version lands on. Empty means the add-on **reads no entity,
|
||||
announces no entity, publishes no topic, shows no status row and adds no page
|
||||
row.** `tools/tel11_pv_trace.py` drives five grid behaviours × eight
|
||||
PV-sensor behaviours through 24 cycles of the **shipped** `Controller`, on
|
||||
`release/1.0` and on this tree, and hashes two separate documents - one of
|
||||
**control** decisions and one of **telemetry**:
|
||||
|
||||
| document | tree | `pv_entity` | sha256 |
|
||||
|---|---|---|---|
|
||||
| control | `release/1.0` | unset | `846a10a5b89e3210…` |
|
||||
| control | TEL-11 | unset | `846a10a5b89e3210…` |
|
||||
| control | TEL-11 | **set** | `846a10a5b89e3210…` |
|
||||
| telemetry | `release/1.0` | unset | `a443a6ceaffe072b…` |
|
||||
| telemetry | TEL-11 | unset | `a443a6ceaffe072b…` |
|
||||
| telemetry | TEL-11 | **set** | `4e6d1cb289a95722…` |
|
||||
|
||||
**Read the two `unset` rows first.** They are byte-identical in **both**
|
||||
documents, which is a stronger claim than "the control path is untouched": an
|
||||
install that does not set the option sees no change of any kind, announcements
|
||||
and status rows included. **Then read the `set` row of the control document** -
|
||||
identical again, while telemetry moves. ⚠️ The tool **REFUSES** unless the two
|
||||
trees' `app/` fingerprints differ and unless exactly one side reacts to the
|
||||
option, so a matching pair cannot be two runs of the same tree.
|
||||
|
||||
⚠️ **IT IS NOT A CONTROL INPUT, AND IT DOES NOT CHANGE WHAT THE LOOP DOES.** It
|
||||
is instrumentation and nothing else. It is not read by the control law, the
|
||||
allocator, the arbiter, the peak guard, the maintenance cycle or any watchdog; it
|
||||
is **not part of `inputs_ok`**, so a PV integration that breaks cannot stop your
|
||||
battery being regulated; and its status row is a **caution** (`warn`), never a
|
||||
`bad` row, so a telemetry channel can never put a battery site into NOT READY.
|
||||
`test_pv.py` part 6 drives 40 cycles of the real `cycle()` and asserts the
|
||||
commands are bit-identical across unset / no reading / 0 W / 8 kW, with a
|
||||
**computed** census of all 33 modules under `app/` showing the material is
|
||||
mentioned in exactly three of them: `pv.py`, `main.py`, `mqtt.py`. Wiring
|
||||
production into the control law would be a behaviour change on a system driving a
|
||||
real battery - and it would destroy the measurement, because the loop would then
|
||||
be reacting to the thing being measured.
|
||||
|
||||
**WHAT YOU GET IF YOU DO SET IT.** One new MQTT-discovery sensor,
|
||||
`sensor.goodwe_pv_power`, `device_class: power` and ⚠️ **`state_class:
|
||||
measurement`** - which is the point of the row, because that is what puts the
|
||||
number into the recorder's long-term statistics, and a week of statistics is what
|
||||
the attribution needs. **Positive is producing.** The unit is **read off your
|
||||
entity's own `unit_of_measurement`** - `mW`, `W`, `kW`, `MW`, `GW`, `TW`, Home
|
||||
Assistant's own power units, every factor an exact power of ten. A sensor with
|
||||
**no** unit, or with a unit that is not power, is **refused by name, and the
|
||||
status page says which entity and which unit.** There is deliberately no
|
||||
magnitude heuristic: nothing can separate 30 W of dusk from 30 kW of noon, and a
|
||||
silent factor of 1000 in the one channel that exists to be believed a week later
|
||||
is worse than a refusal you can read.
|
||||
|
||||
⚠️ **ABSENCE IS PUBLISHED AS `unknown`, NOT AS 0 AND NOT SKIPPED. THIS IS THE
|
||||
DESIGN DECISION WORTH YOUR ATTENTION.**
|
||||
|
||||
| what happened | `sensor.goodwe_pv_power` | the status page |
|
||||
|---|---|---|
|
||||
| `pv_entity` not set | the entity does not exist | no row at all |
|
||||
| set, sensor answered 4100 W | `4100.0` | `PV 4 100 W` |
|
||||
| set, sensor answered 0 W | `0.0` | `PV 0 W` |
|
||||
| set, sensor unavailable / missing / bad unit | `unknown` | `PV —`, plus a caution naming the reason |
|
||||
|
||||
The last two rows are why the key is shaped this way. `publish()` normally
|
||||
**drops** a `None`, which is right for `circulation` - there the last figure
|
||||
standing beats a zero that reads as health - and **exactly wrong here**: a
|
||||
dropped row leaves Home Assistant holding the last number it was told, for ever,
|
||||
and the recorder then draws a **flat line through the outage**, which is
|
||||
indistinguishable from a measurement. Anyone reading that week back would
|
||||
conclude the sun was not shining, ⚠️ **which is precisely the conclusion this
|
||||
channel exists to TEST rather than to manufacture.** So the absence is published,
|
||||
using Home Assistant's own no-value payload (`mqtt/const.py` declares
|
||||
`PAYLOAD_NONE = "None"` and `mqtt/sensor.py` tests it *before* the numeric
|
||||
branch), the entity's state becomes **`unknown`**, and the recorder stores a
|
||||
**gap**. A failed read also **clears** the previous good one: the value never
|
||||
outlives its own sensor.
|
||||
|
||||
⚠️ **WHY IT IS A HOME ASSISTANT ENTITY AND NOT A REGISTER.** The RS485 bus this
|
||||
add-on speaks carries the **battery** inverter it drives. On nearly every site the
|
||||
PV inverter is a different box from a different vendor on its own connection,
|
||||
reporting into Home Assistant through its own integration - so this takes
|
||||
whatever `sensor.*` your PV integration already provides, **any brand Home
|
||||
Assistant can read**, and nothing anywhere assumes it is on the bus.
|
||||
|
||||
**THERE IS NO `pv_invert`, AND THAT IS NOT AN OVERSIGHT.** `meter_invert` and
|
||||
`batt_invert` exist because those two quantities are **bipolar and are read by
|
||||
the control path**, where a reversed sensor hands the site a plausible number it
|
||||
acts on. Production is unipolar and is read by **nothing**, so a reversed sensor
|
||||
cannot mislead a decision, and it announces itself as **−4 kW at noon** in the
|
||||
very history this key exists to create. A flag would only let an installer hide
|
||||
it. There is no negative-value alarm either: a PV inverter's own standby draw
|
||||
makes a small negative reading at night ordinary, so any threshold would be a
|
||||
number nobody has measured.
|
||||
|
||||
⚠️ **WHAT THIS RELEASE DOES NOT DO, AND IT IS THE REASON THE CARD EXISTS.** It
|
||||
does not answer [[OPT-10]]'s question. That card measured this site over sixteen
|
||||
days: the pack reached 90 % SoC on **two** of them, and **20.5 kWh of surplus
|
||||
left the site while the pack still had room** - 12.6 kWh of it with the battery
|
||||
not charging at all - and it could not say **why**, because PV production was
|
||||
recorded **nowhere**. Three causes need three different fixes (no irradiance:
|
||||
nothing to fix; a charge-power limit; the optimiser declining to charge, which is
|
||||
the only software defect), and guessing between them is how an optimiser gets
|
||||
tuned to solve a cloud problem. **0.11.0 adds the channel. The attribution needs
|
||||
a week of history through it, and is OPT-10's own card.** So the reason to set
|
||||
`pv_entity` on the day you take this update is that the clock on that week starts
|
||||
when you do.
|
||||
|
||||
**Full account: `DECISIONS/TEL-11-pv-production.md`, with the operator's version
|
||||
under "PV production" in `DOCS.md`.**
|
||||
|
||||
## 0.10.1
|
||||
|
||||
**A PATCH RELEASE, AND ONE TICKET IS THE WHOLE OF IT: [[SAFETY-57]].** The last
|
||||
@@ -50,8 +352,13 @@ operator can observe on an installed add-on - and are excluded for the reason
|
||||
field history found are NOT in this release**: [[SAFETY-56]] (the stale-reading
|
||||
message says *"is not following its own command"* when the true cause is an
|
||||
empty pack, and the NOT READY row that goes with it) and [[SAFETY-58]] (a trip
|
||||
on the first read after a rest). Both are open, both are wording or bound
|
||||
changes rather than deadlocks, and neither was allowed to delay this one.
|
||||
on the first read after a rest). Both were open when this section was cut, both
|
||||
are wording or bound changes rather than deadlocks, and neither was allowed to
|
||||
delay this one. ⚠️ **[[SAFETY-56]] HAS SINCE BEEN FIXED AND IS IN `## 0.11.0`
|
||||
ABOVE.** The wording quoted in this paragraph is what **0.10.1 actually ships**
|
||||
and is left standing for that reason - if the box in front of you reports
|
||||
`0.10.1`, that is the sentence it will print at you. It is not what 0.11.0 says.
|
||||
[[SAFETY-58]] is still open.
|
||||
|
||||
### SAFETY-57 — the stale-reading verdict gets an exit
|
||||
|
||||
@@ -168,6 +475,17 @@ a clearance the state could not produce. ⚠️ Its **diagnosis** half still say
|
||||
pack is merely empty; that wording is [[SAFETY-56]]'s and is deliberately
|
||||
untouched here.
|
||||
|
||||
⚠️ **AND THAT IS NOW A STATEMENT ABOUT 0.10.1 ONLY — [[SAFETY-56]] REWROTE THE
|
||||
DIAGNOSIS HALF, AND `## 0.11.0` ABOVE CARRIES THE NEW WORDING.** The quotation
|
||||
above is left exactly as it stands because it is what the 0.10.1 image prints,
|
||||
and a reader on 0.10.1 needs to recognise it. The same applies to the reading
|
||||
rule in the paragraph above: on 0.11.0 the add-on no longer asserts that
|
||||
*"the register really is not following"* — it names both explanations and says
|
||||
it cannot tell them apart — so *"the pair repeats → look at the unit, it happens
|
||||
once and stops → the pack was empty"* stops being a correction of the message
|
||||
and becomes the message's own advice. Nothing about **what** the gate does moved
|
||||
in either card.
|
||||
|
||||
## 0.10.0
|
||||
|
||||
⚠️ **A CONFIG THAT IS LEGAL ON 0.9.0 IS REFUSED ON 0.10.0, AND THAT - NOT THE
|
||||
@@ -389,7 +707,10 @@ out of it that no test in this repository had reached**: [[SAFETY-56]] and
|
||||
`tools/predeploy_check.py` enforces that by itself: run against a box already
|
||||
reporting `0.10.0` it refuses with `THE DEPLOY WOULD BE A NO-OP` and names the
|
||||
runtime commits since the bump. **SAFETY-57's fix is in `## 0.10.1` above**,
|
||||
which is what a patch number is for.
|
||||
which is what a patch number is for. ⚠️ **AND SAFETY-56'S IS IN `## 0.11.0`**,
|
||||
which is where it went because it landed after 0.10.1 had already been cut. Both
|
||||
defects named in this paragraph are now fixed; this section, and `## 0.10.1`
|
||||
below it, describe images that still contain them, and are left saying so.
|
||||
|
||||
Contents of this release, in merge order since 0.9.0 (`86bdf49`):
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: GoodWe RS485 Controller
|
||||
version: "0.10.1"
|
||||
version: "0.11.0"
|
||||
slug: goodwe_controller
|
||||
description: >-
|
||||
Drives a GoodWe ES/BP battery inverter over RS485 by emulating its smart
|
||||
|
||||
Reference in New Issue
Block a user