From 395e5346df4e17d44757d9556588a14501a9b00e Mon Sep 17 00:00:00 2001 From: DigiLive Date: Sun, 27 Apr 2025 11:15:30 +0200 Subject: [PATCH] Fix Stateful scenes support - Stateful scene cards now replace classic scene cards when a stateful equivalent exists. - Stateful scene switches are hidden from the switch domain in all views, except for the Scene view. - Stateful scene switches are excluded from the switch chip count. --- src/Registry.ts | 1 + src/cards/SceneCard.ts | 52 +++++++++++++++++---------------------- src/mushroom-strategy.ts | 6 ++++- src/views/AbstractView.ts | 5 +++- 4 files changed, 33 insertions(+), 31 deletions(-) diff --git a/src/Registry.ts b/src/Registry.ts index e8899f6..dabde3d 100644 --- a/src/Registry.ts +++ b/src/Registry.ts @@ -244,6 +244,7 @@ class Registry { states.push( ...new RegistryFilter(Registry.entities) .whereDomain(domain) + .where((entity) => !entity.entity_id.endsWith('_stateful_scene')) .toList() .map((entity) => `states['${entity.entity_id}']`), ); diff --git a/src/cards/SceneCard.ts b/src/cards/SceneCard.ts index 365233e..5bd9729 100644 --- a/src/cards/SceneCard.ts +++ b/src/cards/SceneCard.ts @@ -3,28 +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 { TemplateCardConfig } from '../types/lovelace-mushroom/cards/template-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(): TemplateCardConfig { + static getDefaultConfig(): EntityCardConfig { return { - type: 'custom:mushroom-template-card', - icon: 'mdi:palette', - icon_color: 'disabled', + type: 'custom:mushroom-entity-card', tap_action: { - action: 'call-service', + action: 'perform-action', perform_action: 'scene.turn_on', - target: { - entity_id: undefined, - }, + target: {}, }, }; } @@ -36,31 +35,26 @@ class SceneCard extends AbstractCard { * @param {EntityCardConfig} [customConfiguration] Custom card configuration. */ constructor(entity: EntityRegistryEntry, customConfiguration?: EntityCardConfig) { - super(entity); - const sceneName = entity.entity_id.split('.').pop(); - const configuration = SceneCard.getDefaultConfig(); + 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. - configuration.primary = entity.name ?? entity.original_name ?? '?'; + const configuration = SceneCard.getDefaultConfig(); - if ( - isCallServiceActionConfig(configuration.tap_action) && - isCallServiceActionTarget(configuration.tap_action.target) - ) { - configuration.tap_action.target.entity_id = entity.entity_id; + if (isCallServiceActionConfig(configuration.tap_action)) { + configuration.tap_action.target = { entity_id: entity.entity_id }; } - configuration.icon = Registry.hassStates[entity.entity_id]?.attributes.icon ?? configuration.icon; - // Stateful Scenes support. (https://github.com/hugobloem/stateful_scenes) - configuration.icon_color = ` - {% set state = states('switch.${sceneName}_stateful_scene') %} - {% if state == 'on' %} - blue - {% else %} - disabled - {% endif %} - `; + configuration.icon = Registry.hassStates[entity.entity_id]?.attributes.icon ?? configuration.icon; this.configuration = { ...this.configuration, ...configuration, ...customConfiguration }; } diff --git a/src/mushroom-strategy.ts b/src/mushroom-strategy.ts index 94c779b..0b1bcb0 100644 --- a/src/mushroom-strategy.ts +++ b/src/mushroom-strategy.ts @@ -110,7 +110,11 @@ class MushroomStrategy extends HTMLTemplateElement { // Prepare promises for all supported domains const domainCardPromises = exposedDomainNames.filter(isSupportedDomain).map(async (domain) => { const moduleName = sanitizeClassName(domain + 'Card'); - const entities = new RegistryFilter(areaEntities).whereDomain(domain).toList(); + + const entities = new RegistryFilter(areaEntities) + .whereDomain(domain) + .where((entity) => !(domain === 'switch' && entity.entity_id.endsWith('_stateful_scene'))) + .toList(); if (!entities.length) { return null; diff --git a/src/views/AbstractView.ts b/src/views/AbstractView.ts index 976054c..d096486 100644 --- a/src/views/AbstractView.ts +++ b/src/views/AbstractView.ts @@ -54,9 +54,12 @@ abstract class AbstractView { */ protected async createCardConfigurations(): Promise { const viewCards: LovelaceCardConfig[] = []; - const domainEntities = new RegistryFilter(Registry.entities).whereDomain(this.domain).toList(); const moduleName = sanitizeClassName(this.domain + 'Card'); const DomainCard = (await import(`../cards/${moduleName}`)).default; + const domainEntities = new RegistryFilter(Registry.entities) + .whereDomain(this.domain) + .where((entity) => !entity.entity_id.endsWith('_stateful_scene')) + .toList(); // Create card configurations for each area. for (const area of Registry.areas) {