@@ -117,11 +115,15 @@ export class HuiEntitiesCardEditor extends hassLocalizeLitMixin(LitElement)
this._config.entities = ev.detail.entities;
this._configEntities = processEditorEntities(this._config.entities);
} else if (target.configValue) {
- this._config = {
- ...this._config,
- [target.configValue]:
- target.checked !== undefined ? target.checked : target.value,
- };
+ if (target.value === "") {
+ delete this._config[target.configValue!];
+ } else {
+ this._config = {
+ ...this._config,
+ [target.configValue]:
+ target.checked !== undefined ? target.checked : target.value,
+ };
+ }
}
fireEvent(this, "config-changed", { config: this._config });
diff --git a/src/panels/lovelace/editor/config-elements/hui-entity-button-card-editor.ts b/src/panels/lovelace/editor/config-elements/hui-entity-button-card-editor.ts
new file mode 100644
index 0000000000..d5a7669008
--- /dev/null
+++ b/src/panels/lovelace/editor/config-elements/hui-entity-button-card-editor.ts
@@ -0,0 +1,165 @@
+import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element";
+import { TemplateResult } from "lit-html";
+import "@polymer/paper-input/paper-input";
+
+import { struct } from "../../common/structs/struct";
+import {
+ EntitiesEditorEvent,
+ EditorTarget,
+ actionConfigStruct,
+} from "../types";
+import { hassLocalizeLitMixin } from "../../../../mixins/lit-localize-mixin";
+import { HomeAssistant } from "../../../../types";
+import { LovelaceCardEditor } from "../../types";
+import { fireEvent } from "../../../../common/dom/fire_event";
+import { Config } from "../../cards/hui-entity-button-card";
+import { configElementStyle } from "./config-elements-style";
+import { ActionConfig } from "../../../../data/lovelace";
+
+import "../../components/hui-action-editor";
+import "../../components/hui-theme-select-editor";
+import "../../components/hui-entity-editor";
+
+const cardConfigStruct = struct({
+ type: "string",
+ entity: "string?",
+ name: "string?",
+ icon: "string?",
+ tap_action: actionConfigStruct,
+ hold_action: actionConfigStruct,
+ theme: "string?",
+});
+
+export class HuiEntityButtonCardEditor extends hassLocalizeLitMixin(LitElement)
+ implements LovelaceCardEditor {
+ public hass?: HomeAssistant;
+ private _config?: Config;
+
+ public setConfig(config: Config): void {
+ config = cardConfigStruct(config);
+ this._config = config;
+ }
+
+ static get properties(): PropertyDeclarations {
+ return { hass: {}, _config: {} };
+ }
+
+ get _entity(): string {
+ return this._config!.entity || "";
+ }
+
+ get _name(): string {
+ return this._config!.name || "";
+ }
+
+ get _icon(): string {
+ return this._config!.icon || "";
+ }
+
+ get _tap_action(): ActionConfig {
+ return this._config!.tap_action || { action: "more-info" };
+ }
+
+ get _hold_action(): ActionConfig {
+ return this._config!.hold_action || { action: "none" };
+ }
+
+ get _theme(): string {
+ return this._config!.theme || "default";
+ }
+
+ protected render(): TemplateResult {
+ if (!this.hass) {
+ return html``;
+ }
+
+ const actions = ["more-info", "toggle", "navigate", "call-service", "none"];
+
+ return html`
+ ${configElementStyle}
+
+ `;
+ }
+
+ private _valueChanged(ev: EntitiesEditorEvent): void {
+ if (!this._config || !this.hass) {
+ return;
+ }
+ const target = ev.target! as EditorTarget;
+
+ if (
+ this[`_${target.configValue}`] === target.value ||
+ this[`_${target.configValue}`] === target.config
+ ) {
+ return;
+ }
+ if (target.configValue) {
+ if (target.value === "") {
+ delete this._config[target.configValue!];
+ } else {
+ this._config = {
+ ...this._config,
+ [target.configValue!]: target.value ? target.value : target.config,
+ };
+ }
+ }
+ fireEvent(this, "config-changed", { config: this._config });
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ "hui-entity-button-card-editor": HuiEntityButtonCardEditor;
+ }
+}
+
+customElements.define(
+ "hui-entity-button-card-editor",
+ HuiEntityButtonCardEditor
+);
diff --git a/src/panels/lovelace/editor/config-elements/hui-gauge-card-editor.ts b/src/panels/lovelace/editor/config-elements/hui-gauge-card-editor.ts
new file mode 100644
index 0000000000..1b7d734940
--- /dev/null
+++ b/src/panels/lovelace/editor/config-elements/hui-gauge-card-editor.ts
@@ -0,0 +1,244 @@
+import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element";
+import { TemplateResult } from "lit-html";
+import "@polymer/paper-input/paper-input";
+import "@polymer/paper-toggle-button/paper-toggle-button";
+
+import { struct } from "../../common/structs/struct";
+import { EntitiesEditorEvent, EditorTarget } from "../types";
+import { hassLocalizeLitMixin } from "../../../../mixins/lit-localize-mixin";
+import { HomeAssistant } from "../../../../types";
+import { LovelaceCardEditor } from "../../types";
+import { fireEvent } from "../../../../common/dom/fire_event";
+import { Config, SeverityConfig } from "../../cards/hui-gauge-card";
+import { configElementStyle } from "./config-elements-style";
+
+import "../../components/hui-theme-select-editor";
+import "../../components/hui-entity-editor";
+
+const cardConfigStruct = struct({
+ type: "string",
+ name: "string?",
+ entity: "string?",
+ unit: "string?",
+ min: "number?",
+ max: "number?",
+ severity: "object?",
+ theme: "string?",
+});
+
+export class HuiGaugeCardEditor extends hassLocalizeLitMixin(LitElement)
+ implements LovelaceCardEditor {
+ public hass?: HomeAssistant;
+ private _config?: Config;
+ private _useSeverity?: boolean;
+
+ public setConfig(config: Config): void {
+ config = cardConfigStruct(config);
+ this._useSeverity = config.severity ? true : false;
+ this._config = config;
+ }
+
+ static get properties(): PropertyDeclarations {
+ return { hass: {}, _config: {} };
+ }
+
+ get _name(): string {
+ return this._config!.name || "";
+ }
+
+ get _entity(): string {
+ return this._config!.entity || "";
+ }
+
+ get _unit(): string {
+ return this._config!.unit || "";
+ }
+
+ get _theme(): string {
+ return this._config!.theme || "default";
+ }
+
+ get _min(): number {
+ return this._config!.number || 0;
+ }
+
+ get _max(): number {
+ return this._config!.max || 100;
+ }
+
+ get _severity(): SeverityConfig | undefined {
+ return this._config!.severity || undefined;
+ }
+
+ protected render(): TemplateResult {
+ if (!this.hass) {
+ return html``;
+ }
+
+ return html`
+ ${configElementStyle} ${this.renderStyle()}
+
+ `;
+ }
+
+ private renderStyle(): TemplateResult {
+ return html`
+
+ `;
+ }
+
+ private _toggleSeverity(ev: EntitiesEditorEvent): void {
+ if (!this._config || !this.hass) {
+ return;
+ }
+ const target = ev.target! as EditorTarget;
+
+ this._config.severity = target.checked
+ ? {
+ green: 0,
+ yellow: 0,
+ red: 0,
+ }
+ : undefined;
+ fireEvent(this, "config-changed", { config: this._config });
+ }
+
+ private _severityChanged(ev: EntitiesEditorEvent): void {
+ if (!this._config || !this.hass) {
+ return;
+ }
+ const target = ev.target! as EditorTarget;
+ const severity = {
+ ...this._config.severity,
+ [target.configValue!]: Number(target.value),
+ };
+ this._config = {
+ ...this._config,
+ severity,
+ };
+ fireEvent(this, "config-changed", { config: this._config });
+ }
+
+ private _valueChanged(ev: EntitiesEditorEvent): void {
+ if (!this._config || !this.hass) {
+ return;
+ }
+ const target = ev.target! as EditorTarget;
+
+ if (target.configValue) {
+ if (
+ target.value === "" ||
+ (target.type === "number" && isNaN(Number(target.value)))
+ ) {
+ delete this._config[target.configValue!];
+ } else {
+ let value: any = target.value;
+ if (target.type === "number") {
+ value = Number(value);
+ }
+ this._config = { ...this._config, [target.configValue!]: value };
+ }
+ }
+ fireEvent(this, "config-changed", { config: this._config });
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ "hui-gauge-card-editor": HuiGaugeCardEditor;
+ }
+}
+
+customElements.define("hui-gauge-card-editor", HuiGaugeCardEditor);
diff --git a/src/panels/lovelace/editor/config-elements/hui-glance-card-editor.ts b/src/panels/lovelace/editor/config-elements/hui-glance-card-editor.ts
index dadb82f702..da8b1ff2cd 100644
--- a/src/panels/lovelace/editor/config-elements/hui-glance-card-editor.ts
+++ b/src/panels/lovelace/editor/config-elements/hui-glance-card-editor.ts
@@ -1,11 +1,11 @@
import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element";
import { TemplateResult } from "lit-html";
-import { struct } from "../../common/structs/struct";
import "@polymer/paper-dropdown-menu/paper-dropdown-menu";
import "@polymer/paper-item/paper-item";
import "@polymer/paper-listbox/paper-listbox";
import "@polymer/paper-toggle-button/paper-toggle-button";
+import { struct } from "../../common/structs/struct";
import { processEditorEntities } from "../process-editor-entities";
import { EntitiesEditorEvent, EditorTarget } from "../types";
import { hassLocalizeLitMixin } from "../../../../mixins/lit-localize-mixin";
@@ -48,8 +48,7 @@ export class HuiGlanceCardEditor extends hassLocalizeLitMixin(LitElement)
public setConfig(config: Config): void {
config = cardConfigStruct(config);
-
- this._config = { type: "glance", ...config };
+ this._config = config;
this._configEntities = processEditorEntities(config.entities);
}
@@ -65,8 +64,8 @@ export class HuiGlanceCardEditor extends hassLocalizeLitMixin(LitElement)
return this._config!.theme || "Backend-selected";
}
- get _columns(): string {
- return this._config!.columns ? String(this._config!.columns) : "";
+ get _columns(): number {
+ return this._config!.columns || NaN;
}
protected render(): TemplateResult {
@@ -79,7 +78,7 @@ export class HuiGlanceCardEditor extends hassLocalizeLitMixin(LitElement)
@@ -93,7 +92,7 @@ export class HuiGlanceCardEditor extends hassLocalizeLitMixin(LitElement)
@@ -127,22 +126,29 @@ export class HuiGlanceCardEditor extends hassLocalizeLitMixin(LitElement)
}
const target = ev.target! as EditorTarget;
- if (this[`_${target.configValue}`] === target.value) {
+ if (target.configValue && this[`_${target.configValue}`] === target.value) {
return;
}
if (ev.detail && ev.detail.entities) {
this._config.entities = ev.detail.entities;
this._configEntities = processEditorEntities(this._config.entities);
} else if (target.configValue) {
- let value: any = target.value;
- if (target.type === "number") {
- value = Number(value);
+ if (
+ target.value === "" ||
+ (target.type === "number" && isNaN(Number(target.value)))
+ ) {
+ delete this._config[target.configValue!];
+ } else {
+ let value: any = target.value;
+ if (target.type === "number") {
+ value = Number(value);
+ }
+ this._config = {
+ ...this._config,
+ [target.configValue!]:
+ target.checked !== undefined ? target.checked : value,
+ };
}
- this._config = {
- ...this._config,
- [target.configValue!]:
- target.checked !== undefined ? target.checked : value,
- };
}
fireEvent(this, "config-changed", { config: this._config });
}
diff --git a/src/panels/lovelace/editor/config-elements/hui-iframe-card-editor.ts b/src/panels/lovelace/editor/config-elements/hui-iframe-card-editor.ts
new file mode 100644
index 0000000000..673156032d
--- /dev/null
+++ b/src/panels/lovelace/editor/config-elements/hui-iframe-card-editor.ts
@@ -0,0 +1,111 @@
+import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element";
+import { TemplateResult } from "lit-html";
+import "@polymer/paper-input/paper-input";
+
+import { struct } from "../../common/structs/struct";
+import { EntitiesEditorEvent, EditorTarget } from "../types";
+import { hassLocalizeLitMixin } from "../../../../mixins/lit-localize-mixin";
+import { HomeAssistant } from "../../../../types";
+import { LovelaceCardEditor } from "../../types";
+import { fireEvent } from "../../../../common/dom/fire_event";
+import { Config } from "../../cards/hui-iframe-card";
+import { configElementStyle } from "./config-elements-style";
+
+const cardConfigStruct = struct({
+ type: "string",
+ title: "string?",
+ url: "string?",
+ aspect_ratio: "string?",
+});
+
+export class HuiIframeCardEditor extends hassLocalizeLitMixin(LitElement)
+ implements LovelaceCardEditor {
+ public hass?: HomeAssistant;
+ private _config?: Config;
+
+ public setConfig(config: Config): void {
+ config = cardConfigStruct(config);
+ this._config = config;
+ }
+
+ static get properties(): PropertyDeclarations {
+ return { hass: {}, _config: {} };
+ }
+
+ get _title(): string {
+ return this._config!.title || "";
+ }
+
+ get _url(): string {
+ return this._config!.url || "";
+ }
+
+ get _aspect_ratio(): string {
+ return this._config!.aspect_ratio || "";
+ }
+
+ protected render(): TemplateResult {
+ if (!this.hass) {
+ return html``;
+ }
+
+ return html`
+ ${configElementStyle}
+
+ `;
+ }
+
+ private _valueChanged(ev: EntitiesEditorEvent): void {
+ if (!this._config || !this.hass) {
+ return;
+ }
+ const target = ev.target! as EditorTarget;
+ let value = target.value;
+
+ if (target.configValue! === "aspect_ratio" && target.value) {
+ value += "%";
+ }
+
+ if (this[`_${target.configValue}`] === value) {
+ return;
+ }
+ if (target.configValue) {
+ if (target.value === "") {
+ delete this._config[target.configValue!];
+ } else {
+ this._config = { ...this._config, [target.configValue!]: value };
+ }
+ }
+ fireEvent(this, "config-changed", { config: this._config });
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ "hui-iframe-card-editor": HuiIframeCardEditor;
+ }
+}
+
+customElements.define("hui-iframe-card-editor", HuiIframeCardEditor);
diff --git a/src/panels/lovelace/editor/config-elements/hui-light-card-editor.ts b/src/panels/lovelace/editor/config-elements/hui-light-card-editor.ts
new file mode 100644
index 0000000000..ba65142b1b
--- /dev/null
+++ b/src/panels/lovelace/editor/config-elements/hui-light-card-editor.ts
@@ -0,0 +1,113 @@
+import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element";
+import { TemplateResult } from "lit-html";
+import "@polymer/paper-input/paper-input";
+
+import { struct } from "../../common/structs/struct";
+import { EntitiesEditorEvent, EditorTarget } from "../types";
+import { hassLocalizeLitMixin } from "../../../../mixins/lit-localize-mixin";
+import { HomeAssistant } from "../../../../types";
+import { LovelaceCardEditor } from "../../types";
+import { fireEvent } from "../../../../common/dom/fire_event";
+import { Config } from "../../cards/hui-light-card";
+import { configElementStyle } from "./config-elements-style";
+
+import "../../components/hui-theme-select-editor";
+import "../../components/hui-entity-editor";
+
+const cardConfigStruct = struct({
+ type: "string",
+ name: "string?",
+ entity: "string?",
+ theme: "string?",
+});
+
+export class HuiLightCardEditor extends hassLocalizeLitMixin(LitElement)
+ implements LovelaceCardEditor {
+ public hass?: HomeAssistant;
+ private _config?: Config;
+
+ public setConfig(config: Config): void {
+ config = cardConfigStruct(config);
+ this._config = config;
+ }
+
+ static get properties(): PropertyDeclarations {
+ return { hass: {}, _config: {}, _configEntities: {} };
+ }
+
+ get _name(): string {
+ return this._config!.name || "";
+ }
+
+ get _theme(): string {
+ return this._config!.theme || "default";
+ }
+
+ get _entity(): string {
+ return this._config!.entity || "";
+ }
+
+ protected render(): TemplateResult {
+ if (!this.hass) {
+ return html``;
+ }
+
+ return html`
+ ${configElementStyle}
+
+ `;
+ }
+
+ private _valueChanged(ev: EntitiesEditorEvent): void {
+ if (!this._config || !this.hass) {
+ return;
+ }
+ const target = ev.target! as EditorTarget;
+
+ if (this[`_${target.configValue}`] === target.value) {
+ return;
+ }
+ if (target.configValue) {
+ if (target.value === "") {
+ delete this._config[target.configValue!];
+ } else {
+ this._config = {
+ ...this._config,
+ [target.configValue!]: target.value,
+ };
+ }
+ }
+ fireEvent(this, "config-changed", { config: this._config });
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ "hui-light-card-editor": HuiLightCardEditor;
+ }
+}
+
+customElements.define("hui-light-card-editor", HuiLightCardEditor);
diff --git a/src/panels/lovelace/editor/config-elements/hui-map-card-editor.ts b/src/panels/lovelace/editor/config-elements/hui-map-card-editor.ts
new file mode 100644
index 0000000000..ed6531a0e0
--- /dev/null
+++ b/src/panels/lovelace/editor/config-elements/hui-map-card-editor.ts
@@ -0,0 +1,143 @@
+import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element";
+import { TemplateResult } from "lit-html";
+import "@polymer/paper-input/paper-input";
+
+import { struct } from "../../common/structs/struct";
+import { EntitiesEditorEvent, EditorTarget } from "../types";
+import { hassLocalizeLitMixin } from "../../../../mixins/lit-localize-mixin";
+import { HomeAssistant } from "../../../../types";
+import { LovelaceCardEditor } from "../../types";
+import { fireEvent } from "../../../../common/dom/fire_event";
+import { Config } from "../../cards/hui-alarm-panel-card";
+import { configElementStyle } from "./config-elements-style";
+import { processEditorEntities } from "../process-editor-entities";
+import { EntityConfig } from "../../entity-rows/types";
+
+import "../../components/hui-entity-editor";
+
+const entitiesConfigStruct = struct.union([
+ {
+ entity: "entity-id",
+ name: "string?",
+ icon: "icon?",
+ },
+ "entity-id",
+]);
+
+const cardConfigStruct = struct({
+ type: "string",
+ title: "string?",
+ aspect_ratio: "string?",
+ default_zoom: "number?",
+ entities: [entitiesConfigStruct],
+});
+
+export class HuiMapCardEditor extends hassLocalizeLitMixin(LitElement)
+ implements LovelaceCardEditor {
+ public hass?: HomeAssistant;
+ private _config?: Config;
+ private _configEntities?: EntityConfig[];
+
+ public setConfig(config: Config): void {
+ config = cardConfigStruct(config);
+ this._config = config;
+ this._configEntities = processEditorEntities(config.entities);
+ }
+
+ static get properties(): PropertyDeclarations {
+ return { hass: {}, _config: {}, _configEntities: {} };
+ }
+
+ get _title(): string {
+ return this._config!.title || "";
+ }
+
+ get _aspect_ratio(): string {
+ return this._config!.aspect_ratio || "";
+ }
+
+ get _default_zoom(): number {
+ return this._config!.default_zoom || NaN;
+ }
+
+ get _entities(): string[] {
+ return this._config!.entities || [];
+ }
+
+ protected render(): TemplateResult {
+ if (!this.hass) {
+ return html``;
+ }
+
+ return html`
+ ${configElementStyle}
+
+ `;
+ }
+
+ private _valueChanged(ev: EntitiesEditorEvent): void {
+ if (!this._config || !this.hass) {
+ return;
+ }
+ const target = ev.target! as EditorTarget;
+ if (target.configValue && this[`_${target.configValue}`] === target.value) {
+ return;
+ }
+ if (ev.detail && ev.detail.entities) {
+ this._config.entities = ev.detail.entities;
+ this._configEntities = processEditorEntities(this._config.entities);
+ } else if (target.configValue) {
+ if (
+ target.value === "" ||
+ (target.type === "number" && isNaN(Number(target.value)))
+ ) {
+ delete this._config[target.configValue!];
+ } else {
+ let value: any = target.value;
+ if (target.type === "number") {
+ value = Number(value);
+ }
+ this._config = {
+ ...this._config,
+ [target.configValue!]: value,
+ };
+ }
+ }
+ fireEvent(this, "config-changed", { config: this._config });
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ "hui-map-card-editor": HuiMapCardEditor;
+ }
+}
+
+customElements.define("hui-map-card-editor", HuiMapCardEditor);
diff --git a/src/panels/lovelace/editor/config-elements/hui-markdown-card-editor.ts b/src/panels/lovelace/editor/config-elements/hui-markdown-card-editor.ts
new file mode 100644
index 0000000000..c4b002eff1
--- /dev/null
+++ b/src/panels/lovelace/editor/config-elements/hui-markdown-card-editor.ts
@@ -0,0 +1,99 @@
+import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element";
+import { TemplateResult } from "lit-html";
+import "@polymer/paper-input/paper-input";
+import "@polymer/paper-input/paper-textarea";
+
+import { struct } from "../../common/structs/struct";
+import { EntitiesEditorEvent, EditorTarget } from "../types";
+import { hassLocalizeLitMixin } from "../../../../mixins/lit-localize-mixin";
+import { HomeAssistant } from "../../../../types";
+import { LovelaceCardEditor } from "../../types";
+import { fireEvent } from "../../../../common/dom/fire_event";
+import { Config } from "../../cards/hui-glance-card";
+import { configElementStyle } from "./config-elements-style";
+
+const cardConfigStruct = struct({
+ type: "string",
+ title: "string?",
+ content: "string",
+});
+
+export class HuiMarkdownCardEditor extends hassLocalizeLitMixin(LitElement)
+ implements LovelaceCardEditor {
+ public hass?: HomeAssistant;
+ private _config?: Config;
+
+ public setConfig(config: Config): void {
+ config = cardConfigStruct(config);
+ this._config = config;
+ }
+
+ static get properties(): PropertyDeclarations {
+ return { hass: {}, _config: {} };
+ }
+
+ get _title(): string {
+ return this._config!.title || "";
+ }
+
+ get _content(): string {
+ return this._config!.content || "";
+ }
+
+ protected render(): TemplateResult {
+ if (!this.hass) {
+ return html``;
+ }
+
+ return html`
+ ${configElementStyle}
+
+ `;
+ }
+
+ private _valueChanged(ev: EntitiesEditorEvent): void {
+ if (!this._config || !this.hass) {
+ return;
+ }
+ const target = ev.target! as EditorTarget;
+
+ if (this[`_${target.configValue}`] === target.value) {
+ return;
+ }
+ if (target.configValue) {
+ if (target.value === "") {
+ delete this._config[target.configValue!];
+ } else {
+ this._config = {
+ ...this._config,
+ [target.configValue!]: target.value,
+ };
+ }
+ }
+ fireEvent(this, "config-changed", { config: this._config });
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ "hui-markdown-card-editor": HuiMarkdownCardEditor;
+ }
+}
+
+customElements.define("hui-markdown-card-editor", HuiMarkdownCardEditor);
diff --git a/src/panels/lovelace/editor/config-elements/hui-media-control-card-editor.ts b/src/panels/lovelace/editor/config-elements/hui-media-control-card-editor.ts
new file mode 100644
index 0000000000..b6ac24fe3f
--- /dev/null
+++ b/src/panels/lovelace/editor/config-elements/hui-media-control-card-editor.ts
@@ -0,0 +1,87 @@
+import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element";
+import { TemplateResult } from "lit-html";
+
+import { struct } from "../../common/structs/struct";
+import { EntitiesEditorEvent, EditorTarget } from "../types";
+import { hassLocalizeLitMixin } from "../../../../mixins/lit-localize-mixin";
+import { HomeAssistant } from "../../../../types";
+import { LovelaceCardEditor } from "../../types";
+import { fireEvent } from "../../../../common/dom/fire_event";
+import { Config } from "../../cards/hui-media-control-card";
+
+import "../../../../components/entity/ha-entity-picker";
+
+const cardConfigStruct = struct({
+ type: "string",
+ entity: "string?",
+});
+
+export class HuiMediaControlCardEditor extends hassLocalizeLitMixin(LitElement)
+ implements LovelaceCardEditor {
+ public hass?: HomeAssistant;
+ private _config?: Config;
+
+ public setConfig(config: Config): void {
+ config = cardConfigStruct(config);
+ this._config = config;
+ }
+
+ static get properties(): PropertyDeclarations {
+ return { hass: {}, _config: {} };
+ }
+
+ get _entity(): string {
+ return this._config!.entity || "";
+ }
+
+ protected render(): TemplateResult {
+ if (!this.hass) {
+ return html``;
+ }
+
+ return html`
+
+
+
+ `;
+ }
+
+ private _valueChanged(ev: EntitiesEditorEvent): void {
+ if (!this._config || !this.hass) {
+ return;
+ }
+ const target = ev.target! as EditorTarget;
+ if (this[`_${target.configValue}`] === target.value) {
+ return;
+ }
+ if (target.configValue) {
+ if (target.value === "") {
+ delete this._config[target.configValue!];
+ } else {
+ this._config = {
+ ...this._config,
+ [target.configValue!]: target.value,
+ };
+ }
+ }
+ fireEvent(this, "config-changed", { config: this._config });
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ "hui-media-control-card-editor": HuiMediaControlCardEditor;
+ }
+}
+
+customElements.define(
+ "hui-media-control-card-editor",
+ HuiMediaControlCardEditor
+);
diff --git a/src/panels/lovelace/editor/config-elements/hui-picture-card-editor.ts b/src/panels/lovelace/editor/config-elements/hui-picture-card-editor.ts
new file mode 100644
index 0000000000..52bba01b3c
--- /dev/null
+++ b/src/panels/lovelace/editor/config-elements/hui-picture-card-editor.ts
@@ -0,0 +1,124 @@
+import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element";
+import { TemplateResult } from "lit-html";
+import "@polymer/paper-input/paper-input";
+
+import { struct } from "../../common/structs/struct";
+import {
+ EntitiesEditorEvent,
+ EditorTarget,
+ actionConfigStruct,
+} from "../types";
+import { hassLocalizeLitMixin } from "../../../../mixins/lit-localize-mixin";
+import { HomeAssistant } from "../../../../types";
+import { LovelaceCardEditor } from "../../types";
+import { fireEvent } from "../../../../common/dom/fire_event";
+import { Config } from "../../cards/hui-picture-card";
+import { configElementStyle } from "./config-elements-style";
+import { ActionConfig } from "../../../../data/lovelace";
+
+import "../../components/hui-action-editor";
+
+const cardConfigStruct = struct({
+ type: "string",
+ image: "string?",
+ tap_action: actionConfigStruct,
+ hold_action: actionConfigStruct,
+});
+
+export class HuiPictureCardEditor extends hassLocalizeLitMixin(LitElement)
+ implements LovelaceCardEditor {
+ public hass?: HomeAssistant;
+ private _config?: Config;
+
+ public setConfig(config: Config): void {
+ config = cardConfigStruct(config);
+ this._config = config;
+ }
+
+ static get properties(): PropertyDeclarations {
+ return { hass: {}, _config: {} };
+ }
+
+ get _image(): string {
+ return this._config!.image || "";
+ }
+
+ get _tap_action(): ActionConfig {
+ return this._config!.tap_action || { action: "none" };
+ }
+
+ get _hold_action(): ActionConfig {
+ return this._config!.hold_action || { action: "none" };
+ }
+
+ protected render(): TemplateResult {
+ if (!this.hass) {
+ return html``;
+ }
+
+ const actions = ["navigate", "call-service", "none"];
+
+ return html`
+ ${configElementStyle}
+
+ `;
+ }
+
+ private _valueChanged(ev: EntitiesEditorEvent): void {
+ if (!this._config || !this.hass) {
+ return;
+ }
+ const target = ev.target! as EditorTarget;
+
+ if (
+ this[`_${target.configValue}`] === target.value ||
+ this[`_${target.configValue}`] === target.config
+ ) {
+ return;
+ }
+ if (target.configValue) {
+ if (target.value === "") {
+ delete this._config[target.configValue!];
+ } else {
+ this._config = {
+ ...this._config,
+ [target.configValue!]: target.value ? target.value : target.config,
+ };
+ }
+ }
+ fireEvent(this, "config-changed", { config: this._config });
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ "hui-picture-card-editor": HuiPictureCardEditor;
+ }
+}
+
+customElements.define("hui-picture-card-editor", HuiPictureCardEditor);
diff --git a/src/panels/lovelace/editor/config-elements/hui-plant-status-card-editor.ts b/src/panels/lovelace/editor/config-elements/hui-plant-status-card-editor.ts
new file mode 100644
index 0000000000..d321f52b76
--- /dev/null
+++ b/src/panels/lovelace/editor/config-elements/hui-plant-status-card-editor.ts
@@ -0,0 +1,101 @@
+import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element";
+import { TemplateResult } from "lit-html";
+import "@polymer/paper-input/paper-input";
+
+import { struct } from "../../common/structs/struct";
+import { EntitiesEditorEvent, EditorTarget } from "../types";
+import { hassLocalizeLitMixin } from "../../../../mixins/lit-localize-mixin";
+import { HomeAssistant } from "../../../../types";
+import { LovelaceCardEditor } from "../../types";
+import { fireEvent } from "../../../../common/dom/fire_event";
+import { Config } from "../../cards/hui-alarm-panel-card";
+import { configElementStyle } from "./config-elements-style";
+
+import "../../../../components/entity/ha-entity-picker";
+import "../../../../components/ha-icon";
+
+const cardConfigStruct = struct({
+ type: "string",
+ entity: "string",
+ name: "string?",
+});
+
+export class HuiPlantStatusCardEditor extends hassLocalizeLitMixin(LitElement)
+ implements LovelaceCardEditor {
+ public hass?: HomeAssistant;
+ private _config?: Config;
+
+ public setConfig(config: Config): void {
+ config = cardConfigStruct(config);
+ this._config = config;
+ }
+
+ static get properties(): PropertyDeclarations {
+ return { hass: {}, _config: {} };
+ }
+
+ get _entity(): string {
+ return this._config!.entity || "";
+ }
+
+ get _name(): string {
+ return this._config!.name || "";
+ }
+
+ protected render(): TemplateResult {
+ if (!this.hass) {
+ return html``;
+ }
+
+ return html`
+ ${configElementStyle}
+
+ `;
+ }
+
+ private _valueChanged(ev: EntitiesEditorEvent): void {
+ if (!this._config || !this.hass) {
+ return;
+ }
+ const target = ev.target! as EditorTarget;
+ if (this[`_${target.configValue}`] === target.value) {
+ return;
+ }
+ if (target.configValue) {
+ if (target.value === "") {
+ delete this._config[target.configValue!];
+ } else {
+ this._config = {
+ ...this._config,
+ [target.configValue!]: target.value,
+ };
+ }
+ }
+ fireEvent(this, "config-changed", { config: this._config });
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ "hui-plant-status-card-editor": HuiPlantStatusCardEditor;
+ }
+}
+
+customElements.define("hui-plant-status-card-editor", HuiPlantStatusCardEditor);
diff --git a/src/panels/lovelace/editor/config-elements/hui-sensor-card-editor.ts b/src/panels/lovelace/editor/config-elements/hui-sensor-card-editor.ts
new file mode 100644
index 0000000000..dcf108c3ed
--- /dev/null
+++ b/src/panels/lovelace/editor/config-elements/hui-sensor-card-editor.ts
@@ -0,0 +1,197 @@
+import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element";
+import { TemplateResult } from "lit-html";
+import "@polymer/paper-input/paper-input";
+import "@polymer/paper-dropdown-menu/paper-dropdown-menu";
+import "@polymer/paper-item/paper-item";
+import "@polymer/paper-listbox/paper-listbox";
+
+import { struct } from "../../common/structs/struct";
+import { EntitiesEditorEvent, EditorTarget } from "../types";
+import { hassLocalizeLitMixin } from "../../../../mixins/lit-localize-mixin";
+import { HomeAssistant } from "../../../../types";
+import { LovelaceCardEditor } from "../../types";
+import { fireEvent } from "../../../../common/dom/fire_event";
+import { Config } from "../../cards/hui-sensor-card";
+import { configElementStyle } from "./config-elements-style";
+
+import "../../components/hui-theme-select-editor";
+import "../../../../components/entity/ha-entity-picker";
+
+const cardConfigStruct = struct({
+ type: "string",
+ entity: "string?",
+ name: "string?",
+ icon: "string?",
+ graph: "string?",
+ unit: "string?",
+ detail: "number?",
+ theme: "string?",
+ hours_to_show: "number?",
+});
+
+export class HuiSensorCardEditor extends hassLocalizeLitMixin(LitElement)
+ implements LovelaceCardEditor {
+ public hass?: HomeAssistant;
+ private _config?: Config;
+
+ public setConfig(config: Config): void {
+ config = cardConfigStruct(config);
+ this._config = config;
+ }
+
+ static get properties(): PropertyDeclarations {
+ return { hass: {}, _config: {} };
+ }
+
+ get _entity(): string {
+ return this._config!.entity || "";
+ }
+
+ get _name(): string {
+ return this._config!.name || "";
+ }
+
+ get _icon(): string {
+ return this._config!.icon || "";
+ }
+
+ get _graph(): string {
+ return this._config!.graph || "none";
+ }
+
+ get _unit(): string {
+ return this._config!.unit || "";
+ }
+
+ get _detail(): number | string {
+ return this._config!.number || "1";
+ }
+
+ get _theme(): string {
+ return this._config!.theme || "default";
+ }
+
+ get _hours_to_show(): number | string {
+ return this._config!.hours_to_show || "24";
+ }
+
+ protected render(): TemplateResult {
+ if (!this.hass) {
+ return html``;
+ }
+
+ const graphs = ["line", "none"];
+
+ return html`
+ ${configElementStyle}
+
+
+
+
+
+
+ ${
+ graphs.map((graph) => {
+ return html`
+ ${graph}
+ `;
+ })
+ }
+
+
+
+
+
+
+ `;
+ }
+
+ private _valueChanged(ev: EntitiesEditorEvent): void {
+ if (!this._config || !this.hass) {
+ return;
+ }
+ const target = ev.target! as EditorTarget;
+
+ if (this[`_${target.configValue}`] === target.value) {
+ return;
+ }
+ if (target.configValue) {
+ if (
+ target.value === "" ||
+ (target.type === "number" && isNaN(Number(target.value)))
+ ) {
+ delete this._config[target.configValue!];
+ } else {
+ let value: any = target.value;
+ if (target.type === "number") {
+ value = Number(value);
+ }
+ this._config = { ...this._config, [target.configValue!]: value };
+ }
+ }
+ fireEvent(this, "config-changed", { config: this._config });
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ "hui-sensor-card-editor": HuiSensorCardEditor;
+ }
+}
+
+customElements.define("hui-sensor-card-editor", HuiSensorCardEditor);
diff --git a/src/panels/lovelace/editor/config-elements/hui-shopping-list-editor.ts b/src/panels/lovelace/editor/config-elements/hui-shopping-list-editor.ts
new file mode 100644
index 0000000000..6bd987db6a
--- /dev/null
+++ b/src/panels/lovelace/editor/config-elements/hui-shopping-list-editor.ts
@@ -0,0 +1,82 @@
+import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element";
+import { TemplateResult } from "lit-html";
+import "@polymer/paper-input/paper-input";
+
+import { struct } from "../../common/structs/struct";
+import { EntitiesEditorEvent, EditorTarget } from "../types";
+import { hassLocalizeLitMixin } from "../../../../mixins/lit-localize-mixin";
+import { HomeAssistant } from "../../../../types";
+import { LovelaceCardEditor } from "../../types";
+import { fireEvent } from "../../../../common/dom/fire_event";
+import { Config } from "../../cards/hui-shopping-list-card";
+
+const cardConfigStruct = struct({
+ type: "string",
+ title: "string?",
+});
+
+export class HuiShoppingListEditor extends hassLocalizeLitMixin(LitElement)
+ implements LovelaceCardEditor {
+ public hass?: HomeAssistant;
+ private _config?: Config;
+
+ public setConfig(config: Config): void {
+ config = cardConfigStruct(config);
+ this._config = config;
+ }
+
+ static get properties(): PropertyDeclarations {
+ return { hass: {}, _config: {} };
+ }
+
+ get _title(): string {
+ return this._config!.title || "";
+ }
+
+ protected render(): TemplateResult {
+ if (!this.hass) {
+ return html``;
+ }
+
+ return html`
+
+ `;
+ }
+
+ private _valueChanged(ev: EntitiesEditorEvent): void {
+ if (!this._config || !this.hass) {
+ return;
+ }
+ const target = ev.target! as EditorTarget;
+
+ if (this[`_${target.configValue}`] === target.value) {
+ return;
+ }
+ if (target.configValue) {
+ if (target.value === "") {
+ delete this._config[target.configValue!];
+ } else {
+ this._config = {
+ ...this._config,
+ [target.configValue!]: target.value,
+ };
+ }
+ }
+ fireEvent(this, "config-changed", { config: this._config });
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ "hui-shopping-list-card-editor": HuiShoppingListEditor;
+ }
+}
+
+customElements.define("hui-shopping-list-card-editor", HuiShoppingListEditor);
diff --git a/src/panels/lovelace/editor/config-elements/hui-thermostat-card-editor.ts b/src/panels/lovelace/editor/config-elements/hui-thermostat-card-editor.ts
new file mode 100644
index 0000000000..e1db48583e
--- /dev/null
+++ b/src/panels/lovelace/editor/config-elements/hui-thermostat-card-editor.ts
@@ -0,0 +1,110 @@
+import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element";
+import { TemplateResult } from "lit-html";
+import "@polymer/paper-input/paper-input";
+
+import { struct } from "../../common/structs/struct";
+import { EntitiesEditorEvent, EditorTarget } from "../types";
+import { hassLocalizeLitMixin } from "../../../../mixins/lit-localize-mixin";
+import { HomeAssistant } from "../../../../types";
+import { LovelaceCardEditor } from "../../types";
+import { fireEvent } from "../../../../common/dom/fire_event";
+import { Config } from "../../cards/hui-thermostat-card";
+import { configElementStyle } from "./config-elements-style";
+
+import "../../components/hui-theme-select-editor";
+import "../../../../components/entity/ha-entity-picker";
+
+const cardConfigStruct = struct({
+ type: "string",
+ entity: "string",
+ name: "string?",
+ theme: "string?",
+});
+
+export class HuiThermostatCardEditor extends hassLocalizeLitMixin(LitElement)
+ implements LovelaceCardEditor {
+ public hass?: HomeAssistant;
+ private _config?: Config;
+
+ public setConfig(config: Config): void {
+ config = cardConfigStruct(config);
+ this._config = config;
+ }
+
+ static get properties(): PropertyDeclarations {
+ return { hass: {}, _config: {} };
+ }
+
+ get _entity(): string {
+ return this._config!.entity || "";
+ }
+
+ get _name(): string {
+ return this._config!.name || "";
+ }
+
+ get _theme(): string {
+ return this._config!.theme || "default";
+ }
+
+ protected render(): TemplateResult {
+ if (!this.hass) {
+ return html``;
+ }
+
+ return html`
+ ${configElementStyle}
+
+ `;
+ }
+
+ private _valueChanged(ev: EntitiesEditorEvent): void {
+ if (!this._config || !this.hass) {
+ return;
+ }
+ const target = ev.target! as EditorTarget;
+
+ if (this[`_${target.configValue}`] === target.value) {
+ return;
+ }
+ if (target.configValue) {
+ if (target.value === "") {
+ delete this._config[target.configValue!];
+ } else {
+ this._config = { ...this._config, [target.configValue!]: target.value };
+ }
+ }
+ fireEvent(this, "config-changed", { config: this._config });
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ "hui-thermostat-card-editor": HuiThermostatCardEditor;
+ }
+}
+
+customElements.define("hui-thermostat-card-editor", HuiThermostatCardEditor);
diff --git a/src/panels/lovelace/editor/config-elements/hui-weather-forecast-card-editor.ts b/src/panels/lovelace/editor/config-elements/hui-weather-forecast-card-editor.ts
new file mode 100644
index 0000000000..8854c411a8
--- /dev/null
+++ b/src/panels/lovelace/editor/config-elements/hui-weather-forecast-card-editor.ts
@@ -0,0 +1,103 @@
+import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element";
+import { TemplateResult } from "lit-html";
+
+import { struct } from "../../common/structs/struct";
+import { EntitiesEditorEvent, EditorTarget } from "../types";
+import { hassLocalizeLitMixin } from "../../../../mixins/lit-localize-mixin";
+import { HomeAssistant } from "../../../../types";
+import { LovelaceCardEditor } from "../../types";
+import { fireEvent } from "../../../../common/dom/fire_event";
+import { Config } from "../../cards/hui-weather-forecast-card";
+import { configElementStyle } from "./config-elements-style";
+
+import "../../../../components/entity/ha-entity-picker";
+
+const cardConfigStruct = struct({
+ type: "string",
+ entity: "string?",
+ name: "string?",
+});
+
+export class HuiWeatherForecastCardEditor
+ extends hassLocalizeLitMixin(LitElement)
+ implements LovelaceCardEditor {
+ public hass?: HomeAssistant;
+ private _config?: Config;
+
+ public setConfig(config: Config): void {
+ config = cardConfigStruct(config);
+ this._config = config;
+ }
+
+ static get properties(): PropertyDeclarations {
+ return { hass: {}, _config: {} };
+ }
+
+ get _entity(): string {
+ return this._config!.entity || "";
+ }
+
+ get _name(): string {
+ return this._config!.name || "";
+ }
+
+ protected render(): TemplateResult {
+ if (!this.hass) {
+ return html``;
+ }
+
+ return html`
+ ${configElementStyle}
+
+ `;
+ }
+
+ private _valueChanged(ev: EntitiesEditorEvent): void {
+ if (!this._config || !this.hass) {
+ return;
+ }
+ const target = ev.target! as EditorTarget;
+ if (this[`_${target.configValue}`] === target.value) {
+ return;
+ }
+ if (target.configValue) {
+ if (target.value === "") {
+ delete this._config[target.configValue!];
+ } else {
+ this._config = {
+ ...this._config,
+ [target.configValue!]: target.value,
+ };
+ }
+ }
+ fireEvent(this, "config-changed", { config: this._config });
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ "hui-weather-forecast-card-editor": HuiWeatherForecastCardEditor;
+ }
+}
+
+customElements.define(
+ "hui-weather-forecast-card-editor",
+ HuiWeatherForecastCardEditor
+);
diff --git a/src/panels/lovelace/editor/config-util.ts b/src/panels/lovelace/editor/config-util.ts
index 2c87b57737..d1f834a2c8 100644
--- a/src/panels/lovelace/editor/config-util.ts
+++ b/src/panels/lovelace/editor/config-util.ts
@@ -121,6 +121,44 @@ export const swapCard = (
};
};
+export const moveCard = (
+ config: LovelaceConfig,
+ fromPath: [number, number],
+ toPath: [number]
+): LovelaceConfig => {
+ if (fromPath[0] === toPath[0]) {
+ throw new Error("You can not move a card to the view it is in.");
+ }
+ const fromView = config.views[fromPath[0]];
+ const card = fromView.cards![fromPath[1]];
+
+ const newView1 = {
+ ...fromView,
+ cards: (fromView.cards || []).filter(
+ (_origConf, ind) => ind !== fromPath[1]
+ ),
+ };
+
+ const toView = config.views[toPath[0]];
+ const cards = toView.cards ? [...toView.cards, card] : [card];
+
+ const newView2 = {
+ ...toView,
+ cards,
+ };
+
+ return {
+ ...config,
+ views: config.views.map((origView, index) =>
+ index === toPath[0]
+ ? newView2
+ : index === fromPath[0]
+ ? newView1
+ : origView
+ ),
+ };
+};
+
export const addView = (
config: LovelaceConfig,
viewConfig: LovelaceViewConfig
diff --git a/src/panels/lovelace/editor/hui-dialog-save-config.ts b/src/panels/lovelace/editor/hui-dialog-save-config.ts
index 7e00454cfa..295d9ae87c 100644
--- a/src/panels/lovelace/editor/hui-dialog-save-config.ts
+++ b/src/panels/lovelace/editor/hui-dialog-save-config.ts
@@ -14,7 +14,7 @@ import { hassLocalizeLitMixin } from "../../../mixins/lit-localize-mixin";
import { SaveDialogParams } from "./show-save-config-dialog";
export class HuiSaveConfig extends hassLocalizeLitMixin(LitElement) {
- protected hass?: HomeAssistant;
+ public hass?: HomeAssistant;
private _params?: SaveDialogParams;
private _saving: boolean;
diff --git a/src/panels/lovelace/editor/lovelace-editor/hui-dialog-edit-lovelace.ts b/src/panels/lovelace/editor/lovelace-editor/hui-dialog-edit-lovelace.ts
new file mode 100644
index 0000000000..2cb86b013b
--- /dev/null
+++ b/src/panels/lovelace/editor/lovelace-editor/hui-dialog-edit-lovelace.ts
@@ -0,0 +1,152 @@
+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 "@polymer/paper-dialog-scrollable/paper-dialog-scrollable";
+import "./hui-lovelace-editor";
+import { HomeAssistant } from "../../../../types";
+import { LovelaceConfig } from "../../../../data/lovelace";
+import { hassLocalizeLitMixin } from "../../../../mixins/lit-localize-mixin";
+import { Lovelace } from "../../types";
+
+export class HuiDialogEditLovelace extends hassLocalizeLitMixin(LitElement) {
+ public hass?: HomeAssistant;
+ private _lovelace?: Lovelace;
+ private _config?: LovelaceConfig;
+ private _saving: boolean;
+
+ static get properties(): PropertyDeclarations {
+ return {
+ hass: {},
+ _lovelace: {},
+ };
+ }
+
+ protected constructor() {
+ super();
+ this._saving = false;
+ }
+
+ public async showDialog(lovelace: Lovelace): Promise
{
+ this._lovelace = lovelace;
+ if (this._dialog == null) {
+ await this.updateComplete;
+ }
+
+ const { views, ...lovelaceConfig } = this._lovelace!.config;
+ this._config = lovelaceConfig as LovelaceConfig;
+
+ this._dialog.open();
+ }
+
+ private get _dialog(): PaperDialogElement {
+ return this.shadowRoot!.querySelector("paper-dialog")!;
+ }
+
+ protected render(): TemplateResult {
+ return html`
+ ${this.renderStyle()}
+
+ Edit Lovelace
+
+
+
+
+ `;
+ }
+
+ private _closeDialog(): void {
+ this._config = undefined;
+ this._dialog.close();
+ }
+
+ private async _save(): Promise {
+ if (!this._config) {
+ return;
+ }
+ if (!this._isConfigChanged()) {
+ this._closeDialog();
+ return;
+ }
+
+ this._saving = true;
+ const lovelace = this._lovelace!;
+
+ const config: LovelaceConfig = {
+ ...lovelace.config,
+ ...this._config,
+ };
+
+ try {
+ await lovelace.saveConfig(config);
+ this._closeDialog();
+ } catch (err) {
+ alert(`Saving failed: ${err.message}`);
+ } finally {
+ this._saving = false;
+ }
+ }
+
+ private _ConfigChanged(ev: CustomEvent): void {
+ if (ev.detail && ev.detail.config) {
+ this._config = ev.detail.config;
+ }
+ }
+
+ private _isConfigChanged(): boolean {
+ const { views, ...lovelaceConfig } = this._lovelace!.config;
+ return JSON.stringify(this._config) !== JSON.stringify(lovelaceConfig);
+ }
+
+ private renderStyle(): TemplateResult {
+ return html`
+
+ `;
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ "hui-dialog-edit-lovelace": HuiDialogEditLovelace;
+ }
+}
+
+customElements.define("hui-dialog-edit-lovelace", HuiDialogEditLovelace);
diff --git a/src/panels/lovelace/editor/lovelace-editor/hui-lovelace-editor.ts b/src/panels/lovelace/editor/lovelace-editor/hui-lovelace-editor.ts
new file mode 100644
index 0000000000..68761be409
--- /dev/null
+++ b/src/panels/lovelace/editor/lovelace-editor/hui-lovelace-editor.ts
@@ -0,0 +1,80 @@
+import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element";
+import { TemplateResult } from "lit-html";
+import "@polymer/paper-input/paper-input";
+
+import { EditorTarget } from "../types";
+import { hassLocalizeLitMixin } from "../../../../mixins/lit-localize-mixin";
+import { HomeAssistant } from "../../../../types";
+import { fireEvent } from "../../../../common/dom/fire_event";
+import { configElementStyle } from "../config-elements/config-elements-style";
+
+import { LovelaceConfig } from "../../../../data/lovelace";
+
+declare global {
+ interface HASSDomEvents {
+ "lovelace-config-changed": {
+ config: LovelaceConfig;
+ };
+ }
+}
+
+export class HuiLovelaceEditor extends hassLocalizeLitMixin(LitElement) {
+ static get properties(): PropertyDeclarations {
+ return { hass: {}, config: {} };
+ }
+
+ public hass?: HomeAssistant;
+ public config?: LovelaceConfig;
+
+ get _title(): string {
+ if (!this.config) {
+ return "";
+ }
+ return this.config.title || "";
+ }
+
+ protected render(): TemplateResult {
+ return html`
+ ${configElementStyle}
+
+ `;
+ }
+
+ private _valueChanged(ev: Event): void {
+ if (!this.config) {
+ return;
+ }
+
+ const target = ev.currentTarget! as EditorTarget;
+
+ if (this[`_${target.configValue}`] === target.value) {
+ return;
+ }
+
+ let newConfig;
+
+ if (target.configValue) {
+ newConfig = {
+ ...this.config,
+ [target.configValue]: target.value,
+ };
+ }
+
+ fireEvent(this, "lovelace-config-changed", { config: newConfig });
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ "hui-lovelace-editor": HuiLovelaceEditor;
+ }
+}
+
+customElements.define("hui-lovelace-editor", HuiLovelaceEditor);
diff --git a/src/panels/lovelace/editor/lovelace-editor/show-edit-lovelace-dialog.ts b/src/panels/lovelace/editor/lovelace-editor/show-edit-lovelace-dialog.ts
new file mode 100644
index 0000000000..bc042dfa15
--- /dev/null
+++ b/src/panels/lovelace/editor/lovelace-editor/show-edit-lovelace-dialog.ts
@@ -0,0 +1,32 @@
+import { fireEvent } from "../../../../common/dom/fire_event";
+import { Lovelace } from "../../types";
+
+declare global {
+ // for fire event
+ interface HASSDomEvents {
+ "show-edit-lovelace": Lovelace;
+ }
+}
+
+let registeredDialog = false;
+const dialogShowEvent = "show-edit-lovelace";
+const dialogTag = "hui-dialog-edit-lovelace";
+
+const registerEditLovelaceDialog = (element: HTMLElement) =>
+ fireEvent(element, "register-dialog", {
+ dialogShowEvent,
+ dialogTag,
+ dialogImport: () =>
+ import(/* webpackChunkName: "hui-dialog-edit-lovelace" */ "./hui-dialog-edit-lovelace"),
+ });
+
+export const showEditLovelaceDialog = (
+ element: HTMLElement,
+ lovelace: Lovelace
+) => {
+ if (!registeredDialog) {
+ registeredDialog = true;
+ registerEditLovelaceDialog(element);
+ }
+ fireEvent(element, dialogShowEvent, lovelace);
+};
diff --git a/src/panels/lovelace/editor/show-save-config-dialog.ts b/src/panels/lovelace/editor/show-save-config-dialog.ts
index f5207c8241..8025d5a7b7 100644
--- a/src/panels/lovelace/editor/show-save-config-dialog.ts
+++ b/src/panels/lovelace/editor/show-save-config-dialog.ts
@@ -26,7 +26,8 @@ export const showSaveDialog = (
fireEvent(element, "register-dialog", {
dialogShowEvent,
dialogTag,
- dialogImport: () => import("./hui-dialog-save-config"),
+ dialogImport: () =>
+ import(/* webpackChunkName: "hui-dialog-save-config" */ "./hui-dialog-save-config"),
});
}
fireEvent(element, dialogShowEvent, saveDialogParams);
diff --git a/src/panels/lovelace/editor/types.ts b/src/panels/lovelace/editor/types.ts
index b91e381632..aea4a4e65c 100644
--- a/src/panels/lovelace/editor/types.ts
+++ b/src/panels/lovelace/editor/types.ts
@@ -1,6 +1,11 @@
-import { LovelaceCardConfig, LovelaceViewConfig } from "../../../data/lovelace";
+import {
+ LovelaceCardConfig,
+ LovelaceViewConfig,
+ ActionConfig,
+} from "../../../data/lovelace";
import { EntityConfig } from "../entity-rows/types";
import { InputType } from "zlib";
+import { struct } from "../common/structs/struct";
export interface YamlChangedEvent extends Event {
detail: {
@@ -37,8 +42,16 @@ export interface EditorTarget extends EventTarget {
checked?: boolean;
configValue?: string;
type?: InputType;
+ config: ActionConfig;
}
export interface CardPickTarget extends EventTarget {
type: string;
}
+
+export const actionConfigStruct = struct({
+ action: "string",
+ navigation_path: "string?",
+ service: "string?",
+ service_data: "object?",
+});
diff --git a/src/panels/lovelace/editor/view-editor/hui-edit-view.ts b/src/panels/lovelace/editor/view-editor/hui-edit-view.ts
index 733ec46a39..8b27068812 100644
--- a/src/panels/lovelace/editor/view-editor/hui-edit-view.ts
+++ b/src/panels/lovelace/editor/view-editor/hui-edit-view.ts
@@ -30,7 +30,7 @@ import { deleteView, addView, replaceView } from "../config-util";
export class HuiEditView extends hassLocalizeLitMixin(LitElement) {
public lovelace?: Lovelace;
public viewIndex?: number;
- protected hass?: HomeAssistant;
+ public hass?: HomeAssistant;
private _config?: LovelaceViewConfig;
private _badges?: EntityConfig[];
private _cards?: LovelaceCardConfig[];
diff --git a/src/panels/lovelace/editor/view-editor/hui-view-editor.ts b/src/panels/lovelace/editor/view-editor/hui-view-editor.ts
index f9f95d7ad2..e85cbff0d1 100644
--- a/src/panels/lovelace/editor/view-editor/hui-view-editor.ts
+++ b/src/panels/lovelace/editor/view-editor/hui-view-editor.ts
@@ -69,19 +69,19 @@ export class HuiViewEditor extends hassLocalizeLitMixin(LitElement) {
diff --git a/src/panels/lovelace/editor/view-editor/show-edit-view-dialog.ts b/src/panels/lovelace/editor/view-editor/show-edit-view-dialog.ts
index a912d89662..a3f7748de7 100644
--- a/src/panels/lovelace/editor/view-editor/show-edit-view-dialog.ts
+++ b/src/panels/lovelace/editor/view-editor/show-edit-view-dialog.ts
@@ -26,7 +26,8 @@ const registerEditViewDialog = (element: HTMLElement) =>
fireEvent(element, "register-dialog", {
dialogShowEvent,
dialogTag,
- dialogImport: () => import("./hui-dialog-edit-view"),
+ dialogImport: () =>
+ import(/* webpackChunkName: "hui-dialog-edit-view" */ "./hui-dialog-edit-view"),
});
export const showEditViewDialog = (
diff --git a/src/panels/lovelace/entity-rows/hui-climate-entity-row.ts b/src/panels/lovelace/entity-rows/hui-climate-entity-row.ts
index 35d6720205..3186c9c62b 100644
--- a/src/panels/lovelace/entity-rows/hui-climate-entity-row.ts
+++ b/src/panels/lovelace/entity-rows/hui-climate-entity-row.ts
@@ -31,12 +31,22 @@ class HuiClimateEntityRow extends LitElement implements EntityRow {
return html``;
}
+ const stateObj = this.hass.states[this._config.entity];
+
+ if (!stateObj) {
+ return html`
+
+ `;
+ }
+
return html`
${this.renderStyle()}
`;
diff --git a/src/panels/lovelace/entity-rows/types.ts b/src/panels/lovelace/entity-rows/types.ts
index 88e983bc69..c73cf9ef50 100644
--- a/src/panels/lovelace/entity-rows/types.ts
+++ b/src/panels/lovelace/entity-rows/types.ts
@@ -29,7 +29,7 @@ export type EntityRowConfig =
| WeblinkConfig
| CallServiceConfig;
-export interface EntityRow {
+export interface EntityRow extends HTMLElement {
hass?: HomeAssistant;
setConfig(config: EntityRowConfig);
}
diff --git a/src/panels/lovelace/hui-editor.ts b/src/panels/lovelace/hui-editor.ts
index 202de5b0bb..f2ac450904 100644
--- a/src/panels/lovelace/hui-editor.ts
+++ b/src/panels/lovelace/hui-editor.ts
@@ -1,4 +1,5 @@
import { LitElement, html } from "@polymer/lit-element";
+import { classMap } from "lit-html/directives/classMap";
import { TemplateResult } from "lit-html";
import yaml from "js-yaml";
@@ -7,20 +8,37 @@ import "@polymer/app-layout/app-header/app-header";
import "@polymer/app-layout/app-toolbar/app-toolbar";
import "@polymer/paper-button/paper-button";
import "@polymer/paper-icon-button/paper-icon-button";
+import "@polymer/paper-spinner/paper-spinner";
+import { struct } from "./common/structs/struct";
import { Lovelace } from "./types";
import { hassLocalizeLitMixin } from "../../mixins/lit-localize-mixin";
+import "../../components/ha-icon";
+
const TAB_INSERT = " ";
+const lovelaceStruct = struct.partial({
+ title: "string?",
+ views: ["object"],
+});
+
class LovelaceFullConfigEditor extends hassLocalizeLitMixin(LitElement) {
public lovelace?: Lovelace;
public closeEditor?: () => void;
private _haStyle?: DocumentFragment;
+ private _saving?: boolean;
+ private _changed?: boolean;
+ private _hashAdded?: boolean;
+ private _hash?: boolean;
static get properties() {
return {
lovelace: {},
+ _saving: {},
+ _changed: {},
+ _hashAdded: {},
+ _hash: {},
};
}
@@ -32,10 +50,26 @@ class LovelaceFullConfigEditor extends hassLocalizeLitMixin(LitElement) {
Edit Config
+ ${
+ this._hash
+ ? html`
+
+ `
+ : ""
+ }
Save
+
@@ -44,6 +78,7 @@ class LovelaceFullConfigEditor extends hassLocalizeLitMixin(LitElement) {
autocorrect="off"
autocapitalize="off"
spellcheck="false"
+ @input="${this._yamlChanged}"
>
@@ -54,6 +89,11 @@ class LovelaceFullConfigEditor extends hassLocalizeLitMixin(LitElement) {
const textArea = this.textArea;
textArea.value = yaml.safeDump(this.lovelace!.config);
textArea.addEventListener("keydown", (e) => {
+ if (e.keyCode === 51) {
+ this._hashAdded = true;
+ return;
+ }
+
if (e.keyCode !== 9) {
return;
}
@@ -90,10 +130,17 @@ class LovelaceFullConfigEditor extends hassLocalizeLitMixin(LitElement) {
app-header-layout {
height: 100vh;
}
-
paper-button {
font-size: 16px;
}
+ app-toolbar {
+ background-color: var(--dark-background-color, #455a64);
+ color: var(--dark-text-color);
+ }
+
+ .comments {
+ font-size: 16px;
+ }
.content {
height: calc(100vh - 68px);
@@ -110,20 +157,80 @@ class LovelaceFullConfigEditor extends hassLocalizeLitMixin(LitElement) {
font-family: "Courier New", Courier, monospace;
padding: 8px;
}
+
+ .save-button {
+ opacity: 0;
+ margin-left: -16px;
+ margin-top: -4px;
+ transition: opacity 1.5s;
+ }
+
+ .saved {
+ opacity: 1;
+ }
`;
}
- private _handleSave() {
+ private _closeEditor() {
+ if (this._changed) {
+ if (
+ !confirm("You have unsafed changes, are you sure you want to exit?")
+ ) {
+ return;
+ }
+ }
+ window.onbeforeunload = null;
+ this.closeEditor!();
+ }
+
+ private async _handleSave() {
+ this._saving = true;
+
+ if (this._hashAdded) {
+ if (
+ !confirm(
+ "Your config might contain comments, these will not be saved. Do you want to continue?"
+ )
+ ) {
+ return;
+ }
+ }
+
let value;
try {
value = yaml.safeLoad(this.textArea.value);
} catch (err) {
alert(`Unable to parse YAML: ${err}`);
+ this._saving = false;
return;
}
+ try {
+ value = lovelaceStruct(value);
+ } catch (err) {
+ alert(`Your config is not valid: ${err}`);
+ return;
+ }
+ try {
+ await this.lovelace!.saveConfig(value);
+ } catch (err) {
+ alert(`Unable to save YAML: ${err}`);
+ }
+ window.onbeforeunload = null;
+ this._saving = false;
+ this._changed = false;
+ this._hashAdded = false;
+ }
- this.lovelace!.saveConfig(value);
+ private _yamlChanged() {
+ this._hash = this._hashAdded || this.textArea.value.includes("#");
+ if (this._changed) {
+ return;
+ }
+ window.onbeforeunload = () => {
+ return true;
+ };
+ this._changed = true;
}
private get textArea(): HTMLTextAreaElement {
@@ -131,4 +238,10 @@ class LovelaceFullConfigEditor extends hassLocalizeLitMixin(LitElement) {
}
}
+declare global {
+ interface HTMLElementTagNameMap {
+ "hui-editor": LovelaceFullConfigEditor;
+ }
+}
+
customElements.define("hui-editor", LovelaceFullConfigEditor);
diff --git a/src/panels/lovelace/hui-root.js b/src/panels/lovelace/hui-root.js
deleted file mode 100644
index 415855e555..0000000000
--- a/src/panels/lovelace/hui-root.js
+++ /dev/null
@@ -1,454 +0,0 @@
-import "@polymer/app-layout/app-header-layout/app-header-layout";
-import "@polymer/app-layout/app-header/app-header";
-import "@polymer/app-layout/app-scroll-effects/effects/waterfall";
-import "@polymer/app-layout/app-toolbar/app-toolbar";
-import "@polymer/app-route/app-route";
-import "@polymer/paper-icon-button/paper-icon-button";
-import "@polymer/paper-button/paper-button";
-import "@polymer/paper-item/paper-item";
-import "@polymer/paper-listbox/paper-listbox";
-import "@polymer/paper-menu-button/paper-menu-button";
-import "@polymer/paper-tabs/paper-tab";
-import "@polymer/paper-tabs/paper-tabs";
-
-import { html } from "@polymer/polymer/lib/utils/html-tag";
-import { PolymerElement } from "@polymer/polymer/polymer-element";
-
-import scrollToTarget from "../../common/dom/scroll-to-target";
-
-import EventsMixin from "../../mixins/events-mixin";
-import localizeMixin from "../../mixins/localize-mixin";
-import NavigateMixin from "../../mixins/navigate-mixin";
-
-import "../../layouts/ha-app-layout";
-import "../../components/ha-start-voice-button";
-import "../../components/ha-icon";
-import { loadModule, loadCSS, loadJS } from "../../common/dom/load_resource";
-import { subscribeNotifications } from "../../data/ws-notifications";
-import { computeNotifications } from "./common/compute-notifications";
-import "./components/notifications/hui-notification-drawer";
-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 { showEditViewDialog } from "./editor/view-editor/show-edit-view-dialog";
-
-// CSS and JS should only be imported once. Modules and HTML are safe.
-const CSS_CACHE = {};
-const JS_CACHE = {};
-
-class HUIRoot extends NavigateMixin(
- EventsMixin(localizeMixin(PolymerElement))
-) {
- static get template() {
- return html`
-
-
-
-
-
-
-
-
- [[_computeTitle(config)]]
-
-
-
-
-
-
- Refresh
-
- Unused entities
- [[localize("ui.panel.lovelace.editor.configure_ui")]]
-
- Raw config editor
-
- Help
-
-
-
-
-
-
-
- [[localize("ui.panel.lovelace.editor.header")]]
-
-
-
-
-
-
-
-
-
-
-
- [[_computeTabTitle(item.title)]]
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- `;
- }
-
- static get properties() {
- return {
- narrow: Boolean,
- showMenu: Boolean,
- hass: { type: Object, observer: "_hassChanged" },
- config: {
- type: Object,
- computed: "_computeConfig(lovelace)",
- observer: "_configChanged",
- },
- lovelace: { type: Object },
- columns: { type: Number, observer: "_columnsChanged" },
- _curView: { type: Number, value: 0 },
- route: { type: Object, observer: "_routeChanged" },
- notificationsOpen: { type: Boolean, value: false },
- _persistentNotifications: { type: Array, value: [] },
- _notifications: {
- type: Array,
- computed: "_updateNotifications(hass.states, _persistentNotifications)",
- },
- _yamlMode: {
- type: Boolean,
- computed: "_computeYamlMode(lovelace)",
- },
- _storageMode: {
- type: Boolean,
- computed: "_computeStorageMode(lovelace)",
- },
- _editMode: {
- type: Boolean,
- value: false,
- computed: "_computeEditMode(lovelace)",
- observer: "_editModeChanged",
- },
- routeData: Object,
- };
- }
-
- constructor() {
- super();
- this._debouncedConfigChanged = debounce(
- () => this._selectView(this._curView),
- 100
- );
- }
-
- connectedCallback() {
- super.connectedCallback();
- this._unsubNotifications = subscribeNotifications(
- this.hass.connection,
- (notifications) => {
- this._persistentNotifications = notifications;
- }
- );
- }
-
- disconnectedCallback() {
- super.disconnectedCallback();
- if (typeof this._unsubNotifications === "function") {
- this._unsubNotifications();
- }
- }
-
- _updateNotifications(states, persistent) {
- if (!states) return persistent;
-
- const configurator = computeNotifications(states);
- return persistent.concat(configurator);
- }
-
- _routeChanged(route) {
- const views = this.config && this.config.views;
- if (route.path === "" && route.prefix === "/lovelace" && views) {
- this.navigate(`/lovelace/${views[0].path || 0}`, true);
- } else if (this.routeData.view) {
- const view = this.routeData.view;
- let index = 0;
- for (let i = 0; i < views.length; i++) {
- if (views[i].path === view || i === parseInt(view)) {
- index = i;
- break;
- }
- }
- if (index !== this._curView) this._selectView(index);
- }
- }
-
- _computeViewPath(path, index) {
- return path || index;
- }
-
- _computeTitle(config) {
- return config.title || "Home Assistant";
- }
-
- _computeTabsHidden(views, editMode) {
- return views.length < 2 && !editMode;
- }
-
- _computeTabTitle(title) {
- return title || "Unnamed view";
- }
-
- _handleRefresh() {
- this.fire("config-refresh");
- }
-
- _handleUnusedEntities() {
- this._selectView("unused");
- }
-
- _deselect(ev) {
- ev.target.selected = null;
- }
-
- _handleHelp() {
- window.open("https://www.home-assistant.io/lovelace/", "_blank");
- }
-
- _handleFullEditor() {
- this.lovelace.enableFullEditMode();
- }
-
- _editModeEnable() {
- if (this._yamlMode) {
- window.alert("The edit UI is not available when in YAML mode.");
- return;
- }
- this.lovelace.setEditMode(true);
- if (this.config.views.length < 2) {
- this.$.view.classList.remove("tabs-hidden");
- this.fire("iron-resize");
- }
- }
-
- _editModeDisable() {
- this.lovelace.setEditMode(false);
- if (this.config.views.length < 2) {
- this.$.view.classList.add("tabs-hidden");
- this.fire("iron-resize");
- }
- }
-
- _editModeChanged() {
- this._selectView(this._curView);
- }
-
- _editView() {
- showEditViewDialog(this, {
- lovelace: this.lovelace,
- viewIndex: this._curView,
- });
- }
-
- _addView() {
- showEditViewDialog(this, {
- lovelace: this.lovelace,
- });
- }
-
- _handleViewSelected(ev) {
- const index = ev.detail.selected;
- this._navigateView(index);
- }
-
- _navigateView(viewIndex) {
- if (viewIndex !== this._curView) {
- const path = this.config.views[viewIndex].path || viewIndex;
- this.navigate(`/lovelace/${path}`);
- }
- scrollToTarget(this, this.$.layout.header.scrollTarget);
- }
-
- _selectView(viewIndex) {
- this._curView = viewIndex;
-
- // Recreate a new element to clear the applied themes.
- const root = this.$.view;
- if (root.lastChild) {
- root.removeChild(root.lastChild);
- }
-
- let view;
- let background = this.config.background || "";
-
- if (viewIndex === "unused") {
- view = document.createElement("hui-unused-entities");
- view.setConfig(this.config);
- } else {
- const viewConfig = this.config.views[this._curView];
- if (!viewConfig) {
- this._editModeEnable();
- return;
- }
- if (viewConfig.panel) {
- view = createCardElement(viewConfig.cards[0]);
- view.isPanel = true;
- } else {
- view = document.createElement("hui-view");
- view.lovelace = this.lovelace;
- view.config = viewConfig;
- view.columns = this.columns;
- view.index = viewIndex;
- }
- if (viewConfig.background) background = viewConfig.background;
- }
-
- this.$.view.style.background = background;
-
- view.hass = this.hass;
- root.appendChild(view);
- }
-
- _hassChanged(hass) {
- if (!this.$.view.lastChild) return;
- this.$.view.lastChild.hass = hass;
- }
-
- _configChanged(config) {
- this._loadResources(config.resources || []);
- // On config change, recreate the view from scratch.
- this._selectView(this._curView);
- this.$.view.classList.toggle("tabs-hidden", config.views.length < 2);
- }
-
- _columnsChanged(columns) {
- if (!this.$.view.lastChild) return;
- this.$.view.lastChild.columns = columns;
- }
-
- _loadResources(resources) {
- resources.forEach((resource) => {
- switch (resource.type) {
- case "css":
- if (resource.url in CSS_CACHE) break;
- CSS_CACHE[resource.url] = loadCSS(resource.url);
- break;
-
- case "js":
- if (resource.url in JS_CACHE) break;
- JS_CACHE[resource.url] = loadJS(resource.url);
- break;
-
- case "module":
- loadModule(resource.url);
- break;
-
- case "html":
- import(/* webpackChunkName: "import-href-polyfill" */ "../../resources/html-import/import-href").then(
- ({ importHref }) => importHref(resource.url)
- );
- break;
-
- default:
- // eslint-disable-next-line
- console.warn("Unknown resource type specified: ${resource.type}");
- }
- });
- }
-
- _computeConfig(lovelace) {
- return lovelace ? lovelace.config : null;
- }
-
- _computeYamlMode(lovelace) {
- return lovelace ? lovelace.mode === "yaml" : false;
- }
-
- _computeStorageMode(lovelace) {
- return lovelace ? lovelace.mode === "storage" : false;
- }
-
- _computeEditMode(lovelace) {
- return lovelace ? lovelace.editMode : false;
- }
-}
-customElements.define("hui-root", HUIRoot);
diff --git a/src/panels/lovelace/hui-root.ts b/src/panels/lovelace/hui-root.ts
new file mode 100644
index 0000000000..b261529b38
--- /dev/null
+++ b/src/panels/lovelace/hui-root.ts
@@ -0,0 +1,665 @@
+import {
+ html,
+ LitElement,
+ PropertyDeclarations,
+ PropertyValues,
+} from "@polymer/lit-element";
+import { TemplateResult } from "lit-html";
+import { classMap } from "lit-html/directives/classMap";
+import "@polymer/app-layout/app-header-layout/app-header-layout";
+import "@polymer/app-layout/app-header/app-header";
+import "@polymer/app-layout/app-scroll-effects/effects/waterfall";
+import "@polymer/app-layout/app-toolbar/app-toolbar";
+import "@polymer/app-route/app-route";
+import "@polymer/paper-icon-button/paper-icon-button";
+import "@polymer/paper-button/paper-button";
+import "@polymer/paper-item/paper-item";
+import "@polymer/paper-listbox/paper-listbox";
+import "@polymer/paper-menu-button/paper-menu-button";
+import "@polymer/paper-tabs/paper-tab";
+import "@polymer/paper-tabs/paper-tabs";
+import { HassEntities } from "home-assistant-js-websocket";
+
+import scrollToTarget from "../../common/dom/scroll-to-target";
+
+import "../../layouts/ha-app-layout";
+import "../../components/ha-start-voice-button";
+import "../../components/ha-icon";
+import { loadModule, loadCSS, loadJS } from "../../common/dom/load_resource";
+import { subscribeNotifications } from "../../data/ws-notifications";
+import debounce from "../../common/util/debounce";
+import { hassLocalizeLitMixin } from "../../mixins/lit-localize-mixin";
+import { HomeAssistant } from "../../types";
+import { LovelaceConfig } from "../../data/lovelace";
+import { navigate } from "../../common/navigate";
+import { fireEvent } from "../../common/dom/fire_event";
+import { computeNotifications } from "./common/compute-notifications";
+import "./components/notifications/hui-notification-drawer";
+import "./components/notifications/hui-notifications-button";
+import "./hui-view";
+// Not a duplicate import, this one is for type
+// tslint:disable-next-line
+import { HUIView } from "./hui-view";
+import { createCardElement } from "./common/create-card-element";
+import { showEditViewDialog } from "./editor/view-editor/show-edit-view-dialog";
+import { showEditLovelaceDialog } from "./editor/lovelace-editor/show-edit-lovelace-dialog";
+import { Lovelace } from "./types";
+import { afterNextRender } from "../../common/util/render-status";
+
+// CSS and JS should only be imported once. Modules and HTML are safe.
+const CSS_CACHE = {};
+const JS_CACHE = {};
+
+declare global {
+ // tslint:disable-next-line
+ interface HASSDomEvents {
+ "rebuild-view": {};
+ }
+}
+
+let loadedUnusedEntities = false;
+
+class HUIRoot extends hassLocalizeLitMixin(LitElement) {
+ public narrow?: boolean;
+ public showMenu?: boolean;
+ public hass?: HomeAssistant;
+ public lovelace?: Lovelace;
+ public columns?: number;
+ public route?: { path: string; prefix: string };
+ private _routeData?: { view: string };
+ private _curView?: number | "hass-unused-entities";
+ private _notificationsOpen: boolean;
+ private _persistentNotifications?: Notification[];
+ private _haStyle?: DocumentFragment;
+ private _viewCache?: { [viewId: string]: HUIView };
+
+ private _debouncedConfigChanged: () => void;
+ private _unsubNotifications?: () => void;
+
+ static get properties(): PropertyDeclarations {
+ return {
+ narrow: {},
+ showMenu: {},
+ hass: {},
+ lovelace: {},
+ columns: {},
+ route: {},
+ _routeData: {},
+ _curView: {},
+ _notificationsOpen: {},
+ _persistentNotifications: {},
+ };
+ }
+
+ constructor() {
+ super();
+ this._notificationsOpen = false;
+ // The view can trigger a re-render when it knows that certain
+ // web components have been loaded.
+ this._debouncedConfigChanged = debounce(
+ () => this._selectView(this._curView, true),
+ 100
+ );
+ }
+
+ public connectedCallback(): void {
+ super.connectedCallback();
+ this._unsubNotifications = subscribeNotifications(
+ this.hass!.connection,
+ (notifications) => {
+ this._persistentNotifications = notifications;
+ }
+ );
+ }
+
+ public disconnectedCallback(): void {
+ super.disconnectedCallback();
+ if (this._unsubNotifications) {
+ this._unsubNotifications();
+ }
+ }
+
+ protected render(): TemplateResult {
+ return html`
+ ${this.renderStyle()}
+
+
+
+
+ ${
+ this._editMode
+ ? html`
+
+
+
+ ${
+ this.config.title ||
+ this.localize("ui.panel.lovelace.editor.header")
+ }
+
+
+
+
+
+
+ Raw config editor
+
+
+
+ `
+ : html`
+
+
+ ${this.config.title || "Home Assistant"}
+
+
+
+
+
+ ${
+ this._yamlMode
+ ? html`
+ Refresh
+ `
+ : ""
+ }
+ Unused entities
+ ${
+ this.localize("ui.panel.lovelace.editor.configure_ui")
+ }
+ Help
+
+
+
+ `
+ }
+
+ ${
+ this.lovelace!.config.views.length > 1 || this._editMode
+ ? html`
+
+
+ ${
+ this.lovelace!.config.views.map(
+ (view) => html`
+
+ ${
+ view.icon
+ ? html`
+
+ `
+ : view.title || "Unnamed view"
+ }
+ ${
+ this._editMode
+ ? html`
+
+ `
+ : ""
+ }
+
+ `
+ )
+ }
+ ${
+ this._editMode
+ ? html`
+
+
+
+ `
+ : ""
+ }
+
+
+ `
+ : ""
+ }
+
+
+
+ `;
+ }
+
+ protected renderStyle(): TemplateResult {
+ if (!this._haStyle) {
+ this._haStyle = document.importNode(
+ (document.getElementById("ha-style")!
+ .children[0] as HTMLTemplateElement).content,
+ true
+ );
+ }
+
+ return html`
+ ${this._haStyle}
+
+ `;
+ }
+
+ protected updated(changedProperties: PropertyValues): void {
+ super.updated(changedProperties);
+
+ const view = this._viewRoot;
+ const huiView = view.lastChild as HUIView;
+
+ if (changedProperties.has("columns") && huiView) {
+ huiView.columns = this.columns;
+ }
+
+ if (changedProperties.has("hass") && huiView) {
+ huiView.hass = this.hass;
+ }
+
+ let newSelectView;
+ let force = false;
+
+ if (changedProperties.has("route")) {
+ const views = this.config && this.config.views;
+ if (
+ this.route!.path === "" &&
+ this.route!.prefix === "/lovelace" &&
+ views
+ ) {
+ navigate(this, `/lovelace/${views[0].path || 0}`, true);
+ } else if (this._routeData!.view === "hass-unused-entities") {
+ newSelectView = "hass-unused-entities";
+ } else if (this._routeData!.view) {
+ const selectedView = this._routeData!.view;
+ const selectedViewInt = parseInt(selectedView, 10);
+ let index = 0;
+ for (let i = 0; i < views.length; i++) {
+ if (views[i].path === selectedView || i === selectedViewInt) {
+ index = i;
+ break;
+ }
+ }
+ newSelectView = index;
+ }
+ }
+
+ if (changedProperties.has("lovelace")) {
+ const oldLovelace = changedProperties.get("lovelace") as
+ | Lovelace
+ | undefined;
+
+ if (!oldLovelace || oldLovelace.config !== this.lovelace!.config) {
+ this._loadResources(this.lovelace!.config.resources || []);
+ // On config change, recreate the current view from scratch.
+ force = true;
+ }
+
+ if (!oldLovelace || oldLovelace.editMode !== this.lovelace!.editMode) {
+ // On edit mode change, recreate the current view from scratch
+ force = true;
+ }
+ }
+
+ if (newSelectView !== undefined || force) {
+ if (force && newSelectView === undefined) {
+ newSelectView = this._curView;
+ }
+ this._selectView(newSelectView, force);
+ }
+ }
+
+ private get _notifications() {
+ return this._updateNotifications(
+ this.hass!.states,
+ this._persistentNotifications! || []
+ );
+ }
+
+ private get config(): LovelaceConfig {
+ return this.lovelace!.config;
+ }
+
+ private get _yamlMode(): boolean {
+ return this.lovelace!.mode === "yaml";
+ }
+
+ private get _editMode() {
+ return this.lovelace!.editMode;
+ }
+
+ private get _layout(): any {
+ return this.shadowRoot!.getElementById("layout");
+ }
+
+ private get _viewRoot(): HTMLDivElement {
+ return this.shadowRoot!.getElementById("view") as HTMLDivElement;
+ }
+
+ private _routeDataChanged(ev): void {
+ this._routeData = ev.detail.value;
+ }
+
+ private _handleNotificationsOpenChanged(ev): void {
+ this._notificationsOpen = ev.detail.value;
+ }
+
+ private _updateNotifications(
+ states: HassEntities,
+ persistent: Array
+ ): Array {
+ const configurator = computeNotifications(states);
+ return persistent.concat(configurator);
+ }
+
+ private _handleRefresh(): void {
+ fireEvent(this, "config-refresh");
+ }
+
+ private _handleUnusedEntities(): void {
+ navigate(this, `/lovelace/hass-unused-entities`);
+ }
+
+ private _deselect(ev): void {
+ ev.target.selected = null;
+ }
+
+ private _handleHelp(): void {
+ window.open("https://www.home-assistant.io/lovelace/", "_blank");
+ }
+
+ private _editModeEnable(): void {
+ if (this._yamlMode) {
+ window.alert("The edit UI is not available when in YAML mode.");
+ return;
+ }
+ this.lovelace!.setEditMode(true);
+ if (this.config.views.length < 2) {
+ fireEvent(this, "iron-resize");
+ }
+ }
+
+ private _editModeDisable(): void {
+ this.lovelace!.setEditMode(false);
+ if (this.config.views.length < 2) {
+ fireEvent(this, "iron-resize");
+ }
+ }
+
+ private _editLovelace() {
+ showEditLovelaceDialog(this, this.lovelace!);
+ }
+
+ private _editView() {
+ showEditViewDialog(this, {
+ lovelace: this.lovelace!,
+ viewIndex: this._curView as number,
+ });
+ }
+
+ private _addView() {
+ showEditViewDialog(this, {
+ lovelace: this.lovelace!,
+ });
+ }
+
+ private _handleViewSelected(ev) {
+ const viewIndex = ev.detail.selected as number;
+
+ if (viewIndex !== this._curView) {
+ const path = this.config.views[viewIndex].path || viewIndex;
+ navigate(this, `/lovelace/${path}`);
+ }
+ scrollToTarget(this, this._layout.header.scrollTarget);
+ }
+
+ private async _selectView(
+ viewIndex: HUIRoot["_curView"],
+ force: boolean
+ ): Promise {
+ if (!force && this._curView === viewIndex) {
+ return;
+ }
+
+ viewIndex = viewIndex === undefined ? 0 : viewIndex;
+
+ this._curView = viewIndex;
+
+ if (force) {
+ this._viewCache = {};
+ }
+
+ // Recreate a new element to clear the applied themes.
+ const root = this._viewRoot;
+
+ if (root.lastChild) {
+ root.removeChild(root.lastChild);
+ }
+
+ if (viewIndex === "hass-unused-entities") {
+ if (!loadedUnusedEntities) {
+ loadedUnusedEntities = true;
+ await import(/* webpackChunkName: "hui-unused-entities" */ "./hui-unused-entities");
+ }
+ const unusedEntities = document.createElement("hui-unused-entities");
+ unusedEntities.setConfig(this.config);
+ unusedEntities.hass = this.hass!;
+ root.style.background = this.config.background || "";
+ root.appendChild(unusedEntities);
+ return;
+ }
+
+ let view;
+ const viewConfig = this.config.views[viewIndex];
+
+ if (!viewConfig) {
+ this._editModeEnable();
+ return;
+ }
+
+ if (!force && this._viewCache![viewIndex]) {
+ view = this._viewCache![viewIndex];
+ } else {
+ await new Promise((resolve) => afterNextRender(resolve));
+
+ if (viewConfig.panel && viewConfig.cards && viewConfig.cards.length > 0) {
+ view = createCardElement(viewConfig.cards[0]);
+ view.isPanel = true;
+ } else {
+ view = document.createElement("hui-view");
+ view.lovelace = this.lovelace;
+ view.columns = this.columns;
+ view.index = viewIndex;
+ }
+ this._viewCache![viewIndex] = view;
+ }
+
+ view.hass = this.hass;
+ root.style.background =
+ viewConfig.background || this.config.background || "";
+ root.appendChild(view);
+ }
+
+ private _loadResources(resources) {
+ resources.forEach((resource) => {
+ switch (resource.type) {
+ case "css":
+ if (resource.url in CSS_CACHE) {
+ break;
+ }
+ CSS_CACHE[resource.url] = loadCSS(resource.url);
+ break;
+
+ case "js":
+ if (resource.url in JS_CACHE) {
+ break;
+ }
+ JS_CACHE[resource.url] = loadJS(resource.url);
+ break;
+
+ case "module":
+ loadModule(resource.url);
+ break;
+
+ case "html":
+ import(/* webpackChunkName: "import-href-polyfill" */ "../../resources/html-import/import-href").then(
+ ({ importHref }) => importHref(resource.url)
+ );
+ break;
+
+ default:
+ // tslint:disable-next-line
+ console.warn(`Unknown resource type specified: ${resource.type}`);
+ }
+ });
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ "hui-root": HUIRoot;
+ }
+}
+
+customElements.define("hui-root", HUIRoot);
diff --git a/src/panels/lovelace/hui-unused-entities.ts b/src/panels/lovelace/hui-unused-entities.ts
index 9548c44bec..ec3970b9ae 100644
--- a/src/panels/lovelace/hui-unused-entities.ts
+++ b/src/panels/lovelace/hui-unused-entities.ts
@@ -2,15 +2,16 @@ import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element";
import "./cards/hui-entities-card";
-import computeUnusedEntities from "./common/compute-unused-entities";
-import createCardElement from "./common/create-card-element";
+import { computeUnusedEntities } from "./common/compute-unused-entities";
+import { createCardElement } from "./common/create-card-element";
import { HomeAssistant } from "../../types";
import { TemplateResult } from "lit-html";
import { LovelaceCard } from "./types";
+import { LovelaceConfig } from "../../data/lovelace";
export class HuiUnusedEntities extends LitElement {
private _hass?: HomeAssistant;
- private _config?: object;
+ private _config?: LovelaceConfig;
private _element?: LovelaceCard;
static get properties(): PropertyDeclarations {
@@ -29,10 +30,7 @@ export class HuiUnusedEntities extends LitElement {
this._element.hass = this._hass;
}
- public setConfig(config: object): void {
- if (!config) {
- throw new Error("Card config incorrect");
- }
+ public setConfig(config: LovelaceConfig): void {
this._config = config;
this._createElement();
}
@@ -62,7 +60,7 @@ export class HuiUnusedEntities extends LitElement {
private _createElement(): void {
if (this._hass) {
- const entities = computeUnusedEntities(this._hass, this._config).map(
+ const entities = computeUnusedEntities(this._hass, this._config!).map(
(entity) => ({
entity,
secondary_info: "entity-id",
diff --git a/src/panels/lovelace/hui-view-editable.ts b/src/panels/lovelace/hui-view-editable.ts
new file mode 100644
index 0000000000..8740b29f13
--- /dev/null
+++ b/src/panels/lovelace/hui-view-editable.ts
@@ -0,0 +1,3 @@
+// hui-view dependencies for when in edit mode.
+import "@polymer/paper-fab/paper-fab";
+import "./components/hui-card-options";
diff --git a/src/panels/lovelace/hui-view.js b/src/panels/lovelace/hui-view.js
deleted file mode 100644
index 369dd3fb1c..0000000000
--- a/src/panels/lovelace/hui-view.js
+++ /dev/null
@@ -1,251 +0,0 @@
-import { html } from "@polymer/polymer/lib/utils/html-tag";
-import { PolymerElement } from "@polymer/polymer/polymer-element";
-
-import "@polymer/paper-fab/paper-fab";
-import "../../components/entity/ha-state-label-badge";
-import "./components/hui-card-options";
-
-import applyThemesOnElement from "../../common/dom/apply_themes_on_element";
-
-import EventsMixin from "../../mixins/events-mixin";
-import localizeMixin from "../../mixins/localize-mixin";
-import createCardElement from "./common/create-card-element";
-import { computeCardSize } from "./common/compute-card-size";
-import { showEditCardDialog } from "./editor/card-editor/show-edit-card-dialog";
-
-class HUIView extends localizeMixin(EventsMixin(PolymerElement)) {
- static get template() {
- return html`
-
-
-
-
- `;
- }
-
- static get properties() {
- return {
- hass: {
- type: Object,
- observer: "_hassChanged",
- },
- lovelace: Object,
- config: Object,
- columns: Number,
- editMode: Boolean,
- index: Number,
- };
- }
-
- static get observers() {
- return [
- // Put all properties in 1 observer so we only call configChanged once
- "_createBadges(config)",
- "_createCards(config, columns, editMode)",
- ];
- }
-
- constructor() {
- super();
- this._cards = [];
- this._badges = [];
- }
-
- _addCard() {
- showEditCardDialog(this, {
- lovelace: this.lovelace,
- path: [this.index],
- });
- }
-
- _createBadges(config) {
- const root = this.$.badges;
- while (root.lastChild) {
- root.removeChild(root.lastChild);
- }
-
- if (!config || !config.badges || !Array.isArray(config.badges)) {
- root.style.display = "none";
- this._badges = [];
- return;
- }
-
- const elements = [];
- for (const entityId of config.badges) {
- if (!(entityId in this.hass.states)) continue;
-
- const element = document.createElement("ha-state-label-badge");
- element.setProperties({
- hass: this.hass,
- state: this.hass.states[entityId],
- });
- elements.push({ element, entityId });
- root.appendChild(element);
- }
- this._badges = elements;
- root.style.display = elements.length > 0 ? "block" : "none";
- }
-
- _createCards(config) {
- const root = this.$.columns;
-
- while (root.lastChild) {
- root.removeChild(root.lastChild);
- }
-
- if (!config || !config.cards || !Array.isArray(config.cards)) {
- this._cards = [];
- return;
- }
-
- const elements = [];
- const elementsToAppend = [];
- config.cards.forEach((cardConfig, cardIndex) => {
- const element = createCardElement(cardConfig);
- element.hass = this.hass;
- elements.push(element);
-
- if (!this.lovelace.editMode) {
- elementsToAppend.push(element);
- return;
- }
-
- const wrapper = document.createElement("hui-card-options");
- wrapper.hass = this.hass;
- wrapper.lovelace = this.lovelace;
- wrapper.path = [this.index, cardIndex];
- wrapper.appendChild(element);
- elementsToAppend.push(wrapper);
- });
-
- let columns = [];
- const columnEntityCount = [];
- for (let i = 0; i < this.columns; i++) {
- columns.push([]);
- columnEntityCount.push(0);
- }
-
- // Find column with < 5 entities, else column with lowest count
- function getColumnIndex(size) {
- let minIndex = 0;
- for (let i = 0; i < columnEntityCount.length; i++) {
- if (columnEntityCount[i] < 5) {
- minIndex = i;
- break;
- }
- if (columnEntityCount[i] < columnEntityCount[minIndex]) {
- minIndex = i;
- }
- }
-
- columnEntityCount[minIndex] += size;
-
- return minIndex;
- }
-
- elements.forEach((el, index) => {
- const cardSize = computeCardSize(el);
- // Element to append might be the wrapped card when we're editing.
- columns[getColumnIndex(cardSize)].push(elementsToAppend[index]);
- });
-
- // Remove empty columns
- columns = columns.filter((val) => val.length > 0);
-
- columns.forEach((column) => {
- const columnEl = document.createElement("div");
- columnEl.classList.add("column");
- column.forEach((el) => columnEl.appendChild(el));
- root.appendChild(columnEl);
- });
-
- this._cards = elements;
-
- if ("theme" in config) {
- applyThemesOnElement(root, this.hass.themes, config.theme);
- }
- }
-
- _hassChanged(hass) {
- this._badges.forEach((badge) => {
- const { element, entityId } = badge;
- element.setProperties({
- hass,
- state: hass.states[entityId],
- });
- });
- this._cards.forEach((element) => {
- element.hass = hass;
- });
- }
-}
-
-customElements.define("hui-view", HUIView);
diff --git a/src/panels/lovelace/hui-view.ts b/src/panels/lovelace/hui-view.ts
new file mode 100644
index 0000000000..471e917c1e
--- /dev/null
+++ b/src/panels/lovelace/hui-view.ts
@@ -0,0 +1,298 @@
+import {
+ html,
+ LitElement,
+ PropertyValues,
+ PropertyDeclarations,
+} from "@polymer/lit-element";
+import { TemplateResult } from "lit-html";
+
+import "../../components/entity/ha-state-label-badge";
+// This one is for types
+// tslint:disable-next-line
+import { HaStateLabelBadge } from "../../components/entity/ha-state-label-badge";
+
+import applyThemesOnElement from "../../common/dom/apply_themes_on_element";
+
+import { hassLocalizeLitMixin } from "../../mixins/lit-localize-mixin";
+import { LovelaceViewConfig } from "../../data/lovelace";
+import { HomeAssistant } from "../../types";
+
+import { Lovelace, LovelaceCard } from "./types";
+import { createCardElement } from "./common/create-card-element";
+import { computeCardSize } from "./common/compute-card-size";
+import { showEditCardDialog } from "./editor/card-editor/show-edit-card-dialog";
+
+let editCodeLoaded = false;
+
+// Find column with < 5 entities, else column with lowest count
+const getColumnIndex = (columnEntityCount: number[], size: number) => {
+ let minIndex = 0;
+ for (let i = 0; i < columnEntityCount.length; i++) {
+ if (columnEntityCount[i] < 5) {
+ minIndex = i;
+ break;
+ }
+ if (columnEntityCount[i] < columnEntityCount[minIndex]) {
+ minIndex = i;
+ }
+ }
+
+ columnEntityCount[minIndex] += size;
+
+ return minIndex;
+};
+
+export class HUIView extends hassLocalizeLitMixin(LitElement) {
+ public hass?: HomeAssistant;
+ public lovelace?: Lovelace;
+ public columns?: number;
+ public index?: number;
+ private _cards: LovelaceCard[];
+ private _badges: Array<{ element: HaStateLabelBadge; entityId: string }>;
+
+ static get properties(): PropertyDeclarations {
+ return {
+ hass: {},
+ lovelace: {},
+ columns: {},
+ index: {},
+ _cards: {},
+ _badges: {},
+ };
+ }
+
+ constructor() {
+ super();
+ this._cards = [];
+ this._badges = [];
+ }
+
+ protected render(): TemplateResult {
+ return html`
+ ${this.renderStyles()}
+
+
+ ${
+ this.lovelace!.editMode
+ ? html`
+
+ `
+ : ""
+ }
+ `;
+ }
+
+ protected renderStyles(): TemplateResult {
+ return html`
+
+ `;
+ }
+
+ protected updated(changedProperties: PropertyValues): void {
+ super.updated(changedProperties);
+
+ const lovelace = this.lovelace!;
+
+ if (lovelace.editMode && !editCodeLoaded) {
+ editCodeLoaded = true;
+ import(/* webpackChunkName: "hui-view-editable" */ "./hui-view-editable");
+ }
+
+ let editModeChanged = false;
+ let configChanged = false;
+
+ if (changedProperties.has("lovelace")) {
+ const oldLovelace = changedProperties.get("lovelace") as Lovelace;
+ editModeChanged =
+ !oldLovelace || lovelace.editMode !== oldLovelace.editMode;
+ configChanged = !oldLovelace || lovelace.config !== oldLovelace.config;
+ }
+
+ if (configChanged) {
+ this._createBadges(lovelace.config.views[this.index!]);
+ } else if (changedProperties.has("hass")) {
+ this._badges.forEach((badge) => {
+ const { element, entityId } = badge;
+ element.hass = this.hass!;
+ element.state = this.hass!.states[entityId];
+ });
+ }
+
+ if (configChanged || editModeChanged || changedProperties.has("columns")) {
+ this._createCards(lovelace.config.views[this.index!]);
+ } else if (changedProperties.has("hass")) {
+ this._cards.forEach((element) => {
+ element.hass = this.hass;
+ });
+ }
+ }
+
+ private _addCard(): void {
+ showEditCardDialog(this, {
+ lovelace: this.lovelace!,
+ path: [this.index!],
+ });
+ }
+
+ private _createBadges(config: LovelaceViewConfig): void {
+ const root = this.shadowRoot!.getElementById("badges")!;
+
+ while (root.lastChild) {
+ root.removeChild(root.lastChild);
+ }
+
+ if (!config || !config.badges || !Array.isArray(config.badges)) {
+ root.style.display = "none";
+ this._badges = [];
+ return;
+ }
+
+ const elements: HUIView["_badges"] = [];
+ for (const entityId of config.badges) {
+ const element = document.createElement("ha-state-label-badge");
+ element.hass = this.hass;
+ element.state = this.hass!.states[entityId];
+ elements.push({ element, entityId });
+ root.appendChild(element);
+ }
+ this._badges = elements;
+ root.style.display = elements.length > 0 ? "block" : "none";
+ }
+
+ private _createCards(config: LovelaceViewConfig): void {
+ const root = this.shadowRoot!.getElementById("columns")!;
+
+ while (root.lastChild) {
+ root.removeChild(root.lastChild);
+ }
+
+ if (!config || !config.cards || !Array.isArray(config.cards)) {
+ this._cards = [];
+ return;
+ }
+
+ const elements: LovelaceCard[] = [];
+ const elementsToAppend: HTMLElement[] = [];
+ config.cards.forEach((cardConfig, cardIndex) => {
+ const element = createCardElement(cardConfig) as LovelaceCard;
+ element.hass = this.hass;
+ elements.push(element);
+
+ if (!this.lovelace!.editMode) {
+ elementsToAppend.push(element);
+ return;
+ }
+
+ const wrapper = document.createElement("hui-card-options");
+ wrapper.hass = this.hass;
+ wrapper.lovelace = this.lovelace;
+ wrapper.path = [this.index!, cardIndex];
+ wrapper.appendChild(element);
+ elementsToAppend.push(wrapper);
+ });
+
+ let columns: HTMLElement[][] = [];
+ const columnEntityCount: number[] = [];
+ for (let i = 0; i < this.columns!; i++) {
+ columns.push([]);
+ columnEntityCount.push(0);
+ }
+
+ elements.forEach((el, index) => {
+ const cardSize = computeCardSize(el);
+ // Element to append might be the wrapped card when we're editing.
+ columns[getColumnIndex(columnEntityCount, cardSize)].push(
+ elementsToAppend[index]
+ );
+ });
+
+ // Remove empty columns
+ columns = columns.filter((val) => val.length > 0);
+
+ columns.forEach((column) => {
+ const columnEl = document.createElement("div");
+ columnEl.classList.add("column");
+ column.forEach((el) => columnEl.appendChild(el));
+ root.appendChild(columnEl);
+ });
+
+ this._cards = elements;
+
+ if ("theme" in config) {
+ applyThemesOnElement(root, this.hass!.themes, config.theme);
+ }
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ "hui-view": HUIView;
+ }
+}
+
+customElements.define("hui-view", HUIView);
diff --git a/src/panels/mailbox/ha-panel-mailbox.js b/src/panels/mailbox/ha-panel-mailbox.js
index 6cde979b4a..2d10df3961 100644
--- a/src/panels/mailbox/ha-panel-mailbox.js
+++ b/src/panels/mailbox/ha-panel-mailbox.js
@@ -167,7 +167,8 @@ class HaPanelMailbox extends EventsMixin(LocalizeMixin(PolymerElement)) {
this.fire("register-dialog", {
dialogShowEvent: "show-audio-message-dialog",
dialogTag: "ha-dialog-show-audio-message",
- dialogImport: () => import("./ha-dialog-show-audio-message"),
+ dialogImport: () =>
+ import(/* webpackChunkName: "ha-dialog-show-audio-message" */ "./ha-dialog-show-audio-message"),
});
}
this.hassChanged = this.hassChanged.bind(this);
diff --git a/src/panels/profile/ha-mfa-modules-card.js b/src/panels/profile/ha-mfa-modules-card.js
index daaccd1c2b..50d07606f5 100644
--- a/src/panels/profile/ha-mfa-modules-card.js
+++ b/src/panels/profile/ha-mfa-modules-card.js
@@ -90,7 +90,8 @@ class HaMfaModulesCard extends EventsMixin(LocalizeMixin(PolymerElement)) {
this.fire("register-dialog", {
dialogShowEvent: "show-mfa-module-setup-flow",
dialogTag: "ha-mfa-module-setup-flow",
- dialogImport: () => import("./ha-mfa-module-setup-flow"),
+ dialogImport: () =>
+ import(/* webpackChunkName: "ha-mfa-module-setup-flow" */ "./ha-mfa-module-setup-flow"),
});
}
}
diff --git a/src/translations/en.json b/src/translations/en.json
index 6b85bcad9f..cb2e5b6a0e 100644
--- a/src/translations/en.json
+++ b/src/translations/en.json
@@ -35,6 +35,7 @@
"updater": "Updater",
"vacuum": "Vacuum",
"weblink": "Weblink",
+ "zha": "ZHA",
"zwave": "Z-Wave"
},
"panel": {
@@ -472,6 +473,19 @@
}
},
"dialogs": {
+ "more_info_control": {
+ "script": {
+ "last_action": "Last Action"
+ },
+ "sun": {
+ "elevation": "Elevation",
+ "rising": "Rising",
+ "setting": "Setting"
+ },
+ "updater": {
+ "title": "Update Instructions"
+ }
+ },
"more_info_settings": {
"save": "Save",
"name": "Name",
@@ -751,6 +765,10 @@
"delete_user": "Delete user"
}
},
+ "zha": {
+ "caption": "ZHA",
+ "description": "Zigbee Home Automation network management"
+ },
"zwave": {
"caption": "Z-Wave",
"description": "Manage your Z-Wave network"
diff --git a/src/util/register-service-worker.js b/src/util/register-service-worker.js
index aa49340885..893f27c01e 100644
--- a/src/util/register-service-worker.js
+++ b/src/util/register-service-worker.js
@@ -14,8 +14,8 @@ export default () => {
!__DEV__
) {
// Notify users here of a new frontend being available.
- import("./show-new-frontend-toast").then((mod) =>
- mod.default(installingWorker)
+ import(/* webpackChunkName: "show-new-frontend-toast" */ "./show-new-frontend-toast").then(
+ (mod) => mod.default(installingWorker)
);
}
});
diff --git a/test-mocha/panels/lovelace/editor/config-util.spec.ts b/test-mocha/panels/lovelace/editor/config-util.spec.ts
index b8a9717b21..fc417e2ca1 100644
--- a/test-mocha/panels/lovelace/editor/config-util.spec.ts
+++ b/test-mocha/panels/lovelace/editor/config-util.spec.ts
@@ -1,6 +1,9 @@
import * as assert from "assert";
-import { swapCard } from "../../../../src/panels/lovelace/editor/config-util";
+import {
+ swapCard,
+ moveCard,
+} from "../../../../src/panels/lovelace/editor/config-util";
import { LovelaceConfig } from "../../../../src/data/lovelace";
describe("swapCard", () => {
@@ -52,3 +55,77 @@ describe("swapCard", () => {
assert.deepEqual(expected, result);
});
});
+
+describe("moveCard", () => {
+ it("move a card to an empty view", () => {
+ const config: LovelaceConfig = {
+ views: [
+ {},
+ {
+ cards: [{ type: "card1" }, { type: "card2" }],
+ },
+ ],
+ };
+
+ const result = moveCard(config, [1, 0], [0]);
+ const expected = {
+ views: [
+ {
+ cards: [{ type: "card1" }],
+ },
+ {
+ cards: [{ type: "card2" }],
+ },
+ ],
+ };
+ assert.deepEqual(expected, result);
+ });
+
+ it("move a card to different view", () => {
+ const config: LovelaceConfig = {
+ views: [
+ {
+ cards: [{ type: "v1-c1" }, { type: "v1-c2" }],
+ },
+ {
+ cards: [{ type: "v2-c1" }, { type: "v2-c2" }],
+ },
+ ],
+ };
+
+ const result = moveCard(config, [1, 0], [0]);
+ const expected = {
+ views: [
+ {
+ cards: [{ type: "v1-c1" }, { type: "v1-c2" }, { type: "v2-c1" }],
+ },
+ {
+ cards: [{ type: "v2-c2" }],
+ },
+ ],
+ };
+ assert.deepEqual(expected, result);
+ });
+
+ it("move a card to the same view", () => {
+ const config: LovelaceConfig = {
+ views: [
+ {
+ cards: [{ type: "v1-c1" }, { type: "v1-c2" }],
+ },
+ {
+ cards: [{ type: "v2-c1" }, { type: "v2-c2" }],
+ },
+ ],
+ };
+
+ const result = () => {
+ moveCard(config, [1, 0], [1]);
+ };
+ assert.throws(
+ result,
+ Error,
+ "You can not move a card to the view it is in."
+ );
+ });
+});
diff --git a/translations/bg.json b/translations/bg.json
index 123623a634..d97b7127f2 100644
--- a/translations/bg.json
+++ b/translations/bg.json
@@ -53,8 +53,8 @@
"on": "Регистриран"
},
"motion": {
- "off": "Чисто",
- "on": "Регистрирано"
+ "off": "Без движение",
+ "on": "Движение"
},
"occupancy": {
"off": "Чисто",
@@ -144,7 +144,8 @@
"performance": "Производителност",
"high_demand": "Високо натоварване",
"heat_pump": "Термопомпа",
- "gas": "Газ"
+ "gas": "Газ",
+ "manual": "Ръчен режим"
},
"configurator": {
"configure": "Настройване",
@@ -185,8 +186,8 @@
"on": "Включен"
},
"light": {
- "off": "Изключена",
- "on": "Включен"
+ "off": "Изключено",
+ "on": "Включено"
},
"lock": {
"locked": "Заключен",
@@ -422,6 +423,10 @@
"event": "Събитие",
"enter": "Влизане",
"leave": "Излизане"
+ },
+ "webhook": {
+ "label": "Webhook",
+ "webhook_id": "Webhook идентификатор"
}
}
},
@@ -712,6 +717,47 @@
"required_fields": "Попълнете всички задължителни полета"
}
}
+ },
+ "lovelace": {
+ "cards": {
+ "shopping-list": {
+ "checked_items": "Отметнати артикули",
+ "clear_items": "Изтрий отметнатите артикули",
+ "add_item": "Добави артикул"
+ }
+ },
+ "editor": {
+ "edit_card": {
+ "header": "Конфигуриране на Карта",
+ "save": "Запази",
+ "toggle_editor": "Превключете редактора",
+ "pick_card": "Изберете картата, която искате да добавите.",
+ "add": "Добавяне на карта",
+ "edit": "Редактиране",
+ "delete": "Изтриване"
+ },
+ "migrate": {
+ "header": "Несъвместима конфигурация",
+ "para_no_id": "Този елемент няма идентификатор. Моля, добавете идентификатор към този елемент в \"ui-lovelace.yaml\".",
+ "para_migrate": "Home Assistant може автоматично да добави идентификатори към всичките ви карти и изгледи, като натиска бутона \"Мигриране на конфигурация\".",
+ "migrate": "Мигриране на конфигурация"
+ },
+ "header": "Редактиране на потребителския интерфейс",
+ "configure_ui": "Конфигуриране на потребителския интерфейс",
+ "edit_view": {
+ "header": "Конфигурация на изглед",
+ "add": "Добавяне на изглед",
+ "edit": "Редактиране на изгледа",
+ "delete": "Изтриване на изгледа"
+ },
+ "save_config": {
+ "header": "Поемете контрол над потребителския интерфейс на Lovelace",
+ "para": "По подразбиране Home Assistant поддържа потребителския интерфейс, като го актуализира, когато нови обекти или компоненти на Lovelace станат достъпни. Ако поемете контрол, ние вече няма да правим автоматично промени вместо вас.",
+ "para_sure": "Наистина ли искате да поемете управлението на потребителския интерфейс?",
+ "cancel": "Няма значение",
+ "save": "Поемете контрола"
+ }
+ }
}
},
"sidebar": {
@@ -720,7 +766,8 @@
},
"common": {
"loading": "Зареждане",
- "cancel": "Отмени"
+ "cancel": "Отмени",
+ "save": "Запази"
},
"duration": {
"day": "{count}{count, plural,\n one {ден}\n other {дни}\n}",
@@ -780,7 +827,9 @@
"clear_code": "Изчистване",
"disarm": "Деактивирaне",
"arm_home": "Под охрана - вкъщи",
- "arm_away": "Под охрана"
+ "arm_away": "Под охрана",
+ "arm_night": "Под охрана - нощ",
+ "armed_custom_bypass": "Потребителски байпас"
},
"automation": {
"last_triggered": "Последно задействане",
diff --git a/translations/ca.json b/translations/ca.json
index 6287c8b1a5..5b0e98263c 100644
--- a/translations/ca.json
+++ b/translations/ca.json
@@ -133,8 +133,8 @@
"climate": {
"off": "Apagat",
"on": "Encès",
- "heat": "Calent",
- "cool": "Fred",
+ "heat": "Escalfar",
+ "cool": "Refredar",
"idle": "Inactiu",
"auto": "Automàtic",
"dry": "Assecar",
@@ -423,6 +423,10 @@
"event": "Esdeveniment:",
"enter": "Entrar",
"leave": "Sortir"
+ },
+ "webhook": {
+ "label": "Webhook",
+ "webhook_id": "ID de Webhook"
}
}
},
@@ -820,7 +824,7 @@
},
"alarm_control_panel": {
"code": "Codi",
- "clear_code": "Esborrar",
+ "clear_code": "Supr.",
"disarm": "Desactivar",
"arm_home": "Activar, a casa",
"arm_away": "Activar, fora",
diff --git a/translations/cs.json b/translations/cs.json
index c8d3940366..a298eb373b 100644
--- a/translations/cs.json
+++ b/translations/cs.json
@@ -423,6 +423,10 @@
"event": "Událost:",
"enter": "Vstup",
"leave": "Opuštění"
+ },
+ "webhook": {
+ "label": "Webhook",
+ "webhook_id": "Webhook ID"
}
}
},
@@ -726,13 +730,32 @@
"edit_card": {
"header": "Konfigurace karty",
"save": "Uložit",
- "toggle_editor": "Přepnout Editor"
+ "toggle_editor": "Přepnout Editor",
+ "pick_card": "Vyberte kartu, kterou chcete přidat.",
+ "add": "Přidat kartu",
+ "edit": "Upravit",
+ "delete": "Odstranit"
},
"migrate": {
"header": "Konfigurace není kompatibilní",
"para_no_id": "Tento prvek nemá ID. Přidejte k tomuto prvku ID v 'ui-lovelace.yaml'.",
"para_migrate": "Home Assistant může automaticky přidávat ID ke všem kartám a pohledům stisknutím tlačítka Migrate config.",
"migrate": "Migrovat konfiguraci"
+ },
+ "header": "Upravit UI",
+ "configure_ui": "Konfigurovat UI",
+ "edit_view": {
+ "header": "Zobrazit konfiguraci",
+ "add": "Přidat pohled",
+ "edit": "Upravit pohled",
+ "delete": "Odstranit pohled"
+ },
+ "save_config": {
+ "header": "Převzít kontrolu nad vaší Lovelace UI",
+ "para": "Ve výchozím nastavení Home Assistant bude usprávovat vaše uživatelské rozhraní, aktualizovat při přidání nové entity nebo Lovelace komponenty. Pokud převezmete kontrolu nebudeme již provádět změny automaticky za vás.",
+ "para_sure": "Opravdu chcete převzít kontrolu nad uživalským rohraním ?",
+ "cancel": "Zahodit změnu",
+ "save": "Převzít kontrolu"
}
}
}
@@ -743,7 +766,8 @@
},
"common": {
"loading": "Načítání",
- "cancel": "Zrušit"
+ "cancel": "Zrušit",
+ "save": "Uložit"
},
"duration": {
"day": "{count} {count, plural,\none {den}\nother {dny}\n}",
diff --git a/translations/da.json b/translations/da.json
index 9c9d1aca1e..607c4045b6 100644
--- a/translations/da.json
+++ b/translations/da.json
@@ -423,6 +423,10 @@
"event": "Begivenhed",
"enter": "Ankom",
"leave": "Forlade"
+ },
+ "webhook": {
+ "label": "Webhook",
+ "webhook_id": "Webhook-ID"
}
}
},
@@ -605,12 +609,17 @@
"logout": "Log af",
"change_password": {
"header": "Skift adgangskode",
+ "current_password": "Nuværende adgangskode",
+ "new_password": "Ny adgangskode",
+ "confirm_new_password": "Bekræft ny adgangskode",
+ "error_required": "Påkrævet",
"submit": "Gem og afslut"
},
"mfa": {
"header": "Multifaktor godkendelsesmoduler",
"disable": "Deaktiver",
- "enable": "Aktiver"
+ "enable": "Aktiver",
+ "confirm_disable": "Er du sikker på du vil deaktivere {name}?"
},
"mfa_setup": {
"title_aborted": "Afbrudt",
@@ -708,6 +717,47 @@
"required_fields": "Udfyld alle obligatoriske felter"
}
}
+ },
+ "lovelace": {
+ "cards": {
+ "shopping-list": {
+ "checked_items": "Markerede elementer",
+ "clear_items": "Ryd markerede elementer",
+ "add_item": "Tilføj element"
+ }
+ },
+ "editor": {
+ "edit_card": {
+ "header": "Kortkonfiguration",
+ "save": "Gem",
+ "toggle_editor": "Skifte Editor",
+ "pick_card": "Vælg det kort, du vil tilføje.",
+ "add": "Tilføj kort",
+ "edit": "Rediger",
+ "delete": "Slet"
+ },
+ "migrate": {
+ "header": "Konfiguration ikke færdiggjort",
+ "para_no_id": "Dette element har ikke et id. Tilføj venligst et id til dette element i \"ui-lovelace.yaml\".",
+ "para_migrate": "Hjem assistent kan tilføje id'er til alle dine kort og oversigter automatisk for dig ved at trykke på knappen 'Overfør configuration'.",
+ "migrate": "Migrer opsætning"
+ },
+ "header": "Rediger UI",
+ "configure_ui": "Konfigurer UI",
+ "edit_view": {
+ "header": "Vis konfiguration",
+ "add": "Tilføje visning",
+ "edit": "Redigere visning",
+ "delete": "Slette visning"
+ },
+ "save_config": {
+ "header": "Tage kontrol over din 2Lovelace\" UI",
+ "para": "Som standard vil hjem assistenten fastholde din brugergrænseflade, opdaterer den når nye enheder eller \"Lovelace\" komponenter bliver tilgængelige. Hvis du tager kontrol så vil vi ikke længere foretage ændringer automatisk for dig.",
+ "para_sure": "Er du sikker på du ønsker at tage kontrol over din brugergrænseflade?",
+ "cancel": "Glem det",
+ "save": "tag kontrol"
+ }
+ }
}
},
"sidebar": {
@@ -716,7 +766,8 @@
},
"common": {
"loading": "Indlæser",
- "cancel": "Annuller"
+ "cancel": "Annuller",
+ "save": "Gem"
},
"duration": {
"day": "{count} {count, plural,\none {dag}\nother {dage}\n}",
diff --git a/translations/de.json b/translations/de.json
index 5dbe8b5044..0ac9086658 100644
--- a/translations/de.json
+++ b/translations/de.json
@@ -365,7 +365,7 @@
"alias": "Name",
"triggers": {
"header": "Auslöser",
- "introduction": "Auslöser starten automatisierte Abläufe. Es ist mögliche, mehrere Auslöser für dieselbe Abfolge zu definieren. Wenn ein Auslöser aktiviert wird, prüft Home Assistant die Bedingungen, sofern vorhanden, und führt die Aktion aus.\n\n[Erfahre mehr über Auslöser.](https:\/\/home-assistant.io\/docs\/automation\/trigger\/)",
+ "introduction": "Auslöser starten automatisierte Abläufe. Es ist möglich, mehrere Auslöser für dieselbe Abfolge zu definieren. Wenn ein Auslöser aktiviert wird, prüft Home Assistant die Bedingungen, sofern vorhanden, und führt die Aktion aus.\n\n[Erfahre mehr über Auslöser.](https:\/\/home-assistant.io\/docs\/automation\/trigger\/)",
"add": "Auslöser hinzufügen",
"duplicate": "Duplizieren",
"delete": "Löschen",
@@ -423,12 +423,16 @@
"event": "Ereignis:",
"enter": "Betreten",
"leave": "Verlassen"
+ },
+ "webhook": {
+ "label": "Webhook",
+ "webhook_id": "Webhook ID"
}
}
},
"conditions": {
"header": "Bedingungen",
- "introduction": "Bedingungen sind ein optionaler Bestandteil bei automatisierten Abläufen und können das Ausführen einer Aktion verhindern. Bedingungen sind Auslösern ähnlich, aber dennoch verschieden. Ein Auslöser beobachtet im System ablaufende Ereignisse, wohingegen Bedingungen nur den aktuellen Systemzustand betrachten. Ein Auslöser kann beobachten, dass ein Schalter aktiviert wird. Eine Bedingung kann lediglich sehen, ob ein Schalter aktuell an oder aus ist.\n\n[Erfahre mehr über Bedingungen.](https:\/\/home-assistant.io\/docs\/scripts\/conditions\/)",
+ "introduction": "Bedingungen sind ein optionaler Bestandteil bei automatisierten Abläufen und können das Ausführen einer Aktion verhindern. Bedingungen sind Auslösern ähnlich, aber dennoch verschieden. Ein Auslöser beobachtet im System ablaufende Ereignisse, wohingegen Bedingungen nur den aktuellen Systemzustand betrachten. Ein Auslöser kann beobachten, ob ein Schalter aktiviert wird. Eine Bedingung kann lediglich sehen, ob ein Schalter aktuell an oder aus ist.\n\n[Erfahre mehr über Bedingungen.](https:\/\/home-assistant.io\/docs\/scripts\/conditions\/)",
"add": "Bedingung hinzufügen",
"duplicate": "Duplizieren",
"delete": "Löschen",
@@ -717,9 +721,9 @@
"lovelace": {
"cards": {
"shopping-list": {
- "checked_items": "Markierte Items",
+ "checked_items": "Markierte Artikel",
"clear_items": "Markierte Elemente löschen",
- "add_item": "Item hinzufügen"
+ "add_item": "Artikel hinzufügen"
}
},
"editor": {
@@ -735,6 +739,7 @@
"migrate": {
"header": "Konfiguration inkompatibel",
"para_no_id": "Dieses Element hat keine ID. Bitte füge diesem Element eine ID in 'ui-lovelace.yaml' hinzu.",
+ "para_migrate": "Home Assistant kann für alle Ihre Karten und Ansichten die IDs automatisch generieren, wenn Sie den \"Konfiguration migrieren\"-Button klicken.",
"migrate": "Konfiguration migrieren"
},
"header": "Benutzeroberfläche bearbeiten",
@@ -746,6 +751,9 @@
"delete": "Ansicht löschen"
},
"save_config": {
+ "header": "Lovelace Userinterface selbst verwalten",
+ "para": "Standardmäßig verwaltet Home Assistant Ihre Benutzeroberfläche und aktualisiert sie, sobald neue Entitäten oder Lovelace-Komponenten verfügbar sind. Wenn Sie die Verwaltung selbst übernehmen, nehmen wir für Sie keine Änderungen mehr vor.",
+ "para_sure": "Bist du dir sicher, dass du die Benutzeroberfläche selbst verwalten möchtest?",
"cancel": "Abbrechen",
"save": "Kontrolle übernehmen"
}
diff --git a/translations/el.json b/translations/el.json
index cab81d65c9..56810e8123 100644
--- a/translations/el.json
+++ b/translations/el.json
@@ -222,8 +222,67 @@
"config": {
"zwave": {
"caption": "Z-Wave"
+ },
+ "automation": {
+ "editor": {
+ "triggers": {
+ "type": {
+ "state": {
+ "for": "Για"
+ }
+ }
+ }
+ }
+ }
+ },
+ "lovelace": {
+ "editor": {
+ "edit_card": {
+ "save": "Αποθήκευση",
+ "edit": "Επεξεργασία",
+ "delete": "Διαγραφή"
+ },
+ "header": "Επεξεργασία UI",
+ "configure_ui": "Ρύθμιση UI",
+ "edit_view": {
+ "header": "Ρυθμίσεις",
+ "add": "Προσθήκη προβολής",
+ "edit": "Επεξεργασία προβολής",
+ "delete": "Διαγραφή προβολής"
+ }
}
}
+ },
+ "card": {
+ "cover": {
+ "position": "Θέση",
+ "tilt_position": "Θέση ανάκλισης"
+ },
+ "fan": {
+ "speed": "Ταχύτητα",
+ "oscillate": "Περιστροφή",
+ "direction": "Κατεύθυνση"
+ },
+ "light": {
+ "brightness": "Φωτεινότητα",
+ "color_temperature": "Θερμοκρασία χρώματος",
+ "white_value": "Τιμή λευκού",
+ "effect": "Εφέ"
+ },
+ "media_player": {
+ "text_to_speak": "Κείμενο για εκφώνηση"
+ },
+ "climate": {
+ "currently": "Αυτή τη στιγμή",
+ "target_temperature": "Επιθυμητή θερμοκρασία",
+ "target_humidity": "Επιθυμητή υγρασία",
+ "operation": "Λειτουργία",
+ "fan_mode": "Λειτουργία ανεμιστήρα",
+ "away_mode": "Λειτουργία εκτός σπιτιού"
+ }
+ },
+ "common": {
+ "save": "Αποθήκευση"
}
},
"domain": {
diff --git a/translations/en.json b/translations/en.json
index b5d0c50e59..935b669e1f 100644
--- a/translations/en.json
+++ b/translations/en.json
@@ -423,6 +423,10 @@
"event": "Event:",
"enter": "Enter",
"leave": "Leave"
+ },
+ "webhook": {
+ "label": "Webhook",
+ "webhook_id": "Webhook ID"
}
}
},
diff --git a/translations/es.json b/translations/es.json
index 07e1f98cac..a9b491062a 100644
--- a/translations/es.json
+++ b/translations/es.json
@@ -423,6 +423,10 @@
"event": "Evento:",
"enter": "Entrar",
"leave": "Salir"
+ },
+ "webhook": {
+ "label": "Webhook",
+ "webhook_id": "ID de webhook"
}
}
},
@@ -726,13 +730,32 @@
"edit_card": {
"header": "Configuración de la tarjeta",
"save": "Guardar",
- "toggle_editor": "Alternar editor"
+ "toggle_editor": "Alternar editor",
+ "pick_card": "Escoge la tarjeta que desea agregar.",
+ "add": "Añadir tarjeta",
+ "edit": "Editar",
+ "delete": "Eliminar"
},
"migrate": {
"header": "Configuración incompatible",
"para_no_id": "Este elemento no tiene un ID. Por favor agregue uno este elemento en 'ui-lovelace.yaml'.",
"para_migrate": "Home Assistant puede agregar ID a todas sus tarjetas y vistas automáticamente por usted presionando el botón 'Migrar configuración'.",
"migrate": "Migrar configuración"
+ },
+ "header": "Editar la interfaz de usuario",
+ "configure_ui": "Configurar la interfaz de usuario",
+ "edit_view": {
+ "header": "Ver configuración",
+ "add": "Añadir vista",
+ "edit": "Editar vista",
+ "delete": "Borrar vista"
+ },
+ "save_config": {
+ "header": "Tomar el control de la interfaz de usuario de Lovelace",
+ "para": "Por defecto, Home Assistant mantendrá su interfaz de usuario y la actualizará cuando haya nuevas entidades o componentes de Lovelace disponibles. Si usted toma el control, ya no haremos cambios automáticamente para usted.",
+ "para_sure": "¿Está seguro de que desea tomar el control de su interfaz de usuario?",
+ "cancel": "No importa",
+ "save": "Tomar el control"
}
}
}
@@ -743,7 +766,8 @@
},
"common": {
"loading": "Cargando",
- "cancel": "Cancelar"
+ "cancel": "Cancelar",
+ "save": "Guardar"
},
"duration": {
"day": "{count} {count, plural,\none {día}\nother {días}\n}",
diff --git a/translations/fi.json b/translations/fi.json
index a1c5304b1c..2d8395b35c 100644
--- a/translations/fi.json
+++ b/translations/fi.json
@@ -719,13 +719,25 @@
"edit_card": {
"header": "Kortti-asetukset",
"save": "Tallenna",
- "toggle_editor": "Vaihda editori"
+ "toggle_editor": "Vaihda editori",
+ "pick_card": "Valitse kortti jonka haluat lisätä",
+ "add": "Lisää kortti",
+ "edit": "Muokkaa",
+ "delete": "Poista"
},
"migrate": {
"header": "Epäkelvot asetukset",
"para_no_id": "Elementillä ei ole ID. Lisää ID elementille 'ui-lovelace.yaml'-tiedostossa.",
"para_migrate": "Home Assistant voi lisätä ID:t kaikkiin kortteihisi ja näkymiin automaattisesti painamalla 'Tuo vanhat asetukset'-nappia.",
"migrate": "Tuo vanhat asetukset"
+ },
+ "header": "Muokkaa käyttöliittymää",
+ "configure_ui": "Määrittele käyttöliittymä",
+ "edit_view": {
+ "header": "Näytä asetukset",
+ "add": "Lisää näkymä",
+ "edit": "Muokkaa näkymää",
+ "delete": "Poista näkymä"
}
}
}
@@ -736,7 +748,8 @@
},
"common": {
"loading": "Ladataan",
- "cancel": "Peruuta"
+ "cancel": "Peruuta",
+ "save": "Tallenna"
},
"duration": {
"day": "{count} {count, plural,\n one {päivä}\n other {päivää}\n}",
@@ -755,7 +768,7 @@
"not_available": "Kuvaa ei saatavilla"
},
"persistent_notification": {
- "dismiss": "Peruuta"
+ "dismiss": "Hylkää"
},
"scene": {
"activate": "Aktivoi"
diff --git a/translations/fr.json b/translations/fr.json
index 2d1782d341..637d24b272 100644
--- a/translations/fr.json
+++ b/translations/fr.json
@@ -70,7 +70,7 @@
},
"vibration": {
"off": "RAS",
- "on": "Détecté"
+ "on": "Détectée"
},
"opening": {
"off": "Fermé",
@@ -275,8 +275,8 @@
"alarm_control_panel": {
"armed": "Activé",
"disarmed": "Désactivé",
- "armed_home": "Activée (présent)",
- "armed_away": "Armée (absent)",
+ "armed_home": "Armé",
+ "armed_away": "Armé",
"armed_night": "Activé",
"pending": "En cours",
"arming": "Activer",
@@ -301,7 +301,7 @@
"period": "Période"
},
"logbook": {
- "showing_entries": "Afficher les entrées pour"
+ "showing_entries": "Afficher les entrées pour le"
},
"mailbox": {
"empty": "Vous n'avez aucun message",
@@ -423,6 +423,10 @@
"event": "Événement :",
"enter": "Entre",
"leave": "Quitter"
+ },
+ "webhook": {
+ "label": "Webhook",
+ "webhook_id": "ID Webhook"
}
}
},
@@ -432,7 +436,7 @@
"add": "Ajouter une condition",
"duplicate": "Dupliquer",
"delete": "Effacement",
- "delete_confirm": "Voulez-vous effacer ?",
+ "delete_confirm": "Voulez-vous vraiment effacer ?",
"unsupported_condition": "Condition non supportée : {condition}",
"type_select": "Type de condition",
"type": {
@@ -476,7 +480,7 @@
"introduction": "Les actions sont ce que Home Assistant fera quand une automatisation est déclenchée.\n\n[En apprendre plus sur les actions.](https:\/\/home-assistant.io\/docs\/automation\/action\/)",
"add": "Ajouter une action",
"duplicate": "Dupliquer",
- "delete": "Effacer",
+ "delete": "Supprimer",
"delete_confirm": "Voulez-vous vraiment effacer ?",
"unsupported_action": "Action non supportée : {action}",
"type_select": "Type d'action",
@@ -529,7 +533,7 @@
}
},
"cloud": {
- "caption": "Home Assistant Cloud",
+ "caption": "Nuage Home Assistant",
"description_login": "Connecté en tant que {email}",
"description_not_login": "Pas connecté"
},
@@ -718,19 +722,40 @@
"cards": {
"shopping-list": {
"checked_items": "Éléments cochés",
+ "clear_items": "Effacer éléments cochés",
"add_item": "Ajouter un élément"
}
},
"editor": {
"edit_card": {
+ "header": "Configuration de la carte",
"save": "Enregistrer",
- "toggle_editor": "Activer\/désactiver l’éditeur"
+ "toggle_editor": "Activer\/désactiver l’éditeur",
+ "pick_card": "Choisissez l'automatisation à ajouter",
+ "add": "Ajouter une action",
+ "edit": "Modifier",
+ "delete": "Supprimer"
},
"migrate": {
"header": "Configuration incompatible",
"para_no_id": "Cet élément n'a pas d'identifiant. Veuillez ajouter un identifiant à cet élément dans 'ui-lovelace.yaml'.",
"para_migrate": "Home Assistant peut ajouter automatiquement des identifiants à toutes vos cartes et vues en appuyant sur le bouton «Migrer la configuration».",
"migrate": "Migrer la configuration"
+ },
+ "header": "Modifier l'interface utilisateur",
+ "configure_ui": "Configurer l'interface utilisateur",
+ "edit_view": {
+ "header": "Voir la configuration",
+ "add": "Ajouter la vue\n",
+ "edit": "Modifier la vue",
+ "delete": "Supprimer la vue"
+ },
+ "save_config": {
+ "header": "Prenez le controle de Lovelace UI",
+ "para": "Par défaut, Home Assistant maintient votre interface utilisateur et la met à jour lorsque de nouvelles entités ou de nouveaux composants Lovelace sont disponibles. Si vous prenez le contrôle, nous ne ferons plus les changements automatiquement pour vous.",
+ "para_sure": "Êtes-vous sûr de vouloir prendre le controle de l'interface utilisateur?",
+ "cancel": "Oublie ce que j'ai dit, c'est pas grave.",
+ "save": "Prenez le contrôle"
}
}
}
@@ -741,7 +766,8 @@
},
"common": {
"loading": "Chargement",
- "cancel": "Annuler"
+ "cancel": "Annuler",
+ "save": "Enregistrer"
},
"duration": {
"day": "{count} {count, plural,\none {jour}\nother {jours}\n}",
@@ -801,7 +827,9 @@
"clear_code": "Effacer",
"disarm": "Désarmer",
"arm_home": "Armer (domicile)",
- "arm_away": "Armer (absent)"
+ "arm_away": "Armer (absent)",
+ "arm_night": "Armer nuit",
+ "armed_custom_bypass": "Bypass personnalisé"
},
"automation": {
"last_triggered": "Dernier déclenchement",
diff --git a/translations/he.json b/translations/he.json
index cb3d396540..233496a4bd 100644
--- a/translations/he.json
+++ b/translations/he.json
@@ -423,6 +423,10 @@
"event": "אירוע:",
"enter": "כניסה",
"leave": "יציאה"
+ },
+ "webhook": {
+ "label": "Webhook",
+ "webhook_id": "Webhook ID"
}
}
},
@@ -557,7 +561,7 @@
"profile": {
"push_notifications": {
"header": "הודעות דחיפה",
- "description": "שלח התראות למכשיר זה",
+ "description": "שלח התראות למכשיר זה.",
"error_load_platform": "הגדר את notify.html5.",
"error_use_https": "נדרש SSL מופעל עבור הממשק.",
"push_notifications": "הודעות דחיפה",
diff --git a/translations/hr.json b/translations/hr.json
index ff5c500471..747b55ad88 100644
--- a/translations/hr.json
+++ b/translations/hr.json
@@ -251,11 +251,13 @@
},
"vacuum": {
"cleaning": "Čišćenje",
+ "docked": "Usidreni",
"error": "Greška",
"idle": "Besposlen",
"off": "Ugašeno",
"on": "Upaljeno",
- "paused": "Pauzirano"
+ "paused": "Pauzirano",
+ "returning": "Povratak na dok"
}
},
"state_badge": {
@@ -345,12 +347,24 @@
"unsaved_confirm": "Imate nespremljene izmjene. Jeste li sigurni da želite napustiti?",
"alias": "Ime",
"triggers": {
+ "header": "Okidači",
"introduction": "Okidači su ono što pokreće obradu pravila o automatizaciji. Moguće je odrediti više okidača za isto pravilo. Kada pokrenete okidač, Home Assistant provjerit će uvjete, ako ih ima i pozvati akciju. \n\n [Saznajte više o pokretačima.] (Https:\/\/home-assistant.io\/docs\/automation\/trigger\/)",
+ "add": "Dodaj okidač",
+ "duplicate": "Udvostruči",
"delete": "Obriši",
+ "delete_confirm": "Jeste li sigurni dai želite izbrisati?",
"unsupported_platform": "Nepodržana platforma: {platform}",
+ "type_select": "Tip okidača",
"type": {
+ "event": {
+ "label": "Događaj:",
+ "event_type": "Vrsta događaja",
+ "event_data": "Podaci o događaju"
+ },
"state": {
+ "label": "Stanje",
"from": "Od",
+ "to": "Do",
"for": "Za"
},
"homeassistant": {
@@ -361,7 +375,8 @@
},
"mqtt": {
"label": "MQTT",
- "topic": "Tema"
+ "topic": "Tema",
+ "payload": "Opterećenje (opcionalno)"
},
"numeric_state": {
"label": "Numeričko stanje",
@@ -391,6 +406,10 @@
"event": "Event:",
"enter": "Unesite",
"leave": "Napustiti"
+ },
+ "webhook": {
+ "label": "Webhook",
+ "webhook_id": "Webhook ID"
}
}
},
@@ -474,8 +493,12 @@
}
}
},
+ "script": {
+ "description": "Stvaranje i uređivanje skripti"
+ },
"zwave": {
- "caption": "Z-Wave"
+ "caption": "Z-Wave",
+ "description": "Upravljajte mrežom Z-Wave"
},
"users": {
"caption": "Korisnici",
@@ -632,7 +655,8 @@
"mfa": {
"data": {
"code": "Kôd autentifikacije s dva faktora"
- }
+ },
+ "description": "Otvori **{mfa_module_name}** na svojem uređaju kao bi vidio kod dvofaktorske autentikacije i provjerio svoj identitet:"
}
},
"error": {
@@ -688,13 +712,32 @@
"edit_card": {
"header": "Konfiguracija Kartice ",
"save": "Spremi",
- "toggle_editor": "Uključi uređivač"
+ "toggle_editor": "Uključi uređivač",
+ "pick_card": "Odaberite karticu koju želite dodati.",
+ "add": "Dodaj karticu",
+ "edit": "Uredi",
+ "delete": "Izbrisati"
},
"migrate": {
"header": "Konfiguracija nije kompatibilna",
"para_no_id": "Ovaj element nema ID. Dodajte ID ovom elementu u 'ui-lovelace.yaml'.",
"para_migrate": "Home Assistant može dodati ID-ove na sve vaše kartice i pogleda automatski za vas pritiskom na gumb 'Migriraj konfiguriranje'.",
"migrate": "Migriraj konfiguraciju"
+ },
+ "header": "Uredi UI",
+ "configure_ui": "Konfiguriraj UI",
+ "edit_view": {
+ "header": "Pogledaj konfiguraciju",
+ "add": "Dodaj prikaz",
+ "edit": "Uredi prikaz",
+ "delete": "Izbriši prikaz"
+ },
+ "save_config": {
+ "header": "Preuzmite kontrolu nad Lovelace UI",
+ "para": "Home Assistant će prema zadanim postavkama održavati vaše korisničko sučelje, ažurirati ga kada novi entiteti ili Lovelace komponente postanu dostupni. Ako preuzmete kontrolu, više nećemo automatski vršiti izmjene za vas.",
+ "para_sure": "Jeste li sigurni da želite preuzeti kontrolu nad svojim korisničkim sučeljem?",
+ "cancel": "Nema veze",
+ "save": "Preuzmi kontrolu"
}
}
}
@@ -705,7 +748,8 @@
},
"common": {
"loading": "Učitavam",
- "cancel": "Otkazati"
+ "cancel": "Otkazati",
+ "save": "Spremi"
},
"duration": {
"day": "{count} {count, plural,\n one {dan}\n other {dani}\n}",
@@ -736,21 +780,38 @@
"wind_speed": "Brzina vjetra"
},
"cardinal_direction": {
+ "e": "I",
+ "ene": "ISI",
"ese": "ESE",
"n": "N",
"ne": "NE",
"nne": "NNE",
- "nw": "NW"
- }
+ "nw": "NW",
+ "nnw": "SSZ",
+ "s": "J",
+ "se": "JI",
+ "sse": "JJI",
+ "ssw": "JJZ",
+ "sw": "JZ",
+ "w": "Z",
+ "wnw": "ZSZ",
+ "wsw": "ZJZ"
+ },
+ "forecast": "Prognoza"
},
"alarm_control_panel": {
"code": "Kod",
+ "clear_code": "Vedro",
"disarm": "Deaktiviraj",
"arm_home": "Aktiviran doma",
"arm_away": "Aktiviran odsutno",
"arm_night": "Aktiviran nočni",
"armed_custom_bypass": "Prilagođena obilaznica"
},
+ "automation": {
+ "last_triggered": "Zadnje aktivirano",
+ "trigger": "Okidač"
+ },
"fan": {
"speed": "Brzina",
"oscillate": "Oscilirati",
@@ -784,6 +845,8 @@
},
"vacuum": {
"actions": {
+ "resume_cleaning": "Nastavi čišćenje",
+ "return_to_base": "Povratak na dok",
"start_cleaning": "Započnite čišćenje",
"turn_on": "Uključiti",
"turn_off": "Isključiti"
@@ -803,8 +866,14 @@
"entity": "Entitet"
}
},
+ "service-picker": {
+ "service": "Usluga"
+ },
"relative_time": {
- "past": "{vrijeme} prije"
+ "past": "{vrijeme} prije",
+ "duration": {
+ "week": "{count} {count, plural,\n jedan {week}\n ostali {weeks}\n}"
+ }
},
"history_charts": {
"loading_history": "Učitavanje povijesti stanja ...",
@@ -814,7 +883,8 @@
"dialogs": {
"more_info_settings": {
"save": "Spremi",
- "name": "Ime"
+ "name": "Ime",
+ "entity_id": "ID entiteta"
}
},
"auth_store": {
@@ -863,7 +933,8 @@
"switch": "Prekidač",
"updater": "Ažuriranje",
"weblink": "WebLink",
- "zwave": "Z-Wave"
+ "zwave": "Z-Wave",
+ "vacuum": "Vakuum"
},
"attribute": {
"weather": {
diff --git a/translations/hu.json b/translations/hu.json
index ba41a66451..d135760d3f 100644
--- a/translations/hu.json
+++ b/translations/hu.json
@@ -292,7 +292,7 @@
"ui": {
"panel": {
"shopping-list": {
- "clear_completed": "Kijelöltek törlése",
+ "clear_completed": "Bejelöltek törlése",
"add_item": "Tétel hozzáadása",
"microphone_tip": "Koppints a jobb felső sarokban található mikrofonra, és mondd ki: \"Add candy to my shopping list\""
},
@@ -423,6 +423,10 @@
"event": "Esemény:",
"enter": "Érkezés",
"leave": "Távozás"
+ },
+ "webhook": {
+ "label": "Webhook",
+ "webhook_id": "Webhook ID"
}
}
},
@@ -546,8 +550,8 @@
"no_device": "Entitások eszközök nélkül",
"delete_confirm": "Biztosan törölni szeretnéd ezt az integrációt?",
"restart_confirm": "Indítsd újra a Home Assistant-ot az integráció törlésének befejezéséhez",
- "manuf": "Gyártó: {manufacturer}",
- "hub": "Csatlakoztatva",
+ "manuf": "{manufacturer} által",
+ "hub": "Kapcsolódva",
"firmware": "Firmware: {version}",
"device_unavailable": "eszköz nem érhető el",
"entity_unavailable": "entitás nem érhető el"
@@ -620,7 +624,7 @@
"mfa_setup": {
"title_aborted": "Megszakítva",
"title_success": "Siker!",
- "step_done": "Beállítás kész {step}",
+ "step_done": "{step} beállítás elvégezve",
"close": "Bezárás",
"submit": "Küldés"
}
@@ -699,7 +703,7 @@
}
},
"page-onboarding": {
- "intro": "Készen állsz arra, hogy felébreszd az otthonod, visszaszerezed a magánéleted és csatlakozz egy világhálós közösséghez?",
+ "intro": "Készen állsz arra, hogy felébreszd az otthonod, visszaszerezd a magánéleted és csatlakozz egy világhálós közösséghez?",
"user": {
"intro": "Kezdjük a felhasználói fiók létrehozásával.",
"required_field": "Szükséges",
@@ -717,8 +721,8 @@
"lovelace": {
"cards": {
"shopping-list": {
- "checked_items": "Kijelölt tételek",
- "clear_items": "Kijelölt tételek törlése",
+ "checked_items": "Bejelölt tételek",
+ "clear_items": "Bejelölt tételek törlése",
"add_item": "Tétel hozzáadása"
}
},
@@ -734,8 +738,8 @@
},
"migrate": {
"header": "Inkompatibilis Konfiguráció",
- "para_no_id": "Ez az elem nem rendelkezik azonosítóval. Kérlek, adj hozzá egyet az 'ui-lovelace.yaml' fájlban!",
- "para_migrate": "A Home Assistant a 'Konfiguráció áttelepítése' gomb megnyomásával az összes kártyához és nézethez automatikusan létre tud hozni azonosítókat.",
+ "para_no_id": "Ez az elem nem rendelkezik ID-val. Kérlek, adj hozzá egyet az 'ui-lovelace.yaml' fájlban!",
+ "para_migrate": "A Home Assistant a 'Konfiguráció áttelepítése' gomb megnyomásával az összes kártyához és nézethez automatikusan létre tud hozni ID-kat.",
"migrate": "Konfiguráció áttelepítése"
},
"header": "Felhasználói felület szerkesztése",
diff --git a/translations/it.json b/translations/it.json
index 60fc3a16db..d733a1b540 100644
--- a/translations/it.json
+++ b/translations/it.json
@@ -274,7 +274,7 @@
},
"alarm_control_panel": {
"armed": "Attivo",
- "disarmed": "Disattivo",
+ "disarmed": "Disattiva",
"armed_home": "Attivo",
"armed_away": "Attivo",
"armed_night": "Attivo",
@@ -368,7 +368,7 @@
"introduction": "I trigger sono ciò che avvia l'elaborazione di una regola di automazione. È possibile specificare più trigger per la stessa regola. Una volta avviato il trigger, Home Assistant convaliderà le condizioni, se presenti, e chiamerà l'azione. \n\n [Ulteriori informazioni sui trigger.](Https:\/\/home-assistant.io\/docs\/automation\/trigger\/)",
"add": "Aggiungi trigger",
"duplicate": "Duplica",
- "delete": "Cancella",
+ "delete": "Elimina",
"delete_confirm": "Sicuro di voler eliminare?",
"unsupported_platform": "Piattaforma non supportata: {platform}",
"type_select": "Tipo di trigger",
@@ -423,6 +423,10 @@
"event": "Evento",
"enter": "Ingresso",
"leave": "Uscita"
+ },
+ "webhook": {
+ "label": "Webhook",
+ "webhook_id": "ID Webhook"
}
}
},
@@ -431,7 +435,7 @@
"introduction": "Le condizioni sono una parte facoltativa di una regola di automazione e possono essere utilizzate per impedire che un'azione si verifichi quando viene attivata. Le condizioni sembrano molto simili ai trigger, ma sono molto diverse. Un trigger analizzerà gli eventi che si verificano nel sistema mentre una condizione analizza solo l'aspetto del sistema in questo momento. Un trigger può osservare che un interruttore è in fase di accensione. Una condizione può vedere solo se un interruttore è attivo o meno. \n\n [Ulteriori informazioni sulle condizioni.](Https:\/\/home-assistant.io\/docs\/scripts\/conditions\/)",
"add": "Aggiungi condizione",
"duplicate": "Duplica",
- "delete": "Cancella",
+ "delete": "Elimina",
"delete_confirm": "Sicuro di voler eliminare?",
"unsupported_condition": "Condizione non supportata: {condition}",
"type_select": "Tipo di condizione",
@@ -476,7 +480,7 @@
"introduction": "Le azioni sono ciò che Home Assistant farà quando un trigger attiva un automazione. \n\n [Ulteriori informazioni sulle azioni.](Https:\/\/home-assistant.io\/docs\/automation\/action\/)",
"add": "Aggiungi azione",
"duplicate": "Duplica",
- "delete": "Cancella",
+ "delete": "Elimina",
"delete_confirm": "Sicuro di voler eliminare?",
"unsupported_action": "Azione non supportata: {action}",
"type_select": "Tipo di azione",
@@ -500,7 +504,7 @@
"event": {
"label": "Scatena Evento",
"event": "Evento:",
- "service_data": "Dato servizio"
+ "service_data": "Dati servizio"
}
}
}
@@ -531,7 +535,7 @@
"cloud": {
"caption": "Home Assistant Cloud",
"description_login": "Connesso come {email}",
- "description_not_login": "Non loggato"
+ "description_not_login": "Accesso non effettuato"
},
"integrations": {
"caption": "integrazioni",
@@ -547,7 +551,7 @@
"delete_confirm": "Sei sicuro di voler eliminare questa integrazione?",
"restart_confirm": "Riavvia Home Assistant per terminare la rimozione di questa integrazione",
"manuf": "da {manufacturer}",
- "hub": "Connesso via",
+ "hub": "Connesso tramite",
"firmware": "Firmware: {version}",
"device_unavailable": "dispositivo non disponibile",
"entity_unavailable": "entità non disponibile"
@@ -596,7 +600,7 @@
"create_failed": "Impossibile creare il token di accesso.",
"prompt_name": "Nome?",
"prompt_copy_token": "Copia il tuo token di accesso. Non verrà più mostrato.",
- "empty_state": "Non hai ancora token di accesso di lunga durata.",
+ "empty_state": "Non hai ancora un token di accesso di lunga durata.",
"last_used": "Utilizzato l'ultima volta il {date} da {location}",
"not_used": "Non è mai stato usato"
},
@@ -726,13 +730,32 @@
"edit_card": {
"header": "Configurazione della scheda",
"save": "Salva",
- "toggle_editor": "Attiva \/ disattiva l'editor"
+ "toggle_editor": "Attiva \/ disattiva l'editor",
+ "pick_card": "Scegliere la scheda che si desidera aggiungere.",
+ "add": "Aggiungi scheda",
+ "edit": "Modifica",
+ "delete": "Elimina"
},
"migrate": {
"header": "Configurazione incompatibile",
"para_no_id": "Questo elemento non ha un ID. Aggiungi un ID a questo elemento in 'ui-lovelace.yaml'.",
"para_migrate": "Home Assistant può aggiungere automaticamente gli ID a tutte le tue schede e visualizzazioni automaticamente premendo il pulsante \"Eporta configurazione\".",
"migrate": "Esporta configurazione"
+ },
+ "header": "Modifica dell'interfaccia utente",
+ "configure_ui": "Configurare l'interfaccia utente",
+ "edit_view": {
+ "header": "Visualizza configurazione",
+ "add": "Aggiungi vista",
+ "edit": "Modifica vista",
+ "delete": "Cancella vista"
+ },
+ "save_config": {
+ "header": "Prendi il controllo della tua interfaccia utente di Lovelace",
+ "para": "Per impostazione predefinita, Home Assistant manterrà l'interfaccia utente, aggiornandola quando nuove entità o componenti Lovelace diventano disponibili. Se prendi il controllo non effettueremo più automaticamente le modifiche per te.",
+ "para_sure": "Sei sicuro di voler prendere il controllo della tua interfaccia utente?",
+ "cancel": "Rinuncia",
+ "save": "Prendere il controllo"
}
}
}
@@ -743,7 +766,8 @@
},
"common": {
"loading": "Caricamento",
- "cancel": "Annulla"
+ "cancel": "Annulla",
+ "save": "Salva"
},
"duration": {
"day": "{count} {count, plural,\none {giorno}\nother {giorni}\n}",
@@ -801,8 +825,8 @@
"alarm_control_panel": {
"code": "Codice",
"clear_code": "Canc",
- "disarm": "Disattivato",
- "arm_home": "Attivo in casa",
+ "disarm": "Disattiva",
+ "arm_home": "Attiva In casa",
"arm_away": "Attiva Fuori Casa",
"arm_night": "Attiva Notte",
"armed_custom_bypass": "Attiva con bypass"
diff --git a/translations/ko.json b/translations/ko.json
index 37a9ec5b85..6326175fce 100644
--- a/translations/ko.json
+++ b/translations/ko.json
@@ -423,6 +423,10 @@
"event": "이벤트:",
"enter": "입장",
"leave": "퇴장"
+ },
+ "webhook": {
+ "label": "Webhook",
+ "webhook_id": "Webhook ID"
}
}
},
@@ -751,7 +755,7 @@
"para": "기본적으로 Home Assistant 는 사용자 인터페이스를 유지 관리하고, 사용할 수 있는 새로운 구성요소 또는 Lovelace 구성요소가 있을 때 업데이트를 합니다. 사용자가 직접 관리하는 경우 Home Assistant 는 더 이상 자동으로 변경하지 않습니다.",
"para_sure": "사용자 인터페이스를 직접 관리하시겠습니까?",
"cancel": "아닙니다",
- "save": "직접 관리할께요"
+ "save": "직접 관리할게요"
}
}
}
diff --git a/translations/lb.json b/translations/lb.json
index 98c7829ee3..fe13994b79 100644
--- a/translations/lb.json
+++ b/translations/lb.json
@@ -275,9 +275,9 @@
"alarm_control_panel": {
"armed": "Aktivéiert",
"disarmed": "Desaktivéieren",
- "armed_home": "Aktivéiert",
- "armed_away": "Aktivéiert",
- "armed_night": "Aktivéiert",
+ "armed_home": "Uzbrojony",
+ "armed_away": "Uzbrojony",
+ "armed_night": "Uzbrojony",
"pending": "Ustoend",
"arming": "Aktivéieren",
"disarming": "Desaktivéieren",
@@ -317,7 +317,7 @@
"description": "Konfiguratioun validéieren an de Server kontrolléieren",
"section": {
"core": {
- "header": "Konfiguratioun an Server Kontroll",
+ "header": "Konfiguracja i konktrola serwera",
"introduction": "D'Ännere vun der Konfiguratioun kann e lästege Prozess sinn. Mir wëssen dat. Dës Sektioun probéiert fir Äert Liewen e bësse méi einfach ze maachen.",
"validation": {
"heading": "Validatioun vun der Konfiguratioun",
@@ -419,10 +419,14 @@
"zone": {
"label": "Zone",
"entity": "Entitéit mam Standuert",
- "zone": "Zon",
+ "zone": "Strefa",
"event": "Evenement:",
"enter": "Eran",
"leave": "Verloossen"
+ },
+ "webhook": {
+ "label": "Webhook",
+ "webhook_id": "Webhook ID"
}
}
},
@@ -441,7 +445,7 @@
"state": "Zoustand"
},
"numeric_state": {
- "label": "Numereschen Zoustand",
+ "label": "Stan (numeryczny)",
"above": "Iwwert",
"below": "Ënnert",
"value_template": "Wäerte Modell (optional)"
@@ -465,15 +469,15 @@
"before": "Virdrun"
},
"zone": {
- "label": "Zon",
+ "label": "Strefa",
"entity": "Entitéit mam Standuert",
- "zone": "Zon"
+ "zone": "Strefa"
}
}
},
"actions": {
"header": "Aktiounen",
- "introduction": "Aktioune déi den HomeAssistant ausféiert wann den Automatisme ausgeléist gouf.\n[Léier méi iwwert Aktioune.](https:\/\/home-assistant.io\/docs\/automation\/action\/)",
+ "introduction": "Aktioune déi den Home Assistant ausféiert wann den Automatisme ausgeléist gouf.\n[Léier méi iwwert Aktioune.](https:\/\/home-assistant.io\/docs\/automation\/action\/)",
"add": "Aktioun dobäisetzen",
"duplicate": "Replikéiere",
"delete": "Läschen",
@@ -487,7 +491,7 @@
},
"delay": {
"label": "Delai",
- "delay": "Delai"
+ "delay": "Opóźnienie"
},
"wait_template": {
"label": "Waart",
@@ -581,7 +585,7 @@
"created_at": "Erstallt um {date}",
"confirm_delete": "Sécher fir den Erneierungs Token fir {name} ze läsche?",
"delete_failed": "Fehler beim läschen vum Erneierungs Token",
- "last_used": "Läscht benotz um {date} vun {location}",
+ "last_used": "Fir d'Läscht benotzt um {date} vun {location}",
"not_used": "Nach nie benotzt ginn",
"current_token_tooltip": "Fehler beim läschen vum aktuellen Erneierungs Token"
},
@@ -748,6 +752,7 @@
},
"save_config": {
"header": "Kontroll iwwert Loveloce UI iwwerhuelen",
+ "para": "Standardméisseg verwalt Home Assistant de Benotzer Interface an aktualiséiert en soubal nei Entitéiten oder Lovelace-Komponenten disponibel sinn. Wann dir d'Kontrolle iwwerhuelt, kënne mir keng automatesch Ännerung méi fir iech maachen.",
"para_sure": "Sécher fir d'Kontrolle iwwert de Benotzer Interface z'iwwerhuelen?",
"cancel": "Vergiess et",
"save": "Kontroll iwwerhuelen"
@@ -870,7 +875,7 @@
"actions": {
"resume_cleaning": "Fuer mam botzen weider",
"return_to_base": "Zeréck zur Statioun kommen",
- "start_cleaning": "Fänkt mam botzen un",
+ "start_cleaning": "Fänk mam botzen un",
"turn_on": "Uschalten",
"turn_off": "Ausschalten"
}
@@ -910,8 +915,8 @@
}
},
"notification_toast": {
- "entity_turned_on": "{entity} gouf ausgeschalt",
- "entity_turned_off": "{entity} gouf ugeschalt",
+ "entity_turned_on": "{entity} gouf ugeschalt",
+ "entity_turned_off": "{entity} gouf ausgeschalt",
"service_called": "Service {service} operuff",
"service_call_failed": "Fehler beim opruffen vun {service}",
"connection_lost": "Verbindung verluer. Verbindung gëtt nees opgebaut..."
diff --git a/translations/lv.json b/translations/lv.json
index 6ac3539796..0a6ee3432e 100644
--- a/translations/lv.json
+++ b/translations/lv.json
@@ -317,7 +317,7 @@
"description": "Veiciet konfigurācijas failu pārbaudi un pārvaldiet serveri",
"section": {
"core": {
- "header": "Konfigurācijas un servera pārvaldība",
+ "header": "Konfigurācijas un Servera pārvaldība",
"introduction": "Izmaiņas konfigurācijā var būt nogurdinošs process. Mēs zinām. Šai sadaļai vajadzētu padarīt dzīvi mazliet vieglāku.",
"validation": {
"heading": "Konfigurācijas pārbaude",
@@ -351,7 +351,7 @@
"caption": "Automatizācija",
"description": "Veidojiet un rediģējiet automatizācijas",
"picker": {
- "header": "Automatizāciju redaktors",
+ "header": "Automatizāciju Redaktors",
"introduction": "Automatizācijas redaktors ļauj jums izveidot un rediģēt automatizācijas. Lūdzu, izlasiet [norādījumus] (https:\/\/home-assistant.io\/docs\/automation\/editor\/), lai pārliecinātos, ka esat pareizi konfigurējis Home Assistant.",
"pick_automation": "Izvēlieties automatizāciju kuru rediģēt",
"no_automations": "Mēs nevarējām atrast rediģējamas automatizācijas",
@@ -423,6 +423,10 @@
"event": "Notikums:",
"enter": "Ieiet",
"leave": "Iziet"
+ },
+ "webhook": {
+ "label": "Webhook",
+ "webhook_id": "Webhook ID"
}
}
},
@@ -713,6 +717,47 @@
"required_fields": "Aizpildiet visus obligātos laukus"
}
}
+ },
+ "lovelace": {
+ "cards": {
+ "shopping-list": {
+ "checked_items": "Atzīmētie vienumi",
+ "clear_items": "Notīrīt atzīmētos vienumus",
+ "add_item": "Pievienot vienumu"
+ }
+ },
+ "editor": {
+ "edit_card": {
+ "header": "Kartes Konfigurācija",
+ "save": "Saglabāt",
+ "toggle_editor": "Pārslēgt Redaktoru",
+ "pick_card": "Izvēlieties karti, kuru vēlaties pievienot.",
+ "add": "Pievienot karti",
+ "edit": "Rediģēt",
+ "delete": "Dzēst"
+ },
+ "migrate": {
+ "header": "Konfigurācija Nesaderīga",
+ "para_no_id": "Šim elementam nav ID. Lūdzu, pievienojiet ID šim elementam 'ui-lovelace.yaml' failā.",
+ "para_migrate": "Home Assistant var automātiski pievienot ID visām kartēm un skatiem, nospiežot pogu \"Pārvietot konfigurāciju\".",
+ "migrate": "Pārvietot konfigurāciju"
+ },
+ "header": "Rediģēt lietotāja interfeisu",
+ "configure_ui": "Konfigurēt lietotāja interfeisu",
+ "edit_view": {
+ "header": "Skatīt konfigurāciju",
+ "add": "Pievienot skatu",
+ "edit": "Rediģēt skatu",
+ "delete": "Dzēst skatu"
+ },
+ "save_config": {
+ "header": "Pārņemt kontroli pār savu Lovelace UI",
+ "para": "Pēc noklusējuma Home Assistant uzturēs jūsu lietotāja interfeisu, atjauninot to, kad būs pieejami jauni objekti vai Lovelace komponenti. Ja jūs uzņematies kontroli, mēs jums vairs neveiksim izmaiņas automātiski jūsu vietā.",
+ "para_sure": "Vai tiešām vēlaties kontrolēt savu lietotāja interfeisu?",
+ "cancel": "Nekas",
+ "save": "Pārņemt kontroli"
+ }
+ }
}
},
"sidebar": {
@@ -721,7 +766,8 @@
},
"common": {
"loading": "Ielāde",
- "cancel": "Atcelt"
+ "cancel": "Atcelt",
+ "save": "Saglabāt"
},
"duration": {
"day": "{count} {count, plural,\none {diena}\nother {dienas}\n}",
@@ -778,10 +824,12 @@
},
"alarm_control_panel": {
"code": "Kods",
- "clear_code": "Dzēst",
+ "clear_code": "Notīrīt",
"disarm": "Atslēgt",
"arm_home": "Pieslēgt mājas",
- "arm_away": "Pieslēgt prombūtni"
+ "arm_away": "Pieslēgt prombūtni",
+ "arm_night": "Pieslēgts uz nakti",
+ "armed_custom_bypass": "Pielāgots apvedceļš"
},
"automation": {
"last_triggered": "Pēdējais izsaukums",
diff --git a/translations/nl.json b/translations/nl.json
index f8c4c8c005..8dbf492b90 100644
--- a/translations/nl.json
+++ b/translations/nl.json
@@ -236,7 +236,7 @@
"ready": "Gereed"
},
"query_stage": {
- "initializing": "Initialiseren ({query_stage})",
+ "initializing": "Voorbereiden ({query_stage})",
"dead": "Onbereikbaar ({query_stage})"
}
},
@@ -248,7 +248,7 @@
"lightning": "Bliksem",
"lightning-rainy": "Bliksem, regenachtig",
"partlycloudy": "Gedeeltelijk bewolkt",
- "pouring": "Gieten",
+ "pouring": "Regen",
"rainy": "Regenachtig",
"snowy": "Sneeuwachtig",
"snowy-rainy": "Sneeuw-, regenachtig",
@@ -276,8 +276,8 @@
"armed": "Actief",
"disarmed": "Uit",
"armed_home": "Actief",
- "armed_away": "Ingeschakeld",
- "armed_night": "Ingeschakeld",
+ "armed_away": "Actief",
+ "armed_night": "Actief",
"pending": "Wacht",
"arming": "Activeren",
"disarming": "Uitschakelen",
@@ -301,7 +301,7 @@
"period": "Periode"
},
"logbook": {
- "showing_entries": "Toon items voor"
+ "showing_entries": "Toont gegevens van"
},
"mailbox": {
"empty": "Je hebt geen berichten",
@@ -423,6 +423,10 @@
"event": "Gebeurtenis:",
"enter": "Betreden",
"leave": "Verlaten"
+ },
+ "webhook": {
+ "label": "Webhook",
+ "webhook_id": "Webhook ID"
}
}
},
@@ -441,7 +445,7 @@
"state": "Staat"
},
"numeric_state": {
- "label": "Numerieke status",
+ "label": "Numerieke staat",
"above": "Boven",
"below": "Onder",
"value_template": "Waardetemplate (optioneel)"
@@ -726,13 +730,32 @@
"edit_card": {
"header": "Kaart configuratie",
"save": "Opslaan",
- "toggle_editor": "Toggle Editor"
+ "toggle_editor": "Toggle Editor",
+ "pick_card": "Kies de kaart die je wilt toevoegen.",
+ "add": "Kaart toevoegen",
+ "edit": "Bewerken",
+ "delete": "Verwijder"
},
"migrate": {
"header": "Configuratie incompatibel",
"para_no_id": "Dit element heeft geen ID. Voeg een ID toe aan dit element in 'ui-lovelace.yaml'.",
"para_migrate": "Home Assistant kan ID's voor al je kaarten en weergaven automatisch voor je toevoegen door op de knop 'Migrate config' te klikken.",
"migrate": "Configuratie migreren"
+ },
+ "header": "Bewerk UI",
+ "configure_ui": "Configureer UI",
+ "edit_view": {
+ "header": "Bekijk de configuratie",
+ "add": "Weergave toevoegen",
+ "edit": "Weergave bewerken",
+ "delete": "Weergave verwijderen"
+ },
+ "save_config": {
+ "header": "Neem de controle over uw Lovelace UI",
+ "para": "Normaal gesproken onderhoudt Home Assistant je gebruikersinterface en update die met nieuwe entiteiten of Lovelace-onderdelen wanneer deze beschikbaar zijn. Als je het beheer overneemt, zullen we niet langer automatisch wijzigingen aanbrengen.",
+ "para_sure": "Weet je zeker dat je de controle wilt over je gebruikersinterface?",
+ "cancel": "Laat maar",
+ "save": "Neem over"
}
}
}
@@ -743,7 +766,8 @@
},
"common": {
"loading": "Bezig met laden",
- "cancel": "Annuleren"
+ "cancel": "Annuleren",
+ "save": "Opslaan"
},
"duration": {
"day": "{count} {count, plural,\none {dag}\nother {dagen}\n}",
diff --git a/translations/nn.json b/translations/nn.json
index e8a893de38..32fa52bc00 100644
--- a/translations/nn.json
+++ b/translations/nn.json
@@ -145,7 +145,7 @@
"high_demand": "Høg etterspurnad",
"heat_pump": "Varmepumpe",
"gas": "Gass",
- "manual": "Håndbok"
+ "manual": "Handbok"
},
"configurator": {
"configure": "Konfigurerer",
@@ -423,6 +423,10 @@
"event": "Hending:",
"enter": "Kjem",
"leave": "Forlet"
+ },
+ "webhook": {
+ "label": "Webhook",
+ "webhook_id": "Webhook ID"
}
}
},
@@ -545,12 +549,12 @@
"no_devices": "Essa integração não possui dispositivos.",
"no_device": "Entidades sem dispositivos.",
"delete_confirm": "Voc^tem certeza que deseja apagar essa integração?",
- "restart_confirm": "Reinicie Home Assistant para finalizar essa integração",
- "manuf": "por {fabricante}",
- "hub": "Conectado via",
- "firmware": "Firmware: {versão}",
- "device_unavailable": "Dispositivo indisponível",
- "entity_unavailable": "Entidade indisponível"
+ "restart_confirm": "Restart Home Assistant for å fjerne denne integrasjonen",
+ "manuf": "av {manufacturer}",
+ "hub": "Tilkopla via",
+ "firmware": "Firmware: {version}",
+ "device_unavailable": "Eininga utilgjengelig",
+ "entity_unavailable": "Oppføringa utilgjengelig"
}
}
},
@@ -600,11 +604,11 @@
"last_used": "Sist brukt den {date} frå {location}",
"not_used": "Har aldri vore brukt"
},
- "current_user": "Você está atualmente conectado como {NomeCompleto}.",
+ "current_user": "Du er for augeblinken logga inn som {fullName}.",
"is_owner": "Du er ein eigar",
- "logout": "Sair",
+ "logout": "Logg ut",
"change_password": {
- "header": "Mudar Senha",
+ "header": "Bytt passord",
"current_password": "Senha Atual",
"new_password": "Nova Senha",
"confirm_new_password": "Confirme Nova Senha",
@@ -713,6 +717,47 @@
"required_fields": "Fyll ut dei nødvendige felta"
}
}
+ },
+ "lovelace": {
+ "cards": {
+ "shopping-list": {
+ "checked_items": "Markerte element",
+ "clear_items": "Fjern dei markerrte elementa",
+ "add_item": "Legg til element"
+ }
+ },
+ "editor": {
+ "edit_card": {
+ "header": "Kortkonfigurasjon",
+ "save": "Lagre",
+ "toggle_editor": "Bytt redigeringsverktøy",
+ "pick_card": "Vel kortet du vil legge til.",
+ "add": "Legg til kort",
+ "edit": "Redigere",
+ "delete": "Slett"
+ },
+ "migrate": {
+ "header": "Konfigurasjonen er ikkje kompatibel",
+ "para_no_id": "Dette elementet har ikkje ein ID. Ver vennleg og legg til ein ID til dette elementet i \"ui-lovelace.yaml\"-fila di.",
+ "para_migrate": "Home assistant kan legge til ID-ar til alle korta og sidene dine automatisk for deg ved å trykke \"Overfør konfigurasjon\"-knappen.",
+ "migrate": "Overfør konfigurasjon"
+ },
+ "header": "Rediger brukargrensesnitt",
+ "configure_ui": "Konfigurer brukargrensesnitt",
+ "edit_view": {
+ "header": "Vis konfigurasjon",
+ "add": "Legg til side",
+ "edit": "Rediger sida",
+ "delete": "Slett sida"
+ },
+ "save_config": {
+ "header": "Ta kontroll over Lovelace-brukargrensesnittet",
+ "para": "Som standard kjem Home Assistant til å vedlikehalde brukargrensesnittet, og oppdatere det når nye oppføringar eller Lovelace-komponentar vert tilgjengelege. Dersom du tek kontroll, vil vi ikkje lenger kunne lage dette til automatisk for deg.",
+ "para_sure": "Er du sikker på at du vil ta kontroll over brukergrensesnittet ditt?",
+ "cancel": "Gløym det",
+ "save": "Ta kontroll"
+ }
+ }
}
},
"sidebar": {
@@ -721,7 +766,8 @@
},
"common": {
"loading": "Lastar",
- "cancel": "Avbryt\n"
+ "cancel": "Avbryt\n",
+ "save": "Lagre"
},
"duration": {
"day": "{count} {count, plural,\none {dag}\nother {dagar}\n}",
@@ -782,8 +828,8 @@
"disarm": "Skru av",
"arm_home": "Heimemodus",
"arm_away": "Bortemodus",
- "arm_night": "Acionamento noturno",
- "armed_custom_bypass": "Atalho configurado"
+ "arm_night": "Aktiver natt",
+ "armed_custom_bypass": "Tilpassa bypass"
},
"automation": {
"last_triggered": "Sist utløyst",
@@ -835,11 +881,11 @@
}
},
"water_heater": {
- "currently": "Atualmente",
- "on_off": "Liga \/ desliga",
- "target_temperature": "Temperatura desejada",
- "operation": "Operação",
- "away_mode": "Modo distante"
+ "currently": "For augeblinken",
+ "on_off": "På \/ av",
+ "target_temperature": "Temperaturmål",
+ "operation": "Operasjon",
+ "away_mode": "Bortemodus"
}
},
"components": {
diff --git a/translations/no.json b/translations/no.json
index eabe963409..5d40fcf0d6 100644
--- a/translations/no.json
+++ b/translations/no.json
@@ -423,6 +423,10 @@
"event": "Hendelse:",
"enter": "Ankommer",
"leave": "Forlater"
+ },
+ "webhook": {
+ "label": "Webhook",
+ "webhook_id": "Webhook ID"
}
}
},
@@ -823,7 +827,7 @@
"clear_code": "Klarer",
"disarm": "Deaktiver",
"arm_home": "Armer hjemme",
- "arm_away": "Armer bort",
+ "arm_away": "Armer borte",
"arm_night": "Armer natt",
"armed_custom_bypass": "Tilpasset bypass"
},
diff --git a/translations/pl.json b/translations/pl.json
index 91ef3e2103..41cb3ed7d4 100644
--- a/translations/pl.json
+++ b/translations/pl.json
@@ -423,6 +423,10 @@
"event": "Zdarzenie",
"enter": "Wprowadź",
"leave": "Opuść"
+ },
+ "webhook": {
+ "label": "Webhook",
+ "webhook_id": "Identyfikator Webhook"
}
}
},
@@ -482,7 +486,7 @@
"type_select": "Typ akcji",
"type": {
"service": {
- "label": "Wykonanie usługi",
+ "label": "Wywołanie usługi",
"service_data": "Dane usługi"
},
"delay": {
@@ -490,7 +494,7 @@
"delay": "Opóźnienie"
},
"wait_template": {
- "label": "Czekaj",
+ "label": "Oczekiwanie",
"wait_template": "Szablon czekania",
"timeout": "Limit czasu (opcjonalnie)"
},
@@ -498,7 +502,7 @@
"label": "Warunek"
},
"event": {
- "label": "Uruchom zdarzenie",
+ "label": "Wywołanie zdarzenia",
"event": "Zdarzenie:",
"service_data": "Dane usługi"
}
@@ -821,11 +825,11 @@
"alarm_control_panel": {
"code": "Kod",
"clear_code": "Wyczyść",
- "disarm": "Rozbrojony",
- "arm_home": "uzbrojony (w domu)",
- "arm_away": "uzbrojony (nieobecny)",
- "arm_night": "uzbrojony (noc)",
- "armed_custom_bypass": "uzbrojony (częściowo)"
+ "disarm": "Rozbrojenie",
+ "arm_home": "Uzbrojenie (w domu)",
+ "arm_away": "Uzbrojenie (nieobecny)",
+ "arm_night": "Uzbrojenie (noc)",
+ "armed_custom_bypass": "Uzbrój (częściowo)"
},
"automation": {
"last_triggered": "Ostatnie uruchomienie",
diff --git a/translations/pt-BR.json b/translations/pt-BR.json
index d41350931c..feaf717d7d 100644
--- a/translations/pt-BR.json
+++ b/translations/pt-BR.json
@@ -423,6 +423,10 @@
"event": "Evento:",
"enter": "Entrar",
"leave": "Sair"
+ },
+ "webhook": {
+ "label": "Webhook",
+ "webhook_id": "ID da Webhook"
}
}
},
@@ -726,13 +730,32 @@
"edit_card": {
"header": "Configuração de cartão",
"save": "Salvar",
- "toggle_editor": "Alternar Editor"
+ "toggle_editor": "Alternar Editor",
+ "pick_card": "Escolha o cartão que você deseja adicionar.",
+ "add": "Adicionar cartão",
+ "edit": "Editar",
+ "delete": "Excluir"
},
"migrate": {
"header": "Configuração Incompatível",
"para_no_id": "Este elemento não possui um ID. Por favor adicione um ID a este elemento em 'ui-lovelace.yaml'.",
"para_migrate": "O Home Assistant pode adicionar IDs a todos os seus cards e visualizações automaticamente clicando no botão 'Migrar config'.",
"migrate": "Migrar configuração"
+ },
+ "header": "Editar “interface” do usuário",
+ "configure_ui": "Configurar “interface” do usuário",
+ "edit_view": {
+ "header": "Configurações",
+ "add": "Editar visualização",
+ "edit": "Editar visualização",
+ "delete": "Excluir visualização"
+ },
+ "save_config": {
+ "header": "Assuma o controle da sua interface do Lovelace",
+ "para": "Por padrão, o Home Assistant manterá sua interface de usuário, atualizando-a quando novas entidades ou componentes do Lovelace estiverem disponíveis. Se você assumir o controle, não faremos mais alterações automaticamente para você.",
+ "para_sure": "Tem certeza de que deseja assumir o controle da sua interface de usuário?",
+ "cancel": "Nunca",
+ "save": "Assuma o controle"
}
}
}
@@ -743,7 +766,8 @@
},
"common": {
"loading": "Carregando",
- "cancel": "Cancelar"
+ "cancel": "Cancelar",
+ "save": "Salvar"
},
"duration": {
"day": "{count} {count, plural,\none {dia}\nother {dias}\n}",
diff --git a/translations/pt.json b/translations/pt.json
index 5a44af4799..2b5ecef9ed 100644
--- a/translations/pt.json
+++ b/translations/pt.json
@@ -273,16 +273,16 @@
"unavailable": "Indisp"
},
"alarm_control_panel": {
- "armed": "Armad",
+ "armed": "Armado",
"disarmed": "Desarm",
- "armed_home": "Armad",
- "armed_away": "Armad",
- "armed_night": "Armad",
+ "armed_home": "Armado",
+ "armed_away": "Armado",
+ "armed_night": "Armado",
"pending": "Pend",
"arming": "A armar",
"disarming": "Desarmar",
"triggered": "Disp",
- "armed_custom_bypass": "Armad"
+ "armed_custom_bypass": "Armado"
},
"device_tracker": {
"home": "Casa",
@@ -423,6 +423,10 @@
"event": "Evento:",
"enter": "Entrar",
"leave": "Sair"
+ },
+ "webhook": {
+ "label": "",
+ "webhook_id": ""
}
}
},
@@ -601,7 +605,7 @@
"not_used": "Nunca foi utilizado"
},
"current_user": "Esta actualmente ligado como {fullName}",
- "is_owner": "Você é o proprietário.",
+ "is_owner": "Você é um proprietário.",
"logout": "Sair",
"change_password": {
"header": "Alterar palavra-passe",
@@ -726,13 +730,32 @@
"edit_card": {
"header": "Configuração do cartão",
"save": "Guardar",
- "toggle_editor": "Alterar para editor"
+ "toggle_editor": "Alterar para editor",
+ "pick_card": "Escolha o cartão que deseja adicionar.",
+ "add": "Adicionar Cartão",
+ "edit": "Editar",
+ "delete": "Apagar"
},
"migrate": {
"header": "Configuração Incompatível",
"para_no_id": "Este elemento não possui um ID. Por favor adicione um ID a este elemento em 'ui-lovelace.yaml'.",
"para_migrate": "O Home Assistant pode adicionar IDs a todos os seus cartões e vistas automaticamente clicando no botão 'Migrar configuração'.",
"migrate": "Migrar configuração"
+ },
+ "header": "Editar UI",
+ "configure_ui": "Configurar UI",
+ "edit_view": {
+ "header": "Ver configuração",
+ "add": "Acrescentar vista",
+ "edit": "Editar vista",
+ "delete": "Apagar a vista"
+ },
+ "save_config": {
+ "header": "Assumir controle sobre a interface do Lovelace",
+ "para": "Por omissão o Home Assistant irá manter a sua interface de utilizador, actualizando sempre que uma entidade nova ou componentes Lovelace fiquem disponíveis. Se assumir o controle não será possivel fazer alterações automáticas por si.",
+ "para_sure": "Tem certeza que deseja assumir o controle sobre a interface de utilizador?",
+ "cancel": "Cancelar",
+ "save": "Assumir o controle"
}
}
}
@@ -743,7 +766,8 @@
},
"common": {
"loading": "A carregar",
- "cancel": "Cancelar"
+ "cancel": "Cancelar",
+ "save": "Guardar"
},
"duration": {
"day": "{count} {count, plural,\n one {dia}\n other {dias}\n}",
diff --git a/translations/ru.json b/translations/ru.json
index 1f0489d37e..f63699c76c 100644
--- a/translations/ru.json
+++ b/translations/ru.json
@@ -101,8 +101,8 @@
"on": "Охлаждение"
},
"door": {
- "off": "Закрыто",
- "on": "Открыто"
+ "off": "Закрыта",
+ "on": "Открыта"
},
"garage_door": {
"off": "Закрыто",
@@ -182,20 +182,20 @@
"problem": "Проблема"
},
"input_boolean": {
- "off": "Выключен",
- "on": "Включен"
+ "off": "Выкл",
+ "on": "Вкл"
},
"light": {
- "off": "Выключен",
- "on": "Включен"
+ "off": "Выкл",
+ "on": "Вкл"
},
"lock": {
"locked": "Закрыт",
"unlocked": "Открыт"
},
"media_player": {
- "off": "Выключен",
- "on": "Включен",
+ "off": "Выкл",
+ "on": "Вкл",
"playing": "Воспроизведение",
"paused": "Пауза",
"idle": "Ожидание",
@@ -206,8 +206,8 @@
"problem": "Проблема"
},
"remote": {
- "off": "Выключено",
- "on": "Включено"
+ "off": "Выкл",
+ "on": "Вкл"
},
"scene": {
"scening": "Переход к сцене"
@@ -388,11 +388,11 @@
"label": "Home Assistant",
"event": "Событие:",
"start": "Запуск",
- "shutdown": "Выключение"
+ "shutdown": "Завершение работы"
},
"mqtt": {
"label": "MQTT",
- "topic": "Топик",
+ "topic": "Тема",
"payload": "Значение (опционально)"
},
"numeric_state": {
@@ -423,6 +423,10 @@
"event": "Событие:",
"enter": "Войти",
"leave": "Покинуть"
+ },
+ "webhook": {
+ "label": "Webhook",
+ "webhook_id": "Идентификатор Webhook"
}
}
},
diff --git a/translations/sk.json b/translations/sk.json
index ff0f983fc4..a5a5764fb1 100644
--- a/translations/sk.json
+++ b/translations/sk.json
@@ -13,7 +13,8 @@
"dev-templates": "Šablóny",
"dev-mqtt": "MQTT",
"dev-info": "Info",
- "calendar": "Kalendár"
+ "calendar": "Kalendár",
+ "profile": "Profil"
},
"state": {
"default": {
@@ -116,8 +117,8 @@
"on": "Otvorené"
},
"lock": {
- "off": "Zamknuté",
- "on": "Odomknuté"
+ "off": "Zamknutý",
+ "on": "Odomknutý"
}
},
"calendar": {
@@ -166,17 +167,17 @@
"on": "Zapnutý"
},
"group": {
- "off": "Vypnuté",
- "on": "Zapnuté",
+ "off": "Vypnutá",
+ "on": "Zapnutá",
"home": "Doma",
"not_home": "Preč",
- "open": "Otvorené",
+ "open": "Otvorená",
"opening": "Otvára sa",
- "closed": "Zatvorené",
+ "closed": "Zatvorená",
"closing": "Zatvára sa",
"stopped": "Zastavené",
- "locked": "Zamknuté",
- "unlocked": "Odomknuté",
+ "locked": "Zamknutá",
+ "unlocked": "Odomknutá",
"ok": "OK",
"problem": "Problém"
},
@@ -274,14 +275,14 @@
"alarm_control_panel": {
"armed": "Zakód",
"disarmed": "Odkód",
- "armed_home": "Aktívny",
- "armed_away": "Aktívny",
- "armed_night": "Aktívny",
+ "armed_home": "Zakód",
+ "armed_away": "Zakód",
+ "armed_night": "Zakód",
"pending": "Čaká",
"arming": "Aktivácia",
"disarming": "Deakt",
"triggered": "Alarm",
- "armed_custom_bypass": "Zapnutý"
+ "armed_custom_bypass": "Zakódovaný"
},
"device_tracker": {
"home": "Doma",
@@ -300,7 +301,7 @@
"period": "Obdobie"
},
"logbook": {
- "showing_entries": "Zobrazujú sa záznamy pre"
+ "showing_entries": "Zobrazujú sa záznamy za obdobie"
},
"mailbox": {
"empty": "Nemáte žiadne správy",
@@ -380,7 +381,8 @@
"state": {
"label": "Stav",
"from": "Z",
- "to": "Na"
+ "to": "Na",
+ "for": "Trvanie stavu"
},
"homeassistant": {
"label": "Home Assistant",
@@ -421,6 +423,10 @@
"event": "Udalosť:",
"enter": "Vstúpenie",
"leave": "Opustenie"
+ },
+ "webhook": {
+ "label": "Webhook",
+ "webhook_id": "Webhook ID"
}
}
},
@@ -497,7 +503,7 @@
},
"event": {
"label": "Odpáliť udalosť",
- "event": "Udalosť",
+ "event": "Udalosť:",
"service_data": "Dáta služby"
}
}
@@ -525,6 +531,31 @@
"deactivate_user": "Deaktivovať používateľa",
"delete_user": "Vymazať používateľa"
}
+ },
+ "cloud": {
+ "caption": "Home Assistant Cloud",
+ "description_login": "Prihlásený ako {email}",
+ "description_not_login": "Neprihlásený"
+ },
+ "integrations": {
+ "caption": "Integrácie",
+ "description": "Spravovať pripojené zariadenia a služby",
+ "discovered": "Objavené",
+ "configured": "Nakonfigurovaný",
+ "new": "Nastaviť novú integráciu",
+ "configure": "Konfigurovať",
+ "none": "Nič zatiaľ nebolo nakonfigurované",
+ "config_entry": {
+ "no_devices": "Táto integrácia nemá žiadne zariadenia.",
+ "no_device": "Entity bez zariadení",
+ "delete_confirm": "Naozaj chcete odstrániť túto integráciu?",
+ "restart_confirm": "Ak chcete dokončiť odstránenie tejto integrácie, reštartujte Home Assistant",
+ "manuf": "od {manufacturer}",
+ "hub": "Pripojené cez",
+ "firmware": "Firmvér: {version}",
+ "device_unavailable": "zariadenie nie je dostupné",
+ "entity_unavailable": "Entita nie je dostupná"
+ }
}
},
"profile": {
@@ -546,20 +577,187 @@
"error_no_theme": "Nie sú k dispozícii žiadne témy.",
"link_promo": "Získajte viac informácií o témach",
"dropdown_label": "Téma"
+ },
+ "refresh_tokens": {
+ "header": "Obnovovacie Tokeny",
+ "description": "Každý obnovovací token predstavuje prihlásenú reláciu. Obnovovacie tokeny sa po kliknutí na tlačidlo Odhlásiť sa automaticky odstránia. Pre váš účet sú aktívne nasledovné obnovovacie tokeny.",
+ "token_title": "Obnovovací token pre {clientId}",
+ "created_at": "Vytvorený {date}",
+ "confirm_delete": "Naozaj chcete odstrániť obnovovací token pre {name} ?",
+ "delete_failed": "Nepodarilo sa odstrániť obnovovací token",
+ "last_used": "Naposledy použitý {date} z {location}",
+ "not_used": "Nikdy nebol použitý",
+ "current_token_tooltip": "Nedá sa odstrániť aktuálny obnovovací token"
+ },
+ "long_lived_access_tokens": {
+ "header": "Prístupové tokeny s dlhou životnosťou",
+ "description": "Vytvorte prístupové tokeny s dlhou životnosťou, ktoré umožnia vašim skriptom komunikovať s vašou inštanciou Home Assistant. Každý token bude platný 10 rokov od vytvorenia. Nasledujúce prístupové tokeny s dlhou životnosťou sú v súčasnosti aktívne.",
+ "learn_auth_requests": "Zistite, ako vytvárať overené požiadavky.",
+ "created_at": "Vytvorený {date}",
+ "confirm_delete": "Naozaj chcete odstrániť prístupový token pre {name} ?",
+ "delete_failed": "Nepodarilo sa odstrániť prístupový token.",
+ "create": "Vytvoriť Token",
+ "create_failed": "Nepodarilo sa vytvoriť prístupový token.",
+ "prompt_name": "Názov?",
+ "prompt_copy_token": "Skopírujte svoj nový prístupový token. Znova sa nezobrazí.",
+ "empty_state": "Nemáte žiadne prístupové tokeny s dlhou životnosťou.",
+ "last_used": "Naposledy použitý {date} z {location}",
+ "not_used": "Nikdy nebol použitý"
+ },
+ "current_user": "Momentálne ste prihlásení ako {fullName} .",
+ "is_owner": "Ste vlastníkom.",
+ "logout": "Odhlásiť sa",
+ "change_password": {
+ "header": "Zmena hesla",
+ "current_password": "Aktuálne heslo",
+ "new_password": "Nové heslo",
+ "confirm_new_password": "Potvrďte nové heslo",
+ "error_required": "Požadované",
+ "submit": "Odoslať"
+ },
+ "mfa": {
+ "header": "Multifaktorové autentifikačné moduly",
+ "disable": "Zakázať",
+ "enable": "Povoliť",
+ "confirm_disable": "Naozaj chcete zakázať {name} ?"
+ },
+ "mfa_setup": {
+ "title_aborted": "Prerušené",
+ "title_success": "Úspech!",
+ "step_done": "Nastavenie dokončené krok {step} ",
+ "close": "Zavrieť",
+ "submit": "Odoslať"
}
},
"page-authorize": {
+ "initializing": "Inicializácia",
+ "authorizing_client": "Chystáte sa poskytnúť {clientId} prístup k vašej inštancii Home Assistantu.",
+ "logging_in_with": "Prihlasovanie pomocou ** {authProviderName} **.",
+ "pick_auth_provider": "Alebo sa prihláste prostredníctvom",
+ "abort_intro": "Prihlásenie bolo zrušené",
"form": {
+ "working": "Prosím čakajte",
+ "unknown_error": "Niečo sa pokazilo",
"providers": {
+ "homeassistant": {
+ "step": {
+ "init": {
+ "data": {
+ "username": "Používateľské meno",
+ "password": "Heslo"
+ }
+ },
+ "mfa": {
+ "data": {
+ "code": "Kód dvojfaktorovej autentifikácie"
+ },
+ "description": "Otvorte ** {mfa_module_name} ** v zariadení, aby ste si pozreli svoj dvojfaktorový autentifikačný kód a overili svoju totožnosť:"
+ }
+ },
+ "error": {
+ "invalid_auth": "Nesprávne používateľské meno alebo heslo",
+ "invalid_code": "Neplatný overovací kód"
+ },
+ "abort": {
+ "login_expired": "Platnosť relácie skončila, prosím prihláste znova."
+ }
+ },
+ "legacy_api_password": {
+ "step": {
+ "init": {
+ "data": {
+ "password": "Heslo API rozhrania"
+ },
+ "description": "Prosím zadajte heslo API rozhrania v konfigurácií http:"
+ },
+ "mfa": {
+ "data": {
+ "code": "Kód dvojfaktorovej autentifikácie"
+ },
+ "description": "Otvorte **{mfa_module_name}** na vašom zariadení a pozrite si kód dvojfaktorovej autentifikácie a overte svoju totožnosť."
+ }
+ },
+ "error": {
+ "invalid_auth": "Neplatné heslo rozhrania API",
+ "invalid_code": "Neplatný autentifikačný kód"
+ },
+ "abort": {
+ "no_api_password_set": "Nemáte nakonfigurované heslo rozhrania API.",
+ "login_expired": "Relácia vypršala, prosím prihláste sa znova."
+ }
+ },
"trusted_networks": {
"step": {
"init": {
+ "data": {
+ "user": "Používateľ"
+ },
"description": "Välj en användare att logga in som:"
}
+ },
+ "abort": {
+ "not_whitelisted": "Váš počítač nie je v zozname povolených zariadení."
}
}
}
}
+ },
+ "page-onboarding": {
+ "intro": "Ste pripravení prebudiť váš domov, získať vaše súkromie a pripojiť sa k celosvetovej komunite bastličov?",
+ "user": {
+ "intro": "Poďme začať vytvorením používateľského konta.",
+ "required_field": "Požadované",
+ "data": {
+ "name": "Meno",
+ "username": "Používateľské meno",
+ "password": "Heslo"
+ },
+ "create_account": "Vytvoriť účet",
+ "error": {
+ "required_fields": "Vyplňte všetky povinné polia"
+ }
+ }
+ },
+ "lovelace": {
+ "cards": {
+ "shopping-list": {
+ "checked_items": "Začiarknuté položky",
+ "clear_items": "Vymažte označené položky",
+ "add_item": "Pridať položku"
+ }
+ },
+ "editor": {
+ "edit_card": {
+ "header": "Konfigurácia karty",
+ "save": "Uložiť",
+ "toggle_editor": "Prepnúť Editor",
+ "pick_card": "Vyberte kartu, ktorú chcete pridať.",
+ "add": "Pridať kartu",
+ "edit": "Upraviť",
+ "delete": "Vymazať"
+ },
+ "migrate": {
+ "header": "Nekompatibilná konfigurácia",
+ "para_no_id": "Tento prvok nemá ID. Pridajte ID k tomuto prvku v ui-lovelace.yaml.",
+ "para_migrate": "Home Assistant dokáže automaticky pridať ID na všetky vaše karty a zobrazenia stlačením tlačidla Migrácia konfigurácie.",
+ "migrate": "Migrácia konfigurácie"
+ },
+ "header": "Úprava používateľského rozhrania",
+ "configure_ui": "Konfigurácia používateľského rozhrania",
+ "edit_view": {
+ "header": "Konfigurácia zobrazenia",
+ "add": "Pridať zobrazenie",
+ "edit": "Upraviť zobrazenie",
+ "delete": "Odstrániť zobrazenie"
+ },
+ "save_config": {
+ "header": "Prevziať kontrolu vášho Lovelace rozhrania",
+ "para": "Štandardne Home Assistant bude udržiavať vaše užívateľské rozhranie a aktualizovať ho, keď budú k dispozícii nové entity alebo komponenty služby Lovelace. Ak prevezmete kontrolu, nebudeme už automaticky robiť zmeny.",
+ "para_sure": "Naozaj chcete prevziať kontrolu vášho užívateľského rozhrania?",
+ "cancel": "Nevadí",
+ "save": "Prevziať kontrolu"
+ }
+ }
}
},
"sidebar": {
@@ -568,7 +766,8 @@
},
"common": {
"loading": "Načítava sa",
- "cancel": "Zrušiť"
+ "cancel": "Zrušiť",
+ "save": "Uložiť"
},
"duration": {
"day": "{count} {count, plural,\none {deň}\nfew {dni}\nother {dní}\n}",
@@ -628,7 +827,9 @@
"clear_code": "Zrušiť",
"disarm": "Odkódovať",
"arm_home": "Zakódovať doma",
- "arm_away": "Zakódovať odchod"
+ "arm_away": "Zakódovať odchod",
+ "arm_night": "Zakódovať na noc",
+ "armed_custom_bypass": "Prispôsobené vylúčenie"
},
"automation": {
"last_triggered": "Naposledy spustené",
@@ -678,6 +879,13 @@
"turn_on": "Zapnúť",
"turn_off": "Vypnúť"
}
+ },
+ "water_heater": {
+ "currently": "Aktuálne",
+ "on_off": "Zapnúť \/ vypnúť",
+ "target_temperature": "Cieľová teplota",
+ "operation": "V prevádzke",
+ "away_mode": "Režim neprítomnosti"
}
},
"components": {
@@ -724,6 +932,11 @@
"ask": "Chcete tieto prihlasovacie údaje uložiť?",
"decline": "Nie ďakujem",
"confirm": "Uložiť prihlasovacie údaje"
+ },
+ "notification_drawer": {
+ "click_to_configure": "Kliknutím na tlačidlo nakonfigurujete {entity}",
+ "empty": "Žiadne upozornenia",
+ "title": "Upozornenia"
}
},
"domain": {
diff --git a/translations/sl.json b/translations/sl.json
index 09be57ec64..150082c742 100644
--- a/translations/sl.json
+++ b/translations/sl.json
@@ -423,6 +423,10 @@
"event": "Dogodek:",
"enter": "Vnesite",
"leave": "Odidi"
+ },
+ "webhook": {
+ "label": "Webhook",
+ "webhook_id": "Webhook ID"
}
}
},
diff --git a/translations/sv.json b/translations/sv.json
index 419a226696..f9d8808532 100644
--- a/translations/sv.json
+++ b/translations/sv.json
@@ -423,6 +423,10 @@
"event": "Händelse",
"enter": "Ankommer",
"leave": "Lämnar"
+ },
+ "webhook": {
+ "label": "Webhook",
+ "webhook_id": "Webhook ID"
}
}
},
@@ -726,13 +730,31 @@
"edit_card": {
"header": "Kortkonfiguration",
"save": "Spara",
- "toggle_editor": "Visa \/ Dölj redigerare"
+ "toggle_editor": "Visa \/ Dölj redigerare",
+ "pick_card": "Välj det kort du vill lägga till.",
+ "add": "Lägg till kort",
+ "edit": "Redigera",
+ "delete": "Ta bort"
},
"migrate": {
"header": "Konfigurationen är inte giltig",
"para_no_id": "Det här elementet har inget ID. Lägg till ett ID till det här elementet i \"ui-lovelace.yaml\".",
"para_migrate": "Home Assistant kan automatiskt lägga till IDn till alla dina kort och vyer genom att du klickar på \"Migrera konfiguration\".",
"migrate": "Migrera konfigurationen"
+ },
+ "header": "Ändra användargränssnittet",
+ "configure_ui": "Konfigurera användargränssnittet",
+ "edit_view": {
+ "header": "Visa konfiguration",
+ "add": "Lägg till vy",
+ "edit": "Redigera vy",
+ "delete": "Radera vy"
+ },
+ "save_config": {
+ "header": "Ta kontroll över din Lovelace UI",
+ "para_sure": "Är du säker på att du vill ta kontroll över ditt användargränssnitt?",
+ "cancel": "Glöm det",
+ "save": "Ta kontroll"
}
}
}
@@ -743,7 +765,8 @@
},
"common": {
"loading": "Läser in",
- "cancel": "Avbryt"
+ "cancel": "Avbryt",
+ "save": "Spara"
},
"duration": {
"day": "{count} {count, plural,\none {dag}\nother {dagar}\n}",
diff --git a/translations/uk.json b/translations/uk.json
index 34a09cbfd7..07bfdf8c67 100644
--- a/translations/uk.json
+++ b/translations/uk.json
@@ -50,7 +50,7 @@
},
"gas": {
"off": "Чисто",
- "on": "Виявлено"
+ "on": "Виявлено газ"
},
"motion": {
"off": "Немає руху",
@@ -58,26 +58,26 @@
},
"occupancy": {
"off": "Чисто",
- "on": "Виявлено"
+ "on": "Виявлено присутність"
},
"smoke": {
"off": "Чисто",
- "on": "Виявлено"
+ "on": "Виявлено дим"
},
"sound": {
"off": "Чисто",
- "on": "Виявлено"
+ "on": "Виявлено звук"
},
"vibration": {
- "off": "Чисто",
- "on": "Виявлено"
+ "off": "Не виявлено",
+ "on": "Виявлена вібрація"
},
"opening": {
"off": "Закрито",
"on": "Відкритий"
},
"safety": {
- "off": "Безпека",
+ "off": "Безпечно",
"on": "Небезпечно"
},
"presence": {
@@ -106,14 +106,14 @@
},
"garage_door": {
"off": "ЗачиненІ",
- "on": "Відкрито"
+ "on": "Відкриті"
},
"heat": {
"off": "Норма",
"on": "Нагрівання"
},
"window": {
- "off": "Зачинено",
+ "off": "Зачинене",
"on": "Відчинене"
},
"lock": {
@@ -255,6 +255,9 @@
"sunny": "Сонячно",
"windy": "Вітряно",
"windy-variant": "Вітряно"
+ },
+ "vacuum": {
+ "idle": "Очікування"
}
},
"state_badge": {
@@ -605,6 +608,8 @@
"lovelace": {
"cards": {
"shopping-list": {
+ "checked_items": "Позначені елементи",
+ "clear_items": "Очистити позначені елементи",
"add_item": "Додати елемент"
}
},
@@ -612,20 +617,32 @@
"edit_card": {
"header": "Конфігурація картки",
"save": "Зберегти",
+ "toggle_editor": "Перемкнути редактор",
"pick_card": "Виберіть картку, яку хочете додати.",
"add": "Додати картку",
"edit": "Редагувати",
"delete": "Видалити"
},
"migrate": {
- "header": "Конфігурація несумісна"
+ "header": "Конфігурація несумісна",
+ "para_no_id": "Цей елемент не має ID. Додайте ID до цього елемента в 'ui-lovelace.yaml'.",
+ "para_migrate": "Домашній помічник може автоматично додавати ідентифікатори ID до всіх ваших карт і переглядів, натиснувши кнопку \"Перенести налаштування\".",
+ "migrate": "Перенесення конфігурації"
},
"header": "Редагування інтерфейсу",
+ "configure_ui": "Налаштувати інтерфейс користувача",
"edit_view": {
- "header": "Перегляд Конфігурації "
+ "header": "Перегляд Конфігурації ",
+ "add": "Додати вигляд",
+ "edit": "Редагувати вигляд",
+ "delete": "Видалити вигляд"
},
"save_config": {
- "header": "Візьміть під свій контроль Lovelace UI"
+ "header": "Візьміть під свій контроль Lovelace UI",
+ "para": "За замовчуванням Home Assistant буде підтримувати ваш користувальницький інтерфейс, оновлюючи його, коли з'являться нові об'єкти або компоненти Lovelace. Якщо ви візьмете під контроль, ми більше не будемо автоматично вносити зміни для вас.",
+ "para_sure": "Ви впевнені, що хочете взяти під свій контроль користувальницький інтерфейс?",
+ "cancel": "Неважливо",
+ "save": "Взяти під контроль"
}
}
}
@@ -680,7 +697,8 @@
"disarm": "Зняття з охорони",
"arm_home": "Поставити на охорону",
"arm_away": "Охорона (не вдома)",
- "arm_night": "Нічна охорона"
+ "arm_night": "Нічна охорона",
+ "armed_custom_bypass": "Користувацький обхід"
},
"automation": {
"last_triggered": "Спрацьовано",
diff --git a/translations/vi.json b/translations/vi.json
index 38c49a5f1f..65012c9e14 100644
--- a/translations/vi.json
+++ b/translations/vi.json
@@ -28,7 +28,7 @@
"disarmed": "Vô hiệu hóa",
"armed_home": "Bảo vệ ở nhà",
"armed_away": "Bảo vệ đi vắng",
- "armed_night": "An ninh ban đêm",
+ "armed_night": "Ban đêm",
"pending": "Đang chờ xử lý",
"arming": "Kích hoạt",
"disarming": "Giải giáp",
@@ -747,8 +747,8 @@
"code": "Mã số",
"clear_code": "Xóa",
"disarm": "Vô hiệu hoá",
- "arm_home": "An ninh ở nhà",
- "arm_away": "An ninh đi vắng",
+ "arm_home": "Ở nhà",
+ "arm_away": "Đi vắng",
"arm_night": "An ninh ban đêm",
"armed_custom_bypass": "Bỏ qua tùy chỉnh"
},
diff --git a/translations/zh-Hant.json b/translations/zh-Hant.json
index 7914810c0f..5c83df2a18 100644
--- a/translations/zh-Hant.json
+++ b/translations/zh-Hant.json
@@ -423,6 +423,10 @@
"event": "事件:",
"enter": "進入區域",
"leave": "離開區域"
+ },
+ "webhook": {
+ "label": "Webhook",
+ "webhook_id": "Webhook ID"
}
}
},
@@ -750,8 +754,8 @@
"header": "自行編輯 Lovelace UI",
"para": "Home Assistant 於預設下,將維護您的使用者介面、於新物件或 Lovelace 元件可使用時進行更新。假如選擇自行編輯,系統將不再為您自動進行變更。",
"para_sure": "確定要自行編輯使用者介面?",
- "cancel": "取消",
- "save": "儲存"
+ "cancel": "我再想想",
+ "save": "自行編輯"
}
}
}