mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 21:27:38 +00:00
Synology SSL fix & Error handling (#4325)
* Synology SSL fix & Error handling * change handling for cookies/ssl * fix use not deprecated functions * fix lint * change verify * fix connector close to coro * fix force close * not needed since websession close connector too * fix params * fix lint
This commit is contained in:
parent
e005ebe989
commit
844799a1f7
@ -9,13 +9,14 @@ import logging
|
|||||||
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
|
import aiohttp
|
||||||
from aiohttp import web
|
from aiohttp import web
|
||||||
from aiohttp.web_exceptions import HTTPGatewayTimeout
|
from aiohttp.web_exceptions import HTTPGatewayTimeout
|
||||||
import async_timeout
|
import async_timeout
|
||||||
|
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
CONF_NAME, CONF_USERNAME, CONF_PASSWORD,
|
CONF_NAME, CONF_USERNAME, CONF_PASSWORD,
|
||||||
CONF_URL, CONF_WHITELIST, CONF_VERIFY_SSL)
|
CONF_URL, CONF_WHITELIST, CONF_VERIFY_SSL, EVENT_HOMEASSISTANT_STOP)
|
||||||
from homeassistant.components.camera import (
|
from homeassistant.components.camera import (
|
||||||
Camera, PLATFORM_SCHEMA)
|
Camera, PLATFORM_SCHEMA)
|
||||||
import homeassistant.helpers.config_validation as cv
|
import homeassistant.helpers.config_validation as cv
|
||||||
@ -58,8 +59,14 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
|||||||
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
|
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
|
||||||
"""Setup a Synology IP Camera."""
|
"""Setup a Synology IP Camera."""
|
||||||
if not config.get(CONF_VERIFY_SSL):
|
if not config.get(CONF_VERIFY_SSL):
|
||||||
_LOGGER.warning('SSL verification currently cannot be disabled. '
|
connector = aiohttp.TCPConnector(verify_ssl=False)
|
||||||
'See https://goo.gl/1h1119')
|
else:
|
||||||
|
connector = None
|
||||||
|
|
||||||
|
websession_init = aiohttp.ClientSession(
|
||||||
|
loop=hass.loop,
|
||||||
|
connector=connector
|
||||||
|
)
|
||||||
|
|
||||||
# Determine API to use for authentication
|
# Determine API to use for authentication
|
||||||
syno_api_url = SYNO_API_URL.format(
|
syno_api_url = SYNO_API_URL.format(
|
||||||
@ -73,12 +80,12 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
|
|||||||
}
|
}
|
||||||
try:
|
try:
|
||||||
with async_timeout.timeout(TIMEOUT, loop=hass.loop):
|
with async_timeout.timeout(TIMEOUT, loop=hass.loop):
|
||||||
query_req = yield from hass.websession.get(
|
query_req = yield from websession_init.get(
|
||||||
syno_api_url,
|
syno_api_url,
|
||||||
params=query_payload,
|
params=query_payload
|
||||||
)
|
)
|
||||||
except asyncio.TimeoutError:
|
except (asyncio.TimeoutError, aiohttp.errors.ClientError):
|
||||||
_LOGGER.error("Timeout on %s", syno_api_url)
|
_LOGGER.exception("Error on %s", syno_api_url)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
query_resp = yield from query_req.json()
|
query_resp = yield from query_req.json()
|
||||||
@ -96,12 +103,26 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
|
|||||||
|
|
||||||
session_id = yield from get_session_id(
|
session_id = yield from get_session_id(
|
||||||
hass,
|
hass,
|
||||||
|
websession_init,
|
||||||
config.get(CONF_USERNAME),
|
config.get(CONF_USERNAME),
|
||||||
config.get(CONF_PASSWORD),
|
config.get(CONF_PASSWORD),
|
||||||
syno_auth_url,
|
syno_auth_url
|
||||||
config.get(CONF_VERIFY_SSL)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
websession_init.detach()
|
||||||
|
|
||||||
|
# init websession
|
||||||
|
websession = aiohttp.ClientSession(
|
||||||
|
loop=hass.loop, connector=connector, cookies={'id': session_id})
|
||||||
|
|
||||||
|
@asyncio.coroutine
|
||||||
|
def _async_close_websession(event):
|
||||||
|
"""Close webssesion on shutdown."""
|
||||||
|
yield from websession.close()
|
||||||
|
|
||||||
|
hass.bus.async_listen_once(
|
||||||
|
EVENT_HOMEASSISTANT_STOP, _async_close_websession)
|
||||||
|
|
||||||
# Use SessionID to get cameras in system
|
# Use SessionID to get cameras in system
|
||||||
syno_camera_url = SYNO_API_URL.format(
|
syno_camera_url = SYNO_API_URL.format(
|
||||||
config.get(CONF_URL), WEBAPI_PATH, camera_api)
|
config.get(CONF_URL), WEBAPI_PATH, camera_api)
|
||||||
@ -113,13 +134,12 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
|
|||||||
}
|
}
|
||||||
try:
|
try:
|
||||||
with async_timeout.timeout(TIMEOUT, loop=hass.loop):
|
with async_timeout.timeout(TIMEOUT, loop=hass.loop):
|
||||||
camera_req = yield from hass.websession.get(
|
camera_req = yield from websession.get(
|
||||||
syno_camera_url,
|
syno_camera_url,
|
||||||
params=camera_payload,
|
params=camera_payload
|
||||||
cookies={'id': session_id}
|
|
||||||
)
|
)
|
||||||
except asyncio.TimeoutError:
|
except (asyncio.TimeoutError, aiohttp.errors.ClientError):
|
||||||
_LOGGER.error("Timeout on %s", syno_camera_url)
|
_LOGGER.exception("Error on %s", syno_camera_url)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
camera_resp = yield from camera_req.json()
|
camera_resp = yield from camera_req.json()
|
||||||
@ -128,13 +148,14 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
|
|||||||
|
|
||||||
# add cameras
|
# add cameras
|
||||||
devices = []
|
devices = []
|
||||||
tasks = []
|
|
||||||
for camera in cameras:
|
for camera in cameras:
|
||||||
if not config.get(CONF_WHITELIST):
|
if not config.get(CONF_WHITELIST):
|
||||||
camera_id = camera['id']
|
camera_id = camera['id']
|
||||||
snapshot_path = camera['snapshot_path']
|
snapshot_path = camera['snapshot_path']
|
||||||
|
|
||||||
device = SynologyCamera(
|
device = SynologyCamera(
|
||||||
|
hass,
|
||||||
|
websession,
|
||||||
config,
|
config,
|
||||||
camera_id,
|
camera_id,
|
||||||
camera['name'],
|
camera['name'],
|
||||||
@ -143,15 +164,13 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
|
|||||||
camera_path,
|
camera_path,
|
||||||
auth_path
|
auth_path
|
||||||
)
|
)
|
||||||
tasks.append(device.async_read_sid())
|
|
||||||
devices.append(device)
|
devices.append(device)
|
||||||
|
|
||||||
yield from asyncio.wait(tasks, loop=hass.loop)
|
|
||||||
yield from async_add_devices(devices)
|
yield from async_add_devices(devices)
|
||||||
|
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def get_session_id(hass, username, password, login_url, valid_cert):
|
def get_session_id(hass, websession, username, password, login_url):
|
||||||
"""Get a session id."""
|
"""Get a session id."""
|
||||||
auth_payload = {
|
auth_payload = {
|
||||||
'api': AUTH_API,
|
'api': AUTH_API,
|
||||||
@ -164,12 +183,12 @@ def get_session_id(hass, username, password, login_url, valid_cert):
|
|||||||
}
|
}
|
||||||
try:
|
try:
|
||||||
with async_timeout.timeout(TIMEOUT, loop=hass.loop):
|
with async_timeout.timeout(TIMEOUT, loop=hass.loop):
|
||||||
auth_req = yield from hass.websession.get(
|
auth_req = yield from websession.get(
|
||||||
login_url,
|
login_url,
|
||||||
params=auth_payload,
|
params=auth_payload
|
||||||
)
|
)
|
||||||
except asyncio.TimeoutError:
|
except (asyncio.TimeoutError, aiohttp.errors.ClientError):
|
||||||
_LOGGER.error("Timeout on %s", login_url)
|
_LOGGER.exception("Error on %s", login_url)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
auth_resp = yield from auth_req.json()
|
auth_resp = yield from auth_req.json()
|
||||||
@ -181,36 +200,22 @@ def get_session_id(hass, username, password, login_url, valid_cert):
|
|||||||
class SynologyCamera(Camera):
|
class SynologyCamera(Camera):
|
||||||
"""An implementation of a Synology NAS based IP camera."""
|
"""An implementation of a Synology NAS based IP camera."""
|
||||||
|
|
||||||
def __init__(self, config, camera_id, camera_name,
|
def __init__(self, hass, websession, config, camera_id,
|
||||||
snapshot_path, streaming_path, camera_path, auth_path):
|
camera_name, snapshot_path, streaming_path, camera_path,
|
||||||
|
auth_path):
|
||||||
"""Initialize a Synology Surveillance Station camera."""
|
"""Initialize a Synology Surveillance Station camera."""
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
self.hass = hass
|
||||||
|
self._websession = websession
|
||||||
self._name = camera_name
|
self._name = camera_name
|
||||||
self._username = config.get(CONF_USERNAME)
|
|
||||||
self._password = config.get(CONF_PASSWORD)
|
|
||||||
self._synology_url = config.get(CONF_URL)
|
self._synology_url = config.get(CONF_URL)
|
||||||
self._api_url = config.get(CONF_URL) + 'webapi/'
|
|
||||||
self._login_url = config.get(CONF_URL) + '/webapi/' + 'auth.cgi'
|
|
||||||
self._camera_name = config.get(CONF_CAMERA_NAME)
|
self._camera_name = config.get(CONF_CAMERA_NAME)
|
||||||
self._stream_id = config.get(CONF_STREAM_ID)
|
self._stream_id = config.get(CONF_STREAM_ID)
|
||||||
self._valid_cert = config.get(CONF_VERIFY_SSL)
|
|
||||||
self._camera_id = camera_id
|
self._camera_id = camera_id
|
||||||
self._snapshot_path = snapshot_path
|
self._snapshot_path = snapshot_path
|
||||||
self._streaming_path = streaming_path
|
self._streaming_path = streaming_path
|
||||||
self._camera_path = camera_path
|
self._camera_path = camera_path
|
||||||
self._auth_path = auth_path
|
self._auth_path = auth_path
|
||||||
self._session_id = None
|
|
||||||
|
|
||||||
@asyncio.coroutine
|
|
||||||
def async_read_sid(self):
|
|
||||||
"""Get a session id."""
|
|
||||||
self._session_id = yield from get_session_id(
|
|
||||||
self.hass,
|
|
||||||
self._username,
|
|
||||||
self._password,
|
|
||||||
self._login_url,
|
|
||||||
self._valid_cert
|
|
||||||
)
|
|
||||||
|
|
||||||
def camera_image(self):
|
def camera_image(self):
|
||||||
"""Return bytes of camera image."""
|
"""Return bytes of camera image."""
|
||||||
@ -231,13 +236,12 @@ class SynologyCamera(Camera):
|
|||||||
}
|
}
|
||||||
try:
|
try:
|
||||||
with async_timeout.timeout(TIMEOUT, loop=self.hass.loop):
|
with async_timeout.timeout(TIMEOUT, loop=self.hass.loop):
|
||||||
response = yield from self.hass.websession.get(
|
response = yield from self._websession.get(
|
||||||
image_url,
|
image_url,
|
||||||
params=image_payload,
|
params=image_payload
|
||||||
cookies={'id': self._session_id}
|
|
||||||
)
|
)
|
||||||
except asyncio.TimeoutError:
|
except (asyncio.TimeoutError, aiohttp.errors.ClientError):
|
||||||
_LOGGER.error("Timeout on %s", image_url)
|
_LOGGER.exception("Error on %s", image_url)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
image = yield from response.read()
|
image = yield from response.read()
|
||||||
@ -260,12 +264,12 @@ class SynologyCamera(Camera):
|
|||||||
}
|
}
|
||||||
try:
|
try:
|
||||||
with async_timeout.timeout(TIMEOUT, loop=self.hass.loop):
|
with async_timeout.timeout(TIMEOUT, loop=self.hass.loop):
|
||||||
stream = yield from self.hass.websession.get(
|
stream = yield from self._websession.get(
|
||||||
streaming_url,
|
streaming_url,
|
||||||
payload=streaming_payload,
|
params=streaming_payload
|
||||||
cookies={'id': self._session_id}
|
|
||||||
)
|
)
|
||||||
except asyncio.TimeoutError:
|
except (asyncio.TimeoutError, aiohttp.errors.ClientError):
|
||||||
|
_LOGGER.exception("Error on %s", streaming_url)
|
||||||
raise HTTPGatewayTimeout()
|
raise HTTPGatewayTimeout()
|
||||||
|
|
||||||
response = web.StreamResponse()
|
response = web.StreamResponse()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user