Skip to content

Commit e6679ab

Browse files
authored
Merge pull request #32 from dhalbert/doc-and-cleanup
doc and cleanup pass
2 parents 115237b + 09a9baf commit e6679ab

File tree

21 files changed

+431
-187
lines changed

21 files changed

+431
-187
lines changed

README.rst

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,12 @@ Usage Example
3131

3232
.. code-block:: python
3333
34-
from adafruit_ble import SmartAdapter
34+
from adafruit_ble import BLERadio
3535
36-
adapter = SmartAdapter()
36+
radio = BLERadio()
3737
print("scanning")
3838
found = set()
39-
for entry in adapter.start_scan(timeout=60, minimum_rssi=-80):
39+
for entry in radio.start_scan(timeout=60, minimum_rssi=-80):
4040
addr = entry.address
4141
if addr not in found:
4242
print(entry)

adafruit_ble/__init__.py

Lines changed: 87 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
**Hardware:**
3636
3737
Adafruit Feather nRF52840 Express <https://www.adafruit.com/product/4062>
38+
Adafruit Circuit Playground Bluefruit <https://www.adafruit.com/product/4333>
3839
3940
**Software and Dependencies:**
4041
@@ -53,46 +54,61 @@
5354
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_BLE.git"
5455

5556
class BLEConnection:
56-
"""This represents a connection to a peer BLE device.
57-
58-
It acts as a map from a Service type to a Service instance for the connection.
5957
"""
60-
def __init__(self, connection):
61-
self._connection = connection
62-
self._discovered_services = {}
63-
"""These are the bare remote services from _bleio."""
58+
Represents a connection to a peer BLE device.
59+
It acts as a map from a `Service` type to a `Service` instance for the connection.
60+
61+
:param bleio_connection _bleio.Connection: the native `_bleio.Connection` object to wrap
6462
63+
"""
64+
def __init__(self, bleio_connection):
65+
self._bleio_connection = bleio_connection
66+
# _bleio.Service objects representing services found during discovery.
67+
self._discovered_bleio_services = {}
68+
# Service objects that wrap remote services.
6569
self._constructed_services = {}
66-
"""These are the Service instances from the library that wrap the remote services."""
6770

6871
def _discover_remote(self, uuid):
6972
remote_service = None
70-
if uuid in self._discovered_services:
71-
remote_service = self._discovered_services[uuid]
73+
if uuid in self._discovered_bleio_services:
74+
remote_service = self._discovered_bleio_services[uuid]
7275
else:
73-
results = self._connection.discover_remote_services((uuid.bleio_uuid,))
76+
results = self._bleio_connection.discover_remote_services((uuid.bleio_uuid,))
7477
if results:
7578
remote_service = results[0]
76-
self._discovered_services[uuid] = remote_service
79+
self._discovered_bleio_services[uuid] = remote_service
7780
return remote_service
7881

7982
def __contains__(self, key):
83+
"""
84+
Allows easy testing for a particular Service class or a particular UUID
85+
associated with this connection.
86+
87+
Example::
88+
89+
if UARTService in connection:
90+
# do something
91+
92+
if StandardUUID(0x1234) in connection:
93+
# do something
94+
"""
8095
uuid = key
8196
if hasattr(key, "uuid"):
8297
uuid = key.uuid
8398
return self._discover_remote(uuid) is not None
8499

85100
def __getitem__(self, key):
101+
"""Return the Service for the given Service class or uuid, if any."""
86102
uuid = key
87103
maybe_service = False
88104
if hasattr(key, "uuid"):
89105
uuid = key.uuid
90106
maybe_service = True
91107

92-
remote_service = self._discover_remote(uuid)
93-
94108
if uuid in self._constructed_services:
95109
return self._constructed_services[uuid]
110+
111+
remote_service = self._discover_remote(uuid)
96112
if remote_service:
97113
constructed_service = None
98114
if maybe_service:
@@ -105,16 +121,19 @@ def __getitem__(self, key):
105121
@property
106122
def connected(self):
107123
"""True if the connection to the peer is still active."""
108-
return self._connection.connected
124+
return self._bleio_connection.connected
109125

110126
def disconnect(self):
111127
"""Disconnect from peer."""
112-
self._connection.disconnect()
128+
self._bleio_connection.disconnect()
113129

114130
class BLERadio:
115-
"""The BLERadio class enhances the normal `_bleio.Adapter`.
131+
"""
132+
BLERadio provides the interfaces for BLE advertising,
133+
scanning for advertisements, and connecting to peers. There may be
134+
multiple connections active at once.
116135
117-
It uses the library's `Advertisement` classes and the `BLEConnection` class."""
136+
It uses this library's `Advertisement` classes and the `BLEConnection` class."""
118137

