1 """Provides device automations for Tasmota."""
3 from __future__
import annotations
5 from collections.abc
import Callable
9 from hatasmota.models
import DiscoveryHashType
10 from hatasmota.trigger
import TasmotaTrigger, TasmotaTriggerConfig
11 import voluptuous
as vol
25 from .const
import DOMAIN, TASMOTA_EVENT
26 from .discovery
import TASMOTA_DISCOVERY_ENTITY_UPDATED, clear_discovery_hash
28 _LOGGER = logging.getLogger(__name__)
30 CONF_DISCOVERY_ID =
"discovery_id"
31 CONF_SUBTYPE =
"subtype"
34 TRIGGER_SCHEMA = DEVICE_TRIGGER_BASE_SCHEMA.extend(
36 vol.Required(CONF_PLATFORM): DEVICE,
37 vol.Required(CONF_DOMAIN): DOMAIN,
38 vol.Required(CONF_DEVICE_ID): str,
39 vol.Required(CONF_DISCOVERY_ID): str,
40 vol.Required(CONF_TYPE): cv.string,
41 vol.Required(CONF_SUBTYPE): cv.string,
45 DEVICE_TRIGGERS =
"tasmota_device_triggers"
50 """Attached trigger settings."""
52 action: TriggerActionType = attr.ib()
53 trigger_info: TriggerInfo = attr.ib()
54 trigger: Trigger = attr.ib()
55 remove: CALLBACK_TYPE |
None = attr.ib(default=
None)
58 """Attach event trigger."""
59 assert self.trigger.tasmota_trigger
is not None
61 event_trigger.CONF_PLATFORM:
"event",
62 event_trigger.CONF_EVENT_TYPE: TASMOTA_EVENT,
63 event_trigger.CONF_EVENT_DATA: {
64 "mac": self.trigger.tasmota_trigger.cfg.mac,
65 "source": self.trigger.tasmota_trigger.cfg.subtype,
66 "event": self.trigger.tasmota_trigger.cfg.event,
70 event_config = event_trigger.TRIGGER_SCHEMA(event_config)
75 self.
removeremove = await event_trigger.async_attach_trigger(
80 platform_type=
"device",
86 """Device trigger settings."""
88 device_id: str = attr.ib()
89 discovery_hash: DiscoveryHashType |
None = attr.ib()
90 hass: HomeAssistant = attr.ib()
91 remove_update_signal: Callable[[],
None] |
None = attr.ib()
92 subtype: str = attr.ib()
93 tasmota_trigger: TasmotaTrigger |
None = attr.ib()
95 trigger_instances: list[TriggerInstance] = attr.ib(factory=list)
98 self, action: TriggerActionType, trigger_info: TriggerInfo
99 ) -> Callable[[],
None]:
100 """Add Tasmota trigger."""
102 self.trigger_instances.append(instance)
106 await instance.async_attach_trigger()
110 """Remove trigger."""
111 if instance
not in self.trigger_instances:
116 self.trigger_instances.
remove(instance)
120 def detach_trigger(self) -> None:
121 """Remove Tasmota device trigger."""
126 for trig
in self.trigger_instances:
131 async
def arm_tasmota_trigger(self) -> None:
132 """Arm Tasmota trigger: subscribe to MQTT topics and fire events."""
135 def _on_trigger() -> None:
142 self.hass.bus.async_fire(
148 self.
tasmota_triggertasmota_trigger.set_on_trigger_callback(_on_trigger)
151 async
def set_tasmota_trigger(
152 self, tasmota_trigger: TasmotaTrigger, remove_update_signal: Callable[[],
None]
154 """Set Tasmota trigger."""
155 await self.update_tasmota_trigger(tasmota_trigger.cfg, remove_update_signal)
158 for trig
in self.trigger_instances:
159 await trig.async_attach_trigger()
161 async
def update_tasmota_trigger(
163 tasmota_trigger_cfg: TasmotaTriggerConfig,
164 remove_update_signal: Callable[[],
None],
166 """Update Tasmota trigger."""
168 self.
typetype = tasmota_trigger_cfg.type
169 self.
subtypesubtype = tasmota_trigger_cfg.subtype
174 tasmota_trigger: TasmotaTrigger,
175 config_entry: ConfigEntry,
176 discovery_hash: DiscoveryHashType,
178 """Set up a discovered Tasmota device trigger."""
179 discovery_id = tasmota_trigger.cfg.trigger_id
180 remove_update_signal: Callable[[],
None] |
None =
None
182 "Discovered trigger with ID: %s '%s'", discovery_id, tasmota_trigger.cfg
185 async
def discovery_update(trigger_config: TasmotaTriggerConfig) ->
None:
186 """Handle discovery update."""
188 "Got update for trigger with hash: %s '%s'", discovery_hash, trigger_config
190 device_triggers: dict[str, Trigger] = hass.data[DEVICE_TRIGGERS]
191 if not trigger_config.is_active:
193 _LOGGER.debug(
"Removing trigger: %s", discovery_hash)
194 if discovery_id
in device_triggers:
195 device_trigger = device_triggers[discovery_id]
196 assert device_trigger.tasmota_trigger
197 await device_trigger.tasmota_trigger.unsubscribe_topics()
198 device_trigger.detach_trigger()
200 if remove_update_signal
is not None:
201 remove_update_signal()
204 device_trigger = device_triggers[discovery_id]
205 assert device_trigger.tasmota_trigger
206 if device_trigger.tasmota_trigger.config_same(trigger_config):
208 _LOGGER.debug(
"Ignoring unchanged update for: %s", discovery_hash)
212 _LOGGER.debug(
"Updating trigger: %s", discovery_hash)
213 device_trigger.tasmota_trigger.config_update(trigger_config)
214 assert remove_update_signal
215 await device_trigger.update_tasmota_trigger(
216 trigger_config, remove_update_signal
218 await device_trigger.arm_tasmota_trigger()
222 hass, TASMOTA_DISCOVERY_ENTITY_UPDATED.format(*discovery_hash), discovery_update
225 device_registry = dr.async_get(hass)
226 device = device_registry.async_get_device(
227 connections={(CONNECTION_NETWORK_MAC, tasmota_trigger.cfg.mac)},
233 if DEVICE_TRIGGERS
not in hass.data:
234 hass.data[DEVICE_TRIGGERS] = {}
235 device_triggers: dict[str, Trigger] = hass.data[DEVICE_TRIGGERS]
236 if discovery_id
not in device_triggers:
240 discovery_hash=discovery_hash,
241 subtype=tasmota_trigger.cfg.subtype,
242 tasmota_trigger=tasmota_trigger,
243 type=tasmota_trigger.cfg.type,
244 remove_update_signal=remove_update_signal,
246 device_triggers[discovery_id] = device_trigger
249 device_trigger = device_triggers[discovery_id]
250 await device_trigger.set_tasmota_trigger(tasmota_trigger, remove_update_signal)
251 await device_trigger.arm_tasmota_trigger()
255 """Cleanup any device triggers for a Tasmota device."""
260 device_triggers: dict[str, Trigger] = hass.data[DEVICE_TRIGGERS]
261 for trig
in triggers:
262 device_trigger = device_triggers.pop(trig[CONF_DISCOVERY_ID])
264 discovery_hash = device_trigger.discovery_hash
266 assert device_trigger.tasmota_trigger
267 await device_trigger.tasmota_trigger.unsubscribe_topics()
268 device_trigger.detach_trigger()
270 assert device_trigger.remove_update_signal
271 device_trigger.remove_update_signal()
275 hass: HomeAssistant, device_id: str
276 ) -> list[dict[str, str]]:
277 """List device triggers for a Tasmota device."""
278 triggers: list[dict[str, str]] = []
280 if DEVICE_TRIGGERS
not in hass.data:
283 device_triggers: dict[str, Trigger] = hass.data[DEVICE_TRIGGERS]
284 for discovery_id, trig
in device_triggers.items():
285 if trig.device_id != device_id
or trig.tasmota_trigger
is None:
289 "platform":
"device",
291 "device_id": device_id,
293 "subtype": trig.subtype,
294 "discovery_id": discovery_id,
296 triggers.append(trigger)
304 action: TriggerActionType,
305 trigger_info: TriggerInfo,
307 """Attach a device trigger."""
308 if DEVICE_TRIGGERS
not in hass.data:
309 hass.data[DEVICE_TRIGGERS] = {}
310 device_triggers: dict[str, Trigger] = hass.data[DEVICE_TRIGGERS]
311 device_id = config[CONF_DEVICE_ID]
312 discovery_id = config[CONF_DISCOVERY_ID]
314 if discovery_id
not in device_triggers:
316 device_triggers[discovery_id] =
Trigger(
320 remove_update_signal=
None,
321 type=config[CONF_TYPE],
322 subtype=config[CONF_SUBTYPE],
323 tasmota_trigger=
None,
325 trigger: Trigger = device_triggers[discovery_id]
326 return await trigger.add_trigger(action, trigger_info)
None async_attach_trigger(self)
Callable[[], None] add_trigger(self, TriggerActionType action, TriggerInfo trigger_info)
bool remove(self, _T matcher)
None clear_discovery_hash(HomeAssistant hass, tuple[str, str] discovery_hash)
list[dict[str, str]] async_get_triggers(HomeAssistant hass, str device_id)
None async_remove_triggers(HomeAssistant hass, str device_id)
None async_setup_trigger(HomeAssistant hass, TasmotaTrigger tasmota_trigger, ConfigEntry config_entry, DiscoveryHashType discovery_hash)
CALLBACK_TYPE async_attach_trigger(HomeAssistant hass, ConfigType config, TriggerActionType action, TriggerInfo trigger_info)
Callable[[], None] async_dispatcher_connect(HomeAssistant hass, str signal, Callable[..., Any] target)
None async_remove(HomeAssistant hass, str intent_type)