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:
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user