Bump reolink-aio to 0.12.3 (#140789)

* Add password length restriction

* Bump reolink-aio to 0.12.3

* Add repair issue for too long password

* finish password too long repair issue

* add test
This commit is contained in:
starkillerOG 2025-03-20 11:39:57 +01:00 committed by GitHub
parent d3c40939f6
commit a20601a1f0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 82 additions and 15 deletions

View File

@ -67,9 +67,7 @@ async def async_setup_entry(
hass: HomeAssistant, config_entry: ReolinkConfigEntry
) -> bool:
"""Set up Reolink from a config entry."""
host = ReolinkHost(
hass, config_entry.data, config_entry.options, config_entry.entry_id
)
host = ReolinkHost(hass, config_entry.data, config_entry.options, config_entry)
try:
await host.async_init()

View File

@ -41,7 +41,7 @@ from .exceptions import (
ReolinkWebhookException,
UserNotAdmin,
)
from .util import get_store
from .util import ReolinkConfigEntry, get_store
DEFAULT_TIMEOUT = 30
FIRST_TCP_PUSH_TIMEOUT = 10
@ -67,11 +67,11 @@ class ReolinkHost:
hass: HomeAssistant,
config: Mapping[str, Any],
options: Mapping[str, Any],
config_entry_id: str | None = None,
config_entry: ReolinkConfigEntry | None = None,
) -> None:
"""Initialize Reolink Host. Could be either NVR, or Camera."""
self._hass: HomeAssistant = hass
self._config_entry_id = config_entry_id
self._config_entry = config_entry
self._config = config
self._unique_id: str = ""
@ -151,15 +151,33 @@ class ReolinkHost:
async def async_init(self) -> None:
"""Connect to Reolink host."""
if not self._api.valid_password():
if (
len(self._config[CONF_PASSWORD]) >= 32
and self._config_entry is not None
):
ir.async_create_issue(
self._hass,
DOMAIN,
f"password_too_long_{self._config_entry.entry_id}",
is_fixable=True,
severity=ir.IssueSeverity.ERROR,
translation_key="password_too_long",
translation_placeholders={"name": self._config_entry.title},
)
raise PasswordIncompatible(
"Reolink password contains incompatible special character, "
"please change the password to only contain characters: "
f"a-z, A-Z, 0-9 or {ALLOWED_SPECIAL_CHARS}"
"Reolink password contains incompatible special character or "
"is too long, please change the password to only contain characters: "
f"a-z, A-Z, 0-9 or {ALLOWED_SPECIAL_CHARS} "
"and not be longer than 31 characters"
)
store: Store[str] | None = None
if self._config_entry_id is not None:
store = get_store(self._hass, self._config_entry_id)
if self._config_entry is not None:
ir.async_delete_issue(
self._hass, DOMAIN, f"password_too_long_{self._config_entry.entry_id}"
)
store = get_store(self._hass, self._config_entry.entry_id)
if self._config.get(CONF_SUPPORTS_PRIVACY_MODE) and (
data := await store.async_load()
):

View File

@ -19,5 +19,5 @@
"iot_class": "local_push",
"loggers": ["reolink_aio"],
"quality_scale": "platinum",
"requirements": ["reolink-aio==0.12.3b1"]
"requirements": ["reolink-aio==0.12.3"]
}

View File

