Better handling of source sensor unavailability in Riemman Integration (#93137)
* refactor and increase coverage * fix log order
This commit is contained in:
parent
b754f03eb1
commit
e100bcfaea
2 changed files with 126 additions and 35 deletions
|
@ -197,25 +197,23 @@ class IntegrationSensor(RestoreEntity, SensorEntity):
|
|||
old_state: State | None = event.data.get("old_state")
|
||||
new_state: State | None = event.data.get("new_state")
|
||||
|
||||
if (
|
||||
source_state := self.hass.states.get(self._sensor_source_id)
|
||||
) is None or source_state.state == STATE_UNAVAILABLE:
|
||||
self._attr_available = False
|
||||
self.async_write_ha_state()
|
||||
return
|
||||
|
||||
self._attr_available = True
|
||||
|
||||
if new_state is None or new_state.state in (
|
||||
STATE_UNKNOWN,
|
||||
STATE_UNAVAILABLE,
|
||||
):
|
||||
return
|
||||
|
||||
# We may want to update our state before an early return,
|
||||
# based on the source sensor's unit_of_measurement
|
||||
# or device_class.
|
||||
update_state = False
|
||||
|
||||
if (
|
||||
source_state := self.hass.states.get(self._sensor_source_id)
|
||||
) is None or source_state.state == STATE_UNAVAILABLE:
|
||||
self._attr_available = False
|
||||
update_state = True
|
||||
else:
|
||||
self._attr_available = True
|
||||
|
||||
if old_state is None or new_state is None:
|
||||
# we can't calculate the elapsed time, so we can't calculate the integral
|
||||
return
|
||||
|
||||
unit = new_state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
|
||||
if unit is not None:
|
||||
new_unit_of_measurement = self._unit(unit)
|
||||
|
@ -235,31 +233,53 @@ class IntegrationSensor(RestoreEntity, SensorEntity):
|
|||
if update_state:
|
||||
self.async_write_ha_state()
|
||||
|
||||
if old_state is None or old_state.state in (
|
||||
STATE_UNKNOWN,
|
||||
STATE_UNAVAILABLE,
|
||||
):
|
||||
return
|
||||
|
||||
try:
|
||||
# integration as the Riemann integral of previous measures.
|
||||
area = Decimal(0)
|
||||
elapsed_time = (
|
||||
new_state.last_updated - old_state.last_updated
|
||||
).total_seconds()
|
||||
|
||||
if self._method == METHOD_TRAPEZOIDAL:
|
||||
if (
|
||||
self._method == METHOD_TRAPEZOIDAL
|
||||
and new_state.state
|
||||
not in (
|
||||
STATE_UNKNOWN,
|
||||
STATE_UNAVAILABLE,
|
||||
)
|
||||
and old_state.state
|
||||
not in (
|
||||
STATE_UNKNOWN,
|
||||
STATE_UNAVAILABLE,
|
||||
)
|
||||
):
|
||||
area = (
|
||||
(Decimal(new_state.state) + Decimal(old_state.state))
|
||||
* Decimal(elapsed_time)
|
||||
/ 2
|
||||
)
|
||||
elif self._method == METHOD_LEFT:
|
||||
elif self._method == METHOD_LEFT and old_state.state not in (
|
||||
STATE_UNKNOWN,
|
||||
STATE_UNAVAILABLE,
|
||||
):
|
||||
area = Decimal(old_state.state) * Decimal(elapsed_time)
|
||||
elif self._method == METHOD_RIGHT:
|
||||
elif self._method == METHOD_RIGHT and new_state.state not in (
|
||||
STATE_UNKNOWN,
|
||||
STATE_UNAVAILABLE,
|
||||
):
|
||||
area = Decimal(new_state.state) * Decimal(elapsed_time)
|
||||
else:
|
||||
_LOGGER.debug(
|
||||
"Could not apply method %s to %s -> %s",
|
||||
self._method,
|
||||
old_state.state,
|
||||
new_state.state,
|
||||
)
|
||||
return
|
||||
|
||||
integral = area / (self._unit_prefix * self._unit_time)
|
||||
_LOGGER.debug(
|
||||
"area = %s, integral = %s state = %s", area, integral, self._state
|
||||
)
|
||||
assert isinstance(integral, Decimal)
|
||||
except ValueError as err:
|
||||
_LOGGER.warning("While calculating integration: %s", err)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue