-
-
Notifications
You must be signed in to change notification settings - Fork 37.1k
Add support for total and total_increasing sensor state classes #54523
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
f4d1a90
0ed4d26
b1c3922
96ae8d0
e4b5daf
1e81026
3eb3a37
b04d01d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -17,6 +17,9 @@ | |
| DEVICE_CLASS_PRESSURE, | ||
| DEVICE_CLASS_TEMPERATURE, | ||
| STATE_CLASS_MEASUREMENT, | ||
| STATE_CLASS_TOTAL, | ||
| STATE_CLASS_TOTAL_INCREASING, | ||
| STATE_CLASSES, | ||
| ) | ||
| from homeassistant.const import ( | ||
| ATTR_DEVICE_CLASS, | ||
|
|
@@ -50,15 +53,27 @@ | |
| _LOGGER = logging.getLogger(__name__) | ||
|
|
||
| DEVICE_CLASS_OR_UNIT_STATISTICS = { | ||
| DEVICE_CLASS_BATTERY: {"mean", "min", "max"}, | ||
| DEVICE_CLASS_ENERGY: {"sum"}, | ||
| DEVICE_CLASS_HUMIDITY: {"mean", "min", "max"}, | ||
| DEVICE_CLASS_MONETARY: {"sum"}, | ||
| DEVICE_CLASS_POWER: {"mean", "min", "max"}, | ||
| DEVICE_CLASS_PRESSURE: {"mean", "min", "max"}, | ||
| DEVICE_CLASS_TEMPERATURE: {"mean", "min", "max"}, | ||
| DEVICE_CLASS_GAS: {"sum"}, | ||
| PERCENTAGE: {"mean", "min", "max"}, | ||
| STATE_CLASS_TOTAL: { | ||
| DEVICE_CLASS_ENERGY: {"sum"}, | ||
| DEVICE_CLASS_GAS: {"sum"}, | ||
| DEVICE_CLASS_MONETARY: {"sum"}, | ||
| }, | ||
| STATE_CLASS_MEASUREMENT: { | ||
| DEVICE_CLASS_BATTERY: {"mean", "min", "max"}, | ||
| DEVICE_CLASS_HUMIDITY: {"mean", "min", "max"}, | ||
| DEVICE_CLASS_POWER: {"mean", "min", "max"}, | ||
| DEVICE_CLASS_PRESSURE: {"mean", "min", "max"}, | ||
| DEVICE_CLASS_TEMPERATURE: {"mean", "min", "max"}, | ||
| PERCENTAGE: {"mean", "min", "max"}, | ||
| # Deprecated, support will be removed in Home Assistant 2021.10 | ||
| DEVICE_CLASS_ENERGY: {"sum"}, | ||
| DEVICE_CLASS_GAS: {"sum"}, | ||
| DEVICE_CLASS_MONETARY: {"sum"}, | ||
| }, | ||
| STATE_CLASS_TOTAL_INCREASING: { | ||
| DEVICE_CLASS_ENERGY: {"sum"}, | ||
| DEVICE_CLASS_GAS: {"sum"}, | ||
| }, | ||
| } | ||
|
|
||
| # Normalized units which will be stored in the statistics table | ||
|
|
@@ -109,24 +124,28 @@ | |
| WARN_UNSUPPORTED_UNIT = set() | ||
|
|
||
|
|
||
| def _get_entities(hass: HomeAssistant) -> list[tuple[str, str]]: | ||
| """Get (entity_id, device_class) of all sensors for which to compile statistics.""" | ||
| def _get_entities(hass: HomeAssistant) -> list[tuple[str, str, str]]: | ||
| """Get (entity_id, state_class, key) of all sensors for which to compile statistics. | ||
|
|
||
| Key is either a device class or a unit and is used to index the | ||
| DEVICE_CLASS_OR_UNIT_STATISTICS map. | ||
| """ | ||
| all_sensors = hass.states.all(DOMAIN) | ||
| entity_ids = [] | ||
|
|
||
| for state in all_sensors: | ||
| if state.attributes.get(ATTR_STATE_CLASS) != STATE_CLASS_MEASUREMENT: | ||
| if (state_class := state.attributes.get(ATTR_STATE_CLASS)) not in STATE_CLASSES: | ||
| continue | ||
|
|
||
| if ( | ||
| key := state.attributes.get(ATTR_DEVICE_CLASS) | ||
| ) in DEVICE_CLASS_OR_UNIT_STATISTICS: | ||
| entity_ids.append((state.entity_id, key)) | ||
| ) in DEVICE_CLASS_OR_UNIT_STATISTICS[state_class]: | ||
| entity_ids.append((state.entity_id, state_class, key)) | ||
|
|
||
| if ( | ||
| key := state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) | ||
| ) in DEVICE_CLASS_OR_UNIT_STATISTICS: | ||
| entity_ids.append((state.entity_id, key)) | ||
| ) in DEVICE_CLASS_OR_UNIT_STATISTICS[state_class]: | ||
| entity_ids.append((state.entity_id, state_class, key)) | ||
|
|
||
| return entity_ids | ||
|
|
||
|
|
@@ -228,8 +247,8 @@ def compile_statistics( | |
| hass, start - datetime.timedelta.resolution, end, [i[0] for i in entities] | ||
| ) | ||
|
|
||
| for entity_id, key in entities: | ||
| wanted_statistics = DEVICE_CLASS_OR_UNIT_STATISTICS[key] | ||
| for entity_id, state_class, key in entities: | ||
| wanted_statistics = DEVICE_CLASS_OR_UNIT_STATISTICS[state_class][key] | ||
|
|
||
| if entity_id not in history_list: | ||
| continue | ||
|
|
@@ -272,9 +291,28 @@ def compile_statistics( | |
|
|
||
| for fstate, state in fstates: | ||
|
|
||
| if "last_reset" not in state.attributes: | ||
| # Deprecated, will be removed in Home Assistant 2021.10 | ||
| if ( | ||
| "last_reset" not in state.attributes | ||
| and state_class == STATE_CLASS_MEASUREMENT | ||
| ): | ||
| continue | ||
| if (last_reset := state.attributes["last_reset"]) != old_last_reset: | ||
|
|
||
| reset = False | ||
| if ( | ||
| state_class != STATE_CLASS_TOTAL_INCREASING | ||
| and (last_reset := state.attributes.get("last_reset")) | ||
| != old_last_reset | ||
| ): | ||
| reset = True | ||
| elif old_state is None and last_reset is None: | ||
| reset = True | ||
| elif state_class == STATE_CLASS_TOTAL_INCREASING and ( | ||
| old_state is None or fstate < old_state | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Might be worth usings omething like
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think that needs to be handled by the integration if it claims
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agreed. I think it's best that the integration claiming that the sensor will only increase ensures that is true. I thought of some others where a less than could be an issue but in all cases the integration should know what it's doing better |
||
| ): | ||
| reset = True | ||
|
|
||
| if reset: | ||
| # The sensor has been reset, update the sum | ||
| if old_state is not None: | ||
| _sum += new_state - old_state | ||
|
|
@@ -285,14 +323,21 @@ def compile_statistics( | |
| else: | ||
| new_state = fstate | ||
|
|
||
| if last_reset is None or new_state is None or old_state is None: | ||
| # Deprecated, will be removed in Home Assistant 2021.10 | ||
| if last_reset is None and state_class == STATE_CLASS_MEASUREMENT: | ||
| # No valid updates | ||
| result.pop(entity_id) | ||
| continue | ||
|
|
||
| if new_state is None or old_state is None: | ||
| # No valid updates | ||
| result.pop(entity_id) | ||
| continue | ||
|
|
||
| # Update the sum with the last state | ||
| _sum += new_state - old_state | ||
| stat["last_reset"] = dt_util.parse_datetime(last_reset) | ||
| if last_reset is not None: | ||
| stat["last_reset"] = dt_util.parse_datetime(last_reset) | ||
| stat["sum"] = _sum | ||
| stat["state"] = new_state | ||
|
|
||
|
|
@@ -307,8 +352,8 @@ def list_statistic_ids(hass: HomeAssistant, statistic_type: str | None = None) - | |
|
|
||
| statistic_ids = {} | ||
|
|
||
| for entity_id, key in entities: | ||
| provided_statistics = DEVICE_CLASS_OR_UNIT_STATISTICS[key] | ||
| for entity_id, state_class, key in entities: | ||
| provided_statistics = DEVICE_CLASS_OR_UNIT_STATISTICS[state_class][key] | ||
|
|
||
| if statistic_type is not None and statistic_type not in provided_statistics: | ||
| continue | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not needed, L162 will not continue if this is
NoneThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, added to #54624