From 2bbebeb92516371601c08f26ba83c6bb6cce07ff Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Wed, 26 Apr 2023 15:40:46 -0400 Subject: [PATCH] Add a warning step to ZHA's config flow to advise against some radios (#92078) --- homeassistant/components/zha/config_flow.py | 32 ++++++++++++-- homeassistant/components/zha/radio_manager.py | 6 +++ homeassistant/components/zha/strings.json | 8 ++++ tests/components/zha/test_config_flow.py | 42 ++++++++++++------- 4 files changed, 69 insertions(+), 19 deletions(-) diff --git a/homeassistant/components/zha/config_flow.py b/homeassistant/components/zha/config_flow.py index 53c4e338810..5230d77ce46 100644 --- a/homeassistant/components/zha/config_flow.py +++ b/homeassistant/components/zha/config_flow.py @@ -32,7 +32,11 @@ from .core.const import ( DOMAIN, RadioType, ) -from .radio_manager import HARDWARE_DISCOVERY_SCHEMA, ZhaRadioManager +from .radio_manager import ( + HARDWARE_DISCOVERY_SCHEMA, + RECOMMENDED_RADIOS, + ZhaRadioManager, +) CONF_MANUAL_PATH = "Enter Manually" SUPPORTED_PORT_SETTINGS = ( @@ -192,7 +196,7 @@ class BaseZhaFlow(FlowHandler): else "" ) - return await self.async_step_choose_formation_strategy() + return await self.async_step_verify_radio() # Pre-select the currently configured port default_port = vol.UNDEFINED @@ -252,7 +256,7 @@ class BaseZhaFlow(FlowHandler): self._radio_mgr.device_settings = user_input.copy() if await self._radio_mgr.radio_type.controller.probe(user_input): - return await self.async_step_choose_formation_strategy() + return await self.async_step_verify_radio() errors["base"] = "cannot_connect" @@ -289,6 +293,26 @@ class BaseZhaFlow(FlowHandler): errors=errors, ) + async def async_step_verify_radio( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Add a warning step to dissuade the use of deprecated radios.""" + assert self._radio_mgr.radio_type is not None + + # Skip this step if we are using a recommended radio + if user_input is not None or self._radio_mgr.radio_type in RECOMMENDED_RADIOS: + return await self.async_step_choose_formation_strategy() + + return self.async_show_form( + step_id="verify_radio", + description_placeholders={ + CONF_NAME: self._radio_mgr.radio_type.description, + "docs_recommended_adapters_url": ( + "https://www.home-assistant.io/integrations/zha/#recommended-zigbee-radio-adapters-and-modules" + ), + }, + ) + async def async_step_choose_formation_strategy( self, user_input: dict[str, Any] | None = None ) -> FlowResult: @@ -516,7 +540,7 @@ class ZhaConfigFlowHandler(BaseZhaFlow, config_entries.ConfigFlow, domain=DOMAIN if self._radio_mgr.device_settings is None: return await self.async_step_manual_port_config() - return await self.async_step_choose_formation_strategy() + return await self.async_step_verify_radio() return self.async_show_form( step_id="confirm", diff --git a/homeassistant/components/zha/radio_manager.py b/homeassistant/components/zha/radio_manager.py index 1f02c94e61f..9fbfa03b928 100644 --- a/homeassistant/components/zha/radio_manager.py +++ b/homeassistant/components/zha/radio_manager.py @@ -40,6 +40,12 @@ AUTOPROBE_RADIOS = ( RadioType.zigate, ) +RECOMMENDED_RADIOS = ( + RadioType.ezsp, + RadioType.znp, + RadioType.deconz, +) + CONNECT_DELAY_S = 1.0 MIGRATION_RETRIES = 100 diff --git a/homeassistant/components/zha/strings.json b/homeassistant/components/zha/strings.json index 132f6ed9d95..94b3951f014 100644 --- a/homeassistant/components/zha/strings.json +++ b/homeassistant/components/zha/strings.json @@ -27,6 +27,10 @@ "flow_control": "data flow control" } }, + "verify_radio": { + "title": "Radio is not recommended", + "description": "The radio you are using ({name}) is not recommended and support for it may be removed in the future. Please see the Zigbee Home Automation integration's documentation for [a list of recommended adapters]({docs_recommended_adapters_url})." + }, "choose_formation_strategy": { "title": "Network Formation", "description": "Choose the network settings for your radio.", @@ -116,6 +120,10 @@ "flow_control": "[%key:component::zha::config::step::manual_port_config::data::flow_control%]" } }, + "verify_radio": { + "title": "[%key:component::zha::config::step::verify_radio::title%]", + "description": "[%key:component::zha::config::step::verify_radio::description%]" + }, "choose_formation_strategy": { "title": "[%key:component::zha::config::step::choose_formation_strategy::title%]", "description": "[%key:component::zha::config::step::choose_formation_strategy::description%]", diff --git a/tests/components/zha/test_config_flow.py b/tests/components/zha/test_config_flow.py index d9556451996..7c0d3eac2a9 100644 --- a/tests/components/zha/test_config_flow.py +++ b/tests/components/zha/test_config_flow.py @@ -191,23 +191,30 @@ async def test_zigate_via_zeroconf(setup_entry_mock, hass: HomeAssistant) -> Non ) assert result1["step_id"] == "manual_port_config" - # Confirm port settings + # Confirm the radio is deprecated result2 = await hass.config_entries.flow.async_configure( + flow["flow_id"], user_input={} + ) + assert result2["step_id"] == "verify_radio" + assert "ZiGate" in result2["description_placeholders"]["name"] + + # Confirm port settings + result3 = await hass.config_entries.flow.async_configure( result1["flow_id"], user_input={} ) - assert result2["type"] == FlowResultType.MENU - assert result2["step_id"] == "choose_formation_strategy" + assert result3["type"] == FlowResultType.MENU + assert result3["step_id"] == "choose_formation_strategy" - result3 = await hass.config_entries.flow.async_configure( - result2["flow_id"], + result4 = await hass.config_entries.flow.async_configure( + result3["flow_id"], user_input={"next_step_id": config_flow.FORMATION_REUSE_SETTINGS}, ) await hass.async_block_till_done() - assert result3["type"] == FlowResultType.CREATE_ENTRY - assert result3["title"] == "socket://192.168.1.200:1234" - assert result3["data"] == { + assert result4["type"] == FlowResultType.CREATE_ENTRY + assert result4["title"] == "socket://192.168.1.200:1234" + assert result4["data"] == { CONF_DEVICE: { CONF_DEVICE_PATH: "socket://192.168.1.200:1234", }, @@ -433,21 +440,26 @@ async def test_zigate_discovery_via_usb(probe_mock, hass: HomeAssistant) -> None result2 = await hass.config_entries.flow.async_configure( result["flow_id"], user_input={} ) + assert result2["step_id"] == "verify_radio" + + result3 = await hass.config_entries.flow.async_configure( + result["flow_id"], user_input={} + ) await hass.async_block_till_done() - assert result2["type"] == FlowResultType.MENU - assert result2["step_id"] == "choose_formation_strategy" + assert result3["type"] == FlowResultType.MENU + assert result3["step_id"] == "choose_formation_strategy" with patch("homeassistant.components.zha.async_setup_entry", return_value=True): - result3 = await hass.config_entries.flow.async_configure( - result2["flow_id"], + result4 = await hass.config_entries.flow.async_configure( + result3["flow_id"], user_input={"next_step_id": config_flow.FORMATION_REUSE_SETTINGS}, ) await hass.async_block_till_done() - assert result3["type"] == FlowResultType.CREATE_ENTRY - assert result3["title"] == "zigate radio" - assert result3["data"] == { + assert result4["type"] == FlowResultType.CREATE_ENTRY + assert result4["title"] == "zigate radio" + assert result4["data"] == { "device": { "path": "/dev/ttyZIGBEE", },