From 7078ef52d44098b53c5c163b2b3d8e273ee07184 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Thu, 30 Oct 2025 16:58:52 +0100 Subject: [PATCH] Use entity naming in more cards (#27714) * Use entity naming in more cards * Migrate statistic card * Fix localize --- cast/src/launcher/layout/hc-cast.ts | 7 +-- .../device-detail/ha-device-entities-card.ts | 2 +- src/panels/lovelace/cards/hui-glance-card.ts | 8 ++- .../lovelace/cards/hui-history-graph-card.ts | 44 ++++++++++----- src/panels/lovelace/cards/hui-map-card.ts | 10 +--- .../lovelace/cards/hui-statistic-card.ts | 7 ++- .../cards/hui-statistics-graph-card.ts | 54 ++++++++++++------- src/panels/lovelace/cards/types.ts | 16 ++++-- .../common/generate-lovelace-config.ts | 44 ++++++++------- .../common/process-config-entities.ts | 6 ++- .../lovelace/components/hui-entity-editor.ts | 11 ++-- .../components/hui-generic-entity-row.ts | 12 +++-- .../card-editor/hui-dialog-create-card.ts | 6 +-- .../hui-generic-entity-row-editor.ts | 26 +++++---- .../config-elements/hui-glance-card-editor.ts | 16 ++++-- .../hui-history-graph-card-editor.ts | 8 ++- .../config-elements/hui-map-card-editor.ts | 16 +++--- .../hui-statistic-card-editor.ts | 9 +++- .../editor/structs/entities-struct.ts | 3 +- src/panels/lovelace/editor/types.ts | 5 +- .../unused-entities/hui-unused-entities.ts | 6 +-- .../entity-rows/hui-datetime-entity-row.ts | 16 ++++-- .../hui-input-datetime-entity-row.ts | 8 ++- .../hui-input-select-entity-row.ts | 10 +++- .../entity-rows/hui-input-text-entity-row.ts | 10 +++- .../entity-rows/hui-select-entity-row.ts | 10 +++- .../entity-rows/hui-text-entity-row.ts | 10 +++- .../entity-rows/hui-weather-entity-row.ts | 10 +++- src/panels/lovelace/entity-rows/types.ts | 3 +- .../lovelace/special-rows/hui-button-row.ts | 13 +++-- .../original-states-view-strategy.ts | 5 +- 31 files changed, 259 insertions(+), 152 deletions(-) diff --git a/cast/src/launcher/layout/hc-cast.ts b/cast/src/launcher/layout/hc-cast.ts index 9a15f58f44..2af8599fa2 100644 --- a/cast/src/launcher/layout/hc-cast.ts +++ b/cast/src/launcher/layout/hc-cast.ts @@ -16,9 +16,9 @@ import { } from "../../../../src/common/auth/token_storage"; import { atLeastVersion } from "../../../../src/common/config/version"; import { toggleAttribute } from "../../../../src/common/dom/toggle_attribute"; +import "../../../../src/components/ha-button"; import "../../../../src/components/ha-icon"; import "../../../../src/components/ha-list"; -import "../../../../src/components/ha-button"; import "../../../../src/components/ha-list-item"; import "../../../../src/components/ha-svg-icon"; import { @@ -28,7 +28,6 @@ import { import { isStrategyDashboard } from "../../../../src/data/lovelace/config/types"; import type { LovelaceViewConfig } from "../../../../src/data/lovelace/config/view"; import "../../../../src/layouts/hass-loading-screen"; -import { generateDefaultViewConfig } from "../../../../src/panels/lovelace/common/generate-lovelace-config"; import "./hc-layout"; @customElement("hc-cast") @@ -96,7 +95,9 @@ class HcCast extends LitElement { ${( this.lovelaceViews ?? [ - generateDefaultViewConfig({}, {}, {}, {}, () => ""), + { + title: "Home", + }, ] ).map( (view, idx) => html` 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 7c8d626e80..a20f29828d 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 @@ -228,7 +228,7 @@ export class HaDeviceEntitiesCard extends LitElement { addEntitiesToLovelaceView( this, this.hass, - computeCards(this.hass.states, entities, { + computeCards(this.hass, entities, { title: this.deviceName, }), computeSection(entities, { diff --git a/src/panels/lovelace/cards/hui-glance-card.ts b/src/panels/lovelace/cards/hui-glance-card.ts index 197c3c3e4b..f8aa4ece03 100644 --- a/src/panels/lovelace/cards/hui-glance-card.ts +++ b/src/panels/lovelace/cards/hui-glance-card.ts @@ -5,7 +5,6 @@ import { classMap } from "lit/directives/class-map"; import { ifDefined } from "lit/directives/if-defined"; import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element"; import { computeDomain } from "../../../common/entity/compute_domain"; -import { computeStateName } from "../../../common/entity/compute_state_name"; import "../../../components/entity/state-badge"; import "../../../components/ha-card"; import "../../../components/ha-icon"; @@ -19,6 +18,7 @@ import type { import { SENSOR_DEVICE_CLASS_TIMESTAMP } from "../../../data/sensor"; import type { HomeAssistant } from "../../../types"; import { actionHandler } from "../common/directives/action-handler-directive"; +import { computeLovelaceEntityName } from "../common/entity/compute-lovelace-entity-name"; import { findEntities } from "../common/find-entities"; import { handleAction } from "../common/handle-action"; import { hasAction, hasAnyAction } from "../common/has-action"; @@ -252,7 +252,11 @@ export class HuiGlanceCard extends LitElement implements LovelaceCard { `; } - const name = entityConf.name ?? computeStateName(stateObj); + const name = computeLovelaceEntityName( + this.hass!, + stateObj, + entityConf.name + ); return html`
{ - this._entityIds.push(entity.entity); - if (entity.name) { - this._names[entity.entity] = entity.name; - } - }); + this._entityIds = this._entities.map((entity) => entity.entity); this._hoursToShow = config.hours_to_show || DEFAULT_HOURS_TO_SHOW; this._config = config; + this._computeNames(); + } + + private _computeNames() { + if (!this.hass || !this._config) { + return; + } + this._names = {}; + this._entities.forEach((entity) => { + const stateObj = this.hass!.states[entity.entity]; + this._names[entity.entity] = stateObj + ? computeLovelaceEntityName(this.hass!, stateObj, entity.name) + : entity.entity; + }); + } + + public willUpdate(changedProps: PropertyValues) { + super.willUpdate(changedProps); + if (changedProps.has("hass")) { + this._computeNames(); + } } public connectedCallback() { diff --git a/src/panels/lovelace/cards/hui-map-card.ts b/src/panels/lovelace/cards/hui-map-card.ts index b6eaf4cfde..670b3977bc 100644 --- a/src/panels/lovelace/cards/hui-map-card.ts +++ b/src/panels/lovelace/cards/hui-map-card.ts @@ -35,20 +35,12 @@ import { hasConfigOrEntitiesChanged, } from "../common/has-changed"; import { processConfigEntities } from "../common/process-config-entities"; -import type { EntityConfig } from "../entity-rows/types"; import type { LovelaceCard, LovelaceGridOptions } from "../types"; -import type { MapCardConfig } from "./types"; +import type { MapCardConfig, MapEntityConfig } from "./types"; export const DEFAULT_HOURS_TO_SHOW = 0; export const DEFAULT_ZOOM = 14; -interface MapEntityConfig extends EntityConfig { - label_mode?: "state" | "attribute" | "name"; - attribute?: string; - unit?: string; - focus?: boolean; -} - interface GeoEntity { entity_id: string; label_mode?: "state" | "attribute" | "name" | "icon"; diff --git a/src/panels/lovelace/cards/hui-statistic-card.ts b/src/panels/lovelace/cards/hui-statistic-card.ts index d8fca526f7..31efdeab4f 100644 --- a/src/panels/lovelace/cards/hui-statistic-card.ts +++ b/src/panels/lovelace/cards/hui-statistic-card.ts @@ -20,14 +20,15 @@ import { } from "../../../data/recorder"; import type { HomeAssistant } from "../../../types"; import { computeCardSize } from "../common/compute-card-size"; +import { computeLovelaceEntityName } from "../common/entity/compute-lovelace-entity-name"; import { findEntities } from "../common/find-entities"; import { hasConfigOrEntityChanged } from "../common/has-changed"; import { createHeaderFooterElement } from "../create-element/create-header-footer-element"; import type { LovelaceCard, LovelaceCardEditor, - LovelaceHeaderFooter, LovelaceGridOptions, + LovelaceHeaderFooter, } from "../types"; import type { HuiErrorCard } from "./hui-error-card"; import type { EntityCardConfig, StatisticCardConfig } from "./types"; @@ -180,7 +181,9 @@ export class HuiStatisticCard extends LitElement implements LovelaceCard { const stateObj = this.hass.states[this._config.entity]; const name = - this._config.name || + (this._config.name + ? computeLovelaceEntityName(this.hass, stateObj, this._config.name) + : "") || getStatisticLabel(this.hass, this._config.entity, this._metadata); return html` diff --git a/src/panels/lovelace/cards/hui-statistics-graph-card.ts b/src/panels/lovelace/cards/hui-statistics-graph-card.ts index 7d493f1575..6c6c1b1fab 100644 --- a/src/panels/lovelace/cards/hui-statistics-graph-card.ts +++ b/src/panels/lovelace/cards/hui-statistics-graph-card.ts @@ -1,15 +1,11 @@ +import { differenceInDays, subHours } from "date-fns"; import type { HassEntity, UnsubscribeFunc } from "home-assistant-js-websocket"; -import { subHours, differenceInDays } from "date-fns"; import type { PropertyValues } from "lit"; import { css, html, LitElement, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; import { classMap } from "lit/directives/class-map"; import "../../../components/ha-card"; import { getEnergyDataCollection } from "../../../data/energy"; -import { - getSuggestedMax, - getSuggestedPeriod, -} from "./energy/common/energy-chart-options"; import type { Statistics, StatisticsMetaData, @@ -21,10 +17,16 @@ import { getStatisticMetadata, } from "../../../data/recorder"; import type { HomeAssistant } from "../../../types"; +import { computeLovelaceEntityName } from "../common/entity/compute-lovelace-entity-name"; import { findEntities } from "../common/find-entities"; import { hasConfigOrEntitiesChanged } from "../common/has-changed"; import { processConfigEntities } from "../common/process-config-entities"; +import type { EntityConfig } from "../entity-rows/types"; import type { LovelaceCard, LovelaceGridOptions } from "../types"; +import { + getSuggestedMax, + getSuggestedPeriod, +} from "./energy/common/energy-chart-options"; import type { StatisticsGraphCardConfig } from "./types"; export const DEFAULT_DAYS_TO_SHOW = 30; @@ -67,7 +69,9 @@ export class HuiStatisticsGraphCard extends LitElement implements LovelaceCard { @state() private _unit?: string; - private _entities: string[] = []; + private _entities: EntityConfig[] = []; + + private _entityIds: string[] = []; private _names: Record = {}; @@ -148,17 +152,10 @@ export class HuiStatisticsGraphCard extends LitElement implements LovelaceCard { throw new Error("You must include at least one entity"); } - const configEntities = config.entities + this._entities = config.entities ? processConfigEntities(config.entities, false) : []; - - this._entities = []; - configEntities.forEach((entity) => { - this._entities.push(entity.entity); - if (entity.name) { - this._names[entity.entity] = entity.name; - } - }); + this._entityIds = this._entities.map((ent) => ent.entity); if (typeof config.stat_types === "string") { this._statTypes = [config.stat_types]; @@ -168,6 +165,20 @@ export class HuiStatisticsGraphCard extends LitElement implements LovelaceCard { this._statTypes = config.stat_types; } this._config = config; + this._computeNames(); + } + + private _computeNames() { + if (!this.hass || !this._config) { + return; + } + this._names = {}; + this._entities.forEach((config) => { + const stateObj = this.hass!.states[config.entity]; + this._names[config.entity] = stateObj + ? computeLovelaceEntityName(this.hass!, stateObj, config.name) + : config.entity; + }); } protected shouldUpdate(changedProps: PropertyValues): boolean { @@ -209,6 +220,10 @@ export class HuiStatisticsGraphCard extends LitElement implements LovelaceCard { } } + if (changedProps.has("hass")) { + this._computeNames(); + } + if ( changedProps.has("_config") && oldConfig?.entities !== this._config.entities @@ -232,7 +247,7 @@ export class HuiStatisticsGraphCard extends LitElement implements LovelaceCard { clearInterval(this._interval); this._interval = 0; // block concurrent calls if (fetchMetadata) { - await this._getStatisticsMetaData(this._entities); + await this._getStatisticsMetaData(this._entityIds); } await this._getStatistics(); // statistics are created every hour @@ -344,7 +359,7 @@ export class HuiStatisticsGraphCard extends LitElement implements LovelaceCard { } } if (!unitClass && this._metadata) { - const metadata = this._metadata[this._entities[0]]; + const metadata = this._metadata[this._entityIds[0]]; unitClass = metadata?.unit_class; this._unit = unitClass ? getDisplayUnit(this.hass!, metadata.statistic_id, metadata) || @@ -356,14 +371,15 @@ export class HuiStatisticsGraphCard extends LitElement implements LovelaceCard { this.hass!, startDate, endDate, - this._entities, + this._entityIds, this._period, unitconfig, this._statTypes ); this._statistics = {}; - this._entities.forEach((id) => { + this._entities.forEach((entity) => { + const id = entity.entity; if (id in statistics) { this._statistics![id] = statistics[id]; } diff --git a/src/panels/lovelace/cards/types.ts b/src/panels/lovelace/cards/types.ts index 81bcbf0782..7e7739ed19 100644 --- a/src/panels/lovelace/cards/types.ts +++ b/src/panels/lovelace/cards/types.ts @@ -5,6 +5,7 @@ import type { EnergySourceByType } from "../../../data/energy"; import type { ActionConfig } from "../../../data/lovelace/config/action"; import type { LovelaceCardConfig } from "../../../data/lovelace/config/card"; import type { Statistic, StatisticType } from "../../../data/recorder"; +import type { MediaSelectorValue } from "../../../data/selector"; import type { TimeFormat } from "../../../data/translation"; import type { ForecastType } from "../../../data/weather"; import type { @@ -29,7 +30,6 @@ import type { import type { LovelaceHeaderFooterConfig } from "../header-footer/types"; import type { LovelaceHeadingBadgeConfig } from "../heading-badges/types"; import type { HomeSummary } from "../strategies/home/helpers/home-summaries"; -import type { MediaSelectorValue } from "../../../data/selector"; export type AlarmPanelCardConfigState = | "arm_away" @@ -348,7 +348,15 @@ export interface LogbookCardConfig extends LovelaceCardConfig { state_filter?: string[]; } -interface GeoLocationSourceConfig { +export interface MapEntityConfig extends EntityConfig { + label_mode?: "state" | "attribute" | "name"; + attribute?: string; + unit?: string; + focus?: boolean; + name?: string; +} + +export interface GeoLocationSourceConfig { source: string; label_mode?: "name" | "state" | "attribute" | "icon"; attribute?: string; @@ -363,7 +371,7 @@ export interface MapCardConfig extends LovelaceCardConfig { auto_fit?: boolean; fit_zones?: boolean; default_zoom?: number; - entities?: (EntityConfig | string)[]; + entities?: (MapEntityConfig | string)[]; hours_to_show?: number; geo_location_sources?: (GeoLocationSourceConfig | string)[]; dark_mode?: boolean; @@ -435,7 +443,7 @@ export interface StatisticsGraphCardConfig extends EnergyCardBaseConfig { } export interface StatisticCardConfig extends LovelaceCardConfig { - name?: string; + name?: string | EntityNameItem | EntityNameItem[]; entities: (EntityConfig | string)[]; period: | { diff --git a/src/panels/lovelace/common/generate-lovelace-config.ts b/src/panels/lovelace/common/generate-lovelace-config.ts index 16d348b731..0beee6292d 100644 --- a/src/panels/lovelace/common/generate-lovelace-config.ts +++ b/src/panels/lovelace/common/generate-lovelace-config.ts @@ -1,5 +1,5 @@ import type { HassEntities, HassEntity } from "home-assistant-js-websocket"; -import { SENSOR_ENTITIES, ASSIST_ENTITIES } from "../../../common/const"; +import { ASSIST_ENTITIES, SENSOR_ENTITIES } from "../../../common/const"; import { computeDomain } from "../../../common/entity/compute_domain"; import { computeStateDomain } from "../../../common/entity/compute_state_domain"; import { computeStateName } from "../../../common/entity/compute_state_name"; @@ -14,12 +14,14 @@ import type { GridSourceTypeEnergyPreference, } from "../../../data/energy"; import { domainToName } from "../../../data/integration"; +import type { LovelaceBadgeConfig } from "../../../data/lovelace/config/badge"; import type { LovelaceCardConfig } from "../../../data/lovelace/config/card"; import type { LovelaceSectionConfig } from "../../../data/lovelace/config/section"; import type { LovelaceViewConfig } from "../../../data/lovelace/config/view"; import { computeUserInitials } from "../../../data/user"; import type { HomeAssistant } from "../../../types"; import { HELPER_DOMAINS } from "../../config/helpers/const"; +import type { EntityBadgeConfig } from "../badges/types"; import type { AlarmPanelCardConfig, EntitiesCardConfig, @@ -31,8 +33,7 @@ import type { } from "../cards/types"; import type { EntityConfig } from "../entity-rows/types"; import type { ButtonsHeaderFooterConfig } from "../header-footer/types"; -import type { LovelaceBadgeConfig } from "../../../data/lovelace/config/badge"; -import type { EntityBadgeConfig } from "../badges/types"; +import { computeLovelaceEntityName } from "./entity/compute-lovelace-entity-name"; const HIDE_DOMAIN = new Set([ "ai_task", @@ -125,13 +126,13 @@ export const computeSection = ( }); export const computeCards = ( - states: HassEntities, + hass: HomeAssistant, entityIds: string[], entityCardOptions: Partial, renderFooterEntities = true ): LovelaceCardConfig[] => { const cards: LovelaceCardConfig[] = []; - + const states = hass.states; // For entity card const entitiesConf: (string | EntityConfig)[] = []; @@ -270,19 +271,23 @@ export const computeCards = ( ? states[a] ? computeStateName(states[a]) : "" - : a.name || "", + : states[a.entity] + ? computeLovelaceEntityName(hass, states[a.entity], a.name) + : "", typeof b === "string" ? states[b] ? computeStateName(states[b]) : "" - : b.name || "" + : states[b.entity] + ? computeLovelaceEntityName(hass, states[b.entity], b.name) + : "" ); }); // If we ended up with footer entities but no normal entities, // render the footer entities as normal entities. if (entitiesConf.length === 0 && footerEntities.length > 0) { - return computeCards(states, entityIds, entityCardOptions, false); + return computeCards(hass, entityIds, entityCardOptions, false); } if (entitiesConf.length > 0 || footerEntities.length > 0) { @@ -360,14 +365,14 @@ const computeDefaultViewStates = ( }; export const generateViewConfig = ( - localize: LocalizeFunc, + hass: HomeAssistant, path: string, title: string | undefined, icon: string | undefined, entities: HassEntities ): LovelaceViewConfig => { const ungroupedEntitites: Record = {}; - + const { localize } = hass; // Organize ungrouped entities in ungrouped things for (const entityId of Object.keys(entities)) { const state = entities[entityId]; @@ -470,7 +475,7 @@ export const generateViewConfig = ( .forEach((domain) => { cards.push( ...computeCards( - entities, + hass, ungroupedEntitites[domain].sort((a, b) => stringCompare( computeStateName(entities[a]), @@ -498,16 +503,17 @@ export const generateViewConfig = ( }; export const generateDefaultViewConfig = ( - areaEntries: HomeAssistant["areas"], - deviceEntries: HomeAssistant["devices"], - entityEntries: HomeAssistant["entities"], - entities: HassEntities, + hass: HomeAssistant, localize: LocalizeFunc, energyPrefs?: EnergyPreferences, areasPrefs?: AreasDisplayValue, hideEntitiesWithoutAreas?: boolean, hideEnergy?: boolean ): LovelaceViewConfig => { + const entities = hass.states; + const areaEntries = hass.areas; + const deviceEntries = hass.devices; + const entityEntries = hass.entities; const states = computeDefaultViewStates(entities, entityEntries); const path = "default_view"; const title = "Home"; @@ -549,7 +555,7 @@ export const generateDefaultViewConfig = ( for (const groupEntity of splittedByGroups.groups) { groupCards.push( - ...computeCards(entities, groupEntity.attributes.entity_id, { + ...computeCards(hass, groupEntity.attributes.entity_id, { title: computeStateName(groupEntity), show_header_toggle: groupEntity.attributes.control !== "hidden", }) @@ -557,7 +563,7 @@ export const generateDefaultViewConfig = ( } const config = generateViewConfig( - localize, + hass, path, title, icon, @@ -575,7 +581,7 @@ export const generateDefaultViewConfig = ( const area = areaEntries[areaId]; areaCards.push( ...computeCards( - entities, + hass, areaEntities.map((entity) => entity.entity_id), { title: area.name, @@ -601,7 +607,7 @@ export const generateDefaultViewConfig = ( const device = deviceEntries[deviceId]; deviceCards.push( ...computeCards( - entities, + hass, deviceEntities.map((entity) => entity.entity_id), { title: diff --git a/src/panels/lovelace/common/process-config-entities.ts b/src/panels/lovelace/common/process-config-entities.ts index c51e28ebf8..f9ff1d85c8 100644 --- a/src/panels/lovelace/common/process-config-entities.ts +++ b/src/panels/lovelace/common/process-config-entities.ts @@ -2,8 +2,12 @@ import { isValidEntityId } from "../../../common/entity/valid_entity_id"; import type { EntityConfig, LovelaceRowConfig } from "../entity-rows/types"; +interface BaseEntityConfig { + type: string; + entity: string; +} export const processConfigEntities = < - T extends EntityConfig | LovelaceRowConfig, + T extends BaseEntityConfig | LovelaceRowConfig, >( entities: (T | string)[], checkEntityId = true diff --git a/src/panels/lovelace/components/hui-entity-editor.ts b/src/panels/lovelace/components/hui-entity-editor.ts index 78feddd713..36a0415d47 100644 --- a/src/panels/lovelace/components/hui-entity-editor.ts +++ b/src/panels/lovelace/components/hui-entity-editor.ts @@ -45,14 +45,13 @@ export class HuiEntityEditor extends LitElement { this.hass.devices ); - const name = this.hass.formatEntityName( - stateObj, - useDeviceName ? { type: "device" } : { type: "entity" } - ); - const isRTL = computeRTL(this.hass); - const primary = item.name || name || item.entity; + const primary = + this.hass.formatEntityName( + stateObj, + useDeviceName ? { type: "device" } : { type: "entity" } + ) || item.entity; const secondary = this.hass.formatEntityName( stateObj, diff --git a/src/panels/lovelace/components/hui-generic-entity-row.ts b/src/panels/lovelace/components/hui-generic-entity-row.ts index ec9c22a1cc..ace6a1549c 100644 --- a/src/panels/lovelace/components/hui-generic-entity-row.ts +++ b/src/panels/lovelace/components/hui-generic-entity-row.ts @@ -4,19 +4,19 @@ import { customElement, property } from "lit/decorators"; import { classMap } from "lit/directives/class-map"; import { ifDefined } from "lit/directives/if-defined"; import { DOMAINS_INPUT_ROW } from "../../../common/const"; +import { stopPropagation } from "../../../common/dom/stop_propagation"; import { toggleAttribute } from "../../../common/dom/toggle_attribute"; import { computeDomain } from "../../../common/entity/compute_domain"; -import { computeStateName } from "../../../common/entity/compute_state_name"; import "../../../components/entity/state-badge"; import "../../../components/ha-relative-time"; import type { ActionHandlerEvent } from "../../../data/lovelace/action_handler"; import type { HomeAssistant } from "../../../types"; import type { EntitiesCardEntityConfig } from "../cards/types"; import { actionHandler } from "../common/directives/action-handler-directive"; +import { computeLovelaceEntityName } from "../common/entity/compute-lovelace-entity-name"; import { handleAction } from "../common/handle-action"; import { hasAction, hasAnyAction } from "../common/has-action"; import { createEntityNotFoundWarning } from "./hui-warning"; -import { stopPropagation } from "../../../common/dom/stop_propagation"; @customElement("hui-generic-entity-row") export class HuiGenericEntityRow extends LitElement { @@ -59,7 +59,11 @@ export class HuiGenericEntityRow extends LitElement { const pointer = hasAnyAction(this.config); const hasSecondary = this.secondaryText || this.config.secondary_info; - const name = this.config.name ?? computeStateName(stateObj); + const name = computeLovelaceEntityName( + this.hass, + stateObj, + this.config.name + ); return html`
- ${this.config.name || computeStateName(stateObj)} + ${name} ${hasSecondary ? html`
diff --git a/src/panels/lovelace/editor/card-editor/hui-dialog-create-card.ts b/src/panels/lovelace/editor/card-editor/hui-dialog-create-card.ts index c22c6f2d9a..e5f14f6ade 100644 --- a/src/panels/lovelace/editor/card-editor/hui-dialog-create-card.ts +++ b/src/panels/lovelace/editor/card-editor/hui-dialog-create-card.ts @@ -296,11 +296,7 @@ export class HuiCreateDialogCard } private _suggestCards(): void { - const cardConfig = computeCards( - this.hass.states, - this._selectedEntities, - {} - ); + const cardConfig = computeCards(this.hass, this._selectedEntities, {}); let sectionOptions: Partial = {}; diff --git a/src/panels/lovelace/editor/config-elements/hui-generic-entity-row-editor.ts b/src/panels/lovelace/editor/config-elements/hui-generic-entity-row-editor.ts index ec1f009526..79b9d95999 100644 --- a/src/panels/lovelace/editor/config-elements/hui-generic-entity-row-editor.ts +++ b/src/panels/lovelace/editor/config-elements/hui-generic-entity-row-editor.ts @@ -46,20 +46,18 @@ export class HuiGenericEntityRowEditor return [ { name: "entity", required: true, selector: { entity: {} } }, { - type: "grid", - name: "", - schema: [ - { name: "name", selector: { text: {} } }, - { - name: "icon", - selector: { - icon: {}, - }, - context: { - icon_entity: "entity", - }, - }, - ], + name: "name", + selector: { entity_name: {} }, + context: { entity: "entity" }, + }, + { + name: "icon", + selector: { + icon: {}, + }, + context: { + icon_entity: "entity", + }, }, { name: "secondary_info", diff --git a/src/panels/lovelace/editor/config-elements/hui-glance-card-editor.ts b/src/panels/lovelace/editor/config-elements/hui-glance-card-editor.ts index fedab67069..641ed7a96a 100644 --- a/src/panels/lovelace/editor/config-elements/hui-glance-card-editor.ts +++ b/src/panels/lovelace/editor/config-elements/hui-glance-card-editor.ts @@ -11,20 +11,20 @@ import { string, union, } from "superstruct"; +import type { HASSDomEvent } from "../../../../common/dom/fire_event"; import { fireEvent } from "../../../../common/dom/fire_event"; import "../../../../components/ha-form/ha-form"; -import "../hui-sub-element-editor"; -import type { EditDetailElementEvent, SubElementEditorConfig } from "../types"; -import type { HASSDomEvent } from "../../../../common/dom/fire_event"; import type { SchemaUnion } from "../../../../components/ha-form/types"; import type { HomeAssistant } from "../../../../types"; import type { ConfigEntity, GlanceCardConfig } from "../../cards/types"; import "../../components/hui-entity-editor"; +import type { EntityConfig } from "../../entity-rows/types"; import type { LovelaceCardEditor } from "../../types"; +import "../hui-sub-element-editor"; import { processEditorEntities } from "../process-editor-entities"; import { baseLovelaceCardConfig } from "../structs/base-card-struct"; import { entitiesConfigStruct } from "../structs/entities-struct"; -import type { EntityConfig } from "../../entity-rows/types"; +import type { EditDetailElementEvent, SubElementEditorConfig } from "../types"; const cardConfigStruct = assign( baseLovelaceCardConfig, @@ -42,11 +42,17 @@ const cardConfigStruct = assign( const SUB_SCHEMA = [ { name: "entity", selector: { entity: {} }, required: true }, + { + name: "name", + selector: { entity_name: {} }, + context: { + entity: "entity", + }, + }, { type: "grid", name: "", schema: [ - { name: "name", selector: { text: {} } }, { name: "icon", selector: { diff --git a/src/panels/lovelace/editor/config-elements/hui-history-graph-card-editor.ts b/src/panels/lovelace/editor/config-elements/hui-history-graph-card-editor.ts index b7f4e73e29..38134d8c65 100644 --- a/src/panels/lovelace/editor/config-elements/hui-history-graph-card-editor.ts +++ b/src/panels/lovelace/editor/config-elements/hui-history-graph-card-editor.ts @@ -45,7 +45,13 @@ const cardConfigStruct = assign( const SUB_SCHEMA = [ { name: "entity", selector: { entity: {} }, required: true }, - { name: "name", selector: { text: {} } }, + { + name: "name", + selector: { entity_name: {} }, + context: { + entity: "entity", + }, + }, ] as const; @customElement("hui-history-graph-card-editor") diff --git a/src/panels/lovelace/editor/config-elements/hui-map-card-editor.ts b/src/panels/lovelace/editor/config-elements/hui-map-card-editor.ts index 365daf51ed..724018419d 100644 --- a/src/panels/lovelace/editor/config-elements/hui-map-card-editor.ts +++ b/src/panels/lovelace/editor/config-elements/hui-map-card-editor.ts @@ -2,6 +2,7 @@ import { mdiPalette } from "@mdi/js"; import type { CSSResultGroup } from "lit"; import { css, html, LitElement, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; +import memoizeOne from "memoize-one"; import { array, assert, @@ -13,19 +14,19 @@ import { string, union, } from "superstruct"; -import memoizeOne from "memoize-one"; import { fireEvent } from "../../../../common/dom/fire_event"; -import { hasLocation } from "../../../../common/entity/has_location"; import { computeDomain } from "../../../../common/entity/compute_domain"; +import { hasLocation } from "../../../../common/entity/has_location"; +import type { LocalizeFunc } from "../../../../common/translations/localize"; import "../../../../components/ha-form/ha-form"; import type { SchemaUnion } from "../../../../components/ha-form/types"; -import type { SelectSelector } from "../../../../data/selector"; import "../../../../components/ha-formfield"; -import "../../../../components/ha-switch"; import "../../../../components/ha-selector/ha-selector-select"; +import "../../../../components/ha-switch"; +import type { SelectSelector } from "../../../../data/selector"; import type { HomeAssistant, ValueChangedEvent } from "../../../../types"; import { DEFAULT_HOURS_TO_SHOW, DEFAULT_ZOOM } from "../../cards/hui-map-card"; -import type { MapCardConfig } from "../../cards/types"; +import type { MapCardConfig, MapEntityConfig } from "../../cards/types"; import "../../components/hui-entity-editor"; import type { EntityConfig } from "../../entity-rows/types"; import type { LovelaceCardEditor } from "../../types"; @@ -33,7 +34,6 @@ import { processEditorEntities } from "../process-editor-entities"; import { baseLovelaceCardConfig } from "../structs/base-card-struct"; import type { EntitiesEditorEvent } from "../types"; import { configElementStyle } from "./config-elements-style"; -import type { LocalizeFunc } from "../../../../common/translations/localize"; export const mapEntitiesConfigStruct = union([ object({ @@ -223,7 +223,9 @@ export class HuiMapCardEditor extends LitElement implements LovelaceCardEditor { }) ); - private _entitiesValueChanged(ev: EntitiesEditorEvent): void { + private _entitiesValueChanged( + ev: EntitiesEditorEvent + ): void { if (ev.detail && ev.detail.entities) { this._config = { ...this._config!, entities: ev.detail.entities }; diff --git a/src/panels/lovelace/editor/config-elements/hui-statistic-card-editor.ts b/src/panels/lovelace/editor/config-elements/hui-statistic-card-editor.ts index 53368424b7..2b50c1bcbe 100644 --- a/src/panels/lovelace/editor/config-elements/hui-statistic-card-editor.ts +++ b/src/panels/lovelace/editor/config-elements/hui-statistic-card-editor.ts @@ -21,12 +21,13 @@ import type { StatisticCardConfig } from "../../cards/types"; import { headerFooterConfigStructs } from "../../header-footer/structs"; import type { LovelaceCardEditor } from "../../types"; import { baseLovelaceCardConfig } from "../structs/base-card-struct"; +import { entityNameStruct } from "../structs/entity-name-struct"; const cardConfigStruct = assign( baseLovelaceCardConfig, object({ entity: optional(string()), - name: optional(string()), + name: optional(entityNameStruct), icon: optional(string()), unit: optional(string()), stat_type: optional(string()), @@ -144,11 +145,15 @@ export class HuiStatisticCardEditor } : { object: {} }, }, + { + name: "name", + selector: { entity_name: {} }, + context: { entity: "entity" }, + }, { type: "grid", name: "", schema: [ - { name: "name", selector: { text: {} } }, { name: "icon", selector: { diff --git a/src/panels/lovelace/editor/structs/entities-struct.ts b/src/panels/lovelace/editor/structs/entities-struct.ts index c0bb7aa4a7..20aa15bf88 100644 --- a/src/panels/lovelace/editor/structs/entities-struct.ts +++ b/src/panels/lovelace/editor/structs/entities-struct.ts @@ -4,11 +4,12 @@ import { actionConfigStruct, actionConfigStructConfirmation, } from "./action-struct"; +import { entityNameStruct } from "./entity-name-struct"; export const entitiesConfigStruct = union([ object({ entity: string(), - name: optional(string()), + name: optional(entityNameStruct), icon: optional(string()), image: optional(string()), secondary_info: optional(string()), diff --git a/src/panels/lovelace/editor/types.ts b/src/panels/lovelace/editor/types.ts index 30878a3540..1df3144445 100644 --- a/src/panels/lovelace/editor/types.ts +++ b/src/panels/lovelace/editor/types.ts @@ -43,9 +43,10 @@ export interface ConfigError { message: string; } -export interface EntitiesEditorEvent extends CustomEvent { +export interface EntitiesEditorEvent + extends CustomEvent { detail: { - entities?: EntityConfig[]; + entities?: T[]; item?: any; }; target: EventTarget | null; diff --git a/src/panels/lovelace/editor/unused-entities/hui-unused-entities.ts b/src/panels/lovelace/editor/unused-entities/hui-unused-entities.ts index 74af3ef545..ea36acfdf4 100644 --- a/src/panels/lovelace/editor/unused-entities/hui-unused-entities.ts +++ b/src/panels/lovelace/editor/unused-entities/hui-unused-entities.ts @@ -111,11 +111,7 @@ export class HuiUnusedEntities extends LitElement { } private _addToLovelaceView(): void { - const cardConfig = computeCards( - this.hass.states, - this._selectedEntities, - {} - ); + const cardConfig = computeCards(this.hass, this._selectedEntities, {}); const sectionConfig = computeSection(this._selectedEntities, {}); if (this.lovelace.config.views.length === 1) { diff --git a/src/panels/lovelace/entity-rows/hui-datetime-entity-row.ts b/src/panels/lovelace/entity-rows/hui-datetime-entity-row.ts index 20581cd63e..981f78b2e4 100644 --- a/src/panels/lovelace/entity-rows/hui-datetime-entity-row.ts +++ b/src/panels/lovelace/entity-rows/hui-datetime-entity-row.ts @@ -1,17 +1,17 @@ +import { format } from "date-fns"; import type { PropertyValues, TemplateResult } from "lit"; import { css, html, LitElement, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; import "../../../components/ha-date-input"; -import { format } from "date-fns"; -import { isUnavailableState, UNAVAILABLE } from "../../../data/entity"; +import "../../../components/ha-time-input"; import { setDateTimeValue } from "../../../data/datetime"; +import { isUnavailableState, UNAVAILABLE } from "../../../data/entity"; import type { HomeAssistant } from "../../../types"; import { hasConfigOrEntityChanged } from "../common/has-changed"; import "../components/hui-generic-entity-row"; import { createEntityNotFoundWarning } from "../components/hui-warning"; import type { EntityConfig, LovelaceRow } from "./types"; -import "../../../components/ha-time-input"; -import { computeStateName } from "../../../common/entity/compute_state_name"; +import { computeLovelaceEntityName } from "../common/entity/compute-lovelace-entity-name"; @customElement("hui-datetime-entity-row") class HuiInputDatetimeEntityRow extends LitElement implements LovelaceRow { @@ -53,6 +53,12 @@ class HuiInputDatetimeEntityRow extends LitElement implements LovelaceRow { const time = dateObj ? format(dateObj, "HH:mm:ss") : undefined; const date = dateObj ? format(dateObj, "yyyy-MM-dd") : undefined; + const name = computeLovelaceEntityName( + this.hass!, + stateObj, + this._config.name + ); + return html`
diff --git a/src/panels/lovelace/entity-rows/types.ts b/src/panels/lovelace/entity-rows/types.ts index a5ca00a3fc..dbf0248267 100644 --- a/src/panels/lovelace/entity-rows/types.ts +++ b/src/panels/lovelace/entity-rows/types.ts @@ -1,3 +1,4 @@ +import type { EntityNameItem } from "../../../common/entity/compute_entity_name_display"; import type { ActionConfig, ConfirmationRestrictionConfig, @@ -10,7 +11,7 @@ import type { TimestampRenderingFormat } from "../components/types"; export interface EntityConfig { entity: string; type?: string; - name?: string; + name?: string | EntityNameItem | EntityNameItem[]; icon?: string; image?: string; } diff --git a/src/panels/lovelace/special-rows/hui-button-row.ts b/src/panels/lovelace/special-rows/hui-button-row.ts index e268249eb3..b59741aa7e 100644 --- a/src/panels/lovelace/special-rows/hui-button-row.ts +++ b/src/panels/lovelace/special-rows/hui-button-row.ts @@ -2,15 +2,15 @@ import { css, html, LitElement, nothing } from "lit"; import { customElement, state } from "lit/decorators"; import { DOMAINS_TOGGLE } from "../../../common/const"; import { computeDomain } from "../../../common/entity/compute_domain"; -import { computeStateName } from "../../../common/entity/compute_state_name"; -import "../../../components/ha-state-icon"; import "../../../components/ha-button"; +import "../../../components/ha-state-icon"; +import type { ActionHandlerEvent } from "../../../data/lovelace/action_handler"; import type { HomeAssistant } from "../../../types"; import { actionHandler } from "../common/directives/action-handler-directive"; +import { computeLovelaceEntityName } from "../common/entity/compute-lovelace-entity-name"; import { handleAction } from "../common/handle-action"; import { hasAction } from "../common/has-action"; import type { ButtonRowConfig, LovelaceRow } from "../entity-rows/types"; -import type { ActionHandlerEvent } from "../../../data/lovelace/action_handler"; @customElement("hui-button-row") export class HuiButtonRow extends LitElement implements LovelaceRow { @@ -49,8 +49,11 @@ export class HuiButtonRow extends LitElement implements LovelaceRow { ? this.hass.states[this._config.entity] : undefined; - const name = - this._config.name ?? (stateObj ? computeStateName(stateObj) : ""); + const name = computeLovelaceEntityName( + this.hass!, + stateObj, + this._config.name + ); return html`