Compare commits

...
Author SHA1 Message Date
Bram KragtenandClaude Opus 5 66820826fa Resolve statistic labels with structured entity names
getStatisticLabel resolved names through computeStateName, so every
statistic-shaped label — the energy dashboard cards, the sankey cards,
hui-statistic-card, chart series names and the energy configuration
dialogs — showed the flat friendly_name attribute while the rest of the
UI composed names from the device, area and floor.

It now asks hass.formatEntityName for the default composition, which
also makes the entity branch of computeEnergyLabel redundant.

The metadata fallback stays: external statistics have no entity to
resolve a name against.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-15 15:21:43 +02:00
3 changed files with 95 additions and 9 deletions
-7
View File
@@ -21,7 +21,6 @@ import {
} from "../common/datetime/calc_date";
import type { DateRange } from "../common/datetime/calc_date_range";
import { calcDateRange } from "../common/datetime/calc_date_range";
import { DEFAULT_ENTITY_NAME } from "../common/entity/compute_entity_name_display";
import { formatNumber } from "../common/number/format_number";
import { normalizeValueBySIPrefix } from "../common/number/normalize-by-si-prefix";
import { groupBy } from "../common/util/group-by";
@@ -328,12 +327,6 @@ export const computeEnergyLabel = (
return customName;
}
const stateObj = hass.states[statisticId];
if (stateObj) {
return hass.formatEntityName(stateObj, DEFAULT_ENTITY_NAME);
}
return getStatisticLabel(hass, statisticId, statisticsMetaData);
};
+3 -2
View File
@@ -1,5 +1,5 @@
import type { Connection } from "home-assistant-js-websocket";
import { computeStateName } from "../common/entity/compute_state_name";
import { DEFAULT_ENTITY_NAME } from "../common/entity/compute_entity_name_display";
import type { HaDurationData } from "../components/ha-duration-input";
import type { HomeAssistant } from "../types";
import { firstWeekday } from "../common/datetime/first_weekday";
@@ -348,8 +348,9 @@ export const getStatisticLabel = (
): string => {
const entity = hass.states[statisticsId];
if (entity) {
return computeStateName(entity);
return hass.formatEntityName(entity, DEFAULT_ENTITY_NAME);
}
// External statistics have no entity to resolve a name against.
return statisticsMetaData?.name || statisticsId;
};
@@ -0,0 +1,92 @@
import { describe, expect, it } from "vitest";
import type { StatisticsMetaData } from "../../src/data/recorder";
import { getStatisticLabel, StatisticMeanType } from "../../src/data/recorder";
import {
mockArea,
mockDevice,
mockEntity,
} from "../common/entity/context/context-mock";
import { createMockEntityState, createMockHass } from "../fixtures/hass";
const metadata = (
statistic_id: string,
name: string | null
): StatisticsMetaData => ({
statistic_id,
name,
source: "recorder",
statistics_unit_of_measurement: "kWh",
has_sum: true,
mean_type: StatisticMeanType.NONE,
unit_class: "energy",
});
// The friendly name deliberately differs from the composed name, so the
// assertions fail if the label is taken from the attribute again.
const hassWithRegistry = () =>
createMockHass(
{
"sensor.dishwasher_energy": createMockEntityState(
"sensor.dishwasher_energy",
"12",
{ friendly_name: "Smart plug 3 energy" }
),
},
{
entities: {
"sensor.dishwasher_energy": mockEntity({
entity_id: "sensor.dishwasher_energy",
device_id: "device_1",
name: "Energy",
}),
},
devices: {
device_1: mockDevice({
id: "device_1",
name: "Dishwasher",
area_id: "kitchen",
}),
},
areas: { kitchen: mockArea({ area_id: "kitchen", name: "Kitchen" }) },
}
);
describe("getStatisticLabel", () => {
it("composes the name from the registry rather than friendly_name", () => {
expect(
getStatisticLabel(
hassWithRegistry(),
"sensor.dishwasher_energy",
metadata("sensor.dishwasher_energy", "Recorder name")
)
).toBe("Dishwasher Energy");
});
// External statistics have no entity, so there is no registry context to
// compose a name from and the recorder metadata is all we have.
it("uses the metadata name when there is no entity", () => {
expect(
getStatisticLabel(
createMockHass(),
"energy:solar_production",
metadata("energy:solar_production", "Solar production")
)
).toBe("Solar production");
});
it("falls back to the statistic id when metadata has no name", () => {
expect(
getStatisticLabel(
createMockHass(),
"energy:solar_production",
metadata("energy:solar_production", null)
)
).toBe("energy:solar_production");
});
it("falls back to the statistic id when there is no metadata", () => {
expect(
getStatisticLabel(createMockHass(), "energy:solar_production", undefined)
).toBe("energy:solar_production");
});
});