Merge branch 'add-cards-per-row' into add-device-view

# Conflicts:
#	README.md
#	dist/mushroom-strategy.js
#	package-lock.json
#	package.json
#	src/Registry.ts
#	src/cards/HeaderCard.ts
#	src/configurationDefaults.ts
#	src/mushroom-strategy.ts
#	src/types/strategy/strategy-cards.ts
#	src/types/strategy/strategy-generics.ts
#	src/utilities/RegistryFilter.ts
#	src/views/AbstractView.ts
#	src/views/CameraView.ts
#	src/views/HomeView.ts
#	src/views/VacuumView.ts
This commit is contained in:
DigiLive
2025-05-18 12:24:15 +02:00
26 changed files with 421 additions and 209 deletions

View File

@ -5,7 +5,7 @@ Please fill out the following information to help us review your pull request.
---
### Bug Summary
## Bug Summary
Explain why this fix is needed and what problem it solves.
If it relates to an existing issue, please link it.
@ -15,7 +15,7 @@ See [Linking a pull request to an issue](https://docs.github.com/en/issues/track
---
### Motivation and Context
## Motivation and Context
Explain why this bug needs to be fixed and the impact it has on the project or users.
@ -23,20 +23,20 @@ Explain why this bug needs to be fixed and the impact it has on the project or u
---
### List of Changes
## List of Changes
[Provide a concise list of the main changes introduced by this pull request to fix the bug.]
- ...
### Steps to Reproduce (if not covered in the linked issue)
## Steps to Reproduce (if not covered in the linked issue)
If the steps to reproduce the bug are not clearly outlined in the linked issue, please provide them here:
1. ...
2. ...
### Expected Behavior (if not covered in the linked issue)
## Expected Behavior (if not covered in the linked issue)
Describe what the expected behavior should have been before the bug occurred.
@ -50,13 +50,13 @@ Describe the actual behavior that occurred due to the bug.
---
### Wiki Updates
## Wiki Updates
[If this bug fix requires any updates to the Wiki, please provide details here.]
---
### Agreements
## Agreements
Please confirm the following by inserting an `x` between the brackets:

View File

@ -5,13 +5,13 @@ Please fill out the following information to help us review your pull request.
---
### Feature Summary
## Feature Summary
[Briefly describe the feature you are proposing]
---
### Motivation and Context
## Motivation and Context
Explain why this feature is needed and what problem it solves.
If it relates to an existing issue, please link it.
@ -21,19 +21,19 @@ See [Linking a pull request to an issue](https://docs.github.com/en/issues/track
---
### List of Changes
## List of Changes
[Provide a concise list of the main changes introduced by this pull request.]
- ...
### Wiki Updates
## Wiki Updates
[If this bug feature requires any updates to the Wiki, please provide details here.]
---
### Agreements
## Agreements
Please confirm the following by inserting an `x` between the brackets:

View File

@ -5,7 +5,7 @@ Please fill out the following information to help us review your translation cha
---
### Type of Translation Contribution
## Type of Translation Contribution
Please select the type of contribution:
@ -15,7 +15,7 @@ Please select the type of contribution:
---
### Target Language
## Target Language
Please specify the language you are adding or modifying:
@ -23,7 +23,7 @@ Please specify the language you are adding or modifying:
---
### Motivation and Context
## Motivation and Context
Explain why this translation (addition, fix, or update) is needed.
- For fixes, please describe the original error.
@ -33,7 +33,7 @@ Explain why this translation (addition, fix, or update) is needed.
---
### Scope of Changes
## Scope of Changes
Please describe the scope of your translation changes.
Which parts of the project are affected by these translations?
@ -42,7 +42,7 @@ Which parts of the project are affected by these translations?
---
### List of Changes
## List of Changes
Provide a concise list of the main changes you've made in this pull request.
If it's a large update, you can highlight key areas.
@ -51,7 +51,7 @@ If it's a large update, you can highlight key areas.
---
### Considerations for Reviewers
## Considerations for Reviewers
Are there any specific areas you would like reviewers to pay extra attention to?
For example, specific terminology, cultural nuances, or consistency with existing translations.
@ -60,7 +60,7 @@ For example, specific terminology, cultural nuances, or consistency with existin
---
### Agreements
## Agreements
Please confirm the following:

26
.github/dependabot.yml vendored Normal file
View File

@ -0,0 +1,26 @@
# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
version: 2
updates:
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "weekly"
groups:
dependencies:
patterns: ["*"]
labels:
- "dependencies"
pull-request-branch-name:
separator: "-"
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
groups:
actions:
patterns: ["*"]
labels:
- "actions"
pull-request-branch-name:
separator: "-"

View File

@ -9,6 +9,6 @@ jobs:
runs-on: "ubuntu-latest"
steps:
- name: HACS Action
uses: "hacs/action@main"
uses: "hacs/action@22.5.0"
with:
category: "plugin"

View File

@ -11,24 +11,29 @@ on:
jobs:
build:
name: Build Distribution
runs-on: ubuntu-latest
env:
CI_COMMIT_MESSAGE: Continuous Integration - Build Distribution
CI_COMMIT_MESSAGE: |
Continuous Integration - Build Distribution
[skip codacy]
CI_COMMIT_AUTHOR: Continuous Integration
strategy:
matrix:
node-version: [ 18.x ]
node-version: [22.x]
# Checkout Repository
steps:
- uses: actions/checkout@v3
- name: Checkout Repository
uses: actions/checkout@v4
with:
token: ${{ secrets.WORKFLOW_GIT_ACCESS_TOKEN }}
# Build steps
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
- name: Setup Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
@ -44,9 +49,9 @@ jobs:
run: |
git diff --quiet . || echo "changed=true" >> $GITHUB_OUTPUT
# Commit and push all changed files.
# Commit and push all changed files.
# Must only affect files that are listed in "paths-ignore".
- name: GIT Commit Distribution Build
- name: Commit Distribution Build
# Only run on a main branch push (e.g., pull request merge).
if: github.event_name == 'push' && steps.checkDiff.outputs.changed == 'true'
run: |

View File

@ -153,7 +153,7 @@ Enhancement suggestions are tracked as [GitHub issues][issuesUrl].
### Code Contribution
You can contribute to this project by following the _fork → clone → edit → pull request_ workflow of GitHub.
You can contribute to this project by following the *fork → clone → edit → pull request* workflow of GitHub.
#### Prevent changes to the distribution directory

View File

@ -1,7 +1,8 @@
# Mushroom dashboard strategy
[![release][releaseBadge]][releaseUrl]
[![hacs][hacsBadge]][hacsUrl]
[![Release][releaseBadge]][releaseUrl]
[![HACS][hacsBadge]][hacsUrl]
[![Codacy][codacyBadge]][codacyUrl]
![Preview GIF](./docs/preview.gif)
@ -26,10 +27,10 @@ For easy access, separate views are generated for entities which belong to speci
### Features
- 🛠 Automatically create a dashboard with three lines of YAML.
- 😍 Built-in Views for device-specific controls.
- 🎨 Many options to customize to fit your needs.
- 📈 [Mini graph][miniGraphUrl] cards for sensor entities.
* 🛠 Automatically create a dashboard with three lines of YAML.
* 😍 Built-in Views for device-specific controls.
* 🎨 Many options to customize to fit your needs.
* 📈 [Mini graph][miniGraphUrl] cards for sensor entities.
> [!TIP]
> If you like this package, please star the [project at GitHub][repositoryUrl]!
@ -59,17 +60,21 @@ Visit the [issues][issuesUrl] page.
<!-- Badge References -->
[codacyBadge]: https://app.codacy.com/project/badge/Grade/24de1e79aea445499917d9acd5ce9e04
[hacsBadge]: https://img.shields.io/badge/HACS-Default-blue
[releaseBadge]: https://img.shields.io/github/v/tag/digilive/mushroom-strategy?filter=v2.3.3-alpha.1&label=Release
[sponsorBadge]: https://img.shields.io/badge/Sponsor_him-%E2%9D%A4-%23db61a2.svg?&logo=github&color=%23fe8e86
[releaseBadge]: https://img.shields.io/github/v/tag/digilive/mushroom-strategy?filter=v2.3.0-alpha.1&label=Release
<!-- Repository References -->
[codacyUrl]: https://app.codacy.com/gh/DigiLive/mushroom-strategy/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade
[repositoryUrl]: https://github.com/DigiLive/mushroom-strategy
[releaseUrl]: https://github.com/DigiLive/mushroom-strategy/releases/tag/v2.3.0-alpha.1
[releaseUrl]: https://github.com/DigiLive/mushroom-strategy/releases/tag/v2.3.3-alpha.1
[issuesUrl]: https://github.com/DigiLive/mushroom-strategy/issues

182
package-lock.json generated
View File

@ -1,22 +1,22 @@
{
"name": "mushroom-strategy",
"version": "2.3.0-alpha.1",
"version": "2.3.3-alpha.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "mushroom-strategy",
"version": "2.3.0-alpha.1",
"version": "2.3.2",
"license": "MIT",
"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",
"@typescript-eslint/eslint-plugin": "^8.32.1",
"@typescript-eslint/parser": "^8.32.1",
"eslint": "^9.27.0",
"eslint-config-prettier": "^10.1.5",
"eslint-plugin-prettier": "^5.4.0",
"home-assistant-js-websocket": "^9.5.0",
"prettier": "^3.5.3",
"superstruct": "^2.0.2",
@ -24,7 +24,7 @@
"ts-node": "^10.9.2",
"typescript": "^5.8.3",
"version-bump-prompt": "^6",
"webpack": "^5.99.7",
"webpack": "^5.99.8",
"webpack-cli": "^6.0.1"
},
"funding": {
@ -65,9 +65,9 @@
}
},
"node_modules/@eslint-community/eslint-utils": {
"version": "4.6.1",
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.6.1.tgz",
"integrity": "sha512-KTsJMmobmbrFLe3LDh0PC2FXpcSYJt/MLjlkh/9LEnmKYLSYmT/0EW9JWANjeoemiuZrmogti0tW5Ch+qNUYDw==",
"version": "4.7.0",
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz",
"integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -143,9 +143,9 @@
}
},
"node_modules/@eslint/core": {
"version": "0.13.0",
"resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.13.0.tgz",
"integrity": "sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==",
"version": "0.14.0",
"resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.14.0.tgz",
"integrity": "sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
@ -204,13 +204,16 @@
}
},
"node_modules/@eslint/js": {
"version": "9.25.1",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.25.1.tgz",
"integrity": "sha512-dEIwmjntEx8u3Uvv+kr3PDeeArL8Hw07H9kyYxCjnM9pBjfEhk6uLXSchxxzgiwtRhhzVzqmUSDFBOi1TuZ7qg==",
"version": "9.27.0",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.27.0.tgz",
"integrity": "sha512-G5JD9Tu5HJEu4z2Uo4aHY2sLV64B7CDMXxFzqzjl3NKd6RVzSXNoE80jk7Y0lJkTTkjiIhBAqmlYwjuBY3tvpA==",
"dev": true,
"license": "MIT",
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"url": "https://eslint.org/donate"
}
},
"node_modules/@eslint/object-schema": {
@ -224,13 +227,13 @@
}
},
"node_modules/@eslint/plugin-kit": {
"version": "0.2.8",
"resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.8.tgz",
"integrity": "sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA==",
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.1.tgz",
"integrity": "sha512-0J+zgWxHN+xXONWIyPWKFMgVuJoZuGiIFu8yxk7RJjxkzpGmyja5wRFqZIVtjDVOQpV+Rw0iOAjYPE2eQyjr0w==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"@eslint/core": "^0.13.0",
"@eslint/core": "^0.14.0",
"levn": "^0.4.1"
},
"engines": {
@ -520,21 +523,21 @@
}
},
"node_modules/@typescript-eslint/eslint-plugin": {
"version": "8.31.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.31.0.tgz",
"integrity": "sha512-evaQJZ/J/S4wisevDvC1KFZkPzRetH8kYZbkgcTRyql3mcKsf+ZFDV1BVWUGTCAW5pQHoqn5gK5b8kn7ou9aFQ==",
"version": "8.32.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.32.1.tgz",
"integrity": "sha512-6u6Plg9nP/J1GRpe/vcjjabo6Uc5YQPAMxsgQyGC/I0RuukiG1wIe3+Vtg3IrSCVJDmqK3j8adrtzXSENRtFgg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/regexpp": "^4.10.0",
"@typescript-eslint/scope-manager": "8.31.0",
"@typescript-eslint/type-utils": "8.31.0",
"@typescript-eslint/utils": "8.31.0",
"@typescript-eslint/visitor-keys": "8.31.0",
"@typescript-eslint/scope-manager": "8.32.1",
"@typescript-eslint/type-utils": "8.32.1",
"@typescript-eslint/utils": "8.32.1",
"@typescript-eslint/visitor-keys": "8.32.1",
"graphemer": "^1.4.0",
"ignore": "^5.3.1",
"ignore": "^7.0.0",
"natural-compare": "^1.4.0",
"ts-api-utils": "^2.0.1"
"ts-api-utils": "^2.1.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@ -549,17 +552,27 @@
"typescript": ">=4.8.4 <5.9.0"
}
},
"node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": {
"version": "7.0.4",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.4.tgz",
"integrity": "sha512-gJzzk+PQNznz8ysRrC0aOkBNVRBDtE1n53IqyqEf3PXrYwomFs5q4pGMizBMJF+ykh03insJ27hB8gSrD2Hn8A==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 4"
}
},
"node_modules/@typescript-eslint/parser": {
"version": "8.31.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.31.0.tgz",
"integrity": "sha512-67kYYShjBR0jNI5vsf/c3WG4u+zDnCTHTPqVMQguffaWWFs7artgwKmfwdifl+r6XyM5LYLas/dInj2T0SgJyw==",
"version": "8.32.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.32.1.tgz",
"integrity": "sha512-LKMrmwCPoLhM45Z00O1ulb6jwyVr2kr3XJp+G+tSEZcbauNnScewcQwtJqXDhXeYPDEjZ8C1SjXm015CirEmGg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/scope-manager": "8.31.0",
"@typescript-eslint/types": "8.31.0",
"@typescript-eslint/typescript-estree": "8.31.0",
"@typescript-eslint/visitor-keys": "8.31.0",
"@typescript-eslint/scope-manager": "8.32.1",
"@typescript-eslint/types": "8.32.1",
"@typescript-eslint/typescript-estree": "8.32.1",
"@typescript-eslint/visitor-keys": "8.32.1",
"debug": "^4.3.4"
},
"engines": {
@ -575,14 +588,14 @@
}
},
"node_modules/@typescript-eslint/scope-manager": {
"version": "8.31.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.31.0.tgz",
"integrity": "sha512-knO8UyF78Nt8O/B64i7TlGXod69ko7z6vJD9uhSlm0qkAbGeRUSudcm0+K/4CrRjrpiHfBCjMWlc08Vav1xwcw==",
"version": "8.32.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.32.1.tgz",
"integrity": "sha512-7IsIaIDeZn7kffk7qXC3o6Z4UblZJKV3UBpkvRNpr5NSyLji7tvTcvmnMNYuYLyh26mN8W723xpo3i4MlD33vA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.31.0",
"@typescript-eslint/visitor-keys": "8.31.0"
"@typescript-eslint/types": "8.32.1",
"@typescript-eslint/visitor-keys": "8.32.1"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@ -593,16 +606,16 @@
}
},
"node_modules/@typescript-eslint/type-utils": {
"version": "8.31.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.31.0.tgz",
"integrity": "sha512-DJ1N1GdjI7IS7uRlzJuEDCgDQix3ZVYVtgeWEyhyn4iaoitpMBX6Ndd488mXSx0xah/cONAkEaYyylDyAeHMHg==",
"version": "8.32.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.32.1.tgz",
"integrity": "sha512-mv9YpQGA8iIsl5KyUPi+FGLm7+bA4fgXaeRcFKRDRwDMu4iwrSHeDPipwueNXhdIIZltwCJv+NkxftECbIZWfA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/typescript-estree": "8.31.0",
"@typescript-eslint/utils": "8.31.0",
"@typescript-eslint/typescript-estree": "8.32.1",
"@typescript-eslint/utils": "8.32.1",
"debug": "^4.3.4",
"ts-api-utils": "^2.0.1"
"ts-api-utils": "^2.1.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@ -617,9 +630,9 @@
}
},
"node_modules/@typescript-eslint/types": {
"version": "8.31.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.31.0.tgz",
"integrity": "sha512-Ch8oSjVyYyJxPQk8pMiP2FFGYatqXQfQIaMp+TpuuLlDachRWpUAeEu1u9B/v/8LToehUIWyiKcA/w5hUFRKuQ==",
"version": "8.32.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.32.1.tgz",
"integrity": "sha512-YmybwXUJcgGqgAp6bEsgpPXEg6dcCyPyCSr0CAAueacR/CCBi25G3V8gGQ2kRzQRBNol7VQknxMs9HvVa9Rvfg==",
"dev": true,
"license": "MIT",
"engines": {
@ -631,20 +644,20 @@
}
},
"node_modules/@typescript-eslint/typescript-estree": {
"version": "8.31.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.31.0.tgz",
"integrity": "sha512-xLmgn4Yl46xi6aDSZ9KkyfhhtnYI15/CvHbpOy/eR5NWhK/BK8wc709KKwhAR0m4ZKRP7h07bm4BWUYOCuRpQQ==",
"version": "8.32.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.32.1.tgz",
"integrity": "sha512-Y3AP9EIfYwBb4kWGb+simvPaqQoT5oJuzzj9m0i6FCY6SPvlomY2Ei4UEMm7+FXtlNJbor80ximyslzaQF6xhg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.31.0",
"@typescript-eslint/visitor-keys": "8.31.0",
"@typescript-eslint/types": "8.32.1",
"@typescript-eslint/visitor-keys": "8.32.1",
"debug": "^4.3.4",
"fast-glob": "^3.3.2",
"is-glob": "^4.0.3",
"minimatch": "^9.0.4",
"semver": "^7.6.0",
"ts-api-utils": "^2.0.1"
"ts-api-utils": "^2.1.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@ -658,16 +671,16 @@
}
},
"node_modules/@typescript-eslint/utils": {
"version": "8.31.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.31.0.tgz",
"integrity": "sha512-qi6uPLt9cjTFxAb1zGNgTob4x9ur7xC6mHQJ8GwEzGMGE9tYniublmJaowOJ9V2jUzxrltTPfdG2nKlWsq0+Ww==",
"version": "8.32.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.32.1.tgz",
"integrity": "sha512-DsSFNIgLSrc89gpq1LJB7Hm1YpuhK086DRDJSNrewcGvYloWW1vZLHBTIvarKZDcAORIy/uWNx8Gad+4oMpkSA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.4.0",
"@typescript-eslint/scope-manager": "8.31.0",
"@typescript-eslint/types": "8.31.0",
"@typescript-eslint/typescript-estree": "8.31.0"
"@eslint-community/eslint-utils": "^4.7.0",
"@typescript-eslint/scope-manager": "8.32.1",
"@typescript-eslint/types": "8.32.1",
"@typescript-eslint/typescript-estree": "8.32.1"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@ -682,13 +695,13 @@
}
},
"node_modules/@typescript-eslint/visitor-keys": {
"version": "8.31.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.31.0.tgz",
"integrity": "sha512-QcGHmlRHWOl93o64ZUMNewCdwKGU6WItOU52H0djgNmn1EOrhVudrDzXz4OycCRSCPwFCDrE2iIt5vmuUdHxuQ==",
"version": "8.32.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.32.1.tgz",
"integrity": "sha512-ar0tjQfObzhSaW3C3QNmTc5ofj0hDoNQ5XWrCy6zDyabdr0TWhCkClp+rywGNj/odAFBVzzJrK4tEq5M4Hmu4w==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.31.0",
"@typescript-eslint/types": "8.32.1",
"eslint-visitor-keys": "^4.2.0"
},
"engines": {
@ -1481,9 +1494,9 @@
}
},
"node_modules/eslint": {
"version": "9.25.1",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.25.1.tgz",
"integrity": "sha512-E6Mtz9oGQWDCpV12319d59n4tx9zOTXSTmc8BLVxBx+G/0RdM5MvEEJLU9c0+aleoePYYgVTOsRblx433qmhWQ==",
"version": "9.27.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.27.0.tgz",
"integrity": "sha512-ixRawFQuMB9DZ7fjU3iGGganFDp3+45bPOdaRurcFHSXO1e/sYwUX/FtQZpLZJR6SjMoJH8hR2pPEAfDyCoU2Q==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -1491,10 +1504,10 @@
"@eslint-community/regexpp": "^4.12.1",
"@eslint/config-array": "^0.20.0",
"@eslint/config-helpers": "^0.2.1",
"@eslint/core": "^0.13.0",
"@eslint/core": "^0.14.0",
"@eslint/eslintrc": "^3.3.1",
"@eslint/js": "9.25.1",
"@eslint/plugin-kit": "^0.2.8",
"@eslint/js": "9.27.0",
"@eslint/plugin-kit": "^0.3.1",
"@humanfs/node": "^0.16.6",
"@humanwhocodes/module-importer": "^1.0.1",
"@humanwhocodes/retry": "^0.4.2",
@ -1542,22 +1555,25 @@
}
},
"node_modules/eslint-config-prettier": {
"version": "10.1.2",
"resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.2.tgz",
"integrity": "sha512-Epgp/EofAUeEpIdZkW60MHKvPyru1ruQJxPL+WIycnaPApuseK0Zpkrh/FwL9oIpQvIhJwV7ptOy0DWUjTlCiA==",
"version": "10.1.5",
"resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.5.tgz",
"integrity": "sha512-zc1UmCpNltmVY34vuLRV61r1K27sWuX39E+uyUnY8xS2Bex88VV9cugG+UZbRSRGtGyFboj+D8JODyme1plMpw==",
"dev": true,
"license": "MIT",
"bin": {
"eslint-config-prettier": "bin/cli.js"
},
"funding": {
"url": "https://opencollective.com/eslint-config-prettier"
},
"peerDependencies": {
"eslint": ">=7.0.0"
}
},
"node_modules/eslint-plugin-prettier": {
"version": "5.2.6",
"resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.6.tgz",
"integrity": "sha512-mUcf7QG2Tjk7H055Jk0lGBjbgDnfrvqjhXh9t2xLMSCjZVcw9Rb1V6sVNXO0th3jgeO7zllWPTNRil3JW94TnQ==",
"version": "5.4.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.4.0.tgz",
"integrity": "sha512-BvQOvUhkVQM1i63iMETK9Hjud9QhqBnbtT1Zc642p9ynzBuCe5pybkOnvqZIBypXmMlsGcnU4HZ8sCTPfpAexA==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -3612,9 +3628,9 @@
}
},
"node_modules/webpack": {
"version": "5.99.7",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.99.7.tgz",
"integrity": "sha512-CNqKBRMQjwcmKR0idID5va1qlhrqVUKpovi+Ec79ksW8ux7iS1+A6VqzfZXgVYCFRKl7XL5ap3ZoMpwBJxcg0w==",
"version": "5.99.8",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.99.8.tgz",
"integrity": "sha512-lQ3CPiSTpfOnrEGeXDwoq5hIGzSjmwD72GdfVzF7CQAI7t47rJG9eDWvcEkEn3CUQymAElVvDg3YNTlCYj+qUQ==",
"dev": true,
"license": "MIT",
"dependencies": {

View File

@ -1,6 +1,6 @@
{
"name": "mushroom-strategy",
"version": "2.3.0-alpha.1",
"version": "2.3.3-alpha.1",
"description": "Automatically generate a dashboard of Mushroom cards.",
"keywords": [
"dashboard",
@ -30,11 +30,11 @@
"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",
"@typescript-eslint/eslint-plugin": "^8.32.1",
"@typescript-eslint/parser": "^8.32.1",
"eslint": "^9.27.0",
"eslint-config-prettier": "^10.1.5",
"eslint-plugin-prettier": "^5.4.0",
"home-assistant-js-websocket": "^9.5.0",
"prettier": "^3.5.3",
"superstruct": "^2.0.2",
@ -42,7 +42,7 @@
"ts-node": "^10.9.2",
"typescript": "^5.8.3",
"version-bump-prompt": "^6",
"webpack": "^5.99.7",
"webpack": "^5.99.8",
"webpack-cli": "^6.0.1"
},
"scripts": {

View File

@ -187,7 +187,7 @@ class Registry {
}));
// Process entries of the HASS area registry.
if (Registry.strategyOptions.areas._?.hidden) {
if (Registry.strategyOptions.areas._.hidden) {
Registry._areas = [];
} else {
// Create and add the undisclosed area if not hidden in the strategy options.
@ -201,8 +201,9 @@ class Registry {
return { ...area, ...Registry.strategyOptions.areas['_'], ...Registry.strategyOptions.areas?.[area.area_id] };
});
// Ensure the custom configuration of the undisclosed area doesn't overwrite the area_id.
// Ensure the custom configuration of the undisclosed area doesn't overwrite the required property values.
Registry._strategyOptions.areas.undisclosed.area_id = 'undisclosed';
Registry.strategyOptions.areas.undisclosed.type = 'default';
// Remove hidden areas if configured as so and sort them by name.

View File

@ -28,8 +28,8 @@ class AreaCard extends AbstractCard {
configuration.tap_action.navigation_path = area.area_id;
}
// Don't override the default card type if default is set in the strategy options.
if (customConfig && customConfig.type === 'default') {
// Don't override the card type if set differently in the strategy options.
if (customConfig) {
customConfig = { ...customConfig, type: configuration.type };
}

View File

@ -6,6 +6,9 @@ import { localize } from './utilities/localize';
*/
export const ConfigurationDefaults: StrategyDefaults = {
areas: {
_: {
type: 'AreaCard',
},
undisclosed: {
// TODO: Refactor undisclosed to other.
aliases: [],
@ -43,11 +46,14 @@ export const ConfigurationDefaults: StrategyDefaults = {
_: {
hide_config_entities: undefined,
hide_diagnostic_entities: undefined,
showControls: true,
stack_count: 1,
},
binary_sensor: {
title: `${localize('sensor.binary')} ` + localize('sensor.sensors'),
showControls: false,
hidden: false,
stack_count: 2, // TODO: Add to wiki. also for other configurations.
},
camera: {
title: localize('camera.cameras'),
@ -169,6 +175,9 @@ export const ConfigurationDefaults: StrategyDefaults = {
extra_views: [],
home_view: {
hidden: [],
stack_count: {
_: 2,
},
},
views: {
camera: {

View File

@ -124,6 +124,6 @@ async function main() {
}
}
main().catch((error) => {
main().catch((_) => {
throw 'Mushroom Strategy - An error occurred. Check the console (F12) for details.';
});

View File

@ -0,0 +1,80 @@
{
"camera": {
"all_cameras": "Todas as câmeras",
"cameras": "Câmeras"
},
"climate": {
"all_climates": "Todos os climatizadores",
"climates": "Climatizadores"
},
"cover": {
"all_covers": "Todas as persianas",
"covers": "Persianas"
},
"fan": {
"all_fans": "Todos os ventiladores",
"fans": "Ventiladores"
},
"generic": {
"all": "Todos",
"areas": "Áreas",
"busy": "Ocupado",
"good_afternoon": "Boa tarde",
"good_evening": "Boa noite",
"good_morning": "Bom dia",
"hello": "Olá",
"home": "Início",
"miscellaneous": "Variados",
"numbers": "Números",
"off": "Desligado",
"on": "Ligado",
"open": "Aberto",
"unavailable": "Indisponível",
"unclosed": "Não fechado",
"undisclosed": "Outro",
"unknown": "Desconhecido"
},
"input_select": {
"input_selects": "Seleção de entrada"
},
"light": {
"all_lights": "Todas as luzes",
"lights": "Luzes"
},
"lock": {
"all_locks": "Todas as fechaduras",
"locked": "Travado",
"locks": "Fechaduras",
"unlocked": "Destravado"
},
"media_player": {
"media_players": "Reprodutores de mídia"
},
"scene": {
"scenes": "Cenas"
},
"select": {
"selects": "Seleção"
},
"sensor": {
"binary": "Binário",
"sensors": "Sensores"
},
"switch": {
"all_switches": "Todos os interruptores",
"switches": "Interruptores"
},
"vacuum": {
"all_vacuums": "Todos os aspiradores",
"vacuums": "Aspiradores"
},
"valve": {
"all_valves": "Todas as válvulas",
"valves": "Válvulas",
"open": "Aberto",
"opening": "Abrindo",
"closed": "Fechado",
"closing": "Fechando",
"stopped": "Parado"
}
}

View File

@ -13,7 +13,7 @@ Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
<http://www.apache.org/licenses/LICENSE-2.0>
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,

View File

@ -13,7 +13,7 @@ Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
<http://www.apache.org/licenses/LICENSE-2.0>
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,

View File

@ -1,4 +1,3 @@
import { AreaRegistryEntry } from '../homeassistant/data/area_registry';
import { DeviceRegistryEntry } from '../homeassistant/data/device_registry';
import { EntityRegistryEntry } from '../homeassistant/data/entity_registry';
import { LovelaceCardConfig } from '../homeassistant/data/lovelace/config/card';
@ -8,6 +7,7 @@ import { HomeAssistant } from '../homeassistant/types';
import { LovelaceChipConfig } from '../lovelace-mushroom/utils/lovelace/chip/types';
import { HeaderCardConfig } from './strategy-cards';
import { ConfigEntry } from '../homeassistant/data/config_entries';
import { AreaRegistryEntry } from '../homeassistant/data/area_registry';
/**
* List of supported domains.
@ -69,6 +69,7 @@ const SUPPORTED_CHIPS = ['light', 'fan', 'cover', 'switch', 'climate', 'weather'
*
* This constant array defines the sections that are present in the home view.
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const HOME_VIEW_SECTIONS = ['areas', 'areasTitle', 'chips', 'greeting', 'persons'] as const;
export type SupportedDomains = (typeof SUPPORTED_DOMAINS)[number];
@ -136,14 +137,16 @@ export interface ViewInfo {
/**
* All-Domains Configuration.
*
* @property {boolean | undefined} hide_config_entities - If True, all configuration entities are hidden from the
* dashboard.
* @property {boolean | undefined} hide_diagnostic_entities - If True, all diagnostic entities are hidden from the
* dashboard.
* @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.
* @property {boolean} [showControls] - False to hide controls.
* @property {number} [stack_count] - Number of cards per row.
*/
export interface AllDomainsConfig {
hide_config_entities: boolean | undefined;
hide_diagnostic_entities: boolean | undefined;
hide_config_entities?: boolean;
hide_diagnostic_entities?: boolean;
showControls?: boolean;
stack_count?: number;
}
/**
@ -151,12 +154,55 @@ export interface AllDomainsConfig {
*
* @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 view.
* @property {number} [stack_count] - Number of cards per row.
*/
export interface SingleDomainConfig extends Partial<HeaderCardConfig> {
hidden: boolean;
order?: number;
stack_count?: number;
}
/**
* Strategy Configuration.
*
* @property {Object.<string, StrategyArea>} areas - The configuration of areas.
* @property {Object.<string, CustomCardConfig>} card_options - Card options for entities.
* @property {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 {{ Object }} home_view - List of views to add to the dashboard.
* @property {Record<SupportedViews, 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: ChipConfiguration;
debug: boolean;
domains: { [K in SupportedDomains]: K extends '_' ? AllDomainsConfig : SingleDomainConfig };
extra_cards: LovelaceCardConfig[];
extra_views: StrategyViewConfig[];
home_view: {
hidden: HomeViewSections[];
stack_count: { _: number } & { [K in HomeViewSections]?: K extends 'areas' ? [number, number] : number };
};
views: Record<SupportedViews, StrategyViewConfig>;
quick_access_cards: LovelaceCardConfig[];
}
/**
* Represents the default configuration for a strategy.
*/
export type StrategyDefaults = Omit<StrategyConfig, 'areas'> & {
areas: {
_: AllAreasConfig;
undisclosed: StrategyArea;
};
};
/**
* Strategy Area.
*
@ -173,6 +219,15 @@ export interface StrategyArea extends AreaRegistryEntry {
type?: string;
}
/**
* Configuration for all areas.
*
* @property {string} [type] - The type of area card.
*/
export interface AllAreasConfig {
type?: string;
}
/**
* A list of chips to show in the Home view.
*

View File

@ -16,7 +16,7 @@ import { logMessage, lvlWarn } from './debug';
class RegistryFilter<T extends RegistryEntry, K extends keyof T = keyof T> {
private readonly entries: T[];
private filters: (((entry: T) => boolean) | ((entry: T, index: number) => boolean))[] = [];
private readonly entryIdentifier: ('entity_id' | 'floor_id' | 'entry_id' | 'id') & K;
private readonly entryIdentifier: ('entity_id' | 'area_id' | 'id') & K;
private invertNext: boolean = false;
/**
@ -27,14 +27,8 @@ class RegistryFilter<T extends RegistryEntry, K extends keyof T = keyof T> {
constructor(entries: T[]) {
this.entries = entries;
this.entryIdentifier = (
entries.length === 0 || 'entity_id' in entries[0]
? 'entity_id'
: 'floor_id' in entries[0]
? 'floor_id'
: 'entry_id' in entries[0]
? 'entry_id'
: 'id'
) as ('entity_id' | 'floor_id' | 'id') & K;
entries.length === 0 || 'entity_id' in entries[0] ? 'entity_id' : 'floor_id' in entries[0] ? 'area_id' : 'id'
) as ('entity_id' | 'area_id' | 'id') & K;
}
/**
@ -73,49 +67,36 @@ class RegistryFilter<T extends RegistryEntry, K extends keyof T = keyof T> {
}
/**
* Filters entries **strictly** by their `area_id`.
*
* - Entries with a matching `area_id` are kept.
* - If `expandToDevice` is `true`, the device's `area_id` is evaluated if the entry's area_id doesn't match.
* - If `areaId` is `undefined` (or omitted), entries without an `area_id` property are kept.
* Filters entries by their `area_id`.
*
* @param {string | undefined} areaId - The area id to match.
* @param {boolean} [expandToDevice=true] - Whether to use the device's `area_id` if the entry's doesn't match.
* @param {boolean} [expandToDevice=true] - Whether to evaluate the device's `area_id` (see remarks).
*
* @remarks
* For area id `undisclosed`, the `area_id` of the entry's device may be `undisclosed` or `undefined`.
* For entries with area id `undisclosed` or `undefined`, the device's `area_id` must also match if `expandToDevice`
* is `true`.
*/
whereAreaId(areaId?: string, expandToDevice: boolean = true): this {
const predicate = (entry: T) => {
if ('entry_id' in entry) {
return false;
}
let deviceAreaId: string | null | undefined = undefined;
const entryObject = entry as EntityRegistryEntry;
let deviceAreaId: string | null | undefined = undefined;
// Retrieve the device area ID only if expandToDevice is true
if (expandToDevice && entryObject.device_id) {
deviceAreaId = Registry.devices.find((device) => device.id === entryObject.device_id)?.area_id;
}
// Logic for 'undisclosed' areaId
if (areaId === 'undisclosed') {
return entry.area_id === areaId && (deviceAreaId === areaId || deviceAreaId === undefined);
}
// Logic for undefined areaId
if (areaId === undefined) {
return entry.area_id === undefined && (!expandToDevice || deviceAreaId === undefined);
return entry.area_id === undefined && deviceAreaId === undefined;
}
// Logic for any other areaId
return entry.area_id === areaId || (expandToDevice && deviceAreaId === areaId);
if (entry.area_id === 'undisclosed' || !entry.area_id) {
return deviceAreaId === areaId;
}
return entry.area_id === areaId;
};
this.filters.push(this.checkInversion(predicate));
return this;
}
@ -273,9 +254,12 @@ class RegistryFilter<T extends RegistryEntry, K extends keyof T = keyof T> {
}
const id = entry[this.entryIdentifier] as keyof StrategyConfig['card_options'];
const isHiddenByConfig =
Registry.strategyOptions.device_options['_'].hidden ||
Registry.strategyOptions.card_options[id]?.hidden === true;
const options =
this.entryIdentifier === 'area_id'
? { ...Registry.strategyOptions.areas['_'], ...Registry.strategyOptions.areas[id] }
: Registry.strategyOptions.card_options?.[id];
const isHiddenByConfig = options?.hidden === true;
return !isHiddenByProperty && !isHiddenByConfig;
};
@ -316,7 +300,9 @@ class RegistryFilter<T extends RegistryEntry, K extends keyof T = keyof T> {
const predicate = (entry: T) => {
const category = 'entity_category' in entry ? entry.entity_category : undefined;
const hideOption =
typeof category === 'string' ? Registry.strategyOptions.domains['_']?.[`hide_${category}_entities`] : undefined;
typeof category === 'string'
? Registry.strategyOptions?.domains?.['_']?.[`hide_${category}_entities`]
: undefined;
if (hideOption === true) {
return false;

View File

@ -1,6 +1,7 @@
import { LovelaceCardConfig } from '../types/homeassistant/data/lovelace/config/card';
import { StackCardConfig } from '../types/homeassistant/panels/lovelace/cards/types';
// noinspection GrazieInspection
/**
* Stacks an array of Lovelace card configurations into horizontal stacks based on their type.
*
@ -9,6 +10,7 @@ import { StackCardConfig } from '../types/homeassistant/panels/lovelace/cards/ty
* It returns a new array of stacked card configurations, preserving the original order of the cards.
*
* @param cardConfigurations - An array of Lovelace card configurations to be stacked.
* @param defaultCount - The default number of cards to stack if the type or column count is not found in the mapping.
* @param [columnCounts] - An object mapping card types to their respective column counts.
* If a type is not found in the mapping, it defaults to 2.
* @returns An array of stacked card configurations, where each configuration is a horizontal stack
@ -16,17 +18,26 @@ import { StackCardConfig } from '../types/homeassistant/panels/lovelace/cards/ty
*
* @example
* ```typescript
* stackedCards = stackHorizontal(card, {area: 1, "custom:card": 2});
* stackedCards = stackHorizontal(card, 2, {area: 1, 'custom:card': 2});
* ```
*/
export function stackHorizontal(
cardConfigurations: LovelaceCardConfig[],
defaultCount: number = 2,
columnCounts?: {
[key: string]: number;
[key: string]: number | undefined;
},
): LovelaceCardConfig[] {
if (cardConfigurations.length <= 1) {
return cardConfigurations;
}
// Function to process a sequence of cards
const doStack = (cards: LovelaceCardConfig[], columnCount: number) => {
if (cards.length <= 1) {
return cards;
}
const stackedCardConfigurations: StackCardConfig[] = [];
for (let i = 0; i < cards.length; i += columnCount) {
@ -44,7 +55,7 @@ export function stackHorizontal(
for (let i = 0; i < cardConfigurations.length; ) {
const currentCard = cardConfigurations[i];
const currentType = currentCard.type; // Assuming each card has a 'type' property
const currentType = currentCard.type;
// Start a new sequence
const sequence: LovelaceCardConfig[] = [];
@ -55,7 +66,7 @@ export function stackHorizontal(
i++; // Move to the next card
}
const columnCount = Math.max(columnCounts?.[currentType] || 2, 1);
const columnCount = Math.max(columnCounts?.[currentType] || defaultCount, 1);
// Process the sequence and add the result to the processedConfigurations array
processedConfigurations.push(...doStack(sequence, columnCount));

View File

@ -2,6 +2,7 @@ import * as de from '../translations/de.json';
import * as en from '../translations/en.json';
import * as es from '../translations/es.json';
import * as nl from '../translations/nl.json';
import * as pt_br from '../translations/pt-BR.json';
import { HomeAssistant } from '../types/homeassistant/types';
import { logMessage, lvlWarn } from './debug';
@ -11,6 +12,7 @@ const languages: Record<string, unknown> = {
en,
es,
nl,
'pt-BR': pt_br,
};
/** The fallback language if the user-defined language isn't defined */
@ -50,7 +52,7 @@ let _localize: ((key: string) => string) | undefined = undefined;
* It reads the user-defined language with a fall-back to English and returns a function to get strings from
* language-files by keyword.
*
* If the keyword is undefined, or on error, the keyword itself is returned.
* If the keyword is undefined, or on an error, the keyword itself is returned.
*
* @param {HomeAssistant} hass The Home Assistant object.
*/

View File

@ -9,6 +9,7 @@ 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 { stackHorizontal } from '../utilities/cardStacking';
import { AbstractCardConfig, HeaderCardConfig } from '../types/strategy/strategy-cards';
/**
@ -22,7 +23,7 @@ import { AbstractCardConfig, HeaderCardConfig } from '../types/strategy/strategy
*/
abstract class AbstractView {
/** The base configuration of a view. */
protected baseConfiguration: LovelaceViewConfig = {
protected baseConfiguration: ViewConfig = {
icon: 'mdi:view-dashboard',
subview: false,
};
@ -41,7 +42,7 @@ abstract class AbstractView {
*/
protected constructor() {
if (!Registry.initialized) {
logMessage(lvlFatal, 'Registry not initialized!');
logMessage(lvlFatal, 'Registry is not initialized!');
}
}
@ -75,7 +76,7 @@ abstract class AbstractView {
// Create card configurations for each area.
for (const area of Registry.areas) {
const areaCards: AbstractCardConfig[] = [];
let areaCards: AbstractCardConfig[] = [];
// Set the target of the Header card to the current area.
let target: HassServiceTarget = {
@ -97,8 +98,14 @@ abstract class AbstractView {
),
);
// Vertically stack the cards of the current area.
// Stack the cards of the current area.
if (areaCards.length) {
areaCards = stackHorizontal(
areaCards,
Registry.strategyOptions.domains[this.domain as SupportedDomains].stack_count ??
Registry.strategyOptions.domains['_'].stack_count,
);
// Create and insert a Header card.
const areaHeaderCardOptions = (
'headerCardConfiguration' in this.baseConfiguration ? this.baseConfiguration.headerCardConfiguration : {}
@ -132,10 +139,14 @@ abstract class AbstractView {
): void {
this.baseConfiguration = { ...this.baseConfiguration, ...viewConfiguration, ...customConfiguration };
this.baseConfiguration.headerCardConfiguration = {
showControls:
Registry.strategyOptions.domains[this.domain as Exclude<SupportedDomains, 'home'>]?.showControls ??
Registry.strategyOptions.domains['_'].showControls,
};
this.viewHeaderCardConfiguration = new HeaderCard(this.getDomainTargets(), {
...(('headerCardConfiguration' in this.baseConfiguration
? this.baseConfiguration.headerCardConfiguration
: {}) as HeaderCardConfig),
...(this.baseConfiguration.headerCardConfiguration as HeaderCardConfig),
...headerCardConfig,
}).createCard();
}

View File

@ -26,7 +26,7 @@ class CameraView extends AbstractView {
icon: 'mdi:cctv',
subview: false,
headerCardConfiguration: {
showControls: domainConfig.showControls,
showControls: domainConfig.showControls, // FIXME: This should be named "show_controls". Also in other files and Wiki.
on: domainConfig.on,
off: domainConfig.off,
},

View File

@ -78,7 +78,7 @@ class HomeView extends AbstractView {
}
// Create the greeting section.
if (!('greeting' in Registry.strategyOptions.home_view.hidden)) {
if (!Registry.strategyOptions.home_view.hidden.includes('greeting')) {
homeViewCards.push({
type: 'custom:mushroom-template-card',
primary: `{% set time = now().hour %}
@ -125,7 +125,7 @@ class HomeView extends AbstractView {
* If the section is marked as hidden in the strategy option, then the section is not created.
*/
private async createChipsSection(): Promise<ChipsCardConfig | undefined> {
if ((Registry.strategyOptions.home_view.hidden as string[]).includes('chips')) {
if (Registry.strategyOptions.home_view.hidden.includes('chips')) {
// The section is hidden.
return;
}
@ -193,9 +193,8 @@ class HomeView extends AbstractView {
* If the section is marked as hidden in the strategy option, then the section is not created.
*/
private async createPersonsSection(): Promise<StackCardConfig | undefined> {
if ((Registry.strategyOptions.home_view.hidden as string[]).includes('persons')) {
if (Registry.strategyOptions.home_view.hidden.includes('persons')) {
// The section is hidden.
return;
}
@ -210,7 +209,11 @@ class HomeView extends AbstractView {
return {
type: 'vertical-stack',
cards: stackHorizontal(cardConfigurations),
cards: stackHorizontal(
cardConfigurations,
Registry.strategyOptions.home_view.stack_count['persons'] ??
Registry.strategyOptions.home_view.stack_count['_'],
),
};
}
@ -221,24 +224,19 @@ class HomeView extends AbstractView {
* If the section is marked as hidden in the strategy option, then the section is not created.
*/
private async createAreasSection(): Promise<StackCardConfig | undefined> {
if ((Registry.strategyOptions.home_view.hidden as string[]).includes('areas')) {
if (Registry.strategyOptions.home_view.hidden.includes('areas')) {
// Areas section is hidden.
return;
}
const cardConfigurations: (TemplateCardConfig | AreaCardConfig)[] = [];
let onlyDefaultCards = true;
for (const area of Registry.areas) {
const moduleName =
Registry.strategyOptions.areas[area.area_id]?.type ?? Registry.strategyOptions.areas['_']?.type ?? 'default';
let AreaCard;
onlyDefaultCards = onlyDefaultCards && moduleName === 'default';
try {
AreaCard = (await import(`../cards/${moduleName}`)).default;
} catch (e) {
@ -250,15 +248,22 @@ class HomeView extends AbstractView {
}
}
cardConfigurations.push(new AreaCard(area).getCard());
cardConfigurations.push(
new AreaCard(area, {
...Registry.strategyOptions.areas['_'],
...Registry.strategyOptions.areas[area.area_id],
}).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 }),
title: Registry.strategyOptions.home_view.hidden.includes('areasTitle') ? undefined : localize('generic.areas'),
cards: stackHorizontal(cardConfigurations, Registry.strategyOptions.home_view.stack_count['_'], {
'custom:mushroom-template-card': Registry.strategyOptions.home_view.stack_count.areas?.[0],
area: Registry.strategyOptions.home_view.stack_count.areas?.[1],
}),
};
}
}

View File

@ -49,8 +49,8 @@ class VacuumView extends AbstractView {
return {
title: localize('vacuum.all_vacuums'),
subtitle:
`${Registry.getCountTemplate(VacuumView.domain, 'in', '[cleaning, returning]')} ${localize('vacuum.vacuums')} ` +
localize('generic.busy'),
Registry.getCountTemplate(VacuumView.domain, 'in', '[cleaning, returning]') +
` ${localize('vacuum.vacuums')} ${localize('generic.busy')}`,
};
}
}