Files
shaggy-solar/LVX6048/Monitoring.md

309 lines
11 KiB
Markdown
Raw Normal View History

2026-04-24 16:34:10 -04:00
# 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` §46.
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/)