Improve error handling for incomfort gateway (#136317)

This commit is contained in:
Jan Bouwhuis 2025-01-23 18:26:28 +01:00 committed by GitHub
parent 3da9c599dc
commit 33ce795695
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 74 additions and 75 deletions

View File

@ -3,7 +3,7 @@
from __future__ import annotations from __future__ import annotations
from aiohttp import ClientResponseError from aiohttp import ClientResponseError
from incomfortclient import IncomfortError, InvalidHeaterList from incomfortclient import InvalidGateway, InvalidHeaterList
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import Platform from homeassistant.const import Platform
@ -35,12 +35,11 @@ async def async_setup_entry(hass: HomeAssistant, entry: InComfortConfigEntry) ->
await heater.update() await heater.update()
except InvalidHeaterList as exc: except InvalidHeaterList as exc:
raise NoHeaters from exc raise NoHeaters from exc
except IncomfortError as exc: except InvalidGateway as exc:
if isinstance(exc.message, ClientResponseError): raise ConfigEntryAuthFailed("Incorrect credentials") from exc
if exc.message.status == 401: except ClientResponseError as exc:
raise ConfigEntryAuthFailed("Incorrect credentials") from exc if exc.status == 404:
if exc.message.status == 404: raise NotFound from exc
raise NotFound from exc
raise InConfortUnknownError from exc raise InConfortUnknownError from exc
except TimeoutError as exc: except TimeoutError as exc:
raise InConfortTimeout from exc raise InConfortTimeout from exc

View File

@ -5,8 +5,7 @@ from __future__ import annotations
from collections.abc import Mapping from collections.abc import Mapping
from typing import Any from typing import Any
from aiohttp import ClientResponseError from incomfortclient import InvalidGateway, InvalidHeaterList
from incomfortclient import IncomfortError, InvalidHeaterList
import voluptuous as vol import voluptuous as vol
from homeassistant.config_entries import ( from homeassistant.config_entries import (
@ -77,11 +76,6 @@ OPTIONS_SCHEMA = vol.Schema(
} }
) )
ERROR_STATUS_MAPPING: dict[int, tuple[str, str]] = {
401: (CONF_PASSWORD, "auth_error"),
404: ("base", "not_found"),
}
async def async_try_connect_gateway( async def async_try_connect_gateway(
hass: HomeAssistant, config: dict[str, Any] hass: HomeAssistant, config: dict[str, Any]
@ -89,15 +83,10 @@ async def async_try_connect_gateway(
"""Try to connect to the Lan2RF gateway.""" """Try to connect to the Lan2RF gateway."""
try: try:
await async_connect_gateway(hass, config) await async_connect_gateway(hass, config)
except InvalidGateway:
return {"base": "auth_error"}
except InvalidHeaterList: except InvalidHeaterList:
return {"base": "no_heaters"} return {"base": "no_heaters"}
except IncomfortError as exc:
if isinstance(exc.message, ClientResponseError):
scope, error = ERROR_STATUS_MAPPING.get(
exc.message.status, ("base", "unknown")
)
return {scope: error}
return {"base": "unknown"}
except TimeoutError: except TimeoutError:
return {"base": "timeout_error"} return {"base": "timeout_error"}
except Exception: # noqa: BLE001 except Exception: # noqa: BLE001

View File

@ -9,7 +9,7 @@ from aiohttp import ClientResponseError
from incomfortclient import ( from incomfortclient import (
Gateway as InComfortGateway, Gateway as InComfortGateway,
Heater as InComfortHeater, Heater as InComfortHeater,
IncomfortError, InvalidHeaterList,
) )
from homeassistant.const import CONF_HOST from homeassistant.const import CONF_HOST
@ -70,9 +70,10 @@ class InComfortDataCoordinator(DataUpdateCoordinator[InComfortData]):
await heater.update() await heater.update()
except TimeoutError as exc: except TimeoutError as exc:
raise UpdateFailed("Timeout error") from exc raise UpdateFailed("Timeout error") from exc
except IncomfortError as exc: except ClientResponseError as exc:
if isinstance(exc.message, ClientResponseError): if exc.status == 401:
if exc.message.status == 401: raise ConfigEntryError("Incorrect credentials") from exc
raise ConfigEntryError("Incorrect credentials") from exc raise UpdateFailed(exc.message) from exc
except InvalidHeaterList as exc:
raise UpdateFailed(exc.message) from exc raise UpdateFailed(exc.message) from exc
return self.incomfort_data return self.incomfort_data

View File

@ -4,7 +4,7 @@ from typing import Any
from unittest.mock import AsyncMock, MagicMock, patch from unittest.mock import AsyncMock, MagicMock, patch
from aiohttp import ClientResponseError from aiohttp import ClientResponseError
from incomfortclient import IncomfortError, InvalidHeaterList from incomfortclient import InvalidGateway, InvalidHeaterList
import pytest import pytest
from homeassistant.components.incomfort.const import DOMAIN from homeassistant.components.incomfort.const import DOMAIN
@ -81,24 +81,22 @@ async def test_entry_already_configured(
("exc", "error", "base"), ("exc", "error", "base"),
[ [
( (
IncomfortError(ClientResponseError(None, None, status=401)), InvalidGateway,
"auth_error", "auth_error",
CONF_PASSWORD,
),
(
IncomfortError(ClientResponseError(None, None, status=404)),
"not_found",
"base", "base",
), ),
( (
IncomfortError(ClientResponseError(None, None, status=500)), InvalidHeaterList,
"no_heaters",
"base",
),
(
ClientResponseError(None, None, status=500),
"unknown", "unknown",
"base", "base",
), ),
(IncomfortError, "unknown", "base"),
(ValueError, "unknown", "base"),
(TimeoutError, "timeout_error", "base"), (TimeoutError, "timeout_error", "base"),
(InvalidHeaterList, "no_heaters", "base"), (ValueError, "unknown", "base"),
], ],
) )
async def test_form_validation( async def test_form_validation(
@ -243,7 +241,7 @@ async def test_dhcp_flow_wih_auth(
with patch.object( with patch.object(
mock_incomfort(), mock_incomfort(),
"heaters", "heaters",
side_effect=IncomfortError(ClientResponseError(None, None, status=401)), side_effect=InvalidGateway,
): ):
result = await hass.config_entries.flow.async_configure( result = await hass.config_entries.flow.async_configure(
result["flow_id"], {CONF_HOST: "192.168.1.12"} result["flow_id"], {CONF_HOST: "192.168.1.12"}
@ -251,7 +249,7 @@ async def test_dhcp_flow_wih_auth(
assert result["type"] is FlowResultType.FORM assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "dhcp_auth" assert result["step_id"] == "dhcp_auth"
assert result["errors"] == {CONF_PASSWORD: "auth_error"} assert result["errors"] == {"base": "auth_error"}
# Submit the form with added credentials # Submit the form with added credentials
result = await hass.config_entries.flow.async_configure( result = await hass.config_entries.flow.async_configure(
@ -300,14 +298,14 @@ async def test_reauth_flow_failure(
with patch.object( with patch.object(
mock_incomfort(), mock_incomfort(),
"heaters", "heaters",
side_effect=IncomfortError(ClientResponseError(None, None, status=401)), side_effect=InvalidGateway,
): ):
result = await hass.config_entries.flow.async_configure( result = await hass.config_entries.flow.async_configure(
result["flow_id"], result["flow_id"],
user_input={CONF_PASSWORD: "incorrect-password"}, user_input={CONF_PASSWORD: "incorrect-password"},
) )
assert result["type"] is FlowResultType.FORM assert result["type"] is FlowResultType.FORM
assert result["errors"] == {CONF_PASSWORD: "auth_error"} assert result["errors"] == {"base": "auth_error"}
result = await hass.config_entries.flow.async_configure( result = await hass.config_entries.flow.async_configure(
result["flow_id"], result["flow_id"],
@ -352,14 +350,14 @@ async def test_reconfigure_flow_failure(
with patch.object( with patch.object(
mock_incomfort(), mock_incomfort(),
"heaters", "heaters",
side_effect=IncomfortError(ClientResponseError(None, None, status=401)), side_effect=InvalidGateway,
): ):
result = await hass.config_entries.flow.async_configure( result = await hass.config_entries.flow.async_configure(
result["flow_id"], result["flow_id"],
user_input=MOCK_CONFIG | {CONF_PASSWORD: "wrong-password"}, user_input=MOCK_CONFIG | {CONF_PASSWORD: "wrong-password"},
) )
assert result["type"] is FlowResultType.FORM assert result["type"] is FlowResultType.FORM
assert result["errors"] == {CONF_PASSWORD: "auth_error"} assert result["errors"] == {"base": "auth_error"}
result = await hass.config_entries.flow.async_configure( result = await hass.config_entries.flow.async_configure(
result["flow_id"], result["flow_id"],

View File

@ -5,10 +5,9 @@ from unittest.mock import AsyncMock, MagicMock, patch
from aiohttp import ClientResponseError, RequestInfo from aiohttp import ClientResponseError, RequestInfo
from freezegun.api import FrozenDateTimeFactory from freezegun.api import FrozenDateTimeFactory
from incomfortclient import IncomfortError from incomfortclient import InvalidGateway, InvalidHeaterList
import pytest import pytest
from homeassistant.components.incomfort import InvalidHeaterList
from homeassistant.components.incomfort.coordinator import UPDATE_INTERVAL from homeassistant.components.incomfort.coordinator import UPDATE_INTERVAL
from homeassistant.config_entries import ConfigEntry, ConfigEntryState from homeassistant.config_entries import ConfigEntry, ConfigEntryState
from homeassistant.const import STATE_UNAVAILABLE from homeassistant.const import STATE_UNAVAILABLE
@ -66,20 +65,27 @@ async def test_coordinator_updates(
@pytest.mark.parametrize( @pytest.mark.parametrize(
"exc", "exc",
[ [
IncomfortError(ClientResponseError(None, None, status=401)), ClientResponseError(
IncomfortError( RequestInfo(
ClientResponseError( url="http://example.com",
RequestInfo( method="GET",
url="http://example.com", headers=[],
method="GET", real_url="http://example.com",
headers=[], ),
real_url="http://example.com", None,
), status=401,
None, ),
status=500, InvalidHeaterList,
) ClientResponseError(
RequestInfo(
url="http://example.com",
method="GET",
headers=[],
real_url="http://example.com",
),
None,
status=500,
), ),
IncomfortError(ValueError("some_error")),
TimeoutError, TimeoutError,
], ],
) )
@ -113,30 +119,36 @@ async def test_coordinator_update_fails(
("exc", "config_entry_state"), ("exc", "config_entry_state"),
[ [
( (
IncomfortError(ClientResponseError(None, None, status=401)), InvalidGateway,
ConfigEntryState.SETUP_ERROR,
),
(
IncomfortError(ClientResponseError(None, None, status=404)),
ConfigEntryState.SETUP_ERROR, ConfigEntryState.SETUP_ERROR,
), ),
(InvalidHeaterList, ConfigEntryState.SETUP_RETRY), (InvalidHeaterList, ConfigEntryState.SETUP_RETRY),
( (
IncomfortError( ClientResponseError(
ClientResponseError( RequestInfo(
RequestInfo( url="http://example.com",
url="http://example.com", method="GET",
method="GET", headers=[],
headers=[], real_url="http://example.com",
real_url="http://example.com", ),
), None,
None, status=404,
status=500, ),
) ConfigEntryState.SETUP_ERROR,
),
(
ClientResponseError(
RequestInfo(
url="http://example.com",
method="GET",
headers=[],
real_url="http://example.com",
),
None,
status=500,
), ),
ConfigEntryState.SETUP_RETRY, ConfigEntryState.SETUP_RETRY,
), ),
(IncomfortError(ValueError("some_error")), ConfigEntryState.SETUP_RETRY),
(TimeoutError, ConfigEntryState.SETUP_RETRY), (TimeoutError, ConfigEntryState.SETUP_RETRY),
], ],
) )