mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-27 11:16:35 +00:00
Chart updates to improve stability, possible fix for infinite loop (#18329)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
This commit is contained in:
parent
10bcaadcdb
commit
3e6ab8b179
@ -12,6 +12,7 @@ import { styleMap } from "lit/directives/style-map";
|
|||||||
import { clamp } from "../../common/number/clamp";
|
import { clamp } from "../../common/number/clamp";
|
||||||
import { computeRTL } from "../../common/util/compute_rtl";
|
import { computeRTL } from "../../common/util/compute_rtl";
|
||||||
import { HomeAssistant } from "../../types";
|
import { HomeAssistant } from "../../types";
|
||||||
|
import { debounce } from "../../common/util/debounce";
|
||||||
|
|
||||||
export const MIN_TIME_BETWEEN_UPDATES = 60 * 5 * 1000;
|
export const MIN_TIME_BETWEEN_UPDATES = 60 * 5 * 1000;
|
||||||
|
|
||||||
@ -52,6 +53,12 @@ export class HaChartBase extends LitElement {
|
|||||||
|
|
||||||
@state() private _hiddenDatasets: Set<number> = new Set();
|
@state() private _hiddenDatasets: Set<number> = new Set();
|
||||||
|
|
||||||
|
private _paddingUpdateCount = 0;
|
||||||
|
|
||||||
|
private _paddingUpdateLock = false;
|
||||||
|
|
||||||
|
private _paddingYAxisInternal = 0;
|
||||||
|
|
||||||
public disconnectedCallback() {
|
public disconnectedCallback() {
|
||||||
super.disconnectedCallback();
|
super.disconnectedCallback();
|
||||||
this._releaseCanvas();
|
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 {
|
public willUpdate(changedProps: PropertyValues): void {
|
||||||
super.willUpdate(changedProps);
|
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.error(
|
||||||
|
"Detected excessive chart padding updates, possibly an infinite loop. Disabling axis padding."
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
this._debouncedClearUpdates();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!this.hasUpdated || !this.chart) {
|
if (!this.hasUpdated || !this.chart) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -171,10 +213,10 @@ export class HaChartBase extends LitElement {
|
|||||||
this.height ?? this._chartHeight ?? this.clientWidth / 2
|
this.height ?? this._chartHeight ?? this.clientWidth / 2
|
||||||
}px`,
|
}px`,
|
||||||
"padding-left": `${
|
"padding-left": `${
|
||||||
computeRTL(this.hass) ? 0 : this.paddingYAxis
|
computeRTL(this.hass) ? 0 : this._paddingYAxisInternal
|
||||||
}px`,
|
}px`,
|
||||||
"padding-right": `${
|
"padding-right": `${
|
||||||
computeRTL(this.hass) ? this.paddingYAxis : 0
|
computeRTL(this.hass) ? this._paddingYAxisInternal : 0
|
||||||
}px`,
|
}px`,
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
@ -324,7 +366,7 @@ export class HaChartBase extends LitElement {
|
|||||||
clamp(
|
clamp(
|
||||||
context.tooltip.caretX,
|
context.tooltip.caretX,
|
||||||
100,
|
100,
|
||||||
this.clientWidth - 100 - this.paddingYAxis
|
this.clientWidth - 100 - this._paddingYAxisInternal
|
||||||
) -
|
) -
|
||||||
100 +
|
100 +
|
||||||
"px",
|
"px",
|
||||||
|
@ -73,9 +73,9 @@ export class StateHistoryCharts extends LitElement {
|
|||||||
|
|
||||||
@property({ type: Boolean }) public isLoadingData = false;
|
@property({ type: Boolean }) public isLoadingData = false;
|
||||||
|
|
||||||
@state() private _computedStartTime!: Date;
|
private _computedStartTime!: Date;
|
||||||
|
|
||||||
@state() private _computedEndTime!: Date;
|
private _computedEndTime!: Date;
|
||||||
|
|
||||||
@state() private _maxYWidth = 0;
|
@state() private _maxYWidth = 0;
|
||||||
|
|
||||||
@ -114,31 +114,6 @@ export class StateHistoryCharts extends LitElement {
|
|||||||
${this.hass.localize("ui.components.history_charts.no_history_found")}
|
${this.hass.localize("ui.components.history_charts.no_history_found")}
|
||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
const combinedItems = this.historyData.timeline.length
|
||||||
? (this.virtualize
|
? (this.virtualize
|
||||||
? chunkData(this.historyData.timeline, CANVAS_TIMELINE_ROWS_CHUNK)
|
? chunkData(this.historyData.timeline, CANVAS_TIMELINE_ROWS_CHUNK)
|
||||||
@ -220,10 +195,45 @@ export class StateHistoryCharts extends LitElement {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected willUpdate() {
|
protected willUpdate(changedProps: PropertyValues) {
|
||||||
if (!this.hasUpdated) {
|
if (!this.hasUpdated) {
|
||||||
loadVirtualizer();
|
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) {
|
protected updated(changedProps: PropertyValues) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user