Files
goodwe-addon/goodwe_controller/DECISIONS/SMA-2-driver-seam.md
T
glenn schrooyenandClaude c015230452
checks / unit (pull_request) Successful in 23m13s
infra-pin / fresh (pull_request) Successful in 1s
SMA-2 round 2: keep the driver keys out of the schema, translations and labels
Owner decision 2026-11-07: clients see nothing new until SMA-4. config.yaml,
translations, labels and the page layout are back to 23ff9d0's. The validation
of the keys stays for callers that skip the schema.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-11-07 13:09:31 +01:00

56 lines
3.4 KiB
Markdown

# SMA-2 - each inverter is reached through a driver
**Status: for review.** Tests: `test_sma2_driver.py`. Source: SMA-9 report
section 1.1. A pure refactor: no behaviour change for GoodWe, no change to a
command, a log line or a sentence the add-on shows.
Every place that read or steered an inverter used the GoodWe ESPHome entities
directly, so a second make meant touching all of them. `app/drivers.py` now
holds one `Driver` per roster entry (`Inverter.driver`): `configured()`,
`actuator_id()`, `limits()`, `read()`, `write()`, `release()`, `probe()`, and
`link_stale_s`, `batt_bound`, `settle_s`, `brand`, `words`. `GoodWeEsphomeDriver`
makes the same calls in the same order as the code it replaced; its
`release()` is `write(0)`.
**Moved to the driver:** the SoC and power read (`_read_unit`), the write
(`_write_unit`, still under the SAFETY-28 gate, the clamp, shadow mode and the
write budget), the device range (`run_control`, `amain`, the settings panel),
liveness and its window (`has_writer`, `is_live`, `link_note`), the freshness
bound, the status rows' "is a source set" test, rule 2's per-unit range check
and SAFETY-28's census. **Not moved:** the clamp, the gate, the heartbeat and
`command_site_zero()`; stop is still 0 W for every unit, and nothing calls
`release()` yet (SMA-10). `settle_s` and the control constants are not read
from the driver yet either: with only GoodWe they would change nothing.
**The seam is not TCP-shaped.** The census asks each entry's driver for its
actuator id: the setpoint entity for GoodWe, `host:port:unit_id` for a driver
reached over a socket, so two units behind one gateway are two actuators and
one unit named twice is the SAFETY-28 refusal. The census still counts every
`setpoint_entity` key as before and adds the driver's own actuator to it (parity
with 23ff9d0 on 349 option shapes is a test). The driver binds its Home
Assistant on first use by the Controller, so a unit added later is wired too.
**Schema, and why the new keys are not in it.** The brief asks for
`driver`, `host`, `port`, `unit_id`, `maintenance` per entry and for
`setpoint_entity` to become optional. Owner decision 2026-11-07: clients see
nothing new until SMA-4/SMA-7 use the keys. The Supervisor form shows every
schema key (and marks required ones), and a nested entry with a key the schema
lacks has that key silently dropped (measured, shape probe section K), so the
only way to hide them is to keep them out of config.yaml. So for now config.yaml, the
translations and the labels are exactly 23ff9d0's, and `setpoint_entity` stays
required there: until a driver without one is selectable, no entry can lack it.
The code underneath is the brief's: the driver defaults to `goodwe_esphome`,
rule 9(d) is per driver (a GoodWe entry without a setpoint entity is refused,
same sentence), and rule 9(h) checks `driver`, `host`, `port`, `unit_id`,
`maintenance` when a caller that skips the schema (the panel's save path,
sim/inject, the checks) supplies them; those sentences name the key, having no
label. When SMA-4 needs them: add them to the schema as optional with
translations, make `setpoint_entity` `str?`, and add them to `uilayout` as card
fields or a hidden list.
**Checked against 23ff9d0:** replay digests and a log of every Home Assistant
call are byte-identical at N=1 and N=2; the TEL-07 announce diff is 0/0/0; a
fake driver beside a GoodWe unit allocates, grades, drops and publishes per
unit; a call site that goes round the seam is caught.