mirror of
https://github.com/home-assistant/core.git
synced 2025-07-28 07:37:34 +00:00
Yellow firmware selection options flow (#122868)
* Implement Yellow config flow for firmware selection * Use the probed firmware type when setting up Yellow * Add translation strings * Ensure (most) existing `init` tests pass * Remove multi-PAN setup config flow unit tests * Get existing config flow unit tests passing * Add unit tests for uninstalling multi-PAN and such * Consolidate entity creation for Yellow and clean up steps * Be explicit with multiple inheritance overrides * Address review comments
This commit is contained in:
parent
467749eb57
commit
5818e2c2d4
@ -2,18 +2,24 @@
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
from homeassistant.components.hassio import get_os_info, is_hassio
|
from homeassistant.components.hassio import get_os_info, is_hassio
|
||||||
from homeassistant.components.homeassistant_hardware.silabs_multiprotocol_addon import (
|
from homeassistant.components.homeassistant_hardware.silabs_multiprotocol_addon import (
|
||||||
check_multi_pan_addon,
|
check_multi_pan_addon,
|
||||||
get_zigbee_socket,
|
)
|
||||||
multi_pan_addon_using_device,
|
from homeassistant.components.homeassistant_hardware.util import (
|
||||||
|
ApplicationType,
|
||||||
|
guess_firmware_type,
|
||||||
)
|
)
|
||||||
from homeassistant.config_entries import SOURCE_HARDWARE, ConfigEntry
|
from homeassistant.config_entries import SOURCE_HARDWARE, ConfigEntry
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.exceptions import ConfigEntryNotReady, HomeAssistantError
|
from homeassistant.exceptions import ConfigEntryNotReady, HomeAssistantError
|
||||||
from homeassistant.helpers import discovery_flow
|
from homeassistant.helpers import discovery_flow
|
||||||
|
|
||||||
from .const import RADIO_DEVICE, ZHA_HW_DISCOVERY_DATA
|
from .const import FIRMWARE, RADIO_DEVICE, ZHA_HW_DISCOVERY_DATA
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||||
@ -27,34 +33,26 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||||||
# The hassio integration has not yet fetched data from the supervisor
|
# The hassio integration has not yet fetched data from the supervisor
|
||||||
raise ConfigEntryNotReady
|
raise ConfigEntryNotReady
|
||||||
|
|
||||||
board: str | None
|
if os_info.get("board") != "yellow":
|
||||||
if (board := os_info.get("board")) is None or board != "yellow":
|
|
||||||
# Not running on a Home Assistant Yellow, Home Assistant may have been migrated
|
# Not running on a Home Assistant Yellow, Home Assistant may have been migrated
|
||||||
hass.async_create_task(hass.config_entries.async_remove(entry.entry_id))
|
hass.async_create_task(hass.config_entries.async_remove(entry.entry_id))
|
||||||
return False
|
return False
|
||||||
|
|
||||||
try:
|
firmware = ApplicationType(entry.data[FIRMWARE])
|
||||||
await check_multi_pan_addon(hass)
|
|
||||||
except HomeAssistantError as err:
|
|
||||||
raise ConfigEntryNotReady from err
|
|
||||||
|
|
||||||
if not await multi_pan_addon_using_device(hass, RADIO_DEVICE):
|
if firmware is ApplicationType.CPC:
|
||||||
hw_discovery_data = ZHA_HW_DISCOVERY_DATA
|
try:
|
||||||
else:
|
await check_multi_pan_addon(hass)
|
||||||
hw_discovery_data = {
|
except HomeAssistantError as err:
|
||||||
"name": "Yellow Multiprotocol",
|
raise ConfigEntryNotReady from err
|
||||||
"port": {
|
|
||||||
"path": get_zigbee_socket(),
|
|
||||||
},
|
|
||||||
"radio_type": "ezsp",
|
|
||||||
}
|
|
||||||
|
|
||||||
discovery_flow.async_create_flow(
|
if firmware is ApplicationType.EZSP:
|
||||||
hass,
|
discovery_flow.async_create_flow(
|
||||||
"zha",
|
hass,
|
||||||
context={"source": SOURCE_HARDWARE},
|
"zha",
|
||||||
data=hw_discovery_data,
|
context={"source": SOURCE_HARDWARE},
|
||||||
)
|
data=ZHA_HW_DISCOVERY_DATA,
|
||||||
|
)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@ -62,3 +60,39 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||||
"""Unload a config entry."""
|
"""Unload a config entry."""
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
async def async_migrate_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool:
|
||||||
|
"""Migrate old entry."""
|
||||||
|
|
||||||
|
_LOGGER.debug(
|
||||||
|
"Migrating from version %s.%s", config_entry.version, config_entry.minor_version
|
||||||
|
)
|
||||||
|
|
||||||
|
if config_entry.version == 1:
|
||||||
|
if config_entry.minor_version == 1:
|
||||||
|
# Add-on startup with type service get started before Core, always (e.g. the
|
||||||
|
# Multi-Protocol add-on). Probing the firmware would interfere with the add-on,
|
||||||
|
# so we can't safely probe here. Instead, we must make an educated guess!
|
||||||
|
firmware_guess = await guess_firmware_type(hass, RADIO_DEVICE)
|
||||||
|
|
||||||
|
new_data = {**config_entry.data}
|
||||||
|
new_data[FIRMWARE] = firmware_guess.firmware_type.value
|
||||||
|
|
||||||
|
hass.config_entries.async_update_entry(
|
||||||
|
config_entry,
|
||||||
|
data=new_data,
|
||||||
|
version=1,
|
||||||
|
minor_version=2,
|
||||||
|
)
|
||||||
|
|
||||||
|
_LOGGER.debug(
|
||||||
|
"Migration to version %s.%s successful",
|
||||||
|
config_entry.version,
|
||||||
|
config_entry.minor_version,
|
||||||
|
)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
# This means the user has downgraded from a future version
|
||||||
|
return False
|
||||||
|
@ -2,11 +2,13 @@
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from abc import ABC, abstractmethod
|
||||||
import asyncio
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
from typing import Any
|
from typing import Any, final
|
||||||
|
|
||||||
import aiohttp
|
import aiohttp
|
||||||
|
from universal_silabs_flasher.const import ApplicationType
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.components.hassio import (
|
from homeassistant.components.hassio import (
|
||||||
@ -15,12 +17,25 @@ from homeassistant.components.hassio import (
|
|||||||
async_reboot_host,
|
async_reboot_host,
|
||||||
async_set_yellow_settings,
|
async_set_yellow_settings,
|
||||||
)
|
)
|
||||||
from homeassistant.components.homeassistant_hardware import silabs_multiprotocol_addon
|
from homeassistant.components.homeassistant_hardware.firmware_config_flow import (
|
||||||
from homeassistant.config_entries import ConfigEntry, ConfigFlow, ConfigFlowResult
|
BaseFirmwareConfigFlow,
|
||||||
|
BaseFirmwareOptionsFlow,
|
||||||
|
)
|
||||||
|
from homeassistant.components.homeassistant_hardware.silabs_multiprotocol_addon import (
|
||||||
|
OptionsFlowHandler as MultiprotocolOptionsFlowHandler,
|
||||||
|
SerialPortSettings as MultiprotocolSerialPortSettings,
|
||||||
|
)
|
||||||
|
from homeassistant.config_entries import (
|
||||||
|
SOURCE_HARDWARE,
|
||||||
|
ConfigEntry,
|
||||||
|
ConfigFlowResult,
|
||||||
|
OptionsFlow,
|
||||||
|
)
|
||||||
from homeassistant.core import callback
|
from homeassistant.core import callback
|
||||||
from homeassistant.helpers import selector
|
from homeassistant.helpers import discovery_flow, selector
|
||||||
|
|
||||||
from .const import DOMAIN, ZHA_HW_DISCOVERY_DATA
|
from .const import DOMAIN, FIRMWARE, RADIO_DEVICE, ZHA_DOMAIN, ZHA_HW_DISCOVERY_DATA
|
||||||
|
from .hardware import BOARD_NAME
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -33,18 +48,30 @@ STEP_HW_SETTINGS_SCHEMA = vol.Schema(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class HomeAssistantYellowConfigFlow(ConfigFlow, domain=DOMAIN):
|
class HomeAssistantYellowConfigFlow(BaseFirmwareConfigFlow, domain=DOMAIN):
|
||||||
"""Handle a config flow for Home Assistant Yellow."""
|
"""Handle a config flow for Home Assistant Yellow."""
|
||||||
|
|
||||||
VERSION = 1
|
VERSION = 1
|
||||||
|
MINOR_VERSION = 2
|
||||||
|
|
||||||
|
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
||||||
|
"""Instantiate config flow."""
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
self._device = RADIO_DEVICE
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@callback
|
@callback
|
||||||
def async_get_options_flow(
|
def async_get_options_flow(
|
||||||
config_entry: ConfigEntry,
|
config_entry: ConfigEntry,
|
||||||
) -> HomeAssistantYellowOptionsFlow:
|
) -> OptionsFlow:
|
||||||
"""Return the options flow."""
|
"""Return the options flow."""
|
||||||
return HomeAssistantYellowOptionsFlow(config_entry)
|
firmware_type = ApplicationType(config_entry.data[FIRMWARE])
|
||||||
|
|
||||||
|
if firmware_type is ApplicationType.CPC:
|
||||||
|
return HomeAssistantYellowMultiPanOptionsFlowHandler(config_entry)
|
||||||
|
|
||||||
|
return HomeAssistantYellowOptionsFlowHandler(config_entry)
|
||||||
|
|
||||||
async def async_step_system(
|
async def async_step_system(
|
||||||
self, data: dict[str, Any] | None = None
|
self, data: dict[str, Any] | None = None
|
||||||
@ -53,30 +80,56 @@ class HomeAssistantYellowConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||||||
if self._async_current_entries():
|
if self._async_current_entries():
|
||||||
return self.async_abort(reason="single_instance_allowed")
|
return self.async_abort(reason="single_instance_allowed")
|
||||||
|
|
||||||
return self.async_create_entry(title="Home Assistant Yellow", data={})
|
# We do not actually use any portion of `BaseFirmwareConfigFlow` beyond this
|
||||||
|
await self._probe_firmware_type()
|
||||||
|
|
||||||
|
# Kick off ZHA hardware discovery automatically if Zigbee firmware is running
|
||||||
|
if self._probed_firmware_type is ApplicationType.EZSP:
|
||||||
|
discovery_flow.async_create_flow(
|
||||||
|
self.hass,
|
||||||
|
ZHA_DOMAIN,
|
||||||
|
context={"source": SOURCE_HARDWARE},
|
||||||
|
data=ZHA_HW_DISCOVERY_DATA,
|
||||||
|
)
|
||||||
|
|
||||||
|
return self._async_flow_finished()
|
||||||
|
|
||||||
|
def _async_flow_finished(self) -> ConfigFlowResult:
|
||||||
|
"""Create the config entry."""
|
||||||
|
assert self._probed_firmware_type is not None
|
||||||
|
|
||||||
|
return self.async_create_entry(
|
||||||
|
title=BOARD_NAME,
|
||||||
|
data={
|
||||||
|
# Assume the firmware type is EZSP if we cannot probe it
|
||||||
|
FIRMWARE: (self._probed_firmware_type or ApplicationType.EZSP).value,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class HomeAssistantYellowOptionsFlow(silabs_multiprotocol_addon.OptionsFlowHandler):
|
class BaseHomeAssistantYellowOptionsFlow(OptionsFlow, ABC):
|
||||||
"""Handle an option flow for Home Assistant Yellow."""
|
"""Base Home Assistant Yellow options flow shared between firmware and multi-PAN."""
|
||||||
|
|
||||||
_hw_settings: dict[str, bool] | None = None
|
_hw_settings: dict[str, bool] | None = None
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
async def async_step_main_menu(self, _: None = None) -> ConfigFlowResult:
|
||||||
|
"""Show the main menu."""
|
||||||
|
|
||||||
|
@final
|
||||||
|
async def async_step_init(
|
||||||
|
self, user_input: dict[str, Any] | None = None
|
||||||
|
) -> ConfigFlowResult:
|
||||||
|
"""Manage the options flow."""
|
||||||
|
return await self.async_step_main_menu()
|
||||||
|
|
||||||
|
@final
|
||||||
async def async_step_on_supervisor(
|
async def async_step_on_supervisor(
|
||||||
self, user_input: dict[str, Any] | None = None
|
self, user_input: dict[str, Any] | None = None
|
||||||
) -> ConfigFlowResult:
|
) -> ConfigFlowResult:
|
||||||
"""Handle logic when on Supervisor host."""
|
"""Handle logic when on Supervisor host."""
|
||||||
return await self.async_step_main_menu()
|
return await self.async_step_main_menu()
|
||||||
|
|
||||||
async def async_step_main_menu(self, _: None = None) -> ConfigFlowResult:
|
|
||||||
"""Show the main menu."""
|
|
||||||
return self.async_show_menu(
|
|
||||||
step_id="main_menu",
|
|
||||||
menu_options=[
|
|
||||||
"hardware_settings",
|
|
||||||
"multipan_settings",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
async def async_step_hardware_settings(
|
async def async_step_hardware_settings(
|
||||||
self, user_input: dict[str, Any] | None = None
|
self, user_input: dict[str, Any] | None = None
|
||||||
) -> ConfigFlowResult:
|
) -> ConfigFlowResult:
|
||||||
@ -133,18 +186,36 @@ class HomeAssistantYellowOptionsFlow(silabs_multiprotocol_addon.OptionsFlowHandl
|
|||||||
"""Reboot later."""
|
"""Reboot later."""
|
||||||
return self.async_create_entry(data={})
|
return self.async_create_entry(data={})
|
||||||
|
|
||||||
|
|
||||||
|
class HomeAssistantYellowMultiPanOptionsFlowHandler(
|
||||||
|
BaseHomeAssistantYellowOptionsFlow, MultiprotocolOptionsFlowHandler
|
||||||
|
):
|
||||||
|
"""Handle a multi-PAN options flow for Home Assistant Yellow."""
|
||||||
|
|
||||||
|
async def async_step_main_menu(self, _: None = None) -> ConfigFlowResult:
|
||||||
|
"""Show the main menu."""
|
||||||
|
return self.async_show_menu(
|
||||||
|
step_id="main_menu",
|
||||||
|
menu_options=[
|
||||||
|
"hardware_settings",
|
||||||
|
"multipan_settings",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
async def async_step_multipan_settings(
|
async def async_step_multipan_settings(
|
||||||
self, user_input: dict[str, Any] | None = None
|
self, user_input: dict[str, Any] | None = None
|
||||||
) -> ConfigFlowResult:
|
) -> ConfigFlowResult:
|
||||||
"""Handle multipan settings."""
|
"""Handle multipan settings."""
|
||||||
return await super().async_step_on_supervisor(user_input)
|
return await MultiprotocolOptionsFlowHandler.async_step_on_supervisor(
|
||||||
|
self, user_input
|
||||||
|
)
|
||||||
|
|
||||||
async def _async_serial_port_settings(
|
async def _async_serial_port_settings(
|
||||||
self,
|
self,
|
||||||
) -> silabs_multiprotocol_addon.SerialPortSettings:
|
) -> MultiprotocolSerialPortSettings:
|
||||||
"""Return the radio serial port settings."""
|
"""Return the radio serial port settings."""
|
||||||
return silabs_multiprotocol_addon.SerialPortSettings(
|
return MultiprotocolSerialPortSettings(
|
||||||
device="/dev/ttyAMA1",
|
device=RADIO_DEVICE,
|
||||||
baudrate="115200",
|
baudrate="115200",
|
||||||
flow_control=True,
|
flow_control=True,
|
||||||
)
|
)
|
||||||
@ -163,4 +234,64 @@ class HomeAssistantYellowOptionsFlow(silabs_multiprotocol_addon.OptionsFlowHandl
|
|||||||
|
|
||||||
def _hardware_name(self) -> str:
|
def _hardware_name(self) -> str:
|
||||||
"""Return the name of the hardware."""
|
"""Return the name of the hardware."""
|
||||||
return "Home Assistant Yellow"
|
return BOARD_NAME
|
||||||
|
|
||||||
|
async def async_step_flashing_complete(
|
||||||
|
self, user_input: dict[str, Any] | None = None
|
||||||
|
) -> ConfigFlowResult:
|
||||||
|
"""Finish flashing and update the config entry."""
|
||||||
|
self.hass.config_entries.async_update_entry(
|
||||||
|
entry=self.config_entry,
|
||||||
|
data={
|
||||||
|
**self.config_entry.data,
|
||||||
|
FIRMWARE: ApplicationType.EZSP.value,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
return await super().async_step_flashing_complete(user_input)
|
||||||
|
|
||||||
|
|
||||||
|
class HomeAssistantYellowOptionsFlowHandler(
|
||||||
|
BaseHomeAssistantYellowOptionsFlow, BaseFirmwareOptionsFlow
|
||||||
|
):
|
||||||
|
"""Handle a firmware options flow for Home Assistant Yellow."""
|
||||||
|
|
||||||
|
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
||||||
|
"""Instantiate options flow."""
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
self._hardware_name = BOARD_NAME
|
||||||
|
self._device = RADIO_DEVICE
|
||||||
|
|
||||||
|
# Regenerate the translation placeholders
|
||||||
|
self._get_translation_placeholders()
|
||||||
|
|
||||||
|
async def async_step_main_menu(self, _: None = None) -> ConfigFlowResult:
|
||||||
|
"""Show the main menu."""
|
||||||
|
return self.async_show_menu(
|
||||||
|
step_id="main_menu",
|
||||||
|
menu_options=[
|
||||||
|
"hardware_settings",
|
||||||
|
"firmware_settings",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
async def async_step_firmware_settings(
|
||||||
|
self, user_input: dict[str, Any] | None = None
|
||||||
|
) -> ConfigFlowResult:
|
||||||
|
"""Handle firmware configuration settings."""
|
||||||
|
return await super().async_step_pick_firmware()
|
||||||
|
|
||||||
|
def _async_flow_finished(self) -> ConfigFlowResult:
|
||||||
|
"""Create the config entry."""
|
||||||
|
assert self._probed_firmware_type 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,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
return self.async_create_entry(title="", data={})
|
||||||
|
@ -12,3 +12,6 @@ ZHA_HW_DISCOVERY_DATA = {
|
|||||||
},
|
},
|
||||||
"radio_type": "efr32",
|
"radio_type": "efr32",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FIRMWARE = "firmware"
|
||||||
|
ZHA_DOMAIN = "zha"
|
||||||
|
@ -42,6 +42,7 @@
|
|||||||
"main_menu": {
|
"main_menu": {
|
||||||
"menu_options": {
|
"menu_options": {
|
||||||
"hardware_settings": "[%key:component::homeassistant_yellow::options::step::hardware_settings::title%]",
|
"hardware_settings": "[%key:component::homeassistant_yellow::options::step::hardware_settings::title%]",
|
||||||
|
"firmware_settings": "Switch between Zigbee or Thread firmware.",
|
||||||
"multipan_settings": "Configure IEEE 802.15.4 radio multiprotocol support"
|
"multipan_settings": "Configure IEEE 802.15.4 radio multiprotocol support"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -79,6 +80,46 @@
|
|||||||
"start_flasher_addon": {
|
"start_flasher_addon": {
|
||||||
"title": "[%key:component::homeassistant_hardware::silabs_multiprotocol_hardware::options::step::start_flasher_addon::title%]",
|
"title": "[%key:component::homeassistant_hardware::silabs_multiprotocol_hardware::options::step::start_flasher_addon::title%]",
|
||||||
"description": "[%key:component::homeassistant_hardware::silabs_multiprotocol_hardware::options::step::start_flasher_addon::description%]"
|
"description": "[%key:component::homeassistant_hardware::silabs_multiprotocol_hardware::options::step::start_flasher_addon::description%]"
|
||||||
|
},
|
||||||
|
"pick_firmware": {
|
||||||
|
"title": "[%key:component::homeassistant_hardware::firmware_picker::options::step::pick_firmware::title%]",
|
||||||
|
"description": "[%key:component::homeassistant_hardware::firmware_picker::options::step::pick_firmware::description%]",
|
||||||
|
"menu_options": {
|
||||||
|
"pick_firmware_thread": "[%key:component::homeassistant_hardware::firmware_picker::options::step::pick_firmware::menu_options::pick_firmware_thread%]",
|
||||||
|
"pick_firmware_zigbee": "[%key:component::homeassistant_hardware::firmware_picker::options::step::pick_firmware::menu_options::pick_firmware_zigbee%]"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"install_zigbee_flasher_addon": {
|
||||||
|
"title": "[%key:component::homeassistant_hardware::firmware_picker::options::step::install_zigbee_flasher_addon::title%]",
|
||||||
|
"description": "[%key:component::homeassistant_hardware::firmware_picker::options::step::install_zigbee_flasher_addon::description%]"
|
||||||
|
},
|
||||||
|
"run_zigbee_flasher_addon": {
|
||||||
|
"title": "[%key:component::homeassistant_hardware::firmware_picker::options::step::run_zigbee_flasher_addon::title%]",
|
||||||
|
"description": "[%key:component::homeassistant_hardware::firmware_picker::options::step::run_zigbee_flasher_addon::description%]"
|
||||||
|
},
|
||||||
|
"zigbee_flasher_failed": {
|
||||||
|
"title": "[%key:component::homeassistant_hardware::firmware_picker::options::step::zigbee_flasher_failed::title%]",
|
||||||
|
"description": "[%key:component::homeassistant_hardware::firmware_picker::options::step::zigbee_flasher_failed::description%]"
|
||||||
|
},
|
||||||
|
"confirm_zigbee": {
|
||||||
|
"title": "[%key:component::homeassistant_hardware::firmware_picker::options::step::confirm_zigbee::title%]",
|
||||||
|
"description": "[%key:component::homeassistant_hardware::firmware_picker::options::step::confirm_zigbee::description%]"
|
||||||
|
},
|
||||||
|
"install_otbr_addon": {
|
||||||
|
"title": "[%key:component::homeassistant_hardware::firmware_picker::options::step::install_otbr_addon::title%]",
|
||||||
|
"description": "[%key:component::homeassistant_hardware::firmware_picker::options::step::install_otbr_addon::description%]"
|
||||||
|
},
|
||||||
|
"start_otbr_addon": {
|
||||||
|
"title": "[%key:component::homeassistant_hardware::firmware_picker::options::step::start_otbr_addon::title%]",
|
||||||
|
"description": "[%key:component::homeassistant_hardware::firmware_picker::options::step::start_otbr_addon::description%]"
|
||||||
|
},
|
||||||
|
"otbr_failed": {
|
||||||
|
"title": "[%key:component::homeassistant_hardware::firmware_picker::options::step::otbr_failed::title%]",
|
||||||
|
"description": "[%key:component::homeassistant_hardware::firmware_picker::options::step::otbr_failed::description%]"
|
||||||
|
},
|
||||||
|
"confirm_otbr": {
|
||||||
|
"title": "[%key:component::homeassistant_hardware::firmware_picker::options::step::confirm_otbr::title%]",
|
||||||
|
"description": "[%key:component::homeassistant_hardware::firmware_picker::options::step::confirm_otbr::description%]"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
@ -93,11 +134,19 @@
|
|||||||
"not_hassio": "[%key:component::homeassistant_hardware::silabs_multiprotocol_hardware::options::abort::not_hassio%]",
|
"not_hassio": "[%key:component::homeassistant_hardware::silabs_multiprotocol_hardware::options::abort::not_hassio%]",
|
||||||
"read_hw_settings_error": "Failed to read hardware settings",
|
"read_hw_settings_error": "Failed to read hardware settings",
|
||||||
"write_hw_settings_error": "Failed to write hardware settings",
|
"write_hw_settings_error": "Failed to write hardware settings",
|
||||||
"zha_migration_failed": "[%key:component::homeassistant_hardware::silabs_multiprotocol_hardware::options::abort::zha_migration_failed%]"
|
"zha_migration_failed": "[%key:component::homeassistant_hardware::silabs_multiprotocol_hardware::options::abort::zha_migration_failed%]",
|
||||||
|
"not_hassio_thread": "[%key:component::homeassistant_hardware::firmware_picker::options::abort::not_hassio_thread%]",
|
||||||
|
"otbr_addon_already_running": "[%key:component::homeassistant_hardware::firmware_picker::options::abort::otbr_addon_already_running%]",
|
||||||
|
"zha_still_using_stick": "[%key:component::homeassistant_hardware::firmware_picker::options::abort::zha_still_using_stick%]",
|
||||||
|
"otbr_still_using_stick": "[%key:component::homeassistant_hardware::firmware_picker::options::abort::otbr_still_using_stick%]"
|
||||||
},
|
},
|
||||||
"progress": {
|
"progress": {
|
||||||
"install_addon": "[%key:component::homeassistant_hardware::silabs_multiprotocol_hardware::options::progress::install_addon%]",
|
"install_addon": "[%key:component::homeassistant_hardware::silabs_multiprotocol_hardware::options::progress::install_addon%]",
|
||||||
"start_addon": "[%key:component::homeassistant_hardware::silabs_multiprotocol_hardware::options::progress::start_addon%]"
|
"start_addon": "[%key:component::homeassistant_hardware::silabs_multiprotocol_hardware::options::progress::start_addon%]",
|
||||||
|
"start_otbr_addon": "[%key:component::homeassistant_hardware::silabs_multiprotocol_hardware::options::progress::start_addon%]",
|
||||||
|
"install_zigbee_flasher_addon": "[%key:component::homeassistant_hardware::firmware_picker::options::progress::install_zigbee_flasher_addon%]",
|
||||||
|
"run_zigbee_flasher_addon": "[%key:component::homeassistant_hardware::firmware_picker::options::progress::run_zigbee_flasher_addon%]",
|
||||||
|
"uninstall_zigbee_flasher_addon": "[%key:component::homeassistant_hardware::firmware_picker::options::progress::uninstall_zigbee_flasher_addon%]"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,8 +6,17 @@ from unittest.mock import Mock, patch
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from homeassistant.components.hassio import DOMAIN as HASSIO_DOMAIN
|
from homeassistant.components.hassio import DOMAIN as HASSIO_DOMAIN
|
||||||
from homeassistant.components.homeassistant_yellow.const import DOMAIN
|
from homeassistant.components.hassio.addon_manager import AddonInfo, AddonState
|
||||||
from homeassistant.components.zha import DOMAIN as ZHA_DOMAIN
|
from homeassistant.components.homeassistant_hardware.firmware_config_flow import (
|
||||||
|
STEP_PICK_FIRMWARE_ZIGBEE,
|
||||||
|
)
|
||||||
|
from homeassistant.components.homeassistant_hardware.silabs_multiprotocol_addon import (
|
||||||
|
CONF_DISABLE_MULTI_PAN,
|
||||||
|
get_flasher_addon_manager,
|
||||||
|
get_multiprotocol_addon_manager,
|
||||||
|
)
|
||||||
|
from homeassistant.components.homeassistant_hardware.util import ApplicationType
|
||||||
|
from homeassistant.components.homeassistant_yellow.const import DOMAIN, RADIO_DEVICE
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.data_entry_flow import FlowResultType
|
from homeassistant.data_entry_flow import FlowResultType
|
||||||
from homeassistant.setup import async_setup_component
|
from homeassistant.setup import async_setup_component
|
||||||
@ -57,22 +66,28 @@ async def test_config_flow(hass: HomeAssistant) -> None:
|
|||||||
mock_integration(hass, MockModule("hassio"))
|
mock_integration(hass, MockModule("hassio"))
|
||||||
await async_setup_component(hass, HASSIO_DOMAIN, {})
|
await async_setup_component(hass, HASSIO_DOMAIN, {})
|
||||||
|
|
||||||
with patch(
|
with (
|
||||||
"homeassistant.components.homeassistant_yellow.async_setup_entry",
|
patch(
|
||||||
return_value=True,
|
"homeassistant.components.homeassistant_yellow.async_setup_entry",
|
||||||
) as mock_setup_entry:
|
return_value=True,
|
||||||
|
) as mock_setup_entry,
|
||||||
|
patch(
|
||||||
|
"homeassistant.components.homeassistant_hardware.firmware_config_flow.probe_silabs_firmware_type",
|
||||||
|
return_value=ApplicationType.EZSP,
|
||||||
|
),
|
||||||
|
):
|
||||||
result = await hass.config_entries.flow.async_init(
|
result = await hass.config_entries.flow.async_init(
|
||||||
DOMAIN, context={"source": "system"}
|
DOMAIN, context={"source": "system"}
|
||||||
)
|
)
|
||||||
|
|
||||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||||
assert result["title"] == "Home Assistant Yellow"
|
assert result["title"] == "Home Assistant Yellow"
|
||||||
assert result["data"] == {}
|
assert result["data"] == {"firmware": "ezsp"}
|
||||||
assert result["options"] == {}
|
assert result["options"] == {}
|
||||||
assert len(mock_setup_entry.mock_calls) == 1
|
assert len(mock_setup_entry.mock_calls) == 1
|
||||||
|
|
||||||
config_entry = hass.config_entries.async_entries(DOMAIN)[0]
|
config_entry = hass.config_entries.async_entries(DOMAIN)[0]
|
||||||
assert config_entry.data == {}
|
assert config_entry.data == {"firmware": "ezsp"}
|
||||||
assert config_entry.options == {}
|
assert config_entry.options == {}
|
||||||
assert config_entry.title == "Home Assistant Yellow"
|
assert config_entry.title == "Home Assistant Yellow"
|
||||||
|
|
||||||
@ -84,10 +99,12 @@ async def test_config_flow_single_entry(hass: HomeAssistant) -> None:
|
|||||||
|
|
||||||
# Setup the config entry
|
# Setup the config entry
|
||||||
config_entry = MockConfigEntry(
|
config_entry = MockConfigEntry(
|
||||||
data={},
|
data={"firmware": ApplicationType.EZSP},
|
||||||
domain=DOMAIN,
|
domain=DOMAIN,
|
||||||
options={},
|
options={},
|
||||||
title="Home Assistant Yellow",
|
title="Home Assistant Yellow",
|
||||||
|
version=1,
|
||||||
|
minor_version=2,
|
||||||
)
|
)
|
||||||
config_entry.add_to_hass(hass)
|
config_entry.add_to_hass(hass)
|
||||||
|
|
||||||
@ -104,165 +121,6 @@ async def test_config_flow_single_entry(hass: HomeAssistant) -> None:
|
|||||||
mock_setup_entry.assert_not_called()
|
mock_setup_entry.assert_not_called()
|
||||||
|
|
||||||
|
|
||||||
async def test_option_flow_install_multi_pan_addon(
|
|
||||||
hass: HomeAssistant,
|
|
||||||
addon_store_info,
|
|
||||||
addon_info,
|
|
||||||
install_addon,
|
|
||||||
set_addon_options,
|
|
||||||
start_addon,
|
|
||||||
) -> None:
|
|
||||||
"""Test installing the multi pan addon."""
|
|
||||||
mock_integration(hass, MockModule("hassio"))
|
|
||||||
await async_setup_component(hass, HASSIO_DOMAIN, {})
|
|
||||||
|
|
||||||
# Setup the config entry
|
|
||||||
config_entry = MockConfigEntry(
|
|
||||||
data={},
|
|
||||||
domain=DOMAIN,
|
|
||||||
options={},
|
|
||||||
title="Home Assistant Yellow",
|
|
||||||
)
|
|
||||||
config_entry.add_to_hass(hass)
|
|
||||||
|
|
||||||
result = await hass.config_entries.options.async_init(config_entry.entry_id)
|
|
||||||
assert result["type"] is FlowResultType.MENU
|
|
||||||
|
|
||||||
with patch(
|
|
||||||
"homeassistant.components.homeassistant_hardware.silabs_multiprotocol_addon.is_hassio",
|
|
||||||
side_effect=Mock(return_value=True),
|
|
||||||
):
|
|
||||||
result = await hass.config_entries.options.async_configure(
|
|
||||||
result["flow_id"],
|
|
||||||
{"next_step_id": "multipan_settings"},
|
|
||||||
)
|
|
||||||
assert result["type"] is FlowResultType.FORM
|
|
||||||
assert result["step_id"] == "addon_not_installed"
|
|
||||||
|
|
||||||
result = await hass.config_entries.options.async_configure(
|
|
||||||
result["flow_id"],
|
|
||||||
user_input={
|
|
||||||
"enable_multi_pan": True,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
assert result["type"] is FlowResultType.SHOW_PROGRESS
|
|
||||||
assert result["step_id"] == "install_addon"
|
|
||||||
assert result["progress_action"] == "install_addon"
|
|
||||||
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
install_addon.assert_called_once_with(hass, "core_silabs_multiprotocol")
|
|
||||||
|
|
||||||
result = await hass.config_entries.options.async_configure(result["flow_id"])
|
|
||||||
assert result["type"] is FlowResultType.SHOW_PROGRESS
|
|
||||||
assert result["step_id"] == "start_addon"
|
|
||||||
set_addon_options.assert_called_once_with(
|
|
||||||
hass,
|
|
||||||
"core_silabs_multiprotocol",
|
|
||||||
{
|
|
||||||
"options": {
|
|
||||||
"autoflash_firmware": True,
|
|
||||||
"device": "/dev/ttyAMA1",
|
|
||||||
"baudrate": "115200",
|
|
||||||
"flow_control": True,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
start_addon.assert_called_once_with(hass, "core_silabs_multiprotocol")
|
|
||||||
|
|
||||||
result = await hass.config_entries.options.async_configure(result["flow_id"])
|
|
||||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
|
||||||
|
|
||||||
|
|
||||||
async def test_option_flow_install_multi_pan_addon_zha(
|
|
||||||
hass: HomeAssistant,
|
|
||||||
addon_store_info,
|
|
||||||
addon_info,
|
|
||||||
install_addon,
|
|
||||||
set_addon_options,
|
|
||||||
start_addon,
|
|
||||||
) -> None:
|
|
||||||
"""Test installing the multi pan addon when a zha config entry exists."""
|
|
||||||
mock_integration(hass, MockModule("hassio"))
|
|
||||||
await async_setup_component(hass, HASSIO_DOMAIN, {})
|
|
||||||
|
|
||||||
# Setup the config entry
|
|
||||||
config_entry = MockConfigEntry(
|
|
||||||
data={},
|
|
||||||
domain=DOMAIN,
|
|
||||||
options={},
|
|
||||||
title="Home Assistant Yellow",
|
|
||||||
)
|
|
||||||
config_entry.add_to_hass(hass)
|
|
||||||
|
|
||||||
zha_config_entry = MockConfigEntry(
|
|
||||||
data={"device": {"path": "/dev/ttyAMA1"}, "radio_type": "ezsp"},
|
|
||||||
domain=ZHA_DOMAIN,
|
|
||||||
options={},
|
|
||||||
title="Yellow",
|
|
||||||
)
|
|
||||||
zha_config_entry.add_to_hass(hass)
|
|
||||||
|
|
||||||
result = await hass.config_entries.options.async_init(config_entry.entry_id)
|
|
||||||
assert result["type"] is FlowResultType.MENU
|
|
||||||
|
|
||||||
with patch(
|
|
||||||
"homeassistant.components.homeassistant_hardware.silabs_multiprotocol_addon.is_hassio",
|
|
||||||
side_effect=Mock(return_value=True),
|
|
||||||
):
|
|
||||||
result = await hass.config_entries.options.async_configure(
|
|
||||||
result["flow_id"],
|
|
||||||
{"next_step_id": "multipan_settings"},
|
|
||||||
)
|
|
||||||
assert result["type"] is FlowResultType.FORM
|
|
||||||
assert result["step_id"] == "addon_not_installed"
|
|
||||||
|
|
||||||
result = await hass.config_entries.options.async_configure(
|
|
||||||
result["flow_id"],
|
|
||||||
user_input={
|
|
||||||
"enable_multi_pan": True,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
assert result["type"] is FlowResultType.SHOW_PROGRESS
|
|
||||||
assert result["step_id"] == "install_addon"
|
|
||||||
assert result["progress_action"] == "install_addon"
|
|
||||||
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
install_addon.assert_called_once_with(hass, "core_silabs_multiprotocol")
|
|
||||||
|
|
||||||
result = await hass.config_entries.options.async_configure(result["flow_id"])
|
|
||||||
assert result["type"] is FlowResultType.SHOW_PROGRESS
|
|
||||||
assert result["step_id"] == "start_addon"
|
|
||||||
set_addon_options.assert_called_once_with(
|
|
||||||
hass,
|
|
||||||
"core_silabs_multiprotocol",
|
|
||||||
{
|
|
||||||
"options": {
|
|
||||||
"autoflash_firmware": True,
|
|
||||||
"device": "/dev/ttyAMA1",
|
|
||||||
"baudrate": "115200",
|
|
||||||
"flow_control": True,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
# Check the ZHA config entry data is updated
|
|
||||||
assert zha_config_entry.data == {
|
|
||||||
"device": {
|
|
||||||
"path": "socket://core-silabs-multiprotocol:9999",
|
|
||||||
"baudrate": 115200,
|
|
||||||
"flow_control": None,
|
|
||||||
},
|
|
||||||
"radio_type": "ezsp",
|
|
||||||
}
|
|
||||||
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
start_addon.assert_called_once_with(hass, "core_silabs_multiprotocol")
|
|
||||||
|
|
||||||
result = await hass.config_entries.options.async_configure(result["flow_id"])
|
|
||||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
("reboot_menu_choice", "reboot_calls"),
|
("reboot_menu_choice", "reboot_calls"),
|
||||||
[("reboot_now", 1), ("reboot_later", 0)],
|
[("reboot_now", 1), ("reboot_later", 0)],
|
||||||
@ -281,10 +139,12 @@ async def test_option_flow_led_settings(
|
|||||||
|
|
||||||
# Setup the config entry
|
# Setup the config entry
|
||||||
config_entry = MockConfigEntry(
|
config_entry = MockConfigEntry(
|
||||||
data={},
|
data={"firmware": ApplicationType.EZSP},
|
||||||
domain=DOMAIN,
|
domain=DOMAIN,
|
||||||
options={},
|
options={},
|
||||||
title="Home Assistant Yellow",
|
title="Home Assistant Yellow",
|
||||||
|
version=1,
|
||||||
|
minor_version=2,
|
||||||
)
|
)
|
||||||
config_entry.add_to_hass(hass)
|
config_entry.add_to_hass(hass)
|
||||||
|
|
||||||
@ -327,10 +187,12 @@ async def test_option_flow_led_settings_unchanged(
|
|||||||
|
|
||||||
# Setup the config entry
|
# Setup the config entry
|
||||||
config_entry = MockConfigEntry(
|
config_entry = MockConfigEntry(
|
||||||
data={},
|
data={"firmware": ApplicationType.EZSP},
|
||||||
domain=DOMAIN,
|
domain=DOMAIN,
|
||||||
options={},
|
options={},
|
||||||
title="Home Assistant Yellow",
|
title="Home Assistant Yellow",
|
||||||
|
version=1,
|
||||||
|
minor_version=2,
|
||||||
)
|
)
|
||||||
config_entry.add_to_hass(hass)
|
config_entry.add_to_hass(hass)
|
||||||
|
|
||||||
@ -359,10 +221,12 @@ async def test_option_flow_led_settings_fail_1(hass: HomeAssistant) -> None:
|
|||||||
|
|
||||||
# Setup the config entry
|
# Setup the config entry
|
||||||
config_entry = MockConfigEntry(
|
config_entry = MockConfigEntry(
|
||||||
data={},
|
data={"firmware": ApplicationType.EZSP},
|
||||||
domain=DOMAIN,
|
domain=DOMAIN,
|
||||||
options={},
|
options={},
|
||||||
title="Home Assistant Yellow",
|
title="Home Assistant Yellow",
|
||||||
|
version=1,
|
||||||
|
minor_version=2,
|
||||||
)
|
)
|
||||||
config_entry.add_to_hass(hass)
|
config_entry.add_to_hass(hass)
|
||||||
|
|
||||||
@ -391,10 +255,12 @@ async def test_option_flow_led_settings_fail_2(
|
|||||||
|
|
||||||
# Setup the config entry
|
# Setup the config entry
|
||||||
config_entry = MockConfigEntry(
|
config_entry = MockConfigEntry(
|
||||||
data={},
|
data={"firmware": ApplicationType.EZSP},
|
||||||
domain=DOMAIN,
|
domain=DOMAIN,
|
||||||
options={},
|
options={},
|
||||||
title="Home Assistant Yellow",
|
title="Home Assistant Yellow",
|
||||||
|
version=1,
|
||||||
|
minor_version=2,
|
||||||
)
|
)
|
||||||
config_entry.add_to_hass(hass)
|
config_entry.add_to_hass(hass)
|
||||||
|
|
||||||
@ -418,3 +284,139 @@ async def test_option_flow_led_settings_fail_2(
|
|||||||
)
|
)
|
||||||
assert result["type"] is FlowResultType.ABORT
|
assert result["type"] is FlowResultType.ABORT
|
||||||
assert result["reason"] == "write_hw_settings_error"
|
assert result["reason"] == "write_hw_settings_error"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_firmware_options_flow(hass: HomeAssistant) -> None:
|
||||||
|
"""Test the firmware options flow for Yellow."""
|
||||||
|
mock_integration(hass, MockModule("hassio"))
|
||||||
|
await async_setup_component(hass, HASSIO_DOMAIN, {})
|
||||||
|
|
||||||
|
config_entry = MockConfigEntry(
|
||||||
|
data={"firmware": ApplicationType.SPINEL},
|
||||||
|
domain=DOMAIN,
|
||||||
|
options={},
|
||||||
|
title="Home Assistant Yellow",
|
||||||
|
version=1,
|
||||||
|
minor_version=2,
|
||||||
|
)
|
||||||
|
config_entry.add_to_hass(hass)
|
||||||
|
|
||||||
|
# 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"] == "main_menu"
|
||||||
|
assert "firmware_settings" in result["menu_options"]
|
||||||
|
|
||||||
|
# Pick firmware settings
|
||||||
|
result = await hass.config_entries.options.async_configure(
|
||||||
|
result["flow_id"],
|
||||||
|
user_input={"next_step_id": "firmware_settings"},
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["step_id"] == "pick_firmware"
|
||||||
|
assert result["description_placeholders"]["firmware_type"] == "spinel"
|
||||||
|
assert result["description_placeholders"]["model"] == "Home Assistant Yellow"
|
||||||
|
|
||||||
|
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,
|
||||||
|
):
|
||||||
|
result = await hass.config_entries.options.async_configure(
|
||||||
|
result["flow_id"],
|
||||||
|
user_input={"next_step_id": STEP_PICK_FIRMWARE_ZIGBEE},
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||||
|
assert result["result"] is True
|
||||||
|
|
||||||
|
assert config_entry.data == {
|
||||||
|
"firmware": "ezsp",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async def test_options_flow_multipan_uninstall(hass: HomeAssistant) -> None:
|
||||||
|
"""Test options flow for when multi-PAN firmware is installed."""
|
||||||
|
mock_integration(hass, MockModule("hassio"))
|
||||||
|
await async_setup_component(hass, HASSIO_DOMAIN, {})
|
||||||
|
|
||||||
|
config_entry = MockConfigEntry(
|
||||||
|
data={"firmware": ApplicationType.CPC},
|
||||||
|
domain=DOMAIN,
|
||||||
|
options={},
|
||||||
|
title="Home Assistant Yellow",
|
||||||
|
version=1,
|
||||||
|
minor_version=2,
|
||||||
|
)
|
||||||
|
config_entry.add_to_hass(hass)
|
||||||
|
|
||||||
|
# Multi-PAN addon is running
|
||||||
|
mock_multipan_manager = Mock(spec_set=await get_multiprotocol_addon_manager(hass))
|
||||||
|
mock_multipan_manager.async_get_addon_info.return_value = AddonInfo(
|
||||||
|
available=True,
|
||||||
|
hostname=None,
|
||||||
|
options={"device": RADIO_DEVICE},
|
||||||
|
state=AddonState.RUNNING,
|
||||||
|
update_available=False,
|
||||||
|
version="1.0.0",
|
||||||
|
)
|
||||||
|
|
||||||
|
mock_flasher_manager = Mock(spec_set=get_flasher_addon_manager(hass))
|
||||||
|
mock_flasher_manager.async_get_addon_info.return_value = AddonInfo(
|
||||||
|
available=True,
|
||||||
|
hostname=None,
|
||||||
|
options={},
|
||||||
|
state=AddonState.NOT_RUNNING,
|
||||||
|
update_available=False,
|
||||||
|
version="1.0.0",
|
||||||
|
)
|
||||||
|
|
||||||
|
with (
|
||||||
|
patch(
|
||||||
|
"homeassistant.components.homeassistant_hardware.silabs_multiprotocol_addon.get_multiprotocol_addon_manager",
|
||||||
|
return_value=mock_multipan_manager,
|
||||||
|
),
|
||||||
|
patch(
|
||||||
|
"homeassistant.components.homeassistant_hardware.silabs_multiprotocol_addon.get_flasher_addon_manager",
|
||||||
|
return_value=mock_flasher_manager,
|
||||||
|
),
|
||||||
|
patch(
|
||||||
|
"homeassistant.components.homeassistant_hardware.silabs_multiprotocol_addon.is_hassio",
|
||||||
|
return_value=True,
|
||||||
|
),
|
||||||
|
):
|
||||||
|
result = await hass.config_entries.options.async_init(config_entry.entry_id)
|
||||||
|
assert result["type"] is FlowResultType.MENU
|
||||||
|
assert result["step_id"] == "main_menu"
|
||||||
|
assert "multipan_settings" in result["menu_options"]
|
||||||
|
|
||||||
|
# Pick multi-PAN settings
|
||||||
|
result = await hass.config_entries.options.async_configure(
|
||||||
|
result["flow_id"],
|
||||||
|
user_input={"next_step_id": "multipan_settings"},
|
||||||
|
)
|
||||||
|
|
||||||
|
# Pick the uninstall option
|
||||||
|
result = await hass.config_entries.options.async_configure(
|
||||||
|
result["flow_id"],
|
||||||
|
user_input={"next_step_id": "uninstall_addon"},
|
||||||
|
)
|
||||||
|
|
||||||
|
# Check the box
|
||||||
|
result = await hass.config_entries.options.async_configure(
|
||||||
|
result["flow_id"], user_input={CONF_DISABLE_MULTI_PAN: True}
|
||||||
|
)
|
||||||
|
|
||||||
|
# Finish the flow
|
||||||
|
result = await hass.config_entries.options.async_configure(result["flow_id"])
|
||||||
|
await hass.async_block_till_done(wait_background_tasks=True)
|
||||||
|
result = await hass.config_entries.options.async_configure(result["flow_id"])
|
||||||
|
await hass.async_block_till_done(wait_background_tasks=True)
|
||||||
|
result = await hass.config_entries.options.async_configure(result["flow_id"])
|
||||||
|
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||||
|
|
||||||
|
# We've reverted the firmware back to Zigbee
|
||||||
|
assert config_entry.data["firmware"] == "ezsp"
|
||||||
|
@ -6,10 +6,14 @@ import pytest
|
|||||||
|
|
||||||
from homeassistant.components import zha
|
from homeassistant.components import zha
|
||||||
from homeassistant.components.hassio import DOMAIN as HASSIO_DOMAIN
|
from homeassistant.components.hassio import DOMAIN as HASSIO_DOMAIN
|
||||||
from homeassistant.components.hassio.handler import HassioAPIError
|
from homeassistant.components.homeassistant_hardware.util import (
|
||||||
|
ApplicationType,
|
||||||
|
FirmwareGuess,
|
||||||
|
)
|
||||||
from homeassistant.components.homeassistant_yellow.const import DOMAIN
|
from homeassistant.components.homeassistant_yellow.const import DOMAIN
|
||||||
from homeassistant.config_entries import ConfigEntryState
|
from homeassistant.config_entries import ConfigEntryState
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.exceptions import HomeAssistantError
|
||||||
from homeassistant.setup import async_setup_component
|
from homeassistant.setup import async_setup_component
|
||||||
|
|
||||||
from tests.common import MockConfigEntry, MockModule, mock_integration
|
from tests.common import MockConfigEntry, MockModule, mock_integration
|
||||||
@ -27,10 +31,12 @@ async def test_setup_entry(
|
|||||||
|
|
||||||
# Setup the config entry
|
# Setup the config entry
|
||||||
config_entry = MockConfigEntry(
|
config_entry = MockConfigEntry(
|
||||||
data={},
|
data={"firmware": ApplicationType.EZSP},
|
||||||
domain=DOMAIN,
|
domain=DOMAIN,
|
||||||
options={},
|
options={},
|
||||||
title="Home Assistant Yellow",
|
title="Home Assistant Yellow",
|
||||||
|
version=1,
|
||||||
|
minor_version=2,
|
||||||
)
|
)
|
||||||
config_entry.add_to_hass(hass)
|
config_entry.add_to_hass(hass)
|
||||||
with (
|
with (
|
||||||
@ -42,6 +48,14 @@ async def test_setup_entry(
|
|||||||
"homeassistant.components.onboarding.async_is_onboarded",
|
"homeassistant.components.onboarding.async_is_onboarded",
|
||||||
return_value=onboarded,
|
return_value=onboarded,
|
||||||
),
|
),
|
||||||
|
patch(
|
||||||
|
"homeassistant.components.homeassistant_yellow.guess_firmware_type",
|
||||||
|
return_value=FirmwareGuess( # Nothing is setup
|
||||||
|
is_running=False,
|
||||||
|
firmware_type=ApplicationType.EZSP,
|
||||||
|
source="unknown",
|
||||||
|
),
|
||||||
|
),
|
||||||
):
|
):
|
||||||
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
||||||
await hass.async_block_till_done(wait_background_tasks=True)
|
await hass.async_block_till_done(wait_background_tasks=True)
|
||||||
@ -74,118 +88,12 @@ async def test_setup_zha(hass: HomeAssistant, addon_store_info) -> None:
|
|||||||
|
|
||||||
# Setup the config entry
|
# Setup the config entry
|
||||||
config_entry = MockConfigEntry(
|
config_entry = MockConfigEntry(
|
||||||
data={},
|
data={"firmware": ApplicationType.EZSP},
|
||||||
domain=DOMAIN,
|
|
||||||
options={},
|
|
||||||
title="Home Assistant Yellow",
|
|
||||||
)
|
|
||||||
config_entry.add_to_hass(hass)
|
|
||||||
with (
|
|
||||||
patch(
|
|
||||||
"homeassistant.components.homeassistant_yellow.get_os_info",
|
|
||||||
return_value={"board": "yellow"},
|
|
||||||
) as mock_get_os_info,
|
|
||||||
patch(
|
|
||||||
"homeassistant.components.onboarding.async_is_onboarded", return_value=False
|
|
||||||
),
|
|
||||||
):
|
|
||||||
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
|
||||||
await hass.async_block_till_done(wait_background_tasks=True)
|
|
||||||
assert len(mock_get_os_info.mock_calls) == 1
|
|
||||||
|
|
||||||
# Finish setting up ZHA
|
|
||||||
zha_flows = hass.config_entries.flow.async_progress_by_handler("zha")
|
|
||||||
assert len(zha_flows) == 1
|
|
||||||
assert zha_flows[0]["step_id"] == "choose_formation_strategy"
|
|
||||||
|
|
||||||
await hass.config_entries.flow.async_configure(
|
|
||||||
zha_flows[0]["flow_id"],
|
|
||||||
user_input={"next_step_id": zha.config_flow.FORMATION_REUSE_SETTINGS},
|
|
||||||
)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
config_entry = hass.config_entries.async_entries("zha")[0]
|
|
||||||
assert config_entry.data == {
|
|
||||||
"device": {
|
|
||||||
"baudrate": 115200,
|
|
||||||
"flow_control": "hardware",
|
|
||||||
"path": "/dev/ttyAMA1",
|
|
||||||
},
|
|
||||||
"radio_type": "ezsp",
|
|
||||||
}
|
|
||||||
assert config_entry.options == {}
|
|
||||||
assert config_entry.title == "Yellow"
|
|
||||||
|
|
||||||
|
|
||||||
async def test_setup_zha_multipan(
|
|
||||||
hass: HomeAssistant, addon_info, addon_running
|
|
||||||
) -> None:
|
|
||||||
"""Test zha gets the right config."""
|
|
||||||
mock_integration(hass, MockModule("hassio"))
|
|
||||||
await async_setup_component(hass, HASSIO_DOMAIN, {})
|
|
||||||
|
|
||||||
addon_info.return_value["options"]["device"] = "/dev/ttyAMA1"
|
|
||||||
|
|
||||||
# Setup the config entry
|
|
||||||
config_entry = MockConfigEntry(
|
|
||||||
data={},
|
|
||||||
domain=DOMAIN,
|
|
||||||
options={},
|
|
||||||
title="Home Assistant Yellow",
|
|
||||||
)
|
|
||||||
config_entry.add_to_hass(hass)
|
|
||||||
with (
|
|
||||||
patch(
|
|
||||||
"homeassistant.components.homeassistant_yellow.get_os_info",
|
|
||||||
return_value={"board": "yellow"},
|
|
||||||
) as mock_get_os_info,
|
|
||||||
patch(
|
|
||||||
"homeassistant.components.onboarding.async_is_onboarded", return_value=False
|
|
||||||
),
|
|
||||||
):
|
|
||||||
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
|
||||||
await hass.async_block_till_done(wait_background_tasks=True)
|
|
||||||
assert len(mock_get_os_info.mock_calls) == 1
|
|
||||||
|
|
||||||
# Finish setting up ZHA
|
|
||||||
zha_flows = hass.config_entries.flow.async_progress_by_handler("zha")
|
|
||||||
assert len(zha_flows) == 1
|
|
||||||
assert zha_flows[0]["step_id"] == "choose_formation_strategy"
|
|
||||||
|
|
||||||
await hass.config_entries.flow.async_configure(
|
|
||||||
zha_flows[0]["flow_id"],
|
|
||||||
user_input={"next_step_id": zha.config_flow.FORMATION_REUSE_SETTINGS},
|
|
||||||
)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
config_entry = hass.config_entries.async_entries("zha")[0]
|
|
||||||
assert config_entry.data == {
|
|
||||||
"device": {
|
|
||||||
"baudrate": 115200,
|
|
||||||
"flow_control": None,
|
|
||||||
"path": "socket://core-silabs-multiprotocol:9999",
|
|
||||||
},
|
|
||||||
"radio_type": "ezsp",
|
|
||||||
}
|
|
||||||
assert config_entry.options == {}
|
|
||||||
assert config_entry.title == "Yellow Multiprotocol"
|
|
||||||
|
|
||||||
|
|
||||||
async def test_setup_zha_multipan_other_device(
|
|
||||||
hass: HomeAssistant, addon_info, addon_running
|
|
||||||
) -> None:
|
|
||||||
"""Test zha gets the right config."""
|
|
||||||
mock_integration(hass, MockModule("hassio"))
|
|
||||||
await async_setup_component(hass, HASSIO_DOMAIN, {})
|
|
||||||
|
|
||||||
addon_info.return_value["options"]["device"] = "/dev/not_yellow_radio"
|
|
||||||
|
|
||||||
# Setup the config entry
|
|
||||||
config_entry = MockConfigEntry(
|
|
||||||
data={},
|
|
||||||
domain=DOMAIN,
|
domain=DOMAIN,
|
||||||
options={},
|
options={},
|
||||||
title="Home Assistant Yellow",
|
title="Home Assistant Yellow",
|
||||||
|
version=1,
|
||||||
|
minor_version=2,
|
||||||
)
|
)
|
||||||
config_entry.add_to_hass(hass)
|
config_entry.add_to_hass(hass)
|
||||||
with (
|
with (
|
||||||
@ -229,10 +137,12 @@ async def test_setup_entry_no_hassio(hass: HomeAssistant) -> None:
|
|||||||
"""Test setup of a config entry without hassio."""
|
"""Test setup of a config entry without hassio."""
|
||||||
# Setup the config entry
|
# Setup the config entry
|
||||||
config_entry = MockConfigEntry(
|
config_entry = MockConfigEntry(
|
||||||
data={},
|
data={"firmware": ApplicationType.EZSP},
|
||||||
domain=DOMAIN,
|
domain=DOMAIN,
|
||||||
options={},
|
options={},
|
||||||
title="Home Assistant Yellow",
|
title="Home Assistant Yellow",
|
||||||
|
version=1,
|
||||||
|
minor_version=2,
|
||||||
)
|
)
|
||||||
config_entry.add_to_hass(hass)
|
config_entry.add_to_hass(hass)
|
||||||
assert len(hass.config_entries.async_entries()) == 1
|
assert len(hass.config_entries.async_entries()) == 1
|
||||||
@ -254,10 +164,12 @@ async def test_setup_entry_wrong_board(hass: HomeAssistant) -> None:
|
|||||||
|
|
||||||
# Setup the config entry
|
# Setup the config entry
|
||||||
config_entry = MockConfigEntry(
|
config_entry = MockConfigEntry(
|
||||||
data={},
|
data={"firmware": ApplicationType.EZSP},
|
||||||
domain=DOMAIN,
|
domain=DOMAIN,
|
||||||
options={},
|
options={},
|
||||||
title="Home Assistant Yellow",
|
title="Home Assistant Yellow",
|
||||||
|
version=1,
|
||||||
|
minor_version=2,
|
||||||
)
|
)
|
||||||
config_entry.add_to_hass(hass)
|
config_entry.add_to_hass(hass)
|
||||||
assert len(hass.config_entries.async_entries()) == 1
|
assert len(hass.config_entries.async_entries()) == 1
|
||||||
@ -280,10 +192,12 @@ async def test_setup_entry_wait_hassio(hass: HomeAssistant) -> None:
|
|||||||
|
|
||||||
# Setup the config entry
|
# Setup the config entry
|
||||||
config_entry = MockConfigEntry(
|
config_entry = MockConfigEntry(
|
||||||
data={},
|
data={"firmware": ApplicationType.EZSP},
|
||||||
domain=DOMAIN,
|
domain=DOMAIN,
|
||||||
options={},
|
options={},
|
||||||
title="Home Assistant Yellow",
|
title="Home Assistant Yellow",
|
||||||
|
version=1,
|
||||||
|
minor_version=2,
|
||||||
)
|
)
|
||||||
config_entry.add_to_hass(hass)
|
config_entry.add_to_hass(hass)
|
||||||
with patch(
|
with patch(
|
||||||
@ -303,14 +217,15 @@ async def test_setup_entry_addon_info_fails(
|
|||||||
"""Test setup of a config entry when fetching addon info fails."""
|
"""Test setup of a config entry when fetching addon info fails."""
|
||||||
mock_integration(hass, MockModule("hassio"))
|
mock_integration(hass, MockModule("hassio"))
|
||||||
await async_setup_component(hass, HASSIO_DOMAIN, {})
|
await async_setup_component(hass, HASSIO_DOMAIN, {})
|
||||||
addon_store_info.side_effect = HassioAPIError("Boom")
|
|
||||||
|
|
||||||
# Setup the config entry
|
# Setup the config entry
|
||||||
config_entry = MockConfigEntry(
|
config_entry = MockConfigEntry(
|
||||||
data={},
|
data={"firmware": ApplicationType.CPC},
|
||||||
domain=DOMAIN,
|
domain=DOMAIN,
|
||||||
options={},
|
options={},
|
||||||
title="Home Assistant Yellow",
|
title="Home Assistant Yellow",
|
||||||
|
version=1,
|
||||||
|
minor_version=2,
|
||||||
)
|
)
|
||||||
config_entry.add_to_hass(hass)
|
config_entry.add_to_hass(hass)
|
||||||
with (
|
with (
|
||||||
@ -319,41 +234,15 @@ async def test_setup_entry_addon_info_fails(
|
|||||||
return_value={"board": "yellow"},
|
return_value={"board": "yellow"},
|
||||||
),
|
),
|
||||||
patch(
|
patch(
|
||||||
"homeassistant.components.onboarding.async_is_onboarded", return_value=False
|
"homeassistant.components.onboarding.async_is_onboarded",
|
||||||
|
return_value=False,
|
||||||
|
),
|
||||||
|
patch(
|
||||||
|
"homeassistant.components.homeassistant_yellow.check_multi_pan_addon",
|
||||||
|
side_effect=HomeAssistantError("Boom"),
|
||||||
),
|
),
|
||||||
):
|
):
|
||||||
assert not await hass.config_entries.async_setup(config_entry.entry_id)
|
assert not await hass.config_entries.async_setup(config_entry.entry_id)
|
||||||
|
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert config_entry.state is ConfigEntryState.SETUP_RETRY
|
assert config_entry.state is ConfigEntryState.SETUP_RETRY
|
||||||
|
|
||||||
|
|
||||||
async def test_setup_entry_addon_not_running(
|
|
||||||
hass: HomeAssistant, addon_installed, start_addon
|
|
||||||
) -> None:
|
|
||||||
"""Test the addon is started if it is not running."""
|
|
||||||
mock_integration(hass, MockModule("hassio"))
|
|
||||||
await async_setup_component(hass, HASSIO_DOMAIN, {})
|
|
||||||
|
|
||||||
# Setup the config entry
|
|
||||||
config_entry = MockConfigEntry(
|
|
||||||
data={},
|
|
||||||
domain=DOMAIN,
|
|
||||||
options={},
|
|
||||||
title="Home Assistant Yellow",
|
|
||||||
)
|
|
||||||
config_entry.add_to_hass(hass)
|
|
||||||
with (
|
|
||||||
patch(
|
|
||||||
"homeassistant.components.homeassistant_yellow.get_os_info",
|
|
||||||
return_value={"board": "yellow"},
|
|
||||||
),
|
|
||||||
patch(
|
|
||||||
"homeassistant.components.onboarding.async_is_onboarded", return_value=False
|
|
||||||
),
|
|
||||||
):
|
|
||||||
assert not await hass.config_entries.async_setup(config_entry.entry_id)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
assert config_entry.state is ConfigEntryState.SETUP_RETRY
|
|
||||||
start_addon.assert_called_once()
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user