Only ask to refresh dashboard if necessary (#24993)

This commit is contained in:
Paul Bottein 2025-04-11 14:41:12 +02:00 committed by Bram Kragten
parent 20f2a8d53e
commit 7742ccf631
2 changed files with 155 additions and 19 deletions

View File

@ -9,6 +9,8 @@ import {
addSearchParam,
removeSearchParam,
} from "../../common/url/search-params";
import { debounce } from "../../common/util/debounce";
import { deepEqual } from "../../common/util/deep-equal";
import { domainToName } from "../../data/integration";
import { subscribeLovelaceUpdates } from "../../data/lovelace";
import type {
@ -51,6 +53,12 @@ interface LovelacePanelConfig {
let editorLoaded = false;
let resourcesLoaded = false;
declare global {
interface HASSDomEvents {
"strategy-config-changed": undefined;
}
}
@customElement("ha-panel-lovelace")
export class LovelacePanel extends LitElement {
@property({ attribute: false }) public panel?: PanelInfo<LovelacePanelConfig>;
@ -127,6 +135,7 @@ export class LovelacePanel extends LitElement {
.route=${this.route}
.narrow=${this.narrow}
@config-refresh=${this._forceFetchConfig}
@strategy-config-changed=${this._strategyConfigChanged}
></hui-root>
`;
}
@ -199,7 +208,7 @@ export class LovelacePanel extends LitElement {
oldHass.floors !== this.hass.floors
) {
if (this.hass.config.state === "RUNNING") {
this._askRefreshConfig();
this._debounceRegistriesChanged();
}
}
// If ha started, refresh the config
@ -207,25 +216,59 @@ export class LovelacePanel extends LitElement {
this.hass.config.state === "RUNNING" &&
oldHass.config.state !== "RUNNING"
) {
this._refreshConfig();
this._regenerateStrategyConfig();
}
}
}
private _askRefreshConfig = () => {
private _debounceRegistriesChanged = debounce(
() => this._registriesChanged(),
200
);
private _registriesChanged = async () => {
if (!this.hass || !this.lovelace) {
return;
}
const rawConfig = this.lovelace.rawConfig;
if (!isStrategyDashboard(rawConfig)) {
return;
}
const oldConfig = this.lovelace.config;
const generatedConfig = await generateLovelaceDashboardStrategy(
rawConfig,
this.hass!
);
const newConfig = checkLovelaceConfig(generatedConfig) as LovelaceConfig;
// Ask to regenerate if the config changed
if (!deepEqual(newConfig, oldConfig)) {
this._askRegenerateStrategyConfig();
}
};
private _strategyConfigChanged = (ev: CustomEvent) => {
ev.stopPropagation();
this._askRegenerateStrategyConfig();
};
private _askRegenerateStrategyConfig = () => {
showToast(this, {
message: this.hass!.localize("ui.panel.lovelace.changed_toast.message"),
action: {
action: () => this._refreshConfig(),
action: () => this._regenerateStrategyConfig(),
text: this.hass!.localize("ui.common.refresh"),
},
duration: -1,
id: "entity-registry-changed",
id: "regenerate-strategy-config",
dismissable: false,
});
};
private async _refreshConfig() {
private async _regenerateStrategyConfig() {
if (!this.hass || !this.lovelace) {
return;
}

View File

@ -3,7 +3,9 @@ import type { PropertyValues } from "lit";
import { ReactiveElement } from "lit";
import { customElement, property, state } from "lit/decorators";
import { storage } from "../../../common/decorators/storage";
import type { HASSDomEvent } from "../../../common/dom/fire_event";
import { fireEvent, type HASSDomEvent } from "../../../common/dom/fire_event";
import { debounce } from "../../../common/util/debounce";
import { deepEqual } from "../../../common/util/deep-equal";
import "../../../components/entity/ha-state-label-badge";
import "../../../components/ha-svg-icon";
import type { LovelaceViewElement } from "../../../data/lovelace";
@ -11,7 +13,10 @@ import type { LovelaceBadgeConfig } from "../../../data/lovelace/config/badge";
import { ensureBadgeConfig } from "../../../data/lovelace/config/badge";
import type { LovelaceCardConfig } from "../../../data/lovelace/config/card";
import type { LovelaceSectionConfig } from "../../../data/lovelace/config/section";
import type { LovelaceViewConfig } from "../../../data/lovelace/config/view";
import type {
LovelaceViewConfig,
LovelaceViewRawConfig,
} from "../../../data/lovelace/config/view";
import { isStrategyView } from "../../../data/lovelace/config/view";
import type { HomeAssistant } from "../../../types";
import "../badges/hui-badge";
@ -85,6 +90,10 @@ export class HUIView extends ReactiveElement {
private _layoutElement?: LovelaceViewElement;
private _layoutElementConfig?: LovelaceViewConfig;
private _rendered = false;
@storage({
key: "dashboardCardClipboard",
state: false,
@ -145,6 +154,18 @@ export class HUIView extends ReactiveElement {
return this;
}
connectedCallback(): void {
super.connectedCallback();
this.updateComplete.then(() => {
this._rendered = true;
});
}
disconnectedCallback(): void {
super.disconnectedCallback();
this._rendered = false;
}
public willUpdate(changedProperties: PropertyValues<typeof this>): void {
super.willUpdate(changedProperties);
@ -169,9 +190,62 @@ export class HUIView extends ReactiveElement {
oldLovelace.config.views[this.index]))
) {
this._initializeConfig();
return;
}
if (!changedProperties.has("hass")) {
return;
}
const oldHass = changedProperties.get("hass") as HomeAssistant | undefined;
const viewConfig = this.lovelace.config.views[this.index];
if (oldHass && this.hass && this.lovelace && isStrategyView(viewConfig)) {
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") {
// If the page is not rendered yet, we can force the refresh
if (this._rendered) {
this._debounceRefreshConfig(false);
} else {
this._refreshConfig(true);
}
}
}
}
}
private _debounceRefreshConfig = debounce(
(force: boolean) => this._refreshConfig(force),
200
);
private _refreshConfig = async (force: boolean) => {
if (!this.hass || !this.lovelace) {
return;
}
const viewConfig = this.lovelace.config.views[this.index];
if (!isStrategyView(viewConfig)) {
return;
}
const oldConfig = this._layoutElementConfig;
const newConfig = await this._generateConfig(viewConfig);
// Don't ask if the config is the same
if (!deepEqual(newConfig, oldConfig)) {
if (force) {
this._setConfig(newConfig, true);
} else {
fireEvent(this, "strategy-config-changed");
}
}
};
protected update(changedProperties: PropertyValues) {
super.update(changedProperties);
@ -227,20 +301,30 @@ export class HUIView extends ReactiveElement {
}
}
private async _initializeConfig() {
let viewConfig = this.lovelace.config.views[this.index];
let isStrategy = false;
if (isStrategyView(viewConfig)) {
isStrategy = true;
viewConfig = await generateLovelaceViewStrategy(viewConfig, this.hass!);
private async _generateConfig(
config: LovelaceViewRawConfig
): Promise<LovelaceViewConfig> {
if (isStrategyView(config)) {
const generatedConfig = await generateLovelaceViewStrategy(
config,
this.hass!
);
return {
...generatedConfig,
type: getViewType(generatedConfig),
};
}
viewConfig = {
...viewConfig,
type: getViewType(viewConfig),
return {
...config,
type: getViewType(config),
};
}
private async _setConfig(
viewConfig: LovelaceViewConfig,
isStrategy: boolean
) {
// Create a new layout element if necessary.
let addLayoutElement = false;
@ -248,7 +332,7 @@ export class HUIView extends ReactiveElement {
addLayoutElement = true;
this._createLayoutElement(viewConfig);
}
this._layoutElementConfig = viewConfig;
this._createBadges(viewConfig);
this._createCards(viewConfig);
this._createSections(viewConfig);
@ -269,6 +353,15 @@ export class HUIView extends ReactiveElement {
}
}
private async _initializeConfig() {
const rawConfig = this.lovelace.config.views[this.index];
const viewConfig = await this._generateConfig(rawConfig);
const isStrategy = isStrategyView(viewConfig);
this._setConfig(viewConfig, isStrategy);
}
private _createLayoutElement(config: LovelaceViewConfig): void {
this._layoutElement = createViewElement(config) as LovelaceViewElement;
this._layoutElementType = config.type;