Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
prevent unwanted lazy advertising object instantiation
  • Loading branch information
dhalbert committed Jan 17, 2020
commit 948b764fca37e5cff19bfae37714f2694e13b4d7
47 changes: 25 additions & 22 deletions adafruit_ble/advertising/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,31 +116,24 @@ class AdvertisingFlags(AdvertisingDataField):
def __init__(self, advertisement, advertising_data_type):
self._advertisement = advertisement
self._adt = advertising_data_type
self.flags = None
self.flags = 0
if self._adt in self._advertisement.data_dict:
self.flags = self._advertisement.data_dict[self._adt][0]
elif self._advertisement.mutable:
self.flags = 0b110 # Default to General discovery and LE Only
else:
self.flags = 0

def __len__(self):
return 1

def __bytes__(self):
encoded = bytearray(1)
encoded[0] = self.flags
return encoded
return bytes([self.flags])

def __str__(self):
parts = ["<AdvertisingFlags"]
parts = []
for attr in dir(self.__class__):
attribute_instance = getattr(self.__class__, attr)
if issubclass(attribute_instance.__class__, AdvertisingFlag):
if getattr(self, attr):
parts.append(attr)
parts.append(">")
return " ".join(parts)
return "<AdvertisingFlags {} >".format(" ".join(parts))

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


class LazyField(AdvertisingDataField):
class LazyObjectField(AdvertisingDataField):
"""Non-data descriptor useful for lazily binding a complex object to an advertisement object."""
def __init__(self, cls, attribute_name, *, advertising_data_type, **kwargs):
self._cls = cls
Expand All @@ -184,18 +177,24 @@ def __get__(self, obj, cls):
# Return None if our object is immutable and the data is not present.
if not obj.mutable and self._adt not in obj.data_dict:
return None
bound_class = self._cls(obj, advertising_data_type=self._adt, **self._kwargs)
setattr(obj, self._attribute_name, bound_class)
obj.data_dict[self._adt] = bound_class
return bound_class
# Instantiate the object.
bound_obj = self._cls(obj, advertising_data_type=self._adt, **self._kwargs)
setattr(obj, self._attribute_name, bound_obj)
obj.data_dict[self._adt] = bound_obj
return bound_obj

@property
def advertising_data_type(self):
"""Return the data type value used to indicate this field."""
return self._adt

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

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

def __str__(self):
parts = ["<" + self.__class__.__name__]
parts = []
for attr in dir(self.__class__):
attribute_instance = getattr(self.__class__, attr)
if issubclass(attribute_instance.__class__, AdvertisingDataField):
if (issubclass(attribute_instance.__class__, AdvertisingDataField)):
if (issubclass(attribute_instance.__class__, LazyObjectField) and
not attribute_instance.advertising_data_type in self.data_dict):
# Skip uninstantiated lazy objects; if we get
# their value, they will be be instantiated.
continue
value = getattr(self, attr)
if value is not None:
parts.append(attr + "=" + str(value))
parts.append(">")
return " ".join(parts)
parts.append("{}={}".format(attr, str(value)))
return "<{} {} >".format(self.__class__.__name__, " ".join(parts))

def __len__(self):
return compute_length(self.data_dict)
Expand Down
20 changes: 10 additions & 10 deletions adafruit_ble/advertising/adafruit.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,11 @@ class AdafruitColor(Advertisement):
_ADAFRUIT_COMPANY_ID,
struct.calcsize("<HI"),
_COLOR_DATA_ID)
manufacturer_data = LazyField(ManufacturerData,
"manufacturer_data",
advertising_data_type=_MANUFACTURING_DATA_ADT,
company_id=_ADAFRUIT_COMPANY_ID,
key_encoding="<H")
manufacturer_data = LazyObjectField(ManufacturerData,
"manufacturer_data",
advertising_data_type=_MANUFACTURING_DATA_ADT,
company_id=_ADAFRUIT_COMPANY_ID,
key_encoding="<H")
color = ManufacturerDataField(_COLOR_DATA_ID, "<I")
"""Color to broadcast as RGB integer."""

Expand All @@ -71,9 +71,9 @@ class AdafruitRadio(Advertisement):
_MANUFACTURING_DATA_ADT,
_ADAFRUIT_COMPANY_ID,
_RADIO_DATA_ID)
manufacturer_data = LazyField(ManufacturerData,
"manufacturer_data",
advertising_data_type=_MANUFACTURING_DATA_ADT,
company_id=_ADAFRUIT_COMPANY_ID,
key_encoding="<H")
manufacturer_data = LazyObjectField(ManufacturerData,
"manufacturer_data",
advertising_data_type=_MANUFACTURING_DATA_ADT,
company_id=_ADAFRUIT_COMPANY_ID,
key_encoding="<H")
msg = ManufacturerDataField(_RADIO_DATA_ID, "<248s") # 255 byte ads
6 changes: 5 additions & 1 deletion adafruit_ble/advertising/standard.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,8 @@ def __init__(self, *services):
if services:
self.services.extend(services)
self.connectable = True
self.flags.general_discovery = True
self.flags.le_only = True

@classmethod
def matches(cls, entry):
Expand All @@ -171,9 +173,11 @@ def __init__(self, *services):
super().__init__()
self.solicited_services.extend(services)
self.connectable = True
self.flags.general_discovery = True
self.flags.le_only = True


class ManufacturerData:
class ManufacturerData(AdvertisingDataField):
"""Encapsulates manufacturer specific keyed data bytes. The manufacturer is identified by the
company_id and the data is structured like an advertisement with a configurable key
format."""
Expand Down