mirror of
https://github.com/home-assistant/frontend.git
synced 2025-08-13 03:09:26 +00:00
Compare commits
1 Commits
20200623.1
...
restore-co
Author | SHA1 | Date | |
---|---|---|---|
![]() |
3272c32f87 |
4
.dockerignore
Normal file
4
.dockerignore
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
node_modules
|
||||||
|
hass_frontend
|
||||||
|
hass_frontend_es5
|
||||||
|
.git
|
6
.github/workflows/ci.yaml
vendored
6
.github/workflows/ci.yaml
vendored
@@ -34,8 +34,10 @@ jobs:
|
|||||||
run: yarn install
|
run: yarn install
|
||||||
env:
|
env:
|
||||||
CI: true
|
CI: true
|
||||||
- name: Build resources
|
- name: Build icons
|
||||||
run: ./node_modules/.bin/gulp gen-icons-json build-translations gather-gallery-demos
|
run: ./node_modules/.bin/gulp gen-icons-json
|
||||||
|
- name: Build translations
|
||||||
|
run: ./node_modules/.bin/gulp build-translations
|
||||||
- name: Run eslint
|
- name: Run eslint
|
||||||
run: ./node_modules/.bin/eslint '{**/src,src}/**/*.{js,ts,html}' --ignore-path .gitignore
|
run: ./node_modules/.bin/eslint '{**/src,src}/**/*.{js,ts,html}' --ignore-path .gitignore
|
||||||
- name: Run tsc
|
- name: Run tsc
|
||||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@@ -24,7 +24,7 @@ dist
|
|||||||
.vscode/*
|
.vscode/*
|
||||||
!.vscode/extensions.json
|
!.vscode/extensions.json
|
||||||
|
|
||||||
# Cast dev settings
|
# Cast dev settings
|
||||||
src/cast/dev_const.ts
|
src/cast/dev_const.ts
|
||||||
|
|
||||||
# Secrets
|
# Secrets
|
||||||
|
31
Dockerfile
Normal file
31
Dockerfile
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
FROM node:8.11.1-alpine
|
||||||
|
|
||||||
|
# install yarn
|
||||||
|
ENV PATH /root/.yarn/bin:$PATH
|
||||||
|
|
||||||
|
## Install/force base tools
|
||||||
|
RUN apk update \
|
||||||
|
&& apk add make g++ curl bash binutils tar git python2 python3 \
|
||||||
|
&& rm -rf /var/cache/apk/* \
|
||||||
|
&& /bin/bash \
|
||||||
|
&& touch ~/.bashrc
|
||||||
|
|
||||||
|
## Install yarn
|
||||||
|
RUN curl -o- -L https://yarnpkg.com/install.sh | bash
|
||||||
|
|
||||||
|
## Setup the project
|
||||||
|
RUN mkdir -p /frontend
|
||||||
|
|
||||||
|
WORKDIR /frontend
|
||||||
|
|
||||||
|
COPY package.json yarn.lock ./
|
||||||
|
|
||||||
|
RUN yarn install --frozen-lockfile
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
COPY script/docker_entrypoint.sh /usr/bin/docker_entrypoint.sh
|
||||||
|
|
||||||
|
RUN chmod +x /usr/bin/docker_entrypoint.sh
|
||||||
|
|
||||||
|
CMD [ "docker_entrypoint.sh" ]
|
@@ -22,6 +22,15 @@ This is the repository for the official [Home Assistant](https://home-assistant.
|
|||||||
|
|
||||||
A complete guide can be found at the following [link](https://www.home-assistant.io/developers/frontend/). It describes a short guide for the build of project.
|
A complete guide can be found at the following [link](https://www.home-assistant.io/developers/frontend/). It describes a short guide for the build of project.
|
||||||
|
|
||||||
|
### Docker environment
|
||||||
|
|
||||||
|
It is possible to compile the project and/or run commands in the development environment having only the [Docker](https://www.docker.com) pre-installed in the system. On the root of project you can do:
|
||||||
|
|
||||||
|
- `sh ./script/docker_run.sh build` Build all the project with one command
|
||||||
|
- `sh ./script/docker_run.sh bash` Open an interactive shell (the same environment generated by the _classic environment_) where you can run commands. This bash work on your project directory and any change on your file is automatically present within your build bash.
|
||||||
|
|
||||||
|
**Note**: if you have installed `npm` in addition to the `docker`, you can use the commands `npm run docker_build` and `npm run bash` to get a full build or bash as explained above
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
Home Assistant is open-source and Apache 2 licensed. Feel free to browse the repository, learn and reuse parts in your own projects.
|
Home Assistant is open-source and Apache 2 licensed. Feel free to browse the repository, learn and reuse parts in your own projects.
|
||||||
|
@@ -20,13 +20,7 @@ module.exports.emptyPackages = ({ latestBuild }) =>
|
|||||||
// Loads stuff from a CDN
|
// Loads stuff from a CDN
|
||||||
require.resolve("@polymer/font-roboto/roboto.js"),
|
require.resolve("@polymer/font-roboto/roboto.js"),
|
||||||
require.resolve("@vaadin/vaadin-material-styles/font-roboto.js"),
|
require.resolve("@vaadin/vaadin-material-styles/font-roboto.js"),
|
||||||
// Compatibility not needed for latest builds
|
// Polyfill only needed for ES5 workers so filter out in latestBuild
|
||||||
latestBuild &&
|
|
||||||
// wrapped in require.resolve so it blows up if file no longer exists
|
|
||||||
require.resolve(
|
|
||||||
path.resolve(paths.polymer_dir, "src/resources/compatibility.ts")
|
|
||||||
),
|
|
||||||
// This polyfill is loaded in workers to support ES5, filter it out.
|
|
||||||
latestBuild && require.resolve("proxy-polyfill/src/index.js"),
|
latestBuild && require.resolve("proxy-polyfill/src/index.js"),
|
||||||
].filter(Boolean);
|
].filter(Boolean);
|
||||||
|
|
||||||
@@ -57,7 +51,7 @@ module.exports.babelOptions = ({ latestBuild }) => ({
|
|||||||
].filter(Boolean),
|
].filter(Boolean),
|
||||||
plugins: [
|
plugins: [
|
||||||
// Part of ES2018. Converts {...a, b: 2} to Object.assign({}, a, {b: 2})
|
// Part of ES2018. Converts {...a, b: 2} to Object.assign({}, a, {b: 2})
|
||||||
!latestBuild && [
|
[
|
||||||
"@babel/plugin-proposal-object-rest-spread",
|
"@babel/plugin-proposal-object-rest-spread",
|
||||||
{ loose: true, useBuiltIns: true },
|
{ loose: true, useBuiltIns: true },
|
||||||
],
|
],
|
||||||
@@ -73,7 +67,7 @@ module.exports.babelOptions = ({ latestBuild }) => ({
|
|||||||
require("@babel/plugin-proposal-class-properties").default,
|
require("@babel/plugin-proposal-class-properties").default,
|
||||||
{ loose: true },
|
{ loose: true },
|
||||||
],
|
],
|
||||||
].filter(Boolean),
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
// Are already ES5, cause warnings when babelified.
|
// Are already ES5, cause warnings when babelified.
|
||||||
@@ -85,8 +79,8 @@ module.exports.babelExclude = () => [
|
|||||||
const outputPath = (outputRoot, latestBuild) =>
|
const outputPath = (outputRoot, latestBuild) =>
|
||||||
path.resolve(outputRoot, latestBuild ? "frontend_latest" : "frontend_es5");
|
path.resolve(outputRoot, latestBuild ? "frontend_latest" : "frontend_es5");
|
||||||
|
|
||||||
const publicPath = (latestBuild, root = "") =>
|
const publicPath = (latestBuild) =>
|
||||||
latestBuild ? `${root}/frontend_latest/` : `${root}/frontend_es5/`;
|
latestBuild ? "/frontend_latest/" : "/frontend_es5/";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
BundleConfig {
|
BundleConfig {
|
||||||
@@ -118,6 +112,7 @@ module.exports.config = {
|
|||||||
authorize: "./src/entrypoints/authorize.ts",
|
authorize: "./src/entrypoints/authorize.ts",
|
||||||
onboarding: "./src/entrypoints/onboarding.ts",
|
onboarding: "./src/entrypoints/onboarding.ts",
|
||||||
core: "./src/entrypoints/core.ts",
|
core: "./src/entrypoints/core.ts",
|
||||||
|
compatibility: "./src/entrypoints/compatibility.ts",
|
||||||
"custom-panel": "./src/entrypoints/custom-panel.ts",
|
"custom-panel": "./src/entrypoints/custom-panel.ts",
|
||||||
},
|
},
|
||||||
outputPath: outputPath(paths.app_output_root, latestBuild),
|
outputPath: outputPath(paths.app_output_root, latestBuild),
|
||||||
@@ -132,6 +127,10 @@ module.exports.config = {
|
|||||||
return {
|
return {
|
||||||
entry: {
|
entry: {
|
||||||
main: path.resolve(paths.demo_dir, "src/entrypoint.ts"),
|
main: path.resolve(paths.demo_dir, "src/entrypoint.ts"),
|
||||||
|
compatibility: path.resolve(
|
||||||
|
paths.polymer_dir,
|
||||||
|
"src/entrypoints/compatibility.ts"
|
||||||
|
),
|
||||||
},
|
},
|
||||||
outputPath: outputPath(paths.demo_output_root, latestBuild),
|
outputPath: outputPath(paths.demo_output_root, latestBuild),
|
||||||
publicPath: publicPath(latestBuild),
|
publicPath: publicPath(latestBuild),
|
||||||
@@ -170,12 +169,15 @@ module.exports.config = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
hassio({ isProdBuild, latestBuild }) {
|
hassio({ isProdBuild, latestBuild }) {
|
||||||
|
if (latestBuild) {
|
||||||
|
throw new Error("Hass.io does not support latest build!");
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
entry: {
|
entry: {
|
||||||
entrypoint: path.resolve(paths.hassio_dir, "src/entrypoint.ts"),
|
entrypoint: path.resolve(paths.hassio_dir, "src/entrypoint.ts"),
|
||||||
},
|
},
|
||||||
outputPath: outputPath(paths.hassio_output_root, latestBuild),
|
outputPath: paths.hassio_output_root,
|
||||||
publicPath: publicPath(latestBuild, paths.hassio_publicPath),
|
publicPath: paths.hassio_publicPath,
|
||||||
isProdBuild,
|
isProdBuild,
|
||||||
latestBuild,
|
latestBuild,
|
||||||
dontHash: new Set(["entrypoint"]),
|
dontHash: new Set(["entrypoint"]),
|
||||||
|
@@ -20,7 +20,6 @@ gulp.task(
|
|||||||
"translations-enable-merge-backend",
|
"translations-enable-merge-backend",
|
||||||
gulp.parallel("gen-icons-json", "build-translations"),
|
gulp.parallel("gen-icons-json", "build-translations"),
|
||||||
"copy-static-cast",
|
"copy-static-cast",
|
||||||
"gen-index-cast-dev",
|
|
||||||
env.useRollup() ? "rollup-dev-server-cast" : "webpack-dev-server-cast"
|
env.useRollup() ? "rollup-dev-server-cast" : "webpack-dev-server-cast"
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
@@ -6,32 +6,30 @@ const merge = require("merge-stream");
|
|||||||
const path = require("path");
|
const path = require("path");
|
||||||
const paths = require("../paths");
|
const paths = require("../paths");
|
||||||
|
|
||||||
const zopfliOptions = { threshold: 150 };
|
|
||||||
|
|
||||||
gulp.task("compress-app", function compressApp() {
|
gulp.task("compress-app", function compressApp() {
|
||||||
const jsLatest = gulp
|
const jsLatest = gulp
|
||||||
.src(path.resolve(paths.app_output_latest, "**/*.js"))
|
.src(path.resolve(paths.app_output_latest, "**/*.js"))
|
||||||
.pipe(zopfli(zopfliOptions))
|
.pipe(zopfli({ threshold: 150 }))
|
||||||
.pipe(gulp.dest(paths.app_output_latest));
|
.pipe(gulp.dest(paths.app_output_latest));
|
||||||
|
|
||||||
const jsEs5 = gulp
|
const jsEs5 = gulp
|
||||||
.src(path.resolve(paths.app_output_es5, "**/*.js"))
|
.src(path.resolve(paths.app_output_es5, "**/*.js"))
|
||||||
.pipe(zopfli(zopfliOptions))
|
.pipe(zopfli({ threshold: 150 }))
|
||||||
.pipe(gulp.dest(paths.app_output_es5));
|
.pipe(gulp.dest(paths.app_output_es5));
|
||||||
|
|
||||||
const polyfills = gulp
|
const polyfills = gulp
|
||||||
.src(path.resolve(paths.app_output_static, "polyfills/*.js"))
|
.src(path.resolve(paths.app_output_static, "polyfills/*.js"))
|
||||||
.pipe(zopfli(zopfliOptions))
|
.pipe(zopfli({ threshold: 150 }))
|
||||||
.pipe(gulp.dest(path.resolve(paths.app_output_static, "polyfills")));
|
.pipe(gulp.dest(path.resolve(paths.app_output_static, "polyfills")));
|
||||||
|
|
||||||
const translations = gulp
|
const translations = gulp
|
||||||
.src(path.resolve(paths.app_output_static, "translations/**/*.json"))
|
.src(path.resolve(paths.app_output_static, "translations/**/*.json"))
|
||||||
.pipe(zopfli(zopfliOptions))
|
.pipe(zopfli({ threshold: 150 }))
|
||||||
.pipe(gulp.dest(path.resolve(paths.app_output_static, "translations")));
|
.pipe(gulp.dest(path.resolve(paths.app_output_static, "translations")));
|
||||||
|
|
||||||
const icons = gulp
|
const icons = gulp
|
||||||
.src(path.resolve(paths.app_output_static, "mdi/*.json"))
|
.src(path.resolve(paths.app_output_static, "mdi/*.json"))
|
||||||
.pipe(zopfli(zopfliOptions))
|
.pipe(zopfli({ threshold: 150 }))
|
||||||
.pipe(gulp.dest(path.resolve(paths.app_output_static, "mdi")));
|
.pipe(gulp.dest(path.resolve(paths.app_output_static, "mdi")));
|
||||||
|
|
||||||
return merge(jsLatest, jsEs5, polyfills, translations, icons);
|
return merge(jsLatest, jsEs5, polyfills, translations, icons);
|
||||||
@@ -40,6 +38,6 @@ gulp.task("compress-app", function compressApp() {
|
|||||||
gulp.task("compress-hassio", function compressApp() {
|
gulp.task("compress-hassio", function compressApp() {
|
||||||
return gulp
|
return gulp
|
||||||
.src(path.resolve(paths.hassio_output_root, "**/*.js"))
|
.src(path.resolve(paths.hassio_output_root, "**/*.js"))
|
||||||
.pipe(zopfli(zopfliOptions))
|
.pipe(zopfli())
|
||||||
.pipe(gulp.dest(paths.hassio_output_root));
|
.pipe(gulp.dest(paths.hassio_output_root));
|
||||||
});
|
});
|
||||||
|
@@ -53,6 +53,7 @@ gulp.task("gen-pages-dev", (done) => {
|
|||||||
const content = renderTemplate(page, {
|
const content = renderTemplate(page, {
|
||||||
latestPageJS: `/frontend_latest/${page}.js`,
|
latestPageJS: `/frontend_latest/${page}.js`,
|
||||||
|
|
||||||
|
es5Compatibility: "/frontend_es5/compatibility.js",
|
||||||
es5PageJS: `/frontend_es5/${page}.js`,
|
es5PageJS: `/frontend_es5/${page}.js`,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -78,6 +79,7 @@ gulp.task("gen-pages-prod", (done) => {
|
|||||||
const content = renderTemplate(page, {
|
const content = renderTemplate(page, {
|
||||||
latestPageJS: latestManifest[`${page}.js`],
|
latestPageJS: latestManifest[`${page}.js`],
|
||||||
|
|
||||||
|
es5Compatibility: es5Manifest["compatibility.js"],
|
||||||
es5PageJS: es5Manifest[`${page}.js`],
|
es5PageJS: es5Manifest[`${page}.js`],
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -97,6 +99,7 @@ gulp.task("gen-index-app-dev", (done) => {
|
|||||||
latestCoreJS: "/frontend_latest/core.js",
|
latestCoreJS: "/frontend_latest/core.js",
|
||||||
latestCustomPanelJS: "/frontend_latest/custom-panel.js",
|
latestCustomPanelJS: "/frontend_latest/custom-panel.js",
|
||||||
|
|
||||||
|
es5Compatibility: "/frontend_es5/compatibility.js",
|
||||||
es5AppJS: "/frontend_es5/app.js",
|
es5AppJS: "/frontend_es5/app.js",
|
||||||
es5CoreJS: "/frontend_es5/core.js",
|
es5CoreJS: "/frontend_es5/core.js",
|
||||||
es5CustomPanelJS: "/frontend_es5/custom-panel.js",
|
es5CustomPanelJS: "/frontend_es5/custom-panel.js",
|
||||||
@@ -120,6 +123,7 @@ gulp.task("gen-index-app-prod", (done) => {
|
|||||||
latestCoreJS: latestManifest["core.js"],
|
latestCoreJS: latestManifest["core.js"],
|
||||||
latestCustomPanelJS: latestManifest["custom-panel.js"],
|
latestCustomPanelJS: latestManifest["custom-panel.js"],
|
||||||
|
|
||||||
|
es5Compatibility: es5Manifest["compatibility.js"],
|
||||||
es5AppJS: es5Manifest["app.js"],
|
es5AppJS: es5Manifest["app.js"],
|
||||||
es5CoreJS: es5Manifest["core.js"],
|
es5CoreJS: es5Manifest["core.js"],
|
||||||
es5CustomPanelJS: es5Manifest["custom-panel.js"],
|
es5CustomPanelJS: es5Manifest["custom-panel.js"],
|
||||||
@@ -206,6 +210,7 @@ gulp.task("gen-index-demo-dev", (done) => {
|
|||||||
const content = renderDemoTemplate("index", {
|
const content = renderDemoTemplate("index", {
|
||||||
latestDemoJS: "/frontend_latest/main.js",
|
latestDemoJS: "/frontend_latest/main.js",
|
||||||
|
|
||||||
|
es5Compatibility: "/frontend_es5/compatibility.js",
|
||||||
es5DemoJS: "/frontend_es5/main.js",
|
es5DemoJS: "/frontend_es5/main.js",
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -228,6 +233,7 @@ gulp.task("gen-index-demo-prod", (done) => {
|
|||||||
const content = renderDemoTemplate("index", {
|
const content = renderDemoTemplate("index", {
|
||||||
latestDemoJS: latestManifest["main.js"],
|
latestDemoJS: latestManifest["main.js"],
|
||||||
|
|
||||||
|
es5Compatibility: es5Manifest["compatibility.js"],
|
||||||
es5DemoJS: es5Manifest["main.js"],
|
es5DemoJS: es5Manifest["main.js"],
|
||||||
});
|
});
|
||||||
const minified = minifyHtml(content);
|
const minified = minifyHtml(content);
|
||||||
|
@@ -1,10 +1,7 @@
|
|||||||
// Run demo develop mode
|
// Run demo develop mode
|
||||||
const gulp = require("gulp");
|
const gulp = require("gulp");
|
||||||
const fs = require("fs");
|
|
||||||
const path = require("path");
|
|
||||||
|
|
||||||
const env = require("../env");
|
const env = require("../env");
|
||||||
const paths = require("../paths");
|
|
||||||
|
|
||||||
require("./clean.js");
|
require("./clean.js");
|
||||||
require("./translations.js");
|
require("./translations.js");
|
||||||
@@ -15,31 +12,6 @@ require("./service-worker.js");
|
|||||||
require("./entry-html.js");
|
require("./entry-html.js");
|
||||||
require("./rollup.js");
|
require("./rollup.js");
|
||||||
|
|
||||||
gulp.task("gather-gallery-demos", async function gatherDemos() {
|
|
||||||
const files = await fs.promises.readdir(
|
|
||||||
path.resolve(paths.gallery_dir, "src/demos")
|
|
||||||
);
|
|
||||||
|
|
||||||
let content = "export const DEMOS = {\n";
|
|
||||||
|
|
||||||
for (const file of files) {
|
|
||||||
const demoId = path.basename(file, ".ts");
|
|
||||||
const demoPath = "../src/demos/" + demoId;
|
|
||||||
content += ` "${demoId}": () => import("${demoPath}"),\n`;
|
|
||||||
}
|
|
||||||
|
|
||||||
content += "};";
|
|
||||||
|
|
||||||
const galleryBuild = path.resolve(paths.gallery_dir, "build");
|
|
||||||
|
|
||||||
fs.mkdirSync(galleryBuild, { recursive: true });
|
|
||||||
fs.writeFileSync(
|
|
||||||
path.resolve(galleryBuild, "import-demos.ts"),
|
|
||||||
content,
|
|
||||||
"utf-8"
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
gulp.task(
|
gulp.task(
|
||||||
"develop-gallery",
|
"develop-gallery",
|
||||||
gulp.series(
|
gulp.series(
|
||||||
@@ -48,11 +20,7 @@ gulp.task(
|
|||||||
},
|
},
|
||||||
"clean-gallery",
|
"clean-gallery",
|
||||||
"translations-enable-merge-backend",
|
"translations-enable-merge-backend",
|
||||||
gulp.parallel(
|
gulp.parallel("gen-icons-json", "build-translations"),
|
||||||
"gen-icons-json",
|
|
||||||
"build-translations",
|
|
||||||
"gather-gallery-demos"
|
|
||||||
),
|
|
||||||
"copy-static-gallery",
|
"copy-static-gallery",
|
||||||
"gen-index-gallery-dev",
|
"gen-index-gallery-dev",
|
||||||
env.useRollup() ? "rollup-dev-server-gallery" : "webpack-dev-server-gallery"
|
env.useRollup() ? "rollup-dev-server-gallery" : "webpack-dev-server-gallery"
|
||||||
@@ -67,11 +35,7 @@ gulp.task(
|
|||||||
},
|
},
|
||||||
"clean-gallery",
|
"clean-gallery",
|
||||||
"translations-enable-merge-backend",
|
"translations-enable-merge-backend",
|
||||||
gulp.parallel(
|
gulp.parallel("gen-icons-json", "build-translations"),
|
||||||
"gen-icons-json",
|
|
||||||
"build-translations",
|
|
||||||
"gather-gallery-demos"
|
|
||||||
),
|
|
||||||
"copy-static-gallery",
|
"copy-static-gallery",
|
||||||
env.useRollup() ? "rollup-prod-gallery" : "webpack-prod-gallery",
|
env.useRollup() ? "rollup-prod-gallery" : "webpack-prod-gallery",
|
||||||
"gen-index-gallery-prod"
|
"gen-index-gallery-prod"
|
||||||
|
@@ -36,13 +36,11 @@ function copyMdiIcons(staticDir) {
|
|||||||
function copyPolyfills(staticDir) {
|
function copyPolyfills(staticDir) {
|
||||||
const staticPath = genStaticPath(staticDir);
|
const staticPath = genStaticPath(staticDir);
|
||||||
|
|
||||||
// For custom panels using ES5 builds that don't use Babel 7+
|
// Web Component polyfills and adapters
|
||||||
copyFileDir(
|
copyFileDir(
|
||||||
npmPath("@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"),
|
npmPath("@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"),
|
||||||
staticPath("polyfills/")
|
staticPath("polyfills/")
|
||||||
);
|
);
|
||||||
|
|
||||||
// Web Component polyfills and adapters
|
|
||||||
copyFileDir(
|
copyFileDir(
|
||||||
npmPath("@webcomponents/webcomponentsjs/webcomponents-bundle.js"),
|
npmPath("@webcomponents/webcomponentsjs/webcomponents-bundle.js"),
|
||||||
staticPath("polyfills/")
|
staticPath("polyfills/")
|
||||||
|
@@ -1,9 +1,6 @@
|
|||||||
const gulp = require("gulp");
|
const gulp = require("gulp");
|
||||||
const fs = require("fs");
|
|
||||||
const path = require("path");
|
|
||||||
|
|
||||||
const env = require("../env");
|
const env = require("../env");
|
||||||
const paths = require("../paths");
|
|
||||||
|
|
||||||
require("./clean.js");
|
require("./clean.js");
|
||||||
require("./gen-icons-json.js");
|
require("./gen-icons-json.js");
|
||||||
@@ -11,24 +8,6 @@ require("./webpack.js");
|
|||||||
require("./compress.js");
|
require("./compress.js");
|
||||||
require("./rollup.js");
|
require("./rollup.js");
|
||||||
|
|
||||||
async function writeEntrypointJS() {
|
|
||||||
// We ship two builds and we need to do feature detection on what version to load.
|
|
||||||
fs.mkdirSync(paths.hassio_output_root, { recursive: true });
|
|
||||||
fs.writeFileSync(
|
|
||||||
path.resolve(paths.hassio_output_root, "entrypoint.js"),
|
|
||||||
`
|
|
||||||
try {
|
|
||||||
new Function("import('${paths.hassio_publicPath}/frontend_latest/entrypoint.js')")();
|
|
||||||
} catch (err) {
|
|
||||||
var el = document.createElement('script');
|
|
||||||
el.src = '${paths.hassio_publicPath}/frontend_es5/entrypoint.js';
|
|
||||||
document.body.appendChild(el);
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
{ encoding: "utf-8" }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
gulp.task(
|
gulp.task(
|
||||||
"develop-hassio",
|
"develop-hassio",
|
||||||
gulp.series(
|
gulp.series(
|
||||||
@@ -37,7 +16,6 @@ gulp.task(
|
|||||||
},
|
},
|
||||||
"clean-hassio",
|
"clean-hassio",
|
||||||
"gen-icons-json",
|
"gen-icons-json",
|
||||||
writeEntrypointJS,
|
|
||||||
env.useRollup() ? "rollup-watch-hassio" : "webpack-watch-hassio"
|
env.useRollup() ? "rollup-watch-hassio" : "webpack-watch-hassio"
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
@@ -51,7 +29,6 @@ gulp.task(
|
|||||||
"clean-hassio",
|
"clean-hassio",
|
||||||
"gen-icons-json",
|
"gen-icons-json",
|
||||||
env.useRollup() ? "rollup-prod-hassio" : "webpack-prod-hassio",
|
env.useRollup() ? "rollup-prod-hassio" : "webpack-prod-hassio",
|
||||||
writeEntrypointJS,
|
|
||||||
...// Don't compress running tests
|
...// Don't compress running tests
|
||||||
(env.isTest() ? [] : ["compress-hassio"])
|
(env.isTest() ? [] : ["compress-hassio"])
|
||||||
)
|
)
|
||||||
|
@@ -92,7 +92,11 @@ gulp.task("rollup-watch-app", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
gulp.task("rollup-watch-hassio", () => {
|
gulp.task("rollup-watch-hassio", () => {
|
||||||
watchRollup(rollupConfig.createHassioConfig, ["hassio/src/**"]);
|
watchRollup(
|
||||||
|
// Force latestBuild = false for hassio config.
|
||||||
|
(conf) => rollupConfig.createHassioConfig({ ...conf, latestBuild: false }),
|
||||||
|
["hassio/src/**"]
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
gulp.task("rollup-dev-server-demo", () => {
|
gulp.task("rollup-dev-server-demo", () => {
|
||||||
@@ -133,7 +137,12 @@ gulp.task(
|
|||||||
);
|
);
|
||||||
|
|
||||||
gulp.task("rollup-prod-hassio", () =>
|
gulp.task("rollup-prod-hassio", () =>
|
||||||
bothBuilds(rollupConfig.createHassioConfig, { isProdBuild: true })
|
buildRollup(
|
||||||
|
rollupConfig.createHassioConfig({
|
||||||
|
isProdBuild: true,
|
||||||
|
latestBuild: false,
|
||||||
|
})
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
gulp.task("rollup-prod-gallery", () =>
|
gulp.task("rollup-prod-gallery", () =>
|
||||||
|
@@ -129,7 +129,7 @@ gulp.task("webpack-watch-hassio", () => {
|
|||||||
webpack(
|
webpack(
|
||||||
createHassioConfig({
|
createHassioConfig({
|
||||||
isProdBuild: false,
|
isProdBuild: false,
|
||||||
latestBuild: true,
|
latestBuild: false,
|
||||||
})
|
})
|
||||||
).watch({}, handler());
|
).watch({}, handler());
|
||||||
});
|
});
|
||||||
@@ -139,8 +139,9 @@ gulp.task(
|
|||||||
() =>
|
() =>
|
||||||
new Promise((resolve) =>
|
new Promise((resolve) =>
|
||||||
webpack(
|
webpack(
|
||||||
bothBuilds(createHassioConfig, {
|
createHassioConfig({
|
||||||
isProdBuild: true,
|
isProdBuild: true,
|
||||||
|
latestBuild: false,
|
||||||
}),
|
}),
|
||||||
handler(resolve)
|
handler(resolve)
|
||||||
)
|
)
|
||||||
|
@@ -34,7 +34,7 @@ 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_publicPath: "/api/hassio/app",
|
hassio_publicPath: "/api/hassio/app/",
|
||||||
|
|
||||||
translations_src: path.resolve(__dirname, "../src/translations"),
|
translations_src: path.resolve(__dirname, "../src/translations"),
|
||||||
};
|
};
|
||||||
|
@@ -14,6 +14,32 @@ module.exports = function (userOptions = {}) {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
name: "ignore",
|
name: "ignore",
|
||||||
|
resolveId(importee, importer) {
|
||||||
|
// Only use ignore to intercept imports that we don't control
|
||||||
|
// inside node_module dependencies.
|
||||||
|
if (
|
||||||
|
importee.endsWith("commonjsHelpers.js") ||
|
||||||
|
importee.endsWith("rollupPluginBabelHelpers.js") ||
|
||||||
|
importee.endsWith("?commonjs-proxy") ||
|
||||||
|
!importer ||
|
||||||
|
!importer.includes("/node_modules/")
|
||||||
|
) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
let fullPath;
|
||||||
|
try {
|
||||||
|
fullPath = importee.startsWith(".")
|
||||||
|
? path.resolve(importee, importer)
|
||||||
|
: require.resolve(importee);
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Error in ignore plugin", { importee, importer }, err);
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
|
||||||
|
return files.some((toIgnorePath) => fullPath.startsWith(toIgnorePath))
|
||||||
|
? fullPath
|
||||||
|
: null;
|
||||||
|
},
|
||||||
|
|
||||||
load(id) {
|
load(id) {
|
||||||
return files.some((toIgnorePath) => id.startsWith(toIgnorePath))
|
return files.some((toIgnorePath) => id.startsWith(toIgnorePath))
|
||||||
|
@@ -70,9 +70,7 @@ const createWebpackConfig = ({
|
|||||||
if (
|
if (
|
||||||
!context.includes("/node_modules/") ||
|
!context.includes("/node_modules/") ||
|
||||||
// calling define.amd will call require("!!webpack amd options")
|
// calling define.amd will call require("!!webpack amd options")
|
||||||
resource.startsWith("!!webpack") ||
|
resource.startsWith("!!webpack")
|
||||||
// loaded by webpack dev server but doesn't exist.
|
|
||||||
resource === "webpack/hot"
|
|
||||||
) {
|
) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -82,11 +80,7 @@ const createWebpackConfig = ({
|
|||||||
? path.resolve(context, resource)
|
? path.resolve(context, resource)
|
||||||
: require.resolve(resource);
|
: require.resolve(resource);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(
|
console.error("Error in ignore plugin", resource, context);
|
||||||
"Error in Home Assistant ignore plugin",
|
|
||||||
resource,
|
|
||||||
context
|
|
||||||
);
|
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -37,21 +37,24 @@
|
|||||||
<body>
|
<body>
|
||||||
<%= renderTemplate('_js_base') %>
|
<%= renderTemplate('_js_base') %>
|
||||||
|
|
||||||
<script>
|
<script type="module" crossorigin="use-credentials">
|
||||||
import("<%= latestLauncherJS %>");
|
import "<%= latestLauncherJS %>";
|
||||||
window.latestJS = true;
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script>
|
<script nomodule>
|
||||||
if (!window.latestJS) {
|
(function() {
|
||||||
<% if (useRollup) { %>
|
// // Safari 10.1 supports type=module but ignores nomodule, so we add this check.
|
||||||
_ls("/static/js/s.min.js").onload = function() {
|
if (!isS101) {
|
||||||
System.import("<%= es5LauncherJS %>");
|
_ls("/static/polyfills/custom-elements-es5-adapter.js");
|
||||||
};
|
<% if (useRollup) { %>
|
||||||
<% } else { %>
|
_ls("/static/js/s.min.js").onload = function() {
|
||||||
_ls("<%= es5LauncherJS %>");
|
System.import("<%= es5LauncherJS %>");
|
||||||
<% } %>
|
};
|
||||||
}
|
<% } else { %>
|
||||||
|
_ls("<%= es5LauncherJS %>");
|
||||||
|
<% } %>
|
||||||
|
}
|
||||||
|
})();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<hc-layout subtitle="FAQ">
|
<hc-layout subtitle="FAQ">
|
||||||
@@ -251,7 +254,7 @@ http:
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
var _gaq = [["_setAccount", "UA-57927901-9"], ["_trackPageview"]];
|
var _gaq = [["_setAccount", "UA-57927901-9"], ["_trackPageview"]];
|
||||||
(function (d, t) {
|
(function(d, t) {
|
||||||
var g = d.createElement(t),
|
var g = d.createElement(t),
|
||||||
s = d.getElementsByTagName(t)[0];
|
s = d.getElementsByTagName(t)[0];
|
||||||
g.src =
|
g.src =
|
||||||
|
@@ -28,21 +28,24 @@
|
|||||||
|
|
||||||
<hc-connect></hc-connect>
|
<hc-connect></hc-connect>
|
||||||
|
|
||||||
<script>
|
<script type="module" crossorigin="use-credentials">
|
||||||
import("<%= latestLauncherJS %>");
|
import "<%= latestLauncherJS %>";
|
||||||
window.latestJS = true;
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script>
|
<script nomodule>
|
||||||
if (!window.latestJS) {
|
(function() {
|
||||||
<% if (useRollup) { %>
|
// // Safari 10.1 supports type=module but ignores nomodule, so we add this check.
|
||||||
_ls("/static/js/s.min.js").onload = function() {
|
if (!isS101) {
|
||||||
System.import("<%= es5LauncherJS %>");
|
_ls("/static/polyfills/custom-elements-es5-adapter.js");
|
||||||
};
|
<% if (useRollup) { %>
|
||||||
<% } else { %>
|
_ls("/static/js/s.min.js").onload = function() {
|
||||||
_ls("<%= es5LauncherJS %>");
|
System.import("<%= es5LauncherJS %>");
|
||||||
<% } %>
|
};
|
||||||
}
|
<% } else { %>
|
||||||
|
_ls("<%= es5LauncherJS %>");
|
||||||
|
<% } %>
|
||||||
|
}
|
||||||
|
})();
|
||||||
</script>
|
</script>
|
||||||
<script>
|
<script>
|
||||||
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
||||||
|
@@ -192,8 +192,6 @@ export class HcMain extends HassElement {
|
|||||||
this._handleNewLovelaceConfig(lovelaceConfig)
|
this._handleNewLovelaceConfig(lovelaceConfig)
|
||||||
);
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// eslint-disable-next-line
|
|
||||||
console.log("Error fetching Lovelace configuration", err, msg);
|
|
||||||
// Generate a Lovelace config.
|
// Generate a Lovelace config.
|
||||||
this._unsubLovelace = () => undefined;
|
this._unsubLovelace = () => undefined;
|
||||||
await this._generateLovelaceConfig();
|
await this._generateLovelaceConfig();
|
||||||
|
@@ -1,8 +1,11 @@
|
|||||||
const { createCastConfig } = require("../build-scripts/webpack.js");
|
const { createCastConfig } = require("../build-scripts/webpack.js");
|
||||||
const { isProdBuild, isStatsBuild } = require("../build-scripts/env.js");
|
const { isProdBuild } = require("../build-scripts/env.js");
|
||||||
|
|
||||||
|
// File just used for stats builds
|
||||||
|
|
||||||
|
const latestBuild = true;
|
||||||
|
|
||||||
module.exports = createCastConfig({
|
module.exports = createCastConfig({
|
||||||
isProdBuild: isProdBuild(),
|
isProdBuild: isProdBuild(),
|
||||||
isStatsBuild: isStatsBuild(),
|
latestBuild,
|
||||||
latestBuild: true,
|
|
||||||
});
|
});
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
import "@material/mwc-button";
|
import "@material/mwc-button";
|
||||||
|
import "@polymer/paper-spinner/paper-spinner-lite";
|
||||||
import {
|
import {
|
||||||
css,
|
css,
|
||||||
CSSResult,
|
CSSResult,
|
||||||
@@ -9,7 +10,6 @@ import {
|
|||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
import { until } from "lit-html/directives/until";
|
import { until } from "lit-html/directives/until";
|
||||||
import "../../../src/components/ha-card";
|
import "../../../src/components/ha-card";
|
||||||
import "../../../src/components/ha-circular-progress";
|
|
||||||
import { LovelaceCardConfig } from "../../../src/data/lovelace";
|
import { LovelaceCardConfig } from "../../../src/data/lovelace";
|
||||||
import { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
|
import { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
|
||||||
import { Lovelace, LovelaceCard } from "../../../src/panels/lovelace/types";
|
import { Lovelace, LovelaceCard } from "../../../src/panels/lovelace/types";
|
||||||
@@ -49,7 +49,7 @@ export class HADemoCard extends LitElement implements LovelaceCard {
|
|||||||
<div class="picker">
|
<div class="picker">
|
||||||
<div class="label">
|
<div class="label">
|
||||||
${this._switching
|
${this._switching
|
||||||
? html`<ha-circular-progress active></ha-circular-progress>`
|
? html` <paper-spinner-lite active></paper-spinner-lite> `
|
||||||
: until(
|
: until(
|
||||||
selectedDemoConfig.then(
|
selectedDemoConfig.then(
|
||||||
(conf) => html`
|
(conf) => html`
|
||||||
|
@@ -1,4 +1,3 @@
|
|||||||
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";
|
||||||
import {
|
import {
|
||||||
|
@@ -86,26 +86,30 @@
|
|||||||
<%= renderTemplate('_js_base') %>
|
<%= renderTemplate('_js_base') %>
|
||||||
<%= renderTemplate('_preload_roboto') %>
|
<%= renderTemplate('_preload_roboto') %>
|
||||||
|
|
||||||
<script>
|
<script type="module" src="<%= latestDemoJS %>"></script>
|
||||||
import("<%= latestDemoJS %>");
|
|
||||||
window.latestJS = true;
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<script>
|
<script nomodule>
|
||||||
if (!window.latestJS) {
|
(function() {
|
||||||
<% if (useRollup) { %>
|
// // Safari 10.1 supports type=module but ignores nomodule, so we add this check.
|
||||||
_ls("/static/js/s.min.js").onload = function() {
|
if (!isS101) {
|
||||||
System.import("<%= es5DemoJS %>");
|
_ls("/static/polyfills/custom-elements-es5-adapter.js");
|
||||||
};
|
<% if (useRollup) { %>
|
||||||
<% } else { %>
|
_ls("/static/js/s.min.js").onload = function() {
|
||||||
_ls("<%= es5DemoJS %>");
|
System.import("<%= es5Compatibility %>").then(function() {
|
||||||
<% } %>
|
System.import("<%= es5DemoJS %>");
|
||||||
}
|
});
|
||||||
|
};
|
||||||
|
<% } else { %>
|
||||||
|
_ls("<%= es5Compatibility %>");
|
||||||
|
_ls("<%= es5DemoJS %>");
|
||||||
|
<% } %>
|
||||||
|
}
|
||||||
|
})();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
var _gaq = [["_setAccount", "UA-57927901-5"], ["_trackPageview"]];
|
var _gaq = [["_setAccount", "UA-57927901-5"], ["_trackPageview"]];
|
||||||
(function (d, t) {
|
(function(d, t) {
|
||||||
var g = d.createElement(t),
|
var g = d.createElement(t),
|
||||||
s = d.getElementsByTagName(t)[0];
|
s = d.getElementsByTagName(t)[0];
|
||||||
g.src =
|
g.src =
|
||||||
|
@@ -3,7 +3,6 @@ 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-switch";
|
import "../../../src/components/ha-switch";
|
||||||
import "../../../src/components/ha-formfield";
|
|
||||||
import "./demo-card";
|
import "./demo-card";
|
||||||
|
|
||||||
class DemoCards extends PolymerElement {
|
class DemoCards extends PolymerElement {
|
||||||
@@ -27,10 +26,9 @@ class DemoCards extends PolymerElement {
|
|||||||
</style>
|
</style>
|
||||||
<app-toolbar>
|
<app-toolbar>
|
||||||
<div class="filters">
|
<div class="filters">
|
||||||
<ha-formfield label="Show config">
|
<ha-switch checked="[[_showConfig]]" on-change="_showConfigToggled">
|
||||||
<ha-switch checked="[[_showConfig]]" on-change="_showConfigToggled">
|
Show config
|
||||||
</ha-switch>
|
</ha-switch>
|
||||||
</ha-formfield>
|
|
||||||
</div>
|
</div>
|
||||||
</app-toolbar>
|
</app-toolbar>
|
||||||
<div class="cards">
|
<div class="cards">
|
||||||
|
@@ -11,7 +11,9 @@ import { PolymerElement } from "@polymer/polymer/polymer-element";
|
|||||||
import "../../src/components/ha-card";
|
import "../../src/components/ha-card";
|
||||||
import "../../src/managers/notification-manager";
|
import "../../src/managers/notification-manager";
|
||||||
import "../../src/styles/polymer-ha-style";
|
import "../../src/styles/polymer-ha-style";
|
||||||
import { DEMOS } from "../build/import-demos";
|
|
||||||
|
// eslint-disable-next-line no-undef
|
||||||
|
const DEMOS = require.context("./demos", true, /^(.*\.(ts$))[^.]*$/im);
|
||||||
|
|
||||||
const fixPath = (path) => path.substr(2, path.length - 5);
|
const fixPath = (path) => path.substr(2, path.length - 5);
|
||||||
|
|
||||||
@@ -161,7 +163,7 @@ class HaGallery extends PolymerElement {
|
|||||||
},
|
},
|
||||||
_demos: {
|
_demos: {
|
||||||
type: Array,
|
type: Array,
|
||||||
value: Object.keys(DEMOS),
|
value: DEMOS.keys().map(fixPath),
|
||||||
},
|
},
|
||||||
_lovelaceDemos: {
|
_lovelaceDemos: {
|
||||||
type: Array,
|
type: Array,
|
||||||
@@ -208,7 +210,7 @@ class HaGallery extends PolymerElement {
|
|||||||
while (root.lastChild) root.removeChild(root.lastChild);
|
while (root.lastChild) root.removeChild(root.lastChild);
|
||||||
|
|
||||||
if (demo) {
|
if (demo) {
|
||||||
DEMOS[demo]();
|
DEMOS(`./${demo}.ts`);
|
||||||
const el = document.createElement(demo);
|
const el = document.createElement(demo);
|
||||||
root.appendChild(el);
|
root.appendChild(el);
|
||||||
}
|
}
|
||||||
|
@@ -1,8 +1,5 @@
|
|||||||
const { createGalleryConfig } = require("../build-scripts/webpack.js");
|
const { createGalleryConfig } = require("../build-scripts/webpack.js");
|
||||||
const { isProdBuild, isStatsBuild } = require("../build-scripts/env.js");
|
|
||||||
|
|
||||||
module.exports = createGalleryConfig({
|
module.exports = createGalleryConfig({
|
||||||
isProdBuild: isProdBuild(),
|
|
||||||
isStatsBuild: isStatsBuild(),
|
|
||||||
latestBuild: true,
|
latestBuild: true,
|
||||||
});
|
});
|
||||||
|
@@ -22,7 +22,7 @@ import "../../../src/layouts/hass-tabs-subpage";
|
|||||||
import "../../../src/layouts/loading-screen";
|
import "../../../src/layouts/loading-screen";
|
||||||
import { HomeAssistant, Route } from "../../../src/types";
|
import { HomeAssistant, Route } from "../../../src/types";
|
||||||
import { showRepositoriesDialog } from "../dialogs/repositories/show-dialog-repositories";
|
import { showRepositoriesDialog } from "../dialogs/repositories/show-dialog-repositories";
|
||||||
import { supervisorTabs } from "../hassio-tabs";
|
import { supervisorTabs } from "../hassio-panel";
|
||||||
import "./hassio-addon-repository";
|
import "./hassio-addon-repository";
|
||||||
|
|
||||||
const sortRepos = (a: HassioAddonRepository, b: HassioAddonRepository) => {
|
const sortRepos = (a: HassioAddonRepository, b: HassioAddonRepository) => {
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
import "@polymer/paper-spinner/paper-spinner-lite";
|
||||||
import {
|
import {
|
||||||
css,
|
css,
|
||||||
CSSResult,
|
CSSResult,
|
||||||
@@ -11,7 +12,6 @@ import { HassioAddonDetails } 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 { 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";
|
||||||
@@ -24,7 +24,7 @@ class HassioAddonConfigDashboard extends LitElement {
|
|||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
if (!this.addon) {
|
if (!this.addon) {
|
||||||
return html`<ha-circular-progress active></ha-circular-progress>`;
|
return html` <paper-spinner-lite active></paper-spinner-lite> `;
|
||||||
}
|
}
|
||||||
return html`
|
return html`
|
||||||
<div class="content">
|
<div class="content">
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
import "@polymer/paper-spinner/paper-spinner-lite";
|
||||||
import {
|
import {
|
||||||
css,
|
css,
|
||||||
CSSResult,
|
CSSResult,
|
||||||
@@ -13,7 +14,6 @@ import {
|
|||||||
HassioAddonDetails,
|
HassioAddonDetails,
|
||||||
} from "../../../../src/data/hassio/addon";
|
} from "../../../../src/data/hassio/addon";
|
||||||
import "../../../../src/layouts/loading-screen";
|
import "../../../../src/layouts/loading-screen";
|
||||||
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";
|
||||||
@@ -35,7 +35,7 @@ class HassioAddonDocumentationDashboard extends LitElement {
|
|||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
if (!this.addon) {
|
if (!this.addon) {
|
||||||
return html`<ha-circular-progress active></ha-circular-progress>`;
|
return html` <paper-spinner-lite active></paper-spinner-lite> `;
|
||||||
}
|
}
|
||||||
return html`
|
return html`
|
||||||
<div class="content">
|
<div class="content">
|
||||||
|
@@ -4,6 +4,7 @@ import {
|
|||||||
mdiInformationVariant,
|
mdiInformationVariant,
|
||||||
mdiMathLog,
|
mdiMathLog,
|
||||||
} from "@mdi/js";
|
} from "@mdi/js";
|
||||||
|
import "@polymer/paper-spinner/paper-spinner-lite";
|
||||||
import {
|
import {
|
||||||
css,
|
css,
|
||||||
CSSResult,
|
CSSResult,
|
||||||
@@ -19,7 +20,6 @@ import {
|
|||||||
HassioAddonDetails,
|
HassioAddonDetails,
|
||||||
} from "../../../src/data/hassio/addon";
|
} from "../../../src/data/hassio/addon";
|
||||||
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";
|
||||||
@@ -56,7 +56,7 @@ class HassioAddonDashboard extends LitElement {
|
|||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
if (!this.addon) {
|
if (!this.addon) {
|
||||||
return html`<ha-circular-progress active></ha-circular-progress>`;
|
return html` <paper-spinner-lite active></paper-spinner-lite> `;
|
||||||
}
|
}
|
||||||
|
|
||||||
const addonTabs: PageNavigation[] = [
|
const addonTabs: PageNavigation[] = [
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
import "@polymer/paper-spinner/paper-spinner-lite";
|
||||||
import {
|
import {
|
||||||
css,
|
css,
|
||||||
CSSResult,
|
CSSResult,
|
||||||
@@ -8,7 +9,6 @@ import {
|
|||||||
TemplateResult,
|
TemplateResult,
|
||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
import { HassioAddonDetails } from "../../../../src/data/hassio/addon";
|
import { HassioAddonDetails } from "../../../../src/data/hassio/addon";
|
||||||
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";
|
||||||
@@ -24,7 +24,7 @@ class HassioAddonInfoDashboard extends LitElement {
|
|||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
if (!this.addon) {
|
if (!this.addon) {
|
||||||
return html`<ha-circular-progress active></ha-circular-progress>`;
|
return html` <paper-spinner-lite active></paper-spinner-lite> `;
|
||||||
}
|
}
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
import "@polymer/paper-spinner/paper-spinner-lite";
|
||||||
import {
|
import {
|
||||||
css,
|
css,
|
||||||
CSSResult,
|
CSSResult,
|
||||||
@@ -8,7 +9,6 @@ import {
|
|||||||
TemplateResult,
|
TemplateResult,
|
||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
import { HassioAddonDetails } from "../../../../src/data/hassio/addon";
|
import { HassioAddonDetails } from "../../../../src/data/hassio/addon";
|
||||||
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";
|
||||||
@@ -22,7 +22,7 @@ class HassioAddonLogDashboard extends LitElement {
|
|||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
if (!this.addon) {
|
if (!this.addon) {
|
||||||
return html` <ha-circular-progress active></ha-circular-progress> `;
|
return html` <paper-spinner-lite active></paper-spinner-lite> `;
|
||||||
}
|
}
|
||||||
return html`
|
return html`
|
||||||
<div class="content">
|
<div class="content">
|
||||||
|
@@ -15,7 +15,7 @@ import {
|
|||||||
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";
|
||||||
import { supervisorTabs } from "../hassio-tabs";
|
import { supervisorTabs } from "../hassio-panel";
|
||||||
import "./hassio-addons";
|
import "./hassio-addons";
|
||||||
import "./hassio-update";
|
import "./hassio-update";
|
||||||
|
|
||||||
|
@@ -5,7 +5,7 @@ import "@polymer/paper-input/paper-input";
|
|||||||
import type { PaperInputElement } from "@polymer/paper-input/paper-input";
|
import type { PaperInputElement } from "@polymer/paper-input/paper-input";
|
||||||
import "@polymer/paper-item/paper-item";
|
import "@polymer/paper-item/paper-item";
|
||||||
import "@polymer/paper-item/paper-item-body";
|
import "@polymer/paper-item/paper-item-body";
|
||||||
import "../../../../src/components/ha-circular-progress";
|
import "@polymer/paper-spinner/paper-spinner";
|
||||||
import {
|
import {
|
||||||
css,
|
css,
|
||||||
CSSResult,
|
CSSResult,
|
||||||
@@ -108,7 +108,7 @@ class HassioRepositoriesDialog extends LitElement {
|
|||||||
></paper-input>
|
></paper-input>
|
||||||
<mwc-button @click=${this._addRepository}>
|
<mwc-button @click=${this._addRepository}>
|
||||||
${this._prosessing
|
${this._prosessing
|
||||||
? html`<ha-circular-progress active></ha-circular-progress>`
|
? html`<paper-spinner active></paper-spinner>`
|
||||||
: "Add"}
|
: "Add"}
|
||||||
</mwc-button>
|
</mwc-button>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -1,6 +1,9 @@
|
|||||||
import "../../src/resources/compatibility";
|
window.loadES5Adapter().then(() => {
|
||||||
import "../../src/resources/roboto";
|
// eslint-disable-next-line
|
||||||
import "./hassio-main";
|
import(/* webpackChunkName: "roboto" */ "../../src/resources/roboto");
|
||||||
|
// eslint-disable-next-line
|
||||||
|
import(/* webpackChunkName: "hassio-main" */ "./hassio-main");
|
||||||
|
});
|
||||||
|
|
||||||
const styleEl = document.createElement("style");
|
const styleEl = document.createElement("style");
|
||||||
styleEl.innerHTML = `
|
styleEl.innerHTML = `
|
||||||
|
@@ -14,9 +14,7 @@ import {
|
|||||||
createHassioSession,
|
createHassioSession,
|
||||||
fetchHassioHomeAssistantInfo,
|
fetchHassioHomeAssistantInfo,
|
||||||
fetchHassioSupervisorInfo,
|
fetchHassioSupervisorInfo,
|
||||||
fetchHassioInfo,
|
|
||||||
HassioHomeAssistantInfo,
|
HassioHomeAssistantInfo,
|
||||||
HassioInfo,
|
|
||||||
HassioPanelInfo,
|
HassioPanelInfo,
|
||||||
HassioSupervisorInfo,
|
HassioSupervisorInfo,
|
||||||
} from "../../src/data/hassio/supervisor";
|
} from "../../src/data/hassio/supervisor";
|
||||||
@@ -77,8 +75,6 @@ class HassioMain extends ProvideHassLitMixin(HassRouterPage) {
|
|||||||
|
|
||||||
@property() private _hostInfo: HassioHostInfo;
|
@property() private _hostInfo: HassioHostInfo;
|
||||||
|
|
||||||
@property() private _hassioInfo?: HassioInfo;
|
|
||||||
|
|
||||||
@property() private _hassOsInfo?: HassioHassOSInfo;
|
@property() private _hassOsInfo?: HassioHassOSInfo;
|
||||||
|
|
||||||
@property() private _hassInfo: HassioHomeAssistantInfo;
|
@property() private _hassInfo: HassioHomeAssistantInfo;
|
||||||
@@ -151,7 +147,6 @@ class HassioMain extends ProvideHassLitMixin(HassRouterPage) {
|
|||||||
hass: this.hass,
|
hass: this.hass,
|
||||||
narrow: this.narrow,
|
narrow: this.narrow,
|
||||||
supervisorInfo: this._supervisorInfo,
|
supervisorInfo: this._supervisorInfo,
|
||||||
hassioInfo: this._hassioInfo,
|
|
||||||
hostInfo: this._hostInfo,
|
hostInfo: this._hostInfo,
|
||||||
hassInfo: this._hassInfo,
|
hassInfo: this._hassInfo,
|
||||||
hassOsInfo: this._hassOsInfo,
|
hassOsInfo: this._hassOsInfo,
|
||||||
@@ -161,7 +156,6 @@ class HassioMain extends ProvideHassLitMixin(HassRouterPage) {
|
|||||||
el.hass = this.hass;
|
el.hass = this.hass;
|
||||||
el.narrow = this.narrow;
|
el.narrow = this.narrow;
|
||||||
el.supervisorInfo = this._supervisorInfo;
|
el.supervisorInfo = this._supervisorInfo;
|
||||||
el.hassioInfo = this._hassioInfo;
|
|
||||||
el.hostInfo = this._hostInfo;
|
el.hostInfo = this._hostInfo;
|
||||||
el.hassInfo = this._hassInfo;
|
el.hassInfo = this._hassInfo;
|
||||||
el.hassOsInfo = this._hassOsInfo;
|
el.hassOsInfo = this._hassOsInfo;
|
||||||
@@ -175,14 +169,12 @@ class HassioMain extends ProvideHassLitMixin(HassRouterPage) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const [supervisorInfo, hostInfo, hassInfo, hassioInfo] = await Promise.all([
|
const [supervisorInfo, hostInfo, hassInfo] = await Promise.all([
|
||||||
fetchHassioSupervisorInfo(this.hass),
|
fetchHassioSupervisorInfo(this.hass),
|
||||||
fetchHassioHostInfo(this.hass),
|
fetchHassioHostInfo(this.hass),
|
||||||
fetchHassioHomeAssistantInfo(this.hass),
|
fetchHassioHomeAssistantInfo(this.hass),
|
||||||
fetchHassioInfo(this.hass),
|
|
||||||
]);
|
]);
|
||||||
this._supervisorInfo = supervisorInfo;
|
this._supervisorInfo = supervisorInfo;
|
||||||
this._hassioInfo = hassioInfo;
|
|
||||||
this._hostInfo = hostInfo;
|
this._hostInfo = hostInfo;
|
||||||
this._hassInfo = hassInfo;
|
this._hassInfo = hassInfo;
|
||||||
|
|
||||||
|
@@ -3,7 +3,6 @@ import { HassioHassOSInfo, HassioHostInfo } from "../../src/data/hassio/host";
|
|||||||
import {
|
import {
|
||||||
HassioHomeAssistantInfo,
|
HassioHomeAssistantInfo,
|
||||||
HassioSupervisorInfo,
|
HassioSupervisorInfo,
|
||||||
HassioInfo,
|
|
||||||
} from "../../src/data/hassio/supervisor";
|
} from "../../src/data/hassio/supervisor";
|
||||||
import {
|
import {
|
||||||
HassRouterPage,
|
HassRouterPage,
|
||||||
@@ -27,8 +26,6 @@ class HassioPanelRouter extends HassRouterPage {
|
|||||||
|
|
||||||
@property({ attribute: false }) public supervisorInfo: HassioSupervisorInfo;
|
@property({ attribute: false }) public supervisorInfo: HassioSupervisorInfo;
|
||||||
|
|
||||||
@property({ attribute: false }) public hassioInfo!: HassioInfo;
|
|
||||||
|
|
||||||
@property({ attribute: false }) public hostInfo: HassioHostInfo;
|
@property({ attribute: false }) public hostInfo: HassioHostInfo;
|
||||||
|
|
||||||
@property({ attribute: false }) public hassInfo: HassioHomeAssistantInfo;
|
@property({ attribute: false }) public hassInfo: HassioHomeAssistantInfo;
|
||||||
@@ -57,7 +54,6 @@ class HassioPanelRouter extends HassRouterPage {
|
|||||||
el.route = this.route;
|
el.route = this.route;
|
||||||
el.narrow = this.narrow;
|
el.narrow = this.narrow;
|
||||||
el.supervisorInfo = this.supervisorInfo;
|
el.supervisorInfo = this.supervisorInfo;
|
||||||
el.hassioInfo = this.hassioInfo;
|
|
||||||
el.hostInfo = this.hostInfo;
|
el.hostInfo = this.hostInfo;
|
||||||
el.hassInfo = this.hassInfo;
|
el.hassInfo = this.hassInfo;
|
||||||
el.hassOsInfo = this.hassOsInfo;
|
el.hassOsInfo = this.hassOsInfo;
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
import { mdiBackupRestore, mdiCogs, mdiStore, mdiViewDashboard } from "@mdi/js";
|
||||||
import {
|
import {
|
||||||
customElement,
|
customElement,
|
||||||
html,
|
html,
|
||||||
@@ -9,11 +10,34 @@ import { HassioHassOSInfo, HassioHostInfo } from "../../src/data/hassio/host";
|
|||||||
import {
|
import {
|
||||||
HassioHomeAssistantInfo,
|
HassioHomeAssistantInfo,
|
||||||
HassioSupervisorInfo,
|
HassioSupervisorInfo,
|
||||||
HassioInfo,
|
|
||||||
} from "../../src/data/hassio/supervisor";
|
} from "../../src/data/hassio/supervisor";
|
||||||
|
import type { PageNavigation } from "../../src/layouts/hass-tabs-subpage";
|
||||||
import { HomeAssistant, Route } from "../../src/types";
|
import { HomeAssistant, Route } from "../../src/types";
|
||||||
import "./hassio-panel-router";
|
import "./hassio-panel-router";
|
||||||
|
|
||||||
|
export const supervisorTabs: PageNavigation[] = [
|
||||||
|
{
|
||||||
|
name: "Dashboard",
|
||||||
|
path: `/hassio/dashboard`,
|
||||||
|
iconPath: mdiViewDashboard,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Add-on store",
|
||||||
|
path: `/hassio/store`,
|
||||||
|
iconPath: mdiStore,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Snapshots",
|
||||||
|
path: `/hassio/snapshots`,
|
||||||
|
iconPath: mdiBackupRestore,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "System",
|
||||||
|
path: `/hassio/system`,
|
||||||
|
iconPath: mdiCogs,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
@customElement("hassio-panel")
|
@customElement("hassio-panel")
|
||||||
class HassioPanel extends LitElement {
|
class HassioPanel extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
@@ -24,8 +48,6 @@ class HassioPanel extends LitElement {
|
|||||||
|
|
||||||
@property({ attribute: false }) public supervisorInfo!: HassioSupervisorInfo;
|
@property({ attribute: false }) public supervisorInfo!: HassioSupervisorInfo;
|
||||||
|
|
||||||
@property({ attribute: false }) public hassioInfo!: HassioInfo;
|
|
||||||
|
|
||||||
@property({ attribute: false }) public hostInfo!: HassioHostInfo;
|
@property({ attribute: false }) public hostInfo!: HassioHostInfo;
|
||||||
|
|
||||||
@property({ attribute: false }) public hassInfo!: HassioHomeAssistantInfo;
|
@property({ attribute: false }) public hassInfo!: HassioHomeAssistantInfo;
|
||||||
@@ -39,7 +61,6 @@ class HassioPanel extends LitElement {
|
|||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.narrow=${this.narrow}
|
.narrow=${this.narrow}
|
||||||
.supervisorInfo=${this.supervisorInfo}
|
.supervisorInfo=${this.supervisorInfo}
|
||||||
.hassioInfo=${this.hassioInfo}
|
|
||||||
.hostInfo=${this.hostInfo}
|
.hostInfo=${this.hostInfo}
|
||||||
.hassInfo=${this.hassInfo}
|
.hassInfo=${this.hassInfo}
|
||||||
.hassOsInfo=${this.hassOsInfo}
|
.hassOsInfo=${this.hassOsInfo}
|
||||||
|
@@ -1,25 +0,0 @@
|
|||||||
import { mdiBackupRestore, mdiCogs, mdiStore, mdiViewDashboard } from "@mdi/js";
|
|
||||||
import type { PageNavigation } from "../../src/layouts/hass-tabs-subpage";
|
|
||||||
|
|
||||||
export const supervisorTabs: PageNavigation[] = [
|
|
||||||
{
|
|
||||||
name: "Dashboard",
|
|
||||||
path: `/hassio/dashboard`,
|
|
||||||
iconPath: mdiViewDashboard,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Add-on store",
|
|
||||||
path: `/hassio/store`,
|
|
||||||
iconPath: mdiStore,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Snapshots",
|
|
||||||
path: `/hassio/snapshots`,
|
|
||||||
iconPath: mdiBackupRestore,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "System",
|
|
||||||
path: `/hassio/system`,
|
|
||||||
iconPath: mdiCogs,
|
|
||||||
},
|
|
||||||
];
|
|
@@ -37,7 +37,7 @@ import { haStyle } from "../../../src/resources/styles";
|
|||||||
import { HomeAssistant, Route } from "../../../src/types";
|
import { HomeAssistant, Route } from "../../../src/types";
|
||||||
import "../components/hassio-card-content";
|
import "../components/hassio-card-content";
|
||||||
import { showHassioSnapshotDialog } from "../dialogs/snapshot/show-dialog-hassio-snapshot";
|
import { showHassioSnapshotDialog } from "../dialogs/snapshot/show-dialog-hassio-snapshot";
|
||||||
import { supervisorTabs } from "../hassio-tabs";
|
import { supervisorTabs } from "../hassio-panel";
|
||||||
import { hassioStyle } from "../resources/hassio-style";
|
import { hassioStyle } from "../resources/hassio-style";
|
||||||
|
|
||||||
interface CheckboxItem {
|
interface CheckboxItem {
|
||||||
|
@@ -19,7 +19,6 @@ import {
|
|||||||
shutdownHost,
|
shutdownHost,
|
||||||
updateOS,
|
updateOS,
|
||||||
} from "../../../src/data/hassio/host";
|
} from "../../../src/data/hassio/host";
|
||||||
import { HassioInfo } from "../../../src/data/hassio/supervisor";
|
|
||||||
import {
|
import {
|
||||||
showAlertDialog,
|
showAlertDialog,
|
||||||
showConfirmationDialog,
|
showConfirmationDialog,
|
||||||
@@ -36,8 +35,6 @@ class HassioHostInfo extends LitElement {
|
|||||||
|
|
||||||
@property() public hostInfo!: HassioHostInfoType;
|
@property() public hostInfo!: HassioHostInfoType;
|
||||||
|
|
||||||
@property({ attribute: false }) public hassioInfo!: HassioInfo;
|
|
||||||
|
|
||||||
@property() public hassOsInfo!: HassioHassOSInfo;
|
@property() public hassOsInfo!: HassioHassOSInfo;
|
||||||
|
|
||||||
@property() private _errors?: string;
|
@property() private _errors?: string;
|
||||||
@@ -57,12 +54,6 @@ class HassioHostInfo extends LitElement {
|
|||||||
<td>System</td>
|
<td>System</td>
|
||||||
<td>${this.hostInfo.operating_system}</td>
|
<td>${this.hostInfo.operating_system}</td>
|
||||||
</tr>
|
</tr>
|
||||||
${!this.hostInfo.features.includes("hassos")
|
|
||||||
? html`<tr>
|
|
||||||
<td>Docker version</td>
|
|
||||||
<td>${this.hassioInfo.docker}</td>
|
|
||||||
</tr>`
|
|
||||||
: ""}
|
|
||||||
${this.hostInfo.deployment
|
${this.hostInfo.deployment
|
||||||
? html`
|
? html`
|
||||||
<tr>
|
<tr>
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
import "@polymer/paper-menu-button/paper-menu-button";
|
||||||
import {
|
import {
|
||||||
css,
|
css,
|
||||||
CSSResult,
|
CSSResult,
|
||||||
@@ -11,14 +12,11 @@ import {
|
|||||||
HassioHassOSInfo,
|
HassioHassOSInfo,
|
||||||
HassioHostInfo,
|
HassioHostInfo,
|
||||||
} from "../../../src/data/hassio/host";
|
} from "../../../src/data/hassio/host";
|
||||||
import {
|
import { HassioSupervisorInfo } from "../../../src/data/hassio/supervisor";
|
||||||
HassioSupervisorInfo,
|
|
||||||
HassioInfo,
|
|
||||||
} 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";
|
||||||
import { supervisorTabs } from "../hassio-tabs";
|
import { supervisorTabs } from "../hassio-panel";
|
||||||
import { hassioStyle } from "../resources/hassio-style";
|
import { hassioStyle } from "../resources/hassio-style";
|
||||||
import "./hassio-host-info";
|
import "./hassio-host-info";
|
||||||
import "./hassio-supervisor-info";
|
import "./hassio-supervisor-info";
|
||||||
@@ -34,11 +32,9 @@ class HassioSystem extends LitElement {
|
|||||||
|
|
||||||
@property() public supervisorInfo!: HassioSupervisorInfo;
|
@property() public supervisorInfo!: HassioSupervisorInfo;
|
||||||
|
|
||||||
@property({ attribute: false }) public hassioInfo!: HassioInfo;
|
|
||||||
|
|
||||||
@property() public hostInfo!: HassioHostInfo;
|
@property() public hostInfo!: HassioHostInfo;
|
||||||
|
|
||||||
@property({ attribute: false }) public hassOsInfo!: HassioHassOSInfo;
|
@property() public hassOsInfo!: HassioHassOSInfo;
|
||||||
|
|
||||||
public render(): TemplateResult | void {
|
public render(): TemplateResult | void {
|
||||||
return html`
|
return html`
|
||||||
@@ -60,7 +56,6 @@ class HassioSystem extends LitElement {
|
|||||||
></hassio-supervisor-info>
|
></hassio-supervisor-info>
|
||||||
<hassio-host-info
|
<hassio-host-info
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.hassioInfo=${this.hassioInfo}
|
|
||||||
.hostInfo=${this.hostInfo}
|
.hostInfo=${this.hostInfo}
|
||||||
.hassOsInfo=${this.hassOsInfo}
|
.hassOsInfo=${this.hassOsInfo}
|
||||||
></hassio-host-info>
|
></hassio-host-info>
|
||||||
|
@@ -1,8 +1,11 @@
|
|||||||
const { createHassioConfig } = require("../build-scripts/webpack.js");
|
const { createHassioConfig } = require("../build-scripts/webpack.js");
|
||||||
const { isProdBuild, isStatsBuild } = require("../build-scripts/env.js");
|
const { isProdBuild } = require("../build-scripts/env.js");
|
||||||
|
|
||||||
|
// File just used for stats builds
|
||||||
|
|
||||||
|
const latestBuild = false;
|
||||||
|
|
||||||
module.exports = createHassioConfig({
|
module.exports = createHassioConfig({
|
||||||
isProdBuild: isProdBuild(),
|
isProdBuild: isProdBuild(),
|
||||||
isStatsBuild: isStatsBuild(),
|
latestBuild,
|
||||||
latestBuild: true,
|
|
||||||
});
|
});
|
||||||
|
14
package.json
14
package.json
@@ -17,7 +17,9 @@
|
|||||||
"lint": "npm run lint:eslint && npm run lint:prettier && npm run lint:types",
|
"lint": "npm run lint:eslint && npm run lint:prettier && npm run lint:types",
|
||||||
"format": "npm run format:eslint && npm run format:prettier",
|
"format": "npm run format:eslint && npm run format:prettier",
|
||||||
"mocha": "node_modules/.bin/ts-mocha -p test-mocha/tsconfig.test.json --opts test-mocha/mocha.opts",
|
"mocha": "node_modules/.bin/ts-mocha -p test-mocha/tsconfig.test.json --opts test-mocha/mocha.opts",
|
||||||
"test": "npm run lint && npm run mocha"
|
"test": "npm run lint && npm run mocha",
|
||||||
|
"docker_build": "sh ./script/docker_run.sh build $npm_package_version",
|
||||||
|
"bash": "sh ./script/docker_run.sh bash $npm_package_version"
|
||||||
},
|
},
|
||||||
"author": "Paulus Schoutsen <Paulus@PaulusSchoutsen.nl> (http://paulusschoutsen.nl)",
|
"author": "Paulus Schoutsen <Paulus@PaulusSchoutsen.nl> (http://paulusschoutsen.nl)",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
@@ -26,7 +28,6 @@
|
|||||||
"@fullcalendar/core": "^5.0.0-beta.2",
|
"@fullcalendar/core": "^5.0.0-beta.2",
|
||||||
"@fullcalendar/daygrid": "^5.0.0-beta.2",
|
"@fullcalendar/daygrid": "^5.0.0-beta.2",
|
||||||
"@material/chips": "7.0.0-canary.d92d8c93e.0",
|
"@material/chips": "7.0.0-canary.d92d8c93e.0",
|
||||||
"@material/circular-progress": "7.0.0-canary.d92d8c93e.0",
|
|
||||||
"@material/mwc-button": "^0.15.0",
|
"@material/mwc-button": "^0.15.0",
|
||||||
"@material/mwc-checkbox": "^0.15.0",
|
"@material/mwc-checkbox": "^0.15.0",
|
||||||
"@material/mwc-dialog": "^0.15.0",
|
"@material/mwc-dialog": "^0.15.0",
|
||||||
@@ -48,6 +49,7 @@
|
|||||||
"@polymer/iron-image": "^3.0.1",
|
"@polymer/iron-image": "^3.0.1",
|
||||||
"@polymer/iron-input": "^3.0.1",
|
"@polymer/iron-input": "^3.0.1",
|
||||||
"@polymer/iron-label": "^3.0.1",
|
"@polymer/iron-label": "^3.0.1",
|
||||||
|
"@polymer/iron-media-query": "^3.0.1",
|
||||||
"@polymer/iron-overlay-behavior": "^3.0.2",
|
"@polymer/iron-overlay-behavior": "^3.0.2",
|
||||||
"@polymer/iron-resizable-behavior": "^3.0.1",
|
"@polymer/iron-resizable-behavior": "^3.0.1",
|
||||||
"@polymer/paper-card": "^3.0.1",
|
"@polymer/paper-card": "^3.0.1",
|
||||||
@@ -65,6 +67,7 @@
|
|||||||
"@polymer/paper-radio-group": "^3.0.1",
|
"@polymer/paper-radio-group": "^3.0.1",
|
||||||
"@polymer/paper-ripple": "^3.0.1",
|
"@polymer/paper-ripple": "^3.0.1",
|
||||||
"@polymer/paper-slider": "^3.0.1",
|
"@polymer/paper-slider": "^3.0.1",
|
||||||
|
"@polymer/paper-spinner": "^3.0.2",
|
||||||
"@polymer/paper-styles": "^3.0.1",
|
"@polymer/paper-styles": "^3.0.1",
|
||||||
"@polymer/paper-tabs": "^3.0.1",
|
"@polymer/paper-tabs": "^3.0.1",
|
||||||
"@polymer/paper-toast": "^3.0.1",
|
"@polymer/paper-toast": "^3.0.1",
|
||||||
@@ -73,7 +76,6 @@
|
|||||||
"@thomasloven/round-slider": "0.5.0",
|
"@thomasloven/round-slider": "0.5.0",
|
||||||
"@vaadin/vaadin-combo-box": "^5.0.10",
|
"@vaadin/vaadin-combo-box": "^5.0.10",
|
||||||
"@vaadin/vaadin-date-picker": "^4.0.7",
|
"@vaadin/vaadin-date-picker": "^4.0.7",
|
||||||
"@vue/web-component-wrapper": "^1.2.0",
|
|
||||||
"@webcomponents/webcomponentsjs": "^2.2.7",
|
"@webcomponents/webcomponentsjs": "^2.2.7",
|
||||||
"chart.js": "~2.8.0",
|
"chart.js": "~2.8.0",
|
||||||
"chartjs-chart-timeline": "^0.3.0",
|
"chartjs-chart-timeline": "^0.3.0",
|
||||||
@@ -87,7 +89,7 @@
|
|||||||
"fuse.js": "^6.0.0",
|
"fuse.js": "^6.0.0",
|
||||||
"google-timezones-json": "^1.0.2",
|
"google-timezones-json": "^1.0.2",
|
||||||
"hls.js": "^0.12.4",
|
"hls.js": "^0.12.4",
|
||||||
"home-assistant-js-websocket": "^5.3.0",
|
"home-assistant-js-websocket": "^5.2.1",
|
||||||
"idb-keyval": "^3.2.0",
|
"idb-keyval": "^3.2.0",
|
||||||
"intl-messageformat": "^8.3.9",
|
"intl-messageformat": "^8.3.9",
|
||||||
"js-yaml": "^3.13.1",
|
"js-yaml": "^3.13.1",
|
||||||
@@ -106,8 +108,6 @@
|
|||||||
"roboto-fontface": "^0.10.0",
|
"roboto-fontface": "^0.10.0",
|
||||||
"superstruct": "^0.6.1",
|
"superstruct": "^0.6.1",
|
||||||
"unfetch": "^4.1.0",
|
"unfetch": "^4.1.0",
|
||||||
"vue": "^2.6.11",
|
|
||||||
"vue2-daterange-picker": "^0.5.1",
|
|
||||||
"web-animations-js": "^2.3.2",
|
"web-animations-js": "^2.3.2",
|
||||||
"workbox-core": "^5.1.3",
|
"workbox-core": "^5.1.3",
|
||||||
"workbox-precaching": "^5.1.3",
|
"workbox-precaching": "^5.1.3",
|
||||||
@@ -187,7 +187,7 @@
|
|||||||
"sinon": "^7.3.1",
|
"sinon": "^7.3.1",
|
||||||
"source-map-url": "^0.4.0",
|
"source-map-url": "^0.4.0",
|
||||||
"systemjs": "^6.3.2",
|
"systemjs": "^6.3.2",
|
||||||
"terser-webpack-plugin": "^3.0.6",
|
"terser-webpack-plugin": "^1.2.3",
|
||||||
"ts-lit-plugin": "^1.1.10",
|
"ts-lit-plugin": "^1.1.10",
|
||||||
"ts-mocha": "^6.0.0",
|
"ts-mocha": "^6.0.0",
|
||||||
"typescript": "^3.8.3",
|
"typescript": "^3.8.3",
|
||||||
|
14
script/docker_entrypoint.sh
Normal file
14
script/docker_entrypoint.sh
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Docker entry point inspired by travis build and script/build_frontend
|
||||||
|
|
||||||
|
# Stop on errors
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Build the frontend but not used the npm run build
|
||||||
|
/bin/bash script/build_frontend
|
||||||
|
|
||||||
|
# TEST
|
||||||
|
npm run test
|
||||||
|
|
||||||
|
#
|
||||||
|
#xvfb-run wct
|
103
script/docker_run.sh
Executable file
103
script/docker_run.sh
Executable file
@@ -0,0 +1,103 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Basic Docker Management scripts
|
||||||
|
# With this script you can build software, or enter an agnostic development environment and run commands interactively.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
check_mandatory_tools(){
|
||||||
|
if [ "x$(which docker)" == "x" ]; then
|
||||||
|
echo "UNKNOWN - Missing docker binary! Are you sure it is installed and reachable?"
|
||||||
|
exit 3
|
||||||
|
fi
|
||||||
|
|
||||||
|
docker info > /dev/null 2>&1
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
echo "UNKNOWN - Unable to talk to the docker daemon! Maybe the docker daemon is not running"
|
||||||
|
exit 3
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
check_dev_image(){
|
||||||
|
if [[ "$(docker images -q ${IMAGE_NAME}:$IMAGE_TAG 2> /dev/null)" == "" ]]; then
|
||||||
|
echo "UNKNOWN - Can't find the development docker image ${IMAGE_NAME}:$IMAGE_TAG"
|
||||||
|
while true; do
|
||||||
|
read -p "Do you want to create it now?" yn
|
||||||
|
case $yn in
|
||||||
|
[Yy]* ) create_image; break;;
|
||||||
|
[Nn]* ) exit 3;;
|
||||||
|
* ) echo "Please answer y or n";;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Building the basic image for compiling the production frontend
|
||||||
|
create_image(){
|
||||||
|
docker build -t ${IMAGE_NAME}:${IMAGE_TAG} .
|
||||||
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# Execute interactive bash on basic image
|
||||||
|
#
|
||||||
|
run_bash_on_docker(){
|
||||||
|
|
||||||
|
check_dev_image
|
||||||
|
|
||||||
|
docker run -it \
|
||||||
|
-v $PWD/:/frontend/ \
|
||||||
|
-v /frontend/node_modules \
|
||||||
|
-v /frontend/bower_components \
|
||||||
|
${IMAGE_NAME}:${IMAGE_TAG} /bin/bash
|
||||||
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# Execute the basic image for compiling the production frontend
|
||||||
|
#
|
||||||
|
build_all(){
|
||||||
|
|
||||||
|
check_dev_image
|
||||||
|
|
||||||
|
docker run -it \
|
||||||
|
-v $PWD/:/frontend/ \
|
||||||
|
-v /frontend/node_modules \
|
||||||
|
-v /frontend/bower_components \
|
||||||
|
${IMAGE_NAME}:${IMAGE_TAG} /bin/bash script/build_frontend
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
# Init Global Variable
|
||||||
|
IMAGE_NAME=home_assistant_fe_image
|
||||||
|
IMAGE_TAG=${2:-latest}
|
||||||
|
|
||||||
|
check_mandatory_tools
|
||||||
|
|
||||||
|
case "$1" in
|
||||||
|
setup_env)
|
||||||
|
create_image
|
||||||
|
;;
|
||||||
|
bash)
|
||||||
|
run_bash_on_docker
|
||||||
|
;;
|
||||||
|
build)
|
||||||
|
build_all
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "NAME"
|
||||||
|
echo " Docker Management."
|
||||||
|
echo ""
|
||||||
|
echo "SYNOPSIS"
|
||||||
|
echo " ${0} command [version]"
|
||||||
|
echo ""
|
||||||
|
echo "DESCRIPTION"
|
||||||
|
echo " With this script you can build software, or enter an agnostic development environment and run commands interactively."
|
||||||
|
echo ""
|
||||||
|
echo " The command are:"
|
||||||
|
echo " setup_env Create develop images"
|
||||||
|
echo " bash Run bash on develop enviroments"
|
||||||
|
echo " build Run silent build"
|
||||||
|
echo ""
|
||||||
|
echo " The version is optional, if not inserted it assumes \"latest\". "
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
exit 0
|
2
setup.py
2
setup.py
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
|
|||||||
|
|
||||||
setup(
|
setup(
|
||||||
name="home-assistant-frontend",
|
name="home-assistant-frontend",
|
||||||
version="20200623.1",
|
version="20200603.1",
|
||||||
description="The Home Assistant frontend",
|
description="The Home Assistant frontend",
|
||||||
url="https://github.com/home-assistant/home-assistant-polymer",
|
url="https://github.com/home-assistant/home-assistant-polymer",
|
||||||
author="The Home Assistant Authors",
|
author="The Home Assistant Authors",
|
||||||
|
@@ -6,18 +6,19 @@ import {
|
|||||||
property,
|
property,
|
||||||
PropertyValues,
|
PropertyValues,
|
||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
import {
|
import { AuthProvider, fetchAuthProviders } from "../data/auth";
|
||||||
AuthProvider,
|
|
||||||
fetchAuthProviders,
|
|
||||||
AuthUrlSearchParams,
|
|
||||||
} from "../data/auth";
|
|
||||||
import { litLocalizeLiteMixin } from "../mixins/lit-localize-lite-mixin";
|
import { litLocalizeLiteMixin } from "../mixins/lit-localize-lite-mixin";
|
||||||
import { registerServiceWorker } from "../util/register-service-worker";
|
import { registerServiceWorker } from "../util/register-service-worker";
|
||||||
import "./ha-auth-flow";
|
import "./ha-auth-flow";
|
||||||
import { extractSearchParamsObject } from "../common/url/search-params";
|
|
||||||
|
|
||||||
import(/* webpackChunkName: "pick-auth-provider" */ "./ha-pick-auth-provider");
|
import(/* webpackChunkName: "pick-auth-provider" */ "./ha-pick-auth-provider");
|
||||||
|
|
||||||
|
interface QueryParams {
|
||||||
|
client_id?: string;
|
||||||
|
redirect_uri?: string;
|
||||||
|
state?: string;
|
||||||
|
}
|
||||||
|
|
||||||
class HaAuthorize extends litLocalizeLiteMixin(LitElement) {
|
class HaAuthorize extends litLocalizeLiteMixin(LitElement) {
|
||||||
@property() public clientId?: string;
|
@property() public clientId?: string;
|
||||||
|
|
||||||
@@ -32,7 +33,14 @@ class HaAuthorize extends litLocalizeLiteMixin(LitElement) {
|
|||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
this.translationFragment = "page-authorize";
|
this.translationFragment = "page-authorize";
|
||||||
const query = extractSearchParamsObject() as AuthUrlSearchParams;
|
const query: QueryParams = {};
|
||||||
|
const values = location.search.substr(1).split("&");
|
||||||
|
for (const item of values) {
|
||||||
|
const value = item.split("=");
|
||||||
|
if (value.length > 1) {
|
||||||
|
query[decodeURIComponent(value[0])] = decodeURIComponent(value[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
if (query.client_id) {
|
if (query.client_id) {
|
||||||
this.clientId = query.client_id;
|
this.clientId = query.client_id;
|
||||||
}
|
}
|
||||||
@@ -137,7 +145,7 @@ class HaAuthorize extends litLocalizeLiteMixin(LitElement) {
|
|||||||
response.status === 400 &&
|
response.status === 400 &&
|
||||||
authProviders.code === "onboarding_required"
|
authProviders.code === "onboarding_required"
|
||||||
) {
|
) {
|
||||||
location.href = `/onboarding.html${location.search}`;
|
location.href = "/?";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -37,7 +37,6 @@ export const DOMAINS_WITH_MORE_INFO = [
|
|||||||
"fan",
|
"fan",
|
||||||
"group",
|
"group",
|
||||||
"history_graph",
|
"history_graph",
|
||||||
"humidifier",
|
|
||||||
"input_datetime",
|
"input_datetime",
|
||||||
"light",
|
"light",
|
||||||
"lock",
|
"lock",
|
||||||
@@ -80,7 +79,6 @@ export const DOMAINS_TOGGLE = new Set([
|
|||||||
"switch",
|
"switch",
|
||||||
"group",
|
"group",
|
||||||
"automation",
|
"automation",
|
||||||
"humidifier",
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
/** Temperature units. */
|
/** Temperature units. */
|
||||||
|
@@ -55,12 +55,6 @@ export const computeStateDisplay = (
|
|||||||
return formatDateTime(date, language);
|
return formatDateTime(date, language);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (domain === "humidifier") {
|
|
||||||
if (stateObj.state === "on" && stateObj.attributes.humidity) {
|
|
||||||
return `${stateObj.attributes.humidity}%`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
// Return device class translation
|
// Return device class translation
|
||||||
(stateObj.attributes.device_class &&
|
(stateObj.attributes.device_class &&
|
||||||
|
@@ -22,7 +22,6 @@ const fixedIcons = {
|
|||||||
history_graph: "hass:chart-line",
|
history_graph: "hass:chart-line",
|
||||||
homeassistant: "hass:home-assistant",
|
homeassistant: "hass:home-assistant",
|
||||||
homekit: "hass:home-automation",
|
homekit: "hass:home-automation",
|
||||||
humidifier: "hass:air-humidifier",
|
|
||||||
image_processing: "hass:image-filter-frames",
|
image_processing: "hass:image-filter-frames",
|
||||||
input_boolean: "hass:toggle-switch-outline",
|
input_boolean: "hass:toggle-switch-outline",
|
||||||
input_datetime: "hass:calendar-clock",
|
input_datetime: "hass:calendar-clock",
|
||||||
|
@@ -6,7 +6,7 @@ export const isValidEntityId = (entityId: string) =>
|
|||||||
export const createValidEntityId = (input: string) =>
|
export const createValidEntityId = (input: string) =>
|
||||||
input
|
input
|
||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
.replace(/\s|'|\./g, "_") // replace spaces, points and quotes with underscore
|
.replace(/\s|'/g, "_") // replace spaces and quotes with underscore
|
||||||
.replace(/\W/g, "") // remove not allowed chars
|
.replace(/\W/g, "") // remove not allowed chars
|
||||||
.replace(/_{2,}/g, "_") // replace multiple underscores with 1
|
.replace(/_{2,}/g, "_") // replace multiple underscores with 1
|
||||||
.replace(/_$/, ""); // remove underscores at the end
|
.replace(/_$/, ""); // remove underscores at the end
|
||||||
|
@@ -8,7 +8,6 @@ export const iconColorCSS = css`
|
|||||||
ha-icon[data-domain="camera"][data-state="streaming"],
|
ha-icon[data-domain="camera"][data-state="streaming"],
|
||||||
ha-icon[data-domain="cover"][data-state="open"],
|
ha-icon[data-domain="cover"][data-state="open"],
|
||||||
ha-icon[data-domain="fan"][data-state="on"],
|
ha-icon[data-domain="fan"][data-state="on"],
|
||||||
ha-icon[data-domain="humidifier"][data-state="on"],
|
|
||||||
ha-icon[data-domain="light"][data-state="on"],
|
ha-icon[data-domain="light"][data-state="on"],
|
||||||
ha-icon[data-domain="input_boolean"][data-state="on"],
|
ha-icon[data-domain="input_boolean"][data-state="on"],
|
||||||
ha-icon[data-domain="lock"][data-state="unlocked"],
|
ha-icon[data-domain="lock"][data-state="unlocked"],
|
||||||
@@ -58,11 +57,8 @@ export const iconColorCSS = css`
|
|||||||
0% {
|
0% {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
50% {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
100% {
|
100% {
|
||||||
opacity: 1;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,8 +0,0 @@
|
|||||||
export const extractSearchParamsObject = (): { [key: string]: string } => {
|
|
||||||
const query = {};
|
|
||||||
const searchParams = new URLSearchParams(location.search);
|
|
||||||
for (const [key, value] of searchParams.entries()) {
|
|
||||||
query[key] = value;
|
|
||||||
}
|
|
||||||
return query;
|
|
||||||
};
|
|
@@ -1,5 +1,5 @@
|
|||||||
import "@material/mwc-button";
|
import "@material/mwc-button";
|
||||||
import "../ha-circular-progress";
|
import "@polymer/paper-spinner/paper-spinner";
|
||||||
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";
|
||||||
@@ -8,9 +8,6 @@ class HaProgressButton extends PolymerElement {
|
|||||||
static get template() {
|
static get template() {
|
||||||
return html`
|
return html`
|
||||||
<style>
|
<style>
|
||||||
:host {
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
.container {
|
.container {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
@@ -51,9 +48,7 @@ class HaProgressButton extends PolymerElement {
|
|||||||
<slot></slot>
|
<slot></slot>
|
||||||
</mwc-button>
|
</mwc-button>
|
||||||
<template is="dom-if" if="[[progress]]">
|
<template is="dom-if" if="[[progress]]">
|
||||||
<div class="progress">
|
<div class="progress"><paper-spinner active=""></paper-spinner></div>
|
||||||
<ha-circular-progress active size="small"></ha-circular-progress>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
@@ -619,11 +619,6 @@ export class HaDataTable extends LitElement {
|
|||||||
text-transform: inherit;
|
text-transform: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mdc-data-table__cell a {
|
|
||||||
color: inherit;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mdc-data-table__cell--numeric {
|
.mdc-data-table__cell--numeric {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
@@ -1,228 +0,0 @@
|
|||||||
import Vue from "vue";
|
|
||||||
import wrap from "@vue/web-component-wrapper";
|
|
||||||
import DateRangePicker from "vue2-daterange-picker";
|
|
||||||
// @ts-ignore
|
|
||||||
import dateRangePickerStyles from "vue2-daterange-picker/dist/vue2-daterange-picker.css";
|
|
||||||
import { fireEvent } from "../common/dom/fire_event";
|
|
||||||
import { Constructor } from "../types";
|
|
||||||
import { customElement } from "lit-element/lib/decorators";
|
|
||||||
|
|
||||||
const Component = Vue.extend({
|
|
||||||
props: {
|
|
||||||
twentyfourHours: {
|
|
||||||
type: Boolean,
|
|
||||||
default: true,
|
|
||||||
},
|
|
||||||
disabled: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
ranges: {
|
|
||||||
type: Boolean,
|
|
||||||
default: true,
|
|
||||||
},
|
|
||||||
startDate: {
|
|
||||||
type: [String, Date],
|
|
||||||
default() {
|
|
||||||
return new Date();
|
|
||||||
},
|
|
||||||
},
|
|
||||||
endDate: {
|
|
||||||
type: [String, Date],
|
|
||||||
default() {
|
|
||||||
return new Date();
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
render(createElement) {
|
|
||||||
// @ts-ignore
|
|
||||||
return createElement(DateRangePicker, {
|
|
||||||
props: {
|
|
||||||
"time-picker": true,
|
|
||||||
"auto-apply": false,
|
|
||||||
opens: "right",
|
|
||||||
"show-dropdowns": false,
|
|
||||||
"time-picker24-hour": this.twentyfourHours,
|
|
||||||
disabled: this.disabled,
|
|
||||||
ranges: this.ranges ? {} : false,
|
|
||||||
},
|
|
||||||
model: {
|
|
||||||
value: {
|
|
||||||
startDate: this.startDate,
|
|
||||||
endDate: this.endDate,
|
|
||||||
},
|
|
||||||
callback: (value) => {
|
|
||||||
// @ts-ignore
|
|
||||||
fireEvent(this.$el as HTMLElement, "change", value);
|
|
||||||
},
|
|
||||||
expression: "dateRange",
|
|
||||||
},
|
|
||||||
scopedSlots: {
|
|
||||||
input() {
|
|
||||||
return createElement("slot", {
|
|
||||||
domProps: { name: "input" },
|
|
||||||
});
|
|
||||||
},
|
|
||||||
header() {
|
|
||||||
return createElement("slot", {
|
|
||||||
domProps: { name: "header" },
|
|
||||||
});
|
|
||||||
},
|
|
||||||
ranges() {
|
|
||||||
return createElement("slot", {
|
|
||||||
domProps: { name: "ranges" },
|
|
||||||
});
|
|
||||||
},
|
|
||||||
footer() {
|
|
||||||
return createElement("slot", {
|
|
||||||
domProps: { name: "footer" },
|
|
||||||
});
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const WrappedElement: Constructor<HTMLElement> = wrap(Vue, Component);
|
|
||||||
|
|
||||||
@customElement("date-range-picker")
|
|
||||||
class DateRangePickerElement extends WrappedElement {
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
const style = document.createElement("style");
|
|
||||||
style.innerHTML = `
|
|
||||||
${dateRangePickerStyles}
|
|
||||||
.calendars {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
.daterangepicker {
|
|
||||||
left: 0px !important;
|
|
||||||
top: auto;
|
|
||||||
background-color: var(--card-background-color);
|
|
||||||
border: none;
|
|
||||||
border-radius: var(--ha-card-border-radius, 4px);
|
|
||||||
box-shadow: var(
|
|
||||||
--ha-card-box-shadow,
|
|
||||||
0px 2px 1px -1px rgba(0, 0, 0, 0.2),
|
|
||||||
0px 1px 1px 0px rgba(0, 0, 0, 0.14),
|
|
||||||
0px 1px 3px 0px rgba(0, 0, 0, 0.12)
|
|
||||||
);
|
|
||||||
color: var(--primary-text-color);
|
|
||||||
min-width: initial !important;
|
|
||||||
}
|
|
||||||
.daterangepicker:after {
|
|
||||||
border-bottom: 6px solid var(--card-background-color);
|
|
||||||
}
|
|
||||||
.daterangepicker .calendar-table {
|
|
||||||
background-color: var(--card-background-color);
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
.daterangepicker .calendar-table td,
|
|
||||||
.daterangepicker .calendar-table th {
|
|
||||||
background-color: transparent;
|
|
||||||
color: var(--secondary-text-color);
|
|
||||||
border-radius: 0;
|
|
||||||
outline: none;
|
|
||||||
width: 32px;
|
|
||||||
height: 32px;
|
|
||||||
}
|
|
||||||
.daterangepicker td.off,
|
|
||||||
.daterangepicker td.off.end-date,
|
|
||||||
.daterangepicker td.off.in-range,
|
|
||||||
.daterangepicker td.off.start-date {
|
|
||||||
background-color: var(--secondary-background-color);
|
|
||||||
color: var(--disabled-text-color);
|
|
||||||
}
|
|
||||||
.daterangepicker td.in-range {
|
|
||||||
background-color: var(--light-primary-color);
|
|
||||||
color: var(--primary-text-color);
|
|
||||||
}
|
|
||||||
.daterangepicker td.active,
|
|
||||||
.daterangepicker td.active:hover {
|
|
||||||
background-color: var(--primary-color);
|
|
||||||
color: var(--text-primary-color);
|
|
||||||
}
|
|
||||||
.daterangepicker td.start-date.end-date {
|
|
||||||
border-radius: 50%;
|
|
||||||
}
|
|
||||||
.daterangepicker td.start-date {
|
|
||||||
border-radius: 50% 0 0 50%;
|
|
||||||
}
|
|
||||||
.daterangepicker td.end-date {
|
|
||||||
border-radius: 0 50% 50% 0;
|
|
||||||
}
|
|
||||||
.reportrange-text {
|
|
||||||
background: none !important;
|
|
||||||
padding: 0 !important;
|
|
||||||
border: none !important;
|
|
||||||
}
|
|
||||||
.daterangepicker .calendar-table .next span,
|
|
||||||
.daterangepicker .calendar-table .prev span {
|
|
||||||
border: solid var(--primary-text-color);
|
|
||||||
border-width: 0 2px 2px 0;
|
|
||||||
}
|
|
||||||
.daterangepicker .ranges li {
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
.daterangepicker .ranges li:hover {
|
|
||||||
background-color: var(--secondary-background-color);
|
|
||||||
}
|
|
||||||
.daterangepicker .ranges li.active {
|
|
||||||
background-color: var(--primary-color);
|
|
||||||
color: var(--text-primary-color);
|
|
||||||
}
|
|
||||||
.daterangepicker select.ampmselect,
|
|
||||||
.daterangepicker select.hourselect,
|
|
||||||
.daterangepicker select.minuteselect,
|
|
||||||
.daterangepicker select.secondselect {
|
|
||||||
background: transparent;
|
|
||||||
border: 1px solid var(--divider-color);
|
|
||||||
color: var(--primary-color);
|
|
||||||
}
|
|
||||||
.daterangepicker .drp-buttons .btn {
|
|
||||||
border: 1px solid var(--primary-color);
|
|
||||||
background-color: transparent;
|
|
||||||
color: var(--primary-color);
|
|
||||||
border-radius: 4px;
|
|
||||||
padding: 8px;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
.calendars-container {
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
.drp-calendar.col.right .calendar-table {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
.daterangepicker.show-ranges .drp-calendar.left {
|
|
||||||
border-left: 0px;
|
|
||||||
}
|
|
||||||
.daterangepicker .drp-calendar.left {
|
|
||||||
padding: 8px;
|
|
||||||
}
|
|
||||||
.daterangepicker.show-calendar .ranges {
|
|
||||||
margin-top: 0;
|
|
||||||
padding-top: 8px;
|
|
||||||
border-right: 1px solid var(--divider-color);
|
|
||||||
}
|
|
||||||
@media only screen and (max-width: 800px) {
|
|
||||||
.calendars {
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.calendar-table {
|
|
||||||
padding: 0 !important;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
const shadowRoot = this.shadowRoot!;
|
|
||||||
shadowRoot.appendChild(style);
|
|
||||||
// Stop click events from reaching the document, otherwise it will close the picker immediately.
|
|
||||||
shadowRoot.addEventListener("click", (ev) => ev.stopPropagation());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
declare global {
|
|
||||||
interface HTMLElementTagNameMap {
|
|
||||||
"date-range-picker": DateRangePickerElement;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -22,7 +22,7 @@ const isOn = (stateObj?: HassEntity) =>
|
|||||||
!STATES_OFF.includes(stateObj.state) &&
|
!STATES_OFF.includes(stateObj.state) &&
|
||||||
!UNAVAILABLE_STATES.includes(stateObj.state);
|
!UNAVAILABLE_STATES.includes(stateObj.state);
|
||||||
|
|
||||||
export class HaEntityToggle extends LitElement {
|
class HaEntityToggle extends LitElement {
|
||||||
// hass is not a property so that we only re-render on stateObj changes
|
// hass is not a property so that we only re-render on stateObj changes
|
||||||
public hass?: HomeAssistant;
|
public hass?: HomeAssistant;
|
||||||
|
|
||||||
|
@@ -40,7 +40,7 @@ export class HaButtonMenu extends LitElement {
|
|||||||
static get styles(): CSSResult {
|
static get styles(): CSSResult {
|
||||||
return css`
|
return css`
|
||||||
:host {
|
:host {
|
||||||
display: inline-block;
|
display: block;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
@@ -176,9 +176,7 @@ class HaCameraStream extends LitElement {
|
|||||||
Hls: HLSModule,
|
Hls: HLSModule,
|
||||||
url: string
|
url: string
|
||||||
) {
|
) {
|
||||||
const hls = new Hls({
|
const hls = new Hls();
|
||||||
liveBackBufferLength: 60,
|
|
||||||
});
|
|
||||||
this._hlsPolyfillInstance = hls;
|
this._hlsPolyfillInstance = hls;
|
||||||
hls.attachMedia(videoEl);
|
hls.attachMedia(videoEl);
|
||||||
hls.on(Hls.Events.MEDIA_ATTACHED, () => {
|
hls.on(Hls.Events.MEDIA_ATTACHED, () => {
|
||||||
|
@@ -12,8 +12,6 @@ import {
|
|||||||
class HaCard extends LitElement {
|
class HaCard extends LitElement {
|
||||||
@property() public header?: string;
|
@property() public header?: string;
|
||||||
|
|
||||||
@property({ type: Boolean, reflect: true }) public outlined = false;
|
|
||||||
|
|
||||||
static get styles(): CSSResult {
|
static get styles(): CSSResult {
|
||||||
return css`
|
return css`
|
||||||
:host {
|
:host {
|
||||||
@@ -21,12 +19,12 @@ class HaCard extends LitElement {
|
|||||||
--ha-card-background,
|
--ha-card-background,
|
||||||
var(--paper-card-background-color, white)
|
var(--paper-card-background-color, white)
|
||||||
);
|
);
|
||||||
border-radius: var(--ha-card-border-radius, 4px);
|
border-radius: var(--ha-card-border-radius, 2px);
|
||||||
box-shadow: var(
|
box-shadow: var(
|
||||||
--ha-card-box-shadow,
|
--ha-card-box-shadow,
|
||||||
0px 2px 1px -1px rgba(0, 0, 0, 0.2),
|
0 2px 2px 0 rgba(0, 0, 0, 0.14),
|
||||||
0px 1px 1px 0px rgba(0, 0, 0, 0.14),
|
0 1px 5px 0 rgba(0, 0, 0, 0.12),
|
||||||
0px 1px 3px 0px rgba(0, 0, 0, 0.12)
|
0 3px 1px -2px rgba(0, 0, 0, 0.2)
|
||||||
);
|
);
|
||||||
color: var(--primary-text-color);
|
color: var(--primary-text-color);
|
||||||
display: block;
|
display: block;
|
||||||
@@ -34,16 +32,6 @@ class HaCard extends LitElement {
|
|||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
:host([outlined]) {
|
|
||||||
box-shadow: none;
|
|
||||||
border-width: 1px;
|
|
||||||
border-style: solid;
|
|
||||||
border-color: var(
|
|
||||||
--ha-card-border-color,
|
|
||||||
var(--divider-color, #e0e0e0)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-header,
|
.card-header,
|
||||||
:host ::slotted(.card-header) {
|
:host ::slotted(.card-header) {
|
||||||
color: var(--ha-card-header-color, --primary-text-color);
|
color: var(--ha-card-header-color, --primary-text-color);
|
||||||
|
@@ -1,93 +0,0 @@
|
|||||||
import {
|
|
||||||
LitElement,
|
|
||||||
TemplateResult,
|
|
||||||
property,
|
|
||||||
svg,
|
|
||||||
html,
|
|
||||||
customElement,
|
|
||||||
unsafeCSS,
|
|
||||||
SVGTemplateResult,
|
|
||||||
css,
|
|
||||||
} from "lit-element";
|
|
||||||
// @ts-ignore
|
|
||||||
import progressStyles from "@material/circular-progress/dist/mdc.circular-progress.min.css";
|
|
||||||
import { classMap } from "lit-html/directives/class-map";
|
|
||||||
|
|
||||||
@customElement("ha-circular-progress")
|
|
||||||
export class HaCircularProgress extends LitElement {
|
|
||||||
@property({ type: Boolean })
|
|
||||||
public active = false;
|
|
||||||
|
|
||||||
@property()
|
|
||||||
public alt = "Loading";
|
|
||||||
|
|
||||||
@property()
|
|
||||||
public size: "small" | "medium" | "large" = "medium";
|
|
||||||
|
|
||||||
protected render(): TemplateResult | void {
|
|
||||||
let indeterminatePart: SVGTemplateResult;
|
|
||||||
|
|
||||||
if (this.size === "small") {
|
|
||||||
indeterminatePart = svg`
|
|
||||||
<svg class="mdc-circular-progress__indeterminate-circle-graphic" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<circle cx="12" cy="12" r="8.75" stroke-dasharray="54.978" stroke-dashoffset="27.489"/>
|
|
||||||
</svg>`;
|
|
||||||
} else if (this.size === "large") {
|
|
||||||
indeterminatePart = svg`
|
|
||||||
<svg class="mdc-circular-progress__indeterminate-circle-graphic" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<circle cx="24" cy="24" r="18" stroke-dasharray="113.097" stroke-dashoffset="56.549"/>
|
|
||||||
</svg>`;
|
|
||||||
} else {
|
|
||||||
// medium
|
|
||||||
indeterminatePart = svg`
|
|
||||||
<svg class="mdc-circular-progress__indeterminate-circle-graphic" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<circle cx="16" cy="16" r="12.5" stroke-dasharray="78.54" stroke-dashoffset="39.27"/>
|
|
||||||
</svg>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ignoring prettier as it will introduce unwanted whitespace
|
|
||||||
// We have not implemented the determinate support of mdc circular progress.
|
|
||||||
// prettier-ignore
|
|
||||||
return html`
|
|
||||||
<div
|
|
||||||
class="mdc-circular-progress ${classMap({
|
|
||||||
"mdc-circular-progress--indeterminate": this.active,
|
|
||||||
[`mdc-circular-progress--${this.size}`]: true,
|
|
||||||
})}"
|
|
||||||
role="progressbar"
|
|
||||||
aria-label=${this.alt}
|
|
||||||
aria-valuemin="0"
|
|
||||||
aria-valuemax="1"
|
|
||||||
>
|
|
||||||
<div class="mdc-circular-progress__indeterminate-container">
|
|
||||||
<div class="mdc-circular-progress__spinner-layer">
|
|
||||||
<div class="mdc-circular-progress__circle-clipper mdc-circular-progress__circle-left">
|
|
||||||
${indeterminatePart}
|
|
||||||
</div><div class="mdc-circular-progress__gap-patch">
|
|
||||||
${indeterminatePart}
|
|
||||||
</div><div class="mdc-circular-progress__circle-clipper mdc-circular-progress__circle-right">
|
|
||||||
${indeterminatePart}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
static get styles() {
|
|
||||||
return [
|
|
||||||
unsafeCSS(progressStyles),
|
|
||||||
css`
|
|
||||||
:host {
|
|
||||||
text-align: initial;
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
declare global {
|
|
||||||
interface HTMLElementTagNameMap {
|
|
||||||
"ha-circular-progress": HaCircularProgress;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,195 +0,0 @@
|
|||||||
import {
|
|
||||||
css,
|
|
||||||
CSSResult,
|
|
||||||
customElement,
|
|
||||||
html,
|
|
||||||
LitElement,
|
|
||||||
property,
|
|
||||||
TemplateResult,
|
|
||||||
PropertyValues,
|
|
||||||
} from "lit-element";
|
|
||||||
import { HomeAssistant } from "../types";
|
|
||||||
import { mdiCalendar } from "@mdi/js";
|
|
||||||
import { formatDateTime } from "../common/datetime/format_date_time";
|
|
||||||
import "@material/mwc-button/mwc-button";
|
|
||||||
import "@material/mwc-list/mwc-list-item";
|
|
||||||
import "./ha-svg-icon";
|
|
||||||
import "@polymer/paper-input/paper-input";
|
|
||||||
import "@material/mwc-list/mwc-list";
|
|
||||||
import "./date-range-picker";
|
|
||||||
|
|
||||||
export interface DateRangePickerRanges {
|
|
||||||
[key: string]: [Date, Date];
|
|
||||||
}
|
|
||||||
|
|
||||||
@customElement("ha-date-range-picker")
|
|
||||||
export class HaDateRangePicker extends LitElement {
|
|
||||||
@property() public hass!: HomeAssistant;
|
|
||||||
|
|
||||||
@property() public startDate!: Date;
|
|
||||||
|
|
||||||
@property() public endDate!: Date;
|
|
||||||
|
|
||||||
@property() public ranges?: DateRangePickerRanges;
|
|
||||||
|
|
||||||
@property({ type: Boolean }) public disabled = false;
|
|
||||||
|
|
||||||
@property({ type: Boolean }) private _hour24format = false;
|
|
||||||
|
|
||||||
protected updated(changedProps: PropertyValues) {
|
|
||||||
if (changedProps.has("hass")) {
|
|
||||||
const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
|
|
||||||
if (!oldHass || oldHass.language !== this.hass.language) {
|
|
||||||
this._hour24format = this._compute24hourFormat();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
|
||||||
return html`
|
|
||||||
<date-range-picker
|
|
||||||
?disabled=${this.disabled}
|
|
||||||
twentyfour-hours=${this._hour24format}
|
|
||||||
start-date=${this.startDate}
|
|
||||||
end-date=${this.endDate}
|
|
||||||
?ranges=${this.ranges !== undefined}
|
|
||||||
>
|
|
||||||
<div slot="input" class="date-range-inputs">
|
|
||||||
<ha-svg-icon path=${mdiCalendar}></ha-svg-icon>
|
|
||||||
<paper-input
|
|
||||||
.value=${formatDateTime(this.startDate, this.hass.language)}
|
|
||||||
.label=${this.hass.localize(
|
|
||||||
"ui.components.date-range-picker.start_date"
|
|
||||||
)}
|
|
||||||
.disabled=${this.disabled}
|
|
||||||
@click=${this._handleInputClick}
|
|
||||||
readonly
|
|
||||||
></paper-input>
|
|
||||||
<paper-input
|
|
||||||
.value=${formatDateTime(this.endDate, this.hass.language)}
|
|
||||||
label=${this.hass.localize(
|
|
||||||
"ui.components.date-range-picker.end_date"
|
|
||||||
)}
|
|
||||||
.disabled=${this.disabled}
|
|
||||||
@click=${this._handleInputClick}
|
|
||||||
readonly
|
|
||||||
></paper-input>
|
|
||||||
</div>
|
|
||||||
${this.ranges
|
|
||||||
? html`<div slot="ranges" class="date-range-ranges">
|
|
||||||
<mwc-list @click=${this._setDateRange}>
|
|
||||||
${Object.entries(this.ranges).map(
|
|
||||||
([name, dates]) => html`<mwc-list-item
|
|
||||||
.activated=${this.startDate.getTime() ===
|
|
||||||
dates[0].getTime() &&
|
|
||||||
this.endDate.getTime() === dates[1].getTime()}
|
|
||||||
.startDate=${dates[0]}
|
|
||||||
.endDate=${dates[1]}
|
|
||||||
>
|
|
||||||
${name}
|
|
||||||
</mwc-list-item>`
|
|
||||||
)}
|
|
||||||
</mwc-list>
|
|
||||||
</div>`
|
|
||||||
: ""}
|
|
||||||
<div slot="footer" class="date-range-footer">
|
|
||||||
<mwc-button @click=${this._cancelDateRange}
|
|
||||||
>${this.hass.localize("ui.common.cancel")}</mwc-button
|
|
||||||
>
|
|
||||||
<mwc-button @click=${this._applyDateRange}
|
|
||||||
>${this.hass.localize(
|
|
||||||
"ui.components.date-range-picker.select"
|
|
||||||
)}</mwc-button
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</date-range-picker>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
private _compute24hourFormat() {
|
|
||||||
return (
|
|
||||||
new Intl.DateTimeFormat(this.hass.language, {
|
|
||||||
hour: "numeric",
|
|
||||||
})
|
|
||||||
.formatToParts(new Date(2020, 0, 1, 13))
|
|
||||||
.find((part) => part.type === "hour")!.value.length === 2
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private _setDateRange(ev: Event) {
|
|
||||||
const target = ev.target as any;
|
|
||||||
const startDate = target.startDate;
|
|
||||||
const endDate = target.endDate;
|
|
||||||
const dateRangePicker = this._dateRangePicker;
|
|
||||||
dateRangePicker.clickRange([startDate, endDate]);
|
|
||||||
dateRangePicker.clickedApply();
|
|
||||||
}
|
|
||||||
|
|
||||||
private _cancelDateRange() {
|
|
||||||
this._dateRangePicker.clickCancel();
|
|
||||||
}
|
|
||||||
|
|
||||||
private _applyDateRange() {
|
|
||||||
this._dateRangePicker.clickedApply();
|
|
||||||
}
|
|
||||||
|
|
||||||
private get _dateRangePicker() {
|
|
||||||
const dateRangePicker = this.shadowRoot!.querySelector(
|
|
||||||
"date-range-picker"
|
|
||||||
) as any;
|
|
||||||
return dateRangePicker.vueComponent.$children[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
private _handleInputClick() {
|
|
||||||
// close the date picker, so it will open again on the click event
|
|
||||||
if (this._dateRangePicker.open) {
|
|
||||||
this._dateRangePicker.open = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static get styles(): CSSResult {
|
|
||||||
return css`
|
|
||||||
ha-svg-icon {
|
|
||||||
margin-right: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.date-range-inputs {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.date-range-ranges {
|
|
||||||
border-right: 1px solid var(--divider-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
@media only screen and (max-width: 800px) {
|
|
||||||
.date-range-ranges {
|
|
||||||
border-right: none;
|
|
||||||
border-bottom: 1px solid var(--divider-color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.date-range-footer {
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-end;
|
|
||||||
padding: 8px;
|
|
||||||
border-top: 1px solid var(--divider-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
paper-input {
|
|
||||||
display: inline-block;
|
|
||||||
max-width: 200px;
|
|
||||||
}
|
|
||||||
|
|
||||||
paper-input:last-child {
|
|
||||||
margin-left: 8px;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
declare global {
|
|
||||||
interface HTMLElementTagNameMap {
|
|
||||||
"ha-date-range-picker": HaDateRangePicker;
|
|
||||||
}
|
|
||||||
}
|
|
24
src/components/ha-demo-badge.js
Normal file
24
src/components/ha-demo-badge.js
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||||
|
/* eslint-plugin-disable lit */
|
||||||
|
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||||
|
import "./ha-label-badge";
|
||||||
|
|
||||||
|
class HaDemoBadge extends PolymerElement {
|
||||||
|
static get template() {
|
||||||
|
return html`
|
||||||
|
<style>
|
||||||
|
:host {
|
||||||
|
--ha-label-badge-color: #dac90d;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<ha-label-badge
|
||||||
|
icon="hass:emoticon"
|
||||||
|
label="Demo"
|
||||||
|
description=""
|
||||||
|
></ha-label-badge>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define("ha-demo-badge", HaDemoBadge);
|
@@ -13,7 +13,7 @@ export const createCloseHeading = (hass: HomeAssistant, title: string) => html`
|
|||||||
<mwc-icon-button
|
<mwc-icon-button
|
||||||
aria-label=${hass.localize("ui.dialogs.generic.close")}
|
aria-label=${hass.localize("ui.dialogs.generic.close")}
|
||||||
dialogAction="close"
|
dialogAction="close"
|
||||||
class="header_button"
|
class="close_button"
|
||||||
>
|
>
|
||||||
<ha-svg-icon path=${mdiClose}></ha-svg-icon>
|
<ha-svg-icon path=${mdiClose}></ha-svg-icon>
|
||||||
</mwc-icon-button>
|
</mwc-icon-button>
|
||||||
@@ -25,9 +25,6 @@ export class HaDialog extends MwcDialog {
|
|||||||
return [
|
return [
|
||||||
style,
|
style,
|
||||||
css`
|
css`
|
||||||
.mdc-dialog {
|
|
||||||
z-index: var(--dialog-z-index, 7);
|
|
||||||
}
|
|
||||||
.mdc-dialog__actions {
|
.mdc-dialog__actions {
|
||||||
justify-content: var(--justify-action-buttons, flex-end);
|
justify-content: var(--justify-action-buttons, flex-end);
|
||||||
}
|
}
|
||||||
@@ -38,15 +35,10 @@ export class HaDialog extends MwcDialog {
|
|||||||
display: block;
|
display: block;
|
||||||
height: 20px;
|
height: 20px;
|
||||||
}
|
}
|
||||||
.mdc-dialog__content {
|
.close_button {
|
||||||
padding: var(--dialog-content-padding, 20px 24px);
|
|
||||||
}
|
|
||||||
.header_button {
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 16px;
|
right: 16px;
|
||||||
top: 12px;
|
top: 12px;
|
||||||
text-decoration: none;
|
|
||||||
color: inherit;
|
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
];
|
];
|
||||||
|
@@ -1,33 +0,0 @@
|
|||||||
import "@material/mwc-formfield";
|
|
||||||
import type { Formfield } from "@material/mwc-formfield";
|
|
||||||
import { style } from "@material/mwc-formfield/mwc-formfield-css";
|
|
||||||
import { css, CSSResult, customElement } from "lit-element";
|
|
||||||
import { Constructor } from "../types";
|
|
||||||
|
|
||||||
const MwcFormfield = customElements.get("mwc-formfield") as Constructor<
|
|
||||||
Formfield
|
|
||||||
>;
|
|
||||||
|
|
||||||
@customElement("ha-formfield")
|
|
||||||
export class HaFormfield extends MwcFormfield {
|
|
||||||
protected static get styles(): CSSResult[] {
|
|
||||||
return [
|
|
||||||
style,
|
|
||||||
css`
|
|
||||||
::slotted(ha-switch) {
|
|
||||||
margin-right: 10px;
|
|
||||||
}
|
|
||||||
[dir="rtl"] ::slotted(ha-switch),
|
|
||||||
::slotted(ha-switch)[dir="rtl"] {
|
|
||||||
margin-left: 10px;
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
declare global {
|
|
||||||
interface HTMLElementTagNameMap {
|
|
||||||
"ha-formfield": HaFormfield;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,37 +1,17 @@
|
|||||||
import {
|
import { HaIconButton } from "./ha-icon-button";
|
||||||
LitElement,
|
|
||||||
property,
|
|
||||||
TemplateResult,
|
|
||||||
html,
|
|
||||||
customElement,
|
|
||||||
} from "lit-element";
|
|
||||||
import { mdiArrowLeft, mdiArrowRight } from "@mdi/js";
|
|
||||||
import "@material/mwc-icon-button/mwc-icon-button";
|
|
||||||
import "./ha-svg-icon";
|
|
||||||
|
|
||||||
@customElement("ha-icon-button-arrow-next")
|
|
||||||
export class HaIconButtonArrowNext extends LitElement {
|
|
||||||
@property({ type: Boolean }) public disabled = false;
|
|
||||||
|
|
||||||
@property() private _icon = mdiArrowRight;
|
|
||||||
|
|
||||||
|
export class HaIconButtonArrowNext extends HaIconButton {
|
||||||
public connectedCallback() {
|
public connectedCallback() {
|
||||||
super.connectedCallback();
|
super.connectedCallback();
|
||||||
|
|
||||||
// wait to check for direction since otherwise direction is wrong even though top level is RTL
|
// wait to check for direction since otherwise direction is wrong even though top level is RTL
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this._icon =
|
this.icon =
|
||||||
window.getComputedStyle(this).direction === "ltr"
|
window.getComputedStyle(this).direction === "ltr"
|
||||||
? mdiArrowRight
|
? "hass:arrow-right"
|
||||||
: mdiArrowLeft;
|
: "hass:arrow-left";
|
||||||
}, 100);
|
}, 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
|
||||||
return html`<mwc-icon-button .disabled=${this.disabled}>
|
|
||||||
<ha-svg-icon .path=${this._icon}></ha-svg-icon>
|
|
||||||
</mwc-icon-button> `;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
@@ -39,3 +19,5 @@ declare global {
|
|||||||
"ha-icon-button-arrow-next": HaIconButtonArrowNext;
|
"ha-icon-button-arrow-next": HaIconButtonArrowNext;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
customElements.define("ha-icon-button-arrow-next", HaIconButtonArrowNext);
|
||||||
|
@@ -1,15 +1,8 @@
|
|||||||
import {
|
import { LitElement, property, TemplateResult, html } from "lit-element";
|
||||||
LitElement,
|
|
||||||
property,
|
|
||||||
TemplateResult,
|
|
||||||
html,
|
|
||||||
customElement,
|
|
||||||
} from "lit-element";
|
|
||||||
import { mdiArrowLeft, mdiArrowRight } from "@mdi/js";
|
import { mdiArrowLeft, mdiArrowRight } from "@mdi/js";
|
||||||
import "@material/mwc-icon-button/mwc-icon-button";
|
import "@material/mwc-icon-button/mwc-icon-button";
|
||||||
import "./ha-svg-icon";
|
import "./ha-svg-icon";
|
||||||
|
|
||||||
@customElement("ha-icon-button-arrow-prev")
|
|
||||||
export class HaIconButtonArrowPrev extends LitElement {
|
export class HaIconButtonArrowPrev extends LitElement {
|
||||||
@property({ type: Boolean }) public disabled = false;
|
@property({ type: Boolean }) public disabled = false;
|
||||||
|
|
||||||
@@ -39,3 +32,5 @@ declare global {
|
|||||||
"ha-icon-button-arrow-prev": HaIconButtonArrowPrev;
|
"ha-icon-button-arrow-prev": HaIconButtonArrowPrev;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
customElements.define("ha-icon-button-arrow-prev", HaIconButtonArrowPrev);
|
||||||
|
@@ -1,37 +1,17 @@
|
|||||||
import {
|
import { HaIconButton } from "./ha-icon-button";
|
||||||
LitElement,
|
|
||||||
property,
|
|
||||||
TemplateResult,
|
|
||||||
html,
|
|
||||||
customElement,
|
|
||||||
} from "lit-element";
|
|
||||||
import { mdiChevronRight, mdiChevronLeft } from "@mdi/js";
|
|
||||||
import "@material/mwc-icon-button";
|
|
||||||
import "./ha-svg-icon";
|
|
||||||
|
|
||||||
@customElement("ha-icon-button-next")
|
|
||||||
export class HaIconButtonNext extends LitElement {
|
|
||||||
@property({ type: Boolean }) public disabled = false;
|
|
||||||
|
|
||||||
@property() private _icon = mdiChevronRight;
|
|
||||||
|
|
||||||
|
export class HaIconButtonNext extends HaIconButton {
|
||||||
public connectedCallback() {
|
public connectedCallback() {
|
||||||
super.connectedCallback();
|
super.connectedCallback();
|
||||||
|
|
||||||
// wait to check for direction since otherwise direction is wrong even though top level is RTL
|
// wait to check for direction since otherwise direction is wrong even though top level is RTL
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this._icon =
|
this.icon =
|
||||||
window.getComputedStyle(this).direction === "ltr"
|
window.getComputedStyle(this).direction === "ltr"
|
||||||
? mdiChevronRight
|
? "hass:chevron-right"
|
||||||
: mdiChevronLeft;
|
: "hass:chevron-left";
|
||||||
}, 100);
|
}, 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
|
||||||
return html`<mwc-icon-button .disabled=${this.disabled}>
|
|
||||||
<ha-svg-icon .path=${this._icon}></ha-svg-icon>
|
|
||||||
</mwc-icon-button> `;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
@@ -39,3 +19,5 @@ declare global {
|
|||||||
"ha-icon-button-next": HaIconButtonNext;
|
"ha-icon-button-next": HaIconButtonNext;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
customElements.define("ha-icon-button-next", HaIconButtonNext);
|
||||||
|
@@ -1,37 +1,17 @@
|
|||||||
import {
|
import { HaIconButton } from "./ha-icon-button";
|
||||||
LitElement,
|
|
||||||
property,
|
|
||||||
TemplateResult,
|
|
||||||
html,
|
|
||||||
customElement,
|
|
||||||
} from "lit-element";
|
|
||||||
import { mdiChevronRight, mdiChevronLeft } from "@mdi/js";
|
|
||||||
import "@material/mwc-icon-button/mwc-icon-button";
|
|
||||||
import "./ha-svg-icon";
|
|
||||||
|
|
||||||
@customElement("ha-icon-button-prev")
|
|
||||||
export class HaIconButtonPrev extends LitElement {
|
|
||||||
@property({ type: Boolean }) public disabled = false;
|
|
||||||
|
|
||||||
@property() private _icon = mdiChevronLeft;
|
|
||||||
|
|
||||||
|
export class HaIconButtonPrev extends HaIconButton {
|
||||||
public connectedCallback() {
|
public connectedCallback() {
|
||||||
super.connectedCallback();
|
super.connectedCallback();
|
||||||
|
|
||||||
// wait to check for direction since otherwise direction is wrong even though top level is RTL
|
// wait to check for direction since otherwise direction is wrong even though top level is RTL
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this._icon =
|
this.icon =
|
||||||
window.getComputedStyle(this).direction === "ltr"
|
window.getComputedStyle(this).direction === "ltr"
|
||||||
? mdiChevronLeft
|
? "hass:chevron-left"
|
||||||
: mdiChevronRight;
|
: "hass:chevron-right";
|
||||||
}, 100);
|
}, 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
|
||||||
return html`<mwc-icon-button .disabled=${this.disabled}>
|
|
||||||
<ha-svg-icon .path=${this._icon}></ha-svg-icon>
|
|
||||||
</mwc-icon-button> `;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
@@ -39,3 +19,5 @@ declare global {
|
|||||||
"ha-icon-button-prev": HaIconButtonPrev;
|
"ha-icon-button-prev": HaIconButtonPrev;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
customElements.define("ha-icon-button-prev", HaIconButtonPrev);
|
||||||
|
@@ -24,21 +24,28 @@ export class HaIconButton extends LitElement {
|
|||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
return html`
|
return html`
|
||||||
<mwc-icon-button .label=${this.label} .disabled=${this.disabled}>
|
<mwc-icon-button
|
||||||
|
.label=${this.label}
|
||||||
|
?disabled=${this.disabled}
|
||||||
|
@click=${this._handleClick}
|
||||||
|
>
|
||||||
<ha-icon .icon=${this.icon}></ha-icon>
|
<ha-icon .icon=${this.icon}></ha-icon>
|
||||||
</mwc-icon-button>
|
</mwc-icon-button>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _handleClick(ev) {
|
||||||
|
if (this.disabled) {
|
||||||
|
ev.stopPropagation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static get styles(): CSSResult {
|
static get styles(): CSSResult {
|
||||||
return css`
|
return css`
|
||||||
:host {
|
:host {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
:host([disabled]) {
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
mwc-icon-button {
|
mwc-icon-button {
|
||||||
--mdc-theme-on-primary: currentColor;
|
--mdc-theme-on-primary: currentColor;
|
||||||
--mdc-theme-text-disabled-on-light: var(--disabled-text-color);
|
--mdc-theme-text-disabled-on-light: var(--disabled-text-color);
|
||||||
|
@@ -13,7 +13,7 @@ class HaMarkdownElement extends UpdatingElement {
|
|||||||
protected update(changedProps) {
|
protected update(changedProps) {
|
||||||
super.update(changedProps);
|
super.update(changedProps);
|
||||||
if (this.content !== undefined) {
|
if (this.content !== undefined) {
|
||||||
this._render();
|
this._render();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -54,7 +54,7 @@ class HaMarkdown extends LitElement {
|
|||||||
}
|
}
|
||||||
ha-markdown-element code,
|
ha-markdown-element code,
|
||||||
pre {
|
pre {
|
||||||
background-color: var(--markdown-code-background-color, none);
|
background-color: var(--markdown-code-background-color, #f6f8fa);
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
}
|
}
|
||||||
ha-markdown-element code {
|
ha-markdown-element code {
|
||||||
|
@@ -1,5 +1,3 @@
|
|||||||
import "@material/mwc-icon-button";
|
|
||||||
import { mdiMenu } from "@mdi/js";
|
|
||||||
import { UnsubscribeFunc } from "home-assistant-js-websocket";
|
import { UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||||
import {
|
import {
|
||||||
css,
|
css,
|
||||||
@@ -14,7 +12,8 @@ import { fireEvent } from "../common/dom/fire_event";
|
|||||||
import { computeDomain } from "../common/entity/compute_domain";
|
import { computeDomain } from "../common/entity/compute_domain";
|
||||||
import { subscribeNotifications } from "../data/persistent_notification";
|
import { subscribeNotifications } from "../data/persistent_notification";
|
||||||
import { HomeAssistant } from "../types";
|
import { HomeAssistant } from "../types";
|
||||||
import "./ha-svg-icon";
|
import "./ha-icon-button";
|
||||||
|
import { mdiMenu } from "@mdi/js";
|
||||||
|
|
||||||
@customElement("ha-menu-button")
|
@customElement("ha-menu-button")
|
||||||
class HaMenuButton extends LitElement {
|
class HaMenuButton extends LitElement {
|
||||||
|
@@ -1,11 +1,6 @@
|
|||||||
import "@material/mwc-icon-button";
|
import { mdiBell, mdiCellphoneSettingsVariant } from "@mdi/js";
|
||||||
import {
|
import "@polymer/app-layout/app-toolbar/app-toolbar";
|
||||||
mdiBell,
|
import "./ha-icon-button";
|
||||||
mdiCellphoneSettingsVariant,
|
|
||||||
mdiMenuOpen,
|
|
||||||
mdiMenu,
|
|
||||||
mdiViewDashboard,
|
|
||||||
} from "@mdi/js";
|
|
||||||
import "@polymer/paper-item/paper-icon-item";
|
import "@polymer/paper-item/paper-icon-item";
|
||||||
import type { PaperIconItemElement } from "@polymer/paper-item/paper-icon-item";
|
import type { PaperIconItemElement } from "@polymer/paper-item/paper-icon-item";
|
||||||
import "@polymer/paper-item/paper-item";
|
import "@polymer/paper-item/paper-item";
|
||||||
@@ -15,7 +10,6 @@ import {
|
|||||||
CSSResult,
|
CSSResult,
|
||||||
eventOptions,
|
eventOptions,
|
||||||
html,
|
html,
|
||||||
customElement,
|
|
||||||
LitElement,
|
LitElement,
|
||||||
property,
|
property,
|
||||||
PropertyValues,
|
PropertyValues,
|
||||||
@@ -35,9 +29,9 @@ import {
|
|||||||
getExternalConfig,
|
getExternalConfig,
|
||||||
} from "../external_app/external_config";
|
} from "../external_app/external_config";
|
||||||
import type { HomeAssistant, PanelInfo } from "../types";
|
import type { HomeAssistant, PanelInfo } from "../types";
|
||||||
|
import "./ha-svg-icon";
|
||||||
import "./ha-icon";
|
import "./ha-icon";
|
||||||
import "./ha-menu-button";
|
import "./ha-menu-button";
|
||||||
import "./ha-svg-icon";
|
|
||||||
import "./user/ha-user-badge";
|
import "./user/ha-user-badge";
|
||||||
|
|
||||||
const SHOW_AFTER_SPACER = ["config", "developer-tools", "hassio"];
|
const SHOW_AFTER_SPACER = ["config", "developer-tools", "hassio"];
|
||||||
@@ -109,7 +103,9 @@ const computePanels = (hass: HomeAssistant): [PanelInfo[], PanelInfo[]] => {
|
|||||||
return [beforeSpacer, afterSpacer];
|
return [beforeSpacer, afterSpacer];
|
||||||
};
|
};
|
||||||
|
|
||||||
@customElement("ha-sidebar")
|
/*
|
||||||
|
* @appliesMixin LocalizeMixin
|
||||||
|
*/
|
||||||
class HaSidebar extends LitElement {
|
class HaSidebar extends LitElement {
|
||||||
@property() public hass!: HomeAssistant;
|
@property() public hass!: HomeAssistant;
|
||||||
|
|
||||||
@@ -157,16 +153,13 @@ class HaSidebar extends LitElement {
|
|||||||
<div class="menu">
|
<div class="menu">
|
||||||
${!this.narrow
|
${!this.narrow
|
||||||
? html`
|
? html`
|
||||||
<mwc-icon-button
|
<ha-icon-button
|
||||||
.label=${hass.localize("ui.sidebar.sidebar_toggle")}
|
aria-label=${hass.localize("ui.sidebar.sidebar_toggle")}
|
||||||
|
.icon=${hass.dockedSidebar === "docked"
|
||||||
|
? "hass:menu-open"
|
||||||
|
: "hass:menu"}
|
||||||
@click=${this._toggleSidebar}
|
@click=${this._toggleSidebar}
|
||||||
>
|
></ha-icon-button>
|
||||||
<ha-svg-icon
|
|
||||||
.path=${hass.dockedSidebar === "docked"
|
|
||||||
? mdiMenuOpen
|
|
||||||
: mdiMenu}
|
|
||||||
></ha-svg-icon>
|
|
||||||
</mwc-icon-button>
|
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
<span class="title">Home Assistant</span>
|
<span class="title">Home Assistant</span>
|
||||||
@@ -181,16 +174,14 @@ class HaSidebar extends LitElement {
|
|||||||
>
|
>
|
||||||
${this._renderPanel(
|
${this._renderPanel(
|
||||||
defaultPanel.url_path,
|
defaultPanel.url_path,
|
||||||
defaultPanel.title || hass.localize("panel.states"),
|
defaultPanel.icon || "hass:view-dashboard",
|
||||||
defaultPanel.icon,
|
defaultPanel.title || hass.localize("panel.states")
|
||||||
!defaultPanel.icon ? mdiViewDashboard : undefined
|
|
||||||
)}
|
)}
|
||||||
${beforeSpacer.map((panel) =>
|
${beforeSpacer.map((panel) =>
|
||||||
this._renderPanel(
|
this._renderPanel(
|
||||||
panel.url_path,
|
panel.url_path,
|
||||||
hass.localize(`panel.${panel.title}`) || panel.title,
|
|
||||||
panel.icon,
|
panel.icon,
|
||||||
undefined
|
hass.localize(`panel.${panel.title}`) || panel.title
|
||||||
)
|
)
|
||||||
)}
|
)}
|
||||||
<div class="spacer" disabled></div>
|
<div class="spacer" disabled></div>
|
||||||
@@ -198,9 +189,8 @@ class HaSidebar extends LitElement {
|
|||||||
${afterSpacer.map((panel) =>
|
${afterSpacer.map((panel) =>
|
||||||
this._renderPanel(
|
this._renderPanel(
|
||||||
panel.url_path,
|
panel.url_path,
|
||||||
hass.localize(`panel.${panel.title}`) || panel.title,
|
|
||||||
panel.icon,
|
panel.icon,
|
||||||
undefined
|
hass.localize(`panel.${panel.title}`) || panel.title
|
||||||
)
|
)
|
||||||
)}
|
)}
|
||||||
${this._externalConfig && this._externalConfig.hasSettingsScreen
|
${this._externalConfig && this._externalConfig.hasSettingsScreen
|
||||||
@@ -453,12 +443,7 @@ class HaSidebar extends LitElement {
|
|||||||
fireEvent(this, "hass-toggle-menu");
|
fireEvent(this, "hass-toggle-menu");
|
||||||
}
|
}
|
||||||
|
|
||||||
private _renderPanel(
|
private _renderPanel(urlPath, icon, title) {
|
||||||
urlPath: string,
|
|
||||||
title: string | null,
|
|
||||||
icon?: string | null,
|
|
||||||
iconPath?: string | null
|
|
||||||
) {
|
|
||||||
return html`
|
return html`
|
||||||
<a
|
<a
|
||||||
aria-role="option"
|
aria-role="option"
|
||||||
@@ -469,12 +454,7 @@ class HaSidebar extends LitElement {
|
|||||||
@mouseleave=${this._itemMouseLeave}
|
@mouseleave=${this._itemMouseLeave}
|
||||||
>
|
>
|
||||||
<paper-icon-item>
|
<paper-icon-item>
|
||||||
${iconPath
|
<ha-icon slot="item-icon" .icon="${icon}"></ha-icon>
|
||||||
? html`<ha-svg-icon
|
|
||||||
slot="item-icon"
|
|
||||||
.path=${iconPath}
|
|
||||||
></ha-svg-icon>`
|
|
||||||
: html`<ha-icon slot="item-icon" .icon=${icon}></ha-icon>`}
|
|
||||||
<span class="item-text">${title}</span>
|
<span class="item-text">${title}</span>
|
||||||
</paper-icon-item>
|
</paper-icon-item>
|
||||||
</a>
|
</a>
|
||||||
@@ -516,13 +496,13 @@ class HaSidebar extends LitElement {
|
|||||||
width: 256px;
|
width: 256px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu mwc-icon-button {
|
.menu ha-icon-button {
|
||||||
color: var(--sidebar-icon-color);
|
color: var(--sidebar-icon-color);
|
||||||
}
|
}
|
||||||
:host([expanded]) .menu mwc-icon-button {
|
:host([expanded]) .menu ha-icon-button {
|
||||||
margin-right: 23px;
|
margin-right: 23px;
|
||||||
}
|
}
|
||||||
:host([expanded][_rtl]) .menu mwc-icon-button {
|
:host([expanded][_rtl]) .menu ha-icon-button {
|
||||||
margin-right: 0px;
|
margin-right: 0px;
|
||||||
margin-left: 23px;
|
margin-left: 23px;
|
||||||
}
|
}
|
||||||
@@ -734,7 +714,7 @@ class HaSidebar extends LitElement {
|
|||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
:host([_rtl]) .menu mwc-icon-button {
|
:host([_rtl]) .menu ha-icon-button {
|
||||||
-webkit-transform: scaleX(-1);
|
-webkit-transform: scaleX(-1);
|
||||||
transform: scaleX(-1);
|
transform: scaleX(-1);
|
||||||
}
|
}
|
||||||
@@ -747,3 +727,5 @@ declare global {
|
|||||||
"ha-sidebar": HaSidebar;
|
"ha-sidebar": HaSidebar;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
customElements.define("ha-sidebar", HaSidebar);
|
||||||
|
@@ -1,7 +1,15 @@
|
|||||||
|
import { ripple } from "@material/mwc-ripple/ripple-directive";
|
||||||
import "@material/mwc-switch";
|
import "@material/mwc-switch";
|
||||||
import type { Switch } from "@material/mwc-switch";
|
import type { Switch } from "@material/mwc-switch";
|
||||||
import { style } from "@material/mwc-switch/mwc-switch-css";
|
import { style } from "@material/mwc-switch/mwc-switch-css";
|
||||||
import { css, CSSResult, customElement, property } from "lit-element";
|
import {
|
||||||
|
css,
|
||||||
|
CSSResult,
|
||||||
|
customElement,
|
||||||
|
html,
|
||||||
|
property,
|
||||||
|
query,
|
||||||
|
} from "lit-element";
|
||||||
import { forwardHaptic } from "../data/haptics";
|
import { forwardHaptic } from "../data/haptics";
|
||||||
import { Constructor } from "../types";
|
import { Constructor } from "../types";
|
||||||
|
|
||||||
@@ -14,12 +22,18 @@ export class HaSwitch extends MwcSwitch {
|
|||||||
// Do not add haptic when a user is required to press save.
|
// Do not add haptic when a user is required to press save.
|
||||||
@property({ type: Boolean }) public haptic = false;
|
@property({ type: Boolean }) public haptic = false;
|
||||||
|
|
||||||
|
@query("slot") private _slot!: HTMLSlotElement;
|
||||||
|
|
||||||
protected firstUpdated() {
|
protected firstUpdated() {
|
||||||
super.firstUpdated();
|
super.firstUpdated();
|
||||||
this.style.setProperty(
|
this.style.setProperty(
|
||||||
"--mdc-theme-secondary",
|
"--mdc-theme-secondary",
|
||||||
"var(--switch-checked-color)"
|
"var(--switch-checked-color)"
|
||||||
);
|
);
|
||||||
|
this.classList.toggle(
|
||||||
|
"slotted",
|
||||||
|
Boolean(this._slot.assignedNodes().length)
|
||||||
|
);
|
||||||
this.addEventListener("change", () => {
|
this.addEventListener("change", () => {
|
||||||
if (this.haptic) {
|
if (this.haptic) {
|
||||||
forwardHaptic("light");
|
forwardHaptic("light");
|
||||||
@@ -27,10 +41,40 @@ export class HaSwitch extends MwcSwitch {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected render() {
|
||||||
|
return html`
|
||||||
|
<div class="mdc-switch">
|
||||||
|
<div class="mdc-switch__track"></div>
|
||||||
|
<div
|
||||||
|
class="mdc-switch__thumb-underlay"
|
||||||
|
.ripple="${ripple({
|
||||||
|
interactionNode: this,
|
||||||
|
})}"
|
||||||
|
>
|
||||||
|
<div class="mdc-switch__thumb">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
id="basic-switch"
|
||||||
|
class="mdc-switch__native-control"
|
||||||
|
role="switch"
|
||||||
|
@change="${this._haChangeHandler}"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<label for="basic-switch"><slot></slot></label>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
protected static get styles(): CSSResult[] {
|
protected static get styles(): CSSResult[] {
|
||||||
return [
|
return [
|
||||||
style,
|
style,
|
||||||
css`
|
css`
|
||||||
|
:host {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
.mdc-switch.mdc-switch--checked .mdc-switch__thumb {
|
.mdc-switch.mdc-switch--checked .mdc-switch__thumb {
|
||||||
background-color: var(--switch-checked-button-color);
|
background-color: var(--switch-checked-button-color);
|
||||||
border-color: var(--switch-checked-button-color);
|
border-color: var(--switch-checked-button-color);
|
||||||
@@ -47,9 +91,18 @@ export class HaSwitch extends MwcSwitch {
|
|||||||
background-color: var(--switch-unchecked-track-color);
|
background-color: var(--switch-unchecked-track-color);
|
||||||
border-color: var(--switch-unchecked-track-color);
|
border-color: var(--switch-unchecked-track-color);
|
||||||
}
|
}
|
||||||
|
:host(.slotted) .mdc-switch {
|
||||||
|
margin-right: 24px;
|
||||||
|
}
|
||||||
`,
|
`,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _haChangeHandler(e: Event) {
|
||||||
|
this.mdcFoundation.handleChange(e);
|
||||||
|
// catch "click" event and sync properties
|
||||||
|
this.checked = this.formElement.checked;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
|
46
src/components/ha-textarea.js
Normal file
46
src/components/ha-textarea.js
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
Wrapper for paper-textarea.
|
||||||
|
|
||||||
|
paper-textarea crashes on iOS when created programmatically. This only impacts
|
||||||
|
our automation and script editors as they are using Preact. Polymer is using
|
||||||
|
template elements and does not have this issue.
|
||||||
|
|
||||||
|
paper-textarea issue: https://github.com/PolymerElements/paper-input/issues/556
|
||||||
|
WebKit issue: https://bugs.webkit.org/show_bug.cgi?id=174629
|
||||||
|
*/
|
||||||
|
|
||||||
|
import "@polymer/paper-input/paper-textarea";
|
||||||
|
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||||
|
/* eslint-plugin-disable lit */
|
||||||
|
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||||
|
|
||||||
|
class HaTextarea extends PolymerElement {
|
||||||
|
static get template() {
|
||||||
|
return html`
|
||||||
|
<style>
|
||||||
|
:host {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<paper-textarea
|
||||||
|
label="[[label]]"
|
||||||
|
placeholder="[[placeholder]]"
|
||||||
|
value="{{value}}"
|
||||||
|
></paper-textarea>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
static get properties() {
|
||||||
|
return {
|
||||||
|
name: String,
|
||||||
|
label: String,
|
||||||
|
placeholder: String,
|
||||||
|
value: {
|
||||||
|
type: String,
|
||||||
|
notify: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define("ha-textarea", HaTextarea);
|
@@ -262,28 +262,6 @@ class StateHistoryChartLine extends LocalizeMixin(PolymerElement) {
|
|||||||
pushData(new Date(state.last_changed), series);
|
pushData(new Date(state.last_changed), series);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else if (domain === "humidifier") {
|
|
||||||
addColumn(
|
|
||||||
`${this.hass.localize(
|
|
||||||
"ui.card.humidifier.target_humidity_entity",
|
|
||||||
"name",
|
|
||||||
name
|
|
||||||
)}`,
|
|
||||||
true
|
|
||||||
);
|
|
||||||
addColumn(
|
|
||||||
`${this.hass.localize("ui.card.humidifier.on_entity", "name", name)}`,
|
|
||||||
true,
|
|
||||||
true
|
|
||||||
);
|
|
||||||
|
|
||||||
states.states.forEach((state) => {
|
|
||||||
if (!state.attributes) return;
|
|
||||||
const target = safeParseFloat(state.attributes.humidity);
|
|
||||||
const series = [target];
|
|
||||||
series.push(state.state === "on" ? target : null);
|
|
||||||
pushData(new Date(state.last_changed), series);
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
// Only disable interpolation for sensors
|
// Only disable interpolation for sensors
|
||||||
const isStep = domain === "sensor";
|
const isStep = domain === "sensor";
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import "./ha-circular-progress";
|
import "@polymer/paper-spinner/paper-spinner";
|
||||||
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";
|
||||||
|
@@ -1,11 +1,5 @@
|
|||||||
import { HomeAssistant } from "../types";
|
import { HomeAssistant } from "../types";
|
||||||
|
|
||||||
export interface AuthUrlSearchParams {
|
|
||||||
client_id?: string;
|
|
||||||
redirect_uri?: string;
|
|
||||||
state?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface AuthProvider {
|
export interface AuthProvider {
|
||||||
name: string;
|
name: string;
|
||||||
id: string;
|
id: string;
|
||||||
|
@@ -5,7 +5,7 @@ import { HomeAssistant } from "../types";
|
|||||||
import { DataEntryFlowProgress, DataEntryFlowStep } from "./data_entry_flow";
|
import { DataEntryFlowProgress, DataEntryFlowStep } from "./data_entry_flow";
|
||||||
import { domainToName } from "./integration";
|
import { domainToName } from "./integration";
|
||||||
|
|
||||||
export const DISCOVERY_SOURCES = ["unignore", "homekit", "ssdp", "zeroconf", "discovery"];
|
export const DISCOVERY_SOURCES = ["unignore", "homekit", "ssdp", "zeroconf"];
|
||||||
|
|
||||||
export const createConfigFlow = (hass: HomeAssistant, handler: string) =>
|
export const createConfigFlow = (hass: HomeAssistant, handler: string) =>
|
||||||
hass.callApi<DataEntryFlowStep>("POST", "config/config_entries/flow", {
|
hass.callApi<DataEntryFlowStep>("POST", "config/config_entries/flow", {
|
||||||
|
@@ -12,11 +12,6 @@ export interface ConfigUpdateValues {
|
|||||||
internal_url?: string | null;
|
internal_url?: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CheckConfigResult {
|
|
||||||
result: "valid" | "invalid";
|
|
||||||
errors: string | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const saveCoreConfig = (
|
export const saveCoreConfig = (
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
values: Partial<ConfigUpdateValues>
|
values: Partial<ConfigUpdateValues>
|
||||||
@@ -30,6 +25,3 @@ export const detectCoreConfig = (hass: HomeAssistant) =>
|
|||||||
hass.callWS<Partial<ConfigUpdateValues>>({
|
hass.callWS<Partial<ConfigUpdateValues>>({
|
||||||
type: "config/core/detect",
|
type: "config/core/detect",
|
||||||
});
|
});
|
||||||
|
|
||||||
export const checkCoreConfig = (hass: HomeAssistant) =>
|
|
||||||
hass.callApi<CheckConfigResult>("POST", "config/core/check_config");
|
|
||||||
|
@@ -4,20 +4,6 @@ import { hassioApiResultExtractor, HassioResponse } from "./common";
|
|||||||
export type HassioHomeAssistantInfo = any;
|
export type HassioHomeAssistantInfo = any;
|
||||||
export type HassioSupervisorInfo = any;
|
export type HassioSupervisorInfo = any;
|
||||||
|
|
||||||
export type HassioInfo = {
|
|
||||||
arch: string;
|
|
||||||
channel: string;
|
|
||||||
docker: string;
|
|
||||||
hassos?: string;
|
|
||||||
homeassistant: string;
|
|
||||||
hostname: string;
|
|
||||||
logging: string;
|
|
||||||
maching: string;
|
|
||||||
supervisor: string;
|
|
||||||
supported_arch: string[];
|
|
||||||
timezone: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type HassioPanelInfo = PanelInfo<
|
export type HassioPanelInfo = PanelInfo<
|
||||||
| undefined
|
| undefined
|
||||||
| {
|
| {
|
||||||
@@ -52,12 +38,6 @@ export const fetchHassioSupervisorInfo = async (hass: HomeAssistant) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const fetchHassioInfo = async (hass: HomeAssistant) => {
|
|
||||||
return hassioApiResultExtractor(
|
|
||||||
await hass.callApi<HassioResponse<HassioInfo>>("GET", "hassio/info")
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const fetchHassioLogs = async (
|
export const fetchHassioLogs = async (
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
provider: string
|
provider: string
|
||||||
|
@@ -5,15 +5,13 @@ import { computeStateName } from "../common/entity/compute_state_name";
|
|||||||
import { LocalizeFunc } from "../common/translations/localize";
|
import { LocalizeFunc } from "../common/translations/localize";
|
||||||
import { HomeAssistant } from "../types";
|
import { HomeAssistant } from "../types";
|
||||||
|
|
||||||
const DOMAINS_USE_LAST_UPDATED = ["climate", "humidifier", "water_heater"];
|
const DOMAINS_USE_LAST_UPDATED = ["climate", "water_heater"];
|
||||||
const LINE_ATTRIBUTES_TO_KEEP = [
|
const LINE_ATTRIBUTES_TO_KEEP = [
|
||||||
"temperature",
|
"temperature",
|
||||||
"current_temperature",
|
"current_temperature",
|
||||||
"target_temp_low",
|
"target_temp_low",
|
||||||
"target_temp_high",
|
"target_temp_high",
|
||||||
"hvac_action",
|
"hvac_action",
|
||||||
"humidity",
|
|
||||||
"mode",
|
|
||||||
];
|
];
|
||||||
|
|
||||||
export interface LineChartState {
|
export interface LineChartState {
|
||||||
@@ -226,8 +224,6 @@ export const computeHistory = (
|
|||||||
unit = hass.config.unit_system.temperature;
|
unit = hass.config.unit_system.temperature;
|
||||||
} else if (computeStateDomain(stateInfo[0]) === "water_heater") {
|
} else if (computeStateDomain(stateInfo[0]) === "water_heater") {
|
||||||
unit = hass.config.unit_system.temperature;
|
unit = hass.config.unit_system.temperature;
|
||||||
} else if (computeStateDomain(stateInfo[0]) === "humidifier") {
|
|
||||||
unit = "%";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!unit) {
|
if (!unit) {
|
||||||
|
@@ -1,19 +0,0 @@
|
|||||||
import {
|
|
||||||
HassEntityAttributeBase,
|
|
||||||
HassEntityBase,
|
|
||||||
} from "home-assistant-js-websocket";
|
|
||||||
|
|
||||||
export type HumidifierEntity = HassEntityBase & {
|
|
||||||
attributes: HassEntityAttributeBase & {
|
|
||||||
humidity?: number;
|
|
||||||
min_humidity?: number;
|
|
||||||
max_humidity?: number;
|
|
||||||
mode?: string;
|
|
||||||
available_modes?: string[];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export const HUMIDIFIER_SUPPORT_MODES = 1;
|
|
||||||
|
|
||||||
export const HUMIDIFIER_DEVICE_CLASS_HUMIDIFIER = "humidifier";
|
|
||||||
export const HUMIDIFIER_DEVICE_CLASS_DEHUMIDIFIER = "dehumidifier";
|
|
@@ -1,67 +1,7 @@
|
|||||||
import { HomeAssistant } from "../types";
|
|
||||||
|
|
||||||
export interface LogbookEntry {
|
export interface LogbookEntry {
|
||||||
when: string;
|
when: string;
|
||||||
name: string;
|
name: string;
|
||||||
message: string;
|
message: string;
|
||||||
entity_id?: string;
|
entity_id?: string;
|
||||||
domain: string;
|
domain: string;
|
||||||
context_user_id?: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const DATA_CACHE: {
|
|
||||||
[cacheKey: string]: { [entityId: string]: Promise<LogbookEntry[]> };
|
|
||||||
} = {};
|
|
||||||
|
|
||||||
export const getLogbookData = (
|
|
||||||
hass: HomeAssistant,
|
|
||||||
startDate: string,
|
|
||||||
endDate: string,
|
|
||||||
entityId?: string
|
|
||||||
) => {
|
|
||||||
const ALL_ENTITIES = "*";
|
|
||||||
|
|
||||||
if (!entityId) {
|
|
||||||
entityId = ALL_ENTITIES;
|
|
||||||
}
|
|
||||||
|
|
||||||
const cacheKey = `${startDate}${endDate}`;
|
|
||||||
|
|
||||||
if (!DATA_CACHE[cacheKey]) {
|
|
||||||
DATA_CACHE[cacheKey] = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (DATA_CACHE[cacheKey][entityId]) {
|
|
||||||
return DATA_CACHE[cacheKey][entityId];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (entityId !== ALL_ENTITIES && DATA_CACHE[cacheKey][ALL_ENTITIES]) {
|
|
||||||
return DATA_CACHE[cacheKey][ALL_ENTITIES].then((entities) =>
|
|
||||||
entities.filter((entity) => entity.entity_id === entityId)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
DATA_CACHE[cacheKey][entityId] = getLogbookDataFromServer(
|
|
||||||
hass,
|
|
||||||
startDate,
|
|
||||||
endDate,
|
|
||||||
entityId !== ALL_ENTITIES ? entityId : undefined
|
|
||||||
).then((entries) => entries.reverse());
|
|
||||||
return DATA_CACHE[cacheKey][entityId];
|
|
||||||
};
|
|
||||||
|
|
||||||
const getLogbookDataFromServer = async (
|
|
||||||
hass: HomeAssistant,
|
|
||||||
startDate: string,
|
|
||||||
endDate: string,
|
|
||||||
entityId?: string
|
|
||||||
) => {
|
|
||||||
const url = `logbook/${startDate}?end_time=${endDate}${
|
|
||||||
entityId ? `&entity=${entityId}` : ""
|
|
||||||
}`;
|
|
||||||
return hass.callApi<LogbookEntry[]>("GET", url);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const clearLogbookCache = (startDate, endDate) => {
|
|
||||||
DATA_CACHE[`${startDate}${endDate}`] = {};
|
|
||||||
};
|
|
||||||
|
@@ -51,7 +51,7 @@ export const onboardCoreConfigStep = (hass: HomeAssistant) =>
|
|||||||
|
|
||||||
export const onboardIntegrationStep = (
|
export const onboardIntegrationStep = (
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
params: { client_id: string; redirect_uri: string }
|
params: { client_id: string }
|
||||||
) =>
|
) =>
|
||||||
hass.callApi<OnboardingIntegrationStepResponse>(
|
hass.callApi<OnboardingIntegrationStepResponse>(
|
||||||
"POST",
|
"POST",
|
||||||
|
@@ -10,9 +10,7 @@ import {
|
|||||||
TemplateResult,
|
TemplateResult,
|
||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
import "../../components/dialog/ha-paper-dialog";
|
import "../../components/dialog/ha-paper-dialog";
|
||||||
import "../../components/ha-circular-progress";
|
|
||||||
import "../../components/ha-switch";
|
import "../../components/ha-switch";
|
||||||
import "../../components/ha-formfield";
|
|
||||||
import type { HaSwitch } from "../../components/ha-switch";
|
import type { HaSwitch } from "../../components/ha-switch";
|
||||||
import {
|
import {
|
||||||
getConfigEntrySystemOptions,
|
getConfigEntrySystemOptions,
|
||||||
@@ -76,7 +74,7 @@ class DialogConfigEntrySystemOptions extends LitElement {
|
|||||||
${this._loading
|
${this._loading
|
||||||
? html`
|
? html`
|
||||||
<div class="init-spinner">
|
<div class="init-spinner">
|
||||||
<ha-circular-progress active></ha-circular-progress>
|
<paper-spinner-lite active></paper-spinner-lite>
|
||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
: html`
|
: html`
|
||||||
@@ -84,8 +82,13 @@ class DialogConfigEntrySystemOptions extends LitElement {
|
|||||||
? html` <div class="error">${this._error}</div> `
|
? html` <div class="error">${this._error}</div> `
|
||||||
: ""}
|
: ""}
|
||||||
<div class="form">
|
<div class="form">
|
||||||
<ha-formfield
|
<ha-switch
|
||||||
.label=${html`<p>
|
.checked=${!this._disableNewEntities}
|
||||||
|
@change=${this._disableNewEntitiesChanged}
|
||||||
|
.disabled=${this._submitting}
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<p>
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.dialogs.config_entry_system_options.enable_new_entities_label"
|
"ui.dialogs.config_entry_system_options.enable_new_entities_label"
|
||||||
)}
|
)}
|
||||||
@@ -98,15 +101,9 @@ class DialogConfigEntrySystemOptions extends LitElement {
|
|||||||
`component.${this._params.entry.domain}.title`
|
`component.${this._params.entry.domain}.title`
|
||||||
) || this._params.entry.domain
|
) || this._params.entry.domain
|
||||||
)}
|
)}
|
||||||
</p>`}
|
</p>
|
||||||
>
|
</div>
|
||||||
<ha-switch
|
</ha-switch>
|
||||||
.checked=${!this._disableNewEntities}
|
|
||||||
@change=${this._disableNewEntitiesChanged}
|
|
||||||
.disabled=${this._submitting}
|
|
||||||
>
|
|
||||||
</ha-switch>
|
|
||||||
</ha-formfield>
|
|
||||||
</div>
|
</div>
|
||||||
`}
|
`}
|
||||||
</paper-dialog-scrollable>
|
</paper-dialog-scrollable>
|
||||||
@@ -175,6 +172,9 @@ class DialogConfigEntrySystemOptions extends LitElement {
|
|||||||
padding-bottom: 24px;
|
padding-bottom: 24px;
|
||||||
color: var(--primary-text-color);
|
color: var(--primary-text-color);
|
||||||
}
|
}
|
||||||
|
p {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
.secondary {
|
.secondary {
|
||||||
color: var(--secondary-text-color);
|
color: var(--secondary-text-color);
|
||||||
}
|
}
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
import "@material/mwc-button";
|
import "@material/mwc-button";
|
||||||
import "@polymer/paper-dialog-scrollable/paper-dialog-scrollable";
|
import "@polymer/paper-dialog-scrollable/paper-dialog-scrollable";
|
||||||
import "../../components/ha-icon-button";
|
import "../../components/ha-icon-button";
|
||||||
import "../../components/ha-circular-progress";
|
import "@polymer/paper-spinner/paper-spinner";
|
||||||
import "@polymer/paper-tooltip/paper-tooltip";
|
import "@polymer/paper-tooltip/paper-tooltip";
|
||||||
import type { UnsubscribeFunc } from "home-assistant-js-websocket";
|
import type { UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||||
import {
|
import {
|
||||||
@@ -14,7 +14,8 @@ import {
|
|||||||
PropertyValues,
|
PropertyValues,
|
||||||
TemplateResult,
|
TemplateResult,
|
||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
import "../../components/ha-dialog";
|
import "../../components/dialog/ha-paper-dialog";
|
||||||
|
import type { HaPaperDialog } from "../../components/dialog/ha-paper-dialog";
|
||||||
import "../../components/ha-form/ha-form";
|
import "../../components/ha-form/ha-form";
|
||||||
import "../../components/ha-markdown";
|
import "../../components/ha-markdown";
|
||||||
import {
|
import {
|
||||||
@@ -26,6 +27,7 @@ import {
|
|||||||
DeviceRegistryEntry,
|
DeviceRegistryEntry,
|
||||||
subscribeDeviceRegistry,
|
subscribeDeviceRegistry,
|
||||||
} from "../../data/device_registry";
|
} from "../../data/device_registry";
|
||||||
|
import { PolymerChangedEvent } from "../../polymer-types";
|
||||||
import { haStyleDialog } from "../../resources/styles";
|
import { haStyleDialog } from "../../resources/styles";
|
||||||
import type { HomeAssistant } from "../../types";
|
import type { HomeAssistant } from "../../types";
|
||||||
import { DataEntryFlowDialogParams } from "./show-dialog-data-entry-flow";
|
import { DataEntryFlowDialogParams } from "./show-dialog-data-entry-flow";
|
||||||
@@ -88,6 +90,7 @@ class DataEntryFlowDialog extends LitElement {
|
|||||||
// We only load the handlers once
|
// We only load the handlers once
|
||||||
if (this._handlers === undefined) {
|
if (this._handlers === undefined) {
|
||||||
this._loading = true;
|
this._loading = true;
|
||||||
|
this.updateComplete.then(() => this._scheduleCenterDialog());
|
||||||
try {
|
try {
|
||||||
this._handlers = await params.flowConfig.getFlowHandlers(this.hass);
|
this._handlers = await params.flowConfig.getFlowHandlers(this.hass);
|
||||||
} finally {
|
} finally {
|
||||||
@@ -95,6 +98,7 @@ class DataEntryFlowDialog extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
await this.updateComplete;
|
await this.updateComplete;
|
||||||
|
this._scheduleCenterDialog();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,6 +115,9 @@ class DataEntryFlowDialog extends LitElement {
|
|||||||
|
|
||||||
this._processStep(step);
|
this._processStep(step);
|
||||||
this._loading = false;
|
this._loading = false;
|
||||||
|
// When the flow changes, center the dialog.
|
||||||
|
// Don't do it on each step or else the dialog keeps bouncing.
|
||||||
|
this._scheduleCenterDialog();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
@@ -119,84 +126,80 @@ class DataEntryFlowDialog extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<ha-dialog
|
<ha-paper-dialog
|
||||||
open
|
with-backdrop
|
||||||
@closing=${this._close}
|
opened
|
||||||
scrimClickAction
|
modal
|
||||||
escapeKeyAction
|
@opened-changed=${this._openedChanged}
|
||||||
hideActions
|
|
||||||
>
|
>
|
||||||
<div>
|
${this._loading || (this._step === null && this._handlers === undefined)
|
||||||
${this._loading ||
|
? html`
|
||||||
(this._step === null && this._handlers === undefined)
|
<step-flow-loading
|
||||||
? html`
|
.label=${this.hass.localize(
|
||||||
<step-flow-loading
|
"ui.panel.config.integrations.config_flow.loading_first_time"
|
||||||
.label=${this.hass.localize(
|
)}
|
||||||
"ui.panel.config.integrations.config_flow.loading_first_time"
|
></step-flow-loading>
|
||||||
)}
|
`
|
||||||
></step-flow-loading>
|
: this._step === undefined
|
||||||
`
|
? // When we are going to next step, we render 1 round of empty
|
||||||
: this._step === undefined
|
// to reset the element.
|
||||||
? // When we are going to next step, we render 1 round of empty
|
""
|
||||||
// to reset the element.
|
: html`
|
||||||
""
|
<ha-icon-button
|
||||||
: html`
|
aria-label=${this.hass.localize(
|
||||||
<ha-icon-button
|
"ui.panel.config.integrations.config_flow.dismiss"
|
||||||
aria-label=${this.hass.localize(
|
)}
|
||||||
"ui.panel.config.integrations.config_flow.dismiss"
|
icon="hass:close"
|
||||||
)}
|
dialog-dismiss
|
||||||
icon="hass:close"
|
></ha-icon-button>
|
||||||
dialogAction="close"
|
${this._step === null
|
||||||
></ha-icon-button>
|
? // Show handler picker
|
||||||
${this._step === null
|
html`
|
||||||
? // Show handler picker
|
<step-flow-pick-handler
|
||||||
html`
|
.flowConfig=${this._params.flowConfig}
|
||||||
<step-flow-pick-handler
|
.hass=${this.hass}
|
||||||
.flowConfig=${this._params.flowConfig}
|
.handlers=${this._handlers}
|
||||||
.hass=${this.hass}
|
.showAdvanced=${this._params.showAdvanced}
|
||||||
.handlers=${this._handlers}
|
></step-flow-pick-handler>
|
||||||
.showAdvanced=${this._params.showAdvanced}
|
`
|
||||||
></step-flow-pick-handler>
|
: this._step.type === "form"
|
||||||
`
|
? html`
|
||||||
: this._step.type === "form"
|
<step-flow-form
|
||||||
? html`
|
.flowConfig=${this._params.flowConfig}
|
||||||
<step-flow-form
|
.step=${this._step}
|
||||||
.flowConfig=${this._params.flowConfig}
|
.hass=${this.hass}
|
||||||
.step=${this._step}
|
></step-flow-form>
|
||||||
.hass=${this.hass}
|
`
|
||||||
></step-flow-form>
|
: this._step.type === "external"
|
||||||
`
|
? html`
|
||||||
: this._step.type === "external"
|
<step-flow-external
|
||||||
? html`
|
.flowConfig=${this._params.flowConfig}
|
||||||
<step-flow-external
|
.step=${this._step}
|
||||||
.flowConfig=${this._params.flowConfig}
|
.hass=${this.hass}
|
||||||
.step=${this._step}
|
></step-flow-external>
|
||||||
.hass=${this.hass}
|
`
|
||||||
></step-flow-external>
|
: this._step.type === "abort"
|
||||||
`
|
? html`
|
||||||
: this._step.type === "abort"
|
<step-flow-abort
|
||||||
? html`
|
.flowConfig=${this._params.flowConfig}
|
||||||
<step-flow-abort
|
.step=${this._step}
|
||||||
.flowConfig=${this._params.flowConfig}
|
.hass=${this.hass}
|
||||||
.step=${this._step}
|
></step-flow-abort>
|
||||||
.hass=${this.hass}
|
`
|
||||||
></step-flow-abort>
|
: this._devices === undefined || this._areas === undefined
|
||||||
`
|
? // When it's a create entry result, we will fetch device & area registry
|
||||||
: this._devices === undefined || this._areas === undefined
|
html` <step-flow-loading></step-flow-loading> `
|
||||||
? // When it's a create entry result, we will fetch device & area registry
|
: html`
|
||||||
html` <step-flow-loading></step-flow-loading> `
|
<step-flow-create-entry
|
||||||
: html`
|
.flowConfig=${this._params.flowConfig}
|
||||||
<step-flow-create-entry
|
.step=${this._step}
|
||||||
.flowConfig=${this._params.flowConfig}
|
.hass=${this.hass}
|
||||||
.step=${this._step}
|
.devices=${this._devices}
|
||||||
.hass=${this.hass}
|
.areas=${this._areas}
|
||||||
.devices=${this._devices}
|
></step-flow-create-entry>
|
||||||
.areas=${this._areas}
|
`}
|
||||||
></step-flow-create-entry>
|
`}
|
||||||
`}
|
</ha-paper-dialog>
|
||||||
`}
|
|
||||||
</div>
|
|
||||||
</ha-dialog>
|
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -222,6 +225,18 @@ class DataEntryFlowDialog extends LitElement {
|
|||||||
this._areas = [];
|
this._areas = [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (changedProps.has("_devices") && this._dialog) {
|
||||||
|
this._scheduleCenterDialog();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _scheduleCenterDialog() {
|
||||||
|
setTimeout(() => this._dialog.center(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private get _dialog(): HaPaperDialog {
|
||||||
|
return this.shadowRoot!.querySelector("ha-paper-dialog")!;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _fetchDevices(configEntryId) {
|
private async _fetchDevices(configEntryId) {
|
||||||
@@ -295,13 +310,16 @@ class DataEntryFlowDialog extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _close(): void {
|
private _openedChanged(ev: PolymerChangedEvent<boolean>): void {
|
||||||
if (this._step) {
|
// Closed dialog by clicking on the overlay
|
||||||
this._flowDone();
|
if (!ev.detail.value) {
|
||||||
} else if (this._step === null) {
|
if (this._step) {
|
||||||
// Flow aborted during picking flow
|
this._flowDone();
|
||||||
this._step = undefined;
|
} else if (this._step === null) {
|
||||||
this._params = undefined;
|
// Flow aborted during picking flow
|
||||||
|
this._step = undefined;
|
||||||
|
this._params = undefined;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -309,14 +327,18 @@ class DataEntryFlowDialog extends LitElement {
|
|||||||
return [
|
return [
|
||||||
haStyleDialog,
|
haStyleDialog,
|
||||||
css`
|
css`
|
||||||
ha-dialog {
|
ha-paper-dialog {
|
||||||
--dialog-content-padding: 0;
|
max-width: 600px;
|
||||||
|
}
|
||||||
|
ha-paper-dialog > * {
|
||||||
|
margin: 0;
|
||||||
|
display: block;
|
||||||
|
padding: 0;
|
||||||
}
|
}
|
||||||
ha-icon-button {
|
ha-icon-button {
|
||||||
padding: 16px;
|
display: inline-block;
|
||||||
position: absolute;
|
padding: 8px;
|
||||||
top: 0;
|
float: right;
|
||||||
right: 0;
|
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
];
|
];
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import "@material/mwc-button";
|
import "@material/mwc-button";
|
||||||
import "../../components/ha-circular-progress";
|
import "@polymer/paper-spinner/paper-spinner";
|
||||||
import "@polymer/paper-tooltip/paper-tooltip";
|
import "@polymer/paper-tooltip/paper-tooltip";
|
||||||
import {
|
import {
|
||||||
css,
|
css,
|
||||||
@@ -76,7 +76,7 @@ class StepFlowForm extends LitElement {
|
|||||||
${this._loading
|
${this._loading
|
||||||
? html`
|
? html`
|
||||||
<div class="submit-spinner">
|
<div class="submit-spinner">
|
||||||
<ha-circular-progress active></ha-circular-progress>
|
<paper-spinner active></paper-spinner>
|
||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
: html`
|
: html`
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
import "@polymer/paper-spinner/paper-spinner-lite";
|
||||||
import {
|
import {
|
||||||
css,
|
css,
|
||||||
CSSResult,
|
CSSResult,
|
||||||
@@ -7,7 +8,6 @@ import {
|
|||||||
property,
|
property,
|
||||||
TemplateResult,
|
TemplateResult,
|
||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
import "../../components/ha-circular-progress";
|
|
||||||
|
|
||||||
@customElement("step-flow-loading")
|
@customElement("step-flow-loading")
|
||||||
class StepFlowLoading extends LitElement {
|
class StepFlowLoading extends LitElement {
|
||||||
@@ -17,7 +17,7 @@ class StepFlowLoading extends LitElement {
|
|||||||
return html`
|
return html`
|
||||||
<div class="init-spinner">
|
<div class="init-spinner">
|
||||||
${this.label ? html` <div>${this.label}</div> ` : ""}
|
${this.label ? html` <div>${this.label}</div> ` : ""}
|
||||||
<ha-circular-progress active></ha-circular-progress>
|
<paper-spinner-lite active></paper-spinner-lite>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
@@ -28,7 +28,7 @@ class StepFlowLoading extends LitElement {
|
|||||||
padding: 50px 100px;
|
padding: 50px 100px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
ha-circular-progress {
|
paper-spinner-lite {
|
||||||
margin-top: 16px;
|
margin-top: 16px;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
import "@polymer/paper-item/paper-icon-item";
|
import "@polymer/paper-item/paper-icon-item";
|
||||||
import "@polymer/paper-item/paper-item-body";
|
import "@polymer/paper-item/paper-item-body";
|
||||||
|
import "@polymer/paper-spinner/paper-spinner-lite";
|
||||||
import Fuse from "fuse.js";
|
import Fuse from "fuse.js";
|
||||||
import {
|
import {
|
||||||
css,
|
css,
|
||||||
@@ -41,8 +42,6 @@ class StepFlowPickHandler extends LitElement {
|
|||||||
|
|
||||||
private _width?: number;
|
private _width?: number;
|
||||||
|
|
||||||
private _height?: number;
|
|
||||||
|
|
||||||
private _getHandlers = memoizeOne(
|
private _getHandlers = memoizeOne(
|
||||||
(h: string[], filter?: string, _localize?: LocalizeFunc) => {
|
(h: string[], filter?: string, _localize?: LocalizeFunc) => {
|
||||||
const handlers: HandlerObj[] = h.map((handler) => {
|
const handlers: HandlerObj[] = h.map((handler) => {
|
||||||
@@ -83,10 +82,7 @@ class StepFlowPickHandler extends LitElement {
|
|||||||
@value-changed=${this._filterChanged}
|
@value-changed=${this._filterChanged}
|
||||||
></search-input>
|
></search-input>
|
||||||
<div
|
<div
|
||||||
style=${styleMap({
|
style=${styleMap({ width: `${this._width}px` })}
|
||||||
width: `${this._width}px`,
|
|
||||||
height: `${this._height}px`,
|
|
||||||
})}
|
|
||||||
class=${classMap({ advanced: Boolean(this.showAdvanced) })}
|
class=${classMap({ advanced: Boolean(this.showAdvanced) })}
|
||||||
>
|
>
|
||||||
${handlers.map(
|
${handlers.map(
|
||||||
@@ -143,20 +139,13 @@ class StepFlowPickHandler extends LitElement {
|
|||||||
|
|
||||||
protected updated(changedProps) {
|
protected updated(changedProps) {
|
||||||
super.updated(changedProps);
|
super.updated(changedProps);
|
||||||
// Store the width and height so that when we search, box doesn't jump
|
// Store the width so that when we search, box doesn't jump
|
||||||
const div = this.shadowRoot!.querySelector("div")!;
|
|
||||||
if (!this._width) {
|
if (!this._width) {
|
||||||
const width = div.clientWidth;
|
const width = this.shadowRoot!.querySelector("div")!.clientWidth;
|
||||||
if (width) {
|
if (width) {
|
||||||
this._width = width;
|
this._width = width;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!this._height) {
|
|
||||||
const height = div.clientHeight;
|
|
||||||
if (height) {
|
|
||||||
this._height = height;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _filterChanged(e) {
|
private async _filterChanged(e) {
|
||||||
@@ -177,8 +166,8 @@ class StepFlowPickHandler extends LitElement {
|
|||||||
configFlowContentStyles,
|
configFlowContentStyles,
|
||||||
css`
|
css`
|
||||||
img {
|
img {
|
||||||
width: 40px;
|
max-width: 40px;
|
||||||
height: 40px;
|
max-height: 40px;
|
||||||
}
|
}
|
||||||
search-input {
|
search-input {
|
||||||
display: block;
|
display: block;
|
||||||
@@ -191,12 +180,12 @@ class StepFlowPickHandler extends LitElement {
|
|||||||
overflow: auto;
|
overflow: auto;
|
||||||
max-height: 600px;
|
max-height: 600px;
|
||||||
}
|
}
|
||||||
@media all and (max-height: 900px) {
|
@media all and (max-height: 1px) {
|
||||||
div {
|
div {
|
||||||
max-height: calc(100vh - 134px);
|
max-height: calc(100vh - 205px);
|
||||||
}
|
}
|
||||||
div.advanced {
|
div.advanced {
|
||||||
max-height: calc(100vh - 250px);
|
max-height: calc(100vh - 300px);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
paper-icon-item {
|
paper-icon-item {
|
||||||
|
@@ -2,21 +2,8 @@ import { css } from "lit-element";
|
|||||||
|
|
||||||
export const configFlowContentStyles = css`
|
export const configFlowContentStyles = css`
|
||||||
h2 {
|
h2 {
|
||||||
margin: 24px 0 0;
|
margin-top: 24px;
|
||||||
padding: 0 24px;
|
padding: 0 24px;
|
||||||
-moz-osx-font-smoothing: grayscale;
|
|
||||||
-webkit-font-smoothing: antialiased;
|
|
||||||
font-family: var(
|
|
||||||
--mdc-typography-headline6-font-family,
|
|
||||||
var(--mdc-typography-font-family, Roboto, sans-serif)
|
|
||||||
);
|
|
||||||
font-size: var(--mdc-typography-headline6-font-size, 1.25rem);
|
|
||||||
line-height: var(--mdc-typography-headline6-line-height, 2rem);
|
|
||||||
font-weight: var(--mdc-typography-headline6-font-weight, 500);
|
|
||||||
letter-spacing: var(--mdc-typography-headline6-letter-spacing, 0.0125em);
|
|
||||||
text-decoration: var(--mdc-typography-headline6-text-decoration, inherit);
|
|
||||||
text-transform: var(--mdc-typography-headline6-text-transform, inherit);
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.content {
|
.content {
|
||||||
|
@@ -66,7 +66,7 @@ class DialogDeviceRegistryDetail extends LitElement {
|
|||||||
<paper-input
|
<paper-input
|
||||||
.value=${this._nameByUser}
|
.value=${this._nameByUser}
|
||||||
@value-changed=${this._nameChanged}
|
@value-changed=${this._nameChanged}
|
||||||
.label=${this.hass.localize("ui.panel.config.devices.name")}
|
.label=${this.hass.localize("ui.dialogs.devices.name")}
|
||||||
.placeholder=${device.name || ""}
|
.placeholder=${device.name || ""}
|
||||||
.disabled=${this._submitting}
|
.disabled=${this._submitting}
|
||||||
></paper-input>
|
></paper-input>
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
import "@material/mwc-button/mwc-button";
|
import "@material/mwc-button/mwc-button";
|
||||||
|
import "@polymer/paper-dialog-scrollable/paper-dialog-scrollable";
|
||||||
import "@polymer/paper-input/paper-input";
|
import "@polymer/paper-input/paper-input";
|
||||||
import {
|
import {
|
||||||
css,
|
css,
|
||||||
@@ -10,7 +11,7 @@ import {
|
|||||||
TemplateResult,
|
TemplateResult,
|
||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
import { classMap } from "lit-html/directives/class-map";
|
import { classMap } from "lit-html/directives/class-map";
|
||||||
import "../../components/ha-dialog";
|
import "../../components/dialog/ha-paper-dialog";
|
||||||
import "../../components/ha-switch";
|
import "../../components/ha-switch";
|
||||||
import { PolymerChangedEvent } from "../../polymer-types";
|
import { PolymerChangedEvent } from "../../polymer-types";
|
||||||
import { haStyleDialog } from "../../resources/styles";
|
import { haStyleDialog } from "../../resources/styles";
|
||||||
@@ -40,17 +41,21 @@ class DialogBox extends LitElement {
|
|||||||
const confirmPrompt = this._params.confirmation || this._params.prompt;
|
const confirmPrompt = this._params.confirmation || this._params.prompt;
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<ha-dialog
|
<ha-paper-dialog
|
||||||
open
|
with-backdrop
|
||||||
scrimClickAction
|
opened
|
||||||
escapeKeyAction
|
modal
|
||||||
@close=${this._close}
|
@opened-changed="${this._openedChanged}"
|
||||||
.heading=${this._params.title
|
|
||||||
? this._params.title
|
|
||||||
: this._params.confirmation &&
|
|
||||||
this.hass.localize("ui.dialogs.generic.default_confirmation_title")}
|
|
||||||
>
|
>
|
||||||
<div>
|
<h2>
|
||||||
|
${this._params.title
|
||||||
|
? this._params.title
|
||||||
|
: this._params.confirmation &&
|
||||||
|
this.hass.localize(
|
||||||
|
"ui.dialogs.generic.default_confirmation_title"
|
||||||
|
)}
|
||||||
|
</h2>
|
||||||
|
<paper-dialog-scrollable>
|
||||||
${this._params.text
|
${this._params.text
|
||||||
? html`
|
? html`
|
||||||
<p
|
<p
|
||||||
@@ -78,21 +83,23 @@ class DialogBox extends LitElement {
|
|||||||
></paper-input>
|
></paper-input>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
</div>
|
</paper-dialog-scrollable>
|
||||||
${confirmPrompt &&
|
<div class="paper-dialog-buttons">
|
||||||
html`
|
${confirmPrompt &&
|
||||||
<mwc-button @click=${this._dismiss} slot="secondaryAction">
|
html`
|
||||||
${this._params.dismissText
|
<mwc-button @click="${this._dismiss}">
|
||||||
? this._params.dismissText
|
${this._params.dismissText
|
||||||
: this.hass.localize("ui.dialogs.generic.cancel")}
|
? this._params.dismissText
|
||||||
|
: this.hass.localize("ui.dialogs.generic.cancel")}
|
||||||
|
</mwc-button>
|
||||||
|
`}
|
||||||
|
<mwc-button @click="${this._confirm}">
|
||||||
|
${this._params.confirmText
|
||||||
|
? this._params.confirmText
|
||||||
|
: this.hass.localize("ui.dialogs.generic.ok")}
|
||||||
</mwc-button>
|
</mwc-button>
|
||||||
`}
|
</div>
|
||||||
<mwc-button @click=${this._confirm} slot="primaryAction">
|
</ha-paper-dialog>
|
||||||
${this._params.confirmText
|
|
||||||
? this._params.confirmText
|
|
||||||
: this.hass.localize("ui.dialogs.generic.ok")}
|
|
||||||
</mwc-button>
|
|
||||||
</ha-dialog>
|
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,8 +127,10 @@ class DialogBox extends LitElement {
|
|||||||
this._dismiss();
|
this._dismiss();
|
||||||
}
|
}
|
||||||
|
|
||||||
private _close(): void {
|
private _openedChanged(ev: PolymerChangedEvent<boolean>): void {
|
||||||
this._params = undefined;
|
if (!(ev.detail as any).value) {
|
||||||
|
this._params = undefined;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styles(): CSSResult[] {
|
static get styles(): CSSResult[] {
|
||||||
@@ -132,6 +141,15 @@ class DialogBox extends LitElement {
|
|||||||
pointer-events: initial !important;
|
pointer-events: initial !important;
|
||||||
cursor: initial !important;
|
cursor: initial !important;
|
||||||
}
|
}
|
||||||
|
ha-paper-dialog {
|
||||||
|
min-width: 400px;
|
||||||
|
max-width: 500px;
|
||||||
|
}
|
||||||
|
@media (max-width: 400px) {
|
||||||
|
ha-paper-dialog {
|
||||||
|
min-width: initial;
|
||||||
|
}
|
||||||
|
}
|
||||||
a {
|
a {
|
||||||
color: var(--primary-color);
|
color: var(--primary-color);
|
||||||
}
|
}
|
||||||
@@ -147,10 +165,6 @@ class DialogBox extends LitElement {
|
|||||||
.secondary {
|
.secondary {
|
||||||
color: var(--secondary-text-color);
|
color: var(--secondary-text-color);
|
||||||
}
|
}
|
||||||
ha-dialog {
|
|
||||||
/* Place above other dialogs */
|
|
||||||
--dialog-z-index: 104;
|
|
||||||
}
|
|
||||||
`,
|
`,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@@ -2,7 +2,7 @@ import "@material/mwc-button";
|
|||||||
import "@polymer/iron-flex-layout/iron-flex-layout-classes";
|
import "@polymer/iron-flex-layout/iron-flex-layout-classes";
|
||||||
import "@polymer/iron-input/iron-input";
|
import "@polymer/iron-input/iron-input";
|
||||||
import "@polymer/paper-input/paper-input";
|
import "@polymer/paper-input/paper-input";
|
||||||
import "../../../components/ha-circular-progress";
|
import "@polymer/paper-spinner/paper-spinner";
|
||||||
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";
|
||||||
@@ -38,7 +38,7 @@ class MoreInfoConfigurator extends PolymerElement {
|
|||||||
height: 41px;
|
height: 41px;
|
||||||
}
|
}
|
||||||
|
|
||||||
ha-circular-progress {
|
paper-spinner {
|
||||||
width: 14px;
|
width: 14px;
|
||||||
height: 14px;
|
height: 14px;
|
||||||
margin-right: 20px;
|
margin-right: 20px;
|
||||||
@@ -75,11 +75,11 @@ class MoreInfoConfigurator extends PolymerElement {
|
|||||||
disabled="[[isConfiguring]]"
|
disabled="[[isConfiguring]]"
|
||||||
on-click="submitClicked"
|
on-click="submitClicked"
|
||||||
>
|
>
|
||||||
<ha-circular-progress
|
<paper-spinner
|
||||||
active="[[isConfiguring]]"
|
active="[[isConfiguring]]"
|
||||||
hidden="[[!isConfiguring]]"
|
hidden="[[!isConfiguring]]"
|
||||||
alt="Configuring"
|
alt="Configuring"
|
||||||
></ha-circular-progress>
|
></paper-spinner>
|
||||||
[[stateObj.attributes.submit_caption]]
|
[[stateObj.attributes.submit_caption]]
|
||||||
</mwc-button>
|
</mwc-button>
|
||||||
</p>
|
</p>
|
||||||
|
@@ -14,7 +14,6 @@ import "./more-info-default";
|
|||||||
import "./more-info-fan";
|
import "./more-info-fan";
|
||||||
import "./more-info-group";
|
import "./more-info-group";
|
||||||
import "./more-info-history_graph";
|
import "./more-info-history_graph";
|
||||||
import "./more-info-humidifier";
|
|
||||||
import "./more-info-input_datetime";
|
import "./more-info-input_datetime";
|
||||||
import "./more-info-light";
|
import "./more-info-light";
|
||||||
import "./more-info-lock";
|
import "./more-info-lock";
|
||||||
|
@@ -1,218 +0,0 @@
|
|||||||
import "@polymer/iron-flex-layout/iron-flex-layout-classes";
|
|
||||||
import "@polymer/paper-item/paper-item";
|
|
||||||
import "@polymer/paper-listbox/paper-listbox";
|
|
||||||
import {
|
|
||||||
css,
|
|
||||||
CSSResult,
|
|
||||||
html,
|
|
||||||
LitElement,
|
|
||||||
property,
|
|
||||||
PropertyValues,
|
|
||||||
TemplateResult,
|
|
||||||
} from "lit-element";
|
|
||||||
import { classMap } from "lit-html/directives/class-map";
|
|
||||||
import { fireEvent } from "../../../common/dom/fire_event";
|
|
||||||
import { supportsFeature } from "../../../common/entity/supports-feature";
|
|
||||||
import { computeRTLDirection } from "../../../common/util/compute_rtl";
|
|
||||||
import "../../../components/ha-paper-dropdown-menu";
|
|
||||||
import "../../../components/ha-paper-slider";
|
|
||||||
import "../../../components/ha-switch";
|
|
||||||
import {
|
|
||||||
HumidifierEntity,
|
|
||||||
HUMIDIFIER_SUPPORT_MODES,
|
|
||||||
} from "../../../data/humidifier";
|
|
||||||
import { HomeAssistant } from "../../../types";
|
|
||||||
|
|
||||||
class MoreInfoHumidifier extends LitElement {
|
|
||||||
@property() public hass!: HomeAssistant;
|
|
||||||
|
|
||||||
@property() public stateObj?: HumidifierEntity;
|
|
||||||
|
|
||||||
private _resizeDebounce?: number;
|
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
|
||||||
if (!this.stateObj) {
|
|
||||||
return html``;
|
|
||||||
}
|
|
||||||
|
|
||||||
const hass = this.hass;
|
|
||||||
const stateObj = this.stateObj;
|
|
||||||
|
|
||||||
const supportModes = supportsFeature(stateObj, HUMIDIFIER_SUPPORT_MODES);
|
|
||||||
|
|
||||||
const rtlDirection = computeRTLDirection(hass);
|
|
||||||
|
|
||||||
return html`
|
|
||||||
<div
|
|
||||||
class=${classMap({
|
|
||||||
"has-modes": supportModes,
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
<div class="container-humidity">
|
|
||||||
<div>${hass.localize("ui.card.humidifier.humidity")}</div>
|
|
||||||
<div class="single-row">
|
|
||||||
<div class="target-humidity">
|
|
||||||
${stateObj.attributes.humidity} %
|
|
||||||
</div>
|
|
||||||
<ha-paper-slider
|
|
||||||
class="humidity"
|
|
||||||
step="1"
|
|
||||||
pin
|
|
||||||
ignore-bar-touch
|
|
||||||
dir=${rtlDirection}
|
|
||||||
.min=${stateObj.attributes.min_humidity}
|
|
||||||
.max=${stateObj.attributes.max_humidity}
|
|
||||||
.secondaryProgress=${stateObj.attributes.max_humidity}
|
|
||||||
.value=${stateObj.attributes.humidity}
|
|
||||||
@change=${this._targetHumiditySliderChanged}
|
|
||||||
>
|
|
||||||
</ha-paper-slider>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
${supportModes
|
|
||||||
? html`
|
|
||||||
<div class="container-modes">
|
|
||||||
<ha-paper-dropdown-menu
|
|
||||||
label-float
|
|
||||||
dynamic-align
|
|
||||||
.label=${hass.localize("ui.card.humidifier.mode")}
|
|
||||||
>
|
|
||||||
<paper-listbox
|
|
||||||
slot="dropdown-content"
|
|
||||||
attr-for-selected="item-name"
|
|
||||||
.selected=${stateObj.attributes.mode}
|
|
||||||
@selected-changed=${this._handleModeChanged}
|
|
||||||
>
|
|
||||||
${stateObj.attributes.available_modes!.map(
|
|
||||||
(mode) => html`
|
|
||||||
<paper-item item-name=${mode}>
|
|
||||||
${hass.localize(
|
|
||||||
`state_attributes.humidifier.mode.${mode}`
|
|
||||||
) || mode}
|
|
||||||
</paper-item>
|
|
||||||
`
|
|
||||||
)}
|
|
||||||
</paper-listbox>
|
|
||||||
</ha-paper-dropdown-menu>
|
|
||||||
</div>
|
|
||||||
`
|
|
||||||
: ""}
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected updated(changedProps: PropertyValues) {
|
|
||||||
super.updated(changedProps);
|
|
||||||
if (!changedProps.has("stateObj") || !this.stateObj) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this._resizeDebounce) {
|
|
||||||
clearTimeout(this._resizeDebounce);
|
|
||||||
}
|
|
||||||
this._resizeDebounce = window.setTimeout(() => {
|
|
||||||
fireEvent(this, "iron-resize");
|
|
||||||
this._resizeDebounce = undefined;
|
|
||||||
}, 500);
|
|
||||||
}
|
|
||||||
|
|
||||||
private _targetHumiditySliderChanged(ev) {
|
|
||||||
const newVal = ev.target.value;
|
|
||||||
this._callServiceHelper(
|
|
||||||
this.stateObj!.attributes.humidity,
|
|
||||||
newVal,
|
|
||||||
"set_humidity",
|
|
||||||
{ humidity: newVal }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private _handleModeChanged(ev) {
|
|
||||||
const newVal = ev.detail.value || null;
|
|
||||||
this._callServiceHelper(
|
|
||||||
this.stateObj!.attributes.mode,
|
|
||||||
newVal,
|
|
||||||
"set_mode",
|
|
||||||
{ mode: newVal }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async _callServiceHelper(
|
|
||||||
oldVal: unknown,
|
|
||||||
newVal: unknown,
|
|
||||||
service: string,
|
|
||||||
data: {
|
|
||||||
entity_id?: string;
|
|
||||||
[key: string]: unknown;
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
if (oldVal === newVal) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
data.entity_id = this.stateObj!.entity_id;
|
|
||||||
const curState = this.stateObj;
|
|
||||||
|
|
||||||
await this.hass.callService("humidifier", service, data);
|
|
||||||
|
|
||||||
// We reset stateObj to re-sync the inputs with the state. It will be out
|
|
||||||
// of sync if our service call did not result in the entity to be turned
|
|
||||||
// on. Since the state is not changing, the resync is not called automatic.
|
|
||||||
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
||||||
|
|
||||||
// No need to resync if we received a new state.
|
|
||||||
if (this.stateObj !== curState) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.stateObj = undefined;
|
|
||||||
await this.updateComplete;
|
|
||||||
// Only restore if not set yet by a state change
|
|
||||||
if (this.stateObj === undefined) {
|
|
||||||
this.stateObj = curState;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static get styles(): CSSResult {
|
|
||||||
return css`
|
|
||||||
:host {
|
|
||||||
color: var(--primary-text-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
ha-paper-dropdown-menu {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
paper-item {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
ha-paper-slider {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.container-humidity .single-row {
|
|
||||||
display: flex;
|
|
||||||
height: 50px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.target-humidity {
|
|
||||||
width: 90px;
|
|
||||||
font-size: 200%;
|
|
||||||
margin: auto;
|
|
||||||
direction: ltr;
|
|
||||||
}
|
|
||||||
|
|
||||||
.humidity {
|
|
||||||
--paper-slider-active-color: var(--paper-blue-400);
|
|
||||||
--paper-slider-secondary-color: var(--paper-blue-400);
|
|
||||||
}
|
|
||||||
|
|
||||||
.single-row {
|
|
||||||
padding: 8px 0;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
customElements.define("more-info-humidifier", MoreInfoHumidifier);
|
|
@@ -86,7 +86,7 @@ class MoreInfoControls extends LocalizeMixin(EventsMixin(PolymerElement)) {
|
|||||||
<div class="main-title" main-title="" on-click="enlarge">
|
<div class="main-title" main-title="" on-click="enlarge">
|
||||||
[[_computeStateName(stateObj)]]
|
[[_computeStateName(stateObj)]]
|
||||||
</div>
|
</div>
|
||||||
<template is="dom-if" if="[[hass.user.is_admin]]">
|
<template is="dom-if" if="[[_computeConfig(hass)]]">
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
aria-label$="[[localize('ui.dialogs.more_info_control.settings')]]"
|
aria-label$="[[localize('ui.dialogs.more_info_control.settings')]]"
|
||||||
icon="hass:settings"
|
icon="hass:settings"
|
||||||
@@ -219,6 +219,10 @@ class MoreInfoControls extends LocalizeMixin(EventsMixin(PolymerElement)) {
|
|||||||
return stateObj ? computeStateName(stateObj) : "";
|
return stateObj ? computeStateName(stateObj) : "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_computeConfig(hass) {
|
||||||
|
return hass.user.is_admin && isComponentLoaded(hass, "config");
|
||||||
|
}
|
||||||
|
|
||||||
_computeEdit(hass, stateObj) {
|
_computeEdit(hass, stateObj) {
|
||||||
const domain = this._computeDomain(stateObj);
|
const domain = this._computeDomain(stateObj);
|
||||||
return (
|
return (
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user