From d041bd9fd3d04f81aeded34868aa5e04b6d241fc Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Wed, 31 Aug 2022 17:30:44 +0200 Subject: [PATCH] use device and area names in service call description (#13532) --- demo/src/ha-demo.ts | 2 + gallery/src/pages/misc/integration-card.ts | 1 + src/data/automation_i18n.ts | 31 ++++++++ src/data/entity_registry.ts | 13 +++- src/data/script_i18n.ts | 72 +++++++++++++++---- .../config/entities/ha-config-entities.ts | 10 ++- 6 files changed, 111 insertions(+), 18 deletions(-) diff --git a/demo/src/ha-demo.ts b/demo/src/ha-demo.ts index 625ddc6187..4cbc5ef266 100644 --- a/demo/src/ha-demo.ts +++ b/demo/src/ha-demo.ts @@ -61,6 +61,7 @@ class HaDemo extends HomeAssistantAppEl { area_id: null, disabled_by: null, entity_id: "sensor.co2_intensity", + unique_id: "sensor.co2_intensity", name: null, icon: null, platform: "co2signal", @@ -74,6 +75,7 @@ class HaDemo extends HomeAssistantAppEl { area_id: null, disabled_by: null, entity_id: "sensor.grid_fossil_fuel_percentage", + unique_id: "sensor.co2_intensity", name: null, icon: null, platform: "co2signal", diff --git a/gallery/src/pages/misc/integration-card.ts b/gallery/src/pages/misc/integration-card.ts index cf12cefd35..88f436e2ae 100644 --- a/gallery/src/pages/misc/integration-card.ts +++ b/gallery/src/pages/misc/integration-card.ts @@ -191,6 +191,7 @@ const createEntityRegistryEntries = ( hidden_by: null, entity_category: null, entity_id: "binary_sensor.updater", + unique_id: "binary_sensor.updater", name: null, icon: null, platform: "updater", diff --git a/src/data/automation_i18n.ts b/src/data/automation_i18n.ts index 33bf8fa9d1..d3b5fba7cd 100644 --- a/src/data/automation_i18n.ts +++ b/src/data/automation_i18n.ts @@ -2,6 +2,12 @@ import secondsToDuration from "../common/datetime/seconds_to_duration"; import { computeStateName } from "../common/entity/compute_state_name"; import type { HomeAssistant } from "../types"; import { Condition, Trigger } from "./automation"; +import { + DeviceCondition, + DeviceTrigger, + localizeDeviceAutomationCondition, + localizeDeviceAutomationTrigger, +} from "./device_automation"; import { formatAttributeName } from "./entity_attributes"; export const describeTrigger = ( @@ -292,6 +298,19 @@ export const describeTrigger = ( if (trigger.platform === "webhook") { return "When a Webhook payload has been received"; } + + if (trigger.platform === "device") { + const config = trigger as DeviceTrigger; + const localized = localizeDeviceAutomationTrigger(hass, config); + if (localized) { + return localized; + } + const stateObj = hass.states[config.entity_id as string]; + return `${stateObj ? computeStateName(stateObj) : config.entity_id} ${ + config.type + }`; + } + return `${trigger.platform || "Unknown"} trigger`; }; @@ -467,5 +486,17 @@ export const describeCondition = ( }`; } + if (condition.condition === "device") { + const config = condition as DeviceCondition; + const localized = localizeDeviceAutomationCondition(hass, config); + if (localized) { + return localized; + } + const stateObj = hass.states[config.entity_id as string]; + return `${stateObj ? computeStateName(stateObj) : config.entity_id} ${ + config.type + }`; + } + return `${condition.condition} condition`; }; diff --git a/src/data/entity_registry.ts b/src/data/entity_registry.ts index 99ced8904e..e02e801115 100644 --- a/src/data/entity_registry.ts +++ b/src/data/entity_registry.ts @@ -1,5 +1,6 @@ import { Connection, createCollection } from "home-assistant-js-websocket"; import { Store } from "home-assistant-js-websocket/dist/store"; +import memoizeOne from "memoize-one"; import { computeStateName } from "../common/entity/compute_state_name"; import { caseInsensitiveStringCompare } from "../common/string/compare"; import { debounce } from "../common/util/debounce"; @@ -7,6 +8,7 @@ import { HomeAssistant } from "../types"; export interface EntityRegistryEntry { entity_id: string; + unique_id: string; name: string | null; icon: string | null; platform: string; @@ -21,7 +23,6 @@ export interface EntityRegistryEntry { } export interface ExtEntityRegistryEntry extends EntityRegistryEntry { - unique_id: string; capabilities: Record; original_icon?: string; device_class?: string; @@ -161,6 +162,16 @@ export const sortEntityRegistryByName = (entries: EntityRegistryEntry[]) => caseInsensitiveStringCompare(entry1.name || "", entry2.name || "") ); +export const entityRegistryByUniqueId = memoizeOne( + (entries: HomeAssistant["entities"]) => { + const entities: HomeAssistant["entities"] = {}; + for (const entity of Object.values(entries)) { + entities[entity.unique_id] = entity; + } + return entities; + } +); + export const getEntityPlatformLookup = ( entities: EntityRegistryEntry[] ): Record => { diff --git a/src/data/script_i18n.ts b/src/data/script_i18n.ts index c3b9c70246..cfafa5abe8 100644 --- a/src/data/script_i18n.ts +++ b/src/data/script_i18n.ts @@ -5,6 +5,13 @@ import { isTemplate } from "../common/string/has-template"; import { HomeAssistant } from "../types"; import { Condition } from "./automation"; import { describeCondition, describeTrigger } from "./automation_i18n"; +import { localizeDeviceAutomationAction } from "./device_automation"; +import { computeDeviceName } from "./device_registry"; +import { + computeEntityRegistryName, + entityRegistryByUniqueId, +} from "./entity_registry"; +import { domainToName } from "./integration"; import { ActionType, ActionTypes, @@ -47,7 +54,11 @@ export const describeAction = ( ) { base = "Call a service based on a template"; } else if (config.service) { - base = `Call service ${config.service}`; + const [domain, serviceName] = config.service.split(".", 2); + const service = hass.services[domain][serviceName]; + base = service + ? `${domainToName(hass.localize, domain)}: ${service.name}` + : `Call service: ${config.service}`; } else { return actionType; } @@ -66,26 +77,51 @@ export const describeAction = ( ? config.target[key] : [config.target[key]]; - const values: string[] = []; - - let renderValues = true; - for (const targetThing of keyConf) { if (isTemplate(targetThing)) { targets.push(`templated ${label}`); - renderValues = false; break; + } else if (key === "entity_id") { + if (targetThing.includes(".")) { + const state = hass.states[targetThing]; + if (state) { + targets.push(computeStateName(state)); + } else { + targets.push(targetThing); + } + } else { + const entityReg = entityRegistryByUniqueId(hass.entities)[ + targetThing + ]; + if (entityReg) { + targets.push( + computeEntityRegistryName(hass, entityReg) || targetThing + ); + } else { + targets.push(targetThing); + } + } + } else if (key === "device_id") { + const device = hass.devices[targetThing]; + if (device) { + targets.push(computeDeviceName(device, hass)); + } else { + targets.push(targetThing); + } + } else if (key === "area_id") { + const area = hass.areas[targetThing]; + if (area?.name) { + targets.push(area.name); + } else { + targets.push(targetThing); + } } else { - values.push(targetThing); + targets.push(targetThing); } } - - if (renderValues) { - targets.push(`${label} ${values.join(", ")}`); - } } if (targets.length > 0) { - base += ` on ${targets.join(", ")}`; + base += ` ${targets.join(", ")}`; } } @@ -175,11 +211,15 @@ export const describeAction = ( if (actionType === "if") { const config = action as IfAction; return `Perform an action if: ${ - typeof config.if === "string" + !config.if + ? "" + : typeof config.if === "string" ? config.if : ensureArray(config.if).length > 1 ? `${ensureArray(config.if).length} conditions` - : describeCondition(ensureArray(config.if)[0], hass) + : ensureArray(config.if).length + ? describeCondition(ensureArray(config.if)[0], hass) + : "" }${config.else ? " (or else!)" : ""}`; } @@ -219,6 +259,10 @@ export const describeAction = ( if (actionType === "device_action") { const config = action as DeviceAction; + const localized = localizeDeviceAutomationAction(hass, config); + if (localized) { + return localized; + } const stateObj = hass.states[config.entity_id as string]; return `${config.type || "Perform action with"} ${ stateObj ? computeStateName(stateObj) : config.entity_id diff --git a/src/panels/config/entities/ha-config-entities.ts b/src/panels/config/entities/ha-config-entities.ts index 5a610d8169..beb10f83e8 100644 --- a/src/panels/config/entities/ha-config-entities.ts +++ b/src/panels/config/entities/ha-config-entities.ts @@ -68,9 +68,10 @@ import type { HomeAssistant, Route } from "../../../types"; import { configSections } from "../ha-panel-config"; import "../integrations/ha-integration-overflow-menu"; -export interface StateEntity extends EntityRegistryEntry { +export interface StateEntity extends Omit { readonly?: boolean; selectable?: boolean; + unique_id?: string; } export interface EntityRow extends StateEntity { @@ -302,7 +303,7 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) { private _filteredEntitiesAndDomains = memoize( ( - entities: EntityRegistryEntry[], + entities: StateEntity[], devices: DeviceRegistryEntry[] | undefined, areas: AreaRegistryEntry[] | undefined, stateEntities: StateEntity[], @@ -392,7 +393,10 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) { result.push({ ...entry, entity, - name: computeEntityRegistryName(this.hass!, entry), + name: computeEntityRegistryName( + this.hass!, + entry as EntityRegistryEntry + ), unavailable, restored, area: area ? area.name : "—",