From dcf76c7bffd809c88d7a631adba858dccdcc2289 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Tue, 12 Nov 2019 20:54:51 -0500 Subject: [PATCH 1/4] doc and cleanup pass --- README.rst | 6 +- adafruit_ble/__init__.py | 117 ++++++++++++++----- adafruit_ble/advertising/__init__.py | 10 +- adafruit_ble/advertising/standard.py | 13 ++- adafruit_ble/attributes/__init__.py | 43 +++++++ adafruit_ble/characteristics/__init__.py | 130 +++++++++++++++------ adafruit_ble/characteristics/float.py | 14 ++- adafruit_ble/characteristics/int.py | 52 +++++++-- adafruit_ble/characteristics/stream.py | 25 ++-- adafruit_ble/characteristics/string.py | 33 +++--- adafruit_ble/services/__init__.py | 1 + adafruit_ble/services/circuitpython.py | 8 +- adafruit_ble/services/midi.py | 10 +- adafruit_ble/services/nordic.py | 16 +-- adafruit_ble/services/standard/hid.py | 61 +++++----- adafruit_ble/services/standard/standard.py | 4 +- adafruit_ble/uuid/__init__.py | 2 +- docs/bleio_mock.py | 1 + 18 files changed, 366 insertions(+), 180 deletions(-) create mode 100644 adafruit_ble/attributes/__init__.py diff --git a/README.rst b/README.rst index 4a5533e..ac2f09c 100644 --- a/README.rst +++ b/README.rst @@ -31,12 +31,12 @@ Usage Example .. code-block:: python - from adafruit_ble import SmartAdapter + from adafruit_ble import BLERadio - adapter = SmartAdapter() + radio = BLERadio() print("scanning") found = set() - for entry in adapter.start_scan(timeout=60, minimum_rssi=-80): + for entry in radio.start_scan(timeout=60, minimum_rssi=-80): addr = entry.address if addr not in found: print(entry) diff --git a/adafruit_ble/__init__.py b/adafruit_ble/__init__.py index beeb49f..c355794 100755 --- a/adafruit_ble/__init__.py +++ b/adafruit_ble/__init__.py @@ -35,6 +35,7 @@ **Hardware:** Adafruit Feather nRF52840 Express + Adafruit Circuit Playground Bluefruit **Software and Dependencies:** @@ -53,46 +54,60 @@ __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_BLE.git" class BLEConnection: - """This represents a connection to a peer BLE device. - - It acts as a map from a Service type to a Service instance for the connection. """ - def __init__(self, connection): - self._connection = connection - self._discovered_services = {} - """These are the bare remote services from _bleio.""" + Represents a connection to a peer BLE device. + It acts as a map from a `Service` type to a `Service` instance for the connection. + :param bleio_connection _bleio.connection + """ + def __init__(self, bleio_connection): + self._bleio_connection = bleio_connection + # _bleio.Service objects representing services found during discoery. + self._discovered_bleio_services = {} + # Service objects that wrap remote services. self._constructed_services = {} - """These are the Service instances from the library that wrap the remote services.""" def _discover_remote(self, uuid): remote_service = None - if uuid in self._discovered_services: - remote_service = self._discovered_services[uuid] + if uuid in self._discovered_bleio_services: + remote_service = self._discovered_bleio_services[uuid] else: - results = self._connection.discover_remote_services((uuid.bleio_uuid,)) + results = self._bleio_connection.discover_remote_services((uuid.bleio_uuid,)) if results: remote_service = results[0] - self._discovered_services[uuid] = remote_service + self._discovered_bleio_services[uuid] = remote_service return remote_service def __contains__(self, key): + """ + Allows easy testing for a particular Service class or a particular UUID + associated with this connection. + + Example:: + + if UARTService in connection: + # do something + + if StandardUUID(0x1234) in connection: + # do something + """ uuid = key if hasattr(key, "uuid"): uuid = key.uuid return self._discover_remote(uuid) is not None def __getitem__(self, key): + """Return the Service for the given Service class or uuid, if any.""" uuid = key maybe_service = False if hasattr(key, "uuid"): uuid = key.uuid maybe_service = True - remote_service = self._discover_remote(uuid) - if uuid in self._constructed_services: return self._constructed_services[uuid] + + remote_service = self._discover_remote(uuid) if remote_service: constructed_service = None if maybe_service: @@ -105,16 +120,19 @@ def __getitem__(self, key): @property def connected(self): """True if the connection to the peer is still active.""" - return self._connection.connected + return self._bleio_connection.connected def disconnect(self): """Disconnect from peer.""" - self._connection.disconnect() + self._bleio_connection.disconnect() class BLERadio: - """The BLERadio class enhances the normal `_bleio.Adapter`. + """ + BLERadio provides the interfaces for BLE advertising, + scanning for advertisements, and connecting to peers. There may be + multiple connections active at once. - It uses the library's `Advertisement` classes and the `BLEConnection` class.""" + It uses this library's `Advertisement` classes and the `BLEConnection` class.""" def __init__(self, adapter=None): if not adapter: @@ -123,34 +141,62 @@ def __init__(self, adapter=None): self._current_advertisement = None self._connection_cache = {} - def start_advertising(self, advertisement, scan_response=None, **kwargs): - """Starts advertising the given advertisement. + def start_advertising(self, advertisement, scan_response=None, interval=0.1): + """ + Starts advertising the given advertisement. - It takes most kwargs of `_bleio.Adapter.start_advertising`.""" + :param buf scan_response: scan response data packet bytes. + ``None`` if no scan response is needed. + :param float interval: advertising interval, in seconds + """ scan_response_data = None if scan_response: scan_response_data = bytes(scan_response) self._adapter.start_advertising(bytes(advertisement), scan_response=scan_response_data, connectable=advertisement.connectable, - **kwargs) + interval=interval) def stop_advertising(self): """Stops advertising.""" self._adapter.stop_advertising() - def start_scan(self, *advertisement_types, **kwargs): - """Starts scanning. Returns an iterator of advertisement objects of the types given in - advertisement_types. The iterator will block until an advertisement is heard or the scan - times out. - - If any ``advertisement_types`` are given, only Advertisements of those types are produced - by the returned iterator. If none are given then `Advertisement` objects will be - returned.""" + def start_scan(self, *advertisement_types, buffer_size=512, extended=False, timeout=None, + interval=0.1, window=0.1, minimum_rssi=-80, active=True): + """ + Starts scanning. Returns an iterator of advertisement objects of the types given in + advertisement_types. The iterator will block until an advertisement is heard or the scan + times out. + + If any ``advertisement_types`` are given, only Advertisements of those types are produced + by the returned iterator. If none are given then `Advertisement` objects will be + returned. + + Advertisements and scan responses are filtered and returned separately. + + :param int buffer_size: the maximum number of advertising bytes to buffer. + :param bool extended: When True, support extended advertising packets. + Increasing buffer_size is recommended when this is set. + :param float timeout: the scan timeout in seconds. + If None, will scan until `stop_scan` is called. + :param float interval: the interval (in seconds) between the start + of two consecutive scan windows + Must be in the range 0.0025 - 40.959375 seconds. + :param float window: the duration (in seconds) to scan a single BLE channel. + window must be <= interval. + :param int minimum_rssi: the minimum rssi of entries to return. + :param bool active: request and retrieve scan responses for scannable advertisements. + :returns: iterable: If any ``advertisement_types`` are given, + only Advertisements of those types are produced by the returned iterator. + If none are given then `Advertisement` objects will be returned. + """ prefixes = b"" if advertisement_types: prefixes = b"".join(adv.prefix for adv in advertisement_types) - for entry in self._adapter.start_scan(prefixes=prefixes, **kwargs): + for entry in self._adapter.start_scan(prefixes=prefixes, buffer_size=buffer_size, + extended=extended, timeout=timeout, + interval=interval, window=window, + minimum_rssi=minimum_rssi, active=active): adv_type = Advertisement for possible_type in advertisement_types: if possible_type.matches(entry) and issubclass(possible_type, adv_type): @@ -167,7 +213,14 @@ def stop_scan(self): self._adapter.stop_scan() def connect(self, advertisement, *, timeout=4): - """Initiates a `BLEConnection` to the peer that advertised the given advertisement.""" + """ + Initiates a `BLEConnection` to the peer that advertised the given advertisement. + + :param advertisement Advertisement: An `Advertisement` or a subclass of `Advertisement` + :param timeout float: how long to wait for a connection + :returns the connection to the peer + :rtype BLEConnection + """ connection = self._adapter.connect(advertisement.address, timeout=timeout) self._connection_cache[connection] = BLEConnection(connection) return self._connection_cache[connection] diff --git a/adafruit_ble/advertising/__init__.py b/adafruit_ble/advertising/__init__.py index 8125a25..093d708 100644 --- a/adafruit_ble/advertising/__init__.py +++ b/adafruit_ble/advertising/__init__.py @@ -25,15 +25,13 @@ import struct -def to_hex(b): +def to_hex(seq): """Pretty prints a byte sequence as hex values.""" - # pylint: disable=invalid-name - return " ".join(["{:02x}".format(v) for v in b]) + return " ".join("{:02x}".format(v) for v in seq) -def to_bytes_literal(b): +def to_bytes_literal(seq): """Prints a byte sequence as a Python bytes literal that only uses hex encoding.""" - # pylint: disable=invalid-name - return "b\"" + "".join(["\\x{:02x}".format(v) for v in b]) + "\"" + return "b\"" + "".join("\\x{:02x}".format(v) for v in seq) + "\"" def decode_data(data, *, key_encoding="B"): """Helper which decodes length encoded structures into a dictionary with the given key diff --git a/adafruit_ble/advertising/standard.py b/adafruit_ble/advertising/standard.py index 0d75c90..4abae54 100644 --- a/adafruit_ble/advertising/standard.py +++ b/adafruit_ble/advertising/standard.py @@ -64,7 +64,8 @@ def __contains__(self, key): return uuid in self._vendor_services or uuid in self._standard_services def _update(self, adt, uuids): - if len(uuids) == 0: + if not uuids: + # uuids is empty del self._advertisement.data_dict[adt] uuid_length = uuids[0].size // 8 b = bytearray(len(uuids) * uuid_length) @@ -131,12 +132,12 @@ def _present(self, obj): def __get__(self, obj, cls): if not self._present(obj) and not obj.mutable: return None - if not hasattr(obj, "_service_lists"): - obj._service_lists = {} + if not hasattr(obj, "adv_service_lists"): + obj.adv_service_lists = {} first_adt = self.standard_services[0] - if first_adt not in obj._service_lists: - obj._service_lists[first_adt] = BoundServiceList(obj, **self.__dict__) - return obj._service_lists[first_adt] + if first_adt not in obj.adv_service_lists: + obj.adv_service_lists[first_adt] = BoundServiceList(obj, **self.__dict__) + return obj.adv_service_lists[first_adt] class ProvideServicesAdvertisement(Advertisement): """Advertise what services that the device makes available upon connection.""" diff --git a/adafruit_ble/attributes/__init__.py b/adafruit_ble/attributes/__init__.py new file mode 100644 index 0000000..5446a17 --- /dev/null +++ b/adafruit_ble/attributes/__init__.py @@ -0,0 +1,43 @@ +# The MIT License (MIT) +# +# Copyright (c) 2019 Dan Halbert for Adafruit Industries +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +""" +:py:mod:`~adafruit_ble.attributes` +==================================================== + +This module provides definitions common to all kinds of BLE attributes, +specifically characteristics and descriptors. + +""" +import _bleio + +__version__ = "0.0.0-auto.0" +__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_BLE.git" + +class Attribute: + """Constants describing security levels.""" + NO_ACCESS = _bleio.Attribute.NO_ACCESS + OPEN = _bleio.Attribute.OPEN + ENCRYPT_NO_MITM = _bleio.Attribute.ENCRYPT_NO_MITM + ENCRYPT_WITH_MITM = _bleio.Attribute.ENCRYPT_WITH_MITM + LESC_ENCRYPT_WITH_MITM = _bleio.Attribute.LESC_ENCRYPT_WITH_MITM + SIGNED_NO_MITM = _bleio.Attribute.SIGNED_NO_MITM + SIGNED_WITH_MITM = _bleio.Attribute.SIGNED_NO_MITM diff --git a/adafruit_ble/characteristics/__init__.py b/adafruit_ble/characteristics/__init__.py index 78daf3e..88dfd44 100644 --- a/adafruit_ble/characteristics/__init__.py +++ b/adafruit_ble/characteristics/__init__.py @@ -30,32 +30,76 @@ import struct import _bleio +from ..attributes import Attribute + __version__ = "0.0.0-auto.0" __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_BLE.git" class Characteristic: - """Top level Characteristic class that does basic binding.""" - def __init__(self, *, uuid=None, initial_value=None, max_length=None, **kwargs): + """ + Top level Characteristic class that does basic binding. + :param UUID uuid: The uuid of the characteristic + :param int properties: The properties of the characteristic, + specified as a bitmask of these values bitwise-or'd together: + `BROADCAST`, `INDICATE`, `NOTIFY`, `READ`, `WRITE`, `WRITE_NO_RESPONSE`. + :param int read_perm: Specifies whether the characteristic can be read by a client, + and if so, which security mode is required. + Must be one of the integer values `Attribute.NO_ACCESS`, `Attribute.OPEN`, + `Attribute.ENCRYPT_NO_MITM`, `Attribute.ENCRYPT_WITH_MITM`, + `Attribute.LESC_ENCRYPT_WITH_MITM`, + `Attribute.SIGNED_NO_MITM`, or `Attribute.SIGNED_WITH_MITM`. + :param int write_perm: Specifies whether the characteristic can be written by a client, + and if so, which security mode is required. Values allowed are the same as ``read_perm``. + :param int max_length: Maximum length in bytes of the characteristic value. The maximum allowed + is 512, or possibly 510 if ``fixed_length`` is False. The default, 20, is the maximum + number of data bytes that fit in a single BLE 4.x ATT packet. + :param bool fixed_length: True if the characteristic value is of fixed length. + :param buf initial_value: The initial value for this characteristic. If not given, will be + filled with zeros. + :return: the new Characteristic. + +""" + BROADCAST = _bleio.Characteristic.BROADCAST + """Property: allowed in advertising packets.""" + INDICATE = _bleio.Characteristic.INDICATE + """Property: server will indicate to the client when the value is set + and wait for a response. + """ + NOTIFY = _bleio.Characteristic.NOTIFY + """Property: lServer will notify the client when the value is set.""" + READ = _bleio.Characteristic.READ + """Property: clients may read this characteristic.""" + WRITE = _bleio.Characteristic.WRITE + """Property: clients may write this characteristic; a response will be sent back.""" + WRITE_NO_RESPONSE = _bleio.Characteristic.WRITE_NO_RESPONSE + """Property: clients may write this characteristic; no response will be sent back.""" + + + def __init__(self, *, uuid=None, properties=0, + read_perm=Attribute.OPEN, write_perm=Attribute.OPEN, + max_length=20, fixed_length=False, initial_value=None): + self.field_name = None # Set by Service during basic binding + if uuid: self.uuid = uuid - self.kwargs = kwargs - self.initial_value = initial_value + self.properties = properties + self.read_perm = read_perm + self.write_perm = write_perm self.max_length = max_length - self.field_name = None # Set by Service during basic binding + self.fixed_length = fixed_length + self.initial_value = initial_value def _ensure_bound(self, service, initial_value=None): """Binds the characteristic to the local Service or remote Characteristic object given.""" if self.field_name in service.bleio_characteristics: return if service.remote: - bleio_characteristic = None - remote_characteristics = service.bleio_service.characteristics - for characteristic in remote_characteristics: + for characteristic in service.bleio_service.characteristics: if characteristic.uuid == self.uuid.bleio_uuid: bleio_characteristic = characteristic break - if not bleio_characteristic: + else: raise AttributeError("Characteristic not available on remote service") else: bleio_characteristic = self.__bind_locally(service, initial_value) @@ -74,18 +118,14 @@ def __bind_locally(self, service, initial_value): if max_length is None: max_length = len(initial_value) return _bleio.Characteristic.add_to_service( - service.bleio_service, - self.uuid.bleio_uuid, - initial_value=initial_value, - max_length=max_length, - **self.kwargs - ) + service.bleio_service, self.uuid.bleio_uuid, initial_value=initial_value, + max_length=max_length, fixed_length=self.fixed_length, + properties=self.properties, read_perm=self.read_perm, write_perm=self.write_perm) def __get__(self, service, cls=None): self._ensure_bound(service) bleio_characteristic = service.bleio_characteristics[self.field_name] - raw_data = bleio_characteristic.value - return raw_data + return bleio_characteristic.value def __set__(self, service, value): self._ensure_bound(service, value) @@ -93,28 +133,36 @@ def __set__(self, service, value): bleio_characteristic.value = value class ComplexCharacteristic: - """Characteristic class that does complex binding where the subclass returns a full object for - interacting with the characteristic data. The Characteristic itself will be shadowed once it - has been bound to the corresponding instance attribute.""" - def __init__(self, *, uuid=None, **kwargs): + """ + Characteristic class that does complex binding where the subclass returns a full object for + interacting with the characteristic data. The Characteristic itself will be shadowed once it + has been bound to the corresponding instance attribute. + """ + def __init__(self, *, uuid=None, properties=0, + read_perm=Attribute.OPEN, write_perm=Attribute.OPEN, + max_length=20, fixed_length=False, initial_value=None): + self.field_name = None # Set by Service during basic binding + if uuid: self.uuid = uuid - self.kwargs = kwargs - self.field_name = None # Set by Service + self.properties = properties + self.read_perm = read_perm + self.write_perm = write_perm + self.max_length = max_length + self.fixed_length = fixed_length + self.initial_value = initial_value def bind(self, service): """Binds the characteristic to the local Service or remote Characteristic object given.""" if service.remote: - remote_characteristics = service.bleio_service.characteristics - for characteristic in remote_characteristics: + for characteristic in service.bleio_service.characteristics: if characteristic.uuid == self.uuid.bleio_uuid: return characteristic raise AttributeError("Characteristic not available on remote service") return _bleio.Characteristic.add_to_service( - service.bleio_service, - self.uuid.bleio_uuid, - **self.kwargs - ) + service.bleio_service, self.uuid.bleio_uuid, + initial_value=self.initial_value, max_length=self.max_length, + properties=self.properties, read_perm=self.read_perm, write_perm=self.write_perm) def __get__(self, service, cls=None): bound_object = self.bind(service) @@ -122,13 +170,27 @@ def __get__(self, service, cls=None): return bound_object class StructCharacteristic(Characteristic): - """Data descriptor for a structure with a fixed format.""" - def __init__(self, struct_format, **kwargs): + """ + Data descriptor for a structure with a fixed format. + + :param struct_format: a `struct` format string describing how to pack multiple values + into the characteristic bytestring + :param UUID uuid: The uuid of the characteristic + :param int properties: see `Characteristic` + :param int read_perm: see `Characteristic` + :param int write_perm: see `Characteristic` + :param buf initial_value: see `Characteristic` + """ + def __init__(self, struct_format, *, uuid=None, properties=0, + read_perm=Attribute.OPEN, write_perm=Attribute.OPEN, + initial_value=None): self._struct_format = struct_format self._expected_size = struct.calcsize(struct_format) - if "initial_value" in kwargs: - kwargs["initial_value"] = struct.pack(self._struct_format, *kwargs["initial_value"]) - super().__init__(**kwargs, max_length=self._expected_size, fixed_length=True) + if initial_value: + initial_value = struct.pack(self._struct_format, initial_value) + super().__init__(uuid=uuid, initial_value=initial_value, + max_length=self._expected_size, fixed_length=True, + properties=properties, read_perm=read_perm, write_perm=write_perm) def __get__(self, obj, cls=None): raw_data = super().__get__(obj, cls) diff --git a/adafruit_ble/characteristics/float.py b/adafruit_ble/characteristics/float.py index d33903e..eced8c3 100644 --- a/adafruit_ble/characteristics/float.py +++ b/adafruit_ble/characteristics/float.py @@ -27,6 +27,7 @@ """ +from . import Attribute from . import StructCharacteristic __version__ = "0.0.0-auto.0" @@ -34,11 +35,14 @@ class FloatCharacteristic(StructCharacteristic): """32-bit float""" - # TODO: Valid set values as within range. - def __init__(self, **kwargs): - if "initial_value" in kwargs: - kwargs["initial_value"] = (kwargs["initial_value"],) - super().__init__(" Date: Thu, 14 Nov 2019 12:19:24 -0500 Subject: [PATCH 2/4] address comments so far; fix sphinx build --- adafruit_ble/__init__.py | 12 ++++--- adafruit_ble/attributes/__init__.py | 31 ++++++++++++++++- adafruit_ble/characteristics/__init__.py | 34 +++++++++++++------ adafruit_ble/services/standard/device_info.py | 4 +-- adafruit_ble/services/standard/standard.py | 6 ++-- docs/api.rst | 1 + docs/attributes.rst | 5 +++ docs/bleio_mock.py | 9 ++++- 8 files changed, 80 insertions(+), 22 deletions(-) create mode 100644 docs/attributes.rst diff --git a/adafruit_ble/__init__.py b/adafruit_ble/__init__.py index c355794..823bead 100755 --- a/adafruit_ble/__init__.py +++ b/adafruit_ble/__init__.py @@ -58,11 +58,12 @@ class BLEConnection: Represents a connection to a peer BLE device. It acts as a map from a `Service` type to a `Service` instance for the connection. - :param bleio_connection _bleio.connection + :param bleio_connection _bleio.Connection + Wrap the native `_bleio.Connection` object. """ def __init__(self, bleio_connection): self._bleio_connection = bleio_connection - # _bleio.Service objects representing services found during discoery. + # _bleio.Service objects representing services found during discovery. self._discovered_bleio_services = {} # Service objects that wrap remote services. self._constructed_services = {} @@ -186,9 +187,10 @@ def start_scan(self, *advertisement_types, buffer_size=512, extended=False, time window must be <= interval. :param int minimum_rssi: the minimum rssi of entries to return. :param bool active: request and retrieve scan responses for scannable advertisements. - :returns: iterable: If any ``advertisement_types`` are given, + :return: If any ``advertisement_types`` are given, only Advertisements of those types are produced by the returned iterator. If none are given then `Advertisement` objects will be returned. + :rtype: iterable """ prefixes = b"" if advertisement_types: @@ -218,8 +220,8 @@ def connect(self, advertisement, *, timeout=4): :param advertisement Advertisement: An `Advertisement` or a subclass of `Advertisement` :param timeout float: how long to wait for a connection - :returns the connection to the peer - :rtype BLEConnection + :return: the connection to the peer + :rtype: BLEConnection """ connection = self._adapter.connect(advertisement.address, timeout=timeout) self._connection_cache[connection] = BLEConnection(connection) diff --git a/adafruit_ble/attributes/__init__.py b/adafruit_ble/attributes/__init__.py index 5446a17..b0b80d4 100644 --- a/adafruit_ble/attributes/__init__.py +++ b/adafruit_ble/attributes/__init__.py @@ -33,7 +33,36 @@ __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_BLE.git" class Attribute: - """Constants describing security levels.""" + """Constants describing security levels. + + .. data:: NO_ACCESS + + security mode: access not allowed + + .. data:: OPEN + + security_mode: no security (link is not encrypted) + + .. data:: ENCRYPT_NO_MITM + + security_mode: unauthenticated encryption, without man-in-the-middle protection + + .. data:: ENCRYPT_WITH_MITM + + security_mode: authenticated encryption, with man-in-the-middle protection + + .. data:: LESC_ENCRYPT_WITH_MITM + + security_mode: LESC encryption, with man-in-the-middle protection + + .. data:: SIGNED_NO_MITM + + security_mode: unauthenticated data signing, without man-in-the-middle protection + + .. data:: SIGNED_WITH_MITM + + security_mode: authenticated data signing, without man-in-the-middle protection +""" NO_ACCESS = _bleio.Attribute.NO_ACCESS OPEN = _bleio.Attribute.OPEN ENCRYPT_NO_MITM = _bleio.Attribute.ENCRYPT_NO_MITM diff --git a/adafruit_ble/characteristics/__init__.py b/adafruit_ble/characteristics/__init__.py index 88dfd44..883709d 100644 --- a/adafruit_ble/characteristics/__init__.py +++ b/adafruit_ble/characteristics/__init__.py @@ -39,6 +39,7 @@ class Characteristic: """ Top level Characteristic class that does basic binding. + :param UUID uuid: The uuid of the characteristic :param int properties: The properties of the characteristic, specified as a bitmask of these values bitwise-or'd together: @@ -57,24 +58,37 @@ class Characteristic: :param bool fixed_length: True if the characteristic value is of fixed length. :param buf initial_value: The initial value for this characteristic. If not given, will be filled with zeros. - :return: the new Characteristic. + .. data:: BROADCAST + + property: allowed in advertising packets + + .. data:: INDICATE + + property: server will indicate to the client when the value is set and wait for a response + + .. data:: NOTIFY + + property: server will notify the client when the value is set + + .. data:: READ + + property: clients may read this characteristic + + .. data:: WRITE + + property: clients may write this characteristic; a response will be sent back + + .. data:: WRITE_NO_RESPONSE + + property: clients may write this characteristic; no response will be sent back """ BROADCAST = _bleio.Characteristic.BROADCAST - """Property: allowed in advertising packets.""" INDICATE = _bleio.Characteristic.INDICATE - """Property: server will indicate to the client when the value is set - and wait for a response. - """ NOTIFY = _bleio.Characteristic.NOTIFY - """Property: lServer will notify the client when the value is set.""" READ = _bleio.Characteristic.READ - """Property: clients may read this characteristic.""" WRITE = _bleio.Characteristic.WRITE - """Property: clients may write this characteristic; a response will be sent back.""" WRITE_NO_RESPONSE = _bleio.Characteristic.WRITE_NO_RESPONSE - """Property: clients may write this characteristic; no response will be sent back.""" - def __init__(self, *, uuid=None, properties=0, read_perm=Attribute.OPEN, write_perm=Attribute.OPEN, diff --git a/adafruit_ble/services/standard/device_info.py b/adafruit_ble/services/standard/device_info.py index 77dedf2..78e8d52 100644 --- a/adafruit_ble/services/standard/device_info.py +++ b/adafruit_ble/services/standard/device_info.py @@ -48,8 +48,8 @@ import sys import microcontroller -from ..core import Service -from ...core.uuid import StandardUUID +from .. import Service +from ...uuid import StandardUUID from ...characteristics.string import FixedStringCharacteristic __version__ = "0.0.0-auto.0" diff --git a/adafruit_ble/services/standard/standard.py b/adafruit_ble/services/standard/standard.py index 2c5e560..9793a84 100755 --- a/adafruit_ble/services/standard/standard.py +++ b/adafruit_ble/services/standard/standard.py @@ -29,10 +29,10 @@ import time -from ..core import Service -from ...core.uuid import StandardUUID +from .. import Service +from ...uuid import StandardUUID from ..characteristics.string import StringCharacteristic -from ..characteristics.core import StructCharacteristic +from ..characteristics import StructCharacteristic from ..characteristics.int import Uint8Characteristic __version__ = "0.0.0-auto.0" diff --git a/docs/api.rst b/docs/api.rst index d6907b5..353860f 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -8,6 +8,7 @@ top_level advertising + attributes characteristics services uuid diff --git a/docs/attributes.rst b/docs/attributes.rst new file mode 100644 index 0000000..6925e9f --- /dev/null +++ b/docs/attributes.rst @@ -0,0 +1,5 @@ +`adafruit_ble.attributes` +==================================================== + +.. automodule:: adafruit_ble.attributes + :members: diff --git a/docs/bleio_mock.py b/docs/bleio_mock.py index c77c2bd..60887a8 100644 --- a/docs/bleio_mock.py +++ b/docs/bleio_mock.py @@ -1,13 +1,20 @@ class Attribute: - OPEN = 0 NO_ACCESS = 0 + OPEN = 0 + ENCRYPT_NO_MITM = 0 + ENCRYPT_WITH_MITM = 0 + LESC_ENCRYPT_WITH_MITM = 0 + SIGNED_NO_MITM = 0 + SIGNED_WITH_MITM = 0 class UUID: def __init__(self, uuid): pass class Characteristic: + BROADCAST = 0 READ = 0 WRITE = 0 NOTIFY = 0 + INDICATE = 0 WRITE_NO_RESPONSE = 0 From 69cca3f56d13d59cf12320b795500e14b39a80e7 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Thu, 14 Nov 2019 14:23:11 -0500 Subject: [PATCH 3/4] ... not .. in standard .py --- adafruit_ble/services/standard/standard.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/adafruit_ble/services/standard/standard.py b/adafruit_ble/services/standard/standard.py index 9793a84..d91b33a 100755 --- a/adafruit_ble/services/standard/standard.py +++ b/adafruit_ble/services/standard/standard.py @@ -31,9 +31,9 @@ from .. import Service from ...uuid import StandardUUID -from ..characteristics.string import StringCharacteristic -from ..characteristics import StructCharacteristic -from ..characteristics.int import Uint8Characteristic +from ...characteristics.string import StringCharacteristic +from ...characteristics import StructCharacteristic +from ...characteristics.int import Uint8Characteristic __version__ = "0.0.0-auto.0" __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_BLE.git" From 09a9baf7e827cbf0160e9d00bef20665f6260f55 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Thu, 14 Nov 2019 20:03:17 -0500 Subject: [PATCH 4/4] fix doc formatting --- adafruit_ble/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/adafruit_ble/__init__.py b/adafruit_ble/__init__.py index 823bead..2cde100 100755 --- a/adafruit_ble/__init__.py +++ b/adafruit_ble/__init__.py @@ -58,8 +58,8 @@ class BLEConnection: Represents a connection to a peer BLE device. It acts as a map from a `Service` type to a `Service` instance for the connection. - :param bleio_connection _bleio.Connection - Wrap the native `_bleio.Connection` object. + :param bleio_connection _bleio.Connection: the native `_bleio.Connection` object to wrap + """ def __init__(self, bleio_connection): self._bleio_connection = bleio_connection