Home Assistant Unofficial Reference 2024.12.1
trigger.py
Go to the documentation of this file.
1 """Offer webhook triggered automation rules."""
2 
3 from __future__ import annotations
4 
5 from dataclasses import dataclass
6 import logging
7 from typing import Any
8 
9 from aiohttp import hdrs, web
10 import voluptuous as vol
11 
12 from homeassistant.const import CONF_PLATFORM, CONF_WEBHOOK_ID
13 from homeassistant.core import CALLBACK_TYPE, HassJob, HomeAssistant, callback
15 from homeassistant.helpers.trigger import TriggerActionType, TriggerInfo
16 from homeassistant.helpers.typing import ConfigType
17 
18 from . import (
19  DEFAULT_METHODS,
20  DOMAIN,
21  SUPPORTED_METHODS,
22  async_register,
23  async_unregister,
24 )
25 
26 _LOGGER = logging.getLogger(__name__)
27 
28 DEPENDENCIES = ("webhook",)
29 
30 CONF_ALLOWED_METHODS = "allowed_methods"
31 CONF_LOCAL_ONLY = "local_only"
32 
33 TRIGGER_SCHEMA = cv.TRIGGER_BASE_SCHEMA.extend(
34  {
35  vol.Required(CONF_PLATFORM): "webhook",
36  vol.Required(CONF_WEBHOOK_ID): cv.string,
37  vol.Optional(CONF_ALLOWED_METHODS): vol.All(
38  cv.ensure_list,
39  [vol.All(vol.Upper, vol.In(SUPPORTED_METHODS))],
40  vol.Unique(),
41  ),
42  vol.Optional(CONF_LOCAL_ONLY): bool,
43  }
44 )
45 
46 WEBHOOK_TRIGGERS = f"{DOMAIN}_triggers"
47 
48 
49 @dataclass(slots=True)
51  """Attached trigger settings."""
52 
53  trigger_info: TriggerInfo
54  job: HassJob
55 
56 
57 async def _handle_webhook(
58  hass: HomeAssistant, webhook_id: str, request: web.Request
59 ) -> None:
60  """Handle incoming webhook."""
61  base_result: dict[str, Any] = {"platform": "webhook", "webhook_id": webhook_id}
62 
63  if "json" in request.headers.get(hdrs.CONTENT_TYPE, ""):
64  base_result["json"] = await request.json()
65  else:
66  base_result["data"] = await request.post()
67 
68  base_result["query"] = request.query
69  base_result["description"] = "webhook"
70 
71  triggers: dict[str, list[TriggerInstance]] = hass.data.setdefault(
72  WEBHOOK_TRIGGERS, {}
73  )
74  for trigger in triggers[webhook_id]:
75  result = {**base_result, **trigger.trigger_info["trigger_data"]}
76  hass.async_run_hass_job(trigger.job, {"trigger": result})
77 
78 
80  hass: HomeAssistant,
81  config: ConfigType,
82  action: TriggerActionType,
83  trigger_info: TriggerInfo,
84 ) -> CALLBACK_TYPE:
85  """Trigger based on incoming webhooks."""
86  webhook_id: str = config[CONF_WEBHOOK_ID]
87  local_only = config.get(CONF_LOCAL_ONLY, True)
88  allowed_methods = config.get(CONF_ALLOWED_METHODS, DEFAULT_METHODS)
89  job = HassJob(action)
90 
91  triggers: dict[str, list[TriggerInstance]] = hass.data.setdefault(
92  WEBHOOK_TRIGGERS, {}
93  )
94 
95  if webhook_id not in triggers:
97  hass,
98  trigger_info["domain"],
99  trigger_info["name"],
100  webhook_id,
101  _handle_webhook,
102  local_only=local_only,
103  allowed_methods=allowed_methods,
104  )
105  triggers[webhook_id] = []
106 
107  trigger_instance = TriggerInstance(trigger_info, job)
108  triggers[webhook_id].append(trigger_instance)
109 
110  @callback
111  def unregister() -> None:
112  """Unregister webhook."""
113  triggers[webhook_id].remove(trigger_instance)
114  if not triggers[webhook_id]:
115  async_unregister(hass, webhook_id)
116  triggers.pop(webhook_id)
117 
118  return unregister
bool remove(self, _T matcher)
Definition: match.py:214
CALLBACK_TYPE async_attach_trigger(HomeAssistant hass, ConfigType config, TriggerActionType action, TriggerInfo trigger_info)
Definition: trigger.py:84
None _handle_webhook(HomeAssistant hass, str webhook_id, web.Request request)
Definition: trigger.py:59
None async_unregister(HomeAssistant hass, str webhook_id)
Definition: __init__.py:76
None async_register(HomeAssistant hass, str domain, str name, str webhook_id, Callable[[HomeAssistant, str, Request], Awaitable[Response|None]] handler, *bool|None local_only=False, Iterable[str]|None allowed_methods=None)
Definition: __init__.py:49