Skip to content

Commit 40dc09e

Browse files
authored
Merge pull request #63 from dhalbert/fix-lazy-advertising-fields
prevent unwanted lazy advertising object instantiation
2 parents 14f9064 + 92f75d3 commit 40dc09e

File tree

3 files changed

+40
-33
lines changed

3 files changed

+40
-33
lines changed

adafruit_ble/advertising/__init__.py

Lines changed: 24 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -116,31 +116,24 @@ class AdvertisingFlags(AdvertisingDataField):
116116
def __init__(self, advertisement, advertising_data_type):
117117
self._advertisement = advertisement
118118
self._adt = advertising_data_type
119-
self.flags = None
119+
self.flags = 0
120120
if self._adt in self._advertisement.data_dict:
121121
self.flags = self._advertisement.data_dict[self._adt][0]
122-
elif self._advertisement.mutable:
123-
self.flags = 0b110 # Default to General discovery and LE Only
124-
else:
125-
self.flags = 0
126122

127123
def __len__(self):
128124
return 1
129125

130126
def __bytes__(self):
131-
encoded = bytearray(1)
132-
encoded[0] = self.flags
133-
return encoded
127+
return bytes([self.flags])
134128

135129
def __str__(self):
136-
parts = ["<AdvertisingFlags"]
130+
parts = []
137131
for attr in dir(self.__class__):
138132
attribute_instance = getattr(self.__class__, attr)
139133
if issubclass(attribute_instance.__class__, AdvertisingFlag):
140134
if getattr(self, attr):
141135
parts.append(attr)
142-
parts.append(">")
143-
return " ".join(parts)
136+
return "<AdvertisingFlags {} >".format(" ".join(parts))
144137

