From 7954a9c61e32b8f127578045a62c91fe446d95e5 Mon Sep 17 00:00:00 2001 From: Anthony Wertz Date: Fri, 21 Apr 2023 10:39:42 -0400 Subject: [PATCH] Updated to conform to lint specs. --- _bleio/scan_entry.py | 74 +++++++++++++++----------- _bleio/uuid_.py | 124 +++++++++++++++++++++++++------------------ 2 files changed, 113 insertions(+), 85 deletions(-) diff --git a/_bleio/scan_entry.py b/_bleio/scan_entry.py index 789975c..2df4370 100644 --- a/_bleio/scan_entry.py +++ b/_bleio/scan_entry.py @@ -148,46 +148,56 @@ def _advertisement_fields(self) -> List[bytes]: bytes((data_type,)) + data for data_type, data in self._data_dict.items() ) + @staticmethod + def _manufacturer_data_from_bleak(manufacturer_data: Dict[int, bytes]) -> bytes: + # The manufacturer data value is a dictionary. + # Re-concatenate it into bytes + all_mfr_data = bytearray() + for mfr_id, mfr_data in manufacturer_data.items(): + all_mfr_data.extend(mfr_id.to_bytes(2, byteorder="little")) + all_mfr_data.extend(mfr_data) + return bytes(all_mfr_data) + + @staticmethod + def _uuids_from_bleak(uuids: List[str]) -> bytes: + uuids16 = bytearray() + uuids32 = bytearray() + uuids128 = bytearray() + for uuid in uuids: + bleio_uuid = UUID(uuid) + # If this is a Standard UUID in 128-bit form, convert it to a 16- or 32-bit UUID. + if bleio_uuid.is_standard_uuid: + if bleio_uuid.size == 16: + uuids16.extend(bleio_uuid.uuid128[12:14]) + elif bleio_uuid.size == 32: + uuids32.extend(bleio_uuid.uuid128[12:16]) + else: + raise RuntimeError("Unexpected UUID size") + else: + uuids128.extend(bleio_uuid.uuid128) + + fields = {} + if uuids16: + # Complete list of 16-bit UUIDs. + fields[0x03] = uuids16 + if uuids32: + # Complete list of 32-bit UUIDs. + fields[0x05] = uuids32 + if uuids128: + # Complete list of 128-bit UUIDs + fields[0x07] = uuids128 + return fields + @staticmethod def _data_dict_from_bleak( device: BLEDevice, advertisement_data: AdvertisementData ) -> DataDict: data_dict = {} if manufacturer_data := advertisement_data.manufacturer_data: - # The manufacturer data value is a dictionary. - # Re-concatenate it into bytes - all_mfr_data = bytearray() - for mfr_id, mfr_data in manufacturer_data.items(): - all_mfr_data.extend(mfr_id.to_bytes(2, byteorder="little")) - all_mfr_data.extend(mfr_data) - data_dict[0xFF] = all_mfr_data + data_dict[0xFF] = ScanEntry._manufacturer_data_from_bleak(manufacturer_data) if uuids := advertisement_data.service_uuids: - uuids16 = bytearray() - uuids32 = bytearray() - uuids128 = bytearray() - for uuid in uuids: - bleio_uuid = UUID(uuid) - # If this is a Standard UUID in 128-bit form, convert it to a 16- or 32-bit UUID. - if bleio_uuid.is_standard_uuid: - if bleio_uuid.size == 16: - uuids16.extend(bleio_uuid.uuid128[12:14]) - elif bleio_uuid.size == 32: - uuids32.extend(bleio_uuid.uuid128[12:16]) - else: - raise RuntimeError("Unexpected UUID size") - else: - uuids128.extend(bleio_uuid.uuid128) - - if uuids16: - # Complete list of 16-bit UUIDs. - data_dict[0x03] = uuids16 - if uuids32: - # Complete list of 32-bit UUIDs. - data_dict[0x05] = uuids32 - if uuids128: - # Complete list of 128-bit UUIDs - data_dict[0x07] = uuids128 + data_dict.update(ScanEntry._uuids_from_bleak(uuids)) name = advertisement_data.local_name or device.name if name and not ScanEntry._RE_IGNORABLE_NAME.fullmatch(name): diff --git a/_bleio/uuid_.py b/_bleio/uuid_.py index 6a11e4e..c41f237 100644 --- a/_bleio/uuid_.py +++ b/_bleio/uuid_.py @@ -44,63 +44,77 @@ class UUID: @staticmethod def standard_uuid128_from_uuid32(uuid32: int) -> bytes: """Return a 128-bit standard UUID from a 32-bit standard UUID.""" - if not 0 <= uuid32 < 2 ** 32: + if not 0 <= uuid32 < 2**32: raise ValueError("UUID integer value must be unsigned 32-bit") return _BASE_STANDARD_UUID[:-4] + uuid32.to_bytes(4, "little") + @staticmethod + def _init_from_str(uuid: str) -> tuple[bytes, int]: + if _UUID_RE.fullmatch(uuid): + # Pick the smallest standard size. + if _STANDARD_UUID_RE_16.fullmatch(uuid): + size = 16 + uuid16 = int(uuid[4:8], 16) + uuid128 = UUID.standard_uuid128_from_uuid32(uuid16) + return uuid128, size + + if _STANDARD_UUID_RE_32.fullmatch(uuid): + size = 32 + uuid32 = int(uuid[0:8], 16) + uuid128 = UUID.standard_uuid128_from_uuid32(uuid32) + return uuid128, size + + size = 128 + uuid = uuid.replace("-", "") + uuid128 = bytes(int(uuid[i : i + 2], 16) for i in range(30, -1, -2)) + return uuid128, size + + if _STANDARD_HEX_UUID_RE.fullmatch(uuid) and len(uuid) in (4, 8): + # Fall through and reprocess as an int. + uuid_int = int(uuid, 16) + size = len(uuid) * 4 # 4 bits per hex digit + uuid128 = UUID.standard_uuid128_from_uuid32(uuid_int) + return uuid128, size + + raise ValueError( + "UUID string not 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'," + "'xxxx', or 'xxxxxxxx', but is " + uuid + ) + + @staticmethod + def _init_from_int(uuid: int) -> tuple[bytes, int]: + if not 0 <= uuid <= 2**32: + raise ValueError("UUID integer value must be unsigned 16- or 32-bit") + if uuid <= 2**16: + size = 16 + if uuid <= 2**32: + size = 32 + uuid128 = UUID.standard_uuid128_from_uuid32(uuid) + return uuid128, size + + @staticmethod + def _init_from_buf(uuid: Buf) -> tuple[bytes, int]: + try: + uuid = memoryview(uuid) + except TypeError: + raise ValueError("UUID value is not str, int or byte buffer") from TypeError + if len(uuid) != 16: + raise ValueError("Byte buffer must be 16 bytes") + size = 128 + uuid128 = bytes(uuid) + return uuid128, size + def __init__(self, uuid: Union[int, Buf, str]): self.__bleak_uuid = None if isinstance(uuid, str): - if _UUID_RE.fullmatch(uuid): - # Pick the smallest standard size. - if _STANDARD_UUID_RE_16.fullmatch(uuid): - self._size = 16 - uuid16 = int(uuid[4:8], 16) - self._uuid128 = self.standard_uuid128_from_uuid32(uuid16) - elif _STANDARD_UUID_RE_32.fullmatch(uuid): - self._size = 32 - uuid32 = int(uuid[0:8], 16) - self._uuid128 = self.standard_uuid128_from_uuid32(uuid32) - else: - self._size = 128 - uuid = uuid.replace("-", "") - self._uuid128 = bytes( - int(uuid[i : i + 2], 16) for i in range(30, -1, -2) - ) - - elif _STANDARD_HEX_UUID_RE.fullmatch(uuid) and len(uuid) in (4, 8): - # Fall through and reprocess as an int. - uuid_int = int(uuid, 16) - self._size = len(uuid)*4 # 4 bits per hex digit - self._uuid128 = self.standard_uuid128_from_uuid32(uuid_int) - - else: - raise ValueError( - "UUID string not 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx', 'xxxx', or 'xxxxxxxx', but is " - + uuid - ) + self._uuid128, self._size = self._init_from_str(uuid) elif isinstance(uuid, int): - if not 0 <= uuid <= 2**32: - raise ValueError("UUID integer value must be unsigned 16- or 32-bit") - if uuid <= 2**16: - self._size = 16 - if uuid <= 2**32: - self._size = 32 - self._uuid128 = self.standard_uuid128_from_uuid32(uuid) + self._uuid128, self._size = self._init_from_int(uuid) else: - try: - uuid = memoryview(uuid) - except TypeError: - raise ValueError( - "UUID value is not str, int or byte buffer" - ) from TypeError - if len(uuid) != 16: - raise ValueError("Byte buffer must be 16 bytes") - self._size = 128 - self._uuid128 = bytes(uuid) + self._uuid128, self._size = self._init_from_buf(uuid) @classmethod def _from_bleak(cls, bleak_uuid: Any) -> "UUID": @@ -151,18 +165,22 @@ def pack_into(self, buffer, offset=0) -> None: def is_standard_uuid(self) -> bool: """True if this is a standard 16 or 32-bit UUID (xxxxxxxx-0000-1000-8000-00805F9B34FB) even if it's 128-bit.""" - return self.size == 16 or self.size == 32 or ( - self._uuid128[0:12] == _BASE_STANDARD_UUID[0:12] - and self._uuid128[14:] == _BASE_STANDARD_UUID[14:] + return ( + self.size == 16 + or self.size == 32 + or ( + self._uuid128[0:12] == _BASE_STANDARD_UUID[0:12] + and self._uuid128[14:] == _BASE_STANDARD_UUID[14:] + ) ) def __eq__(self, other: Any) -> bool: if isinstance(other, UUID): if self.size == 16 and other.size == 16: return self.uuid16 == other.uuid16 - elif self.size == 32 and other.size == 32: + if self.size == 32 and other.size == 32: return self.uuid32 == other.uuid32 - elif self.size == 128 and other.size == 128: + if self.size == 128 and other.size == 128: return self.uuid128 == other.uuid128 return False @@ -170,7 +188,7 @@ def __eq__(self, other: Any) -> bool: def __hash__(self): if self.size == 16: return hash(self.uuid16) - elif self.size == 32: + if self.size == 32: return hash(self.uuid32) return hash(self.uuid128) @@ -186,6 +204,6 @@ def __str__(self) -> str: def __repr__(self) -> str: if self.size == 16: return f"UUID({self.uuid16:#04x})" - elif self.size == 32: + if self.size == 32: return f"UUID({self.uuid32:#08x})" return f"UUID({self!s})"