mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-23 17:26:42 +00:00
Add support for multiple Lovelace dashboards (#4967)
* Add support for multiple Lovelace dashboards * Fix navigation, add to cast, revert resource loading * Change resource logic * Lint + cast fix * Comments * Fixes * Console.bye * Lint" Co-authored-by: Bram Kragten <mail@bramkragten.nl>
This commit is contained in:
parent
25d6427aed
commit
1d052fa5bb
@ -15,6 +15,7 @@ import {
|
||||
import {
|
||||
LovelaceConfig,
|
||||
getLovelaceCollection,
|
||||
fetchResources,
|
||||
} from "../../../../src/data/lovelace";
|
||||
import "./hc-launch-screen";
|
||||
import { castContext } from "../cast_context";
|
||||
@ -23,6 +24,8 @@ import { ReceiverStatusMessage } from "../../../../src/cast/sender_messages";
|
||||
import { loadLovelaceResources } from "../../../../src/panels/lovelace/common/load-resources";
|
||||
import { isNavigationClick } from "../../../../src/common/dom/is-navigation-click";
|
||||
|
||||
let resourcesLoaded = false;
|
||||
|
||||
@customElement("hc-main")
|
||||
export class HcMain extends HassElement {
|
||||
@property() private _showDemo = false;
|
||||
@ -34,6 +37,7 @@ export class HcMain extends HassElement {
|
||||
@property() private _error?: string;
|
||||
|
||||
private _unsubLovelace?: UnsubscribeFunc;
|
||||
private _urlPath?: string | null;
|
||||
|
||||
public processIncomingMessage(msg: HassMessage) {
|
||||
if (msg.type === "connect") {
|
||||
@ -108,6 +112,7 @@ export class HcMain extends HassElement {
|
||||
if (this.hass) {
|
||||
status.hassUrl = this.hass.auth.data.hassUrl;
|
||||
status.lovelacePath = this._lovelacePath!;
|
||||
status.urlPath = this._urlPath;
|
||||
}
|
||||
|
||||
if (senderId) {
|
||||
@ -163,8 +168,19 @@ export class HcMain extends HassElement {
|
||||
this._error = "Cannot show Lovelace because we're not connected.";
|
||||
return;
|
||||
}
|
||||
if (!this._unsubLovelace) {
|
||||
const llColl = getLovelaceCollection(this.hass!.connection);
|
||||
if (!resourcesLoaded) {
|
||||
resourcesLoaded = true;
|
||||
loadLovelaceResources(
|
||||
await fetchResources(this.hass!.connection),
|
||||
this.hass!.auth.data.hassUrl
|
||||
);
|
||||
}
|
||||
if (!this._unsubLovelace || this._urlPath !== msg.urlPath) {
|
||||
this._urlPath = msg.urlPath;
|
||||
if (this._unsubLovelace) {
|
||||
this._unsubLovelace();
|
||||
}
|
||||
const llColl = getLovelaceCollection(this.hass!.connection, msg.urlPath);
|
||||
// We first do a single refresh because we need to check if there is LL
|
||||
// configuration.
|
||||
try {
|
||||
@ -194,12 +210,6 @@ export class HcMain extends HassElement {
|
||||
private _handleNewLovelaceConfig(lovelaceConfig: LovelaceConfig) {
|
||||
castContext.setApplicationState(lovelaceConfig.title!);
|
||||
this._lovelaceConfig = lovelaceConfig;
|
||||
if (lovelaceConfig.resources) {
|
||||
loadLovelaceResources(
|
||||
lovelaceConfig.resources,
|
||||
this.hass!.auth.data.hassUrl
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private _handleShowDemo(_msg: ShowDemoMessage) {
|
||||
|
@ -21,6 +21,7 @@ export interface ConnectMessage extends BaseCastMessage {
|
||||
export interface ShowLovelaceViewMessage extends BaseCastMessage {
|
||||
type: "show_lovelace_view";
|
||||
viewPath: string | number | null;
|
||||
urlPath: string | null;
|
||||
}
|
||||
|
||||
export interface ShowDemoMessage extends BaseCastMessage {
|
||||
@ -43,11 +44,13 @@ export const castSendAuth = (cast: CastManager, auth: Auth) =>
|
||||
|
||||
export const castSendShowLovelaceView = (
|
||||
cast: CastManager,
|
||||
viewPath: ShowLovelaceViewMessage["viewPath"]
|
||||
viewPath: ShowLovelaceViewMessage["viewPath"],
|
||||
urlPath?: string | null
|
||||
) =>
|
||||
cast.sendMessage({
|
||||
type: "show_lovelace_view",
|
||||
viewPath,
|
||||
urlPath: urlPath || null,
|
||||
});
|
||||
|
||||
export const castSendShowDemo = (cast: CastManager) =>
|
||||
|
@ -8,6 +8,7 @@ export interface ReceiverStatusMessage extends BaseCastMessage {
|
||||
showDemo: boolean;
|
||||
hassUrl?: string;
|
||||
lovelacePath?: string | number | null;
|
||||
urlPath?: string | null;
|
||||
}
|
||||
|
||||
export type SenderMessage = ReceiverStatusMessage;
|
||||
|
@ -46,7 +46,18 @@ const SORT_VALUE_URL_PATHS = {
|
||||
config: 11,
|
||||
};
|
||||
|
||||
const panelSorter = (a, b) => {
|
||||
const panelSorter = (a: PanelInfo, b: PanelInfo) => {
|
||||
// Put all the Lovelace at the top.
|
||||
const aLovelace = a.component_name === "lovelace";
|
||||
const bLovelace = b.component_name === "lovelace";
|
||||
|
||||
if (aLovelace && !bLovelace) {
|
||||
return -1;
|
||||
}
|
||||
if (bLovelace) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
const aBuiltIn = a.url_path in SORT_VALUE_URL_PATHS;
|
||||
const bBuiltIn = b.url_path in SORT_VALUE_URL_PATHS;
|
||||
|
||||
|
@ -1,14 +1,22 @@
|
||||
import { HomeAssistant } from "../types";
|
||||
import { Connection, getCollection } from "home-assistant-js-websocket";
|
||||
import {
|
||||
Connection,
|
||||
getCollection,
|
||||
HassEventBase,
|
||||
} from "home-assistant-js-websocket";
|
||||
import { HASSDomEvent } from "../common/dom/fire_event";
|
||||
|
||||
export interface LovelaceConfig {
|
||||
title?: string;
|
||||
views: LovelaceViewConfig[];
|
||||
background?: string;
|
||||
resources?: Array<{ type: "css" | "js" | "module" | "html"; url: string }>;
|
||||
}
|
||||
|
||||
export type LovelaceResources = Array<{
|
||||
type: "css" | "js" | "module" | "html";
|
||||
url: string;
|
||||
}>;
|
||||
|
||||
export interface LovelaceViewConfig {
|
||||
index?: number;
|
||||
title?: string;
|
||||
@ -95,47 +103,78 @@ export type ActionConfig =
|
||||
| NoActionConfig
|
||||
| CustomActionConfig;
|
||||
|
||||
type LovelaceUpdatedEvent = HassEventBase & {
|
||||
event_type: "lovelace_updated";
|
||||
data: {
|
||||
url_path: string | null;
|
||||
mode: "yaml" | "storage";
|
||||
};
|
||||
};
|
||||
|
||||
export const fetchResources = (conn: Connection): Promise<LovelaceResources> =>
|
||||
conn.sendMessagePromise({
|
||||
type: "lovelace/resources",
|
||||
});
|
||||
export const fetchConfig = (
|
||||
conn: Connection,
|
||||
urlPath: string | null,
|
||||
force: boolean
|
||||
): Promise<LovelaceConfig> =>
|
||||
conn.sendMessagePromise({
|
||||
type: "lovelace/config",
|
||||
url_path: urlPath,
|
||||
force,
|
||||
});
|
||||
|
||||
export const saveConfig = (
|
||||
hass: HomeAssistant,
|
||||
urlPath: string | null,
|
||||
config: LovelaceConfig
|
||||
): Promise<void> =>
|
||||
hass.callWS({
|
||||
type: "lovelace/config/save",
|
||||
url_path: urlPath,
|
||||
config,
|
||||
});
|
||||
|
||||
export const deleteConfig = (hass: HomeAssistant): Promise<void> =>
|
||||
export const deleteConfig = (
|
||||
hass: HomeAssistant,
|
||||
urlPath: string | null
|
||||
): Promise<void> =>
|
||||
hass.callWS({
|
||||
type: "lovelace/config/delete",
|
||||
url_path: urlPath,
|
||||
});
|
||||
|
||||
export const subscribeLovelaceUpdates = (
|
||||
conn: Connection,
|
||||
urlPath: string | null,
|
||||
onChange: () => void
|
||||
) => conn.subscribeEvents(onChange, "lovelace_updated");
|
||||
) =>
|
||||
conn.subscribeEvents<LovelaceUpdatedEvent>((ev) => {
|
||||
if (ev.data.url_path === urlPath) {
|
||||
onChange();
|
||||
}
|
||||
}, "lovelace_updated");
|
||||
|
||||
export const getLovelaceCollection = (conn: Connection) =>
|
||||
export const getLovelaceCollection = (
|
||||
conn: Connection,
|
||||
urlPath: string | null = null
|
||||
) =>
|
||||
getCollection(
|
||||
conn,
|
||||
"_lovelace",
|
||||
(conn2) => fetchConfig(conn2, false),
|
||||
`_lovelace_${urlPath ?? ""}`,
|
||||
(conn2) => fetchConfig(conn2, urlPath, false),
|
||||
(_conn, store) =>
|
||||
subscribeLovelaceUpdates(conn, () =>
|
||||
fetchConfig(conn, false).then((config) => store.setState(config, true))
|
||||
subscribeLovelaceUpdates(conn, urlPath, () =>
|
||||
fetchConfig(conn, urlPath, false).then((config) =>
|
||||
store.setState(config, true)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
export interface WindowWithLovelaceProm extends Window {
|
||||
llConfProm?: Promise<LovelaceConfig>;
|
||||
llResProm?: Promise<LovelaceResources>;
|
||||
}
|
||||
|
||||
export interface ActionHandlerOptions {
|
||||
|
@ -15,7 +15,11 @@ import { subscribeThemes } from "../data/ws-themes";
|
||||
import { subscribeUser } from "../data/ws-user";
|
||||
import { HomeAssistant } from "../types";
|
||||
import { hassUrl } from "../data/auth";
|
||||
import { fetchConfig, WindowWithLovelaceProm } from "../data/lovelace";
|
||||
import {
|
||||
fetchConfig,
|
||||
fetchResources,
|
||||
WindowWithLovelaceProm,
|
||||
} from "../data/lovelace";
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
@ -90,7 +94,12 @@ window.hassConnection.then(({ conn }) => {
|
||||
subscribeUser(conn, noop);
|
||||
|
||||
if (location.pathname === "/" || location.pathname.startsWith("/lovelace/")) {
|
||||
(window as WindowWithLovelaceProm).llConfProm = fetchConfig(conn, false);
|
||||
(window as WindowWithLovelaceProm).llConfProm = fetchConfig(
|
||||
conn,
|
||||
null,
|
||||
false
|
||||
);
|
||||
(window as WindowWithLovelaceProm).llResProm = fetchResources(conn);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -1,13 +1,13 @@
|
||||
import { loadModule, loadCSS, loadJS } from "../../../common/dom/load_resource";
|
||||
|
||||
import { LovelaceConfig } from "../../../data/lovelace";
|
||||
import { LovelaceResources } from "../../../data/lovelace";
|
||||
|
||||
// CSS and JS should only be imported once. Modules and HTML are safe.
|
||||
const CSS_CACHE = {};
|
||||
const JS_CACHE = {};
|
||||
|
||||
export const loadLovelaceResources = (
|
||||
resources: NonNullable<LovelaceConfig["resources"]>,
|
||||
resources: NonNullable<LovelaceResources>,
|
||||
hassUrl: string
|
||||
) =>
|
||||
resources.forEach((resource) => {
|
||||
|
@ -22,7 +22,7 @@ export const addEntitiesToLovelaceView = async (
|
||||
}
|
||||
if (!lovelaceConfig) {
|
||||
try {
|
||||
lovelaceConfig = await fetchConfig(hass.connection, false);
|
||||
lovelaceConfig = await fetchConfig(hass.connection, null, false);
|
||||
} catch {
|
||||
alert(
|
||||
hass.localize(
|
||||
@ -41,7 +41,7 @@ export const addEntitiesToLovelaceView = async (
|
||||
if (!saveConfigFunc) {
|
||||
saveConfigFunc = async (newConfig: LovelaceConfig): Promise<void> => {
|
||||
try {
|
||||
await saveConfig(hass!, newConfig);
|
||||
await saveConfig(hass!, null, newConfig);
|
||||
} catch {
|
||||
alert(
|
||||
hass.localize("ui.panel.config.devices.add_entities.saving_failed")
|
||||
|
@ -39,6 +39,7 @@ export interface CastConfig {
|
||||
icon: string;
|
||||
name: string;
|
||||
view: string | number;
|
||||
dashboard?: string;
|
||||
// Hide the row if either unsupported browser or no API available.
|
||||
hide_if_unavailable: boolean;
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ import {
|
||||
subscribeLovelaceUpdates,
|
||||
WindowWithLovelaceProm,
|
||||
deleteConfig,
|
||||
fetchResources,
|
||||
} from "../../data/lovelace";
|
||||
import "../../layouts/hass-loading-screen";
|
||||
import "../../layouts/hass-error-screen";
|
||||
@ -24,6 +25,7 @@ import {
|
||||
import { showSaveDialog } from "./editor/show-save-config-dialog";
|
||||
import { generateLovelaceConfigFromHass } from "./common/generate-lovelace-config";
|
||||
import { showToast } from "../../util/toast";
|
||||
import { loadLovelaceResources } from "./common/load-resources";
|
||||
|
||||
(window as any).loadCardHelpers = () => import("./custom-card-helpers");
|
||||
|
||||
@ -32,6 +34,7 @@ interface LovelacePanelConfig {
|
||||
}
|
||||
|
||||
let editorLoaded = false;
|
||||
let resourcesLoaded = false;
|
||||
|
||||
class LovelacePanel extends LitElement {
|
||||
@property() public panel?: PanelInfo<LovelacePanelConfig>;
|
||||
@ -67,12 +70,12 @@ class LovelacePanel extends LitElement {
|
||||
if (state === "loaded") {
|
||||
return html`
|
||||
<hui-root
|
||||
.hass="${this.hass}"
|
||||
.lovelace="${this.lovelace}"
|
||||
.route="${this.route}"
|
||||
.columns="${this._columns}"
|
||||
.hass=${this.hass}
|
||||
.lovelace=${this.lovelace}
|
||||
.route=${this.route}
|
||||
.columns=${this._columns}
|
||||
.narrow=${this.narrow}
|
||||
@config-refresh="${this._forceFetchConfig}"
|
||||
@config-refresh=${this._forceFetchConfig}
|
||||
></hui-root>
|
||||
`;
|
||||
}
|
||||
@ -130,10 +133,12 @@ class LovelacePanel extends LitElement {
|
||||
|
||||
public firstUpdated() {
|
||||
this._fetchConfig(false);
|
||||
// we don't want to unsub as we want to stay informed of updates
|
||||
subscribeLovelaceUpdates(this.hass!.connection, () =>
|
||||
this._lovelaceChanged()
|
||||
);
|
||||
if (this.urlPath === null) {
|
||||
// we don't want to unsub as we want to stay informed of updates
|
||||
subscribeLovelaceUpdates(this.hass!.connection, this.urlPath, () =>
|
||||
this._lovelaceChanged()
|
||||
);
|
||||
}
|
||||
// reload lovelace on reconnect so we are sure we have the latest config
|
||||
window.addEventListener("connection-status", (ev) => {
|
||||
if (ev.detail === "connected") {
|
||||
@ -214,6 +219,10 @@ class LovelacePanel extends LitElement {
|
||||
});
|
||||
}
|
||||
|
||||
public get urlPath() {
|
||||
return this.panel!.url_path === "lovelace" ? null : this.panel!.url_path;
|
||||
}
|
||||
|
||||
private _forceFetchConfig() {
|
||||
this._fetchConfig(true);
|
||||
}
|
||||
@ -221,26 +230,40 @@ class LovelacePanel extends LitElement {
|
||||
private async _fetchConfig(forceDiskRefresh: boolean) {
|
||||
let conf: LovelaceConfig;
|
||||
let confMode: Lovelace["mode"] = this.panel!.config.mode;
|
||||
let confProm: Promise<LovelaceConfig>;
|
||||
let confProm: Promise<LovelaceConfig> | undefined;
|
||||
const llWindow = window as WindowWithLovelaceProm;
|
||||
|
||||
// On first load, we speed up loading page by having LL promise ready
|
||||
if (llWindow.llConfProm) {
|
||||
confProm = llWindow.llConfProm;
|
||||
llWindow.llConfProm = undefined;
|
||||
} else {
|
||||
}
|
||||
if (!resourcesLoaded) {
|
||||
resourcesLoaded = true;
|
||||
(
|
||||
llWindow.llConfProm || fetchResources(this.hass!.connection)
|
||||
).then((resources) =>
|
||||
loadLovelaceResources(resources, this.hass!.auth.data.hassUrl)
|
||||
);
|
||||
}
|
||||
|
||||
if (this.urlPath !== null || !confProm) {
|
||||
// Refreshing a YAML config can trigger an update event. We will ignore
|
||||
// all update events while fetching the config and for 2 seconds after the cnofig is back.
|
||||
// all update events while fetching the config and for 2 seconds after the config is back.
|
||||
// We ignore because we already have the latest config.
|
||||
if (this.lovelace && this.lovelace.mode === "yaml") {
|
||||
this._ignoreNextUpdateEvent = true;
|
||||
}
|
||||
|
||||
confProm = fetchConfig(this.hass!.connection, forceDiskRefresh);
|
||||
confProm = fetchConfig(
|
||||
this.hass!.connection,
|
||||
this.urlPath,
|
||||
forceDiskRefresh
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
conf = await confProm;
|
||||
conf = await confProm!;
|
||||
} catch (err) {
|
||||
if (err.code !== "config_not_found") {
|
||||
// tslint:disable-next-line
|
||||
@ -282,6 +305,7 @@ class LovelacePanel extends LitElement {
|
||||
|
||||
private _setLovelaceConfig(config: LovelaceConfig, mode: Lovelace["mode"]) {
|
||||
config = this._checkLovelaceConfig(config);
|
||||
const urlPath = this.urlPath;
|
||||
this.lovelace = {
|
||||
config,
|
||||
mode,
|
||||
@ -313,7 +337,7 @@ class LovelacePanel extends LitElement {
|
||||
mode: "storage",
|
||||
});
|
||||
this._ignoreNextUpdateEvent = true;
|
||||
await saveConfig(this.hass!, newConfig);
|
||||
await saveConfig(this.hass!, urlPath, newConfig);
|
||||
} catch (err) {
|
||||
// tslint:disable-next-line
|
||||
console.error(err);
|
||||
@ -335,7 +359,7 @@ class LovelacePanel extends LitElement {
|
||||
editMode: false,
|
||||
});
|
||||
this._ignoreNextUpdateEvent = true;
|
||||
await deleteConfig(this.hass!);
|
||||
await deleteConfig(this.hass!, urlPath);
|
||||
} catch (err) {
|
||||
// tslint:disable-next-line
|
||||
console.error(err);
|
||||
|
@ -37,7 +37,6 @@ import {
|
||||
const lovelaceStruct = struct.interface({
|
||||
title: "string?",
|
||||
views: ["object"],
|
||||
resources: struct.optional(["object"]),
|
||||
});
|
||||
|
||||
@customElement("hui-editor")
|
||||
@ -261,6 +260,14 @@ class LovelaceFullConfigEditor extends LitElement {
|
||||
});
|
||||
return;
|
||||
}
|
||||
// @ts-ignore
|
||||
if (config.resources) {
|
||||
showAlertDialog(this, {
|
||||
text: this.hass.localize(
|
||||
"ui.panel.lovelace.editor.raw_editor.resources_moved"
|
||||
),
|
||||
});
|
||||
}
|
||||
try {
|
||||
await this.lovelace!.saveConfig(config);
|
||||
} catch (err) {
|
||||
|
@ -47,7 +47,6 @@ import { Lovelace } from "./types";
|
||||
import { afterNextRender } from "../../common/util/render-status";
|
||||
import { haStyle } from "../../resources/styles";
|
||||
import { computeRTLDirection } from "../../common/util/compute_rtl";
|
||||
import { loadLovelaceResources } from "./common/load-resources";
|
||||
import { showVoiceCommandDialog } from "../../dialogs/voice-command-dialog/show-ha-voice-command-dialog";
|
||||
import { isComponentLoaded } from "../../common/config/is_component_loaded";
|
||||
import { showAlertDialog } from "../../dialogs/generic/show-dialog-box";
|
||||
@ -468,13 +467,9 @@ class HUIRoot extends LitElement {
|
||||
let force = false;
|
||||
|
||||
if (changedProperties.has("route")) {
|
||||
const views = this.config && this.config.views;
|
||||
if (
|
||||
this.route!.path === "" &&
|
||||
this.route!.prefix === "/lovelace" &&
|
||||
views
|
||||
) {
|
||||
navigate(this, `/lovelace/${views[0].path || 0}`, true);
|
||||
const views = this.config.views;
|
||||
if (this.route!.path === "" && views) {
|
||||
navigate(this, `${this.route!.prefix}/${views[0].path || 0}`, true);
|
||||
newSelectView = 0;
|
||||
} else if (this._routeData!.view === "hass-unused-entities") {
|
||||
newSelectView = "hass-unused-entities";
|
||||
@ -498,12 +493,6 @@ class HUIRoot extends LitElement {
|
||||
| undefined;
|
||||
|
||||
if (!oldLovelace || oldLovelace.config !== this.lovelace!.config) {
|
||||
if (this.lovelace!.config.resources) {
|
||||
loadLovelaceResources(
|
||||
this.lovelace!.config.resources,
|
||||
this.hass!.auth.data.hassUrl
|
||||
);
|
||||
}
|
||||
// On config change, recreate the current view from scratch.
|
||||
force = true;
|
||||
// Recalculate to see if we need to adjust content area for tab bar
|
||||
@ -517,7 +506,7 @@ class HUIRoot extends LitElement {
|
||||
this._routeData!.view === "hass-unused-entities"
|
||||
) {
|
||||
const views = this.config && this.config.views;
|
||||
navigate(this, `/lovelace/${views[0].path || 0}`);
|
||||
navigate(this, `${this.route?.prefix}/${views[0].path || 0}`);
|
||||
newSelectView = 0;
|
||||
}
|
||||
// On edit mode change, recreate the current view from scratch
|
||||
@ -565,7 +554,7 @@ class HUIRoot extends LitElement {
|
||||
}
|
||||
|
||||
private _handleUnusedEntities(): void {
|
||||
navigate(this, `/lovelace/hass-unused-entities`);
|
||||
navigate(this, `${this.route?.prefix}/hass-unused-entities`);
|
||||
}
|
||||
|
||||
private _deselect(ev): void {
|
||||
@ -638,7 +627,7 @@ class HUIRoot extends LitElement {
|
||||
|
||||
if (viewIndex !== this._curView) {
|
||||
const path = this.config.views[viewIndex].path || viewIndex;
|
||||
navigate(this, `/lovelace/${path}`);
|
||||
navigate(this, `${this.route?.prefix}/${path}`);
|
||||
}
|
||||
scrollToTarget(this, this._layout.header.scrollTarget);
|
||||
}
|
||||
|
@ -49,7 +49,8 @@ class HuiCastRow extends LitElement implements LovelaceRow {
|
||||
const active =
|
||||
this._castManager &&
|
||||
this._castManager.status &&
|
||||
this._config.view === this._castManager.status.lovelacePath;
|
||||
this._config.view === this._castManager.status.lovelacePath &&
|
||||
this._config.dashboard === this._castManager.status.urlPath;
|
||||
|
||||
return html`
|
||||
<ha-icon .icon="${this._config.icon}"></ha-icon>
|
||||
@ -122,7 +123,11 @@ class HuiCastRow extends LitElement implements LovelaceRow {
|
||||
|
||||
private async _sendLovelace() {
|
||||
await ensureConnectedCastSession(this._castManager!, this.hass.auth);
|
||||
castSendShowLovelaceView(this._castManager!, this._config!.view);
|
||||
castSendShowLovelaceView(
|
||||
this._castManager!,
|
||||
this._config!.view,
|
||||
this._config!.dashboard
|
||||
);
|
||||
}
|
||||
|
||||
static get styles(): CSSResult {
|
||||
|
@ -1759,7 +1759,8 @@
|
||||
"error_parse_yaml": "Unable to parse YAML: {error}",
|
||||
"error_invalid_config": "Your configuration is not valid: {error}",
|
||||
"error_save_yaml": "Unable to save YAML: {error}",
|
||||
"error_remove": "Unable to remove configuration: {error}"
|
||||
"error_remove": "Unable to remove configuration: {error}",
|
||||
"resources_moved": "Resources should no longer be added to the Lovelace configuration but can be added in the Lovelace config panel."
|
||||
},
|
||||
"edit_lovelace": {
|
||||
"header": "Title of your Lovelace UI",
|
||||
|
Loading…
x
Reference in New Issue
Block a user