Skip to content

Commit

Permalink
Merge pull request #1117 from custom-components/miband
Browse files Browse the repository at this point in the history
Add Mi Band heart rate sensor
  • Loading branch information
Ernst79 committed Feb 2, 2023
2 parents 32e8412 + 8efa4f5 commit 922bcbb
Show file tree
Hide file tree
Showing 11 changed files with 111 additions and 5 deletions.
3 changes: 2 additions & 1 deletion README.md
Expand Up @@ -55,7 +55,8 @@ This custom component for [Home Assistant](https://www.home-assistant.io) passiv
- Thermopro
- Tilt
- Xiaogui (Scale)
- Xiaomi (MiBeacon)
- Xiaomi (MiBand)
- Xiaomi (MiBeacon sensors)
- Xiaomi (MiScale)

## Important announcement about the future of BLE monitor
Expand Down
7 changes: 6 additions & 1 deletion custom_components/ble_monitor/ble_parser/__init__.py
Expand Up @@ -23,8 +23,9 @@
from .kegtron import parse_kegtron
from .kkm import parse_kkm
from .laica import parse_laica
from .miscale import parse_miscale
from .miband import parse_miband
from .mikrotik import parse_mikrotik
from .miscale import parse_miscale
from .moat import parse_moat
from .oral_b import parse_oral_b
from .qingping import parse_qingping
Expand Down Expand Up @@ -285,6 +286,10 @@ def parse_advertisement(
# Oral-b
sensor_data = parse_oral_b(self, man_spec_data, mac, rssi)
break
elif comp_id == 0x0157 and data_len == 0x1B:
# Miband
sensor_data = parse_miband(self, man_spec_data, mac, rssi)
break
elif comp_id == 0x0499:
# Ruuvitag V3/V5
sensor_data = parse_ruuvitag(self, man_spec_data, mac, rssi)
Expand Down
45 changes: 45 additions & 0 deletions custom_components/ble_monitor/ble_parser/miband.py
@@ -0,0 +1,45 @@
# Parser for Xiaomi Mi Band BLE advertisements
import logging

from .helpers import (
to_mac,
to_unformatted_mac,
)

_LOGGER = logging.getLogger(__name__)


def parse_miband(self, data, source_mac, rssi):
# check for data length
msg_length = len(data)
if msg_length == 28:
device_type = "Mi Band"
heart_rate = data[5]
if heart_rate == 0xFF:
return None
result = {"heart rate": heart_rate}
else:
if self.report_unknown == "Mi Band":
_LOGGER.info(
"BLE ADV from UNKNOWN Mi Band DEVICE: MAC: %s, ADV: %s",
to_mac(source_mac),
data.hex()
)
return None

firmware = device_type

# check for MAC presence in sensor whitelist, if needed
if self.discovery is False and source_mac.lower() not in self.sensor_whitelist:
_LOGGER.debug("Discovery is disabled. MAC: %s is not whitelisted!", to_mac(source_mac))
return None

result.update({
"type": device_type,
"firmware": firmware,
"mac": to_unformatted_mac(source_mac),
"packet": 'no packet id',
"rssi": rssi,
"data": True,
})
return result
14 changes: 14 additions & 0 deletions custom_components/ble_monitor/const.py
Expand Up @@ -802,6 +802,17 @@ class BLEMonitorBinarySensorEntityDescription(
device_class=None,
state_class=SensorStateClass.MEASUREMENT,
),
BLEMonitorSensorEntityDescription(
key="heart rate",
sensor_class="InstantUpdateSensor",
update_behavior="Instantly",
name="ble heart rate",
unique_id="hr_",
icon="mdi:heart-pulse",
native_unit_of_measurement="bpm",
device_class=None,
state_class=SensorStateClass.MEASUREMENT,
),
BLEMonitorSensorEntityDescription(
key="pulse",
sensor_class="InstantUpdateSensor",
Expand Down Expand Up @@ -1144,6 +1155,7 @@ class BLEMonitorBinarySensorEntityDescription(
'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 Band' : [["rssi", "heart rate"], [], []],
'TZC4' : [["rssi"], ["weight", "non-stabilized weight", "impedance"], []],
'QJ-J' : [["rssi"], ["weight", "non-stabilized weight", "impedance"], []],
'Kegtron KT-100' : [["rssi"], ["volume dispensed port 1"], []],
Expand Down Expand Up @@ -1276,6 +1288,7 @@ class BLEMonitorBinarySensorEntityDescription(
'ATC' : 'ATC',
'Mi Scale V1' : 'Xiaomi',
'Mi Scale V2' : 'Xiaomi',
'Mi Band' : 'Xiaomi',
'TZC4' : 'Xiaogui',
'QJ-J' : 'MaxxMee',
'Kegtron KT-100' : 'Kegtron',
Expand Down Expand Up @@ -1461,6 +1474,7 @@ class BLEMonitorBinarySensorEntityDescription(
"Jinou",
"Kegtron",
"KKM",
"Mi Band",
"Mi Scale",
"Mikrotik",
"Moat",
Expand Down
2 changes: 1 addition & 1 deletion custom_components/ble_monitor/manifest.json
Expand Up @@ -13,6 +13,6 @@
],
"dependencies": [],
"codeowners": ["@Ernst79", "@Magalex2x14", "@Thrilleratplay"],
"version": "11.5.2",
"version": "11.6.0",
"iot_class": "local_polling"
}
1 change: 1 addition & 0 deletions custom_components/ble_monitor/sensor.py
Expand Up @@ -364,6 +364,7 @@ class BaseSensor(RestoreEntity, SensorEntity):
# | |**Air Quality Index
# |--InstantUpdateSensor (Class)
# | |**consumable
# | |**heart rate
# | |**Pulse
# | |**Shake
# | |--StateChangedSensor (Class)
Expand Down
22 changes: 22 additions & 0 deletions custom_components/ble_monitor/test/test_miband.py
@@ -0,0 +1,22 @@
"""The tests for the Mi Band ble_parser."""
from ble_monitor.ble_parser import BleParser


class TestMiBand:
"""Tests for the Mi Band parser"""
def test_miband(self):
"""Test Mi Band parser."""
data_string = "043e390d011200004b0b893f09c50100ff7fb20000000000000000001f0201041bff5701020affffffffffffffffffffffffffffff03c5093f890b4b"
data = bytes(bytearray.fromhex(data_string))

# pylint: disable=unused-variable
ble_parser = BleParser()
sensor_msg, tracker_msg = ble_parser.parse_raw_data(data)

assert sensor_msg["firmware"] == "Mi Band"
assert sensor_msg["type"] == "Mi Band"
assert sensor_msg["mac"] == "C5093F890B4B"
assert sensor_msg["packet"] == "no packet id"
assert sensor_msg["data"]
assert sensor_msg["heart rate"] == 10
assert sensor_msg["rssi"] == -78
17 changes: 17 additions & 0 deletions docs/_devices/MiBand.md
@@ -0,0 +1,17 @@
---
manufacturer: xiaomi
name: Mi Band
model: Mi Band 4 and 5
image: Mi_Band.png
physical_description: Oval Watch
broadcasted_properties:
- heart rate
- rssi
broadcasted_property_notes:
broadcast_rate:
active_scan: True
encryption_key:
custom_firmware:
notes:
- The Mi Band also reports `steps`, but this has not been implemented yet. If you want to help adding the steps, make a log with the BLE advertisements of the watch, with active scan turned on.
---
Binary file added docs/assets/images/Mi_Band.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion docs/config_params.md
Expand Up @@ -127,7 +127,7 @@ Data from sensors with other addresses will be ignored. Default value: True
### report_unknown

**Report unknown sensors**
(`Off`, `Acconeer`, `Air Mentor`, `Amazfit`, `ATC`, `BlueMaestro`, `Brifit`, `Govee`, `BTHome`, `iNode`, `iBeacon`, `Jinou`, `Kegtron`, `Mi Scale`, `Mikrotik`, `Qingping`, `Relsib`, `rbaron`, `Ruuvitag`, `Sensirion`, `SensorPush`, `SmartDry`, `Switchbot`, `Teltonika`, `Thermoplus`, `Xiaogui`, `Xiaomi`, `Other` or `False`)(Optional) This option is needed primarily for those who want to request an implementation of device support that is not in the list of [supported sensors](devices). If you set this parameter to one of the sensor brands, then the component will log all messages from unknown devices of the specified brand to the Home Assitant log (`logger` component must be enabled at info level, see for instructions the [FAQ](faq#my-sensor-from-the-xiaomi-ecosystem-is-not-in-the-list-of-supported-ones-how-to-request-implementation)). Using a sensor brand might not catch all BLE advertisements.
(`Off`, `Acconeer`, `Air Mentor`, `Amazfit`, `ATC`, `BlueMaestro`, `Brifit`, `Govee`, `BTHome`, `iNode`, `iBeacon`, `Jinou`, `Kegtron`, `Mi Scale`, `Mi Band`,`Mikrotik`, `Qingping`, `Relsib`, `rbaron`, `Ruuvitag`, `Sensirion`, `SensorPush`, `SmartDry`, `Switchbot`, `Teltonika`, `Thermoplus`, `Xiaogui`, `Xiaomi`, `Other` or `False`)(Optional) This option is needed primarily for those who want to request an implementation of device support that is not in the list of [supported sensors](devices). If you set this parameter to one of the sensor brands, then the component will log all messages from unknown devices of the specified brand to the Home Assitant log (`logger` component must be enabled at info level, see for instructions the [FAQ](faq#my-sensor-from-the-xiaomi-ecosystem-is-not-in-the-list-of-supported-ones-how-to-request-implementation)). Using a sensor brand might not catch all BLE advertisements.

If you can't find the advertisements in this way, you can set this option to `Other`, which will result is all BLE advertisements being logged. You can also enable this option at device level. **Attention!** Enabling this option can lead to huge output to the Home Assistant log, especially when set to `Other`, do not enable it if you do not need it! If you know the MAC address of the sensor, its advised to set this option at device level. Details in the [FAQ](faq#my-sensor-from-the-xiaomi-ecosystem-is-not-in-the-list-of-supported-ones-how-to-request-implementation). Default value: `Off`

Expand Down
3 changes: 2 additions & 1 deletion info.md
Expand Up @@ -57,7 +57,8 @@ This custom component for [Home Assistant](https://www.home-assistant.io) passiv
- Thermopro
- Tilt
- Xiaogui (Scale)
- Xiaomi (MiBeacon)
- Xiaomi (MiBand)
- Xiaomi (MiBeacon sensors)
- Xiaomi (MiScale)


Expand Down

0 comments on commit 922bcbb

Please sign in to comment.