Home Assistant Unofficial Reference 2024.12.1
__init__.py
Go to the documentation of this file.
1 """Component to wrap switch entities in entities of other domains."""
2 
3 from __future__ import annotations
4 
5 import logging
6 
7 import voluptuous as vol
8 
9 from homeassistant.components.homeassistant import exposed_entities
10 from homeassistant.config_entries import ConfigEntry
11 from homeassistant.const import CONF_ENTITY_ID
12 from homeassistant.core import Event, HomeAssistant, callback
13 from homeassistant.helpers import device_registry as dr, entity_registry as er
14 from homeassistant.helpers.event import async_track_entity_registry_updated_event
15 
16 from .const import CONF_INVERT, CONF_TARGET_DOMAIN
17 from .light import LightSwitch
18 
19 __all__ = ["LightSwitch"]
20 
21 _LOGGER = logging.getLogger(__name__)
22 
23 
24 @callback
26  hass: HomeAssistant, entry: ConfigEntry, entity_id: str
27 ) -> str | None:
28  """Add our config entry to the tracked entity's device."""
29  registry = er.async_get(hass)
30  device_registry = dr.async_get(hass)
31  device_id = None
32 
33  if (
34  not (wrapped_switch := registry.async_get(entity_id))
35  or not (device_id := wrapped_switch.device_id)
36  or not (device_registry.async_get(device_id))
37  ):
38  return device_id
39 
40  device_registry.async_update_device(device_id, add_config_entry_id=entry.entry_id)
41 
42  return device_id
43 
44 
45 async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
46  """Set up a config entry."""
47  registry = er.async_get(hass)
48  device_registry = dr.async_get(hass)
49  try:
50  entity_id = er.async_validate_entity_id(registry, entry.options[CONF_ENTITY_ID])
51  except vol.Invalid:
52  # The entity is identified by an unknown entity registry ID
53  _LOGGER.error(
54  "Failed to setup switch_as_x for unknown entity %s",
55  entry.options[CONF_ENTITY_ID],
56  )
57  return False
58 
59  async def async_registry_updated(
60  event: Event[er.EventEntityRegistryUpdatedData],
61  ) -> None:
62  """Handle entity registry update."""
63  data = event.data
64  if data["action"] == "remove":
65  await hass.config_entries.async_remove(entry.entry_id)
66 
67  if data["action"] != "update":
68  return
69 
70  if "entity_id" in data["changes"]:
71  # Entity_id changed, reload the config entry
72  await hass.config_entries.async_reload(entry.entry_id)
73 
74  if device_id and "device_id" in data["changes"]:
75  # If the tracked switch is no longer in the device, remove our config entry
76  # from the device
77  if (
78  not (entity_entry := registry.async_get(data[CONF_ENTITY_ID]))
79  or not device_registry.async_get(device_id)
80  or entity_entry.device_id == device_id
81  ):
82  # No need to do any cleanup
83  return
84 
85  device_registry.async_update_device(
86  device_id, remove_config_entry_id=entry.entry_id
87  )
88 
89  entry.async_on_unload(
91  hass, entity_id, async_registry_updated
92  )
93  )
94  entry.async_on_unload(entry.add_update_listener(config_entry_update_listener))
95 
96  device_id = async_add_to_device(hass, entry, entity_id)
97 
98  await hass.config_entries.async_forward_entry_setups(
99  entry, (entry.options[CONF_TARGET_DOMAIN],)
100  )
101  return True
102 
103 
104 async def async_migrate_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool:
105  """Migrate old entry."""
106  _LOGGER.debug(
107  "Migrating from version %s.%s", config_entry.version, config_entry.minor_version
108  )
109 
110  if config_entry.version > 1:
111  # This means the user has downgraded from a future version
112  return False
113  if config_entry.version == 1:
114  options = {**config_entry.options}
115  if config_entry.minor_version < 2:
116  options.setdefault(CONF_INVERT, False)
117  hass.config_entries.async_update_entry(
118  config_entry, options=options, minor_version=2
119  )
120 
121  _LOGGER.debug(
122  "Migration to version %s.%s successful",
123  config_entry.version,
124  config_entry.minor_version,
125  )
126 
127  return True
128 
129 
130 async def config_entry_update_listener(hass: HomeAssistant, entry: ConfigEntry) -> None:
131  """Update listener, called when the config entry options are changed."""
132  await hass.config_entries.async_reload(entry.entry_id)
133 
134 
135 async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
136  """Unload a config entry."""
137  return await hass.config_entries.async_unload_platforms(
138  entry, (entry.options[CONF_TARGET_DOMAIN],)
139  )
140 
141 
142 async def async_remove_entry(hass: HomeAssistant, entry: ConfigEntry) -> None:
143  """Unload a config entry.
144 
145  This will unhide the wrapped entity and restore assistant expose settings.
146  """
147  registry = er.async_get(hass)
148  try:
149  switch_entity_id = er.async_validate_entity_id(
150  registry, entry.options[CONF_ENTITY_ID]
151  )
152  except vol.Invalid:
153  # The source entity has been removed from the entity registry
154  return
155 
156  if not (switch_entity_entry := registry.async_get(switch_entity_id)):
157  return
158 
159  # Unhide the wrapped entity
160  if switch_entity_entry.hidden_by == er.RegistryEntryHider.INTEGRATION:
161  registry.async_update_entity(switch_entity_id, hidden_by=None)
162 
163  switch_as_x_entries = er.async_entries_for_config_entry(registry, entry.entry_id)
164  if not switch_as_x_entries:
165  return
166 
167  switch_as_x_entry = switch_as_x_entries[0]
168 
169  # Restore assistant expose settings
170  expose_settings = exposed_entities.async_get_entity_settings(
171  hass, switch_as_x_entry.entity_id
172  )
173  for assistant, settings in expose_settings.items():
174  if (should_expose := settings.get("should_expose")) is None:
175  continue
176  exposed_entities.async_expose_entity(
177  hass, assistant, switch_entity_id, should_expose
178  )
str|None async_add_to_device(HomeAssistant hass, ConfigEntry entry, str entity_id)
Definition: __init__.py:27
None async_remove_entry(HomeAssistant hass, ConfigEntry entry)
Definition: __init__.py:142
bool async_unload_entry(HomeAssistant hass, ConfigEntry entry)
Definition: __init__.py:135
bool async_migrate_entry(HomeAssistant hass, ConfigEntry config_entry)
Definition: __init__.py:104
bool async_setup_entry(HomeAssistant hass, ConfigEntry entry)
Definition: __init__.py:45
None config_entry_update_listener(HomeAssistant hass, ConfigEntry entry)
Definition: __init__.py:130
CALLBACK_TYPE async_track_entity_registry_updated_event(HomeAssistant hass, str|Iterable[str] entity_ids, Callable[[Event[EventEntityRegistryUpdatedData]], Any] action, HassJobType|None job_type=None)
Definition: event.py:543