diff --git a/aioesphomeapi/client.py b/aioesphomeapi/client.py index 9f6187f..030e391 100644 --- a/aioesphomeapi/client.py +++ b/aioesphomeapi/client.py @@ -163,6 +163,7 @@ from .model import ( _LOGGER = logging.getLogger(__name__) DEFAULT_BLE_TIMEOUT = 30.0 +DEFAULT_BLE_DISCONNECT_TIMEOUT = 5.0 ExecuteServiceDataType = Dict[ str, Union[bool, int, float, str, List[bool], List[int], List[float], List[str]] @@ -490,6 +491,7 @@ class APIClient: address: int, on_bluetooth_connection_state: Callable[[bool, int, int], None], timeout: float = DEFAULT_BLE_TIMEOUT, + disconnect_timeout: float = DEFAULT_BLE_DISCONNECT_TIMEOUT, ) -> Callable[[], None]: self._check_authenticated() @@ -519,19 +521,30 @@ class APIClient: async with async_timeout.timeout(timeout): await event.wait() except asyncio.TimeoutError as err: + # Disconnect before raising the exception to ensure + # the slot is recovered before the timeout is raised + # to avoid race were we run out even though we have a slot. + await self.bluetooth_device_disconnect(address) + addr = to_human_readable_address(address) + _LOGGER.debug("%s: Connecting timed out, waiting for disconnect", addr) + try: + async with async_timeout.timeout(disconnect_timeout): + await event.wait() + disconnect_timed_out = False + except asyncio.TimeoutError: + disconnect_timed_out = True + _LOGGER.debug("%s: Disconnect timed out: %s", addr, disconnect_timed_out) try: unsub() except ValueError: _LOGGER.warning( "%s: Bluetooth device connection timed out but already unsubscribed", - to_human_readable_address(address), + addr, ) - # Disconnect before raising the exception to ensure - # the slot is recovered before the timeout is raised - # to avoid race were we run out even though we have a slot. - await self.bluetooth_device_disconnect(address) raise TimeoutAPIError( - f"Timeout waiting for connect response while connecting to {to_human_readable_address(address)} after {timeout}s" + f"Timeout waiting for connect response while connecting to {addr} " + f"after {timeout}s, disconnect timed out: {disconnect_timed_out}, " + f" after {disconnect_timeout}s" ) from err return unsub