Compare commits

..

4 Commits

Author SHA1 Message Date
Paul Bottein
b44542c83c Rename special rules for lovelace dashboard 2025-12-09 17:49:07 +01:00
Paul Bottein
36518bbce7 Remove special url path for lovelace 2025-12-09 15:17:57 +01:00
Paul Bottein
bb0c14cc57 Handle lovelace to home redirect 2025-12-09 15:12:58 +01:00
Paul Bottein
0cacc535ca Set home as default dashboard 2025-12-09 15:12:58 +01:00
10 changed files with 74 additions and 126 deletions

View File

@@ -246,7 +246,7 @@ export class HaGenericPicker extends LitElement {
private _unknownValue = memoizeOne( private _unknownValue = memoizeOne(
(value?: string, items?: (PickerComboBoxItem | string)[]) => { (value?: string, items?: (PickerComboBoxItem | string)[]) => {
if (value === undefined || value === null || value === "" || !items) { if (value === undefined || !items) {
return false; return false;
} }

View File

@@ -8,12 +8,11 @@ import {
mdiLightningBolt, mdiLightningBolt,
mdiPlayBoxMultiple, mdiPlayBoxMultiple,
mdiTooltipAccount, mdiTooltipAccount,
mdiViewDashboard,
} from "@mdi/js"; } from "@mdi/js";
import type { HomeAssistant, PanelInfo } from "../types"; import type { HomeAssistant, PanelInfo } from "../types";
/** Panel to show when no panel is picked. */ /** Panel to show when no panel is picked. */
export const DEFAULT_PANEL = "lovelace"; export const DEFAULT_PANEL = "home";
export const getLegacyDefaultPanelUrlPath = (): string | null => { export const getLegacyDefaultPanelUrlPath = (): string | null => {
const defaultPanel = window.localStorage.getItem("defaultPanel"); const defaultPanel = window.localStorage.getItem("defaultPanel");
@@ -33,10 +32,6 @@ export const getDefaultPanel = (hass: HomeAssistant): PanelInfo => {
}; };
export const getPanelNameTranslationKey = (panel: PanelInfo) => { export const getPanelNameTranslationKey = (panel: PanelInfo) => {
if (panel.url_path === "lovelace") {
return "panel.states" as const;
}
if (panel.url_path === "profile") { if (panel.url_path === "profile") {
return "panel.profile" as const; return "panel.profile" as const;
} }
@@ -77,8 +72,6 @@ export const getPanelIcon = (panel: PanelInfo): string | undefined => {
switch (panel.component_name) { switch (panel.component_name) {
case "profile": case "profile":
return "mdi:account"; return "mdi:account";
case "lovelace":
return "mdi:view-dashboard";
} }
} }
@@ -91,9 +84,8 @@ export const PANEL_ICON_PATHS = {
energy: mdiLightningBolt, energy: mdiLightningBolt,
history: mdiChartBox, history: mdiChartBox,
logbook: mdiFormatListBulletedType, logbook: mdiFormatListBulletedType,
lovelace: mdiViewDashboard,
profile: mdiAccount,
map: mdiTooltipAccount, map: mdiTooltipAccount,
profile: mdiAccount,
"media-browser": mdiPlayBoxMultiple, "media-browser": mdiPlayBoxMultiple,
todo: mdiClipboardList, todo: mdiClipboardList,
}; };

View File

@@ -56,6 +56,8 @@ export class HassRouterPage extends ReactiveElement {
private _initialLoadDone = false; private _initialLoadDone = false;
private _showLoadingScreenTimeout?: number;
private _computeTail = memoizeOne((route: Route) => { private _computeTail = memoizeOne((route: Route) => {
const dividerPos = route.path.indexOf("/", 1); const dividerPos = route.path.indexOf("/", 1);
return dividerPos === -1 return dividerPos === -1
@@ -153,7 +155,11 @@ export class HassRouterPage extends ReactiveElement {
? routeOptions.load() ? routeOptions.load()
: Promise.resolve(); : Promise.resolve();
let showLoadingScreenTimeout: undefined | number; // Clear any existing loading screen timeout from previous navigation
if (this._showLoadingScreenTimeout) {
clearTimeout(this._showLoadingScreenTimeout);
this._showLoadingScreenTimeout = undefined;
}
// Check when loading the page source failed. // Check when loading the page source failed.
loadProm.catch((err) => { loadProm.catch((err) => {
@@ -170,8 +176,9 @@ export class HassRouterPage extends ReactiveElement {
this.removeChild(this.lastChild!); this.removeChild(this.lastChild!);
} }
if (showLoadingScreenTimeout) { if (this._showLoadingScreenTimeout) {
clearTimeout(showLoadingScreenTimeout); clearTimeout(this._showLoadingScreenTimeout);
this._showLoadingScreenTimeout = undefined;
} }
// Show error screen // Show error screen
@@ -191,7 +198,7 @@ export class HassRouterPage extends ReactiveElement {
// That way we won't have a double fast flash on fast connections. // That way we won't have a double fast flash on fast connections.
let created = false; let created = false;
showLoadingScreenTimeout = window.setTimeout(() => { this._showLoadingScreenTimeout = window.setTimeout(() => {
if (created || this._currentPage !== newPage) { if (created || this._currentPage !== newPage) {
return; return;
} }

View File

@@ -60,7 +60,6 @@ export class DialogLovelaceDashboardDetail extends LitElement {
} }
const titleInvalid = !this._data.title || !this._data.title.trim(); const titleInvalid = !this._data.title || !this._data.title.trim();
const isLovelaceDashboard = this._params.urlPath === "lovelace";
return html` return html`
<ha-dialog <ha-dialog
@@ -85,20 +84,16 @@ export class DialogLovelaceDashboardDetail extends LitElement {
? this.hass.localize( ? this.hass.localize(
"ui.panel.config.lovelace.dashboards.cant_edit_yaml" "ui.panel.config.lovelace.dashboards.cant_edit_yaml"
) )
: isLovelaceDashboard : html`
? this.hass.localize( <ha-form
"ui.panel.config.lovelace.dashboards.cant_edit_lovelace" .schema=${this._schema(this._params)}
) .data=${this._data}
: html` .hass=${this.hass}
<ha-form .error=${this._error}
.schema=${this._schema(this._params)} .computeLabel=${this._computeLabel}
.data=${this._data} @value-changed=${this._valueChanged}
.hass=${this.hass} ></ha-form>
.error=${this._error} `}
.computeLabel=${this._computeLabel}
@value-changed=${this._valueChanged}
></ha-form>
`}
</div> </div>
${this._params.urlPath ${this._params.urlPath
? html` ? html`

View File

@@ -30,7 +30,6 @@ import "../../../../components/ha-md-list-item";
import "../../../../components/ha-svg-icon"; import "../../../../components/ha-svg-icon";
import "../../../../components/ha-tooltip"; import "../../../../components/ha-tooltip";
import { saveFrontendSystemData } from "../../../../data/frontend"; import { saveFrontendSystemData } from "../../../../data/frontend";
import type { LovelacePanelConfig } from "../../../../data/lovelace";
import type { LovelaceRawConfig } from "../../../../data/lovelace/config/types"; import type { LovelaceRawConfig } from "../../../../data/lovelace/config/types";
import { import {
isStrategyDashboard, isStrategyDashboard,
@@ -306,23 +305,7 @@ export class HaConfigLovelaceDashboards extends LitElement {
private _getItems = memoize( private _getItems = memoize(
(dashboards: LovelaceDashboard[], defaultUrlPath: string | null) => { (dashboards: LovelaceDashboard[], defaultUrlPath: string | null) => {
const mode = (this.hass.panels?.lovelace?.config as LovelacePanelConfig) const result: DataTableItem[] = [];
.mode;
const isDefault = defaultUrlPath === "lovelace";
const result: DataTableItem[] = [
{
icon: "mdi:view-dashboard",
title: this.hass.localize("panel.states"),
default: isDefault,
show_in_sidebar: true,
require_admin: false,
url_path: "lovelace",
mode: mode,
filename: mode === "yaml" ? "ui-lovelace.yaml" : "",
type: "built_in",
localized_type: this._localizeType("built_in"),
},
];
PANEL_DASHBOARDS.forEach((panel) => { PANEL_DASHBOARDS.forEach((panel) => {
const panelInfo = this.hass.panels[panel]; const panelInfo = this.hass.panels[panel];

View File

@@ -88,18 +88,16 @@ export class HuiEnergyGasGraphCard
return html` return html`
<ha-card> <ha-card>
${this._config.title <div class="card-header">
? html` <div class="card-header"> <span>${this._config.title ? this._config.title : nothing}</span>
<span>${this._config.title}</span> ${this._total
${this._total ? html`<hui-energy-graph-chip
? html`<hui-energy-graph-chip .tooltip=${this._formatTotal(this._total)}
.tooltip=${this._formatTotal(this._total)} >
> ${formatNumber(this._total, this.hass.locale)} ${this._unit}
${formatNumber(this._total, this.hass.locale)} ${this._unit} </hui-energy-graph-chip>`
</hui-energy-graph-chip>` : nothing}
: nothing} </div>
</div>`
: nothing}
<div <div
class="content ${classMap({ class="content ${classMap({
"has-header": !!this._config.title, "has-header": !!this._config.title,

View File

@@ -90,18 +90,16 @@ export class HuiEnergySolarGraphCard
return html` return html`
<ha-card> <ha-card>
${this._config.title <div class="card-header">
? html` <div class="card-header"> <span>${this._config.title ? this._config.title : nothing}</span>
<span>${this._config.title}</span> ${this._total
${this._total ? html`<hui-energy-graph-chip
? html`<hui-energy-graph-chip .tooltip=${this._formatTotal(this._total)}
.tooltip=${this._formatTotal(this._total)} >
> ${formatNumber(this._total, this.hass.locale)} kWh
${formatNumber(this._total, this.hass.locale)} kWh </hui-energy-graph-chip>`
</hui-energy-graph-chip>` : nothing}
: nothing} </div>
</div>`
: nothing}
<div <div
class="content ${classMap({ class="content ${classMap({
"has-header": !!this._config.title, "has-header": !!this._config.title,

View File

@@ -103,21 +103,19 @@ export class HuiEnergyUsageGraphCard
return html` return html`
<ha-card> <ha-card>
${this._config.title <div class="card-header">
? html` <div class="card-header"> <span>${this._config.title ? this._config.title : nothing}</span>
<span>${this._config.title}</span> ${this._total
${this._total ? html`<hui-energy-graph-chip
? html`<hui-energy-graph-chip .tooltip=${this._formatTotal(this._total)}
.tooltip=${this._formatTotal(this._total)} >
> ${this.hass.localize(
${this.hass.localize( "ui.panel.lovelace.cards.energy.energy_usage_graph.total_usage",
"ui.panel.lovelace.cards.energy.energy_usage_graph.total_usage", { num: formatNumber(this._total, this.hass.locale) }
{ num: formatNumber(this._total, this.hass.locale) } )}
)} </hui-energy-graph-chip>`
</hui-energy-graph-chip>` : nothing}
: nothing} </div>
</div>`
: nothing}
<div <div
class="content ${classMap({ class="content ${classMap({
"has-header": !!this._config.title, "has-header": !!this._config.title,

View File

@@ -88,18 +88,16 @@ export class HuiEnergyWaterGraphCard
return html` return html`
<ha-card> <ha-card>
${this._config.title <div class="card-header">
? html` <div class="card-header"> <span>${this._config.title ? this._config.title : nothing}</span>
<span>${this._config.title ? this._config.title : nothing}</span> ${this._total
${this._total ? html`<hui-energy-graph-chip
? html`<hui-energy-graph-chip .tooltip=${this._formatTotal(this._total)}
.tooltip=${this._formatTotal(this._total)} >
> ${formatNumber(this._total, this.hass.locale)} ${this._unit}
${formatNumber(this._total, this.hass.locale)} ${this._unit} </hui-energy-graph-chip>`
</hui-energy-graph-chip>` : nothing}
: nothing} </div>
</div>`
: nothing}
<div <div
class="content ${classMap({ class="content ${classMap({
"has-header": !!this._config.title, "has-header": !!this._config.title,

View File

@@ -3,6 +3,7 @@ import type { UnsubscribeFunc } from "home-assistant-js-websocket";
import type { PropertyValues, TemplateResult } from "lit"; import type { PropertyValues, TemplateResult } from "lit";
import { html, LitElement } from "lit"; import { html, LitElement } from "lit";
import { customElement, property, state } from "lit/decorators"; import { customElement, property, state } from "lit/decorators";
import { navigate } from "../../common/navigate";
import { constructUrlCurrentPath } from "../../common/url/construct-url"; import { constructUrlCurrentPath } from "../../common/url/construct-url";
import { import {
addSearchParam, addSearchParam,
@@ -10,6 +11,7 @@ import {
} from "../../common/url/search-params"; } from "../../common/url/search-params";
import { debounce } from "../../common/util/debounce"; import { debounce } from "../../common/util/debounce";
import { deepEqual } from "../../common/util/deep-equal"; import { deepEqual } from "../../common/util/deep-equal";
import "../../components/ha-button";
import { domainToName } from "../../data/integration"; import { domainToName } from "../../data/integration";
import { subscribeLovelaceUpdates } from "../../data/lovelace"; import { subscribeLovelaceUpdates } from "../../data/lovelace";
import type { import type {
@@ -34,7 +36,6 @@ import { checkLovelaceConfig } from "./common/check-lovelace-config";
import { loadLovelaceResources } from "./common/load-resources"; import { loadLovelaceResources } from "./common/load-resources";
import { showSaveDialog } from "./editor/show-save-config-dialog"; import { showSaveDialog } from "./editor/show-save-config-dialog";
import "./hui-root"; import "./hui-root";
import "../../components/ha-button";
import { generateLovelaceDashboardStrategy } from "./strategies/get-strategy"; import { generateLovelaceDashboardStrategy } from "./strategies/get-strategy";
import type { Lovelace } from "./types"; import type { Lovelace } from "./types";
@@ -97,11 +98,6 @@ export class LovelacePanel extends LitElement {
this.lovelace.rawConfig, this.lovelace.rawConfig,
this.lovelace.mode this.lovelace.mode
); );
} else if (this.lovelace && this.lovelace.mode === "generated") {
// When lovelace is generated, we re-generate each time a user goes
// to the states panel to make sure new entities are shown.
this._panelState = "loading";
this._regenerateConfig();
} else if (this._fetchConfigOnConnect) { } else if (this._fetchConfigOnConnect) {
// Config was changed when we were not at the lovelace panel // Config was changed when we were not at the lovelace panel
this._fetchConfig(false); this._fetchConfig(false);
@@ -296,15 +292,6 @@ export class LovelacePanel extends LitElement {
} }
}; };
private async _regenerateConfig() {
const conf = await generateLovelaceDashboardStrategy(
DEFAULT_CONFIG,
this.hass!
);
this._setLovelaceConfig(conf, DEFAULT_CONFIG, "generated");
this._panelState = "loaded";
}
private async _subscribeUpdates() { private async _subscribeUpdates() {
this._unsubUpdates = subscribeLovelaceUpdates( this._unsubUpdates = subscribeLovelaceUpdates(
this.hass!.connection, this.hass!.connection,
@@ -340,7 +327,7 @@ export class LovelacePanel extends LitElement {
} }
public get urlPath() { public get urlPath() {
return this.panel!.url_path === "lovelace" ? null : this.panel!.url_path; return this.panel!.url_path;
} }
private _forceFetchConfig() { private _forceFetchConfig() {
@@ -352,7 +339,7 @@ export class LovelacePanel extends LitElement {
let conf: LovelaceConfig; let conf: LovelaceConfig;
let rawConf: LovelaceRawConfig | undefined; let rawConf: LovelaceRawConfig | undefined;
let confMode: Lovelace["mode"] = this.panel!.config.mode; const confMode: Lovelace["mode"] = this.panel!.config.mode;
let confProm: Promise<LovelaceRawConfig> | undefined; let confProm: Promise<LovelaceRawConfig> | undefined;
const preloadWindow = window as WindowWithPreloads; const preloadWindow = window as WindowWithPreloads;
@@ -404,16 +391,8 @@ export class LovelacePanel extends LitElement {
this._errorMsg = err.message; this._errorMsg = err.message;
return; return;
} }
if (!this.hass?.entities || !this.hass.devices || !this.hass.areas) { navigate("/home");
// We need these to generate a dashboard, wait for them return;
return;
}
conf = await generateLovelaceDashboardStrategy(
DEFAULT_CONFIG,
this.hass!
);
rawConf = DEFAULT_CONFIG;
confMode = "generated";
} finally { } finally {
this._loading = false; this._loading = false;
// Ignore updates for another 2 seconds. // Ignore updates for another 2 seconds.