Custom_component bodymiscale [For Xiaomi Miscale 1& 2] Esphome or BLE Monitor

To be honest: I have no idea how to configure this integration.

In detail: What does it expect when asking for the weight sensor?

I have the mi scale as device_tracker integrated using Bluetooth LE Tracker - Home Assistant. I don’t have a weight sensor. Created an input_number.weight for test purposes but it is not accepted.

I thought this integration would create one! :laughing:

What am I doing wrong here… please assist.

Can I use this integration at all without having a (Xiaomi) bridge? I only have the scale + bluetooth.

Or do I need to update HA Core to a newer version, as the one I am running on currently (2022.8.7) just introduced native Bluetooth support. Is active bluetooth required? That became available in later releases (2022.9 and 2022.10 I think).

You need an integration like BLE monitor to get the weight sensor. Bluetooth LE tracker is only a device tracker, nothing more.

After that, you can use the Body miscale integration to calculate other interesting data, based on weight, length, age, etc, like BMI.

Ah I see. It’s just that because of GitHub - custom-components/ble_monitor: BLE monitor passively monitors BLE sensors (Xiaomi, Qingping, ATC, BlueMaestro, Brifit, Govee, Kegtron, Moat, Inkbird, iNode, Yeelight, RuuviTag, SensorPush, Teltonika, Thermoplus and Thermopro) I thought “well, not worth installing that custom integration as it is constantly replaced by the default integration”.

Seems like that was a wrong thougt as the default bluetooth integration simply still does not recognize my Xiaomi Miscale.

And the Xiaomi BLE integration which seems to be the successor of the BLE monitor when it comes to Xiaomi devices, can’t find any devices:
grafik
grafik

So I’m pretty stuck, especially as I’m trying to avoid installing a future deprecated custom integration…

MiScales have not be moved yet to an official integration, as they use a different format. So, for the meantime, you will have to use BLE monitor. There is also a custom integration only for MiScales.

@Ernst I am trying to get the Miscale2 to work with EspHome / HA. I am getting the weight correctly, but the impedance sensor is never updated, although the Xiaomi app reflects the new measurements.

This is my config:

sensor:
  - platform: xiaomi_miscale
    mac_address: 'D0:3E:7D:3F:63:77'
    weight:
      name: "Xiaomi Mi Scale Weight"
      id: weight_miscale
      on_value:
        then:
          - lambda: |-
              if (id(weight_miscale).state >= 69 && id(weight_miscale).state <= 83) {
                return id(weight_user1).publish_state(x);}
              else if (id(weight_miscale).state >= 83.50) {
                return id(weight_user2).publish_state(x);}

    impedance:
      name: "Xiaomi Mi Scale Impedance"
      id: impedance_miscale
      on_value:
        then:
          - lambda: |-
              if (id(weight_miscale).state >= 69 && id(weight_miscale).state <= 83) {
                return id(impedance_user1).publish_state(x);}
              else if (id(weight_miscale).state >= 83.50) {
                return id(impedance_user2).publish_state(x);}

  - platform: template
    name: Weight User1
    id: weight_user1
    unit_of_measurement: 'kg'
    icon: mdi:weight-kilogram
    accuracy_decimals: 2
  - platform: template
    name: Impedance User1
    id: impedance_user1
    unit_of_measurement: 'Ω'
    icon: mdi:omega
    accuracy_decimals: 0
  - platform: template
    name: Weight User2
    id: weight_user2
    unit_of_measurement: 'kg'
    icon: mdi:weight-kilogram
    accuracy_decimals: 2
  - platform: template
    name: Impedance User2
    id: impedance_user2
    unit_of_measurement: 'Ω'
    icon: mdi:omega
    accuracy_decimals: 0

As I said, the weight works fine and gets mapped to the right person, but the impedance is never updated (also added the impedance_miscale to HA to just see if that was updated at all, but it is also never set). Is there something I am missing? I have esp32_ble_tracker: set.

Which scale do you have exactly?

Mi Smart Scale 1 / Mi Smart Scale 2 do not send impedance. (Xiaomi XMTZC01HM, XMTZC04HM)
Mi Body Composition Scale 2 / Mi Body Fat Scale do send impedance.(Xiaomi XMTZC02HM, XMTZC05HM, NUN4049CN)

It’s the body composition scale 2, XMTZC05HM. The app works and calculates the impedance based metrics. Any idea what might be going on?

No, no idea. Does it work in BLE monitor?

I don’t use BLE monitor. I use Esphome. I was able to change the defaults there to limit the risk of missing the value:

esp32_ble_tracker:
  scan_parameters:
    # default is 320ms:
    interval: 80ms
    # default is 30ms:
    window: 60ms

