diff --git a/src/panels/lovelace/common/data.ts b/src/panels/lovelace/common/data.ts index 373dccb4d65d..289e277b4696 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 d1d6858c1c6f..ebd07672511a 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 6fb993517a02..b8cbe5b8355a 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 000000000000..e5c0f01985e2 --- /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 f69434e714d5..2e7f4237de82 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 c04fb9dedd05..e6e25069cc5e 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 ca7a2e2d5839..6ebd2716762b 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 b7e0846d2b96..c8a14d9d286b 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'.",