# 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/`, 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 (10–80 A in 10 A steps) - `max_utility_charging_current` — MUCHGC (2 / 10–80 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/` 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 08–15 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 -u mqtt -P -v \ -t 'powermon/lvx6048_1/result' \ -t 'powermon/lvx6048_2/result' & # 3. Fire a control command (no-op against current state) mosquitto_pub -h -u mqtt -P \ -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.