Home Assistant Unofficial Reference 2024.12.1
__init__.py
Go to the documentation of this file.
1 """The Tasmota integration."""
2 
3 from __future__ import annotations
4 
5 import logging
6 
7 from hatasmota.const import (
8  CONF_IP,
9  CONF_MAC,
10  CONF_MANUFACTURER,
11  CONF_MODEL,
12  CONF_NAME,
13  CONF_SW_VERSION,
14 )
15 from hatasmota.models import TasmotaDeviceConfig
16 from hatasmota.mqtt import TasmotaMQTTClient
17 
18 from homeassistant.components import mqtt
20  async_prepare_subscribe_topics,
21  async_subscribe_topics,
22  async_unsubscribe_topics,
23 )
24 from homeassistant.config_entries import ConfigEntry
25 from homeassistant.core import HomeAssistant, callback
26 from homeassistant.helpers import device_registry as dr
27 from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC, DeviceRegistry
28 
29 from . import device_automation, discovery
30 from .const import (
31  CONF_DISCOVERY_PREFIX,
32  DATA_REMOVE_DISCOVER_COMPONENT,
33  DATA_UNSUB,
34  PLATFORMS,
35 )
36 
37 _LOGGER = logging.getLogger(__name__)
38 
39 
40 async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
41  """Set up Tasmota from a config entry."""
42  hass.data[DATA_UNSUB] = []
43 
44  async def _publish(
45  topic: str,
46  payload: mqtt.PublishPayloadType,
47  qos: int | None,
48  retain: bool | None,
49  ) -> None:
50  await mqtt.async_publish(hass, topic, payload, qos, retain)
51 
52  async def _subscribe_topics(sub_state: dict | None, topics: dict) -> dict:
53  # Optionally mark message handlers as callback
54  for topic in topics.values():
55  if "msg_callback" in topic and "event_loop_safe" in topic:
56  topic["msg_callback"] = callback(topic["msg_callback"])
57  sub_state = async_prepare_subscribe_topics(hass, sub_state, topics)
58  await async_subscribe_topics(hass, sub_state)
59  return sub_state
60 
61  async def _unsubscribe_topics(sub_state: dict | None) -> dict:
62  return async_unsubscribe_topics(hass, sub_state)
63 
64  tasmota_mqtt = TasmotaMQTTClient(_publish, _subscribe_topics, _unsubscribe_topics)
65 
66  device_registry = dr.async_get(hass)
67 
68  async def async_discover_device(config: TasmotaDeviceConfig, mac: str) -> None:
69  """Discover and add a Tasmota device."""
70  await async_setup_device(
71  hass, mac, config, entry, tasmota_mqtt, device_registry
72  )
73 
74  await device_automation.async_setup_entry(hass, entry)
75  await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
76  discovery_prefix = entry.data[CONF_DISCOVERY_PREFIX]
77  await discovery.async_start(
78  hass, discovery_prefix, entry, tasmota_mqtt, async_discover_device
79  )
80 
81  return True
82 
83 
84 async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
85  """Unload a config entry."""
86 
87  # cleanup platforms
88  unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
89  if not unload_ok:
90  return False
91 
92  # disable discovery
93  await discovery.async_stop(hass)
94 
95  # cleanup subscriptions
96  for unsub in hass.data[DATA_UNSUB]:
97  unsub()
98  hass.data.pop(DATA_REMOVE_DISCOVER_COMPONENT.format("device_automation"))()
99  for platform in PLATFORMS:
100  hass.data.pop(DATA_REMOVE_DISCOVER_COMPONENT.format(platform))()
101 
102  # detach device triggers
103  device_registry = dr.async_get(hass)
104  devices = dr.async_entries_for_config_entry(device_registry, entry.entry_id)
105  for device in devices:
106  await device_automation.async_remove_automations(hass, device.id)
107 
108  return True
109 
110 
111 async def _remove_device(
112  hass: HomeAssistant,
113  config_entry: ConfigEntry,
114  mac: str,
115  tasmota_mqtt: TasmotaMQTTClient,
116  device_registry: DeviceRegistry,
117 ) -> None:
118  """Remove a discovered Tasmota device."""
119  device = device_registry.async_get_device(
120  connections={(CONNECTION_NETWORK_MAC, mac)}
121  )
122 
123  if device is None or config_entry.entry_id not in device.config_entries:
124  return
125 
126  _LOGGER.debug("Removing tasmota from device %s", mac)
127  device_registry.async_update_device(
128  device.id, remove_config_entry_id=config_entry.entry_id
129  )
130 
131 
133  hass: HomeAssistant,
134  config_entry: ConfigEntry,
135  config: TasmotaDeviceConfig,
136  device_registry: DeviceRegistry,
137 ) -> None:
138  """Add or update device registry."""
139  _LOGGER.debug("Adding or updating tasmota device %s", config[CONF_MAC])
140  device_registry.async_get_or_create(
141  config_entry_id=config_entry.entry_id,
142  configuration_url=f"http://{config[CONF_IP]}/",
143  connections={(CONNECTION_NETWORK_MAC, config[CONF_MAC])},
144  manufacturer=config[CONF_MANUFACTURER],
145  model=config[CONF_MODEL],
146  name=config[CONF_NAME],
147  sw_version=config[CONF_SW_VERSION],
148  )
149 
150 
152  hass: HomeAssistant,
153  mac: str,
154  config: TasmotaDeviceConfig,
155  config_entry: ConfigEntry,
156  tasmota_mqtt: TasmotaMQTTClient,
157  device_registry: DeviceRegistry,
158 ) -> None:
159  """Set up the Tasmota device."""
160  if not config:
161  await _remove_device(hass, config_entry, mac, tasmota_mqtt, device_registry)
162  else:
163  _update_device(hass, config_entry, config, device_registry)
164 
165 
167  hass: HomeAssistant, config_entry: ConfigEntry, device_entry: dr.DeviceEntry
168 ) -> bool:
169  """Remove Tasmota config entry from a device."""
170 
171  connections = device_entry.connections
172  macs = [c[1] for c in connections if c[0] == CONNECTION_NETWORK_MAC]
173  tasmota_discovery = hass.data[discovery.TASMOTA_DISCOVERY_INSTANCE]
174  for mac in macs:
175  await tasmota_discovery.clear_discovery_topic(
176  mac, config_entry.data[CONF_DISCOVERY_PREFIX]
177  )
178 
179  return True
ElkSystem|None async_discover_device(HomeAssistant hass, str host)
Definition: discovery.py:78
dict[str, EntitySubscription] async_prepare_subscribe_topics(HomeAssistant hass, dict[str, EntitySubscription]|None sub_state, dict[str, dict[str, Any]] topics)
Definition: subscription.py:91
None async_subscribe_topics(HomeAssistant hass, dict[str, EntitySubscription] sub_state)
None async_setup_device(HomeAssistant hass, str mac, TasmotaDeviceConfig config, ConfigEntry config_entry, TasmotaMQTTClient tasmota_mqtt, DeviceRegistry device_registry)
Definition: __init__.py:158
bool async_unload_entry(HomeAssistant hass, ConfigEntry entry)
Definition: __init__.py:84
None _remove_device(HomeAssistant hass, ConfigEntry config_entry, str mac, TasmotaMQTTClient tasmota_mqtt, DeviceRegistry device_registry)
Definition: __init__.py:117
bool async_setup_entry(HomeAssistant hass, ConfigEntry entry)
Definition: __init__.py:40
None _update_device(HomeAssistant hass, ConfigEntry config_entry, TasmotaDeviceConfig config, DeviceRegistry device_registry)
Definition: __init__.py:137
bool async_remove_config_entry_device(HomeAssistant hass, ConfigEntry config_entry, dr.DeviceEntry device_entry)
Definition: __init__.py:168