@ -31,7 +31,7 @@
"cannot_connect": "Failed to connect, check the IP address of the camera",
"invalid_auth": "[%key:common::config_flow::error::invalid_auth%]",
"not_admin": "User needs to be admin, user \"{username}\" has authorisation level \"{userlevel}\"",
"password_incompatible": "Password contains incompatible special character, only these characters are allowed: a-z, A-Z, 0-9 or {special_chars}",
"password_incompatible": "Password contains incompatible special character or is too long, maximum 31 characters and only these characters are allowed: a-z, A-Z, 0-9 or {special_chars}",
"unknown": "[%key:common::config_flow::error::unknown%]",
"update_needed": "Failed to login because of outdated firmware, please update the firmware to version {needed_firmware} using the Reolink Download Center: {download_center_url}, currently version {current_firmware} is installed",
"webhook_exception": "Home Assistant URL is not available, go to Settings > System > Network > Home Assistant URL and correct the URLs, see {more_info}"
@ -129,6 +129,10 @@
"hub_switch_deprecated": {
"title": "Reolink Home Hub switches deprecated",
"description": "The redundant 'Record', 'Email on event', 'FTP upload', 'Push notifications', and 'Buzzer on event' switches on the Reolink Home Hub are deprecated since the new firmware no longer supports these. Please use the equally named switches under each of the camera devices connected to the Home Hub instead. To remove this issue, please adjust automations accordingly and disable the switch entities mentioned."
},
"password_too_long": {
"title": "Reolink password too long",
"description": "The password for \"{name}\" is more than 31 characters long, this is no longer compatible with the Reolink API. Please change the password using the Reolink app/client to a password with is shorter than 32 characters. After changing the password, fill in the new password in the Reolink Re-authentication flow to continue using this integration. The latest version of the Reolink app/client also has a password limit of 31 characters."
}
},
"services": {

2
requirements_all.txt generated
View File

@ -2622,7 +2622,7 @@ renault-api==0.2.9
renson-endura-delta==1.7.2
# homeassistant.components.reolink
reolink-aio==0.12.3b1
reolink-aio==0.12.3
# homeassistant.components.idteck_prox
rfk101py==0.0.1

View File

@ -2122,7 +2122,7 @@ renault-api==0.2.9
renson-endura-delta==1.7.2
# homeassistant.components.reolink
reolink-aio==0.12.3b1
reolink-aio==0.12.3
# homeassistant.components.rflink
rflink==0.0.66

View File

@ -22,7 +22,11 @@ from homeassistant.components.reolink import (
from homeassistant.components.reolink.const import CONF_BC_PORT, DOMAIN
from homeassistant.config_entries import ConfigEntryState
from homeassistant.const import (
CONF_HOST,
CONF_PASSWORD,
CONF_PORT,
CONF_PROTOCOL,
CONF_USERNAME,
STATE_OFF,
STATE_ON,
STATE_UNAVAILABLE,
@ -35,17 +39,25 @@ from homeassistant.helpers import (
entity_registry as er,
issue_registry as ir,
)
from homeassistant.helpers.device_registry import format_mac
from homeassistant.setup import async_setup_component
from .conftest import (
CONF_SUPPORTS_PRIVACY_MODE,
CONF_USE_HTTPS,
DEFAULT_PROTOCOL,
TEST_BC_PORT,
TEST_CAM_MODEL,
TEST_HOST,
TEST_HOST_MODEL,
TEST_MAC,
TEST_NVR_NAME,
TEST_PORT,
TEST_PRIVACY,
TEST_UID,
TEST_UID_CAM,
TEST_USE_HTTPS,
TEST_USERNAME,
)
from tests.common import MockConfigEntry, async_fire_time_changed
@ -723,6 +735,41 @@ async def test_firmware_repair_issue(
await hass.async_block_till_done()
assert (DOMAIN, "firmware_update_host") in issue_registry.issues
reolink_connect.camera_sw_version_update_required.return_value = False
async def test_password_too_long_repair_issue(
hass: HomeAssistant,
reolink_connect: MagicMock,
issue_registry: ir.IssueRegistry,
) -> None:
"""Test password too long issue is raised."""
reolink_connect.valid_password.return_value = False
config_entry = MockConfigEntry(
domain=DOMAIN,
unique_id=format_mac(TEST_MAC),
data={
CONF_HOST: TEST_HOST,
CONF_USERNAME: TEST_USERNAME,
CONF_PASSWORD: "too_longgggggggggggggggggggggggggggggggggggggggggggggggggg",
CONF_PORT: TEST_PORT,
CONF_USE_HTTPS: TEST_USE_HTTPS,
CONF_SUPPORTS_PRIVACY_MODE: TEST_PRIVACY,
},
options={
CONF_PROTOCOL: DEFAULT_PROTOCOL,
},
title=TEST_NVR_NAME,
)
config_entry.add_to_hass(hass)
assert not await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()
assert (
DOMAIN,
f"password_too_long_{config_entry.entry_id}",
) in issue_registry.issues
reolink_connect.valid_password.return_value = True
async def test_new_device_discovered(