Compare commits
115 Commits
add-import
...
20200519.3
Author | SHA1 | Date | |
---|---|---|---|
![]() |
9c574995ac | ||
![]() |
3f35c603d2 | ||
![]() |
8e0688140e | ||
![]() |
0c57c05a22 | ||
![]() |
c6be3be45a | ||
![]() |
9689db9605 | ||
![]() |
4b8f7e1fe9 | ||
![]() |
e165a96689 | ||
![]() |
91a655a81e | ||
![]() |
612811e2c2 | ||
![]() |
11bd72915c | ||
![]() |
339221e793 | ||
![]() |
9be864b45e | ||
![]() |
fafad302ba | ||
![]() |
0c94ad46b2 | ||
![]() |
d9bb40f934 | ||
![]() |
bbc16b6bc8 | ||
![]() |
16154e9d8b | ||
![]() |
61bd536d7b | ||
![]() |
02c798a8bc | ||
![]() |
c37a691b9b | ||
![]() |
23c68d17e8 | ||
![]() |
0a7f610ad3 | ||
![]() |
c9e8bd2e5d | ||
![]() |
a66d2ca1b9 | ||
![]() |
d38a0f0366 | ||
![]() |
29759de021 | ||
![]() |
264759ddf0 | ||
![]() |
91b0bd5b5e | ||
![]() |
a0e2cc7a3a | ||
![]() |
9e1eb41cbe | ||
![]() |
c37eb023b0 | ||
![]() |
840948ba4a | ||
![]() |
8fbdd88b24 | ||
![]() |
512d35d2e0 | ||
![]() |
0321e55a42 | ||
![]() |
a1ee9ad48b | ||
![]() |
1ad1fd28f1 | ||
![]() |
007f8b50b9 | ||
![]() |
6fe8a87cca | ||
![]() |
5f46679d94 | ||
![]() |
18a3f212f3 | ||
![]() |
67a3f5d87b | ||
![]() |
c88439ba2f | ||
![]() |
67e17d4016 | ||
![]() |
b61cf60faf | ||
![]() |
5503853445 | ||
![]() |
259726f5be | ||
![]() |
bec42d941b | ||
![]() |
dcbbaf08f9 | ||
![]() |
349355584a | ||
![]() |
7ce0b34774 | ||
![]() |
dd894758a4 | ||
![]() |
2aa1eb97fd | ||
![]() |
4c43ae7b2f | ||
![]() |
34e516e0be | ||
![]() |
7bd3427e76 | ||
![]() |
6853db693a | ||
![]() |
252ce1e467 | ||
![]() |
221c12bd61 | ||
![]() |
12edd68874 | ||
![]() |
f469753fb1 | ||
![]() |
c894ecd0e6 | ||
![]() |
2153bc536c | ||
![]() |
86bbac430c | ||
![]() |
16ad8a3c01 | ||
![]() |
b2af91c83e | ||
![]() |
4c0810f530 | ||
![]() |
f70130e21f | ||
![]() |
6bb7b01d00 | ||
![]() |
51e7aaa805 | ||
![]() |
54704e53b3 | ||
![]() |
cc46797576 | ||
![]() |
7f1fb6f75f | ||
![]() |
2c2a1d204b | ||
![]() |
581fafdcc9 | ||
![]() |
10358abbec | ||
![]() |
de1ffe67b1 | ||
![]() |
eb2b24d57c | ||
![]() |
825db8a56a | ||
![]() |
577a21fc5c | ||
![]() |
1b2841eef9 | ||
![]() |
845511e322 | ||
![]() |
96ab057853 | ||
![]() |
f85cf0a238 | ||
![]() |
84a2676a9c | ||
![]() |
466a1af902 | ||
![]() |
ebbe7e805f | ||
![]() |
c861ee025e | ||
![]() |
6d0823328d | ||
![]() |
60be14dc77 | ||
![]() |
2d627819d9 | ||
![]() |
cf575f83f5 | ||
![]() |
79935b2d4c | ||
![]() |
d1cceb2013 | ||
![]() |
f5da130d51 | ||
![]() |
8768304ec5 | ||
![]() |
f10a5dcdbe | ||
![]() |
a27428ebcd | ||
![]() |
d19acf17c2 | ||
![]() |
1a0bf861ee | ||
![]() |
db906ad4d0 | ||
![]() |
75ed0f2f99 | ||
![]() |
29ed1144d5 | ||
![]() |
fa445d4066 | ||
![]() |
d10be4ef2d | ||
![]() |
7f66d5b8e9 | ||
![]() |
0c8cd680c2 | ||
![]() |
a7ba1977b4 | ||
![]() |
a960b39235 | ||
![]() |
3febf059ec | ||
![]() |
20203f7bdb | ||
![]() |
a399d76d06 | ||
![]() |
1ca097c5a0 | ||
![]() |
ae6243b7bf |
@@ -6,12 +6,12 @@ This is the repository for the official [Home Assistant](https://home-assistant.
|
|||||||
|
|
||||||
- [View demo of Home Assistant](https://demo.home-assistant.io/)
|
- [View demo of Home Assistant](https://demo.home-assistant.io/)
|
||||||
- [More information about Home Assistant](https://home-assistant.io)
|
- [More information about Home Assistant](https://home-assistant.io)
|
||||||
- [Frontend development instructions](https://developers.home-assistant.io/docs/en/frontend_index.html)
|
- [Frontend development instructions](https://developers.home-assistant.io/docs/frontend/development/)
|
||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|
||||||
- Initial setup: `script/setup`
|
- Initial setup: `script/setup`
|
||||||
- Development: [Instructions](https://developers.home-assistant.io/docs/en/frontend_development.html)
|
- Development: [Instructions](https://developers.home-assistant.io/docs/frontend/development/)
|
||||||
- Production build: `script/build_frontend`
|
- Production build: `script/build_frontend`
|
||||||
- Gallery: `cd gallery && script/develop_gallery`
|
- Gallery: `cd gallery && script/develop_gallery`
|
||||||
- Hass.io: [Instructions](https://developers.home-assistant.io/docs/en/hassio_hass.html)
|
- Hass.io: [Instructions](https://developers.home-assistant.io/docs/en/hassio_hass.html)
|
||||||
|
@@ -20,7 +20,7 @@ gulp.task(
|
|||||||
},
|
},
|
||||||
"clean",
|
"clean",
|
||||||
gulp.parallel(
|
gulp.parallel(
|
||||||
"gen-service-worker-dev",
|
"gen-service-worker-app-dev",
|
||||||
"gen-icons-json",
|
"gen-icons-json",
|
||||||
"gen-pages-dev",
|
"gen-pages-dev",
|
||||||
"gen-index-app-dev",
|
"gen-index-app-dev",
|
||||||
@@ -46,7 +46,7 @@ gulp.task(
|
|||||||
gulp.parallel(
|
gulp.parallel(
|
||||||
"gen-pages-prod",
|
"gen-pages-prod",
|
||||||
"gen-index-app-prod",
|
"gen-index-app-prod",
|
||||||
"gen-service-worker-prod"
|
"gen-service-worker-app-prod"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
@@ -9,25 +9,30 @@ const paths = require("../paths");
|
|||||||
gulp.task("compress-app", function compressApp() {
|
gulp.task("compress-app", function compressApp() {
|
||||||
const jsLatest = gulp
|
const jsLatest = gulp
|
||||||
.src(path.resolve(paths.output, "**/*.js"))
|
.src(path.resolve(paths.output, "**/*.js"))
|
||||||
.pipe(zopfli())
|
.pipe(zopfli({ threshold: 150 }))
|
||||||
.pipe(gulp.dest(paths.output));
|
.pipe(gulp.dest(paths.output));
|
||||||
|
|
||||||
const jsEs5 = gulp
|
const jsEs5 = gulp
|
||||||
.src(path.resolve(paths.output_es5, "**/*.js"))
|
.src(path.resolve(paths.output_es5, "**/*.js"))
|
||||||
.pipe(zopfli())
|
.pipe(zopfli({ threshold: 150 }))
|
||||||
.pipe(gulp.dest(paths.output_es5));
|
.pipe(gulp.dest(paths.output_es5));
|
||||||
|
|
||||||
const polyfills = gulp
|
const polyfills = gulp
|
||||||
.src(path.resolve(paths.static, "polyfills/*.js"))
|
.src(path.resolve(paths.static, "polyfills/*.js"))
|
||||||
.pipe(zopfli())
|
.pipe(zopfli({ threshold: 150 }))
|
||||||
.pipe(gulp.dest(path.resolve(paths.static, "polyfills")));
|
.pipe(gulp.dest(path.resolve(paths.static, "polyfills")));
|
||||||
|
|
||||||
const translations = gulp
|
const translations = gulp
|
||||||
.src(path.resolve(paths.static, "translations/*.json"))
|
.src(path.resolve(paths.static, "translations/**/*.json"))
|
||||||
.pipe(zopfli())
|
.pipe(zopfli({ threshold: 150 }))
|
||||||
.pipe(gulp.dest(path.resolve(paths.static, "translations")));
|
.pipe(gulp.dest(path.resolve(paths.static, "translations")));
|
||||||
|
|
||||||
return merge(jsLatest, jsEs5, polyfills, translations);
|
const icons = gulp
|
||||||
|
.src(path.resolve(paths.static, "mdi/*.json"))
|
||||||
|
.pipe(zopfli({ threshold: 150 }))
|
||||||
|
.pipe(gulp.dest(path.resolve(paths.static, "mdi")));
|
||||||
|
|
||||||
|
return merge(jsLatest, jsEs5, polyfills, translations, icons);
|
||||||
});
|
});
|
||||||
|
|
||||||
gulp.task("compress-hassio", function compressApp() {
|
gulp.task("compress-hassio", function compressApp() {
|
||||||
|
@@ -1,9 +1,14 @@
|
|||||||
const del = require("del");
|
const del = require("del");
|
||||||
const gulp = require("gulp");
|
const gulp = require("gulp");
|
||||||
|
const fs = require("fs");
|
||||||
const mapStream = require("map-stream");
|
const mapStream = require("map-stream");
|
||||||
|
|
||||||
const inDir = "translations/frontend";
|
const inDirFrontend = "translations/frontend";
|
||||||
const downloadDir = inDir + "/downloads";
|
const inDirBackend = "translations/backend";
|
||||||
|
const downloadDir = "translations/downloads";
|
||||||
|
const srcMeta = "src/translations/translationMetadata.json";
|
||||||
|
|
||||||
|
const encoding = "utf8";
|
||||||
|
|
||||||
const tasks = [];
|
const tasks = [];
|
||||||
|
|
||||||
@@ -53,9 +58,25 @@ gulp.task(taskName, function () {
|
|||||||
});
|
});
|
||||||
tasks.push(taskName);
|
tasks.push(taskName);
|
||||||
|
|
||||||
|
taskName = "check-all-files-exist";
|
||||||
|
gulp.task(taskName, function () {
|
||||||
|
const file = fs.readFileSync(srcMeta, { encoding });
|
||||||
|
const meta = JSON.parse(file);
|
||||||
|
Object.keys(meta).forEach((lang) => {
|
||||||
|
if (!fs.existsSync(`${inDirFrontend}/${lang}.json`)) {
|
||||||
|
fs.writeFileSync(`${inDirFrontend}/${lang}.json`, JSON.stringify({}));
|
||||||
|
}
|
||||||
|
if (!fs.existsSync(`${inDirBackend}/${lang}.json`)) {
|
||||||
|
fs.writeFileSync(`${inDirBackend}/${lang}.json`, JSON.stringify({}));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return Promise.resolve();
|
||||||
|
});
|
||||||
|
tasks.push(taskName);
|
||||||
|
|
||||||
taskName = "move-downloaded-translations";
|
taskName = "move-downloaded-translations";
|
||||||
gulp.task(taskName, function () {
|
gulp.task(taskName, function () {
|
||||||
return gulp.src(`${downloadDir}/*.json`).pipe(gulp.dest(inDir));
|
return gulp.src(`${downloadDir}/*.json`).pipe(gulp.dest(inDirFrontend));
|
||||||
});
|
});
|
||||||
tasks.push(taskName);
|
tasks.push(taskName);
|
||||||
|
|
||||||
@@ -65,6 +86,7 @@ gulp.task(
|
|||||||
gulp.series(
|
gulp.series(
|
||||||
"check-translations-html",
|
"check-translations-html",
|
||||||
"move-downloaded-translations",
|
"move-downloaded-translations",
|
||||||
|
"check-all-files-exist",
|
||||||
"clean-downloaded-translations"
|
"clean-downloaded-translations"
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
@@ -5,18 +5,22 @@
|
|||||||
const gulp = require("gulp");
|
const gulp = require("gulp");
|
||||||
const path = require("path");
|
const path = require("path");
|
||||||
const fs = require("fs-extra");
|
const fs = require("fs-extra");
|
||||||
const config = require("../paths.js");
|
const workboxBuild = require("workbox-build");
|
||||||
|
const sourceMapUrl = require("source-map-url");
|
||||||
|
const paths = require("../paths.js");
|
||||||
|
|
||||||
const swPath = path.resolve(config.root, "service_worker.js");
|
const swDest = path.resolve(paths.root, "service_worker.js");
|
||||||
|
|
||||||
const writeSW = (content) => fs.outputFileSync(swPath, content.trim() + "\n");
|
const writeSW = (content) => fs.outputFileSync(swDest, content.trim() + "\n");
|
||||||
|
|
||||||
gulp.task("gen-service-worker-dev", (done) => {
|
gulp.task("gen-service-worker-app-dev", (done) => {
|
||||||
writeSW(
|
writeSW(
|
||||||
`
|
`
|
||||||
console.debug('Service worker disabled in development');
|
console.debug('Service worker disabled in development');
|
||||||
|
|
||||||
self.addEventListener('install', (event) => {
|
self.addEventListener('install', (event) => {
|
||||||
|
// This will activate the dev service worker,
|
||||||
|
// removing any prod service worker the dev might have running
|
||||||
self.skipWaiting();
|
self.skipWaiting();
|
||||||
});
|
});
|
||||||
`
|
`
|
||||||
@@ -24,10 +28,63 @@ self.addEventListener('install', (event) => {
|
|||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
gulp.task("gen-service-worker-prod", (done) => {
|
gulp.task("gen-service-worker-app-prod", async () => {
|
||||||
fs.copySync(
|
// Read bundled source file
|
||||||
path.resolve(config.output, "service_worker.js"),
|
const bundleManifestLatest = require(path.resolve(
|
||||||
path.resolve(config.root, "service_worker.js")
|
paths.output,
|
||||||
|
"manifest.json"
|
||||||
|
));
|
||||||
|
let serviceWorkerContent = fs.readFileSync(
|
||||||
|
paths.root + bundleManifestLatest["service_worker.js"],
|
||||||
|
"utf-8"
|
||||||
);
|
);
|
||||||
done();
|
|
||||||
|
// Delete old file from frontend_latest so manifest won't pick it up
|
||||||
|
fs.removeSync(paths.root + bundleManifestLatest["service_worker.js"]);
|
||||||
|
fs.removeSync(paths.root + bundleManifestLatest["service_worker.js.map"]);
|
||||||
|
|
||||||
|
// Remove ES5
|
||||||
|
const bundleManifestES5 = require(path.resolve(
|
||||||
|
paths.output_es5,
|
||||||
|
"manifest.json"
|
||||||
|
));
|
||||||
|
fs.removeSync(paths.root + bundleManifestES5["service_worker.js"]);
|
||||||
|
fs.removeSync(paths.root + bundleManifestES5["service_worker.js.map"]);
|
||||||
|
|
||||||
|
const workboxManifest = await workboxBuild.getManifest({
|
||||||
|
// Files that mach this pattern will be considered unique and skip revision check
|
||||||
|
// ignore JS files + translation files
|
||||||
|
dontCacheBustURLsMatching: /(frontend_latest\/.+|static\/translations\/.+)/,
|
||||||
|
|
||||||
|
globDirectory: paths.root,
|
||||||
|
globPatterns: [
|
||||||
|
"frontend_latest/*.js",
|
||||||
|
// Cache all English translations because we catch them as fallback
|
||||||
|
// Using pattern to match hash instead of * to avoid caching en-GB
|
||||||
|
// 'v' added as valid hash letter because in dev we hash with 'dev'
|
||||||
|
"static/translations/**/en-+([a-fv0-9]).json",
|
||||||
|
// Icon shown on splash screen
|
||||||
|
"static/icons/favicon-192x192.png",
|
||||||
|
"static/icons/favicon.ico",
|
||||||
|
// Common fonts
|
||||||
|
"static/fonts/roboto/Roboto-Light.woff2",
|
||||||
|
"static/fonts/roboto/Roboto-Medium.woff2",
|
||||||
|
"static/fonts/roboto/Roboto-Regular.woff2",
|
||||||
|
"static/fonts/roboto/Roboto-Bold.woff2",
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const warning of workboxManifest.warnings) {
|
||||||
|
console.warn(warning);
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove source map and add WB manifest
|
||||||
|
serviceWorkerContent = sourceMapUrl.removeFrom(serviceWorkerContent);
|
||||||
|
serviceWorkerContent = serviceWorkerContent.replace(
|
||||||
|
"WB_MANIFEST",
|
||||||
|
JSON.stringify(workboxManifest.manifestEntries)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Write new file to root
|
||||||
|
fs.writeFileSync(swDest, serviceWorkerContent);
|
||||||
});
|
});
|
||||||
|
@@ -1,8 +1,8 @@
|
|||||||
const webpack = require("webpack");
|
const webpack = require("webpack");
|
||||||
const path = require("path");
|
const path = require("path");
|
||||||
const TerserPlugin = require("terser-webpack-plugin");
|
const TerserPlugin = require("terser-webpack-plugin");
|
||||||
const WorkboxPlugin = require("workbox-webpack-plugin");
|
|
||||||
const ManifestPlugin = require("webpack-manifest-plugin");
|
const ManifestPlugin = require("webpack-manifest-plugin");
|
||||||
|
const WorkerPlugin = require("worker-plugin");
|
||||||
const paths = require("./paths.js");
|
const paths = require("./paths.js");
|
||||||
const env = require("./env.js");
|
const env = require("./env.js");
|
||||||
const { babelLoaderConfig } = require("./babel.js");
|
const { babelLoaderConfig } = require("./babel.js");
|
||||||
@@ -21,7 +21,9 @@ const createWebpackConfig = ({
|
|||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
mode: isProdBuild ? "production" : "development",
|
mode: isProdBuild ? "production" : "development",
|
||||||
devtool: isProdBuild ? "source-map" : "inline-cheap-module-source-map",
|
devtool: isProdBuild
|
||||||
|
? "cheap-module-source-map"
|
||||||
|
: "eval-cheap-module-source-map",
|
||||||
entry,
|
entry,
|
||||||
module: {
|
module: {
|
||||||
rules: [
|
rules: [
|
||||||
@@ -50,6 +52,7 @@ const createWebpackConfig = ({
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
|
new WorkerPlugin(),
|
||||||
new ManifestPlugin(),
|
new ManifestPlugin(),
|
||||||
new webpack.DefinePlugin({
|
new webpack.DefinePlugin({
|
||||||
__DEV__: !isProdBuild,
|
__DEV__: !isProdBuild,
|
||||||
@@ -75,6 +78,10 @@ const createWebpackConfig = ({
|
|||||||
/@polymer\/font-roboto\/roboto\.js$/,
|
/@polymer\/font-roboto\/roboto\.js$/,
|
||||||
path.resolve(paths.polymer_dir, "src/util/empty.js")
|
path.resolve(paths.polymer_dir, "src/util/empty.js")
|
||||||
),
|
),
|
||||||
|
new webpack.NormalModuleReplacementPlugin(
|
||||||
|
/@vaadin\/vaadin-material-styles\/font-roboto\.js$/,
|
||||||
|
path.resolve(paths.polymer_dir, "src/util/empty.js")
|
||||||
|
),
|
||||||
// Ignore mwc icons pointing at CDN.
|
// Ignore mwc icons pointing at CDN.
|
||||||
new webpack.NormalModuleReplacementPlugin(
|
new webpack.NormalModuleReplacementPlugin(
|
||||||
/@material\/mwc-icon\/mwc-icon-font\.js$/,
|
/@material\/mwc-icon\/mwc-icon-font\.js$/,
|
||||||
@@ -100,15 +107,16 @@ const createWebpackConfig = ({
|
|||||||
latestBuild ? "frontend_latest" : "frontend_es5"
|
latestBuild ? "frontend_latest" : "frontend_es5"
|
||||||
),
|
),
|
||||||
publicPath: latestBuild ? "/frontend_latest/" : "/frontend_es5/",
|
publicPath: latestBuild ? "/frontend_latest/" : "/frontend_es5/",
|
||||||
// For workerize loader
|
// To silence warning in worker plugin
|
||||||
globalObject: "self",
|
globalObject: "self",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const createAppConfig = ({ isProdBuild, latestBuild, isStatsBuild }) => {
|
const createAppConfig = ({ isProdBuild, latestBuild, isStatsBuild }) => {
|
||||||
const config = createWebpackConfig({
|
return createWebpackConfig({
|
||||||
entry: {
|
entry: {
|
||||||
|
service_worker: "./src/entrypoints/service_worker.ts",
|
||||||
app: "./src/entrypoints/app.ts",
|
app: "./src/entrypoints/app.ts",
|
||||||
authorize: "./src/entrypoints/authorize.ts",
|
authorize: "./src/entrypoints/authorize.ts",
|
||||||
onboarding: "./src/entrypoints/onboarding.ts",
|
onboarding: "./src/entrypoints/onboarding.ts",
|
||||||
@@ -121,48 +129,6 @@ const createAppConfig = ({ isProdBuild, latestBuild, isStatsBuild }) => {
|
|||||||
latestBuild,
|
latestBuild,
|
||||||
isStatsBuild,
|
isStatsBuild,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (latestBuild) {
|
|
||||||
// Create an object mapping browser urls to their paths during build
|
|
||||||
const translationMetadata = require("../build-translations/translationMetadata.json");
|
|
||||||
const workBoxTranslationsTemplatedURLs = {};
|
|
||||||
const englishFilename = `en-${translationMetadata.translations.en.hash}.json`;
|
|
||||||
|
|
||||||
// core
|
|
||||||
workBoxTranslationsTemplatedURLs[
|
|
||||||
`/static/translations/${englishFilename}`
|
|
||||||
] = `build-translations/output/${englishFilename}`;
|
|
||||||
|
|
||||||
translationMetadata.fragments.forEach((fragment) => {
|
|
||||||
workBoxTranslationsTemplatedURLs[
|
|
||||||
`/static/translations/${fragment}/${englishFilename}`
|
|
||||||
] = `build-translations/output/${fragment}/${englishFilename}`;
|
|
||||||
});
|
|
||||||
|
|
||||||
config.plugins.push(
|
|
||||||
new WorkboxPlugin.InjectManifest({
|
|
||||||
swSrc: "./src/entrypoints/service-worker-hass.js",
|
|
||||||
swDest: "service_worker.js",
|
|
||||||
importWorkboxFrom: "local",
|
|
||||||
include: [/\.js$/],
|
|
||||||
templatedURLs: {
|
|
||||||
...workBoxTranslationsTemplatedURLs,
|
|
||||||
"/static/icons/favicon-192x192.png":
|
|
||||||
"public/icons/favicon-192x192.png",
|
|
||||||
"/static/fonts/roboto/Roboto-Light.woff2":
|
|
||||||
"node_modules/roboto-fontface/fonts/roboto/Roboto-Light.woff2",
|
|
||||||
"/static/fonts/roboto/Roboto-Medium.woff2":
|
|
||||||
"node_modules/roboto-fontface/fonts/roboto/Roboto-Medium.woff2",
|
|
||||||
"/static/fonts/roboto/Roboto-Regular.woff2":
|
|
||||||
"node_modules/roboto-fontface/fonts/roboto/Roboto-Regular.woff2",
|
|
||||||
"/static/fonts/roboto/Roboto-Bold.woff2":
|
|
||||||
"node_modules/roboto-fontface/fonts/roboto/Roboto-Bold.woff2",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return config;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const createDemoConfig = ({ isProdBuild, latestBuild, isStatsBuild }) => {
|
const createDemoConfig = ({ isProdBuild, latestBuild, isStatsBuild }) => {
|
||||||
|
@@ -184,7 +184,7 @@ export class HcConnect extends LitElement {
|
|||||||
this.castManager = null;
|
this.castManager = null;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
registerServiceWorker(false);
|
registerServiceWorker(this, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _handleDemo() {
|
private async _handleDemo() {
|
||||||
|
@@ -7,5 +7,5 @@ set -e
|
|||||||
cd "$(dirname "$0")/.."
|
cd "$(dirname "$0")/.."
|
||||||
|
|
||||||
STATS=1 NODE_ENV=production ../node_modules/.bin/webpack --profile --json > compilation-stats.json
|
STATS=1 NODE_ENV=production ../node_modules/.bin/webpack --profile --json > compilation-stats.json
|
||||||
npx webpack-bundle-analyzer compilation-stats.json dist
|
npx webpack-bundle-analyzer compilation-stats.json dist/frontend_latest
|
||||||
rm compilation-stats.json
|
rm compilation-stats.json
|
||||||
|
@@ -63,7 +63,7 @@ export const demoLovelaceTeachingbirds: DemoConfig["lovelace"] = () => ({
|
|||||||
elements: [
|
elements: [
|
||||||
{
|
{
|
||||||
style: {
|
style: {
|
||||||
"--mdc-icon-size": "100px",
|
"--mdc-icon-size": "100%",
|
||||||
top: "50%",
|
top: "50%",
|
||||||
left: "50%",
|
left: "50%",
|
||||||
},
|
},
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import "@polymer/paper-card/paper-card";
|
import { mdiArrowUpBoldCircle, mdiPuzzle } from "@mdi/js";
|
||||||
import {
|
import {
|
||||||
css,
|
css,
|
||||||
CSSResultArray,
|
CSSResultArray,
|
||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
import { atLeastVersion } from "../../../src/common/config/version";
|
import { atLeastVersion } from "../../../src/common/config/version";
|
||||||
import { navigate } from "../../../src/common/navigate";
|
import { navigate } from "../../../src/common/navigate";
|
||||||
|
import "../../../src/components/ha-card";
|
||||||
import {
|
import {
|
||||||
HassioAddonInfo,
|
HassioAddonInfo,
|
||||||
HassioAddonRepository,
|
HassioAddonRepository,
|
||||||
@@ -66,7 +67,7 @@ class HassioAddonRepositoryEl extends LitElement {
|
|||||||
<div class="card-group">
|
<div class="card-group">
|
||||||
${addons.map(
|
${addons.map(
|
||||||
(addon) => html`
|
(addon) => html`
|
||||||
<paper-card
|
<ha-card
|
||||||
.addon=${addon}
|
.addon=${addon}
|
||||||
class=${addon.available ? "" : "not_available"}
|
class=${addon.available ? "" : "not_available"}
|
||||||
@click=${this._addonTapped}
|
@click=${this._addonTapped}
|
||||||
@@ -78,8 +79,8 @@ class HassioAddonRepositoryEl extends LitElement {
|
|||||||
.description=${addon.description}
|
.description=${addon.description}
|
||||||
.available=${addon.available}
|
.available=${addon.available}
|
||||||
.icon=${addon.installed && addon.installed !== addon.version
|
.icon=${addon.installed && addon.installed !== addon.version
|
||||||
? "hassio:arrow-up-bold-circle"
|
? mdiArrowUpBoldCircle
|
||||||
: "hassio:puzzle"}
|
: mdiPuzzle}
|
||||||
.iconTitle=${addon.installed
|
.iconTitle=${addon.installed
|
||||||
? addon.installed !== addon.version
|
? addon.installed !== addon.version
|
||||||
? "New version available"
|
? "New version available"
|
||||||
@@ -111,7 +112,7 @@ class HassioAddonRepositoryEl extends LitElement {
|
|||||||
: ""}
|
: ""}
|
||||||
></hassio-card-content>
|
></hassio-card-content>
|
||||||
</div>
|
</div>
|
||||||
</paper-card>
|
</ha-card>
|
||||||
`
|
`
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -127,7 +128,7 @@ class HassioAddonRepositoryEl extends LitElement {
|
|||||||
return [
|
return [
|
||||||
hassioStyle,
|
hassioStyle,
|
||||||
css`
|
css`
|
||||||
paper-card {
|
ha-card {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
.not_available {
|
.not_available {
|
||||||
|
@@ -1,3 +1,6 @@
|
|||||||
|
import "@material/mwc-icon-button/mwc-icon-button";
|
||||||
|
import "@material/mwc-list/mwc-list-item";
|
||||||
|
import { mdiDotsVertical } from "@mdi/js";
|
||||||
import {
|
import {
|
||||||
css,
|
css,
|
||||||
CSSResult,
|
CSSResult,
|
||||||
@@ -6,22 +9,21 @@ import {
|
|||||||
PropertyValues,
|
PropertyValues,
|
||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
import { html, TemplateResult } from "lit-html";
|
import { html, TemplateResult } from "lit-html";
|
||||||
|
import "../../../src/common/search/search-input";
|
||||||
|
import "../../../src/components/ha-button-menu";
|
||||||
|
import "../../../src/components/ha-svg-icon";
|
||||||
import {
|
import {
|
||||||
fetchHassioAddonsInfo,
|
fetchHassioAddonsInfo,
|
||||||
HassioAddonInfo,
|
HassioAddonInfo,
|
||||||
HassioAddonRepository,
|
HassioAddonRepository,
|
||||||
reloadHassioAddons,
|
reloadHassioAddons,
|
||||||
} from "../../../src/data/hassio/addon";
|
} from "../../../src/data/hassio/addon";
|
||||||
import "../../../src/components/ha-icon-button";
|
|
||||||
import "../../../src/layouts/loading-screen";
|
|
||||||
import "../../../src/layouts/hass-tabs-subpage";
|
import "../../../src/layouts/hass-tabs-subpage";
|
||||||
|
import "../../../src/layouts/loading-screen";
|
||||||
import { HomeAssistant, Route } from "../../../src/types";
|
import { HomeAssistant, Route } from "../../../src/types";
|
||||||
import "../../../src/common/search/search-input";
|
|
||||||
import "./hassio-addon-repository";
|
|
||||||
|
|
||||||
import { supervisorTabs } from "../hassio-panel";
|
|
||||||
|
|
||||||
import { showRepositoriesDialog } from "../dialogs/repositories/show-dialog-repositories";
|
import { showRepositoriesDialog } from "../dialogs/repositories/show-dialog-repositories";
|
||||||
|
import { supervisorTabs } from "../hassio-panel";
|
||||||
|
import "./hassio-addon-repository";
|
||||||
|
|
||||||
const sortRepos = (a: HassioAddonRepository, b: HassioAddonRepository) => {
|
const sortRepos = (a: HassioAddonRepository, b: HassioAddonRepository) => {
|
||||||
if (a.slug === "local") {
|
if (a.slug === "local") {
|
||||||
@@ -94,27 +96,17 @@ class HassioAddonStore extends LitElement {
|
|||||||
.tabs=${supervisorTabs}
|
.tabs=${supervisorTabs}
|
||||||
>
|
>
|
||||||
<span slot="header">Add-on store</span>
|
<span slot="header">Add-on store</span>
|
||||||
<paper-menu-button
|
<ha-button-menu corner="BOTTOM_START" slot="toolbar-icon">
|
||||||
close-on-activate
|
<mwc-icon-button slot="trigger" alt="menu">
|
||||||
no-animations
|
<ha-svg-icon path=${mdiDotsVertical}></ha-svg-icon>
|
||||||
horizontal-align="right"
|
</mwc-icon-button>
|
||||||
horizontal-offset="-5"
|
<mwc-list-item @tap=${this._manageRepositories}>
|
||||||
slot="toolbar-icon"
|
Repositories
|
||||||
>
|
</mwc-list-item>
|
||||||
<ha-icon-button
|
<mwc-list-item @tap=${this.refreshData}>
|
||||||
icon="hassio:dots-vertical"
|
Reload
|
||||||
slot="dropdown-trigger"
|
</mwc-list-item>
|
||||||
alt="menu"
|
</ha-button-menu>
|
||||||
></ha-icon-button>
|
|
||||||
<paper-listbox slot="dropdown-content" role="listbox">
|
|
||||||
<paper-item @tap=${this._manageRepositories}>
|
|
||||||
Repositories
|
|
||||||
</paper-item>
|
|
||||||
<paper-item @tap=${this.refreshData}>
|
|
||||||
Reload
|
|
||||||
</paper-item>
|
|
||||||
</paper-listbox>
|
|
||||||
</paper-menu-button>
|
|
||||||
${repos.length === 0
|
${repos.length === 0
|
||||||
? html`<loading-screen></loading-screen>`
|
? html`<loading-screen></loading-screen>`
|
||||||
: html`
|
: html`
|
||||||
|
@@ -1,5 +1,4 @@
|
|||||||
import "@material/mwc-button";
|
import "@material/mwc-button";
|
||||||
import "@polymer/paper-card/paper-card";
|
|
||||||
import "@polymer/paper-dropdown-menu/paper-dropdown-menu";
|
import "@polymer/paper-dropdown-menu/paper-dropdown-menu";
|
||||||
import "@polymer/paper-item/paper-item";
|
import "@polymer/paper-item/paper-item";
|
||||||
import "@polymer/paper-listbox/paper-listbox";
|
import "@polymer/paper-listbox/paper-listbox";
|
||||||
@@ -14,6 +13,7 @@ import {
|
|||||||
TemplateResult,
|
TemplateResult,
|
||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
import "web-animations-js/web-animations-next-lite.min";
|
import "web-animations-js/web-animations-next-lite.min";
|
||||||
|
import "../../../../src/components/ha-card";
|
||||||
import {
|
import {
|
||||||
HassioAddonDetails,
|
HassioAddonDetails,
|
||||||
HassioAddonSetOptionParams,
|
HassioAddonSetOptionParams,
|
||||||
@@ -23,9 +23,9 @@ import {
|
|||||||
fetchHassioHardwareAudio,
|
fetchHassioHardwareAudio,
|
||||||
HassioHardwareAudioDevice,
|
HassioHardwareAudioDevice,
|
||||||
} from "../../../../src/data/hassio/hardware";
|
} from "../../../../src/data/hassio/hardware";
|
||||||
import { suggestAddonRestart } from "../../dialogs/suggestAddonRestart";
|
|
||||||
import { haStyle } from "../../../../src/resources/styles";
|
import { haStyle } from "../../../../src/resources/styles";
|
||||||
import { HomeAssistant } from "../../../../src/types";
|
import { HomeAssistant } from "../../../../src/types";
|
||||||
|
import { suggestAddonRestart } from "../../dialogs/suggestAddonRestart";
|
||||||
import { hassioStyle } from "../../resources/hassio-style";
|
import { hassioStyle } from "../../resources/hassio-style";
|
||||||
|
|
||||||
@customElement("hassio-addon-audio")
|
@customElement("hassio-addon-audio")
|
||||||
@@ -46,7 +46,7 @@ class HassioAddonAudio extends LitElement {
|
|||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
return html`
|
return html`
|
||||||
<paper-card heading="Audio">
|
<ha-card header="Audio">
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
${this._error ? html` <div class="errors">${this._error}</div> ` : ""}
|
${this._error ? html` <div class="errors">${this._error}</div> ` : ""}
|
||||||
|
|
||||||
@@ -92,7 +92,7 @@ class HassioAddonAudio extends LitElement {
|
|||||||
<div class="card-actions">
|
<div class="card-actions">
|
||||||
<mwc-button @click=${this._saveSettings}>Save</mwc-button>
|
<mwc-button @click=${this._saveSettings}>Save</mwc-button>
|
||||||
</div>
|
</div>
|
||||||
</paper-card>
|
</ha-card>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,7 +102,7 @@ class HassioAddonAudio extends LitElement {
|
|||||||
hassioStyle,
|
hassioStyle,
|
||||||
css`
|
css`
|
||||||
:host,
|
:host,
|
||||||
paper-card,
|
ha-card,
|
||||||
paper-dropdown-menu {
|
paper-dropdown-menu {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
@@ -8,12 +8,10 @@ import {
|
|||||||
property,
|
property,
|
||||||
TemplateResult,
|
TemplateResult,
|
||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
|
|
||||||
import { HomeAssistant } from "../../../../src/types";
|
|
||||||
import { HassioAddonDetails } from "../../../../src/data/hassio/addon";
|
import { HassioAddonDetails } from "../../../../src/data/hassio/addon";
|
||||||
import { hassioStyle } from "../../resources/hassio-style";
|
|
||||||
import { haStyle } from "../../../../src/resources/styles";
|
import { haStyle } from "../../../../src/resources/styles";
|
||||||
|
import { HomeAssistant } from "../../../../src/types";
|
||||||
|
import { hassioStyle } from "../../resources/hassio-style";
|
||||||
import "./hassio-addon-audio";
|
import "./hassio-addon-audio";
|
||||||
import "./hassio-addon-config";
|
import "./hassio-addon-config";
|
||||||
import "./hassio-addon-network";
|
import "./hassio-addon-network";
|
||||||
|
@@ -1,6 +1,5 @@
|
|||||||
import "@material/mwc-button";
|
import "@material/mwc-button";
|
||||||
import "@polymer/iron-autogrow-textarea/iron-autogrow-textarea";
|
import "@polymer/iron-autogrow-textarea/iron-autogrow-textarea";
|
||||||
import "@polymer/paper-card/paper-card";
|
|
||||||
import {
|
import {
|
||||||
css,
|
css,
|
||||||
CSSResult,
|
CSSResult,
|
||||||
@@ -13,6 +12,7 @@ import {
|
|||||||
TemplateResult,
|
TemplateResult,
|
||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
||||||
|
import "../../../../src/components/ha-card";
|
||||||
import "../../../../src/components/ha-yaml-editor";
|
import "../../../../src/components/ha-yaml-editor";
|
||||||
import type { HaYamlEditor } from "../../../../src/components/ha-yaml-editor";
|
import type { HaYamlEditor } from "../../../../src/components/ha-yaml-editor";
|
||||||
import {
|
import {
|
||||||
@@ -23,9 +23,8 @@ import {
|
|||||||
import { showConfirmationDialog } from "../../../../src/dialogs/generic/show-dialog-box";
|
import { showConfirmationDialog } from "../../../../src/dialogs/generic/show-dialog-box";
|
||||||
import { haStyle } from "../../../../src/resources/styles";
|
import { haStyle } from "../../../../src/resources/styles";
|
||||||
import type { HomeAssistant } from "../../../../src/types";
|
import type { HomeAssistant } from "../../../../src/types";
|
||||||
import { hassioStyle } from "../../resources/hassio-style";
|
|
||||||
|
|
||||||
import { suggestAddonRestart } from "../../dialogs/suggestAddonRestart";
|
import { suggestAddonRestart } from "../../dialogs/suggestAddonRestart";
|
||||||
|
import { hassioStyle } from "../../resources/hassio-style";
|
||||||
|
|
||||||
@customElement("hassio-addon-config")
|
@customElement("hassio-addon-config")
|
||||||
class HassioAddonConfig extends LitElement {
|
class HassioAddonConfig extends LitElement {
|
||||||
@@ -46,7 +45,7 @@ class HassioAddonConfig extends LitElement {
|
|||||||
|
|
||||||
return html`
|
return html`
|
||||||
<h1>${this.addon.name}</h1>
|
<h1>${this.addon.name}</h1>
|
||||||
<paper-card heading="Configuration">
|
<ha-card header="Configuration">
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<ha-yaml-editor
|
<ha-yaml-editor
|
||||||
@value-changed=${this._configChanged}
|
@value-changed=${this._configChanged}
|
||||||
@@ -65,7 +64,7 @@ class HassioAddonConfig extends LitElement {
|
|||||||
Save
|
Save
|
||||||
</mwc-button>
|
</mwc-button>
|
||||||
</div>
|
</div>
|
||||||
</paper-card>
|
</ha-card>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,7 +76,7 @@ class HassioAddonConfig extends LitElement {
|
|||||||
:host {
|
:host {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
paper-card {
|
ha-card {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
.card-actions {
|
.card-actions {
|
||||||
|
@@ -1,4 +1,3 @@
|
|||||||
import "@polymer/paper-card/paper-card";
|
|
||||||
import { PaperInputElement } from "@polymer/paper-input/paper-input";
|
import { PaperInputElement } from "@polymer/paper-input/paper-input";
|
||||||
import {
|
import {
|
||||||
css,
|
css,
|
||||||
@@ -11,15 +10,15 @@ import {
|
|||||||
TemplateResult,
|
TemplateResult,
|
||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
||||||
|
import "../../../../src/components/ha-card";
|
||||||
import {
|
import {
|
||||||
HassioAddonDetails,
|
HassioAddonDetails,
|
||||||
HassioAddonSetOptionParams,
|
HassioAddonSetOptionParams,
|
||||||
setHassioAddonOption,
|
setHassioAddonOption,
|
||||||
} from "../../../../src/data/hassio/addon";
|
} from "../../../../src/data/hassio/addon";
|
||||||
import { suggestAddonRestart } from "../../dialogs/suggestAddonRestart";
|
|
||||||
|
|
||||||
import { haStyle } from "../../../../src/resources/styles";
|
import { haStyle } from "../../../../src/resources/styles";
|
||||||
import { HomeAssistant } from "../../../../src/types";
|
import { HomeAssistant } from "../../../../src/types";
|
||||||
|
import { suggestAddonRestart } from "../../dialogs/suggestAddonRestart";
|
||||||
import { hassioStyle } from "../../resources/hassio-style";
|
import { hassioStyle } from "../../resources/hassio-style";
|
||||||
|
|
||||||
interface NetworkItem {
|
interface NetworkItem {
|
||||||
@@ -53,7 +52,7 @@ class HassioAddonNetwork extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<paper-card heading="Network">
|
<ha-card header="Network">
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
${this._error ? html` <div class="errors">${this._error}</div> ` : ""}
|
${this._error ? html` <div class="errors">${this._error}</div> ` : ""}
|
||||||
|
|
||||||
@@ -90,7 +89,7 @@ class HassioAddonNetwork extends LitElement {
|
|||||||
</mwc-button>
|
</mwc-button>
|
||||||
<mwc-button @click=${this._saveTapped}>Save</mwc-button>
|
<mwc-button @click=${this._saveTapped}>Save</mwc-button>
|
||||||
</div>
|
</div>
|
||||||
</paper-card>
|
</ha-card>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,7 +101,7 @@ class HassioAddonNetwork extends LitElement {
|
|||||||
:host {
|
:host {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
paper-card {
|
ha-card {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
.errors {
|
.errors {
|
||||||
|
@@ -1,5 +1,4 @@
|
|||||||
import "@polymer/paper-spinner/paper-spinner-lite";
|
import "@polymer/paper-spinner/paper-spinner-lite";
|
||||||
import "@polymer/paper-card/paper-card";
|
|
||||||
import {
|
import {
|
||||||
css,
|
css,
|
||||||
CSSResult,
|
CSSResult,
|
||||||
@@ -9,16 +8,15 @@ import {
|
|||||||
property,
|
property,
|
||||||
TemplateResult,
|
TemplateResult,
|
||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
|
|
||||||
import { HomeAssistant } from "../../../../src/types";
|
|
||||||
import {
|
|
||||||
HassioAddonDetails,
|
|
||||||
fetchHassioAddonDocumentation,
|
|
||||||
} from "../../../../src/data/hassio/addon";
|
|
||||||
import "../../../../src/components/ha-markdown";
|
import "../../../../src/components/ha-markdown";
|
||||||
|
import {
|
||||||
|
fetchHassioAddonDocumentation,
|
||||||
|
HassioAddonDetails,
|
||||||
|
} from "../../../../src/data/hassio/addon";
|
||||||
import "../../../../src/layouts/loading-screen";
|
import "../../../../src/layouts/loading-screen";
|
||||||
import { hassioStyle } from "../../resources/hassio-style";
|
|
||||||
import { haStyle } from "../../../../src/resources/styles";
|
import { haStyle } from "../../../../src/resources/styles";
|
||||||
|
import { HomeAssistant } from "../../../../src/types";
|
||||||
|
import { hassioStyle } from "../../resources/hassio-style";
|
||||||
|
|
||||||
@customElement("hassio-addon-documentation-tab")
|
@customElement("hassio-addon-documentation-tab")
|
||||||
class HassioAddonDocumentationDashboard extends LitElement {
|
class HassioAddonDocumentationDashboard extends LitElement {
|
||||||
@@ -41,14 +39,14 @@ class HassioAddonDocumentationDashboard extends LitElement {
|
|||||||
}
|
}
|
||||||
return html`
|
return html`
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<paper-card>
|
<ha-card>
|
||||||
${this._error ? html` <div class="errors">${this._error}</div> ` : ""}
|
${this._error ? html` <div class="errors">${this._error}</div> ` : ""}
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
${this._content
|
${this._content
|
||||||
? html`<ha-markdown .content=${this._content}></ha-markdown>`
|
? html`<ha-markdown .content=${this._content}></ha-markdown>`
|
||||||
: html`<loading-screen></loading-screen>`}
|
: html`<loading-screen></loading-screen>`}
|
||||||
</div>
|
</div>
|
||||||
</paper-card>
|
</ha-card>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
@@ -58,7 +56,7 @@ class HassioAddonDocumentationDashboard extends LitElement {
|
|||||||
haStyle,
|
haStyle,
|
||||||
hassioStyle,
|
hassioStyle,
|
||||||
css`
|
css`
|
||||||
paper-card {
|
ha-card {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
.content {
|
.content {
|
||||||
|
@@ -1,4 +1,9 @@
|
|||||||
import "../../../src/components/ha-icon-button";
|
import {
|
||||||
|
mdiCogs,
|
||||||
|
mdiFileDocument,
|
||||||
|
mdiInformationVariant,
|
||||||
|
mdiMathLog,
|
||||||
|
} from "@mdi/js";
|
||||||
import "@polymer/paper-spinner/paper-spinner-lite";
|
import "@polymer/paper-spinner/paper-spinner-lite";
|
||||||
import {
|
import {
|
||||||
css,
|
css,
|
||||||
@@ -14,18 +19,17 @@ import {
|
|||||||
fetchHassioAddonInfo,
|
fetchHassioAddonInfo,
|
||||||
HassioAddonDetails,
|
HassioAddonDetails,
|
||||||
} from "../../../src/data/hassio/addon";
|
} from "../../../src/data/hassio/addon";
|
||||||
|
import "../../../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";
|
||||||
import { hassioStyle } from "../resources/hassio-style";
|
import { hassioStyle } from "../resources/hassio-style";
|
||||||
import "./config/hassio-addon-audio";
|
import "./config/hassio-addon-audio";
|
||||||
import "./config/hassio-addon-config";
|
import "./config/hassio-addon-config";
|
||||||
|
import "./config/hassio-addon-network";
|
||||||
|
import "./hassio-addon-router";
|
||||||
import "./info/hassio-addon-info";
|
import "./info/hassio-addon-info";
|
||||||
import "./log/hassio-addon-logs";
|
import "./log/hassio-addon-logs";
|
||||||
import "./config/hassio-addon-network";
|
|
||||||
import type { PageNavigation } from "../../../src/layouts/hass-tabs-subpage";
|
|
||||||
import "../../../src/layouts/hass-tabs-subpage";
|
|
||||||
|
|
||||||
import "./hassio-addon-router";
|
|
||||||
|
|
||||||
@customElement("hassio-addon-dashboard")
|
@customElement("hassio-addon-dashboard")
|
||||||
class HassioAddonDashboard extends LitElement {
|
class HassioAddonDashboard extends LitElement {
|
||||||
@@ -59,7 +63,7 @@ class HassioAddonDashboard extends LitElement {
|
|||||||
{
|
{
|
||||||
name: "Info",
|
name: "Info",
|
||||||
path: `/hassio/addon/${this.addon.slug}/info`,
|
path: `/hassio/addon/${this.addon.slug}/info`,
|
||||||
icon: "hassio:information-variant",
|
iconPath: mdiInformationVariant,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -67,7 +71,7 @@ class HassioAddonDashboard extends LitElement {
|
|||||||
addonTabs.push({
|
addonTabs.push({
|
||||||
name: "Documentation",
|
name: "Documentation",
|
||||||
path: `/hassio/addon/${this.addon.slug}/documentation`,
|
path: `/hassio/addon/${this.addon.slug}/documentation`,
|
||||||
icon: "hassio:file-document",
|
iconPath: mdiFileDocument,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,12 +80,12 @@ class HassioAddonDashboard extends LitElement {
|
|||||||
{
|
{
|
||||||
name: "Configuration",
|
name: "Configuration",
|
||||||
path: `/hassio/addon/${this.addon.slug}/config`,
|
path: `/hassio/addon/${this.addon.slug}/config`,
|
||||||
icon: "hassio:cogs",
|
iconPath: mdiCogs,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Log",
|
name: "Log",
|
||||||
path: `/hassio/addon/${this.addon.slug}/logs`,
|
path: `/hassio/addon/${this.addon.slug}/logs`,
|
||||||
icon: "hassio:math-log",
|
iconPath: mdiMathLog,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -115,7 +119,6 @@ class HassioAddonDashboard extends LitElement {
|
|||||||
css`
|
css`
|
||||||
:host {
|
:host {
|
||||||
color: var(--primary-text-color);
|
color: var(--primary-text-color);
|
||||||
--paper-card-header-color: var(--primary-text-color);
|
|
||||||
}
|
}
|
||||||
.content {
|
.content {
|
||||||
padding: 24px 0 32px;
|
padding: 24px 0 32px;
|
||||||
|
@@ -1,15 +1,15 @@
|
|||||||
|
import { customElement, property } from "lit-element";
|
||||||
|
import { HassioAddonDetails } from "../../../src/data/hassio/addon";
|
||||||
import {
|
import {
|
||||||
HassRouterPage,
|
HassRouterPage,
|
||||||
RouterOptions,
|
RouterOptions,
|
||||||
} from "../../../src/layouts/hass-router-page";
|
} from "../../../src/layouts/hass-router-page";
|
||||||
import { customElement, property } from "lit-element";
|
|
||||||
import { HomeAssistant } from "../../../src/types";
|
import { HomeAssistant } from "../../../src/types";
|
||||||
|
import "./config/hassio-addon-config-tab";
|
||||||
|
import "./documentation/hassio-addon-documentation-tab";
|
||||||
// Don't codesplit the others, because it breaks the UI when pushed to a Pi
|
// Don't codesplit the others, because it breaks the UI when pushed to a Pi
|
||||||
import "./info/hassio-addon-info-tab";
|
import "./info/hassio-addon-info-tab";
|
||||||
import "./config/hassio-addon-config-tab";
|
|
||||||
import "./log/hassio-addon-log-tab";
|
import "./log/hassio-addon-log-tab";
|
||||||
import "./documentation/hassio-addon-documentation-tab";
|
|
||||||
import { HassioAddonDetails } from "../../../src/data/hassio/addon";
|
|
||||||
|
|
||||||
@customElement("hassio-addon-router")
|
@customElement("hassio-addon-router")
|
||||||
class HassioAddonRouter extends HassRouterPage {
|
class HassioAddonRouter extends HassRouterPage {
|
||||||
|
@@ -8,12 +8,10 @@ import {
|
|||||||
property,
|
property,
|
||||||
TemplateResult,
|
TemplateResult,
|
||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
|
|
||||||
import { HomeAssistant } from "../../../../src/types";
|
|
||||||
import { HassioAddonDetails } from "../../../../src/data/hassio/addon";
|
import { HassioAddonDetails } from "../../../../src/data/hassio/addon";
|
||||||
import { hassioStyle } from "../../resources/hassio-style";
|
|
||||||
import { haStyle } from "../../../../src/resources/styles";
|
import { haStyle } from "../../../../src/resources/styles";
|
||||||
|
import { HomeAssistant } from "../../../../src/types";
|
||||||
|
import { hassioStyle } from "../../resources/hassio-style";
|
||||||
import "./hassio-addon-info";
|
import "./hassio-addon-info";
|
||||||
|
|
||||||
@customElement("hassio-addon-info-tab")
|
@customElement("hassio-addon-info-tab")
|
||||||
|
@@ -1,5 +1,20 @@
|
|||||||
import "@material/mwc-button";
|
import "@material/mwc-button";
|
||||||
import "@polymer/paper-card/paper-card";
|
import {
|
||||||
|
mdiArrowUpBoldCircle,
|
||||||
|
mdiCheckCircle,
|
||||||
|
mdiChip,
|
||||||
|
mdiCircle,
|
||||||
|
mdiCursorDefaultClickOutline,
|
||||||
|
mdiDocker,
|
||||||
|
mdiExclamationThick,
|
||||||
|
mdiFlask,
|
||||||
|
mdiHomeAssistant,
|
||||||
|
mdiInformation,
|
||||||
|
mdiKey,
|
||||||
|
mdiNetwork,
|
||||||
|
mdiPound,
|
||||||
|
mdiShield,
|
||||||
|
} from "@mdi/js";
|
||||||
import "@polymer/paper-tooltip/paper-tooltip";
|
import "@polymer/paper-tooltip/paper-tooltip";
|
||||||
import {
|
import {
|
||||||
css,
|
css,
|
||||||
@@ -16,10 +31,11 @@ import { fireEvent } from "../../../../src/common/dom/fire_event";
|
|||||||
import { navigate } from "../../../../src/common/navigate";
|
import { navigate } from "../../../../src/common/navigate";
|
||||||
import "../../../../src/components/buttons/ha-call-api-button";
|
import "../../../../src/components/buttons/ha-call-api-button";
|
||||||
import "../../../../src/components/buttons/ha-progress-button";
|
import "../../../../src/components/buttons/ha-progress-button";
|
||||||
|
import "../../../../src/components/ha-card";
|
||||||
import "../../../../src/components/ha-label-badge";
|
import "../../../../src/components/ha-label-badge";
|
||||||
import "../../../../src/components/ha-markdown";
|
import "../../../../src/components/ha-markdown";
|
||||||
|
import "../../../../src/components/ha-svg-icon";
|
||||||
import "../../../../src/components/ha-switch";
|
import "../../../../src/components/ha-switch";
|
||||||
import "../../../../src/components/ha-icon";
|
|
||||||
import {
|
import {
|
||||||
fetchHassioAddonChangelog,
|
fetchHassioAddonChangelog,
|
||||||
HassioAddonDetails,
|
HassioAddonDetails,
|
||||||
@@ -30,23 +46,23 @@ import {
|
|||||||
setHassioAddonSecurity,
|
setHassioAddonSecurity,
|
||||||
uninstallHassioAddon,
|
uninstallHassioAddon,
|
||||||
} from "../../../../src/data/hassio/addon";
|
} from "../../../../src/data/hassio/addon";
|
||||||
|
import { showConfirmationDialog } from "../../../../src/dialogs/generic/show-dialog-box";
|
||||||
import { haStyle } from "../../../../src/resources/styles";
|
import { haStyle } from "../../../../src/resources/styles";
|
||||||
import { HomeAssistant } from "../../../../src/types";
|
import { HomeAssistant } from "../../../../src/types";
|
||||||
import "../../components/hassio-card-content";
|
import "../../components/hassio-card-content";
|
||||||
import { showHassioMarkdownDialog } from "../../dialogs/markdown/show-dialog-hassio-markdown";
|
import { showHassioMarkdownDialog } from "../../dialogs/markdown/show-dialog-hassio-markdown";
|
||||||
import { hassioStyle } from "../../resources/hassio-style";
|
import { hassioStyle } from "../../resources/hassio-style";
|
||||||
import { showConfirmationDialog } from "../../../../src/dialogs/generic/show-dialog-box";
|
|
||||||
|
|
||||||
const STAGE_ICON = {
|
const STAGE_ICON = {
|
||||||
stable: "mdi:check-circle",
|
stable: mdiCheckCircle,
|
||||||
experimental: "mdi:flask",
|
experimental: mdiFlask,
|
||||||
deprecated: "mdi:exclamation-thick",
|
deprecated: mdiExclamationThick,
|
||||||
};
|
};
|
||||||
|
|
||||||
const PERMIS_DESC = {
|
const PERMIS_DESC = {
|
||||||
stage: {
|
stage: {
|
||||||
title: "Add-on Stage",
|
title: "Add-on Stage",
|
||||||
description: `Add-ons can have one of three stages:\n\n<ha-icon icon='${STAGE_ICON.stable}'></ha-icon>**Stable**: These are add-ons ready to be used in production.\n<ha-icon icon='${STAGE_ICON.experimental}'></ha-icon>**Experimental**: These may contain bugs, and may be unfinished.\n<ha-icon icon='${STAGE_ICON.deprecated}'></ha-icon>**Deprecated**: These add-ons will no longer receive any updates.`,
|
description: `Add-ons can have one of three stages:\n\n<ha-svg-icon path='${STAGE_ICON.stable}'></ha-svg-icon> **Stable**: These are add-ons ready to be used in production.\n\n<ha-svg-icon path='${STAGE_ICON.experimental}'></ha-svg-icon> **Experimental**: These may contain bugs, and may be unfinished.\n\n<ha-svg-icon path='${STAGE_ICON.deprecated}'></ha-svg-icon> **Deprecated**: These add-ons will no longer receive any updates.`,
|
||||||
},
|
},
|
||||||
rating: {
|
rating: {
|
||||||
title: "Add-on Security Rating",
|
title: "Add-on Security Rating",
|
||||||
@@ -116,7 +132,7 @@ class HassioAddonInfo extends LitElement {
|
|||||||
return html`
|
return html`
|
||||||
${this._computeUpdateAvailable
|
${this._computeUpdateAvailable
|
||||||
? html`
|
? html`
|
||||||
<paper-card heading="Update available! 🎉">
|
<ha-card header="Update available! 🎉">
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<hassio-card-content
|
<hassio-card-content
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
@@ -124,7 +140,7 @@ class HassioAddonInfo extends LitElement {
|
|||||||
.version_latest} is available"
|
.version_latest} is available"
|
||||||
.description="You are currently running version ${this.addon
|
.description="You are currently running version ${this.addon
|
||||||
.version}"
|
.version}"
|
||||||
icon="hassio:arrow-up-bold-circle"
|
icon=${mdiArrowUpBoldCircle}
|
||||||
iconClass="update"
|
iconClass="update"
|
||||||
></hassio-card-content>
|
></hassio-card-content>
|
||||||
${!this.addon.available
|
${!this.addon.available
|
||||||
@@ -151,12 +167,13 @@ class HassioAddonInfo extends LitElement {
|
|||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
</div>
|
</div>
|
||||||
</paper-card>
|
</ha-card>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
${!this.addon.protected
|
${!this.addon.protected
|
||||||
? html`
|
? html`
|
||||||
<paper-card heading="Warning: Protection mode is disabled!" class="warning">
|
<ha-card class="warning">
|
||||||
|
<div class="card-header">Warning: Protection mode is disabled!</div>
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
Protection mode on this add-on is disabled! This gives the add-on full access to the entire system, which adds security risks, and could damage your system when used incorrectly. Only disable the protection mode if you know, need AND trust the source of this add-on.
|
Protection mode on this add-on is disabled! This gives the add-on full access to the entire system, which adds security risks, and could damage your system when used incorrectly. Only disable the protection mode if you know, need AND trust the source of this add-on.
|
||||||
</div>
|
</div>
|
||||||
@@ -164,11 +181,11 @@ class HassioAddonInfo extends LitElement {
|
|||||||
<mwc-button @click=${this._protectionToggled}>Enable Protection mode</mwc-button>
|
<mwc-button @click=${this._protectionToggled}>Enable Protection mode</mwc-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</paper-card>
|
</ha-card>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
|
|
||||||
<paper-card>
|
<ha-card>
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<div class="addon-header">
|
<div class="addon-header">
|
||||||
${!this.narrow ? this.addon.name : ""}
|
${!this.narrow ? this.addon.name : ""}
|
||||||
@@ -177,18 +194,18 @@ class HassioAddonInfo extends LitElement {
|
|||||||
? html`
|
? html`
|
||||||
${this._computeIsRunning
|
${this._computeIsRunning
|
||||||
? html`
|
? html`
|
||||||
<ha-icon
|
<ha-svg-icon
|
||||||
title="Add-on is running"
|
title="Add-on is running"
|
||||||
class="running"
|
class="running"
|
||||||
icon="hassio:circle"
|
path=${mdiCircle}
|
||||||
></ha-icon>
|
></ha-svg-icon>
|
||||||
`
|
`
|
||||||
: html`
|
: html`
|
||||||
<ha-icon
|
<ha-svg-icon
|
||||||
title="Add-on is stopped"
|
title="Add-on is stopped"
|
||||||
class="stopped"
|
class="stopped"
|
||||||
icon="hassio:circle"
|
path=${mdiCircle}
|
||||||
></ha-icon>
|
></ha-svg-icon>
|
||||||
`}
|
`}
|
||||||
`
|
`
|
||||||
: html` ${this.addon.version_latest} `}
|
: html` ${this.addon.version_latest} `}
|
||||||
@@ -232,10 +249,11 @@ class HassioAddonInfo extends LitElement {
|
|||||||
})}
|
})}
|
||||||
@click=${this._showMoreInfo}
|
@click=${this._showMoreInfo}
|
||||||
id="stage"
|
id="stage"
|
||||||
.icon=${STAGE_ICON[this.addon.stage]}
|
|
||||||
label="stage"
|
label="stage"
|
||||||
description=""
|
description=""
|
||||||
></ha-label-badge>
|
>
|
||||||
|
<ha-svg-icon .path=${STAGE_ICON[this.addon.stage]}></ha-svg-icon>
|
||||||
|
</ha-label-badge>
|
||||||
<ha-label-badge
|
<ha-label-badge
|
||||||
class=${classMap({
|
class=${classMap({
|
||||||
green: [5, 6].includes(Number(this.addon.rating)),
|
green: [5, 6].includes(Number(this.addon.rating)),
|
||||||
@@ -253,10 +271,11 @@ class HassioAddonInfo extends LitElement {
|
|||||||
<ha-label-badge
|
<ha-label-badge
|
||||||
@click=${this._showMoreInfo}
|
@click=${this._showMoreInfo}
|
||||||
id="host_network"
|
id="host_network"
|
||||||
icon="hassio:network"
|
|
||||||
label="host"
|
label="host"
|
||||||
description=""
|
description=""
|
||||||
></ha-label-badge>
|
>
|
||||||
|
<ha-svg-icon path=${mdiNetwork}></ha-svg-icon>
|
||||||
|
</ha-label-badge>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
${this.addon.full_access
|
${this.addon.full_access
|
||||||
@@ -264,10 +283,11 @@ class HassioAddonInfo extends LitElement {
|
|||||||
<ha-label-badge
|
<ha-label-badge
|
||||||
@click=${this._showMoreInfo}
|
@click=${this._showMoreInfo}
|
||||||
id="full_access"
|
id="full_access"
|
||||||
icon="hassio:chip"
|
|
||||||
label="hardware"
|
label="hardware"
|
||||||
description=""
|
description=""
|
||||||
></ha-label-badge>
|
>
|
||||||
|
<ha-svg-icon path=${mdiChip}></ha-svg-icon>
|
||||||
|
</ha-label-badge>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
${this.addon.homeassistant_api
|
${this.addon.homeassistant_api
|
||||||
@@ -275,10 +295,11 @@ class HassioAddonInfo extends LitElement {
|
|||||||
<ha-label-badge
|
<ha-label-badge
|
||||||
@click=${this._showMoreInfo}
|
@click=${this._showMoreInfo}
|
||||||
id="homeassistant_api"
|
id="homeassistant_api"
|
||||||
icon="hassio:home-assistant"
|
|
||||||
label="hass"
|
label="hass"
|
||||||
description=""
|
description=""
|
||||||
></ha-label-badge>
|
>
|
||||||
|
<ha-svg-icon path=${mdiHomeAssistant}></ha-svg-icon>
|
||||||
|
</ha-label-badge>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
${this._computeHassioApi
|
${this._computeHassioApi
|
||||||
@@ -286,10 +307,11 @@ class HassioAddonInfo extends LitElement {
|
|||||||
<ha-label-badge
|
<ha-label-badge
|
||||||
@click=${this._showMoreInfo}
|
@click=${this._showMoreInfo}
|
||||||
id="hassio_api"
|
id="hassio_api"
|
||||||
icon="hassio:home-assistant"
|
|
||||||
label="hassio"
|
label="hassio"
|
||||||
.description=${this.addon.hassio_role}
|
.description=${this.addon.hassio_role}
|
||||||
></ha-label-badge>
|
>
|
||||||
|
<ha-svg-icon path=${mdiHomeAssistant}></ha-svg-icon>
|
||||||
|
</ha-label-badge>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
${this.addon.docker_api
|
${this.addon.docker_api
|
||||||
@@ -297,10 +319,11 @@ class HassioAddonInfo extends LitElement {
|
|||||||
<ha-label-badge
|
<ha-label-badge
|
||||||
@click=${this._showMoreInfo}
|
@click=${this._showMoreInfo}
|
||||||
id="docker_api"
|
id="docker_api"
|
||||||
icon="hassio:docker"
|
|
||||||
label="docker"
|
label="docker"
|
||||||
description=""
|
description=""
|
||||||
></ha-label-badge>
|
>
|
||||||
|
<ha-svg-icon path=${mdiDocker}></ha-svg-icon>
|
||||||
|
</ha-label-badge>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
${this.addon.host_pid
|
${this.addon.host_pid
|
||||||
@@ -308,10 +331,11 @@ class HassioAddonInfo extends LitElement {
|
|||||||
<ha-label-badge
|
<ha-label-badge
|
||||||
@click=${this._showMoreInfo}
|
@click=${this._showMoreInfo}
|
||||||
id="host_pid"
|
id="host_pid"
|
||||||
icon="hassio:pound"
|
|
||||||
label="host pid"
|
label="host pid"
|
||||||
description=""
|
description=""
|
||||||
></ha-label-badge>
|
>
|
||||||
|
<ha-svg-icon path=${mdiPound}></ha-svg-icon>
|
||||||
|
</ha-label-badge>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
${this.addon.apparmor
|
${this.addon.apparmor
|
||||||
@@ -320,10 +344,11 @@ class HassioAddonInfo extends LitElement {
|
|||||||
@click=${this._showMoreInfo}
|
@click=${this._showMoreInfo}
|
||||||
class=${this._computeApparmorClassName}
|
class=${this._computeApparmorClassName}
|
||||||
id="apparmor"
|
id="apparmor"
|
||||||
icon="hassio:shield"
|
|
||||||
label="apparmor"
|
label="apparmor"
|
||||||
description=""
|
description=""
|
||||||
></ha-label-badge>
|
>
|
||||||
|
<ha-svg-icon path=${mdiShield}></ha-svg-icon>
|
||||||
|
</ha-label-badge>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
${this.addon.auth_api
|
${this.addon.auth_api
|
||||||
@@ -331,10 +356,11 @@ class HassioAddonInfo extends LitElement {
|
|||||||
<ha-label-badge
|
<ha-label-badge
|
||||||
@click=${this._showMoreInfo}
|
@click=${this._showMoreInfo}
|
||||||
id="auth_api"
|
id="auth_api"
|
||||||
icon="hassio:key"
|
|
||||||
label="auth"
|
label="auth"
|
||||||
description=""
|
description=""
|
||||||
></ha-label-badge>
|
>
|
||||||
|
<ha-svg-icon path=${mdiKey}></ha-svg-icon>
|
||||||
|
</ha-label-badge>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
${this.addon.ingress
|
${this.addon.ingress
|
||||||
@@ -342,10 +368,13 @@ class HassioAddonInfo extends LitElement {
|
|||||||
<ha-label-badge
|
<ha-label-badge
|
||||||
@click=${this._showMoreInfo}
|
@click=${this._showMoreInfo}
|
||||||
id="ingress"
|
id="ingress"
|
||||||
icon="hassio:cursor-default-click-outline"
|
|
||||||
label="ingress"
|
label="ingress"
|
||||||
description=""
|
description=""
|
||||||
></ha-label-badge>
|
>
|
||||||
|
<ha-svg-icon
|
||||||
|
path=${mdiCursorDefaultClickOutline}
|
||||||
|
></ha-svg-icon>
|
||||||
|
</ha-label-badge>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
</div>
|
</div>
|
||||||
@@ -399,7 +428,7 @@ class HassioAddonInfo extends LitElement {
|
|||||||
<div>
|
<div>
|
||||||
Protection mode
|
Protection mode
|
||||||
<span>
|
<span>
|
||||||
<ha-icon icon="hassio:information"></ha-icon>
|
<ha-svg-icon path=${mdiInformation}></ha-svg-icon>
|
||||||
<paper-tooltip>
|
<paper-tooltip>
|
||||||
Grant the add-on elevated system access.
|
Grant the add-on elevated system access.
|
||||||
</paper-tooltip>
|
</paper-tooltip>
|
||||||
@@ -502,17 +531,17 @@ class HassioAddonInfo extends LitElement {
|
|||||||
</ha-progress-button>
|
</ha-progress-button>
|
||||||
`}
|
`}
|
||||||
</div>
|
</div>
|
||||||
</paper-card>
|
</ha-card>
|
||||||
|
|
||||||
${this.addon.long_description
|
${this.addon.long_description
|
||||||
? html`
|
? html`
|
||||||
<paper-card>
|
<ha-card>
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<ha-markdown
|
<ha-markdown
|
||||||
.content=${this.addon.long_description}
|
.content=${this.addon.long_description}
|
||||||
></ha-markdown>
|
></ha-markdown>
|
||||||
</div>
|
</div>
|
||||||
</paper-card>
|
</ha-card>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
`;
|
`;
|
||||||
@@ -526,16 +555,21 @@ class HassioAddonInfo extends LitElement {
|
|||||||
:host {
|
:host {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
paper-card {
|
ha-card {
|
||||||
display: block;
|
display: block;
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
}
|
}
|
||||||
paper-card.warning {
|
ha-card.warning {
|
||||||
background-color: var(--google-red-500);
|
background-color: var(--google-red-500);
|
||||||
color: white;
|
color: white;
|
||||||
--paper-card-header-color: white;
|
|
||||||
}
|
}
|
||||||
paper-card.warning mwc-button {
|
ha-card.warning .card-header {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
ha-card.warning .card-content {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
ha-card.warning mwc-button {
|
||||||
--mdc-theme-primary: white !important;
|
--mdc-theme-primary: white !important;
|
||||||
}
|
}
|
||||||
.warning {
|
.warning {
|
||||||
@@ -548,7 +582,7 @@ class HassioAddonInfo extends LitElement {
|
|||||||
.addon-header {
|
.addon-header {
|
||||||
padding-left: 8px;
|
padding-left: 8px;
|
||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
color: var(--paper-card-header-color, --primary-text-color);
|
color: var(--ha-card-header-color, --primary-text-color);
|
||||||
}
|
}
|
||||||
.addon-version {
|
.addon-version {
|
||||||
float: right;
|
float: right;
|
||||||
@@ -575,7 +609,7 @@ class HassioAddonInfo extends LitElement {
|
|||||||
width: 180px;
|
width: 180px;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
.state ha-icon {
|
.state ha-svg-icon {
|
||||||
width: 16px;
|
width: 16px;
|
||||||
height: 16px;
|
height: 16px;
|
||||||
color: var(--secondary-text-color);
|
color: var(--secondary-text-color);
|
||||||
@@ -583,10 +617,10 @@ class HassioAddonInfo extends LitElement {
|
|||||||
ha-switch {
|
ha-switch {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
ha-icon.running {
|
ha-svg-icon.running {
|
||||||
color: var(--paper-green-400);
|
color: var(--paper-green-400);
|
||||||
}
|
}
|
||||||
ha-icon.stopped {
|
ha-svg-icon.stopped {
|
||||||
color: var(--google-red-300);
|
color: var(--google-red-300);
|
||||||
}
|
}
|
||||||
ha-call-api-button {
|
ha-call-api-button {
|
||||||
@@ -664,7 +698,7 @@ class HassioAddonInfo extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _showMoreInfo(ev): void {
|
private _showMoreInfo(ev): void {
|
||||||
const id = ev.target.getAttribute("id");
|
const id = ev.currentTarget.id;
|
||||||
showHassioMarkdownDialog(this, {
|
showHassioMarkdownDialog(this, {
|
||||||
title: PERMIS_DESC[id].title,
|
title: PERMIS_DESC[id].title,
|
||||||
content: PERMIS_DESC[id].description,
|
content: PERMIS_DESC[id].description,
|
||||||
|
@@ -1,5 +1,4 @@
|
|||||||
import "@polymer/paper-spinner/paper-spinner-lite";
|
import "@polymer/paper-spinner/paper-spinner-lite";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
css,
|
css,
|
||||||
CSSResult,
|
CSSResult,
|
||||||
@@ -9,12 +8,10 @@ import {
|
|||||||
property,
|
property,
|
||||||
TemplateResult,
|
TemplateResult,
|
||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
|
|
||||||
import { HomeAssistant } from "../../../../src/types";
|
|
||||||
import { HassioAddonDetails } from "../../../../src/data/hassio/addon";
|
import { HassioAddonDetails } from "../../../../src/data/hassio/addon";
|
||||||
import { hassioStyle } from "../../resources/hassio-style";
|
|
||||||
import { haStyle } from "../../../../src/resources/styles";
|
import { haStyle } from "../../../../src/resources/styles";
|
||||||
|
import { HomeAssistant } from "../../../../src/types";
|
||||||
|
import { hassioStyle } from "../../resources/hassio-style";
|
||||||
import "./hassio-addon-logs";
|
import "./hassio-addon-logs";
|
||||||
|
|
||||||
@customElement("hassio-addon-log-tab")
|
@customElement("hassio-addon-log-tab")
|
||||||
|
@@ -1,5 +1,4 @@
|
|||||||
import "@material/mwc-button";
|
import "@material/mwc-button";
|
||||||
import "@polymer/paper-card/paper-card";
|
|
||||||
import {
|
import {
|
||||||
css,
|
css,
|
||||||
CSSResult,
|
CSSResult,
|
||||||
@@ -9,6 +8,7 @@ import {
|
|||||||
property,
|
property,
|
||||||
TemplateResult,
|
TemplateResult,
|
||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
|
import "../../../../src/components/ha-card";
|
||||||
import {
|
import {
|
||||||
fetchHassioAddonLogs,
|
fetchHassioAddonLogs,
|
||||||
HassioAddonDetails,
|
HassioAddonDetails,
|
||||||
@@ -36,7 +36,7 @@ class HassioAddonLogs extends LitElement {
|
|||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
return html`
|
return html`
|
||||||
<h1>${this.addon.name}</h1>
|
<h1>${this.addon.name}</h1>
|
||||||
<paper-card>
|
<ha-card>
|
||||||
${this._error ? html` <div class="errors">${this._error}</div> ` : ""}
|
${this._error ? html` <div class="errors">${this._error}</div> ` : ""}
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
${this._content
|
${this._content
|
||||||
@@ -48,7 +48,7 @@ class HassioAddonLogs extends LitElement {
|
|||||||
<div class="card-actions">
|
<div class="card-actions">
|
||||||
<mwc-button @click=${this._refresh}>Refresh</mwc-button>
|
<mwc-button @click=${this._refresh}>Refresh</mwc-button>
|
||||||
</div>
|
</div>
|
||||||
</paper-card>
|
</ha-card>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,7 +58,7 @@ class HassioAddonLogs extends LitElement {
|
|||||||
hassioStyle,
|
hassioStyle,
|
||||||
css`
|
css`
|
||||||
:host,
|
:host,
|
||||||
paper-card {
|
ha-card {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
.errors {
|
.errors {
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
import { mdiHelpCircle } from "@mdi/js";
|
||||||
import {
|
import {
|
||||||
css,
|
css,
|
||||||
CSSResult,
|
CSSResult,
|
||||||
@@ -8,7 +9,7 @@ import {
|
|||||||
TemplateResult,
|
TemplateResult,
|
||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
import "../../../src/components/ha-relative-time";
|
import "../../../src/components/ha-relative-time";
|
||||||
import "../../../src/components/ha-icon";
|
import "../../../src/components/ha-svg-icon";
|
||||||
import { HomeAssistant } from "../../../src/types";
|
import { HomeAssistant } from "../../../src/types";
|
||||||
|
|
||||||
@customElement("hassio-card-content")
|
@customElement("hassio-card-content")
|
||||||
@@ -31,7 +32,7 @@ class HassioCardContent extends LitElement {
|
|||||||
|
|
||||||
@property() public iconClass?: string;
|
@property() public iconClass?: string;
|
||||||
|
|
||||||
@property() public icon = "hass:help-circle";
|
@property() public icon = mdiHelpCircle;
|
||||||
|
|
||||||
@property() public iconImage?: string;
|
@property() public iconImage?: string;
|
||||||
|
|
||||||
@@ -48,11 +49,11 @@ class HassioCardContent extends LitElement {
|
|||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
: html`
|
: html`
|
||||||
<ha-icon
|
<ha-svg-icon
|
||||||
class=${this.iconClass}
|
class=${this.iconClass}
|
||||||
.icon=${this.icon}
|
.path=${this.icon}
|
||||||
.title=${this.iconTitle}
|
.title=${this.iconTitle}
|
||||||
></ha-icon>
|
></ha-svg-icon>
|
||||||
`}
|
`}
|
||||||
<div>
|
<div>
|
||||||
<div class="title">
|
<div class="title">
|
||||||
@@ -78,25 +79,25 @@ class HassioCardContent extends LitElement {
|
|||||||
|
|
||||||
static get styles(): CSSResult {
|
static get styles(): CSSResult {
|
||||||
return css`
|
return css`
|
||||||
ha-icon {
|
ha-svg-icon {
|
||||||
margin-right: 24px;
|
margin-right: 24px;
|
||||||
margin-left: 8px;
|
margin-left: 8px;
|
||||||
margin-top: 12px;
|
margin-top: 12px;
|
||||||
float: left;
|
float: left;
|
||||||
color: var(--secondary-text-color);
|
color: var(--secondary-text-color);
|
||||||
}
|
}
|
||||||
ha-icon.update {
|
ha-svg-icon.update {
|
||||||
color: var(--paper-orange-400);
|
color: var(--paper-orange-400);
|
||||||
}
|
}
|
||||||
ha-icon.running,
|
ha-svg-icon.running,
|
||||||
ha-icon.installed {
|
ha-svg-icon.installed {
|
||||||
color: var(--paper-green-400);
|
color: var(--paper-green-400);
|
||||||
}
|
}
|
||||||
ha-icon.hassupdate,
|
ha-svg-icon.hassupdate,
|
||||||
ha-icon.snapshot {
|
ha-svg-icon.snapshot {
|
||||||
color: var(--paper-item-icon-color);
|
color: var(--paper-item-icon-color);
|
||||||
}
|
}
|
||||||
ha-icon.not_available {
|
ha-svg-icon.not_available {
|
||||||
color: var(--google-red-500);
|
color: var(--google-red-500);
|
||||||
}
|
}
|
||||||
.title {
|
.title {
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import "@polymer/paper-card/paper-card";
|
import { mdiArrowUpBoldCircle, mdiPuzzle } from "@mdi/js";
|
||||||
import {
|
import {
|
||||||
css,
|
css,
|
||||||
CSSResult,
|
CSSResult,
|
||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
import { atLeastVersion } from "../../../src/common/config/version";
|
import { atLeastVersion } from "../../../src/common/config/version";
|
||||||
import { navigate } from "../../../src/common/navigate";
|
import { navigate } from "../../../src/common/navigate";
|
||||||
|
import "../../../src/components/ha-card";
|
||||||
import { HassioAddonInfo } from "../../../src/data/hassio/addon";
|
import { HassioAddonInfo } from "../../../src/data/hassio/addon";
|
||||||
import { haStyle } from "../../../src/resources/styles";
|
import { haStyle } from "../../../src/resources/styles";
|
||||||
import { HomeAssistant } from "../../../src/types";
|
import { HomeAssistant } from "../../../src/types";
|
||||||
@@ -29,19 +30,19 @@ class HassioAddons extends LitElement {
|
|||||||
<div class="card-group">
|
<div class="card-group">
|
||||||
${!this.addons
|
${!this.addons
|
||||||
? html`
|
? html`
|
||||||
<paper-card>
|
<ha-card>
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
You don't have any add-ons installed yet. Head over to
|
You don't have any add-ons installed yet. Head over to
|
||||||
<a href="#" @click=${this._openStore}>the add-on store</a>
|
<a href="#" @click=${this._openStore}>the add-on store</a>
|
||||||
to get started!
|
to get started!
|
||||||
</div>
|
</div>
|
||||||
</paper-card>
|
</ha-card>
|
||||||
`
|
`
|
||||||
: this.addons
|
: this.addons
|
||||||
.sort((a, b) => (a.name > b.name ? 1 : -1))
|
.sort((a, b) => (a.name > b.name ? 1 : -1))
|
||||||
.map(
|
.map(
|
||||||
(addon) => html`
|
(addon) => html`
|
||||||
<paper-card .addon=${addon} @click=${this._addonTapped}>
|
<ha-card .addon=${addon} @click=${this._addonTapped}>
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<hassio-card-content
|
<hassio-card-content
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
@@ -51,8 +52,8 @@ class HassioAddons extends LitElement {
|
|||||||
.showTopbar=${addon.installed !== addon.version}
|
.showTopbar=${addon.installed !== addon.version}
|
||||||
topbarClass="update"
|
topbarClass="update"
|
||||||
.icon=${addon.installed !== addon.version
|
.icon=${addon.installed !== addon.version
|
||||||
? "hassio:arrow-up-bold-circle"
|
? mdiArrowUpBoldCircle
|
||||||
: "hassio:puzzle"}
|
: mdiPuzzle}
|
||||||
.iconTitle=${addon.state !== "started"
|
.iconTitle=${addon.state !== "started"
|
||||||
? "Add-on is stopped"
|
? "Add-on is stopped"
|
||||||
: addon.installed !== addon.version
|
: addon.installed !== addon.version
|
||||||
@@ -75,7 +76,7 @@ class HassioAddons extends LitElement {
|
|||||||
: undefined}
|
: undefined}
|
||||||
></hassio-card-content>
|
></hassio-card-content>
|
||||||
</div>
|
</div>
|
||||||
</paper-card>
|
</ha-card>
|
||||||
`
|
`
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -88,7 +89,7 @@ class HassioAddons extends LitElement {
|
|||||||
haStyle,
|
haStyle,
|
||||||
hassioStyle,
|
hassioStyle,
|
||||||
css`
|
css`
|
||||||
paper-card {
|
ha-card {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
|
@@ -12,14 +12,13 @@ import {
|
|||||||
HassioHomeAssistantInfo,
|
HassioHomeAssistantInfo,
|
||||||
HassioSupervisorInfo,
|
HassioSupervisorInfo,
|
||||||
} from "../../../src/data/hassio/supervisor";
|
} from "../../../src/data/hassio/supervisor";
|
||||||
|
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 "../../../src/layouts/hass-tabs-subpage";
|
import { supervisorTabs } from "../hassio-panel";
|
||||||
import "./hassio-addons";
|
import "./hassio-addons";
|
||||||
import "./hassio-update";
|
import "./hassio-update";
|
||||||
|
|
||||||
import { supervisorTabs } from "../hassio-panel";
|
|
||||||
|
|
||||||
@customElement("hassio-dashboard")
|
@customElement("hassio-dashboard")
|
||||||
class HassioDashboard extends LitElement {
|
class HassioDashboard extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import "@material/mwc-button";
|
import "@material/mwc-button";
|
||||||
import "@polymer/paper-card/paper-card";
|
import { mdiHomeAssistant } from "@mdi/js";
|
||||||
import {
|
import {
|
||||||
css,
|
css,
|
||||||
CSSResult,
|
CSSResult,
|
||||||
@@ -10,15 +10,15 @@ import {
|
|||||||
TemplateResult,
|
TemplateResult,
|
||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
import "../../../src/components/buttons/ha-call-api-button";
|
import "../../../src/components/buttons/ha-call-api-button";
|
||||||
|
import "../../../src/components/ha-card";
|
||||||
|
import "../../../src/components/ha-svg-icon";
|
||||||
import { HassioHassOSInfo } from "../../../src/data/hassio/host";
|
import { HassioHassOSInfo } from "../../../src/data/hassio/host";
|
||||||
import {
|
import {
|
||||||
HassioHomeAssistantInfo,
|
HassioHomeAssistantInfo,
|
||||||
HassioSupervisorInfo,
|
HassioSupervisorInfo,
|
||||||
} from "../../../src/data/hassio/supervisor";
|
} from "../../../src/data/hassio/supervisor";
|
||||||
import { haStyle } from "../../../src/resources/styles";
|
import { haStyle } from "../../../src/resources/styles";
|
||||||
import "../../../src/components/ha-icon";
|
|
||||||
import { HomeAssistant } from "../../../src/types";
|
import { HomeAssistant } from "../../../src/types";
|
||||||
import "../components/hassio-card-content";
|
|
||||||
import { hassioStyle } from "../resources/hassio-style";
|
import { hassioStyle } from "../resources/hassio-style";
|
||||||
|
|
||||||
@customElement("hassio-update")
|
@customElement("hassio-update")
|
||||||
@@ -72,7 +72,7 @@ export class HassioUpdate extends LitElement {
|
|||||||
`https://${
|
`https://${
|
||||||
this.hassInfo.version_latest.includes("b") ? "rc" : "www"
|
this.hassInfo.version_latest.includes("b") ? "rc" : "www"
|
||||||
}.home-assistant.io/latest-release-notes/`,
|
}.home-assistant.io/latest-release-notes/`,
|
||||||
"hassio:home-assistant"
|
mdiHomeAssistant
|
||||||
)}
|
)}
|
||||||
${this._renderUpdateCard(
|
${this._renderUpdateCard(
|
||||||
"Supervisor",
|
"Supervisor",
|
||||||
@@ -107,12 +107,12 @@ export class HassioUpdate extends LitElement {
|
|||||||
return html``;
|
return html``;
|
||||||
}
|
}
|
||||||
return html`
|
return html`
|
||||||
<paper-card>
|
<ha-card>
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
${icon
|
${icon
|
||||||
? html`
|
? html`
|
||||||
<div class="icon">
|
<div class="icon">
|
||||||
<ha-icon .icon=${icon}></ha-icon>
|
<ha-svg-icon .path=${icon}></ha-svg-icon>
|
||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
@@ -133,7 +133,7 @@ export class HassioUpdate extends LitElement {
|
|||||||
Update
|
Update
|
||||||
</ha-call-api-button>
|
</ha-call-api-button>
|
||||||
</div>
|
</div>
|
||||||
</paper-card>
|
</ha-card>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,7 +1,3 @@
|
|||||||
import "@polymer/app-layout/app-toolbar/app-toolbar";
|
|
||||||
import { PaperDialogElement } from "@polymer/paper-dialog";
|
|
||||||
import "@polymer/paper-dialog-scrollable/paper-dialog-scrollable";
|
|
||||||
import "../../../../src/components/ha-icon-button";
|
|
||||||
import {
|
import {
|
||||||
css,
|
css,
|
||||||
CSSResult,
|
CSSResult,
|
||||||
@@ -9,46 +5,50 @@ import {
|
|||||||
html,
|
html,
|
||||||
LitElement,
|
LitElement,
|
||||||
property,
|
property,
|
||||||
query,
|
|
||||||
TemplateResult,
|
TemplateResult,
|
||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
import "../../../../src/components/dialog/ha-paper-dialog";
|
import { createCloseHeading } from "../../../../src/components/ha-dialog";
|
||||||
import "../../../../src/components/ha-markdown";
|
import "../../../../src/components/ha-markdown";
|
||||||
import { haStyleDialog } from "../../../../src/resources/styles";
|
import { haStyleDialog } from "../../../../src/resources/styles";
|
||||||
|
import { HomeAssistant } from "../../../../src/types";
|
||||||
import { hassioStyle } from "../../resources/hassio-style";
|
import { hassioStyle } from "../../resources/hassio-style";
|
||||||
import { HassioMarkdownDialogParams } from "./show-dialog-hassio-markdown";
|
import { HassioMarkdownDialogParams } from "./show-dialog-hassio-markdown";
|
||||||
|
|
||||||
@customElement("dialog-hassio-markdown")
|
@customElement("dialog-hassio-markdown")
|
||||||
class HassioMarkdownDialog extends LitElement {
|
class HassioMarkdownDialog extends LitElement {
|
||||||
|
@property() public hass!: HomeAssistant;
|
||||||
|
|
||||||
@property() public title!: string;
|
@property() public title!: string;
|
||||||
|
|
||||||
@property() public content!: string;
|
@property() public content!: string;
|
||||||
|
|
||||||
@query("#dialog") private _dialog!: PaperDialogElement;
|
@property() private _opened = false;
|
||||||
|
|
||||||
public showDialog(params: HassioMarkdownDialogParams) {
|
public showDialog(params: HassioMarkdownDialogParams) {
|
||||||
this.title = params.title;
|
this.title = params.title;
|
||||||
this.content = params.content;
|
this.content = params.content;
|
||||||
this._dialog.open();
|
this._opened = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
|
if (!this._opened) {
|
||||||
|
return html``;
|
||||||
|
}
|
||||||
return html`
|
return html`
|
||||||
<ha-paper-dialog id="dialog" with-backdrop="">
|
<ha-dialog
|
||||||
<app-toolbar>
|
open
|
||||||
<ha-icon-button
|
@closing=${this._closeDialog}
|
||||||
icon="hassio:close"
|
.heading=${createCloseHeading(this.hass, this.title)}
|
||||||
dialog-dismiss=""
|
>
|
||||||
></ha-icon-button>
|
<ha-markdown .content=${this.content || ""}></ha-markdown>
|
||||||
<div main-title="">${this.title}</div>
|
</ha-dialog>
|
||||||
</app-toolbar>
|
|
||||||
<paper-dialog-scrollable>
|
|
||||||
<ha-markdown .content=${this.content || ""}></ha-markdown>
|
|
||||||
</paper-dialog-scrollable>
|
|
||||||
</ha-paper-dialog>
|
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _closeDialog(): void {
|
||||||
|
this._opened = false;
|
||||||
|
}
|
||||||
|
|
||||||
static get styles(): CSSResult[] {
|
static get styles(): CSSResult[] {
|
||||||
return [
|
return [
|
||||||
haStyleDialog,
|
haStyleDialog,
|
||||||
|
@@ -1,9 +1,11 @@
|
|||||||
import "@material/mwc-button/mwc-button";
|
import "@material/mwc-button/mwc-button";
|
||||||
|
import "@material/mwc-icon-button/mwc-icon-button";
|
||||||
|
import { mdiDelete } from "@mdi/js";
|
||||||
import "@polymer/paper-input/paper-input";
|
import "@polymer/paper-input/paper-input";
|
||||||
import "@polymer/paper-spinner/paper-spinner";
|
|
||||||
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 {
|
import {
|
||||||
css,
|
css,
|
||||||
CSSResult,
|
CSSResult,
|
||||||
@@ -16,16 +18,14 @@ import {
|
|||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
import "../../../../src/components/ha-dialog";
|
import "../../../../src/components/ha-dialog";
|
||||||
import "../../../../src/components/ha-icon-button";
|
import "../../../../src/components/ha-svg-icon";
|
||||||
|
import {
|
||||||
|
fetchHassioAddonsInfo,
|
||||||
|
HassioAddonRepository,
|
||||||
|
} from "../../../../src/data/hassio/addon";
|
||||||
|
import { setSupervisorOption } from "../../../../src/data/hassio/supervisor";
|
||||||
import { haStyle, haStyleDialog } from "../../../../src/resources/styles";
|
import { haStyle, haStyleDialog } from "../../../../src/resources/styles";
|
||||||
import type { HomeAssistant } from "../../../../src/types";
|
import type { HomeAssistant } from "../../../../src/types";
|
||||||
import {
|
|
||||||
HassioAddonRepository,
|
|
||||||
fetchHassioAddonsInfo,
|
|
||||||
} from "../../../../src/data/hassio/addon";
|
|
||||||
|
|
||||||
import { setSupervisorOption } from "../../../../src/data/hassio/supervisor";
|
|
||||||
import { HassioRepositoryDialogParams } from "./show-dialog-repositories";
|
import { HassioRepositoryDialogParams } from "./show-dialog-repositories";
|
||||||
|
|
||||||
@customElement("dialog-hassio-repositories")
|
@customElement("dialog-hassio-repositories")
|
||||||
@@ -84,12 +84,13 @@ class HassioRepositoriesDialog extends LitElement {
|
|||||||
<div secondary>${repo.maintainer}</div>
|
<div secondary>${repo.maintainer}</div>
|
||||||
<div secondary>${repo.url}</div>
|
<div secondary>${repo.url}</div>
|
||||||
</paper-item-body>
|
</paper-item-body>
|
||||||
<ha-icon-button
|
<mwc-icon-button
|
||||||
.slug=${repo.slug}
|
.slug=${repo.slug}
|
||||||
title="Remove"
|
title="Remove"
|
||||||
@click=${this._removeRepository}
|
@click=${this._removeRepository}
|
||||||
icon="hassio:delete"
|
>
|
||||||
></ha-icon-button>
|
<ha-svg-icon path=${mdiDelete}></ha-svg-icon>
|
||||||
|
</mwc-icon-button>
|
||||||
</paper-item>
|
</paper-item>
|
||||||
`;
|
`;
|
||||||
})
|
})
|
||||||
@@ -194,7 +195,7 @@ class HassioRepositoriesDialog extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async _removeRepository(ev: Event) {
|
private async _removeRepository(ev: Event) {
|
||||||
const slug = (ev.target as any).slug;
|
const slug = (ev.currentTarget as any).slug;
|
||||||
const repositories = this._filteredRepositories(this._repos);
|
const repositories = this._filteredRepositories(this._repos);
|
||||||
const repository = repositories.find((repo) => {
|
const repository = repositories.find((repo) => {
|
||||||
return repo.slug === slug;
|
return repo.slug === slug;
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
||||||
import "./dialog-hassio-repositories";
|
|
||||||
import { HassioAddonRepository } from "../../../../src/data/hassio/addon";
|
import { HassioAddonRepository } from "../../../../src/data/hassio/addon";
|
||||||
|
import "./dialog-hassio-repositories";
|
||||||
|
|
||||||
export interface HassioRepositoryDialogParams {
|
export interface HassioRepositoryDialogParams {
|
||||||
repos: HassioAddonRepository[];
|
repos: HassioAddonRepository[];
|
||||||
|
@@ -1,10 +1,6 @@
|
|||||||
import "@material/mwc-button";
|
import "@material/mwc-button";
|
||||||
import "@polymer/app-layout/app-toolbar/app-toolbar";
|
import { mdiDelete, mdiDownload, mdiHistory } from "@mdi/js";
|
||||||
import { PaperCheckboxElement } from "@polymer/paper-checkbox/paper-checkbox";
|
import { PaperCheckboxElement } from "@polymer/paper-checkbox/paper-checkbox";
|
||||||
import { PaperDialogElement } from "@polymer/paper-dialog";
|
|
||||||
import "@polymer/paper-dialog-scrollable/paper-dialog-scrollable";
|
|
||||||
import "../../../../src/components/ha-icon-button";
|
|
||||||
import "../../../../src/components/ha-icon";
|
|
||||||
import "@polymer/paper-input/paper-input";
|
import "@polymer/paper-input/paper-input";
|
||||||
import {
|
import {
|
||||||
css,
|
css,
|
||||||
@@ -13,10 +9,10 @@ import {
|
|||||||
html,
|
html,
|
||||||
LitElement,
|
LitElement,
|
||||||
property,
|
property,
|
||||||
query,
|
|
||||||
TemplateResult,
|
TemplateResult,
|
||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
import "../../../../src/components/dialog/ha-paper-dialog";
|
import { createCloseHeading } from "../../../../src/components/ha-dialog";
|
||||||
|
import "../../../../src/components/ha-svg-icon";
|
||||||
import { getSignedPath } from "../../../../src/data/auth";
|
import { getSignedPath } from "../../../../src/data/auth";
|
||||||
import {
|
import {
|
||||||
fetchHassioSnapshotInfo,
|
fetchHassioSnapshotInfo,
|
||||||
@@ -76,7 +72,7 @@ class HassioSnapshotDialog extends LitElement {
|
|||||||
|
|
||||||
@property() private _error?: string;
|
@property() private _error?: string;
|
||||||
|
|
||||||
@property() private snapshot?: HassioSnapshotDetail;
|
@property() private _snapshot?: HassioSnapshotDetail;
|
||||||
|
|
||||||
@property() private _folders!: FolderItem[];
|
@property() private _folders!: FolderItem[];
|
||||||
|
|
||||||
@@ -88,49 +84,35 @@ class HassioSnapshotDialog extends LitElement {
|
|||||||
|
|
||||||
@property() private _restoreHass: boolean | null | undefined = true;
|
@property() private _restoreHass: boolean | null | undefined = true;
|
||||||
|
|
||||||
@query("#dialog") private _dialog!: PaperDialogElement;
|
|
||||||
|
|
||||||
public async showDialog(params: HassioSnapshotDialogParams) {
|
public async showDialog(params: HassioSnapshotDialogParams) {
|
||||||
this.snapshot = await fetchHassioSnapshotInfo(this.hass, params.slug);
|
this._snapshot = await fetchHassioSnapshotInfo(this.hass, params.slug);
|
||||||
this._folders = _computeFolders(
|
this._folders = _computeFolders(
|
||||||
this.snapshot.folders
|
this._snapshot.folders
|
||||||
).sort((a: FolderItem, b: FolderItem) => (a.name > b.name ? 1 : -1));
|
).sort((a: FolderItem, b: FolderItem) => (a.name > b.name ? 1 : -1));
|
||||||
this._addons = _computeAddons(
|
this._addons = _computeAddons(
|
||||||
this.snapshot.addons
|
this._snapshot.addons
|
||||||
).sort((a: AddonItem, b: AddonItem) => (a.name > b.name ? 1 : -1));
|
).sort((a: AddonItem, b: AddonItem) => (a.name > b.name ? 1 : -1));
|
||||||
|
|
||||||
this._dialogParams = params;
|
this._dialogParams = params;
|
||||||
|
|
||||||
try {
|
|
||||||
this._dialog.open();
|
|
||||||
} catch {
|
|
||||||
await this.showDialog(params);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
if (!this.snapshot) {
|
if (!this._dialogParams || !this._snapshot) {
|
||||||
return html``;
|
return html``;
|
||||||
}
|
}
|
||||||
return html`
|
return html`
|
||||||
<ha-paper-dialog
|
<ha-dialog
|
||||||
id="dialog"
|
open
|
||||||
with-backdrop=""
|
stacked
|
||||||
.on-iron-overlay-closed=${this._dialogClosed}
|
@closing=${this._closeDialog}
|
||||||
|
.heading=${createCloseHeading(this.hass, this._computeName)}
|
||||||
>
|
>
|
||||||
<app-toolbar>
|
|
||||||
<ha-icon-button
|
|
||||||
icon="hassio:close"
|
|
||||||
dialog-dismiss=""
|
|
||||||
></ha-icon-button>
|
|
||||||
<div main-title="">${this._computeName}</div>
|
|
||||||
</app-toolbar>
|
|
||||||
<div class="details">
|
<div class="details">
|
||||||
${this.snapshot.type === "full"
|
${this._snapshot.type === "full"
|
||||||
? "Full snapshot"
|
? "Full snapshot"
|
||||||
: "Partial snapshot"}
|
: "Partial snapshot"}
|
||||||
(${this._computeSize})<br />
|
(${this._computeSize})<br />
|
||||||
${this._formatDatetime(this.snapshot.date)}
|
${this._formatDatetime(this._snapshot.date)}
|
||||||
</div>
|
</div>
|
||||||
<div>Home Assistant:</div>
|
<div>Home Assistant:</div>
|
||||||
<paper-checkbox
|
<paper-checkbox
|
||||||
@@ -139,7 +121,7 @@ class HassioSnapshotDialog extends LitElement {
|
|||||||
this._restoreHass = (ev.target as PaperCheckboxElement).checked;
|
this._restoreHass = (ev.target as PaperCheckboxElement).checked;
|
||||||
}}"
|
}}"
|
||||||
>
|
>
|
||||||
Home Assistant ${this.snapshot.homeassistant}
|
Home Assistant ${this._snapshot.homeassistant}
|
||||||
</paper-checkbox>
|
</paper-checkbox>
|
||||||
${this._folders.length
|
${this._folders.length
|
||||||
? html`
|
? html`
|
||||||
@@ -183,7 +165,7 @@ class HassioSnapshotDialog extends LitElement {
|
|||||||
</paper-dialog-scrollable>
|
</paper-dialog-scrollable>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
${this.snapshot.protected
|
${this._snapshot.protected
|
||||||
? html`
|
? html`
|
||||||
<paper-input
|
<paper-input
|
||||||
autofocus=""
|
autofocus=""
|
||||||
@@ -197,37 +179,35 @@ class HassioSnapshotDialog extends LitElement {
|
|||||||
${this._error ? html` <p class="error">Error: ${this._error}</p> ` : ""}
|
${this._error ? html` <p class="error">Error: ${this._error}</p> ` : ""}
|
||||||
|
|
||||||
<div>Actions:</div>
|
<div>Actions:</div>
|
||||||
<ul class="buttons">
|
|
||||||
<li>
|
<mwc-button @click=${this._downloadClicked} slot="primaryAction">
|
||||||
<mwc-button @click=${this._downloadClicked}>
|
<ha-svg-icon path=${mdiDownload} class="icon"></ha-svg-icon>
|
||||||
<ha-icon icon="hassio:download" class="icon"></ha-icon>
|
Download Snapshot
|
||||||
Download Snapshot
|
</mwc-button>
|
||||||
</mwc-button>
|
|
||||||
</li>
|
<mwc-button
|
||||||
<li>
|
@click=${this._partialRestoreClicked}
|
||||||
<mwc-button @click=${this._partialRestoreClicked}>
|
slot="secondaryAction"
|
||||||
<ha-icon icon="hassio:history" class="icon"> </ha-icon>
|
>
|
||||||
Restore Selected
|
<ha-svg-icon path=${mdiHistory} class="icon"></ha-svg-icon>
|
||||||
</mwc-button>
|
Restore Selected
|
||||||
</li>
|
</mwc-button>
|
||||||
${this.snapshot.type === "full"
|
${this._snapshot.type === "full"
|
||||||
? html`
|
? html`
|
||||||
<li>
|
<mwc-button
|
||||||
<mwc-button @click=${this._fullRestoreClicked}>
|
@click=${this._fullRestoreClicked}
|
||||||
<ha-icon icon="hassio:history" class="icon"> </ha-icon>
|
slot="secondaryAction"
|
||||||
Wipe & restore
|
>
|
||||||
</mwc-button>
|
<ha-svg-icon path=${mdiHistory} class="icon"></ha-svg-icon>
|
||||||
</li>
|
Wipe & restore
|
||||||
`
|
</mwc-button>
|
||||||
: ""}
|
`
|
||||||
<li>
|
: ""}
|
||||||
<mwc-button @click=${this._deleteClicked}>
|
<mwc-button @click=${this._deleteClicked} slot="secondaryAction">
|
||||||
<ha-icon icon="hassio:delete" class="icon warning"> </ha-icon>
|
<ha-svg-icon path=${mdiDelete} class="icon warning"></ha-svg-icon>
|
||||||
<span class="warning">Delete Snapshot</span>
|
<span class="warning">Delete Snapshot</span>
|
||||||
</mwc-button>
|
</mwc-button>
|
||||||
</li>
|
</ha-dialog>
|
||||||
</ul>
|
|
||||||
</ha-paper-dialog>
|
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -235,37 +215,10 @@ class HassioSnapshotDialog extends LitElement {
|
|||||||
return [
|
return [
|
||||||
haStyleDialog,
|
haStyleDialog,
|
||||||
css`
|
css`
|
||||||
ha-paper-dialog {
|
|
||||||
min-width: 350px;
|
|
||||||
font-size: 14px;
|
|
||||||
border-radius: 2px;
|
|
||||||
}
|
|
||||||
app-toolbar {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0 16px;
|
|
||||||
color: var(--primary-text-color);
|
|
||||||
background-color: var(--secondary-background-color);
|
|
||||||
}
|
|
||||||
app-toolbar [main-title] {
|
|
||||||
margin-left: 16px;
|
|
||||||
}
|
|
||||||
ha-paper-dialog-scrollable {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
paper-checkbox {
|
paper-checkbox {
|
||||||
display: block;
|
display: block;
|
||||||
margin: 4px;
|
margin: 4px;
|
||||||
}
|
}
|
||||||
@media all and (max-width: 450px), all and (max-height: 500px) {
|
|
||||||
ha-paper-dialog {
|
|
||||||
max-height: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
app-toolbar {
|
|
||||||
color: var(--text-primary-color);
|
|
||||||
background-color: var(--primary-color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.details {
|
.details {
|
||||||
color: var(--secondary-text-color);
|
color: var(--secondary-text-color);
|
||||||
}
|
}
|
||||||
@@ -336,7 +289,7 @@ class HassioSnapshotDialog extends LitElement {
|
|||||||
folders,
|
folders,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (this.snapshot!.protected) {
|
if (this._snapshot!.protected) {
|
||||||
data.password = this._snapshotPassword;
|
data.password = this._snapshotPassword;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -344,13 +297,13 @@ class HassioSnapshotDialog extends LitElement {
|
|||||||
.callApi(
|
.callApi(
|
||||||
"POST",
|
"POST",
|
||||||
|
|
||||||
`hassio/snapshots/${this.snapshot!.slug}/restore/partial`,
|
`hassio/snapshots/${this._snapshot!.slug}/restore/partial`,
|
||||||
data
|
data
|
||||||
)
|
)
|
||||||
.then(
|
.then(
|
||||||
() => {
|
() => {
|
||||||
alert("Snapshot restored!");
|
alert("Snapshot restored!");
|
||||||
this._dialog.close();
|
this._closeDialog();
|
||||||
},
|
},
|
||||||
(error) => {
|
(error) => {
|
||||||
this._error = error.body.message;
|
this._error = error.body.message;
|
||||||
@@ -363,20 +316,20 @@ class HassioSnapshotDialog extends LitElement {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = this.snapshot!.protected
|
const data = this._snapshot!.protected
|
||||||
? { password: this._snapshotPassword }
|
? { password: this._snapshotPassword }
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
this.hass
|
this.hass
|
||||||
.callApi(
|
.callApi(
|
||||||
"POST",
|
"POST",
|
||||||
`hassio/snapshots/${this.snapshot!.slug}/restore/full`,
|
`hassio/snapshots/${this._snapshot!.slug}/restore/full`,
|
||||||
data
|
data
|
||||||
)
|
)
|
||||||
.then(
|
.then(
|
||||||
() => {
|
() => {
|
||||||
alert("Snapshot restored!");
|
alert("Snapshot restored!");
|
||||||
this._dialog.close();
|
this._closeDialog();
|
||||||
},
|
},
|
||||||
(error) => {
|
(error) => {
|
||||||
this._error = error.body.message;
|
this._error = error.body.message;
|
||||||
@@ -391,11 +344,11 @@ class HassioSnapshotDialog extends LitElement {
|
|||||||
|
|
||||||
this.hass
|
this.hass
|
||||||
|
|
||||||
.callApi("POST", `hassio/snapshots/${this.snapshot!.slug}/remove`)
|
.callApi("POST", `hassio/snapshots/${this._snapshot!.slug}/remove`)
|
||||||
.then(
|
.then(
|
||||||
() => {
|
() => {
|
||||||
this._dialog.close();
|
|
||||||
this._dialogParams!.onDelete();
|
this._dialogParams!.onDelete();
|
||||||
|
this._closeDialog();
|
||||||
},
|
},
|
||||||
(error) => {
|
(error) => {
|
||||||
this._error = error.body.message;
|
this._error = error.body.message;
|
||||||
@@ -408,7 +361,7 @@ class HassioSnapshotDialog extends LitElement {
|
|||||||
try {
|
try {
|
||||||
signedPath = await getSignedPath(
|
signedPath = await getSignedPath(
|
||||||
this.hass,
|
this.hass,
|
||||||
`/api/hassio/snapshots/${this.snapshot!.slug}/download`
|
`/api/hassio/snapshots/${this._snapshot!.slug}/download`
|
||||||
);
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
alert(`Error: ${err.message}`);
|
alert(`Error: ${err.message}`);
|
||||||
@@ -419,19 +372,19 @@ class HassioSnapshotDialog extends LitElement {
|
|||||||
const a = document.createElement("a");
|
const a = document.createElement("a");
|
||||||
a.href = signedPath.path;
|
a.href = signedPath.path;
|
||||||
a.download = `Hass_io_${name}.tar`;
|
a.download = `Hass_io_${name}.tar`;
|
||||||
this._dialog.appendChild(a);
|
this.shadowRoot!.appendChild(a);
|
||||||
a.click();
|
a.click();
|
||||||
this._dialog.removeChild(a);
|
this.shadowRoot!.removeChild(a);
|
||||||
}
|
}
|
||||||
|
|
||||||
private get _computeName() {
|
private get _computeName() {
|
||||||
return this.snapshot
|
return this._snapshot
|
||||||
? this.snapshot.name || this.snapshot.slug
|
? this._snapshot.name || this._snapshot.slug
|
||||||
: "Unnamed snapshot";
|
: "Unnamed snapshot";
|
||||||
}
|
}
|
||||||
|
|
||||||
private get _computeSize() {
|
private get _computeSize() {
|
||||||
return Math.ceil(this.snapshot!.size * 10) / 10 + " MB";
|
return Math.ceil(this._snapshot!.size * 10) / 10 + " MB";
|
||||||
}
|
}
|
||||||
|
|
||||||
private _formatDatetime(datetime) {
|
private _formatDatetime(datetime) {
|
||||||
@@ -445,9 +398,9 @@ class HassioSnapshotDialog extends LitElement {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private _dialogClosed() {
|
private _closeDialog() {
|
||||||
this._dialogParams = undefined;
|
this._dialogParams = undefined;
|
||||||
this.snapshot = undefined;
|
this._snapshot = undefined;
|
||||||
this._snapshotPassword = "";
|
this._snapshotPassword = "";
|
||||||
this._folders = [];
|
this._folders = [];
|
||||||
this._addons = [];
|
this._addons = [];
|
||||||
|
@@ -3,11 +3,11 @@ import {
|
|||||||
HassioAddonDetails,
|
HassioAddonDetails,
|
||||||
restartHassioAddon,
|
restartHassioAddon,
|
||||||
} from "../../../src/data/hassio/addon";
|
} from "../../../src/data/hassio/addon";
|
||||||
import { HomeAssistant } from "../../../src/types";
|
|
||||||
import {
|
import {
|
||||||
showConfirmationDialog,
|
|
||||||
showAlertDialog,
|
showAlertDialog,
|
||||||
|
showConfirmationDialog,
|
||||||
} from "../../../src/dialogs/generic/show-dialog-box";
|
} from "../../../src/dialogs/generic/show-dialog-box";
|
||||||
|
import { HomeAssistant } from "../../../src/types";
|
||||||
|
|
||||||
export const suggestAddonRestart = async (
|
export const suggestAddonRestart = async (
|
||||||
element: LitElement,
|
element: LitElement,
|
||||||
|
@@ -1,4 +1,3 @@
|
|||||||
import "../../src/components/ha-icon-button";
|
|
||||||
import { PolymerElement } from "@polymer/polymer";
|
import { PolymerElement } from "@polymer/polymer";
|
||||||
import { customElement, property, PropertyValues } from "lit-element";
|
import { customElement, property, PropertyValues } from "lit-element";
|
||||||
import { applyThemesOnElement } from "../../src/common/dom/apply_themes_on_element";
|
import { applyThemesOnElement } from "../../src/common/dom/apply_themes_on_element";
|
||||||
@@ -34,12 +33,6 @@ import { HomeAssistant } from "../../src/types";
|
|||||||
// Don't codesplit it, that way the dashboard always loads fast.
|
// Don't codesplit it, that way the dashboard always loads fast.
|
||||||
import "./hassio-panel";
|
import "./hassio-panel";
|
||||||
|
|
||||||
// The register callback of the IronA11yKeysBehavior inside ha-icon-button
|
|
||||||
// is not called, causing _keyBindings to be uninitiliazed for ha-icon-button,
|
|
||||||
// causing an exception when added to DOM. When transpiled to ES5, this will
|
|
||||||
// break the build.
|
|
||||||
customElements.get("ha-icon-button").prototype._keyBindings = {};
|
|
||||||
|
|
||||||
@customElement("hassio-main")
|
@customElement("hassio-main")
|
||||||
class HassioMain extends ProvideHassLitMixin(HassRouterPage) {
|
class HassioMain extends ProvideHassLitMixin(HassRouterPage) {
|
||||||
@property() public hass!: HomeAssistant;
|
@property() public hass!: HomeAssistant;
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
import { mdiBackupRestore, mdiCogs, mdiStore, mdiViewDashboard } from "@mdi/js";
|
||||||
import {
|
import {
|
||||||
customElement,
|
customElement,
|
||||||
html,
|
html,
|
||||||
@@ -5,37 +6,36 @@ import {
|
|||||||
property,
|
property,
|
||||||
TemplateResult,
|
TemplateResult,
|
||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
|
|
||||||
import { HassioHassOSInfo, HassioHostInfo } from "../../src/data/hassio/host";
|
import { HassioHassOSInfo, HassioHostInfo } from "../../src/data/hassio/host";
|
||||||
import {
|
import {
|
||||||
HassioHomeAssistantInfo,
|
HassioHomeAssistantInfo,
|
||||||
HassioSupervisorInfo,
|
HassioSupervisorInfo,
|
||||||
} from "../../src/data/hassio/supervisor";
|
} from "../../src/data/hassio/supervisor";
|
||||||
|
import type { PageNavigation } from "../../src/layouts/hass-tabs-subpage";
|
||||||
import "../../src/resources/ha-style";
|
import "../../src/resources/ha-style";
|
||||||
import { HomeAssistant, Route } from "../../src/types";
|
import { HomeAssistant, Route } from "../../src/types";
|
||||||
import "./hassio-panel-router";
|
import "./hassio-panel-router";
|
||||||
import type { PageNavigation } from "../../src/layouts/hass-tabs-subpage";
|
|
||||||
|
|
||||||
export const supervisorTabs: PageNavigation[] = [
|
export const supervisorTabs: PageNavigation[] = [
|
||||||
{
|
{
|
||||||
name: "Dashboard",
|
name: "Dashboard",
|
||||||
path: `/hassio/dashboard`,
|
path: `/hassio/dashboard`,
|
||||||
icon: "hassio:view-dashboard",
|
iconPath: mdiViewDashboard,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Add-on store",
|
name: "Add-on store",
|
||||||
path: `/hassio/store`,
|
path: `/hassio/store`,
|
||||||
icon: "hassio:store",
|
iconPath: mdiStore,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Snapshots",
|
name: "Snapshots",
|
||||||
path: `/hassio/snapshots`,
|
path: `/hassio/snapshots`,
|
||||||
icon: "hassio:backup-restore",
|
iconPath: mdiBackupRestore,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "System",
|
name: "System",
|
||||||
path: `/hassio/system`,
|
path: `/hassio/system`,
|
||||||
icon: "hassio:cogs",
|
iconPath: mdiCogs,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@@ -86,9 +86,6 @@ class HassioIngressView extends LitElement {
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
border: 0;
|
border: 0;
|
||||||
}
|
}
|
||||||
ha-icon-button {
|
|
||||||
color: var(--text-primary-color);
|
|
||||||
}
|
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
import "@material/mwc-button";
|
import "@material/mwc-button";
|
||||||
import "@polymer/paper-card/paper-card";
|
import "@material/mwc-icon-button";
|
||||||
|
import { mdiPackageVariant, mdiPackageVariantClosed, mdiReload } from "@mdi/js";
|
||||||
import "@polymer/paper-checkbox/paper-checkbox";
|
import "@polymer/paper-checkbox/paper-checkbox";
|
||||||
import type { PaperCheckboxElement } from "@polymer/paper-checkbox/paper-checkbox";
|
import type { PaperCheckboxElement } from "@polymer/paper-checkbox/paper-checkbox";
|
||||||
import "@polymer/paper-input/paper-input";
|
import "@polymer/paper-input/paper-input";
|
||||||
@@ -18,6 +19,8 @@ import {
|
|||||||
TemplateResult,
|
TemplateResult,
|
||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
import { fireEvent } from "../../../src/common/dom/fire_event";
|
import { fireEvent } from "../../../src/common/dom/fire_event";
|
||||||
|
import "../../../src/components/ha-card";
|
||||||
|
import "../../../src/components/ha-svg-icon";
|
||||||
import {
|
import {
|
||||||
createHassioFullSnapshot,
|
createHassioFullSnapshot,
|
||||||
createHassioPartialSnapshot,
|
createHassioPartialSnapshot,
|
||||||
@@ -28,15 +31,14 @@ import {
|
|||||||
reloadHassioSnapshots,
|
reloadHassioSnapshots,
|
||||||
} from "../../../src/data/hassio/snapshot";
|
} from "../../../src/data/hassio/snapshot";
|
||||||
import { HassioSupervisorInfo } from "../../../src/data/hassio/supervisor";
|
import { HassioSupervisorInfo } from "../../../src/data/hassio/supervisor";
|
||||||
|
import "../../../src/layouts/hass-tabs-subpage";
|
||||||
import { PolymerChangedEvent } from "../../../src/polymer-types";
|
import { PolymerChangedEvent } from "../../../src/polymer-types";
|
||||||
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 "../../../src/layouts/hass-tabs-subpage";
|
|
||||||
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 { hassioStyle } from "../resources/hassio-style";
|
|
||||||
|
|
||||||
import { supervisorTabs } from "../hassio-panel";
|
import { supervisorTabs } from "../hassio-panel";
|
||||||
|
import { hassioStyle } from "../resources/hassio-style";
|
||||||
|
|
||||||
interface CheckboxItem {
|
interface CheckboxItem {
|
||||||
slug: string;
|
slug: string;
|
||||||
@@ -98,12 +100,13 @@ class HassioSnapshots extends LitElement {
|
|||||||
>
|
>
|
||||||
<span slot="header">Snapshots</span>
|
<span slot="header">Snapshots</span>
|
||||||
|
|
||||||
<ha-icon-button
|
<mwc-icon-button
|
||||||
icon="hassio:reload"
|
|
||||||
slot="toolbar-icon"
|
slot="toolbar-icon"
|
||||||
aria-label="Reload snapshots"
|
aria-label="Reload snapshots"
|
||||||
@click=${this.refreshData}
|
@click=${this.refreshData}
|
||||||
></ha-icon-button>
|
>
|
||||||
|
<ha-svg-icon path=${mdiReload}></ha-svg-icon>
|
||||||
|
</mwc-icon-button>
|
||||||
|
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<h1>
|
<h1>
|
||||||
@@ -114,7 +117,7 @@ class HassioSnapshots extends LitElement {
|
|||||||
Home Assistant instance.
|
Home Assistant instance.
|
||||||
</p>
|
</p>
|
||||||
<div class="card-group">
|
<div class="card-group">
|
||||||
<paper-card>
|
<ha-card>
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<paper-input
|
<paper-input
|
||||||
autofocus
|
autofocus
|
||||||
@@ -195,7 +198,7 @@ class HassioSnapshots extends LitElement {
|
|||||||
Create
|
Create
|
||||||
</mwc-button>
|
</mwc-button>
|
||||||
</div>
|
</div>
|
||||||
</paper-card>
|
</ha-card>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h1>Available snapshots</h1>
|
<h1>Available snapshots</h1>
|
||||||
@@ -204,15 +207,15 @@ class HassioSnapshots extends LitElement {
|
|||||||
? undefined
|
? undefined
|
||||||
: this._snapshots.length === 0
|
: this._snapshots.length === 0
|
||||||
? html`
|
? html`
|
||||||
<paper-card>
|
<ha-card>
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
You don't have any snapshots yet.
|
You don't have any snapshots yet.
|
||||||
</div>
|
</div>
|
||||||
</paper-card>
|
</ha-card>
|
||||||
`
|
`
|
||||||
: this._snapshots.map(
|
: this._snapshots.map(
|
||||||
(snapshot) => html`
|
(snapshot) => html`
|
||||||
<paper-card
|
<ha-card
|
||||||
class="pointer"
|
class="pointer"
|
||||||
.snapshot=${snapshot}
|
.snapshot=${snapshot}
|
||||||
@click=${this._snapshotClicked}
|
@click=${this._snapshotClicked}
|
||||||
@@ -224,12 +227,12 @@ class HassioSnapshots extends LitElement {
|
|||||||
.description=${this._computeDetails(snapshot)}
|
.description=${this._computeDetails(snapshot)}
|
||||||
.datetime=${snapshot.date}
|
.datetime=${snapshot.date}
|
||||||
.icon=${snapshot.type === "full"
|
.icon=${snapshot.type === "full"
|
||||||
? "hassio:package-variant-closed"
|
? mdiPackageVariantClosed
|
||||||
: "hassio:package-variant"}
|
: mdiPackageVariant}
|
||||||
.icon-class="snapshot"
|
.icon-class="snapshot"
|
||||||
></hassio-card-content>
|
></hassio-card-content>
|
||||||
</div>
|
</div>
|
||||||
</paper-card>
|
</ha-card>
|
||||||
`
|
`
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
@@ -1,5 +1,4 @@
|
|||||||
import "@material/mwc-button";
|
import "@material/mwc-button";
|
||||||
import "@polymer/paper-card/paper-card";
|
|
||||||
import {
|
import {
|
||||||
css,
|
css,
|
||||||
CSSResult,
|
CSSResult,
|
||||||
@@ -12,23 +11,23 @@ import {
|
|||||||
import "../../../src/components/buttons/ha-call-api-button";
|
import "../../../src/components/buttons/ha-call-api-button";
|
||||||
import { fetchHassioHardwareInfo } from "../../../src/data/hassio/hardware";
|
import { fetchHassioHardwareInfo } from "../../../src/data/hassio/hardware";
|
||||||
import {
|
import {
|
||||||
|
changeHostOptions,
|
||||||
fetchHassioHostInfo,
|
fetchHassioHostInfo,
|
||||||
HassioHassOSInfo,
|
HassioHassOSInfo,
|
||||||
HassioHostInfo as HassioHostInfoType,
|
HassioHostInfo as HassioHostInfoType,
|
||||||
rebootHost,
|
rebootHost,
|
||||||
shutdownHost,
|
shutdownHost,
|
||||||
updateOS,
|
updateOS,
|
||||||
changeHostOptions,
|
|
||||||
} from "../../../src/data/hassio/host";
|
} from "../../../src/data/hassio/host";
|
||||||
|
import {
|
||||||
|
showAlertDialog,
|
||||||
|
showConfirmationDialog,
|
||||||
|
showPromptDialog,
|
||||||
|
} from "../../../src/dialogs/generic/show-dialog-box";
|
||||||
import { haStyle } from "../../../src/resources/styles";
|
import { haStyle } from "../../../src/resources/styles";
|
||||||
import { HomeAssistant } from "../../../src/types";
|
import { HomeAssistant } from "../../../src/types";
|
||||||
import { showHassioMarkdownDialog } from "../dialogs/markdown/show-dialog-hassio-markdown";
|
import { showHassioMarkdownDialog } from "../dialogs/markdown/show-dialog-hassio-markdown";
|
||||||
import { hassioStyle } from "../resources/hassio-style";
|
import { hassioStyle } from "../resources/hassio-style";
|
||||||
import {
|
|
||||||
showConfirmationDialog,
|
|
||||||
showAlertDialog,
|
|
||||||
showPromptDialog,
|
|
||||||
} from "../../../src/dialogs/generic/show-dialog-box";
|
|
||||||
|
|
||||||
@customElement("hassio-host-info")
|
@customElement("hassio-host-info")
|
||||||
class HassioHostInfo extends LitElement {
|
class HassioHostInfo extends LitElement {
|
||||||
@@ -42,7 +41,7 @@ class HassioHostInfo extends LitElement {
|
|||||||
|
|
||||||
public render(): TemplateResult | void {
|
public render(): TemplateResult | void {
|
||||||
return html`
|
return html`
|
||||||
<paper-card>
|
<ha-card>
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<h2>Host system</h2>
|
<h2>Host system</h2>
|
||||||
<table class="info">
|
<table class="info">
|
||||||
@@ -113,7 +112,7 @@ class HassioHostInfo extends LitElement {
|
|||||||
? html` <mwc-button @click=${this._updateOS}>Update</mwc-button> `
|
? html` <mwc-button @click=${this._updateOS}>Update</mwc-button> `
|
||||||
: ""}
|
: ""}
|
||||||
</div>
|
</div>
|
||||||
</paper-card>
|
</ha-card>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,7 +121,7 @@ class HassioHostInfo extends LitElement {
|
|||||||
haStyle,
|
haStyle,
|
||||||
hassioStyle,
|
hassioStyle,
|
||||||
css`
|
css`
|
||||||
paper-card {
|
ha-card {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,4 @@
|
|||||||
import "@material/mwc-button";
|
import "@material/mwc-button";
|
||||||
import "@polymer/paper-card/paper-card";
|
|
||||||
import {
|
import {
|
||||||
css,
|
css,
|
||||||
CSSResult,
|
CSSResult,
|
||||||
@@ -11,15 +10,16 @@ import {
|
|||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
import { fireEvent } from "../../../src/common/dom/fire_event";
|
import { fireEvent } from "../../../src/common/dom/fire_event";
|
||||||
import "../../../src/components/buttons/ha-call-api-button";
|
import "../../../src/components/buttons/ha-call-api-button";
|
||||||
|
import "../../../src/components/ha-card";
|
||||||
import {
|
import {
|
||||||
HassioSupervisorInfo as HassioSupervisorInfoType,
|
HassioSupervisorInfo as HassioSupervisorInfoType,
|
||||||
setSupervisorOption,
|
setSupervisorOption,
|
||||||
SupervisorOptions,
|
SupervisorOptions,
|
||||||
} from "../../../src/data/hassio/supervisor";
|
} from "../../../src/data/hassio/supervisor";
|
||||||
|
import { showConfirmationDialog } from "../../../src/dialogs/generic/show-dialog-box";
|
||||||
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 { showConfirmationDialog } from "../../../src/dialogs/generic/show-dialog-box";
|
|
||||||
|
|
||||||
@customElement("hassio-supervisor-info")
|
@customElement("hassio-supervisor-info")
|
||||||
class HassioSupervisorInfo extends LitElement {
|
class HassioSupervisorInfo extends LitElement {
|
||||||
@@ -31,7 +31,7 @@ class HassioSupervisorInfo extends LitElement {
|
|||||||
|
|
||||||
public render(): TemplateResult | void {
|
public render(): TemplateResult | void {
|
||||||
return html`
|
return html`
|
||||||
<paper-card>
|
<ha-card>
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<h2>Supervisor</h2>
|
<h2>Supervisor</h2>
|
||||||
<table class="info">
|
<table class="info">
|
||||||
@@ -92,7 +92,7 @@ class HassioSupervisorInfo extends LitElement {
|
|||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
</div>
|
</div>
|
||||||
</paper-card>
|
</ha-card>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,7 +101,7 @@ class HassioSupervisorInfo extends LitElement {
|
|||||||
haStyle,
|
haStyle,
|
||||||
hassioStyle,
|
hassioStyle,
|
||||||
css`
|
css`
|
||||||
paper-card {
|
ha-card {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
@@ -2,7 +2,6 @@ import "@material/mwc-button";
|
|||||||
import "@polymer/paper-dropdown-menu/paper-dropdown-menu";
|
import "@polymer/paper-dropdown-menu/paper-dropdown-menu";
|
||||||
import "@polymer/paper-item/paper-item";
|
import "@polymer/paper-item/paper-item";
|
||||||
import "@polymer/paper-listbox/paper-listbox";
|
import "@polymer/paper-listbox/paper-listbox";
|
||||||
import "@polymer/paper-card/paper-card";
|
|
||||||
import {
|
import {
|
||||||
css,
|
css,
|
||||||
CSSResult,
|
CSSResult,
|
||||||
@@ -12,14 +11,13 @@ import {
|
|||||||
property,
|
property,
|
||||||
TemplateResult,
|
TemplateResult,
|
||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
|
import "../../../src/components/ha-card";
|
||||||
|
import { fetchHassioLogs } from "../../../src/data/hassio/supervisor";
|
||||||
|
import "../../../src/layouts/loading-screen";
|
||||||
import { haStyle } from "../../../src/resources/styles";
|
import { haStyle } from "../../../src/resources/styles";
|
||||||
import { HomeAssistant } from "../../../src/types";
|
import { HomeAssistant } from "../../../src/types";
|
||||||
|
|
||||||
import { fetchHassioLogs } from "../../../src/data/hassio/supervisor";
|
|
||||||
|
|
||||||
import "../components/hassio-ansi-to-html";
|
import "../components/hassio-ansi-to-html";
|
||||||
import { hassioStyle } from "../resources/hassio-style";
|
import { hassioStyle } from "../resources/hassio-style";
|
||||||
import "../../../src/layouts/loading-screen";
|
|
||||||
|
|
||||||
interface LogProvider {
|
interface LogProvider {
|
||||||
key: string;
|
key: string;
|
||||||
@@ -70,7 +68,7 @@ class HassioSupervisorLog extends LitElement {
|
|||||||
|
|
||||||
public render(): TemplateResult | void {
|
public render(): TemplateResult | void {
|
||||||
return html`
|
return html`
|
||||||
<paper-card>
|
<ha-card>
|
||||||
${this._error ? html` <div class="errors">${this._error}</div> ` : ""}
|
${this._error ? html` <div class="errors">${this._error}</div> ` : ""}
|
||||||
${this.hass.userData?.showAdvanced
|
${this.hass.userData?.showAdvanced
|
||||||
? html`
|
? html`
|
||||||
@@ -105,7 +103,7 @@ class HassioSupervisorLog extends LitElement {
|
|||||||
<div class="card-actions">
|
<div class="card-actions">
|
||||||
<mwc-button @click=${this._refresh}>Refresh</mwc-button>
|
<mwc-button @click=${this._refresh}>Refresh</mwc-button>
|
||||||
</div>
|
</div>
|
||||||
</paper-card>
|
</ha-card>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,7 +112,7 @@ class HassioSupervisorLog extends LitElement {
|
|||||||
haStyle,
|
haStyle,
|
||||||
hassioStyle,
|
hassioStyle,
|
||||||
css`
|
css`
|
||||||
paper-card {
|
ha-card {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
pre {
|
pre {
|
||||||
|
@@ -13,16 +13,15 @@ import {
|
|||||||
HassioHostInfo,
|
HassioHostInfo,
|
||||||
} from "../../../src/data/hassio/host";
|
} from "../../../src/data/hassio/host";
|
||||||
import { HassioSupervisorInfo } from "../../../src/data/hassio/supervisor";
|
import { HassioSupervisorInfo } from "../../../src/data/hassio/supervisor";
|
||||||
import { haStyle } from "../../../src/resources/styles";
|
|
||||||
import "../../../src/layouts/hass-tabs-subpage";
|
import "../../../src/layouts/hass-tabs-subpage";
|
||||||
|
import { haStyle } from "../../../src/resources/styles";
|
||||||
import { HomeAssistant, Route } from "../../../src/types";
|
import { HomeAssistant, Route } from "../../../src/types";
|
||||||
|
import { supervisorTabs } from "../hassio-panel";
|
||||||
import { hassioStyle } from "../resources/hassio-style";
|
import { hassioStyle } from "../resources/hassio-style";
|
||||||
import "./hassio-host-info";
|
import "./hassio-host-info";
|
||||||
import "./hassio-supervisor-info";
|
import "./hassio-supervisor-info";
|
||||||
import "./hassio-supervisor-log";
|
import "./hassio-supervisor-log";
|
||||||
|
|
||||||
import { supervisorTabs } from "../hassio-panel";
|
|
||||||
|
|
||||||
@customElement("hassio-system")
|
@customElement("hassio-system")
|
||||||
class HassioSystem extends LitElement {
|
class HassioSystem extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
90
package.json
@@ -24,25 +24,25 @@
|
|||||||
"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",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fullcalendar/core": "5.0.0-beta.2",
|
"@formatjs/intl-pluralrules": "^1.5.8",
|
||||||
"@fullcalendar/daygrid": "5.0.0-beta.2",
|
"@fullcalendar/core": "^5.0.0-beta.2",
|
||||||
"@material/chips": "^6.0.0-canary.35a32aaea.0",
|
"@fullcalendar/daygrid": "^5.0.0-beta.2",
|
||||||
"@material/mwc-button": "0.14.1",
|
"@material/chips": "7.0.0-canary.d92d8c93e.0",
|
||||||
"@material/mwc-checkbox": "0.14.1",
|
"@material/mwc-button": "^0.15.0",
|
||||||
"@material/mwc-dialog": "0.14.1",
|
"@material/mwc-checkbox": "^0.15.0",
|
||||||
"@material/mwc-fab": "0.14.1",
|
"@material/mwc-dialog": "^0.15.0",
|
||||||
"@material/mwc-formfield": "0.14.1",
|
"@material/mwc-fab": "^0.15.0",
|
||||||
"@material/mwc-icon-button": "0.14.1",
|
"@material/mwc-formfield": "^0.15.0",
|
||||||
"@material/mwc-list": "0.14.1",
|
"@material/mwc-icon-button": "^0.15.0",
|
||||||
"@material/mwc-menu": "0.14.1",
|
"@material/mwc-list": "^0.15.0",
|
||||||
"@material/mwc-ripple": "0.14.1",
|
"@material/mwc-menu": "^0.15.0",
|
||||||
"@material/mwc-switch": "0.14.1",
|
"@material/mwc-ripple": "^0.15.0",
|
||||||
|
"@material/mwc-switch": "^0.15.0",
|
||||||
"@mdi/js": "4.9.95",
|
"@mdi/js": "4.9.95",
|
||||||
"@mdi/svg": "4.9.95",
|
"@mdi/svg": "4.9.95",
|
||||||
"@polymer/app-layout": "^3.0.2",
|
"@polymer/app-layout": "^3.0.2",
|
||||||
"@polymer/app-route": "^3.0.2",
|
"@polymer/app-route": "^3.0.2",
|
||||||
"@polymer/app-storage": "^3.0.2",
|
"@polymer/app-storage": "^3.0.2",
|
||||||
"@polymer/font-roboto": "^3.0.2",
|
|
||||||
"@polymer/iron-autogrow-textarea": "^3.0.1",
|
"@polymer/iron-autogrow-textarea": "^3.0.1",
|
||||||
"@polymer/iron-flex-layout": "^3.0.1",
|
"@polymer/iron-flex-layout": "^3.0.1",
|
||||||
"@polymer/iron-icon": "^3.0.1",
|
"@polymer/iron-icon": "^3.0.1",
|
||||||
@@ -51,15 +51,12 @@
|
|||||||
"@polymer/iron-label": "^3.0.1",
|
"@polymer/iron-label": "^3.0.1",
|
||||||
"@polymer/iron-media-query": "^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-pages": "^3.0.1",
|
|
||||||
"@polymer/iron-resizable-behavior": "^3.0.1",
|
"@polymer/iron-resizable-behavior": "^3.0.1",
|
||||||
"@polymer/neon-animation": "^3.0.1",
|
|
||||||
"@polymer/paper-card": "^3.0.1",
|
"@polymer/paper-card": "^3.0.1",
|
||||||
"@polymer/paper-checkbox": "^3.1.0",
|
"@polymer/paper-checkbox": "^3.1.0",
|
||||||
"@polymer/paper-dialog": "^3.0.1",
|
"@polymer/paper-dialog": "^3.0.1",
|
||||||
"@polymer/paper-dialog-behavior": "^3.0.1",
|
"@polymer/paper-dialog-behavior": "^3.0.1",
|
||||||
"@polymer/paper-dialog-scrollable": "^3.0.1",
|
"@polymer/paper-dialog-scrollable": "^3.0.1",
|
||||||
"@polymer/paper-drawer-panel": "^3.0.1",
|
|
||||||
"@polymer/paper-dropdown-menu": "^3.0.1",
|
"@polymer/paper-dropdown-menu": "^3.0.1",
|
||||||
"@polymer/paper-input": "^3.0.1",
|
"@polymer/paper-input": "^3.0.1",
|
||||||
"@polymer/paper-item": "^3.0.1",
|
"@polymer/paper-item": "^3.0.1",
|
||||||
@@ -69,7 +66,6 @@
|
|||||||
"@polymer/paper-radio-button": "^3.0.1",
|
"@polymer/paper-radio-button": "^3.0.1",
|
||||||
"@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-scroll-header-panel": "^3.0.1",
|
|
||||||
"@polymer/paper-slider": "^3.0.1",
|
"@polymer/paper-slider": "^3.0.1",
|
||||||
"@polymer/paper-spinner": "^3.0.2",
|
"@polymer/paper-spinner": "^3.0.2",
|
||||||
"@polymer/paper-styles": "^3.0.1",
|
"@polymer/paper-styles": "^3.0.1",
|
||||||
@@ -77,14 +73,14 @@
|
|||||||
"@polymer/paper-toast": "^3.0.1",
|
"@polymer/paper-toast": "^3.0.1",
|
||||||
"@polymer/paper-tooltip": "^3.0.1",
|
"@polymer/paper-tooltip": "^3.0.1",
|
||||||
"@polymer/polymer": "3.1.0",
|
"@polymer/polymer": "3.1.0",
|
||||||
"@thomasloven/round-slider": "0.3.7",
|
"@thomasloven/round-slider": "0.4.1",
|
||||||
"@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",
|
||||||
"@webcomponents/shadycss": "^1.9.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",
|
||||||
"codemirror": "^5.49.0",
|
"codemirror": "^5.49.0",
|
||||||
|
"comlink": "^4.3.0",
|
||||||
"cpx": "^1.5.0",
|
"cpx": "^1.5.0",
|
||||||
"deep-clone-simple": "^1.1.1",
|
"deep-clone-simple": "^1.1.1",
|
||||||
"deep-freeze": "^0.0.1",
|
"deep-freeze": "^0.0.1",
|
||||||
@@ -93,27 +89,30 @@
|
|||||||
"fuse.js": "^3.4.4",
|
"fuse.js": "^3.4.4",
|
||||||
"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.0.0",
|
"home-assistant-js-websocket": "^5.1.2",
|
||||||
"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",
|
||||||
"leaflet": "^1.4.0",
|
"leaflet": "^1.4.0",
|
||||||
"leaflet-draw": "^1.0.4",
|
"leaflet-draw": "^1.0.4",
|
||||||
"lit-element": "^2.2.1",
|
"lit-element": "^2.3.1",
|
||||||
"lit-html": "^1.1.0",
|
"lit-html": "^1.2.1",
|
||||||
"lit-virtualizer": "^0.4.2",
|
"lit-virtualizer": "^0.4.2",
|
||||||
"marked": "^0.6.1",
|
"marked": "^0.6.1",
|
||||||
"mdn-polyfills": "^5.16.0",
|
"mdn-polyfills": "^5.16.0",
|
||||||
"memoize-one": "^5.0.2",
|
"memoize-one": "^5.0.2",
|
||||||
"moment": "^2.24.0",
|
|
||||||
"node-vibrant": "^3.1.5",
|
"node-vibrant": "^3.1.5",
|
||||||
|
"proxy-polyfill": "^0.3.1",
|
||||||
"regenerator-runtime": "^0.13.2",
|
"regenerator-runtime": "^0.13.2",
|
||||||
"resize-observer": "^1.0.0",
|
"resize-observer": "^1.0.0",
|
||||||
"roboto-fontface": "^0.10.0",
|
"roboto-fontface": "^0.10.0",
|
||||||
"superstruct": "^0.6.1",
|
"superstruct": "^0.6.1",
|
||||||
"tslib": "^1.10.0",
|
|
||||||
"unfetch": "^4.1.0",
|
"unfetch": "^4.1.0",
|
||||||
"web-animations-js": "^2.3.2",
|
"web-animations-js": "^2.3.2",
|
||||||
|
"workbox-core": "^5.1.3",
|
||||||
|
"workbox-precaching": "^5.1.3",
|
||||||
|
"workbox-routing": "^5.1.3",
|
||||||
|
"workbox-strategies": "^5.1.3",
|
||||||
"xss": "^1.0.6"
|
"xss": "^1.0.6"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@@ -142,7 +141,6 @@
|
|||||||
"@typescript-eslint/parser": "^2.28.0",
|
"@typescript-eslint/parser": "^2.28.0",
|
||||||
"babel-loader": "^8.1.0",
|
"babel-loader": "^8.1.0",
|
||||||
"chai": "^4.2.0",
|
"chai": "^4.2.0",
|
||||||
"copy-webpack-plugin": "^5.0.2",
|
|
||||||
"del": "^4.0.0",
|
"del": "^4.0.0",
|
||||||
"eslint": "^6.8.0",
|
"eslint": "^6.8.0",
|
||||||
"eslint-config-airbnb-typescript": "^7.2.1",
|
"eslint-config-airbnb-typescript": "^7.2.1",
|
||||||
@@ -153,16 +151,16 @@
|
|||||||
"eslint-plugin-lit": "^1.2.0",
|
"eslint-plugin-lit": "^1.2.0",
|
||||||
"eslint-plugin-prettier": "^3.1.3",
|
"eslint-plugin-prettier": "^3.1.3",
|
||||||
"eslint-plugin-wc": "^1.2.0",
|
"eslint-plugin-wc": "^1.2.0",
|
||||||
|
"fancy-log": "^1.3.3",
|
||||||
"fs-extra": "^7.0.1",
|
"fs-extra": "^7.0.1",
|
||||||
"gulp": "^4.0.0",
|
"gulp": "^4.0.0",
|
||||||
"gulp-foreach": "^0.1.0",
|
"gulp-foreach": "^0.1.0",
|
||||||
"gulp-insert": "^0.5.0",
|
|
||||||
"gulp-json-transform": "^0.4.6",
|
"gulp-json-transform": "^0.4.6",
|
||||||
"gulp-jsonminify": "^1.1.0",
|
"gulp-jsonminify": "^1.1.0",
|
||||||
"gulp-merge-json": "^1.3.1",
|
"gulp-merge-json": "^1.3.1",
|
||||||
"gulp-rename": "^2.0.0",
|
"gulp-rename": "^2.0.0",
|
||||||
"gulp-zopfli-green": "^3.0.1",
|
"gulp-zopfli-green": "^3.0.1",
|
||||||
"html-webpack-plugin": "^3.2.0",
|
"html-minifier": "^4.0.0",
|
||||||
"husky": "^1.3.1",
|
"husky": "^1.3.1",
|
||||||
"lint-staged": "^8.1.5",
|
"lint-staged": "^8.1.5",
|
||||||
"lit-analyzer": "^1.1.10",
|
"lit-analyzer": "^1.1.10",
|
||||||
@@ -171,12 +169,11 @@
|
|||||||
"merge-stream": "^1.0.1",
|
"merge-stream": "^1.0.1",
|
||||||
"mocha": "^6.0.2",
|
"mocha": "^6.0.2",
|
||||||
"object-hash": "^2.0.3",
|
"object-hash": "^2.0.3",
|
||||||
"parse5": "^5.1.0",
|
|
||||||
"prettier": "^2.0.4",
|
"prettier": "^2.0.4",
|
||||||
"raw-loader": "^2.0.0",
|
"raw-loader": "^2.0.0",
|
||||||
"reify": "^0.18.1",
|
|
||||||
"require-dir": "^1.2.0",
|
"require-dir": "^1.2.0",
|
||||||
"sinon": "^7.3.1",
|
"sinon": "^7.3.1",
|
||||||
|
"source-map-url": "^0.4.0",
|
||||||
"terser-webpack-plugin": "^1.2.3",
|
"terser-webpack-plugin": "^1.2.3",
|
||||||
"ts-lit-plugin": "^1.1.10",
|
"ts-lit-plugin": "^1.1.10",
|
||||||
"ts-mocha": "^6.0.0",
|
"ts-mocha": "^6.0.0",
|
||||||
@@ -188,28 +185,29 @@
|
|||||||
"webpack-cli": "^3.3.9",
|
"webpack-cli": "^3.3.9",
|
||||||
"webpack-dev-server": "^3.10.3",
|
"webpack-dev-server": "^3.10.3",
|
||||||
"webpack-manifest-plugin": "^2.0.4",
|
"webpack-manifest-plugin": "^2.0.4",
|
||||||
"workbox-webpack-plugin": "^4.1.1",
|
"workbox-build": "^5.1.3",
|
||||||
"workerize-loader": "^1.1.0"
|
"worker-plugin": "^4.0.3"
|
||||||
},
|
},
|
||||||
"_comment": "Polymer fixed to 3.1 because 3.2 throws on logbook page",
|
"_comment": "Polymer fixed to 3.1 because 3.2 throws on logbook page",
|
||||||
"_comment_2": "Fix in https://github.com/Polymer/polymer/pull/5569",
|
"_comment_2": "Fix in https://github.com/Polymer/polymer/pull/5569",
|
||||||
"resolutions": {
|
"resolutions": {
|
||||||
"@webcomponents/webcomponentsjs": "^2.2.10",
|
"@webcomponents/webcomponentsjs": "^2.2.10",
|
||||||
"@polymer/polymer": "3.1.0",
|
"@polymer/polymer": "3.1.0",
|
||||||
"lit-html": "^1.1.2",
|
"lit-html": "1.2.1",
|
||||||
"@material/animation": "6.0.0",
|
"lit-element": "2.3.1",
|
||||||
"@material/base": "6.0.0",
|
"@material/animation": "7.0.0-canary.d92d8c93e.0",
|
||||||
"@material/checkbox": "6.0.0",
|
"@material/base": "7.0.0-canary.d92d8c93e.0",
|
||||||
"@material/density": "6.0.0",
|
"@material/checkbox": "7.0.0-canary.d92d8c93e.0",
|
||||||
"@material/dom": "6.0.0",
|
"@material/density": "7.0.0-canary.d92d8c93e.0",
|
||||||
"@material/elevation": "6.0.0",
|
"@material/dom": "7.0.0-canary.d92d8c93e.0",
|
||||||
"@material/feature-targeting": "6.0.0",
|
"@material/elevation": "7.0.0-canary.d92d8c93e.0",
|
||||||
"@material/ripple": "6.0.0",
|
"@material/feature-targeting": "7.0.0-canary.d92d8c93e.0",
|
||||||
"@material/rtl": "6.0.0",
|
"@material/ripple": "7.0.0-canary.d92d8c93e.0",
|
||||||
"@material/shape": "6.0.0",
|
"@material/rtl": "7.0.0-canary.d92d8c93e.0",
|
||||||
"@material/theme": "6.0.0",
|
"@material/shape": "7.0.0-canary.d92d8c93e.0",
|
||||||
"@material/touch-target": "6.0.0",
|
"@material/theme": "7.0.0-canary.d92d8c93e.0",
|
||||||
"@material/typography": "6.0.0"
|
"@material/touch-target": "7.0.0-canary.d92d8c93e.0",
|
||||||
|
"@material/typography": "7.0.0-canary.d92d8c93e.0"
|
||||||
},
|
},
|
||||||
"main": "src/home-assistant.js",
|
"main": "src/home-assistant.js",
|
||||||
"husky": {
|
"husky": {
|
||||||
|
Before Width: | Height: | Size: 655 B |
Before Width: | Height: | Size: 888 B |
Before Width: | Height: | Size: 807 B |
Before Width: | Height: | Size: 639 B |
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 840 B |
Before Width: | Height: | Size: 798 B |
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 487 B |
Before Width: | Height: | Size: 774 B |
2
setup.py
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
|
|||||||
|
|
||||||
setup(
|
setup(
|
||||||
name="home-assistant-frontend",
|
name="home-assistant-frontend",
|
||||||
version="20200505.0",
|
version="20200519.3",
|
||||||
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",
|
||||||
|
@@ -121,7 +121,7 @@ class HaAuthorize extends litLocalizeLiteMixin(LitElement) {
|
|||||||
const tempA = document.createElement("a");
|
const tempA = document.createElement("a");
|
||||||
tempA.href = this.redirectUri!;
|
tempA.href = this.redirectUri!;
|
||||||
if (tempA.host === location.host) {
|
if (tempA.host === location.host) {
|
||||||
registerServiceWorker(false);
|
registerServiceWorker(this, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,4 +1,3 @@
|
|||||||
import "../../components/ha-icon-button";
|
|
||||||
import "@polymer/paper-input/paper-input";
|
import "@polymer/paper-input/paper-input";
|
||||||
import {
|
import {
|
||||||
css,
|
css,
|
||||||
@@ -9,8 +8,10 @@ import {
|
|||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
import { html, TemplateResult } from "lit-html";
|
import { html, TemplateResult } from "lit-html";
|
||||||
import { classMap } from "lit-html/directives/class-map";
|
import { classMap } from "lit-html/directives/class-map";
|
||||||
import "../../components/ha-icon";
|
import "../../components/ha-svg-icon";
|
||||||
import { fireEvent } from "../dom/fire_event";
|
import { fireEvent } from "../dom/fire_event";
|
||||||
|
import { mdiMagnify, mdiClose } from "@mdi/js";
|
||||||
|
import "@material/mwc-icon-button/mwc-icon-button";
|
||||||
|
|
||||||
@customElement("search-input")
|
@customElement("search-input")
|
||||||
class SearchInput extends LitElement {
|
class SearchInput extends LitElement {
|
||||||
@@ -47,17 +48,22 @@ class SearchInput extends LitElement {
|
|||||||
@value-changed=${this._filterInputChanged}
|
@value-changed=${this._filterInputChanged}
|
||||||
.noLabelFloat=${this.noLabelFloat}
|
.noLabelFloat=${this.noLabelFloat}
|
||||||
>
|
>
|
||||||
<ha-icon icon="hass:magnify" slot="prefix" class="prefix"></ha-icon>
|
<ha-svg-icon
|
||||||
|
path=${mdiMagnify}
|
||||||
|
slot="prefix"
|
||||||
|
class="prefix"
|
||||||
|
></ha-svg-icon>
|
||||||
${this.filter &&
|
${this.filter &&
|
||||||
html`
|
html`
|
||||||
<ha-icon-button
|
<mwc-icon-button
|
||||||
slot="suffix"
|
slot="suffix"
|
||||||
class="suffix"
|
class="suffix"
|
||||||
@click=${this._clearSearch}
|
@click=${this._clearSearch}
|
||||||
icon="hass:close"
|
|
||||||
alt="Clear"
|
alt="Clear"
|
||||||
title="Clear"
|
title="Clear"
|
||||||
></ha-icon-button>
|
>
|
||||||
|
<ha-svg-icon path=${mdiClose}></ha-svg-icon>
|
||||||
|
</mwc-icon-button>
|
||||||
`}
|
`}
|
||||||
</paper-input>
|
</paper-input>
|
||||||
`;
|
`;
|
||||||
@@ -77,11 +83,14 @@ class SearchInput extends LitElement {
|
|||||||
|
|
||||||
static get styles(): CSSResult {
|
static get styles(): CSSResult {
|
||||||
return css`
|
return css`
|
||||||
ha-icon,
|
ha-svg-icon,
|
||||||
ha-icon-button {
|
mwc-icon-button {
|
||||||
color: var(--primary-text-color);
|
color: var(--primary-text-color);
|
||||||
}
|
}
|
||||||
ha-icon {
|
mwc-icon-button {
|
||||||
|
--mdc-icon-button-size: 24px;
|
||||||
|
}
|
||||||
|
ha-svg-icon.prefix {
|
||||||
margin: 8px;
|
margin: 8px;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
@@ -12,6 +12,10 @@ export interface FormatsType {
|
|||||||
time: FormatType;
|
time: FormatType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!Intl.PluralRules) {
|
||||||
|
import("@formatjs/intl-pluralrules/polyfill-locales");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adapted from Polymer app-localize-behavior.
|
* Adapted from Polymer app-localize-behavior.
|
||||||
*
|
*
|
||||||
|
@@ -14,9 +14,6 @@ import { classMap } from "lit-html/directives/class-map";
|
|||||||
import { ifDefined } from "lit-html/directives/if-defined";
|
import { ifDefined } from "lit-html/directives/if-defined";
|
||||||
import { styleMap } from "lit-html/directives/style-map";
|
import { styleMap } from "lit-html/directives/style-map";
|
||||||
import { scroll } from "lit-virtualizer";
|
import { scroll } from "lit-virtualizer";
|
||||||
// @ts-ignore
|
|
||||||
// eslint-disable-next-line import/no-webpack-loader-syntax
|
|
||||||
import sortFilterWorker from "workerize-loader!./sort_filter_worker";
|
|
||||||
import { fireEvent } from "../../common/dom/fire_event";
|
import { fireEvent } from "../../common/dom/fire_event";
|
||||||
import "../../common/search/search-input";
|
import "../../common/search/search-input";
|
||||||
import { debounce } from "../../common/util/debounce";
|
import { debounce } from "../../common/util/debounce";
|
||||||
@@ -24,6 +21,8 @@ import { nextRender } from "../../common/util/render-status";
|
|||||||
import "../ha-checkbox";
|
import "../ha-checkbox";
|
||||||
import type { HaCheckbox } from "../ha-checkbox";
|
import type { HaCheckbox } from "../ha-checkbox";
|
||||||
import "../ha-icon";
|
import "../ha-icon";
|
||||||
|
import { filterData, sortData } from "./sort-filter";
|
||||||
|
import memoizeOne from "memoize-one";
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
// for fire event
|
// for fire event
|
||||||
@@ -74,6 +73,10 @@ export interface DataTableRowData {
|
|||||||
selectable?: boolean;
|
selectable?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface SortableColumnContainer {
|
||||||
|
[key: string]: DataTableSortColumnData;
|
||||||
|
}
|
||||||
|
|
||||||
@customElement("ha-data-table")
|
@customElement("ha-data-table")
|
||||||
export class HaDataTable extends LitElement {
|
export class HaDataTable extends LitElement {
|
||||||
@property({ type: Object }) public columns: DataTableColumnContainer = {};
|
@property({ type: Object }) public columns: DataTableColumnContainer = {};
|
||||||
@@ -111,14 +114,10 @@ export class HaDataTable extends LitElement {
|
|||||||
|
|
||||||
private _checkedRows: string[] = [];
|
private _checkedRows: string[] = [];
|
||||||
|
|
||||||
private _sortColumns: {
|
private _sortColumns: SortableColumnContainer = {};
|
||||||
[key: string]: DataTableSortColumnData;
|
|
||||||
} = {};
|
|
||||||
|
|
||||||
private curRequest = 0;
|
private curRequest = 0;
|
||||||
|
|
||||||
private _worker: any | undefined;
|
|
||||||
|
|
||||||
private _debounceSearch = debounce(
|
private _debounceSearch = debounce(
|
||||||
(value: string) => {
|
(value: string) => {
|
||||||
this._filter = value;
|
this._filter = value;
|
||||||
@@ -140,11 +139,6 @@ export class HaDataTable extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected firstUpdated(properties: PropertyValues) {
|
|
||||||
super.firstUpdated(properties);
|
|
||||||
this._worker = sortFilterWorker();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected updated(properties: PropertyValues) {
|
protected updated(properties: PropertyValues) {
|
||||||
super.updated(properties);
|
super.updated(properties);
|
||||||
|
|
||||||
@@ -188,7 +182,7 @@ export class HaDataTable extends LitElement {
|
|||||||
properties.has("_sortColumn") ||
|
properties.has("_sortColumn") ||
|
||||||
properties.has("_sortDirection")
|
properties.has("_sortDirection")
|
||||||
) {
|
) {
|
||||||
this._filterData();
|
this._sortFilterData();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -378,20 +372,30 @@ export class HaDataTable extends LitElement {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _filterData() {
|
private async _sortFilterData() {
|
||||||
const startTime = new Date().getTime();
|
const startTime = new Date().getTime();
|
||||||
this.curRequest++;
|
this.curRequest++;
|
||||||
const curRequest = this.curRequest;
|
const curRequest = this.curRequest;
|
||||||
|
|
||||||
const filterProm = this._worker.filterSortData(
|
let filteredData = this.data;
|
||||||
this.data,
|
if (this._filter) {
|
||||||
this._sortColumns,
|
filteredData = await this._memFilterData(
|
||||||
this._filter,
|
this.data,
|
||||||
this._sortDirection,
|
this._sortColumns,
|
||||||
this._sortColumn
|
this._filter
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const [data] = await Promise.all([filterProm, nextRender]);
|
const prom = this._sortColumn
|
||||||
|
? sortData(
|
||||||
|
filteredData,
|
||||||
|
this._sortColumns,
|
||||||
|
this._sortDirection,
|
||||||
|
this._sortColumn
|
||||||
|
)
|
||||||
|
: filteredData;
|
||||||
|
|
||||||
|
const [data] = await Promise.all([prom, nextRender]);
|
||||||
|
|
||||||
const curTime = new Date().getTime();
|
const curTime = new Date().getTime();
|
||||||
const elapsed = curTime - startTime;
|
const elapsed = curTime - startTime;
|
||||||
@@ -405,6 +409,16 @@ export class HaDataTable extends LitElement {
|
|||||||
this._filteredData = data;
|
this._filteredData = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _memFilterData = memoizeOne(
|
||||||
|
async (
|
||||||
|
data: DataTableRowData[],
|
||||||
|
columns: SortableColumnContainer,
|
||||||
|
filter: string
|
||||||
|
): Promise<DataTableRowData[]> => {
|
||||||
|
return filterData(data, columns, filter);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
private _handleHeaderClick(ev: Event) {
|
private _handleHeaderClick(ev: Event) {
|
||||||
const columnId = ((ev.target as HTMLElement).closest(
|
const columnId = ((ev.target as HTMLElement).closest(
|
||||||
".mdc-data-table__header-cell"
|
".mdc-data-table__header-cell"
|
||||||
@@ -646,6 +660,11 @@ export class HaDataTable extends LitElement {
|
|||||||
padding: 8px;
|
padding: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mdc-data-table__cell--icon-button {
|
||||||
|
color: var(--secondary-text-color);
|
||||||
|
text-overflow: clip;
|
||||||
|
}
|
||||||
|
|
||||||
.mdc-data-table__header-cell--icon-button:first-child,
|
.mdc-data-table__header-cell--icon-button:first-child,
|
||||||
.mdc-data-table__cell--icon-button:first-child {
|
.mdc-data-table__cell--icon-button:first-child {
|
||||||
width: 64px;
|
width: 64px;
|
||||||
@@ -659,7 +678,7 @@ export class HaDataTable extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.mdc-data-table__cell--icon-button a {
|
.mdc-data-table__cell--icon-button a {
|
||||||
color: var(--primary-text-color);
|
color: var(--secondary-text-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.mdc-data-table__header-cell {
|
.mdc-data-table__header-cell {
|
||||||
|
34
src/components/data-table/sort-filter.ts
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import { wrap } from "comlink";
|
||||||
|
|
||||||
|
type FilterDataType = typeof import("./sort_filter_worker").api["filterData"];
|
||||||
|
type FilterDataParamTypes = Parameters<FilterDataType>;
|
||||||
|
|
||||||
|
type SortDataType = typeof import("./sort_filter_worker").api["sortData"];
|
||||||
|
type SortDataParamTypes = Parameters<SortDataType>;
|
||||||
|
|
||||||
|
let worker: any | undefined;
|
||||||
|
|
||||||
|
export const filterData = async (
|
||||||
|
data: FilterDataParamTypes[0],
|
||||||
|
columns: FilterDataParamTypes[1],
|
||||||
|
filter: FilterDataParamTypes[2]
|
||||||
|
): Promise<ReturnType<FilterDataType>> => {
|
||||||
|
if (!worker) {
|
||||||
|
worker = wrap(new Worker("./sort_filter_worker", { type: "module" }));
|
||||||
|
}
|
||||||
|
|
||||||
|
return await worker.filterData(data, columns, filter);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const sortData = async (
|
||||||
|
data: SortDataParamTypes[0],
|
||||||
|
columns: SortDataParamTypes[1],
|
||||||
|
direction: SortDataParamTypes[2],
|
||||||
|
sortColumn: SortDataParamTypes[3]
|
||||||
|
): Promise<ReturnType<SortDataType>> => {
|
||||||
|
if (!worker) {
|
||||||
|
worker = wrap(new Worker("./sort_filter_worker", { type: "module" }));
|
||||||
|
}
|
||||||
|
|
||||||
|
return await worker.sortData(data, columns, direction, sortColumn);
|
||||||
|
};
|
@@ -1,60 +1,20 @@
|
|||||||
import memoizeOne from "memoize-one";
|
// To use comlink under ES5
|
||||||
// eslint-disable-next-line import/no-cycle
|
import "proxy-polyfill";
|
||||||
import {
|
import { expose } from "comlink";
|
||||||
DataTableColumnContainer,
|
import type {
|
||||||
DataTableColumnData,
|
DataTableSortColumnData,
|
||||||
DataTableRowData,
|
DataTableRowData,
|
||||||
SortingDirection,
|
SortingDirection,
|
||||||
|
SortableColumnContainer,
|
||||||
} from "./ha-data-table";
|
} from "./ha-data-table";
|
||||||
|
|
||||||
export const filterSortData = memoizeOne(
|
const filterData = (
|
||||||
async (
|
|
||||||
data: DataTableRowData[],
|
|
||||||
columns: DataTableColumnContainer,
|
|
||||||
filter: string,
|
|
||||||
direction: SortingDirection,
|
|
||||||
sortColumn?: string
|
|
||||||
) =>
|
|
||||||
sortColumn
|
|
||||||
? _memSortData(
|
|
||||||
await _memFilterData(data, columns, filter),
|
|
||||||
columns,
|
|
||||||
direction,
|
|
||||||
sortColumn
|
|
||||||
)
|
|
||||||
: _memFilterData(data, columns, filter)
|
|
||||||
);
|
|
||||||
|
|
||||||
const _memFilterData = memoizeOne(
|
|
||||||
async (
|
|
||||||
data: DataTableRowData[],
|
|
||||||
columns: DataTableColumnContainer,
|
|
||||||
filter: string
|
|
||||||
) => {
|
|
||||||
if (!filter) {
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
return filterData(data, columns, filter.toUpperCase());
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const _memSortData = memoizeOne(
|
|
||||||
(
|
|
||||||
data: DataTableRowData[],
|
|
||||||
columns: DataTableColumnContainer,
|
|
||||||
direction: SortingDirection,
|
|
||||||
sortColumn: string
|
|
||||||
) => {
|
|
||||||
return sortData(data, columns[sortColumn], direction, sortColumn);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
export const filterData = (
|
|
||||||
data: DataTableRowData[],
|
data: DataTableRowData[],
|
||||||
columns: DataTableColumnContainer,
|
columns: SortableColumnContainer,
|
||||||
filter: string
|
filter: string
|
||||||
) =>
|
) => {
|
||||||
data.filter((row) => {
|
filter = filter.toUpperCase();
|
||||||
|
return data.filter((row) => {
|
||||||
return Object.entries(columns).some((columnEntry) => {
|
return Object.entries(columns).some((columnEntry) => {
|
||||||
const [key, column] = columnEntry;
|
const [key, column] = columnEntry;
|
||||||
if (column.filterable) {
|
if (column.filterable) {
|
||||||
@@ -69,10 +29,11 @@ export const filterData = (
|
|||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
};
|
||||||
|
|
||||||
export const sortData = (
|
const sortData = (
|
||||||
data: DataTableRowData[],
|
data: DataTableRowData[],
|
||||||
column: DataTableColumnData,
|
column: DataTableSortColumnData,
|
||||||
direction: SortingDirection,
|
direction: SortingDirection,
|
||||||
sortColumn: string
|
sortColumn: string
|
||||||
) =>
|
) =>
|
||||||
@@ -105,3 +66,11 @@ export const sortData = (
|
|||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Export for types
|
||||||
|
export const api = {
|
||||||
|
filterData,
|
||||||
|
sortData,
|
||||||
|
};
|
||||||
|
|
||||||
|
expose(api);
|
||||||
|
@@ -38,15 +38,14 @@ const rowRenderer = (
|
|||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<paper-icon-item>
|
<paper-icon-item>
|
||||||
<state-badge state-obj="[[item]]" slot="item-icon"></state-badge>
|
<state-badge slot="item-icon"></state-badge>
|
||||||
<paper-item-body two-line="">
|
<paper-item-body two-line="">
|
||||||
<div class='name'>[[_computeStateName(item)]]</div>
|
<div class='name'></div>
|
||||||
<div secondary>[[item.entity_id]]</div>
|
<div secondary></div>
|
||||||
</paper-item-body>
|
</paper-item-body>
|
||||||
</paper-icon-item>
|
</paper-icon-item>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
root.querySelector("state-badge")!.stateObj = model.item;
|
root.querySelector("state-badge")!.stateObj = model.item;
|
||||||
root.querySelector(".name")!.textContent = computeStateName(model.item);
|
root.querySelector(".name")!.textContent = computeStateName(model.item);
|
||||||
root.querySelector("[secondary]")!.textContent = model.item.entity_id;
|
root.querySelector("[secondary]")!.textContent = model.item.entity_id;
|
||||||
@@ -148,6 +147,10 @@ class HaEntityPicker extends LitElement {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
protected shouldUpdate(changedProps: PropertyValues) {
|
||||||
|
return !(!changedProps.has("_opened") && this._opened);
|
||||||
|
}
|
||||||
|
|
||||||
protected updated(changedProps: PropertyValues) {
|
protected updated(changedProps: PropertyValues) {
|
||||||
if (changedProps.has("_opened") && this._opened) {
|
if (changedProps.has("_opened") && this._opened) {
|
||||||
const states = this._getStates(
|
const states = this._getStates(
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
import type { HassEntity } from "home-assistant-js-websocket";
|
import type { HassEntity } from "home-assistant-js-websocket";
|
||||||
|
import { styleMap } from "lit-html/directives/style-map";
|
||||||
import {
|
import {
|
||||||
css,
|
css,
|
||||||
CSSResult,
|
CSSResult,
|
||||||
@@ -6,7 +7,6 @@ import {
|
|||||||
LitElement,
|
LitElement,
|
||||||
property,
|
property,
|
||||||
PropertyValues,
|
PropertyValues,
|
||||||
query,
|
|
||||||
TemplateResult,
|
TemplateResult,
|
||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
import { ifDefined } from "lit-html/directives/if-defined";
|
import { ifDefined } from "lit-html/directives/if-defined";
|
||||||
@@ -16,7 +16,6 @@ import { stateIcon } from "../../common/entity/state_icon";
|
|||||||
import { iconColorCSS } from "../../common/style/icon_color_css";
|
import { iconColorCSS } from "../../common/style/icon_color_css";
|
||||||
import type { HomeAssistant } from "../../types";
|
import type { HomeAssistant } from "../../types";
|
||||||
import "../ha-icon";
|
import "../ha-icon";
|
||||||
import type { HaIcon } from "../ha-icon";
|
|
||||||
|
|
||||||
export class StateBadge extends LitElement {
|
export class StateBadge extends LitElement {
|
||||||
public hass?: HomeAssistant;
|
public hass?: HomeAssistant;
|
||||||
@@ -29,12 +28,15 @@ export class StateBadge extends LitElement {
|
|||||||
|
|
||||||
@property({ type: Boolean }) public stateColor?: boolean;
|
@property({ type: Boolean }) public stateColor?: boolean;
|
||||||
|
|
||||||
@query("ha-icon") private _icon!: HaIcon;
|
@property({ type: Boolean, reflect: true, attribute: "icon" })
|
||||||
|
private _showIcon = true;
|
||||||
|
|
||||||
|
@property() private _iconStyle: { [name: string]: string } = {};
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
const stateObj = this.stateObj;
|
const stateObj = this.stateObj;
|
||||||
|
|
||||||
if (!stateObj) {
|
if (!stateObj || !this._showIcon) {
|
||||||
return html``;
|
return html``;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,7 +44,7 @@ export class StateBadge extends LitElement {
|
|||||||
|
|
||||||
return html`
|
return html`
|
||||||
<ha-icon
|
<ha-icon
|
||||||
id="icon"
|
style=${styleMap(this._iconStyle)}
|
||||||
data-domain=${ifDefined(
|
data-domain=${ifDefined(
|
||||||
this.stateColor || (domain === "light" && this.stateColor !== false)
|
this.stateColor || (domain === "light" && this.stateColor !== false)
|
||||||
? domain
|
? domain
|
||||||
@@ -60,14 +62,13 @@ export class StateBadge extends LitElement {
|
|||||||
}
|
}
|
||||||
const stateObj = this.stateObj;
|
const stateObj = this.stateObj;
|
||||||
|
|
||||||
const iconStyle: Partial<CSSStyleDeclaration> = {
|
const iconStyle: { [name: string]: string } = {};
|
||||||
color: "",
|
|
||||||
filter: "",
|
|
||||||
display: "",
|
|
||||||
};
|
|
||||||
const hostStyle: Partial<CSSStyleDeclaration> = {
|
const hostStyle: Partial<CSSStyleDeclaration> = {
|
||||||
backgroundImage: "",
|
backgroundImage: "",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this._showIcon = true;
|
||||||
|
|
||||||
if (stateObj) {
|
if (stateObj) {
|
||||||
// hide icon if we have entity picture
|
// hide icon if we have entity picture
|
||||||
if (
|
if (
|
||||||
@@ -79,7 +80,7 @@ export class StateBadge extends LitElement {
|
|||||||
imageUrl = this.hass.hassUrl(imageUrl);
|
imageUrl = this.hass.hassUrl(imageUrl);
|
||||||
}
|
}
|
||||||
hostStyle.backgroundImage = `url(${imageUrl})`;
|
hostStyle.backgroundImage = `url(${imageUrl})`;
|
||||||
iconStyle.display = "none";
|
this._showIcon = false;
|
||||||
} else if (stateObj.state === "on") {
|
} else if (stateObj.state === "on") {
|
||||||
if (stateObj.attributes.hs_color && this.stateColor !== false) {
|
if (stateObj.attributes.hs_color && this.stateColor !== false) {
|
||||||
const hue = stateObj.attributes.hs_color[0];
|
const hue = stateObj.attributes.hs_color[0];
|
||||||
@@ -102,7 +103,7 @@ export class StateBadge extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Object.assign(this._icon.style, iconStyle);
|
this._iconStyle = iconStyle;
|
||||||
Object.assign(this.style, hostStyle);
|
Object.assign(this.style, hostStyle);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,8 +120,17 @@ export class StateBadge extends LitElement {
|
|||||||
background-size: cover;
|
background-size: cover;
|
||||||
line-height: 40px;
|
line-height: 40px;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
:host(:focus) {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
:host(:not([icon]):focus) {
|
||||||
|
border: 2px solid var(--divider-color);
|
||||||
|
}
|
||||||
|
:host([icon]:focus) {
|
||||||
|
background: var(--divider-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
ha-icon {
|
ha-icon {
|
||||||
transition: color 0.3s ease-in-out, filter 0.3s ease-in-out;
|
transition: color 0.3s ease-in-out, filter 0.3s ease-in-out;
|
||||||
}
|
}
|
||||||
|
@@ -6,18 +6,19 @@ import {
|
|||||||
CSSResult,
|
CSSResult,
|
||||||
css,
|
css,
|
||||||
query,
|
query,
|
||||||
|
property,
|
||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
import "@material/mwc-button";
|
import "@material/mwc-button";
|
||||||
import "@material/mwc-menu";
|
import "@material/mwc-menu";
|
||||||
import "@material/mwc-list/mwc-list-item";
|
import "@material/mwc-list/mwc-list-item";
|
||||||
import type { Menu } from "@material/mwc-menu";
|
import type { Menu, Corner } from "@material/mwc-menu";
|
||||||
|
|
||||||
import { haStyle } from "../resources/styles";
|
|
||||||
|
|
||||||
import "./ha-icon-button";
|
import "./ha-icon-button";
|
||||||
|
|
||||||
@customElement("ha-button-menu")
|
@customElement("ha-button-menu")
|
||||||
export class HaButtonMenu extends LitElement {
|
export class HaButtonMenu extends LitElement {
|
||||||
|
@property() public corner: Corner = "TOP_START";
|
||||||
|
|
||||||
@query("mwc-menu") private _menu?: Menu;
|
@query("mwc-menu") private _menu?: Menu;
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
@@ -25,7 +26,7 @@ export class HaButtonMenu extends LitElement {
|
|||||||
<div @click=${this._handleClick}>
|
<div @click=${this._handleClick}>
|
||||||
<slot name="trigger"></slot>
|
<slot name="trigger"></slot>
|
||||||
</div>
|
</div>
|
||||||
<mwc-menu>
|
<mwc-menu .corner=${this.corner}>
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</mwc-menu>
|
</mwc-menu>
|
||||||
`;
|
`;
|
||||||
@@ -36,15 +37,13 @@ export class HaButtonMenu extends LitElement {
|
|||||||
this._menu!.show();
|
this._menu!.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styles(): CSSResult[] {
|
static get styles(): CSSResult {
|
||||||
return [
|
return css`
|
||||||
haStyle,
|
:host {
|
||||||
css`
|
display: block;
|
||||||
:host {
|
position: relative;
|
||||||
position: relative;
|
}
|
||||||
}
|
`;
|
||||||
`,
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
import "@material/mwc-checkbox";
|
import "@material/mwc-checkbox";
|
||||||
import type { Checkbox } from "@material/mwc-checkbox";
|
import type { Checkbox } from "@material/mwc-checkbox";
|
||||||
import { style } from "@material/mwc-checkbox/mwc-checkbox-css";
|
import { customElement } from "lit-element";
|
||||||
import { css, CSSResult, customElement } from "lit-element";
|
|
||||||
import type { Constructor } from "../types";
|
import type { Constructor } from "../types";
|
||||||
|
|
||||||
const MwcCheckbox = customElements.get("mwc-checkbox") as Constructor<Checkbox>;
|
const MwcCheckbox = customElements.get("mwc-checkbox") as Constructor<Checkbox>;
|
||||||
@@ -12,18 +11,6 @@ export class HaCheckbox extends MwcCheckbox {
|
|||||||
super.firstUpdated();
|
super.firstUpdated();
|
||||||
this.style.setProperty("--mdc-theme-secondary", "var(--primary-color)");
|
this.style.setProperty("--mdc-theme-secondary", "var(--primary-color)");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static get styles(): CSSResult[] {
|
|
||||||
return [
|
|
||||||
style,
|
|
||||||
css`
|
|
||||||
.mdc-checkbox__native-control:enabled:not(:checked):not(:indeterminate)
|
|
||||||
~ .mdc-checkbox__background {
|
|
||||||
border-color: rgba(var(--rgb-primary-text-color), 0.54);
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
|
@@ -12,8 +12,7 @@ class HaComboBox extends EventsMixin(PolymerElement) {
|
|||||||
return html`
|
return html`
|
||||||
<style>
|
<style>
|
||||||
paper-input > ha-icon-button {
|
paper-input > ha-icon-button {
|
||||||
width: 24px;
|
--mdc-icon-button-size: 24px;
|
||||||
height: 24px;
|
|
||||||
padding: 2px;
|
padding: 2px;
|
||||||
color: var(--secondary-text-color);
|
color: var(--secondary-text-color);
|
||||||
}
|
}
|
||||||
|
@@ -30,7 +30,7 @@ class HaCoverControls extends PolymerElement {
|
|||||||
icon="hass:stop"
|
icon="hass:stop"
|
||||||
on-click="onStopTap"
|
on-click="onStopTap"
|
||||||
invisible$="[[!entityObj.supportsStop]]"
|
invisible$="[[!entityObj.supportsStop]]"
|
||||||
disabled="[[computStopDisabled(stateObj)]]"
|
disabled="[[computeStopDisabled(stateObj)]]"
|
||||||
></ha-icon-button>
|
></ha-icon-button>
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
aria-label="Close cover"
|
aria-label="Close cover"
|
||||||
|
@@ -31,7 +31,7 @@ class HaCoverTiltControls extends PolymerElement {
|
|||||||
icon="hass:stop"
|
icon="hass:stop"
|
||||||
on-click="onStopTiltTap"
|
on-click="onStopTiltTap"
|
||||||
invisible$="[[!entityObj.supportsStopTilt]]"
|
invisible$="[[!entityObj.supportsStopTilt]]"
|
||||||
disabled="[[computStopDisabled(stateObj)]]"
|
disabled="[[computeStopDisabled(stateObj)]]"
|
||||||
title="Stop tilt"
|
title="Stop tilt"
|
||||||
></ha-icon-button>
|
></ha-icon-button>
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
|
@@ -4,17 +4,19 @@ import { style } from "@material/mwc-dialog/mwc-dialog-css";
|
|||||||
import "./ha-icon-button";
|
import "./ha-icon-button";
|
||||||
import { css, CSSResult, customElement, html } from "lit-element";
|
import { css, CSSResult, customElement, html } from "lit-element";
|
||||||
import type { Constructor, HomeAssistant } from "../types";
|
import type { Constructor, HomeAssistant } from "../types";
|
||||||
|
import { mdiClose } from "@mdi/js";
|
||||||
|
|
||||||
const MwcDialog = customElements.get("mwc-dialog") as Constructor<Dialog>;
|
const MwcDialog = customElements.get("mwc-dialog") as Constructor<Dialog>;
|
||||||
|
|
||||||
export const createCloseHeading = (hass: HomeAssistant, title: string) => html`
|
export const createCloseHeading = (hass: HomeAssistant, title: string) => html`
|
||||||
${title}
|
${title}
|
||||||
<ha-icon-button
|
<mwc-icon-button
|
||||||
aria-label=${hass.localize("ui.dialogs.generic.close")}
|
aria-label=${hass.localize("ui.dialogs.generic.close")}
|
||||||
icon="hass:close"
|
|
||||||
dialogAction="close"
|
dialogAction="close"
|
||||||
class="close_button"
|
class="close_button"
|
||||||
></ha-icon-button>
|
>
|
||||||
|
<ha-svg-icon path=${mdiClose}></ha-svg-icon>
|
||||||
|
</mwc-icon-button>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@customElement("ha-dialog")
|
@customElement("ha-dialog")
|
||||||
|
@@ -1,41 +0,0 @@
|
|||||||
import "@material/mwc-fab";
|
|
||||||
import type { Fab } from "@material/mwc-fab";
|
|
||||||
import { ripple } from "@material/mwc-ripple/ripple-directive";
|
|
||||||
import { customElement, html, TemplateResult } from "lit-element";
|
|
||||||
import { classMap } from "lit-html/directives/class-map";
|
|
||||||
import type { Constructor } from "../types";
|
|
||||||
import "./ha-icon";
|
|
||||||
|
|
||||||
const MwcFab = customElements.get("mwc-fab") as Constructor<Fab>;
|
|
||||||
|
|
||||||
@customElement("ha-fab")
|
|
||||||
export class HaFab extends MwcFab {
|
|
||||||
// We override the render method because we don't have an icon font and mwc-fab doesn't support our svg-icon sets.
|
|
||||||
// Based on version mwc-fab 0.8
|
|
||||||
protected render(): TemplateResult {
|
|
||||||
const classes = {
|
|
||||||
"mdc-fab--mini": this.mini,
|
|
||||||
"mdc-fab--exited": this.exited,
|
|
||||||
"mdc-fab--extended": this.extended,
|
|
||||||
};
|
|
||||||
const showLabel = this.label !== "" && this.extended;
|
|
||||||
return html`
|
|
||||||
<button
|
|
||||||
.ripple="${ripple()}"
|
|
||||||
class="mdc-fab ${classMap(classes)}"
|
|
||||||
?disabled="${this.disabled}"
|
|
||||||
aria-label="${this.label || this.icon}"
|
|
||||||
>
|
|
||||||
${showLabel && this.showIconAtEnd ? this.label : ""}
|
|
||||||
${this.icon ? html` <ha-icon .icon=${this.icon}></ha-icon> ` : ""}
|
|
||||||
${showLabel && !this.showIconAtEnd ? this.label : ""}
|
|
||||||
</button>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
declare global {
|
|
||||||
interface HTMLElementTagNameMap {
|
|
||||||
"ha-fab": HaFab;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,17 +1,30 @@
|
|||||||
import { HaIconButton } from "./ha-icon-button";
|
import { LitElement, property, TemplateResult, html } from "lit-element";
|
||||||
|
import { mdiArrowLeft, mdiArrowRight } from "@mdi/js";
|
||||||
|
import "@material/mwc-icon-button/mwc-icon-button";
|
||||||
|
import "./ha-svg-icon";
|
||||||
|
|
||||||
|
export class HaIconButtonArrowPrev extends LitElement {
|
||||||
|
@property({ type: Boolean }) public disabled = false;
|
||||||
|
|
||||||
|
@property() private _icon = mdiArrowLeft;
|
||||||
|
|
||||||
export class HaIconButtonArrowPrev 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-left"
|
? mdiArrowLeft
|
||||||
: "hass:arrow-right";
|
: mdiArrowRight;
|
||||||
}, 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 {
|
||||||
|
@@ -48,6 +48,7 @@ export class HaIconButton extends LitElement {
|
|||||||
}
|
}
|
||||||
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);
|
||||||
}
|
}
|
||||||
ha-icon {
|
ha-icon {
|
||||||
--ha-icon-display: inline;
|
--ha-icon-display: inline;
|
||||||
|
@@ -1,5 +1,4 @@
|
|||||||
import "@polymer/iron-icon/iron-icon";
|
import "@polymer/iron-icon/iron-icon";
|
||||||
import { get, set, clear, Store } from "idb-keyval";
|
|
||||||
import {
|
import {
|
||||||
customElement,
|
customElement,
|
||||||
LitElement,
|
LitElement,
|
||||||
@@ -11,84 +10,25 @@ import {
|
|||||||
CSSResult,
|
CSSResult,
|
||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
import "./ha-svg-icon";
|
import "./ha-svg-icon";
|
||||||
|
import { customIconsets, CustomIcon } from "../data/custom_iconsets";
|
||||||
|
import {
|
||||||
|
Chunks,
|
||||||
|
MDI_PREFIXES,
|
||||||
|
getIcon,
|
||||||
|
findIconChunk,
|
||||||
|
Icons,
|
||||||
|
checkCacheVersion,
|
||||||
|
writeCache,
|
||||||
|
} from "../data/iconsets";
|
||||||
import { debounce } from "../common/util/debounce";
|
import { debounce } from "../common/util/debounce";
|
||||||
import { iconMetadata } from "../resources/icon-metadata";
|
|
||||||
import { IconMeta } from "../types";
|
|
||||||
|
|
||||||
interface Icons {
|
|
||||||
[key: string]: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Chunks {
|
|
||||||
[key: string]: Promise<Icons>;
|
|
||||||
}
|
|
||||||
|
|
||||||
const iconStore = new Store("hass-icon-db", "mdi-icon-store");
|
|
||||||
|
|
||||||
get("_version", iconStore).then((version) => {
|
|
||||||
if (!version) {
|
|
||||||
set("_version", iconMetadata.version, iconStore);
|
|
||||||
} else if (version !== iconMetadata.version) {
|
|
||||||
clear(iconStore).then(() =>
|
|
||||||
set("_version", iconMetadata.version, iconStore)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const chunks: Chunks = {};
|
const chunks: Chunks = {};
|
||||||
const MDI_PREFIXES = ["mdi", "hass", "hassio", "hademo"];
|
|
||||||
|
|
||||||
let toRead: Array<[string, (string) => void]> = [];
|
checkCacheVersion();
|
||||||
|
|
||||||
// Queue up as many icon fetches in 1 transaction
|
const debouncedWriteCache = debounce(() => writeCache(chunks), 2000);
|
||||||
const getIcon = (iconName: string) =>
|
|
||||||
new Promise<string>((resolve) => {
|
|
||||||
toRead.push([iconName, resolve]);
|
|
||||||
|
|
||||||
if (toRead.length > 1) {
|
const cachedIcons: { [key: string]: string } = {};
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const results: Array<[(string) => void, IDBRequest]> = [];
|
|
||||||
|
|
||||||
iconStore
|
|
||||||
._withIDBStore("readonly", (store) => {
|
|
||||||
for (const [iconName_, resolve_] of toRead) {
|
|
||||||
results.push([resolve_, store.get(iconName_)]);
|
|
||||||
}
|
|
||||||
toRead = [];
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
for (const [resolve_, request] of results) {
|
|
||||||
resolve_(request.result);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
const findIconChunk = (icon): string => {
|
|
||||||
let lastChunk: IconMeta;
|
|
||||||
for (const chunk of iconMetadata.parts) {
|
|
||||||
if (chunk.start !== undefined && icon < chunk.start) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
lastChunk = chunk;
|
|
||||||
}
|
|
||||||
return lastChunk!.file;
|
|
||||||
};
|
|
||||||
|
|
||||||
const debouncedWriteCache = debounce(async () => {
|
|
||||||
const keys = Object.keys(chunks);
|
|
||||||
const iconsSets: Icons[] = await Promise.all(Object.values(chunks));
|
|
||||||
// We do a batch opening the store just once, for (considerable) performance
|
|
||||||
iconStore._withIDBStore("readwrite", (store) => {
|
|
||||||
iconsSets.forEach((icons, idx) => {
|
|
||||||
Object.entries(icons).forEach(([name, path]) => {
|
|
||||||
store.put(path, name);
|
|
||||||
});
|
|
||||||
delete chunks[keys[idx]];
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}, 2000);
|
|
||||||
|
|
||||||
@customElement("ha-icon")
|
@customElement("ha-icon")
|
||||||
export class HaIcon extends LitElement {
|
export class HaIcon extends LitElement {
|
||||||
@@ -96,11 +36,14 @@ export class HaIcon extends LitElement {
|
|||||||
|
|
||||||
@property() private _path?: string;
|
@property() private _path?: string;
|
||||||
|
|
||||||
@property() private _noMdi = false;
|
@property() private _viewBox?;
|
||||||
|
|
||||||
|
@property() private _legacy = false;
|
||||||
|
|
||||||
protected updated(changedProps: PropertyValues) {
|
protected updated(changedProps: PropertyValues) {
|
||||||
if (changedProps.has("icon")) {
|
if (changedProps.has("icon")) {
|
||||||
this._path = undefined;
|
this._path = undefined;
|
||||||
|
this._viewBox = undefined;
|
||||||
this._loadIcon();
|
this._loadIcon();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -109,28 +52,48 @@ export class HaIcon extends LitElement {
|
|||||||
if (!this.icon) {
|
if (!this.icon) {
|
||||||
return html``;
|
return html``;
|
||||||
}
|
}
|
||||||
if (this._noMdi) {
|
if (this._legacy) {
|
||||||
return html`<iron-icon .icon=${this.icon}></iron-icon>`;
|
return html`<iron-icon .icon=${this.icon}></iron-icon>`;
|
||||||
}
|
}
|
||||||
return html`<ha-svg-icon .path=${this._path}></ha-svg-icon>`;
|
return html`<ha-svg-icon
|
||||||
|
.path=${this._path}
|
||||||
|
.viewBox=${this._viewBox}
|
||||||
|
></ha-svg-icon>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _loadIcon() {
|
private async _loadIcon() {
|
||||||
if (!this.icon) {
|
if (!this.icon) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const icon = this.icon.split(":", 2);
|
const [iconPrefix, iconName] = this.icon.split(":", 2);
|
||||||
if (!MDI_PREFIXES.includes(icon[0])) {
|
|
||||||
this._noMdi = true;
|
if (!iconPrefix || !iconName) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._noMdi = false;
|
if (!MDI_PREFIXES.includes(iconPrefix)) {
|
||||||
|
if (iconPrefix in customIconsets) {
|
||||||
|
const customIconset = customIconsets[iconPrefix];
|
||||||
|
if (customIconset) {
|
||||||
|
this._setCustomPath(customIconset(iconName));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._legacy = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const iconName = icon[1];
|
this._legacy = false;
|
||||||
const cachedPath: string = await getIcon(iconName);
|
|
||||||
if (cachedPath) {
|
if (iconName in cachedIcons) {
|
||||||
this._path = cachedPath;
|
this._path = cachedIcons[iconName];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const databaseIcon: string = await getIcon(iconName);
|
||||||
|
if (databaseIcon) {
|
||||||
|
this._path = databaseIcon;
|
||||||
|
cachedIcons[iconName] = databaseIcon;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const chunk = findIconChunk(iconName);
|
const chunk = findIconChunk(iconName);
|
||||||
@@ -147,9 +110,16 @@ export class HaIcon extends LitElement {
|
|||||||
debouncedWriteCache();
|
debouncedWriteCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async _setCustomPath(promise: Promise<CustomIcon>) {
|
||||||
|
const icon = await promise;
|
||||||
|
this._path = icon.path;
|
||||||
|
this._viewBox = icon.viewBox;
|
||||||
|
}
|
||||||
|
|
||||||
private async _setPath(promise: Promise<Icons>, iconName: string) {
|
private async _setPath(promise: Promise<Icons>, iconName: string) {
|
||||||
const iconPack = await promise;
|
const iconPack = await promise;
|
||||||
this._path = iconPack[iconName];
|
this._path = iconPack[iconName];
|
||||||
|
cachedIcons[iconName] = iconPack[iconName];
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styles(): CSSResult {
|
static get styles(): CSSResult {
|
||||||
|
@@ -31,12 +31,14 @@ class HaLabelBadge extends LitElement {
|
|||||||
big: Boolean(this.value && this.value.length > 4),
|
big: Boolean(this.value && this.value.length > 4),
|
||||||
})}"
|
})}"
|
||||||
>
|
>
|
||||||
${this.icon && !this.value && !this.image
|
<slot>
|
||||||
? html` <ha-icon .icon="${this.icon}"></ha-icon> `
|
${this.icon && !this.value && !this.image
|
||||||
: ""}
|
? html` <ha-icon .icon=${this.icon}></ha-icon> `
|
||||||
${this.value && !this.image
|
: ""}
|
||||||
? html` <span>${this.value}</span> `
|
${this.value && !this.image
|
||||||
: ""}
|
? html` <span>${this.value}</span> `
|
||||||
|
: ""}
|
||||||
|
</slot>
|
||||||
</div>
|
</div>
|
||||||
${this.label
|
${this.label
|
||||||
? html`
|
? html`
|
||||||
|
@@ -1,10 +1,6 @@
|
|||||||
import { customElement, property, UpdatingElement } from "lit-element";
|
import { customElement, property, UpdatingElement } from "lit-element";
|
||||||
// @ts-ignore
|
|
||||||
// eslint-disable-next-line import/no-webpack-loader-syntax
|
|
||||||
import markdownWorker from "workerize-loader!../resources/markdown_worker";
|
|
||||||
import { fireEvent } from "../common/dom/fire_event";
|
import { fireEvent } from "../common/dom/fire_event";
|
||||||
|
import { renderMarkdown } from "../resources/render-markdown";
|
||||||
let worker: any | undefined;
|
|
||||||
|
|
||||||
@customElement("ha-markdown")
|
@customElement("ha-markdown")
|
||||||
class HaMarkdown extends UpdatingElement {
|
class HaMarkdown extends UpdatingElement {
|
||||||
@@ -16,16 +12,11 @@ class HaMarkdown extends UpdatingElement {
|
|||||||
|
|
||||||
protected update(changedProps) {
|
protected update(changedProps) {
|
||||||
super.update(changedProps);
|
super.update(changedProps);
|
||||||
|
|
||||||
if (!worker) {
|
|
||||||
worker = markdownWorker();
|
|
||||||
}
|
|
||||||
|
|
||||||
this._render();
|
this._render();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _render() {
|
private async _render() {
|
||||||
this.innerHTML = await worker.renderMarkdown(
|
this.innerHTML = await renderMarkdown(
|
||||||
this.content,
|
this.content,
|
||||||
{
|
{
|
||||||
breaks: this.breaks,
|
breaks: this.breaks,
|
||||||
@@ -62,7 +53,7 @@ class HaMarkdown extends UpdatingElement {
|
|||||||
node.rel = "noreferrer noopener";
|
node.rel = "noreferrer noopener";
|
||||||
|
|
||||||
// Fire a resize event when images loaded to notify content resized
|
// Fire a resize event when images loaded to notify content resized
|
||||||
} else if (node) {
|
} else if (node instanceof HTMLImageElement) {
|
||||||
node.addEventListener("load", this._resize);
|
node.addEventListener("load", this._resize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -13,6 +13,7 @@ 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-icon-button";
|
||||||
|
import { mdiMenu } from "@mdi/js";
|
||||||
|
|
||||||
@customElement("ha-menu-button")
|
@customElement("ha-menu-button")
|
||||||
class HaMenuButton extends LitElement {
|
class HaMenuButton extends LitElement {
|
||||||
@@ -55,11 +56,12 @@ class HaMenuButton extends LitElement {
|
|||||||
(entityId) => computeDomain(entityId) === "configurator"
|
(entityId) => computeDomain(entityId) === "configurator"
|
||||||
));
|
));
|
||||||
return html`
|
return html`
|
||||||
<ha-icon-button
|
<mwc-icon-button
|
||||||
aria-label=${this.hass.localize("ui.sidebar.sidebar_toggle")}
|
aria-label=${this.hass.localize("ui.sidebar.sidebar_toggle")}
|
||||||
icon="hass:menu"
|
|
||||||
@click=${this._toggleMenu}
|
@click=${this._toggleMenu}
|
||||||
></ha-icon-button>
|
>
|
||||||
|
<ha-svg-icon path=${mdiMenu}></ha-svg-icon>
|
||||||
|
</mwc-icon-button>
|
||||||
${hasNotifications ? html` <div class="dot"></div> ` : ""}
|
${hasNotifications ? html` <div class="dot"></div> ` : ""}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
@@ -133,8 +135,8 @@ class HaMenuButton extends LitElement {
|
|||||||
background-color: var(--accent-color);
|
background-color: var(--accent-color);
|
||||||
width: 12px;
|
width: 12px;
|
||||||
height: 12px;
|
height: 12px;
|
||||||
top: 5px;
|
top: 9px;
|
||||||
right: 2px;
|
right: 7px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
border: 2px solid var(--app-header-background-color);
|
border: 2px solid var(--app-header-background-color);
|
||||||
}
|
}
|
||||||
|
@@ -12,10 +12,12 @@ import {
|
|||||||
export class HaSvgIcon extends LitElement {
|
export class HaSvgIcon extends LitElement {
|
||||||
@property() public path?: string;
|
@property() public path?: string;
|
||||||
|
|
||||||
|
@property() public viewBox?: string;
|
||||||
|
|
||||||
protected render(): SVGTemplateResult {
|
protected render(): SVGTemplateResult {
|
||||||
return svg`
|
return svg`
|
||||||
<svg
|
<svg
|
||||||
viewBox="0 0 24 24"
|
viewBox=${this.viewBox || "0 0 24 24"}
|
||||||
preserveAspectRatio="xMidYMid meet"
|
preserveAspectRatio="xMidYMid meet"
|
||||||
focusable="false">
|
focusable="false">
|
||||||
<g>
|
<g>
|
||||||
|
135
src/components/ha-tab.ts
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
import {
|
||||||
|
css,
|
||||||
|
CSSResult,
|
||||||
|
customElement,
|
||||||
|
LitElement,
|
||||||
|
property,
|
||||||
|
TemplateResult,
|
||||||
|
html,
|
||||||
|
queryAsync,
|
||||||
|
internalProperty,
|
||||||
|
eventOptions,
|
||||||
|
} from "lit-element";
|
||||||
|
import "@material/mwc-ripple/mwc-ripple";
|
||||||
|
import type { Ripple } from "@material/mwc-ripple";
|
||||||
|
import { RippleHandlers } from "@material/mwc-ripple/ripple-handlers";
|
||||||
|
import "./ha-icon";
|
||||||
|
import "./ha-svg-icon";
|
||||||
|
import { ifDefined } from "lit-html/directives/if-defined";
|
||||||
|
|
||||||
|
@customElement("ha-tab")
|
||||||
|
export class HaTab extends LitElement {
|
||||||
|
@property({ type: Boolean, reflect: true }) public active = false;
|
||||||
|
|
||||||
|
@property({ type: Boolean, reflect: true }) public narrow = false;
|
||||||
|
|
||||||
|
@property() public name?: string;
|
||||||
|
|
||||||
|
@queryAsync("mwc-ripple") private _ripple!: Promise<Ripple | null>;
|
||||||
|
|
||||||
|
@internalProperty() private _shouldRenderRipple = false;
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
return html`
|
||||||
|
<div
|
||||||
|
tabindex="0"
|
||||||
|
role="tab"
|
||||||
|
aria-selected=${this.active}
|
||||||
|
aria-label=${ifDefined(this.name)}
|
||||||
|
@focus=${this.handleRippleFocus}
|
||||||
|
@blur=${this.handleRippleBlur}
|
||||||
|
@mousedown=${this.handleRippleActivate}
|
||||||
|
@mouseup=${this.handleRippleDeactivate}
|
||||||
|
@mouseenter=${this.handleRippleMouseEnter}
|
||||||
|
@mouseleave=${this.handleRippleMouseLeave}
|
||||||
|
@touchstart=${this.handleRippleActivate}
|
||||||
|
@touchend=${this.handleRippleDeactivate}
|
||||||
|
@touchcancel=${this.handleRippleDeactivate}
|
||||||
|
@keydown=${this._handleKeyDown}
|
||||||
|
>
|
||||||
|
${this.narrow ? html`<slot name="icon"></slot>` : ""}
|
||||||
|
${!this.narrow || this.active
|
||||||
|
? html`<span class="name">${this.name}</span>`
|
||||||
|
: ""}
|
||||||
|
${this._shouldRenderRipple ? html`<mwc-ripple></mwc-ripple>` : ""}
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _rippleHandlers: RippleHandlers = new RippleHandlers(() => {
|
||||||
|
this._shouldRenderRipple = true;
|
||||||
|
return this._ripple;
|
||||||
|
});
|
||||||
|
|
||||||
|
private _handleKeyDown(ev: KeyboardEvent): void {
|
||||||
|
if (ev.keyCode === 13) {
|
||||||
|
(ev.target as HTMLElement).click();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@eventOptions({ passive: true })
|
||||||
|
private handleRippleActivate(evt?: Event) {
|
||||||
|
this._rippleHandlers.startPress(evt);
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleRippleDeactivate() {
|
||||||
|
this._rippleHandlers.endPress();
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleRippleMouseEnter() {
|
||||||
|
this._rippleHandlers.startHover();
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleRippleMouseLeave() {
|
||||||
|
this._rippleHandlers.endHover();
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleRippleFocus() {
|
||||||
|
this._rippleHandlers.startFocus();
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleRippleBlur() {
|
||||||
|
this._rippleHandlers.endFocus();
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResult {
|
||||||
|
return css`
|
||||||
|
div {
|
||||||
|
padding: 0 32px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
text-align: center;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 64px;
|
||||||
|
cursor: pointer;
|
||||||
|
position: relative;
|
||||||
|
outline: none;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.name {
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
:host([active]) {
|
||||||
|
color: var(--primary-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
:host(:not([narrow])[active]) div {
|
||||||
|
border-bottom: 2px solid var(--primary-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
:host([narrow]) {
|
||||||
|
padding: 0 16px;
|
||||||
|
width: 20%;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ha-tab": HaTab;
|
||||||
|
}
|
||||||
|
}
|
@@ -8,6 +8,8 @@ export interface ConfigUpdateValues {
|
|||||||
elevation: number;
|
elevation: number;
|
||||||
unit_system: "metric" | "imperial";
|
unit_system: "metric" | "imperial";
|
||||||
time_zone: string;
|
time_zone: string;
|
||||||
|
external_url?: string | null;
|
||||||
|
internal_url?: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const saveCoreConfig = (
|
export const saveCoreConfig = (
|
||||||
|
16
src/data/custom_iconsets.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
export interface CustomIcon {
|
||||||
|
path: string;
|
||||||
|
viewBox?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CustomIconsetsWindow {
|
||||||
|
customIconsets?: { [key: string]: (name: string) => Promise<CustomIcon> };
|
||||||
|
}
|
||||||
|
|
||||||
|
const customIconsetsWindow = window as CustomIconsetsWindow;
|
||||||
|
|
||||||
|
if (!("customIconsets" in customIconsetsWindow)) {
|
||||||
|
customIconsetsWindow.customIconsets = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
export const customIconsets = customIconsetsWindow.customIconsets!;
|
79
src/data/iconsets.ts
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
import { iconMetadata } from "../resources/icon-metadata";
|
||||||
|
import { IconMeta } from "../types";
|
||||||
|
import { get, set, clear, Store } from "idb-keyval";
|
||||||
|
|
||||||
|
export interface Icons {
|
||||||
|
[key: string]: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Chunks {
|
||||||
|
[key: string]: Promise<Icons>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const iconStore = new Store("hass-icon-db", "mdi-icon-store");
|
||||||
|
|
||||||
|
export const MDI_PREFIXES = ["mdi", "hass", "hassio", "hademo"];
|
||||||
|
|
||||||
|
let toRead: Array<[string, (string) => void]> = [];
|
||||||
|
|
||||||
|
// Queue up as many icon fetches in 1 transaction
|
||||||
|
export const getIcon = (iconName: string) =>
|
||||||
|
new Promise<string>((resolve) => {
|
||||||
|
toRead.push([iconName, resolve]);
|
||||||
|
|
||||||
|
if (toRead.length > 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const results: Array<[(string) => void, IDBRequest]> = [];
|
||||||
|
|
||||||
|
iconStore
|
||||||
|
._withIDBStore("readonly", (store) => {
|
||||||
|
for (const [iconName_, resolve_] of toRead) {
|
||||||
|
results.push([resolve_, store.get(iconName_)]);
|
||||||
|
}
|
||||||
|
toRead = [];
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
for (const [resolve_, request] of results) {
|
||||||
|
resolve_(request.result);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
export const findIconChunk = (icon): string => {
|
||||||
|
let lastChunk: IconMeta;
|
||||||
|
for (const chunk of iconMetadata.parts) {
|
||||||
|
if (chunk.start !== undefined && icon < chunk.start) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
lastChunk = chunk;
|
||||||
|
}
|
||||||
|
return lastChunk!.file;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const writeCache = async (chunks: Chunks) => {
|
||||||
|
const keys = Object.keys(chunks);
|
||||||
|
const iconsSets: Icons[] = await Promise.all(Object.values(chunks));
|
||||||
|
// We do a batch opening the store just once, for (considerable) performance
|
||||||
|
iconStore._withIDBStore("readwrite", (store) => {
|
||||||
|
iconsSets.forEach((icons, idx) => {
|
||||||
|
Object.entries(icons).forEach(([name, path]) => {
|
||||||
|
store.put(path, name);
|
||||||
|
});
|
||||||
|
delete chunks[keys[idx]];
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const checkCacheVersion = () => {
|
||||||
|
get("_version", iconStore).then((version) => {
|
||||||
|
if (!version) {
|
||||||
|
set("_version", iconMetadata.version, iconStore);
|
||||||
|
} else if (version !== iconMetadata.version) {
|
||||||
|
clear(iconStore).then(() =>
|
||||||
|
set("_version", iconMetadata.version, iconStore)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
@@ -7,6 +7,7 @@ export interface IntegrationManifest {
|
|||||||
name: string;
|
name: string;
|
||||||
config_flow: boolean;
|
config_flow: boolean;
|
||||||
documentation: string;
|
documentation: string;
|
||||||
|
issue_tracker?: string;
|
||||||
dependencies?: string[];
|
dependencies?: string[];
|
||||||
after_dependencies?: string[];
|
after_dependencies?: string[];
|
||||||
codeowners?: string[];
|
codeowners?: string[];
|
||||||
@@ -17,7 +18,11 @@ export interface IntegrationManifest {
|
|||||||
quality_scale?: string;
|
quality_scale?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const integrationIssuesUrl = (domain: string) =>
|
export const integrationIssuesUrl = (
|
||||||
|
domain: string,
|
||||||
|
manifest: IntegrationManifest
|
||||||
|
) =>
|
||||||
|
manifest.issue_tracker ||
|
||||||
`https://github.com/home-assistant/home-assistant/issues?q=is%3Aissue+is%3Aopen+label%3A%22integration%3A+${domain}%22`;
|
`https://github.com/home-assistant/home-assistant/issues?q=is%3Aissue+is%3Aopen+label%3A%22integration%3A+${domain}%22`;
|
||||||
|
|
||||||
export const domainToName = (localize: LocalizeFunc, domain: string) =>
|
export const domainToName = (localize: LocalizeFunc, domain: string) =>
|
||||||
|
@@ -3,6 +3,7 @@ export interface CustomCardEntry {
|
|||||||
name?: string;
|
name?: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
preview?: boolean;
|
preview?: boolean;
|
||||||
|
documentationURL?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CustomCardsWindow {
|
export interface CustomCardsWindow {
|
||||||
|
@@ -1,26 +1,52 @@
|
|||||||
import { HomeAssistant, WeatherEntity } from "../types";
|
import { SVGTemplateResult, svg, html, TemplateResult, css } from "lit-element";
|
||||||
|
import { styleMap } from "lit-html/directives/style-map";
|
||||||
|
|
||||||
export const weatherImages = {
|
import type { HomeAssistant, WeatherEntity } from "../types";
|
||||||
"clear-night": "/static/images/weather/night.png",
|
|
||||||
cloudy: "/static/images/weather/cloudy.png",
|
export const weatherSVGs = new Set<string>([
|
||||||
fog: "/static/images/weather/cloudy.png",
|
"clear-night",
|
||||||
lightning: "/static/images/weather/lightning.png",
|
"cloudy",
|
||||||
"lightning-rainy": "/static/images/weather/lightning-rainy.png",
|
"fog",
|
||||||
partlycloudy: "/static/images/weather/partly-cloudy.png",
|
"lightning",
|
||||||
pouring: "/static/images/weather/pouring.png",
|
"lightning-rainy",
|
||||||
rainy: "/static/images/weather/rainy.png",
|
"partlycloudy",
|
||||||
hail: "/static/images/weather/rainy.png",
|
"pouring",
|
||||||
snowy: "/static/images/weather/snowy.png",
|
"rainy",
|
||||||
"snowy-rainy": "/static/images/weather/snowy.png",
|
"hail",
|
||||||
sunny: "/static/images/weather/sunny.png",
|
"snowy",
|
||||||
windy: "/static/images/weather/windy.png",
|
"snowy-rainy",
|
||||||
"windy-variant": "/static/images/weather/windy.png",
|
"sunny",
|
||||||
};
|
"windy",
|
||||||
|
"windy-variant",
|
||||||
|
]);
|
||||||
|
|
||||||
export const weatherIcons = {
|
export const weatherIcons = {
|
||||||
exceptional: "hass:alert-circle-outline",
|
exceptional: "hass:alert-circle-outline",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const cloudyStates = new Set<string>([
|
||||||
|
"partlycloudy",
|
||||||
|
"cloudy",
|
||||||
|
"fog",
|
||||||
|
"windy",
|
||||||
|
"windy-variant",
|
||||||
|
"hail",
|
||||||
|
"rainy",
|
||||||
|
"snowy",
|
||||||
|
"snowy-rainy",
|
||||||
|
"pouring",
|
||||||
|
"lightning",
|
||||||
|
"lightning-rainy",
|
||||||
|
]);
|
||||||
|
|
||||||
|
const rainStates = new Set<string>(["hail", "rainy", "pouring"]);
|
||||||
|
|
||||||
|
const windyStates = new Set<string>(["windy", "windy-variant"]);
|
||||||
|
|
||||||
|
const snowyStates = new Set<string>(["snowy", "snowy-rainy"]);
|
||||||
|
|
||||||
|
const lightningStates = new Set<string>(["lightning", "lightning-rainy"]);
|
||||||
|
|
||||||
export const cardinalDirections = [
|
export const cardinalDirections = [
|
||||||
"N",
|
"N",
|
||||||
"NNE",
|
"NNE",
|
||||||
@@ -164,3 +190,183 @@ const getWeatherExtrema = (
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const weatherSVGStyles = css`
|
||||||
|
.rain {
|
||||||
|
fill: var(--weather-icon-rain-color, #30b3ff);
|
||||||
|
}
|
||||||
|
.sun {
|
||||||
|
fill: var(--weather-icon-sun-color, #fdd93c);
|
||||||
|
}
|
||||||
|
.moon {
|
||||||
|
fill: var(--weather-icon-moon-color, #fdf9cc);
|
||||||
|
}
|
||||||
|
.cloud-back {
|
||||||
|
fill: var(--weather-icon-cloud-back-color, #d4d4d4);
|
||||||
|
}
|
||||||
|
.cloud-front {
|
||||||
|
fill: var(--weather-icon-cloud-front-color, #f9f9f9);
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const getWeatherStateSVG = (state: string): SVGTemplateResult => {
|
||||||
|
return svg`
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 17 17"
|
||||||
|
>
|
||||||
|
${
|
||||||
|
state === "sunny"
|
||||||
|
? svg`
|
||||||
|
<path
|
||||||
|
class="sun"
|
||||||
|
d="m 14.39303,8.4033507 c 0,3.3114723 -2.684145,5.9956173 -5.9956169,5.9956173 -3.3114716,0 -5.9956168,-2.684145 -5.9956168,-5.9956173 0,-3.311471 2.6841452,-5.995617 5.9956168,-5.995617 3.3114719,0 5.9956169,2.684146 5.9956169,5.995617"
|
||||||
|
/>
|
||||||
|
`
|
||||||
|
: ""
|
||||||
|
}
|
||||||
|
${
|
||||||
|
state === "clear-night"
|
||||||
|
? svg`
|
||||||
|
<path
|
||||||
|
class="moon"
|
||||||
|
d="m 13.502891,11.382935 c -1.011285,1.859223 -2.976664,3.121381 -5.2405751,3.121381 -3.289929,0 -5.953329,-2.663833 -5.953329,-5.9537625 0,-2.263911 1.261724,-4.228856 3.120948,-5.240575 -0.452782,0.842738 -0.712753,1.806363 -0.712753,2.832381 0,3.289928 2.663833,5.9533275 5.9533291,5.9533275 1.026017,0 1.989641,-0.259969 2.83238,-0.712752"
|
||||||
|
/>
|
||||||
|
`
|
||||||
|
: ""
|
||||||
|
}
|
||||||
|
${
|
||||||
|
state === "partlycloudy"
|
||||||
|
? svg`
|
||||||
|
<path
|
||||||
|
class="sun"
|
||||||
|
d="m14.981 4.2112c0 1.9244-1.56 3.4844-3.484 3.4844-1.9244 0-3.4844-1.56-3.4844-3.4844s1.56-3.484 3.4844-3.484c1.924 0 3.484 1.5596 3.484 3.484"
|
||||||
|
/>
|
||||||
|
`
|
||||||
|
: ""
|
||||||
|
}
|
||||||
|
${
|
||||||
|
cloudyStates.has(state)
|
||||||
|
? svg`
|
||||||
|
<path
|
||||||
|
class="cloud-back"
|
||||||
|
d="m3.8863 5.035c-0.54892 0.16898-1.04 0.46637-1.4372 0.8636-0.63077 0.63041-1.0206 1.4933-1.0206 2.455 0 1.9251 1.5589 3.4682 3.4837 3.4682h6.9688c1.9251 0 3.484-1.5981 3.484-3.5232 0-1.9251-1.5589-3.5232-3.484-3.5232h-1.0834c-0.25294-1.6916-1.6986-2.9083-3.4463-2.9083-1.7995 0-3.2805 1.4153-3.465 3.1679"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
class="cloud-front"
|
||||||
|
d="m4.1996 7.6995c-0.33902 0.10407-0.64276 0.28787-0.88794 0.5334-0.39017 0.38982-0.63147 0.92322-0.63147 1.5176 0 1.1896 0.96414 2.1431 2.1537 2.1431h4.3071c1.1896 0 2.153-0.98742 2.153-2.1777 0-1.1896-0.96344-2.1777-2.153-2.1777h-0.66992c-0.15593-1.0449-1.0499-1.7974-2.1297-1.7974-1.112 0-2.0274 0.87524-2.1417 1.9586"
|
||||||
|
/>
|
||||||
|
`
|
||||||
|
: ""
|
||||||
|
}
|
||||||
|
${
|
||||||
|
rainStates.has(state)
|
||||||
|
? svg`
|
||||||
|
<path
|
||||||
|
class="rain"
|
||||||
|
d="m5.2852 14.734c-0.22401 0.24765-0.57115 0.2988-0.77505 0.11395-0.20391-0.1845-0.18732-0.53481 0.036689-0.78281 0.14817-0.16298 0.59126-0.32914 0.87559-0.42369 0.12453-0.04092 0.22684 0.05186 0.19791 0.17956-0.065617 0.2921-0.18732 0.74965-0.33514 0.91299"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
class="rain"
|
||||||
|
d="m11.257 14.163c-0.22437 0.24765-0.57115 0.2988-0.77505 0.11395-0.2039-0.1845-0.18768-0.53481 0.03669-0.78281 0.14817-0.16298 0.59126-0.32914 0.8756-0.42369 0.12453-0.04092 0.22684 0.05186 0.19791 0.17956-0.06562 0.2921-0.18732 0.74965-0.33514 0.91299"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
class="rain"
|
||||||
|
d="m8.432 15.878c-0.15452 0.17039-0.3937 0.20567-0.53446 0.07867-0.14041-0.12735-0.12876-0.36865 0.025753-0.53975 0.10195-0.11218 0.40711-0.22684 0.60325-0.29175 0.085725-0.02858 0.15628 0.03563 0.13652 0.12382-0.045508 0.20108-0.12912 0.51647-0.23107 0.629"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
class="rain"
|
||||||
|
d="m7.9991 14.118c-0.19226 0.21237-0.49001 0.25612-0.66499 0.09737-0.17462-0.15804-0.16051-0.45861 0.03175-0.67098 0.12665-0.14005 0.50729-0.28293 0.75071-0.36336 0.10689-0.03563 0.19473 0.0441 0.17004 0.15346-0.056092 0.25082-0.16051 0.64347-0.28751 0.78352"
|
||||||
|
/>
|
||||||
|
`
|
||||||
|
: ""
|
||||||
|
}
|
||||||
|
${
|
||||||
|
state === "pouring"
|
||||||
|
? svg`
|
||||||
|
<path
|
||||||
|
class="rain"
|
||||||
|
d="m10.648 16.448c-0.19226 0.21449-0.49001 0.25894-0.66499 0.09878-0.17498-0.16016-0.16087-0.4639 0.03175-0.67874 0.12665-0.14146 0.50694-0.2854 0.75071-0.36724 0.10689-0.03563 0.19473 0.0448 0.17004 0.15558-0.05645 0.25365-0.16051 0.65017-0.28751 0.79163"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
class="rain"
|
||||||
|
d="m5.9383 16.658c-0.22437 0.25012-0.5715 0.30162-0.77505 0.11501-0.20391-0.18627-0.18768-0.54046 0.036689-0.79093 0.14817-0.1651 0.59126-0.33267 0.87559-0.42827 0.12418-0.04127 0.22648 0.05221 0.19791 0.18168-0.065617 0.29528-0.18732 0.75741-0.33514 0.92251"
|
||||||
|
/>
|
||||||
|
`
|
||||||
|
: ""
|
||||||
|
}
|
||||||
|
${
|
||||||
|
windyStates.has(state)
|
||||||
|
? svg`
|
||||||
|
<path
|
||||||
|
class="cloud-back"
|
||||||
|
d="m 13.59616,15.30968 c 0,0 -0.09137,-0.0071 -0.250472,-0.0187 -0.158045,-0.01235 -0.381353,-0.02893 -0.64382,-0.05715 -0.262466,-0.02716 -0.564444,-0.06385 -0.877358,-0.124531 -0.156986,-0.03034 -0.315383,-0.06844 -0.473781,-0.111478 -0.157691,-0.04551 -0.313266,-0.09842 -0.463902,-0.161219 l -0.267406,-0.0949 c -0.09984,-0.02646 -0.205669,-0.04904 -0.305153,-0.06738 -0.193322,-0.02716 -0.3838218,-0.03316 -0.5640912,-0.02011 -0.3626556,0.02611 -0.6847417,0.119239 -0.94615,0.226483 -0.2617611,0.108656 -0.4642556,0.230364 -0.600075,0.324203 -0.1358195,0.09419 -0.2049639,0.160514 -0.2049639,0.160514 0,0 0.089958,-0.01623 0.24765,-0.04445 0.1559278,-0.02575 0.3764139,-0.06174 0.6367639,-0.08714 0.2596444,-0.02646 0.5591527,-0.0441 0.8678333,-0.02328 0.076905,0.0035 0.1538111,0.01658 0.2321278,0.02293 0.077611,0.01058 0.1534581,0.02893 0.2314221,0.04022 0.07267,0.01834 0.1397,0.03986 0.213078,0.05644 l 0.238125,0.08925 c 0.09207,0.03281 0.183444,0.07055 0.275872,0.09878 0.09243,0.0261 0.185208,0.05327 0.277636,0.07161 0.184856,0.0388 0.367947,0.06174 0.543983,0.0702 0.353131,0.01905 0.678745,-0.01341 0.951442,-0.06456 0.27305,-0.05292 0.494595,-0.123119 0.646642,-0.181681 0.152047,-0.05785 0.234597,-0.104069 0.234597,-0.104069"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
class="cloud-back"
|
||||||
|
d="m 4.7519154,13.905801 c 0,0 0.091369,-0.0032 0.2511778,-0.0092 0.1580444,-0.0064 0.3820583,-0.01446 0.6455833,-0.03281 0.2631722,-0.01729 0.5662083,-0.04269 0.8812389,-0.09137 0.1576916,-0.02434 0.3175,-0.05609 0.4776611,-0.09384 0.1591027,-0.03951 0.3167944,-0.08643 0.4699,-0.14358 l 0.2702277,-0.08467 c 0.1008945,-0.02222 0.2074334,-0.04127 0.3072695,-0.05574 0.1943805,-0.01976 0.3848805,-0.0187 0.5651499,0.0014 0.3608917,0.03951 0.67945,0.144639 0.936625,0.261761 0.2575278,0.118534 0.4554364,0.247297 0.5873754,0.346781 0.132291,0.09913 0.198966,0.168275 0.198966,0.168275 0,0 -0.08925,-0.01976 -0.245886,-0.05397 C 9.9423347,14.087088 9.7232597,14.042988 9.4639681,14.00736 9.2057347,13.97173 8.9072848,13.94245 8.5978986,13.95162 c -0.077258,7.06e-4 -0.1541638,0.01058 -0.2328333,0.01411 -0.077964,0.0078 -0.1545166,0.02328 -0.2331861,0.03175 -0.073025,0.01588 -0.1404055,0.03422 -0.2141361,0.04798 l -0.2420055,0.08008 c -0.093486,0.02963 -0.1859139,0.06421 -0.2794,0.0889 C 7.3028516,14.23666 7.2093653,14.2603 7.116232,14.27512 6.9303181,14.30722 6.7465209,14.3231 6.5697792,14.32486 6.2166487,14.33046 5.8924459,14.28605 5.6218654,14.224318 5.3505793,14.161565 5.1318571,14.082895 4.9822793,14.01869 4.8327015,13.95519 4.7519154,13.905801 4.7519154,13.905801"
|
||||||
|
/>
|
||||||
|
`
|
||||||
|
: ""
|
||||||
|
}
|
||||||
|
${
|
||||||
|
snowyStates.has(state)
|
||||||
|
? svg`
|
||||||
|
<path
|
||||||
|
class="rain"
|
||||||
|
d="m 8.4319893,15.348341 c 0,0.257881 -0.209197,0.467079 -0.467078,0.467079 -0.258586,0 -0.46743,-0.209198 -0.46743,-0.467079 0,-0.258233 0.208844,-0.467431 0.46743,-0.467431 0.257881,0 0.467078,0.209198 0.467078,0.467431"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
class="rain"
|
||||||
|
d="m 11.263878,14.358553 c 0,0.364067 -0.295275,0.659694 -0.659695,0.659694 -0.364419,0 -0.6596937,-0.295627 -0.6596937,-0.659694 0,-0.364419 0.2952747,-0.659694 0.6596937,-0.659694 0.36442,0 0.659695,0.295275 0.659695,0.659694"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
class="rain"
|
||||||
|
d="m 5.3252173,13.69847 c 0,0.364419 -0.295275,0.660047 -0.659695,0.660047 -0.364067,0 -0.659694,-0.295628 -0.659694,-0.660047 0,-0.364067 0.295627,-0.659694 0.659694,-0.659694 0.36442,0 0.659695,0.295627 0.659695,0.659694"
|
||||||
|
/>
|
||||||
|
`
|
||||||
|
: ""
|
||||||
|
}
|
||||||
|
${
|
||||||
|
lightningStates.has(state)
|
||||||
|
? svg`
|
||||||
|
<path
|
||||||
|
class="sun"
|
||||||
|
d="m 9.9252695,10.935875 -1.6483986,2.341014 1.1170184,0.05929 -1.2169864,2.02141 3.0450261,-2.616159 H 9.8864918 L 10.97937,11.294651 10.700323,10.79794 h -0.508706 l -0.2663475,0.137936"
|
||||||
|
/>
|
||||||
|
`
|
||||||
|
: ""
|
||||||
|
}
|
||||||
|
</svg>`;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getWeatherStateIcon = (
|
||||||
|
state: string,
|
||||||
|
element: HTMLElement
|
||||||
|
): TemplateResult | undefined => {
|
||||||
|
const userDefinedIcon = getComputedStyle(element).getPropertyValue(
|
||||||
|
`--weather-icon-${state}`
|
||||||
|
);
|
||||||
|
|
||||||
|
if (userDefinedIcon) {
|
||||||
|
return html`
|
||||||
|
<div
|
||||||
|
style="background-size: cover;${styleMap({
|
||||||
|
"background-image": userDefinedIcon,
|
||||||
|
})}"
|
||||||
|
></div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (weatherSVGs.has(state)) {
|
||||||
|
return html`${getWeatherStateSVG(state)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state in weatherIcons) {
|
||||||
|
return html`
|
||||||
|
<ha-icon class="weather-icon" .icon=${weatherIcons[state]}></ha-icon>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
};
|
||||||
|
@@ -3,6 +3,7 @@ import { HomeAssistant } from "../types";
|
|||||||
|
|
||||||
export interface ZHAEntityReference extends HassEntity {
|
export interface ZHAEntityReference extends HassEntity {
|
||||||
name: string;
|
name: string;
|
||||||
|
original_name?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ZHADevice {
|
export interface ZHADevice {
|
||||||
@@ -26,6 +27,12 @@ export interface ZHADevice {
|
|||||||
signature: any;
|
signature: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ZHADeviceEndpoint {
|
||||||
|
device: ZHADevice;
|
||||||
|
endpoint_id: number;
|
||||||
|
entities: ZHAEntityReference[];
|
||||||
|
}
|
||||||
|
|
||||||
export interface Attribute {
|
export interface Attribute {
|
||||||
name: string;
|
name: string;
|
||||||
id: number;
|
id: number;
|
||||||
@@ -56,7 +63,12 @@ export interface ReadAttributeServiceData {
|
|||||||
export interface ZHAGroup {
|
export interface ZHAGroup {
|
||||||
name: string;
|
name: string;
|
||||||
group_id: number;
|
group_id: number;
|
||||||
members: ZHADevice[];
|
members: ZHADeviceEndpoint[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ZHAGroupMember {
|
||||||
|
ieee: string;
|
||||||
|
endpoint_id: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const reconfigureNode = (
|
export const reconfigureNode = (
|
||||||
@@ -213,7 +225,7 @@ export const fetchGroup = (
|
|||||||
|
|
||||||
export const fetchGroupableDevices = (
|
export const fetchGroupableDevices = (
|
||||||
hass: HomeAssistant
|
hass: HomeAssistant
|
||||||
): Promise<ZHADevice[]> =>
|
): Promise<ZHADeviceEndpoint[]> =>
|
||||||
hass.callWS({
|
hass.callWS({
|
||||||
type: "zha/devices/groupable",
|
type: "zha/devices/groupable",
|
||||||
});
|
});
|
||||||
@@ -221,7 +233,7 @@ export const fetchGroupableDevices = (
|
|||||||
export const addMembersToGroup = (
|
export const addMembersToGroup = (
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
groupId: number,
|
groupId: number,
|
||||||
membersToAdd: string[]
|
membersToAdd: ZHAGroupMember[]
|
||||||
): Promise<ZHAGroup> =>
|
): Promise<ZHAGroup> =>
|
||||||
hass.callWS({
|
hass.callWS({
|
||||||
type: "zha/group/members/add",
|
type: "zha/group/members/add",
|
||||||
@@ -232,7 +244,7 @@ export const addMembersToGroup = (
|
|||||||
export const removeMembersFromGroup = (
|
export const removeMembersFromGroup = (
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
groupId: number,
|
groupId: number,
|
||||||
membersToRemove: string[]
|
membersToRemove: ZHAGroupMember[]
|
||||||
): Promise<ZHAGroup> =>
|
): Promise<ZHAGroup> =>
|
||||||
hass.callWS({
|
hass.callWS({
|
||||||
type: "zha/group/members/remove",
|
type: "zha/group/members/remove",
|
||||||
@@ -243,7 +255,7 @@ export const removeMembersFromGroup = (
|
|||||||
export const addGroup = (
|
export const addGroup = (
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
groupName: string,
|
groupName: string,
|
||||||
membersToAdd?: string[]
|
membersToAdd?: ZHAGroupMember[]
|
||||||
): Promise<ZHAGroup> =>
|
): Promise<ZHAGroup> =>
|
||||||
hass.callWS({
|
hass.callWS({
|
||||||
type: "zha/group/add",
|
type: "zha/group/add",
|
||||||
|
@@ -339,7 +339,6 @@ class DataEntryFlowDialog extends LitElement {
|
|||||||
ha-icon-button {
|
ha-icon-button {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
margin: 16px 16px 0 0;
|
|
||||||
float: right;
|
float: right;
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
|
@@ -10,6 +10,7 @@ import LocalizeMixin from "../../../mixins/localize-mixin";
|
|||||||
import CoverEntity from "../../../util/cover-model";
|
import CoverEntity from "../../../util/cover-model";
|
||||||
|
|
||||||
const FEATURE_CLASS_NAMES = {
|
const FEATURE_CLASS_NAMES = {
|
||||||
|
4: "has-set_position",
|
||||||
128: "has-set_tilt_position",
|
128: "has-set_tilt_position",
|
||||||
};
|
};
|
||||||
class MoreInfoCover extends LocalizeMixin(PolymerElement) {
|
class MoreInfoCover extends LocalizeMixin(PolymerElement) {
|
||||||
@@ -23,6 +24,7 @@ class MoreInfoCover extends LocalizeMixin(PolymerElement) {
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.has-set_position .current_position,
|
||||||
.has-current_position .current_position,
|
.has-current_position .current_position,
|
||||||
.has-set_tilt_position .tilt,
|
.has-set_tilt_position .tilt,
|
||||||
.has-current_tilt_position .tilt {
|
.has-current_tilt_position .tilt {
|
||||||
|
@@ -456,15 +456,15 @@ export class HaVoiceCommandDialog extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.bouncer {
|
.bouncer {
|
||||||
width: 40px;
|
width: 48px;
|
||||||
height: 40px;
|
height: 48px;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
}
|
}
|
||||||
.double-bounce1,
|
.double-bounce1,
|
||||||
.double-bounce2 {
|
.double-bounce2 {
|
||||||
width: 40px;
|
width: 48px;
|
||||||
height: 40px;
|
height: 48px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
background-color: var(--primary-color);
|
background-color: var(--primary-color);
|
||||||
opacity: 0.2;
|
opacity: 0.2;
|
||||||
|
@@ -2,6 +2,8 @@ import objAssign from "es6-object-assign";
|
|||||||
import "mdn-polyfills/Array.prototype.includes";
|
import "mdn-polyfills/Array.prototype.includes";
|
||||||
import "regenerator-runtime/runtime";
|
import "regenerator-runtime/runtime";
|
||||||
import "unfetch/polyfill";
|
import "unfetch/polyfill";
|
||||||
|
// To use comlink under ES5
|
||||||
|
import "proxy-polyfill";
|
||||||
|
|
||||||
objAssign.polyfill();
|
objAssign.polyfill();
|
||||||
|
|
||||||
|
@@ -104,6 +104,11 @@ window.hassConnection.then(({ conn }) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
window.addEventListener("error", (e) => {
|
window.addEventListener("error", (e) => {
|
||||||
|
if (!__DEV__ && e.message === "ResizeObserver loop limit exceeded") {
|
||||||
|
e.stopImmediatePropagation();
|
||||||
|
e.stopPropagation();
|
||||||
|
return;
|
||||||
|
}
|
||||||
const homeAssistant = document.querySelector("home-assistant") as any;
|
const homeAssistant = document.querySelector("home-assistant") as any;
|
||||||
if (
|
if (
|
||||||
homeAssistant &&
|
homeAssistant &&
|
||||||
|
@@ -1,40 +1,51 @@
|
|||||||
/*
|
/* eslint-disable @typescript-eslint/triple-slash-reference */
|
||||||
This file is not run through webpack, but instead is directly manipulated
|
// eslint-disable-next-line spaced-comment
|
||||||
by Workbox Webpack plugin. So we cannot use __DEV__ or other constants.
|
/// <reference path="../types/service-worker.d.ts" />
|
||||||
*/
|
/* eslint-env serviceworker */
|
||||||
/* global workbox clients */
|
import {
|
||||||
|
CacheFirst,
|
||||||
|
StaleWhileRevalidate,
|
||||||
|
NetworkOnly,
|
||||||
|
} from "workbox-strategies";
|
||||||
|
import { cleanupOutdatedCaches, precacheAndRoute } from "workbox-precaching";
|
||||||
|
import { registerRoute } from "workbox-routing";
|
||||||
|
import { cacheNames } from "workbox-core";
|
||||||
|
|
||||||
|
// Clean up caches from older workboxes and old service workers.
|
||||||
|
// Will help with cleaning up Workbox v4 stuff
|
||||||
|
cleanupOutdatedCaches();
|
||||||
|
|
||||||
function initRouting() {
|
function initRouting() {
|
||||||
workbox.precaching.precacheAndRoute(self.__precacheManifest || []);
|
precacheAndRoute(
|
||||||
|
// @ts-ignore
|
||||||
|
WB_MANIFEST
|
||||||
|
);
|
||||||
|
|
||||||
// Cache static content (including translations) on first access.
|
// Cache static content (including translations) on first access.
|
||||||
workbox.routing.registerRoute(
|
registerRoute(
|
||||||
new RegExp(`${location.host}/(static|frontend_latest|frontend_es5)/.+`),
|
new RegExp(`${location.host}/(static|frontend_latest|frontend_es5)/.+`),
|
||||||
new workbox.strategies.CacheFirst()
|
new CacheFirst()
|
||||||
);
|
);
|
||||||
|
|
||||||
// Get api from network.
|
// Get api from network.
|
||||||
workbox.routing.registerRoute(
|
registerRoute(
|
||||||
new RegExp(`${location.host}/(api|auth)/.*`),
|
new RegExp(`${location.host}/(api|auth)/.*`),
|
||||||
new workbox.strategies.NetworkOnly()
|
new NetworkOnly()
|
||||||
);
|
);
|
||||||
|
|
||||||
// Get manifest, service worker, onboarding from network.
|
// Get manifest, service worker, onboarding from network.
|
||||||
workbox.routing.registerRoute(
|
registerRoute(
|
||||||
new RegExp(
|
new RegExp(
|
||||||
`${location.host}/(service_worker.js|manifest.json|onboarding.html)`
|
`${location.host}/(service_worker.js|manifest.json|onboarding.html)`
|
||||||
),
|
),
|
||||||
new workbox.strategies.NetworkOnly()
|
new NetworkOnly()
|
||||||
);
|
);
|
||||||
|
|
||||||
// For rest of the files (on Home Assistant domain only) try both cache and network.
|
// For rest of the files (on Home Assistant domain only) try both cache and network.
|
||||||
// This includes the root "/" or "/states" response and user files from "/local".
|
// This includes the root "/" or "/states" response and user files from "/local".
|
||||||
// First access might bring stale data from cache, but a single refresh will bring updated
|
// First access might bring stale data from cache, but a single refresh will bring updated
|
||||||
// file.
|
// file.
|
||||||
workbox.routing.registerRoute(
|
registerRoute(new RegExp(`${location.host}/.*`), new StaleWhileRevalidate());
|
||||||
new RegExp(`${location.host}/.*`),
|
|
||||||
new workbox.strategies.StaleWhileRevalidate()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function initPushNotifications() {
|
function initPushNotifications() {
|
||||||
@@ -149,20 +160,24 @@ function initPushNotifications() {
|
|||||||
|
|
||||||
self.addEventListener("install", (event) => {
|
self.addEventListener("install", (event) => {
|
||||||
// Delete all runtime caching, so that index.html has to be refetched.
|
// Delete all runtime caching, so that index.html has to be refetched.
|
||||||
const cacheName = workbox.core.cacheNames.runtime;
|
const cacheName = cacheNames.runtime;
|
||||||
event.waitUntil(caches.delete(cacheName));
|
event.waitUntil(caches.delete(cacheName));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
self.addEventListener("activate", () => {
|
||||||
|
// Attach the service worker to any page of the app
|
||||||
|
// that didn't have a service worker loaded.
|
||||||
|
// Happens the first time they open the app without any
|
||||||
|
// service worker registered.
|
||||||
|
// This will serve code splitted bundles from SW.
|
||||||
|
clients.claim();
|
||||||
|
});
|
||||||
|
|
||||||
self.addEventListener("message", (message) => {
|
self.addEventListener("message", (message) => {
|
||||||
if (message.data.type === "skipWaiting") {
|
if (message.data.type === "skipWaiting") {
|
||||||
self.skipWaiting();
|
self.skipWaiting();
|
||||||
clients.claim();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
workbox.setConfig({
|
|
||||||
debug: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
initRouting();
|
initRouting();
|
||||||
initPushNotifications();
|
initPushNotifications();
|
@@ -18,4 +18,6 @@ export const demoConfig: HassConfig = {
|
|||||||
whitelist_external_dirs: [],
|
whitelist_external_dirs: [],
|
||||||
config_source: "storage",
|
config_source: "storage",
|
||||||
safe_mode: false,
|
safe_mode: false,
|
||||||
|
internal_url: "http://homeassistant.local:8123",
|
||||||
|
external_url: null,
|
||||||
};
|
};
|
||||||
|
@@ -16,7 +16,9 @@ import { navigate } from "../common/navigate";
|
|||||||
import "../components/ha-menu-button";
|
import "../components/ha-menu-button";
|
||||||
import "../components/ha-icon-button-arrow-prev";
|
import "../components/ha-icon-button-arrow-prev";
|
||||||
import { HomeAssistant, Route } from "../types";
|
import { HomeAssistant, Route } from "../types";
|
||||||
|
import "../components/ha-svg-icon";
|
||||||
import "../components/ha-icon";
|
import "../components/ha-icon";
|
||||||
|
import "../components/ha-tab";
|
||||||
|
|
||||||
export interface PageNavigation {
|
export interface PageNavigation {
|
||||||
path: string;
|
path: string;
|
||||||
@@ -26,6 +28,7 @@ export interface PageNavigation {
|
|||||||
core?: boolean;
|
core?: boolean;
|
||||||
advancedOnly?: boolean;
|
advancedOnly?: boolean;
|
||||||
icon?: string;
|
icon?: string;
|
||||||
|
iconPath?: string;
|
||||||
info?: any;
|
info?: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,12 +36,12 @@ export interface PageNavigation {
|
|||||||
class HassTabsSubpage extends LitElement {
|
class HassTabsSubpage extends LitElement {
|
||||||
@property() public hass!: HomeAssistant;
|
@property() public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@property({ type: Boolean }) public hassio = false;
|
||||||
|
|
||||||
@property({ type: String, attribute: "back-path" }) public backPath?: string;
|
@property({ type: String, attribute: "back-path" }) public backPath?: string;
|
||||||
|
|
||||||
@property() public backCallback?: () => void;
|
@property() public backCallback?: () => void;
|
||||||
|
|
||||||
@property({ type: Boolean }) public hassio = false;
|
|
||||||
|
|
||||||
@property({ type: Boolean, attribute: "main-page" }) public mainPage = false;
|
@property({ type: Boolean, attribute: "main-page" }) public mainPage = false;
|
||||||
|
|
||||||
@property() public route!: Route;
|
@property() public route!: Route;
|
||||||
@@ -69,27 +72,23 @@ class HassTabsSubpage extends LitElement {
|
|||||||
return shownTabs.map(
|
return shownTabs.map(
|
||||||
(page) =>
|
(page) =>
|
||||||
html`
|
html`
|
||||||
<div
|
<ha-tab
|
||||||
class="tab ${classMap({
|
.hass=${this.hass}
|
||||||
active: page === activeTab,
|
|
||||||
})}"
|
|
||||||
@click=${this._tabTapped}
|
@click=${this._tabTapped}
|
||||||
.path=${page.path}
|
.path=${page.path}
|
||||||
|
.active=${page === activeTab}
|
||||||
|
.narrow=${this.narrow}
|
||||||
|
.name=${page.translationKey
|
||||||
|
? this.hass.localize(page.translationKey)
|
||||||
|
: page.name}
|
||||||
>
|
>
|
||||||
${this.narrow
|
${page.iconPath
|
||||||
? html` <ha-icon .icon=${page.icon}></ha-icon> `
|
? html`<ha-svg-icon
|
||||||
: ""}
|
slot="icon"
|
||||||
${!this.narrow || page === activeTab
|
.path=${page.iconPath}
|
||||||
? html`
|
></ha-svg-icon>`
|
||||||
<span class="name"
|
: html`<ha-icon slot="icon" .icon=${page.icon}></ha-icon>`}
|
||||||
>${page.translationKey
|
</ha-tab>
|
||||||
? this.hass.localize(page.translationKey)
|
|
||||||
: page.name}</span
|
|
||||||
>
|
|
||||||
`
|
|
||||||
: ""}
|
|
||||||
<mwc-ripple></mwc-ripple>
|
|
||||||
</div>
|
|
||||||
`
|
`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -119,8 +118,8 @@ class HassTabsSubpage extends LitElement {
|
|||||||
${this.mainPage
|
${this.mainPage
|
||||||
? html`
|
? html`
|
||||||
<ha-menu-button
|
<ha-menu-button
|
||||||
.hass=${this.hass}
|
|
||||||
.hassio=${this.hassio}
|
.hassio=${this.hassio}
|
||||||
|
.hass=${this.hass}
|
||||||
.narrow=${this.narrow}
|
.narrow=${this.narrow}
|
||||||
></ha-menu-button>
|
></ha-menu-button>
|
||||||
`
|
`
|
||||||
@@ -150,7 +149,7 @@ class HassTabsSubpage extends LitElement {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _tabTapped(ev: MouseEvent): void {
|
private _tabTapped(ev: Event): void {
|
||||||
navigate(this, (ev.currentTarget as any).path, true);
|
navigate(this, (ev.currentTarget as any).path, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -174,6 +173,10 @@ class HassTabsSubpage extends LitElement {
|
|||||||
background-color: var(--primary-background-color);
|
background-color: var(--primary-background-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ha-menu-button {
|
||||||
|
margin-right: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
.toolbar {
|
.toolbar {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -211,35 +214,6 @@ class HassTabsSubpage extends LitElement {
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab {
|
|
||||||
padding: 0 32px;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
text-align: center;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
height: 64px;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.name {
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tab.active {
|
|
||||||
color: var(--primary-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
#tabbar:not(.bottom-bar) .tab.active {
|
|
||||||
border-bottom: 2px solid var(--primary-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.bottom-bar .tab {
|
|
||||||
padding: 0 16px;
|
|
||||||
width: 20%;
|
|
||||||
min-width: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
:host(:not([narrow])) #toolbar-icon {
|
:host(:not([narrow])) #toolbar-icon {
|
||||||
min-width: 40px;
|
min-width: 40px;
|
||||||
}
|
}
|
||||||
|
@@ -46,7 +46,7 @@ export class HomeAssistantAppEl extends HassElement {
|
|||||||
protected firstUpdated(changedProps) {
|
protected firstUpdated(changedProps) {
|
||||||
super.firstUpdated(changedProps);
|
super.firstUpdated(changedProps);
|
||||||
this._initialize();
|
this._initialize();
|
||||||
setTimeout(registerServiceWorker, 1000);
|
setTimeout(() => registerServiceWorker(this), 1000);
|
||||||
/* polyfill for paper-dropdown */
|
/* polyfill for paper-dropdown */
|
||||||
import(
|
import(
|
||||||
/* webpackChunkName: "polyfill-web-animations-next" */ "web-animations-js/web-animations-next-lite.min"
|
/* webpackChunkName: "polyfill-web-animations-next" */ "web-animations-js/web-animations-next-lite.min"
|
||||||
|
@@ -96,7 +96,7 @@ class HaOnboarding extends litLocalizeLiteMixin(HassElement) {
|
|||||||
import(
|
import(
|
||||||
/* webpackChunkName: "onboarding-core-config" */ "./onboarding-core-config"
|
/* webpackChunkName: "onboarding-core-config" */ "./onboarding-core-config"
|
||||||
);
|
);
|
||||||
registerServiceWorker(false);
|
registerServiceWorker(this, false);
|
||||||
this.addEventListener("onboarding-step", (ev) => this._handleStepDone(ev));
|
this.addEventListener("onboarding-step", (ev) => this._handleStepDone(ev));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -323,6 +323,14 @@ class HAFullCalendar extends LitElement {
|
|||||||
font-family: var(--material-font-family);
|
font-family: var(--material-font-family);
|
||||||
content: "X";
|
content: "X";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.fc-popover {
|
||||||
|
background-color: var(--primary-background-color) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc-popover-header {
|
||||||
|
background-color: var(--secondary-background-color) !important;
|
||||||
|
}
|
||||||
`,
|
`,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@@ -16,7 +16,7 @@ import {
|
|||||||
DataTableColumnContainer,
|
DataTableColumnContainer,
|
||||||
RowClickedEvent,
|
RowClickedEvent,
|
||||||
} from "../../../components/data-table/ha-data-table";
|
} from "../../../components/data-table/ha-data-table";
|
||||||
import "../../../components/ha-fab";
|
import "@material/mwc-fab";
|
||||||
import {
|
import {
|
||||||
AreaRegistryEntry,
|
AreaRegistryEntry,
|
||||||
createAreaRegistryEntry,
|
createAreaRegistryEntry,
|
||||||
@@ -26,6 +26,8 @@ import {
|
|||||||
devicesInArea,
|
devicesInArea,
|
||||||
} from "../../../data/device_registry";
|
} from "../../../data/device_registry";
|
||||||
import { showAlertDialog } from "../../../dialogs/generic/show-dialog-box";
|
import { showAlertDialog } from "../../../dialogs/generic/show-dialog-box";
|
||||||
|
import "../../../components/ha-icon-button";
|
||||||
|
import "../../../components/ha-svg-icon";
|
||||||
import "../../../layouts/hass-loading-screen";
|
import "../../../layouts/hass-loading-screen";
|
||||||
import "../../../layouts/hass-tabs-subpage-data-table";
|
import "../../../layouts/hass-tabs-subpage-data-table";
|
||||||
import { HomeAssistant, Route } from "../../../types";
|
import { HomeAssistant, Route } from "../../../types";
|
||||||
@@ -35,6 +37,7 @@ import {
|
|||||||
loadAreaRegistryDetailDialog,
|
loadAreaRegistryDetailDialog,
|
||||||
showAreaRegistryDetailDialog,
|
showAreaRegistryDetailDialog,
|
||||||
} from "./show-dialog-area-registry-detail";
|
} from "./show-dialog-area-registry-detail";
|
||||||
|
import { mdiPlus } from "@mdi/js";
|
||||||
|
|
||||||
@customElement("ha-config-areas-dashboard")
|
@customElement("ha-config-areas-dashboard")
|
||||||
export class HaConfigAreasDashboard extends LitElement {
|
export class HaConfigAreasDashboard extends LitElement {
|
||||||
@@ -120,15 +123,16 @@ export class HaConfigAreasDashboard extends LitElement {
|
|||||||
@click=${this._showHelp}
|
@click=${this._showHelp}
|
||||||
></ha-icon-button>
|
></ha-icon-button>
|
||||||
</hass-tabs-subpage-data-table>
|
</hass-tabs-subpage-data-table>
|
||||||
<ha-fab
|
<mwc-fab
|
||||||
?is-wide=${this.isWide}
|
?is-wide=${this.isWide}
|
||||||
?narrow=${this.narrow}
|
?narrow=${this.narrow}
|
||||||
icon="hass:plus"
|
|
||||||
title="${this.hass.localize(
|
title="${this.hass.localize(
|
||||||
"ui.panel.config.areas.picker.create_area"
|
"ui.panel.config.areas.picker.create_area"
|
||||||
)}"
|
)}"
|
||||||
@click=${this._createArea}
|
@click=${this._createArea}
|
||||||
></ha-fab>
|
>
|
||||||
|
<ha-svg-icon slot="icon" path=${mdiPlus}></ha-svg-icon>
|
||||||
|
</mwc-fab>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -177,25 +181,25 @@ export class HaConfigAreasDashboard extends LitElement {
|
|||||||
--app-header-background-color: var(--sidebar-background-color);
|
--app-header-background-color: var(--sidebar-background-color);
|
||||||
--app-header-text-color: var(--sidebar-text-color);
|
--app-header-text-color: var(--sidebar-text-color);
|
||||||
}
|
}
|
||||||
ha-fab {
|
mwc-fab {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
bottom: 16px;
|
bottom: 16px;
|
||||||
right: 16px;
|
right: 16px;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
ha-fab[is-wide] {
|
mwc-fab[is-wide] {
|
||||||
bottom: 24px;
|
bottom: 24px;
|
||||||
right: 24px;
|
right: 24px;
|
||||||
}
|
}
|
||||||
ha-fab[narrow] {
|
mwc-fab[narrow] {
|
||||||
bottom: 84px;
|
bottom: 84px;
|
||||||
}
|
}
|
||||||
ha-fab.rtl {
|
mwc-fab.rtl {
|
||||||
right: auto;
|
right: auto;
|
||||||
left: 16px;
|
left: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
ha-fab[is-wide].rtl {
|
mwc-fab[is-wide].rtl {
|
||||||
bottom: 24px;
|
bottom: 24px;
|
||||||
right: auto;
|
right: auto;
|
||||||
left: 24px;
|
left: 24px;
|
||||||
|
@@ -14,7 +14,8 @@ import { classMap } from "lit-html/directives/class-map";
|
|||||||
import { navigate } from "../../../common/navigate";
|
import { navigate } from "../../../common/navigate";
|
||||||
import { computeRTL } from "../../../common/util/compute_rtl";
|
import { computeRTL } from "../../../common/util/compute_rtl";
|
||||||
import "../../../components/ha-card";
|
import "../../../components/ha-card";
|
||||||
import "../../../components/ha-fab";
|
import "../../../components/ha-svg-icon";
|
||||||
|
import "@material/mwc-fab";
|
||||||
import {
|
import {
|
||||||
AutomationConfig,
|
AutomationConfig,
|
||||||
AutomationEntity,
|
AutomationEntity,
|
||||||
@@ -40,6 +41,7 @@ import { HaDeviceAction } from "./action/types/ha-automation-action-device_id";
|
|||||||
import "./condition/ha-automation-condition";
|
import "./condition/ha-automation-condition";
|
||||||
import "./trigger/ha-automation-trigger";
|
import "./trigger/ha-automation-trigger";
|
||||||
import { HaDeviceTrigger } from "./trigger/types/ha-automation-trigger-device";
|
import { HaDeviceTrigger } from "./trigger/types/ha-automation-trigger-device";
|
||||||
|
import { mdiContentSave } from "@mdi/js";
|
||||||
|
|
||||||
export class HaAutomationEditor extends LitElement {
|
export class HaAutomationEditor extends LitElement {
|
||||||
@property() public hass!: HomeAssistant;
|
@property() public hass!: HomeAssistant;
|
||||||
@@ -244,11 +246,10 @@ export class HaAutomationEditor extends LitElement {
|
|||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
<ha-fab
|
<mwc-fab
|
||||||
?is-wide="${this.isWide}"
|
?is-wide="${this.isWide}"
|
||||||
?narrow="${this.narrow}"
|
?narrow="${this.narrow}"
|
||||||
?dirty="${this._dirty}"
|
?dirty="${this._dirty}"
|
||||||
icon="hass:content-save"
|
|
||||||
.title="${this.hass.localize(
|
.title="${this.hass.localize(
|
||||||
"ui.panel.config.automation.editor.save"
|
"ui.panel.config.automation.editor.save"
|
||||||
)}"
|
)}"
|
||||||
@@ -256,7 +257,9 @@ export class HaAutomationEditor extends LitElement {
|
|||||||
class="${classMap({
|
class="${classMap({
|
||||||
rtl: computeRTL(this.hass),
|
rtl: computeRTL(this.hass),
|
||||||
})}"
|
})}"
|
||||||
></ha-fab>
|
>
|
||||||
|
<ha-svg-icon slot="icon" path=${mdiContentSave}></ha-svg-icon>
|
||||||
|
</mwc-fab>
|
||||||
</hass-tabs-subpage>
|
</hass-tabs-subpage>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
@@ -452,7 +455,7 @@ export class HaAutomationEditor extends LitElement {
|
|||||||
ha-entity-toggle {
|
ha-entity-toggle {
|
||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
}
|
}
|
||||||
ha-fab {
|
mwc-fab {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
bottom: 16px;
|
bottom: 16px;
|
||||||
right: 16px;
|
right: 16px;
|
||||||
@@ -461,24 +464,24 @@ export class HaAutomationEditor extends LitElement {
|
|||||||
transition: margin-bottom 0.3s;
|
transition: margin-bottom 0.3s;
|
||||||
}
|
}
|
||||||
|
|
||||||
ha-fab[is-wide] {
|
mwc-fab[is-wide] {
|
||||||
bottom: 24px;
|
bottom: 24px;
|
||||||
right: 24px;
|
right: 24px;
|
||||||
}
|
}
|
||||||
ha-fab[narrow] {
|
mwc-fab[narrow] {
|
||||||
bottom: 84px;
|
bottom: 84px;
|
||||||
margin-bottom: -140px;
|
margin-bottom: -140px;
|
||||||
}
|
}
|
||||||
ha-fab[dirty] {
|
mwc-fab[dirty] {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
ha-fab.rtl {
|
mwc-fab.rtl {
|
||||||
right: auto;
|
right: auto;
|
||||||
left: 16px;
|
left: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
ha-fab[is-wide].rtl {
|
mwc-fab[is-wide].rtl {
|
||||||
bottom: 24px;
|
bottom: 24px;
|
||||||
right: auto;
|
right: auto;
|
||||||
left: 24px;
|
left: 24px;
|
||||||
|
@@ -18,7 +18,7 @@ import { computeStateName } from "../../../common/entity/compute_state_name";
|
|||||||
import { computeRTL } from "../../../common/util/compute_rtl";
|
import { computeRTL } from "../../../common/util/compute_rtl";
|
||||||
import { DataTableColumnContainer } from "../../../components/data-table/ha-data-table";
|
import { DataTableColumnContainer } from "../../../components/data-table/ha-data-table";
|
||||||
import "../../../components/entity/ha-entity-toggle";
|
import "../../../components/entity/ha-entity-toggle";
|
||||||
import "../../../components/ha-fab";
|
import "@material/mwc-fab";
|
||||||
import {
|
import {
|
||||||
AutomationConfig,
|
AutomationConfig,
|
||||||
AutomationEntity,
|
AutomationEntity,
|
||||||
@@ -30,6 +30,8 @@ import { haStyle } from "../../../resources/styles";
|
|||||||
import { HomeAssistant, Route } from "../../../types";
|
import { HomeAssistant, Route } from "../../../types";
|
||||||
import { configSections } from "../ha-panel-config";
|
import { configSections } from "../ha-panel-config";
|
||||||
import { showThingtalkDialog } from "./show-dialog-thingtalk";
|
import { showThingtalkDialog } from "./show-dialog-thingtalk";
|
||||||
|
import "../../../components/ha-svg-icon";
|
||||||
|
import { mdiPlus } from "@mdi/js";
|
||||||
|
|
||||||
@customElement("ha-automation-picker")
|
@customElement("ha-automation-picker")
|
||||||
class HaAutomationPicker extends LitElement {
|
class HaAutomationPicker extends LitElement {
|
||||||
@@ -168,17 +170,18 @@ class HaAutomationPicker extends LitElement {
|
|||||||
hasFab
|
hasFab
|
||||||
>
|
>
|
||||||
</hass-tabs-subpage-data-table>
|
</hass-tabs-subpage-data-table>
|
||||||
<ha-fab
|
<mwc-fab
|
||||||
slot="fab"
|
slot="fab"
|
||||||
?is-wide=${this.isWide}
|
?is-wide=${this.isWide}
|
||||||
?narrow=${this.narrow}
|
?narrow=${this.narrow}
|
||||||
icon="hass:plus"
|
|
||||||
title=${this.hass.localize(
|
title=${this.hass.localize(
|
||||||
"ui.panel.config.automation.picker.add_automation"
|
"ui.panel.config.automation.picker.add_automation"
|
||||||
)}
|
)}
|
||||||
?rtl=${computeRTL(this.hass)}
|
?rtl=${computeRTL(this.hass)}
|
||||||
@click=${this._createNew}
|
@click=${this._createNew}
|
||||||
></ha-fab>
|
>
|
||||||
|
<ha-svg-icon slot="icon" path=${mdiPlus}></ha-svg-icon>
|
||||||
|
</mwc-fab>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -208,7 +211,7 @@ class HaAutomationPicker extends LitElement {
|
|||||||
return [
|
return [
|
||||||
haStyle,
|
haStyle,
|
||||||
css`
|
css`
|
||||||
ha-fab {
|
mwc-fab {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
bottom: 16px;
|
bottom: 16px;
|
||||||
right: 16px;
|
right: 16px;
|
||||||
@@ -216,19 +219,19 @@ class HaAutomationPicker extends LitElement {
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
ha-fab[is-wide] {
|
mwc-fab[is-wide] {
|
||||||
bottom: 24px;
|
bottom: 24px;
|
||||||
right: 24px;
|
right: 24px;
|
||||||
}
|
}
|
||||||
ha-fab[narrow] {
|
mwc-fab[narrow] {
|
||||||
bottom: 84px;
|
bottom: 84px;
|
||||||
}
|
}
|
||||||
ha-fab[rtl] {
|
mwc-fab[rtl] {
|
||||||
right: auto;
|
right: auto;
|
||||||
left: 16px;
|
left: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
ha-fab[rtl][is-wide] {
|
mwc-fab[rtl][is-wide] {
|
||||||
bottom: 24px;
|
bottom: 24px;
|
||||||
right: auto;
|
right: auto;
|
||||||
left: 24px;
|
left: 24px;
|
||||||
|
@@ -76,7 +76,7 @@ class HaConfigAutomation extends HassRouterPage {
|
|||||||
if (this.hass) {
|
if (this.hass) {
|
||||||
if (!pageEl.automations || !changedProps) {
|
if (!pageEl.automations || !changedProps) {
|
||||||
pageEl.automations = this._getAutomations(this.hass.states);
|
pageEl.automations = this._getAutomations(this.hass.states);
|
||||||
} else if (changedProps && changedProps.has("hass")) {
|
} else if (changedProps.has("hass")) {
|
||||||
this._debouncedUpdateAutomations(pageEl);
|
this._debouncedUpdateAutomations(pageEl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -11,6 +11,7 @@ import "../../../resources/ha-style";
|
|||||||
import "../ha-config-section";
|
import "../ha-config-section";
|
||||||
import "./ha-config-core-form";
|
import "./ha-config-core-form";
|
||||||
import "./ha-config-name-form";
|
import "./ha-config-name-form";
|
||||||
|
import "./ha-config-url-form";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* @appliesMixin LocalizeMixin
|
* @appliesMixin LocalizeMixin
|
||||||
@@ -59,6 +60,7 @@ class HaConfigSectionCore extends LocalizeMixin(PolymerElement) {
|
|||||||
|
|
||||||
<ha-config-name-form hass="[[hass]]"></ha-config-name-form>
|
<ha-config-name-form hass="[[hass]]"></ha-config-name-form>
|
||||||
<ha-config-core-form hass="[[hass]]"></ha-config-core-form>
|
<ha-config-core-form hass="[[hass]]"></ha-config-core-form>
|
||||||
|
<ha-config-url-form hass="[[hass]]"></ha-config-url-form>
|
||||||
</ha-config-section>
|
</ha-config-section>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
168
src/panels/config/core/ha-config-url-form.ts
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
import "@material/mwc-button/mwc-button";
|
||||||
|
import "@polymer/paper-input/paper-input";
|
||||||
|
import type { PaperInputElement } from "@polymer/paper-input/paper-input";
|
||||||
|
import {
|
||||||
|
css,
|
||||||
|
CSSResult,
|
||||||
|
customElement,
|
||||||
|
html,
|
||||||
|
LitElement,
|
||||||
|
property,
|
||||||
|
TemplateResult,
|
||||||
|
} from "lit-element";
|
||||||
|
import "../../../components/ha-card";
|
||||||
|
import { saveCoreConfig } from "../../../data/core";
|
||||||
|
import type { PolymerChangedEvent } from "../../../polymer-types";
|
||||||
|
import type { HomeAssistant } from "../../../types";
|
||||||
|
|
||||||
|
@customElement("ha-config-url-form")
|
||||||
|
class ConfigUrlForm extends LitElement {
|
||||||
|
@property() public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@property() private _error?: string;
|
||||||
|
|
||||||
|
@property() private _working = false;
|
||||||
|
|
||||||
|
@property() private _external_url?: string;
|
||||||
|
|
||||||
|
@property() private _internal_url?: string;
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
const canEdit = ["storage", "default"].includes(
|
||||||
|
this.hass.config.config_source
|
||||||
|
);
|
||||||
|
const disabled = this._working || !canEdit;
|
||||||
|
|
||||||
|
if (!this.hass.userData?.showAdvanced) {
|
||||||
|
return html``;
|
||||||
|
}
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<ha-card>
|
||||||
|
<div class="card-content">
|
||||||
|
${!canEdit
|
||||||
|
? html`
|
||||||
|
<p>
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.core.section.core.core_config.edit_requires_storage"
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
|
${this._error ? html`<div class="error">${this._error}</div>` : ""}
|
||||||
|
<div class="row">
|
||||||
|
<div class="flex">
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.core.section.core.core_config.external_url"
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<paper-input
|
||||||
|
class="flex"
|
||||||
|
.label=${this.hass.localize(
|
||||||
|
"ui.panel.config.core.section.core.core_config.external_url"
|
||||||
|
)}
|
||||||
|
name="external_url"
|
||||||
|
type="url"
|
||||||
|
.disabled=${disabled}
|
||||||
|
.value=${this._externalUrlValue}
|
||||||
|
@value-changed=${this._handleChange}
|
||||||
|
>
|
||||||
|
</paper-input>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="flex">
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.core.section.core.core_config.internal_url"
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<paper-input
|
||||||
|
class="flex"
|
||||||
|
.label=${this.hass.localize(
|
||||||
|
"ui.panel.config.core.section.core.core_config.internal_url"
|
||||||
|
)}
|
||||||
|
name="internal_url"
|
||||||
|
type="url"
|
||||||
|
.disabled=${disabled}
|
||||||
|
.value=${this._internalUrlValue}
|
||||||
|
@value-changed=${this._handleChange}
|
||||||
|
>
|
||||||
|
</paper-input>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-actions">
|
||||||
|
<mwc-button @click=${this._save} .disabled=${disabled}>
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.core.section.core.core_config.save_button"
|
||||||
|
)}
|
||||||
|
</mwc-button>
|
||||||
|
</div>
|
||||||
|
</ha-card>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private get _internalUrlValue() {
|
||||||
|
return this._internal_url !== undefined
|
||||||
|
? this._internal_url
|
||||||
|
: this.hass.config.internal_url;
|
||||||
|
}
|
||||||
|
|
||||||
|
private get _externalUrlValue() {
|
||||||
|
return this._external_url !== undefined
|
||||||
|
? this._external_url
|
||||||
|
: this.hass.config.external_url;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _handleChange(ev: PolymerChangedEvent<string>) {
|
||||||
|
const target = ev.currentTarget as PaperInputElement;
|
||||||
|
this[`_${target.name}`] = target.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _save() {
|
||||||
|
this._working = true;
|
||||||
|
this._error = undefined;
|
||||||
|
try {
|
||||||
|
await saveCoreConfig(this.hass, {
|
||||||
|
external_url: this._external_url || null,
|
||||||
|
internal_url: this._internal_url || null,
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
this._error = err.message || err;
|
||||||
|
} finally {
|
||||||
|
this._working = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResult {
|
||||||
|
return css`
|
||||||
|
.row {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
margin: 0 -8px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.secondary {
|
||||||
|
color: var(--secondary-text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row > * {
|
||||||
|
margin: 0 8px;
|
||||||
|
}
|
||||||
|
.error {
|
||||||
|
color: var(--error-color);
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ha-config-url-form": ConfigUrlForm;
|
||||||
|
}
|
||||||
|
}
|
@@ -72,7 +72,11 @@ export class HaDeviceEntitiesCard extends LitElement {
|
|||||||
class="show-more"
|
class="show-more"
|
||||||
@click=${this._toggleShowDisabled}
|
@click=${this._toggleShowDisabled}
|
||||||
>
|
>
|
||||||
+${disabledEntities.length} disabled entities
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.devices.entities.disabled_entities",
|
||||||
|
"count",
|
||||||
|
disabledEntities.length
|
||||||
|
)}
|
||||||
</button>
|
</button>
|
||||||
`
|
`
|
||||||
: html`
|
: html`
|
||||||
@@ -83,7 +87,9 @@ export class HaDeviceEntitiesCard extends LitElement {
|
|||||||
class="show-more"
|
class="show-more"
|
||||||
@click=${this._toggleShowDisabled}
|
@click=${this._toggleShowDisabled}
|
||||||
>
|
>
|
||||||
Hide disabled
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.devices.entities.hide_disabled"
|
||||||
|
)}
|
||||||
</button>
|
</button>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
|
@@ -22,7 +22,7 @@ import {
|
|||||||
DataTableColumnContainer,
|
DataTableColumnContainer,
|
||||||
RowClickedEvent,
|
RowClickedEvent,
|
||||||
} from "../../../components/data-table/ha-data-table";
|
} from "../../../components/data-table/ha-data-table";
|
||||||
import "../../../components/ha-fab";
|
import "@material/mwc-fab";
|
||||||
import "../../../components/ha-icon";
|
import "../../../components/ha-icon";
|
||||||
import "../../../layouts/hass-loading-screen";
|
import "../../../layouts/hass-loading-screen";
|
||||||
import "../../../layouts/hass-tabs-subpage-data-table";
|
import "../../../layouts/hass-tabs-subpage-data-table";
|
||||||
@@ -31,6 +31,8 @@ import { showEntityEditorDialog } from "../entities/show-dialog-entity-editor";
|
|||||||
import { configSections } from "../ha-panel-config";
|
import { configSections } from "../ha-panel-config";
|
||||||
import { HELPER_DOMAINS } from "./const";
|
import { HELPER_DOMAINS } from "./const";
|
||||||
import { showHelperDetailDialog } from "./show-dialog-helper-detail";
|
import { showHelperDetailDialog } from "./show-dialog-helper-detail";
|
||||||
|
import "../../../components/ha-svg-icon";
|
||||||
|
import { mdiPlus } from "@mdi/js";
|
||||||
|
|
||||||
@customElement("ha-config-helpers")
|
@customElement("ha-config-helpers")
|
||||||
export class HaConfigHelpers extends LitElement {
|
export class HaConfigHelpers extends LitElement {
|
||||||
@@ -154,15 +156,16 @@ export class HaConfigHelpers extends LitElement {
|
|||||||
hasFab
|
hasFab
|
||||||
>
|
>
|
||||||
</hass-tabs-subpage-data-table>
|
</hass-tabs-subpage-data-table>
|
||||||
<ha-fab
|
<mwc-fab
|
||||||
?is-wide=${this.isWide}
|
?is-wide=${this.isWide}
|
||||||
?narrow=${this.narrow}
|
?narrow=${this.narrow}
|
||||||
icon="hass:plus"
|
|
||||||
title="${this.hass.localize(
|
title="${this.hass.localize(
|
||||||
"ui.panel.config.helpers.picker.add_helper"
|
"ui.panel.config.helpers.picker.add_helper"
|
||||||
)}"
|
)}"
|
||||||
@click=${this._createHelpler}
|
@click=${this._createHelpler}
|
||||||
></ha-fab>
|
>
|
||||||
|
<ha-svg-icon slot="icon" path=${mdiPlus}></ha-svg-icon>
|
||||||
|
</mwc-fab>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -209,17 +212,17 @@ export class HaConfigHelpers extends LitElement {
|
|||||||
|
|
||||||
static get styles(): CSSResult {
|
static get styles(): CSSResult {
|
||||||
return css`
|
return css`
|
||||||
ha-fab {
|
mwc-fab {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
bottom: 16px;
|
bottom: 16px;
|
||||||
right: 16px;
|
right: 16px;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
ha-fab[is-wide] {
|
mwc-fab[is-wide] {
|
||||||
bottom: 24px;
|
bottom: 24px;
|
||||||
right: 24px;
|
right: 24px;
|
||||||
}
|
}
|
||||||
ha-fab[narrow] {
|
mwc-fab[narrow] {
|
||||||
bottom: 84px;
|
bottom: 84px;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|