Optimize Cards

- Make use of the debug module.
- Make use of the Registry module.
- Align code across the chip modules.
- Make configurations immutable.
- Make use of the refactored types.
This commit is contained in:
DigiLive
2025-04-23 07:48:55 +02:00
parent db3a00d6f2
commit 5b02f20084
5 changed files with 95 additions and 112 deletions

View File

@@ -1,59 +1,59 @@
import {Helper} from "../Helper";
import {EntityCardConfig} from "../types/lovelace-mushroom/cards/entity-card-config";
import {cards} from "../types/strategy/cards";
import {generic} from "../types/strategy/generic";
import { Registry } from '../Registry';
import { EntityCardConfig } from '../types/lovelace-mushroom/cards/entity-card-config';
import { AbstractCardConfig } from '../types/strategy/strategy-cards';
import { RegistryEntry } from '../types/strategy/strategy-generics';
import { logMessage, lvlFatal } from '../utilities/debug';
/**
* Abstract Card Class
*
* To create a new card, extend the new class with this one.
* To create a card configuration, this class should be extended by a child class.
* Child classes should override the default configuration so the card correctly reflects the entity.
*
* @class
* @abstract
* @remarks
* Before using this class, the Registry module must be initialized by calling {@link Registry.initialize}.
*/
abstract class AbstractCard {
/**
* Entity to create the card for.
*
* @type {generic.RegistryEntry}
*/
entity: generic.RegistryEntry;
/** The registry entry this card represents. */
readonly entity: RegistryEntry;
/**
* Configuration of the card.
* The card configuration for this entity.
*
* @type {EntityCardConfig}
* Child classes should override this property to reflect their own card type and options.
*/
config: EntityCardConfig = {
type: "custom:mushroom-entity-card",
icon: "mdi:help-circle",
configuration: EntityCardConfig = {
type: 'custom:mushroom-entity-card',
icon: 'mdi:help-circle',
};
/**
* Class constructor.
*
* @param {generic.RegistryEntry} entity The hass entity to create a card for.
* @throws {Error} If the Helper module isn't initialized.
* @param {RegistryEntry} entity The registry entry to create a card configuration for.
*
* @remarks
* Before this class can be used, the Registry module must be initialized by calling {@link Registry.initialize}.
*/
protected constructor(entity: generic.RegistryEntry) {
if (!Helper.isInitialized()) {
throw new Error("The Helper module must be initialized before using this one.");
protected constructor(entity: RegistryEntry) {
if (!Registry.initialized) {
logMessage(lvlFatal, 'Registry not initialized!');
}
this.entity = entity;
}
/**
* Get a card.
* Get a card configuration.
*
* @returns {cards.AbstractCardConfig} A card object.
* The configuration should be set by any of the child classes so the card correctly reflects an entity.
*/
getCard(): cards.AbstractCardConfig {
getCard(): AbstractCardConfig {
return {
...this.config,
entity: "entity_id" in this.entity ? this.entity.entity_id : undefined,
...this.configuration,
entity: 'entity_id' in this.entity ? this.entity.entity_id : undefined,
};
}
}
export {AbstractCard};
export default AbstractCard;

View File

@@ -1,44 +1,37 @@
import {AbstractCard} from "./AbstractCard";
import {cards} from "../types/strategy/cards";
import {EntityRegistryEntry} from "../types/homeassistant/data/entity_registry";
import {PictureEntityCardConfig} from "../types/homeassistant/panels/lovelace/cards/types";
// noinspection JSUnusedGlobalSymbols Class is dynamically imported.
import { EntityRegistryEntry } from '../types/homeassistant/data/entity_registry';
import { PictureEntityCardConfig } from '../types/homeassistant/panels/lovelace/cards/types';
import AbstractCard from './AbstractCard';
/**
* Camera Card Class
*
* Used to create a card for controlling an entity of the camera domain.
*
* @class
* @extends AbstractCard
* Used to create a card configuration to control an entity of the camera domain.
*/
class CameraCard extends AbstractCard {
/**
* Default configuration of the card.
*
* @type {PictureEntityCardConfig}
* @private
*/
#defaultConfig: PictureEntityCardConfig = {
entity: "",
type: "picture-entity",
show_name: false,
show_state: false,
camera_view: "live",
};
/** Returns the default configuration object for the card. */
static getDefaultConfig(): PictureEntityCardConfig {
return {
entity: '',
type: 'picture-entity',
show_name: false,
show_state: false,
camera_view: 'live',
};
}
/**
* Class constructor.
*
* @param {EntityRegistryEntry} entity The hass entity to create a card for.
* @param {cards.PictureEntityCardOptions} [options={}] Options for the card.
* @throws {Error} If the Helper module isn't initialized.
* @param {EntityRegistryEntry} entity The HASS entity to create a card configuration for.
* @param {PictureEntityCardConfig} [customConfiguration] Custom card configuration.
*/
constructor(entity: EntityRegistryEntry, options: cards.PictureEntityCardOptions = {}) {
constructor(entity: EntityRegistryEntry, customConfiguration?: PictureEntityCardConfig) {
super(entity);
this.config = Object.assign(this.config, this.#defaultConfig, options);
this.configuration = { ...this.configuration, ...CameraCard.getDefaultConfig(), ...customConfiguration };
}
}
export {CameraCard};
export default CameraCard;

View File

@@ -1,49 +1,45 @@
import {AbstractCard} from "./AbstractCard";
import {cards} from "../types/strategy/cards";
import {AreaRegistryEntry} from "../types/homeassistant/data/area_registry";
import {AreaCardConfig} from "../types/homeassistant/panels/lovelace/cards/types";
// noinspection JSUnusedGlobalSymbols Class is dynamically imported.
import { AreaRegistryEntry } from '../types/homeassistant/data/area_registry';
import { AreaCardConfig } from '../types/homeassistant/panels/lovelace/cards/types';
import AbstractCard from './AbstractCard';
/**
* HA Area Card Class
*
* Used to create a card for an entity of the area domain using the built-in type 'area'.
*
* @class
* @extends AbstractCard
* Used to create card configuration for an entry of the HASS area registry.
*/
class AreaCard extends AbstractCard {
/**
* Default configuration of the card.
*
* @type {AreaCardConfig}
* @private
*/
#defaultConfig: AreaCardConfig = {
type: "area",
area: "",
};
/** Returns the default configuration object for the card. */
static getDefaultConfig(): AreaCardConfig {
return {
type: 'area',
area: '',
};
}
/**
* Class constructor.
*
* @param {AreaRegistryEntry} area The area entity to create a card for.
* @param {cards.AreaCardOptions} [options={}] Options for the card.
* @throws {Error} If the Helper module isn't initialized.
* @param {AreaRegistryEntry} area The HASS entity to create a card configuration for.
* @param {AreaCardConfig} [customConfiguration] Custom card configuration.
*/
constructor(area: AreaRegistryEntry, options: cards.AreaCardOptions = {}) {
constructor(area: AreaRegistryEntry, customConfiguration?: AreaCardConfig) {
super(area);
// Initialize the default configuration.
this.#defaultConfig.area = area.area_id;
this.#defaultConfig.navigation_path = this.#defaultConfig.area;
const configuration = AreaCard.getDefaultConfig();
// Enforce the card type.
delete options.type;
configuration.area = area.area_id;
configuration.navigation_path = configuration.area;
this.config = Object.assign(this.config, this.#defaultConfig, options);
this.configuration = {
...this.configuration,
...configuration,
...customConfiguration,
type: configuration.type, // Enforce the card type.
};
}
}
export {AreaCard};
export default AreaCard;

View File

@@ -2,7 +2,7 @@
import { EntityRegistryEntry } from '../types/homeassistant/data/entity_registry';
import { LightCardConfig } from '../types/lovelace-mushroom/cards/light-card-config';
import { isCallServiceActionConfig } from '../types/strategy/strategy-generics';
import { isCallServiceActionConfig, isCallServiceActionTarget } from '../types/strategy/strategy-generics';
import AbstractCard from './AbstractCard';
/**
@@ -44,8 +44,11 @@ class LightCard extends AbstractCard {
const configuration = LightCard.getDefaultConfig();
if (isCallServiceActionConfig(configuration.double_tap_action)) {
configuration.double_tap_action.target = { entity_id: entity.entity_id };
if (
isCallServiceActionConfig(configuration.double_tap_action) &&
isCallServiceActionTarget(configuration.double_tap_action.target)
) {
configuration.double_tap_action.target.entity_id = entity.entity_id;
}
this.configuration = { ...this.configuration, ...configuration, ...customConfiguration };

View File

@@ -3,27 +3,27 @@
import { Registry } from '../Registry';
import { EntityRegistryEntry } from '../types/homeassistant/data/entity_registry';
import { EntityCardConfig } from '../types/lovelace-mushroom/cards/entity-card-config';
import { isCallServiceActionConfig, isCallServiceActionTarget } from '../types/strategy/strategy-generics';
import AbstractCard from './AbstractCard';
import SwitchCard from './SwitchCard';
import { isCallServiceActionConfig } from '../types/strategy/strategy-generics';
/**
* Scene Card Class
*
* Used to create a card configuration to control an entity of the scene domain.
*
* Supports Stateful scenes from https://github.com/hugobloem/stateful_scenes.
* If the stateful scene entity is available, it will be used instead of the original scene entity.
*/
class SceneCard extends AbstractCard {
/** Returns the default configuration object for the card. */
static getDefaultConfig(): EntityCardConfig {
return {
type: 'custom:mushroom-entity-card',
icon: 'mdi:palette',
icon_color: 'blue',
tap_action: {
action: 'perform-action',
action: 'call-service',
perform_action: 'scene.turn_on',
target: {},
target: {
entity_id: undefined,
},
},
};
}
@@ -35,25 +35,16 @@ class SceneCard extends AbstractCard {
* @param {EntityCardConfig} [customConfiguration] Custom card configuration.
*/
constructor(entity: EntityRegistryEntry, customConfiguration?: EntityCardConfig) {
const sceneName = entity.entity_id.split('.').pop();
const statefulScene = Registry.entities.find((entity) => entity.entity_id === `switch.${sceneName}_stateful_scene`);
super(entity);
super(statefulScene ?? entity);
// Stateful scene support.
if (statefulScene) {
this.configuration = new SwitchCard(statefulScene).getCard();
return;
}
// Initialize the default configuration.
const configuration = SceneCard.getDefaultConfig();
if (isCallServiceActionConfig(configuration.tap_action)) {
configuration.tap_action.target = { entity_id: entity.entity_id };
if (
isCallServiceActionConfig(configuration.tap_action) &&
isCallServiceActionTarget(configuration.tap_action.target)
) {
configuration.tap_action.target.entity_id = entity.entity_id;
}
configuration.icon = Registry.hassStates[entity.entity_id]?.attributes.icon ?? configuration.icon;
this.configuration = { ...this.configuration, ...configuration, ...customConfiguration };