Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 17 additions & 6 deletions src/panels/lovelace/common/data.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,21 @@
import { HomeAssistant } from "../../../types";
import { LovelaceCardConfig } from "../types";
import { LovelaceConfig, LovelaceCardConfig } from "../types";

export const migrateConfig = (hass: HomeAssistant): Promise<void> =>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In a future PR, we should move this file to src/data/lovelace.ts, as that's also what we do for all other components.

hass.callWS({
type: "lovelace/config/migrate",
});

export const saveConfig = (
hass: HomeAssistant,
config: LovelaceConfig | string,
configFormat: "json" | "yaml"
): Promise<void> =>
hass.callWS({
type: "lovelace/config/save",
config,
format: configFormat,
});

export const getCardConfig = (
hass: HomeAssistant,
Expand All @@ -22,8 +38,3 @@ export const updateCardConfig = (
card_config: config,
format: configFormat,
});

export const migrateConfig = (hass: HomeAssistant): Promise<void> =>
hass.callWS({
type: "lovelace/config/migrate",
});
18 changes: 8 additions & 10 deletions src/panels/lovelace/components/hui-card-options.ts
Original file line number Diff line number Diff line change
@@ -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,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is perfect, we should do this for all dialogs.

registerEditCardDialog,
} from "../editor/hui-dialog-edit-card";
import { HomeAssistant } from "../../../types";
import { LovelaceCardConfig } from "../types";

Expand All @@ -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);
}
}

