mirror of
https://github.com/home-assistant/frontend.git
synced 2025-08-13 03:09:26 +00:00
Compare commits
3 Commits
data_disk_
...
fix-number
Author | SHA1 | Date | |
---|---|---|---|
![]() |
a63e788ced | ||
![]() |
c377c01c65 | ||
![]() |
aaf65e0599 |
@@ -4,7 +4,7 @@
|
||||
"dockerfile": "Dockerfile",
|
||||
"context": ".."
|
||||
},
|
||||
"appPort": "8124:8123",
|
||||
"appPort": 8123,
|
||||
"context": "..",
|
||||
"postCreateCommand": "script/bootstrap",
|
||||
"extensions": [
|
||||
|
119
.eslintrc.json
119
.eslintrc.json
@@ -4,7 +4,8 @@
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"plugin:wc/recommended",
|
||||
"plugin:lit/recommended",
|
||||
"prettier"
|
||||
"prettier",
|
||||
"prettier/@typescript-eslint"
|
||||
],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
@@ -28,89 +29,63 @@
|
||||
"__BUILD__": false,
|
||||
"__VERSION__": false,
|
||||
"__STATIC_PATH__": false,
|
||||
"Polymer": true
|
||||
"Polymer": true,
|
||||
"webkitSpeechRecognition": false,
|
||||
"ResizeObserver": false
|
||||
},
|
||||
"env": {
|
||||
"browser": true,
|
||||
"es6": true
|
||||
},
|
||||
"rules": {
|
||||
"class-methods-use-this": "off",
|
||||
"new-cap": "off",
|
||||
"prefer-template": "off",
|
||||
"object-shorthand": "off",
|
||||
"func-names": "off",
|
||||
"no-underscore-dangle": "off",
|
||||
"strict": "off",
|
||||
"no-plusplus": "off",
|
||||
"no-bitwise": "error",
|
||||
"comma-dangle": "off",
|
||||
"vars-on-top": "off",
|
||||
"no-continue": "off",
|
||||
"no-param-reassign": "off",
|
||||
"no-multi-assign": "off",
|
||||
"no-console": "error",
|
||||
"radix": "off",
|
||||
"no-alert": "off",
|
||||
"no-nested-ternary": "off",
|
||||
"prefer-destructuring": "off",
|
||||
"class-methods-use-this": 0,
|
||||
"new-cap": 0,
|
||||
"prefer-template": 0,
|
||||
"object-shorthand": 0,
|
||||
"func-names": 0,
|
||||
"prefer-arrow-callback": 0,
|
||||
"no-underscore-dangle": 0,
|
||||
"strict": 0,
|
||||
"prefer-spread": 0,
|
||||
"no-plusplus": 0,
|
||||
"no-bitwise": 2,
|
||||
"comma-dangle": 0,
|
||||
"vars-on-top": 0,
|
||||
"no-continue": 0,
|
||||
"no-param-reassign": 0,
|
||||
"no-multi-assign": 0,
|
||||
"no-console": 2,
|
||||
"radix": 0,
|
||||
"no-alert": 0,
|
||||
"no-return-await": 0,
|
||||
"no-nested-ternary": 0,
|
||||
"prefer-destructuring": 0,
|
||||
"no-restricted-globals": [2, "event"],
|
||||
"prefer-promise-reject-errors": "off",
|
||||
"import/prefer-default-export": "off",
|
||||
"import/no-default-export": "off",
|
||||
"import/no-unresolved": "off",
|
||||
"import/no-cycle": "off",
|
||||
"prefer-promise-reject-errors": 0,
|
||||
"import/order": 0,
|
||||
"import/prefer-default-export": 0,
|
||||
"import/no-unresolved": 0,
|
||||
"import/no-cycle": 0,
|
||||
"import/extensions": [
|
||||
"error",
|
||||
2,
|
||||
"ignorePackages",
|
||||
{ "ts": "never", "js": "never" }
|
||||
],
|
||||
"no-restricted-syntax": ["error", "LabeledStatement", "WithStatement"],
|
||||
"object-curly-newline": "off",
|
||||
"default-case": "off",
|
||||
"wc/no-self-class": "off",
|
||||
"no-shadow": "off",
|
||||
"@typescript-eslint/camelcase": "off",
|
||||
"@typescript-eslint/ban-ts-comment": "off",
|
||||
"@typescript-eslint/no-use-before-define": "off",
|
||||
"@typescript-eslint/no-non-null-assertion": "off",
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"@typescript-eslint/explicit-function-return-type": "off",
|
||||
"@typescript-eslint/explicit-module-boundary-types": "off",
|
||||
"@typescript-eslint/no-shadow": ["error"],
|
||||
"@typescript-eslint/naming-convention": [
|
||||
"off",
|
||||
{
|
||||
"selector": "default",
|
||||
"format": ["camelCase", "snake_case"],
|
||||
"leadingUnderscore": "allow",
|
||||
"trailingUnderscore": "allow"
|
||||
},
|
||||
{
|
||||
"selector": ["variable"],
|
||||
"format": ["camelCase", "snake_case", "UPPER_CASE"],
|
||||
"leadingUnderscore": "allow",
|
||||
"trailingUnderscore": "allow"
|
||||
},
|
||||
{
|
||||
"selector": "typeLike",
|
||||
"format": ["PascalCase"]
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/no-unused-vars": "off",
|
||||
"unused-imports/no-unused-vars": [
|
||||
"error",
|
||||
{
|
||||
"vars": "all",
|
||||
"varsIgnorePattern": "^_",
|
||||
"args": "after-used",
|
||||
"argsIgnorePattern": "^_",
|
||||
"ignoreRestSiblings": true
|
||||
}
|
||||
],
|
||||
"unused-imports/no-unused-imports": "error",
|
||||
"lit/attribute-value-entities": "off"
|
||||
"object-curly-newline": 0,
|
||||
"default-case": 0,
|
||||
"wc/no-self-class": 0,
|
||||
"no-shadow": 0,
|
||||
"@typescript-eslint/camelcase": 0,
|
||||
"@typescript-eslint/ban-ts-comment": 0,
|
||||
"@typescript-eslint/no-use-before-define": 0,
|
||||
"@typescript-eslint/no-non-null-assertion": 0,
|
||||
"@typescript-eslint/no-explicit-any": 0,
|
||||
"@typescript-eslint/no-unused-vars": 0,
|
||||
"@typescript-eslint/explicit-function-return-type": 0,
|
||||
"@typescript-eslint/explicit-module-boundary-types": 0,
|
||||
"@typescript-eslint/no-shadow": ["error"]
|
||||
},
|
||||
"plugins": ["disable", "unused-imports"],
|
||||
"plugins": ["disable", "import", "lit", "prettier", "@typescript-eslint"],
|
||||
"processor": "disable/disable"
|
||||
}
|
||||
|
121
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
121
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -1,121 +0,0 @@
|
||||
name: Report a bug with the UI, Frontend or Lovelace
|
||||
description: Report an issue related to the Home Assistant frontend.
|
||||
labels: bug
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Make sure you are running the [latest version of Home Assistant][releases] before reporting an issue.
|
||||
|
||||
If you have a feature or enhancement request for the frontend, please [start an discussion][fr] instead of creating an issue.
|
||||
|
||||
**Please not not report issues for custom Lovelace cards.**
|
||||
|
||||
[fr]: https://github.com/home-assistant/frontend/discussions
|
||||
[releases]: https://github.com/home-assistant/home-assistant/releases
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Checklist
|
||||
description: Please verify that you've followed these steps
|
||||
options:
|
||||
- label: I have updated to the latest available Home Assistant version.
|
||||
required: true
|
||||
- label: I have cleared the cache of my browser.
|
||||
required: true
|
||||
- label: I have tried a different browser to see if it is related to my browser.
|
||||
required: true
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
## The problem
|
||||
- type: textarea
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: Describe the issue you are experiencing
|
||||
description: Provide a clear and concise description of what the bug is.
|
||||
- type: textarea
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: Describe the behavior you expected
|
||||
description: Describe what you expected to happen or it should look/behave.
|
||||
- type: textarea
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: Steps to reproduce the issue
|
||||
description: |
|
||||
Please tell us exactly how to reproduce your issue.
|
||||
Provide clear and concise step by step instructions and add code snippets if needed.
|
||||
value: |
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
...
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
## Environment
|
||||
- type: input
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: What version of Home Assistant Core has the issue?
|
||||
placeholder: core-
|
||||
description: >
|
||||
Can be found in the Configuration panel -> Info.
|
||||
- type: input
|
||||
attributes:
|
||||
label: What was the last working version of Home Assistant Core?
|
||||
placeholder: core-
|
||||
description: >
|
||||
If known, otherwise leave blank.
|
||||
- type: input
|
||||
attributes:
|
||||
label: In which browser are you experiencing the issue with?
|
||||
placeholder: Google Chrome 88.0.4324.150
|
||||
description: >
|
||||
Provide the full name and don't forget to add the version!
|
||||
- type: input
|
||||
attributes:
|
||||
label: Which operating system are you using to run this browser?
|
||||
placeholder: macOS Big Sur (1.11)
|
||||
description: >
|
||||
Don't forget to add the version!
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
# Details
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: State of relevant entities
|
||||
description: >
|
||||
If your issue is about how an entity is shown in the UI, please add the
|
||||
state and attributes for all situations. You can find this information
|
||||
at Developer Tools -> States.
|
||||
render: txt
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Problem-relevant frontend configuration
|
||||
description: >
|
||||
An example configuration that caused the problem for you, e.g., the YAML
|
||||
configuration of the used cards. Fill this out even if it seems
|
||||
unimportant to you. Please be sure to remove personal information like
|
||||
passwords, private URLs and other credentials.
|
||||
render: yaml
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Javascript errors shown in your browser console/inspector
|
||||
description: >
|
||||
If you come across any Javascript or other error logs, e.g., in your
|
||||
browser console/inspector please provide them.
|
||||
render: txt
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Additional information
|
||||
description: >
|
||||
If you have any additional information for us, use the field below.
|
||||
Please note, you can attach screenshots or screen recordings here, by
|
||||
dragging and dropping files in the field below.
|
82
.github/workflows/ci.yaml
vendored
82
.github/workflows/ci.yaml
vendored
@@ -10,21 +10,26 @@ on:
|
||||
- dev
|
||||
- master
|
||||
|
||||
env:
|
||||
NODE_VERSION: 14
|
||||
NODE_OPTIONS: --max_old_space_size=4096
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out files from GitHub
|
||||
uses: actions/checkout@v2
|
||||
- name: Set up Node ${{ env.NODE_VERSION }}
|
||||
uses: actions/setup-node@v2
|
||||
- name: Setting up Node.js
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
cache: yarn
|
||||
node-version: 12.x
|
||||
- name: Get yarn cache path
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
- name: Fetching Yarn cache
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-yarn-
|
||||
- name: Install dependencies
|
||||
run: yarn install
|
||||
env:
|
||||
@@ -32,40 +37,54 @@ jobs:
|
||||
- name: Build resources
|
||||
run: ./node_modules/.bin/gulp gen-icons-json build-translations gather-gallery-demos
|
||||
- name: Run eslint
|
||||
run: yarn run lint:eslint
|
||||
run: ./node_modules/.bin/eslint '{**/src,src}/**/*.{js,ts,html}' --ignore-path .gitignore
|
||||
- name: Run tsc
|
||||
run: yarn run lint:types
|
||||
- name: Run prettier
|
||||
run: yarn run lint:prettier
|
||||
- name: Check for duplicate dependencies
|
||||
run: yarn dedupe --check
|
||||
run: ./node_modules/.bin/tsc
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out files from GitHub
|
||||
uses: actions/checkout@v2
|
||||
- name: Set up Node ${{ env.NODE_VERSION }}
|
||||
uses: actions/setup-node@v2
|
||||
- name: Setting up Node.js
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
cache: yarn
|
||||
node-version: 12.x
|
||||
- name: Get yarn cache path
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
- name: Fetching Yarn cache
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-yarn-
|
||||
- name: Install dependencies
|
||||
run: yarn install
|
||||
env:
|
||||
CI: true
|
||||
- name: Run Mocha
|
||||
run: yarn run mocha
|
||||
run: npm run mocha
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [lint, test]
|
||||
steps:
|
||||
- name: Check out files from GitHub
|
||||
uses: actions/checkout@v2
|
||||
- name: Set up Node ${{ env.NODE_VERSION }}
|
||||
uses: actions/setup-node@v2
|
||||
- name: Setting up Node.js
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
cache: yarn
|
||||
node-version: 12.x
|
||||
- name: Get yarn cache path
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
- name: Fetching Yarn cache
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-yarn-
|
||||
- name: Install dependencies
|
||||
run: yarn install
|
||||
env:
|
||||
@@ -80,11 +99,20 @@ jobs:
|
||||
steps:
|
||||
- name: Check out files from GitHub
|
||||
uses: actions/checkout@v2
|
||||
- name: Set up Node ${{ env.NODE_VERSION }}
|
||||
uses: actions/setup-node@v2
|
||||
- name: Setting up Node.js
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
cache: yarn
|
||||
node-version: 12.x
|
||||
- name: Get yarn cache path
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
- name: Fetching Yarn cache
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-yarn-
|
||||
- name: Install dependencies
|
||||
run: yarn install
|
||||
env:
|
||||
|
22
.github/workflows/demo.yaml
vendored
22
.github/workflows/demo.yaml
vendored
@@ -4,22 +4,26 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- dev
|
||||
|
||||
env:
|
||||
NODE_VERSION: 14
|
||||
NODE_OPTIONS: --max_old_space_size=4096
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out files from GitHub
|
||||
uses: actions/checkout@v2
|
||||
- name: Set up Node ${{ env.NODE_VERSION }}
|
||||
uses: actions/setup-node@v2
|
||||
- name: Setting up Node.js
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
cache: yarn
|
||||
node-version: 12.x
|
||||
- name: Get yarn cache path
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
- name: Fetching Yarn cache
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-yarn-
|
||||
- name: Install dependencies
|
||||
run: yarn install
|
||||
env:
|
||||
|
18
.github/workflows/release.yaml
vendored
18
.github/workflows/release.yaml
vendored
@@ -6,9 +6,9 @@ on:
|
||||
- published
|
||||
|
||||
env:
|
||||
PYTHON_VERSION: 3.8
|
||||
NODE_VERSION: 14
|
||||
NODE_OPTIONS: --max_old_space_size=4096
|
||||
WHEELS_TAG: 3.7-alpine3.11
|
||||
PYTHON_VERSION: 3.7
|
||||
NODE_VERSION: 12.1
|
||||
|
||||
jobs:
|
||||
release:
|
||||
@@ -30,15 +30,7 @@ jobs:
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
cache: yarn
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn install
|
||||
|
||||
- name: Download Translations
|
||||
run: ./script/translations_download
|
||||
env:
|
||||
LOKALISE_TOKEN: ${{ secrets.LOKALISE_TOKEN }}
|
||||
- name: Build and release package
|
||||
run: |
|
||||
python3 -m pip install twine
|
||||
@@ -72,8 +64,6 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
arch: ["aarch64", "armhf", "armv7", "amd64", "i386"]
|
||||
tag:
|
||||
- "3.9-alpine3.14"
|
||||
steps:
|
||||
- name: Download requirements.txt
|
||||
uses: actions/download-artifact@v2
|
||||
@@ -83,7 +73,7 @@ jobs:
|
||||
- name: Build wheels
|
||||
uses: home-assistant/wheels@master
|
||||
with:
|
||||
tag: ${{ matrix.tag }}
|
||||
tag: ${{ env.WHEELS_TAG }}
|
||||
arch: ${{ matrix.arch }}
|
||||
wheels-host: ${{ secrets.WHEELS_HOST }}
|
||||
wheels-key: ${{ secrets.WHEELS_KEY }}
|
||||
|
44
.github/workflows/translations.yaml
vendored
44
.github/workflows/translations.yaml
vendored
@@ -1,14 +1,16 @@
|
||||
name: Translations
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "30 0 * * *"
|
||||
push:
|
||||
branches:
|
||||
- dev
|
||||
paths:
|
||||
- src/translations/en.json
|
||||
- translations/en.json
|
||||
|
||||
env:
|
||||
NODE_VERSION: 14
|
||||
NODE_VERSION: 12
|
||||
|
||||
jobs:
|
||||
upload:
|
||||
@@ -18,8 +20,46 @@ jobs:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Set up Node ${{ env.NODE_VERSION }}
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
|
||||
- name: Upload Translations
|
||||
run: |
|
||||
export LOKALISE_TOKEN="${{ secrets.LOKALISE_TOKEN }}"
|
||||
|
||||
./script/translations_upload_base
|
||||
|
||||
download:
|
||||
name: Download
|
||||
needs: upload
|
||||
if: github.event_name == 'schedule'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Set up Node ${{ env.NODE_VERSION }}
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
|
||||
- name: Download Translations
|
||||
run: |
|
||||
export LOKALISE_TOKEN="${{ secrets.LOKALISE_TOKEN }}"
|
||||
|
||||
npm install
|
||||
./script/translations_download
|
||||
|
||||
- name: Initialize git
|
||||
uses: home-assistant/actions/helpers/git-init@master
|
||||
with:
|
||||
name: GitHub Action
|
||||
email: github-action@users.noreply.github.com
|
||||
|
||||
- name: Update translation
|
||||
run: |
|
||||
git add translations
|
||||
git commit -am "Translation update"
|
||||
git push
|
||||
|
27
.gitignore
vendored
27
.gitignore
vendored
@@ -1,23 +1,10 @@
|
||||
.DS_Store
|
||||
.reify-cache
|
||||
|
||||
# build
|
||||
build
|
||||
build-translations/*
|
||||
hass_frontend/*
|
||||
dist
|
||||
|
||||
# yarn
|
||||
.yarn/*
|
||||
!.yarn/patches
|
||||
!.yarn/releases
|
||||
!.yarn/plugins
|
||||
!.yarn/sdks
|
||||
!.yarn/versions
|
||||
.pnp.*
|
||||
node_modules/*
|
||||
yarn-error.log
|
||||
npm-debug.log
|
||||
.DS_Store
|
||||
hass_frontend/*
|
||||
.reify-cache
|
||||
|
||||
# Python stuff
|
||||
*.py[cod]
|
||||
@@ -27,8 +14,11 @@ npm-debug.log
|
||||
# venv stuff
|
||||
pyvenv.cfg
|
||||
pip-selfcheck.json
|
||||
venv/*
|
||||
venv
|
||||
.venv
|
||||
lib
|
||||
bin
|
||||
dist
|
||||
|
||||
# vscode
|
||||
.vscode/*
|
||||
@@ -41,8 +31,9 @@ src/cast/dev_const.ts
|
||||
|
||||
# Secrets
|
||||
.lokalise_token
|
||||
yarn-error.log
|
||||
|
||||
# asdf
|
||||
#asdf
|
||||
.tool-versions
|
||||
|
||||
# Home Assistant config
|
||||
|
@@ -1,4 +0,0 @@
|
||||
module.exports = {
|
||||
require: "test-mocha/testconf.js",
|
||||
timeout: 10000,
|
||||
};
|
File diff suppressed because one or more lines are too long
@@ -1,29 +0,0 @@
|
||||
diff --git a/lib/uni-virtualizer/lib/polyfillLoaders/EventTarget.js b/lib/uni-virtualizer/lib/polyfillLoaders/EventTarget.js
|
||||
index d92179f7fd5315203f870a6963e871dc8ddf6c0c..362e284121b97e0fba0925225777aebc32e26b8d 100644
|
||||
--- a/lib/uni-virtualizer/lib/polyfillLoaders/EventTarget.js
|
||||
+++ b/lib/uni-virtualizer/lib/polyfillLoaders/EventTarget.js
|
||||
@@ -1,14 +1,15 @@
|
||||
-let _ET, ET;
|
||||
+let _ET;
|
||||
+let ET;
|
||||
export default async function EventTarget() {
|
||||
- return ET || init();
|
||||
+ return ET || init();
|
||||
}
|
||||
async function init() {
|
||||
- _ET = window.EventTarget;
|
||||
- try {
|
||||
- new _ET();
|
||||
- }
|
||||
- catch (_a) {
|
||||
- _ET = (await import('event-target-shim')).EventTarget;
|
||||
- }
|
||||
- return (ET = _ET);
|
||||
+ _ET = window.EventTarget;
|
||||
+ try {
|
||||
+ new _ET();
|
||||
+ } catch (_a) {
|
||||
+ _ET = (await import("event-target-shim")).default.EventTarget;
|
||||
+ }
|
||||
+ return (ET = _ET);
|
||||
}
|
@@ -1,34 +0,0 @@
|
||||
diff --git a/lib/legacy/class.js b/lib/legacy/class.js
|
||||
index aee2511be1cd9bf900ee552bc98190c1631c57c0..f2f499d68bf52034cac9c28307c99e8ce6b8417d 100644
|
||||
--- a/lib/legacy/class.js
|
||||
+++ b/lib/legacy/class.js
|
||||
@@ -304,17 +304,23 @@ function GenerateClassFromInfo(info, Base, behaviors) {
|
||||
// only proceed if the generated class' prototype has not been registered.
|
||||
const generatedProto = PolymerGenerated.prototype;
|
||||
if (!generatedProto.hasOwnProperty(JSCompiler_renameProperty('__hasRegisterFinished', generatedProto))) {
|
||||
- generatedProto.__hasRegisterFinished = true;
|
||||
+ // make sure legacy lifecycle is called on the *element*'s prototype
|
||||
+ // and not the generated class prototype; if the element has been
|
||||
+ // extended, these are *not* the same.
|
||||
+ const proto = Object.getPrototypeOf(this);
|
||||
+ // Only set flag when generated prototype itself is registered,
|
||||
+ // as this element may be extended from, and needs to run `registered`
|
||||
+ // on all behaviors on the subclass as well.
|
||||
+ if (proto === generatedProto) {
|
||||
+ generatedProto.__hasRegisterFinished = true;
|
||||
+ }
|
||||
// ensure superclass is registered first.
|
||||
super._registered();
|
||||
// copy properties onto the generated class lazily if we're optimizing,
|
||||
- if (legacyOptimizations) {
|
||||
+ if (legacyOptimizations && !Object.hasOwnProperty(generatedProto, '__hasCopiedProperties')) {
|
||||
+ generatedProto.__hasCopiedProperties = true;
|
||||
copyPropertiesToProto(generatedProto);
|
||||
}
|
||||
- // make sure legacy lifecycle is called on the *element*'s prototype
|
||||
- // and not the generated class prototype; if the element has been
|
||||
- // extended, these are *not* the same.
|
||||
- const proto = Object.getPrototypeOf(this);
|
||||
let list = lifecycle.beforeRegister;
|
||||
if (list) {
|
||||
for (let i=0; i < list.length; i++) {
|
File diff suppressed because one or more lines are too long
8
.yarn/plugins/@yarnpkg/plugin-typescript.cjs
vendored
8
.yarn/plugins/@yarnpkg/plugin-typescript.cjs
vendored
File diff suppressed because one or more lines are too long
55
.yarn/releases/yarn-2.4.2.cjs
vendored
55
.yarn/releases/yarn-2.4.2.cjs
vendored
File diff suppressed because one or more lines are too long
@@ -1,9 +0,0 @@
|
||||
nodeLinker: node-modules
|
||||
|
||||
plugins:
|
||||
- path: .yarn/plugins/@yarnpkg/plugin-typescript.cjs
|
||||
spec: "@yarnpkg/plugin-typescript"
|
||||
- path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
|
||||
spec: "@yarnpkg/plugin-interactive-tools"
|
||||
|
||||
yarnPath: .yarn/releases/yarn-2.4.2.cjs
|
@@ -1,170 +0,0 @@
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
const path = require("path");
|
||||
|
||||
// Currently only supports CommonJS modules, as require is synchronous. `import` would need babel running asynchronous.
|
||||
module.exports = function inlineConstants(babel, options, cwd) {
|
||||
const t = babel.types;
|
||||
|
||||
if (!Array.isArray(options.modules)) {
|
||||
throw new TypeError(
|
||||
"babel-plugin-inline-constants: expected a `modules` array to be passed"
|
||||
);
|
||||
}
|
||||
|
||||
if (options.resolveExtensions && !Array.isArray(options.resolveExtensions)) {
|
||||
throw new TypeError(
|
||||
"babel-plugin-inline-constants: expected `resolveExtensions` to be an array"
|
||||
);
|
||||
}
|
||||
|
||||
const ignoreModuleNotFound = options.ignoreModuleNotFound;
|
||||
const resolveExtensions = options.resolveExtensions;
|
||||
|
||||
const hasRelativeModules = options.modules.some(
|
||||
(module) => module.startsWith(".") || module.startsWith("/")
|
||||
);
|
||||
|
||||
const modules = Object.fromEntries(
|
||||
options.modules.map((module) => {
|
||||
const absolute = module.startsWith(".")
|
||||
? require.resolve(module, { paths: [cwd] })
|
||||
: module;
|
||||
// eslint-disable-next-line import/no-dynamic-require
|
||||
return [absolute, require(absolute)];
|
||||
})
|
||||
);
|
||||
|
||||
const toLiteral = (value) => {
|
||||
if (typeof value === "string") {
|
||||
return t.stringLiteral(value);
|
||||
}
|
||||
|
||||
if (typeof value === "number") {
|
||||
return t.numericLiteral(value);
|
||||
}
|
||||
|
||||
if (typeof value === "boolean") {
|
||||
return t.booleanLiteral(value);
|
||||
}
|
||||
|
||||
if (value === null) {
|
||||
return t.nullLiteral();
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
"babel-plugin-inline-constants: cannot handle non-literal `" + value + "`"
|
||||
);
|
||||
};
|
||||
|
||||
const resolveAbsolute = (value, state, resolveExtensionIndex) => {
|
||||
if (!state.filename) {
|
||||
throw new TypeError(
|
||||
"babel-plugin-inline-constants: expected a `filename` to be set for files"
|
||||
);
|
||||
}
|
||||
|
||||
if (resolveExtensions && resolveExtensionIndex !== undefined) {
|
||||
value += resolveExtensions[resolveExtensionIndex];
|
||||
}
|
||||
|
||||
try {
|
||||
return require.resolve(value, { paths: [path.dirname(state.filename)] });
|
||||
} catch (error) {
|
||||
if (
|
||||
error.code === "MODULE_NOT_FOUND" &&
|
||||
resolveExtensions &&
|
||||
(resolveExtensionIndex === undefined ||
|
||||
resolveExtensionIndex < resolveExtensions.length - 1)
|
||||
) {
|
||||
const resolveExtensionIdx = (resolveExtensionIndex || -1) + 1;
|
||||
return resolveAbsolute(value, state, resolveExtensionIdx);
|
||||
}
|
||||
|
||||
if (error.code === "MODULE_NOT_FOUND" && ignoreModuleNotFound) {
|
||||
return undefined;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const importDeclaration = (p, state) => {
|
||||
if (p.node.type !== "ImportDeclaration") {
|
||||
return;
|
||||
}
|
||||
const absolute =
|
||||
hasRelativeModules && p.node.source.value.startsWith(".")
|
||||
? resolveAbsolute(p.node.source.value, state)
|
||||
: p.node.source.value;
|
||||
|
||||
if (!absolute || !(absolute in modules)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const module = modules[absolute];
|
||||
|
||||
for (const specifier of p.node.specifiers) {
|
||||
if (
|
||||
specifier.type === "ImportDefaultSpecifier" &&
|
||||
specifier.local &&
|
||||
specifier.local.type === "Identifier"
|
||||
) {
|
||||
if (!("default" in module)) {
|
||||
throw new Error(
|
||||
"babel-plugin-inline-constants: cannot access default export from `" +
|
||||
p.node.source.value +
|
||||
"`"
|
||||
);
|
||||
}
|
||||
|
||||
const variableValue = toLiteral(module.default);
|
||||
const variable = t.variableDeclarator(
|
||||
t.identifier(specifier.local.name),
|
||||
variableValue
|
||||
);
|
||||
|
||||
p.insertBefore({
|
||||
type: "VariableDeclaration",
|
||||
kind: "const",
|
||||
declarations: [variable],
|
||||
});
|
||||
} else if (
|
||||
specifier.type === "ImportSpecifier" &&
|
||||
specifier.imported &&
|
||||
specifier.imported.type === "Identifier" &&
|
||||
specifier.local &&
|
||||
specifier.local.type === "Identifier"
|
||||
) {
|
||||
if (!(specifier.imported.name in module)) {
|
||||
throw new Error(
|
||||
"babel-plugin-inline-constants: cannot access `" +
|
||||
specifier.imported.name +
|
||||
"` from `" +
|
||||
p.node.source.value +
|
||||
"`"
|
||||
);
|
||||
}
|
||||
|
||||
const variableValue = toLiteral(module[specifier.imported.name]);
|
||||
const variable = t.variableDeclarator(
|
||||
t.identifier(specifier.local.name),
|
||||
variableValue
|
||||
);
|
||||
|
||||
p.insertBefore({
|
||||
type: "VariableDeclaration",
|
||||
kind: "const",
|
||||
declarations: [variable],
|
||||
});
|
||||
} else {
|
||||
throw new Error("Cannot handle specifier `" + specifier.type + "`");
|
||||
}
|
||||
}
|
||||
p.remove();
|
||||
};
|
||||
|
||||
return {
|
||||
visitor: {
|
||||
ImportDeclaration: importDeclaration,
|
||||
},
|
||||
};
|
||||
};
|
@@ -1,10 +1,11 @@
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
const path = require("path");
|
||||
const env = require("./env.js");
|
||||
const paths = require("./paths.js");
|
||||
|
||||
// Files from NPM Packages that should not be imported
|
||||
module.exports.ignorePackages = ({ latestBuild }) => [
|
||||
// Bloats bundle and it's not used.
|
||||
path.resolve(require.resolve("moment"), "../locale"),
|
||||
// Part of yaml.js and only used for !!js functions that we don't use
|
||||
require.resolve("esprima"),
|
||||
];
|
||||
@@ -18,8 +19,7 @@ module.exports.emptyPackages = ({ latestBuild }) =>
|
||||
require.resolve("@polymer/paper-styles/default-theme.js"),
|
||||
// Loads stuff from a CDN
|
||||
require.resolve("@polymer/font-roboto/roboto.js"),
|
||||
require.resolve("@vaadin/vaadin-material-styles/typography.js"),
|
||||
require.resolve("@vaadin/vaadin-material-styles/font-icons.js"),
|
||||
require.resolve("@vaadin/vaadin-material-styles/font-roboto.js"),
|
||||
// Compatibility not needed for latest builds
|
||||
latestBuild &&
|
||||
// wrapped in require.resolve so it blows up if file no longer exists
|
||||
@@ -51,29 +51,17 @@ module.exports.terserOptions = (latestBuild) => ({
|
||||
|
||||
module.exports.babelOptions = ({ latestBuild }) => ({
|
||||
babelrc: false,
|
||||
compact: false,
|
||||
presets: [
|
||||
!latestBuild && [
|
||||
"@babel/preset-env",
|
||||
require("@babel/preset-env").default,
|
||||
{
|
||||
useBuiltIns: "entry",
|
||||
corejs: "3.15",
|
||||
bugfixes: true,
|
||||
corejs: "3.6",
|
||||
},
|
||||
],
|
||||
"@babel/preset-typescript",
|
||||
require("@babel/preset-typescript").default,
|
||||
].filter(Boolean),
|
||||
plugins: [
|
||||
[
|
||||
path.resolve(
|
||||
paths.polymer_dir,
|
||||
"build-scripts/babel-plugins/inline-constants-plugin.js"
|
||||
),
|
||||
{
|
||||
modules: ["@mdi/js"],
|
||||
ignoreModuleNotFound: true,
|
||||
},
|
||||
],
|
||||
// Part of ES2018. Converts {...a, b: 2} to Object.assign({}, a, {b: 2})
|
||||
!latestBuild && [
|
||||
"@babel/plugin-proposal-object-rest-spread",
|
||||
@@ -84,18 +72,23 @@ module.exports.babelOptions = ({ latestBuild }) => ({
|
||||
"@babel/plugin-syntax-dynamic-import",
|
||||
"@babel/plugin-proposal-optional-chaining",
|
||||
"@babel/plugin-proposal-nullish-coalescing-operator",
|
||||
["@babel/plugin-proposal-decorators", { decoratorsBeforeExport: true }],
|
||||
["@babel/plugin-proposal-private-methods", { loose: true }],
|
||||
["@babel/plugin-proposal-private-property-in-object", { loose: true }],
|
||||
["@babel/plugin-proposal-class-properties", { loose: true }],
|
||||
[
|
||||
require("@babel/plugin-proposal-decorators").default,
|
||||
{ decoratorsBeforeExport: true },
|
||||
],
|
||||
[
|
||||
require("@babel/plugin-proposal-class-properties").default,
|
||||
{ loose: true },
|
||||
],
|
||||
].filter(Boolean),
|
||||
exclude: [
|
||||
// \\ for Windows, / for Mac OS and Linux
|
||||
/node_modules[\\/]core-js/,
|
||||
/node_modules[\\/]webpack[\\/]buildin/,
|
||||
],
|
||||
});
|
||||
|
||||
// Are already ES5, cause warnings when babelified.
|
||||
module.exports.babelExclude = () => [
|
||||
require.resolve("@mdi/js/mdi.js"),
|
||||
require.resolve("hls.js"),
|
||||
];
|
||||
|
||||
const outputPath = (outputRoot, latestBuild) =>
|
||||
path.resolve(outputRoot, latestBuild ? "frontend_latest" : "frontend_es5");
|
||||
|
||||
|
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const paths = require("./paths.js");
|
||||
|
@@ -47,8 +47,8 @@ gulp.task(
|
||||
gulp.parallel("gen-icons-json", "build-translations"),
|
||||
"copy-static-app",
|
||||
env.useRollup() ? "rollup-prod-app" : "webpack-prod-app",
|
||||
// Don't compress running tests
|
||||
...(env.isTest() ? [] : ["compress-app"]),
|
||||
...// Don't compress running tests
|
||||
(env.isTest() ? [] : ["compress-app"]),
|
||||
gulp.parallel(
|
||||
"gen-pages-prod",
|
||||
"gen-index-app-prod",
|
||||
|
@@ -5,32 +5,32 @@ require("./translations");
|
||||
|
||||
gulp.task(
|
||||
"clean",
|
||||
gulp.parallel("clean-translations", () =>
|
||||
del([paths.app_output_root, paths.build_dir])
|
||||
)
|
||||
gulp.parallel("clean-translations", function cleanOutputAndBuildDir() {
|
||||
return del([paths.app_output_root, paths.build_dir]);
|
||||
})
|
||||
);
|
||||
|
||||
gulp.task(
|
||||
"clean-demo",
|
||||
gulp.parallel("clean-translations", () =>
|
||||
del([paths.demo_output_root, paths.build_dir])
|
||||
)
|
||||
gulp.parallel("clean-translations", function cleanOutputAndBuildDir() {
|
||||
return del([paths.demo_output_root, paths.build_dir]);
|
||||
})
|
||||
);
|
||||
|
||||
gulp.task(
|
||||
"clean-cast",
|
||||
gulp.parallel("clean-translations", () =>
|
||||
del([paths.cast_output_root, paths.build_dir])
|
||||
)
|
||||
gulp.parallel("clean-translations", function cleanOutputAndBuildDir() {
|
||||
return del([paths.cast_output_root, paths.build_dir]);
|
||||
})
|
||||
);
|
||||
|
||||
gulp.task("clean-hassio", () =>
|
||||
del([paths.hassio_output_root, paths.build_dir])
|
||||
);
|
||||
gulp.task("clean-hassio", function cleanOutputAndBuildDir() {
|
||||
return del([paths.hassio_output_root, paths.build_dir]);
|
||||
});
|
||||
|
||||
gulp.task(
|
||||
"clean-gallery",
|
||||
gulp.parallel("clean-translations", () =>
|
||||
del([paths.gallery_output_root, paths.build_dir])
|
||||
)
|
||||
gulp.parallel("clean-translations", function cleanOutputAndBuildDir() {
|
||||
return del([paths.gallery_output_root, paths.build_dir]);
|
||||
})
|
||||
);
|
||||
|
@@ -302,23 +302,15 @@ gulp.task("gen-index-hassio-prod", async () => {
|
||||
|
||||
function writeHassioEntrypoint(latestEntrypoint, es5Entrypoint) {
|
||||
fs.mkdirSync(paths.hassio_output_root, { recursive: true });
|
||||
// Safari 12 and below does not have a compliant ES2015 implementation of template literals, so we ship ES5
|
||||
fs.writeFileSync(
|
||||
path.resolve(paths.hassio_output_root, "entrypoint.js"),
|
||||
`
|
||||
function loadES5() {
|
||||
try {
|
||||
new Function("import('${latestEntrypoint}')")();
|
||||
} catch (err) {
|
||||
var el = document.createElement('script');
|
||||
el.src = '${es5Entrypoint}';
|
||||
document.body.appendChild(el);
|
||||
}
|
||||
if (/.*Version\\/(?:11|12)(?:\\.\\d+)*.*Safari\\//.test(navigator.userAgent)) {
|
||||
loadES5();
|
||||
} else {
|
||||
try {
|
||||
new Function("import('${latestEntrypoint}')")();
|
||||
} catch (err) {
|
||||
loadES5();
|
||||
}
|
||||
}
|
||||
`,
|
||||
{ encoding: "utf-8" }
|
||||
|
@@ -2,6 +2,7 @@
|
||||
|
||||
const gulp = require("gulp");
|
||||
const path = require("path");
|
||||
const cpx = require("cpx");
|
||||
const fs = require("fs-extra");
|
||||
const paths = require("../paths");
|
||||
|
||||
@@ -12,10 +13,8 @@ const polyPath = (...parts) => path.resolve(paths.polymer_dir, ...parts);
|
||||
const copyFileDir = (fromFile, toDir) =>
|
||||
fs.copySync(fromFile, path.join(toDir, path.basename(fromFile)));
|
||||
|
||||
const genStaticPath =
|
||||
(staticDir) =>
|
||||
(...parts) =>
|
||||
path.resolve(staticDir, ...parts);
|
||||
const genStaticPath = (staticDir) => (...parts) =>
|
||||
path.resolve(staticDir, ...parts);
|
||||
|
||||
function copyTranslations(staticDir) {
|
||||
const staticPath = genStaticPath(staticDir);
|
||||
@@ -63,12 +62,9 @@ function copyLoaderJS(staticDir) {
|
||||
function copyFonts(staticDir) {
|
||||
const staticPath = genStaticPath(staticDir);
|
||||
// Local fonts
|
||||
fs.copySync(
|
||||
npmPath("roboto-fontface/fonts/roboto/"),
|
||||
staticPath("fonts/roboto/"),
|
||||
{
|
||||
filter: (src) => !src.includes(".") || src.endsWith(".woff2"),
|
||||
}
|
||||
cpx.copySync(
|
||||
npmPath("roboto-fontface/fonts/roboto/*.woff2"),
|
||||
staticPath("fonts/roboto")
|
||||
);
|
||||
}
|
||||
|
||||
@@ -89,11 +85,6 @@ gulp.task("copy-translations-app", async () => {
|
||||
copyTranslations(staticDir);
|
||||
});
|
||||
|
||||
gulp.task("copy-translations-supervisor", async () => {
|
||||
const staticDir = paths.hassio_output_static;
|
||||
copyTranslations(staticDir);
|
||||
});
|
||||
|
||||
gulp.task("copy-static-app", async () => {
|
||||
const staticDir = paths.app_output_static;
|
||||
// Basic static files
|
||||
|
@@ -10,8 +10,6 @@ require("./gen-icons-json.js");
|
||||
require("./webpack.js");
|
||||
require("./compress.js");
|
||||
require("./rollup.js");
|
||||
require("./gather-static.js");
|
||||
require("./translations.js");
|
||||
|
||||
gulp.task(
|
||||
"develop-hassio",
|
||||
@@ -22,8 +20,6 @@ gulp.task(
|
||||
"clean-hassio",
|
||||
"gen-icons-json",
|
||||
"gen-index-hassio-dev",
|
||||
"build-supervisor-translations",
|
||||
"copy-translations-supervisor",
|
||||
env.useRollup() ? "rollup-watch-hassio" : "webpack-watch-hassio"
|
||||
)
|
||||
);
|
||||
@@ -36,8 +32,6 @@ gulp.task(
|
||||
},
|
||||
"clean-hassio",
|
||||
"gen-icons-json",
|
||||
"build-supervisor-translations",
|
||||
"copy-translations-supervisor",
|
||||
env.useRollup() ? "rollup-prod-hassio" : "webpack-prod-hassio",
|
||||
"gen-index-hassio-prod",
|
||||
...// Don't compress running tests
|
||||
|
@@ -1,5 +1,3 @@
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
|
||||
const crypto = require("crypto");
|
||||
const del = require("del");
|
||||
const path = require("path");
|
||||
@@ -28,6 +26,13 @@ gulp.task("translations-enable-merge-backend", (done) => {
|
||||
done();
|
||||
});
|
||||
|
||||
String.prototype.rsplit = function (sep, maxsplit) {
|
||||
var split = this.split(sep);
|
||||
return maxsplit
|
||||
? [split.slice(0, -maxsplit).join(sep)].concat(split.slice(-maxsplit))
|
||||
: split;
|
||||
};
|
||||
|
||||
// Panel translations which should be split from the core translations.
|
||||
const TRANSLATION_FRAGMENTS = Object.keys(
|
||||
require("../../src/translations/en.json").ui.panel
|
||||
@@ -35,7 +40,7 @@ const TRANSLATION_FRAGMENTS = Object.keys(
|
||||
|
||||
function recursiveFlatten(prefix, data) {
|
||||
let output = {};
|
||||
Object.keys(data).forEach((key) => {
|
||||
Object.keys(data).forEach(function (key) {
|
||||
if (typeof data[key] === "object") {
|
||||
output = {
|
||||
...output,
|
||||
@@ -96,19 +101,15 @@ function lokaliseTransform(data, original, file) {
|
||||
if (value instanceof Object) {
|
||||
output[key] = lokaliseTransform(value, original, file);
|
||||
} else {
|
||||
output[key] = value.replace(re_key_reference, (_match, lokalise_key) => {
|
||||
const replace = lokalise_key.split("::").reduce((tr, k) => {
|
||||
output[key] = value.replace(re_key_reference, (match, key) => {
|
||||
const replace = key.split("::").reduce((tr, k) => {
|
||||
if (!tr) {
|
||||
throw Error(
|
||||
`Invalid key placeholder ${lokalise_key} in ${file.path}`
|
||||
);
|
||||
throw Error(`Invalid key placeholder ${key} in ${file.path}`);
|
||||
}
|
||||
return tr[k];
|
||||
}, original);
|
||||
if (typeof replace !== "string") {
|
||||
throw Error(
|
||||
`Invalid key placeholder ${lokalise_key} in ${file.path}`
|
||||
);
|
||||
throw Error(`Invalid key placeholder ${key} in ${file.path}`);
|
||||
}
|
||||
return replace;
|
||||
});
|
||||
@@ -117,7 +118,9 @@ function lokaliseTransform(data, original, file) {
|
||||
return output;
|
||||
}
|
||||
|
||||
gulp.task("clean-translations", () => del([workDir]));
|
||||
gulp.task("clean-translations", function () {
|
||||
return del([workDir]);
|
||||
});
|
||||
|
||||
gulp.task("ensure-translations-build-dir", (done) => {
|
||||
if (!fs.existsSync(workDir)) {
|
||||
@@ -126,7 +129,7 @@ gulp.task("ensure-translations-build-dir", (done) => {
|
||||
done();
|
||||
});
|
||||
|
||||
gulp.task("create-test-metadata", (cb) => {
|
||||
gulp.task("create-test-metadata", function (cb) {
|
||||
fs.writeFile(
|
||||
workDir + "/testMetadata.json",
|
||||
JSON.stringify({
|
||||
@@ -140,13 +143,17 @@ gulp.task("create-test-metadata", (cb) => {
|
||||
|
||||
gulp.task(
|
||||
"create-test-translation",
|
||||
gulp.series("create-test-metadata", () =>
|
||||
gulp
|
||||
gulp.series("create-test-metadata", function createTestTranslation() {
|
||||
return gulp
|
||||
.src(path.join(paths.translations_src, "en.json"))
|
||||
.pipe(transform((data, _file) => recursiveEmpty(data)))
|
||||
.pipe(
|
||||
transform(function (data, file) {
|
||||
return recursiveEmpty(data);
|
||||
})
|
||||
)
|
||||
.pipe(rename("test.json"))
|
||||
.pipe(gulp.dest(workDir))
|
||||
)
|
||||
.pipe(gulp.dest(workDir));
|
||||
})
|
||||
);
|
||||
|
||||
/**
|
||||
@@ -158,7 +165,7 @@ gulp.task(
|
||||
* project is buildable immediately after merging new translation keys, since
|
||||
* the Lokalise update to translations/en.json will not happen immediately.
|
||||
*/
|
||||
gulp.task("build-master-translation", () => {
|
||||
gulp.task("build-master-translation", function () {
|
||||
const src = [path.join(paths.translations_src, "en.json")];
|
||||
|
||||
if (mergeBackend) {
|
||||
@@ -167,7 +174,11 @@ gulp.task("build-master-translation", () => {
|
||||
|
||||
return gulp
|
||||
.src(src)
|
||||
.pipe(transform((data, file) => lokaliseTransform(data, data, file)))
|
||||
.pipe(
|
||||
transform(function (data, file) {
|
||||
return lokaliseTransform(data, data, file);
|
||||
})
|
||||
)
|
||||
.pipe(
|
||||
merge({
|
||||
fileName: "translationMaster.json",
|
||||
@@ -176,14 +187,18 @@ gulp.task("build-master-translation", () => {
|
||||
.pipe(gulp.dest(workDir));
|
||||
});
|
||||
|
||||
gulp.task("build-merged-translations", () =>
|
||||
gulp
|
||||
gulp.task("build-merged-translations", function () {
|
||||
return gulp
|
||||
.src([inFrontendDir + "/*.json", workDir + "/test.json"], {
|
||||
allowEmpty: true,
|
||||
})
|
||||
.pipe(transform((data, file) => lokaliseTransform(data, data, file)))
|
||||
.pipe(
|
||||
foreach((stream, file) => {
|
||||
transform(function (data, file) {
|
||||
return lokaliseTransform(data, data, file);
|
||||
})
|
||||
)
|
||||
.pipe(
|
||||
foreach(function (stream, file) {
|
||||
// For each language generate a merged json file. It begins with the master
|
||||
// translation as a failsafe for untranslated strings, and merges all parent
|
||||
// tags into one file for each specific subtag
|
||||
@@ -215,17 +230,17 @@ gulp.task("build-merged-translations", () =>
|
||||
)
|
||||
.pipe(gulp.dest(fullDir));
|
||||
})
|
||||
)
|
||||
);
|
||||
);
|
||||
});
|
||||
|
||||
let taskName;
|
||||
var taskName;
|
||||
|
||||
const splitTasks = [];
|
||||
TRANSLATION_FRAGMENTS.forEach((fragment) => {
|
||||
taskName = "build-translation-fragment-" + fragment;
|
||||
gulp.task(taskName, () =>
|
||||
gulp.task(taskName, function () {
|
||||
// Return only the translations for this fragment.
|
||||
gulp
|
||||
return gulp
|
||||
.src(fullDir + "/*.json")
|
||||
.pipe(
|
||||
transform((data) => ({
|
||||
@@ -236,33 +251,32 @@ TRANSLATION_FRAGMENTS.forEach((fragment) => {
|
||||
},
|
||||
}))
|
||||
)
|
||||
.pipe(gulp.dest(workDir + "/" + fragment))
|
||||
);
|
||||
.pipe(gulp.dest(workDir + "/" + fragment));
|
||||
});
|
||||
splitTasks.push(taskName);
|
||||
});
|
||||
|
||||
taskName = "build-translation-core";
|
||||
gulp.task(taskName, () =>
|
||||
gulp.task(taskName, function () {
|
||||
// Remove the fragment translations from the core translation.
|
||||
gulp
|
||||
return gulp
|
||||
.src(fullDir + "/*.json")
|
||||
.pipe(
|
||||
transform((data, _file) => {
|
||||
transform((data, file) => {
|
||||
TRANSLATION_FRAGMENTS.forEach((fragment) => {
|
||||
delete data.ui.panel[fragment];
|
||||
});
|
||||
delete data.supervisor;
|
||||
return data;
|
||||
})
|
||||
)
|
||||
.pipe(gulp.dest(coreDir))
|
||||
);
|
||||
.pipe(gulp.dest(coreDir));
|
||||
});
|
||||
|
||||
splitTasks.push(taskName);
|
||||
|
||||
gulp.task("build-flattened-translations", () =>
|
||||
gulp.task("build-flattened-translations", function () {
|
||||
// Flatten the split versions of our translations, and move them into outDir
|
||||
gulp
|
||||
return gulp
|
||||
.src(
|
||||
TRANSLATION_FRAGMENTS.map(
|
||||
(fragment) => workDir + "/" + fragment + "/*.json"
|
||||
@@ -270,45 +284,41 @@ gulp.task("build-flattened-translations", () =>
|
||||
{ base: workDir }
|
||||
)
|
||||
.pipe(
|
||||
transform((data) =>
|
||||
transform(function (data) {
|
||||
// Polymer.AppLocalizeBehavior requires flattened json
|
||||
flatten(data)
|
||||
)
|
||||
return flatten(data);
|
||||
})
|
||||
)
|
||||
.pipe(
|
||||
rename((filePath) => {
|
||||
if (filePath.dirname === "core") {
|
||||
filePath.dirname = "";
|
||||
}
|
||||
// In dev we create the file with the fake hash in the filename
|
||||
if (!env.isProdBuild()) {
|
||||
filePath.basename += "-dev";
|
||||
}
|
||||
})
|
||||
)
|
||||
.pipe(gulp.dest(outDir))
|
||||
);
|
||||
.pipe(gulp.dest(outDir));
|
||||
});
|
||||
|
||||
const fingerprints = {};
|
||||
|
||||
gulp.task("build-translation-fingerprints", () => {
|
||||
// Fingerprint full file of each language
|
||||
const files = fs.readdirSync(fullDir);
|
||||
gulp.task(
|
||||
"build-translation-fingerprints",
|
||||
function fingerprintTranslationFiles() {
|
||||
// Fingerprint full file of each language
|
||||
const files = fs.readdirSync(fullDir);
|
||||
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
fingerprints[files[i].split(".")[0]] = {
|
||||
// In dev we create fake hashes
|
||||
hash: env.isProdBuild()
|
||||
? crypto
|
||||
.createHash("md5")
|
||||
.update(fs.readFileSync(path.join(fullDir, files[i]), "utf-8"))
|
||||
.digest("hex")
|
||||
: "dev",
|
||||
};
|
||||
}
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
fingerprints[files[i].split(".")[0]] = {
|
||||
// In dev we create fake hashes
|
||||
hash: env.isProdBuild()
|
||||
? crypto
|
||||
.createHash("md5")
|
||||
.update(fs.readFileSync(path.join(fullDir, files[i]), "utf-8"))
|
||||
.digest("hex")
|
||||
: "dev",
|
||||
};
|
||||
}
|
||||
|
||||
// In dev we create the file with the fake hash in the filename
|
||||
if (env.isProdBuild()) {
|
||||
mapFiles(outDir, ".json", (filename) => {
|
||||
const parsed = path.parse(filename);
|
||||
|
||||
@@ -324,80 +334,12 @@ gulp.task("build-translation-fingerprints", () => {
|
||||
}`
|
||||
);
|
||||
});
|
||||
|
||||
const stream = source("translationFingerprints.json");
|
||||
stream.write(JSON.stringify(fingerprints));
|
||||
process.nextTick(() => stream.end());
|
||||
return stream.pipe(vinylBuffer()).pipe(gulp.dest(workDir));
|
||||
}
|
||||
|
||||
const stream = source("translationFingerprints.json");
|
||||
stream.write(JSON.stringify(fingerprints));
|
||||
process.nextTick(() => stream.end());
|
||||
return stream.pipe(vinylBuffer()).pipe(gulp.dest(workDir));
|
||||
});
|
||||
|
||||
gulp.task("build-translation-fragment-supervisor", () =>
|
||||
gulp
|
||||
.src(fullDir + "/*.json")
|
||||
.pipe(transform((data) => data.supervisor))
|
||||
.pipe(gulp.dest(workDir + "/supervisor"))
|
||||
);
|
||||
|
||||
gulp.task("build-translation-flatten-supervisor", () =>
|
||||
gulp
|
||||
.src(workDir + "/supervisor/*.json")
|
||||
.pipe(
|
||||
transform((data) =>
|
||||
// Polymer.AppLocalizeBehavior requires flattened json
|
||||
flatten(data)
|
||||
)
|
||||
)
|
||||
.pipe(gulp.dest(outDir))
|
||||
);
|
||||
|
||||
gulp.task("build-translation-write-metadata", () =>
|
||||
gulp
|
||||
.src(
|
||||
[
|
||||
path.join(paths.translations_src, "translationMetadata.json"),
|
||||
workDir + "/testMetadata.json",
|
||||
workDir + "/translationFingerprints.json",
|
||||
],
|
||||
{ allowEmpty: true }
|
||||
)
|
||||
.pipe(merge({}))
|
||||
.pipe(
|
||||
transform((data) => {
|
||||
const newData = {};
|
||||
Object.entries(data).forEach(([key, value]) => {
|
||||
// Filter out translations without native name.
|
||||
if (value.nativeName) {
|
||||
newData[key] = value;
|
||||
} else {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn(
|
||||
`Skipping language ${key}. Native name was not translated.`
|
||||
);
|
||||
}
|
||||
});
|
||||
return newData;
|
||||
})
|
||||
)
|
||||
.pipe(
|
||||
transform((data) => ({
|
||||
fragments: TRANSLATION_FRAGMENTS,
|
||||
translations: data,
|
||||
}))
|
||||
)
|
||||
.pipe(rename("translationMetadata.json"))
|
||||
.pipe(gulp.dest(workDir))
|
||||
);
|
||||
|
||||
gulp.task(
|
||||
"create-translations",
|
||||
gulp.series(
|
||||
env.isProdBuild() ? (done) => done() : "create-test-translation",
|
||||
"build-master-translation",
|
||||
"build-merged-translations",
|
||||
gulp.parallel(...splitTasks),
|
||||
"build-flattened-translations"
|
||||
)
|
||||
);
|
||||
|
||||
gulp.task(
|
||||
@@ -405,22 +347,48 @@ gulp.task(
|
||||
gulp.series(
|
||||
"clean-translations",
|
||||
"ensure-translations-build-dir",
|
||||
"create-translations",
|
||||
"build-translation-fingerprints",
|
||||
"build-translation-write-metadata"
|
||||
)
|
||||
);
|
||||
|
||||
gulp.task(
|
||||
"build-supervisor-translations",
|
||||
gulp.series(
|
||||
"clean-translations",
|
||||
"ensure-translations-build-dir",
|
||||
env.isProdBuild() ? (done) => done() : "create-test-translation",
|
||||
"build-master-translation",
|
||||
"build-merged-translations",
|
||||
"build-translation-fragment-supervisor",
|
||||
"build-translation-flatten-supervisor",
|
||||
gulp.parallel(...splitTasks),
|
||||
"build-flattened-translations",
|
||||
"build-translation-fingerprints",
|
||||
"build-translation-write-metadata"
|
||||
function writeMetadata() {
|
||||
return gulp
|
||||
.src(
|
||||
[
|
||||
path.join(paths.translations_src, "translationMetadata.json"),
|
||||
workDir + "/testMetadata.json",
|
||||
workDir + "/translationFingerprints.json",
|
||||
],
|
||||
{ allowEmpty: true }
|
||||
)
|
||||
.pipe(merge({}))
|
||||
.pipe(
|
||||
transform(function (data) {
|
||||
const newData = {};
|
||||
Object.entries(data).forEach(([key, value]) => {
|
||||
// Filter out translations without native name.
|
||||
if (data[key].nativeName) {
|
||||
newData[key] = data[key];
|
||||
} else {
|
||||
console.warn(
|
||||
`Skipping language ${key}. Native name was not translated.`
|
||||
);
|
||||
}
|
||||
if (data[key]) newData[key] = value;
|
||||
});
|
||||
return newData;
|
||||
})
|
||||
)
|
||||
.pipe(
|
||||
transform((data) => ({
|
||||
fragments: TRANSLATION_FRAGMENTS,
|
||||
translations: data,
|
||||
}))
|
||||
)
|
||||
.pipe(rename("translationMetadata.json"))
|
||||
.pipe(gulp.dest(workDir));
|
||||
}
|
||||
)
|
||||
);
|
||||
|
@@ -1,6 +1,4 @@
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
// Tasks to run webpack.
|
||||
const fs = require("fs");
|
||||
const gulp = require("gulp");
|
||||
const webpack = require("webpack");
|
||||
const WebpackDevServer = require("webpack-dev-server");
|
||||
@@ -20,13 +18,6 @@ const bothBuilds = (createConfigFunc, params) => [
|
||||
createConfigFunc({ ...params, latestBuild: false }),
|
||||
];
|
||||
|
||||
const isWsl =
|
||||
fs.existsSync("/proc/version") &&
|
||||
fs
|
||||
.readFileSync("/proc/version", "utf-8")
|
||||
.toLocaleLowerCase()
|
||||
.includes("microsoft");
|
||||
|
||||
/**
|
||||
* @param {{
|
||||
* compiler: import("webpack").Compiler,
|
||||
@@ -45,7 +36,7 @@ const runDevServer = ({
|
||||
open: true,
|
||||
watchContentBase: true,
|
||||
contentBase,
|
||||
}).listen(port, listenHost, (err) => {
|
||||
}).listen(port, listenHost, function (err) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
@@ -66,7 +57,6 @@ const doneHandler = (done) => (err, stats) => {
|
||||
}
|
||||
|
||||
if (stats.hasErrors() || stats.hasWarnings()) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(stats.toString("minimal"));
|
||||
}
|
||||
|
||||
@@ -88,14 +78,13 @@ const prodBuild = (conf) =>
|
||||
|
||||
gulp.task("webpack-watch-app", () => {
|
||||
// This command will run forever because we don't close compiler
|
||||
webpack(
|
||||
process.env.ES5
|
||||
? bothBuilds(createAppConfig, { isProdBuild: false })
|
||||
: createAppConfig({ isProdBuild: false, latestBuild: true })
|
||||
).watch({ poll: isWsl }, doneHandler());
|
||||
webpack(createAppConfig({ isProdBuild: false, latestBuild: true })).watch(
|
||||
{ ignored: /build-translations/ },
|
||||
doneHandler()
|
||||
);
|
||||
gulp.watch(
|
||||
path.join(paths.translations_src, "en.json"),
|
||||
gulp.series("create-translations", "copy-translations-app")
|
||||
gulp.series("build-translations", "copy-translations-app")
|
||||
);
|
||||
});
|
||||
|
||||
@@ -148,12 +137,7 @@ gulp.task("webpack-watch-hassio", () => {
|
||||
isProdBuild: false,
|
||||
latestBuild: true,
|
||||
})
|
||||
).watch({ ignored: /build-translations/, poll: isWsl }, doneHandler());
|
||||
|
||||
gulp.watch(
|
||||
path.join(paths.translations_src, "en.json"),
|
||||
gulp.series("build-supervisor-translations", "copy-translations-supervisor")
|
||||
);
|
||||
).watch({}, doneHandler());
|
||||
});
|
||||
|
||||
gulp.task("webpack-prod-hassio", () =>
|
||||
|
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
const path = require("path");
|
||||
|
||||
module.exports = {
|
||||
@@ -35,7 +34,6 @@ module.exports = {
|
||||
|
||||
hassio_dir: path.resolve(__dirname, "../hassio"),
|
||||
hassio_output_root: path.resolve(__dirname, "../hassio/build"),
|
||||
hassio_output_static: path.resolve(__dirname, "../hassio/build/static"),
|
||||
hassio_output_latest: path.resolve(
|
||||
__dirname,
|
||||
"../hassio/build/frontend_latest"
|
||||
|
File diff suppressed because one or more lines are too long
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
const path = require("path");
|
||||
|
||||
const commonjs = require("@rollup/plugin-commonjs");
|
||||
@@ -33,77 +32,88 @@ const createRollupConfig = ({
|
||||
publicPath,
|
||||
dontHash,
|
||||
isWDS,
|
||||
}) => ({
|
||||
/**
|
||||
* @type { import("rollup").InputOptions }
|
||||
*/
|
||||
inputOptions: {
|
||||
input: entry,
|
||||
// Some entry points contain no JavaScript. This setting silences a warning about that.
|
||||
// https://rollupjs.org/guide/en/#preserveentrysignatures
|
||||
preserveEntrySignatures: false,
|
||||
plugins: [
|
||||
ignore({
|
||||
files: bundle.emptyPackages({ latestBuild }),
|
||||
}),
|
||||
resolve({
|
||||
extensions,
|
||||
preferBuiltins: false,
|
||||
browser: true,
|
||||
rootDir: paths.polymer_dir,
|
||||
}),
|
||||
commonjs(),
|
||||
json(),
|
||||
babel({
|
||||
...bundle.babelOptions({ latestBuild }),
|
||||
extensions,
|
||||
babelHelpers: isWDS ? "inline" : "bundled",
|
||||
}),
|
||||
string({
|
||||
// Import certain extensions as strings
|
||||
include: [path.join(paths.polymer_dir, "node_modules/**/*.css")],
|
||||
}),
|
||||
replace(bundle.definedVars({ isProdBuild, latestBuild, defineOverlay })),
|
||||
!isWDS &&
|
||||
manifest({
|
||||
publicPath,
|
||||
}) => {
|
||||
return {
|
||||
/**
|
||||
* @type { import("rollup").InputOptions }
|
||||
*/
|
||||
inputOptions: {
|
||||
input: entry,
|
||||
// Some entry points contain no JavaScript. This setting silences a warning about that.
|
||||
// https://rollupjs.org/guide/en/#preserveentrysignatures
|
||||
preserveEntrySignatures: false,
|
||||
plugins: [
|
||||
ignore({
|
||||
files: bundle.emptyPackages({ latestBuild }),
|
||||
}),
|
||||
!isWDS && worker(),
|
||||
!isWDS && dontHashPlugin({ dontHash }),
|
||||
!isWDS && isProdBuild && terser(bundle.terserOptions(latestBuild)),
|
||||
!isWDS &&
|
||||
isStatsBuild &&
|
||||
visualizer({
|
||||
// https://github.com/btd/rollup-plugin-visualizer#options
|
||||
open: true,
|
||||
sourcemap: true,
|
||||
resolve({
|
||||
extensions,
|
||||
preferBuiltins: false,
|
||||
browser: true,
|
||||
rootDir: paths.polymer_dir,
|
||||
}),
|
||||
].filter(Boolean),
|
||||
},
|
||||
/**
|
||||
* @type { import("rollup").OutputOptions }
|
||||
*/
|
||||
outputOptions: {
|
||||
// https://rollupjs.org/guide/en/#outputdir
|
||||
dir: outputPath,
|
||||
// https://rollupjs.org/guide/en/#outputformat
|
||||
format: latestBuild ? "es" : "systemjs",
|
||||
// https://rollupjs.org/guide/en/#outputexternallivebindings
|
||||
externalLiveBindings: false,
|
||||
// https://rollupjs.org/guide/en/#outputentryfilenames
|
||||
// https://rollupjs.org/guide/en/#outputchunkfilenames
|
||||
// https://rollupjs.org/guide/en/#outputassetfilenames
|
||||
entryFileNames:
|
||||
isProdBuild && !isStatsBuild ? "[name]-[hash].js" : "[name].js",
|
||||
chunkFileNames: isProdBuild && !isStatsBuild ? "c.[hash].js" : "[name].js",
|
||||
assetFileNames: isProdBuild && !isStatsBuild ? "a.[hash].js" : "[name].js",
|
||||
// https://rollupjs.org/guide/en/#outputsourcemap
|
||||
sourcemap: isProdBuild ? true : "inline",
|
||||
},
|
||||
});
|
||||
commonjs({
|
||||
namedExports: {
|
||||
"js-yaml": ["safeDump", "safeLoad"],
|
||||
},
|
||||
}),
|
||||
json(),
|
||||
babel({
|
||||
...bundle.babelOptions({ latestBuild }),
|
||||
extensions,
|
||||
exclude: bundle.babelExclude(),
|
||||
babelHelpers: isWDS ? "inline" : "bundled",
|
||||
}),
|
||||
string({
|
||||
// Import certain extensions as strings
|
||||
include: [path.join(paths.polymer_dir, "node_modules/**/*.css")],
|
||||
}),
|
||||
replace(
|
||||
bundle.definedVars({ isProdBuild, latestBuild, defineOverlay })
|
||||
),
|
||||
!isWDS &&
|
||||
manifest({
|
||||
publicPath,
|
||||
}),
|
||||
!isWDS && worker(),
|
||||
!isWDS && dontHashPlugin({ dontHash }),
|
||||
!isWDS && isProdBuild && terser(bundle.terserOptions(latestBuild)),
|
||||
!isWDS &&
|
||||
isStatsBuild &&
|
||||
visualizer({
|
||||
// https://github.com/btd/rollup-plugin-visualizer#options
|
||||
open: true,
|
||||
sourcemap: true,
|
||||
}),
|
||||
].filter(Boolean),
|
||||
},
|
||||
/**
|
||||
* @type { import("rollup").OutputOptions }
|
||||
*/
|
||||
outputOptions: {
|
||||
// https://rollupjs.org/guide/en/#outputdir
|
||||
dir: outputPath,
|
||||
// https://rollupjs.org/guide/en/#outputformat
|
||||
format: latestBuild ? "es" : "systemjs",
|
||||
// https://rollupjs.org/guide/en/#outputexternallivebindings
|
||||
externalLiveBindings: false,
|
||||
// https://rollupjs.org/guide/en/#outputentryfilenames
|
||||
// https://rollupjs.org/guide/en/#outputchunkfilenames
|
||||
// https://rollupjs.org/guide/en/#outputassetfilenames
|
||||
entryFileNames:
|
||||
isProdBuild && !isStatsBuild ? "[name]-[hash].js" : "[name].js",
|
||||
chunkFileNames:
|
||||
isProdBuild && !isStatsBuild ? "c.[hash].js" : "[name].js",
|
||||
assetFileNames:
|
||||
isProdBuild && !isStatsBuild ? "a.[hash].js" : "[name].js",
|
||||
// https://rollupjs.org/guide/en/#outputsourcemap
|
||||
sourcemap: isProdBuild ? true : "inline",
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const createAppConfig = ({ isProdBuild, latestBuild, isStatsBuild, isWDS }) =>
|
||||
createRollupConfig(
|
||||
const createAppConfig = ({ isProdBuild, latestBuild, isStatsBuild, isWDS }) => {
|
||||
return createRollupConfig(
|
||||
bundle.config.app({
|
||||
isProdBuild,
|
||||
latestBuild,
|
||||
@@ -111,24 +121,31 @@ const createAppConfig = ({ isProdBuild, latestBuild, isStatsBuild, isWDS }) =>
|
||||
isWDS,
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const createDemoConfig = ({ isProdBuild, latestBuild, isStatsBuild }) =>
|
||||
createRollupConfig(
|
||||
const createDemoConfig = ({ isProdBuild, latestBuild, isStatsBuild }) => {
|
||||
return createRollupConfig(
|
||||
bundle.config.demo({
|
||||
isProdBuild,
|
||||
latestBuild,
|
||||
isStatsBuild,
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const createCastConfig = ({ isProdBuild, latestBuild }) =>
|
||||
createRollupConfig(bundle.config.cast({ isProdBuild, latestBuild }));
|
||||
const createCastConfig = ({ isProdBuild, latestBuild }) => {
|
||||
return createRollupConfig(bundle.config.cast({ isProdBuild, latestBuild }));
|
||||
};
|
||||
|
||||
const createHassioConfig = ({ isProdBuild, latestBuild }) =>
|
||||
createRollupConfig(bundle.config.hassio({ isProdBuild, latestBuild }));
|
||||
const createHassioConfig = ({ isProdBuild, latestBuild }) => {
|
||||
return createRollupConfig(bundle.config.hassio({ isProdBuild, latestBuild }));
|
||||
};
|
||||
|
||||
const createGalleryConfig = ({ isProdBuild, latestBuild }) =>
|
||||
createRollupConfig(bundle.config.gallery({ isProdBuild, latestBuild }));
|
||||
const createGalleryConfig = ({ isProdBuild, latestBuild }) => {
|
||||
return createRollupConfig(
|
||||
bundle.config.gallery({ isProdBuild, latestBuild })
|
||||
);
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
createAppConfig,
|
||||
|
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
const path = require("path");
|
||||
const fs = require("fs");
|
||||
|
||||
|
@@ -1,10 +1,9 @@
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
const webpack = require("webpack");
|
||||
const path = require("path");
|
||||
const TerserPlugin = require("terser-webpack-plugin");
|
||||
const { WebpackManifestPlugin } = require("webpack-manifest-plugin");
|
||||
const ManifestPlugin = require("webpack-manifest-plugin");
|
||||
const paths = require("./paths.js");
|
||||
const bundle = require("./bundle.js");
|
||||
const bundle = require("./bundle");
|
||||
const log = require("fancy-log");
|
||||
|
||||
class LogStartCompilePlugin {
|
||||
@@ -47,18 +46,15 @@ const createWebpackConfig = ({
|
||||
rules: [
|
||||
{
|
||||
test: /\.m?js$|\.ts$/,
|
||||
exclude: bundle.babelExclude(),
|
||||
use: {
|
||||
loader: "babel-loader",
|
||||
options: {
|
||||
...bundle.babelOptions({ latestBuild }),
|
||||
cacheDirectory: !isProdBuild,
|
||||
cacheCompression: false,
|
||||
},
|
||||
options: bundle.babelOptions({ latestBuild }),
|
||||
},
|
||||
},
|
||||
{
|
||||
test: /\.css$/,
|
||||
type: "asset/source",
|
||||
use: "raw-loader",
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -70,11 +66,9 @@ const createWebpackConfig = ({
|
||||
terserOptions: bundle.terserOptions(latestBuild),
|
||||
}),
|
||||
],
|
||||
moduleIds: isProdBuild && !isStatsBuild ? "deterministic" : "named",
|
||||
chunkIds: isProdBuild && !isStatsBuild ? "deterministic" : "named",
|
||||
},
|
||||
plugins: [
|
||||
new WebpackManifestPlugin({
|
||||
new ManifestPlugin({
|
||||
// Only include the JS of entrypoints
|
||||
filter: (file) => file.isInitial && !file.name.endsWith(".map"),
|
||||
}),
|
||||
@@ -100,7 +94,6 @@ const createWebpackConfig = ({
|
||||
? path.resolve(context, resource)
|
||||
: require.resolve(resource);
|
||||
} catch (err) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(
|
||||
"Error in Home Assistant ignore plugin",
|
||||
resource,
|
||||
@@ -118,25 +111,31 @@ const createWebpackConfig = ({
|
||||
new RegExp(bundle.emptyPackages({ latestBuild }).join("|")),
|
||||
path.resolve(paths.polymer_dir, "src/util/empty.js")
|
||||
),
|
||||
// We need to change the import of the polyfill for EventTarget, so we replace the polyfill file with our customized one
|
||||
new webpack.NormalModuleReplacementPlugin(
|
||||
new RegExp(
|
||||
require.resolve(
|
||||
"lit-virtualizer/lib/uni-virtualizer/lib/polyfillLoaders/EventTarget.js"
|
||||
)
|
||||
),
|
||||
path.resolve(paths.polymer_dir, "src/resources/EventTarget-ponyfill.js")
|
||||
),
|
||||
!isProdBuild && new LogStartCompilePlugin(),
|
||||
].filter(Boolean),
|
||||
resolve: {
|
||||
extensions: [".ts", ".js", ".json"],
|
||||
alias: {
|
||||
"lit/decorators$": "lit/decorators.js",
|
||||
"lit/directive$": "lit/directive.js",
|
||||
"lit/polyfill-support$": "lit/polyfill-support.js",
|
||||
},
|
||||
},
|
||||
output: {
|
||||
filename: ({ chunk }) => {
|
||||
if (!isProdBuild || isStatsBuild || dontHash.has(chunk.name)) {
|
||||
if (!isProdBuild || dontHash.has(chunk.name)) {
|
||||
return `${chunk.name}.js`;
|
||||
}
|
||||
return `${chunk.name}.${chunk.hash.substr(0, 8)}.js`;
|
||||
},
|
||||
chunkFilename:
|
||||
isProdBuild && !isStatsBuild ? "[chunkhash:8].js" : "[id].chunk.js",
|
||||
isProdBuild && !isStatsBuild
|
||||
? "chunk.[chunkhash].js"
|
||||
: "[name].chunk.js",
|
||||
path: outputPath,
|
||||
publicPath,
|
||||
// To silence warning in worker plugin
|
||||
@@ -145,24 +144,33 @@ const createWebpackConfig = ({
|
||||
};
|
||||
};
|
||||
|
||||
const createAppConfig = ({ isProdBuild, latestBuild, isStatsBuild }) =>
|
||||
createWebpackConfig(
|
||||
const createAppConfig = ({ isProdBuild, latestBuild, isStatsBuild }) => {
|
||||
return createWebpackConfig(
|
||||
bundle.config.app({ isProdBuild, latestBuild, isStatsBuild })
|
||||
);
|
||||
};
|
||||
|
||||
const createDemoConfig = ({ isProdBuild, latestBuild, isStatsBuild }) =>
|
||||
createWebpackConfig(
|
||||
const createDemoConfig = ({ isProdBuild, latestBuild, isStatsBuild }) => {
|
||||
return createWebpackConfig(
|
||||
bundle.config.demo({ isProdBuild, latestBuild, isStatsBuild })
|
||||
);
|
||||
};
|
||||
|
||||
const createCastConfig = ({ isProdBuild, latestBuild }) =>
|
||||
createWebpackConfig(bundle.config.cast({ isProdBuild, latestBuild }));
|
||||
const createCastConfig = ({ isProdBuild, latestBuild }) => {
|
||||
return createWebpackConfig(bundle.config.cast({ isProdBuild, latestBuild }));
|
||||
};
|
||||
|
||||
const createHassioConfig = ({ isProdBuild, latestBuild }) =>
|
||||
createWebpackConfig(bundle.config.hassio({ isProdBuild, latestBuild }));
|
||||
const createHassioConfig = ({ isProdBuild, latestBuild }) => {
|
||||
return createWebpackConfig(
|
||||
bundle.config.hassio({ isProdBuild, latestBuild })
|
||||
);
|
||||
};
|
||||
|
||||
const createGalleryConfig = ({ isProdBuild, latestBuild }) =>
|
||||
createWebpackConfig(bundle.config.gallery({ isProdBuild, latestBuild }));
|
||||
const createGalleryConfig = ({ isProdBuild, latestBuild }) => {
|
||||
return createWebpackConfig(
|
||||
bundle.config.gallery({ isProdBuild, latestBuild })
|
||||
);
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
createAppConfig,
|
||||
|
@@ -139,7 +139,7 @@
|
||||
Your authentication credentials or Home Assistant url are never sent
|
||||
to the Cloud. You can validate this behavior in
|
||||
<a
|
||||
href="https://github.com/home-assistant/frontend/tree/dev/cast"
|
||||
href="https://github.com/home-assistant/home-assistant-polymer/tree/dev/cast"
|
||||
target="_blank"
|
||||
>the source code</a
|
||||
>.
|
||||
|
@@ -1,9 +1,16 @@
|
||||
import "@material/mwc-button/mwc-button";
|
||||
import "@polymer/paper-item/paper-icon-item";
|
||||
import "@polymer/paper-listbox/paper-listbox";
|
||||
import { Auth, Connection } from "home-assistant-js-websocket";
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import {
|
||||
css,
|
||||
CSSResult,
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
property,
|
||||
internalProperty,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { CastManager } from "../../../../src/cast/cast_manager";
|
||||
import {
|
||||
castSendShowLovelaceView,
|
||||
@@ -25,6 +32,7 @@ import {
|
||||
import "../../../../src/layouts/hass-loading-screen";
|
||||
import { generateDefaultViewConfig } from "../../../../src/panels/lovelace/common/generate-lovelace-config";
|
||||
import "./hc-layout";
|
||||
import "@material/mwc-button/mwc-button";
|
||||
|
||||
@customElement("hc-cast")
|
||||
class HcCast extends LitElement {
|
||||
@@ -34,19 +42,21 @@ class HcCast extends LitElement {
|
||||
|
||||
@property() public castManager!: CastManager;
|
||||
|
||||
@state() private askWrite = false;
|
||||
@internalProperty() private askWrite = false;
|
||||
|
||||
@state() private lovelaceConfig?: LovelaceConfig | null;
|
||||
@internalProperty() private lovelaceConfig?: LovelaceConfig | null;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (this.lovelaceConfig === undefined) {
|
||||
return html`<hass-loading-screen no-toolbar></hass-loading-screen>`;
|
||||
return html` <hass-loading-screen no-toolbar></hass-loading-screen>> `;
|
||||
}
|
||||
|
||||
const error =
|
||||
this.castManager.castState === "NO_DEVICES_AVAILABLE"
|
||||
? html`
|
||||
<p>There were no suitable Chromecast devices to cast to found.</p>
|
||||
<p>
|
||||
There were no suitable Chromecast devices to cast to found.
|
||||
</p>
|
||||
`
|
||||
: undefined;
|
||||
|
||||
@@ -196,7 +206,7 @@ class HcCast extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
static get styles(): CSSResult {
|
||||
return css`
|
||||
.center-item {
|
||||
display: flex;
|
||||
|
@@ -11,8 +11,15 @@ import {
|
||||
getAuth,
|
||||
getAuthOptions,
|
||||
} from "home-assistant-js-websocket";
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, state } from "lit/decorators";
|
||||
import {
|
||||
css,
|
||||
CSSResult,
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
TemplateResult,
|
||||
internalProperty,
|
||||
} from "lit-element";
|
||||
import { CastManager, getCastManager } from "../../../../src/cast/cast_manager";
|
||||
import { castSendShowDemo } from "../../../../src/cast/receiver_messages";
|
||||
import {
|
||||
@@ -53,19 +60,19 @@ const INTRO = html`
|
||||
|
||||
@customElement("hc-connect")
|
||||
export class HcConnect extends LitElement {
|
||||
@state() private loading = false;
|
||||
@internalProperty() private loading = false;
|
||||
|
||||
// If we had stored credentials but we cannot connect,
|
||||
// show a screen asking retry or logout.
|
||||
@state() private cannotConnect = false;
|
||||
@internalProperty() private cannotConnect = false;
|
||||
|
||||
@state() private error?: string | TemplateResult;
|
||||
@internalProperty() private error?: string | TemplateResult;
|
||||
|
||||
@state() private auth?: Auth;
|
||||
@internalProperty() private auth?: Auth;
|
||||
|
||||
@state() private connection?: Connection;
|
||||
@internalProperty() private connection?: Connection;
|
||||
|
||||
@state() private castManager?: CastManager | null;
|
||||
@internalProperty() private castManager?: CastManager | null;
|
||||
|
||||
private openDemo = false;
|
||||
|
||||
@@ -79,7 +86,9 @@ export class HcConnect extends LitElement {
|
||||
</div>
|
||||
<div class="card-actions">
|
||||
<a href="/">
|
||||
<mwc-button> Retry </mwc-button>
|
||||
<mwc-button>
|
||||
Retry
|
||||
</mwc-button>
|
||||
</a>
|
||||
<div class="spacer"></div>
|
||||
<mwc-button @click=${this._handleLogout}>Log out</mwc-button>
|
||||
@@ -290,7 +299,7 @@ export class HcConnect extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
static get styles(): CSSResult {
|
||||
return css`
|
||||
.card-content a {
|
||||
color: var(--primary-color);
|
||||
|
@@ -4,8 +4,15 @@ import {
|
||||
getUser,
|
||||
HassUser,
|
||||
} from "home-assistant-js-websocket";
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import {
|
||||
css,
|
||||
CSSResult,
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
property,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import "../../../../src/components/ha-card";
|
||||
|
||||
@customElement("hc-layout")
|
||||
@@ -62,7 +69,7 @@ class HcLayout extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
static get styles(): CSSResult {
|
||||
return css`
|
||||
:host {
|
||||
display: flex;
|
||||
@@ -91,12 +98,8 @@ class HcLayout extends LitElement {
|
||||
line-height: 32px;
|
||||
padding: 24px 16px 16px;
|
||||
display: block;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.hero {
|
||||
border-radius: 4px 4px 0 0;
|
||||
}
|
||||
.subtitle {
|
||||
font-size: 14px;
|
||||
color: var(--secondary-text-color);
|
||||
|
@@ -5,8 +5,8 @@ import {
|
||||
import { castContext } from "../cast_context";
|
||||
|
||||
export const castDemoLovelace: () => LovelaceConfig = () => {
|
||||
const touchSupported =
|
||||
castContext.getDeviceCapabilities().touch_input_supported;
|
||||
const touchSupported = castContext.getDeviceCapabilities()
|
||||
.touch_input_supported;
|
||||
return {
|
||||
views: [
|
||||
{
|
||||
|
@@ -1,5 +1,10 @@
|
||||
import { html, TemplateResult } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import {
|
||||
customElement,
|
||||
html,
|
||||
internalProperty,
|
||||
property,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { mockHistory } from "../../../../demo/src/stubs/history";
|
||||
import { LovelaceConfig } from "../../../../src/data/lovelace";
|
||||
import {
|
||||
@@ -16,7 +21,7 @@ import "./hc-lovelace";
|
||||
class HcDemo extends HassElement {
|
||||
@property({ attribute: false }) public lovelacePath!: string;
|
||||
|
||||
@state() private _lovelaceConfig?: LovelaceConfig;
|
||||
@internalProperty() private _lovelaceConfig?: LovelaceConfig;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this._lovelaceConfig) {
|
||||
@@ -33,10 +38,10 @@ class HcDemo extends HassElement {
|
||||
|
||||
protected firstUpdated(changedProps) {
|
||||
super.firstUpdated(changedProps);
|
||||
this._initializeHass();
|
||||
this._initialize();
|
||||
}
|
||||
|
||||
private async _initializeHass() {
|
||||
private async _initialize() {
|
||||
const initial: Partial<MockHomeAssistant> = {
|
||||
// Override updateHass so that the correct hass lifecycle methods are called
|
||||
updateHass: (hassUpdate: Partial<HomeAssistant>) =>
|
||||
|
@@ -1,5 +1,12 @@
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import {
|
||||
css,
|
||||
CSSResult,
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
property,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { HomeAssistant } from "../../../../src/types";
|
||||
|
||||
@customElement("hc-launch-screen")
|
||||
@@ -22,7 +29,7 @@ class HcLaunchScreen extends LitElement {
|
||||
`;
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
static get styles(): CSSResult {
|
||||
return css`
|
||||
:host {
|
||||
display: block;
|
||||
|
@@ -1,5 +1,12 @@
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import {
|
||||
css,
|
||||
CSSResult,
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
property,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { LovelaceConfig } from "../../../../src/data/lovelace";
|
||||
import { Lovelace } from "../../../../src/panels/lovelace/types";
|
||||
import "../../../../src/panels/lovelace/views/hui-view";
|
||||
@@ -28,12 +35,11 @@ class HcLovelace extends LitElement {
|
||||
}
|
||||
const lovelace: Lovelace = {
|
||||
config: this.lovelaceConfig,
|
||||
rawConfig: this.lovelaceConfig,
|
||||
editMode: false,
|
||||
urlPath: this.urlPath!,
|
||||
enableFullEditMode: () => undefined,
|
||||
mode: "storage",
|
||||
locale: this.hass.locale,
|
||||
language: "en",
|
||||
saveConfig: async () => undefined,
|
||||
deleteConfig: async () => undefined,
|
||||
setEditMode: () => undefined,
|
||||
@@ -84,11 +90,10 @@ class HcLovelace extends LitElement {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
static get styles(): CSSResult {
|
||||
return css`
|
||||
:host {
|
||||
min-height: 100vh;
|
||||
height: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
box-sizing: border-box;
|
||||
|
@@ -3,8 +3,12 @@ import {
|
||||
getAuth,
|
||||
UnsubscribeFunc,
|
||||
} from "home-assistant-js-websocket";
|
||||
import { html, TemplateResult } from "lit";
|
||||
import { customElement, state } from "lit/decorators";
|
||||
import {
|
||||
customElement,
|
||||
html,
|
||||
internalProperty,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { CAST_NS } from "../../../../src/cast/const";
|
||||
import {
|
||||
ConnectMessage,
|
||||
@@ -32,13 +36,13 @@ let resourcesLoaded = false;
|
||||
|
||||
@customElement("hc-main")
|
||||
export class HcMain extends HassElement {
|
||||
@state() private _showDemo = false;
|
||||
@internalProperty() private _showDemo = false;
|
||||
|
||||
@state() private _lovelaceConfig?: LovelaceConfig;
|
||||
@internalProperty() private _lovelaceConfig?: LovelaceConfig;
|
||||
|
||||
@state() private _lovelacePath: string | number | null = null;
|
||||
@internalProperty() private _lovelacePath: string | number | null = null;
|
||||
|
||||
@state() private _error?: string;
|
||||
@internalProperty() private _error?: string;
|
||||
|
||||
private _unsubLovelace?: UnsubscribeFunc;
|
||||
|
||||
@@ -217,17 +221,11 @@ export class HcMain extends HassElement {
|
||||
}
|
||||
|
||||
private async _generateLovelaceConfig() {
|
||||
const { generateLovelaceDashboardStrategy } = await import(
|
||||
"../../../../src/panels/lovelace/strategies/get-strategy"
|
||||
const { generateLovelaceConfigFromHass } = await import(
|
||||
"../../../../src/panels/lovelace/common/generate-lovelace-config"
|
||||
);
|
||||
this._handleNewLovelaceConfig(
|
||||
await generateLovelaceDashboardStrategy(
|
||||
{
|
||||
hass: this.hass!,
|
||||
narrow: false,
|
||||
},
|
||||
"original-states"
|
||||
)
|
||||
await generateLovelaceConfigFromHass(this.hass!)
|
||||
);
|
||||
}
|
||||
|
||||
|
@@ -246,15 +246,11 @@ export const demoEntitiesArsaboo: DemoConfig["entities"] = (localize) =>
|
||||
|
||||
"light.living_room_lights": {
|
||||
entity_id: "light.living_room_lights",
|
||||
state: "on",
|
||||
state: "off",
|
||||
attributes: {
|
||||
min_mireds: 111,
|
||||
max_mireds: 400,
|
||||
brightness: 175,
|
||||
color_temp: 300,
|
||||
supported_color_modes: ["brightness", "color_temp"],
|
||||
friendly_name: "Living Room Lights",
|
||||
color_mode: "color_temp",
|
||||
supported_features: 55,
|
||||
},
|
||||
},
|
||||
@@ -267,27 +263,13 @@ export const demoEntitiesArsaboo: DemoConfig["entities"] = (localize) =>
|
||||
},
|
||||
"light.kitchen_lights": {
|
||||
entity_id: "light.kitchen_lights",
|
||||
state: "on",
|
||||
attributes: {
|
||||
min_mireds: 111,
|
||||
max_mireds: 400,
|
||||
brightness: 200,
|
||||
rgb_color: [255, 175, 96],
|
||||
supported_color_modes: ["brightness", "color_temp", "rgb"],
|
||||
color_mode: "rgb",
|
||||
friendly_name: "Kitchen Lights",
|
||||
supported_features: 55,
|
||||
},
|
||||
},
|
||||
"light.lifx5": {
|
||||
entity_id: "light.lifx5",
|
||||
state: "off",
|
||||
attributes: {
|
||||
supported_color_modes: ["brightness"],
|
||||
friendly_name: "Garage Lights",
|
||||
friendly_name: "Kitchen Lights",
|
||||
supported_features: 1,
|
||||
},
|
||||
},
|
||||
|
||||
"sensor.plexspy": {
|
||||
entity_id: "sensor.plexspy",
|
||||
state: "0",
|
||||
@@ -500,6 +482,16 @@ export const demoEntitiesArsaboo: DemoConfig["entities"] = (localize) =>
|
||||
icon: "hademo:history",
|
||||
},
|
||||
},
|
||||
"light.lifx5": {
|
||||
entity_id: "light.lifx5",
|
||||
state: "on",
|
||||
attributes: {
|
||||
min_mireds: 111,
|
||||
max_mireds: 400,
|
||||
friendly_name: "Garage Lights",
|
||||
supported_features: 55,
|
||||
},
|
||||
},
|
||||
"sensor.alok_to_home": {
|
||||
entity_id: "sensor.alok_to_home",
|
||||
state: "41",
|
||||
|
@@ -29,11 +29,6 @@ export const demoLovelaceArsaboo: DemoConfig["lovelace"] = (localize) => ({
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Energy distribution today",
|
||||
type: "energy-distribution",
|
||||
link_dashboard: true,
|
||||
},
|
||||
{
|
||||
type: "thermostat",
|
||||
entity: "climate.upstairs",
|
||||
@@ -118,7 +113,8 @@ export const demoLovelaceArsaboo: DemoConfig["lovelace"] = (localize) => ({
|
||||
on: "/assets/arsaboo/icons/light_bulb_on.png",
|
||||
},
|
||||
state_filter: {
|
||||
on: "brightness(130%) saturate(1.5) drop-shadow(0px 0px 10px gold)",
|
||||
on:
|
||||
"brightness(130%) saturate(1.5) drop-shadow(0px 0px 10px gold)",
|
||||
off: "brightness(80%) saturate(0.8)",
|
||||
},
|
||||
style: {
|
||||
@@ -200,7 +196,8 @@ export const demoLovelaceArsaboo: DemoConfig["lovelace"] = (localize) => ({
|
||||
on: "/assets/arsaboo/icons/light_bulb_on.png",
|
||||
},
|
||||
state_filter: {
|
||||
on: "brightness(130%) saturate(1.5) drop-shadow(0px 0px 10px gold)",
|
||||
on:
|
||||
"brightness(130%) saturate(1.5) drop-shadow(0px 0px 10px gold)",
|
||||
off: "brightness(80%) saturate(0.8)",
|
||||
},
|
||||
style: {
|
||||
@@ -280,7 +277,8 @@ export const demoLovelaceArsaboo: DemoConfig["lovelace"] = (localize) => ({
|
||||
on: "/assets/arsaboo/icons/light_bulb_on.png",
|
||||
},
|
||||
state_filter: {
|
||||
on: "brightness(130%) saturate(1.5) drop-shadow(0px 0px 10px gold)",
|
||||
on:
|
||||
"brightness(130%) saturate(1.5) drop-shadow(0px 0px 10px gold)",
|
||||
off: "brightness(80%) saturate(0.8)",
|
||||
},
|
||||
style: {
|
||||
@@ -317,7 +315,8 @@ export const demoLovelaceArsaboo: DemoConfig["lovelace"] = (localize) => ({
|
||||
on: "/assets/arsaboo/icons/light_bulb_on.png",
|
||||
},
|
||||
state_filter: {
|
||||
on: "brightness(130%) saturate(1.5) drop-shadow(0px 0px 10px gold)",
|
||||
on:
|
||||
"brightness(130%) saturate(1.5) drop-shadow(0px 0px 10px gold)",
|
||||
off: "brightness(80%) saturate(0.8)",
|
||||
},
|
||||
style: {
|
||||
|
@@ -1,6 +1,5 @@
|
||||
import { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
|
||||
import { Lovelace } from "../../../src/panels/lovelace/types";
|
||||
import { energyEntities } from "../stubs/entities";
|
||||
import { DemoConfig } from "./types";
|
||||
|
||||
export const demoConfigs: Array<() => Promise<DemoConfig>> = [
|
||||
@@ -13,8 +12,9 @@ export const demoConfigs: Array<() => Promise<DemoConfig>> = [
|
||||
// eslint-disable-next-line import/no-mutable-exports
|
||||
export let selectedDemoConfigIndex = 0;
|
||||
// eslint-disable-next-line import/no-mutable-exports
|
||||
export let selectedDemoConfig: Promise<DemoConfig> =
|
||||
demoConfigs[selectedDemoConfigIndex]();
|
||||
export let selectedDemoConfig: Promise<DemoConfig> = demoConfigs[
|
||||
selectedDemoConfigIndex
|
||||
]();
|
||||
|
||||
export const setDemoConfig = async (
|
||||
hass: MockHomeAssistant,
|
||||
@@ -28,7 +28,6 @@ export const setDemoConfig = async (
|
||||
selectedDemoConfig = confProm;
|
||||
|
||||
hass.addEntities(config.entities(hass.localize), true);
|
||||
hass.addEntities(energyEntities());
|
||||
lovelace.saveConfig(config.lovelace(hass.localize));
|
||||
hass.mockTheme(config.theme());
|
||||
};
|
||||
|
@@ -980,7 +980,8 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
|
||||
icon: "mdi:account-off",
|
||||
custom_ui_state_card: "state-card-custom-ui",
|
||||
templates: {
|
||||
icon: "if (state === 'on') return 'mdi:account'; else if (state === 'off') return 'mdi:account-off';\n",
|
||||
icon:
|
||||
"if (state === 'on') return 'mdi:account'; else if (state === 'off') return 'mdi:account-off';\n",
|
||||
icon_color:
|
||||
"if (state === 'on') return 'rgb(56, 150, 56)'; else if (state === 'off') return 'rgb(249, 251, 255)';\n",
|
||||
},
|
||||
@@ -1004,7 +1005,8 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
|
||||
icon: "mdi:account-multiple-minus",
|
||||
custom_ui_state_card: "state-card-custom-ui",
|
||||
templates: {
|
||||
icon: "if (state === 'on') return 'mdi:account-group'; else if (state === 'off') return 'mdi:account-multiple-minus';\n",
|
||||
icon:
|
||||
"if (state === 'on') return 'mdi:account-group'; else if (state === 'off') return 'mdi:account-multiple-minus';\n",
|
||||
icon_color:
|
||||
"if (state === 'on') return 'rgb(56, 150, 56)'; else if (state === 'off') return 'rgb(249, 251, 255)';\n",
|
||||
},
|
||||
@@ -1112,9 +1114,6 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
|
||||
min_mireds: 153,
|
||||
max_mireds: 500,
|
||||
brightness: 63,
|
||||
color_temp: 200,
|
||||
supported_color_modes: ["brightness", "color_temp", "rgb"],
|
||||
color_mode: "color_temp",
|
||||
friendly_name: "Upstairs lights",
|
||||
supported_features: 63,
|
||||
custom_ui_state_card: "state-card-custom-ui",
|
||||
@@ -1126,7 +1125,6 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
|
||||
attributes: {
|
||||
friendly_name: "Walk in closet lights",
|
||||
supported_features: 41,
|
||||
supported_color_modes: ["brightness", "color_temp"],
|
||||
custom_ui_state_card: "state-card-custom-ui",
|
||||
icon: "mdi:wall-sconce",
|
||||
},
|
||||
@@ -1138,8 +1136,6 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
|
||||
brightness: 254,
|
||||
friendly_name: "Outdoor lights",
|
||||
supported_features: 41,
|
||||
supported_color_modes: ["brightness"],
|
||||
color_mode: "brightness",
|
||||
custom_ui_state_card: "state-card-custom-ui",
|
||||
icon: "mdi:wall-sconce",
|
||||
},
|
||||
@@ -1152,8 +1148,6 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
|
||||
max_mireds: 500,
|
||||
brightness: 128,
|
||||
color_temp: 366,
|
||||
supported_color_modes: ["brightness", "color_temp", "rgb"],
|
||||
color_mode: "color_temp",
|
||||
effect_list: ["colorloop"],
|
||||
friendly_name: "Downstairs lights",
|
||||
supported_features: 63,
|
||||
@@ -1313,7 +1307,6 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
|
||||
attributes: {
|
||||
min_mireds: 153,
|
||||
max_mireds: 500,
|
||||
supported_color_modes: ["brightness", "color_temp"],
|
||||
is_deconz_group: false,
|
||||
friendly_name: "Bedside Lamp",
|
||||
supported_features: 63,
|
||||
@@ -1327,7 +1320,6 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
|
||||
attributes: {
|
||||
min_mireds: 153,
|
||||
max_mireds: 500,
|
||||
supported_color_modes: ["brightness", "color_temp"],
|
||||
is_deconz_group: false,
|
||||
friendly_name: "Floorlamp Reading Light",
|
||||
supported_features: 43,
|
||||
@@ -1343,8 +1335,6 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
|
||||
max_mireds: 500,
|
||||
brightness: 128,
|
||||
color_temp: 366,
|
||||
supported_color_modes: ["brightness", "color_temp", "rgb"],
|
||||
color_mode: "color_temp",
|
||||
effect_list: ["colorloop"],
|
||||
is_deconz_group: false,
|
||||
friendly_name: "Hallway window light",
|
||||
@@ -1359,7 +1349,6 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
|
||||
attributes: {
|
||||
brightness: 77,
|
||||
is_deconz_group: false,
|
||||
supported_color_modes: ["brightness"],
|
||||
friendly_name: "Isa Ceiling Light",
|
||||
supported_features: 41,
|
||||
custom_ui_state_card: "state-card-custom-ui",
|
||||
@@ -1374,8 +1363,6 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
|
||||
max_mireds: 500,
|
||||
brightness: 150,
|
||||
color_temp: 366,
|
||||
supported_color_modes: ["brightness", "color_temp"],
|
||||
color_mode: "color_temp",
|
||||
effect_list: ["colorloop"],
|
||||
is_deconz_group: false,
|
||||
friendly_name: "Floorlamp",
|
||||
@@ -1390,7 +1377,6 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
|
||||
attributes: {
|
||||
friendly_name: "Bedroom Ceiling Light",
|
||||
supported_features: 41,
|
||||
supported_color_modes: ["brightness"],
|
||||
custom_ui_state_card: "state-card-custom-ui",
|
||||
icon: "mdi:ceiling-light",
|
||||
},
|
||||
@@ -1401,7 +1387,6 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
|
||||
attributes: {
|
||||
friendly_name: "Nightlight",
|
||||
supported_features: 17,
|
||||
supported_color_modes: ["brightness"],
|
||||
custom_ui_state_card: "state-card-custom-ui",
|
||||
icon: "mdi:lamp",
|
||||
},
|
||||
@@ -1768,7 +1753,6 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
|
||||
power_consumption: 2.2,
|
||||
friendly_name: "Upstairs Hallway Light",
|
||||
supported_features: 33,
|
||||
supported_color_modes: ["brightness"],
|
||||
custom_ui_state_card: "state-card-custom-ui",
|
||||
icon: "mdi:ceiling-light",
|
||||
},
|
||||
@@ -1784,7 +1768,6 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
|
||||
power_consumption: 0,
|
||||
friendly_name: "Dining Room Light",
|
||||
supported_features: 33,
|
||||
supported_color_modes: ["brightness"],
|
||||
custom_ui_state_card: "state-card-custom-ui",
|
||||
icon: "mdi:ceiling-light",
|
||||
},
|
||||
@@ -1800,7 +1783,6 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
|
||||
power_consumption: 0,
|
||||
friendly_name: "Living room Spotlights",
|
||||
supported_features: 33,
|
||||
supported_color_modes: ["brightness"],
|
||||
custom_ui_state_card: "state-card-custom-ui",
|
||||
icon: "mdi:track-light",
|
||||
},
|
||||
@@ -1817,7 +1799,6 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
|
||||
power_consumption: 2.5,
|
||||
friendly_name: "Passage Lights",
|
||||
supported_features: 33,
|
||||
supported_color_modes: ["brightness"],
|
||||
custom_ui_state_card: "state-card-custom-ui",
|
||||
icon: "mdi:track-light",
|
||||
},
|
||||
@@ -1862,7 +1843,6 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
|
||||
power_consumption: 37.4,
|
||||
friendly_name: "Kitchen Lights",
|
||||
supported_features: 33,
|
||||
supported_color_modes: ["brightness"],
|
||||
custom_ui_state_card: "state-card-custom-ui",
|
||||
icon: "mdi:track-light",
|
||||
},
|
||||
|
@@ -440,43 +440,57 @@ export const demoLovelaceTeachingbirds: DemoConfig["lovelace"] = () => ({
|
||||
type: "horizontal-stack",
|
||||
},
|
||||
{
|
||||
type: "grid",
|
||||
columns: 2,
|
||||
cards: [
|
||||
{
|
||||
graph: "line",
|
||||
type: "sensor",
|
||||
entity: "sensor.temperature_bedroom",
|
||||
cards: [
|
||||
{
|
||||
graph: "line",
|
||||
type: "sensor",
|
||||
entity: "sensor.temperature_bedroom",
|
||||
},
|
||||
{
|
||||
graph: "line",
|
||||
type: "sensor",
|
||||
name: "S's room",
|
||||
entity: "sensor.temperature_stefan",
|
||||
},
|
||||
],
|
||||
type: "horizontal-stack",
|
||||
},
|
||||
{
|
||||
graph: "line",
|
||||
type: "sensor",
|
||||
name: "S's room",
|
||||
entity: "sensor.temperature_stefan",
|
||||
cards: [
|
||||
{
|
||||
graph: "line",
|
||||
type: "sensor",
|
||||
entity: "sensor.temperature_passage",
|
||||
},
|
||||
{
|
||||
graph: "line",
|
||||
type: "sensor",
|
||||
name: "Bathroom",
|
||||
entity: "sensor.temperature_downstairs_bathroom",
|
||||
},
|
||||
],
|
||||
type: "horizontal-stack",
|
||||
},
|
||||
{
|
||||
graph: "line",
|
||||
type: "sensor",
|
||||
entity: "sensor.temperature_passage",
|
||||
},
|
||||
{
|
||||
graph: "line",
|
||||
type: "sensor",
|
||||
name: "Bathroom",
|
||||
entity: "sensor.temperature_downstairs_bathroom",
|
||||
},
|
||||
{
|
||||
graph: "line",
|
||||
type: "sensor",
|
||||
entity: "sensor.temperature_storage",
|
||||
},
|
||||
{
|
||||
graph: "line",
|
||||
type: "sensor",
|
||||
name: "Refrigerator",
|
||||
entity: "sensor.refrigerator",
|
||||
cards: [
|
||||
{
|
||||
graph: "line",
|
||||
type: "sensor",
|
||||
entity: "sensor.temperature_storage",
|
||||
},
|
||||
{
|
||||
graph: "line",
|
||||
type: "sensor",
|
||||
name: "Refrigerator",
|
||||
entity: "sensor.refrigerator",
|
||||
},
|
||||
],
|
||||
type: "horizontal-stack",
|
||||
},
|
||||
],
|
||||
type: "vertical-stack",
|
||||
},
|
||||
{
|
||||
entities: [
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/* eslint-disable */
|
||||
import { LitElement } from "lit";
|
||||
import { LitElement } from "lit-element";
|
||||
import "./card-tools";
|
||||
|
||||
class CardModder extends LitElement {
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/* eslint-disable */
|
||||
import { html, LitElement } from "lit";
|
||||
import { html, LitElement } from "lit-element";
|
||||
|
||||
if (!window.cardTools) {
|
||||
const version = 0.2;
|
||||
|
@@ -1,5 +1,12 @@
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, state } from "lit/decorators";
|
||||
import {
|
||||
css,
|
||||
CSSResult,
|
||||
customElement,
|
||||
html,
|
||||
internalProperty,
|
||||
LitElement,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { CastManager } from "../../../src/cast/cast_manager";
|
||||
import { castSendShowDemo } from "../../../src/cast/receiver_messages";
|
||||
import "../../../src/components/ha-icon";
|
||||
@@ -13,7 +20,7 @@ import { HomeAssistant } from "../../../src/types";
|
||||
class CastDemoRow extends LitElement implements LovelaceRow {
|
||||
public hass!: HomeAssistant;
|
||||
|
||||
@state() private _castManager?: CastManager | null;
|
||||
@internalProperty() private _castManager?: CastManager | null;
|
||||
|
||||
public setConfig(_config: CastConfig): void {
|
||||
// No config possible.
|
||||
@@ -66,7 +73,7 @@ class CastDemoRow extends LitElement implements LovelaceRow {
|
||||
this.style.display = this._castManager ? "" : "none";
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
static get styles(): CSSResult {
|
||||
return css`
|
||||
:host {
|
||||
display: flex;
|
||||
|
@@ -1,7 +1,14 @@
|
||||
import "@material/mwc-button";
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { property, state } from "lit/decorators";
|
||||
import { until } from "lit/directives/until";
|
||||
import {
|
||||
css,
|
||||
CSSResult,
|
||||
html,
|
||||
internalProperty,
|
||||
LitElement,
|
||||
property,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { until } from "lit-html/directives/until";
|
||||
import "../../../src/components/ha-card";
|
||||
import "../../../src/components/ha-circular-progress";
|
||||
import { LovelaceCardConfig } from "../../../src/data/lovelace";
|
||||
@@ -19,7 +26,7 @@ export class HADemoCard extends LitElement implements LovelaceCard {
|
||||
|
||||
@property({ attribute: false }) public hass!: MockHomeAssistant;
|
||||
|
||||
@state() private _switching = false;
|
||||
@internalProperty() private _switching?: boolean;
|
||||
|
||||
private _hidden = localStorage.hide_demo_card;
|
||||
|
||||
@@ -27,7 +34,12 @@ export class HADemoCard extends LitElement implements LovelaceCard {
|
||||
return this._hidden ? 0 : 2;
|
||||
}
|
||||
|
||||
public setConfig(_config: LovelaceCardConfig) {}
|
||||
public setConfig(
|
||||
// @ts-ignore
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
config: LovelaceCardConfig
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
) {}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (this._hidden) {
|
||||
@@ -101,7 +113,7 @@ export class HADemoCard extends LitElement implements LovelaceCard {
|
||||
}
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
static get styles(): CSSResult[] {
|
||||
return [
|
||||
css`
|
||||
a {
|
||||
|
@@ -1,3 +1,5 @@
|
||||
import "@polymer/polymer/lib/elements/dom-if";
|
||||
import "@polymer/polymer/lib/elements/dom-repeat";
|
||||
import "../../src/resources/ha-style";
|
||||
import "../../src/resources/roboto";
|
||||
import "../../src/resources/safari-14-attachshadow-patch";
|
||||
|
@@ -20,14 +20,11 @@ import { mockShoppingList } from "./stubs/shopping_list";
|
||||
import { mockSystemLog } from "./stubs/system_log";
|
||||
import { mockTemplate } from "./stubs/template";
|
||||
import { mockTranslations } from "./stubs/translations";
|
||||
import { mockEnergy } from "./stubs/energy";
|
||||
import { mockConfig } from "./stubs/config";
|
||||
import { energyEntities } from "./stubs/entities";
|
||||
|
||||
class HaDemo extends HomeAssistantAppEl {
|
||||
protected async _initializeHass() {
|
||||
protected async _initialize() {
|
||||
const initial: Partial<MockHomeAssistant> = {
|
||||
panelUrl: (this as any)._panelUrl,
|
||||
panelUrl: (this as any).panelUrl,
|
||||
// Override updateHass so that the correct hass lifecycle methods are called
|
||||
updateHass: (hassUpdate: Partial<HomeAssistant>) =>
|
||||
this._updateHass(hassUpdate),
|
||||
@@ -50,12 +47,8 @@ class HaDemo extends HomeAssistantAppEl {
|
||||
mockEvents(hass);
|
||||
mockMediaPlayer(hass);
|
||||
mockFrontend(hass);
|
||||
mockEnergy(hass);
|
||||
mockConfig(hass);
|
||||
mockPersistentNotification(hass);
|
||||
|
||||
hass.addEntities(energyEntities());
|
||||
|
||||
// Once config is loaded AND localize, set entities and apply theme.
|
||||
Promise.all([selectedDemoConfig, localizePromise]).then(
|
||||
([conf, localize]) => {
|
||||
@@ -77,7 +70,7 @@ class HaDemo extends HomeAssistantAppEl {
|
||||
}
|
||||
|
||||
e.preventDefault();
|
||||
navigate(href);
|
||||
navigate(this, href);
|
||||
},
|
||||
{ capture: true }
|
||||
);
|
||||
|
@@ -1,41 +0,0 @@
|
||||
import { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
|
||||
|
||||
export const mockConfig = (hass: MockHomeAssistant) => {
|
||||
hass.mockAPI("config/config_entries/entry", () => [
|
||||
{
|
||||
entry_id: "co2signal",
|
||||
domain: "co2signal",
|
||||
title: "CO2 Signal",
|
||||
source: "user",
|
||||
state: "loaded",
|
||||
supports_options: false,
|
||||
supports_unload: true,
|
||||
pref_disable_new_entities: false,
|
||||
pref_disable_polling: false,
|
||||
disabled_by: null,
|
||||
reason: null,
|
||||
},
|
||||
]);
|
||||
hass.mockWS("config/entity_registry/list", () => [
|
||||
{
|
||||
config_entry_id: "co2signal",
|
||||
device_id: "co2signal",
|
||||
area_id: null,
|
||||
disabled_by: null,
|
||||
entity_id: "sensor.co2_intensity",
|
||||
name: null,
|
||||
icon: null,
|
||||
platform: "co2signal",
|
||||
},
|
||||
{
|
||||
config_entry_id: "co2signal",
|
||||
device_id: "co2signal",
|
||||
area_id: null,
|
||||
disabled_by: null,
|
||||
entity_id: "sensor.grid_fossil_fuel_percentage",
|
||||
name: null,
|
||||
icon: null,
|
||||
platform: "co2signal",
|
||||
},
|
||||
]);
|
||||
};
|
@@ -1,134 +0,0 @@
|
||||
import { format, startOfToday, startOfTomorrow } from "date-fns";
|
||||
import { EnergySolarForecasts } from "../../../src/data/energy";
|
||||
import { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
|
||||
|
||||
export const mockEnergy = (hass: MockHomeAssistant) => {
|
||||
hass.mockWS("energy/get_prefs", () => ({
|
||||
energy_sources: [
|
||||
{
|
||||
type: "grid",
|
||||
flow_from: [
|
||||
{
|
||||
stat_energy_from: "sensor.energy_consumption_tarif_1",
|
||||
stat_cost: "sensor.energy_consumption_tarif_1_cost",
|
||||
entity_energy_from: "sensor.energy_consumption_tarif_1",
|
||||
entity_energy_price: null,
|
||||
number_energy_price: null,
|
||||
},
|
||||
{
|
||||
stat_energy_from: "sensor.energy_consumption_tarif_2",
|
||||
stat_cost: "sensor.energy_consumption_tarif_2_cost",
|
||||
entity_energy_from: "sensor.energy_consumption_tarif_2",
|
||||
entity_energy_price: null,
|
||||
number_energy_price: null,
|
||||
},
|
||||
],
|
||||
flow_to: [
|
||||
{
|
||||
stat_energy_to: "sensor.energy_production_tarif_1",
|
||||
stat_compensation: "sensor.energy_production_tarif_1_compensation",
|
||||
entity_energy_to: "sensor.energy_production_tarif_1",
|
||||
entity_energy_price: null,
|
||||
number_energy_price: null,
|
||||
},
|
||||
{
|
||||
stat_energy_to: "sensor.energy_production_tarif_2",
|
||||
stat_compensation: "sensor.energy_production_tarif_2_compensation",
|
||||
entity_energy_to: "sensor.energy_production_tarif_2",
|
||||
entity_energy_price: null,
|
||||
number_energy_price: null,
|
||||
},
|
||||
],
|
||||
cost_adjustment_day: 0,
|
||||
},
|
||||
{
|
||||
type: "solar",
|
||||
stat_energy_from: "sensor.solar_production",
|
||||
config_entry_solar_forecast: ["solar_forecast"],
|
||||
},
|
||||
/* {
|
||||
type: "battery",
|
||||
stat_energy_from: "sensor.battery_output",
|
||||
stat_energy_to: "sensor.battery_input",
|
||||
}, */
|
||||
{
|
||||
type: "gas",
|
||||
stat_energy_from: "sensor.energy_gas",
|
||||
stat_cost: "sensor.energy_gas_cost",
|
||||
entity_energy_from: "sensor.energy_gas",
|
||||
entity_energy_price: null,
|
||||
number_energy_price: null,
|
||||
},
|
||||
],
|
||||
device_consumption: [
|
||||
{
|
||||
stat_consumption: "sensor.energy_car",
|
||||
},
|
||||
{
|
||||
stat_consumption: "sensor.energy_ac",
|
||||
},
|
||||
{
|
||||
stat_consumption: "sensor.energy_washing_machine",
|
||||
},
|
||||
{
|
||||
stat_consumption: "sensor.energy_dryer",
|
||||
},
|
||||
{
|
||||
stat_consumption: "sensor.energy_heat_pump",
|
||||
},
|
||||
{
|
||||
stat_consumption: "sensor.energy_boiler",
|
||||
},
|
||||
],
|
||||
}));
|
||||
hass.mockWS("energy/info", () => ({ cost_sensors: [] }));
|
||||
const todayString = format(startOfToday(), "yyyy-MM-dd");
|
||||
const tomorrowString = format(startOfTomorrow(), "yyyy-MM-dd");
|
||||
hass.mockWS(
|
||||
"energy/solar_forecast",
|
||||
(): EnergySolarForecasts => ({
|
||||
solar_forecast: {
|
||||
wh_hours: {
|
||||
[`${todayString}T06:00:00`]: 0,
|
||||
[`${todayString}T06:23:00`]: 6,
|
||||
[`${todayString}T06:45:00`]: 39,
|
||||
[`${todayString}T07:00:00`]: 28,
|
||||
[`${todayString}T08:00:00`]: 208,
|
||||
[`${todayString}T09:00:00`]: 352,
|
||||
[`${todayString}T10:00:00`]: 544,
|
||||
[`${todayString}T11:00:00`]: 748,
|
||||
[`${todayString}T12:00:00`]: 1259,
|
||||
[`${todayString}T13:00:00`]: 1361,
|
||||
[`${todayString}T14:00:00`]: 1373,
|
||||
[`${todayString}T15:00:00`]: 1370,
|
||||
[`${todayString}T16:00:00`]: 1186,
|
||||
[`${todayString}T17:00:00`]: 937,
|
||||
[`${todayString}T18:00:00`]: 652,
|
||||
[`${todayString}T19:00:00`]: 370,
|
||||
[`${todayString}T20:00:00`]: 155,
|
||||
[`${todayString}T21:48:00`]: 24,
|
||||
[`${todayString}T22:36:00`]: 0,
|
||||
[`${tomorrowString}T06:01:00`]: 0,
|
||||
[`${tomorrowString}T06:23:00`]: 9,
|
||||
[`${tomorrowString}T06:45:00`]: 47,
|
||||
[`${tomorrowString}T07:00:00`]: 48,
|
||||
[`${tomorrowString}T08:00:00`]: 473,
|
||||
[`${tomorrowString}T09:00:00`]: 827,
|
||||
[`${tomorrowString}T10:00:00`]: 1153,
|
||||
[`${tomorrowString}T11:00:00`]: 1413,
|
||||
[`${tomorrowString}T12:00:00`]: 1590,
|
||||
[`${tomorrowString}T13:00:00`]: 1652,
|
||||
[`${tomorrowString}T14:00:00`]: 1612,
|
||||
[`${tomorrowString}T15:00:00`]: 1438,
|
||||
[`${tomorrowString}T16:00:00`]: 1149,
|
||||
[`${tomorrowString}T17:00:00`]: 830,
|
||||
[`${tomorrowString}T18:00:00`]: 542,
|
||||
[`${tomorrowString}T19:00:00`]: 311,
|
||||
[`${tomorrowString}T20:00:00`]: 140,
|
||||
[`${tomorrowString}T21:47:00`]: 22,
|
||||
[`${tomorrowString}T22:34:00`]: 0,
|
||||
},
|
||||
},
|
||||
})
|
||||
);
|
||||
};
|
@@ -1,178 +0,0 @@
|
||||
import { convertEntities } from "../../../src/fake_data/entity";
|
||||
|
||||
export const energyEntities = () =>
|
||||
convertEntities({
|
||||
"sensor.grid_fossil_fuel_percentage": {
|
||||
entity_id: "sensor.grid_fossil_fuel_percentage",
|
||||
state: "88.6",
|
||||
attributes: {
|
||||
unit_of_measurement: "%",
|
||||
},
|
||||
},
|
||||
"sensor.solar_production": {
|
||||
entity_id: "sensor.solar_production",
|
||||
state: "88.6",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
friendly_name: "Solar",
|
||||
unit_of_measurement: "kWh",
|
||||
},
|
||||
},
|
||||
"sensor.battery_input": {
|
||||
entity_id: "sensor.battery_input",
|
||||
state: "4",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
friendly_name: "Battery Input",
|
||||
unit_of_measurement: "kWh",
|
||||
},
|
||||
},
|
||||
"sensor.battery_output": {
|
||||
entity_id: "sensor.battery_output",
|
||||
state: "3",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
friendly_name: "Battery Output",
|
||||
unit_of_measurement: "kWh",
|
||||
},
|
||||
},
|
||||
"sensor.energy_consumption_tarif_1": {
|
||||
entity_id: "sensor.energy_consumption_tarif_1 ",
|
||||
state: "88.6",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
friendly_name: "Grid consumption low tariff",
|
||||
unit_of_measurement: "kWh",
|
||||
},
|
||||
},
|
||||
"sensor.energy_consumption_tarif_2": {
|
||||
entity_id: "sensor.energy_consumption_tarif_2",
|
||||
state: "88.6",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
friendly_name: "Grid consumption high tariff",
|
||||
unit_of_measurement: "kWh",
|
||||
},
|
||||
},
|
||||
"sensor.energy_production_tarif_1": {
|
||||
entity_id: "sensor.energy_production_tarif_1",
|
||||
state: "88.6",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
friendly_name: "Returned to grid low tariff",
|
||||
unit_of_measurement: "kWh",
|
||||
},
|
||||
},
|
||||
"sensor.energy_production_tarif_2": {
|
||||
entity_id: "sensor.energy_production_tarif_2",
|
||||
state: "88.6",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
friendly_name: "Returned to grid high tariff",
|
||||
unit_of_measurement: "kWh",
|
||||
},
|
||||
},
|
||||
"sensor.energy_consumption_tarif_1_cost": {
|
||||
entity_id: "sensor.energy_consumption_tarif_1_cost",
|
||||
state: "2",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
unit_of_measurement: "EUR",
|
||||
},
|
||||
},
|
||||
"sensor.energy_consumption_tarif_2_cost": {
|
||||
entity_id: "sensor.energy_consumption_tarif_2_cost",
|
||||
state: "2",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
unit_of_measurement: "EUR",
|
||||
},
|
||||
},
|
||||
"sensor.energy_production_tarif_1_compensation": {
|
||||
entity_id: "sensor.energy_production_tarif_1_compensation",
|
||||
state: "2",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
unit_of_measurement: "EUR",
|
||||
},
|
||||
},
|
||||
"sensor.energy_production_tarif_2_compensation": {
|
||||
entity_id: "sensor.energy_production_tarif_2_compensation",
|
||||
state: "2",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
unit_of_measurement: "EUR",
|
||||
},
|
||||
},
|
||||
"sensor.energy_gas_cost": {
|
||||
entity_id: "sensor.energy_gas_cost",
|
||||
state: "2",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
unit_of_measurement: "EUR",
|
||||
},
|
||||
},
|
||||
"sensor.energy_gas": {
|
||||
entity_id: "sensor.energy_gas",
|
||||
state: "4",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
friendly_name: "Gas",
|
||||
unit_of_measurement: "m³",
|
||||
},
|
||||
},
|
||||
"sensor.energy_car": {
|
||||
entity_id: "sensor.energy_car",
|
||||
state: "4",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
friendly_name: "Electric car",
|
||||
unit_of_measurement: "kWh",
|
||||
},
|
||||
},
|
||||
"sensor.energy_ac": {
|
||||
entity_id: "sensor.energy_ac",
|
||||
state: "3",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
friendly_name: "Air conditioning",
|
||||
unit_of_measurement: "kWh",
|
||||
},
|
||||
},
|
||||
"sensor.energy_washing_machine": {
|
||||
entity_id: "sensor.energy_washing_machine",
|
||||
state: "6",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
friendly_name: "Washing machine",
|
||||
unit_of_measurement: "kWh",
|
||||
},
|
||||
},
|
||||
"sensor.energy_dryer": {
|
||||
entity_id: "sensor.energy_dryer",
|
||||
state: "5.5",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
friendly_name: "Dryer",
|
||||
unit_of_measurement: "kWh",
|
||||
},
|
||||
},
|
||||
"sensor.energy_heat_pump": {
|
||||
entity_id: "sensor.energy_heat_pump",
|
||||
state: "6",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
friendly_name: "Heat pump",
|
||||
unit_of_measurement: "kWh",
|
||||
},
|
||||
},
|
||||
"sensor.energy_boiler": {
|
||||
entity_id: "sensor.energy_boiler",
|
||||
state: "7",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
friendly_name: "Boiler",
|
||||
unit_of_measurement: "kWh",
|
||||
},
|
||||
},
|
||||
});
|
@@ -1,6 +1,4 @@
|
||||
import { addHours, differenceInHours, endOfDay } from "date-fns";
|
||||
import { HassEntity } from "home-assistant-js-websocket";
|
||||
import { StatisticValue } from "../../../src/data/history";
|
||||
import { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
|
||||
|
||||
interface HistoryQueryParams {
|
||||
@@ -66,219 +64,17 @@ const generateHistory = (state, deltas) => {
|
||||
|
||||
const incrementalUnits = ["clients", "queries", "ads"];
|
||||
|
||||
const generateMeanStatistics = (
|
||||
id: string,
|
||||
start: Date,
|
||||
end: Date,
|
||||
initValue: number,
|
||||
maxDiff: number
|
||||
) => {
|
||||
const statistics: StatisticValue[] = [];
|
||||
let currentDate = new Date(start);
|
||||
currentDate.setMinutes(0, 0, 0);
|
||||
let lastVal = initValue;
|
||||
const now = new Date();
|
||||
while (end > currentDate && currentDate < now) {
|
||||
const delta = Math.random() * maxDiff;
|
||||
const mean = lastVal + delta;
|
||||
statistics.push({
|
||||
statistic_id: id,
|
||||
start: currentDate.toISOString(),
|
||||
mean,
|
||||
min: mean - Math.random() * maxDiff,
|
||||
max: mean + Math.random() * maxDiff,
|
||||
last_reset: "1970-01-01T00:00:00+00:00",
|
||||
state: mean,
|
||||
sum: null,
|
||||
});
|
||||
lastVal = mean;
|
||||
currentDate = addHours(currentDate, 1);
|
||||
}
|
||||
return statistics;
|
||||
};
|
||||
|
||||
const generateSumStatistics = (
|
||||
id: string,
|
||||
start: Date,
|
||||
end: Date,
|
||||
initValue: number,
|
||||
maxDiff: number
|
||||
) => {
|
||||
const statistics: StatisticValue[] = [];
|
||||
let currentDate = new Date(start);
|
||||
currentDate.setMinutes(0, 0, 0);
|
||||
let sum = initValue;
|
||||
const now = new Date();
|
||||
while (end > currentDate && currentDate < now) {
|
||||
const add = Math.random() * maxDiff;
|
||||
sum += add;
|
||||
statistics.push({
|
||||
statistic_id: id,
|
||||
start: currentDate.toISOString(),
|
||||
mean: null,
|
||||
min: null,
|
||||
max: null,
|
||||
last_reset: "1970-01-01T00:00:00+00:00",
|
||||
state: initValue + sum,
|
||||
sum,
|
||||
});
|
||||
currentDate = addHours(currentDate, 1);
|
||||
}
|
||||
return statistics;
|
||||
};
|
||||
|
||||
const generateCurvedStatistics = (
|
||||
id: string,
|
||||
start: Date,
|
||||
end: Date,
|
||||
initValue: number,
|
||||
maxDiff: number,
|
||||
metered: boolean
|
||||
) => {
|
||||
const statistics: StatisticValue[] = [];
|
||||
let currentDate = new Date(start);
|
||||
currentDate.setMinutes(0, 0, 0);
|
||||
let sum = initValue;
|
||||
const hours = differenceInHours(end, start) - 1;
|
||||
let i = 0;
|
||||
let half = false;
|
||||
const now = new Date();
|
||||
while (end > currentDate && currentDate < now) {
|
||||
const add = Math.random() * maxDiff;
|
||||
sum += i * add;
|
||||
statistics.push({
|
||||
statistic_id: id,
|
||||
start: currentDate.toISOString(),
|
||||
mean: null,
|
||||
min: null,
|
||||
max: null,
|
||||
last_reset: "1970-01-01T00:00:00+00:00",
|
||||
state: initValue + sum,
|
||||
sum: metered ? sum : null,
|
||||
});
|
||||
currentDate = addHours(currentDate, 1);
|
||||
if (!half && i > hours / 2) {
|
||||
half = true;
|
||||
}
|
||||
i += half ? -1 : 1;
|
||||
}
|
||||
return statistics;
|
||||
};
|
||||
|
||||
const statisticsFunctions: Record<
|
||||
string,
|
||||
(id: string, start: Date, end: Date) => StatisticValue[]
|
||||
> = {
|
||||
"sensor.energy_consumption_tarif_1": (id: string, start: Date, end: Date) => {
|
||||
const morningEnd = new Date(start.getTime() + 10 * 60 * 60 * 1000);
|
||||
const morningLow = generateSumStatistics(id, start, morningEnd, 0, 0.7);
|
||||
const eveningStart = new Date(start.getTime() + 20 * 60 * 60 * 1000);
|
||||
const morningFinalVal = morningLow.length
|
||||
? morningLow[morningLow.length - 1].sum!
|
||||
: 0;
|
||||
const empty = generateSumStatistics(
|
||||
id,
|
||||
morningEnd,
|
||||
eveningStart,
|
||||
morningFinalVal,
|
||||
0
|
||||
);
|
||||
const eveningLow = generateSumStatistics(
|
||||
id,
|
||||
eveningStart,
|
||||
end,
|
||||
morningFinalVal,
|
||||
0.7
|
||||
);
|
||||
return [...morningLow, ...empty, ...eveningLow];
|
||||
},
|
||||
"sensor.energy_consumption_tarif_2": (id: string, start: Date, end: Date) => {
|
||||
const morningEnd = new Date(start.getTime() + 9 * 60 * 60 * 1000);
|
||||
const eveningStart = new Date(start.getTime() + 20 * 60 * 60 * 1000);
|
||||
const highTarif = generateSumStatistics(
|
||||
id,
|
||||
morningEnd,
|
||||
eveningStart,
|
||||
0,
|
||||
0.3
|
||||
);
|
||||
const highTarifFinalVal = highTarif.length
|
||||
? highTarif[highTarif.length - 1].sum!
|
||||
: 0;
|
||||
const morning = generateSumStatistics(id, start, morningEnd, 0, 0);
|
||||
const evening = generateSumStatistics(
|
||||
id,
|
||||
eveningStart,
|
||||
end,
|
||||
highTarifFinalVal,
|
||||
0
|
||||
);
|
||||
return [...morning, ...highTarif, ...evening];
|
||||
},
|
||||
"sensor.energy_production_tarif_1": (id, start, end) =>
|
||||
generateSumStatistics(id, start, end, 0, 0),
|
||||
"sensor.energy_production_tarif_1_compensation": (id, start, end) =>
|
||||
generateSumStatistics(id, start, end, 0, 0),
|
||||
"sensor.energy_production_tarif_2": (id, start, end) => {
|
||||
const productionStart = new Date(start.getTime() + 9 * 60 * 60 * 1000);
|
||||
const productionEnd = new Date(start.getTime() + 21 * 60 * 60 * 1000);
|
||||
const dayEnd = new Date(endOfDay(productionEnd));
|
||||
const production = generateCurvedStatistics(
|
||||
id,
|
||||
productionStart,
|
||||
productionEnd,
|
||||
0,
|
||||
0.15,
|
||||
true
|
||||
);
|
||||
const productionFinalVal = production.length
|
||||
? production[production.length - 1].sum!
|
||||
: 0;
|
||||
const morning = generateSumStatistics(id, start, productionStart, 0, 0);
|
||||
const evening = generateSumStatistics(
|
||||
id,
|
||||
productionEnd,
|
||||
dayEnd,
|
||||
productionFinalVal,
|
||||
0
|
||||
);
|
||||
const rest = generateSumStatistics(id, dayEnd, end, productionFinalVal, 1);
|
||||
return [...morning, ...production, ...evening, ...rest];
|
||||
},
|
||||
"sensor.solar_production": (id, start, end) => {
|
||||
const productionStart = new Date(start.getTime() + 7 * 60 * 60 * 1000);
|
||||
const productionEnd = new Date(start.getTime() + 23 * 60 * 60 * 1000);
|
||||
const dayEnd = new Date(endOfDay(productionEnd));
|
||||
const production = generateCurvedStatistics(
|
||||
id,
|
||||
productionStart,
|
||||
productionEnd,
|
||||
0,
|
||||
0.3,
|
||||
true
|
||||
);
|
||||
const productionFinalVal = production.length
|
||||
? production[production.length - 1].sum!
|
||||
: 0;
|
||||
const morning = generateSumStatistics(id, start, productionStart, 0, 0);
|
||||
const evening = generateSumStatistics(
|
||||
id,
|
||||
productionEnd,
|
||||
dayEnd,
|
||||
productionFinalVal,
|
||||
0
|
||||
);
|
||||
const rest = generateSumStatistics(id, dayEnd, end, productionFinalVal, 2);
|
||||
return [...morning, ...production, ...evening, ...rest];
|
||||
},
|
||||
"sensor.grid_fossil_fuel_percentage": (id, start, end) =>
|
||||
generateMeanStatistics(id, start, end, 35, 1.3),
|
||||
};
|
||||
|
||||
export const mockHistory = (mockHass: MockHomeAssistant) => {
|
||||
mockHass.mockAPI(
|
||||
new RegExp("history/period/.+"),
|
||||
(hass, _method, path, _parameters) => {
|
||||
(
|
||||
hass,
|
||||
// @ts-ignore
|
||||
method,
|
||||
path,
|
||||
// @ts-ignore
|
||||
parameters
|
||||
) => {
|
||||
const params = parseQuery<HistoryQueryParams>(path.split("?")[1]);
|
||||
const entities = params.filter_entity_id.split(",");
|
||||
|
||||
@@ -299,7 +95,7 @@ export const mockHistory = (mockHass: MockHomeAssistant) => {
|
||||
const numberState = Number(state.state);
|
||||
|
||||
if (isNaN(numberState)) {
|
||||
// eslint-disable-next-line no-console
|
||||
// eslint-disable-next-line
|
||||
console.log(
|
||||
"Ignoring state with unparsable state but with a unit",
|
||||
entityId,
|
||||
@@ -344,40 +140,4 @@ export const mockHistory = (mockHass: MockHomeAssistant) => {
|
||||
return results;
|
||||
}
|
||||
);
|
||||
mockHass.mockWS("history/list_statistic_ids", () => []);
|
||||
mockHass.mockWS(
|
||||
"history/statistics_during_period",
|
||||
({ statistic_ids, start_time, end_time }, hass) => {
|
||||
const start = new Date(start_time);
|
||||
const end = end_time ? new Date(end_time) : new Date();
|
||||
|
||||
const statistics: Record<string, StatisticValue[]> = {};
|
||||
|
||||
statistic_ids.forEach((id: string) => {
|
||||
if (id in statisticsFunctions) {
|
||||
statistics[id] = statisticsFunctions[id](id, start, end);
|
||||
} else {
|
||||
const entityState = hass.states[id];
|
||||
const state = entityState ? Number(entityState.state) : 1;
|
||||
statistics[id] =
|
||||
entityState && "last_reset" in entityState.attributes
|
||||
? generateSumStatistics(
|
||||
id,
|
||||
start,
|
||||
end,
|
||||
state,
|
||||
state * (state > 80 ? 0.01 : 0.05)
|
||||
)
|
||||
: generateMeanStatistics(
|
||||
id,
|
||||
start,
|
||||
end,
|
||||
state,
|
||||
state * (state > 80 ? 0.05 : 0.1)
|
||||
);
|
||||
}
|
||||
});
|
||||
return statistics;
|
||||
}
|
||||
);
|
||||
};
|
||||
|
@@ -10,9 +10,10 @@ export const mockLovelace = (
|
||||
localizePromise: Promise<LocalizeFunc>
|
||||
) => {
|
||||
hass.mockWS("lovelace/config", () =>
|
||||
Promise.all([selectedDemoConfig, localizePromise]).then(
|
||||
([config, localize]) => config.lovelace(localize)
|
||||
)
|
||||
Promise.all([
|
||||
selectedDemoConfig,
|
||||
localizePromise,
|
||||
]).then(([config, localize]) => config.lovelace(localize))
|
||||
);
|
||||
|
||||
hass.mockWS("lovelace/config/save", () => Promise.resolve());
|
||||
|
@@ -6,7 +6,7 @@ export const mockTemplate = (hass: MockHomeAssistant) => {
|
||||
body: { message: "Template dev tool does not work in the demo." },
|
||||
})
|
||||
);
|
||||
hass.mockWS("render_template", (msg, _hass, onChange) => {
|
||||
hass.mockWS("render_template", (msg, onChange) => {
|
||||
onChange!({
|
||||
result: msg.template,
|
||||
listeners: { all: false, domains: [], entities: [], time: false },
|
||||
|
@@ -3,6 +3,8 @@ import { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
|
||||
export const mockTranslations = (hass: MockHomeAssistant) => {
|
||||
hass.mockWS(
|
||||
"frontend/get_translations",
|
||||
(/* msg: {language: string, category: string} */) => ({ resources: {} })
|
||||
(/* msg: {language: string, category: string} */) => {
|
||||
return { resources: {} };
|
||||
}
|
||||
);
|
||||
};
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||
/* eslint-plugin-disable lit */
|
||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||
import { load } from "js-yaml";
|
||||
import { safeLoad } from "js-yaml";
|
||||
import { createCardElement } from "../../../src/panels/lovelace/create-element/create-card-element";
|
||||
|
||||
class DemoCard extends PolymerElement {
|
||||
@@ -15,10 +15,6 @@ class DemoCard extends PolymerElement {
|
||||
margin: 0 0 20px;
|
||||
color: var(--primary-color);
|
||||
}
|
||||
h2 small {
|
||||
font-size: 0.5em;
|
||||
color: var(--primary-text-color);
|
||||
}
|
||||
#card {
|
||||
max-width: 400px;
|
||||
width: 100vw;
|
||||
@@ -38,12 +34,7 @@ class DemoCard extends PolymerElement {
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<h2>
|
||||
[[config.heading]]
|
||||
<template is="dom-if" if="[[_size]]">
|
||||
<small>(size [[_size]])</small>
|
||||
</template>
|
||||
</h2>
|
||||
<h2>[[config.heading]]</h2>
|
||||
<div class="root">
|
||||
<div id="card"></div>
|
||||
<template is="dom-if" if="[[showConfig]]">
|
||||
@@ -64,9 +55,6 @@ class DemoCard extends PolymerElement {
|
||||
observer: "_configChanged",
|
||||
},
|
||||
showConfig: Boolean,
|
||||
_size: {
|
||||
type: Number,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -80,19 +68,8 @@ class DemoCard extends PolymerElement {
|
||||
card.removeChild(card.lastChild);
|
||||
}
|
||||
|
||||
const el = this._createCardElement(load(config.config)[0]);
|
||||
const el = this._createCardElement(safeLoad(config.config)[0]);
|
||||
card.appendChild(el);
|
||||
this._getSize(el);
|
||||
}
|
||||
|
||||
async _getSize(el) {
|
||||
await customElements.whenDefined(el.localName);
|
||||
|
||||
if (!("getCardSize" in el)) {
|
||||
this._size = undefined;
|
||||
return;
|
||||
}
|
||||
this._size = await el.getCardSize();
|
||||
}
|
||||
|
||||
_createCardElement(cardConfig) {
|
||||
|
@@ -1,349 +0,0 @@
|
||||
import { DemoTrace } from "./types";
|
||||
|
||||
export const basicTrace: DemoTrace = {
|
||||
trace: {
|
||||
last_step: "action/2",
|
||||
run_id: "0",
|
||||
state: "stopped",
|
||||
timestamp: {
|
||||
start: "2021-03-25T04:36:51.223693+00:00",
|
||||
finish: "2021-03-25T04:36:51.266132+00:00",
|
||||
},
|
||||
trigger: "state of input_boolean.toggle_1",
|
||||
domain: "automation",
|
||||
item_id: "1615419646544",
|
||||
trace: {
|
||||
"trigger/0": [
|
||||
{
|
||||
path: "trigger/0",
|
||||
timestamp: "2021-03-25T04:36:51.223693+00:00",
|
||||
},
|
||||
],
|
||||
"condition/0": [
|
||||
{
|
||||
path: "condition/0",
|
||||
timestamp: "2021-03-25T04:36:51.228243+00:00",
|
||||
changed_variables: {
|
||||
trigger: {
|
||||
platform: "state",
|
||||
entity_id: "input_boolean.toggle_1",
|
||||
from_state: {
|
||||
entity_id: "input_boolean.toggle_1",
|
||||
state: "on",
|
||||
attributes: {
|
||||
editable: true,
|
||||
friendly_name: "Toggle 1",
|
||||
},
|
||||
last_changed: "2021-03-24T19:03:59.141440+00:00",
|
||||
last_updated: "2021-03-24T19:03:59.141440+00:00",
|
||||
context: {
|
||||
id: "5d0918eb379214d07554bdab6a08bcff",
|
||||
parent_id: null,
|
||||
user_id: null,
|
||||
},
|
||||
},
|
||||
to_state: {
|
||||
entity_id: "input_boolean.toggle_1",
|
||||
state: "off",
|
||||
attributes: {
|
||||
editable: true,
|
||||
friendly_name: "Toggle 1",
|
||||
},
|
||||
last_changed: "2021-03-25T04:36:51.220696+00:00",
|
||||
last_updated: "2021-03-25T04:36:51.220696+00:00",
|
||||
context: {
|
||||
id: "664d6d261450a9ecea6738e97269a149",
|
||||
parent_id: null,
|
||||
user_id: "d1b4e89da01445fa8bc98e39fac477ca",
|
||||
},
|
||||
},
|
||||
for: null,
|
||||
attribute: null,
|
||||
description: "state of input_boolean.toggle_1",
|
||||
},
|
||||
},
|
||||
result: {
|
||||
result: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
"action/0": [
|
||||
{
|
||||
path: "action/0",
|
||||
timestamp: "2021-03-25T04:36:51.243018+00:00",
|
||||
changed_variables: {
|
||||
trigger: {
|
||||
platform: "state",
|
||||
entity_id: "input_boolean.toggle_1",
|
||||
from_state: {
|
||||
entity_id: "input_boolean.toggle_1",
|
||||
state: "on",
|
||||
attributes: {
|
||||
editable: true,
|
||||
friendly_name: "Toggle 1",
|
||||
},
|
||||
last_changed: "2021-03-24T19:03:59.141440+00:00",
|
||||
last_updated: "2021-03-24T19:03:59.141440+00:00",
|
||||
context: {
|
||||
id: "5d0918eb379214d07554bdab6a08bcff",
|
||||
parent_id: null,
|
||||
user_id: null,
|
||||
},
|
||||
},
|
||||
to_state: {
|
||||
entity_id: "input_boolean.toggle_1",
|
||||
state: "off",
|
||||
attributes: {
|
||||
editable: true,
|
||||
friendly_name: "Toggle 1",
|
||||
},
|
||||
last_changed: "2021-03-25T04:36:51.220696+00:00",
|
||||
last_updated: "2021-03-25T04:36:51.220696+00:00",
|
||||
context: {
|
||||
id: "664d6d261450a9ecea6738e97269a149",
|
||||
parent_id: null,
|
||||
user_id: "d1b4e89da01445fa8bc98e39fac477ca",
|
||||
},
|
||||
},
|
||||
for: null,
|
||||
attribute: null,
|
||||
description: "state of input_boolean.toggle_1",
|
||||
},
|
||||
context: {
|
||||
id: "6cfcae368e7b3686fad6c59e83ae76c9",
|
||||
parent_id: "664d6d261450a9ecea6738e97269a149",
|
||||
user_id: null,
|
||||
},
|
||||
},
|
||||
result: {
|
||||
params: {
|
||||
domain: "input_boolean",
|
||||
service: "toggle",
|
||||
service_data: {},
|
||||
target: {
|
||||
entity_id: ["input_boolean.toggle_4"],
|
||||
},
|
||||
},
|
||||
running_script: false,
|
||||
limit: 10,
|
||||
},
|
||||
},
|
||||
],
|
||||
"action/1": [
|
||||
{
|
||||
path: "action/1",
|
||||
timestamp: "2021-03-25T04:36:51.252406+00:00",
|
||||
result: {
|
||||
choice: 0,
|
||||
},
|
||||
},
|
||||
],
|
||||
"action/1/choose/0": [
|
||||
{
|
||||
path: "action/1/choose/0",
|
||||
timestamp: "2021-03-25T04:36:51.254569+00:00",
|
||||
result: {
|
||||
result: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
"action/1/choose/0/conditions/0": [
|
||||
{
|
||||
path: "action/1/choose/0/conditions/0",
|
||||
timestamp: "2021-03-25T04:36:51.254697+00:00",
|
||||
result: {
|
||||
result: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
"action/1/choose/0/sequence/0": [
|
||||
{
|
||||
path: "action/1/choose/0/sequence/0",
|
||||
timestamp: "2021-03-25T04:36:51.257360+00:00",
|
||||
result: {
|
||||
params: {
|
||||
domain: "input_boolean",
|
||||
service: "toggle",
|
||||
service_data: {},
|
||||
target: {
|
||||
entity_id: ["input_boolean.toggle_2"],
|
||||
},
|
||||
},
|
||||
running_script: false,
|
||||
limit: 10,
|
||||
},
|
||||
},
|
||||
],
|
||||
"action/1/choose/0/sequence/1": [
|
||||
{
|
||||
path: "action/1/choose/0/sequence/1",
|
||||
timestamp: "2021-03-25T04:36:51.260658+00:00",
|
||||
result: {
|
||||
params: {
|
||||
domain: "input_boolean",
|
||||
service: "toggle",
|
||||
service_data: {},
|
||||
target: {
|
||||
entity_id: ["input_boolean.toggle_3"],
|
||||
},
|
||||
},
|
||||
running_script: false,
|
||||
limit: 10,
|
||||
},
|
||||
},
|
||||
],
|
||||
"action/2": [
|
||||
{
|
||||
path: "action/2",
|
||||
timestamp: "2021-03-25T04:36:51.264159+00:00",
|
||||
result: {
|
||||
params: {
|
||||
domain: "input_boolean",
|
||||
service: "toggle",
|
||||
service_data: {},
|
||||
target: {
|
||||
entity_id: ["input_boolean.toggle_4"],
|
||||
},
|
||||
},
|
||||
running_script: false,
|
||||
limit: 10,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
config: {
|
||||
id: "1615419646544",
|
||||
alias: "Ensure Party mode",
|
||||
description: "",
|
||||
trigger: [
|
||||
{
|
||||
platform: "state",
|
||||
entity_id: "input_boolean.toggle_1",
|
||||
},
|
||||
],
|
||||
condition: [
|
||||
{
|
||||
condition: "template",
|
||||
alias: "Test if Paulus is home",
|
||||
value_template: "{{ true }}",
|
||||
},
|
||||
],
|
||||
action: [
|
||||
{
|
||||
service: "input_boolean.toggle",
|
||||
target: {
|
||||
entity_id: "input_boolean.toggle_4",
|
||||
},
|
||||
},
|
||||
{
|
||||
choose: [
|
||||
{
|
||||
alias: "If toggle 3 is on",
|
||||
conditions: [
|
||||
{
|
||||
condition: "template",
|
||||
value_template:
|
||||
"{{ is_state('input_boolean.toggle_3', 'on') }}",
|
||||
},
|
||||
],
|
||||
sequence: [
|
||||
{
|
||||
service: "input_boolean.toggle",
|
||||
alias: "Toggle 2 while 3 is on",
|
||||
target: {
|
||||
entity_id: "input_boolean.toggle_2",
|
||||
},
|
||||
},
|
||||
{
|
||||
service: "input_boolean.toggle",
|
||||
alias: "Toggle 3",
|
||||
target: {
|
||||
entity_id: "input_boolean.toggle_3",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
default: [
|
||||
{
|
||||
service: "input_boolean.toggle",
|
||||
alias: "Toggle 2",
|
||||
target: {
|
||||
entity_id: "input_boolean.toggle_2",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
service: "input_boolean.toggle",
|
||||
target: {
|
||||
entity_id: "input_boolean.toggle_4",
|
||||
},
|
||||
},
|
||||
],
|
||||
mode: "single",
|
||||
},
|
||||
context: {
|
||||
id: "6cfcae368e7b3686fad6c59e83ae76c9",
|
||||
parent_id: "664d6d261450a9ecea6738e97269a149",
|
||||
user_id: null,
|
||||
},
|
||||
script_execution: "finished",
|
||||
},
|
||||
logbookEntries: [
|
||||
{
|
||||
name: "Ensure Party mode",
|
||||
message: "has been triggered by state of input_boolean.toggle_1",
|
||||
source: "state of input_boolean.toggle_1",
|
||||
entity_id: "automation.toggle_toggles",
|
||||
context_id: "6cfcae368e7b3686fad6c59e83ae76c9",
|
||||
when: "2021-03-25T04:36:51.240832+00:00",
|
||||
domain: "automation",
|
||||
},
|
||||
{
|
||||
when: "2021-03-25T04:36:51.249828+00:00",
|
||||
name: "Toggle 4",
|
||||
state: "on",
|
||||
entity_id: "input_boolean.toggle_4",
|
||||
context_entity_id: "automation.toggle_toggles",
|
||||
context_entity_id_name: "Ensure Party mode",
|
||||
context_event_type: "automation_triggered",
|
||||
context_domain: "automation",
|
||||
context_name: "Ensure Party mode",
|
||||
},
|
||||
{
|
||||
when: "2021-03-25T04:36:51.258947+00:00",
|
||||
name: "Toggle 2",
|
||||
state: "on",
|
||||
entity_id: "input_boolean.toggle_2",
|
||||
context_entity_id: "automation.toggle_toggles",
|
||||
context_entity_id_name: "Ensure Party mode",
|
||||
context_event_type: "automation_triggered",
|
||||
context_domain: "automation",
|
||||
context_name: "Ensure Party mode",
|
||||
},
|
||||
{
|
||||
when: "2021-03-25T04:36:51.261806+00:00",
|
||||
name: "Toggle 3",
|
||||
state: "off",
|
||||
entity_id: "input_boolean.toggle_3",
|
||||
context_entity_id: "automation.toggle_toggles",
|
||||
context_entity_id_name: "Ensure Party mode",
|
||||
context_event_type: "automation_triggered",
|
||||
context_domain: "automation",
|
||||
context_name: "Ensure Party mode",
|
||||
},
|
||||
{
|
||||
when: "2021-03-25T04:36:51.265246+00:00",
|
||||
name: "Toggle 4",
|
||||
state: "off",
|
||||
entity_id: "input_boolean.toggle_4",
|
||||
context_entity_id: "automation.toggle_toggles",
|
||||
context_entity_id_name: "Ensure Party mode",
|
||||
context_event_type: "automation_triggered",
|
||||
context_domain: "automation",
|
||||
context_name: "Ensure Party mode",
|
||||
},
|
||||
],
|
||||
};
|
@@ -1,44 +0,0 @@
|
||||
import { LogbookEntry } from "../../../../src/data/logbook";
|
||||
import { AutomationTraceExtended } from "../../../../src/data/trace";
|
||||
import { DemoTrace } from "./types";
|
||||
|
||||
export const mockDemoTrace = (
|
||||
tracePartial: Partial<AutomationTraceExtended>,
|
||||
logbookEntries?: LogbookEntry[]
|
||||
): DemoTrace => ({
|
||||
trace: {
|
||||
last_step: "",
|
||||
run_id: "0",
|
||||
state: "stopped",
|
||||
timestamp: {
|
||||
start: "2021-03-25T04:36:51.223693+00:00",
|
||||
finish: "2021-03-25T04:36:51.266132+00:00",
|
||||
},
|
||||
trigger: "mocked trigger",
|
||||
domain: "automation",
|
||||
item_id: "1615419646544",
|
||||
trace: {
|
||||
"trigger/0": [
|
||||
{
|
||||
path: "trigger/0",
|
||||
changed_variables: {
|
||||
trigger: {
|
||||
description: "mocked trigger",
|
||||
},
|
||||
},
|
||||
timestamp: "2021-03-25T04:36:51.223693+00:00",
|
||||
},
|
||||
],
|
||||
},
|
||||
config: {
|
||||
trigger: [],
|
||||
action: [],
|
||||
},
|
||||
context: {
|
||||
id: "abcd",
|
||||
},
|
||||
script_execution: "finished",
|
||||
...tracePartial,
|
||||
},
|
||||
logbookEntries: logbookEntries || [],
|
||||
});
|
@@ -1,214 +0,0 @@
|
||||
import { DemoTrace } from "./types";
|
||||
|
||||
export const motionLightTrace: DemoTrace = {
|
||||
trace: {
|
||||
last_step: "action/3",
|
||||
run_id: "1",
|
||||
state: "stopped",
|
||||
timestamp: {
|
||||
start: "2021-03-14T06:07:01.768006+00:00",
|
||||
finish: "2021-03-14T06:07:53.287525+00:00",
|
||||
},
|
||||
trigger: "state of binary_sensor.pauluss_macbook_pro_camera_in_use",
|
||||
domain: "automation",
|
||||
item_id: "1614732497392",
|
||||
trace: {
|
||||
"trigger/0": [
|
||||
{
|
||||
path: "trigger/0",
|
||||
timestamp: "2021-03-25T04:36:51.223693+00:00",
|
||||
},
|
||||
],
|
||||
"action/0": [
|
||||
{
|
||||
path: "action/0",
|
||||
timestamp: "2021-03-14T06:07:01.771038+00:00",
|
||||
changed_variables: {
|
||||
trigger: {
|
||||
platform: "state",
|
||||
entity_id: "binary_sensor.pauluss_macbook_pro_camera_in_use",
|
||||
from_state: {
|
||||
entity_id: "binary_sensor.pauluss_macbook_pro_camera_in_use",
|
||||
state: "off",
|
||||
attributes: {
|
||||
friendly_name: "Paulus’s MacBook Pro Camera In Use",
|
||||
icon: "mdi:camera-off",
|
||||
},
|
||||
last_changed: "2021-03-14T06:06:29.235325+00:00",
|
||||
last_updated: "2021-03-14T06:06:29.235325+00:00",
|
||||
context: {
|
||||
id: "ad4864c5ce957c38a07b50378eeb245d",
|
||||
parent_id: null,
|
||||
user_id: null,
|
||||
},
|
||||
},
|
||||
to_state: {
|
||||
entity_id: "binary_sensor.pauluss_macbook_pro_camera_in_use",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Paulus’s MacBook Pro Camera In Use",
|
||||
icon: "mdi:camera",
|
||||
},
|
||||
last_changed: "2021-03-14T06:07:01.762009+00:00",
|
||||
last_updated: "2021-03-14T06:07:01.762009+00:00",
|
||||
context: {
|
||||
id: "e22ddfd5f11dc4aad9a52fc10dab613b",
|
||||
parent_id: null,
|
||||
user_id: null,
|
||||
},
|
||||
},
|
||||
for: null,
|
||||
attribute: null,
|
||||
description:
|
||||
"state of binary_sensor.pauluss_macbook_pro_camera_in_use",
|
||||
},
|
||||
context: {
|
||||
id: "43b6ee9293a551c5cc14e8eb60af54ba",
|
||||
parent_id: "e22ddfd5f11dc4aad9a52fc10dab613b",
|
||||
user_id: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
"action/1": [
|
||||
{ path: "action/1", timestamp: "2021-03-14T06:07:01.875316+00:00" },
|
||||
],
|
||||
"action/2": [
|
||||
{
|
||||
path: "action/2",
|
||||
timestamp: "2021-03-14T06:07:53.195013+00:00",
|
||||
changed_variables: {
|
||||
wait: {
|
||||
remaining: null,
|
||||
trigger: {
|
||||
platform: "state",
|
||||
entity_id: "binary_sensor.pauluss_macbook_pro_camera_in_use",
|
||||
from_state: {
|
||||
entity_id: "binary_sensor.pauluss_macbook_pro_camera_in_use",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Paulus’s MacBook Pro Camera In Use",
|
||||
icon: "mdi:camera",
|
||||
},
|
||||
last_changed: "2021-03-14T06:07:01.762009+00:00",
|
||||
last_updated: "2021-03-14T06:07:01.762009+00:00",
|
||||
context: {
|
||||
id: "e22ddfd5f11dc4aad9a52fc10dab613b",
|
||||
parent_id: null,
|
||||
user_id: null,
|
||||
},
|
||||
},
|
||||
to_state: {
|
||||
entity_id: "binary_sensor.pauluss_macbook_pro_camera_in_use",
|
||||
state: "off",
|
||||
attributes: {
|
||||
friendly_name: "Paulus’s MacBook Pro Camera In Use",
|
||||
icon: "mdi:camera-off",
|
||||
},
|
||||
last_changed: "2021-03-14T06:07:53.186755+00:00",
|
||||
last_updated: "2021-03-14T06:07:53.186755+00:00",
|
||||
context: {
|
||||
id: "b2308cc91d509ea8e0c623331ab178d6",
|
||||
parent_id: null,
|
||||
user_id: null,
|
||||
},
|
||||
},
|
||||
for: null,
|
||||
attribute: null,
|
||||
description:
|
||||
"state of binary_sensor.pauluss_macbook_pro_camera_in_use",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
"action/3": [
|
||||
{
|
||||
path: "action/3",
|
||||
timestamp: "2021-03-14T06:07:53.196014+00:00",
|
||||
},
|
||||
],
|
||||
},
|
||||
config: {
|
||||
mode: "restart",
|
||||
max_exceeded: "silent",
|
||||
trigger: [
|
||||
{
|
||||
platform: "state",
|
||||
entity_id: "binary_sensor.pauluss_macbook_pro_camera_in_use",
|
||||
from: "off",
|
||||
to: "on",
|
||||
},
|
||||
],
|
||||
action: [
|
||||
{
|
||||
service: "light.turn_on",
|
||||
target: {
|
||||
entity_id: "light.elgato_key_light_air",
|
||||
},
|
||||
},
|
||||
{
|
||||
wait_for_trigger: [
|
||||
{
|
||||
platform: "state",
|
||||
entity_id: "binary_sensor.pauluss_macbook_pro_camera_in_use",
|
||||
from: "on",
|
||||
to: "off",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
delay: 0,
|
||||
},
|
||||
{
|
||||
service: "light.turn_off",
|
||||
target: {
|
||||
entity_id: "light.elgato_key_light_air",
|
||||
},
|
||||
},
|
||||
],
|
||||
id: "1614732497392",
|
||||
alias: "Auto Elgato",
|
||||
description: "",
|
||||
},
|
||||
context: {
|
||||
id: "43b6ee9293a551c5cc14e8eb60af54ba",
|
||||
parent_id: "e22ddfd5f11dc4aad9a52fc10dab613b",
|
||||
user_id: null,
|
||||
},
|
||||
script_execution: "finished",
|
||||
},
|
||||
logbookEntries: [
|
||||
{
|
||||
name: "Auto Elgato",
|
||||
message:
|
||||
"has been triggered by state of binary_sensor.pauluss_macbook_pro_camera_in_use",
|
||||
source: "state of binary_sensor.pauluss_macbook_pro_camera_in_use",
|
||||
entity_id: "automation.auto_elgato",
|
||||
when: "2021-03-14T06:07:01.768492+00:00",
|
||||
domain: "automation",
|
||||
},
|
||||
{
|
||||
when: "2021-03-14T06:07:01.872187+00:00",
|
||||
name: "Elgato Key Light Air",
|
||||
state: "on",
|
||||
entity_id: "light.elgato_key_light_air",
|
||||
context_entity_id: "automation.auto_elgato",
|
||||
context_entity_id_name: "Auto Elgato",
|
||||
context_event_type: "automation_triggered",
|
||||
context_domain: "automation",
|
||||
context_name: "Auto Elgato",
|
||||
},
|
||||
{
|
||||
when: "2021-03-14T06:07:53.284505+00:00",
|
||||
name: "Elgato Key Light Air",
|
||||
state: "off",
|
||||
entity_id: "light.elgato_key_light_air",
|
||||
context_entity_id: "automation.auto_elgato",
|
||||
context_entity_id_name: "Auto Elgato",
|
||||
context_event_type: "automation_triggered",
|
||||
context_domain: "automation",
|
||||
context_name: "Auto Elgato",
|
||||
},
|
||||
],
|
||||
};
|
@@ -1,7 +0,0 @@
|
||||
import { AutomationTraceExtended } from "../../../../src/data/trace";
|
||||
import { LogbookEntry } from "../../../../src/data/logbook";
|
||||
|
||||
export interface DemoTrace {
|
||||
trace: AutomationTraceExtended;
|
||||
logbookEntries: LogbookEntry[];
|
||||
}
|
@@ -1,96 +0,0 @@
|
||||
import { dump } from "js-yaml";
|
||||
import { html, css, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import "../../../src/components/ha-card";
|
||||
import { describeAction } from "../../../src/data/script_i18n";
|
||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||
import { HomeAssistant } from "../../../src/types";
|
||||
|
||||
const actions = [
|
||||
{ wait_template: "{{ true }}", alias: "Something with an alias" },
|
||||
{ delay: "0:05" },
|
||||
{ wait_template: "{{ true }}" },
|
||||
{
|
||||
condition: "template",
|
||||
value_template: "{{ true }}",
|
||||
},
|
||||
{ event: "happy_event" },
|
||||
{
|
||||
device_id: "abcdefgh",
|
||||
domain: "plex",
|
||||
entity_id: "media_player.kitchen",
|
||||
},
|
||||
{ scene: "scene.kitchen_morning" },
|
||||
{
|
||||
wait_for_trigger: [
|
||||
{
|
||||
platform: "state",
|
||||
entity_id: "input_boolean.toggle_1",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
variables: {
|
||||
hello: "world",
|
||||
},
|
||||
},
|
||||
{
|
||||
service: "input_boolean.toggle",
|
||||
target: {
|
||||
entity_id: "input_boolean.toggle_4",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
@customElement("demo-automation-describe-action")
|
||||
export class DemoAutomationDescribeAction extends LitElement {
|
||||
@property({ attribute: false }) hass!: HomeAssistant;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this.hass) {
|
||||
return html``;
|
||||
}
|
||||
return html`
|
||||
<ha-card header="Actions">
|
||||
${actions.map(
|
||||
(conf) => html`
|
||||
<div class="action">
|
||||
<span>${describeAction(this.hass, conf as any)}</span>
|
||||
<pre>${dump(conf)}</pre>
|
||||
</div>
|
||||
`
|
||||
)}
|
||||
</ha-card>
|
||||
`;
|
||||
}
|
||||
|
||||
protected firstUpdated(changedProps) {
|
||||
super.firstUpdated(changedProps);
|
||||
const hass = provideHass(this);
|
||||
hass.updateTranslations(null, "en");
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
return css`
|
||||
ha-card {
|
||||
max-width: 600px;
|
||||
margin: 24px auto;
|
||||
}
|
||||
.action {
|
||||
padding: 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
span {
|
||||
margin-right: 16px;
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"demo-automation-describe-action": DemoAutomationDescribeAction;
|
||||
}
|
||||
}
|
@@ -1,60 +0,0 @@
|
||||
import { dump } from "js-yaml";
|
||||
import { html, css, LitElement, TemplateResult } from "lit";
|
||||
import { customElement } from "lit/decorators";
|
||||
import "../../../src/components/ha-card";
|
||||
import { describeCondition } from "../../../src/data/automation_i18n";
|
||||
|
||||
const conditions = [
|
||||
{ condition: "and" },
|
||||
{ condition: "not" },
|
||||
{ condition: "or" },
|
||||
{ condition: "state" },
|
||||
{ condition: "numeric_state" },
|
||||
{ condition: "sun", after: "sunset" },
|
||||
{ condition: "sun", after: "sunrise" },
|
||||
{ condition: "zone" },
|
||||
{ condition: "time" },
|
||||
{ condition: "template" },
|
||||
];
|
||||
|
||||
@customElement("demo-automation-describe-condition")
|
||||
export class DemoAutomationDescribeCondition extends LitElement {
|
||||
protected render(): TemplateResult {
|
||||
return html`
|
||||
<ha-card header="Conditions">
|
||||
${conditions.map(
|
||||
(conf) => html`
|
||||
<div class="condition">
|
||||
<span>${describeCondition(conf as any)}</span>
|
||||
<pre>${dump(conf)}</pre>
|
||||
</div>
|
||||
`
|
||||
)}
|
||||
</ha-card>
|
||||
`;
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
return css`
|
||||
ha-card {
|
||||
max-width: 600px;
|
||||
margin: 24px auto;
|
||||
}
|
||||
.condition {
|
||||
padding: 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
span {
|
||||
margin-right: 16px;
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"demo-automation-describe-condition": DemoAutomationDescribeCondition;
|
||||
}
|
||||
}
|
@@ -1,63 +0,0 @@
|
||||
import { dump } from "js-yaml";
|
||||
import { html, css, LitElement, TemplateResult } from "lit";
|
||||
import { customElement } from "lit/decorators";
|
||||
import "../../../src/components/ha-card";
|
||||
import { describeTrigger } from "../../../src/data/automation_i18n";
|
||||
|
||||
const triggers = [
|
||||
{ platform: "state" },
|
||||
{ platform: "mqtt" },
|
||||
{ platform: "geo_location" },
|
||||
{ platform: "homeassistant" },
|
||||
{ platform: "numeric_state" },
|
||||
{ platform: "sun" },
|
||||
{ platform: "time_pattern" },
|
||||
{ platform: "webhook" },
|
||||
{ platform: "zone" },
|
||||
{ platform: "tag" },
|
||||
{ platform: "time" },
|
||||
{ platform: "template" },
|
||||
{ platform: "event" },
|
||||
];
|
||||
|
||||
@customElement("demo-automation-describe-trigger")
|
||||
export class DemoAutomationDescribeTrigger extends LitElement {
|
||||
protected render(): TemplateResult {
|
||||
return html`
|
||||
<ha-card header="Triggers">
|
||||
${triggers.map(
|
||||
(conf) => html`
|
||||
<div class="trigger">
|
||||
<span>${describeTrigger(conf as any)}</span>
|
||||
<pre>${dump(conf)}</pre>
|
||||
</div>
|
||||
`
|
||||
)}
|
||||
</ha-card>
|
||||
`;
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
return css`
|
||||
ha-card {
|
||||
max-width: 600px;
|
||||
margin: 24px auto;
|
||||
}
|
||||
.trigger {
|
||||
padding: 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
span {
|
||||
margin-right: 16px;
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"demo-automation-describe-trigger": DemoAutomationDescribeTrigger;
|
||||
}
|
||||
}
|
@@ -1,81 +0,0 @@
|
||||
import { html, css, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import "../../../src/components/ha-card";
|
||||
import "../../../src/components/trace/hat-script-graph";
|
||||
import "../../../src/components/trace/hat-trace-timeline";
|
||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||
import { HomeAssistant } from "../../../src/types";
|
||||
import { mockDemoTrace } from "../data/traces/mock-demo-trace";
|
||||
import { DemoTrace } from "../data/traces/types";
|
||||
|
||||
const traces: DemoTrace[] = [
|
||||
mockDemoTrace({ state: "running" }),
|
||||
mockDemoTrace({ state: "debugged" }),
|
||||
mockDemoTrace({ state: "stopped", script_execution: "failed_conditions" }),
|
||||
mockDemoTrace({ state: "stopped", script_execution: "failed_single" }),
|
||||
mockDemoTrace({ state: "stopped", script_execution: "failed_max_runs" }),
|
||||
mockDemoTrace({ state: "stopped", script_execution: "finished" }),
|
||||
mockDemoTrace({ state: "stopped", script_execution: "aborted" }),
|
||||
mockDemoTrace({
|
||||
state: "stopped",
|
||||
script_execution: "error",
|
||||
error: 'Variable "beer" cannot be None',
|
||||
}),
|
||||
mockDemoTrace({ state: "stopped", script_execution: "cancelled" }),
|
||||
];
|
||||
|
||||
@customElement("demo-automation-trace-timeline")
|
||||
export class DemoAutomationTraceTimeline extends LitElement {
|
||||
@property({ attribute: false }) hass?: HomeAssistant;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this.hass) {
|
||||
return html``;
|
||||
}
|
||||
return html`
|
||||
${traces.map(
|
||||
(trace) => html`
|
||||
<ha-card .header=${trace.trace.config.alias}>
|
||||
<div class="card-content">
|
||||
<hat-trace-timeline
|
||||
.hass=${this.hass}
|
||||
.trace=${trace.trace}
|
||||
.logbookEntries=${trace.logbookEntries}
|
||||
></hat-trace-timeline>
|
||||
<button @click=${() => console.log(trace)}>Log trace</button>
|
||||
</div>
|
||||
</ha-card>
|
||||
`
|
||||
)}
|
||||
`;
|
||||
}
|
||||
|
||||
protected firstUpdated(changedProps) {
|
||||
super.firstUpdated(changedProps);
|
||||
const hass = provideHass(this);
|
||||
hass.updateTranslations(null, "en");
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
return css`
|
||||
ha-card {
|
||||
max-width: 600px;
|
||||
margin: 24px;
|
||||
}
|
||||
.card-content {
|
||||
display: flex;
|
||||
}
|
||||
button {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"demo-automation-trace-timeline": DemoAutomationTraceTimeline;
|
||||
}
|
||||
}
|
@@ -1,91 +0,0 @@
|
||||
import { html, css, LitElement, TemplateResult } from "lit";
|
||||
import "../../../src/components/ha-card";
|
||||
import "../../../src/components/trace/hat-script-graph";
|
||||
import "../../../src/components/trace/hat-trace-timeline";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||
import { HomeAssistant } from "../../../src/types";
|
||||
import { DemoTrace } from "../data/traces/types";
|
||||
import { basicTrace } from "../data/traces/basic_trace";
|
||||
import { motionLightTrace } from "../data/traces/motion-light-trace";
|
||||
|
||||
const traces: DemoTrace[] = [basicTrace, motionLightTrace];
|
||||
|
||||
@customElement("demo-automation-trace")
|
||||
export class DemoAutomationTrace extends LitElement {
|
||||
@property({ attribute: false }) hass?: HomeAssistant;
|
||||
|
||||
@state() private _selected = {};
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this.hass) {
|
||||
return html``;
|
||||
}
|
||||
return html`
|
||||
${traces.map(
|
||||
(trace, idx) => html`
|
||||
<ha-card .header=${trace.trace.config.alias}>
|
||||
<div class="card-content">
|
||||
<hat-script-graph
|
||||
.trace=${trace.trace}
|
||||
.selected=${this._selected[idx]}
|
||||
@graph-node-selected=${(ev) => {
|
||||
this._selected = { ...this._selected, [idx]: ev.detail.path };
|
||||
}}
|
||||
></hat-script-graph>
|
||||
<hat-trace-timeline
|
||||
allowPick
|
||||
.hass=${this.hass}
|
||||
.trace=${trace.trace}
|
||||
.logbookEntries=${trace.logbookEntries}
|
||||
.selectedPath=${this._selected[idx]}
|
||||
@value-changed=${(ev) => {
|
||||
this._selected = {
|
||||
...this._selected,
|
||||
[idx]: ev.detail.value,
|
||||
};
|
||||
}}
|
||||
></hat-trace-timeline>
|
||||
<button @click=${() => console.log(trace)}>Log trace</button>
|
||||
</div>
|
||||
</ha-card>
|
||||
`
|
||||
)}
|
||||
`;
|
||||
}
|
||||
|
||||
protected firstUpdated(changedProps) {
|
||||
super.firstUpdated(changedProps);
|
||||
const hass = provideHass(this);
|
||||
hass.updateTranslations(null, "en");
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
return css`
|
||||
ha-card {
|
||||
max-width: 600px;
|
||||
margin: 24px;
|
||||
}
|
||||
.card-content {
|
||||
display: flex;
|
||||
}
|
||||
.card-content > * {
|
||||
margin-right: 16px;
|
||||
}
|
||||
.card-content > *:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
button {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"demo-automation-trace": DemoAutomationTrace;
|
||||
}
|
||||
}
|
@@ -1,150 +0,0 @@
|
||||
import { html, css, LitElement, TemplateResult } from "lit";
|
||||
import { customElement } from "lit/decorators";
|
||||
import "../../../src/components/ha-alert";
|
||||
import "../../../src/components/ha-card";
|
||||
|
||||
const alerts: {
|
||||
title?: string;
|
||||
description: string | TemplateResult;
|
||||
type: "info" | "warning" | "error" | "success";
|
||||
dismissable?: boolean;
|
||||
action?: string;
|
||||
rtl?: boolean;
|
||||
}[] = [
|
||||
{
|
||||
title: "Test info alert",
|
||||
description: "This is a test info alert with a title and description",
|
||||
type: "info",
|
||||
},
|
||||
{
|
||||
title: "Test warning alert",
|
||||
description: "This is a test warning alert with a title and description",
|
||||
type: "warning",
|
||||
},
|
||||
{
|
||||
title: "Test error alert",
|
||||
description: "This is a test error alert with a title and description",
|
||||
type: "error",
|
||||
},
|
||||
{
|
||||
title: "Test warning with long string",
|
||||
description:
|
||||
"sensor.lorem_ipsum_lorem_ipsum_lorem_ipsum_lorem_ipsum_lorem_ipsum_lorem_ipsum_lorem_ipsum_lorem_ipsum_lorem_ipsum_lorem_ipsum_lorem_ipsum_lorem_ipsum",
|
||||
type: "warning",
|
||||
},
|
||||
{
|
||||
title: "Test success alert",
|
||||
description: "This is a test success alert with a title and description",
|
||||
type: "success",
|
||||
},
|
||||
{
|
||||
description: "This is a test info alert with description only",
|
||||
type: "info",
|
||||
},
|
||||
{
|
||||
description:
|
||||
"This is a test warning alert with a rally really really rally really really rally really really rally really really rally really really rally really really rally really really rally really really rally really really rally really really rally really really rally really really rally really really rally really really rally really really rally really really rally really really rally really really rally really really rally really really rally really really long description only",
|
||||
type: "warning",
|
||||
},
|
||||
{
|
||||
title: "Error with description and list",
|
||||
description: html`<p>
|
||||
This is a test error alert with a title, description and a list
|
||||
</p>
|
||||
<ul>
|
||||
<li>List item #1</li>
|
||||
<li>List item #2</li>
|
||||
<li>List item #3</li>
|
||||
</ul>`,
|
||||
type: "error",
|
||||
},
|
||||
{
|
||||
title: "Test dismissable alert",
|
||||
description: "This is a test success alert that can be dismissable",
|
||||
type: "success",
|
||||
dismissable: true,
|
||||
},
|
||||
{
|
||||
description: "Dismissable information",
|
||||
type: "info",
|
||||
dismissable: true,
|
||||
},
|
||||
{
|
||||
title: "Error with action",
|
||||
description: "This is a test error alert with action",
|
||||
type: "error",
|
||||
action: "restart",
|
||||
},
|
||||
{
|
||||
title: "Unsaved data",
|
||||
description: "You have unsaved data",
|
||||
type: "warning",
|
||||
action: "save",
|
||||
},
|
||||
{
|
||||
description: "Dismissable information (RTL)",
|
||||
type: "info",
|
||||
dismissable: true,
|
||||
rtl: true,
|
||||
},
|
||||
{
|
||||
title: "Error with action",
|
||||
description: "This is a test error alert with action (RTL)",
|
||||
type: "error",
|
||||
action: "restart",
|
||||
rtl: true,
|
||||
},
|
||||
{
|
||||
title: "Test success alert (RTL)",
|
||||
description: "This is a test success alert with a title and description",
|
||||
type: "success",
|
||||
rtl: true,
|
||||
},
|
||||
];
|
||||
|
||||
@customElement("demo-ha-alert")
|
||||
export class DemoHaAlert extends LitElement {
|
||||
protected render(): TemplateResult {
|
||||
return html`
|
||||
<ha-card header="ha-alert demo">
|
||||
${alerts.map(
|
||||
(alert) => html`
|
||||
<ha-alert
|
||||
.title=${alert.title || ""}
|
||||
.alertType=${alert.type}
|
||||
.dismissable=${alert.dismissable || false}
|
||||
.actionText=${alert.action || ""}
|
||||
.rtl=${alert.rtl || false}
|
||||
>
|
||||
${alert.description}
|
||||
</ha-alert>
|
||||
`
|
||||
)}
|
||||
</ha-card>
|
||||
`;
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
return css`
|
||||
ha-card {
|
||||
max-width: 600px;
|
||||
margin: 24px auto;
|
||||
}
|
||||
.condition {
|
||||
padding: 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
span {
|
||||
margin-right: 16px;
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"demo-ha-alert": DemoHaAlert;
|
||||
}
|
||||
}
|
@@ -1,5 +1,11 @@
|
||||
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
|
||||
import { customElement, query } from "lit/decorators";
|
||||
import {
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
PropertyValues,
|
||||
query,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { getEntity } from "../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||
import "../components/demo-cards";
|
||||
@@ -87,8 +93,4 @@ class DemoAlarmPanelEntity extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"demo-hui-alarm-panel-card": DemoAlarmPanelEntity;
|
||||
}
|
||||
}
|
||||
customElements.define("demo-hui-alarm-panel-card", DemoAlarmPanelEntity);
|
||||
|
@@ -1,5 +1,11 @@
|
||||
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
|
||||
import { customElement, query } from "lit/decorators";
|
||||
import {
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
PropertyValues,
|
||||
query,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { getEntity } from "../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||
import "../components/demo-cards";
|
||||
@@ -69,8 +75,4 @@ class DemoConditional extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"demo-hui-conditional-card": DemoConditional;
|
||||
}
|
||||
}
|
||||
customElements.define("demo-hui-conditional-card", DemoConditional);
|
||||
|
@@ -1,5 +1,11 @@
|
||||
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
|
||||
import { customElement, query } from "lit/decorators";
|
||||
import {
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
PropertyValues,
|
||||
query,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { getEntity } from "../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||
import "../components/demo-cards";
|
||||
@@ -233,8 +239,4 @@ class DemoEntities extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"demo-hui-entities-card": DemoEntities;
|
||||
}
|
||||
}
|
||||
customElements.define("demo-hui-entities-card", DemoEntities);
|
||||
|
@@ -1,5 +1,11 @@
|
||||
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
|
||||
import { customElement, query } from "lit/decorators";
|
||||
import {
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
PropertyValues,
|
||||
query,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { getEntity } from "../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||
import "../components/demo-cards";
|
||||
@@ -85,8 +91,4 @@ class DemoButtonEntity extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"demo-hui-entity-button-card": DemoButtonEntity;
|
||||
}
|
||||
}
|
||||
customElements.define("demo-hui-entity-button-card", DemoButtonEntity);
|
||||
|
@@ -1,5 +1,11 @@
|
||||
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
|
||||
import { customElement, query } from "lit/decorators";
|
||||
import {
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
PropertyValues,
|
||||
query,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { getEntity } from "../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||
import "../components/demo-cards";
|
||||
@@ -126,8 +132,4 @@ class DemoEntityFilter extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"demo-hui-entity-filter-card": DemoEntityFilter;
|
||||
}
|
||||
}
|
||||
customElements.define("demo-hui-entity-filter-card", DemoEntityFilter);
|
||||
|
@@ -1,5 +1,11 @@
|
||||
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
|
||||
import { customElement, query } from "lit/decorators";
|
||||
import {
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
PropertyValues,
|
||||
query,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { getEntity } from "../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||
import "../components/demo-cards";
|
||||
@@ -123,8 +129,4 @@ class DemoGaugeEntity extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"demo-hui-gauge-card": DemoGaugeEntity;
|
||||
}
|
||||
}
|
||||
customElements.define("demo-hui-gauge-card", DemoGaugeEntity);
|
||||
|
@@ -1,5 +1,11 @@
|
||||
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
|
||||
import { customElement, query } from "lit/decorators";
|
||||
import {
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
PropertyValues,
|
||||
query,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { getEntity } from "../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||
import "../components/demo-cards";
|
||||
@@ -180,7 +186,7 @@ const CONFIGS = [
|
||||
name:
|
||||
- light.kitchen_lights
|
||||
- entity: lock.kitchen_door
|
||||
name:
|
||||
name:
|
||||
- light.ceiling_lights
|
||||
`,
|
||||
},
|
||||
@@ -188,7 +194,7 @@ const CONFIGS = [
|
||||
heading: "Custom tap action",
|
||||
config: `
|
||||
- type: glance
|
||||
columns: 4
|
||||
columns: 4
|
||||
entities:
|
||||
- entity: lock.kitchen_door
|
||||
name: Custom
|
||||
@@ -226,8 +232,4 @@ class DemoGlanceEntity extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"demo-hui-glance-card": DemoGlanceEntity;
|
||||
}
|
||||
}
|
||||
customElements.define("demo-hui-glance-card", DemoGlanceEntity);
|
||||
|
@@ -1,5 +1,4 @@
|
||||
import { html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement } from "lit/decorators";
|
||||
import { customElement, html, LitElement, TemplateResult } from "lit-element";
|
||||
import "../components/demo-cards";
|
||||
|
||||
const CONFIGS = [
|
||||
@@ -43,8 +42,4 @@ class DemoIframe extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"demo-hui-iframe-card": DemoIframe;
|
||||
}
|
||||
}
|
||||
customElements.define("demo-hui-iframe-card", DemoIframe);
|
||||
|
@@ -1,5 +1,11 @@
|
||||
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
|
||||
import { customElement, query } from "lit/decorators";
|
||||
import {
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
PropertyValues,
|
||||
query,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { getEntity } from "../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||
import "../components/demo-cards";
|
||||
@@ -79,8 +85,4 @@ class DemoLightEntity extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"demo-hui-light-card": DemoLightEntity;
|
||||
}
|
||||
}
|
||||
customElements.define("demo-hui-light-card", DemoLightEntity);
|
||||
|
@@ -1,5 +1,11 @@
|
||||
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
|
||||
import { customElement, query } from "lit/decorators";
|
||||
import {
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
PropertyValues,
|
||||
query,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { getEntity } from "../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||
import "../components/demo-cards";
|
||||
@@ -177,8 +183,4 @@ class DemoMap extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"demo-hui-map-card": DemoMap;
|
||||
}
|
||||
}
|
||||
customElements.define("demo-hui-map-card", DemoMap);
|
||||
|
@@ -1,5 +1,11 @@
|
||||
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
|
||||
import { customElement, query } from "lit/decorators";
|
||||
import {
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
PropertyValues,
|
||||
query,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { mockTemplate } from "../../../demo/src/stubs/template";
|
||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||
import "../components/demo-cards";
|
||||
@@ -270,8 +276,4 @@ class DemoMarkdown extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"demo-hui-markdown-card": DemoMarkdown;
|
||||
}
|
||||
}
|
||||
customElements.define("demo-hui-markdown-card", DemoMarkdown);
|
||||
|
@@ -1,5 +1,11 @@
|
||||
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
|
||||
import { customElement, query } from "lit/decorators";
|
||||
import {
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
PropertyValues,
|
||||
query,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||
import "../components/demo-cards";
|
||||
import { createMediaPlayerEntities } from "../data/media_players";
|
||||
@@ -174,8 +180,4 @@ class DemoHuiMediaControlCard extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"demo-hui-media-control-card": DemoHuiMediaControlCard;
|
||||
}
|
||||
}
|
||||
customElements.define("demo-hui-media-control-card", DemoHuiMediaControlCard);
|
||||
|
@@ -1,5 +1,11 @@
|
||||
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
|
||||
import { customElement, query } from "lit/decorators";
|
||||
import {
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
PropertyValues,
|
||||
query,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||
import "../components/demo-cards";
|
||||
import { createMediaPlayerEntities } from "../data/media_players";
|
||||
@@ -71,8 +77,4 @@ class DemoHuiMediaPlayerRow extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"demo-hui-media-player-row": DemoHuiMediaPlayerRow;
|
||||
}
|
||||
}
|
||||
customElements.define("demo-hui-media-player-row", DemoHuiMediaPlayerRow);
|
||||
|
@@ -1,5 +1,11 @@
|
||||
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
|
||||
import { customElement, query } from "lit/decorators";
|
||||
import {
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
PropertyValues,
|
||||
query,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { getEntity } from "../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||
import "../components/demo-cards";
|
||||
@@ -141,8 +147,4 @@ class DemoPictureElements extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"demo-hui-picture-elements-card": DemoPictureElements;
|
||||
}
|
||||
}
|
||||
customElements.define("demo-hui-picture-elements-card", DemoPictureElements);
|
||||
|
@@ -1,5 +1,11 @@
|
||||
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
|
||||
import { customElement, query } from "lit/decorators";
|
||||
import {
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
PropertyValues,
|
||||
query,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { getEntity } from "../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||
import "../components/demo-cards";
|
||||
@@ -96,8 +102,4 @@ class DemoPictureEntity extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"demo-hui-picture-entity-card": DemoPictureEntity;
|
||||
}
|
||||
}
|
||||
customElements.define("demo-hui-picture-entity-card", DemoPictureEntity);
|
||||
|
@@ -1,5 +1,11 @@
|
||||
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
|
||||
import { customElement, query } from "lit/decorators";
|
||||
import {
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
PropertyValues,
|
||||
query,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { getEntity } from "../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||
import "../components/demo-cards";
|
||||
@@ -137,8 +143,4 @@ class DemoPictureGlance extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"demo-hui-picture-glance-card": DemoPictureGlance;
|
||||
}
|
||||
}
|
||||
customElements.define("demo-hui-picture-glance-card", DemoPictureGlance);
|
||||
|
@@ -1,5 +1,11 @@
|
||||
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
|
||||
import { customElement, query } from "lit/decorators";
|
||||
import {
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
PropertyValues,
|
||||
query,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||
import "../components/demo-cards";
|
||||
import { createPlantEntities } from "../data/plants";
|
||||
@@ -46,8 +52,4 @@ export class DemoPlantEntity extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"demo-hui-plant-card": DemoPlantEntity;
|
||||
}
|
||||
}
|
||||
customElements.define("demo-hui-plant-card", DemoPlantEntity);
|
||||
|
@@ -1,5 +1,11 @@
|
||||
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
|
||||
import { customElement, query } from "lit/decorators";
|
||||
import {
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
PropertyValues,
|
||||
query,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||
import "../components/demo-cards";
|
||||
|
||||
@@ -42,8 +48,4 @@ class DemoShoppingListEntity extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"demo-hui-shopping-list-card": DemoShoppingListEntity;
|
||||
}
|
||||
}
|
||||
customElements.define("demo-hui-shopping-list-card", DemoShoppingListEntity);
|
||||
|
@@ -1,5 +1,11 @@
|
||||
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
|
||||
import { customElement, query } from "lit/decorators";
|
||||
import {
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
PropertyValues,
|
||||
query,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { mockHistory } from "../../../demo/src/stubs/history";
|
||||
import { getEntity } from "../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||
@@ -43,110 +49,6 @@ const ENTITIES = [
|
||||
];
|
||||
|
||||
const CONFIGS = [
|
||||
{
|
||||
heading: "Default Grid",
|
||||
config: `
|
||||
- type: grid
|
||||
cards:
|
||||
- type: entity
|
||||
entity: light.kitchen_lights
|
||||
- type: entity
|
||||
entity: light.bed_light
|
||||
- type: entity
|
||||
entity: device_tracker.demo_paulus
|
||||
- type: sensor
|
||||
entity: sensor.illumination
|
||||
graph: line
|
||||
- type: entity
|
||||
entity: device_tracker.demo_anne_therese
|
||||
`,
|
||||
},
|
||||
{
|
||||
heading: "Non-square Grid with 2 columns",
|
||||
config: `
|
||||
- type: grid
|
||||
columns: 2
|
||||
square: false
|
||||
cards:
|
||||
- type: entity
|
||||
entity: light.kitchen_lights
|
||||
- type: entity
|
||||
entity: light.bed_light
|
||||
- type: entity
|
||||
entity: device_tracker.demo_paulus
|
||||
- type: sensor
|
||||
entity: sensor.illumination
|
||||
graph: line
|
||||
`,
|
||||
},
|
||||
{
|
||||
heading: "Default Grid with title",
|
||||
config: `
|
||||
- type: grid
|
||||
title: Kitchen
|
||||
cards:
|
||||
- type: entity
|
||||
entity: light.kitchen_lights
|
||||
- type: entity
|
||||
entity: light.bed_light
|
||||
- type: entity
|
||||
entity: device_tracker.demo_paulus
|
||||
- type: sensor
|
||||
entity: sensor.illumination
|
||||
graph: line
|
||||
- type: entity
|
||||
entity: device_tracker.demo_anne_therese
|
||||
`,
|
||||
},
|
||||
{
|
||||
heading: "Columns 4",
|
||||
config: `
|
||||
- type: grid
|
||||
columns: 4
|
||||
cards:
|
||||
- type: entity
|
||||
entity: light.kitchen_lights
|
||||
- type: entity
|
||||
entity: light.bed_light
|
||||
- type: entity
|
||||
entity: device_tracker.demo_paulus
|
||||
- type: sensor
|
||||
entity: sensor.illumination
|
||||
graph: line
|
||||
`,
|
||||
},
|
||||
{
|
||||
heading: "Columns 2",
|
||||
config: `
|
||||
- type: grid
|
||||
columns: 2
|
||||
cards:
|
||||
- type: entity
|
||||
entity: light.kitchen_lights
|
||||
- type: entity
|
||||
entity: light.bed_light
|
||||
`,
|
||||
},
|
||||
{
|
||||
heading: "Columns 1",
|
||||
config: `
|
||||
- type: grid
|
||||
columns: 1
|
||||
cards:
|
||||
- type: entity
|
||||
entity: light.kitchen_lights
|
||||
`,
|
||||
},
|
||||
{
|
||||
heading: "Size for single card",
|
||||
config: `
|
||||
- type: grid
|
||||
cards:
|
||||
- type: entity
|
||||
entity: light.kitchen_lights
|
||||
`,
|
||||
},
|
||||
|
||||
{
|
||||
heading: "Vertical Stack",
|
||||
config: `
|
||||
@@ -197,9 +99,45 @@ const CONFIGS = [
|
||||
entity: light.bed_light
|
||||
`,
|
||||
},
|
||||
{
|
||||
heading: "Default Grid",
|
||||
config: `
|
||||
- type: grid
|
||||
cards:
|
||||
- type: entity
|
||||
entity: light.kitchen_lights
|
||||
- type: entity
|
||||
entity: light.bed_light
|
||||
- type: entity
|
||||
entity: device_tracker.demo_paulus
|
||||
- type: sensor
|
||||
entity: sensor.illumination
|
||||
graph: line
|
||||
- type: entity
|
||||
entity: device_tracker.demo_anne_therese
|
||||
`,
|
||||
},
|
||||
{
|
||||
heading: "Non-square Grid with 2 columns",
|
||||
config: `
|
||||
- type: grid
|
||||
columns: 2
|
||||
square: false
|
||||
cards:
|
||||
- type: entity
|
||||
entity: light.kitchen_lights
|
||||
- type: entity
|
||||
entity: light.bed_light
|
||||
- type: entity
|
||||
entity: device_tracker.demo_paulus
|
||||
- type: sensor
|
||||
entity: sensor.illumination
|
||||
graph: line
|
||||
`,
|
||||
},
|
||||
];
|
||||
|
||||
@customElement("demo-hui-grid-and-stack-card")
|
||||
@customElement("demo-hui-stack-card")
|
||||
class DemoStack extends LitElement {
|
||||
@query("#demos") private _demoRoot!: HTMLElement;
|
||||
|
||||
@@ -217,8 +155,4 @@ class DemoStack extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"demo-hui-grid-and-stack-card": DemoStack;
|
||||
}
|
||||
}
|
||||
customElements.define("demo-hui-stack-card", DemoStack);
|
@@ -1,5 +1,11 @@
|
||||
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
|
||||
import { customElement, query } from "lit/decorators";
|
||||
import {
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
PropertyValues,
|
||||
query,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { getEntity } from "../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||
import "../components/demo-cards";
|
||||
@@ -90,8 +96,4 @@ class DemoThermostatEntity extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"demo-hui-thermostat-card": DemoThermostatEntity;
|
||||
}
|
||||
}
|
||||
customElements.define("demo-hui-thermostat-card", DemoThermostatEntity);
|
||||
|
@@ -1,354 +0,0 @@
|
||||
import { html, css, LitElement, TemplateResult } from "lit";
|
||||
import "../../../src/components/ha-formfield";
|
||||
import "../../../src/components/ha-switch";
|
||||
|
||||
import { classMap } from "lit/directives/class-map";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { IntegrationManifest } from "../../../src/data/integration";
|
||||
|
||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||
import { HomeAssistant } from "../../../src/types";
|
||||
import "../../../src/panels/config/integrations/ha-integration-card";
|
||||
import "../../../src/panels/config/integrations/ha-ignored-config-entry-card";
|
||||
import "../../../src/panels/config/integrations/ha-config-flow-card";
|
||||
import type {
|
||||
ConfigEntryExtended,
|
||||
DataEntryFlowProgressExtended,
|
||||
} from "../../../src/panels/config/integrations/ha-config-integrations";
|
||||
import { DeviceRegistryEntry } from "../../../src/data/device_registry";
|
||||
import { EntityRegistryEntry } from "../../../src/data/entity_registry";
|
||||
|
||||
const createConfigEntry = (
|
||||
title: string,
|
||||
override: Partial<ConfigEntryExtended> = {}
|
||||
): ConfigEntryExtended => ({
|
||||
entry_id: title,
|
||||
domain: "esphome",
|
||||
localized_domain_name: "ESPHome",
|
||||
title,
|
||||
source: "zeroconf",
|
||||
state: "loaded",
|
||||
supports_options: false,
|
||||
supports_unload: true,
|
||||
disabled_by: null,
|
||||
pref_disable_new_entities: false,
|
||||
pref_disable_polling: false,
|
||||
reason: null,
|
||||
...override,
|
||||
});
|
||||
|
||||
const createManifest = (
|
||||
isCustom: boolean,
|
||||
isCloud: boolean,
|
||||
name = "ESPHome"
|
||||
): IntegrationManifest => ({
|
||||
name,
|
||||
domain: "esphome",
|
||||
is_built_in: !isCustom,
|
||||
config_flow: false,
|
||||
documentation: "https://www.home-assistant.io/integrations/esphome/",
|
||||
iot_class: isCloud ? "cloud_polling" : "local_polling",
|
||||
});
|
||||
|
||||
const loadedEntry = createConfigEntry("Loaded");
|
||||
const nameAsDomainEntry = createConfigEntry("ESPHome");
|
||||
const longNameEntry = createConfigEntry(
|
||||
"Entry with a super long name that is going to the next line"
|
||||
);
|
||||
const longNonBreakingNameEntry = createConfigEntry(
|
||||
"EntryWithASuperLongNameThatDoesNotBreak"
|
||||
);
|
||||
const configPanelEntry = createConfigEntry("Config Panel", {
|
||||
domain: "mqtt",
|
||||
localized_domain_name: "MQTT",
|
||||
});
|
||||
const optionsFlowEntry = createConfigEntry("Options Flow", {
|
||||
supports_options: true,
|
||||
});
|
||||
const disabledPollingEntry = createConfigEntry("Disabled Polling", {
|
||||
pref_disable_polling: true,
|
||||
});
|
||||
const setupErrorEntry = createConfigEntry("Setup Error", {
|
||||
state: "setup_error",
|
||||
});
|
||||
const migrationErrorEntry = createConfigEntry("Migration Error", {
|
||||
state: "migration_error",
|
||||
});
|
||||
const setupRetryEntry = createConfigEntry("Setup Retry", {
|
||||
state: "setup_retry",
|
||||
});
|
||||
const setupRetryReasonEntry = createConfigEntry("Setup Retry", {
|
||||
state: "setup_retry",
|
||||
reason: "connection_error",
|
||||
});
|
||||
const setupRetryReasonMissingKeyEntry = createConfigEntry("Setup Retry", {
|
||||
state: "setup_retry",
|
||||
reason:
|
||||
"HTTPSConnectionpool: Max retries exceeded with NewConnectionError('<urllib3.connection.HTTPSConnection object at 0x9eedfc10>: Failed to establish a new connection: [Errno 113] Host is unreachable')",
|
||||
});
|
||||
const failedUnloadEntry = createConfigEntry("Failed Unload", {
|
||||
state: "failed_unload",
|
||||
});
|
||||
const notLoadedEntry = createConfigEntry("Not Loaded", { state: "not_loaded" });
|
||||
const disabledEntry = createConfigEntry("Disabled", {
|
||||
state: "not_loaded",
|
||||
disabled_by: "user",
|
||||
});
|
||||
const disabledFailedUnloadEntry = createConfigEntry(
|
||||
"Disabled - Failed Unload",
|
||||
{
|
||||
state: "failed_unload",
|
||||
disabled_by: "user",
|
||||
}
|
||||
);
|
||||
|
||||
const configFlows: DataEntryFlowProgressExtended[] = [
|
||||
{
|
||||
flow_id: "adbb401329d8439ebb78ef29837826a8",
|
||||
handler: "roku",
|
||||
context: {
|
||||
source: "ssdp",
|
||||
unique_id: "YF008D862864",
|
||||
title_placeholders: {
|
||||
name: "Living room Roku",
|
||||
},
|
||||
},
|
||||
step_id: "discovery_confirm",
|
||||
localized_title: "Living room Roku",
|
||||
},
|
||||
{
|
||||
flow_id: "adbb401329d8439ebb78ef29837826a8",
|
||||
handler: "hue",
|
||||
context: {
|
||||
source: "reauth",
|
||||
unique_id: "YF008D862864",
|
||||
title_placeholders: {
|
||||
name: "Living room Roku",
|
||||
},
|
||||
},
|
||||
step_id: "discovery_confirm",
|
||||
localized_title: "Philips Hue",
|
||||
},
|
||||
];
|
||||
|
||||
const configEntries: Array<{
|
||||
items: ConfigEntryExtended[];
|
||||
is_custom?: boolean;
|
||||
disabled?: boolean;
|
||||
highlight?: string;
|
||||
}> = [
|
||||
{ items: [loadedEntry] },
|
||||
{ items: [configPanelEntry] },
|
||||
{ items: [optionsFlowEntry] },
|
||||
{ items: [disabledPollingEntry] },
|
||||
{ items: [nameAsDomainEntry] },
|
||||
{ items: [longNameEntry] },
|
||||
{ items: [longNonBreakingNameEntry] },
|
||||
{ items: [setupErrorEntry] },
|
||||
{ items: [migrationErrorEntry] },
|
||||
{ items: [setupRetryEntry] },
|
||||
{ items: [setupRetryReasonEntry] },
|
||||
{ items: [setupRetryReasonMissingKeyEntry] },
|
||||
{ items: [failedUnloadEntry] },
|
||||
{ items: [notLoadedEntry] },
|
||||
{
|
||||
items: [
|
||||
loadedEntry,
|
||||
setupErrorEntry,
|
||||
migrationErrorEntry,
|
||||
longNameEntry,
|
||||
longNonBreakingNameEntry,
|
||||
setupRetryEntry,
|
||||
failedUnloadEntry,
|
||||
notLoadedEntry,
|
||||
disabledEntry,
|
||||
nameAsDomainEntry,
|
||||
configPanelEntry,
|
||||
optionsFlowEntry,
|
||||
],
|
||||
},
|
||||
{ disabled: true, items: [disabledEntry] },
|
||||
{ disabled: true, items: [disabledFailedUnloadEntry] },
|
||||
{
|
||||
disabled: true,
|
||||
items: [disabledEntry, disabledFailedUnloadEntry],
|
||||
},
|
||||
{
|
||||
items: [loadedEntry, configPanelEntry],
|
||||
highlight: "Loaded",
|
||||
},
|
||||
];
|
||||
|
||||
const createEntityRegistryEntries = (
|
||||
item: ConfigEntryExtended
|
||||
): EntityRegistryEntry[] => [
|
||||
{
|
||||
config_entry_id: item.entry_id,
|
||||
device_id: "mock-device-id",
|
||||
area_id: null,
|
||||
disabled_by: null,
|
||||
entity_id: "binary_sensor.updater",
|
||||
name: null,
|
||||
icon: null,
|
||||
platform: "updater",
|
||||
},
|
||||
];
|
||||
|
||||
const createDeviceRegistryEntries = (
|
||||
item: ConfigEntryExtended
|
||||
): DeviceRegistryEntry[] => [
|
||||
{
|
||||
entry_type: null,
|
||||
config_entries: [item.entry_id],
|
||||
connections: [],
|
||||
manufacturer: "ESPHome",
|
||||
model: "Mock Device",
|
||||
name: "Tag Reader",
|
||||
sw_version: null,
|
||||
id: "mock-device-id",
|
||||
identifiers: [],
|
||||
via_device_id: null,
|
||||
area_id: null,
|
||||
name_by_user: null,
|
||||
disabled_by: null,
|
||||
},
|
||||
];
|
||||
|
||||
@customElement("demo-integration-card")
|
||||
export class DemoIntegrationCard extends LitElement {
|
||||
@property({ attribute: false }) hass?: HomeAssistant;
|
||||
|
||||
@state() isCustomIntegration = false;
|
||||
|
||||
@state() isCloud = false;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this.hass) {
|
||||
return html``;
|
||||
}
|
||||
return html`
|
||||
<div class="container">
|
||||
<div class="filters">
|
||||
<ha-formfield label="Custom Integration">
|
||||
<ha-switch @change=${this._toggleCustomIntegration}></ha-switch>
|
||||
</ha-formfield>
|
||||
<ha-formfield label="Relies on cloud">
|
||||
<ha-switch @change=${this._toggleCloud}></ha-switch>
|
||||
</ha-formfield>
|
||||
</div>
|
||||
|
||||
<ha-ignored-config-entry-card
|
||||
.hass=${this.hass}
|
||||
.entry=${createConfigEntry("Ignored Entry")}
|
||||
.manifest=${createManifest(this.isCustomIntegration, this.isCloud)}
|
||||
></ha-ignored-config-entry-card>
|
||||
|
||||
${configFlows.map(
|
||||
(flow) => html`
|
||||
<ha-config-flow-card
|
||||
.hass=${this.hass}
|
||||
.flow=${flow}
|
||||
.manifest=${createManifest(
|
||||
this.isCustomIntegration,
|
||||
this.isCloud,
|
||||
flow.handler === "roku" ? "Roku" : "Philips Hue"
|
||||
)}
|
||||
></ha-config-flow-card>
|
||||
`
|
||||
)}
|
||||
${configEntries.map(
|
||||
(info) => html`
|
||||
<ha-integration-card
|
||||
class=${classMap({
|
||||
highlight: info.highlight !== undefined,
|
||||
})}
|
||||
.hass=${this.hass}
|
||||
domain="esphome"
|
||||
.items=${info.items}
|
||||
.manifest=${createManifest(
|
||||
this.isCustomIntegration,
|
||||
this.isCloud
|
||||
)}
|
||||
.entityRegistryEntries=${createEntityRegistryEntries(
|
||||
info.items[0]
|
||||
)}
|
||||
.deviceRegistryEntries=${createDeviceRegistryEntries(
|
||||
info.items[0]
|
||||
)}
|
||||
?disabled=${info.disabled}
|
||||
.selectedConfigEntryId=${info.highlight}
|
||||
></ha-integration-card>
|
||||
`
|
||||
)}
|
||||
</div>
|
||||
<div class="container">
|
||||
<!-- One that is standalone to see how it increases height if height
|
||||
not defined by other cards. -->
|
||||
<ha-integration-card
|
||||
.hass=${this.hass}
|
||||
domain="esphome"
|
||||
.items=${[
|
||||
loadedEntry,
|
||||
setupErrorEntry,
|
||||
migrationErrorEntry,
|
||||
setupRetryEntry,
|
||||
failedUnloadEntry,
|
||||
]}
|
||||
.manifest=${createManifest(this.isCustomIntegration, this.isCloud)}
|
||||
.entityRegistryEntries=${createEntityRegistryEntries(loadedEntry)}
|
||||
.deviceRegistryEntries=${createDeviceRegistryEntries(loadedEntry)}
|
||||
></ha-integration-card>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
protected firstUpdated(changedProps) {
|
||||
super.firstUpdated(changedProps);
|
||||
const hass = provideHass(this);
|
||||
hass.updateTranslations(null, "en");
|
||||
hass.updateTranslations("config", "en");
|
||||
// Normally this string is loaded from backend
|
||||
hass.addTranslations(
|
||||
{
|
||||
"component.esphome.config.error.connection_error":
|
||||
"Can't connect to ESP. Please make sure your YAML file contains an 'api:' line.",
|
||||
},
|
||||
"en"
|
||||
);
|
||||
}
|
||||
|
||||
private _toggleCustomIntegration() {
|
||||
this.isCustomIntegration = !this.isCustomIntegration;
|
||||
}
|
||||
|
||||
private _toggleCloud() {
|
||||
this.isCloud = !this.isCloud;
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
return css`
|
||||
.container {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||
grid-gap: 16px 16px;
|
||||
padding: 8px 16px 16px;
|
||||
margin-bottom: 64px;
|
||||
}
|
||||
|
||||
.container > * {
|
||||
max-width: 500px;
|
||||
}
|
||||
|
||||
ha-formfield {
|
||||
margin: 8px 0;
|
||||
display: block;
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"demo-integration-card": DemoIntegrationCard;
|
||||
}
|
||||
}
|
@@ -1,11 +1,21 @@
|
||||
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
|
||||
import { customElement, property, query } from "lit/decorators";
|
||||
import {
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
property,
|
||||
PropertyValues,
|
||||
query,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import "../../../src/components/ha-card";
|
||||
import {
|
||||
LightColorModes,
|
||||
SUPPORT_BRIGHTNESS,
|
||||
SUPPORT_COLOR,
|
||||
SUPPORT_COLOR_TEMP,
|
||||
SUPPORT_EFFECT,
|
||||
SUPPORT_FLASH,
|
||||
SUPPORT_TRANSITION,
|
||||
SUPPORT_WHITE_VALUE,
|
||||
} from "../../../src/data/light";
|
||||
import "../../../src/dialogs/more-info/more-info-content";
|
||||
import { getEntity } from "../../../src/fake_data/entity";
|
||||
@@ -22,8 +32,7 @@ const ENTITIES = [
|
||||
getEntity("light", "kitchen_light", "on", {
|
||||
friendly_name: "Brightness Light",
|
||||
brightness: 200,
|
||||
supported_color_modes: [LightColorModes.BRIGHTNESS],
|
||||
color_mode: LightColorModes.BRIGHTNESS,
|
||||
supported_features: SUPPORT_BRIGHTNESS,
|
||||
}),
|
||||
getEntity("light", "color_temperature_light", "on", {
|
||||
friendly_name: "White Color Temperature Light",
|
||||
@@ -31,96 +40,20 @@ const ENTITIES = [
|
||||
color_temp: 75,
|
||||
min_mireds: 30,
|
||||
max_mireds: 150,
|
||||
supported_color_modes: [
|
||||
LightColorModes.BRIGHTNESS,
|
||||
LightColorModes.COLOR_TEMP,
|
||||
],
|
||||
color_mode: LightColorModes.COLOR_TEMP,
|
||||
supported_features: SUPPORT_BRIGHTNESS + SUPPORT_COLOR_TEMP,
|
||||
}),
|
||||
getEntity("light", "color_hs_light", "on", {
|
||||
friendly_name: "Color HS Light",
|
||||
brightness: 255,
|
||||
hs_color: [30, 100],
|
||||
rgb_color: [30, 100, 255],
|
||||
min_mireds: 30,
|
||||
max_mireds: 150,
|
||||
supported_features: SUPPORT_EFFECT + SUPPORT_FLASH + SUPPORT_TRANSITION,
|
||||
supported_color_modes: [
|
||||
LightColorModes.BRIGHTNESS,
|
||||
LightColorModes.COLOR_TEMP,
|
||||
LightColorModes.HS,
|
||||
],
|
||||
color_mode: LightColorModes.HS,
|
||||
effect_list: ["random", "colorloop"],
|
||||
}),
|
||||
getEntity("light", "color_rgb_ct_light", "on", {
|
||||
friendly_name: "Color RGB + CT Light",
|
||||
brightness: 255,
|
||||
color_temp: 75,
|
||||
min_mireds: 30,
|
||||
max_mireds: 150,
|
||||
supported_features: SUPPORT_EFFECT + SUPPORT_FLASH + SUPPORT_TRANSITION,
|
||||
supported_color_modes: [
|
||||
LightColorModes.BRIGHTNESS,
|
||||
LightColorModes.COLOR_TEMP,
|
||||
LightColorModes.RGB,
|
||||
],
|
||||
color_mode: LightColorModes.COLOR_TEMP,
|
||||
effect_list: ["random", "colorloop"],
|
||||
}),
|
||||
getEntity("light", "color_RGB_light", "on", {
|
||||
getEntity("light", "color_effectslight", "on", {
|
||||
friendly_name: "Color Effets Light",
|
||||
brightness: 255,
|
||||
rgb_color: [30, 100, 255],
|
||||
supported_features: SUPPORT_EFFECT + SUPPORT_FLASH + SUPPORT_TRANSITION,
|
||||
supported_color_modes: [LightColorModes.BRIGHTNESS, LightColorModes.RGB],
|
||||
color_mode: LightColorModes.RGB,
|
||||
effect_list: ["random", "colorloop"],
|
||||
}),
|
||||
getEntity("light", "color_rgbw_light", "on", {
|
||||
friendly_name: "Color RGBW Light",
|
||||
brightness: 255,
|
||||
rgbw_color: [30, 100, 255, 125],
|
||||
min_mireds: 30,
|
||||
max_mireds: 150,
|
||||
supported_features: SUPPORT_EFFECT + SUPPORT_FLASH + SUPPORT_TRANSITION,
|
||||
supported_color_modes: [
|
||||
LightColorModes.BRIGHTNESS,
|
||||
LightColorModes.COLOR_TEMP,
|
||||
LightColorModes.RGBW,
|
||||
],
|
||||
color_mode: LightColorModes.RGBW,
|
||||
effect_list: ["random", "colorloop"],
|
||||
}),
|
||||
getEntity("light", "color_rgbww_light", "on", {
|
||||
friendly_name: "Color RGBWW Light",
|
||||
brightness: 255,
|
||||
rgbww_color: [30, 100, 255, 125, 10],
|
||||
min_mireds: 30,
|
||||
max_mireds: 150,
|
||||
supported_features: SUPPORT_EFFECT + SUPPORT_FLASH + SUPPORT_TRANSITION,
|
||||
supported_color_modes: [
|
||||
LightColorModes.BRIGHTNESS,
|
||||
LightColorModes.COLOR_TEMP,
|
||||
LightColorModes.RGBWW,
|
||||
],
|
||||
color_mode: LightColorModes.RGBWW,
|
||||
effect_list: ["random", "colorloop"],
|
||||
}),
|
||||
getEntity("light", "color_xy_light", "on", {
|
||||
friendly_name: "Color XY Light",
|
||||
brightness: 255,
|
||||
xy_color: [30, 100],
|
||||
rgb_color: [30, 100, 255],
|
||||
min_mireds: 30,
|
||||
max_mireds: 150,
|
||||
supported_features: SUPPORT_EFFECT + SUPPORT_FLASH + SUPPORT_TRANSITION,
|
||||
supported_color_modes: [
|
||||
LightColorModes.BRIGHTNESS,
|
||||
LightColorModes.COLOR_TEMP,
|
||||
LightColorModes.XY,
|
||||
],
|
||||
color_mode: LightColorModes.XY,
|
||||
hs_color: [30, 100],
|
||||
white_value: 36,
|
||||
supported_features:
|
||||
SUPPORT_BRIGHTNESS +
|
||||
SUPPORT_EFFECT +
|
||||
SUPPORT_FLASH +
|
||||
SUPPORT_COLOR +
|
||||
SUPPORT_TRANSITION +
|
||||
SUPPORT_WHITE_VALUE,
|
||||
effect_list: ["random", "colorloop"],
|
||||
}),
|
||||
];
|
||||
@@ -148,8 +81,4 @@ class DemoMoreInfoLight extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"demo-more-info-light": DemoMoreInfoLight;
|
||||
}
|
||||
}
|
||||
customElements.define("demo-more-info-light", DemoMoreInfoLight);
|
||||
|
@@ -1,6 +1,5 @@
|
||||
import "@material/mwc-button";
|
||||
import { html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement } from "lit/decorators";
|
||||
import { customElement, html, LitElement, TemplateResult } from "lit-element";
|
||||
import "../../../src/components/ha-card";
|
||||
import { ActionHandlerEvent } from "../../../src/data/lovelace";
|
||||
import { actionHandler } from "../../../src/panels/lovelace/common/directives/action-handler-directive";
|
||||
|
@@ -111,9 +111,29 @@ class HaGallery extends PolymerElement {
|
||||
</template>
|
||||
</ha-card>
|
||||
|
||||
<ha-card header="Other Demos">
|
||||
<div class="card-content intro"></div>
|
||||
<template is="dom-repeat" items="[[_restDemos]]">
|
||||
<ha-card header="More Info Demos">
|
||||
<div class="card-content intro">
|
||||
<p>
|
||||
More info screens show up when an entity is clicked.
|
||||
</p>
|
||||
</div>
|
||||
<template is="dom-repeat" items="[[_moreInfoDemos]]">
|
||||
<a href="#[[item]]">
|
||||
<paper-item>
|
||||
<paper-item-body>{{ item }}</paper-item-body>
|
||||
<ha-icon icon="hass:chevron-right"></ha-icon>
|
||||
</paper-item>
|
||||
</a>
|
||||
</template>
|
||||
</ha-card>
|
||||
|
||||
<ha-card header="Util Demos">
|
||||
<div class="card-content intro">
|
||||
<p>
|
||||
Test pages for our utility functions.
|
||||
</p>
|
||||
</div>
|
||||
<template is="dom-repeat" items="[[_utilDemos]]">
|
||||
<a href="#[[item]]">
|
||||
<paper-item>
|
||||
<paper-item-body>{{ item }}</paper-item-body>
|
||||
@@ -158,9 +178,13 @@ class HaGallery extends PolymerElement {
|
||||
type: Array,
|
||||
computed: "_computeLovelace(_demos)",
|
||||
},
|
||||
_restDemos: {
|
||||
_moreInfoDemos: {
|
||||
type: Array,
|
||||
computed: "_computeRest(_demos)",
|
||||
computed: "_computeMoreInfos(_demos)",
|
||||
},
|
||||
_utilDemos: {
|
||||
type: Array,
|
||||
computed: "_computeUtil(_demos)",
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -172,14 +196,6 @@ class HaGallery extends PolymerElement {
|
||||
this.$.notifications.showDialog({ message: ev.detail.message })
|
||||
);
|
||||
|
||||
this.addEventListener("alert-dismissed-clicked", () =>
|
||||
this.$.notifications.showDialog({ message: "Alert dismissed clicked" })
|
||||
);
|
||||
|
||||
this.addEventListener("alert-action-clicked", () =>
|
||||
this.$.notifications.showDialog({ message: "Alert action clicked" })
|
||||
);
|
||||
|
||||
this.addEventListener("hass-more-info", (ev) => {
|
||||
if (ev.detail.entityId) {
|
||||
this.$.notifications.showDialog({
|
||||
@@ -221,8 +237,12 @@ class HaGallery extends PolymerElement {
|
||||
return demos.filter((demo) => demo.includes("hui"));
|
||||
}
|
||||
|
||||
_computeRest(demos) {
|
||||
return demos.filter((demo) => !demo.includes("hui"));
|
||||
_computeMoreInfos(demos) {
|
||||
return demos.filter((demo) => demo.includes("more-info"));
|
||||
}
|
||||
|
||||
_computeUtil(demos) {
|
||||
return demos.filter((demo) => demo.includes("util"));
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,6 +1,12 @@
|
||||
import { mdiArrowUpBoldCircle, mdiPuzzle } from "@mdi/js";
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { property } from "lit/decorators";
|
||||
import {
|
||||
css,
|
||||
CSSResultArray,
|
||||
html,
|
||||
LitElement,
|
||||
property,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { atLeastVersion } from "../../../src/common/config/version";
|
||||
import { navigate } from "../../../src/common/navigate";
|
||||
@@ -9,7 +15,6 @@ import {
|
||||
HassioAddonInfo,
|
||||
HassioAddonRepository,
|
||||
} from "../../../src/data/hassio/addon";
|
||||
import { Supervisor } from "../../../src/data/supervisor/supervisor";
|
||||
import { HomeAssistant } from "../../../src/types";
|
||||
import "../components/hassio-card-content";
|
||||
import { filterAndSort } from "../components/hassio-filter-addons";
|
||||
@@ -18,8 +23,6 @@ import { hassioStyle } from "../resources/hassio-style";
|
||||
class HassioAddonRepositoryEl extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public supervisor!: Supervisor;
|
||||
|
||||
@property({ attribute: false }) public repo!: HassioAddonRepository;
|
||||
|
||||
@property({ attribute: false }) public addons!: HassioAddonInfo[];
|
||||
@@ -41,7 +44,9 @@ class HassioAddonRepositoryEl extends LitElement {
|
||||
const repo = this.repo;
|
||||
let _addons = this.addons;
|
||||
if (!this.hass.userData?.showAdvanced) {
|
||||
_addons = _addons.filter((addon) => !addon.advanced);
|
||||
_addons = _addons.filter((addon) => {
|
||||
return !addon.advanced;
|
||||
});
|
||||
}
|
||||
const addons = this._getAddons(_addons, this.filter);
|
||||
|
||||
@@ -49,18 +54,16 @@ class HassioAddonRepositoryEl extends LitElement {
|
||||
return html`
|
||||
<div class="content">
|
||||
<p class="description">
|
||||
${this.supervisor.localize(
|
||||
"store.no_results_found",
|
||||
"repository",
|
||||
repo.name
|
||||
)}
|
||||
No results found in "${repo.name}."
|
||||
</p>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
return html`
|
||||
<div class="content">
|
||||
<h1>${repo.name}</h1>
|
||||
<h1>
|
||||
${repo.name}
|
||||
</h1>
|
||||
<div class="card-group">
|
||||
${addons.map(
|
||||
(addon) => html`
|
||||
@@ -80,13 +83,11 @@ class HassioAddonRepositoryEl extends LitElement {
|
||||
: mdiPuzzle}
|
||||
.iconTitle=${addon.installed
|
||||
? addon.update_available
|
||||
? this.supervisor.localize(
|
||||
"common.new_version_available"
|
||||
)
|
||||
: this.supervisor.localize("addon.installed")
|
||||
? "New version available"
|
||||
: "Add-on is installed"
|
||||
: addon.available
|
||||
? this.supervisor.localize("addon.not_installed")
|
||||
: this.supervisor.localize("addon.not_available")}
|
||||
? "Add-on is not installed"
|
||||
: "Add-on is not available on your system"}
|
||||
.iconClass=${addon.installed
|
||||
? addon.update_available
|
||||
? "update"
|
||||
@@ -120,10 +121,10 @@ class HassioAddonRepositoryEl extends LitElement {
|
||||
}
|
||||
|
||||
private _addonTapped(ev) {
|
||||
navigate(`/hassio/addon/${ev.currentTarget.addon.slug}`);
|
||||
navigate(this, `/hassio/addon/${ev.currentTarget.addon.slug}`);
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
static get styles(): CSSResultArray {
|
||||
return [
|
||||
hassioStyle,
|
||||
css`
|
||||
|
@@ -4,27 +4,26 @@ import "@material/mwc-list/mwc-list-item";
|
||||
import { mdiDotsVertical } from "@mdi/js";
|
||||
import {
|
||||
css,
|
||||
CSSResultGroup,
|
||||
html,
|
||||
CSSResult,
|
||||
internalProperty,
|
||||
LitElement,
|
||||
property,
|
||||
PropertyValues,
|
||||
TemplateResult,
|
||||
} from "lit";
|
||||
import { property, state } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
} from "lit-element";
|
||||
import { html, TemplateResult } from "lit-html";
|
||||
import { atLeastVersion } from "../../../src/common/config/version";
|
||||
import { fireEvent } from "../../../src/common/dom/fire_event";
|
||||
import { navigate } from "../../../src/common/navigate";
|
||||
import "../../../src/common/search/search-input";
|
||||
import { extractSearchParam } from "../../../src/common/url/search-params";
|
||||
import "../../../src/components/ha-button-menu";
|
||||
import "../../../src/components/ha-svg-icon";
|
||||
import {
|
||||
fetchHassioAddonsInfo,
|
||||
HassioAddonInfo,
|
||||
HassioAddonRepository,
|
||||
reloadHassioAddons,
|
||||
} from "../../../src/data/hassio/addon";
|
||||
import { Supervisor } from "../../../src/data/supervisor/supervisor";
|
||||
import { extractApiErrorMessage } from "../../../src/data/hassio/common";
|
||||
import { fetchHassioSupervisorInfo } from "../../../src/data/hassio/supervisor";
|
||||
import "../../../src/layouts/hass-loading-screen";
|
||||
import "../../../src/layouts/hass-tabs-subpage";
|
||||
import { HomeAssistant, Route } from "../../../src/types";
|
||||
@@ -52,41 +51,58 @@ const sortRepos = (a: HassioAddonRepository, b: HassioAddonRepository) => {
|
||||
class HassioAddonStore extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public supervisor!: Supervisor;
|
||||
|
||||
@property({ type: Boolean }) public narrow!: boolean;
|
||||
|
||||
@property({ attribute: false }) public route!: Route;
|
||||
|
||||
@state() private _filter?: string;
|
||||
@property({ attribute: false }) private _addons?: HassioAddonInfo[];
|
||||
|
||||
@property({ attribute: false }) private _repos?: HassioAddonRepository[];
|
||||
|
||||
@internalProperty() private _filter?: string;
|
||||
|
||||
public async refreshData() {
|
||||
this._repos = undefined;
|
||||
this._addons = undefined;
|
||||
this._filter = undefined;
|
||||
await reloadHassioAddons(this.hass);
|
||||
await this._loadData();
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
let repos: TemplateResult[] = [];
|
||||
const repos: TemplateResult[] = [];
|
||||
|
||||
if (this.supervisor.addon.repositories) {
|
||||
repos = this.addonRepositories(
|
||||
this.supervisor.addon.repositories,
|
||||
this.supervisor.addon.addons,
|
||||
this._filter
|
||||
);
|
||||
if (this._repos) {
|
||||
for (const repo of this._repos) {
|
||||
const addons = this._addons!.filter(
|
||||
(addon) => addon.repository === repo.slug
|
||||
);
|
||||
|
||||
if (addons.length === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
repos.push(html`
|
||||
<hassio-addon-repository
|
||||
.hass=${this.hass}
|
||||
.repo=${repo}
|
||||
.addons=${addons}
|
||||
.filter=${this._filter!}
|
||||
></hassio-addon-repository>
|
||||
`);
|
||||
}
|
||||
}
|
||||
|
||||
return html`
|
||||
<hass-tabs-subpage
|
||||
.hass=${this.hass}
|
||||
.localizeFunc=${this.supervisor.localize}
|
||||
.narrow=${this.narrow}
|
||||
.route=${this.route}
|
||||
.tabs=${supervisorTabs}
|
||||
hassio
|
||||
main-page
|
||||
supervisor
|
||||
.tabs=${supervisorTabs}
|
||||
>
|
||||
<span slot="header"> ${this.supervisor.localize("panel.store")} </span>
|
||||
<span slot="header">Add-on Store</span>
|
||||
<ha-button-menu
|
||||
corner="BOTTOM_START"
|
||||
slot="toolbar-icon"
|
||||
@@ -96,15 +112,15 @@ class HassioAddonStore extends LitElement {
|
||||
<ha-svg-icon .path=${mdiDotsVertical}></ha-svg-icon>
|
||||
</mwc-icon-button>
|
||||
<mwc-list-item>
|
||||
${this.supervisor.localize("store.repositories")}
|
||||
Repositories
|
||||
</mwc-list-item>
|
||||
<mwc-list-item>
|
||||
${this.supervisor.localize("common.reload")}
|
||||
Reload
|
||||
</mwc-list-item>
|
||||
${this.hass.userData?.showAdvanced &&
|
||||
atLeastVersion(this.hass.config.version, 0, 117)
|
||||
? html`<mwc-list-item>
|
||||
${this.supervisor.localize("store.registries")}
|
||||
Registries
|
||||
</mwc-list-item>`
|
||||
: ""}
|
||||
</ha-button-menu>
|
||||
@@ -125,9 +141,11 @@ class HassioAddonStore extends LitElement {
|
||||
${!this.hass.userData?.showAdvanced
|
||||
? html`
|
||||
<div class="advanced">
|
||||
Missing add-ons? Enable advanced mode on
|
||||
<a href="/profile" target="_top">
|
||||
${this.supervisor.localize("store.missing_addons")}
|
||||
your profile page
|
||||
</a>
|
||||
.
|
||||
</div>
|
||||
`
|
||||
: ""}
|
||||
@@ -137,45 +155,14 @@ class HassioAddonStore extends LitElement {
|
||||
|
||||
protected firstUpdated(changedProps: PropertyValues) {
|
||||
super.firstUpdated(changedProps);
|
||||
const repositoryUrl = extractSearchParam("repository_url");
|
||||
navigate("/hassio/store", { replace: true });
|
||||
if (repositoryUrl) {
|
||||
this._manageRepositories(repositoryUrl);
|
||||
}
|
||||
|
||||
this.addEventListener("hass-api-called", (ev) => this.apiCalled(ev));
|
||||
this._loadData();
|
||||
}
|
||||
|
||||
private addonRepositories = memoizeOne(
|
||||
(
|
||||
repositories: HassioAddonRepository[],
|
||||
addons: HassioAddonInfo[],
|
||||
filter?: string
|
||||
) =>
|
||||
repositories.sort(sortRepos).map((repo) => {
|
||||
const filteredAddons = addons.filter(
|
||||
(addon) => addon.repository === repo.slug
|
||||
);
|
||||
|
||||
return filteredAddons.length !== 0
|
||||
? html`
|
||||
<hassio-addon-repository
|
||||
.hass=${this.hass}
|
||||
.repo=${repo}
|
||||
.addons=${filteredAddons}
|
||||
.filter=${filter!}
|
||||
.supervisor=${this.supervisor}
|
||||
></hassio-addon-repository>
|
||||
`
|
||||
: html``;
|
||||
})
|
||||
);
|
||||
|
||||
private _handleAction(ev: CustomEvent<ActionDetail>) {
|
||||
switch (ev.detail.index) {
|
||||
case 0:
|
||||
this._manageRepositoriesClicked();
|
||||
this._manageRepositories();
|
||||
break;
|
||||
case 1:
|
||||
this.refreshData();
|
||||
@@ -192,33 +179,37 @@ class HassioAddonStore extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
private _manageRepositoriesClicked() {
|
||||
this._manageRepositories();
|
||||
}
|
||||
|
||||
private async _manageRepositories(url?: string) {
|
||||
private async _manageRepositories() {
|
||||
showRepositoriesDialog(this, {
|
||||
supervisor: this.supervisor,
|
||||
url,
|
||||
repos: this._repos!,
|
||||
loadData: () => this._loadData(),
|
||||
});
|
||||
}
|
||||
|
||||
private async _manageRegistries() {
|
||||
showRegistriesDialog(this, { supervisor: this.supervisor });
|
||||
showRegistriesDialog(this);
|
||||
}
|
||||
|
||||
private async _loadData() {
|
||||
fireEvent(this, "supervisor-collection-refresh", { collection: "addon" });
|
||||
fireEvent(this, "supervisor-collection-refresh", {
|
||||
collection: "supervisor",
|
||||
});
|
||||
try {
|
||||
const [addonsInfo, supervisor] = await Promise.all([
|
||||
fetchHassioAddonsInfo(this.hass),
|
||||
fetchHassioSupervisorInfo(this.hass),
|
||||
]);
|
||||
fireEvent(this, "supervisor-update", { supervisor });
|
||||
this._repos = addonsInfo.repositories;
|
||||
this._repos.sort(sortRepos);
|
||||
this._addons = addonsInfo.addons;
|
||||
} catch (err) {
|
||||
alert(extractApiErrorMessage(err));
|
||||
}
|
||||
}
|
||||
|
||||
private async _filterChanged(e) {
|
||||
this._filter = e.detail.value;
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
static get styles(): CSSResult {
|
||||
return css`
|
||||
hassio-addon-repository {
|
||||
margin-top: 24px;
|
||||
|
@@ -4,16 +4,17 @@ import "@polymer/paper-item/paper-item";
|
||||
import "@polymer/paper-listbox/paper-listbox";
|
||||
import {
|
||||
css,
|
||||
CSSResultGroup,
|
||||
CSSResult,
|
||||
customElement,
|
||||
html,
|
||||
internalProperty,
|
||||
LitElement,
|
||||
property,
|
||||
PropertyValues,
|
||||
TemplateResult,
|
||||
} from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
} from "lit-element";
|
||||
import "web-animations-js/web-animations-next-lite.min";
|
||||
import "../../../../src/components/buttons/ha-progress-button";
|
||||
import "../../../../src/components/ha-alert";
|
||||
import "../../../../src/components/ha-card";
|
||||
import {
|
||||
HassioAddonDetails,
|
||||
@@ -24,7 +25,6 @@ import {
|
||||
fetchHassioHardwareAudio,
|
||||
HassioHardwareAudioDevice,
|
||||
} from "../../../../src/data/hassio/hardware";
|
||||
import { Supervisor } from "../../../../src/data/supervisor/supervisor";
|
||||
import { haStyle } from "../../../../src/resources/styles";
|
||||
import { HomeAssistant } from "../../../../src/types";
|
||||
import { suggestAddonRestart } from "../../dialogs/suggestAddonRestart";
|
||||
@@ -34,34 +34,26 @@ import { hassioStyle } from "../../resources/hassio-style";
|
||||
class HassioAddonAudio extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public supervisor!: Supervisor;
|
||||
|
||||
@property({ attribute: false }) public addon!: HassioAddonDetails;
|
||||
|
||||
@state() private _error?: string;
|
||||
@internalProperty() private _error?: string;
|
||||
|
||||
@state() private _inputDevices?: HassioHardwareAudioDevice[];
|
||||
@internalProperty() private _inputDevices?: HassioHardwareAudioDevice[];
|
||||
|
||||
@state() private _outputDevices?: HassioHardwareAudioDevice[];
|
||||
@internalProperty() private _outputDevices?: HassioHardwareAudioDevice[];
|
||||
|
||||
@state() private _selectedInput!: null | string;
|
||||
@internalProperty() private _selectedInput!: null | string;
|
||||
|
||||
@state() private _selectedOutput!: null | string;
|
||||
@internalProperty() private _selectedOutput!: null | string;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`
|
||||
<ha-card
|
||||
.header=${this.supervisor.localize("addon.configuration.audio.header")}
|
||||
>
|
||||
<ha-card header="Audio">
|
||||
<div class="card-content">
|
||||
${this._error
|
||||
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
|
||||
: ""}
|
||||
${this._error ? html` <div class="errors">${this._error}</div> ` : ""}
|
||||
|
||||
<paper-dropdown-menu
|
||||
.label=${this.supervisor.localize(
|
||||
"addon.configuration.audio.input"
|
||||
)}
|
||||
label="Input"
|
||||
@iron-select=${this._setInputDevice}
|
||||
>
|
||||
<paper-listbox
|
||||
@@ -70,19 +62,17 @@ class HassioAddonAudio extends LitElement {
|
||||
.selected=${this._selectedInput!}
|
||||
>
|
||||
${this._inputDevices &&
|
||||
this._inputDevices.map(
|
||||
(item) => html`
|
||||
<paper-item device=${item.device || ""}>
|
||||
${item.name}
|
||||
</paper-item>
|
||||
`
|
||||
)}
|
||||
this._inputDevices.map((item) => {
|
||||
return html`
|
||||
<paper-item device=${item.device || ""}
|
||||
>${item.name}</paper-item
|
||||
>
|
||||
`;
|
||||
})}
|
||||
</paper-listbox>
|
||||
</paper-dropdown-menu>
|
||||
<paper-dropdown-menu
|
||||
.label=${this.supervisor.localize(
|
||||
"addon.configuration.audio.output"
|
||||
)}
|
||||
label="Output"
|
||||
@iron-select=${this._setOutputDevice}
|
||||
>
|
||||
<paper-listbox
|
||||
@@ -91,26 +81,26 @@ class HassioAddonAudio extends LitElement {
|
||||
.selected=${this._selectedOutput!}
|
||||
>
|
||||
${this._outputDevices &&
|
||||
this._outputDevices.map(
|
||||
(item) => html`
|
||||
this._outputDevices.map((item) => {
|
||||
return html`
|
||||
<paper-item device=${item.device || ""}
|
||||
>${item.name}</paper-item
|
||||
>
|
||||
`
|
||||
)}
|
||||
`;
|
||||
})}
|
||||
</paper-listbox>
|
||||
</paper-dropdown-menu>
|
||||
</div>
|
||||
<div class="card-actions">
|
||||
<ha-progress-button @click=${this._saveSettings}>
|
||||
${this.supervisor.localize("common.save")}
|
||||
Save
|
||||
</ha-progress-button>
|
||||
</div>
|
||||
</ha-card>
|
||||
`;
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
static get styles(): CSSResult[] {
|
||||
return [
|
||||
haStyle,
|
||||
hassioStyle,
|
||||
@@ -120,6 +110,10 @@ class HassioAddonAudio extends LitElement {
|
||||
paper-dropdown-menu {
|
||||
display: block;
|
||||
}
|
||||
.errors {
|
||||
color: var(--error-color);
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
paper-item {
|
||||
width: 450px;
|
||||
}
|
||||
@@ -158,7 +152,7 @@ class HassioAddonAudio extends LitElement {
|
||||
|
||||
const noDevice: HassioHardwareAudioDevice = {
|
||||
device: "default",
|
||||
name: this.supervisor.localize("addon.configuration.audio.default"),
|
||||
name: "Default",
|
||||
};
|
||||
|
||||
try {
|
||||
@@ -195,7 +189,7 @@ class HassioAddonAudio extends LitElement {
|
||||
try {
|
||||
await setHassioAddonOption(this.hass, this.addon.slug, data);
|
||||
if (this.addon?.state === "started") {
|
||||
await suggestAddonRestart(this, this.hass, this.supervisor, this.addon);
|
||||
await suggestAddonRestart(this, this.hass, this.addon);
|
||||
}
|
||||
} catch {
|
||||
this._error = "Failed to set addon audio device";
|
||||
|
@@ -1,8 +1,14 @@
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import {
|
||||
css,
|
||||
CSSResult,
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
property,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import "../../../../src/components/ha-circular-progress";
|
||||
import { HassioAddonDetails } from "../../../../src/data/hassio/addon";
|
||||
import { Supervisor } from "../../../../src/data/supervisor/supervisor";
|
||||
import { haStyle } from "../../../../src/resources/styles";
|
||||
import { HomeAssistant } from "../../../../src/types";
|
||||
import { hassioStyle } from "../../resources/hassio-style";
|
||||
@@ -14,28 +20,26 @@ import "./hassio-addon-network";
|
||||
class HassioAddonConfigDashboard extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public supervisor!: Supervisor;
|
||||
|
||||
@property({ attribute: false }) public addon?: HassioAddonDetails;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this.addon) {
|
||||
return html`<ha-circular-progress active></ha-circular-progress>`;
|
||||
}
|
||||
const hasConfiguration =
|
||||
(this.addon.options && Object.keys(this.addon.options).length) ||
|
||||
(this.addon.schema && Object.keys(this.addon.schema).length);
|
||||
const hasOptions =
|
||||
this.addon.options && Object.keys(this.addon.options).length;
|
||||
const hasSchema =
|
||||
this.addon.schema && Object.keys(this.addon.schema).length;
|
||||
|
||||
return html`
|
||||
<div class="content">
|
||||
${hasConfiguration || this.addon.network || this.addon.audio
|
||||
${hasOptions || hasSchema || this.addon.network || this.addon.audio
|
||||
? html`
|
||||
${hasConfiguration
|
||||
${hasOptions || hasSchema
|
||||
? html`
|
||||
<hassio-addon-config
|
||||
.hass=${this.hass}
|
||||
.addon=${this.addon}
|
||||
.supervisor=${this.supervisor}
|
||||
></hassio-addon-config>
|
||||
`
|
||||
: ""}
|
||||
@@ -44,7 +48,6 @@ class HassioAddonConfigDashboard extends LitElement {
|
||||
<hassio-addon-network
|
||||
.hass=${this.hass}
|
||||
.addon=${this.addon}
|
||||
.supervisor=${this.supervisor}
|
||||
></hassio-addon-network>
|
||||
`
|
||||
: ""}
|
||||
@@ -53,17 +56,16 @@ class HassioAddonConfigDashboard extends LitElement {
|
||||
<hassio-addon-audio
|
||||
.hass=${this.hass}
|
||||
.addon=${this.addon}
|
||||
.supervisor=${this.supervisor}
|
||||
></hassio-addon-audio>
|
||||
`
|
||||
: ""}
|
||||
`
|
||||
: this.supervisor.localize("addon.configuration.no_configuration")}
|
||||
: "This add-on does not expose configuration for you to mess with.... 👋"}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
static get styles(): CSSResult[] {
|
||||
return [
|
||||
haStyle,
|
||||
hassioStyle,
|
||||
|
@@ -2,36 +2,32 @@ import "@material/mwc-button";
|
||||
import { ActionDetail } from "@material/mwc-list";
|
||||
import "@material/mwc-list/mwc-list-item";
|
||||
import { mdiDotsVertical } from "@mdi/js";
|
||||
import { DEFAULT_SCHEMA, Type } from "js-yaml";
|
||||
import "@polymer/iron-autogrow-textarea/iron-autogrow-textarea";
|
||||
import {
|
||||
css,
|
||||
CSSResultGroup,
|
||||
CSSResult,
|
||||
customElement,
|
||||
html,
|
||||
internalProperty,
|
||||
LitElement,
|
||||
property,
|
||||
PropertyValues,
|
||||
query,
|
||||
TemplateResult,
|
||||
} from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
} from "lit-element";
|
||||
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
||||
import "../../../../src/components/buttons/ha-progress-button";
|
||||
import "../../../../src/components/ha-button-menu";
|
||||
import "../../../../src/components/ha-card";
|
||||
import "../../../../src/components/ha-alert";
|
||||
import "../../../../src/components/ha-form/ha-form";
|
||||
import type { HaFormSchema } from "../../../../src/components/ha-form/ha-form";
|
||||
import "../../../../src/components/ha-formfield";
|
||||
import "../../../../src/components/ha-switch";
|
||||
import "../../../../src/components/ha-yaml-editor";
|
||||
import type { HaYamlEditor } from "../../../../src/components/ha-yaml-editor";
|
||||
import {
|
||||
HassioAddonDetails,
|
||||
HassioAddonSetOptionParams,
|
||||
setHassioAddonOption,
|
||||
validateHassioAddonOption,
|
||||
} from "../../../../src/data/hassio/addon";
|
||||
import { extractApiErrorMessage } from "../../../../src/data/hassio/common";
|
||||
import { Supervisor } from "../../../../src/data/supervisor/supervisor";
|
||||
import { showConfirmationDialog } from "../../../../src/dialogs/generic/show-dialog-box";
|
||||
import { haStyle } from "../../../../src/resources/styles";
|
||||
import type { HomeAssistant } from "../../../../src/types";
|
||||
@@ -40,137 +36,70 @@ import { hassioStyle } from "../../resources/hassio-style";
|
||||
|
||||
const SUPPORTED_UI_TYPES = ["string", "select", "boolean", "integer", "float"];
|
||||
|
||||
const ADDON_YAML_SCHEMA = DEFAULT_SCHEMA.extend([
|
||||
new Type("!secret", {
|
||||
kind: "scalar",
|
||||
construct: (data) => `!secret ${data}`,
|
||||
}),
|
||||
]);
|
||||
|
||||
@customElement("hassio-addon-config")
|
||||
class HassioAddonConfig extends LitElement {
|
||||
@property({ attribute: false }) public addon!: HassioAddonDetails;
|
||||
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public supervisor!: Supervisor;
|
||||
|
||||
@property({ type: Boolean }) private _configHasChanged = false;
|
||||
|
||||
@property({ type: Boolean }) private _valid = true;
|
||||
|
||||
@state() private _canShowSchema = false;
|
||||
@internalProperty() private _canShowSchema = false;
|
||||
|
||||
@state() private _showOptional = false;
|
||||
@internalProperty() private _error?: string;
|
||||
|
||||
@state() private _error?: string;
|
||||
@internalProperty() private _options?: Record<string, unknown>;
|
||||
|
||||
@state() private _options?: Record<string, unknown>;
|
||||
|
||||
@state() private _yamlMode = false;
|
||||
@internalProperty() private _yamlMode = false;
|
||||
|
||||
@query("ha-yaml-editor") private _editor?: HaYamlEditor;
|
||||
|
||||
public computeLabel = (entry: HaFormSchema): string =>
|
||||
this.addon.translations[this.hass.language]?.configuration?.[entry.name]
|
||||
?.name ||
|
||||
this.addon.translations.en?.configuration?.[entry.name].name ||
|
||||
entry.name;
|
||||
|
||||
private _filteredShchema = memoizeOne(
|
||||
(options: Record<string, unknown>, schema: HaFormSchema[]) =>
|
||||
schema.filter((entry) => entry.name in options || entry.required)
|
||||
);
|
||||
|
||||
protected render(): TemplateResult {
|
||||
const showForm =
|
||||
!this._yamlMode && this._canShowSchema && this.addon.schema;
|
||||
const hasHiddenOptions =
|
||||
showForm &&
|
||||
JSON.stringify(this.addon.schema) !==
|
||||
JSON.stringify(
|
||||
this._filteredShchema(this.addon.options, this.addon.schema!)
|
||||
);
|
||||
return html`
|
||||
<h1>${this.addon.name}</h1>
|
||||
<ha-card>
|
||||
<div class="header">
|
||||
<h2>
|
||||
${this.supervisor.localize("addon.configuration.options.header")}
|
||||
</h2>
|
||||
<h2>Configuration</h2>
|
||||
<div class="card-menu">
|
||||
<ha-button-menu corner="BOTTOM_START" @action=${this._handleAction}>
|
||||
<mwc-icon-button slot="trigger">
|
||||
<ha-svg-icon .path=${mdiDotsVertical}></ha-svg-icon>
|
||||
</mwc-icon-button>
|
||||
<mwc-list-item .disabled=${!this._canShowSchema}>
|
||||
${this._yamlMode
|
||||
? this.supervisor.localize(
|
||||
"addon.configuration.options.edit_in_ui"
|
||||
)
|
||||
: this.supervisor.localize(
|
||||
"addon.configuration.options.edit_in_yaml"
|
||||
)}
|
||||
${this._yamlMode ? "Edit in UI" : "Edit in YAML"}
|
||||
</mwc-list-item>
|
||||
<mwc-list-item class="warning">
|
||||
${this.supervisor.localize("common.reset_defaults")}
|
||||
Reset to defaults
|
||||
</mwc-list-item>
|
||||
</ha-button-menu>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-content">
|
||||
${showForm
|
||||
${!this._yamlMode && this._canShowSchema && this.addon.schema
|
||||
? html`<ha-form
|
||||
.data=${this._options!}
|
||||
@value-changed=${this._configChanged}
|
||||
.computeLabel=${this.computeLabel}
|
||||
.schema=${this._showOptional
|
||||
? this.addon.schema!
|
||||
: this._filteredShchema(
|
||||
this.addon.options,
|
||||
this.addon.schema!
|
||||
)}
|
||||
.schema=${this.addon.schema}
|
||||
></ha-form>`
|
||||
: html` <ha-yaml-editor
|
||||
@value-changed=${this._configChanged}
|
||||
.yamlSchema=${ADDON_YAML_SCHEMA}
|
||||
></ha-yaml-editor>`}
|
||||
${this._error
|
||||
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
|
||||
: ""}
|
||||
${this._error ? html` <div class="errors">${this._error}</div> ` : ""}
|
||||
${!this._yamlMode ||
|
||||
(this._canShowSchema && this.addon.schema) ||
|
||||
this._valid
|
||||
? ""
|
||||
: html`
|
||||
<ha-alert alert-type="error">
|
||||
${this.supervisor.localize(
|
||||
"addon.configuration.options.invalid_yaml"
|
||||
)}
|
||||
</ha-alert>
|
||||
`}
|
||||
: html` <div class="errors">Invalid YAML</div> `}
|
||||
</div>
|
||||
${hasHiddenOptions
|
||||
? html`<ha-formfield
|
||||
class="show-additional"
|
||||
.label=${this.supervisor.localize(
|
||||
"addon.configuration.options.show_unused_optional"
|
||||
)}
|
||||
>
|
||||
<ha-switch
|
||||
@change=${this._toggleOptional}
|
||||
.checked=${this._showOptional}
|
||||
>
|
||||
</ha-switch>
|
||||
</ha-formfield>`
|
||||
: ""}
|
||||
<div class="card-actions right">
|
||||
<div class="card-actions">
|
||||
<ha-progress-button
|
||||
@click=${this._saveTapped}
|
||||
.disabled=${!this._configHasChanged || !this._valid}
|
||||
>
|
||||
${this.supervisor.localize("common.save")}
|
||||
Save
|
||||
</ha-progress-button>
|
||||
</div>
|
||||
</ha-card>
|
||||
@@ -179,7 +108,7 @@ class HassioAddonConfig extends LitElement {
|
||||
|
||||
protected firstUpdated(changedProps) {
|
||||
super.firstUpdated(changedProps);
|
||||
this._canShowSchema = !this.addon.schema!.find(
|
||||
this._canShowSchema = !this.addon.schema.find(
|
||||
// @ts-ignore
|
||||
(entry) => !SUPPORTED_UI_TYPES.includes(entry.type) || entry.multiple
|
||||
);
|
||||
@@ -215,19 +144,17 @@ class HassioAddonConfig extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
private _toggleOptional() {
|
||||
this._showOptional = !this._showOptional;
|
||||
}
|
||||
|
||||
private _configChanged(ev): void {
|
||||
if (this.addon.schema && this._canShowSchema && !this._yamlMode) {
|
||||
this._valid = true;
|
||||
this._configHasChanged = true;
|
||||
this._options! = ev.detail.value;
|
||||
} else {
|
||||
this._configHasChanged = true;
|
||||
this._valid = ev.detail.isValid;
|
||||
}
|
||||
if (this._valid) {
|
||||
this._options! = ev.detail.value;
|
||||
}
|
||||
}
|
||||
|
||||
private async _resetTapped(ev: CustomEvent): Promise<void> {
|
||||
@@ -235,10 +162,10 @@ class HassioAddonConfig extends LitElement {
|
||||
button.progress = true;
|
||||
|
||||
const confirmed = await showConfirmationDialog(this, {
|
||||
title: this.supervisor.localize("confirm.reset_options.title"),
|
||||
text: this.supervisor.localize("confirm.reset_options.text"),
|
||||
confirmText: this.supervisor.localize("common.reset_options"),
|
||||
dismissText: this.supervisor.localize("common.cancel"),
|
||||
title: this.addon.name,
|
||||
text: "Are you sure you want to reset all your options?",
|
||||
confirmText: "reset options",
|
||||
dismissText: "no",
|
||||
});
|
||||
|
||||
if (!confirmed) {
|
||||
@@ -260,59 +187,42 @@ class HassioAddonConfig extends LitElement {
|
||||
};
|
||||
fireEvent(this, "hass-api-called", eventdata);
|
||||
} catch (err) {
|
||||
this._error = this.supervisor.localize(
|
||||
"addon.common.update_available",
|
||||
"error",
|
||||
extractApiErrorMessage(err)
|
||||
);
|
||||
this._error = `Failed to reset addon configuration, ${extractApiErrorMessage(
|
||||
err
|
||||
)}`;
|
||||
}
|
||||
button.progress = false;
|
||||
}
|
||||
|
||||
private async _saveTapped(ev: CustomEvent): Promise<void> {
|
||||
const button = ev.currentTarget as any;
|
||||
const options: Record<string, unknown> = this._yamlMode
|
||||
? this._editor?.value
|
||||
: this._options;
|
||||
const eventdata = {
|
||||
success: true,
|
||||
response: undefined,
|
||||
path: "options",
|
||||
};
|
||||
button.progress = true;
|
||||
|
||||
this._error = undefined;
|
||||
|
||||
try {
|
||||
const validation = await validateHassioAddonOption(
|
||||
this.hass,
|
||||
this.addon.slug,
|
||||
options
|
||||
);
|
||||
if (!validation.valid) {
|
||||
throw Error(validation.message);
|
||||
}
|
||||
await setHassioAddonOption(this.hass, this.addon.slug, {
|
||||
options,
|
||||
options: this._options!,
|
||||
});
|
||||
|
||||
this._configHasChanged = false;
|
||||
const eventdata = {
|
||||
success: true,
|
||||
response: undefined,
|
||||
path: "options",
|
||||
};
|
||||
fireEvent(this, "hass-api-called", eventdata);
|
||||
if (this.addon?.state === "started") {
|
||||
await suggestAddonRestart(this, this.hass, this.supervisor, this.addon);
|
||||
await suggestAddonRestart(this, this.hass, this.addon);
|
||||
}
|
||||
} catch (err) {
|
||||
this._error = this.supervisor.localize(
|
||||
"addon.failed_to_save",
|
||||
"error",
|
||||
extractApiErrorMessage(err)
|
||||
);
|
||||
eventdata.success = false;
|
||||
this._error = `Failed to save addon configuration, ${extractApiErrorMessage(
|
||||
err
|
||||
)}`;
|
||||
}
|
||||
button.progress = false;
|
||||
fireEvent(this, "hass-api-called", eventdata);
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
static get styles(): CSSResult[] {
|
||||
return [
|
||||
haStyle,
|
||||
hassioStyle,
|
||||
@@ -327,7 +237,17 @@ class HassioAddonConfig extends LitElement {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.errors {
|
||||
color: var(--error-color);
|
||||
margin-top: 16px;
|
||||
}
|
||||
iron-autogrow-textarea {
|
||||
width: 100%;
|
||||
font-family: var(--code-font-family, monospace);
|
||||
}
|
||||
.syntaxerror {
|
||||
color: var(--error-color);
|
||||
}
|
||||
.card-menu {
|
||||
float: right;
|
||||
z-index: 3;
|
||||
@@ -351,13 +271,6 @@ class HassioAddonConfig extends LitElement {
|
||||
margin-block: 0px;
|
||||
font-weight: normal;
|
||||
}
|
||||
.card-actions.right {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.show-additional {
|
||||
padding: 16px;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user