309 lines
11 KiB
Markdown
309 lines
11 KiB
Markdown
# 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: <HA_MQTT_BROKER_IP>
|
||
port: 1883
|
||
username: <MQTT_USER>
|
||
password: <MQTT_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/)
|