diff --git a/src/components/chart/statistics-chart.ts b/src/components/chart/statistics-chart.ts index 0b603dab3c..c2bcca5a4d 100644 --- a/src/components/chart/statistics-chart.ts +++ b/src/components/chart/statistics-chart.ts @@ -197,6 +197,7 @@ class StatisticsChart extends LitElement { elements: { line: { tension: 0.4, + cubicInterpolationMode: "monotone", borderWidth: 1.5, }, bar: { borderWidth: 1.5, borderRadius: 4 }, @@ -280,33 +281,38 @@ class StatisticsChart extends LitElement { // array containing [value1, value2, etc] let prevValues: Array | null = null; + let prevEndTime: Date | undefined; // The datasets for the current statistic const statDataSets: ChartDataset<"line">[] = []; const pushData = ( - timestamp: Date, + start: Date, + end: Date, dataValues: Array | null ) => { if (!dataValues) return; - if (timestamp > endTime) { + if (start > end) { // Drop data points that are after the requested endTime. This could happen if // endTime is "now" and client time is not in sync with server time. return; } statDataSets.forEach((d, i) => { - if (dataValues[i] === null && prevValues && prevValues[i] !== null) { - // null data values show up as gaps in the chart. - // If the current value for the dataset is null and the previous - // value of the data set is not null, then add an 'end' point - // to the chart for the previous value. Otherwise the gap will - // be too big. It will go from the start of the previous data - // value until the start of the next data value. - d.data.push({ x: timestamp.getTime(), y: prevValues[i]! }); + if ( + prevEndTime && + prevValues && + prevEndTime.getTime() !== start.getTime() + ) { + // if the end of the previous data doesn't match the start of the current data, + // we have to draw a gap so add a value at the end time, and then an empty value. + d.data.push({ x: prevEndTime.getTime(), y: prevValues[i]! }); + // @ts-expect-error + d.data.push({ x: prevEndTime.getTime(), y: null }); } - d.data.push({ x: timestamp.getTime(), y: dataValues[i]! }); + d.data.push({ x: start.getTime(), y: dataValues[i]! }); }); prevValues = dataValues; + prevEndTime = end; }; const color = getGraphColorByIndex(colorIndex, this._computedStyle!); @@ -365,11 +371,11 @@ class StatisticsChart extends LitElement { let firstSum: number | null | undefined = null; let prevSum: number | null | undefined = null; stats.forEach((stat) => { - const date = new Date(stat.start); - if (prevDate === date) { + const startDate = new Date(stat.start); + if (prevDate === startDate) { return; } - prevDate = date; + prevDate = startDate; const dataValues: Array = []; statTypes.forEach((type) => { let val: number | null | undefined; @@ -396,7 +402,7 @@ class StatisticsChart extends LitElement { : null ); }); - pushData(date, dataValues); + pushData(startDate, new Date(stat.end), dataValues); }); // Concat two arrays diff --git a/src/panels/lovelace/cards/hui-statistics-graph-card.ts b/src/panels/lovelace/cards/hui-statistics-graph-card.ts index 640b4cf5ff..6e07eb5dcb 100644 --- a/src/panels/lovelace/cards/hui-statistics-graph-card.ts +++ b/src/panels/lovelace/cards/hui-statistics-graph-card.ts @@ -85,13 +85,7 @@ export class HuiStatisticsGraphCard extends LitElement implements LovelaceCard { if (!this.hasUpdated) { return; } - this._getStatistics(); - // statistics are created every hour - clearInterval(this._interval); - this._interval = window.setInterval( - () => this._getStatistics(), - this._intervalTimeout - ); + this._setFetchStatisticsTimer(); } public getCardSize(): number { @@ -138,10 +132,7 @@ export class HuiStatisticsGraphCard extends LitElement implements LovelaceCard { public willUpdate(changedProps: PropertyValues) { super.willUpdate(changedProps); - if ( - !this._config || - (!changedProps.has("_config") && !changedProps.has("_metadata")) - ) { + if (!this._config || !changedProps.has("_config")) { return; } @@ -153,27 +144,33 @@ export class HuiStatisticsGraphCard extends LitElement implements LovelaceCard { changedProps.has("_config") && oldConfig?.entities !== this._config.entities ) { - this._getStatisticsMetaData(this._entities); + this._getStatisticsMetaData(this._entities).then(() => { + this._setFetchStatisticsTimer(); + }); + return; } if ( - changedProps.has("_metadata") || - (changedProps.has("_config") && - (oldConfig?.entities !== this._config.entities || - oldConfig?.days_to_show !== this._config.days_to_show || - oldConfig?.period !== this._config.period || - oldConfig?.unit !== this._config.unit)) + changedProps.has("_config") && + (oldConfig?.entities !== this._config.entities || + oldConfig?.days_to_show !== this._config.days_to_show || + oldConfig?.period !== this._config.period || + oldConfig?.unit !== this._config.unit) ) { - this._getStatistics(); - // statistics are created every hour - clearInterval(this._interval); - this._interval = window.setInterval( - () => this._getStatistics(), - this._intervalTimeout - ); + this._setFetchStatisticsTimer(); } } + private _setFetchStatisticsTimer() { + this._getStatistics(); + // statistics are created every hour + clearInterval(this._interval); + this._interval = window.setInterval( + () => this._getStatistics(), + this._intervalTimeout + ); + } + protected render(): TemplateResult { if (!this.hass || !this._config) { return html``;