1 """Mobile app websocket API."""
3 from __future__
import annotations
5 from functools
import wraps
8 import voluptuous
as vol
13 from .const
import CONF_USER_ID, DATA_CONFIG_ENTRIES, DATA_PUSH_CHANNEL, DOMAIN
14 from .push_notification
import PushChannel
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)
25 """Decorate WS function to ensure user owns the webhook ID."""
29 def with_webhook_access(hass, connection, msg):
31 config_entry = hass.data[DOMAIN][DATA_CONFIG_ENTRIES].
get(msg[
"webhook_id"])
33 if config_entry
is None:
34 connection.send_error(
35 msg[
"id"], websocket_api.ERR_NOT_FOUND,
"Webhook ID not found"
39 if config_entry.data[CONF_USER_ID] != connection.user.id:
40 connection.send_error(
42 websocket_api.ERR_UNAUTHORIZED,
43 "User not linked to this webhook ID",
47 func(hass, connection, msg)
49 return with_webhook_access
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,
64 """Confirm receipt of a push notification."""
65 channel: PushChannel |
None = hass.data[DOMAIN][DATA_PUSH_CHANNEL].
get(
69 connection.send_error(
71 websocket_api.ERR_NOT_FOUND,
72 "Push notification channel not found",
76 if channel.async_confirm_notification(msg[
"confirm_id"]):
77 connection.send_result(msg[
"id"])
79 connection.send_error(
81 websocket_api.ERR_NOT_FOUND,
82 "Push notification channel not found",
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,
91 @_ensure_webhook_access
92 @websocket_api.async_response
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]
102 if webhook_id
in registered_channels:
103 await registered_channels[webhook_id].async_teardown()
106 def on_channel_teardown():
107 """Handle teardown."""
108 if registered_channels.get(webhook_id) == channel:
109 registered_channels.pop(webhook_id)
111 channel = registered_channels[webhook_id] =
PushChannel(
114 msg[
"support_confirm"],
115 lambda data: connection.send_message(
116 websocket_api.messages.event_message(msg[
"id"], data)
121 connection.subscriptions[msg[
"id"]] =
lambda: hass.async_create_task(
122 channel.async_teardown()
124 connection.send_result(msg[
"id"])
125
web.Response get(self, web.Request request, str config_key)
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)
def async_setup_commands(hass)
def _ensure_webhook_access(func)