diff --git a/homeassistant/components/homeassistant_connect_zbt2/config_flow.py b/homeassistant/components/homeassistant_connect_zbt2/config_flow.py index 2492940d4ad..0fcd520fde6 100644 --- a/homeassistant/components/homeassistant_connect_zbt2/config_flow.py +++ b/homeassistant/components/homeassistant_connect_zbt2/config_flow.py @@ -76,9 +76,18 @@ class ZBT2FirmwareMixin(ConfigEntryBaseFlow, FirmwareInstallFlowProtocol): """Mixin for Home Assistant Connect ZBT-2 firmware methods.""" context: ConfigFlowContext - BOOTLOADER_RESET_METHODS = [ResetTarget.RTS_DTR] + ZIGBEE_BAUDRATE = 460800 + # Early ZBT-2 samples used RTS/DTR to trigger the bootloader, later ones use the + # baudrate method. Since the two are mutually exclusive we just use both. + BOOTLOADER_RESET_METHODS = [ResetTarget.RTS_DTR, ResetTarget.BAUDRATE] + APPLICATION_PROBE_METHODS = [ + (ApplicationType.GECKO_BOOTLOADER, 115200), + (ApplicationType.EZSP, ZIGBEE_BAUDRATE), + (ApplicationType.SPINEL, 460800), + ] + async def async_step_install_zigbee_firmware( self, user_input: dict[str, Any] | None = None ) -> ConfigFlowResult: diff --git a/homeassistant/components/homeassistant_connect_zbt2/update.py b/homeassistant/components/homeassistant_connect_zbt2/update.py index db1993217ce..2e7fe0bb6ec 100644 --- a/homeassistant/components/homeassistant_connect_zbt2/update.py +++ b/homeassistant/components/homeassistant_connect_zbt2/update.py @@ -14,7 +14,6 @@ from homeassistant.components.homeassistant_hardware.update import ( from homeassistant.components.homeassistant_hardware.util import ( ApplicationType, FirmwareInfo, - ResetTarget, ) from homeassistant.components.update import UpdateDeviceClass from homeassistant.const import EntityCategory @@ -24,6 +23,7 @@ from homeassistant.helpers.device_registry import DeviceInfo from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from . import HomeAssistantConnectZBT2ConfigEntry +from .config_flow import ZBT2FirmwareMixin from .const import DOMAIN, FIRMWARE, FIRMWARE_VERSION, HARDWARE_NAME, SERIAL_NUMBER _LOGGER = logging.getLogger(__name__) @@ -134,7 +134,8 @@ async def async_setup_entry( class FirmwareUpdateEntity(BaseFirmwareUpdateEntity): """Connect ZBT-2 firmware update entity.""" - bootloader_reset_methods = [ResetTarget.RTS_DTR] + BOOTLOADER_RESET_METHODS = ZBT2FirmwareMixin.BOOTLOADER_RESET_METHODS + APPLICATION_PROBE_METHODS = ZBT2FirmwareMixin.APPLICATION_PROBE_METHODS def __init__( self, diff --git a/homeassistant/components/homeassistant_hardware/firmware_config_flow.py b/homeassistant/components/homeassistant_hardware/firmware_config_flow.py index cc7393d7d5c..39b45bd8843 100644 --- a/homeassistant/components/homeassistant_hardware/firmware_config_flow.py +++ b/homeassistant/components/homeassistant_hardware/firmware_config_flow.py @@ -81,6 +81,7 @@ class BaseFirmwareInstallFlow(ConfigEntryBaseFlow, ABC): ZIGBEE_BAUDRATE = 115200 # Default, subclasses may override BOOTLOADER_RESET_METHODS: list[ResetTarget] = [] # Default, subclasses may override + APPLICATION_PROBE_METHODS: list[tuple[ApplicationType, int]] = [] _picked_firmware_type: PickedFirmwareType _zigbee_flow_strategy: ZigbeeFlowStrategy = ZigbeeFlowStrategy.RECOMMENDED @@ -230,7 +231,11 @@ 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. - self._probed_firmware_info = await probe_silabs_firmware_info(self._device) + self._probed_firmware_info = await probe_silabs_firmware_info( + self._device, + bootloader_reset_methods=self.BOOTLOADER_RESET_METHODS, + application_probe_methods=self.APPLICATION_PROBE_METHODS, + ) firmware_install_required = self._probed_firmware_info is None or ( self._probed_firmware_info.firmware_type != expected_installed_firmware_type @@ -295,6 +300,7 @@ class BaseFirmwareInstallFlow(ConfigEntryBaseFlow, ABC): fw_data=fw_data, expected_installed_firmware_type=expected_installed_firmware_type, bootloader_reset_methods=self.BOOTLOADER_RESET_METHODS, + application_probe_methods=self.APPLICATION_PROBE_METHODS, progress_callback=lambda offset, total: self.async_update_progress( offset / total ), diff --git a/homeassistant/components/homeassistant_hardware/manifest.json b/homeassistant/components/homeassistant_hardware/manifest.json index f3e78454209..6911dbd5eef 100644 --- a/homeassistant/components/homeassistant_hardware/manifest.json +++ b/homeassistant/components/homeassistant_hardware/manifest.json @@ -7,7 +7,7 @@ "documentation": "https://www.home-assistant.io/integrations/homeassistant_hardware", "integration_type": "system", "requirements": [ - "universal-silabs-flasher==0.0.37", + "universal-silabs-flasher==0.1.0", "ha-silabs-firmware-client==0.3.0" ] } diff --git a/homeassistant/components/homeassistant_hardware/update.py b/homeassistant/components/homeassistant_hardware/update.py index 96bacc79a03..17f232b0e6f 100644 --- a/homeassistant/components/homeassistant_hardware/update.py +++ b/homeassistant/components/homeassistant_hardware/update.py @@ -86,7 +86,8 @@ class BaseFirmwareUpdateEntity( # Subclasses provide the mapping between firmware types and entity descriptions entity_description: FirmwareUpdateEntityDescription - bootloader_reset_methods: list[ResetTarget] = [] + BOOTLOADER_RESET_METHODS: list[ResetTarget] + APPLICATION_PROBE_METHODS: list[tuple[ApplicationType, int]] _attr_supported_features = ( UpdateEntityFeature.INSTALL | UpdateEntityFeature.PROGRESS @@ -278,7 +279,8 @@ class BaseFirmwareUpdateEntity( device=self._current_device, fw_data=fw_data, expected_installed_firmware_type=self.entity_description.expected_firmware_type, - bootloader_reset_methods=self.bootloader_reset_methods, + bootloader_reset_methods=self.BOOTLOADER_RESET_METHODS, + application_probe_methods=self.APPLICATION_PROBE_METHODS, progress_callback=self._update_progress, domain=self._config_entry.domain, ) diff --git a/homeassistant/components/homeassistant_hardware/util.py b/homeassistant/components/homeassistant_hardware/util.py index 713806de7dc..2a8c9121692 100644 --- a/homeassistant/components/homeassistant_hardware/util.py +++ b/homeassistant/components/homeassistant_hardware/util.py @@ -4,7 +4,7 @@ from __future__ import annotations import asyncio from collections import defaultdict -from collections.abc import AsyncIterator, Callable, Iterable, Sequence +from collections.abc import AsyncIterator, Callable, Sequence from contextlib import AsyncExitStack, asynccontextmanager from dataclasses import dataclass from enum import StrEnum @@ -309,15 +309,20 @@ async def guess_firmware_info(hass: HomeAssistant, device_path: str) -> Firmware async def probe_silabs_firmware_info( - device: str, *, probe_methods: Iterable[ApplicationType] | None = None + device: str, + *, + bootloader_reset_methods: Sequence[ResetTarget], + application_probe_methods: Sequence[tuple[ApplicationType, int]], ) -> FirmwareInfo | None: """Probe the running firmware on a SiLabs device.""" flasher = Flasher( device=device, - **( - {"probe_methods": [m.as_flasher_application_type() for m in probe_methods]} - if probe_methods - else {} + probe_methods=tuple( + (m.as_flasher_application_type(), baudrate) + for m, baudrate in application_probe_methods + ), + bootloader_reset=tuple( + m.as_flasher_reset_target() for m in bootloader_reset_methods ), ) @@ -343,11 +348,18 @@ async def probe_silabs_firmware_info( async def probe_silabs_firmware_type( - device: str, *, probe_methods: Iterable[ApplicationType] | None = None + device: str, + *, + bootloader_reset_methods: Sequence[ResetTarget], + application_probe_methods: Sequence[tuple[ApplicationType, int]], ) -> ApplicationType | None: """Probe the running firmware type on a SiLabs device.""" - fw_info = await probe_silabs_firmware_info(device, probe_methods=probe_methods) + fw_info = await probe_silabs_firmware_info( + device, + bootloader_reset_methods=bootloader_reset_methods, + application_probe_methods=application_probe_methods, + ) if fw_info is None: return None @@ -359,12 +371,22 @@ async def async_flash_silabs_firmware( device: str, fw_data: bytes, expected_installed_firmware_type: ApplicationType, - bootloader_reset_methods: Sequence[ResetTarget] = (), + bootloader_reset_methods: Sequence[ResetTarget], + application_probe_methods: Sequence[tuple[ApplicationType, int]], progress_callback: Callable[[int, int], None] | None = None, *, domain: str = DOMAIN, ) -> FirmwareInfo: """Flash firmware to the SiLabs device.""" + if not any( + method == expected_installed_firmware_type + for method, _ in application_probe_methods + ): + raise ValueError( + f"Expected installed firmware type {expected_installed_firmware_type!r}" + f" not in application probe methods {application_probe_methods!r}" + ) + async with async_firmware_update_context(hass, device, domain): firmware_info = await guess_firmware_info(hass, device) _LOGGER.debug("Identified firmware info: %s", firmware_info) @@ -373,11 +395,9 @@ async def async_flash_silabs_firmware( flasher = Flasher( device=device, - probe_methods=( - ApplicationType.GECKO_BOOTLOADER.as_flasher_application_type(), - ApplicationType.EZSP.as_flasher_application_type(), - ApplicationType.SPINEL.as_flasher_application_type(), - ApplicationType.CPC.as_flasher_application_type(), + probe_methods=tuple( + (m.as_flasher_application_type(), baudrate) + for m, baudrate in application_probe_methods ), bootloader_reset=tuple( m.as_flasher_reset_target() for m in bootloader_reset_methods @@ -401,7 +421,13 @@ async def async_flash_silabs_firmware( probed_firmware_info = await probe_silabs_firmware_info( device, - probe_methods=(expected_installed_firmware_type,), + bootloader_reset_methods=bootloader_reset_methods, + # Only probe for the expected installed firmware type + application_probe_methods=[ + (method, baudrate) + for method, baudrate in application_probe_methods + if method == expected_installed_firmware_type + ], ) if probed_firmware_info is None: diff --git a/homeassistant/components/homeassistant_sky_connect/config_flow.py b/homeassistant/components/homeassistant_sky_connect/config_flow.py index 1d8095d80a9..7d30833ecfb 100644 --- a/homeassistant/components/homeassistant_sky_connect/config_flow.py +++ b/homeassistant/components/homeassistant_sky_connect/config_flow.py @@ -16,6 +16,7 @@ from homeassistant.components.homeassistant_hardware.helpers import ( from homeassistant.components.homeassistant_hardware.util import ( ApplicationType, FirmwareInfo, + ResetTarget, ) from homeassistant.components.usb import ( usb_service_info_from_device, @@ -79,6 +80,20 @@ class SkyConnectFirmwareMixin(ConfigEntryBaseFlow, FirmwareInstallFlowProtocol): context: ConfigFlowContext + ZIGBEE_BAUDRATE = 115200 + # There is no hardware bootloader trigger + BOOTLOADER_RESET_METHODS: list[ResetTarget] = [] + APPLICATION_PROBE_METHODS = [ + (ApplicationType.GECKO_BOOTLOADER, 115200), + (ApplicationType.EZSP, ZIGBEE_BAUDRATE), + (ApplicationType.SPINEL, 460800), + # CPC baudrates can be removed once multiprotocol is removed + (ApplicationType.CPC, 115200), + (ApplicationType.CPC, 230400), + (ApplicationType.CPC, 460800), + (ApplicationType.ROUTER, 115200), + ] + def _get_translation_placeholders(self) -> dict[str, str]: """Shared translation placeholders.""" placeholders = { diff --git a/homeassistant/components/homeassistant_sky_connect/update.py b/homeassistant/components/homeassistant_sky_connect/update.py index 58177249b62..1f52781d044 100644 --- a/homeassistant/components/homeassistant_sky_connect/update.py +++ b/homeassistant/components/homeassistant_sky_connect/update.py @@ -23,6 +23,7 @@ from homeassistant.helpers.device_registry import DeviceInfo from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from . import HomeAssistantSkyConnectConfigEntry +from .config_flow import SkyConnectFirmwareMixin from .const import ( DOMAIN, FIRMWARE, @@ -151,8 +152,8 @@ async def async_setup_entry( class FirmwareUpdateEntity(BaseFirmwareUpdateEntity): """SkyConnect firmware update entity.""" - # The ZBT-1 does not have a hardware bootloader trigger - bootloader_reset_methods = [] + BOOTLOADER_RESET_METHODS = SkyConnectFirmwareMixin.BOOTLOADER_RESET_METHODS + APPLICATION_PROBE_METHODS = SkyConnectFirmwareMixin.APPLICATION_PROBE_METHODS def __init__( self, diff --git a/homeassistant/components/homeassistant_yellow/config_flow.py b/homeassistant/components/homeassistant_yellow/config_flow.py index 8242a5991d5..d8007252bff 100644 --- a/homeassistant/components/homeassistant_yellow/config_flow.py +++ b/homeassistant/components/homeassistant_yellow/config_flow.py @@ -82,7 +82,18 @@ else: class YellowFirmwareMixin(ConfigEntryBaseFlow, FirmwareInstallFlowProtocol): """Mixin for Home Assistant Yellow firmware methods.""" + ZIGBEE_BAUDRATE = 115200 BOOTLOADER_RESET_METHODS = [ResetTarget.YELLOW] + APPLICATION_PROBE_METHODS = [ + (ApplicationType.GECKO_BOOTLOADER, 115200), + (ApplicationType.EZSP, ZIGBEE_BAUDRATE), + (ApplicationType.SPINEL, 460800), + # CPC baudrates can be removed once multiprotocol is removed + (ApplicationType.CPC, 115200), + (ApplicationType.CPC, 230400), + (ApplicationType.CPC, 460800), + (ApplicationType.ROUTER, 115200), + ] async def async_step_install_zigbee_firmware( self, user_input: dict[str, Any] | None = None @@ -146,7 +157,11 @@ class HomeAssistantYellowConfigFlow( assert self._device is not None # We do not actually use any portion of `BaseFirmwareConfigFlow` beyond this - self._probed_firmware_info = await probe_silabs_firmware_info(self._device) + self._probed_firmware_info = await probe_silabs_firmware_info( + self._device, + bootloader_reset_methods=self.BOOTLOADER_RESET_METHODS, + application_probe_methods=self.APPLICATION_PROBE_METHODS, + ) # Kick off ZHA hardware discovery automatically if Zigbee firmware is running if ( diff --git a/homeassistant/components/homeassistant_yellow/update.py b/homeassistant/components/homeassistant_yellow/update.py index f62ecb06aa5..963d7c963eb 100644 --- a/homeassistant/components/homeassistant_yellow/update.py +++ b/homeassistant/components/homeassistant_yellow/update.py @@ -14,7 +14,6 @@ from homeassistant.components.homeassistant_hardware.update import ( from homeassistant.components.homeassistant_hardware.util import ( ApplicationType, FirmwareInfo, - ResetTarget, ) from homeassistant.components.update import UpdateDeviceClass from homeassistant.const import EntityCategory @@ -24,6 +23,7 @@ from homeassistant.helpers.device_registry import DeviceInfo from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from . import HomeAssistantYellowConfigEntry +from .config_flow import YellowFirmwareMixin from .const import DOMAIN, FIRMWARE, FIRMWARE_VERSION, MANUFACTURER, MODEL, RADIO_DEVICE _LOGGER = logging.getLogger(__name__) @@ -150,7 +150,8 @@ async def async_setup_entry( class FirmwareUpdateEntity(BaseFirmwareUpdateEntity): """Yellow firmware update entity.""" - bootloader_reset_methods = [ResetTarget.YELLOW] # Triggers a GPIO reset + BOOTLOADER_RESET_METHODS = YellowFirmwareMixin.BOOTLOADER_RESET_METHODS + APPLICATION_PROBE_METHODS = YellowFirmwareMixin.APPLICATION_PROBE_METHODS def __init__( self, diff --git a/homeassistant/components/zha/repairs/wrong_silabs_firmware.py b/homeassistant/components/zha/repairs/wrong_silabs_firmware.py index 5b1eed18014..1754de9f773 100644 --- a/homeassistant/components/zha/repairs/wrong_silabs_firmware.py +++ b/homeassistant/components/zha/repairs/wrong_silabs_firmware.py @@ -71,7 +71,20 @@ async def warn_on_wrong_silabs_firmware(hass: HomeAssistant, device: str) -> boo if device.startswith("socket://"): return False - app_type = await probe_silabs_firmware_type(device) + app_type = await probe_silabs_firmware_type( + device, + bootloader_reset_methods=(), + application_probe_methods=[ + (ApplicationType.GECKO_BOOTLOADER, 115200), + (ApplicationType.EZSP, 115200), + (ApplicationType.EZSP, 460800), + (ApplicationType.SPINEL, 460800), + (ApplicationType.CPC, 460800), + (ApplicationType.CPC, 230400), + (ApplicationType.CPC, 115200), + (ApplicationType.ROUTER, 115200), + ], + ) if app_type is None: # Failed to probe, we can't tell if the wrong firmware is installed diff --git a/requirements_all.txt b/requirements_all.txt index 57261f43908..08ec4cb1c3d 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -3084,7 +3084,7 @@ unifi_ap==0.0.2 unifiled==0.11 # homeassistant.components.homeassistant_hardware -universal-silabs-flasher==0.0.37 +universal-silabs-flasher==0.1.0 # homeassistant.components.upb upb-lib==0.6.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 7f34d9a6735..2779610bf35 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -2555,7 +2555,7 @@ ultraheat-api==0.5.7 unifi-discovery==1.2.0 # homeassistant.components.homeassistant_hardware -universal-silabs-flasher==0.0.37 +universal-silabs-flasher==0.1.0 # homeassistant.components.upb upb-lib==0.6.1 diff --git a/tests/components/homeassistant_connect_zbt2/test_config_flow.py b/tests/components/homeassistant_connect_zbt2/test_config_flow.py index 14033e504cf..ecf165461e2 100644 --- a/tests/components/homeassistant_connect_zbt2/test_config_flow.py +++ b/tests/components/homeassistant_connect_zbt2/test_config_flow.py @@ -16,6 +16,7 @@ from homeassistant.components.homeassistant_hardware.helpers import ( from homeassistant.components.homeassistant_hardware.util import ( ApplicationType, FirmwareInfo, + ResetTarget, ) from homeassistant.components.usb import USBDevice from homeassistant.config_entries import ConfigFlowResult @@ -367,7 +368,10 @@ async def test_options_flow( # Verify async_flash_silabs_firmware was called with ZBT-2's reset methods assert flash_mock.call_count == 1 - assert flash_mock.mock_calls[0].kwargs["bootloader_reset_methods"] == ["rts_dtr"] + assert flash_mock.mock_calls[0].kwargs["bootloader_reset_methods"] == [ + ResetTarget.RTS_DTR, + ResetTarget.BAUDRATE, + ] flows = hass.config_entries.flow.async_progress() diff --git a/tests/components/homeassistant_hardware/test_config_flow.py b/tests/components/homeassistant_hardware/test_config_flow.py index ff5b6c32450..960113215be 100644 --- a/tests/components/homeassistant_hardware/test_config_flow.py +++ b/tests/components/homeassistant_hardware/test_config_flow.py @@ -304,6 +304,7 @@ def mock_firmware_info( fw_data: bytes, expected_installed_firmware_type: ApplicationType, bootloader_reset_methods: Sequence[ResetTarget] = (), + application_probe_methods: Sequence[tuple[ApplicationType, int]] = (), progress_callback: Callable[[int, int], None] | None = None, *, domain: str = "homeassistant_hardware", diff --git a/tests/components/homeassistant_hardware/test_update.py b/tests/components/homeassistant_hardware/test_update.py index 3a463362533..86e8e6f820d 100644 --- a/tests/components/homeassistant_hardware/test_update.py +++ b/tests/components/homeassistant_hardware/test_update.py @@ -173,7 +173,12 @@ async def mock_async_setup_update_entities( class MockFirmwareUpdateEntity(BaseFirmwareUpdateEntity): """Mock SkyConnect firmware update entity.""" - bootloader_reset_methods = [] + BOOTLOADER_RESET_METHODS = [ResetTarget.RTS_DTR] + APPLICATION_PROBE_METHODS = [ + (ApplicationType.GECKO_BOOTLOADER, 115200), + (ApplicationType.EZSP, 115200), + (ApplicationType.SPINEL, 460800), + ] def __init__( self, @@ -320,6 +325,7 @@ async def test_update_entity_installation( fw_data: bytes, expected_installed_firmware_type: ApplicationType, bootloader_reset_methods: Sequence[ResetTarget] = (), + application_probe_methods: Sequence[tuple[ApplicationType, int]] = (), progress_callback: Callable[[int, int], None] | None = None, *, domain: str = "homeassistant_hardware", diff --git a/tests/components/homeassistant_hardware/test_util.py b/tests/components/homeassistant_hardware/test_util.py index ef064ba4195..64019c58002 100644 --- a/tests/components/homeassistant_hardware/test_util.py +++ b/tests/components/homeassistant_hardware/test_util.py @@ -23,6 +23,7 @@ from homeassistant.components.homeassistant_hardware.util import ( FirmwareInfo, OwningAddon, OwningIntegration, + ResetTarget, async_flash_silabs_firmware, get_otbr_addon_firmware_info, guess_firmware_info, @@ -502,7 +503,14 @@ async def test_probe_silabs_firmware_info( "homeassistant.components.homeassistant_hardware.util.Flasher", return_value=mock_flasher, ): - result = await probe_silabs_firmware_info("/dev/ttyUSB0") + result = await probe_silabs_firmware_info( + "/dev/ttyUSB0", + bootloader_reset_methods=[ResetTarget.RTS_DTR], + application_probe_methods=[ + (ApplicationType.EZSP, 460800), + (ApplicationType.SPINEL, 460800), + ], + ) assert result == expected_fw_info @@ -531,7 +539,14 @@ async def test_probe_silabs_firmware_type( autospec=True, return_value=probe_result, ): - result = await probe_silabs_firmware_type("/dev/ttyUSB0") + result = await probe_silabs_firmware_type( + "/dev/ttyUSB0", + bootloader_reset_methods=[ResetTarget.RTS_DTR], + application_probe_methods=[ + (ApplicationType.EZSP, 460800), + (ApplicationType.SPINEL, 460800), + ], + ) assert result == expected @@ -596,7 +611,11 @@ async def test_async_flash_silabs_firmware(hass: HomeAssistant) -> None: device="/dev/ttyUSB0", fw_data=b"firmware contents", expected_installed_firmware_type=ApplicationType.SPINEL, - bootloader_reset_methods=(), + bootloader_reset_methods=[ResetTarget.RTS_DTR], + application_probe_methods=[ + (ApplicationType.EZSP, 460800), + (ApplicationType.SPINEL, 460800), + ], progress_callback=progress_callback, ) @@ -605,7 +624,9 @@ async def test_async_flash_silabs_firmware(hass: HomeAssistant) -> None: # Verify Flasher was called with correct bootloader_reset parameter assert flasher_mock.call_count == 1 - assert flasher_mock.mock_calls[0].kwargs["bootloader_reset"] == () + assert flasher_mock.mock_calls[0].kwargs["bootloader_reset"] == ( + ResetTarget.RTS_DTR.as_flasher_reset_target(), + ) # Both owning integrations/addons are stopped and restarted assert owner1.temporarily_stop.mock_calls == [ @@ -623,6 +644,28 @@ async def test_async_flash_silabs_firmware(hass: HomeAssistant) -> None: ] +async def test_async_flash_silabs_firmware_expected_type_not_probed( + hass: HomeAssistant, +) -> None: + """Test firmware flashing requires probing config to exist for firmware type.""" + with pytest.raises( + ValueError, + match=( + r"Expected installed firmware type .*? not in application probe methods .*?" + ), + ): + await async_flash_silabs_firmware( + hass=hass, + device="/dev/ttyUSB0", + fw_data=b"firmware contents", + expected_installed_firmware_type=ApplicationType.SPINEL, + bootloader_reset_methods=[ResetTarget.RTS_DTR], + application_probe_methods=[ + (ApplicationType.EZSP, 460800), + ], + ) + + async def test_async_flash_silabs_firmware_flash_failure(hass: HomeAssistant) -> None: """Test async_flash_silabs_firmware flash failure.""" await async_setup_component(hass, "homeassistant_hardware", {}) @@ -659,7 +702,11 @@ async def test_async_flash_silabs_firmware_flash_failure(hass: HomeAssistant) -> device="/dev/ttyUSB0", fw_data=b"firmware contents", expected_installed_firmware_type=ApplicationType.SPINEL, - bootloader_reset_methods=(), + bootloader_reset_methods=[ResetTarget.RTS_DTR], + application_probe_methods=[ + (ApplicationType.EZSP, 460800), + (ApplicationType.SPINEL, 460800), + ], ) # Both owning integrations/addons are stopped and restarted @@ -719,7 +766,11 @@ async def test_async_flash_silabs_firmware_probe_failure(hass: HomeAssistant) -> device="/dev/ttyUSB0", fw_data=b"firmware contents", expected_installed_firmware_type=ApplicationType.SPINEL, - bootloader_reset_methods=(), + bootloader_reset_methods=[ResetTarget.RTS_DTR], + application_probe_methods=[ + (ApplicationType.EZSP, 460800), + (ApplicationType.SPINEL, 460800), + ], ) # Both owning integrations/addons are stopped and restarted