collect_data called len() on each response. A port that cannot open returns False or None, so the call raised TypeError and ended the server. Treat each empty or absent response as a failed read.
River 2 reported total power and temperature as integers. River 3 reported temperature as an integer. InfluxDB keeps one field type for each measurement, so it rejected these points when another device wrote floats to the same measurement.
Correct the spelling in the JBD and EcoFlow snapshots, the diagnostic tool, and the tests. UPS.from_dict maps the old name to the new name, so a client still reads data from an older server during an upgrade.
InfluxDB and Prometheus now receive this measurement under the new name.
The upstream River 3 schema spells field 355 `ups_alram`. Use `ups_alarm` in the decoder, the generated schema, and the field reference. The schema generator now applies a rename map, so a regenerated schema keeps the corrected name.
Decode River 3 separately and accumulate partial reports against a field-level checklist. Keep watch mode running after completion.
Label fields and nested records from the pinned pr705 schema. Preserve unknown values and document unverified meanings and routing.
Add synthetic regressions, the River 3 field reference, schema-generation tooling, and upstream attribution.
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.
Serial paths and collector interfaces remain compatible. Bluetooth polling uses a 60-second cache and backoff. Offline Bluetooth devices no longer retry forever inside a client request. Invalid device specifications do not echo account IDs.
Decode River 2 charge, temperature, power flow, and time estimates. Preserve precise charge readings across packet reordering and omit unavailable time estimates.
Require valid PD, EMS, and BMS core heartbeats for diagnostic completion. Report optional packets separately and support raw watch output with reliable disconnect cleanup. Use synthetic regression tests and keep captures, credentials, and external keydata private.
Device details come from command-line arguments or discovery. This commit retains Poetry; the separate packaging commit migrates to uv.
server.py
- --device is now repeatable (-d ups1:/dev/ttyUSB0 -d ups2:/dev/ttyUSB1). Bare paths (/dev/ttyUSB0) auto-name from the last path component (ttyUSB0).
- Maintains {name: {ser, data, timestamp}} per UPS — each device has independent data freshness.
- GET response is now {ups_name: JBDUPS}. Accepts optional ups key in the request to return only one.
client.py
- read_data() gains ups=None parameter — pass a name to filter server-side, or omit for all.
- Always returns {ups_name: JBDUPS}.
influxdb.py
- influxdb_create_snapshot() iterates {name: JBDUPS} and tags every InfluxDB point with ups=name.
- influxdb_export() / bmspy-influxdb gain --ups to export only a specific UPS.
__init__.py
- bmspy CLI gains --ups to display only a named UPS.
- Displays each UPS under a === name === header.
... as well as turning debug and ser into global variables (for ease of cleanup). Influx, unlike Prometheus, requires typed values, so a raw_value field has been added to the data structure: value is a string, raw_value keeps its type.