From abda3d8e389c63e186f64647c6cb0992a0156e86 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Thu, 13 Feb 2020 16:44:37 -0800 Subject: [PATCH 1/4] Support bytes() on a UUID --- adafruit_ble/uuid/__init__.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/adafruit_ble/uuid/__init__.py b/adafruit_ble/uuid/__init__.py index e91dc71..b7cd87f 100644 --- a/adafruit_ble/uuid/__init__.py +++ b/adafruit_ble/uuid/__init__.py @@ -49,6 +49,11 @@ def __eq__(self, other): def __str__(self): return str(self.bleio_uuid) + def __bytes__(self): + b = bytearray(self.bleio_uuid.size // 8) + self.bleio_uuid.pack_into(b) + return bytes(b) + def pack_into(self, buffer, offset=0): """Packs the UUID into the buffer at the given offset.""" self.bleio_uuid.pack_into(buffer, offset=offset) From 5d95dc66ebc4f07057ee899550ec1273bd20c38a Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Thu, 13 Feb 2020 16:44:47 -0800 Subject: [PATCH 2/4] Support ServiceData --- adafruit_ble/advertising/standard.py | 70 ++++++++++++++++++++++++++-- 1 file changed, 65 insertions(+), 5 deletions(-) diff --git a/adafruit_ble/advertising/standard.py b/adafruit_ble/advertising/standard.py index 01b1e5b..7418a6d 100644 --- a/adafruit_ble/advertising/standard.py +++ b/adafruit_ble/advertising/standard.py @@ -260,10 +260,70 @@ def __set__(self, obj, value): else: obj.manufacturer_data.data[self._key] = struct.pack(self._format, *value) -# TODO: Handle service data. +class ServiceData(AdvertisingDataField): + """Encapsulates service data. It is read as a memoryview which can be manipulated or set as a + bytearray to change the size.""" + def __init__(self, service): + if isinstance(service.uuid, StandardUUID): + self._adt = 0x16 + elif isinstance(service.uuid, VendorUUID): + self._adt = 0x21 + self._prefix = bytes(service.uuid) + + def __get__(self, obj, cls): + # If not present at all and mutable, then we init it, otherwise None. + if self._adt not in obj.data_dict: + if obj.mutable: + obj.data_dict[self._adt] = bytearray(self._prefix) + else: + return None + + all_service_data = obj.data_dict[self._adt] + # Handle a list of existing data. This doesn't support multiple service data ADTs for the + # same service. + if isinstance(all_service_data, list): + for i, service_data in enumerate(all_service_data): + if service_data.startswith(self._prefix): + if not isinstance(service_data, bytearray): + service_data = bytearray(service_data) + all_service_data[i] = service_data + return memoryview(service_data)[len(self._prefix):] + if obj.mutable: + all_service_data.append(bytearray(self._prefix)) + return memoryview(service_data)[len(self._prefix):] + # Existing data is a single set of bytes. + elif isinstance(all_service_data, (bytes, bytearray)): + service_data = all_service_data + if not bytes(service_data).startswith(self._prefix): + if not obj.mutable: + return None + # Upgrade the value to a list. + service_data = bytearray(self._prefix) + obj.data_dict[self._adt] = [service_data, service_data] + if not isinstance(service_data, bytearray): + service_data = bytearray(service_data) + obj.data_dict[self._adt] = service_data + return memoryview(service_data)[len(self._prefix):] + + return None -# SERVICE_DATA_128BIT_UUID = 0x21 -# """Service data with 128 bit UUID.""" -# SERVICE_DATA_16_BIT_UUID = 0x16 -# """Service data with 16 bit UUID.""" + def __set__(self, obj, value): + if not obj.mutable: + raise RuntimeError("Advertisement immutable") + if not isinstance(value, bytearray): + raise TypeError("Value must be bytearray") + full_value = bytearray(self._prefix) + value + if self._adt not in obj.data_dict: + obj.data_dict[self._adt] = full_value + return + + all_service_data = obj.data_dict[self._adt] + if isinstance(all_service_data, list): + for i, service_data in enumerate(all_service_data): + if service_data.startswith(self._prefix): + all_service_data[i] = full_value + return + all_service_data.append(full_value) + elif isinstance(all_service_data, (bytes, bytearray)): + obj.data_dict[self._adt] = full_value From a741c93797d5d4232789633e0ba3c8fa8ca20a6d Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Thu, 13 Feb 2020 16:51:34 -0800 Subject: [PATCH 3/4] Fix error found by lint when adding to an existing list of service data. --- adafruit_ble/advertising/standard.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/adafruit_ble/advertising/standard.py b/adafruit_ble/advertising/standard.py index 7418a6d..62dbe5b 100644 --- a/adafruit_ble/advertising/standard.py +++ b/adafruit_ble/advertising/standard.py @@ -289,7 +289,8 @@ def __get__(self, obj, cls): all_service_data[i] = service_data return memoryview(service_data)[len(self._prefix):] if obj.mutable: - all_service_data.append(bytearray(self._prefix)) + service_data = bytearray(self._prefix) + all_service_data.append(service_data) return memoryview(service_data)[len(self._prefix):] # Existing data is a single set of bytes. elif isinstance(all_service_data, (bytes, bytearray)): From 9b5b53392f23bd577aa597e99efaf163588e518f Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Fri, 14 Feb 2020 10:13:31 -0800 Subject: [PATCH 4/4] Only convert 16-bit UUIDs to bytes. Reuse the 128-bit value. --- adafruit_ble/uuid/__init__.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/adafruit_ble/uuid/__init__.py b/adafruit_ble/uuid/__init__.py index b7cd87f..f1f5c72 100644 --- a/adafruit_ble/uuid/__init__.py +++ b/adafruit_ble/uuid/__init__.py @@ -50,7 +50,9 @@ def __str__(self): return str(self.bleio_uuid) def __bytes__(self): - b = bytearray(self.bleio_uuid.size // 8) + if self.bleio_uuid.size == 128: + return self.bleio_uuid.uuid128 + b = bytearray(2) self.bleio_uuid.pack_into(b) return bytes(b)