diff --git a/README.md b/README.md index 866a35a..36dc1cf 100644 --- a/README.md +++ b/README.md @@ -185,6 +185,9 @@ I put a lot of work into making this repo and component available and updated to **max**:\ Maximum value of processed values of source sensors. +**trending_towards**:\ + The predicted value if monitored entities keep their current states for the remainder of the period. Requires "end" configuration variable to be set to actual end of period and not now(). + ## Time periods The `average` integration will execute a measure within a precise time period. You should provide none, only `duration` (when period ends at now) or exactly 2 of the following: diff --git a/custom_components/average/const.py b/custom_components/average/const.py index 622d820..ff2e171 100644 --- a/custom_components/average/const.py +++ b/custom_components/average/const.py @@ -50,6 +50,7 @@ ATTR_COUNT: Final = "count" ATTR_MIN_VALUE: Final = "min_value" ATTR_MAX_VALUE: Final = "max_value" +ATTR_TRENDING_TOWARDS: Final = "trending_towards" # ATTR_TO_PROPERTY: Final = [ ATTR_START, @@ -60,6 +61,7 @@ ATTR_COUNT, ATTR_MAX_VALUE, ATTR_MIN_VALUE, + ATTR_TRENDING_TOWARDS, ] diff --git a/custom_components/average/sensor.py b/custom_components/average/sensor.py index 678d59f..da70109 100644 --- a/custom_components/average/sensor.py +++ b/custom_components/average/sensor.py @@ -66,6 +66,7 @@ ATTR_MIN_VALUE, ATTR_START, ATTR_TO_PROPERTY, + ATTR_TRENDING_TOWARDS, CONF_DURATION, CONF_END, CONF_PERIOD_KEYS, @@ -152,6 +153,7 @@ class AverageSensor(SensorEntity): ATTR_COUNT, ATTR_MAX_VALUE, ATTR_MIN_VALUE, + ATTR_TRENDING_TOWARDS, } ) @@ -176,11 +178,13 @@ def __init__( self._precision = precision self._undef = undef self._temperature_mode = None + self._actual_end = None self.sources = expand_entity_ids(hass, entity_ids) self.count_sources = len(self.sources) self.available_sources = 0 self.count = 0 + self.trending_towards = None self.min_value = self.max_value = None self._attr_name = name @@ -399,6 +403,8 @@ async def _async_update_period(self): # pylint: disable=too-many-branches if start > end: start, end = end, start + self._actual_end = end + if start > now: # History hasn't been written yet for this period return @@ -453,8 +459,10 @@ async def _async_update_state( p_start, p_end = p_period # Convert times to UTC + now = dt_util.as_utc(now) start = dt_util.as_utc(start) end = dt_util.as_utc(end) + actual_end = dt_util.as_utc(self._actual_end) p_start = dt_util.as_utc(p_start) p_end = dt_util.as_utc(p_end) @@ -462,6 +470,7 @@ async def _async_update_state( now_ts = math.floor(dt_util.as_timestamp(now)) start_ts = math.floor(dt_util.as_timestamp(start)) end_ts = math.floor(dt_util.as_timestamp(end)) + actual_end_ts = math.floor(dt_util.as_timestamp(actual_end)) p_start_ts = math.floor(dt_util.as_timestamp(p_start)) p_end_ts = math.floor(dt_util.as_timestamp(p_end)) @@ -474,6 +483,8 @@ async def _async_update_state( values = [] self.count = 0 self.min_value = self.max_value = None + last_values = [] + # pylint: disable=too-many-nested-blocks for entity_id in self.sources: @@ -489,6 +500,8 @@ async def _async_update_state( value = 0 elapsed = 0 + trending_last_state = None + if self._period is None: # Get current state @@ -549,12 +562,16 @@ async def _async_update_state( elapsed += last_elapsed if elapsed: value /= elapsed + trending_last_state = last_state _LOGGER.debug("Historical average state: %s", value) if isinstance(value, numbers.Number): values.append(value) self.available_sources += 1 + + if isinstance(trending_last_state, numbers.Number): + last_values.append(trending_last_state) if values: self._attr_native_value = round(sum(values) / len(values), self._precision) @@ -563,6 +580,21 @@ async def _async_update_state( else: self._attr_native_value = None + if last_values: + current_average = round( + sum(last_values) / len(last_values), self._precision + ) + if self._precision < 1: + current_average = int(current_average) + part_of_period = (now_ts - start_ts) / (actual_end_ts - start_ts) + to_now = self._attr_native_value * part_of_period + to_end = current_average * (1 - part_of_period) + self.trending_towards = to_now + to_end + else: + self.trending_towards = None + + _LOGGER.debug("Current trend: %s", self.trending_towards) + _LOGGER.debug( "Total average state: %s %s", self._attr_native_value,