Skip to content

Commit 73a8400

Browse files
committed
wip
1 parent 5d58457 commit 73a8400

File tree

6 files changed

+71
-32
lines changed

6 files changed

+71
-32
lines changed

adafruit_ble/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,7 @@ def start_scan(
231231
"""
232232
if not advertisement_types:
233233
advertisement_types = (Advertisement,)
234-
prefixes = b"".join(adv.prefix for adv in advertisement_types)
234+
prefixes = b"".join(adv.get_prefix_bytes() for adv in advertisement_types)
235235
for entry in self._adapter.start_scan(
236236
prefixes=prefixes,
237237
buffer_size=buffer_size,

adafruit_ble/advertising/__init__.py

Lines changed: 40 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -214,9 +214,17 @@ def advertising_data_type(self):
214214

215215

216216
class Advertisement:
217-
"""Core Advertisement type"""
217+
"""Core Advertisement type.
218+
219+
The class attribute ``match_prefixes``, if not ``None``, is a tuple of
220+
bytestring prefixes to match against the multiple data structures in the advertisement.
221+
"""
222+
223+
match_prefixes = ()
224+
"""For Advertisement, `matches` will always return True. Subclasses may override this value."""
225+
# cached bytes of merged prefixes.
226+
_prefix_bytes = None
218227

219-
prefix = b"\x00" # This is an empty prefix and will match everything.
220228
flags = LazyObjectField(AdvertisingFlags, "flags", advertising_data_type=0x01)
221229
short_name = String(advertising_data_type=0x08)
222230
"""Short local device name (shortened to fit)."""
@@ -257,7 +265,11 @@ def from_entry(cls, entry):
257265
"""Create an Advertisement based on the given ScanEntry. This is done automatically by
258266
`BLERadio` for all scan results."""
259267
self = cls()
260-
self.data_dict = decode_data(entry.advertisement_bytes)
268+
# If data_dict is available, use it directly. Otherwise decode the bytestring.
269+
if hasattr(entry, "data_dict"):
270+
self.data_dict = entry.data_dict
271+
else:
272+
self.data_dict = decode_data(entry.advertisement_bytes)
261273
self.address = entry.address
262274
self._rssi = entry.rssi # pylint: disable=protected-access
263275
self.connectable = entry.connectable
@@ -272,13 +284,32 @@ def rssi(self):
272284
return self._rssi
273285

274286
@classmethod
275-
def matches(cls, entry):
276-
"""Returns true if the given `_bleio.ScanEntry` matches all portions of the Advertisement
277-
type's prefix."""
278-
if not hasattr(cls, "prefix"):
279-
return True
287+
def get_prefix_bytes(cls):
288+
"""Return a merged version of match_prefixes as a single bytes object,
289+
with length headers.
290+
"""
291+
# Do merge once and memoize it.
292+
if cls._prefix_bytes is None:
293+
cls._prefix_bytes = (
294+
b""
295+
if cls.match_prefixes is None
296+
else b"".join(
297+
len(prefix).to_bytes(1, "little") + prefix
298+
for prefix in cls.match_prefixes
299+
)
300+
)
301+
302+
return cls._prefix_bytes
280303

281-
return entry.matches(cls.prefix)
304+
@classmethod
305+
def matches(cls, entry, all_=True):
306+
"""Returns ``True`` if the given `_bleio.ScanEntry` advertisement fields
307+
match any or all of the given prefixes in the `match_prefixes` tuple attribute.
308+
If `all_` is ``True``, all the prefixes must match. If ``all_`` is ``False``,
309+
returns ``True`` if at least one of the prefixes match.
310+
"""
311+
# Returns True if cls.get_prefix_bytes() is empty.
312+
return entry.matches(cls.get_prefix_bytes(), all=all_)
282313

283314
def __bytes__(self):
284315
"""The raw packet bytes."""

adafruit_ble/advertising/adafruit.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -48,14 +48,15 @@
4848
class AdafruitColor(Advertisement):
4949
"""Broadcast a single RGB color."""
5050

51-
# This prefix matches all
52-
prefix = struct.pack(
53-
"<BBHBH",
54-
0x6,
55-
_MANUFACTURING_DATA_ADT,
56-
_ADAFRUIT_COMPANY_ID,
57-
struct.calcsize("<HI"),
58-
_COLOR_DATA_ID,
51+
# This single prefix matches all color advertisements.
52+
match_prefixes = (
53+
struct.pack(
54+
"<BHBH",
55+
_MANUFACTURING_DATA_ADT,
56+
_ADAFRUIT_COMPANY_ID,
57+
struct.calcsize("<HI"),
58+
_COLOR_DATA_ID,
59+
),
5960
)
6061
manufacturer_data = LazyObjectField(
6162
ManufacturerData,

adafruit_ble/advertising/apple.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# class iBeacon(Advertisement):
2-
# prefix = b"\xff\x00\x4c\x02" # Apple manufacturer data with subtype 2
2+
# # Apple manufacturer data with subtype 2
3+
# match_prefixes = (b"\xff\x00\x4c\x02",)
34
#
45
# proximity_uuid =
56
# major =

adafruit_ble/advertising/standard.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ def __str__(self):
135135
data.append(str(service_uuid))
136136
for service_uuid in self._vendor_services:
137137
data.append(str(service_uuid))
138-
return " ".join(data)
138+
return "<BoundServiceList: {}>".format(", ".join(data))
139139

140140

141141
class ServiceList(AdvertisingDataField):
@@ -156,7 +156,7 @@ def _present(self, obj):
156156

157157
def __get__(self, obj, cls):
158158
if not self._present(obj) and not obj.mutable:
159-
return None
159+
return ()
160160
if not hasattr(obj, "adv_service_lists"):
161161
obj.adv_service_lists = {}
162162
first_adt = self.standard_services[0]
@@ -168,8 +168,8 @@ def __get__(self, obj, cls):
168168
class ProvideServicesAdvertisement(Advertisement):
169169
"""Advertise what services that the device makes available upon connection."""
170170

171-
# This is four prefixes, one for each ADT that can carry service UUIDs.
172-
prefix = b"\x01\x02\x01\x03\x01\x06\x01\x07"
171+
# Prefixes that match each ADT that can carry service UUIDs.
172+
match_prefixes = (b"\x02", b"\x03", b"\x06", b"\x07")
173173
services = ServiceList(standard_services=[0x02, 0x03], vendor_services=[0x06, 0x07])
174174
"""List of services the device can provide."""
175175

@@ -182,15 +182,15 @@ def __init__(self, *services):
182182
self.flags.le_only = True
183183

184184
@classmethod
185-
def matches(cls, entry):
186-
return entry.matches(cls.prefix, all=False)
185+
def matches(cls, entry, all_=False):
186+
return super().matches(entry, all_=all_)
187187

188188

189189
class SolicitServicesAdvertisement(Advertisement):
190190
"""Advertise what services the device would like to use over a connection."""
191191

192-
# This is two prefixes, one for each ADT that can carry solicited service UUIDs.
193-
prefix = b"\x01\x14\x01\x15"
192+
# Prefixes that match each ADT that can carry solicited service UUIDs.
193+
match_prefixes = (b"\x14", b"\x15")
194194

195195
solicited_services = ServiceList(standard_services=[0x14], vendor_services=[0x15])
196196
"""List of services the device would like to use."""

adafruit_ble/services/standard/device_info.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@
2929
import binascii
3030
import os
3131
import sys
32-
import microcontroller
3332

3433
from .. import Service
3534
from ...uuid import StandardUUID
@@ -65,11 +64,18 @@ def __init__(
6564
if model_number is None:
6665
model_number = sys.platform
6766
if serial_number is None:
68-
serial_number = binascii.hexlify(
69-
microcontroller.cpu.uid # pylint: disable=no-member
70-
).decode("utf-8")
67+
try:
68+
import microcontroller
69+
serial_number = binascii.hexlify(
70+
microcontroller.cpu.uid # pylint: disable=no-member
71+
).decode("utf-8")
72+
except ImportError:
73+
pass
7174
if firmware_revision is None:
72-
firmware_revision = os.uname().version
75+
try:
76+
firmware_revision = os.uname().version
77+
except AttributeError:
78+
pass
7379
super().__init__(
7480
manufacturer=manufacturer,
7581
software_revision=software_revision,

0 commit comments

Comments
 (0)