mirror of
https://github.com/home-assistant/core.git
synced 2025-04-25 17:57:55 +00:00
Improve error handling and add exception translations for Nettigo Air Monitor integration (#141183)
* Add update_error * Add device_communication_error * Add auth_error * Add device_communication_action_error * Coverage
This commit is contained in:
parent
ef84fc52af
commit
1f122ea54d
@ -4,7 +4,7 @@ from __future__ import annotations
|
||||
|
||||
import logging
|
||||
|
||||
from aiohttp.client_exceptions import ClientConnectorError, ClientError
|
||||
from aiohttp.client_exceptions import ClientError
|
||||
from nettigo_air_monitor import (
|
||||
ApiError,
|
||||
AuthFailedError,
|
||||
@ -38,15 +38,27 @@ async def async_setup_entry(hass: HomeAssistant, entry: NAMConfigEntry) -> bool:
|
||||
options = ConnectionOptions(host=host, username=username, password=password)
|
||||
try:
|
||||
nam = await NettigoAirMonitor.create(websession, options)
|
||||
except (ApiError, ClientError, ClientConnectorError, TimeoutError) as err:
|
||||
raise ConfigEntryNotReady from err
|
||||
except (ApiError, ClientError) as err:
|
||||
raise ConfigEntryNotReady(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="device_communication_error",
|
||||
translation_placeholders={"device": entry.title},
|
||||
) from err
|
||||
|
||||
try:
|
||||
await nam.async_check_credentials()
|
||||
except ApiError as err:
|
||||
raise ConfigEntryNotReady from err
|
||||
except (ApiError, ClientError) as err:
|
||||
raise ConfigEntryNotReady(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="device_communication_error",
|
||||
translation_placeholders={"device": entry.title},
|
||||
) from err
|
||||
except AuthFailedError as err:
|
||||
raise ConfigEntryAuthFailed from err
|
||||
raise ConfigEntryAuthFailed(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="auth_error",
|
||||
translation_placeholders={"device": entry.title},
|
||||
) from err
|
||||
|
||||
coordinator = NAMDataUpdateCoordinator(hass, entry, nam)
|
||||
await coordinator.async_config_entry_first_refresh()
|
||||
|
@ -4,6 +4,9 @@ from __future__ import annotations
|
||||
|
||||
import logging
|
||||
|
||||
from aiohttp.client_exceptions import ClientError
|
||||
from nettigo_air_monitor import ApiError, AuthFailedError
|
||||
|
||||
from homeassistant.components.button import (
|
||||
ButtonDeviceClass,
|
||||
ButtonEntity,
|
||||
@ -11,9 +14,11 @@ from homeassistant.components.button import (
|
||||
)
|
||||
from homeassistant.const import EntityCategory
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import NAMConfigEntry, NAMDataUpdateCoordinator
|
||||
|
||||
PARALLEL_UPDATES = 1
|
||||
@ -59,4 +64,16 @@ class NAMButton(CoordinatorEntity[NAMDataUpdateCoordinator], ButtonEntity):
|
||||
|
||||
async def async_press(self) -> None:
|
||||
"""Triggers the restart."""
|
||||
await self.coordinator.nam.async_restart()
|
||||
try:
|
||||
await self.coordinator.nam.async_restart()
|
||||
except (ApiError, ClientError) as err:
|
||||
raise HomeAssistantError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="device_communication_action_error",
|
||||
translation_placeholders={
|
||||
"entity": self.entity_id,
|
||||
"device": self.coordinator.config_entry.title,
|
||||
},
|
||||
) from err
|
||||
except AuthFailedError:
|
||||
self.coordinator.config_entry.async_start_reauth(self.hass)
|
||||
|
@ -64,6 +64,10 @@ class NAMDataUpdateCoordinator(DataUpdateCoordinator[NAMSensors]):
|
||||
# We do not need to catch AuthFailed exception here because sensor data is
|
||||
# always available without authorization.
|
||||
except (ApiError, InvalidSensorDataError, RetryError) as error:
|
||||
raise UpdateFailed(error) from error
|
||||
raise UpdateFailed(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="update_error",
|
||||
translation_placeholders={"device": self.config_entry.title},
|
||||
) from error
|
||||
|
||||
return data
|
||||
|
@ -205,5 +205,19 @@
|
||||
"name": "Last restart"
|
||||
}
|
||||
}
|
||||
},
|
||||
"exceptions": {
|
||||
"auth_error": {
|
||||
"message": "Authentication failed for {device}, please update your credentials"
|
||||
},
|
||||
"device_communication_error": {
|
||||
"message": "An error occurred while communicating with {device}"
|
||||
},
|
||||
"device_communication_action_error": {
|
||||
"message": "An error occurred while calling action for {entity} for {device}"
|
||||
},
|
||||
"update_error": {
|
||||
"message": "An error occurred while retrieving data from {device}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,9 +2,20 @@
|
||||
|
||||
from unittest.mock import patch
|
||||
|
||||
from homeassistant.components.button import DOMAIN as BUTTON_DOMAIN, ButtonDeviceClass
|
||||
from aiohttp.client_exceptions import ClientError
|
||||
from nettigo_air_monitor import ApiError, AuthFailedError
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.button import (
|
||||
DOMAIN as BUTTON_DOMAIN,
|
||||
SERVICE_PRESS,
|
||||
ButtonDeviceClass,
|
||||
)
|
||||
from homeassistant.components.nam import DOMAIN
|
||||
from homeassistant.config_entries import SOURCE_REAUTH, ConfigEntryState
|
||||
from homeassistant.const import ATTR_DEVICE_CLASS, ATTR_ENTITY_ID, STATE_UNKNOWN
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
from homeassistant.util import dt as dt_util
|
||||
|
||||
@ -38,7 +49,7 @@ async def test_button_press(hass: HomeAssistant) -> None:
|
||||
):
|
||||
await hass.services.async_call(
|
||||
BUTTON_DOMAIN,
|
||||
"press",
|
||||
SERVICE_PRESS,
|
||||
{ATTR_ENTITY_ID: "button.nettigo_air_monitor_restart"},
|
||||
blocking=True,
|
||||
)
|
||||
@ -49,3 +60,55 @@ async def test_button_press(hass: HomeAssistant) -> None:
|
||||
state = hass.states.get("button.nettigo_air_monitor_restart")
|
||||
assert state
|
||||
assert state.state == now.isoformat()
|
||||
|
||||
|
||||
@pytest.mark.parametrize(("exc"), [ApiError("API Error"), ClientError])
|
||||
async def test_button_press_exc(hass: HomeAssistant, exc: Exception) -> None:
|
||||
"""Test button press when exception occurs."""
|
||||
await init_integration(hass)
|
||||
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.components.nam.NettigoAirMonitor.async_restart",
|
||||
side_effect=exc,
|
||||
),
|
||||
pytest.raises(
|
||||
HomeAssistantError,
|
||||
match="An error occurred while calling action for button.nettigo_air_monitor_restart",
|
||||
),
|
||||
):
|
||||
await hass.services.async_call(
|
||||
BUTTON_DOMAIN,
|
||||
SERVICE_PRESS,
|
||||
{ATTR_ENTITY_ID: "button.nettigo_air_monitor_restart"},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
|
||||
async def test_button_press_auth_error(hass: HomeAssistant) -> None:
|
||||
"""Test button press when auth error occurs."""
|
||||
entry = await init_integration(hass)
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.nam.NettigoAirMonitor.async_restart",
|
||||
side_effect=AuthFailedError("auth error"),
|
||||
):
|
||||
await hass.services.async_call(
|
||||
BUTTON_DOMAIN,
|
||||
SERVICE_PRESS,
|
||||
{ATTR_ENTITY_ID: "button.nettigo_air_monitor_restart"},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
assert entry.state is ConfigEntryState.LOADED
|
||||
|
||||
flows = hass.config_entries.flow.async_progress()
|
||||
assert len(flows) == 1
|
||||
|
||||
flow = flows[0]
|
||||
assert flow.get("step_id") == "reauth_confirm"
|
||||
assert flow.get("handler") == DOMAIN
|
||||
|
||||
assert "context" in flow
|
||||
assert flow["context"].get("source") == SOURCE_REAUTH
|
||||
assert flow["context"].get("entry_id") == entry.entry_id
|
||||
|
Loading…
x
Reference in New Issue
Block a user