Files
berger-ble-commands/README.md
T

131 lines
3.7 KiB
Markdown

# Berger LFP BLE Commands
Reverse-engineering notes for the Berger LFP Android app BLE protocol.
This repository documents the custom JBD-style BLE GATT protocol used by the
Berger LFP Android app to read battery telemetry such as SOC, current, voltage,
remaining capacity, cell voltages, temperatures, MOS state, and estimated
charge/discharge time.
## Target App
- App label: Berger LFP
- Package: `com.jbd.berger`
- Version analyzed: `1.0.3` / `8`
- XAPK source: APKPure direct download for Play package `com.jbd.berger`
- XAPK SHA-256: `962eb7d0c62a34e03bfbe034632fd227e0f352cd66fac9ac3d77166cec0abb5e`
- Base APK SHA-256: `aa2325cfe3777824657f0ade0c381cf4bc296f9630179e0d33ed4eb56d78c1bb`
- Signer CN: `Unknown`
- Signer cert SHA-256: `ec11c6d3260adecb382186a61207e61657834b3b025e3ca31bc6ef4d88f720f9`
The XAPK was unpacked and the base APK was decompiled with apktool. The app is
a DCloud/uni-app package, so the useful BLE logic is in
`assets/apps/__UNI__F61076D/www/app-service.js`.
## Main Finding
Berger LFP does not use the standard BLE Battery Service. It connects as a BLE
GATT client, scans for a custom service `ff00`, subscribes to notifications on
`ff01`, and writes JBD command frames to `ff02`.
Android pairing/bonding does not appear to be required by the app flow. The
normal path is adapter init, scan, connect, service discovery, notification
enable, and command writes.
## Quick Start
Build the main live-data read frame:
```sh
python3 tools/berger_frame.py read 0x03
```
Expected output:
```text
DDA50300FFFD77
```
Send that frame to characteristic `0000ff02-0000-1000-8000-00805f9b34fb`
after subscribing to notifications on
`0000ff01-0000-1000-8000-00805f9b34fb`.
## Local Desktop App
This repo includes a small Python GUI client that can scan for Berger/JBD-style
BLE devices, connect, poll telemetry, and display decoded values locally.
### Debian
```sh
sudo apt update
sudo apt install python3 python3-venv python3-pip python3-tk bluez
python3 -m venv .venv
. .venv/bin/activate
python3 -m pip install -r requirements.txt
python3 berger_gui.py
```
If scanning finds no devices, check that Bluetooth is powered and unblocked:
```sh
rfkill list bluetooth
bluetoothctl power on
bluetoothctl scan on
```
### Windows 11
```powershell
py -3 -m venv .venv
.\.venv\Scripts\Activate.ps1
python -m pip install --upgrade pip
python -m pip install -r requirements.txt
python berger_gui.py
```
The GUI uses native WinRT Bluetooth through `bleak` on Windows.
## App Workflow
The app's telemetry flow is:
1. Open the Bluetooth adapter.
2. Start BLE discovery filtered to service `0000ff00-0000-1000-8000-00805f9b34fb`.
3. Connect to the selected peripheral.
4. Discover services and characteristics.
5. Subscribe to notifications on `ff01`.
6. Write JBD read frames to `ff02`.
7. Buffer notify chunks, split complete `DD ... 77` frames, validate checksum,
and parse the response payload into UI state.
## Minimal BLE Workflow
1. Connect to the device over BLE GATT.
2. Discover services.
3. Subscribe to notifications on `ff01` under service `ff00`.
4. Write the base-data read frame to `ff02`:
```text
DDA50300FFFD77
```
5. Write the cell-voltage read frame to `ff02`:
```text
DDA50400FFFC77
```
6. Parse notifications from `ff01`.
## Caveats
- This catalog is code-derived from the Android app bundle, not yet confirmed
against a live HCI capture.
- The desktop app currently implements read-only telemetry. The Android app has
charge/discharge MOS and factory/name/capacity write paths; those are
documented, but deliberately not exposed as GUI buttons yet.
- Only one phone/client can normally hold the battery GATT connection at a time.
Close the Android app before connecting from a PC.