Files
bmspy/docs/grafana-ups.md
T
tim e32d3a8e22 Add a Grafana dashboard generator for the three UPS devices
tools/generate_grafana_ups.py writes grafana-ups.json, which shows the JBD network UPS, the River 2 desktop UPS, and the River 3 NAS UPS in three columns. Git ignores the generated file.

Each EcoFlow column shows one time-remaining panel in hours, for the direction in use. The River 2 histories add pack current and remaining capacity. The River 3 reports neither, so its histories add battery power and charge.
2026-10-10 11:56:29 +02:00

123 lines
5.5 KiB
Markdown

# Three-UPS Grafana dashboard
The repository contains the generator for the dashboard, and Git ignores the
generated file. From the repository root, write `grafana-ups.json`:
```bash
python tools/generate_grafana_ups.py
```
Import `grafana-ups.json` into Grafana. It contains the
Grafana v2 dashboard model directly. Properties such as `title`, `elements`, and
`layout` are at the JSON root, not inside the supplied export's `spec` wrapper.
It uses Flux queries and requires an InfluxDB data source configured for Flux,
not SQL or InfluxQL.
Select that source in the `InfluxDB (Flux)` control. The `Bucket` control
starts at `ups`. Enter the bucket name without quotes. Queries use
`from(bucket: "${bucket:raw}")` to prevent Grafana from adding a second pair of quotes.
The dashboard uses three columns within a 24-column, 24-row grid. This matches
the original dashboard's grid height. Grafana can rearrange panels on small screens.
The original `grafana-network-ups.json` is unchanged.
## Device names
The exporter must write these `ups` tag values:
| Tag | Display label | Device | Column |
|---|---|---|---|
| `network` | Network | JBD serial BMS | Left |
| `desktop` | Desktop | EcoFlow River 2 | Middle |
| `nas` | NAS | EcoFlow River 3 | Right |
The tags are server-assigned names, not device model selectors. Display labels do
not change the stored tags. If your server uses different names, update `DEVICES`
in the generator and regenerate the dashboard. For example:
```bash
uv run --extra ecoflow-bt bmspy-server --socket /tmp/bmspy-ups.sock \
-d network:/dev/ttyUSB0 \
-d 'desktop@RIVER2_MAC/RIVER2_SERIAL/YOUR_USER_ID' \
-d 'nas@RIVER3_MAC/RIVER3_SERIAL/YOUR_USER_ID'
```
Set `INFLUXDB_V2_URL`, `INFLUXDB_V2_ORG`, and `INFLUXDB_V2_TOKEN`.
In another terminal, export all three devices:
```bash
uv run --extra influxdb bmspy-influxdb --socket /tmp/bmspy-ups.sock --bucket ups
```
Do not add `--ups` to this exporter command. That option restricts export to one device.
## What the panels show
All three columns show battery charge and temperature history.
The Network column also shows pack voltage, signed current, capacity in amp-hours,
and individual cell voltages. Its cell voltage history includes current on the
right axis, in amps. Its temperature history includes remaining capacity on the
right axis, in amp-hours. The EcoFlow columns show total input/output power,
the time remaining, and battery power-flow flags.
The EcoFlow histories also show battery values. Each model reports different
values:
| Panel | Desktop (River 2) | NAS (River 3) |
|---|---|---|
| Input / output history | Signed pack current on the right axis, in whole amps. | Signed battery power on the watt axis. Positive means charge. |
| Temperature history | Remaining capacity on the right axis, in amp-hours. | Charge on the right axis, in percent. |
River 3 reports no battery current and no remaining capacity in amp-hours.
Current-value panels query the last five minutes, regardless of the history time
picker. An absent series remains absent, not zero.
A panel with no current series shows a no-data indication.
The server can return cached Bluetooth readings, which receive new export timestamps.
History panels follow the time picker. Network histories plot recorded samples
without averaging or adding empty intervals, as in `grafana-network-ups.json`.
Lines connect recorded samples, including samples on either side of a period
without exports. A continuous line does not prove that exports continued throughout
that period. Explicit null values still break the line. The queries do not replace
missing readings with zero.
EcoFlow histories average readings within each display interval. Missing intervals
remain gaps. Temperature sensors and battery cells remain separate series.
The dashboard refreshes every 30 seconds.
Charge values use ratios from 0 to 1. The dashboard displays them as percentages.
The colors indicate below 20%, 20% to below 50%, and 50% or higher. These are
visual thresholds, not device alarms or battery-specific safety limits.
EcoFlow input/output totals can include AC bypass. They do not establish battery
charge direction. The flow flags come from battery-side measurements. Small idle
losses can count as discharge. JBD charge-control bits describe switches rather
than power flow, so the dashboard does not compare them with the EcoFlow flags.
The time-remaining panels show the device estimate in hours, for the direction
in use. River 2 reports an estimate for that direction only, so its panel shows
`To empty` or `To full`. River 3 reports both estimates at all times, so its
panel uses `ef_remaining_time_minutes`, which holds the estimate for the
direction in the flow flags. If the battery is idle, or the device sends no
estimate, the panel shows `No estimate`. A River 3 that supplies its own small
losses from the battery counts as discharging, and shows a long time to empty.
The dashboard does not invent a runtime estimate for the JBD or apply River 2
state-code meanings to River 3.
## Regeneration
The generator uses the Python standard library and does not need the original
Grafana export. It writes `grafana-ups.json` in the repository root, or the
file that `--output` names.
The default output is the bare dashboard model for Grafana's JSON import/editor.
To include the API resource wrapper, use `--api-resource`:
```bash
python tools/generate_grafana_ups.py --api-resource --output grafana-ups-resource.json
```
This adds `apiVersion`, `kind`, and `metadata`, and places the dashboard under `spec`.
To change the layout or device names, edit the generator and run it again.