Warn when changing multipan channel if there are not 2 known users (#95898)

* Warn when changing multipan channel if there are not 2 known users

* Add test

* Improve messages

* Tweak translation string

* Adjust message

* Remove unused translation placeholders
This commit is contained in:
Erik Montnemery 2023-07-05 16:51:28 +02:00 committed by GitHub
parent ea57f78392
commit c7f6d84058
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 181 additions and 16 deletions

View File

@ -138,6 +138,17 @@ class MultiprotocolAddonManager(AddonManager):
return tasks
async def async_active_platforms(self) -> list[str]:
"""Return a list of platforms using the multipan radio."""
active_platforms: list[str] = []
for integration_domain, platform in self._platforms.items():
if not await platform.async_using_multipan(self._hass):
continue
active_platforms.append(integration_domain)
return active_platforms
@callback
def async_get_channel(self) -> int | None:
"""Get the channel."""
@ -510,7 +521,26 @@ class OptionsFlowHandler(config_entries.OptionsFlow, ABC):
) -> FlowResult:
"""Reconfigure the addon."""
multipan_manager = await get_addon_manager(self.hass)
active_platforms = await multipan_manager.async_active_platforms()
if set(active_platforms) != {"otbr", "zha"}:
return await self.async_step_notify_unknown_multipan_user()
return await self.async_step_change_channel()
async def async_step_notify_unknown_multipan_user(
self, user_input: dict[str, Any] | None = None
) -> FlowResult:
"""Notify that there may be unknown multipan platforms."""
if user_input is None:
return self.async_show_form(
step_id="notify_unknown_multipan_user",
)
return await self.async_step_change_channel()
async def async_step_change_channel(
self, user_input: dict[str, Any] | None = None
) -> FlowResult:
"""Change the channel."""
multipan_manager = await get_addon_manager(self.hass)
if user_input is None:
channels = [str(x) for x in range(11, 27)]
suggested_channel = DEFAULT_CHANNEL
@ -529,7 +559,7 @@ class OptionsFlowHandler(config_entries.OptionsFlow, ABC):
}
)
return self.async_show_form(
step_id="reconfigure_addon", data_schema=data_schema
step_id="change_channel", data_schema=data_schema
)
# Change the shared channel

View File