Expand All @@ -48,11 +48,9 @@ export class HuiCardOptions extends LitElement {
`;
}
private _editCard() {
fireEvent(this, "show-edit-card", {
hass: this.hass,
cardConfig: this.cardConfig,
reloadLovelace: () => fireEvent(this, "config-refresh"),
});
showEditCardDialog(this, this.hass!, this.cardConfig!, () =>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that this should take 2 arguments; this and an object with type EditCardDialogParams. It allows type checking and allows us to just store 1 object in the dialog class too. Example

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, you already have this with EditDialogParams. Well then this function just needs an object of type EditDialogParams passed in .

fireEvent(this, "config-refresh")
);
}
}

Expand Down
34 changes: 29 additions & 5 deletions src/panels/lovelace/editor/hui-dialog-edit-card.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,33 @@ import { TemplateResult } from "lit-html";

import { HomeAssistant } from "../../../types";
import { LovelaceCardConfig } from "../types";
import { EditDialogParams } 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 const registerEditCardDialog = (element: HTMLElement) =>
fireEvent(element, "register-dialog", {
dialogShowEvent,
dialogTag,
dialogImport: () => import("./hui-dialog-edit-card"),
});

export const showEditCardDialog = (
element: HTMLElement,
hass: HomeAssistant,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to pass hass, the dialog manager already takes care of it .

cardConfig: LovelaceCardConfig,
reloadLovelace: () => void
) =>
fireEvent(element, dialogShowEvent, {
hass,
cardConfig,
reloadLovelace,
});

export class HuiDialogEditCard extends LitElement {
protected _hass?: HomeAssistant;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

private _cardConfig?: LovelaceCardConfig;
Expand All @@ -18,10 +42,10 @@ export class HuiDialogEditCard extends LitElement {
};
}

public async showDialog({ hass, cardConfig, reloadLovelace }): Promise<void> {
this._hass = hass;
this._cardConfig = cardConfig;
this._reloadLovelace = reloadLovelace;
public async showDialog(params: EditDialogParams): Promise<void> {
this._hass = params.hass;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would just this._params = params

this._cardConfig = params.cardConfig;
this._reloadLovelace = params.reloadLovelace;
await this.updateComplete;
(this.shadowRoot!.children[0] as any).showDialog();
}
Expand Down Expand Up @@ -55,4 +79,4 @@ declare global {
}
}

customElements.define("hui-dialog-edit-card", HuiDialogEditCard);
customElements.define(dialogTag, HuiDialogEditCard);
151 changes: 151 additions & 0 deletions src/panels/lovelace/editor/hui-dialog-save-config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
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 { SaveDialogParams } 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 const registerSaveDialog = (element: HTMLElement) =>
fireEvent(element, "register-dialog", {
dialogShowEvent,
dialogTag,
dialogImport: () => import("./hui-dialog-save-config"),
});

export const showSaveDialog = (
element: HTMLElement,
hass: HomeAssistant,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need to pass hass, we should pass a single object to showSaveDialog

config: LovelaceConfig,
reloadLovelace: () => void
) => fireEvent(element, dialogShowEvent, { hass, config, reloadLovelace });

export class HuiSaveConfig extends hassLocalizeLitMixin(LitElement) {
protected _hass?: HomeAssistant;
private _config?: LovelaceConfig;
private _reloadLovelace?: () => void;
private _saving: boolean;

static get properties(): PropertyDeclarations {
return {
_hass: {},
_cardConfig: {},
_saving: {},
};
}

protected constructor() {
super();
this._saving = false;
}

public async showDialog(params: SaveDialogParams): Promise<void> {
this._hass = params.hass;
this._config = params.config;
this._reloadLovelace = params.reloadLovelace;
await this.updateComplete;
this._dialog.open();
}

private get _dialog(): PaperDialogElement {
return this.shadowRoot!.querySelector("paper-dialog")!;
}

protected render(): TemplateResult {
return html`
${this.renderStyle()}
<paper-dialog with-backdrop>
<h2>${this.localize("ui.panel.lovelace.editor.save_config.header")}</h2>
<paper-dialog-scrollable>
<p>${this.localize("ui.panel.lovelace.editor.save_config.para")}</p>
<p>
${this.localize("ui.panel.lovelace.editor.save_config.para_sure")}
</p>
</paper-dialog-scrollable>
<div class="paper-dialog-buttons">
<paper-button @click="${this._closeDialog}"
>${
this.localize("ui.panel.lovelace.editor.save_config.cancel")
}</paper-button
>
<paper-button
?disabled="${this._saving}"
@click="${this._saveConfig}"
>
<paper-spinner
?active="${this._saving}"
alt="Saving"
></paper-spinner>
${
this.localize("ui.panel.lovelace.editor.save_config.save")
}</paper-button
>
</div>
</paper-dialog>
`;
}

private renderStyle(): TemplateResult {
return html`
<style>
paper-dialog {
width: 650px;
}
paper-spinner {
display: none;
}
paper-spinner[active] {
display: block;
}
paper-button paper-spinner {
width: 14px;
height: 14px;
margin-right: 20px;
}
</style>
`;
}

private _closeDialog(): void {
this._dialog.close();
}

private async _saveConfig(): Promise<void> {
if (!this._hass || !this._config) {
return;
}
this._saving = true;
delete this._config._frontendAuto;
try {
await saveConfig(this._hass, this._config, "json");
await migrateConfig(this._hass);
this._saving = false;
this._closeDialog();
this._reloadLovelace!();
Comment thread
balloob marked this conversation as resolved.
Outdated
} catch (err) {
alert(`Saving failed: ${err.message}`);
this._saving = false;
}
}
}

declare global {
interface HTMLElementTagNameMap {
"hui-dialog-save-config": HuiSaveConfig;
}
}

customElements.define(dialogTag, HuiSaveConfig);
15 changes: 14 additions & 1 deletion src/panels/lovelace/editor/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { LovelaceCardConfig } from "../types";
import { HomeAssistant } from "../../../types";
import { LovelaceConfig, LovelaceCardConfig } from "../types";
import { EntityConfig } from "../entity-rows/types";

export interface YamlChangedEvent extends Event {
Expand Down Expand Up @@ -30,3 +31,15 @@ export interface EditorTarget extends EventTarget {
checked?: boolean;
configValue?: string;
}

export interface SaveDialogParams {
hass: HomeAssistant;
config: LovelaceConfig;
reloadLovelace: () => void;
}

export interface EditDialogParams {
hass: HomeAssistant;
cardConfig: LovelaceCardConfig;
reloadLovelace: () => void;
}
7 changes: 7 additions & 0 deletions src/panels/lovelace/ha-panel-lovelace.js
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand Down Expand Up @@ -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",
Expand Down
7 changes: 5 additions & 2 deletions src/panels/lovelace/hui-root.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {};
Expand Down Expand Up @@ -275,7 +275,10 @@ class HUIRoot extends NavigateMixin(EventsMixin(PolymerElement)) {

_editModeEnable() {
if (this.config._frontendAuto) {
alert("Unable to edit automatic generated UI yet.");
showSaveDialog(this, this.hass, this.config, () => {
this.fire("config-refresh");
this._editMode = true;
});
return;
}
this._editMode = true;
Expand Down
1 change: 1 addition & 0 deletions src/panels/lovelace/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export interface LovelaceViewConfig {
id?: string;
icon?: string;
}

export interface LovelaceConfig {
_frontendAuto: boolean;
title?: string;
Expand Down
7 changes: 7 additions & 0 deletions src/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -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'.",
Expand Down