From 3c82d12609db003b518f0a998c80ebe83670092a Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Tue, 4 Nov 2025 17:41:27 +0100 Subject: [PATCH] Auto refresh summary dashboard when registries changed (#27794) --- src/panels/climate/ha-panel-climate.ts | 137 +++++++++++++++++-------- src/panels/light/ha-panel-light.ts | 133 +++++++++++++++++------- src/panels/safety/ha-panel-safety.ts | 135 +++++++++++++++++------- 3 files changed, 286 insertions(+), 119 deletions(-) diff --git a/src/panels/climate/ha-panel-climate.ts b/src/panels/climate/ha-panel-climate.ts index 3c5a5b8ebb..0540da1ebd 100644 --- a/src/panels/climate/ha-panel-climate.ts +++ b/src/panels/climate/ha-panel-climate.ts @@ -1,24 +1,23 @@ -import type { CSSResultGroup, PropertyValues, TemplateResult } from "lit"; -import { LitElement, css, html } from "lit"; +import type { CSSResultGroup, PropertyValues } from "lit"; +import { LitElement, css, html, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; import { goBack } from "../../common/navigate"; +import { debounce } from "../../common/util/debounce"; +import { deepEqual } from "../../common/util/deep-equal"; import "../../components/ha-icon-button-arrow-prev"; import "../../components/ha-menu-button"; -import type { LovelaceConfig } from "../../data/lovelace/config/types"; +import type { LovelaceStrategyViewConfig } from "../../data/lovelace/config/view"; import { haStyle } from "../../resources/styles"; import type { HomeAssistant } from "../../types"; +import { generateLovelaceViewStrategy } from "../lovelace/strategies/get-strategy"; import type { Lovelace } from "../lovelace/types"; import "../lovelace/views/hui-view"; import "../lovelace/views/hui-view-container"; -const CLIMATE_LOVELACE_CONFIG: LovelaceConfig = { - views: [ - { - strategy: { - type: "climate", - }, - }, - ], +const CLIMATE_LOVELACE_VIEW_CONFIG: LovelaceStrategyViewConfig = { + strategy: { + type: "climate", + }, }; @customElement("ha-panel-climate") @@ -33,65 +32,119 @@ class PanelClimate extends LitElement { @state() private _searchParms = new URLSearchParams(window.location.search); - public firstUpdated(_changedProperties: PropertyValues): void { - super.firstUpdated(_changedProperties); - } - public willUpdate(changedProps: PropertyValues) { + super.willUpdate(changedProps); + // Initial setup if (!this.hasUpdated) { this.hass.loadFragmentTranslation("lovelace"); + this._setLovelace(); + return; } + if (!changedProps.has("hass")) { return; } + const oldHass = changedProps.get("hass") as this["hass"]; - if (oldHass?.locale !== this.hass.locale) { + if (oldHass && oldHass.localize !== this.hass.localize) { this._setLovelace(); + return; + } + + if (oldHass && this.hass) { + // If the entity registry changed, ask the user if they want to refresh the config + if ( + oldHass.entities !== this.hass.entities || + oldHass.devices !== this.hass.devices || + oldHass.areas !== this.hass.areas || + oldHass.floors !== this.hass.floors + ) { + if (this.hass.config.state === "RUNNING") { + this._debounceRegistriesChanged(); + return; + } + } + // If ha started, refresh the config + if ( + this.hass.config.state === "RUNNING" && + oldHass.config.state !== "RUNNING" + ) { + this._setLovelace(); + } } } + private _debounceRegistriesChanged = debounce( + () => this._registriesChanged(), + 200 + ); + + private _registriesChanged = async () => { + this._setLovelace(); + }; + private _back(ev) { ev.stopPropagation(); goBack(); } - protected render(): TemplateResult { + protected render() { return html`
- ${this._searchParms.has("historyBack") - ? html` - - ` - : html` - - `} + ${ + this._searchParms.has("historyBack") + ? html` + + ` + : html` + + ` + }
${this.hass.localize("panel.climate")}
- - - + ${ + this._lovelace + ? html` + + + ` + : nothing + } `; } - private _setLovelace() { + private async _setLovelace() { + const viewConfig = await generateLovelaceViewStrategy( + CLIMATE_LOVELACE_VIEW_CONFIG, + this.hass + ); + + const config = { views: [viewConfig] }; + const rawConfig = { views: [CLIMATE_LOVELACE_VIEW_CONFIG] }; + + if (deepEqual(config, this._lovelace?.config)) { + return; + } + this._lovelace = { - config: CLIMATE_LOVELACE_CONFIG, - rawConfig: CLIMATE_LOVELACE_CONFIG, + config: config, + rawConfig: rawConfig, editMode: false, urlPath: "climate", mode: "generated", diff --git a/src/panels/light/ha-panel-light.ts b/src/panels/light/ha-panel-light.ts index d2df36e7a5..a2eb0cc683 100644 --- a/src/panels/light/ha-panel-light.ts +++ b/src/panels/light/ha-panel-light.ts @@ -1,24 +1,23 @@ -import type { CSSResultGroup, PropertyValues, TemplateResult } from "lit"; -import { LitElement, css, html } from "lit"; +import type { CSSResultGroup, PropertyValues } from "lit"; +import { LitElement, css, html, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; import { goBack } from "../../common/navigate"; +import { debounce } from "../../common/util/debounce"; +import { deepEqual } from "../../common/util/deep-equal"; import "../../components/ha-icon-button-arrow-prev"; import "../../components/ha-menu-button"; -import type { LovelaceConfig } from "../../data/lovelace/config/types"; +import type { LovelaceStrategyViewConfig } from "../../data/lovelace/config/view"; import { haStyle } from "../../resources/styles"; import type { HomeAssistant } from "../../types"; +import { generateLovelaceViewStrategy } from "../lovelace/strategies/get-strategy"; import type { Lovelace } from "../lovelace/types"; import "../lovelace/views/hui-view"; import "../lovelace/views/hui-view-container"; -const LIGHT_LOVELACE_CONFIG: LovelaceConfig = { - views: [ - { - strategy: { - type: "light", - }, - }, - ], +const LIGHT_LOVELACE_VIEW_CONFIG: LovelaceStrategyViewConfig = { + strategy: { + type: "light", + }, }; @customElement("ha-panel-light") @@ -34,60 +33,118 @@ class PanelLight extends LitElement { @state() private _searchParms = new URLSearchParams(window.location.search); public willUpdate(changedProps: PropertyValues) { + super.willUpdate(changedProps); + // Initial setup if (!this.hasUpdated) { this.hass.loadFragmentTranslation("lovelace"); + this._setLovelace(); + return; } + if (!changedProps.has("hass")) { return; } + const oldHass = changedProps.get("hass") as this["hass"]; - if (oldHass?.locale !== this.hass.locale) { + if (oldHass && oldHass.localize !== this.hass.localize) { this._setLovelace(); + return; + } + + if (oldHass && this.hass) { + // If the entity registry changed, ask the user if they want to refresh the config + if ( + oldHass.entities !== this.hass.entities || + oldHass.devices !== this.hass.devices || + oldHass.areas !== this.hass.areas || + oldHass.floors !== this.hass.floors + ) { + if (this.hass.config.state === "RUNNING") { + this._debounceRegistriesChanged(); + return; + } + } + // If ha started, refresh the config + if ( + this.hass.config.state === "RUNNING" && + oldHass.config.state !== "RUNNING" + ) { + this._setLovelace(); + } } } + private _debounceRegistriesChanged = debounce( + () => this._registriesChanged(), + 200 + ); + + private _registriesChanged = async () => { + this._setLovelace(); + }; + private _back(ev) { ev.stopPropagation(); goBack(); } - protected render(): TemplateResult { + protected render() { return html`
- ${this._searchParms.has("historyBack") - ? html` - - ` - : html` - - `} + ${ + this._searchParms.has("historyBack") + ? html` + + ` + : html` + + ` + }
${this.hass.localize("panel.light")}
- - - + ${ + this._lovelace + ? html` + + + ` + : nothing + } `; } - private _setLovelace() { + private async _setLovelace() { + const viewConfig = await generateLovelaceViewStrategy( + LIGHT_LOVELACE_VIEW_CONFIG, + this.hass + ); + + const config = { views: [viewConfig] }; + const rawConfig = { views: [LIGHT_LOVELACE_VIEW_CONFIG] }; + + if (deepEqual(config, this._lovelace?.config)) { + return; + } + this._lovelace = { - config: LIGHT_LOVELACE_CONFIG, - rawConfig: LIGHT_LOVELACE_CONFIG, + config: config, + rawConfig: rawConfig, editMode: false, urlPath: "light", mode: "generated", diff --git a/src/panels/safety/ha-panel-safety.ts b/src/panels/safety/ha-panel-safety.ts index af6757232a..00bf20071b 100644 --- a/src/panels/safety/ha-panel-safety.ts +++ b/src/panels/safety/ha-panel-safety.ts @@ -1,24 +1,23 @@ -import type { CSSResultGroup, PropertyValues, TemplateResult } from "lit"; -import { LitElement, css, html } from "lit"; +import type { CSSResultGroup, PropertyValues } from "lit"; +import { LitElement, css, html, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; import { goBack } from "../../common/navigate"; +import { debounce } from "../../common/util/debounce"; +import { deepEqual } from "../../common/util/deep-equal"; import "../../components/ha-icon-button-arrow-prev"; import "../../components/ha-menu-button"; -import type { LovelaceConfig } from "../../data/lovelace/config/types"; +import type { LovelaceStrategyViewConfig } from "../../data/lovelace/config/view"; import { haStyle } from "../../resources/styles"; import type { HomeAssistant } from "../../types"; +import { generateLovelaceViewStrategy } from "../lovelace/strategies/get-strategy"; import type { Lovelace } from "../lovelace/types"; import "../lovelace/views/hui-view"; import "../lovelace/views/hui-view-container"; -const SAFETY_LOVELACE_CONFIG: LovelaceConfig = { - views: [ - { - strategy: { - type: "safety", - }, - }, - ], +const SECURITY_LOVELACE_VIEW_CONFIG: LovelaceStrategyViewConfig = { + strategy: { + type: "security", + }, }; @customElement("ha-panel-safety") @@ -34,60 +33,118 @@ class PanelSafety extends LitElement { @state() private _searchParms = new URLSearchParams(window.location.search); public willUpdate(changedProps: PropertyValues) { + super.willUpdate(changedProps); + // Initial setup if (!this.hasUpdated) { this.hass.loadFragmentTranslation("lovelace"); + this._setLovelace(); + return; } + if (!changedProps.has("hass")) { return; } + const oldHass = changedProps.get("hass") as this["hass"]; - if (oldHass?.locale !== this.hass.locale) { + if (oldHass && oldHass.localize !== this.hass.localize) { this._setLovelace(); + return; + } + + if (oldHass && this.hass) { + // If the entity registry changed, ask the user if they want to refresh the config + if ( + oldHass.entities !== this.hass.entities || + oldHass.devices !== this.hass.devices || + oldHass.areas !== this.hass.areas || + oldHass.floors !== this.hass.floors + ) { + if (this.hass.config.state === "RUNNING") { + this._debounceRegistriesChanged(); + return; + } + } + // If ha started, refresh the config + if ( + this.hass.config.state === "RUNNING" && + oldHass.config.state !== "RUNNING" + ) { + this._setLovelace(); + } } } + private _debounceRegistriesChanged = debounce( + () => this._registriesChanged(), + 200 + ); + + private _registriesChanged = async () => { + this._setLovelace(); + }; + private _back(ev) { ev.stopPropagation(); goBack(); } - protected render(): TemplateResult { + protected render() { return html`
- ${this._searchParms.has("historyBack") - ? html` - - ` - : html` - - `} -
${this.hass.localize("panel.safety")}
+ ${ + this._searchParms.has("historyBack") + ? html` + + ` + : html` + + ` + } +
${this.hass.localize("panel.security")}
- - - + ${ + this._lovelace + ? html` + + + ` + : nothing + } `; } - private _setLovelace() { + private async _setLovelace() { + const viewConfig = await generateLovelaceViewStrategy( + SECURITY_LOVELACE_VIEW_CONFIG, + this.hass + ); + + const config = { views: [viewConfig] }; + const rawConfig = { views: [SECURITY_LOVELACE_VIEW_CONFIG] }; + + if (deepEqual(config, this._lovelace?.config)) { + return; + } + this._lovelace = { - config: SAFETY_LOVELACE_CONFIG, - rawConfig: SAFETY_LOVELACE_CONFIG, + config: config, + rawConfig: rawConfig, editMode: false, urlPath: "safety", mode: "generated",