Home Assistant Unofficial Reference 2024.12.1
__init__.py
Go to the documentation of this file.
1 """The template component."""
2 
3 from __future__ import annotations
4 
5 import asyncio
6 from collections.abc import Coroutine
7 import logging
8 from typing import Any
9 
10 from homeassistant import config as conf_util
11 from homeassistant.config_entries import ConfigEntry
12 from homeassistant.const import (
13  CONF_DEVICE_ID,
14  CONF_NAME,
15  CONF_UNIQUE_ID,
16  SERVICE_RELOAD,
17 )
18 from homeassistant.core import Event, HomeAssistant, ServiceCall
19 from homeassistant.exceptions import ConfigEntryError, HomeAssistantError
20 from homeassistant.helpers import discovery
21 from homeassistant.helpers.device import (
22  async_remove_stale_devices_links_keep_current_device,
23 )
24 from homeassistant.helpers.reload import async_reload_integration_platforms
25 from homeassistant.helpers.service import async_register_admin_service
26 from homeassistant.helpers.typing import ConfigType
27 from homeassistant.loader import async_get_integration
28 from homeassistant.util.hass_dict import HassKey
29 
30 from .const import CONF_MAX, CONF_MIN, CONF_STEP, CONF_TRIGGER, DOMAIN, PLATFORMS
31 from .coordinator import TriggerUpdateCoordinator
32 from .helpers import async_get_blueprints
33 
34 _LOGGER = logging.getLogger(__name__)
35 DATA_COORDINATORS: HassKey[list[TriggerUpdateCoordinator]] = HassKey(DOMAIN)
36 
37 
38 async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
39  """Set up the template integration."""
40 
41  # Register template as valid domain for Blueprint
42  blueprints = async_get_blueprints(hass)
43 
44  # Add some default blueprints to blueprints/template, does nothing
45  # if blueprints/template already exists but still has to create
46  # an executor job to check if the folder exists so we run it in a
47  # separate task to avoid waiting for it to finish setting up
48  # since a tracked task will be waited at the end of startup
49  hass.async_create_task(blueprints.async_populate(), eager_start=True)
50 
51  if DOMAIN in config:
52  await _process_config(hass, config)
53 
54  async def _reload_config(call: Event | ServiceCall) -> None:
55  """Reload top-level + platforms."""
56  try:
57  unprocessed_conf = await conf_util.async_hass_config_yaml(hass)
58  except HomeAssistantError as err:
59  _LOGGER.error(err)
60  return
61 
62  integration = await async_get_integration(hass, DOMAIN)
63  conf = await conf_util.async_process_component_and_handle_errors(
64  hass, unprocessed_conf, integration
65  )
66 
67  if conf is None:
68  return
69 
70  await async_reload_integration_platforms(hass, DOMAIN, PLATFORMS)
71 
72  if DOMAIN in conf:
73  await _process_config(hass, conf)
74 
75  hass.bus.async_fire(f"event_{DOMAIN}_reloaded", context=call.context)
76 
77  async_register_admin_service(hass, DOMAIN, SERVICE_RELOAD, _reload_config)
78 
79  return True
80 
81 
82 async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
83  """Set up a config entry."""
84 
86  hass,
87  entry.entry_id,
88  entry.options.get(CONF_DEVICE_ID),
89  )
90 
91  for key in (CONF_MAX, CONF_MIN, CONF_STEP):
92  if key not in entry.options:
93  continue
94  if isinstance(entry.options[key], str):
95  raise ConfigEntryError(
96  f"The '{entry.options.get(CONF_NAME) or ""}' number template needs to "
97  f"be reconfigured, {key} must be a number, got '{entry.options[key]}'"
98  )
99 
100  await hass.config_entries.async_forward_entry_setups(
101  entry, (entry.options["template_type"],)
102  )
103  entry.async_on_unload(entry.add_update_listener(config_entry_update_listener))
104  return True
105 
106 
107 async def config_entry_update_listener(hass: HomeAssistant, entry: ConfigEntry) -> None:
108  """Update listener, called when the config entry options are changed."""
109  await hass.config_entries.async_reload(entry.entry_id)
110 
111 
112 async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
113  """Unload a config entry."""
114  return await hass.config_entries.async_unload_platforms(
115  entry, (entry.options["template_type"],)
116  )
117 
118 
119 async def _process_config(hass: HomeAssistant, hass_config: ConfigType) -> None:
120  """Process config."""
121  coordinators = hass.data.pop(DATA_COORDINATORS, None)
122 
123  # Remove old ones
124  if coordinators:
125  for coordinator in coordinators:
126  coordinator.async_remove()
127 
128  async def init_coordinator(
129  hass: HomeAssistant, conf_section: dict[str, Any]
130  ) -> TriggerUpdateCoordinator:
131  coordinator = TriggerUpdateCoordinator(hass, conf_section)
132  await coordinator.async_setup(hass_config)
133  return coordinator
134 
135  coordinator_tasks: list[Coroutine[Any, Any, TriggerUpdateCoordinator]] = []
136 
137  for conf_section in hass_config[DOMAIN]:
138  if CONF_TRIGGER in conf_section:
139  coordinator_tasks.append(init_coordinator(hass, conf_section))
140  continue
141 
142  for platform_domain in PLATFORMS:
143  if platform_domain in conf_section:
144  hass.async_create_task(
145  discovery.async_load_platform(
146  hass,
147  platform_domain,
148  DOMAIN,
149  {
150  "unique_id": conf_section.get(CONF_UNIQUE_ID),
151  "entities": [
152  {
153  **entity_conf,
154  "raw_blueprint_inputs": conf_section.raw_blueprint_inputs,
155  "raw_configs": conf_section.raw_config,
156  }
157  for entity_conf in conf_section[platform_domain]
158  ],
159  },
160  hass_config,
161  ),
162  eager_start=True,
163  )
164 
165  if coordinator_tasks:
166  hass.data[DATA_COORDINATORS] = await asyncio.gather(*coordinator_tasks)
blueprint.DomainBlueprints async_get_blueprints(HomeAssistant hass)
Definition: helpers.py:29
None _process_config(HomeAssistant hass, ConfigType hass_config)
Definition: __init__.py:119
bool async_setup(HomeAssistant hass, ConfigType config)
Definition: __init__.py:38
bool async_setup_entry(HomeAssistant hass, ConfigEntry entry)
Definition: __init__.py:82
None config_entry_update_listener(HomeAssistant hass, ConfigEntry entry)
Definition: __init__.py:107
bool async_unload_entry(HomeAssistant hass, ConfigEntry entry)
Definition: __init__.py:112
None async_remove_stale_devices_links_keep_current_device(HomeAssistant hass, str entry_id, str|None current_device_id)
Definition: device.py:85
None async_reload_integration_platforms(HomeAssistant hass, str integration_domain, Iterable[str] platform_domains)
Definition: reload.py:30
None async_register_admin_service(HomeAssistant hass, str domain, str service, Callable[[ServiceCall], Awaitable[None]|None] service_func, VolSchemaType schema=vol.Schema({}, extra=vol.PREVENT_EXTRA))
Definition: service.py:1121
Integration async_get_integration(HomeAssistant hass, str domain)
Definition: loader.py:1354