Skip to content

Commit fb91fcb

Browse files
authored
Merge pull request #66 from tannewt/multivalue_mfg
Add support for multiple values in a manufacturer data entry
2 parents 7adbb88 + 5b00d8f commit fb91fcb

File tree

2 files changed

+48
-8
lines changed

2 files changed

+48
-8
lines changed

adafruit_ble/__init__.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -164,12 +164,16 @@ def start_advertising(self, advertisement, scan_response=None, interval=0.1):
164164
`BLERadio.name` and `BLERadio.tx_power`.
165165
:param float interval: advertising interval, in seconds
166166
"""
167-
if not scan_response:
167+
advertisement_bytes = bytes(advertisement)
168+
scan_response_bytes = b""
169+
if not scan_response and len(advertisement_bytes) <= 31:
168170
scan_response = Advertisement()
169171
scan_response.complete_name = self.name
170172
scan_response.tx_power = self.tx_power
171-
self._adapter.start_advertising(bytes(advertisement),
172-
scan_response=bytes(scan_response),
173+
if scan_response:
174+
scan_response_bytes = bytes(scan_response)
175+
self._adapter.start_advertising(advertisement_bytes,
176+
scan_response=scan_response_bytes,
173177
connectable=advertisement.connectable,
174178
interval=interval)
175179

adafruit_ble/advertising/standard.py

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -211,18 +211,54 @@ def __str__(self):
211211
return "<ManufacturerData company_id={:04x} data={} >".format(self.company_id, hex_data)
212212

213213
class ManufacturerDataField:
214-
"""A single piece of data within the manufacturer specific data."""
215-
def __init__(self, key, key_format):
214+
"""A single piece of data within the manufacturer specific data. The format can be repeated."""
215+
def __init__(self, key, value_format, field_names=None):
216216
self._key = key
217-
self._format = key_format
217+
self._format = value_format
218+
# TODO: Support format strings that use numbers to repeat a given type. For now, we strip
219+
# numbers because Radio specifies string length with it.
220+
self.element_count = len(value_format.strip("><!=@0123456789").replace("x", ""))
221+
if self.element_count > 1 and (not field_names or len(field_names) != self.element_count):
222+
raise ValueError("Provide field_names when multiple values are in the format")
223+
self._entry_length = struct.calcsize(value_format)
224+
self.field_names = field_names
218225

219226
def __get__(self, obj, cls):
220-
return struct.unpack_from(self._format, obj.manufacturer_data.data[self._key])[0]
227+
if self._key not in obj.manufacturer_data.data:
228+
return None
229+
packed = obj.manufacturer_data.data[self._key]
230+
if self._entry_length == len(packed):
231+
unpacked = struct.unpack_from(self._format, packed)
232+
if self.element_count == 1:
233+
unpacked = unpacked[0]
234+
return unpacked
235+
if len(packed) % self._entry_length != 0:
236+
raise RuntimeError("Invalid data length")
237+
entry_count = len(packed) // self._entry_length
238+
unpacked = [None] * entry_count
239+
for i in range(entry_count):
240+
offset = i * self._entry_length
241+
unpacked[i] = struct.unpack_from(self._format, packed, offset=offset)
242+
if self.element_count == 1:
243+
unpacked[i] = unpacked[i][0]
244+
return tuple(unpacked)
221245

222246
def __set__(self, obj, value):
223247
if not obj.mutable:
224248
raise AttributeError()
225-
obj.manufacturer_data.data[self._key] = struct.pack(self._format, value)
249+
if isinstance(value, tuple) and (self.element_count == 1 or isinstance(value[0], tuple)):
250+
packed = bytearray(self._entry_length * len(value))
251+
for i, entry in enumerate(value):
252+
offset = i * self._entry_length
253+
if self.element_count > 1:
254+
struct.pack_into(self._format, packed, offset, *entry)
255+
else:
256+
struct.pack_into(self._format, packed, offset, entry)
257+
obj.manufacturer_data.data[self._key] = bytes(packed)
258+
elif self.element_count == 1:
259+
obj.manufacturer_data.data[self._key] = struct.pack(self._format, value)
260+
else:
261+
obj.manufacturer_data.data[self._key] = struct.pack(self._format, *value)
226262

227263
# TODO: Handle service data.
228264

0 commit comments

Comments
 (0)