Compare commits

...

11 Commits

Author SHA1 Message Date
Paul Bottein
d6b7a88f3e Simplify ll-rebuild for section 2026-03-11 17:19:07 +01:00
Paul Bottein
26210c84b9 Remove force refresh in hui-root 2026-03-11 10:47:10 +01:00
Paul Bottein
b7d1ee874f Add deep equal to avoid unecessary re-rendering 2026-03-11 10:47:10 +01:00
Paul Bottein
a390b1aa6c Improve panel home logic 2026-03-11 10:47:10 +01:00
Paul Bottein
6e69811941 Simplify usage with registryDependencies 2026-03-11 10:47:10 +01:00
Paul Bottein
1e23fda276 Fix some conditions 2026-03-11 10:47:10 +01:00
Paul Bottein
4729073b8c Add should regenerate for all strategies 2026-03-11 10:47:10 +01:00
Paul Bottein
4d54b732cf Add should regenerate for view and section 2026-03-11 10:47:10 +01:00
Paul Bottein
09b1f7dd93 Rename method 2026-03-11 10:47:09 +01:00
Paul Bottein
2c800de025 Check for floors 2026-03-11 10:47:09 +01:00
Paul Bottein
2e70d70e38 Add should update method to strategies 2026-03-11 10:47:09 +01:00
23 changed files with 239 additions and 181 deletions

View File

