diff --git a/src/panels/lovelace/common/data.ts b/src/panels/lovelace/common/data.ts index 373dccb4d6..289e277b46 100644 --- a/src/panels/lovelace/common/data.ts +++ b/src/panels/lovelace/common/data.ts @@ -1,5 +1,21 @@ import { HomeAssistant } from "../../../types"; -import { LovelaceCardConfig } from "../types"; +import { LovelaceConfig, LovelaceCardConfig } from "../types"; + +export const migrateConfig = (hass: HomeAssistant): Promise => + hass.callWS({ + type: "lovelace/config/migrate", + }); + +export const saveConfig = ( + hass: HomeAssistant, + config: LovelaceConfig | string, + configFormat: "json" | "yaml" +): Promise => + hass.callWS({ + type: "lovelace/config/save", + config, + format: configFormat, + }); export const getCardConfig = ( hass: HomeAssistant, @@ -22,8 +38,3 @@ export const updateCardConfig = ( card_config: config, format: configFormat, }); - -export const migrateConfig = (hass: HomeAssistant): Promise => - hass.callWS({ - type: "lovelace/config/migrate", - }); diff --git a/src/panels/lovelace/components/hui-card-options.ts b/src/panels/lovelace/components/hui-card-options.ts index d1d6858c1c..ebd0767251 100644 --- a/src/panels/lovelace/components/hui-card-options.ts +++ b/src/panels/lovelace/components/hui-card-options.ts @@ -1,6 +1,10 @@ import "@polymer/paper-button/paper-button"; import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element"; import { fireEvent } from "../../../common/dom/fire_event"; +import { + showEditCardDialog, + registerEditCardDialog, +} from "../editor/hui-dialog-edit-card"; import { HomeAssistant } from "../../../types"; import { LovelaceCardConfig } from "../types"; @@ -18,11 +22,7 @@ export class HuiCardOptions extends LitElement { super.connectedCallback(); if (!registeredDialog) { registeredDialog = true; - fireEvent(this, "register-dialog", { - dialogShowEvent: "show-edit-card", - dialogTag: "hui-dialog-edit-card", - dialogImport: () => import("../editor/hui-dialog-edit-card"), - }); + registerEditCardDialog(this); } } @@ -48,9 +48,8 @@ export class HuiCardOptions extends LitElement { `; } private _editCard() { - fireEvent(this, "show-edit-card", { - hass: this.hass, - cardConfig: this.cardConfig, + showEditCardDialog(this, { + cardConfig: this.cardConfig!, reloadLovelace: () => fireEvent(this, "config-refresh"), }); } diff --git a/src/panels/lovelace/editor/hui-dialog-edit-card.ts b/src/panels/lovelace/editor/hui-dialog-edit-card.ts index 6fb993517a..b8cbe5b835 100644 --- a/src/panels/lovelace/editor/hui-dialog-edit-card.ts +++ b/src/panels/lovelace/editor/hui-dialog-edit-card.ts @@ -3,48 +3,66 @@ import { TemplateResult } from "lit-html"; import { HomeAssistant } from "../../../types"; import { LovelaceCardConfig } from "../types"; +import { fireEvent } from "../../../common/dom/fire_event"; import "./hui-edit-card"; import "./hui-migrate-config"; +const dialogShowEvent = "show-edit-card"; +const dialogTag = "hui-dialog-edit-config"; + +export interface EditCardDialogParams { + cardConfig: LovelaceCardConfig; + reloadLovelace: () => void; +} + +export const registerEditCardDialog = (element: HTMLElement) => + fireEvent(element, "register-dialog", { + dialogShowEvent, + dialogTag, + dialogImport: () => import("./hui-dialog-edit-card"), + }); + +export const showEditCardDialog = ( + element: HTMLElement, + editCardDialogParams: EditCardDialogParams +) => fireEvent(element, dialogShowEvent, editCardDialogParams); + export class HuiDialogEditCard extends LitElement { - protected _hass?: HomeAssistant; - private _cardConfig?: LovelaceCardConfig; - private _reloadLovelace?: () => void; + protected hass?: HomeAssistant; + private _params?: EditCardDialogParams; static get properties(): PropertyDeclarations { return { - _hass: {}, + hass: {}, _cardConfig: {}, }; } - public async showDialog({ hass, cardConfig, reloadLovelace }): Promise { - this._hass = hass; - this._cardConfig = cardConfig; - this._reloadLovelace = reloadLovelace; + public async showDialog(params: EditCardDialogParams): Promise { + this._params = params; await this.updateComplete; (this.shadowRoot!.children[0] as any).showDialog(); } protected render(): TemplateResult { + if (!this._params) { + return html``; + } + if (!this._params.cardConfig.id) { + return html` + + `; + } return html` - ${ - this._cardConfig!.id - ? html` - - - ` - : html` - - ` - } + + `; } } @@ -55,4 +73,4 @@ declare global { } } -customElements.define("hui-dialog-edit-card", HuiDialogEditCard); +customElements.define(dialogTag, HuiDialogEditCard); diff --git a/src/panels/lovelace/editor/hui-dialog-save-config.ts b/src/panels/lovelace/editor/hui-dialog-save-config.ts new file mode 100644 index 0000000000..e5c0f01985 --- /dev/null +++ b/src/panels/lovelace/editor/hui-dialog-save-config.ts @@ -0,0 +1,150 @@ +import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element"; +import { TemplateResult } from "lit-html"; + +import "@polymer/paper-spinner/paper-spinner"; +import "@polymer/paper-dialog/paper-dialog"; +// This is not a duplicate import, one is for types, one is for element. +// tslint:disable-next-line +import { PaperDialogElement } from "@polymer/paper-dialog/paper-dialog"; +import "@polymer/paper-button/paper-button"; + +import { HomeAssistant } from "../../../types"; +import { LovelaceConfig } from "../types"; + +import { saveConfig, migrateConfig } from "../common/data"; +import { fireEvent } from "../../../common/dom/fire_event"; +import { hassLocalizeLitMixin } from "../../../mixins/lit-localize-mixin"; + +const dialogShowEvent = "show-save-config"; +const dialogTag = "hui-dialog-save-config"; + +export interface SaveDialogParams { + config: LovelaceConfig; + reloadLovelace: () => void; +} + +export const registerSaveDialog = (element: HTMLElement) => + fireEvent(element, "register-dialog", { + dialogShowEvent, + dialogTag, + dialogImport: () => import("./hui-dialog-save-config"), + }); + +export const showSaveDialog = ( + element: HTMLElement, + saveDialogParams: SaveDialogParams +) => fireEvent(element, dialogShowEvent, saveDialogParams); + +export class HuiSaveConfig extends hassLocalizeLitMixin(LitElement) { + protected hass?: HomeAssistant; + private _params?: SaveDialogParams; + private _saving: boolean; + + static get properties(): PropertyDeclarations { + return { + hass: {}, + _params: {}, + _saving: {}, + }; + } + + protected constructor() { + super(); + this._saving = false; + } + + public async showDialog(params: SaveDialogParams): Promise { + this._params = params; + await this.updateComplete; + this._dialog.open(); + } + + private get _dialog(): PaperDialogElement { + return this.shadowRoot!.querySelector("paper-dialog")!; + } + + protected render(): TemplateResult { + return html` + ${this.renderStyle()} + +

