Add options for all areas (#52)

Area identifier `_` sets the options for all areas.
Options for a specific area can still be overridden like before.

Also the HA area-card is added to use as a strategy area-card.

---------

Co-authored-by: DigiLive <info@digilive.nl>
This commit is contained in:
Johan Frick
2023-10-13 08:12:11 +02:00
committed by GitHub
parent 8311fa240c
commit 98ef9fed05
8 changed files with 152 additions and 42 deletions

View File

@@ -75,7 +75,7 @@ All the rounded cards can be configured using the Dashboard UI editor.
```yaml
strategy:
type: custom:mushroom-strategy
views: [ ]
views: []
```
### Hidding specific entities
@@ -133,7 +133,7 @@ strategy:
name: Family Room
icon: mdi:sofa
icon_color: green
views: [ ]
views: []
```
### Area Object
@@ -161,6 +161,7 @@ at the top of the area subview.
| `hidden` | boolean | false | Set to `true` to exclude the area from the dashboard and views. |
| `order` | number | Infinity | Ordering position of the area in the list of available areas. |
| `extra_cards` | array of cards | unset or empty | A list of cards to show on the top of the area subview. |
| `type` | string | `default` | Set to a type of area card. (Currently supported: `default` & `HaAreaCard` |
*) `more-info` `toggle` `call-service` `navigate` `url` `none`
@@ -191,7 +192,9 @@ strategy:
order: 2
garage_id:
hidden: true
views: [ ]
hallway_id:
type: HaAreaCard
views: []
```
#### Undisclosed Area
@@ -202,6 +205,22 @@ This area is enabled by default and includes the entities that aren't linked to
The area can be configured like any other area as described above.
To exclude this area from the dashboard and views, set its property `hidden` to `true`.
#### Setting options for all areas
Use `_` as an identifier to set the options for all areas.
The following example sets the type of all area-cards to Home Assistant's area card:
```yaml
strategy:
type: custom:mushroom-strategy
options:
areas:
_:
type: HaAreaCard
views: []
```
### Card Options
The `card_options` entry enables you to specify a card type for an entity or to hide the card from the dashboard.
@@ -222,7 +241,7 @@ strategy:
077ba0492c9bb3b31ffac34f1f3a626a:
hidden: true
views: [ ]
views: []
```
### Pre-built views
@@ -230,7 +249,7 @@ views: [ ]
![Light Views](./docs/light_view.png)
Mushroom strategy includes pre-built views to control/view specific domains.
All devices that are in an area where `hidden` is set to false/undefined are shown.
All devices that are in an area where `hidden` is set to false/undefined are shown.
By default, all pre-built views below are shown:
@@ -270,7 +289,7 @@ strategy:
order: 1
hidden: true
icon: mdi:toggle-switch
views: [ ]
views: []
```
### Supported domains
@@ -312,7 +331,7 @@ strategy:
showControls: false
default:
hidden: true
views: [ ]
views: []
```
### Chips
@@ -475,7 +494,7 @@ strategy:
title: cool view
path: cool-view
icon: mdi:emoticon-cool
badges: [ ]
badges: []
cards:
- type: markdown
content: I am cool

File diff suppressed because one or more lines are too long

View File

@@ -38,9 +38,15 @@ class AreaCard extends AbstractCard {
*/
constructor(area, options = {}) {
super(area);
this.#defaultOptions.primary = area.name;
this.#defaultOptions.tap_action.navigation_path = area.area_id ?? area.name;
// Set card type to default if a type "default" is given in strategy options.
if (options.type === "default") {
options.type = this.#defaultOptions.type;
}
this.mergeOptions(
this.#defaultOptions,
options,

47
src/cards/HaAreaCard.js Normal file
View File

@@ -0,0 +1,47 @@
import {AbstractCard} from "./AbstractCard";
/**
* HA Area Card Class
*
* 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 {
/**
* Default options of the card.
*
* @type {HaAreaCardOptions}
* @private
*/
#defaultOptions = {
type: "area",
area: undefined,
navigation_path: undefined,
};
/**
* Class constructor.
*
* @param {areaEntity} area The area entity to create a card for.
* @param {HaAreaCardOptions} [options={}] Options for the card.
* @throws {Error} If the Helper module isn't initialized.
*/
constructor(area, options = {}) {
super(area);
this.#defaultOptions.area = area.area_id ?? area.name;
this.#defaultOptions.navigation_path = area.area_id ?? area.name;
// Enforce the card type.
options.type = this.#defaultOptions.type;
this.mergeOptions(
this.#defaultOptions,
options,
);
}
}
export {AreaCard};

View File

@@ -112,6 +112,13 @@
* @memberOf typedefs.cards
*/
/**
* @typedef {abstractOptions & Object} HaAreaCardOptions HA Area Card options.
* @property {string} area The id of the area.
* @property {string} navigation_path The id of the area to navigate to.
* @memberOf typedefs.cards
*/
/**
* @typedef {abstractOptions & Object} mediaPlayerCardOptions Media Player Card options.
* @property {boolean} [use_media_info=true] Use media info instead of name, state, and icon when a media is playing

View File

@@ -191,12 +191,11 @@ class MushroomStrategy {
}
if (!Helper.strategyOptions.domains.default.hidden) {
// TODO: Check if default is hidden
// Create cards for any other domain.
// Collect device entities of the current area.
const areaDevices = Helper.devices.filter(device => device.area_id === area.area_id)
.map(device => device.id);
// Collect the remaining entities of which all conditions below are met:
// 1. The entity is linked to a device which is linked to the current area,
// or the entity itself is linked to the current area.
@@ -207,33 +206,33 @@ class MushroomStrategy {
&& entity.disabled_by == null
&& !exposedDomainIds.includes(entity.entity_id.split(".", 1)[0]);
});
// Create a column of miscellaneous entity cards.
if (miscellaneousEntities.length) {
let miscellaneousCards = [];
try {
miscellaneousCards = await import("./cards/MiscellaneousCard").then(cardModule => {
/** @type Object[] */
const miscellaneousCards = [
new TitleCard([area], Helper.strategyOptions.domains.default).createCard(),
];
for (const entity of miscellaneousEntities) {
let cardOptions = Helper.strategyOptions.card_options?.[entity.entity_id] ?? {};
let deviceOptions = Helper.strategyOptions.card_options?.[entity.device_id] ?? {};
if (!cardOptions.hidden && !deviceOptions.hidden) {
miscellaneousCards.push(new cardModule.MiscellaneousCard(entity, cardOptions).getCard());
}
}
return miscellaneousCards;
});
} catch (e) {
console.error(Helper.debug ? e : "An error occurred while creating the domain cards!");
}
viewCards.push({
type: "vertical-stack",
cards: miscellaneousCards,

View File

@@ -35,6 +35,7 @@
* @property {Object[]} [extra_cards] An array of card configurations.
* The configured cards are added to the dashboard.
* This property is added by the custom strategy.
* @property {boolean} [use_ha_area_card] Set to true to use ha area card instead of mushroom.
* @memberOf typedefs.generic
*/

View File

@@ -82,9 +82,9 @@ class HomeView extends AbstractView {
// Add area cards.
homeViewCards.push({
type: "vertical-stack",
cards: areaCards,
});
type: "vertical-stack",
cards: areaCards,
});
// Add custom cards.
if (options.extra_cards) {
@@ -158,10 +158,10 @@ class HomeView extends AbstractView {
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
})) {
return entity.entity_id.startsWith("person.")
&& entity.hidden_by == null
&& entity.disabled_by == null;
})) {
cards.push(new personModule.PersonCard(person).getCard());
}
});
@@ -176,30 +176,61 @@ class HomeView extends AbstractView {
*
* @return {Object[]} A card object array.
*/
#createAreaCards() {
const groupedCards = [{
type: "custom:mushroom-title-card",
title: "Areas",
}];
async #createAreaCards() {
/**
* Cards to be stacked vertically.
*
* Contains a Title card and horizontal stacks of Area cards.
*
* @type {[{}]}
*/
const groupedCards = [
{
type: "custom:mushroom-title-card",
title: "Areas",
},
];
let areaCards = [];
import("../cards/AreaCard").then(areaModule => {
const areaCards = [];
for (const [i, area] of Helper.areas.entries()) {
let module;
let moduleName =
Helper.strategyOptions.areas[area.area_id ?? "undisclosed"]?.type ??
Helper.strategyOptions.areas["_"]?.type ??
"default";
for (const area of Helper.areas) {
if (!Helper.strategyOptions.areas[area.area_id]?.hidden) {
areaCards.push(
new areaModule.AreaCard(area, Helper.strategyOptions.areas[area.area_id ?? "undisclosed"]).getCard());
// Load module by type in strategy options.
try {
module = await import((`../cards/${moduleName}`));
} catch (e) {
// Fallback to the default strategy card.
module = await import("../cards/AreaCard");
if (Helper.strategyOptions.debug && moduleName !== "default") {
console.error(e);
}
}
// Horizontally group every two area cards.
for (let i = 0; i < areaCards.length; i += 2) {
groupedCards.push({
type: "horizontal-stack",
cards: areaCards.slice(i, i + 2),
});
// Get a card for the area.
if (!Helper.strategyOptions.areas[area.area_id]?.hidden) {
let options = {
...Helper.strategyOptions.areas["_"],
...Helper.strategyOptions.areas[area.area_id ?? "undisclosed"],
};
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),
});
}
}
}
return groupedCards;
}