Compare commits

..

16 Commits

Author SHA1 Message Date
Donnie
f138ec8ce0 Update src/components/ha-sidebar.ts
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2021-04-02 12:28:03 -07:00
Donnie
b6cf935953 Consolidate ha-sortable-style and remove rtl styling 2021-04-02 12:28:02 -07:00
Donnie
dca9bec03c Fix typescript error from trying to override listitem styles into an array 2021-04-02 12:28:02 -07:00
Donnie
aa6915cb90 Fix issue with being unable to drag sidebar items when clicking on text or icon 2021-04-02 12:28:01 -07:00
Donnie
28405f6ddf Allow keyboard nav to cycle to top and bottom of full list. Fix font weight and collapse alignment. 2021-04-02 12:28:01 -07:00
Donnie
366bea4344 Fix down arrow regression 2021-04-02 12:28:00 -07:00
Bram Kragten
173c6bc4c3 Fix 2021-04-02 12:28:00 -07:00
Bram Kragten
91e00815df Some styling tweaks 2021-04-02 12:27:59 -07:00
Donnie
29d9219481 Fix hidden items spacer separation 2021-04-02 12:27:59 -07:00
Donnie
1369d0f36d Fix rtl and remove some unused CSS 2021-04-02 12:27:58 -07:00
Donnie
4a38f7a9e0 Fix padding from left edge to list item graphic 2021-04-02 12:27:58 -07:00
Donnie
12aab855d7 Fix notification badge and user avatar circle 2021-04-02 12:27:57 -07:00
Donnie
f24b1ea844 Remove experimental test code 2021-04-02 12:27:57 -07:00
Donnie
3345b52da4 Fix divider issue, restore arrow down/up behavior, enter key clicks item 2021-04-02 12:27:56 -07:00
Donnie
6ec35380ac Pull divider and panel list out into their own components 2021-04-02 12:27:55 -07:00
Donnie
24f334bfe2 Refactor sidebar to use mwc-list 2021-04-02 12:27:55 -07:00
1056 changed files with 35094 additions and 61940 deletions

View File

@@ -4,7 +4,8 @@
"plugin:@typescript-eslint/recommended", "plugin:@typescript-eslint/recommended",
"plugin:wc/recommended", "plugin:wc/recommended",
"plugin:lit/recommended", "plugin:lit/recommended",
"prettier" "prettier",
"prettier/@typescript-eslint"
], ],
"parser": "@typescript-eslint/parser", "parser": "@typescript-eslint/parser",
"parserOptions": { "parserOptions": {
@@ -28,89 +29,64 @@
"__BUILD__": false, "__BUILD__": false,
"__VERSION__": false, "__VERSION__": false,
"__STATIC_PATH__": false, "__STATIC_PATH__": false,
"Polymer": true "Polymer": true,
"webkitSpeechRecognition": false,
"ResizeObserver": false
}, },
"env": { "env": {
"browser": true, "browser": true,
"es6": true "es6": true
}, },
"rules": { "rules": {
"class-methods-use-this": "off", "class-methods-use-this": 0,
"new-cap": "off", "new-cap": 0,
"prefer-template": "off", "prefer-template": 0,
"object-shorthand": "off", "object-shorthand": 0,
"func-names": "off", "func-names": 0,
"no-underscore-dangle": "off", "prefer-arrow-callback": 0,
"strict": "off", "no-underscore-dangle": 0,
"no-plusplus": "off", "strict": 0,
"no-bitwise": "error", "prefer-spread": 0,
"comma-dangle": "off", "no-plusplus": 0,
"vars-on-top": "off", "no-bitwise": 2,
"no-continue": "off", "comma-dangle": 0,
"no-param-reassign": "off", "vars-on-top": 0,
"no-multi-assign": "off", "no-continue": 0,
"no-console": "error", "no-param-reassign": 0,
"radix": "off", "no-multi-assign": 0,
"no-alert": "off", "no-console": 2,
"no-nested-ternary": "off", "radix": 0,
"prefer-destructuring": "off", "no-alert": 0,
"no-return-await": 0,
"no-nested-ternary": 0,
"prefer-destructuring": 0,
"no-restricted-globals": [2, "event"], "no-restricted-globals": [2, "event"],
"prefer-promise-reject-errors": "off", "prefer-promise-reject-errors": 0,
"import/prefer-default-export": "off", "import/order": 0,
"import/no-default-export": "off", "import/prefer-default-export": 0,
"import/no-unresolved": "off", "import/no-unresolved": 0,
"import/no-cycle": "off", "import/no-cycle": 0,
"import/extensions": [ "import/extensions": [
"error", 2,
"ignorePackages", "ignorePackages",
{ "ts": "never", "js": "never" } { "ts": "never", "js": "never" }
], ],
"no-restricted-syntax": ["error", "LabeledStatement", "WithStatement"], "no-restricted-syntax": ["error", "LabeledStatement", "WithStatement"],
"object-curly-newline": "off", "object-curly-newline": 0,
"default-case": "off", "default-case": 0,
"wc/no-self-class": "off", "wc/no-self-class": 0,
"no-shadow": "off", "no-shadow": 0,
"@typescript-eslint/camelcase": "off", "@typescript-eslint/camelcase": 0,
"@typescript-eslint/ban-ts-comment": "off", "@typescript-eslint/ban-ts-comment": 0,
"@typescript-eslint/no-use-before-define": "off", "@typescript-eslint/no-use-before-define": 0,
"@typescript-eslint/no-non-null-assertion": "off", "@typescript-eslint/no-non-null-assertion": 0,
"@typescript-eslint/no-explicit-any": "off", "@typescript-eslint/no-explicit-any": 0,
"@typescript-eslint/explicit-function-return-type": "off", "@typescript-eslint/no-unused-vars": 0,
"@typescript-eslint/explicit-module-boundary-types": "off", "@typescript-eslint/explicit-function-return-type": 0,
"@typescript-eslint/explicit-module-boundary-types": 0,
"@typescript-eslint/no-shadow": ["error"], "@typescript-eslint/no-shadow": ["error"],
"@typescript-eslint/naming-convention": [ "lit/attribute-value-entities": 0
"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"
}, },
"plugins": ["disable", "unused-imports"], "plugins": ["disable", "import", "lit", "prettier", "@typescript-eslint"],
"processor": "disable/disable" "processor": "disable/disable"
} }

View File

@@ -1,6 +1,8 @@
name: Report a bug with the UI, Frontend or Lovelace name: Report a bug with the UI, Frontend or Lovelace
description: Report an issue related to the Home Assistant frontend. about: Report an issue related to the Home Assistant frontend.
labels: bug labels: bug
title: ""
issue_body: true
body: body:
- type: markdown - type: markdown
attributes: attributes:
@@ -95,7 +97,11 @@ body:
If your issue is about how an entity is shown in the UI, please add the 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 state and attributes for all situations. You can find this information
at Developer Tools -> States. at Developer Tools -> States.
render: txt value: |
```yaml
# Paste your state here.
```
- type: textarea - type: textarea
attributes: attributes:
label: Problem-relevant frontend configuration label: Problem-relevant frontend configuration
@@ -104,18 +110,29 @@ body:
configuration of the used cards. Fill this out even if it seems configuration of the used cards. Fill this out even if it seems
unimportant to you. Please be sure to remove personal information like unimportant to you. Please be sure to remove personal information like
passwords, private URLs and other credentials. passwords, private URLs and other credentials.
render: yaml value: |
```yaml
# Paste your YAML here.
```
- type: textarea - type: textarea
attributes: attributes:
label: Javascript errors shown in your browser console/inspector label: Javascript errors shown in your browser console/inspector
description: > description: >
If you come across any Javascript or other error logs, e.g., in your If you come across any Javascript or other error logs, e.g., in your
browser console/inspector please provide them. browser console/inspector please provide them.
render: txt value: |
- type: textarea ```txt
# Paste your logs here.
```
- type: markdown
attributes: attributes:
label: Additional information value: |
description: > ## Additional information
- type: markdown
attributes:
value: |
If you have any additional information for us, use the field below. If you have any additional information for us, use the field below.
Please note, you can attach screenshots or screen recordings here, by Please note, you can attach screenshots or screen recordings here,
dragging and dropping files in the field below. by dragging and dropping files in the field below.

View File

@@ -10,21 +10,26 @@ on:
- dev - dev
- master - master
env:
NODE_VERSION: 14
NODE_OPTIONS: --max_old_space_size=4096
jobs: jobs:
lint: lint:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Check out files from GitHub - name: Check out files from GitHub
uses: actions/checkout@v2 uses: actions/checkout@v2
- name: Set up Node ${{ env.NODE_VERSION }} - name: Setting up Node.js
uses: actions/setup-node@v2 uses: actions/setup-node@v1
with: with:
node-version: ${{ env.NODE_VERSION }} node-version: 12.x
cache: yarn - 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 - name: Install dependencies
run: yarn install run: yarn install
env: env:
@@ -32,40 +37,54 @@ jobs:
- name: Build resources - name: Build resources
run: ./node_modules/.bin/gulp gen-icons-json build-translations gather-gallery-demos run: ./node_modules/.bin/gulp gen-icons-json build-translations gather-gallery-demos
- name: Run eslint - name: Run eslint
run: yarn run lint:eslint run: ./node_modules/.bin/eslint '{**/src,src}/**/*.{js,ts,html}' --ignore-path .gitignore
- name: Run tsc - name: Run tsc
run: yarn run lint:types run: ./node_modules/.bin/tsc
- name: Run prettier
run: yarn run lint:prettier
- name: Check for duplicate dependencies
run: yarn dedupe --check
test: test:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Check out files from GitHub - name: Check out files from GitHub
uses: actions/checkout@v2 uses: actions/checkout@v2
- name: Set up Node ${{ env.NODE_VERSION }} - name: Setting up Node.js
uses: actions/setup-node@v2 uses: actions/setup-node@v1
with: with:
node-version: ${{ env.NODE_VERSION }} node-version: 12.x
cache: yarn - 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 - name: Install dependencies
run: yarn install run: yarn install
env: env:
CI: true CI: true
- name: Run Mocha - name: Run Mocha
run: yarn run mocha run: npm run mocha
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: [lint, test] needs: [lint, test]
steps: steps:
- name: Check out files from GitHub - name: Check out files from GitHub
uses: actions/checkout@v2 uses: actions/checkout@v2
- name: Set up Node ${{ env.NODE_VERSION }} - name: Setting up Node.js
uses: actions/setup-node@v2 uses: actions/setup-node@v1
with: with:
node-version: ${{ env.NODE_VERSION }} node-version: 12.x
cache: yarn - 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 - name: Install dependencies
run: yarn install run: yarn install
env: env:
@@ -80,11 +99,20 @@ jobs:
steps: steps:
- name: Check out files from GitHub - name: Check out files from GitHub
uses: actions/checkout@v2 uses: actions/checkout@v2
- name: Set up Node ${{ env.NODE_VERSION }} - name: Setting up Node.js
uses: actions/setup-node@v2 uses: actions/setup-node@v1
with: with:
node-version: ${{ env.NODE_VERSION }} node-version: 12.x
cache: yarn - 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 - name: Install dependencies
run: yarn install run: yarn install
env: env:

View File

@@ -4,22 +4,26 @@ on:
push: push:
branches: branches:
- dev - dev
env:
NODE_VERSION: 14
NODE_OPTIONS: --max_old_space_size=4096
jobs: jobs:
deploy: deploy:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Check out files from GitHub - name: Check out files from GitHub
uses: actions/checkout@v2 uses: actions/checkout@v2
- name: Set up Node ${{ env.NODE_VERSION }} - name: Setting up Node.js
uses: actions/setup-node@v2 uses: actions/setup-node@v1
with: with:
node-version: ${{ env.NODE_VERSION }} node-version: 12.x
cache: yarn - 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 - name: Install dependencies
run: yarn install run: yarn install
env: env:

View File

@@ -6,9 +6,9 @@ on:
- published - published
env: env:
PYTHON_VERSION: 3.8 WHEELS_TAG: 3.7-alpine3.11
NODE_VERSION: 14 PYTHON_VERSION: 3.7
NODE_OPTIONS: --max_old_space_size=4096 NODE_VERSION: 12.1
jobs: jobs:
release: release:
@@ -30,15 +30,7 @@ jobs:
uses: actions/setup-node@v2 uses: actions/setup-node@v2
with: with:
node-version: ${{ env.NODE_VERSION }} 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 - name: Build and release package
run: | run: |
python3 -m pip install twine python3 -m pip install twine
@@ -72,9 +64,6 @@ jobs:
strategy: strategy:
matrix: matrix:
arch: ["aarch64", "armhf", "armv7", "amd64", "i386"] arch: ["aarch64", "armhf", "armv7", "amd64", "i386"]
tag:
- "3.8-alpine3.12"
- "3.9-alpine3.13"
steps: steps:
- name: Download requirements.txt - name: Download requirements.txt
uses: actions/download-artifact@v2 uses: actions/download-artifact@v2
@@ -84,7 +73,7 @@ jobs:
- name: Build wheels - name: Build wheels
uses: home-assistant/wheels@master uses: home-assistant/wheels@master
with: with:
tag: ${{ matrix.tag }} tag: ${{ env.WHEELS_TAG }}
arch: ${{ matrix.arch }} arch: ${{ matrix.arch }}
wheels-host: ${{ secrets.WHEELS_HOST }} wheels-host: ${{ secrets.WHEELS_HOST }}
wheels-key: ${{ secrets.WHEELS_KEY }} wheels-key: ${{ secrets.WHEELS_KEY }}

View File

@@ -1,6 +1,8 @@
name: Translations name: Translations
on: on:
schedule:
- cron: "30 0 * * *"
push: push:
branches: branches:
- dev - dev
@@ -8,7 +10,7 @@ on:
- src/translations/en.json - src/translations/en.json
env: env:
NODE_VERSION: 14 NODE_VERSION: 12
jobs: jobs:
upload: upload:
@@ -18,8 +20,46 @@ jobs:
- name: Checkout the repository - name: Checkout the repository
uses: actions/checkout@v2 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 - name: Upload Translations
run: | run: |
export LOKALISE_TOKEN="${{ secrets.LOKALISE_TOKEN }}" export LOKALISE_TOKEN="${{ secrets.LOKALISE_TOKEN }}"
./script/translations_upload_base ./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
View File

