Bump hatasmota to 0.0.10, minor refactor of discovery (#41331)
* Bump hatasmota to 0.0.10, minor refactor of discovery * Update tests * Add missing docstrings
This commit is contained in:
parent
9b947e08bf
commit
42fb0e9545
7 changed files with 155 additions and 138 deletions
|
@ -2,13 +2,13 @@
|
||||||
import asyncio
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from hatasmota.const import CONF_RELAY
|
|
||||||
from hatasmota.discovery import (
|
from hatasmota.discovery import (
|
||||||
TasmotaDiscovery,
|
TasmotaDiscovery,
|
||||||
get_device_config as tasmota_get_device_config,
|
get_device_config as tasmota_get_device_config,
|
||||||
get_entities_for_platform as tasmota_get_entities_for_platform,
|
get_entities_for_platform as tasmota_get_entities_for_platform,
|
||||||
get_entity as tasmota_get_entity,
|
get_entity as tasmota_get_entity,
|
||||||
has_entities_with_platform as tasmota_has_entities_with_platform,
|
has_entities_with_platform as tasmota_has_entities_with_platform,
|
||||||
|
unique_id_from_hash,
|
||||||
)
|
)
|
||||||
|
|
||||||
from homeassistant.helpers.dispatcher import async_dispatcher_send
|
from homeassistant.helpers.dispatcher import async_dispatcher_send
|
||||||
|
@ -18,16 +18,16 @@ from .const import DOMAIN
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
SUPPORTED_COMPONENTS = {
|
SUPPORTED_PLATFORMS = [
|
||||||
"switch": CONF_RELAY,
|
"switch",
|
||||||
}
|
]
|
||||||
|
|
||||||
ALREADY_DISCOVERED = "tasmota_discovered_components"
|
ALREADY_DISCOVERED = "tasmota_discovered_components"
|
||||||
CONFIG_ENTRY_IS_SETUP = "tasmota_config_entry_is_setup"
|
CONFIG_ENTRY_IS_SETUP = "tasmota_config_entry_is_setup"
|
||||||
DATA_CONFIG_ENTRY_LOCK = "tasmota_config_entry_lock"
|
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_{}_{}_{}_{}"
|
||||||
|
|
||||||
|
|
||||||
def clear_discovery_hash(hass, discovery_hash):
|
def clear_discovery_hash(hass, discovery_hash):
|
||||||
|
@ -45,6 +45,52 @@ async def async_start(
|
||||||
) -> bool:
|
) -> bool:
|
||||||
"""Start MQTT Discovery."""
|
"""Start MQTT 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):
|
||||||
|
"""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:
|
||||||
|
_LOGGER.debug("Adding new entity: %s %s", platform, discovery_hash)
|
||||||
|
tasmota_entity = tasmota_get_entity(tasmota_entity_config, tasmota_mqtt)
|
||||||
|
|
||||||
|
hass.data[ALREADY_DISCOVERED][discovery_hash] = None
|
||||||
|
|
||||||
|
async_dispatcher_send(
|
||||||
|
hass,
|
||||||
|
TASMOTA_DISCOVERY_ENTITY_NEW.format(platform),
|
||||||
|
tasmota_entity,
|
||||||
|
discovery_hash,
|
||||||
|
)
|
||||||
|
|
||||||
async def async_device_discovered(payload, mac):
|
async def async_device_discovered(payload, mac):
|
||||||
"""Process the received message."""
|
"""Process the received message."""
|
||||||
|
|
||||||
|
@ -57,65 +103,21 @@ async def async_start(
|
||||||
hass, TASMOTA_DISCOVERY_DEVICE, tasmota_device_config, mac
|
hass, TASMOTA_DISCOVERY_DEVICE, tasmota_device_config, mac
|
||||||
)
|
)
|
||||||
|
|
||||||
async with hass.data[DATA_CONFIG_ENTRY_LOCK]:
|
if not payload:
|
||||||
for component, component_key in SUPPORTED_COMPONENTS.items():
|
return
|
||||||
if not tasmota_has_entities_with_platform(payload, component_key):
|
|
||||||
continue
|
|
||||||
config_entries_key = f"{component}.tasmota"
|
|
||||||
if config_entries_key not in hass.data[CONFIG_ENTRY_IS_SETUP]:
|
|
||||||
await hass.config_entries.async_forward_entry_setup(
|
|
||||||
config_entry, component
|
|
||||||
)
|
|
||||||
hass.data[CONFIG_ENTRY_IS_SETUP].add(config_entries_key)
|
|
||||||
|
|
||||||
for component, component_key in SUPPORTED_COMPONENTS.items():
|
for platform in SUPPORTED_PLATFORMS:
|
||||||
tasmota_entities = tasmota_get_entities_for_platform(payload, component_key)
|
if not tasmota_has_entities_with_platform(payload, platform):
|
||||||
for (idx, tasmota_entity_config) in enumerate(tasmota_entities):
|
continue
|
||||||
discovery_hash = (mac, component, idx)
|
await _load_platform(platform)
|
||||||
if not tasmota_entity_config:
|
|
||||||
# Entity disabled, clean up entity registry
|
|
||||||
entity_registry = (
|
|
||||||
await hass.helpers.entity_registry.async_get_registry()
|
|
||||||
)
|
|
||||||
unique_id = "{}_{}_{}".format(*discovery_hash)
|
|
||||||
entity_id = entity_registry.async_get_entity_id(
|
|
||||||
component, DOMAIN, unique_id
|
|
||||||
)
|
|
||||||
if entity_id:
|
|
||||||
_LOGGER.debug(
|
|
||||||
"Removing entity: %s %s", component, discovery_hash
|
|
||||||
)
|
|
||||||
entity_registry.async_remove(entity_id)
|
|
||||||
continue
|
|
||||||
|
|
||||||
if discovery_hash in hass.data[ALREADY_DISCOVERED]:
|
for platform in SUPPORTED_PLATFORMS:
|
||||||
_LOGGER.debug(
|
tasmota_entities = tasmota_get_entities_for_platform(payload, platform)
|
||||||
"Entity already added, sending update: %s %s",
|
for (tasmota_entity_config, discovery_hash) in tasmota_entities:
|
||||||
component,
|
await _discover_entity(tasmota_entity_config, discovery_hash, platform)
|
||||||
discovery_hash,
|
|
||||||
)
|
|
||||||
async_dispatcher_send(
|
|
||||||
hass,
|
|
||||||
TASMOTA_DISCOVERY_ENTITY_UPDATED.format(*discovery_hash),
|
|
||||||
tasmota_entity_config,
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
_LOGGER.debug("Adding new entity: %s %s", component, discovery_hash)
|
|
||||||
hass.data[ALREADY_DISCOVERED][discovery_hash] = None
|
|
||||||
|
|
||||||
tasmota_entity = tasmota_get_entity(
|
|
||||||
tasmota_entity_config, component_key, tasmota_mqtt
|
|
||||||
)
|
|
||||||
|
|
||||||
async_dispatcher_send(
|
|
||||||
hass,
|
|
||||||
TASMOTA_DISCOVERY_ENTITY_NEW.format(component),
|
|
||||||
tasmota_entity,
|
|
||||||
discovery_hash,
|
|
||||||
)
|
|
||||||
|
|
||||||
hass.data[DATA_CONFIG_ENTRY_LOCK] = asyncio.Lock()
|
hass.data[DATA_CONFIG_ENTRY_LOCK] = asyncio.Lock()
|
||||||
hass.data[CONFIG_ENTRY_IS_SETUP] = set()
|
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(async_device_discovered)
|
await tasmota_discovery.start_discovery(async_device_discovered, None)
|
||||||
|
|
|
@ -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.9"],
|
"requirements": ["hatasmota==0.0.10"],
|
||||||
"dependencies": ["mqtt"],
|
"dependencies": ["mqtt"],
|
||||||
"codeowners": ["@emontnemery"]
|
"codeowners": ["@emontnemery"]
|
||||||
}
|
}
|
||||||
|
|
|
@ -729,7 +729,7 @@ hass-nabucasa==0.37.0
|
||||||
hass_splunk==0.1.1
|
hass_splunk==0.1.1
|
||||||
|
|
||||||
# homeassistant.components.tasmota
|
# homeassistant.components.tasmota
|
||||||
hatasmota==0.0.9
|
hatasmota==0.0.10
|
||||||
|
|
||||||
# homeassistant.components.jewish_calendar
|
# homeassistant.components.jewish_calendar
|
||||||
hdate==0.9.5
|
hdate==0.9.5
|
||||||
|
|
|
@ -361,7 +361,7 @@ hangups==0.4.11
|
||||||
hass-nabucasa==0.37.0
|
hass-nabucasa==0.37.0
|
||||||
|
|
||||||
# homeassistant.components.tasmota
|
# homeassistant.components.tasmota
|
||||||
hatasmota==0.0.9
|
hatasmota==0.0.10
|
||||||
|
|
||||||
# homeassistant.components.jewish_calendar
|
# homeassistant.components.jewish_calendar
|
||||||
hdate==0.9.5
|
hdate==0.9.5
|
||||||
|
|
|
@ -11,8 +11,8 @@ from hatasmota.const import (
|
||||||
PREFIX_TELE,
|
PREFIX_TELE,
|
||||||
)
|
)
|
||||||
from hatasmota.utils import (
|
from hatasmota.utils import (
|
||||||
get_state_offline,
|
config_get_state_offline,
|
||||||
get_state_online,
|
config_get_state_online,
|
||||||
get_topic_tele_state,
|
get_topic_tele_state,
|
||||||
get_topic_tele_will,
|
get_topic_tele_will,
|
||||||
)
|
)
|
||||||
|
@ -23,6 +23,38 @@ from homeassistant.const import STATE_UNAVAILABLE
|
||||||
from tests.async_mock import ANY
|
from tests.async_mock import ANY
|
||||||
from tests.common import async_fire_mqtt_message
|
from tests.common import async_fire_mqtt_message
|
||||||
|
|
||||||
|
DEFAULT_CONFIG = {
|
||||||
|
"ip": "192.168.15.10",
|
||||||
|
"dn": "Tasmota",
|
||||||
|
"fn": ["Test", "Beer", "Milk", "Four", None],
|
||||||
|
"hn": "tasmota_49A3BC-0956",
|
||||||
|
"lk": 1, # RGB + white channels linked to a single light
|
||||||
|
"mac": "00000049A3BC",
|
||||||
|
"md": "Sonoff Basic",
|
||||||
|
"ofln": "Offline",
|
||||||
|
"onln": "Online",
|
||||||
|
"state": ["OFF", "ON", "TOGGLE", "HOLD"],
|
||||||
|
"sw": "8.4.0.2",
|
||||||
|
"t": "tasmota_49A3BC",
|
||||||
|
"ft": "%topic%/%prefix%/",
|
||||||
|
"tp": ["cmnd", "stat", "tele"],
|
||||||
|
"rl": [0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
"swc": [-1, -1, -1, -1, -1, -1, -1, -1],
|
||||||
|
"btn": [0, 0, 0, 0],
|
||||||
|
"so": {
|
||||||
|
"11": 0, # Swap button single and double press functionality
|
||||||
|
"13": 0, # Allow immediate action on single button press
|
||||||
|
"17": 1, # Show Color string as hex or comma-separated
|
||||||
|
"20": 0, # Update of Dimmer/Color/CT without turning power on
|
||||||
|
"30": 0, # Enforce Home Assistant auto-discovery as light
|
||||||
|
"68": 0, # Multi-channel PWM instead of a single light
|
||||||
|
"73": 0, # Enable Buttons decoupling and send multi-press and hold MQTT messages
|
||||||
|
"80": 0, # Blinds and shutters support
|
||||||
|
},
|
||||||
|
"lt_st": 0,
|
||||||
|
"ver": 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
async def help_test_availability_when_connection_lost(
|
async def help_test_availability_when_connection_lost(
|
||||||
hass, mqtt_client_mock, mqtt_mock, domain, config
|
hass, mqtt_client_mock, mqtt_mock, domain, config
|
||||||
|
@ -37,7 +69,7 @@ async def help_test_availability_when_connection_lost(
|
||||||
async_fire_mqtt_message(
|
async_fire_mqtt_message(
|
||||||
hass,
|
hass,
|
||||||
get_topic_tele_will(config),
|
get_topic_tele_will(config),
|
||||||
get_state_online(config),
|
config_get_state_online(config),
|
||||||
)
|
)
|
||||||
|
|
||||||
state = hass.states.get(f"{domain}.test")
|
state = hass.states.get(f"{domain}.test")
|
||||||
|
@ -83,7 +115,7 @@ async def help_test_availability(
|
||||||
async_fire_mqtt_message(
|
async_fire_mqtt_message(
|
||||||
hass,
|
hass,
|
||||||
get_topic_tele_will(config),
|
get_topic_tele_will(config),
|
||||||
get_state_online(config),
|
config_get_state_online(config),
|
||||||
)
|
)
|
||||||
|
|
||||||
state = hass.states.get(f"{domain}.test")
|
state = hass.states.get(f"{domain}.test")
|
||||||
|
@ -92,7 +124,7 @@ async def help_test_availability(
|
||||||
async_fire_mqtt_message(
|
async_fire_mqtt_message(
|
||||||
hass,
|
hass,
|
||||||
get_topic_tele_will(config),
|
get_topic_tele_will(config),
|
||||||
get_state_offline(config),
|
config_get_state_offline(config),
|
||||||
)
|
)
|
||||||
|
|
||||||
state = hass.states.get(f"{domain}.test")
|
state = hass.states.get(f"{domain}.test")
|
||||||
|
@ -124,11 +156,11 @@ async def help_test_availability_discovery_update(
|
||||||
availability_topic1 = get_topic_tele_will(config1)
|
availability_topic1 = get_topic_tele_will(config1)
|
||||||
availability_topic2 = get_topic_tele_will(config2)
|
availability_topic2 = get_topic_tele_will(config2)
|
||||||
assert availability_topic1 != availability_topic2
|
assert availability_topic1 != availability_topic2
|
||||||
offline1 = get_state_offline(config1)
|
offline1 = config_get_state_offline(config1)
|
||||||
offline2 = get_state_offline(config2)
|
offline2 = config_get_state_offline(config2)
|
||||||
assert offline1 != offline2
|
assert offline1 != offline2
|
||||||
online1 = get_state_online(config1)
|
online1 = config_get_state_online(config1)
|
||||||
online2 = get_state_online(config2)
|
online2 = config_get_state_online(config2)
|
||||||
assert online1 != online2
|
assert online1 != online2
|
||||||
|
|
||||||
async_fire_mqtt_message(hass, f"{DEFAULT_PREFIX}/{config1[CONF_MAC]}/config", data1)
|
async_fire_mqtt_message(hass, f"{DEFAULT_PREFIX}/{config1[CONF_MAC]}/config", data1)
|
||||||
|
@ -232,13 +264,12 @@ async def help_test_discovery_update_unchanged(
|
||||||
assert discovery_update.called
|
assert discovery_update.called
|
||||||
|
|
||||||
|
|
||||||
async def help_test_discovery_device_remove(hass, mqtt_mock, domain, config):
|
async def help_test_discovery_device_remove(hass, mqtt_mock, domain, unique_id, config):
|
||||||
"""Test domain entity is removed when device is removed."""
|
"""Test domain entity is removed when device is removed."""
|
||||||
device_reg = await hass.helpers.device_registry.async_get_registry()
|
device_reg = await hass.helpers.device_registry.async_get_registry()
|
||||||
entity_reg = await hass.helpers.entity_registry.async_get_registry()
|
entity_reg = await hass.helpers.entity_registry.async_get_registry()
|
||||||
|
|
||||||
config = copy.deepcopy(config)
|
config = copy.deepcopy(config)
|
||||||
unique_id = f"{config[CONF_MAC]}_{domain}_0"
|
|
||||||
|
|
||||||
data = json.dumps(config)
|
data = json.dumps(config)
|
||||||
async_fire_mqtt_message(hass, f"{DEFAULT_PREFIX}/{config[CONF_MAC]}/config", data)
|
async_fire_mqtt_message(hass, f"{DEFAULT_PREFIX}/{config[CONF_MAC]}/config", data)
|
||||||
|
@ -304,16 +335,17 @@ async def help_test_entity_id_update_discovery_update(hass, mqtt_mock, domain, c
|
||||||
async_fire_mqtt_message(hass, f"{DEFAULT_PREFIX}/{config[CONF_MAC]}/config", data)
|
async_fire_mqtt_message(hass, f"{DEFAULT_PREFIX}/{config[CONF_MAC]}/config", data)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
async_fire_mqtt_message(hass, topic, get_state_online(config))
|
async_fire_mqtt_message(hass, topic, config_get_state_online(config))
|
||||||
state = hass.states.get(f"{domain}.test")
|
state = hass.states.get(f"{domain}.test")
|
||||||
assert state.state != STATE_UNAVAILABLE
|
assert state.state != STATE_UNAVAILABLE
|
||||||
|
|
||||||
async_fire_mqtt_message(hass, topic, get_state_offline(config))
|
async_fire_mqtt_message(hass, topic, config_get_state_offline(config))
|
||||||
state = hass.states.get(f"{domain}.test")
|
state = hass.states.get(f"{domain}.test")
|
||||||
assert state.state == STATE_UNAVAILABLE
|
assert state.state == STATE_UNAVAILABLE
|
||||||
|
|
||||||
entity_reg.async_update_entity(f"{domain}.test", new_entity_id=f"{domain}.milk")
|
entity_reg.async_update_entity(f"{domain}.test", new_entity_id=f"{domain}.milk")
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
assert hass.states.get(f"{domain}.milk")
|
||||||
|
|
||||||
assert config[CONF_PREFIX][PREFIX_TELE] != "tele2"
|
assert config[CONF_PREFIX][PREFIX_TELE] != "tele2"
|
||||||
config[CONF_PREFIX][PREFIX_TELE] = "tele2"
|
config[CONF_PREFIX][PREFIX_TELE] = "tele2"
|
||||||
|
@ -323,6 +355,6 @@ async def help_test_entity_id_update_discovery_update(hass, mqtt_mock, domain, c
|
||||||
assert len(hass.states.async_entity_ids(domain)) == 1
|
assert len(hass.states.async_entity_ids(domain)) == 1
|
||||||
|
|
||||||
topic = get_topic_tele_will(config)
|
topic = get_topic_tele_will(config)
|
||||||
async_fire_mqtt_message(hass, topic, get_state_online(config))
|
async_fire_mqtt_message(hass, topic, config_get_state_online(config))
|
||||||
state = hass.states.get(f"{domain}.milk")
|
state = hass.states.get(f"{domain}.milk")
|
||||||
assert state.state != STATE_UNAVAILABLE
|
assert state.state != STATE_UNAVAILABLE
|
||||||
|
|
|
@ -2,34 +2,16 @@
|
||||||
import copy
|
import copy
|
||||||
import json
|
import json
|
||||||
|
|
||||||
from hatasmota.const import CONF_ONLINE
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from homeassistant.components.tasmota.const import DEFAULT_PREFIX
|
from homeassistant.components.tasmota.const import DEFAULT_PREFIX
|
||||||
from homeassistant.components.tasmota.discovery import ALREADY_DISCOVERED
|
from homeassistant.components.tasmota.discovery import ALREADY_DISCOVERED
|
||||||
|
|
||||||
|
from .conftest import setup_tasmota_helper
|
||||||
|
from .test_common import DEFAULT_CONFIG
|
||||||
|
|
||||||
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
|
||||||
from tests.components.tasmota.conftest import setup_tasmota_helper
|
|
||||||
|
|
||||||
DEFAULT_CONFIG = {
|
|
||||||
"dn": "My Device",
|
|
||||||
"fn": ["Beer", "Milk", "Three", "Four", "Five"],
|
|
||||||
"hn": "tasmota_49A3BC",
|
|
||||||
"mac": "00000049A3BC",
|
|
||||||
"md": "Sonoff 123",
|
|
||||||
"ofl": "offline",
|
|
||||||
CONF_ONLINE: "online",
|
|
||||||
"state": ["OFF", "ON", "TOGGLE", "HOLD"],
|
|
||||||
"sw": "2.3.3.4",
|
|
||||||
"t": "tasmota_49A3BC",
|
|
||||||
"t_f": "%topic%/%prefix%/",
|
|
||||||
"t_p": ["cmnd", "stat", "tele"],
|
|
||||||
"li": [0, 0, 0, 0, 0, 0, 0, 0],
|
|
||||||
"rl": [0, 0, 0, 0, 0, 0, 0, 0],
|
|
||||||
"se": [],
|
|
||||||
"ver": 1,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
async def test_subscribing_config_topic(hass, mqtt_mock, setup_tasmota):
|
async def test_subscribing_config_topic(hass, mqtt_mock, setup_tasmota):
|
||||||
|
@ -118,14 +100,14 @@ async def test_correct_config_discovery(
|
||||||
# Verify device and registry entries are created
|
# Verify device and registry entries are created
|
||||||
device_entry = device_reg.async_get_device(set(), {("mac", mac)})
|
device_entry = device_reg.async_get_device(set(), {("mac", mac)})
|
||||||
assert device_entry is not None
|
assert device_entry is not None
|
||||||
entity_entry = entity_reg.async_get("switch.beer")
|
entity_entry = entity_reg.async_get("switch.test")
|
||||||
assert entity_entry is not None
|
assert entity_entry is not None
|
||||||
|
|
||||||
state = hass.states.get("switch.beer")
|
state = hass.states.get("switch.test")
|
||||||
assert state is not None
|
assert state is not None
|
||||||
assert state.name == "Beer"
|
assert state.name == "Test"
|
||||||
|
|
||||||
assert (mac, "switch", 0) in hass.data[ALREADY_DISCOVERED]
|
assert (mac, "switch", "relay", 0) in hass.data[ALREADY_DISCOVERED]
|
||||||
|
|
||||||
|
|
||||||
async def test_device_discover(
|
async def test_device_discover(
|
||||||
|
@ -337,14 +319,14 @@ async def test_entity_duplicate_discovery(hass, mqtt_mock, caplog, setup_tasmota
|
||||||
)
|
)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
state = hass.states.get("switch.beer")
|
state = hass.states.get("switch.test")
|
||||||
state_duplicate = hass.states.get("binary_sensor.beer1")
|
state_duplicate = hass.states.get("binary_sensor.beer1")
|
||||||
|
|
||||||
assert state is not None
|
assert state is not None
|
||||||
assert state.name == "Beer"
|
assert state.name == "Test"
|
||||||
assert state_duplicate is None
|
assert state_duplicate is None
|
||||||
assert (
|
assert (
|
||||||
f"Entity already added, sending update: switch ('{mac}', 'switch', 0)"
|
f"Entity already added, sending update: switch ('{mac}', 'switch', 'relay', 0)"
|
||||||
in caplog.text
|
in caplog.text
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -364,9 +346,9 @@ async def test_entity_duplicate_removal(hass, mqtt_mock, caplog, setup_tasmota):
|
||||||
config["rl"][0] = 0
|
config["rl"][0] = 0
|
||||||
async_fire_mqtt_message(hass, f"{DEFAULT_PREFIX}/{mac}/config", json.dumps(config))
|
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()
|
||||||
assert f"Removing entity: switch ('{mac}', 'switch', 0)" in caplog.text
|
assert f"Removing entity: switch ('{mac}', 'switch', 'relay', 0)" in caplog.text
|
||||||
|
|
||||||
caplog.clear()
|
caplog.clear()
|
||||||
async_fire_mqtt_message(hass, f"{DEFAULT_PREFIX}/{mac}/config", json.dumps(config))
|
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()
|
||||||
assert f"Removing entity: switch ('{mac}', 'switch', 0)" not in caplog.text
|
assert "Removing entity: switch" not in caplog.text
|
||||||
|
|
|
@ -1,14 +1,13 @@
|
||||||
"""The tests for the MQTT switch platform."""
|
"""The tests for the Tasmota switch platform."""
|
||||||
import copy
|
import copy
|
||||||
import json
|
import json
|
||||||
|
|
||||||
from hatasmota.const import CONF_ONLINE
|
|
||||||
|
|
||||||
from homeassistant.components import switch
|
from homeassistant.components import switch
|
||||||
from homeassistant.components.tasmota.const import DEFAULT_PREFIX
|
from homeassistant.components.tasmota.const import DEFAULT_PREFIX
|
||||||
from homeassistant.const import ATTR_ASSUMED_STATE, STATE_OFF, STATE_ON
|
from homeassistant.const import ATTR_ASSUMED_STATE, STATE_OFF, STATE_ON
|
||||||
|
|
||||||
from .test_common import (
|
from .test_common import (
|
||||||
|
DEFAULT_CONFIG,
|
||||||
help_test_availability,
|
help_test_availability,
|
||||||
help_test_availability_discovery_update,
|
help_test_availability_discovery_update,
|
||||||
help_test_availability_when_connection_lost,
|
help_test_availability_when_connection_lost,
|
||||||
|
@ -23,29 +22,11 @@ from tests.async_mock import patch
|
||||||
from tests.common import async_fire_mqtt_message
|
from tests.common import async_fire_mqtt_message
|
||||||
from tests.components.switch import common
|
from tests.components.switch import common
|
||||||
|
|
||||||
DEFAULT_CONFIG = {
|
|
||||||
"dn": "My Device",
|
|
||||||
"fn": ["Test", "Beer", "Milk", "Four", "Five"],
|
|
||||||
"hn": "tasmota_49A3BC",
|
|
||||||
"mac": "00000049A3BC",
|
|
||||||
"md": "Sonoff 123",
|
|
||||||
"ofl": "offline",
|
|
||||||
CONF_ONLINE: "online",
|
|
||||||
"state": ["OFF", "ON", "TOGGLE", "HOLD"],
|
|
||||||
"sw": "2.3.3.4",
|
|
||||||
"t": "tasmota_49A3BC",
|
|
||||||
"t_f": "%topic%/%prefix%/",
|
|
||||||
"t_p": ["cmnd", "stat", "tele"],
|
|
||||||
"li": [0, 0, 0, 0, 0, 0, 0, 0],
|
|
||||||
"rl": [1, 0, 0, 0, 0, 0, 0, 0],
|
|
||||||
"se": [],
|
|
||||||
"ver": 1,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
async def test_controlling_state_via_mqtt(hass, mqtt_mock, setup_tasmota):
|
async def test_controlling_state_via_mqtt(hass, mqtt_mock, setup_tasmota):
|
||||||
"""Test state update via MQTT."""
|
"""Test state update via MQTT."""
|
||||||
config = copy.deepcopy(DEFAULT_CONFIG)
|
config = copy.deepcopy(DEFAULT_CONFIG)
|
||||||
|
config["rl"][0] = 1
|
||||||
mac = config["mac"]
|
mac = config["mac"]
|
||||||
|
|
||||||
async_fire_mqtt_message(
|
async_fire_mqtt_message(
|
||||||
|
@ -59,7 +40,7 @@ async def test_controlling_state_via_mqtt(hass, mqtt_mock, setup_tasmota):
|
||||||
assert state.state == "unavailable"
|
assert state.state == "unavailable"
|
||||||
assert not state.attributes.get(ATTR_ASSUMED_STATE)
|
assert not state.attributes.get(ATTR_ASSUMED_STATE)
|
||||||
|
|
||||||
async_fire_mqtt_message(hass, "tasmota_49A3BC/tele/LWT", "online")
|
async_fire_mqtt_message(hass, "tasmota_49A3BC/tele/LWT", "Online")
|
||||||
state = hass.states.get("switch.test")
|
state = hass.states.get("switch.test")
|
||||||
assert state.state == STATE_OFF
|
assert state.state == STATE_OFF
|
||||||
assert not state.attributes.get(ATTR_ASSUMED_STATE)
|
assert not state.attributes.get(ATTR_ASSUMED_STATE)
|
||||||
|
@ -78,6 +59,7 @@ async def test_controlling_state_via_mqtt(hass, mqtt_mock, setup_tasmota):
|
||||||
async def test_sending_mqtt_commands(hass, mqtt_mock, setup_tasmota):
|
async def test_sending_mqtt_commands(hass, mqtt_mock, setup_tasmota):
|
||||||
"""Test the sending MQTT commands."""
|
"""Test the sending MQTT commands."""
|
||||||
config = copy.deepcopy(DEFAULT_CONFIG)
|
config = copy.deepcopy(DEFAULT_CONFIG)
|
||||||
|
config["rl"][0] = 1
|
||||||
mac = config["mac"]
|
mac = config["mac"]
|
||||||
|
|
||||||
async_fire_mqtt_message(
|
async_fire_mqtt_message(
|
||||||
|
@ -87,9 +69,12 @@ async def test_sending_mqtt_commands(hass, mqtt_mock, setup_tasmota):
|
||||||
)
|
)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
async_fire_mqtt_message(hass, "tasmota_49A3BC/tele/LWT", "online")
|
async_fire_mqtt_message(hass, "tasmota_49A3BC/tele/LWT", "Online")
|
||||||
state = hass.states.get("switch.test")
|
state = hass.states.get("switch.test")
|
||||||
assert state.state == STATE_OFF
|
assert state.state == STATE_OFF
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
mqtt_mock.async_publish.reset_mock()
|
||||||
|
|
||||||
# Turn the switch on and verify MQTT message is sent
|
# Turn the switch on and verify MQTT message is sent
|
||||||
await common.async_turn_on(hass, "switch.test")
|
await common.async_turn_on(hass, "switch.test")
|
||||||
|
@ -116,26 +101,33 @@ async def test_availability_when_connection_lost(
|
||||||
hass, mqtt_client_mock, mqtt_mock, setup_tasmota
|
hass, mqtt_client_mock, mqtt_mock, setup_tasmota
|
||||||
):
|
):
|
||||||
"""Test availability after MQTT disconnection."""
|
"""Test availability after MQTT disconnection."""
|
||||||
|
config = copy.deepcopy(DEFAULT_CONFIG)
|
||||||
|
config["rl"][0] = 1
|
||||||
await help_test_availability_when_connection_lost(
|
await help_test_availability_when_connection_lost(
|
||||||
hass, mqtt_client_mock, mqtt_mock, switch.DOMAIN, DEFAULT_CONFIG
|
hass, mqtt_client_mock, mqtt_mock, switch.DOMAIN, config
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def test_availability(hass, mqtt_mock, setup_tasmota):
|
async def test_availability(hass, mqtt_mock, setup_tasmota):
|
||||||
"""Test availability."""
|
"""Test availability."""
|
||||||
await help_test_availability(hass, mqtt_mock, switch.DOMAIN, DEFAULT_CONFIG)
|
config = copy.deepcopy(DEFAULT_CONFIG)
|
||||||
|
config["rl"][0] = 1
|
||||||
|
await help_test_availability(hass, mqtt_mock, switch.DOMAIN, config)
|
||||||
|
|
||||||
|
|
||||||
async def test_availability_discovery_update(hass, mqtt_mock, setup_tasmota):
|
async def test_availability_discovery_update(hass, mqtt_mock, setup_tasmota):
|
||||||
"""Test availability discovery update."""
|
"""Test availability discovery update."""
|
||||||
|
config = copy.deepcopy(DEFAULT_CONFIG)
|
||||||
|
config["rl"][0] = 1
|
||||||
await help_test_availability_discovery_update(
|
await help_test_availability_discovery_update(
|
||||||
hass, mqtt_mock, switch.DOMAIN, DEFAULT_CONFIG
|
hass, mqtt_mock, switch.DOMAIN, config
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def test_discovery_removal_switch(hass, mqtt_mock, caplog, setup_tasmota):
|
async def test_discovery_removal_switch(hass, mqtt_mock, caplog, setup_tasmota):
|
||||||
"""Test removal of discovered switch."""
|
"""Test removal of discovered switch."""
|
||||||
config1 = copy.deepcopy(DEFAULT_CONFIG)
|
config1 = copy.deepcopy(DEFAULT_CONFIG)
|
||||||
|
config1["rl"][0] = 1
|
||||||
config2 = copy.deepcopy(DEFAULT_CONFIG)
|
config2 = copy.deepcopy(DEFAULT_CONFIG)
|
||||||
config2["rl"][0] = 0
|
config2["rl"][0] = 0
|
||||||
|
|
||||||
|
@ -148,30 +140,39 @@ async def test_discovery_update_unchanged_switch(
|
||||||
hass, mqtt_mock, caplog, setup_tasmota
|
hass, mqtt_mock, caplog, setup_tasmota
|
||||||
):
|
):
|
||||||
"""Test update of discovered switch."""
|
"""Test update of discovered switch."""
|
||||||
|
config = copy.deepcopy(DEFAULT_CONFIG)
|
||||||
|
config["rl"][0] = 1
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.tasmota.switch.TasmotaSwitch.discovery_update"
|
"homeassistant.components.tasmota.switch.TasmotaSwitch.discovery_update"
|
||||||
) as discovery_update:
|
) as discovery_update:
|
||||||
await help_test_discovery_update_unchanged(
|
await help_test_discovery_update_unchanged(
|
||||||
hass, mqtt_mock, caplog, switch.DOMAIN, DEFAULT_CONFIG, discovery_update
|
hass, mqtt_mock, caplog, switch.DOMAIN, config, discovery_update
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def test_discovery_device_remove(hass, mqtt_mock, setup_tasmota):
|
async def test_discovery_device_remove(hass, mqtt_mock, setup_tasmota):
|
||||||
"""Test device registry remove."""
|
"""Test device registry remove."""
|
||||||
|
config = copy.deepcopy(DEFAULT_CONFIG)
|
||||||
|
config["rl"][0] = 1
|
||||||
|
unique_id = f"{DEFAULT_CONFIG['mac']}_switch_relay_0"
|
||||||
await help_test_discovery_device_remove(
|
await help_test_discovery_device_remove(
|
||||||
hass, mqtt_mock, switch.DOMAIN, DEFAULT_CONFIG
|
hass, mqtt_mock, switch.DOMAIN, unique_id, config
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def test_entity_id_update_subscriptions(hass, mqtt_mock, setup_tasmota):
|
async def test_entity_id_update_subscriptions(hass, mqtt_mock, setup_tasmota):
|
||||||
"""Test MQTT subscriptions are managed when entity_id is updated."""
|
"""Test MQTT subscriptions are managed when entity_id is updated."""
|
||||||
|
config = copy.deepcopy(DEFAULT_CONFIG)
|
||||||
|
config["rl"][0] = 1
|
||||||
await help_test_entity_id_update_subscriptions(
|
await help_test_entity_id_update_subscriptions(
|
||||||
hass, mqtt_mock, switch.DOMAIN, DEFAULT_CONFIG
|
hass, mqtt_mock, switch.DOMAIN, config
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def test_entity_id_update_discovery_update(hass, mqtt_mock, setup_tasmota):
|
async def test_entity_id_update_discovery_update(hass, mqtt_mock, setup_tasmota):
|
||||||
"""Test MQTT discovery update when entity_id is updated."""
|
"""Test MQTT discovery update when entity_id is updated."""
|
||||||
|
config = copy.deepcopy(DEFAULT_CONFIG)
|
||||||
|
config["rl"][0] = 1
|
||||||
await help_test_entity_id_update_discovery_update(
|
await help_test_entity_id_update_discovery_update(
|
||||||
hass, mqtt_mock, switch.DOMAIN, DEFAULT_CONFIG
|
hass, mqtt_mock, switch.DOMAIN, config
|
||||||
)
|
)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue