Home Assistant Unofficial Reference 2024.12.1
__init__.py
Go to the documentation of this file.
1 """Support for displaying persistent notifications."""
2 
3 from __future__ import annotations
4 
5 from collections.abc import Callable, Mapping
6 from datetime import datetime
7 from enum import StrEnum
8 from functools import partial
9 import logging
10 from typing import Any, Final, TypedDict
11 
12 import voluptuous as vol
13 
14 from homeassistant.components import websocket_api
15 from homeassistant.core import CALLBACK_TYPE, HomeAssistant, ServiceCall, callback
16 from homeassistant.helpers import config_validation as cv, singleton
18  async_dispatcher_connect,
19  async_dispatcher_send,
20 )
21 from homeassistant.helpers.typing import ConfigType
22 from homeassistant.loader import bind_hass
23 import homeassistant.util.dt as dt_util
24 from homeassistant.util.signal_type import SignalType
25 from homeassistant.util.uuid import random_uuid_hex
26 
27 DOMAIN = "persistent_notification"
28 
29 ATTR_CREATED_AT: Final = "created_at"
30 ATTR_MESSAGE: Final = "message"
31 ATTR_NOTIFICATION_ID: Final = "notification_id"
32 ATTR_TITLE: Final = "title"
33 ATTR_STATUS: Final = "status"
34 
35 
36 class Notification(TypedDict):
37  """Persistent notification."""
38 
39  created_at: datetime
40  message: str
41  notification_id: str
42  title: str | None
43 
44 
45 class UpdateType(StrEnum):
46  """Persistent notification update type."""
47 
48  CURRENT = "current"
49  ADDED = "added"
50  REMOVED = "removed"
51  UPDATED = "updated"
52 
53 
54 SIGNAL_PERSISTENT_NOTIFICATIONS_UPDATED = SignalType[
55  UpdateType, dict[str, Notification]
56 ]("persistent_notifications_updated")
57 
58 SCHEMA_SERVICE_NOTIFICATION = vol.Schema(
59  {vol.Required(ATTR_NOTIFICATION_ID): cv.string}
60 )
61 
62 _LOGGER = logging.getLogger(__name__)
63 
64 CONFIG_SCHEMA = cv.empty_config_schema(DOMAIN)
65 
66 
67 @callback
69  hass: HomeAssistant,
70  _callback: Callable[[UpdateType, dict[str, Notification]], None],
71 ) -> CALLBACK_TYPE:
72  """Register a callback."""
74  hass, SIGNAL_PERSISTENT_NOTIFICATIONS_UPDATED, _callback
75  )
76 
77 
78 @bind_hass
79 def create(
80  hass: HomeAssistant,
81  message: str,
82  title: str | None = None,
83  notification_id: str | None = None,
84 ) -> None:
85  """Generate a notification."""
86  hass.add_job(async_create, hass, message, title, notification_id)
87 
88 
89 @bind_hass
90 def dismiss(hass: HomeAssistant, notification_id: str) -> None:
91  """Remove a notification."""
92  hass.add_job(async_dismiss, hass, notification_id)
93 
94 
95 @callback
96 @bind_hass
98  hass: HomeAssistant,
99  message: str,
100  title: str | None = None,
101  notification_id: str | None = None,
102 ) -> None:
103  """Generate a notification."""
104  notifications = _async_get_or_create_notifications(hass)
105  if notification_id is None:
106  notification_id = random_uuid_hex()
107  notifications[notification_id] = {
108  ATTR_MESSAGE: message,
109  ATTR_NOTIFICATION_ID: notification_id,
110  ATTR_TITLE: title,
111  ATTR_CREATED_AT: dt_util.utcnow(),
112  }
113 
115  hass,
116  SIGNAL_PERSISTENT_NOTIFICATIONS_UPDATED,
117  UpdateType.ADDED,
118  {notification_id: notifications[notification_id]},
119  )
120 
121 
122 @callback
123 @singleton.singleton(DOMAIN)
124 def _async_get_or_create_notifications(hass: HomeAssistant) -> dict[str, Notification]:
125  """Get or create notifications data."""
126  return {}
127 
128 
129 @callback
130 @bind_hass
131 def async_dismiss(hass: HomeAssistant, notification_id: str) -> None:
132  """Remove a notification."""
133  notifications = _async_get_or_create_notifications(hass)
134  if not (notification := notifications.pop(notification_id, None)):
135  return
137  hass,
138  SIGNAL_PERSISTENT_NOTIFICATIONS_UPDATED,
139  UpdateType.REMOVED,
140  {notification_id: notification},
141  )
142 
143 
144 @callback
145 def async_dismiss_all(hass: HomeAssistant) -> None:
146  """Remove all notifications."""
147  notifications = _async_get_or_create_notifications(hass)
148  notifications_copy = notifications.copy()
149  notifications.clear()
151  hass,
152  SIGNAL_PERSISTENT_NOTIFICATIONS_UPDATED,
153  UpdateType.REMOVED,
154  notifications_copy,
155  )
156 
157 
158 async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
159  """Set up the persistent notification component."""
160 
161  @callback
162  def create_service(call: ServiceCall) -> None:
163  """Handle a create notification service call."""
164  async_create(
165  hass,
166  call.data[ATTR_MESSAGE],
167  call.data.get(ATTR_TITLE),
168  call.data.get(ATTR_NOTIFICATION_ID),
169  )
170 
171  @callback
172  def dismiss_service(call: ServiceCall) -> None:
173  """Handle the dismiss notification service call."""
174  async_dismiss(hass, call.data[ATTR_NOTIFICATION_ID])
175 
176  @callback
177  def dismiss_all_service(call: ServiceCall) -> None:
178  """Handle the dismiss all notification service call."""
179  async_dismiss_all(hass)
180 
181  hass.services.async_register(
182  DOMAIN,
183  "create",
184  create_service,
185  vol.Schema(
186  {
187  vol.Required(ATTR_MESSAGE): cv.string,
188  vol.Optional(ATTR_TITLE): cv.string,
189  vol.Optional(ATTR_NOTIFICATION_ID): cv.string,
190  }
191  ),
192  )
193 
194  hass.services.async_register(
195  DOMAIN, "dismiss", dismiss_service, SCHEMA_SERVICE_NOTIFICATION
196  )
197 
198  hass.services.async_register(DOMAIN, "dismiss_all", dismiss_all_service, None)
199 
200  websocket_api.async_register_command(hass, websocket_get_notifications)
201  websocket_api.async_register_command(hass, websocket_subscribe_notifications)
202 
203  return True
204 
205 
206 @callback
207 @websocket_api.websocket_command({vol.Required("type"): "persistent_notification/get"})
209  hass: HomeAssistant,
210  connection: websocket_api.ActiveConnection,
211  msg: Mapping[str, Any],
212 ) -> None:
213  """Return a list of persistent_notifications."""
214  connection.send_message(
215  websocket_api.result_message(
216  msg["id"], list(_async_get_or_create_notifications(hass).values())
217  )
218  )
219 
220 
221 @callback
223  connection: websocket_api.ActiveConnection,
224  msg_id: int,
225  update_type: UpdateType,
226  notifications: dict[str, Notification],
227 ) -> None:
228  """Send persistent_notification update."""
229  connection.send_message(
230  websocket_api.event_message(
231  msg_id, {"type": update_type, "notifications": notifications}
232  )
233  )
234 
235 
236 @callback
237 @websocket_api.websocket_command( {vol.Required("type"): "persistent_notification/subscribe"}
238 )
240  hass: HomeAssistant,
241  connection: websocket_api.ActiveConnection,
242  msg: Mapping[str, Any],
243 ) -> None:
244  """Return a list of persistent_notifications."""
245  notifications = _async_get_or_create_notifications(hass)
246  msg_id = msg["id"]
247  notify_func = partial(_async_send_notification_update, connection, msg_id)
248  connection.subscriptions[msg_id] = async_dispatcher_connect(
249  hass, SIGNAL_PERSISTENT_NOTIFICATIONS_UPDATED, notify_func
250  )
251  connection.send_result(msg_id)
252  notify_func(UpdateType.CURRENT, notifications)
253 
None _async_send_notification_update(websocket_api.ActiveConnection connection, int msg_id, UpdateType update_type, dict[str, Notification] notifications)
Definition: __init__.py:227
None create(HomeAssistant hass, str message, str|None title=None, str|None notification_id=None)
Definition: __init__.py:84
dict[str, Notification] _async_get_or_create_notifications(HomeAssistant hass)
Definition: __init__.py:124
None dismiss(HomeAssistant hass, str notification_id)
Definition: __init__.py:90
None websocket_get_notifications(HomeAssistant hass, websocket_api.ActiveConnection connection, Mapping[str, Any] msg)
Definition: __init__.py:212
None async_dismiss_all(HomeAssistant hass)
Definition: __init__.py:145
None async_dismiss(HomeAssistant hass, str notification_id)
Definition: __init__.py:131
CALLBACK_TYPE async_register_callback(HomeAssistant hass, Callable[[UpdateType, dict[str, Notification]], None] _callback)
Definition: __init__.py:71
None websocket_subscribe_notifications(HomeAssistant hass, websocket_api.ActiveConnection connection, Mapping[str, Any] msg)
Definition: __init__.py:244
bool async_setup(HomeAssistant hass, ConfigType config)
Definition: __init__.py:158
None async_create(HomeAssistant hass, str message, str|None title=None, str|None notification_id=None)
Definition: __init__.py:102
Callable[[], None] async_dispatcher_connect(HomeAssistant hass, str signal, Callable[..., Any] target)
Definition: dispatcher.py:103
None async_dispatcher_send(HomeAssistant hass, str signal, *Any args)
Definition: dispatcher.py:193
str random_uuid_hex()
Definition: uuid.py:6