1
0
mirror of https://github.com/home-assistant/core.git synced 2026-05-08 17:49:37 +01:00

Do not check Reolink firmware at start (#158275)

This commit is contained in:
starkillerOG
2025-12-16 13:27:09 +01:00
committed by GitHub
parent 7eecdc87fd
commit 412ee30584
4 changed files with 94 additions and 7 deletions
+36 -6
View File
@@ -4,8 +4,9 @@ from __future__ import annotations
import asyncio
from collections.abc import Callable
from datetime import timedelta
from datetime import UTC, datetime, timedelta
import logging
from random import uniform
from time import time
from typing import Any
@@ -34,6 +35,7 @@ from .const import (
BATTERY_PASSIVE_WAKE_UPDATE_INTERVAL,
CONF_BC_ONLY,
CONF_BC_PORT,
CONF_FIRMWARE_CHECK_TIME,
CONF_SUPPORTS_PRIVACY_MODE,
CONF_USE_HTTPS,
DOMAIN,
@@ -212,15 +214,41 @@ async def async_setup_entry(
config_entry=config_entry,
name=f"reolink.{host.api.nvr_name}.firmware",
update_method=async_check_firmware_update,
update_interval=FIRMWARE_UPDATE_INTERVAL,
update_interval=None, # Do not fetch data automatically, resume 24h schedule
)
async def first_firmware_check(*args: Any) -> None:
"""Start first firmware check delayed to continue 24h schedule."""
firmware_coordinator.update_interval = FIRMWARE_UPDATE_INTERVAL
await firmware_coordinator.async_refresh()
host.cancel_first_firmware_check = None
# get update time from config entry
check_time_sec = config_entry.data.get(CONF_FIRMWARE_CHECK_TIME)
if check_time_sec is None:
check_time_sec = uniform(0, 86400)
data = {
**config_entry.data,
CONF_FIRMWARE_CHECK_TIME: check_time_sec,
}
hass.config_entries.async_update_entry(config_entry, data=data)
# If camera WAN blocked, firmware check fails and takes long, do not prevent setup
config_entry.async_create_background_task(
hass,
firmware_coordinator.async_refresh(),
f"Reolink firmware check {config_entry.entry_id}",
now = datetime.now(UTC)
check_time = timedelta(seconds=check_time_sec)
delta_midnight = now - now.replace(hour=0, minute=0, second=0, microsecond=0)
firmware_check_delay = check_time - delta_midnight
if firmware_check_delay < timedelta(0):
firmware_check_delay += timedelta(days=1)
_LOGGER.debug(
"Scheduling first Reolink %s firmware check in %s",
host.api.nvr_name,
firmware_check_delay,
)
host.cancel_first_firmware_check = async_call_later(
hass, firmware_check_delay, first_firmware_check
)
# Fetch initial data so we have data when entities subscribe
try:
await device_coordinator.async_config_entry_first_refresh()
@@ -312,6 +340,8 @@ async def async_unload_entry(
host.api.baichuan.unregister_callback(f"camera_{channel}_wake")
if host.cancel_refresh_privacy_mode is not None:
host.cancel_refresh_privacy_mode()
if host.cancel_first_firmware_check is not None:
host.cancel_first_firmware_check()
return await hass.config_entries.async_unload_platforms(config_entry, PLATFORMS)
@@ -6,6 +6,7 @@ CONF_USE_HTTPS = "use_https"
CONF_BC_PORT = "baichuan_port"
CONF_BC_ONLY = "baichuan_only"
CONF_SUPPORTS_PRIVACY_MODE = "privacy_mode_supported"
CONF_FIRMWARE_CHECK_TIME = "firmware_check_time"
# Conserve battery by not waking the battery cameras each minute during normal update
# Most props are cached in the Home Hub and updated, but some are skipped
+1
View File
@@ -130,6 +130,7 @@ class ReolinkHost:
self._lost_subscription_start: bool = False
self._lost_subscription: bool = False
self.cancel_refresh_privacy_mode: CALLBACK_TYPE | None = None
self.cancel_first_firmware_check: CALLBACK_TYPE | None = None
@callback
def async_register_update_cmd(self, cmd: str, channel: int | None = None) -> None:
+56 -1
View File
@@ -2,6 +2,7 @@
import asyncio
from collections.abc import Callable
from datetime import UTC, datetime, timedelta
from typing import Any
from unittest.mock import AsyncMock, MagicMock, Mock, patch
@@ -22,6 +23,7 @@ from homeassistant.components.reolink.const import (
BATTERY_ALL_WAKE_UPDATE_INTERVAL,
BATTERY_PASSIVE_WAKE_UPDATE_INTERVAL,
CONF_BC_PORT,
CONF_FIRMWARE_CHECK_TIME,
DOMAIN,
)
from homeassistant.config_entries import ConfigEntryState
@@ -47,6 +49,7 @@ from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC, format
from homeassistant.setup import async_setup_component
from .conftest import (
CONF_BC_ONLY,
CONF_SUPPORTS_PRIVACY_MODE,
CONF_USE_HTTPS,
DEFAULT_PROTOCOL,
@@ -58,6 +61,7 @@ from .conftest import (
TEST_MAC,
TEST_MAC_CAM,
TEST_NVR_NAME,
TEST_PASSWORD,
TEST_PORT,
TEST_PRIVACY,
TEST_UID,
@@ -146,10 +150,14 @@ async def test_firmware_error_twice(
assert config_entry.state is ConfigEntryState.LOADED
freezer.tick(FIRMWARE_UPDATE_INTERVAL)
async_fire_time_changed(hass)
await hass.async_block_till_done()
entity_id = f"{Platform.UPDATE}.{TEST_NVR_NAME}_firmware"
assert hass.states.get(entity_id).state == STATE_OFF
freezer.tick(FIRMWARE_UPDATE_INTERVAL)
freezer.tick(2 * FIRMWARE_UPDATE_INTERVAL)
async_fire_time_changed(hass)
await hass.async_block_till_done()
@@ -1130,6 +1138,53 @@ async def test_camera_wake_callback(
assert hass.states.get(entity_id).state == STATE_OFF
@pytest.mark.parametrize(("seconds", "call_count"), [(10, 1), (3600, 0)])
async def test_firmware_update_delay(
hass: HomeAssistant,
freezer: FrozenDateTimeFactory,
reolink_host: MagicMock,
seconds: int,
call_count: int,
) -> None:
"""Test delay of firmware update check."""
now = datetime.now(UTC)
check_delay = (
now
+ timedelta(seconds=seconds)
- now.replace(hour=0, minute=0, second=0, microsecond=0)
).total_seconds()
config_entry = MockConfigEntry(
domain=DOMAIN,
unique_id=format_mac(TEST_MAC),
data={
CONF_HOST: TEST_HOST,
CONF_USERNAME: TEST_USERNAME,
CONF_PASSWORD: TEST_PASSWORD,
CONF_PORT: TEST_PORT,
CONF_USE_HTTPS: TEST_USE_HTTPS,
CONF_SUPPORTS_PRIVACY_MODE: TEST_PRIVACY,
CONF_BC_PORT: TEST_BC_PORT,
CONF_BC_ONLY: False,
CONF_FIRMWARE_CHECK_TIME: check_delay,
},
options={
CONF_PROTOCOL: DEFAULT_PROTOCOL,
},
title=TEST_NVR_NAME,
)
config_entry.add_to_hass(hass)
assert await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()
freezer.tick(60)
async_fire_time_changed(hass)
await hass.async_block_till_done()
assert reolink_host.check_new_firmware.call_count == call_count
async def test_baichaun_only(
hass: HomeAssistant,
reolink_host: MagicMock,