Skip to content

Commit 4e371bf

Browse files
committed
examples/passive_scan: Add example using passive scanning mode
In case of BlueZ, using passive scanning mode is not simply passing "passive" as a ``scan_mode`` parameter, but requires more arguments. This example showcases that.
1 parent 8ab27d6 commit 4e371bf

File tree

1 file changed

+128
-0
lines changed

1 file changed

+128
-0
lines changed

examples/passive_scan.py

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
"""
2+
Scanner using passive scanning mode
3+
--------------
4+
5+
Example similar to detection_callback.py, but using passive scanning
6+
7+
Updated on 2022-11-24 by bojanpotocnik <[email protected]>
8+
9+
"""
10+
import argparse
11+
import asyncio
12+
import logging
13+
from typing import Optional, List, Dict, Any
14+
15+
import bleak
16+
from bleak import AdvertisementData, BLEDevice, BleakScanner
17+
18+
logger = logging.getLogger(__name__)
19+
20+
21+
def _get_os_specific_scanning_params(
22+
uuids: Optional[List[str]],
23+
rssi: Optional[int] = None,
24+
macos_use_bdaddr: bool = False,
25+
) -> Dict[str, Any]:
26+
def get_bluez_dbus_scanning_params() -> Dict[str, Any]:
27+
from bleak.assigned_numbers import AdvertisementDataType
28+
from bleak.backends.bluezdbus.advertisement_monitor import OrPattern
29+
from bleak.backends.bluezdbus.scanner import (
30+
BlueZScannerArgs,
31+
BlueZDiscoveryFilters,
32+
)
33+
34+
filters = BlueZDiscoveryFilters(
35+
# UUIDs= Added below, because it cannot be None
36+
# RSSI= Added below, because it cannot be None
37+
Transport="le",
38+
DuplicateData=True,
39+
)
40+
if uuids:
41+
filters["UUIDs"] = uuids
42+
if rssi:
43+
filters["RSSI"] = rssi
44+
45+
# or_patterns ar required for BlueZ passive scanning
46+
or_patterns = [
47+
# General Discoverable (peripherals)
48+
OrPattern(0, AdvertisementDataType.FLAGS, b"\x02"),
49+
# BR/EDR Not Supported (BLE peripherals)
50+
OrPattern(0, AdvertisementDataType.FLAGS, b"\x04"),
51+
# General Discoverable, BR/EDR Not Supported (BLE peripherals)
52+
OrPattern(0, AdvertisementDataType.FLAGS, b"\x06"),
53+
# General Discoverable, LE and BR/EDR Capable (Controller), LE and BR/EDR Capable (Host) (computers, phones)
54+
OrPattern(0, AdvertisementDataType.FLAGS, b"\x1A"),
55+
]
56+
57+
return {"bluez": BlueZScannerArgs(filters=filters, or_patterns=or_patterns)}
58+
59+
def get_core_bluetooth_scanning_params() -> Dict[str, Any]:
60+
from bleak.backends.corebluetooth.scanner import CBScannerArgs
61+
62+
return {"cb": CBScannerArgs(use_bdaddr=macos_use_bdaddr)}
63+
64+
return {
65+
"BleakScannerBlueZDBus": get_bluez_dbus_scanning_params,
66+
"BleakScannerCoreBluetooth": get_core_bluetooth_scanning_params,
67+
# "BleakScannerP4Android": get_p4android_scanning_params,
68+
# "BleakScannerWinRT": get_winrt_scanning_params,
69+
}.get(bleak.get_platform_scanner_backend_type().__name__, lambda: {})()
70+
71+
72+
async def scan(args: argparse.Namespace, passive_mode: bool):
73+
def scan_callback(device: BLEDevice, adv_data: AdvertisementData):
74+
logger.info("%s: %r", device.address, adv_data)
75+
76+
async with BleakScanner(
77+
detection_callback=scan_callback,
78+
**_get_os_specific_scanning_params(
79+
uuids=args.services, macos_use_bdaddr=args.macos_use_bdaddr
80+
),
81+
scanning_mode="passive" if passive_mode else "active",
82+
):
83+
await asyncio.sleep(60)
84+
85+
86+
async def main(args: argparse.Namespace):
87+
try:
88+
await scan(args, passive_mode=True)
89+
except bleak.exc.BleakNoPassiveScanError as e:
90+
if args.fallback:
91+
logger.warning(
92+
f"Passive scanning not possible, using active scanning ({e})"
93+
)
94+
await scan(args, passive_mode=False)
95+
else:
96+
logger.error(f"Passive scanning not possible ({e})")
97+
98+
99+
if __name__ == "__main__":
100+
logging.basicConfig(
101+
level=logging.INFO,
102+
format="%(asctime)-15s %(name)-8s %(levelname)s: %(message)s",
103+
)
104+
105+
parser = argparse.ArgumentParser()
106+
parser.add_argument(
107+
"--fallback",
108+
action="store_true",
109+
help="fallback to active scanning mode if passive mode is not possible",
110+
)
111+
parser.add_argument(
112+
"--macos-use-bdaddr",
113+
action="store_true",
114+
help="when true use Bluetooth address instead of UUID on macOS",
115+
)
116+
parser.add_argument(
117+
"--services",
118+
metavar="<uuid>",
119+
nargs="*",
120+
help="UUIDs of one or more services to filter for",
121+
)
122+
123+
arguments = parser.parse_args()
124+
125+
try:
126+
asyncio.run(main(arguments))
127+
except KeyboardInterrupt:
128+
pass

0 commit comments

Comments
 (0)