diff --git a/package.json b/package.json index 062911b143..4898fc98b7 100644 --- a/package.json +++ b/package.json @@ -17,9 +17,9 @@ "author": "Paulus Schoutsen (http://paulusschoutsen.nl)", "license": "Apache-2.0", "dependencies": { - "@material/mwc-button": "^0.3.6", - "@material/mwc-ripple": "^0.3.6", - "@mdi/svg": "^3.0.39", + "@material/mwc-button": "^0.5.0", + "@material/mwc-ripple": "^0.5.0", + "@mdi/svg": "3.5.95", "@polymer/app-layout": "^3.0.1", "@polymer/app-localize-behavior": "^3.0.1", "@polymer/app-route": "^3.0.2", @@ -34,6 +34,7 @@ "@polymer/iron-input": "^3.0.1", "@polymer/iron-label": "^3.0.1", "@polymer/iron-media-query": "^3.0.1", + "@polymer/iron-overlay-behavior": "^3.0.2", "@polymer/iron-pages": "^3.0.1", "@polymer/iron-resizable-behavior": "^3.0.1", "@polymer/neon-animation": "^3.0.1", @@ -79,7 +80,7 @@ "jquery": "^3.3.1", "js-yaml": "^3.12.0", "leaflet": "^1.3.4", - "lit-element": "^2.0.0", + "lit-element": "^2.1.0", "lit-html": "^1.0.0", "marked": "^0.6.0", "mdn-polyfills": "^5.12.0", @@ -167,7 +168,8 @@ "@webcomponents/webcomponentsjs": "^2.2.6", "@webcomponents/shadycss": "^1.9.0", "@vaadin/vaadin-overlay": "3.2.2", - "@vaadin/vaadin-lumo-styles": "1.3.0" + "@vaadin/vaadin-lumo-styles": "1.3.0", + "@polymer/iron-overlay-behavior": "^3.0.2" }, "main": "src/home-assistant.js", "husky": { diff --git a/setup.py b/setup.py index b8f610553d..5ef268efa2 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ from setuptools import setup, find_packages setup( name="home-assistant-frontend", - version="20190321.0", + version="20190327.0", description="The Home Assistant frontend", url="https://github.com/home-assistant/home-assistant-polymer", author="The Home Assistant Authors", diff --git a/src/auth/ha-auth-flow.js b/src/auth/ha-auth-flow.js index e07774c90b..d397aa4f4e 100644 --- a/src/auth/ha-auth-flow.js +++ b/src/auth/ha-auth-flow.js @@ -2,6 +2,7 @@ import { PolymerElement } from "@polymer/polymer/polymer-element"; import "@material/mwc-button"; import { html } from "@polymer/polymer/lib/utils/html-tag"; import "../components/ha-form"; +import "../components/ha-markdown"; import { localizeLiteMixin } from "../mixins/localize-lite-mixin"; class HaAuthFlow extends localizeLiteMixin(PolymerElement) { @@ -121,6 +122,12 @@ class HaAuthFlow extends localizeLiteMixin(PolymerElement) { const data = await response.json(); if (response.ok) { + // allow auth provider bypass the login form + if (data.type === "create_entry") { + this._redirect(data.result); + return; + } + this._updateStep(data); } else { this.setProperties({ @@ -138,6 +145,24 @@ class HaAuthFlow extends localizeLiteMixin(PolymerElement) { } } + _redirect(authCode) { + // OAuth 2: 3.1.2 we need to retain query component of a redirect URI + let url = this.redirectUri; + if (!url.includes("?")) { + url += "?"; + } else if (!url.endsWith("&")) { + url += "&"; + } + + url += `code=${encodeURIComponent(authCode)}`; + + if (this.oauth2State) { + url += `&state=${encodeURIComponent(this.oauth2State)}`; + } + + document.location = url; + } + _updateStep(step) { const props = { _step: step, @@ -229,21 +254,7 @@ class HaAuthFlow extends localizeLiteMixin(PolymerElement) { const newStep = await response.json(); if (newStep.type === "create_entry") { - // OAuth 2: 3.1.2 we need to retain query component of a redirect URI - let url = this.redirectUri; - if (!url.includes("?")) { - url += "?"; - } else if (!url.endsWith("&")) { - url += "&"; - } - - url += `code=${encodeURIComponent(newStep.result)}`; - - if (this.oauth2State) { - url += `&state=${encodeURIComponent(this.oauth2State)}`; - } - - document.location = url; + this._redirect(newStep.result); return; } this._updateStep(newStep); diff --git a/src/cards/ha-media_player-card.js b/src/cards/ha-media_player-card.js index cb068c3afe..d54176a832 100644 --- a/src/cards/ha-media_player-card.js +++ b/src/cards/ha-media_player-card.js @@ -132,6 +132,10 @@ class HaMediaPlayerCard extends LocalizeMixin(EventsMixin(PolymerElement)) { height: 44px; } + .playback-controls { + direction: ltr; + } + paper-icon-button { opacity: var(--dark-primary-opacity); } @@ -186,7 +190,7 @@ class HaMediaPlayerCard extends LocalizeMixin(EventsMixin(PolymerElement)) { class="self-center secondary" > -
+
+ `; } const domain = computeStateDomain(state); return html` - ${this.renderStyle()} { @@ -201,48 +203,47 @@ export class HaStateLabelBadge extends LitElement { this._timerTimeRemaining = timerTimeRemaining(stateObj); } - private renderStyle(): TemplateResult { - return html` - + .grey { + --ha-label-badge-color: var(--label-badge-grey, var(--paper-grey-500)); + } + + .warning { + --ha-label-badge-color: var(--label-badge-yellow, #fce588); + } `; } } @@ -252,5 +253,3 @@ declare global { "ha-state-label-badge": HaStateLabelBadge; } } - -customElements.define("ha-state-label-badge", HaStateLabelBadge); diff --git a/src/components/ha-climate-control.js b/src/components/ha-climate-control.js index 20c7131373..2a1218ca0f 100644 --- a/src/components/ha-climate-control.js +++ b/src/components/ha-climate-control.js @@ -25,6 +25,7 @@ class HaClimateControl extends EventsMixin(PolymerElement) { #target_temperature { @apply --layout-self-center; font-size: 200%; + direction: ltr; } .control-buttons { font-size: 200%; diff --git a/src/components/ha-climate-state.js b/src/components/ha-climate-state.js index 73562392d9..27693bded9 100644 --- a/src/components/ha-climate-state.js +++ b/src/components/ha-climate-state.js @@ -29,18 +29,24 @@ class HaClimateState extends LocalizeMixin(PolymerElement) { font-weight: bold; text-transform: capitalize; } + + .unit { + display: inline-block; + direction: ltr; + }
- [[computeTarget(hass, stateObj)]] +
[[computeTarget(hass, stateObj)]]
`; diff --git a/src/components/ha-paper-slider.js b/src/components/ha-paper-slider.js index f7cad5fec2..6a088208de 100644 --- a/src/components/ha-paper-slider.js +++ b/src/components/ha-paper-slider.js @@ -58,6 +58,11 @@ class HaPaperSlider extends PaperSliderClass { -webkit-transform: scale(1) translate(0, -10px); transform: scale(1) translate(0, -10px); } + + :host([dir="rtl"]) .pin.expand > .slider-knob > .slider-knob-inner::after { + -webkit-transform: scale(1) translate(0, -17px) scaleX(-1) !important; + transform: scale(1) translate(0, -17px) scaleX(-1) !important; + } `; tpl.content.appendChild(styleEl); return tpl; diff --git a/src/components/ha-sidebar.ts b/src/components/ha-sidebar.ts index 48ebff59e5..98d0a5e097 100644 --- a/src/components/ha-sidebar.ts +++ b/src/components/ha-sidebar.ts @@ -26,19 +26,13 @@ const computePanels = (hass: HomeAssistant) => { if (!panels) { return []; } - const isAdmin = hass.user.is_admin; + const sortValue = { map: 1, logbook: 2, history: 3, }; - const result: Panel[] = []; - - Object.values(panels).forEach((panel) => { - if (panel.title && (panel.component_name !== "config" || isAdmin)) { - result.push(panel); - } - }); + const result: Panel[] = Object.values(panels).filter((panel) => panel.title); result.sort((a, b) => { const aBuiltIn = a.component_name in sortValue; @@ -133,9 +127,8 @@ class HaSidebar extends LitElement { : html``} - ${!hass.user.is_admin - ? "" - : html` + ${hass.user && hass.user.is_admin + ? html`
@@ -192,7 +185,8 @@ class HaSidebar extends LitElement {
- `} + ` + : ""} `; } diff --git a/src/data/panel_custom.ts b/src/data/panel_custom.ts new file mode 100644 index 0000000000..7f8ad4a29e --- /dev/null +++ b/src/data/panel_custom.ts @@ -0,0 +1,8 @@ +export interface CustomPanelConfig { + name: string; + embed_iframe: boolean; + trust_external: boolean; + js_url?: string; + module_url?: string; + html_url?: string; +} diff --git a/src/data/zha.ts b/src/data/zha.ts index bdb965e5bd..87ac9a3a88 100644 --- a/src/data/zha.ts +++ b/src/data/zha.ts @@ -16,6 +16,7 @@ export interface ZHADevice { manufacturer_code: number; device_reg_id: string; user_given_name: string; + area_id: string; } export interface Attribute { @@ -42,7 +43,7 @@ export interface ReadAttributeServiceData { cluster_id: number; cluster_type: string; attribute: number; - manufacturer: number; + manufacturer?: number; } export const reconfigureNode = ( diff --git a/src/dialogs/more-info/controls/more-info-climate.js b/src/dialogs/more-info/controls/more-info-climate.js index eac739b482..529cad6d6b 100644 --- a/src/dialogs/more-info/controls/more-info-climate.js +++ b/src/dialogs/more-info/controls/more-info-climate.js @@ -17,6 +17,7 @@ import { supportsFeature } from "../../../common/entity/supports-feature"; import EventsMixin from "../../../mixins/events-mixin"; import LocalizeMixin from "../../../mixins/localize-mixin"; +import { computeRTLDirection } from "../../../common/util/compute_rtl"; /* * @appliesMixin EventsMixin @@ -84,6 +85,7 @@ class MoreInfoClimate extends LocalizeMixin(EventsMixin(PolymerElement)) { width: 90px; font-size: 200%; margin: auto; + direction: ltr; } ha-climate-control.range-control-left, @@ -181,6 +183,7 @@ class MoreInfoClimate extends LocalizeMixin(EventsMixin(PolymerElement)) { value="[[stateObj.attributes.humidity]]" on-change="targetHumiditySliderChanged" ignore-bar-touch="" + dir="[[rtl]]" >
@@ -314,6 +317,12 @@ class MoreInfoClimate extends LocalizeMixin(EventsMixin(PolymerElement)) { awayToggleChecked: Boolean, auxToggleChecked: Boolean, onToggleChecked: Boolean, + + rtl: { + type: String, + value: "ltr", + computed: "_computeRTLDirection(hass)", + }, }; } @@ -557,6 +566,10 @@ class MoreInfoClimate extends LocalizeMixin(EventsMixin(PolymerElement)) { _localizeFanMode(localize, mode) { return localize(`state_attributes.climate.fan_mode.${mode}`) || mode; } + + _computeRTLDirection(hass) { + return computeRTLDirection(hass); + } } customElements.define("more-info-climate", MoreInfoClimate); diff --git a/src/dialogs/more-info/controls/more-info-media_player.js b/src/dialogs/more-info/controls/more-info-media_player.js index 0ce7af7a71..d78540d0c4 100644 --- a/src/dialogs/more-info/controls/more-info-media_player.js +++ b/src/dialogs/more-info/controls/more-info-media_player.js @@ -14,6 +14,7 @@ import attributeClassNames from "../../../common/entity/attribute_class_names"; import isComponentLoaded from "../../../common/config/is_component_loaded"; import EventsMixin from "../../../mixins/events-mixin"; import LocalizeMixin from "../../../mixins/localize-mixin"; +import { computeRTLDirection } from "../../../common/util/compute_rtl"; /* * @appliesMixin LocalizeMixin @@ -137,6 +138,7 @@ class MoreInfoMediaPlayer extends LocalizeMixin(EventsMixin(PolymerElement)) { on-change="volumeSliderChanged" class="flex" ignore-bar-touch="" + dir="{{rtl}}" > @@ -233,6 +235,11 @@ class MoreInfoMediaPlayer extends LocalizeMixin(EventsMixin(PolymerElement)) { type: String, value: "", }, + + rtl: { + type: String, + computed: "_computeRTLDirection(hass)", + }, }; } @@ -425,6 +432,10 @@ class MoreInfoMediaPlayer extends LocalizeMixin(EventsMixin(PolymerElement)) { this.ttsMessage = ""; this.$.ttsInput.focus(); } + + _computeRTLDirection(hass) { + return computeRTLDirection(hass); + } } customElements.define("more-info-media_player", MoreInfoMediaPlayer); diff --git a/src/entrypoints/custom-panel.js b/src/entrypoints/custom-panel.js deleted file mode 100644 index 1d064943ce..0000000000 --- a/src/entrypoints/custom-panel.js +++ /dev/null @@ -1,80 +0,0 @@ -import { loadJS } from "../common/dom/load_resource"; -import loadCustomPanel from "../util/custom-panel/load-custom-panel"; -import createCustomPanelElement from "../util/custom-panel/create-custom-panel-element"; -import setCustomPanelProperties from "../util/custom-panel/set-custom-panel-properties"; - -const webComponentsSupported = - "customElements" in window && - "import" in document.createElement("link") && - "content" in document.createElement("template"); - -let es5Loaded = null; - -window.loadES5Adapter = () => { - if (!es5Loaded) { - es5Loaded = Promise.all([ - loadJS(`${__STATIC_PATH__}custom-elements-es5-adapter.js`).catch(), - import(/* webpackChunkName: "compat" */ "./compatibility"), - ]); - } - return es5Loaded; -}; - -let root = null; - -function setProperties(properties) { - if (root === null) return; - setCustomPanelProperties(root, properties); -} - -function initialize(panel, properties) { - const style = document.createElement("style"); - style.innerHTML = "body{margin:0}"; - document.head.appendChild(style); - - const config = panel.config._panel_custom; - let start = Promise.resolve(); - - if (!webComponentsSupported) { - start = start.then(() => loadJS("/static/webcomponents-bundle.js")); - } - - if (__BUILD__ === "es5") { - // Load ES5 adapter. Swallow errors as it raises errors on old browsers. - start = start.then(() => window.loadES5Adapter()); - } - - start - .then(() => loadCustomPanel(config)) - // If our element is using es5, let it finish loading that and define element - // This avoids elements getting upgraded after being added to the DOM - .then(() => es5Loaded || Promise.resolve()) - .then( - () => { - root = createCustomPanelElement(config); - - const forwardEvent = (ev) => - window.parent.customPanel.fire(ev.type, ev.detail); - root.addEventListener("hass-toggle-menu", forwardEvent); - window.addEventListener("location-changed", (ev) => - window.parent.customPanel.navigate( - window.location.pathname, - ev.detail ? ev.detail.replace : false - ) - ); - setProperties(Object.assign({ panel }, properties)); - document.body.appendChild(root); - }, - (err) => { - // eslint-disable-next-line - console.error(err, panel); - alert(`Unable to load the panel source: ${err}.`); - } - ); -} - -document.addEventListener( - "DOMContentLoaded", - () => window.parent.customPanel.registerIframe(initialize, setProperties), - { once: true } -); diff --git a/src/entrypoints/custom-panel.ts b/src/entrypoints/custom-panel.ts new file mode 100644 index 0000000000..f5c9c463e5 --- /dev/null +++ b/src/entrypoints/custom-panel.ts @@ -0,0 +1,97 @@ +import { loadJS } from "../common/dom/load_resource"; +import { loadCustomPanel } from "../util/custom-panel/load-custom-panel"; +import { createCustomPanelElement } from "../util/custom-panel/create-custom-panel-element"; +import { setCustomPanelProperties } from "../util/custom-panel/set-custom-panel-properties"; +import { fireEvent } from "../common/dom/fire_event"; +import { navigate } from "../common/navigate"; +import { PolymerElement } from "@polymer/polymer"; +import { Panel } from "../types"; +import { CustomPanelConfig } from "../data/panel_custom"; + +declare global { + interface Window { + loadES5Adapter: () => Promise; + } +} + +const webComponentsSupported = + "customElements" in window && + "import" in document.createElement("link") && + "content" in document.createElement("template"); + +let es5Loaded: Promise | undefined; + +window.loadES5Adapter = () => { + if (!es5Loaded) { + es5Loaded = Promise.all([ + loadJS(`${__STATIC_PATH__}custom-elements-es5-adapter.js`).catch(), + import(/* webpackChunkName: "compat" */ "./compatibility"), + ]); + } + return es5Loaded; +}; + +let panelEl: HTMLElement | PolymerElement | undefined; + +function setProperties(properties) { + if (!panelEl) { + return; + } + setCustomPanelProperties(panelEl, properties); +} + +function initialize(panel: Panel, properties: {}) { + const style = document.createElement("style"); + style.innerHTML = "body{margin:0}"; + document.head.appendChild(style); + + const config = panel.config!._panel_custom as CustomPanelConfig; + let start: Promise = Promise.resolve(); + + if (!webComponentsSupported) { + start = start.then(() => loadJS("/static/webcomponents-bundle.js")); + } + + if (__BUILD__ === "es5") { + // Load ES5 adapter. Swallow errors as it raises errors on old browsers. + start = start.then(() => window.loadES5Adapter()); + } + + start + .then(() => loadCustomPanel(config)) + // If our element is using es5, let it finish loading that and define element + // This avoids elements getting upgraded after being added to the DOM + .then(() => es5Loaded || Promise.resolve()) + .then( + () => { + panelEl = createCustomPanelElement(config); + + const forwardEvent = (ev) => { + if (window.parent.customPanel) { + fireEvent(window.parent.customPanel, ev.type, ev.detail); + } + }; + panelEl!.addEventListener("hass-toggle-menu", forwardEvent); + window.addEventListener("location-changed", (ev: any) => + navigate( + window.parent.customPanel, + window.location.pathname, + ev.detail ? ev.detail.replace : false + ) + ); + setProperties({ panel, ...properties }); + document.body.appendChild(panelEl!); + }, + (err) => { + // tslint:disable-next-line + console.error(err, panel); + alert(`Unable to load the panel source: ${err}.`); + } + ); +} + +document.addEventListener( + "DOMContentLoaded", + () => window.parent.customPanel!.registerIframe(initialize, setProperties), + { once: true } +); diff --git a/src/panels/config/ha-panel-config.ts b/src/panels/config/ha-panel-config.ts index 0d78f87168..052c12aea3 100644 --- a/src/panels/config/ha-panel-config.ts +++ b/src/panels/config/ha-panel-config.ts @@ -73,9 +73,9 @@ class HaPanelConfig extends HassRouterPage { import(/* webpackChunkName: "panel-config-users" */ "./users/ha-config-users"), }, zha: { - tag: "ha-config-zha", + tag: "zha-config-panel", load: () => - import(/* webpackChunkName: "panel-config-zha" */ "./zha/ha-config-zha"), + import(/* webpackChunkName: "panel-config-zha" */ "./zha/zha-config-panel"), }, zwave: { tag: "ha-config-zwave", diff --git a/src/panels/config/person/ha-config-person.ts b/src/panels/config/person/ha-config-person.ts index bc9ea04ca2..470e9de8b5 100644 --- a/src/panels/config/person/ha-config-person.ts +++ b/src/panels/config/person/ha-config-person.ts @@ -180,9 +180,9 @@ class HaConfigPerson extends LitElement { }, removeEntry: async () => { if ( - !confirm(`Are you sure you want to delete this area? + !confirm(`Are you sure you want to delete this person? -All devices in this area will become unassigned.`) +All devices belonging to this person will become unassigned.`) ) { return false; } diff --git a/src/panels/config/zha/ha-config-zha.ts b/src/panels/config/zha/ha-config-zha.ts index 1708b9f5a8..0c96d858a5 100755 --- a/src/panels/config/zha/ha-config-zha.ts +++ b/src/panels/config/zha/ha-config-zha.ts @@ -1,26 +1,26 @@ -import "@polymer/app-layout/app-header/app-header"; -import "@polymer/app-layout/app-toolbar/app-toolbar"; +import "../../../components/ha-paper-icon-button-arrow-prev"; +import "../../../layouts/hass-subpage"; +import "./zha-binding"; +import "./zha-cluster-attributes"; +import "./zha-cluster-commands"; +import "./zha-network"; +import "./zha-node"; +import "@polymer/paper-icon-button/paper-icon-button"; + import { + CSSResult, html, LitElement, property, PropertyValues, TemplateResult, - CSSResult, } from "lit-element"; -import "@polymer/paper-icon-button/paper-icon-button"; + import { HASSDomEvent } from "../../../common/dom/fire_event"; -import { Cluster, ZHADevice, fetchBindableDevices } from "../../../data/zha"; -import "../../../layouts/ha-app-layout"; -import "../../../components/ha-paper-icon-button-arrow-prev"; +import { Cluster, fetchBindableDevices, ZHADevice } from "../../../data/zha"; import { haStyle } from "../../../resources/styles"; import { HomeAssistant } from "../../../types"; import { ZHAClusterSelectedParams, ZHADeviceSelectedParams } from "./types"; -import "./zha-cluster-attributes"; -import "./zha-cluster-commands"; -import "./zha-network"; -import "./zha-node"; -import "./zha-binding"; export class HaConfigZha extends LitElement { @property() public hass?: HomeAssistant; @@ -38,16 +38,7 @@ export class HaConfigZha extends LitElement { protected render(): TemplateResult | void { return html` - - - - -
Zigbee Home Automation
-
-
- + ` : ""} -
+ `; } @@ -117,10 +108,6 @@ export class HaConfigZha extends LitElement { static get styles(): CSSResult[] { return [haStyle]; } - - private _onBackTapped(): void { - history.back(); - } } declare global { diff --git a/src/panels/config/zha/types.ts b/src/panels/config/zha/types.ts index 5e531c98aa..c785614efb 100644 --- a/src/panels/config/zha/types.ts +++ b/src/panels/config/zha/types.ts @@ -8,6 +8,12 @@ export interface ItemSelectedEvent { target?: PickerTarget; } +export interface ZHADeviceRemovedEvent { + detail?: { + device?: ZHADevice; + }; +} + export interface ChangeEvent { detail?: { value?: any; @@ -22,7 +28,7 @@ export interface SetAttributeServiceData { cluster_type: string; attribute: number; value: any; - manufacturer: number; + manufacturer?: number; } export interface IssueCommandServiceData { diff --git a/src/panels/config/zha/zha-add-devices-page.ts b/src/panels/config/zha/zha-add-devices-page.ts new file mode 100644 index 0000000000..9ebcce185a --- /dev/null +++ b/src/panels/config/zha/zha-add-devices-page.ts @@ -0,0 +1,246 @@ +import "../../../components/ha-service-description"; +import "../../../components/ha-textarea"; +import "../../../layouts/hass-subpage"; +import "./zha-device-card"; +import "@material/mwc-button"; +import "@polymer/paper-icon-button/paper-icon-button"; +import "@polymer/paper-spinner/paper-spinner"; + +import { + css, + CSSResult, + customElement, + html, + LitElement, + property, + TemplateResult, +} from "lit-element"; + +import { ZHADevice } from "../../../data/zha"; +import { haStyle } from "../../../resources/styles"; +import { HomeAssistant } from "../../../types"; + +@customElement("zha-add-devices-page") +class ZHAAddDevicesPage extends LitElement { + @property() public hass!: HomeAssistant; + @property() public isWide?: boolean; + @property() private _error?: string; + @property() private _discoveredDevices: ZHADevice[] = []; + @property() private _formattedEvents: string = ""; + @property() private _active: boolean = false; + @property() private _showHelp: boolean = false; + private _addDevicesTimeoutHandle: any = undefined; + private _subscribed?: Promise<() => Promise>; + + public connectedCallback(): void { + super.connectedCallback(); + this._subscribe(); + } + + public disconnectedCallback(): void { + super.disconnectedCallback(); + this._unsubscribe(); + this._error = undefined; + this._discoveredDevices = []; + this._formattedEvents = ""; + } + + protected render(): TemplateResult | void { + return html` + + ${this._active + ? html` +

+ + ${this.hass!.localize( + "ui.panel.config.zha.add_device_page.spinner" + )} +

+ ` + : html` +
+ + Search again + + + ${this._showHelp + ? html` + + ` + : ""} +
+ `} + ${this._error + ? html` +
${this._error}
+ ` + : ""} +
+
+ ${this._discoveredDevices.length < 1 + ? html` +
+

+ ${this.hass!.localize( + "ui.panel.config.zha.add_device_page.discovery_text" + )} +

+
+ ` + : html` + ${this._discoveredDevices.map( + (device) => html` + + ` + )} + `} +
+ + +
+ `; + } + + private _handleMessage(message: any): void { + if (message.type === "log_output") { + this._formattedEvents += message.log_entry.message + "\n"; + if (this.shadowRoot) { + const textArea = this.shadowRoot.querySelector("ha-textarea"); + if (textArea) { + textArea.scrollTop = textArea.scrollHeight; + } + } + } + if (message.type && message.type === "device_fully_initialized") { + this._discoveredDevices.push(message.device_info); + } + } + + private _unsubscribe(): void { + this._active = false; + if (this._addDevicesTimeoutHandle) { + clearTimeout(this._addDevicesTimeoutHandle); + } + if (this._subscribed) { + this._subscribed.then((unsub) => unsub()); + this._subscribed = undefined; + } + } + + private _subscribe(): void { + this._subscribed = this.hass!.connection.subscribeMessage( + (message) => this._handleMessage(message), + { type: "zha/devices/permit" } + ); + this._active = true; + this._addDevicesTimeoutHandle = setTimeout( + () => this._unsubscribe(), + 60000 + ); + } + + private _onHelpTap(): void { + this._showHelp = !this._showHelp; + } + + static get styles(): CSSResult[] { + return [ + haStyle, + css` + .discovery-text, + .content-header { + margin: 16px; + } + .content { + border-top: 1px solid var(--light-primary-color); + min-height: 500px; + display: flex; + flex-wrap: wrap; + padding: 4px; + justify-content: left; + overflow: scroll; + } + .error { + color: var(--google-red-500); + } + paper-spinner { + display: none; + margin-right: 20px; + margin-left: 16px; + } + paper-spinner[active] { + display: block; + float: left; + margin-right: 20px; + margin-left: 16px; + } + .card { + margin-left: 16px; + margin-right: 16px; + margin-bottom: 0px; + margin-top: 10px; + } + .events { + margin: 16px; + border-top: 1px solid var(--light-primary-color); + padding-top: 16px; + min-height: 200px; + max-height: 200px; + overflow: scroll; + } + .toggle-help-icon { + position: absolute; + margin-top: 16px; + margin-right: 16px; + top: -6px; + right: 0; + color: var(--primary-color); + } + ha-service-description { + margin-top: 16px; + margin-left: 16px; + display: block; + color: grey; + } + .search-button { + margin-top: 16px; + margin-left: 16px; + } + .help-text { + color: grey; + padding-left: 16px; + } + `, + ]; + } +} + +declare global { + interface HTMLElementTagNameMap { + "zha-add-devices-page": ZHAAddDevicesPage; + } +} diff --git a/src/panels/config/zha/zha-binding.ts b/src/panels/config/zha/zha-binding.ts index 3394262a83..a2bc43c247 100644 --- a/src/panels/config/zha/zha-binding.ts +++ b/src/panels/config/zha/zha-binding.ts @@ -1,20 +1,26 @@ +import "../../../components/buttons/ha-call-service-button"; +import "../../../components/ha-service-description"; +import "../ha-config-section"; +import "@material/mwc-button/mwc-button"; +import "@polymer/paper-card/paper-card"; +import "@polymer/paper-dropdown-menu/paper-dropdown-menu"; +import "@polymer/paper-icon-button/paper-icon-button"; +import "@polymer/paper-listbox/paper-listbox"; + import { + css, + CSSResult, + customElement, html, LitElement, property, PropertyValues, TemplateResult, - CSSResult, - css, - customElement, } from "lit-element"; -import "@polymer/paper-card/paper-card"; -import "../../../components/buttons/ha-call-service-button"; -import "../../../components/ha-service-description"; -import { ZHADevice, bindDevices, unbindDevices } from "../../../data/zha"; + +import { bindDevices, unbindDevices, ZHADevice } from "../../../data/zha"; import { haStyle } from "../../../resources/styles"; import { HomeAssistant } from "../../../types"; -import "../ha-config-section"; import { ItemSelectedEvent } from "./types"; @customElement("zha-binding-control") diff --git a/src/panels/config/zha/zha-cluster-attributes.ts b/src/panels/config/zha/zha-cluster-attributes.ts index c59126cbf2..10f4a3677e 100644 --- a/src/panels/config/zha/zha-cluster-attributes.ts +++ b/src/panels/config/zha/zha-cluster-attributes.ts @@ -1,17 +1,24 @@ +import "../../../components/buttons/ha-call-service-button"; +import "../../../components/ha-service-description"; +import "../ha-config-section"; +import "@material/mwc-button"; +import "@polymer/paper-card/paper-card"; +import "@polymer/paper-dropdown-menu/paper-dropdown-menu"; +import "@polymer/paper-icon-button/paper-icon-button"; +import "@polymer/paper-input/paper-input"; +import "@polymer/paper-item/paper-item"; +import "@polymer/paper-listbox/paper-listbox"; + import { + css, + CSSResult, html, LitElement, PropertyDeclarations, PropertyValues, TemplateResult, - CSSResult, - css, } from "lit-element"; -import "@material/mwc-button"; -import "@polymer/paper-card/paper-card"; -import "@polymer/paper-icon-button/paper-icon-button"; -import "../../../components/buttons/ha-call-service-button"; -import "../../../components/ha-service-description"; + import { Attribute, Cluster, @@ -22,13 +29,12 @@ import { } from "../../../data/zha"; import { haStyle } from "../../../resources/styles"; import { HomeAssistant } from "../../../types"; -import "../ha-config-section"; +import { formatAsPaddedHex } from "./functions"; import { ChangeEvent, ItemSelectedEvent, SetAttributeServiceData, } from "./types"; -import { formatAsPaddedHex } from "./functions"; export class ZHAClusterAttributes extends LitElement { public hass?: HomeAssistant; @@ -115,7 +121,7 @@ export class ZHAClusterAttributes extends LitElement { ${this.showHelp ? html` -
+
Select an attribute to view or set its value
` @@ -152,6 +158,13 @@ export class ZHAClusterAttributes extends LitElement { Get Zigbee Attribute + ${this.showHelp + ? html` +
+ Get the value for the selected attribute +
+ ` + : ""} ` : ""} @@ -201,7 +215,7 @@ export class ZHAClusterAttributes extends LitElement { attribute: this._attributes[this._selectedAttributeIndex].id, manufacturer: this._manufacturerCodeOverride ? parseInt(this._manufacturerCodeOverride as string, 10) - : this.selectedNode!.manufacturer_code, + : undefined, }; } @@ -220,7 +234,7 @@ export class ZHAClusterAttributes extends LitElement { value: this._attributeValue, manufacturer: this._manufacturerCodeOverride ? parseInt(this._manufacturerCodeOverride as string, 10) - : this.selectedNode!.manufacturer_code, + : undefined, }; } @@ -312,6 +326,16 @@ export class ZHAClusterAttributes extends LitElement { [hidden] { display: none; } + .help-text { + color: grey; + padding-left: 28px; + padding-right: 28px; + padding-bottom: 16px; + } + .help-text2 { + color: grey; + padding: 16px; + } `, ]; } diff --git a/src/panels/config/zha/zha-cluster-commands.ts b/src/panels/config/zha/zha-cluster-commands.ts index 28a68af9ab..d8bbcac2e4 100644 --- a/src/panels/config/zha/zha-cluster-commands.ts +++ b/src/panels/config/zha/zha-cluster-commands.ts @@ -1,15 +1,23 @@ +import "../../../components/buttons/ha-call-service-button"; +import "../../../components/ha-service-description"; +import "../ha-config-section"; +import "@polymer/paper-card/paper-card"; +import "@polymer/paper-dropdown-menu/paper-dropdown-menu"; +import "@polymer/paper-icon-button/paper-icon-button"; +import "@polymer/paper-input/paper-input"; +import "@polymer/paper-item/paper-item"; +import "@polymer/paper-listbox/paper-listbox"; + import { + css, + CSSResult, html, LitElement, PropertyDeclarations, PropertyValues, TemplateResult, - CSSResult, - css, } from "lit-element"; -import "@polymer/paper-card/paper-card"; -import "../../../components/buttons/ha-call-service-button"; -import "../../../components/ha-service-description"; + import { Cluster, Command, @@ -18,13 +26,12 @@ import { } from "../../../data/zha"; import { haStyle } from "../../../resources/styles"; import { HomeAssistant } from "../../../types"; -import "../ha-config-section"; +import { formatAsPaddedHex } from "./functions"; import { ChangeEvent, IssueCommandServiceData, ItemSelectedEvent, } from "./types"; -import { formatAsPaddedHex } from "./functions"; export class ZHAClusterCommands extends LitElement { public hass?: HomeAssistant; @@ -107,7 +114,7 @@ export class ZHAClusterCommands extends LitElement {
${this._showHelp ? html` -
Select a command to interact with
+
Select a command to interact with
` : ""} ${this._selectedCommandIndex !== -1 @@ -135,6 +142,7 @@ export class ZHAClusterCommands extends LitElement { .hass="${this.hass}" domain="zha" service="issue_zigbee_cluster_command" + class="help-text2" > ` : ""} @@ -242,7 +250,14 @@ export class ZHAClusterCommands extends LitElement { position: relative; } - .helpText { + .help-text { + color: grey; + padding-left: 28px; + padding-right: 28px; + padding-bottom: 16px; + } + + .help-text2 { color: grey; padding: 16px; } diff --git a/src/panels/config/zha/zha-clusters.ts b/src/panels/config/zha/zha-clusters.ts index bbabf3e2fa..512cc16d7d 100644 --- a/src/panels/config/zha/zha-clusters.ts +++ b/src/panels/config/zha/zha-clusters.ts @@ -1,22 +1,27 @@ +import "../../../components/buttons/ha-call-service-button"; +import "../../../components/ha-service-description"; +import "../ha-config-section"; +import "@polymer/paper-card/paper-card"; +import "@polymer/paper-dropdown-menu/paper-dropdown-menu"; +import "@polymer/paper-item/paper-item"; +import "@polymer/paper-listbox/paper-listbox"; + import { + css, + CSSResult, html, LitElement, PropertyDeclarations, PropertyValues, TemplateResult, - CSSResult, - css, } from "lit-element"; -import "@polymer/paper-card/paper-card"; + import { fireEvent } from "../../../common/dom/fire_event"; -import "../../../components/buttons/ha-call-service-button"; -import "../../../components/ha-service-description"; import { Cluster, fetchClustersForZhaNode, ZHADevice } from "../../../data/zha"; import { haStyle } from "../../../resources/styles"; import { HomeAssistant } from "../../../types"; -import "../ha-config-section"; -import { ItemSelectedEvent } from "./types"; import { formatAsPaddedHex } from "./functions"; +import { ItemSelectedEvent } from "./types"; declare global { // for fire event @@ -90,7 +95,7 @@ export class ZHAClusters extends LitElement { ${this.showHelp ? html` -
+
Select cluster to view attributes and commands
` @@ -143,9 +148,11 @@ export class ZHAClusters extends LitElement { padding-right: 28px; padding-bottom: 10px; } - .helpText { + .help-text { color: grey; - padding: 16px; + padding-left: 28px; + padding-right: 28px; + padding-bottom: 16px; } `, ]; diff --git a/src/panels/config/zha/zha-config-panel.ts b/src/panels/config/zha/zha-config-panel.ts new file mode 100644 index 0000000000..114c0f20ff --- /dev/null +++ b/src/panels/config/zha/zha-config-panel.ts @@ -0,0 +1,70 @@ +import "../../../layouts/hass-loading-screen"; + +import { customElement, property } from "lit-element"; + +import { listenMediaQuery } from "../../../common/dom/media_query"; +import { + HassRouterPage, + RouterOptions, +} from "../../../layouts/hass-router-page"; +import { HomeAssistant } from "../../../types"; + +@customElement("zha-config-panel") +class ZHAConfigPanel extends HassRouterPage { + @property() public hass!: HomeAssistant; + @property() public _wideSidebar: boolean = false; + @property() public _wide: boolean = false; + + protected routerOptions: RouterOptions = { + defaultPage: "configuration", + cacheAll: true, + preloadAll: true, + routes: { + configuration: { + tag: "ha-config-zha", + load: () => + import(/* webpackChunkName: "zha-configuration-page" */ "./ha-config-zha"), + }, + add: { + tag: "zha-add-devices-page", + load: () => + import(/* webpackChunkName: "zha-add-devices-page" */ "./zha-add-devices-page"), + }, + }, + }; + + private _listeners: Array<() => void> = []; + + public connectedCallback(): void { + super.connectedCallback(); + this._listeners.push( + listenMediaQuery("(min-width: 1040px)", (matches) => { + this._wide = matches; + }) + ); + this._listeners.push( + listenMediaQuery("(min-width: 1296px)", (matches) => { + this._wideSidebar = matches; + }) + ); + } + + public disconnectedCallback(): void { + super.disconnectedCallback(); + while (this._listeners.length) { + this._listeners.pop()!(); + } + } + + protected updatePageEl(el): void { + el.route = this.routeTail; + el.hass = this.hass; + el.isWide = this.hass.dockedSidebar ? this._wideSidebar : this._wide; + } +} + +declare global { + interface HTMLElementTagNameMap { + "zha-config-panel": ZHAConfigPanel; + } +} diff --git a/src/panels/config/zha/zha-device-card.ts b/src/panels/config/zha/zha-device-card.ts index d68d724ecb..c37d3902d9 100644 --- a/src/panels/config/zha/zha-device-card.ts +++ b/src/panels/config/zha/zha-device-card.ts @@ -1,40 +1,123 @@ +import "../../../components/buttons/ha-call-service-button"; +import "../../../components/entity/state-badge"; +import "@material/mwc-button"; +import "@polymer/paper-card/paper-card"; +import "@polymer/paper-dropdown-menu/paper-dropdown-menu"; +import "@polymer/paper-input/paper-input"; +import "@polymer/paper-item/paper-icon-item"; +import "@polymer/paper-item/paper-item"; +import "@polymer/paper-item/paper-item-body"; +import "@polymer/paper-listbox/paper-listbox"; + import { + css, + CSSResult, + customElement, html, LitElement, property, + PropertyValues, TemplateResult, - CSSResult, - css, } from "lit-element"; -import "@polymer/paper-item/paper-icon-item"; -import "@polymer/paper-item/paper-item-body"; -import "@polymer/paper-card/paper-card"; -import "@polymer/paper-dropdown-menu/paper-dropdown-menu"; -import "@polymer/paper-item/paper-item"; -import "@polymer/paper-listbox/paper-listbox"; + import { fireEvent } from "../../../common/dom/fire_event"; +import compare from "../../../common/string/compare"; +import { + AreaRegistryEntry, + fetchAreaRegistry, +} from "../../../data/area_registry"; +import { + DeviceRegistryEntryMutableParams, + updateDeviceRegistryEntry, +} from "../../../data/device_registry"; +import { reconfigureNode, ZHADevice } from "../../../data/zha"; import { haStyle } from "../../../resources/styles"; import { HomeAssistant } from "../../../types"; +import { ItemSelectedEvent, NodeServiceData } from "./types"; -import "../../../components/entity/state-badge"; -import { ZHADevice } from "../../../data/zha"; +declare global { + // for fire event + interface HASSDomEvents { + "zha-device-removed": { + device?: ZHADevice; + }; + } +} +@customElement("zha-device-card") class ZHADeviceCard extends LitElement { @property() public hass?: HomeAssistant; @property() public narrow?: boolean; @property() public device?: ZHADevice; + @property() public showHelp: boolean = false; + @property() public showActions?: boolean; + @property() public isJoinPage?: boolean; + @property() private _serviceData?: NodeServiceData; + @property() private _areas: AreaRegistryEntry[] = []; + @property() private _selectedAreaIndex: number = -1; + + public firstUpdated(changedProperties: PropertyValues): void { + super.firstUpdated(changedProperties); + this.addEventListener("hass-service-called", (ev) => + this.serviceCalled(ev) + ); + this._serviceData = { + ieee_address: this.device!.ieee, + }; + fetchAreaRegistry(this.hass!).then((areas) => { + this._areas = areas.sort((a, b) => compare(a.name, b.name)); + }); + } + + protected updated(changedProperties: PropertyValues): void { + if (changedProperties.has("device")) { + this._selectedAreaIndex = + this._areas.findIndex((area) => area.area_id === this.device!.area_id) + + 1; + } + super.update(changedProperties); + } + + protected serviceCalled(ev): void { + // Check if this is for us + if (ev.detail.success && ev.detail.service === "remove") { + fireEvent(this, "zha-device-removed", { + device: this.device, + }); + } + } protected render(): TemplateResult | void { return html` - + + ${ + this.isJoinPage + ? html` +
+
${this.device!.model}
+
+ ${this.hass!.localize( + "ui.panel.config.integrations.config_entry.manuf", + "manufacturer", + this.device!.manufacturer + )} +
+
+ ` + : "" + }
-
IEEE:
-
${this.device!.ieee}
-
Quirk applied:
-
${this.device!.quirk_applied}
-
Quirk:
-
${this.device!.quirk_class}
+
IEEE:
+
${this.device!.ieee}
+ ${ + this.device!.quirk_applied + ? html` +
Quirk:
+
${this.device!.quirk_class}
+ ` + : "" + }
@@ -49,24 +132,145 @@ class ZHADeviceCard extends LitElement { .stateObj="${this.hass!.states[entity.entity_id]}" slot="item-icon" > - -
${entity.name}
-
${entity.entity_id}
-
+ ${!this.isJoinPage + ? html` + +
${entity.name}
+
+ ${entity.entity_id} +
+
+ ` + : ""} ` )}
+
+ +
+
+ + + + ${this.hass!.localize( + "ui.panel.config.integrations.config_entry.no_area" + )} + + + ${this._areas.map( + (entry) => html` + ${entry.name} + ` + )} + + +
+ ${ + this.showActions + ? html` +
+ Reconfigure Device + ${this.showHelp + ? html` +
+ ${this.hass!.localize( + "ui.panel.config.zha.services.reconfigure" + )} +
+ ` + : ""} + + Remove Device + ${this.showHelp + ? html` +
+ ${this.hass!.localize( + "ui.panel.config.zha.services.remove" + )} +
+ ` + : ""} +
+ ` + : "" + } + `; } + private async _onReconfigureNodeClick(): Promise { + if (this.hass) { + await reconfigureNode(this.hass, this.device!.ieee); + } + } + + private async _saveCustomName(event): Promise { + if (this.hass) { + const values: DeviceRegistryEntryMutableParams = { + name_by_user: event.target.value, + area_id: this.device!.area_id ? this.device!.area_id : undefined, + }; + + await updateDeviceRegistryEntry( + this.hass, + this.device!.device_reg_id, + values + ); + + this.device!.user_given_name = event.target.value; + } + } + private _openMoreInfo(ev: MouseEvent): void { fireEvent(this, "hass-more-info", { entityId: (ev.currentTarget as any).entity.entity_id, }); } + private async _selectedAreaChanged(event: ItemSelectedEvent) { + if (!this.device || !this._areas) { + return; + } + this._selectedAreaIndex = event!.target!.selected; + const area = this._areas[this._selectedAreaIndex - 1]; // account for No Area + if ( + (!area && !this.device.area_id) || + (area && area.area_id === this.device.area_id) + ) { + return; + } + + await updateDeviceRegistryEntry(this.hass!, this.device.device_reg_id, { + area_id: area ? area.area_id : undefined, + name_by_user: this.device!.user_given_name, + }); + } + static get styles(): CSSResult[] { return [ haStyle, @@ -74,29 +278,43 @@ class ZHADeviceCard extends LitElement { :host(:not([narrow])) .device-entities { max-height: 225px; overflow: auto; + display: flex; + flex-wrap: wrap; + padding: 4px; + justify-content: left; } paper-card { flex: 1 0 100%; padding-bottom: 10px; - min-width: 0; + min-width: 425px; } .device { width: 30%; } - .label { + .device .name { font-weight: bold; } + .device .manuf { + color: var(--secondary-text-color); + } + .extra-info { + margin-top: 8px; + } + .manuf, + .zha-info, + .entity-id { + color: var(--secondary-text-color); + } .info { - color: var(--secondary-text-color); - font-weight: bold; + margin-left: 16px; } dl dt { + padding-left: 12px; float: left; - width: 100px; + width: 50px; text-align: left; } dt dd { - margin-left: 10px; text-align: left; } paper-icon-item { @@ -104,6 +322,36 @@ class ZHADeviceCard extends LitElement { padding-top: 4px; padding-bottom: 4px; } + .editable { + padding-left: 28px; + padding-right: 28px; + padding-bottom: 10px; + } + .help-text { + color: grey; + padding: 16px; + } + .flex { + -ms-flex: 1 1 0.000000001px; + -webkit-flex: 1; + flex: 1; + -webkit-flex-basis: 0.000000001px; + flex-basis: 0.000000001px; + } + .node-picker { + display: -ms-flexbox; + display: -webkit-flex; + display: flex; + -ms-flex-direction: row; + -webkit-flex-direction: row; + flex-direction: row; + -ms-flex-align: center; + -webkit-align-items: center; + align-items: center; + padding-left: 28px; + padding-right: 28px; + padding-bottom: 10px; + } `, ]; } @@ -114,5 +362,3 @@ declare global { "zha-device-card": ZHADeviceCard; } } - -customElements.define("zha-device-card", ZHADeviceCard); diff --git a/src/panels/config/zha/zha-network.ts b/src/panels/config/zha/zha-network.ts index 87cf043838..b6c92d694e 100644 --- a/src/panels/config/zha/zha-network.ts +++ b/src/panels/config/zha/zha-network.ts @@ -1,19 +1,22 @@ +import "../../../components/buttons/ha-call-service-button"; +import "../../../components/ha-service-description"; +import "../ha-config-section"; +import "@material/mwc-button"; +import "@polymer/paper-card/paper-card"; +import "@polymer/paper-icon-button/paper-icon-button"; + import { + css, + CSSResult, html, LitElement, PropertyDeclarations, TemplateResult, - CSSResult, - css, } from "lit-element"; -import "@material/mwc-button"; -import "@polymer/paper-card/paper-card"; -import "@polymer/paper-icon-button/paper-icon-button"; -import "../../../components/buttons/ha-call-service-button"; -import "../../../components/ha-service-description"; + +import { navigate } from "../../../common/navigate"; import { haStyle } from "../../../resources/styles"; import { HomeAssistant } from "../../../types"; -import "../ha-config-section"; export class ZHANetwork extends LitElement { public hass?: HomeAssistant; @@ -30,6 +33,7 @@ export class ZHANetwork extends LitElement { hass: {}, isWide: {}, _showHelp: {}, + _joinParams: {}, }; } @@ -37,29 +41,31 @@ export class ZHANetwork extends LitElement { return html`
- Network Management - + Network Management +
Commands that affect entire network -
- Permit - ${ - this._showHelp - ? html` - - ` - : "" - } +
+ + Add Devices + + ${this._showHelp + ? html` + + ` + : ""} +
`; @@ -69,6 +75,10 @@ export class ZHANetwork extends LitElement { this._showHelp = !this._showHelp; } + private _onAddDevicesClick() { + navigate(this, "add"); + } + static get styles(): CSSResult[] { return [ haStyle, @@ -102,6 +112,11 @@ export class ZHANetwork extends LitElement { [hidden] { display: none; } + + .help-text2 { + color: grey; + padding: 16px; + } `, ]; } diff --git a/src/panels/config/zha/zha-node.ts b/src/panels/config/zha/zha-node.ts index 959c7bc594..39ab5a1181 100644 --- a/src/panels/config/zha/zha-node.ts +++ b/src/panels/config/zha/zha-node.ts @@ -1,32 +1,30 @@ -import { - html, - LitElement, - PropertyDeclarations, - TemplateResult, - CSSResult, - PropertyValues, - css, -} from "lit-element"; +import "../../../components/buttons/ha-call-service-button"; +import "../../../components/ha-service-description"; +import "../ha-config-section"; +import "./zha-clusters"; +import "./zha-device-card"; import "@material/mwc-button"; import "@polymer/paper-card/paper-card"; import "@polymer/paper-icon-button/paper-icon-button"; import "@polymer/paper-input/paper-input"; import "@polymer/paper-item/paper-item"; import "@polymer/paper-listbox/paper-listbox"; + +import { + css, + CSSResult, + customElement, + html, + LitElement, + property, + TemplateResult, +} from "lit-element"; + import { fireEvent } from "../../../common/dom/fire_event"; -import "../../../components/buttons/ha-call-service-button"; -import "../../../components/ha-service-description"; +import { fetchDevices, ZHADevice } from "../../../data/zha"; import { haStyle } from "../../../resources/styles"; import { HomeAssistant } from "../../../types"; -import "../ha-config-section"; -import { ItemSelectedEvent, NodeServiceData, ChangeEvent } from "./types"; -import "./zha-clusters"; -import "./zha-device-card"; -import { - updateDeviceRegistryEntry, - DeviceRegistryEntryMutableParams, -} from "../../../data/device_registry"; -import { reconfigureNode, fetchDevices, ZHADevice } from "../../../data/zha"; +import { ItemSelectedEvent, ZHADeviceRemovedEvent } from "./types"; declare global { // for fire event @@ -37,60 +35,25 @@ declare global { } } +@customElement("zha-node") export class ZHANode extends LitElement { - public hass?: HomeAssistant; - public isWide?: boolean; - private _showHelp: boolean; - private _selectedNodeIndex: number; - private _selectedNode?: ZHADevice; - private _serviceData?: {}; - private _nodes: ZHADevice[]; - private _userSelectedName?: string; + @property() public hass?: HomeAssistant; + @property() public isWide?: boolean; + @property() private _showHelp: boolean = false; + @property() private _selectedDeviceIndex: number = -1; + @property() private _selectedDevice?: ZHADevice; + @property() private _nodes: ZHADevice[] = []; - constructor() { - super(); - this._showHelp = false; - this._selectedNodeIndex = -1; - this._nodes = []; - } - - static get properties(): PropertyDeclarations { - return { - hass: {}, - isWide: {}, - _showHelp: {}, - _selectedNodeIndex: {}, - _selectedNode: {}, - _entities: {}, - _serviceData: {}, - _nodes: {}, - _userSelectedName: {}, - }; - } - - public firstUpdated(changedProperties: PropertyValues): void { - super.firstUpdated(changedProperties); - if (this._nodes.length === 0) { - this._fetchDevices(); - } - this.addEventListener("hass-service-called", (ev) => - this.serviceCalled(ev) - ); - } - - protected serviceCalled(ev): void { - // Check if this is for us - if (ev.detail.success && ev.detail.service === "remove") { - this._selectedNodeIndex = -1; - this._fetchDevices(); - } + public connectedCallback(): void { + super.connectedCallback(); + this._fetchDevices(); } protected render(): TemplateResult | void { return html`
- Node Management + Device Management
- Run ZHA commands that affect a single node. Pick a node to see a list - of available commands.

Note: Sleepy (battery powered) + Run ZHA commands that affect a single device. Pick a device to see a + list of available commands.

Note: Sleepy (battery powered) devices need to be awake when executing commands against them. You can generally wake a sleepy device by triggering it.

Some devices such as Xiaomi sensors have a wake up button that you can @@ -108,11 +71,15 @@ export class ZHANode extends LitElement {
- + ${this._nodes.map( (entry) => html` @@ -128,95 +95,36 @@ export class ZHANode extends LitElement {
${this._showHelp ? html` -
- Select node to view per-node options +
+ Select device to view per-device options
` : ""} - ${this._selectedNodeIndex !== -1 + ${this._selectedDeviceIndex !== -1 ? html` ` : ""} - ${this._selectedNodeIndex !== -1 - ? html` -
- -
- ` - : ""} - ${this._selectedNodeIndex !== -1 ? this._renderNodeActions() : ""} - ${this._selectedNode ? this._renderClusters() : ""} + ${this._selectedDevice ? this._renderClusters() : ""} `; } - private _renderNodeActions(): TemplateResult { - return html` -
- Reconfigure Node - ${this._showHelp - ? html` -
- ${this.hass!.localize( - "ui.panel.config.zha.services.reconfigure" - )} -
- ` - : ""} - Remove Node - ${this._showHelp - ? html` - - ` - : ""} - Update Name - ${this._showHelp - ? html` -
- ${this.hass!.localize( - "ui.panel.config.zha.services.updateDeviceName" - )} -
- ` - : ""} -
- `; - } - private _renderClusters(): TemplateResult { return html` `; @@ -226,45 +134,10 @@ export class ZHANode extends LitElement { this._showHelp = !this._showHelp; } - private _selectedNodeChanged(event: ItemSelectedEvent): void { - this._selectedNodeIndex = event!.target!.selected; - this._selectedNode = this._nodes[this._selectedNodeIndex]; - this._userSelectedName = ""; - fireEvent(this, "zha-node-selected", { node: this._selectedNode }); - this._serviceData = this._computeNodeServiceData(); - } - - private async _onReconfigureNodeClick(): Promise { - if (this.hass) { - await reconfigureNode(this.hass, this._selectedNode!.ieee); - } - } - - private _onUserSelectedNameChanged(value: ChangeEvent): void { - this._userSelectedName = value.detail!.value; - } - - private async _onUpdateDeviceNameClick(): Promise { - if (this.hass) { - const values: DeviceRegistryEntryMutableParams = { - name_by_user: this._userSelectedName, - }; - - await updateDeviceRegistryEntry( - this.hass, - this._selectedNode!.device_reg_id, - values - ); - - this._selectedNode!.user_given_name = this._userSelectedName!; - this._userSelectedName = ""; - } - } - - private _computeNodeServiceData(): NodeServiceData { - return { - ieee_address: this._selectedNode!.ieee, - }; + private _selectedDeviceChanged(event: ItemSelectedEvent): void { + this._selectedDeviceIndex = event!.target!.selected; + this._selectedDevice = this._nodes[this._selectedDeviceIndex]; + fireEvent(this, "zha-node-selected", { node: this._selectedDevice }); } private async _fetchDevices() { @@ -273,6 +146,13 @@ export class ZHANode extends LitElement { }); } + private _onDeviceRemoved(event: ZHADeviceRemovedEvent): void { + this._selectedDeviceIndex = -1; + this._nodes.splice(this._nodes.indexOf(event.detail!.device!), 1); + this._selectedDevice = undefined; + fireEvent(this, "zha-node-selected", { node: this._selectedDevice }); + } + static get styles(): CSSResult[] { return [ haStyle, @@ -298,13 +178,10 @@ export class ZHANode extends LitElement { } .help-text { + color: grey; padding-left: 28px; padding-right: 28px; - } - - .helpText { - color: grey; - padding: 16px; + padding-bottom: 16px; } paper-card { @@ -355,12 +232,6 @@ export class ZHANode extends LitElement { right: 0; color: var(--primary-color); } - - .input-text { - padding-left: 28px; - padding-right: 28px; - padding-bottom: 10px; - } `, ]; } @@ -371,5 +242,3 @@ declare global { "zha-node": ZHANode; } } - -customElements.define("zha-node", ZHANode); diff --git a/src/panels/custom/ha-panel-custom.js b/src/panels/custom/ha-panel-custom.ts similarity index 52% rename from src/panels/custom/ha-panel-custom.js rename to src/panels/custom/ha-panel-custom.ts index 7571d697c5..55bb613ad6 100644 --- a/src/panels/custom/ha-panel-custom.js +++ b/src/panels/custom/ha-panel-custom.ts @@ -1,50 +1,69 @@ -import { PolymerElement } from "@polymer/polymer/polymer-element"; +import { property, PropertyValues, UpdatingElement } from "lit-element"; +import { loadCustomPanel } from "../../util/custom-panel/load-custom-panel"; +import { createCustomPanelElement } from "../../util/custom-panel/create-custom-panel-element"; +import { setCustomPanelProperties } from "../../util/custom-panel/set-custom-panel-properties"; +import { HomeAssistant, Route, Panel } from "../../types"; +import { CustomPanelConfig } from "../../data/panel_custom"; -import EventsMixin from "../../mixins/events-mixin"; -import NavigateMixin from "../../mixins/navigate-mixin"; -import loadCustomPanel from "../../util/custom-panel/load-custom-panel"; -import createCustomPanelElement from "../../util/custom-panel/create-custom-panel-element"; -import setCustomPanelProperties from "../../util/custom-panel/set-custom-panel-properties"; +declare global { + interface Window { + customPanel: HaPanelCustom | undefined; + } +} -/* - * Mixins are used by ifram to communicate with main frontend. - * @appliesMixin EventsMixin - * @appliesMixin NavigateMixin - */ -class HaPanelCustom extends NavigateMixin(EventsMixin(PolymerElement)) { - static get properties() { - return { - hass: Object, - narrow: Boolean, - route: Object, - panel: { - type: Object, - observer: "_panelChanged", - }, - }; +export class HaPanelCustom extends UpdatingElement { + @property() public hass!: HomeAssistant; + @property() public narrow!: boolean; + @property() public route!: Route; + @property() public panel!: Panel; + private _setProperties?: (props: {}) => void | undefined; + + public registerIframe(initialize, setProperties) { + initialize(this.panel, { + hass: this.hass, + narrow: this.narrow, + route: this.route, + }); + this._setProperties = setProperties; } - static get observers() { - return ["_dataChanged(hass, narrow, route)"]; + public disconnectedCallback() { + super.disconnectedCallback(); + this._cleanupPanel(); } - constructor() { - super(); - this._setProperties = null; + protected updated(changedProps: PropertyValues) { + if (changedProps.has("panel")) { + // Clean up old things if we had a panel + if (changedProps.get("panel")) { + this._cleanupPanel(); + } + this._createPanel(this.panel); + return; + } + if (!this._setProperties) { + return; + } + const props = {}; + for (const key of changedProps.keys()) { + props[key] = this[key]; + } + this._setProperties(props); } - _panelChanged(panel) { - // Clean up + private _cleanupPanel() { delete window.customPanel; - this._setProperties = null; + this._setProperties = undefined; while (this.lastChild) { this.removeChild(this.lastChild); } + } - const config = panel.config._panel_custom; + private _createPanel(panel: Panel) { + const config = panel.config!._panel_custom as CustomPanelConfig; const tempA = document.createElement("a"); - tempA.href = config.html_url || config.js_url || config.module_url; + tempA.href = config.html_url || config.js_url || config.module_url || ""; if ( !config.trust_external && @@ -96,32 +115,13 @@ It will have access to all data in Home Assistant. `.trim(); - const iframeDoc = this.querySelector("iframe").contentWindow.document; + const iframeDoc = this.querySelector("iframe")!.contentWindow!.document; iframeDoc.open(); iframeDoc.write( `` ); iframeDoc.close(); } - - disconnectedCallback() { - super.disconnectedCallback(); - delete window.customPanel; - } - - _dataChanged(hass, narrow, route) { - if (!this._setProperties) return; - this._setProperties({ hass, narrow, route }); - } - - registerIframe(initialize, setProperties) { - initialize(this.panel, { - hass: this.hass, - narrow: this.narrow, - route: this.route, - }); - this._setProperties = setProperties; - } } customElements.define("ha-panel-custom", HaPanelCustom); diff --git a/src/panels/lovelace/cards/hui-alarm-panel-card.ts b/src/panels/lovelace/cards/hui-alarm-panel-card.ts index 7fadfbf954..8b99421f7e 100644 --- a/src/panels/lovelace/cards/hui-alarm-panel-card.ts +++ b/src/panels/lovelace/cards/hui-alarm-panel-card.ts @@ -10,17 +10,17 @@ import { } from "lit-element"; import { classMap } from "lit-html/directives/class-map"; +import "../../../components/ha-card"; +import "../../../components/ha-label-badge"; +import "../components/hui-warning"; + import { LovelaceCard } from "../types"; import { HomeAssistant } from "../../../types"; -import { LovelaceCardConfig } from "../../../data/lovelace"; import { callAlarmAction, FORMAT_NUMBER, } from "../../../data/alarm_control_panel"; - -import "../../../components/ha-card"; -import "../../../components/ha-label-badge"; -import "../components/hui-warning"; +import { AlarmPanelCardConfig } from "./types"; const ICONS = { armed_away: "hass:shield-lock", @@ -34,12 +34,6 @@ const ICONS = { const BUTTONS = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "", "0", "clear"]; -export interface Config extends LovelaceCardConfig { - entity: string; - name?: string; - states?: string[]; -} - @customElement("hui-alarm-panel-card") class HuiAlarmPanelCard extends LitElement implements LovelaceCard { public static async getConfigElement() { @@ -53,7 +47,7 @@ class HuiAlarmPanelCard extends LitElement implements LovelaceCard { @property() public hass?: HomeAssistant; - @property() private _config?: Config; + @property() private _config?: AlarmPanelCardConfig; @property() private _code?: string; @@ -69,7 +63,7 @@ class HuiAlarmPanelCard extends LitElement implements LovelaceCard { : 8; } - public setConfig(config: Config): void { + public setConfig(config: AlarmPanelCardConfig): void { if ( !config || !config.entity || @@ -305,7 +299,7 @@ class HuiAlarmPanelCard extends LitElement implements LovelaceCard { .actions mwc-button { min-width: calc(var(--base-unit) * 9); - margin: 0 4px; + margin: 0 4px 4px; } mwc-button#disarm { diff --git a/src/panels/lovelace/cards/hui-conditional-card.ts b/src/panels/lovelace/cards/hui-conditional-card.ts index c0c7e39d68..45907b3229 100644 --- a/src/panels/lovelace/cards/hui-conditional-card.ts +++ b/src/panels/lovelace/cards/hui-conditional-card.ts @@ -1,22 +1,16 @@ import { createCardElement } from "../common/create-card-element"; import { computeCardSize } from "../common/compute-card-size"; import { - Condition, checkConditionsMet, validateConditionalConfig, } from "../../lovelace/common/validate-condition"; import { HomeAssistant } from "../../../types"; import { LovelaceCard } from "../types"; -import { LovelaceCardConfig } from "../../../data/lovelace"; - -interface Config extends LovelaceCardConfig { - card: LovelaceCardConfig; - conditions: Condition[]; -} +import { ConditionalCardConfig } from "./types"; class HuiConditionalCard extends HTMLElement implements LovelaceCard { private _hass?: HomeAssistant; - private _config?: Config; + private _config?: ConditionalCardConfig; private _card?: LovelaceCard; public setConfig(config) { diff --git a/src/panels/lovelace/cards/hui-empty-state-card.ts b/src/panels/lovelace/cards/hui-empty-state-card.ts index ed3553685f..4090fdffd8 100644 --- a/src/panels/lovelace/cards/hui-empty-state-card.ts +++ b/src/panels/lovelace/cards/hui-empty-state-card.ts @@ -11,13 +11,8 @@ import { import "@polymer/paper-card/paper-card"; import { LovelaceCard } from "../types"; -import { LovelaceCardConfig } from "../../../data/lovelace"; import { HomeAssistant } from "../../../types"; - -export interface Config extends LovelaceCardConfig { - content: string; - title?: string; -} +import { EmptyStateCardConfig } from "./types"; @customElement("hui-empty-state-card") export class HuiEmptyStateCard extends LitElement implements LovelaceCard { @@ -27,7 +22,7 @@ export class HuiEmptyStateCard extends LitElement implements LovelaceCard { return 2; } - public setConfig(_config: Config): void { + public setConfig(_config: EmptyStateCardConfig): void { // tslint:disable-next-line } diff --git a/src/panels/lovelace/cards/hui-entities-card.ts b/src/panels/lovelace/cards/hui-entities-card.ts index 6b057ab09e..3ff8951ece 100644 --- a/src/panels/lovelace/cards/hui-entities-card.ts +++ b/src/panels/lovelace/cards/hui-entities-card.ts @@ -15,30 +15,15 @@ import "../components/hui-entities-toggle"; import { fireEvent } from "../../../common/dom/fire_event"; import { DOMAINS_HIDE_MORE_INFO } from "../../../common/const"; import { HomeAssistant } from "../../../types"; -import { EntityConfig, EntityRow } from "../entity-rows/types"; +import { EntityRow } from "../entity-rows/types"; import { LovelaceCard, LovelaceCardEditor } from "../types"; -import { LovelaceCardConfig } from "../../../data/lovelace"; import { processConfigEntities } from "../common/process-config-entities"; import { createRowElement } from "../common/create-row-element"; +import { EntitiesCardConfig, EntitiesCardEntityConfig } from "./types"; + import computeDomain from "../../../common/entity/compute_domain"; import applyThemesOnElement from "../../../common/dom/apply_themes_on_element"; -export interface EntitiesCardEntityConfig extends EntityConfig { - type?: string; - secondary_info?: "entity-id" | "last-changed"; - action_name?: string; - service?: string; - service_data?: object; - url?: string; -} - -export interface EntitiesCardConfig extends LovelaceCardConfig { - show_header_toggle?: boolean; - title?: string; - entities: EntitiesCardEntityConfig[]; - theme?: string; -} - @customElement("hui-entities-card") class HuiEntitiesCard extends LitElement implements LovelaceCard { public static async getConfigElement(): Promise { diff --git a/src/panels/lovelace/cards/hui-entity-button-card.ts b/src/panels/lovelace/cards/hui-entity-button-card.ts index cc0b7fdfbe..5f3926b4fd 100644 --- a/src/panels/lovelace/cards/hui-entity-button-card.ts +++ b/src/panels/lovelace/cards/hui-entity-button-card.ts @@ -21,21 +21,13 @@ import computeStateDomain from "../../../common/entity/compute_state_domain"; import computeStateName from "../../../common/entity/compute_state_name"; import applyThemesOnElement from "../../../common/dom/apply_themes_on_element"; import computeDomain from "../../../common/entity/compute_domain"; + import { HomeAssistant, LightEntity } from "../../../types"; import { LovelaceCard, LovelaceCardEditor } from "../types"; -import { LovelaceCardConfig, ActionConfig } from "../../../data/lovelace"; import { longPress } from "../common/directives/long-press-directive"; import { handleClick } from "../common/handle-click"; import { DOMAINS_TOGGLE } from "../../../common/const"; - -export interface Config extends LovelaceCardConfig { - entity: string; - name?: string; - icon?: string; - theme?: string; - tap_action?: ActionConfig; - hold_action?: ActionConfig; -} +import { EntityButtonCardConfig } from "./types"; @customElement("hui-entity-button-card") class HuiEntityButtonCard extends LitElement implements LovelaceCard { @@ -48,18 +40,20 @@ class HuiEntityButtonCard extends LitElement implements LovelaceCard { return { tap_action: { action: "toggle" }, hold_action: { action: "more-info" }, + show_icon: true, + show_name: true, }; } @property() public hass?: HomeAssistant; - @property() private _config?: Config; + @property() private _config?: EntityButtonCardConfig; public getCardSize(): number { return 2; } - public setConfig(config: Config): void { + public setConfig(config: EntityButtonCardConfig): void { if (!isValidEntityId(config.entity)) { throw new Error("Invalid Entity"); } @@ -67,6 +61,8 @@ class HuiEntityButtonCard extends LitElement implements LovelaceCard { this._config = { theme: "default", hold_action: { action: "more-info" }, + show_icon: true, + show_name: true, ...config, }; @@ -126,18 +122,26 @@ class HuiEntityButtonCard extends LitElement implements LovelaceCard { @ha-hold="${this._handleHold}" .longPress="${longPress()}" > - - - ${this._config.name || computeStateName(stateObj)} - + ${this._config.show_icon + ? html` + + ` + : ""} + ${this._config.show_name + ? html` + + ${this._config.name || computeStateName(stateObj)} + + ` + : ""} `; diff --git a/src/panels/lovelace/cards/hui-entity-filter-card.ts b/src/panels/lovelace/cards/hui-entity-filter-card.ts index 10126c2df2..15b8636224 100644 --- a/src/panels/lovelace/cards/hui-entity-filter-card.ts +++ b/src/panels/lovelace/cards/hui-entity-filter-card.ts @@ -4,14 +4,7 @@ import { LovelaceCard } from "../types"; import { LovelaceCardConfig } from "../../../data/lovelace"; import { EntityConfig } from "../entity-rows/types"; import { HomeAssistant } from "../../../types"; - -export interface EntityFilterCardConfig extends LovelaceCardConfig { - type: "entity-filter"; - entities: Array; - state_filter: string[]; - card: Partial; - show_empty?: boolean; -} +import { EntityFilterCardConfig } from "./types"; class EntityFilterCard extends HTMLElement implements LovelaceCard { public isPanel?: boolean; diff --git a/src/panels/lovelace/cards/hui-error-card.ts b/src/panels/lovelace/cards/hui-error-card.ts index 946f782a79..62bbfe4548 100644 --- a/src/panels/lovelace/cards/hui-error-card.ts +++ b/src/panels/lovelace/cards/hui-error-card.ts @@ -11,11 +11,7 @@ import { import { LovelaceCard } from "../types"; import { LovelaceCardConfig } from "../../../data/lovelace"; import { HomeAssistant } from "../../../types"; - -interface Config extends LovelaceCardConfig { - error: string; - origConfig: LovelaceCardConfig; -} +import { ErrorCardConfig } from "./types"; export const createErrorCardElement = (config) => { const el = document.createElement("hui-error-card"); @@ -33,13 +29,13 @@ export const createErrorCardConfig = (error, origConfig) => ({ export class HuiErrorCard extends LitElement implements LovelaceCard { public hass?: HomeAssistant; - @property() private _config?: Config; + @property() private _config?: ErrorCardConfig; public getCardSize(): number { return 4; } - public setConfig(config: Config): void { + public setConfig(config: ErrorCardConfig): void { this._config = config; } diff --git a/src/panels/lovelace/cards/hui-gauge-card.ts b/src/panels/lovelace/cards/hui-gauge-card.ts index 96fcf946fe..677d8378d0 100644 --- a/src/panels/lovelace/cards/hui-gauge-card.ts +++ b/src/panels/lovelace/cards/hui-gauge-card.ts @@ -13,31 +13,15 @@ import { styleMap } from "lit-html/directives/style-map"; import "../../../components/ha-card"; import "../components/hui-warning"; -import { LovelaceCardConfig } from "../../../data/lovelace"; -import { HomeAssistant } from "../../../types"; -import { fireEvent } from "../../../common/dom/fire_event"; -import { hasConfigOrEntityChanged } from "../common/has-changed"; -import { LovelaceCard, LovelaceCardEditor } from "../types"; - import isValidEntityId from "../../../common/entity/valid_entity_id"; import applyThemesOnElement from "../../../common/dom/apply_themes_on_element"; import computeStateName from "../../../common/entity/compute_state_name"; -export interface SeverityConfig { - green?: number; - yellow?: number; - red?: number; -} - -export interface Config extends LovelaceCardConfig { - entity: string; - name?: string; - unit?: string; - min?: number; - max?: number; - severity?: SeverityConfig; - theme?: string; -} +import { HomeAssistant } from "../../../types"; +import { fireEvent } from "../../../common/dom/fire_event"; +import { hasConfigOrEntityChanged } from "../common/has-changed"; +import { LovelaceCard, LovelaceCardEditor } from "../types"; +import { GaugeCardConfig } from "./types"; export const severityMap = { red: "var(--label-badge-red)", @@ -58,7 +42,7 @@ class HuiGaugeCard extends LitElement implements LovelaceCard { @property() public hass?: HomeAssistant; - @property() private _config?: Config; + @property() private _config?: GaugeCardConfig; private _updated?: boolean; @@ -66,7 +50,7 @@ class HuiGaugeCard extends LitElement implements LovelaceCard { return 2; } - public setConfig(config: Config): void { + public setConfig(config: GaugeCardConfig): void { if (!config || !config.entity) { throw new Error("Invalid card configuration"); } diff --git a/src/panels/lovelace/cards/hui-glance-card.ts b/src/panels/lovelace/cards/hui-glance-card.ts index 35a643ba01..21dd867d9f 100644 --- a/src/panels/lovelace/cards/hui-glance-card.ts +++ b/src/panels/lovelace/cards/hui-glance-card.ts @@ -10,14 +10,6 @@ import { } from "lit-element"; import { classMap } from "lit-html/directives/class-map"; -import { HomeAssistant } from "../../../types"; -import { LovelaceCard, LovelaceCardEditor } from "../types"; -import { LovelaceCardConfig, ActionConfig } from "../../../data/lovelace"; -import { longPress } from "../common/directives/long-press-directive"; -import { EntityConfig } from "../entity-rows/types"; -import { processConfigEntities } from "../common/process-config-entities"; -import { handleClick } from "../common/handle-click"; - import computeStateDisplay from "../../../common/entity/compute_state_display"; import computeStateName from "../../../common/entity/compute_state_name"; import applyThemesOnElement from "../../../common/dom/apply_themes_on_element"; @@ -25,22 +17,14 @@ import applyThemesOnElement from "../../../common/dom/apply_themes_on_element"; import "../../../components/entity/state-badge"; import "../../../components/ha-card"; import "../../../components/ha-icon"; -import "../components/hui-warning"; +import "../components/hui-warning-element"; -export interface ConfigEntity extends EntityConfig { - tap_action?: ActionConfig; - hold_action?: ActionConfig; -} - -export interface GlanceCardConfig extends LovelaceCardConfig { - show_name?: boolean; - show_state?: boolean; - show_icon?: boolean; - title?: string; - theme?: string; - entities: ConfigEntity[]; - columns?: number; -} +import { HomeAssistant } from "../../../types"; +import { LovelaceCard, LovelaceCardEditor } from "../types"; +import { longPress } from "../common/directives/long-press-directive"; +import { processConfigEntities } from "../common/process-config-entities"; +import { handleClick } from "../common/handle-click"; +import { GlanceCardConfig, ConfigEntity } from "./types"; @customElement("hui-glance-card") export class HuiGlanceCard extends LitElement implements LovelaceCard { @@ -184,13 +168,13 @@ export class HuiGlanceCard extends LitElement implements LovelaceCard { if (!stateObj) { return html` - ${this.hass!.localize( + + )} + > `; } diff --git a/src/panels/lovelace/cards/hui-iframe-card.ts b/src/panels/lovelace/cards/hui-iframe-card.ts index e49e149d47..b74dc68cdb 100644 --- a/src/panels/lovelace/cards/hui-iframe-card.ts +++ b/src/panels/lovelace/cards/hui-iframe-card.ts @@ -11,14 +11,8 @@ import { import "../../../components/ha-card"; import { LovelaceCard, LovelaceCardEditor } from "../types"; -import { LovelaceCardConfig } from "../../../data/lovelace"; import { styleMap } from "lit-html/directives/style-map"; - -export interface Config extends LovelaceCardConfig { - aspect_ratio?: string; - title?: string; - url: string; -} +import { IframeCardConfig } from "./types"; @customElement("hui-iframe-card") export class HuiIframeCard extends LitElement implements LovelaceCard { @@ -30,7 +24,7 @@ export class HuiIframeCard extends LitElement implements LovelaceCard { return { url: "https://www.home-assistant.io", aspect_ratio: "50%" }; } - @property() protected _config?: Config; + @property() protected _config?: IframeCardConfig; public getCardSize(): number { if (!this._config) { @@ -42,7 +36,7 @@ export class HuiIframeCard extends LitElement implements LovelaceCard { return 1 + aspectRatio / 25; } - public setConfig(config: Config): void { + public setConfig(config: IframeCardConfig): void { if (!config.url) { throw new Error("URL required"); } diff --git a/src/panels/lovelace/cards/hui-light-card.ts b/src/panels/lovelace/cards/hui-light-card.ts index 46ed51c616..9618e4a3bf 100644 --- a/src/panels/lovelace/cards/hui-light-card.ts +++ b/src/panels/lovelace/cards/hui-light-card.ts @@ -8,15 +8,6 @@ import { } from "lit-element"; import "@polymer/paper-icon-button/paper-icon-button"; -import { fireEvent } from "../../../common/dom/fire_event"; -import { styleMap } from "lit-html/directives/style-map"; -import { HomeAssistant, LightEntity } from "../../../types"; -import { LovelaceCard, LovelaceCardEditor } from "../types"; -import { LovelaceCardConfig } from "../../../data/lovelace"; -import { hasConfigOrEntityChanged } from "../common/has-changed"; -import { loadRoundslider } from "../../../resources/jquery.roundslider.ondemand"; -import { toggleEntity } from "../common/entity/toggle-entity"; - import stateIcon from "../../../common/entity/state_icon"; import computeStateName from "../../../common/entity/compute_state_name"; import applyThemesOnElement from "../../../common/dom/apply_themes_on_element"; @@ -25,6 +16,15 @@ import "../../../components/ha-card"; import "../../../components/ha-icon"; import "../components/hui-warning"; +import { fireEvent } from "../../../common/dom/fire_event"; +import { styleMap } from "lit-html/directives/style-map"; +import { HomeAssistant, LightEntity } from "../../../types"; +import { LovelaceCard, LovelaceCardEditor } from "../types"; +import { hasConfigOrEntityChanged } from "../common/has-changed"; +import { loadRoundslider } from "../../../resources/jquery.roundslider.ondemand"; +import { toggleEntity } from "../common/entity/toggle-entity"; +import { LightCardConfig } from "./types"; + const lightConfig = { radius: 80, step: 1, @@ -40,12 +40,6 @@ const lightConfig = { animation: false, }; -export interface Config extends LovelaceCardConfig { - entity: string; - name?: string; - theme?: string; -} - @customElement("hui-light-card") export class HuiLightCard extends LitElement implements LovelaceCard { public static async getConfigElement(): Promise { @@ -58,7 +52,7 @@ export class HuiLightCard extends LitElement implements LovelaceCard { @property() public hass?: HomeAssistant; - @property() private _config?: Config; + @property() private _config?: LightCardConfig; @property() private _roundSliderStyle?: TemplateResult; @@ -70,7 +64,7 @@ export class HuiLightCard extends LitElement implements LovelaceCard { return 2; } - public setConfig(config: Config): void { + public setConfig(config: LightCardConfig): void { if (!config.entity || config.entity.split(".")[0] !== "light") { throw new Error("Specify an entity from within the light domain."); } diff --git a/src/panels/lovelace/cards/hui-map-card.ts b/src/panels/lovelace/cards/hui-map-card.ts index b56c064de7..227304da45 100644 --- a/src/panels/lovelace/cards/hui-map-card.ts +++ b/src/panels/lovelace/cards/hui-map-card.ts @@ -21,20 +21,13 @@ import computeStateDomain from "../../../common/entity/compute_state_domain"; import computeStateName from "../../../common/entity/compute_state_name"; import debounce from "../../../common/util/debounce"; import parseAspectRatio from "../../../common/util/parse-aspect-ratio"; -import { HomeAssistant } from "../../../types"; import computeDomain from "../../../common/entity/compute_domain"; + +import { HomeAssistant } from "../../../types"; import { LovelaceCard } from "../types"; -import { LovelaceCardConfig } from "../../../data/lovelace"; import { EntityConfig } from "../entity-rows/types"; import { processConfigEntities } from "../common/process-config-entities"; - -export interface MapCardConfig extends LovelaceCardConfig { - title: string; - aspect_ratio: string; - default_zoom?: number; - entities?: Array; - geo_location_sources?: string[]; -} +import { MapCardConfig } from "./types"; @customElement("hui-map-card") class HuiMapCard extends LitElement implements LovelaceCard { diff --git a/src/panels/lovelace/cards/hui-markdown-card.ts b/src/panels/lovelace/cards/hui-markdown-card.ts index 9f66d23952..35a92d9231 100644 --- a/src/panels/lovelace/cards/hui-markdown-card.ts +++ b/src/panels/lovelace/cards/hui-markdown-card.ts @@ -13,12 +13,7 @@ import "../../../components/ha-card"; import "../../../components/ha-markdown"; import { LovelaceCard, LovelaceCardEditor } from "../types"; -import { LovelaceCardConfig } from "../../../data/lovelace"; - -export interface Config extends LovelaceCardConfig { - content: string; - title?: string; -} +import { MarkdownCardConfig } from "./types"; @customElement("hui-markdown-card") export class HuiMarkdownCard extends LitElement implements LovelaceCard { @@ -31,13 +26,13 @@ export class HuiMarkdownCard extends LitElement implements LovelaceCard { return { content: " " }; } - @property() private _config?: Config; + @property() private _config?: MarkdownCardConfig; public getCardSize(): number { return this._config!.content.split("\n").length; } - public setConfig(config: Config): void { + public setConfig(config: MarkdownCardConfig): void { if (!config.content) { throw new Error("Invalid Configuration: Content Required"); } diff --git a/src/panels/lovelace/cards/hui-media-control-card.js b/src/panels/lovelace/cards/hui-media-control-card.js index dc78585aa4..3d38b9114f 100644 --- a/src/panels/lovelace/cards/hui-media-control-card.js +++ b/src/panels/lovelace/cards/hui-media-control-card.js @@ -2,11 +2,6 @@ import "../../../cards/ha-media_player-card"; import LegacyWrapperCard from "./hui-legacy-wrapper-card"; -// should be interface when converted to TS -export const Config = { - entity: "", -}; - class HuiMediaControlCard extends LegacyWrapperCard { static async getConfigElement() { await import(/* webpackChunkName: "hui-media-control-card-editor" */ "../editor/config-elements/hui-media-control-card-editor"); diff --git a/src/panels/lovelace/cards/hui-picture-card.ts b/src/panels/lovelace/cards/hui-picture-card.ts index ac2b0128bb..4d6c4fda48 100644 --- a/src/panels/lovelace/cards/hui-picture-card.ts +++ b/src/panels/lovelace/cards/hui-picture-card.ts @@ -11,17 +11,11 @@ import { import "../../../components/ha-card"; import { LovelaceCard, LovelaceCardEditor } from "../types"; -import { LovelaceCardConfig, ActionConfig } from "../../../data/lovelace"; import { HomeAssistant } from "../../../types"; import { classMap } from "lit-html/directives/class-map"; import { handleClick } from "../common/handle-click"; import { longPress } from "../common/directives/long-press-directive"; - -export interface Config extends LovelaceCardConfig { - image?: string; - tap_action?: ActionConfig; - hold_action?: ActionConfig; -} +import { PictureCardConfig } from "./types"; @customElement("hui-picture-card") export class HuiPictureCard extends LitElement implements LovelaceCard { @@ -40,13 +34,13 @@ export class HuiPictureCard extends LitElement implements LovelaceCard { public hass?: HomeAssistant; - @property() protected _config?: Config; + @property() protected _config?: PictureCardConfig; public getCardSize(): number { return 3; } - public setConfig(config: Config): void { + public setConfig(config: PictureCardConfig): void { if (!config || !config.image) { throw new Error("Invalid Configuration: 'image' required"); } diff --git a/src/panels/lovelace/cards/hui-picture-elements-card.ts b/src/panels/lovelace/cards/hui-picture-elements-card.ts index 9f4ec65730..0aa673df83 100644 --- a/src/panels/lovelace/cards/hui-picture-elements-card.ts +++ b/src/panels/lovelace/cards/hui-picture-elements-card.ts @@ -9,25 +9,14 @@ import { } from "lit-element"; import { createStyledHuiElement } from "./picture-elements/create-styled-hui-element"; - import { LovelaceCard } from "../types"; -import { LovelaceCardConfig } from "../../../data/lovelace"; import { HomeAssistant } from "../../../types"; import { LovelaceElementConfig, LovelaceElement } from "../elements/types"; - -interface Config extends LovelaceCardConfig { - title?: string; - image?: string; - camera_image?: string; - state_image?: {}; - aspect_ratio?: string; - entity?: string; - elements: LovelaceElementConfig[]; -} +import { PictureElementsCardConfig } from "./types"; @customElement("hui-picture-elements-card") class HuiPictureElementsCard extends LitElement implements LovelaceCard { - @property() private _config?: Config; + @property() private _config?: PictureElementsCardConfig; private _hass?: HomeAssistant; @@ -43,7 +32,7 @@ class HuiPictureElementsCard extends LitElement implements LovelaceCard { return 4; } - public setConfig(config: Config): void { + public setConfig(config: PictureElementsCardConfig): void { if (!config) { throw new Error("Invalid Configuration"); } else if ( diff --git a/src/panels/lovelace/cards/hui-picture-entity-card.ts b/src/panels/lovelace/cards/hui-picture-entity-card.ts index b13ba4a60a..a65526e901 100644 --- a/src/panels/lovelace/cards/hui-picture-entity-card.ts +++ b/src/panels/lovelace/cards/hui-picture-entity-card.ts @@ -6,6 +6,7 @@ import { property, css, CSSResult, + PropertyValues, } from "lit-element"; import { classMap } from "lit-html/directives/class-map"; @@ -19,35 +20,23 @@ import computeStateName from "../../../common/entity/compute_state_name"; import { longPress } from "../common/directives/long-press-directive"; import { HomeAssistant } from "../../../types"; -import { LovelaceCardConfig, ActionConfig } from "../../../data/lovelace"; import { LovelaceCard } from "../types"; import { handleClick } from "../common/handle-click"; import { UNAVAILABLE } from "../../../data/entity"; - -interface Config extends LovelaceCardConfig { - entity: string; - name?: string; - image?: string; - camera_image?: string; - state_image?: {}; - aspect_ratio?: string; - tap_action?: ActionConfig; - hold_action?: ActionConfig; - show_name?: boolean; - show_state?: boolean; -} +import { hasConfigOrEntityChanged } from "../common/has-changed"; +import { PictureEntityCardConfig } from "./types"; @customElement("hui-picture-entity-card") class HuiPictureEntityCard extends LitElement implements LovelaceCard { @property() public hass?: HomeAssistant; - @property() private _config?: Config; + @property() private _config?: PictureEntityCardConfig; public getCardSize(): number { return 3; } - public setConfig(config: Config): void { + public setConfig(config: PictureEntityCardConfig): void { if (!config || !config.entity) { throw new Error("Invalid Configuration: 'entity' required"); } @@ -62,6 +51,10 @@ class HuiPictureEntityCard extends LitElement implements LovelaceCard { this._config = { show_name: true, show_state: true, ...config }; } + protected shouldUpdate(changedProps: PropertyValues): boolean { + return hasConfigOrEntityChanged(this, changedProps); + } + protected render(): TemplateResult | void { if (!this._config || !this.hass) { return html``; diff --git a/src/panels/lovelace/cards/hui-picture-glance-card.ts b/src/panels/lovelace/cards/hui-picture-glance-card.ts index 17c13ed494..0fb78c5298 100644 --- a/src/panels/lovelace/cards/hui-picture-glance-card.ts +++ b/src/panels/lovelace/cards/hui-picture-glance-card.ts @@ -6,16 +6,10 @@ import { property, css, CSSResult, + PropertyValues, } from "lit-element"; import { classMap } from "lit-html/directives/class-map"; -import { DOMAINS_TOGGLE } from "../../../common/const"; -import { LovelaceCard } from "../types"; -import { LovelaceCardConfig, ActionConfig } from "../../../data/lovelace"; -import { EntityConfig } from "../entity-rows/types"; -import { HomeAssistant } from "../../../types"; -import { longPress } from "../common/directives/long-press-directive"; -import { processConfigEntities } from "../common/process-config-entities"; import computeStateDisplay from "../../../common/entity/compute_state_display"; import computeStateName from "../../../common/entity/compute_state_name"; import computeDomain from "../../../common/entity/compute_domain"; @@ -24,29 +18,26 @@ import stateIcon from "../../../common/entity/state_icon"; import "../../../components/ha-card"; import "../../../components/ha-icon"; import "../components/hui-image"; +import "../components/hui-warning-element"; + +import { DOMAINS_TOGGLE } from "../../../common/const"; +import { LovelaceCard } from "../types"; +import { EntityConfig } from "../entity-rows/types"; +import { HomeAssistant } from "../../../types"; +import { longPress } from "../common/directives/long-press-directive"; +import { processConfigEntities } from "../common/process-config-entities"; import { handleClick } from "../common/handle-click"; import { fireEvent } from "../../../common/dom/fire_event"; import { toggleEntity } from "../common/entity/toggle-entity"; +import { PictureGlanceCardConfig } from "./types"; const STATES_OFF = new Set(["closed", "locked", "not_home", "off"]); -interface Config extends LovelaceCardConfig { - entities: EntityConfig[]; - title?: string; - image?: string; - camera_image?: string; - state_image?: {}; - aspect_ratio?: string; - entity?: string; - tap_action?: ActionConfig; - hold_action?: ActionConfig; -} - @customElement("hui-picture-glance-card") class HuiPictureGlanceCard extends LitElement implements LovelaceCard { @property() public hass?: HomeAssistant; - @property() private _config?: Config; + @property() private _config?: PictureGlanceCardConfig; private _entitiesDialog?: EntityConfig[]; @@ -56,7 +47,7 @@ class HuiPictureGlanceCard extends LitElement implements LovelaceCard { return 3; } - public setConfig(config: Config): void { + public setConfig(config: PictureGlanceCardConfig): void { if ( !config || !config.entities || @@ -85,6 +76,39 @@ class HuiPictureGlanceCard extends LitElement implements LovelaceCard { this._config = config; } + protected shouldUpdate(changedProps: PropertyValues): boolean { + if (changedProps.has("_config")) { + return true; + } + + const oldHass = changedProps.get("hass") as HomeAssistant | undefined; + if (!oldHass) { + return true; + } + + if (this._entitiesDialog) { + for (const entity of this._entitiesDialog) { + if ( + oldHass.states[entity.entity] !== this.hass!.states[entity.entity] + ) { + return true; + } + } + } + + if (this._entitiesToggle) { + for (const entity of this._entitiesToggle) { + if ( + oldHass.states[entity.entity] !== this.hass!.states[entity.entity] + ) { + return true; + } + } + } + + return false; + } + protected render(): TemplateResult | void { if (!this._config || !this.hass) { return html``; @@ -138,7 +162,15 @@ class HuiPictureGlanceCard extends LitElement implements LovelaceCard { const stateObj = this.hass!.states[entityConf.entity]; if (!stateObj) { - return html``; + return html` + + `; } return html` diff --git a/src/panels/lovelace/cards/hui-plant-status-card.ts b/src/panels/lovelace/cards/hui-plant-status-card.ts index c31d5ce90a..239051374c 100644 --- a/src/panels/lovelace/cards/hui-plant-status-card.ts +++ b/src/panels/lovelace/cards/hui-plant-status-card.ts @@ -6,18 +6,20 @@ import { CSSResult, property, customElement, + PropertyValues, } from "lit-element"; +import { HassEntity } from "home-assistant-js-websocket"; import "../../../components/ha-card"; import "../../../components/ha-icon"; -import { HassEntity } from "home-assistant-js-websocket"; import computeStateName from "../../../common/entity/compute_state_name"; import { LovelaceCardEditor, LovelaceCard } from "../types"; import { HomeAssistant } from "../../../types"; -import { LovelaceCardConfig } from "../../../data/lovelace"; import { fireEvent } from "../../../common/dom/fire_event"; +import { hasConfigOrEntityChanged } from "../common/has-changed"; +import { PlantStatusCardConfig, PlantAttributeTarget } from "./types"; const SENSORS = { moisture: "hass:water", @@ -27,15 +29,6 @@ const SENSORS = { battery: "hass:battery", }; -export interface PlantAttributeTarget extends EventTarget { - value?: string; -} - -export interface PlantStatusConfig extends LovelaceCardConfig { - name?: string; - entity: string; -} - @customElement("hui-plant-status-card") class HuiPlantStatusCard extends LitElement implements LovelaceCard { public static async getConfigElement(): Promise { @@ -49,13 +42,13 @@ class HuiPlantStatusCard extends LitElement implements LovelaceCard { @property() public hass?: HomeAssistant; - @property() private _config?: PlantStatusConfig; + @property() private _config?: PlantStatusCardConfig; public getCardSize(): number { return 3; } - public setConfig(config: PlantStatusConfig): void { + public setConfig(config: PlantStatusCardConfig): void { if (!config.entity || config.entity.split(".")[0] !== "plant") { throw new Error("Specify an entity from within the plant domain."); } @@ -63,6 +56,10 @@ class HuiPlantStatusCard extends LitElement implements LovelaceCard { this._config = config; } + protected shouldUpdate(changedProps: PropertyValues): boolean { + return hasConfigOrEntityChanged(this, changedProps); + } + protected render(): TemplateResult | void { if (!this.hass || !this._config) { return html``; @@ -91,7 +88,7 @@ class HuiPlantStatusCard extends LitElement implements LovelaceCard { style="background-image:url(${stateObj.attributes.entity_picture})" >
- ${this._config.title || computeStateName(stateObj)} + ${this._config.name || computeStateName(stateObj)}
diff --git a/src/panels/lovelace/cards/hui-sensor-card.ts b/src/panels/lovelace/cards/hui-sensor-card.ts index fa3770c3b5..76bac76fe4 100644 --- a/src/panels/lovelace/cards/hui-sensor-card.ts +++ b/src/panels/lovelace/cards/hui-sensor-card.ts @@ -11,12 +11,6 @@ import { } from "lit-element"; import "@polymer/paper-spinner/paper-spinner"; -import { LovelaceCard, LovelaceCardEditor } from "../types"; -import { LovelaceCardConfig } from "../../../data/lovelace"; -import { HomeAssistant } from "../../../types"; -import { fireEvent } from "../../../common/dom/fire_event"; -import { fetchRecent } from "../../../data/history"; - import applyThemesOnElement from "../../../common/dom/apply_themes_on_element"; import computeStateName from "../../../common/entity/compute_state_name"; import stateIcon from "../../../common/entity/state_icon"; @@ -25,6 +19,13 @@ import "../../../components/ha-card"; import "../../../components/ha-icon"; import "../components/hui-warning"; +import { LovelaceCard, LovelaceCardEditor } from "../types"; +import { HomeAssistant } from "../../../types"; +import { fireEvent } from "../../../common/dom/fire_event"; +import { fetchRecent } from "../../../data/history"; +import { SensorCardConfig } from "./types"; +import { hasConfigOrEntityChanged } from "../common/has-changed"; + const midPoint = ( _Ax: number, _Ay: number, @@ -137,17 +138,6 @@ const coordinates = ( return calcPoints(history, hours, width, detail, min, max); }; -export interface Config extends LovelaceCardConfig { - entity: string; - name?: string; - icon?: string; - graph?: string; - unit?: string; - detail?: number; - theme?: string; - hours_to_show?: number; -} - @customElement("hui-sensor-card") class HuiSensorCard extends LitElement implements LovelaceCard { public static async getConfigElement(): Promise { @@ -161,13 +151,13 @@ class HuiSensorCard extends LitElement implements LovelaceCard { @property() public hass?: HomeAssistant; - @property() private _config?: Config; + @property() private _config?: SensorCardConfig; @property() private _history?: any; private _date?: Date; - public setConfig(config: Config): void { + public setConfig(config: SensorCardConfig): void { if (!config.entity || config.entity.split(".")[0] !== "sensor") { throw new Error("Specify an entity from within the sensor domain."); } @@ -272,6 +262,14 @@ class HuiSensorCard extends LitElement implements LovelaceCard { this._date = new Date(); } + protected shouldUpdate(changedProps: PropertyValues): boolean { + if (changedProps.has("_history")) { + return true; + } + + return hasConfigOrEntityChanged(this, changedProps); + } + protected updated(changedProps: PropertyValues) { super.updated(changedProps); if (!this._config || this._config.graph !== "line" || !this.hass) { diff --git a/src/panels/lovelace/cards/hui-shopping-list-card.ts b/src/panels/lovelace/cards/hui-shopping-list-card.ts index 7eecc139a7..10f391e6aa 100644 --- a/src/panels/lovelace/cards/hui-shopping-list-card.ts +++ b/src/panels/lovelace/cards/hui-shopping-list-card.ts @@ -16,7 +16,6 @@ import "../../../components/ha-icon"; import { HomeAssistant } from "../../../types"; import { LovelaceCard, LovelaceCardEditor } from "../types"; -import { LovelaceCardConfig } from "../../../data/lovelace"; import { fetchItems, updateItem, @@ -24,10 +23,7 @@ import { clearItems, addItem, } from "../../../data/shopping-list"; - -export interface Config extends LovelaceCardConfig { - title?: string; -} +import { ShoppingListCardConfig } from "./types"; @customElement("hui-shopping-list-card") class HuiShoppingListCard extends LitElement implements LovelaceCard { @@ -42,7 +38,7 @@ class HuiShoppingListCard extends LitElement implements LovelaceCard { @property() public hass?: HomeAssistant; - @property() private _config?: Config; + @property() private _config?: ShoppingListCardConfig; @property() private _uncheckedItems?: ShoppingListItem[]; @@ -54,7 +50,7 @@ class HuiShoppingListCard extends LitElement implements LovelaceCard { return (this._config ? (this._config.title ? 1 : 0) : 0) + 3; } - public setConfig(config: Config): void { + public setConfig(config: ShoppingListCardConfig): void { this._config = config; this._uncheckedItems = []; this._checkedItems = []; diff --git a/src/panels/lovelace/cards/hui-stack-card.ts b/src/panels/lovelace/cards/hui-stack-card.ts index c3fdd18ba6..b2f6d17034 100644 --- a/src/panels/lovelace/cards/hui-stack-card.ts +++ b/src/panels/lovelace/cards/hui-stack-card.ts @@ -1,14 +1,10 @@ import { html, LitElement, TemplateResult } from "lit-element"; import { createCardElement } from "../common/create-card-element"; - import { LovelaceCard } from "../types"; import { LovelaceCardConfig } from "../../../data/lovelace"; import { HomeAssistant } from "../../../types"; - -interface Config extends LovelaceCardConfig { - cards: LovelaceCardConfig[]; -} +import { StackCardConfig } from "./types"; export abstract class HuiStackCard extends LitElement implements LovelaceCard { static get properties() { @@ -29,12 +25,12 @@ export abstract class HuiStackCard extends LitElement implements LovelaceCard { } } protected _cards?: LovelaceCard[]; - private _config?: Config; + private _config?: StackCardConfig; private _hass?: HomeAssistant; public abstract getCardSize(): number; - public setConfig(config: Config): void { + public setConfig(config: StackCardConfig): void { if (!config || !config.cards || !Array.isArray(config.cards)) { throw new Error("Card config incorrect"); } diff --git a/src/panels/lovelace/cards/hui-thermostat-card.ts b/src/panels/lovelace/cards/hui-thermostat-card.ts index 74bec1a21f..a6af4caf24 100644 --- a/src/panels/lovelace/cards/hui-thermostat-card.ts +++ b/src/panels/lovelace/cards/hui-thermostat-card.ts @@ -19,10 +19,10 @@ import computeStateName from "../../../common/entity/compute_state_name"; import { hasConfigOrEntityChanged } from "../common/has-changed"; import { HomeAssistant, ClimateEntity } from "../../../types"; import { LovelaceCard, LovelaceCardEditor } from "../types"; -import { LovelaceCardConfig } from "../../../data/lovelace"; import { loadRoundslider } from "../../../resources/jquery.roundslider.ondemand"; import { UNIT_F } from "../../../common/const"; import { fireEvent } from "../../../common/dom/fire_event"; +import { ThermostatCardConfig } from "./types"; const thermostatConfig = { radius: 150, @@ -47,12 +47,6 @@ const modeIcons = { idle: "hass:power-sleep", }; -export interface Config extends LovelaceCardConfig { - entity: string; - theme?: string; - name?: string; -} - @customElement("hui-thermostat-card") export class HuiThermostatCard extends LitElement implements LovelaceCard { public static async getConfigElement(): Promise { @@ -66,7 +60,7 @@ export class HuiThermostatCard extends LitElement implements LovelaceCard { @property() public hass?: HomeAssistant; - @property() private _config?: Config; + @property() private _config?: ThermostatCardConfig; @property() private _roundSliderStyle?: TemplateResult; @@ -82,7 +76,7 @@ export class HuiThermostatCard extends LitElement implements LovelaceCard { return 4; } - public setConfig(config: Config): void { + public setConfig(config: ThermostatCardConfig): void { if (!config.entity || config.entity.split(".")[0] !== "climate") { throw new Error("Specify an entity from within the climate domain."); } diff --git a/src/panels/lovelace/cards/hui-weather-forecast-card.js b/src/panels/lovelace/cards/hui-weather-forecast-card.js index 0ef2b8c2c2..9c05fdc2c9 100644 --- a/src/panels/lovelace/cards/hui-weather-forecast-card.js +++ b/src/panels/lovelace/cards/hui-weather-forecast-card.js @@ -2,12 +2,6 @@ import "../../../cards/ha-weather-card"; import LegacyWrapperCard from "./hui-legacy-wrapper-card"; -// should be interface when converted to TS -export const Config = { - entity: "", - name: "", -}; - class HuiWeatherForecastCard extends LegacyWrapperCard { static async getConfigElement() { await import(/* webpackChunkName: "hui-weather-forecast-card-editor" */ "../editor/config-elements/hui-weather-forecast-card-editor"); diff --git a/src/panels/lovelace/cards/types.ts b/src/panels/lovelace/cards/types.ts new file mode 100644 index 0000000000..3c8a00b8ad --- /dev/null +++ b/src/panels/lovelace/cards/types.ts @@ -0,0 +1,200 @@ +import { LovelaceCardConfig, ActionConfig } from "../../../data/lovelace"; +import { Condition } from "../common/validate-condition"; +import { EntityConfig } from "../entity-rows/types"; +import { LovelaceElementConfig } from "../elements/types"; + +export interface AlarmPanelCardConfig extends LovelaceCardConfig { + entity: string; + name?: string; + states?: string[]; +} + +export interface ConditionalCardConfig extends LovelaceCardConfig { + card: LovelaceCardConfig; + conditions: Condition[]; +} + +export interface EmptyStateCardConfig extends LovelaceCardConfig { + content: string; + title?: string; +} + +export interface EntitiesCardEntityConfig extends EntityConfig { + type?: string; + secondary_info?: "entity-id" | "last-changed"; + action_name?: string; + service?: string; + service_data?: object; + url?: string; +} + +export interface EntitiesCardConfig extends LovelaceCardConfig { + show_header_toggle?: boolean; + title?: string; + entities: EntitiesCardEntityConfig[]; + theme?: string; +} + +export interface EntityButtonCardConfig extends LovelaceCardConfig { + entity: string; + name?: string; + show_name?: boolean; + icon?: string; + show_icon?: boolean; + theme?: string; + tap_action?: ActionConfig; + hold_action?: ActionConfig; +} + +export interface EntityFilterCardConfig extends LovelaceCardConfig { + type: "entity-filter"; + entities: Array; + state_filter: string[]; + card: Partial; + show_empty?: boolean; +} + +export interface ErrorCardConfig extends LovelaceCardConfig { + error: string; + origConfig: LovelaceCardConfig; +} + +export interface SeverityConfig { + green?: number; + yellow?: number; + red?: number; +} + +export interface GaugeCardConfig extends LovelaceCardConfig { + entity: string; + name?: string; + unit?: string; + min?: number; + max?: number; + severity?: SeverityConfig; + theme?: string; +} + +export interface ConfigEntity extends EntityConfig { + tap_action?: ActionConfig; + hold_action?: ActionConfig; +} + +export interface GlanceCardConfig extends LovelaceCardConfig { + show_name?: boolean; + show_state?: boolean; + show_icon?: boolean; + title?: string; + theme?: string; + entities: ConfigEntity[]; + columns?: number; +} + +export interface IframeCardConfig extends LovelaceCardConfig { + aspect_ratio?: string; + title?: string; + url: string; +} + +export interface LightCardConfig extends LovelaceCardConfig { + entity: string; + name?: string; + theme?: string; +} + +export interface MapCardConfig extends LovelaceCardConfig { + title: string; + aspect_ratio: string; + default_zoom?: number; + entities?: Array; + geo_location_sources?: string[]; +} + +export interface MarkdownCardConfig extends LovelaceCardConfig { + content: string; + title?: string; +} + +export interface MediaControlCardConfig extends LovelaceCardConfig { + entity: string; +} + +export interface PictureCardConfig extends LovelaceCardConfig { + image?: string; + tap_action?: ActionConfig; + hold_action?: ActionConfig; +} + +export interface PictureElementsCardConfig extends LovelaceCardConfig { + title?: string; + image?: string; + camera_image?: string; + state_image?: {}; + aspect_ratio?: string; + entity?: string; + elements: LovelaceElementConfig[]; +} + +export interface PictureEntityCardConfig extends LovelaceCardConfig { + entity: string; + name?: string; + image?: string; + camera_image?: string; + state_image?: {}; + aspect_ratio?: string; + tap_action?: ActionConfig; + hold_action?: ActionConfig; + show_name?: boolean; + show_state?: boolean; +} + +export interface PictureGlanceCardConfig extends LovelaceCardConfig { + entities: EntityConfig[]; + title?: string; + image?: string; + camera_image?: string; + state_image?: {}; + aspect_ratio?: string; + entity?: string; + tap_action?: ActionConfig; + hold_action?: ActionConfig; +} + +export interface PlantAttributeTarget extends EventTarget { + value?: string; +} + +export interface PlantStatusCardConfig extends LovelaceCardConfig { + name?: string; + entity: string; +} + +export interface SensorCardConfig extends LovelaceCardConfig { + entity: string; + name?: string; + icon?: string; + graph?: string; + unit?: string; + detail?: number; + theme?: string; + hours_to_show?: number; +} + +export interface ShoppingListCardConfig extends LovelaceCardConfig { + title?: string; +} + +export interface StackCardConfig extends LovelaceCardConfig { + cards: LovelaceCardConfig[]; +} + +export interface ThermostatCardConfig extends LovelaceCardConfig { + entity: string; + theme?: string; + name?: string; +} + +export interface WeatherForecastCardConfig extends LovelaceCardConfig { + entity: string; + name?: string; +} diff --git a/src/panels/lovelace/common/generate-lovelace-config.ts b/src/panels/lovelace/common/generate-lovelace-config.ts index 70b794cf13..3e771733a2 100644 --- a/src/panels/lovelace/common/generate-lovelace-config.ts +++ b/src/panels/lovelace/common/generate-lovelace-config.ts @@ -5,6 +5,7 @@ import { LovelaceViewConfig, } from "../../../data/lovelace"; import { HassEntity, HassEntities } from "home-assistant-js-websocket"; + import extractViews from "../../../common/entity/extract_views"; import getViewEntities from "../../../common/entity/get_view_entities"; import computeStateName from "../../../common/entity/compute_state_name"; @@ -12,9 +13,10 @@ import splitByGroups from "../../../common/entity/split_by_groups"; import computeObjectId from "../../../common/entity/compute_object_id"; import computeStateDomain from "../../../common/entity/compute_state_domain"; import computeDomain from "../../../common/entity/compute_domain"; + import { EntityRowConfig, WeblinkConfig } from "../entity-rows/types"; -import { EntitiesCardConfig } from "../cards/hui-entities-card"; import { LocalizeFunc } from "../../../common/translations/localize"; +import { EntitiesCardConfig } from "../cards/types"; const DEFAULT_VIEW_ENTITY_ID = "group.default_view"; const DOMAINS_BADGES = [ diff --git a/src/panels/lovelace/common/validate-condition.ts b/src/panels/lovelace/common/validate-condition.ts index 7775cafa40..e50707d871 100644 --- a/src/panels/lovelace/common/validate-condition.ts +++ b/src/panels/lovelace/common/validate-condition.ts @@ -11,13 +11,11 @@ export function checkConditionsMet( hass: HomeAssistant ): boolean { return conditions.every((c) => { - if (!(c.entity in hass.states)) { - return false; - } - if (c.state) { - return hass.states[c.entity].state === c.state; - } - return hass!.states[c.entity].state !== c.state_not; + const state = hass.states[c.entity] + ? hass!.states[c.entity].state + : "unavailable"; + + return c.state ? state === c.state : state !== c.state_not; }); } diff --git a/src/panels/lovelace/components/dialog/ha-dialog.ts b/src/panels/lovelace/components/dialog/ha-dialog.ts new file mode 100644 index 0000000000..25be5b934e --- /dev/null +++ b/src/panels/lovelace/components/dialog/ha-dialog.ts @@ -0,0 +1,26 @@ +import "@polymer/paper-dialog/paper-dialog"; +import { mixinBehaviors } from "@polymer/polymer/lib/legacy/class"; +import { HaIronFocusablesHelper } from "./ha-iron-focusables-helper.js"; + +const paperDialogClass = customElements.get("paper-dialog"); + +// behavior that will override existing iron-overlay-behavior and call the fixed implementation +const haTabFixBehaviorImpl = { + get _focusableNodes() { + return HaIronFocusablesHelper.getTabbableNodes(this); + }, +}; + +// paper-dialog that uses the haTabFixBehaviorImpl behvaior +// export class HaPaperDialog extends paperDialogClass {} +export class HaPaperDialog extends mixinBehaviors( + [haTabFixBehaviorImpl], + paperDialogClass +) {} + +declare global { + interface HTMLElementTagNameMap { + "ha-paper-dialog": HaPaperDialog; + } +} +customElements.define("ha-paper-dialog", HaPaperDialog); diff --git a/src/panels/lovelace/components/dialog/ha-iron-focusables-helper.js b/src/panels/lovelace/components/dialog/ha-iron-focusables-helper.js new file mode 100644 index 0000000000..4ae78090e5 --- /dev/null +++ b/src/panels/lovelace/components/dialog/ha-iron-focusables-helper.js @@ -0,0 +1,90 @@ +/** +@license +Copyright (c) 2016 The Polymer Project Authors. All rights reserved. +This code may only be used under the BSD style license found at +http://polymer.github.io/LICENSE.txt The complete set of authors may be found at +http://polymer.github.io/AUTHORS.txt The complete set of contributors may be +found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as +part of the polymer project is also subject to an additional IP rights grant +found at http://polymer.github.io/PATENTS.txt +*/ +/* + Fixes issue with not using shadow dom properly in iron-overlay-behavior/icon-focusables-helper.js +*/ +import { dom } from "@polymer/polymer/lib/legacy/polymer.dom.js"; + +import { IronFocusablesHelper } from "@polymer/iron-overlay-behavior/iron-focusables-helper.js"; + +export const HaIronFocusablesHelper = { + /** + * Returns a sorted array of tabbable nodes, including the root node. + * It searches the tabbable nodes in the light and shadow dom of the chidren, + * sorting the result by tabindex. + * @param {!Node} node + * @return {!Array} + */ + getTabbableNodes: function(node) { + var result = []; + // If there is at least one element with tabindex > 0, we need to sort + // the final array by tabindex. + var needsSortByTabIndex = this._collectTabbableNodes(node, result); + if (needsSortByTabIndex) { + return IronFocusablesHelper._sortByTabIndex(result); + } + return result; + }, + + /** + * Searches for nodes that are tabbable and adds them to the `result` array. + * Returns if the `result` array needs to be sorted by tabindex. + * @param {!Node} node The starting point for the search; added to `result` + * if tabbable. + * @param {!Array} result + * @return {boolean} + * @private + */ + _collectTabbableNodes: function(node, result) { + // If not an element or not visible, no need to explore children. + if ( + node.nodeType !== Node.ELEMENT_NODE || + !IronFocusablesHelper._isVisible(node) + ) { + return false; + } + var element = /** @type {!HTMLElement} */ (node); + var tabIndex = IronFocusablesHelper._normalizedTabIndex(element); + var needsSort = tabIndex > 0; + if (tabIndex >= 0) { + result.push(element); + } + + // In ShadowDOM v1, tab order is affected by the order of distrubution. + // E.g. getTabbableNodes(#root) in ShadowDOM v1 should return [#A, #B]; + // in ShadowDOM v0 tab order is not affected by the distrubution order, + // in fact getTabbableNodes(#root) returns [#B, #A]. + //
+ // + // + // + // + // + // + //
+ // TODO(valdrin) support ShadowDOM v1 when upgrading to Polymer v2.0. + var children; + if (element.localName === "content" || element.localName === "slot") { + children = dom(element).getDistributedNodes(); + } else { + // ///////////////////////// + // Use shadow root if possible, will check for distributed nodes. + // THIS IS THE CHANGED LINE + children = dom(element.shadowRoot || element.root || element).children; + // ///////////////////////// + } + for (var i = 0; i < children.length; i++) { + // Ensure method is always invoked to collect tabbable children. + needsSort = this._collectTabbableNodes(children[i], result) || needsSort; + } + return needsSort; + }, +}; diff --git a/src/panels/lovelace/components/hui-generic-entity-row.ts b/src/panels/lovelace/components/hui-generic-entity-row.ts index d38b413dfc..515eafdd6b 100644 --- a/src/panels/lovelace/components/hui-generic-entity-row.ts +++ b/src/panels/lovelace/components/hui-generic-entity-row.ts @@ -9,15 +9,15 @@ import { TemplateResult, } from "lit-element"; -import { HomeAssistant } from "../../../types"; -import { EntitiesCardEntityConfig } from "../cards/hui-entities-card"; -import { computeRTL } from "../../../common/util/compute_rtl"; - import "../../../components/entity/state-badge"; import "../../../components/ha-relative-time"; import "../../../components/ha-icon"; import "../components/hui-warning"; +import { HomeAssistant } from "../../../types"; +import { computeRTL } from "../../../common/util/compute_rtl"; +import { EntitiesCardEntityConfig } from "../cards/types"; + class HuiGenericEntityRow extends LitElement { @property() public hass?: HomeAssistant; diff --git a/src/panels/lovelace/components/hui-warning-element.ts b/src/panels/lovelace/components/hui-warning-element.ts new file mode 100644 index 0000000000..b6495ec0fc --- /dev/null +++ b/src/panels/lovelace/components/hui-warning-element.ts @@ -0,0 +1,36 @@ +import { + html, + LitElement, + TemplateResult, + CSSResult, + css, + customElement, + property, +} from "lit-element"; + +import "../../../components/ha-icon"; + +@customElement("hui-warning-element") +export class HuiWarningElement extends LitElement { + @property() public label?: string; + + protected render(): TemplateResult | void { + return html` + + `; + } + + static get styles(): CSSResult { + return css` + ha-icon { + color: #fce588; + } + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + "hui-warning-element": HuiWarningElement; + } +} diff --git a/src/panels/lovelace/components/hui-yaml-editor.ts b/src/panels/lovelace/components/hui-yaml-editor.ts index d0efdf1eed..4f35f85d80 100644 --- a/src/panels/lovelace/components/hui-yaml-editor.ts +++ b/src/panels/lovelace/components/hui-yaml-editor.ts @@ -96,6 +96,10 @@ export class HuiYamlEditor extends HTMLElement { tabSize: 2, autofocus: true, viewportMargin: Infinity, + extraKeys: { + Tab: "indentMore", + "Shift-Tab": "indentLess", + }, gutters: this._hass && computeRTL(this._hass!) ? ["rtl-gutter", "CodeMirror-linenumbers"] diff --git a/src/panels/lovelace/components/notifications/hui-configurator-notification-item.js b/src/panels/lovelace/components/notifications/hui-configurator-notification-item.js deleted file mode 100644 index 020a4b2821..0000000000 --- a/src/panels/lovelace/components/notifications/hui-configurator-notification-item.js +++ /dev/null @@ -1,60 +0,0 @@ -import "@material/mwc-button"; -import "@polymer/paper-icon-button/paper-icon-button"; - -import { html } from "@polymer/polymer/lib/utils/html-tag"; -import { PolymerElement } from "@polymer/polymer/polymer-element"; - -import "./hui-notification-item-template"; - -import EventsMixin from "../../../../mixins/events-mixin"; -import LocalizeMixin from "../../../../mixins/localize-mixin"; - -/* - * @appliesMixin EventsMixin - * @appliesMixin LocalizeMixin - */ -export class HuiConfiguratorNotificationItem extends EventsMixin( - LocalizeMixin(PolymerElement) -) { - static get template() { - return html` - - [[localize('domain.configurator')]] - -
[[_getMessage(notification)]]
- - [[_localizeState(notification.state)]] -
- `; - } - - static get properties() { - return { - hass: Object, - notification: Object, - }; - } - - _handleClick() { - this.fire("hass-more-info", { entityId: this.notification.entity_id }); - } - - _localizeState(state) { - return this.localize(`state.configurator.${state}`); - } - - _getMessage(notification) { - const friendlyName = notification.attributes.friendly_name; - return this.localize( - "ui.notification_drawer.click_to_configure", - "entity", - friendlyName - ); - } -} -customElements.define( - "hui-configurator-notification-item", - HuiConfiguratorNotificationItem -); diff --git a/src/panels/lovelace/components/notifications/hui-configurator-notification-item.ts b/src/panels/lovelace/components/notifications/hui-configurator-notification-item.ts new file mode 100644 index 0000000000..59a779f503 --- /dev/null +++ b/src/panels/lovelace/components/notifications/hui-configurator-notification-item.ts @@ -0,0 +1,59 @@ +import { + html, + LitElement, + TemplateResult, + property, + customElement, +} from "lit-element"; +import "@material/mwc-button"; + +import "./hui-notification-item-template"; + +import { HomeAssistant } from "../../../../types"; +import { HassEntity } from "home-assistant-js-websocket"; +import { fireEvent } from "../../../../common/dom/fire_event"; + +@customElement("hui-configurator-notification-item") +export class HuiConfiguratorNotificationItem extends LitElement { + @property() public hass?: HomeAssistant; + + @property() public notification?: HassEntity; + + protected render(): TemplateResult | void { + if (!this.hass || !this.notification) { + return html``; + } + + return html` + + ${this.hass.localize("domain.configurator")} + +
+ ${this.hass.localize( + "ui.notification_drawer.click_to_configure", + "entity", + this.notification.attributes.friendly_name + )} +
+ + ${this.hass.localize( + `state.configurator.${this.notification.state}` + )} +
+ `; + } + + private _handleClick(): void { + fireEvent(this, "hass-more-info", { + entityId: this.notification!.entity_id, + }); + } +} + +declare global { + interface HTMLElementTagNameMap { + "hui-configurator-notification-item": HuiConfiguratorNotificationItem; + } +} diff --git a/src/panels/lovelace/components/notifications/hui-notification-item.js b/src/panels/lovelace/components/notifications/hui-notification-item.js deleted file mode 100644 index 66ed2f2a2a..0000000000 --- a/src/panels/lovelace/components/notifications/hui-notification-item.js +++ /dev/null @@ -1,35 +0,0 @@ -import { PolymerElement } from "@polymer/polymer/polymer-element"; -import computeDomain from "../../../../common/entity/compute_domain"; - -import "./hui-configurator-notification-item"; -import "./hui-persistent-notification-item"; - -export class HuiNotificationItem extends PolymerElement { - static get properties() { - return { - hass: Object, - notification: { - type: Object, - observer: "_stateChanged", - }, - }; - } - - _stateChanged(notification) { - if (this.lastChild) { - this.removeChild(this.lastChild); - } - - if (!notification) return; - - const domain = notification.entity_id - ? computeDomain(notification.entity_id) - : "persistent_notification"; - const tag = `hui-${domain}-notification-item`; - const el = document.createElement(tag); - el.hass = this.hass; - el.notification = notification; - this.appendChild(el); - } -} -customElements.define("hui-notification-item", HuiNotificationItem); diff --git a/src/panels/lovelace/components/notifications/hui-notification-item.ts b/src/panels/lovelace/components/notifications/hui-notification-item.ts new file mode 100644 index 0000000000..0aa39a4e4d --- /dev/null +++ b/src/panels/lovelace/components/notifications/hui-notification-item.ts @@ -0,0 +1,55 @@ +import { + LitElement, + property, + customElement, + PropertyValues, + TemplateResult, + html, +} from "lit-element"; + +import "./hui-configurator-notification-item"; +import "./hui-persistent-notification-item"; + +import { HassEntity } from "home-assistant-js-websocket"; +import { HomeAssistant } from "../../../../types"; + +@customElement("hui-notification-item") +export class HuiNotificationItem extends LitElement { + @property() public hass?: HomeAssistant; + + @property() public notification?: HassEntity; + + protected shouldUpdate(changedProps: PropertyValues): boolean { + if (!this.hass || !this.notification || changedProps.has("notification")) { + return true; + } + + return false; + } + + protected render(): TemplateResult | void { + if (!this.hass || !this.notification) { + return html``; + } + + return this.notification.entity_id + ? html` + + ` + : html` + + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + "hui-notification-item": HuiNotificationItem; + } +} diff --git a/src/panels/lovelace/components/notifications/hui-persistent-notification-item.js b/src/panels/lovelace/components/notifications/hui-persistent-notification-item.js index d763ab3838..30ee92adc1 100644 --- a/src/panels/lovelace/components/notifications/hui-persistent-notification-item.js +++ b/src/panels/lovelace/components/notifications/hui-persistent-notification-item.js @@ -87,6 +87,6 @@ export class HuiPersistentNotificationItem extends LocalizeMixin( } } customElements.define( - "hui-persistent_notification-notification-item", + "hui-persistent-notification-item", HuiPersistentNotificationItem ); diff --git a/src/panels/lovelace/editor/card-editor/hui-dialog-edit-card.ts b/src/panels/lovelace/editor/card-editor/hui-dialog-edit-card.ts index 28651529ce..c0a058aeca 100644 --- a/src/panels/lovelace/editor/card-editor/hui-dialog-edit-card.ts +++ b/src/panels/lovelace/editor/card-editor/hui-dialog-edit-card.ts @@ -12,6 +12,7 @@ import { LovelaceCardConfig } from "../../../../data/lovelace"; import "./hui-edit-card"; import "./hui-dialog-pick-card"; import { EditCardDialogParams } from "./show-edit-card-dialog"; +import { addCard, replaceCard } from "../config-util"; declare global { // for fire event @@ -32,19 +33,22 @@ export class HuiDialogEditCard extends LitElement { @property() private _cardConfig?: LovelaceCardConfig; + @property() private _newCard?: boolean; + constructor() { super(); this._cardPicked = this._cardPicked.bind(this); this._cancel = this._cancel.bind(this); + this._save = this._save.bind(this); } public async showDialog(params: EditCardDialogParams): Promise { this._params = params; + const [view, card] = params.path; + this._newCard = card !== undefined ? false : true; this._cardConfig = - params.path.length === 2 - ? (this._cardConfig = params.lovelace.config.views[ - params.path[0] - ].cards![params.path[1]]) + card !== undefined + ? params.lovelace.config.views[view].cards![card] : undefined; } @@ -66,9 +70,10 @@ export class HuiDialogEditCard extends LitElement { `; @@ -82,6 +87,19 @@ export class HuiDialogEditCard extends LitElement { this._params = undefined; this._cardConfig = undefined; } + + private async _save(cardConf: LovelaceCardConfig): Promise { + const lovelace = this._params!.lovelace; + await lovelace.saveConfig( + this._params!.path.length === 1 + ? addCard(lovelace.config, this._params!.path as [number], cardConf) + : replaceCard( + lovelace.config, + this._params!.path as [number, number], + cardConf + ) + ); + } } declare global { diff --git a/src/panels/lovelace/editor/card-editor/hui-edit-card.ts b/src/panels/lovelace/editor/card-editor/hui-edit-card.ts index 15644f5fa1..93ce59eaf2 100644 --- a/src/panels/lovelace/editor/card-editor/hui-edit-card.ts +++ b/src/panels/lovelace/editor/card-editor/hui-edit-card.ts @@ -15,9 +15,10 @@ import { haStyleDialog } from "../../../../resources/styles"; import "@polymer/paper-spinner/paper-spinner"; import "@polymer/paper-dialog/paper-dialog"; +import "../../components/dialog/ha-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 { HaPaperDialog } from "../../components/dialog/ha-dialog"; import "@material/mwc-button"; import "@polymer/paper-dialog-scrollable/paper-dialog-scrollable"; import { HomeAssistant } from "../../../../types"; @@ -33,10 +34,9 @@ import "./hui-card-preview"; // tslint:disable-next-line import { HuiCardPreview } from "./hui-card-preview"; import { LovelaceCardEditor, Lovelace } from "../../types"; -import { ConfigValue, ConfigError } from "../types"; +import { ConfigError } from "../types"; import { EntityConfig } from "../../entity-rows/types"; import { getCardElementTag } from "../../common/get-card-element-tag"; -import { addCard, replaceCard } from "../config-util"; import { afterNextRender } from "../../../../common/util/render-status"; declare global { @@ -58,15 +58,17 @@ export class HuiEditCard extends LitElement { public lovelace?: Lovelace; - public path?: [number] | [number, number]; - public closeDialog?: () => void; + public saveCard?: (cardConf: LovelaceCardConfig) => Promise; + + public newCard?: boolean; + @property() private _configElement?: LovelaceCardEditor | null; @property() private _uiEditor?: boolean; - @property() private _configValue?: ConfigValue; + @property() private _cardConfig?: LovelaceCardConfig; @property() private _configState?: string; @@ -76,16 +78,28 @@ export class HuiEditCard extends LitElement { @property() private _errorMsg?: TemplateResult; - private _cardType?: string; - - private get _dialog(): PaperDialogElement { - return this.shadowRoot!.querySelector("paper-dialog")!; + private get _dialog(): HaPaperDialog { + return this.shadowRoot!.querySelector("ha-paper-dialog")!; } private get _previewEl(): HuiCardPreview { return this.shadowRoot!.querySelector("hui-card-preview")!; } + // tslint:disable-next-line + private __cardYaml: string | undefined; + + private get _cardYaml(): string | undefined { + if (!this.__cardYaml) { + this.__cardYaml = yaml.safeDump(this._cardConfig); + } + return this.__cardYaml; + } + + private set _cardYaml(yml: string | undefined) { + this.__cardYaml = yml; + } + public constructor() { super(); this._saving = false; @@ -98,7 +112,8 @@ export class HuiEditCard extends LitElement { return; } - this._configValue = { format: "yaml", value: undefined }; + this._cardConfig = undefined; + this._cardYaml = undefined; this._configState = "OK"; this._uiEditor = true; this._errorMsg = undefined; @@ -119,7 +134,7 @@ export class HuiEditCard extends LitElement { : html` @@ -133,7 +148,7 @@ export class HuiEditCard extends LitElement { } return html` - ${this.hass!.localize("ui.common.cancel")} @@ -187,7 +200,7 @@ export class HuiEditCard extends LitElement {
` : ""} - + `; } @@ -206,7 +219,7 @@ export class HuiEditCard extends LitElement { private async _resizeDialog(): Promise { await this.updateComplete; - fireEvent(this._dialog, "iron-resize"); + fireEvent(this._dialog as HTMLElement, "iron-resize"); } private async _save(): Promise { @@ -222,22 +235,9 @@ export class HuiEditCard extends LitElement { this._saving = true; - const cardConf: LovelaceCardConfig = - this._configValue!.format === "yaml" - ? yaml.safeLoad(this._configValue!.value!) - : this._configValue!.value!; - try { - const lovelace = this.lovelace!; - await lovelace.saveConfig( - this._creatingCard - ? addCard(lovelace.config, this.path as [number], cardConf) - : replaceCard( - lovelace.config, - this.path as [number, number], - cardConf - ) - ); + await this.saveCard!(this._cardConfig!); + this._cardYaml = undefined; this.closeDialog!(); } catch (err) { alert(`Saving failed: ${err.message}`); @@ -247,12 +247,9 @@ export class HuiEditCard extends LitElement { } private _handleYamlChanged(ev: CustomEvent): void { - this._configValue = { format: "yaml", value: ev.detail.value }; + this._cardConfig = yaml.safeLoad(ev.detail.value); try { - const config = yaml.safeLoad( - this._configValue.value - ) as LovelaceCardConfig; - this._updatePreview(config); + this._updatePreview(this._cardConfig!); this._configState = "OK"; } catch (err) { this._configState = "YAML_ERROR"; @@ -264,7 +261,7 @@ export class HuiEditCard extends LitElement { } private _handleUIConfigChanged(value: LovelaceCardConfig): void { - this._configValue = { format: "json", value }; + this._cardConfig = value; this._updatePreview(value); } @@ -294,35 +291,23 @@ export class HuiEditCard extends LitElement { } private async _toggleEditor(): Promise { - if (this._uiEditor && this._configValue!.format === "json") { - this._configValue = { - format: "yaml", - value: yaml.safeDump(this._configValue!.value), - }; - this._uiEditor = !this._uiEditor; - } else if (this._configElement && this._configValue!.format === "yaml") { - const yamlConfig = this._configValue!.value; - const cardConfig = yaml.safeLoad(yamlConfig) as LovelaceCardConfig; - this._uiEditor = !this._uiEditor; - if (cardConfig.type !== this._cardType) { - const succes = await this._loadConfigElement(cardConfig); - if (!succes) { - this._loadedDialog(); - } - this._cardType = cardConfig.type; + this._cardYaml = undefined; + if (this._uiEditor) { + this._uiEditor = false; + } else if (this._configElement) { + const success = await this._loadConfigElement(this._cardConfig!); + if (!success) { + this._loadedDialog(); } else { - this._configValue = { - format: "json", - value: cardConfig, - }; - this._configElement.setConfig(cardConfig); + this._uiEditor = true; + this._configElement.setConfig(this._cardConfig!); } } this._resizeDialog(); } private _isConfigValid(): boolean { - if (!this._configValue || !this._configValue.value) { + if (!this._cardConfig) { return false; } if (this._configState === "OK") { @@ -333,14 +318,10 @@ export class HuiEditCard extends LitElement { } private _isConfigChanged(): boolean { - if (this._creatingCard) { + if (this.newCard) { return true; } - const configValue = - this._configValue!.format === "yaml" - ? yaml.safeLoad(this._configValue!.value) - : this._configValue!.value; - return JSON.stringify(configValue) !== JSON.stringify(this.cardConfig); + return JSON.stringify(this._cardConfig) !== JSON.stringify(this.cardConfig); } private async _loadConfigElement(conf: LovelaceCardConfig): Promise { @@ -357,10 +338,11 @@ export class HuiEditCard extends LitElement { const elClass = customElements.get(tag); let configElement; + this._cardConfig = conf; + if (elClass && elClass.getConfigElement) { configElement = await elClass.getConfigElement(); } else { - this._configValue = { format: "yaml", value: yaml.safeDump(conf) }; this._updatePreview(conf); this._uiEditor = false; this._configElement = null; @@ -374,10 +356,6 @@ export class HuiEditCard extends LitElement { Your config is not supported by the UI editor:
${err.message}
Falling back to YAML editor. `; - this._configValue = { - format: "yaml", - value: yaml.safeDump(conf), - }; this._updatePreview(conf); this._uiEditor = false; this._configElement = null; @@ -388,17 +366,12 @@ export class HuiEditCard extends LitElement { configElement.addEventListener("config-changed", (ev) => this._handleUIConfigChanged(ev.detail.config) ); - this._configValue = { format: "json", value: conf }; this._configElement = configElement; await this.updateComplete; this._updatePreview(conf); return true; } - private get _creatingCard(): boolean { - return this.path!.length === 1; - } - private _openedChanged(ev): void { if (!ev.detail.value) { this.closeDialog!(); diff --git a/src/panels/lovelace/editor/config-elements/hui-alarm-panel-card-editor.ts b/src/panels/lovelace/editor/config-elements/hui-alarm-panel-card-editor.ts index 8daa8ead02..47da255b09 100644 --- a/src/panels/lovelace/editor/config-elements/hui-alarm-panel-card-editor.ts +++ b/src/panels/lovelace/editor/config-elements/hui-alarm-panel-card-editor.ts @@ -16,11 +16,11 @@ import { EntitiesEditorEvent, EditorTarget } from "../types"; 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"; +import { AlarmPanelCardConfig } from "../../cards/types"; const cardConfigStruct = struct({ type: "string", @@ -34,9 +34,9 @@ export class HuiAlarmPanelCardEditor extends LitElement implements LovelaceCardEditor { @property() public hass?: HomeAssistant; - @property() private _config?: Config; + @property() private _config?: AlarmPanelCardConfig; - public setConfig(config: Config): void { + public setConfig(config: AlarmPanelCardConfig): void { config = cardConfigStruct(config); this._config = config; } diff --git a/src/panels/lovelace/editor/config-elements/hui-entities-card-editor.ts b/src/panels/lovelace/editor/config-elements/hui-entities-card-editor.ts index 7e61259e97..4b9be7d22c 100644 --- a/src/panels/lovelace/editor/config-elements/hui-entities-card-editor.ts +++ b/src/panels/lovelace/editor/config-elements/hui-entities-card-editor.ts @@ -10,23 +10,23 @@ import "@polymer/paper-item/paper-item"; import "@polymer/paper-listbox/paper-listbox"; import "@polymer/paper-toggle-button/paper-toggle-button"; +import "../../../../components/entity/state-badge"; +import "../../components/hui-theme-select-editor"; +import "../../components/hui-entity-editor"; +import "../../../../components/ha-card"; +import "../../../../components/ha-icon"; + import { processEditorEntities } from "../process-editor-entities"; import { struct } from "../../common/structs/struct"; import { EntitiesEditorEvent, EditorTarget } from "../types"; import { HomeAssistant } from "../../../../types"; import { LovelaceCardEditor } from "../../types"; import { fireEvent } from "../../../../common/dom/fire_event"; +import { configElementStyle } from "./config-elements-style"; import { EntitiesCardConfig, EntitiesCardEntityConfig, -} from "../../cards/hui-entities-card"; -import { configElementStyle } from "./config-elements-style"; - -import "../../../../components/entity/state-badge"; -import "../../components/hui-theme-select-editor"; -import "../../components/hui-entity-editor"; -import "../../../../components/ha-card"; -import "../../../../components/ha-icon"; +} from "../../cards/types"; const entitiesConfigStruct = struct.union([ { 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 index 7318a1f270..7ac9ffca2a 100644 --- 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 @@ -7,6 +7,10 @@ import { } from "lit-element"; import "@polymer/paper-input/paper-input"; +import "../../components/hui-action-editor"; +import "../../components/hui-theme-select-editor"; +import "../../components/hui-entity-editor"; + import { struct } from "../../common/structs/struct"; import { EntitiesEditorEvent, @@ -16,19 +20,17 @@ import { 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"; +import { EntityButtonCardConfig } from "../../cards/types"; const cardConfigStruct = struct({ type: "string", entity: "string?", name: "string?", + show_name: "boolean?", icon: "string?", + show_icon: "boolean?", tap_action: struct.optional(actionConfigStruct), hold_action: struct.optional(actionConfigStruct), theme: "string?", @@ -39,9 +41,9 @@ export class HuiEntityButtonCardEditor extends LitElement implements LovelaceCardEditor { @property() public hass?: HomeAssistant; - @property() private _config?: Config; + @property() private _config?: EntityButtonCardConfig; - public setConfig(config: Config): void { + public setConfig(config: EntityButtonCardConfig): void { config = cardConfigStruct(config); this._config = config; } @@ -54,10 +56,18 @@ export class HuiEntityButtonCardEditor extends LitElement return this._config!.name || ""; } + get _show_name(): boolean { + return this._config!.show_name || true; + } + get _icon(): string { return this._config!.icon || ""; } + get _show_icon(): boolean { + return this._config!.show_icon || true; + } + get _tap_action(): ActionConfig { return this._config!.tap_action || { action: "more-info" }; } @@ -101,6 +111,20 @@ export class HuiEntityButtonCardEditor extends LitElement @value-changed="${this._valueChanged}" >
+
+ Show Name? + Show Icon? +
${this.hass.localize( + + )}" + > `; } diff --git a/src/panels/lovelace/elements/hui-state-icon-element.ts b/src/panels/lovelace/elements/hui-state-icon-element.ts index df9ddac572..91a41748d9 100644 --- a/src/panels/lovelace/elements/hui-state-icon-element.ts +++ b/src/panels/lovelace/elements/hui-state-icon-element.ts @@ -6,30 +6,25 @@ import { property, css, CSSResult, + PropertyValues, } from "lit-element"; import "../../../components/entity/state-badge"; -import "../components/hui-warning"; +import "../components/hui-warning-element"; import { computeTooltip } from "../common/compute-tooltip"; import { handleClick } from "../common/handle-click"; import { longPress } from "../common/directives/long-press-directive"; -import { LovelaceElement, LovelaceElementConfig } from "./types"; +import { LovelaceElement, StateIconElementConfig } from "./types"; import { HomeAssistant } from "../../../types"; -import { ActionConfig } from "../../../data/lovelace"; - -export interface Config extends LovelaceElementConfig { - entity: string; - tap_action?: ActionConfig; - hold_action?: ActionConfig; -} +import { hasConfigOrEntityChanged } from "../common/has-changed"; @customElement("hui-state-icon-element") export class HuiStateIconElement extends LitElement implements LovelaceElement { @property() public hass?: HomeAssistant; - @property() private _config?: Config; + @property() private _config?: StateIconElementConfig; - public setConfig(config: Config): void { + public setConfig(config: StateIconElementConfig): void { if (!config.entity) { throw Error("Invalid Configuration: 'entity' required"); } @@ -37,6 +32,10 @@ export class HuiStateIconElement extends LitElement implements LovelaceElement { this._config = config; } + protected shouldUpdate(changedProps: PropertyValues): boolean { + return hasConfigOrEntityChanged(this, changedProps); + } + protected render(): TemplateResult | void { if (!this._config || !this.hass) { return html``; @@ -46,13 +45,13 @@ export class HuiStateIconElement extends LitElement implements LovelaceElement { if (!stateObj) { return html` - ${this.hass.localize( + + )} + > `; } diff --git a/src/panels/lovelace/elements/hui-state-label-element.ts b/src/panels/lovelace/elements/hui-state-label-element.ts index 5decd2d415..561e9a8588 100644 --- a/src/panels/lovelace/elements/hui-state-label-element.ts +++ b/src/panels/lovelace/elements/hui-state-label-element.ts @@ -6,33 +6,26 @@ import { property, css, CSSResult, + PropertyValues, } from "lit-element"; import "../../../components/entity/ha-state-label-badge"; -import "../components/hui-warning"; +import "../components/hui-warning-element"; import computeStateDisplay from "../../../common/entity/compute_state_display"; import { computeTooltip } from "../common/compute-tooltip"; import { handleClick } from "../common/handle-click"; import { longPress } from "../common/directives/long-press-directive"; -import { LovelaceElement, LovelaceElementConfig } from "./types"; +import { LovelaceElement, StateLabelElementConfig } from "./types"; import { HomeAssistant } from "../../../types"; -import { ActionConfig } from "../../../data/lovelace"; - -interface Config extends LovelaceElementConfig { - entity: string; - prefix?: string; - suffix?: string; - tap_action?: ActionConfig; - hold_action?: ActionConfig; -} +import { hasConfigOrEntityChanged } from "../common/has-changed"; @customElement("hui-state-label-element") class HuiStateLabelElement extends LitElement implements LovelaceElement { @property() public hass?: HomeAssistant; - @property() private _config?: Config; + @property() private _config?: StateLabelElementConfig; - public setConfig(config: Config): void { + public setConfig(config: StateLabelElementConfig): void { if (!config.entity) { throw Error("Invalid Configuration: 'entity' required"); } @@ -40,6 +33,10 @@ class HuiStateLabelElement extends LitElement implements LovelaceElement { this._config = config; } + protected shouldUpdate(changedProps: PropertyValues): boolean { + return hasConfigOrEntityChanged(this, changedProps); + } + protected render(): TemplateResult | void { if (!this._config || !this.hass) { return html``; @@ -49,13 +46,13 @@ class HuiStateLabelElement extends LitElement implements LovelaceElement { if (!stateObj) { return html` - ${this.hass.localize( + + )} + > `; } diff --git a/src/panels/lovelace/elements/types.ts b/src/panels/lovelace/elements/types.ts index 6d6b908a29..5c82484003 100644 --- a/src/panels/lovelace/elements/types.ts +++ b/src/panels/lovelace/elements/types.ts @@ -1,4 +1,6 @@ import { HomeAssistant } from "../../../types"; +import { Condition } from "../common/validate-condition"; +import { ActionConfig } from "../../../data/lovelace"; export interface LovelaceElementConfig { type: string; @@ -9,3 +11,52 @@ export interface LovelaceElement extends HTMLElement { hass?: HomeAssistant; setConfig(config: LovelaceElementConfig): void; } + +export interface ConditionalElementConfig extends LovelaceElementConfig { + conditions: Condition[]; + elements: LovelaceElementConfig[]; +} + +export interface IconElementConfig extends LovelaceElementConfig { + entity?: string; + name?: string; + tap_action?: ActionConfig; + hold_action?: ActionConfig; + icon: string; +} + +export interface ImageElementConfig extends LovelaceElementConfig { + entity?: string; + tap_action?: ActionConfig; + hold_action?: ActionConfig; + image?: string; + state_image?: string; + camera_image?: string; + filter?: string; + state_filter?: string; + aspect_ratio?: string; +} + +export interface ServiceButtonElementConfig extends LovelaceElementConfig { + title?: string; + service?: string; + service_data?: object; +} + +export interface StateBadgeElementConfig extends LovelaceElementConfig { + entity: string; +} + +export interface StateIconElementConfig extends LovelaceElementConfig { + entity: string; + tap_action?: ActionConfig; + hold_action?: ActionConfig; +} + +export interface StateLabelElementConfig extends LovelaceElementConfig { + entity: string; + prefix?: string; + suffix?: string; + tap_action?: ActionConfig; + hold_action?: ActionConfig; +} 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 586a29615f..c0596839a7 100644 --- a/src/panels/lovelace/entity-rows/hui-climate-entity-row.ts +++ b/src/panels/lovelace/entity-rows/hui-climate-entity-row.ts @@ -6,6 +6,7 @@ import { css, CSSResult, customElement, + PropertyValues, } from "lit-element"; import "../../../components/ha-climate-state"; @@ -14,6 +15,7 @@ import "../components/hui-warning"; import { HomeAssistant } from "../../../types"; import { EntityRow, EntityConfig } from "./types"; +import { hasConfigOrEntityChanged } from "../common/has-changed"; @customElement("hui-climate-entity-row") class HuiClimateEntityRow extends LitElement implements EntityRow { @@ -29,6 +31,10 @@ class HuiClimateEntityRow extends LitElement implements EntityRow { this._config = config; } + protected shouldUpdate(changedProps: PropertyValues): boolean { + return hasConfigOrEntityChanged(this, changedProps); + } + protected render(): TemplateResult | void { if (!this.hass || !this._config) { return html``; diff --git a/src/panels/lovelace/entity-rows/hui-cover-entity-row.ts b/src/panels/lovelace/entity-rows/hui-cover-entity-row.ts index 160c4d6760..f65a0e3ba4 100644 --- a/src/panels/lovelace/entity-rows/hui-cover-entity-row.ts +++ b/src/panels/lovelace/entity-rows/hui-cover-entity-row.ts @@ -6,6 +6,7 @@ import { css, CSSResult, customElement, + PropertyValues, } from "lit-element"; import "../components/hui-generic-entity-row"; @@ -16,6 +17,7 @@ import "../components/hui-warning"; import { isTiltOnly } from "../../../util/cover-model"; import { HomeAssistant } from "../../../types"; import { EntityRow, EntityConfig } from "./types"; +import { hasConfigOrEntityChanged } from "../common/has-changed"; @customElement("hui-cover-entity-row") class HuiCoverEntityRow extends LitElement implements EntityRow { @@ -30,6 +32,10 @@ class HuiCoverEntityRow extends LitElement implements EntityRow { this._config = config; } + protected shouldUpdate(changedProps: PropertyValues): boolean { + return hasConfigOrEntityChanged(this, changedProps); + } + protected render(): TemplateResult | void { if (!this._config || !this.hass) { return html``; diff --git a/src/panels/lovelace/entity-rows/hui-group-entity-row.ts b/src/panels/lovelace/entity-rows/hui-group-entity-row.ts index 165f4eb027..ebc78cee83 100644 --- a/src/panels/lovelace/entity-rows/hui-group-entity-row.ts +++ b/src/panels/lovelace/entity-rows/hui-group-entity-row.ts @@ -4,6 +4,7 @@ import { TemplateResult, property, customElement, + PropertyValues, } from "lit-element"; import "../components/hui-generic-entity-row"; @@ -11,9 +12,11 @@ import "../../../components/entity/ha-entity-toggle"; import "../components/hui-warning"; import computeStateDisplay from "../../../common/entity/compute_state_display"; + import { DOMAINS_TOGGLE } from "../../../common/const"; import { HomeAssistant } from "../../../types"; import { EntityRow, EntityConfig } from "./types"; +import { hasConfigOrEntityChanged } from "../common/has-changed"; @customElement("hui-group-entity-row") class HuiGroupEntityRow extends LitElement implements EntityRow { @@ -28,6 +31,10 @@ class HuiGroupEntityRow extends LitElement implements EntityRow { this._config = config; } + protected shouldUpdate(changedProps: PropertyValues): boolean { + return hasConfigOrEntityChanged(this, changedProps); + } + protected render(): TemplateResult | void { if (!this._config || !this.hass) { return html``; diff --git a/src/panels/lovelace/entity-rows/hui-input-number-entity-row.ts b/src/panels/lovelace/entity-rows/hui-input-number-entity-row.ts index 12fd026996..2140fc3d14 100644 --- a/src/panels/lovelace/entity-rows/hui-input-number-entity-row.ts +++ b/src/panels/lovelace/entity-rows/hui-input-number-entity-row.ts @@ -6,6 +6,7 @@ import { customElement, css, CSSResult, + PropertyValues, } from "lit-element"; import "../components/hui-generic-entity-row"; @@ -16,6 +17,7 @@ import { computeRTLDirection } from "../../../common/util/compute_rtl"; import { EntityRow, EntityConfig } from "./types"; import { HomeAssistant } from "../../../types"; import { setValue } from "../../../data/input_text"; +import { hasConfigOrEntityChanged } from "../common/has-changed"; @customElement("hui-input-number-entity-row") class HuiInputNumberEntityRow extends LitElement implements EntityRow { @@ -48,6 +50,10 @@ class HuiInputNumberEntityRow extends LitElement implements EntityRow { } } + protected shouldUpdate(changedProps: PropertyValues): boolean { + return hasConfigOrEntityChanged(this, changedProps); + } + protected render(): TemplateResult | void { if (!this._config || !this.hass) { return html``; diff --git a/src/panels/lovelace/entity-rows/hui-input-select-entity-row.ts b/src/panels/lovelace/entity-rows/hui-input-select-entity-row.ts index ddb96a54a8..9909c89106 100644 --- a/src/panels/lovelace/entity-rows/hui-input-select-entity-row.ts +++ b/src/panels/lovelace/entity-rows/hui-input-select-entity-row.ts @@ -6,6 +6,7 @@ import { css, CSSResult, customElement, + PropertyValues, } from "lit-element"; import { repeat } from "lit-html/directives/repeat"; import "@polymer/paper-dropdown-menu/paper-dropdown-menu"; @@ -16,9 +17,11 @@ import "../../../components/entity/state-badge"; import "../components/hui-warning"; import computeStateName from "../../../common/entity/compute_state_name"; + import { HomeAssistant } from "../../../types"; import { EntityRow, EntityConfig } from "./types"; import { setOption } from "../../../data/input-select"; +import { hasConfigOrEntityChanged } from "../common/has-changed"; @customElement("hui-input-select-entity-row") class HuiInputSelectEntityRow extends LitElement implements EntityRow { @@ -34,6 +37,10 @@ class HuiInputSelectEntityRow extends LitElement implements EntityRow { this._config = config; } + protected shouldUpdate(changedProps: PropertyValues): boolean { + return hasConfigOrEntityChanged(this, changedProps); + } + protected render(): TemplateResult | void { if (!this.hass || !this._config) { return html``; diff --git a/src/panels/lovelace/entity-rows/hui-input-text-entity-row.ts b/src/panels/lovelace/entity-rows/hui-input-text-entity-row.ts index 42376687cc..586441e16f 100644 --- a/src/panels/lovelace/entity-rows/hui-input-text-entity-row.ts +++ b/src/panels/lovelace/entity-rows/hui-input-text-entity-row.ts @@ -4,6 +4,7 @@ import { TemplateResult, property, customElement, + PropertyValues, } from "lit-element"; import { PaperInputElement } from "@polymer/paper-input/paper-input"; @@ -13,6 +14,7 @@ import "../components/hui-warning"; import { HomeAssistant } from "../../../types"; import { EntityRow, EntityConfig } from "./types"; import { setValue } from "../../../data/input_text"; +import { hasConfigOrEntityChanged } from "../common/has-changed"; @customElement("hui-input-text-entity-row") class HuiInputTextEntityRow extends LitElement implements EntityRow { @@ -27,6 +29,10 @@ class HuiInputTextEntityRow extends LitElement implements EntityRow { this._config = config; } + protected shouldUpdate(changedProps: PropertyValues): boolean { + return hasConfigOrEntityChanged(this, changedProps); + } + protected render(): TemplateResult | void { if (!this._config || !this.hass) { return html``; diff --git a/src/panels/lovelace/entity-rows/hui-lock-entity-row.ts b/src/panels/lovelace/entity-rows/hui-lock-entity-row.ts index 22d6fe6028..fd08fec0c4 100644 --- a/src/panels/lovelace/entity-rows/hui-lock-entity-row.ts +++ b/src/panels/lovelace/entity-rows/hui-lock-entity-row.ts @@ -6,6 +6,7 @@ import { css, CSSResult, customElement, + PropertyValues, } from "lit-element"; import "../components/hui-generic-entity-row"; @@ -13,6 +14,7 @@ import "../components/hui-warning"; import { HomeAssistant } from "../../../types"; import { EntityRow, EntityConfig } from "./types"; +import { hasConfigOrEntityChanged } from "../common/has-changed"; @customElement("hui-lock-entity-row") class HuiLockEntityRow extends LitElement implements EntityRow { @@ -27,6 +29,10 @@ class HuiLockEntityRow extends LitElement implements EntityRow { this._config = config; } + protected shouldUpdate(changedProps: PropertyValues): boolean { + return hasConfigOrEntityChanged(this, changedProps); + } + protected render(): TemplateResult | void { if (!this._config || !this.hass) { return html``; diff --git a/src/panels/lovelace/entity-rows/hui-media-player-entity-row.ts b/src/panels/lovelace/entity-rows/hui-media-player-entity-row.ts index 28273dce7e..a3f3869cdd 100644 --- a/src/panels/lovelace/entity-rows/hui-media-player-entity-row.ts +++ b/src/panels/lovelace/entity-rows/hui-media-player-entity-row.ts @@ -6,6 +6,7 @@ import { CSSResult, property, customElement, + PropertyValues, } from "lit-element"; import "@polymer/paper-icon-button/paper-icon-button"; @@ -22,6 +23,7 @@ import { OFF_STATES, SUPPORT_PAUSE, } from "../../../data/media-player"; +import { hasConfigOrEntityChanged } from "../common/has-changed"; @customElement("hui-media-player-entity-row") class HuiMediaPlayerEntityRow extends LitElement implements EntityRow { @@ -37,6 +39,10 @@ class HuiMediaPlayerEntityRow extends LitElement implements EntityRow { this._config = config; } + protected shouldUpdate(changedProps: PropertyValues): boolean { + return hasConfigOrEntityChanged(this, changedProps); + } + protected render(): TemplateResult | void { if (!this.hass || !this._config) { return html``; diff --git a/src/panels/lovelace/entity-rows/hui-scene-entity-row.ts b/src/panels/lovelace/entity-rows/hui-scene-entity-row.ts index a690adfc5c..38df289b59 100644 --- a/src/panels/lovelace/entity-rows/hui-scene-entity-row.ts +++ b/src/panels/lovelace/entity-rows/hui-scene-entity-row.ts @@ -6,6 +6,7 @@ import { css, property, customElement, + PropertyValues, } from "lit-element"; import "../components/hui-generic-entity-row"; @@ -14,6 +15,7 @@ import "../components/hui-warning"; import { HomeAssistant } from "../../../types"; import { EntityRow, EntityConfig } from "./types"; +import { hasConfigOrEntityChanged } from "../common/has-changed"; @customElement("hui-scene-entity-row") class HuiSceneEntityRow extends LitElement implements EntityRow { @@ -28,6 +30,10 @@ class HuiSceneEntityRow extends LitElement implements EntityRow { this._config = config; } + protected shouldUpdate(changedProps: PropertyValues): boolean { + return hasConfigOrEntityChanged(this, changedProps); + } + protected render(): TemplateResult | void { if (!this._config || !this.hass) { return html``; diff --git a/src/panels/lovelace/entity-rows/hui-script-entity-row.ts b/src/panels/lovelace/entity-rows/hui-script-entity-row.ts index 5d6dfe82ef..b11e144497 100644 --- a/src/panels/lovelace/entity-rows/hui-script-entity-row.ts +++ b/src/panels/lovelace/entity-rows/hui-script-entity-row.ts @@ -6,6 +6,7 @@ import { CSSResult, css, customElement, + PropertyValues, } from "lit-element"; import "../components/hui-generic-entity-row"; @@ -14,10 +15,11 @@ import "../components/hui-warning"; import { HomeAssistant } from "../../../types"; import { EntityRow, EntityConfig } from "./types"; +import { hasConfigOrEntityChanged } from "../common/has-changed"; @customElement("hui-script-entity-row") class HuiScriptEntityRow extends LitElement implements EntityRow { - @property() public hass?: HomeAssistant; + public hass?: HomeAssistant; @property() private _config?: EntityConfig; @@ -28,6 +30,10 @@ class HuiScriptEntityRow extends LitElement implements EntityRow { this._config = config; } + protected shouldUpdate(changedProps: PropertyValues): boolean { + return hasConfigOrEntityChanged(this, changedProps); + } + protected render(): TemplateResult | void { if (!this._config || !this.hass) { return html``; diff --git a/src/panels/lovelace/entity-rows/hui-sensor-entity-row.ts b/src/panels/lovelace/entity-rows/hui-sensor-entity-row.ts index 998ba9450c..96ec9b3a65 100644 --- a/src/panels/lovelace/entity-rows/hui-sensor-entity-row.ts +++ b/src/panels/lovelace/entity-rows/hui-sensor-entity-row.ts @@ -6,6 +6,7 @@ import { CSSResult, css, customElement, + PropertyValues, } from "lit-element"; import "../components/hui-generic-entity-row"; @@ -14,6 +15,7 @@ import "../components/hui-warning"; import { HomeAssistant } from "../../../types"; import { EntityRow, EntityConfig } from "./types"; +import { hasConfigOrEntityChanged } from "../common/has-changed"; import computeStateDisplay from "../../../common/entity/compute_state_display"; @@ -34,6 +36,10 @@ class HuiSensorEntityRow extends LitElement implements EntityRow { this._config = config; } + protected shouldUpdate(changedProps: PropertyValues): boolean { + return hasConfigOrEntityChanged(this, changedProps); + } + protected render(): TemplateResult | void { if (!this._config || !this.hass) { return html``; diff --git a/src/panels/lovelace/entity-rows/hui-text-entity-row.ts b/src/panels/lovelace/entity-rows/hui-text-entity-row.ts index 6c7e564262..34e98c737c 100644 --- a/src/panels/lovelace/entity-rows/hui-text-entity-row.ts +++ b/src/panels/lovelace/entity-rows/hui-text-entity-row.ts @@ -6,14 +6,17 @@ import { CSSResult, css, customElement, + PropertyValues, } from "lit-element"; import "../components/hui-generic-entity-row"; import "../components/hui-warning"; import computeStateDisplay from "../../../common/entity/compute_state_display"; + import { HomeAssistant } from "../../../types"; import { EntityRow, EntityConfig } from "./types"; +import { hasConfigOrEntityChanged } from "../common/has-changed"; @customElement("hui-text-entity-row") class HuiTextEntityRow extends LitElement implements EntityRow { @@ -28,6 +31,10 @@ class HuiTextEntityRow extends LitElement implements EntityRow { this._config = config; } + protected shouldUpdate(changedProps: PropertyValues): boolean { + return hasConfigOrEntityChanged(this, changedProps); + } + protected render(): TemplateResult | void { if (!this._config || !this.hass) { return html``; diff --git a/src/panels/lovelace/entity-rows/hui-timer-entity-row.ts b/src/panels/lovelace/entity-rows/hui-timer-entity-row.ts index 1bcd3e64ef..cd46bccae8 100644 --- a/src/panels/lovelace/entity-rows/hui-timer-entity-row.ts +++ b/src/panels/lovelace/entity-rows/hui-timer-entity-row.ts @@ -12,9 +12,11 @@ import "../components/hui-warning"; import timerTimeRemaining from "../../../common/entity/timer_time_remaining"; import secondsToDuration from "../../../common/datetime/seconds_to_duration"; + import { HomeAssistant } from "../../../types"; import { EntityConfig } from "./types"; import { HassEntity } from "home-assistant-js-websocket"; +import { hasConfigOrEntityChanged } from "../common/has-changed"; @customElement("hui-timer-entity-row") class HuiTimerEntityRow extends LitElement { @@ -64,6 +66,14 @@ class HuiTimerEntityRow extends LitElement { `; } + protected shouldUpdate(changedProps: PropertyValues): boolean { + if (changedProps.has("_timeRemaining")) { + return true; + } + + return hasConfigOrEntityChanged(this, changedProps); + } + protected updated(changedProps: PropertyValues) { super.updated(changedProps); diff --git a/src/panels/lovelace/entity-rows/hui-toggle-entity-row.ts b/src/panels/lovelace/entity-rows/hui-toggle-entity-row.ts index 0ac6a57ecc..22055d8316 100644 --- a/src/panels/lovelace/entity-rows/hui-toggle-entity-row.ts +++ b/src/panels/lovelace/entity-rows/hui-toggle-entity-row.ts @@ -4,6 +4,7 @@ import { TemplateResult, customElement, property, + PropertyValues, } from "lit-element"; import "../components/hui-generic-entity-row"; @@ -14,6 +15,7 @@ import computeStateDisplay from "../../../common/entity/compute_state_display"; import { HomeAssistant } from "../../../types"; import { EntityRow, EntityConfig } from "./types"; +import { hasConfigOrEntityChanged } from "../common/has-changed"; @customElement("hui-toggle-entity-row") class HuiToggleEntityRow extends LitElement implements EntityRow { @@ -28,6 +30,10 @@ class HuiToggleEntityRow extends LitElement implements EntityRow { this._config = config; } + protected shouldUpdate(changedProps: PropertyValues): boolean { + return hasConfigOrEntityChanged(this, changedProps); + } + protected render(): TemplateResult | void { if (!this._config || !this.hass) { return html``; diff --git a/src/panels/lovelace/special-rows/hui-call-service-row.ts b/src/panels/lovelace/special-rows/hui-call-service-row.ts index 479f45d7e2..8226581b0b 100644 --- a/src/panels/lovelace/special-rows/hui-call-service-row.ts +++ b/src/panels/lovelace/special-rows/hui-call-service-row.ts @@ -17,7 +17,7 @@ import { HomeAssistant } from "../../../types"; @customElement("hui-call-service-row") class HuiCallServiceRow extends LitElement implements EntityRow { - @property() public hass?: HomeAssistant; + public hass?: HomeAssistant; @property() private _config?: CallServiceConfig; diff --git a/src/translations/en.json b/src/translations/en.json index 67b7033392..afd77b714c 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -332,7 +332,9 @@ "state_badge": { "default": { "unknown": "Unk", - "unavailable": "Unavai" + "unavailable": "Unavai", + "error": "Error", + "entity_not_found": "Entity Not Found" }, "alarm_control_panel": { "armed": "Armed", @@ -875,7 +877,18 @@ "description": "Zigbee Home Automation network management", "services": { "reconfigure": "Reconfigure ZHA device (heal device). Use this if you are having issues with the device. If the device in question is a battery powered device please ensure it is awake and accepting commands when you use this service.", - "updateDeviceName": "Set a custom name for this device in the device registry." + "updateDeviceName": "Set a custom name for this device in the device registry.", + "remove": "Remove a device from the ZigBee network." + }, + "device_card": { + "device_name_placeholder": "User given name", + "area_picker_label": "Area", + "update_name_button": "Update Name" + }, + "add_device_page": { + "header": "Zigbee Home Automation - Add Devices", + "spinner": "Searching for ZHA Zigbee devices...", + "discovery_text": "Discovered devices will show up here. Follow the instructions for your device(s) and place the device(s) in pairing mode." } }, "zwave": { diff --git a/src/translations/translationMetadata.json b/src/translations/translationMetadata.json index ad5be46a17..bb18f04ba3 100644 --- a/src/translations/translationMetadata.json +++ b/src/translations/translationMetadata.json @@ -76,6 +76,9 @@ "it": { "nativeName": "Italiano" }, + "is": { + "nativeName": "Íslenska" + }, "ja": { "nativeName": "日本語" }, diff --git a/src/types.ts b/src/types.ts index 317f12f2bc..33665a4594 100644 --- a/src/types.ts +++ b/src/types.ts @@ -15,10 +15,13 @@ declare global { var __DEMO__: boolean; var __BUILD__: "latest" | "es5"; var __VERSION__: string; + var __STATIC_PATH__: string; } declare global { interface Window { + // Custom panel entry point url + customPanelJS: string; ShadyCSS: { nativeCss: boolean; nativeShadow: boolean; @@ -137,7 +140,7 @@ export interface HomeAssistant { dockedSidebar: boolean; moreInfoEntityId: string; - user: CurrentUser; + user?: CurrentUser; callService: ( domain: string, service: string, diff --git a/src/util/custom-panel/create-custom-panel-element.js b/src/util/custom-panel/create-custom-panel-element.ts similarity index 78% rename from src/util/custom-panel/create-custom-panel-element.js rename to src/util/custom-panel/create-custom-panel-element.ts index 4969c179de..d4a23699b4 100644 --- a/src/util/custom-panel/create-custom-panel-element.js +++ b/src/util/custom-panel/create-custom-panel-element.ts @@ -1,8 +1,8 @@ -export default function createCustomPanelElement(panelConfig) { +export const createCustomPanelElement = (panelConfig) => { // Legacy support. Custom panels used to have to define element ha-panel-{name} const tagName = "html_url" in panelConfig ? `ha-panel-${panelConfig.name}` : panelConfig.name; return document.createElement(tagName); -} +}; diff --git a/src/util/custom-panel/load-custom-panel.js b/src/util/custom-panel/load-custom-panel.ts similarity index 93% rename from src/util/custom-panel/load-custom-panel.js rename to src/util/custom-panel/load-custom-panel.ts index 54614810aa..08b399d591 100644 --- a/src/util/custom-panel/load-custom-panel.js +++ b/src/util/custom-panel/load-custom-panel.ts @@ -3,7 +3,7 @@ import { loadJS, loadModule } from "../../common/dom/load_resource"; // Make sure we only import every JS-based panel once (HTML import has this built-in) const JS_CACHE = {}; -export default function loadCustomPanel(panelConfig) { +export const loadCustomPanel = (panelConfig): Promise => { if (panelConfig.html_url) { const toLoad = [ import(/* webpackChunkName: "import-href-polyfill" */ "../../resources/html-import/import-href"), @@ -29,4 +29,4 @@ export default function loadCustomPanel(panelConfig) { return loadModule(panelConfig.module_url); } return Promise.reject("No valid url found in panel config."); -} +}; diff --git a/src/util/custom-panel/set-custom-panel-properties.js b/src/util/custom-panel/set-custom-panel-properties.ts similarity index 71% rename from src/util/custom-panel/set-custom-panel-properties.js rename to src/util/custom-panel/set-custom-panel-properties.ts index f54996cd5d..3aa8271ef8 100644 --- a/src/util/custom-panel/set-custom-panel-properties.js +++ b/src/util/custom-panel/set-custom-panel-properties.ts @@ -1,4 +1,4 @@ -export default function setCustomPanelProperties(root, properties) { +export const setCustomPanelProperties = (root, properties) => { if ("setProperties" in root) { root.setProperties(properties); } else { @@ -6,4 +6,4 @@ export default function setCustomPanelProperties(root, properties) { root[key] = properties[key]; }); } -} +}; diff --git a/src/util/hass-media-player-model.js b/src/util/hass-media-player-model.js index 10c32fc09d..79d837eeae 100644 --- a/src/util/hass-media-player-model.js +++ b/src/util/hass-media-player-model.js @@ -37,7 +37,9 @@ export default class MediaPlayerEntity { } get hasMediaControl() { - return ["playing", "paused", "unknown"].indexOf(this.stateObj.state) !== -1; + return ( + ["playing", "paused", "unknown", "on"].indexOf(this.stateObj.state) !== -1 + ); } get volumeSliderValue() { diff --git a/translations/af.json b/translations/af.json index 1371d3b086..fff2615ebc 100644 --- a/translations/af.json +++ b/translations/af.json @@ -7,71 +7,72 @@ "history": "Geskiedenis", "mailbox": "Posbus", "shopping_list": "Inkopielys", - "dev-services": "Services", - "dev-states": "States", - "dev-events": "Events", - "dev-templates": "Templates", + "dev-services": "Dienste", + "dev-states": "State", + "dev-events": "Gebeure", + "dev-templates": "Template", "dev-mqtt": "MQTT", - "dev-info": "Info" + "dev-info": "Info", + "calendar": "Kalender" }, "state": { "default": { "off": "Af", - "on": "Op", - "unknown": "Onbekende", + "on": "Aan", + "unknown": "Onbekend", "unavailable": "Nie beskikbaar nie" }, "alarm_control_panel": { "armed": "Gewapen", "disarmed": "Ontwapen", - "armed_home": "Gewapende huis", - "armed_away": "Gewapen weg", - "armed_night": "Gewapende nag", + "armed_home": "Gewapend tuis", + "armed_away": "Gewapend weg", + "armed_night": "Gewapend nag", "pending": "Hangende", "arming": "Bewapen", "disarming": "Ontwapen", - "triggered": "Veroorsaak", + "triggered": "Geaktiveer", "armed_custom_bypass": "Gewapende pasgemaakte omseil" }, "automation": { "off": "Af", - "on": "Op" + "on": "Aan" }, "binary_sensor": { "default": { "off": "Af", - "on": "Op" + "on": "Aan" }, "moisture": { - "off": "Droë", + "off": "Droog", "on": "Nat" }, "gas": { - "off": "Duidelike", - "on": "Opgespoor" + "off": "Ongemerk", + "on": "Bespeur" }, "motion": { - "off": "Duidelike", - "on": "Opgespoor" + "off": "Ongemerk", + "on": "Bespeur" }, "occupancy": { - "off": "Duidelike", - "on": "Opgespoor" + "off": "Ongemerk", + "on": "Bespeur" }, "smoke": { - "off": "Duidelike", - "on": "Opgespoor" + "off": "Ongemerk", + "on": "Bespeur" }, "sound": { - "off": "Duidelike", - "on": "Opgespoor" + "off": "Ongemerk", + "on": "Bespeur" }, "vibration": { - "off": "Duidelike", - "on": "Opgespoor" + "off": "Ongemerk", + "on": "Bespeur" }, "opening": { - "off": "Gesluit", + "off": "Toe", "on": "Oop" }, "safety": { @@ -79,17 +80,40 @@ "on": "Onveilige" }, "presence": { - "off": "Weg", - "on": "Huis" + "off": "Afwesig", + "on": "Tuis" }, "battery": { - "off": "Normale", - "on": "Lae" + "off": "Normaal", + "on": "Laag" + }, + "problem": { + "off": "OK", + "on": "Probleem" + }, + "cold": { + "on": "Koud" + }, + "door": { + "on": "Oop" + }, + "garage_door": { + "on": "Oop" + }, + "heat": { + "on": "Warm" + }, + "window": { + "on": "Oop" + }, + "lock": { + "off": "Gesluit", + "on": "Oopgesluit" } }, "calendar": { "off": "Af", - "on": "Op" + "on": "Aan" }, "camera": { "recording": "Opname", @@ -98,19 +122,20 @@ }, "climate": { "off": "Af", - "on": "Op", + "on": "Aan", "heat": "Hitte", - "cool": "Afkoel", + "cool": "Koel", "idle": "Ledig", "auto": "Outo", - "dry": "Droë", - "fan_only": "Slegs fan", + "dry": "Droog", + "fan_only": "Slegs waaier", "eco": "Eko", - "electric": "Elektriese", + "electric": "Elektries", "performance": "Prestasie", "high_demand": "Hoë aanvraag", "heat_pump": "Hitte pomp", - "gas": "Gas" + "gas": "Gas", + "manual": "Handmatig" }, "configurator": { "configure": "Konfigureer", @@ -119,65 +144,71 @@ "cover": { "open": "Oop", "opening": "Opening", - "closed": "Gesluit", - "closing": "Sluit", + "closed": "Toe", + "closing": "Sluiting", "stopped": "Gestop" }, "device_tracker": { - "home": "Huis", - "not_home": "Weg" + "home": "Tuis", + "not_home": "Afwesig" }, "fan": { "off": "Af", - "on": "Op" + "on": "Aan" }, "group": { "off": "Af", - "on": "Op", - "home": "Huis", - "not_home": "Weg", + "on": "Aan", + "home": "Tuis", + "not_home": "Afwesig", "open": "Oop", "opening": "Opening", - "closed": "Gesluit", - "closing": "Sluit", + "closed": "Toe", + "closing": "Sluiting", "stopped": "Gestop", "locked": "Gesluit", - "unlocked": "Ontsluit" + "unlocked": "Oopgesluit", + "ok": "OK", + "problem": "Probleem" }, "input_boolean": { "off": "Af", - "on": "Op" + "on": "Aan" }, "light": { "off": "Af", - "on": "Op" + "on": "Aan" }, "lock": { "locked": "Gesluit", - "unlocked": "Ontsluit" + "unlocked": "Oopgesluit" }, "media_player": { "off": "Af", - "on": "Op", + "on": "Aan", "playing": "Speel", "paused": "Onderbreek", "idle": "Ledig", - "standby": "Gereedheid" + "standby": "Staan by" + }, + "plant": { + "ok": "OK", + "problem": "Probleem" }, "remote": { "off": "Af", - "on": "Op" + "on": "Aan" }, "scene": { - "scening": "Scening" + "scening": "Toneeling" }, "script": { "off": "Af", - "on": "Op" + "on": "Aan" }, "sensor": { "off": "Af", - "on": "Op" + "on": "Aan" }, "sun": { "above_horizon": "Bo horison", @@ -185,7 +216,7 @@ }, "switch": { "off": "Af", - "on": "Op" + "on": "Aan" }, "zwave": { "default": { @@ -198,40 +229,723 @@ "initializing": "Inisialiseer ({query_stage})", "dead": "Dood ({query_stage})" } + }, + "weather": { + "clear-night": "Helder, nag", + "cloudy": "Bewolk", + "fog": "Mis", + "hail": "Hael", + "lightning": "Weerlig", + "lightning-rainy": "Weerlig, Reënagtig", + "partlycloudy": "Gedeeltelik bewolk", + "pouring": "Stort", + "rainy": "Reënagtig", + "snowy": "Sneeuagtig", + "snowy-rainy": "Ysreën", + "sunny": "Sonnig", + "windy": "Winderig", + "windy-variant": "Winderig" + }, + "vacuum": { + "cleaning": "Skoonmaak", + "docked": "Vasgemeer", + "on": "Aan", + "paused": "Onderbreek" + }, + "timer": { + "active": "aktief", + "idle": "onaktiewe", + "paused": "Onderbreek" + }, + "person": { + "home": "Tuis", + "not_home": "Afwesig" } }, "state_badge": { "default": { - "unknown": "Unk", - "unavailable": "Unavai" + "unknown": "?", + "unavailable": "Onbeskik" }, "alarm_control_panel": { - "armed": "Armed", - "disarmed": "Disarm", - "armed_home": "Armed", - "armed_away": "Armed", - "armed_night": "Armed", - "pending": "Pend", - "arming": "Arming", - "disarming": "Disarm", - "triggered": "Trig", - "armed_custom_bypass": "Armed" + "armed": "Gewapen", + "disarmed": "Ontwapen", + "armed_home": "Gewapen", + "armed_away": "Gewapen", + "armed_night": "Gewapen", + "pending": "Hangend", + "arming": "Bewapen", + "disarming": "Ontwapen", + "triggered": "Aktief", + "armed_custom_bypass": "Gewapen" }, "device_tracker": { - "home": "Huis", - "not_home": "Weg" + "home": "Tuis", + "not_home": "Afwesig" + }, + "person": { + "home": "Tuis", + "not_home": "Afwesig" } }, "ui": { "panel": { "shopping-list": { - "clear_completed": "Duidelik voltooide", - "add_item": "Voeg item" + "clear_completed": "Maak voltooide items skoon", + "add_item": "Voeg item", + "microphone_tip": "Druk die mikrofoon regs bo en sê \"Add candy to my shopping list\"" + }, + "mailbox": { + "delete_prompt": "Skrap hierdie boodskap?", + "delete_button": "Skrap" + }, + "config": { + "automation": { + "description": "Skep en wysig outomatisasies", + "picker": { + "introduction": "Die outomatiseringsredakteur stel jou in staat om outomatisasies te skep en te wysig. Volg die onderstaande skakel om die instruksies te lees om seker te maak dat u Home Assistant korrek opgestel het.", + "learn_more": "Kom meer te wete oor outomatisasies" + }, + "editor": { + "save": "Stoor", + "alias": "Naam", + "triggers": { + "duplicate": "Dupliseer", + "delete": "Skrap", + "delete_confirm": "Is jy seker jy wil dit skrap?", + "type": { + "event": { + "label": "Gebeurtenis", + "event_type": "Gebeurtenis tipe", + "event_data": "Gebeurtenis data" + }, + "state": { + "label": "Staat" + }, + "homeassistant": { + "event": "Gebeurtenis:" + }, + "numeric_state": { + "label": "Numeriese toestand", + "above": "Bo", + "below": "Onder", + "value_template": "Waarde templaat (opsioneel)" + }, + "sun": { + "label": "Son", + "event": "Gebeurtenis:", + "sunrise": "Sonsopkoms", + "sunset": "Sonsondergang", + "offset": "Verreken (opsioneel)" + }, + "template": { + "label": "Templaat", + "value_template": "Waarde templaat" + }, + "time": { + "label": "Tyd" + }, + "zone": { + "label": "Sone", + "entity": "Entiteit met plek", + "zone": "Sone", + "event": "Gebeurtenis:" + }, + "webhook": { + "label": "Webhook", + "webhook_id": "Webhook ID" + }, + "time_pattern": { + "label": "Tydpatroon", + "hours": "Ure", + "minutes": "Minute", + "seconds": "Sekondes" + }, + "geo_location": { + "label": "Ligginggewing", + "source": "Bron", + "zone": "Sone", + "event": "Gebeurtenis:", + "enter": "Betree", + "leave": "Verlaat" + } + }, + "learn_more": "Kom meer te wete oor snellers" + }, + "conditions": { + "header": "Voorwaardes", + "add": "Voeg voorwaarde by", + "duplicate": "Dupliseer", + "delete": "Skrap", + "delete_confirm": "Is jy seker jy wil dit skrap?", + "unsupported_condition": "Ongesteunde voorwaarde: {condition}", + "type_select": "Voorwaarde tipe", + "type": { + "state": { + "label": "Staat", + "state": "Staat" + }, + "numeric_state": { + "label": "Numeriese toestand", + "above": "Bo", + "below": "Onder", + "value_template": "Waarde templaat (opsioneel)" + }, + "sun": { + "label": "Son", + "before": "Voor:", + "after": "Na:", + "before_offset": "Voor verreken (opsioneel)", + "after_offset": "Na verreken (opsioneel)", + "sunrise": "Sonsopkoms", + "sunset": "Sonsondergang" + }, + "template": { + "label": "Templaat", + "value_template": "Waarde templaat" + }, + "time": { + "label": "Tyd", + "after": "Na", + "before": "Voor" + }, + "zone": { + "label": "Sone", + "entity": "Entiteit met plek", + "zone": "Sone" + } + }, + "learn_more": "Kom meer te wete oor die voorwaardes" + }, + "actions": { + "header": "Aksies", + "introduction": "Die aksies is wat Home Assistant sal doen wanneer die outomatisering geaktiveer word.", + "add": "Voeg aksie by", + "duplicate": "Dupliseer", + "delete": "Skrap", + "delete_confirm": "Is jy seker jy wil dit skrap?", + "unsupported_action": "Ongesteunde aksie: {action}", + "type_select": "Aksie tipe", + "type": { + "service": { + "label": "Roep diens", + "service_data": "Diens data" + }, + "delay": { + "label": "Vertraging", + "delay": "Vertraging" + }, + "wait_template": { + "label": "Wag", + "wait_template": "Wag Templaat", + "timeout": "Tyd verstreke (opsioneel)" + }, + "condition": { + "label": "Voorwaarde" + }, + "event": { + "label": "Vuur geval", + "event": "Gebeurtenis:", + "service_data": "Diens data" + } + }, + "learn_more": "Kom meer te wete oor aksies" + } + } + }, + "script": { + "description": "Skep en wysig skripte" + }, + "users": { + "caption": "Gebruikers", + "description": "Bestuur gebruikers", + "picker": { + "title": "Gebruikers" + }, + "editor": { + "rename_user": "Hernoem gebruiker", + "change_password": "Verander Wagwoord", + "activate_user": "Aktiveer gebruiker", + "deactivate_user": "Deaktiveer gebruiker", + "delete_user": "Skrap gebruiker", + "caption": "Bekyk gebruiker" + }, + "add_user": { + "caption": "Voeg gebruiker by", + "name": "Naam", + "username": "Gebruikersnaam", + "password": "Wagwoord", + "create": "Skep" + } + }, + "integrations": { + "config_entry": { + "delete_confirm": "Is jy seker jy wil hierdie integrasie skrap?", + "restart_confirm": "Herbegin Home Assistant om hierdie integrasie te voltooi", + "manuf": "deur {manufacturer}", + "hub": "Gekonnekteer via", + "firmware": "Fermware: {version}", + "device_unavailable": "toestel nie beskikbaar nie", + "entity_unavailable": "entiteit nie beskikbaar nie", + "no_area": "Geen Gebied" + } + }, + "zha": { + "caption": "ZHA", + "description": "Zigbee Home Automation netwerk bestuur", + "services": { + "reconfigure": "Herkonfigureer ZHA-toestel (heal device). Gebruik dit as jy probleme ondervind met die toestel. As die betrokke toestel 'n battery aangedrewe toestel is, maak asseblief seker dat dit wakker is en bevele aanvaar wanneer u hierdie diens gebruik.", + "updateDeviceName": "Stel 'n persoonlike naam vir hierdie toestel in die toestelregister" + } + }, + "area_registry": { + "caption": "Gebiedsregister", + "description": "Oorsig van alle gebiede in jou huis.", + "picker": { + "header": "Gebiedsregister", + "introduction": "Gebiede word gebruik om toestelle te organiseer gebaseer op waar hulle is. Hierdie inligting sal deur die Home Assistant gebruik word om u te help om u koppelvlak, toestemmings en integrasies met ander stelsels te organiseer.", + "introduction2": "Om toestelle in 'n gebied te plaas, gebruik die skakel hieronder om na die integrasies bladsy toe te gaan en klik dan op 'n gekonfigureerde integrasie om na die toestelkaarte toe te gaan.", + "integrations_page": "Integrasies bladsy", + "no_areas": "Dit lyk asof jy nog geen gebiede het nie!", + "create_area": "SKEP GEBIED" + }, + "no_areas": "Dit lyk asof jy nog geen gebiede het nie!", + "create_area": "SKEP GEBIED", + "editor": { + "default_name": "Nuwe Gebied", + "delete": "SKRAP", + "update": "OPDATEER", + "create": "SKEP" + } + }, + "entity_registry": { + "caption": "Entiteit Register", + "description": "Oorsig van alle bekende entiteite.", + "picker": { + "header": "Entiteit Register", + "unavailable": "(nie beskikbaar nie)", + "introduction": "Home Assistant hou 'n register van al die vorige entiteite wat uniek geïdentifiseer kan word. Elk van hierdie entiteite sal 'n entiteit-ID hê wat vir hierdie entiteit gereserveer sal word.", + "introduction2": "Gebruik die entiteitsregister om die naam te oorskryf, die entiteit ID te verander, of die inskrywing van Home Assistant te verwyder. Let wel, die verwydering van die entiteit registerinskrywing sal nie die entiteit verwyder nie. Om dit te doen, volg die skakel hieronder en verwyder dit uit die integrasies bladsy.", + "integrations_page": "Integrasies bladsy" + }, + "editor": { + "unavailable": "Hierdie entiteit is tans nie beskikbaar nie.", + "default_name": "Nuwe Gebied", + "delete": "SKRAP", + "update": "OPDATEER" + } + }, + "customize": { + "picker": { + "header": "Pasgemaakte Instellings ", + "introduction": "Verfyn per-entiteit eienskappe. Bygevoegde \/ gewysigde aanpassings sal onmiddellik in werking tree. Verwyderde aanpassings sal in werkin tree wanneer die entiteit opgedateer word." + } + }, + "person": { + "caption": "Persone", + "description": "Bestuur die persone wat Home Assistant op spoor.", + "detail": { + "name": "Naam", + "device_tracker_intro": "Kies die toestelle wat aan hierdie persoon behoort.", + "device_tracker_picked": "Spoor toestel op", + "device_tracker_pick": "Kies toestel om op te spoor" + } + } + }, + "page-authorize": { + "form": { + "working": "Wag asseblief", + "providers": { + "homeassistant": { + "step": { + "init": { + "data": { + "username": "Gebruikersnaam", + "password": "Wagwoord" + } + }, + "mfa": { + "data": { + "code": "Twee-faktor-Verifikasiekode" + }, + "description": "Maak die ** {mfa_module_name} ** op jou toestel oop om jou twee-faktor-verifikasiekode te sien en jou identiteit te verifieer:" + } + }, + "error": { + "invalid_auth": "Ongeldige Gebruikersnaam of wagwoord", + "invalid_code": "Ongeldige verifikasiekode" + }, + "abort": { + "login_expired": "Sessie verstryk, teken asseblief weer aan." + } + }, + "legacy_api_password": { + "step": { + "mfa": { + "data": { + "code": "Twee-faktor-Verifikasiekode" + }, + "description": "Maak die ** {mfa_module_name} ** op jou toestel oop om jou twee-faktor-verifikasiekode te sien en jou identiteit te verifieer:" + } + }, + "error": { + "invalid_code": "Ongeldige verifikasiekode" + }, + "abort": { + "login_expired": "Sessie verstryk, teken asseblief weer aan." + } + }, + "command_line": { + "step": { + "init": { + "data": { + "username": "Gebruikersnaam", + "password": "Wagwoord" + } + }, + "mfa": { + "data": { + "code": "Twee-faktor-Verifikasiekode" + }, + "description": "Maak die ** {mfa_module_name} ** op jou toestel oop om jou twee-faktor-verifikasiekode te sien en jou identiteit te verifieer:" + } + }, + "error": { + "invalid_auth": "Ongeldige gebruikersnaam of wagwoord", + "invalid_code": "Ongeldige verifikasiekode" + }, + "abort": { + "login_expired": "Sessie verstryk, teken asseblief weer aan." + } + } + } + } + }, + "page-onboarding": { + "user": { + "data": { + "name": "Naam", + "username": "Gebruikersnaam", + "password": "Wagwoord", + "password_confirm": "Bevestig Wagwoord" + }, + "create_account": "Skep Rekening", + "error": { + "password_not_match": "Wagwoorde stem nie ooreen nie" + } + } + }, + "profile": { + "refresh_tokens": { + "header": "Verfris-tekseenhede", + "description": "Elke verfris-tekseenheid verteenwoordig 'n aanmeldingssessie. Verfris-tekseenhede sal outomaties verwyder word wanneer u op meld af klik. Die volgende verfris-tekseenhede is tans aktief vir u rekening.", + "token_title": "Verfris-tekseenheid vir {clientId}", + "created_at": "Geskep op {date}", + "confirm_delete": "Is jy seker jy wil die verfris-tekseenheid vir {name} skrap?", + "delete_failed": "Het misluk om die verfris-tekseenheid te skrap.", + "last_used": "Laas gebruik op {date} vanaf {location}", + "not_used": "Is nog nooit gebruik nie", + "current_token_tooltip": "Nie in staat om huidige verfris-tekseenheid te skrap nie" + }, + "long_lived_access_tokens": { + "header": "Langlewende-toegangs-tekseenhede", + "description": "Skep langlewende-toegangs-tekseenhede om jou skripte in staat te stel om met jou Home Assistant-instansie te kommunikeer. Elke tekseenheid sal geldig wees vir 10 jaar vanaf die skepping. Die volgende langlewende-toegangs-tekseenheid is tans aktief.", + "learn_auth_requests": "Leer hoe om geverifieerde versoeke te maak.", + "created_at": "Geskep op {date}", + "confirm_delete": "Is jy seker jy wil die toegangs-tekseenheid vir {name} skrap?", + "delete_failed": "Het misluk om die toegangs-tekseenheid te skrap.", + "create": "Skep Tekseenheid", + "create_failed": "Het misluk om die toegangs-tekseenheid te maak.", + "prompt_name": "Naam?", + "prompt_copy_token": "Kopieer jou toegangs-tekseenheid. Dit sal nie weer gewys word nie.", + "empty_state": "Jy het nog geen langlewende-toegangs-tekseenhede nie.", + "last_used": "Laas gebruik op {date} vanaf {location}", + "not_used": "Is nog nooit gebruik nie" + }, + "logout": "Meld af", + "change_password": { + "header": "Verander Wagwoord", + "current_password": "Huidige Wagwoord", + "new_password": "Nuwe Wagwoord", + "confirm_new_password": "Bevestig Nuwe Wagwoord", + "submit": "Dien in" + } + }, + "lovelace": { + "cards": { + "shopping-list": { + "checked_items": "Gemerkte items", + "clear_items": "Maak die gemerkte items skoon", + "add_item": "Voeg item by" + }, + "empty_state": { + "title": "Welkom tuis", + "no_devices": "Hierdie bladsy laat jou toe om jou toestelle te beheer, maar dit lyk asof jy nog nie toestelle opgestel het nie. Gaan na die integrasies bladsy om te begin.", + "go_to_integrations_page": "Gaan na die integrasies bladsy." + } + }, + "editor": { + "edit_card": { + "header": "Kaartkonfigurasie", + "save": "Stoor", + "toggle_editor": "Wissel redigeerder", + "pick_card": "Kies die kaart wat jy wil byvoeg.", + "add": "Voeg Kaart by", + "edit": "Wysig", + "delete": "Skrap", + "move": "Skuif" + }, + "migrate": { + "header": "Konfigurasie Onversoenbaar", + "para_no_id": "Hierdie element het nie 'n ID nie. Voeg asseblief 'n ID by vir hierdie element in 'ui-lovelace.yaml'.", + "para_migrate": "Druk die 'Migreer konfigurasie' knoppie as jy wil hê Home Assistant moet vir jou ID's by al jou kaarte en aansigte outomaties byvoeg.", + "migrate": "Migreer konfigurasie" + }, + "header": "Wysig gebruikerskoppelvlak", + "edit_view": { + "header": "Bekyk konfigurasie", + "add": "Voeg aansig by", + "edit": "Wysig aansig", + "delete": "Skrap aansig" + }, + "save_config": { + "header": "Neem beheer van jou Lovelace gebruikerskoppelvlak", + "para": "Home Assistant sal outomaties u gebruikerskoppelvlak handhaaf, dit opdateer wanneer nuwe entiteite of Lovelace-komponente beskikbaar raak. Alhoewel, as u beheer neem, sal Home Assistant nie meer outomaties veranderings vir u doen nie.", + "para_sure": "Is jy seker jy wil beheer neem oor jou gebruikerskoppelvlak?", + "cancel": "Toemaar", + "save": "Neem beheer" + }, + "menu": { + "raw_editor": "Plat konfigurasie-redigeerder" + }, + "raw_editor": { + "header": "Wysig Konfigurasie", + "save": "Stoor", + "unsaved_changes": "Ongestoorde veranderinge", + "saved": "Gestoor" + } + }, + "menu": { + "configure_ui": "Konfigureer gebruikerskoppelvlak", + "unused_entities": "Ongebruikte entiteite", + "help": "Help", + "refresh": "Verfris" + }, + "warning": { + "entity_not_found": "Entiteit nie beskikbaar nie: {entity}", + "entity_non_numeric": "Entiteit is nie-numeriese: {entity}" + } + }, + "logbook": { + "period": "Tydperk" } }, "sidebar": { - "log_out": "Teken", + "log_out": "Meld af", "developer_tools": "Ontwikkelaar gereedskap" + }, + "duration": { + "day": "{count} {count, plural,\n one {dag}\n other {dae}\n}", + "week": "{count} {count, plural,\n one {week}\n other {weke}\n}", + "second": "{count} {count, plural,\n one {sekonde}\n other {sekondes}\n}", + "minute": "{count} {count, plural,\none {minuut}\nother {minute}\n}", + "hour": "{count} {count, plural,\n one {uur}\n other {ure}\n}" + }, + "login-form": { + "password": "Wagwoord" + }, + "card": { + "scene": { + "activate": "Aktiveer" + }, + "script": { + "execute": "Voer uit" + }, + "weather": { + "attributes": { + "air_pressure": "Lugdruk", + "humidity": "Humiditeit", + "temperature": "Temperatuur", + "visibility": "Sigbaarheid", + "wind_speed": "Wind spoed" + }, + "cardinal_direction": { + "e": "O", + "ene": "ONO", + "ese": "OSO", + "n": "N", + "ne": "NO", + "nne": "NNO", + "nw": "NW", + "nnw": "NNW", + "s": "S", + "se": "SO", + "sse": "SSO", + "ssw": "SSW", + "sw": "SW", + "w": "W", + "wnw": "WNW", + "wsw": "WSW" + }, + "forecast": "Voorspelling" + }, + "alarm_control_panel": { + "code": "Kode", + "clear_code": "Maak skoon", + "disarm": "Skakel Af", + "arm_home": "Skakel tuis aan", + "arm_away": "Skakel weg aan", + "arm_night": "Nag alarm", + "armed_custom_bypass": "Pasgemaakte omseil", + "arm_custom_bypass": "Pasgemaakte omseil" + }, + "automation": { + "last_triggered": "Laas geaktiveer", + "trigger": "Sneller" + }, + "cover": { + "position": "Posisie", + "tilt_position": "Kantel posisie" + }, + "fan": { + "speed": "Spoed", + "oscillate": "Ossilleer", + "direction": "Rigting" + }, + "light": { + "brightness": "Helderheid", + "color_temperature": "Kleur temperatuur", + "white_value": "Wit ligwaarde", + "effect": "Effek" + }, + "media_player": { + "text_to_speak": "Teks na spraak", + "source": "Bron", + "sound_mode": "Klank modus" + }, + "climate": { + "currently": "Tans", + "on_off": "Aan \/ af", + "target_temperature": "Teiken temperatuur", + "target_humidity": "Teiken humiditeit", + "operation": "Operasie", + "fan_mode": "Waaier modus", + "swing_mode": "Swaai modus", + "away_mode": "Wegmodus", + "aux_heat": "Aanvullende hitte" + }, + "lock": { + "code": "Kode", + "lock": "Sluit toe", + "unlock": "Sluit oop" + }, + "water_heater": { + "currently": "Tans", + "on_off": "Aan \/ af", + "target_temperature": "Teiken temperatuur", + "operation": "Operasie", + "away_mode": "Afwesig modus" + } + }, + "components": { + "relative_time": { + "past": "{time} gelede", + "future": "In {time}", + "never": "Nooit", + "duration": { + "second": "{count} {count, plural,\n one {sekonde}\n other {sekondes}\n}", + "minute": "{count} {count, plural,\n one {minuut}\n other {minute}\n}", + "hour": "{count} {count, plural,\n one {uur}\n other {ure}\n}", + "day": "{count} {count, plural,\n one {dag}\n other {dae}\n}", + "week": "{count} {count, plural,\n one {week}\n other {weke}\n}" + } + }, + "history_charts": { + "loading_history": "Laai tans staatsgeskiedenis ...", + "no_history_found": "Geen staatgeskiedenis gevind nie." + } + }, + "notification_toast": { + "entity_turned_on": "{entity} aangeskakel.", + "entity_turned_off": "{entity} afgeskakel.", + "service_called": "Diens {service} geroep.", + "service_call_failed": "Het misluk om diens {service} te roep.", + "connection_lost": "Konneksie verlore. Herkoppel tans..." + }, + "dialogs": { + "more_info_settings": { + "save": "Stoor", + "name": "Naam Oorheers", + "entity_id": "Entiteit ID" + }, + "more_info_control": { + "script": { + "last_action": "Laaste Aksie" + }, + "sun": { + "elevation": "Hoogte", + "rising": "Stygende", + "setting": "Instelling" + }, + "updater": { + "title": "Opdateringsinstruksies" + } + } + }, + "common": { + "save": "Stoor" } + }, + "domain": { + "alarm_control_panel": "Alarm beheer paneel", + "automation": "Outomatisering", + "binary_sensor": "Binêre sensor", + "calendar": "Kalender", + "camera": "Kamera", + "climate": "Klimaat", + "configurator": "Konfigureerder", + "conversation": "Konversasie", + "cover": "Dekking", + "device_tracker": "Toestel opspoorder", + "fan": "Waaier", + "history_graph": "Geskiedenis grafiek", + "group": "Groep", + "image_processing": "Beeldverwerking", + "input_boolean": "Invoer boole", + "input_datetime": "Invoer datum\/tyd", + "input_select": "Invoer seleksie", + "input_number": "Invoer nommer", + "input_text": "Invoer teks", + "light": "Lig", + "lock": "Slot", + "mailbox": "Posbus", + "script": "Skrip", + "sensor": "Sensor", + "sun": "Son", + "switch": "Skakelaar", + "updater": "Opdateerder", + "weblink": "Web skakel", + "zwave": "Z-Wave", + "vacuum": "Vakuum", + "zha": "ZHA", + "hassio": "Hass.io", + "homeassistant": "Home Assistant", + "lovelace": "Lovelace", + "system_health": "Stelsel Gesondheid", + "person": "Persoon" + }, + "state_attributes": { + "climate": { + "fan_mode": { + "off": "Af", + "on": "Aan", + "auto": "Outomatiese" + } + } + }, + "groups": { + "system-admin": "Administrateurs", + "system-users": "Gebruikers", + "system-read-only": "Leesalleen-gebruikers" } } \ No newline at end of file diff --git a/translations/bg.json b/translations/bg.json index b5ffc0e321..609671ef64 100644 --- a/translations/bg.json +++ b/translations/bg.json @@ -128,7 +128,7 @@ "camera": { "recording": "Записване", "streaming": "Предава", - "idle": "Неработещ" + "idle": "Не записва" }, "climate": { "off": "Изключен", diff --git a/translations/cy.json b/translations/cy.json index ccb99c6506..e98a9dbbba 100644 --- a/translations/cy.json +++ b/translations/cy.json @@ -661,8 +661,8 @@ } }, "warning": { - "entity_not_found": "Endid ddim ar gael", - "entity_non_numeric": "Endid di-rhifol: {endid}" + "entity_not_found": "Endid ddim ar gael: {entity}", + "entity_non_numeric": "Endid di-rhifol: {entity}" } }, "page-authorize": { diff --git a/translations/da.json b/translations/da.json index 4cb3a2a6fc..8a9c0b7b7c 100644 --- a/translations/da.json +++ b/translations/da.json @@ -611,13 +611,13 @@ } }, "area_registry": { - "caption": "Område registrering", + "caption": "Områderegistrering", "description": "Oversigt over alle områder i dit hjem.", "picker": { - "header": "Område registrering", + "header": "Områderegistrering", "introduction": "Områder bruges til at organisere hvor enhederne er. Disse oplysninger vil blive brugt i Home Assistant til at hjælpe dig med at organisere din grænseflade, tilladelser og integrationer med andre systemer.", "introduction2": "Hvis du vil placere enheder i et område, skal du bruge linket herunder til at navigere til integrationssiden og derefter klikke på en konfigureret integration for at komme til enhedskortene.", - "integrations_page": "Integrations side", + "integrations_page": "Integrationsside", "no_areas": "Du ikke har ingen områder endnu!", "create_area": "OPRET OMRÅDE" }, @@ -631,14 +631,14 @@ } }, "entity_registry": { - "caption": "Enheds registrering", + "caption": "Enhedsregistrering", "description": "Oversigt over alle kendte enheder.", "picker": { - "header": "Enheds registrering", + "header": "Enhedsregistrering", "unavailable": "(utilgængelig)", "introduction": "Home Assistant opbevarer en registrering over hver unikke enhed det nogensinde har set. Hver af disse enheder vil have et registrerings ID tildelt som er forbeholdt netop denne enhed.", "introduction2": "Brug enhedsregistrering til at overskrive navnet, ændre enheds ID eller fjerne enheden fra Home Assistant. Bemærk at fjernelse af enhedens registrering ikke fjerner enheden. For at gøre det skal du følge linket herunder og fjerne det fra integrationssiden.", - "integrations_page": "Integrations side" + "integrations_page": "Integrationsside" }, "editor": { "unavailable": "Denne enhed er ikke tilgængelig i øjeblikket.", @@ -1143,7 +1143,7 @@ "hassio": "Hass.io", "homeassistant": "Home Assistant", "lovelace": "Lovelace", - "system_health": "System sundhed", + "system_health": "Systemsundhed", "person": "Person" }, "attribute": { diff --git a/translations/de.json b/translations/de.json index c94ae68c52..07831e73ea 100644 --- a/translations/de.json +++ b/translations/de.json @@ -272,7 +272,7 @@ "paused": "pausiert" }, "person": { - "home": "Zuhause", + "home": "Zu Hause", "not_home": "Abwesend" } }, @@ -298,7 +298,7 @@ "not_home": "Abwes." }, "person": { - "home": "Zuhause", + "home": "Zu Hause", "not_home": "Abwesend" } }, diff --git a/translations/es-419.json b/translations/es-419.json index 600d3b461f..2bb0d88c06 100644 --- a/translations/es-419.json +++ b/translations/es-419.json @@ -213,8 +213,8 @@ "scening": "Reproduciendo Escena" }, "script": { - "off": "", - "on": "" + "off": "Apagado", + "on": "Encendido" }, "sensor": { "off": "", diff --git a/translations/eu.json b/translations/eu.json index 132f442750..4551db2cc7 100644 --- a/translations/eu.json +++ b/translations/eu.json @@ -1,11 +1,862 @@ { "panel": { "config": "Konfigurazioa", - "states": "Orokorra", + "states": "Laburpena", "map": "Mapa", - "logbook": "Logbook", + "logbook": "Erregistroa", "history": "Historia", "mailbox": "Postontzia", - "shopping_list": "Erosketa zerrenda" + "shopping_list": "Erosketa zerrenda", + "dev-services": "Zerbitzuak", + "dev-states": "Egoerak", + "dev-events": "Gertaerak", + "dev-templates": "Txantiloiak", + "dev-mqtt": "MQTT", + "dev-info": "Informazioa", + "calendar": "Egutegia", + "profile": "Profila" + }, + "state": { + "default": { + "off": "Itzalita", + "on": "Piztuta", + "unknown": "Ezezaguna", + "unavailable": "Ez dago erabilgarri" + }, + "alarm_control_panel": { + "pending": "Zain", + "triggered": "Abiarazita" + }, + "automation": { + "off": "Itzalita", + "on": "Piztuta" + }, + "binary_sensor": { + "default": { + "off": "Itzalita", + "on": "Piztuta" + }, + "moisture": { + "off": "Lehorra", + "on": "Buztita" + }, + "opening": { + "off": "Itxita", + "on": "Ireki" + }, + "safety": { + "off": "Babestuta" + }, + "presence": { + "off": "Kanpoan", + "on": "Etxean" + }, + "battery": { + "off": "Normala", + "on": "Baxua" + }, + "problem": { + "off": "Ondo", + "on": "Arazoa" + }, + "connectivity": { + "off": "Deskonektatuta", + "on": "Konektatuta" + }, + "cold": { + "off": "Normala", + "on": "Hotza" + }, + "door": { + "off": "Itxita", + "on": "Ireki" + }, + "garage_door": { + "off": "Itxita", + "on": "Ireki" + }, + "heat": { + "off": "Normala", + "on": "Beroa" + }, + "window": { + "off": "Itxita", + "on": "Ireki" + }, + "lock": { + "off": "Itxita", + "on": "Irekita" + } + }, + "calendar": { + "off": "Itzalita", + "on": "Piztuta" + }, + "camera": { + "recording": "Grabatzen" + }, + "climate": { + "off": "Itzalita", + "on": "Piztuta", + "heat": "Beroa", + "cool": "Hotza", + "auto": "Automatikoa", + "dry": "Lehorra", + "fan_only": "Haizagailua bakarrik", + "eco": "Eko", + "electric": "Elektrikoa", + "performance": "Errendimendua", + "high_demand": "Eskari handia", + "heat_pump": "Bero-ponpa", + "gas": "Gasa" + }, + "configurator": { + "configure": "Konfiguratu", + "configured": "Konfiguratuta" + }, + "cover": { + "open": "Irekita", + "opening": "Irekitzen", + "closed": "Itxita", + "closing": "Ixten", + "stopped": "Geldituta" + }, + "device_tracker": { + "home": "Etxean", + "not_home": "Kanpoan" + }, + "fan": { + "off": "Itzalita", + "on": "Piztuta" + }, + "group": { + "off": "Itzalita", + "on": "Piztuta", + "home": "Etxean", + "not_home": "Kanpoan", + "open": "Ireki", + "opening": "Irekitzen", + "closed": "Itxita", + "closing": "Ixten", + "stopped": "Geldirik", + "ok": "Itzalita", + "problem": "Arazoa" + }, + "input_boolean": { + "off": "Itzalita", + "on": "Piztuta" + }, + "light": { + "off": "Itzalita", + "on": "Piztuta" + }, + "media_player": { + "off": "Itzalita", + "on": "Piztuta" + }, + "plant": { + "ok": "Itzalita", + "problem": "Arazoa" + }, + "remote": { + "off": "Itzalita", + "on": "Piztuta" + }, + "script": { + "off": "Itzalita", + "on": "Piztuta" + }, + "sensor": { + "off": "Itzalita", + "on": "Piztuta" + }, + "sun": { + "above_horizon": "Horizonte gainetik", + "below_horizon": "Horizonte azpitik" + }, + "switch": { + "off": "Itzalita", + "on": "Piztuta" + }, + "zwave": { + "default": { + "initializing": "Hasieratzen", + "dead": "Hilda", + "sleeping": "Lotan", + "ready": "Prest" + }, + "query_stage": { + "initializing": "Hasieratzen ({query_stage})", + "dead": "Ez du erantzuten ({query_stage})" + } + }, + "weather": { + "clear-night": "Garbia, gaua", + "cloudy": "Hodeitsua", + "fog": "Lainoa", + "hail": "Txingorra", + "lightning": "Tximistak", + "lightning-rainy": "Tximistak, euritsua", + "partlycloudy": "Ostarteak", + "pouring": "Botatzen", + "rainy": "Euritsua", + "snowy": "Elurtsua", + "snowy-rainy": "Elurtsua, euritsua", + "sunny": "Eguzkitsua", + "windy": "Haizetsua", + "windy-variant": "Haizetsua" + }, + "vacuum": { + "cleaning": "Garbitzen", + "docked": "Basean", + "error": "Errorea", + "off": "Itzalita", + "on": "Piztuta", + "returning": "Basera itzultzen" + }, + "person": { + "home": "Etxean", + "not_home": "Kanpoan" + } + }, + "state_badge": { + "device_tracker": { + "home": "Etxean", + "not_home": "Kanpoan" + }, + "person": { + "home": "Etxean", + "not_home": "Kanpoan" + } + }, + "ui": { + "panel": { + "shopping-list": { + "clear_completed": "Osatutakoak ezabatu", + "add_item": "Artikulua gehitu" + }, + "mailbox": { + "empty": "Ez duzu mezurik", + "delete_prompt": "Mezu hau ezabatu?", + "delete_button": "Ezabatu" + }, + "config": { + "core": { + "caption": "Orokorra", + "section": { + "core": { + "reloading": { + "core": "Nukleoa birkargatu", + "group": "Taldeak birkargatu", + "automation": "Automatizazioak birkargatu", + "script": "Scriptak birkargatu" + }, + "server_management": { + "heading": "Zerbitzariaren kudeaketa", + "introduction": "Zure Home Assistant zerbitzaria... Home Assistantetik kontrolatu", + "restart": "Berrabiarazi", + "stop": "Gelditu" + } + } + } + }, + "script": { + "caption": "Script", + "description": "Scriptak sortu eta editatu" + }, + "zwave": { + "caption": "Z-Wave" + }, + "automation": { + "picker": { + "add_automation": "Automatizazioa gehitu", + "learn_more": "Automatizazioei buruz gehiago ikasi" + }, + "editor": { + "default_name": "Automatizazio berria", + "save": "Gorde", + "alias": "Izena", + "triggers": { + "header": "Abiarazleak", + "add": "Abiarazlea gehitu", + "duplicate": "Bikoiztu", + "delete": "Ezabatu", + "type_select": "Abiarazle mota", + "type": { + "event": { + "label": "Gertaera", + "event_type": "Gertaera mota" + }, + "state": { + "label": "Egoera" + }, + "homeassistant": { + "label": "Home Assistant", + "event": "Gertaera:", + "start": "Hasi", + "shutdown": "Itzali" + }, + "mqtt": { + "label": "MQTT", + "topic": "Gaia" + }, + "numeric_state": { + "above": "Honen gainetik", + "below": "Honen azpitik" + }, + "sun": { + "label": "Eguzkia", + "event": "Gertaera:", + "sunrise": "Egunsentia", + "sunset": "Ilunabarra" + }, + "template": { + "label": "Txantiloia", + "value_template": "Balio txantiloia" + }, + "time": { + "label": "Ordua", + "at": "Noiz" + }, + "zone": { + "enter": "Sartu", + "leave": "Utzi" + }, + "time_pattern": { + "hours": "Orduak", + "minutes": "Minutuak", + "seconds": "Segunduak" + }, + "geo_location": { + "label": "Geokokapena", + "source": "Iturria", + "event": "Gertaera:", + "enter": "Sartu", + "leave": "Utzi" + } + }, + "learn_more": "Abiarazleei buruz gehiago ikasi" + }, + "conditions": { + "header": "Baldintzak", + "add": "Baldintza gehitu", + "duplicate": "Bikoiztu", + "delete": "Ezabatu", + "type_select": "Baldintza mota", + "type": { + "state": { + "label": "Egoera", + "state": "Egoera" + }, + "sun": { + "label": "Eguzkia", + "sunrise": "Egunsentia", + "sunset": "Ilunabarra" + }, + "template": { + "label": "Txantiloia", + "value_template": "Valio txantiloia" + }, + "time": { + "label": "Denbora" + } + }, + "learn_more": "Baldintzei buruz gehiago ikasi" + }, + "actions": { + "header": "Ekintzak", + "add": "Ekintza gehitu", + "duplicate": "Bikoiztu", + "delete": "Ezabatu", + "unsupported_action": "Ekintza ez onartua: {action}", + "type_select": "Ekintza mota", + "type": { + "service": { + "label": "Zerbitzua deitu" + }, + "delay": { + "label": "Atzerapena", + "delay": "Atzerapena" + }, + "wait_template": { + "label": "Itxaron", + "wait_template": "Itxaron txantiloia" + }, + "condition": { + "label": "Baldintza" + } + }, + "learn_more": "Ekintzei buruz gehiago ikasi" + } + } + }, + "users": { + "caption": "Erabiltzaileak", + "description": "Erabiltzaileak kudeatu", + "picker": { + "title": "Erabiltzaileak" + }, + "editor": { + "rename_user": "Erabiltzailea berrizendatu", + "change_password": "Pasahitza aldatu", + "activate_user": "Erabiltzailea aktibatu", + "deactivate_user": "Erabiltzailea desaktibatu", + "delete_user": "Erabiltzailea ezabatu", + "caption": "Erabiltzailea ikusi" + }, + "add_user": { + "caption": "Erabiltzailea gehitu", + "name": "Izena", + "username": "Erabiltzaile izena", + "password": "Pasahitza", + "create": "Sortu" + } + }, + "cloud": { + "caption": "Home Assistant Cloud", + "description_login": "{email} bezala hasi duzu saioa", + "description_not_login": "Ez da saioa hasi" + }, + "integrations": { + "caption": "Integrazioak", + "configured": "Konfiguratuta", + "new": "Integrazio berri bat konfiguratu", + "configure": "Konfiguratu", + "none": "Ez dago ezer konfiguratuta", + "config_entry": { + "no_devices": "Integrazio honek ez du gailurik.", + "firmware": "Firmware: {version}", + "no_area": "Ez dago gunerik" + } + }, + "zha": { + "caption": "ZHA", + "services": { + "updateDeviceName": "Gailu honentzako izen pertsonalizatua ezarri gailuen erregistroan." + } + }, + "area_registry": { + "picker": { + "header": "Gune Erregistroa", + "integrations_page": "Integrazioen orria", + "no_areas": "Oraindik gunerik ez duzula dirudi!", + "create_area": "GUNEA SORTU" + }, + "create_area": "GUNEA SORTU", + "editor": { + "default_name": "Gune berria", + "delete": "EZABATU", + "update": "EGUNERATU", + "create": "SORTU" + } + }, + "entity_registry": { + "picker": { + "unavailable": "(ez dago eskuragarri)", + "integrations_page": "Integrazioak" + }, + "editor": { + "default_name": "Gune berria", + "delete": "EZABATU", + "update": "EGUNERATU" + } + }, + "person": { + "caption": "Pertsonak", + "detail": { + "name": "Izena" + } + } + }, + "profile": { + "push_notifications": { + "header": "Push jakinarazpenak", + "error_load_platform": "notify.html5 konfiguratu.", + "push_notifications": "Push jakinarazpenak", + "link_promo": "Gehiago ikasi" + }, + "language": { + "header": "Hizkuntza", + "link_promo": "Itzultzen lagundu", + "dropdown_label": "Hizkuntza" + }, + "themes": { + "header": "Gaia", + "error_no_theme": "Ez dago gairik eskuragarri", + "link_promo": "Gaiei buruz gehiago ikasi", + "dropdown_label": "Gaia" + }, + "refresh_tokens": { + "header": "Tokenak eguneratu", + "delete_failed": "Errorea sortu da sarbide tokena ezabatzerakoan.", + "not_used": "Ez da inoiz erabili", + "current_token_tooltip": "Ezin da uneko tokena ezabatu" + }, + "long_lived_access_tokens": { + "header": "Iraupen luzeko sarbide tokenak", + "delete_failed": "Errorea sortu da sarbide tokena ezabatzerakoan.", + "create": "Tokena Sortu", + "create_failed": "Ezin izan da sarbide token sortu.", + "prompt_name": "Izena?", + "prompt_copy_token": "Zure sarbide tokena kopiatu. Ez da berriro erakutsiko.", + "not_used": "Ez da inoiz erabili" + }, + "current_user": "{fullName} moduan hasi duzu saioa.", + "is_owner": "Jabea zara", + "logout": "Saioa itxi", + "change_password": { + "header": "Pasahitza aldatu", + "current_password": "Egungo pasahitza", + "new_password": "Pasahitz berria", + "confirm_new_password": "Pasahitz berria baieztatu", + "error_required": "Beharrezkoa", + "submit": "Bidali" + }, + "mfa": { + "enable": "Gaitu", + "confirm_disable": "{name} desgaitu nahi duzula ziur zaude?" + }, + "mfa_setup": { + "title_success": "Arrakasta!", + "close": "Itxi", + "submit": "Bidali" + } + }, + "page-authorize": { + "initializing": "Hasieratzen", + "form": { + "working": "Mesedez, itxaron", + "unknown_error": "Zerbait gaizki joan da", + "providers": { + "homeassistant": { + "step": { + "init": { + "data": { + "username": "Erabiltzaile izena", + "password": "Pasahitza" + } + }, + "mfa": { + "data": { + "code": "Bi faktoreko autentifikazio kodea" + } + } + }, + "error": { + "invalid_auth": "Erabiltzaile edo pasahitz okerra" + } + }, + "legacy_api_password": { + "step": { + "init": { + "data": { + "password": "API pasahitza" + } + } + } + }, + "trusted_networks": { + "step": { + "init": { + "data": { + "user": "Erabiltzailea" + } + } + }, + "abort": { + "not_whitelisted": "Zure ordenagailua ez dago baimenduta." + } + }, + "command_line": { + "step": { + "init": { + "data": { + "username": "Erabiltzaile izena", + "password": "Pasahitza" + } + } + } + } + } + } + }, + "page-onboarding": { + "user": { + "intro": "Erabiltzaile kontu bat sortuz has gaitezen.", + "required_field": "Beharrezkoa", + "data": { + "name": "Izena", + "username": "Erabiltzaile izena", + "password": "Pasahitza", + "password_confirm": "Pasahitza baieztatu" + }, + "create_account": "Kontua sortu", + "error": { + "required_fields": "Beharrezkoak diren eremu guztiak bete", + "password_not_match": "Pasahitzak ez datoz bat" + } + } + }, + "lovelace": { + "cards": { + "shopping-list": { + "checked_items": "Aukeratutako elementuak", + "add_item": "Elementua gehitu" + }, + "empty_state": { + "title": "Ongi etorri Etxera", + "go_to_integrations_page": "Integrazioen orrira joan." + } + }, + "editor": { + "edit_card": { + "header": "Txartelaren konfigurazioa", + "save": "Gorde", + "add": "Txartela gehitu", + "edit": "Editatu", + "delete": "Ezabatu", + "move": "Mugitu" + }, + "migrate": { + "header": "Konfigurazio Bateraezina", + "migrate": "Konfigurazioa migratu" + }, + "header": "Erabiltzaile interfazea editatu", + "edit_view": { + "header": "Konfigurazioa ikusi", + "add": "Bista gehitu", + "edit": "Bista editatu", + "delete": "Bista ezabatu" + }, + "save_config": { + "cancel": "Berdin dio", + "save": "Kontrola hartu" + }, + "raw_editor": { + "header": "Ezarpenak aldatu", + "save": "Gorde", + "unsaved_changes": "Gorde gabeko aldaketak", + "saved": "Gordeta" + } + }, + "menu": { + "configure_ui": "Erabiltzaile interfazea konfiguratu", + "help": "Laguntza" + } + }, + "logbook": { + "period": "Epea" + } + }, + "sidebar": { + "log_out": "Saioa itxi", + "developer_tools": "Garatzaileentzako tresnak" + }, + "common": { + "loading": "Kargatzen", + "save": "Gorde" + }, + "duration": { + "day": "{count} {count, plural,\n one {egun}\n other {egun}\n}", + "week": "{count} {count, plural,\n one {aste}\n other {aste}\n}", + "second": "{count} {count, plural,\n one {segundo}\n other {segundo}\n}", + "minute": "{count} {count, plural,\n one {minutu}\n other {minutu}\n}", + "hour": "{count} {count, plural,\n one {ordu}\n other {ordu}\n}" + }, + "login-form": { + "password": "Pasahitza", + "remember": "Gogoratu", + "log_in": "Saioa hasi" + }, + "card": { + "camera": { + "not_available": "Irudia ez dago eskuragarri" + }, + "scene": { + "activate": "Aktibatu" + }, + "script": { + "execute": "Exekutatu" + }, + "weather": { + "attributes": { + "air_pressure": "Aire presioa", + "humidity": "Hezetasuna", + "temperature": "Tenperatura", + "visibility": "Ikusgarritasuna", + "wind_speed": "Haizearen abiadura" + }, + "cardinal_direction": { + "e": "E", + "ene": "EIE", + "ese": "EHE", + "n": "I", + "ne": "IE", + "nne": "IIE", + "nw": "IM", + "nnw": "IIM", + "s": "H", + "se": "HE", + "sse": "HHE", + "ssw": "HHM", + "sw": "HM", + "w": "M", + "wnw": "MIM", + "wsw": "MHM" + }, + "forecast": "Iragarpena" + }, + "alarm_control_panel": { + "code": "Kodea", + "clear_code": "Garbitu" + }, + "cover": { + "position": "Posizioa" + }, + "fan": { + "speed": "Abiadura" + }, + "light": { + "brightness": "Distira", + "color_temperature": "Kolore tenperatura", + "effect": "Efektua" + }, + "media_player": { + "text_to_speak": "Esateko testua", + "source": "Iturria", + "sound_mode": "Soinu modua" + }, + "climate": { + "currently": "Orain", + "on_off": "Piztuta \/ itzalita", + "operation": "Modua", + "fan_mode": "Haizagailuaren modua", + "away_mode": "Etxetik kanpo" + }, + "lock": { + "code": "Kodea" + }, + "vacuum": { + "actions": { + "resume_cleaning": "Garbitzen jarraitu", + "return_to_base": "Basera itzuli", + "start_cleaning": "Garbitzen hasi", + "turn_on": "Piztu", + "turn_off": "Itzali" + } + }, + "water_heater": { + "currently": "Orain", + "on_off": "Piztuta \/ itzalita", + "operation": "Operazioa" + } + }, + "components": { + "service-picker": { + "service": "Zerbitzua" + }, + "relative_time": { + "past": "Orain dela {time}", + "future": "{time} barru", + "never": "Inoiz", + "duration": { + "second": "{count} {count, plural,\n one {segundo}\n other {segundo}\n}", + "minute": "{count} {count, plural,\n one {minutu}\n other {minutu}\n}", + "hour": "{count} {count, plural,\n one {ordu}\n other {ordu}\n}", + "day": "{count} {count, plural,\n one {egun}\n other {egun}\n}", + "week": "{count} {count, plural,\n one {aste}\n other {aste}\n}" + } + } + }, + "notification_toast": { + "entity_turned_on": "{entity} piztuta.", + "entity_turned_off": "{entity} itzalita.", + "service_called": "{service} zerbitzua deitu da.", + "connection_lost": "Konexioa galdu da. Berriro konektatzen..." + }, + "dialogs": { + "more_info_settings": { + "save": "Gorde", + "name": "Izena" + }, + "more_info_control": { + "script": { + "last_action": "Azken ekintza" + }, + "sun": { + "rising": "Igotzen", + "setting": "Ezarpena" + }, + "updater": { + "title": "Argibideak Eguneratu" + } + } + }, + "auth_store": { + "ask": "Saio hau gorde nahi duzu?", + "decline": "Ez, eskerrik asko", + "confirm": "Erabiltzailea gorde" + }, + "notification_drawer": { + "empty": "Jakinarazpenik ez", + "title": "Jakinarazpenak" + } + }, + "domain": { + "alarm_control_panel": "Alarmen kontrol panela", + "automation": "Automatizazioa", + "binary_sensor": "Sentsore bitarra", + "calendar": "Egutegia", + "camera": "Kamera", + "climate": "Klimatizazioa", + "configurator": "Konfiguratzailea", + "conversation": "Elkarrizketa", + "fan": "Haizagailua", + "group": "Taldea", + "input_boolean": "Sarrera boolearra", + "input_datetime": "Data sarrera", + "input_select": "Aukeraketa sarrera", + "input_number": "Zenbaki sarrera", + "input_text": "Testu sarrera", + "light": "Argia", + "lock": "Sarraila", + "mailbox": "Postontzia", + "notify": "Jakinarazi", + "plant": "Landarea", + "proximity": "Gertutasuna", + "remote": "Urrunekoa", + "scene": "Eszena", + "script": "Script", + "sensor": "Sentsorea", + "sun": "Eguzkia", + "updater": "Eguneratzailea", + "vacuum": "Xurgagailua", + "zha": "ZHA", + "hassio": "Hass.io", + "homeassistant": "Home Assistant", + "lovelace": "Lovelace", + "system_health": "Sistemaren Osasuna", + "person": "Pertsona" + }, + "attribute": { + "weather": { + "humidity": "Hezetasuna", + "visibility": "Ikusgarritasuna", + "wind_speed": "Haizearen abiadura" + } + }, + "state_attributes": { + "climate": { + "fan_mode": { + "off": "Itzalita", + "on": "Piztuta", + "auto": "Auto" + } + } + }, + "groups": { + "system-admin": "Administratzaileak", + "system-users": "Erabiltzaileak", + "system-read-only": "Soilik irakurtzeko erabiltzaileak" } } \ No newline at end of file diff --git a/translations/he.json b/translations/he.json index 57bf0942dd..43d14f146c 100644 --- a/translations/he.json +++ b/translations/he.json @@ -133,8 +133,8 @@ "climate": { "off": "כבוי", "on": "דלוק", - "heat": "חום", - "cool": "קריר", + "heat": "חימום", + "cool": "קרור", "idle": "ממתין", "auto": "אוטומטי", "dry": "יבש", diff --git a/translations/is.json b/translations/is.json new file mode 100644 index 0000000000..be26cc4508 --- /dev/null +++ b/translations/is.json @@ -0,0 +1,11 @@ +{ + "panel": { + "config": "Stillingar", + "states": "Yfirlit", + "map": "Kort", + "logbook": "Logbók", + "history": "Saga", + "mailbox": "Pósthólf", + "shopping_list": "Innkaupalisti" + } +} \ No newline at end of file diff --git a/translations/nl.json b/translations/nl.json index f08fb87d04..2ebeb3d665 100644 --- a/translations/nl.json +++ b/translations/nl.json @@ -606,7 +606,8 @@ "caption": "ZHA", "description": "Zigbee Home Automation netwerkbeheer", "services": { - "reconfigure": "Herconfigureer het ZHA-apparaat (heal device). Gebruik dit als je problemen hebt met het apparaat. Als het een apparaat met batterij is, zorg dan dat het wakker is en commando's accepteert wanneer je deze service gebruikt." + "reconfigure": "Herconfigureer het ZHA-apparaat (heal device). Gebruik dit als je problemen hebt met het apparaat. Als het een apparaat met batterij is, zorg dan dat het wakker is en commando's accepteert wanneer je deze service gebruikt.", + "updateDeviceName": "Stel een aangepaste naam in voor dit apparaat in het apparaatregister." } }, "area_registry": { @@ -617,6 +618,7 @@ "introduction": "Gebieden worden gebruikt om te bepalen waar apparaten zijn. Deze informatie wordt overal in de Home Assistant gebruikt om u te helpen bij het organiseren van uw interface, machtigingen en integraties met andere systemen.", "introduction2": "Als u apparaten in een gebied wilt plaatsen, gebruikt u de onderstaande koppeling om naar de integratiespagina te gaan en vervolgens op een geconfigureerde integratie te klikken om naar de apparaatkaarten te gaan.", "integrations_page": "Integratiespagina", + "no_areas": "Het lijkt erop dat je nog geen ruimtes hebt!", "create_area": "MAAK RUIMTE" }, "no_areas": "Het lijkt erop dat je nog geen gebieden hebt!", @@ -1161,6 +1163,8 @@ } }, "groups": { - "system-users": "Gebruikers" + "system-admin": "Beheerders", + "system-users": "Gebruikers", + "system-read-only": "Alleen-lezen gebruikers" } } \ No newline at end of file diff --git a/translations/nn.json b/translations/nn.json index 5d7f67a86f..ca41f40be5 100644 --- a/translations/nn.json +++ b/translations/nn.json @@ -616,7 +616,8 @@ "header": "Områderegister", "introduction": "Områda vert brukt til å organisere kvar einingar er. Denne informasjonen vil bli brukt av Home Assistant for å hjelpe deg å organisere brukargrensesnittet, løyver og integrasjoner med andre system.", "introduction2": "For å plassere ei eining i eit område, bruk linken under for å navigere til integrasjonssida og trykk deretter på konfigurer integrasjon for å opne einingskortet.", - "integrations_page": "Integrasjonsside" + "integrations_page": "Integrasjonsside", + "no_areas": "Ser ut til at du ikkje har noko område endå!" }, "no_areas": "Ser ut til at du ikkje har noko område endå!", "create_area": "LAG OMRÅDE", @@ -633,7 +634,7 @@ "picker": { "header": "Oppføringsregisteret", "unavailable": "(utilgjengeleg)", - "introduction": "Home Assistant har eit register over alle oppføringane den nokon gang har sett som kan unikt identifiserast. KVar av desse oppføringane har ein eigen oppføringsidentifikasjon som er reservert for akkuratt denne oppføringa.", + "introduction": "Home Assistant har eit register over alle oppføringane den nokon gang har sett som kan unikt identifiserast. Kvar av desse oppføringane har ein eigen oppføringsidentifikasjon som er reservert for akkuratt denne oppføringa.", "introduction2": "Bruk oppføringsregisteret til å skrive over namn, endre oppføringsidentifikasjonane eller fjerne oppføringar frå Home Assistant. Merk deg at å fjerne oppføringa i oppføringregisteret ikkje fjernar sjølve oppføringa. For å gjere dette, gå vidare inn på linken under og fjern oppføringa i integrasjonssida.", "integrations_page": "Integrasjonsside" }, @@ -830,7 +831,8 @@ "data": { "name": "Namn", "username": "Brukarnamn", - "password": "Passord" + "password": "Passord", + "password_confirm": "Stadfest passord" }, "create_account": "Lag konto", "error": { @@ -1156,5 +1158,8 @@ "auto": "Auto" } } + }, + "groups": { + "system-admin": "Administratorer" } } \ No newline at end of file diff --git a/translations/pl.json b/translations/pl.json index f93ce17d31..74fa7f521d 100644 --- a/translations/pl.json +++ b/translations/pl.json @@ -570,7 +570,7 @@ }, "add_user": { "caption": "Dodaj użytkownika", - "name": "Nazwa", + "name": "Imię", "username": "Nazwa użytkownika", "password": "Hasło", "create": "Utwórz" diff --git a/translations/pt.json b/translations/pt.json index a5c4b5823f..c804d15f28 100644 --- a/translations/pt.json +++ b/translations/pt.json @@ -265,6 +265,15 @@ "on": "Ligado", "paused": "Em pausa", "returning": "A regressar ao cais" + }, + "timer": { + "active": "ativo", + "idle": "Em espera", + "paused": "Em pausa" + }, + "person": { + "home": "Casa", + "not_home": "Ausente" } }, "state_badge": { @@ -287,6 +296,10 @@ "device_tracker": { "home": "Casa", "not_home": "Fora" + }, + "person": { + "home": "Casa", + "not_home": "Ausente" } }, "ui": { @@ -319,10 +332,10 @@ "section": { "core": { "header": "Configuração e controlo do servidor", - "introduction": "Alterar a configuração pode ser um processo repetitivo. Nós sabemos. Esta seção pretende tornar a sua vida um pouco mais fácil.", + "introduction": "Alterar a configuração pode ser um processo repetitivo. Nós sabemos. Esta secção pretende tornar a sua vida um pouco mais fácil.", "validation": { "heading": "Validar a configuração", - "introduction": "Valide sua configuração caso tenha alterado recentemente a mesma e quiser certificar-se de que tudo é válido.", + "introduction": "Valide a sua configuração caso tenha alterado recentemente a mesma e quiser certificar-se de que tudo é válido.", "check_config": "Verificar a configuração", "valid": "Configuração válida!", "invalid": "Configuração inválida" @@ -360,7 +373,8 @@ "introduction": "O editor de automação permite criar e editar automações. Leia [as instruções] (https:\/\/home-assistant.io\/docs\/automation\/editor\/) para se certificar de que configurou o Home Assistant corretamente.", "pick_automation": "Escolha a automação a editar", "no_automations": "Não foi possível encontrar nenhuma automação editável", - "add_automation": "Acrescentar automação" + "add_automation": "Acrescentar automação", + "learn_more": "Saber mais sobre automações" }, "editor": { "introduction": "Crie automatizações para tornar a sua casa viva", @@ -492,7 +506,8 @@ "entity": "Entidade com localização", "zone": "Zona" } - } + }, + "learn_more": "Saber mais sobre condições" }, "actions": { "header": "Ações", @@ -525,7 +540,8 @@ "event": "Evento", "service_data": "Informação de Serviço" } - } + }, + "learn_more": "Saber mais sobre ações" } } }, @@ -548,7 +564,15 @@ "change_password": "Alterar palavra-passe", "activate_user": "Ativar utilizador", "deactivate_user": "Desativar utilizador", - "delete_user": "Apagar utilizador" + "delete_user": "Apagar utilizador", + "caption": "Ver utilizador" + }, + "add_user": { + "caption": "Adicionar Utilizador", + "name": "Nome", + "username": "Nome de Utilizador", + "password": "Password", + "create": "Criar" } }, "cloud": { @@ -588,7 +612,9 @@ "caption": "Registo de áreas", "description": "Visão geral de todas as áreas da sua casa.", "picker": { - "header": "Registo de áreas" + "header": "Registo de áreas", + "integrations_page": "Página de Integrações", + "create_area": "CRIAR ÁREA" }, "no_areas": "Parece que ainda não tem áreas!", "create_area": "CRIAR ÁREA", @@ -604,7 +630,8 @@ "description": "Visão geral de todas as entidades conhecidas.", "picker": { "header": "Registo de Entidades", - "unavailable": "(indisponível)" + "unavailable": "(indisponível)", + "integrations_page": "Página de Integrações" }, "editor": { "unavailable": "Esta entidade não está atualmente disponível.", @@ -799,11 +826,13 @@ "data": { "name": "Nome", "username": "Utilizador", - "password": "Palavra-passe" + "password": "Palavra-passe", + "password_confirm": "Confirme Password" }, "create_account": "Criar conta", "error": { - "required_fields": "Preencha todos os campos obrigatórios" + "required_fields": "Preencha todos os campos obrigatórios", + "password_not_match": "Password não coincide" } } }, @@ -855,7 +884,10 @@ "raw_editor": "Editor de configuração fonte." }, "raw_editor": { - "saved": "Salvou" + "header": "Editar configuração", + "save": "Guardar", + "unsaved_changes": "Alterações não gravadas", + "saved": "Guardou" } }, "menu": { @@ -1100,7 +1132,8 @@ "hassio": "Hass.io", "homeassistant": "Home Assistant", "lovelace": "Lovelace", - "system_health": "Saúde do sistema" + "system_health": "Saúde do sistema", + "person": "Pessoa" }, "attribute": { "weather": { @@ -1108,5 +1141,18 @@ "visibility": "Visibilidade", "wind_speed": "Vel. do vento" } + }, + "state_attributes": { + "climate": { + "fan_mode": { + "off": "Off", + "on": "On", + "auto": "Auto" + } + } + }, + "groups": { + "system-admin": "Administradores", + "system-users": "Utilizadores" } } \ No newline at end of file diff --git a/translations/ru.json b/translations/ru.json index 1faa0a1cc8..1fac4cec95 100644 --- a/translations/ru.json +++ b/translations/ru.json @@ -561,11 +561,11 @@ "title": "Пользователи" }, "editor": { - "rename_user": "Изменить имя пользователя", + "rename_user": "Изменить имя", "change_password": "Изменить пароль", - "activate_user": "Активировать пользователя", - "deactivate_user": "Деактивировать пользователя", - "delete_user": "Удалить пользователя", + "activate_user": "Активировать", + "deactivate_user": "Деактивировать", + "delete_user": "Удалить", "caption": "Просмотр пользователя" }, "add_user": { diff --git a/translations/sl.json b/translations/sl.json index 138bc8388b..7d7d01fc84 100644 --- a/translations/sl.json +++ b/translations/sl.json @@ -466,7 +466,7 @@ }, "conditions": { "header": "Pogoji", - "introduction": "Pogoji so neobvezni del pravila za avtomatizacijo in jih je mogoče uporabiti za preprečitev, da bi se dejanje zgodilo ob sprožitvi. Pogoji so zelo podobni sprožilcem, vendar so zelo različni. Sprožilec bo pogledal dogodke, ki se dogajajo v sistemu, medtem ko pogoj gleda samo na to, kako sistem trenutno izgleda. Sprožilec lahko opazi, da je stikalo vklopljeno. Pogoj lahko vidi le, če je stikalo trenutno vklopljeno ali izklopljeno. \n\n [Več o pogojih.] (Https:\/\/home-assistant.io\/docs\/scripts\/conditions\/)", + "introduction": "Pogoji so neobvezni del pravila za avtomatizacijo in jih je mogoče uporabiti za preprečitev, da bi se dejanje zgodilo ob sprožitvi. Pogoji so zelo podobni sprožilcem, vendar so zelo različni. Sprožilec bo pogledal dogodke, ki se dogajajo v sistemu, medtem, ko pogoj gleda samo na to, kako sistem trenutno izgleda. Sprožilec lahko opazi, da je stikalo vklopljeno. Pogoj lahko vidi le, če je stikalo trenutno vklopljeno ali izklopljeno. \n\n [Več o pogojih.] (Https:\/\/home-assistant.io\/docs\/scripts\/conditions\/)", "add": "Dodaj pogoj", "duplicate": "Podvoji", "delete": "Izbriši", @@ -606,7 +606,8 @@ "caption": "ZHA", "description": "Upravljanje omrežja za avtomatizacijo doma Zigbee", "services": { - "reconfigure": "Ponovno konfigurirajte napravo ZHA (\"pozdravite\" napravo). To uporabite, če imate z njo težave. Če ta naprava deluje na baterije, se prepričajte, da je budna in sprejema ukaze pri uporabi te storitve." + "reconfigure": "Ponovno konfigurirajte napravo ZHA (\"pozdravite\" napravo). To uporabite, če imate z njo težave. Če ta naprava deluje na baterije, se prepričajte, da je budna in sprejema ukaze pri uporabi te storitve.", + "updateDeviceName": "Nastavite ime po meri za to napravo v registru naprav." } }, "area_registry": { @@ -614,7 +615,11 @@ "description": "Pregled vseh območij v vašem domu.", "picker": { "header": "Register območij", - "integrations_page": "Stran za integracije" + "introduction": "Področja se uporabljajo za organizacijo področja naprav. Te informacije bodo uporabljene v celotnem Home Assistant-u in vam bodo pomagale pri organizaciji vašega vmesnika, dovoljenj in integracij z drugimi sistemi.", + "introduction2": "Če želite namestiti napravo na območje, uporabite spodnjo povezavo, da se pomaknete na stran za integracije, nato pa kliknite konfigurirano integracijo, da pridete do kartic naprave.", + "integrations_page": "Stran za integracije", + "no_areas": "Izgleda, da še nimate območij!", + "create_area": "USTVARITE OBMOČJE" }, "no_areas": "Izgleda, da še nimate območij!", "create_area": "USTVARITE OBMOČJE", @@ -631,6 +636,8 @@ "picker": { "header": "Register subjekta", "unavailable": "(ni na voljo)", + "introduction": "Home Assistant vodi register vseh entitet, ki jih je kdajkoli videl in jih je mogoče enolično identificirati. Vsak od teh entitet ima dodeljen ID entitete, ki bo rezerviran samo za to entiteto.", + "introduction2": "Z registrom entitet preglasite ime, spremenite ID entitete ali odstranite vnos iz Home Assistent-a. Opomba, odstranitev vnosa registra entitet ne bo odstranila entitete. Če želite to narediti, sledite spodnji povezavi in jo odstranite s strani za integracijo.", "integrations_page": "Stran za integracije" }, "editor": { @@ -826,11 +833,13 @@ "data": { "name": "Ime", "username": "Uporabniško ime", - "password": "Geslo" + "password": "Geslo", + "password_confirm": "Potrdi Geslo" }, "create_account": "Ustvarite račun", "error": { - "required_fields": "Izpolnite vsa zahtevana polja" + "required_fields": "Izpolnite vsa zahtevana polja", + "password_not_match": "Gesli se ne ujemata" } } }, @@ -1065,7 +1074,7 @@ "dialogs": { "more_info_settings": { "save": "Shrani", - "name": "Ime", + "name": "Preglasitev imena", "entity_id": "ID subjekta" }, "more_info_control": { @@ -1152,5 +1161,10 @@ "auto": "Samodejno" } } + }, + "groups": { + "system-admin": "Skrbniki", + "system-users": "Uporabniki", + "system-read-only": "Uporabniki \"samo za branje\"" } } \ No newline at end of file diff --git a/translations/tr.json b/translations/tr.json index 48737568af..98d45921d0 100644 --- a/translations/tr.json +++ b/translations/tr.json @@ -102,11 +102,11 @@ }, "door": { "off": "[%key:state::cover::kapalı%]", - "on": "[%key:state::cover::açık%]" + "on": "Açık" }, "garage_door": { "off": "[%key:state::cover::kapalı%]", - "on": "[%key:state::cover::açık%]" + "on": "Açık" }, "heat": { "off": "Normal", @@ -114,7 +114,7 @@ }, "window": { "off": "[%key:state::cover::kapalı%]", - "on": "[%key:state::cover::açık%]" + "on": "Açık" }, "lock": { "off": "Kilit kapalı", @@ -690,7 +690,7 @@ }, "duration": { "day": "{count}{count, plural,\n one { gün }\n other { gün }\n}", - "week": "{say}{say, çoğul,\n bir { hafta }\n diğer { hafta }\n}", + "week": "{count}{count, plural,\n one { hafta }\n other { hafta }\n}", "second": "{count}{count, plural,\n one { saniye }\n other { saniye }\n}" }, "login-form": { diff --git a/webpack.config.js b/webpack.config.js index e723bd58d7..7e61e7f290 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -44,7 +44,7 @@ function createConfig(isProdBuild, latestBuild) { onboarding: "./src/entrypoints/onboarding.ts", core: "./src/entrypoints/core.ts", compatibility: "./src/entrypoints/compatibility.js", - "custom-panel": "./src/entrypoints/custom-panel.js", + "custom-panel": "./src/entrypoints/custom-panel.ts", "hass-icons": "./src/entrypoints/hass-icons.js", }; @@ -156,7 +156,7 @@ function createConfig(isProdBuild, latestBuild) { include: [ /core.ts$/, /app.js$/, - /custom-panel.js$/, + /custom-panel.ts$/, /hass-icons.js$/, /\.chunk\.js$/, ], diff --git a/yarn.lock b/yarn.lock index 8d5ab06c8a..121f2a2b7a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -728,103 +728,131 @@ log-update "^2.3.0" strip-ansi "^3.0.1" -"@material/animation@^0.40.1": - version "0.40.1" - resolved "https://registry.yarnpkg.com/@material/animation/-/animation-0.40.1.tgz#c5ff31e7d7e17324a0045e889d3530b150b9fcec" - integrity sha512-HtxFUw04EHg4S6pXfTA3Z0wKxnNDNcDhe1Np2Y2geo+lAk2Hb7m8yCL/GaL9o2I/eRYsgUXC0U7+Mk74GCz3zw== - -"@material/base@^0.40.1": - version "0.40.1" - resolved "https://registry.yarnpkg.com/@material/base/-/base-0.40.1.tgz#a0d8e19cee98dae0f96dbf0887a14b3f7acd2aac" - integrity sha512-vrbOK8hONVCYgURQ9h7nkXvMdYnZVVNmAfFFijF8fbWQdwnoPcNTdqV6RoQlhBEqHYHQqLNfdUDlznAPKLclGQ== - -"@material/button@^0.40.0": - version "0.40.1" - resolved "https://registry.yarnpkg.com/@material/button/-/button-0.40.1.tgz#b5a8657f7d3783d7fc0936f95d0187bdb0319eae" - integrity sha512-xLNjq9zySnpZAP4UynyeXnnlLXf3iIA/6ilecwgF4d2ooUmNXcRdlRa8wGYT36JHsCfsP3AeZOjoTZUcmaiejw== +"@material/animation@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@material/animation/-/animation-1.0.0.tgz#dfd8575c8b031203917dc838ac0e3c0fe0f6709b" + integrity sha512-Ed5/vggn6ZhSJ87yn3ZS1d826VJNFz73jHF2bSsgRtHDoB8KCuOwQMfdgAgDa4lKDF6CDIPCKBZPKrs2ubehdw== dependencies: - "@material/elevation" "^0.40.1" - "@material/ripple" "^0.40.1" - "@material/rtl" "^0.40.1" - "@material/shape" "^0.40.1" - "@material/theme" "^0.40.1" - "@material/typography" "^0.40.1" + tslib "^1.9.3" -"@material/elevation@^0.40.1": - version "0.40.1" - resolved "https://registry.yarnpkg.com/@material/elevation/-/elevation-0.40.1.tgz#beb17eb90bde94459c41cd826c2de13f13b10b25" - integrity sha512-VD9ii90WzI+t4df08A9hQIsYLH/N+85a2Mqo10CNVZLZYW5fDOwFH/h7553aNoAuSHKPcGCLdyav9J9oC6TSaQ== +"@material/base@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@material/base/-/base-1.0.0.tgz#e4ef0b22c54aa887af94f5988fb1c0cb3245beba" + integrity sha512-5dxFp46x5FA+Epg6YHLzN+5zRt9S2wR84UdvVAEJ1egea94m9UHUg7y9tAnNSN16aexRSywmzyLwPr+i8PGEYA== dependencies: - "@material/animation" "^0.40.1" - "@material/theme" "^0.40.1" + tslib "^1.9.3" -"@material/mwc-base@^0.3.6": - version "0.3.6" - resolved "https://registry.yarnpkg.com/@material/mwc-base/-/mwc-base-0.3.6.tgz#44382945509602adc3ba07b554e81e0f24810873" - integrity sha512-DVwsTJYCMSgIXzh7yGdn0CW11qz7/QaimPGbQ1jo4zyV/aizbetpKwFlkJj9Ut45/cCqZMlXIU2valjoO0dU8g== +"@material/button@^1.0.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@material/button/-/button-1.1.0.tgz#dbb46c953040d3a161346e1d3cd057159b9a3c34" + integrity sha512-P1oZyyC1ELRe26vdnmax+fO3BWNmftDqHDDlQbJ+gfYMDQsNQtZNJU16ZbnVHsnzEXOpFj729imbmuLfnz8Nbg== dependencies: - lit-element "^2.0.0-rc.2" - lit-html "^1.0.0-rc.2" + "@material/elevation" "^1.1.0" + "@material/feature-targeting" "^0.44.1" + "@material/ripple" "^1.1.0" + "@material/rtl" "^0.42.0" + "@material/shape" "^1.0.0" + "@material/theme" "^1.1.0" + "@material/typography" "^1.0.0" -"@material/mwc-button@^0.3.6": - version "0.3.6" - resolved "https://registry.yarnpkg.com/@material/mwc-button/-/mwc-button-0.3.6.tgz#86a05fe27ad365fd5f7d98d4840ef0def5f72096" - integrity sha512-ueIyVqSklgMFvlVJFszzlQSwDBvCQtM7FXWBpMGPiUFbzyESb1Wy3e7K0RaeHWzWolUrq9INl9Tt+15i1hnBhA== +"@material/dom@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@material/dom/-/dom-1.1.0.tgz#3bd3d1a3415b4181118fecb182d93beda56a6f8c" + integrity sha512-+HWW38ZaM2UBPu4+7QCusLDSf4tFT31rsEXHkTkxYSg/QpDivfPx6YDz4OmYtafmhPR1d1YjqB3MYysUHdodyw== dependencies: - "@material/button" "^0.40.0" - "@material/mwc-base" "^0.3.6" - "@material/mwc-icon" "^0.3.6" - "@material/mwc-ripple" "^0.3.6" + tslib "^1.9.3" -"@material/mwc-icon@^0.3.6": - version "0.3.6" - resolved "https://registry.yarnpkg.com/@material/mwc-icon/-/mwc-icon-0.3.6.tgz#0a4fe6984300f240b0a3371e037c9fd59957c40f" - integrity sha512-ssdZSY1z2i9n974iXZiy26wZzMAd4/LkPCbHtpUAOqxWLq+8r3djmPuoKhIdiI4YCarpBOpgm8HQEuHdvClkGw== +"@material/elevation@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@material/elevation/-/elevation-1.1.0.tgz#def23c360ae067b43c1632a331b9883b9f679cc5" + integrity sha512-m4eATJvDhWK1BT+yA1iHz5mhAk8cV9olC4mjVzm4PTAqhDH2yya4WzjN1HPVHE/a65ObyZ7V4qopxu9MRocm3A== dependencies: - "@material/mwc-base" "^0.3.6" + "@material/animation" "^1.0.0" + "@material/feature-targeting" "^0.44.1" + "@material/theme" "^1.1.0" -"@material/mwc-ripple@^0.3.6": - version "0.3.6" - resolved "https://registry.yarnpkg.com/@material/mwc-ripple/-/mwc-ripple-0.3.6.tgz#27d7748e6d1cf45e2ce299d0786440217bf0b3c9" - integrity sha512-fCqIoHX5M7qW2iOitEIlJF0TBl1IhzHpXb1lroXBv1JlGGYWWSd5m+v7ZSJU3Apc8s/IkPsDDCk1vSHv6VyOwA== +"@material/feature-targeting@^0.44.1": + version "0.44.1" + resolved "https://registry.yarnpkg.com/@material/feature-targeting/-/feature-targeting-0.44.1.tgz#afafc80294e5efab94bee31a187273d43d34979a" + integrity sha512-90cc7njn4aHbH9UxY8qgZth1W5JgOgcEdWdubH1t7sFkwqFxS5g3zgxSBt46TygFBVIXNZNq35Xmg80wgqO7Pg== + +"@material/mwc-base@^0.5.0": + version "0.5.0" + resolved "https://registry.yarnpkg.com/@material/mwc-base/-/mwc-base-0.5.0.tgz#21f9f237acea444b07a0fb1dfd45d30ef8f62374" + integrity sha512-G0n5LCmeXs9QR/ptHgUmV043wUVN2Uq1CWxUeGisGEPCI5e/mt9ISk5/7MubO25yF9muQeQXxXdRt/kFc7xGCg== dependencies: - "@material/mwc-base" "^0.3.6" - "@material/ripple" "^0.40.0" + "@material/base" "^1.0.0" + lit-element "^2.0.1" lit-html "^1.0.0" -"@material/ripple@^0.40.0", "@material/ripple@^0.40.1": - version "0.40.1" - resolved "https://registry.yarnpkg.com/@material/ripple/-/ripple-0.40.1.tgz#57cbc689303b48282229cb9b62556af7442e852a" - integrity sha512-sndeTS4VHa0v1UGj7MNcxMCuO9LJ1DjoL1EjE6BH3Lm3M1MnXJHdsBo2CgPbU/FI84tt6+eyHGOYPdPrEDJhCA== +"@material/mwc-button@^0.5.0": + version "0.5.0" + resolved "https://registry.yarnpkg.com/@material/mwc-button/-/mwc-button-0.5.0.tgz#3456bfd9ef7d91d96c623952cca974216bf47f3e" + integrity sha512-XFzMuEGCtiT80fw5B6TibyAq3LPTUKXlbvgGg8kRSFsWJUXLfZKzCp/J3oJRGxZeSut33HiYXoQ1FAq9cygXeA== dependencies: - "@material/animation" "^0.40.1" - "@material/base" "^0.40.1" - "@material/theme" "^0.40.1" + "@material/button" "^1.0.0" + "@material/mwc-base" "^0.5.0" + "@material/mwc-icon" "^0.5.0" + "@material/mwc-ripple" "^0.5.0" -"@material/rtl@^0.40.1": - version "0.40.1" - resolved "https://registry.yarnpkg.com/@material/rtl/-/rtl-0.40.1.tgz#5b0d973e3c6f8e2ea3656c06ada37ba2fedfa206" - integrity sha512-Pk6Iw1/KrhWZoZtkDsPMDUW0bm7Z1zeXb3MTQRCFmjf1wU5cRxgOTtuoZLcJqlcKGppLAzJL/TJV3E7KEiuL0A== +"@material/mwc-icon@^0.5.0": + version "0.5.0" + resolved "https://registry.yarnpkg.com/@material/mwc-icon/-/mwc-icon-0.5.0.tgz#432a1c5b7d817f1d04341b3c4bb8ef514cbd8cb6" + integrity sha512-vDzNc3sYPm9I5cG//TiYM5BTt3zW283zC8F5mx2rmtK3E7/UXRZmT6b94rv9em7mEUv86DedYPzCzND8oXctxA== + dependencies: + "@material/mwc-base" "^0.5.0" -"@material/shape@^0.40.1": - version "0.40.1" - resolved "https://registry.yarnpkg.com/@material/shape/-/shape-0.40.1.tgz#bd4224902896c3d45fab353d788fe6c4866483a8" - integrity sha512-o1pw5+s/jWqsKbUAkCCaEcB8XLqJ4FlZhYfSvxZ88WRw9zoWOt9iQMMP82wLWhUX1DSzpNRI8BAD7aNLK6yRlA== +"@material/mwc-ripple@^0.5.0": + version "0.5.0" + resolved "https://registry.yarnpkg.com/@material/mwc-ripple/-/mwc-ripple-0.5.0.tgz#6520971df2ff067ef52121c3cc0c4d8960d61977" + integrity sha512-vOB9ZpbBKnNsE+d4GoZIUoupk2am5kHBHqGkwSXXTt4c01SO0HRWkdNxayB2QAHgN56Xnsct0z0/pdfoZz7lKA== + dependencies: + "@material/mwc-base" "^0.5.0" + "@material/ripple" "^1.0.0" + lit-html "^1.0.0" -"@material/theme@^0.40.1": - version "0.40.1" - resolved "https://registry.yarnpkg.com/@material/theme/-/theme-0.40.1.tgz#3cc3f1bf87ee9581df03e347a1979e53ae617221" - integrity sha512-cH1CsGIDisEQ2oroZhLTypV0Ir00x3WIwFXnPo7qv3832tuIDkZY623U3rUax6KNPz4Hh1j0tNpTwgrNZwvwWA== +"@material/ripple@^1.0.0", "@material/ripple@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@material/ripple/-/ripple-1.1.0.tgz#236016fb30c8366faf143297df2c38166d84ffbc" + integrity sha512-mkfDBZAmxjpRG7V9TrfOmLxt1g/wvGHCXtYPgvH7W8ozjf53edqxLOFENEdvHbie27y9nyixzXn0gzU0HnxSeA== + dependencies: + "@material/animation" "^1.0.0" + "@material/base" "^1.0.0" + "@material/dom" "^1.1.0" + "@material/feature-targeting" "^0.44.1" + "@material/theme" "^1.1.0" + tslib "^1.9.3" -"@material/typography@^0.40.1": - version "0.40.1" - resolved "https://registry.yarnpkg.com/@material/typography/-/typography-0.40.1.tgz#68ecb767f7c54ca2f4053cccdd1c4a0198e60f9b" - integrity sha512-LkW2tAsId8zGKxGA5VIFXV/D1h4vCHQIuALRMaDpHbNGffgr2ubtJNvCh2EQkm19MTv4igVLEjn1Svh0dXcTpA== +"@material/rtl@^0.42.0": + version "0.42.0" + resolved "https://registry.yarnpkg.com/@material/rtl/-/rtl-0.42.0.tgz#1836e78186c2d8b996f6fbf97adab203535335bc" + integrity sha512-VrnrKJzhmspsN8WXHuxxBZ69yM5IwhCUqWr1t1eNfw3ZEvEj7i1g3P31HGowKThIN1dc1Wh4LE14rCISWCtv5w== -"@mdi/svg@^3.0.39": - version "3.3.92" - resolved "https://registry.yarnpkg.com/@mdi/svg/-/svg-3.3.92.tgz#40a12670c46ed7673943b219e8410f90d0996e64" - integrity sha512-281S4dn6evXoT8b2yhTooYLiX53PBr7Z+A5snuoz6HspGrL6JSvedX2o7WhO6Cjt8aOi869HKNNZ/O7nUXrSsA== +"@material/shape@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@material/shape/-/shape-1.0.0.tgz#bef17de1f282e5c71138338a34078d8402308f65" + integrity sha512-zfXEacPQZmH+ujVtaFyfAsYiF46j1QCcFzJeZVouG4pznrbA7XD6614Ywg0wbyWX5iB6hD52ld/IN+R/6oxKqA== + dependencies: + "@material/feature-targeting" "^0.44.1" + +"@material/theme@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@material/theme/-/theme-1.1.0.tgz#9c95dd804168c23c30589fcf09ecc5af5b3d1adc" + integrity sha512-YYUV9Rhbx4r/EMb/zoOYJUWjhXChNaLlH1rqt3vpNVyxRcxGqoVMGp5u1XALBCFiD9dACPKLIkKyRYa928nmPQ== + dependencies: + "@material/feature-targeting" "^0.44.1" + +"@material/typography@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@material/typography/-/typography-1.0.0.tgz#327ecfcac5ee3af8a3a102f3f125a761202f4189" + integrity sha512-Oeqbjci1cC7jTE8/n3dwnkqKe9ZeWiaE+rgMtRYtRFw1HvAw14SpGA5EEAS/Li2Hu2KZ50FYCe3HYqShfxtChA== + dependencies: + "@material/feature-targeting" "^0.44.1" + +"@mdi/svg@3.5.95": + version "3.5.95" + resolved "https://registry.yarnpkg.com/@mdi/svg/-/svg-3.5.95.tgz#9e5c3dda43957bb436b75d5e0f3f7e57d8d2efb7" + integrity sha512-OOHqPi6s6nEeOOvo9uZgdfLyTHGDiIXiAXtvIrCBbnU4mZFSHpskQUw7Yqmp/tluYzI6neLN5Dwy/vTusDcsdA== "@mrmlnc/readdir-enhanced@^2.2.1": version "2.2.1" @@ -1084,10 +1112,10 @@ dependencies: "@polymer/polymer" "^3.0.0" -"@polymer/iron-overlay-behavior@^3.0.0-pre.27": - version "3.0.1" - resolved "https://registry.yarnpkg.com/@polymer/iron-overlay-behavior/-/iron-overlay-behavior-3.0.1.tgz#04746be26c7f0ac3b4e80ef363fcb478425dd677" - integrity sha512-7XDaPVmH90G/hiyWdwv0VG+E4B2xpiWGhql8ghHg7Ik1E4gNCZ7pdeTvjnawfycjrsod0ZezhKOFavYWAaXVxA== +"@polymer/iron-overlay-behavior@^3.0.0-pre.27", "@polymer/iron-overlay-behavior@^3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@polymer/iron-overlay-behavior/-/iron-overlay-behavior-3.0.2.tgz#6a12a3f4eab4721eb6978ed950be534c9c283983" + integrity sha512-j1qmt6mJHCwpe1mKOvqK5kcCUPQr5LSrlqpgRDbUuLgUfNJ/vGTipjrkBlfbEUagm5FEQdc1VLPLSQP6WVuP9g== dependencies: "@polymer/iron-a11y-keys-behavior" "^3.0.0-pre.26" "@polymer/iron-fit-behavior" "^3.0.0-pre.26" @@ -8530,14 +8558,14 @@ listr@^0.14.2: p-map "^2.0.0" rxjs "^6.3.3" -lit-element@^2.0.0, lit-element@^2.0.0-rc.2: - version "2.0.1" - resolved "https://registry.yarnpkg.com/lit-element/-/lit-element-2.0.1.tgz#9ec5871d3b64487f432c7c071df80ef031d7091b" - integrity sha512-2bu3B2ZYUZgntvOgu3i5mRK8geo45CLUwxwJEYK54hyednoJasjiTZPB13NBg1D+hNM2JfmWTWJnh1QEUQv7zw== +lit-element@^2.0.1, lit-element@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/lit-element/-/lit-element-2.1.0.tgz#85bc3f1da0227f4b13de8a1be978229b9fa327e9" + integrity sha512-0z/KHm1xZweivfOVRr8AKR06+D3k02u15m9s4jkuRdnGe5wfmEwePzrQQBsSZNILdnfJvfo3TJOeGhBCVZaPbw== dependencies: lit-html "^1.0.0" -lit-html@^1.0.0, lit-html@^1.0.0-rc.2: +lit-html@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/lit-html/-/lit-html-1.0.0.tgz#3dc3781a8ca68a9b5c2ff2a61e263662b9b2267b" integrity sha512-oeWlpLmBW3gFl7979Wol2LKITpmKTUFNn7PnFbh6YNynF61W74l6x5WhwItAwPRSATpexaX1egNnRzlN4GOtfQ== @@ -13262,7 +13290,7 @@ tslib@1.9.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.0.tgz#e37a86fda8cbbaf23a057f473c9f4dc64e5fc2e8" integrity sha512-f/qGG2tUkrISBlQZEjEqoZ3B2+npJjIf04H1wuAv9iA8i04Icp+61KRXxFdha22670NJopsZCIjhC3SnjPRKrQ== -tslib@^1.7.1, tslib@^1.8.0, tslib@^1.8.1, tslib@^1.9.0: +tslib@^1.7.1, tslib@^1.8.0, tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: version "1.9.3" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286" integrity sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==