From 4db06a137f98d61246840cd452c45e270391c137 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Wed, 11 Sep 2024 15:55:09 +0200 Subject: [PATCH 1/7] Separate entity, device and area name in more info dialog header --- src/common/entity/compute_area_name.ts | 4 ++ src/common/entity/compute_device_name.ts | 5 ++ src/common/entity/compute_entity_name.ts | 53 ++++++++++++++++ src/dialogs/more-info/ha-more-info-dialog.ts | 67 ++++++++++++++++++-- 4 files changed, 125 insertions(+), 4 deletions(-) create mode 100644 src/common/entity/compute_area_name.ts create mode 100644 src/common/entity/compute_device_name.ts create mode 100644 src/common/entity/compute_entity_name.ts diff --git a/src/common/entity/compute_area_name.ts b/src/common/entity/compute_area_name.ts new file mode 100644 index 000000000000..eb5bf81b92b0 --- /dev/null +++ b/src/common/entity/compute_area_name.ts @@ -0,0 +1,4 @@ +import { AreaRegistryEntry } from "../../data/area_registry"; + +export const computeAreaName = (area: AreaRegistryEntry): string | undefined => + area.name?.trim(); diff --git a/src/common/entity/compute_device_name.ts b/src/common/entity/compute_device_name.ts new file mode 100644 index 000000000000..b184fe325474 --- /dev/null +++ b/src/common/entity/compute_device_name.ts @@ -0,0 +1,5 @@ +import { DeviceRegistryEntry } from "../../data/device_registry"; + +export const computeDeviceName = ( + device: DeviceRegistryEntry +): string | undefined => (device.name_by_user || device.name)?.trim(); diff --git a/src/common/entity/compute_entity_name.ts b/src/common/entity/compute_entity_name.ts new file mode 100644 index 000000000000..968c95d922ae --- /dev/null +++ b/src/common/entity/compute_entity_name.ts @@ -0,0 +1,53 @@ +import { HassEntity } from "home-assistant-js-websocket"; +import { EntityRegistryDisplayEntry } from "../../data/entity_registry"; +import { HomeAssistant } from "../../types"; +import { stripPrefixFromEntityName } from "./strip_prefix_from_entity_name"; +import { computeStateName } from "./compute_state_name"; +import { computeDeviceName } from "./compute_device_name"; +import { computeAreaName } from "./compute_area_name"; + +export const computeEntityName = ( + stateObj: HassEntity, + entities: HomeAssistant["entities"], + devices: HomeAssistant["devices"] +): string | undefined => { + const entry = entities[stateObj.entity_id] as EntityRegistryDisplayEntry; + const device = entry?.device_id ? devices[entry.device_id] : undefined; + + const name = computeStateName(stateObj).trim(); + const deviceName = device ? computeDeviceName(device) : undefined; + if (!deviceName) { + return name; + } + return stripPrefixFromEntityName(name, deviceName.toLowerCase()) || name; +}; + +export const computeEntityDeviceName = ( + stateObj: HassEntity, + entities: HomeAssistant["entities"], + devices: HomeAssistant["devices"] +): string | undefined => { + const entry = entities[stateObj.entity_id] as + | EntityRegistryDisplayEntry + | undefined; + const device = entry?.device_id ? devices[entry.device_id] : undefined; + + return device ? computeDeviceName(device) : ""; +}; + +export const computeEntityAreaName = ( + stateObj: HassEntity, + entities: HomeAssistant["entities"], + devices: HomeAssistant["devices"], + areas: HomeAssistant["areas"] +): string | undefined => { + const entry = entities[stateObj.entity_id] as + | EntityRegistryDisplayEntry + | undefined; + const device = entry?.device_id ? devices[entry?.device_id] : undefined; + + const areaId = entry?.area_id || device?.area_id; + const area = areaId ? areas[areaId] : undefined; + + return area ? computeAreaName(area) : ""; +}; diff --git a/src/dialogs/more-info/ha-more-info-dialog.ts b/src/dialogs/more-info/ha-more-info-dialog.ts index fa9d5743f799..49670004f416 100644 --- a/src/dialogs/more-info/ha-more-info-dialog.ts +++ b/src/dialogs/more-info/ha-more-info-dialog.ts @@ -14,11 +14,16 @@ import type { PropertyValues } from "lit"; import { LitElement, css, html, nothing } from "lit"; import { customElement, property, query, state } from "lit/decorators"; import { cache } from "lit/directives/cache"; +import { classMap } from "lit/directives/class-map"; import { dynamicElement } from "../../common/dom/dynamic-element-directive"; import { fireEvent } from "../../common/dom/fire_event"; import { stopPropagation } from "../../common/dom/stop_propagation"; import { computeDomain } from "../../common/entity/compute_domain"; -import { computeStateName } from "../../common/entity/compute_state_name"; +import { + computeEntityAreaName, + computeEntityDeviceName, + computeEntityName, +} from "../../common/entity/compute_entity_name"; import { shouldHandleRequestSelectedEvent } from "../../common/mwc/handle-request-selected-event"; import { navigate } from "../../common/navigate"; import "../../components/ha-button-menu"; @@ -266,7 +271,10 @@ export class MoreInfoDialog extends LitElement { const stateObj = this.hass.states[entityId] as HassEntity | undefined; const domain = computeDomain(entityId); - const name = (stateObj && computeStateName(stateObj)) || entityId; + const name = + (stateObj && + computeEntityName(stateObj, this.hass.entities, this.hass.devices)) || + entityId; const isAdmin = this.hass.user!.is_admin; @@ -279,6 +287,24 @@ export class MoreInfoDialog extends LitElement { this._initialView !== DEFAULT_VIEW && !this._childView; const showCloseIcon = isDefaultView || isSpecificInitialView; + const areaName = stateObj + ? computeEntityAreaName( + stateObj, + this.hass.entities, + this.hass.devices, + this.hass.areas + ) + : ""; + const deviceName = stateObj + ? computeEntityDeviceName(stateObj, this.hass.entities, this.hass.devices) + : ""; + + const subtitle = this._childView?.viewTitle + ? undefined + : [name !== deviceName ? deviceName : undefined, areaName] + .filter(Boolean) + .join(" ⸱ "); + return html` `} - - ${title} + +

${title}

+ ${subtitle ? html`

${subtitle}

` : nothing}
${isDefaultView ? html` @@ -593,6 +625,33 @@ export class MoreInfoDialog extends LitElement { --mdc-dialog-max-width: 90vw; } } + + .title { + display: flex; + flex-direction: column; + } + + .title p { + margin: 0; + min-width: 0; + width: 100%; + text-overflow: ellipsis; + overflow: hidden; + } + + .title .primary { + color: var(--primary-text-color); + } + + .title .secondary { + color: var(--secondary-text-color); + font-size: 14px; + } + + .title.two-line .primary { + margin-top: -4px; + margin-bottom: -6px; + } `, ]; } From 25bd16810e8bc524e8f6847be7ad4d0f2c38d635 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Wed, 11 Sep 2024 18:42:39 +0200 Subject: [PATCH 2/7] Use has_entity_name --- src/common/entity/compute_entity_name.ts | 14 ++++++++++++-- src/data/entity_registry.ts | 1 + src/dialogs/more-info/ha-more-info-dialog.ts | 16 +++++++++------- 3 files changed, 22 insertions(+), 9 deletions(-) diff --git a/src/common/entity/compute_entity_name.ts b/src/common/entity/compute_entity_name.ts index 968c95d922ae..f6eb3bf0e1ff 100644 --- a/src/common/entity/compute_entity_name.ts +++ b/src/common/entity/compute_entity_name.ts @@ -12,14 +12,24 @@ export const computeEntityName = ( devices: HomeAssistant["devices"] ): string | undefined => { const entry = entities[stateObj.entity_id] as EntityRegistryDisplayEntry; - const device = entry?.device_id ? devices[entry.device_id] : undefined; + // Use entity name from entity registry + if (entry?.has_entity_name) { + return entry.name; + } + + // Then fallback to state name + const device = entry?.device_id ? devices[entry.device_id] : undefined; const name = computeStateName(stateObj).trim(); const deviceName = device ? computeDeviceName(device) : undefined; if (!deviceName) { return name; } - return stripPrefixFromEntityName(name, deviceName.toLowerCase()) || name; + // if the device name equals the entity name, consider empty entity name + if (deviceName === name) { + return undefined; + } + return stripPrefixFromEntityName(name, deviceName.toLowerCase()); }; export const computeEntityDeviceName = ( diff --git a/src/data/entity_registry.ts b/src/data/entity_registry.ts index 1af01d3575cd..66d22a8ae1c8 100644 --- a/src/data/entity_registry.ts +++ b/src/data/entity_registry.ts @@ -26,6 +26,7 @@ export interface EntityRegistryDisplayEntry { translation_key?: string; platform?: string; display_precision?: number; + has_entity_name?: boolean; } export interface EntityRegistryDisplayEntryResponse { diff --git a/src/dialogs/more-info/ha-more-info-dialog.ts b/src/dialogs/more-info/ha-more-info-dialog.ts index 49670004f416..014e18f395cb 100644 --- a/src/dialogs/more-info/ha-more-info-dialog.ts +++ b/src/dialogs/more-info/ha-more-info-dialog.ts @@ -271,22 +271,20 @@ export class MoreInfoDialog extends LitElement { const stateObj = this.hass.states[entityId] as HassEntity | undefined; const domain = computeDomain(entityId); - const name = - (stateObj && - computeEntityName(stateObj, this.hass.entities, this.hass.devices)) || - entityId; const isAdmin = this.hass.user!.is_admin; const deviceId = this._getDeviceId(); - const title = this._childView?.viewTitle ?? name; - const isDefaultView = this._currView === DEFAULT_VIEW && !this._childView; const isSpecificInitialView = this._initialView !== DEFAULT_VIEW && !this._childView; const showCloseIcon = isDefaultView || isSpecificInitialView; + const entityName = stateObj + ? computeEntityName(stateObj, this.hass.entities, this.hass.devices) + : entityId; + const areaName = stateObj ? computeEntityAreaName( stateObj, @@ -295,13 +293,17 @@ export class MoreInfoDialog extends LitElement { this.hass.areas ) : ""; + const deviceName = stateObj ? computeEntityDeviceName(stateObj, this.hass.entities, this.hass.devices) : ""; + const title = + this._childView?.viewTitle || entityName || deviceName || entityId; + const subtitle = this._childView?.viewTitle ? undefined - : [name !== deviceName ? deviceName : undefined, areaName] + : [entityName ? deviceName : undefined, areaName] .filter(Boolean) .join(" ⸱ "); From 2989ac0e92149d45a1cdca38d43ac4aaa115d955 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Wed, 11 Sep 2024 18:55:04 +0200 Subject: [PATCH 3/7] Separate entity, device and area name in entity picker --- src/components/entity/ha-entity-picker.ts | 136 +++++++++++++++------- 1 file changed, 94 insertions(+), 42 deletions(-) diff --git a/src/components/entity/ha-entity-picker.ts b/src/components/entity/ha-entity-picker.ts index 854406065bf0..6c2051f7e2ab 100644 --- a/src/components/entity/ha-entity-picker.ts +++ b/src/components/entity/ha-entity-picker.ts @@ -1,29 +1,35 @@ -import "../ha-list-item"; +import type { ComboBoxLitRenderer } from "@vaadin/combo-box/lit"; import type { HassEntity } from "home-assistant-js-websocket"; import type { PropertyValues, TemplateResult } from "lit"; -import { html, LitElement } from "lit"; -import type { ComboBoxLitRenderer } from "@vaadin/combo-box/lit"; +import { html, LitElement, nothing } from "lit"; import { customElement, property, query, state } from "lit/decorators"; import memoizeOne from "memoize-one"; import { fireEvent } from "../../common/dom/fire_event"; import { computeDomain } from "../../common/entity/compute_domain"; -import { computeStateName } from "../../common/entity/compute_state_name"; +import { + computeEntityAreaName, + computeEntityDeviceName, + computeEntityName, +} from "../../common/entity/compute_entity_name"; +import { caseInsensitiveStringCompare } from "../../common/string/compare"; import type { ScorableTextItem } from "../../common/string/filter/sequence-matching"; import { fuzzyFilterSort } from "../../common/string/filter/sequence-matching"; -import type { ValueChangedEvent, HomeAssistant } from "../../types"; +import { domainToName } from "../../data/integration"; +import type { HelperDomain } from "../../panels/config/helpers/const"; +import { isHelperDomain } from "../../panels/config/helpers/const"; +import { showHelperDetailDialog } from "../../panels/config/helpers/show-dialog-helper-detail"; +import type { HomeAssistant, ValueChangedEvent } from "../../types"; import "../ha-combo-box"; import type { HaComboBox } from "../ha-combo-box"; import "../ha-icon-button"; +import "../ha-list-item"; import "../ha-svg-icon"; import "./state-badge"; -import { caseInsensitiveStringCompare } from "../../common/string/compare"; -import { showHelperDetailDialog } from "../../panels/config/helpers/show-dialog-helper-detail"; -import { domainToName } from "../../data/integration"; -import type { HelperDomain } from "../../panels/config/helpers/const"; -import { isHelperDomain } from "../../panels/config/helpers/const"; interface HassEntityWithCachedName extends HassEntity, ScorableTextItem { - friendly_name: string; + displayed_name: string; + entity_name?: string; + entity_context?: string; } export type HaEntityPickerEntityFilterFunc = (entity: HassEntity) => boolean; @@ -105,7 +111,7 @@ export class HaEntityPicker extends LitElement { @property({ type: Boolean }) public hideClearIcon = false; @property({ attribute: "item-label-path" }) public itemLabelPath = - "friendly_name"; + "displayed_name"; @state() private _opened = false; @@ -127,22 +133,36 @@ export class HaEntityPicker extends LitElement { private _rowRenderer: ComboBoxLitRenderer = ( item - ) => - html` + ) => html` + ${item.state - ? html`` - : ""} - ${item.friendly_name} - ${item.entity_id.startsWith(CREATE_ID) + ? html` + + ` + : nothing} + ${item.entity_name ?? item.displayed_name} + ${item.entity_context + ? html` +
+ ${item.entity_context} +
+ ` + : nothing} +
+ ${item.entity_id.startsWith(CREATE_ID) ? this.hass.localize("ui.components.entity.entity-picker.new_entity") - : item.entity_id} - `; + : item.entity_id} +
+
+ `; private _getStates = memoizeOne( ( @@ -165,8 +185,8 @@ export class HaEntityPicker extends LitElement { let entityIds = Object.keys(hass.states); const createItems = createDomains?.length - ? createDomains.map((domain) => { - const newFriendlyName = hass.localize( + ? createDomains.map((domain) => { + const displayedName = hass.localize( "ui.components.entity.entity-picker.create_helper", { domain: isHelperDomain(domain) @@ -183,11 +203,11 @@ export class HaEntityPicker extends LitElement { last_changed: "", last_updated: "", context: { id: "", user_id: null, parent_id: null }, - friendly_name: newFriendlyName, + displayed_name: displayedName, attributes: { icon: "mdi:plus", }, - strings: [domain, newFriendlyName], + strings: [domain, displayedName], }; }) : []; @@ -200,7 +220,7 @@ export class HaEntityPicker extends LitElement { last_changed: "", last_updated: "", context: { id: "", user_id: null, parent_id: null }, - friendly_name: this.hass!.localize( + displayed_name: this.hass!.localize( "ui.components.entity.entity-picker.no_entities" ), attributes: { @@ -240,18 +260,11 @@ export class HaEntityPicker extends LitElement { } states = entityIds - .map((key) => { - const friendly_name = computeStateName(hass!.states[key]) || key; - return { - ...hass!.states[key], - friendly_name, - strings: [key, friendly_name], - }; - }) + .map((key) => this._stateObjToRowItem(hass!.states[key], hass)) .sort((entityA, entityB) => caseInsensitiveStringCompare( - entityA.friendly_name, - entityB.friendly_name, + entityA.displayed_name, + entityB.displayed_name, this.hass.locale.language ) ); @@ -294,7 +307,7 @@ export class HaEntityPicker extends LitElement { last_changed: "", last_updated: "", context: { id: "", user_id: null, parent_id: null }, - friendly_name: this.hass!.localize( + displayed_name: this.hass!.localize( "ui.components.entity.entity-picker.no_match" ), attributes: { @@ -317,6 +330,45 @@ export class HaEntityPicker extends LitElement { } ); + private _stateObjToRowItem( + stateObj: HassEntity, + hass: HomeAssistant + ): HassEntityWithCachedName { + const areaName = computeEntityAreaName( + stateObj, + hass.entities, + hass.devices, + hass.areas + ); + const deviceName = computeEntityDeviceName( + stateObj, + hass.entities, + hass.devices + ); + + const entityName = computeEntityName(stateObj, hass.entities, hass.devices); + + const displayedName = [deviceName, entityName].filter(Boolean).join(" ⸱ "); + + // Do not include device name if it's the same as entity name + const entityContext = [entityName ? deviceName : undefined, areaName] + .filter(Boolean) + .join(" ⸱ "); + + return { + ...stateObj, + displayed_name: displayedName, + strings: [ + stateObj.entity_id, + displayedName, + areaName ?? "", + deviceName ?? "", + ].filter(Boolean), + entity_name: entityName || deviceName, + entity_context: entityContext, + }; + } + protected shouldUpdate(changedProps: PropertyValues) { if ( changedProps.has("value") || From ecb847d54050b1cfd215464d45d75badeb2adb83 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Thu, 12 Sep 2024 09:50:12 +0200 Subject: [PATCH 4/7] Fix entity name with has entity name --- src/common/entity/compute_entity_name.ts | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/common/entity/compute_entity_name.ts b/src/common/entity/compute_entity_name.ts index f6eb3bf0e1ff..68c752dbf72b 100644 --- a/src/common/entity/compute_entity_name.ts +++ b/src/common/entity/compute_entity_name.ts @@ -13,23 +13,25 @@ export const computeEntityName = ( ): string | undefined => { const entry = entities[stateObj.entity_id] as EntityRegistryDisplayEntry; - // Use entity name from entity registry - if (entry?.has_entity_name) { - return entry.name; + const device = entry?.device_id ? devices[entry.device_id] : undefined; + const name = entry?.has_entity_name + ? entry.name?.trim() + : computeStateName(stateObj).trim(); + + if (!name) { + return undefined; } - // Then fallback to state name - const device = entry?.device_id ? devices[entry.device_id] : undefined; - const name = computeStateName(stateObj).trim(); const deviceName = device ? computeDeviceName(device) : undefined; if (!deviceName) { return name; } + // if the device name equals the entity name, consider empty entity name if (deviceName === name) { return undefined; } - return stripPrefixFromEntityName(name, deviceName.toLowerCase()); + return stripPrefixFromEntityName(name, deviceName.toLowerCase()) || name; }; export const computeEntityDeviceName = ( @@ -42,7 +44,7 @@ export const computeEntityDeviceName = ( | undefined; const device = entry?.device_id ? devices[entry.device_id] : undefined; - return device ? computeDeviceName(device) : ""; + return device ? computeDeviceName(device) : undefined; }; export const computeEntityAreaName = ( @@ -59,5 +61,5 @@ export const computeEntityAreaName = ( const areaId = entry?.area_id || device?.area_id; const area = areaId ? areas[areaId] : undefined; - return area ? computeAreaName(area) : ""; + return area ? computeAreaName(area) : undefined; }; From 0334fd88369f132d418ac002d6d13a4f06c67f4e Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Thu, 12 Sep 2024 15:23:48 +0200 Subject: [PATCH 5/7] Fix compute entity name --- src/common/entity/compute_entity_name.ts | 18 +++++++----------- src/components/entity/ha-entity-picker.ts | 7 +++++-- src/dialogs/more-info/ha-more-info-dialog.ts | 5 ++--- 3 files changed, 14 insertions(+), 16 deletions(-) diff --git a/src/common/entity/compute_entity_name.ts b/src/common/entity/compute_entity_name.ts index 68c752dbf72b..1680be053c21 100644 --- a/src/common/entity/compute_entity_name.ts +++ b/src/common/entity/compute_entity_name.ts @@ -14,23 +14,19 @@ export const computeEntityName = ( const entry = entities[stateObj.entity_id] as EntityRegistryDisplayEntry; const device = entry?.device_id ? devices[entry.device_id] : undefined; - const name = entry?.has_entity_name - ? entry.name?.trim() - : computeStateName(stateObj).trim(); - if (!name) { - return undefined; - } + const name = (entry ? entry.name : computeStateName(stateObj))?.trim(); const deviceName = device ? computeDeviceName(device) : undefined; - if (!deviceName) { - return name; + + if (!name || !deviceName) { + return name || deviceName; } - // if the device name equals the entity name, consider empty entity name - if (deviceName === name) { - return undefined; + if (name === deviceName) { + return name; } + return stripPrefixFromEntityName(name, deviceName.toLowerCase()) || name; }; diff --git a/src/components/entity/ha-entity-picker.ts b/src/components/entity/ha-entity-picker.ts index 6c2051f7e2ab..217211c407f7 100644 --- a/src/components/entity/ha-entity-picker.ts +++ b/src/components/entity/ha-entity-picker.ts @@ -351,7 +351,10 @@ export class HaEntityPicker extends LitElement { const displayedName = [deviceName, entityName].filter(Boolean).join(" ⸱ "); // Do not include device name if it's the same as entity name - const entityContext = [entityName ? deviceName : undefined, areaName] + const entityContext = [ + entityName !== deviceName ? deviceName : undefined, + areaName, + ] .filter(Boolean) .join(" ⸱ "); @@ -364,7 +367,7 @@ export class HaEntityPicker extends LitElement { areaName ?? "", deviceName ?? "", ].filter(Boolean), - entity_name: entityName || deviceName, + entity_name: entityName, entity_context: entityContext, }; } diff --git a/src/dialogs/more-info/ha-more-info-dialog.ts b/src/dialogs/more-info/ha-more-info-dialog.ts index 014e18f395cb..550ca7db9e9b 100644 --- a/src/dialogs/more-info/ha-more-info-dialog.ts +++ b/src/dialogs/more-info/ha-more-info-dialog.ts @@ -298,12 +298,11 @@ export class MoreInfoDialog extends LitElement { ? computeEntityDeviceName(stateObj, this.hass.entities, this.hass.devices) : ""; - const title = - this._childView?.viewTitle || entityName || deviceName || entityId; + const title = this._childView?.viewTitle || entityName || entityId; const subtitle = this._childView?.viewTitle ? undefined - : [entityName ? deviceName : undefined, areaName] + : [entityName !== deviceName ? deviceName : undefined, areaName] // Do not include device name if it's the same as entity name .filter(Boolean) .join(" ⸱ "); From d70e022190551fb1ea8ac3a178f335fab93a37dd Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Thu, 12 Sep 2024 15:32:52 +0200 Subject: [PATCH 6/7] Add full name --- src/common/entity/compute_entity_name.ts | 28 ++++++++++++++++++++++- src/components/entity/ha-entity-picker.ts | 11 ++++++--- src/data/entity_registry.ts | 1 + src/state/connection-mixin.ts | 1 + 4 files changed, 37 insertions(+), 4 deletions(-) diff --git a/src/common/entity/compute_entity_name.ts b/src/common/entity/compute_entity_name.ts index 1680be053c21..54f8053bc051 100644 --- a/src/common/entity/compute_entity_name.ts +++ b/src/common/entity/compute_entity_name.ts @@ -6,12 +6,38 @@ import { computeStateName } from "./compute_state_name"; import { computeDeviceName } from "./compute_device_name"; import { computeAreaName } from "./compute_area_name"; +export const computeEntityFullName = ( + stateObj: HassEntity, + entities: HomeAssistant["entities"], + devices: HomeAssistant["devices"] +): string | undefined => { + const entry = entities[stateObj.entity_id] as + | EntityRegistryDisplayEntry + | undefined; + + const entityName = computeEntityName(stateObj, entities, devices); + + if (!entry?.has_entity_name) { + return entityName; + } + + const deviceName = computeEntityDeviceName(stateObj, entities, devices); + + if (!entityName || !deviceName || entityName === deviceName) { + return entityName || deviceName; + } + + return `${deviceName} ${entityName}`; +}; + export const computeEntityName = ( stateObj: HassEntity, entities: HomeAssistant["entities"], devices: HomeAssistant["devices"] ): string | undefined => { - const entry = entities[stateObj.entity_id] as EntityRegistryDisplayEntry; + const entry = entities[stateObj.entity_id] as + | EntityRegistryDisplayEntry + | undefined; const device = entry?.device_id ? devices[entry.device_id] : undefined; diff --git a/src/components/entity/ha-entity-picker.ts b/src/components/entity/ha-entity-picker.ts index 217211c407f7..9c8100ba17f6 100644 --- a/src/components/entity/ha-entity-picker.ts +++ b/src/components/entity/ha-entity-picker.ts @@ -9,6 +9,7 @@ import { computeDomain } from "../../common/entity/compute_domain"; import { computeEntityAreaName, computeEntityDeviceName, + computeEntityFullName, computeEntityName, } from "../../common/entity/compute_entity_name"; import { caseInsensitiveStringCompare } from "../../common/string/compare"; @@ -348,7 +349,11 @@ export class HaEntityPicker extends LitElement { const entityName = computeEntityName(stateObj, hass.entities, hass.devices); - const displayedName = [deviceName, entityName].filter(Boolean).join(" ⸱ "); + const displayedName = computeEntityFullName( + stateObj, + hass.entities, + hass.devices + ); // Do not include device name if it's the same as entity name const entityContext = [ @@ -360,10 +365,10 @@ export class HaEntityPicker extends LitElement { return { ...stateObj, - displayed_name: displayedName, + displayed_name: displayedName ?? "", strings: [ stateObj.entity_id, - displayedName, + displayedName ?? "", areaName ?? "", deviceName ?? "", ].filter(Boolean), diff --git a/src/data/entity_registry.ts b/src/data/entity_registry.ts index 66d22a8ae1c8..2a2df47a49bd 100644 --- a/src/data/entity_registry.ts +++ b/src/data/entity_registry.ts @@ -42,6 +42,7 @@ export interface EntityRegistryDisplayEntryResponse { tk?: string; hb?: boolean; dp?: number; + hn?: boolean; }[]; entity_categories: Record; } diff --git a/src/state/connection-mixin.ts b/src/state/connection-mixin.ts index 2a9803d8e8a4..28b129c523b0 100644 --- a/src/state/connection-mixin.ts +++ b/src/state/connection-mixin.ts @@ -246,6 +246,7 @@ export const connectionMixin = >( entity.ec !== undefined ? entityReg.entity_categories[entity.ec] : undefined, + has_entity_name: entity.hn, name: entity.en, icon: entity.ic, hidden: entity.hb, From 8464b00afb2c09cf75d457c0d759d1944d687191 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Wed, 18 Sep 2024 08:55:48 +0200 Subject: [PATCH 7/7] Add floor --- src/common/entity/compute_entity_name.ts | 20 ++++++++++++++++++++ src/common/entity/compute_floor_name.ts | 5 +++++ src/components/entity/ha-entity-picker.ts | 10 ++++++++++ src/dialogs/more-info/ha-more-info-dialog.ts | 17 ++++++++++++++++- 4 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 src/common/entity/compute_floor_name.ts diff --git a/src/common/entity/compute_entity_name.ts b/src/common/entity/compute_entity_name.ts index 54f8053bc051..9de44644e58c 100644 --- a/src/common/entity/compute_entity_name.ts +++ b/src/common/entity/compute_entity_name.ts @@ -5,6 +5,7 @@ import { stripPrefixFromEntityName } from "./strip_prefix_from_entity_name"; import { computeStateName } from "./compute_state_name"; import { computeDeviceName } from "./compute_device_name"; import { computeAreaName } from "./compute_area_name"; +import { computeFloorName } from "./compute_floor_name"; export const computeEntityFullName = ( stateObj: HassEntity, @@ -85,3 +86,22 @@ export const computeEntityAreaName = ( return area ? computeAreaName(area) : undefined; }; + +export const computeEntityFloorName = ( + stateObj: HassEntity, + entities: HomeAssistant["entities"], + devices: HomeAssistant["devices"], + areas: HomeAssistant["areas"], + floors: HomeAssistant["floors"] +): string | undefined => { + const entry = entities[stateObj.entity_id] as + | EntityRegistryDisplayEntry + | undefined; + const device = entry?.device_id ? devices[entry?.device_id] : undefined; + + const areaId = entry?.area_id || device?.area_id; + const area = areaId ? areas[areaId] : undefined; + const floor = area?.floor_id ? floors[area?.floor_id] : undefined; + + return floor ? computeFloorName(floor) : undefined; +}; diff --git a/src/common/entity/compute_floor_name.ts b/src/common/entity/compute_floor_name.ts new file mode 100644 index 000000000000..e6355cdb216d --- /dev/null +++ b/src/common/entity/compute_floor_name.ts @@ -0,0 +1,5 @@ +import { FloorRegistryEntry } from "../../data/floor_registry"; + +export const computeFloorName = ( + floor: FloorRegistryEntry +): string | undefined => floor.name?.trim(); diff --git a/src/components/entity/ha-entity-picker.ts b/src/components/entity/ha-entity-picker.ts index 9c8100ba17f6..18401b7c7ef9 100644 --- a/src/components/entity/ha-entity-picker.ts +++ b/src/components/entity/ha-entity-picker.ts @@ -9,6 +9,7 @@ import { computeDomain } from "../../common/entity/compute_domain"; import { computeEntityAreaName, computeEntityDeviceName, + computeEntityFloorName, computeEntityFullName, computeEntityName, } from "../../common/entity/compute_entity_name"; @@ -341,6 +342,13 @@ export class HaEntityPicker extends LitElement { hass.devices, hass.areas ); + const floorName = computeEntityFloorName( + stateObj, + hass.entities, + hass.devices, + hass.areas, + hass.floors + ); const deviceName = computeEntityDeviceName( stateObj, hass.entities, @@ -359,6 +367,7 @@ export class HaEntityPicker extends LitElement { const entityContext = [ entityName !== deviceName ? deviceName : undefined, areaName, + floorName, ] .filter(Boolean) .join(" ⸱ "); @@ -371,6 +380,7 @@ export class HaEntityPicker extends LitElement { displayedName ?? "", areaName ?? "", deviceName ?? "", + floorName ?? "", ].filter(Boolean), entity_name: entityName, entity_context: entityContext, diff --git a/src/dialogs/more-info/ha-more-info-dialog.ts b/src/dialogs/more-info/ha-more-info-dialog.ts index 550ca7db9e9b..2d24a5def766 100644 --- a/src/dialogs/more-info/ha-more-info-dialog.ts +++ b/src/dialogs/more-info/ha-more-info-dialog.ts @@ -22,6 +22,7 @@ import { computeDomain } from "../../common/entity/compute_domain"; import { computeEntityAreaName, computeEntityDeviceName, + computeEntityFloorName, computeEntityName, } from "../../common/entity/compute_entity_name"; import { shouldHandleRequestSelectedEvent } from "../../common/mwc/handle-request-selected-event"; @@ -294,6 +295,16 @@ export class MoreInfoDialog extends LitElement { ) : ""; + const floorName = stateObj + ? computeEntityFloorName( + stateObj, + this.hass.entities, + this.hass.devices, + this.hass.areas, + this.hass.floors + ) + : ""; + const deviceName = stateObj ? computeEntityDeviceName(stateObj, this.hass.entities, this.hass.devices) : ""; @@ -302,7 +313,11 @@ export class MoreInfoDialog extends LitElement { const subtitle = this._childView?.viewTitle ? undefined - : [entityName !== deviceName ? deviceName : undefined, areaName] // Do not include device name if it's the same as entity name + : [ + entityName !== deviceName ? deviceName : undefined, + areaName, + floorName, + ] // Do not include device name if it's the same as entity name .filter(Boolean) .join(" ⸱ ");