Skip to content
HugoJFPublic

About

Local-first Mi Smart Scale 2 companion: Android BLE notifications, history recovery, JSON export, and Python tools.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

Mi Scale

Step on. Get a notification. Keep your data.

A small Android app and Python toolkit for the Mi Smart Scale 2 (XMTZC04HM). Read live weights, recover stored measurements, and keep everything local.

Android app · Python CLI · BLE protocol

Why this exists

Weighing should take one step. The goal is to capture a reading while your phone stays in your pocket, then put the result in a notification you can actually use. The Android app uses system-managed Bluetooth events to wake its capture code, with a short foreground session while weighing or syncing history.

  • Watch it settle. Changing weight appears in an ongoing notification; the final result becomes a regular New reading notification.
  • Catch up from memory. History sync can recover a final result missed over BLE and deduplicates readings already received.
  • Keep the readings you care about. Mark someone else's measurement Not interested, then Restore it whenever you want. Both actions work in the app and notifications.
  • Take your data with you. Export every saved reading to JSON, including original packets, timestamps, units, and the reversible deleted flag.
  • Keep it local. The Android app has no Internet permission. It uses no account, analytics, battery-exemption request, or permanent idle foreground service.

Experimental PoC. The Python tools have been tested against real XMTZC04HM hardware. Android capture flows have been exercised with a virtual BLE scale; the app is installed on a Galaxy S23 running Android 16, with physical background capture validation still pending. Live delivery timing depends on Android and the phone's Bluetooth implementation. See current limits.

From weighing to notification

flowchart LR
    scale[Mi Smart Scale 2] -->|BLE advertisement| wake[Android scan intent]
    wake --> live[Live weight notification]
    wake -->|New reading or sync due| history[Read scale memory]
    live -->|Settled result| db[(Local readings)]
    history -->|Unseen records| db
    db --> result[New reading: share or mark]
    db --> json[Export JSON]
Loading

The app registers reusable scan intents and companion-device presence observation. A new settled reading requests history sync; availability events sync when the last successful sync was at least ten minutes ago. There is no periodic polling job. The Android notes explain the triggers, batching, recovery, and process lifecycle.

Try the Android app

Requires Android 12+, Bluetooth LE, and Android's companion-device setup support. To build, install JDK 17 and Android SDK 36, and set ANDROID_HOME.

git clone https://github.com/HugoJF/mi-scale.git
cd mi-scale/android
./gradlew :app:assembleDebug
adb install -r app/build/outputs/apk/debug/app-debug.apk
  1. Open Mi Scale and tap Set up scale.
  2. Allow Nearby devices and notifications.
  3. Wake the scale and select MI SCALE2 in Android's device chooser.
  4. Wait for Ready, close the app, and try a weighing.

The initial memory snapshot establishes a silent baseline; existing scale history is marked as seen rather than imported. Subsequent unseen readings are saved. The app shows all saved readings, including those marked Not interested. Export JSON opens Android's file picker so you can choose where to save them.

The app is a native Kotlin prototype using platform UI, SQLite, BLE, and companion APIs. It does not require Google Play services or an application backend.

Try the Python tools

Requires Python 3.11+, a BLE adapter, and a running Bluetooth service. Linux uses BlueZ through Bleak. Close Zepp Life / Mi Fit while connecting to the scale.

cd mi-scale
uv venv
uv pip install -r requirements.txt

# Wake the scale and find its address.
.venv/bin/python mi_scale.py scan --duration 15

# Replace the address with the one found above.
.venv/bin/python mi_scale.py watch --address AA:BB:CC:DD:EE:FF
Command What you get
scan Scale discovery, service data, and address
watch Live and settled readings as JSON Lines; optional GATT indications
history Stored ten-byte measurements with original timestamps and raw bytes
inspect GATT services, characteristic properties, device information, and clock
set-time Local clock update, with readback verification when supported
decode Offline decoding of a captured packet

Clock updates are available in the Python CLI. The Android app leaves the scale's clock unchanged. History transfer enables recording and requests all available records without sending the separate received-history acknowledgement or erase command. CLI usage and troubleshooting cover the complete options.

Export format

Android exports a JSON object containing schema_version: 1 and a readings array. Each reading retains its ID, scale address, original packet, receipt time, scale clock, native weight/unit, kilogram conversion, BLE status flags, and deleted boolean. Marking a reading changes that boolean; export includes both states.

The scale clock is local wall time without a time zone. An unset or invalid clock stays null; receipt time is recorded separately in UTC. The app offers export only. Data details document the fields.

What has been checked

Area Evidence
Real scale Discovery, live/stable/removed broadcasts, complete history transfer, and clock write/readback on firmware V1.0.0.12
Python 25 offline tests for packets, history framing, deduplication, and clock behavior
Android logic 22 JVM tests and 10 emulator instrumentation tests
Virtual BLE radio Screen-off live/final capture, wake after process death, missed-final history recovery, sync gating, and runtime Bluetooth restart
Reading controls App and notification mark/restore, persistence, and actual file-picker JSON export/cancel
Physical Android phone Installed on Galaxy S23 / Android 16; background behavior and battery consumption await physical validation

Cold-process recovery after Bluetooth is toggled still needs a successful physical test. The app cannot receive while force-stopped, without permissions, or out of range. A virtual radio test does not establish Samsung power-policy behavior.

This project targets the weight-only XMTZC04HM, with ten-byte packets under service 0x181D. The Body Composition Scale 2 (XMTZC05HM) uses a different protocol. Check the model printed on the underside of your scale.

Develop and experiment

# Python tests run offline and require no scale.
python3 -m unittest -v

# Android unit tests and lint.
cd android
./gradlew :app:testDebugUnitTest :app:lintDebug

For Android integration tests, use a disposable emulator. The emulator guide includes the virtual scale's weigh, memory, and removed commands. The visibility logger measures host-received BLE reports from a real scale without connecting or writing to it.

Path Purpose
android/ Native Android app, unit tests, and instrumentation tests
mi_scale.py Python BLE commands and decoder
test_mi_scale.py Offline Python tests
tools/ Virtual BLE scale and visibility logger
PROTOCOL.md Packet layout, history commands, clock semantics, and research sources
ANDROID_APP.md App behavior, background strategy, building, and limitations
PYTHON_CLI.md Python command reference and troubleshooting

Build output, APKs, logs, personal captures, databases, editor settings, and test screenshots are excluded from Git. The Gradle wrapper and build configuration are included so the app can be built from source.

Help test it

Try another firmware or Android phone and open an issue with the model, software version, Android version, whether the screen was off, and whether the app process had been killed. Use synthetic or redacted examples when sharing packets; weighings and Bluetooth addresses can be personal data.

The protocol research builds on community documentation from openScale, Gadgetbridge, ESPHome, BLE Scale Sync, and ScaleLink. Exact source links and the observations they support are in PROTOCOL.md.

About

Local-first Mi Smart Scale 2 companion: Android BLE notifications, history recovery, JSON export, and Python tools.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages