Compare commits

..

1 Commits

Author SHA1 Message Date
Thomas Lovén
813c9014e5 Avoid error in map-card 2021-05-20 16:18:31 +02:00
1334 changed files with 69081 additions and 137134 deletions

View File

@@ -16,9 +16,6 @@
"runem.lit-plugin", "runem.lit-plugin",
"ms-python.vscode-pylance" "ms-python.vscode-pylance"
], ],
"containerEnv": {
"WORKSPACE_DIRECTORY": "${containerWorkspaceFolder}"
},
"settings": { "settings": {
"terminal.integrated.shell.linux": "/bin/bash", "terminal.integrated.shell.linux": "/bin/bash",
"files.eol": "\n", "files.eol": "\n",

View File

@@ -1,10 +1,9 @@
{ {
"extends": [ "extends": [
"airbnb-base",
"airbnb-typescript/base", "airbnb-typescript/base",
"plugin:@typescript-eslint/recommended", "plugin:@typescript-eslint/recommended",
"plugin:wc/recommended", "plugin:wc/recommended",
"plugin:lit/all", "plugin:lit/recommended",
"prettier" "prettier"
], ],
"parser": "@typescript-eslint/parser", "parser": "@typescript-eslint/parser",
@@ -29,59 +28,64 @@
"__BUILD__": false, "__BUILD__": false,
"__VERSION__": false, "__VERSION__": false,
"__STATIC_PATH__": false, "__STATIC_PATH__": false,
"__SUPERVISOR__": 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": [ "@typescript-eslint/naming-convention": [
"off", 0,
{ {
"selector": "default", "selector": "default",
"format": ["camelCase", "snake_case"], "format": ["camelCase", "snake_case"],
@@ -99,21 +103,8 @@
"format": ["PascalCase"] "format": ["PascalCase"]
} }
], ],
"@typescript-eslint/no-unused-vars": "off", "lit/attribute-value-entities": 0
"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",
"lit/no-template-map": "off"
}, },
"plugins": ["disable", "unused-imports"], "plugins": ["disable", "import", "lit", "prettier", "@typescript-eslint"],
"processor": "disable/disable" "processor": "disable/disable"
} }

View File

@@ -10,64 +10,83 @@ on:
- dev - dev
- master - master
env:
NODE_VERSION: 14
NODE_OPTIONS: --max_old_space_size=6144
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:
CI: true CI: true
- name: Build resources - name: Build resources
run: ./node_modules/.bin/gulp gen-icons-json build-translations build-locale-data gather-gallery-pages 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: yarn run lint:eslint
- name: Run tsc - name: Run tsc
run: yarn run lint:types run: yarn run lint:types
- name: Run prettier - name: Run prettier
run: yarn run lint: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: Build resources - name: Run Mocha
run: ./node_modules/.bin/gulp build-translations build-locale-data run: npm run mocha
- name: Run Tests
run: yarn run test
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:
@@ -82,11 +101,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=6144
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

@@ -15,5 +15,5 @@ jobs:
- name: Trigger Demo build - name: Trigger Demo build
run: curl -X POST -d {} https://api.netlify.com/build_hooks/${{ secrets.NETLIFY_DEMO_DEV_BUILD_HOOK }} run: curl -X POST -d {} https://api.netlify.com/build_hooks/${{ secrets.NETLIFY_DEMO_DEV_BUILD_HOOK }}
- name: Trigger Design build - name: Trigger Gallery build
run: curl -X POST -d "NIGHTLY" https://api.netlify.com/build_hooks/${{ secrets.NETLIFY_GALLERY_DEV_BUILD_HOOK }} run: curl -X POST -d {} https://api.netlify.com/build_hooks/${{ secrets.NETLIFY_GALLERY_DEV_BUILD_HOOK }}

View File

