Skip to content

Commit 99e1ad8

Browse files
committed
Add Scanner: WIP
1 parent 7283b7b commit 99e1ad8

File tree

2 files changed

+154
-2
lines changed

2 files changed

+154
-2
lines changed

adafruit_ble/beacon.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636

3737
class Beacon:
3838
"""Base class for Beacon advertisers."""
39-
def __init__(self, advertising_packet, interval=1.0):
39+
def __init__(self, advertising_packet):
4040
"""Set up a beacon with the given AdvertisingPacket.
4141
4242
:param AdvertisingPacket advertising_packet
@@ -49,7 +49,8 @@ def start(self, interval=1.0):
4949
5050
:param float interval: Advertising interval in seconds
5151
"""
52-
self._broadcaster.start_advertising(self._advertising_packet.packet_bytes, interval=interval)
52+
self._broadcaster.start_advertising(self._advertising_packet.packet_bytes,
53+
interval=interval)
5354

5455
def stop(self):
5556
"""Turn off beacon."""

adafruit_ble/scanner.py

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
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+
`adafruit_ble.scanner`
24+
====================================================
25+
26+
UART-style communication.
27+
28+
* Author(s): Dan Halbert for Adafruit Industries
29+
30+
"""
31+
import struct
32+
33+
import bleio.ScanEntry
34+
import bleio.Scanner
35+
import bleio.UUID
36+
37+
from .advertising import AdvertisingPacket
38+
39+
class Scanner:
40+
"""
41+
Scan for BLE device advertisements for a period of time. Return what was received.
42+
43+
Example::
44+
45+
from adafruit_ble.scanner import Scanner
46+
scanner = Scanner()
47+
scanner.Scan
48+
49+
# Wait for a connection.
50+
while not uart.connected:
51+
pass
52+
53+
uart.write('abc')
54+
"""
55+
56+
def __init__(self):
57+
self._scanner = bleio.Scanner()
58+
59+
def scan(self, timeout, *, interval=0.1, window=0.1):
60+
"""Scan for advertisements from BLE devices.
61+
62+
:param int timeout: how long to scan for (in seconds)
63+
:param float interval: the interval (in seconds) between the start
64+
of two consecutive scan windows.
65+
Must be in the range 0.0025 - 40.959375 seconds.
66+
:param float window: the duration (in seconds) to scan a single BLE channel.
67+
`window` must be <= `interval`.
68+
:returns: advertising packets found
69+
:rtype: list of `adafruit_ble.ScanEntry` objects
70+
"""
71+
return [ScanEntry(entry) for entry in self._scanner.scan(timeout, interval, window)]
72+
73+
74+
class ScanEntry:
75+
"""
76+
Information about a single transmission of data from a BLE device received by a `Scanner`.
77+
78+
:param bleio.ScanEntry entry: lower-level ScanEntry from a `bleio.Scanner`.
79+
80+
This constructor would normally only be used by `Scanner`.
81+
"""
82+
83+
def __init__(self, entry):
84+
self._bleio_entry = entry
85+
86+
def item(self, item_type):
87+
"""Return the bytes in the advertising packet for given the element type.
88+
89+
:param int element_type: An integer designating an element type.
90+
A number are defined in `AdvertisingPacket`, such as `AdvertisingPacket.TX_POWER`.
91+
:returns: bytes that are the value for the given element type.
92+
If the element type is not present in the packet, return ``None``.
93+
"""
94+
i = 0
95+
adv_bytes = self.advertisement_bytes
96+
while i < len(adv_bytes):
97+
item_length = adv_bytes[i]
98+
if item_type != adv_bytes[i+1]:
99+
# Type doesn't match: skip to next item
100+
i += item_length + 1
101+
else:
102+
return adv_bytes[i + 2:i + item_length]
103+
return None
104+
105+
@property
106+
def advertisement_bytes(self):
107+
"""The raw bytes of the received advertisement."""
108+
return self._bleio_entry.raw_data
109+
110+
@property
111+
def rssi(self):
112+
"""The signal strength of the device at the time of the scan. (read-only)."""
113+
return self._bleio_entry.rssi
114+
115+
@property
116+
def address(self):
117+
"""The address of the device. (read-only)."""
118+
return self._bleio_entry.address
119+
120+
@property
121+
def name(self):
122+
"""The name of the device. (read-only)"""
123+
name = self.item(AdvertisingPacket.COMPLETE_LOCAL_NAME)
124+
return name if name else self.item(AdvertisingPacket.SHORT_LOCAL_NAME)
125+
126+
@property
127+
def service_uuids(self):
128+
"""List of all the service UUIDs in the advertisement."""
129+
concat_uuids = self.item(AdvertisingPacket.ALL_16_BIT_SERVICE_UUIDS)
130+
concat_uuids = concat_uuids if concat_uuids else self.item(
131+
AdvertisingPacket.SOME_16_BIT_SERVICE_UUIDS)
132+
133+
uuid_values = []
134+
if concat_uuids:
135+
for i in range(0, len(uuid_values), 2):
136+
uuid_values.append(struct.unpack("<H", concat_uuids[i:i+2]))
137+
138+
concat_uuids = self.item(AdvertisingPacket.ALL_128_BIT_SERVICE_UUIDS)
139+
concat_uuids = concat_uuids if concat_uuids else self.item(
140+
AdvertisingPacket.SOME_128_BIT_SERVICE_UUIDS)
141+
142+
if concat_uuids:
143+
for i in range(0, len(uuid_values), 16):
144+
uuid_values.append(concat_uuids[i:i+16])
145+
146+
return [bleio.UUID(value) for value in uuid_values]
147+
148+
@property
149+
def manufacturer_specific_data(self):
150+
"""Manufacturer-specific data in the advertisement, returned as bytes."""
151+
return self.item(AdvertisingPacket.MANUFACTURER_SPECIFIC_DATA)

0 commit comments

Comments
 (0)