Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reolink translate key #140821

Merged
merged 8 commits into from
Mar 25, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions homeassistant/components/reolink/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand Down
5 changes: 4 additions & 1 deletion homeassistant/components/reolink/update.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
ReolinkHostCoordinatorEntity,
ReolinkHostEntityDescription,
)
from .util import ReolinkConfigEntry, ReolinkData
from .util import ReolinkConfigEntry, ReolinkData, raise_translated_error

PARALLEL_UPDATES = 0
RESUME_AFTER_INSTALL = 15
Expand Down Expand Up @@ -184,6 +184,7 @@ async def async_release_notes(self) -> str | None:
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:
Expand All @@ -196,6 +197,8 @@ async def async_install(
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",
Expand Down
35 changes: 23 additions & 12 deletions homeassistant/components/reolink/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
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
Expand Down Expand Up @@ -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]],
Expand All @@ -110,73 +121,73 @@ async def decorator_raise_translated_error(*args: P.args, **kwargs: P.kwargs) ->
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

Expand Down
13 changes: 12 additions & 1 deletion tests/components/reolink/test_update.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

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
Expand Down Expand Up @@ -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
Expand Down
8 changes: 8 additions & 0 deletions tests/components/reolink/test_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,14 @@
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,
Expand Down
Loading