mirror of
https://github.com/home-assistant/core.git
synced 2025-04-23 16:57:53 +00:00
Migrate homeassistant_hardware
to use FirmwareInfo
instead of just the application type (#138874)
* Migrate `self._probed_firmware_type` to `self._probed_firmware_info` * Migrate from `firmware_type` to the full `firmware_info` * Implement `probe_silabs_firmware_type` via `probe_silabs_firmware_info` * Fix unit tests * Increase coverage * Bring test coverage to 100% * Simplify test per review comment
This commit is contained in:
parent
508b6c8db0
commit
debee25086
@ -28,12 +28,13 @@ from . import silabs_multiprotocol_addon
|
||||
from .const import OTBR_DOMAIN, ZHA_DOMAIN
|
||||
from .util import (
|
||||
ApplicationType,
|
||||
FirmwareInfo,
|
||||
OwningAddon,
|
||||
OwningIntegration,
|
||||
get_otbr_addon_manager,
|
||||
get_zigbee_flasher_addon_manager,
|
||||
guess_hardware_owners,
|
||||
probe_silabs_firmware_type,
|
||||
probe_silabs_firmware_info,
|
||||
)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
@ -52,7 +53,7 @@ class BaseFirmwareInstallFlow(ConfigEntryBaseFlow, ABC):
|
||||
"""Instantiate base flow."""
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
self._probed_firmware_type: ApplicationType | None = None
|
||||
self._probed_firmware_info: FirmwareInfo | None = None
|
||||
self._device: str | None = None # To be set in a subclass
|
||||
self._hardware_name: str = "unknown" # To be set in a subclass
|
||||
|
||||
@ -64,8 +65,8 @@ class BaseFirmwareInstallFlow(ConfigEntryBaseFlow, ABC):
|
||||
"""Shared translation placeholders."""
|
||||
placeholders = {
|
||||
"firmware_type": (
|
||||
self._probed_firmware_type.value
|
||||
if self._probed_firmware_type is not None
|
||||
self._probed_firmware_info.firmware_type.value
|
||||
if self._probed_firmware_info is not None
|
||||
else "unknown"
|
||||
),
|
||||
"model": self._hardware_name,
|
||||
@ -120,39 +121,49 @@ class BaseFirmwareInstallFlow(ConfigEntryBaseFlow, ABC):
|
||||
description_placeholders=self._get_translation_placeholders(),
|
||||
)
|
||||
|
||||
async def _probe_firmware_type(self) -> bool:
|
||||
"""Probe the firmware currently on the device."""
|
||||
assert self._device is not None
|
||||
|
||||
self._probed_firmware_type = await probe_silabs_firmware_type(
|
||||
self._device,
|
||||
probe_methods=(
|
||||
# We probe in order of frequency: Zigbee, Thread, then multi-PAN
|
||||
ApplicationType.GECKO_BOOTLOADER,
|
||||
ApplicationType.EZSP,
|
||||
ApplicationType.SPINEL,
|
||||
ApplicationType.CPC,
|
||||
),
|
||||
)
|
||||
|
||||
return self._probed_firmware_type in (
|
||||
async def _probe_firmware_info(
|
||||
self,
|
||||
probe_methods: tuple[ApplicationType, ...] = (
|
||||
# We probe in order of frequency: Zigbee, Thread, then multi-PAN
|
||||
ApplicationType.GECKO_BOOTLOADER,
|
||||
ApplicationType.EZSP,
|
||||
ApplicationType.SPINEL,
|
||||
ApplicationType.CPC,
|
||||
),
|
||||
) -> bool:
|
||||
"""Probe the firmware currently on the device."""
|
||||
assert self._device is not None
|
||||
|
||||
self._probed_firmware_info = await probe_silabs_firmware_info(
|
||||
self._device,
|
||||
probe_methods=probe_methods,
|
||||
)
|
||||
|
||||
return (
|
||||
self._probed_firmware_info is not None
|
||||
and self._probed_firmware_info.firmware_type
|
||||
in (
|
||||
ApplicationType.EZSP,
|
||||
ApplicationType.SPINEL,
|
||||
ApplicationType.CPC,
|
||||
)
|
||||
)
|
||||
|
||||
async def async_step_pick_firmware_zigbee(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> ConfigFlowResult:
|
||||
"""Pick Zigbee firmware."""
|
||||
if not await self._probe_firmware_type():
|
||||
if not await self._probe_firmware_info():
|
||||
return self.async_abort(
|
||||
reason="unsupported_firmware",
|
||||
description_placeholders=self._get_translation_placeholders(),
|
||||
)
|
||||
|
||||
# Allow the stick to be used with ZHA without flashing
|
||||
if self._probed_firmware_type == ApplicationType.EZSP:
|
||||
if (
|
||||
self._probed_firmware_info is not None
|
||||
and self._probed_firmware_info.firmware_type == ApplicationType.EZSP
|
||||
):
|
||||
return await self.async_step_confirm_zigbee()
|
||||
|
||||
if not is_hassio(self.hass):
|
||||
@ -338,7 +349,12 @@ class BaseFirmwareInstallFlow(ConfigEntryBaseFlow, ABC):
|
||||
"""Confirm Zigbee setup."""
|
||||
assert self._device is not None
|
||||
assert self._hardware_name is not None
|
||||
self._probed_firmware_type = ApplicationType.EZSP
|
||||
|
||||
if not await self._probe_firmware_info(probe_methods=(ApplicationType.EZSP,)):
|
||||
return self.async_abort(
|
||||
reason="unsupported_firmware",
|
||||
description_placeholders=self._get_translation_placeholders(),
|
||||
)
|
||||
|
||||
if user_input is not None:
|
||||
await self.hass.config_entries.flow.async_init(
|
||||
@ -366,7 +382,7 @@ class BaseFirmwareInstallFlow(ConfigEntryBaseFlow, ABC):
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> ConfigFlowResult:
|
||||
"""Pick Thread firmware."""
|
||||
if not await self._probe_firmware_type():
|
||||
if not await self._probe_firmware_info():
|
||||
return self.async_abort(
|
||||
reason="unsupported_firmware",
|
||||
description_placeholders=self._get_translation_placeholders(),
|
||||
@ -458,7 +474,11 @@ class BaseFirmwareInstallFlow(ConfigEntryBaseFlow, ABC):
|
||||
"""Confirm OTBR setup."""
|
||||
assert self._device is not None
|
||||
|
||||
self._probed_firmware_type = ApplicationType.SPINEL
|
||||
if not await self._probe_firmware_info(probe_methods=(ApplicationType.SPINEL,)):
|
||||
return self.async_abort(
|
||||
reason="unsupported_firmware",
|
||||
description_placeholders=self._get_translation_placeholders(),
|
||||
)
|
||||
|
||||
if user_input is not None:
|
||||
# OTBR discovery is done automatically via hassio
|
||||
@ -497,14 +517,14 @@ class BaseFirmwareConfigFlow(BaseFirmwareInstallFlow, ConfigFlow):
|
||||
class BaseFirmwareOptionsFlow(BaseFirmwareInstallFlow, OptionsFlow):
|
||||
"""Zigbee and Thread options flow handlers."""
|
||||
|
||||
_probed_firmware_info: FirmwareInfo
|
||||
|
||||
def __init__(self, config_entry: ConfigEntry, *args: Any, **kwargs: Any) -> None:
|
||||
"""Instantiate options flow."""
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
self._config_entry = config_entry
|
||||
|
||||
self._probed_firmware_type = ApplicationType(self.config_entry.data["firmware"])
|
||||
|
||||
# Make `context` a regular dictionary
|
||||
self.context = {}
|
||||
|
||||
|
@ -249,10 +249,10 @@ async def guess_firmware_info(hass: HomeAssistant, device_path: str) -> Firmware
|
||||
return guesses[-1][0]
|
||||
|
||||
|
||||
async def probe_silabs_firmware_type(
|
||||
async def probe_silabs_firmware_info(
|
||||
device: str, *, probe_methods: Iterable[ApplicationType] | None = None
|
||||
) -> ApplicationType | None:
|
||||
"""Probe the running firmware on a Silabs device."""
|
||||
) -> FirmwareInfo | None:
|
||||
"""Probe the running firmware on a SiLabs device."""
|
||||
flasher = Flasher(
|
||||
device=device,
|
||||
**(
|
||||
@ -270,4 +270,26 @@ async def probe_silabs_firmware_type(
|
||||
if flasher.app_type is None:
|
||||
return None
|
||||
|
||||
return ApplicationType.from_flasher_application_type(flasher.app_type)
|
||||
return FirmwareInfo(
|
||||
device=device,
|
||||
firmware_type=ApplicationType.from_flasher_application_type(flasher.app_type),
|
||||
firmware_version=(
|
||||
flasher.app_version.orig_version
|
||||
if flasher.app_version is not None
|
||||
else None
|
||||
),
|
||||
source="probe",
|
||||
owners=[],
|
||||
)
|
||||
|
||||
|
||||
async def probe_silabs_firmware_type(
|
||||
device: str, *, probe_methods: Iterable[ApplicationType] | None = None
|
||||
) -> ApplicationType | None:
|
||||
"""Probe the running firmware type on a SiLabs device."""
|
||||
|
||||
fw_info = await probe_silabs_firmware_info(device, probe_methods=probe_methods)
|
||||
if fw_info is None:
|
||||
return None
|
||||
|
||||
return fw_info.firmware_type
|
||||
|
@ -10,7 +10,10 @@ from homeassistant.components.homeassistant_hardware import (
|
||||
firmware_config_flow,
|
||||
silabs_multiprotocol_addon,
|
||||
)
|
||||
from homeassistant.components.homeassistant_hardware.util import ApplicationType
|
||||
from homeassistant.components.homeassistant_hardware.util import (
|
||||
ApplicationType,
|
||||
FirmwareInfo,
|
||||
)
|
||||
from homeassistant.config_entries import (
|
||||
ConfigEntry,
|
||||
ConfigEntryBaseFlow,
|
||||
@ -118,7 +121,7 @@ class HomeAssistantSkyConnectConfigFlow(
|
||||
"""Create the config entry."""
|
||||
assert self._usb_info is not None
|
||||
assert self._hw_variant is not None
|
||||
assert self._probed_firmware_type is not None
|
||||
assert self._probed_firmware_info is not None
|
||||
|
||||
return self.async_create_entry(
|
||||
title=self._hw_variant.full_name,
|
||||
@ -130,7 +133,7 @@ class HomeAssistantSkyConnectConfigFlow(
|
||||
"description": self._usb_info.description, # For backwards compatibility
|
||||
"product": self._usb_info.description,
|
||||
"device": self._usb_info.device,
|
||||
"firmware": self._probed_firmware_type.value,
|
||||
"firmware": self._probed_firmware_info.firmware_type.value,
|
||||
},
|
||||
)
|
||||
|
||||
@ -203,18 +206,26 @@ class HomeAssistantSkyConnectOptionsFlowHandler(
|
||||
self._hardware_name = self._hw_variant.full_name
|
||||
self._device = self._usb_info.device
|
||||
|
||||
self._probed_firmware_info = FirmwareInfo(
|
||||
device=self._device,
|
||||
firmware_type=ApplicationType(self.config_entry.data["firmware"]),
|
||||
firmware_version=None,
|
||||
source="guess",
|
||||
owners=[],
|
||||
)
|
||||
|
||||
# Regenerate the translation placeholders
|
||||
self._get_translation_placeholders()
|
||||
|
||||
def _async_flow_finished(self) -> ConfigFlowResult:
|
||||
"""Create the config entry."""
|
||||
assert self._probed_firmware_type is not None
|
||||
assert self._probed_firmware_info is not None
|
||||
|
||||
self.hass.config_entries.async_update_entry(
|
||||
entry=self.config_entry,
|
||||
data={
|
||||
**self.config_entry.data,
|
||||
"firmware": self._probed_firmware_type.value,
|
||||
"firmware": self._probed_firmware_info.firmware_type.value,
|
||||
},
|
||||
options=self.config_entry.options,
|
||||
)
|
||||
|
@ -24,7 +24,10 @@ from homeassistant.components.homeassistant_hardware.silabs_multiprotocol_addon
|
||||
OptionsFlowHandler as MultiprotocolOptionsFlowHandler,
|
||||
SerialPortSettings as MultiprotocolSerialPortSettings,
|
||||
)
|
||||
from homeassistant.components.homeassistant_hardware.util import ApplicationType
|
||||
from homeassistant.components.homeassistant_hardware.util import (
|
||||
ApplicationType,
|
||||
FirmwareInfo,
|
||||
)
|
||||
from homeassistant.config_entries import (
|
||||
SOURCE_HARDWARE,
|
||||
ConfigEntry,
|
||||
@ -79,10 +82,13 @@ class HomeAssistantYellowConfigFlow(BaseFirmwareConfigFlow, domain=DOMAIN):
|
||||
) -> ConfigFlowResult:
|
||||
"""Handle the initial step."""
|
||||
# We do not actually use any portion of `BaseFirmwareConfigFlow` beyond this
|
||||
await self._probe_firmware_type()
|
||||
await self._probe_firmware_info()
|
||||
|
||||
# Kick off ZHA hardware discovery automatically if Zigbee firmware is running
|
||||
if self._probed_firmware_type is ApplicationType.EZSP:
|
||||
if (
|
||||
self._probed_firmware_info is not None
|
||||
and self._probed_firmware_info.firmware_type is ApplicationType.EZSP
|
||||
):
|
||||
discovery_flow.async_create_flow(
|
||||
self.hass,
|
||||
ZHA_DOMAIN,
|
||||
@ -98,7 +104,11 @@ class HomeAssistantYellowConfigFlow(BaseFirmwareConfigFlow, domain=DOMAIN):
|
||||
title=BOARD_NAME,
|
||||
data={
|
||||
# Assume the firmware type is EZSP if we cannot probe it
|
||||
FIRMWARE: (self._probed_firmware_type or ApplicationType.EZSP).value,
|
||||
FIRMWARE: (
|
||||
self._probed_firmware_info.firmware_type
|
||||
if self._probed_firmware_info is not None
|
||||
else ApplicationType.EZSP
|
||||
).value,
|
||||
},
|
||||
)
|
||||
|
||||
@ -264,6 +274,14 @@ class HomeAssistantYellowOptionsFlowHandler(
|
||||
self._hardware_name = BOARD_NAME
|
||||
self._device = RADIO_DEVICE
|
||||
|
||||
self._probed_firmware_info = FirmwareInfo(
|
||||
device=self._device,
|
||||
firmware_type=ApplicationType(self.config_entry.data["firmware"]),
|
||||
firmware_version=None,
|
||||
source="guess",
|
||||
owners=[],
|
||||
)
|
||||
|
||||
# Regenerate the translation placeholders
|
||||
self._get_translation_placeholders()
|
||||
|
||||
@ -285,13 +303,13 @@ class HomeAssistantYellowOptionsFlowHandler(
|
||||
|
||||
def _async_flow_finished(self) -> ConfigFlowResult:
|
||||
"""Create the config entry."""
|
||||
assert self._probed_firmware_type is not None
|
||||
assert self._probed_firmware_info is not None
|
||||
|
||||
self.hass.config_entries.async_update_entry(
|
||||
entry=self.config_entry,
|
||||
data={
|
||||
**self.config_entry.data,
|
||||
FIRMWARE: self._probed_firmware_type.value,
|
||||
FIRMWARE: self._probed_firmware_info.firmware_type.value,
|
||||
},
|
||||
)
|
||||
|
||||
|
@ -17,6 +17,7 @@ from homeassistant.components.homeassistant_hardware.firmware_config_flow import
|
||||
)
|
||||
from homeassistant.components.homeassistant_hardware.util import (
|
||||
ApplicationType,
|
||||
FirmwareInfo,
|
||||
get_otbr_addon_manager,
|
||||
get_zigbee_flasher_addon_manager,
|
||||
)
|
||||
@ -65,13 +66,13 @@ class FakeFirmwareConfigFlow(BaseFirmwareConfigFlow, domain=TEST_DOMAIN):
|
||||
"""Create the config entry."""
|
||||
assert self._device is not None
|
||||
assert self._hardware_name is not None
|
||||
assert self._probed_firmware_type is not None
|
||||
assert self._probed_firmware_info is not None
|
||||
|
||||
return self.async_create_entry(
|
||||
title=self._hardware_name,
|
||||
data={
|
||||
"device": self._device,
|
||||
"firmware": self._probed_firmware_type.value,
|
||||
"firmware": self._probed_firmware_info.firmware_type.value,
|
||||
"hardware": self._hardware_name,
|
||||
},
|
||||
)
|
||||
@ -87,18 +88,26 @@ class FakeFirmwareOptionsFlowHandler(BaseFirmwareOptionsFlow):
|
||||
self._device = self.config_entry.data["device"]
|
||||
self._hardware_name = self.config_entry.data["hardware"]
|
||||
|
||||
self._probed_firmware_info = FirmwareInfo(
|
||||
device=self._device,
|
||||
firmware_type=ApplicationType(self.config_entry.data["firmware"]),
|
||||
firmware_version=None,
|
||||
source="guess",
|
||||
owners=[],
|
||||
)
|
||||
|
||||
# Regenerate the translation placeholders
|
||||
self._get_translation_placeholders()
|
||||
|
||||
def _async_flow_finished(self) -> ConfigFlowResult:
|
||||
"""Create the config entry."""
|
||||
assert self._probed_firmware_type is not None
|
||||
assert self._probed_firmware_info is not None
|
||||
|
||||
self.hass.config_entries.async_update_entry(
|
||||
entry=self.config_entry,
|
||||
data={
|
||||
**self.config_entry.data,
|
||||
"firmware": self._probed_firmware_type.value,
|
||||
"firmware": self._probed_firmware_info.firmware_type.value,
|
||||
},
|
||||
options=self.config_entry.options,
|
||||
)
|
||||
@ -142,7 +151,7 @@ def mock_addon_info(
|
||||
hass: HomeAssistant,
|
||||
*,
|
||||
is_hassio: bool = True,
|
||||
app_type: ApplicationType = ApplicationType.EZSP,
|
||||
app_type: ApplicationType | None = ApplicationType.EZSP,
|
||||
otbr_addon_info: AddonInfo = AddonInfo(
|
||||
available=True,
|
||||
hostname=None,
|
||||
@ -187,6 +196,17 @@ def mock_addon_info(
|
||||
)
|
||||
mock_otbr_manager.async_get_addon_info.return_value = otbr_addon_info
|
||||
|
||||
if app_type is None:
|
||||
firmware_info_result = None
|
||||
else:
|
||||
firmware_info_result = FirmwareInfo(
|
||||
device="/dev/ttyUSB0", # Not used
|
||||
firmware_type=app_type,
|
||||
firmware_version=None,
|
||||
owners=[],
|
||||
source="probe",
|
||||
)
|
||||
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.components.homeassistant_hardware.firmware_config_flow.get_otbr_addon_manager",
|
||||
@ -209,8 +229,8 @@ def mock_addon_info(
|
||||
return_value=is_hassio,
|
||||
),
|
||||
patch(
|
||||
"homeassistant.components.homeassistant_hardware.firmware_config_flow.probe_silabs_firmware_type",
|
||||
return_value=app_type,
|
||||
"homeassistant.components.homeassistant_hardware.firmware_config_flow.probe_silabs_firmware_info",
|
||||
return_value=firmware_info_result,
|
||||
),
|
||||
):
|
||||
yield mock_otbr_manager, mock_flasher_manager
|
||||
@ -274,10 +294,14 @@ async def test_config_flow_zigbee(hass: HomeAssistant) -> None:
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "confirm_zigbee"
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input={}
|
||||
)
|
||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||
with mock_addon_info(
|
||||
hass,
|
||||
app_type=ApplicationType.EZSP,
|
||||
):
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input={}
|
||||
)
|
||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||
|
||||
config_entry = result["result"]
|
||||
assert config_entry.data == {
|
||||
@ -347,10 +371,14 @@ async def test_config_flow_zigbee_skip_step_if_installed(hass: HomeAssistant) ->
|
||||
result = await hass.config_entries.flow.async_configure(result["flow_id"])
|
||||
|
||||
# Done
|
||||
await hass.async_block_till_done(wait_background_tasks=True)
|
||||
result = await hass.config_entries.flow.async_configure(result["flow_id"])
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "confirm_zigbee"
|
||||
with mock_addon_info(
|
||||
hass,
|
||||
app_type=ApplicationType.EZSP,
|
||||
):
|
||||
await hass.async_block_till_done(wait_background_tasks=True)
|
||||
result = await hass.config_entries.flow.async_configure(result["flow_id"])
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "confirm_zigbee"
|
||||
|
||||
|
||||
async def test_config_flow_thread(hass: HomeAssistant) -> None:
|
||||
@ -419,17 +447,21 @@ async def test_config_flow_thread(hass: HomeAssistant) -> None:
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "confirm_otbr"
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input={}
|
||||
)
|
||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||
with mock_addon_info(
|
||||
hass,
|
||||
app_type=ApplicationType.SPINEL,
|
||||
):
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input={}
|
||||
)
|
||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||
|
||||
config_entry = result["result"]
|
||||
assert config_entry.data == {
|
||||
"firmware": "spinel",
|
||||
"device": TEST_DEVICE,
|
||||
"hardware": TEST_HARDWARE_NAME,
|
||||
}
|
||||
config_entry = result["result"]
|
||||
assert config_entry.data == {
|
||||
"firmware": "spinel",
|
||||
"device": TEST_DEVICE,
|
||||
"hardware": TEST_HARDWARE_NAME,
|
||||
}
|
||||
|
||||
|
||||
async def test_config_flow_thread_addon_already_installed(hass: HomeAssistant) -> None:
|
||||
@ -477,10 +509,14 @@ async def test_config_flow_thread_addon_already_installed(hass: HomeAssistant) -
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "confirm_otbr"
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input={}
|
||||
)
|
||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||
with mock_addon_info(
|
||||
hass,
|
||||
app_type=ApplicationType.SPINEL,
|
||||
):
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input={}
|
||||
)
|
||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||
|
||||
|
||||
async def test_config_flow_zigbee_not_hassio(hass: HomeAssistant) -> None:
|
||||
@ -501,10 +537,10 @@ async def test_config_flow_zigbee_not_hassio(hass: HomeAssistant) -> None:
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "confirm_zigbee"
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input={}
|
||||
)
|
||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input={}
|
||||
)
|
||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||
|
||||
config_entry = result["result"]
|
||||
assert config_entry.data == {
|
||||
@ -538,17 +574,17 @@ async def test_options_flow_zigbee_to_thread(hass: HomeAssistant) -> None:
|
||||
|
||||
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
|
||||
# First step is confirmation
|
||||
result = await hass.config_entries.options.async_init(config_entry.entry_id)
|
||||
assert result["type"] is FlowResultType.MENU
|
||||
assert result["step_id"] == "pick_firmware"
|
||||
assert result["description_placeholders"]["firmware_type"] == "ezsp"
|
||||
assert result["description_placeholders"]["model"] == TEST_HARDWARE_NAME
|
||||
|
||||
with mock_addon_info(
|
||||
hass,
|
||||
app_type=ApplicationType.EZSP,
|
||||
) as (mock_otbr_manager, mock_flasher_manager):
|
||||
# First step is confirmation
|
||||
result = await hass.config_entries.options.async_init(config_entry.entry_id)
|
||||
assert result["type"] is FlowResultType.MENU
|
||||
assert result["step_id"] == "pick_firmware"
|
||||
assert result["description_placeholders"]["firmware_type"] == "ezsp"
|
||||
assert result["description_placeholders"]["model"] == TEST_HARDWARE_NAME
|
||||
|
||||
result = await hass.config_entries.options.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={"next_step_id": STEP_PICK_FIRMWARE_THREAD},
|
||||
@ -599,14 +635,18 @@ async def test_options_flow_zigbee_to_thread(hass: HomeAssistant) -> None:
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "confirm_otbr"
|
||||
|
||||
# We are now done
|
||||
result = await hass.config_entries.options.async_configure(
|
||||
result["flow_id"], user_input={}
|
||||
)
|
||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||
with mock_addon_info(
|
||||
hass,
|
||||
app_type=ApplicationType.SPINEL,
|
||||
):
|
||||
# We are now done
|
||||
result = await hass.config_entries.options.async_configure(
|
||||
result["flow_id"], user_input={}
|
||||
)
|
||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||
|
||||
# The firmware type has been updated
|
||||
assert config_entry.data["firmware"] == "spinel"
|
||||
# The firmware type has been updated
|
||||
assert config_entry.data["firmware"] == "spinel"
|
||||
|
||||
|
||||
async def test_options_flow_thread_to_zigbee(hass: HomeAssistant) -> None:
|
||||
@ -680,11 +720,15 @@ async def test_options_flow_thread_to_zigbee(hass: HomeAssistant) -> None:
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "confirm_zigbee"
|
||||
|
||||
# We are now done
|
||||
result = await hass.config_entries.options.async_configure(
|
||||
result["flow_id"], user_input={}
|
||||
)
|
||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||
with mock_addon_info(
|
||||
hass,
|
||||
app_type=ApplicationType.EZSP,
|
||||
):
|
||||
# We are now done
|
||||
result = await hass.config_entries.options.async_configure(
|
||||
result["flow_id"], user_input={}
|
||||
)
|
||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||
|
||||
# The firmware type has been updated
|
||||
assert config_entry.data["firmware"] == "ezsp"
|
||||
# The firmware type has been updated
|
||||
assert config_entry.data["firmware"] == "ezsp"
|
||||
|
@ -309,6 +309,42 @@ async def test_config_flow_zigbee_flasher_uninstall_fails(hass: HomeAssistant) -
|
||||
assert result["step_id"] == "confirm_zigbee"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"ignore_translations",
|
||||
["component.test_firmware_domain.config.abort.unsupported_firmware"],
|
||||
)
|
||||
async def test_config_flow_zigbee_confirmation_fails(hass: HomeAssistant) -> None:
|
||||
"""Test the config flow failing due to Zigbee firmware not being detected."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
TEST_DOMAIN, context={"source": "hardware"}
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.MENU
|
||||
assert result["step_id"] == "pick_firmware"
|
||||
|
||||
with mock_addon_info(
|
||||
hass,
|
||||
app_type=ApplicationType.EZSP,
|
||||
) as (mock_otbr_manager, mock_flasher_manager):
|
||||
# Pick the menu option: we are now installing the addon
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={"next_step_id": STEP_PICK_FIRMWARE_ZIGBEE},
|
||||
)
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "confirm_zigbee"
|
||||
|
||||
with mock_addon_info(
|
||||
hass,
|
||||
app_type=None, # Probing fails
|
||||
) as (mock_otbr_manager, mock_flasher_manager):
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input={}
|
||||
)
|
||||
assert result["type"] is FlowResultType.ABORT
|
||||
assert result["reason"] == "unsupported_firmware"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"ignore_translations",
|
||||
["component.test_firmware_domain.config.abort.not_hassio_thread"],
|
||||
@ -530,6 +566,48 @@ async def test_config_flow_thread_flasher_uninstall_fails(hass: HomeAssistant) -
|
||||
assert result["step_id"] == "confirm_otbr"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"ignore_translations",
|
||||
["component.test_firmware_domain.config.abort.unsupported_firmware"],
|
||||
)
|
||||
async def test_config_flow_thread_confirmation_fails(hass: HomeAssistant) -> None:
|
||||
"""Test the config flow failing due to OpenThread firmware not being detected."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
TEST_DOMAIN, context={"source": "hardware"}
|
||||
)
|
||||
|
||||
with mock_addon_info(
|
||||
hass,
|
||||
app_type=ApplicationType.EZSP,
|
||||
) as (mock_otbr_manager, mock_flasher_manager):
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input={}
|
||||
)
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={"next_step_id": STEP_PICK_FIRMWARE_THREAD},
|
||||
)
|
||||
result = await hass.config_entries.flow.async_configure(result["flow_id"])
|
||||
await hass.async_block_till_done(wait_background_tasks=True)
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(result["flow_id"])
|
||||
await hass.async_block_till_done(wait_background_tasks=True)
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(result["flow_id"])
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "confirm_otbr"
|
||||
|
||||
with mock_addon_info(
|
||||
hass,
|
||||
app_type=None, # Probing fails
|
||||
) as (mock_otbr_manager, mock_flasher_manager):
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input={}
|
||||
)
|
||||
assert result["type"] is FlowResultType.ABORT
|
||||
assert result["reason"] == "unsupported_firmware"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"ignore_translations",
|
||||
["component.test_firmware_domain.options.abort.zha_still_using_stick"],
|
||||
|
@ -2,6 +2,10 @@
|
||||
|
||||
from unittest.mock import AsyncMock, MagicMock, patch
|
||||
|
||||
import pytest
|
||||
from universal_silabs_flasher.common import Version as FlasherVersion
|
||||
from universal_silabs_flasher.const import ApplicationType as FlasherApplicationType
|
||||
|
||||
from homeassistant.components.hassio import (
|
||||
AddonError,
|
||||
AddonInfo,
|
||||
@ -18,6 +22,8 @@ from homeassistant.components.homeassistant_hardware.util import (
|
||||
OwningIntegration,
|
||||
get_otbr_addon_firmware_info,
|
||||
guess_firmware_info,
|
||||
probe_silabs_firmware_info,
|
||||
probe_silabs_firmware_type,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry, ConfigEntryState
|
||||
from homeassistant.core import HomeAssistant
|
||||
@ -280,3 +286,95 @@ async def test_get_otbr_addon_firmware_info_failure_bad_options(
|
||||
)
|
||||
|
||||
assert (await get_otbr_addon_firmware_info(hass, otbr_addon_manager)) is None
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("app_type", "firmware_version", "expected_fw_info"),
|
||||
[
|
||||
(
|
||||
FlasherApplicationType.EZSP,
|
||||
FlasherVersion("1.0.0"),
|
||||
FirmwareInfo(
|
||||
device="/dev/ttyUSB0",
|
||||
firmware_type=ApplicationType.EZSP,
|
||||
firmware_version="1.0.0",
|
||||
source="probe",
|
||||
owners=[],
|
||||
),
|
||||
),
|
||||
(
|
||||
FlasherApplicationType.EZSP,
|
||||
None,
|
||||
FirmwareInfo(
|
||||
device="/dev/ttyUSB0",
|
||||
firmware_type=ApplicationType.EZSP,
|
||||
firmware_version=None,
|
||||
source="probe",
|
||||
owners=[],
|
||||
),
|
||||
),
|
||||
(
|
||||
FlasherApplicationType.SPINEL,
|
||||
FlasherVersion("2.0.0"),
|
||||
FirmwareInfo(
|
||||
device="/dev/ttyUSB0",
|
||||
firmware_type=ApplicationType.SPINEL,
|
||||
firmware_version="2.0.0",
|
||||
source="probe",
|
||||
owners=[],
|
||||
),
|
||||
),
|
||||
(None, None, None),
|
||||
],
|
||||
)
|
||||
async def test_probe_silabs_firmware_info(
|
||||
app_type: FlasherApplicationType | None,
|
||||
firmware_version: FlasherVersion | None,
|
||||
expected_fw_info: FirmwareInfo | None,
|
||||
) -> None:
|
||||
"""Test getting the firmware info."""
|
||||
|
||||
def probe_app_type() -> None:
|
||||
mock_flasher.app_type = app_type
|
||||
mock_flasher.app_version = firmware_version
|
||||
|
||||
mock_flasher = MagicMock()
|
||||
mock_flasher.app_type = None
|
||||
mock_flasher.app_version = None
|
||||
mock_flasher.probe_app_type = AsyncMock(side_effect=probe_app_type)
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.homeassistant_hardware.util.Flasher",
|
||||
return_value=mock_flasher,
|
||||
):
|
||||
result = await probe_silabs_firmware_info("/dev/ttyUSB0")
|
||||
assert result == expected_fw_info
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("probe_result", "expected"),
|
||||
[
|
||||
(
|
||||
FirmwareInfo(
|
||||
device="/dev/ttyUSB0",
|
||||
firmware_type=ApplicationType.EZSP,
|
||||
firmware_version=None,
|
||||
source="unknown",
|
||||
owners=[],
|
||||
),
|
||||
ApplicationType.EZSP,
|
||||
),
|
||||
(None, None),
|
||||
],
|
||||
)
|
||||
async def test_probe_silabs_firmware_type(
|
||||
probe_result: FirmwareInfo | None, expected: ApplicationType | None
|
||||
) -> None:
|
||||
"""Test getting the firmware type from the probe result."""
|
||||
with patch(
|
||||
"homeassistant.components.homeassistant_hardware.util.probe_silabs_firmware_info",
|
||||
autospec=True,
|
||||
return_value=probe_result,
|
||||
):
|
||||
result = await probe_silabs_firmware_type("/dev/ttyUSB0")
|
||||
assert result == expected
|
||||
|
@ -13,6 +13,10 @@ from homeassistant.components.homeassistant_hardware.silabs_multiprotocol_addon
|
||||
get_flasher_addon_manager,
|
||||
get_multiprotocol_addon_manager,
|
||||
)
|
||||
from homeassistant.components.homeassistant_hardware.util import (
|
||||
ApplicationType,
|
||||
FirmwareInfo,
|
||||
)
|
||||
from homeassistant.components.homeassistant_sky_connect.const import DOMAIN
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.data_entry_flow import FlowResultType
|
||||
@ -61,10 +65,22 @@ async def test_config_flow(
|
||||
async def mock_async_step_pick_firmware_zigbee(self, data):
|
||||
return await self.async_step_confirm_zigbee(user_input={})
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.homeassistant_hardware.firmware_config_flow.BaseFirmwareConfigFlow.async_step_pick_firmware_zigbee",
|
||||
autospec=True,
|
||||
side_effect=mock_async_step_pick_firmware_zigbee,
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.components.homeassistant_hardware.firmware_config_flow.BaseFirmwareConfigFlow.async_step_pick_firmware_zigbee",
|
||||
autospec=True,
|
||||
side_effect=mock_async_step_pick_firmware_zigbee,
|
||||
),
|
||||
patch(
|
||||
"homeassistant.components.homeassistant_hardware.firmware_config_flow.probe_silabs_firmware_info",
|
||||
return_value=FirmwareInfo(
|
||||
device=usb_data.device,
|
||||
firmware_type=ApplicationType.EZSP,
|
||||
firmware_version=None,
|
||||
owners=[],
|
||||
source="probe",
|
||||
),
|
||||
),
|
||||
):
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
@ -134,10 +150,22 @@ async def test_options_flow(
|
||||
async def mock_async_step_pick_firmware_zigbee(self, data):
|
||||
return await self.async_step_confirm_zigbee(user_input={})
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.homeassistant_hardware.firmware_config_flow.BaseFirmwareOptionsFlow.async_step_pick_firmware_zigbee",
|
||||
autospec=True,
|
||||
side_effect=mock_async_step_pick_firmware_zigbee,
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.components.homeassistant_hardware.firmware_config_flow.BaseFirmwareOptionsFlow.async_step_pick_firmware_zigbee",
|
||||
autospec=True,
|
||||
side_effect=mock_async_step_pick_firmware_zigbee,
|
||||
),
|
||||
patch(
|
||||
"homeassistant.components.homeassistant_hardware.firmware_config_flow.probe_silabs_firmware_info",
|
||||
return_value=FirmwareInfo(
|
||||
device=usb_data.device,
|
||||
firmware_type=ApplicationType.EZSP,
|
||||
firmware_version=None,
|
||||
owners=[],
|
||||
source="probe",
|
||||
),
|
||||
),
|
||||
):
|
||||
result = await hass.config_entries.options.async_configure(
|
||||
result["flow_id"],
|
||||
|
@ -18,7 +18,10 @@ from homeassistant.components.homeassistant_hardware.silabs_multiprotocol_addon
|
||||
get_flasher_addon_manager,
|
||||
get_multiprotocol_addon_manager,
|
||||
)
|
||||
from homeassistant.components.homeassistant_hardware.util import ApplicationType
|
||||
from homeassistant.components.homeassistant_hardware.util import (
|
||||
ApplicationType,
|
||||
FirmwareInfo,
|
||||
)
|
||||
from homeassistant.components.homeassistant_yellow.const import DOMAIN, RADIO_DEVICE
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.data_entry_flow import FlowResultType
|
||||
@ -82,8 +85,14 @@ async def test_config_flow(hass: HomeAssistant) -> None:
|
||||
return_value=True,
|
||||
) as mock_setup_entry,
|
||||
patch(
|
||||
"homeassistant.components.homeassistant_hardware.firmware_config_flow.probe_silabs_firmware_type",
|
||||
return_value=ApplicationType.EZSP,
|
||||
"homeassistant.components.homeassistant_hardware.firmware_config_flow.probe_silabs_firmware_info",
|
||||
return_value=FirmwareInfo(
|
||||
device=RADIO_DEVICE,
|
||||
firmware_type=ApplicationType.EZSP,
|
||||
firmware_version=None,
|
||||
owners=[],
|
||||
source="probe",
|
||||
),
|
||||
),
|
||||
):
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
@ -330,10 +339,22 @@ async def test_firmware_options_flow(hass: HomeAssistant) -> None:
|
||||
async def mock_async_step_pick_firmware_zigbee(self, data):
|
||||
return await self.async_step_confirm_zigbee(user_input={})
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.homeassistant_hardware.firmware_config_flow.BaseFirmwareOptionsFlow.async_step_pick_firmware_zigbee",
|
||||
autospec=True,
|
||||
side_effect=mock_async_step_pick_firmware_zigbee,
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.components.homeassistant_hardware.firmware_config_flow.BaseFirmwareOptionsFlow.async_step_pick_firmware_zigbee",
|
||||
autospec=True,
|
||||
side_effect=mock_async_step_pick_firmware_zigbee,
|
||||
),
|
||||
patch(
|
||||
"homeassistant.components.homeassistant_hardware.firmware_config_flow.probe_silabs_firmware_info",
|
||||
return_value=FirmwareInfo(
|
||||
device=RADIO_DEVICE,
|
||||
firmware_type=ApplicationType.EZSP,
|
||||
firmware_version=None,
|
||||
owners=[],
|
||||
source="probe",
|
||||
),
|
||||
),
|
||||
):
|
||||
result = await hass.config_entries.options.async_configure(
|
||||
result["flow_id"],
|
||||
|
Loading…
x
Reference in New Issue
Block a user