145138
class String(AdvertisingDataField):
146139
"""UTF-8 encoded string in an Advertisement.
@@ -172,7 +165,7 @@ def __set__(self, obj, value):
172165
obj.data_dict[self._adt] = struct.pack(self._format, value)
173166

174167

175-
class LazyField(AdvertisingDataField):
168+
class LazyObjectField(AdvertisingDataField):
176169
"""Non-data descriptor useful for lazily binding a complex object to an advertisement object."""
177170
def __init__(self, cls, attribute_name, *, advertising_data_type, **kwargs):
178171
self._cls = cls
@@ -184,18 +177,24 @@ def __get__(self, obj, cls):
184177
# Return None if our object is immutable and the data is not present.
185178
if not obj.mutable and self._adt not in obj.data_dict:
186179
return None
187-
bound_class = self._cls(obj, advertising_data_type=self._adt, **self._kwargs)
188-
setattr(obj, self._attribute_name, bound_class)
189-
obj.data_dict[self._adt] = bound_class
190-
return bound_class
180+
# Instantiate the object.
181+
bound_obj = self._cls(obj, advertising_data_type=self._adt, **self._kwargs)
182+
setattr(obj, self._attribute_name, bound_obj)
183+
obj.data_dict[self._adt] = bound_obj
184+
return bound_obj
185+
186+
@property
187+
def advertising_data_type(self):
188+
"""Return the data type value used to indicate this field."""
189+
return self._adt
191190

192191
# TODO: Add __set_name__ support to CircuitPython so that we automatically tell the descriptor
193192
# instance the attribute name it has and the class it is on.
194193

195194
class Advertisement:
196195
"""Core Advertisement type"""
197196
prefix = b"\x00" # This is an empty prefix and will match everything.
198-
flags = LazyField(AdvertisingFlags, "flags", advertising_data_type=0x01)
197+
flags = LazyObjectField(AdvertisingFlags, "flags", advertising_data_type=0x01)
199198
short_name = String(advertising_data_type=0x08)
200199
"""Short local device name (shortened to fit)."""
201200
complete_name = String(advertising_data_type=0x09)
@@ -263,15 +262,19 @@ def __bytes__(self):
263262
return encode_data(self.data_dict)
264263

265264
def __str__(self):
266-
parts = ["<" + self.__class__.__name__]
265+
parts = []
267266
for attr in dir(self.__class__):
268267
attribute_instance = getattr(self.__class__, attr)
269268
if issubclass(attribute_instance.__class__, AdvertisingDataField):
269+
if (issubclass(attribute_instance.__class__, LazyObjectField) and
270+
not attribute_instance.advertising_data_type in self.data_dict):
271+
# Skip uninstantiated lazy objects; if we get
272+
# their value, they will be be instantiated.
273+
continue
270274
value = getattr(self, attr)
271275
if value is not None:
272-
parts.append(attr + "=" + str(value))
273-
parts.append(">")
274-
return " ".join(parts)
276+
parts.append("{}={}".format(attr, str(value)))
277+
return "<{} {} >".format(self.__class__.__name__, " ".join(parts))
275278

276279
def __len__(self):
277280
return compute_length(self.data_dict)

adafruit_ble/advertising/adafruit.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
import struct
3535
from micropython import const
3636

37-
from . import Advertisement, LazyField
37+
from . import Advertisement, LazyObjectField
3838
from .standard import ManufacturerData, ManufacturerDataField
3939

4040
__version__ = "0.0.0-auto.0"
@@ -55,11 +55,11 @@ class AdafruitColor(Advertisement):
5555
_ADAFRUIT_COMPANY_ID,
5656
struct.calcsize("<HI"),
5757
_COLOR_DATA_ID)
58-
manufacturer_data = LazyField(ManufacturerData,
59-
"manufacturer_data",
60-
advertising_data_type=_MANUFACTURING_DATA_ADT,
61-
company_id=_ADAFRUIT_COMPANY_ID,
62-
key_encoding="<H")
58+
manufacturer_data = LazyObjectField(ManufacturerData,
59+
"manufacturer_data",
60+
advertising_data_type=_MANUFACTURING_DATA_ADT,
61+
company_id=_ADAFRUIT_COMPANY_ID,
62+
key_encoding="<H")
6363
color = ManufacturerDataField(_COLOR_DATA_ID, "<I")
6464
"""Color to broadcast as RGB integer."""
6565

@@ -71,9 +71,9 @@ class AdafruitRadio(Advertisement):
7171
_MANUFACTURING_DATA_ADT,
7272
_ADAFRUIT_COMPANY_ID,
7373
_RADIO_DATA_ID)
74-
manufacturer_data = LazyField(ManufacturerData,
75-
"manufacturer_data",
76-
advertising_data_type=_MANUFACTURING_DATA_ADT,
77-
company_id=_ADAFRUIT_COMPANY_ID,
78-
key_encoding="<H")
74+
manufacturer_data = LazyObjectField(ManufacturerData,
75+
"manufacturer_data",
76+
advertising_data_type=_MANUFACTURING_DATA_ADT,
77+
company_id=_ADAFRUIT_COMPANY_ID,
78+
key_encoding="<H")
7979
msg = ManufacturerDataField(_RADIO_DATA_ID, "<248s") # 255 byte ads

adafruit_ble/advertising/standard.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,8 @@ def __init__(self, *services):
154154
if services:
155155
self.services.extend(services)
156156
self.connectable = True
157+
self.flags.general_discovery = True
158+
self.flags.le_only = True
157159

158160
@classmethod
159161
def matches(cls, entry):
@@ -171,9 +173,11 @@ def __init__(self, *services):
171173
super().__init__()
172174
self.solicited_services.extend(services)
173175
self.connectable = True
176+
self.flags.general_discovery = True
177+
self.flags.le_only = True
174178

175179

176-
class ManufacturerData:
180+
class ManufacturerData(AdvertisingDataField):
177181
"""Encapsulates manufacturer specific keyed data bytes. The manufacturer is identified by the
178182
company_id and the data is structured like an advertisement with a configurable key
179183
format."""

0 commit comments

Comments
 (0)