0
# Device Discovery and Scanning
1
2
Bleak provides comprehensive BLE device discovery capabilities through the `BleakScanner` class. It supports both active and passive scanning modes with platform-specific optimizations and flexible filtering options.
3
4
## Capabilities
5
6
### Scanner Initialization and Configuration
7
8
Initialize scanners with optional detection callbacks, service UUID filtering, and platform-specific arguments for optimal discovery performance.
9
10
```python { .api }
11
class BleakScanner:
12
def __init__(
13
self,
14
detection_callback: Optional[AdvertisementDataCallback] = None,
15
service_uuids: Optional[list[str]] = None,
16
scanning_mode: Literal["active", "passive"] = "active",
17
*,
18
bluez: BlueZScannerArgs = {},
19
cb: CBScannerArgs = {},
20
backend: Optional[type[BaseBleakScanner]] = None,
21
**kwargs: Any,
22
) -> None:
23
"""
24
Initialize BLE scanner.
25
26
Args:
27
detection_callback: Optional function called when devices are discovered
28
service_uuids: Optional list of service UUIDs to filter on
29
scanning_mode: "active" or "passive" scanning mode
30
bluez: BlueZ-specific scanner arguments
31
cb: CoreBluetooth-specific scanner arguments
32
backend: Custom backend implementation
33
"""
34
```
35
36
### Scanner Control Operations
37
38
Start and stop scanning operations with async context manager support for automatic lifecycle management.
39
40
```python { .api }
41
async def start(self) -> None:
42
"""Start scanning for devices."""
43
44
async def stop(self) -> None:
45
"""Stop scanning for devices."""
46
47
async def __aenter__(self) -> Self:
48
"""Async context manager entry - starts scanning."""
49
50
async def __aexit__(
51
self,
52
exc_type: type[BaseException],
53
exc_val: BaseException,
54
exc_tb: TracebackType,
55
) -> None:
56
"""Async context manager exit - stops scanning."""
57
```
58
59
### Advertisement Data Streaming
60
61
Stream advertisement data as devices are discovered using async generators for real-time processing.
62
63
```python { .api }
64
async def advertisement_data(
65
self,
66
) -> AsyncGenerator[tuple[BLEDevice, AdvertisementData], None]:
67
"""
68
Yields devices and advertisement data as they are discovered.
69
70
Note: Ensure scanning is started before calling this method.
71
72
Returns:
73
Async iterator yielding tuples of (BLEDevice, AdvertisementData)
74
"""
75
```
76
77
### One-Time Discovery Operations
78
79
Convenient class methods for simple discovery operations without manual scanner lifecycle management.
80
81
```python { .api }
82
@classmethod
83
async def discover(
84
cls,
85
timeout: float = 5.0,
86
*,
87
return_adv: bool = False,
88
**kwargs: Unpack[ExtraArgs],
89
):
90
"""
91
Scan for devices for specified timeout duration.
92
93
Args:
94
timeout: Time in seconds to scan
95
return_adv: If True, return advertisement data with devices
96
**kwargs: Additional scanner arguments
97
98
Returns:
99
List of BLEDevice objects or dict mapping addresses to (device, adv_data) tuples
100
"""
101
```
102
103
### Device Lookup Operations
104
105
Find specific devices by address, name, or custom filter criteria with configurable timeout.
106
107
```python { .api }
108
@classmethod
109
async def find_device_by_address(
110
cls, device_identifier: str, timeout: float = 10.0, **kwargs: Unpack[ExtraArgs]
111
) -> Optional[BLEDevice]:
112
"""
113
Find device by Bluetooth address or UUID.
114
115
Args:
116
device_identifier: Bluetooth address or UUID to search for
117
timeout: Maximum time to search before giving up
118
**kwargs: Additional scanner arguments
119
120
Returns:
121
BLEDevice if found, None otherwise
122
"""
123
124
@classmethod
125
async def find_device_by_name(
126
cls, name: str, timeout: float = 10.0, **kwargs: Unpack[ExtraArgs]
127
) -> Optional[BLEDevice]:
128
"""
129
Find device by local name in advertisement data.
130
131
Args:
132
name: Device name to search for
133
timeout: Maximum time to search before giving up
134
**kwargs: Additional scanner arguments
135
136
Returns:
137
BLEDevice if found, None otherwise
138
"""
139
140
@classmethod
141
async def find_device_by_filter(
142
cls,
143
filterfunc: AdvertisementDataFilter,
144
timeout: float = 10.0,
145
**kwargs: Unpack[ExtraArgs],
146
) -> Optional[BLEDevice]:
147
"""
148
Find device using custom filter function.
149
150
Args:
151
filterfunc: Function that returns True for desired device
152
timeout: Maximum time to search before giving up
153
**kwargs: Additional scanner arguments
154
155
Returns:
156
BLEDevice if found, None otherwise
157
"""
158
```
159
160
### Discovery Results Access
161
162
Access discovered devices and their advertisement data through scanner properties.
163
164
```python { .api }
165
@property
166
def discovered_devices(self) -> list[BLEDevice]:
167
"""List of discovered devices during scanning."""
168
169
@property
170
def discovered_devices_and_advertisement_data(
171
self,
172
) -> dict[str, tuple[BLEDevice, AdvertisementData]]:
173
"""
174
Map of device addresses to (device, advertisement_data) tuples.
175
176
Returns:
177
Dict with device addresses as keys and (BLEDevice, AdvertisementData) as values
178
"""
179
```
180
181
## Usage Examples
182
183
### Basic Device Discovery
184
185
```python
186
import asyncio
187
from bleak import BleakScanner
188
189
async def discover_devices():
190
# Simple discovery with timeout
191
devices = await BleakScanner.discover(timeout=10.0)
192
193
for device in devices:
194
print(f"Found: {device.name} ({device.address})")
195
196
asyncio.run(discover_devices())
197
```
198
199
### Discovery with Advertisement Data
200
201
```python
202
import asyncio
203
from bleak import BleakScanner
204
205
async def discover_with_data():
206
# Get both devices and advertisement data
207
discovered = await BleakScanner.discover(timeout=10.0, return_adv=True)
208
209
for address, (device, adv_data) in discovered.items():
210
print(f"Device: {device.name} ({address})")
211
print(f" RSSI: {adv_data.rssi} dBm")
212
print(f" Services: {adv_data.service_uuids}")
213
print(f" Manufacturer: {adv_data.manufacturer_data}")
214
215
asyncio.run(discover_with_data())
216
```
217
218
### Continuous Scanning with Callbacks
219
220
```python
221
import asyncio
222
from bleak import BleakScanner, BLEDevice, AdvertisementData
223
224
def detection_callback(device: BLEDevice, advertisement_data: AdvertisementData):
225
print(f"Detected: {device.name} ({device.address}) RSSI: {advertisement_data.rssi}")
226
227
async def continuous_scan():
228
scanner = BleakScanner(detection_callback=detection_callback)
229
230
async with scanner: # Auto start/stop
231
await asyncio.sleep(30) # Scan for 30 seconds
232
233
asyncio.run(continuous_scan())
234
```
235
236
### Filtered Discovery
237
238
```python
239
import asyncio
240
from bleak import BleakScanner
241
242
async def find_heart_rate_monitors():
243
# Find devices advertising Heart Rate service
244
heart_rate_uuid = "0000180d-0000-1000-8000-00805f9b34fb"
245
246
devices = await BleakScanner.discover(
247
timeout=10.0,
248
service_uuids=[heart_rate_uuid]
249
)
250
251
for device in devices:
252
print(f"Heart Rate Monitor: {device.name} ({device.address})")
253
254
asyncio.run(find_heart_rate_monitors())
255
```
256
257
### Platform-Specific Configuration
258
259
```python
260
import asyncio
261
from bleak import BleakScanner
262
from bleak.args.bluez import BlueZScannerArgs
263
264
async def linux_specific_scan():
265
# BlueZ-specific configuration
266
bluez_args = BlueZScannerArgs(
267
filters={"RSSI": -50} # Only devices with RSSI > -50 dBm
268
)
269
270
devices = await BleakScanner.discover(
271
timeout=10.0,
272
bluez=bluez_args
273
)
274
275
for device in devices:
276
print(f"Strong signal device: {device.name} ({device.address})")
277
278
# Only run on Linux
279
if platform.system() == "Linux":
280
asyncio.run(linux_specific_scan())
281
```
282
283
## Types
284
285
```python { .api }
286
# Scanner arguments by platform
287
class BlueZScannerArgs(TypedDict, total=False):
288
filters: BlueZDiscoveryFilters
289
or_patterns: list[OrPatternLike]
290
291
class CBScannerArgs(TypedDict, total=False):
292
use_bdaddr: bool
293
294
# Discovery filter configuration for BlueZ
295
class BlueZDiscoveryFilters(TypedDict, total=False):
296
UUIDs: list[str]
297
RSSI: int
298
Pathloss: int
299
Transport: str
300
DuplicateData: bool
301
Discoverable: bool
302
Pattern: str
303
304
# Advertisement data callback type
305
AdvertisementDataCallback = Callable[
306
[BLEDevice, AdvertisementData],
307
Optional[Coroutine[Any, Any, None]],
308
]
309
310
# Advertisement data filter type
311
AdvertisementDataFilter = Callable[
312
[BLEDevice, AdvertisementData],
313
bool,
314
]
315
```