# LVX6048 Monitoring & Control via Home Assistant Integration plan for monitoring (and eventually controlling) 2x LVX6048 inverters from Home Assistant via a Raspberry Pi running a Python poller that publishes to MQTT. Continue dev from the Raspberry Pi. ## Architecture ``` [LVX6048 #1] ──USB─┐ ├──► [Raspberry Pi] ──MQTT──► [Home Assistant] [LVX6048 #2] ──USB─┘ (mpp-solar/powermon (auto-discovers Python daemon, ~40 entities systemd service) per inverter) ``` ### Why USB (not RS485) The LVX6048 is an MPP Solar / Voltronic-family unit. Its **documented, supported** comm path is USB-HID speaking the **PI18** protocol (some firmware revisions also accept PI30). The RS485 port exists on the hardware but is undocumented for external monitoring on this model — it's primarily intended for parallel/BMS comms. Use USB. ### Why mpp-solar [`jblance/mpp-solar`](https://github.com/jblance/mpp-solar) is the canonical Python package for this inverter family. It ships: - PI18 / PI18LVX / PI30 / PI30MAX protocol drivers (all known query + set commands) - A `powermon` daemon for continuous polling - Built-in MQTT publisher with **Home Assistant auto-discovery** topic format — no HA YAML needed, entities appear automatically ## Hardware Checklist - [ ] Raspberry Pi (Pi 4 or Pi 5 recommended; Pi 3B+ works) running Raspberry Pi OS 64-bit - [ ] microSD card (32GB+) or USB SSD - [ ] 2x USB-A to USB-B cables (one per inverter; ~3ft typical — **keep short**, USB-HID is not spec'd for long runs) - [ ] Pi physically located near the inverter cabinet - [ ] Ethernet (preferred) or WiFi to reach the HA MQTT broker - [ ] Existing MQTT broker running on HA (confirmed operational) ## Step 1 — Base Pi Setup ```bash sudo apt update && sudo apt upgrade -y sudo apt install -y python3-pip python3-venv pipx git pipx ensurepath ``` Reboot (or `exec $SHELL`) so `pipx` PATH takes effect. ## Step 2 — Install mpp-solar ```bash pipx install mpp-solar # verify mpp-solar --version mpp-solar --listProtocols ``` Expect `PI18`, `PI18LVX`, `PI18SV`, `PI30`, `PI30MAX` in the protocol list. ## Step 3 — Plug in Inverters & Identify Them Connect inverter #1 first, alone, and run: ```bash ls -l /dev/hidraw* lsusb dmesg | tail -20 ``` Note the `/dev/hidraw*` device that appeared and the USB VID:PID (typical MPP Solar is `0665:5161` — confirm on your unit). Grab the serial number for a stable udev rule: ```bash udevadm info -a -n /dev/hidraw0 | grep -i serial | head -3 ``` Write down **SERIAL_A**. Unplug #1, plug in #2, repeat to get **SERIAL_B**. ## Step 4 — Smoke Test With inverter #1 still plugged in, query it directly: ```bash mpp-solar -p /dev/hidraw0 -P PI18 -c GS ``` `GS` is the PI18 "General Status" query. Expected output: battery voltage, SoC, PV watts, load watts, grid voltage, inverter mode, etc. If `PI18` returns gibberish or CRC errors, try `PI30`: ```bash mpp-solar -p /dev/hidraw0 -P PI30 -c QPIGS ``` Whichever protocol returns clean data → that's the one to use in the config below. **Record which protocol worked.** Also grab rated info once: ```bash mpp-solar -p /dev/hidraw0 -P PI18 -c PIRI # PI18 rated info # or mpp-solar -p /dev/hidraw0 -P PI30 -c QPIRI # PI30 rated info ``` ## Step 5 — Stable Device Names (udev) Without this, `/dev/hidraw0` and `/dev/hidraw1` can swap on reboot, and the wrong inverter's data ends up under the wrong entity in HA. > **As built on this CM5:** the LVX6048 returns no USB serial string, so serial-based matching below is aspirational. We ended up matching by USB hub port instead — see [`99-lvx6048.rules`](./99-lvx6048.rules) and `Install.md` §2. Create `/etc/udev/rules.d/99-lvx6048.rules`: ``` SUBSYSTEM=="hidraw", ATTRS{idVendor}=="0665", ATTRS{idProduct}=="5161", ATTRS{serial}=="SERIAL_A", SYMLINK+="lvx6048-1", MODE="0660", GROUP="dialout" SUBSYSTEM=="hidraw", ATTRS{idVendor}=="0665", ATTRS{idProduct}=="5161", ATTRS{serial}=="SERIAL_B", SYMLINK+="lvx6048-2", MODE="0660", GROUP="dialout" ``` Replace `SERIAL_A` / `SERIAL_B` and the VID:PID with the real values from Step 3. Then: ```bash sudo usermod -aG dialout $USER sudo udevadm control --reload-rules sudo udevadm trigger ls -l /dev/lvx6048-* ``` Both symlinks should be present. Log out / back in so the `dialout` group applies. ## Step 6 — powermon Config > **As built:** the `ports:` (plural) schema shown below does **not** work with current powermon — its config model allows exactly one `device:` per file. We ship one config file per inverter ([`powermon.yaml`](./powermon.yaml), [`powermon2.yaml`](./powermon2.yaml)) and run two systemd units. See `Install.md` §4–6. Create `~/.config/powermon/powermon.yaml` (adjust MQTT creds and protocol name to match Step 4): ```yaml device: name: lvx6048_pair id: solar_lvx6048 mqttbroker: name: port: 1883 username: password: adhoc_topic: powermon/adhoc adhoc_result_topic: powermon/adhoc/results api: enabled: false daemon: type: systemd keepalive: 60 ports: - name: lvx1 type: usb path: /dev/lvx6048-1 protocol: PI18 # confirmed in Step 4 commands: - command: GS trigger: { every: 5 } outputs: { name: mqtt_ha, type: hass, tag: lvx6048_1 } - command: PIRI trigger: { every: 300 } outputs: { name: mqtt_ha, type: hass, tag: lvx6048_1 } - command: MOD trigger: { every: 10 } outputs: { name: mqtt_ha, type: hass, tag: lvx6048_1 } - command: FWS trigger: { every: 30 } outputs: { name: mqtt_ha, type: hass, tag: lvx6048_1 } - name: lvx2 type: usb path: /dev/lvx6048-2 protocol: PI18 commands: - command: GS trigger: { every: 5 } outputs: { name: mqtt_ha, type: hass, tag: lvx6048_2 } - command: PIRI trigger: { every: 300 } outputs: { name: mqtt_ha, type: hass, tag: lvx6048_2 } - command: MOD trigger: { every: 10 } outputs: { name: mqtt_ha, type: hass, tag: lvx6048_2 } - command: FWS trigger: { every: 30 } outputs: { name: mqtt_ha, type: hass, tag: lvx6048_2 } ``` **Command reference (PI18):** - `GS` — General Status (voltages, currents, power, SoC, temps) → poll fast - `PIRI` — Protocol / Rated Info (nameplate values) → poll slow - `MOD` — Operating Mode (Grid / Battery / Fault / Standby) → poll medium - `FWS` — Fault / Warning Status → poll medium - `ET` — Total Energy (lifetime kWh) → optional, add with `every: 60` - `ED` — Daily Energy → optional Run it in the foreground once to confirm MQTT is flowing: ```bash powermon -C ~/.config/powermon/powermon.yaml ``` In Home Assistant, go to **Settings → Devices & Services → MQTT** — two new devices (`lvx6048_1`, `lvx6048_2`) should appear with all their sensors auto-discovered. ## Step 7 — systemd Service Create `/etc/systemd/system/powermon.service`: ```ini [Unit] Description=powermon LVX6048 → MQTT bridge After=network-online.target Wants=network-online.target [Service] Type=simple User=pi Group=dialout ExecStart=/home/pi/.local/bin/powermon -C /home/pi/.config/powermon/powermon.yaml Restart=on-failure RestartSec=10 # Tolerate USB dropouts / HA broker restarts StartLimitIntervalSec=300 StartLimitBurst=10 [Install] WantedBy=multi-user.target ``` Adjust `User=` / paths if not running as `pi`. Enable: ```bash sudo systemctl daemon-reload sudo systemctl enable --now powermon.service systemctl status powermon.service journalctl -u powermon.service -f ``` ## Step 8 — Home Assistant Dashboard Entities auto-appear. Minimum useful Lovelace card: ```yaml type: entities title: Solar System entities: - sensor.lvx6048_1_battery_capacity # % - sensor.lvx6048_1_battery_voltage # V - sensor.lvx6048_1_pv_input_power # W - sensor.lvx6048_1_ac_output_active_power # W - sensor.lvx6048_1_grid_voltage # V - sensor.lvx6048_1_inverter_heat_sink_temperature - sensor.lvx6048_1_working_mode - sensor.lvx6048_2_battery_capacity - sensor.lvx6048_2_pv_input_power - sensor.lvx6048_2_ac_output_active_power - sensor.lvx6048_2_working_mode ``` Entity IDs will reflect the `tag:` values from the config. Exact names depend on protocol driver — check the MQTT integration page. Recommended extras: - **Energy dashboard** — map `ET` (total lifetime energy) as a solar production source - **Automation**: alert when `working_mode` changes to `Fault` on either unit - **Utility meter** helpers on `ac_output_active_power` for daily/weekly kWh ## Control (Deferred) Once monitoring is stable, control commands are available via the same library. PI18 set commands include: - `POP` — Output source priority (SUB / SBU / etc.) - `PCP` — Charger priority (Solar first / Solar+Utility / only Solar) - `MCHGC` — Max total charging current - `MUCHGC` — Max utility charging current - `PF` — Restore defaults (careful) Exposed in HA as `button`/`select` entities via powermon's MQTT command topic. Plan this as a separate phase after at least a week of stable monitoring data. ## Troubleshooting | Symptom | Likely cause | Fix | |---|---|---| | `Permission denied` on `/dev/hidraw*` | User not in `dialout` | `sudo usermod -aG dialout $USER`, re-login | | CRC errors / garbled output | Wrong protocol selected | Try `PI30` instead of `PI18` (or vice versa) | | Device disappears after reboot | udev serial mismatch | Re-check `udevadm info -a`, verify serial string | | Symlinks swap between units | Two units report same serial | Fall back to `KERNELS=="1-1.2"` USB-port matching | | HA entities don't appear | MQTT discovery disabled or wrong prefix | Verify `discovery: true` in HA MQTT integration; default prefix is `homeassistant/` | | Entities appear but never update | powermon service not actually running | `journalctl -u powermon.service -n 100` | | One inverter works, other doesn't | Parallel-mode RS232 quirk | Query the *master* unit; the slave may not respond to USB queries while linked | ### Parallel-mode caveat The 2x LVX6048s are wired in parallel (per `README.md`). In parallel mode, some MPP Solar firmware only responds to USB/PI commands on the **master** unit — the slave echoes the master's data or returns errors. If Step 4 smoke tests show one unit timing out, check the LCD to see which is master. If only the master responds, a single poller gives you combined system data; per-unit telemetry may require the parallel/sync RJ45 cable's side-channel (undocumented) or firmware that doesn't gate slave comms. **Recommendation:** do Step 4 on each inverter individually (unplug parallel comm cable, query via USB, replug) to confirm both respond. Then test again with parallel cable connected. ## References - [jblance/mpp-solar](https://github.com/jblance/mpp-solar) — Python library - [powermon](https://github.com/jblance/powermon) — daemon mode (split out of mpp-solar) - [Set commands on LVX6048 — discussion #344](https://github.com/jblance/mpp-solar/discussions/344) - [LVX6048WP user manual](https://watts247.com/manuals/mpp/PIP-LVX6048WP/LVX6048WP-manual.pdf) - [MPP Solar LVX6048 product page](https://www.mppsolar.com/v3/lvx6048/) - [HA community — LVX6048 integration thread](https://community.home-assistant.io/t/lvx6048-inverter/851522) - [Connecting to LVX6048WP via USB with Raspberry Pi](https://diysolarforum.com/threads/connecting-to-lvx6048wp-via-usb-with-raspberry-pi.46774/)