XiaomiGateway3
XiaomiGateway3 copied to clipboard
Support for Mi Body Composition Scale s400
Cant figure out how to create a converter for Xiaomi Scale s400 BLE
here the device info: extra: cloud_fw: 2.1.1_0057 cloud_name: Body Composition Scale did: blt.3.1i0oijp3sk400 mac: 84:46:93:20:07:0a market_brand: Xiaomi market_model: 12505, yunmai.scales.ms103 market_name: Body Scale s400 rssi_54ef4437c787: -62 seq: 24 type: ble last_seen: 54ef4437c787: 1m6s listeners: 5 model: 12505 params: {} ttl: 1h5m uid: 84469320070a
Here is debug logs with it (blt.3.1i0oijp3sk400) debug.log
I have the s400 pro,but I don't know how to debug = =
the s400 scale measures three metrics: impedance, heart rate and weight and combines these values into a single value value when reporting to gateway.
The formula is
(impedance * 10) << 18 + (heart_rate - 50) << 11 + (weight * 10)
To decode it properly, you need to add a custom converter:
from dataclasses import dataclass
from custom_components.xiaomi_gateway3.core.devices import *
from homeassistant.components.sensor import (
SensorDeviceClass,
SensorStateClass,
)
@dataclass
class S400RawDataConv(BaseConv):
def decode(self, device: "XDevice", payload: dict, value: int):
if value > 1:
weight = value & (2**11 - 1)
heart_rate = (value >> 11) & (2**7 - 1)
impedance = value >> 18
payload["raw_data"] = value
if weight != 0:
payload["weight"] = weight / 10
if heart_rate > 0 and heart_rate < 127:
payload["heart_rate"] = heart_rate + 50
if impedance != 0:
if weight != 0:
payload["impedance_high"] = impedance / 10
else:
payload["impedance_low"] = impedance / 10
DEVICES = [
{
18639: [
"Xiaomi",
"Body Composition Scale S400",
"12505",
"yunmai.scales.ms103",
],
"spec": [
BaseConv(
"weight",
"sensor",
entity={
"name": "Weight",
"icon": "mdi:human",
"class": SensorDeviceClass.WEIGHT,
"statistics": SensorStateClass.MEASUREMENT,
"units": "kg",
},
),
BaseConv(
"heart_rate",
"sensor",
entity={
"name": "Heart Rate",
"icon": "mdi:heart-pulse",
"statistics": SensorStateClass.MEASUREMENT,
"units": "bpm",
},
),
BaseConv(
"impedance_low",
"sensor",
entity={
"name": "Impedance (low)",
"icon": "mdi:omega",
"statistics": SensorStateClass.MEASUREMENT,
"units": "ohm",
},
),
BaseConv(
"impedance_high",
"sensor",
entity={
"name": "Impedance (high)",
"icon": "mdi:omega",
"statistics": SensorStateClass.MEASUREMENT,
"units": "ohm",
},
),
# https://home.miot-spec.com/spec/yunmai.scales.ms107
S400RawDataConv("raw_data", "sensor", mi="11.e.1022.p.2"),
],
}
] + DEVICES
For other metrics like BMI, body fat, bones mass, you can leverage the bodymiscale custom component.
Final result:
This setup is only works for single user. I only tested with yunmai.scales.ms107 but it should also works with yunmai.scales.ms103 and yunmai.scales.ms104.
@PoiScript can you provide messages examples? So I can write tests on your converters.
@AlexxIT sure! here're example data:
# case 1
input 260823 => weight = 72.7
# case 2
input 1338768096 => weight: 73.6, impedance_high: 510.6
input 1234960384 => impedance_low: 471.1
# case 3
input 1343068894 => weight 73.4, heart_rate: 101, impedance_high: 512.3
input 1240727552 => impedance_low: 473.3
Note that heart rate and impedance data can be missing (case 1) when wearing shoes, as the scale cannot measure them.
Heart rate measurement can be turned off in settings. (case 2)
The scale will report two impedance values (both case 2 and case 3) which is likely measured at different frequencies
https://github.com/AlexxIT/XiaomiGateway3/releases/tag/v4.0.7
Great, but you using a wrong id for it.
id for s400 is 12505, so I changed your code from:
18639: [
"Xiaomi",
"Body Composition Scale S400",
"12505",
"yunmai.scales.ms103",
],
to:
12505: ["Xiaomi", "Body Composition Scale s400", "12505", "yunmai.scales.ms103"]
and it start working.
Thank you!)
Hi I have a s400 pro. ID: 19205 The model :xiaomi.scales.ms110 I don't know how to debug it= =. And the detail code is blow: https://home.miot-spec.com/spec/xiaomi.scales.ms110
@tufeikafei what about features? Are they same for pro model?
I add id with 19205,and found it works.
And with Impedance high and Impedance Low, how to config the BodyMiScale,the Body Mi Scale is need one impedance, I don't know use which impedance.
My high impedance matches the impedance of my old Xiaomi scales.
id for s400 is 12505, so I changed your code from: I add id with 19205,and found it works.
I guess three different BLE IDs correspond to three different models of s400 scale:
And with Impedance high and Impedance Low, how to config the BodyMiScale,the Body Mi Scale is need one impedance, I don't know use which impedance.
You can pick any one of them, or create a helper to calculate the average using https://www.home-assistant.io/integrations/min_max:
btw, I also tried to re-implantation the scale's body metrics algorithm in Home Assistant, but turns out these metrics are actually calculate on the cloud.
So unless you mock the http request, you won't be able to replicate the exact same body metrics as the Mi Home app
Just bought scale yunmai.scales.ms104, "Xiaomi Body Composition Scale S400 (BHR7793GL)".
Hi,
Does the scale connect similarly to Mi Body Composition Scale 2 (tested on XMTZC05HM)? and data (weight, impedance) can be pulled in the same way (plus optional data)
I am this integration author: https://github.com/RobertWojtowicz/export2garmin Is anyone able to help me customize the code (I don't have the scale to test yet), here is the module current to read: https://github.com/RobertWojtowicz/export2garmin/blob/master/miscale/miscale_ble.py
BR, Robert
@RobertWojtowicz Interesting integration. I was planning on doing something similar for my scales. Only take data from Fitbit, Zepp and Xiaomi clouds. And transmit the result to the Garmin cloud. I hope to have time for that someday.
Data from the new scales can be obtained, but the format is different. It is likely that encryption of the data being transmitted is used, as with any new devices.
For this integration, packet decryption is handled by the gateway. So I haven't looked into how to decrypt packets without a gateway. I think you need to wait for the support to appear in this integration: https://github.com/custom-components/ble_monitor
After some time there is support for all BLE devices from this integration.
@AlexxIT
@RobertWojtowicz Interesting integration. I was planning on doing something similar for my scales.
I invite you to cooperate, I will be happy to add you as a co-author :)
He wants to simplify the integration to a minimum and as less costly as possible (using internal ble like cheap dongles)
BR Robert
No, I have other plans. I have a long history with two old scales. Fitbit and Zepp in the clouds. I want to transfer that data to Garmin. And then keep the new scales in the Xiaomi and Garmin clouds. That way the Garmin will have a single history from all my scales.
You can enable debug log and check all incoming data.
Same here but for heartrate, always shows 124bpm, not sure how to use custom converter. may be doing something wrong.
Hi all, and please scold me if I should not be posting this question here (though if you do so, and are aware of a better place for the question, I'd appreciate that info).
I'm trying to decipher the BLE traffic between the Xiaomi S400 and my iPhone's app. I am passively monitoring the traffic. I'm having a very hard time figuring out what's going on--there are definite patterns but when I zero in on the actual substance of the data being exchanged, it seems completely random.
I don't have a gateway, so I don't think the information above is helpful. If anyone has any pointers for me, I'd appreciate it. I'd really like to be able to extract basic measurements from this device using my own code, without relying on Xiaomi's app.
@RobertWojtowicz I finally released my weight synchronization project: https://github.com/AlexxIT/SmartScaleConnect
@AlexxIT
Good job, I also did BLE communication with s400 (export2garmin project), passed the info to mi-scale-exporter project and also it is already in the mobile app thanks @lswiderski :)
I will add a link to your project in my project, great initiative, I like it :)
BR Robert
@PoiScript I don't know where you get your code. Maybe you can get formulas for S800 model?
20962: ["Xiaomi", "8-electrode Body Fat Scale S800", "MJTZC04YM", "xiaomi.scales.ms116"],
logs:
2025-09-24 21:44:22.475 DEBUG (MainThread) [custom_components.xiaomi_gateway3.mqtt.xxx] {'topic': 'miio/report', 'data': b'{"id":88142293, "method":"event_occured","params":{"did":"blt.6.xxx","siid":3,"eiid":1022,"tid":89,"arguments":[{"piid":1,"value":144},{"piid":2,"value":201654272},{"piid":3,"value":293955796}]},"type":6}'}
2025-09-24 21:44:23.498 DEBUG (MainThread) [custom_components.xiaomi_gateway3.mqtt.xxx] {'topic': 'miio/report', 'data': b'{"id":1131698294,"method":"event_occured","params":{"did":"blt.6.xxx","siid":3,"eiid":1022,"tid":90,"arguments":[{"piid":1,"value":144},{"piid":2,"value":202718212},{"piid":3,"value":0}]},"type":6}'}
2025-09-24 21:44:24.515 DEBUG (MainThread) [custom_components.xiaomi_gateway3.mqtt.xxx] {'topic': 'miio/report', 'data': b'{"id":1174714295,"method":"event_occured","params":{"did":"blt.6.xxx","siid":3,"eiid":1022,"tid":91,"arguments":[{"piid":1,"value":144},{"piid":2,"value":203751424},{"piid":3,"value":0}]},"type":6}'}
2025-09-24 21:44:25.536 DEBUG (MainThread) [custom_components.xiaomi_gateway3.mqtt.xxx] {'topic': 'miio/report', 'data': b'{"id":198538296, "method":"event_occured","params":{"did":"blt.6.xxx","siid":3,"eiid":1022,"tid":92,"arguments":[{"piid":1,"value":144},{"piid":2,"value":204800000},{"piid":3,"value":0}]},"type":6}'}
2025-09-24 21:44:27.065 DEBUG (MainThread) [custom_components.xiaomi_gateway3.mqtt.xxx] {'topic': 'miio/report', 'data': b'{"id":120318297, "method":"event_occured","params":{"did":"blt.6.xxx","siid":3,"eiid":1022,"tid":93,"arguments":[{"piid":1,"value":144},{"piid":2,"value":205848576},{"piid":3,"value":0}]},"type":6}'}
2025-09-24 21:44:28.087 DEBUG (MainThread) [custom_components.xiaomi_gateway3.mqtt.xxx] {'topic': 'miio/report', 'data': b'{"id":937360298, "method":"event_occured","params":{"did":"blt.6.xxx","siid":3,"eiid":1022,"tid":94,"arguments":[{"piid":1,"value":144},{"piid":2,"value":206897152},{"piid":3,"value":0}]},"type":6}'}