165 lines
8.1 KiB
Markdown
165 lines
8.1 KiB
Markdown
|
|
# 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 (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/<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 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 <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.
|