1 """Decorators for the Websocket API."""
3 from __future__
import annotations
5 from collections.abc
import Callable
6 from functools
import wraps
7 from typing
import TYPE_CHECKING, Any
9 import voluptuous
as vol
16 from .
import const, messages
17 from .connection
import ActiveConnection
21 func: const.AsyncWebSocketCommandHandler,
23 connection: ActiveConnection,
26 """Create a response and handle exception."""
28 await func(hass, connection, msg)
29 except Exception
as err:
30 connection.async_handle_exception(msg, err)
34 func: const.AsyncWebSocketCommandHandler,
35 ) -> const.WebSocketCommandHandler:
36 """Decorate an async function to handle WebSocket API messages."""
37 task_name = f
"websocket_api.async:{func.__name__}"
42 hass: HomeAssistant, connection: ActiveConnection, msg: dict[str, Any]
44 """Schedule the handler."""
47 hass.async_create_background_task(
53 return schedule_handler
56 def require_admin(func: const.WebSocketCommandHandler) -> const.WebSocketCommandHandler:
57 """Websocket decorator to require user to be an admin."""
61 hass: HomeAssistant, connection: ActiveConnection, msg: dict[str, Any]
63 """Check admin and call function."""
64 user = connection.user
66 if user
is None or not user.is_admin:
69 func(hass, connection, msg)
75 only_owner: bool =
False,
76 only_system_user: bool =
False,
77 allow_system_user: bool =
True,
78 only_active_user: bool =
True,
79 only_inactive_user: bool =
False,
80 only_supervisor: bool =
False,
81 ) -> Callable[[const.WebSocketCommandHandler], const.WebSocketCommandHandler]:
82 """Decorate function validating login user exist in current WS connection.
84 Will write out error message if not authenticated.
87 def validator(func: const.WebSocketCommandHandler) -> const.WebSocketCommandHandler:
91 def check_current_user(
92 hass: HomeAssistant, connection: ActiveConnection, msg: dict[str, Any]
94 """Check current user."""
96 def output_error(message_id: str, message: str) ->
None:
97 """Output error message."""
98 connection.send_message(
99 messages.error_message(msg[
"id"], message_id, message)
102 if only_owner
and not connection.user.is_owner:
103 output_error(
"only_owner",
"Only allowed as owner")
106 if only_system_user
and not connection.user.system_generated:
107 output_error(
"only_system_user",
"Only allowed as system user")
110 if not allow_system_user
and connection.user.system_generated:
111 output_error(
"not_system_user",
"Not allowed as system user")
114 if only_active_user
and not connection.user.is_active:
115 output_error(
"only_active_user",
"Only allowed as active user")
118 if only_inactive_user
and connection.user.is_active:
119 output_error(
"only_inactive_user",
"Not allowed as active user")
122 if only_supervisor
and connection.user.name != HASSIO_USER_NAME:
123 output_error(
"only_supervisor",
"Only allowed as Supervisor")
126 return func(hass, connection, msg)
128 return check_current_user
134 schema: VolDictType | vol.All,
135 ) -> Callable[[const.WebSocketCommandHandler], const.WebSocketCommandHandler]:
136 """Tag a function as a websocket command.
138 The schema must be either a dictionary where the keys are voluptuous markers, or
139 a voluptuous.All schema where the first item is a voluptuous Mapping schema.
141 if is_dict := isinstance(schema, dict):
142 command = schema[
"type"]
144 command = schema.validators[0].schema[
"type"]
146 def decorate(func: const.WebSocketCommandHandler) -> const.WebSocketCommandHandler:
147 """Decorate ws command function."""
148 if is_dict
and len(schema) == 1:
149 func._ws_schema =
False
151 func._ws_schema = messages.BASE_COMMAND_MESSAGE_SCHEMA.extend(schema)
154 assert not isinstance(schema, dict)
155 extended_schema = vol.All(
156 schema.validators[0].extend(
157 messages.BASE_COMMAND_MESSAGE_SCHEMA.schema
159 *schema.validators[1:],
161 func._ws_schema = extended_schema
162 func._ws_command = command
const .WebSocketCommandHandler require_admin(const .WebSocketCommandHandler func)
Callable[[const .WebSocketCommandHandler], const .WebSocketCommandHandler] websocket_command(VolDictType|vol.All schema)
None _handle_async_response(const .AsyncWebSocketCommandHandler func, HomeAssistant hass, ActiveConnection connection, dict[str, Any] msg)
Callable[[const .WebSocketCommandHandler], const .WebSocketCommandHandler] ws_require_user(bool only_owner=False, bool only_system_user=False, bool allow_system_user=True, bool only_active_user=True, bool only_inactive_user=False, bool only_supervisor=False)
const .WebSocketCommandHandler async_response(const .AsyncWebSocketCommandHandler func)