Skip to content

Commit 8d09b5c

Browse files
authored
Relax Reolink update interval and timeout for big installs (home-assistant#156509)
1 parent d92fa7f commit 8d09b5c

File tree

9 files changed

+44
-33
lines changed

9 files changed

+44
-33
lines changed

homeassistant/components/reolink/__init__.py

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,8 @@
5858
Platform.SWITCH,
5959
Platform.UPDATE,
6060
]
61-
DEVICE_UPDATE_INTERVAL = timedelta(seconds=60)
61+
DEVICE_UPDATE_INTERVAL_MIN = timedelta(seconds=60)
62+
DEVICE_UPDATE_INTERVAL_PER_CAM = timedelta(seconds=10)
6263
FIRMWARE_UPDATE_INTERVAL = timedelta(hours=24)
6364
NUM_CRED_ERRORS = 3
6465

@@ -137,9 +138,12 @@ async def async_setup_entry(
137138
}
138139
hass.config_entries.async_update_entry(config_entry, data=data)
139140

141+
min_timeout = host.api.timeout * (RETRY_ATTEMPTS + 2)
142+
update_timeout = max(min_timeout, min_timeout * host.api.num_cameras / 10)
143+
140144
async def async_device_config_update() -> None:
141145
"""Update the host state cache and renew the ONVIF-subscription."""
142-
async with asyncio.timeout(host.api.timeout * (RETRY_ATTEMPTS + 2)):
146+
async with asyncio.timeout(update_timeout):
143147
try:
144148
await host.update_states()
145149
except CredentialsInvalidError as err:
@@ -156,7 +160,7 @@ async def async_device_config_update() -> None:
156160

157161
host.credential_errors = 0
158162

159-
async with asyncio.timeout(host.api.timeout * (RETRY_ATTEMPTS + 2)):
163+
async with asyncio.timeout(min_timeout):
160164
await host.renew()
161165

162166
if host.api.new_devices and config_entry.state == ConfigEntryState.LOADED:
@@ -171,7 +175,7 @@ async def async_device_config_update() -> None:
171175

172176
async def async_check_firmware_update() -> None:
173177
"""Check for firmware updates."""
174-
async with asyncio.timeout(host.api.timeout * (RETRY_ATTEMPTS + 2)):
178+
async with asyncio.timeout(min_timeout):
175179
try:
176180
await host.api.check_new_firmware(host.firmware_ch_list)
177181
except ReolinkError as err:
@@ -197,7 +201,10 @@ async def async_check_firmware_update() -> None:
197201
config_entry=config_entry,
198202
name=f"reolink.{host.api.nvr_name}",
199203
update_method=async_device_config_update,
200-
update_interval=DEVICE_UPDATE_INTERVAL,
204+
update_interval=max(
205+
DEVICE_UPDATE_INTERVAL_MIN,
206+
DEVICE_UPDATE_INTERVAL_PER_CAM * host.api.num_cameras,
207+
),
201208
)
202209
firmware_coordinator = DataUpdateCoordinator(
203210
hass,

homeassistant/components/reolink/update.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
DataUpdateCoordinator,
2424
)
2525

