1technophile / OpenMQTTGateway

MQTT gateway for ESP8266 or ESP32 with bidirectional 433mhz/315mhz/868mhz, Infrared communications, BLE, Bluetooth, beacons detection, mi flora, mi jia, LYWSD02, LYWSD03MMC, Mi Scale, TPMS, BBQ thermometer compatibility & LoRa.
https://docs.openmqttgateway.com
GNU General Public License v3.0
3.51k stars 776 forks source link

Blood pressure sensor Silvercrest SBM69 #806

Open tiagofreire-pt opened 3 years ago

tiagofreire-pt commented 3 years ago

Is your feature request related to a problem? Please describe. No.

This is a request for a new BLE device, such as Silvercrest SBM69 sold by LIDL in Europe.

Describe the solution you'd like I need to integrate this blood pressure product on my Home Assistant instance, regarding my biometrics usage for automations and automatic long term register.

Captured data with the android APP "nRF Connect".

RAW Data: 0x060953424D36390201060302101804FF130601

Blood pressure service: 00001810-0000-1000-8000-00805f9b35fb Blood pressure measurement characteristic: 00002a35-0000-1000-8000-00805f9b34fb Properties: Indicate Value: Systolic: xxx.xx mmHg Disastolic: yyy.yy mmHg Mean AP: zzz.zz mmHg Timestamp: hh:mm:ss dd.mm.yyyy Pulse: aaa.a bpm User ID: 0

Describe alternatives you've considered No other solution known is available.

Additional context This device requires an active BLE connection, inserting a randomly key code generated only on the first pairing moment.

tiagofreire-pt commented 3 years ago

FCC info: https://fccid.io/OU9SBM69-B01

ZolaSat commented 1 year ago

" inserting a randomly key code generated only on the first pairing moment." ...and the solution?

ignisf commented 1 year ago

The device seems to use the much of the standard HDP for blood pressure monitoring with the exception of not using 16-bit floats for sys/dia/avg/pulse. Here's an ImHex pattern that recognises the output:

bitfield Flags {
  blood_pressure_units: 1;
  time_stamp: 1;
  pulse_rate: 1;
  user_id: 1;
  measurement_status: 1;
  reserved_for_future_use: 3;
};

bitfield MeasurementStatus {
  body_movement_detected: 1;
  cuff_too_loose: 1;
  irregular_pulse: 1;
  pulse_rate_exceeds_lower_limit: 1;
  pulse_rate_exceeds_upper_limit: 1;
  improper_measurement_position: 1;
  reserved_for_future_use: 10;
};

struct BloodPressureMeasurement {
  le Flags flags;

  if (flags.blood_pressure_units) {
    le u16 systolic_kpa;
    le u16 diastolic_kpa;
    le u16 mean_arterial_pressure_kpa;
  } else {
    le u16 systolic_mmhg;
    le u16 diastolic_mmhg;
    le u16 mean_arterial_pressure_mmhg;
  }

  if (flags.time_stamp) {
    le u16 year;
    u8 month;
    u8 day;
    u8 hours;
    u8 minutes;
    u8 seconds;
  }

  if (flags.pulse_rate) {
    le u16 pulse_rate;
  }

  if (flags.user_id) {
    u8 user_id;
  }

  if (flags.measurement_status) {
    le MeasurementStatus measurement_status;
  }
};

BloodPressureMeasurement bpm @ 0x00;

https://www.bluetooth.com/specifications/specs/gatt-specification-supplement/ https://www.bluetooth.com/specifications/specs/blood-pressure-service-1-1-1/ https://www.bluetooth.com/specifications/specs/blood-pressure-profile-1-1-1/

ignisf commented 1 year ago

Python PoC that reads out all relevant data from the device:

import asyncio
from bleak import BleakClient
from bleak.backends.characteristic import BleakGATTCharacteristic

address = "b8:b7:7d:XX:XX:XX"

async def main(address):
    async with BleakClient(address) as client:
        for service in client.services:
            print("Service: {0} ({1})".format(service.description, service.uuid))

            for characteristic in service.characteristics:
                print("  Characteristic: {0} ({1}), properties: {2}".format(characteristic.description, characteristic.uuid, ",".join(characteristic.properties)))

                for descriptor in characteristic.descriptors:
                    print("    Descriptor: {0} ({1})".format(descriptor.description, descriptor.uuid))

                if 'read' in characteristic.properties:
                    value = await client.read_gatt_char(characteristic.uuid)
                    print("    Value: {0} ({1})".format("".join(map(chr, value)), value.hex()))

                if 'indicate' in characteristic.properties:
                    try:
                        await asyncio.wait_for(client.start_notify(characteristic.uuid, on_notification), timeout=2)
                    except TimeoutError:
                        await client.stop_notify(characteristic.uuid)

