Add hiding diagnostic entities (#153)

Config and diagnostic entities are now hidden by default.

Their visibility can be defined in the configuration with entries
`hide_config_entities` and `hide_diagnostic_entities` for all or individual domains.
This commit is contained in:
w531t4
2025-03-23 02:33:00 -04:00
committed by GitHub
parent 0c2649e08e
commit 711140d13f
7 changed files with 83 additions and 46 deletions

View File

@@ -6,6 +6,7 @@ import {DeviceRegistryEntry} from "./types/homeassistant/data/device_registry";
import {AreaRegistryEntry} from "./types/homeassistant/data/area_registry";
import {generic} from "./types/strategy/generic";
import setupCustomLocalize from "./localize";
import {applyEntityCategoryFilters} from "./utillties/filters";
import StrategyArea = generic.StrategyArea;
/**
@@ -228,7 +229,6 @@ class Helper {
* @static
*/
static getCountTemplate(domain: string, operator: string, value: string): string {
// noinspection JSMismatchedCollectionQueryUpdate (False positive per 17-04-2023)
/**
* Array of entity state-entries, filtered by domain.
*
@@ -246,22 +246,14 @@ class Helper {
console.warn("Helper class should be initialized before calling this method!");
}
// Get the ID of the devices which are linked to the given area.
// Get the state of entities which are linked to the given area.
for (const area of this.#areas) {
const areaDeviceIds = this.#devices.filter((device) => {
return device.area_id === area.area_id;
}).map((device) => {
return device.id;
});
let entities = this.getDeviceEntities(area, domain);
// Get the entities of which all conditions of the callback function are met. @see areaFilterCallback.
const newStates = this.#entities.filter(
this.#areaFilterCallback, {
area: area,
domain: domain,
areaDeviceIds: areaDeviceIds,
})
.map((entity) => `states['${entity.entity_id}']`);
// Exclude hidden Config and Diagnostic entities.
entities = applyEntityCategoryFilters(entities, domain);
const newStates = entities.map((entity) => `states['${entity.entity_id}']`);
states.push(...newStates);
}

View File

@@ -21,7 +21,8 @@ export const getConfigurationDefaults = (localize: Function): StrategyDefaults =
debug: false,
domains: {
_: {
hide_config_entities: false,
hide_config_entities: true,
hide_diagnostic_entities: true,
},
default: {
title: localize("generic.miscellaneous"),

View File

@@ -6,6 +6,7 @@ import {LovelaceCardConfig, LovelaceConfig, LovelaceViewConfig} from "./types/ho
import {StackCardConfig} from "./types/homeassistant/lovelace/cards/types";
import {EntityCardConfig} from "./types/lovelace-mushroom/cards/entity-card-config";
import {HassServiceTarget} from "home-assistant-js-websocket";
import {applyEntityCategoryFilters} from "./utillties/filters";
import StrategyArea = generic.StrategyArea;
/**
@@ -112,10 +113,10 @@ class MushroomStrategy extends HTMLTemplateElement {
try {
domainCards = await import(`./cards/${className}`).then(cardModule => {
let domainCards: EntityCardConfig[] = [];
const entities = Helper.getDeviceEntities(area, domain);
let configEntityHidden =
Helper.strategyOptions.domains[domain ?? "_"].hide_config_entities
|| Helper.strategyOptions.domains["_"].hide_config_entities;
let entities = Helper.getDeviceEntities(area, domain);
// Exclude hidden Config and Diagnostic entities.
entities = applyEntityCategoryFilters(entities, domain);
// Set the target for controller cards to entities without an area.
if (area.area_id === "undisclosed") {
@@ -175,11 +176,6 @@ class MushroomStrategy extends HTMLTemplateElement {
deviceOptions = Helper.strategyOptions.card_options?.[entity.device_id];
}
// Don't include the config-entity if hidden in the strategy options.
if (entity.entity_category === "config" && configEntityHidden) {
continue;
}
domainCards.push(new cardModule[className](entity, cardOptions).getCard());
}
@@ -219,10 +215,13 @@ class MushroomStrategy extends HTMLTemplateElement {
if (!Helper.strategyOptions.domains.default.hidden) {
// Create cards for any other domain.
// Collect entities of the current area and unexposed domains.
const miscellaneousEntities = Helper.getDeviceEntities(area).filter(
let miscellaneousEntities = Helper.getDeviceEntities(area).filter(
entity => !exposedDomainIds.includes(entity.entity_id.split(".", 1)[0])
);
// Exclude hidden Config and Diagnostic entities.
miscellaneousEntities = applyEntityCategoryFilters(miscellaneousEntities, "default");
// Create a column of miscellaneous entity cards.
if (miscellaneousEntities.length) {
let miscellaneousCards: (StackCardConfig | EntityCardConfig)[] = [];
@@ -237,11 +236,6 @@ class MushroomStrategy extends HTMLTemplateElement {
let cardOptions = Helper.strategyOptions.card_options?.[entity.entity_id];
let deviceOptions = Helper.strategyOptions.card_options?.[entity.device_id ?? "null"];
// Don't include the config-entity if hidden in the strategy options
if (entity.entity_category === "config" && Helper.strategyOptions.domains["_"].hide_config_entities) {
continue;
}
miscellaneousCards.push(new cardModule.MiscellaneousCard(entity, cardOptions).getCard());
}

View File

@@ -38,11 +38,14 @@ export namespace generic {
* @property {boolean} [hidden] True if the entity should be hidden from the dashboard.
* @property {boolean} [hide_config_entities] True if the entity's categorie is "config" and should be hidden from the
* dashboard.
* @property {boolean} [hide_diagnostic_entities] True if the entity's categorie is "diagnostic" and should be hidden
* from the dashboard.
*/
export interface DomainConfig extends Partial<cards.ControllerCardConfig> {
hidden?: boolean;
order?: number;
hide_config_entities?: boolean
hide_diagnostic_entities?: boolean
}
/**

52
src/utillties/filters.ts Normal file
View File

@@ -0,0 +1,52 @@
import {EntityRegistryEntry} from "../types/homeassistant/data/entity_registry";
import {Helper} from "../Helper";
/**
* Filter an array of entities by property/value pair
*
* @param entities The array of entities to filter.
* @param property The property to filter on.
* @param value The value to match.
* @param exclude Whether to exclude entities with the given property/value pair (default: true).
*
* @returns A new list of entities filtered by the given property/value pair.
*/
export function filterEntitiesByPropertyValue(
entities: EntityRegistryEntry[],
property: keyof EntityRegistryEntry,
value: any,
exclude: boolean = true
) {
return entities.filter(entity => exclude ? entity[property] !== value : entity[property] === value);
}
export function applyEntityCategoryFilters(entities: EntityRegistryEntry[], domain: string) {
if (!Helper.isInitialized()) {
throw new Error("The Helper module must be initialized before using this one.");
}
const domainOptions = {
...Helper.strategyOptions.domains["_"],
...Helper.strategyOptions.domains[domain],
};
let filteredEntityCategory = [];
if (domainOptions.hide_config_entities) {
entities = filterEntitiesByPropertyValue(entities, "entity_category", "config");
filteredEntityCategory.push("Config");
}
if (domainOptions.hide_diagnostic_entities) {
entities = filterEntitiesByPropertyValue(entities, "entity_category", "diagnostic");
filteredEntityCategory.push("Diagnostic");
}
if (Helper.debug && filteredEntityCategory.length > 0) {
console.warn(filteredEntityCategory.join(" & ") + " entities are filtered out.");
}
return entities;
}

View File

@@ -5,6 +5,7 @@ import {LovelaceCardConfig, LovelaceViewConfig} from "../types/homeassistant/dat
import {cards} from "../types/strategy/cards";
import {TitleCardConfig} from "../types/lovelace-mushroom/cards/title-card-config";
import {HassServiceTarget} from "home-assistant-js-websocket";
import {applyEntityCategoryFilters} from "../utillties/filters";
import abstractCardConfig = cards.AbstractCardConfig;
/**
@@ -42,24 +43,22 @@ abstract class AbstractView {
* @private
* @readonly
*/
readonly #domain?: string;
readonly #domain: string;
/**
* Class constructor.
*
* @param {string} [domain] The domain which the view is representing.
* @param {string} domain The domain which the view is representing.
*
* @throws {Error} If trying to instantiate this class.
* @throws {Error} If the Helper module isn't initialized.
*/
protected constructor(domain: string = "") {
protected constructor(domain: string) {
if (!Helper.isInitialized()) {
throw new Error("The Helper module must be initialized before using this one.");
}
if (domain) {
this.#domain = domain;
}
this.#domain = domain;
}
/**
@@ -69,14 +68,10 @@ abstract class AbstractView {
*/
async createViewCards(): Promise<(StackCardConfig | TitleCardConfig)[]> {
const viewCards: LovelaceCardConfig[] = [];
const configEntityHidden =
Helper.strategyOptions.domains[this.#domain ?? "_"].hide_config_entities
|| Helper.strategyOptions.domains["_"].hide_config_entities;
// Create cards for each area.
for (const area of Helper.areas) {
const areaCards: abstractCardConfig[] = [];
const entities = Helper.getDeviceEntities(area, this.#domain ?? "");
const className = Helper.sanitizeClassName(this.#domain + "Card");
const cardModule = await import(`../cards/${className}`);
@@ -85,6 +80,10 @@ abstract class AbstractView {
area_id: [area.area_id],
};
let entities = Helper.getDeviceEntities(area, this.#domain);
// Exclude hidden Config and Diagnostic entities.
entities = applyEntityCategoryFilters(entities, this.#domain);
// Set the target for controller cards to entities without an area.
if (area.area_id === "undisclosed") {
target = {
@@ -101,10 +100,6 @@ abstract class AbstractView {
continue;
}
if (entity.entity_category === "config" && configEntityHidden) {
continue;
}
areaCards.push(new cardModule[className](entity, cardOptions).getCard());
}

View File

@@ -39,7 +39,7 @@ class HomeView extends AbstractView {
* @param {views.ViewConfig} [options={}] Options for the view.
*/
constructor(options: views.ViewConfig = {}) {
super();
super("home");
this.config = Object.assign(this.config, this.#defaultConfig, options);
}