diff --git a/homeassistant/components/homeassistant_connect_zbt2/config_flow.py b/homeassistant/components/homeassistant_connect_zbt2/config_flow.py index aea229ece6db..c29c898e3cc8 100644 --- a/homeassistant/components/homeassistant_connect_zbt2/config_flow.py +++ b/homeassistant/components/homeassistant_connect_zbt2/config_flow.py @@ -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 diff --git a/homeassistant/components/homeassistant_connect_zbt2/update.py b/homeassistant/components/homeassistant_connect_zbt2/update.py index 5c9988d84886..820a27a80232 100644 --- a/homeassistant/components/homeassistant_connect_zbt2/update.py +++ b/homeassistant/components/homeassistant_connect_zbt2/update.py @@ -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, diff --git a/homeassistant/components/homeassistant_hardware/firmware_config_flow.py b/homeassistant/components/homeassistant_hardware/firmware_config_flow.py index aee638a6fff3..daf54fa8c2df 100644 --- a/homeassistant/components/homeassistant_hardware/firmware_config_flow.py +++ b/homeassistant/components/homeassistant_hardware/firmware_config_flow.py @@ -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 diff --git a/homeassistant/components/homeassistant_hardware/silabs_multiprotocol_addon.py b/homeassistant/components/homeassistant_hardware/silabs_multiprotocol_addon.py index de061599e315..a86fcf008b40 100644 --- a/homeassistant/components/homeassistant_hardware/silabs_multiprotocol_addon.py +++ b/homeassistant/components/homeassistant_hardware/silabs_multiprotocol_addon.py @@ -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) diff --git a/homeassistant/components/homeassistant_hardware/update.py b/homeassistant/components/homeassistant_hardware/update.py index c226e1724a2e..248a5e338209 100644 --- a/homeassistant/components/homeassistant_hardware/update.py +++ b/homeassistant/components/homeassistant_hardware/update.py @@ -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, ) diff --git a/homeassistant/components/homeassistant_hardware/util.py b/homeassistant/components/homeassistant_hardware/util.py index 723c5654ccf0..d7423462ee0f 100644 --- a/homeassistant/components/homeassistant_hardware/util.py +++ b/homeassistant/components/homeassistant_hardware/util.py @@ -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) diff --git a/homeassistant/components/homeassistant_sky_connect/config_flow.py b/homeassistant/components/homeassistant_sky_connect/config_flow.py index ddce14f13421..4510cab45741 100644 --- a/homeassistant/components/homeassistant_sky_connect/config_flow.py +++ b/homeassistant/components/homeassistant_sky_connect/config_flow.py @@ -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( diff --git a/homeassistant/components/homeassistant_sky_connect/update.py b/homeassistant/components/homeassistant_sky_connect/update.py index 2bcf10acfba0..651a5fa0bf6b 100644 --- a/homeassistant/components/homeassistant_sky_connect/update.py +++ b/homeassistant/components/homeassistant_sky_connect/update.py @@ -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, diff --git a/homeassistant/components/homeassistant_yellow/config_flow.py b/homeassistant/components/homeassistant_yellow/config_flow.py index 25826276c6df..efbc79ec5f99 100644 --- a/homeassistant/components/homeassistant_yellow/config_flow.py +++ b/homeassistant/components/homeassistant_yellow/config_flow.py @@ -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( diff --git a/homeassistant/components/homeassistant_yellow/update.py b/homeassistant/components/homeassistant_yellow/update.py index c3b4b7f90bc6..86ecbf7f3326 100644 --- a/homeassistant/components/homeassistant_yellow/update.py +++ b/homeassistant/components/homeassistant_yellow/update.py @@ -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, diff --git a/homeassistant/components/zha/repairs/wrong_silabs_firmware.py b/homeassistant/components/zha/repairs/wrong_silabs_firmware.py index 9bbdac235881..81e9aaf5d1e0 100644 --- a/homeassistant/components/zha/repairs/wrong_silabs_firmware.py +++ b/homeassistant/components/zha/repairs/wrong_silabs_firmware.py @@ -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), diff --git a/tests/components/homeassistant_connect_zbt2/test_config_flow.py b/tests/components/homeassistant_connect_zbt2/test_config_flow.py index 798bb924265d..bb552e7ac6e2 100644 --- a/tests/components/homeassistant_connect_zbt2/test_config_flow.py +++ b/tests/components/homeassistant_connect_zbt2/test_config_flow.py @@ -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"], diff --git a/tests/components/homeassistant_hardware/test_config_flow.py b/tests/components/homeassistant_hardware/test_config_flow.py index a26c7914512c..55311241d6a0 100644 --- a/tests/components/homeassistant_hardware/test_config_flow.py +++ b/tests/components/homeassistant_hardware/test_config_flow.py @@ -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, diff --git a/tests/components/homeassistant_hardware/test_silabs_multiprotocol_addon.py b/tests/components/homeassistant_hardware/test_silabs_multiprotocol_addon.py index a46547870e20..9a3b515c1b5a 100644 --- a/tests/components/homeassistant_hardware/test_silabs_multiprotocol_addon.py +++ b/tests/components/homeassistant_hardware/test_silabs_multiprotocol_addon.py @@ -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) diff --git a/tests/components/homeassistant_hardware/test_update.py b/tests/components/homeassistant_hardware/test_update.py index b6f25246a880..b4ae8ecda767 100644 --- a/tests/components/homeassistant_hardware/test_update.py +++ b/tests/components/homeassistant_hardware/test_update.py @@ -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, diff --git a/tests/components/homeassistant_hardware/test_util.py b/tests/components/homeassistant_hardware/test_util.py index 2a476a344c15..193c3c1d1683 100644 --- a/tests/components/homeassistant_hardware/test_util.py +++ b/tests/components/homeassistant_hardware/test_util.py @@ -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, diff --git a/tests/components/homeassistant_yellow/test_config_flow.py b/tests/components/homeassistant_yellow/test_config_flow.py index f1987f12e04c..c2457ebb3282 100644 --- a/tests/components/homeassistant_yellow/test_config_flow.py +++ b/tests/components/homeassistant_yellow/test_config_flow.py @@ -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"],