From dde27c3524bf3f77a5c71ff0de1b8dc632dcee18 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Wed, 24 May 2023 14:37:45 +0200 Subject: [PATCH] Support entity reg id in device automations (#15387) --- .../pages/automation/describe-condition.ts | 4 +- .../src/pages/automation/describe-trigger.ts | 4 +- .../device/ha-device-automation-picker.ts | 21 ++- src/data/automation_i18n.ts | 15 +- src/data/context.ts | 2 +- src/data/device_automation.ts | 175 ++++++++++++------ src/data/script_i18n.ts | 22 ++- .../types/ha-automation-action-device_id.ts | 17 +- .../condition/ha-automation-condition-row.ts | 11 +- .../types/ha-automation-condition-device.ts | 11 +- .../trigger/ha-automation-trigger-row.ts | 13 +- .../types/ha-automation-trigger-device.ts | 24 ++- .../ha-device-automation-card.ts | 14 +- src/panels/config/ha-panel-config.ts | 26 ++- src/panels/config/script/ha-config-script.ts | 24 +-- 15 files changed, 279 insertions(+), 104 deletions(-) diff --git a/gallery/src/pages/automation/describe-condition.ts b/gallery/src/pages/automation/describe-condition.ts index dfd959a633..fe089ba861 100644 --- a/gallery/src/pages/automation/describe-condition.ts +++ b/gallery/src/pages/automation/describe-condition.ts @@ -63,7 +63,7 @@ export class DemoAutomationDescribeCondition extends LitElement {
${this._condition - ? describeCondition(this._condition, this.hass) + ? describeCondition(this._condition, this.hass, []) : ""} html`
- ${describeCondition(conf as any, this.hass)} + ${describeCondition(conf as any, this.hass, [])}
${dump(conf)}
` diff --git a/gallery/src/pages/automation/describe-trigger.ts b/gallery/src/pages/automation/describe-trigger.ts index 49a57e652a..6ed8163912 100644 --- a/gallery/src/pages/automation/describe-trigger.ts +++ b/gallery/src/pages/automation/describe-trigger.ts @@ -74,7 +74,7 @@ export class DemoAutomationDescribeTrigger extends LitElement {
${this._trigger - ? describeTrigger(this._trigger, this.hass) + ? describeTrigger(this._trigger, this.hass, []) : ""} html`
- ${describeTrigger(conf as any, this.hass)} + ${describeTrigger(conf as any, this.hass, [])}
${dump(conf)}
` diff --git a/src/components/device/ha-device-automation-picker.ts b/src/components/device/ha-device-automation-picker.ts index a6a6632e1e..0189fc5d4c 100644 --- a/src/components/device/ha-device-automation-picker.ts +++ b/src/components/device/ha-device-automation-picker.ts @@ -1,12 +1,15 @@ +import { consume } from "@lit-labs/context"; import "@material/mwc-list/mwc-list-item"; import { css, CSSResultGroup, html, LitElement, nothing } from "lit"; import { property, state } from "lit/decorators"; import { fireEvent } from "../../common/dom/fire_event"; +import { fullEntitiesContext } from "../../data/context"; import { DeviceAutomation, deviceAutomationsEqual, sortDeviceAutomations, } from "../../data/device_automation"; +import { EntityRegistryEntry } from "../../data/entity_registry"; import { HomeAssistant } from "../../types"; import "../ha-select"; @@ -30,6 +33,10 @@ export abstract class HaDeviceAutomationPicker< // 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" @@ -44,6 +51,7 @@ export abstract class HaDeviceAutomationPicker< private _localizeDeviceAutomation: ( hass: HomeAssistant, + entityRegistry: EntityRegistryEntry[], automation: T ) => string; @@ -75,7 +83,7 @@ export abstract class HaDeviceAutomationPicker< } const idx = this._automations.findIndex((automation) => - deviceAutomationsEqual(automation, this.value!) + deviceAutomationsEqual(this._entityReg, automation, this.value!) ); if (idx === -1) { @@ -110,7 +118,11 @@ export abstract class HaDeviceAutomationPicker< ${this._automations.map( (automation, idx) => html` - ${this._localizeDeviceAutomation(this.hass, automation)} + ${this._localizeDeviceAutomation( + this.hass, + this._entityReg, + automation + )} ` )} @@ -161,7 +173,10 @@ export abstract class HaDeviceAutomationPicker< } private _setValue(automation: T) { - if (this.value && deviceAutomationsEqual(automation, this.value)) { + if ( + this.value && + deviceAutomationsEqual(this._entityReg, automation, this.value) + ) { return; } const value = { ...automation }; diff --git a/src/data/automation_i18n.ts b/src/data/automation_i18n.ts index 0cffd1f0e0..86103aea3a 100644 --- a/src/data/automation_i18n.ts +++ b/src/data/automation_i18n.ts @@ -15,6 +15,7 @@ import { computeAttributeValueDisplay, } from "../common/entity/compute_attribute_display"; import { computeStateDisplay } from "../common/entity/compute_state_display"; +import { EntityRegistryEntry } from "./entity_registry"; const describeDuration = (forTime: number | string | ForDict) => { let duration: string | null; @@ -48,6 +49,7 @@ const ordinalSuffix = (n: number) => { export const describeTrigger = ( trigger: Trigger, hass: HomeAssistant, + entityRegistry: EntityRegistryEntry[], ignoreAlias = false ) => { if (trigger.alias && !ignoreAlias) { @@ -561,7 +563,11 @@ export const describeTrigger = ( return "Device trigger"; } const config = trigger as DeviceTrigger; - const localized = localizeDeviceAutomationTrigger(hass, config); + const localized = localizeDeviceAutomationTrigger( + hass, + entityRegistry, + config + ); if (localized) { return localized; } @@ -579,6 +585,7 @@ export const describeTrigger = ( export const describeCondition = ( condition: Condition, hass: HomeAssistant, + entityRegistry: EntityRegistryEntry[], ignoreAlias = false ) => { if (condition.alias && !ignoreAlias) { @@ -873,7 +880,11 @@ export const describeCondition = ( return "Device condition"; } const config = condition as DeviceCondition; - const localized = localizeDeviceAutomationCondition(hass, config); + const localized = localizeDeviceAutomationCondition( + hass, + entityRegistry, + config + ); if (localized) { return localized; } diff --git a/src/data/context.ts b/src/data/context.ts index 2146180b30..f62e666ebe 100644 --- a/src/data/context.ts +++ b/src/data/context.ts @@ -20,5 +20,5 @@ export const userDataContext = createContext("userData"); export const panelsContext = createContext("panels"); -export const extendedEntitiesContext = +export const fullEntitiesContext = createContext("extendedEntities"); diff --git a/src/data/device_automation.ts b/src/data/device_automation.ts index 9458452675..5dc8e28792 100644 --- a/src/data/device_automation.ts +++ b/src/data/device_automation.ts @@ -2,6 +2,12 @@ import { computeStateName } from "../common/entity/compute_state_name"; import type { HaFormSchema } from "../components/ha-form/types"; import { HomeAssistant } from "../types"; import { BaseTrigger } from "./automation"; +import { + computeEntityRegistryName, + entityRegistryByEntityId, + entityRegistryById, + EntityRegistryEntry, +} from "./entity_registry"; export interface DeviceAutomation { alias?: string; @@ -89,6 +95,7 @@ const deviceAutomationIdentifiers = [ ]; export const deviceAutomationsEqual = ( + entityRegistry: EntityRegistryEntry[], a: DeviceAutomation, b: DeviceAutomation ) => { @@ -100,6 +107,22 @@ export const deviceAutomationsEqual = ( if (!deviceAutomationIdentifiers.includes(property)) { continue; } + if ( + property === "entity_id" && + a[property]?.includes(".") !== b[property]?.includes(".") + ) { + // both entity_id and entity_reg_id could be used, we should compare the entity_reg_id + if ( + !compareEntityIdWithEntityRegId( + entityRegistry, + a[property], + b[property] + ) + ) { + return false; + } + continue; + } if (!Object.is(a[property], b[property])) { return false; } @@ -108,6 +131,22 @@ export const deviceAutomationsEqual = ( if (!deviceAutomationIdentifiers.includes(property)) { continue; } + if ( + property === "entity_id" && + a[property]?.includes(".") !== b[property]?.includes(".") + ) { + // both entity_id and entity_reg_id could be used, we should compare the entity_reg_id + if ( + !compareEntityIdWithEntityRegId( + entityRegistry, + a[property], + b[property] + ) + ) { + return false; + } + continue; + } if (!Object.is(a[property], b[property])) { return false; } @@ -116,71 +155,99 @@ export const deviceAutomationsEqual = ( return true; }; +const compareEntityIdWithEntityRegId = ( + entityRegistry: EntityRegistryEntry[], + entityIdA?: string, + entityIdB?: string +) => { + if (!entityIdA || !entityIdB) { + return false; + } + if (entityIdA.includes(".")) { + entityIdA = entityRegistryByEntityId(entityRegistry)[entityIdA].id; + } + if (entityIdB.includes(".")) { + entityIdB = entityRegistryByEntityId(entityRegistry)[entityIdB].id; + } + return entityIdA === entityIdB; +}; + +const getEntityName = ( + hass: HomeAssistant, + entityRegistry: EntityRegistryEntry[], + entityId: string | undefined +): string => { + if (!entityId) { + return ""; + } + if (entityId.includes(".")) { + const state = hass.states[entityId]; + if (state) { + return computeStateName(state); + } + return entityId; + } + const entityReg = entityRegistryById(entityRegistry)[entityId]; + if (entityReg) { + return computeEntityRegistryName(hass, entityReg) || entityId; + } + return ""; +}; + export const localizeDeviceAutomationAction = ( hass: HomeAssistant, + entityRegistry: EntityRegistryEntry[], action: DeviceAction -): string => { - const state = action.entity_id ? hass.states[action.entity_id] : undefined; - return ( - hass.localize( - `component.${action.domain}.device_automation.action_type.${action.type}`, - "entity_name", - state ? computeStateName(state) : action.entity_id || "", - "subtype", - action.subtype - ? hass.localize( - `component.${action.domain}.device_automation.action_subtype.${action.subtype}` - ) || action.subtype - : "" - ) || (action.subtype ? `"${action.subtype}" ${action.type}` : action.type!) - ); -}; +): string => + hass.localize( + `component.${action.domain}.device_automation.action_type.${action.type}`, + "entity_name", + getEntityName(hass, entityRegistry, action.entity_id), + "subtype", + action.subtype + ? hass.localize( + `component.${action.domain}.device_automation.action_subtype.${action.subtype}` + ) || action.subtype + : "" + ) || (action.subtype ? `"${action.subtype}" ${action.type}` : action.type!); export const localizeDeviceAutomationCondition = ( hass: HomeAssistant, + entityRegistry: EntityRegistryEntry[], condition: DeviceCondition -): string => { - const state = condition.entity_id - ? hass.states[condition.entity_id] - : undefined; - return ( - hass.localize( - `component.${condition.domain}.device_automation.condition_type.${condition.type}`, - "entity_name", - state ? computeStateName(state) : condition.entity_id || "", - "subtype", - condition.subtype - ? hass.localize( - `component.${condition.domain}.device_automation.condition_subtype.${condition.subtype}` - ) || condition.subtype - : "" - ) || - (condition.subtype - ? `"${condition.subtype}" ${condition.type}` - : condition.type!) - ); -}; +): string => + hass.localize( + `component.${condition.domain}.device_automation.condition_type.${condition.type}`, + "entity_name", + getEntityName(hass, entityRegistry, condition.entity_id), + "subtype", + condition.subtype + ? hass.localize( + `component.${condition.domain}.device_automation.condition_subtype.${condition.subtype}` + ) || condition.subtype + : "" + ) || + (condition.subtype + ? `"${condition.subtype}" ${condition.type}` + : condition.type!); export const localizeDeviceAutomationTrigger = ( hass: HomeAssistant, + entityRegistry: EntityRegistryEntry[], trigger: DeviceTrigger -): string => { - const state = trigger.entity_id ? hass.states[trigger.entity_id] : undefined; - return ( - hass.localize( - `component.${trigger.domain}.device_automation.trigger_type.${trigger.type}`, - "entity_name", - state ? computeStateName(state) : trigger.entity_id || "", - "subtype", - trigger.subtype - ? hass.localize( - `component.${trigger.domain}.device_automation.trigger_subtype.${trigger.subtype}` - ) || trigger.subtype - : "" - ) || - (trigger.subtype ? `"${trigger.subtype}" ${trigger.type}` : trigger.type!) - ); -}; +): string => + hass.localize( + `component.${trigger.domain}.device_automation.trigger_type.${trigger.type}`, + "entity_name", + getEntityName(hass, entityRegistry, trigger.entity_id), + "subtype", + trigger.subtype + ? hass.localize( + `component.${trigger.domain}.device_automation.trigger_subtype.${trigger.subtype}` + ) || trigger.subtype + : "" + ) || + (trigger.subtype ? `"${trigger.subtype}" ${trigger.type}` : trigger.type!); export const sortDeviceAutomations = ( automationA: DeviceAutomation, diff --git a/src/data/script_i18n.ts b/src/data/script_i18n.ts index 8d3c1f88b2..90d807fb85 100644 --- a/src/data/script_i18n.ts +++ b/src/data/script_i18n.ts @@ -186,7 +186,7 @@ export const describeAction = ( return "Wait for a trigger"; } return `Wait for ${triggers - .map((trigger) => describeTrigger(trigger, hass)) + .map((trigger) => describeTrigger(trigger, hass, entityRegistry)) .join(", ")}`; } @@ -208,7 +208,7 @@ export const describeAction = ( } if (actionType === "check_condition") { - return describeCondition(action as Condition, hass); + return describeCondition(action as Condition, hass, entityRegistry); } if (actionType === "stop") { @@ -226,7 +226,7 @@ export const describeAction = ( : ensureArray(config.if).length > 1 ? `${ensureArray(config.if).length} conditions` : ensureArray(config.if).length - ? describeCondition(ensureArray(config.if)[0], hass) + ? describeCondition(ensureArray(config.if)[0], hass, entityRegistry) : "" }${config.else ? " (or else!)" : ""}`; } @@ -252,11 +252,11 @@ export const describeAction = ( base += ` ${count} time${Number(count) === 1 ? "" : "s"}`; } else if ("while" in config.repeat) { base += ` while ${ensureArray(config.repeat.while) - .map((condition) => describeCondition(condition, hass)) + .map((condition) => describeCondition(condition, hass, entityRegistry)) .join(", ")} is true`; } else if ("until" in config.repeat) { base += ` until ${ensureArray(config.repeat.until) - .map((condition) => describeCondition(condition, hass)) + .map((condition) => describeCondition(condition, hass, entityRegistry)) .join(", ")} is true`; } else if ("for_each" in config.repeat) { base += ` for every item: ${ensureArray(config.repeat.for_each) @@ -267,7 +267,11 @@ export const describeAction = ( } if (actionType === "check_condition") { - return `Test ${describeCondition(action as Condition, hass)}`; + return `Test ${describeCondition( + action as Condition, + hass, + entityRegistry + )}`; } if (actionType === "device_action") { @@ -275,7 +279,11 @@ export const describeAction = ( if (!config.device_id) { return "Device action"; } - const localized = localizeDeviceAutomationAction(hass, config); + const localized = localizeDeviceAutomationAction( + hass, + entityRegistry, + config + ); if (localized) { return localized; } diff --git a/src/panels/config/automation/action/types/ha-automation-action-device_id.ts b/src/panels/config/automation/action/types/ha-automation-action-device_id.ts index 37d641adba..f0e782d423 100644 --- a/src/panels/config/automation/action/types/ha-automation-action-device_id.ts +++ b/src/panels/config/automation/action/types/ha-automation-action-device_id.ts @@ -1,3 +1,4 @@ +import { consume } from "@lit-labs/context"; import { css, html, LitElement } from "lit"; import { customElement, property, state } from "lit/decorators"; import memoizeOne from "memoize-one"; @@ -5,12 +6,14 @@ import { fireEvent } from "../../../../../common/dom/fire_event"; import "../../../../../components/device/ha-device-action-picker"; import "../../../../../components/device/ha-device-picker"; import "../../../../../components/ha-form/ha-form"; +import { fullEntitiesContext } from "../../../../../data/context"; import { DeviceAction, deviceAutomationsEqual, DeviceCapabilities, fetchDeviceActionCapabilities, } from "../../../../../data/device_automation"; +import { EntityRegistryEntry } from "../../../../../data/entity_registry"; import { HomeAssistant } from "../../../../../types"; @customElement("ha-automation-action-device_id") @@ -25,6 +28,10 @@ export class HaDeviceAction extends LitElement { @state() private _capabilities?: DeviceCapabilities; + @state() + @consume({ context: fullEntitiesContext, subscribe: true }) + _entityReg!: EntityRegistryEntry[]; + private _origAction?: DeviceAction; public static get defaultConfig() { @@ -98,7 +105,10 @@ export class HaDeviceAction extends LitElement { protected updated(changedPros) { const prevAction = changedPros.get("action"); - if (prevAction && !deviceAutomationsEqual(prevAction, this.action)) { + if ( + prevAction && + !deviceAutomationsEqual(this._entityReg, prevAction, this.action) + ) { this._deviceId = undefined; this._getCapabilities(); } @@ -123,7 +133,10 @@ export class HaDeviceAction extends LitElement { private _deviceActionPicked(ev) { ev.stopPropagation(); let action = ev.detail.value; - if (this._origAction && deviceAutomationsEqual(this._origAction, action)) { + if ( + this._origAction && + deviceAutomationsEqual(this._entityReg, this._origAction, action) + ) { action = this._origAction; } fireEvent(this, "value-changed", { value: action }); diff --git a/src/panels/config/automation/condition/ha-automation-condition-row.ts b/src/panels/config/automation/condition/ha-automation-condition-row.ts index c716391085..3edaa0d48b 100644 --- a/src/panels/config/automation/condition/ha-automation-condition-row.ts +++ b/src/panels/config/automation/condition/ha-automation-condition-row.ts @@ -1,3 +1,4 @@ +import { consume } from "@lit-labs/context"; import { ActionDetail } from "@material/mwc-list/mwc-list-foundation"; import "@material/mwc-list/mwc-list-item"; import { @@ -28,6 +29,8 @@ import type { Clipboard } from "../../../../data/automation"; import { describeCondition } from "../../../../data/automation_i18n"; import { CONDITION_TYPES } from "../../../../data/condition"; import { validateConfig } from "../../../../data/config"; +import { fullEntitiesContext } from "../../../../data/context"; +import { EntityRegistryEntry } from "../../../../data/entity_registry"; import { showAlertDialog, showConfirmationDialog, @@ -90,6 +93,10 @@ export default class HaAutomationConditionRow extends LitElement { @state() private _testingResult?: boolean; + @state() + @consume({ context: fullEntitiesContext, subscribe: true }) + _entityReg!: EntityRegistryEntry[]; + protected render() { if (!this.condition) { return nothing; @@ -111,7 +118,7 @@ export default class HaAutomationConditionRow extends LitElement { .path=${CONDITION_TYPES[this.condition.condition]} > ${capitalizeFirstLetter( - describeCondition(this.condition, this.hass) + describeCondition(this.condition, this.hass, this._entityReg) )} @@ -458,7 +465,7 @@ export default class HaAutomationConditionRow extends LitElement { ), inputType: "string", placeholder: capitalizeFirstLetter( - describeCondition(this.condition, this.hass, true) + describeCondition(this.condition, this.hass, this._entityReg, true) ), defaultValue: this.condition.alias, confirmText: this.hass.localize("ui.common.submit"), diff --git a/src/panels/config/automation/condition/types/ha-automation-condition-device.ts b/src/panels/config/automation/condition/types/ha-automation-condition-device.ts index 6e7234ed26..71568fe02c 100644 --- a/src/panels/config/automation/condition/types/ha-automation-condition-device.ts +++ b/src/panels/config/automation/condition/types/ha-automation-condition-device.ts @@ -1,3 +1,4 @@ +import { consume } from "@lit-labs/context"; import { css, html, LitElement } from "lit"; import { customElement, property, state } from "lit/decorators"; import memoizeOne from "memoize-one"; @@ -5,12 +6,14 @@ import { fireEvent } from "../../../../../common/dom/fire_event"; import "../../../../../components/device/ha-device-condition-picker"; import "../../../../../components/device/ha-device-picker"; import "../../../../../components/ha-form/ha-form"; +import { fullEntitiesContext } from "../../../../../data/context"; import { deviceAutomationsEqual, DeviceCapabilities, DeviceCondition, fetchDeviceConditionCapabilities, } from "../../../../../data/device_automation"; +import { EntityRegistryEntry } from "../../../../../data/entity_registry"; import type { HomeAssistant } from "../../../../../types"; @customElement("ha-automation-condition-device") @@ -25,6 +28,10 @@ export class HaDeviceCondition extends LitElement { @state() private _capabilities?: DeviceCapabilities; + @state() + @consume({ context: fullEntitiesContext, subscribe: true }) + _entityReg!: EntityRegistryEntry[]; + private _origCondition?: DeviceCondition; public static get defaultConfig() { @@ -100,7 +107,7 @@ export class HaDeviceCondition extends LitElement { const prevCondition = changedPros.get("condition"); if ( prevCondition && - !deviceAutomationsEqual(prevCondition, this.condition) + !deviceAutomationsEqual(this._entityReg, prevCondition, this.condition) ) { this._getCapabilities(); } @@ -129,7 +136,7 @@ export class HaDeviceCondition extends LitElement { let condition = ev.detail.value; if ( this._origCondition && - deviceAutomationsEqual(this._origCondition, condition) + deviceAutomationsEqual(this._entityReg, this._origCondition, condition) ) { condition = this._origCondition; } diff --git a/src/panels/config/automation/trigger/ha-automation-trigger-row.ts b/src/panels/config/automation/trigger/ha-automation-trigger-row.ts index 0adbb662e6..bdff728f32 100644 --- a/src/panels/config/automation/trigger/ha-automation-trigger-row.ts +++ b/src/panels/config/automation/trigger/ha-automation-trigger-row.ts @@ -1,3 +1,4 @@ +import { consume } from "@lit-labs/context"; import { ActionDetail } from "@material/mwc-list/mwc-list-foundation"; import "@material/mwc-list/mwc-list-item"; import { @@ -32,6 +33,8 @@ import { HaYamlEditor } from "../../../../components/ha-yaml-editor"; import { subscribeTrigger, Trigger } from "../../../../data/automation"; import { describeTrigger } from "../../../../data/automation_i18n"; import { validateConfig } from "../../../../data/config"; +import { fullEntitiesContext } from "../../../../data/context"; +import { EntityRegistryEntry } from "../../../../data/entity_registry"; import { TRIGGER_TYPES } from "../../../../data/trigger"; import { showAlertDialog, @@ -106,6 +109,10 @@ export default class HaAutomationTriggerRow extends LitElement { @query("ha-yaml-editor") private _yamlEditor?: HaYamlEditor; + @state() + @consume({ context: fullEntitiesContext, subscribe: true }) + _entityReg!: EntityRegistryEntry[]; + private _triggerUnsub?: Promise; protected render() { @@ -133,7 +140,9 @@ export default class HaAutomationTriggerRow extends LitElement { class="trigger-icon" .path=${TRIGGER_TYPES[this.trigger.platform]} > - ${capitalizeFirstLetter(describeTrigger(this.trigger, this.hass))} + ${capitalizeFirstLetter( + describeTrigger(this.trigger, this.hass, this._entityReg) + )} @@ -565,7 +574,7 @@ export default class HaAutomationTriggerRow extends LitElement { ), inputType: "string", placeholder: capitalizeFirstLetter( - describeTrigger(this.trigger, this.hass, true) + describeTrigger(this.trigger, this.hass, this._entityReg, true) ), defaultValue: this.trigger.alias, confirmText: this.hass.localize("ui.common.submit"), diff --git a/src/panels/config/automation/trigger/types/ha-automation-trigger-device.ts b/src/panels/config/automation/trigger/types/ha-automation-trigger-device.ts index 05f422e976..8b3a635752 100644 --- a/src/panels/config/automation/trigger/types/ha-automation-trigger-device.ts +++ b/src/panels/config/automation/trigger/types/ha-automation-trigger-device.ts @@ -1,3 +1,4 @@ +import { consume } from "@lit-labs/context"; import { css, html, LitElement } from "lit"; import { customElement, property, state } from "lit/decorators"; import memoizeOne from "memoize-one"; @@ -5,12 +6,14 @@ import { fireEvent } from "../../../../../common/dom/fire_event"; import "../../../../../components/device/ha-device-picker"; import "../../../../../components/device/ha-device-trigger-picker"; import "../../../../../components/ha-form/ha-form"; +import { fullEntitiesContext } from "../../../../../data/context"; import { deviceAutomationsEqual, DeviceCapabilities, DeviceTrigger, fetchDeviceTriggerCapabilities, } from "../../../../../data/device_automation"; +import { EntityRegistryEntry } from "../../../../../data/entity_registry"; import { HomeAssistant } from "../../../../../types"; @customElement("ha-automation-trigger-device") @@ -25,6 +28,10 @@ export class HaDeviceTrigger extends LitElement { @state() private _capabilities?: DeviceCapabilities; + @state() + @consume({ context: fullEntitiesContext, subscribe: true }) + _entityReg!: EntityRegistryEntry[]; + private _origTrigger?: DeviceTrigger; public static get defaultConfig() { @@ -56,7 +63,7 @@ export class HaDeviceTrigger extends LitElement { @value-changed=${this._devicePicked} .hass=${this.hass} .disabled=${this.disabled} - label=${this.hass.localize( + .label=${this.hass.localize( "ui.panel.config.automation.editor.triggers.type.device.label" )} > @@ -66,7 +73,7 @@ export class HaDeviceTrigger extends LitElement { @value-changed=${this._deviceTriggerPicked} .hass=${this.hass} .disabled=${this.disabled} - label=${this.hass.localize( + .label=${this.hass.localize( "ui.panel.config.automation.editor.triggers.type.device.trigger" )} > @@ -96,12 +103,15 @@ export class HaDeviceTrigger extends LitElement { } } - protected updated(changedPros) { - if (!changedPros.has("trigger")) { + protected updated(changedProps) { + if (!changedProps.has("trigger")) { return; } - const prevTrigger = changedPros.get("trigger"); - if (prevTrigger && !deviceAutomationsEqual(prevTrigger, this.trigger)) { + const prevTrigger = changedProps.get("trigger"); + if ( + prevTrigger && + !deviceAutomationsEqual(this._entityReg, prevTrigger, this.trigger) + ) { this._getCapabilities(); } } @@ -129,7 +139,7 @@ export class HaDeviceTrigger extends LitElement { let trigger = ev.detail.value; if ( this._origTrigger && - deviceAutomationsEqual(this._origTrigger, trigger) + deviceAutomationsEqual(this._entityReg, this._origTrigger, trigger) ) { trigger = this._origTrigger; } diff --git a/src/panels/config/devices/device-detail/ha-device-automation-card.ts b/src/panels/config/devices/device-detail/ha-device-automation-card.ts index b1b1c6c697..bee7d36d66 100644 --- a/src/panels/config/devices/device-detail/ha-device-automation-card.ts +++ b/src/panels/config/devices/device-detail/ha-device-automation-card.ts @@ -1,13 +1,16 @@ +import { consume } from "@lit-labs/context"; import { css, html, LitElement, nothing } from "lit"; import { property, state } from "lit/decorators"; import { fireEvent } from "../../../../common/dom/fire_event"; import "../../../../components/ha-chip"; import "../../../../components/ha-chip-set"; import { showAutomationEditor } from "../../../../data/automation"; +import { fullEntitiesContext } from "../../../../data/context"; import { DeviceAction, DeviceAutomation, } from "../../../../data/device_automation"; +import { EntityRegistryEntry } from "../../../../data/entity_registry"; import { showScriptEditor } from "../../../../data/script"; import { buttonLinkStyle } from "../../../../resources/styles"; import { HomeAssistant } from "../../../../types"; @@ -31,12 +34,17 @@ export abstract class HaDeviceAutomationCard< @state() public _showSecondary = false; + @state() + @consume({ context: fullEntitiesContext, subscribe: true }) + _entityReg!: EntityRegistryEntry[]; + abstract headerKey: Parameters[0]; abstract type: "action" | "condition" | "trigger"; private _localizeDeviceAutomation: ( hass: HomeAssistant, + entityRegistry: EntityRegistryEntry[], automation: T ) => string; @@ -79,7 +87,11 @@ export abstract class HaDeviceAutomationCard< @click=${this._handleAutomationClicked} class=${automation.metadata?.secondary ? "secondary" : ""} > - ${this._localizeDeviceAutomation(this.hass, automation)} + ${this._localizeDeviceAutomation( + this.hass, + this._entityReg, + automation + )} ` )} diff --git a/src/panels/config/ha-panel-config.ts b/src/panels/config/ha-panel-config.ts index 3a3daa702d..7004f32a5f 100644 --- a/src/panels/config/ha-panel-config.ts +++ b/src/panels/config/ha-panel-config.ts @@ -1,3 +1,4 @@ +import { ContextProvider } from "@lit-labs/context"; import { mdiAccount, mdiBackupRestore, @@ -28,13 +29,21 @@ import { mdiViewDashboard, } from "@mdi/js"; import { PolymerElement } from "@polymer/polymer"; +import { UnsubscribeFunc } from "home-assistant-js-websocket"; import { PropertyValues } from "lit"; import { customElement, property, state } from "lit/decorators"; import { isComponentLoaded } from "../../common/config/is_component_loaded"; import { listenMediaQuery } from "../../common/dom/media_query"; import { CloudStatus, fetchCloudStatus } from "../../data/cloud"; +import { fullEntitiesContext } from "../../data/context"; +import { + entityRegistryByEntityId, + entityRegistryById, + subscribeEntityRegistry, +} from "../../data/entity_registry"; import { HassRouterPage, RouterOptions } from "../../layouts/hass-router-page"; import { PageNavigation } from "../../layouts/hass-tabs-subpage"; +import { SubscribeMixin } from "../../mixins/subscribe-mixin"; import { HomeAssistant, Route } from "../../types"; declare global { @@ -349,13 +358,26 @@ export const configSections: { [name: string]: PageNavigation[] } = { }; @customElement("ha-panel-config") -class HaPanelConfig extends HassRouterPage { +class HaPanelConfig extends SubscribeMixin(HassRouterPage) { @property({ attribute: false }) public hass!: HomeAssistant; @property() public narrow!: boolean; @property() public route!: Route; + private _entitiesContext = new ContextProvider(this, { + context: fullEntitiesContext, + initialValue: [], + }); + + public hassSubscribe(): UnsubscribeFunc[] { + return [ + subscribeEntityRegistry(this.hass.connection!, (entities) => { + this._entitiesContext.setValue(entities); + }), + ]; + } + protected routerOptions: RouterOptions = { defaultPage: "dashboard", routes: { @@ -545,6 +567,8 @@ class HaPanelConfig extends HassRouterPage { while (this._listeners.length) { this._listeners.pop()!(); } + entityRegistryByEntityId.clear(); + entityRegistryById.clear(); } protected firstUpdated(changedProps: PropertyValues) { diff --git a/src/panels/config/script/ha-config-script.ts b/src/panels/config/script/ha-config-script.ts index 5584075dea..45ff779f91 100644 --- a/src/panels/config/script/ha-config-script.ts +++ b/src/panels/config/script/ha-config-script.ts @@ -1,19 +1,17 @@ -import { HassEntities, UnsubscribeFunc } from "home-assistant-js-websocket"; +import { consume } from "@lit-labs/context"; +import { HassEntities } from "home-assistant-js-websocket"; import { PropertyValues } from "lit"; import { customElement, property, state } from "lit/decorators"; import memoizeOne from "memoize-one"; import { computeStateDomain } from "../../../common/entity/compute_state_domain"; import { debounce } from "../../../common/util/debounce"; -import { - EntityRegistryEntry, - subscribeEntityRegistry, -} from "../../../data/entity_registry"; +import { fullEntitiesContext } from "../../../data/context"; +import { EntityRegistryEntry } from "../../../data/entity_registry"; import { ScriptEntity } from "../../../data/script"; import { HassRouterPage, RouterOptions, } from "../../../layouts/hass-router-page"; -import { SubscribeMixin } from "../../../mixins/subscribe-mixin"; import { HomeAssistant } from "../../../types"; import "./ha-script-editor"; import "./ha-script-picker"; @@ -26,7 +24,7 @@ const equal = (a: ScriptEntity[], b: ScriptEntity[]): boolean => { }; @customElement("ha-config-script") -class HaConfigScript extends SubscribeMixin(HassRouterPage) { +class HaConfigScript extends HassRouterPage { @property({ attribute: false }) public hass!: HomeAssistant; @property() public narrow!: boolean; @@ -37,15 +35,9 @@ class HaConfigScript extends SubscribeMixin(HassRouterPage) { @property() public scripts: ScriptEntity[] = []; - @state() private _entityReg: EntityRegistryEntry[] = []; - - public hassSubscribe(): UnsubscribeFunc[] { - return [ - subscribeEntityRegistry(this.hass.connection!, (entities) => { - this._entityReg = entities; - }), - ]; - } + @state() + @consume({ context: fullEntitiesContext, subscribe: true }) + _entityReg!: EntityRegistryEntry[]; protected routerOptions: RouterOptions = { defaultPage: "dashboard",