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:
parent
4a661e351f
commit
7848381f43
15 changed files with 611 additions and 39 deletions
|
@ -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):
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue