BleakScanner配合asyncio.wait_for时超时异常问题求助
环境信息
- bleak版本: 0.22.2
- Python版本: 3.8.18
- 操作系统: Linux
- BlueZ版本 (
bluetoothctl -v): 5.64
问题描述
使用带超时设置的BleakScanner时,asyncio.wait_for函数返回后过20秒仍触发超时错误,后续扫描会被DBus的Operation already in progress错误阻断。
操作场景
网关以轮询方式持续连接4台同类型设备,运行下方代码后,尽管asyncio.wait_for(_scan_with_timeout(filter, device_id), timeout)已返回,但还是触发了asyncio.TimeoutError日志,后续扫描抛出BleakDBusError('org.bluez.Error.InProgress', 'Operation already in progress')。
代码实现:
async def scan( device_id, timeout=SCAN_TIMEOUT_S ) -> Optional[Tuple[BLEDevice, Set[str]]]: try: filter = get_device_filter(device_id) return await asyncio.wait_for(_scan_with_timeout(filter, device_id), timeout) except asyncio.TimeoutError: logger.info( f"Timeout reached. Device {device_id} not found after {timeout} seconds" ) return None async def _scan_with_timeout(filter, device_id): seen = set() async with BleakScanner() as scanner: async for device, advertisement in scanner.advertisement_data(): found_device_id = get_device_id(device) if found_device_id and found_device_id.startswith(filter): logger.info(f"Found {device!r}") logger.debug(f"{advertisement!r}") logger.debug(f"platform_data: {advertisement.platform_data}") logger.debug(f"service_data: {advertisement.service_data}") seen.add(found_device_id) if found_device_id == device_id: logger.info(f"Connecting") return (device, seen)
关键日志
2024-08-13 15:25:10,427 | INFO | xtel | Connecting 2024-08-13 15:25:30,122 | INFO | xtel | Timeout reached. Device D0:83:D4:00:A2:D1 not found after 30 seconds
解决方案
问题根源
当_scan_with_timeout执行return时,async with BleakScanner()的上下文管理器未及时终止BlueZ端的扫描操作:
async for循环退出后,BleakScanner的后台扫描任务仍在运行,导致DBus上的扫描进程未终止;asyncio.wait_for的超时定时器因事件循环调度延迟,在函数返回后才触发回调;- 未终止的扫描会导致后续启动新扫描时,BlueZ抛出
Operation already in progress错误。
修复步骤
在return前手动调用scanner.stop(),确保扫描操作立即终止,再退出上下文管理器:
修改后的_scan_with_timeout函数:
async def _scan_with_timeout(filter, device_id): seen = set() async with BleakScanner() as scanner: async for device, advertisement in scanner.advertisement_data(): found_device_id = get_device_id(device) if found_device_id and found_device_id.startswith(filter): logger.info(f"Found {device!r}") logger.debug(f"{advertisement!r}") logger.debug(f"platform_data: {advertisement.platform_data}") logger.debug(f"service_data: {advertisement.service_data}") seen.add(found_device_id) if found_device_id == device_id: logger.info(f"Connecting") # 手动停止扫描,确保BlueZ端的扫描进程终止 await scanner.stop() return (device, seen)
额外建议
- 可在
scan函数的finally块中添加扫描状态检查,确保每次扫描后资源都被正确释放; - 轮询场景下,建议在两次扫描之间添加短暂延迟,避免频繁操作导致BlueZ资源占用异常。
内容的提问来源于stack exchange,提问作者icc97
相关产品推荐
相关产品推荐

