From 711140d13fb49f2b10ef09b47b50a79a799aa0fb Mon Sep 17 00:00:00 2001 From: w531t4 <41222371+w531t4@users.noreply.github.com> Date: Sun, 23 Mar 2025 02:33:00 -0400 Subject: [PATCH] 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. --- src/Helper.ts | 22 +++++---------- src/configurationDefaults.ts | 3 +- src/mushroom-strategy.ts | 24 ++++++---------- src/types/strategy/generic.ts | 3 ++ src/utillties/filters.ts | 52 +++++++++++++++++++++++++++++++++++ src/views/AbstractView.ts | 23 ++++++---------- src/views/HomeView.ts | 2 +- 7 files changed, 83 insertions(+), 46 deletions(-) create mode 100644 src/utillties/filters.ts diff --git a/src/Helper.ts b/src/Helper.ts index 30faa90..00f4db4 100644 --- a/src/Helper.ts +++ b/src/Helper.ts @@ -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); } diff --git a/src/configurationDefaults.ts b/src/configurationDefaults.ts index 0fd7bea..961f3ed 100644 --- a/src/configurationDefaults.ts +++ b/src/configurationDefaults.ts @@ -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"), diff --git a/src/mushroom-strategy.ts b/src/mushroom-strategy.ts index a4d6f53..0880fc7 100644 --- a/src/mushroom-strategy.ts +++ b/src/mushroom-strategy.ts @@ -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()); } diff --git a/src/types/strategy/generic.ts b/src/types/strategy/generic.ts index 12197d5..94e3838 100644 --- a/src/types/strategy/generic.ts +++ b/src/types/strategy/generic.ts @@ -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 { hidden?: boolean; order?: number; hide_config_entities?: boolean + hide_diagnostic_entities?: boolean } /** diff --git a/src/utillties/filters.ts b/src/utillties/filters.ts new file mode 100644 index 0000000..d2d4b4d --- /dev/null +++ b/src/utillties/filters.ts @@ -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; +} + + diff --git a/src/views/AbstractView.ts b/src/views/AbstractView.ts index 9e2b3a9..6387017 100644 --- a/src/views/AbstractView.ts +++ b/src/views/AbstractView.ts @@ -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()); } diff --git a/src/views/HomeView.ts b/src/views/HomeView.ts index 6b9dd45..3f9011f 100644 --- a/src/views/HomeView.ts +++ b/src/views/HomeView.ts @@ -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); }