mirror of
https://github.com/home-assistant/core.git
synced 2025-07-20 03:37:07 +00:00
Fix bluetooth adapters with missing firmware patch files not being discovered (#81926)
This commit is contained in:
parent
2813101418
commit
78180b2ad8
@ -3,6 +3,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
from asyncio import Future
|
from asyncio import Future
|
||||||
from collections.abc import Callable, Iterable
|
from collections.abc import Callable, Iterable
|
||||||
|
import datetime
|
||||||
import logging
|
import logging
|
||||||
import platform
|
import platform
|
||||||
from typing import TYPE_CHECKING, cast
|
from typing import TYPE_CHECKING, cast
|
||||||
@ -21,6 +22,7 @@ from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback as hass_ca
|
|||||||
from homeassistant.exceptions import ConfigEntryNotReady
|
from homeassistant.exceptions import ConfigEntryNotReady
|
||||||
from homeassistant.helpers import device_registry as dr, discovery_flow
|
from homeassistant.helpers import device_registry as dr, discovery_flow
|
||||||
from homeassistant.helpers.debounce import Debouncer
|
from homeassistant.helpers.debounce import Debouncer
|
||||||
|
from homeassistant.helpers.event import async_call_later
|
||||||
from homeassistant.helpers.issue_registry import (
|
from homeassistant.helpers.issue_registry import (
|
||||||
IssueSeverity,
|
IssueSeverity,
|
||||||
async_create_issue,
|
async_create_issue,
|
||||||
@ -33,6 +35,7 @@ from .const import (
|
|||||||
ADAPTER_ADDRESS,
|
ADAPTER_ADDRESS,
|
||||||
ADAPTER_HW_VERSION,
|
ADAPTER_HW_VERSION,
|
||||||
ADAPTER_SW_VERSION,
|
ADAPTER_SW_VERSION,
|
||||||
|
BLUETOOTH_DISCOVERY_COOLDOWN_SECONDS,
|
||||||
CONF_ADAPTER,
|
CONF_ADAPTER,
|
||||||
CONF_DETAILS,
|
CONF_DETAILS,
|
||||||
CONF_PASSIVE,
|
CONF_PASSIVE,
|
||||||
@ -40,6 +43,7 @@ from .const import (
|
|||||||
DEFAULT_ADDRESS,
|
DEFAULT_ADDRESS,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS,
|
FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS,
|
||||||
|
LINUX_FIRMWARE_LOAD_FALLBACK_SECONDS,
|
||||||
SOURCE_LOCAL,
|
SOURCE_LOCAL,
|
||||||
AdapterDetails,
|
AdapterDetails,
|
||||||
)
|
)
|
||||||
@ -298,9 +302,17 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
|||||||
await async_discover_adapters(hass, discovered_adapters)
|
await async_discover_adapters(hass, discovered_adapters)
|
||||||
|
|
||||||
discovery_debouncer = Debouncer(
|
discovery_debouncer = Debouncer(
|
||||||
hass, _LOGGER, cooldown=5, immediate=False, function=_async_rediscover_adapters
|
hass,
|
||||||
|
_LOGGER,
|
||||||
|
cooldown=BLUETOOTH_DISCOVERY_COOLDOWN_SECONDS,
|
||||||
|
immediate=False,
|
||||||
|
function=_async_rediscover_adapters,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
async def _async_call_debouncer(now: datetime.datetime) -> None:
|
||||||
|
"""Call the debouncer at a later time."""
|
||||||
|
await discovery_debouncer.async_call()
|
||||||
|
|
||||||
def _async_trigger_discovery() -> None:
|
def _async_trigger_discovery() -> None:
|
||||||
# There are so many bluetooth adapter models that
|
# There are so many bluetooth adapter models that
|
||||||
# we check the bus whenever a usb device is plugged in
|
# we check the bus whenever a usb device is plugged in
|
||||||
@ -310,6 +322,17 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
|||||||
# present.
|
# present.
|
||||||
_LOGGER.debug("Triggering bluetooth usb discovery")
|
_LOGGER.debug("Triggering bluetooth usb discovery")
|
||||||
hass.async_create_task(discovery_debouncer.async_call())
|
hass.async_create_task(discovery_debouncer.async_call())
|
||||||
|
# Because it can take 120s for the firmware loader
|
||||||
|
# fallback to timeout we need to wait that plus
|
||||||
|
# the debounce time to ensure we do not miss the
|
||||||
|
# adapter becoming available to DBus since otherwise
|
||||||
|
# we will never see the new adapter until
|
||||||
|
# Home Assistant is restarted
|
||||||
|
async_call_later(
|
||||||
|
hass,
|
||||||
|
BLUETOOTH_DISCOVERY_COOLDOWN_SECONDS + LINUX_FIRMWARE_LOAD_FALLBACK_SECONDS,
|
||||||
|
_async_call_debouncer,
|
||||||
|
)
|
||||||
|
|
||||||
cancel = usb.async_register_scan_request_callback(hass, _async_trigger_discovery)
|
cancel = usb.async_register_scan_request_callback(hass, _async_trigger_discovery)
|
||||||
hass.bus.async_listen_once(
|
hass.bus.async_listen_once(
|
||||||
|
@ -59,6 +59,15 @@ SCANNER_WATCHDOG_TIMEOUT: Final = 90
|
|||||||
SCANNER_WATCHDOG_INTERVAL: Final = timedelta(seconds=30)
|
SCANNER_WATCHDOG_INTERVAL: Final = timedelta(seconds=30)
|
||||||
|
|
||||||
|
|
||||||
|
# When the linux kernel is configured with
|
||||||
|
# CONFIG_FW_LOADER_USER_HELPER_FALLBACK it
|
||||||
|
# can take up to 120s before the USB device
|
||||||
|
# is available if the firmware files
|
||||||
|
# are not present
|
||||||
|
LINUX_FIRMWARE_LOAD_FALLBACK_SECONDS = 120
|
||||||
|
BLUETOOTH_DISCOVERY_COOLDOWN_SECONDS = 5
|
||||||
|
|
||||||
|
|
||||||
class AdapterDetails(TypedDict, total=False):
|
class AdapterDetails(TypedDict, total=False):
|
||||||
"""Adapter details."""
|
"""Adapter details."""
|
||||||
|
|
||||||
|
@ -20,9 +20,11 @@ from homeassistant.components.bluetooth import (
|
|||||||
scanner,
|
scanner,
|
||||||
)
|
)
|
||||||
from homeassistant.components.bluetooth.const import (
|
from homeassistant.components.bluetooth.const import (
|
||||||
|
BLUETOOTH_DISCOVERY_COOLDOWN_SECONDS,
|
||||||
CONF_PASSIVE,
|
CONF_PASSIVE,
|
||||||
DEFAULT_ADDRESS,
|
DEFAULT_ADDRESS,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
|
LINUX_FIRMWARE_LOAD_FALLBACK_SECONDS,
|
||||||
SOURCE_LOCAL,
|
SOURCE_LOCAL,
|
||||||
UNAVAILABLE_TRACK_SECONDS,
|
UNAVAILABLE_TRACK_SECONDS,
|
||||||
)
|
)
|
||||||
@ -2737,6 +2739,81 @@ async def test_discover_new_usb_adapters(hass, mock_bleak_scanner_start, one_ada
|
|||||||
assert len(hass.config_entries.flow.async_progress(DOMAIN)) == 1
|
assert len(hass.config_entries.flow.async_progress(DOMAIN)) == 1
|
||||||
|
|
||||||
|
|
||||||
|
async def test_discover_new_usb_adapters_with_firmware_fallback_delay(
|
||||||
|
hass, mock_bleak_scanner_start, one_adapter
|
||||||
|
):
|
||||||
|
"""Test we can discover new usb adapters with a firmware fallback delay."""
|
||||||
|
entry = MockConfigEntry(
|
||||||
|
domain=bluetooth.DOMAIN, data={}, unique_id="00:00:00:00:00:01"
|
||||||
|
)
|
||||||
|
entry.add_to_hass(hass)
|
||||||
|
|
||||||
|
saved_callback = None
|
||||||
|
|
||||||
|
def _async_register_scan_request_callback(_hass, _callback):
|
||||||
|
nonlocal saved_callback
|
||||||
|
saved_callback = _callback
|
||||||
|
return lambda: None
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.bluetooth.usb.async_register_scan_request_callback",
|
||||||
|
_async_register_scan_request_callback,
|
||||||
|
):
|
||||||
|
assert await async_setup_component(hass, bluetooth.DOMAIN, {})
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert not hass.config_entries.flow.async_progress(DOMAIN)
|
||||||
|
|
||||||
|
saved_callback()
|
||||||
|
assert not hass.config_entries.flow.async_progress(DOMAIN)
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.bluetooth.util.platform.system", return_value="Linux"
|
||||||
|
), patch(
|
||||||
|
"bluetooth_adapters.get_bluetooth_adapter_details",
|
||||||
|
return_value={},
|
||||||
|
):
|
||||||
|
async_fire_time_changed(
|
||||||
|
hass, dt_util.utcnow() + timedelta(BLUETOOTH_DISCOVERY_COOLDOWN_SECONDS * 2)
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert len(hass.config_entries.flow.async_progress(DOMAIN)) == 0
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.bluetooth.util.platform.system", return_value="Linux"
|
||||||
|
), patch(
|
||||||
|
"bluetooth_adapters.get_bluetooth_adapter_details",
|
||||||
|
return_value={
|
||||||
|
"hci0": {
|
||||||
|
"org.bluez.Adapter1": {
|
||||||
|
"Address": "00:00:00:00:00:01",
|
||||||
|
"Name": "BlueZ 4.63",
|
||||||
|
"Modalias": "usbid:1234",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"hci1": {
|
||||||
|
"org.bluez.Adapter1": {
|
||||||
|
"Address": "00:00:00:00:00:02",
|
||||||
|
"Name": "BlueZ 4.63",
|
||||||
|
"Modalias": "usbid:1234",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
):
|
||||||
|
async_fire_time_changed(
|
||||||
|
hass,
|
||||||
|
dt_util.utcnow()
|
||||||
|
+ timedelta(
|
||||||
|
seconds=LINUX_FIRMWARE_LOAD_FALLBACK_SECONDS
|
||||||
|
+ (BLUETOOTH_DISCOVERY_COOLDOWN_SECONDS * 2)
|
||||||
|
),
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert len(hass.config_entries.flow.async_progress(DOMAIN)) == 1
|
||||||
|
|
||||||
|
|
||||||
async def test_issue_outdated_haos(
|
async def test_issue_outdated_haos(
|
||||||
hass, mock_bleak_scanner_start, one_adapter, operating_system_85
|
hass, mock_bleak_scanner_start, one_adapter, operating_system_85
|
||||||
):
|
):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user