Files
shaggy-solar/LVX6048/2026-04-27-control.md

165 lines
8.1 KiB
Markdown
Raw Normal View History

2026-04-27 06:50:04 -04:00
# 2026-04-27 — LVX6048 remote-control plane
Built the bridge from Home Assistant down to the inverter PI18 setters, so
day-to-day knobs (output / charger priorities, charge-current caps) can be
flipped from a dashboard without touching the LCD or a shell. Three
components: powermon's built-in adhoc mechanism enabled, a Python shim that
validates and mirrors commands, and HA-side `mqtt:` / Lovelace configs.
## What happened
1. **Surveyed PI18 setters available on this firmware.** Confirmed
`POP / PCP / PSP / PEI / PDI / MCHGC / MUCHGC / MCHGV / PSDV / BUCD / PBT`
from our patch. Common PI30-only setters (`PE/PD`, `POPM`, `PF`,
`PCVV`, `PSAVE`, etc.) may also be accepted but weren't tested today.
No software AC-output toggle exists on this family — that requires a
downstream contactor.
2. **Phase 1 — enabled powermon's adhoc command queue.** Powermon already
ships an MQTT-based adhoc mechanism (`adhoc_topic` / `adhoc_result_topic`
under `mqttbroker`). Added both keys to `powermon{,2}.yaml`:
```yaml
mqttbroker:
adhoc_topic: powermon/lvx6048_{1,2}/addcommand
adhoc_result_topic: powermon/lvx6048_{1,2}/result
```
Verified end-to-end with a `PI` (protocol ID) query — both units
responded `protocol_id=18` within ~1 s.
3. **Phase 2 — validation shim (`lvx-control`).** New systemd-managed
Python daemon that subscribes to `solar/control/lvx6048/<action>`,
validates the payload against an allow-list (matching the rules
`flash.py` already enforces), encodes to PI18, and **mirrors to both
inverters' addcommand topics** so the parallel cluster never desyncs
on a setting (which would trigger fault 86 — "Parallel output setting
different"). Reuses the existing powermon venv's Python at install
time (same shebang-rewrite pattern as `lvx-resolve-links`), so no new
dependency setup. Broker creds read from `~/.config/powermon/powermon.yaml`,
so no new secret file to manage. Risky setters (battery thresholds,
type, output mode, factory reset) are deliberately **not** exposed
here — those still go through `flash.py apply`.
Supported actions:
- `output_priority` — POP (SUB / SBU)
- `charger_priority` — PCP (solar-first / solar+utility / solar-only)
- `solar_power_priority` — PSP (battery-first / load-first)
- `max_charging_current` — MCHGC (1080 A in 10 A steps)
- `max_utility_charging_current` — MUCHGC (2 / 1080 A)
4. **Phase 3 — HA-side configs.** Mirrors the `eg4battery/homeassistant/`
pattern. `homeassistant/mqtt_controls.yaml` defines 4 selects + 1
number bound to the friendly control topics; state is read from the
existing PIRI auto-discovery sensors via `value_template` mappings
(PIRI publishes "Solar - Battery - Utility" while the shim accepts
"solar_battery_utility"). `homeassistant/lovelace_controls.yaml` is
a complete dashboard view: summary header, output/charger control,
charge-current control, history graph, per-unit health glance.
`homeassistant/README.md` documents the install path and the control-
plane architecture.
5. **MOD decoder regression fix.** Powermon services were crash-looping
(~3,000 restarts on unit-2) because the inverter started reporting
mode `07` post-commissioning, which our decoder didn't know — it
raised `KeyError` instead of falling back. Added `"07": "Eco"` (best
guess; revisit if observed in non-Eco contexts) plus placeholder
labels `"Mode 08"``"Mode 15"` so any future unknown code shows
up as an opaque label rather than crashing. Defensive expansion via
dict comprehension kept the change small.
6. **Discovered firmware quirk: MCHGC / MUCHGC lock during active charge.**
While the inverter is in mode `06` ("Charge") — i.e. actively bank-
charging from solar or grid — the firmware rejects `MCHGC` / `MUCHGC`
setter writes (responds with NAK; result topic shows `"Failed"`).
`POP` / `PCP` / `PSP` / `PEI` / `PDI` work in all observed modes.
This is defensible firmware behavior (don't change current limits
while current is flowing) but not a documented one. Workarounds are
listed in `lvx-control/README.md`: retry while in Standby (mode 01),
change via LCD Programs 02/11, or use `flash.py apply` (which stops
powermon for exclusive USB access).
7. **`install.sh` updated.** Now copies `lvx-control` into
`/usr/local/bin/` with the powermon-venv shebang rewrite, installs
the systemd unit, and `enable --now`'s the service once
`~/.config/powermon/powermon.yaml` has real (non-placeholder) creds.
Idempotent on re-run.
## Net effect
| Metric | Before | After |
|---|---|---|
| Settings changes from HA | LCD or shell only | `solar/control/lvx6048/<action>` topic |
| Mirror to both inverters | manual / coordinated | automatic (single publish) |
| Validation before sending | none / firmware regex | shim allow-list + firmware regex |
| Audit trail | none | `journalctl -u lvx-control.service` |
| MOD-code crash on unknown value | yes (003,054 restarts on unit-2) | no (defensive 0815 fallback) |
## Files touched
```
M LVX6048/README.md (directory listing + status note)
M LVX6048/config/powermon/powermon.yaml (adhoc topics)
M LVX6048/config/powermon/powermon2.yaml (adhoc topics)
M LVX6048/install.sh (deploys lvx-control)
M LVX6048/powermon-patches/pi18.py (MOD 07 + 08-15 placeholders)
A LVX6048/etc/systemd/system/lvx-control.service (new systemd unit)
A LVX6048/lvx-control/lvx-control (new Python daemon)
A LVX6048/lvx-control/README.md (supported actions / topics)
A LVX6048/homeassistant/mqtt_controls.yaml (HA select + number entities)
A LVX6048/homeassistant/lovelace_controls.yaml (dashboard view)
A LVX6048/homeassistant/README.md (HA-side install steps)
A LVX6048/2026-04-27-control.md (this file)
```
## How to verify
```bash
# 1. Services healthy
systemctl --no-pager is-active \
lvx-resolve-links.service \
powermon.service powermon2.service \
lvx-control.service
# 2. Watch result topics
mosquitto_sub -h <broker> -u mqtt -P <pass> -v \
-t 'powermon/lvx6048_1/result' \
-t 'powermon/lvx6048_2/result' &
# 3. Fire a control command (no-op against current state)
mosquitto_pub -h <broker> -u mqtt -P <pass> \
-t 'solar/control/lvx6048/charger_priority' -m 'solar_first'
# Expected: both inverters publish "Succeeded" within ~1s.
```
```bash
# 4. shim audit log
journalctl -u lvx-control.service -f
# Expect lines like:
# OK charger_priority='solar_first' -> PCP0,0 -> mirror to 2 inverter(s)
# REJECT max_charging_current='999' (amps: 10,20,30,40,50,60,70,80)
```
## Loose ends / next pass
- **Phase 4 — HA automations** (TOU peak-shaving, storm-prep button,
cell-imbalance throttle). Deferred until the manual controls have been
used for a few days and the actual desired automations are clear.
- **MCHGC / MUCHGC reliability.** The current "Failed when in mode 06"
behavior is documented but ugly. Possible improvements:
(a) auto-retry the shim once after a 30 s delay; (b) require the inverter
to be in Standby before sending; (c) extend `flash.py` to push these
via the apply path (stops powermon, sets, restarts) and expose that as
an HA shell-command. Option (c) is the cleanest if frequent changes
matter.
- **Per-component dashboard for LVX6048 stack.** Today's
`lovelace_controls.yaml` is the first one. Worth a follow-up to add an
Energy-dashboard wiring guide once both inverter `ac_output_active_power`
+ battery `pack_power` template sensors stabilize.
- **MOD code 07 label.** "Eco" is an educated guess; observe over the
next few days and relabel if it shows up in unrelated states. Same as
the open follow-up for code 06 from yesterday.
- **PE / PD setters** (buzzer mute, alarm enable/disable, fault recording
toggle, etc.) — these are PI30 family and probably accepted by the
LVX6048 PI18 firmware. Worth a quick test before adding to the shim's
allow-list, since "mute the buzzer" is a useful HA button.