Compare commits

..

3 Commits

Author SHA1 Message Date
Donnie
72bf0c918a Update src/dialogs/template-editor/ha-template-editor.ts
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2020-11-17 16:07:54 -08:00
Donnie
419f5d13bf Update src/dialogs/template-editor/ha-template-editor.ts
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2020-11-17 16:07:48 -08:00
Donnie
16549b3404 Add ability to launch small version of template editor in a dialog using shortcut 2020-11-17 11:41:29 -08:00
918 changed files with 33952 additions and 73498 deletions

View File

@@ -4,7 +4,7 @@
"dockerfile": "Dockerfile", "dockerfile": "Dockerfile",
"context": ".." "context": ".."
}, },
"appPort": "8124:8123", "appPort": 8123,
"context": "..", "context": "..",
"postCreateCommand": "script/bootstrap", "postCreateCommand": "script/bootstrap",
"extensions": [ "extensions": [
@@ -26,9 +26,6 @@
"[typescript]": { "[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode" "editor.defaultFormatter": "esbenp.prettier-vscode"
}, },
"[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"files.trimTrailingWhitespace": true "files.trimTrailingWhitespace": true
} }
} }

View File

@@ -84,8 +84,7 @@
"@typescript-eslint/no-unused-vars": 0, "@typescript-eslint/no-unused-vars": 0,
"@typescript-eslint/explicit-function-return-type": 0, "@typescript-eslint/explicit-function-return-type": 0,
"@typescript-eslint/explicit-module-boundary-types": 0, "@typescript-eslint/explicit-module-boundary-types": 0,
"@typescript-eslint/no-shadow": ["error"], "@typescript-eslint/no-shadow": ["error"]
"lit/attribute-value-entities": 0
}, },
"plugins": ["disable", "import", "lit", "prettier", "@typescript-eslint"], "plugins": ["disable", "import", "lit", "prettier", "@typescript-eslint"],
"processor": "disable/disable" "processor": "disable/disable"

View File

@@ -74,12 +74,12 @@ DO NOT DELETE ANY TEXT from this template! Otherwise, your issue may be closed w
``` ```
## Problem-relevant frontend configuration ## Problem-relevant configuration
<!-- <!--
An example configuration that caused the problem for you, e.g. the YAML configuration An example configuration that caused the problem for you. Fill this out even
of the used cards. Fill this out even if it seems unimportant to you. Please be sure if it seems unimportant to you. Please be sure to remove personal information
to remove personal information like passwords, private URLs and other credentials. like passwords, private URLs and other credentials.
--> -->
```yaml ```yaml
@@ -89,7 +89,7 @@ DO NOT DELETE ANY TEXT from this template! Otherwise, your issue may be closed w
## Javascript errors shown in your browser console/inspector ## Javascript errors shown in your browser console/inspector
<!-- <!--
If you come across any Javascript or other error logs, e.g. in your browser If you come across any javascript or other error logs, e.g., in your browser
console/inspector please provide them. console/inspector please provide them.
--> -->

View File

@@ -1,138 +0,0 @@
name: Report a bug with the UI, Frontend or Lovelace
about: Report an issue related to the Home Assistant frontend.
labels: bug
title: ""
issue_body: true
body:
- type: markdown
attributes:
value: |
Make sure you are running the [latest version of Home Assistant][releases] before reporting an issue.
If you have a feature or enhancement request for the frontend, please [start an discussion][fr] instead of creating an issue.
**Please not not report issues for custom Lovelace cards.**
[fr]: https://github.com/home-assistant/frontend/discussions
[releases]: https://github.com/home-assistant/home-assistant/releases
- type: checkboxes
attributes:
label: Checklist
description: Please verify that you've followed these steps
options:
- label: I have updated to the latest available Home Assistant version.
required: true
- label: I have cleared the cache of my browser.
required: true
- label: I have tried a different browser to see if it is related to my browser.
required: true
- type: markdown
attributes:
value: |
## The problem
- type: textarea
validations:
required: true
attributes:
label: Describe the issue you are experiencing
description: Provide a clear and concise description of what the bug is.
- type: textarea
validations:
required: true
attributes:
label: Describe the behavior you expected
description: Describe what you expected to happen or it should look/behave.
- type: textarea
validations:
required: true
attributes:
label: Steps to reproduce the issue
description: |
Please tell us exactly how to reproduce your issue.
Provide clear and concise step by step instructions and add code snippets if needed.
value: |
1.
2.
3.
...
- type: markdown
attributes:
value: |
## Environment
- type: input
validations:
required: true
attributes:
label: What version of Home Assistant Core has the issue?
placeholder: core-
description: >
Can be found in the Configuration panel -> Info.
- type: input
attributes:
label: What was the last working version of Home Assistant Core?
placeholder: core-
description: >
If known, otherwise leave blank.
- type: input
attributes:
label: In which browser are you experiencing the issue with?
placeholder: Google Chrome 88.0.4324.150
description: >
Provide the full name and don't forget to add the version!
- type: input
attributes:
label: Which operating system are you using to run this browser?
placeholder: macOS Big Sur (1.11)
description: >
Don't forget to add the version!
- type: markdown
attributes:
value: |
# Details
- type: textarea
attributes:
label: State of relevant entities
description: >
If your issue is about how an entity is shown in the UI, please add the
state and attributes for all situations. You can find this information
at Developer Tools -> States.
value: |
```yaml
# Paste your state here.
```
- type: textarea
attributes:
label: Problem-relevant frontend configuration
description: >
An example configuration that caused the problem for you, e.g., the YAML
configuration of the used cards. Fill this out even if it seems
unimportant to you. Please be sure to remove personal information like
passwords, private URLs and other credentials.
value: |
```yaml
# Paste your YAML here.
```
- type: textarea
attributes:
label: Javascript errors shown in your browser console/inspector
description: >
If you come across any Javascript or other error logs, e.g., in your
browser console/inspector please provide them.
value: |
```txt
# Paste your logs here.
```
- type: markdown
attributes:
value: |
## Additional information
- type: markdown
attributes:
value: |
If you have any additional information for us, use the field below.
Please note, you can attach screenshots or screen recordings here,
by dragging and dropping files in the field below.

View File

@@ -1,19 +0,0 @@
name: Netlify
on:
schedule:
- cron: "0 0 * * *"
jobs:
trigger_builds:
name: Trigger netlify build preview
runs-on: "ubuntu-latest"
steps:
- name: Trigger Cast build
run: curl -X POST -d {} https://api.netlify.com/build_hooks/${{ secrets.NETLIFY_CAST_DEV_BUILD_HOOK }}
- name: Trigger Demo build
run: curl -X POST -d {} https://api.netlify.com/build_hooks/${{ secrets.NETLIFY_DEMO_DEV_BUILD_HOOK }}
- name: Trigger Gallery build
run: curl -X POST -d {} https://api.netlify.com/build_hooks/${{ secrets.NETLIFY_GALLERY_DEV_BUILD_HOOK }}

View File

@@ -1,81 +0,0 @@
name: Release
on:
release:
types:
- published
env:
WHEELS_TAG: 3.7-alpine3.11
PYTHON_VERSION: 3.7
NODE_VERSION: 12.1
jobs:
release:
name: Release
runs-on: ubuntu-latest
steps:
- name: Checkout the repository
uses: actions/checkout@v2
- name: Verify version
uses: home-assistant/actions/helpers/verify-version@master
- name: Set up Python ${{ env.PYTHON_VERSION }}
uses: actions/setup-python@v2
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Set up Node ${{ env.NODE_VERSION }}
uses: actions/setup-node@v2
with:
node-version: ${{ env.NODE_VERSION }}
- name: Build and release package
run: |
python3 -m pip install twine
export TWINE_USERNAME="__token__"
export TWINE_PASSWORD="${{ secrets.TWINE_TOKEN }}"
script/release
wheels-init:
name: Init wheels build
needs: release
runs-on: ubuntu-latest
steps:
- name: Generate requirements.txt
run: |
# Sleep to give pypi time to populate the new version across mirrors
sleep 240
version=$(echo "${{ github.ref }}" | awk -F"/" '{print $NF}' )
echo "home-assistant-frontend==$version" > ./requirements.txt
- name: Upload requirements.txt
uses: actions/upload-artifact@v2
with:
name: requirements
path: ./requirements.txt
build-wheels:
name: Build wheels for ${{ matrix.arch }}
needs: wheels-init
runs-on: ubuntu-latest
strategy:
matrix:
arch: ["aarch64", "armhf", "armv7", "amd64", "i386"]
steps:
- name: Download requirements.txt
uses: actions/download-artifact@v2
with:
name: requirements
- name: Build wheels
uses: home-assistant/wheels@master
with:
tag: ${{ env.WHEELS_TAG }}
arch: ${{ matrix.arch }}
wheels-host: ${{ secrets.WHEELS_HOST }}
wheels-key: ${{ secrets.WHEELS_KEY }}
wheels-user: wheels
requirements: "requirements.txt"

View File

@@ -1,65 +0,0 @@
name: Translations
on:
schedule:
- cron: "30 0 * * *"
push:
branches:
- dev
paths:
- src/translations/en.json
env:
NODE_VERSION: 12
jobs:
upload:
name: Upload
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: Upload Translations
run: |
export LOKALISE_TOKEN="${{ secrets.LOKALISE_TOKEN }}"
./script/translations_upload_base
download:
name: Download
needs: upload
if: github.event_name == 'schedule'
runs-on: ubuntu-latest
steps:
- name: Checkout the repository
uses: actions/checkout@v2
- name: Set up Node ${{ env.NODE_VERSION }}
uses: actions/setup-node@v2
with:
node-version: ${{ env.NODE_VERSION }}
- name: Download Translations
run: |
export LOKALISE_TOKEN="${{ secrets.LOKALISE_TOKEN }}"
npm install
./script/translations_download
- name: Initialize git
uses: home-assistant/actions/helpers/git-init@master
with:
name: GitHub Action
email: github-action@users.noreply.github.com
- name: Update translation
run: |
git add translations
git commit -am "Translation update"
git push

6
.hound.yml Normal file
View File

@@ -0,0 +1,6 @@
jshint:
enabled: false
eslint:
enabled: true
config_file: .eslintrc-hound.json

View File

