Optimize Views

- Make use of the debug module.
- Make use of the auxiliaries module.
- Make use of the localization 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:55:29 +02:00
parent 946550278b
commit 3dfeca6b92
10 changed files with 448 additions and 623 deletions

View File

@@ -1,171 +1,159 @@
import {Helper} from "../Helper"; import { HassServiceTarget } from 'home-assistant-js-websocket';
import {ControllerCard} from "../cards/ControllerCard"; import HeaderCard from '../cards/HeaderCard';
import {LovelaceCardConfig} from "../types/homeassistant/data/lovelace"; import { Registry } from '../Registry';
import {cards} from "../types/strategy/cards"; import { LovelaceCardConfig } from '../types/homeassistant/data/lovelace/config/card';
import {TitleCardConfig} from "../types/lovelace-mushroom/cards/title-card-config"; import { LovelaceViewConfig } from '../types/homeassistant/data/lovelace/config/view';
import {HassServiceTarget} from "home-assistant-js-websocket"; import { StackCardConfig } from '../types/homeassistant/panels/lovelace/cards/types';
import {applyEntityCategoryFilters} from "../utillties/filters"; import { AbstractCardConfig, CustomHeaderCardConfig, StrategyHeaderCardConfig } from '../types/strategy/strategy-cards';
import {LovelaceViewConfig} from "../types/homeassistant/data/lovelace/config/view"; import { SupportedDomains } from '../types/strategy/strategy-generics';
import {StackCardConfig} from "../types/homeassistant/panels/lovelace/cards/types"; import { ViewConfig, ViewConstructor } from '../types/strategy/strategy-views';
import {generic} from "../types/strategy/generic"; import { sanitizeClassName } from '../utilities/auxiliaries';
import abstractCardConfig = cards.AbstractCardConfig; import { logMessage, lvlFatal } from '../utilities/debug';
import SupportedDomains = generic.SupportedDomains; import RegistryFilter from '../utilities/RegistryFilter';
/** /**
* Abstract View Class. * Abstract View Class.
* *
* To create a new view, extend the new class with this one. * To create a view configuration, this class should be extended by a child class.
* Child classes should override the default configuration so the view correctly reflects the entities of a domain.
* *
* @class * @remarks
* @abstract * Before this class can be used, the Registry module must be initialized by calling {@link Registry.initialize}.
*/ */
abstract class AbstractView { abstract class AbstractView {
/** /** The base configuration of a view. */
* Configuration of the view. protected baseConfiguration: LovelaceViewConfig = {
* icon: 'mdi:view-dashboard',
* @type {LovelaceViewConfig}
*/
config: LovelaceViewConfig = {
icon: "mdi:view-dashboard",
subview: false, subview: false,
}; };
/** /** A card configuration to control all entities in the view. */
* A card to switch all entities in the view. private viewHeaderCardConfiguration: StackCardConfig = {
*
* @type {StackCardConfig}
*/
viewControllerCard: StackCardConfig = {
cards: [], cards: [],
type: "", type: '',
}; };
/** protected get domain(): SupportedDomains | 'home' {
* The domain of which we operate the devices. return (this.constructor as unknown as ViewConstructor).domain;
* }
* @type {SupportedDomains | "home"}
* @private
* @readonly
*/
readonly #domain: SupportedDomains | "home";
/** /**
* Class constructor. * Class constructor.
* *
* @param {SupportedDomains} domain The domain which the view is representing. * @remarks
* * Before this class can be used, the Registry module must be initialized by calling {@link Registry.initialize}.
* @throws {Error} If trying to instantiate this class.
* @throws {Error} If the Helper module isn't initialized.
*/ */
protected constructor(domain: SupportedDomains | "home") { protected constructor() {
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.#domain = domain;
} }
/** /**
* Create the cards to include in the view. * Create the configuration of the cards to include in the view.
*
* @returns {Promise<(StackCardConfig | TitleCardConfig)[]>} An array of card objects.
*/ */
async createViewCards(): Promise<(StackCardConfig | TitleCardConfig)[]> { protected async createCardConfigurations(): Promise<LovelaceCardConfig[]> {
if (this.#domain === "home") { if (this.domain === 'home') {
// The home domain should override this method because it hasn't entities on its own. // The home domain should override this method because it hasn't entities of its own.
// The method override creates its own cards to show at the home view. // The method override creates its own configurations for cards to show at the home view.
return []; return [];
} }
const viewCards: 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;
// Create cards for each area. // Create card configurations for each area.
for (const area of Helper.areas) { for (const area of Registry.areas) {
const areaCards: abstractCardConfig[] = []; const areaCards: AbstractCardConfig[] = [];
const className = Helper.sanitizeClassName(this.#domain + "Card");
const cardModule = await import(`../cards/${className}`);
// Set the target for controller cards to the current area. // Set the target of the Header card to the current area.
let target: HassServiceTarget = { let target: HassServiceTarget = {
area_id: [area.area_id], area_id: [area.area_id],
}; };
const areaEntities = new RegistryFilter(domainEntities).whereAreaId(area.area_id).toList();
let entities = Helper.getDeviceEntities(area, this.#domain); // Set the target of the Header card to entities without an area.
// Exclude hidden Config and Diagnostic entities. if (area.area_id === 'undisclosed') {
entities = applyEntityCategoryFilters(entities, this.#domain);
// Set the target for controller cards to entities without an area.
if (area.area_id === "undisclosed") {
target = { target = {
entity_id: entities.map(entity => entity.entity_id), entity_id: areaEntities.map((entity) => entity.entity_id),
} };
} }
// Create a card for each domain-entity of the current area. // Create a card configuration for each entity in the current area.
for (const entity of entities) { areaCards.push(
let cardOptions = Helper.strategyOptions.card_options?.[entity.entity_id]; ...areaEntities.map((entity) =>
let deviceOptions = Helper.strategyOptions.card_options?.[entity.device_id ?? "null"]; new DomainCard(entity, Registry.strategyOptions.card_options?.[entity.entity_id]).getCard(),
),
);
if (cardOptions?.hidden || deviceOptions?.hidden) { // Vertically stack the cards of the current area.
continue;
}
areaCards.push(new cardModule[className](entity, cardOptions).getCard());
}
// Vertical stack the area cards if it has entities.
if (areaCards.length) { if (areaCards.length) {
const titleCardOptions = ("controllerCardOptions" in this.config) ? this.config.controllerCardOptions : {}; const areaHeaderCardOptions = (
'headerCardConfiguration' in this.baseConfiguration ? this.baseConfiguration.headerCardConfiguration : {}
) as CustomHeaderCardConfig;
// Create and insert a Controller card. // Create and insert a Header card.
areaCards.unshift(new ControllerCard(target, Object.assign({title: area.name}, titleCardOptions)).createCard()); areaCards.unshift(new HeaderCard(target, { title: area.name, ...areaHeaderCardOptions }).createCard());
viewCards.push({ viewCards.push({ type: 'vertical-stack', cards: areaCards });
type: "vertical-stack",
cards: areaCards,
} as StackCardConfig);
} }
} }
// Add a Controller Card for all the entities in the view. // Add a Header Card to control all the entities in the view.
if (this.viewControllerCard.cards.length && viewCards.length) { if (this.viewHeaderCardConfiguration.cards.length && viewCards.length) {
viewCards.unshift(this.viewControllerCard); viewCards.unshift(this.viewHeaderCardConfiguration);
} }
return viewCards; return viewCards;
} }
/** /**
* Get a view object. * Get a view configuration.
* *
* The view includes the cards which are created by method createViewCards(). * The configuration includes the card configurations which are created by createCardConfigurations().
*
* @returns {Promise<LovelaceViewConfig>} The view object.
*/ */
async getView(): Promise<LovelaceViewConfig> { async getView(): Promise<LovelaceViewConfig> {
return { return {
...this.config, ...this.baseConfiguration,
cards: await this.createViewCards(), cards: await this.createCardConfigurations(),
}; };
} }
/** /**
* Get a target of entity IDs for the given domain. * Get the domain's entity ids to target for a HASS service call.
*
* @param {string} domain - The target domain to retrieve entity IDs from.
* @returns {HassServiceTarget} - A target for a service call.
*/ */
targetDomain(domain: string): HassServiceTarget { private getDomainTargets(): HassServiceTarget {
return { return {
entity_id: Helper.entities.filter( entity_id: Registry.entities
entity => .filter((entity) => entity.entity_id.startsWith(this.domain + '.'))
entity.entity_id.startsWith(domain + ".") .map((entity) => entity.entity_id),
&& !entity.hidden_by
&& !Helper.strategyOptions.card_options?.[entity.entity_id]?.hidden
).map(entity => entity.entity_id),
}; };
} }
/**
* Initialize the view configuration with defaults and custom settings.
*
* @param viewConfiguration The view's default configuration for the view.
* @param customConfiguration The view's custom configuration to apply.
* @param headerCardConfig The view's Header card configuration.
*/
protected initializeViewConfig(
viewConfiguration: ViewConfig,
customConfiguration: ViewConfig = {},
headerCardConfig: CustomHeaderCardConfig,
): void {
this.baseConfiguration = { ...this.baseConfiguration, ...viewConfiguration, ...customConfiguration };
this.viewHeaderCardConfiguration = new HeaderCard(this.getDomainTargets(), {
...(('headerCardConfiguration' in this.baseConfiguration
? this.baseConfiguration.headerCardConfiguration
: {}) as StrategyHeaderCardConfig),
...headerCardConfig,
}).createCard();
}
} }
export {AbstractView}; export default AbstractView;

View File

@@ -1,77 +1,54 @@
import {ControllerCard} from "../cards/ControllerCard";
import {AbstractView} from "./AbstractView";
import {views} from "../types/strategy/views";
import {cards} from "../types/strategy/cards";
import {Helper} from "../Helper";
import {generic} from "../types/strategy/generic";
import SupportedDomains = generic.SupportedDomains;
// noinspection JSUnusedGlobalSymbols Class is dynamically imported. // noinspection JSUnusedGlobalSymbols Class is dynamically imported.
import { Registry } from '../Registry';
import { CustomHeaderCardConfig } from '../types/strategy/strategy-cards';
import { SupportedDomains } from '../types/strategy/strategy-generics';
import { ViewConfig } from '../types/strategy/strategy-views';
import { localize } from '../utilities/localize';
import AbstractView from './AbstractView';
/** /**
* Camera View Class. * Camera View Class.
* *
* Used to create a view for entities of the camera domain. * Used to create a view configuration for entities of the camera domain.
*
* @class CameraView
* @extends AbstractView
*/ */
class CameraView extends AbstractView { class CameraView extends AbstractView {
/** /** The domain of the entities that the view is representing. */
* Domain of the view's entities. static readonly domain: SupportedDomains = 'camera' as const;
*
* @type {SupportedDomains}
* @static
* @private
*/
static #domain: SupportedDomains = "camera";
/** /** Returns the default configuration object for the view. */
* Default configuration of the view. static getDefaultConfig(): ViewConfig {
* return {
* @type {views.ViewConfig} title: localize('camera.cameras'),
* @private path: 'cameras',
*/ icon: 'mdi:cctv',
#defaultConfig: views.ViewConfig = { subview: false,
title: Helper.customLocalize("camera.cameras"), headerCardConfiguration: {
path: "cameras", showControls: false,
icon: "mdi:cctv", },
subview: false, };
controllerCardOptions: { }
showControls: false,
},
};
/** /** Returns the default configuration of the view's Header card. */
* Default configuration of the view's Controller card. static getViewHeaderCardConfig(): CustomHeaderCardConfig {
* return {
* @type {cards.ControllerCardOptions} title: localize('camera.all_cameras'),
* @private subtitle:
*/ `${Registry.getCountTemplate(CameraView.domain, 'ne', 'off')} ${localize('camera.cameras')} ` +
#viewControllerCardConfig: cards.ControllerCardOptions = { localize('generic.busy'),
title: Helper.customLocalize("camera.all_cameras"), };
subtitle: }
`${Helper.getCountTemplate(CameraView.#domain, "ne", "off")} ${Helper.customLocalize("camera.cameras")} `
+ Helper.customLocalize("generic.busy"),
};
/** /**
* Class constructor. * Class constructor.
* *
* @param {views.ViewConfig} [options={}] Options for the view. * @param {ViewConfig} [customConfiguration] Custom view configuration.
*/ */
constructor(options: views.ViewConfig = {}) { constructor(customConfiguration?: ViewConfig) {
super(CameraView.#domain); super();
this.config = Object.assign(this.config, this.#defaultConfig, options); this.initializeViewConfig(CameraView.getDefaultConfig(), customConfiguration, CameraView.getViewHeaderCardConfig());
// Create a Controller card to switch all entities of the domain.
this.viewControllerCard = new ControllerCard(
{},
{
...this.#viewControllerCardConfig,
...("controllerCardOptions" in this.config ? this.config.controllerCardOptions : {}) as cards.ControllerCardConfig,
}).createCard();
} }
} }
export {CameraView}; export default CameraView;

