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:
Paulus Schoutsen 2020-02-25 13:06:25 -08:00 committed by GitHub
parent 25d6427aed
commit 1d052fa5bb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 163 additions and 63 deletions

View File

@ -15,6 +15,7 @@ import {
import { import {
LovelaceConfig, LovelaceConfig,
getLovelaceCollection, getLovelaceCollection,
fetchResources,
} from "../../../../src/data/lovelace"; } from "../../../../src/data/lovelace";
import "./hc-launch-screen"; import "./hc-launch-screen";
import { castContext } from "../cast_context"; 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 { loadLovelaceResources } from "../../../../src/panels/lovelace/common/load-resources";
import { isNavigationClick } from "../../../../src/common/dom/is-navigation-click"; import { isNavigationClick } from "../../../../src/common/dom/is-navigation-click";
let resourcesLoaded = false;
@customElement("hc-main") @customElement("hc-main")
export class HcMain extends HassElement { export class HcMain extends HassElement {
@property() private _showDemo = false; @property() private _showDemo = false;
@ -34,6 +37,7 @@ export class HcMain extends HassElement {
@property() private _error?: string; @property() private _error?: string;
private _unsubLovelace?: UnsubscribeFunc; private _unsubLovelace?: UnsubscribeFunc;
private _urlPath?: string | null;
public processIncomingMessage(msg: HassMessage) { public processIncomingMessage(msg: HassMessage) {
if (msg.type === "connect") { if (msg.type === "connect") {
@ -108,6 +112,7 @@ export class HcMain extends HassElement {
if (this.hass) { if (this.hass) {
status.hassUrl = this.hass.auth.data.hassUrl; status.hassUrl = this.hass.auth.data.hassUrl;
status.lovelacePath = this._lovelacePath!; status.lovelacePath = this._lovelacePath!;
status.urlPath = this._urlPath;
} }
if (senderId) { if (senderId) {
@ -163,8 +168,19 @@ export class HcMain extends HassElement {
this._error = "Cannot show Lovelace because we're not connected."; this._error = "Cannot show Lovelace because we're not connected.";
return; return;
} }
if (!this._unsubLovelace) { if (!resourcesLoaded) {
const llColl = getLovelaceCollection(this.hass!.connection); 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 // We first do a single refresh because we need to check if there is LL
// configuration. // configuration.
try { try {
@ -194,12 +210,6 @@ export class HcMain extends HassElement {
private _handleNewLovelaceConfig(lovelaceConfig: LovelaceConfig) { private _handleNewLovelaceConfig(lovelaceConfig: LovelaceConfig) {
castContext.setApplicationState(lovelaceConfig.title!); castContext.setApplicationState(lovelaceConfig.title!);
this._lovelaceConfig = lovelaceConfig; this._lovelaceConfig = lovelaceConfig;
if (lovelaceConfig.resources) {
loadLovelaceResources(
lovelaceConfig.resources,
this.hass!.auth.data.hassUrl
);
}
} }
private _handleShowDemo(_msg: ShowDemoMessage) { private _handleShowDemo(_msg: ShowDemoMessage) {

View File

@ -21,6 +21,7 @@ export interface ConnectMessage extends BaseCastMessage {
export interface ShowLovelaceViewMessage extends BaseCastMessage { export interface ShowLovelaceViewMessage extends BaseCastMessage {
type: "show_lovelace_view"; type: "show_lovelace_view";
viewPath: string | number | null; viewPath: string | number | null;
urlPath: string | null;
} }
export interface ShowDemoMessage extends BaseCastMessage { export interface ShowDemoMessage extends BaseCastMessage {
@ -43,11 +44,13 @@ export const castSendAuth = (cast: CastManager, auth: Auth) =>
export const castSendShowLovelaceView = ( export const castSendShowLovelaceView = (
cast: CastManager, cast: CastManager,
viewPath: ShowLovelaceViewMessage["viewPath"] viewPath: ShowLovelaceViewMessage["viewPath"],
urlPath?: string | null
) => ) =>
cast.sendMessage({ cast.sendMessage({
type: "show_lovelace_view", type: "show_lovelace_view",
viewPath, viewPath,
urlPath: urlPath || null,
}); });
export const castSendShowDemo = (cast: CastManager) => export const castSendShowDemo = (cast: CastManager) =>

View File

@ -8,6 +8,7 @@ export interface ReceiverStatusMessage extends BaseCastMessage {
showDemo: boolean; showDemo: boolean;
hassUrl?: string; hassUrl?: string;
lovelacePath?: string | number | null; lovelacePath?: string | number | null;
urlPath?: string | null;
} }
export type SenderMessage = ReceiverStatusMessage; export type SenderMessage = ReceiverStatusMessage;

View File

@ -46,7 +46,18 @@ const SORT_VALUE_URL_PATHS = {
config: 11, 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 aBuiltIn = a.url_path in SORT_VALUE_URL_PATHS;
const bBuiltIn = b.url_path in SORT_VALUE_URL_PATHS; const bBuiltIn = b.url_path in SORT_VALUE_URL_PATHS;

View File

@ -1,14 +1,22 @@
import { HomeAssistant } from "../types"; 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"; import { HASSDomEvent } from "../common/dom/fire_event";
export interface LovelaceConfig { export interface LovelaceConfig {
title?: string; title?: string;
views: LovelaceViewConfig[]; views: LovelaceViewConfig[];
background?: string; 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 { export interface LovelaceViewConfig {
index?: number; index?: number;
title?: string; title?: string;
@ -95,47 +103,78 @@ export type ActionConfig =
| NoActionConfig | NoActionConfig
| CustomActionConfig; | 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 = ( export const fetchConfig = (
conn: Connection, conn: Connection,
urlPath: string | null,
force: boolean force: boolean
): Promise<LovelaceConfig> => ): Promise<LovelaceConfig> =>
conn.sendMessagePromise({ conn.sendMessagePromise({
type: "lovelace/config", type: "lovelace/config",
url_path: urlPath,
force, force,
}); });
export const saveConfig = ( export const saveConfig = (
hass: HomeAssistant, hass: HomeAssistant,
urlPath: string | null,
config: LovelaceConfig config: LovelaceConfig
): Promise<void> => ): Promise<void> =>
hass.callWS({ hass.callWS({
type: "lovelace/config/save", type: "lovelace/config/save",
url_path: urlPath,
config, config,
}); });
export const deleteConfig = (hass: HomeAssistant): Promise<void> => export const deleteConfig = (
hass: HomeAssistant,
urlPath: string | null
): Promise<void> =>
hass.callWS({ hass.callWS({
type: "lovelace/config/delete", type: "lovelace/config/delete",
url_path: urlPath,
}); });
export const subscribeLovelaceUpdates = ( export const subscribeLovelaceUpdates = (
conn: Connection, conn: Connection,
urlPath: string | null,
onChange: () => void 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( getCollection(
conn, conn,
"_lovelace", `_lovelace_${urlPath ?? ""}`,
(conn2) => fetchConfig(conn2, false), (conn2) => fetchConfig(conn2, urlPath, false),
(_conn, store) => (_conn, store) =>
subscribeLovelaceUpdates(conn, () => subscribeLovelaceUpdates(conn, urlPath, () =>
fetchConfig(conn, false).then((config) => store.setState(config, true)) fetchConfig(conn, urlPath, false).then((config) =>
store.setState(config, true)
)
) )
); );
export interface WindowWithLovelaceProm extends Window { export interface WindowWithLovelaceProm extends Window {
llConfProm?: Promise<LovelaceConfig>; llConfProm?: Promise<LovelaceConfig>;
llResProm?: Promise<LovelaceResources>;
} }
export interface ActionHandlerOptions { export interface ActionHandlerOptions {

View File

@ -15,7 +15,11 @@ import { subscribeThemes } from "../data/ws-themes";
import { subscribeUser } from "../data/ws-user"; import { subscribeUser } from "../data/ws-user";
import { HomeAssistant } from "../types"; import { HomeAssistant } from "../types";
import { hassUrl } from "../data/auth"; import { hassUrl } from "../data/auth";
import { fetchConfig, WindowWithLovelaceProm } from "../data/lovelace"; import {
fetchConfig,
fetchResources,
WindowWithLovelaceProm,
} from "../data/lovelace";
declare global { declare global {
interface Window { interface Window {
@ -90,7 +94,12 @@ window.hassConnection.then(({ conn }) => {
subscribeUser(conn, noop); subscribeUser(conn, noop);
if (location.pathname === "/" || location.pathname.startsWith("/lovelace/")) { 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);
} }
}); });

View File

@ -1,13 +1,13 @@
import { loadModule, loadCSS, loadJS } from "../../../common/dom/load_resource"; 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. // CSS and JS should only be imported once. Modules and HTML are safe.
const CSS_CACHE = {}; const CSS_CACHE = {};
const JS_CACHE = {}; const JS_CACHE = {};
export const loadLovelaceResources = ( export const loadLovelaceResources = (
resources: NonNullable<LovelaceConfig["resources"]>, resources: NonNullable<LovelaceResources>,
hassUrl: string hassUrl: string
) => ) =>
resources.forEach((resource) => { resources.forEach((resource) => {

View File

@ -22,7 +22,7 @@ export const addEntitiesToLovelaceView = async (
} }
if (!lovelaceConfig) { if (!lovelaceConfig) {
try { try {
lovelaceConfig = await fetchConfig(hass.connection, false); lovelaceConfig = await fetchConfig(hass.connection, null, false);
} catch { } catch {
alert( alert(
hass.localize( hass.localize(
@ -41,7 +41,7 @@ export const addEntitiesToLovelaceView = async (
if (!saveConfigFunc) { if (!saveConfigFunc) {
saveConfigFunc = async (newConfig: LovelaceConfig): Promise<void> => { saveConfigFunc = async (newConfig: LovelaceConfig): Promise<void> => {
try { try {
await saveConfig(hass!, newConfig); await saveConfig(hass!, null, newConfig);
} catch { } catch {
alert( alert(
hass.localize("ui.panel.config.devices.add_entities.saving_failed") hass.localize("ui.panel.config.devices.add_entities.saving_failed")

View File

@ -39,6 +39,7 @@ export interface CastConfig {
icon: string; icon: string;
name: string; name: string;
view: string | number; view: string | number;
dashboard?: string;
// Hide the row if either unsupported browser or no API available. // Hide the row if either unsupported browser or no API available.
hide_if_unavailable: boolean; hide_if_unavailable: boolean;
} }

View File

@ -8,6 +8,7 @@ import {
subscribeLovelaceUpdates, subscribeLovelaceUpdates,
WindowWithLovelaceProm, WindowWithLovelaceProm,
deleteConfig, deleteConfig,
fetchResources,
} from "../../data/lovelace"; } from "../../data/lovelace";
import "../../layouts/hass-loading-screen"; import "../../layouts/hass-loading-screen";
import "../../layouts/hass-error-screen"; import "../../layouts/hass-error-screen";
@ -24,6 +25,7 @@ import {
import { showSaveDialog } from "./editor/show-save-config-dialog"; import { showSaveDialog } from "./editor/show-save-config-dialog";
import { generateLovelaceConfigFromHass } from "./common/generate-lovelace-config"; import { generateLovelaceConfigFromHass } from "./common/generate-lovelace-config";
import { showToast } from "../../util/toast"; import { showToast } from "../../util/toast";
import { loadLovelaceResources } from "./common/load-resources";
(window as any).loadCardHelpers = () => import("./custom-card-helpers"); (window as any).loadCardHelpers = () => import("./custom-card-helpers");
@ -32,6 +34,7 @@ interface LovelacePanelConfig {
} }
let editorLoaded = false; let editorLoaded = false;
let resourcesLoaded = false;
class LovelacePanel extends LitElement { class LovelacePanel extends LitElement {
@property() public panel?: PanelInfo<LovelacePanelConfig>; @property() public panel?: PanelInfo<LovelacePanelConfig>;
@ -67,12 +70,12 @@ class LovelacePanel extends LitElement {
if (state === "loaded") { if (state === "loaded") {
return html` return html`
<hui-root <hui-root
.hass="${this.hass}" .hass=${this.hass}
.lovelace="${this.lovelace}" .lovelace=${this.lovelace}
.route="${this.route}" .route=${this.route}
.columns="${this._columns}" .columns=${this._columns}
.narrow=${this.narrow} .narrow=${this.narrow}
@config-refresh="${this._forceFetchConfig}" @config-refresh=${this._forceFetchConfig}
></hui-root> ></hui-root>
`; `;
} }
@ -130,10 +133,12 @@ class LovelacePanel extends LitElement {
public firstUpdated() { public firstUpdated() {
this._fetchConfig(false); this._fetchConfig(false);
// we don't want to unsub as we want to stay informed of updates if (this.urlPath === null) {
subscribeLovelaceUpdates(this.hass!.connection, () => // we don't want to unsub as we want to stay informed of updates
this._lovelaceChanged() subscribeLovelaceUpdates(this.hass!.connection, this.urlPath, () =>
); this._lovelaceChanged()
);
}
// reload lovelace on reconnect so we are sure we have the latest config // reload lovelace on reconnect so we are sure we have the latest config
window.addEventListener("connection-status", (ev) => { window.addEventListener("connection-status", (ev) => {
if (ev.detail === "connected") { 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() { private _forceFetchConfig() {
this._fetchConfig(true); this._fetchConfig(true);
} }
@ -221,26 +230,40 @@ class LovelacePanel extends LitElement {
private async _fetchConfig(forceDiskRefresh: boolean) { private async _fetchConfig(forceDiskRefresh: boolean) {
let conf: LovelaceConfig; let conf: LovelaceConfig;
let confMode: Lovelace["mode"] = this.panel!.config.mode; let confMode: Lovelace["mode"] = this.panel!.config.mode;
let confProm: Promise<LovelaceConfig>; let confProm: Promise<LovelaceConfig> | undefined;
const llWindow = window as WindowWithLovelaceProm; const llWindow = window as WindowWithLovelaceProm;
// On first load, we speed up loading page by having LL promise ready // On first load, we speed up loading page by having LL promise ready
if (llWindow.llConfProm) { if (llWindow.llConfProm) {
confProm = llWindow.llConfProm; confProm = llWindow.llConfProm;
llWindow.llConfProm = undefined; 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 // 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. // We ignore because we already have the latest config.
if (this.lovelace && this.lovelace.mode === "yaml") { if (this.lovelace && this.lovelace.mode === "yaml") {
this._ignoreNextUpdateEvent = true; this._ignoreNextUpdateEvent = true;
} }
confProm = fetchConfig(this.hass!.connection, forceDiskRefresh); confProm = fetchConfig(
this.hass!.connection,
this.urlPath,
forceDiskRefresh
);
} }
try { try {
conf = await confProm; conf = await confProm!;
} catch (err) { } catch (err) {
if (err.code !== "config_not_found") { if (err.code !== "config_not_found") {
// tslint:disable-next-line // tslint:disable-next-line
@ -282,6 +305,7 @@ class LovelacePanel extends LitElement {
private _setLovelaceConfig(config: LovelaceConfig, mode: Lovelace["mode"]) { private _setLovelaceConfig(config: LovelaceConfig, mode: Lovelace["mode"]) {
config = this._checkLovelaceConfig(config); config = this._checkLovelaceConfig(config);
const urlPath = this.urlPath;
this.lovelace = { this.lovelace = {
config, config,
mode, mode,
@ -313,7 +337,7 @@ class LovelacePanel extends LitElement {
mode: "storage", mode: "storage",
}); });
this._ignoreNextUpdateEvent = true; this._ignoreNextUpdateEvent = true;
await saveConfig(this.hass!, newConfig); await saveConfig(this.hass!, urlPath, newConfig);
} catch (err) { } catch (err) {
// tslint:disable-next-line // tslint:disable-next-line
console.error(err); console.error(err);
@ -335,7 +359,7 @@ class LovelacePanel extends LitElement {
editMode: false, editMode: false,
}); });
this._ignoreNextUpdateEvent = true; this._ignoreNextUpdateEvent = true;
await deleteConfig(this.hass!); await deleteConfig(this.hass!, urlPath);
} catch (err) { } catch (err) {
// tslint:disable-next-line // tslint:disable-next-line
console.error(err); console.error(err);

View File

@ -37,7 +37,6 @@ import {
const lovelaceStruct = struct.interface({ const lovelaceStruct = struct.interface({
title: "string?", title: "string?",
views: ["object"], views: ["object"],
resources: struct.optional(["object"]),
}); });
@customElement("hui-editor") @customElement("hui-editor")
@ -261,6 +260,14 @@ class LovelaceFullConfigEditor extends LitElement {
}); });
return; return;
} }
// @ts-ignore
if (config.resources) {
showAlertDialog(this, {
text: this.hass.localize(
"ui.panel.lovelace.editor.raw_editor.resources_moved"
),
});
}
try { try {
await this.lovelace!.saveConfig(config); await this.lovelace!.saveConfig(config);
} catch (err) { } catch (err) {

View File

@ -47,7 +47,6 @@ import { Lovelace } from "./types";
import { afterNextRender } from "../../common/util/render-status"; import { afterNextRender } from "../../common/util/render-status";
import { haStyle } from "../../resources/styles"; import { haStyle } from "../../resources/styles";
import { computeRTLDirection } from "../../common/util/compute_rtl"; 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 { showVoiceCommandDialog } from "../../dialogs/voice-command-dialog/show-ha-voice-command-dialog";
import { isComponentLoaded } from "../../common/config/is_component_loaded"; import { isComponentLoaded } from "../../common/config/is_component_loaded";
import { showAlertDialog } from "../../dialogs/generic/show-dialog-box"; import { showAlertDialog } from "../../dialogs/generic/show-dialog-box";
@ -468,13 +467,9 @@ class HUIRoot extends LitElement {
let force = false; let force = false;
if (changedProperties.has("route")) { if (changedProperties.has("route")) {
const views = this.config && this.config.views; const views = this.config.views;
if ( if (this.route!.path === "" && views) {
this.route!.path === "" && navigate(this, `${this.route!.prefix}/${views[0].path || 0}`, true);
this.route!.prefix === "/lovelace" &&
views
) {
navigate(this, `/lovelace/${views[0].path || 0}`, true);
newSelectView = 0; newSelectView = 0;
} else if (this._routeData!.view === "hass-unused-entities") { } else if (this._routeData!.view === "hass-unused-entities") {
newSelectView = "hass-unused-entities"; newSelectView = "hass-unused-entities";
@ -498,12 +493,6 @@ class HUIRoot extends LitElement {
| undefined; | undefined;
if (!oldLovelace || oldLovelace.config !== this.lovelace!.config) { 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. // On config change, recreate the current view from scratch.
force = true; force = true;
// Recalculate to see if we need to adjust content area for tab bar // 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" this._routeData!.view === "hass-unused-entities"
) { ) {
const views = this.config && this.config.views; 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; newSelectView = 0;
} }
// On edit mode change, recreate the current view from scratch // On edit mode change, recreate the current view from scratch
@ -565,7 +554,7 @@ class HUIRoot extends LitElement {
} }
private _handleUnusedEntities(): void { private _handleUnusedEntities(): void {
navigate(this, `/lovelace/hass-unused-entities`); navigate(this, `${this.route?.prefix}/hass-unused-entities`);
} }
private _deselect(ev): void { private _deselect(ev): void {
@ -638,7 +627,7 @@ class HUIRoot extends LitElement {
if (viewIndex !== this._curView) { if (viewIndex !== this._curView) {
const path = this.config.views[viewIndex].path || viewIndex; const path = this.config.views[viewIndex].path || viewIndex;
navigate(this, `/lovelace/${path}`); navigate(this, `${this.route?.prefix}/${path}`);
} }
scrollToTarget(this, this._layout.header.scrollTarget); scrollToTarget(this, this._layout.header.scrollTarget);
} }

View File

@ -49,7 +49,8 @@ class HuiCastRow extends LitElement implements LovelaceRow {
const active = const active =
this._castManager && this._castManager &&
this._castManager.status && 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` return html`
<ha-icon .icon="${this._config.icon}"></ha-icon> <ha-icon .icon="${this._config.icon}"></ha-icon>
@ -122,7 +123,11 @@ class HuiCastRow extends LitElement implements LovelaceRow {
private async _sendLovelace() { private async _sendLovelace() {
await ensureConnectedCastSession(this._castManager!, this.hass.auth); 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 { static get styles(): CSSResult {

View File

@ -1759,7 +1759,8 @@
"error_parse_yaml": "Unable to parse YAML: {error}", "error_parse_yaml": "Unable to parse YAML: {error}",
"error_invalid_config": "Your configuration is not valid: {error}", "error_invalid_config": "Your configuration is not valid: {error}",
"error_save_yaml": "Unable to save YAML: {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": { "edit_lovelace": {
"header": "Title of your Lovelace UI", "header": "Title of your Lovelace UI",