Correct state restoring for MQTT temperature sensors (#66741)

* Correct state restoring for MQTT temperature sensors

* Adjust test

* Adjust test
This commit is contained in:
Erik Montnemery 2022-02-18 11:35:44 +01:00 committed by GitHub
parent bfb1abd3a2
commit 2abcd7cd94
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 30 additions and 11 deletions

View File

@ -13,8 +13,8 @@ from homeassistant.components.sensor import (
DEVICE_CLASSES_SCHEMA, DEVICE_CLASSES_SCHEMA,
ENTITY_ID_FORMAT, ENTITY_ID_FORMAT,
STATE_CLASSES_SCHEMA, STATE_CLASSES_SCHEMA,
RestoreSensor,
SensorDeviceClass, SensorDeviceClass,
SensorEntity,
) )
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ( from homeassistant.const import (
@ -30,7 +30,6 @@ from homeassistant.core import HomeAssistant, callback
import homeassistant.helpers.config_validation as cv import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.event import async_track_point_in_utc_time from homeassistant.helpers.event import async_track_point_in_utc_time
from homeassistant.helpers.restore_state import RestoreEntity
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from homeassistant.util import dt as dt_util from homeassistant.util import dt as dt_util
@ -144,7 +143,7 @@ async def _async_setup_entity(
async_add_entities([MqttSensor(hass, config, config_entry, discovery_data)]) async_add_entities([MqttSensor(hass, config, config_entry, discovery_data)])
class MqttSensor(MqttEntity, SensorEntity, RestoreEntity): class MqttSensor(MqttEntity, RestoreSensor):
"""Representation of a sensor that can be updated using MQTT.""" """Representation of a sensor that can be updated using MQTT."""
_entity_id_format = ENTITY_ID_FORMAT _entity_id_format = ENTITY_ID_FORMAT
@ -172,6 +171,8 @@ class MqttSensor(MqttEntity, SensorEntity, RestoreEntity):
and expire_after > 0 and expire_after > 0
and (last_state := await self.async_get_last_state()) is not None and (last_state := await self.async_get_last_state()) is not None
and last_state.state not in [STATE_UNKNOWN, STATE_UNAVAILABLE] and last_state.state not in [STATE_UNKNOWN, STATE_UNAVAILABLE]
and (last_sensor_data := await self.async_get_last_sensor_data())
is not None
# We might have set up a trigger already after subscribing from # We might have set up a trigger already after subscribing from
# super().async_added_to_hass(), then we should not restore state # super().async_added_to_hass(), then we should not restore state
and not self._expiration_trigger and not self._expiration_trigger
@ -182,7 +183,7 @@ class MqttSensor(MqttEntity, SensorEntity, RestoreEntity):
_LOGGER.debug("Skip state recovery after reload for %s", self.entity_id) _LOGGER.debug("Skip state recovery after reload for %s", self.entity_id)
return return
self._expired = False self._expired = False
self._state = last_state.state self._state = last_sensor_data.native_value
self._expiration_trigger = async_track_point_in_utc_time( self._expiration_trigger = async_track_point_in_utc_time(
self.hass, self._value_is_expired, expiration_at self.hass, self._value_is_expired, expiration_at

View File

@ -2,13 +2,19 @@
import copy import copy
from datetime import datetime, timedelta from datetime import datetime, timedelta
import json import json
from unittest.mock import patch from unittest.mock import MagicMock, patch
import pytest import pytest
from homeassistant.components.mqtt.sensor import MQTT_SENSOR_ATTRIBUTES_BLOCKED from homeassistant.components.mqtt.sensor import MQTT_SENSOR_ATTRIBUTES_BLOCKED
import homeassistant.components.sensor as sensor import homeassistant.components.sensor as sensor
from homeassistant.const import EVENT_STATE_CHANGED, STATE_UNAVAILABLE, STATE_UNKNOWN from homeassistant.const import (
EVENT_STATE_CHANGED,
STATE_UNAVAILABLE,
STATE_UNKNOWN,
TEMP_CELSIUS,
TEMP_FAHRENHEIT,
)
import homeassistant.core as ha import homeassistant.core as ha
from homeassistant.helpers import device_registry as dr from homeassistant.helpers import device_registry as dr
from homeassistant.setup import async_setup_component from homeassistant.setup import async_setup_component
@ -989,10 +995,15 @@ async def test_cleanup_triggers_and_restoring_state(
config1["name"] = "test1" config1["name"] = "test1"
config1["expire_after"] = 30 config1["expire_after"] = 30
config1["state_topic"] = "test-topic1" config1["state_topic"] = "test-topic1"
config1["device_class"] = "temperature"
config1["unit_of_measurement"] = TEMP_FAHRENHEIT
config2 = copy.deepcopy(DEFAULT_CONFIG[domain]) config2 = copy.deepcopy(DEFAULT_CONFIG[domain])
config2["name"] = "test2" config2["name"] = "test2"
config2["expire_after"] = 5 config2["expire_after"] = 5
config2["state_topic"] = "test-topic2" config2["state_topic"] = "test-topic2"
config2["device_class"] = "temperature"
config2["unit_of_measurement"] = TEMP_CELSIUS
freezer.move_to("2022-02-02 12:01:00+01:00") freezer.move_to("2022-02-02 12:01:00+01:00")
@ -1004,7 +1015,7 @@ async def test_cleanup_triggers_and_restoring_state(
await hass.async_block_till_done() await hass.async_block_till_done()
async_fire_mqtt_message(hass, "test-topic1", "100") async_fire_mqtt_message(hass, "test-topic1", "100")
state = hass.states.get("sensor.test1") state = hass.states.get("sensor.test1")
assert state.state == "100" assert state.state == "38" # 100 °F -> 38 °C
async_fire_mqtt_message(hass, "test-topic2", "200") async_fire_mqtt_message(hass, "test-topic2", "200")
state = hass.states.get("sensor.test2") state = hass.states.get("sensor.test2")
@ -1026,14 +1037,14 @@ async def test_cleanup_triggers_and_restoring_state(
assert "State recovered after reload for sensor.test2" not in caplog.text assert "State recovered after reload for sensor.test2" not in caplog.text
state = hass.states.get("sensor.test1") state = hass.states.get("sensor.test1")
assert state.state == "100" assert state.state == "38" # 100 °F -> 38 °C
state = hass.states.get("sensor.test2") state = hass.states.get("sensor.test2")
assert state.state == STATE_UNAVAILABLE assert state.state == STATE_UNAVAILABLE
async_fire_mqtt_message(hass, "test-topic1", "101") async_fire_mqtt_message(hass, "test-topic1", "80")
state = hass.states.get("sensor.test1") state = hass.states.get("sensor.test1")
assert state.state == "101" assert state.state == "27" # 80 °F -> 27 °C
async_fire_mqtt_message(hass, "test-topic2", "201") async_fire_mqtt_message(hass, "test-topic2", "201")
state = hass.states.get("sensor.test2") state = hass.states.get("sensor.test2")
@ -1057,10 +1068,16 @@ async def test_skip_restoring_state_with_over_due_expire_trigger(
{}, {},
last_changed=datetime.fromisoformat("2022-02-02 12:01:35+01:00"), last_changed=datetime.fromisoformat("2022-02-02 12:01:35+01:00"),
) )
fake_extra_data = MagicMock()
with patch( with patch(
"homeassistant.helpers.restore_state.RestoreEntity.async_get_last_state", "homeassistant.helpers.restore_state.RestoreEntity.async_get_last_state",
return_value=fake_state, return_value=fake_state,
), assert_setup_component(1, domain): ), patch(
"homeassistant.helpers.restore_state.RestoreEntity.async_get_last_extra_data",
return_value=fake_extra_data,
), assert_setup_component(
1, domain
):
assert await async_setup_component(hass, domain, {domain: config3}) assert await async_setup_component(hass, domain, {domain: config3})
await hass.async_block_till_done() await hass.async_block_till_done()
assert "Skip state recovery after reload for sensor.test3" in caplog.text assert "Skip state recovery after reload for sensor.test3" in caplog.text
@ -1087,4 +1104,5 @@ async def test_encoding_subscribable_topics(
value, value,
attribute, attribute,
attribute_value, attribute_value,
skip_raw_test=True,
) )