Skip to content

Commit 77874aa

Browse files
wendevlinCopilot
andauthored
New target picker (#27284)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent 4808463 commit 77874aa

37 files changed

+3902
-1470
lines changed

src/common/entity/context/get_area_context.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@ interface AreaContext {
88
}
99
export const getAreaContext = (
1010
area: AreaRegistryEntry,
11-
hass: HomeAssistant
11+
hassFloors: HomeAssistant["floors"]
1212
): AreaContext => {
1313
const floorId = area.floor_id;
14-
const floor = floorId ? hass.floors[floorId] : undefined;
14+
const floor = floorId ? hassFloors[floorId] : undefined;
1515

1616
return {
1717
area: area,

src/components/device/ha-device-picker.ts

Lines changed: 9 additions & 162 deletions
Original file line numberDiff line numberDiff line change
@@ -5,36 +5,25 @@ import { customElement, property, query, state } from "lit/decorators";
55
import memoizeOne from "memoize-one";
66
import { fireEvent } from "../../common/dom/fire_event";
77
import { computeAreaName } from "../../common/entity/compute_area_name";
8-
import {
9-
computeDeviceName,
10-
computeDeviceNameDisplay,
11-
} from "../../common/entity/compute_device_name";
12-
import { computeDomain } from "../../common/entity/compute_domain";
8+
import { computeDeviceName } from "../../common/entity/compute_device_name";
139
import { getDeviceContext } from "../../common/entity/context/get_device_context";
1410
import { getConfigEntries, type ConfigEntry } from "../../data/config_entries";
1511
import {
16-
getDeviceEntityDisplayLookup,
17-
type DeviceEntityDisplayLookup,
12+
getDevices,
13+
type DevicePickerItem,
1814
type DeviceRegistryEntry,
1915
} from "../../data/device_registry";
20-
import { domainToName } from "../../data/integration";
2116
import type { HomeAssistant } from "../../types";
2217
import { brandsUrl } from "../../util/brands-url";
2318
import "../ha-generic-picker";
2419
import type { HaGenericPicker } from "../ha-generic-picker";
25-
import type { PickerComboBoxItem } from "../ha-picker-combo-box";
2620

2721
export type HaDevicePickerDeviceFilterFunc = (
2822
device: DeviceRegistryEntry
2923
) => boolean;
3024

3125
export type HaDevicePickerEntityFilterFunc = (entity: HassEntity) => boolean;
3226

33-
interface DevicePickerItem extends PickerComboBoxItem {
34-
domain?: string;
35-
domain_name?: string;
36-
}
37-
3827
@customElement("ha-device-picker")
3928
export class HaDevicePicker extends LitElement {
4029
@property({ attribute: false }) public hass!: HomeAssistant;
@@ -104,6 +93,8 @@ export class HaDevicePicker extends LitElement {
10493

10594
@state() private _configEntryLookup: Record<string, ConfigEntry> = {};
10695

96+
private _getDevicesMemoized = memoizeOne(getDevices);
97+
10798
protected firstUpdated(_changedProperties: PropertyValues): void {
10899
super.firstUpdated(_changedProperties);
109100
this._loadConfigEntries();
@@ -117,162 +108,18 @@ export class HaDevicePicker extends LitElement {
117108
}
118109

119110
private _getItems = () =>
120-
this._getDevices(
121-
this.hass.devices,
122-
this.hass.entities,
111+
this._getDevicesMemoized(
112+
this.hass,
123113
this._configEntryLookup,
124114
this.includeDomains,
125115
this.excludeDomains,
126116
this.includeDeviceClasses,
127117
this.deviceFilter,
128118
this.entityFilter,
129-
this.excludeDevices
119+
this.excludeDevices,
120+
this.value
130121
);
131122

132-
private _getDevices = memoizeOne(
133-
(
134-
haDevices: HomeAssistant["devices"],
135-
haEntities: HomeAssistant["entities"],
136-
configEntryLookup: Record<string, ConfigEntry>,
137-
includeDomains: this["includeDomains"],
138-
excludeDomains: this["excludeDomains"],
139-
includeDeviceClasses: this["includeDeviceClasses"],
140-
deviceFilter: this["deviceFilter"],
141-
entityFilter: this["entityFilter"],
142-
excludeDevices: this["excludeDevices"]
143-
): DevicePickerItem[] => {
144-
const devices = Object.values(haDevices);
145-
const entities = Object.values(haEntities);
146-
147-
let deviceEntityLookup: DeviceEntityDisplayLookup = {};
148-
149-
if (
150-
includeDomains ||
151-
excludeDomains ||
152-
includeDeviceClasses ||
153-
entityFilter
154-
) {
155-
deviceEntityLookup = getDeviceEntityDisplayLookup(entities);
156-
}
157-
158-
let inputDevices = devices.filter(
159-
(device) => device.id === this.value || !device.disabled_by
160-
);
161-
162-
if (includeDomains) {
163-
inputDevices = inputDevices.filter((device) => {
164-
const devEntities = deviceEntityLookup[device.id];
165-
if (!devEntities || !devEntities.length) {
166-
return false;
167-
}
168-
return deviceEntityLookup[device.id].some((entity) =>
169-
includeDomains.includes(computeDomain(entity.entity_id))
170-
);
171-
});
172-
}
173-
174-
if (excludeDomains) {
175-
inputDevices = inputDevices.filter((device) => {
176-
const devEntities = deviceEntityLookup[device.id];
177-
if (!devEntities || !devEntities.length) {
178-
return true;
179-
}
180-
return entities.every(
181-
(entity) =>
182-
!excludeDomains.includes(computeDomain(entity.entity_id))
183-
);
184-
});
185-
}
186-
187-
if (excludeDevices) {
188-
inputDevices = inputDevices.filter(
189-
(device) => !excludeDevices!.includes(device.id)
190-
);
191-
}
192-
193-
if (includeDeviceClasses) {
194-
inputDevices = inputDevices.filter((device) => {
195-
const devEntities = deviceEntityLookup[device.id];
196-
if (!devEntities || !devEntities.length) {
197-
return false;
198-
}
199-
return deviceEntityLookup[device.id].some((entity) => {
200-
const stateObj = this.hass.states[entity.entity_id];
201-
if (!stateObj) {
202-
return false;
203-
}
204-
return (
205-
stateObj.attributes.device_class &&
206-
includeDeviceClasses.includes(stateObj.attributes.device_class)
207-
);
208-
});
209-
});
210-
}
211-
212-
if (entityFilter) {
213-
inputDevices = inputDevices.filter((device) => {
214-
const devEntities = deviceEntityLookup[device.id];
215-
if (!devEntities || !devEntities.length) {
216-
return false;
217-
}
218-
return devEntities.some((entity) => {
219-
const stateObj = this.hass.states[entity.entity_id];
220-
if (!stateObj) {
221-
return false;
222-
}
223-
return entityFilter(stateObj);
224-
});
225-
});
226-
}
227-
228-
if (deviceFilter) {
229-
inputDevices = inputDevices.filter(
230-
(device) =>
231-
// We always want to include the device of the current value
232-
device.id === this.value || deviceFilter!(device)
233-
);
234-
}
235-
236-
const outputDevices = inputDevices.map<DevicePickerItem>((device) => {
237-
const deviceName = computeDeviceNameDisplay(
238-
device,
239-
this.hass,
240-
deviceEntityLookup[device.id]
241-
);
242-
243-
const { area } = getDeviceContext(device, this.hass);
244-
245-
const areaName = area ? computeAreaName(area) : undefined;
246-
247-
const configEntry = device.primary_config_entry
248-
? configEntryLookup?.[device.primary_config_entry]
249-
: undefined;
250-
251-
const domain = configEntry?.domain;
252-
const domainName = domain
253-
? domainToName(this.hass.localize, domain)
254-
: undefined;
255-
256-
return {
257-
id: device.id,
258-
label: "",
259-
primary:
260-
deviceName ||
261-
this.hass.localize("ui.components.device-picker.unnamed_device"),
262-
secondary: areaName,
263-
domain: configEntry?.domain,
264-
domain_name: domainName,
265-
search_labels: [deviceName, areaName, domain, domainName].filter(
266-
Boolean
267-
) as string[],
268-
sorting_label: deviceName || "zzz",
269-
};
270-
});
271-
272-
return outputDevices;
273-
}
274-
);
275-
276123
private _valueRenderer = memoizeOne(
277124
(configEntriesLookup: Record<string, ConfigEntry>) => (value: string) => {
278125
const deviceId = value;

src/components/entity/ha-entities-picker.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { isValidEntityId } from "../../common/entity/valid_entity_id";
77
import type { HomeAssistant, ValueChangedEvent } from "../../types";
88
import "../ha-sortable";
99
import "./ha-entity-picker";
10-
import type { HaEntityPickerEntityFilterFunc } from "./ha-entity-picker";
10+
import type { HaEntityPickerEntityFilterFunc } from "../../data/entity";
1111

1212
@customElement("ha-entities-picker")
1313
class HaEntitiesPicker extends LitElement {

src/components/entity/ha-entity-attribute-picker.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import type { HassEntity } from "home-assistant-js-websocket";
21
import type { PropertyValues } from "lit";
32
import { LitElement, html, nothing } from "lit";
43
import { customElement, property, query, state } from "lit/decorators";
@@ -8,8 +7,6 @@ import type { HomeAssistant, ValueChangedEvent } from "../../types";
87
import "../ha-combo-box";
98
import type { HaComboBox } from "../ha-combo-box";
109

11-
export type HaEntityPickerEntityFilterFunc = (entityId: HassEntity) => boolean;
12-
1310
interface AttributeOption {
1411
value: string;
1512
label: string;

0 commit comments

Comments
 (0)