Files
shaggy-solar/LVX6048/2026-04-27-control.md
2026-04-27 06:50:04 -04:00

8.1 KiB
Raw Permalink Blame History

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:

    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

# 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.
# 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.