Fix ZHA temperature sensor restoration (#30661)

* Add test for restoring state for zha temp.

* Don't restore unit of measurement for ZHA sensors.

Properly restore ZHA temperature sensor state.
This commit is contained in:
Alexei Chetroi 2020-01-10 20:30:58 -05:00 committed by David F. Mulcahey
parent 605b0ceb5f
commit 008dddb17c
2 changed files with 135 additions and 8 deletions

View file

@ -12,9 +12,15 @@ from homeassistant.components.sensor import (
DEVICE_CLASS_TEMPERATURE,
DOMAIN,
)
from homeassistant.const import ATTR_UNIT_OF_MEASUREMENT, POWER_WATT, TEMP_CELSIUS
from homeassistant.const import (
ATTR_UNIT_OF_MEASUREMENT,
POWER_WATT,
STATE_UNKNOWN,
TEMP_CELSIUS,
)
from homeassistant.core import callback
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.util.temperature import fahrenheit_to_celsius
from .core.const import (
CHANNEL_ELECTRICAL_MEASUREMENT,
@ -160,7 +166,6 @@ class Sensor(ZhaEntity):
def async_restore_last_state(self, last_state):
"""Restore previous state."""
self._state = last_state.state
self._unit = last_state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
@callback
async def async_state_attr_provider(self):
@ -277,3 +282,14 @@ class Temperature(Sensor):
_device_class = DEVICE_CLASS_TEMPERATURE
_divisor = 100
_unit = TEMP_CELSIUS
@callback
def async_restore_last_state(self, last_state):
"""Restore previous state."""
if last_state.state == STATE_UNKNOWN:
return
if last_state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) != TEMP_CELSIUS:
ftemp = float(last_state.state)
self._state = round(fahrenheit_to_celsius(ftemp), 1)
return
self._state = last_state.state

View file

@ -1,4 +1,5 @@
"""Test zha sensor."""
import pytest
import zigpy.zcl.clusters.general as general
import zigpy.zcl.clusters.homeautomation as homeautomation
import zigpy.zcl.clusters.measurement as measurement
@ -6,7 +7,20 @@ import zigpy.zcl.clusters.smartenergy as smartenergy
import zigpy.zcl.foundation as zcl_f
from homeassistant.components.sensor import DOMAIN
from homeassistant.const import STATE_UNAVAILABLE, STATE_UNKNOWN
import homeassistant.config as config_util
from homeassistant.const import (
ATTR_ENTITY_ID,
ATTR_UNIT_OF_MEASUREMENT,
CONF_UNIT_SYSTEM,
CONF_UNIT_SYSTEM_IMPERIAL,
CONF_UNIT_SYSTEM_METRIC,
STATE_UNAVAILABLE,
STATE_UNKNOWN,
TEMP_CELSIUS,
TEMP_FAHRENHEIT,
)
from homeassistant.helpers import restore_state
from homeassistant.util import dt as dt_util
from .common import (
async_enable_traffic,
@ -39,7 +53,7 @@ async def test_sensor(hass, config_entry, zha_gateway):
# ensure the sensor entity was created for each id in cluster_ids
for cluster_id in cluster_ids:
zigpy_device_info = zigpy_device_infos[cluster_id]
entity_id = zigpy_device_info["entity_id"]
entity_id = zigpy_device_info[ATTR_ENTITY_ID]
assert hass.states.get(entity_id).state == STATE_UNAVAILABLE
# allow traffic to flow through the gateway and devices
@ -55,7 +69,7 @@ async def test_sensor(hass, config_entry, zha_gateway):
# test that the sensors now have a state of unknown
for cluster_id in cluster_ids:
zigpy_device_info = zigpy_device_infos[cluster_id]
entity_id = zigpy_device_info["entity_id"]
entity_id = zigpy_device_info[ATTR_ENTITY_ID]
assert hass.states.get(entity_id).state == STATE_UNKNOWN
# get the humidity device info and test the associated sensor logic
@ -128,7 +142,7 @@ async def async_build_devices(hass, zha_gateway, config_entry, cluster_ids):
device_info["cluster"] = zigpy_device.endpoints.get(1).in_clusters[cluster_id]
zha_device = zha_gateway.get_device(zigpy_device.ieee)
device_info["zha_device"] = zha_device
device_info["entity_id"] = await find_entity_id(DOMAIN, zha_device, hass)
device_info[ATTR_ENTITY_ID] = await find_entity_id(DOMAIN, zha_device, hass)
await hass.async_block_till_done()
return device_infos
@ -187,6 +201,103 @@ def assert_state(hass, device_info, state, unit_of_measurement):
This is used to ensure that the logic in each sensor class handled the
attribute report it received correctly.
"""
hass_state = hass.states.get(device_info["entity_id"])
hass_state = hass.states.get(device_info[ATTR_ENTITY_ID])
assert hass_state.state == state
assert hass_state.attributes.get("unit_of_measurement") == unit_of_measurement
assert hass_state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == unit_of_measurement
@pytest.fixture
def hass_ms(hass):
"""Hass instance with measurement system."""
async def _hass_ms(meas_sys):
await config_util.async_process_ha_core_config(
hass, {CONF_UNIT_SYSTEM: meas_sys}
)
await hass.async_block_till_done()
return hass
return _hass_ms
@pytest.fixture
def core_rs(hass_storage):
"""Core.restore_state fixture."""
def _storage(entity_id, uom, state):
now = dt_util.utcnow().isoformat()
hass_storage[restore_state.STORAGE_KEY] = {
"version": restore_state.STORAGE_VERSION,
"key": restore_state.STORAGE_KEY,
"data": [
{
"state": {
"entity_id": entity_id,
"state": str(state),
"attributes": {ATTR_UNIT_OF_MEASUREMENT: uom},
"last_changed": now,
"last_updated": now,
"context": {
"id": "3c2243ff5f30447eb12e7348cfd5b8ff",
"user_id": None,
},
},
"last_seen": now,
}
],
}
return
return _storage
@pytest.mark.parametrize(
"uom, raw_temp, expected, restore",
[
(TEMP_CELSIUS, 2900, 29, False),
(TEMP_CELSIUS, 2900, 29, True),
(TEMP_FAHRENHEIT, 2900, 84, False),
(TEMP_FAHRENHEIT, 2900, 84, True),
],
)
async def test_temp_uom(
uom, raw_temp, expected, restore, hass_ms, config_entry, zha_gateway, core_rs
):
"""Test zha temperature sensor unit of measurement."""
entity_id = "sensor.fake1026_fakemodel1026_004f3202_temperature"
if restore:
core_rs(entity_id, uom, state=(expected - 2))
hass = await hass_ms(
CONF_UNIT_SYSTEM_METRIC if uom == TEMP_CELSIUS else CONF_UNIT_SYSTEM_IMPERIAL
)
# list of cluster ids to create devices and sensor entities for
temp_cluster = measurement.TemperatureMeasurement
cluster_ids = [temp_cluster.cluster_id]
# devices that were created from cluster_ids list above
zigpy_device_infos = await async_build_devices(
hass, zha_gateway, config_entry, cluster_ids
)
zigpy_device_info = zigpy_device_infos[temp_cluster.cluster_id]
zha_device = zigpy_device_info["zha_device"]
if not restore:
assert hass.states.get(entity_id).state == STATE_UNAVAILABLE
# allow traffic to flow through the gateway and devices
await async_enable_traffic(hass, zha_gateway, [zha_device])
# test that the sensors now have a state of unknown
if not restore:
assert hass.states.get(entity_id).state == STATE_UNKNOWN
await send_attribute_report(hass, zigpy_device_info["cluster"], 0, raw_temp)
await hass.async_block_till_done()
state = hass.states.get(entity_id)
assert state is not None
assert round(float(state.state)) == expected
assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == uom