@@ -6,22 +6,14 @@ on:
- published - published
env: env:
WHEELS_TAG: 3.8-alpine3.12
PYTHON_VERSION: 3.8 PYTHON_VERSION: 3.8
NODE_VERSION: 14 NODE_VERSION: 12.1
NODE_OPTIONS: --max_old_space_size=6144
# Set default workflow permissions
# All scopes not mentioned here are set to no access
# https://docs.github.com/en/actions/security-guides/automatic-token-authentication#permissions-for-the-github_token
permissions:
actions: none
jobs: jobs:
release: release:
name: Release name: Release
runs-on: ubuntu-latest runs-on: ubuntu-latest
permissions:
contents: write # Required to upload release assets
steps: steps:
- name: Checkout the repository - name: Checkout the repository
uses: actions/checkout@v2 uses: actions/checkout@v2
@@ -38,30 +30,15 @@ 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 build python3 -m pip install twine
export TWINE_USERNAME="__token__" export TWINE_USERNAME="__token__"
export TWINE_PASSWORD="${{ secrets.TWINE_TOKEN }}" export TWINE_PASSWORD="${{ secrets.TWINE_TOKEN }}"
script/release script/release
- name: Upload release assets
uses: softprops/action-gh-release@v0.1.14
with:
files: |
dist/*.whl
dist/*.tar.gz
wheels-init: wheels-init:
name: Init wheels build name: Init wheels build
needs: release needs: release
@@ -87,8 +64,6 @@ jobs:
strategy: strategy:
matrix: matrix:
arch: ["aarch64", "armhf", "armv7", "amd64", "i386"] arch: ["aarch64", "armhf", "armv7", "amd64", "i386"]
tag:
- "3.9-alpine3.14"
steps: steps:
- name: Download requirements.txt - name: Download requirements.txt
uses: actions/download-artifact@v2 uses: actions/download-artifact@v2
@@ -98,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

28
.gitignore vendored
View File

@@ -1,22 +1,10 @@
.DS_Store
.reify-cache
# build
build build
hass_frontend/* build-translations/*
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]
@@ -26,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/*
@@ -40,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

2
.nvmrc
View File

@@ -1 +1 @@
14 12.1

View File

@@ -1,4 +1,5 @@
build build
build-translations/*
translations/* translations/*
node_modules/* node_modules/*
hass_frontend/* hass_frontend/*

View File

@@ -1,29 +0,0 @@
diff --git a/polyfillLoaders/EventTarget.js b/polyfillLoaders/EventTarget.js
index 4e18ade7ba485849f17f28c94c42f0e0e01ac387..8f34f4f646c7f7becc208fb5a546c96034fc74dc 100644
--- a/polyfillLoaders/EventTarget.js
+++ b/polyfillLoaders/EventTarget.js
@@ -6,16 +6,15 @@
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);
}
//# sourceMappingURL=EventTarget.js.map

View File

@@ -1,12 +0,0 @@
diff --git a/mwc-icon-button-base.js b/mwc-icon-button-base.js
index 45cdaab93ccc0a6daaaaabc01266dcdc32e46bfd..b3ea5b541597308d85f86ce6c23fd00785fda835 100644
--- a/mwc-icon-button-base.js
+++ b/mwc-icon-button-base.js
@@ -63,7 +63,6 @@ export class IconButtonBase extends LitElement {
@touchend="${this.handleRippleDeactivate}"
@touchcancel="${this.handleRippleDeactivate}"
>${this.renderRipple()}
- <i class="material-icons">${this.icon}</i>
<span
><slot></slot
></span>

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-3.2.0.cjs

View File

@@ -1,4 +1,5 @@
include README.md include README.md
include LICENSE.md
graft hass_frontend graft hass_frontend
graft hass_frontend_es5 graft hass_frontend_es5
recursive-exclude * *.py[co] recursive-exclude * *.py[co]

View File

@@ -2,7 +2,7 @@
This is the repository for the official [Home Assistant](https://home-assistant.io) frontend. This is the repository for the official [Home Assistant](https://home-assistant.io) frontend.
[![Screenshot of the frontend](https://raw.githubusercontent.com/home-assistant/frontend/master/docs/screenshot.png)](https://demo.home-assistant.io/) [![Screenshot of the frontend](https://raw.githubusercontent.com/home-assistant/home-assistant-polymer/master/docs/screenshot.png)](https://demo.home-assistant.io/)
- [View demo of Home Assistant](https://demo.home-assistant.io/) - [View demo of Home Assistant](https://demo.home-assistant.io/)
- [More information about Home Assistant](https://home-assistant.io) - [More information about Home Assistant](https://home-assistant.io)

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

@@ -5,12 +5,14 @@ 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"),
]; ];
// Files from NPM packages that we should replace with empty file // Files from NPM packages that we should replace with empty file
module.exports.emptyPackages = ({ latestBuild, isHassioBuild }) => module.exports.emptyPackages = ({ latestBuild }) =>
[ [
// Contains all color definitions for all material color sets. // Contains all color definitions for all material color sets.
// We don't use it // We don't use it
@@ -18,8 +20,7 @@ module.exports.emptyPackages = ({ latestBuild, isHassioBuild }) =>
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
@@ -28,15 +29,6 @@ module.exports.emptyPackages = ({ latestBuild, isHassioBuild }) =>
), ),
// This polyfill is loaded in workers to support ES5, filter it out. // This polyfill is loaded in workers to support ES5, filter it out.
latestBuild && require.resolve("proxy-polyfill/src/index.js"), latestBuild && require.resolve("proxy-polyfill/src/index.js"),
// Icons in supervisor conflict with icons in HA so we don't load.
isHassioBuild &&
require.resolve(
path.resolve(paths.polymer_dir, "src/components/ha-icon.ts")
),
isHassioBuild &&
require.resolve(
path.resolve(paths.polymer_dir, "src/components/ha-icon-picker.ts")
),
].filter(Boolean); ].filter(Boolean);
module.exports.definedVars = ({ isProdBuild, latestBuild, defineOverlay }) => ({ module.exports.definedVars = ({ isProdBuild, latestBuild, defineOverlay }) => ({
@@ -44,7 +36,6 @@ module.exports.definedVars = ({ isProdBuild, latestBuild, defineOverlay }) => ({
__BUILD__: JSON.stringify(latestBuild ? "latest" : "es5"), __BUILD__: JSON.stringify(latestBuild ? "latest" : "es5"),
__VERSION__: JSON.stringify(env.version()), __VERSION__: JSON.stringify(env.version()),
__DEMO__: false, __DEMO__: false,
__SUPERVISOR__: false,
__BACKWARDS_COMPAT__: false, __BACKWARDS_COMPAT__: false,
__STATIC_PATH__: "/static/", __STATIC_PATH__: "/static/",
"process.env.NODE_ENV": JSON.stringify( "process.env.NODE_ENV": JSON.stringify(
@@ -61,29 +52,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", "@babel/preset-env",
{ {
useBuiltIns: "entry", useBuiltIns: "entry",
corejs: "3.15", corejs: "3.6",
bugfixes: true,
}, },
], ],
"@babel/preset-typescript", "@babel/preset-typescript",
].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",
@@ -92,21 +71,20 @@ module.exports.babelOptions = ({ latestBuild }) => ({
// Only support the syntax, Webpack will handle it. // Only support the syntax, Webpack will handle it.
"@babel/plugin-syntax-import-meta", "@babel/plugin-syntax-import-meta",
"@babel/plugin-syntax-dynamic-import", "@babel/plugin-syntax-dynamic-import",
"@babel/plugin-syntax-top-level-await",
"@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-decorators", { decoratorsBeforeExport: true }],
["@babel/plugin-proposal-private-methods", { loose: true }], ["@babel/plugin-proposal-private-methods", { loose: true }],
["@babel/plugin-proposal-private-property-in-object", { loose: true }],
["@babel/plugin-proposal-class-properties", { loose: true }], ["@babel/plugin-proposal-class-properties", { 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");
@@ -174,7 +152,6 @@ module.exports.config = {
cast({ isProdBuild, latestBuild }) { cast({ isProdBuild, latestBuild }) {
const entry = { const entry = {
launcher: path.resolve(paths.cast_dir, "src/launcher/entrypoint.ts"), launcher: path.resolve(paths.cast_dir, "src/launcher/entrypoint.ts"),
media: path.resolve(paths.cast_dir, "src/media/entrypoint.ts"),
}; };
if (latestBuild) { if (latestBuild) {
@@ -205,10 +182,6 @@ module.exports.config = {
publicPath: publicPath(latestBuild, paths.hassio_publicPath), publicPath: publicPath(latestBuild, paths.hassio_publicPath),
isProdBuild, isProdBuild,
latestBuild, latestBuild,
isHassioBuild: true,
defineOverlay: {
__SUPERVISOR__: true,
},
}; };
}, },
@@ -221,9 +194,6 @@ module.exports.config = {
publicPath: publicPath(latestBuild), publicPath: publicPath(latestBuild),
isProdBuild, isProdBuild,
latestBuild, latestBuild,
defineOverlay: {
__DEMO__: true,
},
}; };
}, },
}; };

View File

@@ -26,11 +26,11 @@ module.exports = {
}, },
version() { version() {
const version = fs const version = fs
.readFileSync(path.resolve(paths.polymer_dir, "setup.cfg"), "utf8") .readFileSync(path.resolve(paths.polymer_dir, "setup.py"), "utf8")
.match(/version\W+=\W(\d{8}\.\d)/); .match(/\d{8}\.\d+/);
if (!version) { if (!version) {
throw Error("Version not found"); throw Error("Version not found");
} }
return version[1]; return version[0];
}, },
}; };

View File

@@ -5,7 +5,6 @@ const env = require("../env");
require("./clean.js"); require("./clean.js");
require("./translations.js"); require("./translations.js");
require("./locale-data.js");
require("./gen-icons-json.js"); require("./gen-icons-json.js");
require("./gather-static.js"); require("./gather-static.js");
require("./compress.js"); require("./compress.js");
@@ -27,8 +26,7 @@ gulp.task(
"gen-icons-json", "gen-icons-json",
"gen-pages-dev", "gen-pages-dev",
"gen-index-app-dev", "gen-index-app-dev",
"build-translations", "build-translations"
"build-locale-data"
), ),
"copy-static-app", "copy-static-app",
env.useWDS() env.useWDS()
@@ -46,11 +44,11 @@ gulp.task(
process.env.NODE_ENV = "production"; process.env.NODE_ENV = "production";
}, },
"clean", "clean",
gulp.parallel("gen-icons-json", "build-translations", "build-locale-data"), 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

@@ -18,7 +18,7 @@ gulp.task(
}, },
"clean-cast", "clean-cast",
"translations-enable-merge-backend", "translations-enable-merge-backend",
gulp.parallel("gen-icons-json", "build-translations", "build-locale-data"), gulp.parallel("gen-icons-json", "build-translations"),
"copy-static-cast", "copy-static-cast",
"gen-index-cast-dev", "gen-index-cast-dev",
env.useRollup() ? "rollup-dev-server-cast" : "webpack-dev-server-cast" env.useRollup() ? "rollup-dev-server-cast" : "webpack-dev-server-cast"
@@ -33,7 +33,7 @@ gulp.task(
}, },
"clean-cast", "clean-cast",
"translations-enable-merge-backend", "translations-enable-merge-backend",
gulp.parallel("gen-icons-json", "build-translations", "build-locale-data"), gulp.parallel("gen-icons-json", "build-translations"),
"copy-static-cast", "copy-static-cast",
env.useRollup() ? "rollup-prod-cast" : "webpack-prod-cast", env.useRollup() ? "rollup-prod-cast" : "webpack-prod-cast",
"gen-index-cast-prod" "gen-index-cast-prod"

View File

@@ -5,32 +5,32 @@ require("./translations");
gulp.task( gulp.task(
"clean", "clean",
gulp.parallel("clean-translations", () => gulp.parallel("clean-translations", function cleanOutputAndBuildDir() {
del([paths.app_output_root, paths.build_dir]) return del([paths.app_output_root, paths.build_dir]);
) })
); );
gulp.task( gulp.task(
"clean-demo", "clean-demo",
gulp.parallel("clean-translations", () => gulp.parallel("clean-translations", function cleanOutputAndBuildDir() {
del([paths.demo_output_root, paths.build_dir]) return del([paths.demo_output_root, paths.build_dir]);
) })
); );
gulp.task( gulp.task(
"clean-cast", "clean-cast",
gulp.parallel("clean-translations", () => gulp.parallel("clean-translations", function cleanOutputAndBuildDir() {
del([paths.cast_output_root, paths.build_dir]) return del([paths.cast_output_root, paths.build_dir]);
) })
); );
gulp.task("clean-hassio", () => gulp.task("clean-hassio", function cleanOutputAndBuildDir() {
del([paths.hassio_output_root, paths.build_dir]) return del([paths.hassio_output_root, paths.build_dir]);
); });
gulp.task( gulp.task(
"clean-gallery", "clean-gallery",
gulp.parallel("clean-translations", () => gulp.parallel("clean-translations", function cleanOutputAndBuildDir() {
del([paths.gallery_output_root, paths.gallery_build, paths.build_dir]) return del([paths.gallery_output_root, paths.build_dir]);
) })
); );

View File

@@ -20,12 +20,7 @@ gulp.task(
}, },
"clean-demo", "clean-demo",
"translations-enable-merge-backend", "translations-enable-merge-backend",
gulp.parallel( gulp.parallel("gen-icons-json", "gen-index-demo-dev", "build-translations"),
"gen-icons-json",
"gen-index-demo-dev",
"build-translations",
"build-locale-data"
),
"copy-static-demo", "copy-static-demo",
env.useRollup() ? "rollup-dev-server-demo" : "webpack-dev-server-demo" env.useRollup() ? "rollup-dev-server-demo" : "webpack-dev-server-demo"
) )
@@ -40,7 +35,7 @@ gulp.task(
"clean-demo", "clean-demo",
// Cast needs to be backwards compatible and older HA has no translations // Cast needs to be backwards compatible and older HA has no translations
"translations-enable-merge-backend", "translations-enable-merge-backend",
gulp.parallel("gen-icons-json", "build-translations", "build-locale-data"), gulp.parallel("gen-icons-json", "build-translations"),
"copy-static-demo", "copy-static-demo",
env.useRollup() ? "rollup-prod-demo" : "webpack-prod-demo", env.useRollup() ? "rollup-prod-demo" : "webpack-prod-demo",
"gen-index-demo-prod" "gen-index-demo-prod"

View File

@@ -154,15 +154,6 @@ gulp.task("gen-index-cast-dev", (done) => {
contentReceiver contentReceiver
); );
const contentMedia = renderCastTemplate("media", {
latestMediaJS: "/frontend_latest/media.js",
es5MediaJS: "/frontend_es5/media.js",
});
fs.outputFileSync(
path.resolve(paths.cast_output_root, "media.html"),
contentMedia
);
const contentFAQ = renderCastTemplate("launcher-faq", { const contentFAQ = renderCastTemplate("launcher-faq", {
latestLauncherJS: "/frontend_latest/launcher.js", latestLauncherJS: "/frontend_latest/launcher.js",
es5LauncherJS: "/frontend_es5/launcher.js", es5LauncherJS: "/frontend_es5/launcher.js",
@@ -201,15 +192,6 @@ gulp.task("gen-index-cast-prod", (done) => {
contentReceiver contentReceiver
); );
const contentMedia = renderCastTemplate("media", {
latestMediaJS: latestManifest["media.js"],
es5MediaJS: es5Manifest["media.js"],
});
fs.outputFileSync(
path.resolve(paths.cast_output_root, "media.html"),
contentMedia
);
const contentFAQ = renderCastTemplate("launcher-faq", { const contentFAQ = renderCastTemplate("launcher-faq", {
latestLauncherJS: latestManifest["launcher.js"], latestLauncherJS: latestManifest["launcher.js"],
es5LauncherJS: es5Manifest["launcher.js"], es5LauncherJS: es5Manifest["launcher.js"],
@@ -320,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

@@ -1,11 +1,7 @@
/* eslint-disable */
// Run demo develop mode // Run demo develop mode
const gulp = require("gulp"); const gulp = require("gulp");
const fs = require("fs"); const fs = require("fs");
const path = require("path"); const path = require("path");
const { marked } = require("marked");
const glob = require("glob");
const yaml = require("js-yaml");
const env = require("../env"); const env = require("../env");
const paths = require("../paths"); const paths = require("../paths");
@@ -19,129 +15,26 @@ require("./service-worker.js");
require("./entry-html.js"); require("./entry-html.js");
require("./rollup.js"); require("./rollup.js");
gulp.task("gather-gallery-pages", async function gatherPages() { gulp.task("gather-gallery-demos", async function gatherDemos() {
const pageDir = path.resolve(paths.gallery_dir, "src/pages"); const files = await fs.promises.readdir(
const files = glob.sync(path.resolve(pageDir, "**/*")); path.resolve(paths.gallery_dir, "src/demos")
);
const galleryBuild = path.resolve(paths.gallery_dir, "build"); let content = "export const DEMOS = {\n";
fs.mkdirSync(galleryBuild, { recursive: true });
let content = "export const PAGES = {\n";
const processed = new Set();
for (const file of files) { for (const file of files) {
if (fs.lstatSync(file).isDirectory()) { const demoId = path.basename(file, ".ts");
continue; const demoPath = "../src/demos/" + demoId;
} content += ` "${demoId}": () => import("${demoPath}"),\n`;
const pageId = file.substring(pageDir.length + 1, file.lastIndexOf("."));
if (processed.has(pageId)) {
continue;
}
processed.add(pageId);
const [category, name] = pageId.split("/", 2);
const demoFile = path.resolve(pageDir, `${pageId}.ts`);
const descriptionFile = path.resolve(pageDir, `${pageId}.markdown`);
const hasDemo = fs.existsSync(demoFile);
let hasDescription = fs.existsSync(descriptionFile);
let metadata = {};
if (hasDescription) {
let descriptionContent = fs.readFileSync(descriptionFile, "utf-8");
if (descriptionContent.startsWith("---")) {
const metadataEnd = descriptionContent.indexOf("---", 3);
metadata = yaml.load(descriptionContent.substring(3, metadataEnd));
descriptionContent = descriptionContent
.substring(metadataEnd + 3)
.trim();
}
// If description is just metadata
if (descriptionContent === "") {
hasDescription = false;
} else {
descriptionContent = marked(descriptionContent).replace(/`/g, "\\`");
fs.mkdirSync(path.resolve(galleryBuild, category), { recursive: true });
fs.writeFileSync(
path.resolve(galleryBuild, `${pageId}-description.ts`),
`
import {html} from "lit";
export default html\`${descriptionContent}\`
`
);
}
}
content += ` "${pageId}": {
metadata: ${JSON.stringify(metadata)},
${
hasDescription
? `description: () => import("./${pageId}-description").then(m => m.default),`
: ""
}
${hasDemo ? `demo: () => import("../src/pages/${pageId}")` : ""}
},\n`;
} }
content += "};\n"; content += "};";
// Generate sidebar const galleryBuild = path.resolve(paths.gallery_dir, "build");
const sidebarPath = path.resolve(paths.gallery_dir, "sidebar.js");
// To make watch work during development
delete require.cache[sidebarPath];
const sidebar = require(sidebarPath);
const pagesToProcess = {};
for (const key of processed) {
const [category, page] = key.split("/", 2);
if (!(category in pagesToProcess)) {
pagesToProcess[category] = new Set();
}
pagesToProcess[category].add(page);
}
for (const group of Object.values(sidebar)) {
const toProcess = pagesToProcess[group.category];
delete pagesToProcess[group.category];
if (!toProcess) {
console.error("Unknown category", group.category);
if (!group.pages) {
group.pages = [];
}
continue;
}
// Any pre-defined groups will not be sorted.
if (group.pages) {
for (const page of group.pages) {
if (!toProcess.delete(page)) {
console.error("Found unreferenced demo", page);
}
}
} else {
group.pages = [];
}
for (const page of Array.from(toProcess).sort()) {
group.pages.push(page);
}
}
for (const [category, pages] of Object.entries(pagesToProcess)) {
sidebar.push({
category,
header: category,
pages: Array.from(pages).sort(),
});
}
content += `export const SIDEBAR = ${JSON.stringify(sidebar, null, 2)};\n`;
fs.mkdirSync(galleryBuild, { recursive: true });
fs.writeFileSync( fs.writeFileSync(
path.resolve(galleryBuild, "import-pages.ts"), path.resolve(galleryBuild, "import-demos.ts"),
content, content,
"utf-8" "utf-8"
); );
@@ -158,25 +51,11 @@ gulp.task(
gulp.parallel( gulp.parallel(
"gen-icons-json", "gen-icons-json",
"build-translations", "build-translations",
"build-locale-data", "gather-gallery-demos"
"gather-gallery-pages"
), ),
"copy-static-gallery", "copy-static-gallery",
"gen-index-gallery-dev", "gen-index-gallery-dev",
gulp.parallel( env.useRollup() ? "rollup-dev-server-gallery" : "webpack-dev-server-gallery"
env.useRollup()
? "rollup-dev-server-gallery"
: "webpack-dev-server-gallery",
async function watchMarkdownFiles() {
gulp.watch(
[
path.resolve(paths.gallery_dir, "src/pages/**/*.markdown"),
path.resolve(paths.gallery_dir, "sidebar.js"),
],
gulp.series("gather-gallery-pages")
);
}
)
) )
); );
@@ -191,8 +70,7 @@ gulp.task(
gulp.parallel( gulp.parallel(
"gen-icons-json", "gen-icons-json",
"build-translations", "build-translations",
"build-locale-data", "gather-gallery-demos"
"gather-gallery-pages"
), ),
"copy-static-gallery", "copy-static-gallery",
env.useRollup() ? "rollup-prod-gallery" : "webpack-prod-gallery", env.useRollup() ? "rollup-prod-gallery" : "webpack-prod-gallery",

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");
@@ -12,28 +13,19 @@ const polyPath = (...parts) => path.resolve(paths.polymer_dir, ...parts);
const copyFileDir = (fromFile, toDir) => const copyFileDir = (fromFile, toDir) =>
fs.copySync(fromFile, path.join(toDir, path.basename(fromFile))); fs.copySync(fromFile, path.join(toDir, path.basename(fromFile)));
const genStaticPath = const genStaticPath = (staticDir) => (...parts) =>
(staticDir) => path.resolve(staticDir, ...parts);
(...parts) =>
path.resolve(staticDir, ...parts);
function copyTranslations(staticDir) { function copyTranslations(staticDir) {
const staticPath = genStaticPath(staticDir); const staticPath = genStaticPath(staticDir);
// Translation output // Translation output
fs.copySync( fs.copySync(
polyPath("build/translations/output"), polyPath("build-translations/output"),
staticPath("translations") staticPath("translations")
); );
} }
function copyLocaleData(staticDir) {
const staticPath = genStaticPath(staticDir);
// Locale data output
fs.copySync(polyPath("build/locale-data"), staticPath("locale-data"));
}
function copyMdiIcons(staticDir) { function copyMdiIcons(staticDir) {
const staticPath = genStaticPath(staticDir); const staticPath = genStaticPath(staticDir);
@@ -70,20 +62,12 @@ 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"),
}
); );
} }
function copyQrScannerWorker(staticDir) {
const staticPath = genStaticPath(staticDir);
copyFileDir(npmPath("qr-scanner/qr-scanner-worker.min.js"), staticPath("js"));
}
function copyMapPanel(staticDir) { function copyMapPanel(staticDir) {
const staticPath = genStaticPath(staticDir); const staticPath = genStaticPath(staticDir);
copyFileDir( copyFileDir(
@@ -96,11 +80,6 @@ function copyMapPanel(staticDir) {
); );
} }
gulp.task("copy-locale-data", async () => {
const staticDir = paths.app_output_static;
copyLocaleData(staticDir);
});
gulp.task("copy-translations-app", async () => { gulp.task("copy-translations-app", async () => {
const staticDir = paths.app_output_static; const staticDir = paths.app_output_static;
copyTranslations(staticDir); copyTranslations(staticDir);
@@ -111,11 +90,6 @@ gulp.task("copy-translations-supervisor", async () => {
copyTranslations(staticDir); copyTranslations(staticDir);
}); });
gulp.task("copy-locale-data-supervisor", async () => {
const staticDir = paths.hassio_output_static;
copyLocaleData(staticDir);
});
gulp.task("copy-static-app", async () => { gulp.task("copy-static-app", async () => {
const staticDir = paths.app_output_static; const staticDir = paths.app_output_static;
// Basic static files // Basic static files
@@ -125,14 +99,10 @@ gulp.task("copy-static-app", async () => {
copyPolyfills(staticDir); copyPolyfills(staticDir);
copyFonts(staticDir); copyFonts(staticDir);
copyTranslations(staticDir); copyTranslations(staticDir);
copyLocaleData(staticDir);
copyMdiIcons(staticDir); copyMdiIcons(staticDir);
// Panel assets // Panel assets
copyMapPanel(staticDir); copyMapPanel(staticDir);
// Qr Scanner assets
copyQrScannerWorker(staticDir);
}); });
gulp.task("copy-static-demo", async () => { gulp.task("copy-static-demo", async () => {
@@ -149,7 +119,6 @@ gulp.task("copy-static-demo", async () => {
copyMapPanel(paths.demo_output_static); copyMapPanel(paths.demo_output_static);
copyFonts(paths.demo_output_static); copyFonts(paths.demo_output_static);
copyTranslations(paths.demo_output_static); copyTranslations(paths.demo_output_static);
copyLocaleData(paths.demo_output_static);
copyMdiIcons(paths.demo_output_static); copyMdiIcons(paths.demo_output_static);
}); });
@@ -164,7 +133,6 @@ gulp.task("copy-static-cast", async () => {
copyMapPanel(paths.cast_output_static); copyMapPanel(paths.cast_output_static);
copyFonts(paths.cast_output_static); copyFonts(paths.cast_output_static);
copyTranslations(paths.cast_output_static); copyTranslations(paths.cast_output_static);
copyLocaleData(paths.cast_output_static);
copyMdiIcons(paths.cast_output_static); copyMdiIcons(paths.cast_output_static);
}); });
@@ -180,6 +148,5 @@ gulp.task("copy-static-gallery", async () => {
copyMapPanel(paths.gallery_output_static); copyMapPanel(paths.gallery_output_static);
copyFonts(paths.gallery_output_static); copyFonts(paths.gallery_output_static);
copyTranslations(paths.gallery_output_static); copyTranslations(paths.gallery_output_static);
copyLocaleData(paths.gallery_output_static);
copyMdiIcons(paths.gallery_output_static); copyMdiIcons(paths.gallery_output_static);
}); });

View File

@@ -22,40 +22,17 @@ const getMeta = () => {
const svg = fs.readFileSync(`${ICON_PATH}/${icon.name}.svg`, { const svg = fs.readFileSync(`${ICON_PATH}/${icon.name}.svg`, {
encoding, encoding,
}); });
return { return { path: svg.match(/ d="([^"]+)"/)[1], name: icon.name };
path: svg.match(/ d="([^"]+)"/)[1],
name: icon.name,
tags: icon.tags,
aliases: icon.aliases,
};
}); });
}; };
const addRemovedMeta = (meta) => { const addRemovedMeta = (meta) => {
const file = fs.readFileSync(REMOVED_ICONS_PATH, { encoding }); const file = fs.readFileSync(REMOVED_ICONS_PATH, { encoding });
const removed = JSON.parse(file); const removed = JSON.parse(file);
const removedMeta = removed.map((removeIcon) => ({ const combinedMeta = [...meta, ...removed];
path: removeIcon.path,
name: removeIcon.name,
tags: [],
aliases: [],
}));
const combinedMeta = [...meta, ...removedMeta];
return combinedMeta.sort((a, b) => a.name.localeCompare(b.name)); return combinedMeta.sort((a, b) => a.name.localeCompare(b.name));
}; };
const homeAutomationTag = "Home Automation";
const orderMeta = (meta) => {
const homeAutomationMeta = meta.filter((icon) =>
icon.tags.includes(homeAutomationTag)
);
const otherMeta = meta.filter(
(icon) => !icon.tags.includes(homeAutomationTag)
);
return [...homeAutomationMeta, ...otherMeta];
};
const splitBySize = (meta) => { const splitBySize = (meta) => {
const chunks = []; const chunks = [];
const CHUNK_SIZE = 50000; const CHUNK_SIZE = 50000;
@@ -100,10 +77,8 @@ const findDifferentiator = (curString, prevString) => {
}; };
gulp.task("gen-icons-json", (done) => { gulp.task("gen-icons-json", (done) => {
const meta = getMeta(); const meta = addRemovedMeta(getMeta());
const split = splitBySize(meta);
const metaAndRemoved = addRemovedMeta(meta);
const split = splitBySize(metaAndRemoved);
if (!fs.existsSync(OUTPUT_DIR)) { if (!fs.existsSync(OUTPUT_DIR)) {
fs.mkdirSync(OUTPUT_DIR, { recursive: true }); fs.mkdirSync(OUTPUT_DIR, { recursive: true });
@@ -141,18 +116,5 @@ gulp.task("gen-icons-json", (done) => {
JSON.stringify({ version: package.version, parts }) JSON.stringify({ version: package.version, parts })
); );
fs.writeFileSync(
path.resolve(OUTPUT_DIR, "iconList.json"),
JSON.stringify(
orderMeta(meta).map((icon) => ({
name: icon.name,
keywords: [
...icon.tags.map((t) => t.toLowerCase().replace(/\s\/\s/g, " ")),
...icon.aliases,
],
}))
)
);
done(); done();
}); });

View File

@@ -1,6 +1,9 @@
const gulp = require("gulp"); const gulp = require("gulp");
const fs = require("fs");
const path = require("path");
const env = require("../env"); const env = require("../env");
const paths = require("../paths");
require("./clean.js"); require("./clean.js");
require("./gen-icons-json.js"); require("./gen-icons-json.js");
@@ -17,11 +20,10 @@ gulp.task(
process.env.NODE_ENV = "development"; process.env.NODE_ENV = "development";
}, },
"clean-hassio", "clean-hassio",
"gen-icons-json",
"gen-index-hassio-dev", "gen-index-hassio-dev",
"build-supervisor-translations", "build-supervisor-translations",
"copy-translations-supervisor", "copy-translations-supervisor",
"build-locale-data",
"copy-locale-data-supervisor",
env.useRollup() ? "rollup-watch-hassio" : "webpack-watch-hassio" env.useRollup() ? "rollup-watch-hassio" : "webpack-watch-hassio"
) )
); );
@@ -33,10 +35,9 @@ gulp.task(
process.env.NODE_ENV = "production"; process.env.NODE_ENV = "production";
}, },
"clean-hassio", "clean-hassio",
"gen-icons-json",
"build-supervisor-translations", "build-supervisor-translations",
"copy-translations-supervisor", "copy-translations-supervisor",
"build-locale-data",
"copy-locale-data-supervisor",
env.useRollup() ? "rollup-prod-hassio" : "webpack-prod-hassio", env.useRollup() ? "rollup-prod-hassio" : "webpack-prod-hassio",
"gen-index-hassio-prod", "gen-index-hassio-prod",
...// Don't compress running tests ...// Don't compress running tests

View File

@@ -1,74 +0,0 @@
/* eslint-disable @typescript-eslint/no-var-requires */
const del = require("del");
const path = require("path");
const gulp = require("gulp");
const fs = require("fs");
const paths = require("../paths");
const outDir = "build/locale-data";
gulp.task("clean-locale-data", () => del([outDir]));
gulp.task("ensure-locale-data-build-dir", (done) => {
if (!fs.existsSync(outDir)) {
fs.mkdirSync(outDir, { recursive: true });
}
done();
});
const modules = {
"intl-relativetimeformat": "RelativeTimeFormat",
"intl-datetimeformat": "DateTimeFormat",
"intl-numberformat": "NumberFormat",
};
gulp.task("create-locale-data", (done) => {
const translationMeta = JSON.parse(
fs.readFileSync(
path.join(paths.translations_src, "translationMetadata.json")
)
);
Object.entries(modules).forEach(([module, className]) => {
Object.keys(translationMeta).forEach((lang) => {
try {
const localeData = String(
fs.readFileSync(
require.resolve(`@formatjs/${module}/locale-data/${lang}.js`)
)
)
.replace(
new RegExp(
`\\/\\*\\s*@generated\\s*\\*\\/\\s*\\/\\/\\s*prettier-ignore\\s*if\\s*\\(Intl\\.${className}\\s*&&\\s*typeof\\s*Intl\\.${className}\\.__addLocaleData\\s*===\\s*'function'\\)\\s*{\\s*Intl\\.${className}\\.__addLocaleData\\(`,
"im"
),
""
)
.replace(/\)\s*}/im, "");
// make sure we have valid JSON
JSON.parse(localeData);
if (!fs.existsSync(path.join(outDir, module))) {
fs.mkdirSync(path.join(outDir, module), { recursive: true });
}
fs.writeFileSync(
path.join(outDir, `${module}/${lang}.json`),
localeData
);
} catch (e) {
if (e.code !== "MODULE_NOT_FOUND") {
throw e;
}
}
});
done();
});
});
gulp.task(
"build-locale-data",
gulp.series(
"clean-locale-data",
"ensure-locale-data-build-dir",
"create-locale-data"
)
);

View File

