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__) _LOGGER = logging.getLogger(__name__)
ATTR_MIN_VALUE = "min_value" ATTR_MIN_VALUE = "min_value"
ATTR_MIN_ENTITY_ID = "min_entity_id"
ATTR_MAX_VALUE = "max_value" ATTR_MAX_VALUE = "max_value"
ATTR_MAX_ENTITY_ID = "max_entity_id"
ATTR_COUNT_SENSORS = "count_sensors" ATTR_COUNT_SENSORS = "count_sensors"
ATTR_MEAN = "mean" ATTR_MEAN = "mean"
ATTR_LAST = "last" ATTR_LAST = "last"
ATTR_LAST_ENTITY_ID = "last_entity_id"
ATTR_TO_PROPERTY = [ ATTR_TO_PROPERTY = [
ATTR_COUNT_SENSORS, ATTR_COUNT_SENSORS,
ATTR_MAX_VALUE, ATTR_MAX_VALUE,
ATTR_MAX_ENTITY_ID,
ATTR_MEAN, ATTR_MEAN,
ATTR_MIN_VALUE, ATTR_MIN_VALUE,
ATTR_MIN_ENTITY_ID,
ATTR_LAST, ATTR_LAST,
ATTR_LAST_ENTITY_ID,
] ]
CONF_ENTITY_IDS = "entity_ids" 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): def calc_min(sensor_values):
"""Calculate min value, honoring unknown states.""" """Calculate min value, honoring unknown states."""
val = None val = None
for sval in sensor_values: entity_id = None
if sval != STATE_UNKNOWN: for sensor_id, sensor_value in sensor_values:
if val is None or val > sval: if sensor_value != STATE_UNKNOWN:
val = sval if val is None or val > sensor_value:
return val entity_id, val = sensor_id, sensor_value
return entity_id, val
def calc_max(sensor_values): def calc_max(sensor_values):
"""Calculate max value, honoring unknown states.""" """Calculate max value, honoring unknown states."""
val = None val = None
for sval in sensor_values: entity_id = None
if sval != STATE_UNKNOWN: for sensor_id, sensor_value in sensor_values:
if val is None or val < sval: if sensor_value != STATE_UNKNOWN:
val = sval if val is None or val < sensor_value:
return val entity_id, val = sensor_id, sensor_value
return entity_id, val
def calc_mean(sensor_values, round_digits): def calc_mean(sensor_values, round_digits):
"""Calculate mean value, honoring unknown states.""" """Calculate mean value, honoring unknown states."""
val = 0 sensor_value_sum = 0
count = 0 count = 0
for sval in sensor_values: for _, sensor_value in sensor_values:
if sval != STATE_UNKNOWN: if sensor_value != STATE_UNKNOWN:
val += sval sensor_value_sum += sensor_value
count += 1 count += 1
if count == 0: if count == 0:
return None return None
return round(val / count, round_digits) return round(sensor_value_sum / count, round_digits)
class MinMaxSensor(Entity): class MinMaxSensor(Entity):
@ -119,6 +127,7 @@ class MinMaxSensor(Entity):
self._unit_of_measurement = None self._unit_of_measurement = None
self._unit_of_measurement_mismatch = False self._unit_of_measurement_mismatch = False
self.min_value = self.max_value = self.mean = self.last = None 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.count_sensors = len(self._entity_ids)
self.states = {} self.states = {}
@ -149,6 +158,7 @@ class MinMaxSensor(Entity):
try: try:
self.states[entity] = float(new_state.state) self.states[entity] = float(new_state.state)
self.last = float(new_state.state) self.last = float(new_state.state)
self.last_entity_id = entity
except ValueError: except ValueError:
_LOGGER.warning( _LOGGER.warning(
"Unable to store state. Only numerical states are supported" "Unable to store state. Only numerical states are supported"
@ -201,7 +211,11 @@ class MinMaxSensor(Entity):
async def async_update(self): async def async_update(self):
"""Get the latest data and updates the states.""" """Get the latest data and updates the states."""
sensor_values = [self.states[k] for k in self._entity_ids if k in self.states] sensor_values = [
self.min_value = calc_min(sensor_values) (entity_id, self.states[entity_id])
self.max_value = calc_max(sensor_values) 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) 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") state = self.hass.states.get("sensor.test_min")
assert str(float(self.min)) == state.state 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 self.max == state.attributes.get("max_value")
assert entity_ids[1] == state.attributes.get("max_entity_id")
assert self.mean == state.attributes.get("mean") assert self.mean == state.attributes.get("mean")
def test_max_sensor(self): def test_max_sensor(self):
@ -79,7 +81,9 @@ class TestMinMaxSensor(unittest.TestCase):
state = self.hass.states.get("sensor.test_max") state = self.hass.states.get("sensor.test_max")
assert str(float(self.max)) == state.state 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 self.min == state.attributes.get("min_value")
assert entity_ids[1] == state.attributes.get("max_entity_id")
assert self.mean == state.attributes.get("mean") assert self.mean == state.attributes.get("mean")
def test_mean_sensor(self): def test_mean_sensor(self):
@ -105,7 +109,9 @@ class TestMinMaxSensor(unittest.TestCase):
assert str(float(self.mean)) == state.state assert str(float(self.mean)) == state.state
assert self.min == state.attributes.get("min_value") 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 self.max == state.attributes.get("max_value")
assert entity_ids[1] == state.attributes.get("max_entity_id")
def test_mean_1_digit_sensor(self): def test_mean_1_digit_sensor(self):
"""Test the mean with 1-digit precision sensor.""" """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 str(float(self.mean_1_digit)) == state.state
assert self.min == state.attributes.get("min_value") 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 self.max == state.attributes.get("max_value")
assert entity_ids[1] == state.attributes.get("max_entity_id")
def test_mean_4_digit_sensor(self): def test_mean_4_digit_sensor(self):
"""Test the mean with 1-digit precision sensor.""" """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 str(float(self.mean_4_digits)) == state.state
assert self.min == state.attributes.get("min_value") 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 self.max == state.attributes.get("max_value")
assert entity_ids[1] == state.attributes.get("max_entity_id")
def test_not_enough_sensor_value(self): def test_not_enough_sensor_value(self):
"""Test that there is nothing done if not enough values available.""" """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") state = self.hass.states.get("sensor.test_max")
assert STATE_UNKNOWN == state.state 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.states.set(entity_ids[1], self.values[1])
self.hass.block_till_done() self.hass.block_till_done()
state = self.hass.states.get("sensor.test_max") state = self.hass.states.get("sensor.test_max")
assert STATE_UNKNOWN != state.state 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.states.set(entity_ids[2], STATE_UNKNOWN)
self.hass.block_till_done() self.hass.block_till_done()
state = self.hass.states.get("sensor.test_max") state = self.hass.states.get("sensor.test_max")
assert STATE_UNKNOWN != state.state 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.states.set(entity_ids[1], STATE_UNAVAILABLE)
self.hass.block_till_done() self.hass.block_till_done()
state = self.hass.states.get("sensor.test_max") state = self.hass.states.get("sensor.test_max")
assert STATE_UNKNOWN == state.state 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): def test_different_unit_of_measurement(self):
"""Test for different unit of measurement.""" """Test for different unit of measurement."""
@ -264,6 +290,7 @@ class TestMinMaxSensor(unittest.TestCase):
self.hass.block_till_done() self.hass.block_till_done()
state = self.hass.states.get("sensor.test_last") state = self.hass.states.get("sensor.test_last")
assert str(float(value)) == state.state 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.min == state.attributes.get("min_value")
assert self.max == state.attributes.get("max_value") assert self.max == state.attributes.get("max_value")