26-
from . import DEVICE_UPDATE_INTERVAL
26+
from . import DEVICE_UPDATE_INTERVAL_MIN, DEVICE_UPDATE_INTERVAL_PER_CAM
2727
from .const import DOMAIN
2828
from .entity import (
2929
ReolinkChannelCoordinatorEntity,
@@ -221,7 +221,10 @@ async def _pause_update_coordinator(self) -> None:
221221

222222
async def _resume_update_coordinator(self, *args: Any) -> None:
223223
"""Resume updating the states using the data update coordinator (after reboots)."""
224-
self._reolink_data.device_coordinator.update_interval = DEVICE_UPDATE_INTERVAL
224+
self._reolink_data.device_coordinator.update_interval = max(
225+
DEVICE_UPDATE_INTERVAL_MIN,
226+
DEVICE_UPDATE_INTERVAL_PER_CAM * self._host.api.num_cameras,
227+
)
225228
try:
226229
await self._reolink_data.device_coordinator.async_refresh()
227230
finally:

tests/components/reolink/conftest.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ def _init_host_mock(host_mock: MagicMock) -> None:
106106
host_mock.protocol = "rtsp"
107107
host_mock.channels = [0]
108108
host_mock.stream_channels = [0]
109+
host_mock.num_cameras = 1
109110
host_mock.new_devices = False
110111
host_mock.sw_version_update_required = False
111112
host_mock.hardware_version = "IPC_00000"

tests/components/reolink/test_binary_sensor.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
from freezegun.api import FrozenDateTimeFactory
77

8-
from homeassistant.components.reolink import DEVICE_UPDATE_INTERVAL
8+
from homeassistant.components.reolink import DEVICE_UPDATE_INTERVAL_MIN
99
from homeassistant.config_entries import ConfigEntryState
1010
from homeassistant.const import STATE_OFF, STATE_ON, Platform
1111
from homeassistant.core import HomeAssistant
@@ -35,7 +35,7 @@ async def test_motion_sensor(
3535
assert hass.states.get(entity_id).state == STATE_ON
3636

3737
reolink_host.motion_detected.return_value = False
38-
freezer.tick(DEVICE_UPDATE_INTERVAL)
38+
freezer.tick(DEVICE_UPDATE_INTERVAL_MIN)
3939
async_fire_time_changed(hass)
4040
await hass.async_block_till_done()
4141

@@ -69,7 +69,7 @@ async def test_smart_ai_sensor(
6969
assert hass.states.get(entity_id).state == STATE_ON
7070

7171
reolink_host.baichuan.smart_ai_state.return_value = False
72-
freezer.tick(DEVICE_UPDATE_INTERVAL)
72+
freezer.tick(DEVICE_UPDATE_INTERVAL_MIN)
7373
async_fire_time_changed(hass)
7474
await hass.async_block_till_done()
7575

@@ -94,7 +94,7 @@ async def test_index_sensor(
9494
assert hass.states.get(entity_id).state == STATE_ON
9595

9696
reolink_host.baichuan.io_input_state.return_value = False
97-
freezer.tick(DEVICE_UPDATE_INTERVAL)
97+
freezer.tick(DEVICE_UPDATE_INTERVAL_MIN)
9898
async_fire_time_changed(hass)
9999
await hass.async_block_till_done()
100100

tests/components/reolink/test_config_flow.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
)
1717

1818
from homeassistant import config_entries
19-
from homeassistant.components.reolink import DEVICE_UPDATE_INTERVAL
19+
from homeassistant.components.reolink import DEVICE_UPDATE_INTERVAL_MIN
2020
from homeassistant.components.reolink.config_flow import DEFAULT_PROTOCOL
2121
from homeassistant.components.reolink.const import (
2222
CONF_BC_ONLY,
@@ -564,7 +564,7 @@ async def test_dhcp_ip_update_aborted_if_wrong_mac(
564564

565565
# ensure the last_update_succes is False for the device_coordinator.
566566
reolink_host.get_states.side_effect = ReolinkError("Test error")
567-
freezer.tick(DEVICE_UPDATE_INTERVAL)
567+
freezer.tick(DEVICE_UPDATE_INTERVAL_MIN)
568568
async_fire_time_changed(hass)
569569
await hass.async_block_till_done()
570570

@@ -661,7 +661,7 @@ async def test_dhcp_ip_update(
661661

662662
# ensure the last_update_succes is False for the device_coordinator.
663663
reolink_host.get_states.side_effect = ReolinkError("Test error")
664-
freezer.tick(DEVICE_UPDATE_INTERVAL)
664+
freezer.tick(DEVICE_UPDATE_INTERVAL_MIN)
665665
async_fire_time_changed(hass)
666666
await hass.async_block_till_done()
667667

tests/components/reolink/test_host.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
from reolink_aio.enums import SubType
1111
from reolink_aio.exceptions import NotSupportedError, ReolinkError, SubscriptionError
1212

13-
from homeassistant.components.reolink import DEVICE_UPDATE_INTERVAL
13+
from homeassistant.components.reolink import DEVICE_UPDATE_INTERVAL_MIN
1414
from homeassistant.components.reolink.host import (
1515
FIRST_ONVIF_LONG_POLL_TIMEOUT,
1616
FIRST_ONVIF_TIMEOUT,
@@ -258,15 +258,15 @@ async def test_renew(
258258
await hass.async_block_till_done()
259259
assert config_entry.state is ConfigEntryState.LOADED
260260

261-
freezer.tick(DEVICE_UPDATE_INTERVAL)
261+
freezer.tick(DEVICE_UPDATE_INTERVAL_MIN)
262262
async_fire_time_changed(hass)
263263
await hass.async_block_till_done()
264264

265265
reolink_host.renew.assert_called()
266266

267267
reolink_host.renew.side_effect = SubscriptionError("Test error")
268268

269-
freezer.tick(DEVICE_UPDATE_INTERVAL)
269+
freezer.tick(DEVICE_UPDATE_INTERVAL_MIN)
270270
async_fire_time_changed(hass)
271271
await hass.async_block_till_done()
272272

@@ -275,7 +275,7 @@ async def test_renew(
275275
reolink_host.subscribe.reset_mock()
276276
reolink_host.subscribe.side_effect = SubscriptionError("Test error")
277277

278-
freezer.tick(DEVICE_UPDATE_INTERVAL)
278+
freezer.tick(DEVICE_UPDATE_INTERVAL_MIN)
279279
async_fire_time_changed(hass)
280280
await hass.async_block_till_done()
281281

@@ -341,7 +341,7 @@ async def test_long_poll_stop_when_push(
341341
webhook_id = config_entry.runtime_data.host.webhook_id
342342
await client.post(f"/api/webhook/{webhook_id}")
343343

344-
freezer.tick(DEVICE_UPDATE_INTERVAL)
344+
freezer.tick(DEVICE_UPDATE_INTERVAL_MIN)
345345
async_fire_time_changed(hass)
346346
await hass.async_block_till_done()
347347

tests/components/reolink/test_init.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
)
1515

1616
from homeassistant.components.reolink import (
17-
DEVICE_UPDATE_INTERVAL,
17+
DEVICE_UPDATE_INTERVAL_MIN,
1818
FIRMWARE_UPDATE_INTERVAL,
1919
NUM_CRED_ERRORS,
2020
)
@@ -174,7 +174,7 @@ async def test_credential_error_three(
174174
issue_id = f"config_entry_reauth_{DOMAIN}_{config_entry.entry_id}"
175175
for _ in range(NUM_CRED_ERRORS):
176176
assert (HOMEASSISTANT_DOMAIN, issue_id) not in issue_registry.issues
177-
freezer.tick(DEVICE_UPDATE_INTERVAL)
177+
freezer.tick(DEVICE_UPDATE_INTERVAL_MIN)
178178
async_fire_time_changed(hass)
179179
await hass.async_block_till_done()
180180

@@ -917,7 +917,7 @@ async def test_new_device_discovered(
917917
assert reolink_host.logout.call_count == 0
918918
reolink_host.new_devices = True
919919

920-
freezer.tick(DEVICE_UPDATE_INTERVAL)
920+
freezer.tick(DEVICE_UPDATE_INTERVAL_MIN)
921921
async_fire_time_changed(hass)
922922
await hass.async_block_till_done()
923923

@@ -988,7 +988,7 @@ async def test_LoginPrivacyModeError(
988988
reolink_host.baichuan.check_subscribe_events.reset_mock()
989989
assert reolink_host.baichuan.check_subscribe_events.call_count == 0
990990

991-
freezer.tick(DEVICE_UPDATE_INTERVAL)
991+
freezer.tick(DEVICE_UPDATE_INTERVAL_MIN)
992992
async_fire_time_changed(hass)
993993
await hass.async_block_till_done()
994994

tests/components/reolink/test_select.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from reolink_aio.api import Chime
88
from reolink_aio.exceptions import InvalidParameterError, ReolinkError
99

10-
from homeassistant.components.reolink import DEVICE_UPDATE_INTERVAL
10+
from homeassistant.components.reolink import DEVICE_UPDATE_INTERVAL_MIN
1111
from homeassistant.components.select import DOMAIN as SELECT_DOMAIN
1212
from homeassistant.config_entries import ConfigEntryState
1313
from homeassistant.const import (
@@ -68,7 +68,7 @@ async def test_floodlight_mode_select(
6868
)
6969

7070
reolink_host.whiteled_mode.return_value = -99 # invalid value
71-
freezer.tick(DEVICE_UPDATE_INTERVAL)
71+
freezer.tick(DEVICE_UPDATE_INTERVAL_MIN)
7272
async_fire_time_changed(hass)
7373
await hass.async_block_till_done()
7474

@@ -142,7 +142,7 @@ async def test_host_scene_select(
142142
)
143143

144144
reolink_host.baichuan.active_scene = "Invalid value"
145-
freezer.tick(DEVICE_UPDATE_INTERVAL)
145+
freezer.tick(DEVICE_UPDATE_INTERVAL_MIN)
146146
async_fire_time_changed(hass)
147147
await hass.async_block_till_done()
148148

@@ -202,7 +202,7 @@ async def test_chime_select(
202202
# Test unavailable
203203
reolink_chime.event_info = {}
204204
reolink_chime.update_enums()
205-
freezer.tick(DEVICE_UPDATE_INTERVAL)
205+
freezer.tick(DEVICE_UPDATE_INTERVAL_MIN)
206206
async_fire_time_changed(hass)
207207
await hass.async_block_till_done()
208208

tests/components/reolink/test_switch.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from reolink_aio.api import Chime
88
from reolink_aio.exceptions import ReolinkError
99

10-
from homeassistant.components.reolink import DEVICE_UPDATE_INTERVAL
10+
from homeassistant.components.reolink import DEVICE_UPDATE_INTERVAL_MIN
1111
from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN
1212
from homeassistant.config_entries import ConfigEntryState
1313
from homeassistant.const import (
@@ -45,7 +45,7 @@ async def test_switch(
4545
assert hass.states.get(entity_id).state == STATE_ON
4646

4747
reolink_host.audio_record.return_value = False
48-
freezer.tick(DEVICE_UPDATE_INTERVAL)
48+
freezer.tick(DEVICE_UPDATE_INTERVAL_MIN)
4949
async_fire_time_changed(hass)
5050
await hass.async_block_till_done()
5151

@@ -91,7 +91,7 @@ async def test_switch(
9191
reolink_host.set_audio.reset_mock(side_effect=True)
9292

9393
reolink_host.camera_online.return_value = False
94-
freezer.tick(DEVICE_UPDATE_INTERVAL)
94+
freezer.tick(DEVICE_UPDATE_INTERVAL_MIN)
9595
async_fire_time_changed(hass)
9696
await hass.async_block_till_done()
9797

@@ -118,7 +118,7 @@ async def test_host_switch(
118118
assert hass.states.get(entity_id).state == STATE_ON
119119

120120
reolink_host.email_enabled.return_value = False
121-
freezer.tick(DEVICE_UPDATE_INTERVAL)
121+
freezer.tick(DEVICE_UPDATE_INTERVAL_MIN)
122122
async_fire_time_changed(hass)
123123
await hass.async_block_till_done()
124124

@@ -183,7 +183,7 @@ async def test_chime_switch(
183183
assert hass.states.get(entity_id).state == STATE_ON
184184

185185
reolink_chime.led_state = False
186-
freezer.tick(DEVICE_UPDATE_INTERVAL)
186+
freezer.tick(DEVICE_UPDATE_INTERVAL_MIN)
187187
async_fire_time_changed(hass)
188188
await hass.async_block_till_done()
189189

@@ -248,7 +248,7 @@ async def test_rule_switch(
248248
assert hass.states.get(entity_id).state == STATE_ON
249249

250250
reolink_host.baichuan.rule_enabled.return_value = False
251-
freezer.tick(DEVICE_UPDATE_INTERVAL)
251+
freezer.tick(DEVICE_UPDATE_INTERVAL_MIN)
252252
async_fire_time_changed(hass)
253253
await hass.async_block_till_done()
254254

0 commit comments

Comments
 (0)