2019-03-09 07:44:56 +00:00
|
|
|
"""Provides an HTTP API for mobile_app."""
|
2021-03-18 12:07:04 +00:00
|
|
|
from __future__ import annotations
|
|
|
|
|
2021-03-23 13:36:43 +00:00
|
|
|
from contextlib import suppress
|
2021-09-22 18:59:52 +00:00
|
|
|
from http import HTTPStatus
|
2019-12-12 15:46:33 +00:00
|
|
|
import secrets
|
2019-03-09 07:44:56 +00:00
|
|
|
|
2019-10-21 08:05:41 +00:00
|
|
|
from aiohttp.web import Request, Response
|
|
|
|
from nacl.secret import SecretBox
|
2019-12-21 21:45:06 +00:00
|
|
|
import voluptuous as vol
|
2019-03-09 07:44:56 +00:00
|
|
|
|
2022-01-14 15:35:35 +00:00
|
|
|
from homeassistant.components import cloud
|
2019-03-09 07:44:56 +00:00
|
|
|
from homeassistant.components.http import HomeAssistantView
|
|
|
|
from homeassistant.components.http.data_validator import RequestDataValidator
|
2021-09-22 18:59:52 +00:00
|
|
|
from homeassistant.const import ATTR_DEVICE_ID, CONF_WEBHOOK_ID
|
2019-12-21 21:45:06 +00:00
|
|
|
from homeassistant.helpers import config_validation as cv
|
2020-06-21 16:12:15 +00:00
|
|
|
from homeassistant.util import slugify
|
2019-07-31 19:25:30 +00:00
|
|
|
|
|
|
|
from .const import (
|
2019-12-21 21:45:06 +00:00
|
|
|
ATTR_APP_DATA,
|
|
|
|
ATTR_APP_ID,
|
|
|
|
ATTR_APP_NAME,
|
|
|
|
ATTR_APP_VERSION,
|
|
|
|
ATTR_DEVICE_NAME,
|
|
|
|
ATTR_MANUFACTURER,
|
|
|
|
ATTR_MODEL,
|
|
|
|
ATTR_OS_NAME,
|
|
|
|
ATTR_OS_VERSION,
|
2019-07-31 19:25:30 +00:00
|
|
|
ATTR_SUPPORTS_ENCRYPTION,
|
|
|
|
CONF_CLOUDHOOK_URL,
|
|
|
|
CONF_REMOTE_UI_URL,
|
|
|
|
CONF_SECRET,
|
|
|
|
CONF_USER_ID,
|
|
|
|
DOMAIN,
|
2022-01-03 19:02:41 +00:00
|
|
|
SCHEMA_APP_DATA,
|
2019-07-31 19:25:30 +00:00
|
|
|
)
|
2019-04-12 02:06:36 +00:00
|
|
|
from .helpers import supports_encryption
|
2019-03-09 07:44:56 +00:00
|
|
|
|
|
|
|
|
|
|
|
class RegistrationsView(HomeAssistantView):
|
|
|
|
"""A view that accepts registration requests."""
|
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
url = "/api/mobile_app/registrations"
|
|
|
|
name = "api:mobile_app:register"
|
2019-03-09 07:44:56 +00:00
|
|
|
|
2019-12-21 21:45:06 +00:00
|
|
|
@RequestDataValidator(
|
2020-01-04 12:45:03 +00:00
|
|
|
vol.Schema(
|
|
|
|
{
|
2022-01-03 19:02:41 +00:00
|
|
|
vol.Optional(ATTR_APP_DATA, default={}): SCHEMA_APP_DATA,
|
2020-01-04 12:45:03 +00:00
|
|
|
vol.Required(ATTR_APP_ID): cv.string,
|
|
|
|
vol.Required(ATTR_APP_NAME): cv.string,
|
|
|
|
vol.Required(ATTR_APP_VERSION): cv.string,
|
|
|
|
vol.Required(ATTR_DEVICE_NAME): cv.string,
|
|
|
|
vol.Required(ATTR_MANUFACTURER): cv.string,
|
|
|
|
vol.Required(ATTR_MODEL): cv.string,
|
2020-01-04 22:15:50 +00:00
|
|
|
vol.Optional(ATTR_DEVICE_ID): cv.string, # Added in 0.104
|
2020-01-04 12:45:03 +00:00
|
|
|
vol.Required(ATTR_OS_NAME): cv.string,
|
|
|
|
vol.Optional(ATTR_OS_VERSION): cv.string,
|
|
|
|
vol.Required(ATTR_SUPPORTS_ENCRYPTION, default=False): cv.boolean,
|
|
|
|
},
|
|
|
|
# To allow future apps to send more data
|
|
|
|
extra=vol.REMOVE_EXTRA,
|
|
|
|
)
|
2019-12-21 21:45:06 +00:00
|
|
|
)
|
2021-03-18 12:07:04 +00:00
|
|
|
async def post(self, request: Request, data: dict) -> Response:
|
2019-03-09 07:44:56 +00:00
|
|
|
"""Handle the POST request for registration."""
|
2019-07-31 19:25:30 +00:00
|
|
|
hass = request.app["hass"]
|
2019-03-09 07:44:56 +00:00
|
|
|
|
2019-12-12 15:46:33 +00:00
|
|
|
webhook_id = secrets.token_hex()
|
2019-03-09 07:44:56 +00:00
|
|
|
|
2022-01-14 15:35:35 +00:00
|
|
|
if cloud.async_active_subscription(hass):
|
|
|
|
data[CONF_CLOUDHOOK_URL] = await cloud.async_create_cloudhook(
|
|
|
|
hass, webhook_id
|
|
|
|
)
|
2019-03-09 07:44:56 +00:00
|
|
|
|
|
|
|
data[CONF_WEBHOOK_ID] = webhook_id
|
|
|
|
|
|
|
|
if data[ATTR_SUPPORTS_ENCRYPTION] and supports_encryption():
|
2019-12-12 15:46:33 +00:00
|
|
|
data[CONF_SECRET] = secrets.token_hex(SecretBox.KEY_SIZE)
|
2019-03-09 07:44:56 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
data[CONF_USER_ID] = request["hass_user"].id
|
2019-03-09 07:44:56 +00:00
|
|
|
|
2022-02-23 17:54:27 +00:00
|
|
|
# Fallback to DEVICE_ID if slug is empty.
|
|
|
|
if not slugify(data[ATTR_DEVICE_NAME], separator=""):
|
2020-06-21 16:12:15 +00:00
|
|
|
data[ATTR_DEVICE_NAME] = data[ATTR_DEVICE_ID]
|
|
|
|
|
2019-03-14 19:57:50 +00:00
|
|
|
await hass.async_create_task(
|
2020-01-04 12:45:03 +00:00
|
|
|
hass.config_entries.flow.async_init(
|
|
|
|
DOMAIN, data=data, context={"source": "registration"}
|
|
|
|
)
|
2019-07-31 19:25:30 +00:00
|
|
|
)
|
2019-03-09 07:44:56 +00:00
|
|
|
|
2019-03-15 02:46:59 +00:00
|
|
|
remote_ui_url = None
|
2022-12-05 02:10:26 +00:00
|
|
|
if cloud.async_active_subscription(hass):
|
|
|
|
with suppress(hass.components.cloud.CloudNotAvailable):
|
|
|
|
remote_ui_url = cloud.async_remote_ui_url(hass)
|
2019-03-15 02:46:59 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
return self.json(
|
|
|
|
{
|
|
|
|
CONF_CLOUDHOOK_URL: data.get(CONF_CLOUDHOOK_URL),
|
|
|
|
CONF_REMOTE_UI_URL: remote_ui_url,
|
|
|
|
CONF_SECRET: data.get(CONF_SECRET),
|
|
|
|
CONF_WEBHOOK_ID: data[CONF_WEBHOOK_ID],
|
|
|
|
},
|
2021-09-22 18:59:52 +00:00
|
|
|
status_code=HTTPStatus.CREATED,
|
2019-07-31 19:25:30 +00:00
|
|
|
)
|