mirror of
https://github.com/DigiLive/mushroom-strategy.git
synced 2025-08-04 20:14:28 +02:00
Optimize types
The type definitions and interfaces of Home Assistant and Mushroom where outdated. Also, the type definitions and interfaces of the strategy could use some refinement.
This commit is contained in:
1
dist/mushroom-strategy.js
vendored
1
dist/mushroom-strategy.js
vendored
File diff suppressed because one or more lines are too long
1578
package-lock.json
generated
1578
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
39
package.json
39
package.json
@@ -1,49 +1,38 @@
|
||||
{
|
||||
"name": "mushroom-strategy",
|
||||
"version": "2.3.0",
|
||||
"description": "Automatically generate a dashboard of Mushroom cards.",
|
||||
"version": "2.2.1",
|
||||
"description": "Automatically create a dashboard using Mushroom cards",
|
||||
"keywords": [
|
||||
"dashboard",
|
||||
"strategy",
|
||||
"mushroom"
|
||||
],
|
||||
"homepage": "https://github.com/DigiLive/mushroom-strategy",
|
||||
"bugs": "https://github.com/DigiLive/mushroom-strategy/issues",
|
||||
"homepage": "https://github.com/AalianKhan/mushroom-strategy",
|
||||
"bugs": "https://github.com/AalianKhan/mushroom-strategy/issues",
|
||||
"license": "MIT",
|
||||
"author": {
|
||||
"name": "Ferry Cools"
|
||||
"name": "Aalian Khan"
|
||||
},
|
||||
"contributors": [
|
||||
{
|
||||
"name": "Aalian Khan"
|
||||
"name": "Ferry Cools"
|
||||
}
|
||||
],
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/DigiLive"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/DigiLive/mushroom-strategy"
|
||||
"url": "https://github.com/AalianKhan/mushroom-strategy"
|
||||
},
|
||||
"dependencies": {
|
||||
"deepmerge": "^4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@typescript-eslint/eslint-plugin": "^8.31.0",
|
||||
"@typescript-eslint/parser": "^8.31.0",
|
||||
"eslint": "^9.25.1",
|
||||
"eslint-config-prettier": "^10.1.2",
|
||||
"eslint-plugin-prettier": "^5.2.6",
|
||||
"home-assistant-js-websocket": "^9.5.0",
|
||||
"prettier": "^3.5.3",
|
||||
"superstruct": "^2.0.2",
|
||||
"ts-loader": "^9.5.2",
|
||||
"ts-node": "^10.9.2",
|
||||
"typescript": "^5.8.3",
|
||||
"home-assistant-js-websocket": "^9",
|
||||
"superstruct": "^1",
|
||||
"ts-loader": "^9.5.0",
|
||||
"ts-node": "^10",
|
||||
"typescript": "^5",
|
||||
"version-bump-prompt": "^6",
|
||||
"webpack": "^5.99.7",
|
||||
"webpack-cli": "^6.0.1"
|
||||
"webpack": "^5.96.1",
|
||||
"webpack-cli": "^5.1.4"
|
||||
},
|
||||
"scripts": {
|
||||
"build-dev": "webpack --config webpack.dev.config.ts",
|
||||
|
522
src/Helper.ts
Normal file
522
src/Helper.ts
Normal file
@@ -0,0 +1,522 @@
|
||||
import {getConfigurationDefaults} from "./configurationDefaults";
|
||||
import {HassEntities, HassEntity} from "home-assistant-js-websocket";
|
||||
import deepmerge from "deepmerge";
|
||||
import {EntityRegistryEntry} from "./types/homeassistant/data/entity_registry";
|
||||
import {DeviceRegistryEntry} from "./types/homeassistant/data/device_registry";
|
||||
import {AreaRegistryEntry} from "./types/homeassistant/data/area_registry";
|
||||
import {generic} from "./types/strategy/generic";
|
||||
import setupCustomLocalize from "./localize";
|
||||
import {applyEntityCategoryFilters} from "./utillties/filters";
|
||||
import StrategyArea = generic.StrategyArea;
|
||||
import ViewConfig = generic.StrategyViewConfig;
|
||||
import SupportedDomains = generic.SupportedDomains;
|
||||
import supportedViews = generic.SupportedViews;
|
||||
import isSortable = generic.isSortable;
|
||||
import AllDomainsConfig = generic.AllDomainsConfig;
|
||||
import SingleDomainConfig = generic.SingleDomainConfig;
|
||||
import isSupportedView = generic.isSupportedView;
|
||||
import isSupportedDomain = generic.isSupportedDomain;
|
||||
|
||||
/**
|
||||
* Helper Class
|
||||
*
|
||||
* Contains the objects of Home Assistant's registries and helper methods.
|
||||
*/
|
||||
class Helper {
|
||||
/**
|
||||
* An array of entities from Home Assistant's entity registry.
|
||||
*
|
||||
* @type {EntityRegistryEntry[]}
|
||||
* @private
|
||||
*/
|
||||
static #entities: EntityRegistryEntry[];
|
||||
|
||||
/**
|
||||
* An array of entities from Home Assistant's device registry.
|
||||
*
|
||||
* @type {DeviceRegistryEntry[]}
|
||||
* @private
|
||||
*/
|
||||
static #devices: DeviceRegistryEntry[];
|
||||
|
||||
/**
|
||||
* An array of entities from Home Assistant's area registry.
|
||||
*
|
||||
* @type {StrategyArea[]}
|
||||
* @private
|
||||
*/
|
||||
static #areas: StrategyArea[] = [];
|
||||
|
||||
/**
|
||||
* An array of state entities from Home Assistant's Hass-object.
|
||||
*
|
||||
* @type {HassEntities}
|
||||
* @private
|
||||
*/
|
||||
static #hassStates: HassEntities;
|
||||
|
||||
/**
|
||||
* Indicates whether this module is initialized.
|
||||
*
|
||||
* @type {boolean} True if initialized.
|
||||
* @private
|
||||
*/
|
||||
static #initialized: boolean = false;
|
||||
|
||||
/**
|
||||
* The Custom strategy configuration.
|
||||
*
|
||||
* @type {generic.StrategyConfig}
|
||||
* @private
|
||||
*/
|
||||
static #strategyOptions: generic.StrategyConfig;
|
||||
|
||||
/**
|
||||
* Set to true for more verbose information in the console.
|
||||
*
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
static #debug: boolean;
|
||||
static customLocalize: Function;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* This class shouldn't be instantiated directly.
|
||||
* Instead, it should be initialized with method initialize().
|
||||
*
|
||||
* @throws {Error} If trying to instantiate this class.
|
||||
*/
|
||||
constructor() {
|
||||
throw new Error("This class should be invoked with method initialize() instead of using the keyword new!");
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom strategy configuration.
|
||||
*
|
||||
* @returns {generic.StrategyConfig}
|
||||
* @static
|
||||
*/
|
||||
static get strategyOptions(): generic.StrategyConfig {
|
||||
return this.#strategyOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the entities from Home Assistant's area registry.
|
||||
*
|
||||
* @returns {StrategyArea[]}
|
||||
* @static
|
||||
*/
|
||||
static get areas(): StrategyArea[] {
|
||||
return this.#areas;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the devices from Home Assistant's device registry.
|
||||
*
|
||||
* @returns {DeviceRegistryEntry[]}
|
||||
* @static
|
||||
*/
|
||||
static get devices(): DeviceRegistryEntry[] {
|
||||
return this.#devices;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the entities from Home Assistant's entity registry.
|
||||
*
|
||||
* @returns {EntityRegistryEntry[]}
|
||||
* @static
|
||||
*/
|
||||
static get entities(): EntityRegistryEntry[] {
|
||||
return this.#entities;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current debug mode of the mushroom strategy.
|
||||
*
|
||||
* @returns {boolean}
|
||||
* @static
|
||||
*/
|
||||
static get debug(): boolean {
|
||||
return this.#debug;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize this module.
|
||||
*
|
||||
* @param {generic.DashboardInfo} info Strategy information object.
|
||||
* @returns {Promise<void>}
|
||||
* @static
|
||||
*/
|
||||
static async initialize(info: generic.DashboardInfo): Promise<void> {
|
||||
// Initialize properties.
|
||||
this.customLocalize = setupCustomLocalize(info.hass);
|
||||
|
||||
const configurationDefaults = getConfigurationDefaults(this.customLocalize)
|
||||
this.#strategyOptions = deepmerge(configurationDefaults, info.config?.strategy?.options ?? {});
|
||||
|
||||
this.#hassStates = info.hass.states;
|
||||
this.#debug = this.#strategyOptions.debug;
|
||||
|
||||
try {
|
||||
// Query the registries of Home Assistant.
|
||||
|
||||
// noinspection ES6MissingAwait False positive? https://youtrack.jetbrains.com/issue/WEB-63746
|
||||
[Helper.#entities, Helper.#devices, Helper.#areas] = await Promise.all([
|
||||
info.hass.callWS({type: "config/entity_registry/list"}) as Promise<EntityRegistryEntry[]>,
|
||||
info.hass.callWS({type: "config/device_registry/list"}) as Promise<DeviceRegistryEntry[]>,
|
||||
info.hass.callWS({type: "config/area_registry/list"}) as Promise<AreaRegistryEntry[]>,
|
||||
]);
|
||||
} catch (e) {
|
||||
Helper.logError("An error occurred while querying Home assistant's registries!", e);
|
||||
throw 'Check the console for details';
|
||||
}
|
||||
|
||||
// Create and add the undisclosed area if not hidden in the strategy options.
|
||||
if (!this.#strategyOptions.areas.undisclosed?.hidden) {
|
||||
this.#strategyOptions.areas.undisclosed = {
|
||||
...configurationDefaults.areas.undisclosed,
|
||||
...this.#strategyOptions.areas.undisclosed,
|
||||
};
|
||||
|
||||
// Make sure the custom configuration of the undisclosed area doesn't overwrite the area_id.
|
||||
this.#strategyOptions.areas.undisclosed.area_id = "undisclosed";
|
||||
|
||||
this.#areas.push(this.#strategyOptions.areas.undisclosed);
|
||||
}
|
||||
|
||||
// Merge custom areas of the strategy options into strategy areas.
|
||||
this.#areas = Helper.areas.map(area => {
|
||||
return {...area, ...this.#strategyOptions.areas?.[area.area_id]};
|
||||
});
|
||||
|
||||
// Sort strategy areas by order first and then by name.
|
||||
this.#areas.sort((a, b) => {
|
||||
return (a.order ?? Infinity) - (b.order ?? Infinity) || a.name.localeCompare(b.name);
|
||||
});
|
||||
|
||||
// Sort custom and default views of the strategy options by order first and then by title.
|
||||
this.#strategyOptions.views = Object.fromEntries(
|
||||
Object.entries(this.#strategyOptions.views).sort(([, a], [, b]) => {
|
||||
const viewA = a as ViewConfig;
|
||||
const viewB = b as ViewConfig;
|
||||
|
||||
return (viewA.order ?? Infinity) - (viewB.order ?? Infinity)
|
||||
|| (viewA.title ?? "undefined").localeCompare(viewB.title ?? "undefined");
|
||||
}),
|
||||
) as Record<supportedViews, ViewConfig>;
|
||||
|
||||
// Sort custom and default domains of the strategy options by order first and then by title.
|
||||
this.#strategyOptions.domains = Object.fromEntries(
|
||||
Object.entries(this.#strategyOptions.domains).sort(([, a], [, b]) => {
|
||||
if (isSortable(a) && isSortable(b)) {
|
||||
const orderA = ('order' in a) ? a.order ?? Infinity : Infinity;
|
||||
const orderB = ('order' in b) ? b.order ?? Infinity : Infinity;
|
||||
|
||||
return orderA - orderB || (a.title ?? "undefined").localeCompare(b.title ?? "undefined");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}),
|
||||
) as { [K in SupportedDomains]: K extends "_" ? AllDomainsConfig : SingleDomainConfig; };
|
||||
|
||||
this.#initialized = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the initialization status of the Helper class.
|
||||
*
|
||||
* @returns {boolean} True if this module is initialized.
|
||||
* @static
|
||||
*/
|
||||
static isInitialized(): boolean {
|
||||
return this.#initialized;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a template string to define the number of a given domain's entities with a certain state.
|
||||
*
|
||||
* States are compared against a given value by a given operator.
|
||||
* States `unavailable` and `unknown` are always excluded.
|
||||
*
|
||||
* @param {string} domain The domain of the entities.
|
||||
* @param {string} operator The comparison operator between state and value.
|
||||
* @param {string} value The value to which the state is compared against.
|
||||
*
|
||||
* @return {string} The template string.
|
||||
* @static
|
||||
*/
|
||||
static getCountTemplate(domain: SupportedDomains, operator: string, value: string): string {
|
||||
// noinspection JSMismatchedCollectionQueryUpdate
|
||||
/**
|
||||
* Array of entity state-entries, filtered by domain.
|
||||
*
|
||||
* Each element contains a template-string which is used to access home assistant's state machine (state object) in
|
||||
* a template.
|
||||
* E.g. "states['light.kitchen']"
|
||||
*
|
||||
* The array excludes hidden and disabled entities.
|
||||
*
|
||||
* @type {string[]}
|
||||
*/
|
||||
const states: string[] = [];
|
||||
|
||||
if (!this.isInitialized()) {
|
||||
console.warn("Helper class should be initialized before calling this method!");
|
||||
}
|
||||
|
||||
// Get the state of entities which are linked to the given area.
|
||||
for (const area of this.#areas) {
|
||||
let entities = this.getDeviceEntities(area, domain);
|
||||
|
||||
// Exclude hidden Config and Diagnostic entities.
|
||||
entities = applyEntityCategoryFilters(entities, domain);
|
||||
|
||||
const newStates = entities.map((entity) => `states['${entity.entity_id}']`);
|
||||
|
||||
states.push(...newStates);
|
||||
}
|
||||
|
||||
return (
|
||||
`{% set entities = [${states}] %}
|
||||
{{ entities
|
||||
| selectattr('state','${operator}','${value}')
|
||||
| selectattr('state','ne','unavailable')
|
||||
| selectattr('state','ne','unknown')
|
||||
| list
|
||||
| count
|
||||
}}`
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get device entities from the entity registry, filtered by area and domain.
|
||||
*
|
||||
* The entity registry is a registry where Home-Assistant keeps track of all entities.
|
||||
* A device is represented in Home Assistant via one or more entities.
|
||||
*
|
||||
* The result excludes hidden and disabled entities.
|
||||
*
|
||||
* @param {AreaRegistryEntry} area Area entity.
|
||||
* @param {string} [domain] The domain of the entity-id.
|
||||
*
|
||||
* @return {EntityRegistryEntry[]} Array of device entities.
|
||||
* @static
|
||||
*/
|
||||
static getDeviceEntities(area: AreaRegistryEntry, domain?: string): EntityRegistryEntry[] {
|
||||
if (!this.isInitialized()) {
|
||||
console.warn("Helper class should be initialized before calling this method!");
|
||||
}
|
||||
|
||||
// Get the ID of the devices which are linked to the given area.
|
||||
const areaDeviceIds = this.#devices.filter((device) => {
|
||||
return (device.area_id ?? "undisclosed") === area.area_id;
|
||||
}).map((device: DeviceRegistryEntry) => {
|
||||
|
||||
return device.id;
|
||||
});
|
||||
|
||||
// Return the entities of which all conditions of the callback function are met. @see areaFilterCallback.
|
||||
return this.#entities.filter(
|
||||
this.#areaFilterCallback, {
|
||||
area: area,
|
||||
domain: domain,
|
||||
areaDeviceIds: areaDeviceIds,
|
||||
})
|
||||
.sort((a, b) => {
|
||||
return (a.original_name ?? "undefined").localeCompare(b.original_name ?? "undefined");
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get state entities, filtered by area and domain.
|
||||
*
|
||||
* The result excludes hidden and disabled entities.
|
||||
*
|
||||
* @param {AreaRegistryEntry} area Area entity.
|
||||
* @param {string} domain Domain of the entity-id.
|
||||
*
|
||||
* @return {HassEntity[]} Array of state entities.
|
||||
*/
|
||||
static getStateEntities(area: AreaRegistryEntry, domain: string): HassEntity[] {
|
||||
if (!this.isInitialized()) {
|
||||
console.warn("Helper class should be initialized before calling this method!");
|
||||
}
|
||||
|
||||
const states: HassEntity[] = [];
|
||||
|
||||
// Create a map for the hassEntities and devices {id: object} to improve lookup speed.
|
||||
const entityMap: {
|
||||
[s: string]: EntityRegistryEntry;
|
||||
} = Object.fromEntries(this.#entities.map((entity) => [entity.entity_id, entity]));
|
||||
const deviceMap: {
|
||||
[s: string]: DeviceRegistryEntry;
|
||||
} = Object.fromEntries(this.#devices.map((device) => [device.id, device]));
|
||||
|
||||
// Get states whose entity-id starts with the given string.
|
||||
const stateEntities = Object.values(this.#hassStates).filter(
|
||||
(state) => state.entity_id.startsWith(`${domain}.`),
|
||||
);
|
||||
|
||||
for (const state of stateEntities) {
|
||||
const hassEntity = entityMap[state.entity_id];
|
||||
const device = deviceMap[hassEntity?.device_id ?? ""];
|
||||
|
||||
// Collect states of which any (whichever comes first) of the conditions below are met:
|
||||
// 1. The linked entity is linked to the given area.
|
||||
// 2. The entity is linked to a device, and the linked device is linked to the given area.
|
||||
if (
|
||||
(hassEntity?.area_id === area.area_id)
|
||||
|| (device && device.area_id === area.area_id)
|
||||
) {
|
||||
states.push(state);
|
||||
}
|
||||
}
|
||||
|
||||
return states;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the state object of a HASS entity.
|
||||
*
|
||||
* @param {EntityRegistryEntry} entity The entity for which to get the state.
|
||||
* @returns {HassEntity | undefined} The state object of the entity, or undefined if not found.
|
||||
* @static
|
||||
*/
|
||||
static getEntityState(entity: EntityRegistryEntry): HassEntity | undefined {
|
||||
return this.#hassStates[entity.entity_id];
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize a classname.
|
||||
*
|
||||
* The name is sanitized by capitalizing the first character of the name or after an underscore.
|
||||
* Underscores are removed.
|
||||
*
|
||||
* @param {string} className Name of the class to sanitize.
|
||||
* @returns {string} The sanitized classname.
|
||||
*/
|
||||
static sanitizeClassName(className: string): string {
|
||||
className = className.charAt(0).toUpperCase() + className.slice(1);
|
||||
|
||||
return className.replace(/([-_][a-z])/g, (group) => group
|
||||
.toUpperCase()
|
||||
.replace("-", "")
|
||||
.replace("_", ""),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the ids of the views which aren't set to hidden in the strategy options.
|
||||
*
|
||||
* @return {SupportedViews[]} An array of view ids.
|
||||
*/
|
||||
static getExposedViewIds(): supportedViews[] {
|
||||
if (!this.isInitialized()) {
|
||||
console.warn("Helper class should be initialized before calling this method!");
|
||||
}
|
||||
|
||||
const ids = this.#getObjectKeysByPropertyValue(this.#strategyOptions.views, "hidden", false);
|
||||
|
||||
return ids.filter(isSupportedView);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the ids of the domain ids which aren't set to hidden in the strategy options.
|
||||
*
|
||||
* @return {SupportedDomains[]} An array of domain ids.
|
||||
*/
|
||||
static getExposedDomainIds(): SupportedDomains[] {
|
||||
if (!this.isInitialized()) {
|
||||
console.warn("Helper class should be initialized before calling this method!");
|
||||
}
|
||||
|
||||
const ids = this.#getObjectKeysByPropertyValue(this.#strategyOptions.domains, "hidden", false);
|
||||
|
||||
return ids.filter(isSupportedDomain);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback function for filtering entities.
|
||||
*
|
||||
* Entities of which all the conditions below are met are kept:
|
||||
* 1. The entity is not hidden and the entity's device is not hidden by the strategy options.
|
||||
* 2. The entity is not hidden and is not disabled by Hass.
|
||||
* 3. The entity's domain matches the given domain.
|
||||
* 4. The entity itself or else the entity's device is linked to the given area.
|
||||
*
|
||||
* @param {EntityRegistryEntry} entity The current Hass entity to evaluate.
|
||||
* @this {AreaFilterContext}
|
||||
*
|
||||
* @return {boolean} True to keep the entity.
|
||||
* @static
|
||||
*/
|
||||
static #areaFilterCallback(
|
||||
this: {
|
||||
area: AreaRegistryEntry,
|
||||
areaDeviceIds: string[],
|
||||
domain: string,
|
||||
},
|
||||
entity: EntityRegistryEntry): boolean {
|
||||
const cardOptions = Helper.strategyOptions.card_options?.[entity.entity_id];
|
||||
const deviceOptions = Helper.strategyOptions.card_options?.[entity.device_id ?? "null"];
|
||||
|
||||
const entityUnhidden =
|
||||
!cardOptions?.hidden && !deviceOptions?.hidden // Condition 1.
|
||||
&& entity.hidden_by === null && entity.disabled_by === null; // Condition 2.
|
||||
const domainMatches = this.domain === undefined || entity.entity_id.startsWith(`${this.domain}.`); // Condition 3.
|
||||
// Condition 4.
|
||||
const entityLinked = this.area.area_id === "undisclosed"
|
||||
// Undisclosed area.
|
||||
? !entity.area_id && (this.areaDeviceIds.includes(entity.device_id ?? "") || !entity.device_id)
|
||||
// Area is a hass entity. Note: entity.area_id is set to null when using device's area.
|
||||
: entity.area_id === this.area.area_id || (!entity.area_id && this.areaDeviceIds.includes(entity.device_id ?? ""));
|
||||
|
||||
return (entityUnhidden && domainMatches && entityLinked);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the keys of nested objects by its property value.
|
||||
*
|
||||
* @param {Object<string, any>} object An object of objects.
|
||||
* @param {string|number} property The name of the property to evaluate.
|
||||
* @param {*} value The value which the property should match.
|
||||
*
|
||||
* @return {string[]} An array with keys.
|
||||
*/
|
||||
static #getObjectKeysByPropertyValue(
|
||||
object: { [k: string]: any },
|
||||
property: string, value: any
|
||||
): string[] {
|
||||
const keys: string[] = [];
|
||||
|
||||
for (const key of Object.keys(object)) {
|
||||
if (object[key][property] === value) {
|
||||
keys.push(key);
|
||||
}
|
||||
}
|
||||
|
||||
return keys;
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs an error message to the console.
|
||||
*
|
||||
* @param {string} userMessage - The error message to display.
|
||||
* @param {unknown} [e] - (Optional) The error object or additional information.
|
||||
*
|
||||
* @return {void}
|
||||
*/
|
||||
static logError(userMessage: string, e?: unknown): void {
|
||||
if (Helper.debug) {
|
||||
console.error(userMessage, e);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
console.error(userMessage);
|
||||
}
|
||||
}
|
||||
|
||||
export {Helper};
|
@@ -1,37 +1,44 @@
|
||||
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 configuration to control an entity of the camera domain.
|
||||
* Used to create a card for controlling an entity of the camera domain.
|
||||
*
|
||||
* @class
|
||||
* @extends AbstractCard
|
||||
*/
|
||||
class CameraCard extends AbstractCard {
|
||||
/** Returns the default configuration object for the card. */
|
||||
static getDefaultConfig(): PictureEntityCardConfig {
|
||||
return {
|
||||
entity: '',
|
||||
type: 'picture-entity',
|
||||
/**
|
||||
* Default configuration of the card.
|
||||
*
|
||||
* @type {PictureEntityCardConfig}
|
||||
* @private
|
||||
*/
|
||||
#defaultConfig: PictureEntityCardConfig = {
|
||||
entity: "",
|
||||
type: "picture-entity",
|
||||
show_name: false,
|
||||
show_state: false,
|
||||
camera_view: 'live',
|
||||
camera_view: "live",
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param {EntityRegistryEntry} entity The HASS entity to create a card configuration for.
|
||||
* @param {PictureEntityCardConfig} [customConfiguration] Custom card configuration.
|
||||
* @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.
|
||||
*/
|
||||
constructor(entity: EntityRegistryEntry, customConfiguration?: PictureEntityCardConfig) {
|
||||
constructor(entity: EntityRegistryEntry, options: cards.PictureEntityCardOptions = {}) {
|
||||
super(entity);
|
||||
|
||||
this.configuration = { ...this.configuration, ...CameraCard.getDefaultConfig(), ...customConfiguration };
|
||||
this.config = Object.assign(this.config, this.#defaultConfig, options);
|
||||
}
|
||||
}
|
||||
|
||||
export default CameraCard;
|
||||
export {CameraCard};
|
||||
|
102
src/cards/ControllerCard.ts
Normal file
102
src/cards/ControllerCard.ts
Normal file
@@ -0,0 +1,102 @@
|
||||
import {cards} from "../types/strategy/cards";
|
||||
import {LovelaceCardConfig} from "../types/homeassistant/data/lovelace";
|
||||
import {HassServiceTarget} from "home-assistant-js-websocket";
|
||||
import {StackCardConfig} from "../types/homeassistant/panels/lovelace/cards/types";
|
||||
|
||||
/**
|
||||
* Controller Card class.
|
||||
*
|
||||
* Used for creating a Title Card with controls.
|
||||
*
|
||||
* @class
|
||||
*/
|
||||
class ControllerCard {
|
||||
/**
|
||||
* @type {HassServiceTarget} The target to control the entities of.
|
||||
* @private
|
||||
*/
|
||||
readonly #target: HassServiceTarget;
|
||||
|
||||
/**
|
||||
* Default configuration of the card.
|
||||
*
|
||||
* @type {cards.ControllerCardConfig}
|
||||
* @private
|
||||
*/
|
||||
readonly #defaultConfig: cards.ControllerCardConfig = {
|
||||
type: "mushroom-title-card",
|
||||
showControls: true,
|
||||
iconOn: "mdi:power-on",
|
||||
iconOff: "mdi:power-off",
|
||||
onService: "none",
|
||||
offService: "none",
|
||||
};
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param {HassServiceTarget} target The target to control the entities of.
|
||||
* @param {cards.ControllerCardOptions} options Controller Card options.
|
||||
*/
|
||||
constructor(target: HassServiceTarget, options: cards.ControllerCardOptions = {}) {
|
||||
this.#target = target;
|
||||
this.#defaultConfig = {
|
||||
...this.#defaultConfig,
|
||||
...options,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a Controller card.
|
||||
*
|
||||
* @return {StackCardConfig} A Controller card.
|
||||
*/
|
||||
createCard(): StackCardConfig {
|
||||
const cards: LovelaceCardConfig[] = [
|
||||
{
|
||||
type: "custom:mushroom-title-card",
|
||||
title: this.#defaultConfig.title,
|
||||
subtitle: this.#defaultConfig.subtitle,
|
||||
},
|
||||
];
|
||||
|
||||
if (this.#defaultConfig.showControls) {
|
||||
cards.push({
|
||||
type: "horizontal-stack",
|
||||
cards: [
|
||||
{
|
||||
type: "custom:mushroom-template-card",
|
||||
icon: this.#defaultConfig.iconOff,
|
||||
layout: "vertical",
|
||||
icon_color: "red",
|
||||
tap_action: {
|
||||
action: "call-service",
|
||||
service: this.#defaultConfig.offService,
|
||||
target: this.#target,
|
||||
data: {},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "custom:mushroom-template-card",
|
||||
icon: this.#defaultConfig.iconOn,
|
||||
layout: "vertical",
|
||||
icon_color: "amber",
|
||||
tap_action: {
|
||||
action: "call-service",
|
||||
service: this.#defaultConfig.onService,
|
||||
target: this.#target,
|
||||
data: {},
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
type: "horizontal-stack",
|
||||
cards: cards,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export {ControllerCard};
|
@@ -1,45 +1,49 @@
|
||||
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 card configuration for an entry of the HASS area registry.
|
||||
* Used to create a card for an entity of the area domain using the built-in type 'area'.
|
||||
*
|
||||
* @class
|
||||
* @extends AbstractCard
|
||||
*/
|
||||
class AreaCard extends AbstractCard {
|
||||
/** Returns the default configuration object for the card. */
|
||||
static getDefaultConfig(): AreaCardConfig {
|
||||
return {
|
||||
type: 'area',
|
||||
area: '',
|
||||
/**
|
||||
* Default configuration of the card.
|
||||
*
|
||||
* @type {AreaCardConfig}
|
||||
* @private
|
||||
*/
|
||||
#defaultConfig: AreaCardConfig = {
|
||||
type: "area",
|
||||
area: "",
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param {AreaRegistryEntry} area The HASS entity to create a card configuration for.
|
||||
* @param {AreaCardConfig} [customConfiguration] Custom card configuration.
|
||||
* @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.
|
||||
*/
|
||||
constructor(area: AreaRegistryEntry, customConfiguration?: AreaCardConfig) {
|
||||
|
||||
constructor(area: AreaRegistryEntry, options: cards.AreaCardOptions = {}) {
|
||||
super(area);
|
||||
|
||||
// Initialize the default configuration.
|
||||
const configuration = AreaCard.getDefaultConfig();
|
||||
this.#defaultConfig.area = area.area_id;
|
||||
this.#defaultConfig.navigation_path = this.#defaultConfig.area;
|
||||
|
||||
configuration.area = area.area_id;
|
||||
configuration.navigation_path = configuration.area;
|
||||
// Enforce the card type.
|
||||
delete options.type;
|
||||
|
||||
this.configuration = {
|
||||
...this.configuration,
|
||||
...configuration,
|
||||
...customConfiguration,
|
||||
type: configuration.type, // Enforce the card type.
|
||||
};
|
||||
this.config = Object.assign(this.config, this.#defaultConfig, options);
|
||||
}
|
||||
}
|
||||
|
||||
export default AreaCard;
|
||||
export {AreaCard};
|
||||
|
@@ -1,138 +1,130 @@
|
||||
import { StrategyDefaults } from './types/strategy/strategy-generics';
|
||||
import { localize } from './utilities/localize';
|
||||
import {generic} from "./types/strategy/generic";
|
||||
import StrategyDefaults = generic.StrategyDefaults;
|
||||
|
||||
/**
|
||||
* Default configuration for the mushroom strategy.
|
||||
*/
|
||||
export const ConfigurationDefaults: StrategyDefaults = {
|
||||
export const getConfigurationDefaults = (localize: Function): StrategyDefaults => {
|
||||
return {
|
||||
areas: {
|
||||
undisclosed: {
|
||||
// TODO: Refactor undisclosed to other.
|
||||
aliases: [],
|
||||
area_id: 'undisclosed',
|
||||
area_id: "undisclosed",
|
||||
created_at: 0,
|
||||
floor_id: null,
|
||||
hidden: false,
|
||||
humidity_entity_id: null,
|
||||
icon: 'mdi:floor-plan',
|
||||
icon: "mdi:floor-plan",
|
||||
labels: [],
|
||||
modified_at: 0,
|
||||
name: localize('generic.undisclosed'),
|
||||
name: "Undisclosed",
|
||||
picture: null,
|
||||
temperature_entity_id: null,
|
||||
},
|
||||
}
|
||||
},
|
||||
card_options: {},
|
||||
chips: {
|
||||
// TODO: Make chips sortable.
|
||||
weather_entity: 'auto',
|
||||
light_count: true,
|
||||
fan_count: true,
|
||||
cover_count: true,
|
||||
switch_count: true,
|
||||
climate_count: true,
|
||||
extra_chips: [],
|
||||
},
|
||||
chips: {},
|
||||
debug: false,
|
||||
domains: {
|
||||
_: {
|
||||
hide_config_entities: undefined,
|
||||
hide_diagnostic_entities: undefined,
|
||||
hide_config_entities: true,
|
||||
hide_diagnostic_entities: true,
|
||||
},
|
||||
binary_sensor: {
|
||||
title: `${localize('sensor.binary')} ` + localize('sensor.sensors'),
|
||||
title: `${localize("sensor.binary")} ` + localize("sensor.sensors"),
|
||||
showControls: false,
|
||||
hidden: false,
|
||||
},
|
||||
camera: {
|
||||
title: localize('camera.cameras'),
|
||||
title: localize("camera.cameras"),
|
||||
showControls: false,
|
||||
hidden: false,
|
||||
},
|
||||
climate: {
|
||||
title: localize('climate.climates'),
|
||||
title: localize("climate.climates"),
|
||||
showControls: false,
|
||||
hidden: false,
|
||||
},
|
||||
cover: {
|
||||
title: localize('cover.covers'),
|
||||
title: localize("cover.covers"),
|
||||
showControls: true,
|
||||
iconOn: 'mdi:arrow-up',
|
||||
iconOff: 'mdi:arrow-down',
|
||||
onService: 'cover.open_cover',
|
||||
offService: 'cover.close_cover',
|
||||
iconOn: "mdi:arrow-up",
|
||||
iconOff: "mdi:arrow-down",
|
||||
onService: "cover.open_cover",
|
||||
offService: "cover.close_cover",
|
||||
hidden: false,
|
||||
},
|
||||
default: {
|
||||
title: localize('generic.miscellaneous'),
|
||||
title: localize("generic.miscellaneous"),
|
||||
showControls: false,
|
||||
hidden: false,
|
||||
},
|
||||
fan: {
|
||||
title: localize('fan.fans'),
|
||||
title: localize("fan.fans"),
|
||||
showControls: true,
|
||||
iconOn: 'mdi:fan',
|
||||
iconOff: 'mdi:fan-off',
|
||||
onService: 'fan.turn_on',
|
||||
offService: 'fan.turn_off',
|
||||
iconOn: "mdi:fan",
|
||||
iconOff: "mdi:fan-off",
|
||||
onService: "fan.turn_on",
|
||||
offService: "fan.turn_off",
|
||||
hidden: false,
|
||||
},
|
||||
|
||||
input_select: {
|
||||
title: localize('input_select.input_selects'),
|
||||
title: localize("input_select.input_selects"),
|
||||
showControls: false,
|
||||
hidden: false,
|
||||
},
|
||||
light: {
|
||||
title: localize('light.lights'),
|
||||
title: localize("light.lights"),
|
||||
showControls: true,
|
||||
iconOn: 'mdi:lightbulb',
|
||||
iconOff: 'mdi:lightbulb-off',
|
||||
onService: 'light.turn_on',
|
||||
offService: 'light.turn_off',
|
||||
iconOn: "mdi:lightbulb",
|
||||
iconOff: "mdi:lightbulb-off",
|
||||
onService: "light.turn_on",
|
||||
offService: "light.turn_off",
|
||||
hidden: false,
|
||||
},
|
||||
lock: {
|
||||
title: localize('lock.locks'),
|
||||
title: localize("lock.locks"),
|
||||
showControls: false,
|
||||
hidden: false,
|
||||
},
|
||||
media_player: {
|
||||
title: localize('media_player.media_players'),
|
||||
title: localize("media_player.media_players"),
|
||||
showControls: false,
|
||||
hidden: false,
|
||||
},
|
||||
number: {
|
||||
title: localize('generic.numbers'),
|
||||
title: localize("generic.numbers"),
|
||||
showControls: false,
|
||||
hidden: false,
|
||||
},
|
||||
scene: {
|
||||
title: localize('scene.scenes'),
|
||||
title: localize("scene.scenes"),
|
||||
showControls: false,
|
||||
onService: 'scene.turn_on',
|
||||
onService: "scene.turn_on",
|
||||
hidden: false,
|
||||
},
|
||||
select: {
|
||||
title: localize('select.selects'),
|
||||
title: localize("select.selects"),
|
||||
showControls: false,
|
||||
hidden: false,
|
||||
},
|
||||
sensor: {
|
||||
title: localize('sensor.sensors'),
|
||||
title: localize("sensor.sensors"),
|
||||
showControls: false,
|
||||
hidden: false,
|
||||
},
|
||||
switch: {
|
||||
title: localize('switch.switches'),
|
||||
title: localize("switch.switches"),
|
||||
showControls: true,
|
||||
iconOn: 'mdi:power-plug',
|
||||
iconOff: 'mdi:power-plug-off',
|
||||
onService: 'switch.turn_on',
|
||||
offService: 'switch.turn_off',
|
||||
iconOn: "mdi:power-plug",
|
||||
iconOff: "mdi:power-plug-off",
|
||||
onService: "switch.turn_on",
|
||||
offService: "switch.turn_off",
|
||||
hidden: false,
|
||||
},
|
||||
vacuum: {
|
||||
title: localize('vacuum.vacuums'),
|
||||
title: localize("vacuum.vacuums"),
|
||||
showControls: true,
|
||||
hidden: false,
|
||||
},
|
||||
@@ -167,10 +159,6 @@ export const ConfigurationDefaults: StrategyDefaults = {
|
||||
order: 2,
|
||||
hidden: false,
|
||||
},
|
||||
lock: {
|
||||
order: 10,
|
||||
hidden: false,
|
||||
},
|
||||
scene: {
|
||||
order: 9,
|
||||
hidden: false,
|
||||
@@ -184,5 +172,6 @@ export const ConfigurationDefaults: StrategyDefaults = {
|
||||
hidden: false,
|
||||
},
|
||||
},
|
||||
quick_access_cards: [],
|
||||
quick_access_cards: []
|
||||
};
|
||||
};
|
||||
|
@@ -1,208 +1,272 @@
|
||||
import { HassServiceTarget } from 'home-assistant-js-websocket';
|
||||
import HeaderCard from './cards/HeaderCard';
|
||||
import SensorCard from './cards/SensorCard';
|
||||
import { Registry } from './Registry';
|
||||
import { LovelaceCardConfig } from './types/homeassistant/data/lovelace/config/card';
|
||||
import { LovelaceConfig } from './types/homeassistant/data/lovelace/config/types';
|
||||
import { LovelaceViewConfig, LovelaceViewRawConfig } from './types/homeassistant/data/lovelace/config/view';
|
||||
import {
|
||||
DashboardInfo,
|
||||
isSupportedDomain,
|
||||
isSupportedView,
|
||||
StrategyArea,
|
||||
ViewInfo,
|
||||
} from './types/strategy/strategy-generics';
|
||||
import { sanitizeClassName } from './utilities/auxiliaries';
|
||||
import { logMessage, lvlError } from './utilities/debug';
|
||||
import RegistryFilter from './utilities/RegistryFilter';
|
||||
import { stackHorizontal } from './utilities/cardStacking';
|
||||
import {Helper} from "./Helper";
|
||||
import {SensorCard} from "./cards/SensorCard";
|
||||
import {ControllerCard} from "./cards/ControllerCard";
|
||||
import {EntityCardConfig} from "./types/lovelace-mushroom/cards/entity-card-config";
|
||||
import {HassServiceTarget} from "home-assistant-js-websocket";
|
||||
import {applyEntityCategoryFilters} from "./utillties/filters";
|
||||
import {LovelaceConfig} from "./types/homeassistant/data/lovelace/config/types";
|
||||
import {LovelaceViewConfig, LovelaceViewRawConfig} from "./types/homeassistant/data/lovelace/config/view";
|
||||
import {LovelaceCardConfig} from "./types/homeassistant/data/lovelace";
|
||||
import {StackCardConfig} from "./types/homeassistant/panels/lovelace/cards/types";
|
||||
import {generic} from "./types/strategy/generic";
|
||||
import {views} from "./types/strategy/views";
|
||||
import ViewConfig = views.ViewConfig;
|
||||
import StrategyArea = generic.StrategyArea;
|
||||
import SupportedDomains = generic.SupportedDomains;
|
||||
|
||||
/**
|
||||
* Mushroom Dashboard Strategy.<br>
|
||||
* <br>
|
||||
* Mushroom dashboard strategy provides a strategy for Home-Assistant to create a dashboard automatically.<br>
|
||||
* The strategy makes use Mushroom and Mini Graph cards to represent your entities.
|
||||
*
|
||||
* @see https://github.com/DigiLive/mushroom-strategy
|
||||
* The strategy makes use Mushroom and Mini Graph cards to represent your entities.<br>
|
||||
* <br>
|
||||
* Features:<br>
|
||||
* 🛠 Automatically create dashboard with three lines of yaml.<br>
|
||||
* 😍 Built-in Views for several standard domains.<br>
|
||||
* 🎨 Many options to customize to your needs.<br>
|
||||
* <br>
|
||||
* Check the [Repository]{@link https://github.com/AalianKhan/mushroom-strategy} for more information.
|
||||
*/
|
||||
class MushroomStrategy extends HTMLTemplateElement {
|
||||
/**
|
||||
* Generate a dashboard.
|
||||
*
|
||||
* This method creates views for each exposed domain and area.
|
||||
* It also adds custom views if specified in the strategy options.
|
||||
*
|
||||
* @param {DashboardInfo} info Dashboard strategy information object.
|
||||
*
|
||||
* @remarks
|
||||
* Called when opening a dashboard.
|
||||
*
|
||||
* @param {generic.DashboardInfo} info Dashboard strategy information object.
|
||||
* @return {Promise<LovelaceConfig>}
|
||||
*/
|
||||
static async generateDashboard(info: DashboardInfo): Promise<LovelaceConfig> {
|
||||
await Registry.initialize(info);
|
||||
static async generateDashboard(info: generic.DashboardInfo): Promise<LovelaceConfig> {
|
||||
await Helper.initialize(info);
|
||||
|
||||
// Create views.
|
||||
const views: LovelaceViewRawConfig[] = [];
|
||||
|
||||
// Parallelize view imports and creation.
|
||||
const viewPromises = Registry.getExposedNames('view')
|
||||
.filter(isSupportedView)
|
||||
.map(async (viewName) => {
|
||||
try {
|
||||
const moduleName = sanitizeClassName(`${viewName}View`);
|
||||
const View = (await import(`./views/${moduleName}`)).default;
|
||||
const currentView = new View(Registry.strategyOptions.views[viewName]);
|
||||
const viewConfiguration = await currentView.getView();
|
||||
let viewModule;
|
||||
|
||||
if (viewConfiguration.cards.length) {
|
||||
return viewConfiguration;
|
||||
// Create a view for each exposed domain.
|
||||
for (let viewId of Helper.getExposedViewIds()) {
|
||||
try {
|
||||
const viewType = Helper.sanitizeClassName(viewId + "View");
|
||||
viewModule = await import(`./views/${viewType}`);
|
||||
const view: ViewConfig = await new viewModule[viewType](Helper.strategyOptions.views[viewId]).getView();
|
||||
|
||||
if (view.cards?.length) {
|
||||
views.push(view);
|
||||
}
|
||||
} catch (e) {
|
||||
logMessage(lvlError, `Error importing ${viewName} view!`, e);
|
||||
Helper.logError(`View '${viewId}' couldn't be loaded!`, e);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
|
||||
const resolvedViews = (await Promise.all(viewPromises)).filter(Boolean) as LovelaceViewRawConfig[];
|
||||
|
||||
views.push(...resolvedViews);
|
||||
|
||||
// Subviews for areas
|
||||
views.push(
|
||||
...Registry.areas.map((area) => ({
|
||||
// Create subviews for each area.
|
||||
for (let area of Helper.areas) {
|
||||
if (!area.hidden) {
|
||||
views.push({
|
||||
title: area.name,
|
||||
path: area.area_id,
|
||||
path: area.area_id ?? area.name,
|
||||
subview: true,
|
||||
strategy: {
|
||||
type: 'custom:mushroom-strategy',
|
||||
options: { area },
|
||||
type: "custom:mushroom-strategy",
|
||||
options: {
|
||||
area,
|
||||
},
|
||||
})),
|
||||
);
|
||||
|
||||
// Extra views
|
||||
if (Registry.strategyOptions.extra_views) {
|
||||
views.push(...Registry.strategyOptions.extra_views);
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return { views };
|
||||
// Add custom views.
|
||||
if (Helper.strategyOptions.extra_views) {
|
||||
views.push(...Helper.strategyOptions.extra_views);
|
||||
}
|
||||
|
||||
// Return the created views.
|
||||
return {
|
||||
views: views,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a view.
|
||||
*
|
||||
* The method creates cards for each domain (e.g., sensors, switches, etc.) in the current area, using a combination
|
||||
* of Header cards and entity-specific cards.
|
||||
* It also handles miscellaneous entities that don't fit into any supported domain.
|
||||
* Called when opening a subview.
|
||||
*
|
||||
* @param {ViewInfo} info The view's strategy information object.
|
||||
*
|
||||
* @remarks
|
||||
* Called upon opening a subview.
|
||||
* @param {generic.ViewInfo} info The view's strategy information object.
|
||||
* @return {Promise<LovelaceViewConfig>}
|
||||
*/
|
||||
static async generateView(info: ViewInfo): Promise<LovelaceViewConfig> {
|
||||
const exposedDomainNames = Registry.getExposedNames('domain');
|
||||
const area = info.view.strategy?.options?.area ?? ({} as StrategyArea);
|
||||
const areaEntities = new RegistryFilter(Registry.entities).whereAreaId(area.area_id).toList();
|
||||
static async generateView(info: generic.ViewInfo): Promise<LovelaceViewConfig> {
|
||||
const exposedDomainIds = Helper.getExposedDomainIds();
|
||||
const area = info.view.strategy?.options?.area ?? {} as StrategyArea;
|
||||
const viewCards: LovelaceCardConfig[] = [...(area.extra_cards ?? [])];
|
||||
|
||||
// Set the target for any Header card to the current area.
|
||||
const target: HassServiceTarget = { area_id: [area.area_id] };
|
||||
// Set the target for controller cards to the current area.
|
||||
let target: HassServiceTarget = {
|
||||
area_id: [area.area_id],
|
||||
};
|
||||
|
||||
// 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)
|
||||
.where((entity) => !(domain === 'switch' && entity.entity_id.endsWith('_stateful_scene')))
|
||||
.toList();
|
||||
|
||||
if (!entities.length) {
|
||||
return null;
|
||||
// Create cards for each domain.
|
||||
for (const domain of exposedDomainIds) {
|
||||
if (domain === "default") {
|
||||
continue;
|
||||
}
|
||||
|
||||
const titleCard = new HeaderCard(
|
||||
{ entity_id: entities.map((entity) => entity.entity_id) },
|
||||
Registry.strategyOptions.domains[domain],
|
||||
const className = Helper.sanitizeClassName(domain + "Card");
|
||||
|
||||
let domainCards: EntityCardConfig[] = [];
|
||||
|
||||
try {
|
||||
domainCards = await import(`./cards/${className}`).then(cardModule => {
|
||||
let domainCards: EntityCardConfig[] = [];
|
||||
let entities = Helper.getDeviceEntities(area, domain);
|
||||
|
||||
// Exclude hidden Config and Diagnostic entities.
|
||||
entities = applyEntityCategoryFilters(entities, domain);
|
||||
|
||||
// Set the target for controller cards to entities without an area.
|
||||
if (area.area_id === "undisclosed") {
|
||||
target = {
|
||||
entity_id: entities.map(entity => entity.entity_id),
|
||||
}
|
||||
}
|
||||
|
||||
if (entities.length) {
|
||||
// Create a Controller card for the current domain.
|
||||
const titleCard = new ControllerCard(
|
||||
target,
|
||||
Helper.strategyOptions.domains[domain],
|
||||
).createCard();
|
||||
|
||||
try {
|
||||
const DomainCard = (await import(`./cards/${moduleName}`)).default;
|
||||
if (domain === "sensor") {
|
||||
// Create a card for each sensor-entity of the current area.
|
||||
const sensorStates = Helper.getStateEntities(area, "sensor");
|
||||
const sensorCards: EntityCardConfig[] = [];
|
||||
|
||||
if (domain === 'sensor') {
|
||||
const domainCards = entities
|
||||
.filter((entity) => Registry.hassStates[entity.entity_id]?.attributes.unit_of_measurement)
|
||||
.map((entity) => {
|
||||
const options = {
|
||||
...(entity.device_id && Registry.strategyOptions.card_options?.[entity.device_id]),
|
||||
...Registry.strategyOptions.card_options?.[entity.entity_id],
|
||||
type: 'custom:mini-graph-card',
|
||||
entities: [entity.entity_id],
|
||||
for (const sensor of entities) {
|
||||
// Find the state of the current sensor.
|
||||
const sensorState = sensorStates.find(state => state.entity_id === sensor.entity_id);
|
||||
let cardOptions = Helper.strategyOptions.card_options?.[sensor.entity_id];
|
||||
|
||||
if (sensorState?.attributes.unit_of_measurement) {
|
||||
cardOptions = {
|
||||
...{
|
||||
type: "custom:mini-graph-card",
|
||||
entities: [sensor.entity_id],
|
||||
},
|
||||
...cardOptions,
|
||||
};
|
||||
return new SensorCard(entity, options).getCard();
|
||||
});
|
||||
return domainCards.length ? { type: 'vertical-stack', cards: [titleCard, ...domainCards] } : null;
|
||||
|
||||
sensorCards.push(new SensorCard(sensor, cardOptions).getCard());
|
||||
}
|
||||
}
|
||||
|
||||
let domainCards = entities.map((entity) => {
|
||||
const cardOptions = {
|
||||
...(entity.device_id && Registry.strategyOptions.card_options?.[entity.device_id]),
|
||||
...Registry.strategyOptions.card_options?.[entity.entity_id],
|
||||
};
|
||||
return new DomainCard(entity, cardOptions).getCard();
|
||||
if (sensorCards.length) {
|
||||
domainCards.push({
|
||||
type: "vertical-stack",
|
||||
cards: sensorCards,
|
||||
});
|
||||
|
||||
if (domain === 'binary_sensor') {
|
||||
domainCards = stackHorizontal(domainCards);
|
||||
domainCards.unshift(titleCard);
|
||||
}
|
||||
|
||||
return domainCards.length ? { type: 'vertical-stack', cards: [titleCard, ...domainCards] } : null;
|
||||
return domainCards;
|
||||
}
|
||||
|
||||
// Create a card for each other domain-entity of the current area.
|
||||
for (const entity of entities) {
|
||||
let deviceOptions;
|
||||
let cardOptions = Helper.strategyOptions.card_options?.[entity.entity_id];
|
||||
|
||||
if (entity.device_id) {
|
||||
deviceOptions = Helper.strategyOptions.card_options?.[entity.device_id];
|
||||
}
|
||||
|
||||
domainCards.push(new cardModule[className](entity, cardOptions).getCard());
|
||||
}
|
||||
|
||||
if (domain === "binary_sensor") {
|
||||
// Horizontally group every two binary sensor cards.
|
||||
const horizontalCards: EntityCardConfig[] = [];
|
||||
|
||||
for (let i = 0; i < domainCards.length; i += 2) {
|
||||
horizontalCards.push({
|
||||
type: "horizontal-stack",
|
||||
cards: domainCards.slice(i, i + 2),
|
||||
});
|
||||
}
|
||||
|
||||
domainCards = horizontalCards;
|
||||
}
|
||||
|
||||
if (domainCards.length) {
|
||||
domainCards.unshift(titleCard);
|
||||
}
|
||||
}
|
||||
|
||||
return domainCards;
|
||||
});
|
||||
} catch (e) {
|
||||
logMessage(lvlError, `Error creating card configurations for domain ${domain}`, e);
|
||||
return null;
|
||||
Helper.logError("An error occurred while creating the domain cards!", e);
|
||||
}
|
||||
|
||||
if (domainCards.length) {
|
||||
viewCards.push({
|
||||
type: "vertical-stack",
|
||||
cards: domainCards,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Await all domain card stacks
|
||||
const domainCardStacks = (await Promise.all(domainCardPromises)).filter(Boolean) as LovelaceCardConfig[];
|
||||
viewCards.push(...domainCardStacks);
|
||||
if (!Helper.strategyOptions.domains.default.hidden) {
|
||||
// Create cards for any other domain.
|
||||
// Collect entities of the current area and unexposed domains.
|
||||
let miscellaneousEntities = Helper.getDeviceEntities(area).filter(
|
||||
entity => !exposedDomainIds.includes(entity.entity_id.split(".", 1)[0] as SupportedDomains)
|
||||
);
|
||||
|
||||
// Miscellaneous domain
|
||||
if (!Registry.strategyOptions.domains.default.hidden) {
|
||||
const miscellaneousEntities = new RegistryFilter(areaEntities)
|
||||
.not()
|
||||
.where((entity) => isSupportedDomain(entity.entity_id.split('.', 1)[0]))
|
||||
.toList();
|
||||
// Exclude hidden Config and Diagnostic entities.
|
||||
miscellaneousEntities = applyEntityCategoryFilters(miscellaneousEntities, "default");
|
||||
|
||||
// Create a column of miscellaneous entity cards.
|
||||
if (miscellaneousEntities.length) {
|
||||
let miscellaneousCards: (StackCardConfig | EntityCardConfig)[] = [];
|
||||
|
||||
try {
|
||||
const MiscellaneousCard = (await import('./cards/MiscellaneousCard')).default;
|
||||
const miscellaneousCards = [
|
||||
new HeaderCard(target, Registry.strategyOptions.domains.default).createCard(),
|
||||
...miscellaneousEntities.map((entity) =>
|
||||
new MiscellaneousCard(entity, Registry.strategyOptions.card_options?.[entity.entity_id]).getCard(),
|
||||
),
|
||||
miscellaneousCards = await import("./cards/MiscellaneousCard").then(cardModule => {
|
||||
const miscellaneousCards: (StackCardConfig | EntityCardConfig)[] = [
|
||||
new ControllerCard(target, Helper.strategyOptions.domains.default).createCard(),
|
||||
];
|
||||
|
||||
viewCards.push({
|
||||
type: 'vertical-stack',
|
||||
cards: miscellaneousCards,
|
||||
for (const entity of miscellaneousEntities) {
|
||||
let cardOptions = Helper.strategyOptions.card_options?.[entity.entity_id];
|
||||
|
||||
miscellaneousCards.push(new cardModule.MiscellaneousCard(entity, cardOptions).getCard());
|
||||
}
|
||||
|
||||
return miscellaneousCards;
|
||||
});
|
||||
} catch (e) {
|
||||
logMessage(lvlError, 'Error creating card configurations for domain `miscellaneous`', e);
|
||||
Helper.logError("An error occurred while creating the domain cards!", e);
|
||||
}
|
||||
|
||||
viewCards.push({
|
||||
type: "vertical-stack",
|
||||
cards: miscellaneousCards,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return { cards: viewCards };
|
||||
// Return cards.
|
||||
return {
|
||||
cards: viewCards,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define('ll-strategy-mushroom-strategy', MushroomStrategy);
|
||||
customElements.define("ll-strategy-mushroom-strategy", MushroomStrategy);
|
||||
|
||||
const version = 'v2.3.0';
|
||||
const version = "v2.2.1";
|
||||
console.info(
|
||||
'%c Mushroom Strategy %c '.concat(version, ' '),
|
||||
'color: white; background: coral; font-weight: 700;',
|
||||
'color: coral; background: white; font-weight: 700;',
|
||||
"%c Mushroom Strategy %c ".concat(version, " "),
|
||||
"color: white; background: coral; font-weight: 700;", "color: coral; background: white; font-weight: 700;"
|
||||
);
|
||||
|
@@ -1,10 +1,10 @@
|
||||
import type { TranslationDict } from '../../types';
|
||||
import type {TranslationDict} from "../../types";
|
||||
|
||||
// Exclude some patterns from key type checking for now
|
||||
// These are intended to be removed as errors are fixed
|
||||
// Fixing component category will require tighter definition of types from backend and/or web socket
|
||||
export type LocalizeKeys =
|
||||
| FlattenObjectKeys<Omit<TranslationDict, 'supervisor'>>
|
||||
| FlattenObjectKeys<Omit<TranslationDict, "supervisor">>
|
||||
| `panel.${string}`
|
||||
| `ui.card.alarm_control_panel.${string}`
|
||||
| `ui.card.weather.attributes.${string}`
|
||||
@@ -20,7 +20,7 @@ export type LocalizeKeys =
|
||||
| `ui.dialogs.quick-bar.commands.${string}`
|
||||
| `ui.dialogs.unhealthy.reason.${string}`
|
||||
| `ui.dialogs.unsupported.reason.${string}`
|
||||
| `ui.panel.config.${string}.${'caption' | 'description'}`
|
||||
| `ui.panel.config.${string}.${"caption" | "description"}`
|
||||
| `ui.panel.config.dashboard.${string}`
|
||||
| `ui.panel.config.zha.${string}`
|
||||
| `ui.panel.config.zwave_js.${string}`
|
||||
@@ -30,7 +30,10 @@ export type LocalizeKeys =
|
||||
| `component.${string}`;
|
||||
|
||||
// Tweaked from https://www.raygesualdo.com/posts/flattening-object-keys-with-typescript-types
|
||||
export type FlattenObjectKeys<T extends Record<string, any>, Key extends keyof T = keyof T> = Key extends string
|
||||
export type FlattenObjectKeys<
|
||||
T extends Record<string, any>,
|
||||
Key extends keyof T = keyof T,
|
||||
> = Key extends string
|
||||
? T[Key] extends Record<string, unknown>
|
||||
? `${Key}.${FlattenObjectKeys<T[Key]>}`
|
||||
: `${Key}`
|
||||
@@ -41,6 +44,6 @@ export type LocalizeFunc<Keys extends string = LocalizeKeys> = (
|
||||
key: Keys,
|
||||
values?: Record<
|
||||
string,
|
||||
string | number | { _$litType$: 1; strings: TemplateStringsArray; values: Array<unknown> } | null | undefined
|
||||
>,
|
||||
string | number | {_$litType$: 1, strings: TemplateStringsArray, values: Array<unknown>} | null | undefined
|
||||
>
|
||||
) => string;
|
||||
|
@@ -1,20 +1,17 @@
|
||||
import { RegistryEntry } from './registry';
|
||||
import {RegistryEntry} from "./registry";
|
||||
|
||||
/**
|
||||
* Represents an entry in the Area Registry in Home Assistant.
|
||||
* Entry in the Area Registry.
|
||||
*
|
||||
* @property {string[]} aliases - An array of aliases for the area.
|
||||
* @property {string} area_id - The unique identifier for the area.
|
||||
* @property {string|null} floor_id - The identifier for the area's floor, or null if not applicable.
|
||||
* @property {string|null} humidity_entity_id - The identifier for the area's humidity sensor, or null if not
|
||||
* applicable.
|
||||
* @property {string|null} icon - The icon to display for the area, or null if not specified.
|
||||
* @property {string[]} labels - Labels for grouping elements irrespective of their physical location or type.
|
||||
* @property {string} name - The name of the area.
|
||||
* @property {string|null} picture - The URL to a picture that should be used instead of the domain icon, or null if
|
||||
* not specified.
|
||||
* @property {string|null} temperature_entity_id - The identifier for the area's temperature sensor, or null if not
|
||||
* applicable.
|
||||
* @property {string[]} aliases Array of aliases of the area.
|
||||
* @property {string} area_id The id of the area.
|
||||
* @property {string|null} floor_id The id of the area's floor.
|
||||
* @property {string|null} humidity_entity_id The id of the area's humidity sensor.
|
||||
* @property {string|null} icon Icon to show.
|
||||
* @property {string[]} labels Labels allow grouping elements irrespective of their physical location or type.
|
||||
* @property {string} name Name of the area.
|
||||
* @property {string|null} picture URL to a picture that should be used instead of showing the domain icon.
|
||||
* @property {string|null} temperature_entity_id The id of the area's temperature sensor.
|
||||
*/
|
||||
export interface AreaRegistryEntry extends RegistryEntry {
|
||||
aliases: string[];
|
||||
|
@@ -1,26 +1,40 @@
|
||||
/**
|
||||
* Represents a device entity in the of Home Assistant's device registry .
|
||||
* Device Entity.
|
||||
*
|
||||
* @property {string} id - Unique identifier of the device (generated by Home Assistant).
|
||||
* @property {string[]} config_entries - Config entries linked to this device.
|
||||
* @property {Record<string, (string | null)[]>} config_entries_subentries - Subentries for the config entries.
|
||||
* @property {[string, string][]} connections - Tuples of (connection_type, connection identifier).
|
||||
* @property {[string, string][]} identifiers - Set of (DOMAIN, identifier) tuples identifying the device.
|
||||
* @property {string | null} manufacturer - The manufacturer of the device.
|
||||
* @property {string | null} model - The model name of the device.
|
||||
* @property {string | null} model_id - The model identifier of the device.
|
||||
* @property {string | null} name - The name of the device.
|
||||
* @property {string[]} labels - Labels for the device.
|
||||
* @property {string | null} sw_version - The firmware version of the device.
|
||||
* @property {string | null} hw_version - The hardware version of the device.
|
||||
* @property {string | null} serial_number - The serial number of the device.
|
||||
* @property {string | null} via_device_id - Identifier of a device that routes messages to this device.
|
||||
* @property {string | null} area_id - The area which the device is placed in.
|
||||
* @property {string | null} name_by_user - User configured name of the device.
|
||||
* @property {string[] | null} entry_type - The type of entry (e.g., service).
|
||||
* @property {string | null} disabled_by - Indicates what disabled this entity.
|
||||
* @property {string | null} configuration_url - URL for configuring the device.
|
||||
* @property {string | null} primary_config_entry - Identifier of the primary config entry for the device.
|
||||
* @property {string} id Unique ID of a device (generated by Home Assistant).
|
||||
* @property {string[]} config_entries Config entries that are linked to this device.
|
||||
* @property {Record<string, (string | null)[]>} config_entries_subentries
|
||||
* @property {[string, string][]} connections A set of tuples of (connection_type, connection identifier).
|
||||
* Connection types are defined in the device registry module.
|
||||
* Each item in the set uniquely defines a device entry, meaning another
|
||||
* device can't have the same connection.
|
||||
* @property {[string, string][]} identifiers Set of (DOMAIN, identifier) tuples.
|
||||
* Identifiers identify the device in the outside world.
|
||||
* An example is a serial number.
|
||||
* Each item in the set uniquely defines a device entry, meaning another
|
||||
* device can't have the same identifier.
|
||||
* @property {string | null} manufacturer The manufacturer of the device.
|
||||
* @property {string | null} model The model name of the device.
|
||||
* @property {string | null} model_id The model identifier of the device.
|
||||
* @property {string | null} name Name of this device
|
||||
* @property {string[]} labels
|
||||
* @property {string | null} sw_version The firmware version of the device.
|
||||
* @property {string | null} hw_version The hardware version of the device.
|
||||
* @property {string | null} serial_number The serial number of the device.
|
||||
* Unlike a serial number in the identifiers set, this does not need to be
|
||||
* unique.
|
||||
* @property {string | null} via_device_id Identifier of a device that routes messages between this device and Home Assistant.
|
||||
* Examples of such devices are hubs, or parent devices of a sub-device.
|
||||
* This is used to show device topology in Home Assistant.
|
||||
* @property {string} area_id The Area which the device is placed in.
|
||||
* @property {string | null} name_by_user The user configured name of the device.
|
||||
* @property {string[] | null} entry_type The type of entry. Possible values are None and DeviceEntryType enum members
|
||||
* (only service).
|
||||
* @property {string | null} disabled_by Indicates by what this entity is disabled.
|
||||
* @property {string | null} configuration_url A URL on which the device or service can be configured,
|
||||
* linking to paths inside the Home Assistant UI can be done by using
|
||||
* homeassistant://<path>.
|
||||
* @property {string | null} primary_config_entry
|
||||
*/
|
||||
export interface DeviceRegistryEntry {
|
||||
id: string;
|
||||
@@ -39,8 +53,8 @@ export interface DeviceRegistryEntry {
|
||||
via_device_id: string | null;
|
||||
area_id: string | null;
|
||||
name_by_user: string | null;
|
||||
entry_type: 'service' | null;
|
||||
disabled_by: 'user' | 'integration' | 'config_entry' | null;
|
||||
entry_type: "service" | null;
|
||||
disabled_by: "user" | "integration" | "config_entry" | null;
|
||||
configuration_url: string | null;
|
||||
primary_config_entry: string | null;
|
||||
}
|
||||
|
@@ -1,23 +1,7 @@
|
||||
import { LightColor } from './light';
|
||||
import {LightColor} from "./light";
|
||||
|
||||
export type EntityCategory = 'config' | 'diagnostic';
|
||||
type EntityCategory = "config" | "diagnostic";
|
||||
|
||||
/**
|
||||
* Represents the display entry for an entity in the entity registry.
|
||||
*
|
||||
* @property {string} entity_id - The unique identifier for the entity.
|
||||
* @property {string} [name] - The name of the entity.
|
||||
* @property {string} [icon] - The icon associated with the entity.
|
||||
* @property {string} [device_id] - The ID of the device linked to this entity.
|
||||
* @property {string} [area_id] - The ID of the area linked to this entity.
|
||||
* @property {string[]} labels - Labels associated with the entity.
|
||||
* @property {boolean} [hidden] - Indicates if the entity is hidden.
|
||||
* @property {EntityCategory} [entity_category] - The category of the entity.
|
||||
* @property {string} [translation_key] - The translation key for the entity.
|
||||
* @property {string} [platform] - The platform of the entity.
|
||||
* @property {number} [display_precision] - The display precision for the entity.
|
||||
* @property {boolean} [has_entity_name] - Indicates if the entity has a name.
|
||||
*/
|
||||
export interface EntityRegistryDisplayEntry {
|
||||
entity_id: string;
|
||||
name?: string;
|
||||
@@ -34,43 +18,41 @@ export interface EntityRegistryDisplayEntry {
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents an entity in the entity registry of Home Assistant.
|
||||
* Home assistant entity.
|
||||
*
|
||||
* @property {string} id - The unique identifier for the entity.
|
||||
* @property {string} entity_id - The ID of the entity.
|
||||
* @property {string | null} name - The name of the entity.
|
||||
* @property {string | null} icon - The icon associated with the entity.
|
||||
* @property {string | null} platform - The platform of the entity.
|
||||
* @property {string | null} config_entry_id - The ID of the config entry associated with the entity.
|
||||
* @property {string | null} config_subentry_id - The ID of the config subentry associated with the entity.
|
||||
* @property {string | null} device_id - The ID of the device linked to this entity.
|
||||
* @property {string | null} area_id - The ID of the area linked to this entity.
|
||||
* @property {string[]} labels - Labels associated with the entity.
|
||||
* @property {"user" | "device" | "integration" | "config_entry" | null} disabled_by - Indicates what disabled this
|
||||
* entity.
|
||||
* @property {Exclude<EntityRegistryEntry["disabled_by"], "config_entry">} hidden_by - Indicates what hidden this
|
||||
* entity.
|
||||
* @property {EntityCategory | null} entity_category - The category of the entity.
|
||||
* @property {boolean} has_entity_name - Indicates if the entity has a name.
|
||||
* @property {string} [original_name] - The original name of the entity.
|
||||
* @property {string} unique_id - The unique identifier for the entity.
|
||||
* @property {string} [translation_key] - The translation key for the entity.
|
||||
* @property {EntityRegistryOptions | null} options - Additional options for the entity.
|
||||
* @property {Record<string, string>} categories - Categories associated with the entity.
|
||||
* @property {string} id
|
||||
* @property {string} entity_id The id of this entity.
|
||||
* @property {string} name The name of this entity.
|
||||
* @property {string | null} icon
|
||||
* @property {string | null} platform
|
||||
* @property {string | null} config_entry_id
|
||||
* @property {string | null} config_subentry_id
|
||||
* @property {string | null} device_id The id of the device to which this entity is linked.
|
||||
* @property {string | null} area_id The id of the area to which this entity is linked.
|
||||
* @property {string[]} labels
|
||||
* @property {string | null} disabled_by Indicates by what this entity is disabled.
|
||||
* @property {Object} hidden_by Indicates by what this entity is hidden.
|
||||
* @property {EntityCategory | null} entity_category
|
||||
* @property {boolean} has_entity_name
|
||||
* @property {string} [original_name]
|
||||
* @property {string} unique_id
|
||||
* @property {string} [translation_key]
|
||||
* @property {EntityRegistryOptions | null} options
|
||||
* @property {Record<string, string>} categories
|
||||
*/
|
||||
export interface EntityRegistryEntry {
|
||||
id: string;
|
||||
entity_id: string;
|
||||
name: string | null;
|
||||
icon: string | null;
|
||||
platform: string | null;
|
||||
platform: string;
|
||||
config_entry_id: string | null;
|
||||
config_subentry_id: string | null;
|
||||
device_id: string | null;
|
||||
area_id: string | null;
|
||||
labels: string[];
|
||||
disabled_by: 'user' | 'device' | 'integration' | 'config_entry' | null;
|
||||
hidden_by: Exclude<EntityRegistryEntry['disabled_by'], 'config_entry'>;
|
||||
disabled_by: "user" | "device" | "integration" | "config_entry" | null;
|
||||
hidden_by: Exclude<EntityRegistryEntry["disabled_by"], "config_entry">;
|
||||
entity_category: EntityCategory | null;
|
||||
has_entity_name: boolean;
|
||||
original_name?: string;
|
||||
@@ -80,64 +62,28 @@ export interface EntityRegistryEntry {
|
||||
categories: Record<string, string>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents options for a sensor entity in Home Assistant.
|
||||
*
|
||||
* @property {number | null} [display_precision] - The display precision for the sensor.
|
||||
* @property {number | null} [suggested_display_precision] - Suggested display precision for the sensor.
|
||||
* @property {string | null} [unit_of_measurement] - The unit of measurement for the sensor.
|
||||
*/
|
||||
export interface SensorEntityOptions {
|
||||
display_precision?: number | null;
|
||||
suggested_display_precision?: number | null;
|
||||
unit_of_measurement?: string | null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents options for a light entity in Home Assistant.
|
||||
*
|
||||
* @property {LightColor[]} [favorite_colors] - An array of favorite colors for the light.
|
||||
*/
|
||||
export interface LightEntityOptions {
|
||||
favorite_colors?: LightColor[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents options for a number entity in Home Assistant.
|
||||
*
|
||||
* @property {string | null} [unit_of_measurement] - The unit of measurement for the number.
|
||||
*/
|
||||
export interface NumberEntityOptions {
|
||||
unit_of_measurement?: string | null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents options for a lock entity in Home Assistant.
|
||||
*
|
||||
* @property {string | null} [default_code] - The default code for the lock.
|
||||
*/
|
||||
export interface LockEntityOptions {
|
||||
default_code?: string | null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents options for an alarm control panel entity in Home Assistant.
|
||||
*
|
||||
* @property {string | null} [default_code] - The default code for the alarm control panel.
|
||||
*/
|
||||
export interface AlarmControlPanelEntityOptions {
|
||||
default_code?: string | null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents options for a weather entity in Home Assistant.
|
||||
*
|
||||
* @property {string | null} [precipitation_unit] - The unit of measurement for precipitation.
|
||||
* @property {string | null} [pressure_unit] - The unit of measurement for pressure.
|
||||
* @property {string | null} [temperature_unit] - The unit of measurement for temperature.
|
||||
* @property {string | null} [visibility_unit] - The unit of measurement for visibility.
|
||||
* @property {string | null} [wind_speed_unit] - The unit of measurement for wind speed.
|
||||
*/
|
||||
export interface WeatherEntityOptions {
|
||||
precipitation_unit?: string | null;
|
||||
pressure_unit?: string | null;
|
||||
@@ -146,31 +92,11 @@ export interface WeatherEntityOptions {
|
||||
wind_speed_unit?: string | null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents options for a switch entity in Home Assistant.
|
||||
*
|
||||
* @property {string} entity_id - The ID of the entity.
|
||||
* @property {boolean} invert - Indicates if the switch should be inverted.
|
||||
*/
|
||||
export interface SwitchAsXEntityOptions {
|
||||
entity_id: string;
|
||||
invert: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents options for an entity in the entity registry of Home Assistant.
|
||||
*
|
||||
* @property {NumberEntityOptions} [number] - Options for number entities.
|
||||
* @property {SensorEntityOptions} [sensor] - Options for sensor entities.
|
||||
* @property {AlarmControlPanelEntityOptions} [alarm_control_panel] - Options for alarm control panel entities.
|
||||
* @property {LockEntityOptions} [lock] - Options for lock entities.
|
||||
* @property {WeatherEntityOptions} [weather] - Options for weather entities.
|
||||
* @property {LightEntityOptions} [light] - Options for light entities.
|
||||
* @property {SwitchAsXEntityOptions} [switch_as_x] - Options for switch entities.
|
||||
* @property {Record<string, unknown>} [conversation] - Options for conversation entities.
|
||||
* @property {Record<string, unknown>} ["cloud.alexa"] - Options for Alexa cloud integration.
|
||||
* @property {Record<string, unknown>} ["cloud.google_assistant"] - Options for Google Assistant cloud integration.
|
||||
*/
|
||||
export interface EntityRegistryOptions {
|
||||
number?: NumberEntityOptions;
|
||||
sensor?: SensorEntityOptions;
|
||||
@@ -180,6 +106,7 @@ export interface EntityRegistryOptions {
|
||||
light?: LightEntityOptions;
|
||||
switch_as_x?: SwitchAsXEntityOptions;
|
||||
conversation?: Record<string, unknown>;
|
||||
'cloud.alexa'?: Record<string, unknown>;
|
||||
'cloud.google_assistant'?: Record<string, unknown>;
|
||||
"cloud.alexa"?: Record<string, unknown>;
|
||||
"cloud.google_assistant"?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
|
@@ -1,14 +1,5 @@
|
||||
import { RegistryEntry } from './registry';
|
||||
import {RegistryEntry} from "./registry";
|
||||
|
||||
/**
|
||||
* Represents a floor entry in the Floor Registry of Home Assistant.
|
||||
*
|
||||
* @property {string} floor_id - The unique identifier for the floor.
|
||||
* @property {string} name - The name of the floor.
|
||||
* @property {number | null} level - The level of the floor (optional).
|
||||
* @property {string | null} icon - The icon associated with the floor (optional).
|
||||
* @property {string[]} aliases - An array of aliases for the floor.
|
||||
*/
|
||||
export interface FloorRegistryEntry extends RegistryEntry {
|
||||
floor_id: string;
|
||||
name: string;
|
||||
|
@@ -1,8 +1,3 @@
|
||||
/**
|
||||
* Represents user data for the frontend in Home Assistant.
|
||||
*
|
||||
* @property {boolean} [showAdvanced] - Indicates whether advanced options should be shown to the user.
|
||||
*/
|
||||
export interface CoreFrontendUserData {
|
||||
showAdvanced?: boolean;
|
||||
}
|
||||
|
81
src/types/homeassistant/data/lovelace.ts
Normal file
81
src/types/homeassistant/data/lovelace.ts
Normal file
@@ -0,0 +1,81 @@
|
||||
import {HassServiceTarget} from "home-assistant-js-websocket";
|
||||
import {LovelaceGridOptions, LovelaceLayoutOptions} from "../panels/lovelace/types";
|
||||
import {Condition} from "../panels/common/validate-condition";
|
||||
|
||||
export interface LovelaceCardConfig {
|
||||
index?: number;
|
||||
view_index?: number;
|
||||
view_layout?: any;
|
||||
/** @deprecated Use `grid_options` instead */
|
||||
layout_options?: LovelaceLayoutOptions;
|
||||
grid_options?: LovelaceGridOptions;
|
||||
type: string;
|
||||
[key: string]: any;
|
||||
visibility?: Condition[];
|
||||
}
|
||||
|
||||
export interface ToggleActionConfig extends BaseActionConfig {
|
||||
action: "toggle";
|
||||
}
|
||||
|
||||
export interface CallServiceActionConfig extends BaseActionConfig {
|
||||
action: "call-service";
|
||||
service: string;
|
||||
target?: HassServiceTarget;
|
||||
// Property "service_data" is kept for backwards compatibility. Replaced by "data".
|
||||
service_data?: Record<string, unknown>;
|
||||
data?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
export interface NavigateActionConfig extends BaseActionConfig {
|
||||
action: "navigate";
|
||||
navigation_path: string;
|
||||
navigation_replace?: boolean;
|
||||
}
|
||||
|
||||
export interface UrlActionConfig extends BaseActionConfig {
|
||||
action: "url";
|
||||
url_path: string;
|
||||
}
|
||||
|
||||
export interface MoreInfoActionConfig extends BaseActionConfig {
|
||||
action: "more-info";
|
||||
}
|
||||
|
||||
export interface AssistActionConfig extends BaseActionConfig {
|
||||
action: "assist";
|
||||
pipeline_id?: string;
|
||||
start_listening?: boolean;
|
||||
}
|
||||
|
||||
export interface NoActionConfig extends BaseActionConfig {
|
||||
action: "none";
|
||||
}
|
||||
|
||||
export interface CustomActionConfig extends BaseActionConfig {
|
||||
action: "fire-dom-event";
|
||||
}
|
||||
|
||||
export interface BaseActionConfig {
|
||||
action: string;
|
||||
confirmation?: ConfirmationRestrictionConfig;
|
||||
}
|
||||
|
||||
export interface ConfirmationRestrictionConfig {
|
||||
text?: string;
|
||||
exemptions?: RestrictionConfig[];
|
||||
}
|
||||
|
||||
export interface RestrictionConfig {
|
||||
user: string;
|
||||
}
|
||||
|
||||
export type ActionConfig =
|
||||
| ToggleActionConfig
|
||||
| CallServiceActionConfig
|
||||
| NavigateActionConfig
|
||||
| UrlActionConfig
|
||||
| MoreInfoActionConfig
|
||||
| AssistActionConfig
|
||||
| NoActionConfig
|
||||
| CustomActionConfig;
|
@@ -1,15 +1,7 @@
|
||||
import { Condition } from '../../../panels/common/validate-condition';
|
||||
import {Condition} from "../../../panels/common/validate-condition";
|
||||
|
||||
/**
|
||||
* Represents the configuration for a Lovelace badge in Home Assistant.
|
||||
*
|
||||
* @property {string} type - The type of the badge.
|
||||
* @property {Condition[]} [visibility] - An optional array of visibility conditions for the badge.
|
||||
* @property {any} [key] - Additional properties can be included in the configuration.
|
||||
*/
|
||||
export interface LovelaceBadgeConfig {
|
||||
type: string;
|
||||
visibility?: Condition[];
|
||||
|
||||
[key: string]: any;
|
||||
visibility?: Condition[];
|
||||
}
|
||||
|
@@ -1,18 +1,6 @@
|
||||
import { Condition } from '../../../panels/common/validate-condition';
|
||||
import { LovelaceGridOptions, LovelaceLayoutOptions } from '../../../panels/lovelace/types';
|
||||
import {Condition} from "../../../panels/common/validate-condition";
|
||||
import {LovelaceGridOptions, LovelaceLayoutOptions} from "../../../panels/lovelace/types";
|
||||
|
||||
/**
|
||||
* Represents the configuration for a Lovelace card in Home Assistant.
|
||||
*
|
||||
* @property {number} [index] - The index of the card in the view.
|
||||
* @property {number} [view_index] - The index of the view the card belongs to.
|
||||
* @property {any} [view_layout] - The layout options for the card view.
|
||||
* @property {LovelaceLayoutOptions} [layout_options] - Deprecated layout options; use `grid_options` instead.
|
||||
* @property {LovelaceGridOptions} [grid_options] - The grid options for the card layout.
|
||||
* @property {string} type - The type of the card.
|
||||
* @property {Condition[]} [visibility] - An optional array of visibility conditions for the card.
|
||||
* @property {any} [key] - Additional properties can be included in the configuration.
|
||||
*/
|
||||
export interface LovelaceCardConfig {
|
||||
index?: number;
|
||||
view_index?: number;
|
||||
@@ -21,7 +9,6 @@ export interface LovelaceCardConfig {
|
||||
layout_options?: LovelaceLayoutOptions;
|
||||
grid_options?: LovelaceGridOptions;
|
||||
type: string;
|
||||
visibility?: Condition[];
|
||||
|
||||
[key: string]: any;
|
||||
visibility?: Condition[];
|
||||
}
|
||||
|
@@ -1,42 +1,27 @@
|
||||
import { Condition } from '../../../panels/common/validate-condition';
|
||||
import { LovelaceCardConfig } from './card';
|
||||
import { LovelaceStrategyConfig } from './strategy';
|
||||
import {LovelaceStrategyConfig} from "./strategy";
|
||||
import {LovelaceCardConfig} from "../../lovelace";
|
||||
import {Condition} from "../../../panels/common/validate-condition";
|
||||
|
||||
/**
|
||||
* Represents the base configuration for a Lovelace section in Home Assistant.
|
||||
*
|
||||
* @property {Condition[]} [visibility] - An optional array of visibility conditions for the section.
|
||||
* @property {number} [column_span] - The number of columns the section spans.
|
||||
* @property {number} [row_span] - The number of rows the section spans.
|
||||
* @property {string} [title] - The title of the section (deprecated; use heading card instead).
|
||||
*/
|
||||
export interface LovelaceBaseSectionConfig {
|
||||
visibility?: Condition[];
|
||||
column_span?: number;
|
||||
row_span?: number;
|
||||
/** @deprecated Use heading card instead. */
|
||||
/**
|
||||
* @deprecated Use heading card instead.
|
||||
*/
|
||||
title?: string;
|
||||
}
|
||||
|
||||
export interface LovelaceSectionConfig /**
|
||||
* Represents the configuration for a Lovelace section in Home Assistant.
|
||||
*
|
||||
* @property {string} [type] - The type of the section.
|
||||
* @property {LovelaceCardConfig[]} [cards] - An optional array of cards contained within the section.
|
||||
*/
|
||||
extends LovelaceBaseSectionConfig {
|
||||
export interface LovelaceSectionConfig extends LovelaceBaseSectionConfig {
|
||||
type?: string;
|
||||
cards?: LovelaceCardConfig[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the configuration for a Lovelace strategy section in Home Assistant.
|
||||
*
|
||||
* @property {LovelaceStrategyConfig} strategy - The strategy configuration for the section.
|
||||
*/
|
||||
export interface LovelaceStrategySectionConfig extends LovelaceBaseSectionConfig {
|
||||
export interface LovelaceStrategySectionConfig
|
||||
extends LovelaceBaseSectionConfig {
|
||||
strategy: LovelaceStrategyConfig;
|
||||
}
|
||||
|
||||
/** Represents the raw configuration for a Lovelace section in Home Assistant. */
|
||||
export type LovelaceSectionRawConfig = LovelaceSectionConfig | LovelaceStrategySectionConfig;
|
||||
export type LovelaceSectionRawConfig =
|
||||
| LovelaceSectionConfig
|
||||
| LovelaceStrategySectionConfig;
|
||||
|
@@ -1,11 +1,4 @@
|
||||
/**
|
||||
* Represents the configuration for a Lovelace strategy in Home Assistant.
|
||||
*
|
||||
* @property {string} type - The type of the strategy.
|
||||
* @property {any} [key] - Additional properties can be included in the configuration.
|
||||
*/
|
||||
export interface LovelaceStrategyConfig {
|
||||
type: string;
|
||||
|
||||
[key: string]: any;
|
||||
}
|
||||
|
@@ -1,15 +1,10 @@
|
||||
import { LovelaceViewRawConfig } from './view';
|
||||
import {LovelaceViewRawConfig} from "./view";
|
||||
|
||||
/** Represents the base configuration for a Lovelace dashboard in Home Assistant. */
|
||||
export interface LovelaceDashboardBaseConfig {}
|
||||
|
||||
/**
|
||||
* Represents the configuration for a Lovelace dashboard in Home Assistant.
|
||||
*
|
||||
* @property {string} [background] - An optional background image or color for the dashboard.
|
||||
* @property {LovelaceViewRawConfig[]} views - An array of views contained within the dashboard.
|
||||
*/
|
||||
export interface LovelaceConfig extends LovelaceDashboardBaseConfig {
|
||||
background?: string;
|
||||
views: LovelaceViewRawConfig[];
|
||||
}
|
||||
|
||||
|
||||
|
@@ -1,76 +1,36 @@
|
||||
import { LovelaceBadgeConfig } from './badge';
|
||||
import { LovelaceCardConfig } from './card';
|
||||
import { LovelaceSectionRawConfig } from './section';
|
||||
import { LovelaceStrategyConfig } from './strategy';
|
||||
import {LovelaceStrategyConfig} from "./strategy";
|
||||
import {LovelaceSectionRawConfig} from "./section";
|
||||
import {LovelaceCardConfig} from "./card";
|
||||
import {LovelaceBadgeConfig} from "./badge";
|
||||
|
||||
/**
|
||||
* Represents the configuration for showing a view in Home Assistant.
|
||||
*
|
||||
* @property {string} [user] - The user associated with the view.
|
||||
*/
|
||||
export interface ShowViewConfig {
|
||||
user?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the background configuration for a Lovelace view in Home Assistant.
|
||||
*
|
||||
* @property {string} [image] - The background image URL.
|
||||
* @property {number} [opacity] - The opacity of the background.
|
||||
* @property {'auto' | 'cover' | 'contain'} [size] - The size of the background image.
|
||||
* @property {'top left' | 'top center' | 'top right' | 'center left' | 'center' | 'center right' | 'bottom left' |
|
||||
* 'bottom center' | 'bottom right'} [alignment] - The alignment of the background image.
|
||||
* @property {'repeat' | 'no-repeat'} [repeat] - The repeat behavior of the background image.
|
||||
* @property {'scroll' | 'fixed'} [attachment] - The attachment behavior of the background image.
|
||||
*/
|
||||
export interface LovelaceViewBackgroundConfig {
|
||||
image?: string;
|
||||
opacity?: number;
|
||||
size?: 'auto' | 'cover' | 'contain';
|
||||
size?: "auto" | "cover" | "contain";
|
||||
alignment?:
|
||||
| 'top left'
|
||||
| 'top center'
|
||||
| 'top right'
|
||||
| 'center left'
|
||||
| 'center'
|
||||
| 'center right'
|
||||
| 'bottom left'
|
||||
| 'bottom center'
|
||||
| 'bottom right';
|
||||
repeat?: 'repeat' | 'no-repeat';
|
||||
attachment?: 'scroll' | 'fixed';
|
||||
| "top left"
|
||||
| "top center"
|
||||
| "top right"
|
||||
| "center left"
|
||||
| "center"
|
||||
| "center right"
|
||||
| "bottom left"
|
||||
| "bottom center"
|
||||
| "bottom right";
|
||||
repeat?: "repeat" | "no-repeat";
|
||||
attachment?: "scroll" | "fixed";
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the header configuration for a Lovelace view in Home Assistant.
|
||||
*
|
||||
* @property {LovelaceCardConfig} [card] - The card to be displayed in the header.
|
||||
* @property {'start' | 'center' | 'responsive'} [layout] - The layout of the header.
|
||||
* @property {'bottom' | 'top'} [badges_position] - The position of badges in the header.
|
||||
*/
|
||||
export interface LovelaceViewHeaderConfig {
|
||||
card?: LovelaceCardConfig;
|
||||
layout?: 'start' | 'center' | 'responsive';
|
||||
badges_position?: 'bottom' | 'top';
|
||||
layout?: "start" | "center" | "responsive";
|
||||
badges_position?: "bottom" | "top";
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the base configuration for a Lovelace view in Home Assistant.
|
||||
*
|
||||
* @property {number} [index] - The index of the view.
|
||||
* @property {string} [title] - The title of the view.
|
||||
* @property {string} [path] - The path to the view.
|
||||
* @property {string} [icon] - The icon for the view.
|
||||
* @property {string} [theme] - The theme for the view.
|
||||
* @property {boolean} [panel] - Whether the view is a panel view.
|
||||
* @property {string | LovelaceViewBackgroundConfig} [background] - The background configuration for the view.
|
||||
* @property {boolean | ShowViewConfig[]} [visible] - Visibility settings for the view.
|
||||
* @property {boolean} [subview] - Whether the view is a subview.
|
||||
* @property {string} [back_path] - The path to go back to the previous view.
|
||||
* @property {number} [max_columns] - The maximum number of columns in the view.
|
||||
* @property {boolean} [dense_section_placement] - Whether to place sections densely.
|
||||
* @property {boolean} [top_margin] - Whether to add top margin to the view.
|
||||
*/
|
||||
export interface LovelaceBaseViewConfig {
|
||||
index?: number;
|
||||
title?: string;
|
||||
@@ -82,36 +42,33 @@ export interface LovelaceBaseViewConfig {
|
||||
visible?: boolean | ShowViewConfig[];
|
||||
subview?: boolean;
|
||||
back_path?: string;
|
||||
// Only used for section view, it should move to a section view config type when the views will have a dedicated editor.
|
||||
max_columns?: number;
|
||||
dense_section_placement?: boolean;
|
||||
top_margin?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the configuration for a Lovelace view in Home Assistant.
|
||||
* View Config.
|
||||
*
|
||||
* @property {string} [type] - The type of the view.
|
||||
* @property {(string | Partial<LovelaceBadgeConfig>)[]} [badges] - An array of badges for the view.
|
||||
* @property {LovelaceCardConfig[]} [cards] - An array of cards in the view.
|
||||
* @property {LovelaceSectionRawConfig[]} [sections] - An array of sections in the view.
|
||||
* @property {LovelaceViewHeaderConfig} [header] - The header configuration for the view.
|
||||
* @see https://www.home-assistant.io/dashboards/views/
|
||||
*/
|
||||
export interface LovelaceViewConfig extends LovelaceBaseViewConfig {
|
||||
type?: string;
|
||||
badges?: (string | Partial<LovelaceBadgeConfig>)[];
|
||||
badges?: (string | Partial<LovelaceBadgeConfig>)[]; // Badge can be just an entity_id or without type
|
||||
cards?: LovelaceCardConfig[];
|
||||
sections?: LovelaceSectionRawConfig[];
|
||||
header?: LovelaceViewHeaderConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the configuration for a Lovelace strategy view in Home Assistant.
|
||||
*
|
||||
* @property {LovelaceStrategyConfig} strategy - The strategy configuration for the view.
|
||||
*/
|
||||
export interface LovelaceStrategyViewConfig extends LovelaceBaseViewConfig {
|
||||
strategy: LovelaceStrategyConfig;
|
||||
}
|
||||
|
||||
/**Represents the raw configuration for a Lovelace view in Home Assistant. */
|
||||
export type LovelaceViewRawConfig = LovelaceViewConfig | LovelaceStrategyViewConfig;
|
||||
export interface LovelaceStrategyViewConfig extends LovelaceBaseViewConfig {
|
||||
strategy: LovelaceStrategyConfig;
|
||||
}
|
||||
|
||||
export type LovelaceViewRawConfig =
|
||||
| LovelaceViewConfig
|
||||
| LovelaceStrategyViewConfig;
|
||||
|
@@ -1,9 +1,3 @@
|
||||
/**
|
||||
* Represents a registry entry in Home Assistant.
|
||||
*
|
||||
* @property {number} created_at - The timestamp when the entry was created.
|
||||
* @property {number} modified_at - The timestamp when the entry was last modified.
|
||||
*/
|
||||
export interface RegistryEntry {
|
||||
created_at: number;
|
||||
modified_at: number;
|
||||
|
@@ -1,62 +1,47 @@
|
||||
// noinspection JSUnusedGlobalSymbols
|
||||
|
||||
import { HomeAssistant } from '../types';
|
||||
import {HomeAssistant} from "../types";
|
||||
|
||||
/** Represents the different formats for numbers in Home Assistant. */
|
||||
export enum NumberFormat {
|
||||
language = 'language',
|
||||
system = 'system',
|
||||
comma_decimal = 'comma_decimal',
|
||||
decimal_comma = 'decimal_comma',
|
||||
space_comma = 'space_comma',
|
||||
none = 'none',
|
||||
language = "language",
|
||||
system = "system",
|
||||
comma_decimal = "comma_decimal",
|
||||
decimal_comma = "decimal_comma",
|
||||
space_comma = "space_comma",
|
||||
none = "none",
|
||||
}
|
||||
|
||||
/**Represents the different formats for time in Home Assistant. */
|
||||
export enum TimeFormat {
|
||||
language = 'language',
|
||||
system = 'system',
|
||||
am_pm = '12',
|
||||
twenty_four = '24',
|
||||
language = "language",
|
||||
system = "system",
|
||||
am_pm = "12",
|
||||
twenty_four = "24",
|
||||
}
|
||||
|
||||
/** Represents the different time zones in Home Assistant. */
|
||||
export enum TimeZone {
|
||||
local = 'local',
|
||||
server = 'server',
|
||||
local = "local",
|
||||
server = "server",
|
||||
}
|
||||
|
||||
/** Represents the different formats for dates in Home Assistant. */
|
||||
export enum DateFormat {
|
||||
language = 'language',
|
||||
system = 'system',
|
||||
DMY = 'DMY',
|
||||
MDY = 'MDY',
|
||||
YMD = 'YMD',
|
||||
language = "language",
|
||||
system = "system",
|
||||
DMY = "DMY",
|
||||
MDY = "MDY",
|
||||
YMD = "YMD",
|
||||
}
|
||||
|
||||
/**Represents the first weekday in Home Assistant. */
|
||||
export enum FirstWeekday {
|
||||
language = 'language',
|
||||
monday = 'monday',
|
||||
tuesday = 'tuesday',
|
||||
wednesday = 'wednesday',
|
||||
thursday = 'thursday',
|
||||
friday = 'friday',
|
||||
saturday = 'saturday',
|
||||
sunday = 'sunday',
|
||||
language = "language",
|
||||
monday = "monday",
|
||||
tuesday = "tuesday",
|
||||
wednesday = "wednesday",
|
||||
thursday = "thursday",
|
||||
friday = "friday",
|
||||
saturday = "saturday",
|
||||
sunday = "sunday",
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the locale data for the frontend in Home Assistant.
|
||||
*
|
||||
* @property {string} language - The language of the frontend.
|
||||
* @property {NumberFormat} number_format - The format for numbers.
|
||||
* @property {TimeFormat} time_format - The format for time.
|
||||
* @property {DateFormat} date_format - The format for dates.
|
||||
* @property {FirstWeekday} first_weekday - The first weekday.
|
||||
* @property {TimeZone} time_zone - The time zone.
|
||||
*/
|
||||
export interface FrontendLocaleData {
|
||||
language: string;
|
||||
number_format: NumberFormat;
|
||||
@@ -66,47 +51,33 @@ export interface FrontendLocaleData {
|
||||
time_zone: TimeZone;
|
||||
}
|
||||
|
||||
/** Represents a category for translations in Home Assistant. */
|
||||
export type TranslationCategory =
|
||||
| 'title'
|
||||
| 'state'
|
||||
| 'entity'
|
||||
| 'entity_component'
|
||||
| 'exceptions'
|
||||
| 'config'
|
||||
| 'config_subentries'
|
||||
| 'config_panel'
|
||||
| 'options'
|
||||
| 'device_automation'
|
||||
| 'mfa_setup'
|
||||
| 'system_health'
|
||||
| 'application_credentials'
|
||||
| 'issues'
|
||||
| 'selector'
|
||||
| 'services';
|
||||
| "title"
|
||||
| "state"
|
||||
| "entity"
|
||||
| "entity_component"
|
||||
| "exceptions"
|
||||
| "config"
|
||||
| "config_subentries"
|
||||
| "config_panel"
|
||||
| "options"
|
||||
| "device_automation"
|
||||
| "mfa_setup"
|
||||
| "system_health"
|
||||
| "application_credentials"
|
||||
| "issues"
|
||||
| "selector"
|
||||
| "services";
|
||||
|
||||
/**
|
||||
* Retrieves the translations for Home Assistant.
|
||||
*
|
||||
* @async
|
||||
*
|
||||
* @param {HomeAssistant} hass - The Home Assistant instance.
|
||||
* @param {string} language - The language for translations.
|
||||
* @param {TranslationCategory} category - The category of translations.
|
||||
* @param {string | string[]} [integration] - Optional integration name(s).
|
||||
* @param {boolean} [config_flow] - Optional flag for config flow.
|
||||
*
|
||||
* @returns {Promise<Record<string, unknown>>} A promise resolving to an object containing translation key-value pairs.
|
||||
*/
|
||||
export const getHassTranslations = async (
|
||||
hass: HomeAssistant,
|
||||
language: string,
|
||||
category: TranslationCategory,
|
||||
integration?: string | string[],
|
||||
config_flow?: boolean,
|
||||
config_flow?: boolean
|
||||
): Promise<Record<string, unknown>> => {
|
||||
const result = await hass.callWS<{ resources: Record<string, unknown> }>({
|
||||
type: 'frontend/get_translations',
|
||||
type: "frontend/get_translations",
|
||||
language,
|
||||
category,
|
||||
integration,
|
||||
|
@@ -1,26 +1,11 @@
|
||||
/**
|
||||
* Represents the variables for a theme in Home Assistant.
|
||||
*
|
||||
* @property {string} primary-color - The primary color of the theme.
|
||||
* @property {string} text-primary-color - The primary text color of the theme.
|
||||
* @property {string} accent-color - The accent color of the theme.
|
||||
* @property {string} [key] - Additional theme variables as key-value pairs.
|
||||
*/
|
||||
export interface ThemeVars {
|
||||
// Incomplete
|
||||
'primary-color': string;
|
||||
'text-primary-color': string;
|
||||
'accent-color': string;
|
||||
|
||||
"primary-color": string;
|
||||
"text-primary-color": string;
|
||||
"accent-color": string;
|
||||
[key: string]: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a theme configuration in Home Assistant.
|
||||
*
|
||||
* @property {ThemeVars} modes.light - The light mode variables.
|
||||
* @property {ThemeVars} modes.dark - The dark mode variables.
|
||||
*/
|
||||
export type Theme = ThemeVars & {
|
||||
modes?: {
|
||||
light?: ThemeVars;
|
||||
@@ -28,23 +13,14 @@ export type Theme = ThemeVars & {
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents the overall themes configuration in Home Assistant.
|
||||
*
|
||||
* @property {string} default_theme - The default theme name.
|
||||
* @property {string | null} default_dark_theme - The default dark theme name or null.
|
||||
* @property {Record<string, Theme>} themes - A record of available themes.
|
||||
* @property {boolean} darkMode - Currently effective dark mode.
|
||||
* It Will never be undefined.
|
||||
* If the user selected "auto" in the theme picker, this property will still contain
|
||||
* either true or false based on what has been determined via system preferences and
|
||||
* support for the selected theme.
|
||||
* @property {string} theme - Currently globally active theme name
|
||||
*/
|
||||
export interface Themes {
|
||||
default_theme: string;
|
||||
default_dark_theme: string | null;
|
||||
themes: Record<string, Theme>;
|
||||
// Currently effective dark mode. Will never be undefined. If user selected "auto"
|
||||
// in theme picker, this property will still contain either true or false based on
|
||||
// what has been determined via system preferences and support for the selected theme.
|
||||
darkMode: boolean;
|
||||
// Currently globally active theme name
|
||||
theme: string;
|
||||
}
|
||||
|
@@ -1,4 +1,3 @@
|
||||
/** Represents a condition in Home Assistant. */
|
||||
export type Condition =
|
||||
| NumericStateCondition
|
||||
| StateCondition
|
||||
@@ -7,85 +6,40 @@ export type Condition =
|
||||
| OrCondition
|
||||
| AndCondition;
|
||||
|
||||
/**
|
||||
* Base interface for all conditions in Home Assistant.
|
||||
*
|
||||
* @property {string} condition - The type of condition.
|
||||
*/
|
||||
interface BaseCondition {
|
||||
condition: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a numeric state condition in Home Assistant.
|
||||
*
|
||||
* @property {'numeric_state'} condition - The condition type.
|
||||
* @property {string} [entity] - The entity to evaluate.
|
||||
* @property {string | number} [below] - The threshold value below which the condition is true.
|
||||
* @property {string | number} [above] - The threshold value above which the condition is true.
|
||||
*/
|
||||
export interface NumericStateCondition extends BaseCondition {
|
||||
condition: 'numeric_state';
|
||||
condition: "numeric_state";
|
||||
entity?: string;
|
||||
below?: string | number;
|
||||
above?: string | number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a state condition in Home Assistant.
|
||||
*
|
||||
* @property {'state'} condition - The condition type.
|
||||
* @property {string} [entity] - The entity to evaluate.
|
||||
* @property {string | string[]} [state] - The expected state of the entity.
|
||||
* @property {string | string[]} [state_not] - The state that the entity should not be in.
|
||||
*/
|
||||
export interface StateCondition extends BaseCondition {
|
||||
condition: 'state';
|
||||
condition: "state";
|
||||
entity?: string;
|
||||
state?: string | string[];
|
||||
state_not?: string | string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a screen condition in Home Assistant.
|
||||
*
|
||||
* @property {'screen'} condition - The condition type.
|
||||
* @property {string} [media_query] - The media query for screen conditions.
|
||||
*/
|
||||
export interface ScreenCondition extends BaseCondition {
|
||||
condition: 'screen';
|
||||
condition: "screen";
|
||||
media_query?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a user condition in Home Assistant.
|
||||
*
|
||||
* @property {'user'} condition - The condition type.
|
||||
* @property {string[]} [users] - The list of users for the condition.
|
||||
*/
|
||||
export interface UserCondition extends BaseCondition {
|
||||
condition: 'user';
|
||||
condition: "user";
|
||||
users?: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents an OR condition in Home Assistant.
|
||||
*
|
||||
* @property {'or'} condition - The condition type.
|
||||
* @property {Condition[]} [conditions] - The list of conditions to evaluate.
|
||||
*/
|
||||
export interface OrCondition extends BaseCondition {
|
||||
condition: 'or';
|
||||
condition: "or";
|
||||
conditions?: Condition[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents an AND condition in Home Assistant.
|
||||
*
|
||||
* @property {'and'} condition - The condition type.
|
||||
* @property {Condition[]} [conditions] - The list of conditions to evaluate.
|
||||
*/
|
||||
export interface AndCondition extends BaseCondition {
|
||||
condition: 'and';
|
||||
condition: "and";
|
||||
conditions?: Condition[];
|
||||
}
|
||||
|
@@ -1,41 +1,40 @@
|
||||
import { ActionConfig } from '../../../data/lovelace/config/action';
|
||||
import { LovelaceCardConfig } from '../../../data/lovelace/config/card';
|
||||
import {ActionConfig, LovelaceCardConfig} from "../../../data/lovelace";
|
||||
|
||||
/**
|
||||
* Home Assistant Area Card Config.
|
||||
*
|
||||
* @property {string} area - The area associated with the card.
|
||||
* @property {string} [navigation_path] - Optional navigation path for the card.
|
||||
* @property {boolean} [show_camera] - Whether to show the camera view.
|
||||
* @property {"live" | "auto"} [camera_view] - The camera view mode.
|
||||
* @property {string} [aspect_ratio] - The aspect ratio of the card.
|
||||
* @see https://www.home-assistant.io/dashboards/area/
|
||||
*/
|
||||
export interface AreaCardConfig extends LovelaceCardConfig {
|
||||
area: string;
|
||||
navigation_path?: string;
|
||||
show_camera?: boolean;
|
||||
camera_view?: 'live' | 'auto';
|
||||
camera_view?: "live" | "auto";
|
||||
aspect_ratio?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Home Assistant Picture Entity Config.
|
||||
*
|
||||
* @property {string} entity - An entity_id used for the picture.
|
||||
* @property {string} [name] - Overwrite entity name.
|
||||
* @property {string} [image] - URL of an image.
|
||||
* @property {string} [camera_image] - Camera entity_id to use.
|
||||
* @property {"live" | "auto"} [camera_view] - The camera view mode.
|
||||
* @property {Record<string, unknown>} [state_image] - Map entity states to images.
|
||||
* @property {string[]} [state_filter] - State-based CSS filters.
|
||||
* @property {string} [aspect_ratio] - Forces the height of the image to be a ratio of the width.
|
||||
* @property {ActionConfig} [tap_action] - Action taken on card tap.
|
||||
* @property {ActionConfig} [hold_action] - Action taken on card tap and hold.
|
||||
* @property {ActionConfig} [double_tap_action] - Action taken on card double tap.
|
||||
* @property {boolean} [show_name=true] - Shows name in footer.
|
||||
* @property {string} [theme=true] - Override the used theme for this card.
|
||||
* @property {boolean} [show_state] - Shows state in footer.
|
||||
* @property {string} entity An entity_id used for the picture.
|
||||
* @property {string} [name] Overwrite entity name.
|
||||
* @property {string} [image] URL of an image.
|
||||
* @property {string} [camera_image] Camera entity_id to use.
|
||||
* (not required if the entity is already a camera-entity).
|
||||
* @property {string} [camera_view=auto] “live” will show the live view if stream is enabled.
|
||||
* @property {Record<string, unknown>} [state_image] Map entity states to images (state: image URL).
|
||||
* @property {string[]} [state_filter] State-based CSS filters.
|
||||
* @property {string} [aspect_ratio] Forces the height of the image to be a ratio of the width.
|
||||
* Valid formats: Height percentage value (23%) or ratio expressed with colon or “x”
|
||||
* separator (16:9 or 16x9).
|
||||
* For a ratio, the second element can be omitted and will default to “1”
|
||||
* (1.78 equals 1.78:1).
|
||||
* @property {ActionConfig} [tap_action] Action taken on card tap.
|
||||
* @property {ActionConfig} [hold_action] Action taken on card tap and hold.
|
||||
* @property {ActionConfig} [double_tap_action] Action taken on card double tap.
|
||||
* @property {boolean} [show_name=true] Shows name in footer.
|
||||
* @property {string} [theme=true] Override the used theme for this card with any loaded theme.
|
||||
*
|
||||
* @see https://www.home-assistant.io/dashboards/picture-entity/
|
||||
*/
|
||||
export interface PictureEntityCardConfig extends LovelaceCardConfig {
|
||||
@@ -43,7 +42,7 @@ export interface PictureEntityCardConfig extends LovelaceCardConfig {
|
||||
name?: string;
|
||||
image?: string;
|
||||
camera_image?: string;
|
||||
camera_view?: 'live' | 'auto';
|
||||
camera_view?: "live" | "auto";
|
||||
state_image?: Record<string, unknown>;
|
||||
state_filter?: string[];
|
||||
aspect_ratio?: string;
|
||||
@@ -58,13 +57,13 @@ export interface PictureEntityCardConfig extends LovelaceCardConfig {
|
||||
/**
|
||||
* Home Assistant Stack Card Config.
|
||||
*
|
||||
* @property {string} type - The stack type.
|
||||
* @property {Object[]} cards - The content of the stack.
|
||||
* @property {string} type The stack type.
|
||||
* @property {Object[]} cards The content of the stack.
|
||||
*
|
||||
* @see https://www.home-assistant.io/dashboards/horizontal-stack/
|
||||
* @see https://www.home-assistant.io/dashboards/vertical-stack/
|
||||
*/
|
||||
export interface StackCardConfig extends LovelaceCardConfig {
|
||||
type: string;
|
||||
cards: LovelaceCardConfig[];
|
||||
title?: string;
|
||||
}
|
||||
|
@@ -1,35 +1,15 @@
|
||||
/**
|
||||
* Represents the layout options for Lovelace in Home Assistant.
|
||||
*
|
||||
* @property {number | "full"} [grid_columns] - The number of grid columns or "full".
|
||||
* @property {number | "auto"} [grid_rows] - The number of grid rows or "auto".
|
||||
* @property {number} [grid_max_columns] - The maximum number of grid columns.
|
||||
* @property {number} [grid_min_columns] - The minimum number of grid columns.
|
||||
* @property {number} [grid_min_rows] - The minimum number of grid rows.
|
||||
* @property {number} [grid_max_rows] - The maximum number of grid rows.
|
||||
*/
|
||||
export interface LovelaceLayoutOptions {
|
||||
grid_columns?: number | 'full';
|
||||
grid_rows?: number | 'auto';
|
||||
grid_columns?: number | "full";
|
||||
grid_rows?: number | "auto";
|
||||
grid_max_columns?: number;
|
||||
grid_min_columns?: number;
|
||||
grid_min_rows?: number;
|
||||
grid_max_rows?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the grid options for Lovelace in Home Assistant.
|
||||
*
|
||||
* @property {number | "full"} [columns] - The number of columns or "full".
|
||||
* @property {number | "auto"} [rows] - The number of rows or "auto".
|
||||
* @property {number} [max_columns] - The maximum number of columns.
|
||||
* @property {number} [min_columns] - The minimum number of columns.
|
||||
* @property {number} [min_rows] - The minimum number of rows.
|
||||
* @property {number} [max_rows] - The maximum number of rows.
|
||||
*/
|
||||
export interface LovelaceGridOptions {
|
||||
columns?: number | 'full';
|
||||
rows?: number | 'auto';
|
||||
columns?: number | "full";
|
||||
rows?: number | "auto";
|
||||
max_columns?: number;
|
||||
min_columns?: number;
|
||||
min_rows?: number;
|
||||
|
@@ -7,50 +7,27 @@ import {
|
||||
HassServices,
|
||||
HassServiceTarget,
|
||||
MessageBase,
|
||||
} from 'home-assistant-js-websocket';
|
||||
import { LocalizeFunc } from './common/translations/localize';
|
||||
import { AreaRegistryEntry } from './data/area_registry';
|
||||
import { DeviceRegistryEntry } from './data/device_registry';
|
||||
import { EntityRegistryDisplayEntry } from './data/entity_registry';
|
||||
import { FloorRegistryEntry } from './data/floor_registry';
|
||||
import { CoreFrontendUserData } from './data/frontend';
|
||||
import { FrontendLocaleData, getHassTranslations } from './data/translations';
|
||||
import { Themes } from './data/ws-themes';
|
||||
} from "home-assistant-js-websocket";
|
||||
import {AreaRegistryEntry} from "./data/area_registry";
|
||||
import {DeviceRegistryEntry} from "./data/device_registry";
|
||||
import {EntityRegistryDisplayEntry} from "./data/entity_registry";
|
||||
import {FloorRegistryEntry} from "./data/floor_registry";
|
||||
import {Themes} from "./data/ws-themes";
|
||||
import {FrontendLocaleData, getHassTranslations} from "./data/translations";
|
||||
import {LocalizeFunc} from "./common/translations/localize";
|
||||
import {CoreFrontendUserData} from "./data/frontend";
|
||||
|
||||
/**
|
||||
* Represents the credentials for a user in Home Assistant.
|
||||
*
|
||||
* @property {string} auth_provider_type - The type of authentication provider.
|
||||
* @property {string} auth_provider_id - The ID of the authentication provider.
|
||||
*/
|
||||
export interface Credential {
|
||||
auth_provider_type: string;
|
||||
auth_provider_id: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a multifactor authentication module in Home Assistant.
|
||||
*
|
||||
* @property {string} id - The unique identifier for the MFA module.
|
||||
* @property {string} name - The name of the MFA module.
|
||||
* @property {boolean} enabled - Whether the MFA module is enabled.
|
||||
*/
|
||||
export interface MFAModule {
|
||||
id: string;
|
||||
name: string;
|
||||
enabled: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the current user in Home Assistant.
|
||||
*
|
||||
* @property {string} id - The unique identifier for the user.
|
||||
* @property {boolean} is_owner - Indicates if the user is an owner.
|
||||
* @property {boolean} is_admin - Indicates if the user is an admin.
|
||||
* @property {string} name - The name of the user.
|
||||
* @property {Credential[]} credentials - The credentials associated with the user.
|
||||
* @property {MFAModule[]} mfa_modules - The MFA modules associated with the user.
|
||||
*/
|
||||
export interface CurrentUser {
|
||||
id: string;
|
||||
is_owner: boolean;
|
||||
@@ -60,18 +37,6 @@ export interface CurrentUser {
|
||||
mfa_modules: MFAModule[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents information about a panel in Home Assistant.
|
||||
*
|
||||
* @template T The type of the configuration object for the panel.
|
||||
*
|
||||
* @property {string} component_name - The name of the component for the panel.
|
||||
* @property {T} config - The configuration for the panel.
|
||||
* @property {string | null} icon - The icon for the panel.
|
||||
* @property {string | null} title - The title of the panel.
|
||||
* @property {string} url_path - The URL path for the panel.
|
||||
* @property {string} [config_panel_domain] - The domain for the configuration panel.
|
||||
*/
|
||||
export interface PanelInfo<T = Record<string, any> | null> {
|
||||
component_name: string;
|
||||
config: T;
|
||||
@@ -81,108 +46,39 @@ export interface PanelInfo<T = Record<string, any> | null> {
|
||||
config_panel_domain?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the panels in Home Assistant.
|
||||
*
|
||||
* @property {Record<string, PanelInfo>} panels - The panel configurations.
|
||||
*/
|
||||
export interface Panels {
|
||||
panels: Record<string, PanelInfo>;
|
||||
}
|
||||
export type Panels = Record<string, PanelInfo>;
|
||||
|
||||
/**
|
||||
* Represents a translation in Home Assistant.
|
||||
*
|
||||
* @property {string} nativeName - The native name of the language.
|
||||
* @property {boolean} isRTL - Indicates if the language is written right-to-left.
|
||||
* @property {string} hash - The hash for the translation.
|
||||
*/
|
||||
export interface Translation {
|
||||
nativeName: string;
|
||||
isRTL: boolean;
|
||||
hash: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents metadata for translations in Home Assistant.
|
||||
*
|
||||
* @property {string[]} fragments - The fragments of the translation.
|
||||
* @property {Record<string, Translation>} translations - The translations mapped by language.
|
||||
*/
|
||||
export interface TranslationMetadata {
|
||||
fragments: string[];
|
||||
translations: Record<string, Translation>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a dictionary of translations in Home Assistant.
|
||||
*
|
||||
* @property {Record<string, string>} translations - The translations mapped by a key.
|
||||
*/
|
||||
export interface TranslationDict {
|
||||
translations: Record<string, string>;
|
||||
}
|
||||
export type TranslationDict = {[key: string]: string};
|
||||
|
||||
/**
|
||||
* Represents resources in Home Assistant.
|
||||
*
|
||||
* @property {Record<string, Record<string, string>>} resources - The resources mapped by a key.
|
||||
*/
|
||||
export interface Resources {
|
||||
resources: Record<string, Record<string, string>>;
|
||||
}
|
||||
export type Resources = Record<string, Record<string, string>>;
|
||||
|
||||
/**
|
||||
* Represents the settings for themes in Home Assistant.
|
||||
*
|
||||
* @property {string} theme - The name of the selected theme.
|
||||
* @property {boolean} [dark] - Indicates if the theme is dark.
|
||||
* @property {string} [primaryColor] - The primary color of the theme.
|
||||
* @property {string} [accentColor] - The accent color of the theme.
|
||||
*/
|
||||
// Currently selected theme and its settings. These are the values stored in local storage.
|
||||
// Note: These values are not meant to be used at runtime to check whether dark mode is active
|
||||
// or which theme name to use, as this interface represents the config data for the theme picker.
|
||||
// The actually active dark mode and theme name can be read from hass.themes.
|
||||
export interface ThemeSettings {
|
||||
theme: string;
|
||||
// Radio box selection for theme picker. Do not use in Lovelace rendering as
|
||||
// it can be undefined == auto.
|
||||
// Property hass.themes.darkMode carries effective current mode.
|
||||
dark?: boolean;
|
||||
primaryColor?: string;
|
||||
accentColor?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the main Home Assistant object.
|
||||
*
|
||||
* @interface HomeAssistant
|
||||
* @property {Auth} auth - The authentication object.
|
||||
* @property {Connection} connection - The connection object.
|
||||
* @property {boolean} connected - Indicates if the connection is active.
|
||||
* @property {HassEntities} states - The current states of entities.
|
||||
* @property {Record<string, EntityRegistryDisplayEntry>} entities - The entities in the registry.
|
||||
* @property {Record<string, DeviceRegistryEntry>} devices - The devices in the registry.
|
||||
* @property {Record<string, AreaRegistryEntry>} areas - The areas in the registry.
|
||||
* @property {Record<string, FloorRegistryEntry>} floors - The floors in the registry.
|
||||
* @property {HassServices} services - The services available in Home Assistant.
|
||||
* @property {HassConfig} config - The configuration for Home Assistant.
|
||||
* @property {Themes} themes - The available themes.
|
||||
* @property {ThemeSettings | null} selectedTheme - The currently selected theme.
|
||||
* @property {Panels} panels - The panel configurations.
|
||||
* @property {string} panelUrl - The URL for the panel.
|
||||
* @property {string} language - The current language.
|
||||
* @property {string | null} selectedLanguage - The selected language.
|
||||
* @property {FrontendLocaleData} locale - The locale data.
|
||||
* @property {Resources} resources - The resources available.
|
||||
* @property {LocalizeFunc} localize - The localization function.
|
||||
* @property {TranslationMetadata} translationMetadata - The translation metadata.
|
||||
* @property {boolean} suspendWhenHidden - Indicates if the frontend should suspend when hidden.
|
||||
* @property {boolean} enableShortcuts - Indicates if shortcuts are enabled.
|
||||
* @property {boolean} vibrate - Indicates if vibration feedback is enabled.
|
||||
* @property {boolean} debugConnection - Indicates if debug mode is enabled for the connection.
|
||||
* @property {'docked' | 'always_hidden' | 'auto'} dockedSidebar - The sidebar visibility setting.
|
||||
* @property {string} defaultPanel - The default panel to show.
|
||||
* @property {string | null} moreInfoEntityId - The entity ID for more info.
|
||||
* @property {CurrentUser} [user] - The current user object.
|
||||
* @property {CoreFrontendUserData | null} [userData] - The frontend user data.
|
||||
*/
|
||||
export interface HomeAssistant {
|
||||
auth: Auth & { external?: { [key: string]: any } };
|
||||
auth: Auth & { external?: {[key: string]: any;} };
|
||||
connection: Connection;
|
||||
connected: boolean;
|
||||
states: HassEntities;
|
||||
@@ -196,7 +92,14 @@ export interface HomeAssistant {
|
||||
selectedTheme: ThemeSettings | null;
|
||||
panels: Panels;
|
||||
panelUrl: string;
|
||||
// i18n
|
||||
// current effective language in that order:
|
||||
// - backend saved user selected language
|
||||
// - language in local app storage
|
||||
// - browser language
|
||||
// - english (en)
|
||||
language: string;
|
||||
// local stored language, keep that name for backward compatibility
|
||||
selectedLanguage: string | null;
|
||||
locale: FrontendLocaleData;
|
||||
resources: Resources;
|
||||
@@ -206,166 +109,57 @@ export interface HomeAssistant {
|
||||
enableShortcuts: boolean;
|
||||
vibrate: boolean;
|
||||
debugConnection: boolean;
|
||||
dockedSidebar: 'docked' | 'always_hidden' | 'auto';
|
||||
dockedSidebar: "docked" | "always_hidden" | "auto";
|
||||
defaultPanel: string;
|
||||
moreInfoEntityId: string | null;
|
||||
user?: CurrentUser;
|
||||
userData?: CoreFrontendUserData | null;
|
||||
|
||||
/**
|
||||
* Returns the URL for the Home Assistant instance.
|
||||
*
|
||||
* @param {any} path - Optional path to append to the base URL.
|
||||
*/
|
||||
hassUrl(path?: any): string;
|
||||
|
||||
/**
|
||||
* Calls a service in Home Assistant.
|
||||
*
|
||||
* @param {ServiceCallRequest['domain']} domain - The domain of the service.
|
||||
* @param {ServiceCallRequest['service']} service - The name of the service to call.
|
||||
* @param {ServiceCallRequest['serviceData']} [serviceData] - Optional data to send with the service call.
|
||||
* @param {ServiceCallRequest['target']} [target] - Optional target for the service call.
|
||||
* @param {boolean} [notifyOnError] - Whether to notify on error.
|
||||
* @param {boolean} [returnResponse] - Whether to return the response.
|
||||
*/
|
||||
callService(
|
||||
domain: ServiceCallRequest['domain'],
|
||||
service: ServiceCallRequest['service'],
|
||||
serviceData?: ServiceCallRequest['serviceData'],
|
||||
target?: ServiceCallRequest['target'],
|
||||
domain: ServiceCallRequest["domain"],
|
||||
service: ServiceCallRequest["service"],
|
||||
serviceData?: ServiceCallRequest["serviceData"],
|
||||
target?: ServiceCallRequest["target"],
|
||||
notifyOnError?: boolean,
|
||||
returnResponse?: boolean,
|
||||
returnResponse?: boolean
|
||||
): Promise<ServiceCallResponse>;
|
||||
|
||||
/**
|
||||
* Calls the Home Assistant API.
|
||||
*
|
||||
* @template T The expected response type.
|
||||
*
|
||||
* @param {'GET' | 'POST' | 'PUT' | 'DELETE'} method - The HTTP method to use.
|
||||
* @param {string} path - The API endpoint path.
|
||||
* @param {Record<string, any>} [parameters] - Optional parameters to send with the request.
|
||||
* @param {Record<string, string>} [headers] - Optional headers to include in the request.
|
||||
*/
|
||||
callApi<T>(
|
||||
method: 'GET' | 'POST' | 'PUT' | 'DELETE',
|
||||
method: "GET" | "POST" | "PUT" | "DELETE",
|
||||
path: string,
|
||||
parameters?: Record<string, any>,
|
||||
headers?: Record<string, string>,
|
||||
headers?: Record<string, string>
|
||||
): Promise<T>;
|
||||
|
||||
/**
|
||||
* Calls the Home Assistant API with raw response.
|
||||
*
|
||||
* @param {'GET' | 'POST' | 'PUT' | 'DELETE'} method - The HTTP method to use.
|
||||
* @param {string} path - The API endpoint path.
|
||||
* @param {Record<string, any>} [parameters] - Optional parameters to send with the request.
|
||||
* @param {Record<string, string>} [headers] - Optional headers to include in the request.
|
||||
* @param {AbortSignal} [signal] - Optional signal to abort the request.
|
||||
*/
|
||||
callApiRaw(
|
||||
method: 'GET' | 'POST' | 'PUT' | 'DELETE',
|
||||
callApiRaw( // introduced in 2024.11
|
||||
method: "GET" | "POST" | "PUT" | "DELETE",
|
||||
path: string,
|
||||
parameters?: Record<string, any>,
|
||||
headers?: Record<string, string>,
|
||||
signal?: AbortSignal,
|
||||
signal?: AbortSignal
|
||||
): Promise<Response>;
|
||||
|
||||
/**
|
||||
* Fetches a resource with authentication.
|
||||
*
|
||||
* @param {string} path - The resource path to fetch.
|
||||
* @param {Record<string, any>} [init] - Optional fetch options.
|
||||
*/
|
||||
fetchWithAuth(path: string, init?: Record<string, any>): Promise<Response>;
|
||||
|
||||
/**
|
||||
* Sends a WebSocket message.
|
||||
*
|
||||
* @param {MessageBase} msg - The message to send.
|
||||
*/
|
||||
sendWS(msg: MessageBase): void;
|
||||
|
||||
/**
|
||||
* Calls a WebSocket service.
|
||||
*
|
||||
* @template T The expected response type.
|
||||
*
|
||||
* @param {MessageBase} msg - The message to send.
|
||||
*/
|
||||
callWS<T>(msg: MessageBase): Promise<T>;
|
||||
|
||||
/**
|
||||
* Load backend translation.
|
||||
*
|
||||
* @param {Parameters<typeof getHassTranslations>[2]} category - The category of translations.
|
||||
* @param {Parameters<typeof getHassTranslations>[3]} [integrations] - Optional integrations to include.
|
||||
* @param {Parameters<typeof getHassTranslations>[4]} [configFlow] - Optional config flow.
|
||||
*
|
||||
* @returns {Promise<LocalizeFunc>} The localization function.
|
||||
*/
|
||||
loadBackendTranslation(
|
||||
category: Parameters<typeof getHassTranslations>[2],
|
||||
integrations?: Parameters<typeof getHassTranslations>[3],
|
||||
configFlow?: Parameters<typeof getHassTranslations>[4],
|
||||
configFlow?: Parameters<typeof getHassTranslations>[4]
|
||||
): Promise<LocalizeFunc>;
|
||||
|
||||
/**
|
||||
* Load fragment translation.
|
||||
*
|
||||
* @param {string} fragment - The fragment to load.
|
||||
* @returns {Promise<LocalizeFunc | undefined>} The localization function or undefined.
|
||||
*/
|
||||
loadFragmentTranslation(fragment: string): Promise<LocalizeFunc | undefined>;
|
||||
|
||||
/**
|
||||
* Formats the state of an entity.
|
||||
*
|
||||
* @param {HassEntity} stateObj - The state object of the entity.
|
||||
* @param {string} [state] - Optional state to format.
|
||||
*/
|
||||
formatEntityState(stateObj: HassEntity, state?: string): string;
|
||||
|
||||
/**
|
||||
* Formats the value of an entity attribute.
|
||||
*
|
||||
* @param {HassEntity} stateObj - The state object of the entity.
|
||||
* @param {string} attribute - The attribute to format.
|
||||
* @param {any} [value] - Optional value to format.
|
||||
*/
|
||||
formatEntityAttributeValue(stateObj: HassEntity, attribute: string, value?: any): string;
|
||||
|
||||
/**
|
||||
* Formats the name of an entity attribute.
|
||||
*
|
||||
* @param {HassEntity} stateObj - The state object of the entity.
|
||||
* @param {string} attribute - The attribute to format.
|
||||
*/
|
||||
formatEntityAttributeValue(
|
||||
stateObj: HassEntity,
|
||||
attribute: string,
|
||||
value?: any
|
||||
): string;
|
||||
formatEntityAttributeName(stateObj: HassEntity, attribute: string): string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the context of a service call.
|
||||
*
|
||||
* @property {string} id - The unique identifier for the context.
|
||||
* @property {string} [parent_id] - The optional parent ID of the context.
|
||||
* @property {string | null} [user_id] - The optional user ID associated with the context.
|
||||
*/
|
||||
export interface Context {
|
||||
id: string;
|
||||
parent_id?: string;
|
||||
user_id?: string | null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a service call request in Home Assistant.
|
||||
*
|
||||
* @property {string} domain - The domain of the service to call.
|
||||
* @property {string} service - The name of the service to call.
|
||||
* @property {Record<string, any>} [serviceData] - Optional data to send with the service call.
|
||||
* @property {HassServiceTarget} [target] - Optional target for the service call.
|
||||
*/
|
||||
export interface ServiceCallRequest {
|
||||
domain: string;
|
||||
service: string;
|
||||
@@ -373,12 +167,6 @@ export interface ServiceCallRequest {
|
||||
target?: HassServiceTarget;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the response from a service call in Home Assistant.
|
||||
*
|
||||
* @property {Context} context - The context of the service call.
|
||||
* @property {any} [response] - The optional response data from the service call.
|
||||
*/
|
||||
export interface ServiceCallResponse {
|
||||
context: Context;
|
||||
response?: any;
|
||||
|
@@ -1,11 +1,11 @@
|
||||
import { LovelaceCardConfig } from '../../homeassistant/data/lovelace/config/card';
|
||||
import { LovelaceChipConfig } from '../utils/lovelace/chip/types';
|
||||
import {LovelaceCardConfig} from "../../homeassistant/data/lovelace";
|
||||
import {LovelaceChipConfig} from "../utils/lovelace/chip/types";
|
||||
|
||||
/**
|
||||
* Chips Card Configuration
|
||||
*
|
||||
* @property {LovelaceChipConfig[]} chips - Array of chips to display.
|
||||
* @property {string} [alignment] - Chips alignment (start, end, center, justify). Defaults to 'start'.
|
||||
* @param {LovelaceChipConfig[]} chips Chips Array
|
||||
* @param {string} [alignment=start] Chips alignment (start,end, center, justify), when empty default behavior is start.
|
||||
*
|
||||
* @see https://github.com/piitaya/lovelace-mushroom/blob/main/docs/cards/chips.md
|
||||
*/
|
||||
|
@@ -1,15 +1,16 @@
|
||||
import { LovelaceCardConfig } from '../../homeassistant/data/lovelace/config/card';
|
||||
import { ActionsSharedConfig } from '../shared/config/actions-config';
|
||||
import { AppearanceSharedConfig } from '../shared/config/appearance-config';
|
||||
import { EntitySharedConfig } from '../shared/config/entity-config';
|
||||
import {ActionsSharedConfig} from "../shared/config/actions-config";
|
||||
import {LovelaceCardConfig} from "../../homeassistant/data/lovelace";
|
||||
import {EntitySharedConfig} from "../shared/config/entity-config";
|
||||
import {AppearanceSharedConfig} from "../shared/config/appearance-config";
|
||||
|
||||
/**
|
||||
* Fan Card Configuration
|
||||
* Fan Card Config.
|
||||
*
|
||||
* @property {boolean} [icon_animation] - Animate the icon when the fan is on. Defaults to false.
|
||||
* @property {boolean} [show_percentage_control] - Show a slider to control speed. Defaults to false.
|
||||
* @property {boolean} [show_oscillate_control] - Show a button to control oscillation. Defaults to false.
|
||||
* @property {boolean} [show_direction_control] - Show a button to control the direction. Defaults to false.
|
||||
* @property {boolean} [icon_animation=false] Animate the icon when fan is on.
|
||||
* @property {boolean} [show_percentage_control=false] Show a slider to control speed.
|
||||
* @property {boolean} [show_oscillate_control=false] Show a button to control oscillation.
|
||||
* @property {boolean} [show_direction_control=false] Show a button to control the direction.
|
||||
* @property {boolean} [icon_animation=false] Animate the icon when fan is on.
|
||||
*
|
||||
* @see https://github.com/piitaya/lovelace-mushroom/blob/main/docs/cards/fan.md
|
||||
*/
|
||||
@@ -21,4 +22,5 @@ export type FanCardConfig = LovelaceCardConfig &
|
||||
show_percentage_control?: boolean;
|
||||
show_oscillate_control?: boolean;
|
||||
show_direction_control?: boolean;
|
||||
collapsible_controls?: boolean;
|
||||
};
|
||||
|
72
src/types/strategy/cards.ts
Normal file
72
src/types/strategy/cards.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
import {LovelaceCardConfig} from "../homeassistant/data/lovelace";
|
||||
import {TitleCardConfig} from "../lovelace-mushroom/cards/title-card-config";
|
||||
import {EntitySharedConfig} from "../lovelace-mushroom/shared/config/entity-config";
|
||||
import {AppearanceSharedConfig} from "../lovelace-mushroom/shared/config/appearance-config";
|
||||
import {ActionsSharedConfig} from "../lovelace-mushroom/shared/config/actions-config";
|
||||
import {TemplateCardConfig} from "../lovelace-mushroom/cards/template-card-config";
|
||||
import {EntityCardConfig} from "../lovelace-mushroom/cards/entity-card-config";
|
||||
import {AreaCardConfig, PictureEntityCardConfig} from "../homeassistant/panels/lovelace/cards/types";
|
||||
import {ClimateCardConfig} from "../lovelace-mushroom/cards/climate-card-config";
|
||||
import {CoverCardConfig} from "../lovelace-mushroom/cards/cover-card-config";
|
||||
import {FanCardConfig} from "../lovelace-mushroom/cards/fan-card-config";
|
||||
import {LightCardConfig} from "../lovelace-mushroom/cards/light-card-config";
|
||||
import {LockCardConfig} from "../lovelace-mushroom/cards/lock-card-config";
|
||||
import {MediaPlayerCardConfig} from "../lovelace-mushroom/cards/media-player-card-config";
|
||||
import {NumberCardConfig} from "../lovelace-mushroom/cards/number-card-config";
|
||||
import {PersonCardConfig} from "../lovelace-mushroom/cards/person-card-config";
|
||||
import {VacuumCardConfig} from "../lovelace-mushroom/cards/vacuum-card-config";
|
||||
import {SelectCardConfig} from '../lovelace-mushroom/cards/select-card-config';
|
||||
|
||||
export namespace cards {
|
||||
/**
|
||||
* Abstract Card Config.
|
||||
*/
|
||||
export type AbstractCardConfig = LovelaceCardConfig &
|
||||
EntitySharedConfig &
|
||||
AppearanceSharedConfig &
|
||||
ActionsSharedConfig;
|
||||
|
||||
/**
|
||||
* Controller Card Config.
|
||||
*
|
||||
* @property {boolean} [showControls=true] False to hide controls.
|
||||
* @property {string} [iconOn] Icon to show for switching entities from off state.
|
||||
* @property {string} [iconOff] Icon to show for switching entities to off state.
|
||||
* @property {string} [onService=none] Service to call for switching entities from off state.
|
||||
* @property {string} [offService=none] Service to call for switching entities to off state.
|
||||
*/
|
||||
export interface ControllerCardConfig extends TitleCardConfig {
|
||||
type: "mushroom-title-card",
|
||||
showControls?: boolean;
|
||||
iconOn?: string;
|
||||
iconOff?: string;
|
||||
onService?: string;
|
||||
offService?: string;
|
||||
}
|
||||
|
||||
export type AreaCardOptions = Omit<AreaCardConfig, "type">;
|
||||
export type ClimateCardOptions = Omit<ClimateCardConfig, "type">;
|
||||
export type ControllerCardOptions = Omit<ControllerCardConfig, "type">;
|
||||
export type CoverCardOptions = Omit<CoverCardConfig, "type">;
|
||||
export type EntityCardOptions = Omit<EntityCardConfig, "type">;
|
||||
export type FanCardOptions = Omit<FanCardConfig, "type">;
|
||||
export type LightCardOptions = Omit<LightCardConfig, "type">;
|
||||
export type LockCardOptions = Omit<LockCardConfig, "type">;
|
||||
export type MediaPlayerCardOptions = Omit<MediaPlayerCardConfig, "type">;
|
||||
export type NumberCardOptions = Omit<NumberCardConfig, "type">;
|
||||
export type PersonCardOptions = Omit<PersonCardConfig, "type">;
|
||||
export type PictureEntityCardOptions = Omit<PictureEntityCardConfig, "type">;
|
||||
export type TemplateCardOptions = Omit<TemplateCardConfig, "type">;
|
||||
export type VacuumCardOptions = Omit<VacuumCardConfig, "type">;
|
||||
export type SelectCardOptions = Omit<SelectCardConfig, "type">;
|
||||
export type InputSelectCardOptions = Omit<SelectCardConfig, "type">;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
367
src/types/strategy/generic.ts
Normal file
367
src/types/strategy/generic.ts
Normal file
@@ -0,0 +1,367 @@
|
||||
import {CallServiceActionConfig, LovelaceCardConfig,} from "../homeassistant/data/lovelace";
|
||||
import {HomeAssistant} from "../homeassistant/types";
|
||||
import {AreaRegistryEntry} from "../homeassistant/data/area_registry";
|
||||
import {cards} from "./cards";
|
||||
import {EntityRegistryEntry} from "../homeassistant/data/entity_registry";
|
||||
import {LovelaceChipConfig} from "../lovelace-mushroom/utils/lovelace/chip/types";
|
||||
import {HassServiceTarget} from "home-assistant-js-websocket";
|
||||
import {LovelaceViewConfig, LovelaceViewRawConfig} from "../homeassistant/data/lovelace/config/view";
|
||||
import {LovelaceConfig} from "../homeassistant/data/lovelace/config/types";
|
||||
|
||||
/**
|
||||
* List of supported domains.
|
||||
*
|
||||
* This constant array defines the domains that are supported by the strategy.
|
||||
* Each domain represents a specific type of entity within the Home Assistant ecosystem.
|
||||
*
|
||||
* _ refers to all domains.
|
||||
* default refers to the miscellanea domain.
|
||||
*
|
||||
* @readonly
|
||||
* @constant
|
||||
*/
|
||||
const SUPPORTED_DOMAINS = [
|
||||
"_",
|
||||
"binary_sensor",
|
||||
"camera",
|
||||
"climate",
|
||||
"cover",
|
||||
"default",
|
||||
"fan",
|
||||
"input_select",
|
||||
"light",
|
||||
"lock",
|
||||
"media_player",
|
||||
"number",
|
||||
"scene",
|
||||
"select",
|
||||
"sensor",
|
||||
"switch",
|
||||
"vacuum",
|
||||
] as const;
|
||||
|
||||
/**
|
||||
* List of supported views.
|
||||
*
|
||||
* This constant array defines the views that are supported by the strategy.
|
||||
*
|
||||
* @readonly
|
||||
* @constant
|
||||
*/
|
||||
const SUPPORTED_VIEWS = [
|
||||
"camera",
|
||||
"climate",
|
||||
"cover",
|
||||
"fan",
|
||||
"home",
|
||||
"light",
|
||||
"scene",
|
||||
"switch",
|
||||
"vacuum",
|
||||
] as const;
|
||||
|
||||
const SUPPORTED_CHIPS = [
|
||||
"light",
|
||||
"fan",
|
||||
"cover",
|
||||
"switch",
|
||||
"climate",
|
||||
"weather",
|
||||
] as const;
|
||||
|
||||
/**
|
||||
* List of home view sections.
|
||||
*
|
||||
* This constant array defines the sections that are present in the home view.
|
||||
*
|
||||
* @readonly
|
||||
* @constant
|
||||
*/
|
||||
const HOME_VIEW_SECTIONS = [
|
||||
"areas",
|
||||
"areasTitle",
|
||||
"chips",
|
||||
"greeting",
|
||||
"persons",
|
||||
] as const;
|
||||
|
||||
export namespace generic {
|
||||
export type SupportedDomains = typeof SUPPORTED_DOMAINS[number];
|
||||
export type SupportedViews = typeof SUPPORTED_VIEWS[number];
|
||||
export type SupportedChips = typeof SUPPORTED_CHIPS[number];
|
||||
export type HomeViewSections = typeof HOME_VIEW_SECTIONS[number];
|
||||
|
||||
/**
|
||||
* An entry of a Home Assistant Register.
|
||||
*/
|
||||
export type RegistryEntry =
|
||||
| AreaRegistryEntry
|
||||
| DataTransfer
|
||||
| EntityRegistryEntry
|
||||
|
||||
/**
|
||||
* View Configuration of the strategy.
|
||||
*
|
||||
* @interface StrategyViewConfig
|
||||
* @extends LovelaceViewConfig
|
||||
*
|
||||
* @property {boolean} [hidden] If True, the view is hidden from the dashboard.
|
||||
* @property {number} [order] Ordering position of the views at the top of the dashboard.
|
||||
*/
|
||||
export interface StrategyViewConfig extends LovelaceViewConfig {
|
||||
hidden: boolean;
|
||||
order: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* All Domains Configuration.
|
||||
*
|
||||
* @interface AllDomainsConfig
|
||||
*
|
||||
* @property {boolean} [hide_config_entities] If True, all configuration entities are hidden from the dashboard.
|
||||
* @property {boolean} [hide_diagnostic_entities] If True, all diagnostic entities are hidden from the dashboard.
|
||||
*/
|
||||
export interface AllDomainsConfig {
|
||||
hide_config_entities: boolean;
|
||||
hide_diagnostic_entities: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Single Domain Configuration.
|
||||
*
|
||||
* @interface SingleDomainConfig
|
||||
* @extends Partial<cards.ControllerCardConfig>
|
||||
*
|
||||
* @property {boolean} [hidden] If True, all entities of the domain are hidden from the dashboard.
|
||||
* @property {number} [order] Ordering position of the domains in a views.
|
||||
*/
|
||||
export interface SingleDomainConfig extends Partial<cards.ControllerCardConfig> {
|
||||
hidden: boolean;
|
||||
order?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dashboard Info Object.
|
||||
*
|
||||
* Home Assistant passes this object to the Dashboard Generator method.
|
||||
*
|
||||
* @interface DashboardInfo
|
||||
*
|
||||
* @property {LovelaceConfig} config Dashboard configuration.
|
||||
* @property {HomeAssistant} hass The Home Assistant object.
|
||||
*
|
||||
* @see https://developers.home-assistant.io/docs/frontend/custom-ui/custom-strategy/#dashboard-strategies
|
||||
*/
|
||||
export interface DashboardInfo {
|
||||
config: LovelaceViewRawConfig & {
|
||||
strategy: {
|
||||
options?: StrategyConfig & { area: StrategyArea }
|
||||
}
|
||||
};
|
||||
hass: HomeAssistant;
|
||||
}
|
||||
|
||||
/**
|
||||
* View Info Object.
|
||||
*
|
||||
* Home Assistant passes this object to the View Generator method.
|
||||
*
|
||||
* @interface ViewInfo
|
||||
*
|
||||
* @property {LovelaceConfig} config Dashboard configuration.
|
||||
* @property {HomeAssistant} hass The Home Assistant object.
|
||||
* @property {LovelaceViewConfig} view View configuration.
|
||||
*
|
||||
* @see https://developers.home-assistant.io/docs/frontend/custom-ui/custom-strategy/#view-strategies
|
||||
*/
|
||||
export interface ViewInfo {
|
||||
config: LovelaceConfig;
|
||||
hass: HomeAssistant;
|
||||
view: LovelaceViewRawConfig & {
|
||||
strategy: {
|
||||
options?: StrategyConfig & { area: StrategyArea }
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Strategy Configuration.
|
||||
*
|
||||
* @interface StrategyConfig
|
||||
*
|
||||
* @property {Object.<string, StrategyArea>} areas List of areas.
|
||||
* @property {Object.<string, CustomCardConfig>} card_options Card options for entities.
|
||||
* @property {Partial<ChipConfiguration>} chips The configuration of chips in the Home view.
|
||||
* @property {boolean} debug If True, the strategy outputs more verbose debug information in the console.
|
||||
* @property {Object.<string, AllDomainsConfig | SingleDomainConfig>} domains List of domains.
|
||||
* @property {LovelaceCardConfig[]} extra_cards List of cards to show below room cards.
|
||||
* @property {StrategyViewConfig[]} extra_views List of custom-defined views to add to the dashboard.
|
||||
* @property {{ hidden: HomeViewSections[] | [] }} home_view List of views to add to the dashboard.
|
||||
* @property {Object.<hidden, StrategyViewConfig>} views The configurations of views.
|
||||
* @property {LovelaceCardConfig[]} quick_access_cards List of custom-defined cards to show between the welcome card
|
||||
* and rooms cards.
|
||||
*/
|
||||
export interface StrategyConfig {
|
||||
areas: { [S: string]: StrategyArea };
|
||||
card_options: { [S: string]: CustomCardConfig };
|
||||
chips: Partial<ChipConfiguration>;
|
||||
debug: boolean;
|
||||
domains: { [K in SupportedDomains]: K extends "_" ? AllDomainsConfig : SingleDomainConfig; };
|
||||
extra_cards: LovelaceCardConfig[];
|
||||
extra_views: StrategyViewConfig[];
|
||||
home_view: {
|
||||
hidden: HomeViewSections[] | [];
|
||||
}
|
||||
views: Record<SupportedViews, StrategyViewConfig>;
|
||||
quick_access_cards: LovelaceCardConfig[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the default configuration for a strategy.
|
||||
*
|
||||
* @interface StrategyDefaults
|
||||
*/
|
||||
export interface StrategyDefaults extends StrategyConfig {
|
||||
areas: { "undisclosed": StrategyArea } & { [S: string]: StrategyArea };
|
||||
}
|
||||
|
||||
/**
|
||||
* Strategy Area.
|
||||
*
|
||||
* @interface StrategyArea
|
||||
*
|
||||
* @property {boolean} [hidden] True if the entity should be hidden from the dashboard.
|
||||
* @property {object[]} [extra_cards] An array of card configurations.
|
||||
* The configured cards are added to the dashboard.
|
||||
* @property {number} [order] Ordering position of the area in the list of available areas.
|
||||
* @property {string} [type] The type of area card.
|
||||
*/
|
||||
export interface StrategyArea extends AreaRegistryEntry {
|
||||
extra_cards?: LovelaceCardConfig[];
|
||||
hidden?: boolean;
|
||||
order?: number;
|
||||
type?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* A list of chips to show in the Home view.
|
||||
*
|
||||
* @interface ChipConfiguration
|
||||
*
|
||||
* @property {boolean} climate_count Chip to display the number of climates which are not off.
|
||||
* @property {boolean} cover_count Chip to display the number of unclosed covers.
|
||||
* @property {boolean} fan_count Chip to display the number of fans on.
|
||||
* @property {boolean} light_count Chip to display the number of lights on.
|
||||
* @property {boolean} switch_count Chip to display the number of switches on.
|
||||
* @property {string} weather_entity Entity ID for the weather chip to use, accepts `weather.` only.
|
||||
* @property {object[]} extra_chips List of extra chips.
|
||||
*/
|
||||
export interface ChipConfiguration {
|
||||
climate_count: boolean;
|
||||
cover_count: boolean;
|
||||
extra_chips: LovelaceChipConfig[];
|
||||
fan_count: boolean;
|
||||
light_count: boolean;
|
||||
switch_count: boolean;
|
||||
weather_entity: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom Card Configuration for an entity.
|
||||
*
|
||||
* @interface CustomCardConfig
|
||||
* @extends LovelaceCardConfig
|
||||
*
|
||||
* @property {boolean} hidden If True, the card is hidden from the dashboard.
|
||||
*/
|
||||
export interface CustomCardConfig extends LovelaceCardConfig {
|
||||
hidden?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Area Filter Context.
|
||||
*
|
||||
* @interface AreaFilterContext
|
||||
*
|
||||
* @property {AreaRegistryEntry} area Area Entry.
|
||||
* @property {string[]} areaDeviceIds The id of devices which are linked to the area.
|
||||
* @property {string} [domain] Domain of an entity.
|
||||
* Example: `light`.
|
||||
*/
|
||||
export interface AreaFilterContext {
|
||||
area: AreaRegistryEntry;
|
||||
areaDeviceIds: string[];
|
||||
domain?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given object is an instance of CallServiceActionConfig.
|
||||
*
|
||||
* @param {any} obj - The object to be checked.
|
||||
* @returns {boolean} - Returns true if the object is an instance of CallServiceActionConfig, otherwise false.
|
||||
*/
|
||||
export function isCallServiceActionConfig(obj: any): obj is CallServiceActionConfig {
|
||||
return obj && obj.action === "call-service" && ["action", "service"].every(key => key in obj);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given object is an instance of HassServiceTarget.
|
||||
*
|
||||
* @param {any} obj - The object to check.
|
||||
* @returns {boolean} - True if the object is an instance of HassServiceTarget, false otherwise.
|
||||
*/
|
||||
export function isCallServiceActionTarget(obj: any): obj is HassServiceTarget {
|
||||
return obj && ["entity_id", "device_id", "area_id"].some(key => key in obj);
|
||||
}
|
||||
|
||||
interface SortableBase {
|
||||
order: number;
|
||||
}
|
||||
|
||||
type SortableWithTitle = SortableBase & { title: string; name?: never };
|
||||
type SortableWithName = SortableBase & { name: string; title?: never };
|
||||
|
||||
|
||||
/**
|
||||
* The union type of SortableWithTitle and SortableWithName.
|
||||
*
|
||||
* @remarks
|
||||
* This type is used to sort objects by title or by name.
|
||||
* The `order` property is used to sort the objects.
|
||||
* The `title` and `name` properties are used to display the object in the UI.
|
||||
*/
|
||||
export type Sortable = SortableWithTitle | SortableWithName;
|
||||
|
||||
|
||||
/**
|
||||
* Checks if the given object is of a sortable type.
|
||||
*
|
||||
* Sortable types are objects that have a `title` or a `name` property and an `order` property.
|
||||
*
|
||||
* @param {any} obj - The object to check.
|
||||
* @returns {boolean} - True if the object is an instance of Sortable, false otherwise.
|
||||
*/
|
||||
export function isSortable(obj: any): obj is Sortable {
|
||||
return obj && 'order' in obj && ('title' in obj || 'name' in obj);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given view id is a supported view.
|
||||
*
|
||||
* @param {string} id - The view id to check.
|
||||
* @returns {boolean} - Returns true if the view id is a supported view, otherwise false.
|
||||
*/
|
||||
export function isSupportedView(id: string): id is SupportedViews {
|
||||
return SUPPORTED_VIEWS.includes(id as SupportedViews);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given domain id is a supported domain.
|
||||
*
|
||||
* @param {string} id - The domain id to check.
|
||||
* @returns {boolean} - Returns true if the domain id is a supported domain, otherwise false.
|
||||
*/
|
||||
export function isSupportedDomain(id: string): id is SupportedDomains {
|
||||
return SUPPORTED_DOMAINS.includes(id as SupportedDomains);
|
||||
}
|
||||
}
|
17
src/types/strategy/views.ts
Normal file
17
src/types/strategy/views.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import {cards} from "./cards";
|
||||
import {LovelaceViewConfig} from "../homeassistant/data/lovelace/config/view";
|
||||
|
||||
export namespace views {
|
||||
/**
|
||||
* Options for the extended View class.
|
||||
*
|
||||
* @property {cards.ControllerCardConfig} [controllerCardOptions] Options for the Controller card.
|
||||
*/
|
||||
export interface ViewConfig extends LovelaceViewConfig {
|
||||
controllerCardOptions?: cards.ControllerCardOptions;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
64
src/utillties/filters.ts
Normal file
64
src/utillties/filters.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
import {EntityRegistryEntry} from "../types/homeassistant/data/entity_registry";
|
||||
import {Helper} from "../Helper";
|
||||
import {generic} from "../types/strategy/generic";
|
||||
import SupportedDomains = generic.SupportedDomains;
|
||||
|
||||
/**
|
||||
* Filter an array of entities by property/value pair
|
||||
*
|
||||
* @param entities The array of entities to filter.
|
||||
* @param property The property to filter on.
|
||||
* @param value The value to match.
|
||||
* @param exclude Whether to exclude entities with the given property/value pair (default: true).
|
||||
*
|
||||
* @returns A new list of entities filtered by the given property/value pair.
|
||||
*/
|
||||
export function filterEntitiesByPropertyValue(
|
||||
entities: EntityRegistryEntry[],
|
||||
property: keyof EntityRegistryEntry,
|
||||
value: any,
|
||||
exclude: boolean = true
|
||||
) {
|
||||
return entities.filter(entity => exclude ? entity[property] !== value : entity[property] === value);
|
||||
}
|
||||
|
||||
export function applyEntityCategoryFilters(entities: EntityRegistryEntry[], domain: SupportedDomains) {
|
||||
if (!Helper.isInitialized()) {
|
||||
throw new Error("The Helper module must be initialized before using this one.");
|
||||
}
|
||||
|
||||
const domainOptions = {
|
||||
...Helper.strategyOptions.domains["_"],
|
||||
...Helper.strategyOptions.domains[domain],
|
||||
};
|
||||
|
||||
let filteredEntityCategory = [];
|
||||
|
||||
if (domainOptions.hide_config_entities) {
|
||||
entities = filterEntitiesByPropertyValue(entities, "entity_category", "config");
|
||||
filteredEntityCategory.push("Config");
|
||||
}
|
||||
|
||||
if (domainOptions.hide_diagnostic_entities) {
|
||||
entities = filterEntitiesByPropertyValue(entities, "entity_category", "diagnostic");
|
||||
filteredEntityCategory.push("Diagnostic");
|
||||
}
|
||||
|
||||
if (Helper.debug && filteredEntityCategory.length > 0) {
|
||||
console.warn(filteredEntityCategory.join(" & ") + " entities are filtered out.");
|
||||
}
|
||||
|
||||
return entities;
|
||||
}
|
||||
|
||||
/*export function filterHiddenEntities(entities: EntityRegistryEntry[]) {
|
||||
entities = entities.filter(
|
||||
function (entity) {
|
||||
return entity.hidden_by === null // entity is not hidden by HASS settings.
|
||||
&& entity.disabled_by === null // entity is not disabled by HASS settings.
|
||||
&& Helper.strategyOptions.card_options.[entity.entity_id] // entity is not hidden by strategy options.
|
||||
}
|
||||
);
|
||||
}*/
|
||||
|
||||
|
@@ -1,155 +1,171 @@
|
||||
import { HassServiceTarget } from 'home-assistant-js-websocket';
|
||||
import HeaderCard from '../cards/HeaderCard';
|
||||
import { Registry } from '../Registry';
|
||||
import { LovelaceCardConfig } from '../types/homeassistant/data/lovelace/config/card';
|
||||
import { LovelaceViewConfig } from '../types/homeassistant/data/lovelace/config/view';
|
||||
import { StackCardConfig } from '../types/homeassistant/panels/lovelace/cards/types';
|
||||
import { AbstractCardConfig, CustomHeaderCardConfig, StrategyHeaderCardConfig } from '../types/strategy/strategy-cards';
|
||||
import { SupportedDomains } from '../types/strategy/strategy-generics';
|
||||
import { ViewConfig, ViewConstructor } from '../types/strategy/strategy-views';
|
||||
import { sanitizeClassName } from '../utilities/auxiliaries';
|
||||
import { logMessage, lvlFatal } from '../utilities/debug';
|
||||
import RegistryFilter from '../utilities/RegistryFilter';
|
||||
import {Helper} from "../Helper";
|
||||
import {ControllerCard} from "../cards/ControllerCard";
|
||||
import {LovelaceCardConfig} from "../types/homeassistant/data/lovelace";
|
||||
import {cards} from "../types/strategy/cards";
|
||||
import {TitleCardConfig} from "../types/lovelace-mushroom/cards/title-card-config";
|
||||
import {HassServiceTarget} from "home-assistant-js-websocket";
|
||||
import {applyEntityCategoryFilters} from "../utillties/filters";
|
||||
import {LovelaceViewConfig} from "../types/homeassistant/data/lovelace/config/view";
|
||||
import {StackCardConfig} from "../types/homeassistant/panels/lovelace/cards/types";
|
||||
import {generic} from "../types/strategy/generic";
|
||||
import abstractCardConfig = cards.AbstractCardConfig;
|
||||
import SupportedDomains = generic.SupportedDomains;
|
||||
|
||||
/**
|
||||
* Abstract View Class.
|
||||
*
|
||||
* 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.
|
||||
* To create a new view, extend the new class with this one.
|
||||
*
|
||||
* @remarks
|
||||
* Before this class can be used, the Registry module must be initialized by calling {@link Registry.initialize}.
|
||||
* @class
|
||||
* @abstract
|
||||
*/
|
||||
abstract class AbstractView {
|
||||
/** The base configuration of a view. */
|
||||
protected baseConfiguration: LovelaceViewConfig = {
|
||||
icon: 'mdi:view-dashboard',
|
||||
/**
|
||||
* Configuration of the view.
|
||||
*
|
||||
* @type {LovelaceViewConfig}
|
||||
*/
|
||||
config: LovelaceViewConfig = {
|
||||
icon: "mdi:view-dashboard",
|
||||
subview: false,
|
||||
};
|
||||
|
||||
/** A card configuration to control all entities in the view. */
|
||||
private viewHeaderCardConfiguration: StackCardConfig = {
|
||||
/**
|
||||
* A card to switch all entities in the view.
|
||||
*
|
||||
* @type {StackCardConfig}
|
||||
*/
|
||||
viewControllerCard: StackCardConfig = {
|
||||
cards: [],
|
||||
type: '',
|
||||
type: "",
|
||||
};
|
||||
|
||||
protected get domain(): SupportedDomains | 'home' {
|
||||
return (this.constructor as unknown as ViewConstructor).domain;
|
||||
}
|
||||
/**
|
||||
* The domain of which we operate the devices.
|
||||
*
|
||||
* @type {SupportedDomains | "home"}
|
||||
* @private
|
||||
* @readonly
|
||||
*/
|
||||
readonly #domain: SupportedDomains | "home";
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @remarks
|
||||
* Before this class can be used, the Registry module must be initialized by calling {@link Registry.initialize}.
|
||||
* @param {SupportedDomains} domain The domain which the view is representing.
|
||||
*
|
||||
* @throws {Error} If trying to instantiate this class.
|
||||
* @throws {Error} If the Helper module isn't initialized.
|
||||
*/
|
||||
protected constructor() {
|
||||
if (!Registry.initialized) {
|
||||
logMessage(lvlFatal, 'Registry not initialized!');
|
||||
protected constructor(domain: SupportedDomains | "home") {
|
||||
if (!Helper.isInitialized()) {
|
||||
throw new Error("The Helper module must be initialized before using this one.");
|
||||
}
|
||||
|
||||
this.#domain = domain;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the configuration of the cards to include in the view.
|
||||
* Create the cards to include in the view.
|
||||
*
|
||||
* @return {Promise<(StackCardConfig | TitleCardConfig)[]>} An array of card objects.
|
||||
*/
|
||||
protected async createCardConfigurations(): Promise<LovelaceCardConfig[]> {
|
||||
async createViewCards(): Promise<(StackCardConfig | TitleCardConfig)[]> {
|
||||
if (this.#domain === "home") {
|
||||
// The home domain should override this method because it hasn't entities on its own.
|
||||
// The method override creates its own cards to show at the home view.
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
const viewCards: LovelaceCardConfig[] = [];
|
||||
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) {
|
||||
const areaCards: AbstractCardConfig[] = [];
|
||||
// Create cards for each area.
|
||||
for (const area of Helper.areas) {
|
||||
const areaCards: abstractCardConfig[] = [];
|
||||
const className = Helper.sanitizeClassName(this.#domain + "Card");
|
||||
const cardModule = await import(`../cards/${className}`);
|
||||
|
||||
// Set the target of the Header card to the current area.
|
||||
// Set the target for controller cards to the current area.
|
||||
let target: HassServiceTarget = {
|
||||
area_id: [area.area_id],
|
||||
};
|
||||
const areaEntities = new RegistryFilter(domainEntities).whereAreaId(area.area_id).toList();
|
||||
|
||||
// Set the target of the Header card to entities without an area.
|
||||
if (area.area_id === 'undisclosed') {
|
||||
let entities = Helper.getDeviceEntities(area, this.#domain);
|
||||
// Exclude hidden Config and Diagnostic entities.
|
||||
entities = applyEntityCategoryFilters(entities, this.#domain);
|
||||
|
||||
// Set the target for controller cards to entities without an area.
|
||||
if (area.area_id === "undisclosed") {
|
||||
target = {
|
||||
entity_id: areaEntities.map((entity) => entity.entity_id),
|
||||
};
|
||||
entity_id: entities.map(entity => entity.entity_id),
|
||||
}
|
||||
}
|
||||
|
||||
// Create a card configuration for each entity in the current area.
|
||||
areaCards.push(
|
||||
...areaEntities.map((entity) =>
|
||||
new DomainCard(entity, Registry.strategyOptions.card_options?.[entity.entity_id]).getCard(),
|
||||
),
|
||||
);
|
||||
// Create a card for each domain-entity of the current area.
|
||||
for (const entity of entities) {
|
||||
let cardOptions = Helper.strategyOptions.card_options?.[entity.entity_id];
|
||||
let deviceOptions = Helper.strategyOptions.card_options?.[entity.device_id ?? "null"];
|
||||
|
||||
// Vertically stack the cards of the current area.
|
||||
if (cardOptions?.hidden || deviceOptions?.hidden) {
|
||||
continue;
|
||||
}
|
||||
|
||||
areaCards.push(new cardModule[className](entity, cardOptions).getCard());
|
||||
}
|
||||
|
||||
// Vertical stack the area cards if it has entities.
|
||||
if (areaCards.length) {
|
||||
// Create and insert a Header card.
|
||||
const areaHeaderCardOptions = (
|
||||
'headerCardConfiguration' in this.baseConfiguration ? this.baseConfiguration.headerCardConfiguration : {}
|
||||
) as CustomHeaderCardConfig;
|
||||
const titleCardOptions = ("controllerCardOptions" in this.config) ? this.config.controllerCardOptions : {};
|
||||
|
||||
areaCards.unshift(new HeaderCard(target, { title: area.name, ...areaHeaderCardOptions }).createCard());
|
||||
// Create and insert a Controller card.
|
||||
areaCards.unshift(new ControllerCard(target, Object.assign({title: area.name}, titleCardOptions)).createCard());
|
||||
|
||||
viewCards.push({ type: 'vertical-stack', cards: areaCards });
|
||||
viewCards.push({
|
||||
type: "vertical-stack",
|
||||
cards: areaCards,
|
||||
} as StackCardConfig);
|
||||
}
|
||||
}
|
||||
|
||||
// Add a Header Card to control all the entities in the view.
|
||||
if (this.viewHeaderCardConfiguration.cards.length && viewCards.length) {
|
||||
viewCards.unshift(this.viewHeaderCardConfiguration);
|
||||
// Add a Controller Card for all the entities in the view.
|
||||
if (this.viewControllerCard.cards.length && viewCards.length) {
|
||||
viewCards.unshift(this.viewControllerCard);
|
||||
}
|
||||
|
||||
return viewCards;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a view configuration.
|
||||
* Get a view object.
|
||||
*
|
||||
* The configuration includes the card configurations which are created by createCardConfigurations().
|
||||
* The view includes the cards which are created by method createViewCards().
|
||||
*
|
||||
* @returns {Promise<LovelaceViewConfig>} The view object.
|
||||
*/
|
||||
async getView(): Promise<LovelaceViewConfig> {
|
||||
return {
|
||||
...this.baseConfiguration,
|
||||
cards: await this.createCardConfigurations(),
|
||||
...this.config,
|
||||
cards: await this.createViewCards(),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the domain's entity ids to target for a HASS service call.
|
||||
*/
|
||||
private getDomainTargets(): HassServiceTarget {
|
||||
return {
|
||||
entity_id: Registry.entities
|
||||
.filter((entity) => entity.entity_id.startsWith(this.domain + '.'))
|
||||
.map((entity) => entity.entity_id),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the view configuration with defaults and custom settings.
|
||||
* Get a target of entity IDs for the given domain.
|
||||
*
|
||||
* @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.
|
||||
* @param {string} domain - The target domain to retrieve entity IDs from.
|
||||
* @return {HassServiceTarget} - A target for a service call.
|
||||
*/
|
||||
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();
|
||||
targetDomain(domain: string): HassServiceTarget {
|
||||
return {
|
||||
entity_id: Helper.entities.filter(
|
||||
entity =>
|
||||
entity.entity_id.startsWith(domain + ".")
|
||||
&& !entity.hidden_by
|
||||
&& !Helper.strategyOptions.card_options?.[entity.entity_id]?.hidden
|
||||
).map(entity => entity.entity_id),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default AbstractView;
|
||||
export {AbstractView};
|
||||
|
@@ -1,54 +1,77 @@
|
||||
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.
|
||||
|
||||
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.
|
||||
*
|
||||
* Used to create a view configuration for entities of the camera domain.
|
||||
* Used to create a view for entities of the camera domain.
|
||||
*
|
||||
* @class CameraView
|
||||
* @extends AbstractView
|
||||
*/
|
||||
class CameraView extends AbstractView {
|
||||
/** The domain of the entities that the view is representing. */
|
||||
static readonly domain: SupportedDomains = 'camera' as const;
|
||||
/**
|
||||
* Domain of the view's entities.
|
||||
*
|
||||
* @type {SupportedDomains}
|
||||
* @static
|
||||
* @private
|
||||
*/
|
||||
static #domain: SupportedDomains = "camera";
|
||||
|
||||
/** Returns the default configuration object for the view. */
|
||||
static getDefaultConfig(): ViewConfig {
|
||||
return {
|
||||
title: localize('camera.cameras'),
|
||||
path: 'cameras',
|
||||
icon: 'mdi:cctv',
|
||||
/**
|
||||
* Default configuration of the view.
|
||||
*
|
||||
* @type {views.ViewConfig}
|
||||
* @private
|
||||
*/
|
||||
#defaultConfig: views.ViewConfig = {
|
||||
title: Helper.customLocalize("camera.cameras"),
|
||||
path: "cameras",
|
||||
icon: "mdi:cctv",
|
||||
subview: false,
|
||||
headerCardConfiguration: {
|
||||
controllerCardOptions: {
|
||||
showControls: false,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/** Returns the default configuration of the view's Header card. */
|
||||
static getViewHeaderCardConfig(): CustomHeaderCardConfig {
|
||||
return {
|
||||
title: localize('camera.all_cameras'),
|
||||
/**
|
||||
* Default configuration of the view's Controller card.
|
||||
*
|
||||
* @type {cards.ControllerCardOptions}
|
||||
* @private
|
||||
*/
|
||||
#viewControllerCardConfig: cards.ControllerCardOptions = {
|
||||
title: Helper.customLocalize("camera.all_cameras"),
|
||||
subtitle:
|
||||
`${Registry.getCountTemplate(CameraView.domain, 'ne', 'off')} ${localize('camera.cameras')} ` +
|
||||
localize('generic.busy'),
|
||||
`${Helper.getCountTemplate(CameraView.#domain, "ne", "off")} ${Helper.customLocalize("camera.cameras")} `
|
||||
+ Helper.customLocalize("generic.busy"),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param {ViewConfig} [customConfiguration] Custom view configuration.
|
||||
* @param {views.ViewConfig} [options={}] Options for the view.
|
||||
*/
|
||||
constructor(customConfiguration?: ViewConfig) {
|
||||
super();
|
||||
constructor(options: views.ViewConfig = {}) {
|
||||
super(CameraView.#domain);
|
||||
|
||||
this.initializeViewConfig(CameraView.getDefaultConfig(), customConfiguration, CameraView.getViewHeaderCardConfig());
|
||||
this.config = Object.assign(this.config, this.#defaultConfig, options);
|
||||
|
||||
// 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 default CameraView;
|
||||
export {CameraView};
|
||||
|
@@ -1,58 +1,77 @@
|
||||
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.
|
||||
|
||||
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.
|
||||
*
|
||||
* Used to create a view configuration for entities of the climate domain.
|
||||
* Used to create a view for entities of the climate domain.
|
||||
*
|
||||
* @class ClimateView
|
||||
* @extends AbstractView
|
||||
*/
|
||||
class ClimateView extends AbstractView {
|
||||
/**The domain of the entities that the view is representing. */
|
||||
static readonly domain: SupportedDomains = 'climate' as const;
|
||||
/**
|
||||
* Domain of the view's entities.
|
||||
*
|
||||
* @type {SupportedDomains}
|
||||
* @static
|
||||
* @private
|
||||
*/
|
||||
static #domain: SupportedDomains = "climate";
|
||||
|
||||
/** Returns the default configuration object for the view. */
|
||||
static getDefaultConfig(): ViewConfig {
|
||||
return {
|
||||
title: localize('climate.climates'),
|
||||
path: 'climates',
|
||||
icon: 'mdi:thermostat',
|
||||
/**
|
||||
* Default configuration of the view.
|
||||
*
|
||||
* @type {views.ViewConfig}
|
||||
* @private
|
||||
*/
|
||||
#defaultConfig: views.ViewConfig = {
|
||||
title: Helper.customLocalize("climate.climates"),
|
||||
path: "climates",
|
||||
icon: "mdi:thermostat",
|
||||
subview: false,
|
||||
headerCardConfiguration: {
|
||||
controllerCardOptions: {
|
||||
showControls: false,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/** Returns the default configuration of the view's Header card. */
|
||||
static getViewHeaderCardConfig(): CustomHeaderCardConfig {
|
||||
return {
|
||||
title: localize('climate.all_climates'),
|
||||
/**
|
||||
* Default configuration of the view's Controller card.
|
||||
*
|
||||
* @type {cards.ControllerCardOptions}
|
||||
* @private
|
||||
*/
|
||||
#viewControllerCardConfig: cards.ControllerCardOptions = {
|
||||
title: Helper.customLocalize("climate.all_climates"),
|
||||
subtitle:
|
||||
`${Registry.getCountTemplate(ClimateView.domain, 'ne', 'off')} ${localize('climate.climates')} ` +
|
||||
localize('generic.busy'),
|
||||
`${Helper.getCountTemplate(ClimateView.#domain, "ne", "off")} ${Helper.customLocalize("climate.climates")} `
|
||||
+ Helper.customLocalize("generic.busy"),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param {ViewConfig} [customConfiguration] Custom view configuration.
|
||||
* @param {views.ViewConfig} [options={}] Options for the view.
|
||||
*/
|
||||
constructor(customConfiguration?: ViewConfig) {
|
||||
super();
|
||||
constructor(options: views.ViewConfig = {}) {
|
||||
super(ClimateView.#domain);
|
||||
|
||||
this.initializeViewConfig(
|
||||
ClimateView.getDefaultConfig(),
|
||||
customConfiguration,
|
||||
ClimateView.getViewHeaderCardConfig(),
|
||||
);
|
||||
this.config = Object.assign(this.config, this.#defaultConfig, options);
|
||||
|
||||
// Create a Controller card to switch all entities of the domain.
|
||||
this.viewControllerCard = new ControllerCard(
|
||||
this.targetDomain(ClimateView.#domain),
|
||||
{
|
||||
...this.#viewControllerCardConfig,
|
||||
...("controllerCardOptions" in this.config ? this.config.controllerCardOptions : {}) as cards.ControllerCardConfig,
|
||||
}).createCard();
|
||||
}
|
||||
}
|
||||
|
||||
export default ClimateView;
|
||||
export {ClimateView};
|
||||
|
@@ -1,58 +1,80 @@
|
||||
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.
|
||||
|
||||
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.
|
||||
*
|
||||
* Used to create a view configuration for entities of the cover domain.
|
||||
* Used to create a view for entities of the cover domain.
|
||||
*
|
||||
* @class CoverView
|
||||
* @extends AbstractView
|
||||
*/
|
||||
class CoverView extends AbstractView {
|
||||
/** The domain of the entities that the view is representing. */
|
||||
static readonly domain: SupportedDomains = 'cover' as const;
|
||||
/**
|
||||
* Domain of the view's entities.
|
||||
*
|
||||
* @type {SupportedDomains}
|
||||
* @static
|
||||
* @private
|
||||
*/
|
||||
static #domain: SupportedDomains = "cover";
|
||||
|
||||
/** Returns the default configuration object for the view. */
|
||||
static getDefaultConfig(): ViewConfig {
|
||||
return {
|
||||
title: localize('cover.covers'),
|
||||
path: 'covers',
|
||||
icon: 'mdi:window-open',
|
||||
/**
|
||||
* Default configuration of the view.
|
||||
*
|
||||
* @type {views.ViewConfig}
|
||||
* @private
|
||||
*/
|
||||
#defaultConfig: views.ViewConfig = {
|
||||
title: Helper.customLocalize("cover.covers"),
|
||||
path: "covers",
|
||||
icon: "mdi:window-open",
|
||||
subview: false,
|
||||
headerCardConfiguration: {
|
||||
iconOn: 'mdi:arrow-up',
|
||||
iconOff: 'mdi:arrow-down',
|
||||
onService: 'cover.open_cover',
|
||||
offService: 'cover.close_cover',
|
||||
controllerCardOptions: {
|
||||
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. */
|
||||
static getViewHeaderCardConfig(): CustomHeaderCardConfig {
|
||||
return {
|
||||
title: localize('cover.all_covers'),
|
||||
/**
|
||||
* Default configuration of the view's Controller card.
|
||||
*
|
||||
* @type {cards.ControllerCardOptions}
|
||||
* @private
|
||||
*/
|
||||
#viewControllerCardConfig: cards.ControllerCardOptions = {
|
||||
title: Helper.customLocalize("cover.all_covers"),
|
||||
subtitle:
|
||||
`${Registry.getCountTemplate(CoverView.domain, 'search', '(open|opening|closing)')} ` +
|
||||
`${localize('cover.covers')} ` +
|
||||
`${localize('generic.unclosed')}`,
|
||||
`${Helper.getCountTemplate(CoverView.#domain, "search", "(open|opening|closing)")} ${Helper.customLocalize("cover.covers")} `
|
||||
+ Helper.customLocalize("generic.unclosed"),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param {ViewConfig} [customConfiguration] Custom view configuration.
|
||||
* @param {views.ViewConfig} [options={}] Options for the view.
|
||||
*/
|
||||
constructor(customConfiguration?: ViewConfig) {
|
||||
super();
|
||||
constructor(options: views.ViewConfig = {}) {
|
||||
super(CoverView.#domain);
|
||||
|
||||
this.initializeViewConfig(CoverView.getDefaultConfig(), customConfiguration, CoverView.getViewHeaderCardConfig());
|
||||
this.config = Object.assign(this.config, this.#defaultConfig, options);
|
||||
|
||||
// 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 default CoverView;
|
||||
export {CoverView};
|
||||
|
@@ -1,56 +1,80 @@
|
||||
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.
|
||||
|
||||
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.
|
||||
*
|
||||
* Used to create a view configuration for entities of the fan domain.
|
||||
* Used to create a view for entities of the fan domain.
|
||||
*
|
||||
* @class FanView
|
||||
* @extends AbstractView
|
||||
*/
|
||||
class FanView extends AbstractView {
|
||||
/** The domain of the entities that the view is representing. */
|
||||
static readonly domain: SupportedDomains = 'fan' as const;
|
||||
/**
|
||||
* Domain of the view's entities.
|
||||
*
|
||||
* @type {SupportedDomains}
|
||||
* @static
|
||||
* @private
|
||||
*/
|
||||
static #domain: SupportedDomains = "fan";
|
||||
|
||||
/** Returns the default configuration object for the view. */
|
||||
static getDefaultConfig(): ViewConfig {
|
||||
return {
|
||||
title: localize('fan.fans'),
|
||||
path: 'fans',
|
||||
icon: 'mdi:fan',
|
||||
/**
|
||||
* Default configuration of the view.
|
||||
*
|
||||
* @type {views.ViewConfig}
|
||||
* @private
|
||||
*/
|
||||
#defaultConfig: views.ViewConfig = {
|
||||
title: Helper.customLocalize("fan.fans"),
|
||||
path: "fans",
|
||||
icon: "mdi:fan",
|
||||
subview: false,
|
||||
headerCardConfiguration: {
|
||||
iconOn: 'mdi:fan',
|
||||
iconOff: 'mdi:fan-off',
|
||||
onService: 'fan.turn_on',
|
||||
offService: 'fan.turn_off',
|
||||
controllerCardOptions: {
|
||||
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. */
|
||||
static getViewHeaderCardConfig(): CustomHeaderCardConfig {
|
||||
return {
|
||||
title: localize('fan.all_fans'),
|
||||
/**
|
||||
* Default configuration of the view's Controller card.
|
||||
*
|
||||
* @type {cards.ControllerCardOptions}
|
||||
* @private
|
||||
*/
|
||||
#viewControllerCardConfig: cards.ControllerCardOptions = {
|
||||
title: Helper.customLocalize("fan.all_fans"),
|
||||
subtitle:
|
||||
`${Registry.getCountTemplate(FanView.domain, 'eq', 'on')} ${localize('fan.fans')} ` + localize('generic.on'),
|
||||
`${Helper.getCountTemplate(FanView.#domain, "eq", "on")} ${Helper.customLocalize("fan.fans")} `
|
||||
+ Helper.customLocalize("generic.on"),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param {ViewConfig} [customConfiguration] Custom view configuration.
|
||||
* @param {views.ViewConfig} [options={}] Options for the view.
|
||||
*/
|
||||
constructor(customConfiguration?: ViewConfig) {
|
||||
super();
|
||||
constructor(options: views.ViewConfig = {}) {
|
||||
super(FanView.#domain);
|
||||
|
||||
this.initializeViewConfig(FanView.getDefaultConfig(), customConfiguration, FanView.getViewHeaderCardConfig());
|
||||
this.config = Object.assign(this.config, this.#defaultConfig, options);
|
||||
|
||||
// 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 default FanView;
|
||||
export {FanView};
|
||||
|
@@ -1,267 +1,284 @@
|
||||
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.
|
||||
|
||||
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, lvlInfo } from '../utilities/debug';
|
||||
import { localize } from '../utilities/localize';
|
||||
import AbstractView from './AbstractView';
|
||||
import registryFilter from '../utilities/RegistryFilter';
|
||||
import { stackHorizontal } from '../utilities/cardStacking';
|
||||
|
||||
/**
|
||||
* Home View Class.
|
||||
*
|
||||
* Used to create a Home view.
|
||||
*
|
||||
* @class HomeView
|
||||
* @extends AbstractView
|
||||
*/
|
||||
class HomeView extends AbstractView {
|
||||
/** The domain of the entities that the view is representing. */
|
||||
static readonly domain = 'home' as const;
|
||||
|
||||
/** Returns the default configuration object for the view. */
|
||||
static getDefaultConfig(): ViewConfig {
|
||||
return {
|
||||
title: localize('generic.home'),
|
||||
icon: 'mdi:home-assistant',
|
||||
path: 'home',
|
||||
/**
|
||||
* Default configuration of the view.
|
||||
*
|
||||
* @type {views.ViewConfig}
|
||||
* @private
|
||||
*/
|
||||
#defaultConfig: views.ViewConfig = {
|
||||
title: Helper.customLocalize("generic.home"),
|
||||
icon: "mdi:home-assistant",
|
||||
path: "home",
|
||||
subview: false,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param {ViewConfig} [customConfiguration] Custom view configuration.
|
||||
* @param {views.ViewConfig} [options={}] Options for the view.
|
||||
*/
|
||||
constructor(customConfiguration?: ViewConfig) {
|
||||
super();
|
||||
constructor(options: views.ViewConfig = {}) {
|
||||
super("home");
|
||||
|
||||
this.baseConfiguration = { ...this.baseConfiguration, ...HomeView.getDefaultConfig(), ...customConfiguration };
|
||||
this.config = Object.assign(this.config, this.#defaultConfig, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the configuration of the cards to include in the view.
|
||||
* Create the cards to include in the view.
|
||||
*
|
||||
* @return {Promise<(StackCardConfig | TemplateCardConfig | ChipsCardConfig)[]>} Promise a View Card array.
|
||||
* @override
|
||||
*/
|
||||
async createCardConfigurations(): Promise<LovelaceCardConfig[]> {
|
||||
const homeViewCards: LovelaceCardConfig[] = [];
|
||||
async createViewCards(): Promise<(StackCardConfig | TemplateCardConfig | ChipsCardConfig)[]> {
|
||||
return await Promise.all([
|
||||
this.#createChips(),
|
||||
this.#createPersonCards(),
|
||||
this.#createAreaSection(),
|
||||
]).then(([chips, personCards, areaCards]) => {
|
||||
const options = Helper.strategyOptions;
|
||||
const homeViewCards = [];
|
||||
|
||||
let chipsSection, personsSection, areasSection;
|
||||
|
||||
try {
|
||||
[chipsSection, personsSection, areasSection] = await Promise.all([
|
||||
this.createChipsSection(),
|
||||
this.createPersonsSection(),
|
||||
this.createAreasSection(),
|
||||
]);
|
||||
} catch (e) {
|
||||
logMessage(lvlError, 'Error importing created sections!', e);
|
||||
|
||||
return homeViewCards;
|
||||
}
|
||||
|
||||
if (chipsSection) {
|
||||
homeViewCards.push(chipsSection);
|
||||
}
|
||||
|
||||
if (personsSection) {
|
||||
homeViewCards.push(personsSection);
|
||||
}
|
||||
|
||||
// Create the greeting section.
|
||||
if (!('greeting' in Registry.strategyOptions.home_view.hidden)) {
|
||||
if (chips.length) {
|
||||
// TODO: Create the Chip card at this.#createChips()
|
||||
homeViewCards.push({
|
||||
type: 'custom:mushroom-template-card',
|
||||
primary: `{% set time = now().hour %}
|
||||
{% if (time >= 18) %}
|
||||
${localize('generic.good_evening')},{{user}}!
|
||||
{% elif (time >= 12) %}
|
||||
${localize('generic.good_afternoon')}, {{user}}!
|
||||
{% elif (time >= 6) %}
|
||||
${localize('generic.good_morning')}, {{user}}!
|
||||
{% else %}
|
||||
${localize('generic.hello')}, {{user}}! {% endif %}`,
|
||||
icon: 'mdi:hand-wave',
|
||||
icon_color: 'orange',
|
||||
type: "custom:mushroom-chips-card",
|
||||
alignment: "center",
|
||||
chips: chips,
|
||||
} as ChipsCardConfig)
|
||||
}
|
||||
|
||||
if (personCards.length) {
|
||||
// TODO: Create the stack at this.#createPersonCards()
|
||||
homeViewCards.push({
|
||||
type: "horizontal-stack",
|
||||
cards: personCards,
|
||||
} as StackCardConfig);
|
||||
}
|
||||
|
||||
if (!(Helper.strategyOptions.home_view.hidden as string[]).includes("greeting")) {
|
||||
homeViewCards.push({
|
||||
type: "custom:mushroom-template-card",
|
||||
primary:
|
||||
`{% set time = now().hour %} {% if (time >= 18) %} ${Helper.customLocalize("generic.good_evening")},{{user}}!
|
||||
{% elif (time >= 12) %} ${Helper.customLocalize("generic.good_afternoon")}, {{user}}!
|
||||
{% elif (time >= 5) %} ${Helper.customLocalize("generic.good_morning")}, {{user}}!
|
||||
{% else %} ${Helper.customLocalize("generic.hello")}, {{user}}! {% endif %}`,
|
||||
icon: "mdi:hand-wave",
|
||||
icon_color: "orange",
|
||||
tap_action: {
|
||||
action: 'none',
|
||||
action: "none",
|
||||
} as ActionConfig,
|
||||
double_tap_action: {
|
||||
action: 'none',
|
||||
action: "none",
|
||||
} as ActionConfig,
|
||||
hold_action: {
|
||||
action: 'none',
|
||||
action: "none",
|
||||
} as ActionConfig,
|
||||
} as TemplateCardConfig);
|
||||
}
|
||||
|
||||
if (Registry.strategyOptions.quick_access_cards) {
|
||||
homeViewCards.push(...Registry.strategyOptions.quick_access_cards);
|
||||
// Add quick access cards.
|
||||
if (options.quick_access_cards) {
|
||||
homeViewCards.push(...options.quick_access_cards);
|
||||
}
|
||||
|
||||
if (areasSection) {
|
||||
homeViewCards.push(areasSection);
|
||||
}
|
||||
// Add area cards.
|
||||
homeViewCards.push({
|
||||
type: "vertical-stack",
|
||||
cards: areaCards,
|
||||
} as StackCardConfig);
|
||||
|
||||
if (Registry.strategyOptions.extra_cards) {
|
||||
homeViewCards.push(...Registry.strategyOptions.extra_cards);
|
||||
// Add custom cards.
|
||||
if (options.extra_cards) {
|
||||
homeViewCards.push(...options.extra_cards);
|
||||
}
|
||||
|
||||
return homeViewCards;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a chip section to include in the view
|
||||
* Create the chips to include in the view.
|
||||
*
|
||||
* If the section is marked as hidden in the strategy option, then the section is not created.
|
||||
* @return {Promise<LovelaceChipConfig[]>} Promise a chip array.
|
||||
*/
|
||||
private async createChipsSection(): Promise<ChipsCardConfig | undefined> {
|
||||
if ((Registry.strategyOptions.home_view.hidden as string[]).includes('chips')) {
|
||||
// The section is hidden.
|
||||
return;
|
||||
async #createChips(): Promise<LovelaceChipConfig[]> {
|
||||
if ((Helper.strategyOptions.home_view.hidden as string[]).includes("chips")) {
|
||||
// The Chip section is hidden.
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
const chipConfigurations: LovelaceChipConfig[] = [];
|
||||
const exposedChips = Registry.getExposedNames('chip');
|
||||
const chips: LovelaceChipConfig[] = [];
|
||||
const chipOptions = Helper.strategyOptions.chips;
|
||||
|
||||
let Chip;
|
||||
// TODO: Get domains from config.
|
||||
const exposedChips: supportedChips[] = ["light", "fan", "cover", "switch", "climate"];
|
||||
// Create a list of area-ids, used for switching all devices via chips
|
||||
const areaIds = Helper.areas.map(area => area.area_id ?? "");
|
||||
|
||||
let chipModule;
|
||||
|
||||
// Weather chip.
|
||||
// FIXME: It's not possible to hide the weather chip in the configuration.
|
||||
const weatherEntityId =
|
||||
Registry.strategyOptions.chips.weather_entity === 'auto'
|
||||
? Registry.entities.find((entity) => entity.entity_id.startsWith('weather.'))?.entity_id
|
||||
: Registry.strategyOptions.chips.weather_entity;
|
||||
const weatherEntityId = chipOptions.weather_entity ?? Helper.entities.find(
|
||||
(entity) => entity.entity_id.startsWith("weather.") && entity.disabled_by === null && entity.hidden_by === null,
|
||||
)?.entity_id;
|
||||
|
||||
if (weatherEntityId) {
|
||||
try {
|
||||
Chip = (await import('../chips/WeatherChip')).default;
|
||||
const weatherChip = new Chip(weatherEntityId);
|
||||
chipModule = await import("../chips/WeatherChip");
|
||||
const weatherChip = new chipModule.WeatherChip(weatherEntityId);
|
||||
|
||||
chipConfigurations.push(weatherChip.getChipConfiguration());
|
||||
chips.push(weatherChip.getChip());
|
||||
} catch (e) {
|
||||
logMessage(lvlError, 'Error importing chip weather!', e);
|
||||
Helper.logError("An error occurred while creating the weather chip!", e);
|
||||
}
|
||||
} else {
|
||||
logMessage(lvlInfo, 'Weather chip has no entities available.');
|
||||
}
|
||||
|
||||
// Numeric chips.
|
||||
for (const chipName of exposedChips) {
|
||||
if (!isSupportedChip(chipName) || !new registryFilter(Registry.entities).whereDomain(chipName).count()) {
|
||||
logMessage(lvlInfo, `Chip for domain ${chipName} is unsupported or has no entities available.`);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
const moduleName = sanitizeClassName(chipName + 'Chip');
|
||||
|
||||
for (let chipType of exposedChips) {
|
||||
if (chipType !== "weather" && (chipOptions?.[(`${chipType}_count`)] ?? true)) {
|
||||
const className = Helper.sanitizeClassName(chipType + "Chip");
|
||||
try {
|
||||
Chip = (await import(`../chips/${moduleName}`)).default;
|
||||
const currentChip = new Chip();
|
||||
chipModule = await import((`../chips/${className}`));
|
||||
const chip = new chipModule[className]();
|
||||
|
||||
chipConfigurations.push(currentChip.getChipConfiguration());
|
||||
chip.setTapActionTarget({area_id: areaIds});
|
||||
chips.push(chip.getChip());
|
||||
} catch (e) {
|
||||
logMessage(lvlError, `Error importing chip ${chipName}!`, e);
|
||||
Helper.logError(`An error occurred while creating the ${chipType} chip!`, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add extra chips.
|
||||
if (Registry.strategyOptions.chips?.extra_chips) {
|
||||
chipConfigurations.push(...Registry.strategyOptions.chips.extra_chips);
|
||||
// Extra chips.
|
||||
if (chipOptions?.extra_chips) {
|
||||
chips.push(...chipOptions.extra_chips);
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'custom:mushroom-chips-card',
|
||||
alignment: 'center',
|
||||
chips: chipConfigurations,
|
||||
};
|
||||
return chips;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a persons section to include in the view.
|
||||
* Create the person cards to include in the view.
|
||||
*
|
||||
* If the section is marked as hidden in the strategy option, then the section is not created.
|
||||
* @return {PersonCardConfig[]} A Person Card array.
|
||||
*/
|
||||
private async createPersonsSection(): Promise<StackCardConfig | undefined> {
|
||||
if ((Registry.strategyOptions.home_view.hidden as string[]).includes('persons')) {
|
||||
// The section is hidden.
|
||||
#createPersonCards(): PersonCardConfig[] {
|
||||
if ((Helper.strategyOptions.home_view.hidden as string[]).includes("persons")) {
|
||||
// The Person section is hidden.
|
||||
|
||||
return;
|
||||
return [];
|
||||
}
|
||||
|
||||
const cardConfigurations: PersonCardConfig[] = [];
|
||||
const PersonCard = (await import('../cards/PersonCard')).default;
|
||||
const cards: PersonCardConfig[] = [];
|
||||
|
||||
cardConfigurations.push(
|
||||
...Registry.entities
|
||||
.filter((entity) => entity.entity_id.startsWith('person.'))
|
||||
.map((person) => new PersonCard(person).getCard()),
|
||||
);
|
||||
import("../cards/PersonCard").then(personModule => {
|
||||
for (const person of Helper.entities.filter((entity) => {
|
||||
return entity.entity_id.startsWith("person.")
|
||||
&& entity.hidden_by == null
|
||||
&& entity.disabled_by == null;
|
||||
})) {
|
||||
cards.push(new personModule.PersonCard(person).getCard());
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
type: 'vertical-stack',
|
||||
cards: stackHorizontal(cardConfigurations),
|
||||
};
|
||||
return cards;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the area cards to include in the view.
|
||||
*
|
||||
* Area cards are grouped into two areas per row.
|
||||
* If the section is marked as hidden in the strategy option, then the section is not created.
|
||||
*
|
||||
* @return {Promise<(TitleCardConfig | StackCardConfig)[]>} Promise an Area Card Section.
|
||||
*/
|
||||
private async createAreasSection(): Promise<StackCardConfig | undefined> {
|
||||
if ((Registry.strategyOptions.home_view.hidden as string[]).includes('areas')) {
|
||||
async #createAreaSection(): Promise<(TitleCardConfig | StackCardConfig)[]> {
|
||||
if ((Helper.strategyOptions.home_view.hidden as string[]).includes("areas")) {
|
||||
// Areas section is hidden.
|
||||
|
||||
return;
|
||||
return [];
|
||||
}
|
||||
|
||||
const cardConfigurations: (TemplateCardConfig | AreaCardConfig)[] = [];
|
||||
const groupedCards: (TitleCardConfig | StackCardConfig)[] = [];
|
||||
|
||||
let onlyDefaultCards = true;
|
||||
let areaCards: (TemplateCardConfig | AreaCardConfig)[] = [];
|
||||
|
||||
for (const area of Registry.areas) {
|
||||
const moduleName =
|
||||
Registry.strategyOptions.areas[area.area_id]?.type ?? Registry.strategyOptions.areas['_']?.type ?? 'default';
|
||||
if (!(Helper.strategyOptions.home_view.hidden as string[]).includes("areasTitle")) {
|
||||
groupedCards.push({
|
||||
type: "custom:mushroom-title-card",
|
||||
title: Helper.customLocalize("generic.areas"),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
let AreaCard;
|
||||
for (const [i, area] of Helper.areas.entries()) {
|
||||
type ModuleType = typeof import("../cards/AreaCard");
|
||||
|
||||
onlyDefaultCards = onlyDefaultCards && moduleName === 'default';
|
||||
let module: ModuleType;
|
||||
let moduleName =
|
||||
Helper.strategyOptions.areas[area.area_id]?.type ??
|
||||
Helper.strategyOptions.areas["_"]?.type ??
|
||||
"default";
|
||||
|
||||
// Load module by type in strategy options.
|
||||
try {
|
||||
AreaCard = (await import(`../cards/${moduleName}`)).default;
|
||||
module = await import((`../cards/${moduleName}`));
|
||||
} catch (e) {
|
||||
// Fallback to the default strategy card.
|
||||
AreaCard = (await import('../cards/AreaCard')).default;
|
||||
module = await import("../cards/AreaCard");
|
||||
|
||||
if (Registry.strategyOptions.debug && moduleName !== 'default') {
|
||||
logMessage(lvlError, `Error importing ${moduleName}: card!`, e);
|
||||
if (Helper.strategyOptions.debug && moduleName !== "default") {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
cardConfigurations.push(new AreaCard(area).getCard());
|
||||
}
|
||||
|
||||
// FIXME: The columns are too narrow when having HASS area cards.
|
||||
return {
|
||||
type: 'vertical-stack',
|
||||
title: (Registry.strategyOptions.home_view.hidden as HomeViewSections[]).includes('areasTitle')
|
||||
? undefined
|
||||
: localize('generic.areas'),
|
||||
cards: stackHorizontal(cardConfigurations, { area: 1, 'custom:mushroom-template-card': 2 }),
|
||||
// Get a card for the area.
|
||||
if (!Helper.strategyOptions.areas[area.area_id as string]?.hidden) {
|
||||
let options = {
|
||||
...Helper.strategyOptions.areas["_"],
|
||||
...Helper.strategyOptions.areas[area.area_id],
|
||||
};
|
||||
|
||||
areaCards.push(new module.AreaCard(area, options).getCard());
|
||||
}
|
||||
|
||||
// Horizontally group every two area cards if all cards are created.
|
||||
if (i === Helper.areas.length - 1) {
|
||||
for (let i = 0; i < areaCards.length; i += 2) {
|
||||
groupedCards.push({
|
||||
type: "horizontal-stack",
|
||||
cards: areaCards.slice(i, i + 2),
|
||||
} as StackCardConfig);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default HomeView;
|
||||
return groupedCards;
|
||||
}
|
||||
}
|
||||
|
||||
export {HomeView};
|
||||
|
@@ -1,11 +1,12 @@
|
||||
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.
|
||||
|
||||
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.
|
||||
*
|
||||
@@ -15,40 +16,65 @@ import AbstractView from './AbstractView';
|
||||
* @extends AbstractView
|
||||
*/
|
||||
class LightView extends AbstractView {
|
||||
/** The domain of the entities that the view is representing. */
|
||||
static readonly domain = 'light' as const;
|
||||
/**
|
||||
* Domain of the view's entities.
|
||||
*
|
||||
* @type {SupportedDomains}
|
||||
* @static
|
||||
* @private
|
||||
*/
|
||||
static #domain: SupportedDomains = "light";
|
||||
|
||||
/** Returns the default configuration object for the view. */
|
||||
static getDefaultConfig(): ViewConfig {
|
||||
return {
|
||||
title: localize('light.lights'),
|
||||
path: 'lights',
|
||||
icon: 'mdi:lightbulb-group',
|
||||
/**
|
||||
* Default configuration of the view.
|
||||
*
|
||||
* @type {views.ViewConfig}
|
||||
* @private
|
||||
*/
|
||||
#defaultConfig: views.ViewConfig = {
|
||||
title: Helper.customLocalize("light.lights"),
|
||||
path: "lights",
|
||||
icon: "mdi:lightbulb-group",
|
||||
subview: false,
|
||||
headerCardConfiguration: {
|
||||
iconOn: 'mdi:lightbulb',
|
||||
iconOff: 'mdi:lightbulb-off',
|
||||
onService: 'light.turn_on',
|
||||
offService: 'light.turn_off',
|
||||
controllerCardOptions: {
|
||||
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. */
|
||||
static getViewHeaderCardConfig(): CustomHeaderCardConfig {
|
||||
return {
|
||||
title: localize('light.all_lights'),
|
||||
/**
|
||||
* Default configuration of the view's Controller card.
|
||||
*
|
||||
* @type {cards.ControllerCardOptions}
|
||||
* @private
|
||||
*/
|
||||
#viewControllerCardConfig: cards.ControllerCardOptions = {
|
||||
title: Helper.customLocalize("light.all_lights"),
|
||||
subtitle:
|
||||
`${Registry.getCountTemplate(LightView.domain, 'eq', 'on')} ${localize('light.lights')} ` +
|
||||
localize('generic.on'),
|
||||
`${Helper.getCountTemplate(LightView.#domain, "eq", "on")} ${Helper.customLocalize("light.lights")} `
|
||||
+ Helper.customLocalize("generic.on"),
|
||||
};
|
||||
}
|
||||
|
||||
constructor(customConfiguration?: ViewConfig) {
|
||||
super();
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param {views.ViewConfig} [options={}] Options for the view.
|
||||
*/
|
||||
constructor(options: views.ViewConfig = {}) {
|
||||
super(LightView.#domain);
|
||||
|
||||
this.initializeViewConfig(LightView.getDefaultConfig(), customConfiguration, LightView.getViewHeaderCardConfig());
|
||||
this.config = Object.assign(this.config, this.#defaultConfig, options);
|
||||
|
||||
// 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 default LightView;
|
||||
export {LightView};
|
||||
|
@@ -1,47 +1,54 @@
|
||||
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.
|
||||
|
||||
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.
|
||||
*
|
||||
* sed to create a view configuration for entities of the scene domain.
|
||||
* Used to create a view for entities of the scene domain.
|
||||
*
|
||||
* @class SceneView
|
||||
* @extends AbstractView
|
||||
*/
|
||||
class SceneView extends AbstractView {
|
||||
/** The domain of the entities that the view is representing. */
|
||||
static readonly domain = 'scene' as const;
|
||||
/**
|
||||
* Domain of the view's entities.
|
||||
*
|
||||
* @type {SupportedDomains}
|
||||
* @static
|
||||
* @private
|
||||
*/
|
||||
static #domain: SupportedDomains = "scene";
|
||||
|
||||
/** Returns the default configuration object for the view. */
|
||||
static getDefaultConfig(): ViewConfig {
|
||||
return {
|
||||
title: localize('scene.scenes'),
|
||||
path: 'scenes',
|
||||
icon: 'mdi:palette',
|
||||
/**
|
||||
* Default configuration of the view.
|
||||
*
|
||||
* @type {views.ViewConfig}
|
||||
* @private
|
||||
*/
|
||||
#defaultConfig: views.ViewConfig = {
|
||||
title: Helper.customLocalize("scene.scenes"),
|
||||
path: "scenes",
|
||||
icon: "mdi:palette",
|
||||
subview: false,
|
||||
headerCardConfiguration: {
|
||||
controllerCardOptions: {
|
||||
showControls: false,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/** Returns the default configuration of the view's Header card. */
|
||||
static getViewHeaderCardConfig(): CustomHeaderCardConfig {
|
||||
return {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param {ViewConfig} [customConfiguration] Custom view configuration.
|
||||
* @param {views.ViewConfig} [options={}] Options for the view.
|
||||
*/
|
||||
constructor(customConfiguration?: ViewConfig) {
|
||||
super();
|
||||
constructor(options: views.ViewConfig = {}) {
|
||||
super(SceneView.#domain);
|
||||
|
||||
this.initializeViewConfig(SceneView.getDefaultConfig(), customConfiguration, SceneView.getViewHeaderCardConfig());
|
||||
this.config = Object.assign(this.config, this.#defaultConfig, options);
|
||||
}
|
||||
}
|
||||
|
||||
export default SceneView;
|
||||
export {SceneView};
|
||||
|
@@ -1,56 +1,80 @@
|
||||
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.
|
||||
|
||||
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.
|
||||
*
|
||||
* Used to create a view configuration for entities of the switch domain.
|
||||
* Used to create a view for entities of the switch domain.
|
||||
*
|
||||
* @class SwitchView
|
||||
* @extends AbstractView
|
||||
*/
|
||||
class SwitchView extends AbstractView {
|
||||
/** The domain of the entities that the view is representing. */
|
||||
static readonly domain = 'switch' as const;
|
||||
/**
|
||||
* Domain of the view's entities.
|
||||
*
|
||||
* @type {SupportedDomains}
|
||||
* @static
|
||||
* @private
|
||||
*/
|
||||
static #domain: SupportedDomains = "switch";
|
||||
|
||||
/** Returns the default configuration object for the view. */
|
||||
static getDefaultConfig(): ViewConfig {
|
||||
return {
|
||||
title: localize('switch.switches'),
|
||||
path: 'switches',
|
||||
icon: 'mdi:dip-switch',
|
||||
/**
|
||||
* Default configuration of the view.
|
||||
*
|
||||
* @type {views.ViewConfig}
|
||||
* @private
|
||||
*/
|
||||
#defaultConfig: views.ViewConfig = {
|
||||
title: Helper.customLocalize("switch.switches"),
|
||||
path: "switches",
|
||||
icon: "mdi:dip-switch",
|
||||
subview: false,
|
||||
headerCardConfiguration: {
|
||||
iconOn: 'mdi:power-plug',
|
||||
iconOff: 'mdi:power-plug-off',
|
||||
onService: 'switch.turn_on',
|
||||
offService: 'switch.turn_off',
|
||||
controllerCardOptions: {
|
||||
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. */
|
||||
static getViewHeaderCardConfig(): CustomHeaderCardConfig {
|
||||
return {
|
||||
title: localize('switch.all_switches'),
|
||||
/**
|
||||
* Default configuration of the view's Controller card.
|
||||
*
|
||||
* @type {cards.ControllerCardOptions}
|
||||
* @private
|
||||
*/
|
||||
#viewControllerCardConfig: cards.ControllerCardOptions = {
|
||||
title: Helper.customLocalize("switch.all_switches"),
|
||||
subtitle:
|
||||
`${Registry.getCountTemplate(SwitchView.domain, 'eq', 'on')} ${localize('switch.switches')} ` +
|
||||
localize('generic.on'),
|
||||
`${Helper.getCountTemplate(SwitchView.#domain, "eq", "on")} ${Helper.customLocalize("switch.switches")} `
|
||||
+ Helper.customLocalize("generic.on"),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param {ViewConfig} [customConfiguration] Custom view configuration.
|
||||
* @param {views.ViewConfig} [options={}] Options for the view.
|
||||
*/
|
||||
constructor(customConfiguration?: ViewConfig) {
|
||||
super();
|
||||
constructor(options: views.ViewConfig = {}) {
|
||||
super(SwitchView.#domain);
|
||||
|
||||
this.initializeViewConfig(SwitchView.getDefaultConfig(), customConfiguration, SwitchView.getViewHeaderCardConfig());
|
||||
this.config = Object.assign(this.config, this.#defaultConfig, options);
|
||||
|
||||
// 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 default SwitchView;
|
||||
export {SwitchView};
|
||||
|
@@ -1,56 +1,80 @@
|
||||
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.
|
||||
|
||||
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.
|
||||
*
|
||||
* Used to create a view configuration for entities of the vacuum domain.
|
||||
* Used to create a view for entities of the vacuum domain.
|
||||
*
|
||||
* @class VacuumView
|
||||
* @extends AbstractView
|
||||
*/
|
||||
class VacuumView extends AbstractView {
|
||||
/** The domain of the entities that the view is representing. */
|
||||
static readonly domain = 'vacuum' as const;
|
||||
/**
|
||||
* Domain of the view's entities.
|
||||
*
|
||||
* @type {SupportedDomains}
|
||||
* @static
|
||||
* @private
|
||||
*/
|
||||
static #domain: SupportedDomains = "vacuum";
|
||||
|
||||
/** Returns the default configuration object for the view. */
|
||||
static getDefaultConfig(): ViewConfig {
|
||||
return {
|
||||
title: localize('vacuum.vacuums'),
|
||||
path: 'vacuums',
|
||||
icon: 'mdi:robot-vacuum',
|
||||
/**
|
||||
* Default configuration of the view.
|
||||
*
|
||||
* @type {views.ViewConfig}
|
||||
* @private
|
||||
*/
|
||||
#defaultConfig: views.ViewConfig = {
|
||||
title: Helper.customLocalize("vacuum.vacuums"),
|
||||
path: "vacuums",
|
||||
icon: "mdi:robot-vacuum",
|
||||
subview: false,
|
||||
headerCardConfiguration: {
|
||||
iconOn: 'mdi:robot-vacuum',
|
||||
iconOff: 'mdi:robot-vacuum-off',
|
||||
onService: 'vacuum.start',
|
||||
offService: 'vacuum.stop',
|
||||
controllerCardOptions: {
|
||||
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. */
|
||||
static getViewHeaderCardConfig(): CustomHeaderCardConfig {
|
||||
return {
|
||||
title: localize('vacuum.all_vacuums'),
|
||||
/**
|
||||
* Default configuration of the view's Controller card.
|
||||
*
|
||||
* @type {cards.ControllerCardOptions}
|
||||
* @private
|
||||
*/
|
||||
#viewControllerCardConfig: cards.ControllerCardOptions = {
|
||||
title: Helper.customLocalize("vacuum.all_vacuums"),
|
||||
subtitle:
|
||||
`${Registry.getCountTemplate(VacuumView.domain, 'in', '[cleaning, returning]')} ${localize('vacuum.vacuums')} ` +
|
||||
localize('generic.busy'),
|
||||
`${Helper.getCountTemplate(VacuumView.#domain, "ne", "off")} ${Helper.customLocalize("vacuum.vacuums")} `
|
||||
+ Helper.customLocalize("generic.busy"),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param {ViewConfig} [customConfiguration] Custom view configuration.
|
||||
* @param {views.ViewConfig} [options={}] Options for the view.
|
||||
*/
|
||||
constructor(customConfiguration?: ViewConfig) {
|
||||
super();
|
||||
constructor(options: views.ViewConfig = {}) {
|
||||
super(VacuumView.#domain);
|
||||
|
||||
this.initializeViewConfig(VacuumView.getDefaultConfig(), customConfiguration, VacuumView.getViewHeaderCardConfig());
|
||||
this.config = Object.assign(this.config, this.#defaultConfig, options);
|
||||
|
||||
// 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 default VacuumView;
|
||||
export {VacuumView};
|
||||
|
Reference in New Issue
Block a user