Home Assistant Unofficial Reference 2024.12.1
push_notification.py
Go to the documentation of this file.
1 """Push notification handling."""
2 
3 from __future__ import annotations
4 
5 import asyncio
6 from collections.abc import Callable
7 
8 from homeassistant.core import HomeAssistant, callback
9 from homeassistant.helpers.event import async_call_later
10 from homeassistant.util.uuid import random_uuid_hex
11 
12 PUSH_CONFIRM_TIMEOUT = 10 # seconds
13 
14 
16  """Class that represents a push channel."""
17 
18  def __init__(
19  self,
20  hass: HomeAssistant,
21  webhook_id: str,
22  support_confirm: bool,
23  send_message: Callable[[dict], None],
24  on_teardown: Callable[[], None],
25  ) -> None:
26  """Initialize a local push channel."""
27  self.hasshass = hass
28  self.webhook_idwebhook_id = webhook_id
29  self.support_confirmsupport_confirm = support_confirm
30  self._send_message_send_message = send_message
31  self.on_teardownon_teardown = on_teardown
32  self.pending_confirms: dict[str, dict] = {}
33 
34  @callback
35  def async_send_notification(self, data, fallback_send):
36  """Send a push notification."""
37  if not self.support_confirmsupport_confirm:
38  self._send_message_send_message(data)
39  return
40 
41  confirm_id = random_uuid_hex()
42  data["hass_confirm_id"] = confirm_id
43 
44  async def handle_push_failed(_=None):
45  """Handle a failed local push notification."""
46  # Remove this handler from the pending dict
47  # If it didn't exist we hit a race condition between call_later and another
48  # push failing and tearing down the connection.
49  if self.pending_confirms.pop(confirm_id, None) is None:
50  return
51 
52  # Drop local channel if it's still open
53  if self.on_teardownon_teardown is not None:
54  await self.async_teardownasync_teardown()
55 
56  await fallback_send(data)
57 
58  self.pending_confirms[confirm_id] = {
59  "unsub_scheduled_push_failed": async_call_later(
60  self.hasshass, PUSH_CONFIRM_TIMEOUT, handle_push_failed
61  ),
62  "handle_push_failed": handle_push_failed,
63  }
64  self._send_message_send_message(data)
65 
66  @callback
67  def async_confirm_notification(self, confirm_id) -> bool:
68  """Confirm a push notification.
69 
70  Returns if confirmation successful.
71  """
72  if confirm_id not in self.pending_confirms:
73  return False
74 
75  self.pending_confirms.pop(confirm_id)["unsub_scheduled_push_failed"]()
76  return True
77 
78  async def async_teardown(self):
79  """Tear down this channel."""
80  # Tear down is in progress
81  if self.on_teardownon_teardown is None:
82  return
83 
84  self.on_teardownon_teardown()
85  self.on_teardownon_teardown = None
86 
87  cancel_pending_local_tasks = [
88  actions["handle_push_failed"]()
89  for actions in self.pending_confirms.values()
90  ]
91 
92  if cancel_pending_local_tasks:
93  await asyncio.gather(*cancel_pending_local_tasks)
None __init__(self, HomeAssistant hass, str webhook_id, bool support_confirm, Callable[[dict], None] send_message, Callable[[], None] on_teardown)
CALLBACK_TYPE async_call_later(HomeAssistant hass, float|timedelta delay, HassJob[[datetime], Coroutine[Any, Any, None]|None]|Callable[[datetime], Coroutine[Any, Any, None]|None] action)
Definition: event.py:1597
str random_uuid_hex()
Definition: uuid.py:6