Reolink translate key (#140821)

* Add firmware exception translations

* Add test

* Much nicer syntax

* Check if translation key is present in string.json

* fix tests

* fix typo
This commit is contained in:
starkillerOG 2025-03-26 00:30:02 +01:00 committed by GitHub
parent 07bce8850f
commit e78a19ae3e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 53 additions and 14 deletions

View File

@ -103,6 +103,12 @@
},
"config_entry_not_ready": {
"message": "Error while trying to set up {host}: {err}"
},
"update_already_running": {
"message": "Reolink firmware update already running, wait on completion before starting another"
},
"firmware_rate_limit": {
"message": "Reolink firmware update server reached hourly rate limit: updating can be tried again in 1 hour"
}
},
"issues": {

View File

@ -31,7 +31,7 @@ from .entity import (
ReolinkHostCoordinatorEntity,
ReolinkHostEntityDescription,
)
from .util import ReolinkConfigEntry, ReolinkData
from .util import ReolinkConfigEntry, ReolinkData, raise_translated_error
PARALLEL_UPDATES = 0
RESUME_AFTER_INSTALL = 15
@ -184,6 +184,7 @@ class ReolinkUpdateBaseEntity(
f"## Release notes\n\n{new_firmware.release_notes}"
)
@raise_translated_error
async def async_install(
self, version: str | None, backup: bool, **kwargs: Any
) -> None:
@ -196,6 +197,8 @@ class ReolinkUpdateBaseEntity(
try:
await self._host.api.update_firmware(self._channel)
except ReolinkError as err:
if err.translation_key:
raise
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="firmware_install_error",

View File

@ -27,6 +27,7 @@ from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError, ServiceValidationError
from homeassistant.helpers import device_registry as dr
from homeassistant.helpers.storage import Store
from homeassistant.helpers.translation import async_get_exception_message
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
from .const import DOMAIN
@ -97,6 +98,16 @@ def get_device_uid_and_ch(
return (device_uid, ch, is_chime)
def check_translation_key(err: ReolinkError) -> str | None:
"""Check if the translation key from the upstream library is present."""
if not err.translation_key:
return None
if async_get_exception_message(DOMAIN, err.translation_key) == err.translation_key:
# translation key not found in strings.json
return None
return err.translation_key
# Decorators
def raise_translated_error[**P, R](
func: Callable[P, Awaitable[R]],
@ -110,73 +121,73 @@ def raise_translated_error[**P, R](
except InvalidParameterError as err:
raise ServiceValidationError(
translation_domain=DOMAIN,
translation_key="invalid_parameter",
translation_key=check_translation_key(err) or "invalid_parameter",
translation_placeholders={"err": str(err)},
) from err
except ApiError as err:
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="api_error",
translation_key=check_translation_key(err) or "api_error",
translation_placeholders={"err": str(err)},
) from err
except InvalidContentTypeError as err:
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="invalid_content_type",
translation_key=check_translation_key(err) or "invalid_content_type",
translation_placeholders={"err": str(err)},
) from err
except CredentialsInvalidError as err:
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="invalid_credentials",
translation_key=check_translation_key(err) or "invalid_credentials",
translation_placeholders={"err": str(err)},
) from err
except LoginError as err:
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="login_error",
translation_key=check_translation_key(err) or "login_error",
translation_placeholders={"err": str(err)},
) from err
except NoDataError as err:
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="no_data",
translation_key=check_translation_key(err) or "no_data",
translation_placeholders={"err": str(err)},
) from err
except UnexpectedDataError as err:
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="unexpected_data",
translation_key=check_translation_key(err) or "unexpected_data",
translation_placeholders={"err": str(err)},
) from err
except NotSupportedError as err:
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="not_supported",
translation_key=check_translation_key(err) or "not_supported",
translation_placeholders={"err": str(err)},
) from err
except SubscriptionError as err:
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="subscription_error",
translation_key=check_translation_key(err) or "subscription_error",
translation_placeholders={"err": str(err)},
) from err
except ReolinkConnectionError as err:
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="connection_error",
translation_key=check_translation_key(err) or "connection_error",
translation_placeholders={"err": str(err)},
) from err
except ReolinkTimeoutError as err:
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="timeout",
translation_key=check_translation_key(err) or "timeout",
translation_placeholders={"err": str(err)},
) from err
except ReolinkError as err:
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="unexpected",
translation_key=check_translation_key(err) or "unexpected",
translation_placeholders={"err": str(err)},
) from err

View File

@ -6,7 +6,7 @@ from unittest.mock import MagicMock, patch
from freezegun.api import FrozenDateTimeFactory
import pytest
from reolink_aio.exceptions import ReolinkError
from reolink_aio.exceptions import ApiError, ReolinkError
from reolink_aio.software_version import NewSoftwareVersion
from homeassistant.components.reolink.update import POLL_AFTER_INSTALL, POLL_PROGRESS
@ -144,6 +144,17 @@ async def test_update_firm(
blocking=True,
)
reolink_connect.update_firmware.side_effect = ApiError(
"Test error", translation_key="firmware_rate_limit"
)
with pytest.raises(HomeAssistantError):
await hass.services.async_call(
UPDATE_DOMAIN,
SERVICE_INSTALL,
{ATTR_ENTITY_ID: entity_id},
blocking=True,
)
# test _async_update_future
reolink_connect.camera_sw_version.return_value = "v3.3.0.226_23031644"
reolink_connect.firmware_update_available.return_value = False

View File

@ -40,6 +40,14 @@ from tests.common import MockConfigEntry
ApiError("Test error"),
HomeAssistantError,
),
(
ApiError("Test error", translation_key="firmware_rate_limit"),
HomeAssistantError,
),
(
ApiError("Test error", translation_key="not_in_strings.json"),
HomeAssistantError,
),
(
CredentialsInvalidError("Test error"),
HomeAssistantError,