diff --git a/gallery/public/api/media_player_proxy/media_player.walkman b/gallery/public/api/media_player_proxy/media_player.walkman index 76c759dd5b..99ea7e6d32 100644 Binary files a/gallery/public/api/media_player_proxy/media_player.walkman and b/gallery/public/api/media_player_proxy/media_player.walkman differ diff --git a/src/common/util/parse-aspect-ratio.js b/src/common/util/parse-aspect-ratio.js index b5812d8a22..1797e81e3a 100644 --- a/src/common/util/parse-aspect-ratio.js +++ b/src/common/util/parse-aspect-ratio.js @@ -1,24 +1,24 @@ -export default function parseAspectRatio(input) { - // Handle 16x9, 16:9, 1.78x1, 1.78:1, 1.78 - // Ignore everything else - function parseOrThrow(number) { - const parsed = parseFloat(number); - if (isNaN(parsed)) throw new Error(`${number} is not a number`); - return parsed; - } - try { - if (input) { - const arr = input.replace(":", "x").split("x"); - if (arr.length === 0) { - return null; - } - - return arr.length === 1 - ? { w: parseOrThrow(arr[0]), h: 1 } - : { w: parseOrThrow(arr[0]), h: parseOrThrow(arr[1]) }; - } - } catch (err) { - // Ignore the error - } - return null; -} +export default function parseAspectRatio(input) { + // Handle 16x9, 16:9, 1.78x1, 1.78:1, 1.78 + // Ignore everything else + function parseOrThrow(number) { + const parsed = parseFloat(number); + if (isNaN(parsed)) throw new Error(`${number} is not a number`); + return parsed; + } + try { + if (input) { + const arr = input.replace(":", "x").split("x"); + if (arr.length === 0) { + return null; + } + + return arr.length === 1 + ? { w: parseOrThrow(arr[0]), h: 1 } + : { w: parseOrThrow(arr[0]), h: parseOrThrow(arr[1]) }; + } + } catch (err) { + // Ignore the error + } + return null; +} diff --git a/src/panels/dev-info/ha-loaded-components.js b/src/panels/dev-info/ha-loaded-components.js index 30ec7f06be..715e2d021b 100644 --- a/src/panels/dev-info/ha-loaded-components.js +++ b/src/panels/dev-info/ha-loaded-components.js @@ -1,59 +1,59 @@ -import "@polymer/paper-dialog-scrollable/paper-dialog-scrollable"; -import "@polymer/paper-dialog/paper-dialog"; -import { html } from "@polymer/polymer/lib/utils/html-tag"; -import { PolymerElement } from "@polymer/polymer/polymer-element"; - -import "../../resources/ha-style"; - -import EventsMixin from "../../mixins/events-mixin"; - -/* - * @appliesMixin EventsMixin - */ -class HaLoadedComponents extends EventsMixin(PolymerElement) { - static get template() { - return html` - - -

Loaded Components

- -

The following components are currently loaded:

- -
-
- `; - } - - static get properties() { - return { - _hass: Object, - _components: Array, - - _opened: { - type: Boolean, - value: false, - }, - }; - } - - ready() { - super.ready(); - } - - showDialog({ hass }) { - this.hass = hass; - this._opened = true; - this._components = this.hass.config.components.sort(); - setTimeout(() => this.$.dialog.center(), 0); - } -} - -customElements.define("ha-loaded-components", HaLoadedComponents); +import "@polymer/paper-dialog-scrollable/paper-dialog-scrollable"; +import "@polymer/paper-dialog/paper-dialog"; +import { html } from "@polymer/polymer/lib/utils/html-tag"; +import { PolymerElement } from "@polymer/polymer/polymer-element"; + +import "../../resources/ha-style"; + +import EventsMixin from "../../mixins/events-mixin"; + +/* + * @appliesMixin EventsMixin + */ +class HaLoadedComponents extends EventsMixin(PolymerElement) { + static get template() { + return html` + + +

Loaded Components

+ +

The following components are currently loaded:

+ +
+
+ `; + } + + static get properties() { + return { + _hass: Object, + _components: Array, + + _opened: { + type: Boolean, + value: false, + }, + }; + } + + ready() { + super.ready(); + } + + showDialog({ hass }) { + this.hass = hass; + this._opened = true; + this._components = this.hass.config.components.sort(); + setTimeout(() => this.$.dialog.center(), 0); + } +} + +customElements.define("ha-loaded-components", HaLoadedComponents); diff --git a/src/panels/lovelace/cards/hui-alarm-panel-card.js b/src/panels/lovelace/cards/hui-alarm-panel-card.js index 967234d737..49015fbf1a 100644 --- a/src/panels/lovelace/cards/hui-alarm-panel-card.js +++ b/src/panels/lovelace/cards/hui-alarm-panel-card.js @@ -1,258 +1,258 @@ -import { html } from "@polymer/polymer/lib/utils/html-tag"; -import { PolymerElement } from "@polymer/polymer/polymer-element"; - -import "../../../components/ha-card"; - -import EventsMixin from "../../../mixins/events-mixin"; -import LocalizeMixin from "../../../mixins/localize-mixin"; -import "../../../components/ha-label-badge"; - -/* - * @appliesMixin EventsMixin - */ - -const Icons = { - armed_away: "hass:security-lock", - armed_custom_bypass: "hass:security", - armed_home: "hass:security-home", - armed_night: "hass:security-home", - disarmed: "hass:verified", - pending: "hass:shield-outline", - triggered: "hass:bell-ring", -}; - -class HuiAlarmPanelCard extends LocalizeMixin(EventsMixin(PolymerElement)) { - static get template() { - return html` - - - - - - `; - } - - 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); +import { html } from "@polymer/polymer/lib/utils/html-tag"; +import { PolymerElement } from "@polymer/polymer/polymer-element"; + +import "../../../components/ha-card"; + +import EventsMixin from "../../../mixins/events-mixin"; +import LocalizeMixin from "../../../mixins/localize-mixin"; +import "../../../components/ha-label-badge"; + +/* + * @appliesMixin EventsMixin + */ + +const Icons = { + armed_away: "hass:security-lock", + armed_custom_bypass: "hass:security", + armed_home: "hass:security-home", + armed_night: "hass:security-home", + disarmed: "hass:verified", + pending: "hass:shield-outline", + triggered: "hass:bell-ring", +}; + +class HuiAlarmPanelCard extends LocalizeMixin(EventsMixin(PolymerElement)) { + static get template() { + return html` + + + + + + `; + } + + 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 0852483643..3ad239be4f 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"; -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); +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 2a27b2923f..066665c541 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"; -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` -
-
${title}
- ${ - show_header_toggle === false - ? 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` +
+
${title}
+ ${ + show_header_toggle === false + ? 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-filter-card.js b/src/panels/lovelace/cards/hui-entity-filter-card.js index c76299b00b..25403b9582 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"; - -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); +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 5061ad26ba..92ddcb9d1e 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"; -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); +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-history-graph-card.js b/src/panels/lovelace/cards/hui-history-graph-card.js index 66e56b326d..7ed24c4d28 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"; -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); +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 3559e4cdb1..da193c0fab 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"; - -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 11c96a22fe..13827cc89e 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"; - -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); +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 b9903dd697..b9957c41bb 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"; -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)); - } -} +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 53f8dfdee0..d8f97ca029 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"; -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` -
-
-
- -
-
${this._config.name || - computeStateName(stateObj)}
-
-
- ` - } - -
- `; - } - - 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` +
+
+
+ +
+
${this._config.name || + computeStateName(stateObj)}
+
+
+ ` + } + +
+ `; + } + + 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 4f213b88a5..814b2ba644 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"; -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); +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 6c71fd904a..827133de50 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"; - -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); +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 96ad0e75c6..21dc0f2d02 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"; - -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); +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 881f3a46df..44413027c0 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"; -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); +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 2251f0bb27..d30a5b2c01 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"; - -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); +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 77898ab8cb..bea66fe1e7 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"; -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); +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 0b7783a3c8..f9be3f6b5a 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"; -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` - - - - -
- -
- -
-
- -
-
-
- `; - } - - 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` + + + + +
+ +
+ +
+
+ +
+
+
+ `; + } + + 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 500bc61df9..ce88f4e3ac 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"; - -import LegacyWrapperCard from "./hui-legacy-wrapper-card"; - -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 ea25043f30..f0fc56a771 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"; -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()} - -
-
- -
-
- ${this._computeName(_entity)} -
-
-
- ${_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()} + +
+
+ +
+
+ ${this._computeName(_entity)} +
+
+
+ ${_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 709bf441d2..7193b99792 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"; - -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 708a7bcb0a..ef5ca1079f 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"; -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()} - -
-
-
-
${computeStateName(stateObj)}
-
- ${ - stateObj.attributes.current_temperature - } - ${ - this.hass.config.unit_system.temperature - } - -
-
-
-
${this.localize( - `state.climate.${stateObj.state}` - )}
-
- ${(stateObj.attributes.operation_list || []).map((modeItem) => - this._renderIcon(modeItem, mode) - )} -
-
-
- - `; - } - - 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()} + +
+
+
+
${computeStateName(stateObj)}
+
+ ${ + stateObj.attributes.current_temperature + } + ${ + this.hass.config.unit_system.temperature + } + +
+
+
+
${this.localize( + `state.climate.${stateObj.state}` + )}
+
+ ${(stateObj.attributes.operation_list || []).map((modeItem) => + this._renderIcon(modeItem, mode) + )} +
+
+
+ + `; + } + + 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 3d13d2827c..740671179d 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"; - -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 9ad4374231..96c8470b07 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"; - -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); +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 0e9f57316f..691b2138f5 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"; - -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/compute-tooltip.ts b/src/panels/lovelace/common/compute-tooltip.ts index d4441a9278..59920ec8e1 100644 --- a/src/panels/lovelace/common/compute-tooltip.ts +++ b/src/panels/lovelace/common/compute-tooltip.ts @@ -1,38 +1,38 @@ -import computeStateName from "../../../common/entity/compute_state_name"; -import { HomeAssistant } from "../../../types"; -import { LovelaceElementConfig } from "../elements/types"; - -export const computeTooltip = ( - hass: HomeAssistant, - config: LovelaceElementConfig -): string => { - if (config.title) { - return config.title; - } - - let stateName = ""; - let tooltip: string; - - if (config.entity) { - stateName = - config.entity in hass.states - ? computeStateName(hass.states[config.entity]) - : config.entity; - } - - switch (config.tap_action) { - case "navigate": - tooltip = `Navigate to ${config.navigation_path}`; - break; - case "toggle": - tooltip = `Toggle ${stateName}`; - break; - case "call-service": - tooltip = `Call service ${config.service}`; - break; - default: - tooltip = `Show more-info: ${stateName}`; - } - - return tooltip; -}; +import computeStateName from "../../../common/entity/compute_state_name"; +import { HomeAssistant } from "../../../types"; +import { LovelaceElementConfig } from "../elements/types"; + +export const computeTooltip = ( + hass: HomeAssistant, + config: LovelaceElementConfig +): string => { + if (config.title) { + return config.title; + } + + let stateName = ""; + let tooltip: string; + + if (config.entity) { + stateName = + config.entity in hass.states + ? computeStateName(hass.states[config.entity]) + : config.entity; + } + + switch (config.tap_action) { + case "navigate": + tooltip = `Navigate to ${config.navigation_path}`; + break; + case "toggle": + tooltip = `Toggle ${stateName}`; + break; + case "call-service": + tooltip = `Call service ${config.service}`; + break; + default: + tooltip = `Show more-info: ${stateName}`; + } + + return tooltip; +}; diff --git a/src/panels/lovelace/common/create-card-element.js b/src/panels/lovelace/common/create-card-element.js index 9417fa8989..f7cc0db4a3 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"; - -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); -} +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 b02b86dbcf..c9d10924db 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"; -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); -} +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 f973db948d..2d775def52 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"; - -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); -} +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 21820da7f7..91deadcd7b 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"; -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); -} +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 b3a990e74b..7f46f4414b 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"; -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 }); - }); -} +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 dc7e5d1a4d..6a19f0ad74 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"; - -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 c996bf4bac..c7d1321228 100644 --- a/src/panels/lovelace/common/handle-click.ts +++ b/src/panels/lovelace/common/handle-click.ts @@ -1,44 +1,44 @@ -import { HomeAssistant } from "../../../types"; -import { LovelaceElementConfig } from "../elements/types"; -import { fireEvent } from "../../../common/dom/fire_event"; -import { navigate } from "../../../common/navigate"; -import toggleEntity from "../../../../src/panels/lovelace/common/entity/toggle-entity"; - -export const handleClick = ( - node: HTMLElement, - hass: HomeAssistant, - config: LovelaceElementConfig, - hold: boolean -): void => { - let action = config.tap_action || "more-info"; - - if (hold && config.hold_action) { - action = config.hold_action; - } - - if (action === "none") { - return; - } - - switch (action) { - case "more-info": - fireEvent(node, "hass-more-info", { entityId: config.entity }); - break; - case "navigate": - navigate(node, config.navigation_path ? config.navigation_path : ""); - break; - case "toggle": - toggleEntity(hass, config.entity); - break; - case "call-service": { - if (config.service) { - const [domain, service] = config.service.split(".", 2); - const serviceData = { - entity_id: config.entity, - ...config.service_data, - }; - hass.callService(domain, service, serviceData); - } - } - } -}; +import { HomeAssistant } from "../../../types"; +import { LovelaceElementConfig } from "../elements/types"; +import { fireEvent } from "../../../common/dom/fire_event"; +import { navigate } from "../../../common/navigate"; +import toggleEntity from "../../../../src/panels/lovelace/common/entity/toggle-entity"; + +export const handleClick = ( + node: HTMLElement, + hass: HomeAssistant, + config: LovelaceElementConfig, + hold: boolean +): void => { + let action = config.tap_action || "more-info"; + + if (hold && config.hold_action) { + action = config.hold_action; + } + + if (action === "none") { + return; + } + + switch (action) { + case "more-info": + fireEvent(node, "hass-more-info", { entityId: config.entity }); + break; + case "navigate": + navigate(node, config.navigation_path ? config.navigation_path : ""); + break; + case "toggle": + toggleEntity(hass, config.entity); + break; + case "call-service": { + if (config.service) { + const [domain, service] = config.service.split(".", 2); + const serviceData = { + entity_id: config.entity, + ...config.service_data, + }; + hass.callService(domain, service, serviceData); + } + } + } +}; diff --git a/src/panels/lovelace/common/process-config-entities.js b/src/panels/lovelace/common/process-config-entities.js index f8082886b3..68e6e248c2 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"; - -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 1e7b343c12..2392223ed5 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"; -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` - - -
- EDIT -
- `; - } - 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` + + +
+ EDIT +
+ `; + } + 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 7055c2a962..5d5264c12f 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"; -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); +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 092a065c22..161b21c4f3 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"; -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} - - - `; - } - - static get styleTemplate() { - return html` - - `; - } - - static get stateBadgeTemplate() { - return html` - - `; - } - - static get infoTemplate() { - return html` -
- [[_computeName(config.name, _stateObj)]] -
- - -
-
- `; - } - - 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} + + + `; + } + + static get styleTemplate() { + return html` + + `; + } + + static get stateBadgeTemplate() { + return html` + + `; + } + + static get infoTemplate() { + return html` +
+ [[_computeName(config.name, _stateObj)]] +
+ + +
+
+ `; + } + + 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 7164da4c4f..8836f65fc1 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"; -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); +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 9924c40eb8..c613239afc 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"; -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 -); +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 62ce12b5af..f1e051ccb2 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"; -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')]]
- -
-
- - -
-
- `; - } - - 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')]]
+ +
+
+ + +
+
+ `; + } + + 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 de9d8c8085..099831ae7a 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"; -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 -); +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 c54664c6ba..66ed2f2a2a 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"; -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); +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 5c26714296..ad352b9b75 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"; -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); +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 86bf11d006..24a55b930f 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"; -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 -); +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 c674b156c9..bc4b2eea0c 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"; - -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

- - - - -
- Cancel - Save -
-
- `; - } - - 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