@@ -9,6 +9,8 @@ import { DEFAULT_ENERGY_COLLECTION_KEY } from "../ha-panel-energy";
@customElement("energy-overview-view-strategy")
export class EnergyOverviewViewStrategy extends ReactiveElement {
static registryDependencies = [];
static async generate(
_config: LovelaceStrategyConfig,
hass: HomeAssistant

View File

@@ -15,6 +15,8 @@ import {
@customElement("energy-view-strategy")
export class EnergyViewStrategy extends ReactiveElement {
static registryDependencies = [];
static async generate(
_config: LovelaceStrategyConfig,
hass: HomeAssistant

View File

@@ -9,6 +9,8 @@ import type { LovelaceSectionConfig } from "../../../data/lovelace/config/sectio
@customElement("gas-view-strategy")
export class GasViewStrategy extends ReactiveElement {
static registryDependencies = [];
static async generate(
_config: LovelaceStrategyConfig,
hass: HomeAssistant

View File

@@ -11,6 +11,8 @@ import type { LovelaceBadgeConfig } from "../../../data/lovelace/config/badge";
@customElement("power-view-strategy")
export class PowerViewStrategy extends ReactiveElement {
static registryDependencies = [];
static async generate(
_config: LovelaceStrategyConfig,
hass: HomeAssistant

View File

@@ -10,6 +10,8 @@ import { shouldShowFloorsAndAreas } from "./show-floors-and-areas";
@customElement("water-view-strategy")
export class WaterViewStrategy extends ReactiveElement {
static registryDependencies = [];
static async generate(
_config: LovelaceStrategyConfig,
hass: HomeAssistant

View File

@@ -18,6 +18,7 @@ import {
type HomeFrontendSystemData,
} from "../../data/frontend";
import type { LovelaceDashboardStrategyConfig } from "../../data/lovelace/config/types";
import { hasLegacyOverviewPanel } from "../../data/panel";
import { mdiHomeAssistant } from "../../resources/home-assistant-logo-svg";
import type { HomeAssistant, PanelInfo, Route } from "../../types";
import { showToast } from "../../util/toast";
@@ -26,11 +27,13 @@ import { showDeviceRegistryDetailDialog } from "../config/devices/device-registr
import { showAddIntegrationDialog } from "../config/integrations/show-add-integration-dialog";
import "../lovelace/hui-root";
import type { ExtraActionItem } from "../lovelace/hui-root";
import { expandLovelaceConfigStrategies } from "../lovelace/strategies/get-strategy";
import {
checkStrategyShouldRegenerate,
generateLovelaceDashboardStrategy,
} from "../lovelace/strategies/get-strategy";
import type { Lovelace } from "../lovelace/types";
import { showEditHomeDialog } from "./dialogs/show-dialog-edit-home";
import { showNewOverviewDialog } from "./dialogs/show-dialog-new-overview";
import { hasLegacyOverviewPanel } from "../../data/panel";
@customElement("ha-panel-home")
class PanelHome extends LitElement {
@@ -95,26 +98,19 @@ class PanelHome extends LitElement {
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 ||
oldHass.panels !== this.hass.panels
) {
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"
) {
if (oldHass && this.hass && this.hass.config.state === "RUNNING") {
if (oldHass.config.state !== "RUNNING") {
this._setup();
return;
}
const shouldRegenerate = checkStrategyShouldRegenerate(
"dashboard",
this._strategyConfig.strategy,
oldHass,
this.hass
);
if (shouldRegenerate) {
this._debounceRegenerateStrategy();
}
}
}
@@ -135,12 +131,12 @@ class PanelHome extends LitElement {
this._setLovelace();
}
private _debounceRegistriesChanged = debounce(
() => this._registriesChanged(),
private _debounceRegenerateStrategy = debounce(
() => this._regenerateStrategyConfig(),
200
);
private _registriesChanged = async () => {
private _regenerateStrategyConfig() {
// If on an area view that no longer exists, redirect to overview
const path = this.route?.path?.split("/")[1];
if (path?.startsWith("areas-")) {
@@ -151,7 +147,7 @@ class PanelHome extends LitElement {
}
}
this._setLovelace();
};
}
private _updateExtraActionItems() {
const path = this.route?.path?.split("/")[1];
@@ -312,17 +308,19 @@ class PanelHome extends LitElement {
});
}
private async _setLovelace() {
const strategyConfig: LovelaceDashboardStrategyConfig = {
private get _strategyConfig(): LovelaceDashboardStrategyConfig {
return {
strategy: {
type: "home",
favorite_entities: this._config.favorite_entities,
home_panel: true,
},
};
}
const config = await expandLovelaceConfigStrategies(
strategyConfig,
private async _setLovelace() {
const config = await generateLovelaceDashboardStrategy(
this._strategyConfig,
this.hass
);

View File

@@ -25,6 +25,7 @@ import {
isStrategyDashboard,
saveConfig,
} from "../../data/lovelace/config/types";
import { fetchDashboards } from "../../data/lovelace/dashboard";
import { fetchResources } from "../../data/lovelace/resource";
import type { WindowWithPreloads } from "../../data/preloads";
import "../../layouts/hass-error-screen";
@@ -36,10 +37,12 @@ import { checkLovelaceConfig } from "./common/check-lovelace-config";
import { loadLovelaceResources } from "./common/load-resources";
import { showSaveDialog } from "./editor/show-save-config-dialog";
import "./hui-root";
import { generateLovelaceDashboardStrategy } from "./strategies/get-strategy";
import {
checkStrategyShouldRegenerate,
generateLovelaceDashboardStrategy,
} from "./strategies/get-strategy";
import type { Lovelace } from "./types";
import { generateDefaultView } from "./views/default-view";
import { fetchDashboards } from "../../data/lovelace/dashboard";
(window as any).loadCardHelpers = () => import("./custom-card-helpers");
@@ -50,12 +53,6 @@ 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<
@@ -129,7 +126,6 @@ export class LovelacePanel extends LitElement {
.route=${this.route}
.narrow=${this.narrow}
@config-refresh=${this._forceFetchConfig}
@strategy-config-changed=${this._strategyConfigChanged}
></hui-root>
`;
}
@@ -195,61 +191,26 @@ export class LovelacePanel extends LitElement {
this.lovelace &&
isStrategyDashboard(this.lovelace.rawConfig)
) {
// 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();
}
}
// If ha started, refresh the config
if (
this.hass.config.state === "RUNNING" &&
oldHass.config.state !== "RUNNING"
(oldHass.config.state !== "RUNNING" ||
checkStrategyShouldRegenerate(
"dashboard",
this.lovelace.rawConfig.strategy,
oldHass,
this.hass
))
) {
this._regenerateStrategyConfig();
this._debounceRegenerateStrategy();
}
}
}
private _debounceRegistriesChanged = debounce(
() => this._registriesChanged(),
private _debounceRegenerateStrategy = debounce(
() => this._regenerateStrategyConfig(),
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;
// Regenerate if the config changed
if (!deepEqual(newConfig, oldConfig)) {
this._regenerateStrategyConfig();
}
};
private _strategyConfigChanged = (ev: CustomEvent) => {
ev.stopPropagation();
this._regenerateStrategyConfig();
};
private async _regenerateStrategyConfig() {
if (!this.hass || !this.lovelace) {
return;
@@ -263,6 +224,10 @@ export class LovelacePanel extends LitElement {
try {
const conf = await generateLovelaceDashboardStrategy(rawConf, this.hass!);
if (deepEqual(conf, this.lovelace.config)) {
return;
}
this._setLovelaceConfig(conf, rawConf, "generated");
} catch (err: any) {
// eslint-disable-next-line no-console

View File

@@ -35,7 +35,6 @@ import {
extractSearchParamsObject,
removeSearchParam,
} from "../../common/url/search-params";
import { debounce } from "../../common/util/debounce";
import { afterNextRender } from "../../common/util/render-status";
import "../../components/ha-button";
import "../../components/ha-dropdown";
@@ -156,7 +155,7 @@ class HUIRoot extends LitElement {
private _configChangedByUndo = false;
private _viewCache?: Record<string, HUIView>;
private _viewCache: Record<string, HUIView> = {};
private _viewScrollPositions: Record<string, number> = {};
@@ -170,23 +169,10 @@ class HUIRoot extends LitElement {
}),
});
private _debouncedConfigChanged: () => void;
private _conversation = memoizeOne((_components) =>
isComponentLoaded(this.hass, "conversation")
);
constructor() {
super();
// The view can trigger a re-render when it knows that certain
// web components have been loaded.
this._debouncedConfigChanged = debounce(
() => this._selectView(this._curView, true),
100,
false
);
}
private _renderActionItems(): TemplateResult {
const result: TemplateResult[] = [];
@@ -632,7 +618,6 @@ class HUIRoot extends LitElement {
.hass=${this.hass}
.theme=${curViewConfig?.theme}
id="view"
@ll-rebuild=${this._debouncedConfigChanged}
>
<hui-view-background .hass=${this.hass} .background=${background}>
</hui-view-background>
@@ -762,7 +747,6 @@ class HUIRoot extends LitElement {
}
let newSelectView;
let force = false;
let viewPath: string | undefined = this.route!.path.split("/")[1];
viewPath = viewPath ? decodeURI(viewPath) : undefined;
@@ -794,9 +778,8 @@ class HUIRoot extends LitElement {
| Lovelace
| undefined;
if (!oldLovelace || oldLovelace.config !== this.lovelace!.config) {
// On config change, recreate the current view from scratch.
force = true;
if (oldLovelace && oldLovelace.config !== this.lovelace!.config) {
this._cleanupViewCache(oldLovelace);
}
if (!oldLovelace || oldLovelace.editMode !== this.lovelace!.editMode) {
@@ -815,15 +798,12 @@ class HUIRoot extends LitElement {
}
}
if (!force && huiView) {
if (huiView) {
huiView.lovelace = this.lovelace!;
}
}
if (newSelectView !== undefined || force) {
if (force && newSelectView === undefined) {
newSelectView = this._curView;
}
if (newSelectView !== undefined) {
// Will allow for ripples to start rendering
afterNextRender(() => {
if (changedProperties.has("route")) {
@@ -835,7 +815,7 @@ class HUIRoot extends LitElement {
scrollTo({ behavior: "auto", top: position })
);
}
this._selectView(newSelectView, force);
this._selectView(newSelectView);
});
}
}
@@ -1162,8 +1142,27 @@ class HUIRoot extends LitElement {
}
}
private _selectView(viewIndex: HUIRoot["_curView"], force: boolean): void {
if (!force && this._curView === viewIndex) {
private _cleanupViewCache(oldLovelace: Lovelace): void {
// Clean up cache entries for views that no longer exist
const newViewPaths = new Set(
this.lovelace!.config.views.map((v, i) => v.path ?? String(i))
);
const keys = new Set([
...Object.keys(this._viewCache),
...Object.keys(this._viewScrollPositions),
]);
for (const key of keys) {
const index = Number(key);
const oldPath = oldLovelace.config.views[index]?.path ?? String(index);
if (!newViewPaths.has(oldPath)) {
delete this._viewCache[key];
delete this._viewScrollPositions[key];
}
}
}
private _selectView(viewIndex: HUIRoot["_curView"]): void {
if (this._curView === viewIndex) {
return;
}
@@ -1176,11 +1175,6 @@ class HUIRoot extends LitElement {
this._curView = viewIndex;
if (force) {
this._viewCache = {};
this._viewScrollPositions = {};
}
// Recreate a new element to clear the applied themes.
const root = this._viewRoot;
@@ -1208,12 +1202,12 @@ class HUIRoot extends LitElement {
return;
}
if (!force && this._viewCache![viewIndex]) {
view = this._viewCache![viewIndex];
if (this._viewCache[viewIndex]) {
view = this._viewCache[viewIndex];
} else {
view = document.createElement("hui-view");
view.index = viewIndex;
this._viewCache![viewIndex] = view;
this._viewCache[viewIndex] = view;
}
view.lovelace = this.lovelace;

View File

@@ -4,6 +4,8 @@ import { ReactiveElement } from "lit";
import { customElement, property, state } from "lit/decorators";
import { storage } from "../../../common/decorators/storage";
import { fireEvent } from "../../../common/dom/fire_event";
import { debounce } from "../../../common/util/debounce";
import { deepEqual } from "../../../common/util/deep-equal";
import "../../../components/ha-svg-icon";
import type { LovelaceSectionElement } from "../../../data/lovelace";
import type { LovelaceCardConfig } from "../../../data/lovelace/config/card";
@@ -12,8 +14,8 @@ import type {
LovelaceSectionRawConfig,
} from "../../../data/lovelace/config/section";
import { isStrategySection } from "../../../data/lovelace/config/section";
import type { HomeAssistant } from "../../../types";
import { ConditionalListenerMixin } from "../../../mixins/conditional-listener-mixin";
import type { HomeAssistant } from "../../../types";
import "../cards/hui-card";
import type { HuiCard } from "../cards/hui-card";
import { checkConditionsMet } from "../common/validate-condition";
@@ -23,7 +25,10 @@ import { showEditCardDialog } from "../editor/card-editor/show-edit-card-dialog"
import { addCard, replaceCard } from "../editor/config-util";
import { performDeleteCard } from "../editor/delete-card";
import { parseLovelaceCardPath } from "../editor/lovelace-path";
import { generateLovelaceSectionStrategy } from "../strategies/get-strategy";
import {
checkStrategyShouldRegenerate,
generateLovelaceSectionStrategy,
} from "../strategies/get-strategy";
import type { Lovelace } from "../types";
import { DEFAULT_SECTION_LAYOUT } from "./const";
@@ -104,9 +109,36 @@ export class HuiSection extends ConditionalListenerMixin<LovelaceSectionConfig>(
(!oldConfig || this.config !== oldConfig)
) {
this._initializeConfig();
return;
}
if (!changedProperties.has("hass")) {
return;
}
const oldHass = changedProperties.get("hass") as HomeAssistant | undefined;
if (
oldHass &&
this.hass &&
isStrategySection(this.config) &&
this.hass.config.state === "RUNNING" &&
(oldHass.config.state !== "RUNNING" ||
checkStrategyShouldRegenerate(
"section",
this.config.strategy,
oldHass,
this.hass
))
) {
this._debounceRefreshConfig();
}
}
private _debounceRefreshConfig = debounce(
() => this._initializeConfig(),
200
);
public disconnectedCallback() {
super.disconnectedCallback();
}
@@ -165,6 +197,11 @@ export class HuiSection extends ConditionalListenerMixin<LovelaceSectionConfig>(
...sectionConfig,
type: sectionConfig.type || DEFAULT_SECTION_LAYOUT,
};
if (isStrategy && deepEqual(sectionConfig, this._config)) {
return;
}
this._config = sectionConfig;
// Create a new layout element if necessary.

View File

@@ -34,6 +34,8 @@ const computeHeadingCard = (
@customElement("area-view-strategy")
export class AreaViewStrategy extends ReactiveElement {
static registryDependencies = ["entities", "devices", "areas"];
static async generate(
config: AreaViewStrategyConfig,
hass: HomeAssistant

View File

@@ -31,6 +31,8 @@ export interface AreasDashboardStrategyConfig {
@customElement("areas-dashboard-strategy")
export class AreasDashboardStrategy extends ReactiveElement {
static registryDependencies = ["areas"];
static async generate(
config: AreasDashboardStrategyConfig,
hass: HomeAssistant

View File

@@ -18,6 +18,7 @@ import type { AsyncReturnType, HomeAssistant } from "../../../types";
import { cleanLegacyStrategyConfig, isLegacyStrategy } from "./legacy-strategy";
import type {
LovelaceDashboardStrategy,
LovelaceStrategyRegistryKey,
LovelaceSectionStrategy,
LovelaceStrategy,
LovelaceViewStrategy,
@@ -26,6 +27,13 @@ import type {
const MAX_WAIT_STRATEGY_LOAD = 5000;
const CUSTOM_PREFIX = "custom:";
const DEFAULT_REGISTRY_DEPENDENCIES: readonly LovelaceStrategyRegistryKey[] = [
"entities",
"devices",
"areas",
"floors",
];
const STRATEGIES: Record<LovelaceStrategyConfigType, Record<string, any>> = {
dashboard: {
"original-states": () =>
@@ -235,6 +243,45 @@ export const generateLovelaceSectionStrategy = async (
};
};
/**
* Synchronously checks whether a strategy needs regeneration.
* Falls back to checking common registries if the strategy doesn't implement shouldRegenerate.
*/
export const checkStrategyShouldRegenerate = (
configType: LovelaceStrategyConfigType,
strategyConfig: LovelaceStrategyConfig,
oldHass: HomeAssistant,
newHass: HomeAssistant
): boolean => {
const strategyType = strategyConfig.type;
if (!strategyType) {
return false;
}
let strategy: LovelaceStrategy | undefined;
if (strategyType in STRATEGIES[configType]) {
const tag = `${strategyType}-${configType}-strategy`;
strategy = customElements.get(tag) as unknown as
| LovelaceStrategy
| undefined;
} else if (strategyType.startsWith(CUSTOM_PREFIX)) {
const name = strategyType.slice(CUSTOM_PREFIX.length);
const tag = `ll-strategy-${configType}-${name}`;
const legacyTag = `ll-strategy-${name}`;
strategy = (customElements.get(tag) ??
customElements.get(legacyTag)) as unknown as LovelaceStrategy | undefined;
}
if (strategy?.shouldRegenerate) {
return strategy.shouldRegenerate(strategyConfig, oldHass, newHass);
}
const dependencies =
strategy?.registryDependencies ?? DEFAULT_REGISTRY_DEPENDENCIES;
return dependencies.some((key) => oldHass[key] !== newHass[key]);
};
/**
* Find all references to strategies and replaces them with the generated output
*/

View File

@@ -32,6 +32,8 @@ export interface HomeAreaViewStrategyConfig {
@customElement("home-area-view-strategy")
export class HomeAreaViewStrategy extends ReactiveElement {
static registryDependencies = ["entities", "devices", "areas", "panels"];
static async generate(
config: HomeAreaViewStrategyConfig,
hass: HomeAssistant

View File

@@ -21,6 +21,8 @@ export interface HomeDashboardStrategyConfig {
@customElement("home-dashboard-strategy")
export class HomeDashboardStrategy extends ReactiveElement {
static registryDependencies = ["areas"];
static async generate(
config: HomeDashboardStrategyConfig,
hass: HomeAssistant

View File

@@ -72,6 +72,14 @@ const computeAreaCard = (
@customElement("home-overview-view-strategy")
export class HomeOverviewViewStrategy extends ReactiveElement {
static registryDependencies = [
"entities",
"devices",
"areas",
"floors",
"panels",
];
static async generate(
config: HomeOverviewViewStrategyConfig,
hass: HomeAssistant

View File

@@ -8,6 +8,8 @@ export type IframeDashboardStrategyConfig = IframeViewStrategyConfig;
@customElement("iframe-dashboard-strategy")
export class IframeDashboardStrategy extends ReactiveElement {
static registryDependencies = [];
static async generate(
config: IframeDashboardStrategyConfig
): Promise<LovelaceConfig> {

View File

@@ -11,6 +11,8 @@ export interface IframeViewStrategyConfig {
@customElement("iframe-view-strategy")
export class IframeViewStrategy extends ReactiveElement {
static registryDependencies = [];
static async generate(
config: IframeViewStrategyConfig
): Promise<LovelaceViewConfig> {

View File

@@ -7,6 +7,8 @@ export type MapDashboardStrategyConfig = MapViewStrategyConfig;
@customElement("map-dashboard-strategy")
export class MapDashboardStrategy extends ReactiveElement {
static registryDependencies = [];
static async generate(
config: MapDashboardStrategyConfig
): Promise<LovelaceConfig> {

View File

@@ -10,6 +10,8 @@ export interface MapViewStrategyConfig {
@customElement("map-view-strategy")
export class MapViewStrategy extends ReactiveElement {
static registryDependencies = [];
static async generate(
_config: MapViewStrategyConfig,
hass: HomeAssistant

View File

@@ -9,6 +9,8 @@ export type OriginalStatesDashboardStrategyConfig =
@customElement("original-states-dashboard-strategy")
export class OriginalStatesDashboardStrategy extends ReactiveElement {
static registryDependencies = [];
static async generate(
config: OriginalStatesDashboardStrategyConfig
): Promise<LovelaceConfig> {

View File

@@ -5,8 +5,22 @@ import type { LovelaceViewConfig } from "../../../data/lovelace/config/view";
import type { HomeAssistant } from "../../../types";
import type { LovelaceGenericElementEditor } from "../types";
export type LovelaceStrategyRegistryKey =
| "entities"
| "devices"
| "areas"
| "floors"
| "labels"
| "panels";
export interface LovelaceStrategy<T = any> {
generate(config: LovelaceStrategyConfig, hass: HomeAssistant): Promise<T>;
shouldRegenerate?(
config: LovelaceStrategyConfig,
oldHass: HomeAssistant,
newHass: HomeAssistant
): boolean;
registryDependencies?: readonly LovelaceStrategyRegistryKey[];
getConfigElement?: () => LovelaceStrategyEditor;
noEditor?: boolean;
configRequired?: boolean;

View File

@@ -26,6 +26,8 @@ export interface CommonControlSectionStrategyConfig {
@customElement("common-controls-section-strategy")
export class CommonControlsSectionStrategy extends ReactiveElement {
static registryDependencies = [];
static async generate(
config: CommonControlSectionStrategyConfig,
hass: HomeAssistant

View File

@@ -3,7 +3,7 @@ import type { PropertyValues } from "lit";
import { ReactiveElement } from "lit";
import { customElement, property, state } from "lit/decorators";
import { storage } from "../../../common/decorators/storage";
import { fireEvent, type HASSDomEvent } from "../../../common/dom/fire_event";
import 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";
@@ -42,7 +42,10 @@ import { parseLovelaceCardPath } from "../editor/lovelace-path";
import { createErrorSectionConfig } from "../sections/hui-error-section";
import "../sections/hui-section";
import type { HuiSection } from "../sections/hui-section";
import { generateLovelaceViewStrategy } from "../strategies/get-strategy";
import {
checkStrategyShouldRegenerate,
generateLovelaceViewStrategy,
} from "../strategies/get-strategy";
import type { Lovelace } from "../types";
import { getViewType } from "./get-view-type";
@@ -90,9 +93,7 @@ export class HUIView extends ReactiveElement {
private _layoutElement?: LovelaceViewElement;
private _layoutElementConfig?: LovelaceViewConfig;
private _rendered = false;
private _config?: LovelaceViewConfig;
@storage({
key: "dashboardCardClipboard",
@@ -139,11 +140,8 @@ export class HUIView extends ReactiveElement {
element.addEventListener(
"ll-rebuild",
(ev: Event) => {
// In edit mode let it go to hui-root and rebuild whole view.
if (!this.lovelace!.editMode) {
ev.stopPropagation();
this._rebuildSection(element, sectionConfig);
}
ev.stopPropagation();
this._rebuildSection(element, sectionConfig);
},
{ once: true }
);
@@ -154,18 +152,6 @@ 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);
@@ -201,51 +187,25 @@ export class HUIView extends ReactiveElement {
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
this.hass.config.state === "RUNNING" &&
(oldHass.config.state !== "RUNNING" ||
checkStrategyShouldRegenerate(
"view",
viewConfig.strategy,
oldHass,
this.hass
))
) {
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);
}
}
this._debounceRefreshConfig();
}
}
}
private _debounceRefreshConfig = debounce(
(force: boolean) => this._refreshConfig(force),
() => this._initializeConfig(),
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);
@@ -325,6 +285,12 @@ export class HUIView extends ReactiveElement {
viewConfig: LovelaceViewConfig,
isStrategy: boolean
) {
if (isStrategy && deepEqual(viewConfig, this._config)) {
return;
}
this._config = viewConfig;
// Create a new layout element if necessary.
let addLayoutElement = false;
@@ -332,7 +298,6 @@ export class HUIView extends ReactiveElement {
addLayoutElement = true;
this._createLayoutElement(viewConfig);
}
this._layoutElementConfig = viewConfig;
this._createBadges(viewConfig);
this._createCards(viewConfig);
this._createSections(viewConfig);
@@ -355,9 +320,9 @@ export class HUIView extends ReactiveElement {
private async _initializeConfig() {
const rawConfig = this.lovelace.config.views[this.index];
const isStrategy = isStrategyView(rawConfig);
const viewConfig = await this._generateConfig(rawConfig);
const isStrategy = isStrategyView(viewConfig);
this._setConfig(viewConfig, isStrategy);
}