From 0ddbadefcc0ee9f387d804fce572c252aa14af52 Mon Sep 17 00:00:00 2001 From: DesertOvercrowded Date: Thu, 10 Sep 2026 08:50:08 +0200 Subject: [PATCH] feat: expose Mi Scale stabilized state --- custom_components/ble_monitor/const.py | 14 +++++- .../ble_monitor/test/test_miscale_parser.py | 44 +++++++++++++++++++ docs/_devices/Xiaomi_XMTZC01HM_XMTZC04HM.md | 3 ++ .../Xiaomi_XMTZC02HM_XMTZC05HM_NUN4049CN.md | 3 ++ 4 files changed, 62 insertions(+), 2 deletions(-) diff --git a/custom_components/ble_monitor/const.py b/custom_components/ble_monitor/const.py index 195491e6b..8c6dd5cfe 100644 --- a/custom_components/ble_monitor/const.py +++ b/custom_components/ble_monitor/const.py @@ -402,6 +402,16 @@ class BLEMonitorBinarySensorEntityDescription( device_class=None, force_update=False, ), + BLEMonitorBinarySensorEntityDescription( + key="stabilized", + sensor_class="BaseBinarySensor", + update_behavior="Instantly", + name="ble stabilized", + icon="mdi:scale-bathroom", + unique_id="stabilized_", + device_class=None, + force_update=False, + ), BLEMonitorBinarySensorEntityDescription( key="fingerprint", sensor_class="BaseBinarySensor", @@ -2133,8 +2143,8 @@ class BLEMonitorBinarySensorEntityDescription( 'YLKG07YL/YLKG08YL' : [["rssi"], ["dimmer"], []], 'SU001-T' : [["battery", "rssi"], [], ["motion", "switch"]], 'ATC' : [["temperature", "humidity", "battery", "voltage", "rssi"], [], ["switch", "opening"]], - 'Mi Scale V1' : [["rssi"], ["weight", "non-stabilized weight"], ["weight removed"]], - 'Mi Scale V2' : [["rssi"], ["weight", "stabilized weight", "non-stabilized weight", "impedance"], ["weight removed"]], + 'Mi Scale V1' : [["rssi"], ["weight", "non-stabilized weight"], ["weight removed", "stabilized"]], + 'Mi Scale V2' : [["rssi"], ["weight", "stabilized weight", "non-stabilized weight", "impedance"], ["weight removed", "stabilized"]], 'Mi Band' : [["rssi", "heart rate", "steps"], [], []], 'TZC4' : [["rssi"], ["weight", "non-stabilized weight", "impedance"], []], 'QJ-J' : [["rssi"], ["weight", "non-stabilized weight", "impedance"], []], diff --git a/custom_components/ble_monitor/test/test_miscale_parser.py b/custom_components/ble_monitor/test/test_miscale_parser.py index e57be9442..c5644b378 100644 --- a/custom_components/ble_monitor/test/test_miscale_parser.py +++ b/custom_components/ble_monitor/test/test_miscale_parser.py @@ -1,5 +1,10 @@ """The tests for the Mi Scale ble_parser.""" +from types import SimpleNamespace + +from ble_monitor.binary_sensor import BaseBinarySensor from ble_monitor.ble_parser import BleParser +from ble_monitor.const import BINARY_SENSOR_TYPES, MEASUREMENT_DICT +from ble_monitor.sensor import WeightSensor class TestMiscale: @@ -24,6 +29,17 @@ def test_miscale_v1(self): assert sensor_msg["stabilized"] == 1 assert sensor_msg["rssi"] == -59 + weight_removed_sensor = BaseBinarySensor.__new__(BaseBinarySensor) + weight_removed_sensor._type = "mac" + weight_removed_sensor._device_type = "Mi Scale V1" + weight_removed_sensor.entity_description = SimpleNamespace( + key="weight removed" + ) + weight_removed_sensor._extra_state_attributes = {} + weight_removed_sensor.collect(sensor_msg) + assert weight_removed_sensor._newstate == 1 + assert weight_removed_sensor._extra_state_attributes["weight"] == 87.2 + def test_miscale_v1_ext(self): """Test Mi Scale v1 parser (extended advertisement).""" data_string = "043e390d011300008995c08c47c80100ff7fc70000000000000000001f02010603021d1809ff5701c8478cc095890d161d18821400e507040b101708" @@ -103,5 +119,33 @@ def test_miscale_v2_impedance(self): assert sensor_msg["weight unit"] == "kg" assert sensor_msg["weight removed"] == 0 assert sensor_msg["stabilized"] == 1 + assert sensor_msg["stabilized weight"] == 85.15 + assert sensor_msg["weight"] == 85.15 assert sensor_msg["impedance"] == 428 assert sensor_msg["rssi"] == -66 + + weight_sensor = WeightSensor.__new__(WeightSensor) + weight_sensor._type = "mac" + weight_sensor.entity_description = SimpleNamespace( + key="non-stabilized weight" + ) + weight_sensor._extra_state_attributes = {} + weight_sensor.pending_update = False + weight_sensor.collect(sensor_msg, period_cnt=0) + assert weight_sensor._extra_state_attributes["stabilized"] is True + + def test_miscale_stabilized_binary_sensor_mapping(self): + """Test Mi Scale V1 and V2 expose the parsed stabilization flag.""" + for device_type in ("Mi Scale V1", "Mi Scale V2"): + assert MEASUREMENT_DICT[device_type][2] == [ + "weight removed", + "stabilized", + ] + + description = next( + item for item in BINARY_SENSOR_TYPES if item.key == "stabilized" + ) + assert description.sensor_class == "BaseBinarySensor" + assert description.update_behavior == "Instantly" + assert description.unique_id == "stabilized_" + assert description.device_class is None diff --git a/docs/_devices/Xiaomi_XMTZC01HM_XMTZC04HM.md b/docs/_devices/Xiaomi_XMTZC01HM_XMTZC04HM.md index c1c39540f..b78d2ad22 100644 --- a/docs/_devices/Xiaomi_XMTZC01HM_XMTZC04HM.md +++ b/docs/_devices/Xiaomi_XMTZC01HM_XMTZC04HM.md @@ -7,6 +7,7 @@ physical_description: broadcasted_properties: - weight - non-stabilized weight + - stabilized - weight removed - rssi broadcasted_property_notes: @@ -14,6 +15,8 @@ broadcasted_property_notes: note: is only reported after the scale is stabilized - property: non-stabilized weight note: is reporting all weight measurements + - property: stabilized + note: reports the stabilization flag transmitted by the scale broadcast_rate: active_scan: encryption_key: diff --git a/docs/_devices/Xiaomi_XMTZC02HM_XMTZC05HM_NUN4049CN.md b/docs/_devices/Xiaomi_XMTZC02HM_XMTZC05HM_NUN4049CN.md index 618080d42..0af5e180a 100644 --- a/docs/_devices/Xiaomi_XMTZC02HM_XMTZC05HM_NUN4049CN.md +++ b/docs/_devices/Xiaomi_XMTZC02HM_XMTZC05HM_NUN4049CN.md @@ -8,6 +8,7 @@ broadcasted_properties: - weight - non-stabilized weight - stabilized weight + - stabilized - weight removed - impedance - rssi @@ -18,6 +19,8 @@ broadcasted_property_notes: note: is updated, even if the impedance has not been calculated and send by the scale. This sensor is updated slightly before the weight sensor, even if you step off the scale before the white line starts flashing, but the `weight` measurement does not always correspond to the `impedance` measurement. - property: non-stabilized weight note: this sensor is reporting all weight measurements + - property: stabilized + note: reports the stabilization flag transmitted by the scale broadcast_rate: active_scan: encryption_key: