mirror of
https://github.com/home-assistant/core.git
synced 2026-10-06 14:29:21 -04:00
Fix ONVIF setup being cancelled by a snapshot that never completes (#182628)
This commit is contained in:
@@ -26,6 +26,7 @@ from .const import (
|
||||
CONF_SNAPSHOT_AUTH,
|
||||
DEFAULT_ARGUMENTS,
|
||||
DEFAULT_ENABLE_WEBHOOKS,
|
||||
SNAPSHOT_TIMEOUT,
|
||||
)
|
||||
from .device import ONVIFConfigEntry, ONVIFDevice
|
||||
|
||||
@@ -131,7 +132,11 @@ async def _get_snapshot_auth(device: ONVIFDevice) -> str | None:
|
||||
for basic_auth in (False, True):
|
||||
method = HTTP_BASIC_AUTHENTICATION if basic_auth else HTTP_DIGEST_AUTHENTICATION
|
||||
with suppress(ONVIFError):
|
||||
if await device.device.get_snapshot(device.profiles[0].token, basic_auth):
|
||||
if await device.device.get_snapshot(
|
||||
device.profiles[0].token,
|
||||
basic_auth,
|
||||
timeout=SNAPSHOT_TIMEOUT.total_seconds(),
|
||||
):
|
||||
return method
|
||||
|
||||
return None
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"""Constants for the onvif component."""
|
||||
|
||||
import asyncio
|
||||
from datetime import timedelta
|
||||
import logging
|
||||
|
||||
import aiohttp
|
||||
@@ -21,6 +22,10 @@ CONF_ENABLE_WEBHOOKS = "enable_webhooks"
|
||||
CONF_MORE_OPTIONS = "more_options"
|
||||
DEFAULT_ENABLE_WEBHOOKS = True
|
||||
|
||||
# Bounds each snapshot auth probe during setup, tighter than the library's
|
||||
# default so a camera whose snapshot never completes does not stall setup
|
||||
SNAPSHOT_TIMEOUT = timedelta(seconds=10)
|
||||
|
||||
ATTR_PAN = "pan"
|
||||
ATTR_TILT = "tilt"
|
||||
ATTR_ZOOM = "zoom"
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
"""Tests for the ONVIF integration __init__ module."""
|
||||
|
||||
from types import SimpleNamespace
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from homeassistant.components.onvif.const import DOMAIN
|
||||
from onvif.exceptions import ONVIFTimeoutError
|
||||
|
||||
from homeassistant.components.onvif.const import CONF_SNAPSHOT_AUTH, DOMAIN
|
||||
from homeassistant.config_entries import ConfigEntryState
|
||||
from homeassistant.const import (
|
||||
CONF_HOST,
|
||||
@@ -145,3 +148,39 @@ async def test_setup_entry(hass: HomeAssistant) -> None:
|
||||
mock_onvif_camera_cls.assert_called_once()
|
||||
host, port, username, password = mock_onvif_camera_cls.call_args.args[:4]
|
||||
assert (host, port, username, password) == (HOST, PORT, USERNAME, PASSWORD)
|
||||
|
||||
|
||||
async def test_setup_entry_snapshot_probe_timeout(hass: HomeAssistant) -> None:
|
||||
"""Test a snapshot probe that times out does not hold up setup."""
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
title=NAME,
|
||||
unique_id=MAC,
|
||||
data={
|
||||
CONF_NAME: NAME,
|
||||
CONF_HOST: HOST,
|
||||
CONF_PORT: PORT,
|
||||
CONF_USERNAME: USERNAME,
|
||||
CONF_PASSWORD: PASSWORD,
|
||||
},
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
|
||||
with patch("homeassistant.components.onvif.device.ONVIFCamera") as mock_camera_cls:
|
||||
setup_mock_onvif_camera(mock_camera_cls, with_full_setup=True)
|
||||
media_service = mock_camera_cls.create_media_service.return_value
|
||||
media_service.GetServiceCapabilities.return_value = SimpleNamespace(
|
||||
SnapshotUri=True
|
||||
)
|
||||
mock_camera_cls.get_snapshot.side_effect = ONVIFTimeoutError("Timed out")
|
||||
|
||||
assert await hass.config_entries.async_setup(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert entry.state is ConfigEntryState.LOADED
|
||||
# both auth methods are probed, each with the tighter setup timeout
|
||||
assert [
|
||||
call.kwargs["timeout"] for call in mock_camera_cls.get_snapshot.call_args_list
|
||||
] == [10, 10]
|
||||
# nothing learned about the snapshot auth, so setup will probe again next time
|
||||
assert CONF_SNAPSHOT_AUTH not in entry.data
|
||||
|
||||
Reference in New Issue
Block a user