Filter Integration in Target and Area selectors + clean up some code (#13202)
This commit is contained in:
parent
b131b255ec
commit
e4d233afa8
|
@ -1,8 +1,9 @@
|
||||||
import { UnsubscribeFunc } from "home-assistant-js-websocket";
|
import { HassEntity, UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||||
import { html, LitElement } from "lit";
|
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
import { DeviceRegistryEntry } from "../../data/device_registry";
|
import type { DeviceRegistryEntry } from "../../data/device_registry";
|
||||||
|
import { getDeviceIntegrationLookup } from "../../data/device_registry";
|
||||||
import {
|
import {
|
||||||
EntityRegistryEntry,
|
EntityRegistryEntry,
|
||||||
subscribeEntityRegistry,
|
subscribeEntityRegistry,
|
||||||
|
@ -11,7 +12,11 @@ import {
|
||||||
EntitySources,
|
EntitySources,
|
||||||
fetchEntitySourcesWithCache,
|
fetchEntitySourcesWithCache,
|
||||||
} from "../../data/entity_sources";
|
} from "../../data/entity_sources";
|
||||||
import { AreaSelector } from "../../data/selector";
|
import type { AreaSelector } from "../../data/selector";
|
||||||
|
import {
|
||||||
|
filterSelectorDevices,
|
||||||
|
filterSelectorEntities,
|
||||||
|
} from "../../data/selector";
|
||||||
import { SubscribeMixin } from "../../mixins/subscribe-mixin";
|
import { SubscribeMixin } from "../../mixins/subscribe-mixin";
|
||||||
import { HomeAssistant } from "../../types";
|
import { HomeAssistant } from "../../types";
|
||||||
import "../ha-area-picker";
|
import "../ha-area-picker";
|
||||||
|
@ -29,13 +34,15 @@ export class HaAreaSelector extends SubscribeMixin(LitElement) {
|
||||||
|
|
||||||
@property() public helper?: string;
|
@property() public helper?: string;
|
||||||
|
|
||||||
|
@property({ type: Boolean }) public disabled = false;
|
||||||
|
|
||||||
|
@property({ type: Boolean }) public required = true;
|
||||||
|
|
||||||
@state() private _entitySources?: EntitySources;
|
@state() private _entitySources?: EntitySources;
|
||||||
|
|
||||||
@state() private _entities?: EntityRegistryEntry[];
|
@state() private _entities?: EntityRegistryEntry[];
|
||||||
|
|
||||||
@property({ type: Boolean }) public disabled = false;
|
private _deviceIntegrationLookup = memoizeOne(getDeviceIntegrationLookup);
|
||||||
|
|
||||||
@property({ type: Boolean }) public required = true;
|
|
||||||
|
|
||||||
public hassSubscribe(): UnsubscribeFunc[] {
|
public hassSubscribe(): UnsubscribeFunc[] {
|
||||||
return [
|
return [
|
||||||
|
@ -45,7 +52,7 @@ export class HaAreaSelector extends SubscribeMixin(LitElement) {
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
protected updated(changedProperties) {
|
protected updated(changedProperties: PropertyValues): void {
|
||||||
if (
|
if (
|
||||||
changedProperties.has("selector") &&
|
changedProperties.has("selector") &&
|
||||||
(this.selector.area.device?.integration ||
|
(this.selector.area.device?.integration ||
|
||||||
|
@ -58,7 +65,7 @@ export class HaAreaSelector extends SubscribeMixin(LitElement) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected render() {
|
protected render(): TemplateResult {
|
||||||
if (
|
if (
|
||||||
(this.selector.area.device?.integration ||
|
(this.selector.area.device?.integration ||
|
||||||
this.selector.area.entity?.integration) &&
|
this.selector.area.entity?.integration) &&
|
||||||
|
@ -77,12 +84,6 @@ export class HaAreaSelector extends SubscribeMixin(LitElement) {
|
||||||
no-add
|
no-add
|
||||||
.deviceFilter=${this._filterDevices}
|
.deviceFilter=${this._filterDevices}
|
||||||
.entityFilter=${this._filterEntities}
|
.entityFilter=${this._filterEntities}
|
||||||
.includeDeviceClasses=${this.selector.area.entity?.device_class
|
|
||||||
? [this.selector.area.entity.device_class]
|
|
||||||
: undefined}
|
|
||||||
.includeDomains=${this.selector.area.entity?.domain
|
|
||||||
? [this.selector.area.entity.domain]
|
|
||||||
: undefined}
|
|
||||||
.disabled=${this.disabled}
|
.disabled=${this.disabled}
|
||||||
.required=${this.required}
|
.required=${this.required}
|
||||||
></ha-area-picker>
|
></ha-area-picker>
|
||||||
|
@ -98,27 +99,22 @@ export class HaAreaSelector extends SubscribeMixin(LitElement) {
|
||||||
no-add
|
no-add
|
||||||
.deviceFilter=${this._filterDevices}
|
.deviceFilter=${this._filterDevices}
|
||||||
.entityFilter=${this._filterEntities}
|
.entityFilter=${this._filterEntities}
|
||||||
.includeDeviceClasses=${this.selector.area.entity?.device_class
|
|
||||||
? [this.selector.area.entity.device_class]
|
|
||||||
: undefined}
|
|
||||||
.includeDomains=${this.selector.area.entity?.domain
|
|
||||||
? [this.selector.area.entity.domain]
|
|
||||||
: undefined}
|
|
||||||
.disabled=${this.disabled}
|
.disabled=${this.disabled}
|
||||||
.required=${this.required}
|
.required=${this.required}
|
||||||
></ha-areas-picker>
|
></ha-areas-picker>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _filterEntities = (entity: EntityRegistryEntry): boolean => {
|
private _filterEntities = (entity: HassEntity): boolean => {
|
||||||
const filterIntegration = this.selector.area.entity?.integration;
|
if (!this.selector.area.entity) {
|
||||||
if (
|
return true;
|
||||||
filterIntegration &&
|
|
||||||
this._entitySources?.[entity.entity_id]?.domain !== filterIntegration
|
|
||||||
) {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
|
return filterSelectorEntities(
|
||||||
|
this.selector.area.entity,
|
||||||
|
entity,
|
||||||
|
this._entitySources
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
private _filterDevices = (device: DeviceRegistryEntry): boolean => {
|
private _filterDevices = (device: DeviceRegistryEntry): boolean => {
|
||||||
|
@ -126,47 +122,17 @@ export class HaAreaSelector extends SubscribeMixin(LitElement) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const {
|
const deviceIntegrations =
|
||||||
manufacturer: filterManufacturer,
|
this._entitySources && this._entities
|
||||||
model: filterModel,
|
? this._deviceIntegrationLookup(this._entitySources, this._entities)
|
||||||
integration: filterIntegration,
|
: undefined;
|
||||||
} = this.selector.area.device;
|
|
||||||
|
|
||||||
if (filterManufacturer && device.manufacturer !== filterManufacturer) {
|
return filterSelectorDevices(
|
||||||
return false;
|
this.selector.area.device,
|
||||||
}
|
device,
|
||||||
if (filterModel && device.model !== filterModel) {
|
deviceIntegrations
|
||||||
return false;
|
);
|
||||||
}
|
|
||||||
if (filterIntegration && this._entitySources && this._entities) {
|
|
||||||
const deviceIntegrations = this._deviceIntegrations(
|
|
||||||
this._entitySources,
|
|
||||||
this._entities
|
|
||||||
);
|
|
||||||
if (!deviceIntegrations?.[device.id]?.includes(filterIntegration)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
private _deviceIntegrations = memoizeOne(
|
|
||||||
(entitySources: EntitySources, entities: EntityRegistryEntry[]) => {
|
|
||||||
const deviceIntegrations: Record<string, string[]> = {};
|
|
||||||
|
|
||||||
for (const entity of entities) {
|
|
||||||
const source = entitySources[entity.entity_id];
|
|
||||||
if (!source?.domain) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!deviceIntegrations[entity.device_id!]) {
|
|
||||||
deviceIntegrations[entity.device_id!] = [];
|
|
||||||
}
|
|
||||||
deviceIntegrations[entity.device_id!].push(source.domain);
|
|
||||||
}
|
|
||||||
return deviceIntegrations;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
|
|
|
@ -2,8 +2,8 @@ import { UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||||
import { html, LitElement } from "lit";
|
import { html, LitElement } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
import { ConfigEntry } from "../../data/config_entries";
|
|
||||||
import type { DeviceRegistryEntry } from "../../data/device_registry";
|
import type { DeviceRegistryEntry } from "../../data/device_registry";
|
||||||
|
import { getDeviceIntegrationLookup } from "../../data/device_registry";
|
||||||
import {
|
import {
|
||||||
EntityRegistryEntry,
|
EntityRegistryEntry,
|
||||||
subscribeEntityRegistry,
|
subscribeEntityRegistry,
|
||||||
|
@ -13,6 +13,7 @@ import {
|
||||||
fetchEntitySourcesWithCache,
|
fetchEntitySourcesWithCache,
|
||||||
} from "../../data/entity_sources";
|
} from "../../data/entity_sources";
|
||||||
import type { DeviceSelector } from "../../data/selector";
|
import type { DeviceSelector } from "../../data/selector";
|
||||||
|
import { filterSelectorDevices } from "../../data/selector";
|
||||||
import { SubscribeMixin } from "../../mixins/subscribe-mixin";
|
import { SubscribeMixin } from "../../mixins/subscribe-mixin";
|
||||||
import type { HomeAssistant } from "../../types";
|
import type { HomeAssistant } from "../../types";
|
||||||
import "../device/ha-device-picker";
|
import "../device/ha-device-picker";
|
||||||
|
@ -34,12 +35,12 @@ export class HaDeviceSelector extends SubscribeMixin(LitElement) {
|
||||||
|
|
||||||
@property() public helper?: string;
|
@property() public helper?: string;
|
||||||
|
|
||||||
@state() public _configEntries?: ConfigEntry[];
|
|
||||||
|
|
||||||
@property({ type: Boolean }) public disabled = false;
|
@property({ type: Boolean }) public disabled = false;
|
||||||
|
|
||||||
@property({ type: Boolean }) public required = true;
|
@property({ type: Boolean }) public required = true;
|
||||||
|
|
||||||
|
private _deviceIntegrationLookup = memoizeOne(getDeviceIntegrationLookup);
|
||||||
|
|
||||||
public hassSubscribe(): UnsubscribeFunc[] {
|
public hassSubscribe(): UnsubscribeFunc[] {
|
||||||
return [
|
return [
|
||||||
subscribeEntityRegistry(this.hass.connection!, (entities) => {
|
subscribeEntityRegistry(this.hass.connection!, (entities) => {
|
||||||
|
@ -107,48 +108,17 @@ export class HaDeviceSelector extends SubscribeMixin(LitElement) {
|
||||||
}
|
}
|
||||||
|
|
||||||
private _filterDevices = (device: DeviceRegistryEntry): boolean => {
|
private _filterDevices = (device: DeviceRegistryEntry): boolean => {
|
||||||
const {
|
const deviceIntegrations =
|
||||||
manufacturer: filterManufacturer,
|
this._entitySources && this._entities
|
||||||
model: filterModel,
|
? this._deviceIntegrationLookup(this._entitySources, this._entities)
|
||||||
integration: filterIntegration,
|
: undefined;
|
||||||
} = this.selector.device;
|
|
||||||
|
|
||||||
if (filterManufacturer && device.manufacturer !== filterManufacturer) {
|
return filterSelectorDevices(
|
||||||
return false;
|
this.selector.device,
|
||||||
}
|
device,
|
||||||
if (filterModel && device.model !== filterModel) {
|
deviceIntegrations
|
||||||
return false;
|
);
|
||||||
}
|
|
||||||
if (filterIntegration && this._entitySources && this._entities) {
|
|
||||||
const deviceIntegrations = this._deviceIntegrations(
|
|
||||||
this._entitySources,
|
|
||||||
this._entities
|
|
||||||
);
|
|
||||||
if (!deviceIntegrations?.[device.id]?.includes(filterIntegration)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
private _deviceIntegrations = memoizeOne(
|
|
||||||
(entitySources: EntitySources, entities: EntityRegistryEntry[]) => {
|
|
||||||
const deviceIntegrations: Record<string, string[]> = {};
|
|
||||||
|
|
||||||
for (const entity of entities) {
|
|
||||||
const source = entitySources[entity.entity_id];
|
|
||||||
if (!source?.domain) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!deviceIntegrations[entity.device_id!]) {
|
|
||||||
deviceIntegrations[entity.device_id!] = [];
|
|
||||||
}
|
|
||||||
deviceIntegrations[entity.device_id!].push(source.domain);
|
|
||||||
}
|
|
||||||
return deviceIntegrations;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
import { HassEntity } from "home-assistant-js-websocket";
|
import { HassEntity } from "home-assistant-js-websocket";
|
||||||
import { html, LitElement, PropertyValues } from "lit";
|
import { html, LitElement, PropertyValues } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { computeStateDomain } from "../../common/entity/compute_state_domain";
|
|
||||||
import {
|
import {
|
||||||
EntitySources,
|
EntitySources,
|
||||||
fetchEntitySourcesWithCache,
|
fetchEntitySourcesWithCache,
|
||||||
} from "../../data/entity_sources";
|
} from "../../data/entity_sources";
|
||||||
import { EntitySelector } from "../../data/selector";
|
import type { EntitySelector } from "../../data/selector";
|
||||||
|
import { filterSelectorEntities } from "../../data/selector";
|
||||||
import { HomeAssistant } from "../../types";
|
import { HomeAssistant } from "../../types";
|
||||||
import "../entity/ha-entities-picker";
|
import "../entity/ha-entities-picker";
|
||||||
import "../entity/ha-entity-picker";
|
import "../entity/ha-entity-picker";
|
||||||
|
@ -73,37 +73,8 @@ export class HaEntitySelector extends LitElement {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _filterEntities = (entity: HassEntity): boolean => {
|
private _filterEntities = (entity: HassEntity): boolean =>
|
||||||
const {
|
filterSelectorEntities(this.selector.entity, entity, this._entitySources);
|
||||||
domain: filterDomain,
|
|
||||||
device_class: filterDeviceClass,
|
|
||||||
integration: filterIntegration,
|
|
||||||
} = this.selector.entity;
|
|
||||||
|
|
||||||
if (filterDomain) {
|
|
||||||
const entityDomain = computeStateDomain(entity);
|
|
||||||
if (
|
|
||||||
Array.isArray(filterDomain)
|
|
||||||
? !filterDomain.includes(entityDomain)
|
|
||||||
: entityDomain !== filterDomain
|
|
||||||
) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
filterDeviceClass &&
|
|
||||||
entity.attributes.device_class !== filterDeviceClass
|
|
||||||
) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
filterIntegration &&
|
|
||||||
this._entitySources?.[entity.entity_id]?.domain !== filterIntegration
|
|
||||||
) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
|
|
|
@ -3,17 +3,33 @@ import {
|
||||||
HassServiceTarget,
|
HassServiceTarget,
|
||||||
UnsubscribeFunc,
|
UnsubscribeFunc,
|
||||||
} from "home-assistant-js-websocket";
|
} from "home-assistant-js-websocket";
|
||||||
import { css, CSSResultGroup, html, LitElement } from "lit";
|
|
||||||
import { customElement, property, state } from "lit/decorators";
|
|
||||||
import { ConfigEntry, getConfigEntries } from "../../data/config_entries";
|
|
||||||
import { DeviceRegistryEntry } from "../../data/device_registry";
|
|
||||||
import {
|
import {
|
||||||
EntityRegistryEntry,
|
css,
|
||||||
subscribeEntityRegistry,
|
CSSResultGroup,
|
||||||
} from "../../data/entity_registry";
|
html,
|
||||||
import { TargetSelector } from "../../data/selector";
|
LitElement,
|
||||||
|
PropertyValues,
|
||||||
|
TemplateResult,
|
||||||
|
} from "lit";
|
||||||
|
import { customElement, property, state } from "lit/decorators";
|
||||||
|
import memoizeOne from "memoize-one";
|
||||||
|
import {
|
||||||
|
DeviceRegistryEntry,
|
||||||
|
getDeviceIntegrationLookup,
|
||||||
|
} from "../../data/device_registry";
|
||||||
|
import type { EntityRegistryEntry } from "../../data/entity_registry";
|
||||||
|
import { subscribeEntityRegistry } from "../../data/entity_registry";
|
||||||
|
import {
|
||||||
|
EntitySources,
|
||||||
|
fetchEntitySourcesWithCache,
|
||||||
|
} from "../../data/entity_sources";
|
||||||
|
import {
|
||||||
|
filterSelectorDevices,
|
||||||
|
filterSelectorEntities,
|
||||||
|
TargetSelector,
|
||||||
|
} from "../../data/selector";
|
||||||
import { SubscribeMixin } from "../../mixins/subscribe-mixin";
|
import { SubscribeMixin } from "../../mixins/subscribe-mixin";
|
||||||
import { HomeAssistant } from "../../types";
|
import type { HomeAssistant } from "../../types";
|
||||||
import "../ha-target-picker";
|
import "../ha-target-picker";
|
||||||
|
|
||||||
@customElement("ha-selector-target")
|
@customElement("ha-selector-target")
|
||||||
|
@ -28,119 +44,82 @@ export class HaTargetSelector extends SubscribeMixin(LitElement) {
|
||||||
|
|
||||||
@property() public helper?: string;
|
@property() public helper?: string;
|
||||||
|
|
||||||
@state() private _entityPlaformLookup?: Record<string, string>;
|
|
||||||
|
|
||||||
@state() private _configEntries?: ConfigEntry[];
|
|
||||||
|
|
||||||
@property({ type: Boolean }) public disabled = false;
|
@property({ type: Boolean }) public disabled = false;
|
||||||
|
|
||||||
|
@state() private _entitySources?: EntitySources;
|
||||||
|
|
||||||
|
@state() private _entities?: EntityRegistryEntry[];
|
||||||
|
|
||||||
|
private _deviceIntegrationLookup = memoizeOne(getDeviceIntegrationLookup);
|
||||||
|
|
||||||
public hassSubscribe(): UnsubscribeFunc[] {
|
public hassSubscribe(): UnsubscribeFunc[] {
|
||||||
return [
|
return [
|
||||||
subscribeEntityRegistry(this.hass.connection!, (entities) => {
|
subscribeEntityRegistry(this.hass.connection!, (entities) => {
|
||||||
const entityLookup = {};
|
this._entities = entities.filter((entity) => entity.device_id !== null);
|
||||||
for (const confEnt of entities) {
|
|
||||||
if (!confEnt.platform) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
entityLookup[confEnt.entity_id] = confEnt.platform;
|
|
||||||
}
|
|
||||||
this._entityPlaformLookup = entityLookup;
|
|
||||||
}),
|
}),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
protected updated(changedProperties) {
|
protected updated(changedProperties: PropertyValues): void {
|
||||||
if (changedProperties.has("selector")) {
|
super.updated(changedProperties);
|
||||||
const oldSelector = changedProperties.get("selector");
|
if (
|
||||||
if (
|
changedProperties.has("selector") &&
|
||||||
oldSelector !== this.selector &&
|
this.selector.target.device?.integration &&
|
||||||
(this.selector.target.device?.integration ||
|
!this._entitySources
|
||||||
this.selector.target.entity?.integration)
|
) {
|
||||||
) {
|
fetchEntitySourcesWithCache(this.hass).then((sources) => {
|
||||||
this._loadConfigEntries();
|
this._entitySources = sources;
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected render() {
|
protected render(): TemplateResult {
|
||||||
|
if (
|
||||||
|
(this.selector.target.device?.integration ||
|
||||||
|
this.selector.target.entity?.integration) &&
|
||||||
|
!this._entitySources
|
||||||
|
) {
|
||||||
|
return html``;
|
||||||
|
}
|
||||||
|
|
||||||
return html`<ha-target-picker
|
return html`<ha-target-picker
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.value=${this.value}
|
.value=${this.value}
|
||||||
.helper=${this.helper}
|
.helper=${this.helper}
|
||||||
.deviceFilter=${this._filterDevices}
|
.deviceFilter=${this._filterDevices}
|
||||||
.entityRegFilter=${this._filterRegEntities}
|
|
||||||
.entityFilter=${this._filterEntities}
|
.entityFilter=${this._filterEntities}
|
||||||
.includeDeviceClasses=${this.selector.target.entity?.device_class
|
|
||||||
? [this.selector.target.entity.device_class]
|
|
||||||
: undefined}
|
|
||||||
.includeDomains=${this.selector.target.entity?.domain
|
|
||||||
? [this.selector.target.entity.domain]
|
|
||||||
: undefined}
|
|
||||||
.disabled=${this.disabled}
|
.disabled=${this.disabled}
|
||||||
></ha-target-picker>`;
|
></ha-target-picker>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _filterEntities = (entity: HassEntity): boolean => {
|
private _filterEntities = (entity: HassEntity): boolean => {
|
||||||
if (
|
if (!this.selector.target.entity) {
|
||||||
this.selector.target.entity?.integration ||
|
return true;
|
||||||
this.selector.target.device?.integration
|
|
||||||
) {
|
|
||||||
if (
|
|
||||||
!this._entityPlaformLookup ||
|
|
||||||
this._entityPlaformLookup[entity.entity_id] !==
|
|
||||||
(this.selector.target.entity?.integration ||
|
|
||||||
this.selector.target.device?.integration)
|
|
||||||
) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
private _filterRegEntities = (entity: EntityRegistryEntry): boolean => {
|
return filterSelectorEntities(
|
||||||
if (this.selector.target.entity?.integration) {
|
this.selector.target.entity,
|
||||||
if (entity.platform !== this.selector.target.entity.integration) {
|
entity,
|
||||||
return false;
|
this._entitySources
|
||||||
}
|
);
|
||||||
}
|
|
||||||
return true;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
private _filterDevices = (device: DeviceRegistryEntry): boolean => {
|
private _filterDevices = (device: DeviceRegistryEntry): boolean => {
|
||||||
if (
|
if (!this.selector.target.device) {
|
||||||
this.selector.target.device?.manufacturer &&
|
return true;
|
||||||
device.manufacturer !== this.selector.target.device.manufacturer
|
|
||||||
) {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
if (
|
|
||||||
this.selector.target.device?.model &&
|
|
||||||
device.model !== this.selector.target.device.model
|
|
||||||
) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
this.selector.target.device?.integration ||
|
|
||||||
this.selector.target.entity?.integration
|
|
||||||
) {
|
|
||||||
if (
|
|
||||||
!this._configEntries?.some((entry) =>
|
|
||||||
device.config_entries.includes(entry.entry_id)
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
private async _loadConfigEntries() {
|
const deviceIntegrations =
|
||||||
this._configEntries = (await getConfigEntries(this.hass)).filter(
|
this._entitySources && this._entities
|
||||||
(entry) =>
|
? this._deviceIntegrationLookup(this._entitySources, this._entities)
|
||||||
entry.domain === this.selector.target.device?.integration ||
|
: undefined;
|
||||||
entry.domain === this.selector.target.entity?.integration
|
|
||||||
|
return filterSelectorDevices(
|
||||||
|
this.selector.target.device,
|
||||||
|
device,
|
||||||
|
deviceIntegrations
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
static get styles(): CSSResultGroup {
|
||||||
return css`
|
return css`
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
import { Connection, createCollection } from "home-assistant-js-websocket";
|
import { Connection, createCollection } from "home-assistant-js-websocket";
|
||||||
import { Store } from "home-assistant-js-websocket/dist/store";
|
import type { Store } from "home-assistant-js-websocket/dist/store";
|
||||||
import { computeStateName } from "../common/entity/compute_state_name";
|
import { computeStateName } from "../common/entity/compute_state_name";
|
||||||
import { caseInsensitiveStringCompare } from "../common/string/compare";
|
import { caseInsensitiveStringCompare } from "../common/string/compare";
|
||||||
import { debounce } from "../common/util/debounce";
|
import { debounce } from "../common/util/debounce";
|
||||||
import { HomeAssistant } from "../types";
|
import type { HomeAssistant } from "../types";
|
||||||
import { EntityRegistryEntry } from "./entity_registry";
|
import type { EntityRegistryEntry } from "./entity_registry";
|
||||||
|
import type { EntitySources } from "./entity_sources";
|
||||||
|
|
||||||
export interface DeviceRegistryEntry {
|
export interface DeviceRegistryEntry {
|
||||||
id: string;
|
id: string;
|
||||||
|
@ -142,3 +143,23 @@ export const getDeviceEntityLookup = (
|
||||||
}
|
}
|
||||||
return deviceEntityLookup;
|
return deviceEntityLookup;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getDeviceIntegrationLookup = (
|
||||||
|
entitySources: EntitySources,
|
||||||
|
entities: EntityRegistryEntry[]
|
||||||
|
): Record<string, string[]> => {
|
||||||
|
const deviceIntegrations: Record<string, string[]> = {};
|
||||||
|
|
||||||
|
for (const entity of entities) {
|
||||||
|
const source = entitySources[entity.entity_id];
|
||||||
|
if (!source?.domain || entity.device_id === null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!deviceIntegrations[entity.device_id!]) {
|
||||||
|
deviceIntegrations[entity.device_id!] = [];
|
||||||
|
}
|
||||||
|
deviceIntegrations[entity.device_id!].push(source.domain);
|
||||||
|
}
|
||||||
|
return deviceIntegrations;
|
||||||
|
};
|
||||||
|
|
|
@ -160,3 +160,16 @@ export const sortEntityRegistryByName = (entries: EntityRegistryEntry[]) =>
|
||||||
entries.sort((entry1, entry2) =>
|
entries.sort((entry1, entry2) =>
|
||||||
caseInsensitiveStringCompare(entry1.name || "", entry2.name || "")
|
caseInsensitiveStringCompare(entry1.name || "", entry2.name || "")
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const getEntityPlatformLookup = (
|
||||||
|
entities: EntityRegistryEntry[]
|
||||||
|
): Record<string, string> => {
|
||||||
|
const entityLookup = {};
|
||||||
|
for (const confEnt of entities) {
|
||||||
|
if (!confEnt.platform) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
entityLookup[confEnt.entity_id] = confEnt.platform;
|
||||||
|
}
|
||||||
|
return entityLookup;
|
||||||
|
};
|
||||||
|
|
|
@ -1,3 +1,8 @@
|
||||||
|
import type { HassEntity } from "home-assistant-js-websocket";
|
||||||
|
import { computeStateDomain } from "../common/entity/compute_state_domain";
|
||||||
|
import type { DeviceRegistryEntry } from "./device_registry";
|
||||||
|
import type { EntitySources } from "./entity_sources";
|
||||||
|
|
||||||
export type Selector =
|
export type Selector =
|
||||||
| ActionSelector
|
| ActionSelector
|
||||||
| AddonSelector
|
| AddonSelector
|
||||||
|
@ -35,18 +40,22 @@ export interface AddonSelector {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface SelectorDevice {
|
||||||
|
integration?: DeviceSelector["device"]["integration"];
|
||||||
|
manufacturer?: DeviceSelector["device"]["manufacturer"];
|
||||||
|
model?: DeviceSelector["device"]["model"];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SelectorEntity {
|
||||||
|
integration?: EntitySelector["entity"]["integration"];
|
||||||
|
domain?: EntitySelector["entity"]["domain"];
|
||||||
|
device_class?: EntitySelector["entity"]["device_class"];
|
||||||
|
}
|
||||||
|
|
||||||
export interface AreaSelector {
|
export interface AreaSelector {
|
||||||
area: {
|
area: {
|
||||||
entity?: {
|
entity?: SelectorEntity;
|
||||||
integration?: EntitySelector["entity"]["integration"];
|
device?: SelectorDevice;
|
||||||
domain?: EntitySelector["entity"]["domain"];
|
|
||||||
device_class?: EntitySelector["entity"]["device_class"];
|
|
||||||
};
|
|
||||||
device?: {
|
|
||||||
integration?: DeviceSelector["device"]["integration"];
|
|
||||||
manufacturer?: DeviceSelector["device"]["manufacturer"];
|
|
||||||
model?: DeviceSelector["device"]["model"];
|
|
||||||
};
|
|
||||||
multiple?: boolean;
|
multiple?: boolean;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -89,10 +98,7 @@ export interface DeviceSelector {
|
||||||
integration?: string;
|
integration?: string;
|
||||||
manufacturer?: string;
|
manufacturer?: string;
|
||||||
model?: string;
|
model?: string;
|
||||||
entity?: {
|
entity?: SelectorEntity;
|
||||||
domain?: EntitySelector["entity"]["domain"];
|
|
||||||
device_class?: EntitySelector["entity"]["device_class"];
|
|
||||||
};
|
|
||||||
multiple?: boolean;
|
multiple?: boolean;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -201,16 +207,8 @@ export interface StringSelector {
|
||||||
|
|
||||||
export interface TargetSelector {
|
export interface TargetSelector {
|
||||||
target: {
|
target: {
|
||||||
entity?: {
|
entity?: SelectorEntity;
|
||||||
integration?: EntitySelector["entity"]["integration"];
|
device?: SelectorDevice;
|
||||||
domain?: EntitySelector["entity"]["domain"];
|
|
||||||
device_class?: EntitySelector["entity"]["device_class"];
|
|
||||||
};
|
|
||||||
device?: {
|
|
||||||
integration?: DeviceSelector["device"]["integration"];
|
|
||||||
manufacturer?: DeviceSelector["device"]["manufacturer"];
|
|
||||||
model?: DeviceSelector["device"]["model"];
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -227,3 +225,69 @@ export interface TimeSelector {
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||||
time: {};
|
time: {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const filterSelectorDevices = (
|
||||||
|
filterDevice: SelectorDevice,
|
||||||
|
device: DeviceRegistryEntry,
|
||||||
|
deviceIntegrationLookup: Record<string, string[]> | undefined
|
||||||
|
): boolean => {
|
||||||
|
const {
|
||||||
|
manufacturer: filterManufacturer,
|
||||||
|
model: filterModel,
|
||||||
|
integration: filterIntegration,
|
||||||
|
} = filterDevice;
|
||||||
|
|
||||||
|
if (filterManufacturer && device.manufacturer !== filterManufacturer) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filterModel && device.model !== filterModel) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filterIntegration && deviceIntegrationLookup) {
|
||||||
|
if (!deviceIntegrationLookup?.[device.id]?.includes(filterIntegration)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const filterSelectorEntities = (
|
||||||
|
filterEntity: SelectorEntity,
|
||||||
|
entity: HassEntity,
|
||||||
|
entitySources?: EntitySources
|
||||||
|
): boolean => {
|
||||||
|
const {
|
||||||
|
domain: filterDomain,
|
||||||
|
device_class: filterDeviceClass,
|
||||||
|
integration: filterIntegration,
|
||||||
|
} = filterEntity;
|
||||||
|
|
||||||
|
if (filterDomain) {
|
||||||
|
const entityDomain = computeStateDomain(entity);
|
||||||
|
if (
|
||||||
|
Array.isArray(filterDomain)
|
||||||
|
? !filterDomain.includes(entityDomain)
|
||||||
|
: entityDomain !== filterDomain
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
filterDeviceClass &&
|
||||||
|
entity.attributes.device_class !== filterDeviceClass
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
filterIntegration &&
|
||||||
|
entitySources?.[entity.entity_id]?.domain !== filterIntegration
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
Loading…
Reference in New Issue