diff --git a/src/panels/lovelace/common/data.ts b/src/panels/lovelace/common/data.ts index 9baa221ab8..685c00c52b 100644 --- a/src/panels/lovelace/common/data.ts +++ b/src/panels/lovelace/common/data.ts @@ -1,4 +1,5 @@ import { HomeAssistant } from "../../../types"; +import { LovelaceConfig } from "../types"; export const getCardConfig = ( hass: HomeAssistant, @@ -12,10 +13,17 @@ export const getCardConfig = ( export const updateCardConfig = ( hass: HomeAssistant, cardId: string, - config: any + config: LovelaceConfig | string, + configFormat: "json" | "yaml" ): Promise => - hass!.callWS({ + hass.callWS({ type: "lovelace/config/card/update", card_id: cardId, 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 6b8578dca4..436cfa65c7 100644 --- a/src/panels/lovelace/components/hui-card-options.ts +++ b/src/panels/lovelace/components/hui-card-options.ts @@ -2,17 +2,16 @@ import "@polymer/paper-button/paper-button"; import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element"; import { fireEvent } from "../../../common/dom/fire_event"; import { HomeAssistant } from "../../../types"; +import { LovelaceConfig } from "../types"; let registeredDialog = false; export class HuiCardOptions extends LitElement { - public cardId?: string; + public cardConfig?: LovelaceConfig; protected hass?: HomeAssistant; static get properties(): PropertyDeclarations { - return { - hass: {}, - }; + return { hass: {} }; } public connectedCallback() { @@ -51,7 +50,7 @@ export class HuiCardOptions extends LitElement { private _editCard() { fireEvent(this, "show-edit-card", { hass: this.hass, - cardId: this.cardId, + cardConfig: this.cardConfig, reloadLovelace: () => fireEvent(this, "config-refresh"), }); } diff --git a/src/panels/lovelace/editor/hui-card-preview.ts b/src/panels/lovelace/editor/hui-card-preview.ts new file mode 100644 index 0000000000..57f4491fbd --- /dev/null +++ b/src/panels/lovelace/editor/hui-card-preview.ts @@ -0,0 +1,73 @@ +import "@polymer/paper-input/paper-textarea"; + +import createCardElement from "../common/create-card-element"; +import createErrorCardConfig from "../common/create-error-card-config"; +import { HomeAssistant } from "../../../types"; +import { LovelaceCard, LovelaceConfig } from "../types"; +import { ConfigError } from "./types"; + +const CUSTOM_TYPE_PREFIX = "custom:"; + +export class HuiCardPreview extends HTMLElement { + private _hass?: HomeAssistant; + private _element?: LovelaceCard; + + set hass(value: HomeAssistant) { + this._hass = value; + if (this._element) { + this._element.hass = value; + } + } + + set error(error: ConfigError) { + const configValue = createErrorCardConfig( + `${error.type}: ${error.message}`, + undefined + ); + + this._createCard(configValue); + } + + set config(configValue: LovelaceConfig) { + if (!configValue) { + return; + } + + if (!this._element) { + this._createCard(configValue); + return; + } + + const tag = configValue.type.startsWith(CUSTOM_TYPE_PREFIX) + ? configValue.type.substr(CUSTOM_TYPE_PREFIX.length) + : `hui-${configValue.type}-card`; + + if (tag.toUpperCase() === this._element.tagName) { + this._element.setConfig(configValue); + } else { + this._createCard(configValue); + } + } + + private _createCard(configValue: LovelaceConfig): void { + if (this._element) { + this.removeChild(this._element); + } + + this._element = createCardElement(configValue); + + if (this._hass) { + this._element!.hass = this._hass; + } + + this.appendChild(this._element!); + } +} + +declare global { + interface HTMLElementTagNameMap { + "hui-card-preview": HuiCardPreview; + } +} + +customElements.define("hui-card-preview", HuiCardPreview); diff --git a/src/panels/lovelace/editor/hui-dialog-edit-card.ts b/src/panels/lovelace/editor/hui-dialog-edit-card.ts index b982e09487..478c0c1b48 100644 --- a/src/panels/lovelace/editor/hui-dialog-edit-card.ts +++ b/src/panels/lovelace/editor/hui-dialog-edit-card.ts @@ -1,229 +1,52 @@ import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element"; -import yaml from "js-yaml"; -import { when } from "lit-html/directives/when"; import { TemplateResult } from "lit-html"; -import "@polymer/paper-button/paper-button"; -import "@polymer/paper-input/paper-textarea"; -import "@polymer/paper-dialog-scrollable/paper-dialog-scrollable"; -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 { HomeAssistant } from "../../../types"; -import { getCardConfig, updateCardConfig } from "../common/data"; -import { fireEvent } from "../../../common/dom/fire_event"; - -import "./hui-yaml-editor"; -import "./hui-yaml-card-preview"; -// This is not a duplicate import, one is for types, one is for element. -// tslint:disable-next-line -import { HuiYAMLCardPreview } from "./hui-yaml-card-preview"; -import { LovelaceCardEditor, LovelaceConfig } from "../types"; -import { YamlChangedEvent, ConfigValue } from "./types"; - -const CUSTOM_TYPE_PREFIX = "custom:"; +import { LovelaceConfig } from "../types"; +import "./hui-edit-card"; +import "./hui-migrate-config"; export class HuiDialogEditCard extends LitElement { - protected hass?: HomeAssistant; - private _cardId?: string; - private _originalConfigYaml?: string; - private _configElement?: LovelaceCardEditor | null; + protected _hass?: HomeAssistant; + private _cardConfig?: LovelaceConfig; private _reloadLovelace?: () => void; - private _editorToggle?: boolean; - private _configValue?: ConfigValue; static get properties(): PropertyDeclarations { return { - hass: {}, - cardId: { - type: Number, - }, - _dialogClosedCallback: {}, - _configElement: {}, - _editorToggle: {}, + _hass: {}, + _cardConfig: {}, }; } - public async showDialog({ hass, cardId, reloadLovelace }) { - this.hass = hass; - this._cardId = cardId; + public async showDialog({ hass, cardConfig, reloadLovelace }): Promise { + this._hass = hass; + this._cardConfig = cardConfig; this._reloadLovelace = reloadLovelace; - this._editorToggle = true; - this._configElement = undefined; - this._configValue = { format: "yaml", value: "" }; - this._loadConfig().then(() => this._loadConfigElement()); - // Wait till dialog is rendered. await this.updateComplete; - this._dialog.open(); - } - - private get _dialog(): PaperDialogElement { - return this.shadowRoot!.querySelector("paper-dialog")!; - } - - private get _previewEl(): HuiYAMLCardPreview { - return this.shadowRoot!.querySelector("hui-yaml-card-preview")!; + (this.shadowRoot!.children[0] as any).showDialog(); } protected render(): TemplateResult { return html` - - -

Card Configuration

- - ${ - this._editorToggle && this._configElement !== null - ? html` -
- ${ - when( - this._configElement, - () => this._configElement, - () => - html` - Loading... - ` - ) - } -
- ` - : html` - - ` - } - -
-
- Toggle Editor - Cancel - Save -
-
+ ${ + this._cardConfig!.id + ? html` + + + ` + : html` + + ` + } `; } - - private _handleYamlChanged(ev: YamlChangedEvent): void { - this._configValue = { format: "yaml", value: ev.detail.yaml }; - this._updatePreview(this._configValue); - } - - private _handleJSConfigChanged(value: LovelaceConfig): void { - this._configElement!.setConfig(value); - this._configValue = { format: "js", value }; - this._updatePreview(this._configValue); - } - - private _updatePreview(value: ConfigValue) { - if (!this._previewEl) { - return; - } - this._previewEl.value = value; - } - - private _closeDialog(): void { - this._dialog.close(); - } - - private _toggleEditor(): void { - if (this._editorToggle && this._configValue!.format === "js") { - this._configValue = { - format: "yaml", - value: yaml.safeDump(this._configValue!.value), - }; - } else if (this._configElement && this._configValue!.format === "yaml") { - this._configValue = { - format: "js", - value: yaml.safeLoad(this._configValue!.value), - }; - this._configElement.setConfig(this._configValue!.value as LovelaceConfig); - } - this._editorToggle = !this._editorToggle; - } - - private async _loadConfig(): Promise { - const cardConfig = await getCardConfig(this.hass!, this._cardId!); - this._configValue = { - format: "yaml", - value: cardConfig, - }; - this._originalConfigYaml = cardConfig; - - // This will center the dialog with the updated config Element - fireEvent(this._dialog, "iron-resize"); - } - - private async _loadConfigElement(): Promise { - const conf = yaml.safeLoad(this._configValue!.value); - - const tag = conf.type.startsWith(CUSTOM_TYPE_PREFIX) - ? conf.type.substr(CUSTOM_TYPE_PREFIX.length) - : `hui-${conf.type}-card`; - - const elClass = customElements.get(tag); - let configElement; - - try { - configElement = await elClass.getConfigElement(); - } catch (err) { - this._configElement = null; - return; - } - - configElement.setConfig(conf); - configElement.hass = this.hass; - configElement.addEventListener("config-changed", (ev) => - this._handleJSConfigChanged(ev.detail.config) - ); - this._configValue = { format: "js", value: conf }; - this._configElement = configElement; - - // This will center the dialog with the updated config Element - fireEvent(this._dialog, "iron-resize"); - } - - private async _updateConfigInBackend(): Promise { - if (this._configValue!.format === "js") { - this._configValue = { - format: "yaml", - value: yaml.safeDump(this._configValue!.value), - }; - } - - if (this._configValue!.value === this._originalConfigYaml) { - this._dialog.close(); - return; - } - - try { - await updateCardConfig( - this.hass!, - this._cardId!, - this._configValue!.value - ); - this._dialog.close(); - this._reloadLovelace!(); - } catch (err) { - alert(`Saving failed: ${err.reason}`); - } - } } declare global { diff --git a/src/panels/lovelace/editor/hui-edit-card.ts b/src/panels/lovelace/editor/hui-edit-card.ts new file mode 100644 index 0000000000..3fd7022c82 --- /dev/null +++ b/src/panels/lovelace/editor/hui-edit-card.ts @@ -0,0 +1,376 @@ +import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element"; +import { classMap } from "lit-html/directives/classMap"; +import { TemplateResult } from "lit-html"; +import yaml from "js-yaml"; + +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 "@polymer/paper-input/paper-textarea"; +import "@polymer/paper-dialog-scrollable/paper-dialog-scrollable"; +import { HomeAssistant } from "../../../types"; +import { updateCardConfig } from "../common/data"; +import { fireEvent } from "../../../common/dom/fire_event"; +import { hassLocalizeLitMixin } from "../../../mixins/lit-localize-mixin"; + +import "./hui-yaml-editor"; +import "./hui-card-preview"; +// This is not a duplicate import, one is for types, one is for element. +// tslint:disable-next-line +import { HuiCardPreview } from "./hui-card-preview"; +import { LovelaceCardEditor, LovelaceConfig } from "../types"; +import { YamlChangedEvent, ConfigValue, ConfigError } from "./types"; +import { extYamlSchema } from "./yaml-ext-schema"; + +const CUSTOM_TYPE_PREFIX = "custom:"; + +export class HuiEditCard extends hassLocalizeLitMixin(LitElement) { + protected hass?: HomeAssistant; + private _cardId?: string; + private _originalConfig?: LovelaceConfig; + private _configElement?: LovelaceCardEditor | null; + private _uiEditor?: boolean; + private _configValue?: ConfigValue; + private _configState?: string; + private _loading?: boolean; + private _isToggleAvailable?: boolean; + private _saving: boolean; + + static get properties(): PropertyDeclarations { + return { + _hass: {}, + _cardId: {}, + _originalConfig: {}, + _configElement: {}, + _configValue: {}, + _configState: {}, + _uiEditor: {}, + _saving: {}, + _loading: {}, + _isToggleAvailable: {}, + }; + } + + protected constructor() { + super(); + this._saving = false; + } + + set cardConfig(cardConfig: LovelaceConfig) { + this._originalConfig = cardConfig; + if (String(cardConfig.id) !== this._cardId) { + this._loading = true; + this._uiEditor = true; + this._configElement = undefined; + this._configValue = { format: "yaml", value: undefined }; + this._configState = "OK"; + this._isToggleAvailable = false; + this._cardId = String(cardConfig.id); + this._loadConfigElement(); + } + } + + public async showDialog(): Promise { + // Wait till dialog is rendered. + if (this._dialog == null) { + await this.updateComplete; + } + this._dialog.open(); + } + + private get _dialog(): PaperDialogElement { + return this.shadowRoot!.querySelector("paper-dialog")!; + } + + private get _previewEl(): HuiCardPreview { + return this.shadowRoot!.querySelector("hui-card-preview")!; + } + + protected render(): TemplateResult { + return html` + ${this.renderStyle()} + +

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

+ + + ${ + this._uiEditor && this._configElement !== null + ? html` +
${this._configElement}
+ ` + : html` + + ` + } + +
+ ${ + !this._loading + ? html` +
+ ${ + this.localize( + "ui.panel.lovelace.editor.edit.toggle_editor" + ) + } + ${this.localize("ui.common.cancel")} + + + ${ + this.localize("ui.panel.lovelace.editor.edit.save") + } +
+ ` + : html`` + } +
+ `; + } + + private renderStyle(): TemplateResult { + return html` + + `; + } + + private _toggleEditor(): void { + if (!this._isToggleAvailable) { + alert("You can't switch editor."); + return; + } + if (this._uiEditor && this._configValue!.format === "json") { + if (this._isConfigChanged()) { + this._configValue = { + format: "yaml", + value: yaml.safeDump(this._configValue!.value), + }; + } else { + this._configValue = { format: "yaml", value: undefined }; + } + this._uiEditor = !this._uiEditor; + } else if (this._configElement && this._configValue!.format === "yaml") { + this._configValue = { + format: "json", + value: yaml.safeLoad(this._configValue!.value, { + schema: extYamlSchema, + }), + }; + this._configElement.setConfig(this._configValue!.value as LovelaceConfig); + this._uiEditor = !this._uiEditor; + } + this._resizeDialog(); + } + + private _save(): void { + this._saving = true; + this._updateConfigInBackend(); + } + + private _saveDone(): void { + this._saving = false; + } + + private async _loadedDialog(): Promise { + await this.updateComplete; + this._loading = false; + this._resizeDialog(); + } + + private async _resizeDialog(): Promise { + await this.updateComplete; + fireEvent(this._dialog, "iron-resize"); + } + + private _closeDialog(): void { + this._dialog.close(); + } + + private async _updateConfigInBackend(): Promise { + if (!this._isConfigValid()) { + alert("Your config is not valid, please fix your config before saving."); + this._saveDone(); + return; + } + + if (!this._isConfigChanged()) { + this._closeDialog(); + this._saveDone(); + return; + } + + try { + await updateCardConfig( + this.hass!, + this._cardId!, + this._configValue!.value!, + this._configValue!.format + ); + this._closeDialog(); + this._saveDone(); + fireEvent(this, "reload-lovelace"); + } catch (err) { + alert(`Saving failed: ${err.message}`); + this._saveDone(); + } + } + + private _handleYamlChanged(ev: YamlChangedEvent): void { + this._configValue = { format: "yaml", value: ev.detail.yaml }; + try { + const config = yaml.safeLoad(this._configValue.value, { + schema: extYamlSchema, + }) as LovelaceConfig; + this._updatePreview(config); + this._configState = "OK"; + if (!this._isToggleAvailable && this._configElement !== null) { + this._isToggleAvailable = true; + } + } catch (err) { + this._isToggleAvailable = false; + this._configState = "YAML_ERROR"; + this._setPreviewError({ + type: "YAML Error", + message: err, + }); + } + } + + private _handleUIConfigChanged(value: LovelaceConfig): void { + this._configElement!.setConfig(value); + this._configValue = { format: "json", value }; + this._updatePreview(value); + } + + private _updatePreview(config: LovelaceConfig) { + if (!this._previewEl) { + return; + } + + this._previewEl.config = config; + + if (this._loading) { + this._loadedDialog(); + } else { + this._resizeDialog(); + } + } + + private _setPreviewError(error: ConfigError) { + if (!this._previewEl) { + return; + } + this._previewEl.error = error; + + this._resizeDialog(); + } + + private _isConfigValid() { + if (!this._cardId || !this._configValue || !this._configValue.value) { + return false; + } + if (this._configState === "OK") { + return true; + } else { + return false; + } + } + + private _isConfigChanged(): boolean { + const configValue = + this._configValue!.format === "yaml" + ? yaml.safeDump(this._configValue!.value) + : this._configValue!.value; + return JSON.stringify(configValue) !== JSON.stringify(this._originalConfig); + } + + private async _loadConfigElement(): Promise { + const conf = this._originalConfig; + const tag = conf!.type.startsWith(CUSTOM_TYPE_PREFIX) + ? conf!.type.substr(CUSTOM_TYPE_PREFIX.length) + : `hui-${conf!.type}-card`; + + const elClass = customElements.get(tag); + let configElement; + + try { + configElement = await elClass.getConfigElement(); + } catch (err) { + this._configElement = null; + this._uiEditor = false; + return; + } + + this._isToggleAvailable = true; + + configElement.setConfig(conf); + configElement.hass = this.hass; + configElement.addEventListener("config-changed", (ev) => + this._handleUIConfigChanged(ev.detail.config) + ); + this._configValue = { format: "json", value: conf! }; + this._configElement = configElement; + } +} + +declare global { + interface HTMLElementTagNameMap { + "hui-edit-card": HuiEditCard; + } +} + +customElements.define("hui-edit-card", HuiEditCard); diff --git a/src/panels/lovelace/editor/hui-migrate-config.ts b/src/panels/lovelace/editor/hui-migrate-config.ts new file mode 100644 index 0000000000..078a8a53cc --- /dev/null +++ b/src/panels/lovelace/editor/hui-migrate-config.ts @@ -0,0 +1,112 @@ +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 { fireEvent } from "../../../common/dom/fire_event"; + +import { HomeAssistant } from "../../../types"; +import { hassLocalizeLitMixin } from "../../../mixins/lit-localize-mixin"; +import { migrateConfig } from "../common/data"; + +export class HuiMigrateConfig extends hassLocalizeLitMixin(LitElement) { + protected hass?: HomeAssistant; + private _migrating?: boolean; + + static get properties(): PropertyDeclarations { + return { _hass: {}, _migrating: {} }; + } + + private get _dialog(): PaperDialogElement { + return this.shadowRoot!.querySelector("paper-dialog")!; + } + + public async showDialog(): Promise { + // Wait till dialog is rendered. + if (this._dialog == null) { + await this.updateComplete; + } + this._dialog.open(); + } + + protected render(): TemplateResult { + return html` + ${this.renderStyle()} + +

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

+ +

${this.localize("ui.panel.lovelace.editor.migrate.para_no_id")}

+

+ ${this.localize("ui.panel.lovelace.editor.migrate.para_migrate")} +

+
+
+ ${this.localize("ui.common.cancel")} + + + ${ + this.localize("ui.panel.lovelace.editor.migrate.migrate") + } +
+
+ `; + } + + private renderStyle(): TemplateResult { + return html` + + `; + } + + private _closeDialog(): void { + this._dialog.close(); + } + + private async _migrateConfig(): Promise { + this._migrating = true; + try { + await migrateConfig(this.hass!); + this._closeDialog(); + fireEvent(this, "reload-lovelace"); + } catch (err) { + alert(`Migration failed: ${err.message}`); + this._migrating = false; + } + } +} + +declare global { + interface HTMLElementTagNameMap { + "hui-migrate-config": HuiMigrateConfig; + } +} + +customElements.define("hui-migrate-config", HuiMigrateConfig); diff --git a/src/panels/lovelace/editor/hui-yaml-card-preview.ts b/src/panels/lovelace/editor/hui-yaml-card-preview.ts deleted file mode 100644 index c187c2c496..0000000000 --- a/src/panels/lovelace/editor/hui-yaml-card-preview.ts +++ /dev/null @@ -1,57 +0,0 @@ -import yaml from "js-yaml"; - -import "@polymer/paper-input/paper-textarea"; - -import createCardElement from "../common/create-card-element"; -import createErrorCardConfig from "../common/create-error-card-config"; -import { HomeAssistant } from "../../../types"; -import { LovelaceCard } from "../types"; -import { ConfigValue } from "./types"; - -export class HuiYAMLCardPreview extends HTMLElement { - private _hass?: HomeAssistant; - - set hass(value: HomeAssistant) { - this._hass = value; - if (this.lastChild) { - (this.lastChild as LovelaceCard).hass = value; - } - } - - set value(configValue: ConfigValue) { - if (this.lastChild) { - this.removeChild(this.lastChild); - } - - if (!configValue.value || configValue.value === "") { - return; - } - - let conf; - if (configValue.format === "yaml") { - try { - conf = yaml.safeLoad(configValue.value); - } catch (err) { - conf = createErrorCardConfig(`Invalid YAML: ${err.message}`, undefined); - } - } else { - conf = configValue.value; - } - - const element = createCardElement(conf); - - if (this._hass) { - element.hass = this._hass; - } - - this.appendChild(element); - } -} - -declare global { - interface HTMLElementTagNameMap { - "hui-yaml-card-preview": HuiYAMLCardPreview; - } -} - -customElements.define("hui-yaml-card-preview", HuiYAMLCardPreview); diff --git a/src/panels/lovelace/editor/hui-yaml-editor.ts b/src/panels/lovelace/editor/hui-yaml-editor.ts index eb712779a4..da8f322cbf 100644 --- a/src/panels/lovelace/editor/hui-yaml-editor.ts +++ b/src/panels/lovelace/editor/hui-yaml-editor.ts @@ -1,37 +1,87 @@ import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element"; import { TemplateResult } from "lit-html"; import "@polymer/paper-input/paper-textarea"; +import "@polymer/paper-spinner/paper-spinner"; +import { HomeAssistant } from "../../../types"; import { fireEvent } from "../../../common/dom/fire_event"; +import { getCardConfig } from "../common/data"; export class HuiYAMLEditor extends LitElement { - public yaml?: string; + public cardId?: string; + protected hass?: HomeAssistant; + private _yaml?: string; + private _loading?: boolean; static get properties(): PropertyDeclarations { - return { - yaml: {}, - }; + return { _yaml: {}, cardId: {} }; + } + + set yaml(yaml: string) { + if (yaml === undefined) { + this._loading = true; + this._loadConfig(); + } else { + this._yaml = yaml; + if (this._loading) { + this._loading = false; + } + } } protected render(): TemplateResult { return html` - + ${this.renderStyle()} + `; } - private _valueChanged(ev: MouseEvent): void { + private renderStyle(): TemplateResult { + return html` + + `; + } + + private async _loadConfig(): Promise { + if (!this.hass || !this.cardId) { + return; + } + + this._yaml = await getCardConfig(this.hass, this.cardId); + if (this._loading) { + this._loading = false; + } + } + + private _valueChanged(ev: Event): void { const target = ev.target! as any; - this.yaml = target.value; - fireEvent(this, "yaml-changed", { yaml: target.value }); + this._yaml = target.value; + fireEvent(this, "yaml-changed", { + yaml: target.value, + }); } } diff --git a/src/panels/lovelace/editor/types.ts b/src/panels/lovelace/editor/types.ts index 534583bac8..3fbc360b47 100644 --- a/src/panels/lovelace/editor/types.ts +++ b/src/panels/lovelace/editor/types.ts @@ -8,8 +8,13 @@ export interface YamlChangedEvent extends Event { } export interface ConfigValue { - format: "js" | "yaml"; - value: string | LovelaceConfig; + format: "json" | "yaml"; + value?: string | LovelaceConfig; +} + +export interface ConfigError { + type: string; + message: string; } export interface EntitiesEditorEvent { diff --git a/src/panels/lovelace/editor/yaml-ext-schema.ts b/src/panels/lovelace/editor/yaml-ext-schema.ts new file mode 100644 index 0000000000..357962d911 --- /dev/null +++ b/src/panels/lovelace/editor/yaml-ext-schema.ts @@ -0,0 +1,22 @@ +import yaml from "js-yaml"; + +const secretYamlType = new yaml.Type("!secret", { + kind: "scalar", + construct(data) { + data = data || ""; + return "!secret " + data; + }, +}); + +const includeYamlType = new yaml.Type("!include", { + kind: "scalar", + construct(data) { + data = data || ""; + return "!include " + data; + }, +}); + +export const extYamlSchema = yaml.Schema.create([ + secretYamlType, + includeYamlType, +]); diff --git a/src/panels/lovelace/hui-view.js b/src/panels/lovelace/hui-view.js index 76826f0255..92c6b5d074 100644 --- a/src/panels/lovelace/hui-view.js +++ b/src/panels/lovelace/hui-view.js @@ -147,7 +147,7 @@ class HUIView extends PolymerElement { const wrapper = document.createElement("hui-card-options"); wrapper.hass = this.hass; - wrapper.cardId = cardConfig.id; + wrapper.cardConfig = cardConfig; wrapper.editMode = this.editMode; wrapper.appendChild(element); elementsToAppend.push(wrapper); diff --git a/src/panels/lovelace/types.ts b/src/panels/lovelace/types.ts index 32905d0534..7f3fabeaae 100644 --- a/src/panels/lovelace/types.ts +++ b/src/panels/lovelace/types.ts @@ -2,6 +2,7 @@ import { HomeAssistant } from "../../types"; export interface LovelaceConfig { type: string; + id: string; } export interface LovelaceCard extends HTMLElement { diff --git a/src/translations/en.json b/src/translations/en.json index eef749dda3..b7e0846d2b 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -765,6 +765,19 @@ "clear_items": "Clear checked items", "add_item": "Add item" } + }, + "editor": { + "edit": { + "header": "Card Configuration", + "save": "Save", + "toggle_editor": "Toggle Editor" + }, + "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'.", + "para_migrate": "Home Assistant can add ID's to all your cards and views automatically for you by pressing the 'Migrate config' button.", + "migrate": "Migrate config" + } } }, "mailbox": {