BleakClient.connect() only succeeds when timeout parameter is set (ServicesResolved never arrives otherwise)
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)?
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.