Fix ONVIF camera snapshot with auth (#33241)
This commit is contained in:
parent
2a3c94bad0
commit
1fa996ed68
1 changed files with 26 additions and 23 deletions
|
@ -5,13 +5,13 @@ import logging
|
||||||
import os
|
import os
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from aiohttp import ClientError
|
|
||||||
from aiohttp.client_exceptions import ClientConnectionError, ServerDisconnectedError
|
from aiohttp.client_exceptions import ClientConnectionError, ServerDisconnectedError
|
||||||
import async_timeout
|
|
||||||
from haffmpeg.camera import CameraMjpeg
|
from haffmpeg.camera import CameraMjpeg
|
||||||
from haffmpeg.tools import IMAGE_JPEG, ImageFrame
|
from haffmpeg.tools import IMAGE_JPEG, ImageFrame
|
||||||
import onvif
|
import onvif
|
||||||
from onvif import ONVIFCamera, exceptions
|
from onvif import ONVIFCamera, exceptions
|
||||||
|
import requests
|
||||||
|
from requests.auth import HTTPDigestAuth
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
from zeep.asyncio import AsyncTransport
|
from zeep.asyncio import AsyncTransport
|
||||||
from zeep.exceptions import Fault
|
from zeep.exceptions import Fault
|
||||||
|
@ -412,17 +412,12 @@ class ONVIFHassCamera(Camera):
|
||||||
req.ProfileToken = profiles[self._profile_index].token
|
req.ProfileToken = profiles[self._profile_index].token
|
||||||
|
|
||||||
snapshot_uri = await media_service.GetSnapshotUri(req)
|
snapshot_uri = await media_service.GetSnapshotUri(req)
|
||||||
uri_no_auth = snapshot_uri.Uri
|
self._snapshot = snapshot_uri.Uri
|
||||||
uri_for_log = uri_no_auth.replace("http://", "http://<user>:<password>@", 1)
|
|
||||||
# Same authentication as rtsp
|
|
||||||
self._snapshot = uri_no_auth.replace(
|
|
||||||
"http://", f"http://{self._username}:{self._password}@", 1
|
|
||||||
)
|
|
||||||
|
|
||||||
_LOGGER.debug(
|
_LOGGER.debug(
|
||||||
"ONVIF Camera Using the following URL for %s snapshot: %s",
|
"ONVIF Camera Using the following URL for %s snapshot: %s",
|
||||||
self._name,
|
self._name,
|
||||||
uri_for_log,
|
self._snapshot,
|
||||||
)
|
)
|
||||||
except exceptions.ONVIFError as err:
|
except exceptions.ONVIFError as err:
|
||||||
_LOGGER.error("Couldn't setup camera '%s'. Error: %s", self._name, err)
|
_LOGGER.error("Couldn't setup camera '%s'. Error: %s", self._name, err)
|
||||||
|
@ -509,25 +504,33 @@ class ONVIFHassCamera(Camera):
|
||||||
|
|
||||||
async def async_camera_image(self):
|
async def async_camera_image(self):
|
||||||
"""Return a still image response from the camera."""
|
"""Return a still image response from the camera."""
|
||||||
|
|
||||||
_LOGGER.debug("Retrieving image from camera '%s'", self._name)
|
_LOGGER.debug("Retrieving image from camera '%s'", self._name)
|
||||||
|
image = None
|
||||||
|
|
||||||
if self._snapshot is not None:
|
if self._snapshot is not None:
|
||||||
|
auth = None
|
||||||
|
if self._username and self._password:
|
||||||
|
auth = HTTPDigestAuth(self._username, self._password)
|
||||||
|
|
||||||
|
def fetch():
|
||||||
|
"""Read image from a URL."""
|
||||||
try:
|
try:
|
||||||
websession = async_get_clientsession(self.hass)
|
response = requests.get(self._snapshot, timeout=5, auth=auth)
|
||||||
with async_timeout.timeout(10):
|
return response.content
|
||||||
response = await websession.get(self._snapshot)
|
except requests.exceptions.RequestException as error:
|
||||||
image = await response.read()
|
_LOGGER.error(
|
||||||
except asyncio.TimeoutError:
|
"Fetch snapshot image failed from %s, falling back to FFmpeg; %s",
|
||||||
_LOGGER.error("Timeout getting image from: %s", self._name)
|
self._name,
|
||||||
image = None
|
error,
|
||||||
except ClientError as err:
|
)
|
||||||
_LOGGER.error("Error getting new camera image: %s", err)
|
|
||||||
image = None
|
image = await self.hass.async_add_job(fetch)
|
||||||
|
|
||||||
|
if image is None:
|
||||||
|
# Don't keep trying the snapshot URL
|
||||||
|
self._snapshot = None
|
||||||
|
|
||||||
if self._snapshot is None or image is None:
|
|
||||||
ffmpeg = ImageFrame(self.hass.data[DATA_FFMPEG].binary, loop=self.hass.loop)
|
ffmpeg = ImageFrame(self.hass.data[DATA_FFMPEG].binary, loop=self.hass.loop)
|
||||||
|
|
||||||
image = await asyncio.shield(
|
image = await asyncio.shield(
|
||||||
ffmpeg.get_image(
|
ffmpeg.get_image(
|
||||||
self._input,
|
self._input,
|
||||||
|
|
Loading…
Add table
Reference in a new issue