Poll state when Tasmota device becomes available (#41401)
This commit is contained in:
parent
b51a160cce
commit
35287828bc
6 changed files with 163 additions and 4 deletions
|
@ -96,6 +96,8 @@ class TasmotaAvailability(TasmotaEntity):
|
|||
@callback
|
||||
def availability_updated(self, available: bool) -> None:
|
||||
"""Handle updated availability."""
|
||||
if available and not self._available:
|
||||
self._tasmota_entity.poll_status()
|
||||
self._available = available
|
||||
self.async_write_ha_state()
|
||||
|
||||
|
@ -103,13 +105,13 @@ class TasmotaAvailability(TasmotaEntity):
|
|||
def async_mqtt_connected(self, _):
|
||||
"""Update state on connection/disconnection to MQTT broker."""
|
||||
if not self.hass.is_stopping:
|
||||
if not mqtt_connected(self.hass):
|
||||
self._available = False
|
||||
self.async_write_ha_state()
|
||||
|
||||
@property
|
||||
def available(self) -> bool:
|
||||
"""Return if the device is available."""
|
||||
if not mqtt_connected(self.hass) and not self.hass.is_stopping:
|
||||
return False
|
||||
return self._available
|
||||
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ from homeassistant.components.tasmota.const import (
|
|||
DOMAIN,
|
||||
)
|
||||
|
||||
from tests.async_mock import patch
|
||||
from tests.common import MockConfigEntry, mock_device_registry, mock_registry
|
||||
|
||||
|
||||
|
@ -24,6 +25,13 @@ def entity_reg(hass):
|
|||
return mock_registry(hass)
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def disable_debounce():
|
||||
"""Set MQTT debounce timer to zero."""
|
||||
with patch("hatasmota.mqtt.DEBOUNCE_TIMEOUT", 0):
|
||||
yield
|
||||
|
||||
|
||||
async def setup_tasmota_helper(hass):
|
||||
"""Set up Tasmota."""
|
||||
hass.config.components.add("tasmota")
|
||||
|
|
|
@ -25,6 +25,7 @@ from .test_common import (
|
|||
DEFAULT_CONFIG,
|
||||
help_test_availability,
|
||||
help_test_availability_discovery_update,
|
||||
help_test_availability_poll_state,
|
||||
help_test_availability_when_connection_lost,
|
||||
help_test_discovery_device_remove,
|
||||
help_test_discovery_removal,
|
||||
|
@ -162,6 +163,18 @@ async def test_availability_discovery_update(hass, mqtt_mock, setup_tasmota):
|
|||
)
|
||||
|
||||
|
||||
async def test_availability_poll_state(
|
||||
hass, mqtt_client_mock, mqtt_mock, setup_tasmota
|
||||
):
|
||||
"""Test polling after MQTT connection (re)established."""
|
||||
config = copy.deepcopy(DEFAULT_CONFIG)
|
||||
config["swc"][0] = 1
|
||||
poll_topic = "tasmota_49A3BC/cmnd/STATUS"
|
||||
await help_test_availability_poll_state(
|
||||
hass, mqtt_client_mock, mqtt_mock, binary_sensor.DOMAIN, config, poll_topic, "8"
|
||||
)
|
||||
|
||||
|
||||
async def test_discovery_removal_binary_sensor(hass, mqtt_mock, caplog, setup_tasmota):
|
||||
"""Test removal of discovered binary_sensor."""
|
||||
config1 = copy.deepcopy(DEFAULT_CONFIG)
|
||||
|
|
|
@ -59,22 +59,27 @@ DEFAULT_CONFIG = {
|
|||
async def help_test_availability_when_connection_lost(
|
||||
hass, mqtt_client_mock, mqtt_mock, domain, config
|
||||
):
|
||||
"""Test availability after MQTT disconnection."""
|
||||
"""Test availability after MQTT disconnection.
|
||||
|
||||
This is a test helper for the TasmotaAvailability mixin.
|
||||
"""
|
||||
async_fire_mqtt_message(
|
||||
hass,
|
||||
f"{DEFAULT_PREFIX}/{config[CONF_MAC]}/config",
|
||||
json.dumps(config),
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# Device online
|
||||
async_fire_mqtt_message(
|
||||
hass,
|
||||
get_topic_tele_will(config),
|
||||
config_get_state_online(config),
|
||||
)
|
||||
|
||||
state = hass.states.get(f"{domain}.test")
|
||||
assert state.state != STATE_UNAVAILABLE
|
||||
|
||||
# Disconnected from MQTT server -> state changed to unavailable
|
||||
mqtt_mock.connected = False
|
||||
await hass.async_add_executor_job(mqtt_client_mock.on_disconnect, None, None, 0)
|
||||
await hass.async_block_till_done()
|
||||
|
@ -83,12 +88,22 @@ async def help_test_availability_when_connection_lost(
|
|||
state = hass.states.get(f"{domain}.test")
|
||||
assert state.state == STATE_UNAVAILABLE
|
||||
|
||||
# Reconnected to MQTT server -> state still unavailable
|
||||
mqtt_mock.connected = True
|
||||
await hass.async_add_executor_job(mqtt_client_mock.on_connect, None, None, None, 0)
|
||||
await hass.async_block_till_done()
|
||||
await hass.async_block_till_done()
|
||||
await hass.async_block_till_done()
|
||||
state = hass.states.get(f"{domain}.test")
|
||||
assert state.state == STATE_UNAVAILABLE
|
||||
|
||||
# Receive LWT again
|
||||
async_fire_mqtt_message(
|
||||
hass,
|
||||
get_topic_tele_will(config),
|
||||
config_get_state_online(config),
|
||||
)
|
||||
state = hass.states.get(f"{domain}.test")
|
||||
assert state.state != STATE_UNAVAILABLE
|
||||
|
||||
|
||||
|
@ -194,6 +209,61 @@ async def help_test_availability_discovery_update(
|
|||
assert state.state != STATE_UNAVAILABLE
|
||||
|
||||
|
||||
async def help_test_availability_poll_state(
|
||||
hass, mqtt_client_mock, mqtt_mock, domain, config, poll_topic, poll_payload
|
||||
):
|
||||
"""Test polling of state when device is available.
|
||||
|
||||
This is a test helper for the TasmotaAvailability mixin.
|
||||
"""
|
||||
async_fire_mqtt_message(
|
||||
hass,
|
||||
f"{DEFAULT_PREFIX}/{config[CONF_MAC]}/config",
|
||||
json.dumps(config),
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
mqtt_mock.async_publish.reset_mock()
|
||||
|
||||
# Device online, verify poll for state
|
||||
async_fire_mqtt_message(
|
||||
hass,
|
||||
get_topic_tele_will(config),
|
||||
config_get_state_online(config),
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
await hass.async_block_till_done()
|
||||
await hass.async_block_till_done()
|
||||
mqtt_mock.async_publish.assert_called_once_with(poll_topic, poll_payload, 0, False)
|
||||
mqtt_mock.async_publish.reset_mock()
|
||||
|
||||
# Disconnected from MQTT server
|
||||
mqtt_mock.connected = False
|
||||
await hass.async_add_executor_job(mqtt_client_mock.on_disconnect, None, None, 0)
|
||||
await hass.async_block_till_done()
|
||||
await hass.async_block_till_done()
|
||||
await hass.async_block_till_done()
|
||||
assert not mqtt_mock.async_publish.called
|
||||
|
||||
# Reconnected to MQTT server
|
||||
mqtt_mock.connected = True
|
||||
await hass.async_add_executor_job(mqtt_client_mock.on_connect, None, None, None, 0)
|
||||
await hass.async_block_till_done()
|
||||
await hass.async_block_till_done()
|
||||
await hass.async_block_till_done()
|
||||
assert not mqtt_mock.async_publish.called
|
||||
|
||||
# Device online, verify poll for state
|
||||
async_fire_mqtt_message(
|
||||
hass,
|
||||
get_topic_tele_will(config),
|
||||
config_get_state_online(config),
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
await hass.async_block_till_done()
|
||||
await hass.async_block_till_done()
|
||||
mqtt_mock.async_publish.assert_called_once_with(poll_topic, poll_payload, 0, False)
|
||||
|
||||
|
||||
async def help_test_discovery_removal(
|
||||
hass, mqtt_mock, caplog, domain, config1, config2
|
||||
):
|
||||
|
|
53
tests/components/tasmota/test_mixins.py
Normal file
53
tests/components/tasmota/test_mixins.py
Normal file
|
@ -0,0 +1,53 @@
|
|||
"""The tests for the Tasmota mixins."""
|
||||
import copy
|
||||
import json
|
||||
|
||||
from hatasmota.const import CONF_MAC
|
||||
from hatasmota.utils import config_get_state_online, get_topic_tele_will
|
||||
|
||||
from homeassistant.components.tasmota.const import DEFAULT_PREFIX
|
||||
|
||||
from .test_common import DEFAULT_CONFIG
|
||||
|
||||
from tests.async_mock import call
|
||||
from tests.common import async_fire_mqtt_message
|
||||
|
||||
|
||||
async def test_availability_poll_state_once(
|
||||
hass, mqtt_client_mock, mqtt_mock, setup_tasmota
|
||||
):
|
||||
"""Test several entities send a single message to update state."""
|
||||
config = copy.deepcopy(DEFAULT_CONFIG)
|
||||
config["rl"][0] = 1
|
||||
config["rl"][1] = 1
|
||||
config["swc"][0] = 1
|
||||
config["swc"][1] = 1
|
||||
poll_payload_relay = ""
|
||||
poll_payload_switch = "8"
|
||||
poll_topic_relay = "tasmota_49A3BC/cmnd/STATE"
|
||||
poll_topic_switch = "tasmota_49A3BC/cmnd/STATUS"
|
||||
|
||||
async_fire_mqtt_message(
|
||||
hass,
|
||||
f"{DEFAULT_PREFIX}/{config[CONF_MAC]}/config",
|
||||
json.dumps(config),
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
mqtt_mock.async_publish.reset_mock()
|
||||
|
||||
# Device online, verify poll for state
|
||||
async_fire_mqtt_message(
|
||||
hass,
|
||||
get_topic_tele_will(config),
|
||||
config_get_state_online(config),
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
await hass.async_block_till_done()
|
||||
await hass.async_block_till_done()
|
||||
mqtt_mock.async_publish.assert_has_calls(
|
||||
[
|
||||
call(poll_topic_relay, poll_payload_relay, 0, False),
|
||||
call(poll_topic_switch, poll_payload_switch, 0, False),
|
||||
],
|
||||
any_order=True,
|
||||
)
|
|
@ -10,6 +10,7 @@ from .test_common import (
|
|||
DEFAULT_CONFIG,
|
||||
help_test_availability,
|
||||
help_test_availability_discovery_update,
|
||||
help_test_availability_poll_state,
|
||||
help_test_availability_when_connection_lost,
|
||||
help_test_discovery_device_remove,
|
||||
help_test_discovery_removal,
|
||||
|
@ -124,6 +125,18 @@ async def test_availability_discovery_update(hass, mqtt_mock, setup_tasmota):
|
|||
)
|
||||
|
||||
|
||||
async def test_availability_poll_state(
|
||||
hass, mqtt_client_mock, mqtt_mock, setup_tasmota
|
||||
):
|
||||
"""Test polling after MQTT connection (re)established."""
|
||||
config = copy.deepcopy(DEFAULT_CONFIG)
|
||||
config["rl"][0] = 1
|
||||
poll_topic = "tasmota_49A3BC/cmnd/STATE"
|
||||
await help_test_availability_poll_state(
|
||||
hass, mqtt_client_mock, mqtt_mock, switch.DOMAIN, config, poll_topic, ""
|
||||
)
|
||||
|
||||
|
||||
async def test_discovery_removal_switch(hass, mqtt_mock, caplog, setup_tasmota):
|
||||
"""Test removal of discovered switch."""
|
||||
config1 = copy.deepcopy(DEFAULT_CONFIG)
|
||||
|
|
Loading…
Add table
Reference in a new issue