# LVX6048 → Home Assistant Reproducible install package for monitoring 2× MPP Solar LVX6048 inverters over USB-HID (PI18) via [`powermon`](https://github.com/jblance/powermon), publishing to a Home Assistant MQTT broker with HA auto-discovery. ## What's in the box ``` LVX6048/ ├── README.md ← start here ├── Install.md ← detailed walkthrough (what install.sh does) ├── Monitoring.md ← background / design notes ├── install.sh ← one-shot installer (idempotent, safe to re-run) │ ├── etc/ mirror of target system paths │ ├── udev/rules.d/99-lvx6048.rules │ └── systemd/system/ │ ├── lvx-resolve-links.service │ ├── powermon.service │ ├── powermon2.service │ ├── powermon.service.d/10-resolver.conf │ └── powermon2.service.d/10-resolver.conf │ ├── config/powermon/ lands at ~/.config/powermon/ (mode 600) │ ├── powermon.yaml ← unit #1 — edit MQTT creds before deploying │ └── powermon2.yaml ← unit #2 — edit MQTT creds before deploying │ ├── bin/ │ └── lvx-resolve-links ← installed as /usr/local/sbin/lvx-resolve-links │ (install.sh rewrites the shebang to the uv-installed python) │ ├── powermon-patches/ ← drop-in files for the uv tool install │ ├── README.md ← what each patch does + upgrade path │ ├── pi18.py, usbport.py, mqttbroker.py, │ └── port_config_model.py, ports_init.py │ ├── lvx-flash/ ← settings-profile CLI │ ├── flash.py ← dump / diff / apply / compare / sync-check │ ├── README.md │ └── profiles/current.yaml │ └── smoketest/ ← adhoc test configs (one-off powermon -C usage) ├── console.yaml └── smoketest.yaml ``` ## Quick start (reproducing on a fresh machine) ```bash # 1. Clone / scp this folder into place (e.g. /home//solar/LVX6048) cd ~/solar/LVX6048 # 2. Install uv if not already: https://docs.astral.sh/uv/ # 3. Run the installer ./install.sh # 4. Edit the two things install.sh warns about: # a. ~/.config/powermon/powermon{,2}.yaml — MQTT broker IP / user / password # b. /usr/local/sbin/lvx-resolve-links — SERIAL_UNIT_1 / SERIAL_UNIT_2 (see below) # lvx-flash/flash.py — same two constants # 5. Capture each inverter's PI18 serial (with services stopped): sudo systemctl stop lvx-resolve-links.service powermon.service powermon2.service for d in /dev/hidraw0 /dev/hidraw1; do TMP=$(mktemp --suffix=.yaml) printf 'loop: once\ndevice:\n name: probe\n port: {type: usb, path: %s, protocol: PI18}\ncommands:\n - {command: ID, trigger: {loops: 1}}\n' "$d" > "$TMP" echo "=== $d ===" ~/.local/bin/powermon -C "$TMP" 2>&1 | grep serial_number rm -f "$TMP"; sleep 2 done # Edit SERIAL_UNIT_{1,2} in /usr/local/sbin/lvx-resolve-links and lvx-flash/flash.py, # then: sudo systemctl start lvx-resolve-links.service powermon.service powermon2.service ``` ## How the pieces fit together ``` ┌─────────────────────────┐ ┌─────────────────────────┐ │ LVX6048 #1 (USB-HID) │ ──▶ │ /dev/hidraw{0|1} │ │ LVX6048 #2 (USB-HID) │ ──▶ │ (vid:pid 0665:5161) │ └─────────────────────────┘ └──────────┬──────────────┘ │ │ (99-lvx6048.rules → group=dialout) │ ▼ ┌──────────────────────────────────┐ │ lvx-resolve-links.service │ │ (oneshot, runs before powermon) │ │ │ │ probes each hidraw w/ PI18 ID │ │ creates /dev/lvx6048-{1,2} │ │ symlinks keyed to serial │ └──────────┬───────────────────────┘ │ │ After= / Requires= ▼ ┌──────────────────────────────────┐ │ powermon.service (unit #1) │ │ powermon2.service (unit #2) │ │ │ │ poll GS / MOD / PIRI / ET │ │ publish to HA auto-discovery │ │ topics under homeassistant/* │ └──────────┬───────────────────────┘ │ MQTT (port 1883) ▼ ┌──────────────────────────────────┐ │ Home Assistant Mosquitto broker │ │ ~29 auto-discovered sensors/unit │ └──────────────────────────────────┘ ``` Separate from the monitoring pipeline, **`lvx-flash/`** is a manual settings tool: dump the inverter's current config into a YAML profile, diff against a target profile, apply changes safely (stops powermon, writes via PI18 setters, verifies via PIRI readback). Also supports `compare` (diff live settings between two inverters) and `sync-check` (verify parallel-stack health). ## Cable moves Identification is PI18-serial-based, so moving USB cables between hub ports never requires config edits. After any cable shuffle: ```bash sudo systemctl restart lvx-resolve-links.service powermon.service powermon2.service ``` ## Replacing an inverter When a unit is swapped, capture its new PI18 serial (see step 5 of Quick start) and update the two `SERIAL_UNIT_*` constants in: - `/usr/local/sbin/lvx-resolve-links` - `~/solar/LVX6048/lvx-flash/flash.py` then restart the three services. ## Next steps / not done - **Firmware parity:** on this dev stack, unit #1 is at main=06303/slave=06126 and unit #2 is at 06440/06021. Parallel operation requires matching firmware (fault code 71 "Parallel version different") — the sync kit's cables are wired correctly, but the inverters won't phase-lock until both CPUs match. Firmware upload is not part of this package (MPP Solar Windows-only tool). - **PGS field layout:** the LVX6048-specific 30-field PGS response layout is only partially decoded in `powermon-patches/pi18.py`. The key fields (parallel_valid, fault_code, grid_hz, ac_output_voltage) are named; the rest are exposed as raw strings. - **Control / set commands via HA:** PI18 setters (POP, PCP, MCHGC, MUCHGC, PF) are implemented in `lvx-flash/flash.py` for offline use, but aren't exposed as HA button/select entities. Deferred until monitoring has been stable for at least a week and firmware parity is restored.