${this.localize("ui.panel.lovelace.editor.save_config.header")}

+ +

${this.localize("ui.panel.lovelace.editor.save_config.para")}

+

+ ${this.localize("ui.panel.lovelace.editor.save_config.para_sure")} +

+
+
+ ${ + this.localize("ui.panel.lovelace.editor.save_config.cancel") + } + + + ${ + this.localize("ui.panel.lovelace.editor.save_config.save") + } +
+
+ `; + } + + private renderStyle(): TemplateResult { + return html` + + `; + } + + private _closeDialog(): void { + this._dialog.close(); + } + + private async _saveConfig(): Promise { + if (!this.hass || !this._params) { + return; + } + this._saving = true; + delete this._params.config._frontendAuto; + try { + await saveConfig(this.hass, this._params.config, "json"); + await migrateConfig(this.hass); + this._saving = false; + this._closeDialog(); + this._params.reloadLovelace!(); + } catch (err) { + alert(`Saving failed: ${err.message}`); + this._saving = false; + } + } +} + +declare global { + interface HTMLElementTagNameMap { + "hui-dialog-save-config": HuiSaveConfig; + } +} + +customElements.define(dialogTag, HuiSaveConfig); diff --git a/src/panels/lovelace/ha-panel-lovelace.js b/src/panels/lovelace/ha-panel-lovelace.js index f69434e714..2e7f4237de 100644 --- a/src/panels/lovelace/ha-panel-lovelace.js +++ b/src/panels/lovelace/ha-panel-lovelace.js @@ -2,11 +2,14 @@ import { html } from "@polymer/polymer/lib/utils/html-tag"; import { PolymerElement } from "@polymer/polymer/polymer-element"; import "@polymer/paper-button/paper-button"; +import { registerSaveDialog } from "./editor/hui-dialog-save-config"; import "../../layouts/hass-loading-screen"; import "../../layouts/hass-error-screen"; import "./hui-root"; import localizeMixin from "../../mixins/localize-mixin"; +let registeredDialog = false; + class Lovelace extends localizeMixin(PolymerElement) { static get template() { return html` @@ -125,6 +128,10 @@ class Lovelace extends localizeMixin(PolymerElement) { _config: generateLovelaceConfig(this.hass, this.localize), _state: "loaded", }); + if (!registeredDialog) { + registeredDialog = true; + registerSaveDialog(this); + } } else { this.setProperties({ _state: "error", diff --git a/src/panels/lovelace/hui-root.js b/src/panels/lovelace/hui-root.js index c04fb9dedd..e6e25069cc 100644 --- a/src/panels/lovelace/hui-root.js +++ b/src/panels/lovelace/hui-root.js @@ -29,8 +29,8 @@ import "./components/notifications/hui-notifications-button"; import "./hui-unused-entities"; import "./hui-view"; import debounce from "../../common/util/debounce"; - import createCardElement from "./common/create-card-element"; +import { showSaveDialog } from "./editor/hui-dialog-save-config"; // CSS and JS should only be imported once. Modules and HTML are safe. const CSS_CACHE = {}; @@ -275,7 +275,13 @@ class HUIRoot extends NavigateMixin(EventsMixin(PolymerElement)) { _editModeEnable() { if (this.config._frontendAuto) { - alert("Unable to edit automatic generated UI yet."); + showSaveDialog(this, { + config: this.config, + reloadLovelace: () => { + this.fire("config-refresh"); + this._editMode = true; + }, + }); return; } this._editMode = true; diff --git a/src/panels/lovelace/types.ts b/src/panels/lovelace/types.ts index ca7a2e2d58..6ebd271676 100644 --- a/src/panels/lovelace/types.ts +++ b/src/panels/lovelace/types.ts @@ -13,6 +13,7 @@ export interface LovelaceViewConfig { id?: string; icon?: string; } + export interface LovelaceConfig { _frontendAuto: boolean; title?: string; diff --git a/src/translations/en.json b/src/translations/en.json index b7e0846d2b..c8a14d9d28 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -772,6 +772,13 @@ "save": "Save", "toggle_editor": "Toggle Editor" }, + "save_config": { + "header": "Take control of your Lovelace UI", + "para": "By default Home Assistant will maintain your user interface, updating it when new entities or Lovelace components become available. If you take control we will no longer make changes automatically for you.", + "para_sure": "Are you sure you want to take control of your user interface?", + "cancel": "Never mind", + "save": "Take control" + }, "migrate": { "header": "Configuration Incompatible", "para_no_id": "This element doesn't have an ID. Please add an ID to this element in 'ui-lovelace.yaml'.",