Home Assistant Unofficial Reference 2024.12.1
__init__.py
Go to the documentation of this file.
1 """Provides functionality to notify people."""
2 
3 from __future__ import annotations
4 
5 from datetime import timedelta
6 from enum import IntFlag
7 from functools import partial
8 import logging
9 from typing import Any, final, override
10 
11 from propcache import cached_property
12 import voluptuous as vol
13 
15 from homeassistant.config_entries import ConfigEntry
16 from homeassistant.const import CONF_NAME, CONF_PLATFORM, STATE_UNAVAILABLE
17 from homeassistant.core import HomeAssistant, ServiceCall
19 from homeassistant.helpers.entity import EntityDescription
20 from homeassistant.helpers.entity_component import EntityComponent
21 from homeassistant.helpers.restore_state import RestoreEntity
22 from homeassistant.helpers.typing import ConfigType
23 from homeassistant.util import dt as dt_util
24 from homeassistant.util.hass_dict import HassKey
25 
26 from .const import ( # noqa: F401
27  ATTR_DATA,
28  ATTR_MESSAGE,
29  ATTR_RECIPIENTS,
30  ATTR_TARGET,
31  ATTR_TITLE,
32  DOMAIN,
33  NOTIFY_SERVICE_SCHEMA,
34  SERVICE_NOTIFY,
35  SERVICE_PERSISTENT_NOTIFICATION,
36  SERVICE_SEND_MESSAGE,
37 )
38 from .legacy import ( # noqa: F401
39  BaseNotificationService,
40  async_reload,
41  async_reset_platform,
42  async_setup_legacy,
43 )
44 from .repairs import migrate_notify_issue # noqa: F401
45 
46 # mypy: disallow-any-generics
47 
48 # Platform specific data
49 ATTR_TITLE_DEFAULT = "Home Assistant"
50 
51 DATA_COMPONENT: HassKey[EntityComponent[NotifyEntity]] = HassKey(DOMAIN)
52 ENTITY_ID_FORMAT = DOMAIN + ".{}"
53 
54 MIN_TIME_BETWEEN_SCANS = timedelta(seconds=10)
55 
56 _LOGGER = logging.getLogger(__name__)
57 
58 PLATFORM_SCHEMA = vol.Schema(
59  {vol.Required(CONF_PLATFORM): cv.string, vol.Optional(CONF_NAME): cv.string},
60  extra=vol.ALLOW_EXTRA,
61 )
62 
63 
64 class NotifyEntityFeature(IntFlag):
65  """Supported features of a notify entity."""
66 
67  TITLE = 1
68 
69 
70 async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
71  """Set up the notify services."""
72 
73  for setup in async_setup_legacy(hass, config):
74  # Tasks are created as tracked tasks to ensure startup
75  # waits for them to finish, but we explicitly do not
76  # want to wait for them to finish here because we want
77  # any config entries that use notify as a base platform
78  # to be able to start with out having to wait for the
79  # legacy platforms to finish setting up.
80  hass.async_create_task(setup, eager_start=True)
81 
82  component = hass.data[DATA_COMPONENT] = EntityComponent[NotifyEntity](
83  _LOGGER, DOMAIN, hass
84  )
85  component.async_register_entity_service(
86  SERVICE_SEND_MESSAGE,
87  {
88  vol.Required(ATTR_MESSAGE): cv.string,
89  vol.Optional(ATTR_TITLE): cv.string,
90  },
91  "_async_send_message",
92  )
93 
94  async def persistent_notification(service: ServiceCall) -> None:
95  """Send notification via the built-in persistent_notify integration."""
96  message: str = service.data[ATTR_MESSAGE]
97  title: str | None = service.data.get(ATTR_TITLE)
98 
99  notification_id = None
100  if data := service.data.get(ATTR_DATA):
101  notification_id = data.get(pn.ATTR_NOTIFICATION_ID)
102 
103  pn.async_create(hass, message, title, notification_id)
104 
105  hass.services.async_register(
106  DOMAIN,
107  SERVICE_PERSISTENT_NOTIFICATION,
108  persistent_notification,
109  schema=NOTIFY_SERVICE_SCHEMA,
110  )
111 
112  return True
113 
114 
115 class NotifyEntityDescription(EntityDescription, frozen_or_thawed=True):
116  """A class that describes button entities."""
117 
118 
119 async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
120  """Set up a config entry."""
121  return await hass.data[DATA_COMPONENT].async_setup_entry(entry)
122 
123 
124 async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
125  """Unload a config entry."""
126  return await hass.data[DATA_COMPONENT].async_unload_entry(entry)
127 
128 
130  """Representation of a notify entity."""
131 
132  entity_description: NotifyEntityDescription
133  _attr_supported_features: NotifyEntityFeature = NotifyEntityFeature(0)
134  _attr_should_poll = False
135  _attr_device_class: None
136  _attr_state: None = None
137  __last_notified_isoformat: str | None = None
138 
139  @cached_property
140  @final
141  @override
142  def state(self) -> str | None:
143  """Return the entity state."""
144  return self.__last_notified_isoformat__last_notified_isoformat
145 
146  def __set_state(self, state: str | None) -> None:
147  """Invalidate the cache of the cached property."""
148  self.__dict__.pop("state", None)
149  self.__last_notified_isoformat__last_notified_isoformat = state
150 
151  async def async_internal_added_to_hass(self) -> None:
152  """Call when the notify entity is added to hass."""
153  await super().async_internal_added_to_hass()
154  state = await self.async_get_last_stateasync_get_last_state()
155  if state is not None and state.state not in (STATE_UNAVAILABLE, None):
156  self.__set_state__set_state(state.state)
157 
158  @final
159  async def _async_send_message(self, **kwargs: Any) -> None:
160  """Send a notification message (from e.g., service call).
161 
162  Should not be overridden, handle setting last notification timestamp.
163  """
164  self.__set_state__set_state(dt_util.utcnow().isoformat())
165  self.async_write_ha_stateasync_write_ha_state()
166  await self.async_send_messageasync_send_message(**kwargs)
167 
168  def send_message(self, message: str, title: str | None = None) -> None:
169  """Send a message."""
170  raise NotImplementedError
171 
172  async def async_send_message(self, message: str, title: str | None = None) -> None:
173  """Send a message."""
174  kwargs: dict[str, Any] = {}
175  if (
176  title is not None
177  and self.supported_featuressupported_features
178  and self.supported_featuressupported_features & NotifyEntityFeature.TITLE
179  ):
180  kwargs[ATTR_TITLE] = title
181  await self.hasshass.async_add_executor_job(
182  partial(self.send_messagesend_message, message, **kwargs)
183  )
None __set_state(self, str|None state)
Definition: __init__.py:146
None async_send_message(self, str message, str|None title=None)
Definition: __init__.py:172
None _async_send_message(self, **Any kwargs)
Definition: __init__.py:159
None send_message(self, str message, str|None title=None)
Definition: __init__.py:168
int|None supported_features(self)
Definition: entity.py:861
list[Coroutine[Any, Any, None]] async_setup_legacy(HomeAssistant hass, ConfigType config)
Definition: legacy.py:70
bool async_setup(HomeAssistant hass, ConfigType config)
Definition: __init__.py:70
bool async_unload_entry(HomeAssistant hass, ConfigEntry entry)
Definition: __init__.py:124
bool async_setup_entry(HomeAssistant hass, ConfigEntry entry)
Definition: __init__.py:119