diff --git a/src/panels/lovelace/header-footer/hui-graph-header-footer.ts b/src/panels/lovelace/header-footer/hui-graph-header-footer.ts index b289f1a343..543b8aec4f 100644 --- a/src/panels/lovelace/header-footer/hui-graph-header-footer.ts +++ b/src/panels/lovelace/header-footer/hui-graph-header-footer.ts @@ -6,6 +6,7 @@ import { isComponentLoaded } from "../../../common/config/is_component_loaded"; import { fireEvent } from "../../../common/dom/fire_event"; import { computeDomain } from "../../../common/entity/compute_domain"; import "../../../components/ha-spinner"; +import type { HistoryStates } from "../../../data/history"; import { subscribeHistoryStatesTimeWindow } from "../../../data/history"; import type { HomeAssistant } from "../../../types"; import { findEntities } from "../common/find-entities"; @@ -66,6 +67,8 @@ export class HuiGraphHeaderFooter private _error?: string; + private _history?: HistoryStates; + private _interval?: number; private _subscribed?: Promise<(() => Promise) | undefined>; @@ -161,24 +164,8 @@ export class HuiGraphHeaderFooter // Message came in before we had a chance to unload return; } - const width = this.clientWidth || this.offsetWidth; - // sample to 1 point per hour or 1 point per 5 pixels - const maxDetails = Math.max( - 10, - this._config.detail! > 1 - ? Math.max(width / 5, this._config.hours_to_show!) - : this._config.hours_to_show! - ); - const useMean = this._config.detail !== 2; - const { points } = coordinatesMinimalResponseCompressedState( - combinedHistory[this._config.entity], - width, - width / 5, - maxDetails, - { minY: this._config.limits?.min, maxY: this._config.limits?.max }, - useMean - ); - this._coordinates = points; + this._history = combinedHistory; + this._computeCoordinates(); }, this._config.hours_to_show!, [this._config.entity] @@ -190,10 +177,63 @@ export class HuiGraphHeaderFooter this._setRedrawTimer(); } - private _redrawGraph() { - if (this._coordinates) { - this._coordinates = [...this._coordinates]; + private _computeCoordinates() { + if (!this._history || !this._config) { + return; } + const entityHistory = this._history[this._config.entity]; + if (!entityHistory?.length) { + return; + } + const width = this.clientWidth || this.offsetWidth; + // sample to 1 point per hour or 1 point per 5 pixels + const maxDetails = Math.max( + 10, + this._config.detail! > 1 + ? Math.max(width / 5, this._config.hours_to_show!) + : this._config.hours_to_show! + ); + const useMean = this._config.detail !== 2; + const { points } = coordinatesMinimalResponseCompressedState( + entityHistory, + width, + width / 5, + maxDetails, + { minY: this._config.limits?.min, maxY: this._config.limits?.max }, + useMean + ); + this._coordinates = points; + } + + private _redrawGraph() { + if (!this._history || !this._config?.hours_to_show) { + return; + } + const entityId = this._config.entity; + const entityHistory = this._history[entityId]; + if (entityHistory?.length) { + const purgeBeforeTimestamp = + (Date.now() - this._config.hours_to_show * 60 * 60 * 1000) / 1000; + let purgedHistory = entityHistory.filter( + (entry) => entry.lu >= purgeBeforeTimestamp + ); + if (purgedHistory.length !== entityHistory.length) { + if ( + !purgedHistory.length || + purgedHistory[0].lu !== purgeBeforeTimestamp + ) { + // Preserve the last expired state as the start boundary + const lastExpiredState = { + ...entityHistory[entityHistory.length - purgedHistory.length - 1], + }; + lastExpiredState.lu = purgeBeforeTimestamp; + delete lastExpiredState.lc; + purgedHistory = [lastExpiredState, ...purgedHistory]; + } + this._history = { ...this._history, [entityId]: purgedHistory }; + } + } + this._computeCoordinates(); } private _setRedrawTimer() { @@ -211,6 +251,7 @@ export class HuiGraphHeaderFooter this._subscribed.then((unsub) => unsub?.()); this._subscribed = undefined; } + this._history = undefined; } protected updated(changedProps: PropertyValues) {