Use standard project metadata, dependency groups, and the uv build backend. The new lock preserves the previous dependency versions. Documentation and services use uv, and services leave their shared environment unchanged at startup. Remove the entry point for the empty USB HID prototype. Exclude local prototypes, captures, and the external Type 7 lookup table from version control and distributions.
185 lines
6.7 KiB
Markdown
185 lines
6.7 KiB
Markdown
# bmspy
|
|
|
|
bmspy is a tool to get information from battery management systems and UPS devices. It supports:
|
|
|
|
- [Xiaoxiang-type](https://www.lithiumbatterypcb.com/product/4s-or-3s-12v-li-ion-or-lifepo4-battery-smart-bms-with-bluetooth-function-uart-and-rs485-communication-with-60a-to-120a-constant-current/?attribute_specification-selection=4S+Lifepo4+120A+with+UART+and+RS485) BMS devices via serial (USB)
|
|
- EcoFlow power stations via Bluetooth LE
|
|
|
|
It can display the information as text, in JSON, or export the data continuously to InfluxDB or a Prometheus exporter.
|
|
|
|
Multiple devices can be connected at once. Each is identified by a name, and data from all of them (or just one) can be pushed to InfluxDB or Prometheus in the same connection, with each measurement tagged with the device name.
|
|
|
|
Install [uv](https://docs.astral.sh/uv/getting-started/installation/) and use Python 3.12 or later, below Python 4.
|
|
Then create the local environment:
|
|
|
|
```bash
|
|
git clone https://git.treehouse.org.za/tim/bmspy
|
|
cd bmspy
|
|
uv sync --locked
|
|
```
|
|
|
|
To install with EcoFlow Bluetooth LE support:
|
|
|
|
```bash
|
|
uv sync --locked --extra ecoflow-bt
|
|
```
|
|
|
|
To install with InfluxDB and Prometheus support:
|
|
|
|
```bash
|
|
uv sync --locked --extra influxdb --extra prometheus
|
|
```
|
|
|
|
These optional dependency sets are called extras. Select the required extras
|
|
with each `uv run` command, or use `--all-extras` to enable all of them.
|
|
`uv sync` also installs development dependencies by default. Use `--no-dev`
|
|
for a deployment that does not need the test tools.
|
|
|
|
## Running the server
|
|
|
|
The server daemon reads from one or more devices and makes the data available over a Unix socket.
|
|
|
|
Single JBD BMS device (defaults to `/dev/ttyUSB0`):
|
|
|
|
uv run bmspy-server
|
|
|
|
Multiple JBD BMS devices, with optional names (default name is derived from the device path, e.g. `ttyUSB0`):
|
|
|
|
uv run bmspy-server -d network:/dev/ttyUSB0 -d nas:/dev/ttyUSB1
|
|
|
|
### EcoFlow Bluetooth LE devices
|
|
|
|
Install the `ecoflow-bt` extra as shown above. Close the EcoFlow app and stop other
|
|
Bluetooth collectors before connecting to a device.
|
|
|
|
#### Setup
|
|
|
|
Get your EcoFlow account user ID:
|
|
|
|
```bash
|
|
uv run bmspy-ecoflow-login you@example.com
|
|
```
|
|
|
|
The command prompts for your password. Add `--region EU` or `--region US` if needed.
|
|
Use the diagnostic scan below to find device addresses and serial numbers.
|
|
|
|
Type 7 authentication also needs an external lookup table from
|
|
[ha-ef-ble](https://github.com/rabits/ha-ef-ble). From the bmspy repository root, download it:
|
|
|
|
```bash
|
|
curl --fail --location --output bmspy/ecoflow_keydata.py \
|
|
https://raw.githubusercontent.com/rabits/ha-ef-ble/main/custom_components/ef_ble/eflib/keydata.py
|
|
```
|
|
|
|
This file is ignored by Git and excluded from packages. Type 1 devices do not need it.
|
|
|
|
#### Dump a River 2
|
|
|
|
Device arguments use `name@ADDRESS/SERIAL/USERID`. Replace the placeholders below
|
|
with the device's Bluetooth address, serial number, and your account user ID:
|
|
|
|
```bash
|
|
uv run --extra ecoflow-bt bmspy-server --socket /tmp/bmspy-rivers.sock \
|
|
-d 'river2@RIVER2_MAC/RIVER2_SERIAL/YOUR_USER_ID'
|
|
```
|
|
|
|
Leave the server running. In another terminal, print its snapshot:
|
|
|
|
```bash
|
|
uv run bmspy --socket /tmp/bmspy-rivers.sock
|
|
```
|
|
|
|
Omit `--ups` for all devices. Add `--ups river2` to select one. `--ups all` means a
|
|
device named `all`, not all devices. To include a serial BMS, add a server argument
|
|
such as `-d network:/dev/ttyUSB0`.
|
|
|
|
The server caches Bluetooth data for 60 seconds. Failed reads wait 60 seconds
|
|
before retrying. Unavailable devices are omitted from the response.
|
|
|
|
#### Diagnostics
|
|
|
|
Stop the server before connecting directly. Scan and collect reports from nearby supported devices:
|
|
|
|
```bash
|
|
uv run --extra ecoflow-bt python -u tools/ecoflow-bt.py --user-id YOUR_USER_ID --scan --timeout 30
|
|
```
|
|
|
|
Add `--watch` to print every packet until Ctrl+C. Watch mode ignores the telemetry
|
|
timeout, but stops on a lost connection. Its payloads are already decrypted.
|
|
Keep diagnostic output private because it can contain device and account identifiers.
|
|
|
|
The scan detects encryption automatically. To select one discovered device,
|
|
add `--device ADDRESS`. Without scanning, replace `--scan` with
|
|
`--device ADDRESS --serial-number SERIAL`. Direct connections default to Type 7.
|
|
Session keys stay internal. The script has no encryption override or key-export option.
|
|
|
|
River 2 collection waits for three valid core reports, not optional inverter or
|
|
solar reports. A timeout reports incomplete collection. Use `--watch` to receive
|
|
later reports.
|
|
|
|
River 2 reports charge, temperature, power, and time estimates. Missing measurements
|
|
stay absent. See the [River 2 measurement reference](docs/ecoflow-river2-fields.md)
|
|
for units, decoding rules, and known limits.
|
|
|
|
Client snapshots include all complete River 2 heartbeat fields under
|
|
`ef_river2_*`, alongside the shared measurements and raw packets.
|
|
Partial reports retain earlier field values. See the
|
|
[River 2 field reference](docs/ecoflow-river2-fields.md#reported-fields-available-to-clients)
|
|
for names, units, and limits.
|
|
|
|
### Running via systemd
|
|
|
|
Before you start a service, install its dependencies in the service's working
|
|
directory with `uv sync --locked --no-dev`. Add `--extra ecoflow-bt` for Bluetooth,
|
|
`--extra influxdb` for InfluxDB, or both if needed.
|
|
|
|
The service files use `uv run --no-sync`. This keeps services from changing
|
|
the shared environment when they start. Set the `uv` executable to an absolute
|
|
path if it is not on systemd's search path.
|
|
|
|
Copy `bmspy-server.service` to `/etc/systemd/system`, adjust `WorkingDirectory` and the `ExecStart` line as needed, then enable and start it:
|
|
|
|
cp bmspy-server.service /etc/systemd/system
|
|
$EDITOR /etc/systemd/system/bmspy-server.service
|
|
systemctl daemon-reload
|
|
systemctl enable bmspy-server
|
|
systemctl start bmspy-server
|
|
|
|
## Running a client
|
|
|
|
To print a summary of all connected devices:
|
|
|
|
uv run bmspy
|
|
|
|
To show only a specific device:
|
|
|
|
uv run bmspy --ups network
|
|
|
|
To push data for all devices to InfluxDB (each measurement is tagged `ups=<name>`):
|
|
|
|
uv run --extra influxdb bmspy-influxdb --url https://influx.example.com --org myorg --token mytoken
|
|
|
|
To push data for a single device only:
|
|
|
|
uv run --extra influxdb bmspy-influxdb --ups network --url ...
|
|
|
|
InfluxDB connection details can also be supplied via environment variables (`INFLUXDB_V2_URL`, `INFLUXDB_V2_ORG`, `INFLUXDB_V2_TOKEN`) instead of command-line flags.
|
|
|
|
## Development
|
|
|
|
Install all extras and run the tests:
|
|
|
|
```bash
|
|
uv sync --locked --all-extras
|
|
uv run --locked --all-extras pytest
|
|
```
|
|
|
|
To build a source archive and a wheel, which is an installable Python package:
|
|
|
|
```bash
|
|
uv build
|
|
```
|
|
|
|
`uv.lock` records the resolved dependency versions. After you change dependencies
|
|
in `pyproject.toml`, run `uv lock` and include the updated lock file with the change.
|