Xiaomi Body Scale
The xiaomi_body_scale sensor platform lets you track the output of the Xiaomi Smart Scale S200 and the Xiaomi Body
Composition Scale S400 using a BLE tracker hub such as the ESP32 BLE Tracker. This
component listens passively to BLE advertisement packets and does not pair with the device, so ESPHome has no impact
on battery life.
The scale broadcasts encrypted MiBeacon packets (UUID 0xFE95). A bindkey is required to decrypt the
payload; see Getting the Bindkey.
The S200 reports weight and profile ID only. The S400 also measures impedance and heart rate. A bare feet S400 measurement produces two BLE packets; with socks, only the first packet is sent:
- Packet 1 — weight + heart rate +
impedance_lowonly (50 kHz, low frequency, larger value) - Packet 2 —
impedance_high(250 kHz, high frequency, smaller value)
NOTE
Impedance naming convention follows the BIA standard used by
bodymiscale:
low/high refers to the measurement frequency, not the numerical value.
At 50 kHz, current flows only through extracellular fluid → higher resistance.
At 250 kHz, current penetrates cell membranes → lower resistance.
Supported Devices
Section titled “Supported Devices”| Device ID | Model | Name |
|---|---|---|
0x45C9 | MJTZC02YM | Smart Scale S200 |
0x4C04 | MJTZC02YM | Smart Scale S200 |
0x4DCB | MJTZC02YM | Smart Scale S200 |
0x30D9 | MJTZC01YM | Body Composition Scale S400 |
0x3BD5 | MJTZC01YM | Body Composition Scale S400 White |
0x48CF | MJTZC01YM | Body Composition Scale S400 Blue |
0x4B05 | MJTZC03YM | Body Composition Scale S400 Pro |
Configuration
Section titled “Configuration”# Example configuration entryesp32_ble_tracker: # or another BLE tracker hub
sensor: - platform: xiaomi_body_scale mac_address: "XX:XX:XX:XX:XX:XX" bindkey: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" weight: name: "Scale Weight"Configuration Variables
Section titled “Configuration Variables”- mac_address (Required, MAC Address): The MAC address of the scale.
- bindkey (Required, string): 32-character hex key to decrypt the BLE payload.
- weight (Optional): Stabilized weight in kg.
- All options from Sensor.
- impedance_low (Optional, S400 only): Low-frequency impedance at 50 kHz in Ω. Numerically larger value
(~558 Ω). Published together with weight and heart rate.
- All options from Sensor.
- impedance_high (Optional, S400 only): High-frequency impedance at 250 kHz in Ω. Numerically smaller value
(~503 Ω). Published in the second packet without weight or heart rate.
- All options from Sensor.
- heart_rate (Optional, S400 only): Heart rate in bpm.
- All options from Sensor.
- profile_id (Optional): User profile slot (1–5, matches the Xiaomi Home app user assignment).
- All options from Sensor.
- stabilized (Optional, S400 only): Binary sensor that turns
ONwhen the measurement cycle is complete (last BLE packet received). TurnsOFFduring the measurement.- All options from Binary Sensor.
Examples
Section titled “Examples”Clearing Stale Readings
Section titled “Clearing Stale Readings”A timeout filter without a value publishes NaN when no new measurement arrives, so Home Assistant shows the
sensor as unknown instead of keeping the last weigh-in. Unlike resetting to 0, this keeps fake readings out of the
history and statistics. Add the same filter to any other numeric sensor.
sensor: - platform: xiaomi_body_scale mac_address: "XX:XX:XX:XX:XX:XX" bindkey: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" weight: name: "Scale Weight" filters: - timeout: 30sBLE Payload Structure
Section titled “BLE Payload Structure”The S400 broadcasts encrypted MiBeacon packets of 24 bytes over UUID 0xFE95:
Byte(s) Content0-1 Frame control (0x48 59 = has_data + has_encryption + MiBeacon v5)2-3 Device type (e.g. 0x3BD5, little-endian)4 Frame counter (used for deduplication)5-16 Encrypted payload (12 bytes, AES-CCM, cipher_pos=5)17-19 Payload counter (used in AES-CCM nonce construction)20-23 MIC — AES-CCM authentication tag (4 bytes)After decryption, the S400 payload at offset 5 contains object ID 0x6E16 + length 0x09 + 9 data bytes:
data[0] Profile ID (uint8, 1–5)data[1-4] Compressed metrics (uint32, little-endian): bits 0-10 Weight × 10 (0.1 kg, uint11) bits 11-17 Heart rate − 50 (1 bpm, uint7) bits 18-31 Impedance × 10 (0.1 Ω, uint14)data[5-8] UNIX timestamp (uint32, requires Xiaomi Home app — not published)The S200 sends object ID 0x4E16 with the same layout, except that data[1-4] holds only the weight × 100
(0.01 kg).
Impedance packet routing (BIA frequency convention):
| Packet content | Frequency | Sensor | Typical value |
|---|---|---|---|
| with weight + heart rate | 50 kHz | impedance_low | ~558 Ω |
| without weight | 250 kHz | impedance_high | ~503 Ω |
Getting the Bindkey
Section titled “Getting the Bindkey”-
Install token_extractor
-
Run it and log in with your Xiaomi account
-
Find your scale in the generated list.
NOTE
In the Xiaomi Cloud, the S400 variants (
MJTZC01YM/MJTZC03YM) do not appear by their commercial model name. Instead, they are identified asyunmai.scales.ms103,ms104,ms107, orxiaomi.scales.ms110. This mapping is for the S400 only. -
Copy the BLE KEY field (32 hex characters); this is your
bindkey.
Body Score Calculation
Section titled “Body Score Calculation”The S400 provides raw biometric data (weight + dual-frequency impedance). To compute body composition scores (BMI, body fat %, muscle mass, ECW/ICW, BCM, skeletal muscle mass, etc.) based on weight, impedance, age, height and gender, see:
- bodymiscale — Home Assistant custom component
- lovelace-body-miscale-card — Lovelace card