CFG-04 (#45) merged first and moved the base. Two conflicts, both resolved as the union of the two sides: - app/main.py: an import-block collision. CFG-04 added validate_unit_ranges to the .validate import; SAFETY-30 added AGAINST_METER, AGAINST_OWN_POWER, SIGN_OPTION_DEFAULTS and SignWatchdog to the .watchdog import. Neither side dropped anything, so the resolution keeps both lists whole. - CHANGELOG.md: two adjacent new entries under ## Unreleased. Both kept. Both sides also independently rewrote large parts of test_peakguard.py (60 and 47 insertions against the merge base) and git merged them textually, so the full suite was run on the MERGE RESULT rather than inferred from the two parents: 19 passed, 0 failed, twice. One caveat recorded rather than hidden: test_peakguard.py failed once during the first sequential full-suite run and then passed 10/10 in isolation and in two further full runs on this same tree. It passes on both parents too, so this is the pre-existing flakiness CFG-04 already disclosed, not a merge defect - it is timing- or load-sensitive. It sits on the required CI context and can therefore block an unrelated merge at random, which wants a card of its own. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BwRLw7eKYj2byVdwB6kbjU
This commit is contained in:
@@ -55,6 +55,82 @@ Use the ESP32's readings rather than the inverter's cloud or dongle sensors:
|
||||
those serve cached values, and a stale reading here ends the maintenance charge
|
||||
phase having charged nothing.
|
||||
|
||||
### More than one inverter (`inverters`)
|
||||
|
||||
The six keys above describe **one** inverter, and if that is your site there is
|
||||
nothing to do here — `inverters` ships empty and empty means *"the flat keys
|
||||
above describe the one inverter"*. **An install that upgrades into 0.6.0 reads
|
||||
identically to before: same entities, same rating, same behaviour, no operator
|
||||
action.**
|
||||
|
||||
To run two, declare them. Each entry names its own actuator, its own rating and
|
||||
its own sensors:
|
||||
|
||||
```yaml
|
||||
setpoint_entity: "" # ⚠️ must be cleared — see below
|
||||
inverters:
|
||||
- name: inverter-a
|
||||
setpoint_entity: number.goodwe_a_setpoint_w
|
||||
soc_entity: sensor.goodwe_a_soc
|
||||
batt_entity: sensor.goodwe_a_battery_power
|
||||
max_w: 5000
|
||||
- name: inverter-b
|
||||
setpoint_entity: number.goodwe_b_setpoint_w
|
||||
soc_entity: sensor.goodwe_b_soc
|
||||
batt_entity: sensor.goodwe_b_battery_power
|
||||
max_w: 3000
|
||||
```
|
||||
|
||||
| per-entry key | required | meaning |
|
||||
|---|---|---|
|
||||
| `name` | yes | Short identifier. It namespaces this unit's SAFETY-02 latch, heartbeat and write bookkeeping in the add-on's state file, so **two units may not share one** |
|
||||
| `setpoint_entity` | yes | This inverter's own ESPHome `number.*_goodwe_setpoint_w` |
|
||||
| `max_w` | yes | The supervised cap on what may be written to **this** inverter. The site's ceiling is the **sum** |
|
||||
| `soc_entity` | | This pack's state of charge |
|
||||
| `batt_entity` | | This unit's battery power, `+` = discharging |
|
||||
| `batt_invert` | | Flip if needed |
|
||||
|
||||
⚠️ **Nothing is inherited.** An entry that leaves out `soc_entity` has **no SoC
|
||||
sensor** — it does *not* fall back to the flat one. That is deliberate: every
|
||||
per-device value became per-instance in 0.5.x precisely so that no inverter is
|
||||
ever graded against a sibling's numbers, and a silent fallback would rebuild
|
||||
that in YAML. `name`, `setpoint_entity` and `max_w` are required of every entry
|
||||
for the same reason: there is no rating to borrow.
|
||||
|
||||
⚠️ **Clear the flat `setpoint_entity` when you declare a roster,** or the add-on
|
||||
refuses to start and says so. It is the single-inverter form and it is not read
|
||||
once a roster exists, so leaving it set means one of two things: it names an
|
||||
entity an entry also names — two writers on one actuator, which specs §1.5 makes
|
||||
a non-goal and the add-on refuses outright — or it names a different one, which
|
||||
nothing would ever write, leaving a site running quietly smaller than its
|
||||
configuration reads. `soc_entity`, `batt_entity`, `batt_invert` and `max_w`
|
||||
cannot be blanked (the schema gives them no empty value); they are simply not
|
||||
read, and the add-on writes one line into its action log at every start saying
|
||||
so.
|
||||
|
||||
⚠️ **Two inverters naming the same `setpoint_entity` are refused,** whichever
|
||||
keys they are declared under and whatever the capitalisation. Two independent
|
||||
setpoints on one inverter is the arbitration problem this add-on exists not to
|
||||
have.
|
||||
|
||||
⚠️ **Update the add-on first, then add the roster.** Home Assistant validates the
|
||||
options you save against the schema of the version that is *installed*, so
|
||||
`inverters` cannot be set before 0.6.0 is installed. The order is: update, then
|
||||
configure.
|
||||
|
||||
⚠️ **Going the other way — downgrading with a roster configured — is recoverable
|
||||
but NOT uneventful, and the add-on will not start until you refill a field.**
|
||||
The *schema* side is fine: the older version's schema has no `inverters` key, so
|
||||
it drops the key with a warning rather than refusing the whole blob (measured —
|
||||
`DECISIONS/CFG-04-inverter-roster.md` §E). The *add-on* side is not. Rule 9(e)
|
||||
requires the flat `setpoint_entity` be **cleared** when a roster is declared, so
|
||||
a config that downgrades cleanly is by construction a config whose flat
|
||||
actuator is blank — and the older version has nothing else to write to. It stops
|
||||
at startup validation with a refusal naming `setpoint_entity`, which is loud and
|
||||
in the log rather than silent, but it is an outage until someone puts the
|
||||
single-inverter entity back in that field. **Before downgrading, write down the
|
||||
`setpoint_entity` of the unit you want to keep running.**
|
||||
|
||||
### P1 meter ingestion
|
||||
|
||||
`meter_entity` above expects one signed sensor, which usually means a template
|
||||
@@ -272,7 +348,7 @@ behind it would trip the firmware watchdog on a system that is working fine.
|
||||
|
||||
| option | default | meaning |
|
||||
|---|---|---|
|
||||
| `max_w` | 2000 | Hard limit on what may be commanded. Start low, raise after commissioning |
|
||||
| `max_w` | 2000 | Hard limit on what may be commanded. Start low, raise after commissioning. **Not read when `inverters` is declared** — each entry carries its own, and the site ceiling is their sum |
|
||||
| `gain` | 0.6 | Correction per cycle. **At the limit — do not raise** |
|
||||
| `slew_w` | 1000 | Maximum change per cycle |
|
||||
| `deadband_w` | 15 | Ignore errors smaller than this |
|
||||
|
||||
Reference in New Issue
Block a user