# 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.