2020-10-14 15:47:13 +00:00
|
|
|
"""Support for Tasmota device discovery."""
|
2020-10-06 12:51:58 +00:00
|
|
|
import logging
|
|
|
|
|
|
|
|
from hatasmota.discovery import (
|
|
|
|
TasmotaDiscovery,
|
|
|
|
get_device_config as tasmota_get_device_config,
|
|
|
|
get_entities_for_platform as tasmota_get_entities_for_platform,
|
|
|
|
get_entity as tasmota_get_entity,
|
2020-10-16 06:16:07 +00:00
|
|
|
get_trigger as tasmota_get_trigger,
|
|
|
|
get_triggers as tasmota_get_triggers,
|
2020-10-06 20:32:36 +00:00
|
|
|
unique_id_from_hash,
|
2020-10-06 12:51:58 +00:00
|
|
|
)
|
|
|
|
|
2020-10-12 05:27:06 +00:00
|
|
|
import homeassistant.components.sensor as sensor
|
2020-10-06 12:51:58 +00:00
|
|
|
from homeassistant.helpers.dispatcher import async_dispatcher_send
|
2020-10-12 05:27:06 +00:00
|
|
|
from homeassistant.helpers.entity_registry import async_entries_for_device
|
2020-10-06 12:51:58 +00:00
|
|
|
from homeassistant.helpers.typing import HomeAssistantType
|
|
|
|
|
2020-10-22 23:22:51 +00:00
|
|
|
from .const import DOMAIN, PLATFORMS
|
2020-10-06 12:51:58 +00:00
|
|
|
|
|
|
|
_LOGGER = logging.getLogger(__name__)
|
|
|
|
|
|
|
|
ALREADY_DISCOVERED = "tasmota_discovered_components"
|
|
|
|
TASMOTA_DISCOVERY_DEVICE = "tasmota_discovery_device"
|
|
|
|
TASMOTA_DISCOVERY_ENTITY_NEW = "tasmota_discovery_entity_new_{}"
|
2020-10-06 20:32:36 +00:00
|
|
|
TASMOTA_DISCOVERY_ENTITY_UPDATED = "tasmota_discovery_entity_updated_{}_{}_{}_{}"
|
2020-10-22 23:22:51 +00:00
|
|
|
TASMOTA_DISCOVERY_INSTANCE = "tasmota_discovery_instance"
|
2020-10-06 12:51:58 +00:00
|
|
|
|
|
|
|
|
|
|
|
def clear_discovery_hash(hass, discovery_hash):
|
|
|
|
"""Clear entry in ALREADY_DISCOVERED list."""
|
|
|
|
del hass.data[ALREADY_DISCOVERED][discovery_hash]
|
|
|
|
|
|
|
|
|
|
|
|
def set_discovery_hash(hass, discovery_hash):
|
|
|
|
"""Set entry in ALREADY_DISCOVERED list."""
|
|
|
|
hass.data[ALREADY_DISCOVERED][discovery_hash] = {}
|
|
|
|
|
|
|
|
|
|
|
|
async def async_start(
|
|
|
|
hass: HomeAssistantType, discovery_topic, config_entry, tasmota_mqtt
|
|
|
|
) -> bool:
|
2020-10-14 15:47:13 +00:00
|
|
|
"""Start Tasmota device discovery."""
|
2020-10-06 12:51:58 +00:00
|
|
|
|
2020-10-06 20:32:36 +00:00
|
|
|
async def _discover_entity(tasmota_entity_config, discovery_hash, platform):
|
|
|
|
"""Handle adding or updating a discovered entity."""
|
|
|
|
if not tasmota_entity_config:
|
|
|
|
# Entity disabled, clean up entity registry
|
|
|
|
entity_registry = await hass.helpers.entity_registry.async_get_registry()
|
|
|
|
unique_id = unique_id_from_hash(discovery_hash)
|
|
|
|
entity_id = entity_registry.async_get_entity_id(platform, DOMAIN, unique_id)
|
|
|
|
if entity_id:
|
|
|
|
_LOGGER.debug("Removing entity: %s %s", platform, discovery_hash)
|
|
|
|
entity_registry.async_remove(entity_id)
|
|
|
|
return
|
|
|
|
|
|
|
|
if discovery_hash in hass.data[ALREADY_DISCOVERED]:
|
|
|
|
_LOGGER.debug(
|
|
|
|
"Entity already added, sending update: %s %s",
|
|
|
|
platform,
|
|
|
|
discovery_hash,
|
|
|
|
)
|
|
|
|
async_dispatcher_send(
|
|
|
|
hass,
|
|
|
|
TASMOTA_DISCOVERY_ENTITY_UPDATED.format(*discovery_hash),
|
|
|
|
tasmota_entity_config,
|
|
|
|
)
|
|
|
|
else:
|
|
|
|
tasmota_entity = tasmota_get_entity(tasmota_entity_config, tasmota_mqtt)
|
2020-10-22 23:22:51 +00:00
|
|
|
_LOGGER.debug(
|
|
|
|
"Adding new entity: %s %s %s",
|
|
|
|
platform,
|
|
|
|
discovery_hash,
|
|
|
|
tasmota_entity.unique_id,
|
|
|
|
)
|
2020-10-06 20:32:36 +00:00
|
|
|
|
|
|
|
hass.data[ALREADY_DISCOVERED][discovery_hash] = None
|
|
|
|
|
|
|
|
async_dispatcher_send(
|
|
|
|
hass,
|
|
|
|
TASMOTA_DISCOVERY_ENTITY_NEW.format(platform),
|
|
|
|
tasmota_entity,
|
|
|
|
discovery_hash,
|
|
|
|
)
|
|
|
|
|
2020-10-06 12:51:58 +00:00
|
|
|
async def async_device_discovered(payload, mac):
|
|
|
|
"""Process the received message."""
|
|
|
|
|
|
|
|
if ALREADY_DISCOVERED not in hass.data:
|
2020-10-22 23:22:51 +00:00
|
|
|
# Discovery is shutting down
|
|
|
|
return
|
2020-10-06 12:51:58 +00:00
|
|
|
|
|
|
|
_LOGGER.debug("Received discovery data for tasmota device: %s", mac)
|
|
|
|
tasmota_device_config = tasmota_get_device_config(payload)
|
|
|
|
async_dispatcher_send(
|
|
|
|
hass, TASMOTA_DISCOVERY_DEVICE, tasmota_device_config, mac
|
|
|
|
)
|
|
|
|
|
2020-10-06 20:32:36 +00:00
|
|
|
if not payload:
|
|
|
|
return
|
|
|
|
|
2020-10-16 06:16:07 +00:00
|
|
|
tasmota_triggers = tasmota_get_triggers(payload)
|
|
|
|
for trigger_config in tasmota_triggers:
|
|
|
|
discovery_hash = (mac, "automation", "trigger", trigger_config.trigger_id)
|
|
|
|
if discovery_hash in hass.data[ALREADY_DISCOVERED]:
|
|
|
|
_LOGGER.debug(
|
|
|
|
"Trigger already added, sending update: %s",
|
|
|
|
discovery_hash,
|
|
|
|
)
|
|
|
|
async_dispatcher_send(
|
|
|
|
hass,
|
|
|
|
TASMOTA_DISCOVERY_ENTITY_UPDATED.format(*discovery_hash),
|
|
|
|
trigger_config,
|
|
|
|
)
|
|
|
|
elif trigger_config.is_active:
|
2020-10-17 11:07:21 +00:00
|
|
|
_LOGGER.debug("Adding new trigger: %s", discovery_hash)
|
2020-10-16 06:16:07 +00:00
|
|
|
hass.data[ALREADY_DISCOVERED][discovery_hash] = None
|
|
|
|
|
|
|
|
tasmota_trigger = tasmota_get_trigger(trigger_config, tasmota_mqtt)
|
|
|
|
|
|
|
|
async_dispatcher_send(
|
|
|
|
hass,
|
|
|
|
TASMOTA_DISCOVERY_ENTITY_NEW.format("device_automation"),
|
|
|
|
tasmota_trigger,
|
|
|
|
discovery_hash,
|
|
|
|
)
|
|
|
|
|
2020-10-22 23:22:51 +00:00
|
|
|
for platform in PLATFORMS:
|
2020-10-06 20:32:36 +00:00
|
|
|
tasmota_entities = tasmota_get_entities_for_platform(payload, platform)
|
|
|
|
for (tasmota_entity_config, discovery_hash) in tasmota_entities:
|
|
|
|
await _discover_entity(tasmota_entity_config, discovery_hash, platform)
|
2020-10-06 12:51:58 +00:00
|
|
|
|
2020-10-12 05:27:06 +00:00
|
|
|
async def async_sensors_discovered(sensors, mac):
|
|
|
|
"""Handle discovery of (additional) sensors."""
|
|
|
|
platform = sensor.DOMAIN
|
|
|
|
|
|
|
|
device_registry = await hass.helpers.device_registry.async_get_registry()
|
|
|
|
entity_registry = await hass.helpers.entity_registry.async_get_registry()
|
|
|
|
device = device_registry.async_get_device(set(), {("mac", mac)})
|
|
|
|
|
|
|
|
if device is None:
|
|
|
|
_LOGGER.warning("Got sensors for unknown device mac: %s", mac)
|
|
|
|
return
|
|
|
|
|
|
|
|
orphaned_entities = {
|
|
|
|
entry.unique_id
|
|
|
|
for entry in async_entries_for_device(entity_registry, device.id)
|
|
|
|
if entry.domain == sensor.DOMAIN and entry.platform == DOMAIN
|
|
|
|
}
|
|
|
|
for (tasmota_sensor_config, discovery_hash) in sensors:
|
|
|
|
if tasmota_sensor_config:
|
|
|
|
orphaned_entities.discard(tasmota_sensor_config.unique_id)
|
|
|
|
await _discover_entity(tasmota_sensor_config, discovery_hash, platform)
|
|
|
|
for unique_id in orphaned_entities:
|
|
|
|
entity_id = entity_registry.async_get_entity_id(platform, DOMAIN, unique_id)
|
|
|
|
if entity_id:
|
|
|
|
_LOGGER.debug("Removing entity: %s %s", platform, entity_id)
|
|
|
|
entity_registry.async_remove(entity_id)
|
|
|
|
|
2020-10-22 23:22:51 +00:00
|
|
|
hass.data[ALREADY_DISCOVERED] = {}
|
2020-10-06 12:51:58 +00:00
|
|
|
|
|
|
|
tasmota_discovery = TasmotaDiscovery(discovery_topic, tasmota_mqtt)
|
2020-10-12 05:27:06 +00:00
|
|
|
await tasmota_discovery.start_discovery(
|
|
|
|
async_device_discovered, async_sensors_discovered
|
|
|
|
)
|
2020-10-22 23:22:51 +00:00
|
|
|
hass.data[TASMOTA_DISCOVERY_INSTANCE] = tasmota_discovery
|
|
|
|
|
|
|
|
|
|
|
|
async def async_stop(hass: HomeAssistantType) -> bool:
|
|
|
|
"""Stop Tasmota device discovery."""
|
|
|
|
hass.data.pop(ALREADY_DISCOVERED)
|
|
|
|
tasmota_discovery = hass.data.pop(TASMOTA_DISCOVERY_INSTANCE)
|
|
|
|
await tasmota_discovery.stop_discovery()
|