mirror of
https://github.com/home-assistant/core.git
synced 2025-07-22 04:37:06 +00:00
Fix missing services with esp32 proxies (#83192)
This commit is contained in:
parent
5422a17a35
commit
955d4abf13
@ -7,7 +7,7 @@
|
|||||||
"quality_scale": "internal",
|
"quality_scale": "internal",
|
||||||
"requirements": [
|
"requirements": [
|
||||||
"bleak==0.19.2",
|
"bleak==0.19.2",
|
||||||
"bleak-retry-connector==2.8.9",
|
"bleak-retry-connector==2.9.0",
|
||||||
"bluetooth-adapters==0.11.0",
|
"bluetooth-adapters==0.11.0",
|
||||||
"bluetooth-auto-recovery==0.5.4",
|
"bluetooth-auto-recovery==0.5.4",
|
||||||
"bluetooth-data-tools==0.3.0",
|
"bluetooth-data-tools==0.3.0",
|
||||||
|
@ -12,7 +12,7 @@ from bleak import BleakClient, BleakError
|
|||||||
from bleak.backends.client import BaseBleakClient, get_platform_client_backend_type
|
from bleak.backends.client import BaseBleakClient, get_platform_client_backend_type
|
||||||
from bleak.backends.device import BLEDevice
|
from bleak.backends.device import BLEDevice
|
||||||
from bleak.backends.scanner import AdvertisementDataCallback, BaseBleakScanner
|
from bleak.backends.scanner import AdvertisementDataCallback, BaseBleakScanner
|
||||||
from bleak_retry_connector import NO_RSSI_VALUE, ble_device_description
|
from bleak_retry_connector import NO_RSSI_VALUE, ble_device_description, clear_cache
|
||||||
|
|
||||||
from homeassistant.core import CALLBACK_TYPE, callback as hass_callback
|
from homeassistant.core import CALLBACK_TYPE, callback as hass_callback
|
||||||
from homeassistant.helpers.frame import report
|
from homeassistant.helpers.frame import report
|
||||||
@ -169,6 +169,12 @@ class HaBleakClientWrapper(BleakClient):
|
|||||||
"""Return True if the client is connected to a device."""
|
"""Return True if the client is connected to a device."""
|
||||||
return self._backend is not None and self._backend.is_connected
|
return self._backend is not None and self._backend.is_connected
|
||||||
|
|
||||||
|
async def clear_cache(self) -> bool:
|
||||||
|
"""Clear the GATT cache."""
|
||||||
|
if self._backend is not None and hasattr(self._backend, "clear_cache"):
|
||||||
|
return await self._backend.clear_cache() # type: ignore[no-any-return]
|
||||||
|
return await clear_cache(self.__address)
|
||||||
|
|
||||||
def set_disconnected_callback(
|
def set_disconnected_callback(
|
||||||
self,
|
self,
|
||||||
callback: Callable[[BleakClient], None] | None,
|
callback: Callable[[BleakClient], None] | None,
|
||||||
|
@ -449,6 +449,11 @@ class ESPHomeClient(BaseBleakClient):
|
|||||||
raise BleakError(f"Characteristic {char_specifier} was not found!")
|
raise BleakError(f"Characteristic {char_specifier} was not found!")
|
||||||
return characteristic
|
return characteristic
|
||||||
|
|
||||||
|
async def clear_cache(self) -> None:
|
||||||
|
"""Clear the GATT cache."""
|
||||||
|
self.entry_data.clear_gatt_services_cache(self._address_as_int)
|
||||||
|
self.entry_data.clear_gatt_mtu_cache(self._address_as_int)
|
||||||
|
|
||||||
@verify_connected
|
@verify_connected
|
||||||
@api_error_as_bleak_error
|
@api_error_as_bleak_error
|
||||||
async def read_gatt_char(
|
async def read_gatt_char(
|
||||||
|
@ -119,6 +119,10 @@ class RuntimeEntryData:
|
|||||||
"""Set the BleakGATTServiceCollection for the given address."""
|
"""Set the BleakGATTServiceCollection for the given address."""
|
||||||
self._gatt_services_cache[address] = services
|
self._gatt_services_cache[address] = services
|
||||||
|
|
||||||
|
def clear_gatt_services_cache(self, address: int) -> None:
|
||||||
|
"""Clear the BleakGATTServiceCollection for the given address."""
|
||||||
|
self._gatt_services_cache.pop(address, None)
|
||||||
|
|
||||||
def get_gatt_mtu_cache(self, address: int) -> int | None:
|
def get_gatt_mtu_cache(self, address: int) -> int | None:
|
||||||
"""Get the mtu cache for the given address."""
|
"""Get the mtu cache for the given address."""
|
||||||
return self._gatt_mtu_cache.get(address)
|
return self._gatt_mtu_cache.get(address)
|
||||||
@ -127,6 +131,10 @@ class RuntimeEntryData:
|
|||||||
"""Set the mtu cache for the given address."""
|
"""Set the mtu cache for the given address."""
|
||||||
self._gatt_mtu_cache[address] = mtu
|
self._gatt_mtu_cache[address] = mtu
|
||||||
|
|
||||||
|
def clear_gatt_mtu_cache(self, address: int) -> None:
|
||||||
|
"""Clear the mtu cache for the given address."""
|
||||||
|
self._gatt_mtu_cache.pop(address, None)
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_update_ble_connection_limits(self, free: int, limit: int) -> None:
|
def async_update_ble_connection_limits(self, free: int, limit: int) -> None:
|
||||||
"""Update the BLE connection limits."""
|
"""Update the BLE connection limits."""
|
||||||
|
@ -10,7 +10,7 @@ atomicwrites-homeassistant==1.4.1
|
|||||||
attrs==21.2.0
|
attrs==21.2.0
|
||||||
awesomeversion==22.9.0
|
awesomeversion==22.9.0
|
||||||
bcrypt==3.1.7
|
bcrypt==3.1.7
|
||||||
bleak-retry-connector==2.8.9
|
bleak-retry-connector==2.9.0
|
||||||
bleak==0.19.2
|
bleak==0.19.2
|
||||||
bluetooth-adapters==0.11.0
|
bluetooth-adapters==0.11.0
|
||||||
bluetooth-auto-recovery==0.5.4
|
bluetooth-auto-recovery==0.5.4
|
||||||
|
@ -422,7 +422,7 @@ bimmer_connected==0.10.4
|
|||||||
bizkaibus==0.1.1
|
bizkaibus==0.1.1
|
||||||
|
|
||||||
# homeassistant.components.bluetooth
|
# homeassistant.components.bluetooth
|
||||||
bleak-retry-connector==2.8.9
|
bleak-retry-connector==2.9.0
|
||||||
|
|
||||||
# homeassistant.components.bluetooth
|
# homeassistant.components.bluetooth
|
||||||
bleak==0.19.2
|
bleak==0.19.2
|
||||||
|
@ -346,7 +346,7 @@ bellows==0.34.5
|
|||||||
bimmer_connected==0.10.4
|
bimmer_connected==0.10.4
|
||||||
|
|
||||||
# homeassistant.components.bluetooth
|
# homeassistant.components.bluetooth
|
||||||
bleak-retry-connector==2.8.9
|
bleak-retry-connector==2.9.0
|
||||||
|
|
||||||
# homeassistant.components.bluetooth
|
# homeassistant.components.bluetooth
|
||||||
bleak==0.19.2
|
bleak==0.19.2
|
||||||
|
@ -219,3 +219,7 @@ class MockBleakClient(BleakClient):
|
|||||||
async def get_services(self, *args, **kwargs):
|
async def get_services(self, *args, **kwargs):
|
||||||
"""Mock get_services."""
|
"""Mock get_services."""
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
async def clear_cache(self, *args, **kwargs):
|
||||||
|
"""Mock clear_cache."""
|
||||||
|
return True
|
||||||
|
@ -45,6 +45,7 @@ async def test_wrapped_bleak_client_raises_device_missing(hass, enable_bluetooth
|
|||||||
await client.connect()
|
await client.connect()
|
||||||
assert client.is_connected is False
|
assert client.is_connected is False
|
||||||
await client.disconnect()
|
await client.disconnect()
|
||||||
|
assert await client.clear_cache() is False
|
||||||
|
|
||||||
|
|
||||||
async def test_wrapped_bleak_client_set_disconnected_callback_before_connected(
|
async def test_wrapped_bleak_client_set_disconnected_callback_before_connected(
|
||||||
@ -168,6 +169,62 @@ async def test_ble_device_with_proxy_client_out_of_connections(
|
|||||||
await client.disconnect()
|
await client.disconnect()
|
||||||
|
|
||||||
|
|
||||||
|
async def test_ble_device_with_proxy_clear_cache(hass, enable_bluetooth, one_adapter):
|
||||||
|
"""Test we can clear cache on the proxy."""
|
||||||
|
manager = _get_manager()
|
||||||
|
|
||||||
|
switchbot_proxy_device_with_connection_slot = BLEDevice(
|
||||||
|
"44:44:33:11:23:45",
|
||||||
|
"wohand",
|
||||||
|
{
|
||||||
|
"connector": HaBluetoothConnector(
|
||||||
|
MockBleakClient, "mock_bleak_client", lambda: True
|
||||||
|
),
|
||||||
|
"path": "/org/bluez/hci0/dev_44_44_33_11_23_45",
|
||||||
|
},
|
||||||
|
rssi=-30,
|
||||||
|
)
|
||||||
|
switchbot_adv = generate_advertisement_data(
|
||||||
|
local_name="wohand", service_uuids=[], manufacturer_data={1: b"\x01"}
|
||||||
|
)
|
||||||
|
|
||||||
|
class FakeScanner(BaseHaScanner):
|
||||||
|
@property
|
||||||
|
def discovered_devices_and_advertisement_data(
|
||||||
|
self,
|
||||||
|
) -> dict[str, tuple[BLEDevice, AdvertisementData]]:
|
||||||
|
"""Return a list of discovered devices."""
|
||||||
|
return {
|
||||||
|
switchbot_proxy_device_with_connection_slot.address: (
|
||||||
|
switchbot_proxy_device_with_connection_slot,
|
||||||
|
switchbot_adv,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
async def async_get_device_by_address(self, address: str) -> BLEDevice | None:
|
||||||
|
"""Return a list of discovered devices."""
|
||||||
|
if address == switchbot_proxy_device_with_connection_slot.address:
|
||||||
|
return switchbot_adv
|
||||||
|
return None
|
||||||
|
|
||||||
|
scanner = FakeScanner(hass, "esp32", "esp32")
|
||||||
|
cancel = manager.async_register_scanner(scanner, True)
|
||||||
|
inject_advertisement_with_source(
|
||||||
|
hass, switchbot_proxy_device_with_connection_slot, switchbot_adv, "esp32"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert manager.async_discovered_devices(True) == [
|
||||||
|
switchbot_proxy_device_with_connection_slot
|
||||||
|
]
|
||||||
|
|
||||||
|
client = HaBleakClientWrapper(switchbot_proxy_device_with_connection_slot)
|
||||||
|
await client.connect()
|
||||||
|
assert client.is_connected is True
|
||||||
|
assert await client.clear_cache() is True
|
||||||
|
await client.disconnect()
|
||||||
|
cancel()
|
||||||
|
|
||||||
|
|
||||||
async def test_ble_device_with_proxy_client_out_of_connections_uses_best_available(
|
async def test_ble_device_with_proxy_client_out_of_connections_uses_best_available(
|
||||||
hass, enable_bluetooth, one_adapter
|
hass, enable_bluetooth, one_adapter
|
||||||
):
|
):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user