From 9eaf8bd21bb0e1f29f88511e0d9174a9a806c833 Mon Sep 17 00:00:00 2001 From: Thomas Dietrich <Thomas@Nurzen.de> Date: Mon, 8 Nov 2021 23:26:00 +0100 Subject: [PATCH] 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 --- homeassistant/components/statistics/sensor.py | 8 +- tests/components/statistics/test_sensor.py | 76 ++++++++++++++++++- 2 files changed, 79 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/statistics/sensor.py b/homeassistant/components/statistics/sensor.py index 3d5857e2b03..7b2951a1ab6 100644 --- a/homeassistant/components/statistics/sensor.py +++ b/homeassistant/components/statistics/sensor.py @@ -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): diff --git a/tests/components/statistics/test_sensor.py b/tests/components/statistics/test_sensor.py index a2eaa9add03..c3b14ba360d 100644 --- a/tests/components/statistics/test_sensor.py +++ b/tests/components/statistics/test_sensor.py @@ -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