Fix counting of hidden entities and devices

Evaluation an entity's hidden property in the strategy options, is now
included in Helper.#areaFilterCallback().
This commit is contained in:
w531t4
2025-02-09 00:48:38 -05:00
committed by GitHub
parent 9ed17d9a56
commit da7d0cd57c
3 changed files with 36 additions and 58 deletions

View File

@ -287,12 +287,12 @@ class Helper {
* The result excludes hidden and disabled entities. * The result excludes hidden and disabled entities.
* *
* @param {AreaRegistryEntry} area Area entity. * @param {AreaRegistryEntry} area Area entity.
* @param {string} domain The domain of the entity-id. * @param {string} [domain] The domain of the entity-id.
* *
* @return {EntityRegistryEntry[]} Array of device entities. * @return {EntityRegistryEntry[]} Array of device entities.
* @static * @static
*/ */
static getDeviceEntities(area: AreaRegistryEntry, domain: string): EntityRegistryEntry[] { static getDeviceEntities(area: AreaRegistryEntry, domain?: string): EntityRegistryEntry[] {
if (!this.isInitialized()) { if (!this.isInitialized()) {
console.warn("Helper class should be initialized before calling this method!"); console.warn("Helper class should be initialized before calling this method!");
} }
@ -414,12 +414,12 @@ class Helper {
* Callback function for filtering entities. * Callback function for filtering entities.
* *
* Entities of which all the conditions below are met are kept: * Entities of which all the conditions below are met are kept:
* 1. The entity is not hidden and is not disabled. * 1. The entity is not hidden and the entity's device is not hidden by the strategy options.
* 2. The entity's domain matches the given domain. * 2. The entity is not hidden and is not disabled by Hass.
* 3. The entity itself or else the entity's linked device is linked to the given area. * 3. The entity's domain matches the given domain.
* (See variable areaMatch) * 4. The entity itself or else the entity's device is linked to the given area.
* *
* @param {EntityRegistryEntry} entity The current hass entity to evaluate. * @param {EntityRegistryEntry} entity The current Hass entity to evaluate.
* @this {AreaFilterContext} * @this {AreaFilterContext}
* *
* @return {boolean} True to keep the entity. * @return {boolean} True to keep the entity.
@ -432,16 +432,20 @@ class Helper {
domain: string, domain: string,
}, },
entity: EntityRegistryEntry): boolean { entity: EntityRegistryEntry): boolean {
const entityUnhidden = entity.hidden_by === null && entity.disabled_by === null; const cardOptions = Helper.strategyOptions.card_options?.[entity.entity_id];
const domainMatches = entity.entity_id.startsWith(`${this.domain}.`); const deviceOptions = Helper.strategyOptions.card_options?.[entity.device_id ?? "null"];
const entityUnhidden =
!cardOptions?.hidden && !deviceOptions?.hidden // Condition 1.
&& entity.hidden_by === null && entity.disabled_by === null; // Condition 2.
const domainMatches = this.domain === undefined || entity.entity_id.startsWith(`${this.domain}.`); // Condition 3.
// Condition 4.
const entityLinked = this.area.area_id === "undisclosed" const entityLinked = this.area.area_id === "undisclosed"
// Undisclosed area; // Undisclosed area.
// nor the entity itself, neither the entity's linked device (if any) is linked to any area.
? !entity.area_id && (this.areaDeviceIds.includes(entity.device_id ?? "") || !entity.device_id) ? !entity.area_id && (this.areaDeviceIds.includes(entity.device_id ?? "") || !entity.device_id)
// Area is a hass entity; // Area is a hass entity. Note: entity.area_id is set to null when using device's area.
// The entity itself or the entity's device is linked to the given area.
// Note: entity.area_id is set to null when using device's area.
: entity.area_id === this.area.area_id || (!entity.area_id && this.areaDeviceIds.includes(entity.device_id ?? "")); : entity.area_id === this.area.area_id || (!entity.area_id && this.areaDeviceIds.includes(entity.device_id ?? ""));
return (entityUnhidden && domainMatches && entityLinked); return (entityUnhidden && domainMatches && entityLinked);
} }

View File

@ -107,11 +107,11 @@ class MushroomStrategy extends HTMLTemplateElement {
const className = Helper.sanitizeClassName(domain + "Card"); const className = Helper.sanitizeClassName(domain + "Card");
let domainCards = []; let domainCards: EntityCardConfig[] = [];
try { try {
domainCards = await import(`./cards/${className}`).then(cardModule => { domainCards = await import(`./cards/${className}`).then(cardModule => {
let domainCards = []; let domainCards: EntityCardConfig[] = [];
const entities = Helper.getDeviceEntities(area, domain); const entities = Helper.getDeviceEntities(area, domain);
let configEntityHidden = let configEntityHidden =
Helper.strategyOptions.domains[domain ?? "_"].hide_config_entities Helper.strategyOptions.domains[domain ?? "_"].hide_config_entities
@ -132,7 +132,7 @@ class MushroomStrategy extends HTMLTemplateElement {
).createCard(); ).createCard();
if (domain === "sensor") { if (domain === "sensor") {
// Create a card for each entity-sensor of the current area. // Create a card for each sensor-entity of the current area.
const sensorStates = Helper.getStateEntities(area, "sensor"); const sensorStates = Helper.getStateEntities(area, "sensor");
const sensorCards: EntityCardConfig[] = []; const sensorCards: EntityCardConfig[] = [];
@ -140,18 +140,15 @@ class MushroomStrategy extends HTMLTemplateElement {
// Find the state of the current sensor. // Find the state of the current sensor.
const sensorState = sensorStates.find(state => state.entity_id === sensor.entity_id); const sensorState = sensorStates.find(state => state.entity_id === sensor.entity_id);
let cardOptions = Helper.strategyOptions.card_options?.[sensor.entity_id]; let cardOptions = Helper.strategyOptions.card_options?.[sensor.entity_id];
let deviceOptions = Helper.strategyOptions.card_options?.[sensor.device_id ?? "null"];
if (!cardOptions?.hidden && !deviceOptions?.hidden) { if (sensorState?.attributes.unit_of_measurement) {
if (sensorState?.attributes.unit_of_measurement) { cardOptions = {
cardOptions = { ...{
...{ type: "custom:mini-graph-card",
type: "custom:mini-graph-card", entities: [sensor.entity_id],
entities: [sensor.entity_id], },
}, ...cardOptions,
...cardOptions, };
};
}
sensorCards.push(new SensorCard(sensor, cardOptions).getCard()); sensorCards.push(new SensorCard(sensor, cardOptions).getCard());
} }
@ -178,11 +175,6 @@ class MushroomStrategy extends HTMLTemplateElement {
deviceOptions = Helper.strategyOptions.card_options?.[entity.device_id]; deviceOptions = Helper.strategyOptions.card_options?.[entity.device_id];
} }
// Don't include the entity if hidden in the strategy options.
if (cardOptions?.hidden || deviceOptions?.hidden) {
continue;
}
// Don't include the config-entity if hidden in the strategy options. // Don't include the config-entity if hidden in the strategy options.
if (entity.entity_category === "config" && configEntityHidden) { if (entity.entity_category === "config" && configEntityHidden) {
continue; continue;
@ -193,7 +185,7 @@ class MushroomStrategy extends HTMLTemplateElement {
if (domain === "binary_sensor") { if (domain === "binary_sensor") {
// Horizontally group every two binary sensor cards. // Horizontally group every two binary sensor cards.
const horizontalCards = []; const horizontalCards: EntityCardConfig[] = [];
for (let i = 0; i < domainCards.length; i += 2) { for (let i = 0; i < domainCards.length; i += 2) {
horizontalCards.push({ horizontalCards.push({
@ -226,22 +218,10 @@ class MushroomStrategy extends HTMLTemplateElement {
if (!Helper.strategyOptions.domains.default.hidden) { if (!Helper.strategyOptions.domains.default.hidden) {
// Create cards for any other domain. // Create cards for any other domain.
// Collect device entities of the current area. // Collect entities of the current area and unexposed domains.
const areaDevices = Helper.devices.filter((device) => device.area_id === area.area_id) const miscellaneousEntities = Helper.getDeviceEntities(area).filter(
.map((device) => device.id); entity => !exposedDomainIds.includes(entity.entity_id.split(".", 1)[0])
);
// Collect the remaining entities of which all conditions below are met:
// 1. The entity is not hidden.
// 2. The entity's domain isn't exposed (entities of exposed domains are already included).
// 3. The entity is linked to a device which is linked to the current area,
// or the entity itself is linked to the current area.
const miscellaneousEntities = Helper.entities.filter((entity) => {
const entityLinked = areaDevices.includes(entity.device_id ?? "null") || entity.area_id === area.area_id;
const entityUnhidden = entity.hidden_by === null && entity.disabled_by === null;
const domainExposed = exposedDomainIds.includes(entity.entity_id.split(".", 1)[0]);
return entityUnhidden && !domainExposed && entityLinked;
});
// Create a column of miscellaneous entity cards. // Create a column of miscellaneous entity cards.
if (miscellaneousEntities.length) { if (miscellaneousEntities.length) {
@ -257,11 +237,6 @@ class MushroomStrategy extends HTMLTemplateElement {
let cardOptions = Helper.strategyOptions.card_options?.[entity.entity_id]; let cardOptions = Helper.strategyOptions.card_options?.[entity.entity_id];
let deviceOptions = Helper.strategyOptions.card_options?.[entity.device_id ?? "null"]; let deviceOptions = Helper.strategyOptions.card_options?.[entity.device_id ?? "null"];
// Don't include the entity if hidden in the strategy options.
if (cardOptions?.hidden || deviceOptions?.hidden) {
continue;
}
// Don't include the config-entity if hidden in the strategy options // Don't include the config-entity if hidden in the strategy options
if (entity.entity_category === "config" && Helper.strategyOptions.domains["_"].hide_config_entities) { if (entity.entity_category === "config" && Helper.strategyOptions.domains["_"].hide_config_entities) {
continue; continue;

View File

@ -186,13 +186,12 @@ export namespace generic {
* *
* @property {AreaRegistryEntry} area Area Entity. * @property {AreaRegistryEntry} area Area Entity.
* @property {string[]} areaDeviceIds The id of devices which are linked to the area entity. * @property {string[]} areaDeviceIds The id of devices which are linked to the area entity.
* @property {string} domain Domain of the entity. * @property {string} [domain] Domain of the entity. Example: `light`.
* Example: `light`.
*/ */
export interface AreaFilterContext { export interface AreaFilterContext {
area: AreaRegistryEntry; area: AreaRegistryEntry;
areaDeviceIds: string[]; areaDeviceIds: string[];
domain: string; domain?: string;
} }
/** /**