Hi @e-raser . Did you manage to find a solution? I have a similar setup to you, and cannot figure it out. Any help will be appreciated.

In this topic - which started looking to integrate another Bluetooth device - I also try to get the Mi Scale weight as sensor:

As long as it is not available for the native Bluetooth integration, this would be fine for me.

In case anyone benefits from this (BLE (data coming in unsorted) > split by user > BodyMiScale (one device for each users)), if You want to have 2 or more users You can create two sensor templates like this and just fill in the correct one based on the weight:

  - platform: template
    sensors:
      filtered_weight_andris:
        friendly_name: "Filtered weight Andris"
        unit_of_measurement: "kg"
        value_template: >
          {{ states.sensor.ble_weight_mi_scale2.state if ((states.sensor.ble_weight_mi_scale2.state) | int) > 68 }}
  - platform: template
    sensors:
      filtered_impedance_andris:
        friendly_name: "Filtered inpedance Andris"
        unit_of_measurement: "Ω"
        value_template: >
          {{ states.sensor.ble_impedance_mi_scale2.state if ((states.sensor.ble_weight_mi_scale2.state) | int) > 68 }}
  - platform: template
    sensors:
      filtered_weight_niki:
        friendly_name: "Filtered weight Niki"
        unit_of_measurement: "kg"
        value_template: >
          {{ states.sensor.ble_weight_mi_scale2.state if ((states.sensor.ble_weight_mi_scale2.state) | int) < 68 }}
  - platform: template
    sensors:
      filtered_impedance_niki:
        friendly_name: "Filtered inpedance Niki"
        unit_of_measurement: "Ω"
        value_template: >
          {{ states.sensor.ble_impedance_mi_scale2.state if ((states.sensor.ble_weight_mi_scale2.state) | int) < 68 }}

So I just have the Passive BLE monitor providing me the above weight and impedance values which I turn into two template sensors each (based on above or below 68), which in return are used as the weight and imp. source for the BodyMyScale integration (so the resulting 2x2 entries become the data feed of the BodyMiScale).

1 Like

Some extra info on where to put that code would be welcomed.

do we put that into sensors.yaml?
in configuration.yaml?

do we have to alter something in bodymiscale?

I put it in configuration.yaml.
No altering is needed.
BodyMiScale requires a weight and an impedance sensor value to work. Normally You would add the sensor data (weight/impedance) coming from the BLE sensor. But if You create (with the above code) 2 template sensors (just like the original), then You basically mimic the BLE output, You create two pairs of very similar sensors. These template sensors can then provide the input to two MiBodyScale instances. See here:


As You can see I added 2 services and I am NOT using the BLE sensor output for weight and impedance (at least not directly), but the template sensors I created with the above code.
I hope this helps.

Thanks for answering man. I have it working a couple of days now but it gets errors in the logs sometimes and also it is like this is not a real sensor somehow. Like its a float that get erased after restart.

some of the errors

Error doing job: Exception in callback _async_at_core_state.<locals>._matched_event(<Event homeassistant_start[L]>) at /usr/src/homeassistant/homeassistant/helpers/start.py:37
Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/components/sensor/__init__.py", line 616, in state
    numerical_value = int(value)
                      ^^^^^^^^^^
ValueError: invalid literal for int() with base 10: ''

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/components/sensor/__init__.py", line 619, in state
    numerical_value = float(value)
                      ^^^^^^^^^^^^
