diff --git a/src/data/lovelace.ts b/src/data/lovelace.ts index 091ced7c7b89..0b32f0da5b86 100644 --- a/src/data/lovelace.ts +++ b/src/data/lovelace.ts @@ -17,12 +17,14 @@ export interface LovelacePanelConfig { mode: "yaml" | "storage"; } +export interface LovelaceStrategyConfig { + type: string; + options?: Record; +} + export interface LovelaceConfig { title?: string; - strategy?: { - type: string; - options?: Record; - }; + strategy?: LovelaceStrategyConfig; views: LovelaceViewConfig[]; background?: string; } diff --git a/src/panels/lovelace/editor/dashboard-strategy-editor/hui-dashboard-strategy-element-editor.ts b/src/panels/lovelace/editor/dashboard-strategy-editor/hui-dashboard-strategy-element-editor.ts new file mode 100644 index 000000000000..24ed9fef9faf --- /dev/null +++ b/src/panels/lovelace/editor/dashboard-strategy-editor/hui-dashboard-strategy-element-editor.ts @@ -0,0 +1,32 @@ +import { customElement } from "lit/decorators"; +import { LovelaceConfig } from "../../../../data/lovelace"; +import { + getLovelaceStrategy, + LovelaceDashboardStrategy, + type LovelaceDashboardStrategyEditor, +} from "../../strategies/get-strategy"; +import { HuiElementEditor } from "../hui-element-editor"; + +@customElement("hui-dashboard-strategy-element-editor") +export class HuiDashboardStrategyElementEditor extends HuiElementEditor { + protected async getConfigElement(): Promise< + LovelaceDashboardStrategyEditor | undefined + > { + const elClass = await getLovelaceStrategy( + this.configElementType! + ); + + // Check if a GUI editor exists + if (elClass && elClass.getConfigElement) { + return elClass.getConfigElement(); + } + + return undefined; + } +} + +declare global { + interface HTMLElementTagNameMap { + "hui-dashboard-strategy-element-editor": HuiDashboardStrategyElementEditor; + } +} diff --git a/src/panels/lovelace/editor/dashboard-strategy-editor/hui-original-states-dashboard-strategy-editor.ts b/src/panels/lovelace/editor/dashboard-strategy-editor/hui-original-states-dashboard-strategy-editor.ts new file mode 100644 index 000000000000..500011cf2713 --- /dev/null +++ b/src/panels/lovelace/editor/dashboard-strategy-editor/hui-original-states-dashboard-strategy-editor.ts @@ -0,0 +1,77 @@ +import { html, LitElement, nothing } from "lit"; +import { customElement, property, state } from "lit/decorators"; +import { fireEvent } from "../../../../common/dom/fire_event"; +import "../../../../components/ha-form/ha-form"; +import type { + HaFormSchema, + SchemaUnion, +} from "../../../../components/ha-form/types"; +import { LovelaceStrategyConfig } from "../../../../data/lovelace"; +import type { HomeAssistant } from "../../../../types"; +import { LovelaceDashboardStrategyEditor } from "../../strategies/get-strategy"; + +const SCHEMA = [ + { + name: "no_area_group", + selector: { + boolean: {}, + }, + }, +] as const satisfies readonly HaFormSchema[]; + +@customElement("hui-original-states-dashboard-strategy-editor") +export class HuiSensorCardEditor + extends LitElement + implements LovelaceDashboardStrategyEditor +{ + @property({ attribute: false }) public hass?: HomeAssistant; + + @state() private _config?: LovelaceStrategyConfig; + + public setConfig(config: LovelaceStrategyConfig): void { + this._config = config; + } + + protected render() { + if (!this.hass || !this._config) { + return nothing; + } + + const data = this._config.options; + + return html` + + `; + } + + private _valueChanged(ev: CustomEvent): void { + const config = { + type: this._config!.type, + options: ev.detail.value, + }; + fireEvent(this, "config-changed", { config }); + } + + private _computeLabelCallback = (schema: SchemaUnion) => { + switch (schema.name) { + case "no_area_group": + return "Do not group by area"; + default: + return this.hass!.localize( + `ui.panel.lovelace.editor.card.generic.${schema.name}` + ); + } + }; +} + +declare global { + interface HTMLElementTagNameMap { + "hui-original-states-dashboard-strategy-editor": HuiSensorCardEditor; + } +} diff --git a/src/panels/lovelace/editor/hui-element-editor.ts b/src/panels/lovelace/editor/hui-element-editor.ts index 2d2b58f4a10f..0ac7eb6e8783 100644 --- a/src/panels/lovelace/editor/hui-element-editor.ts +++ b/src/panels/lovelace/editor/hui-element-editor.ts @@ -19,6 +19,7 @@ import type { HaCodeEditor } from "../../../components/ha-code-editor"; import type { LovelaceCardConfig, LovelaceConfig, + LovelaceStrategyConfig, } from "../../../data/lovelace"; import type { HomeAssistant } from "../../../types"; import type { LovelaceRowConfig } from "../entity-rows/types"; @@ -38,7 +39,8 @@ export interface ConfigChangedEvent { | LovelaceCardConfig | LovelaceRowConfig | LovelaceHeaderFooterConfig - | LovelaceTileFeatureConfig; + | LovelaceTileFeatureConfig + | LovelaceStrategyConfig; error?: string; guiModeAvailable?: boolean; } diff --git a/src/panels/lovelace/hui-root.ts b/src/panels/lovelace/hui-root.ts index e9423fb81a21..349cad4f6dd3 100644 --- a/src/panels/lovelace/hui-root.ts +++ b/src/panels/lovelace/hui-root.ts @@ -19,12 +19,12 @@ import { import "@polymer/paper-tabs/paper-tab"; import "@polymer/paper-tabs/paper-tabs"; import { - css, CSSResultGroup, - html, LitElement, PropertyValues, TemplateResult, + css, + html, } from "lit"; import { customElement, property, state } from "lit/decorators"; import { classMap } from "lit/directives/class-map"; @@ -68,6 +68,7 @@ import { documentationUrl } from "../../util/documentation-url"; import { swapView } from "./editor/config-util"; import { showEditLovelaceDialog } from "./editor/lovelace-editor/show-edit-lovelace-dialog"; import { showEditViewDialog } from "./editor/view-editor/show-edit-view-dialog"; +import { showDashboardStrategyEditorDialog } from "./strategies/device-registry-detail/show-dialog-dashboard-strategy-editor"; import type { Lovelace } from "./types"; import "./views/hui-view"; import type { HUIView } from "./views/hui-view"; @@ -806,6 +807,14 @@ class HUIRoot extends LitElement { }); return; } + if (this.lovelace!.rawConfig?.strategy != null) { + showDashboardStrategyEditorDialog(this, { + lovelaceConfig: this.lovelace!.rawConfig, + saveConfig: this.lovelace!.saveConfig, + strategyConfig: this.lovelace!.rawConfig.strategy, + }); + return; + } this.lovelace!.setEditMode(true); } diff --git a/src/panels/lovelace/strategies/device-registry-detail/dialog-dashboard-strategy-editor.ts b/src/panels/lovelace/strategies/device-registry-detail/dialog-dashboard-strategy-editor.ts new file mode 100644 index 000000000000..85261badc8a2 --- /dev/null +++ b/src/panels/lovelace/strategies/device-registry-detail/dialog-dashboard-strategy-editor.ts @@ -0,0 +1,125 @@ +import { css, CSSResultGroup, html, LitElement, nothing } from "lit"; +import { customElement, property, query, state } from "lit/decorators"; +import { fireEvent, HASSDomEvent } from "../../../../common/dom/fire_event"; +import { createCloseHeading } from "../../../../components/ha-dialog"; +import { LovelaceStrategyConfig } from "../../../../data/lovelace"; +import { haStyle, haStyleDialog } from "../../../../resources/styles"; +import type { HomeAssistant } from "../../../../types"; +import { showSaveSuccessToast } from "../../../../util/toast-saved-success"; +import "../../editor/dashboard-strategy-editor/hui-dashboard-strategy-element-editor"; +import type { HuiDashboardStrategyElementEditor } from "../../editor/dashboard-strategy-editor/hui-dashboard-strategy-element-editor"; +import { ConfigChangedEvent } from "../../editor/hui-element-editor"; +import { GUIModeChangedEvent } from "../../editor/types"; +import type { DashboardStrategyEditorDialogParams } from "./show-dialog-dashboard-strategy-editor"; + +@customElement("dialog-dashboard-strategy-editor") +class DialogDashboardStrategyEditor extends LitElement { + @property({ attribute: false }) public hass!: HomeAssistant; + + @state() private _params?: DashboardStrategyEditorDialogParams; + + @state() private _strategyConfig?: LovelaceStrategyConfig; + + @state() private _GUImode = true; + + @state() private _guiModeAvailable? = true; + + @query("hui-dashboard-strategy-element-editor") + private _strategyEditorEl?: HuiDashboardStrategyElementEditor; + + public async showDialog( + params: DashboardStrategyEditorDialogParams + ): Promise { + this._params = params; + this._strategyConfig = params.strategyConfig; + await this.updateComplete; + } + + public closeDialog(): void { + this._params = undefined; + this._strategyConfig = undefined; + fireEvent(this, "dialog-closed", { dialog: this.localName }); + } + + private _handleConfigChanged(ev: HASSDomEvent) { + ev.stopPropagation(); + this._guiModeAvailable = ev.detail.guiModeAvailable; + this._strategyConfig = ev.detail.config as LovelaceStrategyConfig; + } + + private _handleGUIModeChanged(ev: HASSDomEvent): void { + ev.stopPropagation(); + this._GUImode = ev.detail.guiMode; + this._guiModeAvailable = ev.detail.guiModeAvailable; + } + + private _toggleMode(): void { + this._strategyEditorEl?.toggleMode(); + } + + private _opened() { + this._strategyEditorEl?.focusYamlEditor(); + } + + private async _save(): Promise { + await this._params!.saveConfig({ + ...this._params!.lovelaceConfig, + strategy: this._strategyConfig, + }); + showSaveSuccessToast(this, this.hass); + this.closeDialog(); + } + + protected render() { + if (!this._params || !this._strategyConfig) { + return nothing; + } + + return html` + + + ${this._strategyConfig !== undefined + ? html` + + ${this.hass!.localize( + !this._strategyEditorEl || this._GUImode + ? "ui.panel.lovelace.editor.edit_card.show_code_editor" + : "ui.panel.lovelace.editor.edit_card.show_visual_editor" + )} + + + ${this.hass!.localize("ui.common.save")} + + ` + : nothing} + + `; + } + + static get styles(): CSSResultGroup { + return [haStyle, haStyleDialog, css``]; + } +} + +declare global { + interface HTMLElementTagNameMap { + "dialog-dashboard-strategy-editor": DialogDashboardStrategyEditor; + } +} diff --git a/src/panels/lovelace/strategies/device-registry-detail/show-dialog-dashboard-strategy-editor.ts b/src/panels/lovelace/strategies/device-registry-detail/show-dialog-dashboard-strategy-editor.ts new file mode 100644 index 000000000000..8125a115f9a4 --- /dev/null +++ b/src/panels/lovelace/strategies/device-registry-detail/show-dialog-dashboard-strategy-editor.ts @@ -0,0 +1,25 @@ +import { fireEvent } from "../../../../common/dom/fire_event"; +import { + LovelaceConfig, + LovelaceStrategyConfig, +} from "../../../../data/lovelace"; + +export interface DashboardStrategyEditorDialogParams { + lovelaceConfig: LovelaceConfig; + saveConfig: (config: LovelaceConfig) => void; + strategyConfig: LovelaceStrategyConfig; +} + +export const loadDashboardStrategyEditorDialog = () => + import("./dialog-dashboard-strategy-editor"); + +export const showDashboardStrategyEditorDialog = ( + element: HTMLElement, + params: DashboardStrategyEditorDialogParams +): void => { + fireEvent(element, "show-dialog", { + dialogTag: "dialog-dashboard-strategy-editor", + dialogImport: loadDashboardStrategyEditorDialog, + dialogParams: params, + }); +}; diff --git a/src/panels/lovelace/strategies/get-strategy.ts b/src/panels/lovelace/strategies/get-strategy.ts index 59ae47b141a7..0fe14639806d 100644 --- a/src/panels/lovelace/strategies/get-strategy.ts +++ b/src/panels/lovelace/strategies/get-strategy.ts @@ -1,5 +1,10 @@ -import { LovelaceConfig, LovelaceViewConfig } from "../../../data/lovelace"; +import { + LovelaceConfig, + LovelaceStrategyConfig, + LovelaceViewConfig, +} from "../../../data/lovelace"; import { AsyncReturnType, HomeAssistant } from "../../../types"; +import { LovelaceGenericElementEditor } from "../types"; const MAX_WAIT_STRATEGY_LOAD = 5000; const CUSTOM_PREFIX = "custom:"; @@ -10,6 +15,7 @@ export interface LovelaceDashboardStrategy { hass: HomeAssistant; narrow: boolean | undefined; }): Promise; + getConfigElement?: () => LovelaceDashboardStrategyEditor; } export interface LovelaceViewStrategy { @@ -21,6 +27,11 @@ export interface LovelaceViewStrategy { }): Promise; } +export interface LovelaceDashboardStrategyEditor + extends LovelaceGenericElementEditor { + setConfig(config: LovelaceStrategyConfig): void; +} + const strategies: Record< string, () => Promise @@ -31,7 +42,7 @@ const strategies: Record< (await import("../../energy/strategies/energy-strategy")).EnergyStrategy, }; -const getLovelaceStrategy = async < +export const getLovelaceStrategy = async < T extends LovelaceDashboardStrategy | LovelaceViewStrategy, >( strategyType: string diff --git a/src/panels/lovelace/strategies/original-states-strategy.ts b/src/panels/lovelace/strategies/original-states-strategy.ts index 7c538d5fc14a..206910aa1001 100644 --- a/src/panels/lovelace/strategies/original-states-strategy.ts +++ b/src/panels/lovelace/strategies/original-states-strategy.ts @@ -4,6 +4,7 @@ import { getEnergyPreferences } from "../../../data/energy"; import { generateDefaultViewConfig } from "../common/generate-lovelace-config"; import { LovelaceDashboardStrategy, + LovelaceDashboardStrategyEditor, LovelaceViewStrategy, } from "./get-strategy"; @@ -36,7 +37,7 @@ export class OriginalStatesStrategy { // User can override default view. If they didn't, we will add one // that contains all entities. const view = generateDefaultViewConfig( - hass.areas, + info.view.strategy?.options?.no_area_group ? {} : hass.areas, hass.devices, hass.entities, hass.states, @@ -71,9 +72,21 @@ export class OriginalStatesStrategy { title: info.hass.config.location_name, views: [ { - strategy: { type: "original-states" }, + strategy: { + type: "original-states", + options: info.config?.strategy?.options, + }, }, ], }; } + + public static async getConfigElement(): Promise { + await import( + "../editor/dashboard-strategy-editor/hui-original-states-dashboard-strategy-editor" + ); + return document.createElement( + "hui-original-states-dashboard-strategy-editor" + ); + } }