diff --git a/adafruit_ble/advertising/standard.py b/adafruit_ble/advertising/standard.py index 01b1e5b..62dbe5b 100644 --- a/adafruit_ble/advertising/standard.py +++ b/adafruit_ble/advertising/standard.py @@ -260,10 +260,71 @@ 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: + 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)): + 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 diff --git a/adafruit_ble/uuid/__init__.py b/adafruit_ble/uuid/__init__.py index e91dc71..f1f5c72 100644 --- a/adafruit_ble/uuid/__init__.py +++ b/adafruit_ble/uuid/__init__.py @@ -49,6 +49,13 @@ def __eq__(self, other): def __str__(self): return str(self.bleio_uuid) + def __bytes__(self): + if self.bleio_uuid.size == 128: + return self.bleio_uuid.uuid128 + b = bytearray(2) + 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)