Skip ignored discovery entries when showing migrate/setup config flow steps for ZHA and Hardware (#152895)

This commit is contained in:
puddly
2025-09-24 11:31:04 -04:00
committed by GitHub
parent 70077511a3
commit ccf0011ac2
4 changed files with 129 additions and 8 deletions

View File

@@ -123,8 +123,12 @@ class BaseFirmwareInstallFlow(ConfigEntryBaseFlow, ABC):
) -> ConfigFlowResult:
"""Pick Thread or Zigbee firmware."""
# Determine if ZHA or Thread are already configured to present migrate options
zha_entries = self.hass.config_entries.async_entries(ZHA_DOMAIN)
otbr_entries = self.hass.config_entries.async_entries(OTBR_DOMAIN)
zha_entries = self.hass.config_entries.async_entries(
ZHA_DOMAIN, include_ignore=False
)
otbr_entries = self.hass.config_entries.async_entries(
OTBR_DOMAIN, include_ignore=False
)
return self.async_show_menu(
step_id="pick_firmware",

View File

@@ -364,7 +364,7 @@ class BaseZhaFlow(ConfigEntryBaseFlow):
if user_input is not None or self._radio_mgr.radio_type in RECOMMENDED_RADIOS:
# ZHA disables the single instance check and will decide at runtime if we
# are migrating or setting up from scratch
if self.hass.config_entries.async_entries(DOMAIN):
if self.hass.config_entries.async_entries(DOMAIN, include_ignore=False):
return await self.async_step_choose_migration_strategy()
return await self.async_step_choose_setup_strategy()
@@ -386,7 +386,7 @@ class BaseZhaFlow(ConfigEntryBaseFlow):
# Allow onboarding for new users to just create a new network automatically
if (
not onboarding.async_is_onboarded(self.hass)
and not self.hass.config_entries.async_entries(DOMAIN)
and not self.hass.config_entries.async_entries(DOMAIN, include_ignore=False)
and not self._radio_mgr.backups
):
return await self.async_step_setup_strategy_recommended()
@@ -438,7 +438,9 @@ class BaseZhaFlow(ConfigEntryBaseFlow):
"""Erase the old radio's network settings before migration."""
# Like in the options flow, pull the correct settings from the config entry
config_entries = self.hass.config_entries.async_entries(DOMAIN)
config_entries = self.hass.config_entries.async_entries(
DOMAIN, include_ignore=False
)
if config_entries:
assert len(config_entries) == 1
@@ -697,7 +699,9 @@ class ZhaConfigFlowHandler(BaseZhaFlow, ConfigFlow, domain=DOMAIN):
self._set_confirm_only()
zha_config_entries = self.hass.config_entries.async_entries(DOMAIN)
zha_config_entries = self.hass.config_entries.async_entries(
DOMAIN, include_ignore=False
)
# Without confirmation, discovery can automatically progress into parts of the
# config flow logic that interacts with hardware.
@@ -866,7 +870,9 @@ class ZhaConfigFlowHandler(BaseZhaFlow, ConfigFlow, domain=DOMAIN):
# ZHA is still single instance only, even though we use discovery to allow for
# migrating to a new radio
zha_config_entries = self.hass.config_entries.async_entries(DOMAIN)
zha_config_entries = self.hass.config_entries.async_entries(
DOMAIN, include_ignore=False
)
data = await self._get_config_entry_data()
if len(zha_config_entries) == 1:

View File

@@ -26,7 +26,13 @@ from homeassistant.components.homeassistant_hardware.util import (
ApplicationType,
FirmwareInfo,
)
from homeassistant.config_entries import ConfigEntry, ConfigFlowResult, OptionsFlow
from homeassistant.config_entries import (
SOURCE_IGNORE,
SOURCE_USER,
ConfigEntry,
ConfigFlowResult,
OptionsFlow,
)
from homeassistant.core import HomeAssistant, callback
from homeassistant.data_entry_flow import FlowResultType
from homeassistant.exceptions import HomeAssistantError
@@ -1100,3 +1106,59 @@ async def test_config_flow_thread_migrate_handler(hass: HomeAssistant) -> None:
assert result["type"] is FlowResultType.SHOW_PROGRESS
assert result["progress_action"] == "install_firmware"
assert result["step_id"] == "install_thread_firmware"
@pytest.mark.parametrize(
("zha_source", "otbr_source", "expected_menu"),
[
(
SOURCE_USER,
SOURCE_USER,
["pick_firmware_zigbee_migrate", "pick_firmware_thread_migrate"],
),
(
SOURCE_IGNORE,
SOURCE_USER,
["pick_firmware_zigbee", "pick_firmware_thread_migrate"],
),
(
SOURCE_USER,
SOURCE_IGNORE,
["pick_firmware_zigbee_migrate", "pick_firmware_thread"],
),
(
SOURCE_IGNORE,
SOURCE_IGNORE,
["pick_firmware_zigbee", "pick_firmware_thread"],
),
],
)
async def test_config_flow_pick_firmware_with_ignored_entries(
hass: HomeAssistant, zha_source: str, otbr_source: str, expected_menu: str
) -> None:
"""Test that ignored entries are properly excluded from migration menu options."""
zha_entry = MockConfigEntry(
domain="zha",
data={"device": {"path": "/dev/ttyUSB1"}},
title="ZHA",
source=zha_source,
)
zha_entry.add_to_hass(hass)
otbr_entry = MockConfigEntry(
domain="otbr",
data={"url": "http://192.168.1.100:8081"},
title="OTBR",
source=otbr_source,
)
otbr_entry.add_to_hass(hass)
# Set up the flow
init_result = await hass.config_entries.flow.async_init(
TEST_DOMAIN, context={"source": "hardware"}
)
assert init_result["type"] is FlowResultType.MENU
assert init_result["step_id"] == "pick_firmware"
assert init_result["menu_options"] == expected_menu

View File

@@ -2378,3 +2378,52 @@ async def test_formation_strategy_restore_manual_backup_overwrite_ieee_ezsp_writ
assert mock_restore_backup.call_count == 1
assert mock_restore_backup.mock_calls[0].kwargs["overwrite_ieee"] is True
@patch(f"bellows.{PROBE_FUNCTION_PATH}", AsyncMock(return_value=True))
async def test_migrate_setup_options_with_ignored_discovery(
hass: HomeAssistant, config_entry: MockConfigEntry
) -> None:
"""Test that ignored discovery info is migrated to options."""
# Ignored ZHA
entry = MockConfigEntry(
domain=DOMAIN,
unique_id="AAAA:AAAA_1234_test_zigbee radio",
data={
CONF_DEVICE: {
CONF_DEVICE_PATH: "/dev/ttyUSB1",
CONF_BAUDRATE: 115200,
CONF_FLOW_CONTROL: None,
}
},
source=config_entries.SOURCE_IGNORE,
)
entry.add_to_hass(hass)
# Set up one discovery entry
discovery_info = UsbServiceInfo(
device="/dev/ttyZIGBEE",
pid="BBBB",
vid="BBBB",
serial_number="5678",
description="zigbee radio",
manufacturer="test manufacturer",
)
discovery_result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USB}, data=discovery_info
)
await hass.async_block_till_done()
# Progress the discovery
confirm_result = await hass.config_entries.flow.async_configure(
discovery_result["flow_id"], user_input={}
)
await hass.async_block_till_done()
# We only show "setup" options, not "migrate"
assert confirm_result["step_id"] == "choose_setup_strategy"
assert confirm_result["menu_options"] == [
"setup_strategy_recommended",
"setup_strategy_advanced",
]