+ + + + +
+ Cancel + Save +
+
+ `; + } + + 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 9eae71466a..3ca81c2c5f 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"; - -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 f1bdaa58e0..e1977849c7 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"; - -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); +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 3a3fce32a8..ea45b16ba0 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"; - -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()} - - `; - } - - 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()} + + `; + } + + 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 5f5992a9cf..2c1af807f5 100644 --- a/src/panels/lovelace/elements/hui-image-element.ts +++ b/src/panels/lovelace/elements/hui-image-element.ts @@ -1,94 +1,94 @@ -import { html, LitElement } from "@polymer/lit-element"; - -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"; -import { HomeAssistant } from "../../../types"; -import { TemplateResult } from "lit-html"; - -interface Config extends LovelaceElementConfig { - image?: string; - state_image?: string; - camera_image?: string; - filter?: string; - state_filter?: string; - aspect_ratio?: string; -} - -export class HuiImageElement extends hassLocalizeLitMixin(LitElement) - implements LovelaceElement { - public hass?: HomeAssistant; - private _config?: Config; - - static get properties() { - return { hass: {}, _config: {} }; - } - - public setConfig(config: Config): void { - if (!config) { - throw Error("Error in element configuration"); - } - - this.classList.toggle("clickable", config.tap_action !== "none"); - this._config = config; - } - - protected render(): TemplateResult { - if (!this._config) { - return html``; - } - - return html` - ${this.renderStyle()} - - `; - } - - private renderStyle(): TemplateResult { - return html` - - `; - } - - private _handleClick() { - handleClick(this, this.hass!, this._config!, false); - } - - private _handleHold() { - handleClick(this, this.hass!, this._config!, true); - } -} - -declare global { - interface HTMLElementTagNameMap { - "hui-image-element": HuiImageElement; - } -} - -customElements.define("hui-image-element", HuiImageElement); +import { html, LitElement } from "@polymer/lit-element"; + +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"; +import { HomeAssistant } from "../../../types"; +import { TemplateResult } from "lit-html"; + +interface Config extends LovelaceElementConfig { + image?: string; + state_image?: string; + camera_image?: string; + filter?: string; + state_filter?: string; + aspect_ratio?: string; +} + +export class HuiImageElement extends hassLocalizeLitMixin(LitElement) + implements LovelaceElement { + public hass?: HomeAssistant; + private _config?: Config; + + static get properties() { + return { hass: {}, _config: {} }; + } + + public setConfig(config: Config): void { + if (!config) { + throw Error("Error in element configuration"); + } + + this.classList.toggle("clickable", config.tap_action !== "none"); + this._config = config; + } + + protected render(): TemplateResult { + if (!this._config) { + return html``; + } + + return html` + ${this.renderStyle()} + + `; + } + + private renderStyle(): TemplateResult { + return html` + + `; + } + + private _handleClick() { + handleClick(this, this.hass!, this._config!, false); + } + + private _handleHold() { + handleClick(this, this.hass!, this._config!, true); + } +} + +declare global { + interface HTMLElementTagNameMap { + "hui-image-element": HuiImageElement; + } +} + +customElements.define("hui-image-element", HuiImageElement); diff --git a/src/panels/lovelace/elements/hui-service-button-element.ts b/src/panels/lovelace/elements/hui-service-button-element.ts index 7a39f19d35..157dfae9ba 100644 --- a/src/panels/lovelace/elements/hui-service-button-element.ts +++ b/src/panels/lovelace/elements/hui-service-button-element.ts @@ -1,74 +1,74 @@ -import { html, LitElement } from "@polymer/lit-element"; -import { TemplateResult } from "lit-html"; - -import "../../../components/buttons/ha-call-service-button"; - -import { LovelaceElement, LovelaceElementConfig } from "./types"; -import { HomeAssistant } from "../../../types"; - -export class HuiServiceButtonElement extends LitElement - implements LovelaceElement { - public hass?: HomeAssistant; - private _config?: LovelaceElementConfig; - private _domain?: string; - private _service?: string; - - static get properties() { - return { _config: {} }; - } - - public setConfig(config: LovelaceElementConfig): void { - if (!config || !config.service) { - throw Error("Invalid Configuration: 'service' required"); - } - - [this._domain, this._service] = config.service.split(".", 2); - - if (!this._domain) { - throw Error("Invalid Configuration: 'service' does not have a domain"); - } - - if (!this._service) { - throw Error( - "Invalid Configuration: 'service' does not have a service name" - ); - } - - this._config = config; - } - - protected render(): TemplateResult { - if (!this._config) { - return html``; - } - - return html` - ${this.renderStyle()} - ${this._config.title} - `; - } - - private renderStyle(): TemplateResult { - return html` - - `; - } -} - -declare global { - interface HTMLElementTagNameMap { - "hui-service-button-element": HuiServiceButtonElement; - } -} - -customElements.define("hui-service-button-element", HuiServiceButtonElement); +import { html, LitElement } from "@polymer/lit-element"; +import { TemplateResult } from "lit-html"; + +import "../../../components/buttons/ha-call-service-button"; + +import { LovelaceElement, LovelaceElementConfig } from "./types"; +import { HomeAssistant } from "../../../types"; + +export class HuiServiceButtonElement extends LitElement + implements LovelaceElement { + public hass?: HomeAssistant; + private _config?: LovelaceElementConfig; + private _domain?: string; + private _service?: string; + + static get properties() { + return { _config: {} }; + } + + public setConfig(config: LovelaceElementConfig): void { + if (!config || !config.service) { + throw Error("Invalid Configuration: 'service' required"); + } + + [this._domain, this._service] = config.service.split(".", 2); + + if (!this._domain) { + throw Error("Invalid Configuration: 'service' does not have a domain"); + } + + if (!this._service) { + throw Error( + "Invalid Configuration: 'service' does not have a service name" + ); + } + + this._config = config; + } + + protected render(): TemplateResult { + if (!this._config) { + return html``; + } + + return html` + ${this.renderStyle()} + ${this._config.title} + `; + } + + private renderStyle(): TemplateResult { + return html` + + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + "hui-service-button-element": HuiServiceButtonElement; + } +} + +customElements.define("hui-service-button-element", HuiServiceButtonElement); diff --git a/src/panels/lovelace/elements/hui-state-badge-element.ts b/src/panels/lovelace/elements/hui-state-badge-element.ts index 0c27e33f6d..f3189c990f 100644 --- a/src/panels/lovelace/elements/hui-state-badge-element.ts +++ b/src/panels/lovelace/elements/hui-state-badge-element.ts @@ -1,53 +1,53 @@ -import { html, LitElement } from "@polymer/lit-element"; - -import "../../../components/entity/ha-state-label-badge"; - -import computeStateName from "../../../common/entity/compute_state_name"; -import { LovelaceElement, LovelaceElementConfig } from "./types"; -import { HomeAssistant } from "../../../types"; -import { TemplateResult } from "lit-html"; - -export class HuiStateBadgeElement extends LitElement - implements LovelaceElement { - public hass?: HomeAssistant; - private _config?: LovelaceElementConfig; - - static get properties() { - return { hass: {}, _config: {} }; - } - - public setConfig(config: LovelaceElementConfig): void { - if (!config.entity) { - throw Error("Invalid Configuration: 'entity' required"); - } - - this._config = config; - } - - protected render(): TemplateResult { - if ( - !this._config || - !this.hass || - !this.hass.states[this._config.entity!] - ) { - return html``; - } - - const state = this.hass.states[this._config.entity!]; - return html` - - `; - } -} - -declare global { - interface HTMLElementTagNameMap { - "hui-state-badge-element": HuiStateBadgeElement; - } -} - -customElements.define("hui-state-badge-element", HuiStateBadgeElement); +import { html, LitElement } from "@polymer/lit-element"; + +import "../../../components/entity/ha-state-label-badge"; + +import computeStateName from "../../../common/entity/compute_state_name"; +import { LovelaceElement, LovelaceElementConfig } from "./types"; +import { HomeAssistant } from "../../../types"; +import { TemplateResult } from "lit-html"; + +export class HuiStateBadgeElement extends LitElement + implements LovelaceElement { + public hass?: HomeAssistant; + private _config?: LovelaceElementConfig; + + static get properties() { + return { hass: {}, _config: {} }; + } + + public setConfig(config: LovelaceElementConfig): void { + if (!config.entity) { + throw Error("Invalid Configuration: 'entity' required"); + } + + this._config = config; + } + + protected render(): TemplateResult { + if ( + !this._config || + !this.hass || + !this.hass.states[this._config.entity!] + ) { + return html``; + } + + const state = this.hass.states[this._config.entity!]; + return html` + + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + "hui-state-badge-element": HuiStateBadgeElement; + } +} + +customElements.define("hui-state-badge-element", HuiStateBadgeElement); diff --git a/src/panels/lovelace/elements/hui-state-icon-element.ts b/src/panels/lovelace/elements/hui-state-icon-element.ts index 5615af8614..812ea10555 100644 --- a/src/panels/lovelace/elements/hui-state-icon-element.ts +++ b/src/panels/lovelace/elements/hui-state-icon-element.ts @@ -1,77 +1,77 @@ -import { html, LitElement } from "@polymer/lit-element"; -import { TemplateResult } from "lit-html"; - -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"; -import { HomeAssistant } from "../../../types"; - -export class HuiStateIconElement extends hassLocalizeLitMixin(LitElement) - implements LovelaceElement { - public hass?: HomeAssistant; - private _config?: LovelaceElementConfig; - - static get properties() { - return { hass: {}, _config: {} }; - } - - public setConfig(config: LovelaceElementConfig): void { - if (!config.entity) { - throw Error("Invalid Configuration: 'entity' required"); - } - - this._config = config; - } - - protected render(): TemplateResult { - if ( - !this._config || - !this.hass || - !this.hass.states[this._config.entity!] - ) { - return html``; - } - - const state = this.hass!.states[this._config.entity!]; - return html` - ${this.renderStyle()} - - `; - } - - private renderStyle(): TemplateResult { - return html` - - `; - } - - private _handleClick() { - handleClick(this, this.hass!, this._config!, false); - } - - private _handleHold() { - handleClick(this, this.hass!, this._config!, true); - } -} - -declare global { - interface HTMLElementTagNameMap { - "hui-state-icon-element": HuiStateIconElement; - } -} - -customElements.define("hui-state-icon-element", HuiStateIconElement); +import { html, LitElement } from "@polymer/lit-element"; +import { TemplateResult } from "lit-html"; + +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"; +import { HomeAssistant } from "../../../types"; + +export class HuiStateIconElement extends hassLocalizeLitMixin(LitElement) + implements LovelaceElement { + public hass?: HomeAssistant; + private _config?: LovelaceElementConfig; + + static get properties() { + return { hass: {}, _config: {} }; + } + + public setConfig(config: LovelaceElementConfig): void { + if (!config.entity) { + throw Error("Invalid Configuration: 'entity' required"); + } + + this._config = config; + } + + protected render(): TemplateResult { + if ( + !this._config || + !this.hass || + !this.hass.states[this._config.entity!] + ) { + return html``; + } + + const state = this.hass!.states[this._config.entity!]; + return html` + ${this.renderStyle()} + + `; + } + + private renderStyle(): TemplateResult { + return html` + + `; + } + + private _handleClick() { + handleClick(this, this.hass!, this._config!, false); + } + + private _handleHold() { + handleClick(this, this.hass!, this._config!, true); + } +} + +declare global { + interface HTMLElementTagNameMap { + "hui-state-icon-element": HuiStateIconElement; + } +} + +customElements.define("hui-state-icon-element", HuiStateIconElement); diff --git a/src/panels/lovelace/elements/hui-state-label-element.ts b/src/panels/lovelace/elements/hui-state-label-element.ts index 6ad1a6b93d..20b8c3287d 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"; - -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()} -
- ${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()} +
+ ${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 2bc9a156d1..32dd257b2a 100644 --- a/src/panels/lovelace/entity-rows/hui-climate-entity-row.ts +++ b/src/panels/lovelace/entity-rows/hui-climate-entity-row.ts @@ -1,65 +1,65 @@ -import { html, LitElement } from "@polymer/lit-element"; -import { TemplateResult } from "lit-html"; - -import "../../../components/ha-climate-state"; -import "../components/hui-generic-entity-row"; - -import { HomeAssistant } from "../../../types"; -import { EntityRow, EntityConfig } from "./types"; - -class HuiClimateEntityRow extends LitElement implements EntityRow { - public hass?: HomeAssistant; - private _config?: EntityConfig; - - static get properties() { - return { - hass: {}, - _config: {}, - }; - } - - public setConfig(config: EntityConfig): void { - if (!config || !config.entity) { - throw new Error("Invalid Configuration: 'entity' required"); - } - - this._config = config; - } - - protected render(): TemplateResult { - if (!this.hass || !this._config) { - return html``; - } - - return html` - ${this.renderStyle()} - - - - `; - } - - private renderStyle(): TemplateResult { - return html` - - `; - } -} - -declare global { - interface HTMLElementTagNameMap { - "hui-climate-entity-row": HuiClimateEntityRow; - } -} - -customElements.define("hui-climate-entity-row", HuiClimateEntityRow); +import { html, LitElement } from "@polymer/lit-element"; +import { TemplateResult } from "lit-html"; + +import "../../../components/ha-climate-state"; +import "../components/hui-generic-entity-row"; + +import { HomeAssistant } from "../../../types"; +import { EntityRow, EntityConfig } from "./types"; + +class HuiClimateEntityRow extends LitElement implements EntityRow { + public hass?: HomeAssistant; + private _config?: EntityConfig; + + static get properties() { + return { + hass: {}, + _config: {}, + }; + } + + public setConfig(config: EntityConfig): void { + if (!config || !config.entity) { + throw new Error("Invalid Configuration: 'entity' required"); + } + + this._config = config; + } + + protected render(): TemplateResult { + if (!this.hass || !this._config) { + return html``; + } + + return html` + ${this.renderStyle()} + + + + `; + } + + private renderStyle(): TemplateResult { + return html` + + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + "hui-climate-entity-row": HuiClimateEntityRow; + } +} + +customElements.define("hui-climate-entity-row", HuiClimateEntityRow); 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 b4c3de6953..4cdc254166 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"; -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); +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 77720fd0bf..890b1946a0 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"; -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` - - - `; - } - - 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` + + + `; + } + + 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 33b8272216..51435e6f18 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"; -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` -
- - -
- `; - } - - 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` +
+ + +
+ `; + } + + 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 61ce4317c4..a1f6815cff 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"; -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} - - - `; - } - - 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} + + + `; + } + + 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 dc27bb8315..e6e00de53d 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"; -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); +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 f75559d30a..57a6835813 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"; -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); +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 b6d90e944f..44f5bad81c 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"; -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` - - - -
- [[_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` + + + +
+ [[_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 f0d1544335..62cf454c74 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"; -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); +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 db5b948160..baf27f55f5 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"; -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` - - - `; - } - - 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` + + + `; + } + + 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 9c78c3a4e9..74548b7062 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"; -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); +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 ad9d979184..8454f37ba4 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"; -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); +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 c75055635f..531462ff3b 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"; -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` - - - `; - } - - 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` + + + `; + } + + 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 1d11e2d0f3..b0a2476f6c 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"; -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` - - - - - `; - } - - 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` + + + + + `; + } + + 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 3e937ca764..24f7fe6167 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"; -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` - - - - - - - - -
- - - -
-
- -
- - `; - } - - 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); +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` + + + + + + + + +
+ + + +
+
+ +
+ + `; + } + + 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 7b066f9269..d2b84f59d1 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"; -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); +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 91931c71ed..770fdf878b 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"; -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); +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 44c7f2354c..4ac0d03ff4 100644 --- a/src/panels/lovelace/special-rows/hui-call-service-row.ts +++ b/src/panels/lovelace/special-rows/hui-call-service-row.ts @@ -1,93 +1,93 @@ -import { html, LitElement } from "@polymer/lit-element"; -import "@polymer/paper-button/paper-button"; - -import "../../../components/ha-icon"; - -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 { - public hass?: HomeAssistant; - private _config?: CallServiceConfig; - - static get properties() { - return { - hass: {}, - _config: {}, - }; - } - - public setConfig(config: CallServiceConfig): void { - if (!config || !config.name || !config.service) { - throw new Error("Error in card configuration."); - } - - this._config = { icon: "hass:remote", action_name: "Run", ...config }; - } - - protected render(): TemplateResult { - if (!this._config) { - return html``; - } - - return html` - ${this.renderStyle()} - -
-
- ${this._config.name} -
- ${this._config.action_name} -
- `; - } - - private renderStyle(): TemplateResult { - return html` - - `; - } - - private _callService() { - callService(this._config, this.hass); - } -} - -declare global { - interface HTMLElementTagNameMap { - "hui-call-service-row": HuiCallServiceRow; - } -} - -customElements.define("hui-call-service-row", HuiCallServiceRow); +import { html, LitElement } from "@polymer/lit-element"; +import "@polymer/paper-button/paper-button"; + +import "../../../components/ha-icon"; + +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 { + public hass?: HomeAssistant; + private _config?: CallServiceConfig; + + static get properties() { + return { + hass: {}, + _config: {}, + }; + } + + public setConfig(config: CallServiceConfig): void { + if (!config || !config.name || !config.service) { + throw new Error("Error in card configuration."); + } + + this._config = { icon: "hass:remote", action_name: "Run", ...config }; + } + + protected render(): TemplateResult { + if (!this._config) { + return html``; + } + + return html` + ${this.renderStyle()} + +
+
+ ${this._config.name} +
+ ${this._config.action_name} +
+ `; + } + + private renderStyle(): TemplateResult { + return html` + + `; + } + + private _callService() { + callService(this._config, this.hass); + } +} + +declare global { + interface HTMLElementTagNameMap { + "hui-call-service-row": HuiCallServiceRow; + } +} + +customElements.define("hui-call-service-row", HuiCallServiceRow); diff --git a/src/panels/lovelace/special-rows/hui-divider-row.ts b/src/panels/lovelace/special-rows/hui-divider-row.ts index 1720063db7..f1feced482 100644 --- a/src/panels/lovelace/special-rows/hui-divider-row.ts +++ b/src/panels/lovelace/special-rows/hui-divider-row.ts @@ -1,53 +1,53 @@ -import { html, LitElement } from "@polymer/lit-element"; -import { EntityRow, DividerConfig } from "../entity-rows/types"; -import { HomeAssistant } from "../../../types"; -import { TemplateResult } from "lit-html"; - -class HuiDividerRow extends LitElement implements EntityRow { - public hass?: HomeAssistant; - private _config?: DividerConfig; - - static get properties() { - return { - _config: {}, - }; - } - - public setConfig(config): void { - if (!config) { - throw new Error("Error in card configuration."); - } - - this._config = { - style: { - height: "1px", - "background-color": "var(--secondary-text-color)", - }, - ...config, - }; - } - - protected render(): TemplateResult { - if (!this._config) { - return html``; - } - - const el = document.createElement("div"); - - Object.keys(this._config.style).forEach((prop) => { - el.style.setProperty(prop, this._config!.style[prop]); - }); - - return html` - ${el} - `; - } -} - -declare global { - interface HTMLElementTagNameMap { - "hui-divider-row": HuiDividerRow; - } -} - -customElements.define("hui-divider-row", HuiDividerRow); +import { html, LitElement } from "@polymer/lit-element"; +import { EntityRow, DividerConfig } from "../entity-rows/types"; +import { HomeAssistant } from "../../../types"; +import { TemplateResult } from "lit-html"; + +class HuiDividerRow extends LitElement implements EntityRow { + public hass?: HomeAssistant; + private _config?: DividerConfig; + + static get properties() { + return { + _config: {}, + }; + } + + public setConfig(config): void { + if (!config) { + throw new Error("Error in card configuration."); + } + + this._config = { + style: { + height: "1px", + "background-color": "var(--secondary-text-color)", + }, + ...config, + }; + } + + protected render(): TemplateResult { + if (!this._config) { + return html``; + } + + const el = document.createElement("div"); + + Object.keys(this._config.style).forEach((prop) => { + el.style.setProperty(prop, this._config!.style[prop]); + }); + + return html` + ${el} + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + "hui-divider-row": HuiDividerRow; + } +} + +customElements.define("hui-divider-row", HuiDividerRow); diff --git a/src/panels/lovelace/special-rows/hui-section-row.ts b/src/panels/lovelace/special-rows/hui-section-row.ts index 9033755203..a698e493ec 100644 --- a/src/panels/lovelace/special-rows/hui-section-row.ts +++ b/src/panels/lovelace/special-rows/hui-section-row.ts @@ -1,70 +1,70 @@ -import { html, LitElement } from "@polymer/lit-element"; -import { EntityRow, SectionConfig } from "../entity-rows/types"; -import { HomeAssistant } from "../../../types"; - -import "../../../components/ha-icon"; -import { TemplateResult } from "lit-html"; - -class HuiSectionRow extends LitElement implements EntityRow { - public hass?: HomeAssistant; - private _config?: SectionConfig; - - static get properties() { - return { - _config: {}, - }; - } - - public setConfig(config: SectionConfig): void { - if (!config) { - throw new Error("Error in card configuration."); - } - - this._config = config; - } - - protected render(): TemplateResult { - if (!this._config) { - return html``; - } - - return html` - ${this.renderStyle()} -
- ${ - this._config.label - ? html`
${this._config.label}
` - : html`` - } - `; - } - - private renderStyle(): TemplateResult { - return html` - - `; - } -} - -declare global { - interface HTMLElementTagNameMap { - "hui-section-row": HuiSectionRow; - } -} - -customElements.define("hui-section-row", HuiSectionRow); +import { html, LitElement } from "@polymer/lit-element"; +import { EntityRow, SectionConfig } from "../entity-rows/types"; +import { HomeAssistant } from "../../../types"; + +import "../../../components/ha-icon"; +import { TemplateResult } from "lit-html"; + +class HuiSectionRow extends LitElement implements EntityRow { + public hass?: HomeAssistant; + private _config?: SectionConfig; + + static get properties() { + return { + _config: {}, + }; + } + + public setConfig(config: SectionConfig): void { + if (!config) { + throw new Error("Error in card configuration."); + } + + this._config = config; + } + + protected render(): TemplateResult { + if (!this._config) { + return html``; + } + + return html` + ${this.renderStyle()} +
+ ${ + this._config.label + ? html`
${this._config.label}
` + : html`` + } + `; + } + + private renderStyle(): TemplateResult { + return html` + + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + "hui-section-row": HuiSectionRow; + } +} + +customElements.define("hui-section-row", HuiSectionRow); diff --git a/src/panels/lovelace/special-rows/hui-weblink-row.ts b/src/panels/lovelace/special-rows/hui-weblink-row.ts index a9ff56c400..2b42031240 100644 --- a/src/panels/lovelace/special-rows/hui-weblink-row.ts +++ b/src/panels/lovelace/special-rows/hui-weblink-row.ts @@ -1,75 +1,75 @@ -import { html, LitElement } from "@polymer/lit-element"; -import { EntityRow, WeblinkConfig } from "../entity-rows/types"; -import { HomeAssistant } from "../../../types"; - -import "../../../components/ha-icon"; - -import { TemplateResult } from "lit-html"; - -class HuiWeblinkRow extends LitElement implements EntityRow { - public hass?: HomeAssistant; - private _config?: WeblinkConfig; - - static get properties() { - return { - _config: {}, - }; - } - - public setConfig(config: WeblinkConfig): void { - if (!config || !config.url) { - throw new Error("Invalid Configuration: 'url' required"); - } - - this._config = { - icon: "hass:link", - name: config.url, - ...config, - }; - } - - protected render(): TemplateResult { - if (!this._config) { - return html``; - } - - return html` - ${this.renderStyle()} - - -
${this._config.name}
-
- `; - } - - private renderStyle(): TemplateResult { - return html` - - `; - } -} - -declare global { - interface HTMLElementTagNameMap { - "hui-weblink-row": HuiWeblinkRow; - } -} - -customElements.define("hui-weblink-row", HuiWeblinkRow); +import { html, LitElement } from "@polymer/lit-element"; +import { EntityRow, WeblinkConfig } from "../entity-rows/types"; +import { HomeAssistant } from "../../../types"; + +import "../../../components/ha-icon"; + +import { TemplateResult } from "lit-html"; + +class HuiWeblinkRow extends LitElement implements EntityRow { + public hass?: HomeAssistant; + private _config?: WeblinkConfig; + + static get properties() { + return { + _config: {}, + }; + } + + public setConfig(config: WeblinkConfig): void { + if (!config || !config.url) { + throw new Error("Invalid Configuration: 'url' required"); + } + + this._config = { + icon: "hass:link", + name: config.url, + ...config, + }; + } + + protected render(): TemplateResult { + if (!this._config) { + return html``; + } + + return html` + ${this.renderStyle()} + + +
${this._config.name}
+
+ `; + } + + private renderStyle(): TemplateResult { + return html` + + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + "hui-weblink-row": HuiWeblinkRow; + } +} + +customElements.define("hui-weblink-row", HuiWeblinkRow); diff --git a/test-mocha/common/util/parse_aspect_ratio_test.js b/test-mocha/common/util/parse_aspect_ratio_test.js index 477a387a99..dd6af4f3cc 100644 --- a/test-mocha/common/util/parse_aspect_ratio_test.js +++ b/test-mocha/common/util/parse_aspect_ratio_test.js @@ -1,53 +1,53 @@ -import assert from "assert"; - -import parseAspectRatio from "../../../src/common/util/parse-aspect-ratio"; - -describe("parseAspectRatio", () => { - const ratio16by9 = { w: 16, h: 9 }; - const ratio178 = { w: 1.78, h: 1 }; - - it("Parses 16x9", () => { - const r = parseAspectRatio("16x9"); - assert.deepEqual(r, ratio16by9); - }); - - it("Parses 16:9", () => { - const r = parseAspectRatio("16:9"); - assert.deepEqual(r, ratio16by9); - }); - - it("Parses 1.78x1", () => { - const r = parseAspectRatio("1.78x1"); - assert.deepEqual(r, ratio178); - }); - - it("Parses 1.78:1", () => { - const r = parseAspectRatio("1.78:1"); - assert.deepEqual(r, ratio178); - }); - - it("Parses 1.78", () => { - const r = parseAspectRatio("1.78"); - assert.deepEqual(r, ratio178); - }); - - it("Skips null states", () => { - const r = parseAspectRatio(null); - assert.equal(r, null); - }); - - it("Skips empty states", () => { - const r = parseAspectRatio(" "); - assert.equal(r, null); - }); - - it("Skips invalid input", () => { - const r = parseAspectRatio("mary had a little lamb"); - assert.equal(r, null); - }); - - it("Skips invalid, but close input", () => { - const r = parseAspectRatio("mary:lamb"); - assert.equal(r, null); - }); -}); +import assert from "assert"; + +import parseAspectRatio from "../../../src/common/util/parse-aspect-ratio"; + +describe("parseAspectRatio", () => { + const ratio16by9 = { w: 16, h: 9 }; + const ratio178 = { w: 1.78, h: 1 }; + + it("Parses 16x9", () => { + const r = parseAspectRatio("16x9"); + assert.deepEqual(r, ratio16by9); + }); + + it("Parses 16:9", () => { + const r = parseAspectRatio("16:9"); + assert.deepEqual(r, ratio16by9); + }); + + it("Parses 1.78x1", () => { + const r = parseAspectRatio("1.78x1"); + assert.deepEqual(r, ratio178); + }); + + it("Parses 1.78:1", () => { + const r = parseAspectRatio("1.78:1"); + assert.deepEqual(r, ratio178); + }); + + it("Parses 1.78", () => { + const r = parseAspectRatio("1.78"); + assert.deepEqual(r, ratio178); + }); + + it("Skips null states", () => { + const r = parseAspectRatio(null); + assert.equal(r, null); + }); + + it("Skips empty states", () => { + const r = parseAspectRatio(" "); + assert.equal(r, null); + }); + + it("Skips invalid input", () => { + const r = parseAspectRatio("mary had a little lamb"); + assert.equal(r, null); + }); + + it("Skips invalid, but close input", () => { + const r = parseAspectRatio("mary:lamb"); + assert.equal(r, null); + }); +});