119138
def __init__(self, adapter=None):
120139
if not adapter:
@@ -123,34 +142,63 @@ def __init__(self, adapter=None):
123142
self._current_advertisement = None
124143
self._connection_cache = {}
125144

126-
def start_advertising(self, advertisement, scan_response=None, **kwargs):
127-
"""Starts advertising the given advertisement.
145+
def start_advertising(self, advertisement, scan_response=None, interval=0.1):
146+
"""
147+
Starts advertising the given advertisement.
128148
129-
It takes most kwargs of `_bleio.Adapter.start_advertising`."""
149+
:param buf scan_response: scan response data packet bytes.
150+
``None`` if no scan response is needed.
151+
:param float interval: advertising interval, in seconds
152+
"""
130153
scan_response_data = None
131154
if scan_response:
132155
scan_response_data = bytes(scan_response)
133156
self._adapter.start_advertising(bytes(advertisement),
134157
scan_response=scan_response_data,
135158
connectable=advertisement.connectable,
136-
**kwargs)
159+
interval=interval)
137160

138161
def stop_advertising(self):
139162
"""Stops advertising."""
140163
self._adapter.stop_advertising()
141164

142-
def start_scan(self, *advertisement_types, **kwargs):
143-
"""Starts scanning. Returns an iterator of advertisement objects of the types given in
144-
advertisement_types. The iterator will block until an advertisement is heard or the scan
145-
times out.
146-
147-
If any ``advertisement_types`` are given, only Advertisements of those types are produced
148-
by the returned iterator. If none are given then `Advertisement` objects will be
149-
returned."""
165+
def start_scan(self, *advertisement_types, buffer_size=512, extended=False, timeout=None,
166+
interval=0.1, window=0.1, minimum_rssi=-80, active=True):
167+
"""
168+
Starts scanning. Returns an iterator of advertisement objects of the types given in
169+
advertisement_types. The iterator will block until an advertisement is heard or the scan
170+
times out.
171+
172+
If any ``advertisement_types`` are given, only Advertisements of those types are produced
173+
by the returned iterator. If none are given then `Advertisement` objects will be
174+
returned.
175+
176+
Advertisements and scan responses are filtered and returned separately.
177+
178+
:param int buffer_size: the maximum number of advertising bytes to buffer.
179+
:param bool extended: When True, support extended advertising packets.
180+
Increasing buffer_size is recommended when this is set.
181+
:param float timeout: the scan timeout in seconds.
182+
If None, will scan until `stop_scan` is called.
183+
:param float interval: the interval (in seconds) between the start
184+
of two consecutive scan windows
185+
Must be in the range 0.0025 - 40.959375 seconds.
186+
:param float window: the duration (in seconds) to scan a single BLE channel.
187+
window must be <= interval.
188+
:param int minimum_rssi: the minimum rssi of entries to return.
189+
:param bool active: request and retrieve scan responses for scannable advertisements.
190+
:return: If any ``advertisement_types`` are given,
191+
only Advertisements of those types are produced by the returned iterator.
192+
If none are given then `Advertisement` objects will be returned.
193+
:rtype: iterable
194+
"""
150195
prefixes = b""
151196
if advertisement_types:
152197
prefixes = b"".join(adv.prefix for adv in advertisement_types)
153-
for entry in self._adapter.start_scan(prefixes=prefixes, **kwargs):
198+
for entry in self._adapter.start_scan(prefixes=prefixes, buffer_size=buffer_size,
199+
extended=extended, timeout=timeout,
200+
interval=interval, window=window,
201+
minimum_rssi=minimum_rssi, active=active):
154202
adv_type = Advertisement
155203
for possible_type in advertisement_types:
156204
if possible_type.matches(entry) and issubclass(possible_type, adv_type):
@@ -167,7 +215,14 @@ def stop_scan(self):
167215
self._adapter.stop_scan()
168216

169217
def connect(self, advertisement, *, timeout=4):
170-
"""Initiates a `BLEConnection` to the peer that advertised the given advertisement."""
218+
"""
219+
Initiates a `BLEConnection` to the peer that advertised the given advertisement.
220+
221+
:param advertisement Advertisement: An `Advertisement` or a subclass of `Advertisement`
222+
:param timeout float: how long to wait for a connection
223+
:return: the connection to the peer
224+
:rtype: BLEConnection
225+
"""
171226
connection = self._adapter.connect(advertisement.address, timeout=timeout)
172227
self._connection_cache[connection] = BLEConnection(connection)
173228
return self._connection_cache[connection]

adafruit_ble/advertising/__init__.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,13 @@
2525

2626
import struct
2727

28-
def to_hex(b):
28+
def to_hex(seq):
2929
"""Pretty prints a byte sequence as hex values."""
30-
# pylint: disable=invalid-name
31-
return " ".join(["{:02x}".format(v) for v in b])
30+
return " ".join("{:02x}".format(v) for v in seq)
3231

