Compare commits

...

3 Commits

Author SHA1 Message Date
Erik
18dae08244 Set unique_id to DOMAIN 2023-01-17 19:03:17 +01:00
Erik
5d21b6e7a7 Validate URL 2023-01-17 16:33:19 +01:00
Erik
eea98b22e0 Allow manually setting up the Thread integration 2023-01-17 16:01:02 +01:00
6 changed files with 137 additions and 2 deletions

View File

@@ -1,9 +1,14 @@
"""Config flow for the Open Thread Border Router integration.""" """Config flow for the Open Thread Border Router integration."""
from __future__ import annotations from __future__ import annotations
import python_otbr_api
import voluptuous as vol
from homeassistant.components.hassio import HassioServiceInfo from homeassistant.components.hassio import HassioServiceInfo
from homeassistant.config_entries import ConfigFlow from homeassistant.config_entries import ConfigFlow
from homeassistant.const import CONF_URL
from homeassistant.data_entry_flow import FlowResult from homeassistant.data_entry_flow import FlowResult
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from .const import DOMAIN from .const import DOMAIN
@@ -13,13 +18,43 @@ class OTBRConfigFlow(ConfigFlow, domain=DOMAIN):
VERSION = 1 VERSION = 1
async def async_step_user(
self, user_input: dict[str, str] | None = None
) -> FlowResult:
"""Set up by user."""
if self._async_current_entries():
return self.async_abort(reason="single_instance_allowed")
errors = {}
if user_input is not None:
url = user_input[CONF_URL]
api = python_otbr_api.OTBR(url, async_get_clientsession(self.hass), 10)
try:
await api.get_active_dataset_tlvs()
except python_otbr_api.OTBRError:
errors["base"] = "cannot_connect"
else:
await self.async_set_unique_id(DOMAIN)
return self.async_create_entry(
title="Thread",
data={"url": url},
)
data_schema = vol.Schema({CONF_URL: str})
return self.async_show_form(
step_id="user", data_schema=data_schema, errors=errors
)
async def async_step_hassio(self, discovery_info: HassioServiceInfo) -> FlowResult: async def async_step_hassio(self, discovery_info: HassioServiceInfo) -> FlowResult:
"""Handle hassio discovery.""" """Handle hassio discovery."""
if self._async_current_entries(): if self._async_current_entries():
return self.async_abort(reason="single_instance_allowed") return self.async_abort(reason="single_instance_allowed")
config = discovery_info.config config = discovery_info.config
url = f"http://{config['host']}:{config['port']}"
await self.async_set_unique_id(DOMAIN)
return self.async_create_entry( return self.async_create_entry(
title="Thread", title="Thread",
data={"url": f"http://{config['host']}:{config['port']}"}, data={"url": url},
) )

View File

@@ -3,7 +3,7 @@
"after_dependencies": ["hassio"], "after_dependencies": ["hassio"],
"domain": "otbr", "domain": "otbr",
"iot_class": "local_polling", "iot_class": "local_polling",
"config_flow": false, "config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/otbr", "documentation": "https://www.home-assistant.io/integrations/otbr",
"integration_type": "system", "integration_type": "system",
"name": "Thread", "name": "Thread",

View File

@@ -0,0 +1,18 @@
{
"config": {
"step": {
"user": {
"data": {
"url": "[%key:common::config_flow::data::url%]"
},
"description": "Provide URL for the Open Thread Border Router's REST API"
}
},
"error": {
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]"
},
"abort": {
"already_configured": "[%key:common::config_flow::abort::already_configured_service%]"
}
}
}

View File

@@ -0,0 +1,18 @@
{
"config": {
"abort": {
"already_configured": "Service is already configured"
},
"error": {
"cannot_connect": "Failed to connect"
},
"step": {
"user": {
"data": {
"url": "URL"
},
"description": "Provide URL for the Open Thread Border Router's REST API"
}
}
}
}

View File

@@ -304,6 +304,7 @@ FLOWS = {
"openuv", "openuv",
"openweathermap", "openweathermap",
"oralb", "oralb",
"otbr",
"overkiz", "overkiz",
"ovo_energy", "ovo_energy",
"owntracks", "owntracks",

View File

@@ -1,4 +1,5 @@
"""Test the Open Thread Border Router config flow.""" """Test the Open Thread Border Router config flow."""
from http import HTTPStatus
from unittest.mock import patch from unittest.mock import patch
from homeassistant.components import hassio, otbr from homeassistant.components import hassio, otbr
@@ -6,6 +7,7 @@ from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import FlowResultType from homeassistant.data_entry_flow import FlowResultType
from tests.common import MockConfigEntry, MockModule, mock_integration from tests.common import MockConfigEntry, MockModule, mock_integration
from tests.test_util.aiohttp import AiohttpClientMocker
HASSIO_DATA = hassio.HassioServiceInfo( HASSIO_DATA = hassio.HassioServiceInfo(
config={"host": "blah", "port": "bluh"}, config={"host": "blah", "port": "bluh"},
@@ -14,6 +16,67 @@ HASSIO_DATA = hassio.HassioServiceInfo(
) )
async def test_user_flow(
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker
) -> None:
"""Test the user flow."""
url = "http://custom_url:1234"
aioclient_mock.get(f"{url}/node/dataset/active", text="aa")
result = await hass.config_entries.flow.async_init(
otbr.DOMAIN, context={"source": "user"}
)
expected_data = {"url": url}
assert result["type"] == FlowResultType.FORM
assert result["errors"] == {}
with patch(
"homeassistant.components.otbr.async_setup_entry",
return_value=True,
) as mock_setup_entry:
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
"url": url,
},
)
assert result["type"] == FlowResultType.CREATE_ENTRY
assert result["title"] == "Thread"
assert result["data"] == expected_data
assert result["options"] == {}
assert len(mock_setup_entry.mock_calls) == 1
config_entry = hass.config_entries.async_entries(otbr.DOMAIN)[0]
assert config_entry.data == expected_data
assert config_entry.options == {}
assert config_entry.title == "Thread"
assert config_entry.unique_id is None
async def test_user_flow_404(
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker
) -> None:
"""Test the user flow."""
url = "http://custom_url:1234"
aioclient_mock.get(f"{url}/node/dataset/active", status=HTTPStatus.NOT_FOUND)
result = await hass.config_entries.flow.async_init(
otbr.DOMAIN, context={"source": "user"}
)
assert result["type"] == FlowResultType.FORM
assert result["errors"] == {}
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
"url": url,
},
)
assert result["type"] == FlowResultType.FORM
assert result["errors"] == {"base": "cannot_connect"}
async def test_hassio_discovery_flow(hass: HomeAssistant) -> None: async def test_hassio_discovery_flow(hass: HomeAssistant) -> None:
"""Test the hassio discovery flow.""" """Test the hassio discovery flow."""
with patch( with patch(