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

3.4 KiB

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.