diff --git a/src/bthome_ble/__init__.py b/src/bthome_ble/__init__.py index 93b02f2..37e2531 100644 --- a/src/bthome_ble/__init__.py +++ b/src/bthome_ble/__init__.py @@ -13,14 +13,11 @@ Units, ) -from .const import SLEEPY_BINARY_SENSORS, SLEEPY_SENSORS from .parser import BTHomeBluetoothDeviceData __version__ = "2.10.1" __all__ = [ - "SLEEPY_BINARY_SENSORS", - "SLEEPY_SENSORS", "BinarySensorDeviceClass", "BTHomeBluetoothDeviceData", "DeviceClass", diff --git a/src/bthome_ble/const.py b/src/bthome_ble/const.py index 28746cd..bbd74a0 100644 --- a/src/bthome_ble/const.py +++ b/src/bthome_ble/const.py @@ -343,21 +343,3 @@ class MeasTypes: factor=0.001, ), } - - -SLEEPY_BINARY_SENSORS = { - "door", - "garage_door", - "lock", - "opening", - "presence", - "tamper", - "vibration", - "window", -} - - -SLEEPY_SENSORS = { - "count", - "mass", -} diff --git a/src/bthome_ble/parser.py b/src/bthome_ble/parser.py index a7e235d..9b56845 100644 --- a/src/bthome_ble/parser.py +++ b/src/bthome_ble/parser.py @@ -116,14 +116,14 @@ def __init__(self, bindkey: bytes | None = None) -> None: # Encryption to expect, based on flags in the UUID. self.encryption_scheme = EncryptionScheme.NONE - # If true, then we know the actual MAC of the device. + # If True, then we know the actual MAC of the device. # On macOS, we don't unless the device includes it in the advertisement # (CoreBluetooth uses UUID's generated by CoreBluetooth instead of the MAC) self.mac_known = sys.platform != "darwin" - # If true then we have used the provided encryption key to decrypt at least + # If True then we have used the provided encryption key to decrypt at least # one payload. - # If false then we have either not seen an encrypted payload, the key is wrong + # If False then we have either not seen an encrypted payload, the key is wrong # or encryption is not in use self.bindkey_verified = False @@ -136,6 +136,9 @@ def __init__(self, bindkey: bytes | None = None) -> None: # value with a new bindkey. self.last_service_info: BluetoothServiceInfo | None = None + # If this is True, the device is not sending advertisements in a regular interval + self.sleepy_device = False + def supported(self, data: BluetoothServiceInfo) -> bool: if not super().supported(data): return False @@ -255,6 +258,19 @@ def _parse_bthome_v2( else: self.encryption_scheme = EncryptionScheme.NONE + # If True, the first 6 bytes contain the mac address + mac_included = adv_info & (1 << 1) # bit 1 + if mac_included: + bthome_mac_reversed = data[1:7] + mac_readable = to_mac(bthome_mac_reversed[::-1]) + payload = data[7:] + else: + mac_readable = service_info.address + payload = data[1:] + + # If True, the device is not updating regularly + self.sleepy_device = bool(adv_info & (1 << 2)) # bit 2 + # Check BTHome version sw_version = (adv_info >> 5) & 7 # 3 bits (5-7) if sw_version == 2: @@ -298,15 +314,6 @@ def _parse_bthome_v2( self.set_title(f"{name} {identifier}") self.set_device_type(device_type) - mac_included = adv_info & (1 << 1) # bit 1 - if mac_included: - bthome_mac_reversed = data[1:7] - mac_readable = to_mac(bthome_mac_reversed[::-1]) - payload = data[7:] - else: - mac_readable = service_info.address - payload = data[1:] - if self.encryption_scheme == EncryptionScheme.BTHOME_BINDKEY: if len(mac_readable) != 17 and mac_readable[2] != ":": # On macOS, we get a UUID, which is useless for BTHome sensors diff --git a/tests/test_parser_v2.py b/tests/test_parser_v2.py index e6b09dd..7d98887 100644 --- a/tests/test_parser_v2.py +++ b/tests/test_parser_v2.py @@ -120,6 +120,36 @@ def test_encryption_no_key_needed(): assert not device.bindkey_verified +def test_sleepy_device(): + """Test that we can detect that a device that doesn't update regularly.""" + data_string = b"\x44\x04\x13\x8a\x01" + advertisement = bytes_to_service_info( + payload=data_string, + local_name="ATC_8D18B2", + address="A4:C1:38:8D:18:B2", + ) + + device = BTHomeBluetoothDeviceData() + + assert device.supported(advertisement) + assert device.sleepy_device + + +def test_no_sleepy_device(): + """Test that we can detect that a device that updates regularly.""" + data_string = b"\x40\x04\x13\x8a\x01" + advertisement = bytes_to_service_info( + payload=data_string, + local_name="ATC_8D18B2", + address="A4:C1:38:8D:18:B2", + ) + + device = BTHomeBluetoothDeviceData() + + assert device.supported(advertisement) + assert not device.sleepy_device + + def test_mac_as_name(): """ A sensor without a name gets its MAC address as name from BluetoothServiceInfo.