Robbie Trencheny 9ab0753cf7 mobile_app improvements (#21607)
* First webhook commands for getting and deleting single registrations

* Keep a list of deleted webhook IDs so we can 410 if the webhook receives traffic in the future

* Return a empty JSON object instead of None

* Split up mobile_app bits into individual files

* Add typing

* Sort keys

* Remove unused async_setup_entry

* New decorator method of registering webhooks

* Add tests for cloud hook forwarding and improve error handling for cloud hooks

* Initial implementation of platform specific logic

* Add get registrations by user ID websocket call, minor style fixes

* Stop using resp dictionary during registration

* Move mobile_app/ios.py to ios/mobile_app.py

* Log any errors encountered during webhook

* Improve update registration call

* Split up mobile_app tests to match split up component

* Fix tests

* Remove integration_map in favor of component name in registration

* Add a few helper functions for custom logic components to use

* Load the app_component platform at device registration or component setup time

* Remove extraneous function

* Use guard function for checking if component is in device

* Inline websocket schemas

* Rename ATTR_s used in storage to DATA_ prefix

* squash flake8 and pylint issues

* Remove ios.mobile_app platform

* Dont mark websocket_api as a dependency

* Return standard empty_okay_response with 400 if no JSON sent

* Ensure deleted webhook IDs are registered at launch

* Remove the creation of cloudhooks during handle_webhook

* Rename device to registration everywhere applicable

* Dont check if cloud is logged in, just check if cloud is in components

* Dont ever use cloudhook_id

* Remove component loading logic for a later PR

* Cast exception to string

* Remove unused functions
2019-03-08 23:44:56 -08:00

79 lines
2.7 KiB
Python

"""Provides an HTTP API for mobile_app."""
from typing import Dict
from aiohttp.web import Response, Request
from homeassistant.auth.util import generate_secret
from homeassistant.components.cloud import async_create_cloudhook
from homeassistant.components.http import HomeAssistantView
from homeassistant.components.http.data_validator import RequestDataValidator
from homeassistant.const import (HTTP_CREATED, HTTP_INTERNAL_SERVER_ERROR,
CONF_WEBHOOK_ID)
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.storage import Store
from homeassistant.helpers.typing import HomeAssistantType
from .const import (DATA_REGISTRATIONS, ATTR_SUPPORTS_ENCRYPTION,
CONF_CLOUDHOOK_URL, CONF_SECRET, CONF_USER_ID,
DOMAIN, REGISTRATION_SCHEMA)
from .helpers import supports_encryption, savable_state
from .webhook import setup_registration
def register_http_handlers(hass: HomeAssistantType, store: Store) -> bool:
"""Register the HTTP handlers/views."""
hass.http.register_view(RegistrationsView(store))
return True
class RegistrationsView(HomeAssistantView):
"""A view that accepts registration requests."""
url = '/api/mobile_app/registrations'
name = 'api:mobile_app:register'
def __init__(self, store: Store) -> None:
"""Initialize the view."""
self._store = store
@RequestDataValidator(REGISTRATION_SCHEMA)
async def post(self, request: Request, data: Dict) -> Response:
"""Handle the POST request for registration."""
hass = request.app['hass']
webhook_id = generate_secret()
if "cloud" in hass.config.components:
cloudhook = await async_create_cloudhook(hass, webhook_id)
if cloudhook is not None:
data[CONF_CLOUDHOOK_URL] = cloudhook[CONF_CLOUDHOOK_URL]
data[CONF_WEBHOOK_ID] = webhook_id
if data[ATTR_SUPPORTS_ENCRYPTION] and supports_encryption():
secret = generate_secret(16)
data[CONF_SECRET] = secret
data[CONF_USER_ID] = request['hass_user'].id
hass.data[DOMAIN][DATA_REGISTRATIONS][webhook_id] = data
try:
await self._store.async_save(savable_state(hass))
except HomeAssistantError:
return self.json_message("Error saving registration.",
HTTP_INTERNAL_SERVER_ERROR)
setup_registration(hass, self._store, data)
return self.json({
CONF_CLOUDHOOK_URL: data.get(CONF_CLOUDHOOK_URL),
CONF_SECRET: data.get(CONF_SECRET),
CONF_WEBHOOK_ID: data[CONF_WEBHOOK_ID],
}, status_code=HTTP_CREATED)