@@ -14,7 +14,7 @@ This is the repository for the official [Home Assistant](https://home-assistant.
- Development: [Instructions](https://developers.home-assistant.io/docs/frontend/development/) - Development: [Instructions](https://developers.home-assistant.io/docs/frontend/development/)
- Production build: `script/build_frontend` - Production build: `script/build_frontend`
- Gallery: `cd gallery && script/develop_gallery` - Gallery: `cd gallery && script/develop_gallery`
- Supervisor: [Instructions](https://developers.home-assistant.io/docs/supervisor/developing) - Hass.io: [Instructions](https://developers.home-assistant.io/docs/en/hassio_hass.html)
## Frontend development ## Frontend development

View File

@@ -0,0 +1,30 @@
# https://dev.azure.com/home-assistant
trigger: none
pr: none
schedules:
- cron: "0 0 * * *"
displayName: "build preview"
branches:
include:
- dev
always: true
variables:
- group: netlify
jobs:
- job: 'Netlify_preview'
pool:
vmImage: 'ubuntu-latest'
steps:
- script: |
# Cast
curl -X POST -d {} https://api.netlify.com/build_hooks/${NETLIFY_CAST}
# Demo
curl -X POST -d {} https://api.netlify.com/build_hooks/${NETLIFY_DEMO}
# Gallery
curl -X POST -d {} https://api.netlify.com/build_hooks/${NETLIFY_GALLERY}
displayName: 'Trigger netlify build preview'

View File

@@ -0,0 +1,59 @@
# https://dev.azure.com/home-assistant
trigger:
batch: true
tags:
include:
- "*"
pr: none
variables:
- name: versionWheels
value: '1.10.1-3.7-alpine3.11'
- name: versionNode
value: '12.1'
- group: twine
resources:
repositories:
- repository: azure
type: github
name: 'home-assistant/ci-azure'
endpoint: 'home-assistant'
stages:
- stage: "Validate"
jobs:
- template: templates/azp-job-version.yaml@azure
- stage: "Build"
jobs:
- job: "ReleasePython"
pool:
vmImage: "ubuntu-latest"
steps:
- task: UsePythonVersion@0
displayName: "Use Python 3.7"
inputs:
versionSpec: "3.7"
- task: NodeTool@0
displayName: "Use Node $(versionNode)"
inputs:
versionSpec: "$(versionNode)"
- script: pip install twine wheel
displayName: "Install tools"
- script: |
export TWINE_USERNAME="$(twineUser)"
export TWINE_PASSWORD="$(twinePassword)"
script/release
displayName: "Build and release package"
- stage: "Wheels"
jobs:
- template: templates/azp-job-wheels.yaml@azure
parameters:
builderVersion: '$(versionWheels)'
wheelsRequirement: 'requirement.txt'
preBuild:
- script: |
sleep 240
echo "home-assistant-frontend==$(Build.SourceBranchName)" > requirement.txt

View File

@@ -0,0 +1,70 @@
# https://dev.azure.com/home-assistant
trigger:
batch: true
branches:
include:
- dev
paths:
include:
- translations/en.json
pr: none
schedules:
- cron: "30 0 * * *"
displayName: "frontend translation update"
branches:
include:
- dev
always: true
variables:
- group: translation
resources:
repositories:
- repository: azure
type: github
name: 'home-assistant/ci-azure'
endpoint: 'home-assistant'
jobs:
- job: 'Upload'
pool:
vmImage: 'ubuntu-latest'
steps:
- task: NodeTool@0
displayName: 'Use Node 12.x'
inputs:
versionSpec: '12.x'
- script: |
export LOKALISE_TOKEN="$(lokaliseToken)"
export AZURE_BRANCH="$(Build.SourceBranchName)"
./script/translations_upload_base
displayName: 'Upload Translation'
- job: 'Download'
dependsOn:
- 'Upload'
condition: or(eq(variables['Build.Reason'], 'Schedule'), eq(variables['Build.Reason'], 'Manual'))
pool:
vmImage: 'ubuntu-latest'
steps:
- task: NodeTool@0
displayName: 'Use Node 12.x'
inputs:
versionSpec: '12.x'
- template: templates/azp-step-git-init.yaml@azure
- script: |
export LOKALISE_TOKEN="$(lokaliseToken)"
export AZURE_BRANCH="$(Build.SourceBranchName)"
npm install
./script/translations_download
displayName: 'Download Translation'
- script: |
git checkout dev
git add translation
git commit -am "[ci skip] Translation update"
git push
displayName: 'Update translation'

View File

@@ -1,39 +0,0 @@
# Bundling Home Assistant Frontend
The Home Assistant build pipeline contains various steps to prepare a build.
- Generating icon files to be included
- Generating translation files to be included
- Converting TypeScript, CSS and JSON files to JavaScript
- Bundling
- Minifying the files
- Generating the HTML entrypoint files
- Generating the service worker
- Compressing the files
## Converting files
Currently in Home Assistant we use a bundler to convert TypeScript, CSS and JSON files to JavaScript files that the browser understands.
We currently rely on Webpack but also have experimental Rollup support. Both of these programs bundle the converted files in both production and development.
For development, bundling is optional. We just want to get the right files in the browser.
Responsibilities of the converter during development:
- Convert TypeScript to JavaScript
- Convert CSS to JavaScript that sets the content as the default export
- Convert JSON to JavaScript that sets the content as the default export
- Make sure import, dynamic import and web worker references work
- Add extensions where missing
- Resolve absolute package imports
- Filter out specific imports/packages
- Replace constants with values
In production, the following responsibilities are added:
- Minify HTML
- Bundle multiple imports so that the browser can fetch less files
- Generate a second version that is ES5 compatible
Configuration for all these steps are specified in [bundle.js](bundle.js).

View File

@@ -44,7 +44,7 @@ module.exports.definedVars = ({ isProdBuild, latestBuild, defineOverlay }) => ({
}); });
module.exports.terserOptions = (latestBuild) => ({ module.exports.terserOptions = (latestBuild) => ({
safari10: !latestBuild, safari10: true,
ecma: latestBuild ? undefined : 5, ecma: latestBuild ? undefined : 5,
output: { comments: false }, output: { comments: false },
}); });
@@ -117,7 +117,7 @@ BundleConfig {
*/ */
module.exports.config = { module.exports.config = {
app({ isProdBuild, latestBuild, isStatsBuild, isWDS }) { app({ isProdBuild, latestBuild, isStatsBuild }) {
return { return {
entry: { entry: {
service_worker: "./src/entrypoints/service_worker.ts", service_worker: "./src/entrypoints/service_worker.ts",
@@ -132,7 +132,6 @@ module.exports.config = {
isProdBuild, isProdBuild,
latestBuild, latestBuild,
isStatsBuild, isStatsBuild,
isWDS,
}; };
}, },

View File

@@ -6,9 +6,6 @@ module.exports = {
useRollup() { useRollup() {
return process.env.ROLLUP === "1"; return process.env.ROLLUP === "1";
}, },
useWDS() {
return process.env.WDS === "1";
},
isProdBuild() { isProdBuild() {
return ( return (
process.env.NODE_ENV === "production" || module.exports.isStatsBuild() process.env.NODE_ENV === "production" || module.exports.isStatsBuild()

View File

@@ -12,7 +12,6 @@ require("./webpack.js");
require("./service-worker.js"); require("./service-worker.js");
require("./entry-html.js"); require("./entry-html.js");
require("./rollup.js"); require("./rollup.js");
require("./wds.js");
gulp.task( gulp.task(
"develop-app", "develop-app",
@@ -29,11 +28,7 @@ gulp.task(
"build-translations" "build-translations"
), ),
"copy-static-app", "copy-static-app",
env.useWDS() env.useRollup() ? "rollup-watch-app" : "webpack-watch-app"
? "wds-watch-app"
: env.useRollup()
? "rollup-watch-app"
: "webpack-watch-app"
) )
); );

View File

@@ -19,7 +19,6 @@ const renderTemplate = (pth, data = {}, pathFunc = templatePath) => {
return compiled({ return compiled({
...data, ...data,
useRollup: env.useRollup(), useRollup: env.useRollup(),
useWDS: env.useWDS(),
renderTemplate, renderTemplate,
}); });
}; };
@@ -91,23 +90,10 @@ gulp.task("gen-pages-prod", (done) => {
}); });
gulp.task("gen-index-app-dev", (done) => { gulp.task("gen-index-app-dev", (done) => {
let latestAppJS, latestCoreJS, latestCustomPanelJS;
if (env.useWDS()) {
latestAppJS = "http://localhost:8000/src/entrypoints/app.ts";
latestCoreJS = "http://localhost:8000/src/entrypoints/core.ts";
latestCustomPanelJS =
"http://localhost:8000/src/entrypoints/custom-panel.ts";
} else {
latestAppJS = "/frontend_latest/app.js";
latestCoreJS = "/frontend_latest/core.js";
latestCustomPanelJS = "/frontend_latest/custom-panel.js";
}
const content = renderTemplate("index", { const content = renderTemplate("index", {
latestAppJS, latestAppJS: "/frontend_latest/app.js",
latestCoreJS, latestCoreJS: "/frontend_latest/core.js",
latestCustomPanelJS, latestCustomPanelJS: "/frontend_latest/custom-panel.js",
es5AppJS: "/frontend_es5/app.js", es5AppJS: "/frontend_es5/app.js",
es5CoreJS: "/frontend_es5/core.js", es5CoreJS: "/frontend_es5/core.js",

View File

@@ -85,11 +85,6 @@ gulp.task("copy-translations-app", async () => {
copyTranslations(staticDir); copyTranslations(staticDir);
}); });
gulp.task("copy-translations-supervisor", async () => {
const staticDir = paths.hassio_output_static;
copyTranslations(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

View File

@@ -10,8 +10,6 @@ require("./gen-icons-json.js");
require("./webpack.js"); require("./webpack.js");
require("./compress.js"); require("./compress.js");
require("./rollup.js"); require("./rollup.js");
require("./gather-static.js");
require("./translations.js");
gulp.task( gulp.task(
"develop-hassio", "develop-hassio",
@@ -22,8 +20,6 @@ gulp.task(
"clean-hassio", "clean-hassio",
"gen-icons-json", "gen-icons-json",
"gen-index-hassio-dev", "gen-index-hassio-dev",
"build-supervisor-translations",
"copy-translations-supervisor",
env.useRollup() ? "rollup-watch-hassio" : "webpack-watch-hassio" env.useRollup() ? "rollup-watch-hassio" : "webpack-watch-hassio"
) )
); );
@@ -36,8 +32,6 @@ gulp.task(
}, },
"clean-hassio", "clean-hassio",
"gen-icons-json", "gen-icons-json",
"build-supervisor-translations",
"copy-translations-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

@@ -33,10 +33,21 @@ String.prototype.rsplit = function (sep, maxsplit) {
: split; : split;
}; };
// Panel translations which should be split from the core translations. // Panel translations which should be split from the core translations. These
const TRANSLATION_FRAGMENTS = Object.keys( // should mirror the fragment definitions in polymer.json, so that we load
require("../../src/translations/en.json").ui.panel // additional resources at equivalent points.
); const TRANSLATION_FRAGMENTS = [
"config",
"history",
"logbook",
"mailbox",
"profile",
"shopping-list",
"page-authorize",
"page-demo",
"page-onboarding",
"developer-tools",
];
function recursiveFlatten(prefix, data) { function recursiveFlatten(prefix, data) {
let output = {}; let output = {};
@@ -266,7 +277,6 @@ gulp.task(taskName, function () {
TRANSLATION_FRAGMENTS.forEach((fragment) => { TRANSLATION_FRAGMENTS.forEach((fragment) => {
delete data.ui.panel[fragment]; delete data.ui.panel[fragment];
}); });
delete data.supervisor;
return data; return data;
}) })
) )
@@ -343,62 +353,6 @@ gulp.task(
} }
); );
gulp.task("build-translation-fragment-supervisor", function () {
return gulp
.src(fullDir + "/*.json")
.pipe(transform((data) => data.supervisor))
.pipe(gulp.dest(workDir + "/supervisor"));
});
gulp.task("build-translation-flatten-supervisor", function () {
return gulp
.src(workDir + "/supervisor/*.json")
.pipe(
transform(function (data) {
// Polymer.AppLocalizeBehavior requires flattened json
return flatten(data);
})
)
.pipe(gulp.dest(outDir));
});
gulp.task("build-translation-write-metadata", function writeMetadata() {
return gulp
.src(
[
path.join(paths.translations_src, "translationMetadata.json"),
workDir + "/testMetadata.json",
workDir + "/translationFingerprints.json",
],
{ allowEmpty: true }
)
.pipe(merge({}))
.pipe(
transform(function (data) {
const newData = {};
Object.entries(data).forEach(([key, value]) => {
// Filter out translations without native name.
if (value.nativeName) {
newData[key] = value;
} else {
console.warn(
`Skipping language ${key}. Native name was not translated.`
);
}
});
return newData;
})
)
.pipe(
transform((data) => ({
fragments: TRANSLATION_FRAGMENTS,
translations: data,
}))
)
.pipe(rename("translationMetadata.json"))
.pipe(gulp.dest(workDir));
});
gulp.task( gulp.task(
"build-translations", "build-translations",
gulp.series( gulp.series(
@@ -410,20 +364,42 @@ gulp.task(
gulp.parallel(...splitTasks), gulp.parallel(...splitTasks),
"build-flattened-translations", "build-flattened-translations",
"build-translation-fingerprints", "build-translation-fingerprints",
"build-translation-write-metadata" function writeMetadata() {
) return gulp
); .src(
[
gulp.task( path.join(paths.translations_src, "translationMetadata.json"),
"build-supervisor-translations", workDir + "/testMetadata.json",
gulp.series( workDir + "/translationFingerprints.json",
"clean-translations", ],
"ensure-translations-build-dir", { allowEmpty: true }
"build-master-translation", )
"build-merged-translations", .pipe(merge({}))
"build-translation-fragment-supervisor", .pipe(
"build-translation-flatten-supervisor", transform(function (data) {
"build-translation-fingerprints", const newData = {};
"build-translation-write-metadata" Object.entries(data).forEach(([key, value]) => {
// Filter out translations without native name.
if (data[key].nativeName) {
newData[key] = data[key];
} else {
console.warn(
`Skipping language ${key}. Native name was not translated.`
);
}
if (data[key]) newData[key] = value;
});
return newData;
})
)
.pipe(
transform((data) => ({
fragments: TRANSLATION_FRAGMENTS,
translations: data,
}))
)
.pipe(rename("translationMetadata.json"))
.pipe(gulp.dest(workDir));
}
) )
); );

View File

@@ -1,11 +0,0 @@
// Tasks to run Rollup
const gulp = require("gulp");
const { startDevServer } = require("@web/dev-server");
gulp.task("wds-watch-app", () => {
startDevServer({
config: {
watch: true,
},
});
});

View File

@@ -47,7 +47,7 @@ const runDevServer = ({
); );
}); });
const doneHandler = (done) => (err, stats) => { const handler = (done) => (err, stats) => {
if (err) { if (err) {
log.error(err.stack || err); log.error(err.stack || err);
if (err.details) { if (err.details) {
@@ -67,20 +67,11 @@ const doneHandler = (done) => (err, stats) => {
} }
}; };
const prodBuild = (conf) =>
new Promise((resolve) => {
webpack(
conf,
// Resolve promise when done. Because we pass a callback, webpack closes itself
doneHandler(resolve)
);
});
gulp.task("webpack-watch-app", () => { gulp.task("webpack-watch-app", () => {
// This command will run forever because we don't close compiler // we are not calling done, so this command will run forever
webpack(createAppConfig({ isProdBuild: false, latestBuild: true })).watch( webpack(createAppConfig({ isProdBuild: false, latestBuild: true })).watch(
{ ignored: /build-translations/ }, { ignored: /build-translations/ },
doneHandler() handler()
); );
gulp.watch( gulp.watch(
path.join(paths.translations_src, "en.json"), path.join(paths.translations_src, "en.json"),
@@ -88,12 +79,15 @@ gulp.task("webpack-watch-app", () => {
); );
}); });
gulp.task("webpack-prod-app", () => gulp.task(
prodBuild( "webpack-prod-app",
bothBuilds(createAppConfig, { () =>
isProdBuild: true, new Promise((resolve) =>
}) webpack(
) bothBuilds(createAppConfig, { isProdBuild: true }),
handler(resolve)
)
)
); );
gulp.task("webpack-dev-server-demo", () => { gulp.task("webpack-dev-server-demo", () => {
@@ -104,12 +98,17 @@ gulp.task("webpack-dev-server-demo", () => {
}); });
}); });
gulp.task("webpack-prod-demo", () => gulp.task(
prodBuild( "webpack-prod-demo",
bothBuilds(createDemoConfig, { () =>
isProdBuild: true, new Promise((resolve) =>
}) webpack(
) bothBuilds(createDemoConfig, {
isProdBuild: true,
}),
handler(resolve)
)
)
); );
gulp.task("webpack-dev-server-cast", () => { gulp.task("webpack-dev-server-cast", () => {
@@ -122,35 +121,41 @@ gulp.task("webpack-dev-server-cast", () => {
}); });
}); });
gulp.task("webpack-prod-cast", () => gulp.task(
prodBuild( "webpack-prod-cast",
bothBuilds(createCastConfig, { () =>
isProdBuild: true, new Promise((resolve) =>
}) webpack(
) bothBuilds(createCastConfig, {
isProdBuild: true,
}),
handler(resolve)
)
)
); );
gulp.task("webpack-watch-hassio", () => { gulp.task("webpack-watch-hassio", () => {
// This command will run forever because we don't close compiler // we are not calling done, so this command will run forever
webpack( webpack(
createHassioConfig({ createHassioConfig({
isProdBuild: false, isProdBuild: false,
latestBuild: true, latestBuild: true,
}) })
).watch({ ignored: /build-translations/ }, doneHandler()); ).watch({}, handler());
gulp.watch(
path.join(paths.translations_src, "en.json"),
gulp.series("build-supervisor-translations", "copy-translations-supervisor")
);
}); });
gulp.task("webpack-prod-hassio", () => gulp.task(
prodBuild( "webpack-prod-hassio",
bothBuilds(createHassioConfig, { () =>
isProdBuild: true, new Promise((resolve) =>
}) webpack(
) bothBuilds(createHassioConfig, {
isProdBuild: true,
}),
handler(resolve)
)
)
); );
gulp.task("webpack-dev-server-gallery", () => { gulp.task("webpack-dev-server-gallery", () => {
@@ -162,11 +167,17 @@ gulp.task("webpack-dev-server-gallery", () => {
}); });
}); });
gulp.task("webpack-prod-gallery", () => gulp.task(
prodBuild( "webpack-prod-gallery",
createGalleryConfig({ () =>
isProdBuild: true, new Promise((resolve) =>
latestBuild: true, webpack(
}) createGalleryConfig({
) isProdBuild: true,
latestBuild: true,
}),
handler(resolve)
)
)
); );

View File

@@ -34,7 +34,6 @@ module.exports = {
hassio_dir: path.resolve(__dirname, "../hassio"), hassio_dir: path.resolve(__dirname, "../hassio"),
hassio_output_root: path.resolve(__dirname, "../hassio/build"), hassio_output_root: path.resolve(__dirname, "../hassio/build"),
hassio_output_static: path.resolve(__dirname, "../hassio/build/static"),
hassio_output_latest: path.resolve( hassio_output_latest: path.resolve(
__dirname, __dirname,
"../hassio/build/frontend_latest" "../hassio/build/frontend_latest"

View File

@@ -1,3 +1,5 @@
const path = require("path");
module.exports = function (userOptions = {}) { module.exports = function (userOptions = {}) {
// Files need to be absolute paths. // Files need to be absolute paths.
// This only works if the file has no exports // This only works if the file has no exports

View File

@@ -3,7 +3,7 @@ const path = require("path");
const commonjs = require("@rollup/plugin-commonjs"); const commonjs = require("@rollup/plugin-commonjs");
const resolve = require("@rollup/plugin-node-resolve"); const resolve = require("@rollup/plugin-node-resolve");
const json = require("@rollup/plugin-json"); const json = require("@rollup/plugin-json");
const babel = require("@rollup/plugin-babel").babel; const babel = require("rollup-plugin-babel");
const replace = require("@rollup/plugin-replace"); const replace = require("@rollup/plugin-replace");
const visualizer = require("rollup-plugin-visualizer"); const visualizer = require("rollup-plugin-visualizer");
const { string } = require("rollup-plugin-string"); const { string } = require("rollup-plugin-string");
@@ -31,7 +31,6 @@ const createRollupConfig = ({
isStatsBuild, isStatsBuild,
publicPath, publicPath,
dontHash, dontHash,
isWDS,
}) => { }) => {
return { return {
/** /**
@@ -62,7 +61,6 @@ const createRollupConfig = ({
...bundle.babelOptions({ latestBuild }), ...bundle.babelOptions({ latestBuild }),
extensions, extensions,
exclude: bundle.babelExclude(), exclude: bundle.babelExclude(),
babelHelpers: isWDS ? "inline" : "bundled",
}), }),
string({ string({
// Import certain extensions as strings // Import certain extensions as strings
@@ -71,21 +69,19 @@ const createRollupConfig = ({
replace( replace(
bundle.definedVars({ isProdBuild, latestBuild, defineOverlay }) bundle.definedVars({ isProdBuild, latestBuild, defineOverlay })
), ),
!isWDS && manifest({
manifest({ publicPath,
publicPath, }),
}), worker(),
!isWDS && worker(), dontHashPlugin({ dontHash }),
!isWDS && dontHashPlugin({ dontHash }), isProdBuild && terser(bundle.terserOptions(latestBuild)),
!isWDS && isProdBuild && terser(bundle.terserOptions(latestBuild)), isStatsBuild &&
!isWDS &&
isStatsBuild &&
visualizer({ visualizer({
// https://github.com/btd/rollup-plugin-visualizer#options // https://github.com/btd/rollup-plugin-visualizer#options
open: true, open: true,
sourcemap: true, sourcemap: true,
}), }),
].filter(Boolean), ],
}, },
/** /**
* @type { import("rollup").OutputOptions } * @type { import("rollup").OutputOptions }
@@ -112,13 +108,12 @@ const createRollupConfig = ({
}; };
}; };
const createAppConfig = ({ isProdBuild, latestBuild, isStatsBuild, isWDS }) => { const createAppConfig = ({ isProdBuild, latestBuild, isStatsBuild }) => {
return createRollupConfig( return createRollupConfig(
bundle.config.app({ bundle.config.app({
isProdBuild, isProdBuild,
latestBuild, latestBuild,
isStatsBuild, isStatsBuild,
isWDS,
}) })
); );
}; };

View File

@@ -1,7 +1,7 @@
const webpack = require("webpack"); const webpack = require("webpack");
const path = require("path"); const path = require("path");
const TerserPlugin = require("terser-webpack-plugin"); const TerserPlugin = require("terser-webpack-plugin");
const { WebpackManifestPlugin } = require("webpack-manifest-plugin"); const ManifestPlugin = require("webpack-manifest-plugin");
const paths = require("./paths.js"); const paths = require("./paths.js");
const bundle = require("./bundle"); const bundle = require("./bundle");
const log = require("fancy-log"); const log = require("fancy-log");
@@ -36,7 +36,6 @@ const createWebpackConfig = ({
const ignorePackages = bundle.ignorePackages({ latestBuild }); const ignorePackages = bundle.ignorePackages({ latestBuild });
return { return {
mode: isProdBuild ? "production" : "development", mode: isProdBuild ? "production" : "development",
target: ["web", latestBuild ? "es2017" : "es5"],
devtool: isProdBuild devtool: isProdBuild
? "cheap-module-source-map" ? "cheap-module-source-map"
: "eval-cheap-module-source-map", : "eval-cheap-module-source-map",
@@ -68,7 +67,7 @@ const createWebpackConfig = ({
], ],
}, },
plugins: [ plugins: [
new WebpackManifestPlugin({ new ManifestPlugin({
// 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"),
}), }),
@@ -132,6 +131,22 @@ const createWebpackConfig = ({
} }
return `${chunk.name}.${chunk.hash.substr(0, 8)}.js`; return `${chunk.name}.${chunk.hash.substr(0, 8)}.js`;
}, },
environment: {
// The environment supports arrow functions ('() => { ... }').
arrowFunction: latestBuild,
// The environment supports BigInt as literal (123n).
bigIntLiteral: false,
// The environment supports const and let for variable declarations.
const: latestBuild,
// The environment supports destructuring ('{ a, b } = obj').
destructuring: latestBuild,
// The environment supports an async import() function to import EcmaScript modules.
dynamicImport: latestBuild,
// The environment supports 'for of' iteration ('for (const x of array) { ... }').
forOf: latestBuild,
// The environment supports ECMAScript Module syntax to import ECMAScript modules (import ... from '...').
module: latestBuild,
},
chunkFilename: chunkFilename:
isProdBuild && !isStatsBuild isProdBuild && !isStatsBuild
? "chunk.[chunkhash].js" ? "chunk.[chunkhash].js"

View File

@@ -48,7 +48,7 @@ class HcCast extends LitElement {
protected render(): TemplateResult { protected render(): TemplateResult {
if (this.lovelaceConfig === undefined) { if (this.lovelaceConfig === undefined) {
return html`<hass-loading-screen no-toolbar></hass-loading-screen>`; return html` <hass-loading-screen no-toolbar></hass-loading-screen>> `;
} }
const error = const error =

View File

@@ -98,12 +98,8 @@ class HcLayout extends LitElement {
line-height: 32px; line-height: 32px;
padding: 24px 16px 16px; padding: 24px 16px 16px;
display: block; display: block;
margin: 0;
} }
.hero {
border-radius: 4px 4px 0 0;
}
.subtitle { .subtitle {
font-size: 14px; font-size: 14px;
color: var(--secondary-text-color); color: var(--secondary-text-color);

View File

@@ -1,8 +1,8 @@
import { import {
customElement, customElement,
html, html,
internalProperty,
property, property,
internalProperty,
TemplateResult, TemplateResult,
} from "lit-element"; } from "lit-element";
import { mockHistory } from "../../../../demo/src/stubs/history"; import { mockHistory } from "../../../../demo/src/stubs/history";

View File

@@ -39,7 +39,7 @@ class HcLovelace extends LitElement {
urlPath: this.urlPath!, urlPath: this.urlPath!,
enableFullEditMode: () => undefined, enableFullEditMode: () => undefined,
mode: "storage", mode: "storage",
locale: this.hass.locale, language: "en",
saveConfig: async () => undefined, saveConfig: async () => undefined,
deleteConfig: async () => undefined, deleteConfig: async () => undefined,
setEditMode: () => undefined, setEditMode: () => undefined,
@@ -94,7 +94,6 @@ class HcLovelace extends LitElement {
return css` return css`
:host { :host {
min-height: 100vh; min-height: 100vh;
height: 0;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
box-sizing: border-box; box-sizing: border-box;

View File

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

View File

@@ -54,8 +54,6 @@ export const demoEntitiesArsaboo: DemoConfig["entities"] = (localize) =>
state: "21", state: "21",
attributes: { attributes: {
friendly_name: "Living room temperature", friendly_name: "Living room temperature",
device_class: "temperature",
unit_of_measurement: "°C",
}, },
}, },
"sensor.study_temp_rounded": { "sensor.study_temp_rounded": {
@@ -63,8 +61,6 @@ export const demoEntitiesArsaboo: DemoConfig["entities"] = (localize) =>
state: "23", state: "23",
attributes: { attributes: {
friendly_name: "Study temperature", friendly_name: "Study temperature",
device_class: "temperature",
unit_of_measurement: "°C",
}, },
}, },
"sensor.living_room": { "sensor.living_room": {
@@ -265,7 +261,7 @@ export const demoEntitiesArsaboo: DemoConfig["entities"] = (localize) =>
entity_id: "light.kitchen_lights", entity_id: "light.kitchen_lights",
state: "off", state: "off",
attributes: { attributes: {
friendly_name: "Kitchen Lights", friendly_name: "Kitchen lights",
supported_features: 1, supported_features: 1,
}, },
}, },
@@ -488,7 +484,7 @@ export const demoEntitiesArsaboo: DemoConfig["entities"] = (localize) =>
attributes: { attributes: {
min_mireds: 111, min_mireds: 111,
max_mireds: 400, max_mireds: 400,
friendly_name: "Garage Lights", friendly_name: "Garage lights",
supported_features: 55, supported_features: 55,
}, },
}, },

View File

@@ -12,7 +12,6 @@ export const demoLovelaceArsaboo: DemoConfig["lovelace"] = (localize) => ({
{ {
type: "entities", type: "entities",
title: localize("ui.panel.page-demo.config.arsaboo.labels.lights"), title: localize("ui.panel.page-demo.config.arsaboo.labels.lights"),
state_color: true,
entities: [ entities: [
{ {
entity: "light.kitchen_lights", entity: "light.kitchen_lights",

View File

@@ -3,10 +3,22 @@ import { Lovelace } from "../../../src/panels/lovelace/types";
import { DemoConfig } from "./types"; import { DemoConfig } from "./types";
export const demoConfigs: Array<() => Promise<DemoConfig>> = [ export const demoConfigs: Array<() => Promise<DemoConfig>> = [
() => import("./arsaboo").then((mod) => mod.demoArsaboo), () =>
() => import("./teachingbirds").then((mod) => mod.demoTeachingbirds), import(/* webpackChunkName: "arsaboo" */ "./arsaboo").then(
() => import("./kernehed").then((mod) => mod.demoKernehed), (mod) => mod.demoArsaboo
() => import("./jimpower").then((mod) => mod.demoJimpower), ),
() =>
import(/* webpackChunkName: "teachingbirds" */ "./teachingbirds").then(
(mod) => mod.demoTeachingbirds
),
() =>
import(/* webpackChunkName: "kernehed" */ "./kernehed").then(
(mod) => mod.demoKernehed
),
() =>
import(/* webpackChunkName: "jimpower" */ "./jimpower").then(
(mod) => mod.demoJimpower
),
]; ];
// eslint-disable-next-line import/no-mutable-exports // eslint-disable-next-line import/no-mutable-exports

View File

@@ -653,7 +653,7 @@ export const demoEntitiesJimpower: DemoConfig["entities"] = () =>
entity_id: "binary_sensor.smoke_sensor_158d0001b8ddc7", entity_id: "binary_sensor.smoke_sensor_158d0001b8ddc7",
state: "off", state: "off",
attributes: { attributes: {
density: 0, Density: 0,
battery_level: 59, battery_level: 59,
friendly_name: "Downstairs Smoke Detector", friendly_name: "Downstairs Smoke Detector",
device_class: "smoke", device_class: "smoke",
@@ -663,7 +663,7 @@ export const demoEntitiesJimpower: DemoConfig["entities"] = () =>
entity_id: "binary_sensor.smoke_sensor_158d0001b8deba", entity_id: "binary_sensor.smoke_sensor_158d0001b8deba",
state: "off", state: "off",
attributes: { attributes: {
density: 0, Density: 0,
battery_level: 65, battery_level: 65,
friendly_name: "Upstairs Smoke Detector", friendly_name: "Upstairs Smoke Detector",
device_class: "smoke", device_class: "smoke",

View File

@@ -3,7 +3,49 @@ import { DemoConfig } from "../types";
export const demoLovelaceJimpower: DemoConfig["lovelace"] = () => ({ export const demoLovelaceJimpower: DemoConfig["lovelace"] = () => ({
name: "Kingia Castle", name: "Kingia Castle",
resources: [], resources: [
// {
// url: "/local/custom_ui/dark-sky-weather-card.js?v=4",
// type: "js",
// },
// {
// url: "/local/custom_ui/mini-media-player-bundle.js?v=0.9.8",
// type: "module",
// },
// {
// url: "/local/custom_ui/tracker-card.js?v=0.1.5",
// type: "js",
// },
// {
// url: "/local/custom_ui/surveillance-card.js?v=0.0.1",
// type: "module",
// },
// {
// url: "/local/custom_ui/mini-graph-card-bundle.js?v=0.1.0",
// type: "module",
// },
// {
// url: "/local/custom_ui/slider-entity-row.js?v=d6da75",
// type: "js",
// },
// {
// url:
// "/local/custom_ui/compact-custom-header/compact-custom-header.js?v=0.2.7",
// type: "js",
// },
// {
// url: "/local/custom_ui/waze-card.js?v=1.1.1",
// type: "js",
// },
// {
// url: "/local/custom_ui/circle-sensor-card.js?v=1.2.0",
// type: "module",
// },
// {
// url: "/local/custom_ui/monster-card.js?v=0.2.3",
// type: "js",
// },
],
views: [ views: [
{ {
cards: [ cards: [
@@ -561,6 +603,89 @@ export const demoLovelaceJimpower: DemoConfig["lovelace"] = () => ({
}, },
{ {
cards: [ cards: [
// {
// style: {
// "background-image": 'url("/assets/jimpower/cardbackK.png")',
// "background-size": "100% 400px",
// "box-shadow": "3px 3px rgba(0,0,0,0.4)",
// "background-repeat": "no-repeat",
// color: "#999999",
// "border-radius": "20px",
// border: "solid 1px rgba(100,100,100,0.3)",
// "background-color": "rgba(50,50,50,0.3)",
// },
// type: "custom:card-modder",
// card: {
// entity_visibility: "sensor.dark_sky_visibility",
// entity_sun: "sun.sun",
// entity_daily_summary:
// "sensor.bom_gc_forecast_detailed_summary_0",
// entity_temperature: "sensor.bom_temp",
// entity_forecast_high_temp_3:
// "sensor.bom_gc_forecast_max_temp_c_3",
// entity_forecast_high_temp_2:
// "sensor.bom_gc_forecast_max_temp_c_2",
// entity_forecast_high_temp_5:
// "sensor.bom_gc_forecast_max_temp_c_5",
// entity_forecast_high_temp_4:
// "sensor.bom_gc_forecast_max_temp_c_4",
// entity_wind_speed: "sensor.bom_wind_sp",
// entity_forecast_icon_4: "sensor.dark_sky_icon_4",
// entity_forecast_icon_5: "sensor.dark_sky_icon_5",
// entity_forecast_icon_2: "sensor.dark_sky_icon_2",
// entity_forecast_icon_3: "sensor.dark_sky_icon_3",
// entity_forecast_icon_1: "sensor.dark_sky_icon_1",
// entity_forecast_high_temp_1:
// "sensor.bom_gc_forecast_max_temp_c_1",
// entity_wind_bearing: "sensor.bom_wind_bear",
// entity_forecast_low_temp_2:
// "sensor.bom_gc_forecast_min_temp_c_2",
// entity_forecast_low_temp_3:
// "sensor.bom_gc_forecast_min_temp_c_3",
// entity_pressure: "sensor.bom_pres",
// entity_forecast_low_temp_1:
// "sensor.bom_gc_forecast_min_temp_c_1",
// entity_forecast_low_temp_4:
// "sensor.bom_gc_forecast_min_temp_c_4",
// entity_forecast_low_temp_5:
// "sensor.bom_gc_forecast_min_temp_c_5",
// entity_humidity: "sensor.bom_humd",
// type: "custom:dark-sky-weather-card",
// entity_current_conditions: "sensor.dark_sky_icon",
// },
// },
// {
// style: {
// "background-image": 'url("/assets/jimpower/home/waze_5.png")',
// "background-size": "100% 400px",
// "box-shadow": "3px 3px rgba(0,0,0,0.4)",
// "background-repeat": "no-repeat",
// "border-radius": "20px",
// border: "solid 1px rgba(100,100,100,0.3)",
// "background-color": "rgba(50,50,50,0.3)",
// },
// type: "custom:card-modder",
// card: {
// entities: [
// {
// name: "James",
// zone: "zone.home",
// entity: "sensor.james_to_home",
// },
// {
// name: "Tina",
// zone: "zone.home",
// entity: "sensor.tina_to_home",
// },
// {
// name: "Work",
// zone: "zone.powertec",
// entity: "sensor.commute_to_work",
// },
// ],
// type: "custom:waze-card",
// },
// },
{ {
style: { style: {
"border-radius": "20px", "border-radius": "20px",
@@ -597,8 +722,46 @@ export const demoLovelaceJimpower: DemoConfig["lovelace"] = () => ({
], ],
type: "vertical-stack", type: "vertical-stack",
}, },
// {
// cards: [
// {
// style: {
// "border-radius": "20px",
// color: "#999999",
// "box-shadow": "3px 3px rgba(0,0,0,0.4)",
// border: "solid 1px rgba(100,100,100,0.3)",
// },
// type: "custom:card-modder",
// card: {
// type: "picture-entity",
// entity: "camera.bom_radar",
// },
// },
// // {
// // style: {
// // "background-image": 'url("/assets/jimpower/cardbackK.png")',
// // "background-size": "100% 525px",
// // "box-shadow": "3px 3px rgba(0,0,0,0.4)",
// // "background-repeat": "no-repeat",
// // color: "#999999",
// // "border-radius": "20px",
// // border: "solid 1px rgba(100,100,100,0.3)",
// // "background-color": "rgba(50,50,50,0.3)",
// // },
// // type: "custom:card-modder",
// // card: {
// // title: null,
// // type: "custom:tracker-card",
// // trackers: [
// // "sensor.custom_card_tracker",
// // "sensor.custom_component_tracker",
// // ],
// // },
// // },
// ],
// type: "vertical-stack",
// },
], ],
path: "home",
icon: "mdi:castle", icon: "mdi:castle",
name: "Home", name: "Home",
background: background:
@@ -718,13 +881,26 @@ export const demoLovelaceJimpower: DemoConfig["lovelace"] = () => ({
card: { card: {
image: "/assets/jimpower/security/air_8.jpg", image: "/assets/jimpower/security/air_8.jpg",
elements: [ elements: [
{
image:
"https://www.airvisual.com/assets/aqi/ic-face-1-green.svg",
type: "image",
style: {
width: "80px",
top: "30%",
left: "12%",
transform: "none",
height: "80px",
},
entity: "sensor.us_air_pollution_level_2",
},
{ {
style: { style: {
color: "hsl(120, 41%, 39%)", color: "hsl(120, 41%, 39%)",
top: "50%", top: "50%",
"font-weight": 600, "font-weight": 600,
"font-size": "50px", "font-size": "20px",
left: "30%", left: "44%",
}, },
type: "state-label", type: "state-label",
entity: "sensor.us_air_pollution_level_2", entity: "sensor.us_air_pollution_level_2",
@@ -744,7 +920,7 @@ export const demoLovelaceJimpower: DemoConfig["lovelace"] = () => ({
style: { style: {
color: "white", color: "white",
top: "80%", top: "80%",
left: "48%", left: "52%",
}, },
type: "state-icon", type: "state-icon",
entity: "sensor.us_main_pollutant_2", entity: "sensor.us_main_pollutant_2",
@@ -1235,7 +1411,6 @@ export const demoLovelaceJimpower: DemoConfig["lovelace"] = () => ({
type: "vertical-stack", type: "vertical-stack",
}, },
], ],
path: "security",
icon: "hass:shield-home", icon: "hass:shield-home",
name: "Security", name: "Security",
background: background:

View File

@@ -101,12 +101,7 @@ export const demoEntitiesKernehed: DemoConfig["entities"] = () =>
"sensor.zwave_battery_front_door": { "sensor.zwave_battery_front_door": {
entity_id: "sensor.zwave_battery_front_door", entity_id: "sensor.zwave_battery_front_door",
state: "63", state: "63",
attributes: { attributes: { friendly_name: "Battery", icon: "mdi:battery-60" },
friendly_name: "Battery",
icon: "mdi:battery-60",
unit_of_measurement: "%",
device_class: "battery",
},
}, },
"sensor.oskar_devices": { "sensor.oskar_devices": {
entity_id: "sensor.oskar_devices", entity_id: "sensor.oskar_devices",
@@ -169,7 +164,7 @@ export const demoEntitiesKernehed: DemoConfig["entities"] = () =>
}, },
"input_select.christmas_pattern": { "input_select.christmas_pattern": {
entity_id: "input_select.christmas_pattern", entity_id: "input_select.christmas_pattern",
state: "Rainbow", state: "None",
attributes: { attributes: {
options: [ options: [
"None", "None",
@@ -191,7 +186,7 @@ export const demoEntitiesKernehed: DemoConfig["entities"] = () =>
}, },
"input_select.christmas_palette": { "input_select.christmas_palette": {
entity_id: "input_select.christmas_palette", entity_id: "input_select.christmas_palette",
state: "Party", state: "None",
attributes: { attributes: {
options: [ options: [
"None", "None",
@@ -462,7 +457,7 @@ export const demoEntitiesKernehed: DemoConfig["entities"] = () =>
state: "0.0", state: "0.0",
attributes: { attributes: {
unit_of_measurement: "kB/s", unit_of_measurement: "kB/s",
friendly_name: "Downloading", friendly_name: "Nedladdning",
icon: "mdi:file-download", icon: "mdi:file-download",
}, },
}, },
@@ -476,7 +471,7 @@ export const demoEntitiesKernehed: DemoConfig["entities"] = () =>
state: "0.0", state: "0.0",
attributes: { attributes: {
unit_of_measurement: "kB/s", unit_of_measurement: "kB/s",
friendly_name: "Uploading", friendly_name: "Uppladdning",
icon: "mdi:file-upload", icon: "mdi:file-upload",
}, },
}, },

View File

@@ -2,7 +2,44 @@ import { DemoConfig } from "../types";
export const demoLovelaceKernehed: DemoConfig["lovelace"] = () => ({ export const demoLovelaceKernehed: DemoConfig["lovelace"] = () => ({
name: "Hem", name: "Hem",
resources: [], resources: [
// {
// url: "/local/custom-lovelace/monster-card.js",
// type: "js",
// },
// {
// url: "/local/custom-lovelace/mini-media-player-bundle.js?v=0.9.8",
// type: "module",
// },
// {
// url: "/local/custom-lovelace/slideshow-card.js?=1.1.0",
// type: "js",
// },
// {
// url: "/local/custom-lovelace/fold-entity-row.js?v=3ae2c4",
// type: "js",
// },
// {
// url: "/local/custom-lovelace/swipe-card/swipe-card.js?v=2.0.0",
// type: "module",
// },
// {
// url: "/local/custom-lovelace/upcoming-media-card/upcoming-media-card.js",
// type: "js",
// },
// {
// url: "/local/custom-lovelace/tracker-card.js?v=0.1.5",
// type: "js",
// },
// {
// url: "/local/custom-lovelace/card-tools.js?v=6ce5d0",
// type: "js",
// },
// {
// url: "/local/custom-lovelace/krisinfo.js?=0.0.1",
// type: "js",
// },
],
views: [ views: [
{ {
cards: [ cards: [
@@ -27,7 +64,7 @@ export const demoLovelaceKernehed: DemoConfig["lovelace"] = () => ({
style: { style: {
color: "white", color: "white",
top: "93%", top: "93%",
left: "85%", left: "90%",
}, },
type: "state-label", type: "state-label",
entity: "sensor.battery_oskar", entity: "sensor.battery_oskar",
@@ -50,7 +87,7 @@ export const demoLovelaceKernehed: DemoConfig["lovelace"] = () => ({
{ {
style: { style: {
color: "white", color: "white",
top: "93%", top: "92%",
left: "20%", left: "20%",
}, },
type: "state-label", type: "state-label",
@@ -59,8 +96,8 @@ export const demoLovelaceKernehed: DemoConfig["lovelace"] = () => ({
{ {
style: { style: {
color: "white", color: "white",
top: "93%", top: "92%",
left: "85%", left: "90%",
}, },
type: "state-label", type: "state-label",
entity: "sensor.battery_bella", entity: "sensor.battery_bella",
@@ -68,7 +105,7 @@ export const demoLovelaceKernehed: DemoConfig["lovelace"] = () => ({
{ {
style: { style: {
color: "white", color: "white",
top: "93%", top: "92%",
left: "55%", left: "55%",
}, },
type: "state-label", type: "state-label",
@@ -94,6 +131,78 @@ export const demoLovelaceKernehed: DemoConfig["lovelace"] = () => ({
type: "entities", type: "entities",
title: "Lock", title: "Lock",
}, },
// {
// filter: {
// exclude: [
// {
// state: "not_home",
// },
// ],
// include: [
// {
// entity_id: "device_tracker.annasiphone",
// },
// {
// entity_id: "device_tracker.iphone_2",
// },
// ],
// },
// type: "custom:monster-card",
// card: {
// show_header_toggle: false,
// type: "entities",
// title: "G\u00e4ster",
// },
// show_empty: false,
// },
// {
// filter: {
// exclude: [
// {
// state: "Inget",
// },
// {
// state: "i.u.",
// },
// ],
// include: [
// {
// entity_id: "sensor.pollen_al",
// },
// {
// entity_id: "sensor.pollen_alm",
// },
// {
// entity_id: "sensor.pollen_salg_vide",
// },
// {
// entity_id: "sensor.pollen_bjork",
// },
// {
// entity_id: "sensor.pollen_bok",
// },
// {
// entity_id: "sensor.pollen_ek",
// },
// {
// entity_id: "sensor.pollen_grabo",
// },
// {
// entity_id: "sensor.pollen_gras",
// },
// {
// entity_id: "sensor.pollen_hassel",
// },
// ],
// },
// type: "custom:monster-card",
// card: {
// show_header_toggle: false,
// type: "entities",
// title: "Pollenniv\u00e5er",
// },
// show_empty: false,
// },
{ {
cards: [ cards: [
{ {
@@ -117,6 +226,10 @@ export const demoLovelaceKernehed: DemoConfig["lovelace"] = () => ({
], ],
type: "vertical-stack", type: "vertical-stack",
}, },
// {
// url: "https://embed.windy.com/embed2.html",
// type: "iframe",
// },
{ {
entities: [ entities: [
{ {
@@ -150,7 +263,6 @@ export const demoLovelaceKernehed: DemoConfig["lovelace"] = () => ({
], ],
type: "glance", type: "glance",
show_state: false, show_state: false,
columns: 4,
}, },
{ {
entities: ["sensor.oskar_bluetooth"], entities: ["sensor.oskar_bluetooth"],
@@ -158,6 +270,32 @@ export const demoLovelaceKernehed: DemoConfig["lovelace"] = () => ({
type: "entities", type: "entities",
title: "Occupancy", title: "Occupancy",
}, },
// {
// filter: {
// exclude: [
// {
// state: false,
// },
// ],
// include: [
// {
// entity_id:
// "binary_sensor.fibaro_system_unknown_type0c02_id1003_sensor_2",
// },
// {
// entity_id:
// "binary_sensor.fibaro_system_unknown_type0c02_id1003_sensor_3",
// },
// ],
// },
// type: "custom:monster-card",
// card: {
// show_header_toggle: false,
// type: "entities",
// title: "Brandvarnare",
// },
// show_empty: false,
// },
{ {
type: "weather-forecast", type: "weather-forecast",
entity: "weather.smhi_vader", entity: "weather.smhi_vader",
@@ -240,9 +378,41 @@ export const demoLovelaceKernehed: DemoConfig["lovelace"] = () => ({
"binary_sensor.windows_server", "binary_sensor.windows_server",
"binary_sensor.teamspeak", "binary_sensor.teamspeak",
"binary_sensor.harmony_hub", "binary_sensor.harmony_hub",
// {
// style: {
// height: "1px",
// width: "85%",
// "margin-left": "auto",
// background: "#62717b",
// "margin-right": "auto",
// },
// type: "divider",
// },
// {
// items: ["sensor.uptime_router", "sensor.installerad_routeros"],
// head: {
// entity: "binary_sensor.router",
// },
// type: "custom:fold-entity-row",
// group_config: {
// icon: "mdi:router",
// },
// },
// {
// items: [
// "sensor.uptime_router_server",
// "sensor.installerad_routeros_server",
// ],
// head: {
// entity: "binary_sensor.router_server",
// },
// type: "custom:fold-entity-row",
// group_config: {
// icon: "mdi:router",
// },
// },
], ],
show_header_toggle: false, show_header_toggle: false,
state_color: true,
type: "entities", type: "entities",
title: "Network", title: "Network",
}, },
@@ -252,10 +422,29 @@ export const demoLovelaceKernehed: DemoConfig["lovelace"] = () => ({
"binary_sensor.ubiquiti_switch", "binary_sensor.ubiquiti_switch",
"binary_sensor.ubiquiti_nvr", "binary_sensor.ubiquiti_nvr",
"binary_sensor.entre_kamera", "binary_sensor.entre_kamera",
// {
// items: ["sensor.uptime_ap_1"],
// head: {
// entity: "binary_sensor.accesspunkt_1",
// },
// type: "custom:fold-entity-row",
// group_config: {
// icon: "router-wireless",
// },
// },
// {
// items: ["sensor.uptime_ap_2"],
// head: {
// entity: "binary_sensor.accesspunkt_2",
// },
// type: "custom:fold-entity-row",
// group_config: {
// icon: "router-wireless",
// },
// },
"sensor.total_clients_wireless", "sensor.total_clients_wireless",
], ],
show_header_toggle: false, show_header_toggle: false,
state_color: true,
type: "entities", type: "entities",
title: "Ubiquiti", title: "Ubiquiti",
}, },

View File

@@ -215,7 +215,6 @@ export const demoLovelaceTeachingbirds: DemoConfig["lovelace"] = () => ({
card: { card: {
type: "glance", type: "glance",
show_state: false, show_state: false,
columns: 4,
}, },
state_filter: ["on"], state_filter: ["on"],
}, },
@@ -809,6 +808,67 @@ export const demoLovelaceTeachingbirds: DemoConfig["lovelace"] = () => ({
], ],
type: "vertical-stack", type: "vertical-stack",
}, },
// {
// cards: [
// {
// entities: [
// {
// hide_when_off: true,
// toggle: true,
// type: "custom:slider-entity-row",
// name: "Bedside",
// entity: "light.bedside_lamp",
// },
// {
// hide_when_off: true,
// toggle: true,
// type: "custom:slider-entity-row",
// name: "Bedroom",
// entity: "light.bedroom_ceiling_light",
// },
// {
// hide_when_off: true,
// toggle: true,
// type: "custom:slider-entity-row",
// name: "Isa",
// entity: "light.isa_ceiling_light",
// },
// {
// hide_when_off: true,
// toggle: true,
// type: "custom:slider-entity-row",
// name: "Upstairs hallway",
// entity: "light.upstairs_hallway_ceiling_light_level",
// },
// {
// hide_when_off: true,
// toggle: true,
// type: "custom:slider-entity-row",
// name: "Nightlight",
// entity: "light.gateway_light_34ce008bfc4b",
// },
// {
// hide_when_off: true,
// toggle: true,
// type: "custom:slider-entity-row",
// name: "Walk in closet",
// entity: "light.walk_in_closet_lights",
// },
// {
// hide_when_off: true,
// toggle: false,
// type: "custom:slider-entity-row",
// name: "Stefan",
// entity: "light.stefan_lightstrip",
// },
// ],
// show_header_toggle: false,
// type: "entities",
// title: "Upstairs",
// },
// ],
// type: "vertical-stack",
// },
], ],
path: "lights", path: "lights",
title: "Lights", title: "Lights",
@@ -858,6 +918,10 @@ export const demoLovelaceTeachingbirds: DemoConfig["lovelace"] = () => ({
name: "Dafang", name: "Dafang",
icon: "mdi:webcam", icon: "mdi:webcam",
}, },
{
name: "IR Hallway",
entity: "sensor.system_ir_blaster",
},
{ {
name: "IR Bedroom", name: "IR Bedroom",
entity: "sensor.system_ir_blaster_bedroom", entity: "sensor.system_ir_blaster_bedroom",
@@ -876,7 +940,7 @@ export const demoLovelaceTeachingbirds: DemoConfig["lovelace"] = () => ({
"sensor.system_ring_chime", "sensor.system_ring_chime",
], ],
type: "glance", type: "glance",
columns: 4, columns: 5,
show_state: false, show_state: false,
}, },
{ {

View File

@@ -9,5 +9,5 @@ export interface DemoConfig {
authorUrl: string; authorUrl: string;
lovelace: (localize: LocalizeFunc) => LovelaceConfig; lovelace: (localize: LocalizeFunc) => LovelaceConfig;
entities: (localize: LocalizeFunc) => Entity[]; entities: (localize: LocalizeFunc) => Entity[];
theme: () => Record<string, string> | null; theme: () => { [key: string]: string } | null;
} }

View File

@@ -3,8 +3,8 @@ import {
CSSResult, CSSResult,
customElement, customElement,
html, html,
internalProperty,
LitElement, LitElement,
internalProperty,
TemplateResult, TemplateResult,
} from "lit-element"; } from "lit-element";
import { CastManager } from "../../../src/cast/cast_manager"; import { CastManager } from "../../../src/cast/cast_manager";

View File

@@ -3,9 +3,9 @@ import {
css, css,
CSSResult, CSSResult,
html, html,
internalProperty,
LitElement, LitElement,
property, property,
internalProperty,
TemplateResult, TemplateResult,
} from "lit-element"; } from "lit-element";
import { until } from "lit-html/directives/until"; import { until } from "lit-html/directives/until";

View File

@@ -1,11 +1,13 @@
import "../../src/resources/safari-14-attachshadow-patch";
import "@polymer/polymer/lib/elements/dom-if"; import "@polymer/polymer/lib/elements/dom-if";
import "@polymer/polymer/lib/elements/dom-repeat"; 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 "./ha-demo"; import "./ha-demo";
/* polyfill for paper-dropdown */ /* polyfill for paper-dropdown */
setTimeout(() => { setTimeout(() => {
import("web-animations-js/web-animations-next-lite.min"); import(
/* webpackChunkName: "polyfill-web-animations-next" */ "web-animations-js/web-animations-next-lite.min"
);
}, 1000); }, 1000);

View File

@@ -1,4 +1,3 @@
// Compat needs to be first import
import "../../src/resources/compatibility"; import "../../src/resources/compatibility";
import { isNavigationClick } from "../../src/common/dom/is-navigation-click"; import { isNavigationClick } from "../../src/common/dom/is-navigation-click";
import { navigate } from "../../src/common/navigate"; import { navigate } from "../../src/common/navigate";

View File

@@ -21,16 +21,15 @@ class DemoCard extends PolymerElement {
} }
pre { pre {
width: 400px; width: 400px;
margin: 0 16px; margin: 16px;
overflow: auto; overflow: auto;
color: var(--primary-text-color);
} }
@media only screen and (max-width: 800px) { @media only screen and (max-width: 800px) {
.root { .root {
flex-direction: column; flex-direction: column;
} }
pre { pre {
margin: 16px 0; margin-left: 0;
} }
} }
</style> </style>

View File

@@ -2,10 +2,10 @@ import "@polymer/app-layout/app-toolbar/app-toolbar";
import { html } from "@polymer/polymer/lib/utils/html-tag"; import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */ /* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element"; import { PolymerElement } from "@polymer/polymer/polymer-element";
import { applyThemesOnElement } from "../../../src/common/dom/apply_themes_on_element";
import "../../../src/components/ha-formfield";
import "../../../src/components/ha-switch"; import "../../../src/components/ha-switch";
import "../../../src/components/ha-formfield";
import "./demo-card"; import "./demo-card";
import { applyThemesOnElement } from "../../../src/common/dom/apply_themes_on_element";
class DemoCards extends PolymerElement { class DemoCards extends PolymerElement {
static get template() { static get template() {

View File

@@ -2,62 +2,58 @@ import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */ /* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element"; import { PolymerElement } from "@polymer/polymer/polymer-element";
import "../../../src/components/ha-card"; import "../../../src/components/ha-card";
import "../../../src/dialogs/more-info/more-info-content";
import "../../../src/state-summary/state-card-content"; import "../../../src/state-summary/state-card-content";
import "../../../src/dialogs/more-info/more-info-content";
class DemoMoreInfo extends PolymerElement { class DemoMoreInfo extends PolymerElement {
static get template() { static get template() {
return html` return html`
<style> <style>
.root { :host {
display: flex; display: flex;
align-items: start;
} }
#card {
max-width: 400px;
width: 100vw;
}
ha-card { ha-card {
width: 352px; width: 333px;
padding: 20px 24px; padding: 20px 24px;
} }
state-card-content { state-card-content {
display: block; display: block;
margin-bottom: 16px; margin-bottom: 16px;
} }
pre { pre {
width: 400px; width: 400px;
margin: 0 16px; margin: 16px;
overflow: auto; overflow: auto;
color: var(--primary-text-color);
} }
@media only screen and (max-width: 800px) { @media only screen and (max-width: 800px) {
.root { :host {
flex-direction: column; flex-direction: column;
} }
pre { pre {
margin: 16px 0; margin-left: 0;
} }
} }
</style> </style>
<div class="root"> <ha-card>
<div id="card"> <state-card-content
<ha-card> state-obj="[[_stateObj]]"
<state-card-content hass="[[hass]]"
state-obj="[[_stateObj]]" in-dialog
hass="[[hass]]" ></state-card-content>
in-dialog
></state-card-content>
<more-info-content <more-info-content
hass="[[hass]]" hass="[[hass]]"
state-obj="[[_stateObj]]" state-obj="[[_stateObj]]"
></more-info-content> ></more-info-content>
</ha-card> </ha-card>
</div> <template is="dom-if" if="[[showConfig]]">
<template is="dom-if" if="[[showConfig]]"> <pre>[[_jsonEntity(_stateObj)]]</pre>
<pre>[[_jsonEntity(_stateObj)]]</pre> </template>
</template>
</div>
`; `;
} }

View File

@@ -2,8 +2,6 @@ import "@polymer/app-layout/app-toolbar/app-toolbar";
import { html } from "@polymer/polymer/lib/utils/html-tag"; import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */ /* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element"; import { PolymerElement } from "@polymer/polymer/polymer-element";
import { applyThemesOnElement } from "../../../src/common/dom/apply_themes_on_element";
import "../../../src/components/ha-formfield";
import "../../../src/components/ha-switch"; import "../../../src/components/ha-switch";
import "./demo-more-info"; import "./demo-more-info";
@@ -11,10 +9,6 @@ class DemoMoreInfos extends PolymerElement {
static get template() { static get template() {
return html` return html`
<style> <style>
#container {
min-height: calc(100vh - 128px);
background: var(--primary-background-color);
}
.cards { .cards {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
@@ -29,31 +23,20 @@ class DemoMoreInfos extends PolymerElement {
.filters { .filters {
margin-left: 60px; margin-left: 60px;
} }
ha-formfield {
margin-right: 16px;
}
</style> </style>
<app-toolbar> <app-toolbar>
<div class="filters"> <div class="filters">
<ha-formfield label="Show entities"> <ha-switch checked="{{_showConfig}}">Show entity</ha-switch>
<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> </div>
</app-toolbar> </app-toolbar>
<div id="container"> <div class="cards">
<div class="cards"> <template is="dom-repeat" items="[[entities]]">
<template is="dom-repeat" items="[[entities]]"> <demo-more-info
<demo-more-info entity-id="[[item]]"
entity-id="[[item]]" show-config="[[_showConfig]]"
show-config="[[_showConfig]]" hass="[[hass]]"
hass="[[hass]]" ></demo-more-info>
></demo-more-info> </template>
</template>
</div>
</div> </div>
`; `;
} }
@@ -68,16 +51,6 @@ class DemoMoreInfos extends PolymerElement {
}, },
}; };
} }
_showConfigToggled(ev) {
this._showConfig = ev.target.checked;
}
_darkThemeToggled(ev) {
applyThemesOnElement(this.$.container, { themes: {} }, "default", {
dark: ev.target.checked,
});
}
} }
customElements.define("demo-more-infos", DemoMoreInfos); customElements.define("demo-more-infos", DemoMoreInfos);

View File

@@ -7,8 +7,8 @@ export const createMediaPlayerEntities = () => [
media_title: "I Wanna Be A Hippy (Flamman & Abraxas Radio Mix)", media_title: "I Wanna Be A Hippy (Flamman & Abraxas Radio Mix)",
media_artist: "Technohead", media_artist: "Technohead",
// Pause + Seek + Volume Set + Volume Mute + Previous Track + Next Track + Play Media + // Pause + Seek + Volume Set + Volume Mute + Previous Track + Next Track + Play Media +
// Select Source + Stop + Clear + Play + Shuffle Set // Select Source + Stop + Clear + Play + Shuffle Set + Browse Media
supported_features: 64063, supported_features: 195135,
entity_picture: "/images/album_cover_2.jpg", entity_picture: "/images/album_cover_2.jpg",
media_duration: 300, media_duration: 300,
media_position: 50, media_position: 50,
@@ -24,8 +24,8 @@ export const createMediaPlayerEntities = () => [
media_title: "I Wanna Be A Hippy (Flamman & Abraxas Radio Mix)", media_title: "I Wanna Be A Hippy (Flamman & Abraxas Radio Mix)",
media_artist: "Technohead", media_artist: "Technohead",
// Pause + Seek + Volume Set + Volume Mute + Previous Track + Next Track + Play Media + // Pause + Seek + Volume Set + Volume Mute + Previous Track + Next Track + Play Media +
// Select Source + Stop + Clear + Play + Shuffle Set + Browse Media // Select Source + Stop + Clear + Play + Shuffle Set
supported_features: 195135, supported_features: 64063,
entity_picture: "/images/album_cover.jpg", entity_picture: "/images/album_cover.jpg",
media_duration: 300, media_duration: 300,
media_position: 0, media_position: 0,

View File

@@ -1,72 +0,0 @@
import { getEntity } from "../../../src/fake_data/entity";
export const createPlantEntities = () => [
getEntity("plant", "lemon_tree", "ok", {
problem: "none",
sensors: {
moisture: "sensor.lemon_tree_moisture",
battery: "sensor.lemon_tree_battery",
temperature: "sensor.lemon_tree_temperature",
conductivity: "sensor.lemon_tree_conductivity",
brightness: "sensor.lemon_tree_brightness",
},
unit_of_measurement_dict: {
temperature: "°C",
moisture: "%",
brightness: "lx",
battery: "%",
conductivity: "μS/cm",
},
moisture: 54,
battery: 95,
temperature: 15.6,
conductivity: 1,
brightness: 12,
max_brightness: 20,
friendly_name: "Lemon Tree",
}),
getEntity("plant", "apple_tree", "ok", {
problem: "brightness",
sensors: {
moisture: "sensor.apple_tree_moisture",
battery: "sensor.apple_tree_battery",
temperature: "sensor.apple_tree_temperature",
conductivity: "sensor.apple_tree_conductivity",
brightness: "sensor.apple_tree_brightness",
},
unit_of_measurement_dict: {
temperature: "°C",
moisture: "%",
brightness: "lx",
battery: "%",
conductivity: "μS/cm",
},
moisture: 54,
battery: 2,
temperature: 15.6,
conductivity: 1,
brightness: 25,
max_brightness: 20,
friendly_name: "Apple Tree",
}),
getEntity("plant", "sunflowers", "ok", {
problem: "moisture, temperature, conductivity",
sensors: {
moisture: "sensor.sunflowers_moisture",
temperature: "sensor.sunflowers_temperature",
conductivity: "sensor.sunflowers_conductivity",
brightness: "sensor.sunflowers_brightness",
},
unit_of_measurement_dict: {
temperature: "°C",
moisture: "%",
brightness: "lx",
conductivity: "μS/cm",
},
moisture: 54,
temperature: 15.6,
conductivity: 1,
brightness: 25,
entity_picture: "/images/sunflowers.jpg",
}),
];

View File

@@ -1,349 +0,0 @@
import { DemoTrace } from "./types";
export const basicTrace: DemoTrace = {
trace: {
last_step: "action/2",
run_id: "0",
state: "stopped",
timestamp: {
start: "2021-03-25T04:36:51.223693+00:00",
finish: "2021-03-25T04:36:51.266132+00:00",
},
trigger: "state of input_boolean.toggle_1",
domain: "automation",
item_id: "1615419646544",
trace: {
"trigger/0": [
{
path: "trigger/0",
timestamp: "2021-03-25T04:36:51.223693+00:00",
},
],
"condition/0": [
{
path: "condition/0",
timestamp: "2021-03-25T04:36:51.228243+00:00",
changed_variables: {
trigger: {
platform: "state",
entity_id: "input_boolean.toggle_1",
from_state: {
entity_id: "input_boolean.toggle_1",
state: "on",
attributes: {
editable: true,
friendly_name: "Toggle 1",
},
last_changed: "2021-03-24T19:03:59.141440+00:00",
last_updated: "2021-03-24T19:03:59.141440+00:00",
context: {
id: "5d0918eb379214d07554bdab6a08bcff",
parent_id: null,
user_id: null,
},
},
to_state: {
entity_id: "input_boolean.toggle_1",
state: "off",
attributes: {
editable: true,
friendly_name: "Toggle 1",
},
last_changed: "2021-03-25T04:36:51.220696+00:00",
last_updated: "2021-03-25T04:36:51.220696+00:00",
context: {
id: "664d6d261450a9ecea6738e97269a149",
parent_id: null,
user_id: "d1b4e89da01445fa8bc98e39fac477ca",
},
},
for: null,
attribute: null,
description: "state of input_boolean.toggle_1",
},
},
result: {
result: true,
},
},
],
"action/0": [
{
path: "action/0",
timestamp: "2021-03-25T04:36:51.243018+00:00",
changed_variables: {
trigger: {
platform: "state",
entity_id: "input_boolean.toggle_1",
from_state: {
entity_id: "input_boolean.toggle_1",
state: "on",
attributes: {
editable: true,
friendly_name: "Toggle 1",
},
last_changed: "2021-03-24T19:03:59.141440+00:00",
last_updated: "2021-03-24T19:03:59.141440+00:00",
context: {
id: "5d0918eb379214d07554bdab6a08bcff",
parent_id: null,
user_id: null,
},
},
to_state: {
entity_id: "input_boolean.toggle_1",
state: "off",
attributes: {
editable: true,
friendly_name: "Toggle 1",
},
last_changed: "2021-03-25T04:36:51.220696+00:00",
last_updated: "2021-03-25T04:36:51.220696+00:00",
context: {
id: "664d6d261450a9ecea6738e97269a149",
parent_id: null,
user_id: "d1b4e89da01445fa8bc98e39fac477ca",
},
},
for: null,
attribute: null,
description: "state of input_boolean.toggle_1",
},
context: {
id: "6cfcae368e7b3686fad6c59e83ae76c9",
parent_id: "664d6d261450a9ecea6738e97269a149",
user_id: null,
},
},
result: {
params: {
domain: "input_boolean",
service: "toggle",
service_data: {},
target: {
entity_id: ["input_boolean.toggle_4"],
},
},
running_script: false,
limit: 10,
},
},
],
"action/1": [
{
path: "action/1",
timestamp: "2021-03-25T04:36:51.252406+00:00",
result: {
choice: 0,
},
},
],
"action/1/choose/0": [
{
path: "action/1/choose/0",
timestamp: "2021-03-25T04:36:51.254569+00:00",
result: {
result: true,
},
},
],
"action/1/choose/0/conditions/0": [
{
path: "action/1/choose/0/conditions/0",
timestamp: "2021-03-25T04:36:51.254697+00:00",
result: {
result: true,
},
},
],
"action/1/choose/0/sequence/0": [
{
path: "action/1/choose/0/sequence/0",
timestamp: "2021-03-25T04:36:51.257360+00:00",
result: {
params: {
domain: "input_boolean",
service: "toggle",
service_data: {},
target: {
entity_id: ["input_boolean.toggle_2"],
},
},
running_script: false,
limit: 10,
},
},
],
"action/1/choose/0/sequence/1": [
{
path: "action/1/choose/0/sequence/1",
timestamp: "2021-03-25T04:36:51.260658+00:00",
result: {
params: {
domain: "input_boolean",
service: "toggle",
service_data: {},
target: {
entity_id: ["input_boolean.toggle_3"],
},
},
running_script: false,
limit: 10,
},
},
],
"action/2": [
{
path: "action/2",
timestamp: "2021-03-25T04:36:51.264159+00:00",
result: {
params: {
domain: "input_boolean",
service: "toggle",
service_data: {},
target: {
entity_id: ["input_boolean.toggle_4"],
},
},
running_script: false,
limit: 10,
},
},
],
},
config: {
id: "1615419646544",
alias: "Ensure Party mode",
description: "",
trigger: [
{
platform: "state",
entity_id: "input_boolean.toggle_1",
},
],
condition: [
{
condition: "template",
alias: "Test if Paulus is home",
value_template: "{{ true }}",
},
],
action: [
{
service: "input_boolean.toggle",
target: {
entity_id: "input_boolean.toggle_4",
},
},
{
choose: [
{
alias: "If toggle 3 is on",
conditions: [
{
condition: "template",
value_template:
"{{ is_state('input_boolean.toggle_3', 'on') }}",
},
],
sequence: [
{
service: "input_boolean.toggle",
alias: "Toggle 2 while 3 is on",
target: {
entity_id: "input_boolean.toggle_2",
},
},
{
service: "input_boolean.toggle",
alias: "Toggle 3",
target: {
entity_id: "input_boolean.toggle_3",
},
},
],
},
],
default: [
{
service: "input_boolean.toggle",
alias: "Toggle 2",
target: {
entity_id: "input_boolean.toggle_2",
},
},
],
},
{
service: "input_boolean.toggle",
target: {
entity_id: "input_boolean.toggle_4",
},
},
],
mode: "single",
},
context: {
id: "6cfcae368e7b3686fad6c59e83ae76c9",
parent_id: "664d6d261450a9ecea6738e97269a149",
user_id: null,
},
script_execution: "finished",
},
logbookEntries: [
{
name: "Ensure Party mode",
message: "has been triggered by state of input_boolean.toggle_1",
source: "state of input_boolean.toggle_1",
entity_id: "automation.toggle_toggles",
context_id: "6cfcae368e7b3686fad6c59e83ae76c9",
when: "2021-03-25T04:36:51.240832+00:00",
domain: "automation",
},
{
when: "2021-03-25T04:36:51.249828+00:00",
name: "Toggle 4",
state: "on",
entity_id: "input_boolean.toggle_4",
context_entity_id: "automation.toggle_toggles",
context_entity_id_name: "Ensure Party mode",
context_event_type: "automation_triggered",
context_domain: "automation",
context_name: "Ensure Party mode",
},
{
when: "2021-03-25T04:36:51.258947+00:00",
name: "Toggle 2",
state: "on",
entity_id: "input_boolean.toggle_2",
context_entity_id: "automation.toggle_toggles",
context_entity_id_name: "Ensure Party mode",
context_event_type: "automation_triggered",
context_domain: "automation",
context_name: "Ensure Party mode",
},
{
when: "2021-03-25T04:36:51.261806+00:00",
name: "Toggle 3",
state: "off",
entity_id: "input_boolean.toggle_3",
context_entity_id: "automation.toggle_toggles",
context_entity_id_name: "Ensure Party mode",
context_event_type: "automation_triggered",
context_domain: "automation",
context_name: "Ensure Party mode",
},
{
when: "2021-03-25T04:36:51.265246+00:00",
name: "Toggle 4",
state: "off",
entity_id: "input_boolean.toggle_4",
context_entity_id: "automation.toggle_toggles",
context_entity_id_name: "Ensure Party mode",
context_event_type: "automation_triggered",
context_domain: "automation",
context_name: "Ensure Party mode",
},
],
};

View File

@@ -1,44 +0,0 @@
import { LogbookEntry } from "../../../../src/data/logbook";
import { AutomationTraceExtended } from "../../../../src/data/trace";
import { DemoTrace } from "./types";
export const mockDemoTrace = (
tracePartial: Partial<AutomationTraceExtended>,
logbookEntries?: LogbookEntry[]
): DemoTrace => ({
trace: {
last_step: "",
run_id: "0",
state: "stopped",
timestamp: {
start: "2021-03-25T04:36:51.223693+00:00",
finish: "2021-03-25T04:36:51.266132+00:00",
},
trigger: "mocked trigger",
domain: "automation",
item_id: "1615419646544",
trace: {
"trigger/0": [
{
path: "trigger/0",
changed_variables: {
trigger: {
description: "mocked trigger",
},
},
timestamp: "2021-03-25T04:36:51.223693+00:00",
},
],
},
config: {
trigger: [],
action: [],
},
context: {
id: "abcd",
},
script_execution: "finished",
...tracePartial,
},
logbookEntries: logbookEntries || [],
});

View File

@@ -1,214 +0,0 @@
import { DemoTrace } from "./types";
export const motionLightTrace: DemoTrace = {
trace: {
last_step: "action/3",
run_id: "1",
state: "stopped",
timestamp: {
start: "2021-03-14T06:07:01.768006+00:00",
finish: "2021-03-14T06:07:53.287525+00:00",
},
trigger: "state of binary_sensor.pauluss_macbook_pro_camera_in_use",
domain: "automation",
item_id: "1614732497392",
trace: {
"trigger/0": [
{
path: "trigger/0",
timestamp: "2021-03-25T04:36:51.223693+00:00",
},
],
"action/0": [
{
path: "action/0",
timestamp: "2021-03-14T06:07:01.771038+00:00",
changed_variables: {
trigger: {
platform: "state",
entity_id: "binary_sensor.pauluss_macbook_pro_camera_in_use",
from_state: {
entity_id: "binary_sensor.pauluss_macbook_pro_camera_in_use",
state: "off",
attributes: {
friendly_name: "Pauluss MacBook Pro Camera In Use",
icon: "mdi:camera-off",
},
last_changed: "2021-03-14T06:06:29.235325+00:00",
last_updated: "2021-03-14T06:06:29.235325+00:00",
context: {
id: "ad4864c5ce957c38a07b50378eeb245d",
parent_id: null,
user_id: null,
},
},
to_state: {
entity_id: "binary_sensor.pauluss_macbook_pro_camera_in_use",
state: "on",
attributes: {
friendly_name: "Pauluss MacBook Pro Camera In Use",
icon: "mdi:camera",
},
last_changed: "2021-03-14T06:07:01.762009+00:00",
last_updated: "2021-03-14T06:07:01.762009+00:00",
context: {
id: "e22ddfd5f11dc4aad9a52fc10dab613b",
parent_id: null,
user_id: null,
},
},
for: null,
attribute: null,
description:
"state of binary_sensor.pauluss_macbook_pro_camera_in_use",
},
context: {
id: "43b6ee9293a551c5cc14e8eb60af54ba",
parent_id: "e22ddfd5f11dc4aad9a52fc10dab613b",
user_id: null,
},
},
},
],
"action/1": [
{ path: "action/1", timestamp: "2021-03-14T06:07:01.875316+00:00" },
],
"action/2": [
{
path: "action/2",
timestamp: "2021-03-14T06:07:53.195013+00:00",
changed_variables: {
wait: {
remaining: null,
trigger: {
platform: "state",
entity_id: "binary_sensor.pauluss_macbook_pro_camera_in_use",
from_state: {
entity_id: "binary_sensor.pauluss_macbook_pro_camera_in_use",
state: "on",
attributes: {
friendly_name: "Pauluss MacBook Pro Camera In Use",
icon: "mdi:camera",
},
last_changed: "2021-03-14T06:07:01.762009+00:00",
last_updated: "2021-03-14T06:07:01.762009+00:00",
context: {
id: "e22ddfd5f11dc4aad9a52fc10dab613b",
parent_id: null,
user_id: null,
},
},
to_state: {
entity_id: "binary_sensor.pauluss_macbook_pro_camera_in_use",
state: "off",
attributes: {
friendly_name: "Pauluss MacBook Pro Camera In Use",
icon: "mdi:camera-off",
},
last_changed: "2021-03-14T06:07:53.186755+00:00",
last_updated: "2021-03-14T06:07:53.186755+00:00",
context: {
id: "b2308cc91d509ea8e0c623331ab178d6",
parent_id: null,
user_id: null,
},
},
for: null,
attribute: null,
description:
"state of binary_sensor.pauluss_macbook_pro_camera_in_use",
},
},
},
},
],
"action/3": [
{
path: "action/3",
timestamp: "2021-03-14T06:07:53.196014+00:00",
},
],
},
config: {
mode: "restart",
max_exceeded: "silent",
trigger: [
{
platform: "state",
entity_id: "binary_sensor.pauluss_macbook_pro_camera_in_use",
from: "off",
to: "on",
},
],
action: [
{
service: "light.turn_on",
target: {
entity_id: "light.elgato_key_light_air",
},
},
{
wait_for_trigger: [
{
platform: "state",
entity_id: "binary_sensor.pauluss_macbook_pro_camera_in_use",
from: "on",
to: "off",
},
],
},
{
delay: 0,
},
{
service: "light.turn_off",
target: {
entity_id: "light.elgato_key_light_air",
},
},
],
id: "1614732497392",
alias: "Auto Elgato",
description: "",
},
context: {
id: "43b6ee9293a551c5cc14e8eb60af54ba",
parent_id: "e22ddfd5f11dc4aad9a52fc10dab613b",
user_id: null,
},
script_execution: "finished",
},
logbookEntries: [
{
name: "Auto Elgato",
message:
"has been triggered by state of binary_sensor.pauluss_macbook_pro_camera_in_use",
source: "state of binary_sensor.pauluss_macbook_pro_camera_in_use",
entity_id: "automation.auto_elgato",
when: "2021-03-14T06:07:01.768492+00:00",
domain: "automation",
},
{
when: "2021-03-14T06:07:01.872187+00:00",
name: "Elgato Key Light Air",
state: "on",
entity_id: "light.elgato_key_light_air",
context_entity_id: "automation.auto_elgato",
context_entity_id_name: "Auto Elgato",
context_event_type: "automation_triggered",
context_domain: "automation",
context_name: "Auto Elgato",
},
{
when: "2021-03-14T06:07:53.284505+00:00",
name: "Elgato Key Light Air",
state: "off",
entity_id: "light.elgato_key_light_air",
context_entity_id: "automation.auto_elgato",
context_entity_id_name: "Auto Elgato",
context_event_type: "automation_triggered",
context_domain: "automation",
context_name: "Auto Elgato",
},
],
};

View File

@@ -1,7 +0,0 @@
import { AutomationTraceExtended } from "../../../../src/data/trace";
import { LogbookEntry } from "../../../../src/data/logbook";
export interface DemoTrace {
trace: AutomationTraceExtended;
logbookEntries: LogbookEntry[];
}

View File

@@ -1,102 +0,0 @@
import { safeDump } from "js-yaml";
import {
customElement,
html,
css,
LitElement,
TemplateResult,
property,
} from "lit-element";
import "../../../src/components/ha-card";
import { describeAction } from "../../../src/data/script_i18n";
import { provideHass } from "../../../src/fake_data/provide_hass";
import { HomeAssistant } from "../../../src/types";
const actions = [
{ wait_template: "{{ true }}", alias: "Something with an alias" },
{ delay: "0:05" },
{ wait_template: "{{ true }}" },
{
condition: "template",
value_template: "{{ true }}",
},
{ event: "happy_event" },
{
device_id: "abcdefgh",
domain: "plex",
entity_id: "media_player.kitchen",
},
{ scene: "scene.kitchen_morning" },
{
wait_for_trigger: [
{
platform: "state",
entity_id: "input_boolean.toggle_1",
},
],
},
{
variables: {
hello: "world",
},
},
{
service: "input_boolean.toggle",
target: {
entity_id: "input_boolean.toggle_4",
},
},
];
@customElement("demo-automation-describe-action")
export class DemoAutomationDescribeAction extends LitElement {
@property({ attribute: false }) hass!: HomeAssistant;
protected render(): TemplateResult {
if (!this.hass) {
return html``;
}
return html`
<ha-card header="Actions">
${actions.map(
(conf) => html`
<div class="action">
<span>${describeAction(this.hass, conf as any)}</span>
<pre>${safeDump(conf)}</pre>
</div>
`
)}
</ha-card>
`;
}
protected firstUpdated(changedProps) {
super.firstUpdated(changedProps);
const hass = provideHass(this);
hass.updateTranslations(null, "en");
}
static get styles() {
return css`
ha-card {
max-width: 600px;
margin: 24px auto;
}
.action {
padding: 16px;
display: flex;
align-items: center;
justify-content: space-between;
}
span {
margin-right: 16px;
}
`;
}
}
declare global {
interface HTMLElementTagNameMap {
"demo-automation-describe-action": DemoAutomationDescribeAction;
}
}

View File

@@ -1,65 +0,0 @@
import { safeDump } from "js-yaml";
import {
customElement,
html,
css,
LitElement,
TemplateResult,
} from "lit-element";
import "../../../src/components/ha-card";
import { describeCondition } from "../../../src/data/automation_i18n";
const conditions = [
{ condition: "and" },
{ condition: "not" },
{ condition: "or" },
{ condition: "state" },
{ condition: "numeric_state" },
{ condition: "sun", after: "sunset" },
{ condition: "sun", after: "sunrise" },
{ condition: "zone" },
{ condition: "time" },
{ condition: "template" },
];
@customElement("demo-automation-describe-condition")
export class DemoAutomationDescribeCondition extends LitElement {
protected render(): TemplateResult {
return html`
<ha-card header="Conditions">
${conditions.map(
(conf) => html`
<div class="condition">
<span>${describeCondition(conf as any)}</span>
<pre>${safeDump(conf)}</pre>
</div>
`
)}
</ha-card>
`;
}
static get styles() {
return css`
ha-card {
max-width: 600px;
margin: 24px auto;
}
.condition {
padding: 16px;
display: flex;
align-items: center;
justify-content: space-between;
}
span {
margin-right: 16px;
}
`;
}
}
declare global {
interface HTMLElementTagNameMap {
"demo-automation-describe-condition": DemoAutomationDescribeCondition;
}
}

View File

@@ -1,68 +0,0 @@
import { safeDump } from "js-yaml";
import {
customElement,
html,
css,
LitElement,
TemplateResult,
} from "lit-element";
import "../../../src/components/ha-card";
import { describeTrigger } from "../../../src/data/automation_i18n";
const triggers = [
{ platform: "state" },
{ platform: "mqtt" },
{ platform: "geo_location" },
{ platform: "homeassistant" },
{ platform: "numeric_state" },
{ platform: "sun" },
{ platform: "time_pattern" },
{ platform: "webhook" },
{ platform: "zone" },
{ platform: "tag" },
{ platform: "time" },
{ platform: "template" },
{ platform: "event" },
];
@customElement("demo-automation-describe-trigger")
export class DemoAutomationDescribeTrigger extends LitElement {
protected render(): TemplateResult {
return html`
<ha-card header="Triggers">
${triggers.map(
(conf) => html`
<div class="trigger">
<span>${describeTrigger(conf as any)}</span>
<pre>${safeDump(conf)}</pre>
</div>
`
)}
</ha-card>
`;
}
static get styles() {
return css`
ha-card {
max-width: 600px;
margin: 24px auto;
}
.trigger {
padding: 16px;
display: flex;
align-items: center;
justify-content: space-between;
}
span {
margin-right: 16px;
}
`;
}
}
declare global {
interface HTMLElementTagNameMap {
"demo-automation-describe-trigger": DemoAutomationDescribeTrigger;
}
}

View File

@@ -1,87 +0,0 @@
import {
customElement,
html,
css,
LitElement,
TemplateResult,
property,
} from "lit-element";
import "../../../src/components/ha-card";
import "../../../src/components/trace/hat-script-graph";
import "../../../src/components/trace/hat-trace-timeline";
import { provideHass } from "../../../src/fake_data/provide_hass";
import { HomeAssistant } from "../../../src/types";
import { mockDemoTrace } from "../data/traces/mock-demo-trace";
import { DemoTrace } from "../data/traces/types";
const traces: DemoTrace[] = [
mockDemoTrace({ state: "running" }),
mockDemoTrace({ state: "debugged" }),
mockDemoTrace({ state: "stopped", script_execution: "failed_condition" }),
mockDemoTrace({ state: "stopped", script_execution: "failed_single" }),
mockDemoTrace({ state: "stopped", script_execution: "failed_max_runs" }),
mockDemoTrace({ state: "stopped", script_execution: "finished" }),
mockDemoTrace({ state: "stopped", script_execution: "aborted" }),
mockDemoTrace({
state: "stopped",
script_execution: "error",
error: 'Variable "beer" cannot be None',
}),
mockDemoTrace({ state: "stopped", script_execution: "cancelled" }),
];
@customElement("demo-automation-trace-timeline")
export class DemoAutomationTraceTimeline extends LitElement {
@property({ attribute: false }) hass?: HomeAssistant;
protected render(): TemplateResult {
if (!this.hass) {
return html``;
}
return html`
${traces.map(
(trace) => html`
<ha-card .header=${trace.trace.config.alias}>
<div class="card-content">
<hat-trace-timeline
.hass=${this.hass}
.trace=${trace.trace}
.logbookEntries=${trace.logbookEntries}
></hat-trace-timeline>
<button @click=${() => console.log(trace)}>Log trace</button>
</div>
</ha-card>
`
)}
`;
}
protected firstUpdated(changedProps) {
super.firstUpdated(changedProps);
const hass = provideHass(this);
hass.updateTranslations(null, "en");
}
static get styles() {
return css`
ha-card {
max-width: 600px;
margin: 24px;
}
.card-content {
display: flex;
}
button {
position: absolute;
top: 0;
right: 0;
}
`;
}
}
declare global {
interface HTMLElementTagNameMap {
"demo-automation-trace-timeline": DemoAutomationTraceTimeline;
}
}

View File

@@ -1,98 +0,0 @@
import {
customElement,
html,
css,
LitElement,
TemplateResult,
internalProperty,
property,
} from "lit-element";
import "../../../src/components/ha-card";
import "../../../src/components/trace/hat-script-graph";
import "../../../src/components/trace/hat-trace-timeline";
import { provideHass } from "../../../src/fake_data/provide_hass";
import { HomeAssistant } from "../../../src/types";
import { DemoTrace } from "../data/traces/types";
import { basicTrace } from "../data/traces/basic_trace";
import { motionLightTrace } from "../data/traces/motion-light-trace";
const traces: DemoTrace[] = [basicTrace, motionLightTrace];
@customElement("demo-automation-trace")
export class DemoAutomationTrace extends LitElement {
@property({ attribute: false }) hass?: HomeAssistant;
@internalProperty() private _selected = {};
protected render(): TemplateResult {
if (!this.hass) {
return html``;
}
return html`
${traces.map(
(trace, idx) => html`
<ha-card .header=${trace.trace.config.alias}>
<div class="card-content">
<hat-script-graph
.trace=${trace.trace}
.selected=${this._selected[idx]}
@graph-node-selected=${(ev) => {
this._selected = { ...this._selected, [idx]: ev.detail.path };
}}
></hat-script-graph>
<hat-trace-timeline
allowPick
.hass=${this.hass}
.trace=${trace.trace}
.logbookEntries=${trace.logbookEntries}
.selectedPath=${this._selected[idx]}
@value-changed=${(ev) => {
this._selected = {
...this._selected,
[idx]: ev.detail.value,
};
}}
></hat-trace-timeline>
<button @click=${() => console.log(trace)}>Log trace</button>
</div>
</ha-card>
`
)}
`;
}
protected firstUpdated(changedProps) {
super.firstUpdated(changedProps);
const hass = provideHass(this);
hass.updateTranslations(null, "en");
}
static get styles() {
return css`
ha-card {
max-width: 600px;
margin: 24px;
}
.card-content {
display: flex;
}
.card-content > * {
margin-right: 16px;
}
.card-content > *:last-child {
margin-right: 0;
}
button {
position: absolute;
top: 0;
right: 0;
}
`;
}
}
declare global {
interface HTMLElementTagNameMap {
"demo-automation-trace": DemoAutomationTrace;
}
}

View File

@@ -1,11 +1,6 @@
import { import { html } from "@polymer/polymer/lib/utils/html-tag";
customElement, /* eslint-plugin-disable lit */
html, import { PolymerElement } from "@polymer/polymer/polymer-element";
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { getEntity } from "../../../src/fake_data/entity"; import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards"; import "../components/demo-cards";
@@ -76,19 +71,35 @@ const CONFIGS = [
}, },
]; ];
@customElement("demo-hui-alarm-panel-card") class DemoAlarmPanelEntity extends PolymerElement {
class DemoAlarmPanelEntity extends LitElement { static get template() {
@query("#demos") private _demoRoot!: HTMLElement; return html`
<demo-cards
protected render(): TemplateResult { id="demos"
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`; hass="[[hass]]"
configs="[[_configs]]"
></demo-cards>
`;
} }
protected firstUpdated(changedProperties: PropertyValues) { static get properties() {
super.firstUpdated(changedProperties); return {
const hass = provideHass(this._demoRoot); _configs: {
hass.updateTranslations(null, "en"); type: Object,
hass.updateTranslations("lovelace", "en"); value: CONFIGS,
},
hass: Object,
};
}
public ready() {
super.ready();
this._setupDemo();
}
private async _setupDemo() {
const hass = provideHass(this.$.demos);
await hass.updateTranslations(null, "en");
hass.addEntities(ENTITIES); hass.addEntities(ENTITIES);
} }
} }

View File

@@ -1,11 +1,6 @@
import { import { html } from "@polymer/polymer/lib/utils/html-tag";
customElement, /* eslint-plugin-disable lit */
html, import { PolymerElement } from "@polymer/polymer/polymer-element";
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { getEntity } from "../../../src/fake_data/entity"; import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards"; import "../components/demo-cards";
@@ -58,19 +53,31 @@ const CONFIGS = [
}, },
]; ];
@customElement("demo-hui-conditional-card") class DemoConditional extends PolymerElement {
class DemoConditional extends LitElement { static get template() {
@query("#demos") private _demoRoot!: HTMLElement; return html`
<demo-cards
protected render(): TemplateResult { id="demos"
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`; hass="[[hass]]"
configs="[[_configs]]"
></demo-cards>
`;
} }
protected firstUpdated(changedProperties: PropertyValues) { static get properties() {
super.firstUpdated(changedProperties); return {
const hass = provideHass(this._demoRoot); _configs: {
type: Object,
value: CONFIGS,
},
hass: Object,
};
}
public ready() {
super.ready();
const hass = provideHass(this.$.demos);
hass.updateTranslations(null, "en"); hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES); hass.addEntities(ENTITIES);
} }
} }

View File

@@ -1,11 +1,6 @@
import { import { html } from "@polymer/polymer/lib/utils/html-tag";
customElement, /* eslint-plugin-disable lit */
html, import { PolymerElement } from "@polymer/polymer/polymer-element";
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { getEntity } from "../../../src/fake_data/entity"; import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards"; import "../components/demo-cards";
@@ -222,19 +217,24 @@ const CONFIGS = [
}, },
]; ];
@customElement("demo-hui-entities-card") class DemoEntities extends PolymerElement {
class DemoEntities extends LitElement { static get template() {
@query("#demos") private _demoRoot!: HTMLElement; return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
protected render(): TemplateResult {
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
} }
protected firstUpdated(changedProperties: PropertyValues) { static get properties() {
super.firstUpdated(changedProperties); return {
const hass = provideHass(this._demoRoot); _configs: {
type: Object,
value: CONFIGS,
},
};
}
public ready() {
super.ready();
const hass = provideHass(this.$.demos);
hass.updateTranslations(null, "en"); hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES); hass.addEntities(ENTITIES);
} }
} }

View File

@@ -1,11 +1,6 @@
import { import { html } from "@polymer/polymer/lib/utils/html-tag";
customElement, /* eslint-plugin-disable lit */
html, import { PolymerElement } from "@polymer/polymer/polymer-element";
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { getEntity } from "../../../src/fake_data/entity"; import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards"; import "../components/demo-cards";
@@ -25,10 +20,10 @@ const CONFIGS = [
`, `,
}, },
{ {
heading: "With Name (defined in card)", heading: "With Name",
config: ` config: `
- type: button - type: button
name: Custom Name name: Bedroom
entity: light.bed_light entity: light.bed_light
`, `,
}, },
@@ -37,7 +32,7 @@ const CONFIGS = [
config: ` config: `
- type: button - type: button
entity: light.bed_light entity: light.bed_light
icon: mdi:tools icon: mdi:hotel
`, `,
}, },
{ {
@@ -53,7 +48,7 @@ const CONFIGS = [
config: ` config: `
- type: button - type: button
entity: light.bed_light entity: light.bed_light
tap_action: tap_action:
action: toggle action: toggle
`, `,
}, },
@@ -74,19 +69,31 @@ const CONFIGS = [
}, },
]; ];
@customElement("demo-hui-entity-button-card") class DemoButtonEntity extends PolymerElement {
class DemoButtonEntity extends LitElement { static get template() {
@query("#demos") private _demoRoot!: HTMLElement; return html`
<demo-cards
protected render(): TemplateResult { id="demos"
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`; hass="[[hass]]"
configs="[[_configs]]"
></demo-cards>
`;
} }
protected firstUpdated(changedProperties: PropertyValues) { static get properties() {
super.firstUpdated(changedProperties); return {
const hass = provideHass(this._demoRoot); _configs: {
type: Object,
value: CONFIGS,
},
hass: Object,
};
}
public ready() {
super.ready();
const hass = provideHass(this.$.demos);
hass.updateTranslations(null, "en"); hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES); hass.addEntities(ENTITIES);
} }
} }

View File

@@ -1,11 +1,6 @@
import { import { html } from "@polymer/polymer/lib/utils/html-tag";
customElement, /* eslint-plugin-disable lit */
html, import { PolymerElement } from "@polymer/polymer/polymer-element";
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { getEntity } from "../../../src/fake_data/entity"; import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards"; import "../components/demo-cards";
@@ -48,7 +43,7 @@ const ENTITIES = [
const CONFIGS = [ const CONFIGS = [
{ {
heading: "Unfiltered controller", heading: "Controller",
config: ` config: `
- type: entities - type: entities
entities: entities:
@@ -58,7 +53,7 @@ const CONFIGS = [
`, `,
}, },
{ {
heading: "Filtered entities card", heading: "Basic",
config: ` config: `
- type: entity-filter - type: entity-filter
entities: entities:
@@ -74,27 +69,7 @@ const CONFIGS = [
`, `,
}, },
{ {
heading: 'With "entities" card config', heading: "With card config",
config: `
- type: entity-filter
entities:
- device_tracker.demo_anne_therese
- device_tracker.demo_home_boy
- device_tracker.demo_paulus
- light.bed_light
- light.ceiling_lights
- light.kitchen_lights
state_filter:
- "on"
- not_home
card:
type: entities
title: Custom Title
show_header_toggle: false
`,
},
{
heading: 'With "glance" card config',
config: ` config: `
- type: entity-filter - type: entity-filter
entities: entities:
@@ -109,27 +84,31 @@ const CONFIGS = [
- not_home - not_home
card: card:
type: glance type: glance
show_state: true show_state: false
title: Custom Title
`, `,
}, },
]; ];
@customElement("demo-hui-entity-filter-card") class DemoFilter extends PolymerElement {
class DemoEntityFilter extends LitElement { static get template() {
@query("#demos") private _demoRoot!: HTMLElement; return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
protected render(): TemplateResult {
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
} }
protected firstUpdated(changedProperties: PropertyValues) { static get properties() {
super.firstUpdated(changedProperties); return {
const hass = provideHass(this._demoRoot); _configs: {
type: Object,
value: CONFIGS,
},
};
}
public ready() {
super.ready();
const hass = provideHass(this.$.demos);
hass.updateTranslations(null, "en"); hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES); hass.addEntities(ENTITIES);
} }
} }
customElements.define("demo-hui-entity-filter-card", DemoEntityFilter); customElements.define("demo-hui-entity-filter-card", DemoFilter);

View File

@@ -1,11 +1,6 @@
import { import { html } from "@polymer/polymer/lib/utils/html-tag";
customElement, /* eslint-plugin-disable lit */
html, import { PolymerElement } from "@polymer/polymer/polymer-element";
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { getEntity } from "../../../src/fake_data/entity"; import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards"; import "../components/demo-cards";
@@ -112,19 +107,24 @@ const CONFIGS = [
}, },
]; ];
@customElement("demo-hui-gauge-card") class DemoGaugeEntity extends PolymerElement {
class DemoGaugeEntity extends LitElement { static get template() {
@query("#demos") private _demoRoot!: HTMLElement; return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
protected render(): TemplateResult {
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
} }
protected firstUpdated(changedProperties: PropertyValues) { static get properties() {
super.firstUpdated(changedProperties); return {
const hass = provideHass(this._demoRoot); _configs: {
type: Object,
value: CONFIGS,
},
};
}
public ready() {
super.ready();
const hass = provideHass(this.$.demos);
hass.updateTranslations(null, "en"); hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES); hass.addEntities(ENTITIES);
} }
} }

View File

@@ -1,11 +1,6 @@
import { import { html } from "@polymer/polymer/lib/utils/html-tag";
customElement, /* eslint-plugin-disable lit */
html, import { PolymerElement } from "@polymer/polymer/polymer-element";
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { getEntity } from "../../../src/fake_data/entity"; import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards"; import "../components/demo-cards";
@@ -82,8 +77,7 @@ const CONFIGS = [
heading: "With title", heading: "With title",
config: ` config: `
- type: glance - type: glance
title: Custom title title: This is glance
columns: 4
entities: entities:
- device_tracker.demo_paulus - device_tracker.demo_paulus
- media_player.living_room - media_player.living_room
@@ -110,10 +104,9 @@ const CONFIGS = [
`, `,
}, },
{ {
heading: "No entity names", heading: "No name",
config: ` config: `
- type: glance - type: glance
columns: 4
show_name: false show_name: false
entities: entities:
- device_tracker.demo_paulus - device_tracker.demo_paulus
@@ -126,10 +119,9 @@ const CONFIGS = [
`, `,
}, },
{ {
heading: "No state labels", heading: "No state",
config: ` config: `
- type: glance - type: glance
columns: 4
show_state: false show_state: false
entities: entities:
- device_tracker.demo_paulus - device_tracker.demo_paulus
@@ -142,10 +134,9 @@ const CONFIGS = [
`, `,
}, },
{ {
heading: "No names and no state labels", heading: "No name and no state",
config: ` config: `
- type: glance - type: glance
columns: 4
show_name: false show_name: false
show_state: false show_state: false
entities: entities:
@@ -159,24 +150,47 @@ const CONFIGS = [
`, `,
}, },
{ {
heading: "Custom name + custom icon", heading: "Custom name, custom icon",
config: ` config: `
- type: glance - type: glance
columns: 4
entities: entities:
- entity: device_tracker.demo_paulus - entity: device_tracker.demo_paulus
name: ¯\\_(ツ)_/¯ name: ¯\\_(ツ)_/¯
icon: mdi:home-assistant icon: mdi:home-assistant
- entity: media_player.living_room - media_player.living_room
name: ¯\\_(ツ)_/¯ - sun.sun
icon: mdi:home-assistant - cover.kitchen_window
- entity: light.kitchen_lights
icon: mdi:alarm-light
- lock.kitchen_door
- light.ceiling_lights
`,
},
{
heading: "Custom tap action",
config: `
- type: glance
entities:
- entity: lock.kitchen_door
tap_action:
type: toggle
- entity: light.ceiling_lights
tap_action:
action: call-service
service: light.turn_on
service_data:
entity_id: light.ceiling_lights
- device_tracker.demo_paulus
- media_player.living_room
- sun.sun
- cover.kitchen_window
- light.kitchen_lights
`, `,
}, },
{ {
heading: "Selectively hidden name", heading: "Selectively hidden name",
config: ` config: `
- type: glance - type: glance
columns: 4
entities: entities:
- device_tracker.demo_paulus - device_tracker.demo_paulus
- entity: media_player.living_room - entity: media_player.living_room
@@ -185,51 +199,45 @@ const CONFIGS = [
- entity: cover.kitchen_window - entity: cover.kitchen_window
name: name:
- light.kitchen_lights - light.kitchen_lights
- entity: lock.kitchen_door
name:
- light.ceiling_lights
`, `,
}, },
{ {
heading: "Custom tap action", heading: "Primary theme",
config: ` config: `
- type: glance - type: glance
columns: 4 theming: primary
entities: entities:
- entity: lock.kitchen_door - device_tracker.demo_paulus
name: Custom - media_player.living_room
tap_action: - sun.sun
type: toggle - cover.kitchen_window
- entity: light.ceiling_lights - light.kitchen_lights
name: Custom - lock.kitchen_door
tap_action: - light.ceiling_lights
action: call-service
service: light.turn_on
service_data:
entity_id: light.ceiling_lights
- entity: sun.sun
name: Regular
- entity: light.kitchen_lights
name: Regular
`, `,
}, },
]; ];
@customElement("demo-hui-glance-card") class DemoPicEntity extends PolymerElement {
class DemoGlanceEntity extends LitElement { static get template() {
@query("#demos") private _demoRoot!: HTMLElement; return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
protected render(): TemplateResult {
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
} }
protected firstUpdated(changedProperties: PropertyValues) { static get properties() {
super.firstUpdated(changedProperties); return {
const hass = provideHass(this._demoRoot); _configs: {
type: Object,
value: CONFIGS,
},
};
}
public ready() {
super.ready();
const hass = provideHass(this.$.demos);
hass.updateTranslations(null, "en"); hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES); hass.addEntities(ENTITIES);
} }
} }
customElements.define("demo-hui-glance-card", DemoGlanceEntity); customElements.define("demo-hui-glance-card", DemoPicEntity);

View File

@@ -1,4 +1,6 @@
import { customElement, html, LitElement, TemplateResult } from "lit-element"; import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element";
import "../components/demo-cards"; import "../components/demo-cards";
const CONFIGS = [ const CONFIGS = [
@@ -35,10 +37,18 @@ const CONFIGS = [
}, },
]; ];
@customElement("demo-hui-iframe-card") class DemoIframe extends PolymerElement {
class DemoIframe extends LitElement { static get template() {
protected render(): TemplateResult { return html` <demo-cards configs="[[_configs]]"></demo-cards> `;
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`; }
static get properties() {
return {
_configs: {
type: Object,
value: CONFIGS,
},
};
} }
} }

View File

@@ -1,11 +1,6 @@
import { import { html } from "@polymer/polymer/lib/utils/html-tag";
customElement, /* eslint-plugin-disable lit */
html, import { PolymerElement } from "@polymer/polymer/polymer-element";
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { getEntity } from "../../../src/fake_data/entity"; import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards"; import "../components/demo-cards";
@@ -68,19 +63,24 @@ const CONFIGS = [
}, },
]; ];
@customElement("demo-hui-light-card") class DemoLightEntity extends PolymerElement {
class DemoLightEntity extends LitElement { static get template() {
@query("#demos") private _demoRoot!: HTMLElement; return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
protected render(): TemplateResult {
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
} }
protected firstUpdated(changedProperties: PropertyValues) { static get properties() {
super.firstUpdated(changedProperties); return {
const hass = provideHass(this._demoRoot); _configs: {
type: Object,
value: CONFIGS,
},
};
}
public ready() {
super.ready();
const hass = provideHass(this.$.demos);
hass.updateTranslations(null, "en"); hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES); hass.addEntities(ENTITIES);
} }
} }

View File

@@ -1,11 +1,6 @@
import { import { html } from "@polymer/polymer/lib/utils/html-tag";
customElement, /* eslint-plugin-disable lit */
html, import { PolymerElement } from "@polymer/polymer/polymer-element";
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { getEntity } from "../../../src/fake_data/entity"; import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards"; import "../components/demo-cards";
@@ -166,19 +161,31 @@ const CONFIGS = [
}, },
]; ];
@customElement("demo-hui-map-card") class DemoMap extends PolymerElement {
class DemoMap extends LitElement { static get template() {
@query("#demos") private _demoRoot!: HTMLElement; return html`
<demo-cards
protected render(): TemplateResult { id="demos"
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`; hass="[[hass]]"
configs="[[_configs]]"
></demo-cards>
`;
} }
protected firstUpdated(changedProperties: PropertyValues) { static get properties() {
super.firstUpdated(changedProperties); return {
const hass = provideHass(this._demoRoot); _configs: {
type: Object,
value: CONFIGS,
},
hass: Object,
};
}
public ready() {
super.ready();
const hass = provideHass(this.$.demos);
hass.updateTranslations(null, "en"); hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES); hass.addEntities(ENTITIES);
} }
} }

View File

@@ -1,11 +1,6 @@
import { import { html } from "@polymer/polymer/lib/utils/html-tag";
customElement, /* eslint-plugin-disable lit */
html, import { PolymerElement } from "@polymer/polymer/polymer-element";
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { mockTemplate } from "../../../demo/src/stubs/template"; import { mockTemplate } from "../../../demo/src/stubs/template";
import { provideHass } from "../../../src/fake_data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards"; import "../components/demo-cards";
@@ -259,19 +254,23 @@ const CONFIGS = [
}, },
]; ];
@customElement("demo-hui-markdown-card") class DemoMarkdown extends PolymerElement {
class DemoMarkdown extends LitElement { static get template() {
@query("#demos") private _demoRoot!: HTMLElement; return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
protected render(): TemplateResult {
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
} }
protected firstUpdated(changedProperties: PropertyValues) { static get properties() {
super.firstUpdated(changedProperties); return {
const hass = provideHass(this._demoRoot); _configs: {
hass.updateTranslations(null, "en"); type: Object,
hass.updateTranslations("lovelace", "en"); value: CONFIGS,
},
};
}
public ready() {
super.ready();
const hass = provideHass(this.$.demos);
mockTemplate(hass); mockTemplate(hass);
} }
} }

View File

@@ -1,11 +1,6 @@
import { import { html } from "@polymer/polymer/lib/utils/html-tag";
customElement, /* eslint-plugin-disable lit */
html, import { PolymerElement } from "@polymer/polymer/polymer-element";
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { provideHass } from "../../../src/fake_data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards"; import "../components/demo-cards";
import { createMediaPlayerEntities } from "../data/media_players"; import { createMediaPlayerEntities } from "../data/media_players";
@@ -151,33 +146,35 @@ const CONFIGS = [
entity: media_player.receiver_off entity: media_player.receiver_off
`, `,
}, },
{
heading: "Grid Full Size",
config: `
- type: grid
columns: 1
cards:
- type: media-control
entity: media_player.music_paused
`,
},
]; ];
@customElement("demo-hui-media-control-card") class DemoHuiMediControlCard extends PolymerElement {
class DemoHuiMediaControlCard extends LitElement { static get template() {
@query("#demos") private _demoRoot!: HTMLElement; return html`
<demo-cards
protected render(): TemplateResult { id="demos"
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`; hass="[[hass]]"
configs="[[_configs]]"
></demo-cards>
`;
} }
protected firstUpdated(changedProperties: PropertyValues) { static get properties() {
super.firstUpdated(changedProperties); return {
const hass = provideHass(this._demoRoot); _configs: {
type: Object,
value: CONFIGS,
},
hass: Object,
};
}
public ready() {
super.ready();
const hass = provideHass(this.$.demos);
hass.updateTranslations(null, "en"); hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(createMediaPlayerEntities()); hass.addEntities(createMediaPlayerEntities());
} }
} }
customElements.define("demo-hui-media-control-card", DemoHuiMediaControlCard); customElements.define("demo-hui-media-control-card", DemoHuiMediControlCard);

View File

@@ -1,11 +1,6 @@
import { import { html } from "@polymer/polymer/lib/utils/html-tag";
customElement, /* eslint-plugin-disable lit */
html, import { PolymerElement } from "@polymer/polymer/polymer-element";
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { provideHass } from "../../../src/fake_data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards"; import "../components/demo-cards";
import { createMediaPlayerEntities } from "../data/media_players"; import { createMediaPlayerEntities } from "../data/media_players";
@@ -31,9 +26,9 @@ const CONFIGS = [
- entity: media_player.android_cast - entity: media_player.android_cast
name: Screen casting name: Screen casting
- entity: media_player.image_display - entity: media_player.image_display
name: Digital Picture Frame name: Digital Picture Frame
- entity: media_player.sonos_idle - entity: media_player.sonos_idle
name: Sonos Idle name: Sonos Idle
- entity: media_player.idle_browse_media - entity: media_player.idle_browse_media
name: Idle waiting for Browse Media name: Idle waiting for Browse Media
- entity: media_player.theater_off - entity: media_player.theater_off
@@ -43,7 +38,7 @@ const CONFIGS = [
- entity: media_player.theater_off_static - entity: media_player.theater_off_static
name: Player Off (cannot be switched on) name: Player Off (cannot be switched on)
- entity: media_player.theater_on_static - entity: media_player.theater_on_static
name: Player On (cannot be switched off) name: Player On (cannot be switched off)
- entity: media_player.idle - entity: media_player.idle
name: Player Idle name: Player Idle
- entity: media_player.playing - entity: media_player.playing
@@ -60,21 +55,33 @@ const CONFIGS = [
}, },
]; ];
@customElement("demo-hui-media-player-row") class DemoHuiMediaPlayerRows extends PolymerElement {
class DemoHuiMediaPlayerRow extends LitElement { static get template() {
@query("#demos") private _demoRoot!: HTMLElement; return html`
<demo-cards
protected render(): TemplateResult { id="demos"
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`; hass="[[hass]]"
configs="[[_configs]]"
></demo-cards>
`;
} }
protected firstUpdated(changedProperties: PropertyValues) { static get properties() {
super.firstUpdated(changedProperties); return {
const hass = provideHass(this._demoRoot); _configs: {
type: Object,
value: CONFIGS,
},
hass: Object,
};
}
public ready() {
super.ready();
const hass = provideHass(this.$.demos);
hass.updateTranslations(null, "en"); hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(createMediaPlayerEntities()); hass.addEntities(createMediaPlayerEntities());
} }
} }
customElements.define("demo-hui-media-player-row", DemoHuiMediaPlayerRow); customElements.define("demo-hui-media-player-rows", DemoHuiMediaPlayerRows);

View File

@@ -1,11 +1,6 @@
import { import { html } from "@polymer/polymer/lib/utils/html-tag";
customElement, /* eslint-plugin-disable lit */
html, import { PolymerElement } from "@polymer/polymer/polymer-element";
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { getEntity } from "../../../src/fake_data/entity"; import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards"; import "../components/demo-cards";
@@ -130,21 +125,26 @@ const CONFIGS = [
}, },
]; ];
@customElement("demo-hui-picture-elements-card") class DemoPicElements extends PolymerElement {
class DemoPictureElements extends LitElement { static get template() {
@query("#demos") private _demoRoot!: HTMLElement; return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
protected render(): TemplateResult {
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
} }
protected firstUpdated(changedProperties: PropertyValues) { static get properties() {
super.firstUpdated(changedProperties); return {
const hass = provideHass(this._demoRoot); _configs: {
type: Object,
value: CONFIGS,
},
};
}
public ready() {
super.ready();
const hass = provideHass(this.$.demos);
hass.updateTranslations(null, "en"); hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES); hass.addEntities(ENTITIES);
} }
} }
customElements.define("demo-hui-picture-elements-card", DemoPictureElements); customElements.define("demo-hui-picture-elements-card", DemoPicElements);

View File

@@ -1,11 +1,6 @@
import { import { html } from "@polymer/polymer/lib/utils/html-tag";
customElement, /* eslint-plugin-disable lit */
html, import { PolymerElement } from "@polymer/polymer/polymer-element";
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { getEntity } from "../../../src/fake_data/entity"; import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards"; import "../components/demo-cards";
@@ -85,21 +80,26 @@ const CONFIGS = [
}, },
]; ];
@customElement("demo-hui-picture-entity-card") class DemoPicEntity extends PolymerElement {
class DemoPictureEntity extends LitElement { static get template() {
@query("#demos") private _demoRoot!: HTMLElement; return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
protected render(): TemplateResult {
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
} }
protected firstUpdated(changedProperties: PropertyValues) { static get properties() {
super.firstUpdated(changedProperties); return {
const hass = provideHass(this._demoRoot); _configs: {
type: Object,
value: CONFIGS,
},
};
}
public ready() {
super.ready();
const hass = provideHass(this.$.demos);
hass.updateTranslations(null, "en"); hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES); hass.addEntities(ENTITIES);
} }
} }
customElements.define("demo-hui-picture-entity-card", DemoPictureEntity); customElements.define("demo-hui-picture-entity-card", DemoPicEntity);

View File

@@ -1,11 +1,6 @@
import { import { html } from "@polymer/polymer/lib/utils/html-tag";
customElement, /* eslint-plugin-disable lit */
html, import { PolymerElement } from "@polymer/polymer/polymer-element";
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { getEntity } from "../../../src/fake_data/entity"; import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards"; import "../components/demo-cards";
@@ -126,21 +121,26 @@ const CONFIGS = [
}, },
]; ];
@customElement("demo-hui-picture-glance-card") class DemoPicGlance extends PolymerElement {
class DemoPictureGlance extends LitElement { static get template() {
@query("#demos") private _demoRoot!: HTMLElement; return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
protected render(): TemplateResult {
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
} }
protected firstUpdated(changedProperties: PropertyValues) { static get properties() {
super.firstUpdated(changedProperties); return {
const hass = provideHass(this._demoRoot); _configs: {
type: Object,
value: CONFIGS,
},
};
}
public ready() {
super.ready();
const hass = provideHass(this.$.demos);
hass.updateTranslations(null, "en"); hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES); hass.addEntities(ENTITIES);
} }
} }
customElements.define("demo-hui-picture-glance-card", DemoPictureGlance); customElements.define("demo-hui-picture-glance-card", DemoPicGlance);

View File

@@ -1,55 +0,0 @@
import {
customElement,
html,
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
import { createPlantEntities } from "../data/plants";
const CONFIGS = [
{
heading: "Basic example",
config: `
- type: plant-status
entity: plant.lemon_tree
`,
},
{
heading: "Problem (too bright) + low battery",
config: `
- type: plant-status
entity: plant.apple_tree
`,
},
{
heading: "With picture + multiple problems",
config: `
- type: plant-status
entity: plant.sunflowers
name: Sunflowers Name Overwrite
`,
},
];
@customElement("demo-hui-plant-card")
export class DemoPlantEntity extends LitElement {
@query("#demos") private _demoRoot!: HTMLElement;
protected render(): TemplateResult {
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
}
protected firstUpdated(changedProperties: PropertyValues) {
super.firstUpdated(changedProperties);
const hass = provideHass(this._demoRoot);
hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(createPlantEntities());
}
}
customElements.define("demo-hui-plant-card", DemoPlantEntity);

View File

@@ -1,11 +1,6 @@
import { import { html } from "@polymer/polymer/lib/utils/html-tag";
customElement, /* eslint-plugin-disable lit */
html, import { PolymerElement } from "@polymer/polymer/polymer-element";
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { provideHass } from "../../../src/fake_data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards"; import "../components/demo-cards";
@@ -25,19 +20,24 @@ const CONFIGS = [
}, },
]; ];
@customElement("demo-hui-shopping-list-card") class DemoShoppingListEntity extends PolymerElement {
class DemoShoppingListEntity extends LitElement { static get template() {
@query("#demos") private _demoRoot!: HTMLElement; return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
protected render(): TemplateResult {
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
} }
protected firstUpdated(changedProperties: PropertyValues) { static get properties() {
super.firstUpdated(changedProperties); return {
const hass = provideHass(this._demoRoot); _configs: {
type: Object,
value: CONFIGS,
},
};
}
public ready() {
super.ready();
const hass = provideHass(this.$.demos);
hass.updateTranslations(null, "en"); hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.mockAPI("shopping_list", () => [ hass.mockAPI("shopping_list", () => [
{ name: "list", id: 1, complete: false }, { name: "list", id: 1, complete: false },

View File

@@ -1,11 +1,6 @@
import { import { html } from "@polymer/polymer/lib/utils/html-tag";
customElement, /* eslint-plugin-disable lit */
html, import { PolymerElement } from "@polymer/polymer/polymer-element";
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { mockHistory } from "../../../demo/src/stubs/history"; import { mockHistory } from "../../../demo/src/stubs/history";
import { getEntity } from "../../../src/fake_data/entity"; import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
@@ -137,19 +132,24 @@ const CONFIGS = [
}, },
]; ];
@customElement("demo-hui-stack-card") class DemoStack extends PolymerElement {
class DemoStack extends LitElement { static get template() {
@query("#demos") private _demoRoot!: HTMLElement; return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
protected render(): TemplateResult {
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
} }
protected firstUpdated(changedProperties: PropertyValues) { static get properties() {
super.firstUpdated(changedProperties); return {
const hass = provideHass(this._demoRoot); _configs: {
type: Object,
value: CONFIGS,
},
};
}
public ready() {
super.ready();
const hass = provideHass(this.$.demos);
hass.updateTranslations(null, "en"); hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES); hass.addEntities(ENTITIES);
mockHistory(hass); mockHistory(hass);
} }

View File

@@ -1,11 +1,6 @@
import { import { html } from "@polymer/polymer/lib/utils/html-tag";
customElement, /* eslint-plugin-disable lit */
html, import { PolymerElement } from "@polymer/polymer/polymer-element";
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { getEntity } from "../../../src/fake_data/entity"; import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards"; import "../components/demo-cards";
@@ -79,19 +74,24 @@ const CONFIGS = [
}, },
]; ];
@customElement("demo-hui-thermostat-card") class DemoThermostatEntity extends PolymerElement {
class DemoThermostatEntity extends LitElement { static get template() {
@query("#demos") private _demoRoot!: HTMLElement; return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
protected render(): TemplateResult {
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
} }
protected firstUpdated(changedProperties: PropertyValues) { static get properties() {
super.firstUpdated(changedProperties); return {
const hass = provideHass(this._demoRoot); _configs: {
type: Object,
value: CONFIGS,
},
};
}
public ready() {
super.ready();
const hass = provideHass(this.$.demos);
hass.updateTranslations(null, "en"); hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES); hass.addEntities(ENTITIES);
} }
} }

View File

@@ -1,29 +1,12 @@
import { import { html } from "@polymer/polymer/lib/utils/html-tag";
customElement, /* eslint-plugin-disable lit */
html, import { PolymerElement } from "@polymer/polymer/polymer-element";
LitElement,
property,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import "../../../src/components/ha-card"; import "../../../src/components/ha-card";
import { import { SUPPORT_BRIGHTNESS } from "../../../src/data/light";
SUPPORT_BRIGHTNESS,
SUPPORT_COLOR,
SUPPORT_COLOR_TEMP,
SUPPORT_EFFECT,
SUPPORT_FLASH,
SUPPORT_TRANSITION,
SUPPORT_WHITE_VALUE,
} from "../../../src/data/light";
import "../../../src/dialogs/more-info/more-info-content";
import { getEntity } from "../../../src/fake_data/entity"; import { getEntity } from "../../../src/fake_data/entity";
import { import { provideHass } from "../../../src/fake_data/provide_hass";
MockHomeAssistant,
provideHass,
} from "../../../src/fake_data/provide_hass";
import "../components/demo-more-infos"; import "../components/demo-more-infos";
import "../../../src/dialogs/more-info/more-info-content";
const ENTITIES = [ const ENTITIES = [
getEntity("light", "bed_light", "on", { getEntity("light", "bed_light", "on", {
@@ -31,58 +14,40 @@ const ENTITIES = [
}), }),
getEntity("light", "kitchen_light", "on", { getEntity("light", "kitchen_light", "on", {
friendly_name: "Brightness Light", friendly_name: "Brightness Light",
brightness: 200, brightness: 80,
supported_features: SUPPORT_BRIGHTNESS, supported_features: SUPPORT_BRIGHTNESS,
}), }),
getEntity("light", "color_temperature_light", "on", {
friendly_name: "White Color Temperature Light",
brightness: 128,
color_temp: 75,
min_mireds: 30,
max_mireds: 150,
supported_features: SUPPORT_BRIGHTNESS + SUPPORT_COLOR_TEMP,
}),
getEntity("light", "color_effectslight", "on", {
friendly_name: "Color Effets Light",
brightness: 255,
hs_color: [30, 100],
white_value: 36,
supported_features:
SUPPORT_BRIGHTNESS +
SUPPORT_EFFECT +
SUPPORT_FLASH +
SUPPORT_COLOR +
SUPPORT_TRANSITION +
SUPPORT_WHITE_VALUE,
effect_list: ["random", "colorloop"],
}),
]; ];
@customElement("demo-more-info-light") class DemoMoreInfoLight extends PolymerElement {
class DemoMoreInfoLight extends LitElement { static get template() {
@property() public hass!: MockHomeAssistant;
@query("demo-more-infos") private _demoRoot!: HTMLElement;
protected render(): TemplateResult {
return html` return html`
<demo-more-infos <demo-more-infos
.hass=${this.hass} hass="[[hass]]"
.entities=${ENTITIES.map((ent) => ent.entityId)} entities="[[_entities]]"
></demo-more-infos> ></demo-more-infos>
`; `;
} }
protected firstUpdated(changedProperties: PropertyValues) { static get properties() {
super.firstUpdated(changedProperties); return {
const hass = provideHass(this._demoRoot); _entities: {
hass.updateTranslations(null, "en"); type: Array,
value: ENTITIES.map((ent) => ent.entityId),
},
};
}
public ready() {
super.ready();
this._setupDemo();
}
private async _setupDemo() {
const hass = provideHass(this);
await hass.updateTranslations(null, "en");
hass.addEntities(ENTITIES); hass.addEntities(ENTITIES);
} }
} }
declare global { customElements.define("demo-more-info-light", DemoMoreInfoLight);
interface HTMLElementTagNameMap {
"demo-more-info-light": DemoMoreInfoLight;
}
}

View File

@@ -1,10 +1,9 @@
import "@material/mwc-button"; import "@material/mwc-button";
import { customElement, html, LitElement, TemplateResult } from "lit-element"; import { html, LitElement, TemplateResult } from "lit-element";
import "../../../src/components/ha-card"; import "../../../src/components/ha-card";
import { ActionHandlerEvent } from "../../../src/data/lovelace"; import { ActionHandlerEvent } from "../../../src/data/lovelace";
import { actionHandler } from "../../../src/panels/lovelace/common/directives/action-handler-directive"; import { actionHandler } from "../../../src/panels/lovelace/common/directives/action-handler-directive";
@customElement("demo-util-long-press")
export class DemoUtilLongPress extends LitElement { export class DemoUtilLongPress extends LitElement {
protected render(): TemplateResult { protected render(): TemplateResult {
return html` return html`
@@ -21,7 +20,7 @@ export class DemoUtilLongPress extends LitElement {
<textarea></textarea> <textarea></textarea>
<div>Try pressing and scrolling too!</div> <div>(try pressing and scrolling too!)</div>
</ha-card> </ha-card>
` `
)} )}
@@ -63,3 +62,5 @@ export class DemoUtilLongPress extends LitElement {
`; `;
} }
} }
customElements.define("demo-util-long-press", DemoUtilLongPress);

View File

@@ -14,51 +14,54 @@ import "../../src/styles/polymer-ha-style";
// eslint-disable-next-line import/extensions // eslint-disable-next-line import/extensions
import { DEMOS } from "../build/import-demos"; import { DEMOS } from "../build/import-demos";
const fixPath = (path) => path.substr(2, path.length - 5);
class HaGallery extends PolymerElement { class HaGallery extends PolymerElement {
static get template() { static get template() {
return html` return html`
<style include="iron-positioning ha-style"> <style include="iron-positioning ha-style">
:host { :host {
-ms-user-select: initial; -ms-user-select: initial;
-webkit-user-select: initial; -webkit-user-select: initial;
-moz-user-select: initial; -moz-user-select: initial;
} }
app-header-layout { app-header-layout {
min-height: 100vh; min-height: 100vh;
} }
ha-icon-button.invisible { ha-icon-button.invisible {
visibility: hidden; visibility: hidden;
} }
.pickers { .pickers {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
justify-content: center; justify-content: center;
align-items: start; align-items: start;
} }
.pickers ha-card { .pickers ha-card {
width: 400px; width: 400px;
display: block; display: block;
margin: 16px 8px; margin: 16px 8px;
} }
.pickers ha-card:last-child { .pickers ha-card:last-child {
margin-bottom: 16px; margin-bottom: 16px;
} }
.intro { .intro {
margin: -1em 0; margin: -1em 0;
} }
p a { p a {
color: var(--primary-color); color: var(--primary-color);
} }
a {
color: var(--primary-text-color);
text-decoration: none;
}
a {
color: var(--primary-text-color);
text-decoration: none;
}
</style> </style>
<app-header-layout> <app-header-layout>
@@ -67,42 +70,32 @@ class HaGallery extends PolymerElement {
<ha-icon-button <ha-icon-button
icon="hass:arrow-left" icon="hass:arrow-left"
on-click="_backTapped" on-click="_backTapped"
class$="[[_computeHeaderButtonClass(_demo)]]" class$='[[_computeHeaderButtonClass(_demo)]]'
></ha-icon-button> ></ha-icon-button>
<div main-title> <div main-title>[[_withDefault(_demo, "Home Assistant Gallery")]]</div>
[[_withDefault(_demo, "Home Assistant Gallery")]]
</div>
</app-toolbar> </app-toolbar>
</app-header> </app-header>
<div class="content"> <div class='content'>
<div id="demo"></div> <div id='demo'></div>
<template is="dom-if" if="[[!_demo]]"> <template is='dom-if' if='[[!_demo]]'>
<div class="pickers"> <div class='pickers'>
<ha-card header="Lovelace Card Demos"> <ha-card header="Lovelace card demos">
<div class="card-content intro"> <div class='card-content intro'>
<p> <p>
Lovelace has many different cards. Each card allows the user Lovelace has many different cards. Each card allows the user to tell a different story about what is going on in their house. These cards are very customizable, as no household is the same.
to tell a different story about what is going on in their
house. These cards are very customizable, as no household is
the same.
</p> </p>
<p> <p>
This gallery helps our developers and designers to see all This gallery helps our developers and designers to see all the different states that each card can be in.
the different states that each card can be in.
</p> </p>
<p> <p>
Check Check <a href='https://www.home-assistant.io/lovelace'>the official website</a> for instructions on how to get started with Lovelace.</a>.
<a href="https://www.home-assistant.io/lovelace"
>the official website</a
>
for instructions on how to get started with Lovelace.
</p> </p>
</div> </div>
<template is="dom-repeat" items="[[_lovelaceDemos]]"> <template is='dom-repeat' items='[[_lovelaceDemos]]'>
<a href="#[[item]]"> <a href='#[[item]]'>
<paper-item> <paper-item>
<paper-item-body>{{ item }}</paper-item-body> <paper-item-body>{{ item }}</paper-item-body>
<ha-icon icon="hass:chevron-right"></ha-icon> <ha-icon icon="hass:chevron-right"></ha-icon>
@@ -111,10 +104,30 @@ class HaGallery extends PolymerElement {
</template> </template>
</ha-card> </ha-card>
<ha-card header="Other Demos"> <ha-card header="More Info demos">
<div class="card-content intro"></div> <div class='card-content intro'>
<template is="dom-repeat" items="[[_restDemos]]"> <p>
<a href="#[[item]]"> More info screens show up when an entity is clicked.
</p>
</div>
<template is='dom-repeat' items='[[_moreInfoDemos]]'>
<a href='#[[item]]'>
<paper-item>
<paper-item-body>{{ item }}</paper-item-body>
<ha-icon icon="hass:chevron-right"></ha-icon>
</paper-item>
</a>
</template>
</ha-card>
<ha-card header="Util demos">
<div class='card-content intro'>
<p>
Test pages for our utility functions.
</p>
</div>
<template is='dom-repeat' items='[[_utilDemos]]'>
<a href='#[[item]]'>
<paper-item> <paper-item>
<paper-item-body>{{ item }}</paper-item-body> <paper-item-body>{{ item }}</paper-item-body>
<ha-icon icon="hass:chevron-right"></ha-icon> <ha-icon icon="hass:chevron-right"></ha-icon>
@@ -126,10 +139,7 @@ class HaGallery extends PolymerElement {
</template> </template>
</div> </div>
</app-header-layout> </app-header-layout>
<notification-manager <notification-manager hass=[[_fakeHass]] id='notifications'></notification-manager>
hass="[[_fakeHass]]"
id="notifications"
></notification-manager>
`; `;
} }
@@ -158,9 +168,13 @@ class HaGallery extends PolymerElement {
type: Array, type: Array,
computed: "_computeLovelace(_demos)", computed: "_computeLovelace(_demos)",
}, },
_restDemos: { _moreInfoDemos: {
type: Array, type: Array,
computed: "_computeRest(_demos)", computed: "_computeMoreInfos(_demos)",
},
_utilDemos: {
type: Array,
computed: "_computeUtil(_demos)",
}, },
}; };
} }
@@ -213,8 +227,12 @@ class HaGallery extends PolymerElement {
return demos.filter((demo) => demo.includes("hui")); return demos.filter((demo) => demo.includes("hui"));
} }
_computeRest(demos) { _computeMoreInfos(demos) {
return demos.filter((demo) => !demo.includes("hui")); return demos.filter((demo) => demo.includes("more-info"));
}
_computeUtil(demos) {
return demos.filter((demo) => demo.includes("util"));
} }
} }

View File

@@ -15,7 +15,6 @@ import {
HassioAddonInfo, HassioAddonInfo,
HassioAddonRepository, HassioAddonRepository,
} from "../../../src/data/hassio/addon"; } from "../../../src/data/hassio/addon";
import { Supervisor } from "../../../src/data/supervisor/supervisor";
import { HomeAssistant } from "../../../src/types"; import { HomeAssistant } from "../../../src/types";
import "../components/hassio-card-content"; import "../components/hassio-card-content";
import { filterAndSort } from "../components/hassio-filter-addons"; import { filterAndSort } from "../components/hassio-filter-addons";
@@ -24,8 +23,6 @@ import { hassioStyle } from "../resources/hassio-style";
class HassioAddonRepositoryEl extends LitElement { class HassioAddonRepositoryEl extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant; @property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public supervisor!: Supervisor;
@property({ attribute: false }) public repo!: HassioAddonRepository; @property({ attribute: false }) public repo!: HassioAddonRepository;
@property({ attribute: false }) public addons!: HassioAddonInfo[]; @property({ attribute: false }) public addons!: HassioAddonInfo[];
@@ -57,11 +54,7 @@ class HassioAddonRepositoryEl extends LitElement {
return html` return html`
<div class="content"> <div class="content">
<p class="description"> <p class="description">
${this.supervisor.localize( No results found in "${repo.name}."
"store.no_results_found",
"repository",
repo.name
)}
</p> </p>
</div> </div>
`; `;
@@ -90,13 +83,11 @@ class HassioAddonRepositoryEl extends LitElement {
: mdiPuzzle} : mdiPuzzle}
.iconTitle=${addon.installed .iconTitle=${addon.installed
? addon.update_available ? addon.update_available
? this.supervisor.localize( ? "New version available"
"common.new_version_available" : "Add-on is installed"
)
: this.supervisor.localize("addon.installed")
: addon.available : addon.available
? this.supervisor.localize("addon.not_installed") ? "Add-on is not installed"
: this.supervisor.localize("addon.not_available")} : "Add-on is not available on your system"}
.iconClass=${addon.installed .iconClass=${addon.installed
? addon.update_available ? addon.update_available
? "update" ? "update"

View File

@@ -11,20 +11,17 @@ import {
PropertyValues, PropertyValues,
} from "lit-element"; } from "lit-element";
import { html, TemplateResult } from "lit-html"; import { html, TemplateResult } from "lit-html";
import memoizeOne from "memoize-one";
import { atLeastVersion } from "../../../src/common/config/version"; import { atLeastVersion } from "../../../src/common/config/version";
import { fireEvent } from "../../../src/common/dom/fire_event";
import { navigate } from "../../../src/common/navigate";
import "../../../src/common/search/search-input"; import "../../../src/common/search/search-input";
import { extractSearchParam } from "../../../src/common/url/search-params";
import "../../../src/components/ha-button-menu"; import "../../../src/components/ha-button-menu";
import "../../../src/components/ha-svg-icon"; import "../../../src/components/ha-svg-icon";
import { import {
fetchHassioAddonsInfo,
HassioAddonInfo, HassioAddonInfo,
HassioAddonRepository, HassioAddonRepository,
reloadHassioAddons, reloadHassioAddons,
} from "../../../src/data/hassio/addon"; } from "../../../src/data/hassio/addon";
import { Supervisor } from "../../../src/data/supervisor/supervisor"; import { extractApiErrorMessage } from "../../../src/data/hassio/common";
import "../../../src/layouts/hass-loading-screen"; import "../../../src/layouts/hass-loading-screen";
import "../../../src/layouts/hass-tabs-subpage"; import "../../../src/layouts/hass-tabs-subpage";
import { HomeAssistant, Route } from "../../../src/types"; import { HomeAssistant, Route } from "../../../src/types";
@@ -52,43 +49,58 @@ const sortRepos = (a: HassioAddonRepository, b: HassioAddonRepository) => {
class HassioAddonStore extends LitElement { class HassioAddonStore extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant; @property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public supervisor!: Supervisor;
@property({ type: Boolean }) public narrow!: boolean; @property({ type: Boolean }) public narrow!: boolean;
@property({ attribute: false }) public route!: Route; @property({ attribute: false }) public route!: Route;
@property({ attribute: false }) private _addons?: HassioAddonInfo[];
@property({ attribute: false }) private _repos?: HassioAddonRepository[];
@internalProperty() private _filter?: string; @internalProperty() private _filter?: string;
public async refreshData() { public async refreshData() {
this._repos = undefined;
this._addons = undefined;
this._filter = undefined;
await reloadHassioAddons(this.hass); await reloadHassioAddons(this.hass);
await this._loadData(); await this._loadData();
} }
protected render(): TemplateResult { protected render(): TemplateResult {
let repos: TemplateResult[] = []; const repos: TemplateResult[] = [];
if (this.supervisor.addon.repositories) { if (this._repos) {
repos = this.addonRepositories( for (const repo of this._repos) {
this.supervisor.addon.repositories, const addons = this._addons!.filter(
this.supervisor.addon.addons, (addon) => addon.repository === repo.slug
this._filter );
);
if (addons.length === 0) {
continue;
}
repos.push(html`
<hassio-addon-repository
.hass=${this.hass}
.repo=${repo}
.addons=${addons}
.filter=${this._filter!}
></hassio-addon-repository>
`);
}
} }
return html` return html`
<hass-tabs-subpage <hass-tabs-subpage
.hass=${this.hass} .hass=${this.hass}
.localizeFunc=${this.supervisor.localize}
.narrow=${this.narrow} .narrow=${this.narrow}
.route=${this.route} .route=${this.route}
.tabs=${supervisorTabs} hassio
main-page main-page
supervisor .tabs=${supervisorTabs}
> >
<span slot="header"> <span slot="header">Add-on Store</span>
${this.supervisor.localize("panel.store")}
</span>
<ha-button-menu <ha-button-menu
corner="BOTTOM_START" corner="BOTTOM_START"
slot="toolbar-icon" slot="toolbar-icon"
@@ -98,15 +110,15 @@ class HassioAddonStore extends LitElement {
<ha-svg-icon .path=${mdiDotsVertical}></ha-svg-icon> <ha-svg-icon .path=${mdiDotsVertical}></ha-svg-icon>
</mwc-icon-button> </mwc-icon-button>
<mwc-list-item> <mwc-list-item>
${this.supervisor.localize("store.repositories")} Repositories
</mwc-list-item> </mwc-list-item>
<mwc-list-item> <mwc-list-item>
${this.supervisor.localize("common.reload")} Reload
</mwc-list-item> </mwc-list-item>
${this.hass.userData?.showAdvanced && ${this.hass.userData?.showAdvanced &&
atLeastVersion(this.hass.config.version, 0, 117) atLeastVersion(this.hass.config.version, 0, 117)
? html`<mwc-list-item> ? html`<mwc-list-item>
${this.supervisor.localize("store.registries")} Registries
</mwc-list-item>` </mwc-list-item>`
: ""} : ""}
</ha-button-menu> </ha-button-menu>
@@ -127,9 +139,11 @@ class HassioAddonStore extends LitElement {
${!this.hass.userData?.showAdvanced ${!this.hass.userData?.showAdvanced
? html` ? html`
<div class="advanced"> <div class="advanced">
Missing add-ons? Enable advanced mode on
<a href="/profile" target="_top"> <a href="/profile" target="_top">
${this.supervisor.localize("store.missing_addons")} your profile page
</a> </a>
.
</div> </div>
` `
: ""} : ""}
@@ -139,46 +153,14 @@ class HassioAddonStore extends LitElement {
protected firstUpdated(changedProps: PropertyValues) { protected firstUpdated(changedProps: PropertyValues) {
super.firstUpdated(changedProps); super.firstUpdated(changedProps);
const repositoryUrl = extractSearchParam("repository_url");
navigate(this, "/hassio/store", true);
if (repositoryUrl) {
this._manageRepositories(repositoryUrl);
}
this.addEventListener("hass-api-called", (ev) => this.apiCalled(ev)); this.addEventListener("hass-api-called", (ev) => this.apiCalled(ev));
this._loadData(); this._loadData();
} }
private addonRepositories = memoizeOne(
(
repositories: HassioAddonRepository[],
addons: HassioAddonInfo[],
filter?: string
) => {
return repositories.sort(sortRepos).map((repo) => {
const filteredAddons = addons.filter(
(addon) => addon.repository === repo.slug
);
return filteredAddons.length !== 0
? html`
<hassio-addon-repository
.hass=${this.hass}
.repo=${repo}
.addons=${filteredAddons}
.filter=${filter!}
.supervisor=${this.supervisor}
></hassio-addon-repository>
`
: html``;
});
}
);
private _handleAction(ev: CustomEvent<ActionDetail>) { private _handleAction(ev: CustomEvent<ActionDetail>) {
switch (ev.detail.index) { switch (ev.detail.index) {
case 0: case 0:
this._manageRepositoriesClicked(); this._manageRepositories();
break; break;
case 1: case 1:
this.refreshData(); this.refreshData();
@@ -195,26 +177,26 @@ class HassioAddonStore extends LitElement {
} }
} }
private _manageRepositoriesClicked() { private async _manageRepositories() {
this._manageRepositories();
}
private async _manageRepositories(url?: string) {
showRepositoriesDialog(this, { showRepositoriesDialog(this, {
supervisor: this.supervisor, repos: this._repos!,
url, loadData: () => this._loadData(),
}); });
} }
private async _manageRegistries() { private async _manageRegistries() {
showRegistriesDialog(this, { supervisor: this.supervisor }); showRegistriesDialog(this);
} }
private async _loadData() { private async _loadData() {
fireEvent(this, "supervisor-collection-refresh", { collection: "addon" }); try {
fireEvent(this, "supervisor-collection-refresh", { const addonsInfo = await fetchHassioAddonsInfo(this.hass);
collection: "supervisor", this._repos = addonsInfo.repositories;
}); this._repos.sort(sortRepos);
this._addons = addonsInfo.addons;
} catch (err) {
alert(extractApiErrorMessage(err));
}
} }
private async _filterChanged(e) { private async _filterChanged(e) {

View File

@@ -7,14 +7,13 @@ import {
CSSResult, CSSResult,
customElement, customElement,
html, html,
internalProperty,
LitElement, LitElement,
property, property,
internalProperty,
PropertyValues, PropertyValues,
TemplateResult, TemplateResult,
} from "lit-element"; } from "lit-element";
import "web-animations-js/web-animations-next-lite.min"; import "web-animations-js/web-animations-next-lite.min";
import "../../../../src/components/buttons/ha-progress-button";
import "../../../../src/components/ha-card"; import "../../../../src/components/ha-card";
import { import {
HassioAddonDetails, HassioAddonDetails,
@@ -25,18 +24,16 @@ import {
fetchHassioHardwareAudio, fetchHassioHardwareAudio,
HassioHardwareAudioDevice, HassioHardwareAudioDevice,
} from "../../../../src/data/hassio/hardware"; } from "../../../../src/data/hassio/hardware";
import { Supervisor } from "../../../../src/data/supervisor/supervisor";
import { haStyle } from "../../../../src/resources/styles"; import { haStyle } from "../../../../src/resources/styles";
import { HomeAssistant } from "../../../../src/types"; import { HomeAssistant } from "../../../../src/types";
import { suggestAddonRestart } from "../../dialogs/suggestAddonRestart"; import { suggestAddonRestart } from "../../dialogs/suggestAddonRestart";
import { hassioStyle } from "../../resources/hassio-style"; import { hassioStyle } from "../../resources/hassio-style";
import "../../../../src/components/buttons/ha-progress-button";
@customElement("hassio-addon-audio") @customElement("hassio-addon-audio")
class HassioAddonAudio extends LitElement { class HassioAddonAudio extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant; @property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public supervisor!: Supervisor;
@property({ attribute: false }) public addon!: HassioAddonDetails; @property({ attribute: false }) public addon!: HassioAddonDetails;
@internalProperty() private _error?: string; @internalProperty() private _error?: string;
@@ -51,16 +48,12 @@ class HassioAddonAudio extends LitElement {
protected render(): TemplateResult { protected render(): TemplateResult {
return html` return html`
<ha-card <ha-card header="Audio">
.header=${this.supervisor.localize("addon.configuration.audio.header")}
>
<div class="card-content"> <div class="card-content">
${this._error ? html` <div class="errors">${this._error}</div> ` : ""} ${this._error ? html` <div class="errors">${this._error}</div> ` : ""}
<paper-dropdown-menu <paper-dropdown-menu
.label=${this.supervisor.localize( label="Input"
"addon.configuration.audio.input"
)}
@iron-select=${this._setInputDevice} @iron-select=${this._setInputDevice}
> >
<paper-listbox <paper-listbox
@@ -71,17 +64,15 @@ class HassioAddonAudio extends LitElement {
${this._inputDevices && ${this._inputDevices &&
this._inputDevices.map((item) => { this._inputDevices.map((item) => {
return html` return html`
<paper-item device=${item.device || ""}> <paper-item device=${item.device || ""}
${item.name} >${item.name}</paper-item
</paper-item> >
`; `;
})} })}
</paper-listbox> </paper-listbox>
</paper-dropdown-menu> </paper-dropdown-menu>
<paper-dropdown-menu <paper-dropdown-menu
.label=${this.supervisor.localize( label="Output"
"addon.configuration.audio.output"
)}
@iron-select=${this._setOutputDevice} @iron-select=${this._setOutputDevice}
> >
<paper-listbox <paper-listbox
@@ -102,7 +93,7 @@ class HassioAddonAudio extends LitElement {
</div> </div>
<div class="card-actions"> <div class="card-actions">
<ha-progress-button @click=${this._saveSettings}> <ha-progress-button @click=${this._saveSettings}>
${this.supervisor.localize("common.save")} Save
</ha-progress-button> </ha-progress-button>
</div> </div>
</ha-card> </ha-card>
@@ -161,7 +152,7 @@ class HassioAddonAudio extends LitElement {
const noDevice: HassioHardwareAudioDevice = { const noDevice: HassioHardwareAudioDevice = {
device: "default", device: "default",
name: this.supervisor.localize("addon.configuration.audio.default"), name: "Default",
}; };
try { try {
@@ -198,7 +189,7 @@ class HassioAddonAudio extends LitElement {
try { try {
await setHassioAddonOption(this.hass, this.addon.slug, data); await setHassioAddonOption(this.hass, this.addon.slug, data);
if (this.addon?.state === "started") { if (this.addon?.state === "started") {
await suggestAddonRestart(this, this.hass, this.supervisor, this.addon); await suggestAddonRestart(this, this.hass, this.addon);
} }
} catch { } catch {
this._error = "Failed to set addon audio device"; this._error = "Failed to set addon audio device";

View File

@@ -7,12 +7,11 @@ import {
property, property,
TemplateResult, TemplateResult,
} from "lit-element"; } from "lit-element";
import "../../../../src/components/ha-circular-progress";
import { HassioAddonDetails } from "../../../../src/data/hassio/addon"; import { HassioAddonDetails } from "../../../../src/data/hassio/addon";
import { Supervisor } from "../../../../src/data/supervisor/supervisor";
import { haStyle } from "../../../../src/resources/styles"; import { haStyle } from "../../../../src/resources/styles";
import { HomeAssistant } from "../../../../src/types"; import { HomeAssistant } from "../../../../src/types";
import { hassioStyle } from "../../resources/hassio-style"; import { hassioStyle } from "../../resources/hassio-style";
import "../../../../src/components/ha-circular-progress";
import "./hassio-addon-audio"; import "./hassio-addon-audio";
import "./hassio-addon-config"; import "./hassio-addon-config";
import "./hassio-addon-network"; import "./hassio-addon-network";
@@ -21,51 +20,34 @@ import "./hassio-addon-network";
class HassioAddonConfigDashboard extends LitElement { class HassioAddonConfigDashboard extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant; @property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public supervisor!: Supervisor;
@property({ attribute: false }) public addon?: HassioAddonDetails; @property({ attribute: false }) public addon?: HassioAddonDetails;
protected render(): TemplateResult { protected render(): TemplateResult {
if (!this.addon) { if (!this.addon) {
return html`<ha-circular-progress active></ha-circular-progress>`; return html`<ha-circular-progress active></ha-circular-progress>`;
} }
const hasConfiguration =
(this.addon.options && Object.keys(this.addon.options).length) ||
(this.addon.schema && Object.keys(this.addon.schema).length);
return html` return html`
<div class="content"> <div class="content">
${hasConfiguration || this.addon.network || this.addon.audio <hassio-addon-config
.hass=${this.hass}
.addon=${this.addon}
></hassio-addon-config>
${this.addon.network
? html` ? html`
${hasConfiguration <hassio-addon-network
? html` .hass=${this.hass}
<hassio-addon-config .addon=${this.addon}
.hass=${this.hass} ></hassio-addon-network>
.addon=${this.addon}
.supervisor=${this.supervisor}
></hassio-addon-config>
`
: ""}
${this.addon.network
? html`
<hassio-addon-network
.hass=${this.hass}
.addon=${this.addon}
.supervisor=${this.supervisor}
></hassio-addon-network>
`
: ""}
${this.addon.audio
? html`
<hassio-addon-audio
.hass=${this.hass}
.addon=${this.addon}
.supervisor=${this.supervisor}
></hassio-addon-audio>
`
: ""}
` `
: this.supervisor.localize("addon.configuration.no_configuration")} : ""}
${this.addon.audio
? html`
<hassio-addon-audio
.hass=${this.hass}
.addon=${this.addon}
></hassio-addon-audio>
`
: ""}
</div> </div>
`; `;
} }

View File

@@ -1,7 +1,4 @@
import "@material/mwc-button"; import "@material/mwc-button";
import { ActionDetail } from "@material/mwc-list";
import "@material/mwc-list/mwc-list-item";
import { mdiDotsVertical } from "@mdi/js";
import "@polymer/iron-autogrow-textarea/iron-autogrow-textarea"; import "@polymer/iron-autogrow-textarea/iron-autogrow-textarea";
import { import {
css, css,
@@ -15,15 +12,9 @@ import {
query, query,
TemplateResult, TemplateResult,
} from "lit-element"; } from "lit-element";
import memoizeOne from "memoize-one";
import { fireEvent } from "../../../../src/common/dom/fire_event"; import { fireEvent } from "../../../../src/common/dom/fire_event";
import "../../../../src/components/buttons/ha-progress-button"; import "../../../../src/components/buttons/ha-progress-button";
import "../../../../src/components/ha-button-menu";
import "../../../../src/components/ha-card"; import "../../../../src/components/ha-card";
import "../../../../src/components/ha-form/ha-form";
import type { HaFormSchema } from "../../../../src/components/ha-form/ha-form";
import "../../../../src/components/ha-formfield";
import "../../../../src/components/ha-switch";
import "../../../../src/components/ha-yaml-editor"; import "../../../../src/components/ha-yaml-editor";
import type { HaYamlEditor } from "../../../../src/components/ha-yaml-editor"; import type { HaYamlEditor } from "../../../../src/components/ha-yaml-editor";
import { import {
@@ -32,197 +23,62 @@ import {
setHassioAddonOption, setHassioAddonOption,
} from "../../../../src/data/hassio/addon"; } from "../../../../src/data/hassio/addon";
import { extractApiErrorMessage } from "../../../../src/data/hassio/common"; import { extractApiErrorMessage } from "../../../../src/data/hassio/common";
import { Supervisor } from "../../../../src/data/supervisor/supervisor";
import { showConfirmationDialog } from "../../../../src/dialogs/generic/show-dialog-box"; import { showConfirmationDialog } from "../../../../src/dialogs/generic/show-dialog-box";
import { haStyle } from "../../../../src/resources/styles"; import { haStyle } from "../../../../src/resources/styles";
import type { HomeAssistant } from "../../../../src/types"; import type { HomeAssistant } from "../../../../src/types";
import { suggestAddonRestart } from "../../dialogs/suggestAddonRestart"; import { suggestAddonRestart } from "../../dialogs/suggestAddonRestart";
import { hassioStyle } from "../../resources/hassio-style"; import { hassioStyle } from "../../resources/hassio-style";
const SUPPORTED_UI_TYPES = ["string", "select", "boolean", "integer", "float"];
@customElement("hassio-addon-config") @customElement("hassio-addon-config")
class HassioAddonConfig extends LitElement { class HassioAddonConfig extends LitElement {
@property({ attribute: false }) public addon!: HassioAddonDetails;
@property({ attribute: false }) public hass!: HomeAssistant; @property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public supervisor!: Supervisor; @property({ attribute: false }) public addon!: HassioAddonDetails;
@internalProperty() private _error?: string;
@property({ type: Boolean }) private _configHasChanged = false; @property({ type: Boolean }) private _configHasChanged = false;
@property({ type: Boolean }) private _valid = true; @property({ type: Boolean }) private _valid = true;
@internalProperty() private _canShowSchema = false; @query("ha-yaml-editor", true) private _editor!: HaYamlEditor;
@internalProperty() private _showOptional = false;
@internalProperty() private _error?: string;
@internalProperty() private _options?: Record<string, unknown>;
@internalProperty() private _yamlMode = false;
@query("ha-yaml-editor") private _editor?: HaYamlEditor;
public computeLabel = (entry: HaFormSchema): string => {
return (
this.addon.translations[this.hass.language]?.configuration?.[entry.name]
?.name ||
this.addon.translations.en?.configuration?.[entry.name].name ||
entry.name
);
};
private _filteredShchema = memoizeOne(
(options: Record<string, unknown>, schema: HaFormSchema[]) => {
return schema.filter((entry) => entry.name in options || entry.required);
}
);
protected render(): TemplateResult { protected render(): TemplateResult {
const showForm =
!this._yamlMode && this._canShowSchema && this.addon.schema;
const hasHiddenOptions =
showForm &&
JSON.stringify(this.addon.schema) !==
JSON.stringify(
this._filteredShchema(this.addon.options, this.addon.schema!)
);
return html` return html`
<h1>${this.addon.name}</h1> <h1>${this.addon.name}</h1>
<ha-card> <ha-card header="Configuration">
<div class="header">
<h2>
${this.supervisor.localize("addon.configuration.options.header")}
</h2>
<div class="card-menu">
<ha-button-menu corner="BOTTOM_START" @action=${this._handleAction}>
<mwc-icon-button slot="trigger">
<ha-svg-icon .path=${mdiDotsVertical}></ha-svg-icon>
</mwc-icon-button>
<mwc-list-item .disabled=${!this._canShowSchema}>
${this._yamlMode
? this.supervisor.localize(
"addon.configuration.options.edit_in_ui"
)
: this.supervisor.localize(
"addon.configuration.options.edit_in_yaml"
)}
</mwc-list-item>
<mwc-list-item class="warning">
${this.supervisor.localize("common.reset_defaults")}
</mwc-list-item>
</ha-button-menu>
</div>
</div>
<div class="card-content"> <div class="card-content">
${showForm <ha-yaml-editor
? html`<ha-form @value-changed=${this._configChanged}
.data=${this._options!} ></ha-yaml-editor>
@value-changed=${this._configChanged}
.computeLabel=${this.computeLabel}
.schema=${this._showOptional
? this.addon.schema!
: this._filteredShchema(
this.addon.options,
this.addon.schema!
)}
></ha-form>`
: html` <ha-yaml-editor
@value-changed=${this._configChanged}
></ha-yaml-editor>`}
${this._error ? html` <div class="errors">${this._error}</div> ` : ""} ${this._error ? html` <div class="errors">${this._error}</div> ` : ""}
${!this._yamlMode || ${this._valid ? "" : html` <div class="errors">Invalid YAML</div> `}
(this._canShowSchema && this.addon.schema) ||
this._valid
? ""
: html`
<div class="errors">
${this.supervisor.localize(
"addon.configuration.options.invalid_yaml"
)}
</div>
`}
</div> </div>
${hasHiddenOptions <div class="card-actions">
? html`<ha-formfield <ha-progress-button class="warning" @click=${this._resetTapped}>
class="show-additional" Reset to defaults
.label=${this.supervisor.localize( </ha-progress-button>
"addon.configuration.options.show_unused_optional"
)}
>
<ha-switch
@change=${this._toggleOptional}
.checked=${this._showOptional}
>
</ha-switch>
</ha-formfield>`
: ""}
<div class="card-actions right">
<ha-progress-button <ha-progress-button
@click=${this._saveTapped} @click=${this._saveTapped}
.disabled=${!this._configHasChanged || !this._valid} .disabled=${!this._configHasChanged || !this._valid}
> >
${this.supervisor.localize("common.save")} Save
</ha-progress-button> </ha-progress-button>
</div> </div>
</ha-card> </ha-card>
`; `;
} }
protected firstUpdated(changedProps) {
super.firstUpdated(changedProps);
this._canShowSchema = !this.addon.schema!.find(
// @ts-ignore
(entry) => !SUPPORTED_UI_TYPES.includes(entry.type) || entry.multiple
);
this._yamlMode = !this._canShowSchema;
}
protected updated(changedProperties: PropertyValues): void { protected updated(changedProperties: PropertyValues): void {
if (changedProperties.has("addon")) {
this._options = { ...this.addon.options };
}
super.updated(changedProperties); super.updated(changedProperties);
if ( if (changedProperties.has("addon")) {
changedProperties.has("_yamlMode") || this._editor.setValue(this.addon.options);
changedProperties.has("_options")
) {
if (this._yamlMode) {
const editor = this._editor;
if (editor) {
editor.setValue(this._options!);
}
}
} }
} }
private _handleAction(ev: CustomEvent<ActionDetail>) {
switch (ev.detail.index) {
case 0:
this._yamlMode = !this._yamlMode;
break;
case 1:
this._resetTapped(ev);
break;
}
}
private _toggleOptional() {
this._showOptional = !this._showOptional;
}
private _configChanged(ev): void { private _configChanged(ev): void {
if (this.addon.schema && this._canShowSchema && !this._yamlMode) { this._configHasChanged = true;
this._valid = true; this._valid = ev.detail.isValid;
this._configHasChanged = true;
this._options! = ev.detail.value;
} else {
this._configHasChanged = true;
this._valid = ev.detail.isValid;
}
} }
private async _resetTapped(ev: CustomEvent): Promise<void> { private async _resetTapped(ev: CustomEvent): Promise<void> {
@@ -230,10 +86,10 @@ class HassioAddonConfig extends LitElement {
button.progress = true; button.progress = true;
const confirmed = await showConfirmationDialog(this, { const confirmed = await showConfirmationDialog(this, {
title: this.supervisor.localize("confirm.reset_options.title"), title: this.addon.name,
text: this.supervisor.localize("confirm.reset_options.text"), text: "Are you sure you want to reset all your options?",
confirmText: this.supervisor.localize("common.reset_options"), confirmText: "reset options",
dismissText: this.supervisor.localize("common.cancel"), dismissText: "no",
}); });
if (!confirmed) { if (!confirmed) {
@@ -255,11 +111,9 @@ class HassioAddonConfig extends LitElement {
}; };
fireEvent(this, "hass-api-called", eventdata); fireEvent(this, "hass-api-called", eventdata);
} catch (err) { } catch (err) {
this._error = this.supervisor.localize( this._error = `Failed to reset addon configuration, ${extractApiErrorMessage(
"addon.common.update_available", err
"error", )}`;
extractApiErrorMessage(err)
);
} }
button.progress = false; button.progress = false;
} }
@@ -268,13 +122,18 @@ class HassioAddonConfig extends LitElement {
const button = ev.currentTarget as any; const button = ev.currentTarget as any;
button.progress = true; button.progress = true;
let data: HassioAddonSetOptionParams;
this._error = undefined; this._error = undefined;
try { try {
await setHassioAddonOption(this.hass, this.addon.slug, { data = {
options: this._yamlMode ? this._editor?.value : this._options, options: this._editor.value,
}); };
} catch (err) {
this._error = err;
return;
}
try {
await setHassioAddonOption(this.hass, this.addon.slug, data);
this._configHasChanged = false; this._configHasChanged = false;
const eventdata = { const eventdata = {
success: true, success: true,
@@ -283,14 +142,12 @@ class HassioAddonConfig extends LitElement {
}; };
fireEvent(this, "hass-api-called", eventdata); fireEvent(this, "hass-api-called", eventdata);
if (this.addon?.state === "started") { if (this.addon?.state === "started") {
await suggestAddonRestart(this, this.hass, this.supervisor, this.addon); await suggestAddonRestart(this, this.hass, this.addon);
} }
} catch (err) { } catch (err) {
this._error = this.supervisor.localize( this._error = `Failed to save addon configuration, ${extractApiErrorMessage(
"addon.configuration.options.failed_to_save", err
"error", )}`;
extractApiErrorMessage(err)
);
} }
button.progress = false; button.progress = false;
} }
@@ -321,36 +178,6 @@ class HassioAddonConfig extends LitElement {
.syntaxerror { .syntaxerror {
color: var(--error-color); color: var(--error-color);
} }
.card-menu {
float: right;
z-index: 3;
--mdc-theme-text-primary-on-background: var(--primary-text-color);
}
mwc-list-item[disabled] {
--mdc-theme-text-primary-on-background: var(--disabled-text-color);
}
.header {
display: flex;
justify-content: space-between;
}
.header h2 {
color: var(--ha-card-header-color, --primary-text-color);
font-family: var(--ha-card-header-font-family, inherit);
font-size: var(--ha-card-header-font-size, 24px);
letter-spacing: -0.012em;
line-height: 48px;
padding: 12px 16px 16px;
display: block;
margin-block: 0px;
font-weight: normal;
}
.card-actions.right {
justify-content: flex-end;
}
.show-additional {
padding: 16px;
}
`, `,
]; ];
} }

View File

@@ -19,7 +19,6 @@ import {
setHassioAddonOption, setHassioAddonOption,
} from "../../../../src/data/hassio/addon"; } from "../../../../src/data/hassio/addon";
import { extractApiErrorMessage } from "../../../../src/data/hassio/common"; import { extractApiErrorMessage } from "../../../../src/data/hassio/common";
import { Supervisor } from "../../../../src/data/supervisor/supervisor";
import { haStyle } from "../../../../src/resources/styles"; import { haStyle } from "../../../../src/resources/styles";
import { HomeAssistant } from "../../../../src/types"; import { HomeAssistant } from "../../../../src/types";
import { suggestAddonRestart } from "../../dialogs/suggestAddonRestart"; import { suggestAddonRestart } from "../../dialogs/suggestAddonRestart";
@@ -39,8 +38,6 @@ interface NetworkItemInput extends PaperInputElement {
class HassioAddonNetwork extends LitElement { class HassioAddonNetwork extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant; @property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public supervisor!: Supervisor;
@property({ attribute: false }) public addon!: HassioAddonDetails; @property({ attribute: false }) public addon!: HassioAddonDetails;
@internalProperty() private _error?: string; @internalProperty() private _error?: string;
@@ -58,30 +55,16 @@ class HassioAddonNetwork extends LitElement {
} }
return html` return html`
<ha-card <ha-card header="Network">
.header=${this.supervisor.localize(
"addon.configuration.network.header"
)}
>
<div class="card-content"> <div class="card-content">
${this._error ? html` <div class="errors">${this._error}</div> ` : ""} ${this._error ? html` <div class="errors">${this._error}</div> ` : ""}
<table> <table>
<tbody> <tbody>
<tr> <tr>
<th> <th>Container</th>
${this.supervisor.localize( <th>Host</th>
"addon.configuration.network.container" <th>Description</th>
)}
</th>
<th>
${this.supervisor.localize(
"addon.configuration.network.host"
)}
</th>
<th>
${this.supervisor.localize("common.description")}
</th>
</tr> </tr>
${this._config!.map((item) => { ${this._config!.map((item) => {
return html` return html`
@@ -90,15 +73,13 @@ class HassioAddonNetwork extends LitElement {
<td> <td>
<paper-input <paper-input
@value-changed=${this._configChanged} @value-changed=${this._configChanged}
placeholder="${this.supervisor.localize( placeholder="disabled"
"addon.configuration.network.disabled"
)}"
.value=${item.host ? String(item.host) : ""} .value=${item.host ? String(item.host) : ""}
.container=${item.container} .container=${item.container}
no-label-float no-label-float
></paper-input> ></paper-input>
</td> </td>
<td>${this._computeDescription(item)}</td> <td>${item.description}</td>
</tr> </tr>
`; `;
})} })}
@@ -107,10 +88,10 @@ class HassioAddonNetwork extends LitElement {
</div> </div>
<div class="card-actions"> <div class="card-actions">
<ha-progress-button class="warning" @click=${this._resetTapped}> <ha-progress-button class="warning" @click=${this._resetTapped}>
${this.supervisor.localize("common.reset_defaults")} Reset to defaults
</ha-progress-button> </ha-progress-button>
<ha-progress-button @click=${this._saveTapped}> <ha-progress-button @click=${this._saveTapped}>
${this.supervisor.localize("common.save")} Save
</ha-progress-button> </ha-progress-button>
</div> </div>
</ha-card> </ha-card>
@@ -124,15 +105,6 @@ class HassioAddonNetwork extends LitElement {
} }
} }
private _computeDescription = (item: NetworkItem): string => {
return (
this.addon.translations[this.hass.language]?.network?.[item.container]
?.description ||
this.addon.translations.en?.network?.[item.container]?.description ||
item.description
);
};
private _setNetworkConfig(): void { private _setNetworkConfig(): void {
const network = this.addon.network || {}; const network = this.addon.network || {};
const description = this.addon.network_description || {}; const description = this.addon.network_description || {};
@@ -175,14 +147,12 @@ class HassioAddonNetwork extends LitElement {
}; };
fireEvent(this, "hass-api-called", eventdata); fireEvent(this, "hass-api-called", eventdata);
if (this.addon?.state === "started") { if (this.addon?.state === "started") {
await suggestAddonRestart(this, this.hass, this.supervisor, this.addon); await suggestAddonRestart(this, this.hass, this.addon);
} }
} catch (err) { } catch (err) {
this._error = this.supervisor.localize( this._error = `Failed to set addon network configuration, ${extractApiErrorMessage(
"addon.failed_to_reset", err
"error", )}`;
extractApiErrorMessage(err)
);
} }
button.progress = false; button.progress = false;
@@ -211,14 +181,12 @@ class HassioAddonNetwork extends LitElement {
}; };
fireEvent(this, "hass-api-called", eventdata); fireEvent(this, "hass-api-called", eventdata);
if (this.addon?.state === "started") { if (this.addon?.state === "started") {
await suggestAddonRestart(this, this.hass, this.supervisor, this.addon); await suggestAddonRestart(this, this.hass, this.addon);
} }
} catch (err) { } catch (err) {
this._error = this.supervisor.localize( this._error = `Failed to set addon network configuration, ${extractApiErrorMessage(
"addon.failed_to_save", err
"error", )}`;
extractApiErrorMessage(err)
);
} }
button.progress = false; button.progress = false;
} }

View File

@@ -1,4 +1,3 @@
import "../../../../src/components/ha-card";
import { import {
css, css,
CSSResult, CSSResult,
@@ -20,14 +19,11 @@ import "../../../../src/layouts/hass-loading-screen";
import { haStyle } from "../../../../src/resources/styles"; import { haStyle } from "../../../../src/resources/styles";
import { HomeAssistant } from "../../../../src/types"; import { HomeAssistant } from "../../../../src/types";
import { hassioStyle } from "../../resources/hassio-style"; import { hassioStyle } from "../../resources/hassio-style";
import { Supervisor } from "../../../../src/data/supervisor/supervisor";
@customElement("hassio-addon-documentation-tab") @customElement("hassio-addon-documentation-tab")
class HassioAddonDocumentationDashboard extends LitElement { class HassioAddonDocumentationDashboard extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant; @property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public supervisor!: Supervisor;
@property({ attribute: false }) public addon?: HassioAddonDetails; @property({ attribute: false }) public addon?: HassioAddonDetails;
@internalProperty() private _error?: string; @internalProperty() private _error?: string;
@@ -85,11 +81,9 @@ class HassioAddonDocumentationDashboard extends LitElement {
this.addon!.slug this.addon!.slug
); );
} catch (err) { } catch (err) {
this._error = this.supervisor.localize( this._error = `Failed to get addon documentation, ${extractApiErrorMessage(
"addon.documentation.get_logs", err
"error", )}`;
extractApiErrorMessage(err)
);
} }
} }
} }

View File

@@ -9,26 +9,17 @@ import {
CSSResult, CSSResult,
customElement, customElement,
html, html,
internalProperty,
LitElement, LitElement,
property, property,
TemplateResult, TemplateResult,
} from "lit-element"; } from "lit-element";
import memoizeOne from "memoize-one"; import memoizeOne from "memoize-one";
import { fireEvent } from "../../../src/common/dom/fire_event";
import { navigate } from "../../../src/common/navigate";
import { extractSearchParam } from "../../../src/common/url/search-params";
import "../../../src/components/ha-circular-progress";
import { import {
fetchHassioAddonInfo, fetchHassioAddonInfo,
fetchHassioAddonsInfo,
HassioAddonDetails, HassioAddonDetails,
} from "../../../src/data/hassio/addon"; } from "../../../src/data/hassio/addon";
import { extractApiErrorMessage } from "../../../src/data/hassio/common";
import { Supervisor } from "../../../src/data/supervisor/supervisor";
import "../../../src/layouts/hass-error-screen";
import "../../../src/layouts/hass-loading-screen";
import "../../../src/layouts/hass-tabs-subpage"; import "../../../src/layouts/hass-tabs-subpage";
import "../../../src/components/ha-circular-progress";
import type { PageNavigation } from "../../../src/layouts/hass-tabs-subpage"; import type { PageNavigation } from "../../../src/layouts/hass-tabs-subpage";
import { haStyle } from "../../../src/resources/styles"; import { haStyle } from "../../../src/resources/styles";
import { HomeAssistant, Route } from "../../../src/types"; import { HomeAssistant, Route } from "../../../src/types";
@@ -44,16 +35,12 @@ import "./log/hassio-addon-logs";
class HassioAddonDashboard extends LitElement { class HassioAddonDashboard extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant; @property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public supervisor!: Supervisor;
@property({ attribute: false }) public route!: Route; @property({ attribute: false }) public route!: Route;
@property({ attribute: false }) public addon?: HassioAddonDetails; @property({ attribute: false }) public addon?: HassioAddonDetails;
@property({ type: Boolean }) public narrow!: boolean; @property({ type: Boolean }) public narrow!: boolean;
@internalProperty() _error?: string;
private _computeTail = memoizeOne((route: Route) => { private _computeTail = memoizeOne((route: Route) => {
const dividerPos = route.path.indexOf("/", 1); const dividerPos = route.path.indexOf("/", 1);
return dividerPos === -1 return dividerPos === -1
@@ -68,19 +55,13 @@ class HassioAddonDashboard extends LitElement {
}); });
protected render(): TemplateResult { protected render(): TemplateResult {
if (this._error) {
return html`<hass-error-screen
.error=${this._error}
></hass-error-screen>`;
}
if (!this.addon) { if (!this.addon) {
return html`<hass-loading-screen></hass-loading-screen>`; return html`<ha-circular-progress active></ha-circular-progress>`;
} }
const addonTabs: PageNavigation[] = [ const addonTabs: PageNavigation[] = [
{ {
translationKey: "addon.panel.info", name: "Info",
path: `/hassio/addon/${this.addon.slug}/info`, path: `/hassio/addon/${this.addon.slug}/info`,
iconPath: mdiInformationVariant, iconPath: mdiInformationVariant,
}, },
@@ -88,7 +69,7 @@ class HassioAddonDashboard extends LitElement {
if (this.addon.documentation) { if (this.addon.documentation) {
addonTabs.push({ addonTabs.push({
translationKey: "addon.panel.documentation", name: "Documentation",
path: `/hassio/addon/${this.addon.slug}/documentation`, path: `/hassio/addon/${this.addon.slug}/documentation`,
iconPath: mdiFileDocument, iconPath: mdiFileDocument,
}); });
@@ -97,12 +78,12 @@ class HassioAddonDashboard extends LitElement {
if (this.addon.version) { if (this.addon.version) {
addonTabs.push( addonTabs.push(
{ {
translationKey: "addon.panel.configuration", name: "Configuration",
path: `/hassio/addon/${this.addon.slug}/config`, path: `/hassio/addon/${this.addon.slug}/config`,
iconPath: mdiCogs, iconPath: mdiCogs,
}, },
{ {
translationKey: "addon.panel.log", name: "Log",
path: `/hassio/addon/${this.addon.slug}/logs`, path: `/hassio/addon/${this.addon.slug}/logs`,
iconPath: mdiMathLog, iconPath: mdiMathLog,
} }
@@ -114,19 +95,17 @@ class HassioAddonDashboard extends LitElement {
return html` return html`
<hass-tabs-subpage <hass-tabs-subpage
.hass=${this.hass} .hass=${this.hass}
.localizeFunc=${this.supervisor.localize}
.narrow=${this.narrow} .narrow=${this.narrow}
.backPath=${this.addon.version ? "/hassio/dashboard" : "/hassio/store"} .backPath=${this.addon.version ? "/hassio/dashboard" : "/hassio/store"}
.route=${route} .route=${route}
hassio
.tabs=${addonTabs} .tabs=${addonTabs}
supervisor
> >
<span slot="header">${this.addon.name}</span> <span slot="header">${this.addon.name}</span>
<hassio-addon-router <hassio-addon-router
.route=${route} .route=${route}
.narrow=${this.narrow} .narrow=${this.narrow}
.hass=${this.hass} .hass=${this.hass}
.supervisor=${this.supervisor}
.addon=${this.addon} .addon=${this.addon}
></hassio-addon-router> ></hassio-addon-router>
</hass-tabs-subpage> </hass-tabs-subpage>
@@ -173,60 +152,30 @@ class HassioAddonDashboard extends LitElement {
} }
protected async firstUpdated(): Promise<void> { protected async firstUpdated(): Promise<void> {
if (this.route.path === "") { await this._routeDataChanged(this.route);
const requestedAddon = extractSearchParam("addon");
if (requestedAddon) {
const addonsInfo = await fetchHassioAddonsInfo(this.hass);
const validAddon = addonsInfo.addons
.some((addon) => addon.slug === requestedAddon);
if (!validAddon) {
this._error = this.supervisor.localize("my.error_addon_not_found");
} else {
navigate(this, `/hassio/addon/${requestedAddon}`, true);
}
}
}
this.addEventListener("hass-api-called", (ev) => this._apiCalled(ev)); this.addEventListener("hass-api-called", (ev) => this._apiCalled(ev));
} }
private async _apiCalled(ev): Promise<void> { private async _apiCalled(ev): Promise<void> {
const pathSplit: string[] = ev.detail.path?.split("/"); const path: string = ev.detail.path;
if (!pathSplit || pathSplit.length === 0) { if (!path) {
return; return;
} }
const path: string = pathSplit[pathSplit.length - 1];
if (["uninstall", "install", "update", "start", "stop"].includes(path)) {
fireEvent(this, "supervisor-collection-refresh", {
collection: "supervisor",
});
}
if (path === "uninstall") { if (path === "uninstall") {
window.history.back(); history.back();
} else { } else {
await this._routeDataChanged(); await this._routeDataChanged(this.route);
} }
} }
protected updated(changedProperties) { private async _routeDataChanged(routeData: Route): Promise<void> {
if (changedProperties.has("route") && !this.addon) { const addon = routeData.path.split("/")[1];
this._routeDataChanged();
}
}
private async _routeDataChanged(): Promise<void> {
const addon = this.route.path.split("/")[1];
if (!addon) {
return;
}
try { try {
const addoninfo = await fetchHassioAddonInfo(this.hass, addon); const addoninfo = await fetchHassioAddonInfo(this.hass, addon);
this.addon = addoninfo; this.addon = addoninfo;
} catch (err) { } catch {
this._error = `Error fetching addon info: ${extractApiErrorMessage(err)}`;
this.addon = undefined; this.addon = undefined;
} }
} }

View File

@@ -1,6 +1,5 @@
import { customElement, property } from "lit-element"; import { customElement, property } from "lit-element";
import { HassioAddonDetails } from "../../../src/data/hassio/addon"; import { HassioAddonDetails } from "../../../src/data/hassio/addon";
import { Supervisor } from "../../../src/data/supervisor/supervisor";
import { import {
HassRouterPage, HassRouterPage,
RouterOptions, RouterOptions,
@@ -18,8 +17,6 @@ class HassioAddonRouter extends HassRouterPage {
@property({ attribute: false }) public hass!: HomeAssistant; @property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public supervisor!: Supervisor;
@property({ attribute: false }) public addon!: HassioAddonDetails; @property({ attribute: false }) public addon!: HassioAddonDetails;
protected routerOptions: RouterOptions = { protected routerOptions: RouterOptions = {
@@ -44,7 +41,6 @@ class HassioAddonRouter extends HassRouterPage {
protected updatePageEl(el) { protected updatePageEl(el) {
el.route = this.routeTail; el.route = this.routeTail;
el.hass = this.hass; el.hass = this.hass;
el.supervisor = this.supervisor;
el.addon = this.addon; el.addon = this.addon;
el.narrow = this.narrow; el.narrow = this.narrow;
} }

View File

@@ -7,9 +7,8 @@ import {
property, property,
TemplateResult, TemplateResult,
} from "lit-element"; } from "lit-element";
import "../../../../src/components/ha-circular-progress";
import { HassioAddonDetails } from "../../../../src/data/hassio/addon"; import { HassioAddonDetails } from "../../../../src/data/hassio/addon";
import { Supervisor } from "../../../../src/data/supervisor/supervisor"; import "../../../../src/components/ha-circular-progress";
import { haStyle } from "../../../../src/resources/styles"; import { haStyle } from "../../../../src/resources/styles";
import { HomeAssistant } from "../../../../src/types"; import { HomeAssistant } from "../../../../src/types";
import { hassioStyle } from "../../resources/hassio-style"; import { hassioStyle } from "../../resources/hassio-style";
@@ -21,8 +20,6 @@ class HassioAddonInfoDashboard extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant; @property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public supervisor!: Supervisor;
@property({ attribute: false }) public addon?: HassioAddonDetails; @property({ attribute: false }) public addon?: HassioAddonDetails;
protected render(): TemplateResult { protected render(): TemplateResult {
@@ -35,7 +32,6 @@ class HassioAddonInfoDashboard extends LitElement {
<hassio-addon-info <hassio-addon-info
.narrow=${this.narrow} .narrow=${this.narrow}
.hass=${this.hass} .hass=${this.hass}
.supervisor=${this.supervisor}
.addon=${this.addon} .addon=${this.addon}
></hassio-addon-info> ></hassio-addon-info>
</div> </div>

File diff suppressed because it is too large Load Diff

View File

@@ -7,9 +7,8 @@ import {
property, property,
TemplateResult, TemplateResult,
} from "lit-element"; } from "lit-element";
import "../../../../src/components/ha-circular-progress";
import { HassioAddonDetails } from "../../../../src/data/hassio/addon"; import { HassioAddonDetails } from "../../../../src/data/hassio/addon";
import { Supervisor } from "../../../../src/data/supervisor/supervisor"; import "../../../../src/components/ha-circular-progress";
import { haStyle } from "../../../../src/resources/styles"; import { haStyle } from "../../../../src/resources/styles";
import { HomeAssistant } from "../../../../src/types"; import { HomeAssistant } from "../../../../src/types";
import { hassioStyle } from "../../resources/hassio-style"; import { hassioStyle } from "../../resources/hassio-style";
@@ -19,8 +18,6 @@ import "./hassio-addon-logs";
class HassioAddonLogDashboard extends LitElement { class HassioAddonLogDashboard extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant; @property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public supervisor!: Supervisor;
@property({ attribute: false }) public addon?: HassioAddonDetails; @property({ attribute: false }) public addon?: HassioAddonDetails;
protected render(): TemplateResult { protected render(): TemplateResult {
@@ -31,7 +28,6 @@ class HassioAddonLogDashboard extends LitElement {
<div class="content"> <div class="content">
<hassio-addon-logs <hassio-addon-logs
.hass=${this.hass} .hass=${this.hass}
.supervisor=${this.supervisor}
.addon=${this.addon} .addon=${this.addon}
></hassio-addon-logs> ></hassio-addon-logs>
</div> </div>

View File

@@ -15,7 +15,6 @@ import {
HassioAddonDetails, HassioAddonDetails,
} from "../../../../src/data/hassio/addon"; } from "../../../../src/data/hassio/addon";
import { extractApiErrorMessage } from "../../../../src/data/hassio/common"; import { extractApiErrorMessage } from "../../../../src/data/hassio/common";
import { Supervisor } from "../../../../src/data/supervisor/supervisor";
import { haStyle } from "../../../../src/resources/styles"; import { haStyle } from "../../../../src/resources/styles";
import { HomeAssistant } from "../../../../src/types"; import { HomeAssistant } from "../../../../src/types";
import "../../components/hassio-ansi-to-html"; import "../../components/hassio-ansi-to-html";
@@ -25,8 +24,6 @@ import { hassioStyle } from "../../resources/hassio-style";
class HassioAddonLogs extends LitElement { class HassioAddonLogs extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant; @property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public supervisor!: Supervisor;
@property({ attribute: false }) public addon!: HassioAddonDetails; @property({ attribute: false }) public addon!: HassioAddonDetails;
@internalProperty() private _error?: string; @internalProperty() private _error?: string;
@@ -51,9 +48,7 @@ class HassioAddonLogs extends LitElement {
: ""} : ""}
</div> </div>
<div class="card-actions"> <div class="card-actions">
<mwc-button @click=${this._refresh}> <mwc-button @click=${this._refresh}>Refresh</mwc-button>
${this.supervisor.localize("common.refresh")}
</mwc-button>
</div> </div>
</ha-card> </ha-card>
`; `;
@@ -81,11 +76,7 @@ class HassioAddonLogs extends LitElement {
try { try {
this._content = await fetchHassioAddonLogs(this.hass, this.addon.slug); this._content = await fetchHassioAddonLogs(this.hass, this.addon.slug);
} catch (err) { } catch (err) {
this._error = this.supervisor.localize( this._error = `Failed to get addon logs, ${extractApiErrorMessage(err)}`;
"addon.logs.get_logs",
"error",
extractApiErrorMessage(err)
);
} }
} }

View File

@@ -44,7 +44,7 @@ class HassioCardContent extends LitElement {
${this.iconImage ${this.iconImage
? html` ? html`
<div class="icon_image ${this.iconClass}"> <div class="icon_image ${this.iconClass}">
<img src="${this.iconImage}" .title=${this.iconTitle} /> <img src="${this.iconImage}" title="${this.iconTitle}" />
<div></div> <div></div>
</div> </div>
` `

View File

@@ -27,8 +27,6 @@ declare global {
} }
} }
const MAX_FILE_SIZE = 1 * 1024 * 1024 * 1024; // 1GB
@customElement("hassio-upload-snapshot") @customElement("hassio-upload-snapshot")
export class HassioUploadSnapshot extends LitElement { export class HassioUploadSnapshot extends LitElement {
public hass!: HomeAssistant; public hass!: HomeAssistant;
@@ -53,20 +51,6 @@ export class HassioUploadSnapshot extends LitElement {
private async _uploadFile(ev) { private async _uploadFile(ev) {
const file = ev.detail.files[0]; const file = ev.detail.files[0];
if (file.size > MAX_FILE_SIZE) {
showAlertDialog(this, {
title: "Snapshot file is too big",
text: html`The maximum allowed filesize is 1GB.<br />
<a
href="https://www.home-assistant.io/hassio/haos_common_tasks/#restoring-a-snapshot-on-a-new-install"
target="_blank"
>Have a look here on how to restore it.</a
>`,
confirmText: "ok",
});
return;
}
if (!["application/x-tar"].includes(file.type)) { if (!["application/x-tar"].includes(file.type)) {
showAlertDialog(this, { showAlertDialog(this, {
title: "Unsupported file format", title: "Unsupported file format",

View File

@@ -1,87 +0,0 @@
import {
css,
CSSResult,
customElement,
html,
LitElement,
property,
TemplateResult,
} from "lit-element";
import { classMap } from "lit-html/directives/class-map";
import "../../../src/components/ha-bar";
import "../../../src/components/ha-settings-row";
import { roundWithOneDecimal } from "../../../src/util/calculate";
@customElement("supervisor-metric")
class SupervisorMetric extends LitElement {
@property({ type: Number }) public value!: number;
@property({ type: String }) public description!: string;
@property({ type: String }) public tooltip?: string;
protected render(): TemplateResult {
const roundedValue = roundWithOneDecimal(this.value);
return html`<ha-settings-row>
<span slot="heading">
${this.description}
</span>
<div slot="description" .title=${this.tooltip ?? ""}>
<span class="value">
${roundedValue} %
</span>
<ha-bar
class="${classMap({
"target-warning": roundedValue > 50,
"target-critical": roundedValue > 85,
})}"
.value=${this.value}
></ha-bar>
</div>
</ha-settings-row>`;
}
static get styles(): CSSResult {
return css`
ha-settings-row {
padding: 0;
height: 54px;
width: 100%;
}
ha-settings-row > div[slot="description"] {
white-space: normal;
color: var(--secondary-text-color);
display: flex;
justify-content: space-between;
}
ha-bar {
--ha-bar-primary-color: var(
--hassio-bar-ok-color,
var(--success-color)
);
}
.target-warning {
--ha-bar-primary-color: var(
--hassio-bar-warning-color,
var(--warning-color)
);
}
.target-critical {
--ha-bar-primary-color: var(
--hassio-bar-critical-color,
var(--error-color)
);
}
.value {
width: 42px;
padding-right: 4px;
}
`;
}
}
declare global {
interface HTMLElementTagNameMap {
"supervisor-metric": SupervisorMetric;
}
}

View File

@@ -12,7 +12,7 @@ import { atLeastVersion } from "../../../src/common/config/version";
import { navigate } from "../../../src/common/navigate"; import { navigate } from "../../../src/common/navigate";
import { compare } from "../../../src/common/string/compare"; import { compare } from "../../../src/common/string/compare";
import "../../../src/components/ha-card"; import "../../../src/components/ha-card";
import { Supervisor } from "../../../src/data/supervisor/supervisor"; import { HassioAddonInfo } from "../../../src/data/hassio/addon";
import { haStyle } from "../../../src/resources/styles"; import { haStyle } from "../../../src/resources/styles";
import { HomeAssistant } from "../../../src/types"; import { HomeAssistant } from "../../../src/types";
import "../components/hassio-card-content"; import "../components/hassio-card-content";
@@ -22,24 +22,26 @@ import { hassioStyle } from "../resources/hassio-style";
class HassioAddons extends LitElement { class HassioAddons extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant; @property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public supervisor!: Supervisor; @property({ attribute: false }) public addons?: HassioAddonInfo[];
protected render(): TemplateResult { protected render(): TemplateResult {
return html` return html`
<div class="content"> <div class="content">
<h1>${this.supervisor.localize("dashboard.addons")}</h1> <h1>Add-ons</h1>
<div class="card-group"> <div class="card-group">
${!this.supervisor.supervisor.addons?.length ${!this.addons?.length
? html` ? html`
<ha-card> <ha-card>
<div class="card-content"> <div class="card-content">
You don't have any add-ons installed yet. Head over to
<button class="link" @click=${this._openStore}> <button class="link" @click=${this._openStore}>
${this.supervisor.localize("dashboard.no_addons")} the add-on store
</button> </button>
to get started!
</div> </div>
</ha-card> </ha-card>
` `
: this.supervisor.supervisor.addons : this.addons
.sort((a, b) => compare(a.name, b.name)) .sort((a, b) => compare(a.name, b.name))
.map( .map(
(addon) => html` (addon) => html`
@@ -56,16 +58,10 @@ class HassioAddons extends LitElement {
? mdiArrowUpBoldCircle ? mdiArrowUpBoldCircle
: mdiPuzzle} : mdiPuzzle}
.iconTitle=${addon.state !== "started" .iconTitle=${addon.state !== "started"
? this.supervisor.localize( ? "Add-on is stopped"
"dashboard.addon_stopped"
)
: addon.update_available! : addon.update_available!
? this.supervisor.localize( ? "New version available"
"dashboard.addon_new_version" : "Add-on is running"}
)
: this.supervisor.localize(
"dashboard.addon_running"
)}
.iconClass=${addon.update_available .iconClass=${addon.update_available
? addon.state === "started" ? addon.state === "started"
? "update" ? "update"

View File

@@ -7,7 +7,11 @@ import {
property, property,
TemplateResult, TemplateResult,
} from "lit-element"; } from "lit-element";
import { Supervisor } from "../../../src/data/supervisor/supervisor"; import { HassioHassOSInfo } from "../../../src/data/hassio/host";
import {
HassioHomeAssistantInfo,
HassioSupervisorInfo,
} from "../../../src/data/hassio/supervisor";
import "../../../src/layouts/hass-tabs-subpage"; import "../../../src/layouts/hass-tabs-subpage";
import { haStyle } from "../../../src/resources/styles"; import { haStyle } from "../../../src/resources/styles";
import { HomeAssistant, Route } from "../../../src/types"; import { HomeAssistant, Route } from "../../../src/types";
@@ -19,34 +23,37 @@ import "./hassio-update";
class HassioDashboard extends LitElement { class HassioDashboard extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant; @property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public supervisor!: Supervisor;
@property({ type: Boolean }) public narrow!: boolean; @property({ type: Boolean }) public narrow!: boolean;
@property({ attribute: false }) public route!: Route; @property({ attribute: false }) public route!: Route;
@property({ attribute: false }) public supervisorInfo!: HassioSupervisorInfo;
@property({ attribute: false }) public hassInfo!: HassioHomeAssistantInfo;
@property({ attribute: false }) public hassOsInfo!: HassioHassOSInfo;
protected render(): TemplateResult { protected render(): TemplateResult {
return html` return html`
<hass-tabs-subpage <hass-tabs-subpage
.hass=${this.hass} .hass=${this.hass}
.localizeFunc=${this.supervisor.localize}
.narrow=${this.narrow} .narrow=${this.narrow}
hassio
main-page
.route=${this.route} .route=${this.route}
.tabs=${supervisorTabs} .tabs=${supervisorTabs}
main-page
supervisor
> >
<span slot="header"> <span slot="header">Dashboard</span>
${this.supervisor.localize("panel.dashboard")}
</span>
<div class="content"> <div class="content">
<hassio-update <hassio-update
.hass=${this.hass} .hass=${this.hass}
.supervisor=${this.supervisor} .hassInfo=${this.hassInfo}
.supervisorInfo=${this.supervisorInfo}
.hassOsInfo=${this.hassOsInfo}
></hassio-update> ></hassio-update>
<hassio-addons <hassio-addons
.hass=${this.hass} .hass=${this.hass}
.supervisor=${this.supervisor} .addons=${this.supervisorInfo.addons}
></hassio-addons> ></hassio-addons>
</div> </div>
</hass-tabs-subpage> </hass-tabs-subpage>

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