btdiag
by Crsuraj1
README.md
# btdiag β universal Bluetooth audio diagnostics + battery alerts
> ### π§ Why this exists β a note from the person who *had the problem*
>
> I had a problem: my earbuds kept dying mid-song and **nothing** β not Windows,
> not the buds, not any app β would just *tell me the battery level* before it hit
> zero. Cheap earbuds, no companion app, no warning. So I built the thing I wished
> existed.
>
> I had a problem. I built a solution. (Well β *of course* Claude built the
> solution. I brought the problem, the stubbornness, and the *"no, make it work for
> **any** earbud, not just the expensive ones"* energy.)
>
> And honestly? That's the part I'm proud of. GitHub has **millions** of repos β
> most built once and never opened again, by people who never actually *use* the
> thing they made. This one I use **every single day.** Every *"π earbuds at 20%"*
> buzz on my phone is my own little tool, quietly doing its job, solving my problem
> on my terms. That small daily *"huh β it actually works"* is the whole payoff.
> That's why this exists.
>
> If it spares you one dead-battery moment too β even better. π
---
A small, open tool that **surfaces everything your OS and your earbuds expose** β
battery (with the timestamp of when it was reported), every supported profile
*in plain English*, device class, vendor, connection history, the adapter in
use, and the vendor control channels β for **any** Bluetooth audio device,
regardless of brand. Then it **alerts you before the battery dies** at
thresholds you choose.
No vendor app. No account. No cloud. Works on devices that never shipped an app
at all (budget TWS like Noise, boAt, Baseus, TOZO, Wingsβ¦).
---
## β‘ AI-native: control your earbuds with MCP
btdiag ships an [MCP](https://modelcontextprotocol.io) server. Point Claude,
Claude Code, or an **on-device/edge model** at `btdiag_mcp.py` and just *say what
you want*:
> *"is my left bud dying?"* β `bt_battery` Β· *"disconnect my earbuds"* β `bt_disconnect`
> *"find my earbuds"* β `bt_findmy` Β· *"what is my headset sending?"* β `bt_snoop_decode`
```jsonc
// Register in any MCP client (e.g. Claude Desktop config):
"btdiag": { "command": "python", "args": ["C:/path/to/btdiag_mcp.py"] }
```
9 tools: `bt_scan` Β· `bt_info` Β· `bt_battery` Β· `bt_monitor_status` Β·
`bt_connect` Β· `bt_disconnect` Β· `bt_unpair` Β· `bt_findmy` Β· `bt_snoop_decode`.
**How this differs from other Bluetooth MCP servers.** A few BLE MCP servers
exist (e.g. [bluetooth-mcp-server](https://github.com/Hypijump31/bluetooth-mcp-server),
[ble-mcp-server](https://github.com/es617/ble-mcp-server)) β they expose **raw BLE
plumbing**: scan, connect, read/write GATT characteristics. btdiag is different: it
exposes **high-level device management** β battery %, low-battery *alerts*,
connect/disconnect/**unpair on Classic BT** (not just BLE), plain-English
diagnostics, find-my proximity, and an HCI-snoop **packet explainer** β and MCP is
just one face of a **complete product** (GUI + CLI + Android app + background
monitor) sharing one core. The aim isn't "expose BLE to an LLM"; it's a universal,
brand-agnostic Bluetooth tool that humans *and* AI can drive.
See [docs/RESEARCH_AI_FINDMY_TRANSPORT.md](docs/RESEARCH_AI_FINDMY_TRANSPORT.md)
for the edge-AI architecture (on-device function-calling models β MCP β Bluetooth).
---
## Why this exists
Most budget earbuds β the majority of units sold β have **no PC app, no Linux
app, and often no/dropped mobile app**. Windows itself still has **no built-in
low-battery alert** for Bluetooth headsets. Existing open tools each target a
single premium brand:
| Project | Platform | Brands | Reach |
|---|---|---|---|
| GadgetBridge | Android | Galaxy/Huawei/Sony/Pixel | premium only |
| OpenFreebuds | Win+Linux | Huawei | single brand |
| FreeBuddy | Android | Huawei 4i | single model |
| baseus-desktop | Windows | Baseus | 1 verified model |
| Bluetooth Battery Monitor | Windows | any (battery only) | paid, closed, no diagnostics |
**btdiag is brand-agnostic by design.** Layer 1 (this release) uses standard OS
interfaces, so it works on every headset today. Layer 2 (in progress) adds
vendor-protocol modules (BES TOTA, JieLi RCSP) for deeper data β per-bud + case
battery, ANC, EQ, find-my β across whole families of cheap earbuds at once.
## Layout
```
btdiag.py CLI entry point
btdiag/ Python package (Windows + Android backends, decoders)
android-app/ standalone Android app (Kotlin) β battery alerts on phone
dist/ build outputs (Android APK; Windows .exe via PyInstaller)
scripts/ install/uninstall the silent Windows background monitor
docs/ research report
research/ every approach we tried, worked or not (research/README.md)
apk_work/ decompiler + tooling (not part of source)
```
## Windows (PC)
Requires Python 3.10+ . No third-party packages for the core (standard library only).
```
python btdiag.py scan # list all paired BT devices, one line each
python btdiag.py info [name] # full decoded report for connected device(s)
python btdiag.py raw [name] # dump every hidden PnP property (the full unfurl)
python btdiag.py monitor [name] # low-battery alert loop (80/60/40/20/10%)
python btdiag.py status | stop # check / stop the background monitor
```
`[name]` is an optional case-insensitive filter, e.g. `python btdiag.py info noise`.
### Run silently at every login (no console, no admin)
```
powershell -ExecutionPolicy Bypass -File scripts\install_autostart.ps1
```
This drops a hidden launcher in your Startup folder and starts the monitor now.
Remove it any time with `scripts\uninstall_autostart.ps1`.
## Android (phone)
1. **Standalone app** (recommended) β install `dist/btbattery-debug.apk`. It
runs as a background service, listens for the headset's `IPHONEACCEV` battery
report, shows a live status notification, and alerts at each threshold. After
install it needs **no PC and no debugging** β it is a normal app, auto-starts
on boot.
2. **From the PC over ADB** (development/diagnostics) β `python btdiag.py battery`
(one-shot) or `python btdiag.py android` (continuous). Needs the phone on USB
or wireless debugging; port discovery is automatic via mDNS.
## Example
```
============================================================
Airdopes 148 Gen 2
============================================================
Status : CONNECTED
Address : BB:D1:7A:A6:BA:BF
Address note : randomized / locally-administered address (no IEEE vendor)
Via adapter : 40:1A:58:4B:82:5F
Device class : Audio / Video > Wearable Headset
Capabilities : Rendering, Audio
Battery : [####################] 100%
Profiles / services (what the device can do):
- Advanced Audio Distribution (A2DP)
-> streams stereo music to the device
- Hands-Free (HFP)
-> call audio + microphone + battery reporting
- Serial Port (SPP)
-> raw data channel - often the vendor control/config link
First paired : 05/26/2026 16:49:07
Last connected: 06/14/2026 18:07:17
============================================================
```
That report was produced unmodified against a boAt device, though the tool was
developed against a Noise device β the same code handles both.
## Architecture
```
btdiag/
bt_constants.py Class-of-Device decoder, profile names + plain-English meanings,
MAC/OUI vendor analysis, vendor UUIDs
win_query.ps1 fast batched PnP property query (Windows backend)
windows.py assembles physical devices, decodes, reads SDP cache
android.py ADB backend: battery via IPHONEACCEV from dumpsys
report.py human-readable rendering
monitor.py multi-threshold, charging-aware battery alerts
tota.py BES TOTA Layer-2 module (read-only; reverse-engineered)
btdiag.py CLI entry point
```
The backends are isolated, so macOS/Linux backends and Layer-2 protocol modules
plug in without touching the CLI or rendering.
### Optional: full vendor names
The MAC/OUI vendor table is a small curated hint list. For complete vendor
coverage, drop the IEEE/Wireshark `oui.txt` into `btdiag/oui.txt` β it is loaded
automatically if present.
## What's readable from Bluetooth (honest scope)
1. **OS-exposed, any device:** name, MAC, vendor, device class, profiles,
battery %, connection state, pairing history. `info` decodes all of it.
2. **Vendor control, device-specific:** per-bud battery, ANC, EQ, gestures β
needs the vendor protocol (BES TOTA module).
3. **Raw live packets:** require Android HCI snoop log or a hardware sniffer β
a research activity, not a runtime feature. See `research/README.md`.
## Roadmap
- [x] Layer 1: universal OS-level diagnostics + battery alerts (Windows)
- [x] Android battery alerts (standalone APK + ADB backend)
- [ ] BES TOTA protocol module β per-bud + case battery, ANC, EQ, find-my
- [ ] JieLi RCSP protocol module
- [ ] Linux (BlueZ) and macOS backends
- [ ] System-tray UI
## History
This began as a focused reverse-engineering effort on the Noise earbuds; see
`docs/RESEARCH_REPORT.md` for the full protocol findings (GATT map, BES TOTA
auth handshake, command IDs) and `research/README.md` for every approach tried.
`research/superseded/battery_monitor.py` is the original single-file monitor that
`btdiag` replaced.
## License
MIT β see [LICENSE](LICENSE). Safe by design: read-only against the OS; vendor
modules use only documented, non-destructive commands (never firmware/OTA).
This server cannot be deployed
Maintenance
ActivityStale
ResponsivenessNo issues