From 6517e3e21e5828a317ea8b814d0345f2536b9170 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 11 Nov 2022 02:09:28 -0600 Subject: [PATCH] Fix esphome bleak client seeing disconnects too late (#81932) * Fix esphome bleak client seeing disconnects too late Because allbacks are delivered asynchronously its possible that we find out during the operation before the callback is delivered telling us about the disconnected. We now watch for error code -1 which indicates the connection dropped out from under us * debug logging * cleanup comment * Fix comment grammar Co-authored-by: Martin Hjelmare --- .../components/esphome/bluetooth/client.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/homeassistant/components/esphome/bluetooth/client.py b/homeassistant/components/esphome/bluetooth/client.py index c6b60831577..ceac4e5aaae 100644 --- a/homeassistant/components/esphome/bluetooth/client.py +++ b/homeassistant/components/esphome/bluetooth/client.py @@ -13,6 +13,7 @@ from aioesphomeapi import ( BLEConnectionError, ) from aioesphomeapi.connection import APIConnectionError, TimeoutAPIError +from aioesphomeapi.core import BluetoothGATTAPIError import async_timeout from bleak.backends.characteristic import BleakGATTCharacteristic from bleak.backends.client import BaseBleakClient, NotifyCallback @@ -83,6 +84,24 @@ def api_error_as_bleak_error(func: _WrapFuncType) -> _WrapFuncType: return await func(self, *args, **kwargs) except TimeoutAPIError as err: raise asyncio.TimeoutError(str(err)) from err + except BluetoothGATTAPIError as ex: + # If the device disconnects in the middle of an operation + # be sure to mark it as disconnected so any library using + # the proxy knows to reconnect. + # + # Because callbacks are delivered asynchronously it's possible + # that we find out about the disconnection during the operation + # before the callback is delivered. + if ex.error.error == -1: + _LOGGER.debug( + "%s: %s - %s: BLE device disconnected during %s operation", + self._source, # pylint: disable=protected-access + self._ble_device.name, # pylint: disable=protected-access + self._ble_device.address, # pylint: disable=protected-access + func.__name__, + ) + self._async_ble_device_disconnected() # pylint: disable=protected-access + raise BleakError(str(ex)) from ex except APIConnectionError as err: raise BleakError(str(err)) from err