Allow managing cloud webhook (#18672)

* Add cloud webhook support

* Simplify payload

* Add cloud http api tests

* Fix tests

* Lint

* Handle cloud webhooks

* Fix things

* Fix name

* Rename it to cloudhook

* Final rename

* Final final rename?

* Fix docstring

* More tests

* Lint

* Add types

* Fix things
This commit is contained in:
Paulus Schoutsen 2018-11-26 14:10:18 +01:00 committed by GitHub
parent 4a661e351f
commit 7848381f43
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 611 additions and 39 deletions

View file

@ -3,6 +3,7 @@ import asyncio
from functools import wraps
import logging
import aiohttp
import async_timeout
import voluptuous as vol
@ -44,6 +45,20 @@ SCHEMA_WS_SUBSCRIPTION = websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend({
})
WS_TYPE_HOOK_CREATE = 'cloud/cloudhook/create'
SCHEMA_WS_HOOK_CREATE = websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend({
vol.Required('type'): WS_TYPE_HOOK_CREATE,
vol.Required('webhook_id'): str
})
WS_TYPE_HOOK_DELETE = 'cloud/cloudhook/delete'
SCHEMA_WS_HOOK_DELETE = websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend({
vol.Required('type'): WS_TYPE_HOOK_DELETE,
vol.Required('webhook_id'): str
})
async def async_setup(hass):
"""Initialize the HTTP API."""
hass.components.websocket_api.async_register_command(
@ -58,6 +73,14 @@ async def async_setup(hass):
WS_TYPE_UPDATE_PREFS, websocket_update_prefs,
SCHEMA_WS_UPDATE_PREFS
)
hass.components.websocket_api.async_register_command(
WS_TYPE_HOOK_CREATE, websocket_hook_create,
SCHEMA_WS_HOOK_CREATE
)
hass.components.websocket_api.async_register_command(
WS_TYPE_HOOK_DELETE, websocket_hook_delete,
SCHEMA_WS_HOOK_DELETE
)
hass.http.register_view(GoogleActionsSyncView)
hass.http.register_view(CloudLoginView)
hass.http.register_view(CloudLogoutView)
@ -76,7 +99,7 @@ _CLOUD_ERRORS = {
def _handle_cloud_errors(handler):
"""Handle auth errors."""
"""Webview decorator to handle auth errors."""
@wraps(handler)
async def error_handler(view, request, *args, **kwargs):
"""Handle exceptions that raise from the wrapped request handler."""
@ -240,17 +263,49 @@ def websocket_cloud_status(hass, connection, msg):
websocket_api.result_message(msg['id'], _account_data(cloud)))
def _require_cloud_login(handler):
"""Websocket decorator that requires cloud to be logged in."""
@wraps(handler)
def with_cloud_auth(hass, connection, msg):
"""Require to be logged into the cloud."""
cloud = hass.data[DOMAIN]
if not cloud.is_logged_in:
connection.send_message(websocket_api.error_message(
msg['id'], 'not_logged_in',
'You need to be logged in to the cloud.'))
return
handler(hass, connection, msg)
return with_cloud_auth
def _handle_aiohttp_errors(handler):
"""Websocket decorator that handlers aiohttp errors.
Can only wrap async handlers.
"""
@wraps(handler)
async def with_error_handling(hass, connection, msg):
"""Handle aiohttp errors."""
try:
await handler(hass, connection, msg)
except asyncio.TimeoutError:
connection.send_message(websocket_api.error_message(
msg['id'], 'timeout', 'Command timed out.'))
except aiohttp.ClientError:
connection.send_message(websocket_api.error_message(
msg['id'], 'unknown', 'Error making request.'))
return with_error_handling
@_require_cloud_login
@websocket_api.async_response
async def websocket_subscription(hass, connection, msg):
"""Handle request for account info."""
cloud = hass.data[DOMAIN]
if not cloud.is_logged_in:
connection.send_message(websocket_api.error_message(
msg['id'], 'not_logged_in',
'You need to be logged in to the cloud.'))
return
with async_timeout.timeout(REQUEST_TIMEOUT, loop=hass.loop):
response = await cloud.fetch_subscription_info()
@ -277,24 +332,37 @@ async def websocket_subscription(hass, connection, msg):
connection.send_message(websocket_api.result_message(msg['id'], data))
@_require_cloud_login
@websocket_api.async_response
async def websocket_update_prefs(hass, connection, msg):
"""Handle request for account info."""
cloud = hass.data[DOMAIN]
if not cloud.is_logged_in:
connection.send_message(websocket_api.error_message(
msg['id'], 'not_logged_in',
'You need to be logged in to the cloud.'))
return
changes = dict(msg)
changes.pop('id')
changes.pop('type')
await cloud.prefs.async_update(**changes)
connection.send_message(websocket_api.result_message(
msg['id'], {'success': True}))
connection.send_message(websocket_api.result_message(msg['id']))
@_require_cloud_login
@websocket_api.async_response
@_handle_aiohttp_errors
async def websocket_hook_create(hass, connection, msg):
"""Handle request for account info."""
cloud = hass.data[DOMAIN]
hook = await cloud.cloudhooks.async_create(msg['webhook_id'])
connection.send_message(websocket_api.result_message(msg['id'], hook))
@_require_cloud_login
@websocket_api.async_response
async def websocket_hook_delete(hass, connection, msg):
"""Handle request for account info."""
cloud = hass.data[DOMAIN]
await cloud.cloudhooks.async_delete(msg['webhook_id'])
connection.send_message(websocket_api.result_message(msg['id']))
def _account_data(cloud):