import { mdiCheck } from "@mdi/js"; import "@polymer/paper-input/paper-input"; import "@polymer/paper-item/paper-icon-item"; import "@polymer/paper-item/paper-item-body"; import "@vaadin/vaadin-combo-box/theme/material/vaadin-combo-box-light"; import { HassEntity } from "home-assistant-js-websocket"; import { css, CSSResultGroup, html, LitElement, PropertyValues, TemplateResult, } from "lit"; import { ComboBoxLitRenderer } from "lit-vaadin-helpers"; import { customElement, property, query, state } from "lit/decorators"; import memoizeOne from "memoize-one"; import { fireEvent } from "../../common/dom/fire_event"; import { computeStateName } from "../../common/entity/compute_state_name"; import { stringCompare } from "../../common/string/compare"; import { getStatisticIds, StatisticsMetaData } from "../../data/history"; import { PolymerChangedEvent } from "../../polymer-types"; import { HomeAssistant } from "../../types"; import { documentationUrl } from "../../util/documentation-url"; import "../ha-combo-box"; import type { HaComboBox } from "../ha-combo-box"; import "../ha-svg-icon"; import "./state-badge"; @customElement("ha-statistic-picker") export class HaStatisticPicker extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; @property() public label?: string; @property() public value?: string; @property({ attribute: "statistic-types" }) public statisticTypes?: "mean" | "sum"; @property({ type: Array }) public statisticIds?: StatisticsMetaData[]; @property({ type: Boolean }) public disabled?: boolean; /** * Show only statistics with these unit of measuments. * @type {Array} * @attr include-unit-of-measurement */ @property({ type: Array, attribute: "include-unit-of-measurement" }) public includeUnitOfMeasurement?: string[]; /** * Show only statistics with these device classes. * @type {Array} * @attr include-device-classes */ @property({ type: Array, attribute: "include-device-classes" }) public includeDeviceClasses?: string[]; /** * Show only statistics on entities. * @type {Boolean} * @attr entities-only */ @property({ type: Boolean, attribute: "entities-only" }) public entitiesOnly = false; @state() private _opened?: boolean; @query("ha-combo-box", true) public comboBox!: HaComboBox; private _init = false; private _rowRenderer: ComboBoxLitRenderer<{ id: string; name: string; state?: HassEntity; // eslint-disable-next-line lit/prefer-static-styles }> = (item) => html` ${item.name} ${item.id === "" || item.id === "__missing" ? html`${this.hass.localize( "ui.components.statistic-picker.learn_more" )}` : item.id} `; private _getStatistics = memoizeOne( ( statisticIds: StatisticsMetaData[], includeUnitOfMeasurement?: string[], includeDeviceClasses?: string[], entitiesOnly?: boolean ): Array<{ id: string; name: string; state?: HassEntity }> => { if (!statisticIds.length) { return [ { id: "", name: this.hass.localize( "ui.components.statistic-picker.no_statistics" ), }, ]; } if (includeUnitOfMeasurement) { statisticIds = statisticIds.filter((meta) => includeUnitOfMeasurement.includes(meta.unit_of_measurement) ); } const output: Array<{ id: string; name: string; state?: HassEntity; }> = []; statisticIds.forEach((meta) => { const entityState = this.hass.states[meta.statistic_id]; if (!entityState) { if (!entitiesOnly) { output.push({ id: meta.statistic_id, name: meta.statistic_id }); } return; } if ( !includeDeviceClasses || includeDeviceClasses.includes( entityState!.attributes.device_class || "" ) ) { output.push({ id: meta.statistic_id, name: computeStateName(entityState), state: entityState, }); } }); if (!output.length) { return [ { id: "", name: this.hass.localize("ui.components.statistic-picker.no_match"), }, ]; } if (output.length > 1) { output.sort((a, b) => stringCompare(a.name || "", b.name || "")); } output.push({ id: "__missing", name: this.hass.localize( "ui.components.statistic-picker.missing_entity" ), }); return output; } ); public open() { this.comboBox?.open(); } public focus() { this.comboBox?.focus(); } public willUpdate(changedProps: PropertyValues) { if ( (!this.hasUpdated && !this.statisticIds) || changedProps.has("statisticTypes") ) { this._getStatisticIds(); } if ( (!this._init && this.statisticIds) || (changedProps.has("_opened") && this._opened) ) { this._init = true; if (this.hasUpdated) { (this.comboBox as any).items = this._getStatistics( this.statisticIds!, this.includeUnitOfMeasurement, this.includeDeviceClasses, this.entitiesOnly ); } else { this.updateComplete.then(() => { (this.comboBox as any).items = this._getStatistics( this.statisticIds!, this.includeUnitOfMeasurement, this.includeDeviceClasses, this.entitiesOnly ); }); } } } protected render(): TemplateResult { return html` `; } private async _getStatisticIds() { this.statisticIds = await getStatisticIds(this.hass, this.statisticTypes); } private get _value() { return this.value || ""; } private _statisticChanged(ev: PolymerChangedEvent) { ev.stopPropagation(); let newValue = ev.detail.value; if (newValue === "__missing") { newValue = ""; } if (newValue !== this._value) { this._setValue(newValue); } } private _openedChanged(ev: PolymerChangedEvent) { this._opened = ev.detail.value; } private _setValue(value: string) { this.value = value; setTimeout(() => { fireEvent(this, "value-changed", { value }); fireEvent(this, "change"); }, 0); } static get styles(): CSSResultGroup { return css` paper-input > ha-icon-button { --mdc-icon-button-size: 24px; padding: 2px; color: var(--secondary-text-color); } [hidden] { display: none; } `; } } declare global { interface HTMLElementTagNameMap { "ha-statistic-picker": HaStatisticPicker; } }