Imported from mnm-matin/miscale (
AGENTS.md). Install upstream withnpx skills add mnm-matin/miscale. Copyright stays with the author.
Mi Scale S400 Project
This repository is a small toolkit for capturing measurements from a Xiaomi Body Composition Scale S400.
Architecture
- Python BLE exporter: listens for encrypted Xiaomi MiBeacon advertisements and writes JSON/NDJSON measurements.
- Python monitor: debug tool for checking whether the scale is advertising and whether frames decrypt.
- Body composition module: computes Xiaomi-like estimated metrics from weight, impedance, sex, age, and height.
- Optional server/dashboard: FastAPI + SQLite API and a static dashboard for private measurement history.
- Android app: Kotlin BLE scanner, local Room storage, body composition calculations, and optional server sync.
Key Files
| File | Purpose |
|---|---|
s400_export.py |
Main BLE scanner and measurement exporter |
s400_monitor.py |
Debug monitor for MiBeacon frames |
s400_body_composition.py |
Body composition calculations |
s400_sync.py |
Optional server upload client |
server/main.py |
FastAPI routes |
server/database.py |
SQLite models and helpers |
android/ |
Kotlin Android app |
BLE Protocol
The scale uses Xiaomi MiBeacon service 0xFE95 with AES-CCM encryption.
- Encryption: AES-CCM with a 16-byte bindkey and a 4-byte tag.
- Nonce:
MAC_reversed + frame[2:5] + frame[-7:-4]. - Object ID:
0x6E16for S400 measurements.
S400 Object
The S400 object is 9 bytes:
Byte 0: Profile ID
Bytes 1-4: Packed data, little-endian
Bits 0-10: Mass, weight_kg * 10
Bits 11-17: Heart rate raw, bpm - 50
Bits 18-31: Impedance, ohm * 10
Bytes 5-8: Device timestamp
Private Configuration
Keep secrets and personal measurements out of Git. The root .gitignore should protect these paths:
/private/s400_secrets.py:SCALE_MAC,BINDKEY_HEX/private/s400_profile.py:USER_SEX,USER_AGE_YEARS,USER_HEIGHT_CM/private/s400_server.json: optionalserver_urlandapi_key/private/s400_measurements.ndjson,/private/s400_latest.json,/private/s400_frames.log/TASK.mdand/PLAN.md: private task notes/data/: local SQLite database
Do not copy credentials, real bindkeys, device tokens, real MAC addresses, or personal measurement history into tracked docs, tests, CI, Android resources, or examples.
Commands
Use uv for Python work.
# BLE scanner
uv run python s400_export.py
# Monitor advertisements
uv run python s400_monitor.py --verbose --timeout 15
# Tests
uv run --with pytest pytest tests/
# Server, local development
SECRET_KEY=replace-with-a-random-secret uv run --extra server uvicorn server.main:app --reload
# Android debug APK
cd android && ./gradlew assembleDebug
Notes
- The optional server is meant for a private dashboard. Require a real
SECRET_KEYand review CORS/deployment settings before exposing it beyond localhost. - Android stores bindkeys, API keys, and health metrics locally. Keep generated APKs, build outputs,
local.properties, keystores, and cache directories out of Git.