Home Assistant Unofficial Reference 2024.12.1
websocket_api.py
Go to the documentation of this file.
1 """Mobile app websocket API."""
2 
3 from __future__ import annotations
4 
5 from functools import wraps
6 from typing import Any
7 
8 import voluptuous as vol
9 
10 from homeassistant.components import websocket_api
11 from homeassistant.core import HomeAssistant, callback
12 
13 from .const import CONF_USER_ID, DATA_CONFIG_ENTRIES, DATA_PUSH_CHANNEL, DOMAIN
14 from .push_notification import PushChannel
15 
16 
17 @callback
19  """Set up the mobile app websocket API."""
20  websocket_api.async_register_command(hass, handle_push_notification_channel)
21  websocket_api.async_register_command(hass, handle_push_notification_confirm)
22 
23 
25  """Decorate WS function to ensure user owns the webhook ID."""
26 
27  @callback
28  @wraps(func)
29  def with_webhook_access(hass, connection, msg):
30  # Validate that the webhook ID is registered to the user of the websocket connection
31  config_entry = hass.data[DOMAIN][DATA_CONFIG_ENTRIES].get(msg["webhook_id"])
32 
33  if config_entry is None:
34  connection.send_error(
35  msg["id"], websocket_api.ERR_NOT_FOUND, "Webhook ID not found"
36  )
37  return
38 
39  if config_entry.data[CONF_USER_ID] != connection.user.id:
40  connection.send_error(
41  msg["id"],
42  websocket_api.ERR_UNAUTHORIZED,
43  "User not linked to this webhook ID",
44  )
45  return
46 
47  func(hass, connection, msg)
48 
49  return with_webhook_access
50 
51 
52 @callback
53 @_ensure_webhook_access
54 @websocket_api.websocket_command( { vol.Required("type"): "mobile_app/push_notification_confirm",
55  vol.Required("webhook_id"): str,
56  vol.Required("confirm_id"): str,
57  }
58 )
60  hass: HomeAssistant,
62  msg: dict[str, Any],
63 ) -> None:
64  """Confirm receipt of a push notification."""
65  channel: PushChannel | None = hass.data[DOMAIN][DATA_PUSH_CHANNEL].get(
66  msg["webhook_id"]
67  )
68  if channel is None:
69  connection.send_error(
70  msg["id"],
71  websocket_api.ERR_NOT_FOUND,
72  "Push notification channel not found",
73  )
74  return
75 
76  if channel.async_confirm_notification(msg["confirm_id"]):
77  connection.send_result(msg["id"])
78  else:
79  connection.send_error(
80  msg["id"],
81  websocket_api.ERR_NOT_FOUND,
82  "Push notification channel not found",
83  )
84 
85 
86 @websocket_api.websocket_command( { vol.Required("type"): "mobile_app/push_notification_channel",
87  vol.Required("webhook_id"): str,
88  vol.Optional("support_confirm", default=False): bool,
89  }
90 )
91 @_ensure_webhook_access
92 @websocket_api.async_response
94  hass: HomeAssistant,
96  msg: dict[str, Any],
97 ) -> None:
98  """Set up a direct push notification channel."""
99  webhook_id = msg["webhook_id"]
100  registered_channels: dict[str, PushChannel] = hass.data[DOMAIN][DATA_PUSH_CHANNEL]
101 
102  if webhook_id in registered_channels:
103  await registered_channels[webhook_id].async_teardown()
104 
105  @callback
106  def on_channel_teardown():
107  """Handle teardown."""
108  if registered_channels.get(webhook_id) == channel:
109  registered_channels.pop(webhook_id)
110 
111  channel = registered_channels[webhook_id] = PushChannel(
112  hass,
113  webhook_id,
114  msg["support_confirm"],
115  lambda data: connection.send_message(
116  websocket_api.messages.event_message(msg["id"], data)
117  ),
118  on_channel_teardown,
119  )
120 
121  connection.subscriptions[msg["id"]] = lambda: hass.async_create_task(
122  channel.async_teardown()
123  )
124  connection.send_result(msg["id"])
125 
web.Response get(self, web.Request request, str config_key)
Definition: view.py:88
None handle_push_notification_channel(HomeAssistant hass, websocket_api.ActiveConnection connection, dict[str, Any] msg)
None handle_push_notification_confirm(HomeAssistant hass, websocket_api.ActiveConnection connection, dict[str, Any] msg)