Fix ONVIF camera snapshot with auth (#33241)

This commit is contained in:
Franck Nijhof 2020-03-25 13:32:28 +01:00 committed by GitHub
parent 2a3c94bad0
commit 1fa996ed68
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -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,