mirror of
https://github.com/home-assistant/frontend.git
synced 2026-04-02 08:33:31 +01:00
Convert energy panel to use a dashboard strategy (#30170)
* Convert energy panel to use a dashboard strategy Move the view-selection logic from ha-panel-energy into a dedicated energy dashboard strategy, consistent with how home/areas/map dashboards work. The strategy decides which view strategies (electricity, gas, water, power, overview) to show based on energy preferences. Extract shared constants into a separate module to avoid circular dependencies between the panel and view strategies. * Remove unused energy collection constants from ha-panel-energy
This commit is contained in:
2
src/panels/energy/constants.ts
Normal file
2
src/panels/energy/constants.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export const DEFAULT_ENERGY_COLLECTION_KEY = "energy_dashboard";
|
||||
export const DEFAULT_POWER_COLLECTION_KEY = "energy_dashboard_now";
|
||||
@@ -1,78 +1,20 @@
|
||||
import type { CSSResultGroup, PropertyValues } from "lit";
|
||||
import { LitElement, css, html, nothing } from "lit";
|
||||
import { LitElement, css, html } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { navigate } from "../../common/navigate";
|
||||
import type { LocalizeKeys } from "../../common/translations/localize";
|
||||
import "../../components/ha-alert";
|
||||
import "../../components/ha-icon-button-arrow-prev";
|
||||
import "../../components/ha-menu-button";
|
||||
import "../../components/ha-top-app-bar-fixed";
|
||||
import type { EnergyPreferences } from "../../data/energy";
|
||||
import { getEnergyDataCollection } from "../../data/energy";
|
||||
import type { LovelaceConfig } from "../../data/lovelace/config/types";
|
||||
import type { LovelaceViewConfig } from "../../data/lovelace/config/view";
|
||||
import { haStyle } from "../../resources/styles";
|
||||
import type { HomeAssistant, PanelInfo } from "../../types";
|
||||
import { generateLovelaceDashboardStrategy } from "../lovelace/strategies/get-strategy";
|
||||
import "../lovelace/hui-root";
|
||||
import type { Lovelace } from "../lovelace/types";
|
||||
import "../lovelace/views/hui-view";
|
||||
import "../lovelace/views/hui-view-container";
|
||||
|
||||
export const DEFAULT_ENERGY_COLLECTION_KEY = "energy_dashboard";
|
||||
export const DEFAULT_POWER_COLLECTION_KEY = "energy_dashboard_now";
|
||||
|
||||
const EMPTY_PREFERENCES: EnergyPreferences = {
|
||||
energy_sources: [],
|
||||
device_consumption: [],
|
||||
device_consumption_water: [],
|
||||
};
|
||||
|
||||
const OVERVIEW_VIEW = {
|
||||
path: "overview",
|
||||
strategy: {
|
||||
type: "energy-overview",
|
||||
collection_key: DEFAULT_ENERGY_COLLECTION_KEY,
|
||||
},
|
||||
} as LovelaceViewConfig;
|
||||
|
||||
const ENERGY_VIEW = {
|
||||
path: "electricity",
|
||||
strategy: {
|
||||
type: "energy",
|
||||
collection_key: DEFAULT_ENERGY_COLLECTION_KEY,
|
||||
},
|
||||
} as LovelaceViewConfig;
|
||||
|
||||
const WATER_VIEW = {
|
||||
path: "water",
|
||||
strategy: {
|
||||
type: "water",
|
||||
collection_key: DEFAULT_ENERGY_COLLECTION_KEY,
|
||||
},
|
||||
} as LovelaceViewConfig;
|
||||
|
||||
const GAS_VIEW = {
|
||||
path: "gas",
|
||||
strategy: {
|
||||
type: "gas",
|
||||
collection_key: DEFAULT_ENERGY_COLLECTION_KEY,
|
||||
},
|
||||
} as LovelaceViewConfig;
|
||||
|
||||
const POWER_VIEW = {
|
||||
path: "now",
|
||||
strategy: {
|
||||
type: "power",
|
||||
collection_key: DEFAULT_POWER_COLLECTION_KEY,
|
||||
},
|
||||
} as LovelaceViewConfig;
|
||||
|
||||
const WIZARD_VIEW = {
|
||||
type: "panel",
|
||||
path: "setup",
|
||||
cards: [{ type: "custom:energy-setup-wizard-card" }],
|
||||
};
|
||||
|
||||
@customElement("ha-panel-energy")
|
||||
class PanelEnergy extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
@@ -88,9 +30,6 @@ class PanelEnergy extends LitElement {
|
||||
prefix: string;
|
||||
};
|
||||
|
||||
@state()
|
||||
private _prefs?: EnergyPreferences;
|
||||
|
||||
@state() private _searchParms = new URLSearchParams(window.location.search);
|
||||
|
||||
@state()
|
||||
@@ -115,35 +54,16 @@ class PanelEnergy extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
private _fetchEnergyPrefs = async (): Promise<
|
||||
EnergyPreferences | undefined
|
||||
> => {
|
||||
const collection = getEnergyDataCollection(this.hass, {
|
||||
key: DEFAULT_ENERGY_COLLECTION_KEY,
|
||||
});
|
||||
try {
|
||||
await collection.refresh();
|
||||
} catch (err: any) {
|
||||
if (err.code === "not_found") {
|
||||
return undefined;
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
return collection.prefs;
|
||||
};
|
||||
|
||||
private async _loadConfig() {
|
||||
try {
|
||||
this._error = undefined;
|
||||
const prefs = await this._fetchEnergyPrefs();
|
||||
this._prefs = prefs || EMPTY_PREFERENCES;
|
||||
await this._setLovelace();
|
||||
} catch (err) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error("Failed to load prefs:", err);
|
||||
this._prefs = EMPTY_PREFERENCES;
|
||||
console.error("Failed to load energy config:", err);
|
||||
this._error = (err as Error).message || "Unknown error";
|
||||
return;
|
||||
}
|
||||
await this._setLovelace();
|
||||
|
||||
// Check if current path is valid, navigate to first view if not
|
||||
const views = this._lovelace!.config?.views || [];
|
||||
@@ -158,7 +78,10 @@ class PanelEnergy extends LitElement {
|
||||
}
|
||||
|
||||
private async _setLovelace() {
|
||||
const config = await this._generateLovelaceConfig();
|
||||
const config: LovelaceConfig = await generateLovelaceDashboardStrategy(
|
||||
{ strategy: { type: "energy" } },
|
||||
this.hass
|
||||
);
|
||||
|
||||
this._lovelace = {
|
||||
config: config,
|
||||
@@ -188,8 +111,7 @@ class PanelEnergy extends LitElement {
|
||||
`;
|
||||
}
|
||||
|
||||
if (!this._prefs) {
|
||||
// Still loading
|
||||
if (!this._lovelace) {
|
||||
return html`
|
||||
<div class="centered">
|
||||
<ha-spinner size="large"></ha-spinner>
|
||||
@@ -197,10 +119,6 @@ class PanelEnergy extends LitElement {
|
||||
`;
|
||||
}
|
||||
|
||||
if (!this._lovelace) {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
return html`
|
||||
<hui-root
|
||||
.hass=${this.hass}
|
||||
@@ -216,78 +134,6 @@ class PanelEnergy extends LitElement {
|
||||
`;
|
||||
}
|
||||
|
||||
private async _generateLovelaceConfig(): Promise<LovelaceConfig> {
|
||||
if (
|
||||
!this._prefs ||
|
||||
(this._prefs.device_consumption.length === 0 &&
|
||||
this._prefs.energy_sources.length === 0)
|
||||
) {
|
||||
await import("./cards/energy-setup-wizard-card");
|
||||
return {
|
||||
views: [WIZARD_VIEW],
|
||||
};
|
||||
}
|
||||
|
||||
const hasEnergy = this._prefs.energy_sources.some((source) =>
|
||||
["grid", "solar", "battery"].includes(source.type)
|
||||
);
|
||||
|
||||
const hasPowerSource = this._prefs.energy_sources.some((source) => {
|
||||
if (source.type === "solar" && source.stat_rate) return true;
|
||||
if (source.type === "battery" && source.stat_rate) return true;
|
||||
if (source.type === "grid") {
|
||||
return !!source.stat_rate || !!source.power_config;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
const hasDevicePower = this._prefs.device_consumption.some(
|
||||
(device) => device.stat_rate
|
||||
);
|
||||
|
||||
const hasPower = hasPowerSource || hasDevicePower;
|
||||
|
||||
const hasWater =
|
||||
this._prefs.energy_sources.some((source) => source.type === "water") ||
|
||||
this._prefs.device_consumption_water?.length > 0;
|
||||
|
||||
const hasGas = this._prefs.energy_sources.some(
|
||||
(source) => source.type === "gas"
|
||||
);
|
||||
|
||||
const hasDeviceConsumption = this._prefs.device_consumption.length > 0;
|
||||
|
||||
const views: LovelaceViewConfig[] = [];
|
||||
if (hasEnergy || hasDeviceConsumption) {
|
||||
views.push(ENERGY_VIEW);
|
||||
}
|
||||
if (hasGas) {
|
||||
views.push(GAS_VIEW);
|
||||
}
|
||||
if (hasWater) {
|
||||
views.push(WATER_VIEW);
|
||||
}
|
||||
if (hasPower) {
|
||||
views.push(POWER_VIEW);
|
||||
}
|
||||
if (
|
||||
hasPowerSource ||
|
||||
[hasEnergy, hasGas, hasWater].filter(Boolean).length > 1
|
||||
) {
|
||||
views.unshift(OVERVIEW_VIEW);
|
||||
}
|
||||
return {
|
||||
views: views.map((view) => ({
|
||||
...view,
|
||||
title:
|
||||
view.title ||
|
||||
this.hass.localize(
|
||||
`ui.panel.energy.title.${view.path}` as LocalizeKeys
|
||||
),
|
||||
})),
|
||||
};
|
||||
}
|
||||
|
||||
private _navigateConfig(ev?: Event) {
|
||||
ev?.stopPropagation();
|
||||
const viewPath = this.route?.path?.split("/")[1] || "";
|
||||
|
||||
170
src/panels/energy/strategies/energy-dashboard-strategy.ts
Normal file
170
src/panels/energy/strategies/energy-dashboard-strategy.ts
Normal file
@@ -0,0 +1,170 @@
|
||||
import { ReactiveElement } from "lit";
|
||||
import { customElement } from "lit/decorators";
|
||||
import { getEnergyDataCollection } from "../../../data/energy";
|
||||
import type { EnergyPreferences } from "../../../data/energy";
|
||||
import type { LovelaceStrategyConfig } from "../../../data/lovelace/config/strategy";
|
||||
import type { LovelaceConfig } from "../../../data/lovelace/config/types";
|
||||
import type { LovelaceViewConfig } from "../../../data/lovelace/config/view";
|
||||
import type { LocalizeKeys } from "../../../common/translations/localize";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import {
|
||||
DEFAULT_ENERGY_COLLECTION_KEY,
|
||||
DEFAULT_POWER_COLLECTION_KEY,
|
||||
} from "../constants";
|
||||
|
||||
const OVERVIEW_VIEW = {
|
||||
path: "overview",
|
||||
strategy: {
|
||||
type: "energy-overview",
|
||||
collection_key: DEFAULT_ENERGY_COLLECTION_KEY,
|
||||
},
|
||||
} as LovelaceViewConfig;
|
||||
|
||||
const ENERGY_VIEW = {
|
||||
path: "electricity",
|
||||
strategy: {
|
||||
type: "energy",
|
||||
collection_key: DEFAULT_ENERGY_COLLECTION_KEY,
|
||||
},
|
||||
} as LovelaceViewConfig;
|
||||
|
||||
const WATER_VIEW = {
|
||||
path: "water",
|
||||
strategy: {
|
||||
type: "water",
|
||||
collection_key: DEFAULT_ENERGY_COLLECTION_KEY,
|
||||
},
|
||||
} as LovelaceViewConfig;
|
||||
|
||||
const GAS_VIEW = {
|
||||
path: "gas",
|
||||
strategy: {
|
||||
type: "gas",
|
||||
collection_key: DEFAULT_ENERGY_COLLECTION_KEY,
|
||||
},
|
||||
} as LovelaceViewConfig;
|
||||
|
||||
const POWER_VIEW = {
|
||||
path: "now",
|
||||
strategy: {
|
||||
type: "power",
|
||||
collection_key: DEFAULT_POWER_COLLECTION_KEY,
|
||||
},
|
||||
} as LovelaceViewConfig;
|
||||
|
||||
const WIZARD_VIEW = {
|
||||
type: "panel",
|
||||
path: "setup",
|
||||
cards: [{ type: "custom:energy-setup-wizard-card" }],
|
||||
};
|
||||
|
||||
const EMPTY_PREFERENCES: EnergyPreferences = {
|
||||
energy_sources: [],
|
||||
device_consumption: [],
|
||||
device_consumption_water: [],
|
||||
};
|
||||
|
||||
export interface EnergyDashboardStrategyConfig extends LovelaceStrategyConfig {
|
||||
type: "energy";
|
||||
}
|
||||
|
||||
@customElement("energy-dashboard-strategy")
|
||||
export class EnergyDashboardStrategy extends ReactiveElement {
|
||||
static async generate(
|
||||
_config: EnergyDashboardStrategyConfig,
|
||||
hass: HomeAssistant
|
||||
): Promise<LovelaceConfig> {
|
||||
const prefs = await fetchEnergyPrefs(hass);
|
||||
|
||||
if (
|
||||
!prefs ||
|
||||
(prefs.device_consumption.length === 0 &&
|
||||
prefs.energy_sources.length === 0)
|
||||
) {
|
||||
await import("../cards/energy-setup-wizard-card");
|
||||
return {
|
||||
views: [WIZARD_VIEW],
|
||||
};
|
||||
}
|
||||
|
||||
const hasEnergy = prefs.energy_sources.some((source) =>
|
||||
["grid", "solar", "battery"].includes(source.type)
|
||||
);
|
||||
|
||||
const hasPowerSource = prefs.energy_sources.some((source) => {
|
||||
if (source.type === "solar" && source.stat_rate) return true;
|
||||
if (source.type === "battery" && source.stat_rate) return true;
|
||||
if (source.type === "grid") {
|
||||
return !!source.stat_rate || !!source.power_config;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
const hasDevicePower = prefs.device_consumption.some(
|
||||
(device) => device.stat_rate
|
||||
);
|
||||
|
||||
const hasPower = hasPowerSource || hasDevicePower;
|
||||
|
||||
const hasWater =
|
||||
prefs.energy_sources.some((source) => source.type === "water") ||
|
||||
prefs.device_consumption_water?.length > 0;
|
||||
|
||||
const hasGas = prefs.energy_sources.some((source) => source.type === "gas");
|
||||
|
||||
const hasDeviceConsumption = prefs.device_consumption.length > 0;
|
||||
|
||||
const views: LovelaceViewConfig[] = [];
|
||||
if (hasEnergy || hasDeviceConsumption) {
|
||||
views.push(ENERGY_VIEW);
|
||||
}
|
||||
if (hasGas) {
|
||||
views.push(GAS_VIEW);
|
||||
}
|
||||
if (hasWater) {
|
||||
views.push(WATER_VIEW);
|
||||
}
|
||||
if (hasPower) {
|
||||
views.push(POWER_VIEW);
|
||||
}
|
||||
if (
|
||||
hasPowerSource ||
|
||||
[hasEnergy, hasGas, hasWater].filter(Boolean).length > 1
|
||||
) {
|
||||
views.unshift(OVERVIEW_VIEW);
|
||||
}
|
||||
return {
|
||||
views: views.map((view) => ({
|
||||
...view,
|
||||
title:
|
||||
view.title ||
|
||||
hass.localize(`ui.panel.energy.title.${view.path}` as LocalizeKeys),
|
||||
})),
|
||||
};
|
||||
}
|
||||
|
||||
static noEditor = true;
|
||||
}
|
||||
|
||||
async function fetchEnergyPrefs(
|
||||
hass: HomeAssistant
|
||||
): Promise<EnergyPreferences> {
|
||||
const collection = getEnergyDataCollection(hass, {
|
||||
key: DEFAULT_ENERGY_COLLECTION_KEY,
|
||||
});
|
||||
try {
|
||||
await collection.refresh();
|
||||
} catch (err: any) {
|
||||
if (err.code === "not_found") {
|
||||
return EMPTY_PREFERENCES;
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
return collection.prefs || EMPTY_PREFERENCES;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"energy-dashboard-strategy": EnergyDashboardStrategy;
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import { getEnergyDataCollection } from "../../../data/energy";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import type { LovelaceViewConfig } from "../../../data/lovelace/config/view";
|
||||
import type { LovelaceStrategyConfig } from "../../../data/lovelace/config/strategy";
|
||||
import { DEFAULT_ENERGY_COLLECTION_KEY } from "../ha-panel-energy";
|
||||
import { DEFAULT_ENERGY_COLLECTION_KEY } from "../constants";
|
||||
|
||||
@customElement("energy-overview-view-strategy")
|
||||
export class EnergyOverviewViewStrategy extends ReactiveElement {
|
||||
|
||||
@@ -6,7 +6,7 @@ import type { LovelaceCardConfig } from "../../../data/lovelace/config/card";
|
||||
import type { LovelaceStrategyConfig } from "../../../data/lovelace/config/strategy";
|
||||
import type { LovelaceViewConfig } from "../../../data/lovelace/config/view";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import { DEFAULT_ENERGY_COLLECTION_KEY } from "../ha-panel-energy";
|
||||
import { DEFAULT_ENERGY_COLLECTION_KEY } from "../constants";
|
||||
import { shouldShowFloorsAndAreas } from "./show-floors-and-areas";
|
||||
import {
|
||||
LARGE_SCREEN_CONDITION,
|
||||
|
||||
@@ -4,7 +4,7 @@ import { getEnergyDataCollection } from "../../../data/energy";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import type { LovelaceViewConfig } from "../../../data/lovelace/config/view";
|
||||
import type { LovelaceStrategyConfig } from "../../../data/lovelace/config/strategy";
|
||||
import { DEFAULT_ENERGY_COLLECTION_KEY } from "../ha-panel-energy";
|
||||
import { DEFAULT_ENERGY_COLLECTION_KEY } from "../constants";
|
||||
import type { LovelaceSectionConfig } from "../../../data/lovelace/config/section";
|
||||
|
||||
@customElement("gas-view-strategy")
|
||||
|
||||
@@ -4,7 +4,7 @@ import { getEnergyDataCollection } from "../../../data/energy";
|
||||
import type { LovelaceStrategyConfig } from "../../../data/lovelace/config/strategy";
|
||||
import type { LovelaceViewConfig } from "../../../data/lovelace/config/view";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import { DEFAULT_ENERGY_COLLECTION_KEY } from "../ha-panel-energy";
|
||||
import { DEFAULT_ENERGY_COLLECTION_KEY } from "../constants";
|
||||
import { shouldShowFloorsAndAreas } from "./show-floors-and-areas";
|
||||
import type { LovelaceSectionConfig } from "../../../data/lovelace/config/section";
|
||||
import type { LovelaceBadgeConfig } from "../../../data/lovelace/config/badge";
|
||||
|
||||
@@ -5,7 +5,7 @@ import type { LovelaceSectionConfig } from "../../../data/lovelace/config/sectio
|
||||
import type { LovelaceStrategyConfig } from "../../../data/lovelace/config/strategy";
|
||||
import type { LovelaceViewConfig } from "../../../data/lovelace/config/view";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import { DEFAULT_ENERGY_COLLECTION_KEY } from "../ha-panel-energy";
|
||||
import { DEFAULT_ENERGY_COLLECTION_KEY } from "../constants";
|
||||
import { shouldShowFloorsAndAreas } from "./show-floors-and-areas";
|
||||
|
||||
@customElement("water-view-strategy")
|
||||
|
||||
@@ -34,6 +34,7 @@ const STRATEGIES: Record<LovelaceStrategyConfigType, Record<string, any>> = {
|
||||
iframe: () => import("./iframe/iframe-dashboard-strategy"),
|
||||
areas: () => import("./areas/areas-dashboard-strategy"),
|
||||
home: () => import("./home/home-dashboard-strategy"),
|
||||
energy: () => import("../../energy/strategies/energy-dashboard-strategy"),
|
||||
},
|
||||
view: {
|
||||
"original-states": () =>
|
||||
|
||||
Reference in New Issue
Block a user