Auto refresh summary dashboard when registries changed (#27794)

This commit is contained in:
Paul Bottein
2025-11-04 17:41:27 +01:00
committed by Bram Kragten
parent 15d67997e7
commit 3c82d12609
3 changed files with 286 additions and 119 deletions

View File

@@ -1,24 +1,23 @@
import type { CSSResultGroup, PropertyValues, TemplateResult } from "lit"; import type { CSSResultGroup, PropertyValues } from "lit";
import { LitElement, css, html } from "lit"; import { LitElement, css, html, nothing } from "lit";
import { customElement, property, state } from "lit/decorators"; import { customElement, property, state } from "lit/decorators";
import { goBack } from "../../common/navigate"; 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-icon-button-arrow-prev";
import "../../components/ha-menu-button"; 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 { haStyle } from "../../resources/styles";
import type { HomeAssistant } from "../../types"; import type { HomeAssistant } from "../../types";
import { generateLovelaceViewStrategy } from "../lovelace/strategies/get-strategy";
import type { Lovelace } from "../lovelace/types"; import type { Lovelace } from "../lovelace/types";
import "../lovelace/views/hui-view"; import "../lovelace/views/hui-view";
import "../lovelace/views/hui-view-container"; import "../lovelace/views/hui-view-container";
const CLIMATE_LOVELACE_CONFIG: LovelaceConfig = { const CLIMATE_LOVELACE_VIEW_CONFIG: LovelaceStrategyViewConfig = {
views: [
{
strategy: { strategy: {
type: "climate", type: "climate",
}, },
},
],
}; };
@customElement("ha-panel-climate") @customElement("ha-panel-climate")
@@ -33,33 +32,68 @@ class PanelClimate extends LitElement {
@state() private _searchParms = new URLSearchParams(window.location.search); @state() private _searchParms = new URLSearchParams(window.location.search);
public firstUpdated(_changedProperties: PropertyValues): void {
super.firstUpdated(_changedProperties);
}
public willUpdate(changedProps: PropertyValues) { public willUpdate(changedProps: PropertyValues) {
super.willUpdate(changedProps);
// Initial setup
if (!this.hasUpdated) { if (!this.hasUpdated) {
this.hass.loadFragmentTranslation("lovelace"); this.hass.loadFragmentTranslation("lovelace");
this._setLovelace();
return;
} }
if (!changedProps.has("hass")) { if (!changedProps.has("hass")) {
return; return;
} }
const oldHass = changedProps.get("hass") as this["hass"]; 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(); this._setLovelace();
} }
} }
}
private _debounceRegistriesChanged = debounce(
() => this._registriesChanged(),
200
);
private _registriesChanged = async () => {
this._setLovelace();
};
private _back(ev) { private _back(ev) {
ev.stopPropagation(); ev.stopPropagation();
goBack(); goBack();
} }
protected render(): TemplateResult { protected render() {
return html` return html`
<div class="header"> <div class="header">
<div class="toolbar"> <div class="toolbar">
${this._searchParms.has("historyBack") ${
this._searchParms.has("historyBack")
? html` ? html`
<ha-icon-button-arrow-prev <ha-icon-button-arrow-prev
@click=${this._back} @click=${this._back}
@@ -72,26 +106,45 @@ class PanelClimate extends LitElement {
.hass=${this.hass} .hass=${this.hass}
.narrow=${this.narrow} .narrow=${this.narrow}
></ha-menu-button> ></ha-menu-button>
`} `
}
<div class="main-title">${this.hass.localize("panel.climate")}</div> <div class="main-title">${this.hass.localize("panel.climate")}</div>
</div> </div>
</div> </div>
${
this._lovelace
? html`
<hui-view-container .hass=${this.hass}> <hui-view-container .hass=${this.hass}>
<hui-view <hui-view
.hass=${this.hass} .hass=${this.hass}
.narrow=${this.narrow} .narrow=${this.narrow}
.lovelace=${this._lovelace} .lovelace=${this._lovelace}
.index=${this._viewIndex} .index=${this._viewIndex}
></hui-view> ></hui-view
></hui-view-container>
`
: nothing
}
</hui-view-container> </hui-view-container>
`; `;
} }
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 = { this._lovelace = {
config: CLIMATE_LOVELACE_CONFIG, config: config,
rawConfig: CLIMATE_LOVELACE_CONFIG, rawConfig: rawConfig,
editMode: false, editMode: false,
urlPath: "climate", urlPath: "climate",
mode: "generated", mode: "generated",

View File

@@ -1,24 +1,23 @@
import type { CSSResultGroup, PropertyValues, TemplateResult } from "lit"; import type { CSSResultGroup, PropertyValues } from "lit";
import { LitElement, css, html } from "lit"; import { LitElement, css, html, nothing } from "lit";
import { customElement, property, state } from "lit/decorators"; import { customElement, property, state } from "lit/decorators";
import { goBack } from "../../common/navigate"; 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-icon-button-arrow-prev";
import "../../components/ha-menu-button"; 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 { haStyle } from "../../resources/styles";
import type { HomeAssistant } from "../../types"; import type { HomeAssistant } from "../../types";
import { generateLovelaceViewStrategy } from "../lovelace/strategies/get-strategy";
import type { Lovelace } from "../lovelace/types"; import type { Lovelace } from "../lovelace/types";
import "../lovelace/views/hui-view"; import "../lovelace/views/hui-view";
import "../lovelace/views/hui-view-container"; import "../lovelace/views/hui-view-container";
const LIGHT_LOVELACE_CONFIG: LovelaceConfig = { const LIGHT_LOVELACE_VIEW_CONFIG: LovelaceStrategyViewConfig = {
views: [
{
strategy: { strategy: {
type: "light", type: "light",
}, },
},
],
}; };
@customElement("ha-panel-light") @customElement("ha-panel-light")
@@ -34,28 +33,67 @@ class PanelLight extends LitElement {
@state() private _searchParms = new URLSearchParams(window.location.search); @state() private _searchParms = new URLSearchParams(window.location.search);
public willUpdate(changedProps: PropertyValues) { public willUpdate(changedProps: PropertyValues) {
super.willUpdate(changedProps);
// Initial setup
if (!this.hasUpdated) { if (!this.hasUpdated) {
this.hass.loadFragmentTranslation("lovelace"); this.hass.loadFragmentTranslation("lovelace");
this._setLovelace();
return;
} }
if (!changedProps.has("hass")) { if (!changedProps.has("hass")) {
return; return;
} }
const oldHass = changedProps.get("hass") as this["hass"]; 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(); this._setLovelace();
} }
} }
}
private _debounceRegistriesChanged = debounce(
() => this._registriesChanged(),
200
);
private _registriesChanged = async () => {
this._setLovelace();
};
private _back(ev) { private _back(ev) {
ev.stopPropagation(); ev.stopPropagation();
goBack(); goBack();
} }
protected render(): TemplateResult { protected render() {
return html` return html`
<div class="header"> <div class="header">
<div class="toolbar"> <div class="toolbar">
${this._searchParms.has("historyBack") ${
this._searchParms.has("historyBack")
? html` ? html`
<ha-icon-button-arrow-prev <ha-icon-button-arrow-prev
@click=${this._back} @click=${this._back}
@@ -68,26 +106,45 @@ class PanelLight extends LitElement {
.hass=${this.hass} .hass=${this.hass}
.narrow=${this.narrow} .narrow=${this.narrow}
></ha-menu-button> ></ha-menu-button>
`} `
}
<div class="main-title">${this.hass.localize("panel.light")}</div> <div class="main-title">${this.hass.localize("panel.light")}</div>
</div> </div>
</div> </div>
${
this._lovelace
? html`
<hui-view-container .hass=${this.hass}> <hui-view-container .hass=${this.hass}>
<hui-view <hui-view
.hass=${this.hass} .hass=${this.hass}
.narrow=${this.narrow} .narrow=${this.narrow}
.lovelace=${this._lovelace} .lovelace=${this._lovelace}
.index=${this._viewIndex} .index=${this._viewIndex}
></hui-view> ></hui-view
></hui-view-container>
`
: nothing
}
</hui-view-container> </hui-view-container>
`; `;
} }
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 = { this._lovelace = {
config: LIGHT_LOVELACE_CONFIG, config: config,
rawConfig: LIGHT_LOVELACE_CONFIG, rawConfig: rawConfig,
editMode: false, editMode: false,
urlPath: "light", urlPath: "light",
mode: "generated", mode: "generated",

View File

@@ -1,24 +1,23 @@
import type { CSSResultGroup, PropertyValues, TemplateResult } from "lit"; import type { CSSResultGroup, PropertyValues } from "lit";
import { LitElement, css, html } from "lit"; import { LitElement, css, html, nothing } from "lit";
import { customElement, property, state } from "lit/decorators"; import { customElement, property, state } from "lit/decorators";
import { goBack } from "../../common/navigate"; 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-icon-button-arrow-prev";
import "../../components/ha-menu-button"; 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 { haStyle } from "../../resources/styles";
import type { HomeAssistant } from "../../types"; import type { HomeAssistant } from "../../types";
import { generateLovelaceViewStrategy } from "../lovelace/strategies/get-strategy";
import type { Lovelace } from "../lovelace/types"; import type { Lovelace } from "../lovelace/types";
import "../lovelace/views/hui-view"; import "../lovelace/views/hui-view";
import "../lovelace/views/hui-view-container"; import "../lovelace/views/hui-view-container";
const SAFETY_LOVELACE_CONFIG: LovelaceConfig = { const SECURITY_LOVELACE_VIEW_CONFIG: LovelaceStrategyViewConfig = {
views: [
{
strategy: { strategy: {
type: "safety", type: "security",
}, },
},
],
}; };
@customElement("ha-panel-safety") @customElement("ha-panel-safety")
@@ -34,28 +33,67 @@ class PanelSafety extends LitElement {
@state() private _searchParms = new URLSearchParams(window.location.search); @state() private _searchParms = new URLSearchParams(window.location.search);
public willUpdate(changedProps: PropertyValues) { public willUpdate(changedProps: PropertyValues) {
super.willUpdate(changedProps);
// Initial setup
if (!this.hasUpdated) { if (!this.hasUpdated) {
this.hass.loadFragmentTranslation("lovelace"); this.hass.loadFragmentTranslation("lovelace");
this._setLovelace();
return;
} }
if (!changedProps.has("hass")) { if (!changedProps.has("hass")) {
return; return;
} }
const oldHass = changedProps.get("hass") as this["hass"]; 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(); this._setLovelace();
} }
} }
}
private _debounceRegistriesChanged = debounce(
() => this._registriesChanged(),
200
);
private _registriesChanged = async () => {
this._setLovelace();
};
private _back(ev) { private _back(ev) {
ev.stopPropagation(); ev.stopPropagation();
goBack(); goBack();
} }
protected render(): TemplateResult { protected render() {
return html` return html`
<div class="header"> <div class="header">
<div class="toolbar"> <div class="toolbar">
${this._searchParms.has("historyBack") ${
this._searchParms.has("historyBack")
? html` ? html`
<ha-icon-button-arrow-prev <ha-icon-button-arrow-prev
@click=${this._back} @click=${this._back}
@@ -68,26 +106,45 @@ class PanelSafety extends LitElement {
.hass=${this.hass} .hass=${this.hass}
.narrow=${this.narrow} .narrow=${this.narrow}
></ha-menu-button> ></ha-menu-button>
`} `
<div class="main-title">${this.hass.localize("panel.safety")}</div> }
<div class="main-title">${this.hass.localize("panel.security")}</div>
</div> </div>
</div> </div>
${
this._lovelace
? html`
<hui-view-container .hass=${this.hass}> <hui-view-container .hass=${this.hass}>
<hui-view <hui-view
.hass=${this.hass} .hass=${this.hass}
.narrow=${this.narrow} .narrow=${this.narrow}
.lovelace=${this._lovelace} .lovelace=${this._lovelace}
.index=${this._viewIndex} .index=${this._viewIndex}
></hui-view> ></hui-view
></hui-view-container>
`
: nothing
}
</hui-view-container> </hui-view-container>
`; `;
} }
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 = { this._lovelace = {
config: SAFETY_LOVELACE_CONFIG, config: config,
rawConfig: SAFETY_LOVELACE_CONFIG, rawConfig: rawConfig,
editMode: false, editMode: false,
urlPath: "safety", urlPath: "safety",
mode: "generated", mode: "generated",