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.
This commit is contained in:
DigiLive
2025-04-27 11:15:30 +02:00
parent c536ee1302
commit 395e5346df
4 changed files with 33 additions and 31 deletions

View File

@@ -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}']`),
);

View File

@@ -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 };
}

View File

@@ -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;

View File

@@ -54,9 +54,12 @@ abstract class AbstractView {
*/
protected async createCardConfigurations(): Promise<LovelaceCardConfig[]> {
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) {