├── .npmrc ├── .github ├── FUNDING.yml ├── dependabot.yml ├── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md └── workflows │ ├── release.yml │ └── build.yml ├── .gitignore ├── .devcontainer ├── configuration.yaml ├── devcontainer.json └── ui-lovelace.yaml ├── hacs.json ├── .prettierrc.js ├── CHANGELOG.md ├── src ├── types.ts ├── utils.ts ├── deep-replace.ts └── decluttering-card.ts ├── .vscode ├── extensions.json ├── launch.json └── tasks.json ├── tsconfig.json ├── .eslintrc.js ├── LICENSE ├── release.config.js ├── rollup.config.js ├── package.json └── README.md /.npmrc: -------------------------------------------------------------------------------- 1 | git-tag-version=false 2 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [RomRider] 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | yarn.lock 3 | .rpt2_cache/ 4 | /dist/** 5 | -------------------------------------------------------------------------------- /.devcontainer/configuration.yaml: -------------------------------------------------------------------------------- 1 | default_config: 2 | lovelace: 3 | mode: yaml 4 | demo: -------------------------------------------------------------------------------- /hacs.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Decluttering Card", 3 | "render_readme": true, 4 | "filename": "decluttering-card.js" 5 | } 6 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | semi: true, 3 | trailingComma: 'all', 4 | singleQuote: true, 5 | printWidth: 120, 6 | tabWidth: 2, 7 | }; 8 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 1.0.0 (2023-04-02) 2 | 3 | 4 | ### Bug Fixes 5 | 6 | * card broken with HA 2023.4 beta and up ([0ccd5b0](https://github.com/custom-cards/decluttering-card/commit/0ccd5b05a99202c80de21606df1b8e94ea9ee668)), closes [#63](https://github.com/custom-cards/decluttering-card/issues/63) 7 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: 'npm' 4 | directory: '/' 5 | schedule: 6 | interval: 'weekly' 7 | open-pull-requests-limit: 0 8 | target-branch: 'dev' 9 | - package-ecosystem: 'github-actions' 10 | directory: '/' 11 | schedule: 12 | interval: 'weekly' 13 | target-branch: 'dev' -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | export interface DeclutteringCardConfig { 3 | variables?: VariablesConfig[]; 4 | template: string; 5 | } 6 | 7 | export interface VariablesConfig { 8 | [key: string]: any; 9 | } 10 | 11 | export interface TemplateConfig { 12 | default: VariablesConfig[]; 13 | card?: any; 14 | element?: any; 15 | } 16 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "github.vscode-pull-request-github", 4 | "eamodio.gitlens", 5 | "dbaeumer.vscode-eslint", 6 | "esbenp.prettier-vscode", 7 | "bierner.lit-html", 8 | "runem.lit-plugin", 9 | "auchenberg.vscode-browser-preview", 10 | "davidanson.vscode-markdownlint", 11 | "redhat.vscode-yaml", 12 | "msjsdiag.debugger-for-chrome", 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "chrome", 9 | "request": "launch", 10 | "name": "Chrome Localhost", 11 | "url": "http://localhost:8123", 12 | "webRoot": "${workspaceFolder}/dist", 13 | "sourceMaps": true 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2017", 4 | "module": "esnext", 5 | "moduleResolution": "node", 6 | "lib": [ 7 | "es2017", 8 | "dom", 9 | "dom.iterable" 10 | ], 11 | "plugins": [ 12 | { 13 | "name": "ts-lit-plugin" 14 | } 15 | ], 16 | "noEmit": true, 17 | "noUnusedParameters": true, 18 | "noImplicitReturns": true, 19 | "noFallthroughCasesInSwitch": true, 20 | "strict": true, 21 | "noImplicitAny": false, 22 | "skipLibCheck": true, 23 | "resolveJsonModule": true, 24 | "experimentalDecorators": true, 25 | "sourceMap": true, 26 | }, 27 | "include": [ 28 | "src/*" 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: feature request 6 | assignees: '' 7 | --- 8 | 9 | **Is your feature request related to a problem? Please describe.** 10 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 11 | 12 | **Describe the solution you'd like** 13 | A clear and concise description of what you want to happen. 14 | 15 | **Describe alternatives you've considered** 16 | A clear and concise description of any alternative solutions or features you've considered. 17 | 18 | **Additional context** 19 | Add any other context or screenshots about the feature request here. 20 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [{ 4 | "label": "Run Home Assistant on port 9123", 5 | "type": "shell", 6 | "command": "dc start", 7 | "problemMatcher": [] 8 | }, 9 | { 10 | "label": "Run Home Assistant configuration against /config", 11 | "type": "shell", 12 | "command": "dc check", 13 | "problemMatcher": [] 14 | }, 15 | { 16 | "label": "Upgrade Home Assistant to latest dev", 17 | "type": "shell", 18 | "command": "dc install", 19 | "problemMatcher": [] 20 | }, 21 | { 22 | "label": "Install a spesific version of Home Assistant", 23 | "type": "shell", 24 | "command": "dc set-version", 25 | "problemMatcher": [] 26 | } 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | parser: '@typescript-eslint/parser', // Specifies the ESLint parser 3 | extends: [ 4 | 'plugin:@typescript-eslint/recommended', // Uses the recommended rules from the @typescript-eslint/eslint-plugin 5 | 'prettier/@typescript-eslint', // Uses eslint-config-prettier to disable ESLint rules from @typescript-eslint/eslint-plugin that would conflict with prettier 6 | 'plugin:prettier/recommended', // Enables eslint-plugin-prettier and displays prettier errors as ESLint errors. Make sure this is always the last configuration in the extends array. 7 | ], 8 | parserOptions: { 9 | ecmaVersion: 2018, // Allows for the parsing of modern ECMAScript features 10 | sourceType: 'module', // Allows for the use of imports 11 | experimentalDecorators: true, 12 | }, 13 | rules: { 14 | "@typescript-eslint/camelcase": 0 15 | } 16 | }; 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | The MIT License (MIT) 3 | Copyright (c) 2018, Alexandre Garcia 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: workflow_dispatch 4 | 5 | jobs: 6 | release-bundle: 7 | runs-on: ubuntu-latest 8 | if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/dev' 9 | 10 | outputs: 11 | new_release_published: ${{ steps.semantic.outputs.new_release_published }} 12 | new_release_version: ${{ steps.semantic.outputs.new_release_version }} 13 | 14 | steps: 15 | - uses: actions/checkout@v3 16 | - name: Use Node.js 18.x 17 | uses: actions/setup-node@v3.6.0 18 | with: 19 | node-version: 18.x 20 | - name: Cache Node.js modules 21 | uses: actions/cache@v3.3.1 22 | with: 23 | path: ~/.npm # npm cache files are stored in `~/.npm` on Linux/macOS 24 | key: ${{ runner.OS }}-node-18.x-${{ hashFiles('**/package-lock.json') }} 25 | restore-keys: | 26 | ${{ runner.OS }}-node-18.x 27 | ${{ runner.OS }}- 28 | - name: Install dependencies 29 | run: npm ci 30 | - name: Release 31 | run: npx semantic-release 32 | env: 33 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | // See https://aka.ms/vscode-remote/devcontainer.json for format details. 2 | { 3 | "name": "Decluttering Card Development", 4 | "image": "ghcr.io/ludeeus/devcontainer/generic:stable", 5 | "context": "..", 6 | "remoteUser": "vscode", 7 | "appPort": [ 8 | "5000:5000", 9 | "9123:8123" 10 | ], 11 | "postCreateCommand": "npm install", 12 | "runArgs": [ 13 | "-v", 14 | "${env:HOME}${env:USERPROFILE}/.ssh:/tmp/.ssh" // This is added so you can push from inside the container 15 | ], 16 | "extensions": [ 17 | "github.vscode-pull-request-github", 18 | "eamodio.gitlens", 19 | "dbaeumer.vscode-eslint", 20 | "esbenp.prettier-vscode", 21 | "bierner.lit-html", 22 | "runem.lit-plugin", 23 | "auchenberg.vscode-browser-preview", 24 | "davidanson.vscode-markdownlint", 25 | "redhat.vscode-yaml", 26 | "msjsdiag.debugger-for-chrome", 27 | ], 28 | "settings": { 29 | "files.eol": "\n", 30 | "editor.tabSize": 2, 31 | "terminal.integrated.shell.linux": "/bin/bash", 32 | "editor.formatOnPaste": false, 33 | "editor.formatOnSave": true, 34 | "editor.formatOnType": true, 35 | "files.trimTrailingWhitespace": true 36 | } 37 | } -------------------------------------------------------------------------------- /release.config.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-template-curly-in-string */ 2 | module.exports = { 3 | plugins: [ 4 | '@semantic-release/commit-analyzer', 5 | [ 6 | '@semantic-release/release-notes-generator', 7 | { 8 | preset: 'conventionalcommits', 9 | presetConfig: { 10 | types: [ 11 | { type: 'feat', section: 'Features' }, 12 | { type: 'fix', section: 'Bug Fixes' }, 13 | { type: 'doc', hidden: false, section: 'Documentation' }, 14 | { type: 'docs', hidden: false, section: 'Documentation' }, 15 | { type: 'chore', hidden: true, section: 'Chores' }, 16 | ], 17 | }, 18 | }, 19 | ], 20 | '@semantic-release/changelog', 21 | [ 22 | '@semantic-release/npm', 23 | { 24 | npmPublish: false, 25 | }, 26 | ], 27 | [ 28 | '@semantic-release/git', 29 | { 30 | assets: ['CHANGELOG.md', 'README.md', 'package.json', 'yarn.lock'], 31 | }, 32 | ], 33 | [ 34 | '@semantic-release/github', 35 | { 36 | assets: 'dist/*.js', 37 | }, 38 | ], 39 | ], 40 | preset: 'conventionalcommits', 41 | branches: [{ name: 'master' }, { name: 'dev', channel: 'beta', prerelease: true }], 42 | }; -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | --- 8 | 9 | 14 | 15 | **Checklist:** 16 | 17 | - [ ] I updated to the latest version available 18 | - [ ] I cleared the cache of my browser 19 | 20 | **Release with the issue:** 21 | 22 | **Last working release (if known):** 23 | 24 | **Browser and Operating System:** 25 | 26 | 29 | 30 | **Description of problem:** 31 | 32 | 35 | 36 | **Javascript errors shown in the web inspector (if applicable):** 37 | 38 | ``` 39 | 40 | ``` 41 | 42 | **Additional information:** 43 | -------------------------------------------------------------------------------- /src/utils.ts: -------------------------------------------------------------------------------- 1 | import { LovelaceConfig } from 'custom-card-helpers'; 2 | 3 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 4 | export function getLovelaceCast(): any { 5 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 6 | let root: any = document.querySelector('hc-main'); 7 | root = root && root.shadowRoot; 8 | root = root && root.querySelector('hc-lovelace'); 9 | root = root && root.shadowRoot; 10 | root = root && root.querySelector('hui-view'); 11 | if (root) { 12 | const ll = root.lovelace; 13 | ll.current_view = root.___curView; 14 | return ll; 15 | } 16 | return null; 17 | } 18 | 19 | export function getLovelace(): LovelaceConfig | null { 20 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 21 | let root: any = document.querySelector('home-assistant'); 22 | root = root && root.shadowRoot; 23 | root = root && root.querySelector('home-assistant-main'); 24 | root = root && root.shadowRoot; 25 | root = root && root.querySelector('app-drawer-layout partial-panel-resolver, ha-drawer partial-panel-resolver'); 26 | root = (root && root.shadowRoot) || root; 27 | root = root && root.querySelector('ha-panel-lovelace'); 28 | root = root && root.shadowRoot; 29 | root = root && root.querySelector('hui-root'); 30 | if (root) { 31 | const ll = root.lovelace; 32 | ll.current_view = root.___curView; 33 | return ll; 34 | } 35 | return null; 36 | } 37 | -------------------------------------------------------------------------------- /src/deep-replace.ts: -------------------------------------------------------------------------------- 1 | import { VariablesConfig, TemplateConfig } from './types'; 2 | import { LovelaceCardConfig } from 'custom-card-helpers'; 3 | 4 | export default (variables: VariablesConfig[] | undefined, templateConfig: TemplateConfig): LovelaceCardConfig => { 5 | if (!variables && !templateConfig.default) { 6 | return templateConfig.card; 7 | } 8 | let variableArray: VariablesConfig[] = []; 9 | if (variables) { 10 | variableArray = variables.slice(0); 11 | } 12 | if (templateConfig.default) { 13 | variableArray = variableArray.concat(templateConfig.default); 14 | } 15 | let jsonConfig = templateConfig.card ? JSON.stringify(templateConfig.card) : JSON.stringify(templateConfig.element); 16 | variableArray.forEach(variable => { 17 | const key = Object.keys(variable)[0]; 18 | const value = Object.values(variable)[0]; 19 | if (typeof value === 'number' || typeof value === 'boolean') { 20 | const rxp2 = new RegExp(`"\\[\\[${key}\\]\\]"`, 'gm'); 21 | jsonConfig = jsonConfig.replace(rxp2, (value as unknown) as string); 22 | } 23 | if (typeof value === 'object') { 24 | const rxp2 = new RegExp(`"\\[\\[${key}\\]\\]"`, 'gm'); 25 | const valueString = JSON.stringify(value); 26 | jsonConfig = jsonConfig.replace(rxp2, valueString); 27 | } else { 28 | const rxp = new RegExp(`\\[\\[${key}\\]\\]`, 'gm'); 29 | jsonConfig = jsonConfig.replace(rxp, value); 30 | } 31 | }); 32 | return JSON.parse(jsonConfig); 33 | }; 34 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import typescript from 'rollup-plugin-typescript2'; 2 | import commonjs from '@rollup/plugin-commonjs'; 3 | import nodeResolve from '@rollup/plugin-node-resolve'; 4 | import babel from '@rollup/plugin-babel'; 5 | import { terser } from 'rollup-plugin-terser'; 6 | import serve from 'rollup-plugin-serve'; 7 | import json from '@rollup/plugin-json'; 8 | 9 | // eslint-disable-next-line no-undef 10 | const dev = process.env.ROLLUP_WATCH; 11 | 12 | const serveopts = { 13 | contentBase: ['./dist'], 14 | host: '0.0.0.0', 15 | port: 5000, 16 | allowCrossOrigin: true, 17 | headers: { 18 | 'Access-Control-Allow-Origin': '*', 19 | }, 20 | }; 21 | 22 | const plugins = [ 23 | nodeResolve({}), 24 | commonjs(), 25 | typescript(), 26 | json(), 27 | babel({ 28 | exclude: 'node_modules/**', 29 | babelHelpers: 'bundled', 30 | babelrc: false, 31 | presets: [ 32 | '@babel/preset-env', 33 | { 34 | useBuiltIns: 'entry', 35 | targets: '> 0.25%, not dead', 36 | }, 37 | ], 38 | }), 39 | dev && serve(serveopts), 40 | !dev && 41 | terser({ 42 | // format: { 43 | // comments: false, 44 | // }, 45 | mangle: { 46 | safari10: true, 47 | }, 48 | }), 49 | ]; 50 | 51 | export default [ 52 | { 53 | input: 'src/decluttering-card.ts', 54 | output: { 55 | dir: './dist', 56 | format: 'es', 57 | sourcemap: dev ? true : false, 58 | }, 59 | plugins: [...plugins], 60 | watch: { 61 | exclude: 'node_modules/**', 62 | }, 63 | }, 64 | ]; -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - '*' 7 | push: 8 | branches: 9 | - '*' 10 | 11 | jobs: 12 | lint: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v3 16 | - name: Use Node.js 18.x 17 | uses: actions/setup-node@v3.6.0 18 | with: 19 | node-version: 18.x 20 | - name: Cache Node.js modules 21 | uses: actions/cache@v3.3.1 22 | with: 23 | path: ~/.npm # npm cache files are stored in `~/.npm` on Linux/macOS 24 | key: ${{ runner.OS }}-node-18.x-${{ hashFiles('**/package-lock.json') }} 25 | restore-keys: | 26 | ${{ runner.OS }}-node-18.x 27 | ${{ runner.OS }}- 28 | - name: Install dependencies 29 | run: npm ci 30 | - name: Lint the code 31 | run: npm run lint --if-present 32 | build: 33 | runs-on: ubuntu-latest 34 | 35 | strategy: 36 | matrix: 37 | node-version: [18.x] 38 | 39 | steps: 40 | - uses: actions/checkout@v3 41 | - name: Use Node.js ${{ matrix.node-version }} 42 | uses: actions/setup-node@v3.6.0 43 | with: 44 | node-version: ${{ matrix.node-version }} 45 | - name: Cache Node.js modules 46 | uses: actions/cache@v3.3.1 47 | with: 48 | path: ~/.npm # npm cache files are stored in `~/.npm` on Linux/macOS 49 | key: ${{ runner.OS }}-node-${{ matrix.node-version }}-${{ hashFiles('**/package-lock.json') }} 50 | restore-keys: | 51 | ${{ runner.OS }}-node-${{ matrix.node-version }} 52 | ${{ runner.OS }}- 53 | - name: Install dependencies 54 | run: npm ci 55 | - name: Build the project 56 | run: npm run build --if-present -------------------------------------------------------------------------------- /.devcontainer/ui-lovelace.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - url: http://127.0.0.1:5000/decluttering-card.js 3 | type: module 4 | - url: https://cdn.jsdelivr.net/gh/thomasloven/lovelace-card-mod@master/card-mod.js 5 | type: module 6 | 7 | decluttering_templates: 8 | my_first_template: # This is the name of a template 9 | default: 10 | - icon: fire 11 | card: 12 | type: button 13 | name: '[[name]]' 14 | icon: 'mdi:[[icon]]' 15 | large_divider: 16 | default: 17 | - opacity: 0.25 18 | card: 19 | type: divider 20 | style: 21 | background-color: var(--secondary-text-color) 22 | height: 1px 23 | margin: 15px auto 24 | opacity: '[[opacity]]' 25 | demo_icon: 26 | element: 27 | type: icon 28 | icon: mdi:alert-circle 29 | title: Problem detected! 30 | entity: '[[entity]]' 31 | tap_action: 32 | action: more-info 33 | style: 34 | right: 50% 35 | top: 50% 36 | color: var(--google-red-500) 37 | filter: drop-shadow(black 0 0 1px) 38 | test_hidden: 39 | card: 40 | type: conditional 41 | conditions: 42 | - entity: light.bed_light 43 | state: 'on' 44 | card: 45 | type: entities 46 | entities: 47 | - sun.sun 48 | 49 | views: 50 | - cards: 51 | - type: custom:decluttering-card 52 | template: my_first_template 53 | variables: 54 | - name: This is a test 55 | - type: entities 56 | entities: 57 | - light.bed_light 58 | - type: custom:decluttering-card 59 | template: large_divider 60 | - sun.sun 61 | - type: picture-elements 62 | image: https://www.w3schools.com/w3css/img_lights.jpg 63 | elements: 64 | - type: custom:decluttering-card 65 | template: demo_icon 66 | variables: 67 | - entity: sun.sun 68 | - type: horizontal-stack 69 | cards: 70 | - type: custom:decluttering-card 71 | template: my_first_template 72 | variables: 73 | - name: Full width when light Off 74 | - type: custom:decluttering-card 75 | template: test_hidden 76 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "decluttering-card", 3 | "version": "1.0.0", 4 | "description": "Decluttering Card for Lovelace", 5 | "main": "dist/decluttering-card.js", 6 | "scripts": { 7 | "build": "npm run lint && npm run rollup", 8 | "rollup": "rollup -c", 9 | "babel": "babel dist/decluttering-card.js --out-file dist/decluttering-card.js", 10 | "lint": "eslint src/*.ts", 11 | "postversion": "npm run build", 12 | "start": "rollup -c --watch" 13 | }, 14 | "repository": { 15 | "type": "git", 16 | "url": "git+https://github.com/custom-cards/decluttering-card.git" 17 | }, 18 | "keywords": [ 19 | "lovelace" 20 | ], 21 | "author": "RomRider", 22 | "license": "MIT", 23 | "bugs": { 24 | "url": "https://github.com/custom-cards/decluttering-card/issues" 25 | }, 26 | "homepage": "https://github.com/custom-cards/decluttering-card/README.md", 27 | "devDependencies": { 28 | "@babel/core": "^7.21.4", 29 | "@babel/plugin-proposal-class-properties": "^7.8.3", 30 | "@babel/plugin-proposal-decorators": "^7.8.3", 31 | "@rollup/plugin-babel": "^6.0.3", 32 | "@rollup/plugin-commonjs": "^24.0.1", 33 | "@rollup/plugin-json": "^6.0.0", 34 | "@rollup/plugin-node-resolve": "^15.0.1", 35 | "@semantic-release/changelog": "^6.0.3", 36 | "@semantic-release/commit-analyzer": "^9.0.2", 37 | "@semantic-release/exec": "^6.0.3", 38 | "@semantic-release/git": "^10.0.1", 39 | "@semantic-release/npm": "^10.0.2", 40 | "@semantic-release/release-notes-generator": "^10.0.3", 41 | "@typescript-eslint/eslint-plugin": "^2.24.0", 42 | "@typescript-eslint/parser": "^2.24.0", 43 | "babel-cli": "^6.26.0", 44 | "conventional-changelog-conventionalcommits": "^5.0.0", 45 | "eslint": "^6.8.0", 46 | "eslint-config-airbnb-base": "^14.1.0", 47 | "eslint-config-prettier": "^6.10.0", 48 | "eslint-plugin-import": "^2.20.1", 49 | "eslint-plugin-prettier": "^3.1.2", 50 | "npm": "^9.6.3", 51 | "prettier": "^1.19.0", 52 | "prettier-eslint": "^9.0.1", 53 | "rollup-plugin-serve": "^1.0.1", 54 | "rollup-plugin-terser": "^5.3.0", 55 | "rollup-plugin-typescript2": "^0.26.0", 56 | "ts-lit-plugin": "^1.2.1", 57 | "typescript": "^3.8.3", 58 | "typescript-styled-plugin": "^0.15.0" 59 | }, 60 | "dependencies": { 61 | "custom-card-helpers": "^1.5.0", 62 | "home-assistant-js-websocket": "^5.1.0", 63 | "lit-element": "^2.3.0", 64 | "resize-observer": "^1.0.0" 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/decluttering-card.ts: -------------------------------------------------------------------------------- 1 | import { LitElement, html, customElement, property, TemplateResult, css, CSSResult } from 'lit-element'; 2 | import { HomeAssistant, createThing, LovelaceCardConfig, LovelaceCard } from 'custom-card-helpers'; 3 | import { DeclutteringCardConfig, TemplateConfig } from './types'; 4 | import deepReplace from './deep-replace'; 5 | import { getLovelace, getLovelaceCast } from './utils'; 6 | import { ResizeObserver } from 'resize-observer'; 7 | import * as pjson from '../package.json'; 8 | 9 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 10 | const HELPERS = (window as any).loadCardHelpers ? (window as any).loadCardHelpers() : undefined; 11 | 12 | console.info( 13 | `%c DECLUTTERING-CARD \n%c Version ${pjson.version} `, 14 | 'color: orange; font-weight: bold; background: black', 15 | 'color: white; font-weight: bold; background: dimgray', 16 | ); 17 | 18 | @customElement('decluttering-card') 19 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 20 | class DeclutteringCard extends LitElement { 21 | @property() protected _card?: LovelaceCard; 22 | 23 | @property() private _config?: LovelaceCardConfig; 24 | 25 | private _ro?: ResizeObserver; 26 | 27 | private _hass?: HomeAssistant; 28 | 29 | private _type?: 'element' | 'card'; 30 | 31 | set hass(hass: HomeAssistant) { 32 | if (!hass) return; 33 | if (!this._hass && hass) { 34 | // eslint-disable-next-line @typescript-eslint/no-non-null-assertion 35 | this._createCard(this._config!, this._type!).then(card => { 36 | this._card = card; 37 | this._card && this._ro?.observe(this._card); 38 | return this._card; 39 | }); 40 | } 41 | this._hass = hass; 42 | if (this._card) { 43 | this._card.hass = hass; 44 | } 45 | } 46 | 47 | static get styles(): CSSResult { 48 | return css` 49 | :host(.child-card-hidden) { 50 | display: none; 51 | } 52 | `; 53 | } 54 | 55 | protected firstUpdated(): void { 56 | this.updateComplete.then(() => { 57 | this._displayHidden(); 58 | }); 59 | } 60 | 61 | protected _displayHidden(): void { 62 | if (this._card?.style.display === 'none') { 63 | this.classList.add('child-card-hidden'); 64 | } else if (this.classList.contains('child-card-hidden')) { 65 | this.classList.remove('child-card-hidden'); 66 | } 67 | } 68 | 69 | public setConfig(config: DeclutteringCardConfig): void { 70 | if (!config.template) { 71 | throw new Error('Missing template object in your config'); 72 | } 73 | const ll = getLovelace() || getLovelaceCast(); 74 | if (!ll.config && !ll.config.decluttering_templates) { 75 | throw new Error("The object decluttering_templates doesn't exist in your main lovelace config."); 76 | } 77 | const templateConfig = ll.config.decluttering_templates[config.template] as TemplateConfig; 78 | if (!templateConfig) { 79 | throw new Error(`The template "${config.template}" doesn't exist in decluttering_templates`); 80 | } else if (!(templateConfig.card || templateConfig.element)) { 81 | throw new Error('You shoud define either a card or an element in the template'); 82 | } else if (templateConfig.card && templateConfig.element) { 83 | throw new Error('You can define a card and an element in the template'); 84 | } 85 | this._ro = new ResizeObserver(() => { 86 | this._displayHidden(); 87 | }); 88 | this._config = deepReplace(config.variables, templateConfig); 89 | this._type = templateConfig.card ? 'card' : 'element'; 90 | } 91 | 92 | protected render(): TemplateResult | void { 93 | if (!this._hass || !this._card || !this._config) return html``; 94 | 95 | return html` 96 |