Reverse-engineering the Xiaomi Mi Smart Band 5 (model XMSH10HM) BLE protocol — a clean,
documented, cloud-free Python toolkit to talk to the band directly from your own computer.
No Xiaomi/Zepp app required at runtime. Read heart rate, steps, battery and activity history, make it vibrate, and change its settings — straight over Bluetooth LE.
Hobby / educational reverse-engineering project. Works with your own band. Not affiliated with or endorsed by Xiaomi/Huami/Zepp.
Tested against: model XMSH10HM · firmware V1.0.2.66 · hardware V0.44.17.2 ·
protocol family classic Huami (MiBand5Support → MiBand4Support → HuamiSupport).
Other firmware versions likely differ — auth, raw-sensor and event behavior are firmware-specific
(see PROTOCOL.md). If you run a different firmware, please report what changed.
| Capability | Status |
|---|---|
| Auth (AES-128-ECB challenge-response) | ✅ verified |
| Heart rate (live stream) | ✅ |
| Realtime steps + distance + calories | ✅ |
| Battery level / charge state | ✅ |
| Activity history (per-minute steps/HR/intensity) | ✅ |
| Vibration / find-device | ✅ |
| Settings/config (24h, units, lift-wrist, DND, …) | ✅ mechanism, 📖 full table |
| Raw PPG optical waveform | ❌ not exposed on this firmware |
| Unsolicited device events (button, etc.) | ❌ silent on app-less unit |
Full byte-level spec, including the negative results, is in PROTOCOL.md.
- Python 3.10+
- A Mi Smart Band 5 you own
- macOS / Linux / Windows with BLE (developed on macOS;
bleakis cross-platform) - The band's auth key (see Getting the auth key)
python3 -m venv .venv
.venv/bin/pip install bleak pycryptodome# 1. Find your band and its address
.venv/bin/python scan.py
# 2. Tell the tools which device is yours (address from scan.py)
# macOS shows a CoreBluetooth UUID; Linux/Windows show a MAC.
export BAND_ADDRESS="<address-from-scan>" # or: echo "<addr>" > .band_address
# 3. Put your 32-hex-char auth key in auth_key.txt
echo "<your32hexkey>" > auth_key.txt
# 4. Go
.venv/bin/python live.py # live HR + steps + battery
.venv/bin/python fetch_history.py 1 # pull stored activity -> CSVOr use the library:
import asyncio
from miband5 import MiBand5
async def main():
async with await MiBand5.open() as band: # scan + connect + auth
print(await band.battery())
await band.vibrate(1.5)
n = 0
async for bpm in band.heart_rate():
print(bpm, "bpm")
n += 1
if n >= 10: break
asyncio.run(main())Mi Band 5 uses server-based pairing — unlike Band 2/3/4 you cannot register your own key
locally (the band returns 10 01 81 and never bonds). The key is negotiated with Xiaomi/Huami
servers during the first bind via the official app and stored on the band. You bind once, then
extract that key and reuse it forever (it stays valid until a factory reset).
Two routes (pick one):
The official Zepp Life app fetches your device list from the server, and that response contains
additionalInfo.auth_key. Intercept it with mitmproxy:
pip install mitmproxy, thenmitmdump -s mitm_capture.pyon your computer.- Set your phone's Wi-Fi HTTP proxy to your computer's IP, port 8080.
- Install + trust the mitmproxy CA cert on the phone (visit
http://mitm.it).- iOS: also enable it under Settings → General → About → Certificate Trust Settings.
- Open Zepp Life, view your band.
mitm_capture.pyprints theauth_keyfor each device. SetBAND_MAC=<your-mac>first to auto-writeauth_key.txt. - Remove the proxy + cert from the phone afterwards.
Zepp Life is not certificate-pinned, so this works without a jailbreak/root.
argrento/huami-token logs into your Xiaomi/Zepp
account and downloads keys for bound devices. Note: it defaults to US servers — for EU/other
regions you must point it at the right host (e.g. api-user-de.huami.com, region eu-central-1)
or login 401s. Still requires one app bind first.
Either way: drop the 32 hex chars into auth_key.txt.
| File | What it does |
|---|---|
miband5.py |
The library — async client (auth, HR, steps, battery, vibrate, config, history) |
scan.py |
Find the band, print its address |
gatt.py |
Dump the full GATT table with Huami labels |
band.py |
Shared resolver (scan-retry) + UUID/protocol constants |
live.py |
Live HR + steps + battery monitor |
fetch_history.py |
Pull stored per-minute activity → activity_history.csv |
probe.py, events_probe.py, explore.py |
Verification / discovery harnesses |
mitm_capture.py |
mitmproxy addon to extract the auth key |
PROTOCOL.md |
The reverse-engineered protocol spec |
- The band advertises in short windows —
band.resolve()retries the scan; keep the screen lit. - One BLE owner at a time — unbind/disconnect the band from any phone app first.
- macOS stuck-handle bug after failed connects:
blueutil -p 0 && blueutil -p 1clears it. - bleak mislabels Huami 16-bit UUIDs as SDP names ("RFCOMM" etc.) — ignore; use the
CHARSmap inband.py. - Activity history is incremental — the band only stores data since the last sync, and a successful fetch advances the sync pointer (data won't repeat). With no app installed, this tool becomes the only sync client and accumulates everything going forward.
- Never commit
auth_key.txt— it's your band's secret key (.gitignored here). .band_address, captured tokens, logs and CSVs are also git-ignored.- The auth key grants full BLE control of your band; treat it like a password.
- Gadgetbridge (GPLv3) — the authoritative Huami protocol reference used to label and verify commands. Not redistributed here; clone it separately if you want the source.
argrento/huami-token— auth-key retrieval.- bleak — cross-platform BLE.
MIT — see LICENSE.