import { consume } from "@lit/context"; import { css, html, LitElement, nothing } from "lit"; import { property, state } from "lit/decorators"; import { fireEvent } from "../../common/dom/fire_event"; import { fullEntitiesContext } from "../../data/context"; import type { DeviceAutomation } from "../../data/device_automation"; import { deviceAutomationsEqual, sortDeviceAutomations, } from "../../data/device_automation"; import type { EntityRegistryEntry } from "../../data/entity_registry"; import type { HomeAssistant } from "../../types"; import "../ha-list-item"; import "../ha-select"; import { stopPropagation } from "../../common/dom/stop_propagation"; const NO_AUTOMATION_KEY = "NO_AUTOMATION"; const UNKNOWN_AUTOMATION_KEY = "UNKNOWN_AUTOMATION"; export abstract class HaDeviceAutomationPicker< T extends DeviceAutomation, > extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; @property() public label?: string; @property({ attribute: false }) public deviceId?: string; @property({ type: Object }) public value?: T; @state() private _automations: T[] = []; // Trigger an empty render so we start with a clean DOM. // paper-listbox does not like changing things around. @state() private _renderEmpty = false; @state() @consume({ context: fullEntitiesContext, subscribe: true }) _entityReg!: EntityRegistryEntry[]; protected get NO_AUTOMATION_TEXT() { return this.hass.localize( "ui.panel.config.devices.automation.actions.no_actions" ); } protected get UNKNOWN_AUTOMATION_TEXT() { return this.hass.localize( "ui.panel.config.devices.automation.actions.unknown_action" ); } private _localizeDeviceAutomation: ( hass: HomeAssistant, entityRegistry: EntityRegistryEntry[], automation: T ) => string; private _fetchDeviceAutomations: ( hass: HomeAssistant, deviceId: string ) => Promise; private _createNoAutomation: (deviceId?: string) => T; constructor( localizeDeviceAutomation: HaDeviceAutomationPicker["_localizeDeviceAutomation"], fetchDeviceAutomations: HaDeviceAutomationPicker["_fetchDeviceAutomations"], createNoAutomation: HaDeviceAutomationPicker["_createNoAutomation"] ) { super(); this._localizeDeviceAutomation = localizeDeviceAutomation; this._fetchDeviceAutomations = fetchDeviceAutomations; this._createNoAutomation = createNoAutomation; } private get _value() { if (!this.value) { return ""; } if (!this._automations.length) { return NO_AUTOMATION_KEY; } const idx = this._automations.findIndex((automation) => deviceAutomationsEqual(this._entityReg, automation, this.value!) ); if (idx === -1) { return UNKNOWN_AUTOMATION_KEY; } return `${this._automations[idx].device_id}_${idx}`; } protected render() { if (this._renderEmpty) { return nothing; } const value = this._value; return html` ${value === NO_AUTOMATION_KEY ? html` ${this.NO_AUTOMATION_TEXT} ` : ""} ${value === UNKNOWN_AUTOMATION_KEY ? html` ${this.UNKNOWN_AUTOMATION_TEXT} ` : ""} ${this._automations.map( (automation, idx) => html` ${this._localizeDeviceAutomation( this.hass, this._entityReg, automation )} ` )} `; } protected updated(changedProps) { super.updated(changedProps); if (changedProps.has("deviceId")) { this._updateDeviceInfo(); } } private async _updateDeviceInfo() { this._automations = this.deviceId ? (await this._fetchDeviceAutomations(this.hass, this.deviceId)).sort( sortDeviceAutomations ) : // No device, clear the list of automations []; // If there is no value, or if we have changed the device ID, reset the value. if (!this.value || this.value.device_id !== this.deviceId) { this._setValue( this._automations.length ? this._automations[0] : this._createNoAutomation(this.deviceId) ); } this._renderEmpty = true; await this.updateComplete; this._renderEmpty = false; } private _automationChanged(ev) { const value = ev.target.value; if (!value || [UNKNOWN_AUTOMATION_KEY, NO_AUTOMATION_KEY].includes(value)) { return; } const [deviceId, idx] = value.split("_"); const automation = this._automations[idx]; if (automation.device_id !== deviceId) { return; } this._setValue(automation); } private _setValue(automation: T) { if ( this.value && deviceAutomationsEqual(this._entityReg, automation, this.value) ) { return; } const value = { ...automation }; delete value.metadata; fireEvent(this, "value-changed", { value }); } static styles = css` ha-select { display: block; } `; }