Improve config flow for GIOS (#139935)

* Initial commit

* Use TYPE_CHECKING

* Update strings

* Remove default value

* Improve tests
This commit is contained in:
Maciej Bieniek 2025-03-11 12:52:40 +01:00 committed by GitHub
parent d3a96ba688
commit 98cf936ff5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 79 additions and 46 deletions

View File

@ -3,7 +3,7 @@
from __future__ import annotations from __future__ import annotations
import asyncio import asyncio
from typing import Any from typing import TYPE_CHECKING, Any
from aiohttp.client_exceptions import ClientConnectorError from aiohttp.client_exceptions import ClientConnectorError
from gios import ApiError, Gios, InvalidSensorsDataError, NoStationError from gios import ApiError, Gios, InvalidSensorsDataError, NoStationError
@ -12,6 +12,12 @@ import voluptuous as vol
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
from homeassistant.const import CONF_NAME from homeassistant.const import CONF_NAME
from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.selector import (
SelectOptionDict,
SelectSelector,
SelectSelectorConfig,
SelectSelectorMode,
)
from .const import API_TIMEOUT, CONF_STATION_ID, DOMAIN from .const import API_TIMEOUT, CONF_STATION_ID, DOMAIN
@ -27,40 +33,59 @@ class GiosFlowHandler(ConfigFlow, domain=DOMAIN):
"""Handle a flow initialized by the user.""" """Handle a flow initialized by the user."""
errors = {} errors = {}
websession = async_get_clientsession(self.hass)
if user_input is not None: if user_input is not None:
station_id = user_input[CONF_STATION_ID]
try: try:
await self.async_set_unique_id( await self.async_set_unique_id(station_id, raise_on_progress=False)
str(user_input[CONF_STATION_ID]), raise_on_progress=False
)
self._abort_if_unique_id_configured() self._abort_if_unique_id_configured()
websession = async_get_clientsession(self.hass)
async with asyncio.timeout(API_TIMEOUT): async with asyncio.timeout(API_TIMEOUT):
gios = await Gios.create(websession, user_input[CONF_STATION_ID]) gios = await Gios.create(websession, int(station_id))
await gios.async_update() await gios.async_update()
assert gios.station_name is not None # GIOS treats station ID as int
user_input[CONF_STATION_ID] = int(station_id)
if TYPE_CHECKING:
assert gios.station_name is not None
return self.async_create_entry( return self.async_create_entry(
title=gios.station_name, title=gios.station_name,
data=user_input, data=user_input,
) )
except (ApiError, ClientConnectorError, TimeoutError): except (ApiError, ClientConnectorError, TimeoutError):
errors["base"] = "cannot_connect" errors["base"] = "cannot_connect"
except NoStationError:
errors[CONF_STATION_ID] = "wrong_station_id"
except InvalidSensorsDataError: except InvalidSensorsDataError:
errors[CONF_STATION_ID] = "invalid_sensors_data" errors[CONF_STATION_ID] = "invalid_sensors_data"
try:
gios = await Gios.create(websession)
except (ApiError, ClientConnectorError, NoStationError):
return self.async_abort(reason="cannot_connect")
options: list[SelectOptionDict] = [
SelectOptionDict(value=str(station.id), label=station.name)
for station in gios.measurement_stations.values()
]
schema: vol.Schema = vol.Schema(
{
vol.Required(CONF_STATION_ID): SelectSelector(
SelectSelectorConfig(
options=options,
sort=True,
mode=SelectSelectorMode.DROPDOWN,
),
),
vol.Optional(CONF_NAME, default=self.hass.config.location_name): str,
}
)
return self.async_show_form( return self.async_show_form(
step_id="user", step_id="user",
data_schema=vol.Schema( data_schema=schema,
{
vol.Required(CONF_STATION_ID): int,
vol.Optional(
CONF_NAME, default=self.hass.config.location_name
): str,
}
),
errors=errors, errors=errors,
) )

View File

@ -5,17 +5,17 @@
"title": "GIO\u015a (Polish Chief Inspectorate Of Environmental Protection)", "title": "GIO\u015a (Polish Chief Inspectorate Of Environmental Protection)",
"data": { "data": {
"name": "[%key:common::config_flow::data::name%]", "name": "[%key:common::config_flow::data::name%]",
"station_id": "ID of the measuring station" "station_id": "Measuring station"
} }
} }
}, },
"error": { "error": {
"wrong_station_id": "ID of the measuring station is not correct.",
"invalid_sensors_data": "Invalid sensors' data for this measuring station.", "invalid_sensors_data": "Invalid sensors' data for this measuring station.",
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]" "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]"
}, },
"abort": { "abort": {
"already_configured": "[%key:common::config_flow::abort::already_configured_location%]" "already_configured": "[%key:common::config_flow::abort::already_configured_location%]",
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]"
} }
}, },
"system_health": { "system_health": {

View File

@ -6,7 +6,8 @@ from unittest.mock import patch
from gios import ApiError from gios import ApiError
from homeassistant.components.gios import config_flow from homeassistant.components.gios import config_flow
from homeassistant.components.gios.const import CONF_STATION_ID from homeassistant.components.gios.const import CONF_STATION_ID, DOMAIN
from homeassistant.config_entries import SOURCE_USER
from homeassistant.const import CONF_NAME from homeassistant.const import CONF_NAME
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import FlowResultType from homeassistant.data_entry_flow import FlowResultType
@ -17,36 +18,35 @@ from tests.common import load_fixture
CONFIG = { CONFIG = {
CONF_NAME: "Foo", CONF_NAME: "Foo",
CONF_STATION_ID: 123, CONF_STATION_ID: "123",
} }
async def test_show_form(hass: HomeAssistant) -> None: async def test_show_form(hass: HomeAssistant) -> None:
"""Test that the form is served with no input.""" """Test that the form is served with no input."""
flow = config_flow.GiosFlowHandler() with patch(
flow.hass = hass "homeassistant.components.gios.coordinator.Gios._get_stations",
return_value=STATIONS,
result = await flow.async_step_user(user_input=None) ):
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER}
)
assert result["type"] is FlowResultType.FORM assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "user" assert result["step_id"] == "user"
async def test_invalid_station_id(hass: HomeAssistant) -> None: async def test_form_with_api_error(hass: HomeAssistant) -> None:
"""Test that errors are shown when measuring station ID is invalid.""" """Test the form is aborted because of API error."""
with patch( with patch(
"homeassistant.components.gios.coordinator.Gios._get_stations", "homeassistant.components.gios.coordinator.Gios._get_stations",
return_value=STATIONS, side_effect=ApiError("error"),
): ):
flow = config_flow.GiosFlowHandler() result = await hass.config_entries.flow.async_init(
flow.hass = hass DOMAIN, context={"source": SOURCE_USER}
flow.context = {}
result = await flow.async_step_user(
user_input={CONF_NAME: "Foo", CONF_STATION_ID: 0}
) )
assert result["errors"] == {CONF_STATION_ID: "wrong_station_id"} assert result["type"] is FlowResultType.ABORT
async def test_invalid_sensor_data(hass: HomeAssistant) -> None: async def test_invalid_sensor_data(hass: HomeAssistant) -> None:
@ -76,17 +76,25 @@ async def test_invalid_sensor_data(hass: HomeAssistant) -> None:
async def test_cannot_connect(hass: HomeAssistant) -> None: async def test_cannot_connect(hass: HomeAssistant) -> None:
"""Test that errors are shown when cannot connect to GIOS server.""" """Test that errors are shown when cannot connect to GIOS server."""
with patch( with (
"homeassistant.components.gios.coordinator.Gios._async_get", patch(
side_effect=ApiError("error"), "homeassistant.components.gios.coordinator.Gios._get_stations",
return_value=STATIONS,
),
patch(
"homeassistant.components.gios.coordinator.Gios._async_get",
side_effect=ApiError("error"),
),
): ):
flow = config_flow.GiosFlowHandler() result = await hass.config_entries.flow.async_init(
flow.hass = hass DOMAIN, context={"source": SOURCE_USER}
flow.context = {} )
result = await hass.config_entries.flow.async_configure(
result["flow_id"], CONFIG
)
await hass.async_block_till_done()
result = await flow.async_step_user(user_input=CONFIG) assert result["errors"] == {"base": "cannot_connect"}
assert result["errors"] == {"base": "cannot_connect"}
async def test_create_entry(hass: HomeAssistant) -> None: async def test_create_entry(hass: HomeAssistant) -> None: