Files
shaggy-solar/LVX6048/Monitoring.md
2026-04-24 16:34:10 -04:00

309 lines
11 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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/)