Home Assistant Unofficial Reference 2024.12.1
http_api.py
Go to the documentation of this file.
1 """Provides an HTTP API for mobile_app."""
2 
3 from __future__ import annotations
4 
5 from contextlib import suppress
6 from http import HTTPStatus
7 import secrets
8 
9 from aiohttp.web import Request, Response
10 from nacl.secret import SecretBox
11 import voluptuous as vol
12 
13 from homeassistant.components import cloud
14 from homeassistant.components.http import KEY_HASS, HomeAssistantView
15 from homeassistant.components.http.data_validator import RequestDataValidator
16 from homeassistant.const import ATTR_DEVICE_ID, CONF_WEBHOOK_ID
17 from homeassistant.helpers import config_validation as cv
18 from homeassistant.util import slugify
19 
20 from .const import (
21  ATTR_APP_DATA,
22  ATTR_APP_ID,
23  ATTR_APP_NAME,
24  ATTR_APP_VERSION,
25  ATTR_DEVICE_NAME,
26  ATTR_MANUFACTURER,
27  ATTR_MODEL,
28  ATTR_OS_NAME,
29  ATTR_OS_VERSION,
30  ATTR_SUPPORTS_ENCRYPTION,
31  CONF_CLOUDHOOK_URL,
32  CONF_REMOTE_UI_URL,
33  CONF_SECRET,
34  CONF_USER_ID,
35  DOMAIN,
36  SCHEMA_APP_DATA,
37 )
38 from .util import async_create_cloud_hook
39 
40 
41 class RegistrationsView(HomeAssistantView):
42  """A view that accepts registration requests."""
43 
44  url = "/api/mobile_app/registrations"
45  name = "api:mobile_app:register"
46 
47  @RequestDataValidator( vol.Schema( { vol.Optional(ATTR_APP_DATA, default={}): SCHEMA_APP_DATA,
48  vol.Required(ATTR_APP_ID): cv.string,
49  vol.Required(ATTR_APP_NAME): cv.string,
50  vol.Required(ATTR_APP_VERSION): cv.string,
51  vol.Required(ATTR_DEVICE_NAME): cv.string,
52  vol.Required(ATTR_MANUFACTURER): cv.string,
53  vol.Required(ATTR_MODEL): cv.string,
54  vol.Optional(ATTR_DEVICE_ID): cv.string, # Added in 0.104
55  vol.Required(ATTR_OS_NAME): cv.string,
56  vol.Optional(ATTR_OS_VERSION): cv.string,
57  vol.Required(ATTR_SUPPORTS_ENCRYPTION, default=False): cv.boolean,
58  },
59  # To allow future apps to send more data
60  extra=vol.REMOVE_EXTRA,
61  )
62  )
63  async def post(self, request: Request, data: dict) -> Response:
64  """Handle the POST request for registration."""
65  hass = request.app[KEY_HASS]
66 
67  webhook_id = secrets.token_hex()
68 
69  if cloud.async_active_subscription(hass):
70  data[CONF_CLOUDHOOK_URL] = await async_create_cloud_hook(
71  hass, webhook_id, None
72  )
73 
74  data[CONF_WEBHOOK_ID] = webhook_id
75 
76  if data[ATTR_SUPPORTS_ENCRYPTION]:
77  data[CONF_SECRET] = secrets.token_hex(SecretBox.KEY_SIZE)
78 
79  data[CONF_USER_ID] = request["hass_user"].id
80 
81  # Fallback to DEVICE_ID if slug is empty.
82  if not slugify(data[ATTR_DEVICE_NAME], separator=""):
83  data[ATTR_DEVICE_NAME] = data[ATTR_DEVICE_ID]
84 
85  await hass.async_create_task(
86  hass.config_entries.flow.async_init(
87  DOMAIN, data=data, context={"source": "registration"}
88  )
89  )
90 
91  remote_ui_url = None
92  if cloud.async_active_subscription(hass):
93  with suppress(cloud.CloudNotAvailable):
94  remote_ui_url = cloud.async_remote_ui_url(hass)
95 
96  return self.json(
97  {
98  CONF_CLOUDHOOK_URL: data.get(CONF_CLOUDHOOK_URL),
99  CONF_REMOTE_UI_URL: remote_ui_url,
100  CONF_SECRET: data.get(CONF_SECRET),
101  CONF_WEBHOOK_ID: data[CONF_WEBHOOK_ID],
102  },
103  status_code=HTTPStatus.CREATED,
104  )