33-
def to_bytes_literal(b):
32+
def to_bytes_literal(seq):
3433
"""Prints a byte sequence as a Python bytes literal that only uses hex encoding."""
35-
# pylint: disable=invalid-name
36-
return "b\"" + "".join(["\\x{:02x}".format(v) for v in b]) + "\""
34+
return "b\"" + "".join("\\x{:02x}".format(v) for v in seq) + "\""
3735

3836
def decode_data(data, *, key_encoding="B"):
3937
"""Helper which decodes length encoded structures into a dictionary with the given key

adafruit_ble/advertising/standard.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,8 @@ def __contains__(self, key):
6464
return uuid in self._vendor_services or uuid in self._standard_services
6565

6666
def _update(self, adt, uuids):
67-
if len(uuids) == 0:
67+
if not uuids:
68+
# uuids is empty
6869
del self._advertisement.data_dict[adt]
6970
uuid_length = uuids[0].size // 8
7071
b = bytearray(len(uuids) * uuid_length)
@@ -131,12 +132,12 @@ def _present(self, obj):
131132
def __get__(self, obj, cls):
132133
if not self._present(obj) and not obj.mutable:
133134
return None
134-
if not hasattr(obj, "_service_lists"):
135-
obj._service_lists = {}
135+
if not hasattr(obj, "adv_service_lists"):
136+
obj.adv_service_lists = {}
136137
first_adt = self.standard_services[0]
137-
if first_adt not in obj._service_lists:
138-
obj._service_lists[first_adt] = BoundServiceList(obj, **self.__dict__)
139-
return obj._service_lists[first_adt]
138+
if first_adt not in obj.adv_service_lists:
139+
obj.adv_service_lists[first_adt] = BoundServiceList(obj, **self.__dict__)
140+
return obj.adv_service_lists[first_adt]
140141

141142
class ProvideServicesAdvertisement(Advertisement):
142143
"""Advertise what services that the device makes available upon connection."""

adafruit_ble/attributes/__init__.py

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
# The MIT License (MIT)
2+
#
3+
# Copyright (c) 2019 Dan Halbert for Adafruit Industries
4+
#
5+
# Permission is hereby granted, free of charge, to any person obtaining a copy
6+
# of this software and associated documentation files (the "Software"), to deal
7+
# in the Software without restriction, including without limitation the rights
8+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
# copies of the Software, and to permit persons to whom the Software is
10+
# furnished to do so, subject to the following conditions:
11+
#
12+
# The above copyright notice and this permission notice shall be included in
13+
# all copies or substantial portions of the Software.
14+
#
15+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
# THE SOFTWARE.
22+
"""
23+
:py:mod:`~adafruit_ble.attributes`
24+
====================================================
25+
26+
This module provides definitions common to all kinds of BLE attributes,
27+
specifically characteristics and descriptors.
28+
29+
"""
30+
import _bleio
31+
32+
__version__ = "0.0.0-auto.0"
33+
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_BLE.git"
34+
35+
class Attribute:
36+
"""Constants describing security levels.
37+
38+
.. data:: NO_ACCESS
39+
40+
security mode: access not allowed
41+
42+
.. data:: OPEN
43+
44+
security_mode: no security (link is not encrypted)
45+
46+
.. data:: ENCRYPT_NO_MITM
47+
48+
security_mode: unauthenticated encryption, without man-in-the-middle protection
49+
50+
.. data:: ENCRYPT_WITH_MITM
51+
52+
security_mode: authenticated encryption, with man-in-the-middle protection
53+
54+
.. data:: LESC_ENCRYPT_WITH_MITM
55+
56+
security_mode: LESC encryption, with man-in-the-middle protection
57+
58+
.. data:: SIGNED_NO_MITM
59+
60+
security_mode: unauthenticated data signing, without man-in-the-middle protection
61+
62+
.. data:: SIGNED_WITH_MITM
63+
64+
security_mode: authenticated data signing, without man-in-the-middle protection
65+
"""
66+
NO_ACCESS = _bleio.Attribute.NO_ACCESS
67+
OPEN = _bleio.Attribute.OPEN
68+
ENCRYPT_NO_MITM = _bleio.Attribute.ENCRYPT_NO_MITM
69+
ENCRYPT_WITH_MITM = _bleio.Attribute.ENCRYPT_WITH_MITM
70+
LESC_ENCRYPT_WITH_MITM = _bleio.Attribute.LESC_ENCRYPT_WITH_MITM
71+
SIGNED_NO_MITM = _bleio.Attribute.SIGNED_NO_MITM
72+
SIGNED_WITH_MITM = _bleio.Attribute.SIGNED_NO_MITM

0 commit comments

Comments
 (0)