Skip to content

Commit 5d95dc6

Browse files
committed
Support ServiceData
1 parent abda3d8 commit 5d95dc6

File tree

1 file changed

+65
-5
lines changed

1 file changed

+65
-5
lines changed

adafruit_ble/advertising/standard.py

Lines changed: 65 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -260,10 +260,70 @@ def __set__(self, obj, value):
260260
else:
261261
obj.manufacturer_data.data[self._key] = struct.pack(self._format, *value)
262262

263-
# TODO: Handle service data.
263+
class ServiceData(AdvertisingDataField):
264+
"""Encapsulates service data. It is read as a memoryview which can be manipulated or set as a
265+
bytearray to change the size."""
266+
def __init__(self, service):
267+
if isinstance(service.uuid, StandardUUID):
268+
self._adt = 0x16
269+
elif isinstance(service.uuid, VendorUUID):
270+
self._adt = 0x21
271+
self._prefix = bytes(service.uuid)
272+
273+
def __get__(self, obj, cls):
274+
# If not present at all and mutable, then we init it, otherwise None.
275+
if self._adt not in obj.data_dict:
276+
if obj.mutable:
277+
obj.data_dict[self._adt] = bytearray(self._prefix)
278+
else:
279+
return None
280+
281+
all_service_data = obj.data_dict[self._adt]
282+
# Handle a list of existing data. This doesn't support multiple service data ADTs for the
283+
# same service.
284+
if isinstance(all_service_data, list):
285+
for i, service_data in enumerate(all_service_data):
286+
if service_data.startswith(self._prefix):
287+
if not isinstance(service_data, bytearray):
288+
service_data = bytearray(service_data)
289+
all_service_data[i] = service_data
290+
return memoryview(service_data)[len(self._prefix):]
291+
if obj.mutable:
292+
all_service_data.append(bytearray(self._prefix))
293+
return memoryview(service_data)[len(self._prefix):]
294+
# Existing data is a single set of bytes.
295+
elif isinstance(all_service_data, (bytes, bytearray)):
296+
service_data = all_service_data
297+
if not bytes(service_data).startswith(self._prefix):
298+
if not obj.mutable:
299+
return None
300+
# Upgrade the value to a list.
301+
service_data = bytearray(self._prefix)
302+
obj.data_dict[self._adt] = [service_data, service_data]
303+
if not isinstance(service_data, bytearray):
304+
service_data = bytearray(service_data)
305+
obj.data_dict[self._adt] = service_data
306+
return memoryview(service_data)[len(self._prefix):]
307+
308+
return None
264309

265-
# SERVICE_DATA_128BIT_UUID = 0x21
266-
# """Service data with 128 bit UUID."""
267310

268-
# SERVICE_DATA_16_BIT_UUID = 0x16
269-
# """Service data with 16 bit UUID."""
311+
def __set__(self, obj, value):
312+
if not obj.mutable:
313+
raise RuntimeError("Advertisement immutable")
314+
if not isinstance(value, bytearray):
315+
raise TypeError("Value must be bytearray")
316+
full_value = bytearray(self._prefix) + value
317+
if self._adt not in obj.data_dict:
318+
obj.data_dict[self._adt] = full_value
319+
return
320+
321+
all_service_data = obj.data_dict[self._adt]
322+
if isinstance(all_service_data, list):
323+
for i, service_data in enumerate(all_service_data):
324+
if service_data.startswith(self._prefix):
325+
all_service_data[i] = full_value
326+
return
327+
all_service_data.append(full_value)
328+
elif isinstance(all_service_data, (bytes, bytearray)):
329+
obj.data_dict[self._adt] = full_value

0 commit comments

Comments
 (0)