Compare commits

..

1 Commits

Author SHA1 Message Date
Paulus Schoutsen
368973b668 Add app alias 2020-06-21 12:37:12 -07:00
658 changed files with 9886 additions and 29938 deletions

View File

@@ -1,7 +1,7 @@
{ {
"extends": [ "extends": [
"airbnb-typescript/base",
"plugin:@typescript-eslint/recommended", "plugin:@typescript-eslint/recommended",
"airbnb-typescript/base",
"plugin:wc/recommended", "plugin:wc/recommended",
"plugin:lit/recommended", "plugin:lit/recommended",
"prettier", "prettier",
@@ -45,16 +45,16 @@
"func-names": 0, "func-names": 0,
"prefer-arrow-callback": 0, "prefer-arrow-callback": 0,
"no-underscore-dangle": 0, "no-underscore-dangle": 0,
"no-var": 0,
"strict": 0, "strict": 0,
"prefer-spread": 0, "prefer-spread": 0,
"no-plusplus": 0, "no-plusplus": 0,
"no-bitwise": 2, "no-bitwise": 0,
"comma-dangle": 0, "comma-dangle": 0,
"vars-on-top": 0, "vars-on-top": 0,
"no-continue": 0, "no-continue": 0,
"no-param-reassign": 0, "no-param-reassign": 0,
"no-multi-assign": 0, "no-multi-assign": 0,
"no-console": 2,
"radix": 0, "radix": 0,
"no-alert": 0, "no-alert": 0,
"no-return-await": 0, "no-return-await": 0,
@@ -66,11 +66,7 @@
"import/prefer-default-export": 0, "import/prefer-default-export": 0,
"import/no-unresolved": 0, "import/no-unresolved": 0,
"import/no-cycle": 0, "import/no-cycle": 0,
"import/extensions": [ "import/extensions": 0,
2,
"ignorePackages",
{ "ts": "never", "js": "never" }
],
"no-restricted-syntax": ["error", "LabeledStatement", "WithStatement"], "no-restricted-syntax": ["error", "LabeledStatement", "WithStatement"],
"object-curly-newline": 0, "object-curly-newline": 0,
"default-case": 0, "default-case": 0,

View File

@@ -51,7 +51,7 @@ DO NOT DELETE ANY TEXT from this template! Otherwise, your issue may be closed w
<!-- <!--
Provide details about the versions you are using, which helps us reproducing Provide details about the versions you are using, which helps us reproducing
and finding the issue quicker. Version information is found in the and finding the issue quicker. Version information is found in the
Home Assistant frontend: Configuration -> Info. Home Assistant frontend: Developer tools -> Info.
Browser version and operating system is important! Please try to replicate Browser version and operating system is important! Please try to replicate
your issue in a different browser and be sure to include your findings. your issue in a different browser and be sure to include your findings.

View File

@@ -34,8 +34,10 @@ jobs:
run: yarn install run: yarn install
env: env:
CI: true CI: true
- name: Build resources - name: Build icons
run: ./node_modules/.bin/gulp gen-icons-json build-translations gather-gallery-demos run: ./node_modules/.bin/gulp gen-icons-json
- name: Build translations
run: ./node_modules/.bin/gulp build-translations
- name: Run eslint - name: Run eslint
run: ./node_modules/.bin/eslint '{**/src,src}/**/*.{js,ts,html}' --ignore-path .gitignore run: ./node_modules/.bin/eslint '{**/src,src}/**/*.{js,ts,html}' --ignore-path .gitignore
- name: Run tsc - name: Run tsc

2
.gitignore vendored
View File

@@ -24,7 +24,7 @@ dist
.vscode/* .vscode/*
!.vscode/extensions.json !.vscode/extensions.json
# Cast dev settings # Cast dev settings
src/cast/dev_const.ts src/cast/dev_const.ts
# Secrets # Secrets

View File

@@ -57,7 +57,7 @@ module.exports.babelOptions = ({ latestBuild }) => ({
].filter(Boolean), ].filter(Boolean),
plugins: [ plugins: [
// Part of ES2018. Converts {...a, b: 2} to Object.assign({}, a, {b: 2}) // Part of ES2018. Converts {...a, b: 2} to Object.assign({}, a, {b: 2})
!latestBuild && [ [
"@babel/plugin-proposal-object-rest-spread", "@babel/plugin-proposal-object-rest-spread",
{ loose: true, useBuiltIns: true }, { loose: true, useBuiltIns: true },
], ],
@@ -73,7 +73,7 @@ module.exports.babelOptions = ({ latestBuild }) => ({
require("@babel/plugin-proposal-class-properties").default, require("@babel/plugin-proposal-class-properties").default,
{ loose: true }, { loose: true },
], ],
].filter(Boolean), ],
}); });
// Are already ES5, cause warnings when babelified. // Are already ES5, cause warnings when babelified.
@@ -178,6 +178,7 @@ module.exports.config = {
publicPath: publicPath(latestBuild, paths.hassio_publicPath), publicPath: publicPath(latestBuild, paths.hassio_publicPath),
isProdBuild, isProdBuild,
latestBuild, latestBuild,
dontHash: new Set(["entrypoint"]),
}; };
}, },

View File

@@ -90,6 +90,8 @@ gulp.task("gen-pages-prod", (done) => {
}); });
gulp.task("gen-index-app-dev", (done) => { gulp.task("gen-index-app-dev", (done) => {
// In dev mode we don't mangle names, so we hardcode urls. That way we can
// run webpack as last in watch mode, which blocks output.
const content = renderTemplate("index", { const content = renderTemplate("index", {
latestAppJS: "/frontend_latest/app.js", latestAppJS: "/frontend_latest/app.js",
latestCoreJS: "/frontend_latest/core.js", latestCoreJS: "/frontend_latest/core.js",
@@ -199,6 +201,8 @@ gulp.task("gen-index-cast-prod", (done) => {
}); });
gulp.task("gen-index-demo-dev", (done) => { gulp.task("gen-index-demo-dev", (done) => {
// In dev mode we don't mangle names, so we hardcode urls. That way we can
// run webpack as last in watch mode, which blocks output.
const content = renderDemoTemplate("index", { const content = renderDemoTemplate("index", {
latestDemoJS: "/frontend_latest/main.js", latestDemoJS: "/frontend_latest/main.js",
@@ -236,6 +240,8 @@ gulp.task("gen-index-demo-prod", (done) => {
}); });
gulp.task("gen-index-gallery-dev", (done) => { gulp.task("gen-index-gallery-dev", (done) => {
// In dev mode we don't mangle names, so we hardcode urls. That way we can
// run webpack as last in watch mode, which blocks output.
const content = renderGalleryTemplate("index", { const content = renderGalleryTemplate("index", {
latestGalleryJS: "./frontend_latest/entrypoint.js", latestGalleryJS: "./frontend_latest/entrypoint.js",
}); });
@@ -263,42 +269,3 @@ gulp.task("gen-index-gallery-prod", (done) => {
); );
done(); done();
}); });
gulp.task("gen-index-hassio-dev", async () => {
writeHassioEntrypoint(
`${paths.hassio_publicPath}/frontend_latest/entrypoint.js`,
`${paths.hassio_publicPath}/frontend_es5/entrypoint.js`
);
});
gulp.task("gen-index-hassio-prod", async () => {
const latestManifest = require(path.resolve(
paths.hassio_output_latest,
"manifest.json"
));
const es5Manifest = require(path.resolve(
paths.hassio_output_es5,
"manifest.json"
));
writeHassioEntrypoint(
latestManifest["entrypoint.js"],
es5Manifest["entrypoint.js"]
);
});
function writeHassioEntrypoint(latestEntrypoint, es5Entrypoint) {
fs.mkdirSync(paths.hassio_output_root, { recursive: true });
fs.writeFileSync(
path.resolve(paths.hassio_output_root, "entrypoint.js"),
`
try {
new Function("import('${latestEntrypoint}')")();
} catch (err) {
var el = document.createElement('script');
el.src = '${es5Entrypoint}';
document.body.appendChild(el);
}
`,
{ encoding: "utf-8" }
);
}

View File

@@ -1,10 +1,7 @@
// Run demo develop mode // Run demo develop mode
const gulp = require("gulp"); const gulp = require("gulp");
const fs = require("fs");
const path = require("path");
const env = require("../env"); const env = require("../env");
const paths = require("../paths");
require("./clean.js"); require("./clean.js");
require("./translations.js"); require("./translations.js");
@@ -15,31 +12,6 @@ require("./service-worker.js");
require("./entry-html.js"); require("./entry-html.js");
require("./rollup.js"); require("./rollup.js");
gulp.task("gather-gallery-demos", async function gatherDemos() {
const files = await fs.promises.readdir(
path.resolve(paths.gallery_dir, "src/demos")
);
let content = "export const DEMOS = {\n";
for (const file of files) {
const demoId = path.basename(file, ".ts");
const demoPath = "../src/demos/" + demoId;
content += ` "${demoId}": () => import("${demoPath}"),\n`;
}
content += "};";
const galleryBuild = path.resolve(paths.gallery_dir, "build");
fs.mkdirSync(galleryBuild, { recursive: true });
fs.writeFileSync(
path.resolve(galleryBuild, "import-demos.ts"),
content,
"utf-8"
);
});
gulp.task( gulp.task(
"develop-gallery", "develop-gallery",
gulp.series( gulp.series(
@@ -48,11 +20,7 @@ gulp.task(
}, },
"clean-gallery", "clean-gallery",
"translations-enable-merge-backend", "translations-enable-merge-backend",
gulp.parallel( gulp.parallel("gen-icons-json", "build-translations"),
"gen-icons-json",
"build-translations",
"gather-gallery-demos"
),
"copy-static-gallery", "copy-static-gallery",
"gen-index-gallery-dev", "gen-index-gallery-dev",
env.useRollup() ? "rollup-dev-server-gallery" : "webpack-dev-server-gallery" env.useRollup() ? "rollup-dev-server-gallery" : "webpack-dev-server-gallery"
@@ -67,11 +35,7 @@ gulp.task(
}, },
"clean-gallery", "clean-gallery",
"translations-enable-merge-backend", "translations-enable-merge-backend",
gulp.parallel( gulp.parallel("gen-icons-json", "build-translations"),
"gen-icons-json",
"build-translations",
"gather-gallery-demos"
),
"copy-static-gallery", "copy-static-gallery",
env.useRollup() ? "rollup-prod-gallery" : "webpack-prod-gallery", env.useRollup() ? "rollup-prod-gallery" : "webpack-prod-gallery",
"gen-index-gallery-prod" "gen-index-gallery-prod"

View File

@@ -11,7 +11,6 @@ const META_PATH = path.resolve(ICON_PACKAGE_PATH, "meta.json");
const PACKAGE_PATH = path.resolve(ICON_PACKAGE_PATH, "package.json"); const PACKAGE_PATH = path.resolve(ICON_PACKAGE_PATH, "package.json");
const ICON_PATH = path.resolve(ICON_PACKAGE_PATH, "svg"); const ICON_PATH = path.resolve(ICON_PACKAGE_PATH, "svg");
const OUTPUT_DIR = path.resolve(__dirname, "../../build/mdi"); const OUTPUT_DIR = path.resolve(__dirname, "../../build/mdi");
const REMOVED_ICONS_PATH = path.resolve(__dirname, "../removedIcons.json");
const encoding = "utf8"; const encoding = "utf8";
@@ -26,13 +25,6 @@ const getMeta = () => {
}); });
}; };
const addRemovedMeta = (meta) => {
const file = fs.readFileSync(REMOVED_ICONS_PATH, { encoding });
const removed = JSON.parse(file);
const combinedMeta = [...meta, ...removed];
return combinedMeta.sort((a, b) => a.name.localeCompare(b.name));
};
const splitBySize = (meta) => { const splitBySize = (meta) => {
const chunks = []; const chunks = [];
const CHUNK_SIZE = 50000; const CHUNK_SIZE = 50000;
@@ -77,7 +69,7 @@ const findDifferentiator = (curString, prevString) => {
}; };
gulp.task("gen-icons-json", (done) => { gulp.task("gen-icons-json", (done) => {
const meta = addRemovedMeta(getMeta()); const meta = getMeta();
const split = splitBySize(meta); const split = splitBySize(meta);
if (!fs.existsSync(OUTPUT_DIR)) { if (!fs.existsSync(OUTPUT_DIR)) {

View File

@@ -11,6 +11,24 @@ require("./webpack.js");
require("./compress.js"); require("./compress.js");
require("./rollup.js"); require("./rollup.js");
async function writeEntrypointJS() {
// We ship two builds and we need to do feature detection on what version to load.
fs.mkdirSync(paths.hassio_output_root, { recursive: true });
fs.writeFileSync(
path.resolve(paths.hassio_output_root, "entrypoint.js"),
`
try {
new Function("import('${paths.hassio_publicPath}/frontend_latest/entrypoint.js')")();
} catch (err) {
var el = document.createElement('script');
el.src = '${paths.hassio_publicPath}/frontend_es5/entrypoint.js';
document.body.appendChild(el);
}
`,
{ encoding: "utf-8" }
);
}
gulp.task( gulp.task(
"develop-hassio", "develop-hassio",
gulp.series( gulp.series(
@@ -19,7 +37,7 @@ gulp.task(
}, },
"clean-hassio", "clean-hassio",
"gen-icons-json", "gen-icons-json",
"gen-index-hassio-dev", writeEntrypointJS,
env.useRollup() ? "rollup-watch-hassio" : "webpack-watch-hassio" env.useRollup() ? "rollup-watch-hassio" : "webpack-watch-hassio"
) )
); );
@@ -33,7 +51,7 @@ gulp.task(
"clean-hassio", "clean-hassio",
"gen-icons-json", "gen-icons-json",
env.useRollup() ? "rollup-prod-hassio" : "webpack-prod-hassio", env.useRollup() ? "rollup-prod-hassio" : "webpack-prod-hassio",
"gen-index-hassio-prod", writeEntrypointJS,
...// Don't compress running tests ...// Don't compress running tests
(env.isTest() ? [] : ["compress-hassio"]) (env.isTest() ? [] : ["compress-hassio"])
) )

View File

@@ -34,11 +34,6 @@ module.exports = {
hassio_dir: path.resolve(__dirname, "../hassio"), hassio_dir: path.resolve(__dirname, "../hassio"),
hassio_output_root: path.resolve(__dirname, "../hassio/build"), hassio_output_root: path.resolve(__dirname, "../hassio/build"),
hassio_output_latest: path.resolve(
__dirname,
"../hassio/build/frontend_latest"
),
hassio_output_es5: path.resolve(__dirname, "../hassio/build/frontend_es5"),
hassio_publicPath: "/api/hassio/app", hassio_publicPath: "/api/hassio/app",
translations_src: path.resolve(__dirname, "../src/translations"), translations_src: path.resolve(__dirname, "../src/translations"),

View File

@@ -1,267 +0,0 @@
[
{
"path": "M17.5,15.61C17.33,15.37 9.53,5.4 9.27,5.08C9,4.75 9.08,4.65 9.13,4.59C9.22,4.5 9.36,4.5 9.93,4.5C10.26,4.5 13.59,4.5 13.94,4.47C14.66,4.47 14.78,4.53 14.85,4.56C14.93,4.58 15.13,4.75 15.26,4.92C15.33,5 22.32,13.36 22.39,13.45C22.46,13.54 22.59,13.69 22.67,13.84C22.76,14 22.77,14.18 22.64,14.25C22.56,14.3 18.7,15.89 18.59,15.93C18.5,16 18.27,16.06 18.11,16.04C18,16 17.77,15.92 17.5,15.61M21.47,15.42L21.75,15.47C21.75,15.47 22.68,15.65 22.77,15.67C22.87,15.69 22.96,15.76 22.95,15.79C22.94,15.87 22.9,15.91 22.83,15.95C22.77,16 18.58,18.58 18.5,18.62C18.43,18.66 18.33,18.72 18.11,18.75C17.7,18.83 16.91,18.61 16.66,18.56C16.41,18.5 6.15,16.23 6.06,16.2C5.97,16.17 5.91,16.16 5.9,16.08C5.89,15.94 6.11,15.88 6.28,15.81C6.46,15.75 11.28,14 11.45,13.93C11.62,13.86 11.84,13.84 11.95,13.83C12.06,13.82 12.73,13.93 13.03,13.97C13.34,14 14.2,14.15 14.2,14.15L16.16,16.7C16.5,17.09 16.72,17.25 17,17.28C17.15,17.29 17.31,17.25 17.42,17.2C17.5,17.16 21.47,15.42 21.47,15.42M10.25,9.18L11.96,11.37L12,11.45V11.5C11.96,11.54 8.93,14.32 8.91,14.35L5.72,15.5C5.72,15.5 5.63,15.55 5.58,15.58C5.53,15.61 5.47,15.67 5.5,15.82C5.5,15.87 5.5,16.59 5.5,16.79L1.56,18.04C1.37,18.1 1,18.23 0.95,18.19C0.88,18.14 0.97,18.03 1,17.97C1.06,17.91 9.08,10 9.39,9.7C9.84,9.24 10.25,9.18 10.25,9.18",
"name": "accusoft"
},
{
"path": "M4.94,11.12C5.23,11.12 5.5,11.16 5.76,11.23C5.77,9.09 7.5,7.35 9.65,7.35C11.27,7.35 12.67,8.35 13.24,9.77C13.83,9 14.74,8.53 15.76,8.53C17.5,8.53 18.94,9.95 18.94,11.71C18.94,11.95 18.91,12.2 18.86,12.43C19.1,12.34 19.37,12.29 19.65,12.29C20.95,12.29 22,13.35 22,14.65C22,15.95 20.95,17 19.65,17C18.35,17 6.36,17 4.94,17C3.32,17 2,15.68 2,14.06C2,12.43 3.32,11.12 4.94,11.12Z",
"name": "amazon-drive"
},
{
"path": "M8,11.5A1.25,1.25 0 0,0 6.75,12.75A1.25,1.25 0 0,0 8,14A1.25,1.25 0 0,0 9.25,12.75A1.25,1.25 0 0,0 8,11.5M16,11.5A1.25,1.25 0 0,0 14.75,12.75A1.25,1.25 0 0,0 16,14A1.25,1.25 0 0,0 17.25,12.75A1.25,1.25 0 0,0 16,11.5M12,7C13.5,7 14.9,7.33 16.18,7.91L18.34,5.75C18.73,5.36 19.36,5.36 19.75,5.75C20.14,6.14 20.14,6.77 19.75,7.16L17.95,8.96C20.41,10.79 22,13.71 22,17H2C2,13.71 3.59,10.79 6.05,8.96L4.25,7.16C3.86,6.77 3.86,6.14 4.25,5.75C4.64,5.36 5.27,5.36 5.66,5.75L7.82,7.91C9.1,7.33 10.5,7 12,7Z",
"name": "android-head"
},
{
"path": "M2,16.25C2,16.25 4,3.75 12,3.75C20,3.75 22,16.25 22,16.25C22,16.25 20,20.25 12,20.25C4,20.25 2,16.25 2,16.25M3.35,15.65C3.35,15.65 4.3,19 12,19C17,19 20,17.8 20.65,15.85C21.3,13.9 15.65,7.6 14.65,7.6C13.65,7.6 11.2,12 10.45,12C8.45,12 8.9,10 7.15,10C5.4,10 3.35,15.65 3.35,15.65Z",
"name": "basecamp"
},
{
"path": "M7,12A5,5 0 0,0 12,17A5,5 0 0,0 17,12A5,5 0 0,0 12,7C10.87,7 9.84,7.37 9,8V2.46C9.95,2.16 10.95,2 12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12C2,8.3 4,5.07 7,3.34V12M12,9A3,3 0 0,1 15,12A3,3 0 0,1 12,15A3,3 0 0,1 9,12A3,3 0 0,1 12,9Z",
"name": "beats"
},
{
"path": "M19.58,12.27C19.54,11.65 19.33,11.18 18.96,10.86C18.59,10.54 18.13,10.38 17.58,10.38C17,10.38 16.5,10.55 16.19,10.89C15.86,11.23 15.65,11.69 15.57,12.27M21.92,12.04C22,12.45 22,13.04 22,13.81H15.5C15.55,14.71 15.85,15.33 16.44,15.69C16.79,15.92 17.22,16.03 17.73,16.03C18.26,16.03 18.69,15.89 19,15.62C19.2,15.47 19.36,15.27 19.5,15H21.88C21.82,15.54 21.53,16.07 21,16.62C20.22,17.5 19.1,17.92 17.66,17.92C16.47,17.92 15.43,17.55 14.5,16.82C13.62,16.09 13.16,14.9 13.16,13.25C13.16,11.7 13.57,10.5 14.39,9.7C15.21,8.87 16.27,8.46 17.58,8.46C18.35,8.46 19.05,8.6 19.67,8.88C20.29,9.16 20.81,9.59 21.21,10.2C21.58,10.73 21.81,11.34 21.92,12.04M9.58,14.07C9.58,13.42 9.31,12.97 8.79,12.73C8.5,12.6 8.08,12.53 7.54,12.5H4.87V15.84H7.5C8.04,15.84 8.46,15.77 8.76,15.62C9.31,15.35 9.58,14.83 9.58,14.07M4.87,10.46H7.5C8.04,10.46 8.5,10.36 8.82,10.15C9.16,9.95 9.32,9.58 9.32,9.06C9.32,8.5 9.1,8.1 8.66,7.91C8.27,7.78 7.78,7.72 7.19,7.72H4.87M11.72,12.42C12.04,12.92 12.2,13.53 12.2,14.24C12.2,15 12,15.64 11.65,16.23C11.41,16.62 11.12,16.94 10.77,17.21C10.37,17.5 9.9,17.72 9.36,17.83C8.82,17.94 8.24,18 7.61,18H2V5.55H8C9.53,5.58 10.6,6 11.23,6.88C11.61,7.41 11.8,8.04 11.8,8.78C11.8,9.54 11.61,10.15 11.23,10.61C11,10.87 10.7,11.11 10.28,11.32C10.91,11.55 11.39,11.92 11.72,12.42M20.06,7.32H15.05V6.07H20.06V7.32Z",
"name": "behance"
},
{
"path": "M5.45,10.28C6.4,10.28 7.5,11.05 7.5,12C7.5,12.95 6.4,13.72 5.45,13.72H2L2.69,10.28H5.45M6.14,4.76C7.09,4.76 8.21,5.53 8.21,6.5C8.21,7.43 7.09,8.21 6.14,8.21H2.69L3.38,4.76H6.14M13.03,4.76C14,4.76 15.1,5.53 15.1,6.5C15.1,7.43 14,8.21 13.03,8.21H9.41L10.1,4.76H13.03M12.34,10.28C13.3,10.28 14.41,11.05 14.41,12C14.41,12.95 13.3,13.72 12.34,13.72H8.72L9.41,10.28H12.34M10.97,15.79C11.92,15.79 13.03,16.57 13.03,17.5C13.03,18.47 11.92,19.24 10.97,19.24H7.5L8.21,15.79H10.97M18.55,13.72C19.5,13.72 20.62,14.5 20.62,15.45C20.62,16.4 19.5,17.17 18.55,17.17H15.1L15.79,13.72H18.55M19.93,8.21C20.88,8.21 22,9 22,9.93C22,10.88 20.88,11.66 19.93,11.66H16.5L17.17,8.21H19.93Z",
"name": "blackberry"
},
{
"path": "M12,3A9,9 0 0,1 21,12A9,9 0 0,1 12,21A9,9 0 0,1 3,12A9,9 0 0,1 12,3M5.94,8.5C4,11.85 5.15,16.13 8.5,18.06C11.85,20 18.85,7.87 15.5,5.94C12.15,4 7.87,5.15 5.94,8.5Z",
"name": "cisco-webex"
},
{
"path": "M11.9,14.5H10.8V9.5H11.9C13.5,9.5 14.6,10.4 14.6,12C14.6,13.6 13.5,14.5 11.9,14.5M11.9,7H8.1V17H11.8C15.3,17 17.4,14.9 17.4,12V12C17.4,9.1 15.4,7 11.9,7M12,20C10.1,20 8.3,19.3 6.9,18.1L6.2,17.5L4.5,17.7L5.2,16.1L4.9,15.3C4.4,14.2 4.2,13.1 4.2,11.9C4.2,7.5 7.8,3.9 12.1,3.9C16.4,3.9 19.9,7.6 19.9,12C19.9,16.4 16.3,20 12,20M12,2C6.5,2 2.1,6.5 2.1,12C2.1,13.5 2.4,14.9 3,16.2L1.4,20.3L5.7,19.7C7.4,21.2 9.7,22.1 12.1,22.1C17.6,22.1 22,17.6 22,12.1C22,6.6 17.5,2 12,2Z",
"name": "disqus-outline"
},
{
"path": "M16.42,18.42C16,16.5 15.5,14.73 15,13.17C15.5,13.1 16,13.06 16.58,13.06H16.6V13.06H16.6C17.53,13.06 18.55,13.18 19.66,13.43C19.28,15.5 18.08,17.27 16.42,18.42M12,19.8C10.26,19.8 8.66,19.23 7.36,18.26C7.64,17.81 8.23,16.94 9.18,16.04C10.14,15.11 11.5,14.15 13.23,13.58C13.82,15.25 14.36,17.15 14.77,19.29C13.91,19.62 13,19.8 12,19.8M4.2,12C4.2,11.96 4.2,11.93 4.2,11.89C4.42,11.9 4.71,11.9 5.05,11.9H5.06C6.62,11.89 9.36,11.76 12.14,10.89C12.29,11.22 12.44,11.56 12.59,11.92C10.73,12.54 9.27,13.53 8.19,14.5C7.16,15.46 6.45,16.39 6.04,17C4.9,15.66 4.2,13.91 4.2,12M8.55,5C9.1,5.65 10.18,7.06 11.34,9.25C9,9.96 6.61,10.12 5.18,10.12C5.14,10.12 5.1,10.12 5.06,10.12H5.05C4.81,10.12 4.6,10.12 4.43,10.11C5,7.87 6.5,6 8.55,5M12,4.2C13.84,4.2 15.53,4.84 16.86,5.91C15.84,7.14 14.5,8 13.03,8.65C12,6.67 11,5.25 10.34,4.38C10.88,4.27 11.43,4.2 12,4.2M18.13,7.18C19.1,8.42 19.71,9.96 19.79,11.63C18.66,11.39 17.6,11.28 16.6,11.28V11.28H16.59C15.79,11.28 15.04,11.35 14.33,11.47C14.16,11.05 14,10.65 13.81,10.26C15.39,9.57 16.9,8.58 18.13,7.18M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2Z",
"name": "dribbble"
},
{
"path": "M5,3H19A2,2 0 0,1 21,5V19A2,2 0 0,1 19,21H5A2,2 0 0,1 3,19V5A2,2 0 0,1 5,3M15.09,16.5C14.81,15.14 14.47,13.91 14.08,12.82L15.2,12.74H15.22V12.74C15.87,12.74 16.59,12.82 17.36,13C17.09,14.44 16.26,15.69 15.09,16.5M12,17.46C10.79,17.46 9.66,17.06 8.76,16.39C8.95,16.07 9.36,15.46 10,14.83C10.7,14.18 11.64,13.5 12.86,13.11C13.28,14.27 13.65,15.6 13.94,17.1C13.33,17.33 12.68,17.46 12,17.46M6.54,12V11.92L7.14,11.93V11.93C8.24,11.93 10.15,11.83 12.1,11.22L12.41,11.94C11.11,12.38 10.09,13.07 9.34,13.76C8.61,14.42 8.12,15.08 7.83,15.5C7.03,14.56 6.54,13.34 6.54,12M9.59,7.11C9.97,7.56 10.73,8.54 11.54,10.08C9.89,10.57 8.23,10.68 7.22,10.68H7.14V10.68H6.7C7.09,9.11 8.17,7.81 9.59,7.11M12,6.54C13.29,6.54 14.47,7 15.41,7.74C14.69,8.6 13.74,9.22 12.72,9.66C12,8.27 11.31,7.28 10.84,6.67C11.21,6.59 11.6,6.54 12,6.54M16.29,8.63C16.97,9.5 17.4,10.57 17.45,11.74C16.66,11.58 15.92,11.5 15.22,11.5V11.5C14.66,11.5 14.13,11.54 13.63,11.63L13.27,10.78C14.37,10.3 15.43,9.61 16.29,8.63M12,5A7,7 0 0,0 5,12A7,7 0 0,0 12,19A7,7 0 0,0 19,12A7,7 0 0,0 12,5Z",
"name": "dribbble-box"
},
{
"path": "M6.72,20.78C8.23,20.71 10.07,20.78 11.87,20.78C13.72,20.78 15.62,20.66 17.12,20.78C17.72,20.83 18.28,21.19 18.77,20.87C19.16,20.38 18.87,19.71 18.96,19.05C19.12,17.78 20.28,16.27 18.59,15.95C17.87,16.61 18.35,17.23 17.95,18.05C17.45,19.03 15.68,19.37 14,19.5C12.54,19.62 10,19.76 9.5,18.77C9.04,17.94 9.29,16.65 9.29,15.58C9.29,14.38 9.16,13.22 9.5,12.3C11.32,12.43 13.7,11.69 15,12.5C15.87,13 15.37,14.06 16.38,14.4C17.07,14.21 16.7,13.32 16.66,12.5C16.63,11.94 16.63,11.19 16.66,10.57C16.69,9.73 17,8.76 16.1,8.74C15.39,9.3 15.93,10.23 15.18,10.75C14.95,10.92 14.43,11 14.08,11C12.7,11.17 10.54,11.05 9.38,10.84C9.23,9.16 9.24,6.87 9.38,5.19C10,4.57 11.45,4.54 12.42,4.55C14.13,4.55 16.79,4.7 17.3,5.55C17.58,6 17.36,7 17.85,7.1C18.85,7.33 18.36,5.55 18.41,4.73C18.44,4.11 18.71,3.72 18.59,3.27C18.27,2.83 17.79,3.05 17.5,3.09C14.35,3.5 9.6,3.27 6.26,3.27C5.86,3.27 5.16,3.07 4.88,3.54C4.68,4.6 6.12,4.16 6.62,4.73C6.79,4.91 7.03,5.73 7.08,6.28C7.23,7.74 7.08,9.97 7.08,12.12C7.08,14.38 7.26,16.67 7.08,18.05C7,18.53 6.73,19.3 6.62,19.41C6,20.04 4.34,19.35 4.5,20.69C5.09,21.08 5.93,20.82 6.72,20.78Z",
"name": "etsy"
},
{
"path": "M12,17.5C10.15,17.5 8.42,16.56 7.41,15L17.41,12.75L22.08,11.75C22.05,10.32 21.71,8.92 21.08,7.64C18.66,2.61 12.62,0.5 7.58,2.92C2.55,5.34 0.44,11.38 2.86,16.41C5.29,21.44 11.33,23.56 16.36,21.14C18.5,20.09 20.25,18.31 21.22,16.11L16.61,15C15.6,16.57 13.86,17.5 12,17.5M12,6.5C13.76,6.5 15.41,7.34 16.44,8.77L6.57,11.19C6.96,8.5 9.28,6.5 12,6.5Z",
"name": "eventbrite"
},
{
"path": "M5,3H19A2,2 0 0,1 21,5V19A2,2 0 0,1 19,21H5A2,2 0 0,1 3,19V5A2,2 0 0,1 5,3M18,5H15.5A3.5,3.5 0 0,0 12,8.5V11H10V14H12V21H15V14H18V11H15V9A1,1 0 0,1 16,8H18V5Z",
"name": "facebook-box"
},
{
"path": "M21,12A9,9 0 0,1 12,21H4.5L9.74,15.76L11.16,17.17L9.33,19H12A7,7 0 0,0 19,12V7L21,5V12M3,12A9,9 0 0,1 12,3H19.5L14.26,8.24L12.84,6.83L14.67,5H12A7,7 0 0,0 5,12V17L3,19V12Z",
"name": "flattr"
},
{
"path": "M11,12C11,14.5 9,16.5 6.5,16.5C4,16.5 2,14.5 2,12C2,9.5 4,7.5 6.5,7.5C9,7.5 11,9.5 11,12M17.5,7.5C15,7.5 13,9.5 13,12C13,14.5 15,16.5 17.5,16.5C20,16.5 22,14.5 22,12C22,9.5 20,7.5 17.5,7.5Z",
"name": "flickr"
},
{
"path": "M17,5L16.57,7.5C16.5,7.73 16.2,8 15.91,8C15.61,8 12,8 12,8C11.53,8 10.95,8.32 10.95,8.79V9.2C10.95,9.67 11.53,10 12,10C12,10 14.95,10 15.28,10C15.61,10 15.93,10.36 15.86,10.71C15.79,11.07 14.94,13.28 14.9,13.5C14.86,13.67 14.64,14 14.25,14C13.92,14 11.37,14 11.37,14C10.85,14 10.69,14.07 10.34,14.5C10,14.94 7.27,18.1 7.27,18.1C7.24,18.13 7,18.04 7,18V5C7,4.7 7.61,4 8,4C8,4 16.17,4 16.5,4C16.82,4 17.08,4.61 17,5M17,14.45C17.11,13.97 18.78,6.72 19.22,4.55M17.58,2C17.58,2 8.38,2 6.91,2C5.43,2 5,3.11 5,3.8C5,4.5 5,20.76 5,20.76C5,21.54 5.42,21.84 5.66,21.93C5.9,22.03 6.55,22.11 6.94,21.66C6.94,21.66 11.65,16.22 11.74,16.13C11.87,16 11.87,16 12,16C12.26,16 14.2,16 15.26,16C16.63,16 16.85,15 17,14.45C17.11,13.97 18.78,6.72 19.22,4.55C19.56,2.89 19.14,2 17.58,2Z",
"name": "foursquare"
},
{
"path": "M5,3H19A2,2 0 0,1 21,5V19A2,2 0 0,1 19,21H14.56C14.24,20.93 14.23,20.32 14.23,20.11L14.24,17.64C14.24,16.8 13.95,16.25 13.63,15.97C15.64,15.75 17.74,15 17.74,11.53C17.74,10.55 17.39,9.74 16.82,9.11C16.91,8.89 17.22,7.97 16.73,6.73C16.73,6.73 15.97,6.5 14.25,7.66C13.53,7.46 12.77,7.36 12,7.35C11.24,7.36 10.46,7.46 9.75,7.66C8.03,6.5 7.27,6.73 7.27,6.73C6.78,7.97 7.09,8.89 7.18,9.11C6.61,9.74 6.26,10.55 6.26,11.53C6.26,15 8.36,15.75 10.36,16C10.1,16.2 9.87,16.6 9.79,17.18C9.27,17.41 7.97,17.81 7.17,16.43C7.17,16.43 6.69,15.57 5.79,15.5C5.79,15.5 4.91,15.5 5.73,16.05C5.73,16.05 6.32,16.33 6.73,17.37C6.73,17.37 7.25,19.12 9.76,18.58L9.77,20.11C9.77,20.32 9.75,20.93 9.43,21H5A2,2 0 0,1 3,19V5A2,2 0 0,1 5,3Z",
"name": "github-box"
},
{
"path": "M20.38,8.53C20.54,8.13 21.06,6.54 20.21,4.39C20.21,4.39 18.9,4 15.91,6C14.66,5.67 13.33,5.62 12,5.62C10.68,5.62 9.34,5.67 8.09,6C5.1,3.97 3.79,4.39 3.79,4.39C2.94,6.54 3.46,8.13 3.63,8.53C2.61,9.62 2,11 2,12.72C2,19.16 6.16,20.61 12,20.61C17.79,20.61 22,19.16 22,12.72C22,11 21.39,9.62 20.38,8.53M12,19.38C7.88,19.38 4.53,19.19 4.53,15.19C4.53,14.24 5,13.34 5.8,12.61C7.14,11.38 9.43,12.03 12,12.03C14.59,12.03 16.85,11.38 18.2,12.61C19,13.34 19.5,14.23 19.5,15.19C19.5,19.18 16.13,19.38 12,19.38M8.86,13.12C8.04,13.12 7.36,14.12 7.36,15.34C7.36,16.57 8.04,17.58 8.86,17.58C9.69,17.58 10.36,16.58 10.36,15.34C10.36,14.11 9.69,13.12 8.86,13.12M15.14,13.12C14.31,13.12 13.64,14.11 13.64,15.34C13.64,16.58 14.31,17.58 15.14,17.58C15.96,17.58 16.64,16.58 16.64,15.34C16.64,14.11 16,13.12 15.14,13.12Z",
"name": "github-face"
},
{
"path": "M8,2A3,3 0 0,0 5,5V16.5H8V5H19A3,3 0 0,0 16,2H8M16,7.5V19H5A3,3 0 0,0 8,22H16A3,3 0 0,0 19,19V7.5H16Z",
"name": "glassdoor"
},
{
"path": "M2,22L8.5,2H15.4L9.2,20C9.2,20 8.6,22 7,22C5.9,22 2,22 2,22M16.4,5L13,15L15,20.7C15,20.7 15.4,22 17,22C18.3,22 22,22 22,22L16.4,5Z",
"name": "google-adwords"
},
{
"path": "M19,3H13V8L17,7L16,11H21V5C21,3.89 20.1,3 19,3M17,17L13,16V21H19A2,2 0 0,0 21,19V13H16M8,13H3V19A2,2 0 0,0 5,21H11V16L7,17M3,5V11H8L7,7L11,8V3H5C3.89,3 3,3.89 3,5Z",
"name": "google-pages"
},
{
"path": "M12,1.5A9,9 0 0,1 21,10.5C21,13.11 19.89,15.47 18.11,17.11L17.05,16.05C18.55,14.68 19.5,12.7 19.5,10.5A7.5,7.5 0 0,0 12,3A7.5,7.5 0 0,0 4.5,10.5C4.5,12.7 5.45,14.68 6.95,16.05L5.89,17.11C4.11,15.47 3,13.11 3,10.5A9,9 0 0,1 12,1.5M12,4.5A6,6 0 0,1 18,10.5C18,12.28 17.22,13.89 16,15L14.92,13.92C15.89,13.1 16.5,11.87 16.5,10.5C16.5,8 14.5,6 12,6C9.5,6 7.5,8 7.5,10.5C7.5,11.87 8.11,13.1 9.08,13.92L8,15C6.78,13.89 6,12.28 6,10.5A6,6 0 0,1 12,4.5M8.11,17.65L11.29,14.46C11.68,14.07 12.32,14.07 12.71,14.46L15.89,17.65C16.28,18.04 16.28,18.67 15.89,19.06L12.71,22.24C12.32,22.63 11.68,22.63 11.29,22.24L8.11,19.06C7.72,18.67 7.72,18.04 8.11,17.65Z",
"name": "google-physical-web"
},
{
"path": "M5,3H19A2,2 0 0,1 21,5V19A2,2 0 0,1 19,21H5A2,2 0 0,1 3,19V5A2,2 0 0,1 5,3M19.5,12H18V10.5H17V12H15.5V13H17V14.5H18V13H19.5V12M9.65,11.36V12.9H12.22C12.09,13.54 11.45,14.83 9.65,14.83C8.11,14.83 6.89,13.54 6.89,12C6.89,10.46 8.11,9.17 9.65,9.17C10.55,9.17 11.13,9.56 11.45,9.88L12.67,8.72C11.9,7.95 10.87,7.5 9.65,7.5C7.14,7.5 5.15,9.5 5.15,12C5.15,14.5 7.14,16.5 9.65,16.5C12.22,16.5 13.96,14.7 13.96,12.13C13.96,11.81 13.96,11.61 13.89,11.36H9.65Z",
"name": "google-plus-box"
},
{
"path": "M14,20.95H20V10.78L8,7.34V3.05H4V20.95H10V15.31H14V20.95Z",
"name": "houzz"
},
{
"path": "M5,3H19A2,2 0 0,1 21,5V19A2,2 0 0,1 19,21H5A2,2 0 0,1 3,19V5A2,2 0 0,1 5,3M13.5,18.71H18V11.09L9,8.5V5.29H6V18.71H10.5V14.5H13.5V18.71Z",
"name": "houzz-box"
},
{
"path": "M10,5A1,1 0 0,0 9,4H8V2H16V4H15A1,1 0 0,0 14,5V19A1,1 0 0,0 15,20H16V22H8V20H9A1,1 0 0,0 10,19V5Z",
"name": "instapaper"
},
{
"path": "M7.85,17.07C7.03,17.17 3.5,17.67 4.06,20.26C4.69,23.3 9.87,22.59 9.83,19C9.81,16.57 9.83,9.2 9.83,9.2C9.83,9.2 9.76,8.53 10.43,8.39L18.19,6.79C18.19,6.79 18.83,6.65 18.83,7.29C18.83,7.89 18.83,14.2 18.83,14.2C18.83,14.2 18.9,14.83 18.12,15C17.34,15.12 13.91,15.4 14.19,18C14.5,21.07 20,20.65 20,17.07V2.61C20,2.61 20.04,1.62 18.9,1.87L9.5,3.78C9.5,3.78 8.66,3.96 8.66,4.77C8.66,5.5 8.66,16.11 8.66,16.11C8.66,16.11 8.66,16.96 7.85,17.07Z",
"name": "itunes"
},
{
"path": "M2,5.69C8.92,1.07 11.1,7 11.28,10.27C11.46,13.53 8.29,17.64 4.31,14.92V20.3L2,18.77V5.69M4.22,7.4V12.78C7.84,14.95 9.08,13.17 9.08,10.09C9.08,5.74 6.57,5.59 4.22,7.4M15.08,4.15C15.08,4.15 14.9,7.64 15.08,11.07C15.44,14.5 19.69,11.84 19.69,11.84V4.92L22,5.2V14.44C22,20.6 15.85,20.3 15.85,20.3L15.08,18C20.46,18 19.78,14.43 19.78,14.43C13.27,16.97 12.77,12.61 12.77,12.61V5.69L15.08,4.15Z",
"name": "language-python-text"
},
{
"path": "M18,17.93C15.92,17.92 14.81,16.9 14.04,15.09L13.82,14.6L11.92,10.23C11.29,8.69 9.72,7.64 7.96,7.64C5.57,7.64 3.63,9.59 3.63,12C3.63,14.41 5.57,16.36 7.96,16.36C9.62,16.36 11.08,15.41 11.8,14L12.57,15.81C11.5,17.15 9.82,18 7.96,18C4.67,18 2,15.32 2,12C2,8.69 4.67,6 7.96,6C10.44,6 12.45,7.34 13.47,9.7C13.54,9.89 14.54,12.24 15.42,14.24C15.96,15.5 16.42,16.31 17.91,16.36C19.38,16.41 20.39,15.5 20.39,14.37C20.39,13.26 19.62,13 18.32,12.56C16,11.79 14.79,11 14.79,9.15C14.79,7.33 16,6.12 18,6.12C19.31,6.12 20.24,6.7 20.89,7.86L19.62,8.5C19.14,7.84 18.61,7.57 17.94,7.57C17,7.57 16.33,8.23 16.33,9.1C16.33,10.34 17.43,10.53 18.97,11.03C21.04,11.71 22,12.5 22,14.42C22,16.45 20.27,17.93 18,17.93Z",
"name": "lastfm"
},
{
"path": "M19,3A2,2 0 0,1 21,5V19A2,2 0 0,1 19,21H5A2,2 0 0,1 3,19V5A2,2 0 0,1 5,3H19M18.5,18.5V13.2A3.26,3.26 0 0,0 15.24,9.94C14.39,9.94 13.4,10.46 12.92,11.24V10.13H10.13V18.5H12.92V13.57C12.92,12.8 13.54,12.17 14.31,12.17A1.4,1.4 0 0,1 15.71,13.57V18.5H18.5M6.88,8.56A1.68,1.68 0 0,0 8.56,6.88C8.56,5.95 7.81,5.19 6.88,5.19A1.69,1.69 0 0,0 5.19,6.88C5.19,7.81 5.95,8.56 6.88,8.56M8.27,18.5V10.13H5.5V18.5H8.27Z",
"name": "linkedin-box"
},
{
"path": "M9.56,12.5C9.56,12.6 9.5,12.72 9.4,12.79C9.2,12.97 8.89,12.94 8.71,12.74C8.63,12.65 8.59,12.53 8.59,12.41V8.5H5.66V13.39A2.44,2.44 0 0,0 8.1,15.83C8.68,15.83 9.24,15.62 9.68,15.24C9.64,15.6 9.43,15.93 9.11,16.11C8.75,16.31 8.35,16.42 7.94,16.41C7.46,16.41 7,16.3 6.56,16.09L6.39,16V18.6C7.04,18.86 7.74,19 8.44,19C9.47,19 10.46,18.66 11.25,18C12.08,17.25 12.54,16.18 12.5,15.06V8.5H9.56V12.5M4.93,13.39V5.59H2V12.9C1.84,14.35 2.88,15.65 4.33,15.81C4.41,15.82 4.5,15.83 4.56,15.83V15.83C4.93,15.83 5.29,15.74 5.63,15.59L5.75,15.5L5.65,15.41C5.17,14.85 4.91,14.13 4.93,13.39M22,11.39V8.5H21C20.59,6.38 18.53,5 16.41,5.41C16.17,5.45 15.94,5.5 15.71,5.61C14.28,6.24 13.33,7.62 13.26,9.18V15.83H13.39C14.95,15.76 16.19,14.47 16.19,12.9H17.41V10H16.15V9.17C16.15,8.86 16.32,8.57 16.59,8.41C17.06,8.13 17.68,8.28 17.96,8.76C18.05,8.91 18.09,9.07 18.1,9.24V11.93C18.07,14.05 19.75,15.79 21.87,15.83H22V12.9H22A1,1 0 0,1 21,11.9V11.41L22,11.39Z",
"name": "lyft"
},
{
"path": "M15.45,11.91C15.34,9.7 13.7,8.37 11.72,8.37H11.64C9.35,8.37 8.09,10.17 8.09,12.21C8.09,14.5 9.62,15.95 11.63,15.95C13.88,15.95 15.35,14.3 15.46,12.36M11.65,6.39C13.18,6.39 14.62,7.07 15.67,8.13V8.13C15.67,7.62 16,7.24 16.5,7.24H16.61C17.35,7.24 17.5,7.94 17.5,8.16V16.06C17.46,16.58 18.04,16.84 18.37,16.5C19.64,15.21 21.15,9.81 17.58,6.69C14.25,3.77 9.78,4.25 7.4,5.89C4.88,7.63 3.26,11.5 4.83,15.11C6.54,19.06 11.44,20.24 14.35,19.06C15.83,18.47 16.5,20.46 15,21.11C12.66,22.1 6.23,22 3.22,16.79C1.19,13.27 1.29,7.08 6.68,3.87C10.81,1.42 16.24,2.1 19.5,5.5C22.95,9.1 22.75,15.8 19.4,18.41C17.89,19.59 15.64,18.44 15.66,16.71L15.64,16.15C14.59,17.2 13.18,17.81 11.65,17.81C8.63,17.81 6,15.15 6,12.13C6,9.08 8.63,6.39 11.65,6.39Z",
"name": "mail-ru"
},
{
"path": "M20.93,14C20.66,15.4 18.5,16.95 15.97,17.25C14.66,17.4 13.38,17.55 12,17.5C9.76,17.38 8,16.95 8,16.95L8.03,17.57C8.32,19.78 10.22,19.92 12.03,20C13.85,20.04 15.47,19.53 15.47,19.53L15.55,21.17C15.55,21.17 14.27,21.86 12,22C10.75,22.05 9.2,21.95 7.39,21.47C3.47,20.43 2.79,16.25 2.69,12L2.68,8.57C2.68,4.23 5.5,2.96 5.5,2.96C6.95,2.3 9.41,2 11.97,2H12.03C14.59,2 17.05,2.3 18.5,2.96C18.5,2.96 21.33,4.23 21.33,8.57C21.33,8.57 21.36,11.77 20.93,14M8.33,10.32C8.33,9.54 7.7,8.91 6.93,8.91C6.15,8.91 5.5,9.54 5.5,10.32C5.5,11.09 6.15,11.72 6.93,11.72A1.4,1.4 0 0,0 8.33,10.32M13.41,10.32A1.41,1.41 0 0,0 12,8.91A1.41,1.41 0 0,0 10.59,10.32C10.59,11.09 11.22,11.72 12,11.72C12.78,11.72 13.41,11.09 13.41,10.32M18.5,10.32C18.5,9.54 17.85,8.91 17.07,8.91C16.3,8.91 15.67,9.54 15.67,10.32A1.4,1.4 0 0,0 17.07,11.72C17.85,11.72 18.5,11.09 18.5,10.32Z",
"name": "mastodon-variant"
},
{
"path": "M4.37,7.3C4.4,7.05 4.3,6.81 4.12,6.65L2.25,4.4V4.06H8.05L12.53,13.89L16.47,4.06H22V4.4L20.4,5.93C20.27,6.03 20.2,6.21 20.23,6.38V17.62C20.2,17.79 20.27,17.97 20.4,18.07L21.96,19.6V19.94H14.12V19.6L15.73,18.03C15.89,17.88 15.89,17.83 15.89,17.59V8.5L11.4,19.9H10.8L5.57,8.5V16.14C5.5,16.46 5.63,16.78 5.86,17L7.96,19.57V19.9H2V19.57L4.1,17C4.33,16.78 4.43,16.46 4.37,16.14V7.3Z",
"name": "medium"
},
{
"path": "M19.61,14.86C19.61,16.68 18.3,18.25 16.5,18.55C16.29,18.59 16.07,18.62 15.84,18.61C15.76,18.61 15.73,18.64 15.71,18.71C15.35,19.74 14.64,20.35 13.57,20.5C12.86,20.6 12.22,20.41 11.65,19.97C11.57,19.9 11.5,19.9 11.44,19.96C10.78,20.43 10.04,20.64 9.23,20.59C7.66,20.5 6.33,19.29 6.08,17.74C6.06,17.63 6.04,17.5 6.04,17.41C6.04,17.32 6,17.29 5.92,17.27C5.44,17.18 5,17 4.63,16.68C3.92,16.13 3.5,15.41 3.4,14.5C3.29,13.5 3.61,12.62 4.32,11.89C4.38,11.83 4.38,11.79 4.34,11.72C4.07,11.24 3.94,10.72 3.96,10.17C4,8.79 4.97,7.65 6.31,7.37C6.46,7.33 6.54,7.27 6.61,7.13C7.27,5.71 8.37,4.85 9.91,4.56C11,4.36 12,4.58 12.94,5.13C13,5.18 13.08,5.18 13.17,5.16C14.67,4.72 16,5.04 17.12,6.11C17.78,6.74 18.15,7.54 18.26,8.46C18.28,8.66 18.29,8.86 18.28,9.06C18.27,9.14 18.29,9.17 18.37,9.19C19.04,9.44 19.5,9.91 19.71,10.6C19.96,11.45 19.75,12.21 19.11,12.83C19.05,12.89 19.07,12.92 19.1,12.97C19.44,13.56 19.61,14.18 19.61,14.86M12.93,14.57C12.93,15.34 13.43,16 14.14,16.26C14.5,16.37 14.85,16.43 15.22,16.45C15.5,16.46 15.75,16.44 16,16.32C16.19,16.22 16.28,16.06 16.27,15.85C16.26,15.64 16.16,15.5 15.96,15.4C15.89,15.37 15.82,15.34 15.74,15.33C15.5,15.29 15.3,15.26 15.07,15.21C14.71,15.14 14.55,14.95 14.55,14.57C14.54,14.24 14.63,13.93 14.73,13.63C14.92,13.07 15.17,12.53 15.41,12C15.64,11.47 15.88,10.95 16.04,10.4C16.13,10.1 16.18,9.8 16.09,9.5C15.97,9 15.69,8.7 15.2,8.61C14.75,8.5 14.3,8.5 13.9,8.78C13.76,8.87 13.63,8.85 13.5,8.74C13.43,8.67 13.34,8.58 13.26,8.5C12.84,8.12 12.3,8.1 11.85,8.45C11.67,8.59 11.5,8.76 11.33,8.89C11.16,9 11,9.03 10.79,8.92C10.6,8.83 10.42,8.74 10.23,8.65C10.03,8.57 9.85,8.46 9.63,8.44C8.95,8.38 8.24,8.79 7.94,9.41C7.8,9.68 7.69,9.96 7.59,10.25C7.11,11.57 6.72,12.91 6.32,14.26C6.14,14.86 6.35,15.45 6.86,15.77C7.26,16 7.69,16.09 8.14,15.95C8.5,15.84 8.71,15.55 8.85,15.22C9.31,14.13 9.73,13 10.17,11.91C10.29,11.61 10.41,11.3 10.54,11C10.67,10.7 11.04,10.6 11.26,10.8C11.4,10.92 11.44,11.09 11.42,11.26C11.41,11.45 11.34,11.62 11.27,11.79C11,12.5 10.69,13.24 10.4,13.97C10.34,14.11 10.28,14.26 10.25,14.42C10.21,14.69 10.31,14.93 10.54,15C10.76,15.12 11,15.14 11.23,15.05C11.5,14.95 11.67,14.74 11.79,14.5C12.22,13.65 12.65,12.8 13.08,11.95C13.28,11.56 13.5,11.17 13.68,10.78C13.76,10.64 13.85,10.5 14,10.41C14.12,10.33 14.25,10.33 14.38,10.4C14.5,10.47 14.5,10.6 14.5,10.73C14.5,10.8 14.5,10.87 14.47,10.93C14.41,11.07 14.36,11.2 14.3,11.33C13.94,12.09 13.57,12.84 13.22,13.59C13.07,13.91 12.91,14.23 12.93,14.57M17.96,20.12C17.96,19.62 17.54,19.2 17.04,19.2C16.5,19.2 16.1,19.61 16.1,20.12A0.93,0.93 0 0,0 17.03,21.05A0.93,0.93 0 0,0 17.96,20.12M2.38,12.46C2.86,12.46 3.27,12.05 3.27,11.57C3.27,11.09 2.87,10.69 2.39,10.69C1.89,10.69 1.5,11.08 1.5,11.57C1.5,12.06 1.89,12.46 2.38,12.46M13.26,2.55C12.77,2.55 12.37,2.94 12.37,3.42C12.37,3.91 12.77,4.3 13.25,4.3C13.74,4.3 14.13,3.92 14.13,3.43C14.13,2.95 13.74,2.55 13.26,2.55M20.45,8.03C20.45,7.63 20.11,7.29 19.71,7.29C19.3,7.28 18.95,7.63 18.95,8.04C18.95,8.45 19.28,8.78 19.7,8.78C20.12,8.78 20.46,8.45 20.45,8.03M5.04,5.89C5.04,6.27 5.34,6.56 5.71,6.56C6.09,6.56 6.39,6.26 6.38,5.88C6.38,5.5 6.09,5.22 5.72,5.22C5.33,5.22 5.04,5.5 5.04,5.89M12.06,21.44C12.06,21.12 11.81,20.86 11.5,20.86C11.16,20.86 10.91,21.11 10.91,21.44C10.91,21.75 11.16,22 11.5,22C11.8,22 12.06,21.75 12.06,21.44M21,12.5C20.71,12.5 20.45,12.78 20.45,13.08A0.55,0.55 0 0,0 21,13.63C21.33,13.63 21.57,13.4 21.57,13.08C21.57,12.77 21.33,12.5 21,12.5M7.62,2C7.35,2 7.14,2.2 7.14,2.47C7.14,2.73 7.35,2.94 7.62,2.94A0.47,0.47 0 0,0 8.09,2.47C8.09,2.2 7.89,2 7.62,2M22.08,10C21.86,10 21.67,10.17 21.66,10.4C21.66,10.63 21.85,10.82 22.08,10.82C22.32,10.82 22.5,10.64 22.5,10.41C22.5,10.17 22.32,10 22.08,10M5.5,18.26C5.5,18.04 5.29,17.85 5.06,17.84C4.84,17.84 4.65,18.03 4.65,18.27C4.65,18.5 4.84,18.68 5.07,18.68C5.3,18.68 5.5,18.5 5.5,18.26Z",
"name": "meetup"
},
{
"path": "M21.11,18.5C20.97,18.5 20.83,18.44 20.71,18.36C20.37,18.13 20.28,17.68 20.5,17.34C21.18,16.34 21.54,15.16 21.54,13.93C21.54,12.71 21.18,11.53 20.5,10.5C20.28,10.18 20.37,9.73 20.71,9.5C21.04,9.28 21.5,9.37 21.72,9.7C22.56,10.95 23,12.41 23,13.93C23,15.45 22.56,16.91 21.72,18.16C21.58,18.37 21.35,18.5 21.11,18.5M19,17.29C18.88,17.29 18.74,17.25 18.61,17.17C18.28,16.94 18.19,16.5 18.42,16.15C18.86,15.5 19.1,14.73 19.1,13.93C19.1,13.14 18.86,12.37 18.42,11.71C18.19,11.37 18.28,10.92 18.61,10.69C18.95,10.47 19.4,10.55 19.63,10.89C20.24,11.79 20.56,12.84 20.56,13.93C20.56,15 20.24,16.07 19.63,16.97C19.5,17.18 19.25,17.29 19,17.29M14.9,15.73C15.89,15.73 16.7,14.92 16.7,13.93C16.7,13.17 16.22,12.5 15.55,12.25C15.5,12.55 15.43,12.85 15.34,13.14C15.23,13.44 14.95,13.64 14.64,13.64C14.57,13.64 14.5,13.62 14.41,13.6C14.03,13.47 13.82,13.06 13.95,12.67C14.09,12.24 14.17,11.78 14.17,11.32C14.17,8.93 12.22,7 9.82,7C8.1,7 6.56,8 5.87,9.5C6.54,9.7 7.16,10.04 7.66,10.54C7.95,10.83 7.95,11.29 7.66,11.58C7.38,11.86 6.91,11.86 6.63,11.58C6.17,11.12 5.56,10.86 4.9,10.86C3.56,10.86 2.46,11.96 2.46,13.3C2.46,14.64 3.56,15.73 4.9,15.73H14.9M15.6,10.75C17.06,11.07 18.17,12.37 18.17,13.93C18.17,15.73 16.7,17.19 14.9,17.19H4.9C2.75,17.19 1,15.45 1,13.3C1,11.34 2.45,9.73 4.33,9.45C5.12,7.12 7.33,5.5 9.82,5.5C12.83,5.5 15.31,7.82 15.6,10.75Z",
"name": "mixcloud"
},
{
"path": "M5.68,3.96L11.41,11.65C11.55,11.84 11.55,12.1 11.41,12.29L5.65,20L5.5,20.18C4.76,21 3.47,21.07 2.64,20.31C1.85,19.59 1.79,18.37 2.43,17.5L6.56,11.97L2.46,6.47C1.83,5.62 1.88,4.39 2.67,3.67L2.82,3.54C3.73,2.87 5,3.05 5.68,3.96M18.32,3.96C19,3.05 20.27,2.87 21.18,3.54L21.33,3.67C22.12,4.39 22.17,5.61 21.54,6.47L17.44,11.97L21.57,17.5C22.21,18.36 22.15,19.59 21.36,20.31C20.53,21.07 19.24,21 18.5,20.18L18.35,20L12.59,12.29C12.45,12.1 12.45,11.84 12.59,11.65L18.32,3.96Z",
"name": "mixer"
},
{
"path": "M3.25,4.03L19.95,20.73L18.7,22L14.86,18.13C14.77,18.12 14.68,18.09 14.59,18.05C14.26,17.89 14.14,17.62 14.11,17.38L12.18,15.45C12.14,15.53 12.09,15.6 12.05,15.66C11.62,16.26 11.19,16.26 10.86,16.04C10.54,15.83 5.5,12 5.23,11.87C4.95,11.76 4.85,12.03 5.12,13.5C5.39,15 4.95,15.39 4.57,15.45C4.2,15.5 3.06,15.18 3,12.14C2.95,9.11 3.76,8.62 4.14,8.62C4.6,8.62 7.08,10.69 8.84,12.12L2,5.28L3.25,4.03M18.38,16.56C18.75,15.4 19.12,13.8 19.1,12.03V12C19.14,8.5 17.66,5.58 17.66,5.58C17.66,5.58 17.42,4.72 18.12,4.39C18.83,4.06 19.3,4.61 19.3,4.61C21.12,8.22 21,11.64 21,12C21,12.27 21.09,14.96 19.88,18.05L18.38,16.56M15.14,13.31C15.19,12.92 15.22,12.5 15.24,12.03V12C15.14,8.5 14.13,7.21 14.13,7.21C14.13,7.21 13.89,6.34 14.59,6C15.3,5.69 15.77,6.23 15.77,6.23C17.26,8.94 17.16,11.64 17.14,12C17.15,12.2 17.2,13.38 16.82,15L15.14,13.31M10.2,8.38C10.23,7.77 10.59,7.64 10.59,7.64C10.59,7.64 11.19,7.37 11.57,7.8C11.91,8.19 12.72,9.57 12.89,11.07L10.2,8.38Z",
"name": "nfc-off"
},
{ "path": "M20,4H4V20H12V8H16V20H20V4", "name": "npm-variant" },
{
"path": "M3,3V21H21V3H3M6,6H18V18H15V9H12V18H6V6Z",
"name": "npm-variant-outline"
},
{
"path": "M8.32,21.97C8.21,21.92 8.08,21.76 8.06,21.65C8.03,21.5 8,21.76 8.66,17.56C9.26,13.76 9.25,13.82 9.33,13.71C9.46,13.54 9.44,13.54 10.94,13.53C12.26,13.5 12.54,13.5 13.13,13.41C16.38,12.96 18.39,11.05 19.09,7.75C19.13,7.53 19.17,7.34 19.18,7.34C19.18,7.33 19.25,7.38 19.33,7.44C20.36,8.22 20.71,9.66 20.32,11.58C19.86,13.87 18.64,15.39 16.74,16.04C15.93,16.32 15.25,16.43 14.05,16.46C13.25,16.5 13.23,16.5 13,16.65C12.83,16.82 12.84,16.79 12.45,19.2C12.18,20.9 12.08,21.45 12.04,21.55C11.97,21.71 11.83,21.85 11.67,21.93L11.56,22H10C8.71,22 8.38,22 8.32,21.97V21.97M3.82,19.74C3.63,19.64 3.5,19.47 3.5,19.27C3.5,19 6.11,2.68 6.18,2.5C6.27,2.32 6.5,2.13 6.68,2.06L6.83,2H10.36C14.27,2 14.12,2 15,2.2C17.62,2.75 18.82,4.5 18.37,7.13C17.87,10.06 16.39,11.8 13.87,12.43C13,12.64 12.39,12.7 10.73,12.7C9.42,12.7 9.32,12.71 9.06,12.85C8.8,13 8.59,13.27 8.5,13.6C8.46,13.67 8.23,15.07 7.97,16.7C7.71,18.33 7.5,19.69 7.5,19.72L7.47,19.78H5.69C4.11,19.78 3.89,19.78 3.82,19.74V19.74Z",
"name": "paypal"
},
{
"path": "M12,7A2,2 0 0,1 10,9A2,2 0 0,1 8,7C7.37,7.84 7,8.87 7,10A5,5 0 0,0 12,15A5,5 0 0,0 17,10A5,5 0 0,0 12,5C11.57,5 11.16,5.05 10.77,5.15C11.5,5.45 12,6.17 12,7M12,2A8,8 0 0,1 20,10C20,11.05 19.8,12.04 19.43,12.96C17.89,17.38 13.63,22 12,22C10.37,22 6.11,17.38 4.57,12.96C4.2,12.04 4,11.05 4,10A8,8 0 0,1 12,2Z",
"name": "periscope"
},
{
"path": "M5,3H19A2,2 0 0,1 21,5V19A2,2 0 0,1 19,21H9.29C9.69,20.33 10.19,19.38 10.39,18.64L11.05,16.34C11.36,16.95 12.28,17.45 13.22,17.45C16.17,17.45 18.22,14.78 18.22,11.45C18.22,8.28 15.64,5.89 12.3,5.89C8.14,5.89 5.97,8.67 5.97,11.72C5.97,13.14 6.69,14.89 7.91,15.45C8.08,15.56 8.19,15.5 8.19,15.34L8.47,14.28C8.5,14.14 8.5,14.06 8.41,14C7.97,13.45 7.69,12.61 7.69,11.78C7.69,9.64 9.3,7.61 12.03,7.61C14.42,7.61 16.08,9.19 16.08,11.5C16.08,14.11 14.75,15.95 13.03,15.95C12.05,15.95 11.39,15.11 11.55,14.17C11.83,13.03 12.39,11.83 12.39,11C12.39,10.22 12,9.61 11.16,9.61C10.22,9.61 9.39,10.61 9.39,11.95C9.39,12.83 9.66,13.39 9.66,13.39L8.55,18.17C8.39,19 8.47,20.25 8.55,21H5A2,2 0 0,1 3,19V5A2,2 0 0,1 5,3Z",
"name": "pinterest-box"
},
{
"path": "M21.9,4.26C21.64,3.55 20.96,3.07 20.2,3.07H20.19L18.46,3.07H3.81C3.07,3.07 2.39,3.54 2.12,4.24C2.04,4.45 2,4.66 2,4.88V10.92L2.07,12.12C2.36,14.85 3.78,17.23 5.97,18.9C6,18.93 6.05,18.96 6.09,19H6.11C7.29,19.86 8.6,20.44 10,20.73C10.68,20.86 11.35,20.93 12,20.93C12.63,20.93 13.25,20.87 13.85,20.76C13.93,20.75 14,20.73 14.07,20.72C14.09,20.71 14.11,20.7 14.14,20.69C15.5,20.4 16.76,19.83 17.89,19H17.91C17.95,18.96 18,18.93 18.03,18.9C20.22,17.23 21.64,14.85 21.93,12.12L22,10.92V4.88C22,4.68 21.97,4.47 21.9,4.26M17.67,10.55L12.96,15.06C12.7,15.32 12.35,15.44 12,15.44C11.67,15.44 11.33,15.32 11.06,15.06L6.36,10.55C5.81,10.03 5.79,9.16 6.32,8.61C6.84,8.06 7.71,8.05 8.26,8.57L12,12.17L15.77,8.57C16.31,8.05 17.18,8.07 17.71,8.61C18.23,9.16 18.21,10.03 17.67,10.55Z",
"name": "pocket"
},
{
"path": "M12,3A9,9 0 0,1 21,12C21,13.76 20.5,15.4 19.62,16.79L21,18.17V20A1,1 0 0,1 20,21H18.18L16.79,19.62C15.41,20.5 13.76,21 12,21A9,9 0 0,1 3,12A9,9 0 0,1 12,3M12,7A5,5 0 0,0 7,12A5,5 0 0,0 12,17C12.65,17 13.26,16.88 13.83,16.65L10.95,13.77C10.17,13 10.17,11.72 10.95,10.94C11.73,10.16 13,10.16 13.78,10.94L16.66,13.82C16.88,13.26 17,12.64 17,12A5,5 0 0,0 12,7Z",
"name": "quicktime"
},
{
"path": "M18.61,5.89C18.6,5.79 18.5,5.73 18.44,5.73C18.37,5.72 16.83,5.61 16.83,5.61C16.83,5.61 15.76,4.55 15.65,4.43C15.53,4.31 15.3,4.35 15.21,4.37C15.2,4.37 15,4.44 14.61,4.55C14.25,3.5 13.62,2.58 12.43,2.58C12.11,2.18 11.72,2 11.38,2C8.8,2 7.57,5.22 7.18,6.86C6.18,7.17 5.47,7.39 5.37,7.42C4.82,7.6 4.8,7.62 4.73,8.14C4.67,8.54 3.21,19.86 3.21,19.86L14.61,22L20.79,20.66C20.79,20.66 18.62,6 18.61,5.89M14,4.76C13.69,4.85 13.37,4.95 13,5.06C13,5 13,4.93 13,4.85C13,4.21 12.93,3.7 12.79,3.29C13.35,3.36 13.73,4 14,4.76M12.08,3.42C12.24,3.82 12.34,4.39 12.34,5.16C12.34,5.2 12.34,5.24 12.34,5.27C11.71,5.46 11.03,5.68 10.35,5.89C10.73,4.4 11.45,3.69 12.08,3.42M11.31,2.69C11.42,2.69 11.53,2.73 11.64,2.8C10.81,3.19 9.93,4.17 9.55,6.12C9,6.3 8.47,6.46 8,6.62C8.42,5.12 9.46,2.69 11.31,2.69M12.5,9.15L11.76,11.42C11.76,11.42 11.09,11.06 10.27,11.06C9.07,11.06 9,11.81 9,12C9,13.04 11.71,13.43 11.71,15.86C11.71,17.77 10.5,19 8.87,19C6.91,19 5.91,17.78 5.91,17.78L6.43,16.05C6.43,16.05 7.46,16.93 8.33,16.93C8.9,16.93 9.13,16.5 9.13,16.16C9.13,14.81 6.92,14.75 6.92,12.53C6.92,10.66 8.26,8.85 10.97,8.85C12,8.85 12.5,9.15 12.5,9.15M15.43,5.29L16.75,6.6L17.71,6.68C18.05,9 19.19,16.73 19.66,19.88L14.66,20.97L15.43,5.29Z",
"name": "shopify"
},
{
"path": "M7.47,17.19C7.37,17.95 7.08,18.21 6.19,18.21C5.27,18.21 4.87,17.79 4.81,16.87L4.61,13.65C4.61,12.91 4.87,12.55 5.86,12.55C7.21,12.55 7.04,13.54 7.64,14.5C8.33,15.62 10,16.35 12.31,16.35C15.17,16.35 17,15.14 17,13.57C17,12.23 15.89,11.39 13.85,11.16L11.5,10.89C7.21,10.42 5.1,9.19 5.1,6.62C5.1,4.07 8.06,2 12.21,2C13.5,2 14.81,2.29 16.29,2.76C16.29,2.26 16.58,2.1 17.3,2.1C18.46,2.1 18.55,2.39 18.62,3.08L18.85,5.88C18.85,6.5 18.39,6.83 17.63,6.83C16.35,6.83 16.55,5.88 15.86,5.07C15.17,4.26 13.79,3.73 12.08,3.73C9.44,3.73 7.7,4.89 7.7,6.5C7.7,7.8 8.92,8.56 11.38,8.82L13.95,9.08C17.7,9.5 19.61,10.92 19.61,13.33C19.61,16.17 16.71,18.08 12.21,18.08C10.56,18.08 9.08,17.77 7.47,17.19M1,16H2V21H23V22H1V16Z",
"name": "slackware"
},
{
"path": "M6,3H18A3,3 0 0,1 21,6V18A3,3 0 0,1 18,21H6A3,3 0 0,1 3,18V6A3,3 0 0,1 6,3M7,6A1,1 0 0,0 6,7V17A1,1 0 0,0 7,18H17A1,1 0 0,0 18,17V7A1,1 0 0,0 17,6H7M9.5,9H14.5A0.5,0.5 0 0,1 15,9.5V14.5A0.5,0.5 0 0,1 14.5,15H9.5A0.5,0.5 0 0,1 9,14.5V9.5A0.5,0.5 0 0,1 9.5,9Z",
"name": "square-inc"
},
{
"path": "M5.5,0H18.5A5.5,5.5 0 0,1 24,5.5V18.5A5.5,5.5 0 0,1 18.5,24H5.5A5.5,5.5 0 0,1 0,18.5V5.5A5.5,5.5 0 0,1 5.5,0M15.39,15.18C15.39,16.76 14.5,17.81 12.85,17.95V12.61C14.55,13.13 15.39,13.66 15.39,15.18M11.65,6V10.88C10.34,10.5 9.03,9.93 9.03,8.43C9.03,6.94 10.18,6.12 11.65,6M15.5,7.6L16.5,6.8C15.62,5.66 14.4,4.92 12.85,4.77V3.8H11.65V3.8L11.65,4.75C9.5,4.89 7.68,6.17 7.68,8.5C7.68,11 9.74,11.78 11.65,12.29V17.96C10.54,17.84 9.29,17.31 8.43,16.03L7.3,16.78C8.2,18.12 9.76,19 11.65,19.14V20.2H12.07L12.85,20.2V19.16C15.35,19 16.7,17.34 16.7,15.14C16.7,12.58 14.81,11.76 12.85,11.19V6.05C14,6.22 14.85,6.76 15.5,7.6Z",
"name": "square-inc-cash"
},
{
"path": "M19,3A2,2 0 0,1 21,5V19A2,2 0 0,1 19,21H5A2,2 0 0,1 3,19V15L6.19,16.31C6.45,17.6 7.6,18.58 8.97,18.58C10.53,18.58 11.8,17.31 11.8,15.75V15.62L15.2,13.19H15.28C17.36,13.19 19.05,11.5 19.05,9.42C19.05,7.34 17.36,5.65 15.28,5.65C13.2,5.65 11.5,7.34 11.5,9.42V9.47L9.13,12.93L8.97,12.92C8.38,12.92 7.83,13.1 7.38,13.41L3,11.6V5A2,2 0 0,1 5,3H19M8.28,17.17C9.08,17.5 10,17.13 10.33,16.33C10.66,15.53 10.28,14.62 9.5,14.29L8.22,13.76C8.71,13.58 9.26,13.57 9.78,13.79C10.31,14 10.72,14.41 10.93,14.94C11.15,15.46 11.15,16.04 10.93,16.56C10.5,17.64 9.23,18.16 8.15,17.71C7.65,17.5 7.27,17.12 7.06,16.67L8.28,17.17M17.8,9.42C17.8,10.81 16.67,11.94 15.28,11.94C13.9,11.94 12.77,10.81 12.77,9.42A2.51,2.51 0 0,1 15.28,6.91C16.67,6.91 17.8,8.04 17.8,9.42M13.4,9.42C13.4,10.46 14.24,11.31 15.29,11.31C16.33,11.31 17.17,10.46 17.17,9.42C17.17,8.38 16.33,7.53 15.29,7.53C14.24,7.53 13.4,8.38 13.4,9.42Z",
"name": "steam-box"
},
{
"path": "M14.92,17.16L16.75,13.53H19.45L14.94,22.5L10.37,13.53H13.07L14.92,17.16M10.63,8.66L8.18,13.55H4.55L10.61,1.5L16.74,13.55H13.11L10.63,8.66Z",
"name": "strava"
},
{
"path": "M12,14C11,14 9,15 9,16C9,18 12,18 12,18V17A1,1 0 0,1 11,16A1,1 0 0,1 12,15V14M12,19C12,19 8,18.5 8,16.5C8,13.5 11,12.75 12,12.75V11.5C11,11.5 7,13 7,16C7,20 12,20 12,20V19M10.07,7.03L11.26,7.56C11.69,5.12 12.84,3.5 12.84,3.5C12.41,4.53 12.13,5.38 11.95,6.05C13.16,3.55 15.61,2 15.61,2C14.43,3.18 13.56,4.46 12.97,5.53C14.55,3.85 16.74,2.75 16.74,2.75C14.05,4.47 12.84,7.2 12.54,7.96L13.09,8.04C13.09,8.56 13.09,9.04 13.34,9.42C14.1,11.31 18,11.47 18,16C18,20.53 13.97,22 11.83,22C9.69,22 5,21.03 5,16C5,10.97 9.95,10.93 10.83,8.92C10.95,8.54 10.07,7.03 10.07,7.03Z",
"name": "tor"
},
{
"path": "M17,11H13V15.5C13,16.44 13.28,17 14.5,17H17V21C17,21 15.54,21.05 14.17,21.05C10.8,21.05 9.5,19 9.5,16.75V11H7V7C10.07,6.74 10.27,4.5 10.5,3H13V7H17",
"name": "tumblr"
},
{
"path": "M16,11H13V14.9C13,15.63 13.14,16 14.1,16H16V19C16,19 14.97,19.1 13.9,19.1C11.25,19.1 10,17.5 10,15.7V11H8V8.2C10.41,8 10.62,6.16 10.8,5H13V8H16M20,2H4C2.89,2 2,2.89 2,4V20A2,2 0 0,0 4,22H20A2,2 0 0,0 22,20V4C22,2.89 21.1,2 20,2Z",
"name": "tumblr-box"
},
{
"path": "M3.75,17L8,12.75V16H18V11.5L20,9.5V16A2,2 0 0,1 18,18H8V21.25L3.75,17M20.25,7L16,11.25V8H6V12.5L4,14.5V8A2,2 0 0,1 6,6H16V2.75L20.25,7Z",
"name": "tumblr-reblog"
},
{
"path": "M5,3H19A2,2 0 0,1 21,5V19A2,2 0 0,1 19,21H5A2,2 0 0,1 3,19V5A2,2 0 0,1 5,3M17.71,9.33C18.19,8.93 18.75,8.45 19,7.92C18.59,8.13 18.1,8.26 17.56,8.33C18.06,7.97 18.47,7.5 18.68,6.86C18.16,7.14 17.63,7.38 16.97,7.5C15.42,5.63 11.71,7.15 12.37,9.95C9.76,9.79 8.17,8.61 6.85,7.16C6.1,8.38 6.75,10.23 7.64,10.74C7.18,10.71 6.83,10.57 6.5,10.41C6.54,11.95 7.39,12.69 8.58,13.09C8.22,13.16 7.82,13.18 7.44,13.12C7.81,14.19 8.58,14.86 9.9,15C9,15.76 7.34,16.29 6,16.08C7.15,16.81 8.46,17.39 10.28,17.31C14.69,17.11 17.64,13.95 17.71,9.33Z",
"name": "twitter-box"
},
{
"path": "M17.71,9.33C18.19,8.93 18.75,8.45 19,7.92C18.59,8.13 18.1,8.26 17.56,8.33C18.06,7.97 18.47,7.5 18.68,6.86C18.16,7.14 17.63,7.38 16.97,7.5C15.42,5.63 11.71,7.15 12.37,9.95C9.76,9.79 8.17,8.61 6.85,7.16C6.1,8.38 6.75,10.23 7.64,10.74C7.18,10.71 6.83,10.57 6.5,10.41C6.54,11.95 7.39,12.69 8.58,13.09C8.22,13.16 7.82,13.18 7.44,13.12C7.81,14.19 8.58,14.86 9.9,15C9,15.76 7.34,16.29 6,16.08C7.15,16.81 8.46,17.39 10.28,17.31C14.69,17.11 17.64,13.95 17.71,9.33M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2Z",
"name": "twitter-circle"
},
{
"path": "M6.38,13.24V13.24C6.38,11.84 6.38,10.44 6.38,9.04H7.4V15.84H6.39C6.39,15.63 6.39,15.42 6.39,15.21C5.93,15.68 5.29,15.96 4.58,15.96C3.12,15.96 2,14.9 2,13.3V9.04H3V13.24C3,14.33 3.74,15.04 4.7,15.04C5.64,15.04 6.38,14.31 6.38,13.24M9.14,9.04V11.5C9.37,11.29 9.65,11.1 9.95,10.97C10.25,10.85 10.58,10.78 10.91,10.78C12.37,10.78 13.5,11.94 13.5,13.37C13.5,14.8 12.37,15.96 10.91,15.96C10.58,15.96 10.25,15.89 9.95,15.77C9.64,15.64 9.37,15.45 9.13,15.22C9.13,15.43 9.13,15.63 9.13,15.84C8.81,15.84 8.5,15.84 8.16,15.84V9.04H9.14M12.55,13.37V13.37C12.55,12.41 11.77,11.65 10.84,11.65C9.89,11.65 9.13,12.41 9.13,13.37C9.13,14.32 9.88,15.09 10.84,15.09C11.77,15.09 12.55,14.32 12.55,13.37M16.46,10.79C17.9,10.79 18.95,11.89 18.95,13.36V13.69H14.91C15.04,14.5 15.71,15.09 16.55,15.09C17.13,15.09 17.61,14.86 18,14.36L18.7,14.89C18.2,15.55 17.46,15.95 16.55,15.95C15.06,15.95 13.91,14.84 13.91,13.36C13.91,11.97 15,10.79 16.46,10.79M14.92,12.91H17.95C17.79,12.15 17.18,11.65 16.44,11.65C15.71,11.65 15.1,12.15 14.92,12.91M20.5,13V15.84H19.5V10.89C19.82,10.89 20.14,10.89 20.47,10.89V11.5C20.71,11.1 21.11,10.85 21.66,10.85H22V11.76H21.59C20.95,11.76 20.5,12.26 20.5,13",
"name": "uber"
},
{
"path": "M19.5,3C20.14,4.08 20.44,5.19 20.44,6.6C20.44,11.08 16.61,16.91 13.5,21H6.41L3.56,4L9.77,3.39L11.28,15.5C12.69,13.21 14.42,9.61 14.42,7.16C14.42,5.81 14.19,4.9 13.83,4.15L19.5,3Z",
"name": "venmo"
},
{
"path": "M5,3A2,2 0 0,0 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V5A2,2 0 0,0 19,3H5M5.5,8.5H7C7.36,8.5 7.5,8.66 7.64,9.07C8.36,11.17 9.57,13 10.07,13C10.26,13 10.35,12.92 10.35,12.45V10.28C10.29,9.28 9.76,9.19 9.76,8.84C9.76,8.67 9.9,8.5 10.14,8.5H12.45C12.77,8.5 12.87,8.67 12.87,9.04V11.96C12.87,12.27 13,12.38 13.1,12.38C13.29,12.38 13.45,12.27 13.79,11.93C14.85,10.74 15.6,8.92 15.6,8.92C15.7,8.7 15.87,8.5 16.24,8.5H17.71C18.16,8.5 18.26,8.73 18.16,9.04C17.97,9.9 16.18,12.43 16.18,12.43C16,12.68 15.96,12.8 16.18,13.09C16.33,13.3 16.85,13.74 17.19,14.15C17.83,14.86 18.3,15.46 18.44,15.87C18.56,16.29 18.35,16.5 17.93,16.5H16.45C15.89,16.5 15.73,16.05 14.73,15.05C13.85,14.21 13.5,14.1 13.26,14.1C12.96,14.1 12.87,14.18 12.87,14.61V15.93C12.87,16.29 12.76,16.5 11.82,16.5C10.26,16.5 8.54,15.55 7.33,13.8C5.5,11.24 5,9.31 5,8.92C5,8.7 5.08,8.5 5.5,8.5Z",
"name": "vk-box"
},
{
"path": "M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M5.5,8.5H7C7.36,8.5 7.5,8.66 7.64,9.07C8.36,11.17 9.57,13 10.07,13C10.26,13 10.35,12.92 10.35,12.45V10.28C10.29,9.28 9.76,9.19 9.76,8.84C9.76,8.67 9.9,8.5 10.14,8.5H12.45C12.77,8.5 12.87,8.67 12.87,9.04V11.96C12.87,12.27 13,12.38 13.1,12.38C13.29,12.38 13.45,12.27 13.79,11.93C14.85,10.74 15.6,8.92 15.6,8.92C15.7,8.7 15.87,8.5 16.24,8.5H17.71C18.16,8.5 18.26,8.73 18.16,9.04C17.97,9.9 16.18,12.43 16.18,12.43C16,12.68 15.96,12.8 16.18,13.09C16.33,13.3 16.85,13.74 17.19,14.15C17.83,14.86 18.3,15.46 18.44,15.87C18.56,16.29 18.35,16.5 17.93,16.5H16.45C15.89,16.5 15.73,16.05 14.73,15.05C13.85,14.21 13.5,14.1 13.26,14.1C12.96,14.1 12.87,14.18 12.87,14.61V15.93C12.87,16.29 12.76,16.5 11.82,16.5C10.26,16.5 8.54,15.55 7.33,13.8C5.5,11.24 5,9.31 5,8.92C5,8.7 5.08,8.5 5.5,8.5Z",
"name": "vk-circle"
},
{
"path": "M17,17.5L12,15L7,17.5V5H5V19H19V5H17V17.5M12,12.42L14.25,13.77L13.65,11.22L15.64,9.5L13,9.27L12,6.86L11,9.27L8.36,9.5L10.35,11.22L9.75,13.77L12,12.42M5,3H19A2,2 0 0,1 21,5V19A2,2 0 0,1 19,21H5A2,2 0 0,1 3,19V5A2,2 0 0,1 5,3Z",
"name": "wunderlist"
},
{
"path": "M0 16.7L3.2 12.9L0 9.1L1.5 7.8L4.5 11.3L7.5 7.8L9 9.1L5.8 12.9L9 16.7L7.5 18L4.5 14.4L1.5 18L0 16.7M24 16.9C24 17.4 23.6 17.9 23 17.9H20C18.9 17.9 18 17 18 15.9V13.9C18 12.8 18.9 11.9 20 11.9H22V9.9H18V8H23C23.5 8 24 8.4 24 9M22 14H20V16H22V14M16 16.9C16 17.4 15.6 17.9 15 17.9H12C10.9 17.9 10 17 10 15.9V9.9C10 8.8 10.9 7.9 12 7.9H14V5H16V16.9M14 15.9V9.9H12V15.9H14Z",
"name": "xda"
},
{
"path": "M4.8,3C3.8,3 3,3.8 3,4.8V19.2C3,20.2 3.8,21 4.8,21H19.2C20.2,21 21,20.2 21,19.2V4.8C21,3.8 20.2,3 19.2,3M16.07,5H18.11C18.23,5 18.33,5.04 18.37,5.13C18.43,5.22 18.43,5.33 18.37,5.44L13.9,13.36L16.75,18.56C16.81,18.67 16.81,18.78 16.75,18.87C16.7,18.95 16.61,19 16.5,19H14.47C14.16,19 14,18.79 13.91,18.61L11.04,13.35C11.18,13.1 15.53,5.39 15.53,5.39C15.64,5.19 15.77,5 16.07,5M7.09,7.76H9.1C9.41,7.76 9.57,7.96 9.67,8.15L11.06,10.57C10.97,10.71 8.88,14.42 8.88,14.42C8.77,14.61 8.63,14.81 8.32,14.81H6.3C6.18,14.81 6.09,14.76 6.04,14.67C6,14.59 6,14.47 6.04,14.36L8.18,10.57L6.82,8.2C6.77,8.09 6.75,8 6.81,7.89C6.86,7.81 6.96,7.76 7.09,7.76Z",
"name": "xing-box"
},
{
"path": "M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M15.85,6H17.74C17.86,6 17.94,6.04 18,6.12C18.04,6.2 18.04,6.3 18,6.41L13.84,13.76L16.5,18.59C16.53,18.69 16.53,18.8 16.5,18.88C16.43,18.96 16.35,19 16.24,19H14.36C14.07,19 13.93,18.81 13.84,18.64L11.17,13.76C11.31,13.5 15.35,6.36 15.35,6.36C15.45,6.18 15.57,6 15.85,6M7.5,8.57H9.39C9.67,8.57 9.81,8.75 9.9,8.92L11.19,11.17C11.12,11.3 9.17,14.75 9.17,14.75C9.07,14.92 8.94,15.11 8.66,15.11H6.78C6.67,15.11 6.59,15.06 6.54,15C6.5,14.9 6.5,14.8 6.54,14.69L8.53,11.17L7.27,9C7.21,8.87 7.2,8.77 7.25,8.69C7.3,8.61 7.39,8.57 7.5,8.57Z",
"name": "xing-circle"
},
{
"path": "M10.59,2C11.23,2 11.5,2.27 11.58,2.97L11.79,6.14L12.03,10.29C12.05,10.64 12,11 11.86,11.32C11.64,11.77 11.14,11.89 10.73,11.58C10.5,11.39 10.31,11.14 10.15,10.87L6.42,4.55C6.06,3.94 6.17,3.54 6.77,3.16C7.5,2.68 9.73,2 10.59,2M14.83,14.85L15.09,14.91L18.95,16.31C19.61,16.55 19.79,16.92 19.5,17.57C19.06,18.7 18.34,19.66 17.42,20.45C16.96,20.85 16.5,20.78 16.21,20.28L13.94,16.32C13.55,15.61 14.03,14.8 14.83,14.85M4.5,14C4.5,13.26 4.5,12.55 4.75,11.87C4.97,11.2 5.33,11 6,11.27L9.63,12.81C10.09,13 10.35,13.32 10.33,13.84C10.3,14.36 9.97,14.58 9.53,14.73L5.85,15.94C5.15,16.17 4.79,15.96 4.64,15.25C4.55,14.83 4.47,14.4 4.5,14M11.97,21C11.95,21.81 11.6,22.12 10.81,22C9.77,21.8 8.81,21.4 7.96,20.76C7.54,20.44 7.45,19.95 7.76,19.53L10.47,15.97C10.7,15.67 11.03,15.6 11.39,15.74C11.77,15.88 11.97,16.18 11.97,16.59V21M14.45,13.32C13.73,13.33 13.23,12.5 13.64,11.91C14.47,10.67 15.35,9.46 16.23,8.26C16.5,7.85 16.94,7.82 17.31,8.16C18.24,9 18.91,10 19.29,11.22C19.43,11.67 19.25,12.08 18.83,12.2L15.09,13.17L14.45,13.32Z",
"name": "yelp"
}
]

View File

@@ -37,21 +37,23 @@
<body> <body>
<%= renderTemplate('_js_base') %> <%= renderTemplate('_js_base') %>
<script> <script type="module" crossorigin="use-credentials">
import("<%= latestLauncherJS %>"); import "<%= latestLauncherJS %>";
window.latestJS = true;
</script> </script>
<script> <script nomodule>
if (!window.latestJS) { (function() {
<% if (useRollup) { %> // // Safari 10.1 supports type=module but ignores nomodule, so we add this check.
_ls("/static/js/s.min.js").onload = function() { if (!isS101) {
System.import("<%= es5LauncherJS %>"); <% if (useRollup) { %>
}; _ls("/static/js/s.min.js").onload = function() {
<% } else { %> System.import("<%= es5LauncherJS %>");
_ls("<%= es5LauncherJS %>"); };
<% } %> <% } else { %>
} _ls("<%= es5LauncherJS %>");
<% } %>
}
})();
</script> </script>
<hc-layout subtitle="FAQ"> <hc-layout subtitle="FAQ">
@@ -251,7 +253,7 @@ http:
<script> <script>
var _gaq = [["_setAccount", "UA-57927901-9"], ["_trackPageview"]]; var _gaq = [["_setAccount", "UA-57927901-9"], ["_trackPageview"]];
(function (d, t) { (function(d, t) {
var g = d.createElement(t), var g = d.createElement(t),
s = d.getElementsByTagName(t)[0]; s = d.getElementsByTagName(t)[0];
g.src = g.src =

View File

@@ -28,21 +28,23 @@
<hc-connect></hc-connect> <hc-connect></hc-connect>
<script> <script type="module" crossorigin="use-credentials">
import("<%= latestLauncherJS %>"); import "<%= latestLauncherJS %>";
window.latestJS = true;
</script> </script>
<script> <script nomodule>
if (!window.latestJS) { (function() {
<% if (useRollup) { %> // // Safari 10.1 supports type=module but ignores nomodule, so we add this check.
_ls("/static/js/s.min.js").onload = function() { if (!isS101) {
System.import("<%= es5LauncherJS %>"); <% if (useRollup) { %>
}; _ls("/static/js/s.min.js").onload = function() {
<% } else { %> System.import("<%= es5LauncherJS %>");
_ls("<%= es5LauncherJS %>"); };
<% } %> <% } else { %>
} _ls("<%= es5LauncherJS %>");
<% } %>
}
})();
</script> </script>
<script> <script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){

View File

@@ -1,3 +1,3 @@
import "../../../src/resources/ha-style"; import "~app/resources/ha-style";
import "../../../src/resources/roboto"; import "~app/resources/roboto";
import "./layout/hc-connect"; import "./layout/hc-connect";

View File

@@ -8,7 +8,6 @@ import {
html, html,
LitElement, LitElement,
property, property,
internalProperty,
TemplateResult, TemplateResult,
} from "lit-element"; } from "lit-element";
import { CastManager } from "../../../../src/cast/cast_manager"; import { CastManager } from "../../../../src/cast/cast_manager";
@@ -29,7 +28,7 @@ import {
getLovelaceCollection, getLovelaceCollection,
LovelaceConfig, LovelaceConfig,
} from "../../../../src/data/lovelace"; } from "../../../../src/data/lovelace";
import "../../../../src/layouts/hass-loading-screen"; import "../../../../src/layouts/loading-screen";
import { generateDefaultViewConfig } from "../../../../src/panels/lovelace/common/generate-lovelace-config"; import { generateDefaultViewConfig } from "../../../../src/panels/lovelace/common/generate-lovelace-config";
import "./hc-layout"; import "./hc-layout";
import "@material/mwc-button/mwc-button"; import "@material/mwc-button/mwc-button";
@@ -42,13 +41,13 @@ class HcCast extends LitElement {
@property() public castManager!: CastManager; @property() public castManager!: CastManager;
@internalProperty() private askWrite = false; @property() private askWrite = false;
@internalProperty() private lovelaceConfig?: LovelaceConfig | null; @property() private lovelaceConfig?: LovelaceConfig | null;
protected render(): TemplateResult { protected render(): TemplateResult {
if (this.lovelaceConfig === undefined) { if (this.lovelaceConfig === undefined) {
return html` <hass-loading-screen no-toolbar></hass-loading-screen>> `; return html` <loading-screen></loading-screen>> `;
} }
const error = const error =

View File

@@ -17,8 +17,8 @@ import {
customElement, customElement,
html, html,
LitElement, LitElement,
property,
TemplateResult, TemplateResult,
internalProperty,
} from "lit-element"; } from "lit-element";
import { CastManager, getCastManager } from "../../../../src/cast/cast_manager"; import { CastManager, getCastManager } from "../../../../src/cast/cast_manager";
import { castSendShowDemo } from "../../../../src/cast/receiver_messages"; import { castSendShowDemo } from "../../../../src/cast/receiver_messages";
@@ -27,7 +27,7 @@ import {
saveTokens, saveTokens,
} from "../../../../src/common/auth/token_storage"; } from "../../../../src/common/auth/token_storage";
import "../../../../src/components/ha-icon"; import "../../../../src/components/ha-icon";
import "../../../../src/layouts/hass-loading-screen"; import "../../../../src/layouts/loading-screen";
import { registerServiceWorker } from "../../../../src/util/register-service-worker"; import { registerServiceWorker } from "../../../../src/util/register-service-worker";
import "./hc-layout"; import "./hc-layout";
@@ -60,19 +60,19 @@ const INTRO = html`
@customElement("hc-connect") @customElement("hc-connect")
export class HcConnect extends LitElement { export class HcConnect extends LitElement {
@internalProperty() private loading = false; @property() private loading = false;
// If we had stored credentials but we cannot connect, // If we had stored credentials but we cannot connect,
// show a screen asking retry or logout. // show a screen asking retry or logout.
@internalProperty() private cannotConnect = false; @property() private cannotConnect = false;
@internalProperty() private error?: string | TemplateResult; @property() private error?: string | TemplateResult;
@internalProperty() private auth?: Auth; @property() private auth?: Auth;
@internalProperty() private connection?: Connection; @property() private connection?: Connection;
@internalProperty() private castManager?: CastManager | null; @property() private castManager?: CastManager | null;
private openDemo = false; private openDemo = false;
@@ -98,7 +98,7 @@ export class HcConnect extends LitElement {
} }
if (this.castManager === undefined || this.loading) { if (this.castManager === undefined || this.loading) {
return html` <hass-loading-screen no-toolbar></hass-loading-screen> `; return html` <loading-screen></loading-screen> `;
} }
if (this.castManager === null) { if (this.castManager === null) {

View File

@@ -1,7 +1,7 @@
/* eslint-disable no-undef */ /* eslint-disable no-undef */
import { CAST_NS } from "../../../src/cast/const"; import { CAST_NS } from "~app/cast/const";
import { HassMessage } from "../../../src/cast/receiver_messages"; import { HassMessage } from "~app/cast/receiver_messages";
import "../../../src/resources/custom-card-support"; import "~app/resources/custom-card-support";
import { castContext } from "./cast_context"; import { castContext } from "./cast_context";
import { HcMain } from "./layout/hc-main"; import { HcMain } from "./layout/hc-main";
import { ReceivedMessage } from "./types"; import { ReceivedMessage } from "./types";

View File

@@ -1,10 +1,4 @@
import { import { customElement, html, property, TemplateResult } from "lit-element";
customElement,
html,
property,
internalProperty,
TemplateResult,
} from "lit-element";
import { mockHistory } from "../../../../demo/src/stubs/history"; import { mockHistory } from "../../../../demo/src/stubs/history";
import { LovelaceConfig } from "../../../../src/data/lovelace"; import { LovelaceConfig } from "../../../../src/data/lovelace";
import { import {
@@ -19,9 +13,9 @@ import "./hc-lovelace";
@customElement("hc-demo") @customElement("hc-demo")
class HcDemo extends HassElement { class HcDemo extends HassElement {
@property({ attribute: false }) public lovelacePath!: string; @property() public lovelacePath!: string;
@internalProperty() private _lovelaceConfig?: LovelaceConfig; @property() private _lovelaceConfig?: LovelaceConfig;
protected render(): TemplateResult { protected render(): TemplateResult {
if (!this._lovelaceConfig) { if (!this._lovelaceConfig) {

View File

@@ -11,7 +11,7 @@ import { HomeAssistant } from "../../../../src/types";
@customElement("hc-launch-screen") @customElement("hc-launch-screen")
class HcLaunchScreen extends LitElement { class HcLaunchScreen extends LitElement {
@property({ attribute: false }) public hass?: HomeAssistant; @property() public hass?: HomeAssistant;
@property() public error?: string; @property() public error?: string;

View File

@@ -16,14 +16,12 @@ import "./hc-launch-screen";
@customElement("hc-lovelace") @customElement("hc-lovelace")
class HcLovelace extends LitElement { class HcLovelace extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant; @property() public hass!: HomeAssistant;
@property({ attribute: false }) public lovelaceConfig!: LovelaceConfig; @property() public lovelaceConfig!: LovelaceConfig;
@property() public viewPath?: string | number; @property() public viewPath?: string | number;
public urlPath?: string | null;
protected render(): TemplateResult { protected render(): TemplateResult {
const index = this._viewIndex; const index = this._viewIndex;
if (index === undefined) { if (index === undefined) {
@@ -37,7 +35,6 @@ class HcLovelace extends LitElement {
const lovelace: Lovelace = { const lovelace: Lovelace = {
config: this.lovelaceConfig, config: this.lovelaceConfig,
editMode: false, editMode: false,
urlPath: this.urlPath!,
enableFullEditMode: () => undefined, enableFullEditMode: () => undefined,
mode: "storage", mode: "storage",
language: "en", language: "en",

View File

@@ -3,12 +3,7 @@ import {
getAuth, getAuth,
UnsubscribeFunc, UnsubscribeFunc,
} from "home-assistant-js-websocket"; } from "home-assistant-js-websocket";
import { import { customElement, html, property, TemplateResult } from "lit-element";
customElement,
html,
internalProperty,
TemplateResult,
} from "lit-element";
import { CAST_NS } from "../../../../src/cast/const"; import { CAST_NS } from "../../../../src/cast/const";
import { import {
ConnectMessage, ConnectMessage,
@@ -36,13 +31,13 @@ let resourcesLoaded = false;
@customElement("hc-main") @customElement("hc-main")
export class HcMain extends HassElement { export class HcMain extends HassElement {
@internalProperty() private _showDemo = false; @property() private _showDemo = false;
@internalProperty() private _lovelaceConfig?: LovelaceConfig; @property() private _lovelaceConfig?: LovelaceConfig;
@internalProperty() private _lovelacePath: string | number | null = null; @property() private _lovelacePath: string | number | null = null;
@internalProperty() private _error?: string; @property() private _error?: string;
private _unsubLovelace?: UnsubscribeFunc; private _unsubLovelace?: UnsubscribeFunc;
@@ -87,7 +82,6 @@ export class HcMain extends HassElement {
.hass=${this.hass} .hass=${this.hass}
.lovelaceConfig=${this._lovelaceConfig} .lovelaceConfig=${this._lovelaceConfig}
.viewPath=${this._lovelacePath} .viewPath=${this._lovelacePath}
.urlPath=${this._urlPath}
@config-refresh=${this._generateLovelaceConfig} @config-refresh=${this._generateLovelaceConfig}
></hc-lovelace> ></hc-lovelace>
`; `;

View File

@@ -26,9 +26,9 @@ export const demoThemeJimpower = () => ({
"switch-checked-color": "var(--accent-color)", "switch-checked-color": "var(--accent-color)",
"paper-dialog-background-color": "#434954", "paper-dialog-background-color": "#434954",
"secondary-text-color": "#5294E2", "secondary-text-color": "#5294E2",
"error-color": "#E45E65", "google-red-500": "#E45E65",
"divider-color": "rgba(0, 0, 0, .12)", "divider-color": "rgba(0, 0, 0, .12)",
"success-color": "#39E949", "google-green-500": "#39E949",
"switch-unchecked-button-color": "var(--disabled-text-color)", "switch-unchecked-button-color": "var(--disabled-text-color)",
"label-badge-border-color": "green", "label-badge-border-color": "green",
"paper-listbox-color": "var(--primary-color)", "paper-listbox-color": "var(--primary-color)",

View File

@@ -27,9 +27,9 @@ export const demoThemeKernehed = () => ({
"switch-checked-color": "var(--accent-color)", "switch-checked-color": "var(--accent-color)",
"paper-dialog-background-color": "#292929", "paper-dialog-background-color": "#292929",
"secondary-text-color": "#b58e31", "secondary-text-color": "#b58e31",
"error-color": "#b58e31", "google-red-500": "#b58e31",
"divider-color": "rgba(0, 0, 0, .12)", "divider-color": "rgba(0, 0, 0, .12)",
"success-color": "#2980b9", "google-green-500": "#2980b9",
"switch-unchecked-button-color": "var(--disabled-text-color)", "switch-unchecked-button-color": "var(--disabled-text-color)",
"label-badge-border-color": "green", "label-badge-border-color": "green",
"paper-listbox-color": "#777777", "paper-listbox-color": "#777777",

View File

@@ -4,7 +4,7 @@ import {
customElement, customElement,
html, html,
LitElement, LitElement,
internalProperty, property,
TemplateResult, TemplateResult,
} from "lit-element"; } from "lit-element";
import { CastManager } from "../../../src/cast/cast_manager"; import { CastManager } from "../../../src/cast/cast_manager";
@@ -20,7 +20,7 @@ import { HomeAssistant } from "../../../src/types";
class CastDemoRow extends LitElement implements LovelaceRow { class CastDemoRow extends LitElement implements LovelaceRow {
public hass!: HomeAssistant; public hass!: HomeAssistant;
@internalProperty() private _castManager?: CastManager | null; @property() private _castManager?: CastManager | null;
public setConfig(_config: CastConfig): void { public setConfig(_config: CastConfig): void {
// No config possible. // No config possible.
@@ -52,6 +52,7 @@ class CastDemoRow extends LitElement implements LovelaceRow {
}); });
mgr.castContext.addEventListener( mgr.castContext.addEventListener(
// eslint-disable-next-line no-undef // eslint-disable-next-line no-undef
// @ts-ignore
cast.framework.CastContextEventType.SESSION_STATE_CHANGED, cast.framework.CastContextEventType.SESSION_STATE_CHANGED,
(ev) => { (ev) => {
// On Android, opening a new session always results in SESSION_RESUMED. // On Android, opening a new session always results in SESSION_RESUMED.

View File

@@ -1,16 +1,15 @@
import "@material/mwc-button"; import "@material/mwc-button";
import "@polymer/paper-spinner/paper-spinner-lite";
import { import {
css, css,
CSSResult, CSSResult,
html, html,
LitElement, LitElement,
property, property,
internalProperty,
TemplateResult, TemplateResult,
} from "lit-element"; } from "lit-element";
import { until } from "lit-html/directives/until"; import { until } from "lit-html/directives/until";
import "../../../src/components/ha-card"; import "../../../src/components/ha-card";
import "../../../src/components/ha-circular-progress";
import { LovelaceCardConfig } from "../../../src/data/lovelace"; import { LovelaceCardConfig } from "../../../src/data/lovelace";
import { MockHomeAssistant } from "../../../src/fake_data/provide_hass"; import { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
import { Lovelace, LovelaceCard } from "../../../src/panels/lovelace/types"; import { Lovelace, LovelaceCard } from "../../../src/panels/lovelace/types";
@@ -22,11 +21,11 @@ import {
} from "../configs/demo-configs"; } from "../configs/demo-configs";
export class HADemoCard extends LitElement implements LovelaceCard { export class HADemoCard extends LitElement implements LovelaceCard {
@property({ attribute: false }) public lovelace?: Lovelace; @property() public lovelace?: Lovelace;
@property({ attribute: false }) public hass!: MockHomeAssistant; @property() public hass!: MockHomeAssistant;
@internalProperty() private _switching?: boolean; @property() private _switching?: boolean;
private _hidden = localStorage.hide_demo_card; private _hidden = localStorage.hide_demo_card;
@@ -50,7 +49,7 @@ export class HADemoCard extends LitElement implements LovelaceCard {
<div class="picker"> <div class="picker">
<div class="label"> <div class="label">
${this._switching ${this._switching
? html`<ha-circular-progress active></ha-circular-progress>` ? html` <paper-spinner-lite active></paper-spinner-lite> `
: until( : until(
selectedDemoConfig.then( selectedDemoConfig.then(
(conf) => html` (conf) => html`

View File

@@ -86,26 +86,26 @@
<%= renderTemplate('_js_base') %> <%= renderTemplate('_js_base') %>
<%= renderTemplate('_preload_roboto') %> <%= renderTemplate('_preload_roboto') %>
<script> <script type="module" src="<%= latestDemoJS %>"></script>
import("<%= latestDemoJS %>");
window.latestJS = true;
</script>
<script> <script nomodule>
if (!window.latestJS) { (function() {
<% if (useRollup) { %> // // Safari 10.1 supports type=module but ignores nomodule, so we add this check.
_ls("/static/js/s.min.js").onload = function() { if (!isS101) {
System.import("<%= es5DemoJS %>"); <% if (useRollup) { %>
}; _ls("/static/js/s.min.js").onload = function() {
<% } else { %> System.import("<%= es5DemoJS %>");
_ls("<%= es5DemoJS %>"); };
<% } %> <% } else { %>
} _ls("<%= es5DemoJS %>");
<% } %>
}
})();
</script> </script>
<script> <script>
var _gaq = [["_setAccount", "UA-57927901-5"], ["_trackPageview"]]; var _gaq = [["_setAccount", "UA-57927901-5"], ["_trackPageview"]];
(function (d, t) { (function(d, t) {
var g = d.createElement(t), var g = d.createElement(t),
s = d.getElementsByTagName(t)[0]; s = d.getElementsByTagName(t)[0];
g.src = g.src =

View File

@@ -2,8 +2,8 @@ import "@polymer/app-layout/app-toolbar/app-toolbar";
import { html } from "@polymer/polymer/lib/utils/html-tag"; import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */ /* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element"; import { PolymerElement } from "@polymer/polymer/polymer-element";
import "../../../src/components/ha-switch"; import "~app/components/ha-switch";
import "../../../src/components/ha-formfield"; import "~app/components/ha-formfield";
import "./demo-card"; import "./demo-card";
class DemoCards extends PolymerElement { class DemoCards extends PolymerElement {

View File

@@ -2,7 +2,7 @@ import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */ /* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element"; import { PolymerElement } from "@polymer/polymer/polymer-element";
import "../../../src/components/ha-card"; import "../../../src/components/ha-card";
import "../../../src/dialogs/more-info/more-info-content"; import "../../../src/dialogs/more-info/controls/more-info-content";
import "../../../src/state-summary/state-card-content"; import "../../../src/state-summary/state-card-content";
class DemoMoreInfo extends PolymerElement { class DemoMoreInfo extends PolymerElement {

View File

@@ -420,6 +420,15 @@ export default {
last_changed: "2018-07-19T10:44:46.105940+00:00", last_changed: "2018-07-19T10:44:46.105940+00:00",
last_updated: "2018-07-19T10:44:46.105940+00:00", last_updated: "2018-07-19T10:44:46.105940+00:00",
}, },
"weblink.router": {
entity_id: "weblink.router",
state: "http://192.168.1.1",
attributes: {
friendly_name: "Router",
},
last_changed: "2018-07-19T10:44:46.107286+00:00",
last_updated: "2018-07-19T10:44:46.107286+00:00",
},
"group.all_plants": { "group.all_plants": {
entity_id: "group.all_plants", entity_id: "group.all_plants",
state: "ok", state: "ok",
@@ -1081,6 +1090,18 @@ export default {
last_changed: "2018-07-19T10:44:46.510448+00:00", last_changed: "2018-07-19T10:44:46.510448+00:00",
last_updated: "2018-07-19T10:44:46.510448+00:00", last_updated: "2018-07-19T10:44:46.510448+00:00",
}, },
"history_graph.recent_switches": {
entity_id: "history_graph.recent_switches",
state: "unknown",
attributes: {
hours_to_show: 1,
refresh: 60,
entity_id: ["switch.ac", "switch.decorative_lights"],
friendly_name: "Recent Switches",
},
last_changed: "2018-07-19T10:44:46.512351+00:00",
last_updated: "2018-07-19T10:44:46.512351+00:00",
},
"scene.switch_on_and_off": { "scene.switch_on_and_off": {
entity_id: "scene.switch_on_and_off", entity_id: "scene.switch_on_and_off",
state: "scening", state: "scening",

View File

@@ -3,7 +3,7 @@ import { html } from "@polymer/polymer/lib/utils/html-tag";
import { PolymerElement } from "@polymer/polymer/polymer-element"; import { PolymerElement } from "@polymer/polymer/polymer-element";
import "../../../src/components/ha-card"; import "../../../src/components/ha-card";
import { SUPPORT_BRIGHTNESS } from "../../../src/data/light"; import { SUPPORT_BRIGHTNESS } from "../../../src/data/light";
import "../../../src/dialogs/more-info/more-info-content"; import "../../../src/dialogs/more-info/controls/more-info-content";
import { getEntity } from "../../../src/fake_data/entity"; import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-more-infos"; import "../components/demo-more-infos";

View File

@@ -11,7 +11,9 @@ import { PolymerElement } from "@polymer/polymer/polymer-element";
import "../../src/components/ha-card"; import "../../src/components/ha-card";
import "../../src/managers/notification-manager"; import "../../src/managers/notification-manager";
import "../../src/styles/polymer-ha-style"; import "../../src/styles/polymer-ha-style";
import { DEMOS } from "../build/import-demos";
// eslint-disable-next-line no-undef
const DEMOS = require.context("./demos", true, /^(.*\.(ts$))[^.]*$/im);
const fixPath = (path) => path.substr(2, path.length - 5); const fixPath = (path) => path.substr(2, path.length - 5);
@@ -161,7 +163,7 @@ class HaGallery extends PolymerElement {
}, },
_demos: { _demos: {
type: Array, type: Array,
value: Object.keys(DEMOS), value: DEMOS.keys().map(fixPath),
}, },
_lovelaceDemos: { _lovelaceDemos: {
type: Array, type: Array,
@@ -208,7 +210,7 @@ class HaGallery extends PolymerElement {
while (root.lastChild) root.removeChild(root.lastChild); while (root.lastChild) root.removeChild(root.lastChild);
if (demo) { if (demo) {
DEMOS[demo](); DEMOS(`./${demo}.ts`);
const el = document.createElement(demo); const el = document.createElement(demo);
root.appendChild(el); root.appendChild(el);
} }

View File

@@ -21,7 +21,7 @@ import { filterAndSort } from "../components/hassio-filter-addons";
import { hassioStyle } from "../resources/hassio-style"; import { hassioStyle } from "../resources/hassio-style";
class HassioAddonRepositoryEl extends LitElement { class HassioAddonRepositoryEl extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant; @property() public hass!: HomeAssistant;
@property() public repo!: HassioAddonRepository; @property() public repo!: HassioAddonRepository;

View File

@@ -6,7 +6,6 @@ import {
CSSResult, CSSResult,
LitElement, LitElement,
property, property,
internalProperty,
PropertyValues, PropertyValues,
} from "lit-element"; } from "lit-element";
import { html, TemplateResult } from "lit-html"; import { html, TemplateResult } from "lit-html";
@@ -20,12 +19,11 @@ import {
reloadHassioAddons, reloadHassioAddons,
} from "../../../src/data/hassio/addon"; } from "../../../src/data/hassio/addon";
import "../../../src/layouts/hass-tabs-subpage"; import "../../../src/layouts/hass-tabs-subpage";
import "../../../src/layouts/hass-loading-screen"; import "../../../src/layouts/loading-screen";
import { HomeAssistant, Route } from "../../../src/types"; import { HomeAssistant, Route } from "../../../src/types";
import { showRepositoriesDialog } from "../dialogs/repositories/show-dialog-repositories"; import { showRepositoriesDialog } from "../dialogs/repositories/show-dialog-repositories";
import { supervisorTabs } from "../hassio-tabs"; import { supervisorTabs } from "../hassio-panel";
import "./hassio-addon-repository"; import "./hassio-addon-repository";
import { ActionDetail } from "@material/mwc-list/mwc-list-foundation";
const sortRepos = (a: HassioAddonRepository, b: HassioAddonRepository) => { const sortRepos = (a: HassioAddonRepository, b: HassioAddonRepository) => {
if (a.slug === "local") { if (a.slug === "local") {
@@ -54,7 +52,7 @@ class HassioAddonStore extends LitElement {
@property({ attribute: false }) private _repos?: HassioAddonRepository[]; @property({ attribute: false }) private _repos?: HassioAddonRepository[];
@internalProperty() private _filter?: string; @property() private _filter?: string;
public async refreshData() { public async refreshData() {
this._repos = undefined; this._repos = undefined;
@@ -98,23 +96,19 @@ class HassioAddonStore extends LitElement {
.tabs=${supervisorTabs} .tabs=${supervisorTabs}
> >
<span slot="header">Add-on store</span> <span slot="header">Add-on store</span>
<ha-button-menu <ha-button-menu corner="BOTTOM_START" slot="toolbar-icon">
corner="BOTTOM_START"
slot="toolbar-icon"
@action=${this._handleAction}
>
<mwc-icon-button slot="trigger" alt="menu"> <mwc-icon-button slot="trigger" alt="menu">
<ha-svg-icon path=${mdiDotsVertical}></ha-svg-icon> <ha-svg-icon path=${mdiDotsVertical}></ha-svg-icon>
</mwc-icon-button> </mwc-icon-button>
<mwc-list-item> <mwc-list-item @tap=${this._manageRepositories}>
Repositories Repositories
</mwc-list-item> </mwc-list-item>
<mwc-list-item> <mwc-list-item @tap=${this.refreshData}>
Reload Reload
</mwc-list-item> </mwc-list-item>
</ha-button-menu> </ha-button-menu>
${repos.length === 0 ${repos.length === 0
? html`<hass-loading-screen no-toolbar></hass-loading-screen>` ? html`<loading-screen></loading-screen>`
: html` : html`
<div class="search"> <div class="search">
<search-input <search-input
@@ -148,17 +142,6 @@ class HassioAddonStore extends LitElement {
this._loadData(); this._loadData();
} }
private _handleAction(ev: CustomEvent<ActionDetail>) {
switch (ev.detail.index) {
case 0:
this._manageRepositories();
break;
case 1:
this.refreshData();
break;
}
}
private apiCalled(ev) { private apiCalled(ev) {
if (ev.detail.success) { if (ev.detail.success) {
this._loadData(); this._loadData();

View File

@@ -9,7 +9,6 @@ import {
html, html,
LitElement, LitElement,
property, property,
internalProperty,
PropertyValues, PropertyValues,
TemplateResult, TemplateResult,
} from "lit-element"; } from "lit-element";
@@ -35,15 +34,15 @@ class HassioAddonAudio extends LitElement {
@property({ attribute: false }) public addon!: HassioAddonDetails; @property({ attribute: false }) public addon!: HassioAddonDetails;
@internalProperty() private _error?: string; @property() private _error?: string;
@internalProperty() private _inputDevices?: HassioHardwareAudioDevice[]; @property() private _inputDevices?: HassioHardwareAudioDevice[];
@internalProperty() private _outputDevices?: HassioHardwareAudioDevice[]; @property() private _outputDevices?: HassioHardwareAudioDevice[];
@internalProperty() private _selectedInput!: null | string; @property() private _selectedInput!: null | string;
@internalProperty() private _selectedOutput!: null | string; @property() private _selectedOutput!: null | string;
protected render(): TemplateResult { protected render(): TemplateResult {
return html` return html`
@@ -108,7 +107,7 @@ class HassioAddonAudio extends LitElement {
display: block; display: block;
} }
.errors { .errors {
color: var(--error-color); color: var(--google-red-500);
margin-bottom: 16px; margin-bottom: 16px;
} }
paper-item { paper-item {

View File

@@ -1,3 +1,4 @@
import "@polymer/paper-spinner/paper-spinner-lite";
import { import {
css, css,
CSSResult, CSSResult,
@@ -11,7 +12,6 @@ import { HassioAddonDetails } from "../../../../src/data/hassio/addon";
import { haStyle } from "../../../../src/resources/styles"; import { haStyle } from "../../../../src/resources/styles";
import { HomeAssistant } from "../../../../src/types"; import { HomeAssistant } from "../../../../src/types";
import { hassioStyle } from "../../resources/hassio-style"; import { hassioStyle } from "../../resources/hassio-style";
import "../../../../src/components/ha-circular-progress";
import "./hassio-addon-audio"; import "./hassio-addon-audio";
import "./hassio-addon-config"; import "./hassio-addon-config";
import "./hassio-addon-network"; import "./hassio-addon-network";
@@ -24,7 +24,7 @@ class HassioAddonConfigDashboard extends LitElement {
protected render(): TemplateResult { protected render(): TemplateResult {
if (!this.addon) { if (!this.addon) {
return html`<ha-circular-progress active></ha-circular-progress>`; return html` <paper-spinner-lite active></paper-spinner-lite> `;
} }
return html` return html`
<div class="content"> <div class="content">

View File

@@ -7,7 +7,6 @@ import {
html, html,
LitElement, LitElement,
property, property,
internalProperty,
PropertyValues, PropertyValues,
query, query,
TemplateResult, TemplateResult,
@@ -33,7 +32,7 @@ class HassioAddonConfig extends LitElement {
@property({ attribute: false }) public addon!: HassioAddonDetails; @property({ attribute: false }) public addon!: HassioAddonDetails;
@internalProperty() private _error?: string; @property() private _error?: string;
@property({ type: Boolean }) private _configHasChanged = false; @property({ type: Boolean }) private _configHasChanged = false;
@@ -85,7 +84,7 @@ class HassioAddonConfig extends LitElement {
justify-content: space-between; justify-content: space-between;
} }
.errors { .errors {
color: var(--error-color); color: var(--google-red-500);
margin-top: 16px; margin-top: 16px;
} }
iron-autogrow-textarea { iron-autogrow-textarea {
@@ -93,7 +92,7 @@ class HassioAddonConfig extends LitElement {
font-family: monospace; font-family: monospace;
} }
.syntaxerror { .syntaxerror {
color: var(--error-color); color: var(--google-red-500);
} }
`, `,
]; ];

View File

@@ -6,7 +6,6 @@ import {
html, html,
LitElement, LitElement,
property, property,
internalProperty,
PropertyValues, PropertyValues,
TemplateResult, TemplateResult,
} from "lit-element"; } from "lit-element";
@@ -38,9 +37,9 @@ class HassioAddonNetwork extends LitElement {
@property({ attribute: false }) public addon!: HassioAddonDetails; @property({ attribute: false }) public addon!: HassioAddonDetails;
@internalProperty() private _error?: string; @property() private _error?: string;
@internalProperty() private _config?: NetworkItem[]; @property() private _config?: NetworkItem[];
public connectedCallback(): void { public connectedCallback(): void {
super.connectedCallback(); super.connectedCallback();
@@ -72,7 +71,7 @@ class HassioAddonNetwork extends LitElement {
<paper-input <paper-input
@value-changed=${this._configChanged} @value-changed=${this._configChanged}
placeholder="disabled" placeholder="disabled"
.value=${item.host ? String(item.host) : ""} .value=${String(item.host)}
.container=${item.container} .container=${item.container}
no-label-float no-label-float
></paper-input> ></paper-input>
@@ -106,7 +105,7 @@ class HassioAddonNetwork extends LitElement {
display: block; display: block;
} }
.errors { .errors {
color: var(--error-color); color: var(--google-red-500);
margin-bottom: 16px; margin-bottom: 16px;
} }
.card-actions { .card-actions {

View File

@@ -1,3 +1,4 @@
import "@polymer/paper-spinner/paper-spinner-lite";
import { import {
css, css,
CSSResult, CSSResult,
@@ -5,7 +6,6 @@ import {
html, html,
LitElement, LitElement,
property, property,
internalProperty,
TemplateResult, TemplateResult,
} from "lit-element"; } from "lit-element";
import "../../../../src/components/ha-markdown"; import "../../../../src/components/ha-markdown";
@@ -13,8 +13,7 @@ import {
fetchHassioAddonDocumentation, fetchHassioAddonDocumentation,
HassioAddonDetails, HassioAddonDetails,
} from "../../../../src/data/hassio/addon"; } from "../../../../src/data/hassio/addon";
import "../../../../src/layouts/hass-loading-screen"; import "../../../../src/layouts/loading-screen";
import "../../../../src/components/ha-circular-progress";
import { haStyle } from "../../../../src/resources/styles"; import { haStyle } from "../../../../src/resources/styles";
import { HomeAssistant } from "../../../../src/types"; import { HomeAssistant } from "../../../../src/types";
import { hassioStyle } from "../../resources/hassio-style"; import { hassioStyle } from "../../resources/hassio-style";
@@ -25,9 +24,9 @@ class HassioAddonDocumentationDashboard extends LitElement {
@property({ attribute: false }) public addon?: HassioAddonDetails; @property({ attribute: false }) public addon?: HassioAddonDetails;
@internalProperty() private _error?: string; @property() private _error?: string;
@internalProperty() private _content?: string; @property() private _content?: string;
public async connectedCallback(): Promise<void> { public async connectedCallback(): Promise<void> {
super.connectedCallback(); super.connectedCallback();
@@ -36,7 +35,7 @@ class HassioAddonDocumentationDashboard extends LitElement {
protected render(): TemplateResult { protected render(): TemplateResult {
if (!this.addon) { if (!this.addon) {
return html`<ha-circular-progress active></ha-circular-progress>`; return html` <paper-spinner-lite active></paper-spinner-lite> `;
} }
return html` return html`
<div class="content"> <div class="content">
@@ -45,7 +44,7 @@ class HassioAddonDocumentationDashboard extends LitElement {
<div class="card-content"> <div class="card-content">
${this._content ${this._content
? html`<ha-markdown .content=${this._content}></ha-markdown>` ? html`<ha-markdown .content=${this._content}></ha-markdown>`
: html`<hass-loading-screen no-toolbar></hass-loading-screen>`} : html`<loading-screen></loading-screen>`}
</div> </div>
</ha-card> </ha-card>
</div> </div>

View File

@@ -4,6 +4,7 @@ import {
mdiInformationVariant, mdiInformationVariant,
mdiMathLog, mdiMathLog,
} from "@mdi/js"; } from "@mdi/js";
import "@polymer/paper-spinner/paper-spinner-lite";
import { import {
css, css,
CSSResult, CSSResult,
@@ -19,7 +20,6 @@ import {
HassioAddonDetails, HassioAddonDetails,
} from "../../../src/data/hassio/addon"; } from "../../../src/data/hassio/addon";
import "../../../src/layouts/hass-tabs-subpage"; import "../../../src/layouts/hass-tabs-subpage";
import "../../../src/components/ha-circular-progress";
import type { PageNavigation } from "../../../src/layouts/hass-tabs-subpage"; import type { PageNavigation } from "../../../src/layouts/hass-tabs-subpage";
import { haStyle } from "../../../src/resources/styles"; import { haStyle } from "../../../src/resources/styles";
import { HomeAssistant, Route } from "../../../src/types"; import { HomeAssistant, Route } from "../../../src/types";
@@ -56,7 +56,7 @@ class HassioAddonDashboard extends LitElement {
protected render(): TemplateResult { protected render(): TemplateResult {
if (!this.addon) { if (!this.addon) {
return html`<ha-circular-progress active></ha-circular-progress>`; return html` <paper-spinner-lite active></paper-spinner-lite> `;
} }
const addonTabs: PageNavigation[] = [ const addonTabs: PageNavigation[] = [

View File

@@ -1,3 +1,4 @@
import "@polymer/paper-spinner/paper-spinner-lite";
import { import {
css, css,
CSSResult, CSSResult,
@@ -8,7 +9,6 @@ import {
TemplateResult, TemplateResult,
} from "lit-element"; } from "lit-element";
import { HassioAddonDetails } from "../../../../src/data/hassio/addon"; import { HassioAddonDetails } from "../../../../src/data/hassio/addon";
import "../../../../src/components/ha-circular-progress";
import { haStyle } from "../../../../src/resources/styles"; import { haStyle } from "../../../../src/resources/styles";
import { HomeAssistant } from "../../../../src/types"; import { HomeAssistant } from "../../../../src/types";
import { hassioStyle } from "../../resources/hassio-style"; import { hassioStyle } from "../../resources/hassio-style";
@@ -24,7 +24,7 @@ class HassioAddonInfoDashboard extends LitElement {
protected render(): TemplateResult { protected render(): TemplateResult {
if (!this.addon) { if (!this.addon) {
return html`<ha-circular-progress active></ha-circular-progress>`; return html` <paper-spinner-lite active></paper-spinner-lite> `;
} }
return html` return html`

View File

@@ -9,6 +9,7 @@ import {
mdiExclamationThick, mdiExclamationThick,
mdiFlask, mdiFlask,
mdiHomeAssistant, mdiHomeAssistant,
mdiInformation,
mdiKey, mdiKey,
mdiNetwork, mdiNetwork,
mdiPound, mdiPound,
@@ -22,7 +23,6 @@ import {
html, html,
LitElement, LitElement,
property, property,
internalProperty,
TemplateResult, TemplateResult,
} from "lit-element"; } from "lit-element";
import { classMap } from "lit-html/directives/class-map"; import { classMap } from "lit-html/directives/class-map";
@@ -52,7 +52,6 @@ import { HomeAssistant } from "../../../../src/types";
import "../../components/hassio-card-content"; import "../../components/hassio-card-content";
import { showHassioMarkdownDialog } from "../../dialogs/markdown/show-dialog-hassio-markdown"; import { showHassioMarkdownDialog } from "../../dialogs/markdown/show-dialog-hassio-markdown";
import { hassioStyle } from "../../resources/hassio-style"; import { hassioStyle } from "../../resources/hassio-style";
import "../../../../src/components/ha-settings-row";
const STAGE_ICON = { const STAGE_ICON = {
stable: mdiCheckCircle, stable: mdiCheckCircle,
@@ -125,7 +124,7 @@ class HassioAddonInfo extends LitElement {
@property({ attribute: false }) public addon!: HassioAddonDetails; @property({ attribute: false }) public addon!: HassioAddonDetails;
@internalProperty() private _error?: string; @property() private _error?: string;
@property({ type: Boolean }) private _installing = false; @property({ type: Boolean }) private _installing = false;
@@ -242,23 +241,19 @@ class HassioAddonInfo extends LitElement {
` `
: ""} : ""}
<div class="security"> <div class="security">
${this.addon.stage !== "stable" <ha-label-badge
? html` <ha-label-badge class=${classMap({
class=${classMap({ green: this.addon.stage === "stable",
yellow: this.addon.stage === "experimental", yellow: this.addon.stage === "experimental",
red: this.addon.stage === "deprecated", red: this.addon.stage === "deprecated",
})} })}
@click=${this._showMoreInfo} @click=${this._showMoreInfo}
id="stage" id="stage"
label="stage" label="stage"
description="" description=""
> >
<ha-svg-icon <ha-svg-icon .path=${STAGE_ICON[this.addon.stage]}></ha-svg-icon>
.path=${STAGE_ICON[this.addon.stage]} </ha-label-badge>
></ha-svg-icon>
</ha-label-badge>`
: ""}
<ha-label-badge <ha-label-badge
class=${classMap({ class=${classMap({
green: [5, 6].includes(Number(this.addon.rating)), green: [5, 6].includes(Number(this.addon.rating)),
@@ -386,94 +381,67 @@ class HassioAddonInfo extends LitElement {
${this.addon.version ${this.addon.version
? html` ? html`
<div class="addon-options"> <div class="state">
<ha-settings-row ?three-line=${this.narrow}> <div>Start on boot</div>
<span slot="heading"> <ha-switch
Start on boot @change=${this._startOnBootToggled}
</span> .checked=${this.addon.boot === "auto"}
<span slot="description"> haptic
Make the add-on start during a system boot ></ha-switch>
</span>
<ha-switch
@change=${this._startOnBootToggled}
.checked=${this.addon.boot === "auto"}
haptic
></ha-switch>
</ha-settings-row>
${this.hass.userData?.showAdvanced
? html`
<ha-settings-row ?three-line=${this.narrow}>
<span slot="heading">
Watchdog
</span>
<span slot="description">
This will start the add-on if it crashes
</span>
<ha-switch
@change=${this._watchdogToggled}
.checked=${this.addon.watchdog}
haptic
></ha-switch>
</ha-settings-row>
`
: ""}
${this.addon.auto_update || this.hass.userData?.showAdvanced
? html`
<ha-settings-row ?three-line=${this.narrow}>
<span slot="heading">
Auto update
</span>
<span slot="description">
Auto update the add-on when there is a new version
available
</span>
<ha-switch
@change=${this._autoUpdateToggled}
.checked=${this.addon.auto_update}
haptic
></ha-switch>
</ha-settings-row>
`
: ""}
${this.addon.ingress
? html`
<ha-settings-row ?three-line=${this.narrow}>
<span slot="heading">
Show in sidebar
</span>
<span slot="description">
${this._computeCannotIngressSidebar
? "This option requires Home Assistant 0.92 or later."
: "Add this add-on to your sidebar"}
</span>
<ha-switch
@change=${this._panelToggled}
.checked=${this.addon.ingress_panel}
.disabled=${this._computeCannotIngressSidebar}
haptic
></ha-switch>
</ha-settings-row>
`
: ""}
${this._computeUsesProtectedOptions
? html`
<ha-settings-row ?three-line=${this.narrow}>
<span slot="heading">
Protection mode
</span>
<span slot="description">
Blocks elevated system access from the add-on
</span>
<ha-switch
@change=${this._protectionToggled}
.checked=${this.addon.protected}
haptic
></ha-switch>
</ha-settings-row>
`
: ""}
</div> </div>
${this.addon.auto_update || this.hass.userData?.showAdvanced
? html`
<div class="state">
<div>Auto update</div>
<ha-switch
@change=${this._autoUpdateToggled}
.checked=${this.addon.auto_update}
haptic
></ha-switch>
</div>
`
: ""}
${this.addon.ingress
? html`
<div class="state">
<div>Show in sidebar</div>
<ha-switch
@change=${this._panelToggled}
.checked=${this.addon.ingress_panel}
.disabled=${this._computeCannotIngressSidebar}
haptic
></ha-switch>
${this._computeCannotIngressSidebar
? html`
<span>
This option requires Home Assistant 0.92 or
later.
</span>
`
: ""}
</div>
`
: ""}
${this._computeUsesProtectedOptions
? html`
<div class="state">
<div>
Protection mode
<span>
<ha-svg-icon path=${mdiInformation}></ha-svg-icon>
<paper-tooltip>
Grant the add-on elevated system access.
</paper-tooltip>
</span>
</div>
<ha-switch
@change=${this._protectionToggled}
.checked=${this.addon.protected}
haptic
></ha-switch>
</div>
`
: ""}
` `
: ""} : ""}
${this._error ? html` <div class="errors">${this._error}</div> ` : ""} ${this._error ? html` <div class="errors">${this._error}</div> ` : ""}
@@ -579,6 +547,137 @@ class HassioAddonInfo extends LitElement {
`; `;
} }
static get styles(): CSSResult[] {
return [
haStyle,
hassioStyle,
css`
:host {
display: block;
}
ha-card {
display: block;
margin-bottom: 16px;
}
ha-card.warning {
background-color: var(--google-red-500);
color: white;
}
ha-card.warning .card-header {
color: white;
}
ha-card.warning .card-content {
color: white;
}
ha-card.warning mwc-button {
--mdc-theme-primary: white !important;
}
.warning {
color: var(--google-red-500);
--mdc-theme-primary: var(--google-red-500);
}
.light-color {
color: var(--secondary-text-color);
}
.addon-header {
padding-left: 8px;
font-size: 24px;
color: var(--ha-card-header-color, --primary-text-color);
}
.addon-version {
float: right;
font-size: 15px;
vertical-align: middle;
}
.errors {
color: var(--google-red-500);
margin-bottom: 16px;
}
.description {
margin-bottom: 16px;
}
img.logo {
max-height: 60px;
margin: 16px 0;
display: block;
}
.state {
display: flex;
margin: 33px 0;
}
.state div {
width: 180px;
display: inline-block;
}
.state ha-svg-icon {
width: 16px;
height: 16px;
color: var(--secondary-text-color);
}
ha-switch {
display: flex;
}
ha-svg-icon.running {
color: var(--paper-green-400);
}
ha-svg-icon.stopped {
color: var(--google-red-300);
}
ha-call-api-button {
font-weight: 500;
color: var(--primary-color);
}
.right {
float: right;
}
protection-enable mwc-button {
--mdc-theme-primary: white;
}
.description a {
color: var(--primary-color);
}
.red {
--ha-label-badge-color: var(--label-badge-red, #df4c1e);
}
.blue {
--ha-label-badge-color: var(--label-badge-blue, #039be5);
}
.green {
--ha-label-badge-color: var(--label-badge-green, #0da035);
}
.yellow {
--ha-label-badge-color: var(--label-badge-yellow, #f4b400);
}
.security {
margin-bottom: 16px;
}
.card-actions {
display: flow-root;
}
.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 {
display: contents;
}
.changelog-link {
color: var(--primary-color);
text-decoration: underline;
cursor: pointer;
}
ha-markdown {
padding: 16px;
}
`,
];
}
private get _computeHassioApi(): boolean { private get _computeHassioApi(): boolean {
return ( return (
this.addon.hassio_api && this.addon.hassio_api &&
@@ -667,24 +766,6 @@ class HassioAddonInfo extends LitElement {
} }
} }
private async _watchdogToggled(): Promise<void> {
this._error = undefined;
const data: HassioAddonSetOptionParams = {
watchdog: !this.addon.watchdog,
};
try {
await setHassioAddonOption(this.hass, this.addon.slug, data);
const eventdata = {
success: true,
response: undefined,
path: "option",
};
fireEvent(this, "hass-api-called", eventdata);
} catch (err) {
this._error = `Failed to set addon option, ${err.body?.message || err}`;
}
}
private async _autoUpdateToggled(): Promise<void> { private async _autoUpdateToggled(): Promise<void> {
this._error = undefined; this._error = undefined;
const data: HassioAddonSetOptionParams = { const data: HassioAddonSetOptionParams = {
@@ -801,146 +882,6 @@ class HassioAddonInfo extends LitElement {
this._error = `Failed to uninstall addon, ${err.body?.message || err}`; this._error = `Failed to uninstall addon, ${err.body?.message || err}`;
} }
} }
static get styles(): CSSResult[] {
return [
haStyle,
hassioStyle,
css`
:host {
display: block;
}
ha-card {
display: block;
margin-bottom: 16px;
}
ha-card.warning {
background-color: var(--error-color);
color: white;
}
ha-card.warning .card-header {
color: white;
}
ha-card.warning .card-content {
color: white;
}
ha-card.warning mwc-button {
--mdc-theme-primary: white !important;
}
.warning {
color: var(--error-color);
--mdc-theme-primary: var(--error-color);
}
.light-color {
color: var(--secondary-text-color);
}
.addon-header {
padding-left: 8px;
font-size: 24px;
color: var(--ha-card-header-color, --primary-text-color);
}
.addon-version {
float: right;
font-size: 15px;
vertical-align: middle;
}
.errors {
color: var(--error-color);
margin-bottom: 16px;
}
.description {
margin-bottom: 16px;
}
img.logo {
max-height: 60px;
margin: 16px 0;
display: block;
}
ha-switch {
display: flex;
}
ha-svg-icon.running {
color: var(--paper-green-400);
}
ha-svg-icon.stopped {
color: var(--google-red-300);
}
ha-call-api-button {
font-weight: 500;
color: var(--primary-color);
}
.right {
float: right;
}
protection-enable mwc-button {
--mdc-theme-primary: white;
}
.description a {
color: var(--primary-color);
}
.red {
--ha-label-badge-color: var(--label-badge-red, #df4c1e);
}
.blue {
--ha-label-badge-color: var(--label-badge-blue, #039be5);
}
.green {
--ha-label-badge-color: var(--label-badge-green, #0da035);
}
.yellow {
--ha-label-badge-color: var(--label-badge-yellow, #f4b400);
}
.security {
margin-bottom: 16px;
}
.card-actions {
display: flow-root;
}
.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 {
display: contents;
}
.changelog-link {
color: var(--primary-color);
text-decoration: underline;
cursor: pointer;
}
ha-markdown {
padding: 16px;
}
ha-settings-row {
padding: 0;
height: 54px;
width: 100%;
}
ha-settings-row > span[slot="description"] {
white-space: normal;
color: var(--secondary-text-color);
}
ha-settings-row[three-line] {
height: 74px;
}
.addon-options {
max-width: 50%;
}
@media (max-width: 720px) {
.addon-options {
max-width: 100%;
}
}
`,
];
}
} }
declare global { declare global {
interface HTMLElementTagNameMap { interface HTMLElementTagNameMap {

View File

@@ -1,3 +1,4 @@
import "@polymer/paper-spinner/paper-spinner-lite";
import { import {
css, css,
CSSResult, CSSResult,
@@ -8,7 +9,6 @@ import {
TemplateResult, TemplateResult,
} from "lit-element"; } from "lit-element";
import { HassioAddonDetails } from "../../../../src/data/hassio/addon"; import { HassioAddonDetails } from "../../../../src/data/hassio/addon";
import "../../../../src/components/ha-circular-progress";
import { haStyle } from "../../../../src/resources/styles"; import { haStyle } from "../../../../src/resources/styles";
import { HomeAssistant } from "../../../../src/types"; import { HomeAssistant } from "../../../../src/types";
import { hassioStyle } from "../../resources/hassio-style"; import { hassioStyle } from "../../resources/hassio-style";
@@ -22,7 +22,7 @@ class HassioAddonLogDashboard extends LitElement {
protected render(): TemplateResult { protected render(): TemplateResult {
if (!this.addon) { if (!this.addon) {
return html` <ha-circular-progress active></ha-circular-progress> `; return html` <paper-spinner-lite active></paper-spinner-lite> `;
} }
return html` return html`
<div class="content"> <div class="content">

View File

@@ -6,7 +6,6 @@ import {
html, html,
LitElement, LitElement,
property, property,
internalProperty,
TemplateResult, TemplateResult,
} from "lit-element"; } from "lit-element";
import "../../../../src/components/ha-card"; import "../../../../src/components/ha-card";
@@ -25,9 +24,9 @@ class HassioAddonLogs extends LitElement {
@property({ attribute: false }) public addon!: HassioAddonDetails; @property({ attribute: false }) public addon!: HassioAddonDetails;
@internalProperty() private _error?: string; @property() private _error?: string;
@internalProperty() private _content?: string; @property() private _content?: string;
public async connectedCallback(): Promise<void> { public async connectedCallback(): Promise<void> {
super.connectedCallback(); super.connectedCallback();
@@ -63,7 +62,7 @@ class HassioAddonLogs extends LitElement {
display: block; display: block;
} }
.errors { .errors {
color: var(--error-color); color: var(--google-red-500);
margin-bottom: 16px; margin-bottom: 16px;
} }
`, `,

View File

@@ -14,7 +14,7 @@ import { HomeAssistant } from "../../../src/types";
@customElement("hassio-card-content") @customElement("hassio-card-content")
class HassioCardContent extends LitElement { class HassioCardContent extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant; @property() public hass!: HomeAssistant;
@property() public title!: string; @property() public title!: string;
@@ -98,7 +98,7 @@ class HassioCardContent extends LitElement {
color: var(--paper-item-icon-color); color: var(--paper-item-icon-color);
} }
ha-svg-icon.not_available { ha-svg-icon.not_available {
color: var(--error-color); color: var(--google-red-500);
} }
.title { .title {
color: var(--primary-text-color); color: var(--primary-text-color);

View File

@@ -19,7 +19,7 @@ import { hassioStyle } from "../resources/hassio-style";
@customElement("hassio-addons") @customElement("hassio-addons")
class HassioAddons extends LitElement { class HassioAddons extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant; @property() public hass!: HomeAssistant;
@property() public addons?: HassioAddonInfo[]; @property() public addons?: HassioAddonInfo[];

View File

@@ -15,7 +15,7 @@ import {
import "../../../src/layouts/hass-tabs-subpage"; import "../../../src/layouts/hass-tabs-subpage";
import { haStyle } from "../../../src/resources/styles"; import { haStyle } from "../../../src/resources/styles";
import { HomeAssistant, Route } from "../../../src/types"; import { HomeAssistant, Route } from "../../../src/types";
import { supervisorTabs } from "../hassio-tabs"; import { supervisorTabs } from "../hassio-panel";
import "./hassio-addons"; import "./hassio-addons";
import "./hassio-update"; import "./hassio-update";

View File

@@ -7,10 +7,9 @@ import {
html, html,
LitElement, LitElement,
property, property,
internalProperty,
TemplateResult, TemplateResult,
} from "lit-element"; } from "lit-element";
import "../../../src/components/buttons/ha-progress-button"; import "../../../src/components/buttons/ha-call-api-button";
import "../../../src/components/ha-card"; import "../../../src/components/ha-card";
import "../../../src/components/ha-svg-icon"; import "../../../src/components/ha-svg-icon";
import { HassioHassOSInfo } from "../../../src/data/hassio/host"; import { HassioHassOSInfo } from "../../../src/data/hassio/host";
@@ -21,23 +20,18 @@ import {
import { haStyle } from "../../../src/resources/styles"; import { haStyle } from "../../../src/resources/styles";
import { HomeAssistant } from "../../../src/types"; import { HomeAssistant } from "../../../src/types";
import { hassioStyle } from "../resources/hassio-style"; import { hassioStyle } from "../resources/hassio-style";
import {
showConfirmationDialog,
showAlertDialog,
} from "../../../src/dialogs/generic/show-dialog-box";
import { HassioResponse } from "../../../src/data/hassio/common";
@customElement("hassio-update") @customElement("hassio-update")
export class HassioUpdate extends LitElement { export class HassioUpdate extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant; @property() public hass!: HomeAssistant;
@property({ attribute: false }) public hassInfo: HassioHomeAssistantInfo; @property() public hassInfo: HassioHomeAssistantInfo;
@property({ attribute: false }) public hassOsInfo?: HassioHassOSInfo; @property() public hassOsInfo?: HassioHassOSInfo;
@property() public supervisorInfo: HassioSupervisorInfo; @property() public supervisorInfo: HassioSupervisorInfo;
@internalProperty() private _error?: string; @property() private _error?: string;
protected render(): TemplateResult { protected render(): TemplateResult {
const updatesAvailable: number = [ const updatesAvailable: number = [
@@ -131,43 +125,31 @@ export class HassioUpdate extends LitElement {
<a href="${releaseNotesUrl}" target="_blank" rel="noreferrer"> <a href="${releaseNotesUrl}" target="_blank" rel="noreferrer">
<mwc-button>Release notes</mwc-button> <mwc-button>Release notes</mwc-button>
</a> </a>
<ha-progress-button <ha-call-api-button
.apiPath=${apiPath} .hass=${this.hass}
.name=${name} .path=${apiPath}
.version=${lastVersion} @hass-api-called=${this._apiCalled}
@click=${this._confirmUpdate}
> >
Update Update
</ha-progress-button> </ha-call-api-button>
</div> </div>
</ha-card> </ha-card>
`; `;
} }
private async _confirmUpdate(ev): Promise<void> { private _apiCalled(ev): void {
const item = ev.target; if (ev.detail.success) {
item.progress = true; this._error = "";
const confirmed = await showConfirmationDialog(this, {
title: `Update ${item.name}`,
text: `Are you sure you want to upgrade ${item.name} to version ${item.version}?`,
confirmText: "update",
dismissText: "cancel",
});
if (!confirmed) {
item.progress = false;
return; return;
} }
try {
await this.hass.callApi<HassioResponse<void>>("POST", item.apiPath); const response = ev.detail.response;
} catch (err) {
showAlertDialog(this, { if (typeof response.body === "object") {
title: "Update failed", this._error = response.body.message || "Unknown error";
text: } else {
typeof err === "object" ? err.body?.message || "Unkown error" : err, this._error = response.body;
});
} }
item.progress = false;
} }
static get styles(): CSSResult[] { static get styles(): CSSResult[] {
@@ -198,7 +180,7 @@ export class HassioUpdate extends LitElement {
text-align: right; text-align: right;
} }
.errors { .errors {
color: var(--error-color); color: var(--google-red-500);
padding: 16px; padding: 16px;
} }
a { a {

View File

@@ -5,7 +5,6 @@ import {
html, html,
LitElement, LitElement,
property, property,
internalProperty,
TemplateResult, TemplateResult,
} from "lit-element"; } from "lit-element";
import { createCloseHeading } from "../../../../src/components/ha-dialog"; import { createCloseHeading } from "../../../../src/components/ha-dialog";
@@ -17,13 +16,13 @@ import { HassioMarkdownDialogParams } from "./show-dialog-hassio-markdown";
@customElement("dialog-hassio-markdown") @customElement("dialog-hassio-markdown")
class HassioMarkdownDialog extends LitElement { class HassioMarkdownDialog extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant; @property() public hass!: HomeAssistant;
@property() public title!: string; @property() public title!: string;
@property() public content!: string; @property() public content!: string;
@internalProperty() private _opened = false; @property() private _opened = false;
public showDialog(params: HassioMarkdownDialogParams) { public showDialog(params: HassioMarkdownDialogParams) {
this.title = params.title; this.title = params.title;
@@ -31,10 +30,6 @@ class HassioMarkdownDialog extends LitElement {
this._opened = true; this._opened = true;
} }
public closeDialog() {
this._opened = false;
}
protected render(): TemplateResult { protected render(): TemplateResult {
if (!this._opened) { if (!this._opened) {
return html``; return html``;
@@ -42,7 +37,7 @@ class HassioMarkdownDialog extends LitElement {
return html` return html`
<ha-dialog <ha-dialog
open open
@closed=${this.closeDialog} @closing=${this._closeDialog}
.heading=${createCloseHeading(this.hass, this.title)} .heading=${createCloseHeading(this.hass, this.title)}
> >
<ha-markdown .content=${this.content || ""}></ha-markdown> <ha-markdown .content=${this.content || ""}></ha-markdown>
@@ -50,6 +45,10 @@ class HassioMarkdownDialog extends LitElement {
`; `;
} }
private _closeDialog(): void {
this._opened = false;
}
static get styles(): CSSResult[] { static get styles(): CSSResult[] {
return [ return [
haStyleDialog, haStyleDialog,

View File

@@ -1,328 +0,0 @@
import "@material/mwc-button/mwc-button";
import "@material/mwc-icon-button";
import "@material/mwc-tab-bar";
import "@material/mwc-tab";
import { PaperInputElement } from "@polymer/paper-input/paper-input";
import { mdiClose } from "@mdi/js";
import {
css,
CSSResult,
customElement,
html,
LitElement,
property,
internalProperty,
TemplateResult,
} from "lit-element";
import { cache } from "lit-html/directives/cache";
import {
updateNetworkInterface,
NetworkInterface,
} from "../../../../src/data/hassio/network";
import { fireEvent } from "../../../../src/common/dom/fire_event";
import { HassioNetworkDialogParams } from "./show-dialog-network";
import { haStyleDialog } from "../../../../src/resources/styles";
import {
showAlertDialog,
showConfirmationDialog,
} from "../../../../src/dialogs/generic/show-dialog-box";
import type { HomeAssistant } from "../../../../src/types";
import type { HaRadio } from "../../../../src/components/ha-radio";
import { HassDialog } from "../../../../src/dialogs/make-dialog-manager";
import "../../../../src/components/ha-circular-progress";
import "../../../../src/components/ha-dialog";
import "../../../../src/components/ha-formfield";
import "../../../../src/components/ha-header-bar";
import "../../../../src/components/ha-radio";
import "../../../../src/components/ha-related-items";
import "../../../../src/components/ha-svg-icon";
@customElement("dialog-hassio-network")
export class DialogHassioNetwork extends LitElement implements HassDialog {
@property({ attribute: false }) public hass!: HomeAssistant;
@internalProperty() private _prosessing = false;
@internalProperty() private _params?: HassioNetworkDialogParams;
@internalProperty() private _network!: {
interface: string;
data: NetworkInterface;
}[];
@internalProperty() private _curTabIndex = 0;
@internalProperty() private _device?: {
interface: string;
data: NetworkInterface;
};
@internalProperty() private _dirty = false;
public async showDialog(params: HassioNetworkDialogParams): Promise<void> {
this._params = params;
this._dirty = false;
this._curTabIndex = 0;
this._network = Object.keys(params.network?.interfaces)
.map((device) => ({
interface: device,
data: params.network.interfaces[device],
}))
.sort((a, b) => {
return a.data.primary > b.data.primary ? -1 : 1;
});
this._device = this._network[this._curTabIndex];
this._device.data.nameservers = String(this._device.data.nameservers);
await this.updateComplete;
}
public closeDialog(): void {
this._params = undefined;
this._prosessing = false;
fireEvent(this, "dialog-closed", { dialog: this.localName });
}
protected render(): TemplateResult {
if (!this._params || !this._network) {
return html``;
}
return html`
<ha-dialog open .heading=${true} hideActions @closed=${this.closeDialog}>
<div slot="heading">
<ha-header-bar>
<span slot="title">
Network settings
</span>
<mwc-icon-button slot="actionItems" dialogAction="cancel">
<ha-svg-icon .path=${mdiClose}></ha-svg-icon>
</mwc-icon-button>
</ha-header-bar>
${this._network.length > 1
? html` <mwc-tab-bar
.activeIndex=${this._curTabIndex}
@MDCTabBar:activated=${this._handleTabActivated}
>${this._network.map(
(device) =>
html`<mwc-tab
.id=${device.interface}
.label=${device.interface}
>
</mwc-tab>`
)}
</mwc-tab-bar>`
: ""}
</div>
${cache(this._renderTab())}
</ha-dialog>
`;
}
private _renderTab() {
return html` <div class="form container">
<ha-formfield label="DHCP">
<ha-radio
@change=${this._handleRadioValueChanged}
value="dhcp"
name="method"
?checked=${this._device!.data.method === "dhcp"}
>
</ha-radio>
</ha-formfield>
<ha-formfield label="Static">
<ha-radio
@change=${this._handleRadioValueChanged}
value="static"
name="method"
?checked=${this._device!.data.method === "static"}
>
</ha-radio>
</ha-formfield>
${this._device!.data.method !== "dhcp"
? html` <paper-input
class="flex-auto"
id="ip_address"
label="IP address/Netmask"
.value="${this._device!.data.ip_address}"
@value-changed=${this._handleInputValueChanged}
></paper-input>
<paper-input
class="flex-auto"
id="gateway"
label="Gateway address"
.value="${this._device!.data.gateway}"
@value-changed=${this._handleInputValueChanged}
></paper-input>
<paper-input
class="flex-auto"
id="nameservers"
label="DNS servers"
.value="${this._device!.data.nameservers as string}"
@value-changed=${this._handleInputValueChanged}
></paper-input>
NB!: If you are changing IP or gateway addresses, you might lose
the connection.`
: ""}
</div>
<div class="buttons">
<mwc-button label="close" @click=${this.closeDialog}> </mwc-button>
<mwc-button @click=${this._updateNetwork} ?disabled=${!this._dirty}>
${this._prosessing
? html`<ha-circular-progress active></ha-circular-progress>`
: "Update"}
</mwc-button>
</div>`;
}
private async _updateNetwork() {
this._prosessing = true;
let options: Partial<NetworkInterface> = {
method: this._device!.data.method,
};
if (options.method !== "dhcp") {
options = {
...options,
address: this._device!.data.ip_address,
gateway: this._device!.data.gateway,
dns: String(this._device!.data.nameservers).split(","),
};
}
try {
await updateNetworkInterface(this.hass, this._device!.interface, options);
} catch (err) {
showAlertDialog(this, {
title: "Failed to change network settings",
text:
typeof err === "object" ? err.body.message || "Unkown error" : err,
});
this._prosessing = false;
return;
}
this._params?.loadData();
this.closeDialog();
}
private async _handleTabActivated(ev: CustomEvent): Promise<void> {
if (this._dirty) {
const confirm = await showConfirmationDialog(this, {
text:
"You have unsaved changes, these will get lost if you change tabs, do you want to continue?",
confirmText: "yes",
dismissText: "no",
});
if (!confirm) {
this.requestUpdate("_device");
return;
}
}
this._curTabIndex = ev.detail.index;
this._device = this._network[ev.detail.index];
this._device.data.nameservers = String(this._device.data.nameservers);
}
private _handleRadioValueChanged(ev: CustomEvent): void {
const value = (ev.target as HaRadio).value as "dhcp" | "static";
if (!value || !this._device || this._device!.data.method === value) {
return;
}
this._dirty = true;
this._device!.data.method = value;
this.requestUpdate("_device");
}
private _handleInputValueChanged(ev: CustomEvent): void {
const value: string | null | undefined = (ev.target as PaperInputElement)
.value;
const id = (ev.target as PaperInputElement).id;
if (!value || !this._device || this._device.data[id] === value) {
return;
}
this._dirty = true;
this._device.data[id] = value;
}
static get styles(): CSSResult[] {
return [
haStyleDialog,
css`
ha-header-bar {
--mdc-theme-on-primary: var(--primary-text-color);
--mdc-theme-primary: var(--mdc-theme-surface);
flex-shrink: 0;
}
mwc-tab-bar {
border-bottom: 1px solid
var(--mdc-dialog-scroll-divider-color, rgba(0, 0, 0, 0.12));
}
ha-dialog {
--dialog-content-position: static;
--dialog-content-padding: 0;
--dialog-z-index: 6;
}
@media all and (min-width: 451px) and (min-height: 501px) {
.container {
width: 400px;
}
}
.content {
display: block;
padding: 20px 24px;
}
/* overrule the ha-style-dialog max-height on small screens */
@media all and (max-width: 450px), all and (max-height: 500px) {
ha-header-bar {
--mdc-theme-primary: var(--app-header-background-color);
--mdc-theme-on-primary: var(--app-header-text-color, white);
}
}
mwc-button.warning {
--mdc-theme-primary: var(--error-color);
}
:host([rtl]) app-toolbar {
direction: rtl;
text-align: right;
}
.container {
padding: 20px 24px;
}
.form {
margin-bottom: 53px;
}
.buttons {
position: absolute;
bottom: 0;
width: 100%;
box-sizing: border-box;
border-top: 1px solid
var(--mdc-dialog-scroll-divider-color, rgba(0, 0, 0, 0.12));
display: flex;
justify-content: space-between;
padding: 8px;
padding-bottom: max(env(safe-area-inset-bottom), 8px);
background-color: var(--mdc-theme-surface, #fff);
}
`,
];
}
}
declare global {
interface HTMLElementTagNameMap {
"dialog-hassio-network": DialogHassioNetwork;
}
}

View File

@@ -1,22 +0,0 @@
import { fireEvent } from "../../../../src/common/dom/fire_event";
import { NetworkInfo } from "../../../../src/data/hassio/network";
import "./dialog-hassio-network";
export interface HassioNetworkDialogParams {
network: NetworkInfo;
loadData: () => Promise<void>;
}
export const showNetworkDialog = (
element: HTMLElement,
dialogParams: HassioNetworkDialogParams
): void => {
fireEvent(element, "show-dialog", {
dialogTag: "dialog-hassio-network",
dialogImport: () =>
import(
/* webpackChunkName: "dialog-hassio-network" */ "./dialog-hassio-network"
),
dialogParams,
});
};

View File

@@ -5,7 +5,7 @@ import "@polymer/paper-input/paper-input";
import type { PaperInputElement } from "@polymer/paper-input/paper-input"; import type { PaperInputElement } from "@polymer/paper-input/paper-input";
import "@polymer/paper-item/paper-item"; import "@polymer/paper-item/paper-item";
import "@polymer/paper-item/paper-item-body"; import "@polymer/paper-item/paper-item-body";
import "../../../../src/components/ha-circular-progress"; import "@polymer/paper-spinner/paper-spinner";
import { import {
css, css,
CSSResult, CSSResult,
@@ -13,7 +13,6 @@ import {
html, html,
LitElement, LitElement,
property, property,
internalProperty,
query, query,
TemplateResult, TemplateResult,
} from "lit-element"; } from "lit-element";
@@ -40,11 +39,11 @@ class HassioRepositoriesDialog extends LitElement {
@query("#repository_input") private _optionInput?: PaperInputElement; @query("#repository_input") private _optionInput?: PaperInputElement;
@internalProperty() private _opened = false; @property() private _opened = false;
@internalProperty() private _prosessing = false; @property() private _prosessing = false;
@internalProperty() private _error?: string; @property() private _error?: string;
public async showDialog(_dialogParams: any): Promise<void> { public async showDialog(_dialogParams: any): Promise<void> {
this._dialogParams = _dialogParams; this._dialogParams = _dialogParams;
@@ -109,7 +108,7 @@ class HassioRepositoriesDialog extends LitElement {
></paper-input> ></paper-input>
<mwc-button @click=${this._addRepository}> <mwc-button @click=${this._addRepository}>
${this._prosessing ${this._prosessing
? html`<ha-circular-progress active></ha-circular-progress>` ? html`<paper-spinner active></paper-spinner>`
: "Add"} : "Add"}
</mwc-button> </mwc-button>
</div> </div>

View File

@@ -7,7 +7,6 @@ import {
CSSResult, CSSResult,
customElement, customElement,
html, html,
internalProperty,
LitElement, LitElement,
property, property,
TemplateResult, TemplateResult,
@@ -19,7 +18,6 @@ import {
fetchHassioSnapshotInfo, fetchHassioSnapshotInfo,
HassioSnapshotDetail, HassioSnapshotDetail,
} from "../../../../src/data/hassio/snapshot"; } from "../../../../src/data/hassio/snapshot";
import { showConfirmationDialog } from "../../../../src/dialogs/generic/show-dialog-box";
import { PolymerChangedEvent } from "../../../../src/polymer-types"; import { PolymerChangedEvent } from "../../../../src/polymer-types";
import { haStyleDialog } from "../../../../src/resources/styles"; import { haStyleDialog } from "../../../../src/resources/styles";
import { HomeAssistant } from "../../../../src/types"; import { HomeAssistant } from "../../../../src/types";
@@ -70,21 +68,21 @@ interface FolderItem {
@customElement("dialog-hassio-snapshot") @customElement("dialog-hassio-snapshot")
class HassioSnapshotDialog extends LitElement { class HassioSnapshotDialog extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant; @property() public hass!: HomeAssistant;
@internalProperty() private _error?: string; @property() private _error?: string;
@internalProperty() private _snapshot?: HassioSnapshotDetail; @property() private _snapshot?: HassioSnapshotDetail;
@internalProperty() private _folders!: FolderItem[]; @property() private _folders!: FolderItem[];
@internalProperty() private _addons!: AddonItem[]; @property() private _addons!: AddonItem[];
@internalProperty() private _dialogParams?: HassioSnapshotDialogParams; @property() private _dialogParams?: HassioSnapshotDialogParams;
@internalProperty() private _snapshotPassword!: string; @property() private _snapshotPassword!: string;
@internalProperty() private _restoreHass: boolean | null | undefined = true; @property() private _restoreHass: boolean | null | undefined = true;
public async showDialog(params: HassioSnapshotDialogParams) { public async showDialog(params: HassioSnapshotDialogParams) {
this._snapshot = await fetchHassioSnapshotInfo(this.hass, params.slug); this._snapshot = await fetchHassioSnapshotInfo(this.hass, params.slug);
@@ -226,7 +224,7 @@ class HassioSnapshotDialog extends LitElement {
} }
.warning, .warning,
.error { .error {
color: var(--error-color); color: var(--google-red-500);
} }
.buttons { .buttons {
display: flex; display: flex;
@@ -267,12 +265,8 @@ class HassioSnapshotDialog extends LitElement {
this._snapshotPassword = ev.detail.value; this._snapshotPassword = ev.detail.value;
} }
private async _partialRestoreClicked() { private _partialRestoreClicked() {
if ( if (!confirm("Are you sure you want to restore this snapshot?")) {
!(await showConfirmationDialog(this, {
title: "Are you sure you want partially to restore this snapshot?",
}))
) {
return; return;
} }
@@ -317,13 +311,8 @@ class HassioSnapshotDialog extends LitElement {
); );
} }
private async _fullRestoreClicked() { private _fullRestoreClicked() {
if ( if (!confirm("Are you sure you want to restore this snapshot?")) {
!(await showConfirmationDialog(this, {
title:
"Are you sure you want to wipe your system and restore this snapshot?",
}))
) {
return; return;
} }
@@ -348,12 +337,8 @@ class HassioSnapshotDialog extends LitElement {
); );
} }
private async _deleteClicked() { private _deleteClicked() {
if ( if (!confirm("Are you sure you want to delete this snapshot?")) {
!(await showConfirmationDialog(this, {
title: "Are you sure you want to delete this snapshot?",
}))
) {
return; return;
} }

View File

@@ -1,5 +1,5 @@
import "../../src/resources/compatibility"; import "~app/resources/compatibility";
import "../../src/resources/roboto"; import "~app/resources/roboto";
import "./hassio-main"; import "./hassio-main";
const styleEl = document.createElement("style"); const styleEl = document.createElement("style");

View File

@@ -1,35 +1,111 @@
import { import { PolymerElement } from "@polymer/polymer";
html, import { customElement, property, PropertyValues } from "lit-element";
PropertyValues,
customElement,
LitElement,
property,
} from "lit-element";
import "./hassio-router";
import { urlSyncMixin } from "../../src/state/url-sync-mixin";
import { ProvideHassLitMixin } from "../../src/mixins/provide-hass-lit-mixin";
import { HomeAssistant, Route } from "../../src/types";
import { HassioPanelInfo } from "../../src/data/hassio/supervisor";
import { applyThemesOnElement } from "../../src/common/dom/apply_themes_on_element"; import { applyThemesOnElement } from "../../src/common/dom/apply_themes_on_element";
import { fireEvent } from "../../src/common/dom/fire_event"; import { fireEvent } from "../../src/common/dom/fire_event";
import { navigate } from "../../src/common/navigate";
import { fetchHassioAddonInfo } from "../../src/data/hassio/addon";
import {
fetchHassioHassOsInfo,
fetchHassioHostInfo,
HassioHassOSInfo,
HassioHostInfo,
} from "../../src/data/hassio/host";
import {
createHassioSession,
fetchHassioHomeAssistantInfo,
fetchHassioSupervisorInfo,
fetchHassioInfo,
HassioHomeAssistantInfo,
HassioInfo,
HassioPanelInfo,
HassioSupervisorInfo,
} from "../../src/data/hassio/supervisor";
import {
AlertDialogParams,
showAlertDialog,
} from "../../src/dialogs/generic/show-dialog-box";
import { makeDialogManager } from "../../src/dialogs/make-dialog-manager"; import { makeDialogManager } from "../../src/dialogs/make-dialog-manager";
import { atLeastVersion } from "../../src/common/config/version"; import {
HassRouterPage,
RouterOptions,
} from "../../src/layouts/hass-router-page";
import { ProvideHassLitMixin } from "../../src/mixins/provide-hass-lit-mixin";
import "../../src/resources/ha-style";
import { HomeAssistant } from "../../src/types";
// Don't codesplit it, that way the dashboard always loads fast.
import "./hassio-panel";
@customElement("hassio-main") @customElement("hassio-main")
export class HassioMain extends urlSyncMixin(ProvideHassLitMixin(LitElement)) { class HassioMain extends ProvideHassLitMixin(HassRouterPage) {
@property({ attribute: false }) public hass!: HomeAssistant; @property() public hass!: HomeAssistant;
@property() public panel!: HassioPanelInfo; @property() public panel!: HassioPanelInfo;
@property() public narrow!: boolean; @property() public narrow!: boolean;
@property() public route?: Route; protected routerOptions: RouterOptions = {
// Hass.io has a page with tabs, so we route all non-matching routes to it.
defaultPage: "dashboard",
initialLoad: () => this._fetchData(),
showLoading: true,
routes: {
dashboard: {
tag: "hassio-panel",
cache: true,
},
snapshots: "dashboard",
store: "dashboard",
system: "dashboard",
addon: {
tag: "hassio-addon-dashboard",
load: () =>
import(
/* webpackChunkName: "hassio-addon-dashboard" */ "./addon-view/hassio-addon-dashboard"
),
},
ingress: {
tag: "hassio-ingress-view",
load: () =>
import(
/* webpackChunkName: "hassio-ingress-view" */ "./ingress-view/hassio-ingress-view"
),
},
},
};
@property() private _supervisorInfo: HassioSupervisorInfo;
@property() private _hostInfo: HassioHostInfo;
@property() private _hassioInfo?: HassioInfo;
@property() private _hassOsInfo?: HassioHassOSInfo;
@property() private _hassInfo: HassioHomeAssistantInfo;
protected firstUpdated(changedProps: PropertyValues) { protected firstUpdated(changedProps: PropertyValues) {
super.firstUpdated(changedProps); super.firstUpdated(changedProps);
this._applyTheme(); applyThemesOnElement(
this.parentElement,
this.hass.themes,
this.hass.selectedTheme || this.hass.themes.default_theme
);
this.style.setProperty(
"--app-header-background-color",
"var(--sidebar-background-color)"
);
this.style.setProperty(
"--app-header-text-color",
"var(--sidebar-text-color)"
);
this.style.setProperty(
"--app-header-border-bottom",
"1px solid var(--divider-color)"
);
this.addEventListener("hass-api-called", (ev) => this._apiCalled(ev));
// Paulus - March 17, 2019 // Paulus - March 17, 2019
// We went to a single hass-toggle-menu event in HA 0.90. However, the // We went to a single hass-toggle-menu event in HA 0.90. However, the
// supervisor UI can also run under older versions of Home Assistant. // supervisor UI can also run under older versions of Home Assistant.
@@ -62,61 +138,152 @@ export class HassioMain extends urlSyncMixin(ProvideHassLitMixin(LitElement)) {
}); });
}); });
makeDialogManager(this, this.shadowRoot!); makeDialogManager(this, document.body);
} }
protected updated(changedProps: PropertyValues) { protected updatePageEl(el) {
super.updated(changedProps); // the tabs page does its own routing so needs full route.
const oldHass = changedProps.get("hass") as HomeAssistant | undefined; const route = el.nodeName === "HASSIO-PANEL" ? this.route : this.routeTail;
if (!oldHass) {
if ("setProperties" in el) {
// As long as we have Polymer pages
(el as PolymerElement).setProperties({
hass: this.hass,
narrow: this.narrow,
supervisorInfo: this._supervisorInfo,
hassioInfo: this._hassioInfo,
hostInfo: this._hostInfo,
hassInfo: this._hassInfo,
hassOsInfo: this._hassOsInfo,
route,
});
} else {
el.hass = this.hass;
el.narrow = this.narrow;
el.supervisorInfo = this._supervisorInfo;
el.hassioInfo = this._hassioInfo;
el.hostInfo = this._hostInfo;
el.hassInfo = this._hassInfo;
el.hassOsInfo = this._hassOsInfo;
el.route = route;
}
}
private async _fetchData() {
if (this.panel.config && this.panel.config.ingress) {
await this._redirectIngress(this.panel.config.ingress);
return; return;
} }
if (oldHass.themes !== this.hass.themes) {
this._applyTheme(); const [supervisorInfo, hostInfo, hassInfo, hassioInfo] = await Promise.all([
fetchHassioSupervisorInfo(this.hass),
fetchHassioHostInfo(this.hass),
fetchHassioHomeAssistantInfo(this.hass),
fetchHassioInfo(this.hass),
]);
this._supervisorInfo = supervisorInfo;
this._hassioInfo = hassioInfo;
this._hostInfo = hostInfo;
this._hassInfo = hassInfo;
if (this._hostInfo.features && this._hostInfo.features.includes("hassos")) {
this._hassOsInfo = await fetchHassioHassOsInfo(this.hass);
} }
} }
protected render() { private async _redirectIngress(addonSlug: string) {
return html` // When we trigger a navigation, we sleep to make sure we don't
<hassio-router // show the hassio dashboard before navigating away.
.hass=${this.hass} const awaitAlert = async (
.route=${this.route} alertParams: AlertDialogParams,
.panel=${this.panel} action: () => void
.narrow=${this.narrow} ) => {
></hassio-router> await new Promise((resolve) => {
`; alertParams.confirm = resolve;
} showAlertDialog(this, alertParams);
});
action();
await new Promise((resolve) => setTimeout(resolve, 1000));
};
private _applyTheme() { const createSessionPromise = createHassioSession(this.hass).then(
let themeName: string; () => true,
let options: Partial<HomeAssistant["selectedTheme"]> | undefined; () => false
if (atLeastVersion(this.hass.config.version, 0, 114)) {
themeName =
this.hass.selectedTheme?.theme ||
(this.hass.themes.darkMode && this.hass.themes.default_dark_theme
? this.hass.themes.default_dark_theme!
: this.hass.themes.default_theme);
options = this.hass.selectedTheme;
if (themeName === "default" && options?.dark === undefined) {
options = {
...this.hass.selectedTheme,
dark: this.hass.themes.darkMode,
};
}
} else {
themeName =
((this.hass.selectedTheme as unknown) as string) ||
this.hass.themes.default_theme;
}
applyThemesOnElement(
this.parentElement,
this.hass.themes,
themeName,
options
); );
let addon;
try {
addon = await fetchHassioAddonInfo(this.hass, addonSlug);
} catch (err) {
await awaitAlert(
{
text: "Unable to fetch add-on info to start Ingress",
title: "Supervisor",
},
() => history.back()
);
return;
}
if (!addon.ingress_url) {
await awaitAlert(
{
text: "Add-on does not support Ingress",
title: addon.name,
},
() => history.back()
);
return;
}
if (addon.state !== "started") {
await awaitAlert(
{
text: "Add-on is not running. Please start it first",
title: addon.name,
},
() => navigate(this, `/hassio/addon/${addon.slug}/info`, true)
);
return;
}
if (!(await createSessionPromise)) {
await awaitAlert(
{
text: "Unable to create an Ingress session",
title: addon.name,
},
() => history.back()
);
return;
}
location.assign(addon.ingress_url);
// await a promise that doesn't resolve, so we show the loading screen
// while we load the next page.
await new Promise(() => undefined);
}
private _apiCalled(ev) {
if (!ev.detail.success) {
return;
}
let tries = 1;
const tryUpdate = () => {
this._fetchData().catch(() => {
tries += 1;
setTimeout(tryUpdate, Math.min(tries, 5) * 1000);
});
};
tryUpdate();
} }
} }

View File

@@ -1,11 +1,10 @@
import { mdiBackupRestore, mdiCogs, mdiStore, mdiViewDashboard } from "@mdi/js";
import { import {
customElement, customElement,
html, html,
LitElement, LitElement,
property, property,
TemplateResult, TemplateResult,
css,
CSSResult,
} from "lit-element"; } from "lit-element";
import { HassioHassOSInfo, HassioHostInfo } from "../../src/data/hassio/host"; import { HassioHassOSInfo, HassioHostInfo } from "../../src/data/hassio/host";
import { import {
@@ -13,9 +12,33 @@ import {
HassioSupervisorInfo, HassioSupervisorInfo,
HassioInfo, HassioInfo,
} from "../../src/data/hassio/supervisor"; } from "../../src/data/hassio/supervisor";
import type { PageNavigation } from "../../src/layouts/hass-tabs-subpage";
import { HomeAssistant, Route } from "../../src/types"; import { HomeAssistant, Route } from "../../src/types";
import "./hassio-panel-router"; import "./hassio-panel-router";
export const supervisorTabs: PageNavigation[] = [
{
name: "Dashboard",
path: `/hassio/dashboard`,
iconPath: mdiViewDashboard,
},
{
name: "Add-on store",
path: `/hassio/store`,
iconPath: mdiStore,
},
{
name: "Snapshots",
path: `/hassio/snapshots`,
iconPath: mdiBackupRestore,
},
{
name: "System",
path: `/hassio/system`,
iconPath: mdiCogs,
},
];
@customElement("hassio-panel") @customElement("hassio-panel")
class HassioPanel extends LitElement { class HassioPanel extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant; @property({ attribute: false }) public hass!: HomeAssistant;
@@ -35,9 +58,6 @@ class HassioPanel extends LitElement {
@property({ attribute: false }) public hassOsInfo!: HassioHassOSInfo; @property({ attribute: false }) public hassOsInfo!: HassioHassOSInfo;
protected render(): TemplateResult { protected render(): TemplateResult {
if (!this.supervisorInfo) {
return html``;
}
return html` return html`
<hassio-panel-router <hassio-panel-router
.route=${this.route} .route=${this.route}
@@ -51,16 +71,6 @@ class HassioPanel extends LitElement {
></hassio-panel-router> ></hassio-panel-router>
`; `;
} }
static get styles(): CSSResult {
return css`
:host {
--app-header-background-color: var(--sidebar-background-color);
--app-header-text-color: var(--sidebar-text-color);
--app-header-border-bottom: 1px solid var(--divider-color);
}
`;
}
} }
declare global { declare global {

View File

@@ -1,150 +0,0 @@
import {
customElement,
property,
internalProperty,
PropertyValues,
} from "lit-element";
import {
fetchHassioHassOsInfo,
fetchHassioHostInfo,
HassioHassOSInfo,
HassioHostInfo,
} from "../../src/data/hassio/host";
import {
fetchHassioHomeAssistantInfo,
fetchHassioSupervisorInfo,
fetchHassioInfo,
HassioHomeAssistantInfo,
HassioInfo,
HassioPanelInfo,
HassioSupervisorInfo,
} from "../../src/data/hassio/supervisor";
import {
HassRouterPage,
RouterOptions,
} from "../../src/layouts/hass-router-page";
import "../../src/resources/ha-style";
import { HomeAssistant } from "../../src/types";
// Don't codesplit it, that way the dashboard always loads fast.
import "./hassio-panel";
@customElement("hassio-router")
class HassioRouter extends HassRouterPage {
@property({ attribute: false }) public hass!: HomeAssistant;
@property() public panel!: HassioPanelInfo;
@property() public narrow!: boolean;
protected routerOptions: RouterOptions = {
// Hass.io has a page with tabs, so we route all non-matching routes to it.
defaultPage: "dashboard",
initialLoad: () => this._fetchData(),
showLoading: true,
routes: {
dashboard: {
tag: "hassio-panel",
cache: true,
},
snapshots: "dashboard",
store: "dashboard",
system: "dashboard",
addon: {
tag: "hassio-addon-dashboard",
load: () =>
import(
/* webpackChunkName: "hassio-addon-dashboard" */ "./addon-view/hassio-addon-dashboard"
),
},
ingress: {
tag: "hassio-ingress-view",
load: () =>
import(
/* webpackChunkName: "hassio-ingress-view" */ "./ingress-view/hassio-ingress-view"
),
},
},
};
@internalProperty() private _supervisorInfo: HassioSupervisorInfo;
@internalProperty() private _hostInfo: HassioHostInfo;
@internalProperty() private _hassioInfo?: HassioInfo;
@internalProperty() private _hassOsInfo?: HassioHassOSInfo;
@internalProperty() private _hassInfo: HassioHomeAssistantInfo;
protected firstUpdated(changedProps: PropertyValues) {
super.firstUpdated(changedProps);
this.addEventListener("hass-api-called", (ev) => this._apiCalled(ev));
}
protected updatePageEl(el) {
// the tabs page does its own routing so needs full route.
const route = el.nodeName === "HASSIO-PANEL" ? this.route : this.routeTail;
el.hass = this.hass;
el.narrow = this.narrow;
el.supervisorInfo = this._supervisorInfo;
el.hassioInfo = this._hassioInfo;
el.hostInfo = this._hostInfo;
el.hassInfo = this._hassInfo;
el.hassOsInfo = this._hassOsInfo;
el.route = route;
if (el.localName === "hassio-ingress-view") {
el.ingressPanel = this.panel.config && this.panel.config.ingress;
}
}
private async _fetchData() {
if (this.panel.config && this.panel.config.ingress) {
this._redirectIngress(this.panel.config.ingress);
return;
}
const [supervisorInfo, hostInfo, hassInfo, hassioInfo] = await Promise.all([
fetchHassioSupervisorInfo(this.hass),
fetchHassioHostInfo(this.hass),
fetchHassioHomeAssistantInfo(this.hass),
fetchHassioInfo(this.hass),
]);
this._supervisorInfo = supervisorInfo;
this._hassioInfo = hassioInfo;
this._hostInfo = hostInfo;
this._hassInfo = hassInfo;
if (this._hostInfo.features && this._hostInfo.features.includes("hassos")) {
this._hassOsInfo = await fetchHassioHassOsInfo(this.hass);
}
}
private _redirectIngress(addonSlug: string) {
this.route = { prefix: "/hassio", path: `/ingress/${addonSlug}` };
}
private _apiCalled(ev) {
if (!ev.detail.success) {
return;
}
let tries = 1;
const tryUpdate = () => {
this._fetchData().catch(() => {
tries += 1;
setTimeout(tryUpdate, Math.min(tries, 5) * 1000);
});
};
tryUpdate();
}
}
declare global {
interface HTMLElementTagNameMap {
"hassio-router": HassioRouter;
}
}

View File

@@ -1,25 +0,0 @@
import { mdiBackupRestore, mdiCogs, mdiStore, mdiViewDashboard } from "@mdi/js";
import type { PageNavigation } from "../../src/layouts/hass-tabs-subpage";
export const supervisorTabs: PageNavigation[] = [
{
name: "Dashboard",
path: `/hassio/dashboard`,
iconPath: mdiViewDashboard,
},
{
name: "Add-on store",
path: `/hassio/store`,
iconPath: mdiStore,
},
{
name: "Snapshots",
path: `/hassio/snapshots`,
iconPath: mdiBackupRestore,
},
{
name: "System",
path: `/hassio/system`,
iconPath: mdiCogs,
},
];

View File

@@ -5,7 +5,6 @@ import {
html, html,
LitElement, LitElement,
property, property,
internalProperty,
PropertyValues, PropertyValues,
TemplateResult, TemplateResult,
} from "lit-element"; } from "lit-element";
@@ -17,56 +16,29 @@ import { createHassioSession } from "../../../src/data/hassio/supervisor";
import "../../../src/layouts/hass-loading-screen"; import "../../../src/layouts/hass-loading-screen";
import "../../../src/layouts/hass-subpage"; import "../../../src/layouts/hass-subpage";
import { HomeAssistant, Route } from "../../../src/types"; import { HomeAssistant, Route } from "../../../src/types";
import { showAlertDialog } from "../../../src/dialogs/generic/show-dialog-box";
import { navigate } from "../../../src/common/navigate";
import { mdiMenu } from "@mdi/js";
import { fireEvent } from "../../../src/common/dom/fire_event";
@customElement("hassio-ingress-view") @customElement("hassio-ingress-view")
class HassioIngressView extends LitElement { class HassioIngressView extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant; @property() public hass!: HomeAssistant;
@property() public route!: Route; @property() public route!: Route;
@property() public ingressPanel = false; @property() private _addon?: HassioAddonDetails;
@internalProperty() private _addon?: HassioAddonDetails;
@property({ type: Boolean })
public narrow = false;
protected render(): TemplateResult { protected render(): TemplateResult {
if (!this._addon) { if (!this._addon) {
return html` <hass-loading-screen></hass-loading-screen> `; return html` <hass-loading-screen></hass-loading-screen> `;
} }
const iframe = html`<iframe src=${this._addon.ingress_url!}></iframe>`; return html`
<hass-subpage .header=${this._addon.name} hassio>
if (!this.ingressPanel) { <iframe src=${this._addon.ingress_url}></iframe>
return html`<hass-subpage </hass-subpage>
.header=${this._addon.name} `;
.narrow=${this.narrow}
>
${iframe}
</hass-subpage>`;
}
return html`${this.narrow || this.hass.dockedSidebar === "always_hidden"
? html`<div class="header">
<mwc-icon-button
aria-label=${this.hass.localize("ui.sidebar.sidebar_toggle")}
@click=${this._toggleMenu}
>
<ha-svg-icon path=${mdiMenu}></ha-svg-icon>
</mwc-icon-button>
<div class="main-title">${this._addon.name}</div>
</div>
${iframe}`
: iframe}`;
} }
protected updated(changedProps: PropertyValues) { protected updated(changedProps: PropertyValues) {
super.updated(changedProps); super.firstUpdated(changedProps);
if (!changedProps.has("route")) { if (!changedProps.has("route")) {
return; return;
@@ -83,56 +55,27 @@ class HassioIngressView extends LitElement {
} }
private async _fetchData(addonSlug: string) { private async _fetchData(addonSlug: string) {
const createSessionPromise = createHassioSession(this.hass).then(
() => true,
() => false
);
let addon;
try { try {
addon = await fetchHassioAddonInfo(this.hass, addonSlug); const [addon] = await Promise.all([
fetchHassioAddonInfo(this.hass, addonSlug).catch(() => {
throw new Error("Failed to fetch add-on info");
}),
createHassioSession(this.hass).catch(() => {
throw new Error("Failed to create an ingress session");
}),
]);
if (!addon.ingress) {
throw new Error("This add-on does not support ingress");
}
this._addon = addon;
} catch (err) { } catch (err) {
await showAlertDialog(this, { // eslint-disable-next-line
text: "Unable to fetch add-on info to start Ingress", console.error(err);
title: "Supervisor", alert(err.message || "Unknown error starting ingress.");
});
history.back(); history.back();
return;
} }
if (!addon.ingress_url) {
await showAlertDialog(this, {
text: "Add-on does not support Ingress",
title: addon.name,
});
history.back();
return;
}
if (addon.state !== "started") {
await showAlertDialog(this, {
text: "Add-on is not running. Please start it first",
title: addon.name,
});
navigate(this, `/hassio/addon/${addon.slug}/info`, true);
return;
}
if (!(await createSessionPromise)) {
await showAlertDialog(this, {
text: "Unable to create an Ingress session",
title: addon.name,
});
history.back();
return;
}
this._addon = addon;
}
private _toggleMenu(): void {
fireEvent(this, "hass-toggle-menu");
} }
static get styles(): CSSResult { static get styles(): CSSResult {
@@ -143,41 +86,6 @@ class HassioIngressView extends LitElement {
height: 100%; height: 100%;
border: 0; border: 0;
} }
.header + iframe {
height: calc(100% - 40px);
}
.header {
display: flex;
align-items: center;
font-size: 16px;
height: 40px;
padding: 0 16px;
pointer-events: none;
background-color: var(--app-header-background-color);
font-weight: 400;
color: var(--app-header-text-color, white);
border-bottom: var(--app-header-border-bottom, none);
box-sizing: border-box;
--mdc-icon-size: 20px;
}
.main-title {
margin: 0 0 0 24px;
line-height: 20px;
flex-grow: 1;
}
mwc-icon-button {
pointer-events: auto;
}
hass-subpage {
--app-header-background-color: var(--sidebar-background-color);
--app-header-text-color: var(--sidebar-text-color);
--app-header-border-bottom: 1px solid var(--divider-color);
}
`; `;
} }
} }

View File

@@ -15,7 +15,6 @@ import {
html, html,
LitElement, LitElement,
property, property,
internalProperty,
PropertyValues, PropertyValues,
TemplateResult, TemplateResult,
} from "lit-element"; } from "lit-element";
@@ -38,7 +37,7 @@ import { haStyle } from "../../../src/resources/styles";
import { HomeAssistant, Route } from "../../../src/types"; import { HomeAssistant, Route } from "../../../src/types";
import "../components/hassio-card-content"; import "../components/hassio-card-content";
import { showHassioSnapshotDialog } from "../dialogs/snapshot/show-dialog-hassio-snapshot"; import { showHassioSnapshotDialog } from "../dialogs/snapshot/show-dialog-hassio-snapshot";
import { supervisorTabs } from "../hassio-tabs"; import { supervisorTabs } from "../hassio-panel";
import { hassioStyle } from "../resources/hassio-style"; import { hassioStyle } from "../resources/hassio-style";
interface CheckboxItem { interface CheckboxItem {
@@ -57,19 +56,19 @@ class HassioSnapshots extends LitElement {
@property({ attribute: false }) public supervisorInfo!: HassioSupervisorInfo; @property({ attribute: false }) public supervisorInfo!: HassioSupervisorInfo;
@internalProperty() private _snapshotName = ""; @property() private _snapshotName = "";
@internalProperty() private _snapshotPassword = ""; @property() private _snapshotPassword = "";
@internalProperty() private _snapshotHasPassword = false; @property() private _snapshotHasPassword = false;
@internalProperty() private _snapshotType: HassioSnapshot["type"] = "full"; @property() private _snapshotType: HassioSnapshot["type"] = "full";
@internalProperty() private _snapshots?: HassioSnapshot[] = []; @property() private _snapshots?: HassioSnapshot[] = [];
@internalProperty() private _addonList: CheckboxItem[] = []; @property() private _addonList: CheckboxItem[] = [];
@internalProperty() private _folderList: CheckboxItem[] = [ @property() private _folderList: CheckboxItem[] = [
{ {
slug: "homeassistant", slug: "homeassistant",
name: "Home Assistant configuration", name: "Home Assistant configuration",
@@ -80,9 +79,9 @@ class HassioSnapshots extends LitElement {
{ slug: "addons/local", name: "Local add-ons", checked: true }, { slug: "addons/local", name: "Local add-ons", checked: true },
]; ];
@internalProperty() private _creatingSnapshot = false; @property() private _creatingSnapshot = false;
@internalProperty() private _error = ""; @property() private _error = "";
public async refreshData() { public async refreshData() {
await reloadHassioSnapshots(this.hass); await reloadHassioSnapshots(this.hass);

View File

@@ -1,23 +1,17 @@
import "@material/mwc-button"; import "@material/mwc-button";
import "@material/mwc-list/mwc-list-item";
import { ActionDetail } from "@material/mwc-list/mwc-list-foundation";
import { mdiDotsVertical } from "@mdi/js";
import { safeDump } from "js-yaml";
import memoizeOne from "memoize-one";
import { import {
css, css,
CSSResult, CSSResult,
customElement, customElement,
html, html,
internalProperty,
LitElement, LitElement,
property, property,
TemplateResult, TemplateResult,
} from "lit-element"; } from "lit-element";
import "../../../src/components/buttons/ha-call-api-button";
import { fetchHassioHardwareInfo } from "../../../src/data/hassio/hardware";
import { import {
changeHostOptions, changeHostOptions,
configSyncOS,
fetchHassioHostInfo, fetchHassioHostInfo,
HassioHassOSInfo, HassioHassOSInfo,
HassioHostInfo as HassioHostInfoType, HassioHostInfo as HassioHostInfoType,
@@ -25,161 +19,107 @@ import {
shutdownHost, shutdownHost,
updateOS, updateOS,
} from "../../../src/data/hassio/host"; } from "../../../src/data/hassio/host";
import { fetchHassioHardwareInfo } from "../../../src/data/hassio/hardware";
import {
fetchNetworkInfo,
NetworkInfo,
} from "../../../src/data/hassio/network";
import { HassioInfo } from "../../../src/data/hassio/supervisor"; import { HassioInfo } from "../../../src/data/hassio/supervisor";
import { hassioStyle } from "../resources/hassio-style";
import { haStyle } from "../../../src/resources/styles";
import { HomeAssistant } from "../../../src/types";
import { import {
showAlertDialog, showAlertDialog,
showConfirmationDialog, showConfirmationDialog,
showPromptDialog, showPromptDialog,
} from "../../../src/dialogs/generic/show-dialog-box"; } from "../../../src/dialogs/generic/show-dialog-box";
import { haStyle } from "../../../src/resources/styles";
import { HomeAssistant } from "../../../src/types";
import { showHassioMarkdownDialog } from "../dialogs/markdown/show-dialog-hassio-markdown"; import { showHassioMarkdownDialog } from "../dialogs/markdown/show-dialog-hassio-markdown";
import { showNetworkDialog } from "../dialogs/network/show-dialog-network"; import { hassioStyle } from "../resources/hassio-style";
import "../../../src/components/ha-button-menu";
import "../../../src/components/ha-card";
import "../../../src/components/ha-settings-row";
@customElement("hassio-host-info") @customElement("hassio-host-info")
class HassioHostInfo extends LitElement { class HassioHostInfo extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant; @property() public hass!: HomeAssistant;
@property() public hostInfo!: HassioHostInfoType; @property() public hostInfo!: HassioHostInfoType;
@property({ attribute: false }) public hassioInfo!: HassioInfo; @property({ attribute: false }) public hassioInfo!: HassioInfo;
@property({ attribute: false }) public hassOsInfo!: HassioHassOSInfo; @property() public hassOsInfo!: HassioHassOSInfo;
@internalProperty() public _networkInfo?: NetworkInfo; @property() private _errors?: string;
public render(): TemplateResult | void { public render(): TemplateResult | void {
const primaryIpAddress = this.hostInfo.features.includes("network")
? this._primaryIpAddress(this._networkInfo!)
: "";
return html` return html`
<ha-card header="Host System"> <ha-card>
<div class="card-content"> <div class="card-content">
<h2>Host system</h2>
<table class="info">
<tbody>
<tr>
<td>Hostname</td>
<td>${this.hostInfo.hostname}</td>
</tr>
<tr>
<td>System</td>
<td>${this.hostInfo.operating_system}</td>
</tr>
${!this.hostInfo.features.includes("hassos")
? html`<tr>
<td>Docker version</td>
<td>${this.hassioInfo.docker}</td>
</tr>`
: ""}
${this.hostInfo.deployment
? html`
<tr>
<td>Deployment</td>
<td>${this.hostInfo.deployment}</td>
</tr>
`
: ""}
</tbody>
</table>
<mwc-button raised @click=${this._showHardware} class="info">
Hardware
</mwc-button>
${this.hostInfo.features.includes("hostname") ${this.hostInfo.features.includes("hostname")
? html`<ha-settings-row> ? html`
<span slot="heading">
Hostname
</span>
<span slot="description">
${this.hostInfo.hostname}
</span>
<mwc-button <mwc-button
title="Change the hostname" raised
label="Change"
@click=${this._changeHostnameClicked} @click=${this._changeHostnameClicked}
class="info"
> >
Change hostname
</mwc-button> </mwc-button>
</ha-settings-row>` `
: ""} : ""}
${this.hostInfo.features.includes("network") ${this._errors
? html` <ha-settings-row> ? html` <div class="errors">Error: ${this._errors}</div> `
<span slot="heading">
IP address
</span>
<span slot="description">
${primaryIpAddress}
</span>
<mwc-button
title="Change the network"
label="Change"
@click=${this._changeNetworkClicked}
>
</mwc-button>
</ha-settings-row>`
: ""}
<ha-settings-row>
<span slot="heading">
Operating system
</span>
<span slot="description">
${this.hostInfo.operating_system}
</span>
${this.hostInfo.version !== this.hostInfo.version_latest &&
this.hostInfo.features.includes("hassos")
? html`
<mwc-button
title="Update the host OS"
label="Update"
@click=${this._osUpdate}
>
</mwc-button>
`
: ""}
</ha-settings-row>
${!this.hostInfo.features.includes("hassos")
? html`<ha-settings-row>
<span slot="heading">
Docker version
</span>
<span slot="description">
${this.hassioInfo.docker}
</span>
</ha-settings-row>`
: ""}
${this.hostInfo.deployment
? html`<ha-settings-row>
<span slot="heading">
Deployment
</span>
<span slot="description">
${this.hostInfo.deployment}
</span>
</ha-settings-row>`
: ""} : ""}
</div> </div>
<div class="card-actions"> <div class="card-actions">
${this.hostInfo.features.includes("reboot") ${this.hostInfo.features.includes("reboot")
? html` ? html`
<mwc-button <mwc-button class="warning" @click=${this._rebootHost}
title="Reboot the host OS" >Reboot</mwc-button
label="Reboot"
class="warning"
@click=${this._hostReboot}
> >
</mwc-button>
` `
: ""} : ""}
${this.hostInfo.features.includes("shutdown") ${this.hostInfo.features.includes("shutdown")
? html` ? html`
<mwc-button <mwc-button class="warning" @click=${this._shutdownHost}
title="Shutdown the host OS" >Shutdown</mwc-button
label="Shutdown"
class="warning"
@click=${this._hostShutdown}
> >
</mwc-button>
` `
: ""} : ""}
${this.hostInfo.features.includes("hassos")
<ha-button-menu ? html`
corner="BOTTOM_START" <ha-call-api-button
@action=${this._handleMenuAction} class="warning"
> .hass=${this.hass}
<mwc-icon-button slot="trigger"> path="hassio/os/config/sync"
<ha-svg-icon .path=${mdiDotsVertical}></ha-svg-icon>
</mwc-icon-button>
<mwc-list-item title="Show a list of hardware">
Hardware
</mwc-list-item>
${this.hostInfo.features.includes("hassos")
? html`<mwc-list-item
title="Load HassOS configs or updates from USB" title="Load HassOS configs or updates from USB"
>Import from USB</ha-call-api-button
> >
Import from USB `
</mwc-list-item>` : ""}
: ""} ${this.hostInfo.version !== this.hostInfo.version_latest
</ha-button-menu> ? html` <mwc-button @click=${this._updateOS}>Update</mwc-button> `
: ""}
</div> </div>
</ha-card> </ha-card>
`; `;
@@ -192,96 +132,72 @@ class HassioHostInfo extends LitElement {
css` css`
ha-card { ha-card {
height: 100%; height: 100%;
justify-content: space-between;
flex-direction: column;
display: flex;
}
.card-actions {
height: 48px;
border-top: none;
display: flex;
justify-content: space-between;
align-items: center;
}
ha-settings-row {
padding: 0;
height: 54px;
width: 100%; width: 100%;
} }
ha-settings-row[three-line] { .card-content {
height: 74px; color: var(--primary-text-color);
box-sizing: border-box;
height: calc(100% - 47px);
} }
ha-settings-row > span[slot="description"] { .info {
white-space: normal; width: 100%;
color: var(--secondary-text-color); }
.info td:nth-child(2) {
text-align: right;
}
.errors {
color: var(--google-red-500);
margin-top: 16px;
}
mwc-button.info {
max-width: calc(50% - 12px);
}
table.info {
margin-bottom: 10px;
} }
.warning { .warning {
--mdc-theme-primary: var(--error-color); --mdc-theme-primary: var(--google-red-500);
}
ha-button-menu {
color: var(--secondary-text-color);
--mdc-menu-min-width: 200px;
}
@media (min-width: 563px) {
paper-listbox {
max-height: 150px;
overflow: auto;
}
}
paper-item {
cursor: pointer;
min-height: 35px;
}
mwc-list-item ha-svg-icon {
color: var(--secondary-text-color);
} }
`, `,
]; ];
} }
protected firstUpdated(): void { protected firstUpdated(): void {
this._loadData(); this.addEventListener("hass-api-called", (ev) => this._apiCalled(ev));
} }
private _primaryIpAddress = memoizeOne((network_info: NetworkInfo) => { private _apiCalled(ev): void {
if (!network_info) { if (ev.detail.success) {
return ""; this._errors = undefined;
return;
} }
return Object.keys(network_info?.interfaces)
.map((device) => network_info.interfaces[device])
.find((device) => device.primary)?.ip_address;
});
private async _handleMenuAction(ev: CustomEvent<ActionDetail>) { const response = ev.detail.response;
switch (ev.detail.index) {
case 0: this._errors =
await this._showHardware(); typeof response.body === "object"
break; ? response.body.message || "Unknown error"
case 1: : response.body;
await this._importFromUSB();
break;
}
} }
private async _showHardware(): Promise<void> { private async _showHardware(): Promise<void> {
try { try {
const content = await fetchHassioHardwareInfo(this.hass); const content = this._objectToMarkdown(
await fetchHassioHardwareInfo(this.hass)
);
showHassioMarkdownDialog(this, { showHassioMarkdownDialog(this, {
title: "Hardware", title: "Hardware",
content: `<pre>${safeDump(content, { indent: 2 })}</pre>`, content,
}); });
} catch (err) { } catch (err) {
showAlertDialog(this, { showHassioMarkdownDialog(this, {
title: "Failed to get Hardware list", title: "Hardware",
text: content: "Error getting hardware info",
typeof err === "object" ? err.body?.message || "Unkown error" : err,
}); });
} }
} }
private async _hostReboot(): Promise<void> { private async _rebootHost(): Promise<void> {
const confirmed = await showConfirmationDialog(this, { const confirmed = await showConfirmationDialog(this, {
title: "Reboot", title: "Reboot",
text: "Are you sure you want to reboot the host?", text: "Are you sure you want to reboot the host?",
@@ -298,13 +214,12 @@ class HassioHostInfo extends LitElement {
} catch (err) { } catch (err) {
showAlertDialog(this, { showAlertDialog(this, {
title: "Failed to reboot", title: "Failed to reboot",
text: text: err.body.message,
typeof err === "object" ? err.body?.message || "Unkown error" : err,
}); });
} }
} }
private async _hostShutdown(): Promise<void> { private async _shutdownHost(): Promise<void> {
const confirmed = await showConfirmationDialog(this, { const confirmed = await showConfirmationDialog(this, {
title: "Shutdown", title: "Shutdown",
text: "Are you sure you want to shutdown the host?", text: "Are you sure you want to shutdown the host?",
@@ -321,13 +236,12 @@ class HassioHostInfo extends LitElement {
} catch (err) { } catch (err) {
showAlertDialog(this, { showAlertDialog(this, {
title: "Failed to shutdown", title: "Failed to shutdown",
text: text: err.body.message,
typeof err === "object" ? err.body?.message || "Unkown error" : err,
}); });
} }
} }
private async _osUpdate(): Promise<void> { private async _updateOS(): Promise<void> {
const confirmed = await showConfirmationDialog(this, { const confirmed = await showConfirmationDialog(this, {
title: "Update", title: "Update",
text: "Are you sure you want to update the OS?", text: "Are you sure you want to update the OS?",
@@ -344,17 +258,30 @@ class HassioHostInfo extends LitElement {
} catch (err) { } catch (err) {
showAlertDialog(this, { showAlertDialog(this, {
title: "Failed to update", title: "Failed to update",
text: text: err.body.message,
typeof err === "object" ? err.body?.message || "Unkown error" : err,
}); });
} }
} }
private async _changeNetworkClicked(): Promise<void> { private _objectToMarkdown(obj, indent = ""): string {
showNetworkDialog(this, { let data = "";
network: this._networkInfo!, Object.keys(obj).forEach((key) => {
loadData: () => this._loadData(), if (typeof obj[key] !== "object") {
data += `${indent}- ${key}: ${obj[key]}\n`;
} else {
data += `${indent}- ${key}:\n`;
if (Array.isArray(obj[key])) {
if (obj[key].length) {
data +=
`${indent} - ` + obj[key].join(`\n${indent} - `) + "\n";
}
} else {
data += this._objectToMarkdown(obj[key], ` ${indent}`);
}
}
}); });
return data;
} }
private async _changeHostnameClicked(): Promise<void> { private async _changeHostnameClicked(): Promise<void> {
@@ -373,29 +300,11 @@ class HassioHostInfo extends LitElement {
} catch (err) { } catch (err) {
showAlertDialog(this, { showAlertDialog(this, {
title: "Setting hostname failed", title: "Setting hostname failed",
text: text: err.body.message,
typeof err === "object" ? err.body?.message || "Unkown error" : err,
}); });
} }
} }
} }
private async _importFromUSB(): Promise<void> {
try {
await configSyncOS(this.hass);
this.hostInfo = await fetchHassioHostInfo(this.hass);
} catch (err) {
showAlertDialog(this, {
title: "Failed to import from USB",
text:
typeof err === "object" ? err.body?.message || "Unkown error" : err,
});
}
}
private async _loadData(): Promise<void> {
this._networkInfo = await fetchNetworkInfo(this.hass);
}
} }
declare global { declare global {

View File

@@ -8,137 +8,89 @@ import {
property, property,
TemplateResult, TemplateResult,
} from "lit-element"; } from "lit-element";
import { fireEvent } from "../../../src/common/dom/fire_event";
import { HassioHostInfo as HassioHostInfoType } from "../../../src/data/hassio/host"; import "../../../src/components/buttons/ha-call-api-button";
import { hassioStyle } from "../resources/hassio-style"; import "../../../src/components/ha-card";
import { haStyle } from "../../../src/resources/styles";
import { HomeAssistant } from "../../../src/types";
import { import {
HassioSupervisorInfo as HassioSupervisorInfoType, HassioSupervisorInfo as HassioSupervisorInfoType,
reloadSupervisor,
setSupervisorOption, setSupervisorOption,
SupervisorOptions, SupervisorOptions,
updateSupervisor,
} from "../../../src/data/hassio/supervisor"; } from "../../../src/data/hassio/supervisor";
import { import { showConfirmationDialog } from "../../../src/dialogs/generic/show-dialog-box";
showAlertDialog, import { haStyle } from "../../../src/resources/styles";
showConfirmationDialog, import { HomeAssistant } from "../../../src/types";
} from "../../../src/dialogs/generic/show-dialog-box"; import { hassioStyle } from "../resources/hassio-style";
import "../../../src/components/ha-card";
import "../../../src/components/ha-settings-row";
import "../../../src/components/ha-switch";
@customElement("hassio-supervisor-info") @customElement("hassio-supervisor-info")
class HassioSupervisorInfo extends LitElement { class HassioSupervisorInfo extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant; @property() public hass!: HomeAssistant;
@property() public supervisorInfo!: HassioSupervisorInfoType; @property() public supervisorInfo!: HassioSupervisorInfoType;
@property() public hostInfo!: HassioHostInfoType; @property() private _errors?: string;
public render(): TemplateResult | void { public render(): TemplateResult | void {
return html` return html`
<ha-card header="Supervisor"> <ha-card>
<div class="card-content"> <div class="card-content">
<ha-settings-row> <h2>Supervisor</h2>
<span slot="heading"> <table class="info">
Version <tbody>
</span> <tr>
<span slot="description"> <td>Version</td>
${this.supervisorInfo.version} <td>${this.supervisorInfo.version}</td>
</span> </tr>
</ha-settings-row> <tr>
<ha-settings-row> <td>Latest version</td>
<span slot="heading"> <td>${this.supervisorInfo.version_latest}</td>
Newest version </tr>
</span> ${this.supervisorInfo.channel !== "stable"
<span slot="description"> ? html`
${this.supervisorInfo.version_latest} <tr>
</span> <td>Channel</td>
${this.supervisorInfo.version !== this.supervisorInfo.version_latest <td>${this.supervisorInfo.channel}</td>
? html` </tr>
<mwc-button `
title="Update the supervisor" : ""}
label="Update" </tbody>
@click=${this._supervisorUpdate} </table>
> ${this._errors
</mwc-button> ? html` <div class="errors">Error: ${this._errors}</div> `
` : ""}
: ""}
</ha-settings-row>
<ha-settings-row>
<span slot="heading">
Channel
</span>
<span slot="description">
${this.supervisorInfo.channel}
</span>
${this.supervisorInfo.channel === "beta"
? html`
<mwc-button
@click=${this._toggleBeta}
label="Leave beta channel"
title="Get stable updates for Home Assistant, supervisor and host"
>
</mwc-button>
`
: this.supervisorInfo.channel === "stable"
? html`
<mwc-button
@click=${this._toggleBeta}
label="Join beta channel"
title="Get beta updates for Home Assistant (RCs), supervisor and host"
>
</mwc-button>
`
: ""}
</ha-settings-row>
${this.supervisorInfo?.supported
? html` <ha-settings-row three-line>
<span slot="heading">
Share diagnostics
</span>
<div slot="description" class="diagnostics-description">
Share crash reports and diagnostic information.
<button
class="link"
title="Show more information about this"
@click=${this._diagnosticsInformationDialog}
>
Learn more
</button>
</div>
<ha-switch
haptic
.checked=${this.supervisorInfo.diagnostics}
@change=${this._toggleDiagnostics}
></ha-switch>
</ha-settings-row>`
: html`<div class="error">
You are running an unsupported installation.
<a
href="https://github.com/home-assistant/architecture/blob/master/adr/${this.hostInfo.features.includes(
"hassos"
)
? "0015-home-assistant-os.md"
: "0014-home-assistant-supervised.md"}"
target="_blank"
rel="noreferrer"
title="Learn more about how you can make your system compliant"
>
Learn More
</a>
</div>`}
</div> </div>
<div class="card-actions"> <div class="card-actions">
<mwc-button <ha-call-api-button .hass=${this.hass} path="hassio/supervisor/reload"
@click=${this._supervisorReload} >Reload</ha-call-api-button
title="Reload parts of the supervisor."
label="Reload"
> >
</mwc-button> ${this.supervisorInfo.version !== this.supervisorInfo.version_latest
? html`
<ha-call-api-button
.hass=${this.hass}
path="hassio/supervisor/update"
>Update</ha-call-api-button
>
`
: ""}
${this.supervisorInfo.channel === "beta"
? html`
<ha-call-api-button
.hass=${this.hass}
path="hassio/supervisor/options"
.data=${{ channel: "stable" }}
>Leave beta channel</ha-call-api-button
>
`
: ""}
${this.supervisorInfo.channel === "stable"
? html`
<mwc-button
@click=${this._joinBeta}
class="warning"
title="Get beta updates for Home Assistant (RCs), supervisor and host"
>Join beta channel</mwc-button
>
`
: ""}
</div> </div>
</ha-card> </ha-card>
`; `;
@@ -151,129 +103,81 @@ class HassioSupervisorInfo extends LitElement {
css` css`
ha-card { ha-card {
height: 100%; height: 100%;
justify-content: space-between;
flex-direction: column;
display: flex;
}
.card-actions {
height: 48px;
border-top: none;
display: flex;
justify-content: space-between;
align-items: center;
}
button.link {
color: var(--primary-color);
}
ha-settings-row {
padding: 0;
height: 54px;
width: 100%; width: 100%;
} }
ha-settings-row[three-line] { .card-content {
height: 74px; color: var(--primary-text-color);
box-sizing: border-box;
height: calc(100% - 47px);
} }
ha-settings-row > span[slot="description"] { .info {
white-space: normal; width: 100%;
color: var(--secondary-text-color); }
.info td:nth-child(2) {
text-align: right;
}
.errors {
color: var(--google-red-500);
margin-top: 16px;
} }
`, `,
]; ];
} }
private async _toggleBeta(): Promise<void> { protected firstUpdated(): void {
if (this.supervisorInfo.channel === "stable") { this.addEventListener("hass-api-called", (ev) => this._apiCalled(ev));
const confirmed = await showConfirmationDialog(this, {
title: "WARNING",
text: html` Beta releases are for testers and early adopters and can
contain unstable code changes.
<br />
<b>
Make sure you have backups of your data before you activate this
feature.
</b>
<br /><br />
This includes beta releases for:
<li>Home Assistant Core</li>
<li>Home Assistant Supervisor</li>
<li>Home Assistant Operating System</li>
<br />
Do you want to join the beta channel?`,
confirmText: "join beta",
dismissText: "no",
});
if (!confirmed) {
return;
}
}
try {
const data: Partial<SupervisorOptions> = {
channel: this.supervisorInfo.channel !== "stable" ? "beta" : "stable",
};
await setSupervisorOption(this.hass, data);
await reloadSupervisor(this.hass);
} catch (err) {
showAlertDialog(this, {
title: "Failed to set supervisor option",
text:
typeof err === "object" ? err.body?.message || "Unkown error" : err,
});
}
} }
private async _supervisorReload(): Promise<void> { private _apiCalled(ev): void {
try { if (ev.detail.success) {
await reloadSupervisor(this.hass); this._errors = undefined;
} catch (err) { return;
showAlertDialog(this, {
title: "Failed to reload the supervisor",
text:
typeof err === "object" ? err.body?.message || "Unkown error" : err,
});
} }
const response = ev.detail.response;
this._errors =
typeof response.body === "object"
? response.body.message || "Unknown error"
: response.body;
} }
private async _supervisorUpdate(): Promise<void> { private async _joinBeta() {
try { const confirmed = await showConfirmationDialog(this, {
await updateSupervisor(this.hass); title: "WARNING",
} catch (err) { text: html` Beta releases are for testers and early adopters and can
showAlertDialog(this, { contain unstable code changes.
title: "Failed to update the supervisor", <br />
text: <b>
typeof err === "object" ? err.body.message || "Unkown error" : err, Make sure you have backups of your data before you activate this
}); feature.
} </b>
}
private async _diagnosticsInformationDialog(): Promise<void> {
await showAlertDialog(this, {
title: "Help Improve Home Assistant",
text: html`Would you want to automatically share crash reports and
diagnostic information when the supervisor encounters unexpected errors?
<br /><br /> <br /><br />
This will allow us to fix the problems, the information is only This includes beta releases for:
accessible to the Home Assistant Core team and will not be shared with <li>Home Assistant Core</li>
others. <li>Home Assistant Supervisor</li>
<br /><br /> <li>Home Assistant Operating System</li>
The data does not include any private/sensitive information and you can <br />
disable this in settings at any time you want.`, Do you want to join the beta channel?`,
confirmText: "join beta",
dismissText: "no",
}); });
}
private async _toggleDiagnostics(): Promise<void> { if (!confirmed) {
return;
}
try { try {
const data: SupervisorOptions = { const data: SupervisorOptions = { channel: "beta" };
diagnostics: !this.supervisorInfo?.diagnostics,
};
await setSupervisorOption(this.hass, data); await setSupervisorOption(this.hass, data);
const eventdata = {
success: true,
response: undefined,
path: "option",
};
fireEvent(this, "hass-api-called", eventdata);
} catch (err) { } catch (err) {
showAlertDialog(this, { this._errors = `Error joining beta channel, ${err.body?.message || err}`;
title: "Failed to set supervisor option",
text:
typeof err === "object" ? err.body.message || "Unkown error" : err,
});
} }
} }
} }

View File

@@ -7,20 +7,17 @@ import {
CSSResult, CSSResult,
customElement, customElement,
html, html,
internalProperty,
LitElement, LitElement,
property, property,
TemplateResult, TemplateResult,
} from "lit-element"; } from "lit-element";
import "../../../src/components/ha-card";
import { fetchHassioLogs } from "../../../src/data/hassio/supervisor"; import { fetchHassioLogs } from "../../../src/data/hassio/supervisor";
import { hassioStyle } from "../resources/hassio-style"; import "../../../src/layouts/loading-screen";
import { haStyle } from "../../../src/resources/styles"; import { haStyle } from "../../../src/resources/styles";
import { HomeAssistant } from "../../../src/types"; import { HomeAssistant } from "../../../src/types";
import "../../../src/components/ha-card";
import "../../../src/layouts/hass-loading-screen";
import "../components/hassio-ansi-to-html"; import "../components/hassio-ansi-to-html";
import { hassioStyle } from "../resources/hassio-style";
interface LogProvider { interface LogProvider {
key: string; key: string;
@@ -58,11 +55,11 @@ const logProviders: LogProvider[] = [
class HassioSupervisorLog extends LitElement { class HassioSupervisorLog extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant; @property({ attribute: false }) public hass!: HomeAssistant;
@internalProperty() private _error?: string; @property() private _error?: string;
@internalProperty() private _selectedLogProvider = "supervisor"; @property() private _selectedLogProvider = "supervisor";
@internalProperty() private _content?: string; @property() private _content?: string;
public async connectedCallback(): Promise<void> { public async connectedCallback(): Promise<void> {
super.connectedCallback(); super.connectedCallback();
@@ -101,10 +98,10 @@ class HassioSupervisorLog extends LitElement {
? html`<hassio-ansi-to-html ? html`<hassio-ansi-to-html
.content=${this._content} .content=${this._content}
></hassio-ansi-to-html>` ></hassio-ansi-to-html>`
: html`<hass-loading-screen no-toolbar></hass-loading-screen>`} : html`<loading-screen></loading-screen>`}
</div> </div>
<div class="card-actions"> <div class="card-actions">
<mwc-button @click=${this._loadData}>Refresh</mwc-button> <mwc-button @click=${this._refresh}>Refresh</mwc-button>
</div> </div>
</ha-card> </ha-card>
`; `;
@@ -116,7 +113,6 @@ class HassioSupervisorLog extends LitElement {
hassioStyle, hassioStyle,
css` css`
ha-card { ha-card {
margin-top: 8px;
width: 100%; width: 100%;
} }
pre { pre {
@@ -127,9 +123,12 @@ class HassioSupervisorLog extends LitElement {
width: 96%; width: 96%;
} }
.errors { .errors {
color: var(--error-color); color: var(--google-red-500);
margin-bottom: 16px; margin-bottom: 16px;
} }
.card-content {
padding-top: 0px;
}
`, `,
]; ];
} }
@@ -142,6 +141,7 @@ class HassioSupervisorLog extends LitElement {
private async _loadData(): Promise<void> { private async _loadData(): Promise<void> {
this._error = undefined; this._error = undefined;
this._content = undefined;
try { try {
this._content = await fetchHassioLogs( this._content = await fetchHassioLogs(
@@ -150,10 +150,14 @@ class HassioSupervisorLog extends LitElement {
); );
} catch (err) { } catch (err) {
this._error = `Failed to get supervisor logs, ${ this._error = `Failed to get supervisor logs, ${
typeof err === "object" ? err.body?.message || "Unkown error" : err err.body?.message || err
}`; }`;
} }
} }
private async _refresh(): Promise<void> {
await this._loadData();
}
} }
declare global { declare global {

View File

@@ -18,7 +18,7 @@ import {
import "../../../src/layouts/hass-tabs-subpage"; import "../../../src/layouts/hass-tabs-subpage";
import { haStyle } from "../../../src/resources/styles"; import { haStyle } from "../../../src/resources/styles";
import { HomeAssistant, Route } from "../../../src/types"; import { HomeAssistant, Route } from "../../../src/types";
import { supervisorTabs } from "../hassio-tabs"; import { supervisorTabs } from "../hassio-panel";
import { hassioStyle } from "../resources/hassio-style"; import { hassioStyle } from "../resources/hassio-style";
import "./hassio-host-info"; import "./hassio-host-info";
import "./hassio-supervisor-info"; import "./hassio-supervisor-info";
@@ -52,10 +52,10 @@ class HassioSystem extends LitElement {
> >
<span slot="header">System</span> <span slot="header">System</span>
<div class="content"> <div class="content">
<h1>Information</h1>
<div class="card-group"> <div class="card-group">
<hassio-supervisor-info <hassio-supervisor-info
.hass=${this.hass} .hass=${this.hass}
.hostInfo=${this.hostInfo}
.supervisorInfo=${this.supervisorInfo} .supervisorInfo=${this.supervisorInfo}
></hassio-supervisor-info> ></hassio-supervisor-info>
<hassio-host-info <hassio-host-info
@@ -65,6 +65,7 @@ class HassioSystem extends LitElement {
.hassOsInfo=${this.hassOsInfo} .hassOsInfo=${this.hassOsInfo}
></hassio-host-info> ></hassio-host-info>
</div> </div>
<h1>System log</h1>
<hassio-supervisor-log .hass=${this.hass}></hassio-supervisor-log> <hassio-supervisor-log .hass=${this.hass}></hassio-supervisor-log>
</div> </div>
</hass-tabs-subpage> </hass-tabs-subpage>

View File

@@ -13,7 +13,7 @@
"lint:prettier": "prettier '**/src/**/*.{js,ts,json,css,md}' --check", "lint:prettier": "prettier '**/src/**/*.{js,ts,json,css,md}' --check",
"format:prettier": "prettier '**/src/**/*.{js,ts,json,css,md}' --write", "format:prettier": "prettier '**/src/**/*.{js,ts,json,css,md}' --write",
"lint:types": "tsc", "lint:types": "tsc",
"lint:lit": "lit-analyzer '**/src/**/*.ts' --format markdown --outFile result.md", "lint:lit": "lit-analyzer '**/src/**/*.ts'",
"lint": "npm run lint:eslint && npm run lint:prettier && npm run lint:types", "lint": "npm run lint:eslint && npm run lint:prettier && npm run lint:types",
"format": "npm run format:eslint && npm run format:prettier", "format": "npm run format:eslint && npm run format:prettier",
"mocha": "node_modules/.bin/ts-mocha -p test-mocha/tsconfig.test.json --opts test-mocha/mocha.opts", "mocha": "node_modules/.bin/ts-mocha -p test-mocha/tsconfig.test.json --opts test-mocha/mocha.opts",
@@ -22,30 +22,23 @@
"author": "Paulus Schoutsen <Paulus@PaulusSchoutsen.nl> (http://paulusschoutsen.nl)", "author": "Paulus Schoutsen <Paulus@PaulusSchoutsen.nl> (http://paulusschoutsen.nl)",
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"~app": "file:./src",
"@formatjs/intl-pluralrules": "^1.5.8", "@formatjs/intl-pluralrules": "^1.5.8",
"@fullcalendar/common": "5.1.0", "@fullcalendar/core": "^5.0.0-beta.2",
"@fullcalendar/core": "5.1.0", "@fullcalendar/daygrid": "^5.0.0-beta.2",
"@fullcalendar/daygrid": "5.1.0", "@material/chips": "7.0.0-canary.d92d8c93e.0",
"@fullcalendar/interaction": "5.1.0", "@material/mwc-button": "^0.15.0",
"@fullcalendar/list": "5.1.0", "@material/mwc-checkbox": "^0.15.0",
"@material/chips": "=8.0.0-canary.096a7a066.0", "@material/mwc-dialog": "^0.15.0",
"@material/circular-progress": "=8.0.0-canary.a78ceb112.0", "@material/mwc-fab": "^0.15.0",
"@material/mwc-button": "^0.18.0", "@material/mwc-formfield": "^0.15.0",
"@material/mwc-checkbox": "^0.18.0", "@material/mwc-icon-button": "^0.15.0",
"@material/mwc-dialog": "^0.18.0", "@material/mwc-list": "^0.15.0",
"@material/mwc-fab": "^0.18.0", "@material/mwc-menu": "^0.15.0",
"@material/mwc-formfield": "^0.18.0", "@material/mwc-ripple": "^0.15.0",
"@material/mwc-icon-button": "^0.18.0", "@material/mwc-switch": "^0.15.0",
"@material/mwc-list": "^0.18.0", "@mdi/js": "4.9.95",
"@material/mwc-menu": "^0.18.0", "@mdi/svg": "4.9.95",
"@material/mwc-radio": "^0.18.0",
"@material/mwc-ripple": "^0.18.0",
"@material/mwc-switch": "^0.18.0",
"@material/mwc-tab": "^0.18.0",
"@material/mwc-tab-bar": "^0.18.0",
"@material/top-app-bar": "=8.0.0-canary.096a7a066.0",
"@mdi/js": "5.5.55",
"@mdi/svg": "5.5.55",
"@polymer/app-layout": "^3.0.2", "@polymer/app-layout": "^3.0.2",
"@polymer/app-route": "^3.0.2", "@polymer/app-route": "^3.0.2",
"@polymer/app-storage": "^3.0.2", "@polymer/app-storage": "^3.0.2",
@@ -55,6 +48,7 @@
"@polymer/iron-image": "^3.0.1", "@polymer/iron-image": "^3.0.1",
"@polymer/iron-input": "^3.0.1", "@polymer/iron-input": "^3.0.1",
"@polymer/iron-label": "^3.0.1", "@polymer/iron-label": "^3.0.1",
"@polymer/iron-media-query": "^3.0.1",
"@polymer/iron-overlay-behavior": "^3.0.2", "@polymer/iron-overlay-behavior": "^3.0.2",
"@polymer/iron-resizable-behavior": "^3.0.1", "@polymer/iron-resizable-behavior": "^3.0.1",
"@polymer/paper-card": "^3.0.1", "@polymer/paper-card": "^3.0.1",
@@ -72,13 +66,13 @@
"@polymer/paper-radio-group": "^3.0.1", "@polymer/paper-radio-group": "^3.0.1",
"@polymer/paper-ripple": "^3.0.1", "@polymer/paper-ripple": "^3.0.1",
"@polymer/paper-slider": "^3.0.1", "@polymer/paper-slider": "^3.0.1",
"@polymer/paper-spinner": "^3.0.2",
"@polymer/paper-styles": "^3.0.1", "@polymer/paper-styles": "^3.0.1",
"@polymer/paper-tabs": "^3.0.1", "@polymer/paper-tabs": "^3.0.1",
"@polymer/paper-toast": "^3.0.1", "@polymer/paper-toast": "^3.0.1",
"@polymer/paper-tooltip": "^3.0.1", "@polymer/paper-tooltip": "^3.0.1",
"@polymer/polymer": "3.1.0", "@polymer/polymer": "3.1.0",
"@thomasloven/round-slider": "0.5.0", "@thomasloven/round-slider": "0.5.0",
"@types/chromecast-caf-sender": "^1.0.3",
"@vaadin/vaadin-combo-box": "^5.0.10", "@vaadin/vaadin-combo-box": "^5.0.10",
"@vaadin/vaadin-date-picker": "^4.0.7", "@vaadin/vaadin-date-picker": "^4.0.7",
"@vue/web-component-wrapper": "^1.2.0", "@vue/web-component-wrapper": "^1.2.0",
@@ -88,15 +82,14 @@
"codemirror": "^5.49.0", "codemirror": "^5.49.0",
"comlink": "^4.3.0", "comlink": "^4.3.0",
"cpx": "^1.5.0", "cpx": "^1.5.0",
"cropperjs": "^1.5.7",
"deep-clone-simple": "^1.1.1", "deep-clone-simple": "^1.1.1",
"deep-freeze": "^0.0.1", "deep-freeze": "^0.0.1",
"es6-object-assign": "^1.1.0", "es6-object-assign": "^1.1.0",
"fecha": "^4.2.0", "fecha": "^4.2.0",
"fuse.js": "^6.0.0", "fuse.js": "^6.0.0",
"google-timezones-json": "^1.0.2", "google-timezones-json": "^1.0.2",
"hls.js": "^0.13.2", "hls.js": "^0.12.4",
"home-assistant-js-websocket": "^5.4.1", "home-assistant-js-websocket": "^5.3.0",
"idb-keyval": "^3.2.0", "idb-keyval": "^3.2.0",
"intl-messageformat": "^8.3.9", "intl-messageformat": "^8.3.9",
"js-yaml": "^3.13.1", "js-yaml": "^3.13.1",
@@ -105,16 +98,15 @@
"lit-element": "^2.3.1", "lit-element": "^2.3.1",
"lit-html": "^1.2.1", "lit-html": "^1.2.1",
"lit-virtualizer": "^0.4.2", "lit-virtualizer": "^0.4.2",
"marked": "^1.1.1", "marked": "^0.6.1",
"mdn-polyfills": "^5.16.0", "mdn-polyfills": "^5.16.0",
"memoize-one": "^5.0.2", "memoize-one": "^5.0.2",
"node-vibrant": "^3.1.5", "node-vibrant": "^3.1.5",
"proxy-polyfill": "^0.3.1", "proxy-polyfill": "^0.3.1",
"punycode": "^2.1.1",
"regenerator-runtime": "^0.13.2", "regenerator-runtime": "^0.13.2",
"resize-observer-polyfill": "^1.5.1", "resize-observer-polyfill": "^1.5.1",
"roboto-fontface": "^0.10.0", "roboto-fontface": "^0.10.0",
"superstruct": "^0.10.12", "superstruct": "^0.6.1",
"unfetch": "^4.1.0", "unfetch": "^4.1.0",
"vue": "^2.6.11", "vue": "^2.6.11",
"vue2-daterange-picker": "^0.5.1", "vue2-daterange-picker": "^0.5.1",
@@ -142,14 +134,13 @@
"@rollup/plugin-replace": "^2.3.2", "@rollup/plugin-replace": "^2.3.2",
"@types/chai": "^4.1.7", "@types/chai": "^4.1.7",
"@types/chromecast-caf-receiver": "^3.0.12", "@types/chromecast-caf-receiver": "^3.0.12",
"@types/codemirror": "^0.0.97", "@types/codemirror": "^0.0.78",
"@types/hls.js": "^0.12.3", "@types/hls.js": "^0.12.3",
"@types/js-yaml": "^3.12.1", "@types/js-yaml": "^3.12.1",
"@types/leaflet": "^1.4.3", "@types/leaflet": "^1.4.3",
"@types/leaflet-draw": "^1.0.1", "@types/leaflet-draw": "^1.0.1",
"@types/marked": "^1.1.0",
"@types/memoize-one": "4.1.0", "@types/memoize-one": "4.1.0",
"@types/mocha": "^7.0.2", "@types/mocha": "^5.2.6",
"@types/resize-observer-browser": "^0.1.3", "@types/resize-observer-browser": "^0.1.3",
"@types/webspeechapi": "^0.0.29", "@types/webspeechapi": "^0.0.29",
"@typescript-eslint/eslint-plugin": "^2.28.0", "@typescript-eslint/eslint-plugin": "^2.28.0",
@@ -160,7 +151,7 @@
"eslint": "^6.8.0", "eslint": "^6.8.0",
"eslint-config-airbnb-typescript": "^7.2.1", "eslint-config-airbnb-typescript": "^7.2.1",
"eslint-config-prettier": "^6.10.1", "eslint-config-prettier": "^6.10.1",
"eslint-import-resolver-webpack": "^0.12.2", "eslint-import-resolver-webpack": "^0.12.1",
"eslint-plugin-disable": "^2.0.1", "eslint-plugin-disable": "^2.0.1",
"eslint-plugin-import": "^2.20.2", "eslint-plugin-import": "^2.20.2",
"eslint-plugin-lit": "^1.2.0", "eslint-plugin-lit": "^1.2.0",
@@ -178,12 +169,12 @@
"html-minifier": "^4.0.0", "html-minifier": "^4.0.0",
"husky": "^1.3.1", "husky": "^1.3.1",
"lint-staged": "^8.1.5", "lint-staged": "^8.1.5",
"lit-analyzer": "^1.2.0", "lit-analyzer": "^1.1.10",
"lodash.template": "^4.5.0", "lodash.template": "^4.5.0",
"magic-string": "^0.25.7", "magic-string": "^0.25.7",
"map-stream": "^0.0.7", "map-stream": "^0.0.7",
"merge-stream": "^1.0.1", "merge-stream": "^1.0.1",
"mocha": "^7.2.0", "mocha": "^6.0.2",
"object-hash": "^2.0.3", "object-hash": "^2.0.3",
"open": "^7.0.4", "open": "^7.0.4",
"prettier": "^2.0.4", "prettier": "^2.0.4",
@@ -199,8 +190,8 @@
"source-map-url": "^0.4.0", "source-map-url": "^0.4.0",
"systemjs": "^6.3.2", "systemjs": "^6.3.2",
"terser-webpack-plugin": "^3.0.6", "terser-webpack-plugin": "^3.0.6",
"ts-lit-plugin": "^1.2.0", "ts-lit-plugin": "^1.1.10",
"ts-mocha": "^7.0.0", "ts-mocha": "^6.0.0",
"typescript": "^3.8.3", "typescript": "^3.8.3",
"vinyl-buffer": "^1.0.1", "vinyl-buffer": "^1.0.1",
"vinyl-source-stream": "^2.0.0", "vinyl-source-stream": "^2.0.0",
@@ -218,10 +209,19 @@
"@polymer/polymer": "3.1.0", "@polymer/polymer": "3.1.0",
"lit-html": "1.2.1", "lit-html": "1.2.1",
"lit-element": "2.3.1", "lit-element": "2.3.1",
"@material/animation": "8.0.0-canary.096a7a066.0", "@material/animation": "7.0.0-canary.d92d8c93e.0",
"@material/base": "8.0.0-canary.096a7a066.0", "@material/base": "7.0.0-canary.d92d8c93e.0",
"@material/feature-targeting": "8.0.0-canary.096a7a066.0", "@material/checkbox": "7.0.0-canary.d92d8c93e.0",
"@material/theme": "8.0.0-canary.096a7a066.0" "@material/density": "7.0.0-canary.d92d8c93e.0",
"@material/dom": "7.0.0-canary.d92d8c93e.0",
"@material/elevation": "7.0.0-canary.d92d8c93e.0",
"@material/feature-targeting": "7.0.0-canary.d92d8c93e.0",
"@material/ripple": "7.0.0-canary.d92d8c93e.0",
"@material/rtl": "7.0.0-canary.d92d8c93e.0",
"@material/shape": "7.0.0-canary.d92d8c93e.0",
"@material/theme": "7.0.0-canary.d92d8c93e.0",
"@material/touch-target": "7.0.0-canary.d92d8c93e.0",
"@material/typography": "7.0.0-canary.d92d8c93e.0"
}, },
"main": "src/home-assistant.js", "main": "src/home-assistant.js",
"husky": { "husky": {

View File

@@ -16,31 +16,14 @@ function today() {
)}${String(now.getDate()).padStart(2, "0")}.0`; )}${String(now.getDate()).padStart(2, "0")}.0`;
} }
function auto(version) {
const todayVersion = today();
if (todayVersion !== version) {
return todayVersion;
}
return patch(version);
}
const methods = { const methods = {
patch, patch,
today, today,
auto,
}; };
async function main(args) { async function main(args) {
let method; const method = args.length > 0 && methods[args[0]];
let commit; const commit = args.length > 1 && args[1] == "--commit";
if (args.length === 0) {
method = methods.auto;
commit = true;
} else {
method = args.length > 0 && methods[args[0]];
commit = args.length > 1 && args[1] == "--commit";
}
if (!method) { if (!method) {
console.error( console.error(

View File

@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
setup( setup(
name="home-assistant-frontend", name="home-assistant-frontend",
version="20200824.0", version="20200620.0",
description="The Home Assistant frontend", description="The Home Assistant frontend",
url="https://github.com/home-assistant/home-assistant-polymer", url="https://github.com/home-assistant/home-assistant-polymer",
author="The Home Assistant Authors", author="The Home Assistant Authors",

View File

@@ -5,7 +5,6 @@ import {
html, html,
LitElement, LitElement,
property, property,
internalProperty,
PropertyValues, PropertyValues,
TemplateResult, TemplateResult,
} from "lit-element"; } from "lit-element";
@@ -29,13 +28,13 @@ class HaAuthFlow extends litLocalizeLiteMixin(LitElement) {
@property() public oauth2State?: string; @property() public oauth2State?: string;
@internalProperty() private _state: State = "loading"; @property() private _state: State = "loading";
@internalProperty() private _stepData: any = {}; @property() private _stepData: any = {};
@internalProperty() private _step?: DataEntryFlowStep; @property() private _step?: DataEntryFlowStep;
@internalProperty() private _errorMessage?: string; @property() private _errorMessage?: string;
protected render() { protected render() {
return html` return html`

View File

@@ -4,7 +4,6 @@ import {
html, html,
LitElement, LitElement,
property, property,
internalProperty,
PropertyValues, PropertyValues,
} from "lit-element"; } from "lit-element";
import { import {
@@ -16,7 +15,6 @@ import { litLocalizeLiteMixin } from "../mixins/lit-localize-lite-mixin";
import { registerServiceWorker } from "../util/register-service-worker"; import { registerServiceWorker } from "../util/register-service-worker";
import "./ha-auth-flow"; import "./ha-auth-flow";
import { extractSearchParamsObject } from "../common/url/search-params"; import { extractSearchParamsObject } from "../common/url/search-params";
import punycode from "punycode";
import(/* webpackChunkName: "pick-auth-provider" */ "./ha-pick-auth-provider"); import(/* webpackChunkName: "pick-auth-provider" */ "./ha-pick-auth-provider");
@@ -27,9 +25,9 @@ class HaAuthorize extends litLocalizeLiteMixin(LitElement) {
@property() public oauth2State?: string; @property() public oauth2State?: string;
@internalProperty() private _authProvider?: AuthProvider; @property() private _authProvider?: AuthProvider;
@internalProperty() private _authProviders?: AuthProvider[]; @property() private _authProviders?: AuthProvider[];
constructor() { constructor() {
super(); super();
@@ -76,7 +74,7 @@ class HaAuthorize extends litLocalizeLiteMixin(LitElement) {
${this.localize( ${this.localize(
"ui.panel.page-authorize.authorizing_client", "ui.panel.page-authorize.authorizing_client",
"clientId", "clientId",
this.clientId ? punycode.toASCII(this.clientId) : this.clientId this.clientId
)} )}
</p> </p>
${loggingInWith} ${loggingInWith}

View File

@@ -0,0 +1,131 @@
import "@polymer/paper-card/paper-card";
import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element";
import { computeStateName } from "../common/entity/compute_state_name";
import "../components/state-history-charts";
import "../data/ha-state-history-data";
import { EventsMixin } from "../mixins/events-mixin";
/*
* @appliesMixin EventsMixin
*/
class HaHistoryGraphCard extends EventsMixin(PolymerElement) {
static get template() {
return html`
<style>
paper-card:not([dialog]) .content {
padding: 0 16px 16px;
}
paper-card[dialog] {
padding-top: 16px;
background-color: transparent;
}
paper-card {
width: 100%;
/* prevent new stacking context, chart tooltip needs to overflow */
position: static;
}
.header {
@apply --paper-font-headline;
line-height: 40px;
color: var(--primary-text-color);
padding: 20px 16px 12px;
@apply --paper-font-common-nowrap;
}
paper-card[dialog] .header {
display: none;
}
</style>
<ha-state-history-data
hass="[[hass]]"
filter-type="recent-entity"
entity-id="[[computeHistoryEntities(stateObj)]]"
data="{{stateHistory}}"
is-loading="{{stateHistoryLoading}}"
cache-config="[[cacheConfig]]"
></ha-state-history-data>
<paper-card
dialog$="[[inDialog]]"
on-click="cardTapped"
elevation="[[computeElevation(inDialog)]]"
>
<div class="header">[[computeTitle(stateObj)]]</div>
<div class="content">
<state-history-charts
hass="[[hass]]"
history-data="[[stateHistory]]"
is-loading-data="[[stateHistoryLoading]]"
up-to-now
no-single
>
</state-history-charts>
</div>
</paper-card>
`;
}
static get properties() {
return {
hass: Object,
stateObj: {
type: Object,
observer: "stateObjObserver",
},
inDialog: {
type: Boolean,
value: false,
},
stateHistory: Object,
stateHistoryLoading: Boolean,
cacheConfig: {
type: Object,
value: {
refresh: 0,
cacheKey: null,
hoursToShow: 24,
},
},
};
}
stateObjObserver(stateObj) {
if (!stateObj) return;
if (
this.cacheConfig.cacheKey !== stateObj.entity_id ||
this.cacheConfig.refresh !== (stateObj.attributes.refresh || 0) ||
this.cacheConfig.hoursToShow !== (stateObj.attributes.hours_to_show || 24)
) {
this.cacheConfig = {
refresh: stateObj.attributes.refresh || 0,
cacheKey: stateObj.entity_id,
hoursToShow: stateObj.attributes.hours_to_show || 24,
};
}
}
computeTitle(stateObj) {
return computeStateName(stateObj);
}
computeContentClass(inDialog) {
return inDialog ? "" : "content";
}
computeHistoryEntities(stateObj) {
return stateObj.attributes.entity_id;
}
computeElevation(inDialog) {
return inDialog ? 0 : 1;
}
cardTapped(ev) {
const mq = window.matchMedia("(min-width: 610px) and (min-height: 550px)");
if (mq.matches) {
ev.stopPropagation();
this.fire("hass-more-info", { entityId: this.stateObj.entity_id });
}
}
}
customElements.define("ha-history_graph-card", HaHistoryGraphCard);

View File

@@ -1,8 +1,4 @@
/* eslint-disable no-undef, no-console */ /* eslint-disable no-undef, no-console */
import {
CastStateEventData,
SessionStateEventData,
} from "chromecast-caf-receiver/cast.framework";
import { Auth } from "home-assistant-js-websocket"; import { Auth } from "home-assistant-js-websocket";
import { castApiAvailable } from "./cast_framework"; import { castApiAvailable } from "./cast_framework";
import { CAST_APP_ID, CAST_DEV, CAST_NS } from "./const"; import { CAST_APP_ID, CAST_DEV, CAST_NS } from "./const";
@@ -44,13 +40,16 @@ export class CastManager {
const context = this.castContext; const context = this.castContext;
context.setOptions({ context.setOptions({
receiverApplicationId: CAST_APP_ID, receiverApplicationId: CAST_APP_ID,
// @ts-ignore
autoJoinPolicy: chrome.cast.AutoJoinPolicy.ORIGIN_SCOPED, autoJoinPolicy: chrome.cast.AutoJoinPolicy.ORIGIN_SCOPED,
}); });
context.addEventListener( context.addEventListener(
// @ts-ignore
cast.framework.CastContextEventType.SESSION_STATE_CHANGED, cast.framework.CastContextEventType.SESSION_STATE_CHANGED,
(ev) => this._sessionStateChanged(ev) (ev) => this._sessionStateChanged(ev)
); );
context.addEventListener( context.addEventListener(
// @ts-ignore
cast.framework.CastContextEventType.CAST_STATE_CHANGED, cast.framework.CastContextEventType.CAST_STATE_CHANGED,
(ev) => this._castStateChanged(ev) (ev) => this._castStateChanged(ev)
); );
@@ -119,7 +118,7 @@ export class CastManager {
} }
} }
private _sessionStateChanged(ev: SessionStateEventData) { private _sessionStateChanged(ev) {
if (__DEV__) { if (__DEV__) {
console.log("Cast session state changed", ev.sessionState); console.log("Cast session state changed", ev.sessionState);
} }
@@ -142,7 +141,7 @@ export class CastManager {
} }
} }
private _castStateChanged(ev: CastStateEventData) { private _castStateChanged(ev) {
if (__DEV__) { if (__DEV__) {
console.log("Cast state changed", ev.castState); console.log("Cast state changed", ev.castState);
} }

View File

@@ -1,113 +0,0 @@
const expand_hex = (hex: string): string => {
let result = "";
for (const val of hex) {
result += val + val;
}
return result;
};
const rgb_hex = (component: number): string => {
const hex = Math.round(Math.min(Math.max(component, 0), 255)).toString(16);
return hex.length === 1 ? `0${hex}` : hex;
};
// Conversion between HEX and RGB
export const hex2rgb = (hex: string): [number, number, number] => {
hex = hex.replace("#", "");
if (hex.length === 3 || hex.length === 4) {
hex = expand_hex(hex);
}
return [
parseInt(hex.substring(0, 2), 16),
parseInt(hex.substring(2, 4), 16),
parseInt(hex.substring(4, 6), 16),
];
};
export const rgb2hex = (rgb: [number, number, number]): string => {
return `#${rgb_hex(rgb[0])}${rgb_hex(rgb[1])}${rgb_hex(rgb[2])}`;
};
// Conversion between LAB, XYZ and RGB from https://github.com/gka/chroma.js
// Copyright (c) 2011-2019, Gregor Aisch
// Constants for XYZ and LAB conversion
const Xn = 0.95047;
const Yn = 1;
const Zn = 1.08883;
const t0 = 0.137931034; // 4 / 29
const t1 = 0.206896552; // 6 / 29
const t2 = 0.12841855; // 3 * t1 * t1
const t3 = 0.008856452; // t1 * t1 * t1
const rgb_xyz = (r: number) => {
r /= 255;
if (r <= 0.04045) {
return r / 12.92;
}
return ((r + 0.055) / 1.055) ** 2.4;
};
const xyz_lab = (t: number) => {
if (t > t3) {
return t ** (1 / 3);
}
return t / t2 + t0;
};
const xyz_rgb = (r: number) => {
return 255 * (r <= 0.00304 ? 12.92 * r : 1.055 * r ** (1 / 2.4) - 0.055);
};
const lab_xyz = (t: number) => {
return t > t1 ? t * t * t : t2 * (t - t0);
};
// Conversions between RGB and LAB
const rgb2xyz = (rgb: [number, number, number]): [number, number, number] => {
let [r, g, b] = rgb;
r = rgb_xyz(r);
g = rgb_xyz(g);
b = rgb_xyz(b);
const x = xyz_lab((0.4124564 * r + 0.3575761 * g + 0.1804375 * b) / Xn);
const y = xyz_lab((0.2126729 * r + 0.7151522 * g + 0.072175 * b) / Yn);
const z = xyz_lab((0.0193339 * r + 0.119192 * g + 0.9503041 * b) / Zn);
return [x, y, z];
};
export const rgb2lab = (
rgb: [number, number, number]
): [number, number, number] => {
const [x, y, z] = rgb2xyz(rgb);
const l = 116 * y - 16;
return [l < 0 ? 0 : l, 500 * (x - y), 200 * (y - z)];
};
export const lab2rgb = (
lab: [number, number, number]
): [number, number, number] => {
const [l, a, b] = lab;
let y = (l + 16) / 116;
let x = isNaN(a) ? y : y + a / 500;
let z = isNaN(b) ? y : y - b / 200;
y = Yn * lab_xyz(y);
x = Xn * lab_xyz(x);
z = Zn * lab_xyz(z);
const r = xyz_rgb(3.2404542 * x - 1.5371385 * y - 0.4985314 * z); // D65 -> sRGB
const g = xyz_rgb(-0.969266 * x + 1.8760108 * y + 0.041556 * z);
const b_ = xyz_rgb(0.0556434 * x - 0.2040259 * y + 1.0572252 * z);
return [r, g, b_];
};
export const lab2hex = (lab: [number, number, number]): string => {
const rgb = lab2rgb(lab);
return rgb2hex(rgb);
};

View File

@@ -1,16 +0,0 @@
// From https://github.com/gka/chroma.js
// Copyright (c) 2011-2019, Gregor Aisch
export const labDarken = (
lab: [number, number, number],
amount = 1
): [number, number, number] => {
return [lab[0] - 18 * amount, lab[1], lab[2]];
};
export const labBrighten = (
lab: [number, number, number],
amount = 1
): [number, number, number] => {
return labDarken(lab, -amount);
};

View File

@@ -1,24 +0,0 @@
const luminosity = (rgb: [number, number, number]): number => {
// http://www.w3.org/TR/WCAG20/#relativeluminancedef
const lum: [number, number, number] = [0, 0, 0];
for (let i = 0; i < rgb.length; i++) {
const chan = rgb[i] / 255;
lum[i] = chan <= 0.03928 ? chan / 12.92 : ((chan + 0.055) / 1.055) ** 2.4;
}
return 0.2126 * lum[0] + 0.7152 * lum[1] + 0.0722 * lum[2];
};
export const rgbContrast = (
color1: [number, number, number],
color2: [number, number, number]
) => {
const lum1 = luminosity(color1);
const lum2 = luminosity(color2);
if (lum1 > lum2) {
return (lum1 + 0.05) / (lum2 + 0.05);
}
return (lum2 + 0.05) / (lum1 + 0.05);
};

View File

@@ -1,9 +0,0 @@
import { HomeAssistant } from "../../types";
/** Return if a service is loaded. */
export const isServiceLoaded = (
hass: HomeAssistant,
domain: string,
service: string
): boolean =>
hass && domain in hass.services && service in hass.services[domain];

View File

@@ -22,6 +22,7 @@ export const DOMAINS_WITH_CARD = [
"timer", "timer",
"vacuum", "vacuum",
"water_heater", "water_heater",
"weblink",
]; ];
/** Domains with separate more info dialog. */ /** Domains with separate more info dialog. */
@@ -35,7 +36,7 @@ export const DOMAINS_WITH_MORE_INFO = [
"cover", "cover",
"fan", "fan",
"group", "group",
"humidifier", "history_graph",
"input_datetime", "input_datetime",
"light", "light",
"lock", "lock",
@@ -56,10 +57,16 @@ export const DOMAINS_HIDE_MORE_INFO = [
"input_select", "input_select",
"input_text", "input_text",
"scene", "scene",
"weblink",
]; ];
/** Domains that should have the history hidden in the more info dialog. */ /** Domains that should have the history hidden in the more info dialog. */
export const DOMAINS_MORE_INFO_NO_HISTORY = ["camera", "configurator", "scene"]; export const DOMAINS_MORE_INFO_NO_HISTORY = [
"camera",
"configurator",
"history_graph",
"scene",
];
/** States that we consider "off". */ /** States that we consider "off". */
export const STATES_OFF = ["closed", "locked", "off"]; export const STATES_OFF = ["closed", "locked", "off"];
@@ -72,7 +79,6 @@ export const DOMAINS_TOGGLE = new Set([
"switch", "switch",
"group", "group",
"automation", "automation",
"humidifier",
]); ]);
/** Temperature units. */ /** Temperature units. */

View File

@@ -20,29 +20,31 @@ export default function relativeTime(
let delta = (compareTime.getTime() - dateObj.getTime()) / 1000; let delta = (compareTime.getTime() - dateObj.getTime()) / 1000;
const tense = delta >= 0 ? "past" : "future"; const tense = delta >= 0 ? "past" : "future";
delta = Math.abs(delta); delta = Math.abs(delta);
let roundedDelta = Math.round(delta);
if (roundedDelta === 0) { let timeDesc;
return localize("ui.components.relative_time.just_now");
}
let unit = "week";
for (let i = 0; i < tests.length; i++) { for (let i = 0; i < tests.length; i++) {
if (roundedDelta < tests[i]) { if (delta < tests[i]) {
unit = langKey[i]; delta = Math.floor(delta);
timeDesc = localize(
`ui.components.relative_time.duration.${langKey[i]}`,
"count",
delta
);
break; break;
} }
delta /= tests[i]; delta /= tests[i];
roundedDelta = Math.round(delta);
} }
const timeDesc = localize( if (timeDesc === undefined) {
`ui.components.relative_time.duration.${unit}`, delta = Math.floor(delta);
"count", timeDesc = localize(
roundedDelta "ui.components.relative_time.duration.week",
); "count",
delta
);
}
return options.includeTense === false return options.includeTense === false
? timeDesc ? timeDesc

View File

@@ -1,33 +0,0 @@
import type { LitElement } from "lit-element";
import type { ClassElement } from "../../types";
export const restoreScroll = (selector: string): any => {
return (element: ClassElement) => ({
kind: "method",
placement: "prototype",
key: element.key,
descriptor: {
set(this: LitElement, value: number) {
this[`__${String(element.key)}`] = value;
},
get(this: LitElement) {
return this[`__${String(element.key)}`];
},
enumerable: true,
configurable: true,
},
finisher(cls: typeof LitElement) {
const connectedCallback = cls.prototype.connectedCallback;
cls.prototype.connectedCallback = function () {
connectedCallback.call(this);
if (this[element.key]) {
const target = this.renderRoot.querySelector(selector);
if (!target) {
return;
}
target.scrollTop = this[element.key];
}
};
},
});
};

View File

@@ -1,20 +1,26 @@
import { derivedStyles, darkStyles } from "../../resources/styles"; import { derivedStyles } from "../../resources/styles";
import { HomeAssistant, Theme } from "../../types"; import { HomeAssistant, Theme } from "../../types";
import {
hex2rgb,
rgb2hex,
rgb2lab,
lab2rgb,
lab2hex,
} from "../color/convert-color";
import { rgbContrast } from "../color/rgb";
import { labDarken, labBrighten } from "../color/lab";
interface ProcessedTheme { interface ProcessedTheme {
keys: { [key: string]: "" }; keys: { [key: string]: "" };
styles: { [key: string]: string }; styles: { [key: string]: string };
} }
const hexToRgb = (hex: string): string | null => {
const shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
const checkHex = hex.replace(shorthandRegex, (_m, r, g, b) => {
return r + r + g + g + b + b;
});
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(checkHex);
return result
? `${parseInt(result[1], 16)}, ${parseInt(result[2], 16)}, ${parseInt(
result[3],
16
)}`
: null;
};
let PROCESSED_THEMES: { [key: string]: ProcessedTheme } = {}; let PROCESSED_THEMES: { [key: string]: ProcessedTheme } = {};
/** /**
@@ -27,56 +33,17 @@ let PROCESSED_THEMES: { [key: string]: ProcessedTheme } = {};
export const applyThemesOnElement = ( export const applyThemesOnElement = (
element, element,
themes: HomeAssistant["themes"], themes: HomeAssistant["themes"],
selectedTheme?: string, selectedTheme?: string
themeOptions?: Partial<HomeAssistant["selectedTheme"]>
) => { ) => {
let cacheKey = selectedTheme; const newTheme = selectedTheme
let themeRules: Partial<Theme> = {}; ? PROCESSED_THEMES[selectedTheme] || processTheme(selectedTheme, themes)
: undefined;
if (selectedTheme === "default" && themeOptions) { if (!element._themes && !newTheme) {
if (themeOptions.dark) {
cacheKey = `${cacheKey}__dark`;
themeRules = darkStyles;
}
if (themeOptions.primaryColor) {
cacheKey = `${cacheKey}__primary_${themeOptions.primaryColor}`;
const rgbPrimaryColor = hex2rgb(themeOptions.primaryColor);
const labPrimaryColor = rgb2lab(rgbPrimaryColor);
themeRules["primary-color"] = themeOptions.primaryColor;
const rgbLigthPrimaryColor = lab2rgb(labBrighten(labPrimaryColor));
themeRules["light-primary-color"] = rgb2hex(rgbLigthPrimaryColor);
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(rgbLigthPrimaryColor, [33, 33, 33]) < 6
? "#fff"
: "#212121";
themeRules["state-icon-color"] = themeRules["dark-primary-color"];
}
if (themeOptions.accentColor) {
cacheKey = `${cacheKey}__accent_${themeOptions.accentColor}`;
themeRules["accent-color"] = themeOptions.accentColor;
const rgbAccentColor = hex2rgb(themeOptions.accentColor);
themeRules["text-accent-color"] =
rgbContrast(rgbAccentColor, [33, 33, 33]) < 6 ? "#fff" : "#212121";
}
}
if (selectedTheme && themes.themes[selectedTheme]) {
themeRules = themes.themes[selectedTheme];
}
if (!element._themes && !Object.keys(themeRules).length) {
// No styles to reset, and no styles to set // No styles to reset, and no styles to set
return; return;
} }
const newTheme =
themeRules && cacheKey
? PROCESSED_THEMES[cacheKey] || processTheme(cacheKey, themeRules)
: undefined;
// Add previous set keys to reset them, and new theme // Add previous set keys to reset them, and new theme
const styles = { ...element._themes, ...newTheme?.styles }; const styles = { ...element._themes, ...newTheme?.styles };
element._themes = newTheme?.keys; element._themes = newTheme?.keys;
@@ -91,45 +58,42 @@ export const applyThemesOnElement = (
}; };
const processTheme = ( const processTheme = (
cacheKey: string, themeName: string,
theme: Partial<Theme> themes: HomeAssistant["themes"]
): ProcessedTheme | undefined => { ): ProcessedTheme | undefined => {
if (!theme || !Object.keys(theme).length) { if (!themes.themes[themeName]) {
return undefined; return undefined;
} }
const combinedTheme: Partial<Theme> = { const theme: Theme = {
...derivedStyles, ...derivedStyles,
...theme, ...themes.themes[themeName],
}; };
const styles = {}; const styles = {};
const keys = {}; const keys = {};
for (const key of Object.keys(combinedTheme)) { for (const key of Object.keys(theme)) {
const prefixedKey = `--${key}`; const prefixedKey = `--${key}`;
const value = combinedTheme[key]!; const value = theme[key];
styles[prefixedKey] = value; styles[prefixedKey] = value;
keys[prefixedKey] = ""; keys[prefixedKey] = "";
// Try to create a rgb value for this key if it is not a var // Try to create a rgb value for this key if it is a hex color
if (!value.startsWith("#")) { if (!value.startsWith("#")) {
// Can't convert non hex value // Not a hex color
continue; continue;
} }
const rgbKey = `rgb-${key}`; const rgbKey = `rgb-${key}`;
if (combinedTheme[rgbKey] !== undefined) { if (theme[rgbKey] !== undefined) {
// Theme has it's own rgb value // Theme has it's own rgb value
continue; continue;
} }
try { const rgbValue = hexToRgb(value);
const rgbValue = hex2rgb(value).join(","); if (rgbValue !== null) {
const prefixedRgbKey = `--${rgbKey}`; const prefixedRgbKey = `--${rgbKey}`;
styles[prefixedRgbKey] = rgbValue; styles[prefixedRgbKey] = rgbValue;
keys[prefixedRgbKey] = ""; keys[prefixedRgbKey] = "";
} catch (e) {
continue;
} }
} }
PROCESSED_THEMES[cacheKey] = { styles, keys }; PROCESSED_THEMES[themeName] = { styles, keys };
return { styles, keys }; return { styles, keys };
}; };

View File

@@ -1,8 +0,0 @@
export const deepActiveElement = (
root: DocumentOrShadowRoot = document
): Element | null => {
if (root.activeElement?.shadowRoot?.activeElement) {
return deepActiveElement(root.activeElement.shadowRoot);
}
return root.activeElement;
};

View File

@@ -22,9 +22,6 @@ const _load = (
(element as HTMLScriptElement).async = true; (element as HTMLScriptElement).async = true;
if (type) { if (type) {
(element as HTMLScriptElement).type = type; (element as HTMLScriptElement).type = type;
// https://github.com/home-assistant/frontend/pull/6328
(element as HTMLScriptElement).crossOrigin =
url.substr(0, 1) === "/" ? "use-credentials" : "anonymous";
} }
break; break;
case "link": case "link":

View File

@@ -1,4 +1,4 @@
import type { Map, TileLayer } from "leaflet"; import type { Map } from "leaflet";
// Sets up a Leaflet map on the provided DOM element // Sets up a Leaflet map on the provided DOM element
export type LeafletModuleType = typeof import("leaflet"); export type LeafletModuleType = typeof import("leaflet");
@@ -6,9 +6,9 @@ export type LeafletDrawModuleType = typeof import("leaflet-draw");
export const setupLeafletMap = async ( export const setupLeafletMap = async (
mapElement: HTMLElement, mapElement: HTMLElement,
darkMode?: boolean, darkMode = false,
draw = false draw = false
): Promise<[Map, LeafletModuleType, TileLayer]> => { ): Promise<[Map, LeafletModuleType]> => {
if (!mapElement.parentNode) { if (!mapElement.parentNode) {
throw new Error("Cannot setup Leaflet map on disconnected element"); throw new Error("Cannot setup Leaflet map on disconnected element");
} }
@@ -28,28 +28,15 @@ export const setupLeafletMap = async (
style.setAttribute("rel", "stylesheet"); style.setAttribute("rel", "stylesheet");
mapElement.parentNode.appendChild(style); mapElement.parentNode.appendChild(style);
map.setView([52.3731339, 4.8903147], 13); map.setView([52.3731339, 4.8903147], 13);
createTileLayer(Leaflet, darkMode).addTo(map);
const tileLayer = createTileLayer(Leaflet, Boolean(darkMode)).addTo(map); return [map, Leaflet];
return [map, Leaflet, tileLayer];
}; };
export const replaceTileLayer = ( export const createTileLayer = (
leaflet: LeafletModuleType,
map: Map,
tileLayer: TileLayer,
darkMode: boolean
): TileLayer => {
map.removeLayer(tileLayer);
tileLayer = createTileLayer(leaflet, darkMode);
tileLayer.addTo(map);
return tileLayer;
};
const createTileLayer = (
leaflet: LeafletModuleType, leaflet: LeafletModuleType,
darkMode: boolean darkMode: boolean
): TileLayer => { ) => {
return leaflet.tileLayer( return leaflet.tileLayer(
`https://{s}.basemaps.cartocdn.com/${ `https://{s}.basemaps.cartocdn.com/${
darkMode ? "dark_all" : "light_all" darkMode ? "dark_all" : "light_all"

View File

@@ -1,28 +0,0 @@
/** Return an icon representing a battery state. */
import { HassEntity } from "home-assistant-js-websocket";
export const batteryIcon = (
batteryState: HassEntity,
batteryChargingState?: HassEntity
) => {
const battery = Number(batteryState.state);
const battery_charging =
batteryChargingState && batteryChargingState.state === "on";
if (isNaN(battery)) {
return "hass:battery-unknown";
}
let icon = "hass:battery";
const batteryRound = Math.round(battery / 10) * 10;
if (battery_charging && battery > 10) {
icon += `-charging-${batteryRound}`;
} else if (battery_charging) {
icon += "-outline";
} else if (battery <= 5) {
icon += "-alert";
} else if (battery > 5 && battery < 95) {
icon += `-${batteryRound}`;
}
return icon;
};

View File

@@ -55,12 +55,6 @@ export const computeStateDisplay = (
return formatDateTime(date, language); return formatDateTime(date, language);
} }
if (domain === "humidifier") {
if (stateObj.state === "on" && stateObj.attributes.humidity) {
return `${stateObj.attributes.humidity}%`;
}
}
return ( return (
// Return device class translation // Return device class translation
(stateObj.attributes.device_class && (stateObj.attributes.device_class &&

View File

@@ -8,27 +8,26 @@ import { DEFAULT_DOMAIN_ICON } from "../const";
const fixedIcons = { const fixedIcons = {
alert: "hass:alert", alert: "hass:alert",
alexa: "hass:amazon-alexa", alexa: "hass:amazon-alexa",
air_quality: "hass:air-filter",
automation: "hass:robot", automation: "hass:robot",
calendar: "hass:calendar", calendar: "hass:calendar",
camera: "hass:video", camera: "hass:video",
climate: "hass:thermostat", climate: "hass:thermostat",
configurator: "hass:cog", configurator: "hass:settings",
conversation: "hass:text-to-speech", conversation: "hass:text-to-speech",
counter: "hass:counter", counter: "hass:counter",
device_tracker: "hass:account", device_tracker: "hass:account",
fan: "hass:fan", fan: "hass:fan",
google_assistant: "hass:google-assistant", google_assistant: "hass:google-assistant",
group: "hass:google-circles-communities", group: "hass:google-circles-communities",
history_graph: "hass:chart-line",
homeassistant: "hass:home-assistant", homeassistant: "hass:home-assistant",
homekit: "hass:home-automation", homekit: "hass:home-automation",
humidifier: "hass:air-humidifier",
image_processing: "hass:image-filter-frames", image_processing: "hass:image-filter-frames",
input_boolean: "hass:toggle-switch-outline", input_boolean: "hass:toggle-switch-outline",
input_datetime: "hass:calendar-clock", input_datetime: "hass:calendar-clock",
input_number: "hass:ray-vertex", input_number: "hass:ray-vertex",
input_select: "hass:format-list-bulleted", input_select: "hass:format-list-bulleted",
input_text: "hass:form-textbox", input_text: "hass:textbox",
light: "hass:lightbulb", light: "hass:lightbulb",
mailbox: "hass:mailbox", mailbox: "hass:mailbox",
notify: "hass:comment-alert", notify: "hass:comment-alert",
@@ -43,11 +42,12 @@ const fixedIcons = {
simple_alarm: "hass:bell", simple_alarm: "hass:bell",
sun: "hass:white-balance-sunny", sun: "hass:white-balance-sunny",
switch: "hass:flash", switch: "hass:flash",
timer: "hass:timer-outline", timer: "hass:timer",
updater: "hass:cloud-upload", updater: "hass:cloud-upload",
vacuum: "hass:robot-vacuum", vacuum: "hass:robot-vacuum",
water_heater: "hass:thermometer", water_heater: "hass:thermometer",
weather: "hass:weather-cloudy", weather: "hass:weather-cloudy",
weblink: "hass:open-in-new",
zone: "hass:map-marker-radius", zone: "hass:map-marker-radius",
}; };

View File

@@ -14,20 +14,20 @@ export const getViewEntities = (
view.attributes.entity_id.forEach((entityId) => { view.attributes.entity_id.forEach((entityId) => {
const entity = entities[entityId]; const entity = entities[entityId];
if (!entity) { if (entity && !entity.attributes.hidden) {
return; viewEntities[entity.entity_id] = entity;
}
viewEntities[entity.entity_id] = entity; if (computeDomain(entity.entity_id) === "group") {
const groupEntities = getGroupEntities(entities, entity as GroupEntity);
if (computeDomain(entity.entity_id) === "group") { Object.keys(groupEntities).forEach((grEntityId) => {
const groupEntities = getGroupEntities(entities, entity as GroupEntity); const grEntity = groupEntities[grEntityId];
Object.keys(groupEntities).forEach((grEntityId) => { if (!grEntity.attributes.hidden) {
const grEntity = groupEntities[grEntityId]; viewEntities[grEntityId] = grEntity;
}
viewEntities[grEntityId] = grEntity; });
}); }
} }
}); });

View File

@@ -2,19 +2,14 @@
import { HassEntity } from "home-assistant-js-websocket"; import { HassEntity } from "home-assistant-js-websocket";
import { UNIT_C, UNIT_F } from "../const"; import { UNIT_C, UNIT_F } from "../const";
import { domainIcon } from "./domain_icon"; import { domainIcon } from "./domain_icon";
import { batteryIcon } from "./battery_icon";
const fixedDeviceClassIcons = { const fixedDeviceClassIcons = {
current: "hass:current-ac",
energy: "hass:flash",
humidity: "hass:water-percent", humidity: "hass:water-percent",
illuminance: "hass:brightness-5", illuminance: "hass:brightness-5",
temperature: "hass:thermometer", temperature: "hass:thermometer",
pressure: "hass:gauge", pressure: "hass:gauge",
power: "hass:flash", power: "hass:flash",
power_factor: "hass:angle-acute",
signal_strength: "hass:wifi", signal_strength: "hass:wifi",
voltage: "hass:sine-wave",
}; };
export const sensorIcon = (state: HassEntity) => { export const sensorIcon = (state: HassEntity) => {
@@ -24,7 +19,29 @@ export const sensorIcon = (state: HassEntity) => {
return fixedDeviceClassIcons[dclass]; return fixedDeviceClassIcons[dclass];
} }
if (dclass === "battery") { if (dclass === "battery") {
return batteryIcon(state); const battery = Number(state.state);
if (isNaN(battery)) {
return "hass:battery-unknown";
}
const batteryRound = Math.round(battery / 10) * 10;
if (batteryRound >= 100) {
return "hass:battery";
}
if (batteryRound <= 0) {
return "hass:battery-alert";
}
// Will return one of the following icons: (listed so extractor picks up)
// hass:battery-10
// hass:battery-20
// hass:battery-30
// hass:battery-40
// hass:battery-50
// hass:battery-60
// hass:battery-70
// hass:battery-80
// hass:battery-90
// We obscure 'hass' in iconname so this name does not get picked up
return `${"hass"}:battery-${batteryRound}`;
} }
const unit = state.attributes.unit_of_measurement; const unit = state.attributes.unit_of_measurement;

View File

@@ -4,6 +4,6 @@ export const supportsFeature = (
stateObj: HassEntity, stateObj: HassEntity,
feature: number feature: number
): boolean => { ): boolean => {
// eslint-disable-next-line no-bitwise // eslint-disable-next-line:no-bitwise
return (stateObj.attributes.supported_features! & feature) !== 0; return (stateObj.attributes.supported_features! & feature) !== 0;
}; };

View File

@@ -2,3 +2,11 @@ const validEntityId = /^(\w+)\.(\w+)$/;
export const isValidEntityId = (entityId: string) => export const isValidEntityId = (entityId: string) =>
validEntityId.test(entityId); validEntityId.test(entityId);
export const createValidEntityId = (input: string) =>
input
.toLowerCase()
.replace(/\s|'|\./g, "_") // replace spaces, points and quotes with underscore
.replace(/\W/g, "") // remove not allowed chars
.replace(/_{2,}/g, "_") // replace multiple underscores with 1
.replace(/_$/, ""); // remove underscores at the end

View File

@@ -1,14 +0,0 @@
import {
RequestSelectedDetail,
ListItem,
} from "@material/mwc-list/mwc-list-item";
export const shouldHandleRequestSelectedEvent = (
ev: CustomEvent<RequestSelectedDetail>
): boolean => {
if (!ev.detail.selected && ev.detail.source !== "property") {
return false;
}
(ev.target as ListItem).selected = false;
return true;
};

View File

@@ -26,9 +26,6 @@ class SearchInput extends LitElement {
@property({ type: Boolean }) @property({ type: Boolean })
public autofocus = false; public autofocus = false;
@property({ type: String })
public label?: string;
public focus() { public focus() {
this.shadowRoot!.querySelector("paper-input")!.focus(); this.shadowRoot!.querySelector("paper-input")!.focus();
} }
@@ -46,7 +43,7 @@ class SearchInput extends LitElement {
<paper-input <paper-input
class=${classMap({ "no-underline": this.noUnderline })} class=${classMap({ "no-underline": this.noUnderline })}
.autofocus=${this.autofocus} .autofocus=${this.autofocus}
.label=${this.label || "Search"} label="Search"
.value=${this.filter} .value=${this.filter}
@value-changed=${this._filterInputChanged} @value-changed=${this._filterInputChanged}
.noLabelFloat=${this.noLabelFloat} .noLabelFloat=${this.noLabelFloat}

View File

@@ -1,19 +1,19 @@
// https://gist.github.com/hagemann/382adfc57adbd5af078dc93feef01fe1 // https://gist.github.com/hagemann/382adfc57adbd5af078dc93feef01fe1
export const slugify = (value: string, delimiter = "_") => { export const slugify = (value: string) => {
const a = const a =
"àáäâãåăæąçćčđďèéěėëêęğǵḧìíïîįłḿǹńňñòóöôœøṕŕřßşśšșťțùúüûǘůűūųẃẍÿýźžż·/_,:;"; "àáäâãåăæąçćčđďèéěėëêęğǵḧìíïîįłḿǹńňñòóöôœøṕŕřßşśšșťțùúüûǘůűūųẃẍÿýźžż·/_,:;";
const b = `aaaaaaaaacccddeeeeeeegghiiiiilmnnnnooooooprrsssssttuuuuuuuuuwxyyzzz${delimiter}${delimiter}${delimiter}${delimiter}${delimiter}${delimiter}`; const b =
"aaaaaaaaacccddeeeeeeegghiiiiilmnnnnooooooprrsssssttuuuuuuuuuwxyyzzz------";
const p = new RegExp(a.split("").join("|"), "g"); const p = new RegExp(a.split("").join("|"), "g");
return value return value
.toString() .toString()
.toLowerCase() .toLowerCase()
.replace(/\s+/g, delimiter) // Replace spaces with delimiter .replace(/\s+/g, "-") // Replace spaces with -
.replace(p, (c) => b.charAt(a.indexOf(c))) // Replace special characters .replace(p, (c) => b.charAt(a.indexOf(c))) // Replace special characters
.replace(/&/g, `${delimiter}and${delimiter}`) // Replace & with 'and' .replace(/&/g, "-and-") // Replace & with 'and'
.replace(/[^\w-]+/g, "") // Remove all non-word characters .replace(/[^\w-]+/g, "") // Remove all non-word characters
.replace(/-/, delimiter) // Replace - with delimiter .replace(/--+/g, "-") // Replace multiple - with single -
.replace(new RegExp(`/${delimiter}${delimiter}+/`, "g"), delimiter) // Replace multiple delimiters with single delimiter .replace(/^-+/, "") // Trim - from start of text
.replace(new RegExp(`/^${delimiter}+/`), "") // Trim delimiter from start of text .replace(/-+$/, ""); // Trim - from end of text
.replace(new RegExp(`/-+$/`), ""); // Trim delimiter from end of text
}; };

View File

@@ -8,7 +8,6 @@ export const iconColorCSS = css`
ha-icon[data-domain="camera"][data-state="streaming"], ha-icon[data-domain="camera"][data-state="streaming"],
ha-icon[data-domain="cover"][data-state="open"], ha-icon[data-domain="cover"][data-state="open"],
ha-icon[data-domain="fan"][data-state="on"], ha-icon[data-domain="fan"][data-state="on"],
ha-icon[data-domain="humidifier"][data-state="on"],
ha-icon[data-domain="light"][data-state="on"], ha-icon[data-domain="light"][data-state="on"],
ha-icon[data-domain="input_boolean"][data-state="on"], ha-icon[data-domain="input_boolean"][data-state="on"],
ha-icon[data-domain="lock"][data-state="unlocked"], ha-icon[data-domain="lock"][data-state="unlocked"],
@@ -19,13 +18,7 @@ export const iconColorCSS = css`
ha-icon[data-domain="sun"][data-state="above_horizon"], ha-icon[data-domain="sun"][data-state="above_horizon"],
ha-icon[data-domain="switch"][data-state="on"], ha-icon[data-domain="switch"][data-state="on"],
ha-icon[data-domain="timer"][data-state="active"], ha-icon[data-domain="timer"][data-state="active"],
ha-icon[data-domain="vacuum"][data-state="cleaning"], ha-icon[data-domain="vacuum"][data-state="cleaning"] {
ha-icon[data-domain="group"][data-state="on"],
ha-icon[data-domain="group"][data-state="home"],
ha-icon[data-domain="group"][data-state="open"],
ha-icon[data-domain="group"][data-state="locked"],
ha-icon[data-domain="group"][data-state="problem"]
{
color: var(--paper-item-icon-active-color, #fdd835); color: var(--paper-item-icon-active-color, #fdd835);
} }

View File

@@ -9,9 +9,5 @@ export function computeRTL(hass: HomeAssistant) {
} }
export function computeRTLDirection(hass: HomeAssistant) { export function computeRTLDirection(hass: HomeAssistant) {
return emitRTLDirection(computeRTL(hass)); return computeRTL(hass) ? "rtl" : "ltr";
}
export function emitRTLDirection(rtl: boolean) {
return rtl ? "rtl" : "ltr";
} }

View File

@@ -54,8 +54,8 @@ class HaCallServiceButton extends EventsMixin(PolymerElement) {
callService() { callService() {
this.progress = true; this.progress = true;
// eslint-disable-next-line @typescript-eslint/no-this-alias // eslint-disable-next-line @typescript-eslint/no-this-alias
const el = this; var el = this;
const eventData = { var eventData = {
domain: this.domain, domain: this.domain,
service: this.service, service: this.service,
serviceData: this.serviceData, serviceData: this.serviceData,

View File

@@ -1,5 +1,5 @@
import "@material/mwc-button"; import "@material/mwc-button";
import "../ha-circular-progress"; import "@polymer/paper-spinner/paper-spinner";
import { html } from "@polymer/polymer/lib/utils/html-tag"; import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */ /* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element"; import { PolymerElement } from "@polymer/polymer/polymer-element";
@@ -22,13 +22,13 @@ class HaProgressButton extends PolymerElement {
.success mwc-button { .success mwc-button {
--mdc-theme-primary: white; --mdc-theme-primary: white;
background-color: var(--success-color); background-color: var(--google-green-500);
transition: none; transition: none;
} }
.error mwc-button { .error mwc-button {
--mdc-theme-primary: white; --mdc-theme-primary: white;
background-color: var(--error-color); background-color: var(--google-red-500);
transition: none; transition: none;
} }
@@ -51,9 +51,7 @@ class HaProgressButton extends PolymerElement {
<slot></slot> <slot></slot>
</mwc-button> </mwc-button>
<template is="dom-if" if="[[progress]]"> <template is="dom-if" if="[[progress]]">
<div class="progress"> <div class="progress"><paper-spinner active=""></paper-spinner></div>
<ha-circular-progress active size="small"></ha-circular-progress>
</div>
</template> </template>
</div> </div>
`; `;
@@ -78,7 +76,7 @@ class HaProgressButton extends PolymerElement {
} }
tempClass(className) { tempClass(className) {
const classList = this.$.container.classList; var classList = this.$.container.classList;
classList.add(className); classList.add(className);
setTimeout(() => { setTimeout(() => {
classList.remove(className); classList.remove(className);

View File

@@ -3,9 +3,7 @@ import {
css, css,
CSSResult, CSSResult,
customElement, customElement,
eventOptions,
html, html,
internalProperty,
LitElement, LitElement,
property, property,
PropertyValues, PropertyValues,
@@ -16,8 +14,6 @@ import { classMap } from "lit-html/directives/class-map";
import { ifDefined } from "lit-html/directives/if-defined"; import { ifDefined } from "lit-html/directives/if-defined";
import { styleMap } from "lit-html/directives/style-map"; import { styleMap } from "lit-html/directives/style-map";
import { scroll } from "lit-virtualizer"; import { scroll } from "lit-virtualizer";
import memoizeOne from "memoize-one";
import { restoreScroll } from "../../common/decorators/restore-scroll";
import { fireEvent } from "../../common/dom/fire_event"; import { fireEvent } from "../../common/dom/fire_event";
import "../../common/search/search-input"; import "../../common/search/search-input";
import { debounce } from "../../common/util/debounce"; import { debounce } from "../../common/util/debounce";
@@ -26,6 +22,7 @@ import "../ha-checkbox";
import type { HaCheckbox } from "../ha-checkbox"; import type { HaCheckbox } from "../ha-checkbox";
import "../ha-icon"; import "../ha-icon";
import { filterData, sortData } from "./sort-filter"; import { filterData, sortData } from "./sort-filter";
import memoizeOne from "memoize-one";
declare global { declare global {
// for fire event // for fire event
@@ -69,8 +66,6 @@ export interface DataTableColumnData extends DataTableSortColumnData {
width?: string; width?: string;
maxWidth?: string; maxWidth?: string;
grows?: boolean; grows?: boolean;
forceLTR?: boolean;
hidden?: boolean;
} }
export interface DataTableRowData { export interface DataTableRowData {
@@ -99,19 +94,17 @@ export class HaDataTable extends LitElement {
@property({ type: String }) public noDataText?: string; @property({ type: String }) public noDataText?: string;
@property({ type: String }) public searchLabel?: string;
@property({ type: String }) public filter = ""; @property({ type: String }) public filter = "";
@internalProperty() private _filterable = false; @property({ type: Boolean }) private _filterable = false;
@internalProperty() private _filter = ""; @property({ type: String }) private _filter = "";
@internalProperty() private _sortColumn?: string; @property({ type: String }) private _sortColumn?: string;
@internalProperty() private _sortDirection: SortingDirection = null; @property({ type: String }) private _sortDirection: SortingDirection = null;
@internalProperty() private _filteredData: DataTableRowData[] = []; @property({ type: Array }) private _filteredData: DataTableRowData[] = [];
@query("slot[name='header']") private _header!: HTMLSlotElement; @query("slot[name='header']") private _header!: HTMLSlotElement;
@@ -125,9 +118,6 @@ export class HaDataTable extends LitElement {
private curRequest = 0; private curRequest = 0;
// @ts-ignore
@restoreScroll(".scroller") private _savedScrollPos?: number;
private _debounceSearch = debounce( private _debounceSearch = debounce(
(value: string) => { (value: string) => {
this._filter = value; this._filter = value;
@@ -205,7 +195,6 @@ export class HaDataTable extends LitElement {
<div class="table-header"> <div class="table-header">
<search-input <search-input
@value-changed=${this._handleSearchChange} @value-changed=${this._handleSearchChange}
.label=${this.searchLabel}
></search-input> ></search-input>
</div> </div>
` `
@@ -215,15 +204,13 @@ export class HaDataTable extends LitElement {
class="mdc-data-table__table ${classMap({ class="mdc-data-table__table ${classMap({
"auto-height": this.autoHeight, "auto-height": this.autoHeight,
})}" })}"
role="table"
aria-rowcount=${this._filteredData.length}
style=${styleMap({ style=${styleMap({
height: this.autoHeight height: this.autoHeight
? `${(this._filteredData.length || 1) * 53 + 57}px` ? `${(this._filteredData.length || 1) * 53 + 57}px`
: `calc(100% - ${this._header?.clientHeight}px)`, : `calc(100% - ${this._header?.clientHeight}px)`,
})} })}
> >
<div class="mdc-data-table__header-row" role="row"> <div class="mdc-data-table__header-row">
${this.selectable ${this.selectable
? html` ? html`
<div <div
@@ -243,10 +230,8 @@ export class HaDataTable extends LitElement {
</div> </div>
` `
: ""} : ""}
${Object.entries(this.columns).map(([key, column]) => { ${Object.entries(this.columns).map((columnEntry) => {
if (column.hidden) { const [key, column] = columnEntry;
return "";
}
const sorted = key === this._sortColumn; const sorted = key === this._sortColumn;
const classes = { const classes = {
"mdc-data-table__header-cell--numeric": Boolean( "mdc-data-table__header-cell--numeric": Boolean(
@@ -293,30 +278,25 @@ export class HaDataTable extends LitElement {
${!this._filteredData.length ${!this._filteredData.length
? html` ? html`
<div class="mdc-data-table__content"> <div class="mdc-data-table__content">
<div class="mdc-data-table__row" role="row"> <div class="mdc-data-table__row">
<div class="mdc-data-table__cell grows center" role="cell"> <div class="mdc-data-table__cell grows center">
${this.noDataText || "No data"} ${this.noDataText || "No data"}
</div> </div>
</div> </div>
</div> </div>
` `
: html` : html`
<div <div class="mdc-data-table__content scroller">
class="mdc-data-table__content scroller"
@scroll=${this._saveScrollPos}
>
${scroll({ ${scroll({
items: !this.hasFab items: !this.hasFab
? this._filteredData ? this._filteredData
: [...this._filteredData, ...[{ empty: true }]], : [...this._filteredData, ...[{ empty: true }]],
renderItem: (row: DataTableRowData, index) => { renderItem: (row: DataTableRowData) => {
if (row.empty) { if (row.empty) {
return html` <div class="mdc-data-table__row"></div> `; return html` <div class="mdc-data-table__row"></div> `;
} }
return html` return html`
<div <div
aria-rowindex=${index}
role="row"
.rowId="${row[this.id]}" .rowId="${row[this.id]}"
@click=${this._handleRowClick} @click=${this._handleRowClick}
class="mdc-data-table__row ${classMap({ class="mdc-data-table__row ${classMap({
@@ -335,7 +315,6 @@ export class HaDataTable extends LitElement {
? html` ? html`
<div <div
class="mdc-data-table__cell mdc-data-table__cell--checkbox" class="mdc-data-table__cell mdc-data-table__cell--checkbox"
role="cell"
> >
<ha-checkbox <ha-checkbox
class="mdc-data-table__row-checkbox" class="mdc-data-table__row-checkbox"
@@ -349,45 +328,39 @@ export class HaDataTable extends LitElement {
</div> </div>
` `
: ""} : ""}
${Object.entries(this.columns).map( ${Object.entries(this.columns).map((columnEntry) => {
([key, column]) => { const [key, column] = columnEntry;
if (column.hidden) { return html`
return ""; <div
} class="mdc-data-table__cell ${classMap({
return html` "mdc-data-table__cell--numeric": Boolean(
<div column.type === "numeric"
role="cell" ),
class="mdc-data-table__cell ${classMap({ "mdc-data-table__cell--icon": Boolean(
"mdc-data-table__cell--numeric": Boolean( column.type === "icon"
column.type === "numeric" ),
), "mdc-data-table__cell--icon-button": Boolean(
"mdc-data-table__cell--icon": Boolean( column.type === "icon-button"
column.type === "icon" ),
), grows: Boolean(column.grows),
"mdc-data-table__cell--icon-button": Boolean( })}"
column.type === "icon-button" style=${column.width
), ? styleMap({
grows: Boolean(column.grows), [column.grows
forceLTR: Boolean(column.forceLTR), ? "minWidth"
})}" : "width"]: column.width,
style=${column.width maxWidth: column.maxWidth
? styleMap({ ? column.maxWidth
[column.grows : "",
? "minWidth" })
: "width"]: column.width, : ""}
maxWidth: column.maxWidth >
? column.maxWidth ${column.template
: "", ? column.template(row[key], row)
}) : row[key]}
: ""} </div>
> `;
${column.template })}
? column.template(row[key], row)
: row[key]}
</div>
`;
}
)}
</div> </div>
`; `;
}, },
@@ -526,11 +499,6 @@ export class HaDataTable extends LitElement {
this._table.style.height = `calc(100% - ${this._header.clientHeight}px)`; this._table.style.height = `calc(100% - ${this._header.clientHeight}px)`;
} }
@eventOptions({ passive: true })
private _saveScrollPos(e: Event) {
this._savedScrollPos = (e.target as HTMLDivElement).scrollTop;
}
static get styles(): CSSResult { static get styles(): CSSResult {
return css` return css`
/* default mdc styles, colors changed, without checkbox styles */ /* default mdc styles, colors changed, without checkbox styles */
@@ -554,7 +522,7 @@ export class HaDataTable extends LitElement {
border-radius: 4px; border-radius: 4px;
border-width: 1px; border-width: 1px;
border-style: solid; border-style: solid;
border-color: var(--divider-color); border-color: rgba(var(--rgb-primary-text-color), 0.12);
display: inline-flex; display: inline-flex;
flex-direction: column; flex-direction: column;
box-sizing: border-box; box-sizing: border-box;
@@ -572,7 +540,7 @@ export class HaDataTable extends LitElement {
} }
.mdc-data-table__row ~ .mdc-data-table__row { .mdc-data-table__row ~ .mdc-data-table__row {
border-top: 1px solid var(--divider-color); border-top: 1px solid rgba(var(--rgb-primary-text-color), 0.12);
} }
.mdc-data-table__row:not(.mdc-data-table__row--selected):hover { .mdc-data-table__row:not(.mdc-data-table__row--selected):hover {
@@ -591,7 +559,7 @@ export class HaDataTable extends LitElement {
height: 56px; height: 56px;
display: flex; display: flex;
width: 100%; width: 100%;
border-bottom: 1px solid var(--divider-color); border-bottom: 1px solid rgba(var(--rgb-primary-text-color), 0.12);
overflow-x: auto; overflow-x: auto;
} }
@@ -622,8 +590,10 @@ export class HaDataTable extends LitElement {
padding-right: 0; padding-right: 0;
width: 56px; width: 56px;
} }
:host([dir="rtl"]) .mdc-data-table__header-cell--checkbox, [dir="rtl"] .mdc-data-table__header-cell--checkbox,
:host([dir="rtl"]) .mdc-data-table__cell--checkbox { .mdc-data-table__header-cell--checkbox[dir="rtl"],
[dir="rtl"] .mdc-data-table__cell--checkbox,
.mdc-data-table__cell--checkbox[dir="rtl"] {
/* @noflip */ /* @noflip */
padding-left: 0; padding-left: 0;
/* @noflip */ /* @noflip */
@@ -657,7 +627,8 @@ export class HaDataTable extends LitElement {
.mdc-data-table__cell--numeric { .mdc-data-table__cell--numeric {
text-align: right; text-align: right;
} }
:host([dir="rtl"]) .mdc-data-table__cell--numeric { [dir="rtl"] .mdc-data-table__cell--numeric,
.mdc-data-table__cell--numeric[dir="rtl"] {
/* @noflip */ /* @noflip */
text-align: left; text-align: left;
} }
@@ -675,33 +646,18 @@ export class HaDataTable extends LitElement {
.mdc-data-table__header-cell.mdc-data-table__header-cell--icon { .mdc-data-table__header-cell.mdc-data-table__header-cell--icon {
text-align: center; text-align: center;
} }
.mdc-data-table__header-cell.sortable.mdc-data-table__header-cell--icon:hover, .mdc-data-table__header-cell.sortable.mdc-data-table__header-cell--icon:hover,
.mdc-data-table__header-cell.sortable.mdc-data-table__header-cell--icon:not(.not-sorted) { .mdc-data-table__header-cell.sortable.mdc-data-table__header-cell--icon:not(.not-sorted) {
text-align: left; text-align: left;
} }
:host([dir="rtl"])
.mdc-data-table__header-cell.sortable.mdc-data-table__header-cell--icon:hover,
:host([dir="rtl"])
.mdc-data-table__header-cell.sortable.mdc-data-table__header-cell--icon:not(.not-sorted) {
text-align: right;
}
.mdc-data-table__cell--icon:first-child ha-icon { .mdc-data-table__cell--icon:first-child ha-icon {
margin-left: 8px; margin-left: 8px;
} }
:host([dir="rtl"]) .mdc-data-table__cell--icon:first-child ha-icon {
margin-left: auto;
margin-right: 8px;
}
.mdc-data-table__cell--icon:first-child state-badge { .mdc-data-table__cell--icon:first-child state-badge {
margin-right: -8px; margin-right: -8px;
} }
:host([dir="rtl"]) .mdc-data-table__cell--icon:first-child state-badge {
margin-right: auto;
margin-left: -8px;
}
.mdc-data-table__header-cell--icon-button, .mdc-data-table__header-cell--icon-button,
.mdc-data-table__cell--icon-button { .mdc-data-table__cell--icon-button {
@@ -719,22 +675,12 @@ export class HaDataTable extends LitElement {
width: 64px; width: 64px;
padding-left: 16px; padding-left: 16px;
} }
:host([dir="rtl"]) .mdc-data-table__header-cell--icon-button:first-child,
:host([dir="rtl"]) .mdc-data-table__cell--icon-button:first-child {
padding-left: auto;
padding-right: 16px;
}
.mdc-data-table__header-cell--icon-button:last-child, .mdc-data-table__header-cell--icon-button:last-child,
.mdc-data-table__cell--icon-button:last-child { .mdc-data-table__cell--icon-button:last-child {
width: 64px; width: 64px;
padding-right: 16px; padding-right: 16px;
} }
:host([dir="rtl"]) .mdc-data-table__header-cell--icon-button:last-child,
:host([dir="rtl"]) .mdc-data-table__cell--icon-button:last-child {
padding-right: auto;
padding-left: 16px;
}
.mdc-data-table__cell--icon-button a { .mdc-data-table__cell--icon-button a {
color: var(--secondary-text-color); color: var(--secondary-text-color);
@@ -752,7 +698,8 @@ export class HaDataTable extends LitElement {
text-transform: inherit; text-transform: inherit;
text-align: left; text-align: left;
} }
:host([dir="rtl"]) .mdc-data-table__header-cell { [dir="rtl"] .mdc-data-table__header-cell,
.mdc-data-table__header-cell[dir="rtl"] {
/* @noflip */ /* @noflip */
text-align: right; text-align: right;
} }
@@ -764,15 +711,11 @@ export class HaDataTable extends LitElement {
.mdc-data-table__header-cell--numeric.sortable:not(.not-sorted) { .mdc-data-table__header-cell--numeric.sortable:not(.not-sorted) {
text-align: left; text-align: left;
} }
:host([dir="rtl"]) .mdc-data-table__header-cell--numeric { [dir="rtl"] .mdc-data-table__header-cell--numeric,
.mdc-data-table__header-cell--numeric[dir="rtl"] {
/* @noflip */ /* @noflip */
text-align: left; text-align: left;
} }
:host([dir="rtl"]) .mdc-data-table__header-cell--numeric.sortable:hover,
:host([dir="rtl"])
.mdc-data-table__header-cell--numeric.sortable:not(.not-sorted) {
text-align: right;
}
/* custom from here */ /* custom from here */
@@ -793,10 +736,6 @@ export class HaDataTable extends LitElement {
position: relative; position: relative;
left: 0px; left: 0px;
} }
:host([dir="rtl"]) .mdc-data-table__header-cell span {
left: auto;
right: 0px;
}
.mdc-data-table__header-cell.sortable { .mdc-data-table__header-cell.sortable {
cursor: pointer; cursor: pointer;
@@ -804,9 +743,6 @@ export class HaDataTable extends LitElement {
.mdc-data-table__header-cell > * { .mdc-data-table__header-cell > * {
transition: left 0.2s ease; transition: left 0.2s ease;
} }
:host([dir="rtl"]) .mdc-data-table__header-cell > * {
transition: right 0.2s ease;
}
.mdc-data-table__header-cell ha-icon { .mdc-data-table__header-cell ha-icon {
top: -3px; top: -3px;
position: absolute; position: absolute;
@@ -814,37 +750,16 @@ export class HaDataTable extends LitElement {
.mdc-data-table__header-cell.not-sorted ha-icon { .mdc-data-table__header-cell.not-sorted ha-icon {
left: -20px; left: -20px;
} }
:host([dir="rtl"]) .mdc-data-table__header-cell.not-sorted ha-icon {
right: -20px;
}
.mdc-data-table__header-cell.sortable:not(.not-sorted) span, .mdc-data-table__header-cell.sortable:not(.not-sorted) span,
.mdc-data-table__header-cell.sortable.not-sorted:hover span { .mdc-data-table__header-cell.sortable.not-sorted:hover span {
left: 24px; left: 24px;
} }
:host([dir="rtl"])
.mdc-data-table__header-cell.sortable:not(.not-sorted)
span,
:host([dir="rtl"])
.mdc-data-table__header-cell.sortable.not-sorted:hover
span {
left: auto;
right: 24px;
}
.mdc-data-table__header-cell.sortable:not(.not-sorted) ha-icon, .mdc-data-table__header-cell.sortable:not(.not-sorted) ha-icon,
.mdc-data-table__header-cell.sortable:hover.not-sorted ha-icon { .mdc-data-table__header-cell.sortable:hover.not-sorted ha-icon {
left: 12px; left: 12px;
} }
:host([dir="rtl"])
.mdc-data-table__header-cell.sortable:not(.not-sorted)
ha-icon,
:host([dir="rtl"])
.mdc-data-table__header-cell.sortable:hover.not-sorted
ha-icon {
left: auto;
right: 12px;
}
.table-header { .table-header {
border-bottom: 1px solid var(--divider-color); border-bottom: 1px solid rgba(var(--rgb-primary-text-color), 0.12);
padding: 0 16px; padding: 0 16px;
} }
search-input { search-input {
@@ -873,9 +788,6 @@ export class HaDataTable extends LitElement {
flex-grow: 1; flex-grow: 1;
flex-shrink: 1; flex-shrink: 1;
} }
.forceLTR {
direction: ltr;
}
`; `;
} }
} }

View File

@@ -1,11 +1,11 @@
// To use comlink under ES5 // To use comlink under ES5
import { expose } from "comlink";
import "proxy-polyfill"; import "proxy-polyfill";
import { expose } from "comlink";
import type { import type {
DataTableRowData,
DataTableSortColumnData, DataTableSortColumnData,
SortableColumnContainer, DataTableRowData,
SortingDirection, SortingDirection,
SortableColumnContainer,
} from "./ha-data-table"; } from "./ha-data-table";
const filterData = ( const filterData = (
@@ -19,7 +19,7 @@ const filterData = (
const [key, column] = columnEntry; const [key, column] = columnEntry;
if (column.filterable) { if (column.filterable) {
if ( if (
String(column.filterKey ? row[key][column.filterKey] : row[key]) (column.filterKey ? row[key][column.filterKey] : row[key])
.toUpperCase() .toUpperCase()
.includes(filter) .includes(filter)
) { ) {

View File

@@ -135,7 +135,7 @@ class DateRangePickerElement extends WrappedElement {
} }
.daterangepicker td.in-range { .daterangepicker td.in-range {
background-color: var(--light-primary-color); background-color: var(--light-primary-color);
color: var(--text-light-primary-color, var(--primary-text-color)); color: var(--primary-text-color);
} }
.daterangepicker td.active, .daterangepicker td.active,
.daterangepicker td.active:hover { .daterangepicker td.active:hover {

View File

@@ -13,7 +13,6 @@ import {
html, html,
LitElement, LitElement,
property, property,
internalProperty,
PropertyValues, PropertyValues,
TemplateResult, TemplateResult,
} from "lit-element"; } from "lit-element";
@@ -88,7 +87,7 @@ const rowRenderer = (
@customElement("ha-area-devices-picker") @customElement("ha-area-devices-picker")
export class HaAreaDevicesPicker extends SubscribeMixin(LitElement) { export class HaAreaDevicesPicker extends SubscribeMixin(LitElement) {
@property({ attribute: false }) public hass!: HomeAssistant; @property() public hass!: HomeAssistant;
@property() public label?: string; @property() public label?: string;
@@ -125,13 +124,13 @@ export class HaAreaDevicesPicker extends SubscribeMixin(LitElement) {
@property({ type: Boolean }) @property({ type: Boolean })
private _opened?: boolean; private _opened?: boolean;
@internalProperty() private _areaPicker = true; @property() private _areaPicker = true;
@internalProperty() private _devices?: DeviceRegistryEntry[]; @property() private _devices?: DeviceRegistryEntry[];
@internalProperty() private _areas?: AreaRegistryEntry[]; @property() private _areas?: AreaRegistryEntry[];
@internalProperty() private _entities?: EntityRegistryEntry[]; @property() private _entities?: EntityRegistryEntry[];
private _selectedDevices: string[] = []; private _selectedDevices: string[] = [];

View File

@@ -8,7 +8,6 @@ import {
html, html,
LitElement, LitElement,
property, property,
internalProperty,
TemplateResult, TemplateResult,
} from "lit-element"; } from "lit-element";
import { fireEvent } from "../../common/dom/fire_event"; import { fireEvent } from "../../common/dom/fire_event";
@@ -25,7 +24,7 @@ const UNKNOWN_AUTOMATION_KEY = "UNKNOWN_AUTOMATION";
export abstract class HaDeviceAutomationPicker< export abstract class HaDeviceAutomationPicker<
T extends DeviceAutomation T extends DeviceAutomation
> extends LitElement { > extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant; @property() public hass!: HomeAssistant;
@property() public label?: string; @property() public label?: string;
@@ -37,11 +36,11 @@ export abstract class HaDeviceAutomationPicker<
protected UNKNOWN_AUTOMATION_TEXT = "Unknown automation"; protected UNKNOWN_AUTOMATION_TEXT = "Unknown automation";
@internalProperty() private _automations: T[] = []; @property() private _automations: T[] = [];
// Trigger an empty render so we start with a clean DOM. // Trigger an empty render so we start with a clean DOM.
// paper-listbox does not like changing things around. // paper-listbox does not like changing things around.
@internalProperty() private _renderEmpty = false; @property() private _renderEmpty = false;
private _localizeDeviceAutomation: ( private _localizeDeviceAutomation: (
hass: HomeAssistant, hass: HomeAssistant,
@@ -117,7 +116,11 @@ export abstract class HaDeviceAutomationPicker<
> >
${this.NO_AUTOMATION_TEXT} ${this.NO_AUTOMATION_TEXT}
</paper-item> </paper-item>
<paper-item key=${UNKNOWN_AUTOMATION_KEY} hidden> <paper-item
key=${UNKNOWN_AUTOMATION_KEY}
.automation=${this.value}
hidden
>
${this.UNKNOWN_AUTOMATION_TEXT} ${this.UNKNOWN_AUTOMATION_TEXT}
</paper-item> </paper-item>
${this._automations.map( ${this._automations.map(
@@ -171,17 +174,18 @@ export abstract class HaDeviceAutomationPicker<
} }
private _automationChanged(ev) { private _automationChanged(ev) {
if (ev.detail.item.automation) { this._setValue(ev.detail.item.automation);
this._setValue(ev.detail.item.automation);
}
} }
private _setValue(automation: T) { private _setValue(automation: T) {
if (this.value && deviceAutomationsEqual(automation, this.value)) { if (this.value && deviceAutomationsEqual(automation, this.value)) {
return; return;
} }
fireEvent(this, "change"); this.value = automation;
fireEvent(this, "value-changed", { value: automation }); setTimeout(() => {
fireEvent(this, "change");
fireEvent(this, "value-changed", { value: automation });
}, 0);
} }
static get styles(): CSSResult { static get styles(): CSSResult {

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