Add WS command for changing thread channels (#94525)

pull/96896/head
Erik Montnemery 2023-07-19 10:48:32 +02:00 committed by GitHub
parent 80a7447030
commit b53eae2846
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 200 additions and 56 deletions

View File

@ -3,11 +3,14 @@
from typing import cast from typing import cast
import python_otbr_api import python_otbr_api
from python_otbr_api import tlv_parser from python_otbr_api import PENDING_DATASET_DELAY_TIMER, tlv_parser
from python_otbr_api.tlv_parser import MeshcopTLVType from python_otbr_api.tlv_parser import MeshcopTLVType
import voluptuous as vol import voluptuous as vol
from homeassistant.components import websocket_api from homeassistant.components import websocket_api
from homeassistant.components.homeassistant_hardware.silabs_multiprotocol_addon import (
is_multiprotocol_url,
)
from homeassistant.components.thread import async_add_dataset, async_get_dataset from homeassistant.components.thread import async_add_dataset, async_get_dataset
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import HomeAssistantError from homeassistant.exceptions import HomeAssistantError
@ -22,6 +25,7 @@ def async_setup(hass: HomeAssistant) -> None:
websocket_api.async_register_command(hass, websocket_info) websocket_api.async_register_command(hass, websocket_info)
websocket_api.async_register_command(hass, websocket_create_network) websocket_api.async_register_command(hass, websocket_create_network)
websocket_api.async_register_command(hass, websocket_get_extended_address) websocket_api.async_register_command(hass, websocket_get_extended_address)
websocket_api.async_register_command(hass, websocket_set_channel)
websocket_api.async_register_command(hass, websocket_set_network) websocket_api.async_register_command(hass, websocket_set_network)
@ -43,7 +47,8 @@ async def websocket_info(
data: OTBRData = hass.data[DOMAIN] data: OTBRData = hass.data[DOMAIN]
try: try:
dataset = await data.get_active_dataset_tlvs() dataset = await data.get_active_dataset()
dataset_tlvs = await data.get_active_dataset_tlvs()
except HomeAssistantError as exc: except HomeAssistantError as exc:
connection.send_error(msg["id"], "get_dataset_failed", str(exc)) connection.send_error(msg["id"], "get_dataset_failed", str(exc))
return return
@ -52,7 +57,8 @@ async def websocket_info(
msg["id"], msg["id"],
{ {
"url": data.url, "url": data.url,
"active_dataset_tlvs": dataset.hex() if dataset else None, "active_dataset_tlvs": dataset_tlvs.hex() if dataset_tlvs else None,
"channel": dataset.channel if dataset else None,
}, },
) )
@ -205,3 +211,41 @@ async def websocket_get_extended_address(
return return
connection.send_result(msg["id"], {"extended_address": extended_address.hex()}) connection.send_result(msg["id"], {"extended_address": extended_address.hex()})
@websocket_api.websocket_command(
{
"type": "otbr/set_channel",
vol.Required("channel"): int,
}
)
@websocket_api.require_admin
@websocket_api.async_response
async def websocket_set_channel(
hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg: dict
) -> None:
"""Set current channel."""
if DOMAIN not in hass.data:
connection.send_error(msg["id"], "not_loaded", "No OTBR API loaded")
return
data: OTBRData = hass.data[DOMAIN]
if is_multiprotocol_url(data.url):
connection.send_error(
msg["id"],
"multiprotocol_enabled",
"Channel change not allowed when in multiprotocol mode",
)
return
channel: int = msg["channel"]
delay: float = PENDING_DATASET_DELAY_TIMER / 1000
try:
await data.set_channel(channel)
except HomeAssistantError as exc:
connection.send_error(msg["id"], "set_channel_failed", str(exc))
return
connection.send_result(msg["id"], {"delay": delay})

View File

@ -1,7 +1,7 @@
"""Tests for the Open Thread Border Router integration.""" """Tests for the Open Thread Border Router integration."""
BASE_URL = "http://core-silabs-multiprotocol:8081" BASE_URL = "http://core-silabs-multiprotocol:8081"
CONFIG_ENTRY_DATA = {"url": "http://core-silabs-multiprotocol:8081"} CONFIG_ENTRY_DATA_MULTIPAN = {"url": "http://core-silabs-multiprotocol:8081"}
CONFIG_ENTRY_DATA_2 = {"url": "http://core-silabs-multiprotocol_2:8081"} CONFIG_ENTRY_DATA_THREAD = {"url": "/dev/ttyAMA1"}
DATASET_CH15 = bytes.fromhex( DATASET_CH15 = bytes.fromhex(
"0E080000000000010000000300000F35060004001FFFE00208F642646DA209B1D00708FDF57B5A" "0E080000000000010000000300000F35060004001FFFE00208F642646DA209B1D00708FDF57B5A"

View File

@ -6,16 +6,34 @@ import pytest
from homeassistant.components import otbr from homeassistant.components import otbr
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from . import CONFIG_ENTRY_DATA, DATASET_CH16 from . import CONFIG_ENTRY_DATA_MULTIPAN, CONFIG_ENTRY_DATA_THREAD, DATASET_CH16
from tests.common import MockConfigEntry from tests.common import MockConfigEntry
@pytest.fixture(name="otbr_config_entry") @pytest.fixture(name="otbr_config_entry_multipan")
async def otbr_config_entry_fixture(hass): async def otbr_config_entry_multipan_fixture(hass):
"""Mock Open Thread Border Router config entry.""" """Mock Open Thread Border Router config entry."""
config_entry = MockConfigEntry( config_entry = MockConfigEntry(
data=CONFIG_ENTRY_DATA, data=CONFIG_ENTRY_DATA_MULTIPAN,
domain=otbr.DOMAIN,
options={},
title="Open Thread Border Router",
)
config_entry.add_to_hass(hass)
with patch(
"python_otbr_api.OTBR.get_active_dataset_tlvs", return_value=DATASET_CH16
), patch(
"homeassistant.components.otbr.util.compute_pskc"
): # Patch to speed up tests
assert await hass.config_entries.async_setup(config_entry.entry_id)
@pytest.fixture(name="otbr_config_entry_thread")
async def otbr_config_entry_thread_fixture(hass):
"""Mock Open Thread Border Router config entry."""
config_entry = MockConfigEntry(
data=CONFIG_ENTRY_DATA_THREAD,
domain=otbr.DOMAIN, domain=otbr.DOMAIN,
options={}, options={},
title="Open Thread Border Router", title="Open Thread Border Router",

View File

@ -15,8 +15,8 @@ from homeassistant.setup import async_setup_component
from . import ( from . import (
BASE_URL, BASE_URL,
CONFIG_ENTRY_DATA, CONFIG_ENTRY_DATA_MULTIPAN,
CONFIG_ENTRY_DATA_2, CONFIG_ENTRY_DATA_THREAD,
DATASET_CH15, DATASET_CH15,
DATASET_CH16, DATASET_CH16,
DATASET_INSECURE_NW_KEY, DATASET_INSECURE_NW_KEY,
@ -38,7 +38,7 @@ async def test_import_dataset(hass: HomeAssistant) -> None:
issue_registry = ir.async_get(hass) issue_registry = ir.async_get(hass)
config_entry = MockConfigEntry( config_entry = MockConfigEntry(
data=CONFIG_ENTRY_DATA, data=CONFIG_ENTRY_DATA_MULTIPAN,
domain=otbr.DOMAIN, domain=otbr.DOMAIN,
options={}, options={},
title="My OTBR", title="My OTBR",
@ -74,7 +74,7 @@ async def test_import_share_radio_channel_collision(
multiprotocol_addon_manager_mock.async_get_channel.return_value = 15 multiprotocol_addon_manager_mock.async_get_channel.return_value = 15
config_entry = MockConfigEntry( config_entry = MockConfigEntry(
data=CONFIG_ENTRY_DATA, data=CONFIG_ENTRY_DATA_MULTIPAN,
domain=otbr.DOMAIN, domain=otbr.DOMAIN,
options={}, options={},
title="My OTBR", title="My OTBR",
@ -107,7 +107,7 @@ async def test_import_share_radio_no_channel_collision(
multiprotocol_addon_manager_mock.async_get_channel.return_value = 15 multiprotocol_addon_manager_mock.async_get_channel.return_value = 15
config_entry = MockConfigEntry( config_entry = MockConfigEntry(
data=CONFIG_ENTRY_DATA, data=CONFIG_ENTRY_DATA_MULTIPAN,
domain=otbr.DOMAIN, domain=otbr.DOMAIN,
options={}, options={},
title="My OTBR", title="My OTBR",
@ -138,7 +138,7 @@ async def test_import_insecure_dataset(hass: HomeAssistant, dataset: bytes) -> N
issue_registry = ir.async_get(hass) issue_registry = ir.async_get(hass)
config_entry = MockConfigEntry( config_entry = MockConfigEntry(
data=CONFIG_ENTRY_DATA, data=CONFIG_ENTRY_DATA_MULTIPAN,
domain=otbr.DOMAIN, domain=otbr.DOMAIN,
options={}, options={},
title="My OTBR", title="My OTBR",
@ -169,7 +169,7 @@ async def test_config_entry_not_ready(hass: HomeAssistant, error) -> None:
"""Test raising ConfigEntryNotReady .""" """Test raising ConfigEntryNotReady ."""
config_entry = MockConfigEntry( config_entry = MockConfigEntry(
data=CONFIG_ENTRY_DATA, data=CONFIG_ENTRY_DATA_MULTIPAN,
domain=otbr.DOMAIN, domain=otbr.DOMAIN,
options={}, options={},
title="My OTBR", title="My OTBR",
@ -182,7 +182,7 @@ async def test_config_entry_not_ready(hass: HomeAssistant, error) -> None:
async def test_config_entry_update(hass: HomeAssistant) -> None: async def test_config_entry_update(hass: HomeAssistant) -> None:
"""Test update config entry settings.""" """Test update config entry settings."""
config_entry = MockConfigEntry( config_entry = MockConfigEntry(
data=CONFIG_ENTRY_DATA, data=CONFIG_ENTRY_DATA_MULTIPAN,
domain=otbr.DOMAIN, domain=otbr.DOMAIN,
options={}, options={},
title="My OTBR", title="My OTBR",
@ -193,10 +193,10 @@ async def test_config_entry_update(hass: HomeAssistant) -> None:
with patch("python_otbr_api.OTBR", return_value=mock_api) as mock_otrb_api: with patch("python_otbr_api.OTBR", return_value=mock_api) as mock_otrb_api:
assert await hass.config_entries.async_setup(config_entry.entry_id) assert await hass.config_entries.async_setup(config_entry.entry_id)
mock_otrb_api.assert_called_once_with(CONFIG_ENTRY_DATA["url"], ANY, ANY) mock_otrb_api.assert_called_once_with(CONFIG_ENTRY_DATA_MULTIPAN["url"], ANY, ANY)
new_config_entry_data = {"url": "http://core-silabs-multiprotocol:8082"} new_config_entry_data = {"url": "http://core-silabs-multiprotocol:8082"}
assert CONFIG_ENTRY_DATA["url"] != new_config_entry_data["url"] assert CONFIG_ENTRY_DATA_MULTIPAN["url"] != new_config_entry_data["url"]
with patch("python_otbr_api.OTBR", return_value=mock_api) as mock_otrb_api: with patch("python_otbr_api.OTBR", return_value=mock_api) as mock_otrb_api:
hass.config_entries.async_update_entry(config_entry, data=new_config_entry_data) hass.config_entries.async_update_entry(config_entry, data=new_config_entry_data)
await hass.async_block_till_done() await hass.async_block_till_done()
@ -205,7 +205,7 @@ async def test_config_entry_update(hass: HomeAssistant) -> None:
async def test_remove_entry( async def test_remove_entry(
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker, otbr_config_entry hass: HomeAssistant, aioclient_mock: AiohttpClientMocker, otbr_config_entry_multipan
) -> None: ) -> None:
"""Test async_get_active_dataset_tlvs after removing the config entry.""" """Test async_get_active_dataset_tlvs after removing the config entry."""
@ -221,7 +221,7 @@ async def test_remove_entry(
async def test_get_active_dataset_tlvs( async def test_get_active_dataset_tlvs(
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker, otbr_config_entry hass: HomeAssistant, aioclient_mock: AiohttpClientMocker, otbr_config_entry_multipan
) -> None: ) -> None:
"""Test async_get_active_dataset_tlvs.""" """Test async_get_active_dataset_tlvs."""
@ -239,7 +239,7 @@ async def test_get_active_dataset_tlvs(
async def test_get_active_dataset_tlvs_empty( async def test_get_active_dataset_tlvs_empty(
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker, otbr_config_entry hass: HomeAssistant, aioclient_mock: AiohttpClientMocker, otbr_config_entry_multipan
) -> None: ) -> None:
"""Test async_get_active_dataset_tlvs.""" """Test async_get_active_dataset_tlvs."""
@ -255,7 +255,7 @@ async def test_get_active_dataset_tlvs_addon_not_installed(hass: HomeAssistant)
async def test_get_active_dataset_tlvs_404( async def test_get_active_dataset_tlvs_404(
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker, otbr_config_entry hass: HomeAssistant, aioclient_mock: AiohttpClientMocker, otbr_config_entry_multipan
) -> None: ) -> None:
"""Test async_get_active_dataset_tlvs with error.""" """Test async_get_active_dataset_tlvs with error."""
@ -265,7 +265,7 @@ async def test_get_active_dataset_tlvs_404(
async def test_get_active_dataset_tlvs_201( async def test_get_active_dataset_tlvs_201(
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker, otbr_config_entry hass: HomeAssistant, aioclient_mock: AiohttpClientMocker, otbr_config_entry_multipan
) -> None: ) -> None:
"""Test async_get_active_dataset_tlvs with error.""" """Test async_get_active_dataset_tlvs with error."""
@ -275,7 +275,7 @@ async def test_get_active_dataset_tlvs_201(
async def test_get_active_dataset_tlvs_invalid( async def test_get_active_dataset_tlvs_invalid(
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker, otbr_config_entry hass: HomeAssistant, aioclient_mock: AiohttpClientMocker, otbr_config_entry_multipan
) -> None: ) -> None:
"""Test async_get_active_dataset_tlvs with error.""" """Test async_get_active_dataset_tlvs with error."""
@ -290,13 +290,13 @@ async def test_remove_extra_entries(
"""Test we remove additional config entries.""" """Test we remove additional config entries."""
config_entry1 = MockConfigEntry( config_entry1 = MockConfigEntry(
data=CONFIG_ENTRY_DATA, data=CONFIG_ENTRY_DATA_MULTIPAN,
domain=otbr.DOMAIN, domain=otbr.DOMAIN,
options={}, options={},
title="Open Thread Border Router", title="Open Thread Border Router",
) )
config_entry2 = MockConfigEntry( config_entry2 = MockConfigEntry(
data=CONFIG_ENTRY_DATA_2, data=CONFIG_ENTRY_DATA_THREAD,
domain=otbr.DOMAIN, domain=otbr.DOMAIN,
options={}, options={},
title="Open Thread Border Router", title="Open Thread Border Router",

View File

@ -31,7 +31,9 @@ DATASET_CH16_PENDING = (
) )
async def test_async_change_channel(hass: HomeAssistant, otbr_config_entry) -> None: async def test_async_change_channel(
hass: HomeAssistant, otbr_config_entry_multipan
) -> None:
"""Test test_async_change_channel.""" """Test test_async_change_channel."""
store = await dataset_store.async_get_store(hass) store = await dataset_store.async_get_store(hass)
@ -55,7 +57,7 @@ async def test_async_change_channel(hass: HomeAssistant, otbr_config_entry) -> N
async def test_async_change_channel_no_pending( async def test_async_change_channel_no_pending(
hass: HomeAssistant, otbr_config_entry hass: HomeAssistant, otbr_config_entry_multipan
) -> None: ) -> None:
"""Test test_async_change_channel when the pending dataset already expired.""" """Test test_async_change_channel when the pending dataset already expired."""
@ -83,7 +85,7 @@ async def test_async_change_channel_no_pending(
async def test_async_change_channel_no_update( async def test_async_change_channel_no_update(
hass: HomeAssistant, otbr_config_entry hass: HomeAssistant, otbr_config_entry_multipan
) -> None: ) -> None:
"""Test test_async_change_channel when we didn't get a dataset from the OTBR.""" """Test test_async_change_channel when we didn't get a dataset from the OTBR."""
@ -112,7 +114,9 @@ async def test_async_change_channel_no_otbr(hass: HomeAssistant) -> None:
mock_set_channel.assert_not_awaited() mock_set_channel.assert_not_awaited()
async def test_async_get_channel(hass: HomeAssistant, otbr_config_entry) -> None: async def test_async_get_channel(
hass: HomeAssistant, otbr_config_entry_multipan
) -> None:
"""Test test_async_get_channel.""" """Test test_async_get_channel."""
with patch( with patch(
@ -124,7 +128,7 @@ async def test_async_get_channel(hass: HomeAssistant, otbr_config_entry) -> None
async def test_async_get_channel_no_dataset( async def test_async_get_channel_no_dataset(
hass: HomeAssistant, otbr_config_entry hass: HomeAssistant, otbr_config_entry_multipan
) -> None: ) -> None:
"""Test test_async_get_channel.""" """Test test_async_get_channel."""
@ -136,7 +140,9 @@ async def test_async_get_channel_no_dataset(
mock_get_active_dataset.assert_awaited_once_with() mock_get_active_dataset.assert_awaited_once_with()
async def test_async_get_channel_error(hass: HomeAssistant, otbr_config_entry) -> None: async def test_async_get_channel_error(
hass: HomeAssistant, otbr_config_entry_multipan
) -> None:
"""Test test_async_get_channel.""" """Test test_async_get_channel."""
with patch( with patch(
@ -160,7 +166,7 @@ async def test_async_get_channel_no_otbr(hass: HomeAssistant) -> None:
[(OTBR_MULTIPAN_URL, True), (OTBR_NON_MULTIPAN_URL, False)], [(OTBR_MULTIPAN_URL, True), (OTBR_NON_MULTIPAN_URL, False)],
) )
async def test_async_using_multipan( async def test_async_using_multipan(
hass: HomeAssistant, otbr_config_entry, url: str, expected: bool hass: HomeAssistant, otbr_config_entry_multipan, url: str, expected: bool
) -> None: ) -> None:
"""Test async_change_channel when otbr is not configured.""" """Test async_change_channel when otbr is not configured."""
data: otbr.OTBRData = hass.data[otbr.DOMAIN] data: otbr.OTBRData = hass.data[otbr.DOMAIN]

View File

@ -23,20 +23,23 @@ async def websocket_client(hass, hass_ws_client):
async def test_get_info( async def test_get_info(
hass: HomeAssistant, hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker, aioclient_mock: AiohttpClientMocker,
otbr_config_entry, otbr_config_entry_multipan,
websocket_client, websocket_client,
) -> None: ) -> None:
"""Test async_get_info.""" """Test async_get_info."""
aioclient_mock.get(f"{BASE_URL}/node/dataset/active", text=DATASET_CH16.hex()) with patch(
"python_otbr_api.OTBR.get_active_dataset",
return_value=python_otbr_api.ActiveDataSet(channel=16),
), patch("python_otbr_api.OTBR.get_active_dataset_tlvs", return_value=DATASET_CH16):
await websocket_client.send_json_auto_id({"type": "otbr/info"}) await websocket_client.send_json_auto_id({"type": "otbr/info"})
msg = await websocket_client.receive_json() msg = await websocket_client.receive_json()
assert msg["success"] assert msg["success"]
assert msg["result"] == { assert msg["result"] == {
"url": BASE_URL, "url": BASE_URL,
"active_dataset_tlvs": DATASET_CH16.hex().lower(), "active_dataset_tlvs": DATASET_CH16.hex().lower(),
"channel": 16,
} }
@ -58,12 +61,12 @@ async def test_get_info_no_entry(
async def test_get_info_fetch_fails( async def test_get_info_fetch_fails(
hass: HomeAssistant, hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker, aioclient_mock: AiohttpClientMocker,
otbr_config_entry, otbr_config_entry_multipan,
websocket_client, websocket_client,
) -> None: ) -> None:
"""Test async_get_info.""" """Test async_get_info."""
with patch( with patch(
"python_otbr_api.OTBR.get_active_dataset_tlvs", "python_otbr_api.OTBR.get_active_dataset",
side_effect=python_otbr_api.OTBRError, side_effect=python_otbr_api.OTBRError,
): ):
await websocket_client.send_json_auto_id({"type": "otbr/info"}) await websocket_client.send_json_auto_id({"type": "otbr/info"})
@ -76,7 +79,7 @@ async def test_get_info_fetch_fails(
async def test_create_network( async def test_create_network(
hass: HomeAssistant, hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker, aioclient_mock: AiohttpClientMocker,
otbr_config_entry, otbr_config_entry_multipan,
websocket_client, websocket_client,
) -> None: ) -> None:
"""Test create network.""" """Test create network."""
@ -127,7 +130,7 @@ async def test_create_network_no_entry(
async def test_create_network_fails_1( async def test_create_network_fails_1(
hass: HomeAssistant, hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker, aioclient_mock: AiohttpClientMocker,
otbr_config_entry, otbr_config_entry_multipan,
websocket_client, websocket_client,
) -> None: ) -> None:
"""Test create network.""" """Test create network."""
@ -145,7 +148,7 @@ async def test_create_network_fails_1(
async def test_create_network_fails_2( async def test_create_network_fails_2(
hass: HomeAssistant, hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker, aioclient_mock: AiohttpClientMocker,
otbr_config_entry, otbr_config_entry_multipan,
websocket_client, websocket_client,
) -> None: ) -> None:
"""Test create network.""" """Test create network."""
@ -165,7 +168,7 @@ async def test_create_network_fails_2(
async def test_create_network_fails_3( async def test_create_network_fails_3(
hass: HomeAssistant, hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker, aioclient_mock: AiohttpClientMocker,
otbr_config_entry, otbr_config_entry_multipan,
websocket_client, websocket_client,
) -> None: ) -> None:
"""Test create network.""" """Test create network."""
@ -187,7 +190,7 @@ async def test_create_network_fails_3(
async def test_create_network_fails_4( async def test_create_network_fails_4(
hass: HomeAssistant, hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker, aioclient_mock: AiohttpClientMocker,
otbr_config_entry, otbr_config_entry_multipan,
websocket_client, websocket_client,
) -> None: ) -> None:
"""Test create network.""" """Test create network."""
@ -209,7 +212,7 @@ async def test_create_network_fails_4(
async def test_create_network_fails_5( async def test_create_network_fails_5(
hass: HomeAssistant, hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker, aioclient_mock: AiohttpClientMocker,
otbr_config_entry, otbr_config_entry_multipan,
websocket_client, websocket_client,
) -> None: ) -> None:
"""Test create network.""" """Test create network."""
@ -228,7 +231,7 @@ async def test_create_network_fails_5(
async def test_create_network_fails_6( async def test_create_network_fails_6(
hass: HomeAssistant, hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker, aioclient_mock: AiohttpClientMocker,
otbr_config_entry, otbr_config_entry_multipan,
websocket_client, websocket_client,
) -> None: ) -> None:
"""Test create network.""" """Test create network."""
@ -248,7 +251,7 @@ async def test_create_network_fails_6(
async def test_set_network( async def test_set_network(
hass: HomeAssistant, hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker, aioclient_mock: AiohttpClientMocker,
otbr_config_entry, otbr_config_entry_multipan,
websocket_client, websocket_client,
) -> None: ) -> None:
"""Test set network.""" """Test set network."""
@ -303,7 +306,7 @@ async def test_set_network_channel_conflict(
hass: HomeAssistant, hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker, aioclient_mock: AiohttpClientMocker,
multiprotocol_addon_manager_mock, multiprotocol_addon_manager_mock,
otbr_config_entry, otbr_config_entry_multipan,
websocket_client, websocket_client,
) -> None: ) -> None:
"""Test set network.""" """Test set network."""
@ -329,7 +332,7 @@ async def test_set_network_channel_conflict(
async def test_set_network_unknown_dataset( async def test_set_network_unknown_dataset(
hass: HomeAssistant, hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker, aioclient_mock: AiohttpClientMocker,
otbr_config_entry, otbr_config_entry_multipan,
websocket_client, websocket_client,
) -> None: ) -> None:
"""Test set network.""" """Test set network."""
@ -350,7 +353,7 @@ async def test_set_network_unknown_dataset(
async def test_set_network_fails_1( async def test_set_network_fails_1(
hass: HomeAssistant, hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker, aioclient_mock: AiohttpClientMocker,
otbr_config_entry, otbr_config_entry_multipan,
websocket_client, websocket_client,
) -> None: ) -> None:
"""Test set network.""" """Test set network."""
@ -377,7 +380,7 @@ async def test_set_network_fails_1(
async def test_set_network_fails_2( async def test_set_network_fails_2(
hass: HomeAssistant, hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker, aioclient_mock: AiohttpClientMocker,
otbr_config_entry, otbr_config_entry_multipan,
websocket_client, websocket_client,
) -> None: ) -> None:
"""Test set network.""" """Test set network."""
@ -406,7 +409,7 @@ async def test_set_network_fails_2(
async def test_set_network_fails_3( async def test_set_network_fails_3(
hass: HomeAssistant, hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker, aioclient_mock: AiohttpClientMocker,
otbr_config_entry, otbr_config_entry_multipan,
websocket_client, websocket_client,
) -> None: ) -> None:
"""Test set network.""" """Test set network."""
@ -435,7 +438,7 @@ async def test_set_network_fails_3(
async def test_get_extended_address( async def test_get_extended_address(
hass: HomeAssistant, hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker, aioclient_mock: AiohttpClientMocker,
otbr_config_entry, otbr_config_entry_multipan,
websocket_client, websocket_client,
) -> None: ) -> None:
"""Test get extended address.""" """Test get extended address."""
@ -469,7 +472,7 @@ async def test_get_extended_address_no_entry(
async def test_get_extended_address_fetch_fails( async def test_get_extended_address_fetch_fails(
hass: HomeAssistant, hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker, aioclient_mock: AiohttpClientMocker,
otbr_config_entry, otbr_config_entry_multipan,
websocket_client, websocket_client,
) -> None: ) -> None:
"""Test get extended address.""" """Test get extended address."""
@ -482,3 +485,76 @@ async def test_get_extended_address_fetch_fails(
assert not msg["success"] assert not msg["success"]
assert msg["error"]["code"] == "get_extended_address_failed" assert msg["error"]["code"] == "get_extended_address_failed"
async def test_set_channel(
hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker,
otbr_config_entry_thread,
websocket_client,
) -> None:
"""Test set channel."""
with patch("python_otbr_api.OTBR.set_channel"):
await websocket_client.send_json_auto_id(
{"type": "otbr/set_channel", "channel": 12}
)
msg = await websocket_client.receive_json()
assert msg["success"]
assert msg["result"] == {"delay": 300.0}
async def test_set_channel_multiprotocol(
hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker,
otbr_config_entry_multipan,
websocket_client,
) -> None:
"""Test set channel."""
with patch("python_otbr_api.OTBR.set_channel"):
await websocket_client.send_json_auto_id(
{"type": "otbr/set_channel", "channel": 12}
)
msg = await websocket_client.receive_json()
assert not msg["success"]
assert msg["error"]["code"] == "multiprotocol_enabled"
async def test_set_channel_no_entry(
hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker,
hass_ws_client: WebSocketGenerator,
) -> None:
"""Test set channel."""
await async_setup_component(hass, "otbr", {})
websocket_client = await hass_ws_client(hass)
await websocket_client.send_json_auto_id(
{"type": "otbr/set_channel", "channel": 12}
)
msg = await websocket_client.receive_json()
assert not msg["success"]
assert msg["error"]["code"] == "not_loaded"
async def test_set_channel_fails(
hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker,
otbr_config_entry_thread,
websocket_client,
) -> None:
"""Test set channel."""
with patch(
"python_otbr_api.OTBR.set_channel",
side_effect=python_otbr_api.OTBRError,
):
await websocket_client.send_json_auto_id(
{"type": "otbr/set_channel", "channel": 12}
)
msg = await websocket_client.receive_json()
assert not msg["success"]
assert msg["error"]["code"] == "set_channel_failed"