ValueError: could not convert string to float: ''

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/usr/local/lib/python3.11/asyncio/events.py", line 80, in _run
    self._context.run(self._callback, *self._args)
  File "/usr/src/homeassistant/homeassistant/core.py", line 1236, in _onetime_listener
    self._hass.async_run_job(listener, event)
  File "/usr/src/homeassistant/homeassistant/core.py", line 719, in async_run_job
    return self.async_run_hass_job(HassJob(target), *args)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/src/homeassistant/homeassistant/core.py", line 671, in async_run_hass_job
    hassjob.target(*args)
  File "/usr/src/homeassistant/homeassistant/helpers/start.py", line 40, in _matched_event
    hass.async_run_hass_job(at_start_job, hass)
  File "/usr/src/homeassistant/homeassistant/core.py", line 671, in async_run_hass_job
    hassjob.target(*args)
  File "/usr/src/homeassistant/homeassistant/components/template/template_entity.py", line 478, in _async_template_startup
    result_info.async_refresh()
  File "/usr/src/homeassistant/homeassistant/helpers/event.py", line 1035, in async_refresh
    self._refresh(None)
  File "/usr/src/homeassistant/homeassistant/helpers/event.py", line 1215, in _refresh
    self.hass.async_run_hass_job(self._job, event, updates)
  File "/usr/src/homeassistant/homeassistant/core.py", line 671, in async_run_hass_job
    hassjob.target(*args)
  File "/usr/src/homeassistant/homeassistant/components/template/template_entity.py", line 429, in _handle_results
    self.async_write_ha_state()
  File "/usr/src/homeassistant/homeassistant/helpers/entity.py", line 745, in async_write_ha_state
    self._async_write_ha_state()
  File "/usr/src/homeassistant/homeassistant/helpers/entity.py", line 845, in _async_write_ha_state
    state, attr = self._async_generate_attributes()
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/src/homeassistant/homeassistant/helpers/entity.py", line 786, in _async_generate_attributes
    state = self._stringify_state(available)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/src/homeassistant/homeassistant/helpers/entity.py", line 751, in _stringify_state
    if (state := self.state) is None:
                 ^^^^^^^^^^
  File "/usr/src/homeassistant/homeassistant/components/sensor/__init__.py", line 623, in state
    raise ValueError(
ValueError: Sensor sensor.filtered_weight_gnf has device class 'None', state class 'None' unit 'kg' and suggested precision 'None' thus indicating it has a numeric value; however, it has the non-numeric value: '' (<class 'str'>)
Logger: homeassistant.helpers.event
Source: helpers/event.py:296
First occurred: December 15, 2023 at 12:39:58 (2 occurrences)
Last logged: December 15, 2023 at 12:39:58

Error while dispatching event for sensor.mi_body_composition_scale_7dc3_mass to <Job track state_changed event {'sensor.mi_body_composition_scale_7dc3_mass'} HassJobType.Callback <bound method TrackTemplateResultInfo._refresh of <TrackTemplateResultInfo {Template<template=({{ states.sensor.mi_body_composition_scale_7dc3_mass.state if ((states.sensor.mi_body_composition_scale_7dc3_mass.state) | int) < 60 }}) renders=8>: <RenderInfo Template<template=({{ states.sensor.mi_body_composition_scale_7dc3_mass.state if ((states.sensor.mi_body_composition_scale_7dc3_mass.state) | int) < 60 }}) renders=8> all_states=False all_states_lifecycle=False domains=frozenset() domains_lifecycle=frozenset() entities=frozenset({'sensor.mi_body_composition_scale_7dc3_mass'}) rate_limit=None has_time=False exception=None is_static=False>}>>>
Error while dispatching event for sensor.mi_body_composition_scale_7dc3_mass to <Job track state_changed event {'sensor.mi_body_composition_scale_7dc3_impedance', 'sensor.mi_body_composition_scale_7dc3_mass'} HassJobType.Callback <bound method TrackTemplateResultInfo._refresh of <TrackTemplateResultInfo {Template<template=({{ states.sensor.mi_body_composition_scale_7dc3_impedance.state if ((states.sensor.mi_body_composition_scale_7dc3_mass.state) | int) < 60 }}) renders=10>: <RenderInfo Template<template=({{ states.sensor.mi_body_composition_scale_7dc3_impedance.state if ((states.sensor.mi_body_composition_scale_7dc3_mass.state) | int) < 60 }}) renders=10> all_states=False all_states_lifecycle=False domains=frozenset() domains_lifecycle=frozenset() entities=frozenset({'sensor.mi_body_composition_scale_7dc3_mass'}) rate_limit=None has_time=False exception=None is_static=False>}>>>
Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/components/sensor/__init__.py", line 616, in state
    numerical_value = int(value)
                      ^^^^^^^^^^
ValueError: invalid literal for int() with base 10: ''

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/components/sensor/__init__.py", line 619, in state
    numerical_value = float(value)
                      ^^^^^^^^^^^^
ValueError: could not convert string to float: ''

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/helpers/event.py", line 296, in _async_dispatch_entity_id_event
    hass.async_run_hass_job(job, event)
  File "/usr/src/homeassistant/homeassistant/core.py", line 671, in async_run_hass_job
    hassjob.target(*args)
  File "/usr/src/homeassistant/homeassistant/helpers/event.py", line 1215, in _refresh
    self.hass.async_run_hass_job(self._job, event, updates)
  File "/usr/src/homeassistant/homeassistant/core.py", line 671, in async_run_hass_job
    hassjob.target(*args)
  File "/usr/src/homeassistant/homeassistant/components/template/template_entity.py", line 429, in _handle_results
    self.async_write_ha_state()
  File "/usr/src/homeassistant/homeassistant/helpers/entity.py", line 745, in async_write_ha_state
    self._async_write_ha_state()
  File "/usr/src/homeassistant/homeassistant/helpers/entity.py", line 845, in _async_write_ha_state
    state, attr = self._async_generate_attributes()
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/src/homeassistant/homeassistant/helpers/entity.py", line 786, in _async_generate_attributes
    state = self._stringify_state(available)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/src/homeassistant/homeassistant/helpers/entity.py", line 751, in _stringify_state
    if (state := self.state) is None:
                 ^^^^^^^^^^
  File "/usr/src/homeassistant/homeassistant/components/sensor/__init__.py", line 623, in state
    raise ValueError(
ValueError: Sensor sensor.filtered_weight_florentia has device class 'None', state class 'None' unit 'kg' and suggested precision 'None' thus indicating it has a numeric value; however, it has the non-numeric value: '' (<class 'str'>)

I am trying to get the new sensor way of home assistant and not the legacy one.

template:
  - trigger:
    - platform: numeric_state
      value_template: "{{ states('sensor.mi_body_composition_scale_7dc3_mass') | float(0) >= 60 }}"
    sensor:
    - unique_id: filtered_weight_gnf
      name: "filtered_weight_gnf"
      friendly_name: "Filtered weight GnF"
      unit_of_measurement: "kg"
      state: "{{ states('sensor.mi_body_composition_scale_7dc3_mass') }}"

  - trigger:
    - platform: numeric_state
      value_template: "{{ states('sensor.mi_body_composition_scale_7dc3_mass') | float(0) >= 60 }}"
    sensor:
    - unique_id: filtered_impedance_gnf
      name: "filtered_impedance_gnf"
      friendly_name: "Filtered inpedance GnF"
      unit_of_measurement: "Ω"
      state: "{{ states('sensor.mi_body_composition_scale_7dc3_impedance') }}"

But i get this error.

Configuration warnings

Invalid config for 'sensor' at configuration.yaml, line 12: required key 'platform' not provided

Hey GnF, I will not be able to help You with so deep tech, I did not check myself if I have errors as long as it works reliable (my system is running now for 4 years, so I have a lot of misconfigs). However as far as I am understanding what we are doing the sensor being reseted at restart is normal (as its just a template being filled out by its input when it arrives), but I think there should be a way to make it non volatile. Sorry that I can not help You further.

1 Like

really man thanks for even answering.
I will post any finds i have.

this is what i have till now

# sensors.yaml
- platform: template
  sensors:
    filtered_weight_gnf:
      unique_id: filtered_weight_gnf
      friendly_name: "Filtered Weight GNF"
      unit_of_measurement: "kg"
      value_template: >-
        {% if states('sensor.mi_body_composition_scale_7dc3_mass') | float > 60 %}
          {{ states('sensor.mi_body_composition_scale_7dc3_mass') }}
        {% endif %}

    filtered_impedance_gnf:
      unique_id: filtered_impedance_gnf
      friendly_name: "Filtered Impedance GNF"
      unit_of_measurement: "ohms"
      value_template: >-
        {% if states('sensor.mi_body_composition_scale_7dc3_mass') | float > 60 %}
          {{ states('sensor.mi_body_composition_scale_7dc3_impedance') }}
        {% endif %}

    filtered_weight_florentia:
      unique_id: filtered_weight_florentia
      friendly_name: "Filtered Weight Florentia"
      unit_of_measurement: "kg"
      value_template: >-
        {% if states('sensor.mi_body_composition_scale_7dc3_mass') | float <= 60 %}
          {{ states('sensor.mi_body_composition_scale_7dc3_mass') }}
        {% endif %}

    filtered_impedance_florentia:
      unique_id: filtered_impedance_florentia
      friendly_name: "Filtered Impedance Florentia"
      unit_of_measurement: "ohms"
      value_template: >-
        {% if states('sensor.mi_body_composition_scale_7dc3_mass') | float <= 60 %}
          {{ states('sensor.mi_body_composition_scale_7dc3_impedance') }}
        {% endif %}

I did try to use else return to 0 if it does not have a measure or if it looses data but it does not work ok. I will keep trying.

So the above again does not work very well.
Lets say user one and two.
User one uses the scale and all filtered sensors update normaly. Also the card for that user is working.
User two uses the scale and all is well.

If after a day or two user one has not use the scale but user two is keep using it then the card for user two is working normally but the card for user one says unavailable.

Damn

Since the data are stored in a time based db maybe the data from user one are now old? And this integration does not see them?
I have to find a way to keep updating the filtered value on the new sensors on the time even if there are no new data. Damn

Hopefully someone can help me here…

I´ve successfully configured the integration through BLE, and the first weigh-in shows up correctly. But I just cant get it to update with new values when i´m weighing myself again. The Zepp Life app registers new weight but nothing is updated in HA…

Why is this? There´s no error logs either

The signal strength is: -73 dBm. Maybe thats the issue? But the scale is 3 meters away from my raspberry pi