Reolink translate errors (#132301)

This commit is contained in:
starkillerOG 2024-12-18 20:22:33 +01:00 committed by GitHub
parent c8f050ecbc
commit 19e6867f1a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 331 additions and 152 deletions

View File

@ -73,7 +73,9 @@ async def async_setup_entry(
) as err:
await host.stop()
raise ConfigEntryNotReady(
f"Error while trying to setup {host.api.host}:{host.api.port}: {err!s}"
translation_domain=DOMAIN,
translation_key="config_entry_not_ready",
translation_placeholders={"host": host.api.host, "err": str(err)},
) from err
except BaseException:
await host.stop()

View File

@ -7,7 +7,6 @@ from dataclasses import dataclass
from typing import Any
from reolink_aio.api import GuardEnum, Host, PtzEnum
from reolink_aio.exceptions import ReolinkError
import voluptuous as vol
from homeassistant.components.button import (
@ -18,7 +17,6 @@ from homeassistant.components.button import (
from homeassistant.components.camera import CameraEntityFeature
from homeassistant.const import EntityCategory
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.entity_platform import (
AddEntitiesCallback,
@ -31,7 +29,7 @@ from .entity import (
ReolinkHostCoordinatorEntity,
ReolinkHostEntityDescription,
)
from .util import ReolinkConfigEntry, ReolinkData
from .util import ReolinkConfigEntry, ReolinkData, raise_translated_error
PARALLEL_UPDATES = 0
ATTR_SPEED = "speed"
@ -205,22 +203,18 @@ class ReolinkButtonEntity(ReolinkChannelCoordinatorEntity, ButtonEntity):
):
self._attr_supported_features = SUPPORT_PTZ_SPEED
@raise_translated_error
async def async_press(self) -> None:
"""Execute the button action."""
try:
await self.entity_description.method(self._host.api, self._channel)
except ReolinkError as err:
raise HomeAssistantError(err) from err
await self.entity_description.method(self._host.api, self._channel)
@raise_translated_error
async def async_ptz_move(self, **kwargs: Any) -> None:
"""PTZ move with speed."""
speed = kwargs[ATTR_SPEED]
try:
await self._host.api.set_ptz_command(
self._channel, command=self.entity_description.ptz_cmd, speed=speed
)
except ReolinkError as err:
raise HomeAssistantError(err) from err
await self._host.api.set_ptz_command(
self._channel, command=self.entity_description.ptz_cmd, speed=speed
)
class ReolinkHostButtonEntity(ReolinkHostCoordinatorEntity, ButtonEntity):
@ -237,9 +231,7 @@ class ReolinkHostButtonEntity(ReolinkHostCoordinatorEntity, ButtonEntity):
self.entity_description = entity_description
super().__init__(reolink_data)
@raise_translated_error
async def async_press(self) -> None:
"""Execute the button action."""
try:
await self.entity_description.method(self._host.api)
except ReolinkError as err:
raise HomeAssistantError(err) from err
await self.entity_description.method(self._host.api)

View File

@ -6,7 +6,6 @@ from dataclasses import dataclass
import logging
from reolink_aio.api import DUAL_LENS_MODELS
from reolink_aio.exceptions import ReolinkError
from homeassistant.components.camera import (
Camera,
@ -14,11 +13,10 @@ from homeassistant.components.camera import (
CameraEntityFeature,
)
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .entity import ReolinkChannelCoordinatorEntity, ReolinkChannelEntityDescription
from .util import ReolinkConfigEntry, ReolinkData
from .util import ReolinkConfigEntry, ReolinkData, raise_translated_error
_LOGGER = logging.getLogger(__name__)
PARALLEL_UPDATES = 0
@ -142,13 +140,11 @@ class ReolinkCamera(ReolinkChannelCoordinatorEntity, Camera):
self._channel, self.entity_description.stream
)
@raise_translated_error
async def async_camera_image(
self, width: int | None = None, height: int | None = None
) -> bytes | None:
"""Return a still image response from the camera."""
try:
return await self._host.api.get_snapshot(
self._channel, self.entity_description.stream
)
except ReolinkError as err:
raise HomeAssistantError(err) from err
return await self._host.api.get_snapshot(
self._channel, self.entity_description.stream
)

View File

@ -7,7 +7,6 @@ from dataclasses import dataclass
from typing import Any
from reolink_aio.api import Host
from reolink_aio.exceptions import InvalidParameterError, ReolinkError
from homeassistant.components.light import (
ATTR_BRIGHTNESS,
@ -17,7 +16,6 @@ from homeassistant.components.light import (
)
from homeassistant.const import EntityCategory
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError, ServiceValidationError
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .entity import (
@ -26,7 +24,7 @@ from .entity import (
ReolinkHostCoordinatorEntity,
ReolinkHostEntityDescription,
)
from .util import ReolinkConfigEntry, ReolinkData
from .util import ReolinkConfigEntry, ReolinkData, raise_translated_error
PARALLEL_UPDATES = 0
@ -154,37 +152,28 @@ class ReolinkLightEntity(ReolinkChannelCoordinatorEntity, LightEntity):
return round(255 * bright_pct / 100.0)
@raise_translated_error
async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn light off."""
try:
await self.entity_description.turn_on_off_fn(
self._host.api, self._channel, False
)
except ReolinkError as err:
raise HomeAssistantError(err) from err
await self.entity_description.turn_on_off_fn(
self._host.api, self._channel, False
)
self.async_write_ha_state()
@raise_translated_error
async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn light on."""
if (
brightness := kwargs.get(ATTR_BRIGHTNESS)
) is not None and self.entity_description.set_brightness_fn is not None:
brightness_pct = int(brightness / 255.0 * 100)
try:
await self.entity_description.set_brightness_fn(
self._host.api, self._channel, brightness_pct
)
except InvalidParameterError as err:
raise ServiceValidationError(err) from err
except ReolinkError as err:
raise HomeAssistantError(err) from err
try:
await self.entity_description.turn_on_off_fn(
self._host.api, self._channel, True
await self.entity_description.set_brightness_fn(
self._host.api, self._channel, brightness_pct
)
except ReolinkError as err:
raise HomeAssistantError(err) from err
await self.entity_description.turn_on_off_fn(
self._host.api, self._channel, True
)
self.async_write_ha_state()
@ -209,18 +198,14 @@ class ReolinkHostLightEntity(ReolinkHostCoordinatorEntity, LightEntity):
"""Return true if light is on."""
return self.entity_description.is_on_fn(self._host.api)
@raise_translated_error
async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn light off."""
try:
await self.entity_description.turn_on_off_fn(self._host.api, False)
except ReolinkError as err:
raise HomeAssistantError(err) from err
await self.entity_description.turn_on_off_fn(self._host.api, False)
self.async_write_ha_state()
@raise_translated_error
async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn light on."""
try:
await self.entity_description.turn_on_off_fn(self._host.api, True)
except ReolinkError as err:
raise HomeAssistantError(err) from err
await self.entity_description.turn_on_off_fn(self._host.api, True)
self.async_write_ha_state()

View File

@ -7,7 +7,6 @@ from dataclasses import dataclass
from typing import Any
from reolink_aio.api import Chime, Host
from reolink_aio.exceptions import InvalidParameterError, ReolinkError
from homeassistant.components.number import (
NumberEntity,
@ -16,7 +15,6 @@ from homeassistant.components.number import (
)
from homeassistant.const import EntityCategory, UnitOfTime
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError, ServiceValidationError
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .entity import (
@ -27,7 +25,7 @@ from .entity import (
ReolinkHostCoordinatorEntity,
ReolinkHostEntityDescription,
)
from .util import ReolinkConfigEntry, ReolinkData
from .util import ReolinkConfigEntry, ReolinkData, raise_translated_error
PARALLEL_UPDATES = 0
@ -589,14 +587,10 @@ class ReolinkNumberEntity(ReolinkChannelCoordinatorEntity, NumberEntity):
"""State of the number entity."""
return self.entity_description.value(self._host.api, self._channel)
@raise_translated_error
async def async_set_native_value(self, value: float) -> None:
"""Update the current value."""
try:
await self.entity_description.method(self._host.api, self._channel, value)
except InvalidParameterError as err:
raise ServiceValidationError(err) from err
except ReolinkError as err:
raise HomeAssistantError(err) from err
await self.entity_description.method(self._host.api, self._channel, value)
self.async_write_ha_state()
@ -621,14 +615,10 @@ class ReolinkHostNumberEntity(ReolinkHostCoordinatorEntity, NumberEntity):
"""State of the number entity."""
return self.entity_description.value(self._host.api)
@raise_translated_error
async def async_set_native_value(self, value: float) -> None:
"""Update the current value."""
try:
await self.entity_description.method(self._host.api, value)
except InvalidParameterError as err:
raise ServiceValidationError(err) from err
except ReolinkError as err:
raise HomeAssistantError(err) from err
await self.entity_description.method(self._host.api, value)
self.async_write_ha_state()
@ -654,12 +644,8 @@ class ReolinkChimeNumberEntity(ReolinkChimeCoordinatorEntity, NumberEntity):
"""State of the number entity."""
return self.entity_description.value(self._chime)
@raise_translated_error
async def async_set_native_value(self, value: float) -> None:
"""Update the current value."""
try:
await self.entity_description.method(self._chime, value)
except InvalidParameterError as err:
raise ServiceValidationError(err) from err
except ReolinkError as err:
raise HomeAssistantError(err) from err
await self.entity_description.method(self._chime, value)
self.async_write_ha_state()

View File

@ -54,7 +54,7 @@ rules:
entity-device-class: done
entity-disabled-by-default: done
entity-translations: done
exception-translations: todo
exception-translations: done
icon-translations: done
reconfiguration-flow: done
repair-issues: done

View File

@ -19,12 +19,10 @@ from reolink_aio.api import (
StatusLedEnum,
TrackMethodEnum,
)
from reolink_aio.exceptions import InvalidParameterError, ReolinkError
from homeassistant.components.select import SelectEntity, SelectEntityDescription
from homeassistant.const import EntityCategory, UnitOfDataRate, UnitOfFrequency
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError, ServiceValidationError
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .entity import (
@ -33,7 +31,7 @@ from .entity import (
ReolinkChimeCoordinatorEntity,
ReolinkChimeEntityDescription,
)
from .util import ReolinkConfigEntry, ReolinkData
from .util import ReolinkConfigEntry, ReolinkData, raise_translated_error
_LOGGER = logging.getLogger(__name__)
PARALLEL_UPDATES = 0
@ -354,14 +352,10 @@ class ReolinkSelectEntity(ReolinkChannelCoordinatorEntity, SelectEntity):
self._log_error = True
return option
@raise_translated_error
async def async_select_option(self, option: str) -> None:
"""Change the selected option."""
try:
await self.entity_description.method(self._host.api, self._channel, option)
except InvalidParameterError as err:
raise ServiceValidationError(err) from err
except ReolinkError as err:
raise HomeAssistantError(err) from err
await self.entity_description.method(self._host.api, self._channel, option)
self.async_write_ha_state()
@ -396,12 +390,8 @@ class ReolinkChimeSelectEntity(ReolinkChimeCoordinatorEntity, SelectEntity):
self._log_error = True
return option
@raise_translated_error
async def async_select_option(self, option: str) -> None:
"""Change the selected option."""
try:
await self.entity_description.method(self._chime, option)
except InvalidParameterError as err:
raise ServiceValidationError(err) from err
except ReolinkError as err:
raise HomeAssistantError(err) from err
await self.entity_description.method(self._chime, option)
self.async_write_ha_state()

View File

@ -4,18 +4,17 @@ from __future__ import annotations
from reolink_aio.api import Chime
from reolink_aio.enums import ChimeToneEnum
from reolink_aio.exceptions import InvalidParameterError, ReolinkError
import voluptuous as vol
from homeassistant.config_entries import ConfigEntryState
from homeassistant.const import ATTR_DEVICE_ID
from homeassistant.core import HomeAssistant, ServiceCall, callback
from homeassistant.exceptions import HomeAssistantError, ServiceValidationError
from homeassistant.exceptions import ServiceValidationError
from homeassistant.helpers import device_registry as dr
from .const import DOMAIN
from .host import ReolinkHost
from .util import get_device_uid_and_ch
from .util import get_device_uid_and_ch, raise_translated_error
ATTR_RINGTONE = "ringtone"
@ -24,6 +23,7 @@ ATTR_RINGTONE = "ringtone"
def async_setup_services(hass: HomeAssistant) -> None:
"""Set up Reolink services."""
@raise_translated_error
async def async_play_chime(service_call: ServiceCall) -> None:
"""Play a ringtone."""
service_data = service_call.data
@ -58,12 +58,7 @@ def async_setup_services(hass: HomeAssistant) -> None:
)
ringtone = service_data[ATTR_RINGTONE]
try:
await chime.play(ChimeToneEnum[ringtone].value)
except InvalidParameterError as err:
raise ServiceValidationError(err) from err
except ReolinkError as err:
raise HomeAssistantError(err) from err
await chime.play(ChimeToneEnum[ringtone].value)
hass.services.async_register(
DOMAIN,

View File

@ -5,8 +5,6 @@ from __future__ import annotations
from dataclasses import dataclass
from typing import Any
from reolink_aio.exceptions import InvalidParameterError, ReolinkError
from homeassistant.components.siren import (
ATTR_DURATION,
ATTR_VOLUME_LEVEL,
@ -15,11 +13,10 @@ from homeassistant.components.siren import (
SirenEntityFeature,
)
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError, ServiceValidationError
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .entity import ReolinkChannelCoordinatorEntity, ReolinkChannelEntityDescription
from .util import ReolinkConfigEntry, ReolinkData
from .util import ReolinkConfigEntry, ReolinkData, raise_translated_error
PARALLEL_UPDATES = 0
@ -77,26 +74,15 @@ class ReolinkSirenEntity(ReolinkChannelCoordinatorEntity, SirenEntity):
self.entity_description = entity_description
super().__init__(reolink_data, channel)
@raise_translated_error
async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn on the siren."""
if (volume := kwargs.get(ATTR_VOLUME_LEVEL)) is not None:
try:
await self._host.api.set_volume(self._channel, int(volume * 100))
except InvalidParameterError as err:
raise ServiceValidationError(err) from err
except ReolinkError as err:
raise HomeAssistantError(err) from err
await self._host.api.set_volume(self._channel, int(volume * 100))
duration = kwargs.get(ATTR_DURATION)
try:
await self._host.api.set_siren(self._channel, True, duration)
except InvalidParameterError as err:
raise ServiceValidationError(err) from err
except ReolinkError as err:
raise HomeAssistantError(err) from err
await self._host.api.set_siren(self._channel, True, duration)
@raise_translated_error
async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn off the siren."""
try:
await self._host.api.set_siren(self._channel, False, None)
except ReolinkError as err:
raise HomeAssistantError(err) from err
await self._host.api.set_siren(self._channel, False, None)

View File

@ -55,6 +55,45 @@
},
"service_not_chime": {
"message": "Reolink play_chime error: {device_name} is not a chime"
},
"invalid_parameter": {
"message": "Invalid input parameter: {err}"
},
"api_error": {
"message": "The device responded with a error: {err}"
},
"invalid_content_type": {
"message": "Received a different content type than expected: {err}"
},
"invalid_credentials": {
"message": "Invalid credentials: {err}"
},
"login_error": {
"message": "Error during login attempt: {err}"
},
"no_data": {
"message": "Device returned no data: {err}"
},
"unexpected_data": {
"message": "Device returned unexpected data: {err}"
},
"not_supported": {
"message": "Function not supported by this device: {err}"
},
"subscription_error": {
"message": "Error during ONVIF subscription: {err}"
},
"connection_error": {
"message": "Could not connect to the device: {err}"
},
"timeout": {
"message": "Timeout waiting on a response: {err}"
},
"firmware_install_error": {
"message": "Error trying to update Reolink firmware: {err}"
},
"config_entry_not_ready": {
"message": "Error while trying to setup {host}: {err}"
}
},
"issues": {

View File

@ -7,12 +7,10 @@ from dataclasses import dataclass
from typing import Any
from reolink_aio.api import Chime, Host
from reolink_aio.exceptions import ReolinkError
from homeassistant.components.switch import SwitchEntity, SwitchEntityDescription
from homeassistant.const import EntityCategory
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import entity_registry as er, issue_registry as ir
from homeassistant.helpers.entity_platform import AddEntitiesCallback
@ -25,7 +23,7 @@ from .entity import (
ReolinkHostCoordinatorEntity,
ReolinkHostEntityDescription,
)
from .util import ReolinkConfigEntry, ReolinkData
from .util import ReolinkConfigEntry, ReolinkData, raise_translated_error
PARALLEL_UPDATES = 0
@ -430,20 +428,16 @@ class ReolinkSwitchEntity(ReolinkChannelCoordinatorEntity, SwitchEntity):
"""Return true if switch is on."""
return self.entity_description.value(self._host.api, self._channel)
@raise_translated_error
async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn the entity on."""
try:
await self.entity_description.method(self._host.api, self._channel, True)
except ReolinkError as err:
raise HomeAssistantError(err) from err
await self.entity_description.method(self._host.api, self._channel, True)
self.async_write_ha_state()
@raise_translated_error
async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn the entity off."""
try:
await self.entity_description.method(self._host.api, self._channel, False)
except ReolinkError as err:
raise HomeAssistantError(err) from err
await self.entity_description.method(self._host.api, self._channel, False)
self.async_write_ha_state()
@ -466,20 +460,16 @@ class ReolinkNVRSwitchEntity(ReolinkHostCoordinatorEntity, SwitchEntity):
"""Return true if switch is on."""
return self.entity_description.value(self._host.api)
@raise_translated_error
async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn the entity on."""
try:
await self.entity_description.method(self._host.api, True)
except ReolinkError as err:
raise HomeAssistantError(err) from err
await self.entity_description.method(self._host.api, True)
self.async_write_ha_state()
@raise_translated_error
async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn the entity off."""
try:
await self.entity_description.method(self._host.api, False)
except ReolinkError as err:
raise HomeAssistantError(err) from err
await self.entity_description.method(self._host.api, False)
self.async_write_ha_state()
@ -503,18 +493,14 @@ class ReolinkChimeSwitchEntity(ReolinkChimeCoordinatorEntity, SwitchEntity):
"""Return true if switch is on."""
return self.entity_description.value(self._chime)
@raise_translated_error
async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn the entity on."""
try:
await self.entity_description.method(self._chime, True)
except ReolinkError as err:
raise HomeAssistantError(err) from err
await self.entity_description.method(self._chime, True)
self.async_write_ha_state()
@raise_translated_error
async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn the entity off."""
try:
await self.entity_description.method(self._chime, False)
except ReolinkError as err:
raise HomeAssistantError(err) from err
await self.entity_description.method(self._chime, False)
self.async_write_ha_state()

View File

@ -24,6 +24,7 @@ from homeassistant.helpers.update_coordinator import (
)
from . import DEVICE_UPDATE_INTERVAL
from .const import DOMAIN
from .entity import (
ReolinkChannelCoordinatorEntity,
ReolinkChannelEntityDescription,
@ -196,7 +197,9 @@ class ReolinkUpdateBaseEntity(
await self._host.api.update_firmware(self._channel)
except ReolinkError as err:
raise HomeAssistantError(
f"Error trying to update Reolink firmware: {err}"
translation_domain=DOMAIN,
translation_key="firmware_install_error",
translation_placeholders={"err": str(err)},
) from err
finally:
self.async_write_ha_state()

View File

@ -2,10 +2,28 @@
from __future__ import annotations
from collections.abc import Awaitable, Callable, Coroutine
from dataclasses import dataclass
from typing import Any, ParamSpec, TypeVar
from reolink_aio.exceptions import (
ApiError,
CredentialsInvalidError,
InvalidContentTypeError,
InvalidParameterError,
LoginError,
NoDataError,
NotSupportedError,
ReolinkConnectionError,
ReolinkError,
ReolinkTimeoutError,
SubscriptionError,
UnexpectedDataError,
)
from homeassistant import config_entries
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError, ServiceValidationError
from homeassistant.helpers import device_registry as dr
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
@ -53,3 +71,89 @@ def get_device_uid_and_ch(
else:
ch = host.api.channel_for_uid(device_uid[1])
return (device_uid, ch, is_chime)
T = TypeVar("T")
P = ParamSpec("P")
# Decorators
def raise_translated_error(
func: Callable[P, Awaitable[T]],
) -> Callable[P, Coroutine[Any, Any, T]]:
"""Wrap a reolink-aio function to translate any potential errors."""
async def decorator_raise_translated_error(*args: P.args, **kwargs: P.kwargs) -> T:
"""Try a reolink-aio function and translate any potential errors."""
try:
return await func(*args, **kwargs)
except InvalidParameterError as err:
raise ServiceValidationError(
translation_domain=DOMAIN,
translation_key="invalid_parameter",
translation_placeholders={"err": str(err)},
) from err
except ApiError as err:
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="api_error",
translation_placeholders={"err": str(err)},
) from err
except InvalidContentTypeError as err:
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="invalid_content_type",
translation_placeholders={"err": str(err)},
) from err
except CredentialsInvalidError as err:
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="invalid_credentials",
translation_placeholders={"err": str(err)},
) from err
except LoginError as err:
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="login_error",
translation_placeholders={"err": str(err)},
) from err
except NoDataError as err:
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="no_data",
translation_placeholders={"err": str(err)},
) from err
except UnexpectedDataError as err:
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="unexpected_data",
translation_placeholders={"err": str(err)},
) from err
except NotSupportedError as err:
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="not_supported",
translation_placeholders={"err": str(err)},
) from err
except SubscriptionError as err:
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="subscription_error",
translation_placeholders={"err": str(err)},
) from err
except ReolinkConnectionError as err:
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="connection_error",
translation_placeholders={"err": str(err)},
) from err
except ReolinkTimeoutError as err:
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="timeout",
translation_placeholders={"err": str(err)},
) from err
except ReolinkError as err:
raise HomeAssistantError(err) from err
return decorator_raise_translated_error

View File

@ -0,0 +1,115 @@
"""Test the Reolink util functions."""
from unittest.mock import MagicMock, patch
import pytest
from reolink_aio.exceptions import (
ApiError,
CredentialsInvalidError,
InvalidContentTypeError,
InvalidParameterError,
LoginError,
NoDataError,
NotSupportedError,
ReolinkConnectionError,
ReolinkError,
ReolinkTimeoutError,
SubscriptionError,
UnexpectedDataError,
)
from homeassistant.components.number import (
ATTR_VALUE,
DOMAIN as NUMBER_DOMAIN,
SERVICE_SET_VALUE,
)
from homeassistant.config_entries import ConfigEntryState
from homeassistant.const import ATTR_ENTITY_ID, Platform
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError, ServiceValidationError
from .conftest import TEST_NVR_NAME
from tests.common import MockConfigEntry
@pytest.mark.parametrize(
("side_effect", "expected"),
[
(
ApiError("Test error"),
HomeAssistantError,
),
(
CredentialsInvalidError("Test error"),
HomeAssistantError,
),
(
InvalidContentTypeError("Test error"),
HomeAssistantError,
),
(
InvalidParameterError("Test error"),
ServiceValidationError,
),
(
LoginError("Test error"),
HomeAssistantError,
),
(
NoDataError("Test error"),
HomeAssistantError,
),
(
NotSupportedError("Test error"),
HomeAssistantError,
),
(
ReolinkConnectionError("Test error"),
HomeAssistantError,
),
(
ReolinkError("Test error"),
HomeAssistantError,
),
(
ReolinkTimeoutError("Test error"),
HomeAssistantError,
),
(
SubscriptionError("Test error"),
HomeAssistantError,
),
(
UnexpectedDataError("Test error"),
HomeAssistantError,
),
],
)
async def test_try_function(
hass: HomeAssistant,
config_entry: MockConfigEntry,
reolink_connect: MagicMock,
side_effect: ReolinkError,
expected: Exception,
) -> None:
"""Test try_function error translations using number entity."""
reolink_connect.volume.return_value = 80
with patch("homeassistant.components.reolink.PLATFORMS", [Platform.NUMBER]):
assert await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()
assert config_entry.state is ConfigEntryState.LOADED
entity_id = f"{Platform.NUMBER}.{TEST_NVR_NAME}_volume"
reolink_connect.set_volume.side_effect = side_effect
with pytest.raises(expected):
await hass.services.async_call(
NUMBER_DOMAIN,
SERVICE_SET_VALUE,
{ATTR_ENTITY_ID: entity_id, ATTR_VALUE: 50},
blocking=True,
)
reolink_connect.set_volume.reset_mock(side_effect=True)