mirror of
https://github.com/home-assistant/frontend.git
synced 2026-09-25 14:33:23 +00:00
Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
97192fe316 | ||
|
|
00f2c71231 | ||
|
|
66820826fa |
@@ -19,37 +19,16 @@ import {
|
||||
getEntityContext,
|
||||
getEntityEntryContext,
|
||||
} from "./context/get_entity_context";
|
||||
import type { EntityNameItem, EntityNameOptions } from "./entity_name_config";
|
||||
|
||||
const DEFAULT_SEPARATOR = " ";
|
||||
|
||||
export const DEFAULT_ENTITY_NAME = [
|
||||
{ type: "parent_device" },
|
||||
{ type: "device" },
|
||||
{ type: "entity" },
|
||||
] satisfies EntityNameItem[];
|
||||
|
||||
export const ENTITY_NAME_TYPES = [
|
||||
"floor",
|
||||
"area",
|
||||
"parent_device",
|
||||
"device",
|
||||
"entity",
|
||||
] as const;
|
||||
|
||||
export type EntityNameType = (typeof ENTITY_NAME_TYPES)[number];
|
||||
|
||||
export type EntityNameItem =
|
||||
| {
|
||||
type: EntityNameType;
|
||||
}
|
||||
| {
|
||||
type: "text";
|
||||
text: string;
|
||||
};
|
||||
|
||||
export interface EntityNameOptions {
|
||||
separator?: string;
|
||||
}
|
||||
export { DEFAULT_ENTITY_NAME, ENTITY_NAME_TYPES } from "./entity_name_config";
|
||||
export type {
|
||||
EntityNameItem,
|
||||
EntityNameOptions,
|
||||
EntityNameType,
|
||||
} from "./entity_name_config";
|
||||
|
||||
// Joins items that need no entity context. Returns undefined as soon as one
|
||||
// item references the registries (device, area, ...).
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Shape of a composed entity name, kept free of runtime dependencies so that
|
||||
* modules in the core entry chunk can name an entity without pulling in the
|
||||
* registry-aware formatters from `compute_entity_name_display`.
|
||||
*/
|
||||
|
||||
export const ENTITY_NAME_TYPES = [
|
||||
"floor",
|
||||
"area",
|
||||
"parent_device",
|
||||
"device",
|
||||
"entity",
|
||||
] as const;
|
||||
|
||||
export type EntityNameType = (typeof ENTITY_NAME_TYPES)[number];
|
||||
|
||||
export type EntityNameItem =
|
||||
| {
|
||||
type: EntityNameType;
|
||||
}
|
||||
| {
|
||||
type: "text";
|
||||
text: string;
|
||||
};
|
||||
|
||||
export interface EntityNameOptions {
|
||||
separator?: string;
|
||||
}
|
||||
|
||||
export const DEFAULT_ENTITY_NAME = [
|
||||
{ type: "parent_device" },
|
||||
{ type: "device" },
|
||||
{ type: "entity" },
|
||||
] satisfies EntityNameItem[];
|
||||
@@ -125,7 +125,23 @@ export class StatisticsChart extends LitElement {
|
||||
private _yAxisFractionDigits = 1;
|
||||
|
||||
protected shouldUpdate(changedProps: PropertyValues<this>): boolean {
|
||||
return changedProps.size > 1 || !changedProps.has("hass");
|
||||
return (
|
||||
changedProps.size > 1 ||
|
||||
!changedProps.has("hass") ||
|
||||
this._entityNamesChanged(changedProps)
|
||||
);
|
||||
}
|
||||
|
||||
// Series names are resolved once and cached in _chartData, so a hass update
|
||||
// that only replaces the formatters has to regenerate them. The formatters are
|
||||
// swapped as a set whenever the registries change, which is what renames a
|
||||
// series.
|
||||
private _entityNamesChanged(changedProps: PropertyValues): boolean {
|
||||
if (!changedProps.has("hass")) {
|
||||
return false;
|
||||
}
|
||||
const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
|
||||
return !!oldHass && oldHass.formatEntityName !== this.hass.formatEntityName;
|
||||
}
|
||||
|
||||
public willUpdate(changedProps: PropertyValues) {
|
||||
@@ -135,7 +151,8 @@ export class StatisticsChart extends LitElement {
|
||||
changedProps.has("chartType") ||
|
||||
changedProps.has("hideLegend") ||
|
||||
changedProps.has("_hiddenStats") ||
|
||||
changedProps.has("names")
|
||||
changedProps.has("names") ||
|
||||
this._entityNamesChanged(changedProps)
|
||||
) {
|
||||
this._generateData();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
|
||||
@@ -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/entity_name_config";
|
||||
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");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user