Support reloading Tasmota config entries (#42097)
This commit is contained in:
parent
3661035397
commit
de35d58fd4
15 changed files with 179 additions and 75 deletions
|
@ -1,4 +1,5 @@
|
||||||
"""The Tasmota integration."""
|
"""The Tasmota integration."""
|
||||||
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from hatasmota.const import (
|
from hatasmota.const import (
|
||||||
|
@ -21,8 +22,8 @@ from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC
|
||||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||||
from homeassistant.helpers.typing import HomeAssistantType
|
from homeassistant.helpers.typing import HomeAssistantType
|
||||||
|
|
||||||
from . import discovery
|
from . import device_automation, discovery
|
||||||
from .const import CONF_DISCOVERY_PREFIX
|
from .const import CONF_DISCOVERY_PREFIX, DATA_REMOVE_DISCOVER_COMPONENT, PLATFORMS
|
||||||
from .discovery import TASMOTA_DISCOVERY_DEVICE
|
from .discovery import TASMOTA_DISCOVERY_DEVICE
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
@ -54,14 +55,52 @@ async def async_setup_entry(hass, entry):
|
||||||
|
|
||||||
tasmota_mqtt = TasmotaMQTTClient(_publish, _subscribe_topics, _unsubscribe_topics)
|
tasmota_mqtt = TasmotaMQTTClient(_publish, _subscribe_topics, _unsubscribe_topics)
|
||||||
|
|
||||||
discovery_prefix = entry.data[CONF_DISCOVERY_PREFIX]
|
|
||||||
await discovery.async_start(hass, discovery_prefix, entry, tasmota_mqtt)
|
|
||||||
|
|
||||||
async def async_discover_device(config, mac):
|
async def async_discover_device(config, mac):
|
||||||
"""Discover and add a Tasmota device."""
|
"""Discover and add a Tasmota device."""
|
||||||
await async_setup_device(hass, mac, config, entry, tasmota_mqtt)
|
await async_setup_device(hass, mac, config, entry, tasmota_mqtt)
|
||||||
|
|
||||||
async_dispatcher_connect(hass, TASMOTA_DISCOVERY_DEVICE, async_discover_device)
|
hass.data[
|
||||||
|
DATA_REMOVE_DISCOVER_COMPONENT.format("device")
|
||||||
|
] = async_dispatcher_connect(hass, TASMOTA_DISCOVERY_DEVICE, async_discover_device)
|
||||||
|
|
||||||
|
async def start_platforms():
|
||||||
|
await device_automation.async_setup_entry(hass, entry)
|
||||||
|
await asyncio.gather(
|
||||||
|
*[
|
||||||
|
hass.config_entries.async_forward_entry_setup(entry, component)
|
||||||
|
for component in PLATFORMS
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
discovery_prefix = entry.data[CONF_DISCOVERY_PREFIX]
|
||||||
|
await discovery.async_start(hass, discovery_prefix, entry, tasmota_mqtt)
|
||||||
|
|
||||||
|
hass.async_create_task(start_platforms())
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
async def async_unload_entry(hass, entry):
|
||||||
|
"""Unload a config entry."""
|
||||||
|
|
||||||
|
# cleanup platforms
|
||||||
|
unload_ok = all(
|
||||||
|
await asyncio.gather(
|
||||||
|
*[
|
||||||
|
hass.config_entries.async_forward_entry_unload(entry, component)
|
||||||
|
for component in PLATFORMS
|
||||||
|
]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if not unload_ok:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# disable discovery
|
||||||
|
await discovery.async_stop(hass)
|
||||||
|
hass.data.pop(DEVICE_MACS)
|
||||||
|
hass.data[DATA_REMOVE_DISCOVER_COMPONENT.format("device")]()
|
||||||
|
hass.data.pop(DATA_REMOVE_DISCOVER_COMPONENT.format("device_automation"))()
|
||||||
|
for component in PLATFORMS:
|
||||||
|
hass.data.pop(DATA_REMOVE_DISCOVER_COMPONENT.format(component))()
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ from homeassistant.core import callback
|
||||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||||
import homeassistant.helpers.event as evt
|
import homeassistant.helpers.event as evt
|
||||||
|
|
||||||
from .const import DOMAIN as TASMOTA_DOMAIN
|
from .const import DATA_REMOVE_DISCOVER_COMPONENT, DOMAIN as TASMOTA_DOMAIN
|
||||||
from .discovery import TASMOTA_DISCOVERY_ENTITY_NEW
|
from .discovery import TASMOTA_DISCOVERY_ENTITY_NEW
|
||||||
from .mixins import TasmotaAvailability, TasmotaDiscoveryUpdate
|
from .mixins import TasmotaAvailability, TasmotaDiscoveryUpdate
|
||||||
|
|
||||||
|
@ -25,7 +25,9 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
async_dispatcher_connect(
|
hass.data[
|
||||||
|
DATA_REMOVE_DISCOVER_COMPONENT.format(binary_sensor.DOMAIN)
|
||||||
|
] = async_dispatcher_connect(
|
||||||
hass,
|
hass,
|
||||||
TASMOTA_DISCOVERY_ENTITY_NEW.format(binary_sensor.DOMAIN, TASMOTA_DOMAIN),
|
TASMOTA_DISCOVERY_ENTITY_NEW.format(binary_sensor.DOMAIN, TASMOTA_DOMAIN),
|
||||||
async_discover,
|
async_discover,
|
||||||
|
|
|
@ -1,8 +1,17 @@
|
||||||
"""Constants used by multiple Tasmota modules."""
|
"""Constants used by multiple Tasmota modules."""
|
||||||
CONF_DISCOVERY_PREFIX = "discovery_prefix"
|
CONF_DISCOVERY_PREFIX = "discovery_prefix"
|
||||||
|
|
||||||
|
DATA_REMOVE_DISCOVER_COMPONENT = "tasmota_discover_{}"
|
||||||
|
|
||||||
DEFAULT_PREFIX = "tasmota/discovery"
|
DEFAULT_PREFIX = "tasmota/discovery"
|
||||||
|
|
||||||
DOMAIN = "tasmota"
|
DOMAIN = "tasmota"
|
||||||
|
|
||||||
|
PLATFORMS = [
|
||||||
|
"binary_sensor",
|
||||||
|
"light",
|
||||||
|
"sensor",
|
||||||
|
"switch",
|
||||||
|
]
|
||||||
|
|
||||||
TASMOTA_EVENT = "tasmota_event"
|
TASMOTA_EVENT = "tasmota_event"
|
||||||
|
|
|
@ -6,6 +6,7 @@ from homeassistant.helpers.device_registry import EVENT_DEVICE_REGISTRY_UPDATED
|
||||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||||
|
|
||||||
from . import device_trigger
|
from . import device_trigger
|
||||||
|
from .const import DATA_REMOVE_DISCOVER_COMPONENT
|
||||||
from .discovery import TASMOTA_DISCOVERY_ENTITY_NEW
|
from .discovery import TASMOTA_DISCOVERY_ENTITY_NEW
|
||||||
|
|
||||||
|
|
||||||
|
@ -25,7 +26,9 @@ async def async_setup_entry(hass, config_entry):
|
||||||
hass, tasmota_automation, config_entry, discovery_hash
|
hass, tasmota_automation, config_entry, discovery_hash
|
||||||
)
|
)
|
||||||
|
|
||||||
async_dispatcher_connect(
|
hass.data[
|
||||||
|
DATA_REMOVE_DISCOVER_COMPONENT.format("device_automation")
|
||||||
|
] = async_dispatcher_connect(
|
||||||
hass,
|
hass,
|
||||||
TASMOTA_DISCOVERY_ENTITY_NEW.format("device_automation", "tasmota"),
|
TASMOTA_DISCOVERY_ENTITY_NEW.format("device_automation", "tasmota"),
|
||||||
async_discover,
|
async_discover,
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
"""Support for Tasmota device discovery."""
|
"""Support for Tasmota device discovery."""
|
||||||
import asyncio
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from hatasmota.discovery import (
|
from hatasmota.discovery import (
|
||||||
|
@ -9,7 +8,6 @@ from hatasmota.discovery import (
|
||||||
get_entity as tasmota_get_entity,
|
get_entity as tasmota_get_entity,
|
||||||
get_trigger as tasmota_get_trigger,
|
get_trigger as tasmota_get_trigger,
|
||||||
get_triggers as tasmota_get_triggers,
|
get_triggers as tasmota_get_triggers,
|
||||||
has_entities_with_platform as tasmota_has_entities_with_platform,
|
|
||||||
unique_id_from_hash,
|
unique_id_from_hash,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -18,23 +16,15 @@ from homeassistant.helpers.dispatcher import async_dispatcher_send
|
||||||
from homeassistant.helpers.entity_registry import async_entries_for_device
|
from homeassistant.helpers.entity_registry import async_entries_for_device
|
||||||
from homeassistant.helpers.typing import HomeAssistantType
|
from homeassistant.helpers.typing import HomeAssistantType
|
||||||
|
|
||||||
from .const import DOMAIN
|
from .const import DOMAIN, PLATFORMS
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
SUPPORTED_PLATFORMS = [
|
|
||||||
"binary_sensor",
|
|
||||||
"light",
|
|
||||||
"sensor",
|
|
||||||
"switch",
|
|
||||||
]
|
|
||||||
|
|
||||||
ALREADY_DISCOVERED = "tasmota_discovered_components"
|
ALREADY_DISCOVERED = "tasmota_discovered_components"
|
||||||
CONFIG_ENTRY_IS_SETUP = "tasmota_config_entry_is_setup"
|
|
||||||
DATA_CONFIG_ENTRY_LOCK = "tasmota_config_entry_lock"
|
|
||||||
TASMOTA_DISCOVERY_DEVICE = "tasmota_discovery_device"
|
TASMOTA_DISCOVERY_DEVICE = "tasmota_discovery_device"
|
||||||
TASMOTA_DISCOVERY_ENTITY_NEW = "tasmota_discovery_entity_new_{}"
|
TASMOTA_DISCOVERY_ENTITY_NEW = "tasmota_discovery_entity_new_{}"
|
||||||
TASMOTA_DISCOVERY_ENTITY_UPDATED = "tasmota_discovery_entity_updated_{}_{}_{}_{}"
|
TASMOTA_DISCOVERY_ENTITY_UPDATED = "tasmota_discovery_entity_updated_{}_{}_{}_{}"
|
||||||
|
TASMOTA_DISCOVERY_INSTANCE = "tasmota_discovery_instance"
|
||||||
|
|
||||||
|
|
||||||
def clear_discovery_hash(hass, discovery_hash):
|
def clear_discovery_hash(hass, discovery_hash):
|
||||||
|
@ -52,16 +42,6 @@ async def async_start(
|
||||||
) -> bool:
|
) -> bool:
|
||||||
"""Start Tasmota device discovery."""
|
"""Start Tasmota device discovery."""
|
||||||
|
|
||||||
async def _load_platform(platform):
|
|
||||||
"""Load a Tasmota platform if not already done."""
|
|
||||||
async with hass.data[DATA_CONFIG_ENTRY_LOCK]:
|
|
||||||
config_entries_key = f"{platform}.tasmota"
|
|
||||||
if config_entries_key not in hass.data[CONFIG_ENTRY_IS_SETUP]:
|
|
||||||
await hass.config_entries.async_forward_entry_setup(
|
|
||||||
config_entry, platform
|
|
||||||
)
|
|
||||||
hass.data[CONFIG_ENTRY_IS_SETUP].add(config_entries_key)
|
|
||||||
|
|
||||||
async def _discover_entity(tasmota_entity_config, discovery_hash, platform):
|
async def _discover_entity(tasmota_entity_config, discovery_hash, platform):
|
||||||
"""Handle adding or updating a discovered entity."""
|
"""Handle adding or updating a discovered entity."""
|
||||||
if not tasmota_entity_config:
|
if not tasmota_entity_config:
|
||||||
|
@ -86,8 +66,13 @@ async def async_start(
|
||||||
tasmota_entity_config,
|
tasmota_entity_config,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
_LOGGER.debug("Adding new entity: %s %s", platform, discovery_hash)
|
|
||||||
tasmota_entity = tasmota_get_entity(tasmota_entity_config, tasmota_mqtt)
|
tasmota_entity = tasmota_get_entity(tasmota_entity_config, tasmota_mqtt)
|
||||||
|
_LOGGER.debug(
|
||||||
|
"Adding new entity: %s %s %s",
|
||||||
|
platform,
|
||||||
|
discovery_hash,
|
||||||
|
tasmota_entity.unique_id,
|
||||||
|
)
|
||||||
|
|
||||||
hass.data[ALREADY_DISCOVERED][discovery_hash] = None
|
hass.data[ALREADY_DISCOVERED][discovery_hash] = None
|
||||||
|
|
||||||
|
@ -102,7 +87,8 @@ async def async_start(
|
||||||
"""Process the received message."""
|
"""Process the received message."""
|
||||||
|
|
||||||
if ALREADY_DISCOVERED not in hass.data:
|
if ALREADY_DISCOVERED not in hass.data:
|
||||||
hass.data[ALREADY_DISCOVERED] = {}
|
# Discovery is shutting down
|
||||||
|
return
|
||||||
|
|
||||||
_LOGGER.debug("Received discovery data for tasmota device: %s", mac)
|
_LOGGER.debug("Received discovery data for tasmota device: %s", mac)
|
||||||
tasmota_device_config = tasmota_get_device_config(payload)
|
tasmota_device_config = tasmota_get_device_config(payload)
|
||||||
|
@ -114,17 +100,6 @@ async def async_start(
|
||||||
return
|
return
|
||||||
|
|
||||||
tasmota_triggers = tasmota_get_triggers(payload)
|
tasmota_triggers = tasmota_get_triggers(payload)
|
||||||
async with hass.data[DATA_CONFIG_ENTRY_LOCK]:
|
|
||||||
if any(trigger.is_active for trigger in tasmota_triggers):
|
|
||||||
config_entries_key = "device_automation.tasmota"
|
|
||||||
if config_entries_key not in hass.data[CONFIG_ENTRY_IS_SETUP]:
|
|
||||||
# Local import to avoid circular dependencies
|
|
||||||
# pylint: disable=import-outside-toplevel
|
|
||||||
from . import device_automation
|
|
||||||
|
|
||||||
await device_automation.async_setup_entry(hass, config_entry)
|
|
||||||
hass.data[CONFIG_ENTRY_IS_SETUP].add(config_entries_key)
|
|
||||||
|
|
||||||
for trigger_config in tasmota_triggers:
|
for trigger_config in tasmota_triggers:
|
||||||
discovery_hash = (mac, "automation", "trigger", trigger_config.trigger_id)
|
discovery_hash = (mac, "automation", "trigger", trigger_config.trigger_id)
|
||||||
if discovery_hash in hass.data[ALREADY_DISCOVERED]:
|
if discovery_hash in hass.data[ALREADY_DISCOVERED]:
|
||||||
|
@ -150,12 +125,7 @@ async def async_start(
|
||||||
discovery_hash,
|
discovery_hash,
|
||||||
)
|
)
|
||||||
|
|
||||||
for platform in SUPPORTED_PLATFORMS:
|
for platform in PLATFORMS:
|
||||||
if not tasmota_has_entities_with_platform(payload, platform):
|
|
||||||
continue
|
|
||||||
await _load_platform(platform)
|
|
||||||
|
|
||||||
for platform in SUPPORTED_PLATFORMS:
|
|
||||||
tasmota_entities = tasmota_get_entities_for_platform(payload, platform)
|
tasmota_entities = tasmota_get_entities_for_platform(payload, platform)
|
||||||
for (tasmota_entity_config, discovery_hash) in tasmota_entities:
|
for (tasmota_entity_config, discovery_hash) in tasmota_entities:
|
||||||
await _discover_entity(tasmota_entity_config, discovery_hash, platform)
|
await _discover_entity(tasmota_entity_config, discovery_hash, platform)
|
||||||
|
@ -163,7 +133,6 @@ async def async_start(
|
||||||
async def async_sensors_discovered(sensors, mac):
|
async def async_sensors_discovered(sensors, mac):
|
||||||
"""Handle discovery of (additional) sensors."""
|
"""Handle discovery of (additional) sensors."""
|
||||||
platform = sensor.DOMAIN
|
platform = sensor.DOMAIN
|
||||||
await _load_platform(platform)
|
|
||||||
|
|
||||||
device_registry = await hass.helpers.device_registry.async_get_registry()
|
device_registry = await hass.helpers.device_registry.async_get_registry()
|
||||||
entity_registry = await hass.helpers.entity_registry.async_get_registry()
|
entity_registry = await hass.helpers.entity_registry.async_get_registry()
|
||||||
|
@ -188,10 +157,17 @@ async def async_start(
|
||||||
_LOGGER.debug("Removing entity: %s %s", platform, entity_id)
|
_LOGGER.debug("Removing entity: %s %s", platform, entity_id)
|
||||||
entity_registry.async_remove(entity_id)
|
entity_registry.async_remove(entity_id)
|
||||||
|
|
||||||
hass.data[DATA_CONFIG_ENTRY_LOCK] = asyncio.Lock()
|
hass.data[ALREADY_DISCOVERED] = {}
|
||||||
hass.data[CONFIG_ENTRY_IS_SETUP] = set()
|
|
||||||
|
|
||||||
tasmota_discovery = TasmotaDiscovery(discovery_topic, tasmota_mqtt)
|
tasmota_discovery = TasmotaDiscovery(discovery_topic, tasmota_mqtt)
|
||||||
await tasmota_discovery.start_discovery(
|
await tasmota_discovery.start_discovery(
|
||||||
async_device_discovered, async_sensors_discovered
|
async_device_discovered, async_sensors_discovered
|
||||||
)
|
)
|
||||||
|
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()
|
||||||
|
|
|
@ -27,7 +27,7 @@ from homeassistant.core import callback
|
||||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||||
import homeassistant.util.color as color_util
|
import homeassistant.util.color as color_util
|
||||||
|
|
||||||
from .const import DOMAIN as TASMOTA_DOMAIN
|
from .const import DATA_REMOVE_DISCOVER_COMPONENT, DOMAIN as TASMOTA_DOMAIN
|
||||||
from .discovery import TASMOTA_DISCOVERY_ENTITY_NEW
|
from .discovery import TASMOTA_DISCOVERY_ENTITY_NEW
|
||||||
from .mixins import TasmotaAvailability, TasmotaDiscoveryUpdate
|
from .mixins import TasmotaAvailability, TasmotaDiscoveryUpdate
|
||||||
|
|
||||||
|
@ -45,7 +45,9 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||||
[TasmotaLight(tasmota_entity=tasmota_entity, discovery_hash=discovery_hash)]
|
[TasmotaLight(tasmota_entity=tasmota_entity, discovery_hash=discovery_hash)]
|
||||||
)
|
)
|
||||||
|
|
||||||
async_dispatcher_connect(
|
hass.data[
|
||||||
|
DATA_REMOVE_DISCOVER_COMPONENT.format(light.DOMAIN)
|
||||||
|
] = async_dispatcher_connect(
|
||||||
hass,
|
hass,
|
||||||
TASMOTA_DISCOVERY_ENTITY_NEW.format(light.DOMAIN, TASMOTA_DOMAIN),
|
TASMOTA_DISCOVERY_ENTITY_NEW.format(light.DOMAIN, TASMOTA_DOMAIN),
|
||||||
async_discover,
|
async_discover,
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
"name": "Tasmota (beta)",
|
"name": "Tasmota (beta)",
|
||||||
"config_flow": true,
|
"config_flow": true,
|
||||||
"documentation": "https://www.home-assistant.io/integrations/tasmota",
|
"documentation": "https://www.home-assistant.io/integrations/tasmota",
|
||||||
"requirements": ["hatasmota==0.0.19"],
|
"requirements": ["hatasmota==0.0.20"],
|
||||||
"dependencies": ["mqtt"],
|
"dependencies": ["mqtt"],
|
||||||
"mqtt": ["tasmota/discovery/#"],
|
"mqtt": ["tasmota/discovery/#"],
|
||||||
"codeowners": ["@emontnemery"]
|
"codeowners": ["@emontnemery"]
|
||||||
|
|
|
@ -153,6 +153,12 @@ class TasmotaDiscoveryUpdate(TasmotaEntity):
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def add_to_platform_abort(self) -> None:
|
||||||
|
"""Abort adding an entity to a platform."""
|
||||||
|
clear_discovery_hash(self.hass, self._discovery_hash)
|
||||||
|
super().add_to_platform_abort()
|
||||||
|
|
||||||
async def async_will_remove_from_hass(self) -> None:
|
async def async_will_remove_from_hass(self) -> None:
|
||||||
"""Stop listening to signal and cleanup discovery data.."""
|
"""Stop listening to signal and cleanup discovery data.."""
|
||||||
if not self._removed_from_hass:
|
if not self._removed_from_hass:
|
||||||
|
|
|
@ -58,7 +58,7 @@ from homeassistant.core import callback
|
||||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||||
from homeassistant.helpers.entity import Entity
|
from homeassistant.helpers.entity import Entity
|
||||||
|
|
||||||
from .const import DOMAIN as TASMOTA_DOMAIN
|
from .const import DATA_REMOVE_DISCOVER_COMPONENT, DOMAIN as TASMOTA_DOMAIN
|
||||||
from .discovery import TASMOTA_DISCOVERY_ENTITY_NEW
|
from .discovery import TASMOTA_DISCOVERY_ENTITY_NEW
|
||||||
from .mixins import TasmotaAvailability, TasmotaDiscoveryUpdate
|
from .mixins import TasmotaAvailability, TasmotaDiscoveryUpdate
|
||||||
|
|
||||||
|
@ -123,7 +123,9 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
async_dispatcher_connect(
|
hass.data[
|
||||||
|
DATA_REMOVE_DISCOVER_COMPONENT.format(sensor.DOMAIN)
|
||||||
|
] = async_dispatcher_connect(
|
||||||
hass,
|
hass,
|
||||||
TASMOTA_DISCOVERY_ENTITY_NEW.format(sensor.DOMAIN, TASMOTA_DOMAIN),
|
TASMOTA_DISCOVERY_ENTITY_NEW.format(sensor.DOMAIN, TASMOTA_DOMAIN),
|
||||||
async_discover_sensor,
|
async_discover_sensor,
|
||||||
|
|
|
@ -5,7 +5,7 @@ from homeassistant.components.switch import SwitchEntity
|
||||||
from homeassistant.core import callback
|
from homeassistant.core import callback
|
||||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||||
|
|
||||||
from .const import DOMAIN as TASMOTA_DOMAIN
|
from .const import DATA_REMOVE_DISCOVER_COMPONENT, DOMAIN as TASMOTA_DOMAIN
|
||||||
from .discovery import TASMOTA_DISCOVERY_ENTITY_NEW
|
from .discovery import TASMOTA_DISCOVERY_ENTITY_NEW
|
||||||
from .mixins import TasmotaAvailability, TasmotaDiscoveryUpdate
|
from .mixins import TasmotaAvailability, TasmotaDiscoveryUpdate
|
||||||
|
|
||||||
|
@ -24,7 +24,9 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
async_dispatcher_connect(
|
hass.data[
|
||||||
|
DATA_REMOVE_DISCOVER_COMPONENT.format(switch.DOMAIN)
|
||||||
|
] = async_dispatcher_connect(
|
||||||
hass,
|
hass,
|
||||||
TASMOTA_DISCOVERY_ENTITY_NEW.format(switch.DOMAIN, TASMOTA_DOMAIN),
|
TASMOTA_DISCOVERY_ENTITY_NEW.format(switch.DOMAIN, TASMOTA_DOMAIN),
|
||||||
async_discover,
|
async_discover,
|
||||||
|
|
|
@ -732,7 +732,7 @@ hass-nabucasa==0.37.1
|
||||||
hass_splunk==0.1.1
|
hass_splunk==0.1.1
|
||||||
|
|
||||||
# homeassistant.components.tasmota
|
# homeassistant.components.tasmota
|
||||||
hatasmota==0.0.19
|
hatasmota==0.0.20
|
||||||
|
|
||||||
# homeassistant.components.jewish_calendar
|
# homeassistant.components.jewish_calendar
|
||||||
hdate==0.9.5
|
hdate==0.9.5
|
||||||
|
|
|
@ -367,7 +367,7 @@ hangups==0.4.11
|
||||||
hass-nabucasa==0.37.1
|
hass-nabucasa==0.37.1
|
||||||
|
|
||||||
# homeassistant.components.tasmota
|
# homeassistant.components.tasmota
|
||||||
hatasmota==0.0.19
|
hatasmota==0.0.20
|
||||||
|
|
||||||
# homeassistant.components.jewish_calendar
|
# homeassistant.components.jewish_calendar
|
||||||
hdate==0.9.5
|
hdate==0.9.5
|
||||||
|
|
|
@ -72,6 +72,7 @@ async def setup_tasmota_helper(hass):
|
||||||
entry.add_to_hass(hass)
|
entry.add_to_hass(hass)
|
||||||
|
|
||||||
assert await hass.config_entries.async_setup(entry.entry_id)
|
assert await hass.config_entries.async_setup(entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
assert "tasmota" in hass.config.components
|
assert "tasmota" in hass.config.components
|
||||||
|
|
||||||
|
|
|
@ -27,40 +27,41 @@ async def test_valid_discovery_message(hass, mqtt_mock, caplog):
|
||||||
config = copy.deepcopy(DEFAULT_CONFIG)
|
config = copy.deepcopy(DEFAULT_CONFIG)
|
||||||
|
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.tasmota.discovery.tasmota_has_entities_with_platform"
|
"homeassistant.components.tasmota.discovery.tasmota_get_device_config",
|
||||||
) as mock_tasmota_has_entities:
|
return_value={},
|
||||||
|
) as mock_tasmota_get_device_config:
|
||||||
await setup_tasmota_helper(hass)
|
await setup_tasmota_helper(hass)
|
||||||
|
|
||||||
async_fire_mqtt_message(
|
async_fire_mqtt_message(
|
||||||
hass, f"{DEFAULT_PREFIX}/00000049A3BC/config", json.dumps(config)
|
hass, f"{DEFAULT_PREFIX}/00000049A3BC/config", json.dumps(config)
|
||||||
)
|
)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert mock_tasmota_has_entities.called
|
assert mock_tasmota_get_device_config.called
|
||||||
|
|
||||||
|
|
||||||
async def test_invalid_topic(hass, mqtt_mock):
|
async def test_invalid_topic(hass, mqtt_mock):
|
||||||
"""Test receiving discovery message on wrong topic."""
|
"""Test receiving discovery message on wrong topic."""
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.tasmota.discovery.tasmota_has_entities_with_platform"
|
"homeassistant.components.tasmota.discovery.tasmota_get_device_config"
|
||||||
) as mock_tasmota_has_entities:
|
) as mock_tasmota_get_device_config:
|
||||||
await setup_tasmota_helper(hass)
|
await setup_tasmota_helper(hass)
|
||||||
|
|
||||||
async_fire_mqtt_message(hass, f"{DEFAULT_PREFIX}/123456/configuration", "{}")
|
async_fire_mqtt_message(hass, f"{DEFAULT_PREFIX}/123456/configuration", "{}")
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert not mock_tasmota_has_entities.called
|
assert not mock_tasmota_get_device_config.called
|
||||||
|
|
||||||
|
|
||||||
async def test_invalid_message(hass, mqtt_mock, caplog):
|
async def test_invalid_message(hass, mqtt_mock, caplog):
|
||||||
"""Test receiving an invalid message."""
|
"""Test receiving an invalid message."""
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.tasmota.discovery.tasmota_has_entities_with_platform"
|
"homeassistant.components.tasmota.discovery.tasmota_get_device_config"
|
||||||
) as mock_tasmota_has_entities:
|
) as mock_tasmota_get_device_config:
|
||||||
await setup_tasmota_helper(hass)
|
await setup_tasmota_helper(hass)
|
||||||
|
|
||||||
async_fire_mqtt_message(hass, f"{DEFAULT_PREFIX}/123456/config", "asd")
|
async_fire_mqtt_message(hass, f"{DEFAULT_PREFIX}/123456/config", "asd")
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert "Invalid discovery message" in caplog.text
|
assert "Invalid discovery message" in caplog.text
|
||||||
assert not mock_tasmota_has_entities.called
|
assert not mock_tasmota_get_device_config.called
|
||||||
|
|
||||||
|
|
||||||
async def test_invalid_mac(hass, mqtt_mock, caplog):
|
async def test_invalid_mac(hass, mqtt_mock, caplog):
|
||||||
|
@ -68,8 +69,8 @@ async def test_invalid_mac(hass, mqtt_mock, caplog):
|
||||||
config = copy.deepcopy(DEFAULT_CONFIG)
|
config = copy.deepcopy(DEFAULT_CONFIG)
|
||||||
|
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.tasmota.discovery.tasmota_has_entities_with_platform"
|
"homeassistant.components.tasmota.discovery.tasmota_get_device_config"
|
||||||
) as mock_tasmota_has_entities:
|
) as mock_tasmota_get_device_config:
|
||||||
await setup_tasmota_helper(hass)
|
await setup_tasmota_helper(hass)
|
||||||
|
|
||||||
async_fire_mqtt_message(
|
async_fire_mqtt_message(
|
||||||
|
@ -77,7 +78,7 @@ async def test_invalid_mac(hass, mqtt_mock, caplog):
|
||||||
)
|
)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert "MAC mismatch" in caplog.text
|
assert "MAC mismatch" in caplog.text
|
||||||
assert not mock_tasmota_has_entities.called
|
assert not mock_tasmota_get_device_config.called
|
||||||
|
|
||||||
|
|
||||||
async def test_correct_config_discovery(
|
async def test_correct_config_discovery(
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
"""The tests for the Tasmota sensor platform."""
|
"""The tests for the Tasmota sensor platform."""
|
||||||
import copy
|
import copy
|
||||||
|
from datetime import timedelta
|
||||||
import json
|
import json
|
||||||
|
|
||||||
from hatasmota.utils import (
|
from hatasmota.utils import (
|
||||||
|
@ -9,9 +10,11 @@ from hatasmota.utils import (
|
||||||
)
|
)
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
from homeassistant import config_entries
|
||||||
from homeassistant.components import sensor
|
from homeassistant.components import sensor
|
||||||
from homeassistant.components.tasmota.const import DEFAULT_PREFIX
|
from homeassistant.components.tasmota.const import DEFAULT_PREFIX
|
||||||
from homeassistant.const import ATTR_ASSUMED_STATE, STATE_UNKNOWN
|
from homeassistant.const import ATTR_ASSUMED_STATE, STATE_UNKNOWN
|
||||||
|
from homeassistant.util import dt
|
||||||
|
|
||||||
from .test_common import (
|
from .test_common import (
|
||||||
DEFAULT_CONFIG,
|
DEFAULT_CONFIG,
|
||||||
|
@ -27,7 +30,7 @@ from .test_common import (
|
||||||
)
|
)
|
||||||
|
|
||||||
from tests.async_mock import patch
|
from tests.async_mock import patch
|
||||||
from tests.common import async_fire_mqtt_message
|
from tests.common import async_fire_mqtt_message, async_fire_time_changed
|
||||||
|
|
||||||
DEFAULT_SENSOR_CONFIG = {
|
DEFAULT_SENSOR_CONFIG = {
|
||||||
"sn": {
|
"sn": {
|
||||||
|
@ -377,6 +380,64 @@ async def test_indexed_sensor_attributes(hass, mqtt_mock, setup_tasmota):
|
||||||
assert state.attributes.get("unit_of_measurement") == "ppm"
|
assert state.attributes.get("unit_of_measurement") == "ppm"
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("status_sensor_disabled", [False])
|
||||||
|
async def test_enable_status_sensor(hass, mqtt_mock, setup_tasmota):
|
||||||
|
"""Test enabling status sensor."""
|
||||||
|
entity_reg = await hass.helpers.entity_registry.async_get_registry()
|
||||||
|
|
||||||
|
config = copy.deepcopy(DEFAULT_CONFIG)
|
||||||
|
mac = config["mac"]
|
||||||
|
|
||||||
|
async_fire_mqtt_message(
|
||||||
|
hass,
|
||||||
|
f"{DEFAULT_PREFIX}/{mac}/config",
|
||||||
|
json.dumps(config),
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
state = hass.states.get("sensor.tasmota_status")
|
||||||
|
assert state is None
|
||||||
|
entry = entity_reg.async_get("sensor.tasmota_status")
|
||||||
|
assert entry.disabled
|
||||||
|
assert entry.disabled_by == "integration"
|
||||||
|
|
||||||
|
# Enable the status sensor
|
||||||
|
updated_entry = entity_reg.async_update_entity(
|
||||||
|
"sensor.tasmota_status", disabled_by=None
|
||||||
|
)
|
||||||
|
assert updated_entry != entry
|
||||||
|
assert updated_entry.disabled is False
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
async_fire_time_changed(
|
||||||
|
hass,
|
||||||
|
dt.utcnow()
|
||||||
|
+ timedelta(
|
||||||
|
seconds=config_entries.EntityRegistryDisabledHandler.RELOAD_AFTER_UPDATE_DELAY
|
||||||
|
+ 1
|
||||||
|
),
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
# Fake re-send of retained discovery message
|
||||||
|
async_fire_mqtt_message(
|
||||||
|
hass,
|
||||||
|
f"{DEFAULT_PREFIX}/{mac}/config",
|
||||||
|
json.dumps(config),
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
state = hass.states.get("sensor.tasmota_status")
|
||||||
|
assert state.state == "unavailable"
|
||||||
|
assert not state.attributes.get(ATTR_ASSUMED_STATE)
|
||||||
|
|
||||||
|
async_fire_mqtt_message(hass, "tasmota_49A3BC/tele/LWT", "Online")
|
||||||
|
state = hass.states.get("sensor.tasmota_status")
|
||||||
|
assert state.state == STATE_UNKNOWN
|
||||||
|
assert not state.attributes.get(ATTR_ASSUMED_STATE)
|
||||||
|
|
||||||
|
|
||||||
async def test_availability_when_connection_lost(
|
async def test_availability_when_connection_lost(
|
||||||
hass, mqtt_client_mock, mqtt_mock, setup_tasmota
|
hass, mqtt_client_mock, mqtt_mock, setup_tasmota
|
||||||
):
|
):
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue