Add silabs_multiprotocol
platform (#92904)
* Add silabs_multiprotocol platform * Add new files * Add ZHA tests * Prevent ZHA from creating database during tests * Add delay parameter to async_change_channel * Add the updated dataset to the dataset store * Allow MultipanProtocol.async_change_channel to return a task * Notify user about the duration of migration * Update tests
This commit is contained in:
parent
4f153a8f90
commit
15e5cf01bb
19 changed files with 1072 additions and 148 deletions
|
@ -1,7 +1,7 @@
|
|||
"""Test fixtures for the Home Assistant Hardware integration."""
|
||||
from collections.abc import Generator
|
||||
from typing import Any
|
||||
from unittest.mock import MagicMock, patch
|
||||
from unittest.mock import AsyncMock, MagicMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
|
@ -32,6 +32,17 @@ def mock_zha_config_flow_setup() -> Generator[None, None, None]:
|
|||
yield
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def mock_zha_get_last_network_settings() -> Generator[None, None, None]:
|
||||
"""Mock zha.api.async_get_last_network_settings."""
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.zha.api.async_get_last_network_settings",
|
||||
AsyncMock(return_value=None),
|
||||
):
|
||||
yield
|
||||
|
||||
|
||||
@pytest.fixture(name="addon_running")
|
||||
def mock_addon_running(addon_store_info, addon_info):
|
||||
"""Mock add-on already running."""
|
||||
|
|
|
@ -11,12 +11,16 @@ from homeassistant.components.hassio.handler import HassioAPIError
|
|||
from homeassistant.components.homeassistant_hardware import silabs_multiprotocol_addon
|
||||
from homeassistant.components.zha.core.const import DOMAIN as ZHA_DOMAIN
|
||||
from homeassistant.config_entries import ConfigEntry, ConfigFlow
|
||||
from homeassistant.const import EVENT_COMPONENT_LOADED
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.data_entry_flow import FlowResult, FlowResultType
|
||||
from homeassistant.setup import ATTR_COMPONENT
|
||||
|
||||
from tests.common import (
|
||||
MockConfigEntry,
|
||||
MockModule,
|
||||
MockPlatform,
|
||||
flush_store,
|
||||
mock_config_flow,
|
||||
mock_integration,
|
||||
mock_platform,
|
||||
|
@ -96,6 +100,54 @@ def config_flow_handler(
|
|||
yield
|
||||
|
||||
|
||||
class MockMultiprotocolPlatform(MockPlatform):
|
||||
"""A mock multiprotocol platform."""
|
||||
|
||||
channel = 15
|
||||
using_multipan = True
|
||||
|
||||
def __init__(self, **kwargs: Any) -> None:
|
||||
"""Initialize."""
|
||||
super().__init__(**kwargs)
|
||||
self.change_channel_calls = []
|
||||
|
||||
async def async_change_channel(
|
||||
self, hass: HomeAssistant, channel: int, delay: float
|
||||
) -> None:
|
||||
"""Set the channel to be used."""
|
||||
self.change_channel_calls.append((channel, delay))
|
||||
|
||||
async def async_get_channel(self, hass: HomeAssistant) -> int | None:
|
||||
"""Return the channel."""
|
||||
return self.channel
|
||||
|
||||
async def async_using_multipan(self, hass: HomeAssistant) -> bool:
|
||||
"""Return if the multiprotocol device is used."""
|
||||
return self.using_multipan
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_multiprotocol_platform(
|
||||
hass: HomeAssistant,
|
||||
) -> Generator[FakeConfigFlow, None, None]:
|
||||
"""Fixture for a test silabs multiprotocol platform."""
|
||||
hass.config.components.add(TEST_DOMAIN)
|
||||
platform = MockMultiprotocolPlatform()
|
||||
mock_platform(hass, f"{TEST_DOMAIN}.silabs_multiprotocol", platform)
|
||||
return platform
|
||||
|
||||
|
||||
def get_suggested(schema, key):
|
||||
"""Get suggested value for key in voluptuous schema."""
|
||||
for k in schema:
|
||||
if k == key:
|
||||
if k.description is None or "suggested_value" not in k.description:
|
||||
return None
|
||||
return k.description["suggested_value"]
|
||||
# Wanted key absent from schema
|
||||
raise Exception
|
||||
|
||||
|
||||
async def test_option_flow_install_multi_pan_addon(
|
||||
hass: HomeAssistant,
|
||||
addon_store_info,
|
||||
|
@ -215,7 +267,13 @@ async def test_option_flow_install_multi_pan_addon_zha(
|
|||
assert result["step_id"] == "configure_addon"
|
||||
install_addon.assert_called_once_with(hass, "core_silabs_multiprotocol")
|
||||
|
||||
result = await hass.config_entries.options.async_configure(result["flow_id"])
|
||||
multipan_manager = await silabs_multiprotocol_addon.get_addon_manager(hass)
|
||||
assert multipan_manager._channel is None
|
||||
with patch(
|
||||
"homeassistant.components.zha.silabs_multiprotocol.async_get_channel",
|
||||
return_value=11,
|
||||
):
|
||||
result = await hass.config_entries.options.async_configure(result["flow_id"])
|
||||
assert result["type"] == FlowResultType.SHOW_PROGRESS
|
||||
assert result["step_id"] == "start_addon"
|
||||
set_addon_options.assert_called_once_with(
|
||||
|
@ -230,6 +288,8 @@ async def test_option_flow_install_multi_pan_addon_zha(
|
|||
}
|
||||
},
|
||||
)
|
||||
# Check the channel is initialized from ZHA
|
||||
assert multipan_manager._channel == 11
|
||||
# Check the ZHA config entry data is updated
|
||||
assert zha_config_entry.data == {
|
||||
"device": {
|
||||
|
@ -393,7 +453,64 @@ async def test_option_flow_addon_installed_other_device(
|
|||
assert result["type"] == FlowResultType.CREATE_ENTRY
|
||||
|
||||
|
||||
async def test_option_flow_addon_installed_same_device(
|
||||
@pytest.mark.parametrize(
|
||||
("configured_channel", "suggested_channel"), [(None, "15"), (11, "11")]
|
||||
)
|
||||
async def test_option_flow_addon_installed_same_device_reconfigure(
|
||||
hass: HomeAssistant,
|
||||
addon_info,
|
||||
addon_store_info,
|
||||
addon_installed,
|
||||
mock_multiprotocol_platform: MockMultiprotocolPlatform,
|
||||
configured_channel: int | None,
|
||||
suggested_channel: int,
|
||||
) -> None:
|
||||
"""Test installing 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)
|
||||
|
||||
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"] == "reconfigure_addon"
|
||||
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
|
||||
|
||||
assert mock_multiprotocol_platform.change_channel_calls == [(14, 300)]
|
||||
|
||||
|
||||
async def test_option_flow_addon_installed_same_device_uninstall(
|
||||
hass: HomeAssistant,
|
||||
addon_info,
|
||||
addon_store_info,
|
||||
|
@ -417,8 +534,15 @@ async def test_option_flow_addon_installed_same_device(
|
|||
side_effect=Mock(return_value=True),
|
||||
):
|
||||
result = await hass.config_entries.options.async_init(config_entry.entry_id)
|
||||
assert result["type"] == FlowResultType.FORM
|
||||
assert result["step_id"] == "show_revert_guide"
|
||||
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": "uninstall_addon"},
|
||||
)
|
||||
assert result["type"] == FlowResultType.FORM
|
||||
assert result["step_id"] == "show_revert_guide"
|
||||
|
||||
result = await hass.config_entries.options.async_configure(result["flow_id"], {})
|
||||
assert result["type"] == FlowResultType.CREATE_ENTRY
|
||||
|
@ -806,3 +930,80 @@ def test_is_multiprotocol_url() -> None:
|
|||
"http://core-silabs-multiprotocol:8081"
|
||||
)
|
||||
assert not silabs_multiprotocol_addon.is_multiprotocol_url("/dev/ttyAMA1")
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
(
|
||||
"initial_multipan_channel",
|
||||
"platform_using_multipan",
|
||||
"platform_channel",
|
||||
"new_multipan_channel",
|
||||
),
|
||||
[
|
||||
(None, True, 15, 15),
|
||||
(None, False, 15, None),
|
||||
(11, True, 15, 11),
|
||||
(None, True, None, None),
|
||||
],
|
||||
)
|
||||
async def test_import_channel(
|
||||
hass: HomeAssistant,
|
||||
initial_multipan_channel: int | None,
|
||||
platform_using_multipan: bool,
|
||||
platform_channel: int | None,
|
||||
new_multipan_channel: int | None,
|
||||
) -> None:
|
||||
"""Test channel is initialized from first platform."""
|
||||
multipan_manager = await silabs_multiprotocol_addon.get_addon_manager(hass)
|
||||
multipan_manager._channel = initial_multipan_channel
|
||||
|
||||
mock_multiprotocol_platform = MockMultiprotocolPlatform()
|
||||
mock_multiprotocol_platform.channel = platform_channel
|
||||
mock_multiprotocol_platform.using_multipan = platform_using_multipan
|
||||
|
||||
hass.config.components.add(TEST_DOMAIN)
|
||||
mock_platform(
|
||||
hass, f"{TEST_DOMAIN}.silabs_multiprotocol", mock_multiprotocol_platform
|
||||
)
|
||||
hass.bus.async_fire(EVENT_COMPONENT_LOADED, {ATTR_COMPONENT: TEST_DOMAIN})
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert multipan_manager.async_get_channel() == new_multipan_channel
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
(
|
||||
"platform_using_multipan",
|
||||
"expected_calls",
|
||||
),
|
||||
[
|
||||
(True, [(15, 10)]),
|
||||
(False, []),
|
||||
],
|
||||
)
|
||||
async def test_change_channel(
|
||||
hass: HomeAssistant,
|
||||
mock_multiprotocol_platform: MockMultiprotocolPlatform,
|
||||
platform_using_multipan: bool,
|
||||
expected_calls: list[int],
|
||||
) -> None:
|
||||
"""Test channel is initialized from first platform."""
|
||||
multipan_manager = await silabs_multiprotocol_addon.get_addon_manager(hass)
|
||||
mock_multiprotocol_platform.using_multipan = platform_using_multipan
|
||||
|
||||
await multipan_manager.async_change_channel(15, 10)
|
||||
assert mock_multiprotocol_platform.change_channel_calls == expected_calls
|
||||
|
||||
|
||||
async def test_load_preferences(hass: HomeAssistant) -> None:
|
||||
"""Make sure that we can load/save data correctly."""
|
||||
multipan_manager = await silabs_multiprotocol_addon.get_addon_manager(hass)
|
||||
assert multipan_manager._channel != 11
|
||||
multipan_manager.async_set_channel(11)
|
||||
|
||||
await flush_store(multipan_manager._store)
|
||||
|
||||
multipan_manager2 = silabs_multiprotocol_addon.MultiprotocolAddonManager(hass)
|
||||
await multipan_manager2.async_setup()
|
||||
|
||||
assert multipan_manager._channel == multipan_manager2._channel
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue