Ensure onvif retries setup if camera fails to respond correctly (#91866)

This commit is contained in:
J. Nick Koston 2023-04-22 18:27:54 -05:00 committed by GitHub
parent 6013584b7b
commit 82340907c2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 27 additions and 10 deletions

View file

@ -31,6 +31,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
try:
await device.async_setup()
if not entry.data.get(CONF_SNAPSHOT_AUTH):
await async_populate_snapshot_auth(hass, device, entry)
except RequestError as err:
await device.device.close()
raise ConfigEntryNotReady(
@ -52,9 +54,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
if not device.available:
raise ConfigEntryNotReady()
if not entry.data.get(CONF_SNAPSHOT_AUTH):
await async_populate_snapshot_auth(hass, device, entry)
hass.data[DOMAIN][entry.unique_id] = device
device.platforms = [Platform.BUTTON, Platform.CAMERA]

View file

@ -1,6 +1,8 @@
"""Support for ONVIF Cameras with FFmpeg as decoder."""
from __future__ import annotations
import asyncio
from haffmpeg.camera import CameraMjpeg
from onvif.exceptions import ONVIFError
import voluptuous as vol
@ -110,6 +112,7 @@ class ONVIFCameraEntity(ONVIFBaseEntity, Camera):
== HTTP_BASIC_AUTHENTICATION
)
self._stream_uri: str | None = None
self._stream_uri_future: asyncio.Future[str] | None = None
@property
def name(self) -> str:
@ -130,7 +133,7 @@ class ONVIFCameraEntity(ONVIFBaseEntity, Camera):
async def stream_source(self):
"""Return the stream source."""
return self._stream_uri
return await self._async_get_stream_uri()
async def async_camera_image(
self, width: int | None = None, height: int | None = None
@ -158,10 +161,10 @@ class ONVIFCameraEntity(ONVIFBaseEntity, Camera):
self.device.name,
)
assert self._stream_uri
stream_uri = await self._async_get_stream_uri()
return await ffmpeg.async_get_image(
self.hass,
self._stream_uri,
stream_uri,
extra_cmd=self.device.config_entry.options.get(CONF_EXTRA_ARGUMENTS),
width=width,
height=height,
@ -173,9 +176,10 @@ class ONVIFCameraEntity(ONVIFBaseEntity, Camera):
ffmpeg_manager = get_ffmpeg_manager(self.hass)
stream = CameraMjpeg(ffmpeg_manager.binary)
stream_uri = await self._async_get_stream_uri()
await stream.open_camera(
self._stream_uri,
stream_uri,
extra_cmd=self.device.config_entry.options.get(CONF_EXTRA_ARGUMENTS),
)
@ -190,13 +194,27 @@ class ONVIFCameraEntity(ONVIFBaseEntity, Camera):
finally:
await stream.close()
async def async_added_to_hass(self) -> None:
"""Run when entity about to be added to hass."""
async def _async_get_stream_uri(self) -> str:
"""Return the stream URI."""
if self._stream_uri:
return self._stream_uri
if self._stream_uri_future:
return await self._stream_uri_future
loop = asyncio.get_running_loop()
self._stream_uri_future = loop.create_future()
try:
uri_no_auth = await self.device.async_get_stream_uri(self.profile)
except (asyncio.TimeoutError, Exception) as err: # pylint: disable=broad-except
LOGGER.error("Failed to get stream uri: %s", err)
if self._stream_uri_future:
self._stream_uri_future.set_exception(err)
raise
url = URL(uri_no_auth)
url = url.with_user(self.device.username)
url = url.with_password(self.device.password)
self._stream_uri = str(url)
self._stream_uri_future.set_result(self._stream_uri)
return self._stream_uri
async def async_perform_ptz(
self,