3 Commits
Author SHA1 Message Date
glenn schrooyenandClaude Opus 5 c24bc0a011 Pin LF, because the deployment target is a Linux container
core.autocrlf=true gave the checkout CRLF .py and .yaml. run.sh happened to be
LF, which is the only reason a plain copy would not have produced a "bad
interpreter" failure on the add-on's entrypoint.

0.3.0 was deployed by extracting from git with autocrlf forced off and verified
byte-identical to the blobs before copying. This makes that the default rather
than something the deployer has to remember.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-25 16:01:44 +02:00
glenn schrooyenandClaude Opus 5 80402c978f DEPLOY-01: 0.3.0, so the update is installable at all
release/1.0 carried SAFETY-04 and TEL-01 but still declared version 0.2.1 -
identical to what is installed and running on the house. Home Assistant keys
add-on updates off the version string, so the update would never have been
offered.

Two files. No code, no option defaults. What reaches the house at these
defaults is SAFETY-04's control law alone, and it carries a 4,928-case
equivalence proof against the previous law at integrator_max_w: 0. TEL-01 is
inert until meter_source is turned on.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-25 15:52:47 +02:00
glenn schrooyenandClaude Opus 5 6c980e87b0 DEPLOY-01: bump version to 0.3.0, changelog for SAFETY-04 and TEL-01
Fixes the version collision noticed while planning DEPLOY-01: release/1.0
still carried version 0.2.1, identical to what is already running on the
live system, so Home Assistant would not have offered the update at all.

- config.yaml: version 0.2.1 -> 0.3.0 (minor: TEL-01 adds a feature,
  SAFETY-04 changes the control law's internals)
- CHANGELOG.md: 0.3.0 entry for SAFETY-04 and TEL-01, in the existing voice

No code under app/ touched, no option defaults changed. Verified:
meter_source: off, integrator_max_w: 0, target_grid_w: -10 all unchanged.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Du77usMj8XNKNFZGmUiWDa
2026-08-25 10:33:17 +02:00
3 changed files with 47 additions and 1 deletions
+10
View File
@@ -0,0 +1,10 @@
# This add-on is deployed to a Linux container. core.autocrlf=true on the
# authoring box gave the checkout CRLF, so a plain copy shipped CRLF files -
# run.sh with CRLF is a "bad interpreter" failure, and any hash-based drift
# check between repo and deployment fails for a reason that has nothing to do
# with the code. Deploy with:
# git -c core.autocrlf=false archive release/1.0 goodwe_controller | tar -x
# which is how 0.3.0 went out, byte-identical to the blobs.
* text=auto eol=lf
*.png binary
*.gz binary
+36
View File
@@ -1,5 +1,41 @@
# Changelog # Changelog
## 0.3.0
**SAFETY-04.** The control law's integrator is now an explicit accumulator,
bounded independently of the output clamp instead of inheriting whatever
headroom the clamp happened to leave. It also freezes while the inverter is
not tracking, rather than continuing to wind up against a command nothing is
acting on. `integrator_max_w` (default `0`) governs the bound; `0` means
"follow `max_w`", which is the existing behaviour.
Behaviour is unchanged at the defaults - a 4,928-case equivalence sweep
against the previous control law confirms it decides identically at
`integrator_max_w: 0`.
**TEL-01.** P1 meter ingestion, so a Belgian P1's two unsigned registers
(consumption, injection) no longer need a hand-written signed template
sensor: the subtraction moves into the add-on, done once and tested. Two
transports, chosen with the new `meter_source` option: `ha_dsmr` subscribes
to the DSMR integration over the HA WebSocket, `mqtt_p1` reads a topic.
Defaults to `off`, which keeps the existing `meter_entity` path untouched -
nothing changes for an install that does not opt in.
Enabling it publishes `sensor.p1_sample_age_s`: seconds since the newest
accepted telegram, recomputed against the clock and republished roughly once
a second rather than only when a telegram lands. That is deliberate - Home
Assistant only pushes a state on change, so a meter sitting at a genuinely
constant reading would otherwise look identical to a dead one. Watching the
age instead means a frozen meter shows a climbing age, not a flat line. The
firmware watchdog subscribes to this exact entity id.
Known limits, both already in DOCS.md: on `mqtt_p1`, a bridge stuck
republishing its last telegram still "arrives", so the age cannot detect
that particular failure - prefer `ha_dsmr` where both are available. And a
dead P1 meter takes 45 s to reach 0 W commanded (30 s for `meter_max_age_s`
to call the reading stale, then 15 s of `stale_input_s` on top), which is
`meter_max_age_s` and `stale_input_s` stacking, not either one alone.
## 0.2.1 ## 0.2.1
`target_grid_w` (default -10 W): what the meter should rest at. The deadband `target_grid_w` (default -10 W): what the meter should rest at. The deadband
+1 -1
View File
@@ -1,5 +1,5 @@
name: GoodWe RS485 Controller name: GoodWe RS485 Controller
version: "0.2.1" version: "0.3.0"
slug: goodwe_controller slug: goodwe_controller
description: >- description: >-
Drives a GoodWe ES/BP battery inverter over RS485 by emulating its smart Drives a GoodWe ES/BP battery inverter over RS485 by emulating its smart