@ -18,6 +18,12 @@
"uninstall_addon": "[%key:component::homeassistant_hardware::silabs_multiprotocol_hardware::options::step::uninstall_addon::title%]"
}
},
"change_channel": {
"title": "[%key:component::homeassistant_hardware::silabs_multiprotocol_hardware::options::step::reconfigure_addon::title%]",
"data": {
"channel": "Channel"
}
},
"install_addon": {
"title": "The Silicon Labs Multiprotocol add-on installation has started"
},
@ -25,11 +31,12 @@
"title": "Channel change initiated",
"description": "A Zigbee and Thread channel change has been initiated and will finish in {delay_minutes} minutes."
},
"notify_unknown_multipan_user": {
"title": "Manual configuration may be needed",
"description": "Home Assistant can automatically change the channels for otbr and zha. If you have configured another integration to use the radio, for example Zigbee2MQTT, you will have to reconfigure the channel in that integration after completing this guide."
},
"reconfigure_addon": {
"title": "Reconfigure IEEE 802.15.4 radio multiprotocol support",
"data": {
"channel": "Channel"
}
"title": "Reconfigure IEEE 802.15.4 radio multiprotocol support"
},
"show_revert_guide": {
"title": "Multiprotocol support is enabled for this device",

View File

@ -17,6 +17,12 @@
"uninstall_addon": "[%key:component::homeassistant_hardware::silabs_multiprotocol_hardware::options::step::addon_menu::menu_options::uninstall_addon%]"
}
},
"change_channel": {
"title": "[%key:component::homeassistant_hardware::silabs_multiprotocol_hardware::options::step::change_channel::title%]",
"data": {
"channel": "[%key:component::homeassistant_hardware::silabs_multiprotocol_hardware::options::step::change_channel::data::channel%]"
}
},
"install_addon": {
"title": "[%key:component::homeassistant_hardware::silabs_multiprotocol_hardware::options::step::install_addon::title%]"
},
@ -24,11 +30,12 @@
"title": "[%key:component::homeassistant_hardware::silabs_multiprotocol_hardware::options::step::notify_channel_change::title%]",
"description": "[%key:component::homeassistant_hardware::silabs_multiprotocol_hardware::options::step::notify_channel_change::description%]"
},
"notify_unknown_multipan_user": {
"title": "[%key:component::homeassistant_hardware::silabs_multiprotocol_hardware::options::step::notify_unknown_multipan_user::title%]",
"description": "[%key:component::homeassistant_hardware::silabs_multiprotocol_hardware::options::step::notify_unknown_multipan_user::description%]"
},
"reconfigure_addon": {
"title": "[%key:component::homeassistant_hardware::silabs_multiprotocol_hardware::options::step::reconfigure_addon::title%]",
"data": {
"channel": "[%key:component::homeassistant_hardware::silabs_multiprotocol_hardware::options::step::reconfigure_addon::data::channel%]"
}
"title": "[%key:component::homeassistant_hardware::silabs_multiprotocol_hardware::options::step::reconfigure_addon::title%]"
},
"show_revert_guide": {
"title": "[%key:component::homeassistant_hardware::silabs_multiprotocol_hardware::options::step::show_revert_guide::title%]",

View File

@ -17,6 +17,12 @@
"uninstall_addon": "[%key:component::homeassistant_hardware::silabs_multiprotocol_hardware::options::step::addon_menu::menu_options::uninstall_addon%]"
}
},
"change_channel": {
"title": "[%key:component::homeassistant_hardware::silabs_multiprotocol_hardware::options::step::change_channel::title%]",
"data": {
"channel": "[%key:component::homeassistant_hardware::silabs_multiprotocol_hardware::options::step::change_channel::data::channel%]"
}
},
"hardware_settings": {
"title": "Configure hardware settings",
"data": {
@ -38,6 +44,10 @@
"multipan_settings": "Configure IEEE 802.15.4 radio multiprotocol support"
}
},
"notify_unknown_multipan_user": {
"title": "[%key:component::homeassistant_hardware::silabs_multiprotocol_hardware::options::step::notify_unknown_multipan_user::title%]",
"description": "[%key:component::homeassistant_hardware::silabs_multiprotocol_hardware::options::step::notify_unknown_multipan_user::description%]"
},
"reboot_menu": {
"title": "Reboot required",
"description": "The settings have changed, but the new settings will not take effect until the system is rebooted",
@ -47,10 +57,7 @@
}
},
"reconfigure_addon": {
"title": "[%key:component::homeassistant_hardware::silabs_multiprotocol_hardware::options::step::reconfigure_addon::title%]",
"data": {
"channel": "[%key:component::homeassistant_hardware::silabs_multiprotocol_hardware::options::step::reconfigure_addon::data::channel%]"
}
"title": "[%key:component::homeassistant_hardware::silabs_multiprotocol_hardware::options::step::reconfigure_addon::title%]"
},
"show_revert_guide": {
"title": "[%key:component::homeassistant_hardware::silabs_multiprotocol_hardware::options::step::show_revert_guide::title%]",

View File

@ -27,6 +27,7 @@ from tests.common import (
)
TEST_DOMAIN = "test"
TEST_DOMAIN_2 = "test_2"
class FakeConfigFlow(ConfigFlow):
@ -456,7 +457,7 @@ async def test_option_flow_addon_installed_other_device(
@pytest.mark.parametrize(
("configured_channel", "suggested_channel"), [(None, "15"), (11, "11")]
)
async def test_option_flow_addon_installed_same_device_reconfigure(
async def test_option_flow_addon_installed_same_device_reconfigure_unexpected_users(
hass: HomeAssistant,
addon_info,
addon_store_info,
@ -465,7 +466,7 @@ async def test_option_flow_addon_installed_same_device_reconfigure(
configured_channel: int | None,
suggested_channel: int,
) -> None:
"""Test installing the multi pan addon."""
"""Test reconfiguring the multi pan addon."""
mock_integration(hass, MockModule("hassio"))
addon_info.return_value["options"]["device"] = "/dev/ttyTEST123"
@ -494,7 +495,11 @@ async def test_option_flow_addon_installed_same_device_reconfigure(
{"next_step_id": "reconfigure_addon"},
)
assert result["type"] == FlowResultType.FORM
assert result["step_id"] == "reconfigure_addon"
assert result["step_id"] == "notify_unknown_multipan_user"
result = await hass.config_entries.options.async_configure(result["flow_id"], {})
assert result["type"] == FlowResultType.FORM
assert result["step_id"] == "change_channel"
assert get_suggested(result["data_schema"].schema, "channel") == suggested_channel
result = await hass.config_entries.options.async_configure(
@ -508,6 +513,79 @@ async def test_option_flow_addon_installed_same_device_reconfigure(
assert result["type"] == FlowResultType.CREATE_ENTRY
assert mock_multiprotocol_platform.change_channel_calls == [(14, 300)]
assert multipan_manager._channel == 14
@pytest.mark.parametrize(
("configured_channel", "suggested_channel"), [(None, "15"), (11, "11")]
)
async def test_option_flow_addon_installed_same_device_reconfigure_expected_users(
hass: HomeAssistant,
addon_info,
addon_store_info,
addon_installed,
configured_channel: int | None,
suggested_channel: int,
) -> None:
"""Test reconfiguring the multi pan addon."""
mock_integration(hass, MockModule("hassio"))
addon_info.return_value["options"]["device"] = "/dev/ttyTEST123"
multipan_manager = await silabs_multiprotocol_addon.get_addon_manager(hass)
multipan_manager._channel = configured_channel
# Setup the config entry
config_entry = MockConfigEntry(
data={},
domain=TEST_DOMAIN,
options={},
title="Test HW",
)
config_entry.add_to_hass(hass)
mock_multiprotocol_platforms = {}
for domain in ["otbr", "zha"]:
mock_multiprotocol_platform = MockMultiprotocolPlatform()
mock_multiprotocol_platforms[domain] = mock_multiprotocol_platform
mock_multiprotocol_platform.channel = configured_channel
mock_multiprotocol_platform.using_multipan = True
hass.config.components.add(domain)
mock_platform(
hass, f"{domain}.silabs_multiprotocol", mock_multiprotocol_platform
)
hass.bus.async_fire(EVENT_COMPONENT_LOADED, {ATTR_COMPONENT: domain})
await hass.async_block_till_done()
with patch(
"homeassistant.components.homeassistant_hardware.silabs_multiprotocol_addon.is_hassio",
side_effect=Mock(return_value=True),
):
result = await hass.config_entries.options.async_init(config_entry.entry_id)
assert result["type"] == FlowResultType.MENU
assert result["step_id"] == "addon_menu"
result = await hass.config_entries.options.async_configure(
result["flow_id"],
{"next_step_id": "reconfigure_addon"},
)
assert result["type"] == FlowResultType.FORM
assert result["step_id"] == "change_channel"
assert get_suggested(result["data_schema"].schema, "channel") == suggested_channel
result = await hass.config_entries.options.async_configure(
result["flow_id"], {"channel": "14"}
)
assert result["type"] == FlowResultType.FORM
assert result["step_id"] == "notify_channel_change"
assert result["description_placeholders"] == {"delay_minutes": "5"}
result = await hass.config_entries.options.async_configure(result["flow_id"], {})
assert result["type"] == FlowResultType.CREATE_ENTRY
for domain in ["otbr", "zha"]:
assert mock_multiprotocol_platforms[domain].change_channel_calls == [(14, 300)]
assert multipan_manager._channel == 14
async def test_option_flow_addon_installed_same_device_uninstall(
@ -1007,3 +1085,39 @@ async def test_load_preferences(hass: HomeAssistant) -> None:
await multipan_manager2.async_setup()
assert multipan_manager._channel == multipan_manager2._channel
@pytest.mark.parametrize(
(
"multipan_platforms",
"active_platforms",
),
[
({}, []),
({TEST_DOMAIN: False}, []),
({TEST_DOMAIN: True}, [TEST_DOMAIN]),
({TEST_DOMAIN: True, TEST_DOMAIN_2: False}, [TEST_DOMAIN]),
({TEST_DOMAIN: True, TEST_DOMAIN_2: True}, [TEST_DOMAIN, TEST_DOMAIN_2]),
],
)
async def test_active_plaforms(
hass: HomeAssistant,
multipan_platforms: dict[str, bool],
active_platforms: list[str],
) -> None:
"""Test async_active_platforms."""
multipan_manager = await silabs_multiprotocol_addon.get_addon_manager(hass)
for domain, platform_using_multipan in multipan_platforms.items():
mock_multiprotocol_platform = MockMultiprotocolPlatform()
mock_multiprotocol_platform.channel = 11
mock_multiprotocol_platform.using_multipan = platform_using_multipan
hass.config.components.add(domain)
mock_platform(
hass, f"{domain}.silabs_multiprotocol", mock_multiprotocol_platform
)
hass.bus.async_fire(EVENT_COMPONENT_LOADED, {ATTR_COMPONENT: domain})
await hass.async_block_till_done()
assert await multipan_manager.async_active_platforms() == active_platforms