mirror of
https://github.com/home-assistant/core.git
synced 2025-07-19 11:17:21 +00:00
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
|
import asyncio
|
||||||
from collections.abc import Callable, Coroutine
|
from collections.abc import Callable, Coroutine
|
||||||
import contextlib
|
import contextlib
|
||||||
from functools import partial
|
|
||||||
import logging
|
import logging
|
||||||
from typing import Any, TypeVar, cast
|
from typing import Any, TypeVar, cast
|
||||||
import uuid
|
import uuid
|
||||||
@ -17,6 +16,7 @@ from aioesphomeapi import (
|
|||||||
)
|
)
|
||||||
from aioesphomeapi.connection import APIConnectionError, TimeoutAPIError
|
from aioesphomeapi.connection import APIConnectionError, TimeoutAPIError
|
||||||
from aioesphomeapi.core import BluetoothGATTAPIError
|
from aioesphomeapi.core import BluetoothGATTAPIError
|
||||||
|
from async_interrupt import interrupt
|
||||||
import async_timeout
|
import async_timeout
|
||||||
from bleak.backends.characteristic import BleakGATTCharacteristic
|
from bleak.backends.characteristic import BleakGATTCharacteristic
|
||||||
from bleak.backends.client import BaseBleakClient, NotifyCallback
|
from bleak.backends.client import BaseBleakClient, NotifyCallback
|
||||||
@ -57,11 +57,6 @@ def mac_to_int(address: str) -> int:
|
|||||||
return int(address.replace(":", ""), 16)
|
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:
|
def verify_connected(func: _WrapFuncType) -> _WrapFuncType:
|
||||||
"""Define a wrapper throw BleakError if not connected."""
|
"""Define a wrapper throw BleakError if not connected."""
|
||||||
|
|
||||||
@ -72,25 +67,17 @@ def verify_connected(func: _WrapFuncType) -> _WrapFuncType:
|
|||||||
loop = self._loop
|
loop = self._loop
|
||||||
disconnected_futures = self._disconnected_futures
|
disconnected_futures = self._disconnected_futures
|
||||||
disconnected_future = loop.create_future()
|
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)
|
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:
|
try:
|
||||||
return await func(self, *args, **kwargs)
|
async with interrupt(disconnected_future, BleakError, disconnect_message):
|
||||||
except asyncio.CancelledError as ex:
|
return await func(self, *args, **kwargs)
|
||||||
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
|
|
||||||
finally:
|
finally:
|
||||||
disconnected_futures.discard(disconnected_future)
|
disconnected_futures.discard(disconnected_future)
|
||||||
disconnected_future.remove_done_callback(disconnect_handler)
|
|
||||||
|
|
||||||
return cast(_WrapFuncType, _async_wrap_bluetooth_connected_operation)
|
return cast(_WrapFuncType, _async_wrap_bluetooth_connected_operation)
|
||||||
|
|
||||||
@ -340,7 +327,7 @@ class ESPHomeClient(BaseBleakClient):
|
|||||||
# exception.
|
# exception.
|
||||||
await connected_future
|
await connected_future
|
||||||
raise
|
raise
|
||||||
except Exception:
|
except Exception as ex:
|
||||||
if connected_future.done():
|
if connected_future.done():
|
||||||
with contextlib.suppress(BleakError):
|
with contextlib.suppress(BleakError):
|
||||||
# If the connect call throws an exception,
|
# If the connect call throws an exception,
|
||||||
@ -350,7 +337,7 @@ class ESPHomeClient(BaseBleakClient):
|
|||||||
# exception from the connect call as it
|
# exception from the connect call as it
|
||||||
# will be more descriptive.
|
# will be more descriptive.
|
||||||
await connected_future
|
await connected_future
|
||||||
connected_future.cancel()
|
connected_future.cancel(f"Unhandled exception in connect call: {ex}")
|
||||||
raise
|
raise
|
||||||
await connected_future
|
await connected_future
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
"iot_class": "local_push",
|
"iot_class": "local_push",
|
||||||
"loggers": ["aioesphomeapi", "noiseprotocol"],
|
"loggers": ["aioesphomeapi", "noiseprotocol"],
|
||||||
"requirements": [
|
"requirements": [
|
||||||
|
"async_interrupt==1.1.1",
|
||||||
"aioesphomeapi==15.1.14",
|
"aioesphomeapi==15.1.14",
|
||||||
"bluetooth-data-tools==1.6.0",
|
"bluetooth-data-tools==1.6.0",
|
||||||
"esphome-dashboard-api==1.2.3"
|
"esphome-dashboard-api==1.2.3"
|
||||||
|
@ -442,6 +442,9 @@ asterisk-mbox==0.5.0
|
|||||||
# homeassistant.components.yeelight
|
# homeassistant.components.yeelight
|
||||||
async-upnp-client==0.33.2
|
async-upnp-client==0.33.2
|
||||||
|
|
||||||
|
# homeassistant.components.esphome
|
||||||
|
async_interrupt==1.1.1
|
||||||
|
|
||||||
# homeassistant.components.keyboard_remote
|
# homeassistant.components.keyboard_remote
|
||||||
asyncinotify==4.0.2
|
asyncinotify==4.0.2
|
||||||
|
|
||||||
|
@ -396,6 +396,9 @@ arcam-fmj==1.4.0
|
|||||||
# homeassistant.components.yeelight
|
# homeassistant.components.yeelight
|
||||||
async-upnp-client==0.33.2
|
async-upnp-client==0.33.2
|
||||||
|
|
||||||
|
# homeassistant.components.esphome
|
||||||
|
async_interrupt==1.1.1
|
||||||
|
|
||||||
# homeassistant.components.sleepiq
|
# homeassistant.components.sleepiq
|
||||||
asyncsleepiq==1.3.5
|
asyncsleepiq==1.3.5
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user