Metadata-Version: 2.4
Name: voltkeeper
Version: 2026.5
Summary: Voltkeeper CLI, supporting Bluetti power station devices — scan, connect, and read data over BLE.
Project-URL: Repository, https://github.com/mikemccllstr/voltkeeper
Project-URL: Issues, https://github.com/mikemccllstr/voltkeeper/issues
Author-email: Michael McCallister <mike@mccllstr.com>
License-Expression: MIT
License-File: LICENSE
Keywords: battery,ble,bluetooth,bluetti,cli,power-station,solar,voltkeeper
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Intended Audience :: End Users/Desktop
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: MacOS
Classifier: Operating System :: Microsoft :: Windows
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Home Automation
Classifier: Topic :: System :: Monitoring
Classifier: Topic :: Utilities
Requires-Python: >=3.10
Requires-Dist: aiomqtt>=2.0
Requires-Dist: bleak>=3.0.2
Requires-Dist: click>=8.0
Requires-Dist: cryptography>=47.0.0
Requires-Dist: paho-mqtt>=2.0
Requires-Dist: pyyaml>=6.0
Requires-Dist: watchdog>=6.0
Description-Content-Type: text/markdown

# voltkeeper

CLI tool for Bluetti power stations — scan, connect, and read battery data over BLE.

## Install

### Quick run (no clone required)

```bash
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

```bash
git clone https://github.com/mikemccllstr/voltkeeper
cd voltkeeper
uv run voltkeeper --help
```

## Usage

### Scan for devices

```bash
voltkeeper scan
```

Displays all nearby Bluetti devices and shows the exact command to connect to each one.

Options:
- `-t, --timeout FLOAT`  Scan timeout in seconds (default: 10.0)

### Read battery status

```bash
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 FLOAT`  Scan timeout in seconds (default: 10.0, used only when no address given)
- `-v, --verbose`         Display 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)

```bash
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

```bash
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)

```bash
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

```bash
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

```bash
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 TEXT`           Device serial number (overrides BLE lookup for MQTT topic)
- `--broker TEXT`           MQTT broker hostname (required)
- `--port INTEGER`          MQTT broker port (default: 1883)
- `--username TEXT`         MQTT broker username
- `--password TEXT`         MQTT broker password
- `--interval INTEGER`      Seconds between polling cycles (default: 0 = as fast as possible)
- `--ha-config MODE`        Home Assistant discovery mode: `normal`, `none`, `advanced` (default: normal)
- `--restart-on-source-change`  Exit cleanly when source code changes, so systemd restarts the process

### MQTT listen — shutdown watchdog

```bash
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 TEXT`           Device serial number (or provide ADDRESS for BLE lookup)
- `--broker TEXT`           MQTT broker hostname (required)
- `--port INTEGER`          MQTT broker port (default: 1883)
- `--username TEXT`         MQTT broker username
- `--password TEXT`         MQTT broker password
- `--shutdown-at INTEGER`   SOC % threshold for shutdown (default: 10)
- `--grace-period INTEGER`  Seconds below threshold before shutdown (default: 60)
- `--restart-on-source-change`  Exit 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

```bash
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 PATH`        CSV output file (default: `ac2a_load_test_YYYYMMDD_HHMMSS.csv`)
- `-i, --interval SECONDS`   Sample interval, minimum 15s (default: 60)
- `-l, --expected-load W`    Known load wattage for analysis reference
- `-p, --phase TEXT`         Label for this test phase (useful for multi-phase testing)

Example:
```bash
voltkeeper load-test AA:BB:CC:DD:EE:FF -l 500 -p "500W heater on AC"
```

The test will:
1. Prompt you to fully charge, connect a load, and disconnect charging
2. Verify prerequisites (SOC ≥ 95%, no grid/PV input)
3. 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 `totalDCEnergy` register (for validation)
4. Warn if grid or PV charging is detected (> 10 W)
5. End when SOC reaches 0%, BLE connection drops, or you press Ctrl-C
6. 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

```bash
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 NAME`    System user to run as (default: current user)
- `--exec PATH`    Path to `voltkeeper` executable (default: auto-detect)
- `-o, --output PATH`  Write to file instead of stdout

#### MQTT listen service

```bash
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 NAME`    System user to run as (default: root, needed for shutdown)
- `--exec PATH`    Path to `voltkeeper` executable (default: auto-detect)
- `-o, --output PATH`  Write to file instead of stdout

#### Installing a generated service

```bash
sudo cp voltkeeper-*.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now voltkeeper-*.service
```
- `-o, --output PATH`  Write to file instead of stdout

Install the generated file:
```bash
sudo cp voltkeeper-mqtt-*.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now voltkeeper-mqtt-*.service
```

### Help

```bash
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

```bash
uv run pytest                 # unit tests (fast, no BLE required)
uv run pytest -m integration  # integration tests (requires BLE adapter)
```

## Development

See [docs/FINDINGS.md](docs/FINDINGS.md) for reverse-engineering notes
and protocol details helpful when enhancing this tool.

## Contributing a new device

See [docs/CONTRIBUTING_DEVICES.md](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](docs/MAINTAINING.md).
