diff --git a/README.md b/README.md index d069dccb..3911aa33 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,7 @@ This custom component for [Home Assistant](https://www.home-assistant.io) passiv - Ruuvitag - Sensirion - SensorPush +- Senssun (Scale) - SmartDry - Switchbot - Teltonika diff --git a/custom_components/ble_monitor/ble_parser/__init__.py b/custom_components/ble_monitor/ble_parser/__init__.py index 5e3a1159..b82855f8 100644 --- a/custom_components/ble_monitor/ble_parser/__init__.py +++ b/custom_components/ble_monitor/ble_parser/__init__.py @@ -38,6 +38,7 @@ from .ruuvitag import parse_ruuvitag from .sensirion import parse_sensirion from .sensorpush import parse_sensorpush +from .senssun import parse_senssun from .smartdry import parse_smartdry from .switchbot import parse_switchbot from .teltonika import parse_teltonika @@ -422,6 +423,10 @@ def parse_advertisement( # Laica sensor_data = parse_laica(self, man_spec_data, mac) break + elif comp_id == 0x0100 and data_len == 0x14: + # Senssun IF_B7 + sensor_data = parse_senssun(self, man_spec_data, mac) + break # Filter on part of the UUID16 elif man_spec_data[2] == 0xC0 and data_len == 0x10: diff --git a/custom_components/ble_monitor/ble_parser/senssun.py b/custom_components/ble_monitor/ble_parser/senssun.py new file mode 100644 index 00000000..5d18e339 --- /dev/null +++ b/custom_components/ble_monitor/ble_parser/senssun.py @@ -0,0 +1,50 @@ +"""Parser for Senssun Scale BLE advertisements""" + +import logging +from struct import unpack + +from .helpers import to_mac, to_unformatted_mac + +_LOGGER = logging.getLogger(__name__) + + +def read_stable(ctr1): + """Parse Stable""" + return int((ctr1 & 0xA0) == 0xA0) + +def parse_senssun(self, data: bytes, mac: str): + """Parser for Senssun Scales.""" + xvalue = data[13:19] + + (division, weight, impedance, ctr1) = unpack(">bhhb", xvalue) + result = { + "type": "Senssun Smart Scale", + "firmware": "Senssun", + "mac": to_unformatted_mac(mac), + "data": True, + "impedance": impedance, + "weight": weight / 100.0, + "stabilized": read_stable(ctr1), + } + + # Check for duplicate messages + packet_id = xvalue.hex() + try: + prev_packet = self.lpacket_ids[mac] + except KeyError: + # start with empty first packet + prev_packet = None + if prev_packet == packet_id: + # only process new messages + if self.filter_duplicates is True: + return None + self.lpacket_ids[mac] = packet_id + if prev_packet is None: + if self.filter_duplicates is True: + # ignore first message after a restart + return None + + result.update({ + "packet": packet_id, + }) + return result diff --git a/custom_components/ble_monitor/const.py b/custom_components/ble_monitor/const.py index 9fa8e032..522435ff 100755 --- a/custom_components/ble_monitor/const.py +++ b/custom_components/ble_monitor/const.py @@ -1975,6 +1975,7 @@ class BLEMonitorBinarySensorEntityDescription( 'K6 Sensor Beacon' : [["temperature", "humidity", "acceleration", "voltage", "battery", "rssi"], [], []], 'DSL-C08' : [["battery", "rssi", "voltage"], [], ["lock", "childlock"]], 'SmartDry cloth dryer' : [["temperature", "humidity", "voltage", "battery", "shake", "rssi"], [], ["switch"]], + 'Senssun Smart Scale' : [["rssi"], ["weight", "impedance"], []], } # Sensor manufacturer dictionary @@ -2111,6 +2112,7 @@ class BLEMonitorBinarySensorEntityDescription( 'Laica Smart Scale' : 'Laica', 'K6 Sensor Beacon' : 'KKM', 'SmartDry cloth dryer' : 'SmartDry', + 'Senssun Smart Scale' : 'Senssun', } diff --git a/custom_components/ble_monitor/test/test_senssun_parser.py b/custom_components/ble_monitor/test/test_senssun_parser.py new file mode 100644 index 00000000..e7aad570 --- /dev/null +++ b/custom_components/ble_monitor/test/test_senssun_parser.py @@ -0,0 +1,28 @@ +"""The tests for the Senssun ble_parser.""" + +import datetime + +from ble_monitor.ble_parser import BleParser + + +class TestSenssun: + """Tests for the Senssun parser""" + + def test_Senssun_IF_B7(self): + """Test Senssun parser for IF_B7.""" + data_string = "043E2B0201030033B3C1937A181F020106060949465F423714FF0001020311187A93C1B333021A4500B4A1AB61C5" + 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"] == "Senssun" + assert sensor_msg["type"] == "Senssun Smart Scale" + assert sensor_msg["mac"] == "187A93C1B333" + assert sensor_msg["packet"] == "021a4500b4a1" + assert sensor_msg["data"] + assert sensor_msg["weight"] == 67.25 + assert sensor_msg["stabilized"] == 1 + assert sensor_msg["impedance"] == 180 + assert sensor_msg["rssi"] == -59 diff --git a/docs/_devices/Senssun_Smart_Scale.md b/docs/_devices/Senssun_Smart_Scale.md new file mode 100644 index 00000000..12e57b4b --- /dev/null +++ b/docs/_devices/Senssun_Smart_Scale.md @@ -0,0 +1,17 @@ +--- +manufacturer: Senssun +name: Senssun Smart Scale +model: IF_B7 +image: IF_B7.jpg +physical_description: +broadcasted_properties: + - weight + - impedance + - rssi +broadcasted_property_notes: +broadcast_rate: +active_scan: +encryption_key: +custom_firmware: +notes: +--- diff --git a/docs/assets/images/IF_B7.jpg b/docs/assets/images/IF_B7.jpg new file mode 100644 index 00000000..09dd8af5 Binary files /dev/null and b/docs/assets/images/IF_B7.jpg differ diff --git a/docs/config_params.md b/docs/config_params.md index 6bf69636..31097601 100644 --- a/docs/config_params.md +++ b/docs/config_params.md @@ -123,7 +123,7 @@ Data from sensors with other addresses will be ignored. Default value: True **Report unknown sensors** - (`Off`, `Acconeer`, `Air Mentor`, `Amazfit`, `ATC`, `BlueMaestro`, `Blustream`, `Brifit`, `BTHome`, `Chef iQ`, `Govee`, `Grundfos`, `HolyIOT`, `Hormann`, `HHCC`, `iNode`, `iBeacon`, `Jinou`, `Kegtron`, `Mi Scale`, `Mi Band`,`Mikrotik`, `Oras`, `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 Assistant 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`, `Blustream`, `Brifit`, `BTHome`, `Chef iQ`, `Govee`, `Grundfos`, `HolyIOT`, `Hormann`, `HHCC`, `iNode`, `iBeacon`, `Jinou`, `Kegtron`, `Mi Scale`, `Mi Band`,`Mikrotik`, `Oras`, `Qingping`, `Relsib`, `rbaron`, `Ruuvitag`, `Sensirion`, `SensorPush`, `Senssun`, `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 Assistant 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` diff --git a/info.md b/info.md index 8b110a62..dd9f1e84 100644 --- a/info.md +++ b/info.md @@ -55,6 +55,7 @@ This custom component for [Home Assistant](https://www.home-assistant.io) passiv - Ruuvitag - Sensirion - SensorPush +- Senssun - SmartDry - Switchbot - Teltonika