diff --git a/src/components/chart/statistics-chart.ts b/src/components/chart/statistics-chart.ts index e5f60ad4e0..ea566c9fc7 100644 --- a/src/components/chart/statistics-chart.ts +++ b/src/components/chart/statistics-chart.ts @@ -30,6 +30,15 @@ import { import type { HomeAssistant } from "../../types"; import "./ha-chart-base"; +export type ExtendedStatisticType = StatisticType | "state"; + +export const statTypeMap: Record = { + mean: "mean", + min: "min", + max: "max", + sum: "sum", + state: "sum", +}; @customElement("statistics-chart") class StatisticsChart extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; @@ -42,7 +51,7 @@ class StatisticsChart extends LitElement { @property({ attribute: false }) public endTime?: Date; - @property({ type: Array }) public statTypes: Array = [ + @property({ type: Array }) public statTypes: Array = [ "sum", "min", "mean", @@ -307,7 +316,7 @@ class StatisticsChart extends LitElement { : this.statTypes; sortedTypes.forEach((type) => { - if (statisticsHaveType(stats, type)) { + if (statisticsHaveType(stats, statTypeMap[type])) { const band = drawBands && (type === "min" || type === "max"); statTypes.push(type); statDataSets.push({ @@ -335,7 +344,6 @@ class StatisticsChart extends LitElement { let prevDate: Date | null = null; // Process chart data. - let initVal: number | null = null; let prevSum: number | null = null; stats.forEach((stat) => { const date = new Date(stat.start); @@ -347,11 +355,11 @@ class StatisticsChart extends LitElement { statTypes.forEach((type) => { let val: number | null; if (type === "sum") { - if (initVal === null) { - initVal = val = stat.state || 0; + if (prevSum === null) { + val = 0; prevSum = stat.sum; } else { - val = initVal + ((stat.sum || 0) - prevSum!); + val = (stat.sum || 0) - prevSum; } } else { val = stat[type]; diff --git a/src/data/recorder.ts b/src/data/recorder.ts index f98c5b75a8..42f9ad5342 100644 --- a/src/data/recorder.ts +++ b/src/data/recorder.ts @@ -1,7 +1,7 @@ import { computeStateName } from "../common/entity/compute_state_name"; import { HomeAssistant } from "../types"; -export type StatisticType = "sum" | "min" | "max" | "mean"; +export type StatisticType = "state" | "sum" | "min" | "max" | "mean"; export interface Statistics { [statisticId: string]: StatisticValue[]; diff --git a/src/panels/lovelace/cards/hui-statistics-graph-card.ts b/src/panels/lovelace/cards/hui-statistics-graph-card.ts index 4a6f4d39c7..47f737927a 100644 --- a/src/panels/lovelace/cards/hui-statistics-graph-card.ts +++ b/src/panels/lovelace/cards/hui-statistics-graph-card.ts @@ -38,8 +38,6 @@ export class HuiStatisticsGraphCard extends LitElement implements LovelaceCard { private _names: Record = {}; - private _fetching = false; - private _interval?: number; public disconnectedCallback() { @@ -92,7 +90,10 @@ export class HuiStatisticsGraphCard extends LitElement implements LovelaceCard { if (typeof config.stat_types === "string") { this._config = { ...config, stat_types: [config.stat_types] }; } else if (!config.stat_types) { - this._config = { ...config, stat_types: ["sum", "min", "max", "mean"] }; + this._config = { + ...config, + stat_types: ["state", "sum", "min", "max", "mean"], + }; } else { this._config = config; } @@ -156,15 +157,11 @@ export class HuiStatisticsGraphCard extends LitElement implements LovelaceCard { } private async _getStatistics(): Promise { - if (this._fetching) { - return; - } const startDate = new Date(); startDate.setTime( startDate.getTime() - 1000 * 60 * 60 * (24 * (this._config!.days_to_show || 30) + 1) ); - this._fetching = true; try { this._statistics = await fetchStatistics( this.hass!, @@ -173,8 +170,8 @@ export class HuiStatisticsGraphCard extends LitElement implements LovelaceCard { this._entities, this._config!.period ); - } finally { - this._fetching = false; + } catch (err) { + this._statistics = undefined; } } diff --git a/src/panels/lovelace/editor/config-elements/hui-statistics-graph-card-editor.ts b/src/panels/lovelace/editor/config-elements/hui-statistics-graph-card-editor.ts index 1dd5f3d8dc..606b1d2166 100644 --- a/src/panels/lovelace/editor/config-elements/hui-statistics-graph-card-editor.ts +++ b/src/panels/lovelace/editor/config-elements/hui-statistics-graph-card-editor.ts @@ -36,8 +36,11 @@ import { statisticsMetaHasType, } from "../../../../data/recorder"; import { deepEqual } from "../../../../common/util/deep-equal"; +import { statTypeMap } from "../../../../components/chart/statistics-chart"; +import { ensureArray } from "../../../../common/ensure-array"; const statTypeStruct = union([ + literal("state"), literal("sum"), literal("min"), literal("max"), @@ -64,7 +67,7 @@ const cardConfigStruct = assign( ); const periods = ["5minute", "hour", "day", "month"] as const; -const stat_types = ["mean", "min", "max", "sum"] as const; +const stat_types = ["mean", "min", "max", "sum", "state"] as const; @customElement("hui-statistics-graph-card-editor") export class HuiStatisticsGraphCardEditor @@ -156,7 +159,7 @@ export class HuiStatisticsGraphCardEditor disabled: !metaDatas || !metaDatas?.every((metaData) => - statisticsMetaHasType(metaData, stat_type) + statisticsMetaHasType(metaData, statTypeMap[stat_type]) ), })), }, @@ -190,7 +193,11 @@ export class HuiStatisticsGraphCardEditor ? Array.isArray(this._config!.stat_types) ? this._config!.stat_types : [this._config!.stat_types] - : stat_types; + : stat_types.filter((stat_type) => + this._metaDatas?.every((metaData) => + statisticsMetaHasType(metaData, statTypeMap[stat_type]) + ) + ); const data = { chart_type: "line", period: "hour", @@ -230,9 +237,27 @@ export class HuiStatisticsGraphCardEditor fireEvent(this, "config-changed", { config: ev.detail.value }); } - private _entitiesChanged(ev: CustomEvent): void { + private async _entitiesChanged(ev: CustomEvent): Promise { + const config = { ...this._config!, entities: ev.detail.value }; + if ( + config.entities?.some((statistic_id) => statistic_id.includes(":")) && + config.period === "5minute" + ) { + delete config.period; + } + if (config.stat_types && config.entities.length) { + const metadata = await getStatisticMetadata(this.hass!, config.entities); + config.stat_types = ensureArray(config.stat_types).filter((stat_type) => + metadata.every((metaData) => + statisticsMetaHasType(metaData, statTypeMap[stat_type]) + ) + ); + if (!config.stat_types.length) { + delete config.stat_types; + } + } fireEvent(this, "config-changed", { - config: { ...this._config!, entities: ev.detail.value }, + config, }); } diff --git a/src/translations/en.json b/src/translations/en.json index 9bca98f512..75e0a794af 100755 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -517,6 +517,7 @@ "min": "min", "max": "max", "mean": "mean", + "state": "state", "sum": "sum" } }, @@ -3969,7 +3970,8 @@ "mean": "Mean", "min": "Min", "max": "Max", - "sum": "Sum" + "state": "State", + "sum": "Sum (change during period)" }, "chart_type": "Chart type", "periods": {