Report entity IDs for min/max sensor platform (#33806)

* Report entity ids for min/max sensor platform

* Use better variable names
pull/36140/head
Thomas Hollstegge 2020-05-25 22:08:49 +02:00 committed by GitHub
parent 2793e6cdd8
commit fa55f01c8c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 59 additions and 18 deletions

View File

@ -19,17 +19,23 @@ from homeassistant.helpers.event import async_track_state_change
_LOGGER = logging.getLogger(__name__)
ATTR_MIN_VALUE = "min_value"
ATTR_MIN_ENTITY_ID = "min_entity_id"
ATTR_MAX_VALUE = "max_value"
ATTR_MAX_ENTITY_ID = "max_entity_id"
ATTR_COUNT_SENSORS = "count_sensors"
ATTR_MEAN = "mean"
ATTR_LAST = "last"
ATTR_LAST_ENTITY_ID = "last_entity_id"
ATTR_TO_PROPERTY = [
ATTR_COUNT_SENSORS,
ATTR_MAX_VALUE,
ATTR_MAX_ENTITY_ID,
ATTR_MEAN,
ATTR_MIN_VALUE,
ATTR_MIN_ENTITY_ID,
ATTR_LAST,
ATTR_LAST_ENTITY_ID,
]
CONF_ENTITY_IDS = "entity_ids"
@ -72,34 +78,36 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
def calc_min(sensor_values):
"""Calculate min value, honoring unknown states."""
val = None
for sval in sensor_values:
if sval != STATE_UNKNOWN:
if val is None or val > sval:
val = sval
return val
entity_id = None
for sensor_id, sensor_value in sensor_values:
if sensor_value != STATE_UNKNOWN:
if val is None or val > sensor_value:
entity_id, val = sensor_id, sensor_value
return entity_id, val
def calc_max(sensor_values):
"""Calculate max value, honoring unknown states."""
val = None
for sval in sensor_values:
if sval != STATE_UNKNOWN:
if val is None or val < sval:
val = sval
return val
entity_id = None
for sensor_id, sensor_value in sensor_values:
if sensor_value != STATE_UNKNOWN:
if val is None or val < sensor_value:
entity_id, val = sensor_id, sensor_value
return entity_id, val
def calc_mean(sensor_values, round_digits):
"""Calculate mean value, honoring unknown states."""
val = 0
sensor_value_sum = 0
count = 0
for sval in sensor_values:
if sval != STATE_UNKNOWN:
val += sval
for _, sensor_value in sensor_values:
if sensor_value != STATE_UNKNOWN:
sensor_value_sum += sensor_value
count += 1
if count == 0:
return None
return round(val / count, round_digits)
return round(sensor_value_sum / count, round_digits)
class MinMaxSensor(Entity):
@ -119,6 +127,7 @@ class MinMaxSensor(Entity):
self._unit_of_measurement = None
self._unit_of_measurement_mismatch = False
self.min_value = self.max_value = self.mean = self.last = None
self.min_entity_id = self.max_entity_id = self.last_entity_id = None
self.count_sensors = len(self._entity_ids)
self.states = {}
@ -149,6 +158,7 @@ class MinMaxSensor(Entity):
try:
self.states[entity] = float(new_state.state)
self.last = float(new_state.state)
self.last_entity_id = entity
except ValueError:
_LOGGER.warning(
"Unable to store state. Only numerical states are supported"
@ -201,7 +211,11 @@ class MinMaxSensor(Entity):
async def async_update(self):
"""Get the latest data and updates the states."""
sensor_values = [self.states[k] for k in self._entity_ids if k in self.states]
self.min_value = calc_min(sensor_values)
self.max_value = calc_max(sensor_values)
sensor_values = [
(entity_id, self.states[entity_id])
for entity_id in self._entity_ids
if entity_id in self.states
]
self.min_entity_id, self.min_value = calc_min(sensor_values)
self.max_entity_id, self.max_value = calc_max(sensor_values)
self.mean = calc_mean(sensor_values, self._round_digits)

View File

@ -54,7 +54,9 @@ class TestMinMaxSensor(unittest.TestCase):
state = self.hass.states.get("sensor.test_min")
assert str(float(self.min)) == state.state
assert entity_ids[2] == state.attributes.get("min_entity_id")
assert self.max == state.attributes.get("max_value")
assert entity_ids[1] == state.attributes.get("max_entity_id")
assert self.mean == state.attributes.get("mean")
def test_max_sensor(self):
@ -79,7 +81,9 @@ class TestMinMaxSensor(unittest.TestCase):
state = self.hass.states.get("sensor.test_max")
assert str(float(self.max)) == state.state
assert entity_ids[2] == state.attributes.get("min_entity_id")
assert self.min == state.attributes.get("min_value")
assert entity_ids[1] == state.attributes.get("max_entity_id")
assert self.mean == state.attributes.get("mean")
def test_mean_sensor(self):
@ -105,7 +109,9 @@ class TestMinMaxSensor(unittest.TestCase):
assert str(float(self.mean)) == state.state
assert self.min == state.attributes.get("min_value")
assert entity_ids[2] == state.attributes.get("min_entity_id")
assert self.max == state.attributes.get("max_value")
assert entity_ids[1] == state.attributes.get("max_entity_id")
def test_mean_1_digit_sensor(self):
"""Test the mean with 1-digit precision sensor."""
@ -131,7 +137,9 @@ class TestMinMaxSensor(unittest.TestCase):
assert str(float(self.mean_1_digit)) == state.state
assert self.min == state.attributes.get("min_value")
assert entity_ids[2] == state.attributes.get("min_entity_id")
assert self.max == state.attributes.get("max_value")
assert entity_ids[1] == state.attributes.get("max_entity_id")
def test_mean_4_digit_sensor(self):
"""Test the mean with 1-digit precision sensor."""
@ -157,7 +165,9 @@ class TestMinMaxSensor(unittest.TestCase):
assert str(float(self.mean_4_digits)) == state.state
assert self.min == state.attributes.get("min_value")
assert entity_ids[2] == state.attributes.get("min_entity_id")
assert self.max == state.attributes.get("max_value")
assert entity_ids[1] == state.attributes.get("max_entity_id")
def test_not_enough_sensor_value(self):
"""Test that there is nothing done if not enough values available."""
@ -179,24 +189,40 @@ class TestMinMaxSensor(unittest.TestCase):
state = self.hass.states.get("sensor.test_max")
assert STATE_UNKNOWN == state.state
assert state.attributes.get("min_entity_id") is None
assert state.attributes.get("min_value") is None
assert state.attributes.get("max_entity_id") is None
assert state.attributes.get("max_value") is None
self.hass.states.set(entity_ids[1], self.values[1])
self.hass.block_till_done()
state = self.hass.states.get("sensor.test_max")
assert STATE_UNKNOWN != state.state
assert entity_ids[1] == state.attributes.get("min_entity_id")
assert self.values[1] == state.attributes.get("min_value")
assert entity_ids[1] == state.attributes.get("max_entity_id")
assert self.values[1] == state.attributes.get("max_value")
self.hass.states.set(entity_ids[2], STATE_UNKNOWN)
self.hass.block_till_done()
state = self.hass.states.get("sensor.test_max")
assert STATE_UNKNOWN != state.state
assert entity_ids[1] == state.attributes.get("min_entity_id")
assert self.values[1] == state.attributes.get("min_value")
assert entity_ids[1] == state.attributes.get("max_entity_id")
assert self.values[1] == state.attributes.get("max_value")
self.hass.states.set(entity_ids[1], STATE_UNAVAILABLE)
self.hass.block_till_done()
state = self.hass.states.get("sensor.test_max")
assert STATE_UNKNOWN == state.state
assert state.attributes.get("min_entity_id") is None
assert state.attributes.get("min_value") is None
assert state.attributes.get("max_entity_id") is None
assert state.attributes.get("max_value") is None
def test_different_unit_of_measurement(self):
"""Test for different unit of measurement."""
@ -264,6 +290,7 @@ class TestMinMaxSensor(unittest.TestCase):
self.hass.block_till_done()
state = self.hass.states.get("sensor.test_last")
assert str(float(value)) == state.state
assert entity_id == state.attributes.get("last_entity_id")
assert self.min == state.attributes.get("min_value")
assert self.max == state.attributes.get("max_value")