View File

@@ -1,77 +1,58 @@
import {Helper} from "../Helper";
import {ControllerCard} from "../cards/ControllerCard";
import {AbstractView} from "./AbstractView";
import {views} from "../types/strategy/views";
import {cards} from "../types/strategy/cards";
import {generic} from "../types/strategy/generic";
import SupportedDomains = generic.SupportedDomains;
// noinspection JSUnusedGlobalSymbols Class is dynamically imported. // noinspection JSUnusedGlobalSymbols Class is dynamically imported.
import { Registry } from '../Registry';
import { CustomHeaderCardConfig } from '../types/strategy/strategy-cards';
import { SupportedDomains } from '../types/strategy/strategy-generics';
import { ViewConfig } from '../types/strategy/strategy-views';
import { localize } from '../utilities/localize';
import AbstractView from './AbstractView';
/** /**
* Climate View Class. * Climate View Class.
* *
* Used to create a view for entities of the climate domain. * Used to create a view configuration for entities of the climate domain.
*
* @class ClimateView
* @extends AbstractView
*/ */
class ClimateView extends AbstractView { class ClimateView extends AbstractView {
/** /**The domain of the entities that the view is representing. */
* Domain of the view's entities. static readonly domain: SupportedDomains = 'climate' as const;
*
* @type {SupportedDomains}
* @static
* @private
*/
static #domain: SupportedDomains = "climate";
/** /** Returns the default configuration object for the view. */
* Default configuration of the view. static getDefaultConfig(): ViewConfig {
* return {
* @type {views.ViewConfig} title: localize('climate.climates'),
* @private path: 'climates',
*/ icon: 'mdi:thermostat',
#defaultConfig: views.ViewConfig = { subview: false,
title: Helper.customLocalize("climate.climates"), headerCardConfiguration: {
path: "climates", showControls: false,
icon: "mdi:thermostat", },
subview: false, };
controllerCardOptions: { }
showControls: false,
},
};
/** /** Returns the default configuration of the view's Header card. */
* Default configuration of the view's Controller card. static getViewHeaderCardConfig(): CustomHeaderCardConfig {
* return {
* @type {cards.ControllerCardOptions} title: localize('climate.all_climates'),
* @private subtitle:
*/ `${Registry.getCountTemplate(ClimateView.domain, 'ne', 'off')} ${localize('climate.climates')} ` +
#viewControllerCardConfig: cards.ControllerCardOptions = { localize('generic.busy'),
title: Helper.customLocalize("climate.all_climates"), };
subtitle: }
`${Helper.getCountTemplate(ClimateView.#domain, "ne", "off")} ${Helper.customLocalize("climate.climates")} `
+ Helper.customLocalize("generic.busy"),
};
/** /**
* Class constructor. * Class constructor.
* *
* @param {views.ViewConfig} [options={}] Options for the view. * @param {ViewConfig} [customConfiguration] Custom view configuration.
*/ */
constructor(options: views.ViewConfig = {}) { constructor(customConfiguration?: ViewConfig) {
super(ClimateView.#domain); super();
this.config = Object.assign(this.config, this.#defaultConfig, options); this.initializeViewConfig(
ClimateView.getDefaultConfig(),
// Create a Controller card to switch all entities of the domain. customConfiguration,
this.viewControllerCard = new ControllerCard( ClimateView.getViewHeaderCardConfig(),
this.targetDomain(ClimateView.#domain), );
{
...this.#viewControllerCardConfig,
...("controllerCardOptions" in this.config ? this.config.controllerCardOptions : {}) as cards.ControllerCardConfig,
}).createCard();
} }
} }
export {ClimateView}; export default ClimateView;

View File

@@ -1,80 +1,58 @@
import {Helper} from "../Helper";
import {ControllerCard} from "../cards/ControllerCard";
import {AbstractView} from "./AbstractView";
import {views} from "../types/strategy/views";
import {cards} from "../types/strategy/cards";
import {generic} from "../types/strategy/generic";
import SupportedDomains = generic.SupportedDomains;
// noinspection JSUnusedGlobalSymbols Class is dynamically imported. // noinspection JSUnusedGlobalSymbols Class is dynamically imported.
import { Registry } from '../Registry';
import { CustomHeaderCardConfig } from '../types/strategy/strategy-cards';
import { SupportedDomains } from '../types/strategy/strategy-generics';
import { ViewConfig } from '../types/strategy/strategy-views';
import { localize } from '../utilities/localize';
import AbstractView from './AbstractView';
/** /**
* Cover View Class. * Cover View Class.
* *
* Used to create a view for entities of the cover domain. * Used to create a view configuration for entities of the cover domain.
*
* @class CoverView
* @extends AbstractView
*/ */
class CoverView extends AbstractView { class CoverView extends AbstractView {
/** /** The domain of the entities that the view is representing. */
* Domain of the view's entities. static readonly domain: SupportedDomains = 'cover' as const;
*
* @type {SupportedDomains}
* @static
* @private
*/
static #domain: SupportedDomains = "cover";
/** /** Returns the default configuration object for the view. */
* Default configuration of the view. static getDefaultConfig(): ViewConfig {
* return {
* @type {views.ViewConfig} title: localize('cover.covers'),
* @private path: 'covers',
*/ icon: 'mdi:window-open',
#defaultConfig: views.ViewConfig = { subview: false,
title: Helper.customLocalize("cover.covers"), headerCardConfiguration: {
path: "covers", iconOn: 'mdi:arrow-up',
icon: "mdi:window-open", iconOff: 'mdi:arrow-down',
subview: false, onService: 'cover.open_cover',
controllerCardOptions: { offService: 'cover.close_cover',
iconOn: "mdi:arrow-up", },
iconOff: "mdi:arrow-down", };
onService: "cover.open_cover", }
offService: "cover.close_cover",
},
};
/** /** Returns the default configuration of the view's Header card. */
* Default configuration of the view's Controller card. static getViewHeaderCardConfig(): CustomHeaderCardConfig {
* return {
* @type {cards.ControllerCardOptions} title: localize('cover.all_covers'),
* @private subtitle:
*/ `${Registry.getCountTemplate(CoverView.domain, 'search', '(open|opening|closing)')} ` +
#viewControllerCardConfig: cards.ControllerCardOptions = { `${localize('cover.covers')} ` +
title: Helper.customLocalize("cover.all_covers"), `${localize('generic.unclosed')}`,
subtitle: };
`${Helper.getCountTemplate(CoverView.#domain, "search", "(open|opening|closing)")} ${Helper.customLocalize("cover.covers")} ` }
+ Helper.customLocalize("generic.unclosed"),
};
/** /**
* Class constructor. * Class constructor.
* *
* @param {views.ViewConfig} [options={}] Options for the view. * @param {ViewConfig} [customConfiguration] Custom view configuration.
*/ */
constructor(options: views.ViewConfig = {}) { constructor(customConfiguration?: ViewConfig) {
super(CoverView.#domain); super();
this.config = Object.assign(this.config, this.#defaultConfig, options); this.initializeViewConfig(CoverView.getDefaultConfig(), customConfiguration, CoverView.getViewHeaderCardConfig());
// Create a Controller card to switch all entities of the domain.
this.viewControllerCard = new ControllerCard(
this.targetDomain(CoverView.#domain),
{
...this.#viewControllerCardConfig,
...("controllerCardOptions" in this.config ? this.config.controllerCardOptions : {}) as cards.ControllerCardConfig,
}).createCard();
} }
} }
export {CoverView}; export default CoverView;

View File

@@ -1,80 +1,56 @@
import {Helper} from "../Helper";
import {ControllerCard} from "../cards/ControllerCard";
import {AbstractView} from "./AbstractView";
import {views} from "../types/strategy/views";
import {cards} from "../types/strategy/cards";
import {generic} from "../types/strategy/generic";
import SupportedDomains = generic.SupportedDomains;
// noinspection JSUnusedGlobalSymbols Class is dynamically imported. // noinspection JSUnusedGlobalSymbols Class is dynamically imported.
import { Registry } from '../Registry';
import { CustomHeaderCardConfig } from '../types/strategy/strategy-cards';
import { SupportedDomains } from '../types/strategy/strategy-generics';
import { ViewConfig } from '../types/strategy/strategy-views';
import { localize } from '../utilities/localize';
import AbstractView from './AbstractView';
/** /**
* Fan View Class. * Fan View Class.
* *
* Used to create a view for entities of the fan domain. * Used to create a view configuration for entities of the fan domain.
*
* @class FanView
* @extends AbstractView
*/ */
class FanView extends AbstractView { class FanView extends AbstractView {
/** /** The domain of the entities that the view is representing. */
* Domain of the view's entities. static readonly domain: SupportedDomains = 'fan' as const;
*
* @type {SupportedDomains}
* @static
* @private
*/
static #domain: SupportedDomains = "fan";
/** /** Returns the default configuration object for the view. */
* Default configuration of the view. static getDefaultConfig(): ViewConfig {
* return {
* @type {views.ViewConfig} title: localize('fan.fans'),
* @private path: 'fans',
*/ icon: 'mdi:fan',
#defaultConfig: views.ViewConfig = { subview: false,
title: Helper.customLocalize("fan.fans"), headerCardConfiguration: {
path: "fans", iconOn: 'mdi:fan',
icon: "mdi:fan", iconOff: 'mdi:fan-off',
subview: false, onService: 'fan.turn_on',
controllerCardOptions: { offService: 'fan.turn_off',
iconOn: "mdi:fan", },
iconOff: "mdi:fan-off", };
onService: "fan.turn_on", }
offService: "fan.turn_off",
},
};
/** /** Returns the default configuration of the view's Header card. */
* Default configuration of the view's Controller card. static getViewHeaderCardConfig(): CustomHeaderCardConfig {
* return {
* @type {cards.ControllerCardOptions} title: localize('fan.all_fans'),
* @private subtitle:
*/ `${Registry.getCountTemplate(FanView.domain, 'eq', 'on')} ${localize('fan.fans')} ` + localize('generic.on'),
#viewControllerCardConfig: cards.ControllerCardOptions = { };
title: Helper.customLocalize("fan.all_fans"), }
subtitle:
`${Helper.getCountTemplate(FanView.#domain, "eq", "on")} ${Helper.customLocalize("fan.fans")} `
+ Helper.customLocalize("generic.on"),
};
/** /**
* Class constructor. * Class constructor.
* *
* @param {views.ViewConfig} [options={}] Options for the view. * @param {ViewConfig} [customConfiguration] Custom view configuration.
*/ */
constructor(options: views.ViewConfig = {}) { constructor(customConfiguration?: ViewConfig) {
super(FanView.#domain); super();
this.config = Object.assign(this.config, this.#defaultConfig, options); this.initializeViewConfig(FanView.getDefaultConfig(), customConfiguration, FanView.getViewHeaderCardConfig());
// Create a Controller card to switch all entities of the domain.
this.viewControllerCard = new ControllerCard(
this.targetDomain(FanView.#domain),
{
...this.#viewControllerCardConfig,
...("controllerCardOptions" in this.config ? this.config.controllerCardOptions : {}) as cards.ControllerCardConfig,
}).createCard();
} }
} }
export {FanView}; export default FanView;

View File

@@ -1,65 +1,71 @@
import {Helper} from "../Helper";
import {AbstractView} from "./AbstractView";
import {views} from "../types/strategy/views";
import {LovelaceChipConfig} from "../types/lovelace-mushroom/utils/lovelace/chip/types";
import {ChipsCardConfig} from "../types/lovelace-mushroom/cards/chips-card";
import {TemplateCardConfig} from "../types/lovelace-mushroom/cards/template-card-config";
import {ActionConfig} from "../types/homeassistant/data/lovelace";
import {TitleCardConfig} from "../types/lovelace-mushroom/cards/title-card-config";
import {PersonCardConfig} from "../types/lovelace-mushroom/cards/person-card-config";
import {AreaCardConfig, StackCardConfig} from "../types/homeassistant/panels/lovelace/cards/types";
import {generic} from "../types/strategy/generic";
import supportedChips = generic.SupportedChips;
// noinspection JSUnusedGlobalSymbols Class is dynamically imported. // noinspection JSUnusedGlobalSymbols Class is dynamically imported.
import { Registry } from '../Registry';
import { ActionConfig } from '../types/homeassistant/data/lovelace/config/action';
import { LovelaceCardConfig } from '../types/homeassistant/data/lovelace/config/card';
import { AreaCardConfig, StackCardConfig } from '../types/homeassistant/panels/lovelace/cards/types';
import { ChipsCardConfig } from '../types/lovelace-mushroom/cards/chips-card';
import { PersonCardConfig } from '../types/lovelace-mushroom/cards/person-card-config';
import { TemplateCardConfig } from '../types/lovelace-mushroom/cards/template-card-config';
import { LovelaceChipConfig } from '../types/lovelace-mushroom/utils/lovelace/chip/types';
import { HomeViewSections, isSupportedChip } from '../types/strategy/strategy-generics';
import { ViewConfig } from '../types/strategy/strategy-views';
import { sanitizeClassName } from '../utilities/auxiliaries';
import { logMessage, lvlError } from '../utilities/debug';
import { localize } from '../utilities/localize';
import AbstractView from './AbstractView';
/** /**
* Home View Class. * Home View Class.
* *
* Used to create a Home view. * Used to create a Home view.
*
* @class HomeView
* @extends AbstractView
*/ */
class HomeView extends AbstractView { class HomeView extends AbstractView {
/** /** The domain of the entities that the view is representing. */
* Default configuration of the view. static readonly domain = 'home' as const;
*
* @type {views.ViewConfig} /** Returns the default configuration object for the view. */
* @private static getDefaultConfig(): ViewConfig {
*/ return {
#defaultConfig: views.ViewConfig = { title: localize('generic.home'),
title: Helper.customLocalize("generic.home"), icon: 'mdi:home-assistant',
icon: "mdi:home-assistant", path: 'home',
path: "home", subview: false,
subview: false, };
}; }
/** /**
* Class constructor. * Class constructor.
* *
* @param {views.ViewConfig} [options={}] Options for the view. * @param {ViewConfig} [customConfiguration] Custom view configuration.
*/ */
constructor(options: views.ViewConfig = {}) { constructor(customConfiguration?: ViewConfig) {
super("home"); super();
this.config = Object.assign(this.config, this.#defaultConfig, options); this.baseConfiguration = { ...this.baseConfiguration, ...HomeView.getDefaultConfig(), ...customConfiguration };
} }
/** /**
* Create the cards to include in the view. * Create the configuration of the cards to include in the view.
* *
* @returns {Promise<(StackCardConfig | TemplateCardConfig | ChipsCardConfig)[]>} Promise a View Card array.
* @override * @override
*/ */
async createViewCards(): Promise<(StackCardConfig | TemplateCardConfig | ChipsCardConfig)[]> { async createCardConfigurations(): Promise<LovelaceCardConfig[]> {
return await Promise.all([ const homeViewCards: LovelaceCardConfig[] = [];
this.#createChips(),
this.#createPersonCards(), let chipsSection, personsSection, areasSection;
this.#createAreaSection(),
]).then(([chips, personCards, areaCards]) => { try {
const options = Helper.strategyOptions; [chipsSection, personsSection, areasSection] = await Promise.all([
const homeViewCards = []; this.createChipsSection(),
this.createPersonsSection(),
this.createAreasSection(),
]);
} catch (e) {
logMessage(lvlError, 'Error importing created sections!', e);
return homeViewCards;
}
if (chips.length) { if (chips.length) {
// TODO: Create the Chip card at this.#createChips() // TODO: Create the Chip card at this.#createChips()

View File

@@ -1,12 +1,11 @@
import {Helper} from "../Helper";
import {ControllerCard} from "../cards/ControllerCard";
import {AbstractView} from "./AbstractView";
import {views} from "../types/strategy/views";
import {cards} from "../types/strategy/cards";
import {generic} from "../types/strategy/generic";
import SupportedDomains = generic.SupportedDomains;
// noinspection JSUnusedGlobalSymbols Class is dynamically imported. // noinspection JSUnusedGlobalSymbols Class is dynamically imported.
import { Registry } from '../Registry';
import { CustomHeaderCardConfig } from '../types/strategy/strategy-cards';
import { ViewConfig } from '../types/strategy/strategy-views';
import { localize } from '../utilities/localize';
import AbstractView from './AbstractView';
/** /**
* Light View Class. * Light View Class.
* *
@@ -16,65 +15,40 @@ import SupportedDomains = generic.SupportedDomains;
* @extends AbstractView * @extends AbstractView
*/ */
class LightView extends AbstractView { class LightView extends AbstractView {
/** /** The domain of the entities that the view is representing. */
* Domain of the view's entities. static readonly domain = 'light' as const;
*
* @type {SupportedDomains}
* @static
* @private
*/
static #domain: SupportedDomains = "light";
/** /** Returns the default configuration object for the view. */
* Default configuration of the view. static getDefaultConfig(): ViewConfig {
* return {
* @type {views.ViewConfig} title: localize('light.lights'),
* @private path: 'lights',
*/ icon: 'mdi:lightbulb-group',
#defaultConfig: views.ViewConfig = { subview: false,
title: Helper.customLocalize("light.lights"), headerCardConfiguration: {
path: "lights", iconOn: 'mdi:lightbulb',
icon: "mdi:lightbulb-group", iconOff: 'mdi:lightbulb-off',
subview: false, onService: 'light.turn_on',
controllerCardOptions: { offService: 'light.turn_off',
iconOn: "mdi:lightbulb", },
iconOff: "mdi:lightbulb-off", };
onService: "light.turn_on", }
offService: "light.turn_off",
},
};
/** /** Returns the default configuration of the view's Header card. */
* Default configuration of the view's Controller card. static getViewHeaderCardConfig(): CustomHeaderCardConfig {
* return {
* @type {cards.ControllerCardOptions} title: localize('light.all_lights'),
* @private subtitle:
*/ `${Registry.getCountTemplate(LightView.domain, 'eq', 'on')} ${localize('light.lights')} ` +
#viewControllerCardConfig: cards.ControllerCardOptions = { localize('generic.on'),
title: Helper.customLocalize("light.all_lights"), };
subtitle: }
`${Helper.getCountTemplate(LightView.#domain, "eq", "on")} ${Helper.customLocalize("light.lights")} `
+ Helper.customLocalize("generic.on"),
};
/** constructor(customConfiguration?: ViewConfig) {
* Class constructor. super();
*
* @param {views.ViewConfig} [options={}] Options for the view.
*/
constructor(options: views.ViewConfig = {}) {
super(LightView.#domain);
this.config = Object.assign(this.config, this.#defaultConfig, options); this.initializeViewConfig(LightView.getDefaultConfig(), customConfiguration, LightView.getViewHeaderCardConfig());
// Create a Controller card to switch all entities of the domain.
this.viewControllerCard = new ControllerCard(
this.targetDomain(LightView.#domain),
{
...this.#viewControllerCardConfig,
...("controllerCardOptions" in this.config ? this.config.controllerCardOptions : {}) as cards.ControllerCardConfig,
}).createCard();
} }
} }
export {LightView}; export default LightView;

View File

@@ -1,54 +1,47 @@
import {Helper} from "../Helper";
import {AbstractView} from "./AbstractView";
import {views} from "../types/strategy/views";
import {generic} from "../types/strategy/generic";
import SupportedDomains = generic.SupportedDomains;
// noinspection JSUnusedGlobalSymbols Class is dynamically imported. // noinspection JSUnusedGlobalSymbols Class is dynamically imported.
import { CustomHeaderCardConfig } from '../types/strategy/strategy-cards';
import { ViewConfig } from '../types/strategy/strategy-views';
import { localize } from '../utilities/localize';
import AbstractView from './AbstractView';
/** /**
* Scene View Class. * Scene View Class.
* *
* Used to create a view for entities of the scene domain. * sed to create a view configuration for entities of the scene domain.
*
* @class SceneView
* @extends AbstractView
*/ */
class SceneView extends AbstractView { class SceneView extends AbstractView {
/** /** The domain of the entities that the view is representing. */
* Domain of the view's entities. static readonly domain = 'scene' as const;
*
* @type {SupportedDomains}
* @static
* @private
*/
static #domain: SupportedDomains = "scene";
/** /** Returns the default configuration object for the view. */
* Default configuration of the view. static getDefaultConfig(): ViewConfig {
* return {
* @type {views.ViewConfig} title: localize('scene.scenes'),
* @private path: 'scenes',
*/ icon: 'mdi:palette',
#defaultConfig: views.ViewConfig = { subview: false,
title: Helper.customLocalize("scene.scenes"), headerCardConfiguration: {
path: "scenes", showControls: false,
icon: "mdi:palette", },
subview: false, };
controllerCardOptions: { }
showControls: false,
}, /** Returns the default configuration of the view's Header card. */
}; static getViewHeaderCardConfig(): CustomHeaderCardConfig {
return {};
}
/** /**
* Class constructor. * Class constructor.
* *
* @param {views.ViewConfig} [options={}] Options for the view. * @param {ViewConfig} [customConfiguration] Custom view configuration.
*/ */
constructor(options: views.ViewConfig = {}) { constructor(customConfiguration?: ViewConfig) {
super(SceneView.#domain); super();
this.config = Object.assign(this.config, this.#defaultConfig, options); this.initializeViewConfig(SceneView.getDefaultConfig(), customConfiguration, SceneView.getViewHeaderCardConfig());
} }
} }
export {SceneView}; export default SceneView;

View File

@@ -1,80 +1,56 @@
import {Helper} from "../Helper";
import {ControllerCard} from "../cards/ControllerCard";
import {AbstractView} from "./AbstractView";
import {views} from "../types/strategy/views";
import {cards} from "../types/strategy/cards";
import {generic} from "../types/strategy/generic";
import SupportedDomains = generic.SupportedDomains;
// noinspection JSUnusedGlobalSymbols Class is dynamically imported. // noinspection JSUnusedGlobalSymbols Class is dynamically imported.
import { Registry } from '../Registry';
import { CustomHeaderCardConfig } from '../types/strategy/strategy-cards';
import { ViewConfig } from '../types/strategy/strategy-views';
import { localize } from '../utilities/localize';
import AbstractView from './AbstractView';
/** /**
* Switch View Class. * Switch View Class.
* *
* Used to create a view for entities of the switch domain. * Used to create a view configuration for entities of the switch domain.
*
* @class SwitchView
* @extends AbstractView
*/ */
class SwitchView extends AbstractView { class SwitchView extends AbstractView {
/** /** The domain of the entities that the view is representing. */
* Domain of the view's entities. static readonly domain = 'switch' as const;
*
* @type {SupportedDomains}
* @static
* @private
*/
static #domain: SupportedDomains = "switch";
/** /** Returns the default configuration object for the view. */
* Default configuration of the view. static getDefaultConfig(): ViewConfig {
* return {
* @type {views.ViewConfig} title: localize('switch.switches'),
* @private path: 'switches',
*/ icon: 'mdi:dip-switch',
#defaultConfig: views.ViewConfig = { subview: false,
title: Helper.customLocalize("switch.switches"), headerCardConfiguration: {
path: "switches", iconOn: 'mdi:power-plug',
icon: "mdi:dip-switch", iconOff: 'mdi:power-plug-off',
subview: false, onService: 'switch.turn_on',
controllerCardOptions: { offService: 'switch.turn_off',
iconOn: "mdi:power-plug", },
iconOff: "mdi:power-plug-off", };
onService: "switch.turn_on", }
offService: "switch.turn_off",
},
};
/** /** Returns the default configuration of the view's Header card. */
* Default configuration of the view's Controller card. static getViewHeaderCardConfig(): CustomHeaderCardConfig {
* return {
* @type {cards.ControllerCardOptions} title: localize('switch.all_switches'),
* @private subtitle:
*/ `${Registry.getCountTemplate(SwitchView.domain, 'eq', 'on')} ${localize('switch.switches')} ` +
#viewControllerCardConfig: cards.ControllerCardOptions = { localize('generic.on'),
title: Helper.customLocalize("switch.all_switches"), };
subtitle: }
`${Helper.getCountTemplate(SwitchView.#domain, "eq", "on")} ${Helper.customLocalize("switch.switches")} `
+ Helper.customLocalize("generic.on"),
};
/** /**
* Class constructor. * Class constructor.
* *
* @param {views.ViewConfig} [options={}] Options for the view. * @param {ViewConfig} [customConfiguration] Custom view configuration.
*/ */
constructor(options: views.ViewConfig = {}) { constructor(customConfiguration?: ViewConfig) {
super(SwitchView.#domain); super();
this.config = Object.assign(this.config, this.#defaultConfig, options); this.initializeViewConfig(SwitchView.getDefaultConfig(), customConfiguration, SwitchView.getViewHeaderCardConfig());
// Create a Controller card to switch all entities of the domain.
this.viewControllerCard = new ControllerCard(
this.targetDomain(SwitchView.#domain),
{
...this.#viewControllerCardConfig,
...("controllerCardOptions" in this.config ? this.config.controllerCardOptions : {}) as cards.ControllerCardConfig,
}).createCard();
} }
} }
export {SwitchView}; export default SwitchView;

View File

@@ -1,80 +1,56 @@
import {Helper} from "../Helper";
import {ControllerCard} from "../cards/ControllerCard";
import {AbstractView} from "./AbstractView";
import {views} from "../types/strategy/views";
import {cards} from "../types/strategy/cards";
import {generic} from "../types/strategy/generic";
import SupportedDomains = generic.SupportedDomains;
// noinspection JSUnusedGlobalSymbols Class is dynamically imported. // noinspection JSUnusedGlobalSymbols Class is dynamically imported.
import { Registry } from '../Registry';
import { CustomHeaderCardConfig } from '../types/strategy/strategy-cards';
import { ViewConfig } from '../types/strategy/strategy-views';
import { localize } from '../utilities/localize';
import AbstractView from './AbstractView';
/** /**
* Vacuum View Class. * Vacuum View Class.
* *
* Used to create a view for entities of the vacuum domain. * Used to create a view configuration for entities of the vacuum domain.
*
* @class VacuumView
* @extends AbstractView
*/ */
class VacuumView extends AbstractView { class VacuumView extends AbstractView {
/** /** The domain of the entities that the view is representing. */
* Domain of the view's entities. static readonly domain = 'vacuum' as const;
*
* @type {SupportedDomains}
* @static
* @private
*/
static #domain: SupportedDomains = "vacuum";
/** /** Returns the default configuration object for the view. */
* Default configuration of the view. static getDefaultConfig(): ViewConfig {
* return {
* @type {views.ViewConfig} title: localize('vacuum.vacuums'),
* @private path: 'vacuums',
*/ icon: 'mdi:robot-vacuum',
#defaultConfig: views.ViewConfig = { subview: false,
title: Helper.customLocalize("vacuum.vacuums"), headerCardConfiguration: {
path: "vacuums", iconOn: 'mdi:robot-vacuum',
icon: "mdi:robot-vacuum", iconOff: 'mdi:robot-vacuum-off',
subview: false, onService: 'vacuum.start',
controllerCardOptions: { offService: 'vacuum.stop',
iconOn: "mdi:robot-vacuum", },
iconOff: "mdi:robot-vacuum-off", };
onService: "vacuum.start", }
offService: "vacuum.stop",
},
};
/** /** Returns the default configuration of the view's Header card. */
* Default configuration of the view's Controller card. static getViewHeaderCardConfig(): CustomHeaderCardConfig {
* return {
* @type {cards.ControllerCardOptions} title: localize('vacuum.all_vacuums'),
* @private subtitle:
*/ `${Registry.getCountTemplate(VacuumView.domain, 'ne', 'off')} ${localize('vacuum.vacuums')} ` +
#viewControllerCardConfig: cards.ControllerCardOptions = { localize('generic.busy'),
title: Helper.customLocalize("vacuum.all_vacuums"), };
subtitle: }
`${Helper.getCountTemplate(VacuumView.#domain, "ne", "off")} ${Helper.customLocalize("vacuum.vacuums")} `
+ Helper.customLocalize("generic.busy"),
};
/** /**
* Class constructor. * Class constructor.
* *
* @param {views.ViewConfig} [options={}] Options for the view. * @param {ViewConfig} [customConfiguration] Custom view configuration.
*/ */
constructor(options: views.ViewConfig = {}) { constructor(customConfiguration?: ViewConfig) {
super(VacuumView.#domain); super();
this.config = Object.assign(this.config, this.#defaultConfig, options); this.initializeViewConfig(VacuumView.getDefaultConfig(), customConfiguration, VacuumView.getViewHeaderCardConfig());
// Create a Controller card to switch all entities of the domain.
this.viewControllerCard = new ControllerCard(
this.targetDomain(VacuumView.#domain),
{
...this.#viewControllerCardConfig,
...("controllerCardOptions" in this.config ? this.config.controllerCardOptions : {}) as cards.ControllerCardConfig,
}).createCard();
} }
} }
export {VacuumView}; export default VacuumView;