Home Assistant Unofficial Reference 2024.12.1
device_trigger.py
Go to the documentation of this file.
1 """Provides device triggers for BTHome BLE."""
2 
3 from __future__ import annotations
4 
5 from typing import TYPE_CHECKING, Any
6 
7 import voluptuous as vol
8 
10  DEVICE_TRIGGER_BASE_SCHEMA,
11  InvalidDeviceAutomationConfig,
12 )
13 from homeassistant.components.homeassistant.triggers import event as event_trigger
14 from homeassistant.const import (
15  CONF_DEVICE_ID,
16  CONF_DOMAIN,
17  CONF_EVENT,
18  CONF_PLATFORM,
19  CONF_TYPE,
20 )
21 from homeassistant.core import CALLBACK_TYPE, HomeAssistant
22 from homeassistant.helpers import device_registry as dr
23 from homeassistant.helpers.trigger import TriggerActionType, TriggerInfo
24 from homeassistant.helpers.typing import ConfigType
25 
26 from .const import (
27  BTHOME_BLE_EVENT,
28  CONF_DISCOVERED_EVENT_CLASSES,
29  CONF_SUBTYPE,
30  DOMAIN,
31  EVENT_CLASS,
32  EVENT_CLASS_BUTTON,
33  EVENT_CLASS_DIMMER,
34  EVENT_TYPE,
35 )
36 
37 EVENT_TYPES_BY_EVENT_CLASS = {
38  EVENT_CLASS_BUTTON: {
39  "press",
40  "double_press",
41  "triple_press",
42  "long_press",
43  "long_double_press",
44  "long_triple_press",
45  },
46  EVENT_CLASS_DIMMER: {"rotate_left", "rotate_right"},
47 }
48 
49 TRIGGER_SCHEMA = DEVICE_TRIGGER_BASE_SCHEMA.extend(
50  {vol.Required(CONF_TYPE): str, vol.Required(CONF_SUBTYPE): str}
51 )
52 
53 
54 def get_event_classes_by_device_id(hass: HomeAssistant, device_id: str) -> list[str]:
55  """Get the supported event classes for a device.
56 
57  Events for BTHome BLE devices are dynamically discovered
58  and stored in the device config entry when they are first seen.
59  """
60  device_registry = dr.async_get(hass)
61  device = device_registry.async_get(device_id)
62  if TYPE_CHECKING:
63  assert device is not None
64 
65  config_entries = [
66  hass.config_entries.async_get_entry(entry_id)
67  for entry_id in device.config_entries
68  ]
69  bthome_config_entry = next(
70  entry for entry in config_entries if entry and entry.domain == DOMAIN
71  )
72  return bthome_config_entry.data.get(CONF_DISCOVERED_EVENT_CLASSES, [])
73 
74 
75 def get_event_types_by_event_class(event_class: str) -> set[str]:
76  """Get the supported event types for an event class.
77 
78  If the device has multiple buttons they will have
79  event classes like button_1 button_2, button_3, etc
80  but if there is only one button then it will be
81  button without a number postfix.
82  """
83  return EVENT_TYPES_BY_EVENT_CLASS.get(event_class.split("_")[0], set())
84 
85 
87  hass: HomeAssistant, config: ConfigType
88 ) -> ConfigType:
89  """Validate trigger config."""
90  config = TRIGGER_SCHEMA(config)
91  event_class = config[CONF_TYPE]
92  event_type = config[CONF_SUBTYPE]
93  device_id = config[CONF_DEVICE_ID]
94  event_classes = get_event_classes_by_device_id(hass, device_id)
95 
96  if event_class not in event_classes:
97  raise InvalidDeviceAutomationConfig(
98  f"BTHome trigger {event_class} is not valid for device_id '{device_id}'"
99  )
100 
101  if event_type not in get_event_types_by_event_class(event_class):
102  raise InvalidDeviceAutomationConfig(
103  f"BTHome trigger {event_type} is not valid for device_id '{device_id}'"
104  )
105 
106  return config
107 
108 
110  hass: HomeAssistant, device_id: str
111 ) -> list[dict[str, Any]]:
112  """Return a list of triggers for BTHome BLE devices."""
113  event_classes = get_event_classes_by_device_id(hass, device_id)
114  return [
115  {
116  # Required fields of TRIGGER_BASE_SCHEMA
117  CONF_PLATFORM: "device",
118  CONF_DEVICE_ID: device_id,
119  CONF_DOMAIN: DOMAIN,
120  # Required fields of TRIGGER_SCHEMA
121  CONF_TYPE: event_class,
122  CONF_SUBTYPE: event_type,
123  }
124  for event_class in event_classes
125  for event_type in get_event_types_by_event_class(event_class)
126  ]
127 
128 
130  hass: HomeAssistant,
131  config: ConfigType,
132  action: TriggerActionType,
133  trigger_info: TriggerInfo,
134 ) -> CALLBACK_TYPE:
135  """Attach a trigger."""
136  return await event_trigger.async_attach_trigger(
137  hass,
138  event_trigger.TRIGGER_SCHEMA(
139  {
140  event_trigger.CONF_PLATFORM: CONF_EVENT,
141  event_trigger.CONF_EVENT_TYPE: BTHOME_BLE_EVENT,
142  event_trigger.CONF_EVENT_DATA: {
143  CONF_DEVICE_ID: config[CONF_DEVICE_ID],
144  EVENT_CLASS: config[CONF_TYPE],
145  EVENT_TYPE: config[CONF_SUBTYPE],
146  },
147  }
148  ),
149  action,
150  trigger_info,
151  platform_type="device",
152  )
CALLBACK_TYPE async_attach_trigger(HomeAssistant hass, ConfigType config, TriggerActionType action, TriggerInfo trigger_info)
set[str] get_event_types_by_event_class(str event_class)
ConfigType async_validate_trigger_config(HomeAssistant hass, ConfigType config)
list[str] get_event_classes_by_device_id(HomeAssistant hass, str device_id)
list[dict[str, Any]] async_get_triggers(HomeAssistant hass, str device_id)