From e21f7baa93ddfe9850d7496ef6929192077e6f04 Mon Sep 17 00:00:00 2001 From: Tom Carpenter Date: Fri, 13 Mar 2026 16:15:53 +0000 Subject: [PATCH] Support Energy Collections in Statistics Graph Card Visual Editor (#29628) * Add date selection keys to statistics-graph editor Add support for the `energy_date_selection` and `collection_key` entires in the visual statistics-graph card editor, along with validation of the collection key in the statistics-graph's setConfig. * Add missing option to stats card editor Was missing the expand_legend config option in the visual editor. * Tidy statistics graph editor arrangement 1. Group the various settings cleanly. 2. Auto-hide the collections key if energy date picker selection is disabled 3. Auto-hide days to show if a collection is linked * Add "auto" option for statistics-chart period When using the energy date picker option, enable the ability to select a period of "auto" which will enable use of the energy systems automatic period selection function. * Correct hiding days to show Should be hidden whenever energy_date_selection is true, regardless of collection key to cope with upgrading existing cards for which no key has been set. * Hide "auto" period on statistics graph card editor When not using energy, hide the auto option to avoid confusion. * Swap date selection and collection key order This keeps the toggle in a consistent location. * Remove duplicate config key There is a title? key now in EnergyCardBaseConfig. * Correct energy_date_selection translation * Improve collection key description * Improve terminology for energy card collection --- .../cards/hui-statistics-graph-card.ts | 18 +- src/panels/lovelace/cards/types.ts | 9 +- .../hui-statistics-graph-card-editor.ts | 214 ++++++++++++------ src/translations/en.json | 14 +- 4 files changed, 168 insertions(+), 87 deletions(-) diff --git a/src/panels/lovelace/cards/hui-statistics-graph-card.ts b/src/panels/lovelace/cards/hui-statistics-graph-card.ts index 4eb32f77e6..100ffa64d6 100644 --- a/src/panels/lovelace/cards/hui-statistics-graph-card.ts +++ b/src/panels/lovelace/cards/hui-statistics-graph-card.ts @@ -11,6 +11,7 @@ import "../../../components/ha-tooltip"; import { getEnergyDataCollection, getSuggestedPeriod, + validateEnergyCollectionKey, } from "../../../data/energy"; import type { Statistics, @@ -157,6 +158,10 @@ export class HuiStatisticsGraphCard extends LitElement implements LovelaceCard { throw new Error("You must include at least one entity"); } + if (config.energy_date_selection && config.collection_key) { + validateEnergyCollectionKey(config.collection_key); + } + this._entities = config.entities ? processConfigEntities(config.entities, false) : []; @@ -265,12 +270,13 @@ export class HuiStatisticsGraphCard extends LitElement implements LovelaceCard { } private get _period() { - return ( - this._config?.period ?? - (this._energyStart && this._energyEnd - ? getSuggestedPeriod(this._energyStart, this._energyEnd) - : undefined) - ); + const period = this._config?.period; + const autoMode = period === "auto"; + return this._energyStart && this._energyEnd && (!period || autoMode) + ? getSuggestedPeriod(this._energyStart, this._energyEnd) + : autoMode + ? undefined + : period; } protected render() { diff --git a/src/panels/lovelace/cards/types.ts b/src/panels/lovelace/cards/types.ts index 89b71ae66f..08a6de1b69 100644 --- a/src/panels/lovelace/cards/types.ts +++ b/src/panels/lovelace/cards/types.ts @@ -4,7 +4,11 @@ import type { HaDurationData } from "../../../components/ha-duration-input"; import type { EnergySourceByType } from "../../../data/energy"; import type { ActionConfig } from "../../../data/lovelace/config/action"; import type { LovelaceCardConfig } from "../../../data/lovelace/config/card"; -import type { Statistic, StatisticType } from "../../../data/recorder"; +import type { + Statistic, + StatisticPeriod, + StatisticType, +} from "../../../data/recorder"; import type { MediaSelectorValue } from "../../../data/selector"; import type { TimeFormat } from "../../../data/translation"; import type { ForecastType } from "../../../data/weather"; @@ -458,11 +462,10 @@ export interface HistoryGraphCardConfig extends LovelaceCardConfig { } export interface StatisticsGraphCardConfig extends EnergyCardBaseConfig { - title?: string; entities: (EntityConfig | string)[]; unit?: string; days_to_show?: number; - period?: "5minute" | "hour" | "day" | "month"; + period?: "auto" | StatisticPeriod; stat_types?: StatisticType | StatisticType[]; chart_type?: "line" | "bar"; min_y_axis?: number; 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 7416f7bf34..4baa2e90b9 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 @@ -63,20 +63,26 @@ const cardConfigStruct = assign( literal("week"), literal("month"), literal("year"), + literal("auto"), ]) ), chart_type: optional(union([literal("bar"), literal("line")])), stat_types: optional(union([array(statTypeStruct), statTypeStruct])), unit: optional(string()), hide_legend: optional(boolean()), + expand_legend: optional(boolean()), logarithmic_scale: optional(boolean()), min_y_axis: optional(number()), max_y_axis: optional(number()), fit_y_data: optional(boolean()), + energy_date_selection: optional(boolean()), + collection_key: optional(string()), }) ); const periods = ["5minute", "hour", "day", "week", "month", "year"] as const; +const energyPeriods = [...periods, "auto"] as const; + const stat_types = [ "mean", "min", @@ -131,7 +137,9 @@ export class HuiStatisticsGraphCardEditor localize: LocalizeFunc, statisticIds: string[] | undefined, metaDatas: StatisticsMetaData[] | undefined, - showFitOption: boolean + showFitOption: boolean, + hiddenLegend: boolean, + enableDateSelect: boolean ) => { const units = new Set(); metaDatas?.forEach((metaData) => { @@ -150,31 +158,89 @@ export class HuiStatisticsGraphCardEditor name: "", type: "grid", schema: [ + { + name: "", + type: "grid", + schema: [ + { + name: "chart_type", + required: true, + type: "select", + options: [ + [ + "line", + localize( + `ui.panel.lovelace.editor.card.statistics-graph.chart_type_labels.line` + ), + ], + [ + "bar", + localize( + `ui.panel.lovelace.editor.card.statistics-graph.chart_type_labels.bar` + ), + ], + ], + }, + ...(!enableDateSelect + ? ([ + { + name: "days_to_show", + default: DEFAULT_DAYS_TO_SHOW, + selector: { number: { min: 1, mode: "box" } }, + }, + ] as HaFormSchema[]) + : []), + ], + }, { name: "period", required: true, selector: { select: { - options: periods.map((period) => ({ - value: period, - label: localize( - `ui.panel.lovelace.editor.card.statistics-graph.periods.${period}` - ), - disabled: - period === "5minute" && - // External statistics don't support 5-minute statistics. - statisticIds?.some((statistic_id) => - isExternalStatistic(statistic_id) + mode: "list", + options: (enableDateSelect ? energyPeriods : periods).map( + (period) => ({ + value: period, + label: localize( + `ui.panel.lovelace.editor.card.statistics-graph.periods.${period}` ), - })), + disabled: + // External statistics don't support 5-minute statistics. + period === "5minute" && + statisticIds?.some((statistic_id) => + isExternalStatistic(statistic_id) + ), + }) + ), }, }, }, + ], + }, + { + name: "", + type: "grid", + schema: [ + ...(enableDateSelect + ? ([ + { + type: "string", + name: "collection_key", + required: false, + }, + ] as HaFormSchema[]) + : []), { - name: "days_to_show", - default: DEFAULT_DAYS_TO_SHOW, - selector: { number: { min: 1, mode: "box" } }, + name: "energy_date_selection", + required: false, + selector: { boolean: {} }, }, + ], + }, + { + name: "", + type: "grid", + schema: [ { name: "stat_types", required: true, @@ -199,25 +265,6 @@ export class HuiStatisticsGraphCardEditor }, }, }, - { - name: "chart_type", - required: true, - type: "select", - options: [ - [ - "line", - localize( - `ui.panel.lovelace.editor.card.statistics-graph.chart_type_labels.line` - ), - ], - [ - "bar", - localize( - `ui.panel.lovelace.editor.card.statistics-graph.chart_type_labels.bar` - ), - ], - ], - }, { name: "", type: "grid", @@ -232,48 +279,56 @@ export class HuiStatisticsGraphCardEditor required: false, selector: { number: { mode: "box", step: "any" } }, }, + ...(showFitOption + ? [ + { + name: "fit_y_data", + required: false, + selector: { boolean: {} }, + }, + ] + : []), + { + name: "logarithmic_scale", + required: false, + selector: { boolean: {} }, + }, + { + name: "hide_legend", + required: false, + selector: { boolean: {} }, + }, + ...(!hiddenLegend + ? [ + { + name: "expand_legend", + required: false, + selector: { boolean: {} }, + }, + ] + : []), ], }, - - ...(showFitOption - ? [ - { - name: "fit_y_data", - required: false, - selector: { boolean: {} }, - }, - ] - : []), - - { - name: "hide_legend", - required: false, - selector: { boolean: {} }, - }, - { - name: "logarithmic_scale", - required: false, - selector: { boolean: {} }, - }, ], }, + ...(units.size > 1 + ? [ + { + name: "unit", + required: false, + selector: { + select: { + options: Array.from(units).map((unit) => ({ + value: unit, + label: unit, + })), + }, + }, + }, + ] + : []), ]; - if (units.size > 1) { - (schema[1] as any).schema.push({ - name: "unit", - required: false, - selector: { - select: { - options: Array.from(units).map((unit) => ({ - value: unit, - label: unit, - })), - }, - }, - }); - } - return schema; } ); @@ -288,7 +343,9 @@ export class HuiStatisticsGraphCardEditor this._configEntities, this._metaDatas, this._config!.min_y_axis !== undefined || - this._config!.max_y_axis !== undefined + this._config!.max_y_axis !== undefined, + !!this._config!.hide_legend, + !!this._config!.energy_date_selection ); const configured_stat_types = this._config!.stat_types ? ensureArray(this._config.stat_types) @@ -299,7 +356,7 @@ export class HuiStatisticsGraphCardEditor ); const data = { chart_type: "line", - period: "hour", + period: this._config!.energy_date_selection ? "auto" : "hour", ...this._config, stat_types: configured_stat_types, }; @@ -314,6 +371,7 @@ export class HuiStatisticsGraphCardEditor .data=${data} .schema=${schema} .computeLabel=${this._computeLabelCallback} + .computeHelper=${this._computeHelperCallback} @value-changed=${this._valueChanged} > { + switch (schema.name) { + case "collection_key": + return this.hass!.localize( + `ui.panel.lovelace.editor.card.generic.collection_key_description` + ); + default: + return undefined; + } + }; + private _computeLabelCallback = (schema) => { switch (schema.name) { case "chart_type": @@ -391,6 +460,7 @@ export class HuiStatisticsGraphCardEditor case "period": case "unit": case "hide_legend": + case "expand_legend": case "logarithmic_scale": case "min_y_axis": case "max_y_axis": diff --git a/src/translations/en.json b/src/translations/en.json index 2574cd92cc..858bba2c5d 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -8960,16 +8960,18 @@ "bar": "Bar" }, "periods": { + "auto": "Auto", + "5minute": "5 minutes", "hour": "Hour", "day": "Day", - "month": "Month", "week": "Week", - "year": "Year", - "5minute": "5 minutes" + "month": "Month", + "year": "Year" }, "pick_statistic": "Add a statistic", "picked_statistic": "Statistic", "hide_legend": "Hide legend", + "expand_legend": "Expanded legend", "logarithmic_scale": "Logarithmic scale", "min_y_axis": "Y axis minimum", "max_y_axis": "Y axis maximum", @@ -9033,9 +9035,9 @@ "auto": "Auto", "live": "Live" }, - "energy_date_selection": "Link to energy date selector", - "collection_key": "Energy collection name", - "collection_key_description": "Optionally connect a collection of date selection, statistics graphs and energy cards. All cards with matching key will respond to the connected date selection card. Any card on this dashboard with no key will automatically be linked together.", + "energy_date_selection": "Link to energy card collection", + "collection_key": "Energy card collection key", + "collection_key_description": "Optionally connect a collection of date picker, statistics graph and energy cards. Cards with matching key will respond to any connected date picker card. Any energy card on this dashboard with no key will automatically be linked together.", "double_tap_action": "Double tap behavior", "entities": "Entities", "entity": "Entity",