From 373a0adaa2d3999601f7f3dfe373ffef7c9268c1 Mon Sep 17 00:00:00 2001 From: karwosts Date: Sat, 22 Jun 2024 15:23:34 -0700 Subject: [PATCH 1/2] Fix device integration filter for entityless devices --- .../ha-selector/ha-selector-area.ts | 13 ++++++++++- .../ha-selector/ha-selector-device.ts | 13 ++++++++++- .../ha-selector/ha-selector-floor.ts | 13 ++++++++++- src/data/device_registry.ts | 23 ++++++++++++++++++- 4 files changed, 58 insertions(+), 4 deletions(-) diff --git a/src/components/ha-selector/ha-selector-area.ts b/src/components/ha-selector/ha-selector-area.ts index 7690c387adfd..47230ff88d11 100644 --- a/src/components/ha-selector/ha-selector-area.ts +++ b/src/components/ha-selector/ha-selector-area.ts @@ -11,6 +11,7 @@ import { fetchEntitySourcesWithCache, } from "../../data/entity_sources"; import type { AreaSelector } from "../../data/selector"; +import { ConfigEntry, getConfigEntries } from "../../data/config_entries"; import { filterSelectorDevices, filterSelectorEntities, @@ -37,6 +38,8 @@ export class HaAreaSelector extends LitElement { @state() private _entitySources?: EntitySources; + @state() private _configEntries?: ConfigEntry[]; + private _deviceIntegrationLookup = memoizeOne(getDeviceIntegrationLookup); private _hasIntegration(selector: AreaSelector) { @@ -72,6 +75,12 @@ export class HaAreaSelector extends LitElement { this._entitySources = sources; }); } + if (!this._configEntries && this._hasIntegration(this.selector)) { + this._configEntries = []; + getConfigEntries(this.hass).then((entries) => { + this._configEntries = entries; + }); + } } protected render() { @@ -136,7 +145,9 @@ export class HaAreaSelector extends LitElement { const deviceIntegrations = this._entitySources ? this._deviceIntegrationLookup( this._entitySources, - Object.values(this.hass.entities) + Object.values(this.hass.entities), + Object.values(this.hass.devices), + this._configEntries ) : undefined; diff --git a/src/components/ha-selector/ha-selector-device.ts b/src/components/ha-selector/ha-selector-device.ts index b3ffebd4b667..0131b9b69854 100644 --- a/src/components/ha-selector/ha-selector-device.ts +++ b/src/components/ha-selector/ha-selector-device.ts @@ -11,6 +11,7 @@ import { fetchEntitySourcesWithCache, } from "../../data/entity_sources"; import type { DeviceSelector } from "../../data/selector"; +import { ConfigEntry, getConfigEntries } from "../../data/config_entries"; import { filterSelectorDevices, filterSelectorEntities, @@ -27,6 +28,8 @@ export class HaDeviceSelector extends LitElement { @state() private _entitySources?: EntitySources; + @state() private _configEntries?: ConfigEntry[]; + @property() public value?: any; @property() public label?: string; @@ -75,6 +78,12 @@ export class HaDeviceSelector extends LitElement { this._entitySources = sources; }); } + if (!this._configEntries && this._hasIntegration(this.selector)) { + this._configEntries = []; + getConfigEntries(this.hass).then((entries) => { + this._configEntries = entries; + }); + } } protected render() { @@ -123,7 +132,9 @@ export class HaDeviceSelector extends LitElement { const deviceIntegrations = this._entitySources ? this._deviceIntegrationLookup( this._entitySources, - Object.values(this.hass.entities) + Object.values(this.hass.entities), + Object.values(this.hass.devices), + this._configEntries ) : undefined; diff --git a/src/components/ha-selector/ha-selector-floor.ts b/src/components/ha-selector/ha-selector-floor.ts index eac63f414edb..6d4d0d29c834 100644 --- a/src/components/ha-selector/ha-selector-floor.ts +++ b/src/components/ha-selector/ha-selector-floor.ts @@ -11,6 +11,7 @@ import { fetchEntitySourcesWithCache, } from "../../data/entity_sources"; import type { FloorSelector } from "../../data/selector"; +import { ConfigEntry, getConfigEntries } from "../../data/config_entries"; import { filterSelectorDevices, filterSelectorEntities, @@ -37,6 +38,8 @@ export class HaFloorSelector extends LitElement { @state() private _entitySources?: EntitySources; + @state() private _configEntries?: ConfigEntry[]; + private _deviceIntegrationLookup = memoizeOne(getDeviceIntegrationLookup); private _hasIntegration(selector: FloorSelector) { @@ -72,6 +75,12 @@ export class HaFloorSelector extends LitElement { this._entitySources = sources; }); } + if (!this._configEntries && this._hasIntegration(this.selector)) { + this._configEntries = []; + getConfigEntries(this.hass).then((entries) => { + this._configEntries = entries; + }); + } } protected render() { @@ -136,7 +145,9 @@ export class HaFloorSelector extends LitElement { const deviceIntegrations = this._entitySources ? this._deviceIntegrationLookup( this._entitySources, - Object.values(this.hass.entities) + Object.values(this.hass.entities), + Object.values(this.hass.devices), + this._configEntries ) : undefined; diff --git a/src/data/device_registry.ts b/src/data/device_registry.ts index 80e9a5b05373..8de391d9021b 100644 --- a/src/data/device_registry.ts +++ b/src/data/device_registry.ts @@ -5,6 +5,7 @@ import type { EntityRegistryDisplayEntry, EntityRegistryEntry, } from "./entity_registry"; +import { ConfigEntry } from "./config_entries"; import type { EntitySources } from "./entity_sources"; export { @@ -142,7 +143,9 @@ export const getDeviceEntityDisplayLookup = ( export const getDeviceIntegrationLookup = ( entitySources: EntitySources, - entities: EntityRegistryDisplayEntry[] | EntityRegistryEntry[] + entities: EntityRegistryDisplayEntry[] | EntityRegistryEntry[], + devices?: DeviceRegistryEntry[], + configEntries?: ConfigEntry[] ): Record => { const deviceIntegrations: Record = {}; @@ -157,5 +160,23 @@ export const getDeviceIntegrationLookup = ( } deviceIntegrations[entity.device_id!].push(source.domain); } + // Lookup devices that have no entities + if (devices && configEntries) { + for (const device of devices) { + if (!deviceIntegrations[device.id]) { + for (const config_entry_id of device.config_entries) { + const entry = configEntries.find( + (e) => e.entry_id === config_entry_id + ); + if (entry?.domain) { + ( + deviceIntegrations[device.id] || + (deviceIntegrations[device.id] = []) + ).push(entry.domain); + } + } + } + } + } return deviceIntegrations; }; From 88f73d7d834a25435789f41f5a6f36d0799e275d Mon Sep 17 00:00:00 2001 From: karwosts Date: Wed, 26 Jun 2024 08:47:43 -0700 Subject: [PATCH 2/2] code review --- src/data/device_registry.ts | 28 +++++++++++----------------- src/data/selector.ts | 4 ++-- 2 files changed, 13 insertions(+), 19 deletions(-) diff --git a/src/data/device_registry.ts b/src/data/device_registry.ts index 8de391d9021b..90bd894e8a62 100644 --- a/src/data/device_registry.ts +++ b/src/data/device_registry.ts @@ -146,8 +146,8 @@ export const getDeviceIntegrationLookup = ( entities: EntityRegistryDisplayEntry[] | EntityRegistryEntry[], devices?: DeviceRegistryEntry[], configEntries?: ConfigEntry[] -): Record => { - const deviceIntegrations: Record = {}; +): Record> => { + const deviceIntegrations: Record> = {}; for (const entity of entities) { const source = entitySources[entity.entity_id]; @@ -155,25 +155,19 @@ export const getDeviceIntegrationLookup = ( continue; } - if (!deviceIntegrations[entity.device_id!]) { - deviceIntegrations[entity.device_id!] = []; - } - deviceIntegrations[entity.device_id!].push(source.domain); + deviceIntegrations[entity.device_id!] = + deviceIntegrations[entity.device_id!] || new Set(); + deviceIntegrations[entity.device_id!].add(source.domain); } // Lookup devices that have no entities if (devices && configEntries) { for (const device of devices) { - if (!deviceIntegrations[device.id]) { - for (const config_entry_id of device.config_entries) { - const entry = configEntries.find( - (e) => e.entry_id === config_entry_id - ); - if (entry?.domain) { - ( - deviceIntegrations[device.id] || - (deviceIntegrations[device.id] = []) - ).push(entry.domain); - } + for (const config_entry_id of device.config_entries) { + const entry = configEntries.find((e) => e.entry_id === config_entry_id); + if (entry?.domain) { + deviceIntegrations[device.id] = + deviceIntegrations[device.id] || new Set(); + deviceIntegrations[device.id].add(entry.domain); } } } diff --git a/src/data/selector.ts b/src/data/selector.ts index bb90c792f48b..60b9e4973b1c 100644 --- a/src/data/selector.ts +++ b/src/data/selector.ts @@ -696,7 +696,7 @@ export const entityMeetsTargetSelector = ( export const filterSelectorDevices = ( filterDevice: DeviceSelectorFilter, device: DeviceRegistryEntry, - deviceIntegrationLookup?: Record | undefined + deviceIntegrationLookup?: Record> | undefined ): boolean => { const { manufacturer: filterManufacturer, @@ -713,7 +713,7 @@ export const filterSelectorDevices = ( } if (filterIntegration && deviceIntegrationLookup) { - if (!deviceIntegrationLookup?.[device.id]?.includes(filterIntegration)) { + if (!deviceIntegrationLookup?.[device.id]?.has(filterIntegration)) { return false; } }