Fix statistics sensor mean and median when only one sample is available. (#11180)

* Fix statistics sensor mean and median when only one sample is available.

With only one data point stddev and variance throw an exception.
This would clear the (valid) mean and median calculations.

Separate the try..catch blocks for one-or-more and two-or-more stats so
that this doesn't happen.

Test this with a new sampling_size_1 test.

* test_statistics trivial whitespace fix
pull/11232/head
markferry 2017-12-18 20:21:27 +00:00 committed by Fabian Affolter
parent 061395d2f8
commit ef22a6e18d
2 changed files with 41 additions and 3 deletions

View File

@ -175,15 +175,20 @@ class StatisticsSensor(Entity):
self._purge_old()
if not self.is_binary:
try:
try: # require only one data point
self.mean = round(statistics.mean(self.states), 2)
self.median = round(statistics.median(self.states), 2)
except statistics.StatisticsError as err:
_LOGGER.error(err)
self.mean = self.median = STATE_UNKNOWN
try: # require at least two data points
self.stdev = round(statistics.stdev(self.states), 2)
self.variance = round(statistics.variance(self.states), 2)
except statistics.StatisticsError as err:
_LOGGER.error(err)
self.mean = self.median = STATE_UNKNOWN
self.stdev = self.variance = STATE_UNKNOWN
if self.states:
self.total = round(sum(self.states), 2)
self.min = min(self.states)

View File

@ -3,7 +3,8 @@ import unittest
import statistics
from homeassistant.setup import setup_component
from homeassistant.const import (ATTR_UNIT_OF_MEASUREMENT, TEMP_CELSIUS)
from homeassistant.const import (
ATTR_UNIT_OF_MEASUREMENT, TEMP_CELSIUS, STATE_UNKNOWN)
from homeassistant.util import dt as dt_util
from tests.common import get_test_home_assistant
from unittest.mock import patch
@ -106,6 +107,38 @@ class TestStatisticsSensor(unittest.TestCase):
self.assertEqual(3.8, state.attributes.get('min_value'))
self.assertEqual(14, state.attributes.get('max_value'))
def test_sampling_size_1(self):
"""Test validity of stats requiring only one sample."""
assert setup_component(self.hass, 'sensor', {
'sensor': {
'platform': 'statistics',
'name': 'test',
'entity_id': 'sensor.test_monitored',
'sampling_size': 1,
}
})
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})
self.hass.block_till_done()
state = self.hass.states.get('sensor.test_mean')
# require only one data point
self.assertEqual(self.values[-1], state.attributes.get('min_value'))
self.assertEqual(self.values[-1], state.attributes.get('max_value'))
self.assertEqual(self.values[-1], state.attributes.get('mean'))
self.assertEqual(self.values[-1], state.attributes.get('median'))
self.assertEqual(self.values[-1], state.attributes.get('total'))
self.assertEqual(0, state.attributes.get('change'))
self.assertEqual(0, state.attributes.get('average_change'))
# require at least two data points
self.assertEqual(STATE_UNKNOWN, state.attributes.get('variance'))
self.assertEqual(STATE_UNKNOWN,
state.attributes.get('standard_deviation'))
def test_max_age(self):
"""Test value deprecation."""
mock_data = {