From 53bb8251fa25fa54fb672eefc9dfaeaa96da0b0c Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Wed, 26 Mar 2025 14:58:34 +0100 Subject: [PATCH] Separate entity, device and area name in more info dialog header (#21951) * Separate entity, device and area name in more info dialog header * Use has_entity_name * Separate entity, device and area name in entity picker * Fix entity name with has entity name * Fix compute entity name * Add full name * Add floor * Improve code quality * Use compute entity name in device entities card * Use context functions * Remove floor * Use state name provided by backend * Use breadcrumb for more info * Revert entity picker changes * Use new logic in device page * Use breadcrumb * Use join directive * Add comments * Use secondary text color * Update compute_entity_name.ts Co-authored-by: Bram Kragten * Remove html join * Improve computeDeviceNameDisplay * Fallback to original name * Simplify more info logic * Include breadcrumb for child view (voice assistant) --------- Co-authored-by: Bram Kragten --- build-scripts/rspack.cjs | 6 ++ src/common/entity/compute_area_name.ts | 4 + src/common/entity/compute_device_name.ts | 38 ++++++++++ src/common/entity/compute_entity_name.ts | 52 +++++++++++++ src/common/entity/compute_floor_name.ts | 4 + src/common/entity/get_area_context.ts | 18 +++++ src/common/entity/get_device_context.ts | 24 ++++++ src/common/entity/get_entity_context.ts | 34 +++++++++ .../entity/strip_prefix_from_entity_name.ts | 8 +- src/components/device/ha-device-picker.ts | 8 +- src/components/ha-filter-devices.ts | 12 +-- src/components/ha-target-picker.ts | 6 +- src/data/device_registry.ts | 14 ---- src/data/entity_registry.ts | 2 + src/data/script_i18n.ts | 4 +- .../config-flow/step-flow-create-entry.ts | 11 ++- src/dialogs/more-info/ha-more-info-dialog.ts | 75 +++++++++++++++++-- src/dialogs/quick-bar/ha-quick-bar.ts | 8 +- .../config/areas/ha-config-area-page.ts | 12 ++- .../config/dashboard/ha-config-updates.ts | 10 +-- .../device-detail/ha-device-entities-card.ts | 32 +++----- .../device-detail/ha-device-info-card.ts | 10 ++- .../ha-device-via-devices-card.ts | 8 +- .../mqtt/dialog-mqtt-device-debug-info.ts | 4 +- .../dialog-device-registry-detail.ts | 4 +- .../config/devices/ha-config-device-page.ts | 21 +++--- .../devices/ha-config-devices-dashboard.ts | 10 +-- .../dialog-zwave_js-node-statistics.ts | 11 +-- .../dialog-zwave_js-rebuild-node-routes.ts | 27 ++++--- .../dialog-zwave_js-update-firmware-node.ts | 4 +- .../zwave_js/zwave_js-node-config.ts | 16 ++-- .../zwave_js/zwave_js-node-installer.ts | 8 +- src/panels/config/scene/ha-scene-editor.ts | 20 ++--- ...-config-voice-assistants-assist-devices.ts | 4 +- src/state-display/state-display.ts | 10 +-- src/state/connection-mixin.ts | 1 + 36 files changed, 377 insertions(+), 163 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 create mode 100644 src/common/entity/compute_floor_name.ts create mode 100644 src/common/entity/get_area_context.ts create mode 100644 src/common/entity/get_device_context.ts create mode 100644 src/common/entity/get_entity_context.ts diff --git a/build-scripts/rspack.cjs b/build-scripts/rspack.cjs index 0c7b264deb..02bff46372 100644 --- a/build-scripts/rspack.cjs +++ b/build-scripts/rspack.cjs @@ -1,12 +1,17 @@ const { existsSync } = require("fs"); const path = require("path"); const rspack = require("@rspack/core"); +// eslint-disable-next-line @typescript-eslint/naming-convention const { RsdoctorRspackPlugin } = require("@rsdoctor/rspack-plugin"); +// eslint-disable-next-line @typescript-eslint/naming-convention const { StatsWriterPlugin } = require("webpack-stats-plugin"); const filterStats = require("@bundle-stats/plugin-webpack-filter"); +// eslint-disable-next-line @typescript-eslint/naming-convention const TerserPlugin = require("terser-webpack-plugin"); +// eslint-disable-next-line @typescript-eslint/naming-convention const { WebpackManifestPlugin } = require("rspack-manifest-plugin"); const log = require("fancy-log"); +// eslint-disable-next-line @typescript-eslint/naming-convention const WebpackBar = require("webpackbar/rspack"); const paths = require("./paths.cjs"); const bundle = require("./bundle.cjs"); @@ -190,6 +195,7 @@ const createRspackConfig = ({ "lit/directives/if-defined$": "lit/directives/if-defined.js", "lit/directives/guard$": "lit/directives/guard.js", "lit/directives/cache$": "lit/directives/cache.js", + "lit/directives/join$": "lit/directives/join.js", "lit/directives/repeat$": "lit/directives/repeat.js", "lit/directives/live$": "lit/directives/live.js", "lit/directives/keyed$": "lit/directives/keyed.js", diff --git a/src/common/entity/compute_area_name.ts b/src/common/entity/compute_area_name.ts new file mode 100644 index 0000000000..fdd515ba9a --- /dev/null +++ b/src/common/entity/compute_area_name.ts @@ -0,0 +1,4 @@ +import type { 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 0000000000..b0f1cc7d14 --- /dev/null +++ b/src/common/entity/compute_device_name.ts @@ -0,0 +1,38 @@ +import type { DeviceRegistryEntry } from "../../data/device_registry"; +import type { + EntityRegistryDisplayEntry, + EntityRegistryEntry, +} from "../../data/entity_registry"; +import type { HomeAssistant } from "../../types"; +import { computeStateName } from "./compute_state_name"; + +export const computeDeviceName = ( + device: DeviceRegistryEntry +): string | undefined => (device.name_by_user || device.name)?.trim(); + +export const computeDeviceNameDisplay = ( + device: DeviceRegistryEntry, + hass: HomeAssistant, + entities?: EntityRegistryEntry[] | EntityRegistryDisplayEntry[] | string[] +) => + computeDeviceName(device) || + (entities && fallbackDeviceName(hass, entities)) || + hass.localize("ui.panel.config.devices.unnamed_device", { + type: hass.localize( + `ui.panel.config.devices.type.${device.entry_type || "device"}` + ), + }); + +export const fallbackDeviceName = ( + hass: HomeAssistant, + entities: EntityRegistryEntry[] | EntityRegistryDisplayEntry[] | string[] +) => { + for (const entity of entities || []) { + const entityId = typeof entity === "string" ? entity : entity.entity_id; + const stateObj = hass.states[entityId]; + if (stateObj) { + return computeStateName(stateObj); + } + } + return undefined; +}; diff --git a/src/common/entity/compute_entity_name.ts b/src/common/entity/compute_entity_name.ts new file mode 100644 index 0000000000..57843063d4 --- /dev/null +++ b/src/common/entity/compute_entity_name.ts @@ -0,0 +1,52 @@ +import type { HassEntity } from "home-assistant-js-websocket"; +import type { + EntityRegistryDisplayEntry, + EntityRegistryEntry, +} from "../../data/entity_registry"; +import type { HomeAssistant } from "../../types"; +import { computeDeviceName } from "./compute_device_name"; +import { computeStateName } from "./compute_state_name"; +import { stripPrefixFromEntityName } from "./strip_prefix_from_entity_name"; + +export const computeEntityName = ( + stateObj: HassEntity, + hass: HomeAssistant +): string | undefined => { + const entry = hass.entities[stateObj.entity_id] as + | EntityRegistryDisplayEntry + | undefined; + + if (!entry) { + // Fall back to state name if not in the entity registry (friendly name) + return computeStateName(stateObj); + } + return computeEntityEntryName(entry, hass); +}; + +export const computeEntityEntryName = ( + entry: EntityRegistryDisplayEntry | EntityRegistryEntry, + hass: HomeAssistant +): string | undefined => { + const name = + entry.name || ("original_name" in entry ? entry.original_name : undefined); + + const device = entry.device_id ? hass.devices[entry.device_id] : undefined; + + if (!device) { + return name; + } + + const deviceName = computeDeviceName(device); + + // If the device name is the same as the entity name, consider empty entity name + if (deviceName === name) { + return undefined; + } + + // Remove the device name from the entity name if it starts with it + if (deviceName && name) { + return stripPrefixFromEntityName(name, deviceName) || name; + } + + return name; +}; diff --git a/src/common/entity/compute_floor_name.ts b/src/common/entity/compute_floor_name.ts new file mode 100644 index 0000000000..e3e0f23db9 --- /dev/null +++ b/src/common/entity/compute_floor_name.ts @@ -0,0 +1,4 @@ +import type { FloorRegistryEntry } from "../../data/floor_registry"; + +export const computeFloorName = (floor: FloorRegistryEntry): string => + floor.name?.trim(); diff --git a/src/common/entity/get_area_context.ts b/src/common/entity/get_area_context.ts new file mode 100644 index 0000000000..0d14290ff3 --- /dev/null +++ b/src/common/entity/get_area_context.ts @@ -0,0 +1,18 @@ +import type { AreaRegistryEntry } from "../../data/area_registry"; +import type { FloorRegistryEntry } from "../../data/floor_registry"; +import type { HomeAssistant } from "../../types"; + +interface AreaContext { + floor: FloorRegistryEntry | null; +} +export const getAreaContext = ( + area: AreaRegistryEntry, + hass: HomeAssistant +): AreaContext => { + const floorId = area.floor_id; + const floor = floorId ? hass.floors[floorId] : null; + + return { + floor: floor, + }; +}; diff --git a/src/common/entity/get_device_context.ts b/src/common/entity/get_device_context.ts new file mode 100644 index 0000000000..c25f551350 --- /dev/null +++ b/src/common/entity/get_device_context.ts @@ -0,0 +1,24 @@ +import type { AreaRegistryEntry } from "../../data/area_registry"; +import type { DeviceRegistryEntry } from "../../data/device_registry"; +import type { FloorRegistryEntry } from "../../data/floor_registry"; +import type { HomeAssistant } from "../../types"; + +interface DeviceContext { + area: AreaRegistryEntry | null; + floor: FloorRegistryEntry | null; +} + +export const getDeviceContext = ( + device: DeviceRegistryEntry, + hass: HomeAssistant +): DeviceContext => { + const areaId = device.area_id; + const area = areaId ? hass.areas[areaId] : null; + const floorId = area?.floor_id; + const floor = floorId ? hass.floors[floorId] : null; + + return { + area: area, + floor: floor, + }; +}; diff --git a/src/common/entity/get_entity_context.ts b/src/common/entity/get_entity_context.ts new file mode 100644 index 0000000000..b08e2d20a9 --- /dev/null +++ b/src/common/entity/get_entity_context.ts @@ -0,0 +1,34 @@ +import type { HassEntity } from "home-assistant-js-websocket"; +import type { AreaRegistryEntry } from "../../data/area_registry"; +import type { DeviceRegistryEntry } from "../../data/device_registry"; +import type { EntityRegistryDisplayEntry } from "../../data/entity_registry"; +import type { FloorRegistryEntry } from "../../data/floor_registry"; +import type { HomeAssistant } from "../../types"; + +interface EntityContext { + device: DeviceRegistryEntry | null; + area: AreaRegistryEntry | null; + floor: FloorRegistryEntry | null; +} + +export const getEntityContext = ( + stateObj: HassEntity, + hass: HomeAssistant +): EntityContext => { + const entry = hass.entities[stateObj.entity_id] as + | EntityRegistryDisplayEntry + | undefined; + + const deviceId = entry?.device_id; + const device = deviceId ? hass.devices[deviceId] : null; + const areaId = entry?.area_id || device?.area_id; + const area = areaId ? hass.areas[areaId] : null; + const floorId = area?.floor_id; + const floor = floorId ? hass.floors[floorId] : null; + + return { + device: device, + area: area, + floor: floor, + }; +}; diff --git a/src/common/entity/strip_prefix_from_entity_name.ts b/src/common/entity/strip_prefix_from_entity_name.ts index bbb024fa37..419e9d9094 100644 --- a/src/common/entity/strip_prefix_from_entity_name.ts +++ b/src/common/entity/strip_prefix_from_entity_name.ts @@ -1,17 +1,17 @@ -const SUFFIXES = [" ", ": "]; +const SUFFIXES = [" ", ": ", " - "]; /** * Strips a device name from an entity name. * @param entityName the entity name - * @param lowerCasedPrefix the prefix to strip, lower cased + * @param prefix the prefix to strip * @returns */ export const stripPrefixFromEntityName = ( entityName: string, - lowerCasedPrefix: string + prefix: string ) => { const lowerCasedEntityName = entityName.toLowerCase(); - + const lowerCasedPrefix = prefix.toLowerCase(); for (const suffix of SUFFIXES) { const lowerCasedPrefixWithSuffix = `${lowerCasedPrefix}${suffix}`; diff --git a/src/components/device/ha-device-picker.ts b/src/components/device/ha-device-picker.ts index 4182ee106a..8d8cb13f35 100644 --- a/src/components/device/ha-device-picker.ts +++ b/src/components/device/ha-device-picker.ts @@ -5,6 +5,7 @@ import { LitElement, html } from "lit"; import { customElement, property, query, state } from "lit/decorators"; import memoizeOne from "memoize-one"; import { fireEvent } from "../../common/dom/fire_event"; +import { computeDeviceNameDisplay } from "../../common/entity/compute_device_name"; import { computeDomain } from "../../common/entity/compute_domain"; import { stringCompare } from "../../common/string/compare"; import type { ScorableTextItem } from "../../common/string/filter/sequence-matching"; @@ -13,10 +14,7 @@ import type { DeviceEntityDisplayLookup, DeviceRegistryEntry, } from "../../data/device_registry"; -import { - computeDeviceName, - getDeviceEntityDisplayLookup, -} from "../../data/device_registry"; +import { getDeviceEntityDisplayLookup } from "../../data/device_registry"; import type { EntityRegistryDisplayEntry } from "../../data/entity_registry"; import type { HomeAssistant, ValueChangedEvent } from "../../types"; import "../ha-combo-box"; @@ -214,7 +212,7 @@ export class HaDevicePicker extends LitElement { } const outputDevices = inputDevices.map((device) => { - const name = computeDeviceName( + const name = computeDeviceNameDisplay( device, this.hass, deviceEntityLookup[device.id] diff --git a/src/components/ha-filter-devices.ts b/src/components/ha-filter-devices.ts index 9c3498a744..69b53b4d29 100644 --- a/src/components/ha-filter-devices.ts +++ b/src/components/ha-filter-devices.ts @@ -5,8 +5,8 @@ import { css, html, LitElement, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; import memoizeOne from "memoize-one"; import { fireEvent } from "../common/dom/fire_event"; +import { computeDeviceNameDisplay } from "../common/entity/compute_device_name"; import { stringCompare } from "../common/string/compare"; -import { computeDeviceName } from "../data/device_registry"; import type { RelatedResult } from "../data/search"; import { findRelated } from "../data/search"; import { haStyleScrollbar } from "../resources/styles"; @@ -95,7 +95,7 @@ export class HaFilterDevices extends LitElement { .value=${device.id} .selected=${this.value?.includes(device.id) ?? false} > - ${computeDeviceName(device, this.hass)} + ${computeDeviceNameDisplay(device, this.hass)} `; private _handleItemClick(ev) { @@ -142,12 +142,14 @@ export class HaFilterDevices extends LitElement { .filter( (device) => !filter || - computeDeviceName(device, this.hass).toLowerCase().includes(filter) + computeDeviceNameDisplay(device, this.hass) + .toLowerCase() + .includes(filter) ) .sort((a, b) => stringCompare( - computeDeviceName(a, this.hass), - computeDeviceName(b, this.hass), + computeDeviceNameDisplay(a, this.hass), + computeDeviceNameDisplay(b, this.hass), this.hass.locale.language ) ); diff --git a/src/components/ha-target-picker.ts b/src/components/ha-target-picker.ts index 17d108b637..a3296fbe65 100644 --- a/src/components/ha-target-picker.ts +++ b/src/components/ha-target-picker.ts @@ -26,12 +26,12 @@ import { computeCssColor } from "../common/color/compute-color"; import { hex2rgb } from "../common/color/convert-color"; import { fireEvent } from "../common/dom/fire_event"; import { stopPropagation } from "../common/dom/stop_propagation"; +import { computeDeviceNameDisplay } from "../common/entity/compute_device_name"; import { computeDomain } from "../common/entity/compute_domain"; import { computeStateName } from "../common/entity/compute_state_name"; import { isValidEntityId } from "../common/entity/valid_entity_id"; import type { AreaRegistryEntry } from "../data/area_registry"; import type { DeviceRegistryEntry } from "../data/device_registry"; -import { computeDeviceName } from "../data/device_registry"; import type { EntityRegistryDisplayEntry } from "../data/entity_registry"; import type { LabelRegistryEntry } from "../data/label_registry"; import { subscribeLabelRegistry } from "../data/label_registry"; @@ -150,7 +150,9 @@ export class HaTargetPicker extends SubscribeMixin(LitElement) { return this._renderChip( "device_id", device_id, - device ? computeDeviceName(device, this.hass) : device_id, + device + ? computeDeviceNameDisplay(device, this.hass) + : device_id, undefined, undefined, mdiDevices diff --git a/src/data/device_registry.ts b/src/data/device_registry.ts index 3cc33c62f0..eece4e9537 100644 --- a/src/data/device_registry.ts +++ b/src/data/device_registry.ts @@ -65,20 +65,6 @@ export const fallbackDeviceName = ( return undefined; }; -export const computeDeviceName = ( - device: DeviceRegistryEntry, - hass: HomeAssistant, - entities?: EntityRegistryEntry[] | EntityRegistryDisplayEntry[] | string[] -) => - device.name_by_user || - device.name || - (entities && fallbackDeviceName(hass, entities)) || - hass.localize("ui.panel.config.devices.unnamed_device", { - type: hass.localize( - `ui.panel.config.devices.type.${device.entry_type || "device"}` - ), - }); - export const devicesInArea = (devices: DeviceRegistryEntry[], areaId: string) => devices.filter((device) => device.area_id === areaId); diff --git a/src/data/entity_registry.ts b/src/data/entity_registry.ts index b76773658b..4376c7bd45 100644 --- a/src/data/entity_registry.ts +++ b/src/data/entity_registry.ts @@ -24,6 +24,7 @@ export interface EntityRegistryDisplayEntry { translation_key?: string; platform?: string; display_precision?: number; + has_entity_name?: boolean; } export interface EntityRegistryDisplayEntryResponse { @@ -39,6 +40,7 @@ export interface EntityRegistryDisplayEntryResponse { tk?: string; hb?: boolean; dp?: number; + hn?: boolean; }[]; entity_categories: Record; } diff --git a/src/data/script_i18n.ts b/src/data/script_i18n.ts index 0e289a9474..a95927407c 100644 --- a/src/data/script_i18n.ts +++ b/src/data/script_i18n.ts @@ -1,6 +1,7 @@ import { ensureArray } from "../common/array/ensure-array"; import { formatNumericDuration } from "../common/datetime/format_duration"; import secondsToDuration from "../common/datetime/seconds_to_duration"; +import { computeDeviceNameDisplay } from "../common/entity/compute_device_name"; import { computeStateName } from "../common/entity/compute_state_name"; import { formatListWithAnds } from "../common/string/format-list"; import { isTemplate } from "../common/string/has-template"; @@ -8,7 +9,6 @@ import type { HomeAssistant } from "../types"; import type { Condition } from "./automation"; import { describeCondition } from "./automation_i18n"; import { localizeDeviceAutomationAction } from "./device_automation"; -import { computeDeviceName } from "./device_registry"; import type { EntityRegistryEntry } from "./entity_registry"; import { computeEntityRegistryName, @@ -147,7 +147,7 @@ const tryDescribeAction = ( } else if (key === "device_id") { const device = hass.devices[targetThing]; if (device) { - targets.push(computeDeviceName(device, hass)); + targets.push(computeDeviceNameDisplay(device, hass)); } else { targets.push( hass.localize( diff --git a/src/dialogs/config-flow/step-flow-create-entry.ts b/src/dialogs/config-flow/step-flow-create-entry.ts index 64f7ac1b3b..db6b147f57 100644 --- a/src/dialogs/config-flow/step-flow-create-entry.ts +++ b/src/dialogs/config-flow/step-flow-create-entry.ts @@ -4,22 +4,20 @@ import { css, html, LitElement, nothing } from "lit"; import { customElement, property } from "lit/decorators"; import memoizeOne from "memoize-one"; import { fireEvent } from "../../common/dom/fire_event"; +import { computeDeviceNameDisplay } from "../../common/entity/compute_device_name"; import { computeDomain } from "../../common/entity/compute_domain"; +import { navigate } from "../../common/navigate"; import "../../components/ha-area-picker"; import { assistSatelliteSupportsSetupFlow } from "../../data/assist_satellite"; import type { DataEntryFlowStepCreateEntry } from "../../data/data_entry_flow"; import type { DeviceRegistryEntry } from "../../data/device_registry"; -import { - computeDeviceName, - updateDeviceRegistryEntry, -} from "../../data/device_registry"; +import { updateDeviceRegistryEntry } from "../../data/device_registry"; import type { EntityRegistryDisplayEntry } from "../../data/entity_registry"; import type { HomeAssistant } from "../../types"; import { showAlertDialog } from "../generic/show-dialog-box"; import { showVoiceAssistantSetupDialog } from "../voice-assistant-setup/show-voice-assistant-setup-dialog"; import type { FlowConfig } from "./show-dialog-data-entry-flow"; import { configFlowContentStyles } from "./styles"; -import { navigate } from "../../common/navigate"; @customElement("step-flow-create-entry") class StepFlowCreateEntry extends LitElement { @@ -124,7 +122,8 @@ class StepFlowCreateEntry extends LitElement { (device) => html`
- ${computeDeviceName(device, this.hass)}
+ ${computeDeviceNameDisplay(device, this.hass)}
${!device.model && !device.manufacturer ? html` ` : html`${device.model} diff --git a/src/dialogs/more-info/ha-more-info-dialog.ts b/src/dialogs/more-info/ha-more-info-dialog.ts index cc383b86ed..19f79acb0e 100644 --- a/src/dialogs/more-info/ha-more-info-dialog.ts +++ b/src/dialogs/more-info/ha-more-info-dialog.ts @@ -14,11 +14,15 @@ import type { PropertyValues } from "lit"; import { LitElement, css, html, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; import { cache } from "lit/directives/cache"; +import { join } from "lit/directives/join"; import { dynamicElement } from "../../common/dom/dynamic-element-directive"; import { fireEvent } from "../../common/dom/fire_event"; import { stopPropagation } from "../../common/dom/stop_propagation"; +import { computeAreaName } from "../../common/entity/compute_area_name"; +import { computeDeviceName } from "../../common/entity/compute_device_name"; import { computeDomain } from "../../common/entity/compute_domain"; -import { computeStateName } from "../../common/entity/compute_state_name"; +import { computeEntityName } from "../../common/entity/compute_entity_name"; +import { getEntityContext } from "../../common/entity/get_entity_context"; import { shouldHandleRequestSelectedEvent } from "../../common/mwc/handle-request-selected-event"; import { navigate } from "../../common/navigate"; import "../../components/ha-button-menu"; @@ -34,7 +38,9 @@ import type { } from "../../data/entity_registry"; import { getExtendedEntityRegistryEntry } from "../../data/entity_registry"; import { lightSupportsFavoriteColors } from "../../data/light"; +import type { ItemType } from "../../data/search"; import { SearchableDomains } from "../../data/search"; +import { getSensorNumericDeviceClasses } from "../../data/sensor"; import { haStyleDialog } from "../../resources/styles"; import "../../state-summary/state-card-content"; import type { HomeAssistant } from "../../types"; @@ -50,7 +56,6 @@ import "./ha-more-info-history-and-logbook"; import "./ha-more-info-info"; import "./ha-more-info-settings"; import "./more-info-content"; -import { getSensorNumericDeviceClasses } from "../../data/sensor"; export interface MoreInfoDialogParams { entityId: string | null; @@ -278,19 +283,31 @@ 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 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 context = stateObj ? getEntityContext(stateObj, this.hass) : null; + + const entityName = stateObj + ? computeEntityName(stateObj, this.hass) + : undefined; + const deviceName = context?.device + ? computeDeviceName(context.device) + : undefined; + const areaName = context?.area ? computeAreaName(context.area) : undefined; + + const breadcrumb = [areaName, deviceName, entityName].filter( + (v): v is string => Boolean(v) + ); + const title = this._childView?.viewTitle || breadcrumb.pop(); + return html` `} - - ${title} + + ${breadcrumb.length > 0 + ? html` + + ` + : nothing} +

${title}

${isDefaultView ? html` @@ -512,7 +541,7 @@ export class MoreInfoDialog extends LitElement { .hass=${this.hass} .itemId=${entityId} .itemType=${SearchableDomains.has(domain) - ? domain + ? (domain as ItemType) : "entity"} > ` @@ -610,6 +639,36 @@ 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 .main { + color: var(--primary-text-color); + font-size: 20px; + line-height: 24px; + } + + .title .breadcrumb { + color: var(--secondary-text-color); + font-size: 14px; + line-height: 16px; + margin-top: -6px; + } + + .title .breadcrumb { + --mdc-icon-size: 16px; + } `, ]; } diff --git a/src/dialogs/quick-bar/ha-quick-bar.ts b/src/dialogs/quick-bar/ha-quick-bar.ts index 7c4321f43c..d64fb13ce8 100644 --- a/src/dialogs/quick-bar/ha-quick-bar.ts +++ b/src/dialogs/quick-bar/ha-quick-bar.ts @@ -19,16 +19,17 @@ import { canShowPage } from "../../common/config/can_show_page"; import { componentsWithService } from "../../common/config/components_with_service"; import { isComponentLoaded } from "../../common/config/is_component_loaded"; import { fireEvent } from "../../common/dom/fire_event"; +import { computeDeviceNameDisplay } from "../../common/entity/compute_device_name"; import { computeStateName } from "../../common/entity/compute_state_name"; import { navigate } from "../../common/navigate"; import { caseInsensitiveStringCompare } from "../../common/string/compare"; import type { ScorableTextItem } from "../../common/string/filter/sequence-matching"; import { fuzzyFilterSort } from "../../common/string/filter/sequence-matching"; import { debounce } from "../../common/util/debounce"; -import "../../components/ha-spinner"; import "../../components/ha-icon-button"; import "../../components/ha-label"; import "../../components/ha-list-item"; +import "../../components/ha-spinner"; import "../../components/ha-textfield"; import { fetchHassioAddonsInfo } from "../../data/hassio/addon"; import { domainToName } from "../../data/integration"; @@ -40,7 +41,6 @@ import { loadVirtualizer } from "../../resources/virtualizer"; import type { HomeAssistant } from "../../types"; import { showConfirmationDialog } from "../generic/show-dialog-box"; import { QuickBarMode, type QuickBarParams } from "./show-dialog-quick-bar"; -import { computeDeviceName } from "../../data/device_registry"; interface QuickBarItem extends ScorableTextItem { primaryText: string; @@ -529,9 +529,7 @@ export class QuickBar extends LitElement { ? this.hass.areas[device.area_id] : undefined; const deviceItem = { - primaryText: - computeDeviceName(device, this.hass) || - this.hass.localize("ui.components.device-picker.unnamed_device"), + primaryText: computeDeviceNameDisplay(device, this.hass), deviceId: device.id, area: area?.name, action: () => navigate(`/config/devices/device/${device.id}`), diff --git a/src/panels/config/areas/ha-config-area-page.ts b/src/panels/config/areas/ha-config-area-page.ts index 398bedb30c..1297511018 100644 --- a/src/panels/config/areas/ha-config-area-page.ts +++ b/src/panels/config/areas/ha-config-area-page.ts @@ -1,7 +1,7 @@ -import "@material/mwc-list/mwc-list-item"; import { consume } from "@lit-labs/context"; import "@material/mwc-button"; import "@material/mwc-list"; +import "@material/mwc-list/mwc-list-item"; import { mdiDelete, mdiDotsVertical, mdiImagePlus, mdiPencil } from "@mdi/js"; import type { HassEntity } from "home-assistant-js-websocket/dist/types"; import type { CSSResultGroup } from "lit"; @@ -10,16 +10,17 @@ import { customElement, property, state } from "lit/decorators"; import { ifDefined } from "lit/directives/if-defined"; import memoizeOne from "memoize-one"; import { isComponentLoaded } from "../../../common/config/is_component_loaded"; +import { computeDeviceNameDisplay } from "../../../common/entity/compute_device_name"; import { computeDomain } from "../../../common/entity/compute_domain"; import { computeStateName } from "../../../common/entity/compute_state_name"; import { caseInsensitiveStringCompare } from "../../../common/string/compare"; import { groupBy } from "../../../common/util/group-by"; import { afterNextRender } from "../../../common/util/render-status"; +import "../../../components/ha-button-menu"; import "../../../components/ha-card"; import "../../../components/ha-icon-button"; import "../../../components/ha-icon-next"; import "../../../components/ha-list-item"; -import "../../../components/ha-button-menu"; import "../../../components/ha-tooltip"; import type { AreaRegistryEntry } from "../../../data/area_registry"; import { @@ -29,10 +30,7 @@ import { import type { AutomationEntity } from "../../../data/automation"; import { fullEntitiesContext } from "../../../data/context"; import type { DeviceRegistryEntry } from "../../../data/device_registry"; -import { - computeDeviceName, - sortDeviceRegistryByName, -} from "../../../data/device_registry"; +import { sortDeviceRegistryByName } from "../../../data/device_registry"; import type { EntityRegistryEntry } from "../../../data/entity_registry"; import { computeEntityRegistryName, @@ -166,7 +164,7 @@ class HaConfigAreaPage extends LitElement { // Pre-compute the entity and device names, so we can sort by them if (devices) { devices.forEach((entry) => { - entry.name = computeDeviceName(entry, this.hass); + entry.name = computeDeviceNameDisplay(entry, this.hass); }); sortDeviceRegistryByName(devices, this.hass.locale.language); } diff --git a/src/panels/config/dashboard/ha-config-updates.ts b/src/panels/config/dashboard/ha-config-updates.ts index 45829c1beb..6450785dd0 100644 --- a/src/panels/config/dashboard/ha-config-updates.ts +++ b/src/panels/config/dashboard/ha-config-updates.ts @@ -7,16 +7,14 @@ import { customElement, property, state } from "lit/decorators"; import { ifDefined } from "lit/directives/if-defined"; import memoizeOne from "memoize-one"; import { fireEvent } from "../../../common/dom/fire_event"; +import { computeDeviceNameDisplay } from "../../../common/entity/compute_device_name"; import "../../../components/entity/state-badge"; import "../../../components/ha-alert"; -import "../../../components/ha-spinner"; import "../../../components/ha-icon-next"; import "../../../components/ha-list-item"; +import "../../../components/ha-spinner"; import type { DeviceRegistryEntry } from "../../../data/device_registry"; -import { - computeDeviceName, - subscribeDeviceRegistry, -} from "../../../data/device_registry"; +import { subscribeDeviceRegistry } from "../../../data/device_registry"; import type { EntityRegistryEntry } from "../../../data/entity_registry"; import { subscribeEntityRegistry } from "../../../data/entity_registry"; import type { UpdateEntity } from "../../../data/update"; @@ -114,7 +112,7 @@ class HaConfigUpdates extends SubscribeMixin(LitElement) { : ""} ${deviceEntry - ? computeDeviceName(deviceEntry, this.hass) + ? computeDeviceNameDisplay(deviceEntry, this.hass) : entity.attributes.friendly_name} diff --git a/src/panels/config/devices/device-detail/ha-device-entities-card.ts b/src/panels/config/devices/device-detail/ha-device-entities-card.ts index 70441cca27..5415f29028 100644 --- a/src/panels/config/devices/device-detail/ha-device-entities-card.ts +++ b/src/panels/config/devices/device-detail/ha-device-entities-card.ts @@ -4,7 +4,7 @@ import { css, html, LitElement, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; import { classMap } from "lit/directives/class-map"; import { until } from "lit/directives/until"; -import { computeStateName } from "../../../../common/entity/compute_state_name"; +import { computeEntityName } from "../../../../common/entity/compute_entity_name"; import { stripPrefixFromEntityName } from "../../../../common/entity/strip_prefix_from_entity_name"; import "../../../../components/ha-card"; import "../../../../components/ha-icon"; @@ -15,17 +15,17 @@ import { entryIcon } from "../../../../data/icons"; import { showMoreInfoDialog } from "../../../../dialogs/more-info/show-ha-more-info-dialog"; import type { HomeAssistant } from "../../../../types"; import type { HuiErrorCard } from "../../../lovelace/cards/hui-error-card"; -import { createRowElement } from "../../../lovelace/create-element/create-row-element"; -import { addEntitiesToLovelaceView } from "../../../lovelace/editor/add-entities-to-view"; -import type { - LovelaceRowConfig, - LovelaceRow, -} from "../../../lovelace/entity-rows/types"; -import type { EntityRegistryStateEntry } from "../ha-config-device-page"; import { computeCards, computeSection, } from "../../../lovelace/common/generate-lovelace-config"; +import { createRowElement } from "../../../lovelace/create-element/create-row-element"; +import { addEntitiesToLovelaceView } from "../../../lovelace/editor/add-entities-to-view"; +import type { + LovelaceRow, + LovelaceRowConfig, +} from "../../../lovelace/entity-rows/types"; +import type { EntityRegistryStateEntry } from "../ha-config-device-page"; @customElement("ha-device-entities-card") export class HaDeviceEntitiesCard extends LitElement { @@ -171,18 +171,7 @@ export class HaDeviceEntitiesCard extends LitElement { element.hass = this.hass; const stateObj = this.hass.states[entry.entity_id]; - let name = entry.name - ? stripPrefixFromEntityName(entry.name, this.deviceName.toLowerCase()) - : entry.has_entity_name - ? entry.original_name || this.deviceName - : stripPrefixFromEntityName( - computeStateName(stateObj), - this.deviceName.toLowerCase() - ); - - if (!name) { - name = computeStateName(stateObj); - } + let name = computeEntityName(stateObj, this.hass) || this.deviceName; if (entry.hidden_by) { name += ` (${this.hass.localize( @@ -216,8 +205,7 @@ export class HaDeviceEntitiesCard extends LitElement {
${name - ? stripPrefixFromEntityName(name, this.deviceName.toLowerCase()) || - name + ? stripPrefixFromEntityName(name, this.deviceName) || name : entry.entity_id}
diff --git a/src/panels/config/devices/device-detail/ha-device-info-card.ts b/src/panels/config/devices/device-detail/ha-device-info-card.ts index 0e6eec2c98..03bbdad91e 100644 --- a/src/panels/config/devices/device-detail/ha-device-info-card.ts +++ b/src/panels/config/devices/device-detail/ha-device-info-card.ts @@ -1,10 +1,10 @@ import type { CSSResultGroup, TemplateResult } from "lit"; import { css, html, LitElement } from "lit"; import { customElement, property } from "lit/decorators"; +import { computeDeviceNameDisplay } from "../../../../common/entity/compute_device_name"; import { titleCase } from "../../../../common/string/title-case"; import "../../../../components/ha-card"; import type { DeviceRegistryEntry } from "../../../../data/device_registry"; -import { computeDeviceName } from "../../../../data/device_registry"; import { haStyle } from "../../../../resources/styles"; import type { HomeAssistant } from "../../../../types"; @@ -56,7 +56,9 @@ export class HaDeviceCard extends LitElement { ${this._computeDeviceName(this.device.via_device_id)}${this._computeDeviceNameDislay( + this.device.via_device_id + )}
@@ -118,10 +120,10 @@ export class HaDeviceCard extends LitElement { ); } - private _computeDeviceName(deviceId) { + private _computeDeviceNameDislay(deviceId) { const device = this.hass.devices[deviceId]; return device - ? computeDeviceName(device, this.hass) + ? computeDeviceNameDisplay(device, this.hass) : `<${this.hass.localize( "ui.panel.config.integrations.config_entry.unknown_via_device" )}>`; diff --git a/src/panels/config/devices/device-detail/ha-device-via-devices-card.ts b/src/panels/config/devices/device-detail/ha-device-via-devices-card.ts index 3df155ab37..598963951f 100644 --- a/src/panels/config/devices/device-detail/ha-device-via-devices-card.ts +++ b/src/panels/config/devices/device-detail/ha-device-via-devices-card.ts @@ -2,11 +2,11 @@ import "@material/mwc-list/mwc-list-item"; import { css, html, LitElement, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; import memoizeOne from "memoize-one"; +import { computeDeviceNameDisplay } from "../../../../common/entity/compute_device_name"; import { caseInsensitiveStringCompare } from "../../../../common/string/compare"; import "../../../../components/ha-card"; import "../../../../components/ha-icon-next"; import type { DeviceRegistryEntry } from "../../../../data/device_registry"; -import { computeDeviceName } from "../../../../data/device_registry"; import type { HomeAssistant } from "../../../../types"; const MAX_VISIBLE_VIA_DEVICES = 10; @@ -28,8 +28,8 @@ export class HaDeviceViaDevicesCard extends LitElement { .filter((device) => device.via_device_id === deviceId) .sort((d1, d2) => caseInsensitiveStringCompare( - computeDeviceName(d1, this.hass), - computeDeviceName(d2, this.hass), + computeDeviceNameDisplay(d1, this.hass), + computeDeviceNameDisplay(d2, this.hass), this.hass.locale.language ) ) @@ -56,7 +56,7 @@ export class HaDeviceViaDevicesCard extends LitElement { (viaDevice) => html` - ${computeDeviceName(viaDevice, this.hass)} + ${computeDeviceNameDisplay(viaDevice, this.hass)} diff --git a/src/panels/config/devices/device-detail/integration-elements/mqtt/dialog-mqtt-device-debug-info.ts b/src/panels/config/devices/device-detail/integration-elements/mqtt/dialog-mqtt-device-debug-info.ts index dd6d6a2c52..b531b5e2d3 100644 --- a/src/panels/config/devices/device-detail/integration-elements/mqtt/dialog-mqtt-device-debug-info.ts +++ b/src/panels/config/devices/device-detail/integration-elements/mqtt/dialog-mqtt-device-debug-info.ts @@ -2,12 +2,12 @@ import "@material/mwc-button/mwc-button"; import type { CSSResultGroup, TemplateResult } from "lit"; import { css, html, LitElement, nothing } from "lit"; import { customElement, state } from "lit/decorators"; +import { computeDeviceNameDisplay } from "../../../../../../common/entity/compute_device_name"; import { computeStateName } from "../../../../../../common/entity/compute_state_name"; import "../../../../../../components/ha-dialog"; import "../../../../../../components/ha-formfield"; import "../../../../../../components/ha-switch"; import type { HaSwitch } from "../../../../../../components/ha-switch"; -import { computeDeviceName } from "../../../../../../data/device_registry"; import type { MQTTDeviceDebugInfo } from "../../../../../../data/mqtt"; import { fetchMQTTDebugInfo } from "../../../../../../data/mqtt"; import { haStyleDialog } from "../../../../../../resources/styles"; @@ -48,7 +48,7 @@ class DialogMQTTDeviceDebugInfo extends LitElement { @closed=${this._close} .heading=${this.hass!.localize( "ui.dialogs.mqtt_device_debug_info.title", - { device: computeDeviceName(this._params.device, this.hass) } + { device: computeDeviceNameDisplay(this._params.device, this.hass) } )} >

diff --git a/src/panels/config/devices/device-registry-detail/dialog-device-registry-detail.ts b/src/panels/config/devices/device-registry-detail/dialog-device-registry-detail.ts index 7fc4add93f..7b912ef489 100644 --- a/src/panels/config/devices/device-registry-detail/dialog-device-registry-detail.ts +++ b/src/panels/config/devices/device-registry-detail/dialog-device-registry-detail.ts @@ -3,6 +3,7 @@ import type { CSSResultGroup } from "lit"; import { css, html, LitElement, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; import { fireEvent } from "../../../../common/dom/fire_event"; +import { computeDeviceNameDisplay } from "../../../../common/entity/compute_device_name"; import "../../../../components/ha-alert"; import "../../../../components/ha-area-picker"; import "../../../../components/ha-dialog"; @@ -10,7 +11,6 @@ import "../../../../components/ha-labels-picker"; import type { HaSwitch } from "../../../../components/ha-switch"; import "../../../../components/ha-textfield"; import type { DeviceRegistryEntry } from "../../../../data/device_registry"; -import { computeDeviceName } from "../../../../data/device_registry"; import { haStyle, haStyleDialog } from "../../../../resources/styles"; import type { HomeAssistant } from "../../../../types"; import type { DeviceRegistryDetailDialogParams } from "./show-dialog-device-registry-detail"; @@ -60,7 +60,7 @@ class DialogDeviceRegistryDetail extends LitElement {
${this._error diff --git a/src/panels/config/devices/ha-config-device-page.ts b/src/panels/config/devices/ha-config-device-page.ts index 33e78e7a9c..67317d0ae6 100644 --- a/src/panels/config/devices/ha-config-device-page.ts +++ b/src/panels/config/devices/ha-config-device-page.ts @@ -16,7 +16,9 @@ import { ifDefined } from "lit/directives/if-defined"; import memoizeOne from "memoize-one"; import { isComponentLoaded } from "../../../common/config/is_component_loaded"; import { ASSIST_ENTITIES, SENSOR_ENTITIES } from "../../../common/const"; +import { computeDeviceNameDisplay } from "../../../common/entity/compute_device_name"; import { computeDomain } from "../../../common/entity/compute_domain"; +import { computeEntityEntryName } from "../../../common/entity/compute_entity_name"; import { computeStateDomain } from "../../../common/entity/compute_state_domain"; import { computeStateName } from "../../../common/entity/compute_state_name"; import { stringCompare } from "../../../common/string/compare"; @@ -25,11 +27,12 @@ import { groupBy } from "../../../common/util/group-by"; import "../../../components/entity/ha-battery-icon"; import "../../../components/ha-alert"; import "../../../components/ha-button-menu"; +import "../../../components/ha-expansion-panel"; import "../../../components/ha-icon-button"; import "../../../components/ha-icon-next"; import "../../../components/ha-svg-icon"; -import "../../../components/ha-expansion-panel"; import "../../../components/ha-tooltip"; +import { assistSatelliteSupportsSetupFlow } from "../../../data/assist_satellite"; import { getSignedPath } from "../../../data/auth"; import type { ConfigEntry, @@ -42,7 +45,6 @@ import { import { fullEntitiesContext } from "../../../data/context"; import type { DeviceRegistryEntry } from "../../../data/device_registry"; import { - computeDeviceName, removeConfigEntryFromDevice, updateDeviceRegistryEntry, } from "../../../data/device_registry"; @@ -68,6 +70,7 @@ import { showAlertDialog, showConfirmationDialog, } from "../../../dialogs/generic/show-dialog-box"; +import { showVoiceAssistantSetupDialog } from "../../../dialogs/voice-assistant-setup/show-voice-assistant-setup-dialog"; import "../../../layouts/hass-error-screen"; import "../../../layouts/hass-subpage"; import { haStyle } from "../../../resources/styles"; @@ -83,8 +86,6 @@ import { loadDeviceRegistryDetailDialog, showDeviceRegistryDetailDialog, } from "./device-registry-detail/show-dialog-device-registry-detail"; -import { showVoiceAssistantSetupDialog } from "../../../dialogs/voice-assistant-setup/show-voice-assistant-setup-dialog"; -import { assistSatelliteSupportsSetupFlow } from "../../../data/assist_satellite"; export interface EntityRegistryStateEntry extends EntityRegistryEntry { stateName?: string | null; @@ -310,7 +311,7 @@ export class HaConfigDevicePage extends LitElement { `; } - const deviceName = computeDeviceName(device, this.hass); + const deviceName = computeDeviceNameDisplay(device, this.hass); const integrations = this._integrations( device, this.entries, @@ -1155,11 +1156,11 @@ export class HaConfigDevicePage extends LitElement { } private _computeEntityName(entity: EntityRegistryEntry) { - if (entity.name) { - return entity.name; - } - const entityState = this.hass.states[entity.entity_id]; - return entityState ? computeStateName(entityState) : null; + const device = this.hass.devices[this.deviceId]; + return ( + computeEntityEntryName(entity, this.hass) || + computeDeviceNameDisplay(device, this.hass) + ); } private _onImageLoad(ev) { diff --git a/src/panels/config/devices/ha-config-devices-dashboard.ts b/src/panels/config/devices/ha-config-devices-dashboard.ts index 0425b74f50..e2deb9c186 100644 --- a/src/panels/config/devices/ha-config-devices-dashboard.ts +++ b/src/panels/config/devices/ha-config-devices-dashboard.ts @@ -18,6 +18,7 @@ import { computeCssColor } from "../../../common/color/compute-color"; import { formatShortDateTime } from "../../../common/datetime/format_date_time"; import { storage } from "../../../common/decorators/storage"; import type { HASSDomEvent } from "../../../common/dom/fire_event"; +import { computeDeviceNameDisplay } from "../../../common/entity/compute_device_name"; import { computeStateDomain } from "../../../common/entity/compute_state_domain"; import { PROTOCOL_INTEGRATIONS, @@ -40,7 +41,6 @@ import "../../../components/entity/ha-battery-icon"; import "../../../components/ha-alert"; import "../../../components/ha-button-menu"; import "../../../components/ha-check-list-item"; -import "../../../components/ha-md-divider"; import "../../../components/ha-fab"; import "../../../components/ha-filter-devices"; import "../../../components/ha-filter-floor-areas"; @@ -48,6 +48,7 @@ import "../../../components/ha-filter-integrations"; import "../../../components/ha-filter-labels"; import "../../../components/ha-filter-states"; import "../../../components/ha-icon-button"; +import "../../../components/ha-md-divider"; import "../../../components/ha-md-menu-item"; import "../../../components/ha-sub-menu"; import { createAreaRegistryEntry } from "../../../data/area_registry"; @@ -63,10 +64,7 @@ import type { DeviceEntityLookup, DeviceRegistryEntry, } from "../../../data/device_registry"; -import { - computeDeviceName, - updateDeviceRegistryEntry, -} from "../../../data/device_registry"; +import { updateDeviceRegistryEntry } from "../../../data/device_registry"; import type { EntityRegistryEntry } from "../../../data/entity_registry"; import { findBatteryChargingEntity, @@ -426,7 +424,7 @@ export class HaConfigDeviceDashboard extends SubscribeMixin(LitElement) { return { ...device, - name: computeDeviceName( + name: computeDeviceNameDisplay( device, this.hass, deviceEntityLookup[device.id] diff --git a/src/panels/config/integrations/integration-panels/zwave_js/dialog-zwave_js-node-statistics.ts b/src/panels/config/integrations/integration-panels/zwave_js/dialog-zwave_js-node-statistics.ts index 5085f86eb1..bbe442323b 100644 --- a/src/panels/config/integrations/integration-panels/zwave_js/dialog-zwave_js-node-statistics.ts +++ b/src/panels/config/integrations/integration-panels/zwave_js/dialog-zwave_js-node-statistics.ts @@ -6,15 +6,13 @@ import type { CSSResultGroup, TemplateResult } from "lit"; import { css, html, LitElement, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; import { fireEvent } from "../../../../../common/dom/fire_event"; +import { computeDeviceNameDisplay } from "../../../../../common/entity/compute_device_name"; import { createCloseHeading } from "../../../../../components/ha-dialog"; import "../../../../../components/ha-expansion-panel"; import "../../../../../components/ha-help-tooltip"; import "../../../../../components/ha-svg-icon"; import type { DeviceRegistryEntry } from "../../../../../data/device_registry"; -import { - computeDeviceName, - subscribeDeviceRegistry, -} from "../../../../../data/device_registry"; +import { subscribeDeviceRegistry } from "../../../../../data/device_registry"; import type { ZWaveJSNodeStatisticsUpdatedMessage, ZWaveJSRouteStatistics, @@ -419,7 +417,10 @@ class DialogZWaveJSNodeStatistics extends LitElement { (devices: DeviceRegistryEntry[]) => { const devicesIdToName = {}; devices.forEach((device) => { - devicesIdToName[device.id] = computeDeviceName(device, this.hass); + devicesIdToName[device.id] = computeDeviceNameDisplay( + device, + this.hass + ); }); this._deviceIDsToName = devicesIdToName; } diff --git a/src/panels/config/integrations/integration-panels/zwave_js/dialog-zwave_js-rebuild-node-routes.ts b/src/panels/config/integrations/integration-panels/zwave_js/dialog-zwave_js-rebuild-node-routes.ts index be5f1342b8..19b12b06fd 100644 --- a/src/panels/config/integrations/integration-panels/zwave_js/dialog-zwave_js-rebuild-node-routes.ts +++ b/src/panels/config/integrations/integration-panels/zwave_js/dialog-zwave_js-rebuild-node-routes.ts @@ -4,10 +4,10 @@ import type { CSSResultGroup } from "lit"; import { css, html, LitElement, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; import { fireEvent } from "../../../../../common/dom/fire_event"; -import "../../../../../components/ha-spinner"; +import { computeDeviceNameDisplay } from "../../../../../common/entity/compute_device_name"; import { createCloseHeading } from "../../../../../components/ha-dialog"; +import "../../../../../components/ha-spinner"; import type { DeviceRegistryEntry } from "../../../../../data/device_registry"; -import { computeDeviceName } from "../../../../../data/device_registry"; import type { ZWaveJSNetwork } from "../../../../../data/zwave_js"; import { fetchZwaveNetworkStatus, @@ -68,9 +68,9 @@ class DialogZWaveJSRebuildNodeRoutes extends LitElement { ${this.hass.localize( "ui.panel.config.zwave_js.rebuild_node_routes.introduction", { - device: html`${computeDeviceName(this.device, this.hass!)}`, + device: html` + ${computeDeviceNameDisplay(this.device, this.hass!)} + `, } )}

@@ -102,9 +102,9 @@ class DialogZWaveJSRebuildNodeRoutes extends LitElement { ${this.hass.localize( "ui.panel.config.zwave_js.rebuild_node_routes.in_progress", { - device: html`${computeDeviceName(this.device, this.hass!)}`, + device: html` + ${computeDeviceNameDisplay(this.device, this.hass!)} + `, } )}

@@ -128,7 +128,10 @@ class DialogZWaveJSRebuildNodeRoutes extends LitElement { "ui.panel.config.zwave_js.rebuild_node_routes.rebuilding_routes_failed", { device: html`${computeDeviceName(this.device, this.hass!)}${computeDeviceNameDisplay( + this.device, + this.hass! + )}`, } )} @@ -161,9 +164,9 @@ class DialogZWaveJSRebuildNodeRoutes extends LitElement { ${this.hass.localize( "ui.panel.config.zwave_js.rebuild_node_routes.rebuilding_routes_complete", { - device: html`${computeDeviceName(this.device, this.hass!)}`, + device: html` + ${computeDeviceNameDisplay(this.device, this.hass!)} + `, } )}

diff --git a/src/panels/config/integrations/integration-panels/zwave_js/dialog-zwave_js-update-firmware-node.ts b/src/panels/config/integrations/integration-panels/zwave_js/dialog-zwave_js-update-firmware-node.ts index 99bd71b7b9..706d394b85 100644 --- a/src/panels/config/integrations/integration-panels/zwave_js/dialog-zwave_js-update-firmware-node.ts +++ b/src/panels/config/integrations/integration-panels/zwave_js/dialog-zwave_js-update-firmware-node.ts @@ -6,13 +6,13 @@ import type { CSSResultGroup } from "lit"; import { css, html, LitElement, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; import { fireEvent } from "../../../../../common/dom/fire_event"; +import { computeDeviceNameDisplay } from "../../../../../common/entity/compute_device_name"; import { createCloseHeading } from "../../../../../components/ha-dialog"; import "../../../../../components/ha-file-upload"; import "../../../../../components/ha-form/ha-form"; import type { HaFormSchema } from "../../../../../components/ha-form/types"; import "../../../../../components/ha-svg-icon"; import type { DeviceRegistryEntry } from "../../../../../data/device_registry"; -import { computeDeviceName } from "../../../../../data/device_registry"; import type { ZWaveJSControllerFirmwareUpdateFinishedMessage, ZWaveJSFirmwareUpdateProgressMessage, @@ -78,7 +78,7 @@ class DialogZWaveJSUpdateFirmwareNode extends LitElement { private _deviceName?: string; public showDialog(params: ZWaveJSUpdateFirmwareNodeDialogParams): void { - this._deviceName = computeDeviceName(params.device, this.hass!); + this._deviceName = computeDeviceNameDisplay(params.device, this.hass!); this.device = params.device; this._fetchData(); this._subscribeNodeStatus(); diff --git a/src/panels/config/integrations/integration-panels/zwave_js/zwave_js-node-config.ts b/src/panels/config/integrations/integration-panels/zwave_js/zwave_js-node-config.ts index 7d5d931a2b..25865a3fca 100644 --- a/src/panels/config/integrations/integration-panels/zwave_js/zwave_js-node-config.ts +++ b/src/panels/config/integrations/integration-panels/zwave_js/zwave_js-node-config.ts @@ -10,17 +10,18 @@ import type { CSSResultGroup, PropertyValues, TemplateResult } from "lit"; import { LitElement, css, html, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; import { classMap } from "lit/directives/class-map"; +import { fireEvent } from "../../../../../common/dom/fire_event"; +import { computeDeviceNameDisplay } from "../../../../../common/entity/compute_device_name"; import { groupBy } from "../../../../../common/util/group-by"; +import "../../../../../components/buttons/ha-progress-button"; +import type { HaProgressButton } from "../../../../../components/buttons/ha-progress-button"; import "../../../../../components/ha-alert"; import "../../../../../components/ha-card"; import "../../../../../components/ha-select"; +import "../../../../../components/ha-selector/ha-selector-boolean"; import "../../../../../components/ha-settings-row"; import "../../../../../components/ha-svg-icon"; import "../../../../../components/ha-textfield"; -import "../../../../../components/ha-selector/ha-selector-boolean"; -import "../../../../../components/buttons/ha-progress-button"; -import type { HaProgressButton } from "../../../../../components/buttons/ha-progress-button"; -import { computeDeviceName } from "../../../../../data/device_registry"; import type { ZWaveJSNodeCapabilities, ZWaveJSNodeConfigParam, @@ -35,6 +36,7 @@ import { invokeZWaveCCApi, setZwaveNodeConfigParameter, } from "../../../../../data/zwave_js"; +import { showConfirmationDialog } from "../../../../../dialogs/generic/show-dialog-box"; import "../../../../../layouts/hass-error-screen"; import "../../../../../layouts/hass-loading-screen"; import "../../../../../layouts/hass-tabs-subpage"; @@ -43,8 +45,6 @@ import type { HomeAssistant, Route } from "../../../../../types"; import "../../../ha-config-section"; import { configTabs } from "./zwave_js-config-router"; import "./zwave_js-custom-param"; -import { showConfirmationDialog } from "../../../../../dialogs/generic/show-dialog-box"; -import { fireEvent } from "../../../../../common/dom/fire_event"; const icons = { accepted: mdiCheckCircle, @@ -105,7 +105,9 @@ class ZWaveJSNodeConfig extends LitElement { const device = this.hass.devices[this.deviceId]; - const deviceName = device ? computeDeviceName(device, this.hass) : ""; + const deviceName = device + ? computeDeviceNameDisplay(device, this.hass) + : ""; return html` -

${computeDeviceName(device, this.hass)}

+

${computeDeviceNameDisplay(device, this.hass)}

${device.manufacturer} ${device.model}

` diff --git a/src/panels/config/scene/ha-scene-editor.ts b/src/panels/config/scene/ha-scene-editor.ts index 36f0d1fb8a..0d9c3d7f2c 100644 --- a/src/panels/config/scene/ha-scene-editor.ts +++ b/src/panels/config/scene/ha-scene-editor.ts @@ -1,6 +1,6 @@ -import type { ActionDetail } from "@material/mwc-list/mwc-list-foundation"; -import "@material/mwc-list/mwc-list"; import { consume } from "@lit-labs/context"; +import "@material/mwc-list/mwc-list"; +import type { ActionDetail } from "@material/mwc-list/mwc-list-foundation"; import { mdiCog, mdiContentDuplicate, @@ -21,19 +21,18 @@ import { customElement, property, state } from "lit/decorators"; import { classMap } from "lit/directives/class-map"; import memoizeOne from "memoize-one"; import { fireEvent } from "../../../common/dom/fire_event"; +import { computeDeviceNameDisplay } from "../../../common/entity/compute_device_name"; import { computeDomain } from "../../../common/entity/compute_domain"; import { computeStateName } from "../../../common/entity/compute_state_name"; import { navigate } from "../../../common/navigate"; import { computeRTL } from "../../../common/util/compute_rtl"; import { afterNextRender } from "../../../common/util/render-status"; -import { showMoreInfoDialog } from "../../../dialogs/more-info/show-ha-more-info-dialog"; -import { showAssignCategoryDialog } from "../category/show-dialog-assign-category"; import "../../../components/device/ha-device-picker"; import "../../../components/entity/ha-entities-picker"; -import "../../../components/ha-area-picker"; -import "../../../components/ha-button-menu"; import "../../../components/ha-alert"; +import "../../../components/ha-area-picker"; import "../../../components/ha-button"; +import "../../../components/ha-button-menu"; import "../../../components/ha-card"; import "../../../components/ha-fab"; import "../../../components/ha-icon-button"; @@ -41,8 +40,8 @@ import "../../../components/ha-icon-picker"; import "../../../components/ha-list-item"; import "../../../components/ha-svg-icon"; import "../../../components/ha-textfield"; +import { fullEntitiesContext } from "../../../data/context"; import type { DeviceRegistryEntry } from "../../../data/device_registry"; -import { computeDeviceName } from "../../../data/device_registry"; import type { EntityRegistryEntry } from "../../../data/entity_registry"; import { updateEntityRegistryEntry } from "../../../data/entity_registry"; import type { @@ -65,14 +64,15 @@ import { showAlertDialog, showConfirmationDialog, } from "../../../dialogs/generic/show-dialog-box"; +import { showMoreInfoDialog } from "../../../dialogs/more-info/show-ha-more-info-dialog"; import "../../../layouts/hass-subpage"; import { KeyboardShortcutMixin } from "../../../mixins/keyboard-shortcut-mixin"; +import { PreventUnsavedMixin } from "../../../mixins/prevent-unsaved-mixin"; import { haStyle } from "../../../resources/styles"; import type { HomeAssistant, Route } from "../../../types"; import { showToast } from "../../../util/toast"; +import { showAssignCategoryDialog } from "../category/show-dialog-assign-category"; import "../ha-config-section"; -import { PreventUnsavedMixin } from "../../../mixins/prevent-unsaved-mixin"; -import { fullEntitiesContext } from "../../../data/context"; interface DeviceEntities { id: string; @@ -175,7 +175,7 @@ export class HaSceneEditor extends PreventUnsavedMixin( const device = deviceLookup[deviceId]; const deviceEntities: string[] = deviceEntityLookup[deviceId] || []; outputDevices.push({ - name: computeDeviceName( + name: computeDeviceNameDisplay( device, this.hass, this._deviceEntityLookup[device.id] diff --git a/src/panels/config/voice-assistants/assist/ha-config-voice-assistants-assist-devices.ts b/src/panels/config/voice-assistants/assist/ha-config-voice-assistants-assist-devices.ts index 4283f930d9..f2c26c7795 100644 --- a/src/panels/config/voice-assistants/assist/ha-config-voice-assistants-assist-devices.ts +++ b/src/panels/config/voice-assistants/assist/ha-config-voice-assistants-assist-devices.ts @@ -2,6 +2,7 @@ import type { PropertyValues } from "lit"; import { LitElement, html } from "lit"; import { customElement, property, state } from "lit/decorators"; import memoizeOne from "memoize-one"; +import { computeDeviceNameDisplay } from "../../../../common/entity/compute_device_name"; import { navigate } from "../../../../common/navigate"; import type { LocalizeFunc } from "../../../../common/translations/localize"; import "../../../../components/data-table/ha-data-table"; @@ -14,7 +15,6 @@ import { listAssistDevices, listAssistPipelines, } from "../../../../data/assist_pipeline"; -import { computeDeviceName } from "../../../../data/device_registry"; import "../../../../layouts/hass-subpage"; import type { HomeAssistant } from "../../../../types"; @@ -87,7 +87,7 @@ class AssistDevicesPage extends LitElement { return { ...assistDevice, - name: device ? computeDeviceName(device, this.hass) : "", + name: device ? computeDeviceNameDisplay(device, this.hass) : "", pipeline: isPreferred ? localize("ui.components.pipeline-picker.preferred", { preferred: pipelineName, diff --git a/src/state-display/state-display.ts b/src/state-display/state-display.ts index cb4ded97f9..ba018f0dfc 100644 --- a/src/state-display/state-display.ts +++ b/src/state-display/state-display.ts @@ -1,7 +1,8 @@ import type { HassEntity } from "home-assistant-js-websocket"; import type { TemplateResult } from "lit"; -import { html, LitElement, nothing } from "lit"; +import { html, LitElement } from "lit"; import { customElement, property } from "lit/decorators"; +import { join } from "lit/directives/join"; import { ensureArray } from "../common/array/ensure-array"; import { computeStateDomain } from "../common/entity/compute_state_domain"; import { computeStateName } from "../common/entity/compute_state_name"; @@ -182,12 +183,7 @@ class StateDisplay extends LitElement { return html`${this.hass!.formatEntityState(stateObj)}`; } - return html` - ${values.map( - (value, index, array) => - html`${value}${index < array.length - 1 ? " ⸱ " : nothing}` - )} - `; + return join(values, " ⸱ "); } } diff --git a/src/state/connection-mixin.ts b/src/state/connection-mixin.ts index fa017031dc..87b6aa4f2a 100644 --- a/src/state/connection-mixin.ts +++ b/src/state/connection-mixin.ts @@ -247,6 +247,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,