mirror of
https://github.com/home-assistant/frontend.git
synced 2025-08-25 09:09:26 +00:00
Compare commits
1 Commits
ha-formfie
...
data-disk-
Author | SHA1 | Date | |
---|---|---|---|
![]() |
2c440976aa |
@@ -29,7 +29,6 @@
|
|||||||
"__BUILD__": false,
|
"__BUILD__": false,
|
||||||
"__VERSION__": false,
|
"__VERSION__": false,
|
||||||
"__STATIC_PATH__": false,
|
"__STATIC_PATH__": false,
|
||||||
"__SUPERVISOR__": false,
|
|
||||||
"Polymer": true
|
"Polymer": true
|
||||||
},
|
},
|
||||||
"env": {
|
"env": {
|
||||||
@@ -112,7 +111,8 @@
|
|||||||
],
|
],
|
||||||
"unused-imports/no-unused-imports": "error",
|
"unused-imports/no-unused-imports": "error",
|
||||||
"lit/attribute-value-entities": "off",
|
"lit/attribute-value-entities": "off",
|
||||||
"lit/no-template-map": "off"
|
"lit/no-template-map": "off",
|
||||||
|
"lit/no-template-arrow": "warn"
|
||||||
},
|
},
|
||||||
"plugins": ["disable", "unused-imports"],
|
"plugins": ["disable", "unused-imports"],
|
||||||
"processor": "disable/disable"
|
"processor": "disable/disable"
|
||||||
|
4
.github/workflows/ci.yaml
vendored
4
.github/workflows/ci.yaml
vendored
@@ -30,7 +30,7 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
CI: true
|
CI: true
|
||||||
- name: Build resources
|
- name: Build resources
|
||||||
run: ./node_modules/.bin/gulp gen-icons-json build-translations build-locale-data gather-gallery-demos
|
run: ./node_modules/.bin/gulp gen-icons-json build-translations gather-gallery-demos
|
||||||
- name: Run eslint
|
- name: Run eslint
|
||||||
run: yarn run lint:eslint
|
run: yarn run lint:eslint
|
||||||
- name: Run tsc
|
- name: Run tsc
|
||||||
@@ -53,8 +53,6 @@ jobs:
|
|||||||
run: yarn install
|
run: yarn install
|
||||||
env:
|
env:
|
||||||
CI: true
|
CI: true
|
||||||
- name: Build resources
|
|
||||||
run: ./node_modules/.bin/gulp build-translations build-locale-data
|
|
||||||
- name: Run Tests
|
- name: Run Tests
|
||||||
run: yarn run test
|
run: yarn run test
|
||||||
build:
|
build:
|
||||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
# build
|
# build
|
||||||
build
|
build
|
||||||
|
build-translations/*
|
||||||
hass_frontend/*
|
hass_frontend/*
|
||||||
dist
|
dist
|
||||||
|
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
build
|
build
|
||||||
|
build-translations/*
|
||||||
translations/*
|
translations/*
|
||||||
node_modules/*
|
node_modules/*
|
||||||
hass_frontend/*
|
hass_frontend/*
|
||||||
|
@@ -1,12 +0,0 @@
|
|||||||
diff --git a/mwc-icon-button-base.js b/mwc-icon-button-base.js
|
|
||||||
index 45cdaab93ccc0a6daaaaabc01266dcdc32e46bfd..b3ea5b541597308d85f86ce6c23fd00785fda835 100644
|
|
||||||
--- a/mwc-icon-button-base.js
|
|
||||||
+++ b/mwc-icon-button-base.js
|
|
||||||
@@ -63,7 +63,6 @@ export class IconButtonBase extends LitElement {
|
|
||||||
@touchend="${this.handleRippleDeactivate}"
|
|
||||||
@touchcancel="${this.handleRippleDeactivate}"
|
|
||||||
>${this.renderRipple()}
|
|
||||||
- <i class="material-icons">${this.icon}</i>
|
|
||||||
<span
|
|
||||||
><slot></slot
|
|
||||||
></span>
|
|
@@ -35,7 +35,6 @@ module.exports.definedVars = ({ isProdBuild, latestBuild, defineOverlay }) => ({
|
|||||||
__BUILD__: JSON.stringify(latestBuild ? "latest" : "es5"),
|
__BUILD__: JSON.stringify(latestBuild ? "latest" : "es5"),
|
||||||
__VERSION__: JSON.stringify(env.version()),
|
__VERSION__: JSON.stringify(env.version()),
|
||||||
__DEMO__: false,
|
__DEMO__: false,
|
||||||
__SUPERVISOR__: false,
|
|
||||||
__BACKWARDS_COMPAT__: false,
|
__BACKWARDS_COMPAT__: false,
|
||||||
__STATIC_PATH__: "/static/",
|
__STATIC_PATH__: "/static/",
|
||||||
"process.env.NODE_ENV": JSON.stringify(
|
"process.env.NODE_ENV": JSON.stringify(
|
||||||
@@ -165,7 +164,6 @@ module.exports.config = {
|
|||||||
cast({ isProdBuild, latestBuild }) {
|
cast({ isProdBuild, latestBuild }) {
|
||||||
const entry = {
|
const entry = {
|
||||||
launcher: path.resolve(paths.cast_dir, "src/launcher/entrypoint.ts"),
|
launcher: path.resolve(paths.cast_dir, "src/launcher/entrypoint.ts"),
|
||||||
media: path.resolve(paths.cast_dir, "src/media/entrypoint.ts"),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (latestBuild) {
|
if (latestBuild) {
|
||||||
@@ -196,9 +194,6 @@ module.exports.config = {
|
|||||||
publicPath: publicPath(latestBuild, paths.hassio_publicPath),
|
publicPath: publicPath(latestBuild, paths.hassio_publicPath),
|
||||||
isProdBuild,
|
isProdBuild,
|
||||||
latestBuild,
|
latestBuild,
|
||||||
defineOverlay: {
|
|
||||||
__SUPERVISOR__: true,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -211,9 +206,6 @@ module.exports.config = {
|
|||||||
publicPath: publicPath(latestBuild),
|
publicPath: publicPath(latestBuild),
|
||||||
isProdBuild,
|
isProdBuild,
|
||||||
latestBuild,
|
latestBuild,
|
||||||
defineOverlay: {
|
|
||||||
__DEMO__: true,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@@ -5,7 +5,6 @@ const env = require("../env");
|
|||||||
|
|
||||||
require("./clean.js");
|
require("./clean.js");
|
||||||
require("./translations.js");
|
require("./translations.js");
|
||||||
require("./locale-data.js");
|
|
||||||
require("./gen-icons-json.js");
|
require("./gen-icons-json.js");
|
||||||
require("./gather-static.js");
|
require("./gather-static.js");
|
||||||
require("./compress.js");
|
require("./compress.js");
|
||||||
@@ -27,8 +26,7 @@ gulp.task(
|
|||||||
"gen-icons-json",
|
"gen-icons-json",
|
||||||
"gen-pages-dev",
|
"gen-pages-dev",
|
||||||
"gen-index-app-dev",
|
"gen-index-app-dev",
|
||||||
"build-translations",
|
"build-translations"
|
||||||
"build-locale-data"
|
|
||||||
),
|
),
|
||||||
"copy-static-app",
|
"copy-static-app",
|
||||||
env.useWDS()
|
env.useWDS()
|
||||||
@@ -46,7 +44,7 @@ gulp.task(
|
|||||||
process.env.NODE_ENV = "production";
|
process.env.NODE_ENV = "production";
|
||||||
},
|
},
|
||||||
"clean",
|
"clean",
|
||||||
gulp.parallel("gen-icons-json", "build-translations", "build-locale-data"),
|
gulp.parallel("gen-icons-json", "build-translations"),
|
||||||
"copy-static-app",
|
"copy-static-app",
|
||||||
env.useRollup() ? "rollup-prod-app" : "webpack-prod-app",
|
env.useRollup() ? "rollup-prod-app" : "webpack-prod-app",
|
||||||
// Don't compress running tests
|
// Don't compress running tests
|
||||||
|
@@ -18,7 +18,7 @@ gulp.task(
|
|||||||
},
|
},
|
||||||
"clean-cast",
|
"clean-cast",
|
||||||
"translations-enable-merge-backend",
|
"translations-enable-merge-backend",
|
||||||
gulp.parallel("gen-icons-json", "build-translations", "build-locale-data"),
|
gulp.parallel("gen-icons-json", "build-translations"),
|
||||||
"copy-static-cast",
|
"copy-static-cast",
|
||||||
"gen-index-cast-dev",
|
"gen-index-cast-dev",
|
||||||
env.useRollup() ? "rollup-dev-server-cast" : "webpack-dev-server-cast"
|
env.useRollup() ? "rollup-dev-server-cast" : "webpack-dev-server-cast"
|
||||||
@@ -33,7 +33,7 @@ gulp.task(
|
|||||||
},
|
},
|
||||||
"clean-cast",
|
"clean-cast",
|
||||||
"translations-enable-merge-backend",
|
"translations-enable-merge-backend",
|
||||||
gulp.parallel("gen-icons-json", "build-translations", "build-locale-data"),
|
gulp.parallel("gen-icons-json", "build-translations"),
|
||||||
"copy-static-cast",
|
"copy-static-cast",
|
||||||
env.useRollup() ? "rollup-prod-cast" : "webpack-prod-cast",
|
env.useRollup() ? "rollup-prod-cast" : "webpack-prod-cast",
|
||||||
"gen-index-cast-prod"
|
"gen-index-cast-prod"
|
||||||
|
@@ -20,12 +20,7 @@ gulp.task(
|
|||||||
},
|
},
|
||||||
"clean-demo",
|
"clean-demo",
|
||||||
"translations-enable-merge-backend",
|
"translations-enable-merge-backend",
|
||||||
gulp.parallel(
|
gulp.parallel("gen-icons-json", "gen-index-demo-dev", "build-translations"),
|
||||||
"gen-icons-json",
|
|
||||||
"gen-index-demo-dev",
|
|
||||||
"build-translations",
|
|
||||||
"build-locale-data"
|
|
||||||
),
|
|
||||||
"copy-static-demo",
|
"copy-static-demo",
|
||||||
env.useRollup() ? "rollup-dev-server-demo" : "webpack-dev-server-demo"
|
env.useRollup() ? "rollup-dev-server-demo" : "webpack-dev-server-demo"
|
||||||
)
|
)
|
||||||
@@ -40,7 +35,7 @@ gulp.task(
|
|||||||
"clean-demo",
|
"clean-demo",
|
||||||
// Cast needs to be backwards compatible and older HA has no translations
|
// Cast needs to be backwards compatible and older HA has no translations
|
||||||
"translations-enable-merge-backend",
|
"translations-enable-merge-backend",
|
||||||
gulp.parallel("gen-icons-json", "build-translations", "build-locale-data"),
|
gulp.parallel("gen-icons-json", "build-translations"),
|
||||||
"copy-static-demo",
|
"copy-static-demo",
|
||||||
env.useRollup() ? "rollup-prod-demo" : "webpack-prod-demo",
|
env.useRollup() ? "rollup-prod-demo" : "webpack-prod-demo",
|
||||||
"gen-index-demo-prod"
|
"gen-index-demo-prod"
|
||||||
|
@@ -154,15 +154,6 @@ gulp.task("gen-index-cast-dev", (done) => {
|
|||||||
contentReceiver
|
contentReceiver
|
||||||
);
|
);
|
||||||
|
|
||||||
const contentMedia = renderCastTemplate("media", {
|
|
||||||
latestMediaJS: "/frontend_latest/media.js",
|
|
||||||
es5MediaJS: "/frontend_es5/media.js",
|
|
||||||
});
|
|
||||||
fs.outputFileSync(
|
|
||||||
path.resolve(paths.cast_output_root, "media.html"),
|
|
||||||
contentMedia
|
|
||||||
);
|
|
||||||
|
|
||||||
const contentFAQ = renderCastTemplate("launcher-faq", {
|
const contentFAQ = renderCastTemplate("launcher-faq", {
|
||||||
latestLauncherJS: "/frontend_latest/launcher.js",
|
latestLauncherJS: "/frontend_latest/launcher.js",
|
||||||
es5LauncherJS: "/frontend_es5/launcher.js",
|
es5LauncherJS: "/frontend_es5/launcher.js",
|
||||||
@@ -201,15 +192,6 @@ gulp.task("gen-index-cast-prod", (done) => {
|
|||||||
contentReceiver
|
contentReceiver
|
||||||
);
|
);
|
||||||
|
|
||||||
const contentMedia = renderCastTemplate("media", {
|
|
||||||
latestMediaJS: latestManifest["media.js"],
|
|
||||||
es5MediaJS: es5Manifest["media.js"],
|
|
||||||
});
|
|
||||||
fs.outputFileSync(
|
|
||||||
path.resolve(paths.cast_output_root, "media.html"),
|
|
||||||
contentMedia
|
|
||||||
);
|
|
||||||
|
|
||||||
const contentFAQ = renderCastTemplate("launcher-faq", {
|
const contentFAQ = renderCastTemplate("launcher-faq", {
|
||||||
latestLauncherJS: latestManifest["launcher.js"],
|
latestLauncherJS: latestManifest["launcher.js"],
|
||||||
es5LauncherJS: es5Manifest["launcher.js"],
|
es5LauncherJS: es5Manifest["launcher.js"],
|
||||||
|
@@ -51,7 +51,6 @@ gulp.task(
|
|||||||
gulp.parallel(
|
gulp.parallel(
|
||||||
"gen-icons-json",
|
"gen-icons-json",
|
||||||
"build-translations",
|
"build-translations",
|
||||||
"build-locale-data",
|
|
||||||
"gather-gallery-demos"
|
"gather-gallery-demos"
|
||||||
),
|
),
|
||||||
"copy-static-gallery",
|
"copy-static-gallery",
|
||||||
@@ -71,7 +70,6 @@ gulp.task(
|
|||||||
gulp.parallel(
|
gulp.parallel(
|
||||||
"gen-icons-json",
|
"gen-icons-json",
|
||||||
"build-translations",
|
"build-translations",
|
||||||
"build-locale-data",
|
|
||||||
"gather-gallery-demos"
|
"gather-gallery-demos"
|
||||||
),
|
),
|
||||||
"copy-static-gallery",
|
"copy-static-gallery",
|
||||||
|
@@ -22,18 +22,11 @@ function copyTranslations(staticDir) {
|
|||||||
|
|
||||||
// Translation output
|
// Translation output
|
||||||
fs.copySync(
|
fs.copySync(
|
||||||
polyPath("build/translations/output"),
|
polyPath("build-translations/output"),
|
||||||
staticPath("translations")
|
staticPath("translations")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function copyLocaleData(staticDir) {
|
|
||||||
const staticPath = genStaticPath(staticDir);
|
|
||||||
|
|
||||||
// Locale data output
|
|
||||||
fs.copySync(polyPath("build/locale-data"), staticPath("locale-data"));
|
|
||||||
}
|
|
||||||
|
|
||||||
function copyMdiIcons(staticDir) {
|
function copyMdiIcons(staticDir) {
|
||||||
const staticPath = genStaticPath(staticDir);
|
const staticPath = genStaticPath(staticDir);
|
||||||
|
|
||||||
@@ -91,11 +84,6 @@ function copyMapPanel(staticDir) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
gulp.task("copy-locale-data", async () => {
|
|
||||||
const staticDir = paths.app_output_static;
|
|
||||||
copyLocaleData(staticDir);
|
|
||||||
});
|
|
||||||
|
|
||||||
gulp.task("copy-translations-app", async () => {
|
gulp.task("copy-translations-app", async () => {
|
||||||
const staticDir = paths.app_output_static;
|
const staticDir = paths.app_output_static;
|
||||||
copyTranslations(staticDir);
|
copyTranslations(staticDir);
|
||||||
@@ -106,11 +94,6 @@ gulp.task("copy-translations-supervisor", async () => {
|
|||||||
copyTranslations(staticDir);
|
copyTranslations(staticDir);
|
||||||
});
|
});
|
||||||
|
|
||||||
gulp.task("copy-locale-data-supervisor", async () => {
|
|
||||||
const staticDir = paths.hassio_output_static;
|
|
||||||
copyLocaleData(staticDir);
|
|
||||||
});
|
|
||||||
|
|
||||||
gulp.task("copy-static-app", async () => {
|
gulp.task("copy-static-app", async () => {
|
||||||
const staticDir = paths.app_output_static;
|
const staticDir = paths.app_output_static;
|
||||||
// Basic static files
|
// Basic static files
|
||||||
@@ -120,7 +103,6 @@ gulp.task("copy-static-app", async () => {
|
|||||||
copyPolyfills(staticDir);
|
copyPolyfills(staticDir);
|
||||||
copyFonts(staticDir);
|
copyFonts(staticDir);
|
||||||
copyTranslations(staticDir);
|
copyTranslations(staticDir);
|
||||||
copyLocaleData(staticDir);
|
|
||||||
copyMdiIcons(staticDir);
|
copyMdiIcons(staticDir);
|
||||||
|
|
||||||
// Panel assets
|
// Panel assets
|
||||||
@@ -141,7 +123,6 @@ gulp.task("copy-static-demo", async () => {
|
|||||||
copyMapPanel(paths.demo_output_static);
|
copyMapPanel(paths.demo_output_static);
|
||||||
copyFonts(paths.demo_output_static);
|
copyFonts(paths.demo_output_static);
|
||||||
copyTranslations(paths.demo_output_static);
|
copyTranslations(paths.demo_output_static);
|
||||||
copyLocaleData(paths.demo_output_static);
|
|
||||||
copyMdiIcons(paths.demo_output_static);
|
copyMdiIcons(paths.demo_output_static);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -156,7 +137,6 @@ gulp.task("copy-static-cast", async () => {
|
|||||||
copyMapPanel(paths.cast_output_static);
|
copyMapPanel(paths.cast_output_static);
|
||||||
copyFonts(paths.cast_output_static);
|
copyFonts(paths.cast_output_static);
|
||||||
copyTranslations(paths.cast_output_static);
|
copyTranslations(paths.cast_output_static);
|
||||||
copyLocaleData(paths.cast_output_static);
|
|
||||||
copyMdiIcons(paths.cast_output_static);
|
copyMdiIcons(paths.cast_output_static);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -172,6 +152,5 @@ gulp.task("copy-static-gallery", async () => {
|
|||||||
copyMapPanel(paths.gallery_output_static);
|
copyMapPanel(paths.gallery_output_static);
|
||||||
copyFonts(paths.gallery_output_static);
|
copyFonts(paths.gallery_output_static);
|
||||||
copyTranslations(paths.gallery_output_static);
|
copyTranslations(paths.gallery_output_static);
|
||||||
copyLocaleData(paths.gallery_output_static);
|
|
||||||
copyMdiIcons(paths.gallery_output_static);
|
copyMdiIcons(paths.gallery_output_static);
|
||||||
});
|
});
|
||||||
|
@@ -22,40 +22,17 @@ const getMeta = () => {
|
|||||||
const svg = fs.readFileSync(`${ICON_PATH}/${icon.name}.svg`, {
|
const svg = fs.readFileSync(`${ICON_PATH}/${icon.name}.svg`, {
|
||||||
encoding,
|
encoding,
|
||||||
});
|
});
|
||||||
return {
|
return { path: svg.match(/ d="([^"]+)"/)[1], name: icon.name };
|
||||||
path: svg.match(/ d="([^"]+)"/)[1],
|
|
||||||
name: icon.name,
|
|
||||||
tags: icon.tags,
|
|
||||||
aliases: icon.aliases,
|
|
||||||
};
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const addRemovedMeta = (meta) => {
|
const addRemovedMeta = (meta) => {
|
||||||
const file = fs.readFileSync(REMOVED_ICONS_PATH, { encoding });
|
const file = fs.readFileSync(REMOVED_ICONS_PATH, { encoding });
|
||||||
const removed = JSON.parse(file);
|
const removed = JSON.parse(file);
|
||||||
const removedMeta = removed.map((removeIcon) => ({
|
const combinedMeta = [...meta, ...removed];
|
||||||
path: removeIcon.path,
|
|
||||||
name: removeIcon.name,
|
|
||||||
tags: [],
|
|
||||||
aliases: [],
|
|
||||||
}));
|
|
||||||
const combinedMeta = [...meta, ...removedMeta];
|
|
||||||
return combinedMeta.sort((a, b) => a.name.localeCompare(b.name));
|
return combinedMeta.sort((a, b) => a.name.localeCompare(b.name));
|
||||||
};
|
};
|
||||||
|
|
||||||
const homeAutomationTag = "Home Automation";
|
|
||||||
|
|
||||||
const orderMeta = (meta) => {
|
|
||||||
const homeAutomationMeta = meta.filter((icon) =>
|
|
||||||
icon.tags.includes(homeAutomationTag)
|
|
||||||
);
|
|
||||||
const otherMeta = meta.filter(
|
|
||||||
(icon) => !icon.tags.includes(homeAutomationTag)
|
|
||||||
);
|
|
||||||
return [...homeAutomationMeta, ...otherMeta];
|
|
||||||
};
|
|
||||||
|
|
||||||
const splitBySize = (meta) => {
|
const splitBySize = (meta) => {
|
||||||
const chunks = [];
|
const chunks = [];
|
||||||
const CHUNK_SIZE = 50000;
|
const CHUNK_SIZE = 50000;
|
||||||
@@ -100,10 +77,8 @@ const findDifferentiator = (curString, prevString) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
gulp.task("gen-icons-json", (done) => {
|
gulp.task("gen-icons-json", (done) => {
|
||||||
const meta = getMeta();
|
const meta = addRemovedMeta(getMeta());
|
||||||
|
const split = splitBySize(meta);
|
||||||
const metaAndRemoved = addRemovedMeta(meta);
|
|
||||||
const split = splitBySize(metaAndRemoved);
|
|
||||||
|
|
||||||
if (!fs.existsSync(OUTPUT_DIR)) {
|
if (!fs.existsSync(OUTPUT_DIR)) {
|
||||||
fs.mkdirSync(OUTPUT_DIR, { recursive: true });
|
fs.mkdirSync(OUTPUT_DIR, { recursive: true });
|
||||||
@@ -141,18 +116,5 @@ gulp.task("gen-icons-json", (done) => {
|
|||||||
JSON.stringify({ version: package.version, parts })
|
JSON.stringify({ version: package.version, parts })
|
||||||
);
|
);
|
||||||
|
|
||||||
fs.writeFileSync(
|
|
||||||
path.resolve(OUTPUT_DIR, "iconList.json"),
|
|
||||||
JSON.stringify(
|
|
||||||
orderMeta(meta).map((icon) => ({
|
|
||||||
name: icon.name,
|
|
||||||
keywords: [
|
|
||||||
...icon.tags.map((t) => t.toLowerCase().replace(/\s\/\s/g, " ")),
|
|
||||||
...icon.aliases,
|
|
||||||
],
|
|
||||||
}))
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
@@ -1,6 +1,9 @@
|
|||||||
const gulp = require("gulp");
|
const gulp = require("gulp");
|
||||||
|
const fs = require("fs");
|
||||||
|
const path = require("path");
|
||||||
|
|
||||||
const env = require("../env");
|
const env = require("../env");
|
||||||
|
const paths = require("../paths");
|
||||||
|
|
||||||
require("./clean.js");
|
require("./clean.js");
|
||||||
require("./gen-icons-json.js");
|
require("./gen-icons-json.js");
|
||||||
@@ -17,11 +20,10 @@ gulp.task(
|
|||||||
process.env.NODE_ENV = "development";
|
process.env.NODE_ENV = "development";
|
||||||
},
|
},
|
||||||
"clean-hassio",
|
"clean-hassio",
|
||||||
|
"gen-icons-json",
|
||||||
"gen-index-hassio-dev",
|
"gen-index-hassio-dev",
|
||||||
"build-supervisor-translations",
|
"build-supervisor-translations",
|
||||||
"copy-translations-supervisor",
|
"copy-translations-supervisor",
|
||||||
"build-locale-data",
|
|
||||||
"copy-locale-data-supervisor",
|
|
||||||
env.useRollup() ? "rollup-watch-hassio" : "webpack-watch-hassio"
|
env.useRollup() ? "rollup-watch-hassio" : "webpack-watch-hassio"
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
@@ -33,10 +35,9 @@ gulp.task(
|
|||||||
process.env.NODE_ENV = "production";
|
process.env.NODE_ENV = "production";
|
||||||
},
|
},
|
||||||
"clean-hassio",
|
"clean-hassio",
|
||||||
|
"gen-icons-json",
|
||||||
"build-supervisor-translations",
|
"build-supervisor-translations",
|
||||||
"copy-translations-supervisor",
|
"copy-translations-supervisor",
|
||||||
"build-locale-data",
|
|
||||||
"copy-locale-data-supervisor",
|
|
||||||
env.useRollup() ? "rollup-prod-hassio" : "webpack-prod-hassio",
|
env.useRollup() ? "rollup-prod-hassio" : "webpack-prod-hassio",
|
||||||
"gen-index-hassio-prod",
|
"gen-index-hassio-prod",
|
||||||
...// Don't compress running tests
|
...// Don't compress running tests
|
||||||
|
@@ -1,74 +0,0 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
|
||||||
|
|
||||||
const del = require("del");
|
|
||||||
const path = require("path");
|
|
||||||
const gulp = require("gulp");
|
|
||||||
const fs = require("fs");
|
|
||||||
const paths = require("../paths");
|
|
||||||
|
|
||||||
const outDir = "build/locale-data";
|
|
||||||
|
|
||||||
gulp.task("clean-locale-data", () => del([outDir]));
|
|
||||||
|
|
||||||
gulp.task("ensure-locale-data-build-dir", (done) => {
|
|
||||||
if (!fs.existsSync(outDir)) {
|
|
||||||
fs.mkdirSync(outDir, { recursive: true });
|
|
||||||
}
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
|
|
||||||
const modules = {
|
|
||||||
"intl-relativetimeformat": "RelativeTimeFormat",
|
|
||||||
"intl-datetimeformat": "DateTimeFormat",
|
|
||||||
"intl-numberformat": "NumberFormat",
|
|
||||||
};
|
|
||||||
|
|
||||||
gulp.task("create-locale-data", (done) => {
|
|
||||||
const translationMeta = JSON.parse(
|
|
||||||
fs.readFileSync(
|
|
||||||
path.join(paths.translations_src, "translationMetadata.json")
|
|
||||||
)
|
|
||||||
);
|
|
||||||
Object.entries(modules).forEach(([module, className]) => {
|
|
||||||
Object.keys(translationMeta).forEach((lang) => {
|
|
||||||
try {
|
|
||||||
const localeData = String(
|
|
||||||
fs.readFileSync(
|
|
||||||
require.resolve(`@formatjs/${module}/locale-data/${lang}.js`)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.replace(
|
|
||||||
new RegExp(
|
|
||||||
`\\/\\*\\s*@generated\\s*\\*\\/\\s*\\/\\/\\s*prettier-ignore\\s*if\\s*\\(Intl\\.${className}\\s*&&\\s*typeof\\s*Intl\\.${className}\\.__addLocaleData\\s*===\\s*'function'\\)\\s*{\\s*Intl\\.${className}\\.__addLocaleData\\(`,
|
|
||||||
"im"
|
|
||||||
),
|
|
||||||
""
|
|
||||||
)
|
|
||||||
.replace(/\)\s*}/im, "");
|
|
||||||
// make sure we have valid JSON
|
|
||||||
JSON.parse(localeData);
|
|
||||||
if (!fs.existsSync(path.join(outDir, module))) {
|
|
||||||
fs.mkdirSync(path.join(outDir, module), { recursive: true });
|
|
||||||
}
|
|
||||||
fs.writeFileSync(
|
|
||||||
path.join(outDir, `${module}/${lang}.json`),
|
|
||||||
localeData
|
|
||||||
);
|
|
||||||
} catch (e) {
|
|
||||||
if (e.code !== "MODULE_NOT_FOUND") {
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
gulp.task(
|
|
||||||
"build-locale-data",
|
|
||||||
gulp.series(
|
|
||||||
"clean-locale-data",
|
|
||||||
"ensure-locale-data-build-dir",
|
|
||||||
"create-locale-data"
|
|
||||||
)
|
|
||||||
);
|
|
@@ -17,7 +17,7 @@ const paths = require("../paths");
|
|||||||
|
|
||||||
const inFrontendDir = "translations/frontend";
|
const inFrontendDir = "translations/frontend";
|
||||||
const inBackendDir = "translations/backend";
|
const inBackendDir = "translations/backend";
|
||||||
const workDir = "build/translations";
|
const workDir = "build-translations";
|
||||||
const fullDir = workDir + "/full";
|
const fullDir = workDir + "/full";
|
||||||
const coreDir = workDir + "/core";
|
const coreDir = workDir + "/core";
|
||||||
const outDir = workDir + "/output";
|
const outDir = workDir + "/output";
|
||||||
@@ -121,7 +121,7 @@ gulp.task("clean-translations", () => del([workDir]));
|
|||||||
|
|
||||||
gulp.task("ensure-translations-build-dir", (done) => {
|
gulp.task("ensure-translations-build-dir", (done) => {
|
||||||
if (!fs.existsSync(workDir)) {
|
if (!fs.existsSync(workDir)) {
|
||||||
fs.mkdirSync(workDir, { recursive: true });
|
fs.mkdirSync(workDir);
|
||||||
}
|
}
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
@@ -35,29 +35,26 @@ const isWsl =
|
|||||||
* listenHost?: string
|
* listenHost?: string
|
||||||
* }}
|
* }}
|
||||||
*/
|
*/
|
||||||
const runDevServer = async ({
|
const runDevServer = ({
|
||||||
compiler,
|
compiler,
|
||||||
contentBase,
|
contentBase,
|
||||||
port,
|
port,
|
||||||
listenHost = "localhost",
|
listenHost = "localhost",
|
||||||
}) => {
|
}) =>
|
||||||
const server = new WebpackDevServer(
|
new WebpackDevServer(compiler, {
|
||||||
{
|
open: true,
|
||||||
open: true,
|
watchContentBase: true,
|
||||||
host: listenHost,
|
contentBase,
|
||||||
port,
|
}).listen(port, listenHost, (err) => {
|
||||||
static: {
|
if (err) {
|
||||||
directory: contentBase,
|
throw err;
|
||||||
watch: true,
|
}
|
||||||
},
|
// Server listening
|
||||||
},
|
log(
|
||||||
compiler
|
"[webpack-dev-server]",
|
||||||
);
|
`Project is running at http://localhost:${port}`
|
||||||
|
);
|
||||||
await server.start();
|
});
|
||||||
// Server listening
|
|
||||||
log("[webpack-dev-server]", `Project is running at http://localhost:${port}`);
|
|
||||||
};
|
|
||||||
|
|
||||||
const doneHandler = (done) => (err, stats) => {
|
const doneHandler = (done) => (err, stats) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
@@ -110,13 +107,13 @@ gulp.task("webpack-prod-app", () =>
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
gulp.task("webpack-dev-server-demo", () =>
|
gulp.task("webpack-dev-server-demo", () => {
|
||||||
runDevServer({
|
runDevServer({
|
||||||
compiler: webpack(bothBuilds(createDemoConfig, { isProdBuild: false })),
|
compiler: webpack(bothBuilds(createDemoConfig, { isProdBuild: false })),
|
||||||
contentBase: paths.demo_output_root,
|
contentBase: paths.demo_output_root,
|
||||||
port: 8090,
|
port: 8090,
|
||||||
})
|
});
|
||||||
);
|
});
|
||||||
|
|
||||||
gulp.task("webpack-prod-demo", () =>
|
gulp.task("webpack-prod-demo", () =>
|
||||||
prodBuild(
|
prodBuild(
|
||||||
@@ -126,15 +123,15 @@ gulp.task("webpack-prod-demo", () =>
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
gulp.task("webpack-dev-server-cast", () =>
|
gulp.task("webpack-dev-server-cast", () => {
|
||||||
runDevServer({
|
runDevServer({
|
||||||
compiler: webpack(bothBuilds(createCastConfig, { isProdBuild: false })),
|
compiler: webpack(bothBuilds(createCastConfig, { isProdBuild: false })),
|
||||||
contentBase: paths.cast_output_root,
|
contentBase: paths.cast_output_root,
|
||||||
port: 8080,
|
port: 8080,
|
||||||
// Accessible from the network, because that's how Cast hits it.
|
// Accessible from the network, because that's how Cast hits it.
|
||||||
listenHost: "0.0.0.0",
|
listenHost: "0.0.0.0",
|
||||||
})
|
});
|
||||||
);
|
});
|
||||||
|
|
||||||
gulp.task("webpack-prod-cast", () =>
|
gulp.task("webpack-prod-cast", () =>
|
||||||
prodBuild(
|
prodBuild(
|
||||||
@@ -151,7 +148,7 @@ gulp.task("webpack-watch-hassio", () => {
|
|||||||
isProdBuild: false,
|
isProdBuild: false,
|
||||||
latestBuild: true,
|
latestBuild: true,
|
||||||
})
|
})
|
||||||
).watch({ ignored: /build/, poll: isWsl }, doneHandler());
|
).watch({ ignored: /build-translations/, poll: isWsl }, doneHandler());
|
||||||
|
|
||||||
gulp.watch(
|
gulp.watch(
|
||||||
path.join(paths.translations_src, "en.json"),
|
path.join(paths.translations_src, "en.json"),
|
||||||
@@ -167,15 +164,14 @@ gulp.task("webpack-prod-hassio", () =>
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
gulp.task("webpack-dev-server-gallery", () =>
|
gulp.task("webpack-dev-server-gallery", () => {
|
||||||
runDevServer({
|
runDevServer({
|
||||||
// We don't use the es5 build, but the dev server will fuck up the publicPath if we don't
|
// We don't use the es5 build, but the dev server will fuck up the publicPath if we don't
|
||||||
compiler: webpack(bothBuilds(createGalleryConfig, { isProdBuild: false })),
|
compiler: webpack(bothBuilds(createGalleryConfig, { isProdBuild: false })),
|
||||||
contentBase: paths.gallery_output_root,
|
contentBase: paths.gallery_output_root,
|
||||||
port: 8100,
|
port: 8100,
|
||||||
listenHost: "0.0.0.0",
|
});
|
||||||
})
|
});
|
||||||
);
|
|
||||||
|
|
||||||
gulp.task("webpack-prod-gallery", () =>
|
gulp.task("webpack-prod-gallery", () =>
|
||||||
prodBuild(
|
prodBuild(
|
||||||
|
File diff suppressed because one or more lines are too long
@@ -1,46 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<script src="//www.gstatic.com/cast/sdk/libs/caf_receiver/v3/cast_receiver_framework.js"></script>
|
|
||||||
<style>
|
|
||||||
body {
|
|
||||||
--logo-image: url('https://www.home-assistant.io/images/home-assistant-logo.svg');
|
|
||||||
--logo-repeat: no-repeat;
|
|
||||||
--playback-logo-image: url('https://www.home-assistant.io/images/home-assistant-logo.svg');
|
|
||||||
--theme-hue: 200;
|
|
||||||
--progress-color: #03a9f4;
|
|
||||||
--splash-image: url('https://home-assistant.io/images/cast/splash.png');
|
|
||||||
--splash-size: cover;
|
|
||||||
--background-color: #41bdf5;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<script>
|
|
||||||
var _gaq=[['_setAccount','UA-57927901-10'],['_trackPageview']];
|
|
||||||
(function(d,t){var g=d.createElement(t),s=d.getElementsByTagName(t)[0];
|
|
||||||
g.src=('https:'==location.protocol?'//ssl':'//www')+'.google-analytics.com/ga.js';
|
|
||||||
s.parentNode.insertBefore(g,s)}(document,'script'));
|
|
||||||
</script>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<%= renderTemplate('_js_base') %>
|
|
||||||
|
|
||||||
<cast-media-player></cast-media-player>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import("<%= latestMediaJS %>");
|
|
||||||
window.latestJS = true;
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
if (!window.latestJS) {
|
|
||||||
<% if (useRollup) { %>
|
|
||||||
_ls("/static/js/s.min.js").onload = function() {
|
|
||||||
System.import("<%= es5MediaJS %>");
|
|
||||||
};
|
|
||||||
<% } else { %>
|
|
||||||
_ls("<%= es5MediaJS %>");
|
|
||||||
<% } %>
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@@ -1,5 +1,4 @@
|
|||||||
import "@material/mwc-button/mwc-button";
|
import "@material/mwc-button/mwc-button";
|
||||||
import { mdiCast, mdiCastConnected } from "@mdi/js";
|
|
||||||
import "@polymer/paper-item/paper-icon-item";
|
import "@polymer/paper-item/paper-icon-item";
|
||||||
import "@polymer/paper-listbox/paper-listbox";
|
import "@polymer/paper-listbox/paper-listbox";
|
||||||
import { Auth, Connection } from "home-assistant-js-websocket";
|
import { Auth, Connection } from "home-assistant-js-websocket";
|
||||||
@@ -18,7 +17,6 @@ import {
|
|||||||
import { atLeastVersion } from "../../../../src/common/config/version";
|
import { atLeastVersion } from "../../../../src/common/config/version";
|
||||||
import { toggleAttribute } from "../../../../src/common/dom/toggle_attribute";
|
import { toggleAttribute } from "../../../../src/common/dom/toggle_attribute";
|
||||||
import "../../../../src/components/ha-icon";
|
import "../../../../src/components/ha-icon";
|
||||||
import "../../../../src/components/ha-svg-icon";
|
|
||||||
import {
|
import {
|
||||||
getLegacyLovelaceCollection,
|
getLegacyLovelaceCollection,
|
||||||
getLovelaceCollection,
|
getLovelaceCollection,
|
||||||
@@ -75,7 +73,7 @@ class HcCast extends LitElement {
|
|||||||
? html`
|
? html`
|
||||||
<p class="center-item">
|
<p class="center-item">
|
||||||
<mwc-button raised @click=${this._handleLaunch}>
|
<mwc-button raised @click=${this._handleLaunch}>
|
||||||
<ha-svg-icon .path=${mdiCast}></ha-svg-icon>
|
<ha-icon icon="hass:cast"></ha-icon>
|
||||||
Start Casting
|
Start Casting
|
||||||
</mwc-button>
|
</mwc-button>
|
||||||
</p>
|
</p>
|
||||||
@@ -113,7 +111,7 @@ class HcCast extends LitElement {
|
|||||||
${this.castManager.status
|
${this.castManager.status
|
||||||
? html`
|
? html`
|
||||||
<mwc-button @click=${this._handleLaunch}>
|
<mwc-button @click=${this._handleLaunch}>
|
||||||
<ha-svg-icon .path=${mdiCastConnected}></ha-svg-icon>
|
<ha-icon icon="hass:cast-connected"></ha-icon>
|
||||||
Manage
|
Manage
|
||||||
</mwc-button>
|
</mwc-button>
|
||||||
`
|
`
|
||||||
@@ -235,7 +233,7 @@ class HcCast extends LitElement {
|
|||||||
color: var(--secondary-text-color);
|
color: var(--secondary-text-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
mwc-button ha-svg-icon {
|
mwc-button ha-icon {
|
||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
height: 18px;
|
height: 18px;
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,4 @@
|
|||||||
import "@material/mwc-button";
|
import "@material/mwc-button";
|
||||||
import { mdiCastConnected, mdiCast } from "@mdi/js";
|
|
||||||
import "@polymer/paper-input/paper-input";
|
import "@polymer/paper-input/paper-input";
|
||||||
import {
|
import {
|
||||||
Auth,
|
Auth,
|
||||||
@@ -20,7 +19,7 @@ import {
|
|||||||
loadTokens,
|
loadTokens,
|
||||||
saveTokens,
|
saveTokens,
|
||||||
} from "../../../../src/common/auth/token_storage";
|
} from "../../../../src/common/auth/token_storage";
|
||||||
import "../../../../src/components/ha-svg-icon";
|
import "../../../../src/components/ha-icon";
|
||||||
import "../../../../src/layouts/hass-loading-screen";
|
import "../../../../src/layouts/hass-loading-screen";
|
||||||
import { registerServiceWorker } from "../../../../src/util/register-service-worker";
|
import { registerServiceWorker } from "../../../../src/util/register-service-worker";
|
||||||
import "./hc-layout";
|
import "./hc-layout";
|
||||||
@@ -128,11 +127,11 @@ export class HcConnect extends LitElement {
|
|||||||
<div class="card-actions">
|
<div class="card-actions">
|
||||||
<mwc-button @click=${this._handleDemo}>
|
<mwc-button @click=${this._handleDemo}>
|
||||||
Show Demo
|
Show Demo
|
||||||
<ha-svg-icon
|
<ha-icon
|
||||||
.path=${this.castManager.castState === "CONNECTED"
|
.icon=${this.castManager.castState === "CONNECTED"
|
||||||
? mdiCastConnected
|
? "hass:cast-connected"
|
||||||
: mdiCast}
|
: "hass:cast"}
|
||||||
></ha-svg-icon>
|
></ha-icon>
|
||||||
</mwc-button>
|
</mwc-button>
|
||||||
<div class="spacer"></div>
|
<div class="spacer"></div>
|
||||||
<mwc-button @click=${this._handleConnect}>Authorize</mwc-button>
|
<mwc-button @click=${this._handleConnect}>Authorize</mwc-button>
|
||||||
@@ -308,7 +307,7 @@ export class HcConnect extends LitElement {
|
|||||||
color: darkred;
|
color: darkred;
|
||||||
}
|
}
|
||||||
|
|
||||||
mwc-button ha-svg-icon {
|
mwc-button ha-icon {
|
||||||
margin-left: 8px;
|
margin-left: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,22 +0,0 @@
|
|||||||
const castContext = cast.framework.CastReceiverContext.getInstance();
|
|
||||||
|
|
||||||
const playerManager = castContext.getPlayerManager();
|
|
||||||
|
|
||||||
playerManager.setMessageInterceptor(
|
|
||||||
cast.framework.messages.MessageType.LOAD,
|
|
||||||
(loadRequestData) => {
|
|
||||||
const media = loadRequestData.media;
|
|
||||||
// Special handling if it came from Google Assistant
|
|
||||||
if (media.entity) {
|
|
||||||
media.contentId = media.entity;
|
|
||||||
media.streamType = cast.framework.messages.StreamType.LIVE;
|
|
||||||
media.contentType = "application/vnd.apple.mpegurl";
|
|
||||||
// @ts-ignore
|
|
||||||
media.hlsVideoSegmentFormat =
|
|
||||||
cast.framework.messages.HlsVideoSegmentFormat.FMP4;
|
|
||||||
}
|
|
||||||
return loadRequestData;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
castContext.start();
|
|
@@ -8,9 +8,6 @@ import { ReceivedMessage } from "./types";
|
|||||||
|
|
||||||
const lovelaceController = new HcMain();
|
const lovelaceController = new HcMain();
|
||||||
document.body.append(lovelaceController);
|
document.body.append(lovelaceController);
|
||||||
lovelaceController.addEventListener("cast-view-changed", (ev) => {
|
|
||||||
playDummyMedia(ev.detail.title);
|
|
||||||
});
|
|
||||||
|
|
||||||
const mediaPlayer = document.createElement("cast-media-player");
|
const mediaPlayer = document.createElement("cast-media-player");
|
||||||
mediaPlayer.style.display = "none";
|
mediaPlayer.style.display = "none";
|
||||||
@@ -31,31 +28,6 @@ const setTouchControlsVisibility = (visible: boolean) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let timeOut: number | undefined;
|
|
||||||
|
|
||||||
const playDummyMedia = (viewTitle?: string) => {
|
|
||||||
const loadRequestData = new cast.framework.messages.LoadRequestData();
|
|
||||||
loadRequestData.autoplay = true;
|
|
||||||
loadRequestData.media = new cast.framework.messages.MediaInformation();
|
|
||||||
loadRequestData.media.contentId =
|
|
||||||
"https://cast.home-assistant.io/images/google-nest-hub.png";
|
|
||||||
loadRequestData.media.contentType = "image/jpeg";
|
|
||||||
loadRequestData.media.streamType = cast.framework.messages.StreamType.NONE;
|
|
||||||
const metadata = new cast.framework.messages.GenericMediaMetadata();
|
|
||||||
metadata.title = viewTitle;
|
|
||||||
loadRequestData.media.metadata = metadata;
|
|
||||||
|
|
||||||
loadRequestData.requestId = 0;
|
|
||||||
playerManager.load(loadRequestData);
|
|
||||||
if (timeOut) {
|
|
||||||
clearTimeout(timeOut);
|
|
||||||
timeOut = undefined;
|
|
||||||
}
|
|
||||||
if (castContext.getDeviceCapabilities().touch_input_supported) {
|
|
||||||
timeOut = window.setTimeout(() => playDummyMedia(viewTitle), 540000); // repeat every 9 minutes to keep it active (gets deactivated after 10 minutes)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const showLovelaceController = () => {
|
const showLovelaceController = () => {
|
||||||
mediaPlayer.style.display = "none";
|
mediaPlayer.style.display = "none";
|
||||||
lovelaceController.style.display = "initial";
|
lovelaceController.style.display = "initial";
|
||||||
@@ -79,7 +51,6 @@ const showMediaPlayer = () => {
|
|||||||
--progress-color: #03a9f4;
|
--progress-color: #03a9f4;
|
||||||
--splash-image: url('https://home-assistant.io/images/cast/splash.png');
|
--splash-image: url('https://home-assistant.io/images/cast/splash.png');
|
||||||
--splash-size: cover;
|
--splash-size: cover;
|
||||||
--background-color: #41bdf5;
|
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
document.head.appendChild(style);
|
document.head.appendChild(style);
|
||||||
@@ -92,6 +63,22 @@ options.customNamespaces = {
|
|||||||
[CAST_NS]: cast.framework.system.MessageType.JSON,
|
[CAST_NS]: cast.framework.system.MessageType.JSON,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// The docs say we need to set options.touchScreenOptimizeApp = true
|
||||||
|
// https://developers.google.com/cast/docs/caf_receiver/customize_ui#accessing_ui_controls
|
||||||
|
// This doesn't work.
|
||||||
|
// @ts-ignore
|
||||||
|
options.touchScreenOptimizedApp = true;
|
||||||
|
|
||||||
|
// The class reference say we can set a uiConfig in options to set it
|
||||||
|
// https://developers.google.com/cast/docs/reference/caf_receiver/cast.framework.CastReceiverOptions#uiConfig
|
||||||
|
// This doesn't work either.
|
||||||
|
// @ts-ignore
|
||||||
|
options.uiConfig = new cast.framework.ui.UiConfig();
|
||||||
|
// @ts-ignore
|
||||||
|
options.uiConfig.touchScreenOptimizedApp = true;
|
||||||
|
|
||||||
|
castContext.setInactivityTimeout(86400); // 1 day
|
||||||
|
|
||||||
castContext.addCustomMessageListener(
|
castContext.addCustomMessageListener(
|
||||||
CAST_NS,
|
CAST_NS,
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
@@ -116,12 +103,6 @@ const playerManager = castContext.getPlayerManager();
|
|||||||
playerManager.setMessageInterceptor(
|
playerManager.setMessageInterceptor(
|
||||||
cast.framework.messages.MessageType.LOAD,
|
cast.framework.messages.MessageType.LOAD,
|
||||||
(loadRequestData) => {
|
(loadRequestData) => {
|
||||||
if (
|
|
||||||
loadRequestData.media.contentId ===
|
|
||||||
"https://cast.home-assistant.io/images/google-nest-hub.png"
|
|
||||||
) {
|
|
||||||
return loadRequestData;
|
|
||||||
}
|
|
||||||
// We received a play media command, hide Lovelace and show media player
|
// We received a play media command, hide Lovelace and show media player
|
||||||
showMediaPlayer();
|
showMediaPlayer();
|
||||||
const media = loadRequestData.media;
|
const media = loadRequestData.media;
|
||||||
|
@@ -1,6 +1,5 @@
|
|||||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||||
import { customElement, property } from "lit/decorators";
|
import { customElement, property } from "lit/decorators";
|
||||||
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
|
||||||
import { LovelaceConfig } from "../../../../src/data/lovelace";
|
import { LovelaceConfig } from "../../../../src/data/lovelace";
|
||||||
import { Lovelace } from "../../../../src/panels/lovelace/types";
|
import { Lovelace } from "../../../../src/panels/lovelace/types";
|
||||||
import "../../../../src/panels/lovelace/views/hui-view";
|
import "../../../../src/panels/lovelace/views/hui-view";
|
||||||
@@ -15,7 +14,7 @@ class HcLovelace extends LitElement {
|
|||||||
|
|
||||||
@property() public viewPath?: string | number;
|
@property() public viewPath?: string | number;
|
||||||
|
|
||||||
@property() public urlPath: string | null = null;
|
public urlPath?: string | null;
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
const index = this._viewIndex;
|
const index = this._viewIndex;
|
||||||
@@ -31,7 +30,7 @@ class HcLovelace extends LitElement {
|
|||||||
config: this.lovelaceConfig,
|
config: this.lovelaceConfig,
|
||||||
rawConfig: this.lovelaceConfig,
|
rawConfig: this.lovelaceConfig,
|
||||||
editMode: false,
|
editMode: false,
|
||||||
urlPath: this.urlPath,
|
urlPath: this.urlPath!,
|
||||||
enableFullEditMode: () => undefined,
|
enableFullEditMode: () => undefined,
|
||||||
mode: "storage",
|
mode: "storage",
|
||||||
locale: this.hass.locale,
|
locale: this.hass.locale,
|
||||||
@@ -55,21 +54,6 @@ class HcLovelace extends LitElement {
|
|||||||
const index = this._viewIndex;
|
const index = this._viewIndex;
|
||||||
|
|
||||||
if (index !== undefined) {
|
if (index !== undefined) {
|
||||||
const dashboardTitle = this.lovelaceConfig.title || this.urlPath;
|
|
||||||
|
|
||||||
const viewTitle =
|
|
||||||
this.lovelaceConfig.views[index].title ||
|
|
||||||
this.lovelaceConfig.views[index].path;
|
|
||||||
|
|
||||||
fireEvent(this, "cast-view-changed", {
|
|
||||||
title:
|
|
||||||
dashboardTitle || viewTitle
|
|
||||||
? `${dashboardTitle || ""}${
|
|
||||||
dashboardTitle && viewTitle ? ": " : ""
|
|
||||||
}${viewTitle || ""}`
|
|
||||||
: undefined,
|
|
||||||
});
|
|
||||||
|
|
||||||
const configBackground =
|
const configBackground =
|
||||||
this.lovelaceConfig.views[index].background ||
|
this.lovelaceConfig.views[index].background ||
|
||||||
this.lovelaceConfig.background;
|
this.lovelaceConfig.background;
|
||||||
@@ -117,15 +101,8 @@ class HcLovelace extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CastViewChanged {
|
|
||||||
title: string | undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HTMLElementTagNameMap {
|
interface HTMLElementTagNameMap {
|
||||||
"hc-lovelace": HcLovelace;
|
"hc-lovelace": HcLovelace;
|
||||||
}
|
}
|
||||||
interface HASSDomEvents {
|
|
||||||
"cast-view-changed": CastViewChanged;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -13,11 +13,7 @@ import {
|
|||||||
ShowDemoMessage,
|
ShowDemoMessage,
|
||||||
ShowLovelaceViewMessage,
|
ShowLovelaceViewMessage,
|
||||||
} from "../../../../src/cast/receiver_messages";
|
} from "../../../../src/cast/receiver_messages";
|
||||||
import {
|
import { ReceiverStatusMessage } from "../../../../src/cast/sender_messages";
|
||||||
ReceiverErrorCode,
|
|
||||||
ReceiverErrorMessage,
|
|
||||||
ReceiverStatusMessage,
|
|
||||||
} from "../../../../src/cast/sender_messages";
|
|
||||||
import { atLeastVersion } from "../../../../src/common/config/version";
|
import { atLeastVersion } from "../../../../src/common/config/version";
|
||||||
import { isNavigationClick } from "../../../../src/common/dom/is-navigation-click";
|
import { isNavigationClick } from "../../../../src/common/dom/is-navigation-click";
|
||||||
import {
|
import {
|
||||||
@@ -44,10 +40,10 @@ export class HcMain extends HassElement {
|
|||||||
|
|
||||||
@state() private _error?: string;
|
@state() private _error?: string;
|
||||||
|
|
||||||
@state() private _urlPath?: string | null;
|
|
||||||
|
|
||||||
private _unsubLovelace?: UnsubscribeFunc;
|
private _unsubLovelace?: UnsubscribeFunc;
|
||||||
|
|
||||||
|
private _urlPath?: string | null;
|
||||||
|
|
||||||
public processIncomingMessage(msg: HassMessage) {
|
public processIncomingMessage(msg: HassMessage) {
|
||||||
if (msg.type === "connect") {
|
if (msg.type === "connect") {
|
||||||
this._handleConnectMessage(msg);
|
this._handleConnectMessage(msg);
|
||||||
@@ -72,10 +68,8 @@ export class HcMain extends HassElement {
|
|||||||
!this._lovelaceConfig ||
|
!this._lovelaceConfig ||
|
||||||
this._lovelacePath === null ||
|
this._lovelacePath === null ||
|
||||||
// Guard against part of HA not being loaded yet.
|
// Guard against part of HA not being loaded yet.
|
||||||
!this.hass ||
|
(this.hass &&
|
||||||
!this.hass.states ||
|
(!this.hass.states || !this.hass.config || !this.hass.services))
|
||||||
!this.hass.config ||
|
|
||||||
!this.hass.services
|
|
||||||
) {
|
) {
|
||||||
return html`
|
return html`
|
||||||
<hc-launch-screen
|
<hc-launch-screen
|
||||||
@@ -113,7 +107,6 @@ export class HcMain extends HassElement {
|
|||||||
this._sendStatus();
|
this._sendStatus();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.addEventListener("dialog-closed", this._dialogClosed);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private _sendStatus(senderId?: string) {
|
private _sendStatus(senderId?: string) {
|
||||||
@@ -125,7 +118,7 @@ export class HcMain extends HassElement {
|
|||||||
|
|
||||||
if (this.hass) {
|
if (this.hass) {
|
||||||
status.hassUrl = this.hass.auth.data.hassUrl;
|
status.hassUrl = this.hass.auth.data.hassUrl;
|
||||||
status.lovelacePath = this._lovelacePath;
|
status.lovelacePath = this._lovelacePath!;
|
||||||
status.urlPath = this._urlPath;
|
status.urlPath = this._urlPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,30 +131,6 @@ export class HcMain extends HassElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _sendError(
|
|
||||||
error_code: number,
|
|
||||||
error_message: string,
|
|
||||||
senderId?: string
|
|
||||||
) {
|
|
||||||
const error: ReceiverErrorMessage = {
|
|
||||||
type: "receiver_error",
|
|
||||||
error_code,
|
|
||||||
error_message,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (senderId) {
|
|
||||||
this.sendMessage(senderId, error);
|
|
||||||
} else {
|
|
||||||
for (const sender of castContext.getSenders()) {
|
|
||||||
this.sendMessage(sender.id, error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private _dialogClosed = () => {
|
|
||||||
document.body.setAttribute("style", "overflow-y: auto !important");
|
|
||||||
};
|
|
||||||
|
|
||||||
private async _handleGetStatusMessage(msg: GetStatusMessage) {
|
private async _handleGetStatusMessage(msg: GetStatusMessage) {
|
||||||
this._sendStatus(msg.senderId!);
|
this._sendStatus(msg.senderId!);
|
||||||
}
|
}
|
||||||
@@ -180,18 +149,14 @@ export class HcMain extends HassElement {
|
|||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
const errorMessage = this._getErrorMessage(err);
|
this._error = this._getErrorMessage(err);
|
||||||
this._error = errorMessage;
|
|
||||||
this._sendError(err, errorMessage);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let connection;
|
let connection;
|
||||||
try {
|
try {
|
||||||
connection = await createConnection({ auth });
|
connection = await createConnection({ auth });
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
const errorMessage = this._getErrorMessage(err);
|
this._error = this._getErrorMessage(err);
|
||||||
this._error = errorMessage;
|
|
||||||
this._sendError(err, errorMessage);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (this.hass) {
|
if (this.hass) {
|
||||||
@@ -203,29 +168,24 @@ export class HcMain extends HassElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async _handleShowLovelaceMessage(msg: ShowLovelaceViewMessage) {
|
private async _handleShowLovelaceMessage(msg: ShowLovelaceViewMessage) {
|
||||||
this._showDemo = false;
|
|
||||||
// We should not get this command before we are connected.
|
// We should not get this command before we are connected.
|
||||||
// Means a client got out of sync. Let's send status to them.
|
// Means a client got out of sync. Let's send status to them.
|
||||||
if (!this.hass) {
|
if (!this.hass) {
|
||||||
this._sendStatus(msg.senderId!);
|
this._sendStatus(msg.senderId!);
|
||||||
this._error = "Cannot show Lovelace because we're not connected.";
|
this._error = "Cannot show Lovelace because we're not connected.";
|
||||||
this._sendError(ReceiverErrorCode.NOT_CONNECTED, this._error);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this._error = undefined;
|
|
||||||
if (msg.urlPath === "lovelace") {
|
if (msg.urlPath === "lovelace") {
|
||||||
msg.urlPath = null;
|
msg.urlPath = null;
|
||||||
}
|
}
|
||||||
this._lovelacePath = msg.viewPath;
|
|
||||||
if (!this._unsubLovelace || this._urlPath !== msg.urlPath) {
|
if (!this._unsubLovelace || this._urlPath !== msg.urlPath) {
|
||||||
this._urlPath = msg.urlPath;
|
this._urlPath = msg.urlPath;
|
||||||
this._lovelaceConfig = undefined;
|
|
||||||
if (this._unsubLovelace) {
|
if (this._unsubLovelace) {
|
||||||
this._unsubLovelace();
|
this._unsubLovelace();
|
||||||
}
|
}
|
||||||
const llColl = atLeastVersion(this.hass.connection.haVersion, 0, 107)
|
const llColl = atLeastVersion(this.hass.connection.haVersion, 0, 107)
|
||||||
? getLovelaceCollection(this.hass.connection, msg.urlPath)
|
? getLovelaceCollection(this.hass!.connection, msg.urlPath)
|
||||||
: getLegacyLovelaceCollection(this.hass.connection);
|
: getLegacyLovelaceCollection(this.hass!.connection);
|
||||||
// We first do a single refresh because we need to check if there is LL
|
// We first do a single refresh because we need to check if there is LL
|
||||||
// configuration.
|
// configuration.
|
||||||
try {
|
try {
|
||||||
@@ -234,16 +194,8 @@ export class HcMain extends HassElement {
|
|||||||
this._handleNewLovelaceConfig(lovelaceConfig)
|
this._handleNewLovelaceConfig(lovelaceConfig)
|
||||||
);
|
);
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
if (
|
// eslint-disable-next-line
|
||||||
atLeastVersion(this.hass.connection.haVersion, 0, 107) &&
|
console.log("Error fetching Lovelace configuration", err, msg);
|
||||||
err.code !== "config_not_found"
|
|
||||||
) {
|
|
||||||
// eslint-disable-next-line
|
|
||||||
console.log("Error fetching Lovelace configuration", err, msg);
|
|
||||||
this._error = `Error fetching Lovelace configuration: ${err.message}`;
|
|
||||||
this._sendError(ReceiverErrorCode.FETCH_CONFIG_FAILED, this._error);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Generate a Lovelace config.
|
// Generate a Lovelace config.
|
||||||
this._unsubLovelace = () => undefined;
|
this._unsubLovelace = () => undefined;
|
||||||
await this._generateLovelaceConfig();
|
await this._generateLovelaceConfig();
|
||||||
@@ -258,6 +210,8 @@ export class HcMain extends HassElement {
|
|||||||
loadLovelaceResources(resources, this.hass!.auth.data.hassUrl);
|
loadLovelaceResources(resources, this.hass!.auth.data.hassUrl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
this._showDemo = false;
|
||||||
|
this._lovelacePath = msg.viewPath;
|
||||||
|
|
||||||
this._sendStatus();
|
this._sendStatus();
|
||||||
}
|
}
|
||||||
@@ -278,7 +232,7 @@ export class HcMain extends HassElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _handleNewLovelaceConfig(lovelaceConfig: LovelaceConfig) {
|
private _handleNewLovelaceConfig(lovelaceConfig: LovelaceConfig) {
|
||||||
castContext.setApplicationState(lovelaceConfig.title || "");
|
castContext.setApplicationState(lovelaceConfig.title!);
|
||||||
this._lovelaceConfig = lovelaceConfig;
|
this._lovelaceConfig = lovelaceConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,4 +1,3 @@
|
|||||||
import { mdiTelevision } from "@mdi/js";
|
|
||||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||||
import { customElement, state } from "lit/decorators";
|
import { customElement, state } from "lit/decorators";
|
||||||
import { CastManager } from "../../../src/cast/cast_manager";
|
import { CastManager } from "../../../src/cast/cast_manager";
|
||||||
@@ -28,7 +27,7 @@ class CastDemoRow extends LitElement implements LovelaceRow {
|
|||||||
return html``;
|
return html``;
|
||||||
}
|
}
|
||||||
return html`
|
return html`
|
||||||
<ha-svg-icon .path=${mdiTelevision}></ha-svg-icon>
|
<ha-icon icon="hademo:television"></ha-icon>
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
<div class="name">Show Chromecast interface</div>
|
<div class="name">Show Chromecast interface</div>
|
||||||
<google-cast-launcher></google-cast-launcher>
|
<google-cast-launcher></google-cast-launcher>
|
||||||
@@ -73,7 +72,7 @@ class CastDemoRow extends LitElement implements LovelaceRow {
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
ha-svg-icon {
|
ha-icon {
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
color: var(--paper-item-icon-color);
|
color: var(--paper-item-icon-color);
|
||||||
}
|
}
|
||||||
|
File diff suppressed because one or more lines are too long
@@ -1,7 +0,0 @@
|
|||||||
import { AreaRegistryEntry } from "../../../src/data/area_registry";
|
|
||||||
import type { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
|
|
||||||
|
|
||||||
export const mockAreaRegistry = (
|
|
||||||
hass: MockHomeAssistant,
|
|
||||||
data: AreaRegistryEntry[] = []
|
|
||||||
) => hass.mockWS("config/area_registry/list", () => data);
|
|
@@ -1,7 +0,0 @@
|
|||||||
import { DeviceRegistryEntry } from "../../../src/data/device_registry";
|
|
||||||
import type { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
|
|
||||||
|
|
||||||
export const mockDeviceRegistry = (
|
|
||||||
hass: MockHomeAssistant,
|
|
||||||
data: DeviceRegistryEntry[] = []
|
|
||||||
) => hass.mockWS("config/device_registry/list", () => data);
|
|
@@ -1,7 +0,0 @@
|
|||||||
import { EntityRegistryEntry } from "../../../src/data/entity_registry";
|
|
||||||
import type { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
|
|
||||||
|
|
||||||
export const mockEntityRegistry = (
|
|
||||||
hass: MockHomeAssistant,
|
|
||||||
data: EntityRegistryEntry[] = []
|
|
||||||
) => hass.mockWS("config/entity_registry/list", () => data);
|
|
@@ -1,59 +0,0 @@
|
|||||||
import { HassioSupervisorInfo } from "../../../src/data/hassio/supervisor";
|
|
||||||
import type { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
|
|
||||||
|
|
||||||
export const mockHassioSupervisor = (hass: MockHomeAssistant) => {
|
|
||||||
hass.config.components.push("hassio");
|
|
||||||
hass.mockWS("supervisor/api", (msg) => {
|
|
||||||
if (msg.endpoint === "/supervisor/info") {
|
|
||||||
const data: HassioSupervisorInfo = {
|
|
||||||
version: "2021.10.dev0805",
|
|
||||||
version_latest: "2021.10.dev0806",
|
|
||||||
update_available: true,
|
|
||||||
channel: "dev",
|
|
||||||
arch: "aarch64",
|
|
||||||
supported: true,
|
|
||||||
healthy: true,
|
|
||||||
ip_address: "172.30.32.2",
|
|
||||||
wait_boot: 5,
|
|
||||||
timezone: "America/Los_Angeles",
|
|
||||||
logging: "info",
|
|
||||||
debug: false,
|
|
||||||
debug_block: false,
|
|
||||||
diagnostics: true,
|
|
||||||
addons: [
|
|
||||||
{
|
|
||||||
name: "Visual Studio Code",
|
|
||||||
slug: "a0d7b954_vscode",
|
|
||||||
description:
|
|
||||||
"Fully featured VSCode experience, to edit your HA config in the browser, including auto-completion!",
|
|
||||||
state: "started",
|
|
||||||
version: "3.6.2",
|
|
||||||
version_latest: "3.6.2",
|
|
||||||
update_available: false,
|
|
||||||
repository: "a0d7b954",
|
|
||||||
icon: true,
|
|
||||||
logo: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Z-Wave JS",
|
|
||||||
slug: "core_zwave_js",
|
|
||||||
description:
|
|
||||||
"Control a ZWave network with Home Assistant Z-Wave JS",
|
|
||||||
state: "started",
|
|
||||||
version: "0.1.45",
|
|
||||||
version_latest: "0.1.45",
|
|
||||||
update_available: false,
|
|
||||||
repository: "core",
|
|
||||||
icon: true,
|
|
||||||
logo: true,
|
|
||||||
},
|
|
||||||
] as any,
|
|
||||||
addons_repositories: [
|
|
||||||
"https://github.com/hassio-addons/repository",
|
|
||||||
] as any,
|
|
||||||
};
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
return Promise.reject(`${msg.method} ${msg.endpoint} is not implemented`);
|
|
||||||
});
|
|
||||||
};
|
|
Binary file not shown.
Before Width: | Height: | Size: 147 KiB |
@@ -1,35 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
TARGET_LABEL="Needs gallery preview"
|
|
||||||
|
|
||||||
if [[ "$NETLIFY" != "true" ]]; then
|
|
||||||
echo "This script can only be run on Netlify"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
function createStatus() {
|
|
||||||
state="$1"
|
|
||||||
description="$2"
|
|
||||||
target_url="$3"
|
|
||||||
curl -X POST -H "Accept: application/vnd.github.v3+json" -H "Authorization: token $GITHUB_TOKEN" \
|
|
||||||
"https://api.github.com/repos/home-assistant/frontend/statuses/$COMMIT_REF" \
|
|
||||||
-d '{"state": "'"${state}"'", "context": "Netlify/Gallery Preview Build", "description": "'"$description"'", "target_url": "'"$target_url"'"}'
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if [[ "${PULL_REQUEST}" == "false" ]]; then
|
|
||||||
gulp build-gallery
|
|
||||||
else
|
|
||||||
if [[ "$(curl -sSLf -H "Accept: application/vnd.github.v3+json" -H "Authorization: token $GITHUB_TOKEN" \
|
|
||||||
"https://api.github.com/repos/home-assistant/frontend/pulls/${REVIEW_ID}" | jq '.labels[].name' -r)" =~ "$TARGET_LABEL" ]]; then
|
|
||||||
createStatus "pending" "Building gallery preview" "https://app.netlify.com/sites/home-assistant-gallery/deploys/$BUILD_ID"
|
|
||||||
gulp build-gallery
|
|
||||||
if [ $? -eq 0 ]; then
|
|
||||||
createStatus "success" "Build complete" "$DEPLOY_URL"
|
|
||||||
else
|
|
||||||
createStatus "error" "Build failed" "https://app.netlify.com/sites/home-assistant-gallery/deploys/$BUILD_ID"
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
createStatus "success" "Build was not requested by PR label"
|
|
||||||
fi
|
|
||||||
fi
|
|
@@ -1,143 +0,0 @@
|
|||||||
import { Button } from "@material/mwc-button";
|
|
||||||
import { html, LitElement, css, TemplateResult } from "lit";
|
|
||||||
import { customElement, property } from "lit/decorators";
|
|
||||||
import { applyThemesOnElement } from "../../../src/common/dom/apply_themes_on_element";
|
|
||||||
import { fireEvent } from "../../../src/common/dom/fire_event";
|
|
||||||
|
|
||||||
@customElement("demo-black-white-row")
|
|
||||||
class DemoBlackWhiteRow extends LitElement {
|
|
||||||
@property() title!: string;
|
|
||||||
|
|
||||||
@property() value!: any;
|
|
||||||
|
|
||||||
@property() disabled = false;
|
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
|
||||||
return html`
|
|
||||||
<div class="row">
|
|
||||||
<div class="content light">
|
|
||||||
<ha-card .header=${this.title}>
|
|
||||||
<div class="card-content">
|
|
||||||
<slot name="light"></slot>
|
|
||||||
</div>
|
|
||||||
<div class="card-actions">
|
|
||||||
<mwc-button
|
|
||||||
.disabled=${this.disabled}
|
|
||||||
@click=${this.handleSubmit}
|
|
||||||
>
|
|
||||||
Submit
|
|
||||||
</mwc-button>
|
|
||||||
</div>
|
|
||||||
</ha-card>
|
|
||||||
</div>
|
|
||||||
<div class="content dark">
|
|
||||||
<ha-card .header=${this.title}>
|
|
||||||
<div class="card-content">
|
|
||||||
<slot name="dark"></slot>
|
|
||||||
</div>
|
|
||||||
<div class="card-actions">
|
|
||||||
<mwc-button
|
|
||||||
.disabled=${this.disabled}
|
|
||||||
@click=${this.handleSubmit}
|
|
||||||
>
|
|
||||||
Submit
|
|
||||||
</mwc-button>
|
|
||||||
</div>
|
|
||||||
</ha-card>
|
|
||||||
<pre>${JSON.stringify(this.value, undefined, 2)}</pre>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
firstUpdated(changedProps) {
|
|
||||||
super.firstUpdated(changedProps);
|
|
||||||
applyThemesOnElement(
|
|
||||||
this.shadowRoot!.querySelector(".dark"),
|
|
||||||
{
|
|
||||||
default_theme: "default",
|
|
||||||
default_dark_theme: "default",
|
|
||||||
themes: {},
|
|
||||||
darkMode: false,
|
|
||||||
},
|
|
||||||
"default",
|
|
||||||
{ dark: true }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
handleSubmit(ev) {
|
|
||||||
const content = (ev.target as Button).closest(".content")!;
|
|
||||||
fireEvent(this, "submitted" as any, {
|
|
||||||
slot: content.classList.contains("light") ? "light" : "dark",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
static styles = css`
|
|
||||||
.row {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
.content {
|
|
||||||
padding: 50px 0;
|
|
||||||
background-color: var(--primary-background-color);
|
|
||||||
}
|
|
||||||
.light {
|
|
||||||
flex: 1;
|
|
||||||
padding-left: 50px;
|
|
||||||
padding-right: 50px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
.light ha-card {
|
|
||||||
margin-left: auto;
|
|
||||||
}
|
|
||||||
.dark {
|
|
||||||
display: flex;
|
|
||||||
flex: 1;
|
|
||||||
padding-left: 50px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
}
|
|
||||||
ha-card {
|
|
||||||
width: 400px;
|
|
||||||
}
|
|
||||||
pre {
|
|
||||||
width: 300px;
|
|
||||||
margin: 0 16px 0;
|
|
||||||
overflow: auto;
|
|
||||||
color: var(--primary-text-color);
|
|
||||||
}
|
|
||||||
.card-actions {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row-reverse;
|
|
||||||
border-top: none;
|
|
||||||
}
|
|
||||||
@media only screen and (max-width: 1500px) {
|
|
||||||
.light {
|
|
||||||
flex: initial;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@media only screen and (max-width: 1000px) {
|
|
||||||
.light,
|
|
||||||
.dark {
|
|
||||||
padding: 16px;
|
|
||||||
}
|
|
||||||
.row,
|
|
||||||
.dark {
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
ha-card {
|
|
||||||
margin: 0 auto;
|
|
||||||
width: 100%;
|
|
||||||
max-width: 400px;
|
|
||||||
}
|
|
||||||
pre {
|
|
||||||
margin: 16px auto;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
declare global {
|
|
||||||
interface HTMLElementTagNameMap {
|
|
||||||
"demo-black-white-row": DemoBlackWhiteRow;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,91 +0,0 @@
|
|||||||
/* eslint-disable lit/no-template-arrow */
|
|
||||||
import { LitElement, TemplateResult, html } from "lit";
|
|
||||||
import { customElement, state } from "lit/decorators";
|
|
||||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
|
||||||
import type { HomeAssistant } from "../../../src/types";
|
|
||||||
import "../components/demo-black-white-row";
|
|
||||||
import { mockEntityRegistry } from "../../../demo/src/stubs/entity_registry";
|
|
||||||
import { mockDeviceRegistry } from "../../../demo/src/stubs/device_registry";
|
|
||||||
import { mockAreaRegistry } from "../../../demo/src/stubs/area_registry";
|
|
||||||
import { mockHassioSupervisor } from "../../../demo/src/stubs/hassio_supervisor";
|
|
||||||
import "../../../src/panels/config/automation/action/ha-automation-action";
|
|
||||||
import { HaChooseAction } from "../../../src/panels/config/automation/action/types/ha-automation-action-choose";
|
|
||||||
import { HaDelayAction } from "../../../src/panels/config/automation/action/types/ha-automation-action-delay";
|
|
||||||
import { HaDeviceAction } from "../../../src/panels/config/automation/action/types/ha-automation-action-device_id";
|
|
||||||
import { HaEventAction } from "../../../src/panels/config/automation/action/types/ha-automation-action-event";
|
|
||||||
import { HaRepeatAction } from "../../../src/panels/config/automation/action/types/ha-automation-action-repeat";
|
|
||||||
import { HaSceneAction } from "../../../src/panels/config/automation/action/types/ha-automation-action-scene";
|
|
||||||
import { HaServiceAction } from "../../../src/panels/config/automation/action/types/ha-automation-action-service";
|
|
||||||
import { HaWaitForTriggerAction } from "../../../src/panels/config/automation/action/types/ha-automation-action-wait_for_trigger";
|
|
||||||
import { HaWaitAction } from "../../../src/panels/config/automation/action/types/ha-automation-action-wait_template";
|
|
||||||
import { Action } from "../../../src/data/script";
|
|
||||||
import { HaConditionAction } from "../../../src/panels/config/automation/action/types/ha-automation-action-condition";
|
|
||||||
|
|
||||||
const SCHEMAS: { name: string; actions: Action[] }[] = [
|
|
||||||
{ name: "Event", actions: [HaEventAction.defaultConfig] },
|
|
||||||
{ name: "Device", actions: [HaDeviceAction.defaultConfig] },
|
|
||||||
{ name: "Service", actions: [HaServiceAction.defaultConfig] },
|
|
||||||
{ name: "Condition", actions: [HaConditionAction.defaultConfig] },
|
|
||||||
{ name: "Delay", actions: [HaDelayAction.defaultConfig] },
|
|
||||||
{ name: "Scene", actions: [HaSceneAction.defaultConfig] },
|
|
||||||
{ name: "Wait", actions: [HaWaitAction.defaultConfig] },
|
|
||||||
{ name: "WaitForTrigger", actions: [HaWaitForTriggerAction.defaultConfig] },
|
|
||||||
{ name: "Repeat", actions: [HaRepeatAction.defaultConfig] },
|
|
||||||
{ name: "Choose", actions: [HaChooseAction.defaultConfig] },
|
|
||||||
{ name: "Variables", actions: [{ variables: { hello: "1" } }] },
|
|
||||||
];
|
|
||||||
|
|
||||||
@customElement("demo-automation-editor-action")
|
|
||||||
class DemoHaAutomationEditorAction extends LitElement {
|
|
||||||
@state() private hass!: HomeAssistant;
|
|
||||||
|
|
||||||
private data: any = SCHEMAS.map((info) => info.actions);
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
const hass = provideHass(this);
|
|
||||||
hass.updateTranslations(null, "en");
|
|
||||||
hass.updateTranslations("config", "en");
|
|
||||||
mockEntityRegistry(hass);
|
|
||||||
mockDeviceRegistry(hass);
|
|
||||||
mockAreaRegistry(hass);
|
|
||||||
mockHassioSupervisor(hass);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
|
||||||
const valueChanged = (ev) => {
|
|
||||||
const sampleIdx = ev.target.sampleIdx;
|
|
||||||
this.data[sampleIdx] = ev.detail.value;
|
|
||||||
this.requestUpdate();
|
|
||||||
};
|
|
||||||
return html`
|
|
||||||
${SCHEMAS.map(
|
|
||||||
(info, sampleIdx) => html`
|
|
||||||
<demo-black-white-row
|
|
||||||
.title=${info.name}
|
|
||||||
.value=${this.data[sampleIdx]}
|
|
||||||
>
|
|
||||||
${["light", "dark"].map(
|
|
||||||
(slot) =>
|
|
||||||
html`
|
|
||||||
<ha-automation-action
|
|
||||||
slot=${slot}
|
|
||||||
.hass=${this.hass}
|
|
||||||
.actions=${this.data[sampleIdx]}
|
|
||||||
.sampleIdx=${sampleIdx}
|
|
||||||
@value-changed=${valueChanged}
|
|
||||||
></ha-automation-action>
|
|
||||||
`
|
|
||||||
)}
|
|
||||||
</demo-black-white-row>
|
|
||||||
`
|
|
||||||
)}
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
declare global {
|
|
||||||
interface HTMLElementTagNameMap {
|
|
||||||
"demo-ha-automation-editor-action": DemoHaAutomationEditorAction;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,127 +0,0 @@
|
|||||||
/* eslint-disable lit/no-template-arrow */
|
|
||||||
import { LitElement, TemplateResult, html } from "lit";
|
|
||||||
import { customElement, state } from "lit/decorators";
|
|
||||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
|
||||||
import type { HomeAssistant } from "../../../src/types";
|
|
||||||
import "../components/demo-black-white-row";
|
|
||||||
import { mockEntityRegistry } from "../../../demo/src/stubs/entity_registry";
|
|
||||||
import { mockDeviceRegistry } from "../../../demo/src/stubs/device_registry";
|
|
||||||
import { mockAreaRegistry } from "../../../demo/src/stubs/area_registry";
|
|
||||||
import { mockHassioSupervisor } from "../../../demo/src/stubs/hassio_supervisor";
|
|
||||||
import type { Condition } from "../../../src/data/automation";
|
|
||||||
import "../../../src/panels/config/automation/condition/ha-automation-condition";
|
|
||||||
import { HaDeviceCondition } from "../../../src/panels/config/automation/condition/types/ha-automation-condition-device";
|
|
||||||
import { HaLogicalCondition } from "../../../src/panels/config/automation/condition/types/ha-automation-condition-logical";
|
|
||||||
import HaNumericStateCondition from "../../../src/panels/config/automation/condition/types/ha-automation-condition-numeric_state";
|
|
||||||
import { HaStateCondition } from "../../../src/panels/config/automation/condition/types/ha-automation-condition-state";
|
|
||||||
import { HaSunCondition } from "../../../src/panels/config/automation/condition/types/ha-automation-condition-sun";
|
|
||||||
import { HaTemplateCondition } from "../../../src/panels/config/automation/condition/types/ha-automation-condition-template";
|
|
||||||
import { HaTimeCondition } from "../../../src/panels/config/automation/condition/types/ha-automation-condition-time";
|
|
||||||
import { HaTriggerCondition } from "../../../src/panels/config/automation/condition/types/ha-automation-condition-trigger";
|
|
||||||
import { HaZoneCondition } from "../../../src/panels/config/automation/condition/types/ha-automation-condition-zone";
|
|
||||||
|
|
||||||
const SCHEMAS: { name: string; conditions: Condition[] }[] = [
|
|
||||||
{
|
|
||||||
name: "State",
|
|
||||||
conditions: [{ condition: "state", ...HaStateCondition.defaultConfig }],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Numeric State",
|
|
||||||
conditions: [
|
|
||||||
{ condition: "numeric_state", ...HaNumericStateCondition.defaultConfig },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Sun",
|
|
||||||
conditions: [{ condition: "sun", ...HaSunCondition.defaultConfig }],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Zone",
|
|
||||||
conditions: [{ condition: "zone", ...HaZoneCondition.defaultConfig }],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Time",
|
|
||||||
conditions: [{ condition: "time", ...HaTimeCondition.defaultConfig }],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Template",
|
|
||||||
conditions: [
|
|
||||||
{ condition: "template", ...HaTemplateCondition.defaultConfig },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Device",
|
|
||||||
conditions: [{ condition: "device", ...HaDeviceCondition.defaultConfig }],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "And",
|
|
||||||
conditions: [{ condition: "and", ...HaLogicalCondition.defaultConfig }],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Or",
|
|
||||||
conditions: [{ condition: "or", ...HaLogicalCondition.defaultConfig }],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Not",
|
|
||||||
conditions: [{ condition: "not", ...HaLogicalCondition.defaultConfig }],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Trigger",
|
|
||||||
conditions: [{ condition: "trigger", ...HaTriggerCondition.defaultConfig }],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
@customElement("demo-automation-editor-condition")
|
|
||||||
class DemoHaAutomationEditorCondition extends LitElement {
|
|
||||||
@state() private hass!: HomeAssistant;
|
|
||||||
|
|
||||||
private data: any = SCHEMAS.map((info) => info.conditions);
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
const hass = provideHass(this);
|
|
||||||
hass.updateTranslations(null, "en");
|
|
||||||
hass.updateTranslations("config", "en");
|
|
||||||
mockEntityRegistry(hass);
|
|
||||||
mockDeviceRegistry(hass);
|
|
||||||
mockAreaRegistry(hass);
|
|
||||||
mockHassioSupervisor(hass);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
|
||||||
const valueChanged = (ev) => {
|
|
||||||
const sampleIdx = ev.target.sampleIdx;
|
|
||||||
this.data[sampleIdx] = ev.detail.value;
|
|
||||||
this.requestUpdate();
|
|
||||||
};
|
|
||||||
return html`
|
|
||||||
${SCHEMAS.map(
|
|
||||||
(info, sampleIdx) => html`
|
|
||||||
<demo-black-white-row
|
|
||||||
.title=${info.name}
|
|
||||||
.value=${this.data[sampleIdx]}
|
|
||||||
>
|
|
||||||
${["light", "dark"].map(
|
|
||||||
(slot) =>
|
|
||||||
html`
|
|
||||||
<ha-automation-condition
|
|
||||||
slot=${slot}
|
|
||||||
.hass=${this.hass}
|
|
||||||
.conditions=${this.data[sampleIdx]}
|
|
||||||
.sampleIdx=${sampleIdx}
|
|
||||||
@value-changed=${valueChanged}
|
|
||||||
></ha-automation-condition>
|
|
||||||
`
|
|
||||||
)}
|
|
||||||
</demo-black-white-row>
|
|
||||||
`
|
|
||||||
)}
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
declare global {
|
|
||||||
interface HTMLElementTagNameMap {
|
|
||||||
"demo-ha-automation-editor-condition": DemoHaAutomationEditorCondition;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,159 +0,0 @@
|
|||||||
/* eslint-disable lit/no-template-arrow */
|
|
||||||
import { LitElement, TemplateResult, html } from "lit";
|
|
||||||
import { customElement, state } from "lit/decorators";
|
|
||||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
|
||||||
import type { HomeAssistant } from "../../../src/types";
|
|
||||||
import "../components/demo-black-white-row";
|
|
||||||
import { mockEntityRegistry } from "../../../demo/src/stubs/entity_registry";
|
|
||||||
import { mockDeviceRegistry } from "../../../demo/src/stubs/device_registry";
|
|
||||||
import { mockAreaRegistry } from "../../../demo/src/stubs/area_registry";
|
|
||||||
import { mockHassioSupervisor } from "../../../demo/src/stubs/hassio_supervisor";
|
|
||||||
import type { Trigger } from "../../../src/data/automation";
|
|
||||||
import { HaGeolocationTrigger } from "../../../src/panels/config/automation/trigger/types/ha-automation-trigger-geo_location";
|
|
||||||
import { HaEventTrigger } from "../../../src/panels/config/automation/trigger/types/ha-automation-trigger-event";
|
|
||||||
import { HaHassTrigger } from "../../../src/panels/config/automation/trigger/types/ha-automation-trigger-homeassistant";
|
|
||||||
import { HaNumericStateTrigger } from "../../../src/panels/config/automation/trigger/types/ha-automation-trigger-numeric_state";
|
|
||||||
import { HaSunTrigger } from "../../../src/panels/config/automation/trigger/types/ha-automation-trigger-sun";
|
|
||||||
import { HaTagTrigger } from "../../../src/panels/config/automation/trigger/types/ha-automation-trigger-tag";
|
|
||||||
import { HaTemplateTrigger } from "../../../src/panels/config/automation/trigger/types/ha-automation-trigger-template";
|
|
||||||
import { HaTimeTrigger } from "../../../src/panels/config/automation/trigger/types/ha-automation-trigger-time";
|
|
||||||
import { HaTimePatternTrigger } from "../../../src/panels/config/automation/trigger/types/ha-automation-trigger-time_pattern";
|
|
||||||
import { HaWebhookTrigger } from "../../../src/panels/config/automation/trigger/types/ha-automation-trigger-webhook";
|
|
||||||
import { HaZoneTrigger } from "../../../src/panels/config/automation/trigger/types/ha-automation-trigger-zone";
|
|
||||||
import { HaDeviceTrigger } from "../../../src/panels/config/automation/trigger/types/ha-automation-trigger-device";
|
|
||||||
import { HaStateTrigger } from "../../../src/panels/config/automation/trigger/types/ha-automation-trigger-state";
|
|
||||||
import { HaMQTTTrigger } from "../../../src/panels/config/automation/trigger/types/ha-automation-trigger-mqtt";
|
|
||||||
import "../../../src/panels/config/automation/trigger/ha-automation-trigger";
|
|
||||||
|
|
||||||
const SCHEMAS: { name: string; triggers: Trigger[] }[] = [
|
|
||||||
{
|
|
||||||
name: "State",
|
|
||||||
triggers: [{ platform: "state", ...HaStateTrigger.defaultConfig }],
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
name: "MQTT",
|
|
||||||
triggers: [{ platform: "mqtt", ...HaMQTTTrigger.defaultConfig }],
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
name: "GeoLocation",
|
|
||||||
triggers: [
|
|
||||||
{ platform: "geo_location", ...HaGeolocationTrigger.defaultConfig },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
name: "Home Assistant",
|
|
||||||
triggers: [{ platform: "homeassistant", ...HaHassTrigger.defaultConfig }],
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
name: "Numeric State",
|
|
||||||
triggers: [
|
|
||||||
{ platform: "numeric_state", ...HaNumericStateTrigger.defaultConfig },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
name: "Sun",
|
|
||||||
triggers: [{ platform: "sun", ...HaSunTrigger.defaultConfig }],
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
name: "Time Pattern",
|
|
||||||
triggers: [
|
|
||||||
{ platform: "time_pattern", ...HaTimePatternTrigger.defaultConfig },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
name: "Webhook",
|
|
||||||
triggers: [{ platform: "webhook", ...HaWebhookTrigger.defaultConfig }],
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
name: "Zone",
|
|
||||||
triggers: [{ platform: "zone", ...HaZoneTrigger.defaultConfig }],
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
name: "Tag",
|
|
||||||
triggers: [{ platform: "tag", ...HaTagTrigger.defaultConfig }],
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
name: "Time",
|
|
||||||
triggers: [{ platform: "time", ...HaTimeTrigger.defaultConfig }],
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
name: "Template",
|
|
||||||
triggers: [{ platform: "template", ...HaTemplateTrigger.defaultConfig }],
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
name: "Event",
|
|
||||||
triggers: [{ platform: "event", ...HaEventTrigger.defaultConfig }],
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
name: "Device Trigger",
|
|
||||||
triggers: [{ platform: "device", ...HaDeviceTrigger.defaultConfig }],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
@customElement("demo-automation-editor-trigger")
|
|
||||||
class DemoHaAutomationEditorTrigger extends LitElement {
|
|
||||||
@state() private hass!: HomeAssistant;
|
|
||||||
|
|
||||||
private data: any = SCHEMAS.map((info) => info.triggers);
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
const hass = provideHass(this);
|
|
||||||
hass.updateTranslations(null, "en");
|
|
||||||
hass.updateTranslations("config", "en");
|
|
||||||
mockEntityRegistry(hass);
|
|
||||||
mockDeviceRegistry(hass);
|
|
||||||
mockAreaRegistry(hass);
|
|
||||||
mockHassioSupervisor(hass);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
|
||||||
const valueChanged = (ev) => {
|
|
||||||
const sampleIdx = ev.target.sampleIdx;
|
|
||||||
this.data[sampleIdx] = ev.detail.value;
|
|
||||||
this.requestUpdate();
|
|
||||||
};
|
|
||||||
return html`
|
|
||||||
${SCHEMAS.map(
|
|
||||||
(info, sampleIdx) => html`
|
|
||||||
<demo-black-white-row
|
|
||||||
.title=${info.name}
|
|
||||||
.value=${this.data[sampleIdx]}
|
|
||||||
>
|
|
||||||
${["light", "dark"].map(
|
|
||||||
(slot) =>
|
|
||||||
html`
|
|
||||||
<ha-automation-trigger
|
|
||||||
slot=${slot}
|
|
||||||
.hass=${this.hass}
|
|
||||||
.triggers=${this.data[sampleIdx]}
|
|
||||||
.sampleIdx=${sampleIdx}
|
|
||||||
@value-changed=${valueChanged}
|
|
||||||
></ha-automation-trigger>
|
|
||||||
`
|
|
||||||
)}
|
|
||||||
</demo-black-white-row>
|
|
||||||
`
|
|
||||||
)}
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
declare global {
|
|
||||||
interface HTMLElementTagNameMap {
|
|
||||||
"demo-ha-automation-editor-trigger": DemoHaAutomationEditorTrigger;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,19 +1,15 @@
|
|||||||
import "@material/mwc-button/mwc-button";
|
import { html, css, LitElement, TemplateResult } from "lit";
|
||||||
import { css, html, LitElement, TemplateResult } from "lit";
|
|
||||||
import { customElement } from "lit/decorators";
|
import { customElement } from "lit/decorators";
|
||||||
import { applyThemesOnElement } from "../../../src/common/dom/apply_themes_on_element";
|
|
||||||
import "../../../src/components/ha-alert";
|
import "../../../src/components/ha-alert";
|
||||||
import "../../../src/components/ha-card";
|
import "../../../src/components/ha-card";
|
||||||
import "../../../src/components/ha-logo-svg";
|
|
||||||
|
|
||||||
const alerts: {
|
const alerts: {
|
||||||
title?: string;
|
title?: string;
|
||||||
description: string | TemplateResult;
|
description: string | TemplateResult;
|
||||||
type: "info" | "warning" | "error" | "success";
|
type: "info" | "warning" | "error" | "success";
|
||||||
dismissable?: boolean;
|
dismissable?: boolean;
|
||||||
|
action?: string;
|
||||||
rtl?: boolean;
|
rtl?: boolean;
|
||||||
iconSlot?: TemplateResult;
|
|
||||||
actionSlot?: TemplateResult;
|
|
||||||
}[] = [
|
}[] = [
|
||||||
{
|
{
|
||||||
title: "Test info alert",
|
title: "Test info alert",
|
||||||
@@ -77,35 +73,13 @@ const alerts: {
|
|||||||
title: "Error with action",
|
title: "Error with action",
|
||||||
description: "This is a test error alert with action",
|
description: "This is a test error alert with action",
|
||||||
type: "error",
|
type: "error",
|
||||||
actionSlot: html`<mwc-button slot="action" label="restart"></mwc-button>`,
|
action: "restart",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Unsaved data",
|
title: "Unsaved data",
|
||||||
description: "You have unsaved data",
|
description: "You have unsaved data",
|
||||||
type: "warning",
|
type: "warning",
|
||||||
actionSlot: html`<mwc-button slot="action" label="save"></mwc-button>`,
|
action: "save",
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Slotted icon",
|
|
||||||
description: "Alert with slotted icon",
|
|
||||||
type: "warning",
|
|
||||||
iconSlot: html`<span slot="icon" class="image">
|
|
||||||
<ha-logo-svg></ha-logo-svg>
|
|
||||||
</span>`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Slotted image",
|
|
||||||
description: "Alert with slotted image",
|
|
||||||
type: "warning",
|
|
||||||
iconSlot: html`<span slot="icon" class="image"
|
|
||||||
><img src="https://www.home-assistant.io/images/home-assistant-logo.svg"
|
|
||||||
/></span>`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Slotted action",
|
|
||||||
description: "Alert with slotted action",
|
|
||||||
type: "info",
|
|
||||||
actionSlot: html`<mwc-button slot="action" label="action"></mwc-button>`,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: "Dismissable information (RTL)",
|
description: "Dismissable information (RTL)",
|
||||||
@@ -117,7 +91,7 @@ const alerts: {
|
|||||||
title: "Error with action",
|
title: "Error with action",
|
||||||
description: "This is a test error alert with action (RTL)",
|
description: "This is a test error alert with action (RTL)",
|
||||||
type: "error",
|
type: "error",
|
||||||
actionSlot: html`<mwc-button slot="action" label="restart"></mwc-button>`,
|
action: "restart",
|
||||||
rtl: true,
|
rtl: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -132,83 +106,38 @@ const alerts: {
|
|||||||
export class DemoHaAlert extends LitElement {
|
export class DemoHaAlert extends LitElement {
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
return html`
|
return html`
|
||||||
${["light", "dark"].map(
|
<ha-card header="ha-alert demo">
|
||||||
(mode) => html`
|
${alerts.map(
|
||||||
<div class=${mode}>
|
(alert) => html`
|
||||||
<ha-card header="ha-alert ${mode} demo">
|
<ha-alert
|
||||||
<div class="card-content">
|
.title=${alert.title || ""}
|
||||||
${alerts.map(
|
.alertType=${alert.type}
|
||||||
(alert) => html`
|
.dismissable=${alert.dismissable || false}
|
||||||
<ha-alert
|
.actionText=${alert.action || ""}
|
||||||
.title=${alert.title || ""}
|
.rtl=${alert.rtl || false}
|
||||||
.alertType=${alert.type}
|
>
|
||||||
.dismissable=${alert.dismissable || false}
|
${alert.description}
|
||||||
.rtl=${alert.rtl || false}
|
</ha-alert>
|
||||||
>
|
`
|
||||||
${alert.iconSlot} ${alert.description} ${alert.actionSlot}
|
)}
|
||||||
</ha-alert>
|
</ha-card>
|
||||||
`
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</ha-card>
|
|
||||||
</div>
|
|
||||||
`
|
|
||||||
)}
|
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
firstUpdated(changedProps) {
|
|
||||||
super.firstUpdated(changedProps);
|
|
||||||
applyThemesOnElement(
|
|
||||||
this.shadowRoot!.querySelector(".dark"),
|
|
||||||
{
|
|
||||||
default_theme: "default",
|
|
||||||
default_dark_theme: "default",
|
|
||||||
themes: {},
|
|
||||||
darkMode: false,
|
|
||||||
},
|
|
||||||
"default",
|
|
||||||
{ dark: true }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
static get styles() {
|
static get styles() {
|
||||||
return css`
|
return css`
|
||||||
:host {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
.dark,
|
|
||||||
.light {
|
|
||||||
display: block;
|
|
||||||
background-color: var(--primary-background-color);
|
|
||||||
padding: 0 50px;
|
|
||||||
}
|
|
||||||
ha-card {
|
ha-card {
|
||||||
|
max-width: 600px;
|
||||||
margin: 24px auto;
|
margin: 24px auto;
|
||||||
}
|
}
|
||||||
ha-alert {
|
|
||||||
display: block;
|
|
||||||
margin: 24px 0;
|
|
||||||
}
|
|
||||||
.condition {
|
.condition {
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
.image {
|
span {
|
||||||
display: inline-flex;
|
margin-right: 16px;
|
||||||
height: 100%;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
img {
|
|
||||||
max-height: 24px;
|
|
||||||
width: 24px;
|
|
||||||
}
|
|
||||||
mwc-button {
|
|
||||||
--mdc-theme-primary: var(--primary-text-color);
|
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
@@ -1,85 +0,0 @@
|
|||||||
import { html, css, LitElement, TemplateResult } from "lit";
|
|
||||||
import { customElement } from "lit/decorators";
|
|
||||||
import { classMap } from "lit/directives/class-map";
|
|
||||||
import "../../../src/components/ha-bar";
|
|
||||||
import "../../../src/components/ha-card";
|
|
||||||
|
|
||||||
const bars: {
|
|
||||||
min?: number;
|
|
||||||
max?: number;
|
|
||||||
value: number;
|
|
||||||
warning?: number;
|
|
||||||
error?: number;
|
|
||||||
}[] = [
|
|
||||||
{
|
|
||||||
value: 33,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 150,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
min: -10,
|
|
||||||
value: 0,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 80,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 200,
|
|
||||||
max: 13,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 4,
|
|
||||||
min: 13,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
@customElement("demo-ha-bar")
|
|
||||||
export class DemoHaBar extends LitElement {
|
|
||||||
protected render(): TemplateResult {
|
|
||||||
return html`
|
|
||||||
${bars
|
|
||||||
.map((bar) => ({ min: 0, max: 100, warning: 70, error: 90, ...bar }))
|
|
||||||
.map(
|
|
||||||
(bar) => html`
|
|
||||||
<ha-card>
|
|
||||||
<div class="card-content">
|
|
||||||
<pre>Config: ${JSON.stringify(bar)}</pre>
|
|
||||||
<ha-bar
|
|
||||||
class=${classMap({
|
|
||||||
warning: bar.value > bar.warning,
|
|
||||||
error: bar.value > bar.error,
|
|
||||||
})}
|
|
||||||
.min=${bar.min}
|
|
||||||
.max=${bar.max}
|
|
||||||
.value=${bar.value}
|
|
||||||
>
|
|
||||||
</ha-bar>
|
|
||||||
</div>
|
|
||||||
</ha-card>
|
|
||||||
`
|
|
||||||
)}
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
static get styles() {
|
|
||||||
return css`
|
|
||||||
ha-card {
|
|
||||||
max-width: 600px;
|
|
||||||
margin: 24px auto;
|
|
||||||
}
|
|
||||||
.warning {
|
|
||||||
--ha-bar-primary-color: var(--warning-color);
|
|
||||||
}
|
|
||||||
.error {
|
|
||||||
--ha-bar-primary-color: var(--error-color);
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
declare global {
|
|
||||||
interface HTMLElementTagNameMap {
|
|
||||||
"demo-ha-bar": DemoHaBar;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,86 +0,0 @@
|
|||||||
import { mdiHomeAssistant } from "@mdi/js";
|
|
||||||
import { css, html, LitElement, TemplateResult } from "lit";
|
|
||||||
import { customElement } from "lit/decorators";
|
|
||||||
import "../../../src/components/ha-card";
|
|
||||||
import "../../../src/components/ha-chip";
|
|
||||||
import "../../../src/components/ha-chip-set";
|
|
||||||
import "../../../src/components/ha-svg-icon";
|
|
||||||
|
|
||||||
const chips: {
|
|
||||||
icon?: string;
|
|
||||||
content?: string;
|
|
||||||
}[] = [
|
|
||||||
{},
|
|
||||||
{
|
|
||||||
icon: mdiHomeAssistant,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
content: "Content",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: mdiHomeAssistant,
|
|
||||||
content: "Content",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
@customElement("demo-ha-chips")
|
|
||||||
export class DemoHaChips extends LitElement {
|
|
||||||
protected render(): TemplateResult {
|
|
||||||
return html`
|
|
||||||
<ha-card header="ha-chip demo">
|
|
||||||
<div class="card-content">
|
|
||||||
${chips.map(
|
|
||||||
(chip) => html`
|
|
||||||
<ha-chip .hasIcon=${chip.icon !== undefined}>
|
|
||||||
${chip.icon
|
|
||||||
? html`<ha-svg-icon slot="icon" .path=${chip.icon}>
|
|
||||||
</ha-svg-icon>`
|
|
||||||
: ""}
|
|
||||||
${chip.content}
|
|
||||||
</ha-chip>
|
|
||||||
`
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</ha-card>
|
|
||||||
<ha-card header="ha-chip-set demo">
|
|
||||||
<div class="card-content">
|
|
||||||
<ha-chip-set>
|
|
||||||
${chips.map(
|
|
||||||
(chip) => html`
|
|
||||||
<ha-chip .hasIcon=${chip.icon !== undefined}>
|
|
||||||
${chip.icon
|
|
||||||
? html`<ha-svg-icon slot="icon" .path=${chip.icon}>
|
|
||||||
</ha-svg-icon>`
|
|
||||||
: ""}
|
|
||||||
${chip.content}
|
|
||||||
</ha-chip>
|
|
||||||
`
|
|
||||||
)}
|
|
||||||
</ha-chip-set>
|
|
||||||
</div>
|
|
||||||
</ha-card>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
static get styles() {
|
|
||||||
return css`
|
|
||||||
ha-card {
|
|
||||||
max-width: 600px;
|
|
||||||
margin: 24px auto;
|
|
||||||
}
|
|
||||||
ha-chip {
|
|
||||||
margin-bottom: 4px;
|
|
||||||
}
|
|
||||||
.card-content {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
declare global {
|
|
||||||
interface HTMLElementTagNameMap {
|
|
||||||
"demo-ha-chips": DemoHaChips;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,306 +0,0 @@
|
|||||||
/* eslint-disable lit/no-template-arrow */
|
|
||||||
import "@material/mwc-button";
|
|
||||||
import { LitElement, TemplateResult, html } from "lit";
|
|
||||||
import { customElement } from "lit/decorators";
|
|
||||||
import { computeInitialHaFormData } from "../../../src/components/ha-form/compute-initial-ha-form-data";
|
|
||||||
import type { HaFormSchema } from "../../../src/components/ha-form/types";
|
|
||||||
import "../../../src/components/ha-form/ha-form";
|
|
||||||
import "../components/demo-black-white-row";
|
|
||||||
|
|
||||||
const SCHEMAS: {
|
|
||||||
title: string;
|
|
||||||
translations?: Record<string, string>;
|
|
||||||
error?: Record<string, string>;
|
|
||||||
schema: HaFormSchema[];
|
|
||||||
data?: Record<string, any>;
|
|
||||||
}[] = [
|
|
||||||
{
|
|
||||||
title: "Authentication",
|
|
||||||
translations: {
|
|
||||||
username: "Username",
|
|
||||||
password: "Password",
|
|
||||||
invalid_login: "Invalid username or password",
|
|
||||||
},
|
|
||||||
error: {
|
|
||||||
base: "invalid_login",
|
|
||||||
},
|
|
||||||
schema: [
|
|
||||||
{
|
|
||||||
type: "string",
|
|
||||||
name: "username",
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "string",
|
|
||||||
name: "password",
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
title: "One of each",
|
|
||||||
schema: [
|
|
||||||
{
|
|
||||||
type: "constant",
|
|
||||||
value: "Constant Value",
|
|
||||||
name: "constant",
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "boolean",
|
|
||||||
name: "bool",
|
|
||||||
optional: true,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "integer",
|
|
||||||
name: "int",
|
|
||||||
optional: true,
|
|
||||||
default: 10,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "float",
|
|
||||||
name: "float",
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "string",
|
|
||||||
name: "string",
|
|
||||||
optional: true,
|
|
||||||
default: "Default",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "select",
|
|
||||||
options: [
|
|
||||||
["default", "default"],
|
|
||||||
["other", "other"],
|
|
||||||
],
|
|
||||||
name: "select",
|
|
||||||
optional: true,
|
|
||||||
default: "default",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "multi_select",
|
|
||||||
options: {
|
|
||||||
default: "Default",
|
|
||||||
other: "Other",
|
|
||||||
},
|
|
||||||
name: "multi",
|
|
||||||
optional: true,
|
|
||||||
default: ["default"],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "positive_time_period_dict",
|
|
||||||
name: "time",
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Numbers",
|
|
||||||
schema: [
|
|
||||||
{
|
|
||||||
type: "integer",
|
|
||||||
name: "int",
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "integer",
|
|
||||||
name: "int with default",
|
|
||||||
optional: true,
|
|
||||||
default: 10,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "integer",
|
|
||||||
name: "int range required",
|
|
||||||
required: true,
|
|
||||||
default: 5,
|
|
||||||
valueMin: 0,
|
|
||||||
valueMax: 10,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "integer",
|
|
||||||
name: "int range optional",
|
|
||||||
optional: true,
|
|
||||||
valueMin: 0,
|
|
||||||
valueMax: 10,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "select",
|
|
||||||
schema: [
|
|
||||||
{
|
|
||||||
type: "select",
|
|
||||||
options: [
|
|
||||||
["default", "Default"],
|
|
||||||
["other", "Other"],
|
|
||||||
],
|
|
||||||
name: "select",
|
|
||||||
required: true,
|
|
||||||
default: "default",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "select",
|
|
||||||
options: [
|
|
||||||
["default", "Default"],
|
|
||||||
["other", "Other"],
|
|
||||||
],
|
|
||||||
name: "select optional",
|
|
||||||
optional: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "select",
|
|
||||||
options: [
|
|
||||||
["default", "Default"],
|
|
||||||
["other", "Other"],
|
|
||||||
["uno", "mas"],
|
|
||||||
["one", "more"],
|
|
||||||
["and", "another_one"],
|
|
||||||
["option", "1000"],
|
|
||||||
],
|
|
||||||
name: "select many otions",
|
|
||||||
optional: true,
|
|
||||||
default: "default",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Multi select",
|
|
||||||
schema: [
|
|
||||||
{
|
|
||||||
type: "multi_select",
|
|
||||||
options: {
|
|
||||||
default: "Default",
|
|
||||||
other: "Other",
|
|
||||||
},
|
|
||||||
name: "multi",
|
|
||||||
required: true,
|
|
||||||
default: ["default"],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "multi_select",
|
|
||||||
options: {
|
|
||||||
default: "Default",
|
|
||||||
other: "Other",
|
|
||||||
uno: "mas",
|
|
||||||
one: "more",
|
|
||||||
and: "another_one",
|
|
||||||
option: "1000",
|
|
||||||
},
|
|
||||||
name: "multi many otions",
|
|
||||||
optional: true,
|
|
||||||
default: ["default"],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Field specific error",
|
|
||||||
data: {
|
|
||||||
new_password: "hello",
|
|
||||||
new_password_2: "bye",
|
|
||||||
},
|
|
||||||
translations: {
|
|
||||||
new_password: "New Password",
|
|
||||||
new_password_2: "Re-type Password",
|
|
||||||
not_match: "The passwords do not match",
|
|
||||||
},
|
|
||||||
error: {
|
|
||||||
new_password_2: "not_match",
|
|
||||||
},
|
|
||||||
schema: [
|
|
||||||
{
|
|
||||||
type: "string",
|
|
||||||
name: "new_password",
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "string",
|
|
||||||
name: "new_password_2",
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "OctoPrint",
|
|
||||||
translations: {
|
|
||||||
username: "Username",
|
|
||||||
host: "Host",
|
|
||||||
port: "Port Number",
|
|
||||||
path: "Application Path",
|
|
||||||
ssl: "Use SSL",
|
|
||||||
},
|
|
||||||
schema: [
|
|
||||||
{ type: "string", name: "username", required: true, default: "" },
|
|
||||||
{ type: "string", name: "host", required: true, default: "" },
|
|
||||||
{
|
|
||||||
type: "integer",
|
|
||||||
valueMin: 1,
|
|
||||||
valueMax: 65535,
|
|
||||||
name: "port",
|
|
||||||
optional: true,
|
|
||||||
default: 80,
|
|
||||||
},
|
|
||||||
{ type: "string", name: "path", optional: true, default: "/" },
|
|
||||||
{ type: "boolean", name: "ssl", optional: true, default: false },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
@customElement("demo-ha-form")
|
|
||||||
class DemoHaForm extends LitElement {
|
|
||||||
private data = SCHEMAS.map(
|
|
||||||
({ schema, data }) => data || computeInitialHaFormData(schema)
|
|
||||||
);
|
|
||||||
|
|
||||||
private disabled = SCHEMAS.map(() => false);
|
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
|
||||||
return html`
|
|
||||||
${SCHEMAS.map((info, idx) => {
|
|
||||||
const translations = info.translations || {};
|
|
||||||
return html`
|
|
||||||
<demo-black-white-row
|
|
||||||
.title=${info.title}
|
|
||||||
.value=${this.data[idx]}
|
|
||||||
.disabled=${this.disabled[idx]}
|
|
||||||
@submitted=${() => {
|
|
||||||
this.disabled[idx] = true;
|
|
||||||
this.requestUpdate();
|
|
||||||
setTimeout(() => {
|
|
||||||
this.disabled[idx] = false;
|
|
||||||
this.requestUpdate();
|
|
||||||
}, 2000);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
${["light", "dark"].map(
|
|
||||||
(slot) => html`
|
|
||||||
<ha-form
|
|
||||||
slot=${slot}
|
|
||||||
.data=${this.data[idx]}
|
|
||||||
.schema=${info.schema}
|
|
||||||
.error=${info.error}
|
|
||||||
.disabled=${this.disabled[idx]}
|
|
||||||
.computeError=${(error) => translations[error] || error}
|
|
||||||
.computeLabel=${(schema) =>
|
|
||||||
translations[schema.name] || schema.name}
|
|
||||||
@value-changed=${(e) => {
|
|
||||||
this.data[idx] = e.detail.value;
|
|
||||||
this.requestUpdate();
|
|
||||||
}}
|
|
||||||
></ha-form>
|
|
||||||
`
|
|
||||||
)}
|
|
||||||
</demo-black-white-row>
|
|
||||||
`;
|
|
||||||
})}
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
declare global {
|
|
||||||
interface HTMLElementTagNameMap {
|
|
||||||
"demo-ha-form": DemoHaForm;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,122 +0,0 @@
|
|||||||
import { html, css, LitElement, TemplateResult } from "lit";
|
|
||||||
import { customElement } from "lit/decorators";
|
|
||||||
import "../../../src/components/ha-label-badge";
|
|
||||||
import "../../../src/components/ha-card";
|
|
||||||
|
|
||||||
const colors = ["#03a9f4", "#ffa600", "#43a047"];
|
|
||||||
|
|
||||||
const badges: {
|
|
||||||
label?: string;
|
|
||||||
description?: string;
|
|
||||||
image?: string;
|
|
||||||
}[] = [
|
|
||||||
{
|
|
||||||
label: "label",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "label",
|
|
||||||
description: "Description",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: "Description",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "label",
|
|
||||||
description: "Description",
|
|
||||||
image: "/images/living_room.png",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: "Description",
|
|
||||||
image: "/images/living_room.png",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "label",
|
|
||||||
image: "/images/living_room.png",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
image: "/images/living_room.png",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "big label",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "big label",
|
|
||||||
description: "Description",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "big label",
|
|
||||||
description: "Description",
|
|
||||||
image: "/images/living_room.png",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
@customElement("demo-ha-label-badge")
|
|
||||||
export class DemoHaLabelBadge extends LitElement {
|
|
||||||
protected render(): TemplateResult {
|
|
||||||
return html`
|
|
||||||
<ha-card>
|
|
||||||
<div class="card-content">
|
|
||||||
${badges.map(
|
|
||||||
(badge) => html`
|
|
||||||
<ha-label-badge
|
|
||||||
style="--ha-label-badge-color: ${colors[
|
|
||||||
Math.floor(Math.random() * colors.length)
|
|
||||||
]}"
|
|
||||||
.label=${badge.label}
|
|
||||||
.description=${badge.description}
|
|
||||||
.image=${badge.image}
|
|
||||||
>
|
|
||||||
</ha-label-badge>
|
|
||||||
`
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</ha-card>
|
|
||||||
<ha-card>
|
|
||||||
<div class="card-content">
|
|
||||||
${badges.map(
|
|
||||||
(badge) => html`
|
|
||||||
<div class="badge">
|
|
||||||
<ha-label-badge
|
|
||||||
style="--ha-label-badge-color: ${colors[
|
|
||||||
Math.floor(Math.random() * colors.length)
|
|
||||||
]}"
|
|
||||||
.label=${badge.label}
|
|
||||||
.description=${badge.description}
|
|
||||||
.image=${badge.image}
|
|
||||||
>
|
|
||||||
</ha-label-badge>
|
|
||||||
<pre>${JSON.stringify(badge, null, 2)}</pre>
|
|
||||||
</div>
|
|
||||||
`
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</ha-card>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
static get styles() {
|
|
||||||
return css`
|
|
||||||
ha-card {
|
|
||||||
max-width: 600px;
|
|
||||||
margin: 24px auto;
|
|
||||||
}
|
|
||||||
pre {
|
|
||||||
margin-left: 16px;
|
|
||||||
background-color: var(--markdown-code-background-color);
|
|
||||||
padding: 8px;
|
|
||||||
}
|
|
||||||
.badge {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
margin-bottom: 16px;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
declare global {
|
|
||||||
interface HTMLElementTagNameMap {
|
|
||||||
"demo-ha-label-badge": DemoHaLabelBadge;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,131 +0,0 @@
|
|||||||
/* eslint-disable lit/no-template-arrow */
|
|
||||||
import "@material/mwc-button";
|
|
||||||
import { LitElement, TemplateResult, css, html } from "lit";
|
|
||||||
import { customElement, state } from "lit/decorators";
|
|
||||||
import "../../../src/components/ha-selector/ha-selector";
|
|
||||||
import "../../../src/components/ha-settings-row";
|
|
||||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
|
||||||
import type { HomeAssistant } from "../../../src/types";
|
|
||||||
import "../components/demo-black-white-row";
|
|
||||||
import { BlueprintInput } from "../../../src/data/blueprint";
|
|
||||||
import { mockEntityRegistry } from "../../../demo/src/stubs/entity_registry";
|
|
||||||
import { mockDeviceRegistry } from "../../../demo/src/stubs/device_registry";
|
|
||||||
import { mockAreaRegistry } from "../../../demo/src/stubs/area_registry";
|
|
||||||
import { mockHassioSupervisor } from "../../../demo/src/stubs/hassio_supervisor";
|
|
||||||
|
|
||||||
const SCHEMAS: {
|
|
||||||
name: string;
|
|
||||||
input: Record<string, BlueprintInput | null>;
|
|
||||||
}[] = [
|
|
||||||
{
|
|
||||||
name: "One of each",
|
|
||||||
input: {
|
|
||||||
entity: { name: "Entity", selector: { entity: {} } },
|
|
||||||
device: { name: "Device", selector: { device: {} } },
|
|
||||||
addon: { name: "Addon", selector: { addon: {} } },
|
|
||||||
area: { name: "Area", selector: { area: {} } },
|
|
||||||
target: { name: "Target", selector: { target: {} } },
|
|
||||||
number_box: {
|
|
||||||
name: "Number Box",
|
|
||||||
selector: {
|
|
||||||
number: {
|
|
||||||
min: 0,
|
|
||||||
max: 10,
|
|
||||||
mode: "box",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
number_slider: {
|
|
||||||
name: "Number Slider",
|
|
||||||
selector: {
|
|
||||||
number: {
|
|
||||||
min: 0,
|
|
||||||
max: 10,
|
|
||||||
mode: "slider",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
boolean: { name: "Boolean", selector: { boolean: {} } },
|
|
||||||
time: { name: "Time", selector: { time: {} } },
|
|
||||||
action: { name: "Action", selector: { action: {} } },
|
|
||||||
text: { name: "Text", selector: { text: { multiline: false } } },
|
|
||||||
text_multiline: {
|
|
||||||
name: "Text multiline",
|
|
||||||
selector: { text: { multiline: true } },
|
|
||||||
},
|
|
||||||
object: { name: "Object", selector: { object: {} } },
|
|
||||||
select: {
|
|
||||||
name: "Select",
|
|
||||||
selector: { select: { options: ["Option 1", "Option 2"] } },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
@customElement("demo-ha-selector")
|
|
||||||
class DemoHaSelector extends LitElement {
|
|
||||||
@state() private hass!: HomeAssistant;
|
|
||||||
|
|
||||||
private data = SCHEMAS.map(() => ({}));
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
const hass = provideHass(this);
|
|
||||||
hass.updateTranslations(null, "en");
|
|
||||||
hass.updateTranslations("config", "en");
|
|
||||||
mockEntityRegistry(hass);
|
|
||||||
mockDeviceRegistry(hass);
|
|
||||||
mockAreaRegistry(hass);
|
|
||||||
mockHassioSupervisor(hass);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
|
||||||
return html`
|
|
||||||
${SCHEMAS.map((info, idx) => {
|
|
||||||
const data = this.data[idx];
|
|
||||||
const valueChanged = (ev) => {
|
|
||||||
this.data[idx] = {
|
|
||||||
...data,
|
|
||||||
[ev.target.key]: ev.detail.value,
|
|
||||||
};
|
|
||||||
this.requestUpdate();
|
|
||||||
};
|
|
||||||
return html`
|
|
||||||
<demo-black-white-row .title=${info.name} .value=${this.data[idx]}>
|
|
||||||
${["light", "dark"].map((slot) =>
|
|
||||||
Object.entries(info.input).map(
|
|
||||||
([key, value]) =>
|
|
||||||
html`
|
|
||||||
<ha-settings-row narrow slot=${slot}>
|
|
||||||
<span slot="heading">${value?.name || key}</span>
|
|
||||||
<span slot="description">${value?.description}</span>
|
|
||||||
<ha-selector
|
|
||||||
.hass=${this.hass}
|
|
||||||
.selector=${value!.selector}
|
|
||||||
.key=${key}
|
|
||||||
.value=${data[key] ?? value!.default}
|
|
||||||
@value-changed=${valueChanged}
|
|
||||||
></ha-selector>
|
|
||||||
</ha-settings-row>
|
|
||||||
`
|
|
||||||
)
|
|
||||||
)}
|
|
||||||
</demo-black-white-row>
|
|
||||||
`;
|
|
||||||
})}
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
static styles = css`
|
|
||||||
paper-input,
|
|
||||||
ha-selector {
|
|
||||||
width: 60;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
declare global {
|
|
||||||
interface HTMLElementTagNameMap {
|
|
||||||
"demo-ha-selector": DemoHaSelector;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,156 +0,0 @@
|
|||||||
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
|
|
||||||
import { customElement, query } from "lit/decorators";
|
|
||||||
import { getEntity } from "../../../src/fake_data/entity";
|
|
||||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
|
||||||
import "../components/demo-cards";
|
|
||||||
|
|
||||||
const ENTITIES = [
|
|
||||||
getEntity("light", "bed_light", "on", {
|
|
||||||
friendly_name: "Bed Light",
|
|
||||||
}),
|
|
||||||
getEntity("switch", "bed_ac", "on", {
|
|
||||||
friendly_name: "Ecobee",
|
|
||||||
}),
|
|
||||||
getEntity("sensor", "bed_temp", "72", {
|
|
||||||
friendly_name: "Bedroom Temp",
|
|
||||||
device_class: "temperature",
|
|
||||||
unit_of_measurement: "°F",
|
|
||||||
}),
|
|
||||||
getEntity("light", "living_room_light", "off", {
|
|
||||||
friendly_name: "Living Room Light",
|
|
||||||
}),
|
|
||||||
getEntity("fan", "living_room", "on", {
|
|
||||||
friendly_name: "Living Room Fan",
|
|
||||||
}),
|
|
||||||
getEntity("sensor", "office_humidity", "73", {
|
|
||||||
friendly_name: "Office Humidity",
|
|
||||||
device_class: "humidity",
|
|
||||||
unit_of_measurement: "%",
|
|
||||||
}),
|
|
||||||
getEntity("light", "office", "on", {
|
|
||||||
friendly_name: "Office Light",
|
|
||||||
}),
|
|
||||||
getEntity("fan", "kitchen", "on", {
|
|
||||||
friendly_name: "Second Office Fan",
|
|
||||||
}),
|
|
||||||
getEntity("binary_sensor", "kitchen_door", "on", {
|
|
||||||
friendly_name: "Office Door",
|
|
||||||
device_class: "door",
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
|
|
||||||
// TODO: Update image here
|
|
||||||
const CONFIGS = [
|
|
||||||
{
|
|
||||||
heading: "Bedroom",
|
|
||||||
config: `
|
|
||||||
- type: area
|
|
||||||
area: bedroom
|
|
||||||
image: "/images/bed.png"
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
heading: "Living Room",
|
|
||||||
config: `
|
|
||||||
- type: area
|
|
||||||
area: living_room
|
|
||||||
image: "/images/living_room.png"
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
heading: "Office",
|
|
||||||
config: `
|
|
||||||
- type: area
|
|
||||||
area: office
|
|
||||||
image: "/images/office.jpg"
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
heading: "Kitchen",
|
|
||||||
config: `
|
|
||||||
- type: area
|
|
||||||
area: kitchen
|
|
||||||
image: "/images/kitchen.png"
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
@customElement("demo-hui-area-card")
|
|
||||||
class DemoArea extends LitElement {
|
|
||||||
@query("#demos") private _demoRoot!: HTMLElement;
|
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
|
||||||
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected firstUpdated(changedProperties: PropertyValues) {
|
|
||||||
super.firstUpdated(changedProperties);
|
|
||||||
const hass = provideHass(this._demoRoot);
|
|
||||||
hass.updateTranslations(null, "en");
|
|
||||||
hass.updateTranslations("lovelace", "en");
|
|
||||||
hass.addEntities(ENTITIES);
|
|
||||||
hass.mockWS("config/area_registry/list", () => [
|
|
||||||
{
|
|
||||||
name: "Bedroom",
|
|
||||||
area_id: "bedroom",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Living Room",
|
|
||||||
area_id: "living_room",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Office",
|
|
||||||
area_id: "office",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Second Office",
|
|
||||||
area_id: "kitchen",
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
hass.mockWS("config/device_registry/list", () => []);
|
|
||||||
hass.mockWS("config/entity_registry/list", () => [
|
|
||||||
{
|
|
||||||
area_id: "bedroom",
|
|
||||||
entity_id: "light.bed_light",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
area_id: "bedroom",
|
|
||||||
entity_id: "switch.bed_ac",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
area_id: "bedroom",
|
|
||||||
entity_id: "sensor.bed_temp",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
area_id: "living_room",
|
|
||||||
entity_id: "light.living_room_light",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
area_id: "living_room",
|
|
||||||
entity_id: "fan.living_room",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
area_id: "office",
|
|
||||||
entity_id: "light.office",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
area_id: "office",
|
|
||||||
entity_id: "sensor.office_humidity",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
area_id: "kitchen",
|
|
||||||
entity_id: "fan.kitchen",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
area_id: "kitchen",
|
|
||||||
entity_id: "binary_sensor.kitchen_door",
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
declare global {
|
|
||||||
interface HTMLElementTagNameMap {
|
|
||||||
"demo-hui-area-card": DemoArea;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -187,7 +187,6 @@ const createEntityRegistryEntries = (
|
|||||||
device_id: "mock-device-id",
|
device_id: "mock-device-id",
|
||||||
area_id: null,
|
area_id: null,
|
||||||
disabled_by: null,
|
disabled_by: null,
|
||||||
entity_category: null,
|
|
||||||
entity_id: "binary_sensor.updater",
|
entity_id: "binary_sensor.updater",
|
||||||
name: null,
|
name: null,
|
||||||
icon: null,
|
icon: null,
|
||||||
@@ -212,7 +211,6 @@ const createDeviceRegistryEntries = (
|
|||||||
area_id: null,
|
area_id: null,
|
||||||
name_by_user: null,
|
name_by_user: null,
|
||||||
disabled_by: null,
|
disabled_by: null,
|
||||||
configuration_url: null,
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@@ -65,11 +65,10 @@ class HaGallery extends PolymerElement {
|
|||||||
<app-header slot="header" fixed>
|
<app-header slot="header" fixed>
|
||||||
<app-toolbar>
|
<app-toolbar>
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
|
icon="hass:arrow-left"
|
||||||
on-click="_backTapped"
|
on-click="_backTapped"
|
||||||
class$="[[_computeHeaderButtonClass(_demo)]]"
|
class$="[[_computeHeaderButtonClass(_demo)]]"
|
||||||
>
|
></ha-icon-button>
|
||||||
<ha-icon icon="hass:arrow-left"></ha-icon>
|
|
||||||
</ha-icon-button>
|
|
||||||
<div main-title>
|
<div main-title>
|
||||||
[[_withDefault(_demo, "Home Assistant Gallery")]]
|
[[_withDefault(_demo, "Home Assistant Gallery")]]
|
||||||
</div>
|
</div>
|
||||||
@@ -176,6 +175,11 @@ class HaGallery extends PolymerElement {
|
|||||||
this.addEventListener("alert-dismissed-clicked", () =>
|
this.addEventListener("alert-dismissed-clicked", () =>
|
||||||
this.$.notifications.showDialog({ message: "Alert dismissed clicked" })
|
this.$.notifications.showDialog({ message: "Alert dismissed clicked" })
|
||||||
);
|
);
|
||||||
|
|
||||||
|
this.addEventListener("alert-action-clicked", () =>
|
||||||
|
this.$.notifications.showDialog({ message: "Alert action clicked" })
|
||||||
|
);
|
||||||
|
|
||||||
this.addEventListener("hass-more-info", (ev) => {
|
this.addEventListener("hass-more-info", (ev) => {
|
||||||
if (ev.detail.entityId) {
|
if (ev.detail.entityId) {
|
||||||
this.$.notifications.showDialog({
|
this.$.notifications.showDialog({
|
||||||
|
@@ -4,7 +4,6 @@ import { property } from "lit/decorators";
|
|||||||
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 { caseInsensitiveStringCompare } from "../../../src/common/string/compare";
|
|
||||||
import "../../../src/components/ha-card";
|
import "../../../src/components/ha-card";
|
||||||
import {
|
import {
|
||||||
HassioAddonInfo,
|
HassioAddonInfo,
|
||||||
@@ -33,7 +32,7 @@ class HassioAddonRepositoryEl extends LitElement {
|
|||||||
return filterAndSort(addons, filter);
|
return filterAndSort(addons, filter);
|
||||||
}
|
}
|
||||||
return addons.sort((a, b) =>
|
return addons.sort((a, b) =>
|
||||||
caseInsensitiveStringCompare(a.name, b.name)
|
a.name.toUpperCase() < b.name.toUpperCase() ? -1 : 1
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
import "@material/mwc-icon-button/mwc-icon-button";
|
||||||
import { ActionDetail } from "@material/mwc-list/mwc-list-foundation";
|
import { ActionDetail } from "@material/mwc-list/mwc-list-foundation";
|
||||||
import "@material/mwc-list/mwc-list-item";
|
import "@material/mwc-list/mwc-list-item";
|
||||||
import { mdiDotsVertical } from "@mdi/js";
|
import { mdiDotsVertical } from "@mdi/js";
|
||||||
@@ -17,7 +18,7 @@ import { navigate } from "../../../src/common/navigate";
|
|||||||
import "../../../src/common/search/search-input";
|
import "../../../src/common/search/search-input";
|
||||||
import { extractSearchParam } from "../../../src/common/url/search-params";
|
import { extractSearchParam } from "../../../src/common/url/search-params";
|
||||||
import "../../../src/components/ha-button-menu";
|
import "../../../src/components/ha-button-menu";
|
||||||
import "../../../src/components/ha-icon-button";
|
import "../../../src/components/ha-svg-icon";
|
||||||
import {
|
import {
|
||||||
HassioAddonInfo,
|
HassioAddonInfo,
|
||||||
HassioAddonRepository,
|
HassioAddonRepository,
|
||||||
@@ -25,10 +26,11 @@ import {
|
|||||||
} from "../../../src/data/hassio/addon";
|
} from "../../../src/data/hassio/addon";
|
||||||
import { Supervisor } from "../../../src/data/supervisor/supervisor";
|
import { Supervisor } from "../../../src/data/supervisor/supervisor";
|
||||||
import "../../../src/layouts/hass-loading-screen";
|
import "../../../src/layouts/hass-loading-screen";
|
||||||
import "../../../src/layouts/hass-subpage";
|
import "../../../src/layouts/hass-tabs-subpage";
|
||||||
import { HomeAssistant, Route } from "../../../src/types";
|
import { HomeAssistant, Route } from "../../../src/types";
|
||||||
import { showRegistriesDialog } from "../dialogs/registries/show-dialog-registries";
|
import { showRegistriesDialog } from "../dialogs/registries/show-dialog-registries";
|
||||||
import { showRepositoriesDialog } from "../dialogs/repositories/show-dialog-repositories";
|
import { showRepositoriesDialog } from "../dialogs/repositories/show-dialog-repositories";
|
||||||
|
import { supervisorTabs } from "../hassio-tabs";
|
||||||
import "./hassio-addon-repository";
|
import "./hassio-addon-repository";
|
||||||
|
|
||||||
const sortRepos = (a: HassioAddonRepository, b: HassioAddonRepository) => {
|
const sortRepos = (a: HassioAddonRepository, b: HassioAddonRepository) => {
|
||||||
@@ -75,22 +77,24 @@ class HassioAddonStore extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<hass-subpage
|
<hass-tabs-subpage
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
|
.localizeFunc=${this.supervisor.localize}
|
||||||
.narrow=${this.narrow}
|
.narrow=${this.narrow}
|
||||||
.route=${this.route}
|
.route=${this.route}
|
||||||
.header=${this.supervisor.localize("panel.store")}
|
.tabs=${supervisorTabs}
|
||||||
|
main-page
|
||||||
|
supervisor
|
||||||
>
|
>
|
||||||
|
<span slot="header"> ${this.supervisor.localize("panel.store")} </span>
|
||||||
<ha-button-menu
|
<ha-button-menu
|
||||||
corner="BOTTOM_START"
|
corner="BOTTOM_START"
|
||||||
slot="toolbar-icon"
|
slot="toolbar-icon"
|
||||||
@action=${this._handleAction}
|
@action=${this._handleAction}
|
||||||
>
|
>
|
||||||
<ha-icon-button
|
<mwc-icon-button slot="trigger" alt="menu">
|
||||||
.label=${this.supervisor.localize("common.menu")}
|
<ha-svg-icon .path=${mdiDotsVertical}></ha-svg-icon>
|
||||||
.path=${mdiDotsVertical}
|
</mwc-icon-button>
|
||||||
slot="trigger"
|
|
||||||
></ha-icon-button>
|
|
||||||
<mwc-list-item>
|
<mwc-list-item>
|
||||||
${this.supervisor.localize("store.repositories")}
|
${this.supervisor.localize("store.repositories")}
|
||||||
</mwc-list-item>
|
</mwc-list-item>
|
||||||
@@ -109,7 +113,6 @@ class HassioAddonStore extends LitElement {
|
|||||||
: html`
|
: html`
|
||||||
<div class="search">
|
<div class="search">
|
||||||
<search-input
|
<search-input
|
||||||
.hass=${this.hass}
|
|
||||||
no-label-float
|
no-label-float
|
||||||
no-underline
|
no-underline
|
||||||
.filter=${this._filter}
|
.filter=${this._filter}
|
||||||
@@ -128,7 +131,7 @@ class HassioAddonStore extends LitElement {
|
|||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
</hass-subpage>
|
</hass-tabs-subpage>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -15,13 +15,12 @@ import { customElement, property, query, state } from "lit/decorators";
|
|||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
||||||
import "../../../../src/components/buttons/ha-progress-button";
|
import "../../../../src/components/buttons/ha-progress-button";
|
||||||
import "../../../../src/components/ha-alert";
|
|
||||||
import "../../../../src/components/ha-button-menu";
|
import "../../../../src/components/ha-button-menu";
|
||||||
import "../../../../src/components/ha-card";
|
import "../../../../src/components/ha-card";
|
||||||
|
import "../../../../src/components/ha-alert";
|
||||||
import "../../../../src/components/ha-form/ha-form";
|
import "../../../../src/components/ha-form/ha-form";
|
||||||
import type { HaFormSchema } from "../../../../src/components/ha-form/types";
|
import type { HaFormSchema } from "../../../../src/components/ha-form/ha-form";
|
||||||
import "../../../../src/components/ha-formfield";
|
import "../../../../src/components/ha-formfield";
|
||||||
import "../../../../src/components/ha-icon-button";
|
|
||||||
import "../../../../src/components/ha-switch";
|
import "../../../../src/components/ha-switch";
|
||||||
import "../../../../src/components/ha-yaml-editor";
|
import "../../../../src/components/ha-yaml-editor";
|
||||||
import type { HaYamlEditor } from "../../../../src/components/ha-yaml-editor";
|
import type { HaYamlEditor } from "../../../../src/components/ha-yaml-editor";
|
||||||
@@ -78,18 +77,6 @@ class HassioAddonConfig extends LitElement {
|
|||||||
this.addon.translations.en?.configuration?.[entry.name].name ||
|
this.addon.translations.en?.configuration?.[entry.name].name ||
|
||||||
entry.name;
|
entry.name;
|
||||||
|
|
||||||
private _schema = memoizeOne((schema: HaFormSchema[]): HaFormSchema[] =>
|
|
||||||
// @ts-expect-error supervisor does not implement [string, string] for select.options[]
|
|
||||||
schema.map((entry) =>
|
|
||||||
entry.type === "select"
|
|
||||||
? {
|
|
||||||
...entry,
|
|
||||||
options: entry.options.map((option) => [option, option]),
|
|
||||||
}
|
|
||||||
: entry
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
private _filteredShchema = memoizeOne(
|
private _filteredShchema = memoizeOne(
|
||||||
(options: Record<string, unknown>, schema: HaFormSchema[]) =>
|
(options: Record<string, unknown>, schema: HaFormSchema[]) =>
|
||||||
schema.filter((entry) => entry.name in options || entry.required)
|
schema.filter((entry) => entry.name in options || entry.required)
|
||||||
@@ -113,11 +100,9 @@ class HassioAddonConfig extends LitElement {
|
|||||||
</h2>
|
</h2>
|
||||||
<div class="card-menu">
|
<div class="card-menu">
|
||||||
<ha-button-menu corner="BOTTOM_START" @action=${this._handleAction}>
|
<ha-button-menu corner="BOTTOM_START" @action=${this._handleAction}>
|
||||||
<ha-icon-button
|
<mwc-icon-button slot="trigger">
|
||||||
.label=${this.hass.localize("common.menu")}
|
<ha-svg-icon .path=${mdiDotsVertical}></ha-svg-icon>
|
||||||
.path=${mdiDotsVertical}
|
</mwc-icon-button>
|
||||||
slot="trigger"
|
|
||||||
></ha-icon-button>
|
|
||||||
<mwc-list-item .disabled=${!this._canShowSchema}>
|
<mwc-list-item .disabled=${!this._canShowSchema}>
|
||||||
${this._yamlMode
|
${this._yamlMode
|
||||||
? this.supervisor.localize(
|
? this.supervisor.localize(
|
||||||
@@ -140,14 +125,12 @@ class HassioAddonConfig extends LitElement {
|
|||||||
.data=${this._options!}
|
.data=${this._options!}
|
||||||
@value-changed=${this._configChanged}
|
@value-changed=${this._configChanged}
|
||||||
.computeLabel=${this.computeLabel}
|
.computeLabel=${this.computeLabel}
|
||||||
.schema=${this._schema(
|
.schema=${this._showOptional
|
||||||
this._showOptional
|
? this.addon.schema!
|
||||||
? this.addon.schema!
|
: this._filteredShchema(
|
||||||
: this._filteredShchema(
|
this.addon.options,
|
||||||
this.addon.options,
|
this.addon.schema!
|
||||||
this.addon.schema!
|
)}
|
||||||
)
|
|
||||||
)}
|
|
||||||
></ha-form>`
|
></ha-form>`
|
||||||
: html` <ha-yaml-editor
|
: html` <ha-yaml-editor
|
||||||
@value-changed=${this._configChanged}
|
@value-changed=${this._configChanged}
|
||||||
|
@@ -108,6 +108,7 @@ class HassioAddonDashboard extends LitElement {
|
|||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.localizeFunc=${this.supervisor.localize}
|
.localizeFunc=${this.supervisor.localize}
|
||||||
.narrow=${this.narrow}
|
.narrow=${this.narrow}
|
||||||
|
.backPath=${this.addon.version ? "/hassio/dashboard" : "/hassio/store"}
|
||||||
.route=${route}
|
.route=${route}
|
||||||
.tabs=${addonTabs}
|
.tabs=${addonTabs}
|
||||||
supervisor
|
supervisor
|
||||||
|
@@ -4,7 +4,7 @@ import "../../../../src/components/ha-circular-progress";
|
|||||||
import { HassioAddonDetails } from "../../../../src/data/hassio/addon";
|
import { HassioAddonDetails } from "../../../../src/data/hassio/addon";
|
||||||
import { Supervisor } from "../../../../src/data/supervisor/supervisor";
|
import { Supervisor } from "../../../../src/data/supervisor/supervisor";
|
||||||
import { haStyle } from "../../../../src/resources/styles";
|
import { haStyle } from "../../../../src/resources/styles";
|
||||||
import { HomeAssistant, Route } from "../../../../src/types";
|
import { HomeAssistant } from "../../../../src/types";
|
||||||
import { hassioStyle } from "../../resources/hassio-style";
|
import { hassioStyle } from "../../resources/hassio-style";
|
||||||
import "./hassio-addon-info";
|
import "./hassio-addon-info";
|
||||||
|
|
||||||
@@ -12,8 +12,6 @@ import "./hassio-addon-info";
|
|||||||
class HassioAddonInfoDashboard extends LitElement {
|
class HassioAddonInfoDashboard extends LitElement {
|
||||||
@property({ type: Boolean }) public narrow!: boolean;
|
@property({ type: Boolean }) public narrow!: boolean;
|
||||||
|
|
||||||
@property({ attribute: false }) public route!: Route;
|
|
||||||
|
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
@property({ attribute: false }) public supervisor!: Supervisor;
|
@property({ attribute: false }) public supervisor!: Supervisor;
|
||||||
@@ -29,7 +27,6 @@ class HassioAddonInfoDashboard extends LitElement {
|
|||||||
<div class="content">
|
<div class="content">
|
||||||
<hassio-addon-info
|
<hassio-addon-info
|
||||||
.narrow=${this.narrow}
|
.narrow=${this.narrow}
|
||||||
.route=${this.route}
|
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.supervisor=${this.supervisor}
|
.supervisor=${this.supervisor}
|
||||||
.addon=${this.addon}
|
.addon=${this.addon}
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
import "@material/mwc-button";
|
import "@material/mwc-button";
|
||||||
import {
|
import {
|
||||||
|
mdiArrowUpBoldCircle,
|
||||||
mdiCheckCircle,
|
mdiCheckCircle,
|
||||||
mdiChip,
|
mdiChip,
|
||||||
mdiCircle,
|
mdiCircle,
|
||||||
@@ -10,12 +11,6 @@ import {
|
|||||||
mdiHomeAssistant,
|
mdiHomeAssistant,
|
||||||
mdiKey,
|
mdiKey,
|
||||||
mdiNetwork,
|
mdiNetwork,
|
||||||
mdiNumeric1,
|
|
||||||
mdiNumeric2,
|
|
||||||
mdiNumeric3,
|
|
||||||
mdiNumeric4,
|
|
||||||
mdiNumeric5,
|
|
||||||
mdiNumeric6,
|
|
||||||
mdiPound,
|
mdiPound,
|
||||||
mdiShield,
|
mdiShield,
|
||||||
} from "@mdi/js";
|
} from "@mdi/js";
|
||||||
@@ -30,7 +25,7 @@ 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-alert";
|
import "../../../../src/components/ha-alert";
|
||||||
import "../../../../src/components/ha-card";
|
import "../../../../src/components/ha-card";
|
||||||
import "../../../../src/components/ha-chip";
|
import "../../../../src/components/ha-label-badge";
|
||||||
import "../../../../src/components/ha-markdown";
|
import "../../../../src/components/ha-markdown";
|
||||||
import "../../../../src/components/ha-settings-row";
|
import "../../../../src/components/ha-settings-row";
|
||||||
import "../../../../src/components/ha-svg-icon";
|
import "../../../../src/components/ha-svg-icon";
|
||||||
@@ -48,6 +43,7 @@ import {
|
|||||||
startHassioAddon,
|
startHassioAddon,
|
||||||
stopHassioAddon,
|
stopHassioAddon,
|
||||||
uninstallHassioAddon,
|
uninstallHassioAddon,
|
||||||
|
updateHassioAddon,
|
||||||
validateHassioAddonOption,
|
validateHassioAddonOption,
|
||||||
} from "../../../../src/data/hassio/addon";
|
} from "../../../../src/data/hassio/addon";
|
||||||
import {
|
import {
|
||||||
@@ -62,14 +58,14 @@ import {
|
|||||||
showConfirmationDialog,
|
showConfirmationDialog,
|
||||||
} from "../../../../src/dialogs/generic/show-dialog-box";
|
} from "../../../../src/dialogs/generic/show-dialog-box";
|
||||||
import { haStyle } from "../../../../src/resources/styles";
|
import { haStyle } from "../../../../src/resources/styles";
|
||||||
import { HomeAssistant, Route } from "../../../../src/types";
|
import { HomeAssistant } from "../../../../src/types";
|
||||||
import { bytesToString } from "../../../../src/util/bytes-to-string";
|
import { bytesToString } from "../../../../src/util/bytes-to-string";
|
||||||
import "../../components/hassio-card-content";
|
import "../../components/hassio-card-content";
|
||||||
import "../../components/supervisor-metric";
|
import "../../components/supervisor-metric";
|
||||||
import { showHassioMarkdownDialog } from "../../dialogs/markdown/show-dialog-hassio-markdown";
|
import { showHassioMarkdownDialog } from "../../dialogs/markdown/show-dialog-hassio-markdown";
|
||||||
|
import { showDialogSupervisorUpdate } from "../../dialogs/update/show-dialog-update";
|
||||||
import { hassioStyle } from "../../resources/hassio-style";
|
import { hassioStyle } from "../../resources/hassio-style";
|
||||||
import "../../update-available/update-available-card";
|
import { addonArchIsSupported } from "../../util/addon";
|
||||||
import { addonArchIsSupported, extractChangelog } from "../../util/addon";
|
|
||||||
|
|
||||||
const STAGE_ICON = {
|
const STAGE_ICON = {
|
||||||
stable: mdiCheckCircle,
|
stable: mdiCheckCircle,
|
||||||
@@ -77,21 +73,10 @@ const STAGE_ICON = {
|
|||||||
deprecated: mdiExclamationThick,
|
deprecated: mdiExclamationThick,
|
||||||
};
|
};
|
||||||
|
|
||||||
const RATING_ICON = {
|
|
||||||
1: mdiNumeric1,
|
|
||||||
2: mdiNumeric2,
|
|
||||||
3: mdiNumeric3,
|
|
||||||
4: mdiNumeric4,
|
|
||||||
5: mdiNumeric5,
|
|
||||||
6: mdiNumeric6,
|
|
||||||
};
|
|
||||||
|
|
||||||
@customElement("hassio-addon-info")
|
@customElement("hassio-addon-info")
|
||||||
class HassioAddonInfo extends LitElement {
|
class HassioAddonInfo extends LitElement {
|
||||||
@property({ type: Boolean }) public narrow!: boolean;
|
@property({ type: Boolean }) public narrow!: boolean;
|
||||||
|
|
||||||
@property({ attribute: false }) public route!: Route;
|
|
||||||
|
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
@property({ attribute: false }) public addon!: HassioAddonDetails;
|
@property({ attribute: false }) public addon!: HassioAddonDetails;
|
||||||
@@ -128,35 +113,91 @@ class HassioAddonInfo extends LitElement {
|
|||||||
return html`
|
return html`
|
||||||
${this.addon.update_available
|
${this.addon.update_available
|
||||||
? html`
|
? html`
|
||||||
<update-available-card
|
<ha-card
|
||||||
.hass=${this.hass}
|
.header="${this.supervisor.localize(
|
||||||
.narrow=${this.narrow}
|
"common.update_available",
|
||||||
.supervisor=${this.supervisor}
|
"count",
|
||||||
.addonSlug=${this.addon.slug}
|
1
|
||||||
></update-available-card>
|
)}🎉"
|
||||||
|
>
|
||||||
|
<div class="card-content">
|
||||||
|
<hassio-card-content
|
||||||
|
.hass=${this.hass}
|
||||||
|
.title=${this.supervisor.localize(
|
||||||
|
"addon.dashboard.new_update_available",
|
||||||
|
"name",
|
||||||
|
this.addon.name,
|
||||||
|
"version",
|
||||||
|
this.addon.version_latest
|
||||||
|
)}
|
||||||
|
.description=${this.supervisor.localize(
|
||||||
|
"common.running_version",
|
||||||
|
"version",
|
||||||
|
this.addon.version
|
||||||
|
)}
|
||||||
|
icon=${mdiArrowUpBoldCircle}
|
||||||
|
iconClass="update"
|
||||||
|
></hassio-card-content>
|
||||||
|
${!this.addon.available && addonStoreInfo
|
||||||
|
? !addonArchIsSupported(
|
||||||
|
this.supervisor.info.supported_arch,
|
||||||
|
this.addon.arch
|
||||||
|
)
|
||||||
|
? html`
|
||||||
|
<ha-alert alert-type="warning">
|
||||||
|
${this.supervisor.localize(
|
||||||
|
"addon.dashboard.not_available_arch"
|
||||||
|
)}
|
||||||
|
</ha-alert>
|
||||||
|
`
|
||||||
|
: html`
|
||||||
|
<ha-alert alert-type="warning">
|
||||||
|
${this.supervisor.localize(
|
||||||
|
"addon.dashboard.not_available_arch",
|
||||||
|
"core_version_installed",
|
||||||
|
this.supervisor.core.version,
|
||||||
|
"core_version_needed",
|
||||||
|
addonStoreInfo.homeassistant
|
||||||
|
)}
|
||||||
|
</ha-alert>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
|
</div>
|
||||||
|
<div class="card-actions">
|
||||||
|
${this.addon.changelog
|
||||||
|
? html`
|
||||||
|
<mwc-button @click=${this._openChangelog}>
|
||||||
|
${this.supervisor.localize("addon.dashboard.changelog")}
|
||||||
|
</mwc-button>
|
||||||
|
`
|
||||||
|
: html`<span></span>`}
|
||||||
|
<mwc-button @click=${this._updateClicked}>
|
||||||
|
${this.supervisor.localize("common.update")}
|
||||||
|
</mwc-button>
|
||||||
|
</div>
|
||||||
|
</ha-card>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
${!this.addon.protected
|
${!this.addon.protected
|
||||||
? html`
|
? html`
|
||||||
<ha-alert
|
<ha-card class="warning">
|
||||||
alert-type="error"
|
<h1 class="card-header">${this.supervisor.localize(
|
||||||
.title=${this.supervisor.localize(
|
"addon.dashboard.protection_mode.title"
|
||||||
"addon.dashboard.protection_mode.title"
|
)}
|
||||||
)}
|
</h1>
|
||||||
>
|
<div class="card-content">
|
||||||
|
${this.supervisor.localize("addon.dashboard.protection_mode.content")}
|
||||||
|
</div>
|
||||||
|
<div class="card-actions protection-enable">
|
||||||
|
<mwc-button @click=${this._protectionToggled}>
|
||||||
${this.supervisor.localize(
|
${this.supervisor.localize(
|
||||||
"addon.dashboard.protection_mode.content"
|
"addon.dashboard.protection_mode.enable"
|
||||||
)}
|
)}
|
||||||
<mwc-button
|
|
||||||
slot="action"
|
|
||||||
.label=${this.supervisor.localize(
|
|
||||||
"addon.dashboard.protection_mode.enable"
|
|
||||||
)}
|
|
||||||
@click=${this._protectionToggled}
|
|
||||||
>
|
|
||||||
</mwc-button>
|
</mwc-button>
|
||||||
</ha-alert>
|
</div>
|
||||||
`
|
</div>
|
||||||
|
</ha-card>
|
||||||
|
`
|
||||||
: ""}
|
: ""}
|
||||||
|
|
||||||
<ha-card>
|
<ha-card>
|
||||||
@@ -208,163 +249,6 @@ class HassioAddonInfo extends LitElement {
|
|||||||
>`}
|
>`}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="capabilities">
|
|
||||||
${this.addon.stage !== "stable"
|
|
||||||
? html` <ha-chip
|
|
||||||
hasIcon
|
|
||||||
class=${classMap({
|
|
||||||
yellow: this.addon.stage === "experimental",
|
|
||||||
red: this.addon.stage === "deprecated",
|
|
||||||
})}
|
|
||||||
@click=${this._showMoreInfo}
|
|
||||||
id="stage"
|
|
||||||
>
|
|
||||||
<ha-svg-icon
|
|
||||||
slot="icon"
|
|
||||||
.path=${STAGE_ICON[this.addon.stage]}
|
|
||||||
>
|
|
||||||
</ha-svg-icon>
|
|
||||||
${this.supervisor.localize(
|
|
||||||
`addon.dashboard.capability.stages.${this.addon.stage}`
|
|
||||||
)}
|
|
||||||
</ha-chip>`
|
|
||||||
: ""}
|
|
||||||
|
|
||||||
<ha-chip
|
|
||||||
hasIcon
|
|
||||||
class=${classMap({
|
|
||||||
green: [5, 6].includes(Number(this.addon.rating)),
|
|
||||||
yellow: [3, 4].includes(Number(this.addon.rating)),
|
|
||||||
red: [1, 2].includes(Number(this.addon.rating)),
|
|
||||||
})}
|
|
||||||
@click=${this._showMoreInfo}
|
|
||||||
id="rating"
|
|
||||||
>
|
|
||||||
<ha-svg-icon slot="icon" .path=${RATING_ICON[this.addon.rating]}>
|
|
||||||
</ha-svg-icon>
|
|
||||||
|
|
||||||
${this.supervisor.localize(
|
|
||||||
"addon.dashboard.capability.label.rating"
|
|
||||||
)}
|
|
||||||
</ha-chip>
|
|
||||||
${this.addon.host_network
|
|
||||||
? html`
|
|
||||||
<ha-chip
|
|
||||||
hasIcon
|
|
||||||
@click=${this._showMoreInfo}
|
|
||||||
id="host_network"
|
|
||||||
>
|
|
||||||
<ha-svg-icon slot="icon" .path=${mdiNetwork}> </ha-svg-icon>
|
|
||||||
${this.supervisor.localize(
|
|
||||||
"addon.dashboard.capability.label.host"
|
|
||||||
)}
|
|
||||||
</ha-chip>
|
|
||||||
`
|
|
||||||
: ""}
|
|
||||||
${this.addon.full_access
|
|
||||||
? html`
|
|
||||||
<ha-chip
|
|
||||||
hasIcon
|
|
||||||
@click=${this._showMoreInfo}
|
|
||||||
id="full_access"
|
|
||||||
>
|
|
||||||
<ha-svg-icon slot="icon" .path=${mdiChip}></ha-svg-icon>
|
|
||||||
${this.supervisor.localize(
|
|
||||||
"addon.dashboard.capability.label.hardware"
|
|
||||||
)}
|
|
||||||
</ha-chip>
|
|
||||||
`
|
|
||||||
: ""}
|
|
||||||
${this.addon.homeassistant_api
|
|
||||||
? html`
|
|
||||||
<ha-chip
|
|
||||||
hasIcon
|
|
||||||
@click=${this._showMoreInfo}
|
|
||||||
id="homeassistant_api"
|
|
||||||
>
|
|
||||||
<ha-svg-icon
|
|
||||||
slot="icon"
|
|
||||||
.path=${mdiHomeAssistant}
|
|
||||||
></ha-svg-icon>
|
|
||||||
${this.supervisor.localize(
|
|
||||||
"addon.dashboard.capability.label.core"
|
|
||||||
)}
|
|
||||||
</ha-chip>
|
|
||||||
`
|
|
||||||
: ""}
|
|
||||||
${this._computeHassioApi
|
|
||||||
? html`
|
|
||||||
<ha-chip hasIcon @click=${this._showMoreInfo} id="hassio_api">
|
|
||||||
<ha-svg-icon
|
|
||||||
slot="icon"
|
|
||||||
.path=${mdiHomeAssistant}
|
|
||||||
></ha-svg-icon>
|
|
||||||
${this.supervisor.localize(
|
|
||||||
`addon.dashboard.capability.role.${this.addon.hassio_role}`
|
|
||||||
) || this.addon.hassio_role}
|
|
||||||
</ha-chip>
|
|
||||||
`
|
|
||||||
: ""}
|
|
||||||
${this.addon.docker_api
|
|
||||||
? html`
|
|
||||||
<ha-chip hasIcon @click=${this._showMoreInfo} id="docker_api">
|
|
||||||
<ha-svg-icon slot="icon" .path=${mdiDocker}></ha-svg-icon>
|
|
||||||
${this.supervisor.localize(
|
|
||||||
"addon.dashboard.capability.label.docker"
|
|
||||||
)}
|
|
||||||
</ha-chip>
|
|
||||||
`
|
|
||||||
: ""}
|
|
||||||
${this.addon.host_pid
|
|
||||||
? html`
|
|
||||||
<ha-chip hasIcon @click=${this._showMoreInfo} id="host_pid">
|
|
||||||
<ha-svg-icon slot="icon" .path=${mdiPound}></ha-svg-icon>
|
|
||||||
${this.supervisor.localize(
|
|
||||||
"addon.dashboard.capability.label.host_pid"
|
|
||||||
)}
|
|
||||||
</ha-chip>
|
|
||||||
`
|
|
||||||
: ""}
|
|
||||||
${this.addon.apparmor !== "default"
|
|
||||||
? html`
|
|
||||||
<ha-chip
|
|
||||||
hasIcon
|
|
||||||
@click=${this._showMoreInfo}
|
|
||||||
class=${this._computeApparmorClassName}
|
|
||||||
id="apparmor"
|
|
||||||
>
|
|
||||||
<ha-svg-icon slot="icon" .path=${mdiShield}></ha-svg-icon>
|
|
||||||
${this.supervisor.localize(
|
|
||||||
"addon.dashboard.capability.label.apparmor"
|
|
||||||
)}
|
|
||||||
</ha-chip>
|
|
||||||
`
|
|
||||||
: ""}
|
|
||||||
${this.addon.auth_api
|
|
||||||
? html`
|
|
||||||
<ha-chip hasIcon @click=${this._showMoreInfo} id="auth_api">
|
|
||||||
<ha-svg-icon slot="icon" .path=${mdiKey}></ha-svg-icon>
|
|
||||||
${this.supervisor.localize(
|
|
||||||
"addon.dashboard.capability.label.auth"
|
|
||||||
)}
|
|
||||||
</ha-chip>
|
|
||||||
`
|
|
||||||
: ""}
|
|
||||||
${this.addon.ingress
|
|
||||||
? html`
|
|
||||||
<ha-chip hasIcon @click=${this._showMoreInfo} id="ingress">
|
|
||||||
<ha-svg-icon
|
|
||||||
slot="icon"
|
|
||||||
.path=${mdiCursorDefaultClickOutline}
|
|
||||||
></ha-svg-icon>
|
|
||||||
${this.supervisor.localize(
|
|
||||||
"addon.dashboard.capability.label.ingress"
|
|
||||||
)}
|
|
||||||
</ha-chip>
|
|
||||||
`
|
|
||||||
: ""}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="description light-color">
|
<div class="description light-color">
|
||||||
${this.addon.description}.<br />
|
${this.addon.description}.<br />
|
||||||
${this.supervisor.localize(
|
${this.supervisor.localize(
|
||||||
@@ -385,6 +269,171 @@ class HassioAddonInfo extends LitElement {
|
|||||||
/>
|
/>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
|
<div class="security">
|
||||||
|
${this.addon.stage !== "stable"
|
||||||
|
? html` <ha-label-badge
|
||||||
|
class=${classMap({
|
||||||
|
yellow: this.addon.stage === "experimental",
|
||||||
|
red: this.addon.stage === "deprecated",
|
||||||
|
})}
|
||||||
|
@click=${this._showMoreInfo}
|
||||||
|
id="stage"
|
||||||
|
.label=${this.supervisor.localize(
|
||||||
|
"addon.dashboard.capability.label.stage"
|
||||||
|
)}
|
||||||
|
description=""
|
||||||
|
>
|
||||||
|
<ha-svg-icon
|
||||||
|
.path=${STAGE_ICON[this.addon.stage]}
|
||||||
|
></ha-svg-icon>
|
||||||
|
</ha-label-badge>`
|
||||||
|
: ""}
|
||||||
|
|
||||||
|
<ha-label-badge
|
||||||
|
class=${classMap({
|
||||||
|
green: [5, 6].includes(Number(this.addon.rating)),
|
||||||
|
yellow: [3, 4].includes(Number(this.addon.rating)),
|
||||||
|
red: [1, 2].includes(Number(this.addon.rating)),
|
||||||
|
})}
|
||||||
|
@click=${this._showMoreInfo}
|
||||||
|
id="rating"
|
||||||
|
.value=${this.addon.rating}
|
||||||
|
label="rating"
|
||||||
|
description=""
|
||||||
|
></ha-label-badge>
|
||||||
|
${this.addon.host_network
|
||||||
|
? html`
|
||||||
|
<ha-label-badge
|
||||||
|
@click=${this._showMoreInfo}
|
||||||
|
id="host_network"
|
||||||
|
.label=${this.supervisor.localize(
|
||||||
|
"addon.dashboard.capability.label.host"
|
||||||
|
)}
|
||||||
|
description=""
|
||||||
|
>
|
||||||
|
<ha-svg-icon .path=${mdiNetwork}></ha-svg-icon>
|
||||||
|
</ha-label-badge>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
|
${this.addon.full_access
|
||||||
|
? html`
|
||||||
|
<ha-label-badge
|
||||||
|
@click=${this._showMoreInfo}
|
||||||
|
id="full_access"
|
||||||
|
.label=${this.supervisor.localize(
|
||||||
|
"addon.dashboard.capability.label.hardware"
|
||||||
|
)}
|
||||||
|
description=""
|
||||||
|
>
|
||||||
|
<ha-svg-icon .path=${mdiChip}></ha-svg-icon>
|
||||||
|
</ha-label-badge>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
|
${this.addon.homeassistant_api
|
||||||
|
? html`
|
||||||
|
<ha-label-badge
|
||||||
|
@click=${this._showMoreInfo}
|
||||||
|
id="homeassistant_api"
|
||||||
|
.label=${this.supervisor.localize(
|
||||||
|
"addon.dashboard.capability.label.hass"
|
||||||
|
)}
|
||||||
|
description=""
|
||||||
|
>
|
||||||
|
<ha-svg-icon .path=${mdiHomeAssistant}></ha-svg-icon>
|
||||||
|
</ha-label-badge>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
|
${this._computeHassioApi
|
||||||
|
? html`
|
||||||
|
<ha-label-badge
|
||||||
|
@click=${this._showMoreInfo}
|
||||||
|
id="hassio_api"
|
||||||
|
.label=${this.supervisor.localize(
|
||||||
|
"addon.dashboard.capability.label.hassio"
|
||||||
|
)}
|
||||||
|
.description=${this.supervisor.localize(
|
||||||
|
`addon.dashboard.capability.role.${this.addon.hassio_role}`
|
||||||
|
) || this.addon.hassio_role}
|
||||||
|
>
|
||||||
|
<ha-svg-icon .path=${mdiHomeAssistant}></ha-svg-icon>
|
||||||
|
</ha-label-badge>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
|
${this.addon.docker_api
|
||||||
|
? html`
|
||||||
|
<ha-label-badge
|
||||||
|
@click=${this._showMoreInfo}
|
||||||
|
id="docker_api"
|
||||||
|
.label=".${this.supervisor.localize(
|
||||||
|
"addon.dashboard.capability.label.docker"
|
||||||
|
)}"
|
||||||
|
description=""
|
||||||
|
>
|
||||||
|
<ha-svg-icon .path=${mdiDocker}></ha-svg-icon>
|
||||||
|
</ha-label-badge>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
|
${this.addon.host_pid
|
||||||
|
? html`
|
||||||
|
<ha-label-badge
|
||||||
|
@click=${this._showMoreInfo}
|
||||||
|
id="host_pid"
|
||||||
|
.label=${this.supervisor.localize(
|
||||||
|
"addon.dashboard.capability.label.host_pid"
|
||||||
|
)}
|
||||||
|
description=""
|
||||||
|
>
|
||||||
|
<ha-svg-icon .path=${mdiPound}></ha-svg-icon>
|
||||||
|
</ha-label-badge>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
|
${this.addon.apparmor
|
||||||
|
? html`
|
||||||
|
<ha-label-badge
|
||||||
|
@click=${this._showMoreInfo}
|
||||||
|
class=${this._computeApparmorClassName}
|
||||||
|
id="apparmor"
|
||||||
|
.label=${this.supervisor.localize(
|
||||||
|
"addon.dashboard.capability.label.apparmor"
|
||||||
|
)}
|
||||||
|
description=""
|
||||||
|
>
|
||||||
|
<ha-svg-icon .path=${mdiShield}></ha-svg-icon>
|
||||||
|
</ha-label-badge>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
|
${this.addon.auth_api
|
||||||
|
? html`
|
||||||
|
<ha-label-badge
|
||||||
|
@click=${this._showMoreInfo}
|
||||||
|
id="auth_api"
|
||||||
|
.label=${this.supervisor.localize(
|
||||||
|
"addon.dashboard.capability.label.auth"
|
||||||
|
)}
|
||||||
|
description=""
|
||||||
|
>
|
||||||
|
<ha-svg-icon .path=${mdiKey}></ha-svg-icon>
|
||||||
|
</ha-label-badge>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
|
${this.addon.ingress
|
||||||
|
? html`
|
||||||
|
<ha-label-badge
|
||||||
|
@click=${this._showMoreInfo}
|
||||||
|
id="ingress"
|
||||||
|
.label=${this.supervisor.localize(
|
||||||
|
"addon.dashboard.capability.label.ingress"
|
||||||
|
)}
|
||||||
|
description=""
|
||||||
|
>
|
||||||
|
<ha-svg-icon
|
||||||
|
.path=${mdiCursorDefaultClickOutline}
|
||||||
|
></ha-svg-icon>
|
||||||
|
</ha-label-badge>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
|
</div>
|
||||||
|
|
||||||
${this.addon.version
|
${this.addon.version
|
||||||
? html`
|
? html`
|
||||||
<div
|
<div
|
||||||
@@ -846,14 +895,22 @@ class HassioAddonInfo extends LitElement {
|
|||||||
|
|
||||||
private async _openChangelog(): Promise<void> {
|
private async _openChangelog(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const content = await fetchHassioAddonChangelog(
|
let content = await fetchHassioAddonChangelog(this.hass, this.addon.slug);
|
||||||
this.hass,
|
if (
|
||||||
this.addon.slug
|
content.includes(`# ${this.addon.version}`) &&
|
||||||
);
|
content.includes(`# ${this.addon.version_latest}`)
|
||||||
|
) {
|
||||||
|
const newcontent = content.split(`# ${this.addon.version}`)[0];
|
||||||
|
if (newcontent.includes(`# ${this.addon.version_latest}`)) {
|
||||||
|
// Only change the content if the new version still exist
|
||||||
|
// if the changelog does not have the newests version on top
|
||||||
|
// this will not be true, and we don't modify the content
|
||||||
|
content = newcontent;
|
||||||
|
}
|
||||||
|
}
|
||||||
showHassioMarkdownDialog(this, {
|
showHassioMarkdownDialog(this, {
|
||||||
title: this.supervisor.localize("addon.dashboard.changelog"),
|
title: this.supervisor.localize("addon.dashboard.changelog"),
|
||||||
content: extractChangelog(this.addon, content),
|
content,
|
||||||
});
|
});
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
showAlertDialog(this, {
|
showAlertDialog(this, {
|
||||||
@@ -928,6 +985,33 @@ class HassioAddonInfo extends LitElement {
|
|||||||
button.progress = false;
|
button.progress = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async _updateClicked(): Promise<void> {
|
||||||
|
showDialogSupervisorUpdate(this, {
|
||||||
|
supervisor: this.supervisor,
|
||||||
|
name: this.addon.name,
|
||||||
|
version: this.addon.version_latest,
|
||||||
|
backupParams: {
|
||||||
|
name: `addon_${this.addon.slug}_${this.addon.version}`,
|
||||||
|
addons: [this.addon.slug],
|
||||||
|
homeassistant: false,
|
||||||
|
},
|
||||||
|
updateHandler: async () => this._updateAddon(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _updateAddon(): Promise<void> {
|
||||||
|
await updateHassioAddon(this.hass, this.addon.slug);
|
||||||
|
fireEvent(this, "supervisor-collection-refresh", {
|
||||||
|
collection: "addon",
|
||||||
|
});
|
||||||
|
const eventdata = {
|
||||||
|
success: true,
|
||||||
|
response: undefined,
|
||||||
|
path: "update",
|
||||||
|
};
|
||||||
|
fireEvent(this, "hass-api-called", eventdata);
|
||||||
|
}
|
||||||
|
|
||||||
private async _startClicked(ev: CustomEvent): Promise<void> {
|
private async _startClicked(ev: CustomEvent): Promise<void> {
|
||||||
const button = ev.currentTarget as any;
|
const button = ev.currentTarget as any;
|
||||||
button.progress = true;
|
button.progress = true;
|
||||||
@@ -1093,31 +1177,34 @@ class HassioAddonInfo extends LitElement {
|
|||||||
.description a {
|
.description a {
|
||||||
color: var(--primary-color);
|
color: var(--primary-color);
|
||||||
}
|
}
|
||||||
ha-chip {
|
|
||||||
text-transform: capitalize;
|
|
||||||
--ha-chip-text-color: var(--text-primary-color);
|
|
||||||
--ha-chip-background-color: var(--primary-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.red {
|
.red {
|
||||||
--ha-chip-background-color: var(--label-badge-red, #df4c1e);
|
--ha-label-badge-color: var(--label-badge-red, #df4c1e);
|
||||||
}
|
}
|
||||||
.blue {
|
.blue {
|
||||||
--ha-chip-background-color: var(--label-badge-blue, #039be5);
|
--ha-label-badge-color: var(--label-badge-blue, #039be5);
|
||||||
}
|
}
|
||||||
.green {
|
.green {
|
||||||
--ha-chip-background-color: var(--label-badge-green, #0da035);
|
--ha-label-badge-color: var(--label-badge-green, #0da035);
|
||||||
}
|
}
|
||||||
.yellow {
|
.yellow {
|
||||||
--ha-chip-background-color: var(--label-badge-yellow, #f4b400);
|
--ha-label-badge-color: var(--label-badge-yellow, #f4b400);
|
||||||
}
|
}
|
||||||
.capabilities {
|
.security {
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
}
|
}
|
||||||
.card-actions {
|
.card-actions {
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
.security h3 {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
.security ha-label-badge {
|
||||||
|
cursor: pointer;
|
||||||
|
margin-right: 4px;
|
||||||
|
--ha-label-badge-padding: 8px 0 0 0;
|
||||||
|
}
|
||||||
.changelog {
|
.changelog {
|
||||||
display: contents;
|
display: contents;
|
||||||
}
|
}
|
||||||
@@ -1156,21 +1243,7 @@ class HassioAddonInfo extends LitElement {
|
|||||||
align-self: end;
|
align-self: end;
|
||||||
}
|
}
|
||||||
|
|
||||||
ha-alert mwc-button {
|
|
||||||
--mdc-theme-primary: var(--primary-text-color);
|
|
||||||
}
|
|
||||||
a {
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
update-available-card {
|
|
||||||
padding-bottom: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 720px) {
|
@media (max-width: 720px) {
|
||||||
ha-chip {
|
|
||||||
line-height: 36px;
|
|
||||||
}
|
|
||||||
.addon-options {
|
.addon-options {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
}
|
}
|
||||||
|
@@ -23,8 +23,7 @@ import {
|
|||||||
} from "../../../src/components/data-table/ha-data-table";
|
} from "../../../src/components/data-table/ha-data-table";
|
||||||
import "../../../src/components/ha-button-menu";
|
import "../../../src/components/ha-button-menu";
|
||||||
import "../../../src/components/ha-fab";
|
import "../../../src/components/ha-fab";
|
||||||
import "../../../src/components/ha-icon-button";
|
import { extractApiErrorMessage } from "../../../src/data/hassio/common";
|
||||||
import "../../../src/components/ha-svg-icon";
|
|
||||||
import {
|
import {
|
||||||
fetchHassioBackups,
|
fetchHassioBackups,
|
||||||
friendlyFolderName,
|
friendlyFolderName,
|
||||||
@@ -32,7 +31,6 @@ import {
|
|||||||
reloadHassioBackups,
|
reloadHassioBackups,
|
||||||
removeBackup,
|
removeBackup,
|
||||||
} from "../../../src/data/hassio/backup";
|
} from "../../../src/data/hassio/backup";
|
||||||
import { extractApiErrorMessage } from "../../../src/data/hassio/common";
|
|
||||||
import { Supervisor } from "../../../src/data/supervisor/supervisor";
|
import { Supervisor } from "../../../src/data/supervisor/supervisor";
|
||||||
import {
|
import {
|
||||||
showAlertDialog,
|
showAlertDialog,
|
||||||
@@ -42,9 +40,9 @@ import "../../../src/layouts/hass-tabs-subpage-data-table";
|
|||||||
import type { HaTabsSubpageDataTable } from "../../../src/layouts/hass-tabs-subpage-data-table";
|
import type { HaTabsSubpageDataTable } from "../../../src/layouts/hass-tabs-subpage-data-table";
|
||||||
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 { showBackupUploadDialog } from "../dialogs/backup/show-dialog-backup-upload";
|
|
||||||
import { showHassioBackupDialog } from "../dialogs/backup/show-dialog-hassio-backup";
|
|
||||||
import { showHassioCreateBackupDialog } from "../dialogs/backup/show-dialog-hassio-create-backup";
|
import { showHassioCreateBackupDialog } from "../dialogs/backup/show-dialog-hassio-create-backup";
|
||||||
|
import { showHassioBackupDialog } from "../dialogs/backup/show-dialog-hassio-backup";
|
||||||
|
import { showBackupUploadDialog } from "../dialogs/backup/show-dialog-backup-upload";
|
||||||
import { supervisorTabs } from "../hassio-tabs";
|
import { supervisorTabs } from "../hassio-tabs";
|
||||||
import { hassioStyle } from "../resources/hassio-style";
|
import { hassioStyle } from "../resources/hassio-style";
|
||||||
|
|
||||||
@@ -158,7 +156,7 @@ export class HassioBackups extends LitElement {
|
|||||||
}
|
}
|
||||||
return html`
|
return html`
|
||||||
<hass-tabs-subpage-data-table
|
<hass-tabs-subpage-data-table
|
||||||
.tabs=${supervisorTabs(this.hass)}
|
.tabs=${supervisorTabs}
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.localizeFunc=${this.supervisor.localize}
|
.localizeFunc=${this.supervisor.localize}
|
||||||
.searchLabel=${this.supervisor.localize("search")}
|
.searchLabel=${this.supervisor.localize("search")}
|
||||||
@@ -181,11 +179,9 @@ export class HassioBackups extends LitElement {
|
|||||||
slot="toolbar-icon"
|
slot="toolbar-icon"
|
||||||
@action=${this._handleAction}
|
@action=${this._handleAction}
|
||||||
>
|
>
|
||||||
<ha-icon-button
|
<mwc-icon-button slot="trigger" alt="menu">
|
||||||
.label=${this.hass.localize("common.menu")}
|
<ha-svg-icon .path=${mdiDotsVertical}></ha-svg-icon>
|
||||||
.path=${mdiDotsVertical}
|
</mwc-icon-button>
|
||||||
slot="trigger"
|
|
||||||
></ha-icon-button>
|
|
||||||
<mwc-list-item>
|
<mwc-list-item>
|
||||||
${this.supervisor?.localize("common.reload")}
|
${this.supervisor?.localize("common.reload")}
|
||||||
</mwc-list-item>
|
</mwc-list-item>
|
||||||
@@ -220,15 +216,13 @@ export class HassioBackups extends LitElement {
|
|||||||
</mwc-button>
|
</mwc-button>
|
||||||
`
|
`
|
||||||
: html`
|
: html`
|
||||||
<ha-icon-button
|
<mwc-icon-button
|
||||||
.label=${this.supervisor.localize(
|
|
||||||
"snapshot.delete_selected"
|
|
||||||
)}
|
|
||||||
.path=${mdiDelete}
|
|
||||||
id="delete-btn"
|
id="delete-btn"
|
||||||
class="warning"
|
class="warning"
|
||||||
@click=${this._deleteSelected}
|
@click=${this._deleteSelected}
|
||||||
></ha-icon-button>
|
>
|
||||||
|
<ha-svg-icon .path=${mdiDelete}></ha-svg-icon>
|
||||||
|
</mwc-icon-button>
|
||||||
<paper-tooltip animation-delay="0" for="delete-btn">
|
<paper-tooltip animation-delay="0" for="delete-btn">
|
||||||
${this.supervisor.localize("backup.delete_selected")}
|
${this.supervisor.localize("backup.delete_selected")}
|
||||||
</paper-tooltip>
|
</paper-tooltip>
|
||||||
@@ -374,7 +368,7 @@ export class HassioBackups extends LitElement {
|
|||||||
margin-right: -12px;
|
margin-right: -12px;
|
||||||
}
|
}
|
||||||
.header-btns > mwc-button,
|
.header-btns > mwc-button,
|
||||||
.header-btns > ha-icon-button {
|
.header-btns > mwc-icon-button {
|
||||||
margin: 8px;
|
margin: 8px;
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
import { mdiHelpCircle } from "@mdi/js";
|
import { mdiHelpCircle } from "@mdi/js";
|
||||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||||
import { customElement, property } from "lit/decorators";
|
import { customElement, property } from "lit/decorators";
|
||||||
|
import "../../../src/components/ha-relative-time";
|
||||||
import "../../../src/components/ha-svg-icon";
|
import "../../../src/components/ha-svg-icon";
|
||||||
import { HomeAssistant } from "../../../src/types";
|
import { HomeAssistant } from "../../../src/types";
|
||||||
|
|
||||||
@@ -18,6 +19,8 @@ class HassioCardContent extends LitElement {
|
|||||||
|
|
||||||
@property() public topbarClass?: string;
|
@property() public topbarClass?: string;
|
||||||
|
|
||||||
|
@property() public datetime?: string;
|
||||||
|
|
||||||
@property() public iconTitle?: string;
|
@property() public iconTitle?: string;
|
||||||
|
|
||||||
@property() public iconClass?: string;
|
@property() public iconClass?: string;
|
||||||
@@ -53,6 +56,15 @@ class HassioCardContent extends LitElement {
|
|||||||
/* treat as available when undefined */
|
/* treat as available when undefined */
|
||||||
this.available === false ? " (Not available)" : ""
|
this.available === false ? " (Not available)" : ""
|
||||||
}
|
}
|
||||||
|
${this.datetime
|
||||||
|
? html`
|
||||||
|
<ha-relative-time
|
||||||
|
.hass=${this.hass}
|
||||||
|
class="addition"
|
||||||
|
.datetime=${this.datetime}
|
||||||
|
></ha-relative-time>
|
||||||
|
`
|
||||||
|
: undefined}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
@@ -94,6 +106,9 @@ class HassioCardContent extends LitElement {
|
|||||||
height: 2.4em;
|
height: 2.4em;
|
||||||
line-height: 1.2em;
|
line-height: 1.2em;
|
||||||
}
|
}
|
||||||
|
ha-relative-time {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
.icon_image img {
|
.icon_image img {
|
||||||
max-height: 40px;
|
max-height: 40px;
|
||||||
max-width: 40px;
|
max-width: 40px;
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
import "@material/mwc-icon-button/mwc-icon-button";
|
||||||
import { mdiFolderUpload } from "@mdi/js";
|
import { mdiFolderUpload } from "@mdi/js";
|
||||||
import "@polymer/paper-input/paper-input-container";
|
import "@polymer/paper-input/paper-input-container";
|
||||||
import { html, LitElement, TemplateResult } from "lit";
|
import { html, LitElement, TemplateResult } from "lit";
|
||||||
@@ -5,8 +6,9 @@ import { customElement, state } from "lit/decorators";
|
|||||||
import { fireEvent } from "../../../src/common/dom/fire_event";
|
import { fireEvent } from "../../../src/common/dom/fire_event";
|
||||||
import "../../../src/components/ha-circular-progress";
|
import "../../../src/components/ha-circular-progress";
|
||||||
import "../../../src/components/ha-file-upload";
|
import "../../../src/components/ha-file-upload";
|
||||||
import { HassioBackup, uploadBackup } from "../../../src/data/hassio/backup";
|
import "../../../src/components/ha-svg-icon";
|
||||||
import { extractApiErrorMessage } from "../../../src/data/hassio/common";
|
import { extractApiErrorMessage } from "../../../src/data/hassio/common";
|
||||||
|
import { HassioBackup, uploadBackup } from "../../../src/data/hassio/backup";
|
||||||
import { showAlertDialog } from "../../../src/dialogs/generic/show-dialog-box";
|
import { showAlertDialog } from "../../../src/dialogs/generic/show-dialog-box";
|
||||||
import { HomeAssistant } from "../../../src/types";
|
import { HomeAssistant } from "../../../src/types";
|
||||||
|
|
||||||
@@ -16,9 +18,11 @@ declare global {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const MAX_FILE_SIZE = 1 * 1024 * 1024 * 1024; // 1GB
|
||||||
|
|
||||||
@customElement("hassio-upload-backup")
|
@customElement("hassio-upload-backup")
|
||||||
export class HassioUploadBackup extends LitElement {
|
export class HassioUploadBackup extends LitElement {
|
||||||
public hass?: HomeAssistant;
|
public hass!: HomeAssistant;
|
||||||
|
|
||||||
@state() public value: string | null = null;
|
@state() public value: string | null = null;
|
||||||
|
|
||||||
@@ -27,7 +31,6 @@ export class HassioUploadBackup extends LitElement {
|
|||||||
public render(): TemplateResult {
|
public render(): TemplateResult {
|
||||||
return html`
|
return html`
|
||||||
<ha-file-upload
|
<ha-file-upload
|
||||||
.hass=${this.hass}
|
|
||||||
.uploading=${this._uploading}
|
.uploading=${this._uploading}
|
||||||
.icon=${mdiFolderUpload}
|
.icon=${mdiFolderUpload}
|
||||||
accept="application/x-tar"
|
accept="application/x-tar"
|
||||||
@@ -41,6 +44,20 @@ export class HassioUploadBackup extends LitElement {
|
|||||||
private async _uploadFile(ev) {
|
private async _uploadFile(ev) {
|
||||||
const file = ev.detail.files[0];
|
const file = ev.detail.files[0];
|
||||||
|
|
||||||
|
if (file.size > MAX_FILE_SIZE) {
|
||||||
|
showAlertDialog(this, {
|
||||||
|
title: "Backup file is too big",
|
||||||
|
text: html`The maximum allowed filesize is 1GB.<br />
|
||||||
|
<a
|
||||||
|
href="https://www.home-assistant.io/hassio/haos_common_tasks/#restoring-a-backup-on-a-new-install"
|
||||||
|
target="_blank"
|
||||||
|
>Have a look here on how to restore it.</a
|
||||||
|
>`,
|
||||||
|
confirmText: "ok",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!["application/x-tar"].includes(file.type)) {
|
if (!["application/x-tar"].includes(file.type)) {
|
||||||
showAlertDialog(this, {
|
showAlertDialog(this, {
|
||||||
title: "Unsupported file format",
|
title: "Unsupported file format",
|
||||||
|
@@ -3,7 +3,7 @@ import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
|||||||
import { customElement, property } from "lit/decorators";
|
import { customElement, property } from "lit/decorators";
|
||||||
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 { caseInsensitiveStringCompare } from "../../../src/common/string/compare";
|
import { stringCompare } from "../../../src/common/string/compare";
|
||||||
import "../../../src/components/ha-card";
|
import "../../../src/components/ha-card";
|
||||||
import { Supervisor } from "../../../src/data/supervisor/supervisor";
|
import { Supervisor } from "../../../src/data/supervisor/supervisor";
|
||||||
import { haStyle } from "../../../src/resources/styles";
|
import { haStyle } from "../../../src/resources/styles";
|
||||||
@@ -20,9 +20,7 @@ class HassioAddons extends LitElement {
|
|||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
return html`
|
return html`
|
||||||
<div class="content">
|
<div class="content">
|
||||||
${!atLeastVersion(this.hass.config.version, 2021, 12)
|
<h1>${this.supervisor.localize("dashboard.addons")}</h1>
|
||||||
? html` <h1>${this.supervisor.localize("dashboard.addons")}</h1> `
|
|
||||||
: ""}
|
|
||||||
<div class="card-group">
|
<div class="card-group">
|
||||||
${!this.supervisor.supervisor.addons?.length
|
${!this.supervisor.supervisor.addons?.length
|
||||||
? html`
|
? html`
|
||||||
@@ -35,7 +33,7 @@ class HassioAddons extends LitElement {
|
|||||||
</ha-card>
|
</ha-card>
|
||||||
`
|
`
|
||||||
: this.supervisor.supervisor.addons
|
: this.supervisor.supervisor.addons
|
||||||
.sort((a, b) => caseInsensitiveStringCompare(a.name, b.name))
|
.sort((a, b) => stringCompare(a.name, b.name))
|
||||||
.map(
|
.map(
|
||||||
(addon) => html`
|
(addon) => html`
|
||||||
<ha-card .addon=${addon} @click=${this._addonTapped}>
|
<ha-card .addon=${addon} @click=${this._addonTapped}>
|
||||||
|
@@ -1,8 +1,5 @@
|
|||||||
import { mdiStorePlus } from "@mdi/js";
|
|
||||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||||
import { customElement, property } from "lit/decorators";
|
import { customElement, property } from "lit/decorators";
|
||||||
import { atLeastVersion } from "../../../src/common/config/version";
|
|
||||||
import "../../../src/components/ha-fab";
|
|
||||||
import { Supervisor } from "../../../src/data/supervisor/supervisor";
|
import { Supervisor } from "../../../src/data/supervisor/supervisor";
|
||||||
import "../../../src/layouts/hass-tabs-subpage";
|
import "../../../src/layouts/hass-tabs-subpage";
|
||||||
import { haStyle } from "../../../src/resources/styles";
|
import { haStyle } from "../../../src/resources/styles";
|
||||||
@@ -28,37 +25,23 @@ class HassioDashboard extends LitElement {
|
|||||||
.localizeFunc=${this.supervisor.localize}
|
.localizeFunc=${this.supervisor.localize}
|
||||||
.narrow=${this.narrow}
|
.narrow=${this.narrow}
|
||||||
.route=${this.route}
|
.route=${this.route}
|
||||||
.tabs=${supervisorTabs(this.hass)}
|
.tabs=${supervisorTabs}
|
||||||
main-page
|
main-page
|
||||||
supervisor
|
supervisor
|
||||||
hasFab
|
|
||||||
>
|
>
|
||||||
<span slot="header">
|
<span slot="header">
|
||||||
${this.supervisor.localize("panel.dashboard")}
|
${this.supervisor.localize("panel.dashboard")}
|
||||||
</span>
|
</span>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
${this.hass.config.version.includes("dev") ||
|
<hassio-update
|
||||||
!atLeastVersion(this.hass.config.version, 2021, 12)
|
.hass=${this.hass}
|
||||||
? html`
|
.supervisor=${this.supervisor}
|
||||||
<hassio-update
|
></hassio-update>
|
||||||
.hass=${this.hass}
|
|
||||||
.supervisor=${this.supervisor}
|
|
||||||
></hassio-update>
|
|
||||||
`
|
|
||||||
: ""}
|
|
||||||
<hassio-addons
|
<hassio-addons
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.supervisor=${this.supervisor}
|
.supervisor=${this.supervisor}
|
||||||
></hassio-addons>
|
></hassio-addons>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<a href="/hassio/store" slot="fab">
|
|
||||||
<ha-fab .label=${this.supervisor.localize("panel.store")} extended>
|
|
||||||
<ha-svg-icon
|
|
||||||
slot="icon"
|
|
||||||
.path=${mdiStorePlus}
|
|
||||||
></ha-svg-icon> </ha-fab
|
|
||||||
></a>
|
|
||||||
</hass-tabs-subpage>
|
</hass-tabs-subpage>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
@@ -3,18 +3,34 @@ import { mdiHomeAssistant } from "@mdi/js";
|
|||||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||||
import { customElement, property } from "lit/decorators";
|
import { customElement, property } from "lit/decorators";
|
||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
|
import { atLeastVersion } from "../../../src/common/config/version";
|
||||||
|
import { fireEvent } from "../../../src/common/dom/fire_event";
|
||||||
import "../../../src/components/buttons/ha-progress-button";
|
import "../../../src/components/buttons/ha-progress-button";
|
||||||
import "../../../src/components/ha-card";
|
import "../../../src/components/ha-card";
|
||||||
import "../../../src/components/ha-settings-row";
|
import "../../../src/components/ha-settings-row";
|
||||||
import "../../../src/components/ha-svg-icon";
|
import "../../../src/components/ha-svg-icon";
|
||||||
|
import {
|
||||||
|
extractApiErrorMessage,
|
||||||
|
HassioResponse,
|
||||||
|
ignoreSupervisorError,
|
||||||
|
} from "../../../src/data/hassio/common";
|
||||||
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 { Supervisor } from "../../../src/data/supervisor/supervisor";
|
import { updateCore } from "../../../src/data/supervisor/core";
|
||||||
|
import {
|
||||||
|
Supervisor,
|
||||||
|
supervisorApiWsRequest,
|
||||||
|
} from "../../../src/data/supervisor/supervisor";
|
||||||
|
import {
|
||||||
|
showAlertDialog,
|
||||||
|
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 { showDialogSupervisorUpdate } from "../dialogs/update/show-dialog-update";
|
||||||
import { hassioStyle } from "../resources/hassio-style";
|
import { hassioStyle } from "../resources/hassio-style";
|
||||||
|
|
||||||
const computeVersion = (key: string, version: string): string =>
|
const computeVersion = (key: string, version: string): string =>
|
||||||
@@ -57,18 +73,26 @@ export class HassioUpdate extends LitElement {
|
|||||||
${this._renderUpdateCard(
|
${this._renderUpdateCard(
|
||||||
"Home Assistant Core",
|
"Home Assistant Core",
|
||||||
"core",
|
"core",
|
||||||
this.supervisor.core
|
this.supervisor.core,
|
||||||
|
"hassio/homeassistant/update",
|
||||||
|
`https://${
|
||||||
|
this.supervisor.core.version_latest.includes("b") ? "rc" : "www"
|
||||||
|
}.home-assistant.io/latest-release-notes/`
|
||||||
)}
|
)}
|
||||||
${this._renderUpdateCard(
|
${this._renderUpdateCard(
|
||||||
"Supervisor",
|
"Supervisor",
|
||||||
"supervisor",
|
"supervisor",
|
||||||
this.supervisor.supervisor
|
this.supervisor.supervisor,
|
||||||
|
"hassio/supervisor/update",
|
||||||
|
`https://github.com//home-assistant/hassio/releases/tag/${this.supervisor.supervisor.version_latest}`
|
||||||
)}
|
)}
|
||||||
${this.supervisor.host.features.includes("haos")
|
${this.supervisor.host.features.includes("haos")
|
||||||
? this._renderUpdateCard(
|
? this._renderUpdateCard(
|
||||||
"Operating System",
|
"Operating System",
|
||||||
"os",
|
"os",
|
||||||
this.supervisor.os
|
this.supervisor.os,
|
||||||
|
"hassio/os/update",
|
||||||
|
`https://github.com//home-assistant/hassos/releases/tag/${this.supervisor.os.version_latest}`
|
||||||
)
|
)
|
||||||
: ""}
|
: ""}
|
||||||
</div>
|
</div>
|
||||||
@@ -79,7 +103,9 @@ export class HassioUpdate extends LitElement {
|
|||||||
private _renderUpdateCard(
|
private _renderUpdateCard(
|
||||||
name: string,
|
name: string,
|
||||||
key: string,
|
key: string,
|
||||||
object: HassioHomeAssistantInfo | HassioSupervisorInfo | HassioHassOSInfo
|
object: HassioHomeAssistantInfo | HassioSupervisorInfo | HassioHassOSInfo,
|
||||||
|
apiPath: string,
|
||||||
|
releaseNotesUrl: string
|
||||||
): TemplateResult {
|
): TemplateResult {
|
||||||
if (!object.update_available) {
|
if (!object.update_available) {
|
||||||
return html``;
|
return html``;
|
||||||
@@ -110,15 +136,96 @@ export class HassioUpdate extends LitElement {
|
|||||||
</ha-settings-row>
|
</ha-settings-row>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-actions">
|
<div class="card-actions">
|
||||||
<a href="/hassio/update-available/${key}">
|
<a href=${releaseNotesUrl} target="_blank" rel="noreferrer">
|
||||||
<mwc-button .label=${this.supervisor.localize("common.show")}>
|
<mwc-button>
|
||||||
|
${this.supervisor.localize("common.release_notes")}
|
||||||
</mwc-button>
|
</mwc-button>
|
||||||
</a>
|
</a>
|
||||||
|
<ha-progress-button
|
||||||
|
.apiPath=${apiPath}
|
||||||
|
.name=${name}
|
||||||
|
.key=${key}
|
||||||
|
.version=${object.version_latest}
|
||||||
|
@click=${this._confirmUpdate}
|
||||||
|
>
|
||||||
|
${this.supervisor.localize("common.update")}
|
||||||
|
</ha-progress-button>
|
||||||
</div>
|
</div>
|
||||||
</ha-card>
|
</ha-card>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async _confirmUpdate(ev): Promise<void> {
|
||||||
|
const item = ev.currentTarget;
|
||||||
|
if (item.key === "core") {
|
||||||
|
showDialogSupervisorUpdate(this, {
|
||||||
|
supervisor: this.supervisor,
|
||||||
|
name: "Home Assistant Core",
|
||||||
|
version: this.supervisor.core.version_latest,
|
||||||
|
backupParams: {
|
||||||
|
name: `core_${this.supervisor.core.version}`,
|
||||||
|
folders: ["homeassistant"],
|
||||||
|
homeassistant: true,
|
||||||
|
},
|
||||||
|
updateHandler: async () => this._updateCore(),
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
item.progress = true;
|
||||||
|
const confirmed = await showConfirmationDialog(this, {
|
||||||
|
title: this.supervisor.localize(
|
||||||
|
"confirm.update.title",
|
||||||
|
"name",
|
||||||
|
item.name
|
||||||
|
),
|
||||||
|
text: this.supervisor.localize(
|
||||||
|
"confirm.update.text",
|
||||||
|
"name",
|
||||||
|
item.name,
|
||||||
|
"version",
|
||||||
|
computeVersion(item.key, item.version)
|
||||||
|
),
|
||||||
|
confirmText: this.supervisor.localize("common.update"),
|
||||||
|
dismissText: this.supervisor.localize("common.cancel"),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!confirmed) {
|
||||||
|
item.progress = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
if (atLeastVersion(this.hass.config.version, 2021, 2, 4)) {
|
||||||
|
await supervisorApiWsRequest(this.hass.connection, {
|
||||||
|
method: "post",
|
||||||
|
endpoint: item.apiPath.replace("hassio", ""),
|
||||||
|
timeout: null,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
await this.hass.callApi<HassioResponse<void>>("POST", item.apiPath);
|
||||||
|
}
|
||||||
|
fireEvent(this, "supervisor-collection-refresh", {
|
||||||
|
collection: item.key,
|
||||||
|
});
|
||||||
|
} catch (err: any) {
|
||||||
|
// Only show an error if the status code was not expected (user behind proxy)
|
||||||
|
// or no status at all(connection terminated)
|
||||||
|
if (this.hass.connection.connected && !ignoreSupervisorError(err)) {
|
||||||
|
showAlertDialog(this, {
|
||||||
|
title: this.supervisor.localize("common.error.update_failed"),
|
||||||
|
text: extractApiErrorMessage(err),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
item.progress = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _updateCore(): Promise<void> {
|
||||||
|
await updateCore(this.hass);
|
||||||
|
fireEvent(this, "supervisor-collection-refresh", {
|
||||||
|
collection: "core",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
static get styles(): CSSResultGroup {
|
||||||
return [
|
return [
|
||||||
haStyle,
|
haStyle,
|
||||||
|
@@ -3,7 +3,6 @@ import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
|||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
||||||
import "../../../../src/components/ha-header-bar";
|
import "../../../../src/components/ha-header-bar";
|
||||||
import "../../../../src/components/ha-icon-button";
|
|
||||||
import { HassDialog } from "../../../../src/dialogs/make-dialog-manager";
|
import { HassDialog } from "../../../../src/dialogs/make-dialog-manager";
|
||||||
import { haStyleDialog } from "../../../../src/resources/styles";
|
import { haStyleDialog } from "../../../../src/resources/styles";
|
||||||
import type { HomeAssistant } from "../../../../src/types";
|
import type { HomeAssistant } from "../../../../src/types";
|
||||||
@@ -15,7 +14,7 @@ export class DialogHassioBackupUpload
|
|||||||
extends LitElement
|
extends LitElement
|
||||||
implements HassDialog<HassioBackupUploadDialogParams>
|
implements HassDialog<HassioBackupUploadDialogParams>
|
||||||
{
|
{
|
||||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
@state() private _params?: HassioBackupUploadDialogParams;
|
@state() private _params?: HassioBackupUploadDialogParams;
|
||||||
|
|
||||||
@@ -53,12 +52,9 @@ export class DialogHassioBackupUpload
|
|||||||
<div slot="heading">
|
<div slot="heading">
|
||||||
<ha-header-bar>
|
<ha-header-bar>
|
||||||
<span slot="title"> Upload backup </span>
|
<span slot="title"> Upload backup </span>
|
||||||
<ha-icon-button
|
<mwc-icon-button slot="actionItems" dialogAction="cancel">
|
||||||
.label=${this.hass?.localize("common.close") || "close"}
|
<ha-svg-icon .path=${mdiClose}></ha-svg-icon>
|
||||||
.path=${mdiClose}
|
</mwc-icon-button>
|
||||||
slot="actionItems"
|
|
||||||
dialogAction="cancel"
|
|
||||||
></ha-icon-button>
|
|
||||||
</ha-header-bar>
|
</ha-header-bar>
|
||||||
</div>
|
</div>
|
||||||
<hassio-upload-backup
|
<hassio-upload-backup
|
||||||
|
@@ -9,7 +9,7 @@ import "../../../../src/components/buttons/ha-progress-button";
|
|||||||
import "../../../../src/components/ha-alert";
|
import "../../../../src/components/ha-alert";
|
||||||
import "../../../../src/components/ha-button-menu";
|
import "../../../../src/components/ha-button-menu";
|
||||||
import "../../../../src/components/ha-header-bar";
|
import "../../../../src/components/ha-header-bar";
|
||||||
import "../../../../src/components/ha-icon-button";
|
import "../../../../src/components/ha-svg-icon";
|
||||||
import { getSignedPath } from "../../../../src/data/auth";
|
import { getSignedPath } from "../../../../src/data/auth";
|
||||||
import { extractApiErrorMessage } from "../../../../src/data/hassio/common";
|
import { extractApiErrorMessage } from "../../../../src/data/hassio/common";
|
||||||
import {
|
import {
|
||||||
@@ -35,7 +35,7 @@ class HassioBackupDialog
|
|||||||
extends LitElement
|
extends LitElement
|
||||||
implements HassDialog<HassioBackupDialogParams>
|
implements HassDialog<HassioBackupDialogParams>
|
||||||
{
|
{
|
||||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
@state() private _error?: string;
|
@state() private _error?: string;
|
||||||
|
|
||||||
@@ -76,12 +76,9 @@ class HassioBackupDialog
|
|||||||
<div slot="heading">
|
<div slot="heading">
|
||||||
<ha-header-bar>
|
<ha-header-bar>
|
||||||
<span slot="title">${this._backup.name}</span>
|
<span slot="title">${this._backup.name}</span>
|
||||||
<ha-icon-button
|
<mwc-icon-button slot="actionItems" dialogAction="cancel">
|
||||||
.label=${this.hass?.localize("common.close") || "close"}
|
<ha-svg-icon .path=${mdiClose}></ha-svg-icon>
|
||||||
.path=${mdiClose}
|
</mwc-icon-button>
|
||||||
slot="actionItems"
|
|
||||||
dialogAction="cancel"
|
|
||||||
></ha-icon-button>
|
|
||||||
</ha-header-bar>
|
</ha-header-bar>
|
||||||
</div>
|
</div>
|
||||||
${this._restoringBackup
|
${this._restoringBackup
|
||||||
@@ -113,11 +110,9 @@ class HassioBackupDialog
|
|||||||
@action=${this._handleMenuAction}
|
@action=${this._handleMenuAction}
|
||||||
@closed=${stopPropagation}
|
@closed=${stopPropagation}
|
||||||
>
|
>
|
||||||
<ha-icon-button
|
<mwc-icon-button slot="trigger" alt="menu">
|
||||||
.label=${this.hass!.localize("common.menu")}
|
<ha-svg-icon .path=${mdiDotsVertical}></ha-svg-icon>
|
||||||
.path=${mdiDotsVertical}
|
</mwc-icon-button>
|
||||||
slot="trigger"
|
|
||||||
></ha-icon-button>
|
|
||||||
<mwc-list-item>Download Backup</mwc-list-item>
|
<mwc-list-item>Download Backup</mwc-list-item>
|
||||||
<mwc-list-item class="error">Delete Backup</mwc-list-item>
|
<mwc-list-item class="error">Delete Backup</mwc-list-item>
|
||||||
</ha-button-menu>`
|
</ha-button-menu>`
|
||||||
@@ -131,6 +126,9 @@ class HassioBackupDialog
|
|||||||
haStyle,
|
haStyle,
|
||||||
haStyleDialog,
|
haStyleDialog,
|
||||||
css`
|
css`
|
||||||
|
ha-svg-icon {
|
||||||
|
color: var(--primary-text-color);
|
||||||
|
}
|
||||||
ha-circular-progress {
|
ha-circular-progress {
|
||||||
display: block;
|
display: block;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
@@ -141,9 +139,6 @@ class HassioBackupDialog
|
|||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
ha-icon-button {
|
|
||||||
color: var(--secondary-text-color);
|
|
||||||
}
|
|
||||||
`,
|
`,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@@ -192,23 +187,25 @@ class HassioBackupDialog
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!this._dialogParams?.onboarding) {
|
if (!this._dialogParams?.onboarding) {
|
||||||
this.hass!.callApi(
|
this.hass
|
||||||
"POST",
|
.callApi(
|
||||||
|
"POST",
|
||||||
|
|
||||||
`hassio/${
|
`hassio/${
|
||||||
atLeastVersion(this.hass!.config.version, 2021, 9)
|
atLeastVersion(this.hass.config.version, 2021, 9)
|
||||||
? "backups"
|
? "backups"
|
||||||
: "snapshots"
|
: "snapshots"
|
||||||
}/${this._backup!.slug}/restore/partial`,
|
}/${this._backup!.slug}/restore/partial`,
|
||||||
backupDetails
|
backupDetails
|
||||||
).then(
|
)
|
||||||
() => {
|
.then(
|
||||||
this.closeDialog();
|
() => {
|
||||||
},
|
this.closeDialog();
|
||||||
(error) => {
|
},
|
||||||
this._error = error.body.message;
|
(error) => {
|
||||||
}
|
this._error = error.body.message;
|
||||||
);
|
}
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
fireEvent(this, "restoring");
|
fireEvent(this, "restoring");
|
||||||
fetch(`/api/hassio/backups/${this._backup!.slug}/restore/partial`, {
|
fetch(`/api/hassio/backups/${this._backup!.slug}/restore/partial`, {
|
||||||
@@ -242,22 +239,24 @@ class HassioBackupDialog
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!this._dialogParams?.onboarding) {
|
if (!this._dialogParams?.onboarding) {
|
||||||
this.hass!.callApi(
|
this.hass
|
||||||
"POST",
|
.callApi(
|
||||||
`hassio/${
|
"POST",
|
||||||
atLeastVersion(this.hass!.config.version, 2021, 9)
|
`hassio/${
|
||||||
? "backups"
|
atLeastVersion(this.hass.config.version, 2021, 9)
|
||||||
: "snapshots"
|
? "backups"
|
||||||
}/${this._backup!.slug}/restore/full`,
|
: "snapshots"
|
||||||
backupDetails
|
}/${this._backup!.slug}/restore/full`,
|
||||||
).then(
|
backupDetails
|
||||||
() => {
|
)
|
||||||
this.closeDialog();
|
.then(
|
||||||
},
|
() => {
|
||||||
(error) => {
|
this.closeDialog();
|
||||||
this._error = error.body.message;
|
},
|
||||||
}
|
(error) => {
|
||||||
);
|
this._error = error.body.message;
|
||||||
|
}
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
fireEvent(this, "restoring");
|
fireEvent(this, "restoring");
|
||||||
fetch(`/api/hassio/backups/${this._backup!.slug}/restore/full`, {
|
fetch(`/api/hassio/backups/${this._backup!.slug}/restore/full`, {
|
||||||
@@ -279,33 +278,36 @@ class HassioBackupDialog
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.hass!.callApi(
|
this.hass
|
||||||
atLeastVersion(this.hass!.config.version, 2021, 9) ? "DELETE" : "POST",
|
|
||||||
`hassio/${
|
.callApi(
|
||||||
atLeastVersion(this.hass!.config.version, 2021, 9)
|
atLeastVersion(this.hass.config.version, 2021, 9) ? "DELETE" : "POST",
|
||||||
? `backups/${this._backup!.slug}`
|
`hassio/${
|
||||||
: `snapshots/${this._backup!.slug}/remove`
|
atLeastVersion(this.hass.config.version, 2021, 9)
|
||||||
}`
|
? `backups/${this._backup!.slug}`
|
||||||
).then(
|
: `snapshots/${this._backup!.slug}/remove`
|
||||||
() => {
|
}`
|
||||||
if (this._dialogParams!.onDelete) {
|
)
|
||||||
this._dialogParams!.onDelete();
|
.then(
|
||||||
|
() => {
|
||||||
|
if (this._dialogParams!.onDelete) {
|
||||||
|
this._dialogParams!.onDelete();
|
||||||
|
}
|
||||||
|
this.closeDialog();
|
||||||
|
},
|
||||||
|
(error) => {
|
||||||
|
this._error = error.body.message;
|
||||||
}
|
}
|
||||||
this.closeDialog();
|
);
|
||||||
},
|
|
||||||
(error) => {
|
|
||||||
this._error = error.body.message;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _downloadClicked() {
|
private async _downloadClicked() {
|
||||||
let signedPath: { path: string };
|
let signedPath: { path: string };
|
||||||
try {
|
try {
|
||||||
signedPath = await getSignedPath(
|
signedPath = await getSignedPath(
|
||||||
this.hass!,
|
this.hass,
|
||||||
`/api/hassio/${
|
`/api/hassio/${
|
||||||
atLeastVersion(this.hass!.config.version, 2021, 9)
|
atLeastVersion(this.hass.config.version, 2021, 9)
|
||||||
? "backups"
|
? "backups"
|
||||||
: "snapshots"
|
: "snapshots"
|
||||||
}/${this._backup!.slug}/download`
|
}/${this._backup!.slug}/download`
|
||||||
|
@@ -60,72 +60,85 @@ class HassioDatadiskDialog extends LitElement {
|
|||||||
if (!this.dialogParams) {
|
if (!this.dialogParams) {
|
||||||
return html``;
|
return html``;
|
||||||
}
|
}
|
||||||
|
let heading: string;
|
||||||
|
let content: TemplateResult;
|
||||||
|
|
||||||
|
if (this.moving) {
|
||||||
|
heading = this.dialogParams.supervisor.localize(
|
||||||
|
"dialog.datadisk_move.moving"
|
||||||
|
);
|
||||||
|
content = html`
|
||||||
|
<ha-circular-progress alt="Moving" size="large" active>
|
||||||
|
</ha-circular-progress>
|
||||||
|
<p class="progress-text">
|
||||||
|
${this.dialogParams.supervisor.localize(
|
||||||
|
"dialog.datadisk_move.moving_desc"
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
`;
|
||||||
|
} else {
|
||||||
|
heading = this.dialogParams.supervisor.localize(
|
||||||
|
"dialog.datadisk_move.title"
|
||||||
|
);
|
||||||
|
content = html`
|
||||||
|
${this.devices?.length
|
||||||
|
? html`
|
||||||
|
${this.dialogParams.supervisor.localize(
|
||||||
|
"dialog.datadisk_move.description",
|
||||||
|
{
|
||||||
|
current_path: this.dialogParams.supervisor.os.data_disk,
|
||||||
|
time: calculateMoveTime(this.dialogParams.supervisor),
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
<br /><br />
|
||||||
|
|
||||||
|
<paper-dropdown-menu
|
||||||
|
.label=${this.dialogParams.supervisor.localize(
|
||||||
|
"dialog.datadisk_move.select_device"
|
||||||
|
)}
|
||||||
|
@value-changed=${this._select_device}
|
||||||
|
>
|
||||||
|
<paper-listbox slot="dropdown-content">
|
||||||
|
${this.devices.map(
|
||||||
|
(device) => html`<paper-item>${device}</paper-item>`
|
||||||
|
)}
|
||||||
|
</paper-listbox>
|
||||||
|
</paper-dropdown-menu>
|
||||||
|
`
|
||||||
|
: this.devices === undefined
|
||||||
|
? this.dialogParams.supervisor.localize(
|
||||||
|
"dialog.datadisk_move.loading_devices"
|
||||||
|
)
|
||||||
|
: this.dialogParams.supervisor.localize(
|
||||||
|
"dialog.datadisk_move.no_devices"
|
||||||
|
)}
|
||||||
|
|
||||||
|
<mwc-button slot="secondaryAction" @click=${this.closeDialog}>
|
||||||
|
${this.dialogParams.supervisor.localize(
|
||||||
|
"dialog.datadisk_move.cancel"
|
||||||
|
)}
|
||||||
|
</mwc-button>
|
||||||
|
|
||||||
|
<mwc-button
|
||||||
|
.disabled=${!this.selectedDevice}
|
||||||
|
slot="primaryAction"
|
||||||
|
@click=${this._moveDatadisk}
|
||||||
|
>
|
||||||
|
${this.dialogParams.supervisor.localize("dialog.datadisk_move.move")}
|
||||||
|
</mwc-button>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<ha-dialog
|
<ha-dialog
|
||||||
open
|
open
|
||||||
scrimClickAction
|
scrimClickAction
|
||||||
escapeKeyAction
|
escapeKeyAction
|
||||||
.heading=${this.moving
|
|
||||||
? this.dialogParams.supervisor.localize("dialog.datadisk_move.moving")
|
|
||||||
: this.dialogParams.supervisor.localize("dialog.datadisk_move.title")}
|
|
||||||
@closed=${this.closeDialog}
|
@closed=${this.closeDialog}
|
||||||
?hideActions=${this.moving}
|
?hideActions=${this.moving}
|
||||||
|
.heading=${heading}
|
||||||
>
|
>
|
||||||
${this.moving
|
${content}
|
||||||
? html` <ha-circular-progress alt="Moving" size="large" active>
|
|
||||||
</ha-circular-progress>
|
|
||||||
<p class="progress-text">
|
|
||||||
${this.dialogParams.supervisor.localize(
|
|
||||||
"dialog.datadisk_move.moving_desc"
|
|
||||||
)}
|
|
||||||
</p>`
|
|
||||||
: html` ${this.devices?.length
|
|
||||||
? html`
|
|
||||||
${this.dialogParams.supervisor.localize(
|
|
||||||
"dialog.datadisk_move.description",
|
|
||||||
{
|
|
||||||
current_path: this.dialogParams.supervisor.os.data_disk,
|
|
||||||
time: calculateMoveTime(this.dialogParams.supervisor),
|
|
||||||
}
|
|
||||||
)}
|
|
||||||
<br /><br />
|
|
||||||
|
|
||||||
<paper-dropdown-menu
|
|
||||||
.label=${this.dialogParams.supervisor.localize(
|
|
||||||
"dialog.datadisk_move.select_device"
|
|
||||||
)}
|
|
||||||
@value-changed=${this._select_device}
|
|
||||||
>
|
|
||||||
<paper-listbox slot="dropdown-content">
|
|
||||||
${this.devices.map(
|
|
||||||
(device) => html`<paper-item>${device}</paper-item>`
|
|
||||||
)}
|
|
||||||
</paper-listbox>
|
|
||||||
</paper-dropdown-menu>
|
|
||||||
`
|
|
||||||
: this.devices === undefined
|
|
||||||
? this.dialogParams.supervisor.localize(
|
|
||||||
"dialog.datadisk_move.loading_devices"
|
|
||||||
)
|
|
||||||
: this.dialogParams.supervisor.localize(
|
|
||||||
"dialog.datadisk_move.no_devices"
|
|
||||||
)}
|
|
||||||
|
|
||||||
<mwc-button slot="secondaryAction" @click=${this.closeDialog}>
|
|
||||||
${this.dialogParams.supervisor.localize(
|
|
||||||
"dialog.datadisk_move.cancel"
|
|
||||||
)}
|
|
||||||
</mwc-button>
|
|
||||||
|
|
||||||
<mwc-button
|
|
||||||
.disabled=${!this.selectedDevice}
|
|
||||||
slot="primaryAction"
|
|
||||||
@click=${this._moveDatadisk}
|
|
||||||
>
|
|
||||||
${this.dialogParams.supervisor.localize(
|
|
||||||
"dialog.datadisk_move.move"
|
|
||||||
)}
|
|
||||||
</mwc-button>`}
|
|
||||||
</ha-dialog>
|
</ha-dialog>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
@@ -7,7 +7,6 @@ import "../../../../src/common/search/search-input";
|
|||||||
import { stringCompare } from "../../../../src/common/string/compare";
|
import { stringCompare } from "../../../../src/common/string/compare";
|
||||||
import "../../../../src/components/ha-dialog";
|
import "../../../../src/components/ha-dialog";
|
||||||
import "../../../../src/components/ha-expansion-panel";
|
import "../../../../src/components/ha-expansion-panel";
|
||||||
import "../../../../src/components/ha-icon-button";
|
|
||||||
import { HassioHardwareInfo } from "../../../../src/data/hassio/hardware";
|
import { HassioHardwareInfo } from "../../../../src/data/hassio/hardware";
|
||||||
import { dump } from "../../../../src/resources/js-yaml-dump";
|
import { dump } from "../../../../src/resources/js-yaml-dump";
|
||||||
import { haStyle, haStyleDialog } from "../../../../src/resources/styles";
|
import { haStyle, haStyleDialog } from "../../../../src/resources/styles";
|
||||||
@@ -71,13 +70,10 @@ class HassioHardwareDialog extends LitElement {
|
|||||||
<h2>
|
<h2>
|
||||||
${this._dialogParams.supervisor.localize("dialog.hardware.title")}
|
${this._dialogParams.supervisor.localize("dialog.hardware.title")}
|
||||||
</h2>
|
</h2>
|
||||||
<ha-icon-button
|
<mwc-icon-button dialogAction="close">
|
||||||
.label=${this.hass.localize("common.close")}
|
<ha-svg-icon .path=${mdiClose}></ha-svg-icon>
|
||||||
.path=${mdiClose}
|
</mwc-icon-button>
|
||||||
dialogAction="close"
|
|
||||||
></ha-icon-button>
|
|
||||||
<search-input
|
<search-input
|
||||||
.hass=${this.hass}
|
|
||||||
autofocus
|
autofocus
|
||||||
no-label-float
|
no-label-float
|
||||||
.filter=${this._filter}
|
.filter=${this._filter}
|
||||||
@@ -145,7 +141,7 @@ class HassioHardwareDialog extends LitElement {
|
|||||||
haStyle,
|
haStyle,
|
||||||
haStyleDialog,
|
haStyleDialog,
|
||||||
css`
|
css`
|
||||||
ha-icon-button {
|
mwc-icon-button {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 16px;
|
right: 16px;
|
||||||
top: 10px;
|
top: 10px;
|
||||||
|
@@ -47,6 +47,11 @@ class HassioMarkdownDialog extends LitElement {
|
|||||||
haStyleDialog,
|
haStyleDialog,
|
||||||
hassioStyle,
|
hassioStyle,
|
||||||
css`
|
css`
|
||||||
|
ha-paper-dialog {
|
||||||
|
min-width: 350px;
|
||||||
|
font-size: 14px;
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
app-toolbar {
|
app-toolbar {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0 16px;
|
padding: 0 16px;
|
||||||
@@ -57,6 +62,19 @@ class HassioMarkdownDialog extends LitElement {
|
|||||||
margin-left: 16px;
|
margin-left: 16px;
|
||||||
}
|
}
|
||||||
@media all and (max-width: 450px), all and (max-height: 500px) {
|
@media all and (max-width: 450px), all and (max-height: 500px) {
|
||||||
|
ha-paper-dialog {
|
||||||
|
max-height: 100%;
|
||||||
|
}
|
||||||
|
ha-paper-dialog::before {
|
||||||
|
content: "";
|
||||||
|
position: fixed;
|
||||||
|
z-index: -1;
|
||||||
|
top: 0px;
|
||||||
|
left: 0px;
|
||||||
|
right: 0px;
|
||||||
|
bottom: 0px;
|
||||||
|
background-color: inherit;
|
||||||
|
}
|
||||||
app-toolbar {
|
app-toolbar {
|
||||||
color: var(--text-primary-color);
|
color: var(--text-primary-color);
|
||||||
background-color: var(--primary-color);
|
background-color: var(--primary-color);
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
import "@material/mwc-button/mwc-button";
|
import "@material/mwc-button/mwc-button";
|
||||||
|
import "@material/mwc-icon-button";
|
||||||
import "@material/mwc-list/mwc-list";
|
import "@material/mwc-list/mwc-list";
|
||||||
import "@material/mwc-list/mwc-list-item";
|
import "@material/mwc-list/mwc-list-item";
|
||||||
import "@material/mwc-tab";
|
import "@material/mwc-tab";
|
||||||
@@ -15,9 +16,9 @@ import "../../../../src/components/ha-dialog";
|
|||||||
import "../../../../src/components/ha-expansion-panel";
|
import "../../../../src/components/ha-expansion-panel";
|
||||||
import "../../../../src/components/ha-formfield";
|
import "../../../../src/components/ha-formfield";
|
||||||
import "../../../../src/components/ha-header-bar";
|
import "../../../../src/components/ha-header-bar";
|
||||||
import "../../../../src/components/ha-icon-button";
|
|
||||||
import "../../../../src/components/ha-radio";
|
import "../../../../src/components/ha-radio";
|
||||||
import "../../../../src/components/ha-related-items";
|
import "../../../../src/components/ha-related-items";
|
||||||
|
import "../../../../src/components/ha-svg-icon";
|
||||||
import { extractApiErrorMessage } from "../../../../src/data/hassio/common";
|
import { extractApiErrorMessage } from "../../../../src/data/hassio/common";
|
||||||
import {
|
import {
|
||||||
AccessPoints,
|
AccessPoints,
|
||||||
@@ -103,12 +104,9 @@ export class DialogHassioNetwork
|
|||||||
<span slot="title">
|
<span slot="title">
|
||||||
${this.supervisor.localize("dialog.network.title")}
|
${this.supervisor.localize("dialog.network.title")}
|
||||||
</span>
|
</span>
|
||||||
<ha-icon-button
|
<mwc-icon-button slot="actionItems" dialogAction="cancel">
|
||||||
.label=${this.hass.localize("common.close")}
|
<ha-svg-icon .path=${mdiClose}></ha-svg-icon>
|
||||||
.path=${mdiClose}
|
</mwc-icon-button>
|
||||||
slot="actionItems"
|
|
||||||
dialogAction="cancel"
|
|
||||||
></ha-icon-button>
|
|
||||||
</ha-header-bar>
|
</ha-header-bar>
|
||||||
${this._interfaces.length > 1
|
${this._interfaces.length > 1
|
||||||
? html`<mwc-tab-bar
|
? html`<mwc-tab-bar
|
||||||
|
@@ -1,12 +1,13 @@
|
|||||||
import "@material/mwc-button/mwc-button";
|
import "@material/mwc-button/mwc-button";
|
||||||
|
import "@material/mwc-icon-button/mwc-icon-button";
|
||||||
|
import "@material/mwc-list/mwc-list-item";
|
||||||
import { mdiDelete } from "@mdi/js";
|
import { mdiDelete } from "@mdi/js";
|
||||||
|
import { PaperInputElement } from "@polymer/paper-input/paper-input";
|
||||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
|
import "../../../../src/components/ha-circular-progress";
|
||||||
import { createCloseHeading } from "../../../../src/components/ha-dialog";
|
import { createCloseHeading } from "../../../../src/components/ha-dialog";
|
||||||
import "../../../../src/components/ha-form/ha-form";
|
import "../../../../src/components/ha-svg-icon";
|
||||||
import { HaFormSchema } from "../../../../src/components/ha-form/types";
|
|
||||||
import "../../../../src/components/ha-icon-button";
|
|
||||||
import "../../../../src/components/ha-settings-row";
|
|
||||||
import { extractApiErrorMessage } from "../../../../src/data/hassio/common";
|
import { extractApiErrorMessage } from "../../../../src/data/hassio/common";
|
||||||
import {
|
import {
|
||||||
addHassioDockerRegistry,
|
addHassioDockerRegistry,
|
||||||
@@ -19,41 +20,22 @@ import { haStyle, haStyleDialog } from "../../../../src/resources/styles";
|
|||||||
import type { HomeAssistant } from "../../../../src/types";
|
import type { HomeAssistant } from "../../../../src/types";
|
||||||
import { RegistriesDialogParams } from "./show-dialog-registries";
|
import { RegistriesDialogParams } from "./show-dialog-registries";
|
||||||
|
|
||||||
const SCHEMA = [
|
|
||||||
{
|
|
||||||
type: "string",
|
|
||||||
name: "registry",
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "string",
|
|
||||||
name: "username",
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "string",
|
|
||||||
name: "password",
|
|
||||||
required: true,
|
|
||||||
format: "password",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
@customElement("dialog-hassio-registries")
|
@customElement("dialog-hassio-registries")
|
||||||
class HassioRegistriesDialog extends LitElement {
|
class HassioRegistriesDialog extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
@property({ attribute: false }) public supervisor!: Supervisor;
|
@property({ attribute: false }) public supervisor!: Supervisor;
|
||||||
|
|
||||||
@state() private _registries?: {
|
@property({ attribute: false }) private _registries?: {
|
||||||
registry: string;
|
registry: string;
|
||||||
username: string;
|
username: string;
|
||||||
}[];
|
}[];
|
||||||
|
|
||||||
@state() private _input: {
|
@state() private _registry?: string;
|
||||||
registry?: string;
|
|
||||||
username?: string;
|
@state() private _username?: string;
|
||||||
password?: string;
|
|
||||||
} = {};
|
@state() private _password?: string;
|
||||||
|
|
||||||
@state() private _opened = false;
|
@state() private _opened = false;
|
||||||
|
|
||||||
@@ -66,7 +48,6 @@ class HassioRegistriesDialog extends LitElement {
|
|||||||
@closed=${this.closeDialog}
|
@closed=${this.closeDialog}
|
||||||
scrimClickAction
|
scrimClickAction
|
||||||
escapeKeyAction
|
escapeKeyAction
|
||||||
hideActions
|
|
||||||
.heading=${createCloseHeading(
|
.heading=${createCloseHeading(
|
||||||
this.hass,
|
this.hass,
|
||||||
this._addingRegistry
|
this._addingRegistry
|
||||||
@@ -74,77 +55,100 @@ class HassioRegistriesDialog extends LitElement {
|
|||||||
: this.supervisor.localize("dialog.registries.title_manage")
|
: this.supervisor.localize("dialog.registries.title_manage")
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
${this._addingRegistry
|
<div class="form">
|
||||||
? html`
|
${this._addingRegistry
|
||||||
<ha-form
|
? html`
|
||||||
.data=${this._input}
|
<paper-input
|
||||||
.schema=${SCHEMA}
|
@value-changed=${this._inputChanged}
|
||||||
@value-changed=${this._valueChanged}
|
class="flex-auto"
|
||||||
.computeLabel=${this._computeLabel}
|
name="registry"
|
||||||
></ha-form>
|
.label=${this.supervisor.localize(
|
||||||
<div class="action">
|
"dialog.registries.registry"
|
||||||
|
)}
|
||||||
|
required
|
||||||
|
auto-validate
|
||||||
|
></paper-input>
|
||||||
|
<paper-input
|
||||||
|
@value-changed=${this._inputChanged}
|
||||||
|
class="flex-auto"
|
||||||
|
name="username"
|
||||||
|
.label=${this.supervisor.localize(
|
||||||
|
"dialog.registries.username"
|
||||||
|
)}
|
||||||
|
required
|
||||||
|
auto-validate
|
||||||
|
></paper-input>
|
||||||
|
<paper-input
|
||||||
|
@value-changed=${this._inputChanged}
|
||||||
|
class="flex-auto"
|
||||||
|
name="password"
|
||||||
|
.label=${this.supervisor.localize(
|
||||||
|
"dialog.registries.password"
|
||||||
|
)}
|
||||||
|
type="password"
|
||||||
|
required
|
||||||
|
auto-validate
|
||||||
|
></paper-input>
|
||||||
|
|
||||||
<mwc-button
|
<mwc-button
|
||||||
?disabled=${Boolean(
|
?disabled=${Boolean(
|
||||||
!this._input.registry ||
|
!this._registry || !this._username || !this._password
|
||||||
!this._input.username ||
|
|
||||||
!this._input.password
|
|
||||||
)}
|
)}
|
||||||
@click=${this._addNewRegistry}
|
@click=${this._addNewRegistry}
|
||||||
>
|
>
|
||||||
${this.supervisor.localize("dialog.registries.add_registry")}
|
${this.supervisor.localize("dialog.registries.add_registry")}
|
||||||
</mwc-button>
|
</mwc-button>
|
||||||
</div>
|
`
|
||||||
`
|
: html`${this._registries?.length
|
||||||
: html`${this._registries?.length
|
? this._registries.map(
|
||||||
? this._registries.map(
|
(entry) => html`
|
||||||
(entry) => html`
|
<mwc-list-item class="option" hasMeta twoline>
|
||||||
<ha-settings-row class="registry">
|
<span>${entry.registry}</span>
|
||||||
<span slot="heading"> ${entry.registry} </span>
|
<span slot="secondary"
|
||||||
<span slot="description">
|
>${this.supervisor.localize(
|
||||||
${this.supervisor.localize(
|
"dialog.registries.username"
|
||||||
"dialog.registries.username"
|
)}:
|
||||||
)}:
|
${entry.username}</span
|
||||||
${entry.username}
|
>
|
||||||
</span>
|
<mwc-icon-button
|
||||||
<ha-icon-button
|
.entry=${entry}
|
||||||
.entry=${entry}
|
.title=${this.supervisor.localize(
|
||||||
.label=${this.supervisor.localize(
|
"dialog.registries.remove"
|
||||||
"dialog.registries.remove"
|
)}
|
||||||
)}
|
slot="meta"
|
||||||
.path=${mdiDelete}
|
@click=${this._removeRegistry}
|
||||||
@click=${this._removeRegistry}
|
>
|
||||||
></ha-icon-button>
|
<ha-svg-icon .path=${mdiDelete}></ha-svg-icon>
|
||||||
</ha-settings-row>
|
</mwc-icon-button>
|
||||||
`
|
</mwc-list-item>
|
||||||
)
|
`
|
||||||
: html`
|
)
|
||||||
<ha-alert>
|
: html`
|
||||||
${this.supervisor.localize(
|
<mwc-list-item>
|
||||||
"dialog.registries.no_registries"
|
<span
|
||||||
)}
|
>${this.supervisor.localize(
|
||||||
</ha-alert>
|
"dialog.registries.no_registries"
|
||||||
`}
|
)}</span
|
||||||
<div class="action">
|
>
|
||||||
|
</mwc-list-item>
|
||||||
|
`}
|
||||||
<mwc-button @click=${this._addRegistry}>
|
<mwc-button @click=${this._addRegistry}>
|
||||||
${this.supervisor.localize(
|
${this.supervisor.localize(
|
||||||
"dialog.registries.add_new_registry"
|
"dialog.registries.add_new_registry"
|
||||||
)}
|
)}
|
||||||
</mwc-button>
|
</mwc-button> `}
|
||||||
</div> `}
|
</div>
|
||||||
</ha-dialog>
|
</ha-dialog>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _computeLabel = (schema: HaFormSchema) =>
|
private _inputChanged(ev: Event) {
|
||||||
this.supervisor.localize(`dialog.registries.${schema.name}`) || schema.name;
|
const target = ev.currentTarget as PaperInputElement;
|
||||||
|
this[`_${target.name}`] = target.value;
|
||||||
private _valueChanged(ev: CustomEvent) {
|
|
||||||
this._input = ev.detail.value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async showDialog(dialogParams: RegistriesDialogParams): Promise<void> {
|
public async showDialog(dialogParams: RegistriesDialogParams): Promise<void> {
|
||||||
this._opened = true;
|
this._opened = true;
|
||||||
this._input = {};
|
|
||||||
this.supervisor = dialogParams.supervisor;
|
this.supervisor = dialogParams.supervisor;
|
||||||
await this._loadRegistries();
|
await this._loadRegistries();
|
||||||
await this.updateComplete;
|
await this.updateComplete;
|
||||||
@@ -153,7 +157,6 @@ class HassioRegistriesDialog extends LitElement {
|
|||||||
public closeDialog(): void {
|
public closeDialog(): void {
|
||||||
this._addingRegistry = false;
|
this._addingRegistry = false;
|
||||||
this._opened = false;
|
this._opened = false;
|
||||||
this._input = {};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public focus(): void {
|
public focus(): void {
|
||||||
@@ -178,16 +181,15 @@ class HassioRegistriesDialog extends LitElement {
|
|||||||
|
|
||||||
private async _addNewRegistry(): Promise<void> {
|
private async _addNewRegistry(): Promise<void> {
|
||||||
const data = {};
|
const data = {};
|
||||||
data[this._input.registry!] = {
|
data[this._registry!] = {
|
||||||
username: this._input.username,
|
username: this._username,
|
||||||
password: this._input.password,
|
password: this._password,
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await addHassioDockerRegistry(this.hass, data);
|
await addHassioDockerRegistry(this.hass, data);
|
||||||
await this._loadRegistries();
|
await this._loadRegistries();
|
||||||
this._addingRegistry = false;
|
this._addingRegistry = false;
|
||||||
this._input = {};
|
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
showAlertDialog(this, {
|
showAlertDialog(this, {
|
||||||
title: this.supervisor.localize("dialog.registries.failed_to_add"),
|
title: this.supervisor.localize("dialog.registries.failed_to_add"),
|
||||||
@@ -215,20 +217,32 @@ class HassioRegistriesDialog extends LitElement {
|
|||||||
haStyle,
|
haStyle,
|
||||||
haStyleDialog,
|
haStyleDialog,
|
||||||
css`
|
css`
|
||||||
.registry {
|
ha-dialog.button-left {
|
||||||
|
--justify-action-buttons: flex-start;
|
||||||
|
}
|
||||||
|
paper-icon-item {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.form {
|
||||||
|
color: var(--primary-text-color);
|
||||||
|
}
|
||||||
|
.option {
|
||||||
border: 1px solid var(--divider-color);
|
border: 1px solid var(--divider-color);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
margin-top: 4px;
|
margin-top: 4px;
|
||||||
}
|
}
|
||||||
.action {
|
mwc-button {
|
||||||
margin-top: 24px;
|
margin-left: 8px;
|
||||||
width: 100%;
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-end;
|
|
||||||
}
|
}
|
||||||
ha-icon-button {
|
mwc-icon-button {
|
||||||
color: var(--error-color);
|
color: var(--error-color);
|
||||||
margin-right: -10px;
|
margin: -10px;
|
||||||
|
}
|
||||||
|
mwc-list-item {
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
mwc-list-item span[slot="secondary"] {
|
||||||
|
color: var(--secondary-text-color);
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
];
|
];
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
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 { mdiDelete } from "@mdi/js";
|
||||||
import "@polymer/paper-input/paper-input";
|
import "@polymer/paper-input/paper-input";
|
||||||
import type { PaperInputElement } from "@polymer/paper-input/paper-input";
|
import type { PaperInputElement } from "@polymer/paper-input/paper-input";
|
||||||
@@ -8,11 +9,10 @@ import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
|||||||
import { customElement, property, query, state } from "lit/decorators";
|
import { customElement, property, query, state } from "lit/decorators";
|
||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
||||||
import { caseInsensitiveStringCompare } from "../../../../src/common/string/compare";
|
|
||||||
import "../../../../src/components/ha-alert";
|
import "../../../../src/components/ha-alert";
|
||||||
import "../../../../src/components/ha-circular-progress";
|
import "../../../../src/components/ha-circular-progress";
|
||||||
import { createCloseHeading } from "../../../../src/components/ha-dialog";
|
import { createCloseHeading } from "../../../../src/components/ha-dialog";
|
||||||
import "../../../../src/components/ha-icon-button";
|
import "../../../../src/components/ha-svg-icon";
|
||||||
import {
|
import {
|
||||||
fetchHassioAddonsInfo,
|
fetchHassioAddonsInfo,
|
||||||
HassioAddonRepository,
|
HassioAddonRepository,
|
||||||
@@ -57,7 +57,7 @@ class HassioRepositoriesDialog extends LitElement {
|
|||||||
private _filteredRepositories = memoizeOne((repos: HassioAddonRepository[]) =>
|
private _filteredRepositories = memoizeOne((repos: HassioAddonRepository[]) =>
|
||||||
repos
|
repos
|
||||||
.filter((repo) => repo.slug !== "core" && repo.slug !== "local")
|
.filter((repo) => repo.slug !== "core" && repo.slug !== "local")
|
||||||
.sort((a, b) => caseInsensitiveStringCompare(a.name, b.name))
|
.sort((a, b) => (a.name < b.name ? -1 : 1))
|
||||||
);
|
);
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
@@ -89,14 +89,15 @@ 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}
|
||||||
.label=${this._dialogParams!.supervisor.localize(
|
.title=${this._dialogParams!.supervisor.localize(
|
||||||
"dialog.repositories.remove"
|
"dialog.repositories.remove"
|
||||||
)}
|
)}
|
||||||
.path=${mdiDelete}
|
|
||||||
@click=${this._removeRepository}
|
@click=${this._removeRepository}
|
||||||
></ha-icon-button>
|
>
|
||||||
|
<ha-svg-icon .path=${mdiDelete}></ha-svg-icon>
|
||||||
|
</mwc-icon-button>
|
||||||
</paper-item>
|
</paper-item>
|
||||||
`
|
`
|
||||||
)
|
)
|
||||||
|
204
hassio/src/dialogs/update/dialog-supervisor-update.ts
Normal file
204
hassio/src/dialogs/update/dialog-supervisor-update.ts
Normal file
@@ -0,0 +1,204 @@
|
|||||||
|
import "@material/mwc-button/mwc-button";
|
||||||
|
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||||
|
import { customElement, state } from "lit/decorators";
|
||||||
|
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
||||||
|
import "../../../../src/components/ha-alert";
|
||||||
|
import "../../../../src/components/ha-circular-progress";
|
||||||
|
import "../../../../src/components/ha-dialog";
|
||||||
|
import "../../../../src/components/ha-settings-row";
|
||||||
|
import "../../../../src/components/ha-svg-icon";
|
||||||
|
import "../../../../src/components/ha-switch";
|
||||||
|
import {
|
||||||
|
extractApiErrorMessage,
|
||||||
|
ignoreSupervisorError,
|
||||||
|
} from "../../../../src/data/hassio/common";
|
||||||
|
import { createHassioPartialBackup } from "../../../../src/data/hassio/backup";
|
||||||
|
import { haStyle, haStyleDialog } from "../../../../src/resources/styles";
|
||||||
|
import type { HomeAssistant } from "../../../../src/types";
|
||||||
|
import { SupervisorDialogSupervisorUpdateParams } from "./show-dialog-update";
|
||||||
|
|
||||||
|
@customElement("dialog-supervisor-update")
|
||||||
|
class DialogSupervisorUpdate extends LitElement {
|
||||||
|
public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@state() private _opened = false;
|
||||||
|
|
||||||
|
@state() private _createBackup = true;
|
||||||
|
|
||||||
|
@state() private _action: "backup" | "update" | null = null;
|
||||||
|
|
||||||
|
@state() private _error?: string;
|
||||||
|
|
||||||
|
@state()
|
||||||
|
private _dialogParams?: SupervisorDialogSupervisorUpdateParams;
|
||||||
|
|
||||||
|
public async showDialog(
|
||||||
|
params: SupervisorDialogSupervisorUpdateParams
|
||||||
|
): Promise<void> {
|
||||||
|
this._opened = true;
|
||||||
|
this._dialogParams = params;
|
||||||
|
await this.updateComplete;
|
||||||
|
}
|
||||||
|
|
||||||
|
public closeDialog(): void {
|
||||||
|
this._action = null;
|
||||||
|
this._createBackup = true;
|
||||||
|
this._error = undefined;
|
||||||
|
this._dialogParams = undefined;
|
||||||
|
fireEvent(this, "dialog-closed", { dialog: this.localName });
|
||||||
|
}
|
||||||
|
|
||||||
|
public focus(): void {
|
||||||
|
this.updateComplete.then(() =>
|
||||||
|
(
|
||||||
|
this.shadowRoot?.querySelector("[dialogInitialFocus]") as HTMLElement
|
||||||
|
)?.focus()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
if (!this._dialogParams) {
|
||||||
|
return html``;
|
||||||
|
}
|
||||||
|
return html`
|
||||||
|
<ha-dialog .open=${this._opened} scrimClickAction escapeKeyAction>
|
||||||
|
${this._action === null
|
||||||
|
? html`<slot name="heading">
|
||||||
|
<h2 id="title" class="header_title">
|
||||||
|
${this._dialogParams.supervisor.localize(
|
||||||
|
"confirm.update.title",
|
||||||
|
"name",
|
||||||
|
this._dialogParams.name
|
||||||
|
)}
|
||||||
|
</h2>
|
||||||
|
</slot>
|
||||||
|
<div>
|
||||||
|
${this._dialogParams.supervisor.localize(
|
||||||
|
"confirm.update.text",
|
||||||
|
"name",
|
||||||
|
this._dialogParams.name,
|
||||||
|
"version",
|
||||||
|
this._dialogParams.version
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ha-settings-row>
|
||||||
|
<span slot="heading">
|
||||||
|
${this._dialogParams.supervisor.localize(
|
||||||
|
"dialog.update.backup"
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
<span slot="description">
|
||||||
|
${this._dialogParams.supervisor.localize(
|
||||||
|
"dialog.update.create_backup",
|
||||||
|
"name",
|
||||||
|
this._dialogParams.name
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
<ha-switch
|
||||||
|
.checked=${this._createBackup}
|
||||||
|
haptic
|
||||||
|
@click=${this._toggleBackup}
|
||||||
|
>
|
||||||
|
</ha-switch>
|
||||||
|
</ha-settings-row>
|
||||||
|
<mwc-button @click=${this.closeDialog} slot="secondaryAction">
|
||||||
|
${this._dialogParams.supervisor.localize("common.cancel")}
|
||||||
|
</mwc-button>
|
||||||
|
<mwc-button
|
||||||
|
.disabled=${this._error !== undefined}
|
||||||
|
@click=${this._update}
|
||||||
|
slot="primaryAction"
|
||||||
|
>
|
||||||
|
${this._dialogParams.supervisor.localize("common.update")}
|
||||||
|
</mwc-button>`
|
||||||
|
: html`<ha-circular-progress alt="Updating" size="large" active>
|
||||||
|
</ha-circular-progress>
|
||||||
|
<p class="progress-text">
|
||||||
|
${this._action === "update"
|
||||||
|
? this._dialogParams.supervisor.localize(
|
||||||
|
"dialog.update.updating",
|
||||||
|
"name",
|
||||||
|
this._dialogParams.name,
|
||||||
|
"version",
|
||||||
|
this._dialogParams.version
|
||||||
|
)
|
||||||
|
: this._dialogParams.supervisor.localize(
|
||||||
|
"dialog.update.creating_backup",
|
||||||
|
"name",
|
||||||
|
this._dialogParams.name
|
||||||
|
)}
|
||||||
|
</p>`}
|
||||||
|
${this._error
|
||||||
|
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
|
||||||
|
: ""}
|
||||||
|
</ha-dialog>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _toggleBackup() {
|
||||||
|
this._createBackup = !this._createBackup;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _update() {
|
||||||
|
if (this._createBackup) {
|
||||||
|
this._action = "backup";
|
||||||
|
try {
|
||||||
|
await createHassioPartialBackup(
|
||||||
|
this.hass,
|
||||||
|
this._dialogParams!.backupParams
|
||||||
|
);
|
||||||
|
} catch (err: any) {
|
||||||
|
this._error = extractApiErrorMessage(err);
|
||||||
|
this._action = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this._action = "update";
|
||||||
|
try {
|
||||||
|
await this._dialogParams!.updateHandler!();
|
||||||
|
} catch (err: any) {
|
||||||
|
if (this.hass.connection.connected && !ignoreSupervisorError(err)) {
|
||||||
|
this._error = extractApiErrorMessage(err);
|
||||||
|
this._action = null;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.closeDialog();
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResultGroup {
|
||||||
|
return [
|
||||||
|
haStyle,
|
||||||
|
haStyleDialog,
|
||||||
|
css`
|
||||||
|
.form {
|
||||||
|
color: var(--primary-text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
ha-settings-row {
|
||||||
|
margin-top: 32px;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ha-circular-progress {
|
||||||
|
display: block;
|
||||||
|
margin: 32px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-text {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"dialog-supervisor-update": DialogSupervisorUpdate;
|
||||||
|
}
|
||||||
|
}
|
21
hassio/src/dialogs/update/show-dialog-update.ts
Normal file
21
hassio/src/dialogs/update/show-dialog-update.ts
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
||||||
|
import { Supervisor } from "../../../../src/data/supervisor/supervisor";
|
||||||
|
|
||||||
|
export interface SupervisorDialogSupervisorUpdateParams {
|
||||||
|
supervisor: Supervisor;
|
||||||
|
name: string;
|
||||||
|
version: string;
|
||||||
|
backupParams: any;
|
||||||
|
updateHandler: () => Promise<void>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const showDialogSupervisorUpdate = (
|
||||||
|
element: HTMLElement,
|
||||||
|
dialogParams: SupervisorDialogSupervisorUpdateParams
|
||||||
|
): void => {
|
||||||
|
fireEvent(element, "show-dialog", {
|
||||||
|
dialogTag: "dialog-supervisor-update",
|
||||||
|
dialogImport: () => import("./dialog-supervisor-update"),
|
||||||
|
dialogParams,
|
||||||
|
});
|
||||||
|
};
|
@@ -10,7 +10,7 @@ import { HassioPanelInfo } from "../../src/data/hassio/supervisor";
|
|||||||
import { Supervisor } from "../../src/data/supervisor/supervisor";
|
import { Supervisor } from "../../src/data/supervisor/supervisor";
|
||||||
import { makeDialogManager } from "../../src/dialogs/make-dialog-manager";
|
import { makeDialogManager } from "../../src/dialogs/make-dialog-manager";
|
||||||
import "../../src/layouts/hass-loading-screen";
|
import "../../src/layouts/hass-loading-screen";
|
||||||
import { HomeAssistant } from "../../src/types";
|
import { HomeAssistant, Route } from "../../src/types";
|
||||||
import "./hassio-router";
|
import "./hassio-router";
|
||||||
import { SupervisorBaseElement } from "./supervisor-base-element";
|
import { SupervisorBaseElement } from "./supervisor-base-element";
|
||||||
|
|
||||||
@@ -24,6 +24,8 @@ export class HassioMain extends SupervisorBaseElement {
|
|||||||
|
|
||||||
@property({ type: Boolean }) public narrow!: boolean;
|
@property({ type: Boolean }) public narrow!: boolean;
|
||||||
|
|
||||||
|
@property({ attribute: false }) public route?: Route;
|
||||||
|
|
||||||
protected firstUpdated(changedProps: PropertyValues) {
|
protected firstUpdated(changedProps: PropertyValues) {
|
||||||
super.firstUpdated(changedProps);
|
super.firstUpdated(changedProps);
|
||||||
|
|
||||||
@@ -111,6 +113,12 @@ export class HassioMain extends SupervisorBaseElement {
|
|||||||
: this.hass.themes.default_theme);
|
: this.hass.themes.default_theme);
|
||||||
|
|
||||||
themeSettings = this.hass.selectedTheme;
|
themeSettings = this.hass.selectedTheme;
|
||||||
|
if (themeSettings?.dark === undefined) {
|
||||||
|
themeSettings = {
|
||||||
|
...this.hass.selectedTheme,
|
||||||
|
dark: this.hass.themes.darkMode,
|
||||||
|
};
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
themeName =
|
themeName =
|
||||||
(this.hass.selectedTheme as unknown as string) ||
|
(this.hass.selectedTheme as unknown as string) ||
|
||||||
|
@@ -34,9 +34,6 @@ const REDIRECTS: Redirects = {
|
|||||||
supervisor_store: {
|
supervisor_store: {
|
||||||
redirect: "/hassio/store",
|
redirect: "/hassio/store",
|
||||||
},
|
},
|
||||||
supervisor_addons: {
|
|
||||||
redirect: "/hassio/dashboard",
|
|
||||||
},
|
|
||||||
supervisor_addon: {
|
supervisor_addon: {
|
||||||
redirect: "/hassio/addon",
|
redirect: "/hassio/addon",
|
||||||
params: {
|
params: {
|
||||||
|
@@ -35,10 +35,6 @@ class HassioRouter extends HassRouterPage {
|
|||||||
backups: "dashboard",
|
backups: "dashboard",
|
||||||
store: "dashboard",
|
store: "dashboard",
|
||||||
system: "dashboard",
|
system: "dashboard",
|
||||||
"update-available": {
|
|
||||||
tag: "update-available-dashboard",
|
|
||||||
load: () => import("./update-available/update-available-dashboard"),
|
|
||||||
},
|
|
||||||
addon: {
|
addon: {
|
||||||
tag: "hassio-addon-dashboard",
|
tag: "hassio-addon-dashboard",
|
||||||
load: () => import("./addon-view/hassio-addon-dashboard"),
|
load: () => import("./addon-view/hassio-addon-dashboard"),
|
||||||
|
@@ -1,22 +1,16 @@
|
|||||||
import {
|
import { mdiBackupRestore, mdiCogs, mdiStore, mdiViewDashboard } from "@mdi/js";
|
||||||
mdiBackupRestore,
|
|
||||||
mdiCogs,
|
|
||||||
mdiPuzzle,
|
|
||||||
mdiViewDashboard,
|
|
||||||
} from "@mdi/js";
|
|
||||||
import { atLeastVersion } from "../../src/common/config/version";
|
|
||||||
import type { PageNavigation } from "../../src/layouts/hass-tabs-subpage";
|
import type { PageNavigation } from "../../src/layouts/hass-tabs-subpage";
|
||||||
import { HomeAssistant } from "../../src/types";
|
|
||||||
|
|
||||||
export const supervisorTabs = (hass: HomeAssistant): PageNavigation[] => [
|
export const supervisorTabs: PageNavigation[] = [
|
||||||
{
|
{
|
||||||
translationKey: atLeastVersion(hass.config.version, 2021, 12)
|
translationKey: "panel.dashboard",
|
||||||
? "panel.addons"
|
|
||||||
: "panel.dashboard",
|
|
||||||
path: `/hassio/dashboard`,
|
path: `/hassio/dashboard`,
|
||||||
iconPath: atLeastVersion(hass.config.version, 2021, 12)
|
iconPath: mdiViewDashboard,
|
||||||
? mdiPuzzle
|
},
|
||||||
: mdiViewDashboard,
|
{
|
||||||
|
translationKey: "panel.store",
|
||||||
|
path: `/hassio/store`,
|
||||||
|
iconPath: mdiStore,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
translationKey: "panel.backups",
|
translationKey: "panel.backups",
|
||||||
|
@@ -12,7 +12,6 @@ import { fireEvent } from "../../../src/common/dom/fire_event";
|
|||||||
import { navigate } from "../../../src/common/navigate";
|
import { navigate } from "../../../src/common/navigate";
|
||||||
import { extractSearchParam } from "../../../src/common/url/search-params";
|
import { extractSearchParam } from "../../../src/common/url/search-params";
|
||||||
import { nextRender } from "../../../src/common/util/render-status";
|
import { nextRender } from "../../../src/common/util/render-status";
|
||||||
import "../../../src/components/ha-icon-button";
|
|
||||||
import {
|
import {
|
||||||
fetchHassioAddonInfo,
|
fetchHassioAddonInfo,
|
||||||
HassioAddonDetails,
|
HassioAddonDetails,
|
||||||
@@ -73,11 +72,12 @@ class HassioIngressView extends LitElement {
|
|||||||
|
|
||||||
return html`${this.narrow || this.hass.dockedSidebar === "always_hidden"
|
return html`${this.narrow || this.hass.dockedSidebar === "always_hidden"
|
||||||
? html`<div class="header">
|
? html`<div class="header">
|
||||||
<ha-icon-button
|
<mwc-icon-button
|
||||||
.label=${this.hass.localize("ui.sidebar.sidebar_toggle")}
|
aria-label=${this.hass.localize("ui.sidebar.sidebar_toggle")}
|
||||||
.path=${mdiMenu}
|
|
||||||
@click=${this._toggleMenu}
|
@click=${this._toggleMenu}
|
||||||
></ha-icon-button>
|
>
|
||||||
|
<ha-svg-icon .path=${mdiMenu}></ha-svg-icon>
|
||||||
|
</mwc-icon-button>
|
||||||
<div class="main-title">${this._addon.name}</div>
|
<div class="main-title">${this._addon.name}</div>
|
||||||
</div>
|
</div>
|
||||||
${iframe}`
|
${iframe}`
|
||||||
@@ -241,7 +241,7 @@ class HassioIngressView extends LitElement {
|
|||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
ha-icon-button {
|
mwc-icon-button {
|
||||||
pointer-events: auto;
|
pointer-events: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -25,7 +25,7 @@ import {
|
|||||||
} from "../../src/data/supervisor/supervisor";
|
} from "../../src/data/supervisor/supervisor";
|
||||||
import { ProvideHassLitMixin } from "../../src/mixins/provide-hass-lit-mixin";
|
import { ProvideHassLitMixin } from "../../src/mixins/provide-hass-lit-mixin";
|
||||||
import { urlSyncMixin } from "../../src/state/url-sync-mixin";
|
import { urlSyncMixin } from "../../src/state/url-sync-mixin";
|
||||||
import { HomeAssistant, Route } from "../../src/types";
|
import { HomeAssistant } from "../../src/types";
|
||||||
import { getTranslation } from "../../src/util/common-translation";
|
import { getTranslation } from "../../src/util/common-translation";
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
@@ -38,8 +38,6 @@ declare global {
|
|||||||
export class SupervisorBaseElement extends urlSyncMixin(
|
export class SupervisorBaseElement extends urlSyncMixin(
|
||||||
ProvideHassLitMixin(LitElement)
|
ProvideHassLitMixin(LitElement)
|
||||||
) {
|
) {
|
||||||
@property({ attribute: false }) public route?: Route;
|
|
||||||
|
|
||||||
@property({ attribute: false }) public supervisor: Partial<Supervisor> = {
|
@property({ attribute: false }) public supervisor: Partial<Supervisor> = {
|
||||||
localize: () => "",
|
localize: () => "",
|
||||||
};
|
};
|
||||||
@@ -110,9 +108,7 @@ export class SupervisorBaseElement extends urlSyncMixin(
|
|||||||
this._language = this.hass.language;
|
this._language = this.hass.language;
|
||||||
}
|
}
|
||||||
this._initializeLocalize();
|
this._initializeLocalize();
|
||||||
if (this.route?.prefix === "/hassio") {
|
this._initSupervisor();
|
||||||
this._initSupervisor();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _initializeLocalize() {
|
private async _initializeLocalize() {
|
||||||
|
@@ -2,7 +2,7 @@ import "@material/mwc-button";
|
|||||||
import "@material/mwc-list/mwc-list-item";
|
import "@material/mwc-list/mwc-list-item";
|
||||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { atLeastVersion } from "../../../src/common/config/version";
|
import { fireEvent } from "../../../src/common/dom/fire_event";
|
||||||
import "../../../src/components/buttons/ha-progress-button";
|
import "../../../src/components/buttons/ha-progress-button";
|
||||||
import "../../../src/components/ha-button-menu";
|
import "../../../src/components/ha-button-menu";
|
||||||
import "../../../src/components/ha-card";
|
import "../../../src/components/ha-card";
|
||||||
@@ -12,7 +12,7 @@ import {
|
|||||||
fetchHassioStats,
|
fetchHassioStats,
|
||||||
HassioStats,
|
HassioStats,
|
||||||
} from "../../../src/data/hassio/common";
|
} from "../../../src/data/hassio/common";
|
||||||
import { restartCore } from "../../../src/data/supervisor/core";
|
import { restartCore, updateCore } from "../../../src/data/supervisor/core";
|
||||||
import { Supervisor } from "../../../src/data/supervisor/supervisor";
|
import { Supervisor } from "../../../src/data/supervisor/supervisor";
|
||||||
import {
|
import {
|
||||||
showAlertDialog,
|
showAlertDialog,
|
||||||
@@ -22,6 +22,7 @@ import { haStyle } from "../../../src/resources/styles";
|
|||||||
import { HomeAssistant } from "../../../src/types";
|
import { HomeAssistant } from "../../../src/types";
|
||||||
import { bytesToString } from "../../../src/util/bytes-to-string";
|
import { bytesToString } from "../../../src/util/bytes-to-string";
|
||||||
import "../components/supervisor-metric";
|
import "../components/supervisor-metric";
|
||||||
|
import { showDialogSupervisorUpdate } from "../dialogs/update/show-dialog-update";
|
||||||
import { hassioStyle } from "../resources/hassio-style";
|
import { hassioStyle } from "../resources/hassio-style";
|
||||||
|
|
||||||
@customElement("hassio-core-info")
|
@customElement("hassio-core-info")
|
||||||
@@ -66,15 +67,14 @@ class HassioCoreInfo extends LitElement {
|
|||||||
<span slot="description">
|
<span slot="description">
|
||||||
core-${this.supervisor.core.version_latest}
|
core-${this.supervisor.core.version_latest}
|
||||||
</span>
|
</span>
|
||||||
${!atLeastVersion(this.hass.config.version, 2021, 12) &&
|
${this.supervisor.core.update_available
|
||||||
this.supervisor.core.update_available
|
|
||||||
? html`
|
? html`
|
||||||
<a href="/hassio/update-available/core">
|
<ha-progress-button
|
||||||
<mwc-button
|
.title=${this.supervisor.localize("common.update")}
|
||||||
.label=${this.supervisor.localize("common.show")}
|
@click=${this._coreUpdate}
|
||||||
>
|
>
|
||||||
</mwc-button>
|
${this.supervisor.localize("common.update")}
|
||||||
</a>
|
</ha-progress-button>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
</ha-settings-row>
|
</ha-settings-row>
|
||||||
@@ -160,6 +160,27 @@ class HassioCoreInfo extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async _coreUpdate(): Promise<void> {
|
||||||
|
showDialogSupervisorUpdate(this, {
|
||||||
|
supervisor: this.supervisor,
|
||||||
|
name: "Home Assistant Core",
|
||||||
|
version: this.supervisor.core.version_latest,
|
||||||
|
backupParams: {
|
||||||
|
name: `core_${this.supervisor.core.version}`,
|
||||||
|
folders: ["homeassistant"],
|
||||||
|
homeassistant: true,
|
||||||
|
},
|
||||||
|
updateHandler: async () => this._updateCore(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _updateCore(): Promise<void> {
|
||||||
|
await updateCore(this.hass);
|
||||||
|
fireEvent(this, "supervisor-collection-refresh", {
|
||||||
|
collection: "core",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
static get styles(): CSSResultGroup {
|
||||||
return [
|
return [
|
||||||
haStyle,
|
haStyle,
|
||||||
@@ -218,9 +239,6 @@ class HassioCoreInfo extends LitElement {
|
|||||||
mwc-list-item ha-svg-icon {
|
mwc-list-item ha-svg-icon {
|
||||||
color: var(--secondary-text-color);
|
color: var(--secondary-text-color);
|
||||||
}
|
}
|
||||||
a {
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
`,
|
`,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@@ -9,7 +9,6 @@ import { fireEvent } from "../../../src/common/dom/fire_event";
|
|||||||
import "../../../src/components/buttons/ha-progress-button";
|
import "../../../src/components/buttons/ha-progress-button";
|
||||||
import "../../../src/components/ha-button-menu";
|
import "../../../src/components/ha-button-menu";
|
||||||
import "../../../src/components/ha-card";
|
import "../../../src/components/ha-card";
|
||||||
import "../../../src/components/ha-icon-button";
|
|
||||||
import "../../../src/components/ha-settings-row";
|
import "../../../src/components/ha-settings-row";
|
||||||
import {
|
import {
|
||||||
extractApiErrorMessage,
|
extractApiErrorMessage,
|
||||||
@@ -21,6 +20,7 @@ import {
|
|||||||
configSyncOS,
|
configSyncOS,
|
||||||
rebootHost,
|
rebootHost,
|
||||||
shutdownHost,
|
shutdownHost,
|
||||||
|
updateOS,
|
||||||
} from "../../../src/data/hassio/host";
|
} from "../../../src/data/hassio/host";
|
||||||
import {
|
import {
|
||||||
fetchNetworkInfo,
|
fetchNetworkInfo,
|
||||||
@@ -105,15 +105,11 @@ class HassioHostInfo extends LitElement {
|
|||||||
<span slot="description">
|
<span slot="description">
|
||||||
${this.supervisor.host.operating_system}
|
${this.supervisor.host.operating_system}
|
||||||
</span>
|
</span>
|
||||||
${!atLeastVersion(this.hass.config.version, 2021, 12) &&
|
${this.supervisor.os.update_available
|
||||||
this.supervisor.os.update_available
|
|
||||||
? html`
|
? html`
|
||||||
<a href="/hassio/update-available/os">
|
<ha-progress-button @click=${this._osUpdate}>
|
||||||
<mwc-button
|
${this.supervisor.localize("commmon.update")}
|
||||||
.label=${this.supervisor.localize("common.show")}
|
</ha-progress-button>
|
||||||
>
|
|
||||||
</mwc-button>
|
|
||||||
</a>
|
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
</ha-settings-row>
|
</ha-settings-row>
|
||||||
@@ -185,11 +181,9 @@ class HassioHostInfo extends LitElement {
|
|||||||
: ""}
|
: ""}
|
||||||
|
|
||||||
<ha-button-menu corner="BOTTOM_START">
|
<ha-button-menu corner="BOTTOM_START">
|
||||||
<ha-icon-button
|
<mwc-icon-button slot="trigger">
|
||||||
.label=${this.hass.localize("common.menu")}
|
<ha-svg-icon .path=${mdiDotsVertical}></ha-svg-icon>
|
||||||
.path=${mdiDotsVertical}
|
</mwc-icon-button>
|
||||||
slot="trigger"
|
|
||||||
></ha-icon-button>
|
|
||||||
<mwc-list-item
|
<mwc-list-item
|
||||||
.action=${"hardware"}
|
.action=${"hardware"}
|
||||||
@click=${this._handleMenuAction}
|
@click=${this._handleMenuAction}
|
||||||
@@ -336,6 +330,50 @@ class HassioHostInfo extends LitElement {
|
|||||||
button.progress = false;
|
button.progress = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async _osUpdate(ev: CustomEvent): Promise<void> {
|
||||||
|
const button = ev.currentTarget as any;
|
||||||
|
button.progress = true;
|
||||||
|
|
||||||
|
const confirmed = await showConfirmationDialog(this, {
|
||||||
|
title: this.supervisor.localize(
|
||||||
|
"confirm.update.title",
|
||||||
|
"name",
|
||||||
|
"Home Assistant Operating System"
|
||||||
|
),
|
||||||
|
text: this.supervisor.localize(
|
||||||
|
"confirm.update.text",
|
||||||
|
"name",
|
||||||
|
"Home Assistant Operating System",
|
||||||
|
"version",
|
||||||
|
this.supervisor.os.version_latest
|
||||||
|
),
|
||||||
|
confirmText: this.supervisor.localize("common.update"),
|
||||||
|
dismissText: "no",
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!confirmed) {
|
||||||
|
button.progress = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await updateOS(this.hass);
|
||||||
|
fireEvent(this, "supervisor-collection-refresh", { collection: "os" });
|
||||||
|
} catch (err: any) {
|
||||||
|
if (this.hass.connection.connected) {
|
||||||
|
showAlertDialog(this, {
|
||||||
|
title: this.supervisor.localize(
|
||||||
|
"common.failed_to_update_name",
|
||||||
|
"name",
|
||||||
|
"Home Assistant Operating System"
|
||||||
|
),
|
||||||
|
text: extractApiErrorMessage(err),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
button.progress = false;
|
||||||
|
}
|
||||||
|
|
||||||
private async _changeNetworkClicked(): Promise<void> {
|
private async _changeNetworkClicked(): Promise<void> {
|
||||||
showNetworkDialog(this, {
|
showNetworkDialog(this, {
|
||||||
supervisor: this.supervisor,
|
supervisor: this.supervisor,
|
||||||
@@ -453,9 +491,6 @@ class HassioHostInfo extends LitElement {
|
|||||||
mwc-list-item ha-svg-icon {
|
mwc-list-item ha-svg-icon {
|
||||||
color: var(--secondary-text-color);
|
color: var(--secondary-text-color);
|
||||||
}
|
}
|
||||||
a {
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
`,
|
`,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@@ -17,6 +17,7 @@ import {
|
|||||||
restartSupervisor,
|
restartSupervisor,
|
||||||
setSupervisorOption,
|
setSupervisorOption,
|
||||||
SupervisorOptions,
|
SupervisorOptions,
|
||||||
|
updateSupervisor,
|
||||||
} from "../../../src/data/hassio/supervisor";
|
} from "../../../src/data/hassio/supervisor";
|
||||||
import { Supervisor } from "../../../src/data/supervisor/supervisor";
|
import { Supervisor } from "../../../src/data/supervisor/supervisor";
|
||||||
import {
|
import {
|
||||||
@@ -30,9 +31,29 @@ import { documentationUrl } from "../../../src/util/documentation-url";
|
|||||||
import "../components/supervisor-metric";
|
import "../components/supervisor-metric";
|
||||||
import { hassioStyle } from "../resources/hassio-style";
|
import { hassioStyle } from "../resources/hassio-style";
|
||||||
|
|
||||||
const UNSUPPORTED_REASON_URL = {};
|
const UNSUPPORTED_REASON_URL = {
|
||||||
|
apparmor: "/more-info/unsupported/apparmor",
|
||||||
|
container: "/more-info/unsupported/container",
|
||||||
|
content_trust: "/more-info/unsupported/content_trust",
|
||||||
|
dbus: "/more-info/unsupported/dbus",
|
||||||
|
docker_configuration: "/more-info/unsupported/docker_configuration",
|
||||||
|
docker_version: "/more-info/unsupported/docker_version",
|
||||||
|
job_conditions: "/more-info/unsupported/job_conditions",
|
||||||
|
lxc: "/more-info/unsupported/lxc",
|
||||||
|
network_manager: "/more-info/unsupported/network_manager",
|
||||||
|
os_agent: "/more-info/unsupported/os_agent",
|
||||||
|
os: "/more-info/unsupported/os",
|
||||||
|
privileged: "/more-info/unsupported/privileged",
|
||||||
|
source_mods: "/more-info/unsupported/source_mods",
|
||||||
|
systemd: "/more-info/unsupported/systemd",
|
||||||
|
};
|
||||||
|
|
||||||
const UNHEALTHY_REASON_URL = {
|
const UNHEALTHY_REASON_URL = {
|
||||||
privileged: "/more-info/unsupported/privileged",
|
privileged: "/more-info/unsupported/privileged",
|
||||||
|
supervisor: "/more-info/unhealthy/supervisor",
|
||||||
|
setup: "/more-info/unhealthy/setup",
|
||||||
|
docker: "/more-info/unhealthy/docker",
|
||||||
|
untrusted: "/more-info/unhealthy/untrusted",
|
||||||
};
|
};
|
||||||
|
|
||||||
@customElement("hassio-supervisor-info")
|
@customElement("hassio-supervisor-info")
|
||||||
@@ -76,15 +97,16 @@ class HassioSupervisorInfo extends LitElement {
|
|||||||
<span slot="description">
|
<span slot="description">
|
||||||
supervisor-${this.supervisor.supervisor.version_latest}
|
supervisor-${this.supervisor.supervisor.version_latest}
|
||||||
</span>
|
</span>
|
||||||
${!atLeastVersion(this.hass.config.version, 2021, 12) &&
|
${this.supervisor.supervisor.update_available
|
||||||
this.supervisor.supervisor.update_available
|
|
||||||
? html`
|
? html`
|
||||||
<a href="/hassio/update-available/supervisor">
|
<ha-progress-button
|
||||||
<mwc-button
|
.title=${this.supervisor.localize(
|
||||||
.label=${this.supervisor.localize("common.show")}
|
"system.supervisor.update_supervisor"
|
||||||
>
|
)}
|
||||||
</mwc-button>
|
@click=${this._supervisorUpdate}
|
||||||
</a>
|
>
|
||||||
|
${this.supervisor.localize("common.update")}
|
||||||
|
</ha-progress-button>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
</ha-settings-row>
|
</ha-settings-row>
|
||||||
@@ -151,28 +173,24 @@ class HassioSupervisorInfo extends LitElement {
|
|||||||
></ha-switch>
|
></ha-switch>
|
||||||
</ha-settings-row>`
|
</ha-settings-row>`
|
||||||
: ""
|
: ""
|
||||||
: html`<ha-alert alert-type="warning">
|
: html`<ha-alert
|
||||||
|
alert-type="warning"
|
||||||
|
.actionText=${this.supervisor.localize("common.learn_more")}
|
||||||
|
@alert-action-clicked=${this._unsupportedDialog}
|
||||||
|
>
|
||||||
${this.supervisor.localize(
|
${this.supervisor.localize(
|
||||||
"system.supervisor.unsupported_title"
|
"system.supervisor.unsupported_title"
|
||||||
)}
|
)}
|
||||||
<mwc-button
|
|
||||||
slot="action"
|
|
||||||
.label=${this.supervisor.localize("common.learn_more")}
|
|
||||||
@click=${this._unsupportedDialog}
|
|
||||||
>
|
|
||||||
</mwc-button>
|
|
||||||
</ha-alert>`}
|
</ha-alert>`}
|
||||||
${!this.supervisor.supervisor.healthy
|
${!this.supervisor.supervisor.healthy
|
||||||
? html`<ha-alert alert-type="error">
|
? html`<ha-alert
|
||||||
|
alert-type="error"
|
||||||
|
.actionText=${this.supervisor.localize("common.learn_more")}
|
||||||
|
@alert-action-clicked=${this._unhealthyDialog}
|
||||||
|
>
|
||||||
${this.supervisor.localize(
|
${this.supervisor.localize(
|
||||||
"system.supervisor.unhealthy_title"
|
"system.supervisor.unhealthy_title"
|
||||||
)}
|
)}
|
||||||
<mwc-button
|
|
||||||
slot="action"
|
|
||||||
.label=${this.supervisor.localize("common.learn_more")}
|
|
||||||
@click=${this._unhealthyDialog}
|
|
||||||
>
|
|
||||||
</mwc-button>
|
|
||||||
</ha-alert>`
|
</ha-alert>`
|
||||||
: ""}
|
: ""}
|
||||||
</div>
|
</div>
|
||||||
@@ -339,6 +357,51 @@ class HassioSupervisorInfo extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async _supervisorUpdate(ev: CustomEvent): Promise<void> {
|
||||||
|
const button = ev.currentTarget as any;
|
||||||
|
button.progress = true;
|
||||||
|
|
||||||
|
const confirmed = await showConfirmationDialog(this, {
|
||||||
|
title: this.supervisor.localize(
|
||||||
|
"confirm.update.title",
|
||||||
|
"name",
|
||||||
|
"Supervisor"
|
||||||
|
),
|
||||||
|
text: this.supervisor.localize(
|
||||||
|
"confirm.update.text",
|
||||||
|
"name",
|
||||||
|
"Supervisor",
|
||||||
|
"version",
|
||||||
|
this.supervisor.supervisor.version_latest
|
||||||
|
),
|
||||||
|
confirmText: this.supervisor.localize("common.update"),
|
||||||
|
dismissText: this.supervisor.localize("common.cancel"),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!confirmed) {
|
||||||
|
button.progress = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await updateSupervisor(this.hass);
|
||||||
|
fireEvent(this, "supervisor-collection-refresh", {
|
||||||
|
collection: "supervisor",
|
||||||
|
});
|
||||||
|
} catch (err: any) {
|
||||||
|
showAlertDialog(this, {
|
||||||
|
title: this.supervisor.localize(
|
||||||
|
"common.failed_to_update_name",
|
||||||
|
"name",
|
||||||
|
"Supervisor"
|
||||||
|
),
|
||||||
|
text: extractApiErrorMessage(err),
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
button.progress = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async _diagnosticsInformationDialog(): Promise<void> {
|
private async _diagnosticsInformationDialog(): Promise<void> {
|
||||||
await showAlertDialog(this, {
|
await showAlertDialog(this, {
|
||||||
title: this.supervisor.localize(
|
title: this.supervisor.localize(
|
||||||
@@ -362,19 +425,20 @@ class HassioSupervisorInfo extends LitElement {
|
|||||||
${this.supervisor.resolution.unsupported.map(
|
${this.supervisor.resolution.unsupported.map(
|
||||||
(reason) => html`
|
(reason) => html`
|
||||||
<li>
|
<li>
|
||||||
<a
|
${UNSUPPORTED_REASON_URL[reason]
|
||||||
href=${documentationUrl(
|
? html`<a
|
||||||
this.hass,
|
href=${documentationUrl(
|
||||||
UNSUPPORTED_REASON_URL[reason] ||
|
this.hass,
|
||||||
`/more-info/unsupported/${reason}`
|
UNSUPPORTED_REASON_URL[reason]
|
||||||
)}
|
)}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noreferrer"
|
rel="noreferrer"
|
||||||
>
|
>
|
||||||
${this.supervisor.localize(
|
${this.supervisor.localize(
|
||||||
`system.supervisor.unsupported_reason.${reason}`
|
`system.supervisor.unsupported_reason.${reason}`
|
||||||
) || reason}
|
) || reason}
|
||||||
</a>
|
</a>`
|
||||||
|
: reason}
|
||||||
</li>
|
</li>
|
||||||
`
|
`
|
||||||
)}
|
)}
|
||||||
@@ -392,19 +456,20 @@ class HassioSupervisorInfo extends LitElement {
|
|||||||
${this.supervisor.resolution.unhealthy.map(
|
${this.supervisor.resolution.unhealthy.map(
|
||||||
(reason) => html`
|
(reason) => html`
|
||||||
<li>
|
<li>
|
||||||
<a
|
${UNHEALTHY_REASON_URL[reason]
|
||||||
href=${documentationUrl(
|
? html`<a
|
||||||
this.hass,
|
href=${documentationUrl(
|
||||||
UNHEALTHY_REASON_URL[reason] ||
|
this.hass,
|
||||||
`/more-info/unhealthy/${reason}`
|
UNHEALTHY_REASON_URL[reason]
|
||||||
)}
|
)}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noreferrer"
|
rel="noreferrer"
|
||||||
>
|
>
|
||||||
${this.supervisor.localize(
|
${this.supervisor.localize(
|
||||||
`system.supervisor.unhealthy_reason.${reason}`
|
`system.supervisor.unhealthy_reason.${reason}`
|
||||||
) || reason}
|
) || reason}
|
||||||
</a>
|
</a>`
|
||||||
|
: reason}
|
||||||
</li>
|
</li>
|
||||||
`
|
`
|
||||||
)}
|
)}
|
||||||
@@ -470,12 +535,6 @@ class HassioSupervisorInfo extends LitElement {
|
|||||||
white-space: normal;
|
white-space: normal;
|
||||||
color: var(--secondary-text-color);
|
color: var(--secondary-text-color);
|
||||||
}
|
}
|
||||||
ha-alert mwc-button {
|
|
||||||
--mdc-theme-primary: var(--primary-text-color);
|
|
||||||
}
|
|
||||||
a {
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
`,
|
`,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@@ -28,7 +28,7 @@ class HassioSystem extends LitElement {
|
|||||||
.localizeFunc=${this.supervisor.localize}
|
.localizeFunc=${this.supervisor.localize}
|
||||||
.narrow=${this.narrow}
|
.narrow=${this.narrow}
|
||||||
.route=${this.route}
|
.route=${this.route}
|
||||||
.tabs=${supervisorTabs(this.hass)}
|
.tabs=${supervisorTabs}
|
||||||
main-page
|
main-page
|
||||||
supervisor
|
supervisor
|
||||||
>
|
>
|
||||||
|
@@ -1,401 +0,0 @@
|
|||||||
import "@material/mwc-list/mwc-list-item";
|
|
||||||
import {
|
|
||||||
css,
|
|
||||||
CSSResultGroup,
|
|
||||||
html,
|
|
||||||
LitElement,
|
|
||||||
PropertyValues,
|
|
||||||
TemplateResult,
|
|
||||||
} from "lit";
|
|
||||||
import { customElement, property, state } from "lit/decorators";
|
|
||||||
import memoizeOne from "memoize-one";
|
|
||||||
import { fireEvent } from "../../../src/common/dom/fire_event";
|
|
||||||
import "../../../src/common/search/search-input";
|
|
||||||
import "../../../src/components/buttons/ha-progress-button";
|
|
||||||
import "../../../src/components/ha-alert";
|
|
||||||
import "../../../src/components/ha-button-menu";
|
|
||||||
import "../../../src/components/ha-card";
|
|
||||||
import "../../../src/components/ha-checkbox";
|
|
||||||
import "../../../src/components/ha-expansion-panel";
|
|
||||||
import "../../../src/components/ha-formfield";
|
|
||||||
import "../../../src/components/ha-icon-button";
|
|
||||||
import "../../../src/components/ha-markdown";
|
|
||||||
import "../../../src/components/ha-settings-row";
|
|
||||||
import "../../../src/components/ha-svg-icon";
|
|
||||||
import "../../../src/components/ha-switch";
|
|
||||||
import {
|
|
||||||
fetchHassioAddonChangelog,
|
|
||||||
fetchHassioAddonInfo,
|
|
||||||
HassioAddonDetails,
|
|
||||||
updateHassioAddon,
|
|
||||||
} from "../../../src/data/hassio/addon";
|
|
||||||
import {
|
|
||||||
createHassioPartialBackup,
|
|
||||||
HassioPartialBackupCreateParams,
|
|
||||||
} from "../../../src/data/hassio/backup";
|
|
||||||
import {
|
|
||||||
extractApiErrorMessage,
|
|
||||||
ignoreSupervisorError,
|
|
||||||
} from "../../../src/data/hassio/common";
|
|
||||||
import { updateOS } from "../../../src/data/hassio/host";
|
|
||||||
import { updateSupervisor } from "../../../src/data/hassio/supervisor";
|
|
||||||
import { updateCore } from "../../../src/data/supervisor/core";
|
|
||||||
import { StoreAddon } from "../../../src/data/supervisor/store";
|
|
||||||
import { Supervisor } from "../../../src/data/supervisor/supervisor";
|
|
||||||
import { showAlertDialog } from "../../../src/dialogs/generic/show-dialog-box";
|
|
||||||
import "../../../src/layouts/hass-loading-screen";
|
|
||||||
import "../../../src/layouts/hass-subpage";
|
|
||||||
import "../../../src/layouts/hass-tabs-subpage";
|
|
||||||
import { SUPERVISOR_UPDATE_NAMES } from "../../../src/panels/config/dashboard/ha-config-updates";
|
|
||||||
import { HomeAssistant, Route } from "../../../src/types";
|
|
||||||
import { documentationUrl } from "../../../src/util/documentation-url";
|
|
||||||
import { addonArchIsSupported, extractChangelog } from "../util/addon";
|
|
||||||
|
|
||||||
declare global {
|
|
||||||
interface HASSDomEvents {
|
|
||||||
"update-complete": undefined;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type updateType = "os" | "supervisor" | "core" | "addon";
|
|
||||||
|
|
||||||
const changelogUrl = (
|
|
||||||
hass: HomeAssistant,
|
|
||||||
entry: updateType,
|
|
||||||
version: string
|
|
||||||
): string | undefined => {
|
|
||||||
if (entry === "addon") {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
if (entry === "core") {
|
|
||||||
return version?.includes("dev")
|
|
||||||
? "https://github.com/home-assistant/core/commits/dev"
|
|
||||||
: documentationUrl(hass, "/latest-release-notes/");
|
|
||||||
}
|
|
||||||
if (entry === "os") {
|
|
||||||
return version?.includes("dev")
|
|
||||||
? "https://github.com/home-assistant/operating-system/commits/dev"
|
|
||||||
: `https://github.com/home-assistant/operating-system/releases/tag/${version}`;
|
|
||||||
}
|
|
||||||
if (entry === "supervisor") {
|
|
||||||
return version?.includes("dev")
|
|
||||||
? "https://github.com/home-assistant/supervisor/commits/main"
|
|
||||||
: `https://github.com/home-assistant/supervisor/releases/tag/${version}`;
|
|
||||||
}
|
|
||||||
return undefined;
|
|
||||||
};
|
|
||||||
|
|
||||||
@customElement("update-available-card")
|
|
||||||
class UpdateAvailableCard extends LitElement {
|
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
|
||||||
|
|
||||||
@property({ attribute: false }) public supervisor!: Supervisor;
|
|
||||||
|
|
||||||
@property({ attribute: false }) public route!: Route;
|
|
||||||
|
|
||||||
@property({ type: Boolean }) public narrow!: boolean;
|
|
||||||
|
|
||||||
@property({ attribute: false }) public addonSlug?: string;
|
|
||||||
|
|
||||||
@state() private _updateType?: updateType;
|
|
||||||
|
|
||||||
@state() private _changelogContent?: string;
|
|
||||||
|
|
||||||
@state() private _addonInfo?: HassioAddonDetails;
|
|
||||||
|
|
||||||
@state() private _action: "backup" | "update" | null = null;
|
|
||||||
|
|
||||||
@state() private _error?: string;
|
|
||||||
|
|
||||||
private _addonStoreInfo = memoizeOne(
|
|
||||||
(slug: string, storeAddons: StoreAddon[]) =>
|
|
||||||
storeAddons.find((addon) => addon.slug === slug)
|
|
||||||
);
|
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
|
||||||
if (
|
|
||||||
!this._updateType ||
|
|
||||||
(this._updateType === "addon" && !this._addonInfo)
|
|
||||||
) {
|
|
||||||
return html``;
|
|
||||||
}
|
|
||||||
|
|
||||||
const changelog = changelogUrl(this.hass, this._updateType, this._version);
|
|
||||||
|
|
||||||
return html`
|
|
||||||
<ha-card
|
|
||||||
.header=${this.supervisor.localize("update_available.update_name", {
|
|
||||||
name: this._name,
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
<div class="card-content">
|
|
||||||
${this._error
|
|
||||||
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
|
|
||||||
: ""}
|
|
||||||
${this._action === null
|
|
||||||
? html`
|
|
||||||
${this._changelogContent
|
|
||||||
? html`
|
|
||||||
<ha-expansion-panel header="Changelog" outlined>
|
|
||||||
<ha-markdown .content=${this._changelogContent}>
|
|
||||||
</ha-markdown>
|
|
||||||
</ha-expansion-panel>
|
|
||||||
`
|
|
||||||
: ""}
|
|
||||||
<div class="versions">
|
|
||||||
<p>
|
|
||||||
${this.supervisor.localize("update_available.description", {
|
|
||||||
name: this._name,
|
|
||||||
version: this._version,
|
|
||||||
newest_version: this._version_latest,
|
|
||||||
})}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
${["core", "addon"].includes(this._updateType)
|
|
||||||
? html`
|
|
||||||
<ha-formfield
|
|
||||||
.label=${this.supervisor.localize(
|
|
||||||
"update_available.create_backup"
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<ha-checkbox checked></ha-checkbox>
|
|
||||||
</ha-formfield>
|
|
||||||
`
|
|
||||||
: ""}
|
|
||||||
`
|
|
||||||
: html`<ha-circular-progress alt="Updating" size="large" active>
|
|
||||||
</ha-circular-progress>
|
|
||||||
<p class="progress-text">
|
|
||||||
${this._action === "update"
|
|
||||||
? this.supervisor.localize("update_available.updating", {
|
|
||||||
name: this._name,
|
|
||||||
version: this._version_latest,
|
|
||||||
})
|
|
||||||
: this.supervisor.localize(
|
|
||||||
"update_available.creating_backup",
|
|
||||||
{ name: this._name }
|
|
||||||
)}
|
|
||||||
</p>`}
|
|
||||||
</div>
|
|
||||||
${this._action === null
|
|
||||||
? html`
|
|
||||||
<div class="card-actions">
|
|
||||||
${changelog
|
|
||||||
? html`<a .href=${changelog} target="_blank" rel="noreferrer">
|
|
||||||
<mwc-button
|
|
||||||
.label=${this.supervisor.localize(
|
|
||||||
"update_available.open_release_notes"
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
</mwc-button>
|
|
||||||
</a>`
|
|
||||||
: ""}
|
|
||||||
<span></span>
|
|
||||||
<ha-progress-button
|
|
||||||
.disabled=${!this._version ||
|
|
||||||
(this._shouldCreateBackup &&
|
|
||||||
this.supervisor.info.state !== "running")}
|
|
||||||
@click=${this._update}
|
|
||||||
raised
|
|
||||||
>
|
|
||||||
${this.supervisor.localize("common.update")}
|
|
||||||
</ha-progress-button>
|
|
||||||
</div>
|
|
||||||
`
|
|
||||||
: ""}
|
|
||||||
</ha-card>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected firstUpdated(changedProps: PropertyValues) {
|
|
||||||
super.firstUpdated(changedProps);
|
|
||||||
const pathPart = this.route?.path.substring(1, this.route.path.length);
|
|
||||||
const updateType = ["core", "os", "supervisor"].includes(pathPart)
|
|
||||||
? pathPart
|
|
||||||
: "addon";
|
|
||||||
this._updateType = updateType as updateType;
|
|
||||||
|
|
||||||
if (updateType === "addon") {
|
|
||||||
if (!this.addonSlug) {
|
|
||||||
this.addonSlug = pathPart;
|
|
||||||
}
|
|
||||||
this._loadAddonData();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
get _shouldCreateBackup(): boolean {
|
|
||||||
return this.shadowRoot?.querySelector("ha-checkbox")?.checked || true;
|
|
||||||
}
|
|
||||||
|
|
||||||
get _version(): string {
|
|
||||||
return this._updateType
|
|
||||||
? this._updateType === "addon"
|
|
||||||
? this._addonInfo!.version
|
|
||||||
: this.supervisor[this._updateType]?.version || ""
|
|
||||||
: "";
|
|
||||||
}
|
|
||||||
|
|
||||||
get _version_latest(): string {
|
|
||||||
return this._updateType
|
|
||||||
? this._updateType === "addon"
|
|
||||||
? this._addonInfo!.version_latest
|
|
||||||
: this.supervisor[this._updateType]?.version_latest || ""
|
|
||||||
: "";
|
|
||||||
}
|
|
||||||
|
|
||||||
get _name(): string {
|
|
||||||
return this._updateType
|
|
||||||
? this._updateType === "addon"
|
|
||||||
? this._addonInfo!.name
|
|
||||||
: SUPERVISOR_UPDATE_NAMES[this._updateType]
|
|
||||||
: "";
|
|
||||||
}
|
|
||||||
|
|
||||||
private async _loadAddonData() {
|
|
||||||
try {
|
|
||||||
this._addonInfo = await fetchHassioAddonInfo(this.hass, this.addonSlug!);
|
|
||||||
} catch (err) {
|
|
||||||
showAlertDialog(this, {
|
|
||||||
title: this._updateType,
|
|
||||||
text: extractApiErrorMessage(err),
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const addonStoreInfo =
|
|
||||||
!this._addonInfo.detached && !this._addonInfo.available
|
|
||||||
? this._addonStoreInfo(
|
|
||||||
this._addonInfo.slug,
|
|
||||||
this.supervisor.store.addons
|
|
||||||
)
|
|
||||||
: undefined;
|
|
||||||
|
|
||||||
if (this._addonInfo.changelog) {
|
|
||||||
try {
|
|
||||||
const content = await fetchHassioAddonChangelog(
|
|
||||||
this.hass,
|
|
||||||
this.addonSlug!
|
|
||||||
);
|
|
||||||
this._changelogContent = extractChangelog(this._addonInfo, content);
|
|
||||||
} catch (err) {
|
|
||||||
this._error = extractApiErrorMessage(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this._addonInfo.available && addonStoreInfo) {
|
|
||||||
if (
|
|
||||||
!addonArchIsSupported(
|
|
||||||
this.supervisor.info.supported_arch,
|
|
||||||
this._addonInfo.arch
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
this._error = this.supervisor.localize(
|
|
||||||
"addon.dashboard.not_available_arch"
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
this._error = this.supervisor.localize(
|
|
||||||
"addon.dashboard.not_available_version",
|
|
||||||
{
|
|
||||||
core_version_installed: this.supervisor.core.version,
|
|
||||||
core_version_needed: addonStoreInfo.homeassistant,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async _update() {
|
|
||||||
this._error = undefined;
|
|
||||||
if (this._shouldCreateBackup) {
|
|
||||||
let backupArgs: HassioPartialBackupCreateParams;
|
|
||||||
if (this._updateType === "addon") {
|
|
||||||
backupArgs = {
|
|
||||||
name: `addon_${this.addonSlug}_${this._version}`,
|
|
||||||
addons: [this.addonSlug!],
|
|
||||||
homeassistant: false,
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
backupArgs = {
|
|
||||||
name: `${this._updateType}_${this._version}`,
|
|
||||||
folders: ["homeassistant"],
|
|
||||||
homeassistant: true,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
this._action = "backup";
|
|
||||||
try {
|
|
||||||
await createHassioPartialBackup(this.hass, backupArgs);
|
|
||||||
} catch (err: any) {
|
|
||||||
this._error = extractApiErrorMessage(err);
|
|
||||||
this._action = null;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this._action = "update";
|
|
||||||
try {
|
|
||||||
if (this._updateType === "addon") {
|
|
||||||
await updateHassioAddon(this.hass, this.addonSlug!);
|
|
||||||
} else if (this._updateType === "core") {
|
|
||||||
await updateCore(this.hass);
|
|
||||||
} else if (this._updateType === "os") {
|
|
||||||
await updateOS(this.hass);
|
|
||||||
} else if (this._updateType === "supervisor") {
|
|
||||||
await updateSupervisor(this.hass);
|
|
||||||
}
|
|
||||||
} catch (err: any) {
|
|
||||||
if (this.hass.connection.connected && !ignoreSupervisorError(err)) {
|
|
||||||
this._error = extractApiErrorMessage(err);
|
|
||||||
this._action = null;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fireEvent(this, "update-complete");
|
|
||||||
}
|
|
||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
|
||||||
return css`
|
|
||||||
:host {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
ha-card {
|
|
||||||
margin: auto;
|
|
||||||
}
|
|
||||||
a {
|
|
||||||
text-decoration: none;
|
|
||||||
color: var(--primary-text-color);
|
|
||||||
}
|
|
||||||
ha-settings-row {
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
.card-actions {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
border-top: none;
|
|
||||||
padding: 0 8px 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
ha-circular-progress {
|
|
||||||
display: block;
|
|
||||||
margin: 32px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.progress-text {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
ha-markdown {
|
|
||||||
padding-bottom: 8px;
|
|
||||||
}
|
|
||||||
ha-formfield {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
declare global {
|
|
||||||
interface HTMLElementTagNameMap {
|
|
||||||
"update-available-card": UpdateAvailableCard;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,59 +0,0 @@
|
|||||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
|
||||||
import { customElement, property } from "lit/decorators";
|
|
||||||
import { Supervisor } from "../../../src/data/supervisor/supervisor";
|
|
||||||
import "../../../src/layouts/hass-subpage";
|
|
||||||
import { HomeAssistant, Route } from "../../../src/types";
|
|
||||||
import "./update-available-card";
|
|
||||||
|
|
||||||
@customElement("update-available-dashboard")
|
|
||||||
class UpdateAvailableDashboard extends LitElement {
|
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
|
||||||
|
|
||||||
@property({ attribute: false }) public supervisor!: Supervisor;
|
|
||||||
|
|
||||||
@property({ type: Boolean }) public narrow!: boolean;
|
|
||||||
|
|
||||||
@property({ attribute: false }) public route!: Route;
|
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
|
||||||
return html`
|
|
||||||
<hass-subpage
|
|
||||||
.hass=${this.hass}
|
|
||||||
.narrow=${this.narrow}
|
|
||||||
.route=${this.route}
|
|
||||||
>
|
|
||||||
<update-available-card
|
|
||||||
.hass=${this.hass}
|
|
||||||
.supervisor=${this.supervisor}
|
|
||||||
.route=${this.route}
|
|
||||||
.narrow=${this.narrow}
|
|
||||||
@update-complete=${this._updateComplete}
|
|
||||||
></update-available-card>
|
|
||||||
</hass-subpage>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
private _updateComplete() {
|
|
||||||
history.back();
|
|
||||||
}
|
|
||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
|
||||||
return css`
|
|
||||||
hass-subpage {
|
|
||||||
--app-header-background-color: var(--primary-background-color);
|
|
||||||
--app-header-text-color: var(--sidebar-text-color);
|
|
||||||
}
|
|
||||||
update-available-card {
|
|
||||||
margin: auto;
|
|
||||||
margin-top: 16px;
|
|
||||||
max-width: 600px;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
declare global {
|
|
||||||
interface HTMLElementTagNameMap {
|
|
||||||
"update-available-dashboard": UpdateAvailableDashboard;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,30 +1,7 @@
|
|||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
import { HassioAddonDetails } from "../../../src/data/hassio/addon";
|
|
||||||
import { SupervisorArch } from "../../../src/data/supervisor/supervisor";
|
import { SupervisorArch } from "../../../src/data/supervisor/supervisor";
|
||||||
|
|
||||||
export const addonArchIsSupported = memoizeOne(
|
export const addonArchIsSupported = memoizeOne(
|
||||||
(supported_archs: SupervisorArch[], addon_archs: SupervisorArch[]) =>
|
(supported_archs: SupervisorArch[], addon_archs: SupervisorArch[]) =>
|
||||||
addon_archs.some((arch) => supported_archs.includes(arch))
|
addon_archs.some((arch) => supported_archs.includes(arch))
|
||||||
);
|
);
|
||||||
|
|
||||||
export const extractChangelog = (
|
|
||||||
addon: HassioAddonDetails,
|
|
||||||
content: string
|
|
||||||
): string => {
|
|
||||||
if (content.startsWith("# Changelog")) {
|
|
||||||
content = content.substr(12, content.length);
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
content.includes(`# ${addon.version}`) &&
|
|
||||||
content.includes(`# ${addon.version_latest}`)
|
|
||||||
) {
|
|
||||||
const newcontent = content.split(`# ${addon.version}`)[0];
|
|
||||||
if (newcontent.includes(`# ${addon.version_latest}`)) {
|
|
||||||
// Only change the content if the new version still exist
|
|
||||||
// if the changelog does not have the newests version on top
|
|
||||||
// this will not be true, and we don't modify the content
|
|
||||||
content = newcontent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return content;
|
|
||||||
};
|
|
||||||
|
97
package.json
97
package.json
@@ -22,23 +22,23 @@
|
|||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@braintree/sanitize-url": "^5.0.2",
|
"@braintree/sanitize-url": "^5.0.2",
|
||||||
"@codemirror/commands": "^0.19.5",
|
"@codemirror/commands": "^0.19.2",
|
||||||
"@codemirror/gutter": "^0.19.4",
|
"@codemirror/gutter": "^0.19.1",
|
||||||
"@codemirror/highlight": "^0.19.6",
|
"@codemirror/highlight": "^0.19.2",
|
||||||
"@codemirror/history": "^0.19.0",
|
"@codemirror/history": "^0.19.0",
|
||||||
"@codemirror/legacy-modes": "^0.19.0",
|
"@codemirror/legacy-modes": "^0.19.0",
|
||||||
"@codemirror/rectangular-selection": "^0.19.1",
|
"@codemirror/rectangular-selection": "^0.19.0",
|
||||||
"@codemirror/search": "^0.19.2",
|
"@codemirror/search": "^0.19.0",
|
||||||
"@codemirror/state": "^0.19.4",
|
"@codemirror/state": "^0.19.1",
|
||||||
"@codemirror/stream-parser": "^0.19.2",
|
"@codemirror/stream-parser": "^0.19.1",
|
||||||
"@codemirror/text": "^0.19.5",
|
"@codemirror/text": "^0.19.2",
|
||||||
"@codemirror/view": "^0.19.15",
|
"@codemirror/view": "^0.19.4",
|
||||||
"@formatjs/intl-datetimeformat": "^4.2.5",
|
"@formatjs/intl-datetimeformat": "^4.2.4",
|
||||||
"@formatjs/intl-getcanonicallocales": "^1.8.0",
|
"@formatjs/intl-getcanonicallocales": "^1.7.3",
|
||||||
"@formatjs/intl-locale": "^2.4.40",
|
"@formatjs/intl-locale": "^2.4.38",
|
||||||
"@formatjs/intl-numberformat": "^7.2.5",
|
"@formatjs/intl-numberformat": "^7.2.4",
|
||||||
"@formatjs/intl-pluralrules": "^4.1.5",
|
"@formatjs/intl-pluralrules": "^4.1.4",
|
||||||
"@formatjs/intl-relativetimeformat": "^9.3.2",
|
"@formatjs/intl-relativetimeformat": "^9.3.1",
|
||||||
"@formatjs/intl-utils": "^3.8.4",
|
"@formatjs/intl-utils": "^3.8.4",
|
||||||
"@fullcalendar/common": "5.9.0",
|
"@fullcalendar/common": "5.9.0",
|
||||||
"@fullcalendar/core": "5.9.0",
|
"@fullcalendar/core": "5.9.0",
|
||||||
@@ -46,38 +46,45 @@
|
|||||||
"@fullcalendar/interaction": "5.9.0",
|
"@fullcalendar/interaction": "5.9.0",
|
||||||
"@fullcalendar/list": "5.9.0",
|
"@fullcalendar/list": "5.9.0",
|
||||||
"@lit-labs/virtualizer": "patch:@lit-labs/virtualizer@0.6.0#./.yarn/patches/@lit-labs/virtualizer/0.7.0.patch",
|
"@lit-labs/virtualizer": "patch:@lit-labs/virtualizer@0.6.0#./.yarn/patches/@lit-labs/virtualizer/0.7.0.patch",
|
||||||
"@material/chips": "14.0.0-canary.261f2db59.0",
|
"@material/chips": "13.0.0-canary.65125b3a6.0",
|
||||||
"@material/data-table": "14.0.0-canary.261f2db59.0",
|
"@material/data-table": "13.0.0-canary.65125b3a6.0",
|
||||||
"@material/mwc-button": "0.25.3",
|
"@material/mwc-button": "0.25.1",
|
||||||
"@material/mwc-checkbox": "0.25.3",
|
"@material/mwc-checkbox": "0.25.1",
|
||||||
"@material/mwc-circular-progress": "0.25.3",
|
"@material/mwc-circular-progress": "0.25.1",
|
||||||
"@material/mwc-dialog": "0.25.3",
|
"@material/mwc-dialog": "0.25.1",
|
||||||
"@material/mwc-fab": "0.25.3",
|
"@material/mwc-fab": "0.25.1",
|
||||||
"@material/mwc-formfield": "0.25.3",
|
"@material/mwc-formfield": "0.25.1",
|
||||||
"@material/mwc-icon-button": "patch:@material/mwc-icon-button@0.25.3#./.yarn/patches/@material/mwc-icon-button/remove-icon.patch",
|
"@material/mwc-icon-button": "0.25.1",
|
||||||
"@material/mwc-linear-progress": "0.25.3",
|
"@material/mwc-linear-progress": "0.25.1",
|
||||||
"@material/mwc-list": "0.25.3",
|
"@material/mwc-list": "0.25.1",
|
||||||
"@material/mwc-menu": "0.25.3",
|
"@material/mwc-menu": "0.25.1",
|
||||||
"@material/mwc-radio": "0.25.3",
|
"@material/mwc-radio": "0.25.1",
|
||||||
"@material/mwc-ripple": "0.25.3",
|
"@material/mwc-ripple": "0.25.1",
|
||||||
"@material/mwc-select": "0.25.3",
|
"@material/mwc-switch": "0.25.1",
|
||||||
"@material/mwc-slider": "0.25.3",
|
"@material/mwc-tab": "0.25.1",
|
||||||
"@material/mwc-switch": "0.25.3",
|
"@material/mwc-tab-bar": "0.25.1",
|
||||||
"@material/mwc-tab": "0.25.3",
|
"@material/top-app-bar": "13.0.0-canary.65125b3a6.0",
|
||||||
"@material/mwc-tab-bar": "0.25.3",
|
"@mdi/js": "6.1.95",
|
||||||
"@material/mwc-textfield": "0.25.3",
|
"@mdi/svg": "6.1.95",
|
||||||
"@material/top-app-bar": "14.0.0-canary.261f2db59.0",
|
|
||||||
"@mdi/js": "6.5.95",
|
|
||||||
"@mdi/svg": "6.5.95",
|
|
||||||
"@polymer/app-layout": "^3.1.0",
|
"@polymer/app-layout": "^3.1.0",
|
||||||
"@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",
|
||||||
"@polymer/iron-input": "^3.0.1",
|
"@polymer/iron-input": "^3.0.1",
|
||||||
|
"@polymer/iron-overlay-behavior": "^3.0.3",
|
||||||
"@polymer/iron-resizable-behavior": "^3.0.1",
|
"@polymer/iron-resizable-behavior": "^3.0.1",
|
||||||
|
"@polymer/paper-checkbox": "^3.1.0",
|
||||||
|
"@polymer/paper-dialog": "^3.0.1",
|
||||||
|
"@polymer/paper-dialog-behavior": "^3.0.1",
|
||||||
|
"@polymer/paper-dialog-scrollable": "^3.0.1",
|
||||||
"@polymer/paper-dropdown-menu": "^3.2.0",
|
"@polymer/paper-dropdown-menu": "^3.2.0",
|
||||||
"@polymer/paper-input": "^3.2.1",
|
"@polymer/paper-input": "^3.2.1",
|
||||||
"@polymer/paper-item": "^3.0.1",
|
"@polymer/paper-item": "^3.0.1",
|
||||||
"@polymer/paper-listbox": "^3.0.1",
|
"@polymer/paper-listbox": "^3.0.1",
|
||||||
|
"@polymer/paper-menu-button": "^3.1.0",
|
||||||
|
"@polymer/paper-progress": "^3.0.1",
|
||||||
|
"@polymer/paper-radio-button": "^3.0.1",
|
||||||
|
"@polymer/paper-radio-group": "^3.0.1",
|
||||||
|
"@polymer/paper-ripple": "^3.0.2",
|
||||||
"@polymer/paper-slider": "^3.0.1",
|
"@polymer/paper-slider": "^3.0.1",
|
||||||
"@polymer/paper-styles": "^3.0.1",
|
"@polymer/paper-styles": "^3.0.1",
|
||||||
"@polymer/paper-tabs": "^3.1.0",
|
"@polymer/paper-tabs": "^3.1.0",
|
||||||
@@ -101,14 +108,14 @@
|
|||||||
"deep-freeze": "^0.0.1",
|
"deep-freeze": "^0.0.1",
|
||||||
"fuse.js": "^6.0.0",
|
"fuse.js": "^6.0.0",
|
||||||
"google-timezones-json": "^1.0.2",
|
"google-timezones-json": "^1.0.2",
|
||||||
"hls.js": "^1.0.11",
|
"hls.js": "^1.0.10",
|
||||||
"home-assistant-js-websocket": "^5.11.1",
|
"home-assistant-js-websocket": "^5.11.1",
|
||||||
"idb-keyval": "^5.1.3",
|
"idb-keyval": "^5.1.3",
|
||||||
"intl-messageformat": "^9.9.1",
|
"intl-messageformat": "^9.9.1",
|
||||||
"js-yaml": "^4.1.0",
|
"js-yaml": "^4.1.0",
|
||||||
"leaflet": "^1.7.1",
|
"leaflet": "^1.7.1",
|
||||||
"leaflet-draw": "^1.0.4",
|
"leaflet-draw": "^1.0.4",
|
||||||
"lit": "^2.0.2",
|
"lit": "^2.0.0",
|
||||||
"lit-vaadin-helpers": "^0.2.1",
|
"lit-vaadin-helpers": "^0.2.1",
|
||||||
"marked": "^3.0.2",
|
"marked": "^3.0.2",
|
||||||
"memoize-one": "^5.2.1",
|
"memoize-one": "^5.2.1",
|
||||||
@@ -180,7 +187,7 @@
|
|||||||
"eslint-import-resolver-webpack": "^0.13.1",
|
"eslint-import-resolver-webpack": "^0.13.1",
|
||||||
"eslint-plugin-disable": "^2.0.1",
|
"eslint-plugin-disable": "^2.0.1",
|
||||||
"eslint-plugin-import": "^2.24.2",
|
"eslint-plugin-import": "^2.24.2",
|
||||||
"eslint-plugin-lit": "^1.6.1",
|
"eslint-plugin-lit": "^1.5.1",
|
||||||
"eslint-plugin-prettier": "^4.0.0",
|
"eslint-plugin-prettier": "^4.0.0",
|
||||||
"eslint-plugin-unused-imports": "^1.1.5",
|
"eslint-plugin-unused-imports": "^1.1.5",
|
||||||
"eslint-plugin-wc": "^1.3.2",
|
"eslint-plugin-wc": "^1.3.2",
|
||||||
@@ -230,10 +237,10 @@
|
|||||||
"resolutions": {
|
"resolutions": {
|
||||||
"@polymer/polymer": "patch:@polymer/polymer@3.4.1#./.yarn/patches/@polymer/polymer/pr-5569.patch",
|
"@polymer/polymer": "patch:@polymer/polymer@3.4.1#./.yarn/patches/@polymer/polymer/pr-5569.patch",
|
||||||
"@webcomponents/webcomponentsjs": "^2.2.10",
|
"@webcomponents/webcomponentsjs": "^2.2.10",
|
||||||
"lit": "^2.0.2",
|
"lit": "^2.0.0",
|
||||||
"lit-html": "2.0.1",
|
"lit-html": "2.0.0",
|
||||||
"lit-element": "3.0.1",
|
"lit-element": "3.0.0",
|
||||||
"@lit/reactive-element": "1.0.1"
|
"@lit/reactive-element": "1.0.0"
|
||||||
},
|
},
|
||||||
"main": "src/home-assistant.js",
|
"main": "src/home-assistant.js",
|
||||||
"husky": {
|
"husky": {
|
||||||
|
2
setup.py
2
setup.py
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
|
|||||||
|
|
||||||
setup(
|
setup(
|
||||||
name="home-assistant-frontend",
|
name="home-assistant-frontend",
|
||||||
version="20211123.0",
|
version="20210930.0",
|
||||||
description="The Home Assistant frontend",
|
description="The Home Assistant frontend",
|
||||||
url="https://github.com/home-assistant/frontend",
|
url="https://github.com/home-assistant/frontend",
|
||||||
author="The Home Assistant Authors",
|
author="The Home Assistant Authors",
|
||||||
|
@@ -1,5 +1,4 @@
|
|||||||
import "@material/mwc-button";
|
import "@material/mwc-button";
|
||||||
import { genClientId } from "home-assistant-js-websocket";
|
|
||||||
import {
|
import {
|
||||||
css,
|
css,
|
||||||
CSSResultGroup,
|
CSSResultGroup,
|
||||||
@@ -8,12 +7,9 @@ import {
|
|||||||
PropertyValues,
|
PropertyValues,
|
||||||
TemplateResult,
|
TemplateResult,
|
||||||
} from "lit";
|
} from "lit";
|
||||||
|
import "./ha-password-manager-polyfill";
|
||||||
import { property, state } from "lit/decorators";
|
import { property, state } from "lit/decorators";
|
||||||
import "../components/ha-alert";
|
|
||||||
import "../components/ha-checkbox";
|
|
||||||
import { computeInitialHaFormData } from "../components/ha-form/compute-initial-ha-form-data";
|
|
||||||
import "../components/ha-form/ha-form";
|
import "../components/ha-form/ha-form";
|
||||||
import "../components/ha-formfield";
|
|
||||||
import "../components/ha-markdown";
|
import "../components/ha-markdown";
|
||||||
import { AuthProvider } from "../data/auth";
|
import { AuthProvider } from "../data/auth";
|
||||||
import {
|
import {
|
||||||
@@ -21,7 +17,6 @@ import {
|
|||||||
DataEntryFlowStepForm,
|
DataEntryFlowStepForm,
|
||||||
} from "../data/data_entry_flow";
|
} from "../data/data_entry_flow";
|
||||||
import { litLocalizeLiteMixin } from "../mixins/lit-localize-lite-mixin";
|
import { litLocalizeLiteMixin } from "../mixins/lit-localize-lite-mixin";
|
||||||
import "./ha-password-manager-polyfill";
|
|
||||||
|
|
||||||
type State = "loading" | "error" | "step";
|
type State = "loading" | "error" | "step";
|
||||||
|
|
||||||
@@ -36,44 +31,12 @@ class HaAuthFlow extends litLocalizeLiteMixin(LitElement) {
|
|||||||
|
|
||||||
@state() private _state: State = "loading";
|
@state() private _state: State = "loading";
|
||||||
|
|
||||||
@state() private _stepData?: Record<string, any>;
|
@state() private _stepData: any = {};
|
||||||
|
|
||||||
@state() private _step?: DataEntryFlowStep;
|
@state() private _step?: DataEntryFlowStep;
|
||||||
|
|
||||||
@state() private _errorMessage?: string;
|
@state() private _errorMessage?: string;
|
||||||
|
|
||||||
@state() private _submitting = false;
|
|
||||||
|
|
||||||
@state() private _storeToken = false;
|
|
||||||
|
|
||||||
willUpdate(changedProps: PropertyValues) {
|
|
||||||
super.willUpdate(changedProps);
|
|
||||||
|
|
||||||
if (!changedProps.has("_step")) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this._step) {
|
|
||||||
this._stepData = undefined;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const oldStep = changedProps.get("_step") as HaAuthFlow["_step"];
|
|
||||||
|
|
||||||
if (
|
|
||||||
!oldStep ||
|
|
||||||
this._step.flow_id !== oldStep.flow_id ||
|
|
||||||
(this._step.type === "form" &&
|
|
||||||
oldStep.type === "form" &&
|
|
||||||
this._step.step_id !== oldStep.step_id)
|
|
||||||
) {
|
|
||||||
this._stepData =
|
|
||||||
this._step.type === "form"
|
|
||||||
? computeInitialHaFormData(this._step.data_schema)
|
|
||||||
: undefined;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected render() {
|
protected render() {
|
||||||
return html`
|
return html`
|
||||||
<form>${this._renderForm()}</form>
|
<form>${this._renderForm()}</form>
|
||||||
@@ -113,24 +76,6 @@ class HaAuthFlow extends litLocalizeLiteMixin(LitElement) {
|
|||||||
if (changedProps.has("authProvider")) {
|
if (changedProps.has("authProvider")) {
|
||||||
this._providerChanged(this.authProvider);
|
this._providerChanged(this.authProvider);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!changedProps.has("_step") || this._step?.type !== "form") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 100ms to give all the form elements time to initialize.
|
|
||||||
setTimeout(() => {
|
|
||||||
const form = this.renderRoot.querySelector("ha-form");
|
|
||||||
if (form) {
|
|
||||||
(form as any).focus();
|
|
||||||
}
|
|
||||||
}, 100);
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
this.renderRoot.querySelector(
|
|
||||||
"ha-password-manager-polyfill"
|
|
||||||
)!.boundingRect = this.getBoundingClientRect();
|
|
||||||
}, 500);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private _renderForm(): TemplateResult {
|
private _renderForm(): TemplateResult {
|
||||||
@@ -142,33 +87,27 @@ class HaAuthFlow extends litLocalizeLiteMixin(LitElement) {
|
|||||||
return html`
|
return html`
|
||||||
${this._renderStep(this._step)}
|
${this._renderStep(this._step)}
|
||||||
<div class="action">
|
<div class="action">
|
||||||
<mwc-button
|
<mwc-button raised @click=${this._handleSubmit}
|
||||||
raised
|
>${this._step.type === "form"
|
||||||
@click=${this._handleSubmit}
|
|
||||||
.disabled=${this._submitting}
|
|
||||||
>
|
|
||||||
${this._step.type === "form"
|
|
||||||
? this.localize("ui.panel.page-authorize.form.next")
|
? this.localize("ui.panel.page-authorize.form.next")
|
||||||
: this.localize("ui.panel.page-authorize.form.start_over")}
|
: this.localize(
|
||||||
</mwc-button>
|
"ui.panel.page-authorize.form.start_over"
|
||||||
|
)}</mwc-button
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
case "error":
|
case "error":
|
||||||
return html`
|
return html`
|
||||||
<ha-alert alert-type="error">
|
<div class="error">
|
||||||
${this.localize(
|
${this.localize(
|
||||||
"ui.panel.page-authorize.form.error",
|
"ui.panel.page-authorize.form.error",
|
||||||
"error",
|
"error",
|
||||||
this._errorMessage
|
this._errorMessage
|
||||||
)}
|
)}
|
||||||
</ha-alert>
|
</div>
|
||||||
`;
|
`;
|
||||||
case "loading":
|
case "loading":
|
||||||
return html`
|
return html` ${this.localize("ui.panel.page-authorize.form.working")} `;
|
||||||
<ha-alert alert-type="info">
|
|
||||||
${this.localize("ui.panel.page-authorize.form.working")}
|
|
||||||
</ha-alert>
|
|
||||||
`;
|
|
||||||
default:
|
default:
|
||||||
return html``;
|
return html``;
|
||||||
}
|
}
|
||||||
@@ -201,35 +140,16 @@ class HaAuthFlow extends litLocalizeLiteMixin(LitElement) {
|
|||||||
.data=${this._stepData}
|
.data=${this._stepData}
|
||||||
.schema=${step.data_schema}
|
.schema=${step.data_schema}
|
||||||
.error=${step.errors}
|
.error=${step.errors}
|
||||||
.disabled=${this._submitting}
|
|
||||||
.computeLabel=${this._computeLabelCallback(step)}
|
.computeLabel=${this._computeLabelCallback(step)}
|
||||||
.computeError=${this._computeErrorCallback(step)}
|
.computeError=${this._computeErrorCallback(step)}
|
||||||
@value-changed=${this._stepDataChanged}
|
@value-changed=${this._stepDataChanged}
|
||||||
></ha-form>
|
></ha-form>
|
||||||
${this.clientId === genClientId() &&
|
|
||||||
!["select_mfa_module", "mfa"].includes(step.step_id)
|
|
||||||
? html`
|
|
||||||
<ha-formfield
|
|
||||||
class="store-token"
|
|
||||||
.label=${this.localize("ui.panel.page-authorize.store_token")}
|
|
||||||
>
|
|
||||||
<ha-checkbox
|
|
||||||
.checked=${this._storeToken}
|
|
||||||
@change=${this._storeTokenChanged}
|
|
||||||
></ha-checkbox>
|
|
||||||
</ha-formfield>
|
|
||||||
`
|
|
||||||
: ""}
|
|
||||||
`;
|
`;
|
||||||
default:
|
default:
|
||||||
return html``;
|
return html``;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _storeTokenChanged(e: CustomEvent<HTMLInputElement>) {
|
|
||||||
this._storeToken = (e.currentTarget as HTMLInputElement).checked;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async _providerChanged(newProvider?: AuthProvider) {
|
private async _providerChanged(newProvider?: AuthProvider) {
|
||||||
if (this._step && this._step.type === "form") {
|
if (this._step && this._step.type === "form") {
|
||||||
fetch(`/auth/login_flow/${this._step.flow_id}`, {
|
fetch(`/auth/login_flow/${this._step.flow_id}`, {
|
||||||
@@ -269,8 +189,7 @@ class HaAuthFlow extends litLocalizeLiteMixin(LitElement) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._step = data;
|
await this._updateStep(data);
|
||||||
this._state = "step";
|
|
||||||
} else {
|
} else {
|
||||||
this._state = "error";
|
this._state = "error";
|
||||||
this._errorMessage = data.message;
|
this._errorMessage = data.message;
|
||||||
@@ -297,13 +216,43 @@ class HaAuthFlow extends litLocalizeLiteMixin(LitElement) {
|
|||||||
if (this.oauth2State) {
|
if (this.oauth2State) {
|
||||||
url += `&state=${encodeURIComponent(this.oauth2State)}`;
|
url += `&state=${encodeURIComponent(this.oauth2State)}`;
|
||||||
}
|
}
|
||||||
if (this._storeToken) {
|
|
||||||
url += `&storeToken=true`;
|
|
||||||
}
|
|
||||||
|
|
||||||
document.location.assign(url);
|
document.location.assign(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async _updateStep(step: DataEntryFlowStep) {
|
||||||
|
let stepData: any = null;
|
||||||
|
if (
|
||||||
|
this._step &&
|
||||||
|
(step.flow_id !== this._step.flow_id ||
|
||||||
|
(step.type === "form" &&
|
||||||
|
this._step.type === "form" &&
|
||||||
|
step.step_id !== this._step.step_id))
|
||||||
|
) {
|
||||||
|
stepData = {};
|
||||||
|
}
|
||||||
|
this._step = step;
|
||||||
|
this._state = "step";
|
||||||
|
if (stepData != null) {
|
||||||
|
this._stepData = stepData;
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.updateComplete;
|
||||||
|
// 100ms to give all the form elements time to initialize.
|
||||||
|
setTimeout(() => {
|
||||||
|
const form = this.renderRoot.querySelector("ha-form");
|
||||||
|
if (form) {
|
||||||
|
(form as any).focus();
|
||||||
|
}
|
||||||
|
}, 100);
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
this.renderRoot.querySelector(
|
||||||
|
"ha-password-manager-polyfill"
|
||||||
|
)!.boundingRect = this.getBoundingClientRect();
|
||||||
|
}, 500);
|
||||||
|
}
|
||||||
|
|
||||||
private _stepDataChanged(ev: CustomEvent) {
|
private _stepDataChanged(ev: CustomEvent) {
|
||||||
this._stepData = ev.detail.value;
|
this._stepData = ev.detail.value;
|
||||||
}
|
}
|
||||||
@@ -348,7 +297,9 @@ class HaAuthFlow extends litLocalizeLiteMixin(LitElement) {
|
|||||||
this._providerChanged(this.authProvider);
|
this._providerChanged(this.authProvider);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this._submitting = true;
|
this._state = "loading";
|
||||||
|
// To avoid a jumping UI.
|
||||||
|
this.style.setProperty("min-height", `${this.offsetHeight}px`);
|
||||||
|
|
||||||
const postData = { ...this._stepData, client_id: this.clientId };
|
const postData = { ...this._stepData, client_id: this.clientId };
|
||||||
|
|
||||||
@@ -365,28 +316,29 @@ class HaAuthFlow extends litLocalizeLiteMixin(LitElement) {
|
|||||||
this._redirect(newStep.result);
|
this._redirect(newStep.result);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this._step = newStep;
|
await this._updateStep(newStep);
|
||||||
this._state = "step";
|
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.error("Error submitting step", err);
|
console.error("Error submitting step", err);
|
||||||
this._state = "error";
|
this._state = "error";
|
||||||
this._errorMessage = this._unknownError();
|
this._errorMessage = this._unknownError();
|
||||||
} finally {
|
} finally {
|
||||||
this._submitting = false;
|
this.style.setProperty("min-height", "");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
static get styles(): CSSResultGroup {
|
||||||
return css`
|
return css`
|
||||||
|
:host {
|
||||||
|
/* So we can set min-height to avoid jumping during loading */
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
.action {
|
.action {
|
||||||
margin: 24px 0 8px;
|
margin: 24px 0 8px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
/* Align with the rest of the form. */
|
.error {
|
||||||
.store-token {
|
color: red;
|
||||||
margin-top: 10px;
|
|
||||||
margin-left: -16px;
|
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
@@ -174,10 +174,6 @@ class HaAuthorize extends litLocalizeLiteMixin(LitElement) {
|
|||||||
display: block;
|
display: block;
|
||||||
margin-top: 48px;
|
margin-top: 48px;
|
||||||
}
|
}
|
||||||
ha-auth-flow {
|
|
||||||
display: block;
|
|
||||||
margin-top: 24px;
|
|
||||||
}
|
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -2,8 +2,8 @@
|
|||||||
import { html, LitElement, TemplateResult } from "lit";
|
import { html, LitElement, TemplateResult } from "lit";
|
||||||
import { customElement, property } from "lit/decorators";
|
import { customElement, property } from "lit/decorators";
|
||||||
import { fireEvent } from "../common/dom/fire_event";
|
import { fireEvent } from "../common/dom/fire_event";
|
||||||
import type { HaFormSchema } from "../components/ha-form/types";
|
import { HaFormSchema } from "../components/ha-form/ha-form";
|
||||||
import type { DataEntryFlowStep } from "../data/data_entry_flow";
|
import { DataEntryFlowStep } from "../data/data_entry_flow";
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HTMLElementTagNameMap {
|
interface HTMLElementTagNameMap {
|
||||||
|
@@ -21,11 +21,7 @@ class HaPickAuthProvider extends litLocalizeLiteMixin(LitElement) {
|
|||||||
<p>${this.localize("ui.panel.page-authorize.pick_auth_provider")}:</p>
|
<p>${this.localize("ui.panel.page-authorize.pick_auth_provider")}:</p>
|
||||||
${this.authProviders.map(
|
${this.authProviders.map(
|
||||||
(provider) => html`
|
(provider) => html`
|
||||||
<paper-item
|
<paper-item .auth_provider=${provider} @click=${this._handlePick}>
|
||||||
role="button"
|
|
||||||
.auth_provider=${provider}
|
|
||||||
@click=${this._handlePick}
|
|
||||||
>
|
|
||||||
<paper-item-body>${provider.name}</paper-item-body>
|
<paper-item-body>${provider.name}</paper-item-body>
|
||||||
<ha-icon-next></ha-icon-next>
|
<ha-icon-next></ha-icon-next>
|
||||||
</paper-item>
|
</paper-item>
|
||||||
|
@@ -3,5 +3,5 @@ import { CAST_DEV_APP_ID } from "./dev_const";
|
|||||||
// Guard dev mode with `__dev__` so it can only ever be enabled in dev mode.
|
// Guard dev mode with `__dev__` so it can only ever be enabled in dev mode.
|
||||||
export const CAST_DEV = __DEV__ && true;
|
export const CAST_DEV = __DEV__ && true;
|
||||||
|
|
||||||
export const CAST_APP_ID = CAST_DEV ? CAST_DEV_APP_ID : "A078F6B0";
|
export const CAST_APP_ID = CAST_DEV ? CAST_DEV_APP_ID : "B12CE3CA";
|
||||||
export const CAST_NS = "urn:x-cast:com.nabucasa.hast";
|
export const CAST_NS = "urn:x-cast:com.nabucasa.hast";
|
||||||
|
@@ -11,20 +11,4 @@ export interface ReceiverStatusMessage extends BaseCastMessage {
|
|||||||
urlPath?: string | null;
|
urlPath?: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ReceiverErrorMessage extends BaseCastMessage {
|
|
||||||
type: "receiver_error";
|
|
||||||
error_code: ReceiverErrorCode;
|
|
||||||
error_message: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const enum ReceiverErrorCode {
|
|
||||||
CONNECTION_FAILED = 1,
|
|
||||||
AUTHENTICATION_FAILED = 2,
|
|
||||||
CONNECTION_LOST = 3,
|
|
||||||
HASS_URL_MISSING = 4,
|
|
||||||
NO_HTTPS = 5,
|
|
||||||
NOT_CONNECTED = 21,
|
|
||||||
FETCH_CONFIG_FAILED = 22,
|
|
||||||
}
|
|
||||||
|
|
||||||
export type SenderMessage = ReceiverStatusMessage;
|
export type SenderMessage = ReceiverStatusMessage;
|
||||||
|
@@ -1,5 +1,4 @@
|
|||||||
import { AuthData } from "home-assistant-js-websocket";
|
import { AuthData } from "home-assistant-js-websocket";
|
||||||
import { extractSearchParam } from "../url/search-params";
|
|
||||||
|
|
||||||
const storage = window.localStorage || {};
|
const storage = window.localStorage || {};
|
||||||
|
|
||||||
@@ -31,11 +30,6 @@ export function askWrite() {
|
|||||||
|
|
||||||
export function saveTokens(tokens: AuthData | null) {
|
export function saveTokens(tokens: AuthData | null) {
|
||||||
tokenCache.tokens = tokens;
|
tokenCache.tokens = tokens;
|
||||||
|
|
||||||
if (!tokenCache.writeEnabled && extractSearchParam("storeToken") === "true") {
|
|
||||||
tokenCache.writeEnabled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tokenCache.writeEnabled) {
|
if (tokenCache.writeEnabled) {
|
||||||
try {
|
try {
|
||||||
storage.hassTokens = JSON.stringify(tokens);
|
storage.hassTokens = JSON.stringify(tokens);
|
||||||
@@ -51,6 +45,7 @@ export function enableWrite() {
|
|||||||
saveTokens(tokenCache.tokens);
|
saveTokens(tokenCache.tokens);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function loadTokens() {
|
export function loadTokens() {
|
||||||
if (tokenCache.tokens === undefined) {
|
if (tokenCache.tokens === undefined) {
|
||||||
try {
|
try {
|
||||||
|
@@ -7,13 +7,7 @@ export const canShowPage = (hass: HomeAssistant, page: PageNavigation) =>
|
|||||||
!hideAdvancedPage(hass, page);
|
!hideAdvancedPage(hass, page);
|
||||||
|
|
||||||
const isLoadedIntegration = (hass: HomeAssistant, page: PageNavigation) =>
|
const isLoadedIntegration = (hass: HomeAssistant, page: PageNavigation) =>
|
||||||
page.component
|
!page.component || isComponentLoaded(hass, page.component);
|
||||||
? isComponentLoaded(hass, page.component)
|
|
||||||
: page.components
|
|
||||||
? page.components.some((integration) =>
|
|
||||||
isComponentLoaded(hass, integration)
|
|
||||||
)
|
|
||||||
: true;
|
|
||||||
const isCore = (page: PageNavigation) => page.core;
|
const isCore = (page: PageNavigation) => page.core;
|
||||||
const isAdvancedPage = (page: PageNavigation) => page.advancedOnly;
|
const isAdvancedPage = (page: PageNavigation) => page.advancedOnly;
|
||||||
const userWantsAdvanced = (hass: HomeAssistant) => hass.userData?.showAdvanced;
|
const userWantsAdvanced = (hass: HomeAssistant) => hass.userData?.showAdvanced;
|
||||||
|
@@ -4,10 +4,6 @@ export const atLeastVersion = (
|
|||||||
minor: number,
|
minor: number,
|
||||||
patch?: number
|
patch?: number
|
||||||
): boolean => {
|
): boolean => {
|
||||||
if (__DEMO__) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
const [haMajor, haMinor, haPatch] = version.split(".", 3);
|
const [haMajor, haMinor, haPatch] = version.split(".", 3);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@@ -1,150 +1,91 @@
|
|||||||
/** Constants to be used in the frontend. */
|
/** Constants to be used in the frontend. */
|
||||||
|
|
||||||
import {
|
|
||||||
mdiAccount,
|
|
||||||
mdiAirFilter,
|
|
||||||
mdiAlert,
|
|
||||||
mdiAngleAcute,
|
|
||||||
mdiAppleSafari,
|
|
||||||
mdiBell,
|
|
||||||
mdiBookmark,
|
|
||||||
mdiBrightness5,
|
|
||||||
mdiBullhorn,
|
|
||||||
mdiCalendar,
|
|
||||||
mdiCalendarClock,
|
|
||||||
mdiCash,
|
|
||||||
mdiClock,
|
|
||||||
mdiCloudUpload,
|
|
||||||
mdiCog,
|
|
||||||
mdiCommentAlert,
|
|
||||||
mdiCounter,
|
|
||||||
mdiCurrentAc,
|
|
||||||
mdiEye,
|
|
||||||
mdiFan,
|
|
||||||
mdiFlash,
|
|
||||||
mdiFlower,
|
|
||||||
mdiFormatListBulleted,
|
|
||||||
mdiFormTextbox,
|
|
||||||
mdiGasCylinder,
|
|
||||||
mdiGauge,
|
|
||||||
mdiGoogleAssistant,
|
|
||||||
mdiGoogleCirclesCommunities,
|
|
||||||
mdiHomeAssistant,
|
|
||||||
mdiHomeAutomation,
|
|
||||||
mdiImageFilterFrames,
|
|
||||||
mdiLightbulb,
|
|
||||||
mdiLightningBolt,
|
|
||||||
mdiMailbox,
|
|
||||||
mdiMapMarkerRadius,
|
|
||||||
mdiMolecule,
|
|
||||||
mdiMoleculeCo,
|
|
||||||
mdiMoleculeCo2,
|
|
||||||
mdiPalette,
|
|
||||||
mdiRayVertex,
|
|
||||||
mdiRemote,
|
|
||||||
mdiRobot,
|
|
||||||
mdiRobotVacuum,
|
|
||||||
mdiScriptText,
|
|
||||||
mdiSineWave,
|
|
||||||
mdiTextToSpeech,
|
|
||||||
mdiThermometer,
|
|
||||||
mdiThermostat,
|
|
||||||
mdiTimerOutline,
|
|
||||||
mdiToggleSwitchOutline,
|
|
||||||
mdiVideo,
|
|
||||||
mdiWaterPercent,
|
|
||||||
mdiWeatherCloudy,
|
|
||||||
mdiWhiteBalanceSunny,
|
|
||||||
mdiWifi,
|
|
||||||
} from "@mdi/js";
|
|
||||||
|
|
||||||
// Constants should be alphabetically sorted by name.
|
// Constants should be alphabetically sorted by name.
|
||||||
// Arrays with values should be alphabetically sorted if order doesn't matter.
|
// Arrays with values should be alphabetically sorted if order doesn't matter.
|
||||||
// Each constant should have a description what it is supposed to be used for.
|
// Each constant should have a description what it is supposed to be used for.
|
||||||
|
|
||||||
/** Icon to use when no icon specified for domain. */
|
/** Icon to use when no icon specified for domain. */
|
||||||
export const DEFAULT_DOMAIN_ICON = mdiBookmark;
|
export const DEFAULT_DOMAIN_ICON = "hass:bookmark";
|
||||||
|
|
||||||
/** Icons for each domain */
|
/** Icons for each domain */
|
||||||
export const FIXED_DOMAIN_ICONS = {
|
export const FIXED_DOMAIN_ICONS = {
|
||||||
alert: mdiAlert,
|
alert: "hass:alert",
|
||||||
air_quality: mdiAirFilter,
|
alexa: "hass:amazon-alexa",
|
||||||
automation: mdiRobot,
|
air_quality: "hass:air-filter",
|
||||||
calendar: mdiCalendar,
|
automation: "hass:robot",
|
||||||
camera: mdiVideo,
|
calendar: "hass:calendar",
|
||||||
climate: mdiThermostat,
|
camera: "hass:video",
|
||||||
configurator: mdiCog,
|
climate: "hass:thermostat",
|
||||||
conversation: mdiTextToSpeech,
|
configurator: "hass:cog",
|
||||||
counter: mdiCounter,
|
conversation: "hass:text-to-speech",
|
||||||
fan: mdiFan,
|
counter: "hass:counter",
|
||||||
google_assistant: mdiGoogleAssistant,
|
device_tracker: "hass:account",
|
||||||
group: mdiGoogleCirclesCommunities,
|
fan: "hass:fan",
|
||||||
homeassistant: mdiHomeAssistant,
|
google_assistant: "hass:google-assistant",
|
||||||
homekit: mdiHomeAutomation,
|
group: "hass:google-circles-communities",
|
||||||
image_processing: mdiImageFilterFrames,
|
homeassistant: "hass:home-assistant",
|
||||||
input_boolean: mdiToggleSwitchOutline,
|
homekit: "hass:home-automation",
|
||||||
input_datetime: mdiCalendarClock,
|
image_processing: "hass:image-filter-frames",
|
||||||
input_number: mdiRayVertex,
|
input_boolean: "hass:toggle-switch-outline",
|
||||||
input_select: mdiFormatListBulleted,
|
input_datetime: "hass:calendar-clock",
|
||||||
input_text: mdiFormTextbox,
|
input_number: "hass:ray-vertex",
|
||||||
light: mdiLightbulb,
|
input_select: "hass:format-list-bulleted",
|
||||||
mailbox: mdiMailbox,
|
input_text: "hass:form-textbox",
|
||||||
notify: mdiCommentAlert,
|
light: "hass:lightbulb",
|
||||||
number: mdiRayVertex,
|
mailbox: "hass:mailbox",
|
||||||
persistent_notification: mdiBell,
|
notify: "hass:comment-alert",
|
||||||
person: mdiAccount,
|
number: "hass:ray-vertex",
|
||||||
plant: mdiFlower,
|
persistent_notification: "hass:bell",
|
||||||
proximity: mdiAppleSafari,
|
person: "hass:account",
|
||||||
remote: mdiRemote,
|
plant: "hass:flower",
|
||||||
scene: mdiPalette,
|
proximity: "hass:apple-safari",
|
||||||
script: mdiScriptText,
|
remote: "hass:remote",
|
||||||
select: mdiFormatListBulleted,
|
scene: "hass:palette",
|
||||||
sensor: mdiEye,
|
script: "hass:script-text",
|
||||||
siren: mdiBullhorn,
|
select: "hass:format-list-bulleted",
|
||||||
simple_alarm: mdiBell,
|
sensor: "hass:eye",
|
||||||
sun: mdiWhiteBalanceSunny,
|
simple_alarm: "hass:bell",
|
||||||
timer: mdiTimerOutline,
|
sun: "hass:white-balance-sunny",
|
||||||
updater: mdiCloudUpload,
|
switch: "hass:flash",
|
||||||
vacuum: mdiRobotVacuum,
|
timer: "hass:timer-outline",
|
||||||
water_heater: mdiThermometer,
|
updater: "hass:cloud-upload",
|
||||||
weather: mdiWeatherCloudy,
|
vacuum: "hass:robot-vacuum",
|
||||||
zone: mdiMapMarkerRadius,
|
water_heater: "hass:thermometer",
|
||||||
|
weather: "hass:weather-cloudy",
|
||||||
|
zone: "hass:map-marker-radius",
|
||||||
};
|
};
|
||||||
|
|
||||||
export const FIXED_DEVICE_CLASS_ICONS = {
|
export const FIXED_DEVICE_CLASS_ICONS = {
|
||||||
aqi: mdiAirFilter,
|
aqi: "hass:air-filter",
|
||||||
// battery: mdiBattery, => not included by design since `sensorIcon()` will dynamically determine the icon
|
current: "hass:current-ac",
|
||||||
carbon_dioxide: mdiMoleculeCo2,
|
carbon_dioxide: "mdi:molecule-co2",
|
||||||
carbon_monoxide: mdiMoleculeCo,
|
carbon_monoxide: "mdi:molecule-co",
|
||||||
current: mdiCurrentAc,
|
date: "hass:calendar",
|
||||||
date: mdiCalendar,
|
energy: "hass:lightning-bolt",
|
||||||
energy: mdiLightningBolt,
|
gas: "hass:gas-cylinder",
|
||||||
frequency: mdiSineWave,
|
humidity: "hass:water-percent",
|
||||||
gas: mdiGasCylinder,
|
illuminance: "hass:brightness-5",
|
||||||
humidity: mdiWaterPercent,
|
nitrogen_dioxide: "mdi:molecule",
|
||||||
illuminance: mdiBrightness5,
|
nitrogen_monoxide: "mdi:molecule",
|
||||||
monetary: mdiCash,
|
nitrous_oxide: "mdi:molecule",
|
||||||
nitrogen_dioxide: mdiMolecule,
|
ozone: "mdi:molecule",
|
||||||
nitrogen_monoxide: mdiMolecule,
|
temperature: "hass:thermometer",
|
||||||
nitrous_oxide: mdiMolecule,
|
monetary: "mdi:cash",
|
||||||
ozone: mdiMolecule,
|
pm25: "mdi:molecule",
|
||||||
pm1: mdiMolecule,
|
pm1: "mdi:molecule",
|
||||||
pm10: mdiMolecule,
|
pm10: "mdi:molecule",
|
||||||
pm25: mdiMolecule,
|
pressure: "hass:gauge",
|
||||||
power: mdiFlash,
|
power: "hass:flash",
|
||||||
power_factor: mdiAngleAcute,
|
power_factor: "hass:angle-acute",
|
||||||
pressure: mdiGauge,
|
signal_strength: "hass:wifi",
|
||||||
signal_strength: mdiWifi,
|
sulphur_dioxide: "mdi:molecule",
|
||||||
sulphur_dioxide: mdiMolecule,
|
timestamp: "hass:clock",
|
||||||
temperature: mdiThermometer,
|
volatile_organic_compounds: "mdi:molecule",
|
||||||
timestamp: mdiClock,
|
voltage: "hass:sine-wave",
|
||||||
volatile_organic_compounds: mdiMolecule,
|
|
||||||
voltage: mdiSineWave,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Domains that have a state card. */
|
/** Domains that have a state card. */
|
||||||
export const DOMAINS_WITH_CARD = [
|
export const DOMAINS_WITH_CARD = [
|
||||||
"button",
|
|
||||||
"climate",
|
"climate",
|
||||||
"cover",
|
"cover",
|
||||||
"configurator",
|
"configurator",
|
||||||
|
@@ -36,62 +36,55 @@ export const applyThemesOnElement = (
|
|||||||
let cacheKey = selectedTheme;
|
let cacheKey = selectedTheme;
|
||||||
let themeRules: Partial<ThemeVars> = {};
|
let themeRules: Partial<ThemeVars> = {};
|
||||||
|
|
||||||
// If there is no explicitly desired dark mode provided, we automatically
|
if (themeSettings) {
|
||||||
// use the active one from hass.themes.
|
if (themeSettings.dark) {
|
||||||
if (!themeSettings || themeSettings?.dark === undefined) {
|
cacheKey = `${cacheKey}__dark`;
|
||||||
themeSettings = {
|
themeRules = { ...darkStyles };
|
||||||
...themeSettings,
|
|
||||||
dark: themes.darkMode,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (themeSettings.dark) {
|
|
||||||
cacheKey = `${cacheKey}__dark`;
|
|
||||||
themeRules = { ...darkStyles };
|
|
||||||
}
|
|
||||||
|
|
||||||
if (selectedTheme === "default") {
|
|
||||||
// Determine the primary and accent colors from the current settings.
|
|
||||||
// Fallbacks are implicitly the HA default blue and orange or the
|
|
||||||
// derived "darkStyles" values, depending on the light vs dark mode.
|
|
||||||
const primaryColor = themeSettings.primaryColor;
|
|
||||||
const accentColor = themeSettings.accentColor;
|
|
||||||
|
|
||||||
if (themeSettings.dark && primaryColor) {
|
|
||||||
themeRules["app-header-background-color"] = hexBlend(
|
|
||||||
primaryColor,
|
|
||||||
"#121212",
|
|
||||||
8
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (primaryColor) {
|
if (selectedTheme === "default") {
|
||||||
cacheKey = `${cacheKey}__primary_${primaryColor}`;
|
// Determine the primary and accent colors from the current settings.
|
||||||
const rgbPrimaryColor = hex2rgb(primaryColor);
|
// Fallbacks are implicitly the HA default blue and orange or the
|
||||||
const labPrimaryColor = rgb2lab(rgbPrimaryColor);
|
// derived "darkStyles" values, depending on the light vs dark mode.
|
||||||
themeRules["primary-color"] = primaryColor;
|
const primaryColor = themeSettings.primaryColor;
|
||||||
const rgbLightPrimaryColor = lab2rgb(labBrighten(labPrimaryColor));
|
const accentColor = themeSettings.accentColor;
|
||||||
themeRules["light-primary-color"] = rgb2hex(rgbLightPrimaryColor);
|
|
||||||
themeRules["dark-primary-color"] = lab2hex(labDarken(labPrimaryColor));
|
|
||||||
themeRules["text-primary-color"] =
|
|
||||||
rgbContrast(rgbPrimaryColor, [33, 33, 33]) < 6 ? "#fff" : "#212121";
|
|
||||||
themeRules["text-light-primary-color"] =
|
|
||||||
rgbContrast(rgbLightPrimaryColor, [33, 33, 33]) < 6
|
|
||||||
? "#fff"
|
|
||||||
: "#212121";
|
|
||||||
themeRules["state-icon-color"] = themeRules["dark-primary-color"];
|
|
||||||
}
|
|
||||||
if (accentColor) {
|
|
||||||
cacheKey = `${cacheKey}__accent_${accentColor}`;
|
|
||||||
themeRules["accent-color"] = accentColor;
|
|
||||||
const rgbAccentColor = hex2rgb(accentColor);
|
|
||||||
themeRules["text-accent-color"] =
|
|
||||||
rgbContrast(rgbAccentColor, [33, 33, 33]) < 6 ? "#fff" : "#212121";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Nothing was changed
|
if (themeSettings.dark && primaryColor) {
|
||||||
if (element._themes?.cacheKey === cacheKey) {
|
themeRules["app-header-background-color"] = hexBlend(
|
||||||
return;
|
primaryColor,
|
||||||
|
"#121212",
|
||||||
|
8
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (primaryColor) {
|
||||||
|
cacheKey = `${cacheKey}__primary_${primaryColor}`;
|
||||||
|
const rgbPrimaryColor = hex2rgb(primaryColor);
|
||||||
|
const labPrimaryColor = rgb2lab(rgbPrimaryColor);
|
||||||
|
themeRules["primary-color"] = primaryColor;
|
||||||
|
const rgbLightPrimaryColor = lab2rgb(labBrighten(labPrimaryColor));
|
||||||
|
themeRules["light-primary-color"] = rgb2hex(rgbLightPrimaryColor);
|
||||||
|
themeRules["dark-primary-color"] = lab2hex(labDarken(labPrimaryColor));
|
||||||
|
themeRules["text-primary-color"] =
|
||||||
|
rgbContrast(rgbPrimaryColor, [33, 33, 33]) < 6 ? "#fff" : "#212121";
|
||||||
|
themeRules["text-light-primary-color"] =
|
||||||
|
rgbContrast(rgbLightPrimaryColor, [33, 33, 33]) < 6
|
||||||
|
? "#fff"
|
||||||
|
: "#212121";
|
||||||
|
themeRules["state-icon-color"] = themeRules["dark-primary-color"];
|
||||||
|
}
|
||||||
|
if (accentColor) {
|
||||||
|
cacheKey = `${cacheKey}__accent_${accentColor}`;
|
||||||
|
themeRules["accent-color"] = accentColor;
|
||||||
|
const rgbAccentColor = hex2rgb(accentColor);
|
||||||
|
themeRules["text-accent-color"] =
|
||||||
|
rgbContrast(rgbAccentColor, [33, 33, 33]) < 6 ? "#fff" : "#212121";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nothing was changed
|
||||||
|
if (element._themes?.cacheKey === cacheKey) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,7 +115,7 @@ export const applyThemesOnElement = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
const newTheme =
|
const newTheme =
|
||||||
Object.keys(themeRules).length && cacheKey
|
themeRules && cacheKey
|
||||||
? PROCESSED_THEMES[cacheKey] || processTheme(cacheKey, themeRules)
|
? PROCESSED_THEMES[cacheKey] || processTheme(cacheKey, themeRules)
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
|
@@ -1,3 +1,2 @@
|
|||||||
/** An empty image which can be set as src of an img element. */
|
/** An empty image which can be set as src of an img element. */
|
||||||
export const emptyImageBase64 =
|
export default "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
|
||||||
"data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
|
|
||||||
|
@@ -1,36 +1,24 @@
|
|||||||
/** Return an icon representing a alarm panel state. */
|
/** Return an icon representing a alarm panel state. */
|
||||||
|
|
||||||
import {
|
|
||||||
mdiShieldLock,
|
|
||||||
mdiShieldAirplane,
|
|
||||||
mdiShieldHome,
|
|
||||||
mdiShieldMoon,
|
|
||||||
mdiSecurity,
|
|
||||||
mdiShieldOutline,
|
|
||||||
mdiBellRing,
|
|
||||||
mdiShieldOff,
|
|
||||||
mdiShield,
|
|
||||||
} from "@mdi/js";
|
|
||||||
|
|
||||||
export const alarmPanelIcon = (state?: string) => {
|
export const alarmPanelIcon = (state?: string) => {
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case "armed_away":
|
case "armed_away":
|
||||||
return mdiShieldLock;
|
return "hass:shield-lock";
|
||||||
case "armed_vacation":
|
case "armed_vacation":
|
||||||
return mdiShieldAirplane;
|
return "hass:shield-airplane";
|
||||||
case "armed_home":
|
case "armed_home":
|
||||||
return mdiShieldHome;
|
return "hass:shield-home";
|
||||||
case "armed_night":
|
case "armed_night":
|
||||||
return mdiShieldMoon;
|
return "hass:shield-moon";
|
||||||
case "armed_custom_bypass":
|
case "armed_custom_bypass":
|
||||||
return mdiSecurity;
|
return "hass:security";
|
||||||
case "pending":
|
case "pending":
|
||||||
return mdiShieldOutline;
|
return "hass:shield-outline";
|
||||||
case "triggered":
|
case "triggered":
|
||||||
return mdiBellRing;
|
return "hass:bell-ring";
|
||||||
case "disarmed":
|
case "disarmed":
|
||||||
return mdiShieldOff;
|
return "hass:shield-off";
|
||||||
default:
|
default:
|
||||||
return mdiShield;
|
return "hass:shield";
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@@ -1,92 +1,35 @@
|
|||||||
/** Return an icon representing a battery state. */
|
/** Return an icon representing a battery state. */
|
||||||
import {
|
|
||||||
mdiBattery,
|
|
||||||
mdiBattery10,
|
|
||||||
mdiBattery20,
|
|
||||||
mdiBattery30,
|
|
||||||
mdiBattery40,
|
|
||||||
mdiBattery50,
|
|
||||||
mdiBattery60,
|
|
||||||
mdiBattery70,
|
|
||||||
mdiBattery80,
|
|
||||||
mdiBattery90,
|
|
||||||
mdiBatteryAlert,
|
|
||||||
mdiBatteryAlertVariantOutline,
|
|
||||||
mdiBatteryCharging,
|
|
||||||
mdiBatteryCharging10,
|
|
||||||
mdiBatteryCharging20,
|
|
||||||
mdiBatteryCharging30,
|
|
||||||
mdiBatteryCharging40,
|
|
||||||
mdiBatteryCharging50,
|
|
||||||
mdiBatteryCharging60,
|
|
||||||
mdiBatteryCharging70,
|
|
||||||
mdiBatteryCharging80,
|
|
||||||
mdiBatteryCharging90,
|
|
||||||
mdiBatteryChargingOutline,
|
|
||||||
mdiBatteryUnknown,
|
|
||||||
} from "@mdi/js";
|
|
||||||
import { HassEntity } from "home-assistant-js-websocket";
|
import { HassEntity } from "home-assistant-js-websocket";
|
||||||
|
|
||||||
const BATTERY_ICONS = {
|
export const batteryIcon = (
|
||||||
10: mdiBattery10,
|
|
||||||
20: mdiBattery20,
|
|
||||||
30: mdiBattery30,
|
|
||||||
40: mdiBattery40,
|
|
||||||
50: mdiBattery50,
|
|
||||||
60: mdiBattery60,
|
|
||||||
70: mdiBattery70,
|
|
||||||
80: mdiBattery80,
|
|
||||||
90: mdiBattery90,
|
|
||||||
100: mdiBattery,
|
|
||||||
};
|
|
||||||
const BATTERY_CHARGING_ICONS = {
|
|
||||||
10: mdiBatteryCharging10,
|
|
||||||
20: mdiBatteryCharging20,
|
|
||||||
30: mdiBatteryCharging30,
|
|
||||||
40: mdiBatteryCharging40,
|
|
||||||
50: mdiBatteryCharging50,
|
|
||||||
60: mdiBatteryCharging60,
|
|
||||||
70: mdiBatteryCharging70,
|
|
||||||
80: mdiBatteryCharging80,
|
|
||||||
90: mdiBatteryCharging90,
|
|
||||||
100: mdiBatteryCharging,
|
|
||||||
};
|
|
||||||
|
|
||||||
export const batteryStateIcon = (
|
|
||||||
batteryState: HassEntity,
|
batteryState: HassEntity,
|
||||||
batteryChargingState?: HassEntity
|
batteryChargingState?: HassEntity
|
||||||
) => {
|
) => {
|
||||||
const battery = batteryState.state;
|
const battery = Number(batteryState.state);
|
||||||
const batteryCharging =
|
const battery_charging =
|
||||||
batteryChargingState && batteryChargingState.state === "on";
|
batteryChargingState && batteryChargingState.state === "on";
|
||||||
|
let icon = "hass:battery";
|
||||||
|
|
||||||
return batteryIcon(battery, batteryCharging);
|
if (isNaN(battery)) {
|
||||||
};
|
if (batteryState.state === "off") {
|
||||||
|
icon += "-full";
|
||||||
export const batteryIcon = (
|
} else if (batteryState.state === "on") {
|
||||||
batteryState: number | string,
|
icon += "-alert";
|
||||||
batteryCharging?: boolean
|
} else {
|
||||||
) => {
|
icon += "-unknown";
|
||||||
const batteryValue = Number(batteryState);
|
|
||||||
if (isNaN(batteryValue)) {
|
|
||||||
if (batteryState === "off") {
|
|
||||||
return mdiBattery;
|
|
||||||
}
|
}
|
||||||
if (batteryState === "on") {
|
return icon;
|
||||||
return mdiBatteryAlert;
|
|
||||||
}
|
|
||||||
return mdiBatteryUnknown;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const batteryRound = Math.round(batteryValue / 10) * 10;
|
const batteryRound = Math.round(battery / 10) * 10;
|
||||||
if (batteryCharging && batteryValue >= 10) {
|
if (battery_charging && battery > 10) {
|
||||||
return BATTERY_CHARGING_ICONS[batteryRound];
|
icon += `-charging-${batteryRound}`;
|
||||||
|
} else if (battery_charging) {
|
||||||
|
icon += "-outline";
|
||||||
|
} else if (battery <= 5) {
|
||||||
|
icon += "-alert";
|
||||||
|
} else if (battery > 5 && battery < 95) {
|
||||||
|
icon += `-${batteryRound}`;
|
||||||
}
|
}
|
||||||
if (batteryCharging) {
|
return icon;
|
||||||
return mdiBatteryChargingOutline;
|
|
||||||
}
|
|
||||||
if (batteryValue <= 5) {
|
|
||||||
return mdiBatteryAlertVariantOutline;
|
|
||||||
}
|
|
||||||
return BATTERY_ICONS[batteryRound];
|
|
||||||
};
|
};
|
||||||
|
@@ -1,46 +1,3 @@
|
|||||||
import {
|
|
||||||
mdiAlertCircle,
|
|
||||||
mdiBattery,
|
|
||||||
mdiBatteryCharging,
|
|
||||||
mdiBatteryOutline,
|
|
||||||
mdiBrightness5,
|
|
||||||
mdiBrightness7,
|
|
||||||
mdiCheckboxMarkedCircle,
|
|
||||||
mdiCheckNetworkOutline,
|
|
||||||
mdiCloseNetworkOutline,
|
|
||||||
mdiCheckCircle,
|
|
||||||
mdiCropPortrait,
|
|
||||||
mdiDoorClosed,
|
|
||||||
mdiDoorOpen,
|
|
||||||
mdiFire,
|
|
||||||
mdiGarage,
|
|
||||||
mdiGarageOpen,
|
|
||||||
mdiHome,
|
|
||||||
mdiHomeOutline,
|
|
||||||
mdiLock,
|
|
||||||
mdiLockOpen,
|
|
||||||
mdiMusicNote,
|
|
||||||
mdiMusicNoteOff,
|
|
||||||
mdiPackage,
|
|
||||||
mdiPackageUp,
|
|
||||||
mdiPlay,
|
|
||||||
mdiPowerPlug,
|
|
||||||
mdiPowerPlugOff,
|
|
||||||
mdiRadioboxBlank,
|
|
||||||
mdiRun,
|
|
||||||
mdiSmoke,
|
|
||||||
mdiSnowflake,
|
|
||||||
mdiSquare,
|
|
||||||
mdiSquareOutline,
|
|
||||||
mdiStop,
|
|
||||||
mdiThermometer,
|
|
||||||
mdiVibrate,
|
|
||||||
mdiWalk,
|
|
||||||
mdiWater,
|
|
||||||
mdiWaterOff,
|
|
||||||
mdiWindowClosed,
|
|
||||||
mdiWindowOpen,
|
|
||||||
} from "@mdi/js";
|
|
||||||
import { HassEntity } from "home-assistant-js-websocket";
|
import { HassEntity } from "home-assistant-js-websocket";
|
||||||
|
|
||||||
/** Return an icon representing a binary sensor state. */
|
/** Return an icon representing a binary sensor state. */
|
||||||
@@ -49,55 +6,52 @@ export const binarySensorIcon = (state?: string, stateObj?: HassEntity) => {
|
|||||||
const is_off = state === "off";
|
const is_off = state === "off";
|
||||||
switch (stateObj?.attributes.device_class) {
|
switch (stateObj?.attributes.device_class) {
|
||||||
case "battery":
|
case "battery":
|
||||||
return is_off ? mdiBattery : mdiBatteryOutline;
|
return is_off ? "hass:battery" : "hass:battery-outline";
|
||||||
case "battery_charging":
|
case "battery_charging":
|
||||||
return is_off ? mdiBattery : mdiBatteryCharging;
|
return is_off ? "hass:battery" : "hass:battery-charging";
|
||||||
case "cold":
|
case "cold":
|
||||||
return is_off ? mdiThermometer : mdiSnowflake;
|
return is_off ? "hass:thermometer" : "hass:snowflake";
|
||||||
case "connectivity":
|
case "connectivity":
|
||||||
return is_off ? mdiCloseNetworkOutline : mdiCheckNetworkOutline;
|
return is_off ? "hass:server-network-off" : "hass:server-network";
|
||||||
case "door":
|
case "door":
|
||||||
return is_off ? mdiDoorClosed : mdiDoorOpen;
|
return is_off ? "hass:door-closed" : "hass:door-open";
|
||||||
case "garage_door":
|
case "garage_door":
|
||||||
return is_off ? mdiGarage : mdiGarageOpen;
|
return is_off ? "hass:garage" : "hass:garage-open";
|
||||||
case "power":
|
case "power":
|
||||||
return is_off ? mdiPowerPlugOff : mdiPowerPlug;
|
return is_off ? "hass:power-plug-off" : "hass:power-plug";
|
||||||
case "gas":
|
case "gas":
|
||||||
case "problem":
|
case "problem":
|
||||||
case "safety":
|
case "safety":
|
||||||
case "tamper":
|
return is_off ? "hass:check-circle" : "hass:alert-circle";
|
||||||
return is_off ? mdiCheckCircle : mdiAlertCircle;
|
|
||||||
case "smoke":
|
case "smoke":
|
||||||
return is_off ? mdiCheckCircle : mdiSmoke;
|
return is_off ? "hass:check-circle" : "hass:smoke";
|
||||||
case "heat":
|
case "heat":
|
||||||
return is_off ? mdiThermometer : mdiFire;
|
return is_off ? "hass:thermometer" : "hass:fire";
|
||||||
case "light":
|
case "light":
|
||||||
return is_off ? mdiBrightness5 : mdiBrightness7;
|
return is_off ? "hass:brightness-5" : "hass:brightness-7";
|
||||||
case "lock":
|
case "lock":
|
||||||
return is_off ? mdiLock : mdiLockOpen;
|
return is_off ? "hass:lock" : "hass:lock-open";
|
||||||
case "moisture":
|
case "moisture":
|
||||||
return is_off ? mdiWaterOff : mdiWater;
|
return is_off ? "hass:water-off" : "hass:water";
|
||||||
case "motion":
|
case "motion":
|
||||||
return is_off ? mdiWalk : mdiRun;
|
return is_off ? "hass:walk" : "hass:run";
|
||||||
case "occupancy":
|
case "occupancy":
|
||||||
return is_off ? mdiHomeOutline : mdiHome;
|
return is_off ? "hass:home-outline" : "hass:home";
|
||||||
case "opening":
|
case "opening":
|
||||||
return is_off ? mdiSquare : mdiSquareOutline;
|
return is_off ? "hass:square" : "hass:square-outline";
|
||||||
case "plug":
|
case "plug":
|
||||||
return is_off ? mdiPowerPlugOff : mdiPowerPlug;
|
return is_off ? "hass:power-plug-off" : "hass:power-plug";
|
||||||
case "presence":
|
case "presence":
|
||||||
return is_off ? mdiHomeOutline : mdiHome;
|
return is_off ? "hass:home-outline" : "hass:home";
|
||||||
case "running":
|
|
||||||
return is_off ? mdiStop : mdiPlay;
|
|
||||||
case "sound":
|
case "sound":
|
||||||
return is_off ? mdiMusicNoteOff : mdiMusicNote;
|
return is_off ? "hass:music-note-off" : "hass:music-note";
|
||||||
case "update":
|
case "update":
|
||||||
return is_off ? mdiPackage : mdiPackageUp;
|
return is_off ? "mdi:package" : "mdi:package-up";
|
||||||
case "vibration":
|
case "vibration":
|
||||||
return is_off ? mdiCropPortrait : mdiVibrate;
|
return is_off ? "hass:crop-portrait" : "hass:vibrate";
|
||||||
case "window":
|
case "window":
|
||||||
return is_off ? mdiWindowClosed : mdiWindowOpen;
|
return is_off ? "hass:window-closed" : "hass:window-open";
|
||||||
default:
|
default:
|
||||||
return is_off ? mdiRadioboxBlank : mdiCheckboxMarkedCircle;
|
return is_off ? "hass:radiobox-blank" : "hass:checkbox-marked-circle";
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@@ -4,7 +4,7 @@ import { FrontendLocaleData } from "../../data/translation";
|
|||||||
import { formatDate } from "../datetime/format_date";
|
import { formatDate } from "../datetime/format_date";
|
||||||
import { formatDateTime } from "../datetime/format_date_time";
|
import { formatDateTime } from "../datetime/format_date_time";
|
||||||
import { formatTime } from "../datetime/format_time";
|
import { formatTime } from "../datetime/format_time";
|
||||||
import { formatNumber, isNumericState } from "../number/format_number";
|
import { formatNumber } from "../number/format_number";
|
||||||
import { LocalizeFunc } from "../translations/localize";
|
import { LocalizeFunc } from "../translations/localize";
|
||||||
import { computeStateDomain } from "./compute_state_domain";
|
import { computeStateDomain } from "./compute_state_domain";
|
||||||
|
|
||||||
@@ -20,8 +20,7 @@ export const computeStateDisplay = (
|
|||||||
return localize(`state.default.${compareState}`);
|
return localize(`state.default.${compareState}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Entities with a `unit_of_measurement` or `state_class` are numeric values and should use `formatNumber`
|
if (stateObj.attributes.unit_of_measurement) {
|
||||||
if (isNumericState(stateObj)) {
|
|
||||||
if (stateObj.attributes.device_class === "monetary") {
|
if (stateObj.attributes.device_class === "monetary") {
|
||||||
try {
|
try {
|
||||||
return formatNumber(compareState, locale, {
|
return formatNumber(compareState, locale, {
|
||||||
@@ -32,17 +31,15 @@ export const computeStateDisplay = (
|
|||||||
// fallback to default
|
// fallback to default
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return `${formatNumber(compareState, locale)}${
|
return `${formatNumber(compareState, locale)} ${
|
||||||
stateObj.attributes.unit_of_measurement
|
stateObj.attributes.unit_of_measurement
|
||||||
? " " + stateObj.attributes.unit_of_measurement
|
|
||||||
: ""
|
|
||||||
}`;
|
}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const domain = computeStateDomain(stateObj);
|
const domain = computeStateDomain(stateObj);
|
||||||
|
|
||||||
if (domain === "input_datetime") {
|
if (domain === "input_datetime") {
|
||||||
if (state !== undefined) {
|
if (state) {
|
||||||
// If trying to display an explicit state, need to parse the explict state to `Date` then format.
|
// If trying to display an explicit state, need to parse the explict state to `Date` then format.
|
||||||
// Attributes aren't available, we have to use `state`.
|
// Attributes aren't available, we have to use `state`.
|
||||||
try {
|
try {
|
||||||
@@ -66,7 +63,7 @@ export const computeStateDisplay = (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return state;
|
return state;
|
||||||
} catch (_e) {
|
} catch {
|
||||||
// Formatting methods may throw error if date parsing doesn't go well,
|
// Formatting methods may throw error if date parsing doesn't go well,
|
||||||
// just return the state string in that case.
|
// just return the state string in that case.
|
||||||
return state;
|
return state;
|
||||||
@@ -74,17 +71,7 @@ export const computeStateDisplay = (
|
|||||||
} else {
|
} else {
|
||||||
// If not trying to display an explicit state, create `Date` object from `stateObj`'s attributes then format.
|
// If not trying to display an explicit state, create `Date` object from `stateObj`'s attributes then format.
|
||||||
let date: Date;
|
let date: Date;
|
||||||
if (stateObj.attributes.has_date && stateObj.attributes.has_time) {
|
if (!stateObj.attributes.has_time) {
|
||||||
date = new Date(
|
|
||||||
stateObj.attributes.year,
|
|
||||||
stateObj.attributes.month - 1,
|
|
||||||
stateObj.attributes.day,
|
|
||||||
stateObj.attributes.hour,
|
|
||||||
stateObj.attributes.minute
|
|
||||||
);
|
|
||||||
return formatDateTime(date, locale);
|
|
||||||
}
|
|
||||||
if (stateObj.attributes.has_date) {
|
|
||||||
date = new Date(
|
date = new Date(
|
||||||
stateObj.attributes.year,
|
stateObj.attributes.year,
|
||||||
stateObj.attributes.month - 1,
|
stateObj.attributes.month - 1,
|
||||||
@@ -92,12 +79,20 @@ export const computeStateDisplay = (
|
|||||||
);
|
);
|
||||||
return formatDate(date, locale);
|
return formatDate(date, locale);
|
||||||
}
|
}
|
||||||
if (stateObj.attributes.has_time) {
|
if (!stateObj.attributes.has_date) {
|
||||||
date = new Date();
|
date = new Date();
|
||||||
date.setHours(stateObj.attributes.hour, stateObj.attributes.minute);
|
date.setHours(stateObj.attributes.hour, stateObj.attributes.minute);
|
||||||
return formatTime(date, locale);
|
return formatTime(date, locale);
|
||||||
}
|
}
|
||||||
return stateObj.state;
|
|
||||||
|
date = new Date(
|
||||||
|
stateObj.attributes.year,
|
||||||
|
stateObj.attributes.month - 1,
|
||||||
|
stateObj.attributes.day,
|
||||||
|
stateObj.attributes.hour,
|
||||||
|
stateObj.attributes.minute
|
||||||
|
);
|
||||||
|
return formatDateTime(date, locale);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,14 +111,6 @@ export const computeStateDisplay = (
|
|||||||
return formatNumber(compareState, locale);
|
return formatNumber(compareState, locale);
|
||||||
}
|
}
|
||||||
|
|
||||||
// state of button is a timestamp
|
|
||||||
if (
|
|
||||||
domain === "button" ||
|
|
||||||
(domain === "sensor" && stateObj.attributes.device_class === "timestamp")
|
|
||||||
) {
|
|
||||||
return formatDateTime(new Date(compareState), locale);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
// Return device class translation
|
// Return device class translation
|
||||||
(stateObj.attributes.device_class &&
|
(stateObj.attributes.device_class &&
|
||||||
|
@@ -1,30 +1,4 @@
|
|||||||
/** Return an icon representing a cover state. */
|
/** Return an icon representing a cover state. */
|
||||||
import {
|
|
||||||
mdiArrowUpBox,
|
|
||||||
mdiArrowDownBox,
|
|
||||||
mdiGarage,
|
|
||||||
mdiGarageOpen,
|
|
||||||
mdiGateArrowRight,
|
|
||||||
mdiGate,
|
|
||||||
mdiGateOpen,
|
|
||||||
mdiDoorOpen,
|
|
||||||
mdiDoorClosed,
|
|
||||||
mdiCircle,
|
|
||||||
mdiWindowShutter,
|
|
||||||
mdiWindowShutterOpen,
|
|
||||||
mdiBlinds,
|
|
||||||
mdiBlindsOpen,
|
|
||||||
mdiWindowClosed,
|
|
||||||
mdiWindowOpen,
|
|
||||||
mdiArrowExpandHorizontal,
|
|
||||||
mdiArrowUp,
|
|
||||||
mdiArrowCollapseHorizontal,
|
|
||||||
mdiArrowDown,
|
|
||||||
mdiCircleSlice8,
|
|
||||||
mdiArrowSplitVertical,
|
|
||||||
mdiCurtains,
|
|
||||||
mdiCurtainsClosed,
|
|
||||||
} from "@mdi/js";
|
|
||||||
import { HassEntity } from "home-assistant-js-websocket";
|
import { HassEntity } from "home-assistant-js-websocket";
|
||||||
|
|
||||||
export const coverIcon = (state?: string, stateObj?: HassEntity): string => {
|
export const coverIcon = (state?: string, stateObj?: HassEntity): string => {
|
||||||
@@ -34,84 +8,74 @@ export const coverIcon = (state?: string, stateObj?: HassEntity): string => {
|
|||||||
case "garage":
|
case "garage":
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case "opening":
|
case "opening":
|
||||||
return mdiArrowUpBox;
|
return "hass:arrow-up-box";
|
||||||
case "closing":
|
case "closing":
|
||||||
return mdiArrowDownBox;
|
return "hass:arrow-down-box";
|
||||||
case "closed":
|
case "closed":
|
||||||
return mdiGarage;
|
return "hass:garage";
|
||||||
default:
|
default:
|
||||||
return mdiGarageOpen;
|
return "hass:garage-open";
|
||||||
}
|
}
|
||||||
case "gate":
|
case "gate":
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case "opening":
|
case "opening":
|
||||||
case "closing":
|
case "closing":
|
||||||
return mdiGateArrowRight;
|
return "hass:gate-arrow-right";
|
||||||
case "closed":
|
case "closed":
|
||||||
return mdiGate;
|
return "hass:gate";
|
||||||
default:
|
default:
|
||||||
return mdiGateOpen;
|
return "hass:gate-open";
|
||||||
}
|
}
|
||||||
case "door":
|
case "door":
|
||||||
return open ? mdiDoorOpen : mdiDoorClosed;
|
return open ? "hass:door-open" : "hass:door-closed";
|
||||||
case "damper":
|
case "damper":
|
||||||
return open ? mdiCircle : mdiCircleSlice8;
|
return open ? "hass:circle" : "hass:circle-slice-8";
|
||||||
case "shutter":
|
case "shutter":
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case "opening":
|
case "opening":
|
||||||
return mdiArrowUpBox;
|
return "hass:arrow-up-box";
|
||||||
case "closing":
|
case "closing":
|
||||||
return mdiArrowDownBox;
|
return "hass:arrow-down-box";
|
||||||
case "closed":
|
case "closed":
|
||||||
return mdiWindowShutter;
|
return "hass:window-shutter";
|
||||||
default:
|
default:
|
||||||
return mdiWindowShutterOpen;
|
return "hass:window-shutter-open";
|
||||||
}
|
|
||||||
case "curtain":
|
|
||||||
switch (state) {
|
|
||||||
case "opening":
|
|
||||||
return mdiArrowSplitVertical;
|
|
||||||
case "closing":
|
|
||||||
return mdiArrowCollapseHorizontal;
|
|
||||||
case "closed":
|
|
||||||
return mdiCurtainsClosed;
|
|
||||||
default:
|
|
||||||
return mdiCurtains;
|
|
||||||
}
|
}
|
||||||
case "blind":
|
case "blind":
|
||||||
|
case "curtain":
|
||||||
case "shade":
|
case "shade":
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case "opening":
|
case "opening":
|
||||||
return mdiArrowUpBox;
|
return "hass:arrow-up-box";
|
||||||
case "closing":
|
case "closing":
|
||||||
return mdiArrowDownBox;
|
return "hass:arrow-down-box";
|
||||||
case "closed":
|
case "closed":
|
||||||
return mdiBlinds;
|
return "hass:blinds";
|
||||||
default:
|
default:
|
||||||
return mdiBlindsOpen;
|
return "hass:blinds-open";
|
||||||
}
|
}
|
||||||
case "window":
|
case "window":
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case "opening":
|
case "opening":
|
||||||
return mdiArrowUpBox;
|
return "hass:arrow-up-box";
|
||||||
case "closing":
|
case "closing":
|
||||||
return mdiArrowDownBox;
|
return "hass:arrow-down-box";
|
||||||
case "closed":
|
case "closed":
|
||||||
return mdiWindowClosed;
|
return "hass:window-closed";
|
||||||
default:
|
default:
|
||||||
return mdiWindowOpen;
|
return "hass:window-open";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case "opening":
|
case "opening":
|
||||||
return mdiArrowUpBox;
|
return "hass:arrow-up-box";
|
||||||
case "closing":
|
case "closing":
|
||||||
return mdiArrowDownBox;
|
return "hass:arrow-down-box";
|
||||||
case "closed":
|
case "closed":
|
||||||
return mdiWindowClosed;
|
return "hass:window-closed";
|
||||||
default:
|
default:
|
||||||
return mdiWindowOpen;
|
return "hass:window-open";
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -120,9 +84,9 @@ export const computeOpenIcon = (stateObj: HassEntity): string => {
|
|||||||
case "awning":
|
case "awning":
|
||||||
case "door":
|
case "door":
|
||||||
case "gate":
|
case "gate":
|
||||||
return mdiArrowExpandHorizontal;
|
return "hass:arrow-expand-horizontal";
|
||||||
default:
|
default:
|
||||||
return mdiArrowUp;
|
return "hass:arrow-up";
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -131,8 +95,8 @@ export const computeCloseIcon = (stateObj: HassEntity): string => {
|
|||||||
case "awning":
|
case "awning":
|
||||||
case "door":
|
case "door":
|
||||||
case "gate":
|
case "gate":
|
||||||
return mdiArrowCollapseHorizontal;
|
return "hass:arrow-collapse-horizontal";
|
||||||
default:
|
default:
|
||||||
return mdiArrowDown;
|
return "hass:arrow-down";
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user