mirror of
https://github.com/home-assistant/frontend.git
synced 2025-09-12 14:39:51 +00:00
Compare commits
120 Commits
restore-co
...
20200623.0
Author | SHA1 | Date | |
---|---|---|---|
![]() |
5268afabdb | ||
![]() |
3ea7506003 | ||
![]() |
ee14d206c8 | ||
![]() |
b65f4b9af6 | ||
![]() |
6d000a3f9a | ||
![]() |
9ba0de67f5 | ||
![]() |
d22eaa1318 | ||
![]() |
3f4bfab7fe | ||
![]() |
9292f217c5 | ||
![]() |
3b779bf423 | ||
![]() |
ea410d3af1 | ||
![]() |
4e71c2c500 | ||
![]() |
454ddf366a | ||
![]() |
d0ba5696d1 | ||
![]() |
c53fd0d1e1 | ||
![]() |
7bbecfde2b | ||
![]() |
a06f378582 | ||
![]() |
b3b42b741d | ||
![]() |
020f115d7c | ||
![]() |
2cc9d70915 | ||
![]() |
b242c6651a | ||
![]() |
14a51799a6 | ||
![]() |
79a6dacd2f | ||
![]() |
6891f1df1c | ||
![]() |
497494620d | ||
![]() |
7a13242077 | ||
![]() |
b9d6973a79 | ||
![]() |
ed0e8c5e8d | ||
![]() |
d8f530f8ac | ||
![]() |
a46874b7ff | ||
![]() |
cf68f25a03 | ||
![]() |
a496563b5c | ||
![]() |
a763ad5bf1 | ||
![]() |
f0b0200932 | ||
![]() |
342f22e6a1 | ||
![]() |
372ecc6557 | ||
![]() |
16c604937e | ||
![]() |
5cbffb23fd | ||
![]() |
6de38d3b85 | ||
![]() |
b34ce577d9 | ||
![]() |
4b9fcd7de7 | ||
![]() |
cc71ccaafa | ||
![]() |
9ff2eece3a | ||
![]() |
2bc97cc9c8 | ||
![]() |
a87570cf5a | ||
![]() |
9ffd25e3a0 | ||
![]() |
61fdab294a | ||
![]() |
d4e137bb58 | ||
![]() |
c251e4f241 | ||
![]() |
a55d0f347b | ||
![]() |
f15cc0b424 | ||
![]() |
4e17875011 | ||
![]() |
256b64b6b3 | ||
![]() |
8c0c0592e2 | ||
![]() |
f53f81dbc4 | ||
![]() |
d6c85719c9 | ||
![]() |
c51c80bf47 | ||
![]() |
544832756d | ||
![]() |
ca8586789a | ||
![]() |
1afc2b3518 | ||
![]() |
17352ea5bd | ||
![]() |
6f5e3c2711 | ||
![]() |
bee21cd3fe | ||
![]() |
c4340e05d2 | ||
![]() |
6d6eef4e97 | ||
![]() |
71137032df | ||
![]() |
ffff4dc03d | ||
![]() |
4033131f2e | ||
![]() |
65b16c763e | ||
![]() |
33af3de4a3 | ||
![]() |
a822c1eb2f | ||
![]() |
4eb46bc275 | ||
![]() |
ccc9b73f9b | ||
![]() |
e9ffdeff19 | ||
![]() |
a0ab4dffc9 | ||
![]() |
f5f8ad0e02 | ||
![]() |
82957ff6ef | ||
![]() |
0864aeb9c6 | ||
![]() |
cda6310373 | ||
![]() |
26a87e9280 | ||
![]() |
0b16a4880a | ||
![]() |
db68c5852c | ||
![]() |
256aec5308 | ||
![]() |
20dd3ca21c | ||
![]() |
25cc76e022 | ||
![]() |
168cc607aa | ||
![]() |
edc4601f8e | ||
![]() |
8f86a7ad8c | ||
![]() |
4d7ad0dc51 | ||
![]() |
34ac80567e | ||
![]() |
23bdc03f29 | ||
![]() |
3099748a6d | ||
![]() |
e384f76ac1 | ||
![]() |
7050d19be7 | ||
![]() |
ca678330d3 | ||
![]() |
10a5b3f9c3 | ||
![]() |
0fcedc5046 | ||
![]() |
f558f7fb8c | ||
![]() |
06207defe7 | ||
![]() |
986f9d7633 | ||
![]() |
81277fd12e | ||
![]() |
30442b25c0 | ||
![]() |
67ac3b4ba3 | ||
![]() |
f819e2cf8d | ||
![]() |
a376f4525b | ||
![]() |
5c1553286a | ||
![]() |
2c5d3f7492 | ||
![]() |
58f01ba11a | ||
![]() |
3fdf9a2e28 | ||
![]() |
4cbd8e7673 | ||
![]() |
20ca642e51 | ||
![]() |
05ad7ea011 | ||
![]() |
faea8c9f4c | ||
![]() |
0d4c51f26e | ||
![]() |
5d5d6b247f | ||
![]() |
404d0b8d05 | ||
![]() |
fe63c12cd9 | ||
![]() |
44023c3db7 | ||
![]() |
6242997849 | ||
![]() |
ec5d7508c9 |
@@ -1,4 +0,0 @@
|
|||||||
node_modules
|
|
||||||
hass_frontend
|
|
||||||
hass_frontend_es5
|
|
||||||
.git
|
|
6
.github/workflows/ci.yaml
vendored
6
.github/workflows/ci.yaml
vendored
@@ -34,10 +34,8 @@ jobs:
|
|||||||
run: yarn install
|
run: yarn install
|
||||||
env:
|
env:
|
||||||
CI: true
|
CI: true
|
||||||
- name: Build icons
|
- name: Build resources
|
||||||
run: ./node_modules/.bin/gulp gen-icons-json
|
run: ./node_modules/.bin/gulp gen-icons-json build-translations gather-gallery-demos
|
||||||
- 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
|
||||||
|
31
Dockerfile
31
Dockerfile
@@ -1,31 +0,0 @@
|
|||||||
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,15 +22,6 @@ 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.
|
||||||
|
@@ -57,7 +57,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 +73,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 +85,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) =>
|
const publicPath = (latestBuild, root = "") =>
|
||||||
latestBuild ? "/frontend_latest/" : "/frontend_es5/";
|
latestBuild ? `${root}/frontend_latest/` : `${root}/frontend_es5/`;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
BundleConfig {
|
BundleConfig {
|
||||||
@@ -170,15 +170,12 @@ 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: paths.hassio_output_root,
|
outputPath: outputPath(paths.hassio_output_root, latestBuild),
|
||||||
publicPath: paths.hassio_publicPath,
|
publicPath: publicPath(latestBuild, paths.hassio_publicPath),
|
||||||
isProdBuild,
|
isProdBuild,
|
||||||
latestBuild,
|
latestBuild,
|
||||||
dontHash: new Set(["entrypoint"]),
|
dontHash: new Set(["entrypoint"]),
|
||||||
|
@@ -20,6 +20,7 @@ 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,30 +6,32 @@ 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({ threshold: 150 }))
|
.pipe(zopfli(zopfliOptions))
|
||||||
.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({ threshold: 150 }))
|
.pipe(zopfli(zopfliOptions))
|
||||||
.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({ threshold: 150 }))
|
.pipe(zopfli(zopfliOptions))
|
||||||
.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({ threshold: 150 }))
|
.pipe(zopfli(zopfliOptions))
|
||||||
.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({ threshold: 150 }))
|
.pipe(zopfli(zopfliOptions))
|
||||||
.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);
|
||||||
@@ -38,6 +40,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())
|
.pipe(zopfli(zopfliOptions))
|
||||||
.pipe(gulp.dest(paths.hassio_output_root));
|
.pipe(gulp.dest(paths.hassio_output_root));
|
||||||
});
|
});
|
||||||
|
@@ -1,7 +1,10 @@
|
|||||||
// 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");
|
||||||
@@ -12,6 +15,31 @@ 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(
|
||||||
@@ -20,7 +48,11 @@ gulp.task(
|
|||||||
},
|
},
|
||||||
"clean-gallery",
|
"clean-gallery",
|
||||||
"translations-enable-merge-backend",
|
"translations-enable-merge-backend",
|
||||||
gulp.parallel("gen-icons-json", "build-translations"),
|
gulp.parallel(
|
||||||
|
"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"
|
||||||
@@ -35,7 +67,11 @@ gulp.task(
|
|||||||
},
|
},
|
||||||
"clean-gallery",
|
"clean-gallery",
|
||||||
"translations-enable-merge-backend",
|
"translations-enable-merge-backend",
|
||||||
gulp.parallel("gen-icons-json", "build-translations"),
|
gulp.parallel(
|
||||||
|
"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,11 +36,13 @@ function copyMdiIcons(staticDir) {
|
|||||||
function copyPolyfills(staticDir) {
|
function copyPolyfills(staticDir) {
|
||||||
const staticPath = genStaticPath(staticDir);
|
const staticPath = genStaticPath(staticDir);
|
||||||
|
|
||||||
// Web Component polyfills and adapters
|
// For custom panels using ES5 builds that don't use Babel 7+
|
||||||
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,6 +1,9 @@
|
|||||||
const gulp = require("gulp");
|
const gulp = require("gulp");
|
||||||
|
const fs = require("fs");
|
||||||
|
const path = require("path");
|
||||||
|
|
||||||
const env = require("../env");
|
const env = require("../env");
|
||||||
|
const paths = require("../paths");
|
||||||
|
|
||||||
require("./clean.js");
|
require("./clean.js");
|
||||||
require("./gen-icons-json.js");
|
require("./gen-icons-json.js");
|
||||||
@@ -8,6 +11,24 @@ 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(
|
||||||
@@ -16,6 +37,7 @@ 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"
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
@@ -29,6 +51,7 @@ 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,11 +92,7 @@ gulp.task("rollup-watch-app", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
gulp.task("rollup-watch-hassio", () => {
|
gulp.task("rollup-watch-hassio", () => {
|
||||||
watchRollup(
|
watchRollup(rollupConfig.createHassioConfig, ["hassio/src/**"]);
|
||||||
// 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", () => {
|
||||||
@@ -137,12 +133,7 @@ gulp.task(
|
|||||||
);
|
);
|
||||||
|
|
||||||
gulp.task("rollup-prod-hassio", () =>
|
gulp.task("rollup-prod-hassio", () =>
|
||||||
buildRollup(
|
bothBuilds(rollupConfig.createHassioConfig, { isProdBuild: true })
|
||||||
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: false,
|
latestBuild: true,
|
||||||
})
|
})
|
||||||
).watch({}, handler());
|
).watch({}, handler());
|
||||||
});
|
});
|
||||||
@@ -139,9 +139,8 @@ gulp.task(
|
|||||||
() =>
|
() =>
|
||||||
new Promise((resolve) =>
|
new Promise((resolve) =>
|
||||||
webpack(
|
webpack(
|
||||||
createHassioConfig({
|
bothBuilds(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"),
|
||||||
};
|
};
|
||||||
|
@@ -70,7 +70,9 @@ 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;
|
||||||
}
|
}
|
||||||
@@ -80,7 +82,11 @@ const createWebpackConfig = ({
|
|||||||
? path.resolve(context, resource)
|
? path.resolve(context, resource)
|
||||||
: require.resolve(resource);
|
: require.resolve(resource);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("Error in ignore plugin", resource, context);
|
console.error(
|
||||||
|
"Error in Home Assistant ignore plugin",
|
||||||
|
resource,
|
||||||
|
context
|
||||||
|
);
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -37,15 +37,13 @@
|
|||||||
<body>
|
<body>
|
||||||
<%= renderTemplate('_js_base') %>
|
<%= renderTemplate('_js_base') %>
|
||||||
|
|
||||||
<script type="module" crossorigin="use-credentials">
|
<script>
|
||||||
import "<%= latestLauncherJS %>";
|
import("<%= latestLauncherJS %>");
|
||||||
|
window.latestJS = true;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script nomodule>
|
<script>
|
||||||
(function() {
|
if (!window.latestJS) {
|
||||||
// // Safari 10.1 supports type=module but ignores nomodule, so we add this check.
|
|
||||||
if (!isS101) {
|
|
||||||
_ls("/static/polyfills/custom-elements-es5-adapter.js");
|
|
||||||
<% if (useRollup) { %>
|
<% if (useRollup) { %>
|
||||||
_ls("/static/js/s.min.js").onload = function() {
|
_ls("/static/js/s.min.js").onload = function() {
|
||||||
System.import("<%= es5LauncherJS %>");
|
System.import("<%= es5LauncherJS %>");
|
||||||
@@ -54,7 +52,6 @@
|
|||||||
_ls("<%= es5LauncherJS %>");
|
_ls("<%= es5LauncherJS %>");
|
||||||
<% } %>
|
<% } %>
|
||||||
}
|
}
|
||||||
})();
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<hc-layout subtitle="FAQ">
|
<hc-layout subtitle="FAQ">
|
||||||
|
@@ -28,15 +28,13 @@
|
|||||||
|
|
||||||
<hc-connect></hc-connect>
|
<hc-connect></hc-connect>
|
||||||
|
|
||||||
<script type="module" crossorigin="use-credentials">
|
<script>
|
||||||
import "<%= latestLauncherJS %>";
|
import("<%= latestLauncherJS %>");
|
||||||
|
window.latestJS = true;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script nomodule>
|
<script>
|
||||||
(function() {
|
if (!window.latestJS) {
|
||||||
// // Safari 10.1 supports type=module but ignores nomodule, so we add this check.
|
|
||||||
if (!isS101) {
|
|
||||||
_ls("/static/polyfills/custom-elements-es5-adapter.js");
|
|
||||||
<% if (useRollup) { %>
|
<% if (useRollup) { %>
|
||||||
_ls("/static/js/s.min.js").onload = function() {
|
_ls("/static/js/s.min.js").onload = function() {
|
||||||
System.import("<%= es5LauncherJS %>");
|
System.import("<%= es5LauncherJS %>");
|
||||||
@@ -45,7 +43,6 @@
|
|||||||
_ls("<%= es5LauncherJS %>");
|
_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,6 +192,8 @@ 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,11 +1,8 @@
|
|||||||
const { createCastConfig } = require("../build-scripts/webpack.js");
|
const { createCastConfig } = require("../build-scripts/webpack.js");
|
||||||
const { isProdBuild } = require("../build-scripts/env.js");
|
const { isProdBuild, isStatsBuild } = require("../build-scripts/env.js");
|
||||||
|
|
||||||
// File just used for stats builds
|
|
||||||
|
|
||||||
const latestBuild = true;
|
|
||||||
|
|
||||||
module.exports = createCastConfig({
|
module.exports = createCastConfig({
|
||||||
isProdBuild: isProdBuild(),
|
isProdBuild: isProdBuild(),
|
||||||
latestBuild,
|
isStatsBuild: isStatsBuild(),
|
||||||
|
latestBuild: true,
|
||||||
});
|
});
|
||||||
|
@@ -1,5 +1,4 @@
|
|||||||
import "@material/mwc-button";
|
import "@material/mwc-button";
|
||||||
import "@polymer/paper-spinner/paper-spinner-lite";
|
|
||||||
import {
|
import {
|
||||||
css,
|
css,
|
||||||
CSSResult,
|
CSSResult,
|
||||||
@@ -10,6 +9,7 @@ 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` <paper-spinner-lite active></paper-spinner-lite> `
|
? html`<ha-circular-progress active></ha-circular-progress>`
|
||||||
: until(
|
: until(
|
||||||
selectedDemoConfig.then(
|
selectedDemoConfig.then(
|
||||||
(conf) => html`
|
(conf) => html`
|
||||||
|
@@ -86,13 +86,13 @@
|
|||||||
<%= renderTemplate('_js_base') %>
|
<%= renderTemplate('_js_base') %>
|
||||||
<%= renderTemplate('_preload_roboto') %>
|
<%= renderTemplate('_preload_roboto') %>
|
||||||
|
|
||||||
<script type="module" src="<%= latestDemoJS %>"></script>
|
<script>
|
||||||
|
import("<%= latestDemoJS %>");
|
||||||
|
window.latestJS = true;
|
||||||
|
</script>
|
||||||
|
|
||||||
<script nomodule>
|
<script>
|
||||||
(function() {
|
if (!window.latestJS) {
|
||||||
// // Safari 10.1 supports type=module but ignores nomodule, so we add this check.
|
|
||||||
if (!isS101) {
|
|
||||||
_ls("/static/polyfills/custom-elements-es5-adapter.js");
|
|
||||||
<% if (useRollup) { %>
|
<% if (useRollup) { %>
|
||||||
_ls("/static/js/s.min.js").onload = function() {
|
_ls("/static/js/s.min.js").onload = function() {
|
||||||
System.import("<%= es5DemoJS %>");
|
System.import("<%= es5DemoJS %>");
|
||||||
@@ -101,7 +101,6 @@
|
|||||||
_ls("<%= es5DemoJS %>");
|
_ls("<%= es5DemoJS %>");
|
||||||
<% } %>
|
<% } %>
|
||||||
}
|
}
|
||||||
})();
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
@@ -3,6 +3,7 @@ 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 {
|
||||||
@@ -26,9 +27,10 @@ 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,9 +11,7 @@ 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);
|
||||||
|
|
||||||
@@ -163,7 +161,7 @@ class HaGallery extends PolymerElement {
|
|||||||
},
|
},
|
||||||
_demos: {
|
_demos: {
|
||||||
type: Array,
|
type: Array,
|
||||||
value: DEMOS.keys().map(fixPath),
|
value: Object.keys(DEMOS),
|
||||||
},
|
},
|
||||||
_lovelaceDemos: {
|
_lovelaceDemos: {
|
||||||
type: Array,
|
type: Array,
|
||||||
@@ -210,7 +208,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}.ts`);
|
DEMOS[demo]();
|
||||||
const el = document.createElement(demo);
|
const el = document.createElement(demo);
|
||||||
root.appendChild(el);
|
root.appendChild(el);
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,8 @@
|
|||||||
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-panel";
|
import { supervisorTabs } from "../hassio-tabs";
|
||||||
import "./hassio-addon-repository";
|
import "./hassio-addon-repository";
|
||||||
|
|
||||||
const sortRepos = (a: HassioAddonRepository, b: HassioAddonRepository) => {
|
const sortRepos = (a: HassioAddonRepository, b: HassioAddonRepository) => {
|
||||||
|
@@ -1,4 +1,3 @@
|
|||||||
import "@polymer/paper-spinner/paper-spinner-lite";
|
|
||||||
import {
|
import {
|
||||||
css,
|
css,
|
||||||
CSSResult,
|
CSSResult,
|
||||||
@@ -12,6 +11,7 @@ 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` <paper-spinner-lite active></paper-spinner-lite> `;
|
return html`<ha-circular-progress active></ha-circular-progress>`;
|
||||||
}
|
}
|
||||||
return html`
|
return html`
|
||||||
<div class="content">
|
<div class="content">
|
||||||
|
@@ -1,4 +1,3 @@
|
|||||||
import "@polymer/paper-spinner/paper-spinner-lite";
|
|
||||||
import {
|
import {
|
||||||
css,
|
css,
|
||||||
CSSResult,
|
CSSResult,
|
||||||
@@ -14,6 +13,7 @@ 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` <paper-spinner-lite active></paper-spinner-lite> `;
|
return html`<ha-circular-progress active></ha-circular-progress>`;
|
||||||
}
|
}
|
||||||
return html`
|
return html`
|
||||||
<div class="content">
|
<div class="content">
|
||||||
|
@@ -4,7 +4,6 @@ import {
|
|||||||
mdiInformationVariant,
|
mdiInformationVariant,
|
||||||
mdiMathLog,
|
mdiMathLog,
|
||||||
} from "@mdi/js";
|
} from "@mdi/js";
|
||||||
import "@polymer/paper-spinner/paper-spinner-lite";
|
|
||||||
import {
|
import {
|
||||||
css,
|
css,
|
||||||
CSSResult,
|
CSSResult,
|
||||||
@@ -20,6 +19,7 @@ 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` <paper-spinner-lite active></paper-spinner-lite> `;
|
return html`<ha-circular-progress active></ha-circular-progress>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const addonTabs: PageNavigation[] = [
|
const addonTabs: PageNavigation[] = [
|
||||||
|
@@ -1,4 +1,3 @@
|
|||||||
import "@polymer/paper-spinner/paper-spinner-lite";
|
|
||||||
import {
|
import {
|
||||||
css,
|
css,
|
||||||
CSSResult,
|
CSSResult,
|
||||||
@@ -9,6 +8,7 @@ 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` <paper-spinner-lite active></paper-spinner-lite> `;
|
return html`<ha-circular-progress active></ha-circular-progress>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
|
@@ -1,4 +1,3 @@
|
|||||||
import "@polymer/paper-spinner/paper-spinner-lite";
|
|
||||||
import {
|
import {
|
||||||
css,
|
css,
|
||||||
CSSResult,
|
CSSResult,
|
||||||
@@ -9,6 +8,7 @@ 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` <paper-spinner-lite active></paper-spinner-lite> `;
|
return html` <ha-circular-progress active></ha-circular-progress> `;
|
||||||
}
|
}
|
||||||
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-panel";
|
import { supervisorTabs } from "../hassio-tabs";
|
||||||
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 "@polymer/paper-spinner/paper-spinner";
|
import "../../../../src/components/ha-circular-progress";
|
||||||
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`<paper-spinner active></paper-spinner>`
|
? html`<ha-circular-progress active></ha-circular-progress>`
|
||||||
: "Add"}
|
: "Add"}
|
||||||
</mwc-button>
|
</mwc-button>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -1,9 +1,6 @@
|
|||||||
window.loadES5Adapter().then(() => {
|
import "../../src/resources/compatibility";
|
||||||
// eslint-disable-next-line
|
import "../../src/resources/roboto";
|
||||||
import(/* webpackChunkName: "roboto" */ "../../src/resources/roboto");
|
import "./hassio-main";
|
||||||
// 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,7 +14,9 @@ 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";
|
||||||
@@ -75,6 +77,8 @@ 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;
|
||||||
@@ -147,6 +151,7 @@ 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,
|
||||||
@@ -156,6 +161,7 @@ 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;
|
||||||
@@ -169,12 +175,14 @@ class HassioMain extends ProvideHassLitMixin(HassRouterPage) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const [supervisorInfo, hostInfo, hassInfo] = await Promise.all([
|
const [supervisorInfo, hostInfo, hassInfo, hassioInfo] = 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,6 +3,7 @@ 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,
|
||||||
@@ -26,6 +27,8 @@ 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;
|
||||||
@@ -54,6 +57,7 @@ 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,4 +1,3 @@
|
|||||||
import { mdiBackupRestore, mdiCogs, mdiStore, mdiViewDashboard } from "@mdi/js";
|
|
||||||
import {
|
import {
|
||||||
customElement,
|
customElement,
|
||||||
html,
|
html,
|
||||||
@@ -10,34 +9,11 @@ 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;
|
||||||
@@ -48,6 +24,8 @@ 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;
|
||||||
@@ -61,6 +39,7 @@ 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}
|
||||||
|
25
hassio/src/hassio-tabs.ts
Normal file
25
hassio/src/hassio-tabs.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
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-panel";
|
import { supervisorTabs } from "../hassio-tabs";
|
||||||
import { hassioStyle } from "../resources/hassio-style";
|
import { hassioStyle } from "../resources/hassio-style";
|
||||||
|
|
||||||
interface CheckboxItem {
|
interface CheckboxItem {
|
||||||
|
@@ -19,6 +19,7 @@ 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,
|
||||||
@@ -35,6 +36,8 @@ 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;
|
||||||
@@ -54,6 +57,12 @@ 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,4 +1,3 @@
|
|||||||
import "@polymer/paper-menu-button/paper-menu-button";
|
|
||||||
import {
|
import {
|
||||||
css,
|
css,
|
||||||
CSSResult,
|
CSSResult,
|
||||||
@@ -12,11 +11,14 @@ import {
|
|||||||
HassioHassOSInfo,
|
HassioHassOSInfo,
|
||||||
HassioHostInfo,
|
HassioHostInfo,
|
||||||
} from "../../../src/data/hassio/host";
|
} from "../../../src/data/hassio/host";
|
||||||
import { HassioSupervisorInfo } from "../../../src/data/hassio/supervisor";
|
import {
|
||||||
|
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-panel";
|
import { supervisorTabs } from "../hassio-tabs";
|
||||||
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";
|
||||||
@@ -32,9 +34,11 @@ 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() public hassOsInfo!: HassioHassOSInfo;
|
@property({ attribute: false }) public hassOsInfo!: HassioHassOSInfo;
|
||||||
|
|
||||||
public render(): TemplateResult | void {
|
public render(): TemplateResult | void {
|
||||||
return html`
|
return html`
|
||||||
@@ -56,6 +60,7 @@ 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,11 +1,8 @@
|
|||||||
const { createHassioConfig } = require("../build-scripts/webpack.js");
|
const { createHassioConfig } = require("../build-scripts/webpack.js");
|
||||||
const { isProdBuild } = require("../build-scripts/env.js");
|
const { isProdBuild, isStatsBuild } = require("../build-scripts/env.js");
|
||||||
|
|
||||||
// File just used for stats builds
|
|
||||||
|
|
||||||
const latestBuild = false;
|
|
||||||
|
|
||||||
module.exports = createHassioConfig({
|
module.exports = createHassioConfig({
|
||||||
isProdBuild: isProdBuild(),
|
isProdBuild: isProdBuild(),
|
||||||
latestBuild,
|
isStatsBuild: isStatsBuild(),
|
||||||
|
latestBuild: true,
|
||||||
});
|
});
|
||||||
|
14
package.json
14
package.json
@@ -17,9 +17,7 @@
|
|||||||
"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",
|
||||||
@@ -28,6 +26,7 @@
|
|||||||
"@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",
|
||||||
@@ -49,7 +48,6 @@
|
|||||||
"@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",
|
||||||
@@ -67,7 +65,6 @@
|
|||||||
"@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",
|
||||||
@@ -76,6 +73,7 @@
|
|||||||
"@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",
|
||||||
@@ -89,7 +87,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.2.1",
|
"home-assistant-js-websocket": "^5.3.0",
|
||||||
"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",
|
||||||
@@ -108,6 +106,8 @@
|
|||||||
"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": "^1.2.3",
|
"terser-webpack-plugin": "^3.0.6",
|
||||||
"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",
|
||||||
|
@@ -1,14 +0,0 @@
|
|||||||
#!/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
|
|
@@ -1,103 +0,0 @@
|
|||||||
#!/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="20200603.1",
|
version="20200623.0",
|
||||||
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,19 +6,18 @@ import {
|
|||||||
property,
|
property,
|
||||||
PropertyValues,
|
PropertyValues,
|
||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
import { AuthProvider, fetchAuthProviders } from "../data/auth";
|
import {
|
||||||
|
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;
|
||||||
|
|
||||||
@@ -33,14 +32,7 @@ class HaAuthorize extends litLocalizeLiteMixin(LitElement) {
|
|||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
this.translationFragment = "page-authorize";
|
this.translationFragment = "page-authorize";
|
||||||
const query: QueryParams = {};
|
const query = extractSearchParamsObject() as AuthUrlSearchParams;
|
||||||
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;
|
||||||
}
|
}
|
||||||
@@ -145,7 +137,7 @@ class HaAuthorize extends litLocalizeLiteMixin(LitElement) {
|
|||||||
response.status === 400 &&
|
response.status === 400 &&
|
||||||
authProviders.code === "onboarding_required"
|
authProviders.code === "onboarding_required"
|
||||||
) {
|
) {
|
||||||
location.href = "/?";
|
location.href = `/onboarding.html${location.search}`;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -37,6 +37,7 @@ export const DOMAINS_WITH_MORE_INFO = [
|
|||||||
"fan",
|
"fan",
|
||||||
"group",
|
"group",
|
||||||
"history_graph",
|
"history_graph",
|
||||||
|
"humidifier",
|
||||||
"input_datetime",
|
"input_datetime",
|
||||||
"light",
|
"light",
|
||||||
"lock",
|
"lock",
|
||||||
@@ -79,6 +80,7 @@ export const DOMAINS_TOGGLE = new Set([
|
|||||||
"switch",
|
"switch",
|
||||||
"group",
|
"group",
|
||||||
"automation",
|
"automation",
|
||||||
|
"humidifier",
|
||||||
]);
|
]);
|
||||||
|
|
||||||
/** Temperature units. */
|
/** Temperature units. */
|
||||||
|
@@ -55,6 +55,12 @@ 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,6 +22,7 @@ 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 and quotes with underscore
|
.replace(/\s|'|\./g, "_") // replace spaces, points 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,6 +8,7 @@ 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"],
|
||||||
@@ -57,9 +58,12 @@ export const iconColorCSS = css`
|
|||||||
0% {
|
0% {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
100% {
|
50% {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ha-icon[data-domain="plant"][data-state="problem"],
|
ha-icon[data-domain="plant"][data-state="problem"],
|
||||||
|
8
src/common/url/search-params.ts
Normal file
8
src/common/url/search-params.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
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 "@polymer/paper-spinner/paper-spinner";
|
import "../ha-circular-progress";
|
||||||
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,6 +8,9 @@ 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;
|
||||||
@@ -48,7 +51,9 @@ 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"><paper-spinner active=""></paper-spinner></div>
|
<div class="progress">
|
||||||
|
<ha-circular-progress active size="small"></ha-circular-progress>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
@@ -619,6 +619,11 @@ 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;
|
||||||
}
|
}
|
||||||
|
228
src/components/date-range-picker.ts
Normal file
228
src/components/date-range-picker.ts
Normal file
@@ -0,0 +1,228 @@
|
|||||||
|
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);
|
||||||
|
|
||||||
class HaEntityToggle extends LitElement {
|
export 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: block;
|
display: inline-block;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
@@ -176,7 +176,9 @@ 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,6 +12,8 @@ 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 {
|
||||||
@@ -19,12 +21,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, 2px);
|
border-radius: var(--ha-card-border-radius, 4px);
|
||||||
box-shadow: var(
|
box-shadow: var(
|
||||||
--ha-card-box-shadow,
|
--ha-card-box-shadow,
|
||||||
0 2px 2px 0 rgba(0, 0, 0, 0.14),
|
0px 2px 1px -1px rgba(0, 0, 0, 0.2),
|
||||||
0 1px 5px 0 rgba(0, 0, 0, 0.12),
|
0px 1px 1px 0px rgba(0, 0, 0, 0.14),
|
||||||
0 3px 1px -2px rgba(0, 0, 0, 0.2)
|
0px 1px 3px 0px rgba(0, 0, 0, 0.12)
|
||||||
);
|
);
|
||||||
color: var(--primary-text-color);
|
color: var(--primary-text-color);
|
||||||
display: block;
|
display: block;
|
||||||
@@ -32,6 +34,16 @@ 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);
|
||||||
|
85
src/components/ha-circular-progress.ts
Normal file
85
src/components/ha-circular-progress.ts
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
import {
|
||||||
|
LitElement,
|
||||||
|
TemplateResult,
|
||||||
|
property,
|
||||||
|
svg,
|
||||||
|
html,
|
||||||
|
customElement,
|
||||||
|
unsafeCSS,
|
||||||
|
SVGTemplateResult,
|
||||||
|
} 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ha-circular-progress": HaCircularProgress;
|
||||||
|
}
|
||||||
|
}
|
195
src/components/ha-date-range-picker.ts
Normal file
195
src/components/ha-date-range-picker.ts
Normal file
@@ -0,0 +1,195 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
@@ -1,24 +0,0 @@
|
|||||||
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="close_button"
|
class="header_button"
|
||||||
>
|
>
|
||||||
<ha-svg-icon path=${mdiClose}></ha-svg-icon>
|
<ha-svg-icon path=${mdiClose}></ha-svg-icon>
|
||||||
</mwc-icon-button>
|
</mwc-icon-button>
|
||||||
@@ -25,6 +25,9 @@ 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);
|
||||||
}
|
}
|
||||||
@@ -35,10 +38,15 @@ export class HaDialog extends MwcDialog {
|
|||||||
display: block;
|
display: block;
|
||||||
height: 20px;
|
height: 20px;
|
||||||
}
|
}
|
||||||
.close_button {
|
.mdc-dialog__content {
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
];
|
];
|
||||||
|
33
src/components/ha-formfield.ts
Normal file
33
src/components/ha-formfield.ts
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
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,17 +1,37 @@
|
|||||||
import { HaIconButton } from "./ha-icon-button";
|
import {
|
||||||
|
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"
|
||||||
? "hass:arrow-right"
|
? mdiArrowRight
|
||||||
: "hass:arrow-left";
|
: mdiArrowLeft;
|
||||||
}, 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 {
|
||||||
@@ -19,5 +39,3 @@ declare global {
|
|||||||
"ha-icon-button-arrow-next": HaIconButtonArrowNext;
|
"ha-icon-button-arrow-next": HaIconButtonArrowNext;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define("ha-icon-button-arrow-next", HaIconButtonArrowNext);
|
|
||||||
|
@@ -1,8 +1,15 @@
|
|||||||
import { LitElement, property, TemplateResult, html } from "lit-element";
|
import {
|
||||||
|
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;
|
||||||
|
|
||||||
@@ -32,5 +39,3 @@ declare global {
|
|||||||
"ha-icon-button-arrow-prev": HaIconButtonArrowPrev;
|
"ha-icon-button-arrow-prev": HaIconButtonArrowPrev;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define("ha-icon-button-arrow-prev", HaIconButtonArrowPrev);
|
|
||||||
|
@@ -1,17 +1,37 @@
|
|||||||
import { HaIconButton } from "./ha-icon-button";
|
import {
|
||||||
|
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"
|
||||||
? "hass:chevron-right"
|
? mdiChevronRight
|
||||||
: "hass:chevron-left";
|
: mdiChevronLeft;
|
||||||
}, 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 {
|
||||||
@@ -19,5 +39,3 @@ declare global {
|
|||||||
"ha-icon-button-next": HaIconButtonNext;
|
"ha-icon-button-next": HaIconButtonNext;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define("ha-icon-button-next", HaIconButtonNext);
|
|
||||||
|
@@ -1,17 +1,37 @@
|
|||||||
import { HaIconButton } from "./ha-icon-button";
|
import {
|
||||||
|
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"
|
||||||
? "hass:chevron-left"
|
? mdiChevronLeft
|
||||||
: "hass:chevron-right";
|
: mdiChevronRight;
|
||||||
}, 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 {
|
||||||
@@ -19,5 +39,3 @@ declare global {
|
|||||||
"ha-icon-button-prev": HaIconButtonPrev;
|
"ha-icon-button-prev": HaIconButtonPrev;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define("ha-icon-button-prev", HaIconButtonPrev);
|
|
||||||
|
@@ -24,28 +24,21 @@ export class HaIconButton extends LitElement {
|
|||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
return html`
|
return html`
|
||||||
<mwc-icon-button
|
<mwc-icon-button .label=${this.label} .disabled=${this.disabled}>
|
||||||
.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);
|
||||||
|
@@ -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, #f6f8fa);
|
background-color: var(--markdown-code-background-color, none);
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
}
|
}
|
||||||
ha-markdown-element code {
|
ha-markdown-element code {
|
||||||
|
@@ -1,3 +1,5 @@
|
|||||||
|
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,
|
||||||
@@ -12,8 +14,7 @@ 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-icon-button";
|
import "./ha-svg-icon";
|
||||||
import { mdiMenu } from "@mdi/js";
|
|
||||||
|
|
||||||
@customElement("ha-menu-button")
|
@customElement("ha-menu-button")
|
||||||
class HaMenuButton extends LitElement {
|
class HaMenuButton extends LitElement {
|
||||||
|
@@ -1,6 +1,11 @@
|
|||||||
import { mdiBell, mdiCellphoneSettingsVariant } from "@mdi/js";
|
import "@material/mwc-icon-button";
|
||||||
import "@polymer/app-layout/app-toolbar/app-toolbar";
|
import {
|
||||||
import "./ha-icon-button";
|
mdiBell,
|
||||||
|
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";
|
||||||
@@ -10,6 +15,7 @@ import {
|
|||||||
CSSResult,
|
CSSResult,
|
||||||
eventOptions,
|
eventOptions,
|
||||||
html,
|
html,
|
||||||
|
customElement,
|
||||||
LitElement,
|
LitElement,
|
||||||
property,
|
property,
|
||||||
PropertyValues,
|
PropertyValues,
|
||||||
@@ -29,9 +35,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"];
|
||||||
@@ -103,9 +109,7 @@ 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;
|
||||||
|
|
||||||
@@ -153,13 +157,16 @@ class HaSidebar extends LitElement {
|
|||||||
<div class="menu">
|
<div class="menu">
|
||||||
${!this.narrow
|
${!this.narrow
|
||||||
? html`
|
? html`
|
||||||
<ha-icon-button
|
<mwc-icon-button
|
||||||
aria-label=${hass.localize("ui.sidebar.sidebar_toggle")}
|
.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>
|
||||||
@@ -174,14 +181,16 @@ class HaSidebar extends LitElement {
|
|||||||
>
|
>
|
||||||
${this._renderPanel(
|
${this._renderPanel(
|
||||||
defaultPanel.url_path,
|
defaultPanel.url_path,
|
||||||
defaultPanel.icon || "hass:view-dashboard",
|
defaultPanel.title || hass.localize("panel.states"),
|
||||||
defaultPanel.title || hass.localize("panel.states")
|
defaultPanel.icon,
|
||||||
|
!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,
|
||||||
hass.localize(`panel.${panel.title}`) || panel.title
|
undefined
|
||||||
)
|
)
|
||||||
)}
|
)}
|
||||||
<div class="spacer" disabled></div>
|
<div class="spacer" disabled></div>
|
||||||
@@ -189,8 +198,9 @@ 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,
|
||||||
hass.localize(`panel.${panel.title}`) || panel.title
|
undefined
|
||||||
)
|
)
|
||||||
)}
|
)}
|
||||||
${this._externalConfig && this._externalConfig.hasSettingsScreen
|
${this._externalConfig && this._externalConfig.hasSettingsScreen
|
||||||
@@ -443,7 +453,12 @@ class HaSidebar extends LitElement {
|
|||||||
fireEvent(this, "hass-toggle-menu");
|
fireEvent(this, "hass-toggle-menu");
|
||||||
}
|
}
|
||||||
|
|
||||||
private _renderPanel(urlPath, icon, title) {
|
private _renderPanel(
|
||||||
|
urlPath: string,
|
||||||
|
title: string | null,
|
||||||
|
icon?: string | null,
|
||||||
|
iconPath?: string | null
|
||||||
|
) {
|
||||||
return html`
|
return html`
|
||||||
<a
|
<a
|
||||||
aria-role="option"
|
aria-role="option"
|
||||||
@@ -454,7 +469,12 @@ class HaSidebar extends LitElement {
|
|||||||
@mouseleave=${this._itemMouseLeave}
|
@mouseleave=${this._itemMouseLeave}
|
||||||
>
|
>
|
||||||
<paper-icon-item>
|
<paper-icon-item>
|
||||||
<ha-icon slot="item-icon" .icon="${icon}"></ha-icon>
|
${iconPath
|
||||||
|
? 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>
|
||||||
@@ -496,13 +516,13 @@ class HaSidebar extends LitElement {
|
|||||||
width: 256px;
|
width: 256px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu ha-icon-button {
|
.menu mwc-icon-button {
|
||||||
color: var(--sidebar-icon-color);
|
color: var(--sidebar-icon-color);
|
||||||
}
|
}
|
||||||
:host([expanded]) .menu ha-icon-button {
|
:host([expanded]) .menu mwc-icon-button {
|
||||||
margin-right: 23px;
|
margin-right: 23px;
|
||||||
}
|
}
|
||||||
:host([expanded][_rtl]) .menu ha-icon-button {
|
:host([expanded][_rtl]) .menu mwc-icon-button {
|
||||||
margin-right: 0px;
|
margin-right: 0px;
|
||||||
margin-left: 23px;
|
margin-left: 23px;
|
||||||
}
|
}
|
||||||
@@ -714,7 +734,7 @@ class HaSidebar extends LitElement {
|
|||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
:host([_rtl]) .menu ha-icon-button {
|
:host([_rtl]) .menu mwc-icon-button {
|
||||||
-webkit-transform: scaleX(-1);
|
-webkit-transform: scaleX(-1);
|
||||||
transform: scaleX(-1);
|
transform: scaleX(-1);
|
||||||
}
|
}
|
||||||
@@ -727,5 +747,3 @@ declare global {
|
|||||||
"ha-sidebar": HaSidebar;
|
"ha-sidebar": HaSidebar;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define("ha-sidebar", HaSidebar);
|
|
||||||
|
@@ -1,15 +1,7 @@
|
|||||||
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 {
|
import { css, CSSResult, customElement, property } from "lit-element";
|
||||||
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";
|
||||||
|
|
||||||
@@ -22,18 +14,12 @@ 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");
|
||||||
@@ -41,40 +27,10 @@ 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);
|
||||||
@@ -91,18 +47,9 @@ 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 {
|
||||||
|
@@ -1,46 +0,0 @@
|
|||||||
/*
|
|
||||||
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,6 +262,28 @@ 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 "@polymer/paper-spinner/paper-spinner";
|
import "./ha-circular-progress";
|
||||||
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,5 +1,11 @@
|
|||||||
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"];
|
export const DISCOVERY_SOURCES = ["unignore", "homekit", "ssdp", "zeroconf", "discovery"];
|
||||||
|
|
||||||
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,6 +12,11 @@ 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>
|
||||||
@@ -25,3 +30,6 @@ 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,6 +4,20 @@ 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
|
||||||
| {
|
| {
|
||||||
@@ -38,6 +52,12 @@ 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,13 +5,15 @@ 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", "water_heater"];
|
const DOMAINS_USE_LAST_UPDATED = ["climate", "humidifier", "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 {
|
||||||
@@ -224,6 +226,8 @@ 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) {
|
||||||
|
19
src/data/humidifier.ts
Normal file
19
src/data/humidifier.ts
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
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,7 +1,67 @@
|
|||||||
|
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 }
|
params: { client_id: string; redirect_uri: string }
|
||||||
) =>
|
) =>
|
||||||
hass.callApi<OnboardingIntegrationStepResponse>(
|
hass.callApi<OnboardingIntegrationStepResponse>(
|
||||||
"POST",
|
"POST",
|
||||||
|
@@ -10,7 +10,9 @@ 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,
|
||||||
@@ -74,7 +76,7 @@ class DialogConfigEntrySystemOptions extends LitElement {
|
|||||||
${this._loading
|
${this._loading
|
||||||
? html`
|
? html`
|
||||||
<div class="init-spinner">
|
<div class="init-spinner">
|
||||||
<paper-spinner-lite active></paper-spinner-lite>
|
<ha-circular-progress active></ha-circular-progress>
|
||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
: html`
|
: html`
|
||||||
@@ -82,13 +84,8 @@ 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-switch
|
<ha-formfield
|
||||||
.checked=${!this._disableNewEntities}
|
.label=${html`<p>
|
||||||
@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"
|
||||||
)}
|
)}
|
||||||
@@ -101,9 +98,15 @@ 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
|
||||||
|
.checked=${!this._disableNewEntities}
|
||||||
|
@change=${this._disableNewEntitiesChanged}
|
||||||
|
.disabled=${this._submitting}
|
||||||
|
>
|
||||||
</ha-switch>
|
</ha-switch>
|
||||||
|
</ha-formfield>
|
||||||
</div>
|
</div>
|
||||||
`}
|
`}
|
||||||
</paper-dialog-scrollable>
|
</paper-dialog-scrollable>
|
||||||
@@ -172,9 +175,6 @@ 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 "@polymer/paper-spinner/paper-spinner";
|
import "../../components/ha-circular-progress";
|
||||||
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,8 +14,7 @@ import {
|
|||||||
PropertyValues,
|
PropertyValues,
|
||||||
TemplateResult,
|
TemplateResult,
|
||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
import "../../components/dialog/ha-paper-dialog";
|
import "../../components/ha-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 {
|
||||||
@@ -27,7 +26,6 @@ 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";
|
||||||
@@ -90,7 +88,6 @@ 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 {
|
||||||
@@ -98,7 +95,6 @@ class DataEntryFlowDialog extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
await this.updateComplete;
|
await this.updateComplete;
|
||||||
this._scheduleCenterDialog();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,9 +111,6 @@ 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 {
|
||||||
@@ -126,13 +119,16 @@ class DataEntryFlowDialog extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<ha-paper-dialog
|
<ha-dialog
|
||||||
with-backdrop
|
open
|
||||||
opened
|
@closing=${this._close}
|
||||||
modal
|
scrimClickAction
|
||||||
@opened-changed=${this._openedChanged}
|
escapeKeyAction
|
||||||
|
hideActions
|
||||||
>
|
>
|
||||||
${this._loading || (this._step === null && this._handlers === undefined)
|
<div>
|
||||||
|
${this._loading ||
|
||||||
|
(this._step === null && this._handlers === undefined)
|
||||||
? html`
|
? html`
|
||||||
<step-flow-loading
|
<step-flow-loading
|
||||||
.label=${this.hass.localize(
|
.label=${this.hass.localize(
|
||||||
@@ -150,7 +146,7 @@ class DataEntryFlowDialog extends LitElement {
|
|||||||
"ui.panel.config.integrations.config_flow.dismiss"
|
"ui.panel.config.integrations.config_flow.dismiss"
|
||||||
)}
|
)}
|
||||||
icon="hass:close"
|
icon="hass:close"
|
||||||
dialog-dismiss
|
dialogAction="close"
|
||||||
></ha-icon-button>
|
></ha-icon-button>
|
||||||
${this._step === null
|
${this._step === null
|
||||||
? // Show handler picker
|
? // Show handler picker
|
||||||
@@ -199,7 +195,8 @@ class DataEntryFlowDialog extends LitElement {
|
|||||||
></step-flow-create-entry>
|
></step-flow-create-entry>
|
||||||
`}
|
`}
|
||||||
`}
|
`}
|
||||||
</ha-paper-dialog>
|
</div>
|
||||||
|
</ha-dialog>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -225,18 +222,6 @@ 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) {
|
||||||
@@ -310,9 +295,7 @@ class DataEntryFlowDialog extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _openedChanged(ev: PolymerChangedEvent<boolean>): void {
|
private _close(): void {
|
||||||
// Closed dialog by clicking on the overlay
|
|
||||||
if (!ev.detail.value) {
|
|
||||||
if (this._step) {
|
if (this._step) {
|
||||||
this._flowDone();
|
this._flowDone();
|
||||||
} else if (this._step === null) {
|
} else if (this._step === null) {
|
||||||
@@ -321,24 +304,19 @@ class DataEntryFlowDialog extends LitElement {
|
|||||||
this._params = undefined;
|
this._params = undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
static get styles(): CSSResultArray {
|
static get styles(): CSSResultArray {
|
||||||
return [
|
return [
|
||||||
haStyleDialog,
|
haStyleDialog,
|
||||||
css`
|
css`
|
||||||
ha-paper-dialog {
|
ha-dialog {
|
||||||
max-width: 600px;
|
--dialog-content-padding: 0;
|
||||||
}
|
|
||||||
ha-paper-dialog > * {
|
|
||||||
margin: 0;
|
|
||||||
display: block;
|
|
||||||
padding: 0;
|
|
||||||
}
|
}
|
||||||
ha-icon-button {
|
ha-icon-button {
|
||||||
display: inline-block;
|
padding: 16px;
|
||||||
padding: 8px;
|
position: absolute;
|
||||||
float: right;
|
top: 0;
|
||||||
|
right: 0;
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
];
|
];
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import "@material/mwc-button";
|
import "@material/mwc-button";
|
||||||
import "@polymer/paper-spinner/paper-spinner";
|
import "../../components/ha-circular-progress";
|
||||||
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">
|
||||||
<paper-spinner active></paper-spinner>
|
<ha-circular-progress active></ha-circular-progress>
|
||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
: html`
|
: html`
|
||||||
|
@@ -1,4 +1,3 @@
|
|||||||
import "@polymer/paper-spinner/paper-spinner-lite";
|
|
||||||
import {
|
import {
|
||||||
css,
|
css,
|
||||||
CSSResult,
|
CSSResult,
|
||||||
@@ -8,6 +7,7 @@ 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> ` : ""}
|
||||||
<paper-spinner-lite active></paper-spinner-lite>
|
<ha-circular-progress active></ha-circular-progress>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
@@ -28,7 +28,7 @@ class StepFlowLoading extends LitElement {
|
|||||||
padding: 50px 100px;
|
padding: 50px 100px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
paper-spinner-lite {
|
ha-circular-progress {
|
||||||
margin-top: 16px;
|
margin-top: 16px;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
@@ -1,6 +1,5 @@
|
|||||||
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,
|
||||||
@@ -42,6 +41,8 @@ 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) => {
|
||||||
@@ -82,7 +83,10 @@ class StepFlowPickHandler extends LitElement {
|
|||||||
@value-changed=${this._filterChanged}
|
@value-changed=${this._filterChanged}
|
||||||
></search-input>
|
></search-input>
|
||||||
<div
|
<div
|
||||||
style=${styleMap({ width: `${this._width}px` })}
|
style=${styleMap({
|
||||||
|
width: `${this._width}px`,
|
||||||
|
height: `${this._height}px`,
|
||||||
|
})}
|
||||||
class=${classMap({ advanced: Boolean(this.showAdvanced) })}
|
class=${classMap({ advanced: Boolean(this.showAdvanced) })}
|
||||||
>
|
>
|
||||||
${handlers.map(
|
${handlers.map(
|
||||||
@@ -139,13 +143,20 @@ class StepFlowPickHandler extends LitElement {
|
|||||||
|
|
||||||
protected updated(changedProps) {
|
protected updated(changedProps) {
|
||||||
super.updated(changedProps);
|
super.updated(changedProps);
|
||||||
// Store the width so that when we search, box doesn't jump
|
// Store the width and height so that when we search, box doesn't jump
|
||||||
|
const div = this.shadowRoot!.querySelector("div")!;
|
||||||
if (!this._width) {
|
if (!this._width) {
|
||||||
const width = this.shadowRoot!.querySelector("div")!.clientWidth;
|
const width = 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) {
|
||||||
@@ -166,8 +177,8 @@ class StepFlowPickHandler extends LitElement {
|
|||||||
configFlowContentStyles,
|
configFlowContentStyles,
|
||||||
css`
|
css`
|
||||||
img {
|
img {
|
||||||
max-width: 40px;
|
width: 40px;
|
||||||
max-height: 40px;
|
height: 40px;
|
||||||
}
|
}
|
||||||
search-input {
|
search-input {
|
||||||
display: block;
|
display: block;
|
||||||
@@ -180,12 +191,12 @@ class StepFlowPickHandler extends LitElement {
|
|||||||
overflow: auto;
|
overflow: auto;
|
||||||
max-height: 600px;
|
max-height: 600px;
|
||||||
}
|
}
|
||||||
@media all and (max-height: 1px) {
|
@media all and (max-height: 900px) {
|
||||||
div {
|
div {
|
||||||
max-height: calc(100vh - 205px);
|
max-height: calc(100vh - 134px);
|
||||||
}
|
}
|
||||||
div.advanced {
|
div.advanced {
|
||||||
max-height: calc(100vh - 300px);
|
max-height: calc(100vh - 250px);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
paper-icon-item {
|
paper-icon-item {
|
||||||
|
@@ -2,8 +2,21 @@ import { css } from "lit-element";
|
|||||||
|
|
||||||
export const configFlowContentStyles = css`
|
export const configFlowContentStyles = css`
|
||||||
h2 {
|
h2 {
|
||||||
margin-top: 24px;
|
margin: 24px 0 0;
|
||||||
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.dialogs.devices.name")}
|
.label=${this.hass.localize("ui.panel.config.devices.name")}
|
||||||
.placeholder=${device.name || ""}
|
.placeholder=${device.name || ""}
|
||||||
.disabled=${this._submitting}
|
.disabled=${this._submitting}
|
||||||
></paper-input>
|
></paper-input>
|
||||||
|
@@ -1,5 +1,4 @@
|
|||||||
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,
|
||||||
@@ -11,7 +10,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/dialog/ha-paper-dialog";
|
import "../../components/ha-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";
|
||||||
@@ -41,21 +40,17 @@ 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-paper-dialog
|
<ha-dialog
|
||||||
with-backdrop
|
open
|
||||||
opened
|
scrimClickAction
|
||||||
modal
|
escapeKeyAction
|
||||||
@opened-changed="${this._openedChanged}"
|
@close=${this._close}
|
||||||
>
|
.heading=${this._params.title
|
||||||
<h2>
|
|
||||||
${this._params.title
|
|
||||||
? this._params.title
|
? this._params.title
|
||||||
: this._params.confirmation &&
|
: this._params.confirmation &&
|
||||||
this.hass.localize(
|
this.hass.localize("ui.dialogs.generic.default_confirmation_title")}
|
||||||
"ui.dialogs.generic.default_confirmation_title"
|
>
|
||||||
)}
|
<div>
|
||||||
</h2>
|
|
||||||
<paper-dialog-scrollable>
|
|
||||||
${this._params.text
|
${this._params.text
|
||||||
? html`
|
? html`
|
||||||
<p
|
<p
|
||||||
@@ -83,23 +78,21 @@ class DialogBox extends LitElement {
|
|||||||
></paper-input>
|
></paper-input>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
</paper-dialog-scrollable>
|
</div>
|
||||||
<div class="paper-dialog-buttons">
|
|
||||||
${confirmPrompt &&
|
${confirmPrompt &&
|
||||||
html`
|
html`
|
||||||
<mwc-button @click="${this._dismiss}">
|
<mwc-button @click=${this._dismiss} slot="secondaryAction">
|
||||||
${this._params.dismissText
|
${this._params.dismissText
|
||||||
? this._params.dismissText
|
? this._params.dismissText
|
||||||
: this.hass.localize("ui.dialogs.generic.cancel")}
|
: this.hass.localize("ui.dialogs.generic.cancel")}
|
||||||
</mwc-button>
|
</mwc-button>
|
||||||
`}
|
`}
|
||||||
<mwc-button @click="${this._confirm}">
|
<mwc-button @click=${this._confirm} slot="primaryAction">
|
||||||
${this._params.confirmText
|
${this._params.confirmText
|
||||||
? this._params.confirmText
|
? this._params.confirmText
|
||||||
: this.hass.localize("ui.dialogs.generic.ok")}
|
: this.hass.localize("ui.dialogs.generic.ok")}
|
||||||
</mwc-button>
|
</mwc-button>
|
||||||
</div>
|
</ha-dialog>
|
||||||
</ha-paper-dialog>
|
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,11 +120,9 @@ class DialogBox extends LitElement {
|
|||||||
this._dismiss();
|
this._dismiss();
|
||||||
}
|
}
|
||||||
|
|
||||||
private _openedChanged(ev: PolymerChangedEvent<boolean>): void {
|
private _close(): void {
|
||||||
if (!(ev.detail as any).value) {
|
|
||||||
this._params = undefined;
|
this._params = undefined;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
static get styles(): CSSResult[] {
|
static get styles(): CSSResult[] {
|
||||||
return [
|
return [
|
||||||
@@ -141,15 +132,6 @@ 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);
|
||||||
}
|
}
|
||||||
@@ -165,6 +147,10 @@ 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 "@polymer/paper-spinner/paper-spinner";
|
import "../../../components/ha-circular-progress";
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
paper-spinner {
|
ha-circular-progress {
|
||||||
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"
|
||||||
>
|
>
|
||||||
<paper-spinner
|
<ha-circular-progress
|
||||||
active="[[isConfiguring]]"
|
active="[[isConfiguring]]"
|
||||||
hidden="[[!isConfiguring]]"
|
hidden="[[!isConfiguring]]"
|
||||||
alt="Configuring"
|
alt="Configuring"
|
||||||
></paper-spinner>
|
></ha-circular-progress>
|
||||||
[[stateObj.attributes.submit_caption]]
|
[[stateObj.attributes.submit_caption]]
|
||||||
</mwc-button>
|
</mwc-button>
|
||||||
</p>
|
</p>
|
||||||
|
@@ -14,6 +14,7 @@ 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";
|
||||||
|
218
src/dialogs/more-info/controls/more-info-humidifier.ts
Normal file
218
src/dialogs/more-info/controls/more-info-humidifier.ts
Normal file
@@ -0,0 +1,218 @@
|
|||||||
|
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="[[_computeConfig(hass)]]">
|
<template is="dom-if" if="[[hass.user.is_admin]]">
|
||||||
<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,10 +219,6 @@ 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 (
|
||||||
|
@@ -1,113 +0,0 @@
|
|||||||
import "@polymer/paper-dialog-scrollable/paper-dialog-scrollable";
|
|
||||||
import {
|
|
||||||
css,
|
|
||||||
CSSResult,
|
|
||||||
customElement,
|
|
||||||
html,
|
|
||||||
LitElement,
|
|
||||||
property,
|
|
||||||
TemplateResult,
|
|
||||||
} from "lit-element";
|
|
||||||
import "../../components/dialog/ha-paper-dialog";
|
|
||||||
import type { HaPaperDialog } from "../../components/dialog/ha-paper-dialog";
|
|
||||||
import { fetchZHADevice, ZHADevice } from "../../data/zha";
|
|
||||||
import "../../panels/config/zha/zha-device-card";
|
|
||||||
import type { PolymerChangedEvent } from "../../polymer-types";
|
|
||||||
import { haStyleDialog } from "../../resources/styles";
|
|
||||||
import type { HomeAssistant } from "../../types";
|
|
||||||
import type { ZHADeviceInfoDialogParams } from "./show-dialog-zha-device-info";
|
|
||||||
|
|
||||||
@customElement("dialog-zha-device-info")
|
|
||||||
class DialogZHADeviceInfo extends LitElement {
|
|
||||||
@property() public hass!: HomeAssistant;
|
|
||||||
|
|
||||||
@property() private _params?: ZHADeviceInfoDialogParams;
|
|
||||||
|
|
||||||
@property() private _error?: string;
|
|
||||||
|
|
||||||
@property() private _device?: ZHADevice;
|
|
||||||
|
|
||||||
public async showDialog(params: ZHADeviceInfoDialogParams): Promise<void> {
|
|
||||||
this._params = params;
|
|
||||||
this._device = await fetchZHADevice(this.hass, params.ieee);
|
|
||||||
await this.updateComplete;
|
|
||||||
this._dialog.open();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
|
||||||
if (!this._params || !this._device) {
|
|
||||||
return html``;
|
|
||||||
}
|
|
||||||
|
|
||||||
return html`
|
|
||||||
<ha-paper-dialog
|
|
||||||
with-backdrop
|
|
||||||
opened
|
|
||||||
@opened-changed=${this._openedChanged}
|
|
||||||
>
|
|
||||||
${this._error
|
|
||||||
? html` <div class="error">${this._error}</div> `
|
|
||||||
: html`
|
|
||||||
<zha-device-card
|
|
||||||
class="card"
|
|
||||||
.hass=${this.hass}
|
|
||||||
.device=${this._device}
|
|
||||||
@zha-device-removed=${this._onDeviceRemoved}
|
|
||||||
.showEntityDetail=${false}
|
|
||||||
.showActions="${this._device.device_type !== "Coordinator"}"
|
|
||||||
></zha-device-card>
|
|
||||||
`}
|
|
||||||
</ha-paper-dialog>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
private _openedChanged(ev: PolymerChangedEvent<boolean>): void {
|
|
||||||
if (!ev.detail.value) {
|
|
||||||
this._params = undefined;
|
|
||||||
this._error = undefined;
|
|
||||||
this._device = undefined;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private _onDeviceRemoved(): void {
|
|
||||||
this._closeDialog();
|
|
||||||
}
|
|
||||||
|
|
||||||
private get _dialog(): HaPaperDialog {
|
|
||||||
return this.shadowRoot!.querySelector("ha-paper-dialog")!;
|
|
||||||
}
|
|
||||||
|
|
||||||
private _closeDialog() {
|
|
||||||
this._dialog.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
static get styles(): CSSResult[] {
|
|
||||||
return [
|
|
||||||
haStyleDialog,
|
|
||||||
css`
|
|
||||||
ha-paper-dialog > * {
|
|
||||||
margin: 0;
|
|
||||||
display: block;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
.card {
|
|
||||||
box-sizing: border-box;
|
|
||||||
display: flex;
|
|
||||||
flex: 1 0 300px;
|
|
||||||
min-width: 0;
|
|
||||||
max-width: 600px;
|
|
||||||
word-wrap: break-word;
|
|
||||||
}
|
|
||||||
.error {
|
|
||||||
color: var(--google-red-500);
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
declare global {
|
|
||||||
interface HTMLElementTagNameMap {
|
|
||||||
"dialog-zha-device-info": DialogZHADeviceInfo;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,21 +0,0 @@
|
|||||||
import { fireEvent } from "../../common/dom/fire_event";
|
|
||||||
|
|
||||||
export interface ZHADeviceInfoDialogParams {
|
|
||||||
ieee: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const loadZHADeviceInfoDialog = () =>
|
|
||||||
import(
|
|
||||||
/* webpackChunkName: "dialog-zha-device-info" */ "./dialog-zha-device-info"
|
|
||||||
);
|
|
||||||
|
|
||||||
export const showZHADeviceInfoDialog = (
|
|
||||||
element: HTMLElement,
|
|
||||||
zhaDeviceInfoParams: ZHADeviceInfoDialogParams
|
|
||||||
): void => {
|
|
||||||
fireEvent(element, "show-dialog", {
|
|
||||||
dialogTag: "dialog-zha-device-info",
|
|
||||||
dialogImport: loadZHADeviceInfoDialog,
|
|
||||||
dialogParams: zhaDeviceInfoParams,
|
|
||||||
});
|
|
||||||
};
|
|
@@ -2,7 +2,7 @@
|
|||||||
if (navigator.userAgent.indexOf("Android") === -1 &&
|
if (navigator.userAgent.indexOf("Android") === -1 &&
|
||||||
navigator.userAgent.indexOf("CrOS") === -1) {
|
navigator.userAgent.indexOf("CrOS") === -1) {
|
||||||
function _pf(src, type) {
|
function _pf(src, type) {
|
||||||
const el = document.createElement("link");
|
var el = document.createElement("link");
|
||||||
el.rel = "preload";
|
el.rel = "preload";
|
||||||
el.as = "font";
|
el.as = "font";
|
||||||
el.type = "font/woff2";
|
el.type = "font/woff2";
|
||||||
|
@@ -47,7 +47,6 @@
|
|||||||
(function() {
|
(function() {
|
||||||
// Safari 10.1 supports type=module but ignores nomodule, so we add this check.
|
// Safari 10.1 supports type=module but ignores nomodule, so we add this check.
|
||||||
if (!isS101) {
|
if (!isS101) {
|
||||||
_ls("/static/polyfills/custom-elements-es5-adapter.js");
|
|
||||||
<% if (useRollup) { %>
|
<% if (useRollup) { %>
|
||||||
_ls("/static/js/s.min.js").onload = function() {
|
_ls("/static/js/s.min.js").onload = function() {
|
||||||
System.import("<%= es5PageJS %>");
|
System.import("<%= es5PageJS %>");
|
||||||
|
@@ -52,21 +52,21 @@
|
|||||||
<%= renderTemplate('_js_base') %>
|
<%= renderTemplate('_js_base') %>
|
||||||
<%= renderTemplate('_preload_roboto') %>
|
<%= renderTemplate('_preload_roboto') %>
|
||||||
|
|
||||||
<script>
|
<script crossorigin="use-credentials">
|
||||||
import("<%= latestCoreJS %>");
|
import("<%= latestCoreJS %>");
|
||||||
import("<%= latestAppJS %>");
|
import("<%= latestAppJS %>");
|
||||||
window.customPanelJS = "<%= latestCustomPanelJS %>";
|
window.customPanelJS = "<%= latestCustomPanelJS %>";
|
||||||
window.latestJS = true;
|
window.latestJS = true;
|
||||||
</script>
|
</script>
|
||||||
|
<script>
|
||||||
{% for extra_module in extra_modules -%}
|
{% for extra_module in extra_modules -%}
|
||||||
<script type="module" crossorigin="use-credentials" src="{{ extra_module }}"></script>
|
import("{{ extra_module }}");
|
||||||
{% endfor -%}
|
{% endfor -%}
|
||||||
|
</script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
(function() {
|
|
||||||
if (!window.latestJS) {
|
if (!window.latestJS) {
|
||||||
window.customPanelJS = "<%= es5CustomPanelJS %>";
|
window.customPanelJS = "<%= es5CustomPanelJS %>";
|
||||||
_ls("/static/polyfills/custom-elements-es5-adapter.js");
|
|
||||||
|
|
||||||
<% if (useRollup) { %>
|
<% if (useRollup) { %>
|
||||||
_ls("/static/js/s.min.js").onload = function() {
|
_ls("/static/js/s.min.js").onload = function() {
|
||||||
@@ -80,11 +80,14 @@
|
|||||||
_ls("<%= es5CoreJS %>");
|
_ls("<%= es5CoreJS %>");
|
||||||
_ls("<%= es5AppJS %>");
|
_ls("<%= es5AppJS %>");
|
||||||
<% } %>
|
<% } %>
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<script>
|
||||||
|
if (!window.latestJS) {
|
||||||
{% for extra_script in extra_js_es5 -%}
|
{% for extra_script in extra_js_es5 -%}
|
||||||
_ls("{{ extra_script }}");
|
_ls("{{ extra_script }}");
|
||||||
{% endfor -%}
|
{% endfor -%}
|
||||||
}
|
}
|
||||||
})();
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{% for extra_url in extra_urls -%}
|
{% for extra_url in extra_urls -%}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user