From b5af987a180a213a74b9ffb3c05ab5acafe808d6 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Sun, 10 Dec 2023 23:16:06 +0100 Subject: [PATCH] Bump hatasmota to 0.8.0 (#105440) * Bump hatasmota to 0.8.0 * Keep devices with deep sleep support always available * Add tests --- .../components/tasmota/manifest.json | 2 +- homeassistant/components/tasmota/mixins.py | 9 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- .../components/tasmota/test_binary_sensor.py | 29 +++++ tests/components/tasmota/test_common.py | 120 ++++++++++++++++++ tests/components/tasmota/test_cover.py | 36 ++++++ tests/components/tasmota/test_fan.py | 27 ++++ tests/components/tasmota/test_light.py | 27 ++++ tests/components/tasmota/test_sensor.py | 38 ++++++ tests/components/tasmota/test_switch.py | 25 ++++ 11 files changed, 313 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/tasmota/manifest.json b/homeassistant/components/tasmota/manifest.json index 42fc849a2cf..2ce81772774 100644 --- a/homeassistant/components/tasmota/manifest.json +++ b/homeassistant/components/tasmota/manifest.json @@ -8,5 +8,5 @@ "iot_class": "local_push", "loggers": ["hatasmota"], "mqtt": ["tasmota/discovery/#"], - "requirements": ["HATasmota==0.7.3"] + "requirements": ["HATasmota==0.8.0"] } diff --git a/homeassistant/components/tasmota/mixins.py b/homeassistant/components/tasmota/mixins.py index 21030b8c14b..48dbe51fd67 100644 --- a/homeassistant/components/tasmota/mixins.py +++ b/homeassistant/components/tasmota/mixins.py @@ -112,8 +112,11 @@ class TasmotaAvailability(TasmotaEntity): def __init__(self, **kwds: Any) -> None: """Initialize the availability mixin.""" - self._available = False super().__init__(**kwds) + if self._tasmota_entity.deep_sleep_enabled: + self._available = True + else: + self._available = False async def async_added_to_hass(self) -> None: """Subscribe to MQTT events.""" @@ -122,6 +125,8 @@ class TasmotaAvailability(TasmotaEntity): async_subscribe_connection_status(self.hass, self.async_mqtt_connected) ) await super().async_added_to_hass() + if self._tasmota_entity.deep_sleep_enabled: + await self._tasmota_entity.poll_status() async def availability_updated(self, available: bool) -> None: """Handle updated availability.""" @@ -135,6 +140,8 @@ class TasmotaAvailability(TasmotaEntity): if not self.hass.is_stopping: if not mqtt_connected(self.hass): self._available = False + elif self._tasmota_entity.deep_sleep_enabled: + self._available = True self.async_write_ha_state() @property diff --git a/requirements_all.txt b/requirements_all.txt index d05699120cd..0a3ddf78dc7 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -28,7 +28,7 @@ DoorBirdPy==2.1.0 HAP-python==4.9.1 # homeassistant.components.tasmota -HATasmota==0.7.3 +HATasmota==0.8.0 # homeassistant.components.mastodon Mastodon.py==1.5.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index afe214608ec..5e5b7bbe613 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -25,7 +25,7 @@ DoorBirdPy==2.1.0 HAP-python==4.9.1 # homeassistant.components.tasmota -HATasmota==0.7.3 +HATasmota==0.8.0 # homeassistant.components.doods # homeassistant.components.generic diff --git a/tests/components/tasmota/test_binary_sensor.py b/tests/components/tasmota/test_binary_sensor.py index 2bfb4a9d5e2..d5f1e4d7101 100644 --- a/tests/components/tasmota/test_binary_sensor.py +++ b/tests/components/tasmota/test_binary_sensor.py @@ -31,6 +31,8 @@ from .test_common import ( help_test_availability_discovery_update, help_test_availability_poll_state, help_test_availability_when_connection_lost, + help_test_deep_sleep_availability, + help_test_deep_sleep_availability_when_connection_lost, help_test_discovery_device_remove, help_test_discovery_removal, help_test_discovery_update_unchanged, @@ -313,6 +315,21 @@ async def test_availability_when_connection_lost( ) +async def test_deep_sleep_availability_when_connection_lost( + hass: HomeAssistant, + mqtt_client_mock: MqttMockPahoClient, + mqtt_mock: MqttMockHAClient, + setup_tasmota, +) -> None: + """Test availability after MQTT disconnection.""" + config = copy.deepcopy(DEFAULT_CONFIG) + config["swc"][0] = 1 + config["swn"][0] = "Test" + await help_test_deep_sleep_availability_when_connection_lost( + hass, mqtt_client_mock, mqtt_mock, Platform.BINARY_SENSOR, config + ) + + async def test_availability( hass: HomeAssistant, mqtt_mock: MqttMockHAClient, setup_tasmota ) -> None: @@ -323,6 +340,18 @@ async def test_availability( await help_test_availability(hass, mqtt_mock, Platform.BINARY_SENSOR, config) +async def test_deep_sleep_availability( + hass: HomeAssistant, mqtt_mock: MqttMockHAClient, setup_tasmota +) -> None: + """Test availability when deep sleep is enabled.""" + config = copy.deepcopy(DEFAULT_CONFIG) + config["swc"][0] = 1 + config["swn"][0] = "Test" + await help_test_deep_sleep_availability( + hass, mqtt_mock, Platform.BINARY_SENSOR, config + ) + + async def test_availability_discovery_update( hass: HomeAssistant, mqtt_mock: MqttMockHAClient, setup_tasmota ) -> None: diff --git a/tests/components/tasmota/test_common.py b/tests/components/tasmota/test_common.py index a184f650fae..1f414cb4e5a 100644 --- a/tests/components/tasmota/test_common.py +++ b/tests/components/tasmota/test_common.py @@ -4,6 +4,7 @@ import json from unittest.mock import ANY from hatasmota.const import ( + CONF_DEEP_SLEEP, CONF_MAC, CONF_OFFLINE, CONF_ONLINE, @@ -188,6 +189,76 @@ async def help_test_availability_when_connection_lost( assert state.state != STATE_UNAVAILABLE +async def help_test_deep_sleep_availability_when_connection_lost( + hass, + mqtt_client_mock, + mqtt_mock, + domain, + config, + sensor_config=None, + object_id="tasmota_test", +): + """Test availability after MQTT disconnection when deep sleep is enabled. + + This is a test helper for the TasmotaAvailability mixin. + """ + config[CONF_DEEP_SLEEP] = 1 + async_fire_mqtt_message( + hass, + f"{DEFAULT_PREFIX}/{config[CONF_MAC]}/config", + json.dumps(config), + ) + await hass.async_block_till_done() + if sensor_config: + async_fire_mqtt_message( + hass, + f"{DEFAULT_PREFIX}/{config[CONF_MAC]}/sensors", + json.dumps(sensor_config), + ) + await hass.async_block_till_done() + + # Device online + state = hass.states.get(f"{domain}.{object_id}") + 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() + await hass.async_block_till_done() + await hass.async_block_till_done() + state = hass.states.get(f"{domain}.{object_id}") + assert state.state == STATE_UNAVAILABLE + + # Reconnected to MQTT server -> state no longer 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}.{object_id}") + assert state.state != STATE_UNAVAILABLE + + # Receive LWT again + async_fire_mqtt_message( + hass, + get_topic_tele_will(config), + config_get_state_online(config), + ) + await hass.async_block_till_done() + state = hass.states.get(f"{domain}.{object_id}") + assert state.state != STATE_UNAVAILABLE + + async_fire_mqtt_message( + hass, + get_topic_tele_will(config), + config_get_state_offline(config), + ) + await hass.async_block_till_done() + state = hass.states.get(f"{domain}.{object_id}") + assert state.state != STATE_UNAVAILABLE + + async def help_test_availability( hass, mqtt_mock, @@ -236,6 +307,55 @@ async def help_test_availability( assert state.state == STATE_UNAVAILABLE +async def help_test_deep_sleep_availability( + hass, + mqtt_mock, + domain, + config, + sensor_config=None, + object_id="tasmota_test", +): + """Test availability when deep sleep is enabled. + + This is a test helper for the TasmotaAvailability mixin. + """ + config[CONF_DEEP_SLEEP] = 1 + async_fire_mqtt_message( + hass, + f"{DEFAULT_PREFIX}/{config[CONF_MAC]}/config", + json.dumps(config), + ) + await hass.async_block_till_done() + if sensor_config: + async_fire_mqtt_message( + hass, + f"{DEFAULT_PREFIX}/{config[CONF_MAC]}/sensors", + json.dumps(sensor_config), + ) + await hass.async_block_till_done() + + state = hass.states.get(f"{domain}.{object_id}") + assert state.state != STATE_UNAVAILABLE + + async_fire_mqtt_message( + hass, + get_topic_tele_will(config), + config_get_state_online(config), + ) + await hass.async_block_till_done() + state = hass.states.get(f"{domain}.{object_id}") + assert state.state != STATE_UNAVAILABLE + + async_fire_mqtt_message( + hass, + get_topic_tele_will(config), + config_get_state_offline(config), + ) + await hass.async_block_till_done() + state = hass.states.get(f"{domain}.{object_id}") + assert state.state != STATE_UNAVAILABLE + + async def help_test_availability_discovery_update( hass, mqtt_mock, diff --git a/tests/components/tasmota/test_cover.py b/tests/components/tasmota/test_cover.py index e2bdc8b2ca7..cae65521e21 100644 --- a/tests/components/tasmota/test_cover.py +++ b/tests/components/tasmota/test_cover.py @@ -22,6 +22,8 @@ from .test_common import ( help_test_availability_discovery_update, help_test_availability_poll_state, help_test_availability_when_connection_lost, + help_test_deep_sleep_availability, + help_test_deep_sleep_availability_when_connection_lost, help_test_discovery_device_remove, help_test_discovery_removal, help_test_discovery_update_unchanged, @@ -663,6 +665,27 @@ async def test_availability_when_connection_lost( ) +async def test_deep_sleep_availability_when_connection_lost( + hass: HomeAssistant, + mqtt_client_mock: MqttMockPahoClient, + mqtt_mock: MqttMockHAClient, + setup_tasmota, +) -> None: + """Test availability after MQTT disconnection.""" + config = copy.deepcopy(DEFAULT_CONFIG) + config["dn"] = "Test" + config["rl"][0] = 3 + config["rl"][1] = 3 + await help_test_deep_sleep_availability_when_connection_lost( + hass, + mqtt_client_mock, + mqtt_mock, + Platform.COVER, + config, + object_id="test_cover_1", + ) + + async def test_availability( hass: HomeAssistant, mqtt_mock: MqttMockHAClient, setup_tasmota ) -> None: @@ -676,6 +699,19 @@ async def test_availability( ) +async def test_deep_sleep_availability( + hass: HomeAssistant, mqtt_mock: MqttMockHAClient, setup_tasmota +) -> None: + """Test availability.""" + config = copy.deepcopy(DEFAULT_CONFIG) + config["dn"] = "Test" + config["rl"][0] = 3 + config["rl"][1] = 3 + await help_test_deep_sleep_availability( + hass, mqtt_mock, Platform.COVER, config, object_id="test_cover_1" + ) + + async def test_availability_discovery_update( hass: HomeAssistant, mqtt_mock: MqttMockHAClient, setup_tasmota ) -> None: diff --git a/tests/components/tasmota/test_fan.py b/tests/components/tasmota/test_fan.py index 2a50e2d43b5..05e3151be2e 100644 --- a/tests/components/tasmota/test_fan.py +++ b/tests/components/tasmota/test_fan.py @@ -22,6 +22,8 @@ from .test_common import ( help_test_availability_discovery_update, help_test_availability_poll_state, help_test_availability_when_connection_lost, + help_test_deep_sleep_availability, + help_test_deep_sleep_availability_when_connection_lost, help_test_discovery_device_remove, help_test_discovery_removal, help_test_discovery_update_unchanged, @@ -232,6 +234,20 @@ async def test_availability_when_connection_lost( ) +async def test_deep_sleep_availability_when_connection_lost( + hass: HomeAssistant, + mqtt_client_mock: MqttMockPahoClient, + mqtt_mock: MqttMockHAClient, + setup_tasmota, +) -> None: + """Test availability after MQTT disconnection.""" + config = copy.deepcopy(DEFAULT_CONFIG) + config["if"] = 1 + await help_test_deep_sleep_availability_when_connection_lost( + hass, mqtt_client_mock, mqtt_mock, Platform.FAN, config, object_id="tasmota" + ) + + async def test_availability( hass: HomeAssistant, mqtt_mock: MqttMockHAClient, setup_tasmota ) -> None: @@ -243,6 +259,17 @@ async def test_availability( ) +async def test_deep_sleep_availability( + hass: HomeAssistant, mqtt_mock: MqttMockHAClient, setup_tasmota +) -> None: + """Test availability.""" + config = copy.deepcopy(DEFAULT_CONFIG) + config["if"] = 1 + await help_test_deep_sleep_availability( + hass, mqtt_mock, Platform.FAN, config, object_id="tasmota" + ) + + async def test_availability_discovery_update( hass: HomeAssistant, mqtt_mock: MqttMockHAClient, setup_tasmota ) -> None: diff --git a/tests/components/tasmota/test_light.py b/tests/components/tasmota/test_light.py index 27b7bd1a82a..50f11fb7757 100644 --- a/tests/components/tasmota/test_light.py +++ b/tests/components/tasmota/test_light.py @@ -22,6 +22,8 @@ from .test_common import ( help_test_availability_discovery_update, help_test_availability_poll_state, help_test_availability_when_connection_lost, + help_test_deep_sleep_availability, + help_test_deep_sleep_availability_when_connection_lost, help_test_discovery_device_remove, help_test_discovery_removal, help_test_discovery_update_unchanged, @@ -1669,6 +1671,21 @@ async def test_availability_when_connection_lost( ) +async def test_deep_sleep_availability_when_connection_lost( + hass: HomeAssistant, + mqtt_client_mock: MqttMockPahoClient, + mqtt_mock: MqttMockHAClient, + setup_tasmota, +) -> None: + """Test availability after MQTT disconnection.""" + config = copy.deepcopy(DEFAULT_CONFIG) + config["rl"][0] = 2 + config["lt_st"] = 1 # 1 channel light (Dimmer) + await help_test_deep_sleep_availability_when_connection_lost( + hass, mqtt_client_mock, mqtt_mock, Platform.LIGHT, config + ) + + async def test_availability( hass: HomeAssistant, mqtt_mock: MqttMockHAClient, setup_tasmota ) -> None: @@ -1679,6 +1696,16 @@ async def test_availability( await help_test_availability(hass, mqtt_mock, Platform.LIGHT, config) +async def test_deep_sleep_availability( + hass: HomeAssistant, mqtt_mock: MqttMockHAClient, setup_tasmota +) -> None: + """Test availability.""" + config = copy.deepcopy(DEFAULT_CONFIG) + config["rl"][0] = 2 + config["lt_st"] = 1 # 1 channel light (Dimmer) + await help_test_deep_sleep_availability(hass, mqtt_mock, Platform.LIGHT, config) + + async def test_availability_discovery_update( hass: HomeAssistant, mqtt_mock: MqttMockHAClient, setup_tasmota ) -> None: diff --git a/tests/components/tasmota/test_sensor.py b/tests/components/tasmota/test_sensor.py index 2f50a84ffdd..dc4820779a6 100644 --- a/tests/components/tasmota/test_sensor.py +++ b/tests/components/tasmota/test_sensor.py @@ -28,6 +28,8 @@ from .test_common import ( help_test_availability_discovery_update, help_test_availability_poll_state, help_test_availability_when_connection_lost, + help_test_deep_sleep_availability, + help_test_deep_sleep_availability_when_connection_lost, help_test_discovery_device_remove, help_test_discovery_removal, help_test_discovery_update_unchanged, @@ -1222,6 +1224,26 @@ async def test_availability_when_connection_lost( ) +async def test_deep_sleep_availability_when_connection_lost( + hass: HomeAssistant, + mqtt_client_mock: MqttMockPahoClient, + mqtt_mock: MqttMockHAClient, + setup_tasmota, +) -> None: + """Test availability after MQTT disconnection.""" + config = copy.deepcopy(DEFAULT_CONFIG) + sensor_config = copy.deepcopy(DEFAULT_SENSOR_CONFIG) + await help_test_deep_sleep_availability_when_connection_lost( + hass, + mqtt_client_mock, + mqtt_mock, + Platform.SENSOR, + config, + sensor_config, + "tasmota_dht11_temperature", + ) + + async def test_availability( hass: HomeAssistant, mqtt_mock: MqttMockHAClient, setup_tasmota ) -> None: @@ -1238,6 +1260,22 @@ async def test_availability( ) +async def test_deep_sleep_availability( + hass: HomeAssistant, mqtt_mock: MqttMockHAClient, setup_tasmota +) -> None: + """Test availability.""" + config = copy.deepcopy(DEFAULT_CONFIG) + sensor_config = copy.deepcopy(DEFAULT_SENSOR_CONFIG) + await help_test_deep_sleep_availability( + hass, + mqtt_mock, + Platform.SENSOR, + config, + sensor_config, + "tasmota_dht11_temperature", + ) + + async def test_availability_discovery_update( hass: HomeAssistant, mqtt_mock: MqttMockHAClient, setup_tasmota ) -> None: diff --git a/tests/components/tasmota/test_switch.py b/tests/components/tasmota/test_switch.py index 54d94b46fe8..1a16f372fc9 100644 --- a/tests/components/tasmota/test_switch.py +++ b/tests/components/tasmota/test_switch.py @@ -20,6 +20,8 @@ from .test_common import ( help_test_availability_discovery_update, help_test_availability_poll_state, help_test_availability_when_connection_lost, + help_test_deep_sleep_availability, + help_test_deep_sleep_availability_when_connection_lost, help_test_discovery_device_remove, help_test_discovery_removal, help_test_discovery_update_unchanged, @@ -158,6 +160,20 @@ async def test_availability_when_connection_lost( ) +async def test_deep_sleep_availability_when_connection_lost( + hass: HomeAssistant, + mqtt_client_mock: MqttMockPahoClient, + mqtt_mock: MqttMockHAClient, + setup_tasmota, +) -> None: + """Test availability after MQTT disconnection.""" + config = copy.deepcopy(DEFAULT_CONFIG) + config["rl"][0] = 1 + await help_test_deep_sleep_availability_when_connection_lost( + hass, mqtt_client_mock, mqtt_mock, Platform.SWITCH, config + ) + + async def test_availability( hass: HomeAssistant, mqtt_mock: MqttMockHAClient, setup_tasmota ) -> None: @@ -167,6 +183,15 @@ async def test_availability( await help_test_availability(hass, mqtt_mock, Platform.SWITCH, config) +async def test_deep_sleep_availability( + hass: HomeAssistant, mqtt_mock: MqttMockHAClient, setup_tasmota +) -> None: + """Test availability.""" + config = copy.deepcopy(DEFAULT_CONFIG) + config["rl"][0] = 1 + await help_test_deep_sleep_availability(hass, mqtt_mock, Platform.SWITCH, config) + + async def test_availability_discovery_update( hass: HomeAssistant, mqtt_mock: MqttMockHAClient, setup_tasmota ) -> None: