mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 13:17:32 +00:00
Improve performance of sums in the energy dashboard (#91342)
This commit is contained in:
parent
4e80154ebe
commit
e1a5ad069c
@ -857,10 +857,13 @@ def _reduce_statistics(
|
|||||||
}
|
}
|
||||||
if _want_mean:
|
if _want_mean:
|
||||||
row["mean"] = mean(mean_values) if mean_values else None
|
row["mean"] = mean(mean_values) if mean_values else None
|
||||||
|
mean_values.clear()
|
||||||
if _want_min:
|
if _want_min:
|
||||||
row["min"] = min(min_values) if min_values else None
|
row["min"] = min(min_values) if min_values else None
|
||||||
|
min_values.clear()
|
||||||
if _want_max:
|
if _want_max:
|
||||||
row["max"] = max(max_values) if max_values else None
|
row["max"] = max(max_values) if max_values else None
|
||||||
|
max_values.clear()
|
||||||
if _want_last_reset:
|
if _want_last_reset:
|
||||||
row["last_reset"] = prev_stat.get("last_reset")
|
row["last_reset"] = prev_stat.get("last_reset")
|
||||||
if _want_state:
|
if _want_state:
|
||||||
@ -868,10 +871,6 @@ def _reduce_statistics(
|
|||||||
if _want_sum:
|
if _want_sum:
|
||||||
row["sum"] = prev_stat["sum"]
|
row["sum"] = prev_stat["sum"]
|
||||||
result[statistic_id].append(row)
|
result[statistic_id].append(row)
|
||||||
|
|
||||||
max_values = []
|
|
||||||
mean_values = []
|
|
||||||
min_values = []
|
|
||||||
if _want_max and (_max := statistic.get("max")) is not None:
|
if _want_max and (_max := statistic.get("max")) is not None:
|
||||||
max_values.append(_max)
|
max_values.append(_max)
|
||||||
if _want_mean and (_mean := statistic.get("mean")) is not None:
|
if _want_mean and (_mean := statistic.get("mean")) is not None:
|
||||||
@ -1560,20 +1559,6 @@ def _statistics_during_period_with_session(
|
|||||||
|
|
||||||
if not stats:
|
if not stats:
|
||||||
return {}
|
return {}
|
||||||
# Return statistics combined with metadata
|
|
||||||
if period not in ("day", "week", "month"):
|
|
||||||
return _sorted_statistics_to_dict(
|
|
||||||
hass,
|
|
||||||
session,
|
|
||||||
stats,
|
|
||||||
statistic_ids,
|
|
||||||
metadata,
|
|
||||||
True,
|
|
||||||
table,
|
|
||||||
start_time,
|
|
||||||
units,
|
|
||||||
types,
|
|
||||||
)
|
|
||||||
|
|
||||||
result = _sorted_statistics_to_dict(
|
result = _sorted_statistics_to_dict(
|
||||||
hass,
|
hass,
|
||||||
@ -1588,6 +1573,10 @@ def _statistics_during_period_with_session(
|
|||||||
types,
|
types,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Return statistics combined with metadata
|
||||||
|
if period not in ("day", "week", "month"):
|
||||||
|
return result
|
||||||
|
|
||||||
if period == "day":
|
if period == "day":
|
||||||
return _reduce_statistics_per_day(result, types)
|
return _reduce_statistics_per_day(result, types)
|
||||||
|
|
||||||
@ -1829,7 +1818,34 @@ def _statistics_at_time(
|
|||||||
return cast(Sequence[Row], execute_stmt_lambda_element(session, stmt))
|
return cast(Sequence[Row], execute_stmt_lambda_element(session, stmt))
|
||||||
|
|
||||||
|
|
||||||
def _sorted_statistics_to_dict(
|
def _fast_build_sum_list(
|
||||||
|
stats_list: list[Row],
|
||||||
|
table_duration_seconds: float,
|
||||||
|
convert: Callable | None,
|
||||||
|
start_ts_idx: int,
|
||||||
|
sum_idx: int,
|
||||||
|
) -> list[StatisticsRow]:
|
||||||
|
"""Build a list of sum statistics."""
|
||||||
|
if convert:
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
"start": (start_ts := db_state[start_ts_idx]),
|
||||||
|
"end": start_ts + table_duration_seconds,
|
||||||
|
"sum": convert(db_state[sum_idx]),
|
||||||
|
}
|
||||||
|
for db_state in stats_list
|
||||||
|
]
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
"start": (start_ts := db_state[start_ts_idx]),
|
||||||
|
"end": start_ts + table_duration_seconds,
|
||||||
|
"sum": db_state[sum_idx],
|
||||||
|
}
|
||||||
|
for db_state in stats_list
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def _sorted_statistics_to_dict( # noqa: C901
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
session: Session,
|
session: Session,
|
||||||
stats: Sequence[Row[Any]],
|
stats: Sequence[Row[Any]],
|
||||||
@ -1888,6 +1904,7 @@ def _sorted_statistics_to_dict(
|
|||||||
last_reset_ts_idx = field_map["last_reset_ts"] if "last_reset" in types else None
|
last_reset_ts_idx = field_map["last_reset_ts"] if "last_reset" in types else None
|
||||||
state_idx = field_map["state"] if "state" in types else None
|
state_idx = field_map["state"] if "state" in types else None
|
||||||
sum_idx = field_map["sum"] if "sum" in types else None
|
sum_idx = field_map["sum"] if "sum" in types else None
|
||||||
|
sum_only = len(types) == 1 and sum_idx is not None
|
||||||
# Append all statistic entries, and optionally do unit conversion
|
# Append all statistic entries, and optionally do unit conversion
|
||||||
table_duration_seconds = table.duration.total_seconds()
|
table_duration_seconds = table.duration.total_seconds()
|
||||||
for meta_id, stats_list in stats_by_meta_id.items():
|
for meta_id, stats_list in stats_by_meta_id.items():
|
||||||
@ -1900,6 +1917,23 @@ def _sorted_statistics_to_dict(
|
|||||||
convert = _get_statistic_to_display_unit_converter(unit, state_unit, units)
|
convert = _get_statistic_to_display_unit_converter(unit, state_unit, units)
|
||||||
else:
|
else:
|
||||||
convert = None
|
convert = None
|
||||||
|
|
||||||
|
if sum_only:
|
||||||
|
# This function is extremely flexible and can handle all types of
|
||||||
|
# statistics, but in practice we only ever use a few combinations.
|
||||||
|
#
|
||||||
|
# For energy, we only need sum statistics, so we can optimize
|
||||||
|
# this path to avoid the overhead of the more generic function.
|
||||||
|
assert sum_idx is not None
|
||||||
|
result[statistic_id] = _fast_build_sum_list(
|
||||||
|
stats_list,
|
||||||
|
table_duration_seconds,
|
||||||
|
convert,
|
||||||
|
start_ts_idx,
|
||||||
|
sum_idx,
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
|
||||||
ent_results_append = result[statistic_id].append
|
ent_results_append = result[statistic_id].append
|
||||||
#
|
#
|
||||||
# The below loop is a red hot path for energy, and every
|
# The below loop is a red hot path for energy, and every
|
||||||
|
@ -1223,6 +1223,59 @@ def test_monthly_statistics(
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
stats = statistics_during_period(
|
||||||
|
hass,
|
||||||
|
start_time=zero,
|
||||||
|
statistic_ids=["not", "the", "same", "test:total_energy_import"],
|
||||||
|
period="month",
|
||||||
|
types={"sum"},
|
||||||
|
)
|
||||||
|
sep_start = dt_util.as_utc(dt_util.parse_datetime("2021-09-01 00:00:00"))
|
||||||
|
sep_end = dt_util.as_utc(dt_util.parse_datetime("2021-10-01 00:00:00"))
|
||||||
|
oct_start = dt_util.as_utc(dt_util.parse_datetime("2021-10-01 00:00:00"))
|
||||||
|
oct_end = dt_util.as_utc(dt_util.parse_datetime("2021-11-01 00:00:00"))
|
||||||
|
assert stats == {
|
||||||
|
"test:total_energy_import": [
|
||||||
|
{
|
||||||
|
"start": sep_start.timestamp(),
|
||||||
|
"end": sep_end.timestamp(),
|
||||||
|
"sum": pytest.approx(3.0),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"start": oct_start.timestamp(),
|
||||||
|
"end": oct_end.timestamp(),
|
||||||
|
"sum": pytest.approx(5.0),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
stats = statistics_during_period(
|
||||||
|
hass,
|
||||||
|
start_time=zero,
|
||||||
|
statistic_ids=["not", "the", "same", "test:total_energy_import"],
|
||||||
|
period="month",
|
||||||
|
types={"sum"},
|
||||||
|
units={"energy": "Wh"},
|
||||||
|
)
|
||||||
|
sep_start = dt_util.as_utc(dt_util.parse_datetime("2021-09-01 00:00:00"))
|
||||||
|
sep_end = dt_util.as_utc(dt_util.parse_datetime("2021-10-01 00:00:00"))
|
||||||
|
oct_start = dt_util.as_utc(dt_util.parse_datetime("2021-10-01 00:00:00"))
|
||||||
|
oct_end = dt_util.as_utc(dt_util.parse_datetime("2021-11-01 00:00:00"))
|
||||||
|
assert stats == {
|
||||||
|
"test:total_energy_import": [
|
||||||
|
{
|
||||||
|
"start": sep_start.timestamp(),
|
||||||
|
"end": sep_end.timestamp(),
|
||||||
|
"sum": pytest.approx(3000.0),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"start": oct_start.timestamp(),
|
||||||
|
"end": oct_end.timestamp(),
|
||||||
|
"sum": pytest.approx(5000.0),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
# Use 5minute to ensure table switch works
|
# Use 5minute to ensure table switch works
|
||||||
stats = statistics_during_period(
|
stats = statistics_during_period(
|
||||||
hass,
|
hass,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user