def on_notification(handle: BleakGATTCharacteristic, data: bytearray):
    print("    Value: {0}".format(data.hex()))

asyncio.run(main(address))
Service: Device Information (0000180a-0000-1000-8000-00805f9b34fb)
  Characteristic: PnP ID (00002a50-0000-1000-8000-00805f9b34fb), properties: read
    Value:  ()
  Characteristic: Serial Number String (00002a25-0000-1000-8000-00805f9b34fb), properties: read
    Value: XXXXXXXXXXXX (XXXXXXXXXXXXXXXXXXXXXXXX)
  Characteristic: Software Revision String (00002a28-0000-1000-8000-00805f9b34fb), properties: read
    Value: 2.0 (322e30)
  Characteristic: Firmware Revision String (00002a26-0000-1000-8000-00805f9b34fb), properties: read
    Value: 2.0 (322e30)
  Characteristic: Hardware Revision String (00002a27-0000-1000-8000-00805f9b34fb), properties: read
    Value: 7100 (37313030)
  Characteristic: IEEE 11073-20601 Regulatory Cert. Data List (00002a2a-0000-1000-8000-00805f9b34fb), properties: read
    Value:  ()
  Characteristic: Model Number String (00002a24-0000-1000-8000-00805f9b34fb), properties: read
    Value: SBM69 (53424d3639)
  Characteristic: Manufacturer Name String (00002a29-0000-1000-8000-00805f9b34fb), properties: read
    Value: Hans Dinslage GmbH (48616e732044696e736c61676520476d6248)
  Characteristic: System ID (00002a23-0000-1000-8000-00805f9b34fb), properties: read
    Value:  (0000000000000000)
Service: Generic Attribute Profile (00001801-0000-1000-8000-00805f9b34fb)
  Characteristic: Service Changed (00002a05-0000-1000-8000-00805f9b34fb), properties: indicate
    Descriptor: Client Characteristic Configuration (00002902-0000-1000-8000-00805f9b34fb)
Service: Blood Pressure (00001810-0000-1000-8000-00805f9b34fb)
  Characteristic: Blood Pressure Feature (00002a49-0000-1000-8000-00805f9b34fb), properties: read
    Value:  (0400)
  Characteristic: Intermediate Cuff Pressure (00002a36-0000-1000-8000-00805f9b34fb), properties: notify
    Descriptor: Client Characteristic Configuration (00002902-0000-1000-8000-00805f9b34fb)
  Characteristic: Blood Pressure Measurement (00002a35-0000-1000-8000-00805f9b34fb), properties: indicate
    Descriptor: Client Characteristic Configuration (00002902-0000-1000-8000-00805f9b34fb)
    Value: 1eXXXXXXXXXXXXe70702190d3200XX00000000
    Value: 1eXXXXXXXXXXXXe70702190d3300XX00000000
    Value: 1eXXXXXXXXXXXXe70702190d3500XX00000400
    Value: 1eXXXXXXXXXXXXe7070219170000XX00000000
ignisf commented 1 year ago

Unfortunately it is necessary for the device to be paired at first use. It refuses to respond unless paired.

DigiH commented 1 year ago

Unless some functions require registration for NOTIFY, all others can be read by OpenMQTTGateway's READ command

For OpenMTTGateway to be able to directly publish decoded properties through the Theengs Decoder library, the Silvercrest SBM69 would need to broadcast its information in its advertisement data. If this is the case could you provide some sample data along with the relevant reading?

ignisf commented 1 year ago

Unfortunately you need to subscribe to indicate events for the 'Blood Pressure Measurement (00002a35-0000-1000-8000-00805f9b34fb)' characteristic in order to get the measurement data. No relevant data is part of the advertisements of the device.

DigiH commented 1 year ago

Unfortunately this is then currently not accessible through OpenMQTTGateway.

ignisf commented 1 year ago

FWIW I just created https://github.com/ignisf/sbm69 and will be looking into making a home assistant component that uses it.