Fix esphome ble client leaking notify on disconnect (#83106)

* Fix esphome ble client leaking notify on disconnect

needs: https://github.com/esphome/aioesphomeapi/pull/329

* leak

* more cleanup

* more cleanup

* bump
This commit is contained in:
J. Nick Koston 2022-12-02 14:45:49 -10:00 committed by GitHub
parent 03154e1d83
commit de1e97a81f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 24 additions and 14 deletions

View File

@ -142,7 +142,9 @@ class ESPHomeClient(BaseBleakClient):
self._is_connected = False self._is_connected = False
self._mtu: int | None = None self._mtu: int | None = None
self._cancel_connection_state: CALLBACK_TYPE | None = None self._cancel_connection_state: CALLBACK_TYPE | None = None
self._notify_cancels: dict[int, Callable[[], Coroutine[Any, Any, None]]] = {} self._notify_cancels: dict[
int, tuple[Callable[[], Coroutine[Any, Any, None]], Callable[[], None]]
] = {}
self._disconnected_event: asyncio.Event | None = None self._disconnected_event: asyncio.Event | None = None
device_info = self.entry_data.device_info device_info = self.entry_data.device_info
assert device_info is not None assert device_info is not None
@ -169,15 +171,22 @@ class ESPHomeClient(BaseBleakClient):
) )
self._cancel_connection_state = None self._cancel_connection_state = None
def _async_ble_device_disconnected(self) -> None: def _async_disconnected_cleanup(self) -> None:
"""Handle the BLE device disconnecting from the ESP.""" """Clean up on disconnect."""
was_connected = self._is_connected
self.services = BleakGATTServiceCollection() # type: ignore[no-untyped-call] self.services = BleakGATTServiceCollection() # type: ignore[no-untyped-call]
self._is_connected = False self._is_connected = False
for _, notify_abort in self._notify_cancels.values():
notify_abort()
self._notify_cancels.clear() self._notify_cancels.clear()
if self._disconnected_event: if self._disconnected_event:
self._disconnected_event.set() self._disconnected_event.set()
self._disconnected_event = None self._disconnected_event = None
self._unsubscribe_connection_state()
def _async_ble_device_disconnected(self) -> None:
"""Handle the BLE device disconnecting from the ESP."""
was_connected = self._is_connected
self._async_disconnected_cleanup()
if was_connected: if was_connected:
_LOGGER.debug( _LOGGER.debug(
"%s: %s - %s: BLE device disconnected", "%s: %s - %s: BLE device disconnected",
@ -186,7 +195,6 @@ class ESPHomeClient(BaseBleakClient):
self._ble_device.address, self._ble_device.address,
) )
self._async_call_bleak_disconnected_callback() self._async_call_bleak_disconnected_callback()
self._unsubscribe_connection_state()
def _async_esp_disconnected(self) -> None: def _async_esp_disconnected(self) -> None:
"""Handle the esp32 client disconnecting from hass.""" """Handle the esp32 client disconnecting from hass."""
@ -316,7 +324,7 @@ class ESPHomeClient(BaseBleakClient):
@api_error_as_bleak_error @api_error_as_bleak_error
async def disconnect(self) -> bool: async def disconnect(self) -> bool:
"""Disconnect from the peripheral device.""" """Disconnect from the peripheral device."""
self._unsubscribe_connection_state() self._async_disconnected_cleanup()
await self._client.bluetooth_device_disconnect(self._address_as_int) await self._client.bluetooth_device_disconnect(self._address_as_int)
await self._wait_for_free_connection_slot(DISCONNECT_TIMEOUT) await self._wait_for_free_connection_slot(DISCONNECT_TIMEOUT)
return True return True
@ -551,12 +559,13 @@ class ESPHomeClient(BaseBleakClient):
f"Characteristic {characteristic.uuid} does not have notify or indicate property set." f"Characteristic {characteristic.uuid} does not have notify or indicate property set."
) )
cancel_coro = await self._client.bluetooth_gatt_start_notify( self._notify_cancels[
ble_handle
] = await self._client.bluetooth_gatt_start_notify(
self._address_as_int, self._address_as_int,
ble_handle, ble_handle,
lambda handle, data: callback(data), lambda handle, data: callback(data),
) )
self._notify_cancels[ble_handle] = cancel_coro
if self._connection_version < MIN_BLUETOOTH_PROXY_VERSION_HAS_CACHE: if self._connection_version < MIN_BLUETOOTH_PROXY_VERSION_HAS_CACHE:
return return
@ -604,8 +613,9 @@ class ESPHomeClient(BaseBleakClient):
characteristic = self._resolve_characteristic(char_specifier) characteristic = self._resolve_characteristic(char_specifier)
# Do not raise KeyError if notifications are not enabled on this characteristic # Do not raise KeyError if notifications are not enabled on this characteristic
# to be consistent with the behavior of the BlueZ backend # to be consistent with the behavior of the BlueZ backend
if coro := self._notify_cancels.pop(characteristic.handle, None): if notify_cancel := self._notify_cancels.pop(characteristic.handle, None):
await coro() notify_stop, _ = notify_cancel
await notify_stop()
def __del__(self) -> None: def __del__(self) -> None:
"""Destructor to make sure the connection state is unsubscribed.""" """Destructor to make sure the connection state is unsubscribed."""
@ -617,4 +627,4 @@ class ESPHomeClient(BaseBleakClient):
self._ble_device.address, self._ble_device.address,
) )
if not self._hass.loop.is_closed(): if not self._hass.loop.is_closed():
self._hass.loop.call_soon_threadsafe(self._unsubscribe_connection_state) self._hass.loop.call_soon_threadsafe(self._async_disconnected_cleanup)

View File

@ -3,7 +3,7 @@
"name": "ESPHome", "name": "ESPHome",
"config_flow": true, "config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/esphome", "documentation": "https://www.home-assistant.io/integrations/esphome",
"requirements": ["aioesphomeapi==12.2.1"], "requirements": ["aioesphomeapi==13.0.0"],
"zeroconf": ["_esphomelib._tcp.local."], "zeroconf": ["_esphomelib._tcp.local."],
"dhcp": [{ "registered_devices": true }], "dhcp": [{ "registered_devices": true }],
"codeowners": ["@OttoWinter", "@jesserockz"], "codeowners": ["@OttoWinter", "@jesserockz"],

View File

@ -156,7 +156,7 @@ aioecowitt==2022.11.0
aioemonitor==1.0.5 aioemonitor==1.0.5
# homeassistant.components.esphome # homeassistant.components.esphome
aioesphomeapi==12.2.1 aioesphomeapi==13.0.0
# homeassistant.components.flo # homeassistant.components.flo
aioflo==2021.11.0 aioflo==2021.11.0

View File

@ -143,7 +143,7 @@ aioecowitt==2022.11.0
aioemonitor==1.0.5 aioemonitor==1.0.5
# homeassistant.components.esphome # homeassistant.components.esphome
aioesphomeapi==12.2.1 aioesphomeapi==13.0.0
# homeassistant.components.flo # homeassistant.components.flo
aioflo==2021.11.0 aioflo==2021.11.0