Voltkeeper CLI, supporting Bluetti power station devices — scan, connect, and read data over BLE.
Project description
voltkeeper
CLI tool for Bluetti power stations — scan, connect, and read battery data over BLE.
Install
Quick run (no clone required)
uvx --from git+https://github.com/mikemccllstr/voltkeeper voltkeeper --help
This downloads and runs the tool in an isolated environment. Replace --help with any
command or subcommand.
From source
git clone https://github.com/mikemccllstr/voltkeeper
cd voltkeeper
uv run voltkeeper --help
Usage
Scan for devices
voltkeeper scan
Displays all nearby Bluetti devices and shows the exact command to connect to each one.
Options:
-t, --timeout FLOATScan timeout in seconds (default: 10.0)
Read battery status
voltkeeper status # auto-scan for devices, then pick one
voltkeeper status AA:BB:CC:DD:EE:FF # connect directly
Output:
- Battery SOC (%)
- Pack voltage
- Charging status
- Time to full / time to empty (context-dependent)
- DC load, AC load, and total load (watts)
Options:
-t, --timeout FLOATScan timeout in seconds (default: 10.0, used only when no address given)-v, --verboseDisplay all available device information (power meters, energy totals, PV strings, grid, loads, temperatures, software versions, writable controls, and device capabilities)
Probe a device (register sweep)
voltkeeper probe AA:BB:CC:DD:EE:FF -o device.yaml
Connects to the device, sweeps all known register blocks, and writes a YAML profile. Useful for device reverse-engineering and new-model support.
Validate a profile
voltkeeper validate-profile device.yaml
Parses the register blocks in a probe YAML and flags fields with suspect values (stuck-at-zero, all-0xFFFF, out of range).
Annotate changing registers (interactive)
voltkeeper annotate AA:BB:CC:DD:EE:FF -o draft.yaml
Live-polls the device and highlights byte-level changes in real time. Prompts for field names at each changed offset, saving annotations incrementally to a YAML draft. Press Ctrl-C to stop.
Write device settings
voltkeeper write AA:BB:CC:DD:EE:FF ac_output on
voltkeeper write AA:BB:CC:DD:EE:FF dc_output off
voltkeeper write AA:BB:CC:DD:EE:FF charging_mode turbo
Writable fields (see --verbose output for current values):
- switches:
ac_output,dc_output,power_off,dc_eco_mode,ac_eco_mode,power_lifting,alarm_sound—on/off charging_mode—standard/turbo/silent- numeric:
battery_range_start,battery_range_end,lcd_timeout,led_color,soc_low,soc_high,inv_voltage,inv_freq,working_mode
MQTT publish
voltkeeper mqtt-publish AA:BB:CC:DD:EE:FF --broker 192.168.1.100
Continuously polls the device over BLE and publishes state to an MQTT broker. Supports Home Assistant MQTT auto-discovery (on by default).
Options:
--serial TEXTDevice serial number (overrides BLE lookup for MQTT topic)--broker TEXTMQTT broker hostname (required)--port INTEGERMQTT broker port (default: 1883)--username TEXTMQTT broker username--password TEXTMQTT broker password--interval INTEGERSeconds between polling cycles (default: 0 = as fast as possible)--ha-config MODEHome Assistant discovery mode:normal,none,advanced(default: normal)--restart-on-source-changeExit cleanly when source code changes, so systemd restarts the process
MQTT listen — shutdown watchdog
voltkeeper mqtt-listen --serial 2409000123456 --broker 192.168.1.100
Subscribes to the device's MQTT topic and watches battery SOC. When SOC
drops below the threshold, initiates a system shutdown after a grace period.
The shutdown is latched — once triggered, it cannot be cancelled by SOC
recovery (use systemctl stop to abort before the grace period expires).
Options:
--serial TEXTDevice serial number (or provide ADDRESS for BLE lookup)--broker TEXTMQTT broker hostname (required)--port INTEGERMQTT broker port (default: 1883)--username TEXTMQTT broker username--password TEXTMQTT broker password--shutdown-at INTEGERSOC % threshold for shutdown (default: 10)--grace-period INTEGERSeconds below threshold before shutdown (default: 60)--restart-on-source-changeExit cleanly when source code changes
ADDRESS may be omitted if --serial is provided (useful when running on a
different machine without BLE).
Generate systemd service
voltkeeper load-test [OPTIONS] ADDRESS
Runs a controlled battery discharge test. Coaches you through setup, then logs device stats every N seconds to a CSV file until the battery reaches 0%.
Options:
-o, --output PATHCSV output file (default:ac2a_load_test_YYYYMMDD_HHMMSS.csv)-i, --interval SECONDSSample interval, minimum 15s (default: 60)-l, --expected-load WKnown load wattage for analysis reference-p, --phase TEXTLabel for this test phase (useful for multi-phase testing)
Example:
voltkeeper load-test AA:BB:CC:DD:EE:FF -l 500 -p "500W heater on AC"
The test will:
- Prompt you to fully charge, connect a load, and disconnect charging
- Verify prerequisites (SOC ≥ 95%, no grid/PV input)
- Poll every N seconds — logging SOC, voltage, current, AC/DC/PV/grid power,
temperatures, device time estimate, and two energy measurements:
- Computed — trapezoidal integration of measured power × time
- Register — the device's own
totalDCEnergyregister (for validation)
- Warn if grid or PV charging is detected (> 10 W)
- End when SOC reaches 0%, BLE connection drops, or you press Ctrl-C
- Print a summary with capacity, average load, min voltage, and max temp
The CSV has 17 columns with a comment header block; empty cells for failed BLE reads are Excel-friendly.
Generate systemd services
MQTT publish service
voltkeeper mqtt-publish-service AA:BB:CC:DD:EE:FF --broker 192.168.1.100
Generates a systemd unit file for mqtt-publish.
Options mirror the mqtt-publish command plus:
--user NAMESystem user to run as (default: current user)--exec PATHPath tovoltkeeperexecutable (default: auto-detect)-o, --output PATHWrite to file instead of stdout
MQTT listen service
voltkeeper mqtt-listen-service --serial 2409000123456 --broker 192.168.1.100
Generates a systemd unit file for mqtt-listen.
Options mirror the mqtt-listen command plus:
--user NAMESystem user to run as (default: root, needed for shutdown)--exec PATHPath tovoltkeeperexecutable (default: auto-detect)-o, --output PATHWrite to file instead of stdout
Installing a generated service
sudo cp voltkeeper-*.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now voltkeeper-*.service
-o, --output PATHWrite to file instead of stdout
Install the generated file:
sudo cp voltkeeper-mqtt-*.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now voltkeeper-mqtt-*.service
Help
voltkeeper --help
voltkeeper status --help
voltkeeper scan --help
voltkeeper probe --help
voltkeeper mqtt-publish --help
voltkeeper mqtt-listen --help
voltkeeper load-test --help
voltkeeper --version
Requirements
- Python 3.13+
- Linux with BlueZ, macOS 11+, or Windows 10 build 19041+ (BLE support)
- Bluetooth adapter with scan capability
On Linux, the BLE adapter may require elevated privileges (CAP_NET_ADMIN or
sudo). On macOS, the device's BLE MAC address may be reported as a UUID
rather than a hardware address — use the UUID directly with voltkeeper.
The tool reads plain Modbus RTU over BLE from Bluetti power stations. Encrypted devices (AES-CBC over BLE) are supported; the handshake is handled automatically.
Testing
uv run pytest # unit tests (fast, no BLE required)
uv run pytest -m integration # integration tests (requires BLE adapter)
Development
See docs/FINDINGS.md for reverse-engineering notes and protocol details helpful when enhancing this tool.
Contributing a new device
See docs/CONTRIBUTING_DEVICES.md for the step-by-step guide to capturing the data needed to add support for a new Bluetti device model. Maintainers receiving such a submission should follow docs/MAINTAINING.md.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file voltkeeper-2026.5.tar.gz.
File metadata
- Download URL: voltkeeper-2026.5.tar.gz
- Upload date:
- Size: 356.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b849a5b2cfcfae86e17fcd463b5f50ab0a3f0c97ce77ff7d00d337f554b07251
|
|
| MD5 |
5ebc41d8c7a1684ab0f5377a30994199
|
|
| BLAKE2b-256 |
4dcf580bfbfee5fd98635e102e2020c3529eab6f9ae60a5f93555eae6133dc43
|
Provenance
The following attestation bundles were made for voltkeeper-2026.5.tar.gz:
Publisher:
publish-to-pypi.yml on mikemccllstr/voltkeeper
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
voltkeeper-2026.5.tar.gz -
Subject digest:
b849a5b2cfcfae86e17fcd463b5f50ab0a3f0c97ce77ff7d00d337f554b07251 - Sigstore transparency entry: 1625256639
- Sigstore integration time:
-
Permalink:
mikemccllstr/voltkeeper@44b5a8f7bf5c9784f4cac20247e907d12269fbfa -
Branch / Tag:
refs/tags/v2026.05 - Owner: https://github.com/mikemccllstr
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-to-pypi.yml@44b5a8f7bf5c9784f4cac20247e907d12269fbfa -
Trigger Event:
push
-
Statement type:
File details
Details for the file voltkeeper-2026.5-py3-none-any.whl.
File metadata
- Download URL: voltkeeper-2026.5-py3-none-any.whl
- Upload date:
- Size: 69.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
aabc9201c3674e2dc08e4c877fe02bb0841c6375e094dd90d0ae4e34ae156bc0
|
|
| MD5 |
eecaa8135c411fdd7effbe2ffe8be647
|
|
| BLAKE2b-256 |
d5f65b4f083ed022c91a0fba7ff5aec86f8601e87328849963c9425a561c7c15
|
Provenance
The following attestation bundles were made for voltkeeper-2026.5-py3-none-any.whl:
Publisher:
publish-to-pypi.yml on mikemccllstr/voltkeeper
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
voltkeeper-2026.5-py3-none-any.whl -
Subject digest:
aabc9201c3674e2dc08e4c877fe02bb0841c6375e094dd90d0ae4e34ae156bc0 - Sigstore transparency entry: 1625256678
- Sigstore integration time:
-
Permalink:
mikemccllstr/voltkeeper@44b5a8f7bf5c9784f4cac20247e907d12269fbfa -
Branch / Tag:
refs/tags/v2026.05 - Owner: https://github.com/mikemccllstr
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-to-pypi.yml@44b5a8f7bf5c9784f4cac20247e907d12269fbfa -
Trigger Event:
push
-
Statement type: