diff --git a/src/panels/lovelace/cards/hui-entities-card.js b/src/panels/lovelace/cards/hui-entities-card.js deleted file mode 100644 index 85e89af5f3..0000000000 --- a/src/panels/lovelace/cards/hui-entities-card.js +++ /dev/null @@ -1,150 +0,0 @@ -import "@polymer/iron-flex-layout/iron-flex-layout-classes.js"; -import { html } from "@polymer/polymer/lib/utils/html-tag.js"; -import { PolymerElement } from "@polymer/polymer/polymer-element.js"; - -import "../../../components/ha-card.js"; -import "../components/hui-entities-toggle.js"; - -import createRowElement from "../common/create-row-element.js"; -import computeDomain from "../../../common/entity/compute_domain.js"; -import { DOMAINS_HIDE_MORE_INFO } from "../../../common/const.js"; - -import EventsMixin from "../../../mixins/events-mixin.js"; - -/* - * @appliesMixin EventsMixin - */ -class HuiEntitiesCard extends EventsMixin(PolymerElement) { - static get template() { - return html` - - - - -
-
-`; - } - - static get properties() { - return { - hass: { - type: Object, - observer: "_hassChanged", - }, - _config: Object, - }; - } - - constructor() { - super(); - this._elements = []; - } - - ready() { - super.ready(); - if (this._config) this._buildConfig(); - } - - getCardSize() { - // +1 for the header - return 1 + (this._config ? this._config.entities.length : 0); - } - - _showHeaderToggle(show) { - // If show is undefined, we treat it as true - return show !== false; - } - - _showHeader(config) { - // Show header if either title or toggle configured to show in it - return config.title || config.show_header_toggle; - } - - setConfig(config) { - this._config = config; - this._rows = config.entities.map( - (item) => (typeof item === "string" ? { entity: item } : item) - ); - if (this.$) this._buildConfig(); - } - - _buildConfig() { - const root = this.$.states; - - while (root.lastChild) { - root.removeChild(root.lastChild); - } - - this._elements = []; - - for (const row of this._rows) { - const entityId = row.entity; - const element = createRowElement(row); - if ( - entityId && - !DOMAINS_HIDE_MORE_INFO.includes(computeDomain(entityId)) - ) { - element.classList.add("state-card-dialog"); - element.addEventListener("click", () => - this.fire("hass-more-info", { entityId }) - ); - } - element.hass = this.hass; - this._elements.push(element); - const container = document.createElement("div"); - container.appendChild(element); - root.appendChild(container); - } - } - - _hassChanged(hass) { - this._elements.forEach((element) => { - element.hass = hass; - }); - } - - _filterEntities(items) { - return items - .filter((item) => typeof item === "string" || item.entity) - .map((item) => (typeof item === "string" ? item : item.entity)); - } -} - -customElements.define("hui-entities-card", HuiEntitiesCard); diff --git a/src/panels/lovelace/cards/hui-entities-card.ts b/src/panels/lovelace/cards/hui-entities-card.ts new file mode 100644 index 0000000000..e8f052c076 --- /dev/null +++ b/src/panels/lovelace/cards/hui-entities-card.ts @@ -0,0 +1,184 @@ +import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element"; + +import "../../../components/ha-card.js"; +import "../components/hui-entities-toggle.js"; + +import { fireEvent } from "../../../common/dom/fire_event.js"; +import { DOMAINS_HIDE_MORE_INFO } from "../../../common/const.js"; +import { hassLocalizeLitMixin } from "../../../mixins/lit-localize-mixin"; +import { LovelaceCard, LovelaceConfig } from "../types.js"; +import createRowElement from "../common/create-row-element.js"; +import computeDomain from "../../../common/entity/compute_domain.js"; +import processConfigEntities from "../common/process-config-entities"; +import { HomeAssistant } from "../../../types.js"; +import { EntityConfig, EntityRow } from "../entity-rows/types.js"; + +interface ConfigEntity extends EntityConfig { + type?: string; + secondary_info: "entity-id" | "last-changed"; + action_name?: string; + service?: string; + service_data?: object; + url?: string; +} + +interface Config extends LovelaceConfig { + show_header_toggle?: boolean; + title?: string; + entities: ConfigEntity[]; +} + +class HuiEntitiesCard extends hassLocalizeLitMixin(LitElement) + implements LovelaceCard { + protected _hass?: HomeAssistant; + protected config?: Config; + protected configEntities?: ConfigEntity[]; + + set hass(hass) { + this._hass = hass; + this.shadowRoot!.querySelectorAll("#states > *").forEach( + (element: unknown) => { + (element as EntityRow).hass = hass; + } + ); + } + + static get properties(): PropertyDeclarations { + return { + config: {}, + }; + } + + public getCardSize() { + if (!this.config) { + return 0; + } + // +1 for the header + return (this.config.title ? 1 : 0) + this.config.entities.length; + } + + public setConfig(config: Config) { + const entities = processConfigEntities(config.entities); + for (const entity of entities) { + if ( + entity.type === "call-service" && + (!entity.service || + !entity.name || + !entity.icon || + !entity.service_data || + !entity.action_name) + ) { + throw new Error("Missing required property when type is call-service"); + } else if ( + entity.type === "weblink" && + (!entity.name || !entity.icon || !entity.url) + ) { + throw new Error("Missing required property when type is weblink"); + } + } + + this.config = config; + this.configEntities = entities; + } + + protected render() { + 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() { + return html` + + `; + } + + private renderEntity(entityConf) { + const element = createRowElement(entityConf); + 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 element; + } + + private _handleClick(entityConf: ConfigEntity) { + 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/common/create-card-element.js b/src/panels/lovelace/common/create-card-element.js index 73ee2bf8af..c7d1a876ee 100644 --- a/src/panels/lovelace/common/create-card-element.js +++ b/src/panels/lovelace/common/create-card-element.js @@ -2,7 +2,7 @@ import { fireEvent } from "../../../common/dom/fire_event.js"; import "../cards/hui-alarm-panel-card.js"; import "../cards/hui-conditional-card.ts"; -import "../cards/hui-entities-card.js"; +import "../cards/hui-entities-card.ts"; import "../cards/hui-entity-button-card.ts"; import "../cards/hui-entity-filter-card.js"; import "../cards/hui-error-card.js"; diff --git a/src/panels/lovelace/entity-rows/types.ts b/src/panels/lovelace/entity-rows/types.ts new file mode 100644 index 0000000000..6d145f2698 --- /dev/null +++ b/src/panels/lovelace/entity-rows/types.ts @@ -0,0 +1,28 @@ +import { HomeAssistant } from "../../../types"; + +export interface EntityConfig { + entity: string; + name: string; + icon: string; +} +export interface DividerConfig { + style: string; +} +export interface SectionConfig { + label: string; +} +export interface WeblinkConfig { + name: string; + icon: string; + url: string; +} +export type EntityRowConfig = + | EntityConfig + | DividerConfig + | SectionConfig + | WeblinkConfig; + +export interface EntityRow { + hass: HomeAssistant; + setConfig(config: EntityRowConfig); +} diff --git a/src/panels/lovelace/hui-unused-entities.js b/src/panels/lovelace/hui-unused-entities.js index 196d464b48..9b9e318954 100644 --- a/src/panels/lovelace/hui-unused-entities.js +++ b/src/panels/lovelace/hui-unused-entities.js @@ -4,7 +4,7 @@ import { PolymerElement } from "@polymer/polymer/polymer-element.js"; import computeUnusedEntities from "./common/compute-unused-entities.js"; import createCardElement from "./common/create-card-element.js"; -import "./cards/hui-entities-card.js"; +import "./cards/hui-entities-card.ts"; class HuiUnusedEntities extends PolymerElement { static get template() { diff --git a/src/panels/lovelace/types.ts b/src/panels/lovelace/types.ts index 7583734616..def084c78b 100644 --- a/src/panels/lovelace/types.ts +++ b/src/panels/lovelace/types.ts @@ -1,4 +1,4 @@ -import { HomeAssistant } from "../../types.js"; +import { HomeAssistant } from "../../types"; export interface LovelaceConfig { type: string;