diff --git a/src/data/frontend.ts b/src/data/frontend.ts index 26957d0b29..edff26c1f9 100644 --- a/src/data/frontend.ts +++ b/src/data/frontend.ts @@ -17,9 +17,17 @@ export interface CoreFrontendSystemData { onboarded_date?: string; } +export interface HomeQuickLink { + name: string; + icon?: string; + navigation_path: string; +} + export interface HomeFrontendSystemData { favorite_entities?: string[]; welcome_banner_dismissed?: boolean; + hidden_summaries?: string[]; + quick_links?: HomeQuickLink[]; } declare global { diff --git a/src/panels/home/dialogs/dialog-edit-home.ts b/src/panels/home/dialogs/dialog-edit-home.ts index cfc67441b9..64a9d37985 100644 --- a/src/panels/home/dialogs/dialog-edit-home.ts +++ b/src/panels/home/dialogs/dialog-edit-home.ts @@ -1,17 +1,35 @@ +import { mdiClose, mdiPlus } from "@mdi/js"; import { css, html, LitElement, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; +import { repeat } from "lit/directives/repeat"; import { fireEvent } from "../../../common/dom/fire_event"; import "../../../components/entity/ha-entities-picker"; import "../../../components/ha-alert"; import "../../../components/ha-button"; import "../../../components/ha-dialog-footer"; import "../../../components/ha-dialog"; -import type { HomeFrontendSystemData } from "../../../data/frontend"; +import "../../../components/ha-icon-button"; +import "../../../components/ha-icon-picker"; +import "../../../components/ha-navigation-picker"; +import "../../../components/ha-svg-icon"; +import "../../../components/ha-switch"; +import "../../../components/ha-textfield"; +import type { + HomeFrontendSystemData, + HomeQuickLink, +} from "../../../data/frontend"; import type { HassDialog } from "../../../dialogs/make-dialog-manager"; +import { + HOME_SUMMARIES, + getSummaryLabel, + type HomeSummary, +} from "../../../panels/lovelace/strategies/home/helpers/home-summaries"; import { haStyleDialog } from "../../../resources/styles"; import type { HomeAssistant } from "../../../types"; import type { EditHomeDialogParams } from "./show-dialog-edit-home"; +const ALL_SUMMARY_KEYS = [...HOME_SUMMARIES, "weather"] as const; + @customElement("dialog-edit-home") export class DialogEditHome extends LitElement @@ -50,6 +68,9 @@ export class DialogEditHome return nothing; } + const hiddenSummaries = new Set(this._config?.hidden_summaries || []); + const quickLinks = this._config?.quick_links || []; + return html` +

+ ${this.hass.localize("ui.panel.home.editor.summaries")} +

+

+ ${this.hass.localize("ui.panel.home.editor.summaries_description")} +

+
+ ${ALL_SUMMARY_KEYS.map((key) => { + const label = this._getSummaryLabel(key); + return html` + + `; + })} +
+ +

+ ${this.hass.localize("ui.panel.home.editor.quick_links")} +

+

+ ${this.hass.localize("ui.panel.home.editor.quick_links_description")} +

+ + ${this.hass.localize("ui.panel.home.editor.areas_hint", { areas_page: html` 0 ? [...hiddenSummaries] : undefined, + }; + } + + private _quickLinkChanged( + index: number, + field: keyof HomeQuickLink, + value: string + ): void { + const quickLinks = [...(this._config?.quick_links || [])]; + quickLinks[index] = { ...quickLinks[index], [field]: value }; + this._config = { + ...this._config, + quick_links: quickLinks, + }; + } + + private _removeQuickLink(index: number): void { + const quickLinks = [...(this._config?.quick_links || [])]; + quickLinks.splice(index, 1); + this._config = { + ...this._config, + quick_links: quickLinks.length > 0 ? quickLinks : undefined, + }; + } + + private _addQuickLink(): void { + const quickLinks = [...(this._config?.quick_links || [])]; + quickLinks.push({ name: "", icon: "mdi:link", navigation_path: "" }); + this._config = { + ...this._config, + quick_links: quickLinks, + }; + } + private _favoriteEntitiesChanged(ev: CustomEvent): void { const entities = ev.detail.value as string[]; this._config = { @@ -148,6 +314,63 @@ export class DialogEditHome color: var(--secondary-text-color); } + .section-header { + font-size: 16px; + font-weight: 500; + margin: var(--ha-space-6) 0 var(--ha-space-1) 0; + } + + .section-description { + margin: 0 0 var(--ha-space-2) 0; + color: var(--secondary-text-color); + font-size: 14px; + } + + .summary-toggles { + display: flex; + flex-direction: column; + gap: var(--ha-space-2); + } + + .summary-toggle { + display: flex; + align-items: center; + gap: var(--ha-space-3); + padding: var(--ha-space-1) 0; + cursor: pointer; + } + + .quick-links { + display: flex; + flex-direction: column; + gap: var(--ha-space-3); + } + + .quick-link-row { + display: flex; + align-items: center; + gap: var(--ha-space-2); + } + + .quick-link-row ha-icon-picker { + width: 80px; + flex-shrink: 0; + } + + .quick-link-row ha-textfield { + flex: 1; + min-width: 0; + } + + .quick-link-row ha-navigation-picker { + flex: 2; + min-width: 0; + } + + .quick-link-row ha-icon-button { + flex-shrink: 0; + } + ha-entities-picker { display: block; } diff --git a/src/panels/home/ha-panel-home.ts b/src/panels/home/ha-panel-home.ts index 26c2b942a4..acca053820 100644 --- a/src/panels/home/ha-panel-home.ts +++ b/src/panels/home/ha-panel-home.ts @@ -318,6 +318,8 @@ class PanelHome extends LitElement { type: "home", favorite_entities: this._config.favorite_entities, home_panel: true, + hidden_summaries: this._config.hidden_summaries, + quick_links: this._config.quick_links, }, }; diff --git a/src/panels/lovelace/strategies/home/home-dashboard-strategy.ts b/src/panels/lovelace/strategies/home/home-dashboard-strategy.ts index c6b5fc3b1c..40b7a4f5f0 100644 --- a/src/panels/lovelace/strategies/home/home-dashboard-strategy.ts +++ b/src/panels/lovelace/strategies/home/home-dashboard-strategy.ts @@ -17,6 +17,8 @@ export interface HomeDashboardStrategyConfig { type: "home"; favorite_entities?: string[]; home_panel?: boolean; + hidden_summaries?: string[]; + quick_links?: { name: string; icon?: string; navigation_path: string }[]; } @customElement("home-dashboard-strategy") @@ -94,6 +96,8 @@ export class HomeDashboardStrategy extends ReactiveElement { type: "home-overview", favorite_entities: config.favorite_entities, home_panel: config.home_panel, + hidden_summaries: config.hidden_summaries, + quick_links: config.quick_links, } satisfies HomeOverviewViewStrategyConfig, }, ...areaViews, diff --git a/src/panels/lovelace/strategies/home/home-overview-view-strategy.ts b/src/panels/lovelace/strategies/home/home-overview-view-strategy.ts index 75a2184a1f..7d5e3a04ca 100644 --- a/src/panels/lovelace/strategies/home/home-overview-view-strategy.ts +++ b/src/panels/lovelace/strategies/home/home-overview-view-strategy.ts @@ -39,6 +39,8 @@ export interface HomeOverviewViewStrategyConfig { type: "home-overview"; favorite_entities?: string[]; home_panel?: boolean; + hidden_summaries?: string[]; + quick_links?: { name: string; icon?: string; navigation_path: string }[]; } const computeAreaCard = ( @@ -254,6 +256,8 @@ export class HomeOverviewViewStrategy extends ReactiveElement { ) ?? false); + const hiddenSummaries = new Set(config.hidden_summaries || []); + // Build summary cards (used in both mobile section and sidebar) const summaryCards: LovelaceCardConfig[] = [ // Repairs card - only visible to admins, hides when empty @@ -280,6 +284,7 @@ export class HomeOverviewViewStrategy extends ReactiveElement { hide_empty: true, } satisfies DiscoveredDevicesCardConfig, hasLights && + !hiddenSummaries.has("light") && ({ type: "home-summary", summary: "light", @@ -289,6 +294,7 @@ export class HomeOverviewViewStrategy extends ReactiveElement { }, } satisfies HomeSummaryCard), hasClimate && + !hiddenSummaries.has("climate") && ({ type: "home-summary", summary: "climate", @@ -298,6 +304,7 @@ export class HomeOverviewViewStrategy extends ReactiveElement { }, } satisfies HomeSummaryCard), hasSecurity && + !hiddenSummaries.has("security") && ({ type: "home-summary", summary: "security", @@ -307,6 +314,7 @@ export class HomeOverviewViewStrategy extends ReactiveElement { }, } satisfies HomeSummaryCard), hasMediaPlayers && + !hiddenSummaries.has("media_players") && ({ type: "home-summary", summary: "media_players", @@ -316,6 +324,7 @@ export class HomeOverviewViewStrategy extends ReactiveElement { }, } satisfies HomeSummaryCard), weatherEntity && + !hiddenSummaries.has("weather") && ({ type: "tile", entity: weatherEntity, @@ -325,6 +334,7 @@ export class HomeOverviewViewStrategy extends ReactiveElement { state_content: ["temperature", "state"], } satisfies TileCardConfig), hasEnergy && + !hiddenSummaries.has("energy") && ({ type: "home-summary", summary: "energy", @@ -335,6 +345,21 @@ export class HomeOverviewViewStrategy extends ReactiveElement { : "/energy?historyBack=1", }, } satisfies HomeSummaryCard), + // Quick links (chip-like cards without summary line) + ...(config.quick_links || []).map( + (link) => + ({ + type: "tile", + entity: "zone.home", + name: link.name, + icon: link.icon || "mdi:link", + hide_state: true, + tap_action: { + action: "navigate", + navigation_path: link.navigation_path, + }, + }) satisfies TileCardConfig + ), ].filter(Boolean) as LovelaceCardConfig[]; // Build summary cards for sidebar (full width: columns 12) diff --git a/src/translations/en.json b/src/translations/en.json index 3c56581cd3..966517a12b 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -2444,7 +2444,15 @@ "favorite_entities_helper": "Display your favorite entities. Home Assistant will still suggest based on commonly used up to 8 slots.", "save_failed": "Failed to save Overview page configuration", "areas_hint": "You can rearrange your floors and areas in the order that best represents your house on the {areas_page}.", - "areas_page": "areas page" + "areas_page": "areas page", + "summaries": "Summaries", + "summaries_description": "Choose which summaries to show on your overview page.", + "quick_links": "Quick links", + "quick_links_description": "Add links to dashboards, sidebar items, or other pages.", + "add_quick_link": "Add quick link", + "quick_link_name": "Name", + "quick_link_icon": "Icon", + "quick_link_path": "Navigation path" }, "new_overview_dialog": { "title": "Welcome to your new overview",