Fix statistics precision handling (#59202)

* Fix statistics precision error when configured 0, fix #42547

* Add tests for statistics precision

* Apply precision=0 logic to float numbers only

* Implement contextlib way of exception handling
This commit is contained in:
Thomas Dietrich 2021-11-08 23:26:00 +01:00 committed by GitHub
parent 2924f4605b
commit 9eaf8bd21b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 79 additions and 5 deletions

View file

@ -1,5 +1,6 @@
"""Support for statistics for sensor values."""
from collections import deque
import contextlib
import logging
import statistics
@ -203,7 +204,12 @@ class StatisticsSensor(SensorEntity):
@property
def native_value(self):
"""Return the state of the sensor."""
return self.mean if not self.is_binary else self.count
if self.is_binary:
return self.count
if self._precision == 0:
with contextlib.suppress(TypeError, ValueError):
return int(self.mean)
return self.mean
@property
def native_unit_of_measurement(self):

View file

@ -103,7 +103,9 @@ class TestStatisticsSensor(unittest.TestCase):
for value in self.values:
self.hass.states.set(
"sensor.test_monitored", value, {ATTR_UNIT_OF_MEASUREMENT: TEMP_CELSIUS}
"sensor.test_monitored",
value,
{ATTR_UNIT_OF_MEASUREMENT: TEMP_CELSIUS},
)
self.hass.block_till_done()
@ -163,7 +165,9 @@ class TestStatisticsSensor(unittest.TestCase):
for value in self.values:
self.hass.states.set(
"sensor.test_monitored", value, {ATTR_UNIT_OF_MEASUREMENT: TEMP_CELSIUS}
"sensor.test_monitored",
value,
{ATTR_UNIT_OF_MEASUREMENT: TEMP_CELSIUS},
)
self.hass.block_till_done()
@ -193,7 +197,9 @@ class TestStatisticsSensor(unittest.TestCase):
for value in self.values[-3:]: # just the last 3 will do
self.hass.states.set(
"sensor.test_monitored", value, {ATTR_UNIT_OF_MEASUREMENT: TEMP_CELSIUS}
"sensor.test_monitored",
value,
{ATTR_UNIT_OF_MEASUREMENT: TEMP_CELSIUS},
)
self.hass.block_till_done()
@ -363,6 +369,66 @@ class TestStatisticsSensor(unittest.TestCase):
) == state.attributes.get("max_age")
assert self.change_rate == state.attributes.get("change_rate")
def test_precision_0(self):
"""Test correct result with precision=0 as integer."""
assert setup_component(
self.hass,
"sensor",
{
"sensor": {
"platform": "statistics",
"name": "test",
"entity_id": "sensor.test_monitored",
"precision": 0,
}
},
)
self.hass.block_till_done()
self.hass.start()
self.hass.block_till_done()
for value in self.values:
self.hass.states.set(
"sensor.test_monitored",
value,
{ATTR_UNIT_OF_MEASUREMENT: TEMP_CELSIUS},
)
self.hass.block_till_done()
state = self.hass.states.get("sensor.test")
assert state.state == str(int(state.attributes.get("mean")))
def test_precision_1(self):
"""Test correct result with precision=1 rounded to one decimal."""
assert setup_component(
self.hass,
"sensor",
{
"sensor": {
"platform": "statistics",
"name": "test",
"entity_id": "sensor.test_monitored",
"precision": 1,
}
},
)
self.hass.block_till_done()
self.hass.start()
self.hass.block_till_done()
for value in self.values:
self.hass.states.set(
"sensor.test_monitored",
value,
{ATTR_UNIT_OF_MEASUREMENT: TEMP_CELSIUS},
)
self.hass.block_till_done()
state = self.hass.states.get("sensor.test")
assert state.state == str(round(sum(self.values) / len(self.values), 1))
def test_initialize_from_database(self):
"""Test initializing the statistics from the database."""
# enable the recorder
@ -372,7 +438,9 @@ class TestStatisticsSensor(unittest.TestCase):
# store some values
for value in self.values:
self.hass.states.set(
"sensor.test_monitored", value, {ATTR_UNIT_OF_MEASUREMENT: TEMP_CELSIUS}
"sensor.test_monitored",
value,
{ATTR_UNIT_OF_MEASUREMENT: TEMP_CELSIUS},
)
self.hass.block_till_done()
# wait for the recorder to really store the data