Load the Silicon Labs flasher only when needed (#183283)

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Franck Nijhof
2026-09-27 13:13:53 +02:00
committed by GitHub
co-authored by Claude
parent 75ad9e5c03
commit 6d1815c9d8
17 changed files with 133 additions and 71 deletions
@@ -3,8 +3,6 @@
import logging
from typing import TYPE_CHECKING, Any, Protocol, override
from universal_silabs_flasher.flasher import Zbt2Flasher
from homeassistant.components import usb
from homeassistant.components.homeassistant_hardware import firmware_config_flow
from homeassistant.components.homeassistant_hardware.helpers import (
@@ -13,6 +11,7 @@ from homeassistant.components.homeassistant_hardware.helpers import (
from homeassistant.components.homeassistant_hardware.util import (
ApplicationType,
FirmwareInfo,
FlasherType,
)
from homeassistant.components.usb import usb_service_info_from_device
from homeassistant.config_entries import (
@@ -72,7 +71,7 @@ class ZBT2FirmwareMixin(ConfigEntryBaseFlow, FirmwareInstallFlowProtocol):
context: ConfigFlowContext
ZIGBEE_BAUDRATE = 460800
_flasher_cls = Zbt2Flasher
_flasher_type = FlasherType.ZBT2
async def async_step_install_zigbee_firmware(
self, user_input: dict[str, Any] | None = None
@@ -3,8 +3,6 @@
import logging
from typing import override
from universal_silabs_flasher.flasher import Zbt2Flasher
from homeassistant.components.homeassistant_hardware.coordinator import (
FirmwareUpdateCoordinator,
)
@@ -15,6 +13,7 @@ from homeassistant.components.homeassistant_hardware.update import (
from homeassistant.components.homeassistant_hardware.util import (
ApplicationType,
FirmwareInfo,
FlasherType,
)
from homeassistant.components.update import UpdateDeviceClass
from homeassistant.const import EntityCategory
@@ -134,7 +133,7 @@ async def async_setup_entry(
class FirmwareUpdateEntity(BaseFirmwareUpdateEntity):
"""Connect ZBT-2 firmware update entity."""
_flasher_cls = Zbt2Flasher
_flasher_type = FlasherType.ZBT2
def __init__(
self,
@@ -8,9 +8,6 @@ from typing import Any, override
from aiohttp import ClientError
from ha_silabs_firmware_client import FirmwareUpdateClient, ManifestMissing
from universal_silabs_flasher.common import Version
from universal_silabs_flasher.firmware import NabuCasaMetadata
from universal_silabs_flasher.flasher import DeviceSpecificFlasher
from homeassistant.components.hassio import (
AddonError,
@@ -31,15 +28,20 @@ from homeassistant.data_entry_flow import AbortFlow
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.hassio import is_hassio
from homeassistant.helpers.importlib import async_import_module
from .const import DOMAIN, OTBR_DOMAIN, Z2M_EMBER_DOCS_URL, ZHA_DOMAIN
from .util import (
COMMON_MODULE,
FIRMWARE_MODULE,
ApplicationType,
FirmwareInfo,
FlasherType,
OwningAddon,
OwningIntegration,
async_firmware_flashing_context,
async_flash_silabs_firmware,
async_get_flasher_cls,
get_otbr_addon_manager,
guess_firmware_info,
guess_hardware_owners,
@@ -79,7 +81,7 @@ class BaseFirmwareInstallFlow(ConfigEntryBaseFlow, ABC):
"""Base flow to install firmware."""
ZIGBEE_BAUDRATE = 115200 # Default, subclasses may override
_flasher_cls: type[DeviceSpecificFlasher]
_flasher_type: FlasherType
_picked_firmware_type: PickedFirmwareType
_zigbee_flow_strategy: ZigbeeFlowStrategy = ZigbeeFlowStrategy.RECOMMENDED
@@ -234,9 +236,10 @@ class BaseFirmwareInstallFlow(ConfigEntryBaseFlow, ABC):
# Installing new firmware is only truly required if the wrong type is
# installed: upgrading to the latest release of the current firmware type
# isn't strictly necessary for functionality.
flasher_cls = await async_get_flasher_cls(self.hass, self._flasher_type)
self._probed_firmware_info = await probe_silabs_firmware_info(
self._device,
flasher_cls=self._flasher_cls,
flasher_cls=flasher_cls,
)
firmware_install_required = self._probed_firmware_info is None or (
@@ -273,9 +276,15 @@ class BaseFirmwareInstallFlow(ConfigEntryBaseFlow, ABC):
assert self._probed_firmware_info is not None
# Make sure we do not downgrade the firmware
fw_metadata = NabuCasaMetadata.from_json(fw_manifest.metadata)
firmware_module = await async_import_module(self.hass, FIRMWARE_MODULE)
common_module = await async_import_module(self.hass, COMMON_MODULE)
fw_metadata = firmware_module.NabuCasaMetadata.from_json(
fw_manifest.metadata
)
fw_version = fw_metadata.get_public_version()
probed_fw_version = Version(self._probed_firmware_info.firmware_version)
probed_fw_version = common_module.Version(
self._probed_firmware_info.firmware_version
)
if probed_fw_version >= fw_version:
_LOGGER.debug(
@@ -308,7 +317,7 @@ class BaseFirmwareInstallFlow(ConfigEntryBaseFlow, ABC):
hass=self.hass,
device=self._device,
fw_data=fw_data,
flasher_cls=self._flasher_cls,
flasher_cls=flasher_cls,
expected_installed_firmware_type=expected_installed_firmware_type,
progress_callback=lambda offset, total: self.async_update_progress(
offset / total
@@ -43,9 +43,11 @@ from homeassistant.helpers.storage import Store
from .const import DOMAIN, LOGGER, SILABS_MULTIPROTOCOL_ADDON_SLUG
from .util import (
ApplicationType,
FlasherType,
WaitingAddonManager,
async_firmware_flashing_context,
async_flash_silabs_firmware,
async_get_flasher_cls,
)
_LOGGER = logging.getLogger(__name__)
@@ -296,8 +298,8 @@ class OptionsFlowHandler(OptionsFlow, ABC):
@property
@abstractmethod
def _flasher_cls(self) -> type:
"""Return the hardware-specific flasher class."""
def _flasher_type(self) -> FlasherType:
"""Return the hardware-specific flasher type."""
@property
def flow_manager(self) -> OptionsFlowManager:
@@ -757,7 +759,9 @@ class OptionsFlowHandler(OptionsFlow, ABC):
hass=self.hass,
device=device,
fw_data=fw_data,
flasher_cls=self._flasher_cls,
flasher_cls=await async_get_flasher_cls(
self.hass, self._flasher_type
),
expected_installed_firmware_type=ApplicationType.EZSP,
progress_callback=lambda offset, total: (
self.async_update_progress(offset / total)
@@ -8,7 +8,6 @@ from typing import Any, cast, override
from aiohasupervisor import SupervisorError
from aiohasupervisor.models import RaspberryPiFirmwareInfo
from ha_silabs_firmware_client import FirmwareManifest, FirmwareMetadata
from universal_silabs_flasher.flasher import DeviceSpecificFlasher
from yarl import URL
from homeassistant.components.update import (
@@ -28,8 +27,10 @@ from .helpers import async_register_firmware_info_callback
from .util import (
ApplicationType,
FirmwareInfo,
FlasherType,
async_firmware_flashing_context,
async_flash_silabs_firmware,
async_get_flasher_cls,
async_get_raspberry_pi_firmware_info,
async_update_raspberry_pi_firmware,
humanize_rpi_firmware_version,
@@ -99,7 +100,7 @@ class BaseFirmwareUpdateEntity(
UpdateEntityFeature.INSTALL | UpdateEntityFeature.PROGRESS
)
_attr_has_entity_name = True
_flasher_cls: type[DeviceSpecificFlasher]
_flasher_type: FlasherType
def __init__(
self,
@@ -292,7 +293,9 @@ class BaseFirmwareUpdateEntity(
hass=self.hass,
device=self._current_device,
fw_data=fw_data,
flasher_cls=self._flasher_cls,
flasher_cls=await async_get_flasher_cls(
self.hass, self._flasher_type
),
expected_installed_firmware_type=self.entity_description.expected_firmware_type,
progress_callback=self._update_progress,
)
@@ -7,12 +7,10 @@ from contextlib import AsyncExitStack, asynccontextmanager
from dataclasses import dataclass
from enum import StrEnum
import logging
from typing import TYPE_CHECKING, cast
from aiohasupervisor import SupervisorError, SupervisorNotFoundError
from aiohasupervisor.models import RaspberryPiFirmwareInfo
from universal_silabs_flasher.const import ApplicationType as FlasherApplicationType
from universal_silabs_flasher.firmware import parse_firmware_image
from universal_silabs_flasher.flasher import BaseFlasher, DeviceSpecificFlasher, Flasher
from homeassistant.components.hassio import (
AddonError,
@@ -26,6 +24,7 @@ from homeassistant.config_entries import ConfigEntryState
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.hassio import is_hassio
from homeassistant.helpers.importlib import async_import_module
from homeassistant.helpers.singleton import singleton
from homeassistant.util import dt as dt_util
@@ -39,8 +38,17 @@ from .const import (
)
from .helpers import async_firmware_update_context
if TYPE_CHECKING:
from universal_silabs_flasher.const import ApplicationType as FlasherApplicationType
from universal_silabs_flasher.flasher import BaseFlasher, DeviceSpecificFlasher
_LOGGER = logging.getLogger(__name__)
# The flasher library pulls in the full Zigbee stack, only load it when used
FLASHER_MODULE = "universal_silabs_flasher.flasher"
COMMON_MODULE = "universal_silabs_flasher.common"
FIRMWARE_MODULE = "universal_silabs_flasher.firmware"
ADDON_STATE_POLL_INTERVAL = 3
ADDON_INFO_POLL_TIMEOUT = 15 * 60
@@ -109,10 +117,33 @@ class ApplicationType(StrEnum):
return cls(app_type.value)
def as_flasher_application_type(self) -> FlasherApplicationType:
"""Convert the application type enum into one compatible with USF."""
"""Convert the application type enum into one compatible with USF.
Only call this once the flasher library has been imported.
"""
from universal_silabs_flasher.const import ( # noqa: PLC0415
ApplicationType as FlasherApplicationType,
)
return FlasherApplicationType(self.value)
class FlasherType(StrEnum):
"""Device specific flasher class of the flasher library."""
YELLOW = "YellowFlasher"
ZBT1 = "Zbt1Flasher"
ZBT2 = "Zbt2Flasher"
async def async_get_flasher_cls(
hass: HomeAssistant, flasher_type: FlasherType
) -> type[DeviceSpecificFlasher]:
"""Return a device specific flasher class, importing the flasher on first use."""
flasher_module = await async_import_module(hass, FLASHER_MODULE)
return cast("type[DeviceSpecificFlasher]", getattr(flasher_module, flasher_type))
@singleton(OTBR_ADDON_MANAGER_DATA)
@callback
def get_otbr_addon_manager(hass: HomeAssistant) -> WaitingAddonManager:
@@ -424,12 +455,14 @@ async def probe_silabs_firmware_info(
async def probe_silabs_firmware_type(
hass: HomeAssistant,
device: str,
*,
application_probe_methods: Sequence[tuple[ApplicationType, int]],
) -> ApplicationType | None:
"""Probe the running firmware type on a SiLabs device."""
flasher = Flasher(
flasher_module = await async_import_module(hass, FLASHER_MODULE)
flasher = flasher_module.Flasher(
device=device,
probe_methods=[
(m.as_flasher_application_type(), b) for m, b in application_probe_methods
@@ -476,7 +509,10 @@ async def async_flash_silabs_firmware(
This function is meant to be used within a firmware update context.
"""
fw_image = await hass.async_add_executor_job(parse_firmware_image, fw_data)
firmware_module = await async_import_module(hass, FIRMWARE_MODULE)
fw_image = await hass.async_add_executor_job(
firmware_module.parse_firmware_image, fw_data
)
flasher = flasher_cls(device=device)
@@ -3,8 +3,6 @@
import logging
from typing import TYPE_CHECKING, Any, Protocol, override
from universal_silabs_flasher.flasher import Zbt1Flasher
from homeassistant.components import usb
from homeassistant.components.homeassistant_hardware import (
firmware_config_flow,
@@ -16,6 +14,7 @@ from homeassistant.components.homeassistant_hardware.helpers import (
from homeassistant.components.homeassistant_hardware.util import (
ApplicationType,
FirmwareInfo,
FlasherType,
)
from homeassistant.components.usb import usb_service_info_from_device
from homeassistant.config_entries import (
@@ -77,7 +76,7 @@ class SkyConnectFirmwareMixin(ConfigEntryBaseFlow, FirmwareInstallFlowProtocol):
context: ConfigFlowContext
ZIGBEE_BAUDRATE = 115200
_flasher_cls = Zbt1Flasher
_flasher_type = FlasherType.ZBT1
@override
def _get_translation_placeholders(self) -> dict[str, str]:
@@ -268,9 +267,9 @@ class HomeAssistantSkyConnectMultiPanOptionsFlowHandler(
@property
@override
def _flasher_cls(self) -> type:
"""Return the hardware-specific flasher class."""
return Zbt1Flasher # type: ignore[no-any-return]
def _flasher_type(self) -> FlasherType:
"""Return the hardware-specific flasher type."""
return FlasherType.ZBT1
@override
async def async_step_flashing_complete(
@@ -3,8 +3,6 @@
import logging
from typing import override
from universal_silabs_flasher.flasher import Zbt1Flasher
from homeassistant.components.homeassistant_hardware.coordinator import (
FirmwareUpdateCoordinator,
)
@@ -15,6 +13,7 @@ from homeassistant.components.homeassistant_hardware.update import (
from homeassistant.components.homeassistant_hardware.util import (
ApplicationType,
FirmwareInfo,
FlasherType,
)
from homeassistant.components.update import UpdateDeviceClass
from homeassistant.const import EntityCategory
@@ -152,7 +151,7 @@ async def async_setup_entry(
class FirmwareUpdateEntity(BaseFirmwareUpdateEntity):
"""SkyConnect firmware update entity."""
_flasher_cls = Zbt1Flasher
_flasher_type = FlasherType.ZBT1
def __init__(
self,
@@ -6,7 +6,6 @@ import logging
from typing import TYPE_CHECKING, Any, Protocol, final, override
import probatio
from universal_silabs_flasher.flasher import YellowFlasher
from homeassistant.components.hassio import (
SupervisorError,
@@ -24,6 +23,8 @@ from homeassistant.components.homeassistant_hardware.silabs_multiprotocol_addon
from homeassistant.components.homeassistant_hardware.util import (
ApplicationType,
FirmwareInfo,
FlasherType,
async_get_flasher_cls,
probe_silabs_firmware_info,
)
from homeassistant.config_entries import (
@@ -81,7 +82,7 @@ class YellowFirmwareMixin(ConfigEntryBaseFlow, FirmwareInstallFlowProtocol):
"""Mixin for Home Assistant Yellow firmware methods."""
ZIGBEE_BAUDRATE = 115200
_flasher_cls = YellowFlasher
_flasher_type = FlasherType.YELLOW
async def async_step_install_zigbee_firmware(
self, user_input: dict[str, Any] | None = None
@@ -148,7 +149,7 @@ class HomeAssistantYellowConfigFlow(
# We do not actually use any portion of `BaseFirmwareConfigFlow` beyond this
self._probed_firmware_info = await probe_silabs_firmware_info(
self._device,
flasher_cls=self._flasher_cls,
flasher_cls=await async_get_flasher_cls(self.hass, self._flasher_type),
)
# Kick off ZHA hardware discovery automatically if Zigbee firmware is running
@@ -338,9 +339,9 @@ class HomeAssistantYellowMultiPanOptionsFlowHandler(
@property
@override
def _flasher_cls(self) -> type:
"""Return the hardware-specific flasher class."""
return YellowFlasher # type: ignore[no-any-return]
def _flasher_type(self) -> FlasherType:
"""Return the hardware-specific flasher type."""
return FlasherType.YELLOW
@override
async def async_step_flashing_complete(
@@ -4,7 +4,6 @@ import logging
from typing import override
from aiohasupervisor import SupervisorError
from universal_silabs_flasher.flasher import YellowFlasher
from homeassistant.components.homeassistant_hardware.coordinator import (
FirmwareUpdateCoordinator,
@@ -17,6 +16,7 @@ from homeassistant.components.homeassistant_hardware.update import (
from homeassistant.components.homeassistant_hardware.util import (
ApplicationType,
FirmwareInfo,
FlasherType,
async_get_raspberry_pi_firmware_info,
)
from homeassistant.components.update import UpdateDeviceClass, UpdateEntity
@@ -178,7 +178,7 @@ async def async_setup_entry(
class FirmwareUpdateEntity(BaseFirmwareUpdateEntity):
"""Yellow firmware update entity."""
_flasher_cls = YellowFlasher
_flasher_type = FlasherType.YELLOW
def __init__(
self,
@@ -70,6 +70,7 @@ async def warn_on_wrong_silabs_firmware(hass: HomeAssistant, device: str) -> boo
return False
app_type = await probe_silabs_firmware_type(
hass,
device,
application_probe_methods=[
(ApplicationType.GECKO_BOOTLOADER, 115200),
@@ -324,9 +324,7 @@ async def test_options_flow(
),
],
),
patch(
"homeassistant.components.homeassistant_hardware.util.parse_firmware_image"
),
patch("universal_silabs_flasher.firmware.parse_firmware_image"),
):
pick_result = await hass.config_entries.options.async_configure(
result["flow_id"],
@@ -14,7 +14,7 @@ from ha_silabs_firmware_client import (
FirmwareUpdateClient,
)
import pytest
from universal_silabs_flasher.flasher import DeviceSpecificFlasher, Zbt1Flasher
from universal_silabs_flasher.flasher import DeviceSpecificFlasher
from yarl import URL
from homeassistant.components.homeassistant_hardware.const import (
@@ -30,6 +30,7 @@ from homeassistant.components.homeassistant_hardware.firmware_config_flow import
from homeassistant.components.homeassistant_hardware.util import (
ApplicationType,
FirmwareInfo,
FlasherType,
)
from homeassistant.config_entries import (
SOURCE_IGNORE,
@@ -65,7 +66,7 @@ class FakeFirmwareConfigFlow(BaseFirmwareConfigFlow, domain=TEST_DOMAIN):
VERSION = 1
MINOR_VERSION = 2
_flasher_cls = Zbt1Flasher
_flasher_type = FlasherType.ZBT1
@staticmethod
@callback
@@ -129,7 +130,7 @@ class FakeFirmwareConfigFlow(BaseFirmwareConfigFlow, domain=TEST_DOMAIN):
class FakeFirmwareOptionsFlowHandler(BaseFirmwareOptionsFlow):
"""Options flow for `test_firmware_domain`."""
_flasher_cls = Zbt1Flasher
_flasher_type = FlasherType.ZBT1
def __init__(self, *args: Any, **kwargs: Any) -> None:
"""Instantiate options flow."""
@@ -336,9 +337,7 @@ def mock_firmware_info(
"homeassistant.components.homeassistant_hardware.firmware_config_flow.FirmwareUpdateClient",
return_value=mock_update_client,
),
patch(
"homeassistant.components.homeassistant_hardware.util.parse_firmware_image"
),
patch("universal_silabs_flasher.firmware.parse_firmware_image"),
patch(
"homeassistant.components.homeassistant_hardware.firmware_config_flow.async_flash_silabs_firmware",
side_effect=mock_flash_firmware,
@@ -11,6 +11,7 @@ import pytest
from homeassistant.components.hassio import AddonError, AddonInfo, AddonState, HassIO
from homeassistant.components.homeassistant_hardware import silabs_multiprotocol_addon
from homeassistant.components.homeassistant_hardware.util import FlasherType
from homeassistant.components.zha import DOMAIN as ZHA_DOMAIN
from homeassistant.config_entries import ConfigEntry, ConfigFlow
from homeassistant.const import EVENT_COMPONENT_LOADED
@@ -106,9 +107,9 @@ class FakeOptionsFlow(silabs_multiprotocol_addon.OptionsFlowHandler):
return "test_zigbee_ncp"
@property
def _flasher_cls(self) -> type:
"""Return the hardware-specific flasher class."""
return Mock
def _flasher_type(self) -> FlasherType:
"""Return the hardware-specific flasher type."""
return FlasherType.ZBT1
@pytest.fixture(autouse=True)
@@ -8,7 +8,7 @@ from unittest.mock import Mock, patch
import aiohttp
import pytest
from universal_silabs_flasher.flasher import DeviceSpecificFlasher, Zbt1Flasher
from universal_silabs_flasher.flasher import DeviceSpecificFlasher
from homeassistant.components.homeassistant import (
DOMAIN as HOMEASSISTANT_DOMAIN,
@@ -30,6 +30,7 @@ from homeassistant.components.homeassistant_hardware.update import (
from homeassistant.components.homeassistant_hardware.util import (
ApplicationType,
FirmwareInfo,
FlasherType,
OwningIntegration,
)
from homeassistant.components.update import UpdateDeviceClass
@@ -176,7 +177,7 @@ async def mock_async_setup_update_entities(
class MockFirmwareUpdateEntity(BaseFirmwareUpdateEntity):
"""Mock SkyConnect firmware update entity."""
_flasher_cls = Zbt1Flasher
_flasher_type = FlasherType.ZBT1
def __init__(
self,
@@ -8,6 +8,7 @@ import pytest
from universal_silabs_flasher.common import Version as FlasherVersion
from universal_silabs_flasher.const import ApplicationType as FlasherApplicationType
from universal_silabs_flasher.firmware import GBLImage
from universal_silabs_flasher.flasher import YellowFlasher, Zbt1Flasher, Zbt2Flasher
from homeassistant.components.hassio import (
AddonError,
@@ -22,10 +23,12 @@ from homeassistant.components.homeassistant_hardware.helpers import (
from homeassistant.components.homeassistant_hardware.util import (
ApplicationType,
FirmwareInfo,
FlasherType,
OwningAddon,
OwningIntegration,
async_firmware_flashing_context,
async_flash_silabs_firmware,
async_get_flasher_cls,
get_otbr_addon_firmware_info,
get_z2m_addon_firmware_info,
get_z2m_addon_manager,
@@ -789,6 +792,21 @@ async def test_probe_silabs_firmware_info(
assert result == expected_fw_info
@pytest.mark.parametrize(
("flasher_type", "expected_cls"),
[
(FlasherType.YELLOW, YellowFlasher),
(FlasherType.ZBT1, Zbt1Flasher),
(FlasherType.ZBT2, Zbt2Flasher),
],
)
async def test_async_get_flasher_cls(
hass: HomeAssistant, flasher_type: FlasherType, expected_cls: type
) -> None:
"""Test resolving the flasher class of a flasher type."""
assert await async_get_flasher_cls(hass, flasher_type) is expected_cls
@pytest.mark.parametrize(
("probe_result", "expected"),
[
@@ -798,7 +816,9 @@ async def test_probe_silabs_firmware_info(
],
)
async def test_probe_silabs_firmware_type(
probe_result: FirmwareInfo | None, expected: ApplicationType | None
hass: HomeAssistant,
probe_result: FirmwareInfo | None,
expected: ApplicationType | None,
) -> None:
"""Test getting the firmware type from the probe result."""
@@ -813,11 +833,12 @@ async def test_probe_silabs_firmware_type(
mock_flasher.app_type = None
with patch(
"homeassistant.components.homeassistant_hardware.util.Flasher",
"universal_silabs_flasher.flasher.Flasher",
autospec=True,
return_value=mock_flasher,
):
result = await probe_silabs_firmware_type(
hass,
"/dev/ttyUSB0",
application_probe_methods=[
(ApplicationType.EZSP, 460800),
@@ -872,9 +893,7 @@ async def test_async_flash_silabs_firmware(hass: HomeAssistant) -> None:
owners=[owner1, owner2],
),
),
patch(
"homeassistant.components.homeassistant_hardware.util.parse_firmware_image"
),
patch("universal_silabs_flasher.firmware.parse_firmware_image"),
patch(
"homeassistant.components.homeassistant_hardware.util.probe_silabs_firmware_info",
return_value=expected_firmware_info,
@@ -952,9 +971,7 @@ async def test_async_flash_silabs_firmware_flash_failure(
owners=[owner1, owner2],
),
),
patch(
"homeassistant.components.homeassistant_hardware.util.parse_firmware_image"
),
patch("universal_silabs_flasher.firmware.parse_firmware_image"),
pytest.raises(HomeAssistantError, match=expected_error_msg) as exc,
):
async with async_firmware_flashing_context(
@@ -1006,9 +1023,7 @@ async def test_async_flash_silabs_firmware_probe_failure(hass: HomeAssistant) ->
owners=[owner1, owner2],
),
),
patch(
"homeassistant.components.homeassistant_hardware.util.parse_firmware_image"
),
patch("universal_silabs_flasher.firmware.parse_firmware_image"),
patch(
"homeassistant.components.homeassistant_hardware.util.probe_silabs_firmware_info",
return_value=None,
@@ -395,9 +395,7 @@ async def test_firmware_options_flow_zigbee(hass: HomeAssistant) -> None:
),
],
),
patch(
"homeassistant.components.homeassistant_hardware.util.parse_firmware_image"
),
patch("universal_silabs_flasher.firmware.parse_firmware_image"),
):
pick_result = await hass.config_entries.options.async_configure(
result["flow_id"],