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