1
mirror of https://github.com/home-assistant/frontend synced 2024-09-22 18:30:59 +02:00

Use entity registry display api (#15549)

This commit is contained in:
Bram Kragten 2023-02-23 16:30:13 +01:00 committed by GitHub
parent 7173b30716
commit f69ae84cc6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 315 additions and 154 deletions

View File

@ -136,7 +136,7 @@ export class DemoAutomationDescribeAction extends LitElement {
<div class="action">
<span>
${this._action
? describeAction(this.hass, this._action)
? describeAction(this.hass, [], this._action)
: "<invalid YAML>"}
</span>
<ha-yaml-editor
@ -149,7 +149,7 @@ export class DemoAutomationDescribeAction extends LitElement {
${ACTIONS.map(
(conf) => html`
<div class="action">
<span>${describeAction(this.hass, conf as any)}</span>
<span>${describeAction(this.hass, [], conf as any)}</span>
<pre>${dump(conf)}</pre>
</div>
`

View File

@ -1,5 +1,5 @@
import { HassEntity } from "home-assistant-js-websocket";
import { EntityRegistryEntry } from "../../data/entity_registry";
import { EntityRegistryDisplayEntry } from "../../data/entity_registry";
import { HomeAssistant } from "../../types";
import { LocalizeFunc } from "../translations/localize";
import { computeDomain } from "./compute_domain";
@ -15,7 +15,7 @@ export const computeAttributeValueDisplay = (
const attributeValue =
value !== undefined ? value : stateObj.attributes[attribute];
const domain = computeDomain(entityId);
const entity = entities[entityId] as EntityRegistryEntry | undefined;
const entity = entities[entityId] as EntityRegistryDisplayEntry | undefined;
const translationKey = entity?.translation_key;
return (
@ -38,7 +38,7 @@ export const computeAttributeNameDisplay = (
): string => {
const entityId = stateObj.entity_id;
const domain = computeDomain(entityId);
const entity = entities[entityId] as EntityRegistryEntry | undefined;
const entity = entities[entityId] as EntityRegistryDisplayEntry | undefined;
const translationKey = entity?.translation_key;
return (

View File

@ -1,6 +1,6 @@
import { HassEntity } from "home-assistant-js-websocket";
import { UNAVAILABLE, UNKNOWN } from "../../data/entity";
import { EntityRegistryEntry } from "../../data/entity_registry";
import { EntityRegistryDisplayEntry } from "../../data/entity_registry";
import { FrontendLocaleData } from "../../data/translation";
import {
updateIsInstallingFromAttributes,
@ -49,7 +49,7 @@ export const computeStateDisplayFromEntityAttributes = (
return localize(`state.default.${state}`);
}
const entity = entities[entityId] as EntityRegistryEntry | undefined;
const entity = entities[entityId] as EntityRegistryDisplayEntry | undefined;
// Entities with a `unit_of_measurement` or `state_class` are numeric values and should use `formatNumber`
if (isNumericFromAttributes(attributes)) {

View File

@ -2,7 +2,7 @@ import {
HassEntity,
HassEntityAttributeBase,
} from "home-assistant-js-websocket";
import { EntityRegistryEntry } from "../../data/entity_registry";
import { EntityRegistryDisplayEntry } from "../../data/entity_registry";
import { FrontendLocaleData, NumberFormat } from "../../data/translation";
import { round } from "./round";
@ -92,11 +92,9 @@ export const formatNumber = (
*/
export const getNumberFormatOptions = (
entityState: HassEntity,
entity?: EntityRegistryEntry
entity?: EntityRegistryDisplayEntry
): Intl.NumberFormatOptions | undefined => {
const precision =
entity?.options?.sensor?.display_precision ??
entity?.options?.sensor?.suggested_display_precision;
const precision = entity?.display_precision;
if (precision != null) {
return {
maximumFractionDigits: precision,

View File

@ -22,7 +22,7 @@ import {
isNumericState,
} from "../../common/number/format_number";
import { isUnavailableState, UNAVAILABLE, UNKNOWN } from "../../data/entity";
import { EntityRegistryEntry } from "../../data/entity_registry";
import { EntityRegistryDisplayEntry } from "../../data/entity_registry";
import { timerTimeRemaining } from "../../data/timer";
import { HomeAssistant } from "../../types";
import "../ha-label-badge";
@ -160,7 +160,7 @@ export class HaStateLabelBadge extends LitElement {
private _computeValue(
domain: string,
entityState: HassEntity,
entry?: EntityRegistryEntry
entry?: EntityRegistryDisplayEntry
) {
switch (domain) {
case "alarm_control_panel":
@ -200,7 +200,7 @@ export class HaStateLabelBadge extends LitElement {
private _computeShowIcon(
domain: string,
entityState: HassEntity,
entry?: EntityRegistryEntry
entry?: EntityRegistryDisplayEntry
): boolean {
if (entityState.state === UNAVAILABLE) {
return false;

View File

@ -12,10 +12,11 @@ import {
createAreaRegistryEntry,
} from "../data/area_registry";
import {
DeviceEntityLookup,
DeviceEntityDisplayLookup,
DeviceRegistryEntry,
getDeviceEntityDisplayLookup,
} from "../data/device_registry";
import { EntityRegistryEntry } from "../data/entity_registry";
import { EntityRegistryDisplayEntry } from "../data/entity_registry";
import {
showAlertDialog,
showPromptDialog,
@ -113,7 +114,7 @@ export class HaAreaPicker extends LitElement {
(
areas: AreaRegistryEntry[],
devices: DeviceRegistryEntry[],
entities: EntityRegistryEntry[],
entities: EntityRegistryDisplayEntry[],
includeDomains: this["includeDomains"],
excludeDomains: this["excludeDomains"],
includeDeviceClasses: this["includeDeviceClasses"],
@ -133,9 +134,9 @@ export class HaAreaPicker extends LitElement {
];
}
const deviceEntityLookup: DeviceEntityLookup = {};
let deviceEntityLookup: DeviceEntityDisplayLookup = {};
let inputDevices: DeviceRegistryEntry[] | undefined;
let inputEntities: EntityRegistryEntry[] | undefined;
let inputEntities: EntityRegistryDisplayEntry[] | undefined;
if (
includeDomains ||
@ -143,15 +144,7 @@ export class HaAreaPicker extends LitElement {
includeDeviceClasses ||
entityFilter
) {
for (const entity of entities) {
if (!entity.device_id) {
continue;
}
if (!(entity.device_id in deviceEntityLookup)) {
deviceEntityLookup[entity.device_id] = [];
}
deviceEntityLookup[entity.device_id].push(entity);
}
deviceEntityLookup = getDeviceEntityDisplayLookup(entities);
}
inputDevices = devices;
inputEntities = entities.filter((entity) => entity.area_id);

View File

@ -1,14 +1,10 @@
import { HassEntity, UnsubscribeFunc } from "home-assistant-js-websocket";
import { HassEntity } from "home-assistant-js-websocket";
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
import { customElement, property, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import { ensureArray } from "../../common/array/ensure-array";
import type { DeviceRegistryEntry } from "../../data/device_registry";
import { getDeviceIntegrationLookup } from "../../data/device_registry";
import {
EntityRegistryEntry,
subscribeEntityRegistry,
} from "../../data/entity_registry";
import {
EntitySources,
fetchEntitySourcesWithCache,
@ -18,13 +14,12 @@ import {
filterSelectorDevices,
filterSelectorEntities,
} from "../../data/selector";
import { SubscribeMixin } from "../../mixins/subscribe-mixin";
import { HomeAssistant } from "../../types";
import "../ha-area-picker";
import "../ha-areas-picker";
@customElement("ha-selector-area")
export class HaAreaSelector extends SubscribeMixin(LitElement) {
export class HaAreaSelector extends LitElement {
@property() public hass!: HomeAssistant;
@property() public selector!: AreaSelector;
@ -41,18 +36,8 @@ export class HaAreaSelector extends SubscribeMixin(LitElement) {
@state() private _entitySources?: EntitySources;
@state() private _entities?: EntityRegistryEntry[];
private _deviceIntegrationLookup = memoizeOne(getDeviceIntegrationLookup);
public hassSubscribe(): UnsubscribeFunc[] {
return [
subscribeEntityRegistry(this.hass.connection!, (entities) => {
this._entities = entities.filter((entity) => entity.device_id !== null);
}),
];
}
private _hasIntegration(selector: AreaSelector) {
return (
(selector.area?.entity &&
@ -127,10 +112,12 @@ export class HaAreaSelector extends SubscribeMixin(LitElement) {
return true;
}
const deviceIntegrations =
this._entitySources && this._entities
? this._deviceIntegrationLookup(this._entitySources, this._entities)
: undefined;
const deviceIntegrations = this._entitySources
? this._deviceIntegrationLookup(
this._entitySources,
Object.values(this.hass.entities)
)
: undefined;
return ensureArray(this.selector.area.device).some((filter) =>
filterSelectorDevices(filter, device, deviceIntegrations)

View File

@ -2,12 +2,11 @@ import { html, LitElement, PropertyValues } from "lit";
import { customElement, property } from "lit/decorators";
import { fireEvent } from "../../common/dom/fire_event";
import { AttributeSelector } from "../../data/selector";
import { SubscribeMixin } from "../../mixins/subscribe-mixin";
import { HomeAssistant } from "../../types";
import "../entity/ha-entity-attribute-picker";
@customElement("ha-selector-attribute")
export class HaSelectorAttribute extends SubscribeMixin(LitElement) {
export class HaSelectorAttribute extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public selector!: AttributeSelector;

View File

@ -1,14 +1,10 @@
import { HassEntity, UnsubscribeFunc } from "home-assistant-js-websocket";
import { HassEntity } from "home-assistant-js-websocket";
import { html, LitElement } from "lit";
import { customElement, property, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import { ensureArray } from "../../common/array/ensure-array";
import type { DeviceRegistryEntry } from "../../data/device_registry";
import { getDeviceIntegrationLookup } from "../../data/device_registry";
import {
EntityRegistryEntry,
subscribeEntityRegistry,
} from "../../data/entity_registry";
import {
EntitySources,
fetchEntitySourcesWithCache,
@ -18,21 +14,18 @@ import {
filterSelectorDevices,
filterSelectorEntities,
} from "../../data/selector";
import { SubscribeMixin } from "../../mixins/subscribe-mixin";
import type { HomeAssistant } from "../../types";
import "../device/ha-device-picker";
import "../device/ha-devices-picker";
@customElement("ha-selector-device")
export class HaDeviceSelector extends SubscribeMixin(LitElement) {
export class HaDeviceSelector extends LitElement {
@property() public hass!: HomeAssistant;
@property() public selector!: DeviceSelector;
@state() private _entitySources?: EntitySources;
@state() private _entities?: EntityRegistryEntry[];
@property() public value?: any;
@property() public label?: string;
@ -45,14 +38,6 @@ export class HaDeviceSelector extends SubscribeMixin(LitElement) {
private _deviceIntegrationLookup = memoizeOne(getDeviceIntegrationLookup);
public hassSubscribe(): UnsubscribeFunc[] {
return [
subscribeEntityRegistry(this.hass.connection!, (entities) => {
this._entities = entities.filter((entity) => entity.device_id !== null);
}),
];
}
private _hasIntegration(selector: DeviceSelector) {
return (
(selector.device?.filter &&
@ -118,10 +103,12 @@ export class HaDeviceSelector extends SubscribeMixin(LitElement) {
if (!this.selector.device?.filter) {
return true;
}
const deviceIntegrations =
this._entitySources && this._entities
? this._deviceIntegrationLookup(this._entitySources, this._entities)
: undefined;
const deviceIntegrations = this._entitySources
? this._deviceIntegrationLookup(
this._entitySources,
Object.values(this.hass.entities)
)
: undefined;
return ensureArray(this.selector.device.filter).some((filter) =>
filterSelectorDevices(filter, device, deviceIntegrations)

View File

@ -1,6 +1,7 @@
// @ts-ignore
import chipStyles from "@material/chips/dist/mdc.chips.min.css";
import "@material/mwc-button/mwc-button";
import "@material/mwc-menu/mwc-menu-surface";
import {
mdiClose,
mdiDevices,
@ -9,13 +10,14 @@ import {
mdiUnfoldMoreVertical,
} from "@mdi/js";
import "@polymer/paper-tooltip/paper-tooltip";
import { ComboBoxLightOpenedChangedEvent } from "@vaadin/combo-box/vaadin-combo-box-light";
import { HassEntity, HassServiceTarget } from "home-assistant-js-websocket";
import { css, CSSResultGroup, html, LitElement, unsafeCSS } from "lit";
import { customElement, property, query, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import { ComboBoxLightOpenedChangedEvent } from "@vaadin/combo-box/vaadin-combo-box-light";
import { ensureArray } from "../common/array/ensure-array";
import { fireEvent } from "../common/dom/fire_event";
import { stopPropagation } from "../common/dom/stop_propagation";
import { computeDomain } from "../common/entity/compute_domain";
import { computeStateName } from "../common/entity/compute_state_name";
import { isValidEntityId } from "../common/entity/valid_entity_id";
@ -23,7 +25,7 @@ import {
computeDeviceName,
DeviceRegistryEntry,
} from "../data/device_registry";
import { EntityRegistryEntry } from "../data/entity_registry";
import { EntityRegistryDisplayEntry } from "../data/entity_registry";
import { HomeAssistant } from "../types";
import "./device/ha-device-picker";
import type { HaDevicePickerDeviceFilterFunc } from "./device/ha-device-picker";
@ -33,8 +35,6 @@ import "./ha-area-picker";
import "./ha-icon-button";
import "./ha-input-helper-text";
import "./ha-svg-icon";
import { stopPropagation } from "../common/dom/stop_propagation";
import "@material/mwc-menu/mwc-menu-surface";
@customElement("ha-target-picker")
export class HaTargetPicker extends LitElement {
@ -551,7 +551,7 @@ export class HaTargetPicker extends LitElement {
return true;
}
private _entityRegMeetsFilter(entity: EntityRegistryEntry): boolean {
private _entityRegMeetsFilter(entity: EntityRegistryDisplayEntry): boolean {
if (entity.entity_category) {
return false;
}

View File

@ -6,6 +6,7 @@ import {
mdiProgressWrench,
mdiRecordCircleOutline,
} from "@mdi/js";
import { UnsubscribeFunc } from "home-assistant-js-websocket";
import {
css,
CSSResultGroup,
@ -14,12 +15,16 @@ import {
PropertyValues,
TemplateResult,
} from "lit";
import { customElement, property } from "lit/decorators";
import { customElement, property, state } from "lit/decorators";
import { ifDefined } from "lit/directives/if-defined";
import { formatDateTimeWithSeconds } from "../../common/datetime/format_date_time";
import { relativeTime } from "../../common/datetime/relative_time";
import { fireEvent } from "../../common/dom/fire_event";
import { toggleAttribute } from "../../common/dom/toggle_attribute";
import {
EntityRegistryEntry,
subscribeEntityRegistry,
} from "../../data/entity_registry";
import { LogbookEntry } from "../../data/logbook";
import {
ChooseAction,
@ -193,6 +198,7 @@ class ActionRenderer {
constructor(
private hass: HomeAssistant,
private entityReg: EntityRegistryEntry[],
private entries: TemplateResult[],
private trace: AutomationTraceExtended,
private logbookRenderer: LogbookRenderer,
@ -298,7 +304,7 @@ class ActionRenderer {
this._renderEntry(
path,
describeAction(this.hass, data, actionType),
describeAction(this.hass, this.entityReg, data, actionType),
undefined,
data.enabled === false
);
@ -441,7 +447,9 @@ class ActionRenderer {
) as RepeatAction;
const disabled = repeatConfig.enabled === false;
const name = repeatConfig.alias || describeAction(this.hass, repeatConfig);
const name =
repeatConfig.alias ||
describeAction(this.hass, this.entityReg, repeatConfig);
this._renderEntry(repeatPath, name, undefined, disabled);
@ -577,6 +585,16 @@ export class HaAutomationTracer extends LitElement {
@property({ type: Boolean }) public allowPick = false;
@state() private _entityReg: EntityRegistryEntry[] = [];
public hassSubscribe(): UnsubscribeFunc[] {
return [
subscribeEntityRegistry(this.hass.connection!, (entities) => {
this._entityReg = entities;
}),
];
}
protected render(): TemplateResult {
if (!this.trace) {
return html``;
@ -592,6 +610,7 @@ export class HaAutomationTracer extends LitElement {
);
const actionRenderer = new ActionRenderer(
this.hass,
this._entityReg,
entries,
this.trace,
logbookRenderer,

View File

@ -4,7 +4,10 @@ import { computeStateName } from "../common/entity/compute_state_name";
import { caseInsensitiveStringCompare } from "../common/string/compare";
import { debounce } from "../common/util/debounce";
import type { HomeAssistant } from "../types";
import type { EntityRegistryEntry } from "./entity_registry";
import type {
EntityRegistryDisplayEntry,
EntityRegistryEntry,
} from "./entity_registry";
import type { EntitySources } from "./entity_sources";
export interface DeviceRegistryEntry {
@ -25,6 +28,10 @@ export interface DeviceRegistryEntry {
configuration_url: string | null;
}
export interface DeviceEntityDisplayLookup {
[deviceId: string]: EntityRegistryDisplayEntry[];
}
export interface DeviceEntityLookup {
[deviceId: string]: EntityRegistryEntry[];
}
@ -147,9 +154,25 @@ export const getDeviceEntityLookup = (
return deviceEntityLookup;
};
export const getDeviceEntityDisplayLookup = (
entities: EntityRegistryDisplayEntry[]
): DeviceEntityDisplayLookup => {
const deviceEntityLookup: DeviceEntityDisplayLookup = {};
for (const entity of entities) {
if (!entity.device_id) {
continue;
}
if (!(entity.device_id in deviceEntityLookup)) {
deviceEntityLookup[entity.device_id] = [];
}
deviceEntityLookup[entity.device_id].push(entity);
}
return deviceEntityLookup;
};
export const getDeviceIntegrationLookup = (
entitySources: EntitySources,
entities: EntityRegistryEntry[]
entities: EntityRegistryDisplayEntry[]
): Record<string, string[]> => {
const deviceIntegrations: Record<string, string[]> = {};

View File

@ -6,6 +6,35 @@ import { caseInsensitiveStringCompare } from "../common/string/compare";
import { debounce } from "../common/util/debounce";
import { HomeAssistant } from "../types";
type entityCategory = "config" | "diagnostic";
export interface EntityRegistryDisplayEntry {
entity_id: string;
name?: string;
device_id?: string;
area_id?: string;
hidden?: boolean;
entity_category?: entityCategory;
translation_key?: string;
platform?: string;
display_precision?: number;
}
interface EntityRegistryDisplayEntryResponse {
entities: {
ei: string;
di?: string;
ai?: string;
ec?: number;
en?: string;
pl?: string;
tk?: string;
hb?: boolean;
dp?: number;
}[];
entity_categories: Record<number, entityCategory>;
}
export interface EntityRegistryEntry {
id: string;
entity_id: string;
@ -17,7 +46,7 @@ export interface EntityRegistryEntry {
area_id: string | null;
disabled_by: "user" | "device" | "integration" | "config_entry" | null;
hidden_by: Exclude<EntityRegistryEntry["disabled_by"], "config_entry">;
entity_category: "config" | "diagnostic" | null;
entity_category: entityCategory | null;
has_entity_name: boolean;
original_name?: string;
unique_id: string;
@ -154,6 +183,11 @@ export const fetchEntityRegistry = (conn: Connection) =>
type: "config/entity_registry/list",
});
export const fetchEntityRegistryDisplay = (conn: Connection) =>
conn.sendMessagePromise<EntityRegistryDisplayEntryResponse>({
type: "config/entity_registry/list_for_display",
});
const subscribeEntityRegistryUpdates = (
conn: Connection,
store: Store<EntityRegistryEntry[]>
@ -182,6 +216,34 @@ export const subscribeEntityRegistry = (
onChange
);
const subscribeEntityRegistryDisplayUpdates = (
conn: Connection,
store: Store<EntityRegistryDisplayEntryResponse>
) =>
conn.subscribeEvents(
debounce(
() =>
fetchEntityRegistryDisplay(conn).then((entities) =>
store.setState(entities, true)
),
500,
true
),
"entity_registry_updated"
);
export const subscribeEntityRegistryDisplay = (
conn: Connection,
onChange: (entities: EntityRegistryDisplayEntryResponse) => void
) =>
createCollection<EntityRegistryDisplayEntryResponse>(
"_entityRegistryDisplay",
fetchEntityRegistryDisplay,
subscribeEntityRegistryDisplayUpdates,
conn,
onChange
);
export const sortEntityRegistryByName = (
entries: EntityRegistryEntry[],
language: string
@ -190,10 +252,20 @@ export const sortEntityRegistryByName = (
caseInsensitiveStringCompare(entry1.name || "", entry2.name || "", language)
);
export const entityRegistryByEntityId = memoizeOne(
(entries: EntityRegistryEntry[]) => {
const entities: Record<string, EntityRegistryEntry> = {};
for (const entity of entries) {
entities[entity.entity_id] = entity;
}
return entities;
}
);
export const entityRegistryById = memoizeOne(
(entries: HomeAssistant["entities"]) => {
const entities: HomeAssistant["entities"] = {};
for (const entity of Object.values(entries)) {
(entries: EntityRegistryEntry[]) => {
const entities: Record<string, EntityRegistryEntry> = {};
for (const entity of entries) {
entities[entity.id] = entity;
}
return entities;

View File

@ -11,6 +11,7 @@ import { computeDeviceName } from "./device_registry";
import {
computeEntityRegistryName,
entityRegistryById,
EntityRegistryEntry,
} from "./entity_registry";
import { domainToName } from "./integration";
import {
@ -33,6 +34,7 @@ import {
export const describeAction = <T extends ActionType>(
hass: HomeAssistant,
entityRegistry: EntityRegistryEntry[],
action: ActionTypes[T],
actionType?: T,
ignoreAlias = false
@ -91,7 +93,7 @@ export const describeAction = <T extends ActionType>(
targets.push(targetThing);
}
} else {
const entityReg = entityRegistryById(hass.entities)[targetThing];
const entityReg = entityRegistryById(entityRegistry)[targetThing];
if (entityReg) {
targets.push(
computeEntityRegistryName(hass, entityReg) || targetThing

View File

@ -25,7 +25,11 @@ import "../../components/ha-icon-button";
import "../../components/ha-icon-button-prev";
import "../../components/ha-list-item";
import "../../components/ha-related-items";
import { EntityRegistryEntry } from "../../data/entity_registry";
import {
EntityRegistryEntry,
ExtEntityRegistryEntry,
getExtendedEntityRegistryEntry,
} from "../../data/entity_registry";
import { haStyleDialog } from "../../resources/styles";
import "../../state-summary/state-card-content";
import { HomeAssistant } from "../../types";
@ -77,6 +81,8 @@ export class MoreInfoDialog extends LitElement {
@state() private _childView?: ChildView;
@state() private _entry?: ExtEntityRegistryEntry;
public showDialog(params: MoreInfoDialogParams) {
this._entityId = params.entityId;
if (!this._entityId) {
@ -86,10 +92,22 @@ export class MoreInfoDialog extends LitElement {
this._currView = params.view || "info";
this._childView = undefined;
this.large = false;
this._loadEntityRegistryEntry();
}
private async _loadEntityRegistryEntry() {
if (!this._entityId) {
return;
}
this._entry = await getExtendedEntityRegistryEntry(
this.hass,
this._entityId
);
}
public closeDialog() {
this._entityId = undefined;
this._entry = undefined;
this._childView = undefined;
fireEvent(this, "dialog-closed", { dialog: this.localName });
}
@ -172,7 +190,10 @@ export class MoreInfoDialog extends LitElement {
idToPassThroughUrl = stateObj.attributes.id;
}
if (EDITABLE_DOMAINS_WITH_UNIQUE_ID.includes(domain)) {
idToPassThroughUrl = this.hass.entities[this._entityId!].unique_id;
if (!this._entry) {
return;
}
idToPassThroughUrl = this._entry.unique_id;
}
navigate(`/config/${domain}/edit/${idToPassThroughUrl}`);
@ -358,6 +379,8 @@ export class MoreInfoDialog extends LitElement {
<ha-more-info-settings
.hass=${this.hass}
.entityId=${this._entityId}
.entry=${this._entry}
@entity-entry-updated=${this._entryUpdated}
></ha-more-info-settings>
`
: this._currView === "related"
@ -387,6 +410,10 @@ export class MoreInfoDialog extends LitElement {
}
}
private _entryUpdated(ev: CustomEvent<ExtEntityRegistryEntry>) {
this._entry = ev.detail;
}
private _enlarge() {
this.large = !this.large;
}

View File

@ -6,12 +6,11 @@ import { dynamicElement } from "../../common/dom/dynamic-element-directive";
import {
EntityRegistryEntry,
ExtEntityRegistryEntry,
getExtendedEntityRegistryEntry,
} from "../../data/entity_registry";
import { PLATFORMS_WITH_SETTINGS_TAB } from "../../panels/config/entities/const";
import "../../panels/config/entities/entity-registry-settings";
import type { HomeAssistant } from "../../types";
import { documentationUrl } from "../../util/documentation-url";
import "../../panels/config/entities/entity-registry-settings";
@customElement("ha-more-info-settings")
export class HaMoreInfoSettings extends LitElement {
@ -19,18 +18,18 @@ export class HaMoreInfoSettings extends LitElement {
@property({ attribute: false }) public entityId!: string;
@state() private _entry?: EntityRegistryEntry | ExtEntityRegistryEntry | null;
@state() private entry?: EntityRegistryEntry | ExtEntityRegistryEntry | null;
@state() private _settingsElementTag?: string;
protected render() {
// loading.
if (this._entry === undefined) {
if (this.entry === undefined) {
return html``;
}
// No unique ID
if (this._entry === null) {
if (this.entry === null) {
return html`
<div class="content">
${this.hass.localize(
@ -54,53 +53,31 @@ export class HaMoreInfoSettings extends LitElement {
}
return html`
<div @entity-entry-updated=${this._entryUpdated}>
${dynamicElement(this._settingsElementTag, {
hass: this.hass,
entry: this._entry,
entityId: this.entityId,
})}
</div>
${dynamicElement(this._settingsElementTag, {
hass: this.hass,
entry: this.entry,
entityId: this.entityId,
})}
`;
}
protected willUpdate(changedProps: PropertyValues) {
super.willUpdate(changedProps);
if (changedProps.has("entityId")) {
this._entry = undefined;
if (this.entityId) {
this._getEntityReg();
}
}
}
private async _getEntityReg() {
try {
this._entry = await getExtendedEntityRegistryEntry(
this.hass,
this.entityId
);
public willUpdate(changedProps: PropertyValues) {
if (changedProps.has("entry")) {
this._loadPlatformSettingTabs();
} catch {
this._entry = null;
}
}
private _entryUpdated(ev: CustomEvent<ExtEntityRegistryEntry>) {
this._entry = ev.detail;
}
private async _loadPlatformSettingTabs(): Promise<void> {
if (!this._entry) {
if (!this.entry) {
return;
}
if (
!Object.keys(PLATFORMS_WITH_SETTINGS_TAB).includes(this._entry.platform)
!Object.keys(PLATFORMS_WITH_SETTINGS_TAB).includes(this.entry.platform)
) {
this._settingsElementTag = "entity-registry-settings";
return;
}
const tag = PLATFORMS_WITH_SETTINGS_TAB[this._entry.platform];
const tag = PLATFORMS_WITH_SETTINGS_TAB[this.entry.platform];
await import(`../../panels/config/entities/editor-tabs/settings/${tag}`);
this._settingsElementTag = tag;
}

View File

@ -11,6 +11,7 @@ import {
mdiStopCircleOutline,
mdiSort,
} from "@mdi/js";
import { UnsubscribeFunc } from "home-assistant-js-websocket";
import { css, CSSResultGroup, html, LitElement, PropertyValues } from "lit";
import { customElement, property, query, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
@ -26,6 +27,10 @@ import "../../../../components/ha-icon-button";
import type { HaYamlEditor } from "../../../../components/ha-yaml-editor";
import { ACTION_TYPES } from "../../../../data/action";
import { validateConfig } from "../../../../data/config";
import {
EntityRegistryEntry,
subscribeEntityRegistry,
} from "../../../../data/entity_registry";
import { Action, getActionType } from "../../../../data/script";
import { describeAction } from "../../../../data/script_i18n";
import { callExecuteScript } from "../../../../data/service";
@ -107,6 +112,8 @@ export default class HaAutomationActionRow extends LitElement {
@property({ type: Boolean }) public reOrderMode = false;
@state() private _entityReg: EntityRegistryEntry[] = [];
@state() private _warnings?: string[];
@state() private _uiModeAvailable = true;
@ -115,6 +122,14 @@ export default class HaAutomationActionRow extends LitElement {
@query("ha-yaml-editor") private _yamlEditor?: HaYamlEditor;
public hassSubscribe(): UnsubscribeFunc[] {
return [
subscribeEntityRegistry(this.hass.connection!, (entities) => {
this._entityReg = entities;
}),
];
}
protected willUpdate(changedProperties: PropertyValues) {
if (!changedProperties.has("action")) {
return;
@ -156,7 +171,9 @@ export default class HaAutomationActionRow extends LitElement {
class="action-icon"
.path=${ACTION_TYPES[type!]}
></ha-svg-icon>
${capitalizeFirstLetter(describeAction(this.hass, this.action))}
${capitalizeFirstLetter(
describeAction(this.hass, this._entityReg, this.action)
)}
</h3>
<slot name="icons" slot="icons"></slot>
@ -465,7 +482,7 @@ export default class HaAutomationActionRow extends LitElement {
),
inputType: "string",
placeholder: capitalizeFirstLetter(
describeAction(this.hass, this.action, undefined, true)
describeAction(this.hass, this._entityReg, this.action, undefined, true)
),
defaultValue: this.action.alias,
confirmText: this.hass.localize("ui.common.submit"),

View File

@ -49,6 +49,7 @@ import {
showAutomationEditor,
triggerAutomationActions,
} from "../../../data/automation";
import { fetchEntityRegistry } from "../../../data/entity_registry";
import {
showAlertDialog,
showConfirmationDialog,
@ -479,7 +480,8 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
this._readOnly = false;
this._config = this._normalizeConfig(config);
} catch (err: any) {
const entity = Object.values(this.hass.entities).find(
const entityRegistry = await fetchEntityRegistry(this.hass.connection);
const entity = entityRegistry.find(
(ent) =>
ent.platform === "automation" && ent.unique_id === this.automationId
);

View File

@ -1,14 +1,19 @@
import { HassEntities } from "home-assistant-js-websocket";
import { HassEntities, UnsubscribeFunc } from "home-assistant-js-websocket";
import { PropertyValues } from "lit";
import { customElement, property } from "lit/decorators";
import { customElement, property, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import { computeStateDomain } from "../../../common/entity/compute_state_domain";
import { debounce } from "../../../common/util/debounce";
import {
EntityRegistryEntry,
subscribeEntityRegistry,
} from "../../../data/entity_registry";
import { ScriptEntity } from "../../../data/script";
import {
HassRouterPage,
RouterOptions,
} from "../../../layouts/hass-router-page";
import { SubscribeMixin } from "../../../mixins/subscribe-mixin";
import { HomeAssistant } from "../../../types";
import "./ha-script-editor";
import "./ha-script-picker";
@ -21,7 +26,7 @@ const equal = (a: ScriptEntity[], b: ScriptEntity[]): boolean => {
};
@customElement("ha-config-script")
class HaConfigScript extends HassRouterPage {
class HaConfigScript extends SubscribeMixin(HassRouterPage) {
@property({ attribute: false }) public hass!: HomeAssistant;
@property() public narrow!: boolean;
@ -32,6 +37,16 @@ class HaConfigScript extends HassRouterPage {
@property() public scripts: ScriptEntity[] = [];
@state() private _entityReg: EntityRegistryEntry[] = [];
public hassSubscribe(): UnsubscribeFunc[] {
return [
subscribeEntityRegistry(this.hass.connection!, (entities) => {
this._entityReg = entities;
}),
];
}
protected routerOptions: RouterOptions = {
defaultPage: "dashboard",
routes: {
@ -78,6 +93,7 @@ class HaConfigScript extends HassRouterPage {
pageEl.isWide = this.isWide;
pageEl.route = this.routeTail;
pageEl.showAdvanced = this.showAdvanced;
pageEl.entityRegistry = this._entityReg;
if (this.hass) {
if (!pageEl.scripts || !changedProps) {

View File

@ -39,6 +39,7 @@ import "../../../components/ha-icon-button";
import "../../../components/ha-svg-icon";
import "../../../components/ha-yaml-editor";
import type { HaYamlEditor } from "../../../components/ha-yaml-editor";
import { EntityRegistryEntry } from "../../../data/entity_registry";
import {
deleteScript,
getScriptStateConfig,
@ -75,6 +76,8 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
@property({ type: Boolean }) public narrow!: boolean;
@property({ attribute: false }) public entityRegistry!: EntityRegistryEntry[];
@state() private _config?: ScriptConfig;
@state() private _entityId?: string;
@ -431,7 +434,7 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
this._config = this._normalizeConfig(config);
},
(resp) => {
const entity = Object.values(this.hass.entities).find(
const entity = this.entityRegistry.find(
(ent) =>
ent.platform === "script" && ent.unique_id === this.scriptId
);
@ -477,7 +480,9 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
getScriptStateConfig(this.hass, this.entityId).then((c) => {
this._config = this._normalizeConfig(c.config);
});
const regEntry = this.hass.entities[this.entityId];
const regEntry = this.entityRegistry.find(
(ent) => ent.entity_id === this.entityId
);
if (regEntry?.unique_id) {
this.scriptId = regEntry.unique_id;
}
@ -544,7 +549,7 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
if (!this.scriptId) {
return;
}
const entity = Object.values(this.hass.entities).find(
const entity = this.entityRegistry.find(
(entry) => entry.unique_id === this.scriptId
);
if (!entity) {

View File

@ -44,6 +44,7 @@ import { HomeAssistant, Route } from "../../../types";
import { documentationUrl } from "../../../util/documentation-url";
import { showToast } from "../../../util/toast";
import { configSections } from "../ha-panel-config";
import { EntityRegistryEntry } from "../../../data/entity_registry";
@customElement("ha-script-picker")
class HaScriptPicker extends LitElement {
@ -57,7 +58,9 @@ class HaScriptPicker extends LitElement {
@property() public route!: Route;
@property() private _activeFilters?: string[];
@property({ attribute: false }) public entityRegistry!: EntityRegistryEntry[];
@state() private _activeFilters?: string[];
@state() private _filteredScripts?: string[] | null;
@ -266,7 +269,7 @@ class HaScriptPicker extends LitElement {
}
private _handleRowClicked(ev: HASSDomEvent<RowClickedEvent>) {
const entry = this.hass.entities[ev.detail.id];
const entry = this.entityRegistry.find((e) => e.entity_id === ev.detail.id);
if (entry) {
navigate(`/config/script/edit/${entry.unique_id}`);
} else {
@ -275,7 +278,12 @@ class HaScriptPicker extends LitElement {
}
private _runScript = async (script: any) => {
const entry = this.hass.entities[script.entity_id];
const entry = this.entityRegistry.find(
(e) => e.entity_id === script.entity_id
);
if (!entry) {
return;
}
await triggerScript(this.hass, entry.unique_id);
showToast(this, {
message: this.hass.localize(
@ -291,7 +299,9 @@ class HaScriptPicker extends LitElement {
}
private _showTrace(script: any) {
const entry = this.hass.entities[script.entity_id];
const entry = this.entityRegistry.find(
(e) => e.entity_id === script.entity_id
);
if (entry) {
navigate(`/config/script/trace/${entry.unique_id}`);
}
@ -317,7 +327,12 @@ class HaScriptPicker extends LitElement {
private async _duplicate(script: any) {
try {
const entry = this.hass.entities[script.entity_id];
const entry = this.entityRegistry.find(
(e) => e.entity_id === script.entity_id
);
if (!entry) {
return;
}
const config = await fetchScriptFileConfig(this.hass, entry.unique_id);
showScriptEditor({
...config,
@ -362,8 +377,12 @@ class HaScriptPicker extends LitElement {
private async _delete(script: any) {
try {
const entry = this.hass.entities[script.entity_id];
await deleteScript(this.hass, entry.unique_id);
const entry = this.entityRegistry.find(
(e) => e.entity_id === script.entity_id
);
if (entry) {
await deleteScript(this.hass, entry.unique_id);
}
} catch (err: any) {
await showAlertDialog(this, {
text:

View File

@ -39,6 +39,7 @@ import { HomeAssistant, Route } from "../../../types";
import "../../../layouts/hass-subpage";
import "../../../components/ha-button-menu";
import { fireEvent } from "../../../common/dom/fire_event";
import { EntityRegistryEntry } from "../../../data/entity_registry";
@customElement("ha-script-trace")
export class HaScriptTrace extends LitElement {
@ -54,6 +55,8 @@ export class HaScriptTrace extends LitElement {
@property({ attribute: false }) public route!: Route;
@property({ attribute: false }) public entityRegistry!: EntityRegistryEntry[];
@state() private _entityId?: string;
@state() private _traces?: ScriptTrace[];
@ -318,7 +321,7 @@ export class HaScriptTrace extends LitElement {
const params = new URLSearchParams(location.search);
this._loadTraces(params.get("run_id") || undefined);
this._entityId = Object.values(this.hass.entities).find(
this._entityId = this.entityRegistry.find(
(entry) => entry.unique_id === this.scriptId
)?.entity_id;
}
@ -335,7 +338,7 @@ export class HaScriptTrace extends LitElement {
if (this.scriptId) {
this._loadTraces();
this._entityId = Object.values(this.hass.entities).find(
this._entityId = this.entityRegistry.find(
(entry) => entry.unique_id === this.scriptId
)?.entity_id;
}

View File

@ -130,7 +130,10 @@ class HuiGaugeCard extends LitElement implements LovelaceCard {
.min=${this._config.min!}
.max=${this._config.max!}
.value=${stateObj.state}
.formatOptions=${getNumberFormatOptions(stateObj)}
.formatOptions=${getNumberFormatOptions(
stateObj,
this.hass.entities[stateObj.entity_id]
)}
.locale=${this.hass!.locale}
.label=${this._config!.unit ||
this.hass?.states[this._config!.entity].attributes

View File

@ -278,8 +278,8 @@ const computeDefaultViewStates = (
.filter(
(entry) =>
entry.entity_category ||
HIDE_PLATFORM.has(entry.platform) ||
entry.hidden_by
(entry.platform && HIDE_PLATFORM.has(entry.platform)) ||
entry.hidden
)
.map((entry) => entry.entity_id)
);

View File

@ -14,7 +14,7 @@ import { polyfillsLoaded } from "../common/translations/localize";
import { subscribeAreaRegistry } from "../data/area_registry";
import { broadcastConnectionStatus } from "../data/connection-status";
import { subscribeDeviceRegistry } from "../data/device_registry";
import { subscribeEntityRegistry } from "../data/entity_registry";
import { subscribeEntityRegistryDisplay } from "../data/entity_registry";
import { subscribeFrontendUserData } from "../data/frontend";
import { forwardHaptic } from "../data/haptics";
import { DEFAULT_PANEL } from "../data/panel";
@ -188,10 +188,22 @@ export const connectionMixin = <T extends Constructor<HassBaseEl>>(
});
subscribeEntities(conn, (states) => this._updateHass({ states }));
subscribeEntityRegistry(conn, (entityReg) => {
subscribeEntityRegistryDisplay(conn, (entityReg) => {
const entities: HomeAssistant["entities"] = {};
for (const entity of entityReg) {
entities[entity.entity_id] = entity;
for (const entity of entityReg.entities) {
entities[entity.ei] = {
entity_id: entity.ei,
device_id: entity.di,
area_id: entity.ai,
translation_key: entity.tk,
platform: entity.pl,
entity_category: entity.ec
? entityReg.entity_categories[entity.ec]
: undefined,
name: entity.en,
hidden: entity.hb,
display_precision: entity.dp,
};
}
this._updateHass({ entities });
});

View File

@ -10,7 +10,7 @@ import {
import { LocalizeFunc } from "./common/translations/localize";
import { AreaRegistryEntry } from "./data/area_registry";
import { DeviceRegistryEntry } from "./data/device_registry";
import { EntityRegistryEntry } from "./data/entity_registry";
import { EntityRegistryDisplayEntry } from "./data/entity_registry";
import { CoreFrontendUserData } from "./data/frontend";
import { FrontendLocaleData, getHassTranslations } from "./data/translation";
import { Themes } from "./data/ws-themes";
@ -189,7 +189,7 @@ export interface HomeAssistant {
connection: Connection;
connected: boolean;
states: HassEntities;
entities: { [id: string]: EntityRegistryEntry };
entities: { [id: string]: EntityRegistryDisplayEntry };
devices: { [id: string]: DeviceRegistryEntry };
areas: { [id: string]: AreaRegistryEntry };
services: HassServices;