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 { Registry } from '../Registry';
import {EntityCardConfig} from "../types/lovelace-mushroom/cards/entity-card-config"; import { EntityCardConfig } from '../types/lovelace-mushroom/cards/entity-card-config';
import {cards} from "../types/strategy/cards"; import { AbstractCardConfig } from '../types/strategy/strategy-cards';
import {generic} from "../types/strategy/generic"; import { RegistryEntry } from '../types/strategy/strategy-generics';
import { logMessage, lvlFatal } from '../utilities/debug';
/** /**
* Abstract Card Class * 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 * @remarks
* @abstract * Before using this class, the Registry module must be initialized by calling {@link Registry.initialize}.
*/ */
abstract class AbstractCard { abstract class AbstractCard {
/** /** The registry entry this card represents. */
* Entity to create the card for. readonly entity: RegistryEntry;
*
* @type {generic.RegistryEntry}
*/
entity: generic.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 = { configuration: EntityCardConfig = {
type: "custom:mushroom-entity-card", type: 'custom:mushroom-entity-card',
icon: "mdi:help-circle", icon: 'mdi:help-circle',
}; };
/** /**
* Class constructor. * Class constructor.
* *
* @param {generic.RegistryEntry} entity The hass entity to create a card for. * @param {RegistryEntry} entity The registry entry to create a card configuration for.
* @throws {Error} If the Helper module isn't initialized. *
* @remarks
* Before this class can be used, the Registry module must be initialized by calling {@link Registry.initialize}.
*/ */
protected constructor(entity: generic.RegistryEntry) { protected constructor(entity: RegistryEntry) {
if (!Helper.isInitialized()) { if (!Registry.initialized) {
throw new Error("The Helper module must be initialized before using this one."); logMessage(lvlFatal, 'Registry not initialized!');
} }
this.entity = entity; 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 { return {
...this.config, ...this.configuration,
entity: "entity_id" in this.entity ? this.entity.entity_id : undefined, 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. // 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 * Camera Card Class
* *
* Used to create a card for controlling an entity of the camera domain. * Used to create a card configuration to control an entity of the camera domain.
*
* @class
* @extends AbstractCard
*/ */
class CameraCard extends AbstractCard { class CameraCard extends AbstractCard {
/** /** Returns the default configuration object for the card. */
* Default configuration of the card. static getDefaultConfig(): PictureEntityCardConfig {
* return {
* @type {PictureEntityCardConfig} entity: '',
* @private type: 'picture-entity',
*/
#defaultConfig: PictureEntityCardConfig = {
entity: "",
type: "picture-entity",
show_name: false, show_name: false,
show_state: false, show_state: false,
camera_view: "live", camera_view: 'live',
}; };
}
/** /**
* Class constructor. * Class constructor.
* *
* @param {EntityRegistryEntry} entity The hass entity to create a card for. * @param {EntityRegistryEntry} entity The HASS entity to create a card configuration for.
* @param {cards.PictureEntityCardOptions} [options={}] Options for the card. * @param {PictureEntityCardConfig} [customConfiguration] Custom card configuration.
* @throws {Error} If the Helper module isn't initialized.
*/ */
constructor(entity: EntityRegistryEntry, options: cards.PictureEntityCardOptions = {}) { constructor(entity: EntityRegistryEntry, customConfiguration?: PictureEntityCardConfig) {
super(entity); 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. // 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 * HA Area Card Class
* *
* Used to create a card for an entity of the area domain using the built-in type 'area'. * Used to create card configuration for an entry of the HASS area registry.
*
* @class
* @extends AbstractCard
*/ */
class AreaCard extends AbstractCard { class AreaCard extends AbstractCard {
/** /** Returns the default configuration object for the card. */
* Default configuration of the card. static getDefaultConfig(): AreaCardConfig {
* return {
* @type {AreaCardConfig} type: 'area',
* @private area: '',
*/
#defaultConfig: AreaCardConfig = {
type: "area",
area: "",
}; };
}
/** /**
* Class constructor. * Class constructor.
* *
* @param {AreaRegistryEntry} area The area entity to create a card for. * @param {AreaRegistryEntry} area The HASS entity to create a card configuration for.
* @param {cards.AreaCardOptions} [options={}] Options for the card. * @param {AreaCardConfig} [customConfiguration] Custom card configuration.
* @throws {Error} If the Helper module isn't initialized.
*/ */
constructor(area: AreaRegistryEntry, customConfiguration?: AreaCardConfig) {
constructor(area: AreaRegistryEntry, options: cards.AreaCardOptions = {}) {
super(area); super(area);
// Initialize the default configuration. // Initialize the default configuration.
this.#defaultConfig.area = area.area_id; const configuration = AreaCard.getDefaultConfig();
this.#defaultConfig.navigation_path = this.#defaultConfig.area;
// Enforce the card type. configuration.area = area.area_id;
delete options.type; 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 { EntityRegistryEntry } from '../types/homeassistant/data/entity_registry';
import { LightCardConfig } from '../types/lovelace-mushroom/cards/light-card-config'; 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'; import AbstractCard from './AbstractCard';
/** /**
@@ -44,8 +44,11 @@ class LightCard extends AbstractCard {
const configuration = LightCard.getDefaultConfig(); const configuration = LightCard.getDefaultConfig();
if (isCallServiceActionConfig(configuration.double_tap_action)) { if (
configuration.double_tap_action.target = { entity_id: entity.entity_id }; 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 }; this.configuration = { ...this.configuration, ...configuration, ...customConfiguration };

View File

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