Packaged as a Home Assistant add-on, with a field guide

Turns the reference RS485 controller into something a technician can install
at a client site: a typed config form instead of YAML, an ingress UI that
names misconfiguration in words, and persistent state that cannot be broken by
a timezone.

Why an add-on rather than YAML packages or blueprints:

- Blueprints cannot create helpers, and the maintenance cycle is a state
  machine whose phase and completion date must survive restarts.
- YAML packages need filesystem access, a configuration.yaml edit and a
  restart - none of which belong in a client install.
- Add-ons authenticate with SUPERVISOR_TOKEN, so there is no long-lived token
  to generate, store or leak on someone else's machine.
- Requires HA OS/Supervised. Container and Core installs cannot run add-ons at
  all, which is a market decision, not an oversight.

The control law and the maintenance machine are pure functions with no Home
Assistant imports, and both ship with runnable checks (22 and 22 assertions).
Every assertion corresponds to a rule whose absence caused an observed failure
on hardware - the saturation duration term, the clamp-before-slew ordering, the
deadband, the sign convention.

One behaviour deliberately differs from the implementation it replaces: when
its inputs go missing this commands 0 W rather than replaying the last
setpoint. The reference version kept replaying, which the hardware watchdog
cannot catch - from the ESP32's side, Home Assistant is still talking to it.

Includes the ESPHome firmware (now parameterised: node name, inverter rating,
watchdog timeout) and the optional RS485 e-stop. FIELD-GUIDE.md carries the
commissioning gates, all judged on the wire rather than on how Home Assistant
looks, plus the written statement a site without an e-stop needs signed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016NckgXecasQb2eSsPYNSW6
This commit is contained in:
2026-08-23 01:15:25 +02:00
co-authored by Claude Opus 5
commit 0a1e61dbc9
24 changed files with 2737 additions and 0 deletions
+73
View File
@@ -0,0 +1,73 @@
# GoodWe RS485 Controller — Home Assistant add-on repository
Replaces a GoodWe battery inverter's vendor controller with a local one: holds
net grid exchange at zero by emulating the inverter's smart meter over RS485,
and runs the monthly battery maintenance cycle the vendor box was doing.
```
repository.yaml add-on repository metadata
goodwe_controller/ the add-on
config.yaml manifest + options schema (the per-site config form)
app/ the controller
control.py the control law — pure functions, no I/O
maintenance.py the monthly cycle state machine
hass.py Supervisor/Core API access
store.py persistent state in /data
mqtt.py optional status entities
web.py ingress UI
main.py orchestration, heartbeat, failsafe behaviour
test_control.py runnable checks — no framework needed
test_maintenance.py runnable checks — walks a full cycle in fake time
DOCS.md the add-on's documentation tab
firmware/goodwe-master.yaml ESPHome config for the T-CAN485
estop/rs485_log.py optional RS485 e-stop / bus witness
FIELD-GUIDE.md installation, commissioning gates, troubleshooting
```
## Install
Settings → Add-ons → Add-on store → ⋮ → **Repositories** → add this repository's
URL, then install **GoodWe RS485 Controller**.
Requires **Home Assistant OS or Supervised**. Add-ons cannot be installed on HA
Container or Core.
## Read this first
**The inverter holds its last command forever.** It has no meter-timeout of its
own — a controller that dies mid-command leaves the battery running until a
human intervenes. Measured on real hardware: 5 kW of discharge held for 113
seconds after a controller went silent.
Three layers exist because of that, and only the third covers the Home Assistant
machine itself dying:
1. **ESP32 watchdog** — no fresh setpoint for ~30 s → command 0 W, and keep
commanding it.
2. **Wind-down before firmware updates** — 0 W written before the update starts.
3. **RS485 e-stop (optional)** — writes 0 W after 30 s of total bus silence.
Sites sold without the e-stop must have the acknowledgement in `FIELD-GUIDE.md`
§10 signed.
## Development
The control law and the maintenance machine are pure Python with no Home
Assistant imports, so they run anywhere:
```bash
cd goodwe_controller
python3 test_control.py
python3 test_maintenance.py
```
Both must pass before shipping any change. They are not unit-test theatre —
each assertion corresponds to a rule whose absence produced an observed failure
on real hardware, and the comments in `control.py` say which.
## Compatibility
- GoodWe **ES / BP family** inverters (AA55 / RS485 meter-bus generation)
- The protocol is **reverse-engineered**. There is no vendor contract, and a
firmware change on GoodWe's side could break every installation at once. Say
so when you sell it.