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

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

Make sure to keep standing on the scale until the weight-display has switched itself off again.

did that, twice, also restarted HA. No change :frowning:
Zepp Life registers the changes

Be careful, you cannot be on the Zepp application at the same time.

1 Like

Dont know what fixed it. I “reinstalled” the Bluetooth integration, then opened the integration and clicked on configure and checked “passive scanning”. Cant say if I had this enabled before.

Having the Zepp app open does, in fact, hinder the information to be sent to HA.

I dont have to wait for the display to switch off. Its enough if I wait until the measurment-bar is fully complete.

Is this still the case?

Meanwhile I integrated it using OMG.
But having it natively with plain „Bluetooth on HA“ would be an improvement.