@@ -1,5 +1,3 @@
/* eslint-disable @typescript-eslint/no-var-requires */
const crypto = require("crypto"); const crypto = require("crypto");
const del = require("del"); const del = require("del");
const path = require("path"); const path = require("path");
@@ -7,7 +5,7 @@ const source = require("vinyl-source-stream");
const vinylBuffer = require("vinyl-buffer"); const vinylBuffer = require("vinyl-buffer");
const gulp = require("gulp"); const gulp = require("gulp");
const fs = require("fs"); const fs = require("fs");
const flatmap = require("gulp-flatmap"); const foreach = require("gulp-foreach");
const merge = require("gulp-merge-json"); const merge = require("gulp-merge-json");
const rename = require("gulp-rename"); const rename = require("gulp-rename");
const transform = require("gulp-json-transform"); const transform = require("gulp-json-transform");
@@ -17,7 +15,7 @@ const paths = require("../paths");
const inFrontendDir = "translations/frontend"; const inFrontendDir = "translations/frontend";
const inBackendDir = "translations/backend"; const inBackendDir = "translations/backend";
const workDir = "build/translations"; const workDir = "build-translations";
const fullDir = workDir + "/full"; const fullDir = workDir + "/full";
const coreDir = workDir + "/core"; const coreDir = workDir + "/core";
const outDir = workDir + "/output"; const outDir = workDir + "/output";
@@ -28,6 +26,13 @@ gulp.task("translations-enable-merge-backend", (done) => {
done(); done();
}); });
String.prototype.rsplit = function (sep, maxsplit) {
var split = this.split(sep);
return maxsplit
? [split.slice(0, -maxsplit).join(sep)].concat(split.slice(-maxsplit))
: split;
};
// Panel translations which should be split from the core translations. // Panel translations which should be split from the core translations.
const TRANSLATION_FRAGMENTS = Object.keys( const TRANSLATION_FRAGMENTS = Object.keys(
require("../../src/translations/en.json").ui.panel require("../../src/translations/en.json").ui.panel
@@ -35,7 +40,7 @@ const TRANSLATION_FRAGMENTS = Object.keys(
function recursiveFlatten(prefix, data) { function recursiveFlatten(prefix, data) {
let output = {}; let output = {};
Object.keys(data).forEach((key) => { Object.keys(data).forEach(function (key) {
if (typeof data[key] === "object") { if (typeof data[key] === "object") {
output = { output = {
...output, ...output,
@@ -96,19 +101,15 @@ function lokaliseTransform(data, original, file) {
if (value instanceof Object) { if (value instanceof Object) {
output[key] = lokaliseTransform(value, original, file); output[key] = lokaliseTransform(value, original, file);
} else { } else {
output[key] = value.replace(re_key_reference, (_match, lokalise_key) => { output[key] = value.replace(re_key_reference, (match, key) => {
const replace = lokalise_key.split("::").reduce((tr, k) => { const replace = key.split("::").reduce((tr, k) => {
if (!tr) { if (!tr) {
throw Error( throw Error(`Invalid key placeholder ${key} in ${file.path}`);
`Invalid key placeholder ${lokalise_key} in ${file.path}`
);
} }
return tr[k]; return tr[k];
}, original); }, original);
if (typeof replace !== "string") { if (typeof replace !== "string") {
throw Error( throw Error(`Invalid key placeholder ${key} in ${file.path}`);
`Invalid key placeholder ${lokalise_key} in ${file.path}`
);
} }
return replace; return replace;
}); });
@@ -117,16 +118,18 @@ function lokaliseTransform(data, original, file) {
return output; return output;
} }
gulp.task("clean-translations", () => del([workDir])); gulp.task("clean-translations", function () {
return del([workDir]);
});
gulp.task("ensure-translations-build-dir", (done) => { gulp.task("ensure-translations-build-dir", (done) => {
if (!fs.existsSync(workDir)) { if (!fs.existsSync(workDir)) {
fs.mkdirSync(workDir, { recursive: true }); fs.mkdirSync(workDir);
} }
done(); done();
}); });
gulp.task("create-test-metadata", (cb) => { gulp.task("create-test-metadata", function (cb) {
fs.writeFile( fs.writeFile(
workDir + "/testMetadata.json", workDir + "/testMetadata.json",
JSON.stringify({ JSON.stringify({
@@ -140,13 +143,17 @@ gulp.task("create-test-metadata", (cb) => {
gulp.task( gulp.task(
"create-test-translation", "create-test-translation",
gulp.series("create-test-metadata", () => gulp.series("create-test-metadata", function createTestTranslation() {
gulp return gulp
.src(path.join(paths.translations_src, "en.json")) .src(path.join(paths.translations_src, "en.json"))
.pipe(transform((data, _file) => recursiveEmpty(data))) .pipe(
transform(function (data, file) {
return recursiveEmpty(data);
})
)
.pipe(rename("test.json")) .pipe(rename("test.json"))
.pipe(gulp.dest(workDir)) .pipe(gulp.dest(workDir));
) })
); );
/** /**
@@ -158,7 +165,7 @@ gulp.task(
* project is buildable immediately after merging new translation keys, since * project is buildable immediately after merging new translation keys, since
* the Lokalise update to translations/en.json will not happen immediately. * the Lokalise update to translations/en.json will not happen immediately.
*/ */
gulp.task("build-master-translation", () => { gulp.task("build-master-translation", function () {
const src = [path.join(paths.translations_src, "en.json")]; const src = [path.join(paths.translations_src, "en.json")];
if (mergeBackend) { if (mergeBackend) {
@@ -167,7 +174,11 @@ gulp.task("build-master-translation", () => {
return gulp return gulp
.src(src) .src(src)
.pipe(transform((data, file) => lokaliseTransform(data, data, file))) .pipe(
transform(function (data, file) {
return lokaliseTransform(data, data, file);
})
)
.pipe( .pipe(
merge({ merge({
fileName: "translationMaster.json", fileName: "translationMaster.json",
@@ -176,14 +187,18 @@ gulp.task("build-master-translation", () => {
.pipe(gulp.dest(workDir)); .pipe(gulp.dest(workDir));
}); });
gulp.task("build-merged-translations", () => gulp.task("build-merged-translations", function () {
gulp return gulp
.src([inFrontendDir + "/*.json", workDir + "/test.json"], { .src([inFrontendDir + "/*.json", workDir + "/test.json"], {
allowEmpty: true, allowEmpty: true,
}) })
.pipe(transform((data, file) => lokaliseTransform(data, data, file)))
.pipe( .pipe(
flatmap((stream, file) => { transform(function (data, file) {
return lokaliseTransform(data, data, file);
})
)
.pipe(
foreach(function (stream, file) {
// For each language generate a merged json file. It begins with the master // For each language generate a merged json file. It begins with the master
// translation as a failsafe for untranslated strings, and merges all parent // translation as a failsafe for untranslated strings, and merges all parent
// tags into one file for each specific subtag // tags into one file for each specific subtag
@@ -215,17 +230,17 @@ gulp.task("build-merged-translations", () =>
) )
.pipe(gulp.dest(fullDir)); .pipe(gulp.dest(fullDir));
}) })
) );
); });
let taskName; var taskName;
const splitTasks = []; const splitTasks = [];
TRANSLATION_FRAGMENTS.forEach((fragment) => { TRANSLATION_FRAGMENTS.forEach((fragment) => {
taskName = "build-translation-fragment-" + fragment; taskName = "build-translation-fragment-" + fragment;
gulp.task(taskName, () => gulp.task(taskName, function () {
// Return only the translations for this fragment. // Return only the translations for this fragment.
gulp return gulp
.src(fullDir + "/*.json") .src(fullDir + "/*.json")
.pipe( .pipe(
transform((data) => ({ transform((data) => ({
@@ -236,18 +251,18 @@ TRANSLATION_FRAGMENTS.forEach((fragment) => {
}, },
})) }))
) )
.pipe(gulp.dest(workDir + "/" + fragment)) .pipe(gulp.dest(workDir + "/" + fragment));
); });
splitTasks.push(taskName); splitTasks.push(taskName);
}); });
taskName = "build-translation-core"; taskName = "build-translation-core";
gulp.task(taskName, () => gulp.task(taskName, function () {
// Remove the fragment translations from the core translation. // Remove the fragment translations from the core translation.
gulp return gulp
.src(fullDir + "/*.json") .src(fullDir + "/*.json")
.pipe( .pipe(
transform((data, _file) => { transform((data, file) => {
TRANSLATION_FRAGMENTS.forEach((fragment) => { TRANSLATION_FRAGMENTS.forEach((fragment) => {
delete data.ui.panel[fragment]; delete data.ui.panel[fragment];
}); });
@@ -255,14 +270,14 @@ gulp.task(taskName, () =>
return data; return data;
}) })
) )
.pipe(gulp.dest(coreDir)) .pipe(gulp.dest(coreDir));
); });
splitTasks.push(taskName); splitTasks.push(taskName);
gulp.task("build-flattened-translations", () => gulp.task("build-flattened-translations", function () {
// Flatten the split versions of our translations, and move them into outDir // Flatten the split versions of our translations, and move them into outDir
gulp return gulp
.src( .src(
TRANSLATION_FRAGMENTS.map( TRANSLATION_FRAGMENTS.map(
(fragment) => workDir + "/" + fragment + "/*.json" (fragment) => workDir + "/" + fragment + "/*.json"
@@ -270,45 +285,41 @@ gulp.task("build-flattened-translations", () =>
{ base: workDir } { base: workDir }
) )
.pipe( .pipe(
transform((data) => transform(function (data) {
// Polymer.AppLocalizeBehavior requires flattened json // Polymer.AppLocalizeBehavior requires flattened json
flatten(data) return flatten(data);
) })
) )
.pipe( .pipe(
rename((filePath) => { rename((filePath) => {
if (filePath.dirname === "core") { if (filePath.dirname === "core") {
filePath.dirname = ""; filePath.dirname = "";
} }
// In dev we create the file with the fake hash in the filename
if (!env.isProdBuild()) {
filePath.basename += "-dev";
}
}) })
) )
.pipe(gulp.dest(outDir)) .pipe(gulp.dest(outDir));
); });
const fingerprints = {}; const fingerprints = {};
gulp.task("build-translation-fingerprints", () => { gulp.task(
// Fingerprint full file of each language "build-translation-fingerprints",
const files = fs.readdirSync(fullDir); function fingerprintTranslationFiles() {
// Fingerprint full file of each language
const files = fs.readdirSync(fullDir);
for (let i = 0; i < files.length; i++) { for (let i = 0; i < files.length; i++) {
fingerprints[files[i].split(".")[0]] = { fingerprints[files[i].split(".")[0]] = {
// In dev we create fake hashes // In dev we create fake hashes
hash: env.isProdBuild() hash: env.isProdBuild()
? crypto ? crypto
.createHash("md5") .createHash("md5")
.update(fs.readFileSync(path.join(fullDir, files[i]), "utf-8")) .update(fs.readFileSync(path.join(fullDir, files[i]), "utf-8"))
.digest("hex") .digest("hex")
: "dev", : "dev",
}; };
} }
// In dev we create the file with the fake hash in the filename
if (env.isProdBuild()) {
mapFiles(outDir, ".json", (filename) => { mapFiles(outDir, ".json", (filename) => {
const parsed = path.parse(filename); const parsed = path.parse(filename);
@@ -324,43 +335,35 @@ gulp.task("build-translation-fingerprints", () => {
}` }`
); );
}); });
const stream = source("translationFingerprints.json");
stream.write(JSON.stringify(fingerprints));
process.nextTick(() => stream.end());
return stream.pipe(vinylBuffer()).pipe(gulp.dest(workDir));
} }
);
const stream = source("translationFingerprints.json"); gulp.task("build-translation-fragment-supervisor", function () {
stream.write(JSON.stringify(fingerprints)); return gulp
process.nextTick(() => stream.end());
return stream.pipe(vinylBuffer()).pipe(gulp.dest(workDir));
});
gulp.task("build-translation-fragment-supervisor", () =>
gulp
.src(fullDir + "/*.json") .src(fullDir + "/*.json")
.pipe(transform((data) => data.supervisor)) .pipe(transform((data) => data.supervisor))
.pipe( .pipe(gulp.dest(workDir + "/supervisor"));
rename((filePath) => { });
// In dev we create the file with the fake hash in the filename
if (!env.isProdBuild()) {
filePath.basename += "-dev";
}
})
)
.pipe(gulp.dest(workDir + "/supervisor"))
);
gulp.task("build-translation-flatten-supervisor", () => gulp.task("build-translation-flatten-supervisor", function () {
gulp return gulp
.src(workDir + "/supervisor/*.json") .src(workDir + "/supervisor/*.json")
.pipe( .pipe(
transform((data) => transform(function (data) {
// Polymer.AppLocalizeBehavior requires flattened json // Polymer.AppLocalizeBehavior requires flattened json
flatten(data) return flatten(data);
) })
) )
.pipe(gulp.dest(outDir)) .pipe(gulp.dest(outDir));
); });
gulp.task("build-translation-write-metadata", () => gulp.task("build-translation-write-metadata", function writeMetadata() {
gulp return gulp
.src( .src(
[ [
path.join(paths.translations_src, "translationMetadata.json"), path.join(paths.translations_src, "translationMetadata.json"),
@@ -371,14 +374,13 @@ gulp.task("build-translation-write-metadata", () =>
) )
.pipe(merge({})) .pipe(merge({}))
.pipe( .pipe(
transform((data) => { transform(function (data) {
const newData = {}; const newData = {};
Object.entries(data).forEach(([key, value]) => { Object.entries(data).forEach(([key, value]) => {
// Filter out translations without native name. // Filter out translations without native name.
if (value.nativeName) { if (value.nativeName) {
newData[key] = value; newData[key] = value;
} else { } else {
// eslint-disable-next-line no-console
console.warn( console.warn(
`Skipping language ${key}. Native name was not translated.` `Skipping language ${key}. Native name was not translated.`
); );
@@ -394,26 +396,19 @@ gulp.task("build-translation-write-metadata", () =>
})) }))
) )
.pipe(rename("translationMetadata.json")) .pipe(rename("translationMetadata.json"))
.pipe(gulp.dest(workDir)) .pipe(gulp.dest(workDir));
); });
gulp.task(
"create-translations",
gulp.series(
env.isProdBuild() ? (done) => done() : "create-test-translation",
"build-master-translation",
"build-merged-translations",
gulp.parallel(...splitTasks),
"build-flattened-translations"
)
);
gulp.task( gulp.task(
"build-translations", "build-translations",
gulp.series( gulp.series(
"clean-translations", "clean-translations",
"ensure-translations-build-dir", "ensure-translations-build-dir",
"create-translations", env.isProdBuild() ? (done) => done() : "create-test-translation",
"build-master-translation",
"build-merged-translations",
gulp.parallel(...splitTasks),
"build-flattened-translations",
"build-translation-fingerprints", "build-translation-fingerprints",
"build-translation-write-metadata" "build-translation-write-metadata"
) )

View File

@@ -1,6 +1,4 @@
/* eslint-disable @typescript-eslint/no-var-requires */
// 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");
@@ -20,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,
@@ -35,29 +26,26 @@ const isWsl =
* listenHost?: string * listenHost?: string
* }} * }}
*/ */
const runDevServer = async ({ const runDevServer = ({
compiler, compiler,
contentBase, contentBase,
port, port,
listenHost = "localhost", listenHost = "localhost",
}) => { }) =>
const server = new WebpackDevServer( new WebpackDevServer(compiler, {
{ open: true,
open: true, watchContentBase: true,
host: listenHost, contentBase,
port, }).listen(port, listenHost, function (err) {
static: { if (err) {
directory: contentBase, throw err;
watch: true, }
}, // Server listening
}, log(
compiler "[webpack-dev-server]",
); `Project is running at http://localhost:${port}`
);
await server.start(); });
// Server listening
log("[webpack-dev-server]", `Project is running at http://localhost:${port}`);
};
const doneHandler = (done) => (err, stats) => { const doneHandler = (done) => (err, stats) => {
if (err) { if (err) {
@@ -69,7 +57,6 @@ const doneHandler = (done) => (err, stats) => {
} }
if (stats.hasErrors() || stats.hasWarnings()) { if (stats.hasErrors() || stats.hasWarnings()) {
// eslint-disable-next-line no-console
console.log(stats.toString("minimal")); console.log(stats.toString("minimal"));
} }
@@ -91,14 +78,13 @@ 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 }) doneHandler()
: createAppConfig({ isProdBuild: false, latestBuild: true }) );
).watch({ poll: isWsl }, doneHandler());
gulp.watch( gulp.watch(
path.join(paths.translations_src, "en.json"), path.join(paths.translations_src, "en.json"),
gulp.series("create-translations", "copy-translations-app") gulp.series("build-translations", "copy-translations-app")
); );
}); });
@@ -110,13 +96,13 @@ gulp.task("webpack-prod-app", () =>
) )
); );
gulp.task("webpack-dev-server-demo", () => gulp.task("webpack-dev-server-demo", () => {
runDevServer({ runDevServer({
compiler: webpack(bothBuilds(createDemoConfig, { isProdBuild: false })), compiler: webpack(bothBuilds(createDemoConfig, { isProdBuild: false })),
contentBase: paths.demo_output_root, contentBase: paths.demo_output_root,
port: 8090, port: 8090,
}) });
); });
gulp.task("webpack-prod-demo", () => gulp.task("webpack-prod-demo", () =>
prodBuild( prodBuild(
@@ -126,15 +112,15 @@ gulp.task("webpack-prod-demo", () =>
) )
); );
gulp.task("webpack-dev-server-cast", () => gulp.task("webpack-dev-server-cast", () => {
runDevServer({ runDevServer({
compiler: webpack(bothBuilds(createCastConfig, { isProdBuild: false })), compiler: webpack(bothBuilds(createCastConfig, { isProdBuild: false })),
contentBase: paths.cast_output_root, contentBase: paths.cast_output_root,
port: 8080, port: 8080,
// Accessible from the network, because that's how Cast hits it. // Accessible from the network, because that's how Cast hits it.
listenHost: "0.0.0.0", listenHost: "0.0.0.0",
}) });
); });
gulp.task("webpack-prod-cast", () => gulp.task("webpack-prod-cast", () =>
prodBuild( prodBuild(
@@ -151,7 +137,7 @@ gulp.task("webpack-watch-hassio", () => {
isProdBuild: false, isProdBuild: false,
latestBuild: true, latestBuild: true,
}) })
).watch({ ignored: /build/, 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"),
@@ -167,15 +153,14 @@ gulp.task("webpack-prod-hassio", () =>
) )
); );
gulp.task("webpack-dev-server-gallery", () => gulp.task("webpack-dev-server-gallery", () => {
runDevServer({ runDevServer({
// We don't use the es5 build, but the dev server will fuck up the publicPath if we don't // We don't use the es5 build, but the dev server will fuck up the publicPath if we don't
compiler: webpack(bothBuilds(createGalleryConfig, { isProdBuild: false })), compiler: webpack(bothBuilds(createGalleryConfig, { isProdBuild: false })),
contentBase: paths.gallery_output_root, contentBase: paths.gallery_output_root,
port: 8100, port: 8100,
listenHost: "0.0.0.0", });
}) });
);
gulp.task("webpack-prod-gallery", () => gulp.task("webpack-prod-gallery", () =>
prodBuild( prodBuild(

View File

@@ -26,7 +26,6 @@ module.exports = {
cast_output_es5: path.resolve(__dirname, "../cast/dist/frontend_es5"), cast_output_es5: path.resolve(__dirname, "../cast/dist/frontend_es5"),
gallery_dir: path.resolve(__dirname, "../gallery"), gallery_dir: path.resolve(__dirname, "../gallery"),
gallery_build: path.resolve(__dirname, "../gallery/build"),
gallery_output_root: path.resolve(__dirname, "../gallery/dist"), gallery_output_root: path.resolve(__dirname, "../gallery/dist"),
gallery_output_latest: path.resolve( gallery_output_latest: path.resolve(
__dirname, __dirname,

View File

@@ -52,11 +52,16 @@ const createRollupConfig = ({
browser: true, browser: true,
rootDir: paths.polymer_dir, rootDir: paths.polymer_dir,
}), }),
commonjs(), commonjs({
namedExports: {
"js-yaml": ["safeDump", "safeLoad"],
},
}),
json(), json(),
babel({ babel({
...bundle.babelOptions({ latestBuild }), ...bundle.babelOptions({ latestBuild }),
extensions, extensions,
exclude: bundle.babelExclude(),
babelHelpers: isWDS ? "inline" : "bundled", babelHelpers: isWDS ? "inline" : "bundled",
}), }),
string({ string({

View File

@@ -3,10 +3,9 @@ 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 log = require("fancy-log");
const WebpackBar = require("webpackbar");
const paths = require("./paths.js"); const paths = require("./paths.js");
const bundle = require("./bundle.js"); const bundle = require("./bundle.js");
const log = require("fancy-log");
class LogStartCompilePlugin { class LogStartCompilePlugin {
ignoredFirst = false; ignoredFirst = false;
@@ -30,7 +29,6 @@ const createWebpackConfig = ({
isProdBuild, isProdBuild,
latestBuild, latestBuild,
isStatsBuild, isStatsBuild,
isHassioBuild,
dontHash, dontHash,
}) => { }) => {
if (!dontHash) { if (!dontHash) {
@@ -49,18 +47,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",
}, },
], ],
}, },
@@ -72,11 +67,8 @@ const createWebpackConfig = ({
terserOptions: bundle.terserOptions(latestBuild), terserOptions: bundle.terserOptions(latestBuild),
}), }),
], ],
moduleIds: isProdBuild && !isStatsBuild ? "deterministic" : "named",
chunkIds: isProdBuild && !isStatsBuild ? "deterministic" : "named",
}, },
plugins: [ plugins: [
new WebpackBar({ fancy: !isProdBuild }),
new WebpackManifestPlugin({ new WebpackManifestPlugin({
// Only include the JS of entrypoints // Only include the JS of entrypoints
filter: (file) => file.isInitial && !file.name.endsWith(".map"), filter: (file) => file.isInitial && !file.name.endsWith(".map"),
@@ -118,11 +110,18 @@ const createWebpackConfig = ({
}, },
}), }),
new webpack.NormalModuleReplacementPlugin( new webpack.NormalModuleReplacementPlugin(
new RegExp( new RegExp(bundle.emptyPackages({ latestBuild }).join("|")),
bundle.emptyPackages({ latestBuild, isHassioBuild }).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-labs/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: {
@@ -130,35 +129,25 @@ const createWebpackConfig = ({
alias: { alias: {
"lit/decorators$": "lit/decorators.js", "lit/decorators$": "lit/decorators.js",
"lit/directive$": "lit/directive.js", "lit/directive$": "lit/directive.js",
"lit/directives/until$": "lit/directives/until.js",
"lit/directives/class-map$": "lit/directives/class-map.js",
"lit/directives/style-map$": "lit/directives/style-map.js",
"lit/directives/if-defined$": "lit/directives/if-defined.js",
"lit/directives/guard$": "lit/directives/guard.js",
"lit/directives/cache$": "lit/directives/cache.js",
"lit/directives/repeat$": "lit/directives/repeat.js",
"lit/polyfill-support$": "lit/polyfill-support.js", "lit/polyfill-support$": "lit/polyfill-support.js",
"@lit-labs/virtualizer/layouts/grid":
"@lit-labs/virtualizer/layouts/grid.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
globalObject: "self", globalObject: "self",
}, },
experiments: {
topLevelAwait: true,
},
}; };
}; };

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,46 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<script src="//www.gstatic.com/cast/sdk/libs/caf_receiver/v3/cast_receiver_framework.js"></script>
<style>
body {
--logo-image: url('https://www.home-assistant.io/images/home-assistant-logo.svg');
--logo-repeat: no-repeat;
--playback-logo-image: url('https://www.home-assistant.io/images/home-assistant-logo.svg');
--theme-hue: 200;
--progress-color: #03a9f4;
--splash-image: url('https://home-assistant.io/images/cast/splash.png');
--splash-size: cover;
--background-color: #41bdf5;
}
</style>
<script>
var _gaq=[['_setAccount','UA-57927901-10'],['_trackPageview']];
(function(d,t){var g=d.createElement(t),s=d.getElementsByTagName(t)[0];
g.src=('https:'==location.protocol?'//ssl':'//www')+'.google-analytics.com/ga.js';
s.parentNode.insertBefore(g,s)}(document,'script'));
</script>
</head>
<body>
<%= renderTemplate('_js_base') %>
<cast-media-player></cast-media-player>
<script>
import("<%= latestMediaJS %>");
window.latestJS = true;
</script>
<script>
if (!window.latestJS) {
<% if (useRollup) { %>
_ls("/static/js/s.min.js").onload = function() {
System.import("<%= es5MediaJS %>");
};
<% } else { %>
_ls("<%= es5MediaJS %>");
<% } %>
}
</script>
</body>
</html>

View File

@@ -1,5 +1,4 @@
import "@material/mwc-button/mwc-button"; import "@material/mwc-button/mwc-button";
import { mdiCast, mdiCastConnected } from "@mdi/js";
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";
@@ -18,7 +17,6 @@ import {
import { atLeastVersion } from "../../../../src/common/config/version"; import { atLeastVersion } from "../../../../src/common/config/version";
import { toggleAttribute } from "../../../../src/common/dom/toggle_attribute"; import { toggleAttribute } from "../../../../src/common/dom/toggle_attribute";
import "../../../../src/components/ha-icon"; import "../../../../src/components/ha-icon";
import "../../../../src/components/ha-svg-icon";
import { import {
getLegacyLovelaceCollection, getLegacyLovelaceCollection,
getLovelaceCollection, getLovelaceCollection,
@@ -75,7 +73,7 @@ class HcCast extends LitElement {
? html` ? html`
<p class="center-item"> <p class="center-item">
<mwc-button raised @click=${this._handleLaunch}> <mwc-button raised @click=${this._handleLaunch}>
<ha-svg-icon .path=${mdiCast}></ha-svg-icon> <ha-icon icon="hass:cast"></ha-icon>
Start Casting Start Casting
</mwc-button> </mwc-button>
</p> </p>
@@ -113,7 +111,7 @@ class HcCast extends LitElement {
${this.castManager.status ${this.castManager.status
? html` ? html`
<mwc-button @click=${this._handleLaunch}> <mwc-button @click=${this._handleLaunch}>
<ha-svg-icon .path=${mdiCastConnected}></ha-svg-icon> <ha-icon icon="hass:cast-connected"></ha-icon>
Manage Manage
</mwc-button> </mwc-button>
` `
@@ -193,7 +191,7 @@ class HcCast extends LitElement {
} }
this.connection.close(); this.connection.close();
location.reload(); location.reload();
} catch (err: any) { } catch (err) {
alert("Unable to log out!"); alert("Unable to log out!");
} }
} }
@@ -235,7 +233,7 @@ class HcCast extends LitElement {
color: var(--secondary-text-color); color: var(--secondary-text-color);
} }
mwc-button ha-svg-icon { mwc-button ha-icon {
margin-right: 8px; margin-right: 8px;
height: 18px; height: 18px;
} }

View File

@@ -1,5 +1,4 @@
import "@material/mwc-button"; import "@material/mwc-button";
import { mdiCastConnected, mdiCast } from "@mdi/js";
import "@polymer/paper-input/paper-input"; import "@polymer/paper-input/paper-input";
import { import {
Auth, Auth,
@@ -20,7 +19,7 @@ import {
loadTokens, loadTokens,
saveTokens, saveTokens,
} from "../../../../src/common/auth/token_storage"; } from "../../../../src/common/auth/token_storage";
import "../../../../src/components/ha-svg-icon"; import "../../../../src/components/ha-icon";
import "../../../../src/layouts/hass-loading-screen"; import "../../../../src/layouts/hass-loading-screen";
import { registerServiceWorker } from "../../../../src/util/register-service-worker"; import { registerServiceWorker } from "../../../../src/util/register-service-worker";
import "./hc-layout"; import "./hc-layout";
@@ -128,11 +127,11 @@ export class HcConnect extends LitElement {
<div class="card-actions"> <div class="card-actions">
<mwc-button @click=${this._handleDemo}> <mwc-button @click=${this._handleDemo}>
Show Demo Show Demo
<ha-svg-icon <ha-icon
.path=${this.castManager.castState === "CONNECTED" .icon=${this.castManager.castState === "CONNECTED"
? mdiCastConnected ? "hass:cast-connected"
: mdiCast} : "hass:cast"}
></ha-svg-icon> ></ha-icon>
</mwc-button> </mwc-button>
<div class="spacer"></div> <div class="spacer"></div>
<mwc-button @click=${this._handleConnect}>Authorize</mwc-button> <mwc-button @click=${this._handleConnect}>Authorize</mwc-button>
@@ -213,7 +212,7 @@ export class HcConnect extends LitElement {
let url: URL; let url: URL;
try { try {
url = new URL(value); url = new URL(value);
} catch (err: any) { } catch (err) {
this.error = "Invalid URL"; this.error = "Invalid URL";
return; return;
} }
@@ -241,7 +240,7 @@ export class HcConnect extends LitElement {
try { try {
this.loading = true; this.loading = true;
auth = await getAuth(options); auth = await getAuth(options);
} catch (err: any) { } catch (err) {
if (init === "saved-tokens" && err === ERR_CANNOT_CONNECT) { if (init === "saved-tokens" && err === ERR_CANNOT_CONNECT) {
this.cannotConnect = true; this.cannotConnect = true;
return; return;
@@ -260,7 +259,7 @@ export class HcConnect extends LitElement {
try { try {
conn = await createConnection({ auth }); conn = await createConnection({ auth });
} catch (err: any) { } catch (err) {
// In case of saved tokens, silently solve problems. // In case of saved tokens, silently solve problems.
if (init === "saved-tokens") { if (init === "saved-tokens") {
if (err === ERR_CANNOT_CONNECT) { if (err === ERR_CANNOT_CONNECT) {
@@ -286,7 +285,7 @@ export class HcConnect extends LitElement {
try { try {
saveTokens(null); saveTokens(null);
location.reload(); location.reload();
} catch (err: any) { } catch (err) {
alert("Unable to log out!"); alert("Unable to log out!");
} }
} }
@@ -308,7 +307,7 @@ export class HcConnect extends LitElement {
color: darkred; color: darkred;
} }
mwc-button ha-svg-icon { mwc-button ha-icon {
margin-left: 8px; margin-left: 8px;
} }

View File

@@ -1,22 +0,0 @@
const castContext = cast.framework.CastReceiverContext.getInstance();
const playerManager = castContext.getPlayerManager();
playerManager.setMessageInterceptor(
cast.framework.messages.MessageType.LOAD,
(loadRequestData) => {
const media = loadRequestData.media;
// Special handling if it came from Google Assistant
if (media.entity) {
media.contentId = media.entity;
media.streamType = cast.framework.messages.StreamType.LIVE;
media.contentType = "application/vnd.apple.mpegurl";
// @ts-ignore
media.hlsVideoSegmentFormat =
cast.framework.messages.HlsVideoSegmentFormat.FMP4;
}
return loadRequestData;
}
);
castContext.start();

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

@@ -8,9 +8,6 @@ import { ReceivedMessage } from "./types";
const lovelaceController = new HcMain(); const lovelaceController = new HcMain();
document.body.append(lovelaceController); document.body.append(lovelaceController);
lovelaceController.addEventListener("cast-view-changed", (ev) => {
playDummyMedia(ev.detail.title);
});
const mediaPlayer = document.createElement("cast-media-player"); const mediaPlayer = document.createElement("cast-media-player");
mediaPlayer.style.display = "none"; mediaPlayer.style.display = "none";
@@ -31,31 +28,6 @@ const setTouchControlsVisibility = (visible: boolean) => {
} }
}; };
let timeOut: number | undefined;
const playDummyMedia = (viewTitle?: string) => {
const loadRequestData = new cast.framework.messages.LoadRequestData();
loadRequestData.autoplay = true;
loadRequestData.media = new cast.framework.messages.MediaInformation();
loadRequestData.media.contentId =
"https://cast.home-assistant.io/images/google-nest-hub.png";
loadRequestData.media.contentType = "image/jpeg";
loadRequestData.media.streamType = cast.framework.messages.StreamType.NONE;
const metadata = new cast.framework.messages.GenericMediaMetadata();
metadata.title = viewTitle;
loadRequestData.media.metadata = metadata;
loadRequestData.requestId = 0;
playerManager.load(loadRequestData);
if (timeOut) {
clearTimeout(timeOut);
timeOut = undefined;
}
if (castContext.getDeviceCapabilities().touch_input_supported) {
timeOut = window.setTimeout(() => playDummyMedia(viewTitle), 540000); // repeat every 9 minutes to keep it active (gets deactivated after 10 minutes)
}
};
const showLovelaceController = () => { const showLovelaceController = () => {
mediaPlayer.style.display = "none"; mediaPlayer.style.display = "none";
lovelaceController.style.display = "initial"; lovelaceController.style.display = "initial";
@@ -79,7 +51,6 @@ const showMediaPlayer = () => {
--progress-color: #03a9f4; --progress-color: #03a9f4;
--splash-image: url('https://home-assistant.io/images/cast/splash.png'); --splash-image: url('https://home-assistant.io/images/cast/splash.png');
--splash-size: cover; --splash-size: cover;
--background-color: #41bdf5;
} }
`; `;
document.head.appendChild(style); document.head.appendChild(style);
@@ -92,6 +63,22 @@ options.customNamespaces = {
[CAST_NS]: cast.framework.system.MessageType.JSON, [CAST_NS]: cast.framework.system.MessageType.JSON,
}; };
// The docs say we need to set options.touchScreenOptimizeApp = true
// https://developers.google.com/cast/docs/caf_receiver/customize_ui#accessing_ui_controls
// This doesn't work.
// @ts-ignore
options.touchScreenOptimizedApp = true;
// The class reference say we can set a uiConfig in options to set it
// https://developers.google.com/cast/docs/reference/caf_receiver/cast.framework.CastReceiverOptions#uiConfig
// This doesn't work either.
// @ts-ignore
options.uiConfig = new cast.framework.ui.UiConfig();
// @ts-ignore
options.uiConfig.touchScreenOptimizedApp = true;
castContext.setInactivityTimeout(86400); // 1 day
castContext.addCustomMessageListener( castContext.addCustomMessageListener(
CAST_NS, CAST_NS,
// @ts-ignore // @ts-ignore
@@ -116,12 +103,6 @@ const playerManager = castContext.getPlayerManager();
playerManager.setMessageInterceptor( playerManager.setMessageInterceptor(
cast.framework.messages.MessageType.LOAD, cast.framework.messages.MessageType.LOAD,
(loadRequestData) => { (loadRequestData) => {
if (
loadRequestData.media.contentId ===
"https://cast.home-assistant.io/images/google-nest-hub.png"
) {
return loadRequestData;
}
// We received a play media command, hide Lovelace and show media player // We received a play media command, hide Lovelace and show media player
showMediaPlayer(); showMediaPlayer();
const media = loadRequestData.media; const media = loadRequestData.media;

View File

@@ -1,15 +1,11 @@
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property, query } from "lit/decorators"; import { customElement, property } from "lit/decorators";
import { fireEvent } from "../../../../src/common/dom/fire_event";
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";
import { HomeAssistant } from "../../../../src/types"; import { HomeAssistant } from "../../../../src/types";
import "./hc-launch-screen"; import "./hc-launch-screen";
(window as any).loadCardHelpers = () =>
import("../../../../src/panels/lovelace/custom-card-helpers");
@customElement("hc-lovelace") @customElement("hc-lovelace")
class HcLovelace extends LitElement { class HcLovelace extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant; @property({ attribute: false }) public hass!: HomeAssistant;
@@ -18,9 +14,7 @@ class HcLovelace extends LitElement {
@property() public viewPath?: string | number; @property() public viewPath?: string | number;
@property() public urlPath: string | null = null; public urlPath?: string | null;
@query("hui-view") private _huiView?: HTMLElement;
protected render(): TemplateResult { protected render(): TemplateResult {
const index = this._viewIndex; const index = this._viewIndex;
@@ -36,7 +30,7 @@ class HcLovelace extends LitElement {
config: this.lovelaceConfig, config: this.lovelaceConfig,
rawConfig: this.lovelaceConfig, rawConfig: this.lovelaceConfig,
editMode: false, editMode: false,
urlPath: this.urlPath, urlPath: this.urlPath!,
enableFullEditMode: () => undefined, enableFullEditMode: () => undefined,
mode: "storage", mode: "storage",
locale: this.hass.locale, locale: this.hass.locale,
@@ -60,32 +54,17 @@ class HcLovelace extends LitElement {
const index = this._viewIndex; const index = this._viewIndex;
if (index !== undefined) { if (index !== undefined) {
const dashboardTitle = this.lovelaceConfig.title || this.urlPath;
const viewTitle =
this.lovelaceConfig.views[index].title ||
this.lovelaceConfig.views[index].path;
fireEvent(this, "cast-view-changed", {
title:
dashboardTitle || viewTitle
? `${dashboardTitle || ""}${
dashboardTitle && viewTitle ? ": " : ""
}${viewTitle || ""}`
: undefined,
});
const configBackground = const configBackground =
this.lovelaceConfig.views[index].background || this.lovelaceConfig.views[index].background ||
this.lovelaceConfig.background; this.lovelaceConfig.background;
if (configBackground) { if (configBackground) {
this._huiView!.style.setProperty( (this.shadowRoot!.querySelector(
"hui-view"
) as HTMLElement)!.style.setProperty(
"--lovelace-background", "--lovelace-background",
configBackground configBackground
); );
} else {
this._huiView!.style.removeProperty("--lovelace-background");
} }
} }
} }
@@ -118,22 +97,12 @@ class HcLovelace extends LitElement {
:host > * { :host > * {
flex: 1; flex: 1;
} }
hui-view {
background: var(--lovelace-background, var(--primary-background-color));
}
`; `;
} }
} }
export interface CastViewChanged {
title: string | undefined;
}
declare global { declare global {
interface HTMLElementTagNameMap { interface HTMLElementTagNameMap {
"hc-lovelace": HcLovelace; "hc-lovelace": HcLovelace;
} }
interface HASSDomEvents {
"cast-view-changed": CastViewChanged;
}
} }

View File

@@ -13,11 +13,7 @@ import {
ShowDemoMessage, ShowDemoMessage,
ShowLovelaceViewMessage, ShowLovelaceViewMessage,
} from "../../../../src/cast/receiver_messages"; } from "../../../../src/cast/receiver_messages";
import { import { ReceiverStatusMessage } from "../../../../src/cast/sender_messages";
ReceiverErrorCode,
ReceiverErrorMessage,
ReceiverStatusMessage,
} from "../../../../src/cast/sender_messages";
import { atLeastVersion } from "../../../../src/common/config/version"; import { atLeastVersion } from "../../../../src/common/config/version";
import { isNavigationClick } from "../../../../src/common/dom/is-navigation-click"; import { isNavigationClick } from "../../../../src/common/dom/is-navigation-click";
import { import {
@@ -44,10 +40,10 @@ export class HcMain extends HassElement {
@state() private _error?: string; @state() private _error?: string;
@state() private _urlPath?: string | null;
private _unsubLovelace?: UnsubscribeFunc; private _unsubLovelace?: UnsubscribeFunc;
private _urlPath?: string | null;
public processIncomingMessage(msg: HassMessage) { public processIncomingMessage(msg: HassMessage) {
if (msg.type === "connect") { if (msg.type === "connect") {
this._handleConnectMessage(msg); this._handleConnectMessage(msg);
@@ -72,10 +68,8 @@ export class HcMain extends HassElement {
!this._lovelaceConfig || !this._lovelaceConfig ||
this._lovelacePath === null || this._lovelacePath === null ||
// Guard against part of HA not being loaded yet. // Guard against part of HA not being loaded yet.
!this.hass || (this.hass &&
!this.hass.states || (!this.hass.states || !this.hass.config || !this.hass.services))
!this.hass.config ||
!this.hass.services
) { ) {
return html` return html`
<hc-launch-screen <hc-launch-screen
@@ -113,7 +107,6 @@ export class HcMain extends HassElement {
this._sendStatus(); this._sendStatus();
} }
}); });
this.addEventListener("dialog-closed", this._dialogClosed);
} }
private _sendStatus(senderId?: string) { private _sendStatus(senderId?: string) {
@@ -125,7 +118,7 @@ export class HcMain extends HassElement {
if (this.hass) { if (this.hass) {
status.hassUrl = this.hass.auth.data.hassUrl; status.hassUrl = this.hass.auth.data.hassUrl;
status.lovelacePath = this._lovelacePath; status.lovelacePath = this._lovelacePath!;
status.urlPath = this._urlPath; status.urlPath = this._urlPath;
} }
@@ -138,30 +131,6 @@ export class HcMain extends HassElement {
} }
} }
private _sendError(
error_code: number,
error_message: string,
senderId?: string
) {
const error: ReceiverErrorMessage = {
type: "receiver_error",
error_code,
error_message,
};
if (senderId) {
this.sendMessage(senderId, error);
} else {
for (const sender of castContext.getSenders()) {
this.sendMessage(sender.id, error);
}
}
}
private _dialogClosed = () => {
document.body.setAttribute("style", "overflow-y: auto !important");
};
private async _handleGetStatusMessage(msg: GetStatusMessage) { private async _handleGetStatusMessage(msg: GetStatusMessage) {
this._sendStatus(msg.senderId!); this._sendStatus(msg.senderId!);
} }
@@ -179,19 +148,15 @@ export class HcMain extends HassElement {
expires_in: 0, expires_in: 0,
}), }),
}); });
} catch (err: any) { } catch (err) {
const errorMessage = this._getErrorMessage(err); this._error = this._getErrorMessage(err);
this._error = errorMessage;
this._sendError(err, errorMessage);
return; return;
} }
let connection; let connection;
try { try {
connection = await createConnection({ auth }); connection = await createConnection({ auth });
} catch (err: any) { } catch (err) {
const errorMessage = this._getErrorMessage(err); this._error = this._getErrorMessage(err);
this._error = errorMessage;
this._sendError(err, errorMessage);
return; return;
} }
if (this.hass) { if (this.hass) {
@@ -203,29 +168,24 @@ export class HcMain extends HassElement {
} }
private async _handleShowLovelaceMessage(msg: ShowLovelaceViewMessage) { private async _handleShowLovelaceMessage(msg: ShowLovelaceViewMessage) {
this._showDemo = false;
// We should not get this command before we are connected. // We should not get this command before we are connected.
// Means a client got out of sync. Let's send status to them. // Means a client got out of sync. Let's send status to them.
if (!this.hass) { if (!this.hass) {
this._sendStatus(msg.senderId!); this._sendStatus(msg.senderId!);
this._error = "Cannot show Lovelace because we're not connected."; this._error = "Cannot show Lovelace because we're not connected.";
this._sendError(ReceiverErrorCode.NOT_CONNECTED, this._error);
return; return;
} }
this._error = undefined;
if (msg.urlPath === "lovelace") { if (msg.urlPath === "lovelace") {
msg.urlPath = null; msg.urlPath = null;
} }
this._lovelacePath = msg.viewPath;
if (!this._unsubLovelace || this._urlPath !== msg.urlPath) { if (!this._unsubLovelace || this._urlPath !== msg.urlPath) {
this._urlPath = msg.urlPath; this._urlPath = msg.urlPath;
this._lovelaceConfig = undefined;
if (this._unsubLovelace) { if (this._unsubLovelace) {
this._unsubLovelace(); this._unsubLovelace();
} }
const llColl = atLeastVersion(this.hass.connection.haVersion, 0, 107) const llColl = atLeastVersion(this.hass.connection.haVersion, 0, 107)
? getLovelaceCollection(this.hass.connection, msg.urlPath) ? getLovelaceCollection(this.hass!.connection, msg.urlPath)
: getLegacyLovelaceCollection(this.hass.connection); : getLegacyLovelaceCollection(this.hass!.connection);
// We first do a single refresh because we need to check if there is LL // We first do a single refresh because we need to check if there is LL
// configuration. // configuration.
try { try {
@@ -233,17 +193,9 @@ export class HcMain extends HassElement {
this._unsubLovelace = llColl.subscribe((lovelaceConfig) => this._unsubLovelace = llColl.subscribe((lovelaceConfig) =>
this._handleNewLovelaceConfig(lovelaceConfig) this._handleNewLovelaceConfig(lovelaceConfig)
); );
} catch (err: any) { } catch (err) {
if ( // eslint-disable-next-line
atLeastVersion(this.hass.connection.haVersion, 0, 107) && console.log("Error fetching Lovelace configuration", err, msg);
err.code !== "config_not_found"
) {
// eslint-disable-next-line
console.log("Error fetching Lovelace configuration", err, msg);
this._error = `Error fetching Lovelace configuration: ${err.message}`;
this._sendError(ReceiverErrorCode.FETCH_CONFIG_FAILED, this._error);
return;
}
// Generate a Lovelace config. // Generate a Lovelace config.
this._unsubLovelace = () => undefined; this._unsubLovelace = () => undefined;
await this._generateLovelaceConfig(); await this._generateLovelaceConfig();
@@ -258,6 +210,8 @@ export class HcMain extends HassElement {
loadLovelaceResources(resources, this.hass!.auth.data.hassUrl); loadLovelaceResources(resources, this.hass!.auth.data.hassUrl);
} }
} }
this._showDemo = false;
this._lovelacePath = msg.viewPath;
this._sendStatus(); this._sendStatus();
} }
@@ -278,7 +232,7 @@ export class HcMain extends HassElement {
} }
private _handleNewLovelaceConfig(lovelaceConfig: LovelaceConfig) { private _handleNewLovelaceConfig(lovelaceConfig: LovelaceConfig) {
castContext.setApplicationState(lovelaceConfig.title || ""); castContext.setApplicationState(lovelaceConfig.title!);
this._lovelaceConfig = lovelaceConfig; this._lovelaceConfig = lovelaceConfig;
} }

View File

@@ -1,3 +1,4 @@
import "web-animations-js/web-animations-next-lite.min";
import "../../../src/resources/ha-style"; import "../../../src/resources/ha-style";
import "../../../src/resources/roboto"; import "../../../src/resources/roboto";
import "./layout/hc-lovelace"; import "./layout/hc-lovelace";

View File

@@ -29,11 +29,6 @@ export const demoLovelaceArsaboo: DemoConfig["lovelace"] = (localize) => ({
}, },
], ],
}, },
{
title: "Energy distribution today",
type: "energy-distribution",
link_dashboard: true,
},
{ {
type: "thermostat", type: "thermostat",
entity: "climate.upstairs", entity: "climate.upstairs",
@@ -118,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: {
@@ -200,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: {
@@ -280,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: {
@@ -317,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",
}, },

View File

@@ -1,4 +1,3 @@
import { mdiTelevision } from "@mdi/js";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, state } from "lit/decorators"; import { customElement, state } from "lit/decorators";
import { CastManager } from "../../../src/cast/cast_manager"; import { CastManager } from "../../../src/cast/cast_manager";
@@ -28,7 +27,7 @@ class CastDemoRow extends LitElement implements LovelaceRow {
return html``; return html``;
} }
return html` return html`
<ha-svg-icon .path=${mdiTelevision}></ha-svg-icon> <ha-icon icon="hademo:television"></ha-icon>
<div class="flex"> <div class="flex">
<div class="name">Show Chromecast interface</div> <div class="name">Show Chromecast interface</div>
<google-cast-launcher></google-cast-launcher> <google-cast-launcher></google-cast-launcher>
@@ -73,7 +72,7 @@ class CastDemoRow extends LitElement implements LovelaceRow {
display: flex; display: flex;
align-items: center; align-items: center;
} }
ha-svg-icon { ha-icon {
padding: 8px; padding: 8px;
color: var(--paper-item-icon-color); color: var(--paper-item-icon-color);
} }

View File

@@ -19,7 +19,7 @@ export class HADemoCard extends LitElement implements LovelaceCard {
@property({ attribute: false }) public hass!: MockHomeAssistant; @property({ attribute: false }) public hass!: MockHomeAssistant;
@state() private _switching = false; @state() private _switching?: boolean;
private _hidden = localStorage.hide_demo_card; private _hidden = localStorage.hide_demo_card;
@@ -27,7 +27,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) {
@@ -44,7 +49,7 @@ export class HADemoCard extends LitElement implements LovelaceCard {
(conf) => html` (conf) => html`
${conf.name} ${conf.name}
<small> <small>
<a target="_blank" href=${conf.authorUrl}> <a target="_blank" href="${conf.authorUrl}">
${this.hass.localize( ${this.hass.localize(
"ui.panel.page-demo.cards.demo.demo_by", "ui.panel.page-demo.cards.demo.demo_by",
"name", "name",
@@ -94,7 +99,7 @@ export class HADemoCard extends LitElement implements LovelaceCard {
this._switching = true; this._switching = true;
try { try {
await setDemoConfig(this.hass, this.lovelace!, index); await setDemoConfig(this.hass, this.lovelace!, index);
} catch (err: any) { } catch (err) {
alert("Failed to switch config :-("); alert("Failed to switch config :-(");
} finally { } finally {
this._switching = false; this._switching = false;

View File

@@ -1,4 +1,11 @@
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";
import "./ha-demo"; import "./ha-demo";
/* polyfill for paper-dropdown */
setTimeout(() => {
import("web-animations-js/web-animations-next-lite.min");
}, 1000);

View File

@@ -20,9 +20,6 @@ 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";
class HaDemo extends HomeAssistantAppEl { class HaDemo extends HomeAssistantAppEl {
protected async _initializeHass() { protected async _initializeHass() {
@@ -50,12 +47,8 @@ class HaDemo extends HomeAssistantAppEl {
mockEvents(hass); mockEvents(hass);
mockMediaPlayer(hass); mockMediaPlayer(hass);
mockFrontend(hass); mockFrontend(hass);
mockEnergy(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]) => {
@@ -77,7 +70,7 @@ class HaDemo extends HomeAssistantAppEl {
} }
e.preventDefault(); e.preventDefault();
navigate(href); navigate(this, href);
}, },
{ capture: true } { capture: true }
); );

File diff suppressed because one or more lines are too long

View File

@@ -1,7 +0,0 @@
import { AreaRegistryEntry } from "../../../src/data/area_registry";
import type { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
export const mockAreaRegistry = (
hass: MockHomeAssistant,
data: AreaRegistryEntry[] = []
) => hass.mockWS("config/area_registry/list", () => data);

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,7 +0,0 @@
import { DeviceRegistryEntry } from "../../../src/data/device_registry";
import type { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
export const mockDeviceRegistry = (
hass: MockHomeAssistant,
data: DeviceRegistryEntry[] = []
) => hass.mockWS("config/device_registry/list", () => data);

View File

@@ -1,137 +0,0 @@
import { format, startOfToday, startOfTomorrow } from "date-fns";
import { EnergySolarForecasts } from "../../../src/data/energy";
import { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
export const mockEnergy = (hass: MockHomeAssistant) => {
hass.mockWS("energy/get_prefs", () => ({
energy_sources: [
{
type: "grid",
flow_from: [
{
stat_energy_from: "sensor.energy_consumption_tarif_1",
stat_cost: "sensor.energy_consumption_tarif_1_cost",
entity_energy_from: "sensor.energy_consumption_tarif_1",
entity_energy_price: null,
number_energy_price: null,
},
{
stat_energy_from: "sensor.energy_consumption_tarif_2",
stat_cost: "sensor.energy_consumption_tarif_2_cost",
entity_energy_from: "sensor.energy_consumption_tarif_2",
entity_energy_price: null,
number_energy_price: null,
},
],
flow_to: [
{
stat_energy_to: "sensor.energy_production_tarif_1",
stat_compensation: "sensor.energy_production_tarif_1_compensation",
entity_energy_to: "sensor.energy_production_tarif_1",
entity_energy_price: null,
number_energy_price: null,
},
{
stat_energy_to: "sensor.energy_production_tarif_2",
stat_compensation: "sensor.energy_production_tarif_2_compensation",
entity_energy_to: "sensor.energy_production_tarif_2",
entity_energy_price: null,
number_energy_price: null,
},
],
cost_adjustment_day: 0,
},
{
type: "solar",
stat_energy_from: "sensor.solar_production",
config_entry_solar_forecast: ["solar_forecast"],
},
/* {
type: "battery",
stat_energy_from: "sensor.battery_output",
stat_energy_to: "sensor.battery_input",
}, */
{
type: "gas",
stat_energy_from: "sensor.energy_gas",
stat_cost: "sensor.energy_gas_cost",
entity_energy_from: "sensor.energy_gas",
entity_energy_price: null,
number_energy_price: null,
},
],
device_consumption: [
{
stat_consumption: "sensor.energy_car",
},
{
stat_consumption: "sensor.energy_ac",
},
{
stat_consumption: "sensor.energy_washing_machine",
},
{
stat_consumption: "sensor.energy_dryer",
},
{
stat_consumption: "sensor.energy_heat_pump",
},
{
stat_consumption: "sensor.energy_boiler",
},
],
}));
hass.mockWS("energy/info", () => ({ cost_sensors: [] }));
hass.mockWS("energy/fossil_energy_consumption", ({ period }) => ({
start: period === "month" ? 250 : period === "day" ? 10 : 2,
}));
const todayString = format(startOfToday(), "yyyy-MM-dd");
const tomorrowString = format(startOfTomorrow(), "yyyy-MM-dd");
hass.mockWS(
"energy/solar_forecast",
(): EnergySolarForecasts => ({
solar_forecast: {
wh_hours: {
[`${todayString}T06:00:00`]: 0,
[`${todayString}T06:23:00`]: 6,
[`${todayString}T06:45:00`]: 39,
[`${todayString}T07:00:00`]: 28,
[`${todayString}T08:00:00`]: 208,
[`${todayString}T09:00:00`]: 352,
[`${todayString}T10:00:00`]: 544,
[`${todayString}T11:00:00`]: 748,
[`${todayString}T12:00:00`]: 1259,
[`${todayString}T13:00:00`]: 1361,
[`${todayString}T14:00:00`]: 1373,
[`${todayString}T15:00:00`]: 1370,
[`${todayString}T16:00:00`]: 1186,
[`${todayString}T17:00:00`]: 937,
[`${todayString}T18:00:00`]: 652,
[`${todayString}T19:00:00`]: 370,
[`${todayString}T20:00:00`]: 155,
[`${todayString}T21:48:00`]: 24,
[`${todayString}T22:36:00`]: 0,
[`${tomorrowString}T06:01:00`]: 0,
[`${tomorrowString}T06:23:00`]: 9,
[`${tomorrowString}T06:45:00`]: 47,
[`${tomorrowString}T07:00:00`]: 48,
[`${tomorrowString}T08:00:00`]: 473,
[`${tomorrowString}T09:00:00`]: 827,
[`${tomorrowString}T10:00:00`]: 1153,
[`${tomorrowString}T11:00:00`]: 1413,
[`${tomorrowString}T12:00:00`]: 1590,
[`${tomorrowString}T13:00:00`]: 1652,
[`${tomorrowString}T14:00:00`]: 1612,
[`${tomorrowString}T15:00:00`]: 1438,
[`${tomorrowString}T16:00:00`]: 1149,
[`${tomorrowString}T17:00:00`]: 830,
[`${tomorrowString}T18:00:00`]: 542,
[`${tomorrowString}T19:00:00`]: 311,
[`${tomorrowString}T20:00:00`]: 140,
[`${tomorrowString}T21:47:00`]: 22,
[`${tomorrowString}T22:34:00`]: 0,
},
},
})
);
};

View File

@@ -1,178 +0,0 @@
import { convertEntities } from "../../../src/fake_data/entity";
export const energyEntities = () =>
convertEntities({
"sensor.grid_fossil_fuel_percentage": {
entity_id: "sensor.grid_fossil_fuel_percentage",
state: "88.6",
attributes: {
unit_of_measurement: "%",
},
},
"sensor.solar_production": {
entity_id: "sensor.solar_production",
state: "88.6",
attributes: {
last_reset: "1970-01-01T00:00:00:00+00",
friendly_name: "Solar",
unit_of_measurement: "kWh",
},
},
"sensor.battery_input": {
entity_id: "sensor.battery_input",
state: "4",
attributes: {
last_reset: "1970-01-01T00:00:00:00+00",
friendly_name: "Battery Input",
unit_of_measurement: "kWh",
},
},
"sensor.battery_output": {
entity_id: "sensor.battery_output",
state: "3",
attributes: {
last_reset: "1970-01-01T00:00:00:00+00",
friendly_name: "Battery Output",
unit_of_measurement: "kWh",
},
},
"sensor.energy_consumption_tarif_1": {
entity_id: "sensor.energy_consumption_tarif_1 ",
state: "88.6",
attributes: {
last_reset: "1970-01-01T00:00:00:00+00",
friendly_name: "Grid consumption low tariff",
unit_of_measurement: "kWh",
},
},
"sensor.energy_consumption_tarif_2": {
entity_id: "sensor.energy_consumption_tarif_2",
state: "88.6",
attributes: {
last_reset: "1970-01-01T00:00:00:00+00",
friendly_name: "Grid consumption high tariff",
unit_of_measurement: "kWh",
},
},
"sensor.energy_production_tarif_1": {
entity_id: "sensor.energy_production_tarif_1",
state: "88.6",
attributes: {
last_reset: "1970-01-01T00:00:00:00+00",
friendly_name: "Returned to grid low tariff",
unit_of_measurement: "kWh",
},
},
"sensor.energy_production_tarif_2": {
entity_id: "sensor.energy_production_tarif_2",
state: "88.6",
attributes: {
last_reset: "1970-01-01T00:00:00:00+00",
friendly_name: "Returned to grid high tariff",
unit_of_measurement: "kWh",
},
},
"sensor.energy_consumption_tarif_1_cost": {
entity_id: "sensor.energy_consumption_tarif_1_cost",
state: "2",
attributes: {
last_reset: "1970-01-01T00:00:00:00+00",
unit_of_measurement: "EUR",
},
},
"sensor.energy_consumption_tarif_2_cost": {
entity_id: "sensor.energy_consumption_tarif_2_cost",
state: "2",
attributes: {
last_reset: "1970-01-01T00:00:00:00+00",
unit_of_measurement: "EUR",
},
},
"sensor.energy_production_tarif_1_compensation": {
entity_id: "sensor.energy_production_tarif_1_compensation",
state: "2",
attributes: {
last_reset: "1970-01-01T00:00:00:00+00",
unit_of_measurement: "EUR",
},
},
"sensor.energy_production_tarif_2_compensation": {
entity_id: "sensor.energy_production_tarif_2_compensation",
state: "2",
attributes: {
last_reset: "1970-01-01T00:00:00:00+00",
unit_of_measurement: "EUR",
},
},
"sensor.energy_gas_cost": {
entity_id: "sensor.energy_gas_cost",
state: "2",
attributes: {
last_reset: "1970-01-01T00:00:00:00+00",
unit_of_measurement: "EUR",
},
},
"sensor.energy_gas": {
entity_id: "sensor.energy_gas",
state: "4",
attributes: {
last_reset: "1970-01-01T00:00:00:00+00",
friendly_name: "Gas",
unit_of_measurement: "m³",
},
},
"sensor.energy_car": {
entity_id: "sensor.energy_car",
state: "4",
attributes: {
last_reset: "1970-01-01T00:00:00:00+00",
friendly_name: "Electric car",
unit_of_measurement: "kWh",
},
},
"sensor.energy_ac": {
entity_id: "sensor.energy_ac",
state: "3",
attributes: {
last_reset: "1970-01-01T00:00:00:00+00",
friendly_name: "Air conditioning",
unit_of_measurement: "kWh",
},
},
"sensor.energy_washing_machine": {
entity_id: "sensor.energy_washing_machine",
state: "6",
attributes: {
last_reset: "1970-01-01T00:00:00:00+00",
friendly_name: "Washing machine",
unit_of_measurement: "kWh",
},
},
"sensor.energy_dryer": {
entity_id: "sensor.energy_dryer",
state: "5.5",
attributes: {
last_reset: "1970-01-01T00:00:00:00+00",
friendly_name: "Dryer",
unit_of_measurement: "kWh",
},
},
"sensor.energy_heat_pump": {
entity_id: "sensor.energy_heat_pump",
state: "6",
attributes: {
last_reset: "1970-01-01T00:00:00:00+00",
friendly_name: "Heat pump",
unit_of_measurement: "kWh",
},
},
"sensor.energy_boiler": {
entity_id: "sensor.energy_boiler",
state: "7",
attributes: {
last_reset: "1970-01-01T00:00:00:00+00",
friendly_name: "Boiler",
unit_of_measurement: "kWh",
},
},
});

View File

@@ -1,7 +0,0 @@
import { EntityRegistryEntry } from "../../../src/data/entity_registry";
import type { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
export const mockEntityRegistry = (
hass: MockHomeAssistant,
data: EntityRegistryEntry[] = []
) => hass.mockWS("config/entity_registry/list", () => data);

View File

@@ -1,59 +0,0 @@
import { HassioSupervisorInfo } from "../../../src/data/hassio/supervisor";
import type { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
export const mockHassioSupervisor = (hass: MockHomeAssistant) => {
hass.config.components.push("hassio");
hass.mockWS("supervisor/api", (msg) => {
if (msg.endpoint === "/supervisor/info") {
const data: HassioSupervisorInfo = {
version: "2021.10.dev0805",
version_latest: "2021.10.dev0806",
update_available: true,
channel: "dev",
arch: "aarch64",
supported: true,
healthy: true,
ip_address: "172.30.32.2",
wait_boot: 5,
timezone: "America/Los_Angeles",
logging: "info",
debug: false,
debug_block: false,
diagnostics: true,
addons: [
{
name: "Visual Studio Code",
slug: "a0d7b954_vscode",
description:
"Fully featured VSCode experience, to edit your HA config in the browser, including auto-completion!",
state: "started",
version: "3.6.2",
version_latest: "3.6.2",
update_available: false,
repository: "a0d7b954",
icon: false,
logo: true,
},
{
name: "Z-Wave JS",
slug: "core_zwave_js",
description:
"Control a ZWave network with Home Assistant Z-Wave JS",
state: "started",
version: "0.1.45",
version_latest: "0.1.45",
update_available: false,
repository: "core",
icon: true,
logo: true,
},
] as any,
addons_repositories: [
"https://github.com/hassio-addons/repository",
] as any,
};
return data;
}
return Promise.reject(`${msg.method} ${msg.endpoint} is not implemented`);
});
};

View File

@@ -1,12 +1,4 @@
import {
addDays,
addHours,
addMonths,
differenceInHours,
endOfDay,
} 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 {
@@ -72,335 +64,17 @@ const generateHistory = (state, deltas) => {
const incrementalUnits = ["clients", "queries", "ads"]; const incrementalUnits = ["clients", "queries", "ads"];
const generateMeanStatistics = (
id: string,
start: Date,
end: Date,
period: "5minute" | "hour" | "day" | "month" = "hour",
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(),
end: currentDate.toISOString(),
mean,
min: mean - Math.random() * maxDiff,
max: mean + Math.random() * maxDiff,
last_reset: "1970-01-01T00:00:00+00:00",
state: mean,
sum: null,
});
lastVal = mean;
currentDate =
period === "day"
? addDays(currentDate, 1)
: period === "month"
? addMonths(currentDate, 1)
: addHours(currentDate, 1);
}
return statistics;
};
const generateSumStatistics = (
id: string,
start: Date,
end: Date,
period: "5minute" | "hour" | "day" | "month" = "hour",
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(),
end: currentDate.toISOString(),
mean: null,
min: null,
max: null,
last_reset: "1970-01-01T00:00:00+00:00",
state: initValue + sum,
sum,
});
currentDate =
period === "day"
? addDays(currentDate, 1)
: period === "month"
? addMonths(currentDate, 1)
: addHours(currentDate, 1);
}
return statistics;
};
const generateCurvedStatistics = (
id: string,
start: Date,
end: Date,
_period: "5minute" | "hour" | "day" | "month" = "hour",
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(),
end: 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,
period: "5minute" | "hour" | "day" | "month"
) => StatisticValue[]
> = {
"sensor.energy_consumption_tarif_1": (
id: string,
start: Date,
end: Date,
period = "hour"
) => {
if (period !== "hour") {
return generateSumStatistics(
id,
start,
end,
period,
0,
period === "day" ? 17 : 504
);
}
const morningEnd = new Date(start.getTime() + 10 * 60 * 60 * 1000);
const morningLow = generateSumStatistics(
id,
start,
morningEnd,
period,
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,
period,
morningFinalVal,
0
);
const eveningLow = generateSumStatistics(
id,
eveningStart,
end,
period,
morningFinalVal,
0.7
);
return [...morningLow, ...empty, ...eveningLow];
},
"sensor.energy_consumption_tarif_2": (
id: string,
start: Date,
end: Date,
period = "hour"
) => {
if (period !== "hour") {
return generateSumStatistics(
id,
start,
end,
period,
0,
period === "day" ? 17 : 504
);
}
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,
period,
0,
0.3
);
const highTarifFinalVal = highTarif.length
? highTarif[highTarif.length - 1].sum!
: 0;
const morning = generateSumStatistics(id, start, morningEnd, period, 0, 0);
const evening = generateSumStatistics(
id,
eveningStart,
end,
period,
highTarifFinalVal,
0
);
return [...morning, ...highTarif, ...evening];
},
"sensor.energy_production_tarif_1": (id, start, end, period = "hour") =>
generateSumStatistics(id, start, end, period, 0, 0),
"sensor.energy_production_tarif_1_compensation": (
id,
start,
end,
period = "hour"
) => generateSumStatistics(id, start, end, period, 0, 0),
"sensor.energy_production_tarif_2": (id, start, end, period = "hour") => {
if (period !== "hour") {
return generateSumStatistics(
id,
start,
end,
period,
0,
period === "day" ? 17 : 504
);
}
const productionStart = new Date(start.getTime() + 9 * 60 * 60 * 1000);
const productionEnd = new Date(start.getTime() + 21 * 60 * 60 * 1000);
const dayEnd = new Date(endOfDay(productionEnd));
const production = generateCurvedStatistics(
id,
productionStart,
productionEnd,
period,
0,
0.15,
true
);
const productionFinalVal = production.length
? production[production.length - 1].sum!
: 0;
const morning = generateSumStatistics(
id,
start,
productionStart,
period,
0,
0
);
const evening = generateSumStatistics(
id,
productionEnd,
dayEnd,
period,
productionFinalVal,
0
);
const rest = generateSumStatistics(
id,
dayEnd,
end,
period,
productionFinalVal,
1
);
return [...morning, ...production, ...evening, ...rest];
},
"sensor.solar_production": (id, start, end, period = "hour") => {
if (period !== "hour") {
return generateSumStatistics(
id,
start,
end,
period,
0,
period === "day" ? 17 : 504
);
}
const productionStart = new Date(start.getTime() + 7 * 60 * 60 * 1000);
const productionEnd = new Date(start.getTime() + 23 * 60 * 60 * 1000);
const dayEnd = new Date(endOfDay(productionEnd));
const production = generateCurvedStatistics(
id,
productionStart,
productionEnd,
period,
0,
0.3,
true
);
const productionFinalVal = production.length
? production[production.length - 1].sum!
: 0;
const morning = generateSumStatistics(
id,
start,
productionStart,
period,
0,
0
);
const evening = generateSumStatistics(
id,
productionEnd,
dayEnd,
period,
productionFinalVal,
0
);
const rest = generateSumStatistics(
id,
dayEnd,
end,
period,
productionFinalVal,
2
);
return [...morning, ...production, ...evening, ...rest];
},
};
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(",");
@@ -421,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,
@@ -466,42 +140,4 @@ export const mockHistory = (mockHass: MockHomeAssistant) => {
return results; return results;
} }
); );
mockHass.mockWS("history/list_statistic_ids", () => []);
mockHass.mockWS(
"history/statistics_during_period",
({ statistic_ids, start_time, end_time, period }, hass) => {
const start = new Date(start_time);
const end = end_time ? new Date(end_time) : new Date();
const statistics: Record<string, StatisticValue[]> = {};
statistic_ids.forEach((id: string) => {
if (id in statisticsFunctions) {
statistics[id] = statisticsFunctions[id](id, start, end, period);
} 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,
period,
state,
state * (state > 80 ? 0.01 : 0.05)
)
: generateMeanStatistics(
id,
start,
end,
period,
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());
@@ -23,9 +24,9 @@ customElements.whenDefined("hui-view").then(() => {
// eslint-disable-next-line // eslint-disable-next-line
const HUIView = customElements.get("hui-view"); const HUIView = customElements.get("hui-view");
// Patch HUI-VIEW to make the lovelace object available to the demo card // Patch HUI-VIEW to make the lovelace object available to the demo card
const oldCreateCard = HUIView!.prototype.createCardElement; const oldCreateCard = HUIView.prototype.createCardElement;
HUIView!.prototype.createCardElement = function (config) { HUIView.prototype.createCardElement = function (config) {
const el = oldCreateCard.call(this, config); const el = oldCreateCard.call(this, config);
if (el.tagName === "HA-DEMO-CARD") { if (el.tagName === "HA-DEMO-CARD") {
(el as HADemoCard).lovelace = this.lovelace; (el as HADemoCard).lovelace = this.lovelace;

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 147 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

View File

@@ -1,35 +0,0 @@
#!/bin/bash
TARGET_LABEL="needs design preview"
if [[ "$NETLIFY" != "true" ]]; then
echo "This script can only be run on Netlify"
exit 1
fi
function createStatus() {
state="$1"
description="$2"
target_url="$3"
curl -X POST -H "Accept: application/vnd.github.v3+json" -H "Authorization: token $GITHUB_TOKEN" \
"https://api.github.com/repos/home-assistant/frontend/statuses/$COMMIT_REF" \
-d '{"state": "'"${state}"'", "context": "Netlify/Design Preview Build", "description": "'"$description"'", "target_url": "'"$target_url"'"}'
}
if [[ "${PULL_REQUEST}" == "true" ]]; then
if [[ "$(curl -sSLf -H "Accept: application/vnd.github.v3+json" -H "Authorization: token $GITHUB_TOKEN" \
"https://api.github.com/repos/home-assistant/frontend/pulls/${REVIEW_ID}" | jq '.labels[].name' -r)" =~ "$TARGET_LABEL" ]]; then
createStatus "pending" "Building design preview" "https://app.netlify.com/sites/home-assistant-gallery/deploys/$BUILD_ID"
gulp build-gallery
if [ $? -eq 0 ]; then
createStatus "success" "Build complete" "$DEPLOY_PRIME_URL"
else
createStatus "error" "Build failed" "https://app.netlify.com/sites/home-assistant-gallery/deploys/$BUILD_ID"
fi
else
createStatus "success" "Build was not requested by PR label"
fi
elif [[ "$INCOMING_HOOK_BODY" == "NIGHTLY" ]]; then
gulp build-gallery
fi

View File

@@ -1,52 +0,0 @@
module.exports = [
{
// This section has no header and so all page links are shown directly in the sidebar
category: "concepts",
pages: ["home"],
},
{
category: "lovelace",
// Label for in the sidebar
header: "Lovelace",
// Specify order of pages. Any pages in the category folder but not listed here will
// automatically be added after the pages listed here.
pages: ["introduction"],
},
{
category: "automation",
header: "Automation",
pages: [
"editor-trigger",
"editor-condition",
"editor-action",
"trace",
"trace-timeline",
],
},
{
category: "components",
header: "Components",
},
{
category: "more-info",
header: "More Info dialogs",
},
{
category: "misc",
header: "Miscelaneous",
},
{
category: "brand",
header: "Brand",
},
{
category: "user-test",
header: "Users",
pages: ["user-types", "configuration-menu"],
},
{
category: "design.home-assistant.io",
header: "About",
},
];

View File

@@ -1,146 +0,0 @@
import { Button } from "@material/mwc-button";
import { html, LitElement, css, TemplateResult } from "lit";
import { customElement, property } from "lit/decorators";
import { applyThemesOnElement } from "../../../src/common/dom/apply_themes_on_element";
import { fireEvent } from "../../../src/common/dom/fire_event";
import "../../../src/components/ha-card";
@customElement("demo-black-white-row")
class DemoBlackWhiteRow extends LitElement {
@property() title!: string;
@property() value!: any;
@property() disabled = false;
protected render(): TemplateResult {
return html`
<div class="row">
<div class="content light">
<ha-card .header=${this.title}>
<div class="card-content">
<slot name="light"></slot>
</div>
<div class="card-actions">
<mwc-button
.disabled=${this.disabled}
@click=${this.handleSubmit}
>
Submit
</mwc-button>
</div>
</ha-card>
</div>
<div class="content dark">
<ha-card .header=${this.title}>
<div class="card-content">
<slot name="dark"></slot>
</div>
<div class="card-actions">
<mwc-button
.disabled=${this.disabled}
@click=${this.handleSubmit}
>
Submit
</mwc-button>
</div>
</ha-card>
<pre>${JSON.stringify(this.value, undefined, 2)}</pre>
</div>
</div>
`;
}
firstUpdated(changedProps) {
super.firstUpdated(changedProps);
applyThemesOnElement(
this.shadowRoot!.querySelector(".dark"),
{
default_theme: "default",
default_dark_theme: "default",
themes: {},
darkMode: true,
theme: "default",
},
undefined,
undefined,
true
);
}
handleSubmit(ev) {
const content = (ev.target as Button).closest(".content")!;
fireEvent(this, "submitted" as any, {
slot: content.classList.contains("light") ? "light" : "dark",
});
}
static styles = css`
.row {
display: flex;
}
.content {
padding: 50px 0;
background-color: var(--primary-background-color);
}
.light {
flex: 1;
padding-left: 50px;
padding-right: 50px;
box-sizing: border-box;
}
.light ha-card {
margin-left: auto;
}
.dark {
display: flex;
flex: 1;
padding-left: 50px;
box-sizing: border-box;
flex-wrap: wrap;
}
ha-card {
width: 400px;
}
pre {
width: 300px;
margin: 0 16px 0;
overflow: auto;
color: var(--primary-text-color);
}
.card-actions {
display: flex;
flex-direction: row-reverse;
border-top: none;
}
@media only screen and (max-width: 1500px) {
.light {
flex: initial;
}
}
@media only screen and (max-width: 1000px) {
.light,
.dark {
padding: 16px;
}
.row,
.dark {
flex-direction: column;
}
ha-card {
margin: 0 auto;
width: 100%;
max-width: 400px;
}
pre {
margin: 16px auto;
}
}
`;
}
declare global {
interface HTMLElementTagNameMap {
"demo-black-white-row": DemoBlackWhiteRow;
}
}

View File

@@ -0,0 +1,129 @@
import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element";
import { safeLoad } from "js-yaml";
import { createCardElement } from "../../../src/panels/lovelace/create-element/create-card-element";
class DemoCard extends PolymerElement {
static get template() {
return html`
<style>
.root {
display: flex;
}
h2 {
margin: 0 0 20px;
color: var(--primary-color);
}
h2 small {
font-size: 0.5em;
color: var(--primary-text-color);
}
#card {
max-width: 400px;
width: 100vw;
}
pre {
width: 400px;
margin: 0 16px;
overflow: auto;
color: var(--primary-text-color);
}
@media only screen and (max-width: 800px) {
.root {
flex-direction: column;
}
pre {
margin: 16px 0;
}
}
</style>
<h2>
[[config.heading]]
<template is="dom-if" if="[[_size]]">
<small>(size [[_size]])</small>
</template>
</h2>
<div class="root">
<div id="card"></div>
<template is="dom-if" if="[[showConfig]]">
<pre>[[_trim(config.config)]]</pre>
</template>
</div>
`;
}
static get properties() {
return {
hass: {
type: Object,
observer: "_hassChanged",
},
config: {
type: Object,
observer: "_configChanged",
},
showConfig: Boolean,
_size: {
type: Number,
},
};
}
ready() {
super.ready();
}
_configChanged(config) {
const card = this.$.card;
while (card.lastChild) {
card.removeChild(card.lastChild);
}
const el = this._createCardElement(safeLoad(config.config)[0]);
card.appendChild(el);
this._getSize(el);
}
async _getSize(el) {
await customElements.whenDefined(el.localName);
if (!("getCardSize" in el)) {
this._size = undefined;
return;
}
this._size = await el.getCardSize();
}
_createCardElement(cardConfig) {
const element = createCardElement(cardConfig);
if (this.hass) {
element.hass = this.hass;
}
element.addEventListener(
"ll-rebuild",
(ev) => {
ev.stopPropagation();
this._rebuildCard(element, cardConfig);
},
{ once: true }
);
return element;
}
_rebuildCard(cardElToReplace, config) {
const newCardEl = this._createCardElement(config);
cardElToReplace.parentElement.replaceChild(newCardEl, cardElToReplace);
}
_hassChanged(hass) {
const card = this.$.card.lastChild;
if (card) card.hass = hass;
}
_trim(config) {
return config.trim();
}
}
customElements.define("demo-card", DemoCard);

View File

@@ -1,129 +0,0 @@
import { load } from "js-yaml";
import { html, css, LitElement, PropertyValues } from "lit";
import { customElement, property, query, state } from "lit/decorators";
import { createCardElement } from "../../../src/panels/lovelace/create-element/create-card-element";
import { HomeAssistant } from "../../../src/types";
export interface DemoCardConfig {
heading: string;
config: string;
}
@customElement("demo-card")
class DemoCard extends LitElement {
@property() public hass!: HomeAssistant;
@property() public config!: DemoCardConfig;
@property() public showConfig = false;
@state() private _size?: number;
@query("#card") private _card!: HTMLElement;
render() {
return html`
<h2>
${this.config.heading}
${this._size !== undefined
? html`<small>(size ${this._size})</small>`
: ""}
</h2>
<div class="root">
<div id="card"></div>
${this.showConfig ? html`<pre>${this.config.config.trim()}</pre>` : ""}
</div>
`;
}
updated(changedProps: PropertyValues) {
super.updated(changedProps);
if (changedProps.has("config")) {
const card = this._card;
while (card.lastChild) {
card.removeChild(card.lastChild);
}
const el = this._createCardElement((load(this.config.config) as any)[0]);
card.appendChild(el);
this._getSize(el);
}
if (changedProps.has("hass")) {
const card = this._card.lastChild;
if (card) {
(card as any).hass = this.hass;
}
}
}
async _getSize(el) {
await customElements.whenDefined(el.localName);
if (!("getCardSize" in el)) {
this._size = undefined;
return;
}
this._size = await el.getCardSize();
}
_createCardElement(cardConfig) {
const element = createCardElement(cardConfig);
if (this.hass) {
element.hass = this.hass;
}
element.addEventListener(
"ll-rebuild",
(ev) => {
ev.stopPropagation();
this._rebuildCard(element, cardConfig);
},
{ once: true }
);
return element;
}
_rebuildCard(cardElToReplace, config) {
const newCardEl = this._createCardElement(config);
cardElToReplace.parentElement.replaceChild(newCardEl, cardElToReplace);
}
static styles = css`
.root {
display: flex;
}
h2 {
margin: 0 0 20px;
color: var(--primary-color);
}
h2 small {
font-size: 0.5em;
color: var(--primary-text-color);
}
#card {
max-width: 400px;
width: 100vw;
}
pre {
width: 400px;
margin: 0 16px;
overflow: auto;
color: var(--primary-text-color);
}
@media only screen and (max-width: 800px) {
.root {
flex-direction: column;
}
pre {
margin: 16px 0;
}
}
`;
}
declare global {
interface HTMLElementTagNameMap {
"demo-card": DemoCard;
}
}

View File

@@ -0,0 +1,83 @@
import "@polymer/app-layout/app-toolbar/app-toolbar";
import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element";
import { applyThemesOnElement } from "../../../src/common/dom/apply_themes_on_element";
import "../../../src/components/ha-formfield";
import "../../../src/components/ha-switch";
import "./demo-card";
class DemoCards extends PolymerElement {
static get template() {
return html`
<style>
#container {
min-height: calc(100vh - 128px);
background: var(--primary-background-color);
}
.cards {
display: flex;
flex-wrap: wrap;
justify-content: center;
}
demo-card {
margin: 16px 16px 32px;
}
app-toolbar {
background-color: var(--light-primary-color);
}
.filters {
margin-left: 60px;
}
ha-formfield {
margin-right: 16px;
}
</style>
<app-toolbar>
<div class="filters">
<ha-formfield label="Show config">
<ha-switch checked="[[_showConfig]]" on-change="_showConfigToggled">
</ha-switch>
</ha-formfield>
<ha-formfield label="Dark theme">
<ha-switch on-change="_darkThemeToggled"> </ha-switch>
</ha-formfield>
</div>
</app-toolbar>
<div id="container">
<div class="cards">
<template is="dom-repeat" items="[[configs]]">
<demo-card
config="[[item]]"
show-config="[[_showConfig]]"
hass="[[hass]]"
></demo-card>
</template>
</div>
</div>
`;
}
static get properties() {
return {
configs: Object,
hass: Object,
_showConfig: {
type: Boolean,
value: false,
},
};
}
_showConfigToggled(ev) {
this._showConfig = ev.target.checked;
}
_darkThemeToggled(ev) {
applyThemesOnElement(this.$.container, { themes: {} }, "default", {
dark: ev.target.checked,
});
}
}
customElements.define("demo-cards", DemoCards);

View File

@@ -1,91 +0,0 @@
import "@polymer/app-layout/app-toolbar/app-toolbar";
import { html, css, LitElement } from "lit";
import { customElement, property, query, state } from "lit/decorators";
import { applyThemesOnElement } from "../../../src/common/dom/apply_themes_on_element";
import "../../../src/components/ha-formfield";
import "../../../src/components/ha-switch";
import { HomeAssistant } from "../../../src/types";
import "./demo-card";
import type { DemoCardConfig } from "./demo-card";
@customElement("demo-cards")
class DemoCards extends LitElement {
@property() public configs!: DemoCardConfig[];
@property() public hass!: HomeAssistant;
@state() private _showConfig = false;
@query("#container") private _container!: HTMLElement;
render() {
return html`
<app-toolbar>
<div class="filters">
<ha-formfield label="Show config">
<ha-switch
.checked=${this._showConfig}
@change=${this._showConfigToggled}
>
</ha-switch>
</ha-formfield>
<ha-formfield label="Dark theme">
<ha-switch @change=${this._darkThemeToggled}> </ha-switch>
</ha-formfield>
</div>
</app-toolbar>
<div id="container">
<div class="cards">
${this.configs.map(
(config) => html`
<demo-card
.config=${config}
.showConfig=${this._showConfig}
.hass=${this.hass}
></demo-card>
`
)}
</div>
</div>
`;
}
_showConfigToggled(ev) {
this._showConfig = ev.target.checked;
}
_darkThemeToggled(ev) {
applyThemesOnElement(this._container, { themes: {} } as any, "default", {
dark: ev.target.checked,
});
}
static styles = css`
.cards {
display: flex;
flex-wrap: wrap;
justify-content: center;
}
demo-card {
margin: 16px 16px 32px;
}
app-toolbar {
background-color: var(--light-primary-color);
}
.filters {
margin-left: 60px;
}
ha-formfield {
margin-right: 16px;
}
#container {
background-color: var(--primary-background-color);
}
`;
}
declare global {
interface HTMLElementTagNameMap {
"demo-cards": DemoCards;
}
}

View File

@@ -1,66 +0,0 @@
import { html, css } from "lit";
import { customElement, property } from "lit/decorators";
import { until } from "lit/directives/until";
import { HaMarkdown } from "../../../src/components/ha-markdown";
import { PAGES } from "../../build/import-pages";
@customElement("page-description")
class PageDescription extends HaMarkdown {
@property() public page!: string;
render() {
if (!PAGES[this.page].description) {
return html``;
}
return html`
<div class="heading">
<div class="title">
${PAGES[this.page].metadata.title || this.page.split("/")[1]}
</div>
<div class="subtitle">${PAGES[this.page].metadata.subtitle}</div>
</div>
${until(
PAGES[this.page]
.description()
.then((content) => html`<div class="root">${content}</div>`),
""
)}
`;
}
static styles = [
HaMarkdown.styles,
css`
.heading {
padding: 16px;
border-bottom: 1px solid var(--secondary-background-color);
}
.title {
font-size: 42px;
line-height: 56px;
padding-bottom: 8px;
}
.subtitle {
font-size: 18px;
line-height: 24px;
}
.root {
max-width: 800px;
margin: 16px auto;
}
.root > *:first-child {
margin-top: 0;
}
.root > *:last-child {
margin-bottom: 0;
}
`,
];
}
declare global {
interface HTMLElementTagNameMap {
"page-description": PageDescription;
}
}

View File

@@ -1,11 +0,0 @@
export const LONG_TEXT = `
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc laoreet velit ut elit volutpat, eget ultrices odio lacinia. In imperdiet malesuada est, nec sagittis metus ultricies quis. Sed nisl ex, convallis porttitor ante quis, hendrerit tristique justo. Mauris pharetra venenatis augue, eu maximus sem cursus in. Quisque sed consequat risus. Suspendisse facilisis ligula a odio consectetur condimentum. Curabitur vehicula elit nec augue mollis, et volutpat massa dictum.
Nam pellentesque auctor rutrum. Suspendisse elit est, sodales vel diam nec, porttitor faucibus massa. Ut pretium ac orci eu pharetra. Praesent in nibh at magna viverra rutrum eu vitae tortor. Etiam eget sem ex. Fusce tristique odio nec lacus mattis, vitae tempor nunc malesuada. Maecenas faucibus magna vel libero maximus egestas. Vestibulum luctus semper velit, in lobortis risus tempus non. Curabitur bibendum ornare commodo. Quisque commodo neque sit amet tincidunt lacinia. Proin elementum ante velit, eu congue nulla semper quis. Pellentesque consequat vel nunc at scelerisque. Mauris sit amet venenatis diam, blandit viverra leo. Integer commodo laoreet orci.
Curabitur ipsum tortor, sodales ut augue sed, commodo porttitor libero. Pellentesque molestie vitae mi consectetur tempor. In sed lectus consequat, lobortis neque non, semper ipsum. Etiam eget ex et nibh sagittis pulvinar lacinia ac mauris. Aenean ligula eros, viverra ac nibh at, venenatis semper quam. Sed interdum ligula sit amet massa tincidunt tincidunt. Suspendisse potenti. Aliquam egestas facilisis est, sed faucibus erat scelerisque id. Duis dolor quam, viverra vitae orci euismod, laoreet pellentesque justo. Nunc malesuada non erat at ullamcorper. Mauris eget posuere odio. Vestibulum turpis nunc, pharetra eget ante in, feugiat mollis justo. Proin porttitor, diam nec vulputate pretium, tellus arcu rhoncus turpis, a blandit nisi nulla quis arcu. Nunc ac ullamcorper ligula, nec facilisis leo.
In vitae eros sollicitudin, iaculis ex eget, egestas orci. Etiam sed pretium lorem. Nam nisi enim, consectetur sit amet semper ac, semper pharetra diam. In pulvinar neque sapien, ac ullamcorper est lacinia a. Etiam tincidunt velit sed diam malesuada, eu ornare ex consectetur. Phasellus in imperdiet tellus. Sed bibendum, dui sit amet fringilla aliquet, enim odio sollicitudin lorem, vel semper turpis mauris vel mauris. Aenean congue magna ac massa cursus, in dictum orci commodo. Pellentesque mollis velit in sollicitudin tincidunt. Vestibulum et efficitur nulla.
Quisque posuere, velit sed porttitor dapibus, neque augue fringilla felis, eu luctus nisi nisl nec ipsum. Curabitur pellentesque ac lectus eget ultricies. Vestibulum est dolor, lacinia pharetra vulputate a, facilisis a magna. Nam vitae arcu nibh. Praesent finibus blandit ante, ac gravida ex mollis eget. Donec quam est, pulvinar vitae neque ut, bibendum aliquam erat. Nullam mollis arcu at sem tincidunt, in tristique lectus facilisis. Aenean ut lacus vel nisl finibus iaculis non a turpis. Integer eget ipsum ante. Donec nunc neque, vestibulum ac magna ac, posuere scelerisque dui. Pellentesque massa nibh, rhoncus id dolor quis, placerat posuere turpis. Donec aliquet augue nisi, eu finibus dui auctor et. Vestibulum eu varius lorem. Quisque lectus ante, malesuada pretium risus eget, interdum mattis enim.
`;

View File

@@ -1,22 +1,12 @@
import { dump } from "js-yaml"; import { safeDump } from "js-yaml";
import { html, css, LitElement, TemplateResult } from "lit"; import { html, css, LitElement, TemplateResult } from "lit";
import { customElement, property } from "lit/decorators"; import { customElement, property } from "lit/decorators";
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 { getEntity } from "../../../../src/fake_data/entity"; 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";
const ENTITIES = [ const actions = [
getEntity("scene", "kitchen_morning", "scening", {
friendly_name: "Kitchen Morning",
}),
getEntity("media_player", "kitchen", "playing", {
friendly_name: "Sonos Kitchen",
}),
];
const ACTIONS = [
{ wait_template: "{{ true }}", alias: "Something with an alias" }, { wait_template: "{{ true }}", alias: "Something with an alias" },
{ delay: "0:05" }, { delay: "0:05" },
{ wait_template: "{{ true }}" }, { wait_template: "{{ true }}" },
@@ -29,20 +19,8 @@ const ACTIONS = [
device_id: "abcdefgh", device_id: "abcdefgh",
domain: "plex", domain: "plex",
entity_id: "media_player.kitchen", entity_id: "media_player.kitchen",
type: "turn_on",
}, },
{ scene: "scene.kitchen_morning" }, { scene: "scene.kitchen_morning" },
{
service: "scene.turn_on",
target: { entity_id: "scene.kitchen_morning" },
metadata: {},
},
{
service: "media_player.play_media",
target: { entity_id: "media_player.kitchen" },
data: { media_content_id: "", media_content_type: "" },
metadata: { title: "Happy Song" },
},
{ {
wait_for_trigger: [ wait_for_trigger: [
{ {
@@ -74,11 +52,11 @@ export class DemoAutomationDescribeAction extends LitElement {
} }
return html` return html`
<ha-card header="Actions"> <ha-card header="Actions">
${ACTIONS.map( ${actions.map(
(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>
` `
)} )}
@@ -90,7 +68,6 @@ export class DemoAutomationDescribeAction extends LitElement {
super.firstUpdated(changedProps); super.firstUpdated(changedProps);
const hass = provideHass(this); const hass = provideHass(this);
hass.updateTranslations(null, "en"); hass.updateTranslations(null, "en");
hass.addEntities(ENTITIES);
} }
static get styles() { static get styles() {

View File

@@ -1,8 +1,8 @@
import { dump } from "js-yaml"; import { safeDump } from "js-yaml";
import { html, css, LitElement, TemplateResult } from "lit"; import { html, css, LitElement, TemplateResult } from "lit";
import { customElement } from "lit/decorators"; import { customElement } from "lit/decorators";
import "../../../../src/components/ha-card"; import "../../../src/components/ha-card";
import { describeCondition } from "../../../../src/data/automation_i18n"; import { describeCondition } from "../../../src/data/automation_i18n";
const conditions = [ const conditions = [
{ condition: "and" }, { condition: "and" },
@@ -26,7 +26,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,8 +1,8 @@
import { dump } from "js-yaml"; import { safeDump } from "js-yaml";
import { html, css, LitElement, TemplateResult } from "lit"; import { html, css, LitElement, TemplateResult } from "lit";
import { customElement } from "lit/decorators"; import { customElement } from "lit/decorators";
import "../../../../src/components/ha-card"; import "../../../src/components/ha-card";
import { describeTrigger } from "../../../../src/data/automation_i18n"; import { describeTrigger } from "../../../src/data/automation_i18n";
const triggers = [ const triggers = [
{ platform: "state" }, { platform: "state" },
@@ -29,7 +29,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,13 +1,12 @@
/* eslint-disable lit/no-template-arrow */
import { html, css, LitElement, TemplateResult } from "lit"; import { html, css, LitElement, TemplateResult } from "lit";
import { customElement, property } from "lit/decorators"; import { customElement, property } from "lit/decorators";
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 { 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 { mockDemoTrace } from "../../data/traces/mock-demo-trace"; import { mockDemoTrace } from "../data/traces/mock-demo-trace";
import { DemoTrace } from "../../data/traces/types"; import { DemoTrace } from "../data/traces/types";
const traces: DemoTrace[] = [ const traces: DemoTrace[] = [
mockDemoTrace({ state: "running" }), mockDemoTrace({ state: "running" }),

View File

@@ -1,14 +1,13 @@
/* eslint-disable lit/no-template-arrow */
import { html, css, LitElement, TemplateResult } from "lit"; import { html, css, LitElement, TemplateResult } from "lit";
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 { provideHass } from "../../../src/fake_data/provide_hass";
import { HomeAssistant } from "../../../src/types";
import { DemoTrace } from "../data/traces/types";
import { basicTrace } from "../data/traces/basic_trace";
import { motionLightTrace } from "../data/traces/motion-light-trace";
import { customElement, property, state } from "lit/decorators"; import { customElement, property, state } from "lit/decorators";
import { provideHass } from "../../../../src/fake_data/provide_hass";
import { HomeAssistant } from "../../../../src/types";
import { DemoTrace } from "../../data/traces/types";
import { basicTrace } from "../../data/traces/basic_trace";
import { motionLightTrace } from "../../data/traces/motion-light-trace";
const traces: DemoTrace[] = [basicTrace, motionLightTrace]; const traces: DemoTrace[] = [basicTrace, motionLightTrace];

View File

@@ -1,8 +1,8 @@
import { html, LitElement, PropertyValues, TemplateResult } from "lit"; import { html, LitElement, PropertyValues, TemplateResult } from "lit";
import { customElement, query } from "lit/decorators"; import { customElement, query } from "lit/decorators";
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";
const ENTITIES = [ const ENTITIES = [
getEntity("alarm_control_panel", "alarm", "disarmed", { getEntity("alarm_control_panel", "alarm", "disarmed", {
@@ -70,7 +70,7 @@ const CONFIGS = [
}, },
]; ];
@customElement("demo-lovelace-alarm-panel-card") @customElement("demo-hui-alarm-panel-card")
class DemoAlarmPanelEntity extends LitElement { class DemoAlarmPanelEntity extends LitElement {
@query("#demos") private _demoRoot!: HTMLElement; @query("#demos") private _demoRoot!: HTMLElement;
@@ -89,6 +89,6 @@ class DemoAlarmPanelEntity extends LitElement {
declare global { declare global {
interface HTMLElementTagNameMap { interface HTMLElementTagNameMap {
"demo-lovelace-alarm-panel-card": DemoAlarmPanelEntity; "demo-hui-alarm-panel-card": DemoAlarmPanelEntity;
} }
} }

View File

@@ -1,8 +1,8 @@
import { html, LitElement, PropertyValues, TemplateResult } from "lit"; import { html, LitElement, PropertyValues, TemplateResult } from "lit";
import { customElement, query } from "lit/decorators"; import { customElement, query } from "lit/decorators";
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";
const ENTITIES = [ const ENTITIES = [
getEntity("light", "controller_1", "on", { getEntity("light", "controller_1", "on", {
@@ -52,7 +52,7 @@ const CONFIGS = [
}, },
]; ];
@customElement("demo-lovelace-conditional-card") @customElement("demo-hui-conditional-card")
class DemoConditional extends LitElement { class DemoConditional extends LitElement {
@query("#demos") private _demoRoot!: HTMLElement; @query("#demos") private _demoRoot!: HTMLElement;
@@ -71,6 +71,6 @@ class DemoConditional extends LitElement {
declare global { declare global {
interface HTMLElementTagNameMap { interface HTMLElementTagNameMap {
"demo-lovelace-conditional-card": DemoConditional; "demo-hui-conditional-card": DemoConditional;
} }
} }

View File

@@ -1,8 +1,8 @@
import { html, LitElement, PropertyValues, TemplateResult } from "lit"; import { html, LitElement, PropertyValues, TemplateResult } from "lit";
import { customElement, query } from "lit/decorators"; import { customElement, query } from "lit/decorators";
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";
const ENTITIES = [ const ENTITIES = [
getEntity("light", "bed_light", "on", { getEntity("light", "bed_light", "on", {
@@ -11,10 +11,10 @@ const ENTITIES = [
getEntity("group", "kitchen", "on", { getEntity("group", "kitchen", "on", {
entity_id: ["light.bed_light"], entity_id: ["light.bed_light"],
order: 8, order: 8,
friendly_name: "Kitchen Group", friendly_name: "Kitchen",
}), }),
getEntity("lock", "kitchen_door", "locked", { getEntity("lock", "kitchen_door", "locked", {
friendly_name: "Kitchen Lock", friendly_name: "Kitchen Door",
}), }),
getEntity("cover", "kitchen_window", "open", { getEntity("cover", "kitchen_window", "open", {
friendly_name: "Kitchen Window", friendly_name: "Kitchen Window",
@@ -22,7 +22,7 @@ const ENTITIES = [
}), }),
getEntity("scene", "romantic_lights", "scening", { getEntity("scene", "romantic_lights", "scening", {
entity_id: ["light.bed_light", "light.ceiling_lights"], entity_id: ["light.bed_light", "light.ceiling_lights"],
friendly_name: "Romantic Scene", friendly_name: "Romantic lights",
}), }),
getEntity("device_tracker", "demo_paulus", "home", { getEntity("device_tracker", "demo_paulus", "home", {
source_type: "gps", source_type: "gps",
@@ -50,51 +50,15 @@ const ENTITIES = [
friendly_name: "Ecobee", friendly_name: "Ecobee",
supported_features: 1014, supported_features: 1014,
}), }),
getEntity("input_number", "number", 5, { getEntity("input_number", "noise_allowance", 5, {
min: 0, min: 0,
max: 10, max: 10,
step: 1, step: 1,
mode: "slider", mode: "slider",
unit_of_measurement: "dB", unit_of_measurement: "dB",
friendly_name: "Number", friendly_name: "Allowed Noise",
icon: "mdi:bell-ring", icon: "mdi:bell-ring",
}), }),
getEntity("input_boolean", "toggle", "on", {
friendly_name: "Toggle",
}),
getEntity("input_datetime", "date_and_time", "2022-01-10 00:00:00", {
has_date: true,
has_time: true,
editable: true,
year: 2022,
month: 1,
day: 10,
hour: 0,
minute: 0,
second: 0,
timestamp: 1641801600,
friendly_name: "Date and Time",
}),
getEntity("input_select", "dropdown", "Soda", {
friendly_name: "Dropdown",
options: ["Soda", "Beer", "Wine"],
}),
getEntity("input_text", "text", "Inspiration", {
friendly_name: "Text",
mode: "text",
}),
getEntity("timer", "timer", "idle", {
friendly_name: "Timer",
duration: "0:05:00",
}),
getEntity("counter", "counter", "3", {
friendly_name: "Counter",
initial: 0,
step: 1,
minimum: 0,
maximum: 10,
}),
getEntity("light", "unavailable", "unavailable", { getEntity("light", "unavailable", "unavailable", {
friendly_name: "Bed Light", friendly_name: "Bed Light",
}), }),
@@ -106,7 +70,7 @@ const ENTITIES = [
supported_features: 11, supported_features: 11,
}), }),
getEntity("scene", "unavailable", "unavailable", { getEntity("scene", "unavailable", "unavailable", {
friendly_name: "Romantic Scene", friendly_name: "Romantic lights",
}), }),
getEntity("device_tracker", "unavailable", "unavailable", { getEntity("device_tracker", "unavailable", "unavailable", {
friendly_name: "Paulus", friendly_name: "Paulus",
@@ -141,22 +105,7 @@ const CONFIGS = [
- light.bed_light - light.bed_light
- light.non_existing - light.non_existing
- climate.ecobee - climate.ecobee
- input_number.number - input_number.noise_allowance
`,
},
{
heading: "Helpers",
config: `
- type: entities
title: Helpers
entities:
- entity: input_boolean.toggle
- entity: input_datetime.date_and_time
- entity: input_number.number
- entity: input_select.dropdown
- entity: input_text.text
- entity: timer.timer
- entity: counter.counter
`, `,
}, },
{ {
@@ -171,7 +120,7 @@ const CONFIGS = [
- lock.kitchen_door - lock.kitchen_door
- light.bed_light - light.bed_light
- climate.ecobee - climate.ecobee
- input_number.number - input_number.noise_allowance
title: Random group title: Random group
`, `,
}, },
@@ -187,7 +136,7 @@ const CONFIGS = [
- lock.kitchen_door - lock.kitchen_door
- light.bed_light - light.bed_light
- climate.ecobee - climate.ecobee
- input_number.number - input_number.noise_allowance
title: Random group title: Random group
show_header_toggle: false show_header_toggle: false
`, `,
@@ -234,7 +183,7 @@ const CONFIGS = [
icon: mdi:alarm-light icon: mdi:alarm-light
name: Bed Light Custom Icon name: Bed Light Custom Icon
- climate.ecobee - climate.ecobee
- input_number.number - input_number.noise_allowance
title: Random group title: Random group
show_header_toggle: false show_header_toggle: false
`, `,
@@ -267,7 +216,7 @@ const CONFIGS = [
}, },
]; ];
@customElement("demo-lovelace-entities-card") @customElement("demo-hui-entities-card")
class DemoEntities extends LitElement { class DemoEntities extends LitElement {
@query("#demos") private _demoRoot!: HTMLElement; @query("#demos") private _demoRoot!: HTMLElement;
@@ -286,6 +235,6 @@ class DemoEntities extends LitElement {
declare global { declare global {
interface HTMLElementTagNameMap { interface HTMLElementTagNameMap {
"demo-lovelace-entities-card": DemoEntities; "demo-hui-entities-card": DemoEntities;
} }
} }

View File

@@ -1,8 +1,8 @@
import { html, LitElement, PropertyValues, TemplateResult } from "lit"; import { html, LitElement, PropertyValues, TemplateResult } from "lit";
import { customElement, query } from "lit/decorators"; import { customElement, query } from "lit/decorators";
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";
const ENTITIES = [ const ENTITIES = [
getEntity("light", "bed_light", "on", { getEntity("light", "bed_light", "on", {
@@ -68,7 +68,7 @@ const CONFIGS = [
}, },
]; ];
@customElement("demo-lovelace-entity-button-card") @customElement("demo-hui-entity-button-card")
class DemoButtonEntity extends LitElement { class DemoButtonEntity extends LitElement {
@query("#demos") private _demoRoot!: HTMLElement; @query("#demos") private _demoRoot!: HTMLElement;
@@ -87,6 +87,6 @@ class DemoButtonEntity extends LitElement {
declare global { declare global {
interface HTMLElementTagNameMap { interface HTMLElementTagNameMap {
"demo-lovelace-entity-button-card": DemoButtonEntity; "demo-hui-entity-button-card": DemoButtonEntity;
} }
} }

View File

@@ -1,8 +1,8 @@
import { html, LitElement, PropertyValues, TemplateResult } from "lit"; import { html, LitElement, PropertyValues, TemplateResult } from "lit";
import { customElement, query } from "lit/decorators"; import { customElement, query } from "lit/decorators";
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";
const ENTITIES = [ const ENTITIES = [
getEntity("device_tracker", "demo_paulus", "work", { getEntity("device_tracker", "demo_paulus", "work", {
@@ -109,7 +109,7 @@ const CONFIGS = [
}, },
]; ];
@customElement("demo-lovelace-entity-filter-card") @customElement("demo-hui-entity-filter-card")
class DemoEntityFilter extends LitElement { class DemoEntityFilter extends LitElement {
@query("#demos") private _demoRoot!: HTMLElement; @query("#demos") private _demoRoot!: HTMLElement;
@@ -128,6 +128,6 @@ class DemoEntityFilter extends LitElement {
declare global { declare global {
interface HTMLElementTagNameMap { interface HTMLElementTagNameMap {
"demo-lovelace-entity-filter-card": DemoEntityFilter; "demo-hui-entity-filter-card": DemoEntityFilter;
} }
} }

View File

@@ -1,8 +1,8 @@
import { html, LitElement, PropertyValues, TemplateResult } from "lit"; import { html, LitElement, PropertyValues, TemplateResult } from "lit";
import { customElement, query } from "lit/decorators"; import { customElement, query } from "lit/decorators";
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";
const ENTITIES = [ const ENTITIES = [
getEntity("sensor", "brightness", "12", {}), getEntity("sensor", "brightness", "12", {}),
@@ -106,7 +106,7 @@ const CONFIGS = [
}, },
]; ];
@customElement("demo-lovelace-gauge-card") @customElement("demo-hui-gauge-card")
class DemoGaugeEntity extends LitElement { class DemoGaugeEntity extends LitElement {
@query("#demos") private _demoRoot!: HTMLElement; @query("#demos") private _demoRoot!: HTMLElement;
@@ -125,6 +125,6 @@ class DemoGaugeEntity extends LitElement {
declare global { declare global {
interface HTMLElementTagNameMap { interface HTMLElementTagNameMap {
"demo-lovelace-gauge-card": DemoGaugeEntity; "demo-hui-gauge-card": DemoGaugeEntity;
} }
} }

View File

@@ -1,8 +1,8 @@
import { html, LitElement, PropertyValues, TemplateResult } from "lit"; import { html, LitElement, PropertyValues, TemplateResult } from "lit";
import { customElement, query } from "lit/decorators"; import { customElement, query } from "lit/decorators";
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";
const ENTITIES = [ const ENTITIES = [
getEntity("device_tracker", "demo_paulus", "home", { getEntity("device_tracker", "demo_paulus", "home", {
@@ -209,7 +209,7 @@ const CONFIGS = [
}, },
]; ];
@customElement("demo-lovelace-glance-card") @customElement("demo-hui-glance-card")
class DemoGlanceEntity extends LitElement { class DemoGlanceEntity extends LitElement {
@query("#demos") private _demoRoot!: HTMLElement; @query("#demos") private _demoRoot!: HTMLElement;
@@ -228,6 +228,6 @@ class DemoGlanceEntity extends LitElement {
declare global { declare global {
interface HTMLElementTagNameMap { interface HTMLElementTagNameMap {
"demo-lovelace-glance-card": DemoGlanceEntity; "demo-hui-glance-card": DemoGlanceEntity;
} }
} }

View File

@@ -1,9 +1,9 @@
import { html, LitElement, PropertyValues, TemplateResult } from "lit"; import { html, LitElement, PropertyValues, TemplateResult } from "lit";
import { customElement, query } from "lit/decorators"; import { customElement, query } from "lit/decorators";
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";
import "../../components/demo-cards"; import "../components/demo-cards";
const ENTITIES = [ const ENTITIES = [
getEntity("light", "kitchen_lights", "on", { getEntity("light", "kitchen_lights", "on", {
@@ -199,7 +199,7 @@ const CONFIGS = [
}, },
]; ];
@customElement("demo-lovelace-grid-and-stack-card") @customElement("demo-hui-grid-and-stack-card")
class DemoStack extends LitElement { class DemoStack extends LitElement {
@query("#demos") private _demoRoot!: HTMLElement; @query("#demos") private _demoRoot!: HTMLElement;
@@ -219,6 +219,6 @@ class DemoStack extends LitElement {
declare global { declare global {
interface HTMLElementTagNameMap { interface HTMLElementTagNameMap {
"demo-lovelace-grid-and-stack-card": DemoStack; "demo-hui-grid-and-stack-card": DemoStack;
} }
} }

View File

@@ -1,7 +1,6 @@
import { html, LitElement, PropertyValues, TemplateResult } from "lit"; import { html, LitElement, TemplateResult } from "lit";
import { customElement, query } from "lit/decorators"; import { customElement } from "lit/decorators";
import { provideHass } from "../../../../src/fake_data/provide_hass"; import "../components/demo-cards";
import "../../components/demo-cards";
const CONFIGS = [ const CONFIGS = [
{ {
@@ -37,22 +36,15 @@ const CONFIGS = [
}, },
]; ];
@customElement("demo-lovelace-iframe-card") @customElement("demo-hui-iframe-card")
class DemoIframe extends LitElement { class DemoIframe extends LitElement {
@query("demo-cards") private _demos!: HTMLElement;
protected render(): TemplateResult { protected render(): TemplateResult {
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`; return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
} }
protected firstUpdated(changedProperties: PropertyValues) {
super.firstUpdated(changedProperties);
provideHass(this._demos);
}
} }
declare global { declare global {
interface HTMLElementTagNameMap { interface HTMLElementTagNameMap {
"demo-lovelace-iframe-card": DemoIframe; "demo-hui-iframe-card": DemoIframe;
} }
} }

View File

@@ -1,8 +1,8 @@
import { html, LitElement, PropertyValues, TemplateResult } from "lit"; import { html, LitElement, PropertyValues, TemplateResult } from "lit";
import { customElement, query } from "lit/decorators"; import { customElement, query } from "lit/decorators";
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";
const ENTITIES = [ const ENTITIES = [
getEntity("light", "bed_light", "on", { getEntity("light", "bed_light", "on", {
@@ -62,7 +62,7 @@ const CONFIGS = [
}, },
]; ];
@customElement("demo-lovelace-light-card") @customElement("demo-hui-light-card")
class DemoLightEntity extends LitElement { class DemoLightEntity extends LitElement {
@query("#demos") private _demoRoot!: HTMLElement; @query("#demos") private _demoRoot!: HTMLElement;
@@ -81,6 +81,6 @@ class DemoLightEntity extends LitElement {
declare global { declare global {
interface HTMLElementTagNameMap { interface HTMLElementTagNameMap {
"demo-lovelace-light-card": DemoLightEntity; "demo-hui-light-card": DemoLightEntity;
} }
} }

View File

@@ -1,8 +1,8 @@
import { html, LitElement, PropertyValues, TemplateResult } from "lit"; import { html, LitElement, PropertyValues, TemplateResult } from "lit";
import { customElement, query } from "lit/decorators"; import { customElement, query } from "lit/decorators";
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";
const ENTITIES = [ const ENTITIES = [
getEntity("device_tracker", "demo_paulus", "not_home", { getEntity("device_tracker", "demo_paulus", "not_home", {
@@ -160,7 +160,7 @@ const CONFIGS = [
}, },
]; ];
@customElement("demo-lovelace-map-card") @customElement("demo-hui-map-card")
class DemoMap extends LitElement { class DemoMap extends LitElement {
@query("#demos") private _demoRoot!: HTMLElement; @query("#demos") private _demoRoot!: HTMLElement;
@@ -179,6 +179,6 @@ class DemoMap extends LitElement {
declare global { declare global {
interface HTMLElementTagNameMap { interface HTMLElementTagNameMap {
"demo-lovelace-map-card": DemoMap; "demo-hui-map-card": DemoMap;
} }
} }

View File

@@ -1,15 +1,15 @@
import { html, LitElement, PropertyValues, TemplateResult } from "lit"; import { html, LitElement, PropertyValues, TemplateResult } from "lit";
import { customElement, query } from "lit/decorators"; import { customElement, query } from "lit/decorators";
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";
const CONFIGS = [ const CONFIGS = [
{ {
heading: "markdown-it demo", heading: "markdown-it demo",
config: ` config: `
- type: markdown - type: markdown
content: >- content: >
# h1 Heading 8-) # h1 Heading 8-)
## h2 Heading ## h2 Heading
@@ -249,22 +249,11 @@ const CONFIGS = [
::: warning ::: warning
*here be dragons* *here be dragons*
::: :::
### ha-alert
You can use our [\`ha-alert\`](https://design.home-assistant.io/#components/ha-alert) component in markdown content rendered in the Home Assistant Frontend.
<ha-alert alert-type="error">This is an error alert check it out!</ha-alert>
<ha-alert alert-type="warning">This is a warning alert check it out!</ha-alert>
<ha-alert alert-type="info">This is an info alert check it out!</ha-alert>
<ha-alert alert-type="success">This is a success alert check it out!</ha-alert>
<ha-alert title="Test alert">This is an alert with a title</ha-alert>
`, `,
}, },
]; ];
@customElement("demo-lovelace-markdown-card") @customElement("demo-hui-markdown-card")
class DemoMarkdown extends LitElement { class DemoMarkdown extends LitElement {
@query("#demos") private _demoRoot!: HTMLElement; @query("#demos") private _demoRoot!: HTMLElement;
@@ -283,6 +272,6 @@ class DemoMarkdown extends LitElement {
declare global { declare global {
interface HTMLElementTagNameMap { interface HTMLElementTagNameMap {
"demo-lovelace-markdown-card": DemoMarkdown; "demo-hui-markdown-card": DemoMarkdown;
} }
} }

View File

@@ -1,8 +1,8 @@
import { html, LitElement, PropertyValues, TemplateResult } from "lit"; import { html, LitElement, PropertyValues, TemplateResult } from "lit";
import { customElement, query } from "lit/decorators"; import { customElement, query } from "lit/decorators";
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";
const CONFIGS = [ const CONFIGS = [
{ {
@@ -157,7 +157,7 @@ const CONFIGS = [
}, },
]; ];
@customElement("demo-lovelace-media-control-card") @customElement("demo-hui-media-control-card")
class DemoHuiMediaControlCard extends LitElement { class DemoHuiMediaControlCard extends LitElement {
@query("#demos") private _demoRoot!: HTMLElement; @query("#demos") private _demoRoot!: HTMLElement;
@@ -176,6 +176,6 @@ class DemoHuiMediaControlCard extends LitElement {
declare global { declare global {
interface HTMLElementTagNameMap { interface HTMLElementTagNameMap {
"demo-lovelace-media-control-card": DemoHuiMediaControlCard; "demo-hui-media-control-card": DemoHuiMediaControlCard;
} }
} }

View File

@@ -1,8 +1,8 @@
import { html, LitElement, PropertyValues, TemplateResult } from "lit"; import { html, LitElement, PropertyValues, TemplateResult } from "lit";
import { customElement, query } from "lit/decorators"; import { customElement, query } from "lit/decorators";
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";
const CONFIGS = [ const CONFIGS = [
{ {
@@ -54,7 +54,7 @@ const CONFIGS = [
}, },
]; ];
@customElement("demo-lovelace-media-player-row") @customElement("demo-hui-media-player-row")
class DemoHuiMediaPlayerRow extends LitElement { class DemoHuiMediaPlayerRow extends LitElement {
@query("#demos") private _demoRoot!: HTMLElement; @query("#demos") private _demoRoot!: HTMLElement;
@@ -73,6 +73,6 @@ class DemoHuiMediaPlayerRow extends LitElement {
declare global { declare global {
interface HTMLElementTagNameMap { interface HTMLElementTagNameMap {
"demo-lovelace-media-player-rows": DemoHuiMediaPlayerRow; "demo-hui-media-player-row": DemoHuiMediaPlayerRow;
} }
} }

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