bleak icon indicating copy to clipboard operation
bleak copied to clipboard

BleakClient.connect() only succeeds when timeout parameter is set (ServicesResolved never arrives otherwise)

Open vgamero opened this issue 3 months ago • 1 comments

I tried this simple code to connect to a Polar H10. Using a BleakClient without passing a timeout parameter, the connection almost never succeeds it ends with a TimeoutError. However, when setting timeout=15–20, the connection becomes stable and ServicesResolved is reached successfully. The documentation mentions that setting a timeout is not recommended, but without it, the device never completes service discovery.

def on_disconnect(client: BleakClient):
    """Triggered automatically when the device disconnects."""
    print("Device has been disconnected.")


async def connect_device(client: BleakClient) -> BleakClient | None:
    """Establish a BLE connection using a BleakClient instance."""
    try:
        # Connection only succeeds reliably on BlueZ when timeout is explicitly set
        await client.connect(timeout=20)
    except BluetoothConnectionError as e:
        print(f"Connection error ({type(e).__name__}): {e}")
        return None

    if client.is_connected:
        print("Device connected successfully.")
        return client

    print("Connection failed after connect().")
    return None


async def disconnect_device(client: BleakClient):
    """Controlled disconnection."""
    if client.is_connected:
        try:
            await client.disconnect()
            print("Device disconnected successfully.")
        except Exception as e:
            print(f"Error while disconnecting ({type(e).__name__}): {e}")
    else:
        print("No active connection to disconnect.")


async def hr_monitor():
    devices = await scan_devices(filter_name="polar", show=True)
    if not devices:
        return

    polar = devices[0]
    client = BleakClient(polar, disconnected_callback=on_disconnect)
    print(f"Attempting to connect to {polar.name}...")

    client = await connect_device(client)
    if not client:
        print("Could not connect to the device.")
        return

    try:
        while True:
            await aio.sleep(1)
    finally:
        await disconnect_device(client)
        print("Disconnected by user.")


if __name__ == "__main__":
    try:
        aio.run(hr_monitor())
    except KeyboardInterrupt:
        print("\nStopped by user.")

Environment:

  • OS: Debian 12
  • BlueZ: 5.66
  • Python: 3.10
  • Bleak: 1.1
  • Peripheral: Polar H10 (firmware up to date)

1- Scrip Error: Traceback (most recent call last): File ".../bleak/backends/bluezdbus/client.py", line 355, in connect await self._get_services( File ".../bleak/backends/bluezdbus/client.py", line 713, in _get_services self.services = await manager.get_services( File ".../bleak/backends/bluezdbus/manager.py", line 687, in get_services await self._wait_for_services_discovery(device_path) File ".../bleak/backends/bluezdbus/manager.py", line 841, in _wait_for_services_discovery done, _ = await asyncio.wait(...) asyncio.exceptions.CancelledError

During handling of the above exception, another exception occurred:

Traceback (most recent call last): File ".../bleak/init.py", line 580, in connect await self._backend.connect(self._pair_before_connect, **kwargs) File ".../bleak/backends/bluezdbus/client.py", line 156, in connect async with async_timeout(timeout): File ".../async_timeout/init.py", line 265, in aexit raise asyncio.TimeoutError asyncio.exceptions.TimeoutError

What I checked:

  • D-Bus: Connected=True arrives, but ServicesResolved=True does not arrive in the failing case.
  • HCI/ATT (Wireshark): link up + MTU exchange are visible; when it fails, I don’t see the expected Read By Group Type Response (0x11) following Read By Group Type (0x10), so BlueZ appears to wait indefinitely for service discovery to finish. With timeout=20, the 0x11 responses show up and discovery completes.

Questions / ask

1- Is this a known BlueZ-backend quirk that requires an explicit timeout for some peripherals? 2- Possibly related to: #1806 (service discovery/ServicesResolved behavior)?

vgamero avatar Oct 14 '25 03:10 vgamero

Some devices can very slow advertisement intervals and so can take longer to connect like this. According to the book I read about it, taking up to 30 seconds to connect is normal.

From your notes on the Wireshark capture though, it sounds like the device is actually connecting faster, but just not sending responses very fast when enumerating the services.

So I think setting a longer timeout for this specific device seems reasonable.

An explicit timeout doesn't change anything in how BlueZ acts, it is purely implemented in Python.

dlech avatar Oct 14 '25 14:10 dlech