or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

data-structures.mddevice-discovery.mdexception-handling.mdgatt-client.mdindex.mduuid-utilities.md

device-discovery.mddocs/

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

```