+
+
+
+
3
+
6
+
9
+
[[_label(localize, "clear_code")]]
+
+
+
+ Entity not available: [[_config.entity]]
+
+
+ `;
+ }
+
+ static get properties() {
+ return {
+ hass: {
+ type: Object,
+ },
+ _config: Object,
+ _stateObj: {
+ type: Object,
+ computed: "_computeStateObj(hass.states, _config.entity)",
+ },
+ _value: {
+ type: String,
+ value: "",
+ },
+ };
+ }
+
+ getCardSize() {
+ return 4;
+ }
+
+ setConfig(config) {
+ if (
+ !config ||
+ !config.entity ||
+ config.entity.split(".")[0] !== "alarm_control_panel"
+ ) {
+ throw new Error("Invalid card configuration");
+ }
+
+ const defaults = {
+ states: ["arm_away", "arm_home"],
+ };
+
+ this._config = { ...defaults, ...config };
+ this._icons = Icons;
+ }
+
+ _computeStateObj(states, entityId) {
+ return states && entityId in states ? states[entityId] : null;
+ }
+
+ _computeHeader(localize, stateObj) {
+ if (!stateObj) return "";
+ return this._config.title
+ ? this._config.title
+ : this._label(localize, stateObj.state);
+ }
+
+ _computeIcon(stateObj) {
+ return this._icons[stateObj.state] || "hass:shield-outline";
+ }
+
+ _label(localize, state) {
+ return (
+ localize(`state.alarm_control_panel.${state}`) ||
+ localize(`ui.card.alarm_control_panel.${state}`)
+ );
+ }
+
+ _stateIconLabel(state) {
+ const stateLabel = state.split("_").pop();
+ return stateLabel === "disarmed" || stateLabel === "triggered"
+ ? ""
+ : stateLabel;
+ }
+
+ _showActionToggle(state) {
+ return state === "disarmed";
+ }
+
+ _computeClassName(stateObj) {
+ if (!stateObj) return "not-found";
+ return "";
+ }
+
+ _handlePadClick(e) {
+ const val = e.target.getAttribute("value");
+ this._value = val === "clear" ? "" : this._value + val;
+ }
+
+ _handleActionClick(e) {
+ this.hass.callService("alarm_control_panel", "alarm_" + e.target.id, {
+ entity_id: this._stateObj.entity_id,
+ code: this._value,
+ });
+ this._value = "";
+ }
+}
+
+customElements.define("hui-alarm-panel-card", HuiAlarmPanelCard);
diff --git a/src/panels/lovelace/cards/hui-conditional-card.ts b/src/panels/lovelace/cards/hui-conditional-card.ts
index 508cf50df535..08524836431b 100644
--- a/src/panels/lovelace/cards/hui-conditional-card.ts
+++ b/src/panels/lovelace/cards/hui-conditional-card.ts
@@ -1,83 +1,83 @@
-import computeCardSize from "../common/compute-card-size.js";
-import createCardElement from "../common/create-card-element.js";
-import { HomeAssistant } from "../../../types.js";
-import { LovelaceCard, LovelaceConfig } from "../types.js";
-
-interface Condition {
- entity: string;
- state?: string;
- state_not?: string;
-}
-
-interface Config extends LovelaceConfig {
- card: LovelaceConfig;
- conditions: Condition[];
-}
-
-class HuiConditionalCard extends HTMLElement implements LovelaceCard {
- private _hass?: HomeAssistant;
- private _config?: Config;
- private _card?: LovelaceCard;
-
- public setConfig(config) {
- if (
- !config.card ||
- !config.conditions ||
- !Array.isArray(config.conditions) ||
- !config.conditions.every((c) => c.entity && (c.state || c.state_not))
- ) {
- throw new Error("Error in card configuration.");
- }
-
- if (this._card && this._card.parentElement) {
- this.removeChild(this._card);
- }
-
- this._config = config;
- this._card = createCardElement(config.card);
- if (this._hass) {
- this.hass = this._hass;
- }
- }
-
- set hass(hass: HomeAssistant) {
- this._hass = hass;
-
- if (!this._card) {
- return;
- }
-
- const visible =
- this._config &&
- this._config.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;
- });
-
- if (visible) {
- this._card.hass = hass;
- if (!this._card.parentElement) {
- this.appendChild(this._card);
- }
- } else if (this._card.parentElement) {
- this.removeChild(this._card);
- }
- }
-
- public getCardSize() {
- return computeCardSize(this._card);
- }
-}
-
-declare global {
- interface HTMLElementTagNameMap {
- "hui-conditional-card": HuiConditionalCard;
- }
-}
-
-customElements.define("hui-conditional-card", HuiConditionalCard);
+import computeCardSize from "../common/compute-card-size";
+import createCardElement from "../common/create-card-element";
+import { HomeAssistant } from "../../../types";
+import { LovelaceCard, LovelaceConfig } from "../types";
+
+interface Condition {
+ entity: string;
+ state?: string;
+ state_not?: string;
+}
+
+interface Config extends LovelaceConfig {
+ card: LovelaceConfig;
+ conditions: Condition[];
+}
+
+class HuiConditionalCard extends HTMLElement implements LovelaceCard {
+ private _hass?: HomeAssistant;
+ private _config?: Config;
+ private _card?: LovelaceCard;
+
+ public setConfig(config) {
+ if (
+ !config.card ||
+ !config.conditions ||
+ !Array.isArray(config.conditions) ||
+ !config.conditions.every((c) => c.entity && (c.state || c.state_not))
+ ) {
+ throw new Error("Error in card configuration.");
+ }
+
+ if (this._card && this._card.parentElement) {
+ this.removeChild(this._card);
+ }
+
+ this._config = config;
+ this._card = createCardElement(config.card);
+ if (this._hass) {
+ this.hass = this._hass;
+ }
+ }
+
+ set hass(hass: HomeAssistant) {
+ this._hass = hass;
+
+ if (!this._card) {
+ return;
+ }
+
+ const visible =
+ this._config &&
+ this._config.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;
+ });
+
+ if (visible) {
+ this._card.hass = hass;
+ if (!this._card.parentElement) {
+ this.appendChild(this._card);
+ }
+ } else if (this._card.parentElement) {
+ this.removeChild(this._card);
+ }
+ }
+
+ public getCardSize() {
+ return computeCardSize(this._card);
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ "hui-conditional-card": HuiConditionalCard;
+ }
+}
+
+customElements.define("hui-conditional-card", HuiConditionalCard);
diff --git a/src/panels/lovelace/cards/hui-entities-card.ts b/src/panels/lovelace/cards/hui-entities-card.ts
index d8288198ddfb..2a27b2923f34 100644
--- a/src/panels/lovelace/cards/hui-entities-card.ts
+++ b/src/panels/lovelace/cards/hui-entities-card.ts
@@ -1,187 +1,187 @@
-import {
- html,
- LitElement,
- PropertyDeclarations,
- PropertyValues,
-} from "@polymer/lit-element";
-import { TemplateResult } from "lit-html";
-
-import "../../../components/ha-card.js";
-import "../components/hui-entities-toggle.js";
-
-import { fireEvent } from "../../../common/dom/fire_event.js";
-import { DOMAINS_HIDE_MORE_INFO } from "../../../common/const.js";
-import { hassLocalizeLitMixin } from "../../../mixins/lit-localize-mixin";
-import { HomeAssistant } from "../../../types.js";
-import { EntityConfig, EntityRow } from "../entity-rows/types.js";
-import { LovelaceCard, LovelaceConfig } from "../types.js";
-import processConfigEntities from "../common/process-config-entities";
-import createRowElement from "../common/create-row-element.js";
-import computeDomain from "../../../common/entity/compute_domain.js";
-import applyThemesOnElement from "../../../common/dom/apply_themes_on_element.js";
-
-interface ConfigEntity extends EntityConfig {
- type?: string;
- secondary_info: "entity-id" | "last-changed";
- action_name?: string;
- service?: string;
- service_data?: object;
- url?: string;
-}
-
-interface Config extends LovelaceConfig {
- show_header_toggle?: boolean;
- title?: string;
- entities: ConfigEntity[];
- theme?: string;
-}
-
-class HuiEntitiesCard extends hassLocalizeLitMixin(LitElement)
- implements LovelaceCard {
- protected _hass?: HomeAssistant;
- protected _config?: Config;
- protected _configEntities?: ConfigEntity[];
-
- set hass(hass: HomeAssistant) {
- this._hass = hass;
- this.shadowRoot!.querySelectorAll("#states > div > *").forEach(
- (element: unknown) => {
- (element as EntityRow).hass = hass;
- }
- );
- const entitiesToggle = this.shadowRoot!.querySelector(
- "hui-entities-toggle"
- );
- if (entitiesToggle) {
- (entitiesToggle as any).hass = hass;
- }
- }
-
- static get properties(): PropertyDeclarations {
- return {
- _config: {},
- };
- }
-
- public getCardSize(): number {
- if (!this._config) {
- return 0;
- }
- // +1 for the header
- return (this._config.title ? 1 : 0) + this._config.entities.length;
- }
-
- public setConfig(config: Config): void {
- const entities = processConfigEntities(config.entities);
-
- this._config = { theme: "default", ...config };
- this._configEntities = entities;
- }
-
- protected updated(_changedProperties: PropertyValues): void {
- if (this._hass && this._config) {
- applyThemesOnElement(this, this._hass.themes, this._config.theme);
- }
- }
-
- protected render(): TemplateResult {
- if (!this._config || !this._hass) {
- return html``;
- }
- const { show_header_toggle, title } = this._config;
-
- return html`
- ${this.renderStyle()}
-
- ${
- !title && !show_header_toggle
- ? html``
- : html`
- `
- }
-
- ${this._configEntities!.map((entityConf) =>
- this.renderEntity(entityConf)
- )}
-
-
- `;
- }
-
- private renderStyle(): TemplateResult {
- return html`
-
- `;
- }
-
- private renderEntity(entityConf: ConfigEntity): TemplateResult {
- const element = createRowElement(entityConf);
- if (this._hass) {
- element.hass = this._hass;
- }
- if (
- entityConf.entity &&
- !DOMAINS_HIDE_MORE_INFO.includes(computeDomain(entityConf.entity))
- ) {
- element.classList.add("state-card-dialog");
- element.addEventListener("click", () => this._handleClick(entityConf));
- }
-
- return html`
${element}
`;
- }
-
- private _handleClick(entityConf: ConfigEntity): void {
- const entityId = entityConf.entity;
- fireEvent(this, "hass-more-info", { entityId });
- }
-}
-
-declare global {
- interface HTMLElementTagNameMap {
- "hui-entities-card": HuiEntitiesCard;
- }
-}
-
-customElements.define("hui-entities-card", HuiEntitiesCard);
+import {
+ html,
+ LitElement,
+ PropertyDeclarations,
+ PropertyValues,
+} from "@polymer/lit-element";
+import { TemplateResult } from "lit-html";
+
+import "../../../components/ha-card";
+import "../components/hui-entities-toggle";
+
+import { fireEvent } from "../../../common/dom/fire_event";
+import { DOMAINS_HIDE_MORE_INFO } from "../../../common/const";
+import { hassLocalizeLitMixin } from "../../../mixins/lit-localize-mixin";
+import { HomeAssistant } from "../../../types";
+import { EntityConfig, EntityRow } from "../entity-rows/types";
+import { LovelaceCard, LovelaceConfig } from "../types";
+import processConfigEntities from "../common/process-config-entities";
+import createRowElement from "../common/create-row-element";
+import computeDomain from "../../../common/entity/compute_domain";
+import applyThemesOnElement from "../../../common/dom/apply_themes_on_element";
+
+interface ConfigEntity extends EntityConfig {
+ type?: string;
+ secondary_info: "entity-id" | "last-changed";
+ action_name?: string;
+ service?: string;
+ service_data?: object;
+ url?: string;
+}
+
+interface Config extends LovelaceConfig {
+ show_header_toggle?: boolean;
+ title?: string;
+ entities: ConfigEntity[];
+ theme?: string;
+}
+
+class HuiEntitiesCard extends hassLocalizeLitMixin(LitElement)
+ implements LovelaceCard {
+ protected _hass?: HomeAssistant;
+ protected _config?: Config;
+ protected _configEntities?: ConfigEntity[];
+
+ set hass(hass: HomeAssistant) {
+ this._hass = hass;
+ this.shadowRoot!.querySelectorAll("#states > div > *").forEach(
+ (element: unknown) => {
+ (element as EntityRow).hass = hass;
+ }
+ );
+ const entitiesToggle = this.shadowRoot!.querySelector(
+ "hui-entities-toggle"
+ );
+ if (entitiesToggle) {
+ (entitiesToggle as any).hass = hass;
+ }
+ }
+
+ static get properties(): PropertyDeclarations {
+ return {
+ _config: {},
+ };
+ }
+
+ public getCardSize(): number {
+ if (!this._config) {
+ return 0;
+ }
+ // +1 for the header
+ return (this._config.title ? 1 : 0) + this._config.entities.length;
+ }
+
+ public setConfig(config: Config): void {
+ const entities = processConfigEntities(config.entities);
+
+ this._config = { theme: "default", ...config };
+ this._configEntities = entities;
+ }
+
+ protected updated(_changedProperties: PropertyValues): void {
+ if (this._hass && this._config) {
+ applyThemesOnElement(this, this._hass.themes, this._config.theme);
+ }
+ }
+
+ protected render(): TemplateResult {
+ if (!this._config || !this._hass) {
+ return html``;
+ }
+ const { show_header_toggle, title } = this._config;
+
+ return html`
+ ${this.renderStyle()}
+
+ ${
+ !title && !show_header_toggle
+ ? html``
+ : html`
+ `
+ }
+
+ ${this._configEntities!.map((entityConf) =>
+ this.renderEntity(entityConf)
+ )}
+
+
+ `;
+ }
+
+ private renderStyle(): TemplateResult {
+ return html`
+
+ `;
+ }
+
+ private renderEntity(entityConf: ConfigEntity): TemplateResult {
+ const element = createRowElement(entityConf);
+ if (this._hass) {
+ element.hass = this._hass;
+ }
+ if (
+ entityConf.entity &&
+ !DOMAINS_HIDE_MORE_INFO.includes(computeDomain(entityConf.entity))
+ ) {
+ element.classList.add("state-card-dialog");
+ element.addEventListener("click", () => this._handleClick(entityConf));
+ }
+
+ return html`
${element}
`;
+ }
+
+ private _handleClick(entityConf: ConfigEntity): void {
+ const entityId = entityConf.entity;
+ fireEvent(this, "hass-more-info", { entityId });
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ "hui-entities-card": HuiEntitiesCard;
+ }
+}
+
+customElements.define("hui-entities-card", HuiEntitiesCard);
diff --git a/src/panels/lovelace/cards/hui-entity-button-card.ts b/src/panels/lovelace/cards/hui-entity-button-card.ts
index 62c839453989..69e66b226fb4 100644
--- a/src/panels/lovelace/cards/hui-entity-button-card.ts
+++ b/src/panels/lovelace/cards/hui-entity-button-card.ts
@@ -6,21 +6,21 @@ import {
} from "@polymer/lit-element";
import { HassEntity } from "home-assistant-js-websocket";
import { TemplateResult } from "lit-html";
-import { styleMap } from "lit-html/directives/styleMap.js";
+import { styleMap } from "lit-html/directives/styleMap";
-import "../../../components/ha-card.js";
+import "../../../components/ha-card";
-import toggleEntity from "../common/entity/toggle-entity.js";
-import isValidEntityId from "../../../common/entity/valid_entity_id.js";
-import stateIcon from "../../../common/entity/state_icon.js";
-import computeStateDomain from "../../../common/entity/compute_state_domain.js";
-import computeStateName from "../../../common/entity/compute_state_name.js";
-import applyThemesOnElement from "../../../common/dom/apply_themes_on_element.js";
-import { HomeAssistant, LightEntity } from "../../../types.js";
+import toggleEntity from "../common/entity/toggle-entity";
+import isValidEntityId from "../../../common/entity/valid_entity_id";
+import stateIcon from "../../../common/entity/state_icon";
+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 { HomeAssistant, LightEntity } from "../../../types";
import { hassLocalizeLitMixin } from "../../../mixins/lit-localize-mixin";
-import { LovelaceCard, LovelaceConfig } from "../types.js";
+import { LovelaceCard, LovelaceConfig } from "../types";
import { longPress } from "../common/directives/long-press-directive";
-import { fireEvent } from "../../../common/dom/fire_event.js";
+import { fireEvent } from "../../../common/dom/fire_event";
interface Config extends LovelaceConfig {
entity: string;
diff --git a/src/panels/lovelace/cards/hui-entity-filter-card.js b/src/panels/lovelace/cards/hui-entity-filter-card.js
index 9924e5614241..c76299b00b5b 100644
--- a/src/panels/lovelace/cards/hui-entity-filter-card.js
+++ b/src/panels/lovelace/cards/hui-entity-filter-card.js
@@ -1,77 +1,77 @@
-import { PolymerElement } from "@polymer/polymer/polymer-element.js";
-
-import createCardElement from "../common/create-card-element.js";
-import processConfigEntities from "../common/process-config-entities.js";
-
-function getEntities(hass, filterState, entities) {
- return entities.filter((entityConf) => {
- const stateObj = hass.states[entityConf.entity];
- return stateObj && filterState.includes(stateObj.state);
- });
-}
-
-class HuiEntitiesCard extends PolymerElement {
- static get properties() {
- return {
- hass: {
- type: Object,
- observer: "_hassChanged",
- },
- };
- }
-
- getCardSize() {
- return this.lastChild ? this.lastChild.getCardSize() : 1;
- }
-
- setConfig(config) {
- if (!config.state_filter || !Array.isArray(config.state_filter)) {
- throw new Error("Incorrect filter config.");
- }
-
- this._config = config;
- this._configEntities = processConfigEntities(config.entities);
-
- if (this.lastChild) {
- this.removeChild(this.lastChild);
- this._element = null;
- }
-
- const card = "card" in config ? { ...config.card } : {};
- if (!card.type) card.type = "entities";
- card.entities = [];
-
- const element = createCardElement(card);
- element._filterRawConfig = card;
- this._updateCardConfig(element);
-
- this._element = element;
- }
-
- _hassChanged() {
- this._updateCardConfig(this._element);
- }
-
- _updateCardConfig(element) {
- if (!element || element.tagName === "HUI-ERROR-CARD" || !this.hass) return;
- const entitiesList = getEntities(
- this.hass,
- this._config.state_filter,
- this._configEntities
- );
-
- if (entitiesList.length === 0 && this._config.show_empty === false) {
- this.style.display = "none";
- return;
- }
-
- this.style.display = "block";
- element.setConfig({ ...element._filterRawConfig, entities: entitiesList });
- element.isPanel = this.isPanel;
- element.hass = this.hass;
-
- // Attach element if it has never been attached.
- if (!this.lastChild) this.appendChild(element);
- }
-}
-customElements.define("hui-entity-filter-card", HuiEntitiesCard);
+import { PolymerElement } from "@polymer/polymer/polymer-element";
+
+import createCardElement from "../common/create-card-element";
+import processConfigEntities from "../common/process-config-entities";
+
+function getEntities(hass, filterState, entities) {
+ return entities.filter((entityConf) => {
+ const stateObj = hass.states[entityConf.entity];
+ return stateObj && filterState.includes(stateObj.state);
+ });
+}
+
+class HuiEntitiesCard extends PolymerElement {
+ static get properties() {
+ return {
+ hass: {
+ type: Object,
+ observer: "_hassChanged",
+ },
+ };
+ }
+
+ getCardSize() {
+ return this.lastChild ? this.lastChild.getCardSize() : 1;
+ }
+
+ setConfig(config) {
+ if (!config.state_filter || !Array.isArray(config.state_filter)) {
+ throw new Error("Incorrect filter config.");
+ }
+
+ this._config = config;
+ this._configEntities = processConfigEntities(config.entities);
+
+ if (this.lastChild) {
+ this.removeChild(this.lastChild);
+ this._element = null;
+ }
+
+ const card = "card" in config ? { ...config.card } : {};
+ if (!card.type) card.type = "entities";
+ card.entities = [];
+
+ const element = createCardElement(card);
+ element._filterRawConfig = card;
+ this._updateCardConfig(element);
+
+ this._element = element;
+ }
+
+ _hassChanged() {
+ this._updateCardConfig(this._element);
+ }
+
+ _updateCardConfig(element) {
+ if (!element || element.tagName === "HUI-ERROR-CARD" || !this.hass) return;
+ const entitiesList = getEntities(
+ this.hass,
+ this._config.state_filter,
+ this._configEntities
+ );
+
+ if (entitiesList.length === 0 && this._config.show_empty === false) {
+ this.style.display = "none";
+ return;
+ }
+
+ this.style.display = "block";
+ element.setConfig({ ...element._filterRawConfig, entities: entitiesList });
+ element.isPanel = this.isPanel;
+ element.hass = this.hass;
+
+ // Attach element if it has never been attached.
+ if (!this.lastChild) this.appendChild(element);
+ }
+}
+customElements.define("hui-entity-filter-card", HuiEntitiesCard);
diff --git a/src/panels/lovelace/cards/hui-gauge-card.ts b/src/panels/lovelace/cards/hui-gauge-card.ts
index 4f04285ce8bf..5061ad26bace 100644
--- a/src/panels/lovelace/cards/hui-gauge-card.ts
+++ b/src/panels/lovelace/cards/hui-gauge-card.ts
@@ -1,273 +1,273 @@
-import {
- html,
- LitElement,
- PropertyDeclarations,
- PropertyValues,
-} from "@polymer/lit-element";
-import { LovelaceCard, LovelaceConfig } from "../types.js";
-import { HomeAssistant } from "../../../types.js";
-import { fireEvent } from "../../../common/dom/fire_event.js";
-import { TemplateResult } from "lit-html";
-import isValidEntityId from "../../../common/entity/valid_entity_id.js";
-
-import "../../../components/ha-card.js";
-
-interface Config extends LovelaceConfig {
- entity: string;
- title?: string;
- unit_of_measurement?: string;
- min?: number;
- max?: number;
- severity?: object;
-}
-
-const severityMap = {
- red: "var(--label-badge-red)",
- green: "var(--label-badge-green)",
- yellow: "var(--label-badge-yellow)",
- normal: "var(--label-badge-blue)",
-};
-
-class HuiGaugeCard extends LitElement implements LovelaceCard {
- public hass?: HomeAssistant;
- private _config?: Config;
-
- static get properties(): PropertyDeclarations {
- return {
- hass: {},
- _config: {},
- };
- }
-
- public getCardSize(): number {
- return 2;
- }
-
- public setConfig(config: Config): void {
- if (!config || !config.entity) {
- throw new Error("Invalid card configuration");
- }
- if (!isValidEntityId(config.entity)) {
- throw new Error("Invalid Entity");
- }
- this._config = { min: 0, max: 100, ...config };
- }
-
- protected render(): TemplateResult {
- if (!this._config || !this.hass) {
- return html``;
- }
- const stateObj = this.hass.states[this._config.entity];
- let error;
- if (!stateObj) {
- error = "Entity not available: " + this._config.entity;
- } else if (isNaN(Number(stateObj.state))) {
- error = "Entity is non-numeric: " + this._config.entity;
- }
-
- return html`
- ${this.renderStyle()}
-
- ${
- error
- ? html`${error}
`
- : html`
-
-
-
-
-
-
${stateObj.state}
- ${this._config.unit_of_measurement ||
- stateObj.attributes.unit_of_measurement ||
- ""}
-
-
${this._config.title}
-
-
-
-
- `
- }
-
- `;
- }
-
- protected shouldUpdate(changedProps: PropertyValues): boolean {
- if (changedProps.get("hass")) {
- return (
- (changedProps.get("hass") as any).states[this._config!.entity] !==
- this.hass!.states[this._config!.entity]
- );
- }
- if (changedProps.get("_config")) {
- return changedProps.get("_config") !== this._config;
- }
- return true;
- }
-
- protected updated(): void {
- if (
- !this._config ||
- !this.hass ||
- !this.shadowRoot!.getElementById("gauge")
- ) {
- return;
- }
- const stateObj = this.hass.states[this._config.entity];
- if (isNaN(Number(stateObj.state))) {
- return;
- }
-
- const turn = this._translateTurn(Number(stateObj.state), this._config);
-
- this.shadowRoot!.getElementById(
- "gauge"
- )!.style.cssText = `transform: rotate(${turn}turn); background-color: ${this._computeSeverity(
- stateObj.state,
- this._config.severity!
- )}`;
-
- (this.shadowRoot!.querySelector(
- "ha-card"
- )! as HTMLElement).style.setProperty(
- "--base-unit",
- this._computeBaseUnit()
- );
- }
-
- private renderStyle(): TemplateResult {
- return html`
-
- `;
- }
-
- private _computeSeverity(stateValue: string, sections: object): string {
- const numberValue = Number(stateValue);
-
- if (!sections) {
- return severityMap.normal;
- }
-
- const sectionsArray = Object.keys(sections);
- const sortable = sectionsArray.map((severity) => [
- severity,
- sections[severity],
- ]);
-
- for (const severity of sortable) {
- if (severityMap[severity[0]] == null || isNaN(severity[1])) {
- return severityMap.normal;
- }
- }
- sortable.sort((a, b) => a[1] - b[1]);
-
- if (numberValue >= sortable[0][1] && numberValue < sortable[1][1]) {
- return severityMap[sortable[0][0]];
- }
- if (numberValue >= sortable[1][1] && numberValue < sortable[2][1]) {
- return severityMap[sortable[1][0]];
- }
- if (numberValue >= sortable[2][1]) {
- return severityMap[sortable[2][0]];
- }
- return severityMap.normal;
- }
-
- private _translateTurn(value: number, config: Config): number {
- const maxTurnValue = Math.min(Math.max(value, config.min!), config.max!);
- return (
- (5 * (maxTurnValue - config.min!)) / (config.max! - config.min!) / 10
- );
- }
-
- private _computeBaseUnit(): string {
- return this.clientWidth < 200 ? this.clientWidth / 5 + "px" : "50px";
- }
-
- private _handleClick(): void {
- fireEvent(this, "hass-more-info", { entityId: this._config!.entity });
- }
-}
-
-declare global {
- interface HTMLElementTagNameMap {
- "hui-gauge-card": HuiGaugeCard;
- }
-}
-
-customElements.define("hui-gauge-card", HuiGaugeCard);
+import {
+ html,
+ LitElement,
+ PropertyDeclarations,
+ PropertyValues,
+} from "@polymer/lit-element";
+import { LovelaceCard, LovelaceConfig } from "../types";
+import { HomeAssistant } from "../../../types";
+import { fireEvent } from "../../../common/dom/fire_event";
+import { TemplateResult } from "lit-html";
+import isValidEntityId from "../../../common/entity/valid_entity_id";
+
+import "../../../components/ha-card";
+
+interface Config extends LovelaceConfig {
+ entity: string;
+ title?: string;
+ unit_of_measurement?: string;
+ min?: number;
+ max?: number;
+ severity?: object;
+}
+
+const severityMap = {
+ red: "var(--label-badge-red)",
+ green: "var(--label-badge-green)",
+ yellow: "var(--label-badge-yellow)",
+ normal: "var(--label-badge-blue)",
+};
+
+class HuiGaugeCard extends LitElement implements LovelaceCard {
+ public hass?: HomeAssistant;
+ private _config?: Config;
+
+ static get properties(): PropertyDeclarations {
+ return {
+ hass: {},
+ _config: {},
+ };
+ }
+
+ public getCardSize(): number {
+ return 2;
+ }
+
+ public setConfig(config: Config): void {
+ if (!config || !config.entity) {
+ throw new Error("Invalid card configuration");
+ }
+ if (!isValidEntityId(config.entity)) {
+ throw new Error("Invalid Entity");
+ }
+ this._config = { min: 0, max: 100, ...config };
+ }
+
+ protected render(): TemplateResult {
+ if (!this._config || !this.hass) {
+ return html``;
+ }
+ const stateObj = this.hass.states[this._config.entity];
+ let error;
+ if (!stateObj) {
+ error = "Entity not available: " + this._config.entity;
+ } else if (isNaN(Number(stateObj.state))) {
+ error = "Entity is non-numeric: " + this._config.entity;
+ }
+
+ return html`
+ ${this.renderStyle()}
+
+ ${
+ error
+ ? html`${error}
`
+ : html`
+
+
+
+
+
+
${stateObj.state}
+ ${this._config.unit_of_measurement ||
+ stateObj.attributes.unit_of_measurement ||
+ ""}
+
+
${this._config.title}
+
+
+
+
+ `
+ }
+
+ `;
+ }
+
+ protected shouldUpdate(changedProps: PropertyValues): boolean {
+ if (changedProps.get("hass")) {
+ return (
+ (changedProps.get("hass") as any).states[this._config!.entity] !==
+ this.hass!.states[this._config!.entity]
+ );
+ }
+ if (changedProps.get("_config")) {
+ return changedProps.get("_config") !== this._config;
+ }
+ return true;
+ }
+
+ protected updated(): void {
+ if (
+ !this._config ||
+ !this.hass ||
+ !this.shadowRoot!.getElementById("gauge")
+ ) {
+ return;
+ }
+ const stateObj = this.hass.states[this._config.entity];
+ if (isNaN(Number(stateObj.state))) {
+ return;
+ }
+
+ const turn = this._translateTurn(Number(stateObj.state), this._config);
+
+ this.shadowRoot!.getElementById(
+ "gauge"
+ )!.style.cssText = `transform: rotate(${turn}turn); background-color: ${this._computeSeverity(
+ stateObj.state,
+ this._config.severity!
+ )}`;
+
+ (this.shadowRoot!.querySelector(
+ "ha-card"
+ )! as HTMLElement).style.setProperty(
+ "--base-unit",
+ this._computeBaseUnit()
+ );
+ }
+
+ private renderStyle(): TemplateResult {
+ return html`
+
+ `;
+ }
+
+ private _computeSeverity(stateValue: string, sections: object): string {
+ const numberValue = Number(stateValue);
+
+ if (!sections) {
+ return severityMap.normal;
+ }
+
+ const sectionsArray = Object.keys(sections);
+ const sortable = sectionsArray.map((severity) => [
+ severity,
+ sections[severity],
+ ]);
+
+ for (const severity of sortable) {
+ if (severityMap[severity[0]] == null || isNaN(severity[1])) {
+ return severityMap.normal;
+ }
+ }
+ sortable.sort((a, b) => a[1] - b[1]);
+
+ if (numberValue >= sortable[0][1] && numberValue < sortable[1][1]) {
+ return severityMap[sortable[0][0]];
+ }
+ if (numberValue >= sortable[1][1] && numberValue < sortable[2][1]) {
+ return severityMap[sortable[1][0]];
+ }
+ if (numberValue >= sortable[2][1]) {
+ return severityMap[sortable[2][0]];
+ }
+ return severityMap.normal;
+ }
+
+ private _translateTurn(value: number, config: Config): number {
+ const maxTurnValue = Math.min(Math.max(value, config.min!), config.max!);
+ return (
+ (5 * (maxTurnValue - config.min!)) / (config.max! - config.min!) / 10
+ );
+ }
+
+ private _computeBaseUnit(): string {
+ return this.clientWidth < 200 ? this.clientWidth / 5 + "px" : "50px";
+ }
+
+ private _handleClick(): void {
+ fireEvent(this, "hass-more-info", { entityId: this._config!.entity });
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ "hui-gauge-card": HuiGaugeCard;
+ }
+}
+
+customElements.define("hui-gauge-card", HuiGaugeCard);
diff --git a/src/panels/lovelace/cards/hui-glance-card.ts b/src/panels/lovelace/cards/hui-glance-card.ts
index 14a1ccf55400..0bda6acae722 100644
--- a/src/panels/lovelace/cards/hui-glance-card.ts
+++ b/src/panels/lovelace/cards/hui-glance-card.ts
@@ -4,25 +4,25 @@ import {
PropertyValues,
PropertyDeclarations,
} from "@polymer/lit-element";
-import { classMap } from "lit-html/directives/classMap.js";
+import { TemplateResult } from "lit-html";
+import { classMap } from "lit-html/directives/classMap";
-import computeStateDisplay from "../../../common/entity/compute_state_display.js";
-import computeStateName from "../../../common/entity/compute_state_name.js";
+import computeStateDisplay from "../../../common/entity/compute_state_display";
+import computeStateName from "../../../common/entity/compute_state_name";
import processConfigEntities from "../common/process-config-entities";
-import applyThemesOnElement from "../../../common/dom/apply_themes_on_element.js";
+import applyThemesOnElement from "../../../common/dom/apply_themes_on_element";
-import toggleEntity from "../common/entity/toggle-entity.js";
+import toggleEntity from "../common/entity/toggle-entity";
-import "../../../components/entity/state-badge.js";
-import "../../../components/ha-card.js";
-import "../../../components/ha-icon.js";
+import "../../../components/entity/state-badge";
+import "../../../components/ha-card";
+import "../../../components/ha-icon";
-import { fireEvent } from "../../../common/dom/fire_event.js";
+import { fireEvent } from "../../../common/dom/fire_event";
import { hassLocalizeLitMixin } from "../../../mixins/lit-localize-mixin";
-import { HomeAssistant } from "../../../types.js";
-import { LovelaceCard, LovelaceConfig } from "../types.js";
+import { HomeAssistant } from "../../../types";
+import { LovelaceCard, LovelaceConfig } from "../types";
import { longPress } from "../common/directives/long-press-directive";
-import { TemplateResult } from "lit-html";
interface EntityConfig {
name: string;
diff --git a/src/panels/lovelace/cards/hui-history-graph-card.js b/src/panels/lovelace/cards/hui-history-graph-card.js
index 7a3f4dfdb8a4..66e56b326d52 100644
--- a/src/panels/lovelace/cards/hui-history-graph-card.js
+++ b/src/panels/lovelace/cards/hui-history-graph-card.js
@@ -1,86 +1,86 @@
-import { html } from "@polymer/polymer/lib/utils/html-tag.js";
-import { PolymerElement } from "@polymer/polymer/polymer-element.js";
-
-import "../../../components/ha-card.js";
-import "../../../components/state-history-charts.js";
-import "../../../data/ha-state-history-data";
-
-import processConfigEntities from "../common/process-config-entities.js";
-
-class HuiHistoryGraphCard extends PolymerElement {
- static get template() {
- return html`
-
-
-
-
-
-
- `;
- }
-
- static get properties() {
- return {
- hass: Object,
- _config: Object,
- _names: Object,
- _entities: Array,
-
- _stateHistory: Object,
- _stateHistoryLoading: Boolean,
- _cacheConfig: Object,
- };
- }
-
- getCardSize() {
- return 4;
- }
-
- setConfig(config) {
- const entities = processConfigEntities(config.entities);
-
- this._config = config;
-
- const _entities = [];
- const _names = {};
- for (const entity of entities) {
- _entities.push(entity.entity);
- if (entity.name) {
- _names[entity.entity] = entity.name;
- }
- }
-
- this.setProperties({
- _cacheConfig: {
- cacheKey: _entities.sort().join(),
- hoursToShow: config.hours_to_show || 24,
- refresh: config.refresh_interval || 0,
- },
- _entities,
- _names,
- });
- }
-}
-
-customElements.define("hui-history-graph-card", HuiHistoryGraphCard);
+import { html } from "@polymer/polymer/lib/utils/html-tag";
+import { PolymerElement } from "@polymer/polymer/polymer-element";
+
+import "../../../components/ha-card";
+import "../../../components/state-history-charts";
+import "../../../data/ha-state-history-data";
+
+import processConfigEntities from "../common/process-config-entities";
+
+class HuiHistoryGraphCard extends PolymerElement {
+ static get template() {
+ return html`
+
+
+
+
+
+
+ `;
+ }
+
+ static get properties() {
+ return {
+ hass: Object,
+ _config: Object,
+ _names: Object,
+ _entities: Array,
+
+ _stateHistory: Object,
+ _stateHistoryLoading: Boolean,
+ _cacheConfig: Object,
+ };
+ }
+
+ getCardSize() {
+ return 4;
+ }
+
+ setConfig(config) {
+ const entities = processConfigEntities(config.entities);
+
+ this._config = config;
+
+ const _entities = [];
+ const _names = {};
+ for (const entity of entities) {
+ _entities.push(entity.entity);
+ if (entity.name) {
+ _names[entity.entity] = entity.name;
+ }
+ }
+
+ this.setProperties({
+ _cacheConfig: {
+ cacheKey: _entities.sort().join(),
+ hoursToShow: config.hours_to_show || 24,
+ refresh: config.refresh_interval || 0,
+ },
+ _entities,
+ _names,
+ });
+ }
+}
+
+customElements.define("hui-history-graph-card", HuiHistoryGraphCard);
diff --git a/src/panels/lovelace/cards/hui-horizontal-stack-card.ts b/src/panels/lovelace/cards/hui-horizontal-stack-card.ts
index ba9d1dff0f6b..3559e4cdb12a 100644
--- a/src/panels/lovelace/cards/hui-horizontal-stack-card.ts
+++ b/src/panels/lovelace/cards/hui-horizontal-stack-card.ts
@@ -1,50 +1,50 @@
-import { html } from "@polymer/lit-element";
-import { TemplateResult } from "lit-html";
-
-import computeCardSize from "../common/compute-card-size.js";
-
-import { HuiStackCard } from "./hui-stack-card";
-
-class HuiHorizontalStackCard extends HuiStackCard {
- public getCardSize(): number {
- let totalSize = 0;
-
- if (this._cards) {
- for (const element of this._cards) {
- const elementSize = computeCardSize(element);
- totalSize = elementSize > totalSize ? elementSize : totalSize;
- }
- }
-
- return totalSize;
- }
-
- protected renderStyle(): TemplateResult {
- return html`
-
- `;
- }
-}
-
-declare global {
- interface HTMLElementTagNameMap {
- "hui-horitzontal-stack-card": HuiHorizontalStackCard;
- }
-}
-
-customElements.define("hui-horizontal-stack-card", HuiHorizontalStackCard);
+import { html } from "@polymer/lit-element";
+import { TemplateResult } from "lit-html";
+
+import computeCardSize from "../common/compute-card-size";
+
+import { HuiStackCard } from "./hui-stack-card";
+
+class HuiHorizontalStackCard extends HuiStackCard {
+ public getCardSize(): number {
+ let totalSize = 0;
+
+ if (this._cards) {
+ for (const element of this._cards) {
+ const elementSize = computeCardSize(element);
+ totalSize = elementSize > totalSize ? elementSize : totalSize;
+ }
+ }
+
+ return totalSize;
+ }
+
+ protected renderStyle(): TemplateResult {
+ return html`
+
+ `;
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ "hui-horitzontal-stack-card": HuiHorizontalStackCard;
+ }
+}
+
+customElements.define("hui-horizontal-stack-card", HuiHorizontalStackCard);
diff --git a/src/panels/lovelace/cards/hui-iframe-card.ts b/src/panels/lovelace/cards/hui-iframe-card.ts
index 6530a7c13d5a..11c96a22feac 100644
--- a/src/panels/lovelace/cards/hui-iframe-card.ts
+++ b/src/panels/lovelace/cards/hui-iframe-card.ts
@@ -1,80 +1,80 @@
-import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element";
-
-import "../../../components/ha-card.js";
-
-import { LovelaceCard, LovelaceConfig } from "../types.js";
-import { TemplateResult } from "lit-html";
-
-interface Config extends LovelaceConfig {
- aspect_ratio?: string;
- title?: string;
- url: string;
-}
-
-export class HuiIframeCard extends LitElement implements LovelaceCard {
- protected _config?: Config;
-
- static get properties(): PropertyDeclarations {
- return {
- _config: {},
- };
- }
-
- public getCardSize(): number {
- return 1 + this.offsetHeight / 50;
- }
-
- public setConfig(config: Config): void {
- if (!config.url) {
- throw new Error("URL required");
- }
-
- this._config = config;
- }
-
- protected render(): TemplateResult {
- if (!this._config) {
- return html``;
- }
-
- return html`
- ${this.renderStyle()}
-
-
-
-
-
- `;
- }
-
- private renderStyle(): TemplateResult {
- return html`
-
- `;
- }
-}
-
-declare global {
- interface HTMLElementTagNameMap {
- "hui-iframe-card": HuiIframeCard;
- }
-}
-
-customElements.define("hui-iframe-card", HuiIframeCard);
+import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element";
+
+import "../../../components/ha-card";
+
+import { LovelaceCard, LovelaceConfig } from "../types";
+import { TemplateResult } from "lit-html";
+
+interface Config extends LovelaceConfig {
+ aspect_ratio?: string;
+ title?: string;
+ url: string;
+}
+
+export class HuiIframeCard extends LitElement implements LovelaceCard {
+ protected _config?: Config;
+
+ static get properties(): PropertyDeclarations {
+ return {
+ _config: {},
+ };
+ }
+
+ public getCardSize(): number {
+ return 1 + this.offsetHeight / 50;
+ }
+
+ public setConfig(config: Config): void {
+ if (!config.url) {
+ throw new Error("URL required");
+ }
+
+ this._config = config;
+ }
+
+ protected render(): TemplateResult {
+ if (!this._config) {
+ return html``;
+ }
+
+ return html`
+ ${this.renderStyle()}
+
+
+
+
+
+ `;
+ }
+
+ private renderStyle(): TemplateResult {
+ return html`
+
+ `;
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ "hui-iframe-card": HuiIframeCard;
+ }
+}
+
+customElements.define("hui-iframe-card", HuiIframeCard);
diff --git a/src/panels/lovelace/cards/hui-legacy-wrapper-card.js b/src/panels/lovelace/cards/hui-legacy-wrapper-card.js
index dda181d13f2d..b9903dd6979c 100644
--- a/src/panels/lovelace/cards/hui-legacy-wrapper-card.js
+++ b/src/panels/lovelace/cards/hui-legacy-wrapper-card.js
@@ -1,57 +1,57 @@
-import createErrorCardConfig from "../common/create-error-card-config.js";
-import computeDomain from "../../../common/entity/compute_domain.js";
-
-export default class LegacyWrapperCard extends HTMLElement {
- constructor(tag, domain) {
- super();
- this._tag = tag.toUpperCase();
- this._domain = domain;
- this._element = null;
- }
-
- getCardSize() {
- return 3;
- }
-
- setConfig(config) {
- if (!config.entity) {
- throw new Error("No entity specified");
- }
-
- if (computeDomain(config.entity) !== this._domain) {
- throw new Error(
- `Specified entity needs to be of domain ${this._domain}.`
- );
- }
-
- this._config = config;
- }
-
- set hass(hass) {
- const entityId = this._config.entity;
-
- if (entityId in hass.states) {
- this._ensureElement(this._tag);
- this.lastChild.hass = hass;
- this.lastChild.stateObj = hass.states[entityId];
- } else {
- this._ensureElement("HUI-ERROR-CARD");
- this.lastChild.setConfig(
- createErrorCardConfig(
- `No state available for ${entityId}`,
- this._config
- )
- );
- }
- }
-
- _ensureElement(tag) {
- if (this.lastChild && this.lastChild.tagName === tag) return;
-
- if (this.lastChild) {
- this.removeChild(this.lastChild);
- }
-
- this.appendChild(document.createElement(tag));
- }
-}
+import createErrorCardConfig from "../common/create-error-card-config";
+import computeDomain from "../../../common/entity/compute_domain";
+
+export default class LegacyWrapperCard extends HTMLElement {
+ constructor(tag, domain) {
+ super();
+ this._tag = tag.toUpperCase();
+ this._domain = domain;
+ this._element = null;
+ }
+
+ getCardSize() {
+ return 3;
+ }
+
+ setConfig(config) {
+ if (!config.entity) {
+ throw new Error("No entity specified");
+ }
+
+ if (computeDomain(config.entity) !== this._domain) {
+ throw new Error(
+ `Specified entity needs to be of domain ${this._domain}.`
+ );
+ }
+
+ this._config = config;
+ }
+
+ set hass(hass) {
+ const entityId = this._config.entity;
+
+ if (entityId in hass.states) {
+ this._ensureElement(this._tag);
+ this.lastChild.hass = hass;
+ this.lastChild.stateObj = hass.states[entityId];
+ } else {
+ this._ensureElement("HUI-ERROR-CARD");
+ this.lastChild.setConfig(
+ createErrorCardConfig(
+ `No state available for ${entityId}`,
+ this._config
+ )
+ );
+ }
+ }
+
+ _ensureElement(tag) {
+ if (this.lastChild && this.lastChild.tagName === tag) return;
+
+ if (this.lastChild) {
+ this.removeChild(this.lastChild);
+ }
+
+ this.appendChild(document.createElement(tag));
+ }
+}
diff --git a/src/panels/lovelace/cards/hui-light-card.ts b/src/panels/lovelace/cards/hui-light-card.ts
index cacacc7efcb7..53f8dfdee0a4 100644
--- a/src/panels/lovelace/cards/hui-light-card.ts
+++ b/src/panels/lovelace/cards/hui-light-card.ts
@@ -1,327 +1,327 @@
-import {
- html,
- LitElement,
- PropertyValues,
- PropertyDeclarations,
-} from "@polymer/lit-element";
-import { fireEvent } from "../../../common/dom/fire_event.js";
-import { styleMap } from "lit-html/directives/styleMap.js";
-import computeStateName from "../../../common/entity/compute_state_name.js";
-import stateIcon from "../../../common/entity/state_icon.js";
-import { jQuery } from "../../../resources/jquery";
-
-import "../../../components/ha-card.js";
-import "../../../components/ha-icon.js";
-import { roundSliderStyle } from "../../../resources/jquery.roundslider";
-
-import { HomeAssistant, LightEntity } from "../../../types.js";
-import { hassLocalizeLitMixin } from "../../../mixins/lit-localize-mixin";
-import { LovelaceCard, LovelaceConfig } from "../types.js";
-import { longPress } from "../common/directives/long-press-directive";
-import { TemplateResult } from "lit-html";
-
-const lightConfig = {
- radius: 80,
- step: 1,
- circleShape: "pie",
- startAngle: 315,
- width: 5,
- min: 1,
- max: 100,
- sliderType: "min-range",
- lineCap: "round",
- handleSize: "+12",
- showTooltip: false,
-};
-
-interface Config extends LovelaceConfig {
- entity: string;
- name?: string;
-}
-
-export class HuiLightCard extends hassLocalizeLitMixin(LitElement)
- implements LovelaceCard {
- public hass?: HomeAssistant;
- private _config?: Config;
- private _brightnessTimout?: number;
-
- static get properties(): PropertyDeclarations {
- return {
- hass: {},
- _config: {},
- };
- }
-
- public getCardSize(): number {
- return 2;
- }
-
- public setConfig(config: Config): void {
- if (!config.entity || config.entity.split(".")[0] !== "light") {
- throw new Error("Specify an entity from within the light domain.");
- }
-
- this._config = config;
- }
-
- protected render(): TemplateResult {
- if (!this.hass || !this._config) {
- return html``;
- }
-
- const stateObj = this.hass.states[this._config!.entity] as LightEntity;
-
- return html`
- ${this.renderStyle()}
-
- ${
- !stateObj
- ? html`
- Entity not available: ${
- this._config.entity
- }
`
- : html`
-
-
- `
- }
-
-
- `;
- }
-
- protected shouldUpdate(changedProps: PropertyValues): boolean {
- if (changedProps.get("hass")) {
- return (
- (changedProps.get("hass") as any).states[this._config!.entity] !==
- this.hass!.states[this._config!.entity]
- );
- }
- return (changedProps as unknown) as boolean;
- }
-
- protected firstUpdated(): void {
- const brightness = this.hass!.states[this._config!.entity].attributes
- .brightness;
- jQuery("#light", this.shadowRoot).roundSlider({
- ...lightConfig,
- change: (value) => this._setBrightness(value),
- drag: (value) => this._dragEvent(value),
- start: () => this._showBrightness(),
- stop: () => this._hideBrightness(),
- });
- this.shadowRoot!.querySelector(".brightness")!.innerHTML =
- (Math.round((brightness / 254) * 100) || 0) + "%";
- }
-
- protected updated(): void {
- const attrs = this.hass!.states[this._config!.entity].attributes;
-
- jQuery("#light", this.shadowRoot).roundSlider({
- value: Math.round((attrs.brightness / 254) * 100) || 0,
- });
- }
-
- private renderStyle(): TemplateResult {
- return html`
- ${roundSliderStyle}
-
- `;
- }
-
- private _dragEvent(e: any): void {
- this.shadowRoot!.querySelector(".brightness")!.innerHTML = e.value + "%";
- }
-
- private _showBrightness(): void {
- clearTimeout(this._brightnessTimout);
- this.shadowRoot!.querySelector(".brightness")!.classList.add(
- "show_brightness"
- );
- }
-
- private _hideBrightness(): void {
- this._brightnessTimout = window.setTimeout(() => {
- this.shadowRoot!.querySelector(".brightness")!.classList.remove(
- "show_brightness"
- );
- }, 500);
- }
-
- private _setBrightness(e: any): void {
- this.hass!.callService("light", "turn_on", {
- entity_id: this._config!.entity,
- brightness_pct: e.value,
- });
- }
-
- private _computeBrightness(stateObj: LightEntity): string {
- if (!stateObj.attributes.brightness) {
- return "";
- }
- const brightness = stateObj.attributes.brightness;
- return `brightness(${(brightness + 245) / 5}%)`;
- }
-
- private _computeColor(stateObj: LightEntity): string {
- if (!stateObj.attributes.hs_color) {
- return "";
- }
- const [hue, sat] = stateObj.attributes.hs_color;
- if (sat <= 10) {
- return "";
- }
- return `hsl(${hue}, 100%, ${100 - sat / 2}%)`;
- }
-
- private _handleClick(hold: boolean): void {
- const entityId = this._config!.entity;
-
- if (hold) {
- fireEvent(this, "hass-more-info", {
- entityId,
- });
- return;
- }
-
- this.hass!.callService("light", "toggle", {
- entity_id: entityId,
- });
- }
-}
-
-declare global {
- interface HTMLElementTagNameMap {
- "hui-light-card": HuiLightCard;
- }
-}
-
-customElements.define("hui-light-card", HuiLightCard);
+import {
+ html,
+ LitElement,
+ PropertyValues,
+ PropertyDeclarations,
+} from "@polymer/lit-element";
+import { fireEvent } from "../../../common/dom/fire_event";
+import { styleMap } from "lit-html/directives/styleMap";
+import computeStateName from "../../../common/entity/compute_state_name";
+import stateIcon from "../../../common/entity/state_icon";
+import { jQuery } from "../../../resources/jquery";
+
+import "../../../components/ha-card";
+import "../../../components/ha-icon";
+import { roundSliderStyle } from "../../../resources/jquery.roundslider";
+
+import { HomeAssistant, LightEntity } from "../../../types";
+import { hassLocalizeLitMixin } from "../../../mixins/lit-localize-mixin";
+import { LovelaceCard, LovelaceConfig } from "../types";
+import { longPress } from "../common/directives/long-press-directive";
+import { TemplateResult } from "lit-html";
+
+const lightConfig = {
+ radius: 80,
+ step: 1,
+ circleShape: "pie",
+ startAngle: 315,
+ width: 5,
+ min: 1,
+ max: 100,
+ sliderType: "min-range",
+ lineCap: "round",
+ handleSize: "+12",
+ showTooltip: false,
+};
+
+interface Config extends LovelaceConfig {
+ entity: string;
+ name?: string;
+}
+
+export class HuiLightCard extends hassLocalizeLitMixin(LitElement)
+ implements LovelaceCard {
+ public hass?: HomeAssistant;
+ private _config?: Config;
+ private _brightnessTimout?: number;
+
+ static get properties(): PropertyDeclarations {
+ return {
+ hass: {},
+ _config: {},
+ };
+ }
+
+ public getCardSize(): number {
+ return 2;
+ }
+
+ public setConfig(config: Config): void {
+ if (!config.entity || config.entity.split(".")[0] !== "light") {
+ throw new Error("Specify an entity from within the light domain.");
+ }
+
+ this._config = config;
+ }
+
+ protected render(): TemplateResult {
+ if (!this.hass || !this._config) {
+ return html``;
+ }
+
+ const stateObj = this.hass.states[this._config!.entity] as LightEntity;
+
+ return html`
+ ${this.renderStyle()}
+
+ ${
+ !stateObj
+ ? html`
+ Entity not available: ${
+ this._config.entity
+ }
`
+ : html`
+
+
+ `
+ }
+
+
+ `;
+ }
+
+ protected shouldUpdate(changedProps: PropertyValues): boolean {
+ if (changedProps.get("hass")) {
+ return (
+ (changedProps.get("hass") as any).states[this._config!.entity] !==
+ this.hass!.states[this._config!.entity]
+ );
+ }
+ return (changedProps as unknown) as boolean;
+ }
+
+ protected firstUpdated(): void {
+ const brightness = this.hass!.states[this._config!.entity].attributes
+ .brightness;
+ jQuery("#light", this.shadowRoot).roundSlider({
+ ...lightConfig,
+ change: (value) => this._setBrightness(value),
+ drag: (value) => this._dragEvent(value),
+ start: () => this._showBrightness(),
+ stop: () => this._hideBrightness(),
+ });
+ this.shadowRoot!.querySelector(".brightness")!.innerHTML =
+ (Math.round((brightness / 254) * 100) || 0) + "%";
+ }
+
+ protected updated(): void {
+ const attrs = this.hass!.states[this._config!.entity].attributes;
+
+ jQuery("#light", this.shadowRoot).roundSlider({
+ value: Math.round((attrs.brightness / 254) * 100) || 0,
+ });
+ }
+
+ private renderStyle(): TemplateResult {
+ return html`
+ ${roundSliderStyle}
+
+ `;
+ }
+
+ private _dragEvent(e: any): void {
+ this.shadowRoot!.querySelector(".brightness")!.innerHTML = e.value + "%";
+ }
+
+ private _showBrightness(): void {
+ clearTimeout(this._brightnessTimout);
+ this.shadowRoot!.querySelector(".brightness")!.classList.add(
+ "show_brightness"
+ );
+ }
+
+ private _hideBrightness(): void {
+ this._brightnessTimout = window.setTimeout(() => {
+ this.shadowRoot!.querySelector(".brightness")!.classList.remove(
+ "show_brightness"
+ );
+ }, 500);
+ }
+
+ private _setBrightness(e: any): void {
+ this.hass!.callService("light", "turn_on", {
+ entity_id: this._config!.entity,
+ brightness_pct: e.value,
+ });
+ }
+
+ private _computeBrightness(stateObj: LightEntity): string {
+ if (!stateObj.attributes.brightness) {
+ return "";
+ }
+ const brightness = stateObj.attributes.brightness;
+ return `brightness(${(brightness + 245) / 5}%)`;
+ }
+
+ private _computeColor(stateObj: LightEntity): string {
+ if (!stateObj.attributes.hs_color) {
+ return "";
+ }
+ const [hue, sat] = stateObj.attributes.hs_color;
+ if (sat <= 10) {
+ return "";
+ }
+ return `hsl(${hue}, 100%, ${100 - sat / 2}%)`;
+ }
+
+ private _handleClick(hold: boolean): void {
+ const entityId = this._config!.entity;
+
+ if (hold) {
+ fireEvent(this, "hass-more-info", {
+ entityId,
+ });
+ return;
+ }
+
+ this.hass!.callService("light", "toggle", {
+ entity_id: entityId,
+ });
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ "hui-light-card": HuiLightCard;
+ }
+}
+
+customElements.define("hui-light-card", HuiLightCard);
diff --git a/src/panels/lovelace/cards/hui-map-card.js b/src/panels/lovelace/cards/hui-map-card.js
index 4bc17097f75b..4f213b88a529 100644
--- a/src/panels/lovelace/cards/hui-map-card.js
+++ b/src/panels/lovelace/cards/hui-map-card.js
@@ -1,305 +1,305 @@
-import { html } from "@polymer/polymer/lib/utils/html-tag.js";
-import { PolymerElement } from "@polymer/polymer/polymer-element.js";
-import "@polymer/paper-icon-button/paper-icon-button.js";
-import Leaflet from "leaflet";
-
-import "../../map/ha-entity-marker.js";
-
-import setupLeafletMap from "../../../common/dom/setup-leaflet-map.js";
-import processConfigEntities from "../common/process-config-entities.js";
-import computeStateDomain from "../../../common/entity/compute_state_domain.js";
-import computeStateName from "../../../common/entity/compute_state_name.js";
-import debounce from "../../../common/util/debounce.js";
-
-Leaflet.Icon.Default.imagePath = "/static/images/leaflet";
-
-class HuiMapCard extends PolymerElement {
- static get template() {
- return html`
-
-
-
-
-
-
- `;
- }
-
- static get properties() {
- return {
- hass: {
- type: Object,
- observer: "_drawEntities",
- },
- _config: Object,
- isPanel: {
- type: Boolean,
- reflectToAttribute: true,
- },
- };
- }
-
- constructor() {
- super();
- this._debouncedResizeListener = debounce(this._resetMap.bind(this), 100);
- }
-
- ready() {
- super.ready();
-
- if (!this._config || this.isPanel) {
- return;
- }
-
- this.$.root.style.paddingTop = this._config.aspect_ratio || "100%";
- }
-
- setConfig(config) {
- if (!config) {
- throw new Error("Error in card configuration.");
- }
-
- this._configEntities = processConfigEntities(config.entities);
- this._config = config;
- }
-
- getCardSize() {
- let ar = this._config.aspect_ratio || "100%";
- ar = ar.substr(0, ar.length - 1);
- return 1 + Math.floor(ar / 25) || 3;
- }
-
- connectedCallback() {
- super.connectedCallback();
-
- // Observe changes to map size and invalidate to prevent broken rendering
- // Uses ResizeObserver in Chrome, otherwise window resize event
- if (typeof ResizeObserver === "function") {
- this._resizeObserver = new ResizeObserver(() =>
- this._debouncedResizeListener()
- );
- this._resizeObserver.observe(this.$.map);
- } else {
- window.addEventListener("resize", this._debouncedResizeListener);
- }
-
- this._map = setupLeafletMap(this.$.map);
- this._drawEntities(this.hass);
-
- setTimeout(() => {
- this._resetMap();
- this._fitMap();
- }, 1);
- }
-
- disconnectedCallback() {
- super.disconnectedCallback();
-
- if (this._map) {
- this._map.remove();
- }
-
- if (this._resizeObserver) {
- this._resizeObserver.unobserve(this.$.map);
- } else {
- window.removeEventListener("resize", this._debouncedResizeListener);
- }
- }
-
- _resetMap() {
- if (!this._map) {
- return;
- }
- this._map.invalidateSize();
- }
-
- _fitMap() {
- const zoom = this._config.default_zoom;
- if (this._mapItems.length === 0) {
- this._map.setView(
- new Leaflet.LatLng(
- this.hass.config.latitude,
- this.hass.config.longitude
- ),
- zoom || 14
- );
- return;
- }
-
- const bounds = new Leaflet.latLngBounds(
- this._mapItems.map((item) => item.getLatLng())
- );
- this._map.fitBounds(bounds.pad(0.5));
-
- if (zoom && this._map.getZoom() > zoom) {
- this._map.setZoom(zoom);
- }
- }
-
- _drawEntities(hass) {
- const map = this._map;
- if (!map) {
- return;
- }
-
- if (this._mapItems) {
- this._mapItems.forEach((marker) => marker.remove());
- }
- const mapItems = (this._mapItems = []);
-
- this._configEntities.forEach((entity) => {
- const entityId = entity.entity;
- if (!(entityId in hass.states)) {
- return;
- }
- const stateObj = hass.states[entityId];
- const title = computeStateName(stateObj);
- const {
- latitude,
- longitude,
- passive,
- icon,
- radius,
- entity_picture: entityPicture,
- gps_accuracy: gpsAccuracy,
- } = stateObj.attributes;
-
- if (!(latitude && longitude)) {
- return;
- }
-
- let markerIcon;
- let iconHTML;
- let el;
-
- if (computeStateDomain(stateObj) === "zone") {
- // DRAW ZONE
- if (passive) return;
-
- // create icon
- if (icon) {
- el = document.createElement("ha-icon");
- el.setAttribute("icon", icon);
- iconHTML = el.outerHTML;
- } else {
- iconHTML = title;
- }
-
- markerIcon = Leaflet.divIcon({
- html: iconHTML,
- iconSize: [24, 24],
- className: "",
- });
-
- // create market with the icon
- mapItems.push(
- Leaflet.marker([latitude, longitude], {
- icon: markerIcon,
- interactive: false,
- title: title,
- }).addTo(map)
- );
-
- // create circle around it
- mapItems.push(
- Leaflet.circle([latitude, longitude], {
- interactive: false,
- color: "#FF9800",
- radius: radius,
- }).addTo(map)
- );
-
- return;
- }
-
- // DRAW ENTITY
- // create icon
- const entityName = title
- .split(" ")
- .map((part) => part[0])
- .join("")
- .substr(0, 3);
-
- el = document.createElement("ha-entity-marker");
- el.setAttribute("entity-id", entityId);
- el.setAttribute("entity-name", entityName);
- el.setAttribute("entity-picture", entityPicture || "");
-
- /* Leaflet clones this element before adding it to the map. This messes up
- our Polymer object and we can't pass data through. Thus we hack like this. */
- markerIcon = Leaflet.divIcon({
- html: el.outerHTML,
- iconSize: [48, 48],
- className: "",
- });
-
- // create market with the icon
- mapItems.push(
- Leaflet.marker([latitude, longitude], {
- icon: markerIcon,
- title: computeStateName(stateObj),
- }).addTo(map)
- );
-
- // create circle around if entity has accuracy
- if (gpsAccuracy) {
- mapItems.push(
- Leaflet.circle([latitude, longitude], {
- interactive: false,
- color: "#0288D1",
- radius: gpsAccuracy,
- }).addTo(map)
- );
- }
- });
- }
-}
-
-customElements.define("hui-map-card", HuiMapCard);
+import { html } from "@polymer/polymer/lib/utils/html-tag";
+import { PolymerElement } from "@polymer/polymer/polymer-element";
+import "@polymer/paper-icon-button/paper-icon-button";
+import Leaflet from "leaflet";
+
+import "../../map/ha-entity-marker";
+
+import setupLeafletMap from "../../../common/dom/setup-leaflet-map";
+import processConfigEntities from "../common/process-config-entities";
+import computeStateDomain from "../../../common/entity/compute_state_domain";
+import computeStateName from "../../../common/entity/compute_state_name";
+import debounce from "../../../common/util/debounce";
+
+Leaflet.Icon.Default.imagePath = "/static/images/leaflet";
+
+class HuiMapCard extends PolymerElement {
+ static get template() {
+ return html`
+
+
+
+
+
+
+ `;
+ }
+
+ static get properties() {
+ return {
+ hass: {
+ type: Object,
+ observer: "_drawEntities",
+ },
+ _config: Object,
+ isPanel: {
+ type: Boolean,
+ reflectToAttribute: true,
+ },
+ };
+ }
+
+ constructor() {
+ super();
+ this._debouncedResizeListener = debounce(this._resetMap.bind(this), 100);
+ }
+
+ ready() {
+ super.ready();
+
+ if (!this._config || this.isPanel) {
+ return;
+ }
+
+ this.$.root.style.paddingTop = this._config.aspect_ratio || "100%";
+ }
+
+ setConfig(config) {
+ if (!config) {
+ throw new Error("Error in card configuration.");
+ }
+
+ this._configEntities = processConfigEntities(config.entities);
+ this._config = config;
+ }
+
+ getCardSize() {
+ let ar = this._config.aspect_ratio || "100%";
+ ar = ar.substr(0, ar.length - 1);
+ return 1 + Math.floor(ar / 25) || 3;
+ }
+
+ connectedCallback() {
+ super.connectedCallback();
+
+ // Observe changes to map size and invalidate to prevent broken rendering
+ // Uses ResizeObserver in Chrome, otherwise window resize event
+ if (typeof ResizeObserver === "function") {
+ this._resizeObserver = new ResizeObserver(() =>
+ this._debouncedResizeListener()
+ );
+ this._resizeObserver.observe(this.$.map);
+ } else {
+ window.addEventListener("resize", this._debouncedResizeListener);
+ }
+
+ this._map = setupLeafletMap(this.$.map);
+ this._drawEntities(this.hass);
+
+ setTimeout(() => {
+ this._resetMap();
+ this._fitMap();
+ }, 1);
+ }
+
+ disconnectedCallback() {
+ super.disconnectedCallback();
+
+ if (this._map) {
+ this._map.remove();
+ }
+
+ if (this._resizeObserver) {
+ this._resizeObserver.unobserve(this.$.map);
+ } else {
+ window.removeEventListener("resize", this._debouncedResizeListener);
+ }
+ }
+
+ _resetMap() {
+ if (!this._map) {
+ return;
+ }
+ this._map.invalidateSize();
+ }
+
+ _fitMap() {
+ const zoom = this._config.default_zoom;
+ if (this._mapItems.length === 0) {
+ this._map.setView(
+ new Leaflet.LatLng(
+ this.hass.config.latitude,
+ this.hass.config.longitude
+ ),
+ zoom || 14
+ );
+ return;
+ }
+
+ const bounds = new Leaflet.latLngBounds(
+ this._mapItems.map((item) => item.getLatLng())
+ );
+ this._map.fitBounds(bounds.pad(0.5));
+
+ if (zoom && this._map.getZoom() > zoom) {
+ this._map.setZoom(zoom);
+ }
+ }
+
+ _drawEntities(hass) {
+ const map = this._map;
+ if (!map) {
+ return;
+ }
+
+ if (this._mapItems) {
+ this._mapItems.forEach((marker) => marker.remove());
+ }
+ const mapItems = (this._mapItems = []);
+
+ this._configEntities.forEach((entity) => {
+ const entityId = entity.entity;
+ if (!(entityId in hass.states)) {
+ return;
+ }
+ const stateObj = hass.states[entityId];
+ const title = computeStateName(stateObj);
+ const {
+ latitude,
+ longitude,
+ passive,
+ icon,
+ radius,
+ entity_picture: entityPicture,
+ gps_accuracy: gpsAccuracy,
+ } = stateObj.attributes;
+
+ if (!(latitude && longitude)) {
+ return;
+ }
+
+ let markerIcon;
+ let iconHTML;
+ let el;
+
+ if (computeStateDomain(stateObj) === "zone") {
+ // DRAW ZONE
+ if (passive) return;
+
+ // create icon
+ if (icon) {
+ el = document.createElement("ha-icon");
+ el.setAttribute("icon", icon);
+ iconHTML = el.outerHTML;
+ } else {
+ iconHTML = title;
+ }
+
+ markerIcon = Leaflet.divIcon({
+ html: iconHTML,
+ iconSize: [24, 24],
+ className: "",
+ });
+
+ // create market with the icon
+ mapItems.push(
+ Leaflet.marker([latitude, longitude], {
+ icon: markerIcon,
+ interactive: false,
+ title: title,
+ }).addTo(map)
+ );
+
+ // create circle around it
+ mapItems.push(
+ Leaflet.circle([latitude, longitude], {
+ interactive: false,
+ color: "#FF9800",
+ radius: radius,
+ }).addTo(map)
+ );
+
+ return;
+ }
+
+ // DRAW ENTITY
+ // create icon
+ const entityName = title
+ .split(" ")
+ .map((part) => part[0])
+ .join("")
+ .substr(0, 3);
+
+ el = document.createElement("ha-entity-marker");
+ el.setAttribute("entity-id", entityId);
+ el.setAttribute("entity-name", entityName);
+ el.setAttribute("entity-picture", entityPicture || "");
+
+ /* Leaflet clones this element before adding it to the map. This messes up
+ our Polymer object and we can't pass data through. Thus we hack like this. */
+ markerIcon = Leaflet.divIcon({
+ html: el.outerHTML,
+ iconSize: [48, 48],
+ className: "",
+ });
+
+ // create market with the icon
+ mapItems.push(
+ Leaflet.marker([latitude, longitude], {
+ icon: markerIcon,
+ title: computeStateName(stateObj),
+ }).addTo(map)
+ );
+
+ // create circle around if entity has accuracy
+ if (gpsAccuracy) {
+ mapItems.push(
+ Leaflet.circle([latitude, longitude], {
+ interactive: false,
+ color: "#0288D1",
+ radius: gpsAccuracy,
+ }).addTo(map)
+ );
+ }
+ });
+ }
+}
+
+customElements.define("hui-map-card", HuiMapCard);
diff --git a/src/panels/lovelace/cards/hui-markdown-card.ts b/src/panels/lovelace/cards/hui-markdown-card.ts
index 4f24f48daa12..6c71fd904a7b 100644
--- a/src/panels/lovelace/cards/hui-markdown-card.ts
+++ b/src/panels/lovelace/cards/hui-markdown-card.ts
@@ -1,93 +1,93 @@
-import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element";
-import { classMap } from "lit-html/directives/classMap.js";
-
-import "../../../components/ha-card.js";
-import "../../../components/ha-markdown.js";
-
-import { LovelaceCard, LovelaceConfig } from "../types.js";
-import { TemplateResult } from "lit-html";
-
-interface Config extends LovelaceConfig {
- content: string;
- title?: string;
-}
-
-export class HuiMarkdownCard extends LitElement implements LovelaceCard {
- private _config?: Config;
-
- static get properties(): PropertyDeclarations {
- return {
- _config: {},
- };
- }
-
- public getCardSize(): number {
- return this._config!.content.split("\n").length;
- }
-
- public setConfig(config: Config): void {
- if (!config.content) {
- throw new Error("Invalid Configuration: Content Required");
- }
-
- this._config = config;
- }
-
- protected render(): TemplateResult {
- if (!this._config) {
- return html``;
- }
-
- return html`
- ${this.renderStyle()}
-
-
-
- `;
- }
-
- private renderStyle(): TemplateResult {
- return html`
-
- `;
- }
-}
-
-declare global {
- interface HTMLElementTagNameMap {
- "hui-markdown-card": HuiMarkdownCard;
- }
-}
-
-customElements.define("hui-markdown-card", HuiMarkdownCard);
+import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element";
+import { classMap } from "lit-html/directives/classMap";
+
+import "../../../components/ha-card";
+import "../../../components/ha-markdown";
+
+import { LovelaceCard, LovelaceConfig } from "../types";
+import { TemplateResult } from "lit-html";
+
+interface Config extends LovelaceConfig {
+ content: string;
+ title?: string;
+}
+
+export class HuiMarkdownCard extends LitElement implements LovelaceCard {
+ private _config?: Config;
+
+ static get properties(): PropertyDeclarations {
+ return {
+ _config: {},
+ };
+ }
+
+ public getCardSize(): number {
+ return this._config!.content.split("\n").length;
+ }
+
+ public setConfig(config: Config): void {
+ if (!config.content) {
+ throw new Error("Invalid Configuration: Content Required");
+ }
+
+ this._config = config;
+ }
+
+ protected render(): TemplateResult {
+ if (!this._config) {
+ return html``;
+ }
+
+ return html`
+ ${this.renderStyle()}
+
+
+
+ `;
+ }
+
+ private renderStyle(): TemplateResult {
+ return html`
+
+ `;
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ "hui-markdown-card": HuiMarkdownCard;
+ }
+}
+
+customElements.define("hui-markdown-card", HuiMarkdownCard);
diff --git a/src/panels/lovelace/cards/hui-media-control-card.js b/src/panels/lovelace/cards/hui-media-control-card.js
index 6c8dc12e0941..96ad0e75c68e 100644
--- a/src/panels/lovelace/cards/hui-media-control-card.js
+++ b/src/panels/lovelace/cards/hui-media-control-card.js
@@ -1,11 +1,11 @@
-import "../../../cards/ha-media_player-card.js";
-
-import LegacyWrapperCard from "./hui-legacy-wrapper-card.js";
-
-class HuiMediaControlCard extends LegacyWrapperCard {
- constructor() {
- super("ha-media_player-card", "media_player");
- }
-}
-
-customElements.define("hui-media-control-card", HuiMediaControlCard);
+import "../../../cards/ha-media_player-card";
+
+import LegacyWrapperCard from "./hui-legacy-wrapper-card";
+
+class HuiMediaControlCard extends LegacyWrapperCard {
+ constructor() {
+ super("ha-media_player-card", "media_player");
+ }
+}
+
+customElements.define("hui-media-control-card", HuiMediaControlCard);
diff --git a/src/panels/lovelace/cards/hui-picture-card.js b/src/panels/lovelace/cards/hui-picture-card.js
index 6318eb3a5bf7..881f3a46dfce 100644
--- a/src/panels/lovelace/cards/hui-picture-card.js
+++ b/src/panels/lovelace/cards/hui-picture-card.js
@@ -1,67 +1,67 @@
-import { html } from "@polymer/polymer/lib/utils/html-tag.js";
-import { PolymerElement } from "@polymer/polymer/polymer-element.js";
-
-import "../../../components/ha-card.js";
-
-import NavigateMixin from "../../../mixins/navigate-mixin.js";
-
-/*
- * @appliesMixin NavigateMixin
- */
-class HuiPictureCard extends NavigateMixin(PolymerElement) {
- static get template() {
- return html`
-
-
-
-
-
- `;
- }
-
- static get properties() {
- return {
- hass: Object,
- _config: Object,
- };
- }
-
- getCardSize() {
- return 3;
- }
-
- setConfig(config) {
- if (!config || !config.image) {
- throw new Error("Error in card configuration.");
- }
-
- this._config = config;
- }
-
- _computeClickable(config) {
- return config.navigation_path || config.service;
- }
-
- _cardClicked() {
- if (this._config.navigation_path) {
- this.navigate(this._config.navigation_path);
- }
- if (this._config.service) {
- const [domain, service] = this._config.service.split(".", 2);
- this.hass.callService(domain, service, this._config.service_data);
- }
- }
-}
-
-customElements.define("hui-picture-card", HuiPictureCard);
+import { html } from "@polymer/polymer/lib/utils/html-tag";
+import { PolymerElement } from "@polymer/polymer/polymer-element";
+
+import "../../../components/ha-card";
+
+import NavigateMixin from "../../../mixins/navigate-mixin";
+
+/*
+ * @appliesMixin NavigateMixin
+ */
+class HuiPictureCard extends NavigateMixin(PolymerElement) {
+ static get template() {
+ return html`
+
+
+
+
+
+ `;
+ }
+
+ static get properties() {
+ return {
+ hass: Object,
+ _config: Object,
+ };
+ }
+
+ getCardSize() {
+ return 3;
+ }
+
+ setConfig(config) {
+ if (!config || !config.image) {
+ throw new Error("Error in card configuration.");
+ }
+
+ this._config = config;
+ }
+
+ _computeClickable(config) {
+ return config.navigation_path || config.service;
+ }
+
+ _cardClicked() {
+ if (this._config.navigation_path) {
+ this.navigate(this._config.navigation_path);
+ }
+ if (this._config.service) {
+ const [domain, service] = this._config.service.split(".", 2);
+ this.hass.callService(domain, service, this._config.service_data);
+ }
+ }
+}
+
+customElements.define("hui-picture-card", HuiPictureCard);
diff --git a/src/panels/lovelace/cards/hui-picture-elements-card.ts b/src/panels/lovelace/cards/hui-picture-elements-card.ts
index 0a3c7b9e1245..2251f0bb27a7 100644
--- a/src/panels/lovelace/cards/hui-picture-elements-card.ts
+++ b/src/panels/lovelace/cards/hui-picture-elements-card.ts
@@ -1,111 +1,111 @@
-import { html, LitElement } from "@polymer/lit-element";
-import { TemplateResult } from "lit-html";
-
-import createHuiElement from "../common/create-hui-element.js";
-
-import { LovelaceCard, LovelaceConfig } from "../types";
-import { HomeAssistant } from "../../../types.js";
-import { LovelaceElementConfig, LovelaceElement } from "../elements/types.js";
-
-interface Config extends LovelaceConfig {
- title?: string;
- image: string;
- elements: LovelaceElementConfig[];
-}
-
-class HuiPictureElementsCard extends LitElement implements LovelaceCard {
- private _config?: Config;
- private _hass?: HomeAssistant;
-
- static get properties() {
- return {
- _config: {},
- };
- }
-
- set hass(hass: HomeAssistant) {
- this._hass = hass;
- for (const el of this.shadowRoot!.querySelectorAll("#root > *")) {
- const element = el as LovelaceElement;
- element.hass = this._hass;
- }
- }
-
- public getCardSize(): number {
- return 4;
- }
-
- public setConfig(config: Config): void {
- if (!config) {
- throw new Error("Invalid Configuration");
- } else if (!config.image) {
- throw new Error("Invalid Configuration: image required");
- } else if (!Array.isArray(config.elements)) {
- throw new Error("Invalid Configuration: elements required");
- }
-
- this._config = config;
- }
-
- protected render(): TemplateResult {
- if (!this._config) {
- return html``;
- }
-
- return html`
- ${this.renderStyle()}
-
-
-
- ${this._config.elements.map((elementConfig: LovelaceElementConfig) =>
- this._createHuiElement(elementConfig)
- )}
-
-
- `;
- }
-
- private renderStyle(): TemplateResult {
- return html`
-
- `;
- }
-
- private _createHuiElement(
- elementConfig: LovelaceElementConfig
- ): LovelaceElement {
- const element = createHuiElement(elementConfig) as LovelaceElement;
- element.hass = this._hass;
- element.classList.add("element");
-
- Object.keys(elementConfig.style).forEach((prop) => {
- element.style.setProperty(prop, elementConfig.style[prop]);
- });
-
- return element;
- }
-}
-
-declare global {
- interface HTMLElementTagNameMap {
- "hui-picture-elements-card": HuiPictureElementsCard;
- }
-}
-
-customElements.define("hui-picture-elements-card", HuiPictureElementsCard);
+import { html, LitElement } from "@polymer/lit-element";
+import { TemplateResult } from "lit-html";
+
+import createHuiElement from "../common/create-hui-element";
+
+import { LovelaceCard, LovelaceConfig } from "../types";
+import { HomeAssistant } from "../../../types";
+import { LovelaceElementConfig, LovelaceElement } from "../elements/types";
+
+interface Config extends LovelaceConfig {
+ title?: string;
+ image: string;
+ elements: LovelaceElementConfig[];
+}
+
+class HuiPictureElementsCard extends LitElement implements LovelaceCard {
+ private _config?: Config;
+ private _hass?: HomeAssistant;
+
+ static get properties() {
+ return {
+ _config: {},
+ };
+ }
+
+ set hass(hass: HomeAssistant) {
+ this._hass = hass;
+ for (const el of this.shadowRoot!.querySelectorAll("#root > *")) {
+ const element = el as LovelaceElement;
+ element.hass = this._hass;
+ }
+ }
+
+ public getCardSize(): number {
+ return 4;
+ }
+
+ public setConfig(config: Config): void {
+ if (!config) {
+ throw new Error("Invalid Configuration");
+ } else if (!config.image) {
+ throw new Error("Invalid Configuration: image required");
+ } else if (!Array.isArray(config.elements)) {
+ throw new Error("Invalid Configuration: elements required");
+ }
+
+ this._config = config;
+ }
+
+ protected render(): TemplateResult {
+ if (!this._config) {
+ return html``;
+ }
+
+ return html`
+ ${this.renderStyle()}
+
+
+
+ ${this._config.elements.map((elementConfig: LovelaceElementConfig) =>
+ this._createHuiElement(elementConfig)
+ )}
+
+
+ `;
+ }
+
+ private renderStyle(): TemplateResult {
+ return html`
+
+ `;
+ }
+
+ private _createHuiElement(
+ elementConfig: LovelaceElementConfig
+ ): LovelaceElement {
+ const element = createHuiElement(elementConfig) as LovelaceElement;
+ element.hass = this._hass;
+ element.classList.add("element");
+
+ Object.keys(elementConfig.style).forEach((prop) => {
+ element.style.setProperty(prop, elementConfig.style[prop]);
+ });
+
+ return element;
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ "hui-picture-elements-card": HuiPictureElementsCard;
+ }
+}
+
+customElements.define("hui-picture-elements-card", HuiPictureElementsCard);
diff --git a/src/panels/lovelace/cards/hui-picture-entity-card.js b/src/panels/lovelace/cards/hui-picture-entity-card.js
index cc1a432b965f..77898ab8cb1d 100644
--- a/src/panels/lovelace/cards/hui-picture-entity-card.js
+++ b/src/panels/lovelace/cards/hui-picture-entity-card.js
@@ -1,201 +1,201 @@
-import { html } from "@polymer/polymer/lib/utils/html-tag.js";
-import { PolymerElement } from "@polymer/polymer/polymer-element.js";
-
-import "../../../components/ha-card.js";
-import "../components/hui-image.js";
-
-import computeDomain from "../../../common/entity/compute_domain.js";
-import computeStateDisplay from "../../../common/entity/compute_state_display.js";
-import computeStateName from "../../../common/entity/compute_state_name.js";
-import toggleEntity from "../common/entity/toggle-entity.js";
-
-import EventsMixin from "../../../mixins/events-mixin.js";
-import LocalizeMixin from "../../../mixins/localize-mixin.js";
-import { longPressBind } from "../common/directives/long-press-directive";
-
-const UNAVAILABLE = "Unavailable";
-
-/*
- * @appliesMixin LocalizeMixin
- * @appliesMixin EventsMixin
- */
-class HuiPictureEntityCard extends EventsMixin(LocalizeMixin(PolymerElement)) {
- static get template() {
- return html`
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- `;
- }
-
- static get properties() {
- return {
- hass: {
- type: Object,
- observer: "_hassChanged",
- },
- _config: Object,
- _name: String,
- _state: String,
- };
- }
-
- getCardSize() {
- return 3;
- }
-
- setConfig(config) {
- if (!config || !config.entity) {
- throw new Error("Error in card configuration.");
- }
-
- this._entityDomain = computeDomain(config.entity);
- if (
- this._entityDomain !== "camera" &&
- (!config.image && !config.state_image && !config.camera_image)
- ) {
- throw new Error("No image source configured.");
- }
-
- this._config = config;
- }
-
- ready() {
- super.ready();
- const card = this.shadowRoot.querySelector("#card");
- longPressBind(card);
- card.addEventListener("ha-click", () => this._cardClicked(false));
- card.addEventListener("ha-hold", () => this._cardClicked(true));
- }
-
- _hassChanged(hass) {
- const config = this._config;
- const entityId = config.entity;
- const stateObj = hass.states[entityId];
-
- // Nothing changed
- if (
- (!stateObj && this._oldState === UNAVAILABLE) ||
- (stateObj && stateObj.state === this._oldState)
- ) {
- return;
- }
-
- let name;
- let state;
- let stateLabel;
- let available;
-
- if (stateObj) {
- name = config.name || computeStateName(stateObj);
- state = stateObj.state;
- stateLabel = computeStateDisplay(this.localize, stateObj);
- available = true;
- } else {
- name = config.name || entityId;
- state = UNAVAILABLE;
- stateLabel = this.localize("state.default.unavailable");
- available = false;
- }
-
- this.setProperties({
- _name: name,
- _state: stateLabel,
- _oldState: state,
- });
-
- this.$.card.classList.toggle("canInteract", available);
- }
-
- _showNameAndState(config) {
- return config.show_name !== false && config.show_state !== false;
- }
-
- _showName(config) {
- return config.show_name !== false && config.show_state === false;
- }
-
- _showState(config) {
- return config.show_name === false && config.show_state !== false;
- }
-
- _cardClicked(hold) {
- const config = this._config;
- const entityId = config.entity;
-
- if (!(entityId in this.hass.states)) return;
-
- const action = hold ? config.hold_action : config.tap_action || "more-info";
-
- switch (action) {
- case "toggle":
- toggleEntity(this.hass, entityId);
- break;
- case "more-info":
- this.fire("hass-more-info", { entityId });
- break;
- default:
- }
- }
-
- _getCameraImage(config) {
- return this._entityDomain === "camera"
- ? config.entity
- : config.camera_image;
- }
-}
-
-customElements.define("hui-picture-entity-card", HuiPictureEntityCard);
+import { html } from "@polymer/polymer/lib/utils/html-tag";
+import { PolymerElement } from "@polymer/polymer/polymer-element";
+
+import "../../../components/ha-card";
+import "../components/hui-image";
+
+import computeDomain from "../../../common/entity/compute_domain";
+import computeStateDisplay from "../../../common/entity/compute_state_display";
+import computeStateName from "../../../common/entity/compute_state_name";
+import toggleEntity from "../common/entity/toggle-entity";
+
+import EventsMixin from "../../../mixins/events-mixin";
+import LocalizeMixin from "../../../mixins/localize-mixin";
+import { longPressBind } from "../common/directives/long-press-directive";
+
+const UNAVAILABLE = "Unavailable";
+
+/*
+ * @appliesMixin LocalizeMixin
+ * @appliesMixin EventsMixin
+ */
+class HuiPictureEntityCard extends EventsMixin(LocalizeMixin(PolymerElement)) {
+ static get template() {
+ return html`
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ `;
+ }
+
+ static get properties() {
+ return {
+ hass: {
+ type: Object,
+ observer: "_hassChanged",
+ },
+ _config: Object,
+ _name: String,
+ _state: String,
+ };
+ }
+
+ getCardSize() {
+ return 3;
+ }
+
+ setConfig(config) {
+ if (!config || !config.entity) {
+ throw new Error("Error in card configuration.");
+ }
+
+ this._entityDomain = computeDomain(config.entity);
+ if (
+ this._entityDomain !== "camera" &&
+ (!config.image && !config.state_image && !config.camera_image)
+ ) {
+ throw new Error("No image source configured.");
+ }
+
+ this._config = config;
+ }
+
+ ready() {
+ super.ready();
+ const card = this.shadowRoot.querySelector("#card");
+ longPressBind(card);
+ card.addEventListener("ha-click", () => this._cardClicked(false));
+ card.addEventListener("ha-hold", () => this._cardClicked(true));
+ }
+
+ _hassChanged(hass) {
+ const config = this._config;
+ const entityId = config.entity;
+ const stateObj = hass.states[entityId];
+
+ // Nothing changed
+ if (
+ (!stateObj && this._oldState === UNAVAILABLE) ||
+ (stateObj && stateObj.state === this._oldState)
+ ) {
+ return;
+ }
+
+ let name;
+ let state;
+ let stateLabel;
+ let available;
+
+ if (stateObj) {
+ name = config.name || computeStateName(stateObj);
+ state = stateObj.state;
+ stateLabel = computeStateDisplay(this.localize, stateObj);
+ available = true;
+ } else {
+ name = config.name || entityId;
+ state = UNAVAILABLE;
+ stateLabel = this.localize("state.default.unavailable");
+ available = false;
+ }
+
+ this.setProperties({
+ _name: name,
+ _state: stateLabel,
+ _oldState: state,
+ });
+
+ this.$.card.classList.toggle("canInteract", available);
+ }
+
+ _showNameAndState(config) {
+ return config.show_name !== false && config.show_state !== false;
+ }
+
+ _showName(config) {
+ return config.show_name !== false && config.show_state === false;
+ }
+
+ _showState(config) {
+ return config.show_name === false && config.show_state !== false;
+ }
+
+ _cardClicked(hold) {
+ const config = this._config;
+ const entityId = config.entity;
+
+ if (!(entityId in this.hass.states)) return;
+
+ const action = hold ? config.hold_action : config.tap_action || "more-info";
+
+ switch (action) {
+ case "toggle":
+ toggleEntity(this.hass, entityId);
+ break;
+ case "more-info":
+ this.fire("hass-more-info", { entityId });
+ break;
+ default:
+ }
+ }
+
+ _getCameraImage(config) {
+ return this._entityDomain === "camera"
+ ? config.entity
+ : config.camera_image;
+ }
+}
+
+customElements.define("hui-picture-entity-card", HuiPictureEntityCard);
diff --git a/src/panels/lovelace/cards/hui-picture-glance-card.js b/src/panels/lovelace/cards/hui-picture-glance-card.js
index 727d44b4e31f..0b7783a3c806 100644
--- a/src/panels/lovelace/cards/hui-picture-glance-card.js
+++ b/src/panels/lovelace/cards/hui-picture-glance-card.js
@@ -1,195 +1,195 @@
-import { html } from "@polymer/polymer/lib/utils/html-tag.js";
-import { PolymerElement } from "@polymer/polymer/polymer-element.js";
-
-import "../../../components/ha-card.js";
-import "../../../components/ha-icon.js";
-import "../components/hui-image.js";
-
-import computeStateDisplay from "../../../common/entity/compute_state_display.js";
-import computeStateName from "../../../common/entity/compute_state_name.js";
-import { DOMAINS_TOGGLE } from "../../../common/const.js";
-import stateIcon from "../../../common/entity/state_icon.js";
-import toggleEntity from "../common/entity/toggle-entity.js";
-import processConfigEntities from "../common/process-config-entities";
-
-import EventsMixin from "../../../mixins/events-mixin.js";
-import LocalizeMixin from "../../../mixins/localize-mixin.js";
-import NavigateMixin from "../../../mixins/navigate-mixin.js";
-import computeDomain from "../../../common/entity/compute_domain";
-
-const STATES_OFF = new Set(["closed", "locked", "not_home", "off"]);
-
-/*
- * @appliesMixin EventsMixin
- * @appliesMixin LocalizeMixin
- * @appliesMixin NavigateMixin
- */
-class HuiPictureGlanceCard extends NavigateMixin(
- LocalizeMixin(EventsMixin(PolymerElement))
-) {
- static get template() {
- return html`
-
-
-
-
-
-
- [[_config.title]]
-
-
-
-
-
-
-
-
-
-
-
-
-
- `;
- }
-
- static get properties() {
- return {
- hass: Object,
- _config: Object,
- _entitiesDialog: Array,
- _entitiesToggle: Array,
- };
- }
-
- getCardSize() {
- return 3;
- }
-
- setConfig(config) {
- if (
- !config ||
- !config.entities ||
- !Array.isArray(config.entities) ||
- !(config.image || config.camera_image || config.state_image) ||
- (config.state_image && !config.entity)
- ) {
- throw new Error("Invalid card configuration");
- }
- const entities = processConfigEntities(config.entities);
- const dialog = [];
- const toggle = [];
-
- entities.forEach((item) => {
- if (
- config.force_dialog ||
- !DOMAINS_TOGGLE.has(computeDomain(item.entity))
- ) {
- dialog.push(item);
- } else {
- toggle.push(item);
- }
- });
- this.setProperties({
- _config: config,
- _entitiesDialog: dialog,
- _entitiesToggle: toggle,
- });
- }
-
- _computeVisible(collection, states) {
- return collection.filter((el) => el.entity in states);
- }
-
- _computeIcon(item, states) {
- return item.icon || stateIcon(states[item.entity]);
- }
-
- _computeButtonClass(entityId, states) {
- return STATES_OFF.has(states[entityId].state) ? "" : "state-on";
- }
-
- _computeTooltip(entityId, states) {
- return `${computeStateName(states[entityId])}: ${computeStateDisplay(
- this.localize,
- states[entityId]
- )}`;
- }
-
- _computeImageClass(config) {
- return config.navigation_path || config.camera_image ? "clickable" : "";
- }
-
- _openDialog(ev) {
- this.fire("hass-more-info", { entityId: ev.model.item.entity });
- }
-
- _callService(ev) {
- toggleEntity(this.hass, ev.model.item.entity);
- }
-
- _handleImageClick() {
- if (this._config.navigation_path) {
- this.navigate(this._config.navigation_path);
- return;
- }
-
- if (this._config.camera_image) {
- this.fire("hass-more-info", { entityId: this._config.camera_image });
- }
- }
-}
-customElements.define("hui-picture-glance-card", HuiPictureGlanceCard);
+import { html } from "@polymer/polymer/lib/utils/html-tag";
+import { PolymerElement } from "@polymer/polymer/polymer-element";
+
+import "../../../components/ha-card";
+import "../../../components/ha-icon";
+import "../components/hui-image";
+
+import computeStateDisplay from "../../../common/entity/compute_state_display";
+import computeStateName from "../../../common/entity/compute_state_name";
+import { DOMAINS_TOGGLE } from "../../../common/const";
+import stateIcon from "../../../common/entity/state_icon";
+import toggleEntity from "../common/entity/toggle-entity";
+import processConfigEntities from "../common/process-config-entities";
+
+import EventsMixin from "../../../mixins/events-mixin";
+import LocalizeMixin from "../../../mixins/localize-mixin";
+import NavigateMixin from "../../../mixins/navigate-mixin";
+import computeDomain from "../../../common/entity/compute_domain";
+
+const STATES_OFF = new Set(["closed", "locked", "not_home", "off"]);
+
+/*
+ * @appliesMixin EventsMixin
+ * @appliesMixin LocalizeMixin
+ * @appliesMixin NavigateMixin
+ */
+class HuiPictureGlanceCard extends NavigateMixin(
+ LocalizeMixin(EventsMixin(PolymerElement))
+) {
+ static get template() {
+ return html`
+
+
+
+
+
+
+ [[_config.title]]
+
+
+
+
+
+
+
+
+
+
+
+
+
+ `;
+ }
+
+ static get properties() {
+ return {
+ hass: Object,
+ _config: Object,
+ _entitiesDialog: Array,
+ _entitiesToggle: Array,
+ };
+ }
+
+ getCardSize() {
+ return 3;
+ }
+
+ setConfig(config) {
+ if (
+ !config ||
+ !config.entities ||
+ !Array.isArray(config.entities) ||
+ !(config.image || config.camera_image || config.state_image) ||
+ (config.state_image && !config.entity)
+ ) {
+ throw new Error("Invalid card configuration");
+ }
+ const entities = processConfigEntities(config.entities);
+ const dialog = [];
+ const toggle = [];
+
+ entities.forEach((item) => {
+ if (
+ config.force_dialog ||
+ !DOMAINS_TOGGLE.has(computeDomain(item.entity))
+ ) {
+ dialog.push(item);
+ } else {
+ toggle.push(item);
+ }
+ });
+ this.setProperties({
+ _config: config,
+ _entitiesDialog: dialog,
+ _entitiesToggle: toggle,
+ });
+ }
+
+ _computeVisible(collection, states) {
+ return collection.filter((el) => el.entity in states);
+ }
+
+ _computeIcon(item, states) {
+ return item.icon || stateIcon(states[item.entity]);
+ }
+
+ _computeButtonClass(entityId, states) {
+ return STATES_OFF.has(states[entityId].state) ? "" : "state-on";
+ }
+
+ _computeTooltip(entityId, states) {
+ return `${computeStateName(states[entityId])}: ${computeStateDisplay(
+ this.localize,
+ states[entityId]
+ )}`;
+ }
+
+ _computeImageClass(config) {
+ return config.navigation_path || config.camera_image ? "clickable" : "";
+ }
+
+ _openDialog(ev) {
+ this.fire("hass-more-info", { entityId: ev.model.item.entity });
+ }
+
+ _callService(ev) {
+ toggleEntity(this.hass, ev.model.item.entity);
+ }
+
+ _handleImageClick() {
+ if (this._config.navigation_path) {
+ this.navigate(this._config.navigation_path);
+ return;
+ }
+
+ if (this._config.camera_image) {
+ this.fire("hass-more-info", { entityId: this._config.camera_image });
+ }
+ }
+}
+customElements.define("hui-picture-glance-card", HuiPictureGlanceCard);
diff --git a/src/panels/lovelace/cards/hui-plant-status-card.js b/src/panels/lovelace/cards/hui-plant-status-card.js
index 1b82cf190d06..500bc61df902 100644
--- a/src/panels/lovelace/cards/hui-plant-status-card.js
+++ b/src/panels/lovelace/cards/hui-plant-status-card.js
@@ -1,11 +1,11 @@
-import "../../../cards/ha-plant-card.js";
-
-import LegacyWrapperCard from "./hui-legacy-wrapper-card.js";
-
-class HuiPlantStatusCard extends LegacyWrapperCard {
- constructor() {
- super("ha-plant-card", "plant");
- }
-}
-
-customElements.define("hui-plant-status-card", HuiPlantStatusCard);
+import "../../../cards/ha-plant-card";
+
+import LegacyWrapperCard from "./hui-legacy-wrapper-card";
+
+class HuiPlantStatusCard extends LegacyWrapperCard {
+ constructor() {
+ super("ha-plant-card", "plant");
+ }
+}
+
+customElements.define("hui-plant-status-card", HuiPlantStatusCard);
diff --git a/src/panels/lovelace/cards/hui-sensor-card.js b/src/panels/lovelace/cards/hui-sensor-card.js
index ec11bb7ac09e..ea25043f30b0 100644
--- a/src/panels/lovelace/cards/hui-sensor-card.js
+++ b/src/panels/lovelace/cards/hui-sensor-card.js
@@ -1,292 +1,292 @@
-import { LitElement, html, svg } from "@polymer/lit-element";
-
-import "../../../components/ha-card.js";
-import "../../../components/ha-icon.js";
-
-import computeStateName from "../../../common/entity/compute_state_name.js";
-import stateIcon from "../../../common/entity/state_icon.js";
-
-import EventsMixin from "../../../mixins/events-mixin.js";
-
-class HuiSensorCard extends EventsMixin(LitElement) {
- set hass(hass) {
- this._hass = hass;
- const entity = hass.states[this._config.entity];
- if (entity && this._entity !== entity) {
- this._entity = entity;
- if (
- this._config.graph !== "none" &&
- entity.attributes.unit_of_measurement
- ) {
- this._getHistory();
- }
- }
- }
-
- static get properties() {
- return {
- _hass: {},
- _config: {},
- _entity: {},
- _line: String,
- };
- }
-
- setConfig(config) {
- if (!config.entity || config.entity.split(".")[0] !== "sensor") {
- throw new Error("Specify an entity from within the sensor domain.");
- }
-
- const cardConfig = {
- icon: false,
- hours_to_show: 24,
- accuracy: 10,
- height: 100,
- line_width: 5,
- line_color: "var(--accent-color)",
- ...config,
- };
- cardConfig.hours_to_show = Number(cardConfig.hours_to_show);
- cardConfig.accuracy = Number(cardConfig.accuracy);
- cardConfig.height = Number(cardConfig.height);
- cardConfig.line_width = Number(cardConfig.line_width);
-
- this._config = cardConfig;
- }
-
- shouldUpdate(changedProps) {
- const change = changedProps.has("_entity") || changedProps.has("_line");
- return change;
- }
-
- render({ _config, _entity, _line } = this) {
- return html`
- ${this._style()}
-
-
-
- ${_entity.state}
- ${this._computeUom(_entity)}
-
-
-
- ${
- _line
- ? svg`
-
`
- : ""
- }
-
-
- `;
- }
-
- _handleClick() {
- this.fire("hass-more-info", { entityId: this._config.entity });
- }
-
- _computeIcon(item) {
- return this._config.icon || stateIcon(item);
- }
-
- _computeName(item) {
- return this._config.name || computeStateName(item);
- }
-
- _computeUom(item) {
- return this._config.unit || item.attributes.unit_of_measurement;
- }
-
- _getGraph(items, width, height) {
- const values = this._getValueArr(items);
- const coords = this._calcCoordinates(values, width, height);
- return this._getPath(coords);
- }
-
- _getValueArr(items) {
- return items.map((item) => Number(item.state) || 0);
- }
-
- _calcCoordinates(values, width, height) {
- const margin = this._config.line_width;
- width -= margin * 2;
- height -= margin * 2;
- const min = Math.floor(Math.min.apply(null, values) * 0.95);
- const max = Math.ceil(Math.max.apply(null, values) * 1.05);
-
- if (values.length === 1) values.push(values[0]);
-
- const yRatio = (max - min) / height;
- const xRatio = width / (values.length - 1);
-
- return values.map((value, i) => {
- const y = height - (value - min) / yRatio || 0;
- const x = xRatio * i + margin;
- return [x, y];
- });
- }
-
- _getPath(points) {
- const SPACE = " ";
- let next;
- let Z;
- const X = 0;
- const Y = 1;
- let path = "";
- let point = points[0];
-
- path += "M" + point[X] + "," + point[Y];
- const first = point;
-
- for (let i = 0; i < points.length; i++) {
- next = points[i];
- Z = this._midPoint(point[X], point[Y], next[X], next[Y]);
- path += SPACE + Z[X] + "," + Z[Y];
- path += "Q" + Math.floor(next[X]) + "," + next[Y];
- point = next;
- }
-
- const second = points[1];
- Z = this._midPoint(first[X], first[Y], second[X], second[Y]);
- path += SPACE + Math.floor(next[X]) + "." + points[points.length - 1];
- return path;
- }
-
- _midPoint(Ax, Ay, Bx, By) {
- const Zx = (Ax - Bx) / 2 + Bx;
- const Zy = (Ay - By) / 2 + By;
- return [Zx, Zy];
- }
-
- async _getHistory() {
- const endTime = new Date();
- const startTime = new Date();
- startTime.setHours(endTime.getHours() - this._config.hours_to_show);
- const stateHistory = await this._fetchRecent(
- this._config.entity,
- startTime,
- endTime
- );
- const history = stateHistory[0];
- const valArray = [history[history.length - 1]];
-
- let pos = history.length - 1;
- const accuracy = this._config.accuracy <= pos ? this._config.accuracy : pos;
- let increment = Math.ceil(history.length / accuracy);
- increment = increment <= 0 ? 1 : increment;
- for (let i = accuracy; i >= 2; i--) {
- pos -= increment;
- valArray.unshift(pos >= 0 ? history[pos] : history[0]);
- }
- this._line = this._getGraph(valArray, 500, this._config.height);
- }
-
- async _fetchRecent(entityId, startTime, endTime) {
- let url = "history/period";
- if (startTime) url += "/" + startTime.toISOString();
- url += "?filter_entity_id=" + entityId;
- if (endTime) url += "&end_time=" + endTime.toISOString();
-
- return await this._hass.callApi("GET", url);
- }
-
- getCardSize() {
- return 3;
- }
-
- _style() {
- return html`
- `;
- }
-}
-
-customElements.define("hui-sensor-card", HuiSensorCard);
+import { LitElement, html, svg } from "@polymer/lit-element";
+
+import "../../../components/ha-card";
+import "../../../components/ha-icon";
+
+import computeStateName from "../../../common/entity/compute_state_name";
+import stateIcon from "../../../common/entity/state_icon";
+
+import EventsMixin from "../../../mixins/events-mixin";
+
+class HuiSensorCard extends EventsMixin(LitElement) {
+ set hass(hass) {
+ this._hass = hass;
+ const entity = hass.states[this._config.entity];
+ if (entity && this._entity !== entity) {
+ this._entity = entity;
+ if (
+ this._config.graph !== "none" &&
+ entity.attributes.unit_of_measurement
+ ) {
+ this._getHistory();
+ }
+ }
+ }
+
+ static get properties() {
+ return {
+ _hass: {},
+ _config: {},
+ _entity: {},
+ _line: String,
+ };
+ }
+
+ setConfig(config) {
+ if (!config.entity || config.entity.split(".")[0] !== "sensor") {
+ throw new Error("Specify an entity from within the sensor domain.");
+ }
+
+ const cardConfig = {
+ icon: false,
+ hours_to_show: 24,
+ accuracy: 10,
+ height: 100,
+ line_width: 5,
+ line_color: "var(--accent-color)",
+ ...config,
+ };
+ cardConfig.hours_to_show = Number(cardConfig.hours_to_show);
+ cardConfig.accuracy = Number(cardConfig.accuracy);
+ cardConfig.height = Number(cardConfig.height);
+ cardConfig.line_width = Number(cardConfig.line_width);
+
+ this._config = cardConfig;
+ }
+
+ shouldUpdate(changedProps) {
+ const change = changedProps.has("_entity") || changedProps.has("_line");
+ return change;
+ }
+
+ render({ _config, _entity, _line } = this) {
+ return html`
+ ${this._style()}
+
+
+
+ ${_entity.state}
+ ${this._computeUom(_entity)}
+
+
+
+ ${
+ _line
+ ? svg`
+
`
+ : ""
+ }
+
+
+ `;
+ }
+
+ _handleClick() {
+ this.fire("hass-more-info", { entityId: this._config.entity });
+ }
+
+ _computeIcon(item) {
+ return this._config.icon || stateIcon(item);
+ }
+
+ _computeName(item) {
+ return this._config.name || computeStateName(item);
+ }
+
+ _computeUom(item) {
+ return this._config.unit || item.attributes.unit_of_measurement;
+ }
+
+ _getGraph(items, width, height) {
+ const values = this._getValueArr(items);
+ const coords = this._calcCoordinates(values, width, height);
+ return this._getPath(coords);
+ }
+
+ _getValueArr(items) {
+ return items.map((item) => Number(item.state) || 0);
+ }
+
+ _calcCoordinates(values, width, height) {
+ const margin = this._config.line_width;
+ width -= margin * 2;
+ height -= margin * 2;
+ const min = Math.floor(Math.min.apply(null, values) * 0.95);
+ const max = Math.ceil(Math.max.apply(null, values) * 1.05);
+
+ if (values.length === 1) values.push(values[0]);
+
+ const yRatio = (max - min) / height;
+ const xRatio = width / (values.length - 1);
+
+ return values.map((value, i) => {
+ const y = height - (value - min) / yRatio || 0;
+ const x = xRatio * i + margin;
+ return [x, y];
+ });
+ }
+
+ _getPath(points) {
+ const SPACE = " ";
+ let next;
+ let Z;
+ const X = 0;
+ const Y = 1;
+ let path = "";
+ let point = points[0];
+
+ path += "M" + point[X] + "," + point[Y];
+ const first = point;
+
+ for (let i = 0; i < points.length; i++) {
+ next = points[i];
+ Z = this._midPoint(point[X], point[Y], next[X], next[Y]);
+ path += SPACE + Z[X] + "," + Z[Y];
+ path += "Q" + Math.floor(next[X]) + "," + next[Y];
+ point = next;
+ }
+
+ const second = points[1];
+ Z = this._midPoint(first[X], first[Y], second[X], second[Y]);
+ path += SPACE + Math.floor(next[X]) + "." + points[points.length - 1];
+ return path;
+ }
+
+ _midPoint(Ax, Ay, Bx, By) {
+ const Zx = (Ax - Bx) / 2 + Bx;
+ const Zy = (Ay - By) / 2 + By;
+ return [Zx, Zy];
+ }
+
+ async _getHistory() {
+ const endTime = new Date();
+ const startTime = new Date();
+ startTime.setHours(endTime.getHours() - this._config.hours_to_show);
+ const stateHistory = await this._fetchRecent(
+ this._config.entity,
+ startTime,
+ endTime
+ );
+ const history = stateHistory[0];
+ const valArray = [history[history.length - 1]];
+
+ let pos = history.length - 1;
+ const accuracy = this._config.accuracy <= pos ? this._config.accuracy : pos;
+ let increment = Math.ceil(history.length / accuracy);
+ increment = increment <= 0 ? 1 : increment;
+ for (let i = accuracy; i >= 2; i--) {
+ pos -= increment;
+ valArray.unshift(pos >= 0 ? history[pos] : history[0]);
+ }
+ this._line = this._getGraph(valArray, 500, this._config.height);
+ }
+
+ async _fetchRecent(entityId, startTime, endTime) {
+ let url = "history/period";
+ if (startTime) url += "/" + startTime.toISOString();
+ url += "?filter_entity_id=" + entityId;
+ if (endTime) url += "&end_time=" + endTime.toISOString();
+
+ return await this._hass.callApi("GET", url);
+ }
+
+ getCardSize() {
+ return 3;
+ }
+
+ _style() {
+ return html`
+ `;
+ }
+}
+
+customElements.define("hui-sensor-card", HuiSensorCard);
diff --git a/src/panels/lovelace/cards/hui-stack-card.ts b/src/panels/lovelace/cards/hui-stack-card.ts
index 7c2e69651abe..709bf441d272 100644
--- a/src/panels/lovelace/cards/hui-stack-card.ts
+++ b/src/panels/lovelace/cards/hui-stack-card.ts
@@ -1,66 +1,66 @@
-import { html, LitElement } from "@polymer/lit-element";
-import { TemplateResult } from "lit-html";
-
-import createCardElement from "../common/create-card-element.js";
-
-import { LovelaceCard, LovelaceConfig } from "../types";
-import { HomeAssistant } from "../../../types";
-
-interface Config extends LovelaceConfig {
- cards: LovelaceConfig[];
-}
-
-export abstract class HuiStackCard extends LitElement implements LovelaceCard {
- protected _cards?: LovelaceCard[];
- private _config?: Config;
- private _hass?: HomeAssistant;
-
- static get properties() {
- return {
- _config: {},
- };
- }
-
- set hass(hass: HomeAssistant) {
- this._hass = hass;
-
- if (!this._cards) {
- return;
- }
-
- for (const element of this._cards) {
- element.hass = this._hass;
- }
- }
-
- public abstract getCardSize(): number;
-
- public setConfig(config: Config): void {
- if (!config || !config.cards || !Array.isArray(config.cards)) {
- throw new Error("Card config incorrect");
- }
- this._config = config;
- this._cards = config.cards.map((card) => {
- const element = createCardElement(card) as LovelaceCard;
- if (this._hass) {
- element.hass = this._hass;
- }
- return element;
- });
- }
-
- protected render(): TemplateResult {
- if (!this._config) {
- return html``;
- }
-
- return html`
- ${this.renderStyle()}
-
- ${this._cards}
-
- `;
- }
-
- protected abstract renderStyle(): TemplateResult;
-}
+import { html, LitElement } from "@polymer/lit-element";
+import { TemplateResult } from "lit-html";
+
+import createCardElement from "../common/create-card-element";
+
+import { LovelaceCard, LovelaceConfig } from "../types";
+import { HomeAssistant } from "../../../types";
+
+interface Config extends LovelaceConfig {
+ cards: LovelaceConfig[];
+}
+
+export abstract class HuiStackCard extends LitElement implements LovelaceCard {
+ protected _cards?: LovelaceCard[];
+ private _config?: Config;
+ private _hass?: HomeAssistant;
+
+ static get properties() {
+ return {
+ _config: {},
+ };
+ }
+
+ set hass(hass: HomeAssistant) {
+ this._hass = hass;
+
+ if (!this._cards) {
+ return;
+ }
+
+ for (const element of this._cards) {
+ element.hass = this._hass;
+ }
+ }
+
+ public abstract getCardSize(): number;
+
+ public setConfig(config: Config): void {
+ if (!config || !config.cards || !Array.isArray(config.cards)) {
+ throw new Error("Card config incorrect");
+ }
+ this._config = config;
+ this._cards = config.cards.map((card) => {
+ const element = createCardElement(card) as LovelaceCard;
+ if (this._hass) {
+ element.hass = this._hass;
+ }
+ return element;
+ });
+ }
+
+ protected render(): TemplateResult {
+ if (!this._config) {
+ return html``;
+ }
+
+ return html`
+ ${this.renderStyle()}
+
+ ${this._cards}
+
+ `;
+ }
+
+ protected abstract renderStyle(): TemplateResult;
+}
diff --git a/src/panels/lovelace/cards/hui-thermostat-card.ts b/src/panels/lovelace/cards/hui-thermostat-card.ts
index adae97a61889..708a7bcb0aad 100644
--- a/src/panels/lovelace/cards/hui-thermostat-card.ts
+++ b/src/panels/lovelace/cards/hui-thermostat-card.ts
@@ -1,390 +1,390 @@
-import {
- html,
- LitElement,
- PropertyDeclarations,
- PropertyValues,
-} from "@polymer/lit-element";
-import { classMap } from "lit-html/directives/classMap.js";
-import { jQuery } from "../../../resources/jquery";
-
-import "../../../components/ha-card.js";
-import "../../../components/ha-icon.js";
-import { roundSliderStyle } from "../../../resources/jquery.roundslider";
-
-import { HomeAssistant, ClimateEntity } from "../../../types.js";
-import { hassLocalizeLitMixin } from "../../../mixins/lit-localize-mixin";
-import { LovelaceCard, LovelaceConfig } from "../types.js";
-import computeStateName from "../../../common/entity/compute_state_name.js";
-import { TemplateResult } from "lit-html";
-
-const thermostatConfig = {
- radius: 150,
- step: 1,
- circleShape: "pie",
- startAngle: 315,
- width: 5,
- lineCap: "round",
- handleSize: "+10",
- showTooltip: false,
-};
-
-const modeIcons = {
- auto: "hass:autorenew",
- heat: "hass:fire",
- cool: "hass:snowflake",
- off: "hass:power",
-};
-
-interface Config extends LovelaceConfig {
- entity: string;
-}
-
-function formatTemp(temps: string[]): string {
- return temps.filter(Boolean).join("-");
-}
-
-export class HuiThermostatCard extends hassLocalizeLitMixin(LitElement)
- implements LovelaceCard {
- public hass?: HomeAssistant;
- private _config?: Config;
-
- static get properties(): PropertyDeclarations {
- return {
- hass: {},
- _config: {},
- };
- }
-
- public getCardSize(): number {
- return 4;
- }
-
- public setConfig(config: Config): void {
- if (!config.entity || config.entity.split(".")[0] !== "climate") {
- throw new Error("Specify an entity from within the climate domain.");
- }
-
- this._config = config;
- }
-
- protected render(): TemplateResult {
- if (!this.hass || !this._config) {
- return html``;
- }
- const stateObj = this.hass.states[this._config.entity] as ClimateEntity;
- const broadCard = this.clientWidth > 390;
- const mode = modeIcons[stateObj.attributes.operation_mode || ""]
- ? stateObj.attributes.operation_mode!
- : "unknown-mode";
- return html`
- ${this.renderStyle()}
-
-
-
-
-
- `;
- }
-
- protected shouldUpdate(changedProps: PropertyValues): boolean {
- if (changedProps.get("hass")) {
- return (
- (changedProps.get("hass") as any).states[this._config!.entity] !==
- this.hass!.states[this._config!.entity]
- );
- }
- if (changedProps.has("_config")) {
- return true;
- }
- return true;
- }
-
- protected firstUpdated(): void {
- const stateObj = this.hass!.states[this._config!.entity] as ClimateEntity;
-
- const _sliderType =
- stateObj.attributes.target_temp_low &&
- stateObj.attributes.target_temp_high
- ? "range"
- : "min-range";
-
- jQuery("#thermostat", this.shadowRoot).roundSlider({
- ...thermostatConfig,
- radius: this.clientWidth / 3,
- min: stateObj.attributes.min_temp,
- max: stateObj.attributes.max_temp,
- sliderType: _sliderType,
- change: (value) => this._setTemperature(value),
- drag: (value) => this._dragEvent(value),
- });
- }
-
- protected updated(): void {
- const stateObj = this.hass!.states[this._config!.entity] as ClimateEntity;
-
- let sliderValue;
- let uiValue;
-
- if (
- stateObj.attributes.target_temp_low &&
- stateObj.attributes.target_temp_high
- ) {
- sliderValue = `${stateObj.attributes.target_temp_low}, ${
- stateObj.attributes.target_temp_high
- }`;
- uiValue = formatTemp([
- String(stateObj.attributes.target_temp_low),
- String(stateObj.attributes.target_temp_high),
- ]);
- } else {
- sliderValue = uiValue = stateObj.attributes.temperature;
- }
-
- jQuery("#thermostat", this.shadowRoot).roundSlider({
- value: sliderValue,
- });
- this.shadowRoot!.querySelector("#set-temperature")!.innerHTML = uiValue;
- }
-
- private renderStyle(): TemplateResult {
- return html`
- ${roundSliderStyle}
-
- `;
- }
-
- private _dragEvent(e): void {
- this.shadowRoot!.querySelector("#set-temperature")!.innerHTML = formatTemp(
- String(e.value).split(",")
- );
- }
-
- private _setTemperature(e): void {
- const stateObj = this.hass!.states[this._config!.entity] as ClimateEntity;
- if (
- stateObj.attributes.target_temp_low &&
- stateObj.attributes.target_temp_high
- ) {
- if (e.handle.index === 1) {
- this.hass!.callService("climate", "set_temperature", {
- entity_id: this._config!.entity,
- target_temp_low: e.handle.value,
- target_temp_high: stateObj.attributes.target_temp_high,
- });
- } else {
- this.hass!.callService("climate", "set_temperature", {
- entity_id: this._config!.entity,
- target_temp_low: stateObj.attributes.target_temp_low,
- target_temp_high: e.handle.value,
- });
- }
- } else {
- this.hass!.callService("climate", "set_temperature", {
- entity_id: this._config!.entity,
- temperature: e.value,
- });
- }
- }
-
- private _renderIcon(mode: string, currentMode: string): TemplateResult {
- if (!modeIcons[mode]) {
- return html``;
- }
- return html`
`;
- }
-
- private _handleModeClick(e: MouseEvent): void {
- this.hass!.callService("climate", "set_operation_mode", {
- entity_id: this._config!.entity,
- operation_mode: (e.currentTarget as any).mode,
- });
- }
-}
-
-declare global {
- interface HTMLElementTagNameMap {
- "hui-thermostat-card": HuiThermostatCard;
- }
-}
-
-customElements.define("hui-thermostat-card", HuiThermostatCard);
+import {
+ html,
+ LitElement,
+ PropertyDeclarations,
+ PropertyValues,
+} from "@polymer/lit-element";
+import { classMap } from "lit-html/directives/classMap";
+import { jQuery } from "../../../resources/jquery";
+
+import "../../../components/ha-card";
+import "../../../components/ha-icon";
+import { roundSliderStyle } from "../../../resources/jquery.roundslider";
+
+import { HomeAssistant, ClimateEntity } from "../../../types";
+import { hassLocalizeLitMixin } from "../../../mixins/lit-localize-mixin";
+import { LovelaceCard, LovelaceConfig } from "../types";
+import computeStateName from "../../../common/entity/compute_state_name";
+import { TemplateResult } from "lit-html";
+
+const thermostatConfig = {
+ radius: 150,
+ step: 1,
+ circleShape: "pie",
+ startAngle: 315,
+ width: 5,
+ lineCap: "round",
+ handleSize: "+10",
+ showTooltip: false,
+};
+
+const modeIcons = {
+ auto: "hass:autorenew",
+ heat: "hass:fire",
+ cool: "hass:snowflake",
+ off: "hass:power",
+};
+
+interface Config extends LovelaceConfig {
+ entity: string;
+}
+
+function formatTemp(temps: string[]): string {
+ return temps.filter(Boolean).join("-");
+}
+
+export class HuiThermostatCard extends hassLocalizeLitMixin(LitElement)
+ implements LovelaceCard {
+ public hass?: HomeAssistant;
+ private _config?: Config;
+
+ static get properties(): PropertyDeclarations {
+ return {
+ hass: {},
+ _config: {},
+ };
+ }
+
+ public getCardSize(): number {
+ return 4;
+ }
+
+ public setConfig(config: Config): void {
+ if (!config.entity || config.entity.split(".")[0] !== "climate") {
+ throw new Error("Specify an entity from within the climate domain.");
+ }
+
+ this._config = config;
+ }
+
+ protected render(): TemplateResult {
+ if (!this.hass || !this._config) {
+ return html``;
+ }
+ const stateObj = this.hass.states[this._config.entity] as ClimateEntity;
+ const broadCard = this.clientWidth > 390;
+ const mode = modeIcons[stateObj.attributes.operation_mode || ""]
+ ? stateObj.attributes.operation_mode!
+ : "unknown-mode";
+ return html`
+ ${this.renderStyle()}
+
+
+
+
+
+ `;
+ }
+
+ protected shouldUpdate(changedProps: PropertyValues): boolean {
+ if (changedProps.get("hass")) {
+ return (
+ (changedProps.get("hass") as any).states[this._config!.entity] !==
+ this.hass!.states[this._config!.entity]
+ );
+ }
+ if (changedProps.has("_config")) {
+ return true;
+ }
+ return true;
+ }
+
+ protected firstUpdated(): void {
+ const stateObj = this.hass!.states[this._config!.entity] as ClimateEntity;
+
+ const _sliderType =
+ stateObj.attributes.target_temp_low &&
+ stateObj.attributes.target_temp_high
+ ? "range"
+ : "min-range";
+
+ jQuery("#thermostat", this.shadowRoot).roundSlider({
+ ...thermostatConfig,
+ radius: this.clientWidth / 3,
+ min: stateObj.attributes.min_temp,
+ max: stateObj.attributes.max_temp,
+ sliderType: _sliderType,
+ change: (value) => this._setTemperature(value),
+ drag: (value) => this._dragEvent(value),
+ });
+ }
+
+ protected updated(): void {
+ const stateObj = this.hass!.states[this._config!.entity] as ClimateEntity;
+
+ let sliderValue;
+ let uiValue;
+
+ if (
+ stateObj.attributes.target_temp_low &&
+ stateObj.attributes.target_temp_high
+ ) {
+ sliderValue = `${stateObj.attributes.target_temp_low}, ${
+ stateObj.attributes.target_temp_high
+ }`;
+ uiValue = formatTemp([
+ String(stateObj.attributes.target_temp_low),
+ String(stateObj.attributes.target_temp_high),
+ ]);
+ } else {
+ sliderValue = uiValue = stateObj.attributes.temperature;
+ }
+
+ jQuery("#thermostat", this.shadowRoot).roundSlider({
+ value: sliderValue,
+ });
+ this.shadowRoot!.querySelector("#set-temperature")!.innerHTML = uiValue;
+ }
+
+ private renderStyle(): TemplateResult {
+ return html`
+ ${roundSliderStyle}
+
+ `;
+ }
+
+ private _dragEvent(e): void {
+ this.shadowRoot!.querySelector("#set-temperature")!.innerHTML = formatTemp(
+ String(e.value).split(",")
+ );
+ }
+
+ private _setTemperature(e): void {
+ const stateObj = this.hass!.states[this._config!.entity] as ClimateEntity;
+ if (
+ stateObj.attributes.target_temp_low &&
+ stateObj.attributes.target_temp_high
+ ) {
+ if (e.handle.index === 1) {
+ this.hass!.callService("climate", "set_temperature", {
+ entity_id: this._config!.entity,
+ target_temp_low: e.handle.value,
+ target_temp_high: stateObj.attributes.target_temp_high,
+ });
+ } else {
+ this.hass!.callService("climate", "set_temperature", {
+ entity_id: this._config!.entity,
+ target_temp_low: stateObj.attributes.target_temp_low,
+ target_temp_high: e.handle.value,
+ });
+ }
+ } else {
+ this.hass!.callService("climate", "set_temperature", {
+ entity_id: this._config!.entity,
+ temperature: e.value,
+ });
+ }
+ }
+
+ private _renderIcon(mode: string, currentMode: string): TemplateResult {
+ if (!modeIcons[mode]) {
+ return html``;
+ }
+ return html`
`;
+ }
+
+ private _handleModeClick(e: MouseEvent): void {
+ this.hass!.callService("climate", "set_operation_mode", {
+ entity_id: this._config!.entity,
+ operation_mode: (e.currentTarget as any).mode,
+ });
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ "hui-thermostat-card": HuiThermostatCard;
+ }
+}
+
+customElements.define("hui-thermostat-card", HuiThermostatCard);
diff --git a/src/panels/lovelace/cards/hui-vertical-stack-card.ts b/src/panels/lovelace/cards/hui-vertical-stack-card.ts
index 863641cf1dab..3d13d2827ccd 100644
--- a/src/panels/lovelace/cards/hui-vertical-stack-card.ts
+++ b/src/panels/lovelace/cards/hui-vertical-stack-card.ts
@@ -1,50 +1,50 @@
-import { html } from "@polymer/lit-element";
-
-import computeCardSize from "../common/compute-card-size.js";
-
-import { HuiStackCard } from "./hui-stack-card";
-import { TemplateResult } from "lit-html";
-
-class HuiVerticalStackCard extends HuiStackCard {
- public getCardSize() {
- let totalSize = 0;
-
- if (!this._cards) {
- return totalSize;
- }
-
- for (const element of this._cards) {
- totalSize += computeCardSize(element);
- }
-
- return totalSize;
- }
-
- protected renderStyle(): TemplateResult {
- return html`
-
- `;
- }
-}
-
-declare global {
- interface HTMLElementTagNameMap {
- "hui-vertical-stack-card": HuiVerticalStackCard;
- }
-}
-
-customElements.define("hui-vertical-stack-card", HuiVerticalStackCard);
+import { html } from "@polymer/lit-element";
+
+import computeCardSize from "../common/compute-card-size";
+
+import { HuiStackCard } from "./hui-stack-card";
+import { TemplateResult } from "lit-html";
+
+class HuiVerticalStackCard extends HuiStackCard {
+ public getCardSize() {
+ let totalSize = 0;
+
+ if (!this._cards) {
+ return totalSize;
+ }
+
+ for (const element of this._cards) {
+ totalSize += computeCardSize(element);
+ }
+
+ return totalSize;
+ }
+
+ protected renderStyle(): TemplateResult {
+ return html`
+
+ `;
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ "hui-vertical-stack-card": HuiVerticalStackCard;
+ }
+}
+
+customElements.define("hui-vertical-stack-card", HuiVerticalStackCard);
diff --git a/src/panels/lovelace/cards/hui-weather-forecast-card.js b/src/panels/lovelace/cards/hui-weather-forecast-card.js
index 082c5fc7d408..9ad437423133 100644
--- a/src/panels/lovelace/cards/hui-weather-forecast-card.js
+++ b/src/panels/lovelace/cards/hui-weather-forecast-card.js
@@ -1,15 +1,15 @@
-import "../../../cards/ha-camera-card.js";
-
-import LegacyWrapperCard from "./hui-legacy-wrapper-card.js";
-
-class HuiWeatherForecastCard extends LegacyWrapperCard {
- constructor() {
- super("ha-weather-card", "weather");
- }
-
- getCardSize() {
- return 4;
- }
-}
-
-customElements.define("hui-weather-forecast-card", HuiWeatherForecastCard);
+import "../../../cards/ha-camera-card";
+
+import LegacyWrapperCard from "./hui-legacy-wrapper-card";
+
+class HuiWeatherForecastCard extends LegacyWrapperCard {
+ constructor() {
+ super("ha-weather-card", "weather");
+ }
+
+ getCardSize() {
+ return 4;
+ }
+}
+
+customElements.define("hui-weather-forecast-card", HuiWeatherForecastCard);
diff --git a/src/panels/lovelace/common/compute-notifications.js b/src/panels/lovelace/common/compute-notifications.js
index 94c894e860c0..0e9f57316f19 100644
--- a/src/panels/lovelace/common/compute-notifications.js
+++ b/src/panels/lovelace/common/compute-notifications.js
@@ -1,7 +1,7 @@
-import computeDomain from "../../../common/entity/compute_domain.js";
-
-export default function computeNotifications(states) {
- return Object.keys(states)
- .filter((entityId) => computeDomain(entityId) === "configurator")
- .map((entityId) => states[entityId]);
-}
+import computeDomain from "../../../common/entity/compute_domain";
+
+export default function computeNotifications(states) {
+ return Object.keys(states)
+ .filter((entityId) => computeDomain(entityId) === "configurator")
+ .map((entityId) => states[entityId]);
+}
diff --git a/src/panels/lovelace/common/create-card-element.js b/src/panels/lovelace/common/create-card-element.js
index 92db6f4a6fe0..9417fa8989e8 100644
--- a/src/panels/lovelace/common/create-card-element.js
+++ b/src/panels/lovelace/common/create-card-element.js
@@ -1,112 +1,112 @@
-import { fireEvent } from "../../../common/dom/fire_event.js";
-
-import "../cards/hui-alarm-panel-card.js";
-import "../cards/hui-conditional-card.ts";
-import "../cards/hui-entities-card.ts";
-import "../cards/hui-entity-button-card.ts";
-import "../cards/hui-entity-filter-card.js";
-import "../cards/hui-error-card.ts";
-import "../cards/hui-glance-card.ts";
-import "../cards/hui-history-graph-card.js";
-import "../cards/hui-horizontal-stack-card.ts";
-import "../cards/hui-iframe-card.ts";
-import "../cards/hui-light-card";
-import "../cards/hui-map-card.js";
-import "../cards/hui-markdown-card.ts";
-import "../cards/hui-media-control-card.js";
-import "../cards/hui-picture-card.js";
-import "../cards/hui-picture-elements-card";
-import "../cards/hui-picture-entity-card";
-import "../cards/hui-picture-glance-card";
-import "../cards/hui-plant-status-card.js";
-import "../cards/hui-sensor-card.js";
-import "../cards/hui-vertical-stack-card.ts";
-import "../cards/hui-thermostat-card.ts";
-import "../cards/hui-weather-forecast-card";
-import "../cards/hui-gauge-card";
-
-import createErrorCardConfig from "./create-error-card-config.js";
-
-const CARD_TYPES = new Set([
- "alarm-panel",
- "conditional",
- "entities",
- "entity-button",
- "entity-filter",
- "error",
- "gauge",
- "glance",
- "history-graph",
- "horizontal-stack",
- "iframe",
- "light",
- "map",
- "markdown",
- "media-control",
- "picture",
- "picture-elements",
- "picture-entity",
- "picture-glance",
- "plant-status",
- "sensor",
- "thermostat",
- "vertical-stack",
- "weather-forecast",
-]);
-const CUSTOM_TYPE_PREFIX = "custom:";
-const TIMEOUT = 2000;
-
-function _createElement(tag, config) {
- const element = document.createElement(tag);
- try {
- element.setConfig(config);
- } catch (err) {
- // eslint-disable-next-line
- console.error(tag, err);
- // eslint-disable-next-line
- return _createErrorElement(err.message, config);
- }
- return element;
-}
-
-function _createErrorElement(error, config) {
- return _createElement("hui-error-card", createErrorCardConfig(error, config));
-}
-
-export default function createCardElement(config) {
- if (!config || typeof config !== "object" || !config.type) {
- return _createErrorElement("No card type configured.", config);
- }
-
- if (config.type.startsWith(CUSTOM_TYPE_PREFIX)) {
- const tag = config.type.substr(CUSTOM_TYPE_PREFIX.length);
-
- if (customElements.get(tag)) {
- return _createElement(tag, config);
- }
- const element = _createErrorElement(
- `Custom element doesn't exist: ${tag}.`,
- config
- );
- element.style.display = "None";
- const timer = window.setTimeout(() => {
- element.style.display = "";
- }, TIMEOUT);
-
- customElements.whenDefined(tag).then(() => {
- clearTimeout(timer);
- fireEvent(element, "rebuild-view");
- });
-
- return element;
- }
-
- if (!CARD_TYPES.has(config.type)) {
- return _createErrorElement(
- `Unknown card type encountered: ${config.type}.`,
- config
- );
- }
-
- return _createElement(`hui-${config.type}-card`, config);
-}
+import { fireEvent } from "../../../common/dom/fire_event";
+
+import "../cards/hui-alarm-panel-card";
+import "../cards/hui-conditional-card.ts";
+import "../cards/hui-entities-card.ts";
+import "../cards/hui-entity-button-card.ts";
+import "../cards/hui-entity-filter-card";
+import "../cards/hui-error-card.ts";
+import "../cards/hui-glance-card.ts";
+import "../cards/hui-history-graph-card";
+import "../cards/hui-horizontal-stack-card.ts";
+import "../cards/hui-iframe-card.ts";
+import "../cards/hui-light-card";
+import "../cards/hui-map-card";
+import "../cards/hui-markdown-card.ts";
+import "../cards/hui-media-control-card";
+import "../cards/hui-picture-card";
+import "../cards/hui-picture-elements-card";
+import "../cards/hui-picture-entity-card";
+import "../cards/hui-picture-glance-card";
+import "../cards/hui-plant-status-card";
+import "../cards/hui-sensor-card";
+import "../cards/hui-vertical-stack-card.ts";
+import "../cards/hui-thermostat-card.ts";
+import "../cards/hui-weather-forecast-card";
+import "../cards/hui-gauge-card";
+
+import createErrorCardConfig from "./create-error-card-config";
+
+const CARD_TYPES = new Set([
+ "alarm-panel",
+ "conditional",
+ "entities",
+ "entity-button",
+ "entity-filter",
+ "error",
+ "gauge",
+ "glance",
+ "history-graph",
+ "horizontal-stack",
+ "iframe",
+ "light",
+ "map",
+ "markdown",
+ "media-control",
+ "picture",
+ "picture-elements",
+ "picture-entity",
+ "picture-glance",
+ "plant-status",
+ "sensor",
+ "thermostat",
+ "vertical-stack",
+ "weather-forecast",
+]);
+const CUSTOM_TYPE_PREFIX = "custom:";
+const TIMEOUT = 2000;
+
+function _createElement(tag, config) {
+ const element = document.createElement(tag);
+ try {
+ element.setConfig(config);
+ } catch (err) {
+ // eslint-disable-next-line
+ console.error(tag, err);
+ // eslint-disable-next-line
+ return _createErrorElement(err.message, config);
+ }
+ return element;
+}
+
+function _createErrorElement(error, config) {
+ return _createElement("hui-error-card", createErrorCardConfig(error, config));
+}
+
+export default function createCardElement(config) {
+ if (!config || typeof config !== "object" || !config.type) {
+ return _createErrorElement("No card type configured.", config);
+ }
+
+ if (config.type.startsWith(CUSTOM_TYPE_PREFIX)) {
+ const tag = config.type.substr(CUSTOM_TYPE_PREFIX.length);
+
+ if (customElements.get(tag)) {
+ return _createElement(tag, config);
+ }
+ const element = _createErrorElement(
+ `Custom element doesn't exist: ${tag}.`,
+ config
+ );
+ element.style.display = "None";
+ const timer = window.setTimeout(() => {
+ element.style.display = "";
+ }, TIMEOUT);
+
+ customElements.whenDefined(tag).then(() => {
+ clearTimeout(timer);
+ fireEvent(element, "rebuild-view");
+ });
+
+ return element;
+ }
+
+ if (!CARD_TYPES.has(config.type)) {
+ return _createErrorElement(
+ `Unknown card type encountered: ${config.type}.`,
+ config
+ );
+ }
+
+ return _createElement(`hui-${config.type}-card`, config);
+}
diff --git a/src/panels/lovelace/common/create-hui-element.js b/src/panels/lovelace/common/create-hui-element.js
index 39d99926c949..b02b86dbcfd2 100644
--- a/src/panels/lovelace/common/create-hui-element.js
+++ b/src/panels/lovelace/common/create-hui-element.js
@@ -1,79 +1,79 @@
-import "../elements/hui-icon-element";
-import "../elements/hui-image-element";
-import "../elements/hui-service-button-element";
-import "../elements/hui-state-badge-element";
-import "../elements/hui-state-icon-element";
-import "../elements/hui-state-label-element";
-
-import { fireEvent } from "../../../common/dom/fire_event.js";
-import createErrorCardConfig from "./create-error-card-config.js";
-
-const CUSTOM_TYPE_PREFIX = "custom:";
-const ELEMENT_TYPES = new Set([
- "icon",
- "image",
- "service-button",
- "state-badge",
- "state-icon",
- "state-label",
-]);
-const TIMEOUT = 2000;
-
-function _createElement(tag, config) {
- const element = document.createElement(tag);
- try {
- element.setConfig(config);
- } catch (err) {
- // eslint-disable-next-line
- console.error(tag, err);
- // eslint-disable-next-line
- return _createErrorElement(err.message, config);
- }
- return element;
-}
-
-function _createErrorElement(error, config) {
- return _createElement("hui-error-card", createErrorCardConfig(error, config));
-}
-
-function _hideErrorElement(element) {
- element.style.display = "None";
- return window.setTimeout(() => {
- element.style.display = "";
- }, TIMEOUT);
-}
-
-export default function createHuiElement(config) {
- if (!config || typeof config !== "object" || !config.type) {
- return _createErrorElement("No element type configured.", config);
- }
-
- if (config.type.startsWith(CUSTOM_TYPE_PREFIX)) {
- const tag = config.type.substr(CUSTOM_TYPE_PREFIX.length);
-
- if (customElements.get(tag)) {
- return _createElement(tag, config);
- }
- const element = _createErrorElement(
- `Custom element doesn't exist: ${tag}.`,
- config
- );
- const timer = _hideErrorElement(element);
-
- customElements.whenDefined(tag).then(() => {
- clearTimeout(timer);
- fireEvent(element, "rebuild-view");
- });
-
- return element;
- }
-
- if (!ELEMENT_TYPES.has(config.type)) {
- return _createErrorElement(
- `Unknown element type encountered: ${config.type}.`,
- config
- );
- }
-
- return _createElement(`hui-${config.type}-element`, config);
-}
+import "../elements/hui-icon-element";
+import "../elements/hui-image-element";
+import "../elements/hui-service-button-element";
+import "../elements/hui-state-badge-element";
+import "../elements/hui-state-icon-element";
+import "../elements/hui-state-label-element";
+
+import { fireEvent } from "../../../common/dom/fire_event";
+import createErrorCardConfig from "./create-error-card-config";
+
+const CUSTOM_TYPE_PREFIX = "custom:";
+const ELEMENT_TYPES = new Set([
+ "icon",
+ "image",
+ "service-button",
+ "state-badge",
+ "state-icon",
+ "state-label",
+]);
+const TIMEOUT = 2000;
+
+function _createElement(tag, config) {
+ const element = document.createElement(tag);
+ try {
+ element.setConfig(config);
+ } catch (err) {
+ // eslint-disable-next-line
+ console.error(tag, err);
+ // eslint-disable-next-line
+ return _createErrorElement(err.message, config);
+ }
+ return element;
+}
+
+function _createErrorElement(error, config) {
+ return _createElement("hui-error-card", createErrorCardConfig(error, config));
+}
+
+function _hideErrorElement(element) {
+ element.style.display = "None";
+ return window.setTimeout(() => {
+ element.style.display = "";
+ }, TIMEOUT);
+}
+
+export default function createHuiElement(config) {
+ if (!config || typeof config !== "object" || !config.type) {
+ return _createErrorElement("No element type configured.", config);
+ }
+
+ if (config.type.startsWith(CUSTOM_TYPE_PREFIX)) {
+ const tag = config.type.substr(CUSTOM_TYPE_PREFIX.length);
+
+ if (customElements.get(tag)) {
+ return _createElement(tag, config);
+ }
+ const element = _createErrorElement(
+ `Custom element doesn't exist: ${tag}.`,
+ config
+ );
+ const timer = _hideErrorElement(element);
+
+ customElements.whenDefined(tag).then(() => {
+ clearTimeout(timer);
+ fireEvent(element, "rebuild-view");
+ });
+
+ return element;
+ }
+
+ if (!ELEMENT_TYPES.has(config.type)) {
+ return _createErrorElement(
+ `Unknown element type encountered: ${config.type}.`,
+ config
+ );
+ }
+
+ return _createElement(`hui-${config.type}-element`, config);
+}
diff --git a/src/panels/lovelace/common/create-row-element.js b/src/panels/lovelace/common/create-row-element.js
index 74222bc2584d..f973db948d26 100644
--- a/src/panels/lovelace/common/create-row-element.js
+++ b/src/panels/lovelace/common/create-row-element.js
@@ -1,117 +1,117 @@
-import { fireEvent } from "../../../common/dom/fire_event.js";
-
-import "../entity-rows/hui-climate-entity-row";
-import "../entity-rows/hui-cover-entity-row.js";
-import "../entity-rows/hui-group-entity-row.js";
-import "../entity-rows/hui-input-number-entity-row.js";
-import "../entity-rows/hui-input-select-entity-row.js";
-import "../entity-rows/hui-input-text-entity-row.js";
-import "../entity-rows/hui-lock-entity-row.js";
-import "../entity-rows/hui-media-player-entity-row.js";
-import "../entity-rows/hui-scene-entity-row.js";
-import "../entity-rows/hui-script-entity-row.js";
-import "../entity-rows/hui-text-entity-row.js";
-import "../entity-rows/hui-timer-entity-row.js";
-import "../entity-rows/hui-toggle-entity-row.js";
-
-import "../special-rows/hui-call-service-row";
-import "../special-rows/hui-divider-row";
-import "../special-rows/hui-section-row";
-import "../special-rows/hui-weblink-row";
-
-import createErrorCardConfig from "./create-error-card-config.js";
-
-const CUSTOM_TYPE_PREFIX = "custom:";
-const SPECIAL_TYPES = new Set([
- "call-service",
- "divider",
- "section",
- "weblink",
-]);
-const DOMAIN_TO_ELEMENT_TYPE = {
- automation: "toggle",
- climate: "climate",
- cover: "cover",
- fan: "toggle",
- group: "group",
- input_boolean: "toggle",
- input_number: "input-number",
- input_select: "input-select",
- input_text: "input-text",
- light: "toggle",
- media_player: "media-player",
- lock: "lock",
- scene: "scene",
- script: "script",
- timer: "timer",
- switch: "toggle",
- vacuum: "toggle",
-};
-const TIMEOUT = 2000;
-
-function _createElement(tag, config) {
- const element = document.createElement(tag);
- try {
- if ("setConfig" in element) element.setConfig(config);
- } catch (err) {
- // eslint-disable-next-line
- console.error(tag, err);
- // eslint-disable-next-line
- return _createErrorElement(err.message, config);
- }
-
- return element;
-}
-
-function _createErrorElement(error, config) {
- return _createElement("hui-error-card", createErrorCardConfig(error, config));
-}
-
-function _hideErrorElement(element) {
- element.style.display = "None";
- return window.setTimeout(() => {
- element.style.display = "";
- }, TIMEOUT);
-}
-
-export default function createRowElement(config) {
- let tag;
-
- if (
- !config ||
- typeof config !== "object" ||
- (!config.entity && !config.type)
- ) {
- return _createErrorElement("Invalid config given.", config);
- }
-
- const type = config.type || "default";
- if (SPECIAL_TYPES.has(type)) {
- return _createElement(`hui-${type}-row`, config);
- }
-
- if (type.startsWith(CUSTOM_TYPE_PREFIX)) {
- tag = type.substr(CUSTOM_TYPE_PREFIX.length);
-
- if (customElements.get(tag)) {
- return _createElement(tag, config);
- }
- const element = _createErrorElement(
- `Custom element doesn't exist: ${tag}.`,
- config
- );
- const timer = _hideErrorElement(element);
-
- customElements.whenDefined(tag).then(() => {
- clearTimeout(timer);
- fireEvent(element, "rebuild-view");
- });
-
- return element;
- }
-
- const domain = config.entity.split(".", 1)[0];
- tag = `hui-${DOMAIN_TO_ELEMENT_TYPE[domain] || "text"}-entity-row`;
-
- return _createElement(tag, config);
-}
+import { fireEvent } from "../../../common/dom/fire_event";
+
+import "../entity-rows/hui-climate-entity-row";
+import "../entity-rows/hui-cover-entity-row";
+import "../entity-rows/hui-group-entity-row";
+import "../entity-rows/hui-input-number-entity-row";
+import "../entity-rows/hui-input-select-entity-row";
+import "../entity-rows/hui-input-text-entity-row";
+import "../entity-rows/hui-lock-entity-row";
+import "../entity-rows/hui-media-player-entity-row";
+import "../entity-rows/hui-scene-entity-row";
+import "../entity-rows/hui-script-entity-row";
+import "../entity-rows/hui-text-entity-row";
+import "../entity-rows/hui-timer-entity-row";
+import "../entity-rows/hui-toggle-entity-row";
+
+import "../special-rows/hui-call-service-row";
+import "../special-rows/hui-divider-row";
+import "../special-rows/hui-section-row";
+import "../special-rows/hui-weblink-row";
+
+import createErrorCardConfig from "./create-error-card-config";
+
+const CUSTOM_TYPE_PREFIX = "custom:";
+const SPECIAL_TYPES = new Set([
+ "call-service",
+ "divider",
+ "section",
+ "weblink",
+]);
+const DOMAIN_TO_ELEMENT_TYPE = {
+ automation: "toggle",
+ climate: "climate",
+ cover: "cover",
+ fan: "toggle",
+ group: "group",
+ input_boolean: "toggle",
+ input_number: "input-number",
+ input_select: "input-select",
+ input_text: "input-text",
+ light: "toggle",
+ media_player: "media-player",
+ lock: "lock",
+ scene: "scene",
+ script: "script",
+ timer: "timer",
+ switch: "toggle",
+ vacuum: "toggle",
+};
+const TIMEOUT = 2000;
+
+function _createElement(tag, config) {
+ const element = document.createElement(tag);
+ try {
+ if ("setConfig" in element) element.setConfig(config);
+ } catch (err) {
+ // eslint-disable-next-line
+ console.error(tag, err);
+ // eslint-disable-next-line
+ return _createErrorElement(err.message, config);
+ }
+
+ return element;
+}
+
+function _createErrorElement(error, config) {
+ return _createElement("hui-error-card", createErrorCardConfig(error, config));
+}
+
+function _hideErrorElement(element) {
+ element.style.display = "None";
+ return window.setTimeout(() => {
+ element.style.display = "";
+ }, TIMEOUT);
+}
+
+export default function createRowElement(config) {
+ let tag;
+
+ if (
+ !config ||
+ typeof config !== "object" ||
+ (!config.entity && !config.type)
+ ) {
+ return _createErrorElement("Invalid config given.", config);
+ }
+
+ const type = config.type || "default";
+ if (SPECIAL_TYPES.has(type)) {
+ return _createElement(`hui-${type}-row`, config);
+ }
+
+ if (type.startsWith(CUSTOM_TYPE_PREFIX)) {
+ tag = type.substr(CUSTOM_TYPE_PREFIX.length);
+
+ if (customElements.get(tag)) {
+ return _createElement(tag, config);
+ }
+ const element = _createErrorElement(
+ `Custom element doesn't exist: ${tag}.`,
+ config
+ );
+ const timer = _hideErrorElement(element);
+
+ customElements.whenDefined(tag).then(() => {
+ clearTimeout(timer);
+ fireEvent(element, "rebuild-view");
+ });
+
+ return element;
+ }
+
+ const domain = config.entity.split(".", 1)[0];
+ tag = `hui-${DOMAIN_TO_ELEMENT_TYPE[domain] || "text"}-entity-row`;
+
+ return _createElement(tag, config);
+}
diff --git a/src/panels/lovelace/common/entity/toggle-entity.js b/src/panels/lovelace/common/entity/toggle-entity.js
index aa57a309d837..21820da7f7bf 100644
--- a/src/panels/lovelace/common/entity/toggle-entity.js
+++ b/src/panels/lovelace/common/entity/toggle-entity.js
@@ -1,7 +1,7 @@
-import { STATES_OFF } from "../../../../common/const.js";
-import turnOnOffEntity from "./turn-on-off-entity.js";
-
-export default function toggleEntity(hass, entityId) {
- const turnOn = STATES_OFF.includes(hass.states[entityId].state);
- turnOnOffEntity(hass, entityId, turnOn);
-}
+import { STATES_OFF } from "../../../../common/const";
+import turnOnOffEntity from "./turn-on-off-entity";
+
+export default function toggleEntity(hass, entityId) {
+ const turnOn = STATES_OFF.includes(hass.states[entityId].state);
+ turnOnOffEntity(hass, entityId, turnOn);
+}
diff --git a/src/panels/lovelace/common/entity/turn-on-off-entities.js b/src/panels/lovelace/common/entity/turn-on-off-entities.js
index caacc03e7911..b3a990e74b6c 100644
--- a/src/panels/lovelace/common/entity/turn-on-off-entities.js
+++ b/src/panels/lovelace/common/entity/turn-on-off-entities.js
@@ -1,34 +1,34 @@
-import { STATES_OFF } from "../../../../common/const.js";
-import computeDomain from "../../../../common/entity/compute_domain.js";
-
-export default function turnOnOffEntities(hass, entityIds, turnOn = true) {
- const domainsToCall = {};
- entityIds.forEach((entityId) => {
- if (STATES_OFF.includes(hass.states[entityId].state) === turnOn) {
- const stateDomain = computeDomain(entityId);
- const serviceDomain = ["cover", "lock"].includes(stateDomain)
- ? stateDomain
- : "homeassistant";
-
- if (!(serviceDomain in domainsToCall)) domainsToCall[serviceDomain] = [];
- domainsToCall[serviceDomain].push(entityId);
- }
- });
-
- Object.keys(domainsToCall).forEach((domain) => {
- let service;
- switch (domain) {
- case "lock":
- service = turnOn ? "unlock" : "lock";
- break;
- case "cover":
- service = turnOn ? "open_cover" : "close_cover";
- break;
- default:
- service = turnOn ? "turn_on" : "turn_off";
- }
-
- const entities = domainsToCall[domain];
- hass.callService(domain, service, { entity_id: entities });
- });
-}
+import { STATES_OFF } from "../../../../common/const";
+import computeDomain from "../../../../common/entity/compute_domain";
+
+export default function turnOnOffEntities(hass, entityIds, turnOn = true) {
+ const domainsToCall = {};
+ entityIds.forEach((entityId) => {
+ if (STATES_OFF.includes(hass.states[entityId].state) === turnOn) {
+ const stateDomain = computeDomain(entityId);
+ const serviceDomain = ["cover", "lock"].includes(stateDomain)
+ ? stateDomain
+ : "homeassistant";
+
+ if (!(serviceDomain in domainsToCall)) domainsToCall[serviceDomain] = [];
+ domainsToCall[serviceDomain].push(entityId);
+ }
+ });
+
+ Object.keys(domainsToCall).forEach((domain) => {
+ let service;
+ switch (domain) {
+ case "lock":
+ service = turnOn ? "unlock" : "lock";
+ break;
+ case "cover":
+ service = turnOn ? "open_cover" : "close_cover";
+ break;
+ default:
+ service = turnOn ? "turn_on" : "turn_off";
+ }
+
+ const entities = domainsToCall[domain];
+ hass.callService(domain, service, { entity_id: entities });
+ });
+}
diff --git a/src/panels/lovelace/common/entity/turn-on-off-entity.js b/src/panels/lovelace/common/entity/turn-on-off-entity.js
index f34d04c0642f..dc7e5d1a4d7c 100644
--- a/src/panels/lovelace/common/entity/turn-on-off-entity.js
+++ b/src/panels/lovelace/common/entity/turn-on-off-entity.js
@@ -1,20 +1,20 @@
-import computeDomain from "../../../../common/entity/compute_domain.js";
-
-export default function turnOnOffEntity(hass, entityId, turnOn = true) {
- const stateDomain = computeDomain(entityId);
- const serviceDomain = stateDomain === "group" ? "homeassistant" : stateDomain;
-
- let service;
- switch (stateDomain) {
- case "lock":
- service = turnOn ? "unlock" : "lock";
- break;
- case "cover":
- service = turnOn ? "open_cover" : "close_cover";
- break;
- default:
- service = turnOn ? "turn_on" : "turn_off";
- }
-
- hass.callService(serviceDomain, service, { entity_id: entityId });
-}
+import computeDomain from "../../../../common/entity/compute_domain";
+
+export default function turnOnOffEntity(hass, entityId, turnOn = true) {
+ const stateDomain = computeDomain(entityId);
+ const serviceDomain = stateDomain === "group" ? "homeassistant" : stateDomain;
+
+ let service;
+ switch (stateDomain) {
+ case "lock":
+ service = turnOn ? "unlock" : "lock";
+ break;
+ case "cover":
+ service = turnOn ? "open_cover" : "close_cover";
+ break;
+ default:
+ service = turnOn ? "turn_on" : "turn_off";
+ }
+
+ hass.callService(serviceDomain, service, { entity_id: entityId });
+}
diff --git a/src/panels/lovelace/common/handle-click.ts b/src/panels/lovelace/common/handle-click.ts
index 4d98cf7ad74c..c996bf4bac89 100644
--- a/src/panels/lovelace/common/handle-click.ts
+++ b/src/panels/lovelace/common/handle-click.ts
@@ -1,6 +1,6 @@
import { HomeAssistant } from "../../../types";
import { LovelaceElementConfig } from "../elements/types";
-import { fireEvent } from "../../../common/dom/fire_event.js";
+import { fireEvent } from "../../../common/dom/fire_event";
import { navigate } from "../../../common/navigate";
import toggleEntity from "../../../../src/panels/lovelace/common/entity/toggle-entity";
diff --git a/src/panels/lovelace/common/process-config-entities.js b/src/panels/lovelace/common/process-config-entities.js
index 2008ab0502cd..f8082886b345 100644
--- a/src/panels/lovelace/common/process-config-entities.js
+++ b/src/panels/lovelace/common/process-config-entities.js
@@ -1,38 +1,38 @@
-// Parse array of entity objects from config
-import isValidEntityId from "../../../common/entity/valid_entity_id.js";
-
-export default function processConfigEntities(entities) {
- if (!entities || !Array.isArray(entities)) {
- throw new Error("Entities need to be an array");
- }
-
- return entities.map((entityConf, index) => {
- if (
- typeof entityConf === "object" &&
- !Array.isArray(entityConf) &&
- entityConf.type
- ) {
- return entityConf;
- }
-
- if (typeof entityConf === "string") {
- entityConf = { entity: entityConf };
- } else if (typeof entityConf === "object" && !Array.isArray(entityConf)) {
- if (!entityConf.entity) {
- throw new Error(
- `Entity object at position ${index} is missing entity field.`
- );
- }
- } else {
- throw new Error(`Invalid entity specified at position ${index}.`);
- }
-
- if (!isValidEntityId(entityConf.entity)) {
- throw new Error(
- `Invalid entity ID at position ${index}: ${entityConf.entity}`
- );
- }
-
- return entityConf;
- });
-}
+// Parse array of entity objects from config
+import isValidEntityId from "../../../common/entity/valid_entity_id";
+
+export default function processConfigEntities(entities) {
+ if (!entities || !Array.isArray(entities)) {
+ throw new Error("Entities need to be an array");
+ }
+
+ return entities.map((entityConf, index) => {
+ if (
+ typeof entityConf === "object" &&
+ !Array.isArray(entityConf) &&
+ entityConf.type
+ ) {
+ return entityConf;
+ }
+
+ if (typeof entityConf === "string") {
+ entityConf = { entity: entityConf };
+ } else if (typeof entityConf === "object" && !Array.isArray(entityConf)) {
+ if (!entityConf.entity) {
+ throw new Error(
+ `Entity object at position ${index} is missing entity field.`
+ );
+ }
+ } else {
+ throw new Error(`Invalid entity specified at position ${index}.`);
+ }
+
+ if (!isValidEntityId(entityConf.entity)) {
+ throw new Error(
+ `Invalid entity ID at position ${index}: ${entityConf.entity}`
+ );
+ }
+
+ return entityConf;
+ });
+}
diff --git a/src/panels/lovelace/components/hui-card-options.ts b/src/panels/lovelace/components/hui-card-options.ts
index 7b18f8119ef8..1e7b343c1238 100644
--- a/src/panels/lovelace/components/hui-card-options.ts
+++ b/src/panels/lovelace/components/hui-card-options.ts
@@ -1,68 +1,68 @@
-import "@polymer/paper-button/paper-button.js";
-import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element";
-import { fireEvent } from "../../../common/dom/fire_event.js";
-import { HomeAssistant } from "../../../types.js";
-
-let registeredDialog = false;
-
-export class HuiCardOptions extends LitElement {
- public cardId?: string;
- protected hass?: HomeAssistant;
-
- static get properties(): PropertyDeclarations {
- return {
- hass: {},
- };
- }
-
- public connectedCallback() {
- super.connectedCallback();
- if (!registeredDialog) {
- registeredDialog = true;
- fireEvent(this, "register-dialog", {
- dialogShowEvent: "show-edit-card",
- dialogTag: "hui-dialog-edit-card",
- dialogImport: () => import("../editor/hui-dialog-edit-card"),
- });
- }
- }
-
- protected render() {
- return html`
-
-
-
- `;
- }
- private _editCard() {
- fireEvent(this, "show-edit-card", {
- hass: this.hass,
- cardId: this.cardId,
- reloadLovelace: () => fireEvent(this, "config-refresh"),
- });
- }
-}
-
-declare global {
- interface HTMLElementTagNameMap {
- "hui-card-options": HuiCardOptions;
- }
-}
-
-customElements.define("hui-card-options", HuiCardOptions);
+import "@polymer/paper-button/paper-button";
+import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element";
+import { fireEvent } from "../../../common/dom/fire_event";
+import { HomeAssistant } from "../../../types";
+
+let registeredDialog = false;
+
+export class HuiCardOptions extends LitElement {
+ public cardId?: string;
+ protected hass?: HomeAssistant;
+
+ static get properties(): PropertyDeclarations {
+ return {
+ hass: {},
+ };
+ }
+
+ public connectedCallback() {
+ super.connectedCallback();
+ if (!registeredDialog) {
+ registeredDialog = true;
+ fireEvent(this, "register-dialog", {
+ dialogShowEvent: "show-edit-card",
+ dialogTag: "hui-dialog-edit-card",
+ dialogImport: () => import("../editor/hui-dialog-edit-card"),
+ });
+ }
+ }
+
+ protected render() {
+ return html`
+
+
+
+ `;
+ }
+ private _editCard() {
+ fireEvent(this, "show-edit-card", {
+ hass: this.hass,
+ cardId: this.cardId,
+ reloadLovelace: () => fireEvent(this, "config-refresh"),
+ });
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ "hui-card-options": HuiCardOptions;
+ }
+}
+
+customElements.define("hui-card-options", HuiCardOptions);
diff --git a/src/panels/lovelace/components/hui-entities-toggle.js b/src/panels/lovelace/components/hui-entities-toggle.js
index 6df9e1fb7e92..7055c2a962ff 100644
--- a/src/panels/lovelace/components/hui-entities-toggle.js
+++ b/src/panels/lovelace/components/hui-entities-toggle.js
@@ -1,57 +1,57 @@
-import { html } from "@polymer/polymer/lib/utils/html-tag.js";
-import { PolymerElement } from "@polymer/polymer/polymer-element.js";
-import "@polymer/paper-toggle-button/paper-toggle-button.js";
-
-import { DOMAINS_TOGGLE } from "../../../common/const.js";
-import turnOnOffEntities from "../common/entity/turn-on-off-entities.js";
-
-class HuiEntitiesToggle extends PolymerElement {
- static get template() {
- return html`
-
-
-
-
-`;
- }
-
- static get properties() {
- return {
- hass: Object,
- entities: Array,
- _toggleEntities: {
- type: Array,
- computed: "_computeToggleEntities(hass, entities)",
- },
- };
- }
-
- _computeToggleEntities(hass, entityIds) {
- return entityIds.filter(
- (entityId) =>
- entityId in hass.states && DOMAINS_TOGGLE.has(entityId.split(".", 1)[0])
- );
- }
-
- _computeIsChecked(hass, entityIds) {
- return entityIds.some((entityId) => hass.states[entityId].state === "on");
- }
-
- _callService(ev) {
- const turnOn = ev.target.checked;
- turnOnOffEntities(this.hass, this._toggleEntities, turnOn);
- }
-}
-
-customElements.define("hui-entities-toggle", HuiEntitiesToggle);
+import { html } from "@polymer/polymer/lib/utils/html-tag";
+import { PolymerElement } from "@polymer/polymer/polymer-element";
+import "@polymer/paper-toggle-button/paper-toggle-button";
+
+import { DOMAINS_TOGGLE } from "../../../common/const";
+import turnOnOffEntities from "../common/entity/turn-on-off-entities";
+
+class HuiEntitiesToggle extends PolymerElement {
+ static get template() {
+ return html`
+
+
+
+
+`;
+ }
+
+ static get properties() {
+ return {
+ hass: Object,
+ entities: Array,
+ _toggleEntities: {
+ type: Array,
+ computed: "_computeToggleEntities(hass, entities)",
+ },
+ };
+ }
+
+ _computeToggleEntities(hass, entityIds) {
+ return entityIds.filter(
+ (entityId) =>
+ entityId in hass.states && DOMAINS_TOGGLE.has(entityId.split(".", 1)[0])
+ );
+ }
+
+ _computeIsChecked(hass, entityIds) {
+ return entityIds.some((entityId) => hass.states[entityId].state === "on");
+ }
+
+ _callService(ev) {
+ const turnOn = ev.target.checked;
+ turnOnOffEntities(this.hass, this._toggleEntities, turnOn);
+ }
+}
+
+customElements.define("hui-entities-toggle", HuiEntitiesToggle);
diff --git a/src/panels/lovelace/components/hui-generic-entity-row.js b/src/panels/lovelace/components/hui-generic-entity-row.js
index 6a14507b7000..092a065c224c 100644
--- a/src/panels/lovelace/components/hui-generic-entity-row.js
+++ b/src/panels/lovelace/components/hui-generic-entity-row.js
@@ -1,137 +1,137 @@
-import { html } from "@polymer/polymer/lib/utils/html-tag.js";
-import { PolymerElement } from "@polymer/polymer/polymer-element.js";
-
-import "../../../components/entity/state-badge.js";
-import "../../../components/ha-relative-time.js";
-import "../../../components/ha-icon.js";
-
-import computeStateName from "../../../common/entity/compute_state_name.js";
-
-class HuiGenericEntityRow extends PolymerElement {
- static get template() {
- return html`
- ${this.styleTemplate}
-
- ${this.stateBadgeTemplate}
-
- ${this.infoTemplate}
-
-
-
-
-
- Entity not available: [[config.entity]]
-
-
- `;
- }
-
- static get styleTemplate() {
- return html`
-
- `;
- }
-
- static get stateBadgeTemplate() {
- return html`
-
- `;
- }
-
- static get infoTemplate() {
- return html`
-
- [[_computeName(config.name, _stateObj)]]
-
-
-
- [[_stateObj.entity_id]]
-
-
-
-
-
-
-
-
-
-
- `;
- }
-
- static get properties() {
- return {
- hass: Object,
- config: Object,
- _stateObj: {
- type: Object,
- computed: "_computeStateObj(hass.states, config.entity)",
- },
- showSecondary: {
- type: Boolean,
- value: true,
- },
- };
- }
-
- _equals(a, b) {
- return a === b;
- }
-
- _computeStateObj(states, entityId) {
- return states && entityId in states ? states[entityId] : null;
- }
-
- _computeName(name, stateObj) {
- return name || computeStateName(stateObj);
- }
-}
-customElements.define("hui-generic-entity-row", HuiGenericEntityRow);
+import { html } from "@polymer/polymer/lib/utils/html-tag";
+import { PolymerElement } from "@polymer/polymer/polymer-element";
+
+import "../../../components/entity/state-badge";
+import "../../../components/ha-relative-time";
+import "../../../components/ha-icon";
+
+import computeStateName from "../../../common/entity/compute_state_name";
+
+class HuiGenericEntityRow extends PolymerElement {
+ static get template() {
+ return html`
+ ${this.styleTemplate}
+
+ ${this.stateBadgeTemplate}
+
+ ${this.infoTemplate}
+
+
+
+
+
+ Entity not available: [[config.entity]]
+
+
+ `;
+ }
+
+ static get styleTemplate() {
+ return html`
+
+ `;
+ }
+
+ static get stateBadgeTemplate() {
+ return html`
+
+ `;
+ }
+
+ static get infoTemplate() {
+ return html`
+
+ [[_computeName(config.name, _stateObj)]]
+
+
+
+ [[_stateObj.entity_id]]
+
+
+
+
+
+
+
+
+
+
+ `;
+ }
+
+ static get properties() {
+ return {
+ hass: Object,
+ config: Object,
+ _stateObj: {
+ type: Object,
+ computed: "_computeStateObj(hass.states, config.entity)",
+ },
+ showSecondary: {
+ type: Boolean,
+ value: true,
+ },
+ };
+ }
+
+ _equals(a, b) {
+ return a === b;
+ }
+
+ _computeStateObj(states, entityId) {
+ return states && entityId in states ? states[entityId] : null;
+ }
+
+ _computeName(name, stateObj) {
+ return name || computeStateName(stateObj);
+ }
+}
+customElements.define("hui-generic-entity-row", HuiGenericEntityRow);
diff --git a/src/panels/lovelace/components/hui-image.js b/src/panels/lovelace/components/hui-image.js
index bbaba4ce91ba..7164da4c4fb2 100644
--- a/src/panels/lovelace/components/hui-image.js
+++ b/src/panels/lovelace/components/hui-image.js
@@ -1,198 +1,198 @@
-import { html } from "@polymer/polymer/lib/utils/html-tag.js";
-import { PolymerElement } from "@polymer/polymer/polymer-element.js";
-import "@polymer/paper-toggle-button/paper-toggle-button.js";
-
-import { STATES_OFF } from "../../../common/const.js";
-import LocalizeMixin from "../../../mixins/localize-mixin.js";
-
-import parseAspectRatio from "../../../common/util/parse-aspect-ratio.js";
-
-const UPDATE_INTERVAL = 10000;
-const DEFAULT_FILTER = "grayscale(100%)";
-
-/*
- * @appliesMixin LocalizeMixin
- */
-class HuiImage extends LocalizeMixin(PolymerElement) {
- static get template() {
- return html`
- ${this.styleTemplate}
-
-
-
-
- `;
- }
-
- static get styleTemplate() {
- return html`
-
- `;
- }
-
- static get properties() {
- return {
- hass: {
- type: Object,
- observer: "_hassChanged",
- },
- entity: String,
- image: String,
- stateImage: Object,
- cameraImage: String,
- aspectRatio: String,
- filter: String,
- stateFilter: Object,
- _imageSrc: String,
- };
- }
-
- static get observers() {
- return ["_configChanged(image, stateImage, cameraImage, aspectRatio)"];
- }
-
- connectedCallback() {
- super.connectedCallback();
- if (this.cameraImage) {
- this.timer = setInterval(
- () => this._updateCameraImageSrc(),
- UPDATE_INTERVAL
- );
- }
- }
-
- disconnectedCallback() {
- super.disconnectedCallback();
- clearInterval(this.timer);
- }
-
- _configChanged(image, stateImage, cameraImage, aspectRatio) {
- const ratio = parseAspectRatio(aspectRatio);
-
- if (ratio && ratio.w > 0 && ratio.h > 0) {
- this.$.wrapper.style.paddingBottom = `${(
- (100 * ratio.h) /
- ratio.w
- ).toFixed(2)}%`;
- this.$.wrapper.classList.add("ratio");
- }
-
- if (cameraImage) {
- this._updateCameraImageSrc();
- } else if (image && !stateImage) {
- this._imageSrc = image;
- }
- }
-
- _onImageError() {
- this._imageSrc = null;
- this.$.image.classList.add("hidden");
- if (!this.$.wrapper.classList.contains("ratio")) {
- this.$.brokenImage.style.setProperty(
- "height",
- `${this._lastImageHeight || "100"}px`
- );
- }
- this.$.brokenImage.classList.remove("hidden");
- }
-
- _onImageLoad() {
- this.$.image.classList.remove("hidden");
- this.$.brokenImage.classList.add("hidden");
- if (!this.$.wrapper.classList.contains("ratio")) {
- this._lastImageHeight = this.$.image.offsetHeight;
- }
- }
-
- _hassChanged(hass) {
- if (this.cameraImage || !this.entity) {
- return;
- }
-
- const stateObj = hass.states[this.entity];
- const newState = !stateObj ? "unavailable" : stateObj.state;
-
- if (newState === this._currentState) return;
- this._currentState = newState;
-
- this._updateStateImage();
- this._updateStateFilter(stateObj);
- }
-
- _updateStateImage() {
- if (!this.stateImage) {
- this._imageFallback = true;
- return;
- }
- const stateImg = this.stateImage[this._currentState];
- this._imageSrc = stateImg || this.image;
- this._imageFallback = !stateImg;
- }
-
- _updateStateFilter(stateObj) {
- let filter;
- if (!this.stateFilter) {
- filter = this.filter;
- } else {
- filter = this.stateFilter[this._currentState] || this.filter;
- }
-
- const isOff = !stateObj || STATES_OFF.includes(stateObj.state);
- this.$.image.style.filter =
- filter || (isOff && this._imageFallback && DEFAULT_FILTER) || "";
- }
-
- async _updateCameraImageSrc() {
- try {
- const { content_type: contentType, content } = await this.hass.callWS({
- type: "camera_thumbnail",
- entity_id: this.cameraImage,
- });
- this._imageSrc = `data:${contentType};base64, ${content}`;
- this._onImageLoad();
- } catch (err) {
- this._onImageError();
- }
- }
-}
-
-customElements.define("hui-image", HuiImage);
+import { html } from "@polymer/polymer/lib/utils/html-tag";
+import { PolymerElement } from "@polymer/polymer/polymer-element";
+import "@polymer/paper-toggle-button/paper-toggle-button";
+
+import { STATES_OFF } from "../../../common/const";
+import LocalizeMixin from "../../../mixins/localize-mixin";
+
+import parseAspectRatio from "../../../common/util/parse-aspect-ratio";
+
+const UPDATE_INTERVAL = 10000;
+const DEFAULT_FILTER = "grayscale(100%)";
+
+/*
+ * @appliesMixin LocalizeMixin
+ */
+class HuiImage extends LocalizeMixin(PolymerElement) {
+ static get template() {
+ return html`
+ ${this.styleTemplate}
+
+
+
+
+ `;
+ }
+
+ static get styleTemplate() {
+ return html`
+
+ `;
+ }
+
+ static get properties() {
+ return {
+ hass: {
+ type: Object,
+ observer: "_hassChanged",
+ },
+ entity: String,
+ image: String,
+ stateImage: Object,
+ cameraImage: String,
+ aspectRatio: String,
+ filter: String,
+ stateFilter: Object,
+ _imageSrc: String,
+ };
+ }
+
+ static get observers() {
+ return ["_configChanged(image, stateImage, cameraImage, aspectRatio)"];
+ }
+
+ connectedCallback() {
+ super.connectedCallback();
+ if (this.cameraImage) {
+ this.timer = setInterval(
+ () => this._updateCameraImageSrc(),
+ UPDATE_INTERVAL
+ );
+ }
+ }
+
+ disconnectedCallback() {
+ super.disconnectedCallback();
+ clearInterval(this.timer);
+ }
+
+ _configChanged(image, stateImage, cameraImage, aspectRatio) {
+ const ratio = parseAspectRatio(aspectRatio);
+
+ if (ratio && ratio.w > 0 && ratio.h > 0) {
+ this.$.wrapper.style.paddingBottom = `${(
+ (100 * ratio.h) /
+ ratio.w
+ ).toFixed(2)}%`;
+ this.$.wrapper.classList.add("ratio");
+ }
+
+ if (cameraImage) {
+ this._updateCameraImageSrc();
+ } else if (image && !stateImage) {
+ this._imageSrc = image;
+ }
+ }
+
+ _onImageError() {
+ this._imageSrc = null;
+ this.$.image.classList.add("hidden");
+ if (!this.$.wrapper.classList.contains("ratio")) {
+ this.$.brokenImage.style.setProperty(
+ "height",
+ `${this._lastImageHeight || "100"}px`
+ );
+ }
+ this.$.brokenImage.classList.remove("hidden");
+ }
+
+ _onImageLoad() {
+ this.$.image.classList.remove("hidden");
+ this.$.brokenImage.classList.add("hidden");
+ if (!this.$.wrapper.classList.contains("ratio")) {
+ this._lastImageHeight = this.$.image.offsetHeight;
+ }
+ }
+
+ _hassChanged(hass) {
+ if (this.cameraImage || !this.entity) {
+ return;
+ }
+
+ const stateObj = hass.states[this.entity];
+ const newState = !stateObj ? "unavailable" : stateObj.state;
+
+ if (newState === this._currentState) return;
+ this._currentState = newState;
+
+ this._updateStateImage();
+ this._updateStateFilter(stateObj);
+ }
+
+ _updateStateImage() {
+ if (!this.stateImage) {
+ this._imageFallback = true;
+ return;
+ }
+ const stateImg = this.stateImage[this._currentState];
+ this._imageSrc = stateImg || this.image;
+ this._imageFallback = !stateImg;
+ }
+
+ _updateStateFilter(stateObj) {
+ let filter;
+ if (!this.stateFilter) {
+ filter = this.filter;
+ } else {
+ filter = this.stateFilter[this._currentState] || this.filter;
+ }
+
+ const isOff = !stateObj || STATES_OFF.includes(stateObj.state);
+ this.$.image.style.filter =
+ filter || (isOff && this._imageFallback && DEFAULT_FILTER) || "";
+ }
+
+ async _updateCameraImageSrc() {
+ try {
+ const { content_type: contentType, content } = await this.hass.callWS({
+ type: "camera_thumbnail",
+ entity_id: this.cameraImage,
+ });
+ this._imageSrc = `data:${contentType};base64, ${content}`;
+ this._onImageLoad();
+ } catch (err) {
+ this._onImageError();
+ }
+ }
+}
+
+customElements.define("hui-image", HuiImage);
diff --git a/src/panels/lovelace/components/notifications/hui-configurator-notification-item.js b/src/panels/lovelace/components/notifications/hui-configurator-notification-item.js
index 3d6c670f0a92..9924c40eb800 100644
--- a/src/panels/lovelace/components/notifications/hui-configurator-notification-item.js
+++ b/src/panels/lovelace/components/notifications/hui-configurator-notification-item.js
@@ -1,62 +1,62 @@
-import "@polymer/paper-button/paper-button.js";
-import "@polymer/paper-icon-button/paper-icon-button.js";
-
-import { html } from "@polymer/polymer/lib/utils/html-tag.js";
-import { PolymerElement } from "@polymer/polymer/polymer-element.js";
-
-import "./hui-notification-item-template.js";
-
-import EventsMixin from "../../../../mixins/events-mixin.js";
-import LocalizeMixin from "../../../../mixins/localize-mixin.js";
-
-/*
- * @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
-);
+import "@polymer/paper-button/paper-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-notification-drawer.js b/src/panels/lovelace/components/notifications/hui-notification-drawer.js
index c07d3ecbc115..62ce12b5af88 100644
--- a/src/panels/lovelace/components/notifications/hui-notification-drawer.js
+++ b/src/panels/lovelace/components/notifications/hui-notification-drawer.js
@@ -1,175 +1,175 @@
-import "@polymer/paper-button/paper-button.js";
-import "@polymer/paper-icon-button/paper-icon-button.js";
-import "@polymer/app-layout/app-toolbar/app-toolbar.js";
-
-import { html } from "@polymer/polymer/lib/utils/html-tag.js";
-import { PolymerElement } from "@polymer/polymer/polymer-element.js";
-
-import "./hui-notification-item.js";
-
-import EventsMixin from "../../../../mixins/events-mixin.js";
-import LocalizeMixin from "../../../../mixins/localize-mixin.js";
-
-/*
- * @appliesMixin EventsMixin
- * @appliesMixin LocalizeMixin
- */
-export class HuiNotificationDrawer extends EventsMixin(
- LocalizeMixin(PolymerElement)
-) {
- static get template() {
- return html`
-
-
-
-
- [[localize('ui.notification_drawer.title')]]
-
-
-
-
-
-
-
-
-
-
-
-
-
- [[localize('ui.notification_drawer.empty')]]
-
-
-
- `;
- }
-
- static get properties() {
- return {
- hass: Object,
- narrow: {
- type: Boolean,
- reflectToAttribute: true,
- },
- open: {
- type: Boolean,
- notify: true,
- observer: "_openChanged",
- },
- hidden: {
- type: Boolean,
- value: true,
- reflectToAttribute: true,
- },
- notifications: {
- type: Array,
- value: [],
- },
- };
- }
-
- _closeDrawer(ev) {
- ev.stopPropagation();
- this.open = false;
- }
-
- _empty(notifications) {
- return notifications.length === 0;
- }
-
- _openChanged(open) {
- clearTimeout(this._openTimer);
- if (open) {
- // Render closed then animate open
- this.hidden = false;
- this._openTimer = setTimeout(() => {
- this.classList.add("open");
- }, 50);
- } else {
- // Animate closed then hide
- this.classList.remove("open");
- this._openTimer = setTimeout(() => {
- this.hidden = true;
- }, 250);
- }
- }
-}
-customElements.define("hui-notification-drawer", HuiNotificationDrawer);
+import "@polymer/paper-button/paper-button";
+import "@polymer/paper-icon-button/paper-icon-button";
+import "@polymer/app-layout/app-toolbar/app-toolbar";
+
+import { html } from "@polymer/polymer/lib/utils/html-tag";
+import { PolymerElement } from "@polymer/polymer/polymer-element";
+
+import "./hui-notification-item";
+
+import EventsMixin from "../../../../mixins/events-mixin";
+import LocalizeMixin from "../../../../mixins/localize-mixin";
+
+/*
+ * @appliesMixin EventsMixin
+ * @appliesMixin LocalizeMixin
+ */
+export class HuiNotificationDrawer extends EventsMixin(
+ LocalizeMixin(PolymerElement)
+) {
+ static get template() {
+ return html`
+
+
+
+
+ [[localize('ui.notification_drawer.title')]]
+
+
+
+
+
+
+
+
+
+
+
+
+
+ [[localize('ui.notification_drawer.empty')]]
+
+
+
+ `;
+ }
+
+ static get properties() {
+ return {
+ hass: Object,
+ narrow: {
+ type: Boolean,
+ reflectToAttribute: true,
+ },
+ open: {
+ type: Boolean,
+ notify: true,
+ observer: "_openChanged",
+ },
+ hidden: {
+ type: Boolean,
+ value: true,
+ reflectToAttribute: true,
+ },
+ notifications: {
+ type: Array,
+ value: [],
+ },
+ };
+ }
+
+ _closeDrawer(ev) {
+ ev.stopPropagation();
+ this.open = false;
+ }
+
+ _empty(notifications) {
+ return notifications.length === 0;
+ }
+
+ _openChanged(open) {
+ clearTimeout(this._openTimer);
+ if (open) {
+ // Render closed then animate open
+ this.hidden = false;
+ this._openTimer = setTimeout(() => {
+ this.classList.add("open");
+ }, 50);
+ } else {
+ // Animate closed then hide
+ this.classList.remove("open");
+ this._openTimer = setTimeout(() => {
+ this.hidden = true;
+ }, 250);
+ }
+ }
+}
+customElements.define("hui-notification-drawer", HuiNotificationDrawer);
diff --git a/src/panels/lovelace/components/notifications/hui-notification-item-template.js b/src/panels/lovelace/components/notifications/hui-notification-item-template.js
index 995b0e486993..de9d8c80856c 100644
--- a/src/panels/lovelace/components/notifications/hui-notification-item-template.js
+++ b/src/panels/lovelace/components/notifications/hui-notification-item-template.js
@@ -1,49 +1,49 @@
-import "@polymer/paper-button/paper-button.js";
-import "@polymer/paper-icon-button/paper-icon-button.js";
-
-import { html } from "@polymer/polymer/lib/utils/html-tag.js";
-import { PolymerElement } from "@polymer/polymer/polymer-element.js";
-
-import "../../../../components/ha-card.js";
-
-export class HuiNotificationItemTemplate extends PolymerElement {
- static get template() {
- return html`
-
-
-
-
-
-
-
-
-
-
- `;
- }
-}
-customElements.define(
- "hui-notification-item-template",
- HuiNotificationItemTemplate
-);
+import "@polymer/paper-button/paper-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 "../../../../components/ha-card";
+
+export class HuiNotificationItemTemplate extends PolymerElement {
+ static get template() {
+ return html`
+
+
+
+
+
+
+
+
+
+
+ `;
+ }
+}
+customElements.define(
+ "hui-notification-item-template",
+ HuiNotificationItemTemplate
+);
diff --git a/src/panels/lovelace/components/notifications/hui-notification-item.js b/src/panels/lovelace/components/notifications/hui-notification-item.js
index c9ddb15c92a7..c54664c6babb 100644
--- a/src/panels/lovelace/components/notifications/hui-notification-item.js
+++ b/src/panels/lovelace/components/notifications/hui-notification-item.js
@@ -1,35 +1,35 @@
-import { PolymerElement } from "@polymer/polymer/polymer-element.js";
-import computeDomain from "../../../../common/entity/compute_domain.js";
-
-import "./hui-configurator-notification-item.js";
-import "./hui-persistent-notification-item.js";
-
-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);
+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-notifications-button.js b/src/panels/lovelace/components/notifications/hui-notifications-button.js
index ba73b4c88db5..5c26714296c3 100644
--- a/src/panels/lovelace/components/notifications/hui-notifications-button.js
+++ b/src/panels/lovelace/components/notifications/hui-notifications-button.js
@@ -1,62 +1,62 @@
-import "@polymer/paper-button/paper-button.js";
-import "@polymer/paper-icon-button/paper-icon-button.js";
-import "@polymer/app-layout/app-toolbar/app-toolbar.js";
-
-import { html } from "@polymer/polymer/lib/utils/html-tag.js";
-import { PolymerElement } from "@polymer/polymer/polymer-element.js";
-
-import EventsMixin from "../../../../mixins/events-mixin.js";
-
-/*
- * @appliesMixin EventsMixin
- */
-export class HuiNotificationsButton extends EventsMixin(PolymerElement) {
- static get template() {
- return html`
-
-
-
- `;
- }
-
- static get properties() {
- return {
- notificationsOpen: {
- type: Boolean,
- notify: true,
- },
- notifications: {
- type: Array,
- value: [],
- },
- };
- }
-
- _clicked() {
- this.notificationsOpen = true;
- }
-
- _hasNotifications(notifications) {
- return notifications.length > 0;
- }
-}
-customElements.define("hui-notifications-button", HuiNotificationsButton);
+import "@polymer/paper-button/paper-button";
+import "@polymer/paper-icon-button/paper-icon-button";
+import "@polymer/app-layout/app-toolbar/app-toolbar";
+
+import { html } from "@polymer/polymer/lib/utils/html-tag";
+import { PolymerElement } from "@polymer/polymer/polymer-element";
+
+import EventsMixin from "../../../../mixins/events-mixin";
+
+/*
+ * @appliesMixin EventsMixin
+ */
+export class HuiNotificationsButton extends EventsMixin(PolymerElement) {
+ static get template() {
+ return html`
+
+
+
+ `;
+ }
+
+ static get properties() {
+ return {
+ notificationsOpen: {
+ type: Boolean,
+ notify: true,
+ },
+ notifications: {
+ type: Array,
+ value: [],
+ },
+ };
+ }
+
+ _clicked() {
+ this.notificationsOpen = true;
+ }
+
+ _hasNotifications(notifications) {
+ return notifications.length > 0;
+ }
+}
+customElements.define("hui-notifications-button", HuiNotificationsButton);
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 d9dc8a52dd14..86bf11d006db 100644
--- a/src/panels/lovelace/components/notifications/hui-persistent-notification-item.js
+++ b/src/panels/lovelace/components/notifications/hui-persistent-notification-item.js
@@ -1,89 +1,89 @@
-import "@polymer/paper-button/paper-button.js";
-import "@polymer/paper-icon-button/paper-icon-button.js";
-import "@polymer/paper-tooltip/paper-tooltip.js";
-
-import { html } from "@polymer/polymer/lib/utils/html-tag.js";
-import { PolymerElement } from "@polymer/polymer/polymer-element.js";
-
-import "../../../../components/ha-relative-time.js";
-import "../../../../components/ha-markdown.js";
-import "./hui-notification-item-template.js";
-
-import LocalizeMixin from "../../../../mixins/localize-mixin.js";
-
-/*
- * @appliesMixin LocalizeMixin
- */
-export class HuiPersistentNotificationItem extends LocalizeMixin(
- PolymerElement
-) {
- static get template() {
- return html`
-
-
- [[_computeTitle(notification)]]
-
-
-
-
-
-
- [[_computeTooltip(hass, notification)]]
-
-
-
- [[localize('ui.card.persistent_notification.dismiss')]]
-
- `;
- }
-
- static get properties() {
- return {
- hass: Object,
- notification: Object,
- };
- }
-
- _handleDismiss() {
- this.hass.callService("persistent_notification", "dismiss", {
- notification_id: this.notification.notification_id,
- });
- }
-
- _computeTitle(notification) {
- return notification.title || notification.notification_id;
- }
-
- _computeTooltip(hass, notification) {
- if (!hass || !notification) return null;
-
- const d = new Date(notification.created_at);
- return d.toLocaleDateString(hass.language, {
- year: "numeric",
- month: "short",
- day: "numeric",
- minute: "numeric",
- hour: "numeric",
- });
- }
-}
-customElements.define(
- "hui-persistent_notification-notification-item",
- HuiPersistentNotificationItem
-);
+import "@polymer/paper-button/paper-button";
+import "@polymer/paper-icon-button/paper-icon-button";
+import "@polymer/paper-tooltip/paper-tooltip";
+
+import { html } from "@polymer/polymer/lib/utils/html-tag";
+import { PolymerElement } from "@polymer/polymer/polymer-element";
+
+import "../../../../components/ha-relative-time";
+import "../../../../components/ha-markdown";
+import "./hui-notification-item-template";
+
+import LocalizeMixin from "../../../../mixins/localize-mixin";
+
+/*
+ * @appliesMixin LocalizeMixin
+ */
+export class HuiPersistentNotificationItem extends LocalizeMixin(
+ PolymerElement
+) {
+ static get template() {
+ return html`
+
+
+ [[_computeTitle(notification)]]
+
+
+
+
+
+
+ [[_computeTooltip(hass, notification)]]
+
+
+
+ [[localize('ui.card.persistent_notification.dismiss')]]
+
+ `;
+ }
+
+ static get properties() {
+ return {
+ hass: Object,
+ notification: Object,
+ };
+ }
+
+ _handleDismiss() {
+ this.hass.callService("persistent_notification", "dismiss", {
+ notification_id: this.notification.notification_id,
+ });
+ }
+
+ _computeTitle(notification) {
+ return notification.title || notification.notification_id;
+ }
+
+ _computeTooltip(hass, notification) {
+ if (!hass || !notification) return null;
+
+ const d = new Date(notification.created_at);
+ return d.toLocaleDateString(hass.language, {
+ year: "numeric",
+ month: "short",
+ day: "numeric",
+ minute: "numeric",
+ hour: "numeric",
+ });
+ }
+}
+customElements.define(
+ "hui-persistent_notification-notification-item",
+ HuiPersistentNotificationItem
+);
diff --git a/src/panels/lovelace/editor/hui-dialog-edit-card.ts b/src/panels/lovelace/editor/hui-dialog-edit-card.ts
index adae0b17e8b2..c674b156c9bd 100644
--- a/src/panels/lovelace/editor/hui-dialog-edit-card.ts
+++ b/src/panels/lovelace/editor/hui-dialog-edit-card.ts
@@ -1,122 +1,122 @@
-import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element";
-import { fireEvent } from "../../../common/dom/fire_event.js";
-
-import "@polymer/paper-button/paper-button.js";
-import "@polymer/paper-input/paper-textarea.js";
-import "@polymer/paper-dialog-scrollable/paper-dialog-scrollable.js";
-import "@polymer/paper-dialog/paper-dialog.js";
-// 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.js";
-import { HomeAssistant } from "../../../types";
-import { getCardConfig, updateCardConfig } from "../common/data";
-
-import "./hui-yaml-editor";
-import "./hui-yaml-card-preview";
-// This is not a duplicate import, one is for types, one is for element.
-// tslint:disable-next-line
-import { HuiYAMLCardPreview } from "./hui-yaml-card-preview";
-
-export class HuiDialogEditCard extends LitElement {
- protected hass?: HomeAssistant;
- private _cardId?: string;
- private _cardConfig?: string;
- private _reloadLovelace?: () => void;
-
- static get properties(): PropertyDeclarations {
- return {
- hass: {},
- cardId: {
- type: Number,
- },
- _cardConfig: {},
- _dialogClosedCallback: {},
- };
- }
-
- public async showDialog({ hass, cardId, reloadLovelace }) {
- this.hass = hass;
- this._cardId = cardId;
- this._reloadLovelace = reloadLovelace;
- this._cardConfig = "";
- this._loadConfig();
- // Wait till dialog is rendered.
- await this.updateComplete;
- this._dialog.open();
- }
-
- private get _dialog(): PaperDialogElement {
- return this.shadowRoot!.querySelector("paper-dialog")!;
- }
-
- private get _previewEl(): HuiYAMLCardPreview {
- return this.shadowRoot!.querySelector("hui-yaml-card-preview")!;
- }
-
- protected render() {
- return html`
-
-
- Card Configuration
-
-
-
-
-
-
- `;
- }
-
- private _handleYamlChanged(ev) {
- this._previewEl.yaml = ev.detail.yaml;
- }
-
- private _closeDialog() {
- this._dialog.close();
- }
-
- private async _loadConfig() {
- this._cardConfig = await getCardConfig(this.hass!, this._cardId!);
- await this.updateComplete;
- // This will center the dialog with the updated config
- fireEvent(this._dialog, "iron-resize");
- }
-
- private async _updateConfig() {
- const newCardConfig = this.shadowRoot!.querySelector("hui-yaml-editor")!
- .yaml;
-
- if (this._cardConfig === newCardConfig) {
- this._dialog.close();
- return;
- }
- try {
- await updateCardConfig(this.hass!, this._cardId!, newCardConfig);
- this._dialog.close();
- this._reloadLovelace!();
- } catch (err) {
- alert(`Saving failed: ${err.reason}`);
- }
- }
-}
-
-declare global {
- interface HTMLElementTagNameMap {
- "hui-dialog-edit-card": HuiDialogEditCard;
- }
-}
-
-customElements.define("hui-dialog-edit-card", HuiDialogEditCard);
+import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element";
+import { fireEvent } from "../../../common/dom/fire_event";
+
+import "@polymer/paper-button/paper-button";
+import "@polymer/paper-input/paper-textarea";
+import "@polymer/paper-dialog-scrollable/paper-dialog-scrollable";
+import "@polymer/paper-dialog/paper-dialog";
+// This is not a duplicate import, one is for types, one is for element.
+// tslint:disable-next-line
+import { PaperDialogElement } from "@polymer/paper-dialog/paper-dialog";
+import { HomeAssistant } from "../../../types";
+import { getCardConfig, updateCardConfig } from "../common/data";
+
+import "./hui-yaml-editor";
+import "./hui-yaml-card-preview";
+// This is not a duplicate import, one is for types, one is for element.
+// tslint:disable-next-line
+import { HuiYAMLCardPreview } from "./hui-yaml-card-preview";
+
+export class HuiDialogEditCard extends LitElement {
+ protected hass?: HomeAssistant;
+ private _cardId?: string;
+ private _cardConfig?: string;
+ private _reloadLovelace?: () => void;
+
+ static get properties(): PropertyDeclarations {
+ return {
+ hass: {},
+ cardId: {
+ type: Number,
+ },
+ _cardConfig: {},
+ _dialogClosedCallback: {},
+ };
+ }
+
+ public async showDialog({ hass, cardId, reloadLovelace }) {
+ this.hass = hass;
+ this._cardId = cardId;
+ this._reloadLovelace = reloadLovelace;
+ this._cardConfig = "";
+ this._loadConfig();
+ // Wait till dialog is rendered.
+ await this.updateComplete;
+ this._dialog.open();
+ }
+
+ private get _dialog(): PaperDialogElement {
+ return this.shadowRoot!.querySelector("paper-dialog")!;
+ }
+
+ private get _previewEl(): HuiYAMLCardPreview {
+ return this.shadowRoot!.querySelector("hui-yaml-card-preview")!;
+ }
+
+ protected render() {
+ return html`
+
+
+ Card Configuration
+
+
+
+
+
+
+ `;
+ }
+
+ private _handleYamlChanged(ev) {
+ this._previewEl.yaml = ev.detail.yaml;
+ }
+
+ private _closeDialog() {
+ this._dialog.close();
+ }
+
+ private async _loadConfig() {
+ this._cardConfig = await getCardConfig(this.hass!, this._cardId!);
+ await this.updateComplete;
+ // This will center the dialog with the updated config
+ fireEvent(this._dialog, "iron-resize");
+ }
+
+ private async _updateConfig() {
+ const newCardConfig = this.shadowRoot!.querySelector("hui-yaml-editor")!
+ .yaml;
+
+ if (this._cardConfig === newCardConfig) {
+ this._dialog.close();
+ return;
+ }
+ try {
+ await updateCardConfig(this.hass!, this._cardId!, newCardConfig);
+ this._dialog.close();
+ this._reloadLovelace!();
+ } catch (err) {
+ alert(`Saving failed: ${err.reason}`);
+ }
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ "hui-dialog-edit-card": HuiDialogEditCard;
+ }
+}
+
+customElements.define("hui-dialog-edit-card", HuiDialogEditCard);
diff --git a/src/panels/lovelace/editor/hui-yaml-card-preview.ts b/src/panels/lovelace/editor/hui-yaml-card-preview.ts
index 67ce7f1d6b23..9eae71466a47 100644
--- a/src/panels/lovelace/editor/hui-yaml-card-preview.ts
+++ b/src/panels/lovelace/editor/hui-yaml-card-preview.ts
@@ -1,52 +1,52 @@
-import yaml from "js-yaml";
-
-import "@polymer/paper-input/paper-textarea.js";
-
-import createCardElement from "../common/create-card-element";
-import createErrorCardConfig from "../common/create-error-card-config";
-import { HomeAssistant } from "../../../types";
-import { LovelaceCard } from "../types";
-
-export class HuiYAMLCardPreview extends HTMLElement {
- private _hass?: HomeAssistant;
-
- set hass(value: HomeAssistant) {
- this._hass = value;
- if (this.lastChild) {
- (this.lastChild as LovelaceCard).hass = value;
- }
- }
-
- set yaml(value: string) {
- if (this.lastChild) {
- this.removeChild(this.lastChild);
- }
-
- if (value === "") {
- return;
- }
-
- let conf;
- try {
- conf = yaml.safeLoad(value);
- } catch (err) {
- conf = createErrorCardConfig(`Invalid YAML: ${err.message}`, undefined);
- }
-
- const element = createCardElement(conf);
-
- if (this._hass) {
- element.hass = this._hass;
- }
-
- this.appendChild(element);
- }
-}
-
-declare global {
- interface HTMLElementTagNameMap {
- "hui-yaml-card-preview": HuiYAMLCardPreview;
- }
-}
-
-customElements.define("hui-yaml-card-preview", HuiYAMLCardPreview);
+import yaml from "js-yaml";
+
+import "@polymer/paper-input/paper-textarea";
+
+import createCardElement from "../common/create-card-element";
+import createErrorCardConfig from "../common/create-error-card-config";
+import { HomeAssistant } from "../../../types";
+import { LovelaceCard } from "../types";
+
+export class HuiYAMLCardPreview extends HTMLElement {
+ private _hass?: HomeAssistant;
+
+ set hass(value: HomeAssistant) {
+ this._hass = value;
+ if (this.lastChild) {
+ (this.lastChild as LovelaceCard).hass = value;
+ }
+ }
+
+ set yaml(value: string) {
+ if (this.lastChild) {
+ this.removeChild(this.lastChild);
+ }
+
+ if (value === "") {
+ return;
+ }
+
+ let conf;
+ try {
+ conf = yaml.safeLoad(value);
+ } catch (err) {
+ conf = createErrorCardConfig(`Invalid YAML: ${err.message}`, undefined);
+ }
+
+ const element = createCardElement(conf);
+
+ if (this._hass) {
+ element.hass = this._hass;
+ }
+
+ this.appendChild(element);
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ "hui-yaml-card-preview": HuiYAMLCardPreview;
+ }
+}
+
+customElements.define("hui-yaml-card-preview", HuiYAMLCardPreview);
diff --git a/src/panels/lovelace/editor/hui-yaml-editor.ts b/src/panels/lovelace/editor/hui-yaml-editor.ts
index 170a08b3cb22..f1bdaa58e07a 100644
--- a/src/panels/lovelace/editor/hui-yaml-editor.ts
+++ b/src/panels/lovelace/editor/hui-yaml-editor.ts
@@ -1,41 +1,41 @@
-import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element";
-import { fireEvent } from "../../../common/dom/fire_event.js";
-
-import "@polymer/paper-input/paper-textarea.js";
-
-export class HuiYAMLEditor extends LitElement {
- public yaml?: string;
-
- static get properties(): PropertyDeclarations {
- return {
- yaml: {},
- };
- }
-
- protected render() {
- return html`
-
-
- `;
- }
-
- private _valueChanged(ev) {
- this.yaml = ev.target.value;
- fireEvent(this, "yaml-changed", { yaml: ev.target.value });
- }
-}
-
-declare global {
- interface HTMLElementTagNameMap {
- "hui-yaml-editor": HuiYAMLEditor;
- }
-}
-
-customElements.define("hui-yaml-editor", HuiYAMLEditor);
+import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element";
+import { fireEvent } from "../../../common/dom/fire_event";
+
+import "@polymer/paper-input/paper-textarea";
+
+export class HuiYAMLEditor extends LitElement {
+ public yaml?: string;
+
+ static get properties(): PropertyDeclarations {
+ return {
+ yaml: {},
+ };
+ }
+
+ protected render() {
+ return html`
+
+
+ `;
+ }
+
+ private _valueChanged(ev) {
+ this.yaml = ev.target.value;
+ fireEvent(this, "yaml-changed", { yaml: ev.target.value });
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ "hui-yaml-editor": HuiYAMLEditor;
+ }
+}
+
+customElements.define("hui-yaml-editor", HuiYAMLEditor);
diff --git a/src/panels/lovelace/elements/hui-icon-element.ts b/src/panels/lovelace/elements/hui-icon-element.ts
index 58c408b8dd9f..3a3fce32a8d7 100644
--- a/src/panels/lovelace/elements/hui-icon-element.ts
+++ b/src/panels/lovelace/elements/hui-icon-element.ts
@@ -1,68 +1,68 @@
-import { html, LitElement } from "@polymer/lit-element";
-
-import "../../../components/ha-icon.js";
-
-import { computeTooltip } from "../common/compute-tooltip";
-import { handleClick } from "../common/handle-click";
-import { longPress } from "../common/directives/long-press-directive";
-import { hassLocalizeLitMixin } from "../../../mixins/lit-localize-mixin";
-import { LovelaceElement, LovelaceElementConfig } from "./types.js";
-import { HomeAssistant } from "../../../types.js";
-import { TemplateResult } from "lit-html";
-
-interface Config extends LovelaceElementConfig {
- icon: string;
-}
-
-export class HuiIconElement extends hassLocalizeLitMixin(LitElement)
- implements LovelaceElement {
- public hass?: HomeAssistant;
- private _config?: Config;
-
- static get properties() {
- return { hass: {}, _config: {} };
- }
-
- public setConfig(config: Config): void {
- if (!config.icon) {
- throw Error("Invalid Configuration: 'icon' required");
- }
-
- this._config = config;
- }
-
- protected render(): TemplateResult {
- if (!this._config) {
- return html``;
- }
-
- return html`
- ${this.renderStyle()}
- handleClick(this, this.hass!, this._config!, false)}"
- @ha-hold="${() => handleClick(this, this.hass!, this._config!, true)}"
- .longPress="${longPress()}"
- >
- `;
- }
-
- private renderStyle(): TemplateResult {
- return html`
-
- `;
- }
-}
-
-declare global {
- interface HTMLElementTagNameMap {
- "hui-icon-element": HuiIconElement;
- }
-}
-
-customElements.define("hui-icon-element", HuiIconElement);
+import { html, LitElement } from "@polymer/lit-element";
+
+import "../../../components/ha-icon";
+
+import { computeTooltip } from "../common/compute-tooltip";
+import { handleClick } from "../common/handle-click";
+import { longPress } from "../common/directives/long-press-directive";
+import { hassLocalizeLitMixin } from "../../../mixins/lit-localize-mixin";
+import { LovelaceElement, LovelaceElementConfig } from "./types";
+import { HomeAssistant } from "../../../types";
+import { TemplateResult } from "lit-html";
+
+interface Config extends LovelaceElementConfig {
+ icon: string;
+}
+
+export class HuiIconElement extends hassLocalizeLitMixin(LitElement)
+ implements LovelaceElement {
+ public hass?: HomeAssistant;
+ private _config?: Config;
+
+ static get properties() {
+ return { hass: {}, _config: {} };
+ }
+
+ public setConfig(config: Config): void {
+ if (!config.icon) {
+ throw Error("Invalid Configuration: 'icon' required");
+ }
+
+ this._config = config;
+ }
+
+ protected render(): TemplateResult {
+ if (!this._config) {
+ return html``;
+ }
+
+ return html`
+ ${this.renderStyle()}
+ handleClick(this, this.hass!, this._config!, false)}"
+ @ha-hold="${() => handleClick(this, this.hass!, this._config!, true)}"
+ .longPress="${longPress()}"
+ >
+ `;
+ }
+
+ private renderStyle(): TemplateResult {
+ return html`
+
+ `;
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ "hui-icon-element": HuiIconElement;
+ }
+}
+
+customElements.define("hui-icon-element", HuiIconElement);
diff --git a/src/panels/lovelace/elements/hui-image-element.ts b/src/panels/lovelace/elements/hui-image-element.ts
index 97631063b32e..5f5992a9cf5c 100644
--- a/src/panels/lovelace/elements/hui-image-element.ts
+++ b/src/panels/lovelace/elements/hui-image-element.ts
@@ -1,13 +1,13 @@
import { html, LitElement } from "@polymer/lit-element";
-import "../components/hui-image.js";
+import "../components/hui-image";
import { computeTooltip } from "../common/compute-tooltip";
import { handleClick } from "../common/handle-click";
import { longPress } from "../common/directives/long-press-directive";
import { hassLocalizeLitMixin } from "../../../mixins/lit-localize-mixin";
-import { LovelaceElement, LovelaceElementConfig } from "./types.js";
-import { HomeAssistant } from "../../../types.js";
+import { LovelaceElement, LovelaceElementConfig } from "./types";
+import { HomeAssistant } from "../../../types";
import { TemplateResult } from "lit-html";
interface Config extends LovelaceElementConfig {
diff --git a/src/panels/lovelace/elements/hui-service-button-element.ts b/src/panels/lovelace/elements/hui-service-button-element.ts
index c87e3099def9..7a39f19d35f8 100644
--- a/src/panels/lovelace/elements/hui-service-button-element.ts
+++ b/src/panels/lovelace/elements/hui-service-button-element.ts
@@ -1,10 +1,10 @@
import { html, LitElement } from "@polymer/lit-element";
import { TemplateResult } from "lit-html";
-import "../../../components/buttons/ha-call-service-button.js";
+import "../../../components/buttons/ha-call-service-button";
-import { LovelaceElement, LovelaceElementConfig } from "./types.js";
-import { HomeAssistant } from "../../../types.js";
+import { LovelaceElement, LovelaceElementConfig } from "./types";
+import { HomeAssistant } from "../../../types";
export class HuiServiceButtonElement extends LitElement
implements LovelaceElement {
diff --git a/src/panels/lovelace/elements/hui-state-badge-element.ts b/src/panels/lovelace/elements/hui-state-badge-element.ts
index 9a5a461e892f..0c27e33f6dfa 100644
--- a/src/panels/lovelace/elements/hui-state-badge-element.ts
+++ b/src/panels/lovelace/elements/hui-state-badge-element.ts
@@ -1,10 +1,10 @@
import { html, LitElement } from "@polymer/lit-element";
-import "../../../components/entity/ha-state-label-badge.js";
+import "../../../components/entity/ha-state-label-badge";
import computeStateName from "../../../common/entity/compute_state_name";
-import { LovelaceElement, LovelaceElementConfig } from "./types.js";
-import { HomeAssistant } from "../../../types.js";
+import { LovelaceElement, LovelaceElementConfig } from "./types";
+import { HomeAssistant } from "../../../types";
import { TemplateResult } from "lit-html";
export class HuiStateBadgeElement extends LitElement
diff --git a/src/panels/lovelace/elements/hui-state-icon-element.ts b/src/panels/lovelace/elements/hui-state-icon-element.ts
index 574d2f620b3a..5615af861479 100644
--- a/src/panels/lovelace/elements/hui-state-icon-element.ts
+++ b/src/panels/lovelace/elements/hui-state-icon-element.ts
@@ -1,14 +1,14 @@
import { html, LitElement } from "@polymer/lit-element";
import { TemplateResult } from "lit-html";
-import "../../../components/entity/state-badge.js";
+import "../../../components/entity/state-badge";
import { computeTooltip } from "../common/compute-tooltip";
import { handleClick } from "../common/handle-click";
import { longPress } from "../common/directives/long-press-directive";
import { hassLocalizeLitMixin } from "../../../mixins/lit-localize-mixin";
-import { LovelaceElement, LovelaceElementConfig } from "./types.js";
-import { HomeAssistant } from "../../../types.js";
+import { LovelaceElement, LovelaceElementConfig } from "./types";
+import { HomeAssistant } from "../../../types";
export class HuiStateIconElement extends hassLocalizeLitMixin(LitElement)
implements LovelaceElement {
diff --git a/src/panels/lovelace/elements/hui-state-label-element.ts b/src/panels/lovelace/elements/hui-state-label-element.ts
index b066d81daea0..6ad1a6b93df9 100644
--- a/src/panels/lovelace/elements/hui-state-label-element.ts
+++ b/src/panels/lovelace/elements/hui-state-label-element.ts
@@ -1,78 +1,78 @@
-import { html, LitElement } from "@polymer/lit-element";
-
-import "../../../components/entity/ha-state-label-badge.js";
-
-import computeStateDisplay from "../../../common/entity/compute_state_display.js";
-import { computeTooltip } from "../common/compute-tooltip";
-import { handleClick } from "../common/handle-click";
-import { longPress } from "../common/directives/long-press-directive";
-import { hassLocalizeLitMixin } from "../../../mixins/lit-localize-mixin";
-import { LovelaceElement, LovelaceElementConfig } from "./types.js";
-import { HomeAssistant } from "../../../types.js";
-import { TemplateResult } from "lit-html";
-
-interface Config extends LovelaceElementConfig {
- prefix?: string;
- suffix?: string;
-}
-
-class HuiStateLabelElement extends hassLocalizeLitMixin(LitElement)
- implements LovelaceElement {
- public hass?: HomeAssistant;
- private _config?: Config;
-
- static get properties() {
- return { hass: {}, _config: {} };
- }
-
- public setConfig(config: Config): void {
- if (!config.entity) {
- throw Error("Invalid Configuration: 'entity' required");
- }
-
- this._config = config;
- }
-
- protected render(): TemplateResult {
- if (!this._config) {
- return html``;
- }
-
- const state = this.hass!.states[this._config.entity!];
- return html`
- ${this.renderStyle()}
- handleClick(this, this.hass!, this._config!, false)}"
- @ha-hold="${() => handleClick(this, this.hass!, this._config!, true)}"
- .longPress="${longPress()}"
- >
- ${this._config.prefix}${
- state ? computeStateDisplay(this.localize, state) : "-"
- }${this._config.suffix}
-
- `;
- }
-
- private renderStyle(): TemplateResult {
- return html`
-
- `;
- }
-}
-
-declare global {
- interface HTMLElementTagNameMap {
- "hui-state-label-element": HuiStateLabelElement;
- }
-}
-
-customElements.define("hui-state-label-element", HuiStateLabelElement);
+import { html, LitElement } from "@polymer/lit-element";
+
+import "../../../components/entity/ha-state-label-badge";
+
+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 { hassLocalizeLitMixin } from "../../../mixins/lit-localize-mixin";
+import { LovelaceElement, LovelaceElementConfig } from "./types";
+import { HomeAssistant } from "../../../types";
+import { TemplateResult } from "lit-html";
+
+interface Config extends LovelaceElementConfig {
+ prefix?: string;
+ suffix?: string;
+}
+
+class HuiStateLabelElement extends hassLocalizeLitMixin(LitElement)
+ implements LovelaceElement {
+ public hass?: HomeAssistant;
+ private _config?: Config;
+
+ static get properties() {
+ return { hass: {}, _config: {} };
+ }
+
+ public setConfig(config: Config): void {
+ if (!config.entity) {
+ throw Error("Invalid Configuration: 'entity' required");
+ }
+
+ this._config = config;
+ }
+
+ protected render(): TemplateResult {
+ if (!this._config) {
+ return html``;
+ }
+
+ const state = this.hass!.states[this._config.entity!];
+ return html`
+ ${this.renderStyle()}
+ handleClick(this, this.hass!, this._config!, false)}"
+ @ha-hold="${() => handleClick(this, this.hass!, this._config!, true)}"
+ .longPress="${longPress()}"
+ >
+ ${this._config.prefix}${
+ state ? computeStateDisplay(this.localize, state) : "-"
+ }${this._config.suffix}
+
+ `;
+ }
+
+ private renderStyle(): TemplateResult {
+ return html`
+
+ `;
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ "hui-state-label-element": HuiStateLabelElement;
+ }
+}
+
+customElements.define("hui-state-label-element", HuiStateLabelElement);
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 074ef40c3313..2bc9a156d186 100644
--- a/src/panels/lovelace/entity-rows/hui-climate-entity-row.ts
+++ b/src/panels/lovelace/entity-rows/hui-climate-entity-row.ts
@@ -1,11 +1,11 @@
import { html, LitElement } from "@polymer/lit-element";
import { TemplateResult } from "lit-html";
-import "../../../components/ha-climate-state.js";
-import "../components/hui-generic-entity-row.js";
+import "../../../components/ha-climate-state";
+import "../components/hui-generic-entity-row";
-import { HomeAssistant } from "../../../types.js";
-import { EntityRow, EntityConfig } from "./types.js";
+import { HomeAssistant } from "../../../types";
+import { EntityRow, EntityConfig } from "./types";
class HuiClimateEntityRow extends LitElement implements EntityRow {
public hass?: HomeAssistant;
diff --git a/src/panels/lovelace/entity-rows/hui-cover-entity-row.js b/src/panels/lovelace/entity-rows/hui-cover-entity-row.js
index f0334c9c306f..b4c3de695386 100644
--- a/src/panels/lovelace/entity-rows/hui-cover-entity-row.js
+++ b/src/panels/lovelace/entity-rows/hui-cover-entity-row.js
@@ -1,74 +1,74 @@
-import { html } from "@polymer/polymer/lib/utils/html-tag.js";
-import { PolymerElement } from "@polymer/polymer/polymer-element.js";
-
-import "../components/hui-generic-entity-row.js";
-import "../../../components/ha-cover-controls.js";
-import "../../../components/ha-cover-tilt-controls.js";
-import CoverEntity from "../../../util/cover-model.js";
-
-class HuiCoverEntityRow extends PolymerElement {
- static get template() {
- return html`
- ${this.styleTemplate}
-
- ${this.coverControlTemplate}
-
- `;
- }
-
- static get styleTemplate() {
- return html`
-
- `;
- }
-
- static get coverControlTemplate() {
- return html`
-
-
-
-
-
-
- `;
- }
-
- static get properties() {
- return {
- hass: Object,
- _config: Object,
- _stateObj: {
- type: Object,
- computed: "_computeStateObj(hass.states, _config.entity)",
- },
- _entityObj: {
- type: Object,
- computed: "_computeEntityObj(hass, _stateObj)",
- },
- };
- }
-
- _computeStateObj(states, entityId) {
- return states && entityId in states ? states[entityId] : null;
- }
-
- _computeEntityObj(hass, stateObj) {
- return stateObj ? new CoverEntity(hass, stateObj) : null;
- }
-
- setConfig(config) {
- if (!config || !config.entity) {
- throw new Error("Entity not configured.");
- }
- this._config = config;
- }
-}
-customElements.define("hui-cover-entity-row", HuiCoverEntityRow);
+import { html } from "@polymer/polymer/lib/utils/html-tag";
+import { PolymerElement } from "@polymer/polymer/polymer-element";
+
+import "../components/hui-generic-entity-row";
+import "../../../components/ha-cover-controls";
+import "../../../components/ha-cover-tilt-controls";
+import CoverEntity from "../../../util/cover-model";
+
+class HuiCoverEntityRow extends PolymerElement {
+ static get template() {
+ return html`
+ ${this.styleTemplate}
+
+ ${this.coverControlTemplate}
+
+ `;
+ }
+
+ static get styleTemplate() {
+ return html`
+
+ `;
+ }
+
+ static get coverControlTemplate() {
+ return html`
+
+
+
+
+
+
+ `;
+ }
+
+ static get properties() {
+ return {
+ hass: Object,
+ _config: Object,
+ _stateObj: {
+ type: Object,
+ computed: "_computeStateObj(hass.states, _config.entity)",
+ },
+ _entityObj: {
+ type: Object,
+ computed: "_computeEntityObj(hass, _stateObj)",
+ },
+ };
+ }
+
+ _computeStateObj(states, entityId) {
+ return states && entityId in states ? states[entityId] : null;
+ }
+
+ _computeEntityObj(hass, stateObj) {
+ return stateObj ? new CoverEntity(hass, stateObj) : null;
+ }
+
+ setConfig(config) {
+ if (!config || !config.entity) {
+ throw new Error("Entity not configured.");
+ }
+ this._config = config;
+ }
+}
+customElements.define("hui-cover-entity-row", HuiCoverEntityRow);
diff --git a/src/panels/lovelace/entity-rows/hui-group-entity-row.js b/src/panels/lovelace/entity-rows/hui-group-entity-row.js
index 8da9c1072249..77720fd0bf2c 100644
--- a/src/panels/lovelace/entity-rows/hui-group-entity-row.js
+++ b/src/panels/lovelace/entity-rows/hui-group-entity-row.js
@@ -1,78 +1,78 @@
-import { html } from "@polymer/polymer/lib/utils/html-tag.js";
-import { PolymerElement } from "@polymer/polymer/polymer-element.js";
-
-import "../components/hui-generic-entity-row.js";
-import "../../../components/entity/ha-entity-toggle.js";
-
-import computeStateDisplay from "../../../common/entity/compute_state_display.js";
-import { DOMAINS_TOGGLE } from "../../../common/const.js";
-import LocalizeMixin from "../../../mixins/localize-mixin.js";
-
-/*
- * @appliesMixin LocalizeMixin
- */
-class HuiGroupEntityRow extends LocalizeMixin(PolymerElement) {
- static get template() {
- return html`
-
- ${this.groupControlTemplate}
-
- `;
- }
-
- static get groupControlTemplate() {
- return html`
-
-
-
-
-
- [[_computeState(_stateObj)]]
-
-
- `;
- }
-
- static get properties() {
- return {
- hass: Object,
- _config: Object,
- _stateObj: {
- type: Object,
- computed: "_computeStateObj(hass.states, _config.entity)",
- },
- _canToggle: {
- type: Boolean,
- computed: "_computeCanToggle(_stateObj.attributes.entity_id)",
- },
- };
- }
-
- setConfig(config) {
- if (!config || !config.entity) {
- throw new Error("Entity not configured.");
- }
- this._config = config;
- }
-
- _computeStateObj(states, entityId) {
- return states && entityId in states ? states[entityId] : null;
- }
-
- _computeCanToggle(entityIds) {
- return entityIds.some((entityId) =>
- DOMAINS_TOGGLE.has(entityId.split(".", 1)[0])
- );
- }
-
- _computeState(stateObj) {
- return computeStateDisplay(this.localize, stateObj);
- }
-}
-customElements.define("hui-group-entity-row", HuiGroupEntityRow);
+import { html } from "@polymer/polymer/lib/utils/html-tag";
+import { PolymerElement } from "@polymer/polymer/polymer-element";
+
+import "../components/hui-generic-entity-row";
+import "../../../components/entity/ha-entity-toggle";
+
+import computeStateDisplay from "../../../common/entity/compute_state_display";
+import { DOMAINS_TOGGLE } from "../../../common/const";
+import LocalizeMixin from "../../../mixins/localize-mixin";
+
+/*
+ * @appliesMixin LocalizeMixin
+ */
+class HuiGroupEntityRow extends LocalizeMixin(PolymerElement) {
+ static get template() {
+ return html`
+
+ ${this.groupControlTemplate}
+
+ `;
+ }
+
+ static get groupControlTemplate() {
+ return html`
+
+
+
+
+
+ [[_computeState(_stateObj)]]
+
+
+ `;
+ }
+
+ static get properties() {
+ return {
+ hass: Object,
+ _config: Object,
+ _stateObj: {
+ type: Object,
+ computed: "_computeStateObj(hass.states, _config.entity)",
+ },
+ _canToggle: {
+ type: Boolean,
+ computed: "_computeCanToggle(_stateObj.attributes.entity_id)",
+ },
+ };
+ }
+
+ setConfig(config) {
+ if (!config || !config.entity) {
+ throw new Error("Entity not configured.");
+ }
+ this._config = config;
+ }
+
+ _computeStateObj(states, entityId) {
+ return states && entityId in states ? states[entityId] : null;
+ }
+
+ _computeCanToggle(entityIds) {
+ return entityIds.some((entityId) =>
+ DOMAINS_TOGGLE.has(entityId.split(".", 1)[0])
+ );
+ }
+
+ _computeState(stateObj) {
+ return computeStateDisplay(this.localize, stateObj);
+ }
+}
+customElements.define("hui-group-entity-row", HuiGroupEntityRow);
diff --git a/src/panels/lovelace/entity-rows/hui-input-number-entity-row.js b/src/panels/lovelace/entity-rows/hui-input-number-entity-row.js
index 6c6e81bc0649..33b827221694 100644
--- a/src/panels/lovelace/entity-rows/hui-input-number-entity-row.js
+++ b/src/panels/lovelace/entity-rows/hui-input-number-entity-row.js
@@ -1,170 +1,170 @@
-import { html } from "@polymer/polymer/lib/utils/html-tag.js";
-import { PolymerElement } from "@polymer/polymer/polymer-element.js";
-import "@polymer/paper-input/paper-input.js";
-import { IronResizableBehavior } from "@polymer/iron-resizable-behavior/iron-resizable-behavior.js";
-import { mixinBehaviors } from "@polymer/polymer/lib/legacy/class.js";
-
-import "../components/hui-generic-entity-row.js";
-import "../../../components/ha-slider";
-
-class HuiInputNumberEntityRow extends mixinBehaviors(
- [IronResizableBehavior],
- PolymerElement
-) {
- static get template() {
- return html`
- ${this.styleTemplate}
-
- ${this.inputNumberControlTemplate}
-
- `;
- }
-
- static get styleTemplate() {
- return html`
-
- `;
- }
-
- static get inputNumberControlTemplate() {
- return html`
-
-
-
-
- [[_value]] [[_stateObj.attributes.unit_of_measurement]]
-
-
-
-
-
-
- `;
- }
-
- static get properties() {
- return {
- hass: Object,
- _config: Object,
- _stateObj: {
- type: Object,
- computed: "_computeStateObj(hass.states, _config.entity)",
- observer: "_stateObjChanged",
- },
- _min: {
- type: Number,
- value: 0,
- },
- _max: {
- type: Number,
- value: 100,
- },
- _step: Number,
- _value: Number,
- };
- }
-
- ready() {
- super.ready();
- if (typeof ResizeObserver === "function") {
- const ro = new ResizeObserver((entries) => {
- entries.forEach(() => {
- this._hiddenState();
- });
- });
- ro.observe(this.$.input_number_card);
- } else {
- this.addEventListener("iron-resize", this._hiddenState);
- }
- }
-
- _equals(a, b) {
- return a === b;
- }
-
- _computeStateObj(states, entityId) {
- return states && entityId in states ? states[entityId] : null;
- }
-
- setConfig(config) {
- if (!config || !config.entity) {
- throw new Error("Entity not configured.");
- }
- this._config = config;
- }
-
- _hiddenState() {
- if (
- !this.$ ||
- !this._stateObj ||
- this._stateObj.attributes.mode !== "slider"
- )
- return;
- const width = this.$.input_number_card.offsetWidth;
- const stateEl = this.shadowRoot.querySelector(".state");
- if (!stateEl) return;
- stateEl.hidden = width <= 350;
- }
-
- _stateObjChanged(stateObj, oldStateObj) {
- if (!stateObj) return;
-
- this.setProperties({
- _min: Number(stateObj.attributes.min),
- _max: Number(stateObj.attributes.max),
- _step: Number(stateObj.attributes.step),
- _value: Number(stateObj.state),
- });
- if (
- oldStateObj &&
- stateObj.attributes.mode === "slider" &&
- oldStateObj.attributes.mode !== "slider"
- ) {
- this._hiddenState();
- }
- }
-
- _selectedValueChanged() {
- if (this._value === Number(this._stateObj.state)) return;
-
- this.hass.callService("input_number", "set_value", {
- value: this._value,
- entity_id: this._stateObj.entity_id,
- });
- }
-}
-customElements.define("hui-input-number-entity-row", HuiInputNumberEntityRow);
+import { html } from "@polymer/polymer/lib/utils/html-tag";
+import { PolymerElement } from "@polymer/polymer/polymer-element";
+import "@polymer/paper-input/paper-input";
+import { IronResizableBehavior } from "@polymer/iron-resizable-behavior/iron-resizable-behavior";
+import { mixinBehaviors } from "@polymer/polymer/lib/legacy/class";
+
+import "../components/hui-generic-entity-row";
+import "../../../components/ha-slider";
+
+class HuiInputNumberEntityRow extends mixinBehaviors(
+ [IronResizableBehavior],
+ PolymerElement
+) {
+ static get template() {
+ return html`
+ ${this.styleTemplate}
+
+ ${this.inputNumberControlTemplate}
+
+ `;
+ }
+
+ static get styleTemplate() {
+ return html`
+
+ `;
+ }
+
+ static get inputNumberControlTemplate() {
+ return html`
+
+
+
+
+ [[_value]] [[_stateObj.attributes.unit_of_measurement]]
+
+
+
+
+
+
+ `;
+ }
+
+ static get properties() {
+ return {
+ hass: Object,
+ _config: Object,
+ _stateObj: {
+ type: Object,
+ computed: "_computeStateObj(hass.states, _config.entity)",
+ observer: "_stateObjChanged",
+ },
+ _min: {
+ type: Number,
+ value: 0,
+ },
+ _max: {
+ type: Number,
+ value: 100,
+ },
+ _step: Number,
+ _value: Number,
+ };
+ }
+
+ ready() {
+ super.ready();
+ if (typeof ResizeObserver === "function") {
+ const ro = new ResizeObserver((entries) => {
+ entries.forEach(() => {
+ this._hiddenState();
+ });
+ });
+ ro.observe(this.$.input_number_card);
+ } else {
+ this.addEventListener("iron-resize", this._hiddenState);
+ }
+ }
+
+ _equals(a, b) {
+ return a === b;
+ }
+
+ _computeStateObj(states, entityId) {
+ return states && entityId in states ? states[entityId] : null;
+ }
+
+ setConfig(config) {
+ if (!config || !config.entity) {
+ throw new Error("Entity not configured.");
+ }
+ this._config = config;
+ }
+
+ _hiddenState() {
+ if (
+ !this.$ ||
+ !this._stateObj ||
+ this._stateObj.attributes.mode !== "slider"
+ )
+ return;
+ const width = this.$.input_number_card.offsetWidth;
+ const stateEl = this.shadowRoot.querySelector(".state");
+ if (!stateEl) return;
+ stateEl.hidden = width <= 350;
+ }
+
+ _stateObjChanged(stateObj, oldStateObj) {
+ if (!stateObj) return;
+
+ this.setProperties({
+ _min: Number(stateObj.attributes.min),
+ _max: Number(stateObj.attributes.max),
+ _step: Number(stateObj.attributes.step),
+ _value: Number(stateObj.state),
+ });
+ if (
+ oldStateObj &&
+ stateObj.attributes.mode === "slider" &&
+ oldStateObj.attributes.mode !== "slider"
+ ) {
+ this._hiddenState();
+ }
+ }
+
+ _selectedValueChanged() {
+ if (this._value === Number(this._stateObj.state)) return;
+
+ this.hass.callService("input_number", "set_value", {
+ value: this._value,
+ entity_id: this._stateObj.entity_id,
+ });
+ }
+}
+customElements.define("hui-input-number-entity-row", HuiInputNumberEntityRow);
diff --git a/src/panels/lovelace/entity-rows/hui-input-select-entity-row.js b/src/panels/lovelace/entity-rows/hui-input-select-entity-row.js
index 2f5850386bc3..61ce4317c48f 100644
--- a/src/panels/lovelace/entity-rows/hui-input-select-entity-row.js
+++ b/src/panels/lovelace/entity-rows/hui-input-select-entity-row.js
@@ -1,107 +1,107 @@
-import { html } from "@polymer/polymer/lib/utils/html-tag.js";
-import { PolymerElement } from "@polymer/polymer/polymer-element.js";
-import "@polymer/paper-dropdown-menu/paper-dropdown-menu.js";
-import "@polymer/paper-item/paper-item.js";
-import "@polymer/paper-listbox/paper-listbox.js";
-
-import "../../../components/entity/state-badge.js";
-
-import computeStateName from "../../../common/entity/compute_state_name.js";
-
-import EventsMixin from "../../../mixins/events-mixin.js";
-
-/*
- * @appliesMixin EventsMixin
- */
-class HuiInputSelectEntityRow extends EventsMixin(PolymerElement) {
- static get template() {
- return html`
- ${this.styleTemplate}
-
-
-
-
-
- [[item]]
-
-
-
-
-
-
- Entity not available: [[_config.entity]]
-
-
- `;
- }
-
- static get styleTemplate() {
- return html`
-
- `;
- }
-
- static get properties() {
- return {
- hass: Object,
- _config: Object,
- _stateObj: {
- type: Object,
- computed: "_computeStateObj(hass.states, _config.entity)",
- },
- _selected: {
- type: String,
- observer: "_selectedChanged",
- },
- };
- }
-
- setConfig(config) {
- if (!config || !config.entity) {
- throw new Error("Entity not configured.");
- }
- this._config = config;
- }
-
- _computeStateObj(states, entityId) {
- return states && entityId in states ? states[entityId] : null;
- }
-
- _computeName(name, stateObj) {
- return name || computeStateName(stateObj);
- }
-
- _computeSelected(stateObj) {
- return stateObj.attributes.options.indexOf(stateObj.state);
- }
-
- _selectedChanged(option) {
- // Selected Option will transition to '' before transitioning to new value
- if (option === "" || option === this._stateObj.state) {
- return;
- }
- this.hass.callService("input_select", "select_option", {
- option: option,
- entity_id: this._stateObj.entity_id,
- });
- }
-
- _stopPropagation(ev) {
- ev.stopPropagation();
- }
-}
-customElements.define("hui-input-select-entity-row", HuiInputSelectEntityRow);
+import { html } from "@polymer/polymer/lib/utils/html-tag";
+import { PolymerElement } from "@polymer/polymer/polymer-element";
+import "@polymer/paper-dropdown-menu/paper-dropdown-menu";
+import "@polymer/paper-item/paper-item";
+import "@polymer/paper-listbox/paper-listbox";
+
+import "../../../components/entity/state-badge";
+
+import computeStateName from "../../../common/entity/compute_state_name";
+
+import EventsMixin from "../../../mixins/events-mixin";
+
+/*
+ * @appliesMixin EventsMixin
+ */
+class HuiInputSelectEntityRow extends EventsMixin(PolymerElement) {
+ static get template() {
+ return html`
+ ${this.styleTemplate}
+
+
+
+
+
+ [[item]]
+
+
+
+
+
+
+ Entity not available: [[_config.entity]]
+
+
+ `;
+ }
+
+ static get styleTemplate() {
+ return html`
+
+ `;
+ }
+
+ static get properties() {
+ return {
+ hass: Object,
+ _config: Object,
+ _stateObj: {
+ type: Object,
+ computed: "_computeStateObj(hass.states, _config.entity)",
+ },
+ _selected: {
+ type: String,
+ observer: "_selectedChanged",
+ },
+ };
+ }
+
+ setConfig(config) {
+ if (!config || !config.entity) {
+ throw new Error("Entity not configured.");
+ }
+ this._config = config;
+ }
+
+ _computeStateObj(states, entityId) {
+ return states && entityId in states ? states[entityId] : null;
+ }
+
+ _computeName(name, stateObj) {
+ return name || computeStateName(stateObj);
+ }
+
+ _computeSelected(stateObj) {
+ return stateObj.attributes.options.indexOf(stateObj.state);
+ }
+
+ _selectedChanged(option) {
+ // Selected Option will transition to '' before transitioning to new value
+ if (option === "" || option === this._stateObj.state) {
+ return;
+ }
+ this.hass.callService("input_select", "select_option", {
+ option: option,
+ entity_id: this._stateObj.entity_id,
+ });
+ }
+
+ _stopPropagation(ev) {
+ ev.stopPropagation();
+ }
+}
+customElements.define("hui-input-select-entity-row", HuiInputSelectEntityRow);
diff --git a/src/panels/lovelace/entity-rows/hui-input-text-entity-row.js b/src/panels/lovelace/entity-rows/hui-input-text-entity-row.js
index 98efd8e187de..dc27bb8315ed 100644
--- a/src/panels/lovelace/entity-rows/hui-input-text-entity-row.js
+++ b/src/panels/lovelace/entity-rows/hui-input-text-entity-row.js
@@ -1,73 +1,73 @@
-import { html } from "@polymer/polymer/lib/utils/html-tag.js";
-import { PolymerElement } from "@polymer/polymer/polymer-element.js";
-import "@polymer/paper-input/paper-input.js";
-
-import "../components/hui-generic-entity-row.js";
-
-class HuiInputTextEntityRow extends PolymerElement {
- static get template() {
- return html`
-
- ${this.inputTextControlTemplate}
-
- `;
- }
-
- static get inputTextControlTemplate() {
- return html`
-
- `;
- }
-
- static get properties() {
- return {
- hass: Object,
- _config: Object,
- _stateObj: {
- type: Object,
- computed: "_computeStateObj(hass.states, _config.entity)",
- observer: "_stateObjChanged",
- },
- _value: String,
- };
- }
-
- _computeStateObj(states, entityId) {
- return states && entityId in states ? states[entityId] : null;
- }
-
- setConfig(config) {
- if (!config || !config.entity) {
- throw new Error("Entity not configured.");
- }
- this._config = config;
- }
-
- _stateObjChanged(stateObj) {
- this._value = stateObj && stateObj.state;
- }
-
- _selectedValueChanged() {
- if (this._value === this._stateObj.state) {
- return;
- }
- this.hass.callService("input_text", "set_value", {
- value: this._value,
- entity_id: this._stateObj.entity_id,
- });
- }
-}
-customElements.define("hui-input-text-entity-row", HuiInputTextEntityRow);
+import { html } from "@polymer/polymer/lib/utils/html-tag";
+import { PolymerElement } from "@polymer/polymer/polymer-element";
+import "@polymer/paper-input/paper-input";
+
+import "../components/hui-generic-entity-row";
+
+class HuiInputTextEntityRow extends PolymerElement {
+ static get template() {
+ return html`
+
+ ${this.inputTextControlTemplate}
+
+ `;
+ }
+
+ static get inputTextControlTemplate() {
+ return html`
+
+ `;
+ }
+
+ static get properties() {
+ return {
+ hass: Object,
+ _config: Object,
+ _stateObj: {
+ type: Object,
+ computed: "_computeStateObj(hass.states, _config.entity)",
+ observer: "_stateObjChanged",
+ },
+ _value: String,
+ };
+ }
+
+ _computeStateObj(states, entityId) {
+ return states && entityId in states ? states[entityId] : null;
+ }
+
+ setConfig(config) {
+ if (!config || !config.entity) {
+ throw new Error("Entity not configured.");
+ }
+ this._config = config;
+ }
+
+ _stateObjChanged(stateObj) {
+ this._value = stateObj && stateObj.state;
+ }
+
+ _selectedValueChanged() {
+ if (this._value === this._stateObj.state) {
+ return;
+ }
+ this.hass.callService("input_text", "set_value", {
+ value: this._value,
+ entity_id: this._stateObj.entity_id,
+ });
+ }
+}
+customElements.define("hui-input-text-entity-row", HuiInputTextEntityRow);
diff --git a/src/panels/lovelace/entity-rows/hui-lock-entity-row.js b/src/panels/lovelace/entity-rows/hui-lock-entity-row.js
index 616316d6a242..f75559d30acb 100644
--- a/src/panels/lovelace/entity-rows/hui-lock-entity-row.js
+++ b/src/panels/lovelace/entity-rows/hui-lock-entity-row.js
@@ -1,83 +1,83 @@
-import { html } from "@polymer/polymer/lib/utils/html-tag.js";
-import { PolymerElement } from "@polymer/polymer/polymer-element.js";
-import "@polymer/paper-button/paper-button.js";
-
-import "../components/hui-generic-entity-row.js";
-
-import LocalizeMixin from "../../../mixins/localize-mixin.js";
-
-/*
- * @appliesMixin LocalizeMixin
- */
-class HuiLockEntityRow extends LocalizeMixin(PolymerElement) {
- static get template() {
- return html`
- ${this.styleTemplate}
-
- ${this.lockControlTemplate}
-
- `;
- }
-
- static get styleTemplate() {
- return html`
-
- `;
- }
-
- static get lockControlTemplate() {
- return html`
-
- [[_computeButtonTitle(_stateObj.state)]]
-
- `;
- }
-
- static get properties() {
- return {
- hass: Object,
- _config: Object,
- _stateObj: {
- type: Object,
- computed: "_computeStateObj(hass.states, _config.entity)",
- },
- };
- }
-
- _computeStateObj(states, entityId) {
- return states && entityId in states ? states[entityId] : null;
- }
-
- setConfig(config) {
- if (!config || !config.entity) {
- throw new Error("Entity not configured.");
- }
- this._config = config;
- }
-
- _computeButtonTitle(state) {
- return state === "locked"
- ? this.localize("ui.card.lock.unlock")
- : this.localize("ui.card.lock.lock");
- }
-
- _callService(ev) {
- ev.stopPropagation();
- const stateObj = this._stateObj;
- this.hass.callService(
- "lock",
- stateObj.state === "locked" ? "unlock" : "lock",
- { entity_id: stateObj.entity_id }
- );
- }
-}
-customElements.define("hui-lock-entity-row", HuiLockEntityRow);
+import { html } from "@polymer/polymer/lib/utils/html-tag";
+import { PolymerElement } from "@polymer/polymer/polymer-element";
+import "@polymer/paper-button/paper-button";
+
+import "../components/hui-generic-entity-row";
+
+import LocalizeMixin from "../../../mixins/localize-mixin";
+
+/*
+ * @appliesMixin LocalizeMixin
+ */
+class HuiLockEntityRow extends LocalizeMixin(PolymerElement) {
+ static get template() {
+ return html`
+ ${this.styleTemplate}
+
+ ${this.lockControlTemplate}
+
+ `;
+ }
+
+ static get styleTemplate() {
+ return html`
+
+ `;
+ }
+
+ static get lockControlTemplate() {
+ return html`
+
+ [[_computeButtonTitle(_stateObj.state)]]
+
+ `;
+ }
+
+ static get properties() {
+ return {
+ hass: Object,
+ _config: Object,
+ _stateObj: {
+ type: Object,
+ computed: "_computeStateObj(hass.states, _config.entity)",
+ },
+ };
+ }
+
+ _computeStateObj(states, entityId) {
+ return states && entityId in states ? states[entityId] : null;
+ }
+
+ setConfig(config) {
+ if (!config || !config.entity) {
+ throw new Error("Entity not configured.");
+ }
+ this._config = config;
+ }
+
+ _computeButtonTitle(state) {
+ return state === "locked"
+ ? this.localize("ui.card.lock.unlock")
+ : this.localize("ui.card.lock.lock");
+ }
+
+ _callService(ev) {
+ ev.stopPropagation();
+ const stateObj = this._stateObj;
+ this.hass.callService(
+ "lock",
+ stateObj.state === "locked" ? "unlock" : "lock",
+ { entity_id: stateObj.entity_id }
+ );
+ }
+}
+customElements.define("hui-lock-entity-row", HuiLockEntityRow);
diff --git a/src/panels/lovelace/entity-rows/hui-media-player-entity-row.js b/src/panels/lovelace/entity-rows/hui-media-player-entity-row.js
index 77cbde1b94ed..b6d90e944f59 100644
--- a/src/panels/lovelace/entity-rows/hui-media-player-entity-row.js
+++ b/src/panels/lovelace/entity-rows/hui-media-player-entity-row.js
@@ -1,162 +1,162 @@
-import "@polymer/paper-icon-button/paper-icon-button.js";
-import { html } from "@polymer/polymer/lib/utils/html-tag.js";
-import { PolymerElement } from "@polymer/polymer/polymer-element.js";
-
-import "../components/hui-generic-entity-row.js";
-
-import LocalizeMixin from "../../../mixins/localize-mixin.js";
-
-const SUPPORT_PAUSE = 1;
-const SUPPORT_NEXT_TRACK = 32;
-const SUPPORTS_PLAY = 16384;
-const OFF_STATES = ["off", "idle"];
-
-/*
- * @appliesMixin LocalizeMixin
- */
-class HuiMediaPlayerEntityRow extends LocalizeMixin(PolymerElement) {
- static get template() {
- return html`
- ${this.styleTemplate}
-
- ${this.mediaPlayerControlTemplate}
-
- `;
- }
-
- static get styleTemplate() {
- return html`
-
- `;
- }
-
- static get mediaPlayerControlTemplate() {
- return html`
-
-
-
-
- [[_computeState(_stateObj.state)]]
-
-
-
- [[_computeMediaTitle(_stateObj)]]
-
- `;
- }
-
- static get properties() {
- return {
- hass: Object,
- _config: Object,
- _stateObj: {
- type: Object,
- computed: "_computeStateObj(hass.states, _config.entity)",
- },
- };
- }
-
- _computeStateObj(states, entityId) {
- return states && entityId in states ? states[entityId] : null;
- }
-
- setConfig(config) {
- if (!config || !config.entity) {
- throw new Error("Entity not configured.");
- }
- this._config = config;
- }
-
- _computeControlIcon(stateObj) {
- if (!stateObj) return null;
-
- if (stateObj.state !== "playing") {
- return stateObj.attributes.supported_features & SUPPORTS_PLAY
- ? "hass:play"
- : "";
- }
-
- return stateObj.attributes.supported_features & SUPPORT_PAUSE
- ? "hass:pause"
- : "hass:stop";
- }
-
- _computeMediaTitle(stateObj) {
- if (!stateObj || this._isOff(stateObj.state)) return null;
-
- switch (stateObj.attributes.media_content_type) {
- case "music":
- return `${stateObj.attributes.media_artist}: ${
- stateObj.attributes.media_title
- }`;
- case "tvshow":
- return `${stateObj.attributes.media_series_title}: ${
- stateObj.attributes.media_title
- }`;
- default:
- return (
- stateObj.attributes.media_title ||
- stateObj.attributes.app_name ||
- stateObj.state
- );
- }
- }
-
- _computeState(state) {
- return (
- this.localize(`state.media_player.${state}`) ||
- this.localize(`state.default.${state}`) ||
- state
- );
- }
-
- _callService(service) {
- this.hass.callService("media_player", service, {
- entity_id: this._config.entity,
- });
- }
-
- _playPause(event) {
- event.stopPropagation();
- this._callService("media_play_pause");
- }
-
- _nextTrack(event) {
- event.stopPropagation();
- if (this._stateObj.attributes.supported_features & SUPPORT_NEXT_TRACK) {
- this._callService("media_next_track");
- }
- }
-
- _isOff(state) {
- return OFF_STATES.includes(state);
- }
-
- _supportsNext(stateObj) {
- return (
- stateObj && stateObj.attributes.supported_features & SUPPORT_NEXT_TRACK
- );
- }
-}
-customElements.define("hui-media-player-entity-row", HuiMediaPlayerEntityRow);
+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 "../components/hui-generic-entity-row";
+
+import LocalizeMixin from "../../../mixins/localize-mixin";
+
+const SUPPORT_PAUSE = 1;
+const SUPPORT_NEXT_TRACK = 32;
+const SUPPORTS_PLAY = 16384;
+const OFF_STATES = ["off", "idle"];
+
+/*
+ * @appliesMixin LocalizeMixin
+ */
+class HuiMediaPlayerEntityRow extends LocalizeMixin(PolymerElement) {
+ static get template() {
+ return html`
+ ${this.styleTemplate}
+
+ ${this.mediaPlayerControlTemplate}
+
+ `;
+ }
+
+ static get styleTemplate() {
+ return html`
+
+ `;
+ }
+
+ static get mediaPlayerControlTemplate() {
+ return html`
+
+
+
+
+ [[_computeState(_stateObj.state)]]
+
+
+
+ [[_computeMediaTitle(_stateObj)]]
+
+ `;
+ }
+
+ static get properties() {
+ return {
+ hass: Object,
+ _config: Object,
+ _stateObj: {
+ type: Object,
+ computed: "_computeStateObj(hass.states, _config.entity)",
+ },
+ };
+ }
+
+ _computeStateObj(states, entityId) {
+ return states && entityId in states ? states[entityId] : null;
+ }
+
+ setConfig(config) {
+ if (!config || !config.entity) {
+ throw new Error("Entity not configured.");
+ }
+ this._config = config;
+ }
+
+ _computeControlIcon(stateObj) {
+ if (!stateObj) return null;
+
+ if (stateObj.state !== "playing") {
+ return stateObj.attributes.supported_features & SUPPORTS_PLAY
+ ? "hass:play"
+ : "";
+ }
+
+ return stateObj.attributes.supported_features & SUPPORT_PAUSE
+ ? "hass:pause"
+ : "hass:stop";
+ }
+
+ _computeMediaTitle(stateObj) {
+ if (!stateObj || this._isOff(stateObj.state)) return null;
+
+ switch (stateObj.attributes.media_content_type) {
+ case "music":
+ return `${stateObj.attributes.media_artist}: ${
+ stateObj.attributes.media_title
+ }`;
+ case "tvshow":
+ return `${stateObj.attributes.media_series_title}: ${
+ stateObj.attributes.media_title
+ }`;
+ default:
+ return (
+ stateObj.attributes.media_title ||
+ stateObj.attributes.app_name ||
+ stateObj.state
+ );
+ }
+ }
+
+ _computeState(state) {
+ return (
+ this.localize(`state.media_player.${state}`) ||
+ this.localize(`state.default.${state}`) ||
+ state
+ );
+ }
+
+ _callService(service) {
+ this.hass.callService("media_player", service, {
+ entity_id: this._config.entity,
+ });
+ }
+
+ _playPause(event) {
+ event.stopPropagation();
+ this._callService("media_play_pause");
+ }
+
+ _nextTrack(event) {
+ event.stopPropagation();
+ if (this._stateObj.attributes.supported_features & SUPPORT_NEXT_TRACK) {
+ this._callService("media_next_track");
+ }
+ }
+
+ _isOff(state) {
+ return OFF_STATES.includes(state);
+ }
+
+ _supportsNext(stateObj) {
+ return (
+ stateObj && stateObj.attributes.supported_features & SUPPORT_NEXT_TRACK
+ );
+ }
+}
+customElements.define("hui-media-player-entity-row", HuiMediaPlayerEntityRow);
diff --git a/src/panels/lovelace/entity-rows/hui-scene-entity-row.js b/src/panels/lovelace/entity-rows/hui-scene-entity-row.js
index 5d55e3bb7508..f0d15443359d 100644
--- a/src/panels/lovelace/entity-rows/hui-scene-entity-row.js
+++ b/src/panels/lovelace/entity-rows/hui-scene-entity-row.js
@@ -1,70 +1,70 @@
-import { html } from "@polymer/polymer/lib/utils/html-tag.js";
-import { PolymerElement } from "@polymer/polymer/polymer-element.js";
-import "@polymer/paper-button/paper-button.js";
-
-import "../components/hui-generic-entity-row.js";
-
-import LocalizeMixin from "../../../mixins/localize-mixin.js";
-
-/*
- * @appliesMixin LocalizeMixin
- */
-class HuiSceneEntityRow extends LocalizeMixin(PolymerElement) {
- static get template() {
- return html`
- ${this.styleTemplate}
-
- ${this.sceneControlTemplate}
-
- `;
- }
-
- static get styleTemplate() {
- return html`
-
- `;
- }
-
- static get sceneControlTemplate() {
- return html`
-
- [[localize('ui.card.scene.activate')]]
-
- `;
- }
-
- static get properties() {
- return {
- hass: Object,
- _config: Object,
- };
- }
-
- _computeStateObj(states, entityId) {
- return states && entityId in states ? states[entityId] : null;
- }
-
- setConfig(config) {
- if (!config || !config.entity) {
- throw new Error("Entity not configured.");
- }
- this._config = config;
- }
-
- _callService(ev) {
- ev.stopPropagation();
- this.hass.callService("scene", "turn_on", {
- entity_id: this._config.entity,
- });
- }
-}
-customElements.define("hui-scene-entity-row", HuiSceneEntityRow);
+import { html } from "@polymer/polymer/lib/utils/html-tag";
+import { PolymerElement } from "@polymer/polymer/polymer-element";
+import "@polymer/paper-button/paper-button";
+
+import "../components/hui-generic-entity-row";
+
+import LocalizeMixin from "../../../mixins/localize-mixin";
+
+/*
+ * @appliesMixin LocalizeMixin
+ */
+class HuiSceneEntityRow extends LocalizeMixin(PolymerElement) {
+ static get template() {
+ return html`
+ ${this.styleTemplate}
+
+ ${this.sceneControlTemplate}
+
+ `;
+ }
+
+ static get styleTemplate() {
+ return html`
+
+ `;
+ }
+
+ static get sceneControlTemplate() {
+ return html`
+
+ [[localize('ui.card.scene.activate')]]
+
+ `;
+ }
+
+ static get properties() {
+ return {
+ hass: Object,
+ _config: Object,
+ };
+ }
+
+ _computeStateObj(states, entityId) {
+ return states && entityId in states ? states[entityId] : null;
+ }
+
+ setConfig(config) {
+ if (!config || !config.entity) {
+ throw new Error("Entity not configured.");
+ }
+ this._config = config;
+ }
+
+ _callService(ev) {
+ ev.stopPropagation();
+ this.hass.callService("scene", "turn_on", {
+ entity_id: this._config.entity,
+ });
+ }
+}
+customElements.define("hui-scene-entity-row", HuiSceneEntityRow);
diff --git a/src/panels/lovelace/entity-rows/hui-script-entity-row.js b/src/panels/lovelace/entity-rows/hui-script-entity-row.js
index f00fa0b74429..db5b948160f6 100644
--- a/src/panels/lovelace/entity-rows/hui-script-entity-row.js
+++ b/src/panels/lovelace/entity-rows/hui-script-entity-row.js
@@ -1,78 +1,78 @@
-import { html } from "@polymer/polymer/lib/utils/html-tag.js";
-import { PolymerElement } from "@polymer/polymer/polymer-element.js";
-import "@polymer/paper-button/paper-button.js";
-
-import "../components/hui-generic-entity-row.js";
-import "../../../components/entity/ha-entity-toggle.js";
-
-import LocalizeMixin from "../../../mixins/localize-mixin.js";
-
-/*
- * @appliesMixin LocalizeMixin
- */
-class HuiScriptEntityRow extends LocalizeMixin(PolymerElement) {
- static get template() {
- return html`
- ${this.styleTemplate}
-
- ${this.scriptControlTemplate}
-
- `;
- }
-
- static get styleTemplate() {
- return html`
-
- `;
- }
-
- static get scriptControlTemplate() {
- return html`
-
-
-
-
- [[localize('ui.card.script.execute')]]
-
- `;
- }
-
- static get properties() {
- return {
- hass: Object,
- _config: Object,
- _stateObj: {
- type: Object,
- computed: "_computeStateObj(hass.states, _config.entity)",
- },
- };
- }
-
- _computeStateObj(states, entityId) {
- return states && entityId in states ? states[entityId] : null;
- }
-
- setConfig(config) {
- if (!config || !config.entity) {
- throw new Error("Entity not configured.");
- }
- this._config = config;
- }
-
- _callService(ev) {
- ev.stopPropagation();
- this.hass.callService("script", "turn_on", {
- entity_id: this._config.entity,
- });
- }
-}
-customElements.define("hui-script-entity-row", HuiScriptEntityRow);
+import { html } from "@polymer/polymer/lib/utils/html-tag";
+import { PolymerElement } from "@polymer/polymer/polymer-element";
+import "@polymer/paper-button/paper-button";
+
+import "../components/hui-generic-entity-row";
+import "../../../components/entity/ha-entity-toggle";
+
+import LocalizeMixin from "../../../mixins/localize-mixin";
+
+/*
+ * @appliesMixin LocalizeMixin
+ */
+class HuiScriptEntityRow extends LocalizeMixin(PolymerElement) {
+ static get template() {
+ return html`
+ ${this.styleTemplate}
+
+ ${this.scriptControlTemplate}
+
+ `;
+ }
+
+ static get styleTemplate() {
+ return html`
+
+ `;
+ }
+
+ static get scriptControlTemplate() {
+ return html`
+
+
+
+
+ [[localize('ui.card.script.execute')]]
+
+ `;
+ }
+
+ static get properties() {
+ return {
+ hass: Object,
+ _config: Object,
+ _stateObj: {
+ type: Object,
+ computed: "_computeStateObj(hass.states, _config.entity)",
+ },
+ };
+ }
+
+ _computeStateObj(states, entityId) {
+ return states && entityId in states ? states[entityId] : null;
+ }
+
+ setConfig(config) {
+ if (!config || !config.entity) {
+ throw new Error("Entity not configured.");
+ }
+ this._config = config;
+ }
+
+ _callService(ev) {
+ ev.stopPropagation();
+ this.hass.callService("script", "turn_on", {
+ entity_id: this._config.entity,
+ });
+ }
+}
+customElements.define("hui-script-entity-row", HuiScriptEntityRow);
diff --git a/src/panels/lovelace/entity-rows/hui-text-entity-row.js b/src/panels/lovelace/entity-rows/hui-text-entity-row.js
index 833ed16edb76..9c78c3a4e962 100644
--- a/src/panels/lovelace/entity-rows/hui-text-entity-row.js
+++ b/src/panels/lovelace/entity-rows/hui-text-entity-row.js
@@ -1,70 +1,70 @@
-import { html } from "@polymer/polymer/lib/utils/html-tag.js";
-import { PolymerElement } from "@polymer/polymer/polymer-element.js";
-
-import "../components/hui-generic-entity-row.js";
-
-import computeStateDisplay from "../../../common/entity/compute_state_display.js";
-
-import LocalizeMixin from "../../../mixins/localize-mixin.js";
-
-/*
- * @appliesMixin LocalizeMixin
- */
-class HuiTextEntityRow extends LocalizeMixin(PolymerElement) {
- static get template() {
- return html`
- ${this.styleTemplate}
-
- ${this.textControlTemplate}
-
- `;
- }
-
- static get styleTemplate() {
- return html`
-
- `;
- }
-
- static get textControlTemplate() {
- return html`
-
- [[_computeState(_stateObj)]]
-
- `;
- }
-
- static get properties() {
- return {
- hass: Object,
- _config: Object,
- _stateObj: {
- type: Object,
- computed: "_computeStateObj(hass.states, _config.entity)",
- },
- };
- }
-
- _computeStateObj(states, entityId) {
- return states && entityId in states ? states[entityId] : null;
- }
-
- setConfig(config) {
- if (!config || !config.entity) {
- throw new Error("Entity not configured.");
- }
- this._config = config;
- }
-
- _computeState(stateObj) {
- return stateObj && computeStateDisplay(this.localize, stateObj);
- }
-}
-customElements.define("hui-text-entity-row", HuiTextEntityRow);
+import { html } from "@polymer/polymer/lib/utils/html-tag";
+import { PolymerElement } from "@polymer/polymer/polymer-element";
+
+import "../components/hui-generic-entity-row";
+
+import computeStateDisplay from "../../../common/entity/compute_state_display";
+
+import LocalizeMixin from "../../../mixins/localize-mixin";
+
+/*
+ * @appliesMixin LocalizeMixin
+ */
+class HuiTextEntityRow extends LocalizeMixin(PolymerElement) {
+ static get template() {
+ return html`
+ ${this.styleTemplate}
+
+ ${this.textControlTemplate}
+
+ `;
+ }
+
+ static get styleTemplate() {
+ return html`
+
+ `;
+ }
+
+ static get textControlTemplate() {
+ return html`
+
+ [[_computeState(_stateObj)]]
+
+ `;
+ }
+
+ static get properties() {
+ return {
+ hass: Object,
+ _config: Object,
+ _stateObj: {
+ type: Object,
+ computed: "_computeStateObj(hass.states, _config.entity)",
+ },
+ };
+ }
+
+ _computeStateObj(states, entityId) {
+ return states && entityId in states ? states[entityId] : null;
+ }
+
+ setConfig(config) {
+ if (!config || !config.entity) {
+ throw new Error("Entity not configured.");
+ }
+ this._config = config;
+ }
+
+ _computeState(stateObj) {
+ return stateObj && computeStateDisplay(this.localize, stateObj);
+ }
+}
+customElements.define("hui-text-entity-row", HuiTextEntityRow);
diff --git a/src/panels/lovelace/entity-rows/hui-timer-entity-row.js b/src/panels/lovelace/entity-rows/hui-timer-entity-row.js
index 7bb836200707..ad9d9791846a 100644
--- a/src/panels/lovelace/entity-rows/hui-timer-entity-row.js
+++ b/src/panels/lovelace/entity-rows/hui-timer-entity-row.js
@@ -1,103 +1,103 @@
-import { html } from "@polymer/polymer/lib/utils/html-tag.js";
-import { PolymerElement } from "@polymer/polymer/polymer-element.js";
-
-import "../components/hui-generic-entity-row.js";
-
-import timerTimeRemaining from "../../../common/entity/timer_time_remaining.js";
-import secondsToDuration from "../../../common/datetime/seconds_to_duration.js";
-
-class HuiTimerEntityRow extends PolymerElement {
- static get template() {
- return html`
-
- ${this.timerControlTemplate}
-
- `;
- }
-
- static get timerControlTemplate() {
- return html`
-
- [[_computeDisplay(_stateObj, _timeRemaining)]]
-
- `;
- }
-
- static get properties() {
- return {
- hass: Object,
- _config: Object,
- _stateObj: {
- type: Object,
- computed: "_computeStateObj(hass.states, _config.entity)",
- observer: "_stateObjChanged",
- },
- _timeRemaining: Number,
- };
- }
-
- disconnectedCallback() {
- super.disconnectedCallback();
- this._clearInterval();
- }
-
- _stateObjChanged(stateObj) {
- if (stateObj) {
- this._startInterval(stateObj);
- } else {
- this._clearInterval();
- }
- }
-
- _clearInterval() {
- if (this._updateRemaining) {
- clearInterval(this._updateRemaining);
- this._updateRemaining = null;
- }
- }
-
- _startInterval(stateObj) {
- this._clearInterval();
- this._calculateRemaining(stateObj);
-
- if (stateObj.state === "active") {
- this._updateRemaining = setInterval(
- () => this._calculateRemaining(this._stateObj),
- 1000
- );
- }
- }
-
- _calculateRemaining(stateObj) {
- this._timeRemaining = timerTimeRemaining(stateObj);
- }
-
- _computeDisplay(stateObj, time) {
- if (!stateObj) return null;
-
- if (stateObj.state === "idle" || time === 0) return stateObj.state;
-
- let display = secondsToDuration(time);
-
- if (stateObj.state === "paused") {
- display += " (paused)";
- }
-
- return display;
- }
-
- _computeStateObj(states, entityId) {
- return states && entityId in states ? states[entityId] : null;
- }
-
- setConfig(config) {
- if (!config || !config.entity) {
- throw new Error("Entity not configured.");
- }
- this._config = config;
- }
-}
-customElements.define("hui-timer-entity-row", HuiTimerEntityRow);
+import { html } from "@polymer/polymer/lib/utils/html-tag";
+import { PolymerElement } from "@polymer/polymer/polymer-element";
+
+import "../components/hui-generic-entity-row";
+
+import timerTimeRemaining from "../../../common/entity/timer_time_remaining";
+import secondsToDuration from "../../../common/datetime/seconds_to_duration";
+
+class HuiTimerEntityRow extends PolymerElement {
+ static get template() {
+ return html`
+
+ ${this.timerControlTemplate}
+
+ `;
+ }
+
+ static get timerControlTemplate() {
+ return html`
+
+ [[_computeDisplay(_stateObj, _timeRemaining)]]
+
+ `;
+ }
+
+ static get properties() {
+ return {
+ hass: Object,
+ _config: Object,
+ _stateObj: {
+ type: Object,
+ computed: "_computeStateObj(hass.states, _config.entity)",
+ observer: "_stateObjChanged",
+ },
+ _timeRemaining: Number,
+ };
+ }
+
+ disconnectedCallback() {
+ super.disconnectedCallback();
+ this._clearInterval();
+ }
+
+ _stateObjChanged(stateObj) {
+ if (stateObj) {
+ this._startInterval(stateObj);
+ } else {
+ this._clearInterval();
+ }
+ }
+
+ _clearInterval() {
+ if (this._updateRemaining) {
+ clearInterval(this._updateRemaining);
+ this._updateRemaining = null;
+ }
+ }
+
+ _startInterval(stateObj) {
+ this._clearInterval();
+ this._calculateRemaining(stateObj);
+
+ if (stateObj.state === "active") {
+ this._updateRemaining = setInterval(
+ () => this._calculateRemaining(this._stateObj),
+ 1000
+ );
+ }
+ }
+
+ _calculateRemaining(stateObj) {
+ this._timeRemaining = timerTimeRemaining(stateObj);
+ }
+
+ _computeDisplay(stateObj, time) {
+ if (!stateObj) return null;
+
+ if (stateObj.state === "idle" || time === 0) return stateObj.state;
+
+ let display = secondsToDuration(time);
+
+ if (stateObj.state === "paused") {
+ display += " (paused)";
+ }
+
+ return display;
+ }
+
+ _computeStateObj(states, entityId) {
+ return states && entityId in states ? states[entityId] : null;
+ }
+
+ setConfig(config) {
+ if (!config || !config.entity) {
+ throw new Error("Entity not configured.");
+ }
+ this._config = config;
+ }
+}
+customElements.define("hui-timer-entity-row", HuiTimerEntityRow);
diff --git a/src/panels/lovelace/entity-rows/hui-toggle-entity-row.js b/src/panels/lovelace/entity-rows/hui-toggle-entity-row.js
index 5f9a6f5a4781..c75055635f8d 100644
--- a/src/panels/lovelace/entity-rows/hui-toggle-entity-row.js
+++ b/src/panels/lovelace/entity-rows/hui-toggle-entity-row.js
@@ -1,76 +1,76 @@
-import { html } from "@polymer/polymer/lib/utils/html-tag.js";
-import { PolymerElement } from "@polymer/polymer/polymer-element.js";
-
-import "../components/hui-generic-entity-row.js";
-import "../../../components/entity/ha-entity-toggle.js";
-
-import computeStateDisplay from "../../../common/entity/compute_state_display.js";
-
-import LocalizeMixin from "../../../mixins/localize-mixin.js";
-
-/*
- * @appliesMixin LocalizeMixin
- */
-class HuiToggleEntityRow extends LocalizeMixin(PolymerElement) {
- static get template() {
- return html`
-
- ${this.toggleControlTemplate}
-
- `;
- }
-
- static get toggleControlTemplate() {
- return html`
-
-
-
-
-
- [[_computeState(_stateObj)]]
-
-
- `;
- }
-
- static get properties() {
- return {
- hass: Object,
- _config: Object,
- _stateObj: {
- type: Object,
- computed: "_computeStateObj(hass.states, _config.entity)",
- },
- _canToggle: {
- type: Boolean,
- computed: "_computeCanToggle(_stateObj.state)",
- },
- };
- }
-
- _computeStateObj(states, entityId) {
- return states && entityId in states ? states[entityId] : null;
- }
-
- _computeCanToggle(state) {
- return state === "on" || state === "off";
- }
-
- _computeState(stateObj) {
- return stateObj && computeStateDisplay(this.localize, stateObj);
- }
-
- setConfig(config) {
- if (!config || !config.entity) {
- throw new Error("Entity not configured.");
- }
- this._config = config;
- }
-}
-customElements.define("hui-toggle-entity-row", HuiToggleEntityRow);
+import { html } from "@polymer/polymer/lib/utils/html-tag";
+import { PolymerElement } from "@polymer/polymer/polymer-element";
+
+import "../components/hui-generic-entity-row";
+import "../../../components/entity/ha-entity-toggle";
+
+import computeStateDisplay from "../../../common/entity/compute_state_display";
+
+import LocalizeMixin from "../../../mixins/localize-mixin";
+
+/*
+ * @appliesMixin LocalizeMixin
+ */
+class HuiToggleEntityRow extends LocalizeMixin(PolymerElement) {
+ static get template() {
+ return html`
+
+ ${this.toggleControlTemplate}
+
+ `;
+ }
+
+ static get toggleControlTemplate() {
+ return html`
+
+
+
+
+
+ [[_computeState(_stateObj)]]
+
+
+ `;
+ }
+
+ static get properties() {
+ return {
+ hass: Object,
+ _config: Object,
+ _stateObj: {
+ type: Object,
+ computed: "_computeStateObj(hass.states, _config.entity)",
+ },
+ _canToggle: {
+ type: Boolean,
+ computed: "_computeCanToggle(_stateObj.state)",
+ },
+ };
+ }
+
+ _computeStateObj(states, entityId) {
+ return states && entityId in states ? states[entityId] : null;
+ }
+
+ _computeCanToggle(state) {
+ return state === "on" || state === "off";
+ }
+
+ _computeState(stateObj) {
+ return stateObj && computeStateDisplay(this.localize, stateObj);
+ }
+
+ setConfig(config) {
+ if (!config || !config.entity) {
+ throw new Error("Entity not configured.");
+ }
+ this._config = config;
+ }
+}
+customElements.define("hui-toggle-entity-row", HuiToggleEntityRow);
diff --git a/src/panels/lovelace/ha-panel-lovelace.js b/src/panels/lovelace/ha-panel-lovelace.js
index 0f4603818031..1d11e2d0f395 100644
--- a/src/panels/lovelace/ha-panel-lovelace.js
+++ b/src/panels/lovelace/ha-panel-lovelace.js
@@ -1,125 +1,125 @@
-import { html } from "@polymer/polymer/lib/utils/html-tag.js";
-import { PolymerElement } from "@polymer/polymer/polymer-element.js";
-import "@polymer/paper-button/paper-button.js";
-
-import "../../layouts/hass-loading-screen.js";
-import "../../layouts/hass-error-screen.js";
-import "./hui-root.js";
-
-class Lovelace extends PolymerElement {
- static get template() {
- return html`
-
-
-
-
-
-
-
-
-
- Reload ui-lovelace.yaml
-
-
- `;
- }
-
- static get properties() {
- return {
- hass: Object,
-
- narrow: {
- type: Boolean,
- value: false,
- },
-
- showMenu: {
- type: Boolean,
- value: false,
- },
-
- route: Object,
-
- _columns: {
- type: Number,
- value: 1,
- },
-
- _state: {
- type: String,
- value: "loading",
- },
-
- _errorMsg: String,
-
- _config: {
- type: Object,
- value: null,
- },
- };
- }
-
- static get observers() {
- return ["_updateColumns(narrow, showMenu)"];
- }
-
- ready() {
- this._fetchConfig();
- this._updateColumns = this._updateColumns.bind(this);
- this.mqls = [300, 600, 900, 1200].map((width) => {
- const mql = matchMedia(`(min-width: ${width}px)`);
- mql.addListener(this._updateColumns);
- return mql;
- });
- this._updateColumns();
- super.ready();
- }
-
- _updateColumns() {
- const matchColumns = this.mqls.reduce((cols, mql) => cols + mql.matches, 0);
- // Do -1 column if the menu is docked and open
- this._columns = Math.max(1, matchColumns - (!this.narrow && this.showMenu));
- }
-
- async _fetchConfig() {
- try {
- const conf = await this.hass.callWS({ type: "lovelace/config" });
- this.setProperties({
- _config: conf,
- _state: "loaded",
- });
- } catch (err) {
- this.setProperties({
- _state: "error",
- _errorMsg: err.message,
- });
- }
- }
-
- _equal(a, b) {
- return a === b;
- }
-}
-
-customElements.define("ha-panel-lovelace", Lovelace);
+import { html } from "@polymer/polymer/lib/utils/html-tag";
+import { PolymerElement } from "@polymer/polymer/polymer-element";
+import "@polymer/paper-button/paper-button";
+
+import "../../layouts/hass-loading-screen";
+import "../../layouts/hass-error-screen";
+import "./hui-root";
+
+class Lovelace extends PolymerElement {
+ static get template() {
+ return html`
+
+
+
+
+
+
+
+
+
+ Reload ui-lovelace.yaml
+
+
+ `;
+ }
+
+ static get properties() {
+ return {
+ hass: Object,
+
+ narrow: {
+ type: Boolean,
+ value: false,
+ },
+
+ showMenu: {
+ type: Boolean,
+ value: false,
+ },
+
+ route: Object,
+
+ _columns: {
+ type: Number,
+ value: 1,
+ },
+
+ _state: {
+ type: String,
+ value: "loading",
+ },
+
+ _errorMsg: String,
+
+ _config: {
+ type: Object,
+ value: null,
+ },
+ };
+ }
+
+ static get observers() {
+ return ["_updateColumns(narrow, showMenu)"];
+ }
+
+ ready() {
+ this._fetchConfig();
+ this._updateColumns = this._updateColumns.bind(this);
+ this.mqls = [300, 600, 900, 1200].map((width) => {
+ const mql = matchMedia(`(min-width: ${width}px)`);
+ mql.addListener(this._updateColumns);
+ return mql;
+ });
+ this._updateColumns();
+ super.ready();
+ }
+
+ _updateColumns() {
+ const matchColumns = this.mqls.reduce((cols, mql) => cols + mql.matches, 0);
+ // Do -1 column if the menu is docked and open
+ this._columns = Math.max(1, matchColumns - (!this.narrow && this.showMenu));
+ }
+
+ async _fetchConfig() {
+ try {
+ const conf = await this.hass.callWS({ type: "lovelace/config" });
+ this.setProperties({
+ _config: conf,
+ _state: "loaded",
+ });
+ } catch (err) {
+ this.setProperties({
+ _state: "error",
+ _errorMsg: err.message,
+ });
+ }
+ }
+
+ _equal(a, b) {
+ return a === b;
+ }
+}
+
+customElements.define("ha-panel-lovelace", Lovelace);
diff --git a/src/panels/lovelace/hui-root.js b/src/panels/lovelace/hui-root.js
index 716e1bbb177a..3e937ca7640d 100644
--- a/src/panels/lovelace/hui-root.js
+++ b/src/panels/lovelace/hui-root.js
@@ -1,380 +1,380 @@
-import "@polymer/app-layout/app-header-layout/app-header-layout.js";
-import "@polymer/app-layout/app-header/app-header.js";
-import "@polymer/app-layout/app-scroll-effects/effects/waterfall.js";
-import "@polymer/app-layout/app-toolbar/app-toolbar.js";
-import "@polymer/app-route/app-route.js";
-import "@polymer/paper-icon-button/paper-icon-button.js";
-import "@polymer/paper-item/paper-item.js";
-import "@polymer/paper-listbox/paper-listbox.js";
-import "@polymer/paper-menu-button/paper-menu-button.js";
-import "@polymer/paper-tabs/paper-tab.js";
-import "@polymer/paper-tabs/paper-tabs.js";
-
-import { html } from "@polymer/polymer/lib/utils/html-tag.js";
-import { PolymerElement } from "@polymer/polymer/polymer-element.js";
-
-import scrollToTarget from "../../common/dom/scroll-to-target.js";
-
-import EventsMixin from "../../mixins/events-mixin.js";
-import NavigateMixin from "../../mixins/navigate-mixin.js";
-
-import "../../layouts/ha-app-layout.js";
-import "../../components/ha-start-voice-button.js";
-import "../../components/ha-icon.js";
-import { loadModule, loadCSS, loadJS } from "../../common/dom/load_resource.js";
-import { subscribeNotifications } from "../../data/ws-notifications";
-import "./components/notifications/hui-notification-drawer.js";
-import "./components/notifications/hui-notifications-button.js";
-import "./hui-unused-entities.js";
-import "./hui-view.js";
-import debounce from "../../common/util/debounce.js";
-
-import createCardElement from "./common/create-card-element.js";
-import computeNotifications from "./common/compute-notifications";
-
-// CSS and JS should only be imported once. Modules and HTML are safe.
-const CSS_CACHE = {};
-const JS_CACHE = {};
-
-class HUIRoot extends NavigateMixin(EventsMixin(PolymerElement)) {
- static get template() {
- return html`
-
-
-
-
-
-
-
-
- [[_computeTitle(config)]]
-
-
-
-
-
- Refresh
- Unused entities
- Configure UI
- Help
-
-
-
-
-
-
-
- Edit UI
-
-
-
-
-
-
-
-
-
-
-
- [[_computeTabTitle(item.title)]]
-
-
-
-
-
-
-
-
-
- `;
- }
-
- static get properties() {
- return {
- narrow: Boolean,
- showMenu: Boolean,
- hass: {
- type: Object,
- observer: "_hassChanged",
- },
- config: {
- type: Object,
- observer: "_configChanged",
- },
- columns: {
- type: Number,
- observer: "_columnsChanged",
- },
-
- _curView: {
- type: Number,
- value: 0,
- },
-
- route: {
- type: Object,
- observer: "_routeChanged",
- },
-
- notificationsOpen: {
- type: Boolean,
- value: false,
- },
-
- _persistentNotifications: {
- type: Array,
- value: [],
- },
-
- _notifications: {
- type: Array,
- computed: "_updateNotifications(hass.states, _persistentNotifications)",
- },
-
- _editMode: {
- type: Boolean,
- value: false,
- observer: "_editModeChanged",
- },
-
- routeData: Object,
- };
- }
-
- constructor() {
- super();
- this._debouncedConfigChanged = debounce(
- () => this._selectView(this._curView),
- 100
- );
- }
-
- connectedCallback() {
- super.connectedCallback();
- this._unsubNotifications = subscribeNotifications(
- this.hass.connection,
- (notifications) => {
- this._persistentNotifications = notifications;
- }
- );
- }
-
- disconnectedCallback() {
- super.disconnectedCallback();
- if (typeof this._unsubNotifications === "function") {
- this._unsubNotifications();
- }
- }
-
- _updateNotifications(states, persistent) {
- if (!states) return persistent;
-
- const configurator = computeNotifications(states);
- return persistent.concat(configurator);
- }
-
- _routeChanged(route) {
- const views = this.config && this.config.views;
- if (route.path === "" && route.prefix === "/lovelace" && views) {
- this.navigate(`/lovelace/${views[0].id || 0}`, true);
- } else if (this.routeData.view) {
- const view = this.routeData.view;
- let index = 0;
- for (let i = 0; i < views.length; i++) {
- if (views[i].id === view || i === parseInt(view)) {
- index = i;
- break;
- }
- }
- if (index !== this._curView) this._selectView(index);
- }
- }
-
- _computeViewId(id, index) {
- return id || index;
- }
-
- _computeTitle(config) {
- return config.title || "Home Assistant";
- }
-
- _computeTabsHidden(views) {
- return views.length < 2;
- }
-
- _computeTabTitle(title) {
- return title || "Unnamed view";
- }
-
- _handleRefresh() {
- this.fire("config-refresh");
- }
-
- _handleUnusedEntities() {
- this._selectView("unused");
- }
-
- _deselect(ev) {
- ev.target.selected = null;
- }
-
- _handleHelp() {
- window.open("https://www.home-assistant.io/lovelace/", "_blank");
- }
-
- _editModeEnable() {
- this._editMode = true;
- }
-
- _editModeDisable() {
- this._editMode = false;
- }
-
- _editModeChanged() {
- this._selectView(this._curView);
- }
-
- _handleViewSelected(ev) {
- const index = ev.detail.selected;
- if (index !== this._curView) {
- const id = this.config.views[index].id || index;
- this.navigate(`/lovelace/${id}`);
- }
- scrollToTarget(this, this.$.layout.header.scrollTarget);
- }
-
- _selectView(viewIndex) {
- this._curView = viewIndex;
-
- // Recreate a new element to clear the applied themes.
- const root = this.$.view;
- if (root.lastChild) {
- root.removeChild(root.lastChild);
- }
-
- let view;
- let background = this.config.background || "";
-
- if (viewIndex === "unused") {
- view = document.createElement("hui-unused-entities");
- view.config = this.config;
- } else {
- const viewConfig = this.config.views[this._curView];
- if (viewConfig.panel) {
- view = createCardElement(viewConfig.cards[0]);
- view.isPanel = true;
- view.editMode = this._editMode;
- } else {
- view = document.createElement("hui-view");
- view.config = viewConfig;
- view.columns = this.columns;
- view.editMode = this._editMode;
- }
- if (viewConfig.background) background = viewConfig.background;
- }
-
- this.$.view.style.background = background;
-
- view.hass = this.hass;
- root.appendChild(view);
- }
-
- _hassChanged(hass) {
- if (!this.$.view.lastChild) return;
- this.$.view.lastChild.hass = hass;
- }
-
- _configChanged(config) {
- this._loadResources(config.resources || []);
- // On config change, recreate the view from scratch.
- this._selectView(this._curView);
- this.$.view.classList.toggle("tabs-hidden", config.views.length < 2);
- }
-
- _columnsChanged(columns) {
- if (!this.$.view.lastChild) return;
- this.$.view.lastChild.columns = columns;
- }
-
- _loadResources(resources) {
- resources.forEach((resource) => {
- switch (resource.type) {
- case "css":
- if (resource.url in CSS_CACHE) break;
- CSS_CACHE[resource.url] = loadCSS(resource.url);
- break;
-
- case "js":
- if (resource.url in JS_CACHE) break;
- JS_CACHE[resource.url] = loadJS(resource.url);
- break;
-
- case "module":
- loadModule(resource.url);
- break;
-
- case "html":
- import(/* webpackChunkName: "import-href-polyfill" */ "../../resources/html-import/import-href.js").then(
- ({ importHref }) => importHref(resource.url)
- );
- break;
-
- default:
- // eslint-disable-next-line
- console.warn("Unknown resource type specified: ${resource.type}");
- }
- });
- }
-}
-customElements.define("hui-root", HUIRoot);
+import "@polymer/app-layout/app-header-layout/app-header-layout";
+import "@polymer/app-layout/app-header/app-header";
+import "@polymer/app-layout/app-scroll-effects/effects/waterfall";
+import "@polymer/app-layout/app-toolbar/app-toolbar";
+import "@polymer/app-route/app-route";
+import "@polymer/paper-icon-button/paper-icon-button";
+import "@polymer/paper-item/paper-item";
+import "@polymer/paper-listbox/paper-listbox";
+import "@polymer/paper-menu-button/paper-menu-button";
+import "@polymer/paper-tabs/paper-tab";
+import "@polymer/paper-tabs/paper-tabs";
+
+import { html } from "@polymer/polymer/lib/utils/html-tag";
+import { PolymerElement } from "@polymer/polymer/polymer-element";
+
+import scrollToTarget from "../../common/dom/scroll-to-target";
+
+import EventsMixin from "../../mixins/events-mixin";
+import NavigateMixin from "../../mixins/navigate-mixin";
+
+import "../../layouts/ha-app-layout";
+import "../../components/ha-start-voice-button";
+import "../../components/ha-icon";
+import { loadModule, loadCSS, loadJS } from "../../common/dom/load_resource";
+import { subscribeNotifications } from "../../data/ws-notifications";
+import "./components/notifications/hui-notification-drawer";
+import "./components/notifications/hui-notifications-button";
+import "./hui-unused-entities";
+import "./hui-view";
+import debounce from "../../common/util/debounce";
+
+import createCardElement from "./common/create-card-element";
+import computeNotifications from "./common/compute-notifications";
+
+// CSS and JS should only be imported once. Modules and HTML are safe.
+const CSS_CACHE = {};
+const JS_CACHE = {};
+
+class HUIRoot extends NavigateMixin(EventsMixin(PolymerElement)) {
+ static get template() {
+ return html`
+
+
+
+
+
+
+
+
+ [[_computeTitle(config)]]
+
+
+
+
+
+ Refresh
+ Unused entities
+ Configure UI
+ Help
+
+
+
+
+
+
+
+ Edit UI
+
+
+
+
+
+
+
+
+
+
+
+ [[_computeTabTitle(item.title)]]
+
+
+
+
+
+
+
+
+
+ `;
+ }
+
+ static get properties() {
+ return {
+ narrow: Boolean,
+ showMenu: Boolean,
+ hass: {
+ type: Object,
+ observer: "_hassChanged",
+ },
+ config: {
+ type: Object,
+ observer: "_configChanged",
+ },
+ columns: {
+ type: Number,
+ observer: "_columnsChanged",
+ },
+
+ _curView: {
+ type: Number,
+ value: 0,
+ },
+
+ route: {
+ type: Object,
+ observer: "_routeChanged",
+ },
+
+ notificationsOpen: {
+ type: Boolean,
+ value: false,
+ },
+
+ _persistentNotifications: {
+ type: Array,
+ value: [],
+ },
+
+ _notifications: {
+ type: Array,
+ computed: "_updateNotifications(hass.states, _persistentNotifications)",
+ },
+
+ _editMode: {
+ type: Boolean,
+ value: false,
+ observer: "_editModeChanged",
+ },
+
+ routeData: Object,
+ };
+ }
+
+ constructor() {
+ super();
+ this._debouncedConfigChanged = debounce(
+ () => this._selectView(this._curView),
+ 100
+ );
+ }
+
+ connectedCallback() {
+ super.connectedCallback();
+ this._unsubNotifications = subscribeNotifications(
+ this.hass.connection,
+ (notifications) => {
+ this._persistentNotifications = notifications;
+ }
+ );
+ }
+
+ disconnectedCallback() {
+ super.disconnectedCallback();
+ if (typeof this._unsubNotifications === "function") {
+ this._unsubNotifications();
+ }
+ }
+
+ _updateNotifications(states, persistent) {
+ if (!states) return persistent;
+
+ const configurator = computeNotifications(states);
+ return persistent.concat(configurator);
+ }
+
+ _routeChanged(route) {
+ const views = this.config && this.config.views;
+ if (route.path === "" && route.prefix === "/lovelace" && views) {
+ this.navigate(`/lovelace/${views[0].id || 0}`, true);
+ } else if (this.routeData.view) {
+ const view = this.routeData.view;
+ let index = 0;
+ for (let i = 0; i < views.length; i++) {
+ if (views[i].id === view || i === parseInt(view)) {
+ index = i;
+ break;
+ }
+ }
+ if (index !== this._curView) this._selectView(index);
+ }
+ }
+
+ _computeViewId(id, index) {
+ return id || index;
+ }
+
+ _computeTitle(config) {
+ return config.title || "Home Assistant";
+ }
+
+ _computeTabsHidden(views) {
+ return views.length < 2;
+ }
+
+ _computeTabTitle(title) {
+ return title || "Unnamed view";
+ }
+
+ _handleRefresh() {
+ this.fire("config-refresh");
+ }
+
+ _handleUnusedEntities() {
+ this._selectView("unused");
+ }
+
+ _deselect(ev) {
+ ev.target.selected = null;
+ }
+
+ _handleHelp() {
+ window.open("https://www.home-assistant.io/lovelace/", "_blank");
+ }
+
+ _editModeEnable() {
+ this._editMode = true;
+ }
+
+ _editModeDisable() {
+ this._editMode = false;
+ }
+
+ _editModeChanged() {
+ this._selectView(this._curView);
+ }
+
+ _handleViewSelected(ev) {
+ const index = ev.detail.selected;
+ if (index !== this._curView) {
+ const id = this.config.views[index].id || index;
+ this.navigate(`/lovelace/${id}`);
+ }
+ scrollToTarget(this, this.$.layout.header.scrollTarget);
+ }
+
+ _selectView(viewIndex) {
+ this._curView = viewIndex;
+
+ // Recreate a new element to clear the applied themes.
+ const root = this.$.view;
+ if (root.lastChild) {
+ root.removeChild(root.lastChild);
+ }
+
+ let view;
+ let background = this.config.background || "";
+
+ if (viewIndex === "unused") {
+ view = document.createElement("hui-unused-entities");
+ view.config = this.config;
+ } else {
+ const viewConfig = this.config.views[this._curView];
+ if (viewConfig.panel) {
+ view = createCardElement(viewConfig.cards[0]);
+ view.isPanel = true;
+ view.editMode = this._editMode;
+ } else {
+ view = document.createElement("hui-view");
+ view.config = viewConfig;
+ view.columns = this.columns;
+ view.editMode = this._editMode;
+ }
+ if (viewConfig.background) background = viewConfig.background;
+ }
+
+ this.$.view.style.background = background;
+
+ view.hass = this.hass;
+ root.appendChild(view);
+ }
+
+ _hassChanged(hass) {
+ if (!this.$.view.lastChild) return;
+ this.$.view.lastChild.hass = hass;
+ }
+
+ _configChanged(config) {
+ this._loadResources(config.resources || []);
+ // On config change, recreate the view from scratch.
+ this._selectView(this._curView);
+ this.$.view.classList.toggle("tabs-hidden", config.views.length < 2);
+ }
+
+ _columnsChanged(columns) {
+ if (!this.$.view.lastChild) return;
+ this.$.view.lastChild.columns = columns;
+ }
+
+ _loadResources(resources) {
+ resources.forEach((resource) => {
+ switch (resource.type) {
+ case "css":
+ if (resource.url in CSS_CACHE) break;
+ CSS_CACHE[resource.url] = loadCSS(resource.url);
+ break;
+
+ case "js":
+ if (resource.url in JS_CACHE) break;
+ JS_CACHE[resource.url] = loadJS(resource.url);
+ break;
+
+ case "module":
+ loadModule(resource.url);
+ break;
+
+ case "html":
+ import(/* webpackChunkName: "import-href-polyfill" */ "../../resources/html-import/import-href").then(
+ ({ importHref }) => importHref(resource.url)
+ );
+ break;
+
+ default:
+ // eslint-disable-next-line
+ console.warn("Unknown resource type specified: ${resource.type}");
+ }
+ });
+ }
+}
+customElements.define("hui-root", HUIRoot);
diff --git a/src/panels/lovelace/hui-unused-entities.js b/src/panels/lovelace/hui-unused-entities.js
index 9b9e318954fb..7b066f9269e3 100644
--- a/src/panels/lovelace/hui-unused-entities.js
+++ b/src/panels/lovelace/hui-unused-entities.js
@@ -1,61 +1,61 @@
-import { html } from "@polymer/polymer/lib/utils/html-tag.js";
-import { PolymerElement } from "@polymer/polymer/polymer-element.js";
-
-import computeUnusedEntities from "./common/compute-unused-entities.js";
-import createCardElement from "./common/create-card-element.js";
-
-import "./cards/hui-entities-card.ts";
-
-class HuiUnusedEntities extends PolymerElement {
- static get template() {
- return html`
-
-
- `;
- }
-
- static get properties() {
- return {
- hass: {
- type: Object,
- observer: "_hassChanged",
- },
- config: {
- type: Object,
- observer: "_configChanged",
- },
- };
- }
-
- _configChanged(config) {
- const root = this.$.root;
- if (root.lastChild) root.removeChild(root.lastChild);
-
- const entities = computeUnusedEntities(this.hass, config).map((entity) => ({
- entity,
- secondary_info: "entity-id",
- }));
- const cardConfig = {
- type: "entities",
- title: "Unused entities",
- entities,
- show_header_toggle: false,
- };
- const element = createCardElement(cardConfig);
- element.hass = this.hass;
- root.appendChild(element);
- }
-
- _hassChanged(hass) {
- const root = this.$.root;
- if (!root.lastChild) return;
- root.lastChild.hass = hass;
- }
-}
-customElements.define("hui-unused-entities", HuiUnusedEntities);
+import { html } from "@polymer/polymer/lib/utils/html-tag";
+import { PolymerElement } from "@polymer/polymer/polymer-element";
+
+import computeUnusedEntities from "./common/compute-unused-entities";
+import createCardElement from "./common/create-card-element";
+
+import "./cards/hui-entities-card.ts";
+
+class HuiUnusedEntities extends PolymerElement {
+ static get template() {
+ return html`
+
+
+ `;
+ }
+
+ static get properties() {
+ return {
+ hass: {
+ type: Object,
+ observer: "_hassChanged",
+ },
+ config: {
+ type: Object,
+ observer: "_configChanged",
+ },
+ };
+ }
+
+ _configChanged(config) {
+ const root = this.$.root;
+ if (root.lastChild) root.removeChild(root.lastChild);
+
+ const entities = computeUnusedEntities(this.hass, config).map((entity) => ({
+ entity,
+ secondary_info: "entity-id",
+ }));
+ const cardConfig = {
+ type: "entities",
+ title: "Unused entities",
+ entities,
+ show_header_toggle: false,
+ };
+ const element = createCardElement(cardConfig);
+ element.hass = this.hass;
+ root.appendChild(element);
+ }
+
+ _hassChanged(hass) {
+ const root = this.$.root;
+ if (!root.lastChild) return;
+ root.lastChild.hass = hass;
+ }
+}
+customElements.define("hui-unused-entities", HuiUnusedEntities);
diff --git a/src/panels/lovelace/hui-view.js b/src/panels/lovelace/hui-view.js
index bc334390183c..91931c71ed0a 100644
--- a/src/panels/lovelace/hui-view.js
+++ b/src/panels/lovelace/hui-view.js
@@ -1,218 +1,218 @@
-import { html } from "@polymer/polymer/lib/utils/html-tag.js";
-import { PolymerElement } from "@polymer/polymer/polymer-element.js";
-
-import "../../components/entity/ha-state-label-badge.js";
-import "./components/hui-card-options.ts";
-
-import applyThemesOnElement from "../../common/dom/apply_themes_on_element.js";
-
-import createCardElement from "./common/create-card-element";
-import computeCardSize from "./common/compute-card-size";
-
-class HUIView extends PolymerElement {
- static get template() {
- return html`
-
-
-
- `;
- }
-
- static get properties() {
- return {
- hass: {
- type: Object,
- observer: "_hassChanged",
- },
- config: Object,
- columns: Number,
- editMode: Boolean,
- };
- }
-
- static get observers() {
- return [
- // Put all properties in 1 observer so we only call configChanged once
- "_createBadges(config)",
- "_createCards(config, columns, editMode)",
- ];
- }
-
- constructor() {
- super();
- this._cards = [];
- this._badges = [];
- }
-
- _createBadges(config) {
- const root = this.$.badges;
- while (root.lastChild) {
- root.removeChild(root.lastChild);
- }
-
- if (!config || !config.badges || !Array.isArray(config.badges)) {
- root.style.display = "none";
- this._badges = [];
- return;
- }
-
- const elements = [];
- for (const entityId of config.badges) {
- if (!(entityId in this.hass.states)) continue;
-
- const element = document.createElement("ha-state-label-badge");
- element.setProperties({
- hass: this.hass,
- state: this.hass.states[entityId],
- });
- elements.push({ element, entityId });
- root.appendChild(element);
- }
- this._badges = elements;
- root.style.display = elements.length > 0 ? "block" : "none";
- }
-
- _createCards(config) {
- const root = this.$.columns;
-
- while (root.lastChild) {
- root.removeChild(root.lastChild);
- }
-
- if (!config || !config.cards || !Array.isArray(config.cards)) {
- this._cards = [];
- return;
- }
-
- const elements = [];
- const elementsToAppend = [];
- for (const cardConfig of config.cards) {
- const element = createCardElement(cardConfig);
- element.hass = this.hass;
- elements.push(element);
-
- if (!this.editMode) {
- elementsToAppend.push(element);
- continue;
- }
-
- const wrapper = document.createElement("hui-card-options");
- wrapper.hass = this.hass;
- wrapper.cardId = cardConfig.id;
- wrapper.editMode = this.editMode;
- wrapper.appendChild(element);
- elementsToAppend.push(wrapper);
- }
-
- let columns = [];
- const columnEntityCount = [];
- for (let i = 0; i < this.columns; i++) {
- columns.push([]);
- columnEntityCount.push(0);
- }
-
- // Find column with < 5 entities, else column with lowest count
- function getColumnIndex(size) {
- let minIndex = 0;
- for (let i = 0; i < columnEntityCount.length; i++) {
- if (columnEntityCount[i] < 5) {
- minIndex = i;
- break;
- }
- if (columnEntityCount[i] < columnEntityCount[minIndex]) {
- minIndex = i;
- }
- }
-
- columnEntityCount[minIndex] += size;
-
- return minIndex;
- }
-
- elements.forEach((el, index) => {
- const cardSize = computeCardSize(el);
- // Element to append might be the wrapped card when we're editing.
- columns[getColumnIndex(cardSize)].push(elementsToAppend[index]);
- });
-
- // Remove empty columns
- columns = columns.filter((val) => val.length > 0);
-
- columns.forEach((column) => {
- const columnEl = document.createElement("div");
- columnEl.classList.add("column");
- column.forEach((el) => columnEl.appendChild(el));
- root.appendChild(columnEl);
- });
-
- this._cards = elements;
-
- if ("theme" in config) {
- applyThemesOnElement(root, this.hass.themes, config.theme);
- }
- }
-
- _hassChanged(hass) {
- this._badges.forEach((badge) => {
- const { element, entityId } = badge;
- element.setProperties({
- hass,
- state: hass.states[entityId],
- });
- });
- this._cards.forEach((element) => {
- element.hass = hass;
- });
- }
-}
-
-customElements.define("hui-view", HUIView);
+import { html } from "@polymer/polymer/lib/utils/html-tag";
+import { PolymerElement } from "@polymer/polymer/polymer-element";
+
+import "../../components/entity/ha-state-label-badge";
+import "./components/hui-card-options.ts";
+
+import applyThemesOnElement from "../../common/dom/apply_themes_on_element";
+
+import createCardElement from "./common/create-card-element";
+import computeCardSize from "./common/compute-card-size";
+
+class HUIView extends PolymerElement {
+ static get template() {
+ return html`
+
+
+
+ `;
+ }
+
+ static get properties() {
+ return {
+ hass: {
+ type: Object,
+ observer: "_hassChanged",
+ },
+ config: Object,
+ columns: Number,
+ editMode: Boolean,
+ };
+ }
+
+ static get observers() {
+ return [
+ // Put all properties in 1 observer so we only call configChanged once
+ "_createBadges(config)",
+ "_createCards(config, columns, editMode)",
+ ];
+ }
+
+ constructor() {
+ super();
+ this._cards = [];
+ this._badges = [];
+ }
+
+ _createBadges(config) {
+ const root = this.$.badges;
+ while (root.lastChild) {
+ root.removeChild(root.lastChild);
+ }
+
+ if (!config || !config.badges || !Array.isArray(config.badges)) {
+ root.style.display = "none";
+ this._badges = [];
+ return;
+ }
+
+ const elements = [];
+ for (const entityId of config.badges) {
+ if (!(entityId in this.hass.states)) continue;
+
+ const element = document.createElement("ha-state-label-badge");
+ element.setProperties({
+ hass: this.hass,
+ state: this.hass.states[entityId],
+ });
+ elements.push({ element, entityId });
+ root.appendChild(element);
+ }
+ this._badges = elements;
+ root.style.display = elements.length > 0 ? "block" : "none";
+ }
+
+ _createCards(config) {
+ const root = this.$.columns;
+
+ while (root.lastChild) {
+ root.removeChild(root.lastChild);
+ }
+
+ if (!config || !config.cards || !Array.isArray(config.cards)) {
+ this._cards = [];
+ return;
+ }
+
+ const elements = [];
+ const elementsToAppend = [];
+ for (const cardConfig of config.cards) {
+ const element = createCardElement(cardConfig);
+ element.hass = this.hass;
+ elements.push(element);
+
+ if (!this.editMode) {
+ elementsToAppend.push(element);
+ continue;
+ }
+
+ const wrapper = document.createElement("hui-card-options");
+ wrapper.hass = this.hass;
+ wrapper.cardId = cardConfig.id;
+ wrapper.editMode = this.editMode;
+ wrapper.appendChild(element);
+ elementsToAppend.push(wrapper);
+ }
+
+ let columns = [];
+ const columnEntityCount = [];
+ for (let i = 0; i < this.columns; i++) {
+ columns.push([]);
+ columnEntityCount.push(0);
+ }
+
+ // Find column with < 5 entities, else column with lowest count
+ function getColumnIndex(size) {
+ let minIndex = 0;
+ for (let i = 0; i < columnEntityCount.length; i++) {
+ if (columnEntityCount[i] < 5) {
+ minIndex = i;
+ break;
+ }
+ if (columnEntityCount[i] < columnEntityCount[minIndex]) {
+ minIndex = i;
+ }
+ }
+
+ columnEntityCount[minIndex] += size;
+
+ return minIndex;
+ }
+
+ elements.forEach((el, index) => {
+ const cardSize = computeCardSize(el);
+ // Element to append might be the wrapped card when we're editing.
+ columns[getColumnIndex(cardSize)].push(elementsToAppend[index]);
+ });
+
+ // Remove empty columns
+ columns = columns.filter((val) => val.length > 0);
+
+ columns.forEach((column) => {
+ const columnEl = document.createElement("div");
+ columnEl.classList.add("column");
+ column.forEach((el) => columnEl.appendChild(el));
+ root.appendChild(columnEl);
+ });
+
+ this._cards = elements;
+
+ if ("theme" in config) {
+ applyThemesOnElement(root, this.hass.themes, config.theme);
+ }
+ }
+
+ _hassChanged(hass) {
+ this._badges.forEach((badge) => {
+ const { element, entityId } = badge;
+ element.setProperties({
+ hass,
+ state: hass.states[entityId],
+ });
+ });
+ this._cards.forEach((element) => {
+ element.hass = hass;
+ });
+ }
+}
+
+customElements.define("hui-view", HUIView);
diff --git a/src/panels/lovelace/special-rows/hui-call-service-row.ts b/src/panels/lovelace/special-rows/hui-call-service-row.ts
index 1a06ad90ab1e..44c7f2354cfb 100644
--- a/src/panels/lovelace/special-rows/hui-call-service-row.ts
+++ b/src/panels/lovelace/special-rows/hui-call-service-row.ts
@@ -1,11 +1,11 @@
import { html, LitElement } from "@polymer/lit-element";
-import "@polymer/paper-button/paper-button.js";
+import "@polymer/paper-button/paper-button";
-import "../../../components/ha-icon.js";
+import "../../../components/ha-icon";
-import callService from "../common/call-service.js";
-import { EntityRow, CallServiceConfig } from "../entity-rows/types.js";
-import { HomeAssistant } from "../../../types.js";
+import callService from "../common/call-service";
+import { EntityRow, CallServiceConfig } from "../entity-rows/types";
+import { HomeAssistant } from "../../../types";
import { TemplateResult } from "lit-html";
class HuiCallServiceRow extends LitElement implements EntityRow {
diff --git a/src/panels/lovelace/special-rows/hui-section-row.ts b/src/panels/lovelace/special-rows/hui-section-row.ts
index f590c4d96d4e..9033755203a6 100644
--- a/src/panels/lovelace/special-rows/hui-section-row.ts
+++ b/src/panels/lovelace/special-rows/hui-section-row.ts
@@ -2,7 +2,7 @@ import { html, LitElement } from "@polymer/lit-element";
import { EntityRow, SectionConfig } from "../entity-rows/types";
import { HomeAssistant } from "../../../types";
-import "../../../components/ha-icon.js";
+import "../../../components/ha-icon";
import { TemplateResult } from "lit-html";
class HuiSectionRow extends LitElement implements EntityRow {
diff --git a/src/panels/lovelace/special-rows/hui-weblink-row.ts b/src/panels/lovelace/special-rows/hui-weblink-row.ts
index e49690ece5db..a9ff56c4007a 100644
--- a/src/panels/lovelace/special-rows/hui-weblink-row.ts
+++ b/src/panels/lovelace/special-rows/hui-weblink-row.ts
@@ -2,7 +2,7 @@ import { html, LitElement } from "@polymer/lit-element";
import { EntityRow, WeblinkConfig } from "../entity-rows/types";
import { HomeAssistant } from "../../../types";
-import "../../../components/ha-icon.js";
+import "../../../components/ha-icon";
import { TemplateResult } from "lit-html";
diff --git a/src/panels/mailbox/ha-dialog-show-audio-message.js b/src/panels/mailbox/ha-dialog-show-audio-message.js
index 6e76b9c629f8..68401578fc7a 100644
--- a/src/panels/mailbox/ha-dialog-show-audio-message.js
+++ b/src/panels/mailbox/ha-dialog-show-audio-message.js
@@ -1,12 +1,12 @@
-import "@polymer/paper-button/paper-button.js";
-import "@polymer/paper-dialog/paper-dialog.js";
-import "@polymer/paper-spinner/paper-spinner.js";
-import { html } from "@polymer/polymer/lib/utils/html-tag.js";
-import { PolymerElement } from "@polymer/polymer/polymer-element.js";
+import "@polymer/paper-button/paper-button";
+import "@polymer/paper-dialog/paper-dialog";
+import "@polymer/paper-spinner/paper-spinner";
+import { html } from "@polymer/polymer/lib/utils/html-tag";
+import { PolymerElement } from "@polymer/polymer/polymer-element";
-import "../../resources/ha-style.js";
+import "../../resources/ha-style";
-import LocalizeMixin from "../../mixins/localize-mixin.js";
+import LocalizeMixin from "../../mixins/localize-mixin";
/*
* @appliesMixin LocalizeMixin
diff --git a/src/panels/mailbox/ha-panel-mailbox.js b/src/panels/mailbox/ha-panel-mailbox.js
index c3909ee19275..deb955d89254 100644
--- a/src/panels/mailbox/ha-panel-mailbox.js
+++ b/src/panels/mailbox/ha-panel-mailbox.js
@@ -1,22 +1,22 @@
-import "@polymer/app-layout/app-header-layout/app-header-layout.js";
-import "@polymer/app-layout/app-header/app-header.js";
-import "@polymer/app-layout/app-toolbar/app-toolbar.js";
-import "@polymer/paper-button/paper-button.js";
-import "@polymer/paper-card/paper-card.js";
-import "@polymer/paper-input/paper-textarea.js";
-import "@polymer/paper-item/paper-item-body.js";
-import "@polymer/paper-item/paper-item.js";
-import "@polymer/paper-tabs/paper-tab.js";
-import "@polymer/paper-tabs/paper-tabs.js";
-import { html } from "@polymer/polymer/lib/utils/html-tag.js";
-import { PolymerElement } from "@polymer/polymer/polymer-element.js";
+import "@polymer/app-layout/app-header-layout/app-header-layout";
+import "@polymer/app-layout/app-header/app-header";
+import "@polymer/app-layout/app-toolbar/app-toolbar";
+import "@polymer/paper-button/paper-button";
+import "@polymer/paper-card/paper-card";
+import "@polymer/paper-input/paper-textarea";
+import "@polymer/paper-item/paper-item-body";
+import "@polymer/paper-item/paper-item";
+import "@polymer/paper-tabs/paper-tab";
+import "@polymer/paper-tabs/paper-tabs";
+import { html } from "@polymer/polymer/lib/utils/html-tag";
+import { PolymerElement } from "@polymer/polymer/polymer-element";
-import "../../components/ha-menu-button.js";
-import "../../resources/ha-style.js";
+import "../../components/ha-menu-button";
+import "../../resources/ha-style";
-import formatDateTime from "../../common/datetime/format_date_time.js";
-import LocalizeMixin from "../../mixins/localize-mixin.js";
-import EventsMixin from "../../mixins/events-mixin.js";
+import formatDateTime from "../../common/datetime/format_date_time";
+import LocalizeMixin from "../../mixins/localize-mixin";
+import EventsMixin from "../../mixins/events-mixin";
let registeredDialog = false;
@@ -161,7 +161,7 @@ class HaPanelMailbox extends EventsMixin(LocalizeMixin(PolymerElement)) {
this.fire("register-dialog", {
dialogShowEvent: "show-audio-message-dialog",
dialogTag: "ha-dialog-show-audio-message",
- dialogImport: () => import("./ha-dialog-show-audio-message.js"),
+ dialogImport: () => import("./ha-dialog-show-audio-message"),
});
}
this.hassChanged = this.hassChanged.bind(this);
diff --git a/src/panels/map/ha-entity-marker.js b/src/panels/map/ha-entity-marker.js
index 9101ecd2dafc..a575f22418aa 100644
--- a/src/panels/map/ha-entity-marker.js
+++ b/src/panels/map/ha-entity-marker.js
@@ -1,8 +1,8 @@
-import "@polymer/iron-image/iron-image.js";
-import { html } from "@polymer/polymer/lib/utils/html-tag.js";
-import { PolymerElement } from "@polymer/polymer/polymer-element.js";
+import "@polymer/iron-image/iron-image";
+import { html } from "@polymer/polymer/lib/utils/html-tag";
+import { PolymerElement } from "@polymer/polymer/polymer-element";
-import EventsMixin from "../../mixins/events-mixin.js";
+import EventsMixin from "../../mixins/events-mixin";
/*
* @appliesMixin EventsMixin
diff --git a/src/panels/map/ha-panel-map.js b/src/panels/map/ha-panel-map.js
index 09410d08caf0..bb69bf441610 100644
--- a/src/panels/map/ha-panel-map.js
+++ b/src/panels/map/ha-panel-map.js
@@ -1,17 +1,17 @@
-import "@polymer/app-layout/app-toolbar/app-toolbar.js";
-import { html } from "@polymer/polymer/lib/utils/html-tag.js";
-import { PolymerElement } from "@polymer/polymer/polymer-element.js";
+import "@polymer/app-layout/app-toolbar/app-toolbar";
+import { html } from "@polymer/polymer/lib/utils/html-tag";
+import { PolymerElement } from "@polymer/polymer/polymer-element";
import Leaflet from "leaflet";
-import "../../components/ha-menu-button.js";
-import "../../components/ha-icon.js";
+import "../../components/ha-menu-button";
+import "../../components/ha-icon";
-import "./ha-entity-marker.js";
+import "./ha-entity-marker";
-import computeStateDomain from "../../common/entity/compute_state_domain.js";
-import computeStateName from "../../common/entity/compute_state_name.js";
-import LocalizeMixin from "../../mixins/localize-mixin.js";
-import setupLeafletMap from "../../common/dom/setup-leaflet-map.js";
+import computeStateDomain from "../../common/entity/compute_state_domain";
+import computeStateName from "../../common/entity/compute_state_name";
+import LocalizeMixin from "../../mixins/localize-mixin";
+import setupLeafletMap from "../../common/dom/setup-leaflet-map";
Leaflet.Icon.Default.imagePath = "/static/images/leaflet";
diff --git a/src/panels/profile/ha-change-password-card.js b/src/panels/profile/ha-change-password-card.js
index cb4eac115d36..8511669f6fb8 100644
--- a/src/panels/profile/ha-change-password-card.js
+++ b/src/panels/profile/ha-change-password-card.js
@@ -1,13 +1,13 @@
-import "@polymer/paper-button/paper-button.js";
-import "@polymer/paper-dialog/paper-dialog.js";
-import "@polymer/paper-spinner/paper-spinner.js";
-import "@polymer/paper-card/paper-card.js";
-import { html } from "@polymer/polymer/lib/utils/html-tag.js";
-import { PolymerElement } from "@polymer/polymer/polymer-element.js";
+import "@polymer/paper-button/paper-button";
+import "@polymer/paper-dialog/paper-dialog";
+import "@polymer/paper-spinner/paper-spinner";
+import "@polymer/paper-card/paper-card";
+import { html } from "@polymer/polymer/lib/utils/html-tag";
+import { PolymerElement } from "@polymer/polymer/polymer-element";
-import LocalizeMixin from "../../mixins/localize-mixin.js";
+import LocalizeMixin from "../../mixins/localize-mixin";
-import "../../resources/ha-style.js";
+import "../../resources/ha-style";
/*
* @appliesMixin LocalizeMixin
diff --git a/src/panels/profile/ha-long-lived-access-tokens-card.js b/src/panels/profile/ha-long-lived-access-tokens-card.js
index 076a88825159..a6843d5e6acc 100644
--- a/src/panels/profile/ha-long-lived-access-tokens-card.js
+++ b/src/panels/profile/ha-long-lived-access-tokens-card.js
@@ -1,14 +1,14 @@
-import "@polymer/paper-button/paper-button.js";
+import "@polymer/paper-button/paper-button";
-import { html } from "@polymer/polymer/lib/utils/html-tag.js";
-import { PolymerElement } from "@polymer/polymer/polymer-element.js";
-import EventsMixin from "../../mixins/events-mixin.js";
-import LocalizeMixin from "../../mixins/localize-mixin.js";
-import formatDateTime from "../../common/datetime/format_date_time.js";
+import { html } from "@polymer/polymer/lib/utils/html-tag";
+import { PolymerElement } from "@polymer/polymer/polymer-element";
+import EventsMixin from "../../mixins/events-mixin";
+import LocalizeMixin from "../../mixins/localize-mixin";
+import formatDateTime from "../../common/datetime/format_date_time";
-import "../../resources/ha-style.js";
+import "../../resources/ha-style";
-import "./ha-settings-row.js";
+import "./ha-settings-row";
/*
* @appliesMixin EventsMixin
diff --git a/src/panels/profile/ha-mfa-module-setup-flow.js b/src/panels/profile/ha-mfa-module-setup-flow.js
index 08ea6c958115..c98b02488c08 100644
--- a/src/panels/profile/ha-mfa-module-setup-flow.js
+++ b/src/panels/profile/ha-mfa-module-setup-flow.js
@@ -1,16 +1,16 @@
-import "@polymer/paper-button/paper-button.js";
-import "@polymer/paper-dialog-scrollable/paper-dialog-scrollable.js";
-import "@polymer/paper-dialog/paper-dialog.js";
-import "@polymer/paper-spinner/paper-spinner.js";
-import { html } from "@polymer/polymer/lib/utils/html-tag.js";
-import { PolymerElement } from "@polymer/polymer/polymer-element.js";
-
-import "../../components/ha-form.js";
-import "../../components/ha-markdown.js";
-import "../../resources/ha-style.js";
-
-import EventsMixin from "../../mixins/events-mixin.js";
-import LocalizeMixin from "../../mixins/localize-mixin.js";
+import "@polymer/paper-button/paper-button";
+import "@polymer/paper-dialog-scrollable/paper-dialog-scrollable";
+import "@polymer/paper-dialog/paper-dialog";
+import "@polymer/paper-spinner/paper-spinner";
+import { html } from "@polymer/polymer/lib/utils/html-tag";
+import { PolymerElement } from "@polymer/polymer/polymer-element";
+
+import "../../components/ha-form";
+import "../../components/ha-markdown";
+import "../../resources/ha-style";
+
+import EventsMixin from "../../mixins/events-mixin";
+import LocalizeMixin from "../../mixins/localize-mixin";
let instance = 0;
diff --git a/src/panels/profile/ha-mfa-modules-card.js b/src/panels/profile/ha-mfa-modules-card.js
index de8a66f4553e..9bcd647b043b 100644
--- a/src/panels/profile/ha-mfa-modules-card.js
+++ b/src/panels/profile/ha-mfa-modules-card.js
@@ -1,14 +1,14 @@
-import "@polymer/paper-button/paper-button.js";
-import "@polymer/paper-card/paper-card.js";
-import "@polymer/paper-item/paper-item-body.js";
-import "@polymer/paper-item/paper-item.js";
-import { html } from "@polymer/polymer/lib/utils/html-tag.js";
-import { PolymerElement } from "@polymer/polymer/polymer-element.js";
+import "@polymer/paper-button/paper-button";
+import "@polymer/paper-card/paper-card";
+import "@polymer/paper-item/paper-item-body";
+import "@polymer/paper-item/paper-item";
+import { html } from "@polymer/polymer/lib/utils/html-tag";
+import { PolymerElement } from "@polymer/polymer/polymer-element";
-import "../../resources/ha-style.js";
+import "../../resources/ha-style";
-import EventsMixin from "../../mixins/events-mixin.js";
-import LocalizeMixin from "../../mixins/localize-mixin.js";
+import EventsMixin from "../../mixins/events-mixin";
+import LocalizeMixin from "../../mixins/localize-mixin";
let registeredDialog = false;
@@ -89,7 +89,7 @@ class HaMfaModulesCard extends EventsMixin(LocalizeMixin(PolymerElement)) {
this.fire("register-dialog", {
dialogShowEvent: "show-mfa-module-setup-flow",
dialogTag: "ha-mfa-module-setup-flow",
- dialogImport: () => import("./ha-mfa-module-setup-flow.js"),
+ dialogImport: () => import("./ha-mfa-module-setup-flow"),
});
}
}
diff --git a/src/panels/profile/ha-panel-profile.js b/src/panels/profile/ha-panel-profile.js
index 524310c61bd6..b642aa2764b1 100644
--- a/src/panels/profile/ha-panel-profile.js
+++ b/src/panels/profile/ha-panel-profile.js
@@ -1,27 +1,27 @@
-import "@polymer/app-layout/app-header-layout/app-header-layout.js";
-import "@polymer/app-layout/app-header/app-header.js";
-import "@polymer/paper-card/paper-card.js";
-import "@polymer/paper-item/paper-item-body.js";
-import "@polymer/paper-item/paper-item.js";
-import "@polymer/paper-button/paper-button.js";
-import "@polymer/app-layout/app-toolbar/app-toolbar.js";
-import { html } from "@polymer/polymer/lib/utils/html-tag.js";
-import { PolymerElement } from "@polymer/polymer/polymer-element.js";
-
-import "../../components/ha-menu-button.js";
-import "../../resources/ha-style.js";
-
-import EventsMixin from "../../mixins/events-mixin.js";
-import LocalizeMixin from "../../mixins/localize-mixin.js";
-
-import "./ha-change-password-card.js";
-import "./ha-mfa-modules-card.js";
-import "./ha-refresh-tokens-card.js";
-import "./ha-long-lived-access-tokens-card.js";
-
-import "./ha-pick-language-row.js";
-import "./ha-pick-theme-row.js";
-import "./ha-push-notifications-row.js";
+import "@polymer/app-layout/app-header-layout/app-header-layout";
+import "@polymer/app-layout/app-header/app-header";
+import "@polymer/paper-card/paper-card";
+import "@polymer/paper-item/paper-item-body";
+import "@polymer/paper-item/paper-item";
+import "@polymer/paper-button/paper-button";
+import "@polymer/app-layout/app-toolbar/app-toolbar";
+import { html } from "@polymer/polymer/lib/utils/html-tag";
+import { PolymerElement } from "@polymer/polymer/polymer-element";
+
+import "../../components/ha-menu-button";
+import "../../resources/ha-style";
+
+import EventsMixin from "../../mixins/events-mixin";
+import LocalizeMixin from "../../mixins/localize-mixin";
+
+import "./ha-change-password-card";
+import "./ha-mfa-modules-card";
+import "./ha-refresh-tokens-card";
+import "./ha-long-lived-access-tokens-card";
+
+import "./ha-pick-language-row";
+import "./ha-pick-theme-row";
+import "./ha-push-notifications-row";
/*
* @appliesMixin EventsMixin
diff --git a/src/panels/profile/ha-pick-language-row.js b/src/panels/profile/ha-pick-language-row.js
index 4bf9cd568daf..1b1a1e89de4e 100644
--- a/src/panels/profile/ha-pick-language-row.js
+++ b/src/panels/profile/ha-pick-language-row.js
@@ -1,14 +1,14 @@
-import "@polymer/paper-card/paper-card.js";
-import "@polymer/paper-dropdown-menu/paper-dropdown-menu.js";
-import "@polymer/paper-item/paper-item.js";
-import "@polymer/paper-listbox/paper-listbox.js";
-import { html } from "@polymer/polymer/lib/utils/html-tag.js";
-import { PolymerElement } from "@polymer/polymer/polymer-element.js";
+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 { html } from "@polymer/polymer/lib/utils/html-tag";
+import { PolymerElement } from "@polymer/polymer/polymer-element";
-import EventsMixin from "../../mixins/events-mixin.js";
-import LocalizeMixin from "../../mixins/localize-mixin.js";
+import EventsMixin from "../../mixins/events-mixin";
+import LocalizeMixin from "../../mixins/localize-mixin";
-import "./ha-settings-row.js";
+import "./ha-settings-row";
/*
* @appliesMixin LocalizeMixin
diff --git a/src/panels/profile/ha-pick-theme-row.js b/src/panels/profile/ha-pick-theme-row.js
index a2dba3ff3138..4d3dc557c634 100644
--- a/src/panels/profile/ha-pick-theme-row.js
+++ b/src/panels/profile/ha-pick-theme-row.js
@@ -1,12 +1,12 @@
-import "@polymer/paper-card/paper-card.js";
-import "@polymer/paper-dropdown-menu/paper-dropdown-menu.js";
-import "@polymer/paper-item/paper-item.js";
-import "@polymer/paper-listbox/paper-listbox.js";
-import { html } from "@polymer/polymer/lib/utils/html-tag.js";
-import { PolymerElement } from "@polymer/polymer/polymer-element.js";
+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 { html } from "@polymer/polymer/lib/utils/html-tag";
+import { PolymerElement } from "@polymer/polymer/polymer-element";
-import EventsMixin from "../../mixins/events-mixin.js";
-import LocalizeMixin from "../../mixins/localize-mixin.js";
+import EventsMixin from "../../mixins/events-mixin";
+import LocalizeMixin from "../../mixins/localize-mixin";
/*
* @appliesMixin LocalizeMixin
diff --git a/src/panels/profile/ha-push-notifications-row.js b/src/panels/profile/ha-push-notifications-row.js
index 37f91bc8174e..83c31f01eeb4 100644
--- a/src/panels/profile/ha-push-notifications-row.js
+++ b/src/panels/profile/ha-push-notifications-row.js
@@ -1,15 +1,15 @@
-import "@polymer/iron-flex-layout/iron-flex-layout-classes.js";
-import "@polymer/iron-label/iron-label.js";
-import "@polymer/paper-card/paper-card.js";
-import { html } from "@polymer/polymer/lib/utils/html-tag.js";
-import { PolymerElement } from "@polymer/polymer/polymer-element.js";
+import "@polymer/iron-flex-layout/iron-flex-layout-classes";
+import "@polymer/iron-label/iron-label";
+import "@polymer/paper-card/paper-card";
+import { html } from "@polymer/polymer/lib/utils/html-tag";
+import { PolymerElement } from "@polymer/polymer/polymer-element";
-import isComponentLoaded from "../../common/config/is_component_loaded.js";
-import { pushSupported } from "../../components/ha-push-notifications-toggle.js";
+import isComponentLoaded from "../../common/config/is_component_loaded";
+import { pushSupported } from "../../components/ha-push-notifications-toggle";
-import LocalizeMixin from "../../mixins/localize-mixin.js";
+import LocalizeMixin from "../../mixins/localize-mixin";
-import "./ha-settings-row.js";
+import "./ha-settings-row";
/*
* @appliesMixin LocalizeMixin
diff --git a/src/panels/profile/ha-refresh-tokens-card.js b/src/panels/profile/ha-refresh-tokens-card.js
index e204e42e2bc9..1de26dfb36f5 100644
--- a/src/panels/profile/ha-refresh-tokens-card.js
+++ b/src/panels/profile/ha-refresh-tokens-card.js
@@ -1,13 +1,13 @@
-import "@polymer/paper-icon-button/paper-icon-button.js";
-import "@polymer/paper-tooltip/paper-tooltip.js";
+import "@polymer/paper-icon-button/paper-icon-button";
+import "@polymer/paper-tooltip/paper-tooltip";
-import { html } from "@polymer/polymer/lib/utils/html-tag.js";
-import { PolymerElement } from "@polymer/polymer/polymer-element.js";
-import EventsMixin from "../../mixins/events-mixin.js";
-import LocalizeMixin from "../../mixins/localize-mixin.js";
-import formatDateTime from "../../common/datetime/format_date_time.js";
+import { html } from "@polymer/polymer/lib/utils/html-tag";
+import { PolymerElement } from "@polymer/polymer/polymer-element";
+import EventsMixin from "../../mixins/events-mixin";
+import LocalizeMixin from "../../mixins/localize-mixin";
+import formatDateTime from "../../common/datetime/format_date_time";
-import "./ha-settings-row.js";
+import "./ha-settings-row";
/*
* @appliesMixin EventsMixin
diff --git a/src/panels/profile/ha-settings-row.js b/src/panels/profile/ha-settings-row.js
index bc7f5f0f2f15..7bba46651c18 100644
--- a/src/panels/profile/ha-settings-row.js
+++ b/src/panels/profile/ha-settings-row.js
@@ -1,5 +1,5 @@
-import { html } from "@polymer/polymer/lib/utils/html-tag.js";
-import { PolymerElement } from "@polymer/polymer/polymer-element.js";
+import { html } from "@polymer/polymer/lib/utils/html-tag";
+import { PolymerElement } from "@polymer/polymer/polymer-element";
class HaSettingsRow extends PolymerElement {
static get template() {
diff --git a/src/panels/shopping-list/ha-panel-shopping-list.js b/src/panels/shopping-list/ha-panel-shopping-list.js
index 84fb4471f568..5c4602a4c5f5 100644
--- a/src/panels/shopping-list/ha-panel-shopping-list.js
+++ b/src/panels/shopping-list/ha-panel-shopping-list.js
@@ -1,21 +1,21 @@
-import "@polymer/app-layout/app-header-layout/app-header-layout.js";
-import "@polymer/app-layout/app-header/app-header.js";
-import "@polymer/app-layout/app-toolbar/app-toolbar.js";
-import "@polymer/paper-card/paper-card.js";
-import "@polymer/paper-checkbox/paper-checkbox.js";
-import "@polymer/paper-icon-button/paper-icon-button.js";
-import "@polymer/paper-input/paper-input.js";
-import "@polymer/paper-item/paper-icon-item.js";
-import "@polymer/paper-item/paper-item-body.js";
-import "@polymer/paper-item/paper-item.js";
-import "@polymer/paper-listbox/paper-listbox.js";
-import "@polymer/paper-menu-button/paper-menu-button.js";
-import { html } from "@polymer/polymer/lib/utils/html-tag.js";
-import { PolymerElement } from "@polymer/polymer/polymer-element.js";
-
-import "../../components/ha-menu-button.js";
-import "../../components/ha-start-voice-button.js";
-import LocalizeMixin from "../../mixins/localize-mixin.js";
+import "@polymer/app-layout/app-header-layout/app-header-layout";
+import "@polymer/app-layout/app-header/app-header";
+import "@polymer/app-layout/app-toolbar/app-toolbar";
+import "@polymer/paper-card/paper-card";
+import "@polymer/paper-checkbox/paper-checkbox";
+import "@polymer/paper-icon-button/paper-icon-button";
+import "@polymer/paper-input/paper-input";
+import "@polymer/paper-item/paper-icon-item";
+import "@polymer/paper-item/paper-item-body";
+import "@polymer/paper-item/paper-item";
+import "@polymer/paper-listbox/paper-listbox";
+import "@polymer/paper-menu-button/paper-menu-button";
+import { html } from "@polymer/polymer/lib/utils/html-tag";
+import { PolymerElement } from "@polymer/polymer/polymer-element";
+
+import "../../components/ha-menu-button";
+import "../../components/ha-start-voice-button";
+import LocalizeMixin from "../../mixins/localize-mixin";
/*
* @appliesMixin LocalizeMixin
diff --git a/src/resources/ha-style.js b/src/resources/ha-style.js
index cfd75a93ad58..57c1e51a8d05 100644
--- a/src/resources/ha-style.js
+++ b/src/resources/ha-style.js
@@ -1,5 +1,5 @@
-import "@polymer/paper-styles/paper-styles.js";
-import "@polymer/polymer/polymer-legacy.js";
+import "@polymer/paper-styles/paper-styles";
+import "@polymer/polymer/polymer-legacy";
const documentContainer = document.createElement("template");
documentContainer.setAttribute("style", "display: none;");
diff --git a/src/resources/html-import/import-href.js b/src/resources/html-import/import-href.js
index f74708c2a0ab..5daf931bf859 100644
--- a/src/resources/html-import/import-href.js
+++ b/src/resources/html-import/import-href.js
@@ -1,5 +1,5 @@
/* eslint-disable */
-import "./polyfill.js";
+import "./polyfill";
/**
@license
Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
diff --git a/src/state-summary/state-card-climate.js b/src/state-summary/state-card-climate.js
index f1bb08e70bdb..a3df0974928b 100644
--- a/src/state-summary/state-card-climate.js
+++ b/src/state-summary/state-card-climate.js
@@ -1,9 +1,9 @@
-import "@polymer/iron-flex-layout/iron-flex-layout-classes.js";
-import { html } from "@polymer/polymer/lib/utils/html-tag.js";
-import { PolymerElement } from "@polymer/polymer/polymer-element.js";
+import "@polymer/iron-flex-layout/iron-flex-layout-classes";
+import { html } from "@polymer/polymer/lib/utils/html-tag";
+import { PolymerElement } from "@polymer/polymer/polymer-element";
-import "../components/entity/state-info.js";
-import "../components/ha-climate-state.js";
+import "../components/entity/state-info";
+import "../components/ha-climate-state";
class StateCardClimate extends PolymerElement {
static get template() {
diff --git a/src/state-summary/state-card-configurator.js b/src/state-summary/state-card-configurator.js
index 5f8261879464..e9b49018a0e6 100644
--- a/src/state-summary/state-card-configurator.js
+++ b/src/state-summary/state-card-configurator.js
@@ -1,11 +1,11 @@
-import "@polymer/iron-flex-layout/iron-flex-layout-classes.js";
-import "@polymer/paper-button/paper-button.js";
-import { html } from "@polymer/polymer/lib/utils/html-tag.js";
-import { PolymerElement } from "@polymer/polymer/polymer-element.js";
+import "@polymer/iron-flex-layout/iron-flex-layout-classes";
+import "@polymer/paper-button/paper-button";
+import { html } from "@polymer/polymer/lib/utils/html-tag";
+import { PolymerElement } from "@polymer/polymer/polymer-element";
-import "../components/entity/state-info.js";
+import "../components/entity/state-info";
-import LocalizeMixin from "../mixins/localize-mixin.js";
+import LocalizeMixin from "../mixins/localize-mixin";
/*
* @appliesMixin LocalizeMixin
diff --git a/src/state-summary/state-card-content.js b/src/state-summary/state-card-content.js
index 5068383a4532..7a6c12ff66ff 100644
--- a/src/state-summary/state-card-content.js
+++ b/src/state-summary/state-card-content.js
@@ -1,24 +1,24 @@
-import { PolymerElement } from "@polymer/polymer/polymer-element.js";
+import { PolymerElement } from "@polymer/polymer/polymer-element";
-import "./state-card-climate.js";
-import "./state-card-configurator.js";
-import "./state-card-cover.js";
-import "./state-card-display.js";
-import "./state-card-input_number.js";
-import "./state-card-input_select.js";
-import "./state-card-input_text.js";
-import "./state-card-lock.js";
-import "./state-card-media_player.js";
-import "./state-card-scene.js";
-import "./state-card-script.js";
-import "./state-card-timer.js";
-import "./state-card-toggle.js";
-import "./state-card-vacuum.js";
-import "./state-card-water_heater.js";
-import "./state-card-weblink.js";
+import "./state-card-climate";
+import "./state-card-configurator";
+import "./state-card-cover";
+import "./state-card-display";
+import "./state-card-input_number";
+import "./state-card-input_select";
+import "./state-card-input_text";
+import "./state-card-lock";
+import "./state-card-media_player";
+import "./state-card-scene";
+import "./state-card-script";
+import "./state-card-timer";
+import "./state-card-toggle";
+import "./state-card-vacuum";
+import "./state-card-water_heater";
+import "./state-card-weblink";
-import stateCardType from "../common/entity/state_card_type.js";
-import dynamicContentUpdater from "../common/dom/dynamic_content_updater.js";
+import stateCardType from "../common/entity/state_card_type";
+import dynamicContentUpdater from "../common/dom/dynamic_content_updater";
class StateCardContent extends PolymerElement {
static get properties() {
diff --git a/src/state-summary/state-card-cover.js b/src/state-summary/state-card-cover.js
index 8072af0c96c7..94c275fbb513 100644
--- a/src/state-summary/state-card-cover.js
+++ b/src/state-summary/state-card-cover.js
@@ -1,11 +1,11 @@
-import "@polymer/iron-flex-layout/iron-flex-layout-classes.js";
-import { html } from "@polymer/polymer/lib/utils/html-tag.js";
-import { PolymerElement } from "@polymer/polymer/polymer-element.js";
+import "@polymer/iron-flex-layout/iron-flex-layout-classes";
+import { html } from "@polymer/polymer/lib/utils/html-tag";
+import { PolymerElement } from "@polymer/polymer/polymer-element";
-import "../components/entity/state-info.js";
-import "../components/ha-cover-controls.js";
-import "../components/ha-cover-tilt-controls.js";
-import CoverEntity from "../util/cover-model.js";
+import "../components/entity/state-info";
+import "../components/ha-cover-controls";
+import "../components/ha-cover-tilt-controls";
+import CoverEntity from "../util/cover-model";
class StateCardCover extends PolymerElement {
static get template() {
diff --git a/src/state-summary/state-card-display.js b/src/state-summary/state-card-display.js
index f2d6963271ee..0438fa425f51 100644
--- a/src/state-summary/state-card-display.js
+++ b/src/state-summary/state-card-display.js
@@ -1,12 +1,12 @@
-import "@polymer/iron-flex-layout/iron-flex-layout-classes.js";
-import { html } from "@polymer/polymer/lib/utils/html-tag.js";
-import { PolymerElement } from "@polymer/polymer/polymer-element.js";
+import "@polymer/iron-flex-layout/iron-flex-layout-classes";
+import { html } from "@polymer/polymer/lib/utils/html-tag";
+import { PolymerElement } from "@polymer/polymer/polymer-element";
-import "../components/entity/state-info.js";
-import LocalizeMixin from "../mixins/localize-mixin.js";
+import "../components/entity/state-info";
+import LocalizeMixin from "../mixins/localize-mixin";
-import computeStateDisplay from "../common/entity/compute_state_display.js";
-import attributeClassNames from "../common/entity/attribute_class_names.js";
+import computeStateDisplay from "../common/entity/compute_state_display";
+import attributeClassNames from "../common/entity/attribute_class_names";
/*
* @appliesMixin LocalizeMixin
diff --git a/src/state-summary/state-card-input_number.js b/src/state-summary/state-card-input_number.js
index 8ab377b0aa43..8b13308ab9f3 100644
--- a/src/state-summary/state-card-input_number.js
+++ b/src/state-summary/state-card-input_number.js
@@ -1,11 +1,11 @@
-import "@polymer/iron-flex-layout/iron-flex-layout-classes.js";
-import { IronResizableBehavior } from "@polymer/iron-resizable-behavior/iron-resizable-behavior.js";
-import "@polymer/paper-input/paper-input.js";
-import { mixinBehaviors } from "@polymer/polymer/lib/legacy/class.js";
-import { html } from "@polymer/polymer/lib/utils/html-tag.js";
-import { PolymerElement } from "@polymer/polymer/polymer-element.js";
+import "@polymer/iron-flex-layout/iron-flex-layout-classes";
+import { IronResizableBehavior } from "@polymer/iron-resizable-behavior/iron-resizable-behavior";
+import "@polymer/paper-input/paper-input";
+import { mixinBehaviors } from "@polymer/polymer/lib/legacy/class";
+import { html } from "@polymer/polymer/lib/utils/html-tag";
+import { PolymerElement } from "@polymer/polymer/polymer-element";
-import "../components/entity/state-info.js";
+import "../components/entity/state-info";
import "../components/ha-slider";
class StateCardInputNumber extends mixinBehaviors(
diff --git a/src/state-summary/state-card-input_select.js b/src/state-summary/state-card-input_select.js
index 34b5c6b6550c..4d881a8014b2 100644
--- a/src/state-summary/state-card-input_select.js
+++ b/src/state-summary/state-card-input_select.js
@@ -1,12 +1,12 @@
-import "@polymer/paper-dropdown-menu/paper-dropdown-menu.js";
-import "@polymer/paper-item/paper-item.js";
-import "@polymer/paper-listbox/paper-listbox.js";
-import { html } from "@polymer/polymer/lib/utils/html-tag.js";
-import { PolymerElement } from "@polymer/polymer/polymer-element.js";
+import "@polymer/paper-dropdown-menu/paper-dropdown-menu";
+import "@polymer/paper-item/paper-item";
+import "@polymer/paper-listbox/paper-listbox";
+import { html } from "@polymer/polymer/lib/utils/html-tag";
+import { PolymerElement } from "@polymer/polymer/polymer-element";
-import "../components/entity/state-badge.js";
+import "../components/entity/state-badge";
-import computeStateName from "../common/entity/compute_state_name.js";
+import computeStateName from "../common/entity/compute_state_name";
class StateCardInputSelect extends PolymerElement {
static get template() {
diff --git a/src/state-summary/state-card-input_text.js b/src/state-summary/state-card-input_text.js
index 55a94c88dc79..6f0f2a41580d 100644
--- a/src/state-summary/state-card-input_text.js
+++ b/src/state-summary/state-card-input_text.js
@@ -1,9 +1,9 @@
-import "@polymer/iron-flex-layout/iron-flex-layout-classes.js";
-import "@polymer/paper-input/paper-input.js";
-import { html } from "@polymer/polymer/lib/utils/html-tag.js";
-import { PolymerElement } from "@polymer/polymer/polymer-element.js";
+import "@polymer/iron-flex-layout/iron-flex-layout-classes";
+import "@polymer/paper-input/paper-input";
+import { html } from "@polymer/polymer/lib/utils/html-tag";
+import { PolymerElement } from "@polymer/polymer/polymer-element";
-import "../components/entity/state-info.js";
+import "../components/entity/state-info";
class StateCardInputText extends PolymerElement {
static get template() {
diff --git a/src/state-summary/state-card-lock.js b/src/state-summary/state-card-lock.js
index bc18e29e473f..cb99b3fe3381 100644
--- a/src/state-summary/state-card-lock.js
+++ b/src/state-summary/state-card-lock.js
@@ -1,11 +1,11 @@
-import "@polymer/iron-flex-layout/iron-flex-layout-classes.js";
-import "@polymer/paper-button/paper-button.js";
-import { html } from "@polymer/polymer/lib/utils/html-tag.js";
-import { PolymerElement } from "@polymer/polymer/polymer-element.js";
+import "@polymer/iron-flex-layout/iron-flex-layout-classes";
+import "@polymer/paper-button/paper-button";
+import { html } from "@polymer/polymer/lib/utils/html-tag";
+import { PolymerElement } from "@polymer/polymer/polymer-element";
-import "../components/entity/state-info.js";
+import "../components/entity/state-info";
-import LocalizeMixin from "../mixins/localize-mixin.js";
+import LocalizeMixin from "../mixins/localize-mixin";
/*
* @appliesMixin LocalizeMixin
diff --git a/src/state-summary/state-card-media_player.js b/src/state-summary/state-card-media_player.js
index c18dd2dcf32a..593c71a80058 100644
--- a/src/state-summary/state-card-media_player.js
+++ b/src/state-summary/state-card-media_player.js
@@ -1,10 +1,10 @@
-import "@polymer/iron-flex-layout/iron-flex-layout-classes.js";
-import { html } from "@polymer/polymer/lib/utils/html-tag.js";
-import { PolymerElement } from "@polymer/polymer/polymer-element.js";
+import "@polymer/iron-flex-layout/iron-flex-layout-classes";
+import { html } from "@polymer/polymer/lib/utils/html-tag";
+import { PolymerElement } from "@polymer/polymer/polymer-element";
-import "../components/entity/state-info.js";
-import LocalizeMixin from "../mixins/localize-mixin.js";
-import HassMediaPlayerEntity from "../util/hass-media-player-model.js";
+import "../components/entity/state-info";
+import LocalizeMixin from "../mixins/localize-mixin";
+import HassMediaPlayerEntity from "../util/hass-media-player-model";
/*
* @appliesMixin LocalizeMixin
diff --git a/src/state-summary/state-card-scene.js b/src/state-summary/state-card-scene.js
index c83668e83cf0..335ca5554f9d 100644
--- a/src/state-summary/state-card-scene.js
+++ b/src/state-summary/state-card-scene.js
@@ -1,10 +1,10 @@
-import "@polymer/iron-flex-layout/iron-flex-layout-classes.js";
-import "@polymer/paper-button/paper-button.js";
-import { html } from "@polymer/polymer/lib/utils/html-tag.js";
-import { PolymerElement } from "@polymer/polymer/polymer-element.js";
+import "@polymer/iron-flex-layout/iron-flex-layout-classes";
+import "@polymer/paper-button/paper-button";
+import { html } from "@polymer/polymer/lib/utils/html-tag";
+import { PolymerElement } from "@polymer/polymer/polymer-element";
-import "../components/entity/state-info.js";
-import LocalizeMixin from "../mixins/localize-mixin.js";
+import "../components/entity/state-info";
+import LocalizeMixin from "../mixins/localize-mixin";
/*
* @appliesMixin LocalizeMixin
diff --git a/src/state-summary/state-card-script.js b/src/state-summary/state-card-script.js
index 21982456f467..38155b30570a 100644
--- a/src/state-summary/state-card-script.js
+++ b/src/state-summary/state-card-script.js
@@ -1,12 +1,12 @@
-import "@polymer/iron-flex-layout/iron-flex-layout-classes.js";
-import "@polymer/paper-button/paper-button.js";
-import { html } from "@polymer/polymer/lib/utils/html-tag.js";
-import { PolymerElement } from "@polymer/polymer/polymer-element.js";
+import "@polymer/iron-flex-layout/iron-flex-layout-classes";
+import "@polymer/paper-button/paper-button";
+import { html } from "@polymer/polymer/lib/utils/html-tag";
+import { PolymerElement } from "@polymer/polymer/polymer-element";
-import "../components/entity/ha-entity-toggle.js";
-import "../components/entity/state-info.js";
+import "../components/entity/ha-entity-toggle";
+import "../components/entity/state-info";
-import LocalizeMixin from "../mixins/localize-mixin.js";
+import LocalizeMixin from "../mixins/localize-mixin";
/*
* @appliesMixin LocalizeMixin
diff --git a/src/state-summary/state-card-timer.js b/src/state-summary/state-card-timer.js
index e7f53b8a27d2..b108a200cdd0 100644
--- a/src/state-summary/state-card-timer.js
+++ b/src/state-summary/state-card-timer.js
@@ -1,11 +1,11 @@
-import "@polymer/iron-flex-layout/iron-flex-layout-classes.js";
-import { html } from "@polymer/polymer/lib/utils/html-tag.js";
-import { PolymerElement } from "@polymer/polymer/polymer-element.js";
+import "@polymer/iron-flex-layout/iron-flex-layout-classes";
+import { html } from "@polymer/polymer/lib/utils/html-tag";
+import { PolymerElement } from "@polymer/polymer/polymer-element";
-import "../components/entity/state-info.js";
+import "../components/entity/state-info";
-import timerTimeRemaining from "../common/entity/timer_time_remaining.js";
-import secondsToDuration from "../common/datetime/seconds_to_duration.js";
+import timerTimeRemaining from "../common/entity/timer_time_remaining";
+import secondsToDuration from "../common/datetime/seconds_to_duration";
class StateCardTimer extends PolymerElement {
static get template() {
diff --git a/src/state-summary/state-card-toggle.js b/src/state-summary/state-card-toggle.js
index e9c25d214fca..ae96ab28eda0 100644
--- a/src/state-summary/state-card-toggle.js
+++ b/src/state-summary/state-card-toggle.js
@@ -1,9 +1,9 @@
-import "@polymer/iron-flex-layout/iron-flex-layout-classes.js";
-import { html } from "@polymer/polymer/lib/utils/html-tag.js";
-import { PolymerElement } from "@polymer/polymer/polymer-element.js";
+import "@polymer/iron-flex-layout/iron-flex-layout-classes";
+import { html } from "@polymer/polymer/lib/utils/html-tag";
+import { PolymerElement } from "@polymer/polymer/polymer-element";
-import "../components/entity/ha-entity-toggle.js";
-import "../components/entity/state-info.js";
+import "../components/entity/ha-entity-toggle";
+import "../components/entity/state-info";
class StateCardToggle extends PolymerElement {
static get template() {
diff --git a/src/state-summary/state-card-vacuum.js b/src/state-summary/state-card-vacuum.js
index ad48c336aac2..d976fd5b1cda 100644
--- a/src/state-summary/state-card-vacuum.js
+++ b/src/state-summary/state-card-vacuum.js
@@ -1,9 +1,9 @@
-import "@polymer/iron-flex-layout/iron-flex-layout-classes.js";
-import { html } from "@polymer/polymer/lib/utils/html-tag.js";
-import { PolymerElement } from "@polymer/polymer/polymer-element.js";
+import "@polymer/iron-flex-layout/iron-flex-layout-classes";
+import { html } from "@polymer/polymer/lib/utils/html-tag";
+import { PolymerElement } from "@polymer/polymer/polymer-element";
-import "../components/entity/state-info.js";
-import "../components/ha-vacuum-state.js";
+import "../components/entity/state-info";
+import "../components/ha-vacuum-state";
class StateCardVacuum extends PolymerElement {
static get template() {
diff --git a/src/state-summary/state-card-water_heater.js b/src/state-summary/state-card-water_heater.js
index b9cc41cbb1ad..c04214edbcd1 100644
--- a/src/state-summary/state-card-water_heater.js
+++ b/src/state-summary/state-card-water_heater.js
@@ -1,9 +1,9 @@
-import "@polymer/iron-flex-layout/iron-flex-layout-classes.js";
-import { html } from "@polymer/polymer/lib/utils/html-tag.js";
-import { PolymerElement } from "@polymer/polymer/polymer-element.js";
+import "@polymer/iron-flex-layout/iron-flex-layout-classes";
+import { html } from "@polymer/polymer/lib/utils/html-tag";
+import { PolymerElement } from "@polymer/polymer/polymer-element";
-import "../components/entity/state-info.js";
-import "../components/ha-water_heater-state.js";
+import "../components/entity/state-info";
+import "../components/ha-water_heater-state";
class StateCardWaterHeater extends PolymerElement {
static get template() {
diff --git a/src/state-summary/state-card-weblink.js b/src/state-summary/state-card-weblink.js
index f4f12c42069b..467f5318b23b 100644
--- a/src/state-summary/state-card-weblink.js
+++ b/src/state-summary/state-card-weblink.js
@@ -1,9 +1,9 @@
-import { html } from "@polymer/polymer/lib/utils/html-tag.js";
-import { PolymerElement } from "@polymer/polymer/polymer-element.js";
+import { html } from "@polymer/polymer/lib/utils/html-tag";
+import { PolymerElement } from "@polymer/polymer/polymer-element";
-import "../components/entity/state-badge.js";
+import "../components/entity/state-badge";
-import computeStateName from "../common/entity/compute_state_name.js";
+import computeStateName from "../common/entity/compute_state_name";
class StateCardWeblink extends PolymerElement {
static get template() {
diff --git a/src/util/app-localstorage-document.js b/src/util/app-localstorage-document.js
index 3f1789dc8903..a966f8ee5f3e 100644
--- a/src/util/app-localstorage-document.js
+++ b/src/util/app-localstorage-document.js
@@ -9,9 +9,9 @@ The complete set of contributors may be found at http://polymer.github.io/CONTRI
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
*/
-import { AppStorageBehavior } from "@polymer/app-storage/app-storage-behavior.js";
-import { Polymer } from "@polymer/polymer/lib/legacy/polymer-fn.js";
-import "@polymer/polymer/polymer-legacy.js";
+import { AppStorageBehavior } from "@polymer/app-storage/app-storage-behavior";
+import { Polymer } from "@polymer/polymer/lib/legacy/polymer-fn";
+import "@polymer/polymer/polymer-legacy";
/**
* app-localstorage-document synchronizes storage between an in-memory
diff --git a/src/util/custom-panel/load-custom-panel.js b/src/util/custom-panel/load-custom-panel.js
index 55fffa39c8e3..54614810aa48 100644
--- a/src/util/custom-panel/load-custom-panel.js
+++ b/src/util/custom-panel/load-custom-panel.js
@@ -1,4 +1,4 @@
-import { loadJS, loadModule } from "../../common/dom/load_resource.js";
+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 = {};
@@ -6,12 +6,12 @@ const JS_CACHE = {};
export default function loadCustomPanel(panelConfig) {
if (panelConfig.html_url) {
const toLoad = [
- import(/* webpackChunkName: "import-href-polyfill" */ "../../resources/html-import/import-href.js"),
+ import(/* webpackChunkName: "import-href-polyfill" */ "../../resources/html-import/import-href"),
];
if (!panelConfig.embed_iframe) {
toLoad.push(
- import(/* webpackChunkName: "legacy-support" */ "../legacy-support.js")
+ import(/* webpackChunkName: "legacy-support" */ "../legacy-support")
);
}
diff --git a/src/util/ha-url-sync.js b/src/util/ha-url-sync.js
index feaa661eafac..471c4ff5587b 100644
--- a/src/util/ha-url-sync.js
+++ b/src/util/ha-url-sync.js
@@ -1,6 +1,6 @@
-import { PolymerElement } from "@polymer/polymer/polymer-element.js";
+import { PolymerElement } from "@polymer/polymer/polymer-element";
-import EventsMixin from "../mixins/events-mixin.js";
+import EventsMixin from "../mixins/events-mixin";
/* eslint-disable no-console */
const DEBUG = false;
diff --git a/src/util/hass-call-api.js b/src/util/hass-call-api.js
index 7aedeb45f3d7..dbfde887a01d 100644
--- a/src/util/hass-call-api.js
+++ b/src/util/hass-call-api.js
@@ -1,4 +1,4 @@
-import { fetchWithAuth } from "./fetch-with-auth.js";
+import { fetchWithAuth } from "./fetch-with-auth";
/* eslint-disable no-throw-literal */
diff --git a/src/util/legacy-support.js b/src/util/legacy-support.js
index e243fd96ff71..f45d14fc3ac8 100644
--- a/src/util/legacy-support.js
+++ b/src/util/legacy-support.js
@@ -2,9 +2,9 @@
* Provide legacy support to HTML imports by exposing Polymer and
* Polymer.Element on the window object.
*/
-import { PolymerElement } from "@polymer/polymer/polymer-element.js";
-import { Polymer } from "@polymer/polymer/polymer-legacy.js";
-import { html } from "@polymer/polymer/lib/utils/html-tag.js";
+import { PolymerElement } from "@polymer/polymer/polymer-element";
+import { Polymer } from "@polymer/polymer/polymer-legacy";
+import { html } from "@polymer/polymer/lib/utils/html-tag";
Polymer.Element = PolymerElement;
Polymer.html = html;
diff --git a/src/util/register-service-worker.js b/src/util/register-service-worker.js
index 38f90d899b74..aa49340885b8 100644
--- a/src/util/register-service-worker.js
+++ b/src/util/register-service-worker.js
@@ -1,5 +1,5 @@
const serviceWorkerUrl =
- __BUILD__ === "latest" ? "/service_worker.js" : "/service_worker_es5.js";
+ __BUILD__ === "latest" ? "/service_worker.js" : "/service_worker_es5";
export default () => {
if (!("serviceWorker" in navigator)) return;
@@ -14,7 +14,7 @@ export default () => {
!__DEV__
) {
// Notify users here of a new frontend being available.
- import("./show-new-frontend-toast.js").then((mod) =>
+ import("./show-new-frontend-toast").then((mod) =>
mod.default(installingWorker)
);
}
diff --git a/src/util/show-new-frontend-toast.js b/src/util/show-new-frontend-toast.js
index b228218fbc5b..11c54cc41c3f 100644
--- a/src/util/show-new-frontend-toast.js
+++ b/src/util/show-new-frontend-toast.js
@@ -1,5 +1,5 @@
-import "@polymer/paper-button/paper-button.js";
-import "../components/ha-toast.js";
+import "@polymer/paper-button/paper-button";
+import "../components/ha-toast";
export default (installingWorker) => {
const toast = document.createElement("ha-toast");
diff --git a/test-mocha/common/datetime/duration_to_seconds_test.js b/test-mocha/common/datetime/duration_to_seconds_test.js
index 6fb367c24e01..78752a5cdba1 100644
--- a/test-mocha/common/datetime/duration_to_seconds_test.js
+++ b/test-mocha/common/datetime/duration_to_seconds_test.js
@@ -1,6 +1,6 @@
import { assert } from "chai";
-import durationToSeconds from "../../../src/common/datetime/duration_to_seconds.js";
+import durationToSeconds from "../../../src/common/datetime/duration_to_seconds";
describe("durationToSeconds", () => {
it("works", () => {
diff --git a/test-mocha/common/datetime/seconds_to_duration_test.js b/test-mocha/common/datetime/seconds_to_duration_test.js
index fe064c435573..33f7a9a9a545 100644
--- a/test-mocha/common/datetime/seconds_to_duration_test.js
+++ b/test-mocha/common/datetime/seconds_to_duration_test.js
@@ -1,6 +1,6 @@
import { assert } from "chai";
-import secondsToDuration from "../../../src/common/datetime/seconds_to_duration.js";
+import secondsToDuration from "../../../src/common/datetime/seconds_to_duration";
describe("secondsToDuration", () => {
it("works", () => {
diff --git a/test-mocha/common/entity/compute_state_domain.js b/test-mocha/common/entity/compute_state_domain.js
index df2a37c083b1..f65cb2bac761 100644
--- a/test-mocha/common/entity/compute_state_domain.js
+++ b/test-mocha/common/entity/compute_state_domain.js
@@ -1,6 +1,6 @@
import { assert } from "chai";
-import computeStateDomain from "../../../src/common/entity/compute_state_domain.js";
+import computeStateDomain from "../../../src/common/entity/compute_state_domain";
describe("computeStateDomain", () => {
it("Detects sensor domain", () => {
diff --git a/test-mocha/common/entity/extract_views.spec.js b/test-mocha/common/entity/extract_views.spec.js
index 4977b303cfdb..2feb8879ead1 100644
--- a/test-mocha/common/entity/extract_views.spec.js
+++ b/test-mocha/common/entity/extract_views.spec.js
@@ -1,6 +1,6 @@
import assert from "assert";
-import extractViews from "../../../src/common/entity/extract_views.js";
+import extractViews from "../../../src/common/entity/extract_views";
import { createEntities, createView } from "./test_util";
diff --git a/test-mocha/common/entity/get_group_entities.spec.js b/test-mocha/common/entity/get_group_entities.spec.js
index 1dc3404bb370..35052dee0a34 100644
--- a/test-mocha/common/entity/get_group_entities.spec.js
+++ b/test-mocha/common/entity/get_group_entities.spec.js
@@ -1,6 +1,6 @@
import assert from "assert";
-import getGroupEntities from "../../../src/common/entity/get_group_entities.js";
+import getGroupEntities from "../../../src/common/entity/get_group_entities";
import { createEntities, createGroup, entityMap } from "./test_util";
diff --git a/test-mocha/common/entity/get_view_entities.spec.js b/test-mocha/common/entity/get_view_entities.spec.js
index 28947693bc31..b95cb573acdf 100644
--- a/test-mocha/common/entity/get_view_entities.spec.js
+++ b/test-mocha/common/entity/get_view_entities.spec.js
@@ -1,6 +1,6 @@
import assert from "assert";
-import getViewEntities from "../../../src/common/entity/get_view_entities.js";
+import getViewEntities from "../../../src/common/entity/get_view_entities";
import {
createEntities,
diff --git a/test-mocha/common/entity/has_location.test.js b/test-mocha/common/entity/has_location.test.js
index 4ea6dd09f64e..10389d35be5f 100644
--- a/test-mocha/common/entity/has_location.test.js
+++ b/test-mocha/common/entity/has_location.test.js
@@ -1,6 +1,6 @@
import { assert } from "chai";
-import hasLocation from "../../../src/common/entity/has_location.js";
+import hasLocation from "../../../src/common/entity/has_location";
describe("hasLocation", () => {
it("flags states with location", () => {
diff --git a/test-mocha/common/entity/split_by_groups.spec.js b/test-mocha/common/entity/split_by_groups.spec.js
index 3629fbe8c9c2..85c31f471555 100644
--- a/test-mocha/common/entity/split_by_groups.spec.js
+++ b/test-mocha/common/entity/split_by_groups.spec.js
@@ -1,6 +1,6 @@
import assert from "assert";
-import splitByGroups from "../../../src/common/entity/split_by_groups.js";
+import splitByGroups from "../../../src/common/entity/split_by_groups";
import { createEntities, createGroup, entityMap } from "./test_util";
diff --git a/test-mocha/common/entity/state_card_type_test.js b/test-mocha/common/entity/state_card_type_test.js
index b1569cf3ac6b..3d9c66edbebc 100644
--- a/test-mocha/common/entity/state_card_type_test.js
+++ b/test-mocha/common/entity/state_card_type_test.js
@@ -1,6 +1,6 @@
import { assert } from "chai";
-import stateCardType from "../../../src/common/entity/state_card_type.js";
+import stateCardType from "../../../src/common/entity/state_card_type";
describe("stateCardType", () => {
const hass = {
diff --git a/test-mocha/common/entity/state_more_info_type_test.js b/test-mocha/common/entity/state_more_info_type_test.js
index b0738759e6e7..93a1e8d7cbb0 100644
--- a/test-mocha/common/entity/state_more_info_type_test.js
+++ b/test-mocha/common/entity/state_more_info_type_test.js
@@ -1,6 +1,6 @@
import { assert } from "chai";
-import stateMoreInfoType from "../../../src/common/entity/state_more_info_type.js";
+import stateMoreInfoType from "../../../src/common/entity/state_more_info_type";
describe("stateMoreInfoType", () => {
it("Returns media_player for media_player states", () => {
diff --git a/test-mocha/common/entity/timer_time_remaining_test.js b/test-mocha/common/entity/timer_time_remaining_test.js
index 0904fe2d0922..fd77bc92b853 100644
--- a/test-mocha/common/entity/timer_time_remaining_test.js
+++ b/test-mocha/common/entity/timer_time_remaining_test.js
@@ -1,7 +1,7 @@
import { assert } from "chai";
import sinon from "sinon";
-import timerTimeRemaining from "../../../src/common/entity/timer_time_remaining.js";
+import timerTimeRemaining from "../../../src/common/entity/timer_time_remaining";
describe("timerTimeRemaining", () => {
it("works with idle timers", () => {
diff --git a/test-mocha/common/util/parse_aspect_ratio_test.js b/test-mocha/common/util/parse_aspect_ratio_test.js
index d355e439e984..477a387a995d 100644
--- a/test-mocha/common/util/parse_aspect_ratio_test.js
+++ b/test-mocha/common/util/parse_aspect_ratio_test.js
@@ -1,6 +1,6 @@
import assert from "assert";
-import parseAspectRatio from "../../../src/common/util/parse-aspect-ratio.js";
+import parseAspectRatio from "../../../src/common/util/parse-aspect-ratio";
describe("parseAspectRatio", () => {
const ratio16by9 = { w: 16, h: 9 };