From b2a03ac2014bf91ab88014a3fe34dfcdb710c1f1 Mon Sep 17 00:00:00 2001 From: karwosts Date: Sun, 22 Oct 2023 08:25:27 -0700 Subject: [PATCH 1/2] Chart updates to improve stability, possible fix for infinite loop --- src/components/chart/ha-chart-base.ts | 48 +++++++++++++- src/components/chart/state-history-charts.ts | 66 +++++++++++--------- 2 files changed, 83 insertions(+), 31 deletions(-) diff --git a/src/components/chart/ha-chart-base.ts b/src/components/chart/ha-chart-base.ts index 1d4604fa3b97..8fa3edd81af7 100644 --- a/src/components/chart/ha-chart-base.ts +++ b/src/components/chart/ha-chart-base.ts @@ -12,6 +12,7 @@ import { styleMap } from "lit/directives/style-map"; import { clamp } from "../../common/number/clamp"; import { computeRTL } from "../../common/util/compute_rtl"; import { HomeAssistant } from "../../types"; +import { debounce } from "../../common/util/debounce"; export const MIN_TIME_BETWEEN_UPDATES = 60 * 5 * 1000; @@ -52,6 +53,12 @@ export class HaChartBase extends LitElement { @state() private _hiddenDatasets: Set = new Set(); + private _paddingUpdateCount = 0; + + private _paddingUpdateLock = false; + + private _paddingYAxisInternal = 0; + public disconnectedCallback() { super.disconnectedCallback(); this._releaseCanvas(); @@ -104,9 +111,44 @@ export class HaChartBase extends LitElement { }); } + public shouldUpdate(changedProps: PropertyValues): boolean { + if ( + this._paddingUpdateLock && + changedProps.size === 1 && + changedProps.has("paddingYAxis") + ) { + return false; + } + return true; + } + + private _debouncedClearUpdates = debounce( + () => { + this._paddingUpdateCount = 0; + }, + 2000, + false + ); + public willUpdate(changedProps: PropertyValues): void { super.willUpdate(changedProps); + if (!this._paddingUpdateLock) { + this._paddingYAxisInternal = this.paddingYAxis; + if (changedProps.size === 1 && changedProps.has("paddingYAxis")) { + this._paddingUpdateCount++; + if (this._paddingUpdateCount > 300) { + this._paddingUpdateLock = true; + // eslint-disable-next-line + console.warn( + "Detected excessive chart padding updates, possibly an infinite loop. Disabling axis padding." + ); + } else { + this._debouncedClearUpdates(); + } + } + } + if (!this.hasUpdated || !this.chart) { return; } @@ -171,10 +213,10 @@ export class HaChartBase extends LitElement { this.height ?? this._chartHeight ?? this.clientWidth / 2 }px`, "padding-left": `${ - computeRTL(this.hass) ? 0 : this.paddingYAxis + computeRTL(this.hass) ? 0 : this._paddingYAxisInternal }px`, "padding-right": `${ - computeRTL(this.hass) ? this.paddingYAxis : 0 + computeRTL(this.hass) ? this._paddingYAxisInternal : 0 }px`, })} > @@ -324,7 +366,7 @@ export class HaChartBase extends LitElement { clamp( context.tooltip.caretX, 100, - this.clientWidth - 100 - this.paddingYAxis + this.clientWidth - 100 - this._paddingYAxisInternal ) - 100 + "px", diff --git a/src/components/chart/state-history-charts.ts b/src/components/chart/state-history-charts.ts index 688e32b445ec..24854e8719d1 100644 --- a/src/components/chart/state-history-charts.ts +++ b/src/components/chart/state-history-charts.ts @@ -73,9 +73,9 @@ export class StateHistoryCharts extends LitElement { @property({ type: Boolean }) public isLoadingData = false; - @state() private _computedStartTime!: Date; + private _computedStartTime!: Date; - @state() private _computedEndTime!: Date; + private _computedEndTime!: Date; @state() private _maxYWidth = 0; @@ -114,31 +114,6 @@ export class StateHistoryCharts extends LitElement { ${this.hass.localize("ui.components.history_charts.no_history_found")} `; } - - const now = new Date(); - - this._computedEndTime = - this.upToNow || !this.endTime || this.endTime > now ? now : this.endTime; - - if (this.startTime) { - this._computedStartTime = this.startTime; - } else if (this.hoursToShow) { - this._computedStartTime = new Date( - new Date().getTime() - 60 * 60 * this.hoursToShow * 1000 - ); - } else { - this._computedStartTime = new Date( - this.historyData.timeline.reduce( - (minTime, stateInfo) => - Math.min( - minTime, - new Date(stateInfo.data[0].last_changed).getTime() - ), - new Date().getTime() - ) - ); - } - const combinedItems = this.historyData.timeline.length ? (this.virtualize ? chunkData(this.historyData.timeline, CANVAS_TIMELINE_ROWS_CHUNK) @@ -220,10 +195,45 @@ export class StateHistoryCharts extends LitElement { return true; } - protected willUpdate() { + protected willUpdate(changedProps: PropertyValues) { if (!this.hasUpdated) { loadVirtualizer(); } + if ( + [...changedProps.keys()].some( + (prop) => + !( + ["_maxYWidth", "_childYWidths", "_chartCount"] as PropertyKey[] + ).includes(prop) + ) + ) { + // Don't recompute times when we just want to update layout + const now = new Date(); + + this._computedEndTime = + this.upToNow || !this.endTime || this.endTime > now + ? now + : this.endTime; + + if (this.startTime) { + this._computedStartTime = this.startTime; + } else if (this.hoursToShow) { + this._computedStartTime = new Date( + new Date().getTime() - 60 * 60 * this.hoursToShow * 1000 + ); + } else { + this._computedStartTime = new Date( + this.historyData.timeline.reduce( + (minTime, stateInfo) => + Math.min( + minTime, + new Date(stateInfo.data[0].last_changed).getTime() + ), + new Date().getTime() + ) + ); + } + } } protected updated(changedProps: PropertyValues) { From fbcecceeae717ba6413ec19ca47da0792db5f7a4 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Tue, 24 Oct 2023 22:04:27 +0200 Subject: [PATCH 2/2] Update src/components/chart/ha-chart-base.ts --- src/components/chart/ha-chart-base.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/chart/ha-chart-base.ts b/src/components/chart/ha-chart-base.ts index 8fa3edd81af7..f82305a9834b 100644 --- a/src/components/chart/ha-chart-base.ts +++ b/src/components/chart/ha-chart-base.ts @@ -140,7 +140,7 @@ export class HaChartBase extends LitElement { if (this._paddingUpdateCount > 300) { this._paddingUpdateLock = true; // eslint-disable-next-line - console.warn( + console.error( "Detected excessive chart padding updates, possibly an infinite loop. Disabling axis padding." ); } else {