You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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端的扫描操作:

  1. async for循环退出后,BleakScanner的后台扫描任务仍在运行,导致DBus上的扫描进程未终止;
  2. asyncio.wait_for的超时定时器因事件循环调度延迟,在函数返回后才触发回调;
  3. 未终止的扫描会导致后续启动新扫描时,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)

额外建议

  1. 可在scan函数的finally块中添加扫描状态检查,确保每次扫描后资源都被正确释放;
  2. 轮询场景下,建议在两次扫描之间添加短暂延迟,避免频繁操作导致BlueZ资源占用异常。

内容的提问来源于stack exchange,提问作者icc97

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 14:16:02