Find Entites: Filter Unavailable - Used and unused entities required (#5208)

* Update Find Entities

* lint

* Filter until max - entitiesFill-> entitiesFallback

* Comment - Move unav - unk filter to picker

* lint

* Var name update

* remove unnessary check

* add it back and fix logic

* use typescript ? in condition

* comments

* lint

* Compute used once

* pass in hass

* Return set

* fix merge

* Optimize unused entities in find
This commit is contained in:
Zack Arnett 2020-03-19 14:39:14 -04:00 committed by GitHub
parent eacf58b5a5
commit f4211e3fa3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 149 additions and 155 deletions

View File

@ -25,7 +25,6 @@ import { AlarmPanelCardConfig } from "./types";
import { PaperInputElement } from "@polymer/paper-input/paper-input";
import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element";
import { findEntities } from "../common/find-entites";
import { LovelaceConfig } from "../../../data/lovelace";
import { fireEvent } from "../../../common/dom/fire_event";
const ICONS = {
@ -51,18 +50,16 @@ class HuiAlarmPanelCard extends LitElement implements LovelaceCard {
public static getStubConfig(
hass: HomeAssistant,
lovelaceConfig: LovelaceConfig,
entities?: string[],
entitiesFill?: string[]
entities: string[],
entitiesFallback: string[]
) {
const includeDomains = ["alarm_control_panel"];
const maxEntities = 1;
const foundEntities = findEntities(
hass,
lovelaceConfig,
maxEntities,
entities,
entitiesFill,
entitiesFallback,
includeDomains
);

View File

@ -30,7 +30,7 @@ import { ButtonCardConfig } from "./types";
import { actionHandler } from "../common/directives/action-handler-directive";
import { hasAction } from "../common/has-action";
import { handleAction } from "../common/handle-action";
import { ActionHandlerEvent, LovelaceConfig } from "../../../data/lovelace";
import { ActionHandlerEvent } from "../../../data/lovelace";
import { computeActiveState } from "../../../common/entity/compute_active_state";
import { iconColorCSS } from "../../../common/style/icon_color_css";
import { findEntities } from "../common/find-entites";
@ -46,17 +46,15 @@ export class HuiButtonCard extends LitElement implements LovelaceCard {
public static getStubConfig(
hass: HomeAssistant,
lovelaceConfig: LovelaceConfig,
entities?: string[],
entitiesFill?: string[]
entities: string[],
entitiesFallback: string[]
): object {
const maxEntities = 1;
const foundEntities = findEntities(
hass,
lovelaceConfig,
maxEntities,
entities,
entitiesFill,
entitiesFallback,
["light", "switch"]
);

View File

@ -27,7 +27,6 @@ import { createHeaderFooterElement } from "../create-element/create-header-foote
import { LovelaceHeaderFooterConfig } from "../header-footer/types";
import { DOMAINS_TOGGLE } from "../../../common/const";
import { computeDomain } from "../../../common/entity/compute_domain";
import { LovelaceConfig } from "../../../data/lovelace";
import { findEntities } from "../common/find-entites";
@customElement("hui-entities-card")
@ -41,17 +40,15 @@ class HuiEntitiesCard extends LitElement implements LovelaceCard {
public static getStubConfig(
hass: HomeAssistant,
lovelaceConfig: LovelaceConfig,
entities?: string[],
entitiesFill?: string[]
entities: string[],
entitiesFallback: string[]
) {
const maxEntities = 3;
const foundEntities = findEntities(
hass,
lovelaceConfig,
maxEntities,
entities,
entitiesFill,
entitiesFallback,
["light", "switch", "sensor"]
);

View File

@ -22,7 +22,6 @@ import { fireEvent } from "../../../common/dom/fire_event";
import { hasConfigOrEntityChanged } from "../common/has-changed";
import { LovelaceCard, LovelaceCardEditor } from "../types";
import { GaugeCardConfig } from "./types";
import { LovelaceConfig } from "../../../data/lovelace";
import { findEntities } from "../common/find-entites";
import { HassEntity } from "home-assistant-js-websocket/dist/types";
@ -44,9 +43,8 @@ class HuiGaugeCard extends LitElement implements LovelaceCard {
public static getStubConfig(
hass: HomeAssistant,
lovelaceConfig: LovelaceConfig,
entities?: string[],
entitiesFill?: string[]
entities: string[],
entitiesFallback: string[]
): object {
const includeDomains = ["sensor"];
const maxEntities = 1;
@ -56,10 +54,9 @@ class HuiGaugeCard extends LitElement implements LovelaceCard {
const foundEntities = findEntities(
hass,
lovelaceConfig,
maxEntities,
entities,
entitiesFill,
entitiesFallback,
includeDomains,
entityFilter
);

View File

@ -27,7 +27,7 @@ import { processConfigEntities } from "../common/process-config-entities";
import { GlanceCardConfig, GlanceConfigEntity } from "./types";
import { actionHandler } from "../common/directives/action-handler-directive";
import { hasAction } from "../common/has-action";
import { ActionHandlerEvent, LovelaceConfig } from "../../../data/lovelace";
import { ActionHandlerEvent } from "../../../data/lovelace";
import { handleAction } from "../common/handle-action";
import { computeDomain } from "../../../common/entity/compute_domain";
import { UNAVAILABLE, UNKNOWN } from "../../../data/entity";
@ -44,18 +44,16 @@ export class HuiGlanceCard extends LitElement implements LovelaceCard {
public static getStubConfig(
hass: HomeAssistant,
lovelaceConfig: LovelaceConfig,
entities?: string[],
entitiesFill?: string[]
entities: string[],
entitiesFallback: string[]
): GlanceCardConfig {
const includeDomains = ["sensor"];
const maxEntities = 3;
const foundEntities = findEntities(
hass,
lovelaceConfig,
maxEntities,
entities,
entitiesFill,
entitiesFallback,
includeDomains
);

View File

@ -20,7 +20,6 @@ import { HomeAssistant } from "../../../types";
import { HistoryGraphCardConfig } from "./types";
import { LovelaceCard } from "../types";
import { EntityConfig } from "../entity-rows/types";
import { LovelaceConfig } from "../../../data/lovelace";
import { findEntities } from "../common/find-entites";
@customElement("hui-history-graph-card")
@ -34,18 +33,16 @@ export class HuiHistoryGraphCard extends LitElement implements LovelaceCard {
public static getStubConfig(
hass: HomeAssistant,
lovelaceConfig: LovelaceConfig,
entities?: string[],
entitiesFill?: string[]
entities: string[],
entitiesFallback: string[]
): object {
const includeDomains = ["sensor"];
const maxEntities = 1;
const foundEntities = findEntities(
hass,
lovelaceConfig,
maxEntities,
entities,
entitiesFill,
entitiesFallback,
includeDomains
);

View File

@ -29,7 +29,6 @@ import { toggleEntity } from "../common/entity/toggle-entity";
import { LightCardConfig } from "./types";
import { supportsFeature } from "../../../common/entity/supports-feature";
import { SUPPORT_BRIGHTNESS } from "../../../data/light";
import { LovelaceConfig } from "../../../data/lovelace";
import { findEntities } from "../common/find-entites";
import { UNAVAILABLE } from "../../../data/entity";
@ -44,18 +43,16 @@ export class HuiLightCard extends LitElement implements LovelaceCard {
public static getStubConfig(
hass: HomeAssistant,
lovelaceConfig: LovelaceConfig,
entities?: string[],
entitiesFill?: string[]
entities: string[],
entitiesFallback: string[]
): object {
const includeDomains = ["light"];
const maxEntities = 1;
const foundEntities = findEntities(
hass,
lovelaceConfig,
maxEntities,
entities,
entitiesFill,
entitiesFallback,
includeDomains
);

View File

@ -30,7 +30,6 @@ import { EntityConfig } from "../entity-rows/types";
import { processConfigEntities } from "../common/process-config-entities";
import { MapCardConfig } from "./types";
import { classMap } from "lit-html/directives/class-map";
import { LovelaceConfig } from "../../../data/lovelace";
import { findEntities } from "../common/find-entites";
@customElement("hui-map-card")
@ -44,18 +43,16 @@ class HuiMapCard extends LitElement implements LovelaceCard {
public static getStubConfig(
hass: HomeAssistant,
lovelaceConfig: LovelaceConfig,
entities?: string[],
entitiesFill?: string[]
entities: string[],
entitiesFallback: string[]
): object {
const includeDomains = ["device_tracker"];
const maxEntities = 2;
const foundEntities = findEntities(
hass,
lovelaceConfig,
maxEntities,
entities,
entitiesFill,
entitiesFallback,
includeDomains
);

View File

@ -30,7 +30,6 @@ import { stateIcon } from "../../../common/entity/state_icon";
import { hasConfigOrEntityChanged } from "../common/has-changed";
import { contrast } from "../common/color/contrast";
import { findEntities } from "../common/find-entites";
import { LovelaceConfig } from "../../../data/lovelace";
import { UNAVAILABLE, UNKNOWN } from "../../../data/entity";
import {
SUPPORT_PAUSE,
@ -176,18 +175,16 @@ export class HuiMediaControlCard extends LitElement implements LovelaceCard {
public static getStubConfig(
hass: HomeAssistant,
lovelaceConfig: LovelaceConfig,
entities?: string[],
entitiesFill?: string[]
entities: string[],
entitiesFallback: string[]
): object {
const includeDomains = ["media_player"];
const maxEntities = 1;
const foundEntities = findEntities(
hass,
lovelaceConfig,
maxEntities,
entities,
entitiesFill,
entitiesFallback,
includeDomains
);

View File

@ -15,24 +15,21 @@ import { HomeAssistant } from "../../../types";
import { LovelaceElementConfig, LovelaceElement } from "../elements/types";
import { PictureElementsCardConfig } from "./types";
import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element";
import { LovelaceConfig } from "../../../data/lovelace";
import { findEntities } from "../common/find-entites";
@customElement("hui-picture-elements-card")
class HuiPictureElementsCard extends LitElement implements LovelaceCard {
public static getStubConfig(
hass: HomeAssistant,
lovelaceConfig: LovelaceConfig,
entities?: string[],
entitiesFill?: string[]
entities: string[],
entitiesFallback: string[]
): PictureElementsCardConfig {
const maxEntities = 1;
const foundEntities = findEntities(
hass,
lovelaceConfig,
maxEntities,
entities,
entitiesFill,
entitiesFallback,
["sensor", "binary_sensor"]
);

View File

@ -27,7 +27,7 @@ import { PictureEntityCardConfig } from "./types";
import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element";
import { actionHandler } from "../common/directives/action-handler-directive";
import { hasAction } from "../common/has-action";
import { ActionHandlerEvent, LovelaceConfig } from "../../../data/lovelace";
import { ActionHandlerEvent } from "../../../data/lovelace";
import { handleAction } from "../common/handle-action";
import { findEntities } from "../common/find-entites";
@ -42,17 +42,15 @@ class HuiPictureEntityCard extends LitElement implements LovelaceCard {
public static getStubConfig(
hass: HomeAssistant,
lovelaceConfig: LovelaceConfig,
entities?: string[],
entitiesFill?: string[]
entities: string[],
entitiesFallback: string[]
): object {
const maxEntities = 1;
const foundEntities = findEntities(
hass,
lovelaceConfig,
maxEntities,
entities,
entitiesFill,
entitiesFallback,
["light", "switch"]
);

View File

@ -29,7 +29,7 @@ import { hasConfigOrEntityChanged } from "../common/has-changed";
import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element";
import { actionHandler } from "../common/directives/action-handler-directive";
import { hasAction } from "../common/has-action";
import { ActionHandlerEvent, LovelaceConfig } from "../../../data/lovelace";
import { ActionHandlerEvent } from "../../../data/lovelace";
import { handleAction } from "../common/handle-action";
import { findEntities } from "../common/find-entites";
@ -46,17 +46,15 @@ class HuiPictureGlanceCard extends LitElement implements LovelaceCard {
public static getStubConfig(
hass: HomeAssistant,
lovelaceConfig: LovelaceConfig,
entities?: string[],
entitiesFill?: string[]
entities: string[],
entitiesFallback: string[]
): PictureGlanceCardConfig {
const maxEntities = 2;
const foundEntities = findEntities(
hass,
lovelaceConfig,
maxEntities,
entities,
entitiesFill,
entitiesFallback,
["sensor", "binary_sensor"]
);

View File

@ -22,7 +22,6 @@ import { hasConfigOrEntityChanged } from "../common/has-changed";
import { PlantStatusCardConfig, PlantAttributeTarget } from "./types";
import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element";
import { actionHandler } from "../common/directives/action-handler-directive";
import { LovelaceConfig } from "../../../data/lovelace";
import { findEntities } from "../common/find-entites";
const SENSORS = {
@ -44,18 +43,16 @@ class HuiPlantStatusCard extends LitElement implements LovelaceCard {
public static getStubConfig(
hass: HomeAssistant,
lovelaceConfig: LovelaceConfig,
entities?: string[],
entitiesFill?: string[]
entities: string[],
entitiesFallback: string[]
): object {
const includeDomains = ["plant"];
const maxEntities = 1;
const foundEntities = findEntities(
hass,
lovelaceConfig,
maxEntities,
entities,
entitiesFill,
entitiesFallback,
includeDomains
);

View File

@ -26,7 +26,6 @@ import { fetchRecent } from "../../../data/history";
import { SensorCardConfig } from "./types";
import { hasConfigOrEntityChanged } from "../common/has-changed";
import { actionHandler } from "../common/directives/action-handler-directive";
import { LovelaceConfig } from "../../../data/lovelace";
import { findEntities } from "../common/find-entites";
import { HassEntity } from "home-assistant-js-websocket/dist/types";
@ -179,9 +178,8 @@ class HuiSensorCard extends LitElement implements LovelaceCard {
public static getStubConfig(
hass: HomeAssistant,
lovelaceConfig: LovelaceConfig,
entities?: string[],
entitiesFill?: string[]
entities: string[],
entitiesFallback: string[]
): object {
const includeDomains = ["sensor"];
const maxEntities = 1;
@ -194,10 +192,9 @@ class HuiSensorCard extends LitElement implements LovelaceCard {
const foundEntities = findEntities(
hass,
lovelaceConfig,
maxEntities,
entities,
entitiesFill,
entitiesFallback,
includeDomains,
entityFilter
);

View File

@ -34,7 +34,6 @@ import {
} from "../../../data/climate";
import { HassEntity } from "home-assistant-js-websocket";
import { actionHandler } from "../common/directives/action-handler-directive";
import { LovelaceConfig } from "../../../data/lovelace";
import { findEntities } from "../common/find-entites";
import { UNAVAILABLE } from "../../../data/entity";
@ -59,18 +58,16 @@ export class HuiThermostatCard extends LitElement implements LovelaceCard {
public static getStubConfig(
hass: HomeAssistant,
lovelaceConfig: LovelaceConfig,
entities?: string[],
entitiesFill?: string[]
entities: string[],
entitiesFallback: string[]
): object {
const includeDomains = ["climate"];
const maxEntities = 1;
const foundEntities = findEntities(
hass,
lovelaceConfig,
maxEntities,
entities,
entitiesFill,
entitiesFallback,
includeDomains
);

View File

@ -24,7 +24,6 @@ import { fireEvent } from "../../../common/dom/fire_event";
import { toggleAttribute } from "../../../common/dom/toggle_attribute";
import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element";
import { actionHandler } from "../common/directives/action-handler-directive";
import { LovelaceConfig } from "../../../data/lovelace";
import { findEntities } from "../common/find-entites";
const cardinalDirections = [
@ -76,18 +75,16 @@ class HuiWeatherForecastCard extends LitElement implements LovelaceCard {
public static getStubConfig(
hass: HomeAssistant,
lovelaceConfig: LovelaceConfig,
entities?: string[],
entitiesFill?: string[]
entities: string[],
entitiesFallback: string[]
): object {
const includeDomains = ["weather"];
const maxEntities = 1;
const foundEntities = findEntities(
hass,
lovelaceConfig,
maxEntities,
entities,
entitiesFill,
entitiesFallback,
includeDomains
);

View File

@ -61,7 +61,7 @@ const addEntities = (entities: Set<string>, obj) => {
}
};
export const computeUsedEntities = (config) => {
export const computeUsedEntities = (config: LovelaceConfig): Set<string> => {
const entities = new Set<string>();
config.views.forEach((view) => addEntities(entities, view));
return entities;
@ -70,13 +70,26 @@ export const computeUsedEntities = (config) => {
export const computeUnusedEntities = (
hass: HomeAssistant,
config: LovelaceConfig
): string[] => {
): Set<string> => {
const usedEntities = computeUsedEntities(config);
return Object.keys(hass.states)
.filter(
(entity) =>
!usedEntities.has(entity) &&
!EXCLUDED_DOMAINS.includes(entity.split(".", 1)[0])
)
.sort();
const unusedEntities = calcUnusedEntities(hass, usedEntities);
return unusedEntities;
};
export const calcUnusedEntities = (
hass: HomeAssistant,
usedEntities: Set<string>
): Set<string> => {
const unusedEntities: Set<string> = new Set();
for (const entity of Object.keys(hass.states)) {
if (
!usedEntities.has(entity) &&
!EXCLUDED_DOMAINS.includes(entity.split(".", 1)[0])
) {
unusedEntities.add(entity);
}
}
return unusedEntities;
};

View File

@ -1,59 +1,72 @@
import {
computeUnusedEntities,
computeUsedEntities,
} from "./compute-unused-entities";
import { HomeAssistant } from "../../../types";
import { LovelaceConfig } from "../../../data/lovelace";
import { computeDomain } from "../../../common/entity/compute_domain";
import { HassEntity } from "home-assistant-js-websocket";
const arrayFilter = (
array: any[],
conditions: Array<(value: any) => boolean>,
maxSize: number
) => {
if (!maxSize || maxSize > array.length) {
maxSize = array.length;
}
const filteredArray: any[] = [];
for (let i = 0; i < array.length && filteredArray.length < maxSize; i++) {
let meetsConditions = true;
for (const condition of conditions) {
if (!condition(array[i])) {
meetsConditions = false;
break;
}
}
if (meetsConditions) {
filteredArray.push(array[i]);
}
}
return filteredArray;
};
export const findEntities = (
hass: HomeAssistant,
lovelaceConfig: LovelaceConfig,
maxEntities: number,
entities?: string[],
entitiesFill?: string[],
entities: string[],
entitiesFallback: string[],
includeDomains?: string[],
entityFilter?: (stateObj: HassEntity) => boolean
) => {
let entityIds: string[];
entityIds = !entities?.length
? computeUnusedEntities(hass, lovelaceConfig)
: entities;
const conditions: Array<(value: string) => boolean> = [];
if (includeDomains && includeDomains.length) {
entityIds = entityIds.filter((eid) =>
includeDomains!.includes(computeDomain(eid))
);
if (includeDomains?.length) {
conditions.push((eid) => includeDomains!.includes(computeDomain(eid)));
}
if (entityFilter) {
entityIds = entityIds.filter(
conditions.push(
(eid) => hass.states[eid] && entityFilter(hass.states[eid])
);
}
if (entityIds.length < (maxEntities || 1)) {
let fillEntityIds =
entitiesFill && entitiesFill.length
? entitiesFill
: [...computeUsedEntities(lovelaceConfig)];
entityIds = arrayFilter(entities, conditions, maxEntities);
if (includeDomains && includeDomains.length) {
fillEntityIds = fillEntityIds.filter((eid) =>
includeDomains!.includes(computeDomain(eid))
);
}
if (entityIds.length < maxEntities && entitiesFallback.length) {
const fallbackEntityIds = findEntities(
hass,
maxEntities - entityIds.length,
entitiesFallback,
[],
includeDomains,
entityFilter
);
if (entityFilter) {
fillEntityIds = fillEntityIds.filter(
(eid) => hass.states[eid] && entityFilter(hass.states[eid])
);
}
entityIds = [...entityIds, ...fillEntityIds];
entityIds.push(...fallbackEntityIds);
}
return entityIds.slice(0, maxEntities);
return entityIds;
};

View File

@ -19,9 +19,10 @@ import { fireEvent } from "../../../../common/dom/fire_event";
import { createCardElement } from "../../create-element/create-card-element";
import { getCardStubConfig } from "../get-card-stub-config";
import {
computeUnusedEntities,
computeUsedEntities,
calcUnusedEntities,
} from "../../common/compute-unused-entities";
import { UNKNOWN, UNAVAILABLE } from "../../../../data/entity";
const previewCards: string[] = [
"alarm-panel",
@ -137,8 +138,22 @@ export class HuiCardPicker extends LitElement {
return;
}
this._unusedEntities = computeUnusedEntities(this.hass, this.lovelace);
this._usedEntities = [...computeUsedEntities(this.lovelace)];
const usedEntities = computeUsedEntities(this.lovelace);
const unusedEntities = calcUnusedEntities(this.hass, usedEntities);
this._usedEntities = [...usedEntities].filter(
(eid) =>
this.hass!.states[eid] &&
this.hass!.states[eid].state !== UNKNOWN &&
this.hass!.states[eid].state !== UNAVAILABLE
);
this._unusedEntities = [...unusedEntities].filter(
(eid) =>
this.hass!.states[eid] &&
this.hass!.states[eid].state !== UNKNOWN &&
this.hass!.states[eid].state !== UNAVAILABLE
);
this.requestUpdate();
}
@ -241,10 +256,9 @@ export class HuiCardPicker extends LitElement {
if (this.hass && this.lovelace) {
cardConfig = await getCardStubConfig(
this.hass,
this.lovelace,
type,
this._unusedEntities,
this._usedEntities
this._unusedEntities!,
this._usedEntities!
);
if (!noElement) {

View File

@ -1,13 +1,12 @@
import { HomeAssistant } from "../../../types";
import { LovelaceCardConfig, LovelaceConfig } from "../../../data/lovelace";
import { LovelaceCardConfig } from "../../../data/lovelace";
import { getCardElementClass } from "../create-element/create-card-element";
export const getCardStubConfig = async (
hass: HomeAssistant,
lovelaceConfig: LovelaceConfig,
type: string,
entities?: string[],
entitiesFill?: string[]
entities: string[],
entitiesFallback: string[]
): Promise<LovelaceCardConfig> => {
let cardConfig: LovelaceCardConfig = { type };
@ -16,9 +15,8 @@ export const getCardStubConfig = async (
if (elClass && elClass.getStubConfig) {
const classStubConfig = elClass.getStubConfig(
hass,
lovelaceConfig,
entities,
entitiesFill
entitiesFallback
);
cardConfig = { ...cardConfig, ...classStubConfig };

View File

@ -201,7 +201,8 @@ export class HuiUnusedEntities extends LitElement {
return;
}
this._selectedEntities = [];
this._unusedEntities = computeUnusedEntities(this.hass, this._config!);
const unusedEntities = computeUnusedEntities(this.hass, this._config!);
this._unusedEntities = [...unusedEntities].sort();
}
private _handleSelectionChanged(

View File

@ -40,9 +40,8 @@ export interface LovelaceCard extends HTMLElement {
export interface LovelaceCardConstructor extends Constructor<LovelaceCard> {
getStubConfig?: (
hass: HomeAssistant,
lovelaceConfig: LovelaceConfig,
entities?: string[],
entitiesFill?: string[]
entities: string[],
entitiesFallback: string[]
) => LovelaceCardConfig;
getConfigElement?: () => LovelaceCardEditor;
}