158 lines
5.3 KiB
Markdown
158 lines
5.3 KiB
Markdown
# EG4 LifePower4 v2 → HA Monitoring Install
|
||
|
||
Target: Debian-family Linux (developed on Raspberry Pi CM5), one USB-to-RS-485
|
||
adapter per pack's **RS485** socket, HA MQTT broker on the LAN.
|
||
|
||
> **Shortcut:** [`install.sh`](./install.sh) automates §3–§7 and supports
|
||
> `--dry-run` for a no-hardware smoke test. This doc explains what it does
|
||
> and how to do it by hand.
|
||
|
||
Path conventions: `$BASE` = root of this package (e.g. `~/solar/eg4battery`).
|
||
|
||
## 1. Prerequisites
|
||
|
||
- `uv` on `$PATH` ([docs](https://docs.astral.sh/uv/))
|
||
- `sudo`
|
||
- **One USB-to-RS-485 adapter per pack**. FTDI-based is what we've tested
|
||
(FT232R + MAX485 combo, identified by the Linux kernel as `FT232R USB UART`
|
||
with a unique serial-number suffix). CH340 / CP210x also fine — adjust
|
||
the udev rule's vendor/product ID.
|
||
|
||
## 2. Cabling — read this before wiring
|
||
|
||
LP4V2 back panel has four RJ45 sockets: `CAN`, `RS485`, `Comm1`, `Comm2`.
|
||
Only `RS485` is relevant for our daemon:
|
||
|
||
| Socket | Use for monitoring? |
|
||
|--------|---------------------------------------------------------------------|
|
||
| CAN | No — separate bus, CAN signaling, for inverter BMS comms |
|
||
| RS485 | **Yes.** External monitor port. Pin 1-2 = B/A. Modbus RTU at 9600. |
|
||
| Comm1 | No — inter-pack hub bus (19200 Modbus). Leave for pack daisy-chain. |
|
||
| Comm2 | No — same internal bus as Comm1. |
|
||
|
||
The stock EG4 USB-RS-485 cable (included with each pack) is already wired
|
||
correctly for the RS485 socket (pins 1-2 / A-B).
|
||
|
||
**Topology**: each pack gets its own adapter plugged into its **RS485** socket.
|
||
No daisy chain required for monitoring — each pack is a dedicated bus. The
|
||
Comm1↔Comm2 daisy chain between packs is separate and carries the inter-pack
|
||
hub bus (not our concern).
|
||
|
||
## 3. udev rule
|
||
|
||
Grants `dialout` group access to FTDI USB-serial adapters.
|
||
|
||
```bash
|
||
sudo install -m 644 "$BASE/etc/udev/rules.d/99-eg4-rs485.rules" \
|
||
/etc/udev/rules.d/99-eg4-rs485.rules
|
||
sudo udevadm control --reload-rules
|
||
sudo udevadm trigger --subsystem-match=tty
|
||
ls -l /dev/serial/by-id/ # expect one symlink per adapter
|
||
```
|
||
|
||
## 4. Daemon binary
|
||
|
||
```bash
|
||
sudo install -m 755 "$BASE/bin/eg4-battery" /usr/local/bin/eg4-battery
|
||
```
|
||
|
||
uv handles deps; no venv work on your side.
|
||
|
||
## 5. Config
|
||
|
||
Canonical template: [`config/eg4-battery.yaml.example`](./config/eg4-battery.yaml.example).
|
||
|
||
```bash
|
||
mkdir -p ~/.config/eg4-battery
|
||
install -m 600 "$BASE/config/eg4-battery.yaml.example" \
|
||
~/.config/eg4-battery/eg4-battery.yaml
|
||
# Edit — see "mode selection" below
|
||
```
|
||
|
||
### 5a. mode selection
|
||
|
||
- **`modbus_per_pack`** (default / recommended). Each pack listed with its own
|
||
`port:`, `address:` and `baud:` — the daemon opens one serial port per pack
|
||
and polls each independently.
|
||
```yaml
|
||
bus:
|
||
mode: modbus_per_pack
|
||
timeout_s: 1.0
|
||
poll_interval_s: 10.0
|
||
packs:
|
||
- name: lifepower4_1
|
||
address: 0x40
|
||
port: /dev/serial/by-id/usb-FTDI_FT232R_USB_UART_<ID1>-if00-port0
|
||
baud: 9600
|
||
- name: lifepower4_2
|
||
address: 0x40
|
||
port: /dev/serial/by-id/usb-FTDI_FT232R_USB_UART_<ID2>-if00-port0
|
||
baud: 9600
|
||
```
|
||
|
||
- **`active`** (legacy, V1 hardware only) — single shared bus, EG4 7E/0D
|
||
protocol at 9600. Not used on V2 Auto-Addressing hardware.
|
||
|
||
- **`passive`** (diagnostic) — listen-only Modbus sniff at 19200. See
|
||
[`NOTES.md`](./NOTES.md) "Modes" for details.
|
||
|
||
### 5b. MQTT creds
|
||
|
||
Replace `<MQTT_BROKER_IP>`, `<MQTT_USER>`, `<MQTT_PASSWORD>`. `install.sh`
|
||
will not auto-start the service while those placeholders remain.
|
||
|
||
## 6. Smoke test without hardware
|
||
|
||
```bash
|
||
eg4-battery -C ~/.config/eg4-battery/eg4-battery.yaml --dry-run
|
||
```
|
||
|
||
Mock transport, one cycle per pack, prints every discovery-config and
|
||
state-topic / payload to stdout. Confirms the pipeline end-to-end before
|
||
hardware is involved.
|
||
|
||
## 7. systemd
|
||
|
||
```bash
|
||
sudo install -m 644 "$BASE/etc/systemd/system/eg4-battery.service" \
|
||
/etc/systemd/system/
|
||
sudo systemctl daemon-reload
|
||
sudo systemctl enable --now eg4-battery.service
|
||
journalctl -u eg4-battery.service -f
|
||
```
|
||
|
||
Service includes `Environment=PATH=…` so uv is found under systemd.
|
||
|
||
## 8. Bring up additional packs
|
||
|
||
When a new pack comes online:
|
||
|
||
1. Plug its adapter into the pack's **RS485** socket and power the pack.
|
||
2. `ls -l /dev/serial/by-id/` — note the new symlink.
|
||
3. Add / update an entry in `~/.config/eg4-battery/eg4-battery.yaml`:
|
||
```yaml
|
||
- name: lifepower4_N
|
||
address: 0x40
|
||
port: /dev/serial/by-id/usb-FTDI_FT232R_USB_UART_<ID>-if00-port0
|
||
baud: 9600
|
||
```
|
||
4. `sudo systemctl restart eg4-battery.service`. The journal shows
|
||
`pack lifepower4_N: recovered after N failed cycle(s)` within ~10 s when
|
||
the pack starts responding, and HA auto-discovers ~65 entities.
|
||
|
||
## 9. Verify MQTT flow
|
||
|
||
```bash
|
||
# modbus_per_pack: all named + raw register entities per pack
|
||
mosquitto_sub -h <broker> -u mqtt -P <pass> \
|
||
-t 'homeassistant/sensor/lifepower4_+_pack_voltage/state' \
|
||
-t 'homeassistant/sensor/lifepower4_+_soc/state' \
|
||
-t 'homeassistant/sensor/lifepower4_+_cell_voltage_delta_mv/state' \
|
||
-v
|
||
```
|
||
|
||
## 10. Swapping modes later
|
||
|
||
Change `bus.mode` in the config, restart the service. Config reshape varies
|
||
per mode — see §5a. No binary redeploy needed.
|