From a1fec1e7635ccd833f73a03d9396ca1a86cadf02 Mon Sep 17 00:00:00 2001 From: Erik Date: Mon, 9 Nov 2020 17:00:25 +0100 Subject: [PATCH 1/8] Translate between Tasmota and HA unit constants --- homeassistant/components/tasmota/sensor.py | 68 +++++++++++++++++++++- tests/components/tasmota/test_sensor.py | 4 +- 2 files changed, 69 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/tasmota/sensor.py b/homeassistant/components/tasmota/sensor.py index 480e5d8d2146e..5aefa3c072fc2 100644 --- a/homeassistant/components/tasmota/sensor.py +++ b/homeassistant/components/tasmota/sensor.py @@ -3,6 +3,19 @@ from hatasmota import status_sensor from hatasmota.const import ( + CONCENTRATION_MICROGRAMS_PER_CUBIC_METER as TASMOTA_CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, + CONCENTRATION_PARTS_PER_BILLION as TASMOTA_CONCENTRATION_PARTS_PER_BILLION, + CONCENTRATION_PARTS_PER_MILLION as TASMOTA_CONCENTRATION_PARTS_PER_MILLION, + ELECTRICAL_CURRENT_AMPERE as TASMOTA_ELECTRICAL_CURRENT_AMPERE, + ELECTRICAL_VOLT_AMPERE as TASMOTA_ELECTRICAL_VOLT_AMPERE, + ENERGY_KILO_WATT_HOUR as TASMOTA_ENERGY_KILO_WATT_HOUR, + FREQUENCY_HERTZ as TASMOTA_FREQUENCY_HERTZ, + LENGTH_CENTIMETERS as TASMOTA_LENGTH_CENTIMETERS, + LIGHT_LUX as TASMOTA_LIGHT_LUX, + MASS_KILOGRAMS as TASMOTA_MASS_KILOGRAMS, + PERCENTAGE as TASMOTA_PERCENTAGE, + POWER_WATT as TASMOTA_POWER_WATT, + PRESSURE_HPA as TASMOTA_PRESSURE_HPA, SENSOR_AMBIENT, SENSOR_APPARENT_POWERUSAGE, SENSOR_BATTERY, @@ -49,10 +62,21 @@ SENSOR_VOLTAGE, SENSOR_WEIGHT, SENSOR_YESTERDAY, + SIGNAL_STRENGTH_DECIBELS as TASMOTA_SIGNAL_STRENGTH_DECIBELS, + SPEED_KILOMETERS_PER_HOUR as TASMOTA_SPEED_KILOMETERS_PER_HOUR, + SPEED_METERS_PER_SECOND as TASMOTA_SPEED_METERS_PER_SECOND, + SPEED_MILES_PER_HOUR as TASMOTA_SPEED_MILES_PER_HOUR, + TEMP_CELSIUS as TASMOTA_TEMP_CELSIUS, + TEMP_FAHRENHEIT as TASMOTA_TEMP_FAHRENHEIT, + TEMP_KELVIN as TASMOTA_TEMP_KELVIN, + VOLT as TASMOTA_VOLT, ) from homeassistant.components import sensor from homeassistant.const import ( + CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, + CONCENTRATION_PARTS_PER_BILLION, + CONCENTRATION_PARTS_PER_MILLION, DEVICE_CLASS_BATTERY, DEVICE_CLASS_HUMIDITY, DEVICE_CLASS_ILLUMINANCE, @@ -60,6 +84,24 @@ DEVICE_CLASS_PRESSURE, DEVICE_CLASS_SIGNAL_STRENGTH, DEVICE_CLASS_TEMPERATURE, + ELECTRICAL_CURRENT_AMPERE, + ELECTRICAL_VOLT_AMPERE, + ENERGY_KILO_WATT_HOUR, + FREQUENCY_HERTZ, + LENGTH_CENTIMETERS, + LIGHT_LUX, + MASS_KILOGRAMS, + PERCENTAGE, + POWER_WATT, + PRESSURE_HPA, + SIGNAL_STRENGTH_DECIBELS, + SPEED_KILOMETERS_PER_HOUR, + SPEED_METERS_PER_SECOND, + SPEED_MILES_PER_HOUR, + TEMP_CELSIUS, + TEMP_FAHRENHEIT, + TEMP_KELVIN, + VOLT, ) from homeassistant.core import callback from homeassistant.helpers.dispatcher import async_dispatcher_connect @@ -122,6 +164,30 @@ SENSOR_YESTERDAY: {DEVICE_CLASS: DEVICE_CLASS_POWER}, } +SENSOR_UNIT_MAP = { + TASMOTA_CONCENTRATION_MICROGRAMS_PER_CUBIC_METER: CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, + TASMOTA_CONCENTRATION_PARTS_PER_BILLION: CONCENTRATION_PARTS_PER_BILLION, + TASMOTA_CONCENTRATION_PARTS_PER_MILLION: CONCENTRATION_PARTS_PER_MILLION, + TASMOTA_ELECTRICAL_CURRENT_AMPERE: ELECTRICAL_CURRENT_AMPERE, + TASMOTA_ELECTRICAL_VOLT_AMPERE: ELECTRICAL_VOLT_AMPERE, + TASMOTA_ENERGY_KILO_WATT_HOUR: ENERGY_KILO_WATT_HOUR, + TASMOTA_FREQUENCY_HERTZ: FREQUENCY_HERTZ, + TASMOTA_LENGTH_CENTIMETERS: LENGTH_CENTIMETERS, + TASMOTA_LIGHT_LUX: LIGHT_LUX, + TASMOTA_MASS_KILOGRAMS: MASS_KILOGRAMS, + TASMOTA_PERCENTAGE: PERCENTAGE, + TASMOTA_POWER_WATT: POWER_WATT, + TASMOTA_PRESSURE_HPA: PRESSURE_HPA, + TASMOTA_SIGNAL_STRENGTH_DECIBELS: SIGNAL_STRENGTH_DECIBELS, + TASMOTA_SPEED_KILOMETERS_PER_HOUR: SPEED_KILOMETERS_PER_HOUR, + TASMOTA_SPEED_METERS_PER_SECOND: SPEED_METERS_PER_SECOND, + TASMOTA_SPEED_MILES_PER_HOUR: SPEED_MILES_PER_HOUR, + TASMOTA_TEMP_CELSIUS: TEMP_CELSIUS, + TASMOTA_TEMP_FAHRENHEIT: TEMP_FAHRENHEIT, + TASMOTA_TEMP_KELVIN: TEMP_KELVIN, + TASMOTA_VOLT: VOLT, +} + async def async_setup_entry(hass, config_entry, async_add_entities): """Set up Tasmota sensor dynamically through discovery.""" @@ -195,4 +261,4 @@ def state(self): @property def unit_of_measurement(self): """Return the unit this state is expressed in.""" - return self._tasmota_entity.unit + return SENSOR_UNIT_MAP.get(self._tasmota_entity.unit, self._tasmota_entity.unit) diff --git a/tests/components/tasmota/test_sensor.py b/tests/components/tasmota/test_sensor.py index b8e5583579b44..6d09996751ac2 100644 --- a/tests/components/tasmota/test_sensor.py +++ b/tests/components/tasmota/test_sensor.py @@ -301,7 +301,7 @@ async def test_attributes(hass, mqtt_mock, setup_tasmota): assert state.attributes.get("device_class") == "temperature" assert state.attributes.get("friendly_name") == "Tasmota DHT11 Temperature" assert state.attributes.get("icon") is None - assert state.attributes.get("unit_of_measurement") == "C" + assert state.attributes.get("unit_of_measurement") == "°C" state = hass.states.get("sensor.tasmota_beer_CarbonDioxide") assert state.attributes.get("device_class") is None @@ -371,7 +371,7 @@ async def test_indexed_sensor_attributes(hass, mqtt_mock, setup_tasmota): assert state.attributes.get("device_class") == "temperature" assert state.attributes.get("friendly_name") == "Tasmota Dummy1 Temperature 0" assert state.attributes.get("icon") is None - assert state.attributes.get("unit_of_measurement") == "C" + assert state.attributes.get("unit_of_measurement") == "°C" state = hass.states.get("sensor.tasmota_dummy2_carbondioxide_1") assert state.attributes.get("device_class") is None From 9745dde81a9242f33a6f7de69a8a958fbd2f0c58 Mon Sep 17 00:00:00 2001 From: Erik Date: Tue, 10 Nov 2020 20:34:03 +0100 Subject: [PATCH 2/8] Adapt to new status sensors --- homeassistant/components/tasmota/discovery.py | 4 +- homeassistant/components/tasmota/mixins.py | 3 +- homeassistant/components/tasmota/sensor.py | 12 ++- tests/components/tasmota/test_sensor.py | 83 +++++++++++++++++++ 4 files changed, 95 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/tasmota/discovery.py b/homeassistant/components/tasmota/discovery.py index 2313a8327c5d4..4e17d287d9a4e 100644 --- a/homeassistant/components/tasmota/discovery.py +++ b/homeassistant/components/tasmota/discovery.py @@ -68,7 +68,9 @@ async def _discover_entity(tasmota_entity_config, discovery_hash, platform): tasmota_entity_config, ) else: - tasmota_entity = tasmota_get_entity(tasmota_entity_config, tasmota_mqtt) + tasmota_entity = tasmota_get_entity( + tasmota_entity_config, tasmota_mqtt, hass.async_create_task + ) _LOGGER.debug( "Adding new entity: %s %s %s", platform, diff --git a/homeassistant/components/tasmota/mixins.py b/homeassistant/components/tasmota/mixins.py index 5a1c7a9f3de76..a860b06c57460 100644 --- a/homeassistant/components/tasmota/mixins.py +++ b/homeassistant/components/tasmota/mixins.py @@ -95,8 +95,7 @@ async def async_added_to_hass(self) -> None: @callback def availability_updated(self, available: bool) -> None: """Handle updated availability.""" - if available and not self._available: - self._tasmota_entity.poll_status() + self._tasmota_entity.poll_status() self._available = available self.async_write_ha_state() diff --git a/homeassistant/components/tasmota/sensor.py b/homeassistant/components/tasmota/sensor.py index 5aefa3c072fc2..efede34245a02 100644 --- a/homeassistant/components/tasmota/sensor.py +++ b/homeassistant/components/tasmota/sensor.py @@ -48,12 +48,12 @@ SENSOR_PROXIMITY, SENSOR_REACTIVE_POWERUSAGE, SENSOR_STATUS_IP, + SENSOR_STATUS_LAST_RESTART_TIME, SENSOR_STATUS_LINK_COUNT, SENSOR_STATUS_MQTT_COUNT, - SENSOR_STATUS_RESTART, + SENSOR_STATUS_RESTART_REASON, SENSOR_STATUS_RSSI, SENSOR_STATUS_SIGNAL, - SENSOR_STATUS_UPTIME, SENSOR_TEMPERATURE, SENSOR_TODAY, SENSOR_TOTAL, @@ -84,6 +84,7 @@ DEVICE_CLASS_PRESSURE, DEVICE_CLASS_SIGNAL_STRENGTH, DEVICE_CLASS_TEMPERATURE, + DEVICE_CLASS_TIMESTAMP, ELECTRICAL_CURRENT_AMPERE, ELECTRICAL_VOLT_AMPERE, ENERGY_KILO_WATT_HOUR, @@ -106,6 +107,7 @@ from homeassistant.core import callback from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity import Entity +import homeassistant.util.dt as dt_util from .const import DATA_REMOVE_DISCOVER_COMPONENT, DOMAIN as TASMOTA_DOMAIN from .discovery import TASMOTA_DISCOVERY_ENTITY_NEW @@ -150,10 +152,10 @@ SENSOR_PRESSUREATSEALEVEL: {DEVICE_CLASS: DEVICE_CLASS_PRESSURE}, SENSOR_PROXIMITY: {ICON: "mdi:ruler"}, SENSOR_REACTIVE_POWERUSAGE: {DEVICE_CLASS: DEVICE_CLASS_POWER}, - SENSOR_STATUS_RESTART: {ICON: "mdi:information-outline"}, + SENSOR_STATUS_LAST_RESTART_TIME: {DEVICE_CLASS: DEVICE_CLASS_TIMESTAMP}, + SENSOR_STATUS_RESTART_REASON: {ICON: "mdi:information-outline"}, SENSOR_STATUS_SIGNAL: {DEVICE_CLASS: DEVICE_CLASS_SIGNAL_STRENGTH}, SENSOR_STATUS_RSSI: {ICON: "mdi:access-point"}, - SENSOR_STATUS_UPTIME: {ICON: "mdi:progress-clock"}, SENSOR_TEMPERATURE: {DEVICE_CLASS: DEVICE_CLASS_TEMPERATURE}, SENSOR_TODAY: {DEVICE_CLASS: DEVICE_CLASS_POWER}, SENSOR_TOTAL: {DEVICE_CLASS: DEVICE_CLASS_POWER}, @@ -256,6 +258,8 @@ def icon(self): @property def state(self): """Return the state of the entity.""" + if self._state and self.device_class == DEVICE_CLASS_TIMESTAMP: + return dt_util.as_local(self._state) return self._state @property diff --git a/tests/components/tasmota/test_sensor.py b/tests/components/tasmota/test_sensor.py index 6d09996751ac2..06e24515ce0dc 100644 --- a/tests/components/tasmota/test_sensor.py +++ b/tests/components/tasmota/test_sensor.py @@ -259,6 +259,7 @@ async def test_status_sensor_state_via_mqtt(hass, mqtt_mock, setup_tasmota): async_fire_mqtt_message( hass, "tasmota_49A3BC/tele/STATE", '{"Wifi":{"Signal":20.5}}' ) + await hass.async_block_till_done() state = hass.states.get("sensor.tasmota_status") assert state.state == "20.5" @@ -268,10 +269,92 @@ async def test_status_sensor_state_via_mqtt(hass, mqtt_mock, setup_tasmota): "tasmota_49A3BC/stat/STATUS11", '{"StatusSTS":{"Wifi":{"Signal":20.0}}}', ) + await hass.async_block_till_done() state = hass.states.get("sensor.tasmota_status") assert state.state == "20.0" +@pytest.mark.parametrize("status_sensor_disabled", [False]) +async def test_single_shot_status_sensor_state_via_mqtt(hass, mqtt_mock, setup_tasmota): + """Test state update via MQTT.""" + entity_reg = await hass.helpers.entity_registry.async_get_registry() + + # Pre-enable the status sensor + entity_reg.async_get_or_create( + sensor.DOMAIN, + "tasmota", + "00000049A3BC_status_sensor_status_sensor_status_restart_reason", + suggested_object_id="tasmota_status", + disabled_by=None, + ) + + 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.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) + + # Test polled state update + async_fire_mqtt_message( + hass, + "tasmota_49A3BC/stat/STATUS1", + '{"StatusPRM":{"RestartReason":"Some reason"}}', + ) + await hass.async_block_till_done() + state = hass.states.get("sensor.tasmota_status") + assert state.state == "Some reason" + + # Test polled state update is ignored + async_fire_mqtt_message( + hass, + "tasmota_49A3BC/stat/STATUS1", + '{"StatusPRM":{"RestartReason":"Another reason"}}', + ) + await hass.async_block_till_done() + state = hass.states.get("sensor.tasmota_status") + assert state.state == "Some reason" + + # Device signals online again + async_fire_mqtt_message(hass, "tasmota_49A3BC/tele/LWT", "Online") + await hass.async_block_till_done() + state = hass.states.get("sensor.tasmota_status") + assert state.state == "Some reason" + + # Test polled state update + async_fire_mqtt_message( + hass, + "tasmota_49A3BC/stat/STATUS1", + '{"StatusPRM":{"RestartReason":"Another reason"}}', + ) + await hass.async_block_till_done() + state = hass.states.get("sensor.tasmota_status") + assert state.state == "Another reason" + + # Test polled state update is ignored + async_fire_mqtt_message( + hass, + "tasmota_49A3BC/stat/STATUS1", + '{"StatusPRM":{"RestartReason":"Third reason"}}', + ) + await hass.async_block_till_done() + state = hass.states.get("sensor.tasmota_status") + assert state.state == "Another reason" + + async def test_attributes(hass, mqtt_mock, setup_tasmota): """Test correct attributes for sensors.""" config = copy.deepcopy(DEFAULT_CONFIG) From 3d763f15c13575744ee09ca45524a50cace035a5 Mon Sep 17 00:00:00 2001 From: Erik Date: Tue, 10 Nov 2020 20:53:57 +0100 Subject: [PATCH 3/8] Add test --- .../components/tasmota/test_binary_sensor.py | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/tests/components/tasmota/test_binary_sensor.py b/tests/components/tasmota/test_binary_sensor.py index c3982bf69fdc7..22bf533e18ec6 100644 --- a/tests/components/tasmota/test_binary_sensor.py +++ b/tests/components/tasmota/test_binary_sensor.py @@ -96,6 +96,54 @@ async def test_controlling_state_via_mqtt(hass, mqtt_mock, setup_tasmota): assert state.state == STATE_OFF +async def test_pushon_controlling_state_via_mqtt(hass, mqtt_mock, setup_tasmota): + """Test state update via MQTT.""" + config = copy.deepcopy(DEFAULT_CONFIG) + config["swc"][0] = 13 + mac = config["mac"] + + async_fire_mqtt_message( + hass, + f"{DEFAULT_PREFIX}/{mac}/config", + json.dumps(config), + ) + await hass.async_block_till_done() + + state = hass.states.get("binary_sensor.test") + 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("binary_sensor.test") + assert state.state == STATE_OFF + assert not state.attributes.get(ATTR_ASSUMED_STATE) + + # Test normal state update + async_fire_mqtt_message( + hass, "tasmota_49A3BC/stat/RESULT", '{"Switch1":{"Action":"ON"}}' + ) + state = hass.states.get("binary_sensor.test") + assert state.state == STATE_ON + + async_fire_mqtt_message( + hass, "tasmota_49A3BC/stat/RESULT", '{"Switch1":{"Action":"OFF"}}' + ) + state = hass.states.get("binary_sensor.test") + assert state.state == STATE_OFF + + # Test periodic state update is ignored + async_fire_mqtt_message(hass, "tasmota_49A3BC/tele/SENSOR", '{"Switch1":"ON"}') + state = hass.states.get("binary_sensor.test") + assert state.state == STATE_OFF + + # Test polled state update is ignored + async_fire_mqtt_message( + hass, "tasmota_49A3BC/stat/STATUS10", '{"StatusSNS":{"Switch1":"ON"}}' + ) + state = hass.states.get("binary_sensor.test") + assert state.state == STATE_OFF + + async def test_friendly_names(hass, mqtt_mock, setup_tasmota): """Test state update via MQTT.""" config = copy.deepcopy(DEFAULT_CONFIG) From 65e653a867aced3b568b232ec1ddeb4ab1b99f71 Mon Sep 17 00:00:00 2001 From: Erik Date: Tue, 10 Nov 2020 22:06:21 +0100 Subject: [PATCH 4/8] Bump hatasmota to 0.0.28 --- homeassistant/components/tasmota/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/tasmota/manifest.json b/homeassistant/components/tasmota/manifest.json index 2b8bbc8d10339..6dcb88d7de43c 100644 --- a/homeassistant/components/tasmota/manifest.json +++ b/homeassistant/components/tasmota/manifest.json @@ -3,7 +3,7 @@ "name": "Tasmota (beta)", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/tasmota", - "requirements": ["hatasmota==0.0.27"], + "requirements": ["hatasmota==0.0.28"], "dependencies": ["mqtt"], "mqtt": ["tasmota/discovery/#"], "codeowners": ["@emontnemery"] diff --git a/requirements_all.txt b/requirements_all.txt index 5ec9c9b6d7201..25147aaee5512 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -738,7 +738,7 @@ hass-nabucasa==0.37.1 hass_splunk==0.1.1 # homeassistant.components.tasmota -hatasmota==0.0.27 +hatasmota==0.0.28 # homeassistant.components.jewish_calendar hdate==0.9.12 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 1553395980bca..bb25a6f17d309 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -376,7 +376,7 @@ hangups==0.4.11 hass-nabucasa==0.37.1 # homeassistant.components.tasmota -hatasmota==0.0.27 +hatasmota==0.0.28 # homeassistant.components.jewish_calendar hdate==0.9.12 From 67f851258997d22fe5fc22198cb31e3ed54d8d20 Mon Sep 17 00:00:00 2001 From: Erik Date: Wed, 11 Nov 2020 12:47:12 +0100 Subject: [PATCH 5/8] Don't pass hass.async_create_task to hatasmota --- homeassistant/components/tasmota/discovery.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/homeassistant/components/tasmota/discovery.py b/homeassistant/components/tasmota/discovery.py index 4e17d287d9a4e..2313a8327c5d4 100644 --- a/homeassistant/components/tasmota/discovery.py +++ b/homeassistant/components/tasmota/discovery.py @@ -68,9 +68,7 @@ async def _discover_entity(tasmota_entity_config, discovery_hash, platform): tasmota_entity_config, ) else: - tasmota_entity = tasmota_get_entity( - tasmota_entity_config, tasmota_mqtt, hass.async_create_task - ) + tasmota_entity = tasmota_get_entity(tasmota_entity_config, tasmota_mqtt) _LOGGER.debug( "Adding new entity: %s %s %s", platform, From 64c16057feb689732e668fe33ee92d8f1cd143a2 Mon Sep 17 00:00:00 2001 From: Erik Date: Wed, 11 Nov 2020 12:49:11 +0100 Subject: [PATCH 6/8] Add support for SSID status sensor --- homeassistant/components/tasmota/sensor.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/homeassistant/components/tasmota/sensor.py b/homeassistant/components/tasmota/sensor.py index efede34245a02..7eb2e0f168288 100644 --- a/homeassistant/components/tasmota/sensor.py +++ b/homeassistant/components/tasmota/sensor.py @@ -54,6 +54,7 @@ SENSOR_STATUS_RESTART_REASON, SENSOR_STATUS_RSSI, SENSOR_STATUS_SIGNAL, + SENSOR_STATUS_SSID, SENSOR_TEMPERATURE, SENSOR_TODAY, SENSOR_TOTAL, @@ -156,6 +157,7 @@ SENSOR_STATUS_RESTART_REASON: {ICON: "mdi:information-outline"}, SENSOR_STATUS_SIGNAL: {DEVICE_CLASS: DEVICE_CLASS_SIGNAL_STRENGTH}, SENSOR_STATUS_RSSI: {ICON: "mdi:access-point"}, + SENSOR_STATUS_SSID: {ICON: "mdi:access-point-network"}, SENSOR_TEMPERATURE: {DEVICE_CLASS: DEVICE_CLASS_TEMPERATURE}, SENSOR_TODAY: {DEVICE_CLASS: DEVICE_CLASS_POWER}, SENSOR_TOTAL: {DEVICE_CLASS: DEVICE_CLASS_POWER}, From c16483edabb134d0956b55b8682d59b8e555541c Mon Sep 17 00:00:00 2001 From: Erik Date: Wed, 11 Nov 2020 12:52:24 +0100 Subject: [PATCH 7/8] State of last restart sensor as UTC iso-string, add test --- homeassistant/components/tasmota/sensor.py | 3 +- tests/components/tasmota/test_sensor.py | 54 +++++++++++++++++++++- 2 files changed, 54 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/tasmota/sensor.py b/homeassistant/components/tasmota/sensor.py index 7eb2e0f168288..966bf8648d409 100644 --- a/homeassistant/components/tasmota/sensor.py +++ b/homeassistant/components/tasmota/sensor.py @@ -108,7 +108,6 @@ from homeassistant.core import callback from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity import Entity -import homeassistant.util.dt as dt_util from .const import DATA_REMOVE_DISCOVER_COMPONENT, DOMAIN as TASMOTA_DOMAIN from .discovery import TASMOTA_DISCOVERY_ENTITY_NEW @@ -261,7 +260,7 @@ def icon(self): def state(self): """Return the state of the entity.""" if self._state and self.device_class == DEVICE_CLASS_TIMESTAMP: - return dt_util.as_local(self._state) + return self._state.isoformat() return self._state @property diff --git a/tests/components/tasmota/test_sensor.py b/tests/components/tasmota/test_sensor.py index 06e24515ce0dc..eb30fba951f5f 100644 --- a/tests/components/tasmota/test_sensor.py +++ b/tests/components/tasmota/test_sensor.py @@ -1,8 +1,10 @@ """The tests for the Tasmota sensor platform.""" import copy +import datetime from datetime import timedelta import json +import hatasmota from hatasmota.utils import ( get_topic_stat_status, get_topic_tele_sensor, @@ -29,7 +31,7 @@ help_test_entity_id_update_subscriptions, ) -from tests.async_mock import patch +from tests.async_mock import Mock, patch from tests.common import async_fire_mqtt_message, async_fire_time_changed DEFAULT_SENSOR_CONFIG = { @@ -355,6 +357,56 @@ async def test_single_shot_status_sensor_state_via_mqtt(hass, mqtt_mock, setup_t assert state.state == "Another reason" +@pytest.mark.parametrize("status_sensor_disabled", [False]) +@patch.object(hatasmota.status_sensor, "datetime", Mock(wraps=datetime.datetime)) +async def test_restart_time_status_sensor_state_via_mqtt( + hass, mqtt_mock, setup_tasmota +): + """Test state update via MQTT.""" + entity_reg = await hass.helpers.entity_registry.async_get_registry() + + # Pre-enable the status sensor + entity_reg.async_get_or_create( + sensor.DOMAIN, + "tasmota", + "00000049A3BC_status_sensor_status_sensor_last_restart_time", + suggested_object_id="tasmota_status", + disabled_by=None, + ) + + 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.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) + + # Test polled state update + utc_now = datetime.datetime(2020, 11, 11, 8, 0, 0, tzinfo=dt.UTC) + hatasmota.status_sensor.datetime.now.return_value = utc_now + async_fire_mqtt_message( + hass, + "tasmota_49A3BC/stat/STATUS11", + '{"StatusSTS":{"UptimeSec":"3600"}}', + ) + await hass.async_block_till_done() + state = hass.states.get("sensor.tasmota_status") + assert state.state == "2020-11-11T07:00:00+00:00" + + async def test_attributes(hass, mqtt_mock, setup_tasmota): """Test correct attributes for sensors.""" config = copy.deepcopy(DEFAULT_CONFIG) From 23804e3644e14acef6f0ce8f40ce0f928249b240 Mon Sep 17 00:00:00 2001 From: Erik Date: Wed, 11 Nov 2020 12:53:48 +0100 Subject: [PATCH 8/8] Bump hatasmota to 0.0.29 --- homeassistant/components/tasmota/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/tasmota/manifest.json b/homeassistant/components/tasmota/manifest.json index 6dcb88d7de43c..dbe27e1a003ed 100644 --- a/homeassistant/components/tasmota/manifest.json +++ b/homeassistant/components/tasmota/manifest.json @@ -3,7 +3,7 @@ "name": "Tasmota (beta)", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/tasmota", - "requirements": ["hatasmota==0.0.28"], + "requirements": ["hatasmota==0.0.29"], "dependencies": ["mqtt"], "mqtt": ["tasmota/discovery/#"], "codeowners": ["@emontnemery"] diff --git a/requirements_all.txt b/requirements_all.txt index 25147aaee5512..9356c9505ea7d 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -738,7 +738,7 @@ hass-nabucasa==0.37.1 hass_splunk==0.1.1 # homeassistant.components.tasmota -hatasmota==0.0.28 +hatasmota==0.0.29 # homeassistant.components.jewish_calendar hdate==0.9.12 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index bb25a6f17d309..1bdfa9d676b11 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -376,7 +376,7 @@ hangups==0.4.11 hass-nabucasa==0.37.1 # homeassistant.components.tasmota -hatasmota==0.0.28 +hatasmota==0.0.29 # homeassistant.components.jewish_calendar hdate==0.9.12