Home Assistant Unofficial Reference 2024.12.1
discovery.py
Go to the documentation of this file.
1 """Helper methods to help with platform discovery.
2 
3 There are two different types of discoveries that can be fired/listened for.
4  - listen/discover is for services. These are targeted at a component.
5  - listen_platform/discover_platform is for platforms. These are used by
6  components to allow discovery of their platforms.
7 """
8 
9 from __future__ import annotations
10 
11 from collections.abc import Callable, Coroutine
12 from typing import Any, TypedDict
13 
14 from homeassistant import core, setup
15 from homeassistant.const import Platform
16 from homeassistant.loader import bind_hass
17 from homeassistant.util.signal_type import SignalTypeFormat
18 
19 from .dispatcher import async_dispatcher_connect, async_dispatcher_send_internal
20 from .typing import ConfigType, DiscoveryInfoType
21 
22 SIGNAL_PLATFORM_DISCOVERED: SignalTypeFormat[DiscoveryDict] = SignalTypeFormat(
23  "discovery.platform_discovered_{}"
24 )
25 EVENT_LOAD_PLATFORM = "load_platform.{}"
26 ATTR_PLATFORM = "platform"
27 ATTR_DISCOVERED = "discovered"
28 
29 
30 class DiscoveryDict(TypedDict):
31  """Discovery data."""
32 
33  service: str
34  platform: str | None
35  discovered: DiscoveryInfoType | None
36 
37 
38 @core.callback
39 @bind_hass
41  hass: core.HomeAssistant,
42  service: str,
43  callback: Callable[
44  [str, DiscoveryInfoType | None], Coroutine[Any, Any, None] | None
45  ],
46 ) -> None:
47  """Set up listener for discovery of specific service.
48 
49  Service can be a string or a list/tuple.
50  """
51  job = core.HassJob(callback, f"discovery listener {service}")
52 
53  @core.callback
54  def _async_discovery_event_listener(discovered: DiscoveryDict) -> None:
55  """Listen for discovery events."""
56  hass.async_run_hass_job(job, discovered["service"], discovered["discovered"])
57 
59  hass,
60  SIGNAL_PLATFORM_DISCOVERED.format(service),
61  _async_discovery_event_listener,
62  )
63 
64 
65 @bind_hass
67  hass: core.HomeAssistant,
68  service: str,
69  discovered: DiscoveryInfoType,
70  component: str,
71  hass_config: ConfigType,
72 ) -> None:
73  """Fire discovery event. Can ensure a component is loaded."""
74  hass.create_task(
75  async_discover(hass, service, discovered, component, hass_config),
76  f"discover {service} {component} {discovered}",
77  )
78 
79 
80 @bind_hass
81 async def async_discover(
82  hass: core.HomeAssistant,
83  service: str,
84  discovered: DiscoveryInfoType | None,
85  component: str | None,
86  hass_config: ConfigType,
87 ) -> None:
88  """Fire discovery event. Can ensure a component is loaded."""
89  if component is not None and component not in hass.config.components:
90  await setup.async_setup_component(hass, component, hass_config)
91 
92  data: DiscoveryDict = {
93  "service": service,
94  "platform": None,
95  "discovered": discovered,
96  }
97 
98  async_dispatcher_send_internal(
99  hass, SIGNAL_PLATFORM_DISCOVERED.format(service), data
100  )
101 
102 
103 @bind_hass
105  hass: core.HomeAssistant,
106  component: str,
107  callback: Callable[[str, dict[str, Any] | None], Any],
108 ) -> Callable[[], None]:
109  """Register a platform loader listener.
110 
111  This method must be run in the event loop.
112  """
113  service = EVENT_LOAD_PLATFORM.format(component)
114  job = core.HassJob(callback, f"platform loaded {component}")
115 
116  @core.callback
117  def _async_discovery_platform_listener(discovered: DiscoveryDict) -> None:
118  """Listen for platform discovery events."""
119  if not (platform := discovered["platform"]):
120  return
121  hass.async_run_hass_job(job, platform, discovered.get("discovered"))
122 
124  hass,
125  SIGNAL_PLATFORM_DISCOVERED.format(service),
126  _async_discovery_platform_listener,
127  )
128 
129 
130 @bind_hass
132  hass: core.HomeAssistant,
133  component: Platform | str,
134  platform: str,
135  discovered: DiscoveryInfoType | None,
136  hass_config: ConfigType,
137 ) -> None:
138  """Load a component and platform dynamically."""
139  hass.create_task(
140  async_load_platform(hass, component, platform, discovered, hass_config),
141  f"discovery load_platform {component} {platform}",
142  )
143 
144 
145 @bind_hass
147  hass: core.HomeAssistant,
148  component: Platform | str,
149  platform: str,
150  discovered: DiscoveryInfoType | None,
151  hass_config: ConfigType,
152 ) -> None:
153  """Load a component and platform dynamically.
154 
155  Use `async_listen_platform` to register a callback for these events.
156 
157  Warning: This method can load a base component if its not loaded which
158  can take a long time since base components currently have to import
159  every platform integration listed under it to do config validation.
160  To avoid waiting for this, use
161  `hass.async_create_task(async_load_platform(..))` instead.
162  """
163  assert hass_config is not None, "You need to pass in the real hass config"
164 
165  setup_success = True
166 
167  if component not in hass.config.components:
168  setup_success = await setup.async_setup_component(hass, component, hass_config)
169 
170  # No need to send signal if we could not set up component
171  if not setup_success:
172  return
173 
174  service = EVENT_LOAD_PLATFORM.format(component)
175 
176  data: DiscoveryDict = {
177  "service": service,
178  "platform": platform,
179  "discovered": discovered,
180  }
181 
182  async_dispatcher_send_internal(
183  hass, SIGNAL_PLATFORM_DISCOVERED.format(service), data
184  )
None discover(core.HomeAssistant hass, str service, DiscoveryInfoType discovered, str component, ConfigType hass_config)
Definition: discovery.py:72
None async_load_platform(core.HomeAssistant hass, Platform|str component, str platform, DiscoveryInfoType|None discovered, ConfigType hass_config)
Definition: discovery.py:152
None load_platform(core.HomeAssistant hass, Platform|str component, str platform, DiscoveryInfoType|None discovered, ConfigType hass_config)
Definition: discovery.py:137
None async_discover(core.HomeAssistant hass, str service, DiscoveryInfoType|None discovered, str|None component, ConfigType hass_config)
Definition: discovery.py:87
None async_listen(core.HomeAssistant hass, str service, Callable[[str, DiscoveryInfoType|None], Coroutine[Any, Any, None]|None] callback)
Definition: discovery.py:46
Callable[[], None] async_listen_platform(core.HomeAssistant hass, str component, Callable[[str, dict[str, Any]|None], Any] callback)
Definition: discovery.py:108
Callable[[], None] async_dispatcher_connect(HomeAssistant hass, str signal, Callable[..., Any] target)
Definition: dispatcher.py:103