From d919e8d33322451be19d6d518c5ea1bc40523cf4 Mon Sep 17 00:00:00 2001 From: karwosts <32912880+karwosts@users.noreply.github.com> Date: Thu, 6 Feb 2025 22:29:28 -0800 Subject: [PATCH] Integrate Statistic Card with Energy Date Picker (#23794) * Support energy-date-selection for statistic card * reuse period key --- .../lovelace/cards/hui-statistic-card.ts | 111 ++++++++++++++++-- src/panels/lovelace/cards/types.ts | 12 +- .../hui-statistic-card-editor.ts | 1 + 3 files changed, 109 insertions(+), 15 deletions(-) diff --git a/src/panels/lovelace/cards/hui-statistic-card.ts b/src/panels/lovelace/cards/hui-statistic-card.ts index 7fafcf8a7d..eebe867bbf 100644 --- a/src/panels/lovelace/cards/hui-statistic-card.ts +++ b/src/panels/lovelace/cards/hui-statistic-card.ts @@ -1,4 +1,4 @@ -import type { HassEntity } from "home-assistant-js-websocket"; +import type { HassEntity, UnsubscribeFunc } from "home-assistant-js-websocket"; import type { CSSResultGroup, PropertyValues } from "lit"; import { LitElement, css, html, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; @@ -9,6 +9,7 @@ import { formatNumber } from "../../../common/number/format_number"; import "../../../components/ha-alert"; import "../../../components/ha-card"; import "../../../components/ha-state-icon"; +import { getEnergyDataCollection } from "../../../data/energy"; import type { StatisticsMetaData } from "../../../data/recorder"; import { fetchStatistic, @@ -31,6 +32,8 @@ import type { import type { HuiErrorCard } from "./hui-error-card"; import type { EntityCardConfig, StatisticCardConfig } from "./types"; +export const PERIOD_ENERGY = "energy_date_selection"; + @customElement("hui-statistic-card") export class HuiStatisticCard extends LitElement implements LovelaceCard { public static async getConfigElement(): Promise { @@ -70,15 +73,52 @@ export class HuiStatisticCard extends LitElement implements LovelaceCard { @state() private _error?: string; + private _energySub?: UnsubscribeFunc; + + @state() private _energyStart?: Date; + + @state() private _energyEnd?: Date; + private _interval?: number; private _footerElement?: HuiErrorCard | LovelaceHeaderFooter; public disconnectedCallback() { super.disconnectedCallback(); + this._unsubscribeEnergy(); clearInterval(this._interval); } + public connectedCallback() { + super.connectedCallback(); + if (this._config?.period === PERIOD_ENERGY) { + this._subscribeEnergy(); + } else { + this._setFetchStatisticTimer(); + } + } + + private _subscribeEnergy() { + if (!this._energySub) { + this._energySub = getEnergyDataCollection(this.hass!, { + key: this._config?.collection_key, + }).subscribe((data) => { + this._energyStart = data.start; + this._energyEnd = data.end; + this._fetchStatistic(); + }); + } + } + + private _unsubscribeEnergy() { + if (this._energySub) { + this._energySub(); + this._energySub = undefined; + } + this._energyStart = undefined; + this._energyEnd = undefined; + } + public setConfig(config: StatisticCardConfig): void { if (!config.entity) { throw new Error("Entity must be specified"); @@ -99,8 +139,6 @@ export class HuiStatisticCard extends LitElement implements LovelaceCard { this._config = config; this._error = undefined; - this._fetchStatistic(); - this._fetchMetadata(); if (this._config.footer) { this._footerElement = createHeaderFooterElement(this._config.footer); @@ -174,7 +212,9 @@ export class HuiStatisticCard extends LitElement implements LovelaceCard { if ( changedProps.has("_value") || changedProps.has("_metadata") || - changedProps.has("_error") + changedProps.has("_error") || + changedProps.has("_energyStart") || + changedProps.has("_energyEnd") ) { return true; } @@ -184,6 +224,46 @@ export class HuiStatisticCard extends LitElement implements LovelaceCard { return true; } + protected willUpdate(changedProps: PropertyValues) { + super.willUpdate(changedProps); + if (!this._config || !changedProps.has("_config")) { + return; + } + const oldConfig = changedProps.get("_config") as + | StatisticCardConfig + | undefined; + + if (this.hass) { + if (this._config.period === PERIOD_ENERGY && !this._energySub) { + this._subscribeEnergy(); + return; + } + if (this._config.period !== PERIOD_ENERGY && this._energySub) { + this._unsubscribeEnergy(); + this._setFetchStatisticTimer(); + return; + } + if ( + this._config.period === PERIOD_ENERGY && + this._energySub && + changedProps.has("_config") && + oldConfig?.collection_key !== this._config.collection_key + ) { + this._unsubscribeEnergy(); + this._subscribeEnergy(); + } + } + + if ( + changedProps.has("_config") && + oldConfig?.entity !== this._config.entity + ) { + this._fetchMetadata().then(() => { + this._setFetchStatisticTimer(); + }); + } + } + protected firstUpdated() { this._fetchStatistic(); this._fetchMetadata(); @@ -210,20 +290,31 @@ export class HuiStatisticCard extends LitElement implements LovelaceCard { } } + private _setFetchStatisticTimer() { + this._fetchStatistic(); + // statistics are created every hour + clearInterval(this._interval); + if (this._config?.period !== PERIOD_ENERGY) { + this._interval = window.setInterval( + () => this._fetchStatistic(), + 5 * 1000 * 60 + ); + } + } + private async _fetchStatistic() { if (!this.hass || !this._config) { return; } - clearInterval(this._interval); - this._interval = window.setInterval( - () => this._fetchStatistic(), - 5 * 1000 * 60 - ); try { const stats = await fetchStatistic( this.hass, this._config.entity, - this._config.period + this._energyStart && this._energyEnd + ? { fixed_period: { start: this._energyStart, end: this._energyEnd } } + : typeof this._config?.period === "object" + ? this._config?.period + : {} ); this._value = stats[this._config!.stat_type]; this._error = undefined; diff --git a/src/panels/lovelace/cards/types.ts b/src/panels/lovelace/cards/types.ts index a71fe7c226..01ddaf2f53 100644 --- a/src/panels/lovelace/cards/types.ts +++ b/src/panels/lovelace/cards/types.ts @@ -379,11 +379,13 @@ export interface StatisticsGraphCardConfig extends EnergyCardBaseConfig { export interface StatisticCardConfig extends LovelaceCardConfig { name?: string; entities: (EntityConfig | string)[]; - period: { - fixed_period?: { start: string; end: string }; - calendar?: { period: string; offset: number }; - rolling_window?: { duration: HaDurationData; offset: HaDurationData }; - }; + period: + | { + fixed_period?: { start: string; end: string }; + calendar?: { period: string; offset: number }; + rolling_window?: { duration: HaDurationData; offset: HaDurationData }; + } + | "energy_date_selection"; stat_type: keyof Statistic; theme?: string; } diff --git a/src/panels/lovelace/editor/config-elements/hui-statistic-card-editor.ts b/src/panels/lovelace/editor/config-elements/hui-statistic-card-editor.ts index 4702abe338..b44bb8cca1 100644 --- a/src/panels/lovelace/editor/config-elements/hui-statistic-card-editor.ts +++ b/src/panels/lovelace/editor/config-elements/hui-statistic-card-editor.ts @@ -32,6 +32,7 @@ const cardConfigStruct = assign( period: optional(any()), theme: optional(string()), footer: optional(headerFooterConfigStructs), + collection_key: optional(string()), }) );