Home Assistant Unofficial Reference 2024.12.1
notify.py
Go to the documentation of this file.
1 """Group platform for notify component."""
2 
3 from __future__ import annotations
4 
5 import asyncio
6 from collections.abc import Mapping
7 from copy import deepcopy
8 from typing import Any
9 
10 import voluptuous as vol
11 
13  ATTR_DATA,
14  ATTR_MESSAGE,
15  ATTR_TITLE,
16  DOMAIN as NOTIFY_DOMAIN,
17  PLATFORM_SCHEMA as NOTIFY_PLATFORM_SCHEMA,
18  SERVICE_SEND_MESSAGE,
19  BaseNotificationService,
20  NotifyEntity,
21 )
22 from homeassistant.config_entries import ConfigEntry
23 from homeassistant.const import (
24  ATTR_ENTITY_ID,
25  CONF_ACTION,
26  CONF_ENTITIES,
27  CONF_SERVICE,
28  STATE_UNAVAILABLE,
29 )
30 from homeassistant.core import HomeAssistant, callback
32 from homeassistant.helpers.entity_platform import AddEntitiesCallback
34 from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
35 
36 from .entity import GroupEntity
37 
38 CONF_SERVICES = "services"
39 
40 
41 def _backward_compat_schema(value: Any | None) -> Any:
42  """Backward compatibility for notify service schemas."""
43 
44  if not isinstance(value, dict):
45  return value
46 
47  # `service` has been renamed to `action`
48  if CONF_SERVICE in value:
49  if CONF_ACTION in value:
50  raise vol.Invalid(
51  "Cannot specify both 'service' and 'action'. Please use 'action' only."
52  )
53  value[CONF_ACTION] = value.pop(CONF_SERVICE)
54 
55  return value
56 
57 
58 PLATFORM_SCHEMA = NOTIFY_PLATFORM_SCHEMA.extend(
59  {
60  vol.Required(CONF_SERVICES): vol.All(
61  cv.ensure_list,
62  [
63  vol.All(
64  _backward_compat_schema,
65  {
66  vol.Required(CONF_ACTION): cv.slug,
67  vol.Optional(ATTR_DATA): dict,
68  },
69  )
70  ],
71  )
72  }
73 )
74 
75 
77  input_data: dict[str, Any], default_data: Mapping[str, Any]
78 ) -> dict[str, Any]:
79  """Deep update a dictionary with default values."""
80  for key, val in default_data.items():
81  if isinstance(val, Mapping):
82  input_data[key] = add_defaults(input_data.get(key, {}), val)
83  elif key not in input_data:
84  input_data[key] = val
85  return input_data
86 
87 
89  hass: HomeAssistant,
90  config: ConfigType,
91  discovery_info: DiscoveryInfoType | None = None,
92 ) -> GroupNotifyPlatform:
93  """Get the Group notification service."""
94  return GroupNotifyPlatform(hass, config[CONF_SERVICES])
95 
96 
97 class GroupNotifyPlatform(BaseNotificationService):
98  """Implement the notification service for the group notify platform."""
99 
100  def __init__(self, hass: HomeAssistant, entities: list[dict[str, Any]]) -> None:
101  """Initialize the service."""
102  self.hasshass = hass
103  self.entitiesentities = entities
104 
105  async def async_send_message(self, message: str = "", **kwargs: Any) -> None:
106  """Send message to all entities in the group."""
107  payload: dict[str, Any] = {ATTR_MESSAGE: message}
108  payload.update({key: val for key, val in kwargs.items() if val})
109 
110  tasks: list[asyncio.Task[Any]] = []
111  for entity in self.entitiesentities:
112  sending_payload = deepcopy(payload.copy())
113  if (default_data := entity.get(ATTR_DATA)) is not None:
114  add_defaults(sending_payload, default_data)
115  tasks.append(
116  asyncio.create_task(
117  self.hasshass.services.async_call(
118  NOTIFY_DOMAIN,
119  entity[CONF_ACTION],
120  sending_payload,
121  blocking=True,
122  )
123  )
124  )
125 
126  if tasks:
127  await asyncio.wait(tasks)
128 
129 
131  hass: HomeAssistant,
132  config_entry: ConfigEntry,
133  async_add_entities: AddEntitiesCallback,
134 ) -> None:
135  """Initialize Notify Group config entry."""
136  registry = er.async_get(hass)
137  entities = er.async_validate_entity_ids(
138  registry, config_entry.options[CONF_ENTITIES]
139  )
140 
142  [NotifyGroup(config_entry.entry_id, config_entry.title, entities)]
143  )
144 
145 
146 @callback
148  hass: HomeAssistant, name: str, validated_config: dict[str, Any]
149 ) -> NotifyGroup:
150  """Create a preview notify group."""
151  return NotifyGroup(
152  None,
153  name,
154  validated_config[CONF_ENTITIES],
155  )
156 
157 
159  """Representation of a NotifyGroup."""
160 
161  _attr_available: bool = False
162 
163  def __init__(
164  self,
165  unique_id: str | None,
166  name: str,
167  entity_ids: list[str],
168  ) -> None:
169  """Initialize a NotifyGroup."""
170  self._entity_ids_entity_ids = entity_ids
171  self._attr_name_attr_name = name
172  self._attr_extra_state_attributes_attr_extra_state_attributes = {ATTR_ENTITY_ID: entity_ids}
173  self._attr_unique_id_attr_unique_id = unique_id
174 
175  async def async_send_message(self, message: str, title: str | None = None) -> None:
176  """Send a message to all members of the group."""
177  await self.hasshass.services.async_call(
178  NOTIFY_DOMAIN,
179  SERVICE_SEND_MESSAGE,
180  {
181  ATTR_MESSAGE: message,
182  ATTR_TITLE: title,
183  ATTR_ENTITY_ID: self._entity_ids_entity_ids,
184  },
185  blocking=True,
186  context=self._context_context,
187  )
188 
189  @callback
190  def async_update_group_state(self) -> None:
191  """Query all members and determine the notify group state."""
192  # Set group as unavailable if all members are unavailable or missing
193  self._attr_available_attr_available = any(
194  state.state != STATE_UNAVAILABLE
195  for entity_id in self._entity_ids_entity_ids
196  if (state := self.hasshass.states.get(entity_id)) is not None
197  )
None async_send_message(self, str message="", **Any kwargs)
Definition: notify.py:105
None __init__(self, HomeAssistant hass, list[dict[str, Any]] entities)
Definition: notify.py:100
None async_send_message(self, str message, str|None title=None)
Definition: notify.py:175
None __init__(self, str|None unique_id, str name, list[str] entity_ids)
Definition: notify.py:168
GroupNotifyPlatform async_get_service(HomeAssistant hass, ConfigType config, DiscoveryInfoType|None discovery_info=None)
Definition: notify.py:92
dict[str, Any] add_defaults(dict[str, Any] input_data, Mapping[str, Any] default_data)
Definition: notify.py:78
NotifyGroup async_create_preview_notify(HomeAssistant hass, str name, dict[str, Any] validated_config)
Definition: notify.py:149
Any _backward_compat_schema(Any|None value)
Definition: notify.py:41
None async_setup_entry(HomeAssistant hass, ConfigEntry config_entry, AddEntitiesCallback async_add_entities)
Definition: notify.py:134