hass-core/homeassistant/components/mobile_app/http_api.py
Robbie Trencheny 3769f5893a
Mobile App: Register devices into the registry (#21856)
* Register devices into the registry

* Switch to device ID instead of webhook ID

* Rearchitect mobile_app to support config entries

* Kill DATA_REGISTRATIONS by migrating registrations into config entries

* Fix tests

* Improve how we get the config_entry_id

* Remove single_instance_allowed

* Simplify setup_registration

* Move webhook registering functions into __init__.py since they are only ever used once

* Kill get_registration websocket command

* Support description_placeholders in async_abort

* Add link to mobile_app implementing apps in abort dialog

* Store config entry and device registry entry in hass.data instead of looking it up

* Add testing to ensure that the config entry is created at registration

* Fix busted async_abort test

* Remove unnecessary check for entry is None
2019-03-14 12:57:50 -07:00

74 lines
2.8 KiB
Python

"""Provides an HTTP API for mobile_app."""
import uuid
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, CONF_WEBHOOK_ID)
from homeassistant.loader import get_component
from .const import (ATTR_APP_COMPONENT, ATTR_DEVICE_ID,
ATTR_SUPPORTS_ENCRYPTION, CONF_CLOUDHOOK_URL, CONF_SECRET,
CONF_USER_ID, DOMAIN, ERR_INVALID_COMPONENT,
REGISTRATION_SCHEMA)
from .helpers import error_response, supports_encryption
class RegistrationsView(HomeAssistantView):
"""A view that accepts registration requests."""
url = '/api/mobile_app/registrations'
name = 'api:mobile_app:register'
@RequestDataValidator(REGISTRATION_SCHEMA)
async def post(self, request: Request, data: Dict) -> Response:
"""Handle the POST request for registration."""
hass = request.app['hass']
if ATTR_APP_COMPONENT in data:
component = get_component(hass, data[ATTR_APP_COMPONENT])
if component is None:
fmt_str = "{} is not a valid component."
msg = fmt_str.format(data[ATTR_APP_COMPONENT])
return error_response(ERR_INVALID_COMPONENT, msg)
if (hasattr(component, 'DEPENDENCIES') is False or
(hasattr(component, 'DEPENDENCIES') and
DOMAIN not in component.DEPENDENCIES)):
fmt_str = "{} is not compatible with mobile_app."
msg = fmt_str.format(data[ATTR_APP_COMPONENT])
return error_response(ERR_INVALID_COMPONENT, msg)
webhook_id = generate_secret()
if hass.components.cloud.async_active_subscription():
data[CONF_CLOUDHOOK_URL] = \
await async_create_cloudhook(hass, webhook_id)
data[ATTR_DEVICE_ID] = str(uuid.uuid4()).replace("-", "")
data[CONF_WEBHOOK_ID] = webhook_id
if data[ATTR_SUPPORTS_ENCRYPTION] and supports_encryption():
from nacl.secret import SecretBox
data[CONF_SECRET] = generate_secret(SecretBox.KEY_SIZE)
data[CONF_USER_ID] = request['hass_user'].id
ctx = {'source': 'registration'}
await hass.async_create_task(
hass.config_entries.flow.async_init(DOMAIN, context=ctx,
data=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)