mirror of https://github.com/home-assistant/core
Fix ESPHome bluetooth client cancel behavior when device unexpectedly disconnects (#96918)
This commit is contained in:
parent
a2b18e46b9
commit
7814ce06f4
|
@ -4,7 +4,6 @@ from __future__ import annotations
|
|||
import asyncio
|
||||
from collections.abc import Callable, Coroutine
|
||||
import contextlib
|
||||
from functools import partial
|
||||
import logging
|
||||
from typing import Any, TypeVar, cast
|
||||
import uuid
|
||||
|
@ -17,6 +16,7 @@ from aioesphomeapi import (
|
|||
)
|
||||
from aioesphomeapi.connection import APIConnectionError, TimeoutAPIError
|
||||
from aioesphomeapi.core import BluetoothGATTAPIError
|
||||
from async_interrupt import interrupt
|
||||
import async_timeout
|
||||
from bleak.backends.characteristic import BleakGATTCharacteristic
|
||||
from bleak.backends.client import BaseBleakClient, NotifyCallback
|
||||
|
@ -57,11 +57,6 @@ def mac_to_int(address: str) -> int:
|
|||
return int(address.replace(":", ""), 16)
|
||||
|
||||
|
||||
def _on_disconnected(task: asyncio.Task[Any], _: asyncio.Future[None]) -> None:
|
||||
if task and not task.done():
|
||||
task.cancel()
|
||||
|
||||
|
||||
def verify_connected(func: _WrapFuncType) -> _WrapFuncType:
|
||||
"""Define a wrapper throw BleakError if not connected."""
|
||||
|
||||
|
@ -72,25 +67,17 @@ def verify_connected(func: _WrapFuncType) -> _WrapFuncType:
|
|||
loop = self._loop
|
||||
disconnected_futures = self._disconnected_futures
|
||||
disconnected_future = loop.create_future()
|
||||
disconnect_handler = partial(_on_disconnected, asyncio.current_task(loop))
|
||||
disconnected_future.add_done_callback(disconnect_handler)
|
||||
disconnected_futures.add(disconnected_future)
|
||||
ble_device = self._ble_device
|
||||
disconnect_message = (
|
||||
f"{self._source_name }: {ble_device.name} - {ble_device.address}: "
|
||||
"Disconnected during operation"
|
||||
)
|
||||
try:
|
||||
return await func(self, *args, **kwargs)
|
||||
except asyncio.CancelledError as ex:
|
||||
if not disconnected_future.done():
|
||||
# If the disconnected future is not done, the task was cancelled
|
||||
# externally and we need to raise cancelled error to avoid
|
||||
# blocking the cancellation.
|
||||
raise
|
||||
ble_device = self._ble_device
|
||||
raise BleakError(
|
||||
f"{self._source_name }: {ble_device.name} - {ble_device.address}: "
|
||||
"Disconnected during operation"
|
||||
) from ex
|
||||
async with interrupt(disconnected_future, BleakError, disconnect_message):
|
||||
return await func(self, *args, **kwargs)
|
||||
finally:
|
||||
disconnected_futures.discard(disconnected_future)
|
||||
disconnected_future.remove_done_callback(disconnect_handler)
|
||||
|
||||
return cast(_WrapFuncType, _async_wrap_bluetooth_connected_operation)
|
||||
|
||||
|
@ -340,7 +327,7 @@ class ESPHomeClient(BaseBleakClient):
|
|||
# exception.
|
||||
await connected_future
|
||||
raise
|
||||
except Exception:
|
||||
except Exception as ex:
|
||||
if connected_future.done():
|
||||
with contextlib.suppress(BleakError):
|
||||
# If the connect call throws an exception,
|
||||
|
@ -350,7 +337,7 @@ class ESPHomeClient(BaseBleakClient):
|
|||
# exception from the connect call as it
|
||||
# will be more descriptive.
|
||||
await connected_future
|
||||
connected_future.cancel()
|
||||
connected_future.cancel(f"Unhandled exception in connect call: {ex}")
|
||||
raise
|
||||
await connected_future
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
"iot_class": "local_push",
|
||||
"loggers": ["aioesphomeapi", "noiseprotocol"],
|
||||
"requirements": [
|
||||
"async_interrupt==1.1.1",
|
||||
"aioesphomeapi==15.1.14",
|
||||
"bluetooth-data-tools==1.6.0",
|
||||
"esphome-dashboard-api==1.2.3"
|
||||
|
|
|
@ -442,6 +442,9 @@ asterisk-mbox==0.5.0
|
|||
# homeassistant.components.yeelight
|
||||
async-upnp-client==0.33.2
|
||||
|
||||
# homeassistant.components.esphome
|
||||
async_interrupt==1.1.1
|
||||
|
||||
# homeassistant.components.keyboard_remote
|
||||
asyncinotify==4.0.2
|
||||
|
||||
|
|
|
@ -396,6 +396,9 @@ arcam-fmj==1.4.0
|
|||
# homeassistant.components.yeelight
|
||||
async-upnp-client==0.33.2
|
||||
|
||||
# homeassistant.components.esphome
|
||||
async_interrupt==1.1.1
|
||||
|
||||
# homeassistant.components.sleepiq
|
||||
asyncsleepiq==1.3.5
|
||||
|
||||
|
|
Loading…
Reference in New Issue