@@ -1,23 +1,10 @@
.DS_Store
.reify-cache
# build
build build
build-translations/* build-translations/*
hass_frontend/*
dist
# yarn
.yarn/*
!.yarn/patches
!.yarn/releases
!.yarn/plugins
!.yarn/sdks
!.yarn/versions
.pnp.*
node_modules/* node_modules/*
yarn-error.log
npm-debug.log npm-debug.log
.DS_Store
hass_frontend/*
.reify-cache
# Python stuff # Python stuff
*.py[cod] *.py[cod]
@@ -27,8 +14,11 @@ npm-debug.log
# venv stuff # venv stuff
pyvenv.cfg pyvenv.cfg
pip-selfcheck.json pip-selfcheck.json
venv/* venv
.venv .venv
lib
bin
dist
# vscode # vscode
.vscode/* .vscode/*
@@ -41,8 +31,9 @@ src/cast/dev_const.ts
# Secrets # Secrets
.lokalise_token .lokalise_token
yarn-error.log
# asdf #asdf
.tool-versions .tool-versions
# Home Assistant config # Home Assistant config

View File

@@ -1,4 +0,0 @@
module.exports = {
require: "test-mocha/testconf.js",
timeout: 10000,
};

2
.nvmrc
View File

@@ -1 +1 @@
14 12.1

File diff suppressed because one or more lines are too long

View File

@@ -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);
}

View File

@@ -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

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -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

View File

@@ -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,
},
};
};

View File

@@ -1,10 +1,11 @@
/* eslint-disable @typescript-eslint/no-var-requires */
const path = require("path"); const path = require("path");
const env = require("./env.js"); const env = require("./env.js");
const paths = require("./paths.js"); const paths = require("./paths.js");
// Files from NPM Packages that should not be imported // Files from NPM Packages that should not be imported
module.exports.ignorePackages = ({ latestBuild }) => [ 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 // Part of yaml.js and only used for !!js functions that we don't use
require.resolve("esprima"), require.resolve("esprima"),
]; ];
@@ -18,8 +19,7 @@ module.exports.emptyPackages = ({ latestBuild }) =>
require.resolve("@polymer/paper-styles/default-theme.js"), require.resolve("@polymer/paper-styles/default-theme.js"),
// Loads stuff from a CDN // Loads stuff from a CDN
require.resolve("@polymer/font-roboto/roboto.js"), require.resolve("@polymer/font-roboto/roboto.js"),
require.resolve("@vaadin/vaadin-material-styles/typography.js"), require.resolve("@vaadin/vaadin-material-styles/font-roboto.js"),
require.resolve("@vaadin/vaadin-material-styles/font-icons.js"),
// Compatibility not needed for latest builds // Compatibility not needed for latest builds
latestBuild && latestBuild &&
// wrapped in require.resolve so it blows up if file no longer exists // 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 }) => ({ module.exports.babelOptions = ({ latestBuild }) => ({
babelrc: false, babelrc: false,
compact: false,
presets: [ presets: [
!latestBuild && [ !latestBuild && [
"@babel/preset-env", require("@babel/preset-env").default,
{ {
useBuiltIns: "entry", useBuiltIns: "entry",
corejs: "3.15", corejs: "3.6",
bugfixes: true,
}, },
], ],
"@babel/preset-typescript", require("@babel/preset-typescript").default,
].filter(Boolean), ].filter(Boolean),
plugins: [ 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}) // Part of ES2018. Converts {...a, b: 2} to Object.assign({}, a, {b: 2})
!latestBuild && [ !latestBuild && [
"@babel/plugin-proposal-object-rest-spread", "@babel/plugin-proposal-object-rest-spread",
@@ -84,18 +72,23 @@ module.exports.babelOptions = ({ latestBuild }) => ({
"@babel/plugin-syntax-dynamic-import", "@babel/plugin-syntax-dynamic-import",
"@babel/plugin-proposal-optional-chaining", "@babel/plugin-proposal-optional-chaining",
"@babel/plugin-proposal-nullish-coalescing-operator", "@babel/plugin-proposal-nullish-coalescing-operator",
["@babel/plugin-proposal-decorators", { decoratorsBeforeExport: true }], [
["@babel/plugin-proposal-private-methods", { loose: true }], require("@babel/plugin-proposal-decorators").default,
["@babel/plugin-proposal-private-property-in-object", { loose: true }], { decoratorsBeforeExport: true },
["@babel/plugin-proposal-class-properties", { loose: true }], ],
[
require("@babel/plugin-proposal-class-properties").default,
{ loose: true },
],
].filter(Boolean), ].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) => const outputPath = (outputRoot, latestBuild) =>
path.resolve(outputRoot, latestBuild ? "frontend_latest" : "frontend_es5"); path.resolve(outputRoot, latestBuild ? "frontend_latest" : "frontend_es5");

View File

@@ -1,4 +1,3 @@
/* eslint-disable @typescript-eslint/no-var-requires */
const fs = require("fs"); const fs = require("fs");
const path = require("path"); const path = require("path");
const paths = require("./paths.js"); const paths = require("./paths.js");

View File

@@ -47,8 +47,8 @@ gulp.task(
gulp.parallel("gen-icons-json", "build-translations"), gulp.parallel("gen-icons-json", "build-translations"),
"copy-static-app", "copy-static-app",
env.useRollup() ? "rollup-prod-app" : "webpack-prod-app", env.useRollup() ? "rollup-prod-app" : "webpack-prod-app",
// Don't compress running tests ...// Don't compress running tests
...(env.isTest() ? [] : ["compress-app"]), (env.isTest() ? [] : ["compress-app"]),
gulp.parallel( gulp.parallel(
"gen-pages-prod", "gen-pages-prod",
"gen-index-app-prod", "gen-index-app-prod",

View File

@@ -302,23 +302,15 @@ gulp.task("gen-index-hassio-prod", async () => {
function writeHassioEntrypoint(latestEntrypoint, es5Entrypoint) { function writeHassioEntrypoint(latestEntrypoint, es5Entrypoint) {
fs.mkdirSync(paths.hassio_output_root, { recursive: true }); 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( fs.writeFileSync(
path.resolve(paths.hassio_output_root, "entrypoint.js"), path.resolve(paths.hassio_output_root, "entrypoint.js"),
` `
function loadES5() { try {
new Function("import('${latestEntrypoint}')")();
} catch (err) {
var el = document.createElement('script'); var el = document.createElement('script');
el.src = '${es5Entrypoint}'; el.src = '${es5Entrypoint}';
document.body.appendChild(el); 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" } { encoding: "utf-8" }

View File

@@ -2,6 +2,7 @@
const gulp = require("gulp"); const gulp = require("gulp");
const path = require("path"); const path = require("path");
const cpx = require("cpx");
const fs = require("fs-extra"); const fs = require("fs-extra");
const paths = require("../paths"); const paths = require("../paths");
@@ -61,12 +62,9 @@ function copyLoaderJS(staticDir) {
function copyFonts(staticDir) { function copyFonts(staticDir) {
const staticPath = genStaticPath(staticDir); const staticPath = genStaticPath(staticDir);
// Local fonts // Local fonts
fs.copySync( cpx.copySync(
npmPath("roboto-fontface/fonts/roboto/"), npmPath("roboto-fontface/fonts/roboto/*.woff2"),
staticPath("fonts/roboto/"), staticPath("fonts/roboto")
{
filter: (src) => !src.includes(".") || src.endsWith(".woff2"),
}
); );
} }

View File

@@ -1,5 +1,4 @@
// Tasks to run webpack. // Tasks to run webpack.
const fs = require("fs");
const gulp = require("gulp"); const gulp = require("gulp");
const webpack = require("webpack"); const webpack = require("webpack");
const WebpackDevServer = require("webpack-dev-server"); const WebpackDevServer = require("webpack-dev-server");
@@ -19,13 +18,6 @@ const bothBuilds = (createConfigFunc, params) => [
createConfigFunc({ ...params, latestBuild: false }), createConfigFunc({ ...params, latestBuild: false }),
]; ];
const isWsl =
fs.existsSync("/proc/version") &&
fs
.readFileSync("/proc/version", "utf-8")
.toLocaleLowerCase()
.includes("microsoft");
/** /**
* @param {{ * @param {{
* compiler: import("webpack").Compiler, * compiler: import("webpack").Compiler,
@@ -86,15 +78,8 @@ const prodBuild = (conf) =>
gulp.task("webpack-watch-app", () => { gulp.task("webpack-watch-app", () => {
// This command will run forever because we don't close compiler // This command will run forever because we don't close compiler
webpack( webpack(createAppConfig({ isProdBuild: false, latestBuild: true })).watch(
process.env.ES5 { ignored: /build-translations/ },
? bothBuilds(createAppConfig, { isProdBuild: false })
: createAppConfig({ isProdBuild: false, latestBuild: true })
).watch(
{
ignored: /build-translations/,
poll: isWsl,
},
doneHandler() doneHandler()
); );
gulp.watch( gulp.watch(
@@ -152,7 +137,7 @@ gulp.task("webpack-watch-hassio", () => {
isProdBuild: false, isProdBuild: false,
latestBuild: true, latestBuild: true,
}) })
).watch({ ignored: /build-translations/, poll: isWsl }, doneHandler()); ).watch({ ignored: /build-translations/ }, doneHandler());
gulp.watch( gulp.watch(
path.join(paths.translations_src, "en.json"), path.join(paths.translations_src, "en.json"),

View File

@@ -1,4 +1,3 @@
/* eslint-disable @typescript-eslint/no-var-requires */
const path = require("path"); const path = require("path");
module.exports = { module.exports = {

View File

@@ -1,4 +1,3 @@
/* eslint-disable @typescript-eslint/no-var-requires */
const path = require("path"); const path = require("path");
const commonjs = require("@rollup/plugin-commonjs"); const commonjs = require("@rollup/plugin-commonjs");
@@ -33,77 +32,88 @@ const createRollupConfig = ({
publicPath, publicPath,
dontHash, dontHash,
isWDS, isWDS,
}) => ({ }) => {
/** return {
* @type { import("rollup").InputOptions } /**
*/ * @type { import("rollup").InputOptions }
inputOptions: { */
input: entry, inputOptions: {
// Some entry points contain no JavaScript. This setting silences a warning about that. input: entry,
// https://rollupjs.org/guide/en/#preserveentrysignatures // Some entry points contain no JavaScript. This setting silences a warning about that.
preserveEntrySignatures: false, // https://rollupjs.org/guide/en/#preserveentrysignatures
plugins: [ preserveEntrySignatures: false,
ignore({ plugins: [
files: bundle.emptyPackages({ latestBuild }), 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,
}), }),
!isWDS && worker(), resolve({
!isWDS && dontHashPlugin({ dontHash }), extensions,
!isWDS && isProdBuild && terser(bundle.terserOptions(latestBuild)), preferBuiltins: false,
!isWDS && browser: true,
isStatsBuild && rootDir: paths.polymer_dir,
visualizer({
// https://github.com/btd/rollup-plugin-visualizer#options
open: true,
sourcemap: true,
}), }),
].filter(Boolean), commonjs({
}, namedExports: {
/** "js-yaml": ["safeDump", "safeLoad"],
* @type { import("rollup").OutputOptions } },
*/ }),
outputOptions: { json(),
// https://rollupjs.org/guide/en/#outputdir babel({
dir: outputPath, ...bundle.babelOptions({ latestBuild }),
// https://rollupjs.org/guide/en/#outputformat extensions,
format: latestBuild ? "es" : "systemjs", exclude: bundle.babelExclude(),
// https://rollupjs.org/guide/en/#outputexternallivebindings babelHelpers: isWDS ? "inline" : "bundled",
externalLiveBindings: false, }),
// https://rollupjs.org/guide/en/#outputentryfilenames string({
// https://rollupjs.org/guide/en/#outputchunkfilenames // Import certain extensions as strings
// https://rollupjs.org/guide/en/#outputassetfilenames include: [path.join(paths.polymer_dir, "node_modules/**/*.css")],
entryFileNames: }),
isProdBuild && !isStatsBuild ? "[name]-[hash].js" : "[name].js", replace(
chunkFileNames: isProdBuild && !isStatsBuild ? "c.[hash].js" : "[name].js", bundle.definedVars({ isProdBuild, latestBuild, defineOverlay })
assetFileNames: isProdBuild && !isStatsBuild ? "a.[hash].js" : "[name].js", ),
// https://rollupjs.org/guide/en/#outputsourcemap !isWDS &&
sourcemap: isProdBuild ? true : "inline", 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 }) => const createAppConfig = ({ isProdBuild, latestBuild, isStatsBuild, isWDS }) => {
createRollupConfig( return createRollupConfig(
bundle.config.app({ bundle.config.app({
isProdBuild, isProdBuild,
latestBuild, latestBuild,
@@ -111,24 +121,31 @@ const createAppConfig = ({ isProdBuild, latestBuild, isStatsBuild, isWDS }) =>
isWDS, isWDS,
}) })
); );
};
const createDemoConfig = ({ isProdBuild, latestBuild, isStatsBuild }) => const createDemoConfig = ({ isProdBuild, latestBuild, isStatsBuild }) => {
createRollupConfig( return createRollupConfig(
bundle.config.demo({ bundle.config.demo({
isProdBuild, isProdBuild,
latestBuild, latestBuild,
isStatsBuild, isStatsBuild,
}) })
); );
};
const createCastConfig = ({ isProdBuild, latestBuild }) => const createCastConfig = ({ isProdBuild, latestBuild }) => {
createRollupConfig(bundle.config.cast({ isProdBuild, latestBuild })); return createRollupConfig(bundle.config.cast({ isProdBuild, latestBuild }));
};
const createHassioConfig = ({ isProdBuild, latestBuild }) => const createHassioConfig = ({ isProdBuild, latestBuild }) => {
createRollupConfig(bundle.config.hassio({ isProdBuild, latestBuild })); return createRollupConfig(bundle.config.hassio({ isProdBuild, latestBuild }));
};
const createGalleryConfig = ({ isProdBuild, latestBuild }) => const createGalleryConfig = ({ isProdBuild, latestBuild }) => {
createRollupConfig(bundle.config.gallery({ isProdBuild, latestBuild })); return createRollupConfig(
bundle.config.gallery({ isProdBuild, latestBuild })
);
};
module.exports = { module.exports = {
createAppConfig, createAppConfig,

View File

@@ -1,4 +1,3 @@
/* eslint-disable @typescript-eslint/no-var-requires */
const path = require("path"); const path = require("path");
const fs = require("fs"); const fs = require("fs");

View File

@@ -1,10 +1,9 @@
/* eslint-disable @typescript-eslint/no-var-requires */
const webpack = require("webpack"); const webpack = require("webpack");
const path = require("path"); const path = require("path");
const TerserPlugin = require("terser-webpack-plugin"); const TerserPlugin = require("terser-webpack-plugin");
const { WebpackManifestPlugin } = require("webpack-manifest-plugin"); const { WebpackManifestPlugin } = require("webpack-manifest-plugin");
const paths = require("./paths.js"); const paths = require("./paths.js");
const bundle = require("./bundle.js"); const bundle = require("./bundle");
const log = require("fancy-log"); const log = require("fancy-log");
class LogStartCompilePlugin { class LogStartCompilePlugin {
@@ -47,18 +46,15 @@ const createWebpackConfig = ({
rules: [ rules: [
{ {
test: /\.m?js$|\.ts$/, test: /\.m?js$|\.ts$/,
exclude: bundle.babelExclude(),
use: { use: {
loader: "babel-loader", loader: "babel-loader",
options: { options: bundle.babelOptions({ latestBuild }),
...bundle.babelOptions({ latestBuild }),
cacheDirectory: !isProdBuild,
cacheCompression: false,
},
}, },
}, },
{ {
test: /\.css$/, test: /\.css$/,
type: "asset/source", use: "raw-loader",
}, },
], ],
}, },
@@ -70,8 +66,6 @@ const createWebpackConfig = ({
terserOptions: bundle.terserOptions(latestBuild), terserOptions: bundle.terserOptions(latestBuild),
}), }),
], ],
moduleIds: isProdBuild && !isStatsBuild ? "deterministic" : "named",
chunkIds: isProdBuild && !isStatsBuild ? "deterministic" : "named",
}, },
plugins: [ plugins: [
new WebpackManifestPlugin({ new WebpackManifestPlugin({
@@ -100,7 +94,6 @@ const createWebpackConfig = ({
? path.resolve(context, resource) ? path.resolve(context, resource)
: require.resolve(resource); : require.resolve(resource);
} catch (err) { } catch (err) {
// eslint-disable-next-line no-console
console.error( console.error(
"Error in Home Assistant ignore plugin", "Error in Home Assistant ignore plugin",
resource, resource,
@@ -118,25 +111,31 @@ const createWebpackConfig = ({
new RegExp(bundle.emptyPackages({ latestBuild }).join("|")), new RegExp(bundle.emptyPackages({ latestBuild }).join("|")),
path.resolve(paths.polymer_dir, "src/util/empty.js") 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(), !isProdBuild && new LogStartCompilePlugin(),
].filter(Boolean), ].filter(Boolean),
resolve: { resolve: {
extensions: [".ts", ".js", ".json"], extensions: [".ts", ".js", ".json"],
alias: {
"lit/decorators$": "lit/decorators.js",
"lit/directive$": "lit/directive.js",
"lit/polyfill-support$": "lit/polyfill-support.js",
},
}, },
output: { output: {
filename: ({ chunk }) => { filename: ({ chunk }) => {
if (!isProdBuild || isStatsBuild || dontHash.has(chunk.name)) { if (!isProdBuild || dontHash.has(chunk.name)) {
return `${chunk.name}.js`; return `${chunk.name}.js`;
} }
return `${chunk.name}.${chunk.hash.substr(0, 8)}.js`; return `${chunk.name}.${chunk.hash.substr(0, 8)}.js`;
}, },
chunkFilename: chunkFilename:
isProdBuild && !isStatsBuild ? "[chunkhash:8].js" : "[id].chunk.js", isProdBuild && !isStatsBuild
? "chunk.[chunkhash].js"
: "[name].chunk.js",
path: outputPath, path: outputPath,
publicPath, publicPath,
// To silence warning in worker plugin // To silence warning in worker plugin
@@ -145,24 +144,33 @@ const createWebpackConfig = ({
}; };
}; };
const createAppConfig = ({ isProdBuild, latestBuild, isStatsBuild }) => const createAppConfig = ({ isProdBuild, latestBuild, isStatsBuild }) => {
createWebpackConfig( return createWebpackConfig(
bundle.config.app({ isProdBuild, latestBuild, isStatsBuild }) bundle.config.app({ isProdBuild, latestBuild, isStatsBuild })
); );
};
const createDemoConfig = ({ isProdBuild, latestBuild, isStatsBuild }) => const createDemoConfig = ({ isProdBuild, latestBuild, isStatsBuild }) => {
createWebpackConfig( return createWebpackConfig(
bundle.config.demo({ isProdBuild, latestBuild, isStatsBuild }) bundle.config.demo({ isProdBuild, latestBuild, isStatsBuild })
); );
};
const createCastConfig = ({ isProdBuild, latestBuild }) => const createCastConfig = ({ isProdBuild, latestBuild }) => {
createWebpackConfig(bundle.config.cast({ isProdBuild, latestBuild })); return createWebpackConfig(bundle.config.cast({ isProdBuild, latestBuild }));
};
const createHassioConfig = ({ isProdBuild, latestBuild }) => const createHassioConfig = ({ isProdBuild, latestBuild }) => {
createWebpackConfig(bundle.config.hassio({ isProdBuild, latestBuild })); return createWebpackConfig(
bundle.config.hassio({ isProdBuild, latestBuild })
);
};
const createGalleryConfig = ({ isProdBuild, latestBuild }) => const createGalleryConfig = ({ isProdBuild, latestBuild }) => {
createWebpackConfig(bundle.config.gallery({ isProdBuild, latestBuild })); return createWebpackConfig(
bundle.config.gallery({ isProdBuild, latestBuild })
);
};
module.exports = { module.exports = {
createAppConfig, createAppConfig,

View File

@@ -139,7 +139,7 @@
Your authentication credentials or Home Assistant url are never sent Your authentication credentials or Home Assistant url are never sent
to the Cloud. You can validate this behavior in to the Cloud. You can validate this behavior in
<a <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" target="_blank"
>the source code</a >the source code</a
>. >.

View File

@@ -1,9 +1,16 @@
import "@material/mwc-button/mwc-button";
import "@polymer/paper-item/paper-icon-item"; import "@polymer/paper-item/paper-icon-item";
import "@polymer/paper-listbox/paper-listbox"; import "@polymer/paper-listbox/paper-listbox";
import { Auth, Connection } from "home-assistant-js-websocket"; import { Auth, Connection } from "home-assistant-js-websocket";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import {
import { customElement, property, state } from "lit/decorators"; css,
CSSResult,
customElement,
html,
LitElement,
property,
internalProperty,
TemplateResult,
} from "lit-element";
import { CastManager } from "../../../../src/cast/cast_manager"; import { CastManager } from "../../../../src/cast/cast_manager";
import { import {
castSendShowLovelaceView, castSendShowLovelaceView,
@@ -25,6 +32,7 @@ import {
import "../../../../src/layouts/hass-loading-screen"; import "../../../../src/layouts/hass-loading-screen";
import { generateDefaultViewConfig } from "../../../../src/panels/lovelace/common/generate-lovelace-config"; import { generateDefaultViewConfig } from "../../../../src/panels/lovelace/common/generate-lovelace-config";
import "./hc-layout"; import "./hc-layout";
import "@material/mwc-button/mwc-button";
@customElement("hc-cast") @customElement("hc-cast")
class HcCast extends LitElement { class HcCast extends LitElement {
@@ -34,9 +42,9 @@ class HcCast extends LitElement {
@property() public castManager!: CastManager; @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 { protected render(): TemplateResult {
if (this.lovelaceConfig === undefined) { if (this.lovelaceConfig === undefined) {
@@ -46,7 +54,9 @@ class HcCast extends LitElement {
const error = const error =
this.castManager.castState === "NO_DEVICES_AVAILABLE" this.castManager.castState === "NO_DEVICES_AVAILABLE"
? html` ? 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; : undefined;
@@ -196,7 +206,7 @@ class HcCast extends LitElement {
} }
} }
static get styles(): CSSResultGroup { static get styles(): CSSResult {
return css` return css`
.center-item { .center-item {
display: flex; display: flex;

View File

@@ -11,8 +11,15 @@ import {
getAuth, getAuth,
getAuthOptions, getAuthOptions,
} from "home-assistant-js-websocket"; } from "home-assistant-js-websocket";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import {
import { customElement, state } from "lit/decorators"; css,
CSSResult,
customElement,
html,
LitElement,
TemplateResult,
internalProperty,
} from "lit-element";
import { CastManager, getCastManager } from "../../../../src/cast/cast_manager"; import { CastManager, getCastManager } from "../../../../src/cast/cast_manager";
import { castSendShowDemo } from "../../../../src/cast/receiver_messages"; import { castSendShowDemo } from "../../../../src/cast/receiver_messages";
import { import {
@@ -53,19 +60,19 @@ const INTRO = html`
@customElement("hc-connect") @customElement("hc-connect")
export class HcConnect extends LitElement { export class HcConnect extends LitElement {
@state() private loading = false; @internalProperty() private loading = false;
// If we had stored credentials but we cannot connect, // If we had stored credentials but we cannot connect,
// show a screen asking retry or logout. // 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; private openDemo = false;
@@ -79,7 +86,9 @@ export class HcConnect extends LitElement {
</div> </div>
<div class="card-actions"> <div class="card-actions">
<a href="/"> <a href="/">
<mwc-button> Retry </mwc-button> <mwc-button>
Retry
</mwc-button>
</a> </a>
<div class="spacer"></div> <div class="spacer"></div>
<mwc-button @click=${this._handleLogout}>Log out</mwc-button> <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` return css`
.card-content a { .card-content a {
color: var(--primary-color); color: var(--primary-color);

View File

@@ -4,8 +4,15 @@ import {
getUser, getUser,
HassUser, HassUser,
} from "home-assistant-js-websocket"; } from "home-assistant-js-websocket";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import {
import { customElement, property } from "lit/decorators"; css,
CSSResult,
customElement,
html,
LitElement,
property,
TemplateResult,
} from "lit-element";
import "../../../../src/components/ha-card"; import "../../../../src/components/ha-card";
@customElement("hc-layout") @customElement("hc-layout")
@@ -62,7 +69,7 @@ class HcLayout extends LitElement {
} }
} }
static get styles(): CSSResultGroup { static get styles(): CSSResult {
return css` return css`
:host { :host {
display: flex; display: flex;

View File

@@ -5,8 +5,8 @@ import {
import { castContext } from "../cast_context"; import { castContext } from "../cast_context";
export const castDemoLovelace: () => LovelaceConfig = () => { export const castDemoLovelace: () => LovelaceConfig = () => {
const touchSupported = const touchSupported = castContext.getDeviceCapabilities()
castContext.getDeviceCapabilities().touch_input_supported; .touch_input_supported;
return { return {
views: [ views: [
{ {

View File

@@ -1,5 +1,10 @@
import { html, TemplateResult } from "lit"; import {
import { customElement, property, state } from "lit/decorators"; customElement,
html,
internalProperty,
property,
TemplateResult,
} from "lit-element";
import { mockHistory } from "../../../../demo/src/stubs/history"; import { mockHistory } from "../../../../demo/src/stubs/history";
import { LovelaceConfig } from "../../../../src/data/lovelace"; import { LovelaceConfig } from "../../../../src/data/lovelace";
import { import {
@@ -16,7 +21,7 @@ import "./hc-lovelace";
class HcDemo extends HassElement { class HcDemo extends HassElement {
@property({ attribute: false }) public lovelacePath!: string; @property({ attribute: false }) public lovelacePath!: string;
@state() private _lovelaceConfig?: LovelaceConfig; @internalProperty() private _lovelaceConfig?: LovelaceConfig;
protected render(): TemplateResult { protected render(): TemplateResult {
if (!this._lovelaceConfig) { if (!this._lovelaceConfig) {
@@ -33,10 +38,10 @@ class HcDemo extends HassElement {
protected firstUpdated(changedProps) { protected firstUpdated(changedProps) {
super.firstUpdated(changedProps); super.firstUpdated(changedProps);
this._initializeHass(); this._initialize();
} }
private async _initializeHass() { private async _initialize() {
const initial: Partial<MockHomeAssistant> = { const initial: Partial<MockHomeAssistant> = {
// Override updateHass so that the correct hass lifecycle methods are called // Override updateHass so that the correct hass lifecycle methods are called
updateHass: (hassUpdate: Partial<HomeAssistant>) => updateHass: (hassUpdate: Partial<HomeAssistant>) =>

View File

@@ -1,5 +1,12 @@
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import {
import { customElement, property } from "lit/decorators"; css,
CSSResult,
customElement,
html,
LitElement,
property,
TemplateResult,
} from "lit-element";
import { HomeAssistant } from "../../../../src/types"; import { HomeAssistant } from "../../../../src/types";
@customElement("hc-launch-screen") @customElement("hc-launch-screen")
@@ -22,7 +29,7 @@ class HcLaunchScreen extends LitElement {
`; `;
} }
static get styles(): CSSResultGroup { static get styles(): CSSResult {
return css` return css`
:host { :host {
display: block; display: block;

View File

@@ -1,5 +1,12 @@
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import {
import { customElement, property } from "lit/decorators"; css,
CSSResult,
customElement,
html,
LitElement,
property,
TemplateResult,
} from "lit-element";
import { LovelaceConfig } from "../../../../src/data/lovelace"; import { LovelaceConfig } from "../../../../src/data/lovelace";
import { Lovelace } from "../../../../src/panels/lovelace/types"; import { Lovelace } from "../../../../src/panels/lovelace/types";
import "../../../../src/panels/lovelace/views/hui-view"; import "../../../../src/panels/lovelace/views/hui-view";
@@ -28,7 +35,6 @@ class HcLovelace extends LitElement {
} }
const lovelace: Lovelace = { const lovelace: Lovelace = {
config: this.lovelaceConfig, config: this.lovelaceConfig,
rawConfig: this.lovelaceConfig,
editMode: false, editMode: false,
urlPath: this.urlPath!, urlPath: this.urlPath!,
enableFullEditMode: () => undefined, enableFullEditMode: () => undefined,
@@ -84,7 +90,7 @@ class HcLovelace extends LitElement {
return undefined; return undefined;
} }
static get styles(): CSSResultGroup { static get styles(): CSSResult {
return css` return css`
:host { :host {
min-height: 100vh; min-height: 100vh;

View File

@@ -3,8 +3,12 @@ import {
getAuth, getAuth,
UnsubscribeFunc, UnsubscribeFunc,
} from "home-assistant-js-websocket"; } from "home-assistant-js-websocket";
import { html, TemplateResult } from "lit"; import {
import { customElement, state } from "lit/decorators"; customElement,
html,
internalProperty,
TemplateResult,
} from "lit-element";
import { CAST_NS } from "../../../../src/cast/const"; import { CAST_NS } from "../../../../src/cast/const";
import { import {
ConnectMessage, ConnectMessage,
@@ -32,13 +36,13 @@ let resourcesLoaded = false;
@customElement("hc-main") @customElement("hc-main")
export class HcMain extends HassElement { 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; private _unsubLovelace?: UnsubscribeFunc;
@@ -217,17 +221,11 @@ export class HcMain extends HassElement {
} }
private async _generateLovelaceConfig() { private async _generateLovelaceConfig() {
const { generateLovelaceDashboardStrategy } = await import( const { generateLovelaceConfigFromHass } = await import(
"../../../../src/panels/lovelace/strategies/get-strategy" "../../../../src/panels/lovelace/common/generate-lovelace-config"
); );
this._handleNewLovelaceConfig( this._handleNewLovelaceConfig(
await generateLovelaceDashboardStrategy( await generateLovelaceConfigFromHass(this.hass!)
{
hass: this.hass!,
narrow: false,
},
"original-states"
)
); );
} }

View File

@@ -246,15 +246,11 @@ export const demoEntitiesArsaboo: DemoConfig["entities"] = (localize) =>
"light.living_room_lights": { "light.living_room_lights": {
entity_id: "light.living_room_lights", entity_id: "light.living_room_lights",
state: "on", state: "off",
attributes: { attributes: {
min_mireds: 111, min_mireds: 111,
max_mireds: 400, max_mireds: 400,
brightness: 175,
color_temp: 300,
supported_color_modes: ["brightness", "color_temp"],
friendly_name: "Living Room Lights", friendly_name: "Living Room Lights",
color_mode: "color_temp",
supported_features: 55, supported_features: 55,
}, },
}, },
@@ -267,27 +263,13 @@ export const demoEntitiesArsaboo: DemoConfig["entities"] = (localize) =>
}, },
"light.kitchen_lights": { "light.kitchen_lights": {
entity_id: "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", state: "off",
attributes: { attributes: {
supported_color_modes: ["brightness"], friendly_name: "Kitchen Lights",
friendly_name: "Garage Lights",
supported_features: 1, supported_features: 1,
}, },
}, },
"sensor.plexspy": { "sensor.plexspy": {
entity_id: "sensor.plexspy", entity_id: "sensor.plexspy",
state: "0", state: "0",
@@ -500,6 +482,16 @@ export const demoEntitiesArsaboo: DemoConfig["entities"] = (localize) =>
icon: "hademo:history", 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": { "sensor.alok_to_home": {
entity_id: "sensor.alok_to_home", entity_id: "sensor.alok_to_home",
state: "41", state: "41",

View File

@@ -113,7 +113,8 @@ export const demoLovelaceArsaboo: DemoConfig["lovelace"] = (localize) => ({
on: "/assets/arsaboo/icons/light_bulb_on.png", on: "/assets/arsaboo/icons/light_bulb_on.png",
}, },
state_filter: { 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)", off: "brightness(80%) saturate(0.8)",
}, },
style: { style: {
@@ -195,7 +196,8 @@ export const demoLovelaceArsaboo: DemoConfig["lovelace"] = (localize) => ({
on: "/assets/arsaboo/icons/light_bulb_on.png", on: "/assets/arsaboo/icons/light_bulb_on.png",
}, },
state_filter: { 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)", off: "brightness(80%) saturate(0.8)",
}, },
style: { style: {
@@ -275,7 +277,8 @@ export const demoLovelaceArsaboo: DemoConfig["lovelace"] = (localize) => ({
on: "/assets/arsaboo/icons/light_bulb_on.png", on: "/assets/arsaboo/icons/light_bulb_on.png",
}, },
state_filter: { 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)", off: "brightness(80%) saturate(0.8)",
}, },
style: { style: {
@@ -312,7 +315,8 @@ export const demoLovelaceArsaboo: DemoConfig["lovelace"] = (localize) => ({
on: "/assets/arsaboo/icons/light_bulb_on.png", on: "/assets/arsaboo/icons/light_bulb_on.png",
}, },
state_filter: { 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)", off: "brightness(80%) saturate(0.8)",
}, },
style: { style: {

View File

@@ -1,6 +1,5 @@
import { MockHomeAssistant } from "../../../src/fake_data/provide_hass"; import { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
import { Lovelace } from "../../../src/panels/lovelace/types"; import { Lovelace } from "../../../src/panels/lovelace/types";
import { energyEntities } from "../stubs/entities";
import { DemoConfig } from "./types"; import { DemoConfig } from "./types";
export const demoConfigs: Array<() => Promise<DemoConfig>> = [ export const demoConfigs: Array<() => Promise<DemoConfig>> = [
@@ -13,8 +12,9 @@ export const demoConfigs: Array<() => Promise<DemoConfig>> = [
// eslint-disable-next-line import/no-mutable-exports // eslint-disable-next-line import/no-mutable-exports
export let selectedDemoConfigIndex = 0; export let selectedDemoConfigIndex = 0;
// eslint-disable-next-line import/no-mutable-exports // eslint-disable-next-line import/no-mutable-exports
export let selectedDemoConfig: Promise<DemoConfig> = export let selectedDemoConfig: Promise<DemoConfig> = demoConfigs[
demoConfigs[selectedDemoConfigIndex](); selectedDemoConfigIndex
]();
export const setDemoConfig = async ( export const setDemoConfig = async (
hass: MockHomeAssistant, hass: MockHomeAssistant,
@@ -28,7 +28,6 @@ export const setDemoConfig = async (
selectedDemoConfig = confProm; selectedDemoConfig = confProm;
hass.addEntities(config.entities(hass.localize), true); hass.addEntities(config.entities(hass.localize), true);
hass.addEntities(energyEntities());
lovelace.saveConfig(config.lovelace(hass.localize)); lovelace.saveConfig(config.lovelace(hass.localize));
hass.mockTheme(config.theme()); hass.mockTheme(config.theme());
}; };

View File

@@ -980,7 +980,8 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
icon: "mdi:account-off", icon: "mdi:account-off",
custom_ui_state_card: "state-card-custom-ui", custom_ui_state_card: "state-card-custom-ui",
templates: { 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: icon_color:
"if (state === 'on') return 'rgb(56, 150, 56)'; else if (state === 'off') return 'rgb(249, 251, 255)';\n", "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", icon: "mdi:account-multiple-minus",
custom_ui_state_card: "state-card-custom-ui", custom_ui_state_card: "state-card-custom-ui",
templates: { 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: icon_color:
"if (state === 'on') return 'rgb(56, 150, 56)'; else if (state === 'off') return 'rgb(249, 251, 255)';\n", "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, min_mireds: 153,
max_mireds: 500, max_mireds: 500,
brightness: 63, brightness: 63,
color_temp: 200,
supported_color_modes: ["brightness", "color_temp", "rgb"],
color_mode: "color_temp",
friendly_name: "Upstairs lights", friendly_name: "Upstairs lights",
supported_features: 63, supported_features: 63,
custom_ui_state_card: "state-card-custom-ui", custom_ui_state_card: "state-card-custom-ui",
@@ -1126,7 +1125,6 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
attributes: { attributes: {
friendly_name: "Walk in closet lights", friendly_name: "Walk in closet lights",
supported_features: 41, supported_features: 41,
supported_color_modes: ["brightness", "color_temp"],
custom_ui_state_card: "state-card-custom-ui", custom_ui_state_card: "state-card-custom-ui",
icon: "mdi:wall-sconce", icon: "mdi:wall-sconce",
}, },
@@ -1138,8 +1136,6 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
brightness: 254, brightness: 254,
friendly_name: "Outdoor lights", friendly_name: "Outdoor lights",
supported_features: 41, supported_features: 41,
supported_color_modes: ["brightness"],
color_mode: "brightness",
custom_ui_state_card: "state-card-custom-ui", custom_ui_state_card: "state-card-custom-ui",
icon: "mdi:wall-sconce", icon: "mdi:wall-sconce",
}, },
@@ -1152,8 +1148,6 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
max_mireds: 500, max_mireds: 500,
brightness: 128, brightness: 128,
color_temp: 366, color_temp: 366,
supported_color_modes: ["brightness", "color_temp", "rgb"],
color_mode: "color_temp",
effect_list: ["colorloop"], effect_list: ["colorloop"],
friendly_name: "Downstairs lights", friendly_name: "Downstairs lights",
supported_features: 63, supported_features: 63,
@@ -1313,7 +1307,6 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
attributes: { attributes: {
min_mireds: 153, min_mireds: 153,
max_mireds: 500, max_mireds: 500,
supported_color_modes: ["brightness", "color_temp"],
is_deconz_group: false, is_deconz_group: false,
friendly_name: "Bedside Lamp", friendly_name: "Bedside Lamp",
supported_features: 63, supported_features: 63,
@@ -1327,7 +1320,6 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
attributes: { attributes: {
min_mireds: 153, min_mireds: 153,
max_mireds: 500, max_mireds: 500,
supported_color_modes: ["brightness", "color_temp"],
is_deconz_group: false, is_deconz_group: false,
friendly_name: "Floorlamp Reading Light", friendly_name: "Floorlamp Reading Light",
supported_features: 43, supported_features: 43,
@@ -1343,8 +1335,6 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
max_mireds: 500, max_mireds: 500,
brightness: 128, brightness: 128,
color_temp: 366, color_temp: 366,
supported_color_modes: ["brightness", "color_temp", "rgb"],
color_mode: "color_temp",
effect_list: ["colorloop"], effect_list: ["colorloop"],
is_deconz_group: false, is_deconz_group: false,
friendly_name: "Hallway window light", friendly_name: "Hallway window light",
@@ -1359,7 +1349,6 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
attributes: { attributes: {
brightness: 77, brightness: 77,
is_deconz_group: false, is_deconz_group: false,
supported_color_modes: ["brightness"],
friendly_name: "Isa Ceiling Light", friendly_name: "Isa Ceiling Light",
supported_features: 41, supported_features: 41,
custom_ui_state_card: "state-card-custom-ui", custom_ui_state_card: "state-card-custom-ui",
@@ -1374,8 +1363,6 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
max_mireds: 500, max_mireds: 500,
brightness: 150, brightness: 150,
color_temp: 366, color_temp: 366,
supported_color_modes: ["brightness", "color_temp"],
color_mode: "color_temp",
effect_list: ["colorloop"], effect_list: ["colorloop"],
is_deconz_group: false, is_deconz_group: false,
friendly_name: "Floorlamp", friendly_name: "Floorlamp",
@@ -1390,7 +1377,6 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
attributes: { attributes: {
friendly_name: "Bedroom Ceiling Light", friendly_name: "Bedroom Ceiling Light",
supported_features: 41, supported_features: 41,
supported_color_modes: ["brightness"],
custom_ui_state_card: "state-card-custom-ui", custom_ui_state_card: "state-card-custom-ui",
icon: "mdi:ceiling-light", icon: "mdi:ceiling-light",
}, },
@@ -1401,7 +1387,6 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
attributes: { attributes: {
friendly_name: "Nightlight", friendly_name: "Nightlight",
supported_features: 17, supported_features: 17,
supported_color_modes: ["brightness"],
custom_ui_state_card: "state-card-custom-ui", custom_ui_state_card: "state-card-custom-ui",
icon: "mdi:lamp", icon: "mdi:lamp",
}, },
@@ -1768,7 +1753,6 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
power_consumption: 2.2, power_consumption: 2.2,
friendly_name: "Upstairs Hallway Light", friendly_name: "Upstairs Hallway Light",
supported_features: 33, supported_features: 33,
supported_color_modes: ["brightness"],
custom_ui_state_card: "state-card-custom-ui", custom_ui_state_card: "state-card-custom-ui",
icon: "mdi:ceiling-light", icon: "mdi:ceiling-light",
}, },
@@ -1784,7 +1768,6 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
power_consumption: 0, power_consumption: 0,
friendly_name: "Dining Room Light", friendly_name: "Dining Room Light",
supported_features: 33, supported_features: 33,
supported_color_modes: ["brightness"],
custom_ui_state_card: "state-card-custom-ui", custom_ui_state_card: "state-card-custom-ui",
icon: "mdi:ceiling-light", icon: "mdi:ceiling-light",
}, },
@@ -1800,7 +1783,6 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
power_consumption: 0, power_consumption: 0,
friendly_name: "Living room Spotlights", friendly_name: "Living room Spotlights",
supported_features: 33, supported_features: 33,
supported_color_modes: ["brightness"],
custom_ui_state_card: "state-card-custom-ui", custom_ui_state_card: "state-card-custom-ui",
icon: "mdi:track-light", icon: "mdi:track-light",
}, },
@@ -1817,7 +1799,6 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
power_consumption: 2.5, power_consumption: 2.5,
friendly_name: "Passage Lights", friendly_name: "Passage Lights",
supported_features: 33, supported_features: 33,
supported_color_modes: ["brightness"],
custom_ui_state_card: "state-card-custom-ui", custom_ui_state_card: "state-card-custom-ui",
icon: "mdi:track-light", icon: "mdi:track-light",
}, },
@@ -1862,7 +1843,6 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
power_consumption: 37.4, power_consumption: 37.4,
friendly_name: "Kitchen Lights", friendly_name: "Kitchen Lights",
supported_features: 33, supported_features: 33,
supported_color_modes: ["brightness"],
custom_ui_state_card: "state-card-custom-ui", custom_ui_state_card: "state-card-custom-ui",
icon: "mdi:track-light", icon: "mdi:track-light",
}, },

View File

@@ -440,43 +440,57 @@ export const demoLovelaceTeachingbirds: DemoConfig["lovelace"] = () => ({
type: "horizontal-stack", type: "horizontal-stack",
}, },
{ {
type: "grid",
columns: 2,
cards: [ cards: [
{ {
graph: "line", cards: [
type: "sensor", {
entity: "sensor.temperature_bedroom", 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", cards: [
type: "sensor", {
name: "S's room", graph: "line",
entity: "sensor.temperature_stefan", type: "sensor",
entity: "sensor.temperature_passage",
},
{
graph: "line",
type: "sensor",
name: "Bathroom",
entity: "sensor.temperature_downstairs_bathroom",
},
],
type: "horizontal-stack",
}, },
{ {
graph: "line", cards: [
type: "sensor", {
entity: "sensor.temperature_passage", graph: "line",
}, type: "sensor",
{ entity: "sensor.temperature_storage",
graph: "line", },
type: "sensor", {
name: "Bathroom", graph: "line",
entity: "sensor.temperature_downstairs_bathroom", type: "sensor",
}, name: "Refrigerator",
{ entity: "sensor.refrigerator",
graph: "line", },
type: "sensor", ],
entity: "sensor.temperature_storage", type: "horizontal-stack",
},
{
graph: "line",
type: "sensor",
name: "Refrigerator",
entity: "sensor.refrigerator",
}, },
], ],
type: "vertical-stack",
}, },
{ {
entities: [ entities: [

View File

@@ -1,5 +1,5 @@
/* eslint-disable */ /* eslint-disable */
import { LitElement } from "lit"; import { LitElement } from "lit-element";
import "./card-tools"; import "./card-tools";
class CardModder extends LitElement { class CardModder extends LitElement {

View File

@@ -1,5 +1,5 @@
/* eslint-disable */ /* eslint-disable */
import { html, LitElement } from "lit"; import { html, LitElement } from "lit-element";
if (!window.cardTools) { if (!window.cardTools) {
const version = 0.2; const version = 0.2;

View File

@@ -1,5 +1,12 @@
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import {
import { customElement, state } from "lit/decorators"; css,
CSSResult,
customElement,
html,
internalProperty,
LitElement,
TemplateResult,
} from "lit-element";
import { CastManager } from "../../../src/cast/cast_manager"; import { CastManager } from "../../../src/cast/cast_manager";
import { castSendShowDemo } from "../../../src/cast/receiver_messages"; import { castSendShowDemo } from "../../../src/cast/receiver_messages";
import "../../../src/components/ha-icon"; import "../../../src/components/ha-icon";
@@ -13,7 +20,7 @@ import { HomeAssistant } from "../../../src/types";
class CastDemoRow extends LitElement implements LovelaceRow { class CastDemoRow extends LitElement implements LovelaceRow {
public hass!: HomeAssistant; public hass!: HomeAssistant;
@state() private _castManager?: CastManager | null; @internalProperty() private _castManager?: CastManager | null;
public setConfig(_config: CastConfig): void { public setConfig(_config: CastConfig): void {
// No config possible. // No config possible.
@@ -66,7 +73,7 @@ class CastDemoRow extends LitElement implements LovelaceRow {
this.style.display = this._castManager ? "" : "none"; this.style.display = this._castManager ? "" : "none";
} }
static get styles(): CSSResultGroup { static get styles(): CSSResult {
return css` return css`
:host { :host {
display: flex; display: flex;

View File

@@ -1,7 +1,14 @@
import "@material/mwc-button"; import "@material/mwc-button";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import {
import { property, state } from "lit/decorators"; css,
import { until } from "lit/directives/until"; 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-card";
import "../../../src/components/ha-circular-progress"; import "../../../src/components/ha-circular-progress";
import { LovelaceCardConfig } from "../../../src/data/lovelace"; import { LovelaceCardConfig } from "../../../src/data/lovelace";
@@ -19,7 +26,7 @@ export class HADemoCard extends LitElement implements LovelaceCard {
@property({ attribute: false }) public hass!: MockHomeAssistant; @property({ attribute: false }) public hass!: MockHomeAssistant;
@state() private _switching = false; @internalProperty() private _switching?: boolean;
private _hidden = localStorage.hide_demo_card; private _hidden = localStorage.hide_demo_card;
@@ -27,7 +34,12 @@ export class HADemoCard extends LitElement implements LovelaceCard {
return this._hidden ? 0 : 2; 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 { protected render(): TemplateResult {
if (this._hidden) { if (this._hidden) {
@@ -101,7 +113,7 @@ export class HADemoCard extends LitElement implements LovelaceCard {
} }
} }
static get styles(): CSSResultGroup { static get styles(): CSSResult[] {
return [ return [
css` css`
a { a {

View File

@@ -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/ha-style";
import "../../src/resources/roboto"; import "../../src/resources/roboto";
import "../../src/resources/safari-14-attachshadow-patch"; import "../../src/resources/safari-14-attachshadow-patch";

View File

@@ -20,15 +20,11 @@ import { mockShoppingList } from "./stubs/shopping_list";
import { mockSystemLog } from "./stubs/system_log"; import { mockSystemLog } from "./stubs/system_log";
import { mockTemplate } from "./stubs/template"; import { mockTemplate } from "./stubs/template";
import { mockTranslations } from "./stubs/translations"; import { mockTranslations } from "./stubs/translations";
import { mockEnergy } from "./stubs/energy";
import { mockConfig } from "./stubs/config";
import { energyEntities } from "./stubs/entities";
import { mockForecastSolar } from "./stubs/forecast_solar";
class HaDemo extends HomeAssistantAppEl { class HaDemo extends HomeAssistantAppEl {
protected async _initializeHass() { protected async _initialize() {
const initial: Partial<MockHomeAssistant> = { 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 // Override updateHass so that the correct hass lifecycle methods are called
updateHass: (hassUpdate: Partial<HomeAssistant>) => updateHass: (hassUpdate: Partial<HomeAssistant>) =>
this._updateHass(hassUpdate), this._updateHass(hassUpdate),
@@ -51,13 +47,8 @@ class HaDemo extends HomeAssistantAppEl {
mockEvents(hass); mockEvents(hass);
mockMediaPlayer(hass); mockMediaPlayer(hass);
mockFrontend(hass); mockFrontend(hass);
mockEnergy(hass);
mockForecastSolar(hass);
mockConfig(hass);
mockPersistentNotification(hass); mockPersistentNotification(hass);
hass.addEntities(energyEntities());
// Once config is loaded AND localize, set entities and apply theme. // Once config is loaded AND localize, set entities and apply theme.
Promise.all([selectedDemoConfig, localizePromise]).then( Promise.all([selectedDemoConfig, localizePromise]).then(
([conf, localize]) => { ([conf, localize]) => {
@@ -79,7 +70,7 @@ class HaDemo extends HomeAssistantAppEl {
} }
e.preventDefault(); e.preventDefault();
navigate(href); navigate(this, href);
}, },
{ capture: true } { capture: true }
); );

View File

@@ -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",
},
]);
};

View File

@@ -1,70 +0,0 @@
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"],
},
],
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: [] }));
};

View File

@@ -1,143 +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.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_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",
},
},
});

View File

@@ -1,55 +0,0 @@
import { format, startOfToday, startOfTomorrow } from "date-fns";
import { ForecastSolarForecast } from "../../../src/data/forecast_solar";
import { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
export const mockForecastSolar = (hass: MockHomeAssistant) => {
const todayString = format(startOfToday(), "yyyy-MM-dd");
const tomorrowString = format(startOfTomorrow(), "yyyy-MM-dd");
hass.mockWS(
"forecast_solar/forecasts",
(): Record<string, ForecastSolarForecast> => ({
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,
},
},
})
);
};

View File

@@ -1,6 +1,4 @@
import { addHours, differenceInHours } from "date-fns";
import { HassEntity } from "home-assistant-js-websocket"; import { HassEntity } from "home-assistant-js-websocket";
import { StatisticValue } from "../../../src/data/history";
import { MockHomeAssistant } from "../../../src/fake_data/provide_hass"; import { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
interface HistoryQueryParams { interface HistoryQueryParams {
@@ -66,215 +64,17 @@ const generateHistory = (state, deltas) => {
const incrementalUnits = ["clients", "queries", "ads"]; 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,
max: mean,
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 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,
end,
productionFinalVal,
0
);
return [...morning, ...production, ...evening];
},
"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 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,
end,
productionFinalVal,
0
);
return [...morning, ...production, ...evening];
},
"sensor.grid_fossil_fuel_percentage": (id, start, end) =>
generateMeanStatistics(id, start, end, 35, 1.3),
};
export const mockHistory = (mockHass: MockHomeAssistant) => { export const mockHistory = (mockHass: MockHomeAssistant) => {
mockHass.mockAPI( mockHass.mockAPI(
new RegExp("history/period/.+"), new RegExp("history/period/.+"),
(hass, _method, path, _parameters) => { (
hass,
// @ts-ignore
method,
path,
// @ts-ignore
parameters
) => {
const params = parseQuery<HistoryQueryParams>(path.split("?")[1]); const params = parseQuery<HistoryQueryParams>(path.split("?")[1]);
const entities = params.filter_entity_id.split(","); const entities = params.filter_entity_id.split(",");
@@ -295,7 +95,7 @@ export const mockHistory = (mockHass: MockHomeAssistant) => {
const numberState = Number(state.state); const numberState = Number(state.state);
if (isNaN(numberState)) { if (isNaN(numberState)) {
// eslint-disable-next-line no-console // eslint-disable-next-line
console.log( console.log(
"Ignoring state with unparsable state but with a unit", "Ignoring state with unparsable state but with a unit",
entityId, entityId,
@@ -340,39 +140,4 @@ export const mockHistory = (mockHass: MockHomeAssistant) => {
return results; return results;
} }
); );
mockHass.mockWS(
"history/statistics_during_period",
({ statistic_ids, start_time, end_time }, hass) => {
const start = new Date(start_time);
const end = new Date(end_time);
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;
}
);
}; };

View File

@@ -10,9 +10,10 @@ export const mockLovelace = (
localizePromise: Promise<LocalizeFunc> localizePromise: Promise<LocalizeFunc>
) => { ) => {
hass.mockWS("lovelace/config", () => hass.mockWS("lovelace/config", () =>
Promise.all([selectedDemoConfig, localizePromise]).then( Promise.all([
([config, localize]) => config.lovelace(localize) selectedDemoConfig,
) localizePromise,
]).then(([config, localize]) => config.lovelace(localize))
); );
hass.mockWS("lovelace/config/save", () => Promise.resolve()); hass.mockWS("lovelace/config/save", () => Promise.resolve());

View File

@@ -6,7 +6,7 @@ export const mockTemplate = (hass: MockHomeAssistant) => {
body: { message: "Template dev tool does not work in the demo." }, 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!({ onChange!({
result: msg.template, result: msg.template,
listeners: { all: false, domains: [], entities: [], time: false }, listeners: { all: false, domains: [], entities: [], time: false },

View File

@@ -3,6 +3,8 @@ import { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
export const mockTranslations = (hass: MockHomeAssistant) => { export const mockTranslations = (hass: MockHomeAssistant) => {
hass.mockWS( hass.mockWS(
"frontend/get_translations", "frontend/get_translations",
(/* msg: {language: string, category: string} */) => ({ resources: {} }) (/* msg: {language: string, category: string} */) => {
return { resources: {} };
}
); );
}; };

View File

@@ -1,7 +1,7 @@
import { html } from "@polymer/polymer/lib/utils/html-tag"; import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */ /* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element"; 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"; import { createCardElement } from "../../../src/panels/lovelace/create-element/create-card-element";
class DemoCard extends PolymerElement { class DemoCard extends PolymerElement {
@@ -15,10 +15,6 @@ class DemoCard extends PolymerElement {
margin: 0 0 20px; margin: 0 0 20px;
color: var(--primary-color); color: var(--primary-color);
} }
h2 small {
font-size: 0.5em;
color: var(--primary-text-color);
}
#card { #card {
max-width: 400px; max-width: 400px;
width: 100vw; width: 100vw;
@@ -38,12 +34,7 @@ class DemoCard extends PolymerElement {
} }
} }
</style> </style>
<h2> <h2>[[config.heading]]</h2>
[[config.heading]]
<template is="dom-if" if="[[_size]]">
<small>(size [[_size]])</small>
</template>
</h2>
<div class="root"> <div class="root">
<div id="card"></div> <div id="card"></div>
<template is="dom-if" if="[[showConfig]]"> <template is="dom-if" if="[[showConfig]]">
@@ -64,9 +55,6 @@ class DemoCard extends PolymerElement {
observer: "_configChanged", observer: "_configChanged",
}, },
showConfig: Boolean, showConfig: Boolean,
_size: {
type: Number,
},
}; };
} }
@@ -80,19 +68,8 @@ class DemoCard extends PolymerElement {
card.removeChild(card.lastChild); card.removeChild(card.lastChild);
} }
const el = this._createCardElement(load(config.config)[0]); const el = this._createCardElement(safeLoad(config.config)[0]);
card.appendChild(el); 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) { _createCardElement(cardConfig) {

View File

@@ -1,6 +1,12 @@
import { dump } from "js-yaml"; import { safeDump } from "js-yaml";
import { html, css, LitElement, TemplateResult } from "lit"; import {
import { customElement, property } from "lit/decorators"; customElement,
html,
css,
LitElement,
TemplateResult,
property,
} from "lit-element";
import "../../../src/components/ha-card"; import "../../../src/components/ha-card";
import { describeAction } from "../../../src/data/script_i18n"; import { describeAction } from "../../../src/data/script_i18n";
import { provideHass } from "../../../src/fake_data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
@@ -56,7 +62,7 @@ export class DemoAutomationDescribeAction extends LitElement {
(conf) => html` (conf) => html`
<div class="action"> <div class="action">
<span>${describeAction(this.hass, conf as any)}</span> <span>${describeAction(this.hass, conf as any)}</span>
<pre>${dump(conf)}</pre> <pre>${safeDump(conf)}</pre>
</div> </div>
` `
)} )}

View File

@@ -1,6 +1,11 @@
import { dump } from "js-yaml"; import { safeDump } from "js-yaml";
import { html, css, LitElement, TemplateResult } from "lit"; import {
import { customElement } from "lit/decorators"; customElement,
html,
css,
LitElement,
TemplateResult,
} from "lit-element";
import "../../../src/components/ha-card"; import "../../../src/components/ha-card";
import { describeCondition } from "../../../src/data/automation_i18n"; import { describeCondition } from "../../../src/data/automation_i18n";
@@ -26,7 +31,7 @@ export class DemoAutomationDescribeCondition extends LitElement {
(conf) => html` (conf) => html`
<div class="condition"> <div class="condition">
<span>${describeCondition(conf as any)}</span> <span>${describeCondition(conf as any)}</span>
<pre>${dump(conf)}</pre> <pre>${safeDump(conf)}</pre>
</div> </div>
` `
)} )}

View File

@@ -1,6 +1,11 @@
import { dump } from "js-yaml"; import { safeDump } from "js-yaml";
import { html, css, LitElement, TemplateResult } from "lit"; import {
import { customElement } from "lit/decorators"; customElement,
html,
css,
LitElement,
TemplateResult,
} from "lit-element";
import "../../../src/components/ha-card"; import "../../../src/components/ha-card";
import { describeTrigger } from "../../../src/data/automation_i18n"; import { describeTrigger } from "../../../src/data/automation_i18n";
@@ -29,7 +34,7 @@ export class DemoAutomationDescribeTrigger extends LitElement {
(conf) => html` (conf) => html`
<div class="trigger"> <div class="trigger">
<span>${describeTrigger(conf as any)}</span> <span>${describeTrigger(conf as any)}</span>
<pre>${dump(conf)}</pre> <pre>${safeDump(conf)}</pre>
</div> </div>
` `
)} )}

View File

@@ -1,5 +1,11 @@
import { html, css, LitElement, TemplateResult } from "lit"; import {
import { customElement, property } from "lit/decorators"; customElement,
html,
css,
LitElement,
TemplateResult,
property,
} from "lit-element";
import "../../../src/components/ha-card"; import "../../../src/components/ha-card";
import "../../../src/components/trace/hat-script-graph"; import "../../../src/components/trace/hat-script-graph";
import "../../../src/components/trace/hat-trace-timeline"; import "../../../src/components/trace/hat-trace-timeline";
@@ -11,7 +17,7 @@ import { DemoTrace } from "../data/traces/types";
const traces: DemoTrace[] = [ const traces: DemoTrace[] = [
mockDemoTrace({ state: "running" }), mockDemoTrace({ state: "running" }),
mockDemoTrace({ state: "debugged" }), mockDemoTrace({ state: "debugged" }),
mockDemoTrace({ state: "stopped", script_execution: "failed_conditions" }), mockDemoTrace({ state: "stopped", script_execution: "failed_condition" }),
mockDemoTrace({ state: "stopped", script_execution: "failed_single" }), mockDemoTrace({ state: "stopped", script_execution: "failed_single" }),
mockDemoTrace({ state: "stopped", script_execution: "failed_max_runs" }), mockDemoTrace({ state: "stopped", script_execution: "failed_max_runs" }),
mockDemoTrace({ state: "stopped", script_execution: "finished" }), mockDemoTrace({ state: "stopped", script_execution: "finished" }),

View File

@@ -1,8 +1,15 @@
import { html, css, LitElement, TemplateResult } from "lit"; import {
customElement,
html,
css,
LitElement,
TemplateResult,
internalProperty,
property,
} from "lit-element";
import "../../../src/components/ha-card"; import "../../../src/components/ha-card";
import "../../../src/components/trace/hat-script-graph"; import "../../../src/components/trace/hat-script-graph";
import "../../../src/components/trace/hat-trace-timeline"; import "../../../src/components/trace/hat-trace-timeline";
import { customElement, property, state } from "lit/decorators";
import { provideHass } from "../../../src/fake_data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
import { HomeAssistant } from "../../../src/types"; import { HomeAssistant } from "../../../src/types";
import { DemoTrace } from "../data/traces/types"; import { DemoTrace } from "../data/traces/types";
@@ -15,7 +22,7 @@ const traces: DemoTrace[] = [basicTrace, motionLightTrace];
export class DemoAutomationTrace extends LitElement { export class DemoAutomationTrace extends LitElement {
@property({ attribute: false }) hass?: HomeAssistant; @property({ attribute: false }) hass?: HomeAssistant;
@state() private _selected = {}; @internalProperty() private _selected = {};
protected render(): TemplateResult { protected render(): TemplateResult {
if (!this.hass) { if (!this.hass) {

View File

@@ -1,5 +1,11 @@
import { html, LitElement, PropertyValues, TemplateResult } from "lit"; import {
import { customElement, query } from "lit/decorators"; customElement,
html,
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { getEntity } from "../../../src/fake_data/entity"; import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards"; import "../components/demo-cards";
@@ -87,8 +93,4 @@ class DemoAlarmPanelEntity extends LitElement {
} }
} }
declare global { customElements.define("demo-hui-alarm-panel-card", DemoAlarmPanelEntity);
interface HTMLElementTagNameMap {
"demo-hui-alarm-panel-card": DemoAlarmPanelEntity;
}
}

View File

@@ -1,5 +1,11 @@
import { html, LitElement, PropertyValues, TemplateResult } from "lit"; import {
import { customElement, query } from "lit/decorators"; customElement,
html,
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { getEntity } from "../../../src/fake_data/entity"; import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards"; import "../components/demo-cards";
@@ -69,8 +75,4 @@ class DemoConditional extends LitElement {
} }
} }
declare global { customElements.define("demo-hui-conditional-card", DemoConditional);
interface HTMLElementTagNameMap {
"demo-hui-conditional-card": DemoConditional;
}
}

View File

@@ -1,5 +1,11 @@
import { html, LitElement, PropertyValues, TemplateResult } from "lit"; import {
import { customElement, query } from "lit/decorators"; customElement,
html,
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { getEntity } from "../../../src/fake_data/entity"; import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards"; import "../components/demo-cards";
@@ -233,8 +239,4 @@ class DemoEntities extends LitElement {
} }
} }
declare global { customElements.define("demo-hui-entities-card", DemoEntities);
interface HTMLElementTagNameMap {
"demo-hui-entities-card": DemoEntities;
}
}

View File

@@ -1,5 +1,11 @@
import { html, LitElement, PropertyValues, TemplateResult } from "lit"; import {
import { customElement, query } from "lit/decorators"; customElement,
html,
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { getEntity } from "../../../src/fake_data/entity"; import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards"; import "../components/demo-cards";
@@ -85,8 +91,4 @@ class DemoButtonEntity extends LitElement {
} }
} }
declare global { customElements.define("demo-hui-entity-button-card", DemoButtonEntity);
interface HTMLElementTagNameMap {
"demo-hui-entity-button-card": DemoButtonEntity;
}
}

View File

@@ -1,5 +1,11 @@
import { html, LitElement, PropertyValues, TemplateResult } from "lit"; import {
import { customElement, query } from "lit/decorators"; customElement,
html,
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { getEntity } from "../../../src/fake_data/entity"; import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards"; import "../components/demo-cards";
@@ -126,8 +132,4 @@ class DemoEntityFilter extends LitElement {
} }
} }
declare global { customElements.define("demo-hui-entity-filter-card", DemoEntityFilter);
interface HTMLElementTagNameMap {
"demo-hui-entity-filter-card": DemoEntityFilter;
}
}

View File

@@ -1,5 +1,11 @@
import { html, LitElement, PropertyValues, TemplateResult } from "lit"; import {
import { customElement, query } from "lit/decorators"; customElement,
html,
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { getEntity } from "../../../src/fake_data/entity"; import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards"; import "../components/demo-cards";
@@ -123,8 +129,4 @@ class DemoGaugeEntity extends LitElement {
} }
} }
declare global { customElements.define("demo-hui-gauge-card", DemoGaugeEntity);
interface HTMLElementTagNameMap {
"demo-hui-gauge-card": DemoGaugeEntity;
}
}

View File

@@ -1,5 +1,11 @@
import { html, LitElement, PropertyValues, TemplateResult } from "lit"; import {
import { customElement, query } from "lit/decorators"; customElement,
html,
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { getEntity } from "../../../src/fake_data/entity"; import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards"; import "../components/demo-cards";
@@ -180,7 +186,7 @@ const CONFIGS = [
name: name:
- light.kitchen_lights - light.kitchen_lights
- entity: lock.kitchen_door - entity: lock.kitchen_door
name: name:
- light.ceiling_lights - light.ceiling_lights
`, `,
}, },
@@ -188,7 +194,7 @@ const CONFIGS = [
heading: "Custom tap action", heading: "Custom tap action",
config: ` config: `
- type: glance - type: glance
columns: 4 columns: 4
entities: entities:
- entity: lock.kitchen_door - entity: lock.kitchen_door
name: Custom name: Custom
@@ -226,8 +232,4 @@ class DemoGlanceEntity extends LitElement {
} }
} }
declare global { customElements.define("demo-hui-glance-card", DemoGlanceEntity);
interface HTMLElementTagNameMap {
"demo-hui-glance-card": DemoGlanceEntity;
}
}

View File

@@ -1,5 +1,4 @@
import { html, LitElement, TemplateResult } from "lit"; import { customElement, html, LitElement, TemplateResult } from "lit-element";
import { customElement } from "lit/decorators";
import "../components/demo-cards"; import "../components/demo-cards";
const CONFIGS = [ const CONFIGS = [
@@ -43,8 +42,4 @@ class DemoIframe extends LitElement {
} }
} }
declare global { customElements.define("demo-hui-iframe-card", DemoIframe);
interface HTMLElementTagNameMap {
"demo-hui-iframe-card": DemoIframe;
}
}

View File

@@ -1,5 +1,11 @@
import { html, LitElement, PropertyValues, TemplateResult } from "lit"; import {
import { customElement, query } from "lit/decorators"; customElement,
html,
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { getEntity } from "../../../src/fake_data/entity"; import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards"; import "../components/demo-cards";
@@ -79,8 +85,4 @@ class DemoLightEntity extends LitElement {
} }
} }
declare global { customElements.define("demo-hui-light-card", DemoLightEntity);
interface HTMLElementTagNameMap {
"demo-hui-light-card": DemoLightEntity;
}
}

View File

@@ -1,5 +1,11 @@
import { html, LitElement, PropertyValues, TemplateResult } from "lit"; import {
import { customElement, query } from "lit/decorators"; customElement,
html,
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { getEntity } from "../../../src/fake_data/entity"; import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards"; import "../components/demo-cards";
@@ -177,8 +183,4 @@ class DemoMap extends LitElement {
} }
} }
declare global { customElements.define("demo-hui-map-card", DemoMap);
interface HTMLElementTagNameMap {
"demo-hui-map-card": DemoMap;
}
}

View File

@@ -1,5 +1,11 @@
import { html, LitElement, PropertyValues, TemplateResult } from "lit"; import {
import { customElement, query } from "lit/decorators"; customElement,
html,
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { mockTemplate } from "../../../demo/src/stubs/template"; import { mockTemplate } from "../../../demo/src/stubs/template";
import { provideHass } from "../../../src/fake_data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards"; import "../components/demo-cards";
@@ -270,8 +276,4 @@ class DemoMarkdown extends LitElement {
} }
} }
declare global { customElements.define("demo-hui-markdown-card", DemoMarkdown);
interface HTMLElementTagNameMap {
"demo-hui-markdown-card": DemoMarkdown;
}
}

View File

@@ -1,5 +1,11 @@
import { html, LitElement, PropertyValues, TemplateResult } from "lit"; import {
import { customElement, query } from "lit/decorators"; customElement,
html,
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { provideHass } from "../../../src/fake_data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards"; import "../components/demo-cards";
import { createMediaPlayerEntities } from "../data/media_players"; import { createMediaPlayerEntities } from "../data/media_players";
@@ -174,8 +180,4 @@ class DemoHuiMediaControlCard extends LitElement {
} }
} }
declare global { customElements.define("demo-hui-media-control-card", DemoHuiMediaControlCard);
interface HTMLElementTagNameMap {
"demo-hui-media-control-card": DemoHuiMediaControlCard;
}
}

View File

@@ -1,5 +1,11 @@
import { html, LitElement, PropertyValues, TemplateResult } from "lit"; import {
import { customElement, query } from "lit/decorators"; customElement,
html,
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { provideHass } from "../../../src/fake_data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards"; import "../components/demo-cards";
import { createMediaPlayerEntities } from "../data/media_players"; import { createMediaPlayerEntities } from "../data/media_players";
@@ -71,8 +77,4 @@ class DemoHuiMediaPlayerRow extends LitElement {
} }
} }
declare global { customElements.define("demo-hui-media-player-row", DemoHuiMediaPlayerRow);
interface HTMLElementTagNameMap {
"demo-hui-media-player-row": DemoHuiMediaPlayerRow;
}
}

View File

@@ -1,5 +1,11 @@
import { html, LitElement, PropertyValues, TemplateResult } from "lit"; import {
import { customElement, query } from "lit/decorators"; customElement,
html,
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { getEntity } from "../../../src/fake_data/entity"; import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards"; import "../components/demo-cards";
@@ -141,8 +147,4 @@ class DemoPictureElements extends LitElement {
} }
} }
declare global { customElements.define("demo-hui-picture-elements-card", DemoPictureElements);
interface HTMLElementTagNameMap {
"demo-hui-picture-elements-card": DemoPictureElements;
}
}

View File

@@ -1,5 +1,11 @@
import { html, LitElement, PropertyValues, TemplateResult } from "lit"; import {
import { customElement, query } from "lit/decorators"; customElement,
html,
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { getEntity } from "../../../src/fake_data/entity"; import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards"; import "../components/demo-cards";
@@ -96,8 +102,4 @@ class DemoPictureEntity extends LitElement {
} }
} }
declare global { customElements.define("demo-hui-picture-entity-card", DemoPictureEntity);
interface HTMLElementTagNameMap {
"demo-hui-picture-entity-card": DemoPictureEntity;
}
}

View File

@@ -1,5 +1,11 @@
import { html, LitElement, PropertyValues, TemplateResult } from "lit"; import {
import { customElement, query } from "lit/decorators"; customElement,
html,
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { getEntity } from "../../../src/fake_data/entity"; import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards"; import "../components/demo-cards";
@@ -137,8 +143,4 @@ class DemoPictureGlance extends LitElement {
} }
} }
declare global { customElements.define("demo-hui-picture-glance-card", DemoPictureGlance);
interface HTMLElementTagNameMap {
"demo-hui-picture-glance-card": DemoPictureGlance;
}
}

View File

@@ -1,5 +1,11 @@
import { html, LitElement, PropertyValues, TemplateResult } from "lit"; import {
import { customElement, query } from "lit/decorators"; customElement,
html,
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { provideHass } from "../../../src/fake_data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards"; import "../components/demo-cards";
import { createPlantEntities } from "../data/plants"; import { createPlantEntities } from "../data/plants";
@@ -46,8 +52,4 @@ export class DemoPlantEntity extends LitElement {
} }
} }
declare global { customElements.define("demo-hui-plant-card", DemoPlantEntity);
interface HTMLElementTagNameMap {
"demo-hui-plant-card": DemoPlantEntity;
}
}

View File

@@ -1,5 +1,11 @@
import { html, LitElement, PropertyValues, TemplateResult } from "lit"; import {
import { customElement, query } from "lit/decorators"; customElement,
html,
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { provideHass } from "../../../src/fake_data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards"; import "../components/demo-cards";
@@ -42,8 +48,4 @@ class DemoShoppingListEntity extends LitElement {
} }
} }
declare global { customElements.define("demo-hui-shopping-list-card", DemoShoppingListEntity);
interface HTMLElementTagNameMap {
"demo-hui-shopping-list-card": DemoShoppingListEntity;
}
}

View File

@@ -1,5 +1,11 @@
import { html, LitElement, PropertyValues, TemplateResult } from "lit"; import {
import { customElement, query } from "lit/decorators"; customElement,
html,
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { mockHistory } from "../../../demo/src/stubs/history"; import { mockHistory } from "../../../demo/src/stubs/history";
import { getEntity } from "../../../src/fake_data/entity"; import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
@@ -43,110 +49,6 @@ const ENTITIES = [
]; ];
const CONFIGS = [ 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", heading: "Vertical Stack",
config: ` config: `
@@ -197,9 +99,45 @@ const CONFIGS = [
entity: light.bed_light 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 { class DemoStack extends LitElement {
@query("#demos") private _demoRoot!: HTMLElement; @query("#demos") private _demoRoot!: HTMLElement;
@@ -217,8 +155,4 @@ class DemoStack extends LitElement {
} }
} }
declare global { customElements.define("demo-hui-stack-card", DemoStack);
interface HTMLElementTagNameMap {
"demo-hui-grid-and-stack-card": DemoStack;
}
}

View File

@@ -1,5 +1,11 @@
import { html, LitElement, PropertyValues, TemplateResult } from "lit"; import {
import { customElement, query } from "lit/decorators"; customElement,
html,
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { getEntity } from "../../../src/fake_data/entity"; import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards"; import "../components/demo-cards";
@@ -90,8 +96,4 @@ class DemoThermostatEntity extends LitElement {
} }
} }
declare global { customElements.define("demo-hui-thermostat-card", DemoThermostatEntity);
interface HTMLElementTagNameMap {
"demo-hui-thermostat-card": DemoThermostatEntity;
}
}

View File

@@ -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;
}
}

View File

@@ -1,11 +1,21 @@
import { html, LitElement, PropertyValues, TemplateResult } from "lit"; import {
import { customElement, property, query } from "lit/decorators"; customElement,
html,
LitElement,
property,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import "../../../src/components/ha-card"; import "../../../src/components/ha-card";
import { import {
LightColorModes, SUPPORT_BRIGHTNESS,
SUPPORT_COLOR,
SUPPORT_COLOR_TEMP,
SUPPORT_EFFECT, SUPPORT_EFFECT,
SUPPORT_FLASH, SUPPORT_FLASH,
SUPPORT_TRANSITION, SUPPORT_TRANSITION,
SUPPORT_WHITE_VALUE,
} from "../../../src/data/light"; } from "../../../src/data/light";
import "../../../src/dialogs/more-info/more-info-content"; import "../../../src/dialogs/more-info/more-info-content";
import { getEntity } from "../../../src/fake_data/entity"; import { getEntity } from "../../../src/fake_data/entity";
@@ -22,8 +32,7 @@ const ENTITIES = [
getEntity("light", "kitchen_light", "on", { getEntity("light", "kitchen_light", "on", {
friendly_name: "Brightness Light", friendly_name: "Brightness Light",
brightness: 200, brightness: 200,
supported_color_modes: [LightColorModes.BRIGHTNESS], supported_features: SUPPORT_BRIGHTNESS,
color_mode: LightColorModes.BRIGHTNESS,
}), }),
getEntity("light", "color_temperature_light", "on", { getEntity("light", "color_temperature_light", "on", {
friendly_name: "White Color Temperature Light", friendly_name: "White Color Temperature Light",
@@ -31,96 +40,20 @@ const ENTITIES = [
color_temp: 75, color_temp: 75,
min_mireds: 30, min_mireds: 30,
max_mireds: 150, max_mireds: 150,
supported_color_modes: [ supported_features: SUPPORT_BRIGHTNESS + SUPPORT_COLOR_TEMP,
LightColorModes.BRIGHTNESS,
LightColorModes.COLOR_TEMP,
],
color_mode: LightColorModes.COLOR_TEMP,
}), }),
getEntity("light", "color_hs_light", "on", { getEntity("light", "color_effectslight", "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", {
friendly_name: "Color Effets Light", friendly_name: "Color Effets Light",
brightness: 255, brightness: 255,
rgb_color: [30, 100, 255], hs_color: [30, 100],
supported_features: SUPPORT_EFFECT + SUPPORT_FLASH + SUPPORT_TRANSITION, white_value: 36,
supported_color_modes: [LightColorModes.BRIGHTNESS, LightColorModes.RGB], supported_features:
color_mode: LightColorModes.RGB, SUPPORT_BRIGHTNESS +
effect_list: ["random", "colorloop"], SUPPORT_EFFECT +
}), SUPPORT_FLASH +
getEntity("light", "color_rgbw_light", "on", { SUPPORT_COLOR +
friendly_name: "Color RGBW Light", SUPPORT_TRANSITION +
brightness: 255, SUPPORT_WHITE_VALUE,
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,
effect_list: ["random", "colorloop"], effect_list: ["random", "colorloop"],
}), }),
]; ];

View File

@@ -1,6 +1,5 @@
import "@material/mwc-button"; import "@material/mwc-button";
import { html, LitElement, TemplateResult } from "lit"; import { customElement, html, LitElement, TemplateResult } from "lit-element";
import { customElement } from "lit/decorators";
import "../../../src/components/ha-card"; import "../../../src/components/ha-card";
import { ActionHandlerEvent } from "../../../src/data/lovelace"; import { ActionHandlerEvent } from "../../../src/data/lovelace";
import { actionHandler } from "../../../src/panels/lovelace/common/directives/action-handler-directive"; import { actionHandler } from "../../../src/panels/lovelace/common/directives/action-handler-directive";

View File

@@ -1,6 +1,12 @@
import { mdiArrowUpBoldCircle, mdiPuzzle } from "@mdi/js"; import { mdiArrowUpBoldCircle, mdiPuzzle } from "@mdi/js";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import {
import { property } from "lit/decorators"; css,
CSSResultArray,
html,
LitElement,
property,
TemplateResult,
} from "lit-element";
import memoizeOne from "memoize-one"; import memoizeOne from "memoize-one";
import { atLeastVersion } from "../../../src/common/config/version"; import { atLeastVersion } from "../../../src/common/config/version";
import { navigate } from "../../../src/common/navigate"; import { navigate } from "../../../src/common/navigate";
@@ -41,7 +47,9 @@ class HassioAddonRepositoryEl extends LitElement {
const repo = this.repo; const repo = this.repo;
let _addons = this.addons; let _addons = this.addons;
if (!this.hass.userData?.showAdvanced) { 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); const addons = this._getAddons(_addons, this.filter);
@@ -60,7 +68,9 @@ class HassioAddonRepositoryEl extends LitElement {
} }
return html` return html`
<div class="content"> <div class="content">
<h1>${repo.name}</h1> <h1>
${repo.name}
</h1>
<div class="card-group"> <div class="card-group">
${addons.map( ${addons.map(
(addon) => html` (addon) => html`
@@ -120,10 +130,10 @@ class HassioAddonRepositoryEl extends LitElement {
} }
private _addonTapped(ev) { 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 [ return [
hassioStyle, hassioStyle,
css` css`

View File

@@ -4,13 +4,13 @@ import "@material/mwc-list/mwc-list-item";
import { mdiDotsVertical } from "@mdi/js"; import { mdiDotsVertical } from "@mdi/js";
import { import {
css, css,
CSSResultGroup, CSSResult,
html, internalProperty,
LitElement, LitElement,
property,
PropertyValues, PropertyValues,
TemplateResult, } from "lit-element";
} from "lit"; import { html, TemplateResult } from "lit-html";
import { property, state } from "lit/decorators";
import memoizeOne from "memoize-one"; import memoizeOne from "memoize-one";
import { atLeastVersion } from "../../../src/common/config/version"; import { atLeastVersion } from "../../../src/common/config/version";
import { fireEvent } from "../../../src/common/dom/fire_event"; import { fireEvent } from "../../../src/common/dom/fire_event";
@@ -58,7 +58,7 @@ class HassioAddonStore extends LitElement {
@property({ attribute: false }) public route!: Route; @property({ attribute: false }) public route!: Route;
@state() private _filter?: string; @internalProperty() private _filter?: string;
public async refreshData() { public async refreshData() {
await reloadHassioAddons(this.hass); await reloadHassioAddons(this.hass);
@@ -86,7 +86,9 @@ class HassioAddonStore extends LitElement {
main-page main-page
supervisor supervisor
> >
<span slot="header"> ${this.supervisor.localize("panel.store")} </span> <span slot="header">
${this.supervisor.localize("panel.store")}
</span>
<ha-button-menu <ha-button-menu
corner="BOTTOM_START" corner="BOTTOM_START"
slot="toolbar-icon" slot="toolbar-icon"
@@ -138,7 +140,7 @@ class HassioAddonStore extends LitElement {
protected firstUpdated(changedProps: PropertyValues) { protected firstUpdated(changedProps: PropertyValues) {
super.firstUpdated(changedProps); super.firstUpdated(changedProps);
const repositoryUrl = extractSearchParam("repository_url"); const repositoryUrl = extractSearchParam("repository_url");
navigate("/hassio/store", { replace: true }); navigate(this, "/hassio/store", true);
if (repositoryUrl) { if (repositoryUrl) {
this._manageRepositories(repositoryUrl); this._manageRepositories(repositoryUrl);
} }
@@ -152,8 +154,8 @@ class HassioAddonStore extends LitElement {
repositories: HassioAddonRepository[], repositories: HassioAddonRepository[],
addons: HassioAddonInfo[], addons: HassioAddonInfo[],
filter?: string filter?: string
) => ) => {
repositories.sort(sortRepos).map((repo) => { return repositories.sort(sortRepos).map((repo) => {
const filteredAddons = addons.filter( const filteredAddons = addons.filter(
(addon) => addon.repository === repo.slug (addon) => addon.repository === repo.slug
); );
@@ -169,7 +171,8 @@ class HassioAddonStore extends LitElement {
></hassio-addon-repository> ></hassio-addon-repository>
` `
: html``; : html``;
}) });
}
); );
private _handleAction(ev: CustomEvent<ActionDetail>) { private _handleAction(ev: CustomEvent<ActionDetail>) {
@@ -218,7 +221,7 @@ class HassioAddonStore extends LitElement {
this._filter = e.detail.value; this._filter = e.detail.value;
} }
static get styles(): CSSResultGroup { static get styles(): CSSResult {
return css` return css`
hassio-addon-repository { hassio-addon-repository {
margin-top: 24px; margin-top: 24px;

View File

@@ -4,13 +4,15 @@ import "@polymer/paper-item/paper-item";
import "@polymer/paper-listbox/paper-listbox"; import "@polymer/paper-listbox/paper-listbox";
import { import {
css, css,
CSSResultGroup, CSSResult,
customElement,
html, html,
internalProperty,
LitElement, LitElement,
property,
PropertyValues, PropertyValues,
TemplateResult, TemplateResult,
} from "lit"; } from "lit-element";
import { customElement, property, state } from "lit/decorators";
import "web-animations-js/web-animations-next-lite.min"; import "web-animations-js/web-animations-next-lite.min";
import "../../../../src/components/buttons/ha-progress-button"; import "../../../../src/components/buttons/ha-progress-button";
import "../../../../src/components/ha-card"; import "../../../../src/components/ha-card";
@@ -37,15 +39,15 @@ class HassioAddonAudio extends LitElement {
@property({ attribute: false }) public addon!: HassioAddonDetails; @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 { protected render(): TemplateResult {
return html` return html`
@@ -67,13 +69,13 @@ class HassioAddonAudio extends LitElement {
.selected=${this._selectedInput!} .selected=${this._selectedInput!}
> >
${this._inputDevices && ${this._inputDevices &&
this._inputDevices.map( this._inputDevices.map((item) => {
(item) => html` return html`
<paper-item device=${item.device || ""}> <paper-item device=${item.device || ""}>
${item.name} ${item.name}
</paper-item> </paper-item>
` `;
)} })}
</paper-listbox> </paper-listbox>
</paper-dropdown-menu> </paper-dropdown-menu>
<paper-dropdown-menu <paper-dropdown-menu
@@ -88,13 +90,13 @@ class HassioAddonAudio extends LitElement {
.selected=${this._selectedOutput!} .selected=${this._selectedOutput!}
> >
${this._outputDevices && ${this._outputDevices &&
this._outputDevices.map( this._outputDevices.map((item) => {
(item) => html` return html`
<paper-item device=${item.device || ""} <paper-item device=${item.device || ""}
>${item.name}</paper-item >${item.name}</paper-item
> >
` `;
)} })}
</paper-listbox> </paper-listbox>
</paper-dropdown-menu> </paper-dropdown-menu>
</div> </div>
@@ -107,7 +109,7 @@ class HassioAddonAudio extends LitElement {
`; `;
} }
static get styles(): CSSResultGroup { static get styles(): CSSResult[] {
return [ return [
haStyle, haStyle,
hassioStyle, hassioStyle,

View File

@@ -1,5 +1,12 @@
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import {
import { customElement, property } from "lit/decorators"; css,
CSSResult,
customElement,
html,
LitElement,
property,
TemplateResult,
} from "lit-element";
import "../../../../src/components/ha-circular-progress"; import "../../../../src/components/ha-circular-progress";
import { HassioAddonDetails } from "../../../../src/data/hassio/addon"; import { HassioAddonDetails } from "../../../../src/data/hassio/addon";
import { Supervisor } from "../../../../src/data/supervisor/supervisor"; import { Supervisor } from "../../../../src/data/supervisor/supervisor";
@@ -63,7 +70,7 @@ class HassioAddonConfigDashboard extends LitElement {
`; `;
} }
static get styles(): CSSResultGroup { static get styles(): CSSResult[] {
return [ return [
haStyle, haStyle,
hassioStyle, hassioStyle,

View File

@@ -2,16 +2,19 @@ import "@material/mwc-button";
import { ActionDetail } from "@material/mwc-list"; import { ActionDetail } from "@material/mwc-list";
import "@material/mwc-list/mwc-list-item"; import "@material/mwc-list/mwc-list-item";
import { mdiDotsVertical } from "@mdi/js"; import { mdiDotsVertical } from "@mdi/js";
import { DEFAULT_SCHEMA, Type } from "js-yaml"; import "@polymer/iron-autogrow-textarea/iron-autogrow-textarea";
import { import {
css, css,
CSSResultGroup, CSSResult,
customElement,
html, html,
internalProperty,
LitElement, LitElement,
property,
PropertyValues, PropertyValues,
query,
TemplateResult, TemplateResult,
} from "lit"; } from "lit-element";
import { customElement, property, query, state } from "lit/decorators";
import memoizeOne from "memoize-one"; import memoizeOne from "memoize-one";
import { fireEvent } from "../../../../src/common/dom/fire_event"; import { fireEvent } from "../../../../src/common/dom/fire_event";
import "../../../../src/components/buttons/ha-progress-button"; import "../../../../src/components/buttons/ha-progress-button";
@@ -27,7 +30,6 @@ import {
HassioAddonDetails, HassioAddonDetails,
HassioAddonSetOptionParams, HassioAddonSetOptionParams,
setHassioAddonOption, setHassioAddonOption,
validateHassioAddonOption,
} from "../../../../src/data/hassio/addon"; } from "../../../../src/data/hassio/addon";
import { extractApiErrorMessage } from "../../../../src/data/hassio/common"; import { extractApiErrorMessage } from "../../../../src/data/hassio/common";
import { Supervisor } from "../../../../src/data/supervisor/supervisor"; import { Supervisor } from "../../../../src/data/supervisor/supervisor";
@@ -39,13 +41,6 @@ import { hassioStyle } from "../../resources/hassio-style";
const SUPPORTED_UI_TYPES = ["string", "select", "boolean", "integer", "float"]; 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") @customElement("hassio-addon-config")
class HassioAddonConfig extends LitElement { class HassioAddonConfig extends LitElement {
@property({ attribute: false }) public addon!: HassioAddonDetails; @property({ attribute: false }) public addon!: HassioAddonDetails;
@@ -58,27 +53,31 @@ class HassioAddonConfig extends LitElement {
@property({ type: Boolean }) private _valid = true; @property({ type: Boolean }) private _valid = true;
@state() private _canShowSchema = false; @internalProperty() private _canShowSchema = false;
@state() private _showOptional = false; @internalProperty() private _showOptional = false;
@state() private _error?: string; @internalProperty() private _error?: string;
@state() private _options?: Record<string, unknown>; @internalProperty() private _options?: Record<string, unknown>;
@state() private _yamlMode = false; @internalProperty() private _yamlMode = false;
@query("ha-yaml-editor") private _editor?: HaYamlEditor; @query("ha-yaml-editor") private _editor?: HaYamlEditor;
public computeLabel = (entry: HaFormSchema): string => public computeLabel = (entry: HaFormSchema): string => {
this.addon.translations[this.hass.language]?.configuration?.[entry.name] return (
?.name || this.addon.translations[this.hass.language]?.configuration?.[entry.name]
this.addon.translations.en?.configuration?.[entry.name].name || ?.name ||
entry.name; this.addon.translations.en?.configuration?.[entry.name].name ||
entry.name
);
};
private _filteredShchema = memoizeOne( private _filteredShchema = memoizeOne(
(options: Record<string, unknown>, schema: HaFormSchema[]) => (options: Record<string, unknown>, schema: HaFormSchema[]) => {
schema.filter((entry) => entry.name in options || entry.required) return schema.filter((entry) => entry.name in options || entry.required);
}
); );
protected render(): TemplateResult { protected render(): TemplateResult {
@@ -133,7 +132,6 @@ class HassioAddonConfig extends LitElement {
></ha-form>` ></ha-form>`
: html` <ha-yaml-editor : html` <ha-yaml-editor
@value-changed=${this._configChanged} @value-changed=${this._configChanged}
.yamlSchema=${ADDON_YAML_SCHEMA}
></ha-yaml-editor>`} ></ha-yaml-editor>`}
${this._error ? html` <div class="errors">${this._error}</div> ` : ""} ${this._error ? html` <div class="errors">${this._error}</div> ` : ""}
${!this._yamlMode || ${!this._yamlMode ||
@@ -268,48 +266,36 @@ class HassioAddonConfig extends LitElement {
private async _saveTapped(ev: CustomEvent): Promise<void> { private async _saveTapped(ev: CustomEvent): Promise<void> {
const button = ev.currentTarget as any; 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; button.progress = true;
this._error = undefined; this._error = undefined;
try { 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, { await setHassioAddonOption(this.hass, this.addon.slug, {
options, options: this._yamlMode ? this._editor?.value : this._options,
}); });
this._configHasChanged = false; this._configHasChanged = false;
const eventdata = {
success: true,
response: undefined,
path: "options",
};
fireEvent(this, "hass-api-called", eventdata);
if (this.addon?.state === "started") { if (this.addon?.state === "started") {
await suggestAddonRestart(this, this.hass, this.supervisor, this.addon); await suggestAddonRestart(this, this.hass, this.supervisor, this.addon);
} }
} catch (err) { } catch (err) {
this._error = this.supervisor.localize( this._error = this.supervisor.localize(
"addon.failed_to_save", "addon.configuration.options.failed_to_save",
"error", "error",
extractApiErrorMessage(err) extractApiErrorMessage(err)
); );
eventdata.success = false;
} }
button.progress = false; button.progress = false;
fireEvent(this, "hass-api-called", eventdata);
} }
static get styles(): CSSResultGroup { static get styles(): CSSResult[] {
return [ return [
haStyle, haStyle,
hassioStyle, hassioStyle,
@@ -328,6 +314,10 @@ class HassioAddonConfig extends LitElement {
color: var(--error-color); color: var(--error-color);
margin-top: 16px; margin-top: 16px;
} }
iron-autogrow-textarea {
width: 100%;
font-family: var(--code-font-family, monospace);
}
.syntaxerror { .syntaxerror {
color: var(--error-color); color: var(--error-color);
} }

View File

@@ -1,13 +1,15 @@
import { PaperInputElement } from "@polymer/paper-input/paper-input"; import { PaperInputElement } from "@polymer/paper-input/paper-input";
import { import {
css, css,
CSSResultGroup, CSSResult,
customElement,
html, html,
internalProperty,
LitElement, LitElement,
property,
PropertyValues, PropertyValues,
TemplateResult, TemplateResult,
} from "lit"; } from "lit-element";
import { customElement, property, state } from "lit/decorators";
import { fireEvent } from "../../../../src/common/dom/fire_event"; import { fireEvent } from "../../../../src/common/dom/fire_event";
import "../../../../src/components/buttons/ha-progress-button"; import "../../../../src/components/buttons/ha-progress-button";
import "../../../../src/components/ha-card"; import "../../../../src/components/ha-card";
@@ -41,9 +43,9 @@ class HassioAddonNetwork extends LitElement {
@property({ attribute: false }) public addon!: HassioAddonDetails; @property({ attribute: false }) public addon!: HassioAddonDetails;
@state() private _error?: string; @internalProperty() private _error?: string;
@state() private _config?: NetworkItem[]; @internalProperty() private _config?: NetworkItem[];
public connectedCallback(): void { public connectedCallback(): void {
super.connectedCallback(); super.connectedCallback();
@@ -77,10 +79,12 @@ class HassioAddonNetwork extends LitElement {
"addon.configuration.network.host" "addon.configuration.network.host"
)} )}
</th> </th>
<th>${this.supervisor.localize("common.description")}</th> <th>
${this.supervisor.localize("common.description")}
</th>
</tr> </tr>
${this._config!.map( ${this._config!.map((item) => {
(item) => html` return html`
<tr> <tr>
<td>${item.container}</td> <td>${item.container}</td>
<td> <td>
@@ -96,8 +100,8 @@ class HassioAddonNetwork extends LitElement {
</td> </td>
<td>${this._computeDescription(item)}</td> <td>${this._computeDescription(item)}</td>
</tr> </tr>
` `;
)} })}
</tbody> </tbody>
</table> </table>
</div> </div>
@@ -120,20 +124,25 @@ class HassioAddonNetwork extends LitElement {
} }
} }
private _computeDescription = (item: NetworkItem): string => private _computeDescription = (item: NetworkItem): string => {
this.addon.translations[this.hass.language]?.network?.[item.container] return (
?.description || this.addon.translations[this.hass.language]?.network?.[item.container]
this.addon.translations.en?.network?.[item.container]?.description || ?.description ||
item.description; this.addon.translations.en?.network?.[item.container]?.description ||
item.description
);
};
private _setNetworkConfig(): void { private _setNetworkConfig(): void {
const network = this.addon.network || {}; const network = this.addon.network || {};
const description = this.addon.network_description || {}; const description = this.addon.network_description || {};
const items: NetworkItem[] = Object.keys(network).map((key) => ({ const items: NetworkItem[] = Object.keys(network).map((key) => {
container: key, return {
host: network[key], container: key,
description: description[key], host: network[key],
})); description: description[key],
};
});
this._config = items.sort((a, b) => (a.container > b.container ? 1 : -1)); this._config = items.sort((a, b) => (a.container > b.container ? 1 : -1));
} }
@@ -214,7 +223,7 @@ class HassioAddonNetwork extends LitElement {
button.progress = false; button.progress = false;
} }
static get styles(): CSSResultGroup { static get styles(): CSSResult[] {
return [ return [
haStyle, haStyle,
hassioStyle, hassioStyle,

View File

@@ -1,8 +1,16 @@
import "../../../../src/components/ha-card"; import "../../../../src/components/ha-card";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import {
css,
CSSResult,
customElement,
html,
internalProperty,
LitElement,
property,
TemplateResult,
} from "lit-element";
import "../../../../src/components/ha-circular-progress"; import "../../../../src/components/ha-circular-progress";
import "../../../../src/components/ha-markdown"; import "../../../../src/components/ha-markdown";
import { customElement, property, state } from "lit/decorators";
import { import {
fetchHassioAddonDocumentation, fetchHassioAddonDocumentation,
HassioAddonDetails, HassioAddonDetails,
@@ -22,9 +30,9 @@ class HassioAddonDocumentationDashboard extends LitElement {
@property({ attribute: false }) public addon?: HassioAddonDetails; @property({ attribute: false }) public addon?: HassioAddonDetails;
@state() private _error?: string; @internalProperty() private _error?: string;
@state() private _content?: string; @internalProperty() private _content?: string;
public async connectedCallback(): Promise<void> { public async connectedCallback(): Promise<void> {
super.connectedCallback(); super.connectedCallback();
@@ -49,7 +57,7 @@ class HassioAddonDocumentationDashboard extends LitElement {
`; `;
} }
static get styles(): CSSResultGroup { static get styles(): CSSResult[] {
return [ return [
haStyle, haStyle,
hassioStyle, hassioStyle,

View File

@@ -4,8 +4,16 @@ import {
mdiInformationVariant, mdiInformationVariant,
mdiMathLog, mdiMathLog,
} from "@mdi/js"; } from "@mdi/js";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import {
import { customElement, property, state } from "lit/decorators"; css,
CSSResult,
customElement,
html,
internalProperty,
LitElement,
property,
TemplateResult,
} from "lit-element";
import memoizeOne from "memoize-one"; import memoizeOne from "memoize-one";
import { fireEvent } from "../../../src/common/dom/fire_event"; import { fireEvent } from "../../../src/common/dom/fire_event";
import { navigate } from "../../../src/common/navigate"; import { navigate } from "../../../src/common/navigate";
@@ -44,7 +52,7 @@ class HassioAddonDashboard extends LitElement {
@property({ type: Boolean }) public narrow!: boolean; @property({ type: Boolean }) public narrow!: boolean;
@state() _error?: string; @internalProperty() _error?: string;
private _computeTail = memoizeOne((route: Route) => { private _computeTail = memoizeOne((route: Route) => {
const dividerPos = route.path.indexOf("/", 1); const dividerPos = route.path.indexOf("/", 1);
@@ -125,7 +133,7 @@ class HassioAddonDashboard extends LitElement {
`; `;
} }
static get styles(): CSSResultGroup { static get styles(): CSSResult[] {
return [ return [
haStyle, haStyle,
hassioStyle, hassioStyle,
@@ -169,13 +177,12 @@ class HassioAddonDashboard extends LitElement {
const requestedAddon = extractSearchParam("addon"); const requestedAddon = extractSearchParam("addon");
if (requestedAddon) { if (requestedAddon) {
const addonsInfo = await fetchHassioAddonsInfo(this.hass); const addonsInfo = await fetchHassioAddonsInfo(this.hass);
const validAddon = addonsInfo.addons.some( const validAddon = addonsInfo.addons
(addon) => addon.slug === requestedAddon .some((addon) => addon.slug === requestedAddon);
);
if (!validAddon) { if (!validAddon) {
this._error = this.supervisor.localize("my.error_addon_not_found"); this._error = this.supervisor.localize("my.error_addon_not_found");
} else { } else {
navigate(`/hassio/addon/${requestedAddon}`, { replace: true }); navigate(this, `/hassio/addon/${requestedAddon}`, true);
} }
} }
} }
@@ -183,10 +190,6 @@ class HassioAddonDashboard extends LitElement {
} }
private async _apiCalled(ev): Promise<void> { private async _apiCalled(ev): Promise<void> {
if (!ev.detail.success) {
return;
}
const pathSplit: string[] = ev.detail.path?.split("/"); const pathSplit: string[] = ev.detail.path?.split("/");
if (!pathSplit || pathSplit.length === 0) { if (!pathSplit || pathSplit.length === 0) {

View File

@@ -1,4 +1,4 @@
import { customElement, property } from "lit/decorators"; import { customElement, property } from "lit-element";
import { HassioAddonDetails } from "../../../src/data/hassio/addon"; import { HassioAddonDetails } from "../../../src/data/hassio/addon";
import { Supervisor } from "../../../src/data/supervisor/supervisor"; import { Supervisor } from "../../../src/data/supervisor/supervisor";
import { import {

View File

@@ -1,5 +1,12 @@
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import {
import { customElement, property } from "lit/decorators"; css,
CSSResult,
customElement,
html,
LitElement,
property,
TemplateResult,
} from "lit-element";
import "../../../../src/components/ha-circular-progress"; import "../../../../src/components/ha-circular-progress";
import { HassioAddonDetails } from "../../../../src/data/hassio/addon"; import { HassioAddonDetails } from "../../../../src/data/hassio/addon";
import { Supervisor } from "../../../../src/data/supervisor/supervisor"; import { Supervisor } from "../../../../src/data/supervisor/supervisor";
@@ -35,7 +42,7 @@ class HassioAddonInfoDashboard extends LitElement {
`; `;
} }
static get styles(): CSSResultGroup { static get styles(): CSSResult[] {
return [ return [
haStyle, haStyle,
hassioStyle, hassioStyle,

View File

@@ -14,9 +14,17 @@ import {
mdiPound, mdiPound,
mdiShield, mdiShield,
} from "@mdi/js"; } from "@mdi/js";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import {
import { customElement, property, state } from "lit/decorators"; css,
import { classMap } from "lit/directives/class-map"; CSSResult,
customElement,
html,
internalProperty,
LitElement,
property,
TemplateResult,
} from "lit-element";
import { classMap } from "lit-html/directives/class-map";
import memoizeOne from "memoize-one"; import memoizeOne from "memoize-one";
import { atLeastVersion } from "../../../../src/common/config/version"; import { atLeastVersion } from "../../../../src/common/config/version";
import { fireEvent } from "../../../../src/common/dom/fire_event"; import { fireEvent } from "../../../../src/common/dom/fire_event";
@@ -82,9 +90,9 @@ class HassioAddonInfo extends LitElement {
@property({ attribute: false }) public supervisor!: Supervisor; @property({ attribute: false }) public supervisor!: Supervisor;
@state() private _metrics?: HassioStats; @internalProperty() private _metrics?: HassioStats;
@state() private _error?: string; @internalProperty() private _error?: string;
private _addonStoreInfo = memoizeOne( private _addonStoreInfo = memoizeOne(
(slug: string, storeAddons: StoreAddon[]) => (slug: string, storeAddons: StoreAddon[]) =>
@@ -163,16 +171,16 @@ class HassioAddonInfo extends LitElement {
: ""} : ""}
</div> </div>
<div class="card-actions"> <div class="card-actions">
<mwc-button @click=${this._updateClicked}>
${this.supervisor.localize("common.update")}
</mwc-button>
${this.addon.changelog ${this.addon.changelog
? html` ? html`
<mwc-button @click=${this._openChangelog}> <mwc-button @click=${this._openChangelog}>
${this.supervisor.localize("addon.dashboard.changelog")} ${this.supervisor.localize("addon.dashboard.changelog")}
</mwc-button> </mwc-button>
` `
: html`<span></span>`} : ""}
<mwc-button @click=${this._updateClicked}>
${this.supervisor.localize("common.update")}
</mwc-button>
</div> </div>
</ha-card> </ha-card>
` `
@@ -234,18 +242,14 @@ class HassioAddonInfo extends LitElement {
? html` ? html`
Current version: ${this.addon.version} Current version: ${this.addon.version}
<div class="changelog" @click=${this._openChangelog}> <div class="changelog" @click=${this._openChangelog}>
(<span class="changelog-link" (<span class="changelog-link">${
>${this.supervisor.localize( this.supervisor.localize("addon.dashboard.changelog")}</span
"addon.dashboard.changelog"
)}</span
>) >)
</div> </div>
` `
: html`<span class="changelog-link" @click=${this._openChangelog} : html`<span class="changelog-link" @click=${this._openChangelog}>${
>${this.supervisor.localize( this.supervisor.localize("addon.dashboard.changelog")
"addon.dashboard.changelog" }</span>`}
)}</span
>`}
</div> </div>
<div class="description light-color"> <div class="description light-color">
@@ -253,9 +257,13 @@ class HassioAddonInfo extends LitElement {
${this.supervisor.localize( ${this.supervisor.localize(
"addon.dashboard.visit_addon_page", "addon.dashboard.visit_addon_page",
"name", "name",
html`<a href="${this.addon.url!}" target="_blank" rel="noreferrer" html`<a
>${this.addon.name}</a href="${this.addon.url!}"
>` target="_blank"
rel="noreferrer"
>
${this.addon.name}
</a>`
)} )}
</div> </div>
<div class="addon-container"> <div class="addon-container">
@@ -554,7 +562,9 @@ class HassioAddonInfo extends LitElement {
<span slot="heading"> <span slot="heading">
${this.supervisor.localize("addon.dashboard.hostname")} ${this.supervisor.localize("addon.dashboard.hostname")}
</span> </span>
<code slot="description"> ${this.addon.hostname} </code> <code slot="description">
${this.addon.hostname}
</code>
</ha-settings-row> </ha-settings-row>
${metrics.map( ${metrics.map(
(metric) => (metric) =>
@@ -761,7 +771,7 @@ class HassioAddonInfo extends LitElement {
} }
private _openIngress(): void { private _openIngress(): void {
navigate(`/hassio/ingress/${this.addon.slug}`); navigate(this, `/hassio/ingress/${this.addon.slug}`);
} }
private get _computeShowIngressUI(): boolean { private get _computeShowIngressUI(): boolean {
@@ -892,19 +902,10 @@ class HassioAddonInfo extends LitElement {
private async _openChangelog(): Promise<void> { private async _openChangelog(): Promise<void> {
try { try {
let content = await fetchHassioAddonChangelog(this.hass, this.addon.slug); const content = await fetchHassioAddonChangelog(
if ( this.hass,
content.includes(`# ${this.addon.version}`) && this.addon.slug
content.includes(`# ${this.addon.version_latest}`) );
) {
const newcontent = content.split(`# ${this.addon.version}`)[0];
if (newcontent.includes(`# ${this.addon.version_latest}`)) {
// Only change the content if the new version still exist
// if the changelog does not have the newests version on top
// this will not be true, and we don't modify the content
content = newcontent;
}
}
showHassioMarkdownDialog(this, { showHassioMarkdownDialog(this, {
title: this.supervisor.localize("addon.dashboard.changelog"), title: this.supervisor.localize("addon.dashboard.changelog"),
content, content,
@@ -992,7 +993,7 @@ class HassioAddonInfo extends LitElement {
addons: [this.addon.slug], addons: [this.addon.slug],
homeassistant: false, homeassistant: false,
}, },
updateHandler: async () => this._updateAddon(), updateHandler: async () => await this._updateAddon(),
}); });
} }
@@ -1060,7 +1061,7 @@ class HassioAddonInfo extends LitElement {
} }
private _openConfiguration(): void { private _openConfiguration(): void {
navigate(`/hassio/addon/${this.addon.slug}/config`); navigate(this, `/hassio/addon/${this.addon.slug}/config`);
} }
private async _uninstallClicked(ev: CustomEvent): Promise<void> { private async _uninstallClicked(ev: CustomEvent): Promise<void> {
@@ -1099,7 +1100,7 @@ class HassioAddonInfo extends LitElement {
button.progress = false; button.progress = false;
} }
static get styles(): CSSResultGroup { static get styles(): CSSResult[] {
return [ return [
haStyle, haStyle,
hassioStyle, hassioStyle,

View File

@@ -1,5 +1,12 @@
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import {
import { customElement, property } from "lit/decorators"; css,
CSSResult,
customElement,
html,
LitElement,
property,
TemplateResult,
} from "lit-element";
import "../../../../src/components/ha-circular-progress"; import "../../../../src/components/ha-circular-progress";
import { HassioAddonDetails } from "../../../../src/data/hassio/addon"; import { HassioAddonDetails } from "../../../../src/data/hassio/addon";
import { Supervisor } from "../../../../src/data/supervisor/supervisor"; import { Supervisor } from "../../../../src/data/supervisor/supervisor";
@@ -31,7 +38,7 @@ class HassioAddonLogDashboard extends LitElement {
`; `;
} }
static get styles(): CSSResultGroup { static get styles(): CSSResult[] {
return [ return [
haStyle, haStyle,
hassioStyle, hassioStyle,

View File

@@ -1,6 +1,14 @@
import "@material/mwc-button"; import "@material/mwc-button";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import {
import { customElement, property, state } from "lit/decorators"; css,
CSSResult,
customElement,
html,
internalProperty,
LitElement,
property,
TemplateResult,
} from "lit-element";
import "../../../../src/components/ha-card"; import "../../../../src/components/ha-card";
import { import {
fetchHassioAddonLogs, fetchHassioAddonLogs,
@@ -21,9 +29,9 @@ class HassioAddonLogs extends LitElement {
@property({ attribute: false }) public addon!: HassioAddonDetails; @property({ attribute: false }) public addon!: HassioAddonDetails;
@state() private _error?: string; @internalProperty() private _error?: string;
@state() private _content?: string; @internalProperty() private _content?: string;
public async connectedCallback(): Promise<void> { public async connectedCallback(): Promise<void> {
super.connectedCallback(); super.connectedCallback();
@@ -51,7 +59,7 @@ class HassioAddonLogs extends LitElement {
`; `;
} }
static get styles(): CSSResultGroup { static get styles(): CSSResult[] {
return [ return [
haStyle, haStyle,
hassioStyle, hassioStyle,

View File

@@ -1,5 +1,12 @@
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import {
import { customElement, property } from "lit/decorators"; css,
CSSResult,
customElement,
html,
LitElement,
property,
TemplateResult,
} from "lit-element";
interface State { interface State {
bold: boolean; bold: boolean;
@@ -18,7 +25,7 @@ class HassioAnsiToHtml extends LitElement {
return html`${this._parseTextToColoredPre(this.content)}`; return html`${this._parseTextToColoredPre(this.content)}`;
} }
static get styles(): CSSResultGroup { static get styles(): CSSResult {
return css` return css`
pre { pre {
overflow-x: auto; overflow-x: auto;

View File

@@ -1,6 +1,13 @@
import { mdiHelpCircle } from "@mdi/js"; import { mdiHelpCircle } from "@mdi/js";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import {
import { customElement, property } from "lit/decorators"; css,
CSSResult,
customElement,
html,
LitElement,
property,
TemplateResult,
} from "lit-element";
import "../../../src/components/ha-relative-time"; import "../../../src/components/ha-relative-time";
import "../../../src/components/ha-svg-icon"; import "../../../src/components/ha-svg-icon";
import { HomeAssistant } from "../../../src/types"; import { HomeAssistant } from "../../../src/types";
@@ -49,13 +56,13 @@ class HassioCardContent extends LitElement {
></ha-svg-icon> ></ha-svg-icon>
`} `}
<div> <div>
<div class="title">${this.title}</div> <div class="title">
${this.title}
</div>
<div class="addition"> <div class="addition">
${this.description} ${this.description}
${ ${/* treat as available when undefined */
/* treat as available when undefined */ this.available === false ? " (Not available)" : ""}
this.available === false ? " (Not available)" : ""
}
${this.datetime ${this.datetime
? html` ? html`
<ha-relative-time <ha-relative-time
@@ -70,7 +77,7 @@ class HassioCardContent extends LitElement {
`; `;
} }
static get styles(): CSSResultGroup { static get styles(): CSSResult {
return css` return css`
ha-svg-icon { ha-svg-icon {
margin-right: 24px; margin-right: 24px;

View File

@@ -1,8 +1,14 @@
import "@material/mwc-icon-button/mwc-icon-button"; import "@material/mwc-icon-button/mwc-icon-button";
import { mdiFolderUpload } from "@mdi/js"; import { mdiFolderUpload } from "@mdi/js";
import "@polymer/iron-input/iron-input";
import "@polymer/paper-input/paper-input-container"; import "@polymer/paper-input/paper-input-container";
import { html, LitElement, TemplateResult } from "lit"; import {
import { customElement, state } from "lit/decorators"; customElement,
html,
internalProperty,
LitElement,
TemplateResult,
} from "lit-element";
import { fireEvent } from "../../../src/common/dom/fire_event"; import { fireEvent } from "../../../src/common/dom/fire_event";
import "../../../src/components/ha-circular-progress"; import "../../../src/components/ha-circular-progress";
import "../../../src/components/ha-file-upload"; import "../../../src/components/ha-file-upload";
@@ -27,9 +33,9 @@ const MAX_FILE_SIZE = 1 * 1024 * 1024 * 1024; // 1GB
export class HassioUploadSnapshot extends LitElement { export class HassioUploadSnapshot extends LitElement {
public hass!: HomeAssistant; public hass!: HomeAssistant;
@state() public value: string | null = null; @internalProperty() public value: string | null = null;
@state() private _uploading = false; @internalProperty() private _uploading = false;
public render(): TemplateResult { public render(): TemplateResult {
return html` return html`

Some files were not shown because too many files have changed in this diff Show More