mirror of
https://github.com/home-assistant/core.git
synced 2025-07-20 11:47:06 +00:00
Automatically configure HTTP auth type in ONVIF snapshots (#38729)
* Allow selection of HTTP auth type in ONVIF snapshots * Auto populate snapshot auth * Fix no auth case * Add missing return
This commit is contained in:
parent
c1b8497aaa
commit
a87fedc0af
@ -1,6 +1,9 @@
|
|||||||
"""The ONVIF integration."""
|
"""The ONVIF integration."""
|
||||||
import asyncio
|
import asyncio
|
||||||
|
|
||||||
|
import requests
|
||||||
|
from requests.auth import HTTPDigestAuth
|
||||||
|
from urllib3.exceptions import ReadTimeoutError
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.components.ffmpeg import CONF_EXTRA_ARGUMENTS
|
from homeassistant.components.ffmpeg import CONF_EXTRA_ARGUMENTS
|
||||||
@ -12,6 +15,8 @@ from homeassistant.const import (
|
|||||||
CONF_PORT,
|
CONF_PORT,
|
||||||
CONF_USERNAME,
|
CONF_USERNAME,
|
||||||
EVENT_HOMEASSISTANT_STOP,
|
EVENT_HOMEASSISTANT_STOP,
|
||||||
|
HTTP_BASIC_AUTHENTICATION,
|
||||||
|
HTTP_DIGEST_AUTHENTICATION,
|
||||||
)
|
)
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.exceptions import ConfigEntryNotReady
|
from homeassistant.exceptions import ConfigEntryNotReady
|
||||||
@ -19,6 +24,7 @@ from homeassistant.helpers import config_per_platform
|
|||||||
|
|
||||||
from .const import (
|
from .const import (
|
||||||
CONF_RTSP_TRANSPORT,
|
CONF_RTSP_TRANSPORT,
|
||||||
|
CONF_SNAPSHOT_AUTH,
|
||||||
DEFAULT_ARGUMENTS,
|
DEFAULT_ARGUMENTS,
|
||||||
DEFAULT_NAME,
|
DEFAULT_NAME,
|
||||||
DEFAULT_PASSWORD,
|
DEFAULT_PASSWORD,
|
||||||
@ -76,6 +82,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
|
|||||||
if not device.available:
|
if not device.available:
|
||||||
raise ConfigEntryNotReady()
|
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
|
hass.data[DOMAIN][entry.unique_id] = device
|
||||||
|
|
||||||
platforms = ["camera"]
|
platforms = ["camera"]
|
||||||
@ -113,6 +122,39 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def _get_snapshot_auth(hass, device, entry):
|
||||||
|
if not (device.username and device.password):
|
||||||
|
return HTTP_DIGEST_AUTHENTICATION
|
||||||
|
|
||||||
|
snapshot_uri = await device.async_get_snapshot_uri(device.profiles[0])
|
||||||
|
auth = HTTPDigestAuth(device.username, device.password)
|
||||||
|
|
||||||
|
def _get():
|
||||||
|
# so we can handle keyword arguments
|
||||||
|
return requests.get(snapshot_uri, timeout=1, auth=auth)
|
||||||
|
|
||||||
|
try:
|
||||||
|
response = await hass.async_add_executor_job(_get)
|
||||||
|
|
||||||
|
if response.status_code == 401:
|
||||||
|
return HTTP_BASIC_AUTHENTICATION
|
||||||
|
|
||||||
|
return HTTP_DIGEST_AUTHENTICATION
|
||||||
|
except requests.exceptions.Timeout:
|
||||||
|
return HTTP_BASIC_AUTHENTICATION
|
||||||
|
except requests.exceptions.ConnectionError as error:
|
||||||
|
if isinstance(error.args[0], ReadTimeoutError):
|
||||||
|
return HTTP_BASIC_AUTHENTICATION
|
||||||
|
return HTTP_DIGEST_AUTHENTICATION
|
||||||
|
|
||||||
|
|
||||||
|
async def async_populate_snapshot_auth(hass, device, entry):
|
||||||
|
"""Check if digest auth for snapshots is possible."""
|
||||||
|
auth = await _get_snapshot_auth(hass, device, entry)
|
||||||
|
new_data = {**entry.data, CONF_SNAPSHOT_AUTH: auth}
|
||||||
|
hass.config_entries.async_update_entry(entry, data=new_data)
|
||||||
|
|
||||||
|
|
||||||
async def async_populate_options(hass, entry):
|
async def async_populate_options(hass, entry):
|
||||||
"""Populate default options for device."""
|
"""Populate default options for device."""
|
||||||
options = {
|
options = {
|
||||||
|
@ -4,11 +4,12 @@ import asyncio
|
|||||||
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 requests
|
import requests
|
||||||
from requests.auth import HTTPDigestAuth
|
from requests.auth import HTTPBasicAuth, HTTPDigestAuth
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.components.camera import SUPPORT_STREAM, Camera
|
from homeassistant.components.camera import SUPPORT_STREAM, Camera
|
||||||
from homeassistant.components.ffmpeg import CONF_EXTRA_ARGUMENTS, DATA_FFMPEG
|
from homeassistant.components.ffmpeg import CONF_EXTRA_ARGUMENTS, DATA_FFMPEG
|
||||||
|
from homeassistant.const import HTTP_BASIC_AUTHENTICATION
|
||||||
from homeassistant.helpers import config_validation as cv, entity_platform
|
from homeassistant.helpers import config_validation as cv, entity_platform
|
||||||
from homeassistant.helpers.aiohttp_client import async_aiohttp_proxy_stream
|
from homeassistant.helpers.aiohttp_client import async_aiohttp_proxy_stream
|
||||||
|
|
||||||
@ -24,6 +25,7 @@ from .const import (
|
|||||||
ATTR_TILT,
|
ATTR_TILT,
|
||||||
ATTR_ZOOM,
|
ATTR_ZOOM,
|
||||||
CONF_RTSP_TRANSPORT,
|
CONF_RTSP_TRANSPORT,
|
||||||
|
CONF_SNAPSHOT_AUTH,
|
||||||
CONTINUOUS_MOVE,
|
CONTINUOUS_MOVE,
|
||||||
DIR_DOWN,
|
DIR_DOWN,
|
||||||
DIR_LEFT,
|
DIR_LEFT,
|
||||||
@ -79,6 +81,10 @@ class ONVIFCameraEntity(ONVIFBaseEntity, Camera):
|
|||||||
self.stream_options[CONF_RTSP_TRANSPORT] = device.config_entry.options.get(
|
self.stream_options[CONF_RTSP_TRANSPORT] = device.config_entry.options.get(
|
||||||
CONF_RTSP_TRANSPORT
|
CONF_RTSP_TRANSPORT
|
||||||
)
|
)
|
||||||
|
self._basic_auth = (
|
||||||
|
device.config_entry.data.get(CONF_SNAPSHOT_AUTH)
|
||||||
|
== HTTP_BASIC_AUTHENTICATION
|
||||||
|
)
|
||||||
self._stream_uri = None
|
self._stream_uri = None
|
||||||
self._snapshot_uri = None
|
self._snapshot_uri = None
|
||||||
|
|
||||||
@ -115,6 +121,9 @@ class ONVIFCameraEntity(ONVIFBaseEntity, Camera):
|
|||||||
if self.device.capabilities.snapshot:
|
if self.device.capabilities.snapshot:
|
||||||
auth = None
|
auth = None
|
||||||
if self.device.username and self.device.password:
|
if self.device.username and self.device.password:
|
||||||
|
if self._basic_auth:
|
||||||
|
auth = HTTPBasicAuth(self.device.username, self.device.password)
|
||||||
|
else:
|
||||||
auth = HTTPDigestAuth(self.device.username, self.device.password)
|
auth = HTTPDigestAuth(self.device.username, self.device.password)
|
||||||
|
|
||||||
def fetch():
|
def fetch():
|
||||||
|
@ -13,6 +13,7 @@ DEFAULT_ARGUMENTS = "-pred 1"
|
|||||||
|
|
||||||
CONF_DEVICE_ID = "deviceid"
|
CONF_DEVICE_ID = "deviceid"
|
||||||
CONF_RTSP_TRANSPORT = "rtsp_transport"
|
CONF_RTSP_TRANSPORT = "rtsp_transport"
|
||||||
|
CONF_SNAPSHOT_AUTH = "snapshot_auth"
|
||||||
|
|
||||||
RTSP_TRANS_PROTOCOLS = ["tcp", "udp", "udp_multicast", "http"]
|
RTSP_TRANS_PROTOCOLS = ["tcp", "udp", "udp_multicast", "http"]
|
||||||
|
|
||||||
|
@ -13,8 +13,8 @@
|
|||||||
"step": {
|
"step": {
|
||||||
"auth": {
|
"auth": {
|
||||||
"data": {
|
"data": {
|
||||||
"password": "Password",
|
"password": "[%key:common::config_flow::data::password%]",
|
||||||
"username": "Username"
|
"username": "[%key:common::config_flow::data::username%]"
|
||||||
},
|
},
|
||||||
"title": "Configure authentication"
|
"title": "Configure authentication"
|
||||||
},
|
},
|
||||||
@ -33,9 +33,9 @@
|
|||||||
},
|
},
|
||||||
"manual_input": {
|
"manual_input": {
|
||||||
"data": {
|
"data": {
|
||||||
"host": "Host",
|
"host": "[%key:common::config_flow::data::host%]",
|
||||||
"name": "Name",
|
"name": "Name",
|
||||||
"port": "Port"
|
"port": "[%key:common::config_flow::data::port%]"
|
||||||
},
|
},
|
||||||
"title": "Configure ONVIF device"
|
"title": "Configure ONVIF device"
|
||||||
},
|
},
|
||||||
|
Loading…
x
Reference in New Issue
Block a user