mirror of
				https://github.com/home-assistant/frontend.git
				synced 2025-11-04 00:19:47 +00:00 
			
		
		
		
	Compare commits
	
		
			7 Commits
		
	
	
		
			blocking-u
			...
			analytics-
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					f69bce534a | ||
| 
						 | 
					575f58bd88 | ||
| 
						 | 
					35535628fc | ||
| 
						 | 
					8e018c9cfe | ||
| 
						 | 
					5ae268b792 | ||
| 
						 | 
					329732ac30 | ||
| 
						 | 
					7f88bab552 | 
@@ -4,7 +4,8 @@
 | 
			
		||||
    "plugin:@typescript-eslint/recommended",
 | 
			
		||||
    "plugin:wc/recommended",
 | 
			
		||||
    "plugin:lit/recommended",
 | 
			
		||||
    "prettier"
 | 
			
		||||
    "prettier",
 | 
			
		||||
    "prettier/@typescript-eslint"
 | 
			
		||||
  ],
 | 
			
		||||
  "parser": "@typescript-eslint/parser",
 | 
			
		||||
  "parserOptions": {
 | 
			
		||||
@@ -28,7 +29,9 @@
 | 
			
		||||
    "__BUILD__": false,
 | 
			
		||||
    "__VERSION__": false,
 | 
			
		||||
    "__STATIC_PATH__": false,
 | 
			
		||||
    "Polymer": true
 | 
			
		||||
    "Polymer": true,
 | 
			
		||||
    "webkitSpeechRecognition": false,
 | 
			
		||||
    "ResizeObserver": false
 | 
			
		||||
  },
 | 
			
		||||
  "env": {
 | 
			
		||||
    "browser": true,
 | 
			
		||||
@@ -82,28 +85,8 @@
 | 
			
		||||
    "@typescript-eslint/explicit-function-return-type": 0,
 | 
			
		||||
    "@typescript-eslint/explicit-module-boundary-types": 0,
 | 
			
		||||
    "@typescript-eslint/no-shadow": ["error"],
 | 
			
		||||
    "@typescript-eslint/naming-convention": [
 | 
			
		||||
      0,
 | 
			
		||||
      {
 | 
			
		||||
        "selector": "default",
 | 
			
		||||
        "format": ["camelCase", "snake_case"],
 | 
			
		||||
        "leadingUnderscore": "allow",
 | 
			
		||||
        "trailingUnderscore": "allow"
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "selector": ["variable"],
 | 
			
		||||
        "format": ["camelCase", "snake_case", "UPPER_CASE"],
 | 
			
		||||
        "leadingUnderscore": "allow",
 | 
			
		||||
        "trailingUnderscore": "allow"
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "selector": "typeLike",
 | 
			
		||||
        "format": ["PascalCase"]
 | 
			
		||||
      }
 | 
			
		||||
    ],
 | 
			
		||||
    "lit/attribute-value-entities": 0
 | 
			
		||||
  },
 | 
			
		||||
  "plugins": ["disable", "import", "lit", "prettier", "@typescript-eslint"],
 | 
			
		||||
  "processor": "disable/disable",
 | 
			
		||||
  "ignorePatterns": ["src/resources/lit-virtualizer/*"]
 | 
			
		||||
  "processor": "disable/disable"
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										6
									
								
								.github/workflows/ci.yaml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								.github/workflows/ci.yaml
									
									
									
									
										vendored
									
									
								
							@@ -37,11 +37,9 @@ jobs:
 | 
			
		||||
      - name: Build resources
 | 
			
		||||
        run: ./node_modules/.bin/gulp gen-icons-json build-translations gather-gallery-demos
 | 
			
		||||
      - name: Run eslint
 | 
			
		||||
        run: yarn run lint:eslint
 | 
			
		||||
        run: ./node_modules/.bin/eslint '{**/src,src}/**/*.{js,ts,html}' --ignore-path .gitignore
 | 
			
		||||
      - name: Run tsc
 | 
			
		||||
        run: yarn run lint:types
 | 
			
		||||
      - name: Run prettier
 | 
			
		||||
        run: yarn run lint:prettier
 | 
			
		||||
        run: ./node_modules/.bin/tsc
 | 
			
		||||
  test:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    steps:
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										8
									
								
								.github/workflows/release.yaml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								.github/workflows/release.yaml
									
									
									
									
										vendored
									
									
								
							@@ -6,7 +6,8 @@ on:
 | 
			
		||||
      - published
 | 
			
		||||
 | 
			
		||||
env:
 | 
			
		||||
  PYTHON_VERSION: 3.8
 | 
			
		||||
  WHEELS_TAG: 3.7-alpine3.11
 | 
			
		||||
  PYTHON_VERSION: 3.7
 | 
			
		||||
  NODE_VERSION: 12.1
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
@@ -63,9 +64,6 @@ jobs:
 | 
			
		||||
    strategy:
 | 
			
		||||
      matrix:
 | 
			
		||||
        arch: ["aarch64", "armhf", "armv7", "amd64", "i386"]
 | 
			
		||||
        tag:
 | 
			
		||||
          - "3.8-alpine3.12"
 | 
			
		||||
          - "3.9-alpine3.13"
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Download requirements.txt
 | 
			
		||||
        uses: actions/download-artifact@v2
 | 
			
		||||
@@ -75,7 +73,7 @@ jobs:
 | 
			
		||||
      - name: Build wheels
 | 
			
		||||
        uses: home-assistant/wheels@master
 | 
			
		||||
        with:
 | 
			
		||||
          tag: ${{ matrix.tag }}
 | 
			
		||||
          tag: ${{ env.WHEELS_TAG }}
 | 
			
		||||
          arch: ${{ matrix.arch }}
 | 
			
		||||
          wheels-host: ${{ secrets.WHEELS_HOST }}
 | 
			
		||||
          wheels-key: ${{ secrets.WHEELS_KEY }}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										21
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										21
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -1,17 +1,10 @@
 | 
			
		||||
.DS_Store
 | 
			
		||||
.reify-cache
 | 
			
		||||
 | 
			
		||||
# build
 | 
			
		||||
build
 | 
			
		||||
build-translations/*
 | 
			
		||||
hass_frontend/*
 | 
			
		||||
dist
 | 
			
		||||
 | 
			
		||||
# yarn
 | 
			
		||||
.yarn
 | 
			
		||||
yarn-error.log
 | 
			
		||||
node_modules/*
 | 
			
		||||
npm-debug.log
 | 
			
		||||
.DS_Store
 | 
			
		||||
hass_frontend/*
 | 
			
		||||
.reify-cache
 | 
			
		||||
 | 
			
		||||
# Python stuff
 | 
			
		||||
*.py[cod]
 | 
			
		||||
@@ -21,8 +14,11 @@ npm-debug.log
 | 
			
		||||
# venv stuff
 | 
			
		||||
pyvenv.cfg
 | 
			
		||||
pip-selfcheck.json
 | 
			
		||||
venv/*
 | 
			
		||||
venv
 | 
			
		||||
.venv
 | 
			
		||||
lib
 | 
			
		||||
bin
 | 
			
		||||
dist
 | 
			
		||||
 | 
			
		||||
# vscode
 | 
			
		||||
.vscode/*
 | 
			
		||||
@@ -35,8 +31,9 @@ src/cast/dev_const.ts
 | 
			
		||||
 | 
			
		||||
# Secrets
 | 
			
		||||
.lokalise_token
 | 
			
		||||
yarn-error.log
 | 
			
		||||
 | 
			
		||||
# asdf
 | 
			
		||||
#asdf
 | 
			
		||||
.tool-versions
 | 
			
		||||
 | 
			
		||||
# Home Assistant config
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +0,0 @@
 | 
			
		||||
module.exports = {
 | 
			
		||||
  require: "test-mocha/testconf.js",
 | 
			
		||||
  timeout: 10000,
 | 
			
		||||
};
 | 
			
		||||
@@ -1,4 +1,3 @@
 | 
			
		||||
/* eslint-disable @typescript-eslint/no-var-requires */
 | 
			
		||||
const path = require("path");
 | 
			
		||||
const env = require("./env.js");
 | 
			
		||||
const paths = require("./paths.js");
 | 
			
		||||
@@ -52,16 +51,15 @@ module.exports.terserOptions = (latestBuild) => ({
 | 
			
		||||
 | 
			
		||||
module.exports.babelOptions = ({ latestBuild }) => ({
 | 
			
		||||
  babelrc: false,
 | 
			
		||||
  compact: false,
 | 
			
		||||
  presets: [
 | 
			
		||||
    !latestBuild && [
 | 
			
		||||
      "@babel/preset-env",
 | 
			
		||||
      require("@babel/preset-env").default,
 | 
			
		||||
      {
 | 
			
		||||
        useBuiltIns: "entry",
 | 
			
		||||
        corejs: "3.6",
 | 
			
		||||
      },
 | 
			
		||||
    ],
 | 
			
		||||
    "@babel/preset-typescript",
 | 
			
		||||
    require("@babel/preset-typescript").default,
 | 
			
		||||
  ].filter(Boolean),
 | 
			
		||||
  plugins: [
 | 
			
		||||
    // Part of ES2018. Converts {...a, b: 2} to Object.assign({}, a, {b: 2})
 | 
			
		||||
@@ -74,12 +72,23 @@ module.exports.babelOptions = ({ latestBuild }) => ({
 | 
			
		||||
    "@babel/plugin-syntax-dynamic-import",
 | 
			
		||||
    "@babel/plugin-proposal-optional-chaining",
 | 
			
		||||
    "@babel/plugin-proposal-nullish-coalescing-operator",
 | 
			
		||||
    ["@babel/plugin-proposal-decorators", { decoratorsBeforeExport: true }],
 | 
			
		||||
    ["@babel/plugin-proposal-private-methods", { loose: true }],
 | 
			
		||||
    ["@babel/plugin-proposal-class-properties", { loose: true }],
 | 
			
		||||
    [
 | 
			
		||||
      require("@babel/plugin-proposal-decorators").default,
 | 
			
		||||
      { decoratorsBeforeExport: true },
 | 
			
		||||
    ],
 | 
			
		||||
    [
 | 
			
		||||
      require("@babel/plugin-proposal-class-properties").default,
 | 
			
		||||
      { loose: true },
 | 
			
		||||
    ],
 | 
			
		||||
  ].filter(Boolean),
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
// Are already ES5, cause warnings when babelified.
 | 
			
		||||
module.exports.babelExclude = () => [
 | 
			
		||||
  require.resolve("@mdi/js/mdi.js"),
 | 
			
		||||
  require.resolve("hls.js"),
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
const outputPath = (outputRoot, latestBuild) =>
 | 
			
		||||
  path.resolve(outputRoot, latestBuild ? "frontend_latest" : "frontend_es5");
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,3 @@
 | 
			
		||||
/* eslint-disable @typescript-eslint/no-var-requires */
 | 
			
		||||
const fs = require("fs");
 | 
			
		||||
const path = require("path");
 | 
			
		||||
const paths = require("./paths.js");
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,3 @@
 | 
			
		||||
/* eslint-disable @typescript-eslint/no-var-requires */
 | 
			
		||||
const path = require("path");
 | 
			
		||||
 | 
			
		||||
module.exports = {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,3 @@
 | 
			
		||||
/* eslint-disable @typescript-eslint/no-var-requires */
 | 
			
		||||
const path = require("path");
 | 
			
		||||
 | 
			
		||||
const commonjs = require("@rollup/plugin-commonjs");
 | 
			
		||||
@@ -33,77 +32,88 @@ const createRollupConfig = ({
 | 
			
		||||
  publicPath,
 | 
			
		||||
  dontHash,
 | 
			
		||||
  isWDS,
 | 
			
		||||
}) => ({
 | 
			
		||||
  /**
 | 
			
		||||
   * @type { import("rollup").InputOptions }
 | 
			
		||||
   */
 | 
			
		||||
  inputOptions: {
 | 
			
		||||
    input: entry,
 | 
			
		||||
    // Some entry points contain no JavaScript. This setting silences a warning about that.
 | 
			
		||||
    // https://rollupjs.org/guide/en/#preserveentrysignatures
 | 
			
		||||
    preserveEntrySignatures: false,
 | 
			
		||||
    plugins: [
 | 
			
		||||
      ignore({
 | 
			
		||||
        files: bundle.emptyPackages({ latestBuild }),
 | 
			
		||||
      }),
 | 
			
		||||
      resolve({
 | 
			
		||||
        extensions,
 | 
			
		||||
        preferBuiltins: false,
 | 
			
		||||
        browser: true,
 | 
			
		||||
        rootDir: paths.polymer_dir,
 | 
			
		||||
      }),
 | 
			
		||||
      commonjs(),
 | 
			
		||||
      json(),
 | 
			
		||||
      babel({
 | 
			
		||||
        ...bundle.babelOptions({ latestBuild }),
 | 
			
		||||
        extensions,
 | 
			
		||||
        babelHelpers: isWDS ? "inline" : "bundled",
 | 
			
		||||
      }),
 | 
			
		||||
      string({
 | 
			
		||||
        // Import certain extensions as strings
 | 
			
		||||
        include: [path.join(paths.polymer_dir, "node_modules/**/*.css")],
 | 
			
		||||
      }),
 | 
			
		||||
      replace(bundle.definedVars({ isProdBuild, latestBuild, defineOverlay })),
 | 
			
		||||
      !isWDS &&
 | 
			
		||||
        manifest({
 | 
			
		||||
          publicPath,
 | 
			
		||||
}) => {
 | 
			
		||||
  return {
 | 
			
		||||
    /**
 | 
			
		||||
     * @type { import("rollup").InputOptions }
 | 
			
		||||
     */
 | 
			
		||||
    inputOptions: {
 | 
			
		||||
      input: entry,
 | 
			
		||||
      // Some entry points contain no JavaScript. This setting silences a warning about that.
 | 
			
		||||
      // https://rollupjs.org/guide/en/#preserveentrysignatures
 | 
			
		||||
      preserveEntrySignatures: false,
 | 
			
		||||
      plugins: [
 | 
			
		||||
        ignore({
 | 
			
		||||
          files: bundle.emptyPackages({ latestBuild }),
 | 
			
		||||
        }),
 | 
			
		||||
      !isWDS && worker(),
 | 
			
		||||
      !isWDS && dontHashPlugin({ dontHash }),
 | 
			
		||||
      !isWDS && isProdBuild && terser(bundle.terserOptions(latestBuild)),
 | 
			
		||||
      !isWDS &&
 | 
			
		||||
        isStatsBuild &&
 | 
			
		||||
        visualizer({
 | 
			
		||||
          // https://github.com/btd/rollup-plugin-visualizer#options
 | 
			
		||||
          open: true,
 | 
			
		||||
          sourcemap: true,
 | 
			
		||||
        resolve({
 | 
			
		||||
          extensions,
 | 
			
		||||
          preferBuiltins: false,
 | 
			
		||||
          browser: true,
 | 
			
		||||
          rootDir: paths.polymer_dir,
 | 
			
		||||
        }),
 | 
			
		||||
    ].filter(Boolean),
 | 
			
		||||
  },
 | 
			
		||||
  /**
 | 
			
		||||
   * @type { import("rollup").OutputOptions }
 | 
			
		||||
   */
 | 
			
		||||
  outputOptions: {
 | 
			
		||||
    // https://rollupjs.org/guide/en/#outputdir
 | 
			
		||||
    dir: outputPath,
 | 
			
		||||
    // https://rollupjs.org/guide/en/#outputformat
 | 
			
		||||
    format: latestBuild ? "es" : "systemjs",
 | 
			
		||||
    // https://rollupjs.org/guide/en/#outputexternallivebindings
 | 
			
		||||
    externalLiveBindings: false,
 | 
			
		||||
    // https://rollupjs.org/guide/en/#outputentryfilenames
 | 
			
		||||
    // https://rollupjs.org/guide/en/#outputchunkfilenames
 | 
			
		||||
    // https://rollupjs.org/guide/en/#outputassetfilenames
 | 
			
		||||
    entryFileNames:
 | 
			
		||||
      isProdBuild && !isStatsBuild ? "[name]-[hash].js" : "[name].js",
 | 
			
		||||
    chunkFileNames: isProdBuild && !isStatsBuild ? "c.[hash].js" : "[name].js",
 | 
			
		||||
    assetFileNames: isProdBuild && !isStatsBuild ? "a.[hash].js" : "[name].js",
 | 
			
		||||
    // https://rollupjs.org/guide/en/#outputsourcemap
 | 
			
		||||
    sourcemap: isProdBuild ? true : "inline",
 | 
			
		||||
  },
 | 
			
		||||
});
 | 
			
		||||
        commonjs({
 | 
			
		||||
          namedExports: {
 | 
			
		||||
            "js-yaml": ["safeDump", "safeLoad"],
 | 
			
		||||
          },
 | 
			
		||||
        }),
 | 
			
		||||
        json(),
 | 
			
		||||
        babel({
 | 
			
		||||
          ...bundle.babelOptions({ latestBuild }),
 | 
			
		||||
          extensions,
 | 
			
		||||
          exclude: bundle.babelExclude(),
 | 
			
		||||
          babelHelpers: isWDS ? "inline" : "bundled",
 | 
			
		||||
        }),
 | 
			
		||||
        string({
 | 
			
		||||
          // Import certain extensions as strings
 | 
			
		||||
          include: [path.join(paths.polymer_dir, "node_modules/**/*.css")],
 | 
			
		||||
        }),
 | 
			
		||||
        replace(
 | 
			
		||||
          bundle.definedVars({ isProdBuild, latestBuild, defineOverlay })
 | 
			
		||||
        ),
 | 
			
		||||
        !isWDS &&
 | 
			
		||||
          manifest({
 | 
			
		||||
            publicPath,
 | 
			
		||||
          }),
 | 
			
		||||
        !isWDS && worker(),
 | 
			
		||||
        !isWDS && dontHashPlugin({ dontHash }),
 | 
			
		||||
        !isWDS && isProdBuild && terser(bundle.terserOptions(latestBuild)),
 | 
			
		||||
        !isWDS &&
 | 
			
		||||
          isStatsBuild &&
 | 
			
		||||
          visualizer({
 | 
			
		||||
            // https://github.com/btd/rollup-plugin-visualizer#options
 | 
			
		||||
            open: true,
 | 
			
		||||
            sourcemap: true,
 | 
			
		||||
          }),
 | 
			
		||||
      ].filter(Boolean),
 | 
			
		||||
    },
 | 
			
		||||
    /**
 | 
			
		||||
     * @type { import("rollup").OutputOptions }
 | 
			
		||||
     */
 | 
			
		||||
    outputOptions: {
 | 
			
		||||
      // https://rollupjs.org/guide/en/#outputdir
 | 
			
		||||
      dir: outputPath,
 | 
			
		||||
      // https://rollupjs.org/guide/en/#outputformat
 | 
			
		||||
      format: latestBuild ? "es" : "systemjs",
 | 
			
		||||
      // https://rollupjs.org/guide/en/#outputexternallivebindings
 | 
			
		||||
      externalLiveBindings: false,
 | 
			
		||||
      // https://rollupjs.org/guide/en/#outputentryfilenames
 | 
			
		||||
      // https://rollupjs.org/guide/en/#outputchunkfilenames
 | 
			
		||||
      // https://rollupjs.org/guide/en/#outputassetfilenames
 | 
			
		||||
      entryFileNames:
 | 
			
		||||
        isProdBuild && !isStatsBuild ? "[name]-[hash].js" : "[name].js",
 | 
			
		||||
      chunkFileNames:
 | 
			
		||||
        isProdBuild && !isStatsBuild ? "c.[hash].js" : "[name].js",
 | 
			
		||||
      assetFileNames:
 | 
			
		||||
        isProdBuild && !isStatsBuild ? "a.[hash].js" : "[name].js",
 | 
			
		||||
      // https://rollupjs.org/guide/en/#outputsourcemap
 | 
			
		||||
      sourcemap: isProdBuild ? true : "inline",
 | 
			
		||||
    },
 | 
			
		||||
  };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const createAppConfig = ({ isProdBuild, latestBuild, isStatsBuild, isWDS }) =>
 | 
			
		||||
  createRollupConfig(
 | 
			
		||||
const createAppConfig = ({ isProdBuild, latestBuild, isStatsBuild, isWDS }) => {
 | 
			
		||||
  return createRollupConfig(
 | 
			
		||||
    bundle.config.app({
 | 
			
		||||
      isProdBuild,
 | 
			
		||||
      latestBuild,
 | 
			
		||||
@@ -111,24 +121,31 @@ const createAppConfig = ({ isProdBuild, latestBuild, isStatsBuild, isWDS }) =>
 | 
			
		||||
      isWDS,
 | 
			
		||||
    })
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const createDemoConfig = ({ isProdBuild, latestBuild, isStatsBuild }) =>
 | 
			
		||||
  createRollupConfig(
 | 
			
		||||
const createDemoConfig = ({ isProdBuild, latestBuild, isStatsBuild }) => {
 | 
			
		||||
  return createRollupConfig(
 | 
			
		||||
    bundle.config.demo({
 | 
			
		||||
      isProdBuild,
 | 
			
		||||
      latestBuild,
 | 
			
		||||
      isStatsBuild,
 | 
			
		||||
    })
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const createCastConfig = ({ isProdBuild, latestBuild }) =>
 | 
			
		||||
  createRollupConfig(bundle.config.cast({ isProdBuild, latestBuild }));
 | 
			
		||||
const createCastConfig = ({ isProdBuild, latestBuild }) => {
 | 
			
		||||
  return createRollupConfig(bundle.config.cast({ isProdBuild, latestBuild }));
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const createHassioConfig = ({ isProdBuild, latestBuild }) =>
 | 
			
		||||
  createRollupConfig(bundle.config.hassio({ isProdBuild, latestBuild }));
 | 
			
		||||
const createHassioConfig = ({ isProdBuild, latestBuild }) => {
 | 
			
		||||
  return createRollupConfig(bundle.config.hassio({ isProdBuild, latestBuild }));
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const createGalleryConfig = ({ isProdBuild, latestBuild }) =>
 | 
			
		||||
  createRollupConfig(bundle.config.gallery({ isProdBuild, latestBuild }));
 | 
			
		||||
const createGalleryConfig = ({ isProdBuild, latestBuild }) => {
 | 
			
		||||
  return createRollupConfig(
 | 
			
		||||
    bundle.config.gallery({ isProdBuild, latestBuild })
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
module.exports = {
 | 
			
		||||
  createAppConfig,
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,3 @@
 | 
			
		||||
/* eslint-disable @typescript-eslint/no-var-requires */
 | 
			
		||||
const path = require("path");
 | 
			
		||||
const fs = require("fs");
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,10 +1,9 @@
 | 
			
		||||
/* eslint-disable @typescript-eslint/no-var-requires */
 | 
			
		||||
const webpack = require("webpack");
 | 
			
		||||
const path = require("path");
 | 
			
		||||
const TerserPlugin = require("terser-webpack-plugin");
 | 
			
		||||
const { WebpackManifestPlugin } = require("webpack-manifest-plugin");
 | 
			
		||||
const paths = require("./paths.js");
 | 
			
		||||
const bundle = require("./bundle.js");
 | 
			
		||||
const bundle = require("./bundle");
 | 
			
		||||
const log = require("fancy-log");
 | 
			
		||||
 | 
			
		||||
class LogStartCompilePlugin {
 | 
			
		||||
@@ -47,6 +46,7 @@ const createWebpackConfig = ({
 | 
			
		||||
      rules: [
 | 
			
		||||
        {
 | 
			
		||||
          test: /\.m?js$|\.ts$/,
 | 
			
		||||
          exclude: bundle.babelExclude(),
 | 
			
		||||
          use: {
 | 
			
		||||
            loader: "babel-loader",
 | 
			
		||||
            options: bundle.babelOptions({ latestBuild }),
 | 
			
		||||
@@ -94,7 +94,6 @@ const createWebpackConfig = ({
 | 
			
		||||
              ? path.resolve(context, resource)
 | 
			
		||||
              : require.resolve(resource);
 | 
			
		||||
          } catch (err) {
 | 
			
		||||
            // eslint-disable-next-line no-console
 | 
			
		||||
            console.error(
 | 
			
		||||
              "Error in Home Assistant ignore plugin",
 | 
			
		||||
              resource,
 | 
			
		||||
@@ -115,9 +114,8 @@ const createWebpackConfig = ({
 | 
			
		||||
      // We need to change the import of the polyfill for EventTarget, so we replace the polyfill file with our customized one
 | 
			
		||||
      new webpack.NormalModuleReplacementPlugin(
 | 
			
		||||
        new RegExp(
 | 
			
		||||
          path.resolve(
 | 
			
		||||
            paths.polymer_dir,
 | 
			
		||||
            "src/resources/lit-virtualizer/lib/uni-virtualizer/lib/polyfillLoaders/EventTarget.js"
 | 
			
		||||
          require.resolve(
 | 
			
		||||
            "lit-virtualizer/lib/uni-virtualizer/lib/polyfillLoaders/EventTarget.js"
 | 
			
		||||
          )
 | 
			
		||||
        ),
 | 
			
		||||
        path.resolve(paths.polymer_dir, "src/resources/EventTarget-ponyfill.js")
 | 
			
		||||
@@ -126,11 +124,6 @@ const createWebpackConfig = ({
 | 
			
		||||
    ].filter(Boolean),
 | 
			
		||||
    resolve: {
 | 
			
		||||
      extensions: [".ts", ".js", ".json"],
 | 
			
		||||
      alias: {
 | 
			
		||||
        "lit/decorators$": "lit/decorators.js",
 | 
			
		||||
        "lit/directive$": "lit/directive.js",
 | 
			
		||||
        "lit/polyfill-support$": "lit/polyfill-support.js",
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
    output: {
 | 
			
		||||
      filename: ({ chunk }) => {
 | 
			
		||||
@@ -151,24 +144,33 @@ const createWebpackConfig = ({
 | 
			
		||||
  };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const createAppConfig = ({ isProdBuild, latestBuild, isStatsBuild }) =>
 | 
			
		||||
  createWebpackConfig(
 | 
			
		||||
const createAppConfig = ({ isProdBuild, latestBuild, isStatsBuild }) => {
 | 
			
		||||
  return createWebpackConfig(
 | 
			
		||||
    bundle.config.app({ isProdBuild, latestBuild, isStatsBuild })
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const createDemoConfig = ({ isProdBuild, latestBuild, isStatsBuild }) =>
 | 
			
		||||
  createWebpackConfig(
 | 
			
		||||
const createDemoConfig = ({ isProdBuild, latestBuild, isStatsBuild }) => {
 | 
			
		||||
  return createWebpackConfig(
 | 
			
		||||
    bundle.config.demo({ isProdBuild, latestBuild, isStatsBuild })
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const createCastConfig = ({ isProdBuild, latestBuild }) =>
 | 
			
		||||
  createWebpackConfig(bundle.config.cast({ isProdBuild, latestBuild }));
 | 
			
		||||
const createCastConfig = ({ isProdBuild, latestBuild }) => {
 | 
			
		||||
  return createWebpackConfig(bundle.config.cast({ isProdBuild, latestBuild }));
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const createHassioConfig = ({ isProdBuild, latestBuild }) =>
 | 
			
		||||
  createWebpackConfig(bundle.config.hassio({ isProdBuild, latestBuild }));
 | 
			
		||||
const createHassioConfig = ({ isProdBuild, latestBuild }) => {
 | 
			
		||||
  return createWebpackConfig(
 | 
			
		||||
    bundle.config.hassio({ isProdBuild, latestBuild })
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const createGalleryConfig = ({ isProdBuild, latestBuild }) =>
 | 
			
		||||
  createWebpackConfig(bundle.config.gallery({ isProdBuild, latestBuild }));
 | 
			
		||||
const createGalleryConfig = ({ isProdBuild, latestBuild }) => {
 | 
			
		||||
  return createWebpackConfig(
 | 
			
		||||
    bundle.config.gallery({ isProdBuild, latestBuild })
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
module.exports = {
 | 
			
		||||
  createAppConfig,
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,16 @@
 | 
			
		||||
import "@material/mwc-button/mwc-button";
 | 
			
		||||
import "@polymer/paper-item/paper-icon-item";
 | 
			
		||||
import "@polymer/paper-listbox/paper-listbox";
 | 
			
		||||
import { Auth, Connection } from "home-assistant-js-websocket";
 | 
			
		||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
 | 
			
		||||
import { customElement, property, state } from "lit/decorators";
 | 
			
		||||
import {
 | 
			
		||||
  css,
 | 
			
		||||
  CSSResult,
 | 
			
		||||
  customElement,
 | 
			
		||||
  html,
 | 
			
		||||
  LitElement,
 | 
			
		||||
  property,
 | 
			
		||||
  internalProperty,
 | 
			
		||||
  TemplateResult,
 | 
			
		||||
} from "lit-element";
 | 
			
		||||
import { CastManager } from "../../../../src/cast/cast_manager";
 | 
			
		||||
import {
 | 
			
		||||
  castSendShowLovelaceView,
 | 
			
		||||
@@ -25,6 +32,7 @@ import {
 | 
			
		||||
import "../../../../src/layouts/hass-loading-screen";
 | 
			
		||||
import { generateDefaultViewConfig } from "../../../../src/panels/lovelace/common/generate-lovelace-config";
 | 
			
		||||
import "./hc-layout";
 | 
			
		||||
import "@material/mwc-button/mwc-button";
 | 
			
		||||
 | 
			
		||||
@customElement("hc-cast")
 | 
			
		||||
class HcCast extends LitElement {
 | 
			
		||||
@@ -34,9 +42,9 @@ class HcCast extends LitElement {
 | 
			
		||||
 | 
			
		||||
  @property() public castManager!: CastManager;
 | 
			
		||||
 | 
			
		||||
  @state() private askWrite = false;
 | 
			
		||||
  @internalProperty() private askWrite = false;
 | 
			
		||||
 | 
			
		||||
  @state() private lovelaceConfig?: LovelaceConfig | null;
 | 
			
		||||
  @internalProperty() private lovelaceConfig?: LovelaceConfig | null;
 | 
			
		||||
 | 
			
		||||
  protected render(): TemplateResult {
 | 
			
		||||
    if (this.lovelaceConfig === undefined) {
 | 
			
		||||
@@ -46,7 +54,9 @@ class HcCast extends LitElement {
 | 
			
		||||
    const error =
 | 
			
		||||
      this.castManager.castState === "NO_DEVICES_AVAILABLE"
 | 
			
		||||
        ? html`
 | 
			
		||||
            <p>There were no suitable Chromecast devices to cast to found.</p>
 | 
			
		||||
            <p>
 | 
			
		||||
              There were no suitable Chromecast devices to cast to found.
 | 
			
		||||
            </p>
 | 
			
		||||
          `
 | 
			
		||||
        : undefined;
 | 
			
		||||
 | 
			
		||||
@@ -196,7 +206,7 @@ class HcCast extends LitElement {
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static get styles(): CSSResultGroup {
 | 
			
		||||
  static get styles(): CSSResult {
 | 
			
		||||
    return css`
 | 
			
		||||
      .center-item {
 | 
			
		||||
        display: flex;
 | 
			
		||||
 
 | 
			
		||||
@@ -11,8 +11,15 @@ import {
 | 
			
		||||
  getAuth,
 | 
			
		||||
  getAuthOptions,
 | 
			
		||||
} from "home-assistant-js-websocket";
 | 
			
		||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
 | 
			
		||||
import { customElement, state } from "lit/decorators";
 | 
			
		||||
import {
 | 
			
		||||
  css,
 | 
			
		||||
  CSSResult,
 | 
			
		||||
  customElement,
 | 
			
		||||
  html,
 | 
			
		||||
  LitElement,
 | 
			
		||||
  TemplateResult,
 | 
			
		||||
  internalProperty,
 | 
			
		||||
} from "lit-element";
 | 
			
		||||
import { CastManager, getCastManager } from "../../../../src/cast/cast_manager";
 | 
			
		||||
import { castSendShowDemo } from "../../../../src/cast/receiver_messages";
 | 
			
		||||
import {
 | 
			
		||||
@@ -53,19 +60,19 @@ const INTRO = html`
 | 
			
		||||
 | 
			
		||||
@customElement("hc-connect")
 | 
			
		||||
export class HcConnect extends LitElement {
 | 
			
		||||
  @state() private loading = false;
 | 
			
		||||
  @internalProperty() private loading = false;
 | 
			
		||||
 | 
			
		||||
  // If we had stored credentials but we cannot connect,
 | 
			
		||||
  // show a screen asking retry or logout.
 | 
			
		||||
  @state() private cannotConnect = false;
 | 
			
		||||
  @internalProperty() private cannotConnect = false;
 | 
			
		||||
 | 
			
		||||
  @state() private error?: string | TemplateResult;
 | 
			
		||||
  @internalProperty() private error?: string | TemplateResult;
 | 
			
		||||
 | 
			
		||||
  @state() private auth?: Auth;
 | 
			
		||||
  @internalProperty() private auth?: Auth;
 | 
			
		||||
 | 
			
		||||
  @state() private connection?: Connection;
 | 
			
		||||
  @internalProperty() private connection?: Connection;
 | 
			
		||||
 | 
			
		||||
  @state() private castManager?: CastManager | null;
 | 
			
		||||
  @internalProperty() private castManager?: CastManager | null;
 | 
			
		||||
 | 
			
		||||
  private openDemo = false;
 | 
			
		||||
 | 
			
		||||
@@ -79,7 +86,9 @@ export class HcConnect extends LitElement {
 | 
			
		||||
          </div>
 | 
			
		||||
          <div class="card-actions">
 | 
			
		||||
            <a href="/">
 | 
			
		||||
              <mwc-button> Retry </mwc-button>
 | 
			
		||||
              <mwc-button>
 | 
			
		||||
                Retry
 | 
			
		||||
              </mwc-button>
 | 
			
		||||
            </a>
 | 
			
		||||
            <div class="spacer"></div>
 | 
			
		||||
            <mwc-button @click=${this._handleLogout}>Log out</mwc-button>
 | 
			
		||||
@@ -290,7 +299,7 @@ export class HcConnect extends LitElement {
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static get styles(): CSSResultGroup {
 | 
			
		||||
  static get styles(): CSSResult {
 | 
			
		||||
    return css`
 | 
			
		||||
      .card-content a {
 | 
			
		||||
        color: var(--primary-color);
 | 
			
		||||
 
 | 
			
		||||
@@ -4,8 +4,15 @@ import {
 | 
			
		||||
  getUser,
 | 
			
		||||
  HassUser,
 | 
			
		||||
} from "home-assistant-js-websocket";
 | 
			
		||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
 | 
			
		||||
import { customElement, property } from "lit/decorators";
 | 
			
		||||
import {
 | 
			
		||||
  css,
 | 
			
		||||
  CSSResult,
 | 
			
		||||
  customElement,
 | 
			
		||||
  html,
 | 
			
		||||
  LitElement,
 | 
			
		||||
  property,
 | 
			
		||||
  TemplateResult,
 | 
			
		||||
} from "lit-element";
 | 
			
		||||
import "../../../../src/components/ha-card";
 | 
			
		||||
 | 
			
		||||
@customElement("hc-layout")
 | 
			
		||||
@@ -62,7 +69,7 @@ class HcLayout extends LitElement {
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static get styles(): CSSResultGroup {
 | 
			
		||||
  static get styles(): CSSResult {
 | 
			
		||||
    return css`
 | 
			
		||||
      :host {
 | 
			
		||||
        display: flex;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,10 @@
 | 
			
		||||
import { html, TemplateResult } from "lit";
 | 
			
		||||
import { customElement, property, state } from "lit/decorators";
 | 
			
		||||
import {
 | 
			
		||||
  customElement,
 | 
			
		||||
  html,
 | 
			
		||||
  internalProperty,
 | 
			
		||||
  property,
 | 
			
		||||
  TemplateResult,
 | 
			
		||||
} from "lit-element";
 | 
			
		||||
import { mockHistory } from "../../../../demo/src/stubs/history";
 | 
			
		||||
import { LovelaceConfig } from "../../../../src/data/lovelace";
 | 
			
		||||
import {
 | 
			
		||||
@@ -16,7 +21,7 @@ import "./hc-lovelace";
 | 
			
		||||
class HcDemo extends HassElement {
 | 
			
		||||
  @property({ attribute: false }) public lovelacePath!: string;
 | 
			
		||||
 | 
			
		||||
  @state() private _lovelaceConfig?: LovelaceConfig;
 | 
			
		||||
  @internalProperty() private _lovelaceConfig?: LovelaceConfig;
 | 
			
		||||
 | 
			
		||||
  protected render(): TemplateResult {
 | 
			
		||||
    if (!this._lovelaceConfig) {
 | 
			
		||||
@@ -33,10 +38,10 @@ class HcDemo extends HassElement {
 | 
			
		||||
 | 
			
		||||
  protected firstUpdated(changedProps) {
 | 
			
		||||
    super.firstUpdated(changedProps);
 | 
			
		||||
    this._initializeHass();
 | 
			
		||||
    this._initialize();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private async _initializeHass() {
 | 
			
		||||
  private async _initialize() {
 | 
			
		||||
    const initial: Partial<MockHomeAssistant> = {
 | 
			
		||||
      // Override updateHass so that the correct hass lifecycle methods are called
 | 
			
		||||
      updateHass: (hassUpdate: Partial<HomeAssistant>) =>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,12 @@
 | 
			
		||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
 | 
			
		||||
import { customElement, property } from "lit/decorators";
 | 
			
		||||
import {
 | 
			
		||||
  css,
 | 
			
		||||
  CSSResult,
 | 
			
		||||
  customElement,
 | 
			
		||||
  html,
 | 
			
		||||
  LitElement,
 | 
			
		||||
  property,
 | 
			
		||||
  TemplateResult,
 | 
			
		||||
} from "lit-element";
 | 
			
		||||
import { HomeAssistant } from "../../../../src/types";
 | 
			
		||||
 | 
			
		||||
@customElement("hc-launch-screen")
 | 
			
		||||
@@ -22,7 +29,7 @@ class HcLaunchScreen extends LitElement {
 | 
			
		||||
    `;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static get styles(): CSSResultGroup {
 | 
			
		||||
  static get styles(): CSSResult {
 | 
			
		||||
    return css`
 | 
			
		||||
      :host {
 | 
			
		||||
        display: block;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,12 @@
 | 
			
		||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
 | 
			
		||||
import { customElement, property } from "lit/decorators";
 | 
			
		||||
import {
 | 
			
		||||
  css,
 | 
			
		||||
  CSSResult,
 | 
			
		||||
  customElement,
 | 
			
		||||
  html,
 | 
			
		||||
  LitElement,
 | 
			
		||||
  property,
 | 
			
		||||
  TemplateResult,
 | 
			
		||||
} from "lit-element";
 | 
			
		||||
import { LovelaceConfig } from "../../../../src/data/lovelace";
 | 
			
		||||
import { Lovelace } from "../../../../src/panels/lovelace/types";
 | 
			
		||||
import "../../../../src/panels/lovelace/views/hui-view";
 | 
			
		||||
@@ -84,7 +91,7 @@ class HcLovelace extends LitElement {
 | 
			
		||||
    return undefined;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static get styles(): CSSResultGroup {
 | 
			
		||||
  static get styles(): CSSResult {
 | 
			
		||||
    return css`
 | 
			
		||||
      :host {
 | 
			
		||||
        min-height: 100vh;
 | 
			
		||||
 
 | 
			
		||||
@@ -3,8 +3,12 @@ import {
 | 
			
		||||
  getAuth,
 | 
			
		||||
  UnsubscribeFunc,
 | 
			
		||||
} from "home-assistant-js-websocket";
 | 
			
		||||
import { html, TemplateResult } from "lit";
 | 
			
		||||
import { customElement, state } from "lit/decorators";
 | 
			
		||||
import {
 | 
			
		||||
  customElement,
 | 
			
		||||
  html,
 | 
			
		||||
  internalProperty,
 | 
			
		||||
  TemplateResult,
 | 
			
		||||
} from "lit-element";
 | 
			
		||||
import { CAST_NS } from "../../../../src/cast/const";
 | 
			
		||||
import {
 | 
			
		||||
  ConnectMessage,
 | 
			
		||||
@@ -32,13 +36,13 @@ let resourcesLoaded = false;
 | 
			
		||||
 | 
			
		||||
@customElement("hc-main")
 | 
			
		||||
export class HcMain extends HassElement {
 | 
			
		||||
  @state() private _showDemo = false;
 | 
			
		||||
  @internalProperty() private _showDemo = false;
 | 
			
		||||
 | 
			
		||||
  @state() private _lovelaceConfig?: LovelaceConfig;
 | 
			
		||||
  @internalProperty() private _lovelaceConfig?: LovelaceConfig;
 | 
			
		||||
 | 
			
		||||
  @state() private _lovelacePath: string | number | null = null;
 | 
			
		||||
  @internalProperty() private _lovelacePath: string | number | null = null;
 | 
			
		||||
 | 
			
		||||
  @state() private _error?: string;
 | 
			
		||||
  @internalProperty() private _error?: string;
 | 
			
		||||
 | 
			
		||||
  private _unsubLovelace?: UnsubscribeFunc;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -246,15 +246,11 @@ export const demoEntitiesArsaboo: DemoConfig["entities"] = (localize) =>
 | 
			
		||||
 | 
			
		||||
    "light.living_room_lights": {
 | 
			
		||||
      entity_id: "light.living_room_lights",
 | 
			
		||||
      state: "on",
 | 
			
		||||
      state: "off",
 | 
			
		||||
      attributes: {
 | 
			
		||||
        min_mireds: 111,
 | 
			
		||||
        max_mireds: 400,
 | 
			
		||||
        brightness: 175,
 | 
			
		||||
        color_temp: 300,
 | 
			
		||||
        supported_color_modes: ["brightness", "color_temp"],
 | 
			
		||||
        friendly_name: "Living Room Lights",
 | 
			
		||||
        color_mode: "color_temp",
 | 
			
		||||
        supported_features: 55,
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
@@ -267,27 +263,13 @@ export const demoEntitiesArsaboo: DemoConfig["entities"] = (localize) =>
 | 
			
		||||
    },
 | 
			
		||||
    "light.kitchen_lights": {
 | 
			
		||||
      entity_id: "light.kitchen_lights",
 | 
			
		||||
      state: "on",
 | 
			
		||||
      attributes: {
 | 
			
		||||
        min_mireds: 111,
 | 
			
		||||
        max_mireds: 400,
 | 
			
		||||
        brightness: 200,
 | 
			
		||||
        rgb_color: [255, 175, 96],
 | 
			
		||||
        supported_color_modes: ["brightness", "color_temp", "rgb"],
 | 
			
		||||
        color_mode: "rgb",
 | 
			
		||||
        friendly_name: "Kitchen Lights",
 | 
			
		||||
        supported_features: 55,
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
    "light.lifx5": {
 | 
			
		||||
      entity_id: "light.lifx5",
 | 
			
		||||
      state: "off",
 | 
			
		||||
      attributes: {
 | 
			
		||||
        supported_color_modes: ["brightness"],
 | 
			
		||||
        friendly_name: "Garage Lights",
 | 
			
		||||
        friendly_name: "Kitchen Lights",
 | 
			
		||||
        supported_features: 1,
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    "sensor.plexspy": {
 | 
			
		||||
      entity_id: "sensor.plexspy",
 | 
			
		||||
      state: "0",
 | 
			
		||||
@@ -500,6 +482,16 @@ export const demoEntitiesArsaboo: DemoConfig["entities"] = (localize) =>
 | 
			
		||||
        icon: "hademo:history",
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
    "light.lifx5": {
 | 
			
		||||
      entity_id: "light.lifx5",
 | 
			
		||||
      state: "on",
 | 
			
		||||
      attributes: {
 | 
			
		||||
        min_mireds: 111,
 | 
			
		||||
        max_mireds: 400,
 | 
			
		||||
        friendly_name: "Garage Lights",
 | 
			
		||||
        supported_features: 55,
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
    "sensor.alok_to_home": {
 | 
			
		||||
      entity_id: "sensor.alok_to_home",
 | 
			
		||||
      state: "41",
 | 
			
		||||
 
 | 
			
		||||
@@ -1114,9 +1114,6 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
 | 
			
		||||
        min_mireds: 153,
 | 
			
		||||
        max_mireds: 500,
 | 
			
		||||
        brightness: 63,
 | 
			
		||||
        color_temp: 200,
 | 
			
		||||
        supported_color_modes: ["brightness", "color_temp", "rgb"],
 | 
			
		||||
        color_mode: "color_temp",
 | 
			
		||||
        friendly_name: "Upstairs lights",
 | 
			
		||||
        supported_features: 63,
 | 
			
		||||
        custom_ui_state_card: "state-card-custom-ui",
 | 
			
		||||
@@ -1128,7 +1125,6 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
 | 
			
		||||
      attributes: {
 | 
			
		||||
        friendly_name: "Walk in closet lights",
 | 
			
		||||
        supported_features: 41,
 | 
			
		||||
        supported_color_modes: ["brightness", "color_temp"],
 | 
			
		||||
        custom_ui_state_card: "state-card-custom-ui",
 | 
			
		||||
        icon: "mdi:wall-sconce",
 | 
			
		||||
      },
 | 
			
		||||
@@ -1140,8 +1136,6 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
 | 
			
		||||
        brightness: 254,
 | 
			
		||||
        friendly_name: "Outdoor lights",
 | 
			
		||||
        supported_features: 41,
 | 
			
		||||
        supported_color_modes: ["brightness"],
 | 
			
		||||
        color_mode: "brightness",
 | 
			
		||||
        custom_ui_state_card: "state-card-custom-ui",
 | 
			
		||||
        icon: "mdi:wall-sconce",
 | 
			
		||||
      },
 | 
			
		||||
@@ -1154,8 +1148,6 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
 | 
			
		||||
        max_mireds: 500,
 | 
			
		||||
        brightness: 128,
 | 
			
		||||
        color_temp: 366,
 | 
			
		||||
        supported_color_modes: ["brightness", "color_temp", "rgb"],
 | 
			
		||||
        color_mode: "color_temp",
 | 
			
		||||
        effect_list: ["colorloop"],
 | 
			
		||||
        friendly_name: "Downstairs lights",
 | 
			
		||||
        supported_features: 63,
 | 
			
		||||
@@ -1315,7 +1307,6 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
 | 
			
		||||
      attributes: {
 | 
			
		||||
        min_mireds: 153,
 | 
			
		||||
        max_mireds: 500,
 | 
			
		||||
        supported_color_modes: ["brightness", "color_temp"],
 | 
			
		||||
        is_deconz_group: false,
 | 
			
		||||
        friendly_name: "Bedside Lamp",
 | 
			
		||||
        supported_features: 63,
 | 
			
		||||
@@ -1329,7 +1320,6 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
 | 
			
		||||
      attributes: {
 | 
			
		||||
        min_mireds: 153,
 | 
			
		||||
        max_mireds: 500,
 | 
			
		||||
        supported_color_modes: ["brightness", "color_temp"],
 | 
			
		||||
        is_deconz_group: false,
 | 
			
		||||
        friendly_name: "Floorlamp Reading Light",
 | 
			
		||||
        supported_features: 43,
 | 
			
		||||
@@ -1345,8 +1335,6 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
 | 
			
		||||
        max_mireds: 500,
 | 
			
		||||
        brightness: 128,
 | 
			
		||||
        color_temp: 366,
 | 
			
		||||
        supported_color_modes: ["brightness", "color_temp", "rgb"],
 | 
			
		||||
        color_mode: "color_temp",
 | 
			
		||||
        effect_list: ["colorloop"],
 | 
			
		||||
        is_deconz_group: false,
 | 
			
		||||
        friendly_name: "Hallway window light",
 | 
			
		||||
@@ -1361,7 +1349,6 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
 | 
			
		||||
      attributes: {
 | 
			
		||||
        brightness: 77,
 | 
			
		||||
        is_deconz_group: false,
 | 
			
		||||
        supported_color_modes: ["brightness"],
 | 
			
		||||
        friendly_name: "Isa Ceiling Light",
 | 
			
		||||
        supported_features: 41,
 | 
			
		||||
        custom_ui_state_card: "state-card-custom-ui",
 | 
			
		||||
@@ -1376,8 +1363,6 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
 | 
			
		||||
        max_mireds: 500,
 | 
			
		||||
        brightness: 150,
 | 
			
		||||
        color_temp: 366,
 | 
			
		||||
        supported_color_modes: ["brightness", "color_temp"],
 | 
			
		||||
        color_mode: "color_temp",
 | 
			
		||||
        effect_list: ["colorloop"],
 | 
			
		||||
        is_deconz_group: false,
 | 
			
		||||
        friendly_name: "Floorlamp",
 | 
			
		||||
@@ -1392,7 +1377,6 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
 | 
			
		||||
      attributes: {
 | 
			
		||||
        friendly_name: "Bedroom Ceiling Light",
 | 
			
		||||
        supported_features: 41,
 | 
			
		||||
        supported_color_modes: ["brightness"],
 | 
			
		||||
        custom_ui_state_card: "state-card-custom-ui",
 | 
			
		||||
        icon: "mdi:ceiling-light",
 | 
			
		||||
      },
 | 
			
		||||
@@ -1403,7 +1387,6 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
 | 
			
		||||
      attributes: {
 | 
			
		||||
        friendly_name: "Nightlight",
 | 
			
		||||
        supported_features: 17,
 | 
			
		||||
        supported_color_modes: ["brightness"],
 | 
			
		||||
        custom_ui_state_card: "state-card-custom-ui",
 | 
			
		||||
        icon: "mdi:lamp",
 | 
			
		||||
      },
 | 
			
		||||
@@ -1770,7 +1753,6 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
 | 
			
		||||
        power_consumption: 2.2,
 | 
			
		||||
        friendly_name: "Upstairs Hallway Light",
 | 
			
		||||
        supported_features: 33,
 | 
			
		||||
        supported_color_modes: ["brightness"],
 | 
			
		||||
        custom_ui_state_card: "state-card-custom-ui",
 | 
			
		||||
        icon: "mdi:ceiling-light",
 | 
			
		||||
      },
 | 
			
		||||
@@ -1786,7 +1768,6 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
 | 
			
		||||
        power_consumption: 0,
 | 
			
		||||
        friendly_name: "Dining Room Light",
 | 
			
		||||
        supported_features: 33,
 | 
			
		||||
        supported_color_modes: ["brightness"],
 | 
			
		||||
        custom_ui_state_card: "state-card-custom-ui",
 | 
			
		||||
        icon: "mdi:ceiling-light",
 | 
			
		||||
      },
 | 
			
		||||
@@ -1802,7 +1783,6 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
 | 
			
		||||
        power_consumption: 0,
 | 
			
		||||
        friendly_name: "Living room Spotlights",
 | 
			
		||||
        supported_features: 33,
 | 
			
		||||
        supported_color_modes: ["brightness"],
 | 
			
		||||
        custom_ui_state_card: "state-card-custom-ui",
 | 
			
		||||
        icon: "mdi:track-light",
 | 
			
		||||
      },
 | 
			
		||||
@@ -1819,7 +1799,6 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
 | 
			
		||||
        power_consumption: 2.5,
 | 
			
		||||
        friendly_name: "Passage Lights",
 | 
			
		||||
        supported_features: 33,
 | 
			
		||||
        supported_color_modes: ["brightness"],
 | 
			
		||||
        custom_ui_state_card: "state-card-custom-ui",
 | 
			
		||||
        icon: "mdi:track-light",
 | 
			
		||||
      },
 | 
			
		||||
@@ -1864,7 +1843,6 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
 | 
			
		||||
        power_consumption: 37.4,
 | 
			
		||||
        friendly_name: "Kitchen Lights",
 | 
			
		||||
        supported_features: 33,
 | 
			
		||||
        supported_color_modes: ["brightness"],
 | 
			
		||||
        custom_ui_state_card: "state-card-custom-ui",
 | 
			
		||||
        icon: "mdi:track-light",
 | 
			
		||||
      },
 | 
			
		||||
 
 | 
			
		||||
@@ -440,43 +440,57 @@ export const demoLovelaceTeachingbirds: DemoConfig["lovelace"] = () => ({
 | 
			
		||||
          type: "horizontal-stack",
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          type: "grid",
 | 
			
		||||
          columns: 2,
 | 
			
		||||
          cards: [
 | 
			
		||||
            {
 | 
			
		||||
              graph: "line",
 | 
			
		||||
              type: "sensor",
 | 
			
		||||
              entity: "sensor.temperature_bedroom",
 | 
			
		||||
              cards: [
 | 
			
		||||
                {
 | 
			
		||||
                  graph: "line",
 | 
			
		||||
                  type: "sensor",
 | 
			
		||||
                  entity: "sensor.temperature_bedroom",
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                  graph: "line",
 | 
			
		||||
                  type: "sensor",
 | 
			
		||||
                  name: "S's room",
 | 
			
		||||
                  entity: "sensor.temperature_stefan",
 | 
			
		||||
                },
 | 
			
		||||
              ],
 | 
			
		||||
              type: "horizontal-stack",
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
              graph: "line",
 | 
			
		||||
              type: "sensor",
 | 
			
		||||
              name: "S's room",
 | 
			
		||||
              entity: "sensor.temperature_stefan",
 | 
			
		||||
              cards: [
 | 
			
		||||
                {
 | 
			
		||||
                  graph: "line",
 | 
			
		||||
                  type: "sensor",
 | 
			
		||||
                  entity: "sensor.temperature_passage",
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                  graph: "line",
 | 
			
		||||
                  type: "sensor",
 | 
			
		||||
                  name: "Bathroom",
 | 
			
		||||
                  entity: "sensor.temperature_downstairs_bathroom",
 | 
			
		||||
                },
 | 
			
		||||
              ],
 | 
			
		||||
              type: "horizontal-stack",
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
              graph: "line",
 | 
			
		||||
              type: "sensor",
 | 
			
		||||
              entity: "sensor.temperature_passage",
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
              graph: "line",
 | 
			
		||||
              type: "sensor",
 | 
			
		||||
              name: "Bathroom",
 | 
			
		||||
              entity: "sensor.temperature_downstairs_bathroom",
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
              graph: "line",
 | 
			
		||||
              type: "sensor",
 | 
			
		||||
              entity: "sensor.temperature_storage",
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
              graph: "line",
 | 
			
		||||
              type: "sensor",
 | 
			
		||||
              name: "Refrigerator",
 | 
			
		||||
              entity: "sensor.refrigerator",
 | 
			
		||||
              cards: [
 | 
			
		||||
                {
 | 
			
		||||
                  graph: "line",
 | 
			
		||||
                  type: "sensor",
 | 
			
		||||
                  entity: "sensor.temperature_storage",
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                  graph: "line",
 | 
			
		||||
                  type: "sensor",
 | 
			
		||||
                  name: "Refrigerator",
 | 
			
		||||
                  entity: "sensor.refrigerator",
 | 
			
		||||
                },
 | 
			
		||||
              ],
 | 
			
		||||
              type: "horizontal-stack",
 | 
			
		||||
            },
 | 
			
		||||
          ],
 | 
			
		||||
          type: "vertical-stack",
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          entities: [
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/* eslint-disable */
 | 
			
		||||
import { LitElement } from "lit";
 | 
			
		||||
import { LitElement } from "lit-element";
 | 
			
		||||
import "./card-tools";
 | 
			
		||||
 | 
			
		||||
class CardModder extends LitElement {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/* eslint-disable */
 | 
			
		||||
import { html, LitElement } from "lit";
 | 
			
		||||
import { html, LitElement } from "lit-element";
 | 
			
		||||
 | 
			
		||||
if (!window.cardTools) {
 | 
			
		||||
  const version = 0.2;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,12 @@
 | 
			
		||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
 | 
			
		||||
import { customElement, state } from "lit/decorators";
 | 
			
		||||
import {
 | 
			
		||||
  css,
 | 
			
		||||
  CSSResult,
 | 
			
		||||
  customElement,
 | 
			
		||||
  html,
 | 
			
		||||
  internalProperty,
 | 
			
		||||
  LitElement,
 | 
			
		||||
  TemplateResult,
 | 
			
		||||
} from "lit-element";
 | 
			
		||||
import { CastManager } from "../../../src/cast/cast_manager";
 | 
			
		||||
import { castSendShowDemo } from "../../../src/cast/receiver_messages";
 | 
			
		||||
import "../../../src/components/ha-icon";
 | 
			
		||||
@@ -13,7 +20,7 @@ import { HomeAssistant } from "../../../src/types";
 | 
			
		||||
class CastDemoRow extends LitElement implements LovelaceRow {
 | 
			
		||||
  public hass!: HomeAssistant;
 | 
			
		||||
 | 
			
		||||
  @state() private _castManager?: CastManager | null;
 | 
			
		||||
  @internalProperty() private _castManager?: CastManager | null;
 | 
			
		||||
 | 
			
		||||
  public setConfig(_config: CastConfig): void {
 | 
			
		||||
    // No config possible.
 | 
			
		||||
@@ -66,7 +73,7 @@ class CastDemoRow extends LitElement implements LovelaceRow {
 | 
			
		||||
    this.style.display = this._castManager ? "" : "none";
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static get styles(): CSSResultGroup {
 | 
			
		||||
  static get styles(): CSSResult {
 | 
			
		||||
    return css`
 | 
			
		||||
      :host {
 | 
			
		||||
        display: flex;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,14 @@
 | 
			
		||||
import "@material/mwc-button";
 | 
			
		||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
 | 
			
		||||
import { property, state } from "lit/decorators";
 | 
			
		||||
import { until } from "lit/directives/until";
 | 
			
		||||
import {
 | 
			
		||||
  css,
 | 
			
		||||
  CSSResult,
 | 
			
		||||
  html,
 | 
			
		||||
  internalProperty,
 | 
			
		||||
  LitElement,
 | 
			
		||||
  property,
 | 
			
		||||
  TemplateResult,
 | 
			
		||||
} from "lit-element";
 | 
			
		||||
import { until } from "lit-html/directives/until";
 | 
			
		||||
import "../../../src/components/ha-card";
 | 
			
		||||
import "../../../src/components/ha-circular-progress";
 | 
			
		||||
import { LovelaceCardConfig } from "../../../src/data/lovelace";
 | 
			
		||||
@@ -19,7 +26,7 @@ export class HADemoCard extends LitElement implements LovelaceCard {
 | 
			
		||||
 | 
			
		||||
  @property({ attribute: false }) public hass!: MockHomeAssistant;
 | 
			
		||||
 | 
			
		||||
  @state() private _switching?: boolean;
 | 
			
		||||
  @internalProperty() private _switching?: boolean;
 | 
			
		||||
 | 
			
		||||
  private _hidden = localStorage.hide_demo_card;
 | 
			
		||||
 | 
			
		||||
@@ -106,7 +113,7 @@ export class HADemoCard extends LitElement implements LovelaceCard {
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static get styles(): CSSResultGroup {
 | 
			
		||||
  static get styles(): CSSResult[] {
 | 
			
		||||
    return [
 | 
			
		||||
      css`
 | 
			
		||||
        a {
 | 
			
		||||
 
 | 
			
		||||
@@ -22,9 +22,9 @@ import { mockTemplate } from "./stubs/template";
 | 
			
		||||
import { mockTranslations } from "./stubs/translations";
 | 
			
		||||
 | 
			
		||||
class HaDemo extends HomeAssistantAppEl {
 | 
			
		||||
  protected async _initializeHass() {
 | 
			
		||||
  protected async _initialize() {
 | 
			
		||||
    const initial: Partial<MockHomeAssistant> = {
 | 
			
		||||
      panelUrl: (this as any)._panelUrl,
 | 
			
		||||
      panelUrl: (this as any).panelUrl,
 | 
			
		||||
      // Override updateHass so that the correct hass lifecycle methods are called
 | 
			
		||||
      updateHass: (hassUpdate: Partial<HomeAssistant>) =>
 | 
			
		||||
        this._updateHass(hassUpdate),
 | 
			
		||||
@@ -70,7 +70,7 @@ class HaDemo extends HomeAssistantAppEl {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        e.preventDefault();
 | 
			
		||||
        navigate(href);
 | 
			
		||||
        navigate(this, href);
 | 
			
		||||
      },
 | 
			
		||||
      { capture: true }
 | 
			
		||||
    );
 | 
			
		||||
 
 | 
			
		||||
@@ -3,6 +3,8 @@ import { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
 | 
			
		||||
export const mockTranslations = (hass: MockHomeAssistant) => {
 | 
			
		||||
  hass.mockWS(
 | 
			
		||||
    "frontend/get_translations",
 | 
			
		||||
    (/* msg: {language: string, category: string} */) => ({ resources: {} })
 | 
			
		||||
    (/* msg: {language: string, category: string} */) => {
 | 
			
		||||
      return { resources: {} };
 | 
			
		||||
    }
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
 | 
			
		||||
/* eslint-plugin-disable lit */
 | 
			
		||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
 | 
			
		||||
import { load } from "js-yaml";
 | 
			
		||||
import { safeLoad } from "js-yaml";
 | 
			
		||||
import { createCardElement } from "../../../src/panels/lovelace/create-element/create-card-element";
 | 
			
		||||
 | 
			
		||||
class DemoCard extends PolymerElement {
 | 
			
		||||
@@ -15,10 +15,6 @@ class DemoCard extends PolymerElement {
 | 
			
		||||
          margin: 0 0 20px;
 | 
			
		||||
          color: var(--primary-color);
 | 
			
		||||
        }
 | 
			
		||||
        h2 small {
 | 
			
		||||
          font-size: 0.5em;
 | 
			
		||||
          color: var(--primary-text-color);
 | 
			
		||||
        }
 | 
			
		||||
        #card {
 | 
			
		||||
          max-width: 400px;
 | 
			
		||||
          width: 100vw;
 | 
			
		||||
@@ -38,12 +34,7 @@ class DemoCard extends PolymerElement {
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      </style>
 | 
			
		||||
      <h2>
 | 
			
		||||
        [[config.heading]]
 | 
			
		||||
        <template is="dom-if" if="[[_size]]">
 | 
			
		||||
          <small>(size [[_size]])</small>
 | 
			
		||||
        </template>
 | 
			
		||||
      </h2>
 | 
			
		||||
      <h2>[[config.heading]]</h2>
 | 
			
		||||
      <div class="root">
 | 
			
		||||
        <div id="card"></div>
 | 
			
		||||
        <template is="dom-if" if="[[showConfig]]">
 | 
			
		||||
@@ -64,9 +55,6 @@ class DemoCard extends PolymerElement {
 | 
			
		||||
        observer: "_configChanged",
 | 
			
		||||
      },
 | 
			
		||||
      showConfig: Boolean,
 | 
			
		||||
      _size: {
 | 
			
		||||
        type: Number,
 | 
			
		||||
      },
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -80,19 +68,8 @@ class DemoCard extends PolymerElement {
 | 
			
		||||
      card.removeChild(card.lastChild);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const el = this._createCardElement(load(config.config)[0]);
 | 
			
		||||
    const el = this._createCardElement(safeLoad(config.config)[0]);
 | 
			
		||||
    card.appendChild(el);
 | 
			
		||||
    this._getSize(el);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async _getSize(el) {
 | 
			
		||||
    await customElements.whenDefined(el.localName);
 | 
			
		||||
 | 
			
		||||
    if (!("getCardSize" in el)) {
 | 
			
		||||
      this._size = undefined;
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    this._size = await el.getCardSize();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  _createCardElement(cardConfig) {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,12 @@
 | 
			
		||||
import { dump } from "js-yaml";
 | 
			
		||||
import { html, css, LitElement, TemplateResult } from "lit";
 | 
			
		||||
import { customElement, property } from "lit/decorators";
 | 
			
		||||
import { safeDump } from "js-yaml";
 | 
			
		||||
import {
 | 
			
		||||
  customElement,
 | 
			
		||||
  html,
 | 
			
		||||
  css,
 | 
			
		||||
  LitElement,
 | 
			
		||||
  TemplateResult,
 | 
			
		||||
  property,
 | 
			
		||||
} from "lit-element";
 | 
			
		||||
import "../../../src/components/ha-card";
 | 
			
		||||
import { describeAction } from "../../../src/data/script_i18n";
 | 
			
		||||
import { provideHass } from "../../../src/fake_data/provide_hass";
 | 
			
		||||
@@ -56,7 +62,7 @@ export class DemoAutomationDescribeAction extends LitElement {
 | 
			
		||||
          (conf) => html`
 | 
			
		||||
            <div class="action">
 | 
			
		||||
              <span>${describeAction(this.hass, conf as any)}</span>
 | 
			
		||||
              <pre>${dump(conf)}</pre>
 | 
			
		||||
              <pre>${safeDump(conf)}</pre>
 | 
			
		||||
            </div>
 | 
			
		||||
          `
 | 
			
		||||
        )}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,11 @@
 | 
			
		||||
import { dump } from "js-yaml";
 | 
			
		||||
import { html, css, LitElement, TemplateResult } from "lit";
 | 
			
		||||
import { customElement } from "lit/decorators";
 | 
			
		||||
import { safeDump } from "js-yaml";
 | 
			
		||||
import {
 | 
			
		||||
  customElement,
 | 
			
		||||
  html,
 | 
			
		||||
  css,
 | 
			
		||||
  LitElement,
 | 
			
		||||
  TemplateResult,
 | 
			
		||||
} from "lit-element";
 | 
			
		||||
import "../../../src/components/ha-card";
 | 
			
		||||
import { describeCondition } from "../../../src/data/automation_i18n";
 | 
			
		||||
 | 
			
		||||
@@ -26,7 +31,7 @@ export class DemoAutomationDescribeCondition extends LitElement {
 | 
			
		||||
          (conf) => html`
 | 
			
		||||
            <div class="condition">
 | 
			
		||||
              <span>${describeCondition(conf as any)}</span>
 | 
			
		||||
              <pre>${dump(conf)}</pre>
 | 
			
		||||
              <pre>${safeDump(conf)}</pre>
 | 
			
		||||
            </div>
 | 
			
		||||
          `
 | 
			
		||||
        )}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,11 @@
 | 
			
		||||
import { dump } from "js-yaml";
 | 
			
		||||
import { html, css, LitElement, TemplateResult } from "lit";
 | 
			
		||||
import { customElement } from "lit/decorators";
 | 
			
		||||
import { safeDump } from "js-yaml";
 | 
			
		||||
import {
 | 
			
		||||
  customElement,
 | 
			
		||||
  html,
 | 
			
		||||
  css,
 | 
			
		||||
  LitElement,
 | 
			
		||||
  TemplateResult,
 | 
			
		||||
} from "lit-element";
 | 
			
		||||
import "../../../src/components/ha-card";
 | 
			
		||||
import { describeTrigger } from "../../../src/data/automation_i18n";
 | 
			
		||||
 | 
			
		||||
@@ -29,7 +34,7 @@ export class DemoAutomationDescribeTrigger extends LitElement {
 | 
			
		||||
          (conf) => html`
 | 
			
		||||
            <div class="trigger">
 | 
			
		||||
              <span>${describeTrigger(conf as any)}</span>
 | 
			
		||||
              <pre>${dump(conf)}</pre>
 | 
			
		||||
              <pre>${safeDump(conf)}</pre>
 | 
			
		||||
            </div>
 | 
			
		||||
          `
 | 
			
		||||
        )}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,11 @@
 | 
			
		||||
import { html, css, LitElement, TemplateResult } from "lit";
 | 
			
		||||
import { customElement, property } from "lit/decorators";
 | 
			
		||||
import {
 | 
			
		||||
  customElement,
 | 
			
		||||
  html,
 | 
			
		||||
  css,
 | 
			
		||||
  LitElement,
 | 
			
		||||
  TemplateResult,
 | 
			
		||||
  property,
 | 
			
		||||
} from "lit-element";
 | 
			
		||||
import "../../../src/components/ha-card";
 | 
			
		||||
import "../../../src/components/trace/hat-script-graph";
 | 
			
		||||
import "../../../src/components/trace/hat-trace-timeline";
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,12 @@
 | 
			
		||||
import { html, css, LitElement, TemplateResult } from "lit";
 | 
			
		||||
import {
 | 
			
		||||
  customElement,
 | 
			
		||||
  html,
 | 
			
		||||
  css,
 | 
			
		||||
  LitElement,
 | 
			
		||||
  TemplateResult,
 | 
			
		||||
  internalProperty,
 | 
			
		||||
  property,
 | 
			
		||||
} from "lit-element";
 | 
			
		||||
import "../../../src/components/ha-card";
 | 
			
		||||
import "../../../src/components/trace/hat-script-graph";
 | 
			
		||||
import "../../../src/components/trace/hat-trace-timeline";
 | 
			
		||||
@@ -7,7 +15,6 @@ import { HomeAssistant } from "../../../src/types";
 | 
			
		||||
import { DemoTrace } from "../data/traces/types";
 | 
			
		||||
import { basicTrace } from "../data/traces/basic_trace";
 | 
			
		||||
import { motionLightTrace } from "../data/traces/motion-light-trace";
 | 
			
		||||
import { customElement, property, state } from "lit/decorators";
 | 
			
		||||
 | 
			
		||||
const traces: DemoTrace[] = [basicTrace, motionLightTrace];
 | 
			
		||||
 | 
			
		||||
@@ -15,7 +22,7 @@ const traces: DemoTrace[] = [basicTrace, motionLightTrace];
 | 
			
		||||
export class DemoAutomationTrace extends LitElement {
 | 
			
		||||
  @property({ attribute: false }) hass?: HomeAssistant;
 | 
			
		||||
 | 
			
		||||
  @state() private _selected = {};
 | 
			
		||||
  @internalProperty() private _selected = {};
 | 
			
		||||
 | 
			
		||||
  protected render(): TemplateResult {
 | 
			
		||||
    if (!this.hass) {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,11 @@
 | 
			
		||||
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
 | 
			
		||||
import { customElement, query } from "lit/decorators";
 | 
			
		||||
import {
 | 
			
		||||
  customElement,
 | 
			
		||||
  html,
 | 
			
		||||
  LitElement,
 | 
			
		||||
  PropertyValues,
 | 
			
		||||
  query,
 | 
			
		||||
  TemplateResult,
 | 
			
		||||
} from "lit-element";
 | 
			
		||||
import { getEntity } from "../../../src/fake_data/entity";
 | 
			
		||||
import { provideHass } from "../../../src/fake_data/provide_hass";
 | 
			
		||||
import "../components/demo-cards";
 | 
			
		||||
@@ -87,8 +93,4 @@ class DemoAlarmPanelEntity extends LitElement {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
declare global {
 | 
			
		||||
  interface HTMLElementTagNameMap {
 | 
			
		||||
    "demo-hui-alarm-panel-card": DemoAlarmPanelEntity;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
customElements.define("demo-hui-alarm-panel-card", DemoAlarmPanelEntity);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,11 @@
 | 
			
		||||
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
 | 
			
		||||
import { customElement, query } from "lit/decorators";
 | 
			
		||||
import {
 | 
			
		||||
  customElement,
 | 
			
		||||
  html,
 | 
			
		||||
  LitElement,
 | 
			
		||||
  PropertyValues,
 | 
			
		||||
  query,
 | 
			
		||||
  TemplateResult,
 | 
			
		||||
} from "lit-element";
 | 
			
		||||
import { getEntity } from "../../../src/fake_data/entity";
 | 
			
		||||
import { provideHass } from "../../../src/fake_data/provide_hass";
 | 
			
		||||
import "../components/demo-cards";
 | 
			
		||||
@@ -69,8 +75,4 @@ class DemoConditional extends LitElement {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
declare global {
 | 
			
		||||
  interface HTMLElementTagNameMap {
 | 
			
		||||
    "demo-hui-conditional-card": DemoConditional;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
customElements.define("demo-hui-conditional-card", DemoConditional);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,11 @@
 | 
			
		||||
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
 | 
			
		||||
import { customElement, query } from "lit/decorators";
 | 
			
		||||
import {
 | 
			
		||||
  customElement,
 | 
			
		||||
  html,
 | 
			
		||||
  LitElement,
 | 
			
		||||
  PropertyValues,
 | 
			
		||||
  query,
 | 
			
		||||
  TemplateResult,
 | 
			
		||||
} from "lit-element";
 | 
			
		||||
import { getEntity } from "../../../src/fake_data/entity";
 | 
			
		||||
import { provideHass } from "../../../src/fake_data/provide_hass";
 | 
			
		||||
import "../components/demo-cards";
 | 
			
		||||
@@ -233,8 +239,4 @@ class DemoEntities extends LitElement {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
declare global {
 | 
			
		||||
  interface HTMLElementTagNameMap {
 | 
			
		||||
    "demo-hui-entities-card": DemoEntities;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
customElements.define("demo-hui-entities-card", DemoEntities);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,11 @@
 | 
			
		||||
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
 | 
			
		||||
import { customElement, query } from "lit/decorators";
 | 
			
		||||
import {
 | 
			
		||||
  customElement,
 | 
			
		||||
  html,
 | 
			
		||||
  LitElement,
 | 
			
		||||
  PropertyValues,
 | 
			
		||||
  query,
 | 
			
		||||
  TemplateResult,
 | 
			
		||||
} from "lit-element";
 | 
			
		||||
import { getEntity } from "../../../src/fake_data/entity";
 | 
			
		||||
import { provideHass } from "../../../src/fake_data/provide_hass";
 | 
			
		||||
import "../components/demo-cards";
 | 
			
		||||
@@ -85,8 +91,4 @@ class DemoButtonEntity extends LitElement {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
declare global {
 | 
			
		||||
  interface HTMLElementTagNameMap {
 | 
			
		||||
    "demo-hui-entity-button-card": DemoButtonEntity;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
customElements.define("demo-hui-entity-button-card", DemoButtonEntity);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,11 @@
 | 
			
		||||
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
 | 
			
		||||
import { customElement, query } from "lit/decorators";
 | 
			
		||||
import {
 | 
			
		||||
  customElement,
 | 
			
		||||
  html,
 | 
			
		||||
  LitElement,
 | 
			
		||||
  PropertyValues,
 | 
			
		||||
  query,
 | 
			
		||||
  TemplateResult,
 | 
			
		||||
} from "lit-element";
 | 
			
		||||
import { getEntity } from "../../../src/fake_data/entity";
 | 
			
		||||
import { provideHass } from "../../../src/fake_data/provide_hass";
 | 
			
		||||
import "../components/demo-cards";
 | 
			
		||||
@@ -126,8 +132,4 @@ class DemoEntityFilter extends LitElement {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
declare global {
 | 
			
		||||
  interface HTMLElementTagNameMap {
 | 
			
		||||
    "demo-hui-entity-filter-card": DemoEntityFilter;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
customElements.define("demo-hui-entity-filter-card", DemoEntityFilter);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,11 @@
 | 
			
		||||
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
 | 
			
		||||
import { customElement, query } from "lit/decorators";
 | 
			
		||||
import {
 | 
			
		||||
  customElement,
 | 
			
		||||
  html,
 | 
			
		||||
  LitElement,
 | 
			
		||||
  PropertyValues,
 | 
			
		||||
  query,
 | 
			
		||||
  TemplateResult,
 | 
			
		||||
} from "lit-element";
 | 
			
		||||
import { getEntity } from "../../../src/fake_data/entity";
 | 
			
		||||
import { provideHass } from "../../../src/fake_data/provide_hass";
 | 
			
		||||
import "../components/demo-cards";
 | 
			
		||||
@@ -123,8 +129,4 @@ class DemoGaugeEntity extends LitElement {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
declare global {
 | 
			
		||||
  interface HTMLElementTagNameMap {
 | 
			
		||||
    "demo-hui-gauge-card": DemoGaugeEntity;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
customElements.define("demo-hui-gauge-card", DemoGaugeEntity);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,11 @@
 | 
			
		||||
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
 | 
			
		||||
import { customElement, query } from "lit/decorators";
 | 
			
		||||
import {
 | 
			
		||||
  customElement,
 | 
			
		||||
  html,
 | 
			
		||||
  LitElement,
 | 
			
		||||
  PropertyValues,
 | 
			
		||||
  query,
 | 
			
		||||
  TemplateResult,
 | 
			
		||||
} from "lit-element";
 | 
			
		||||
import { getEntity } from "../../../src/fake_data/entity";
 | 
			
		||||
import { provideHass } from "../../../src/fake_data/provide_hass";
 | 
			
		||||
import "../components/demo-cards";
 | 
			
		||||
@@ -180,7 +186,7 @@ const CONFIGS = [
 | 
			
		||||
      name:
 | 
			
		||||
    - light.kitchen_lights
 | 
			
		||||
    - entity: lock.kitchen_door
 | 
			
		||||
      name:
 | 
			
		||||
      name: 
 | 
			
		||||
    - light.ceiling_lights
 | 
			
		||||
    `,
 | 
			
		||||
  },
 | 
			
		||||
@@ -188,7 +194,7 @@ const CONFIGS = [
 | 
			
		||||
    heading: "Custom tap action",
 | 
			
		||||
    config: `
 | 
			
		||||
- type: glance
 | 
			
		||||
  columns: 4
 | 
			
		||||
  columns: 4  
 | 
			
		||||
  entities:
 | 
			
		||||
    - entity: lock.kitchen_door
 | 
			
		||||
      name: Custom
 | 
			
		||||
@@ -226,8 +232,4 @@ class DemoGlanceEntity extends LitElement {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
declare global {
 | 
			
		||||
  interface HTMLElementTagNameMap {
 | 
			
		||||
    "demo-hui-glance-card": DemoGlanceEntity;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
customElements.define("demo-hui-glance-card", DemoGlanceEntity);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,4 @@
 | 
			
		||||
import { html, LitElement, TemplateResult } from "lit";
 | 
			
		||||
import { customElement } from "lit/decorators";
 | 
			
		||||
import { customElement, html, LitElement, TemplateResult } from "lit-element";
 | 
			
		||||
import "../components/demo-cards";
 | 
			
		||||
 | 
			
		||||
const CONFIGS = [
 | 
			
		||||
@@ -43,8 +42,4 @@ class DemoIframe extends LitElement {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
declare global {
 | 
			
		||||
  interface HTMLElementTagNameMap {
 | 
			
		||||
    "demo-hui-iframe-card": DemoIframe;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
customElements.define("demo-hui-iframe-card", DemoIframe);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,11 @@
 | 
			
		||||
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
 | 
			
		||||
import { customElement, query } from "lit/decorators";
 | 
			
		||||
import {
 | 
			
		||||
  customElement,
 | 
			
		||||
  html,
 | 
			
		||||
  LitElement,
 | 
			
		||||
  PropertyValues,
 | 
			
		||||
  query,
 | 
			
		||||
  TemplateResult,
 | 
			
		||||
} from "lit-element";
 | 
			
		||||
import { getEntity } from "../../../src/fake_data/entity";
 | 
			
		||||
import { provideHass } from "../../../src/fake_data/provide_hass";
 | 
			
		||||
import "../components/demo-cards";
 | 
			
		||||
@@ -79,8 +85,4 @@ class DemoLightEntity extends LitElement {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
declare global {
 | 
			
		||||
  interface HTMLElementTagNameMap {
 | 
			
		||||
    "demo-hui-light-card": DemoLightEntity;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
customElements.define("demo-hui-light-card", DemoLightEntity);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,11 @@
 | 
			
		||||
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
 | 
			
		||||
import { customElement, query } from "lit/decorators";
 | 
			
		||||
import {
 | 
			
		||||
  customElement,
 | 
			
		||||
  html,
 | 
			
		||||
  LitElement,
 | 
			
		||||
  PropertyValues,
 | 
			
		||||
  query,
 | 
			
		||||
  TemplateResult,
 | 
			
		||||
} from "lit-element";
 | 
			
		||||
import { getEntity } from "../../../src/fake_data/entity";
 | 
			
		||||
import { provideHass } from "../../../src/fake_data/provide_hass";
 | 
			
		||||
import "../components/demo-cards";
 | 
			
		||||
@@ -177,8 +183,4 @@ class DemoMap extends LitElement {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
declare global {
 | 
			
		||||
  interface HTMLElementTagNameMap {
 | 
			
		||||
    "demo-hui-map-card": DemoMap;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
customElements.define("demo-hui-map-card", DemoMap);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,11 @@
 | 
			
		||||
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
 | 
			
		||||
import { customElement, query } from "lit/decorators";
 | 
			
		||||
import {
 | 
			
		||||
  customElement,
 | 
			
		||||
  html,
 | 
			
		||||
  LitElement,
 | 
			
		||||
  PropertyValues,
 | 
			
		||||
  query,
 | 
			
		||||
  TemplateResult,
 | 
			
		||||
} from "lit-element";
 | 
			
		||||
import { mockTemplate } from "../../../demo/src/stubs/template";
 | 
			
		||||
import { provideHass } from "../../../src/fake_data/provide_hass";
 | 
			
		||||
import "../components/demo-cards";
 | 
			
		||||
@@ -270,8 +276,4 @@ class DemoMarkdown extends LitElement {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
declare global {
 | 
			
		||||
  interface HTMLElementTagNameMap {
 | 
			
		||||
    "demo-hui-markdown-card": DemoMarkdown;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
customElements.define("demo-hui-markdown-card", DemoMarkdown);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,11 @@
 | 
			
		||||
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
 | 
			
		||||
import { customElement, query } from "lit/decorators";
 | 
			
		||||
import {
 | 
			
		||||
  customElement,
 | 
			
		||||
  html,
 | 
			
		||||
  LitElement,
 | 
			
		||||
  PropertyValues,
 | 
			
		||||
  query,
 | 
			
		||||
  TemplateResult,
 | 
			
		||||
} from "lit-element";
 | 
			
		||||
import { provideHass } from "../../../src/fake_data/provide_hass";
 | 
			
		||||
import "../components/demo-cards";
 | 
			
		||||
import { createMediaPlayerEntities } from "../data/media_players";
 | 
			
		||||
@@ -174,8 +180,4 @@ class DemoHuiMediaControlCard extends LitElement {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
declare global {
 | 
			
		||||
  interface HTMLElementTagNameMap {
 | 
			
		||||
    "demo-hui-media-control-card": DemoHuiMediaControlCard;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
customElements.define("demo-hui-media-control-card", DemoHuiMediaControlCard);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,11 @@
 | 
			
		||||
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
 | 
			
		||||
import { customElement, query } from "lit/decorators";
 | 
			
		||||
import {
 | 
			
		||||
  customElement,
 | 
			
		||||
  html,
 | 
			
		||||
  LitElement,
 | 
			
		||||
  PropertyValues,
 | 
			
		||||
  query,
 | 
			
		||||
  TemplateResult,
 | 
			
		||||
} from "lit-element";
 | 
			
		||||
import { provideHass } from "../../../src/fake_data/provide_hass";
 | 
			
		||||
import "../components/demo-cards";
 | 
			
		||||
import { createMediaPlayerEntities } from "../data/media_players";
 | 
			
		||||
@@ -71,8 +77,4 @@ class DemoHuiMediaPlayerRow extends LitElement {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
declare global {
 | 
			
		||||
  interface HTMLElementTagNameMap {
 | 
			
		||||
    "demo-hui-media-player-row": DemoHuiMediaPlayerRow;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
customElements.define("demo-hui-media-player-row", DemoHuiMediaPlayerRow);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,11 @@
 | 
			
		||||
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
 | 
			
		||||
import { customElement, query } from "lit/decorators";
 | 
			
		||||
import {
 | 
			
		||||
  customElement,
 | 
			
		||||
  html,
 | 
			
		||||
  LitElement,
 | 
			
		||||
  PropertyValues,
 | 
			
		||||
  query,
 | 
			
		||||
  TemplateResult,
 | 
			
		||||
} from "lit-element";
 | 
			
		||||
import { getEntity } from "../../../src/fake_data/entity";
 | 
			
		||||
import { provideHass } from "../../../src/fake_data/provide_hass";
 | 
			
		||||
import "../components/demo-cards";
 | 
			
		||||
@@ -141,8 +147,4 @@ class DemoPictureElements extends LitElement {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
declare global {
 | 
			
		||||
  interface HTMLElementTagNameMap {
 | 
			
		||||
    "demo-hui-picture-elements-card": DemoPictureElements;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
customElements.define("demo-hui-picture-elements-card", DemoPictureElements);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,11 @@
 | 
			
		||||
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
 | 
			
		||||
import { customElement, query } from "lit/decorators";
 | 
			
		||||
import {
 | 
			
		||||
  customElement,
 | 
			
		||||
  html,
 | 
			
		||||
  LitElement,
 | 
			
		||||
  PropertyValues,
 | 
			
		||||
  query,
 | 
			
		||||
  TemplateResult,
 | 
			
		||||
} from "lit-element";
 | 
			
		||||
import { getEntity } from "../../../src/fake_data/entity";
 | 
			
		||||
import { provideHass } from "../../../src/fake_data/provide_hass";
 | 
			
		||||
import "../components/demo-cards";
 | 
			
		||||
@@ -96,8 +102,4 @@ class DemoPictureEntity extends LitElement {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
declare global {
 | 
			
		||||
  interface HTMLElementTagNameMap {
 | 
			
		||||
    "demo-hui-picture-entity-card": DemoPictureEntity;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
customElements.define("demo-hui-picture-entity-card", DemoPictureEntity);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,11 @@
 | 
			
		||||
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
 | 
			
		||||
import { customElement, query } from "lit/decorators";
 | 
			
		||||
import {
 | 
			
		||||
  customElement,
 | 
			
		||||
  html,
 | 
			
		||||
  LitElement,
 | 
			
		||||
  PropertyValues,
 | 
			
		||||
  query,
 | 
			
		||||
  TemplateResult,
 | 
			
		||||
} from "lit-element";
 | 
			
		||||
import { getEntity } from "../../../src/fake_data/entity";
 | 
			
		||||
import { provideHass } from "../../../src/fake_data/provide_hass";
 | 
			
		||||
import "../components/demo-cards";
 | 
			
		||||
@@ -137,8 +143,4 @@ class DemoPictureGlance extends LitElement {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
declare global {
 | 
			
		||||
  interface HTMLElementTagNameMap {
 | 
			
		||||
    "demo-hui-picture-glance-card": DemoPictureGlance;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
customElements.define("demo-hui-picture-glance-card", DemoPictureGlance);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,11 @@
 | 
			
		||||
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
 | 
			
		||||
import { customElement, query } from "lit/decorators";
 | 
			
		||||
import {
 | 
			
		||||
  customElement,
 | 
			
		||||
  html,
 | 
			
		||||
  LitElement,
 | 
			
		||||
  PropertyValues,
 | 
			
		||||
  query,
 | 
			
		||||
  TemplateResult,
 | 
			
		||||
} from "lit-element";
 | 
			
		||||
import { provideHass } from "../../../src/fake_data/provide_hass";
 | 
			
		||||
import "../components/demo-cards";
 | 
			
		||||
import { createPlantEntities } from "../data/plants";
 | 
			
		||||
@@ -46,8 +52,4 @@ export class DemoPlantEntity extends LitElement {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
declare global {
 | 
			
		||||
  interface HTMLElementTagNameMap {
 | 
			
		||||
    "demo-hui-plant-card": DemoPlantEntity;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
customElements.define("demo-hui-plant-card", DemoPlantEntity);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,11 @@
 | 
			
		||||
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
 | 
			
		||||
import { customElement, query } from "lit/decorators";
 | 
			
		||||
import {
 | 
			
		||||
  customElement,
 | 
			
		||||
  html,
 | 
			
		||||
  LitElement,
 | 
			
		||||
  PropertyValues,
 | 
			
		||||
  query,
 | 
			
		||||
  TemplateResult,
 | 
			
		||||
} from "lit-element";
 | 
			
		||||
import { provideHass } from "../../../src/fake_data/provide_hass";
 | 
			
		||||
import "../components/demo-cards";
 | 
			
		||||
 | 
			
		||||
@@ -42,8 +48,4 @@ class DemoShoppingListEntity extends LitElement {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
declare global {
 | 
			
		||||
  interface HTMLElementTagNameMap {
 | 
			
		||||
    "demo-hui-shopping-list-card": DemoShoppingListEntity;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
customElements.define("demo-hui-shopping-list-card", DemoShoppingListEntity);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,11 @@
 | 
			
		||||
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
 | 
			
		||||
import { customElement, query } from "lit/decorators";
 | 
			
		||||
import {
 | 
			
		||||
  customElement,
 | 
			
		||||
  html,
 | 
			
		||||
  LitElement,
 | 
			
		||||
  PropertyValues,
 | 
			
		||||
  query,
 | 
			
		||||
  TemplateResult,
 | 
			
		||||
} from "lit-element";
 | 
			
		||||
import { mockHistory } from "../../../demo/src/stubs/history";
 | 
			
		||||
import { getEntity } from "../../../src/fake_data/entity";
 | 
			
		||||
import { provideHass } from "../../../src/fake_data/provide_hass";
 | 
			
		||||
@@ -43,110 +49,6 @@ const ENTITIES = [
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
const CONFIGS = [
 | 
			
		||||
  {
 | 
			
		||||
    heading: "Default Grid",
 | 
			
		||||
    config: `
 | 
			
		||||
- type: grid
 | 
			
		||||
  cards:
 | 
			
		||||
    - type: entity
 | 
			
		||||
      entity: light.kitchen_lights
 | 
			
		||||
    - type: entity
 | 
			
		||||
      entity: light.bed_light
 | 
			
		||||
    - type: entity
 | 
			
		||||
      entity: device_tracker.demo_paulus
 | 
			
		||||
    - type: sensor
 | 
			
		||||
      entity: sensor.illumination
 | 
			
		||||
      graph: line
 | 
			
		||||
    - type: entity
 | 
			
		||||
      entity: device_tracker.demo_anne_therese
 | 
			
		||||
    `,
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    heading: "Non-square Grid with 2 columns",
 | 
			
		||||
    config: `
 | 
			
		||||
- type: grid
 | 
			
		||||
  columns: 2
 | 
			
		||||
  square: false
 | 
			
		||||
  cards:
 | 
			
		||||
    - type: entity
 | 
			
		||||
      entity: light.kitchen_lights
 | 
			
		||||
    - type: entity
 | 
			
		||||
      entity: light.bed_light
 | 
			
		||||
    - type: entity
 | 
			
		||||
      entity: device_tracker.demo_paulus
 | 
			
		||||
    - type: sensor
 | 
			
		||||
      entity: sensor.illumination
 | 
			
		||||
      graph: line
 | 
			
		||||
    `,
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    heading: "Default Grid with title",
 | 
			
		||||
    config: `
 | 
			
		||||
- type: grid
 | 
			
		||||
  title: Kitchen
 | 
			
		||||
  cards:
 | 
			
		||||
    - type: entity
 | 
			
		||||
      entity: light.kitchen_lights
 | 
			
		||||
    - type: entity
 | 
			
		||||
      entity: light.bed_light
 | 
			
		||||
    - type: entity
 | 
			
		||||
      entity: device_tracker.demo_paulus
 | 
			
		||||
    - type: sensor
 | 
			
		||||
      entity: sensor.illumination
 | 
			
		||||
      graph: line
 | 
			
		||||
    - type: entity
 | 
			
		||||
      entity: device_tracker.demo_anne_therese
 | 
			
		||||
    `,
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    heading: "Columns 4",
 | 
			
		||||
    config: `
 | 
			
		||||
- type: grid
 | 
			
		||||
  columns: 4
 | 
			
		||||
  cards:
 | 
			
		||||
    - type: entity
 | 
			
		||||
      entity: light.kitchen_lights
 | 
			
		||||
    - type: entity
 | 
			
		||||
      entity: light.bed_light
 | 
			
		||||
    - type: entity
 | 
			
		||||
      entity: device_tracker.demo_paulus
 | 
			
		||||
    - type: sensor
 | 
			
		||||
      entity: sensor.illumination
 | 
			
		||||
      graph: line
 | 
			
		||||
    `,
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    heading: "Columns 2",
 | 
			
		||||
    config: `
 | 
			
		||||
- type: grid
 | 
			
		||||
  columns: 2
 | 
			
		||||
  cards:
 | 
			
		||||
    - type: entity
 | 
			
		||||
      entity: light.kitchen_lights
 | 
			
		||||
    - type: entity
 | 
			
		||||
      entity: light.bed_light
 | 
			
		||||
    `,
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    heading: "Columns 1",
 | 
			
		||||
    config: `
 | 
			
		||||
- type: grid
 | 
			
		||||
  columns: 1
 | 
			
		||||
  cards:
 | 
			
		||||
  - type: entity
 | 
			
		||||
    entity: light.kitchen_lights
 | 
			
		||||
    `,
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    heading: "Size for single card",
 | 
			
		||||
    config: `
 | 
			
		||||
- type: grid
 | 
			
		||||
  cards:
 | 
			
		||||
  - type: entity
 | 
			
		||||
    entity: light.kitchen_lights
 | 
			
		||||
    `,
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  {
 | 
			
		||||
    heading: "Vertical Stack",
 | 
			
		||||
    config: `
 | 
			
		||||
@@ -197,9 +99,45 @@ const CONFIGS = [
 | 
			
		||||
      entity: light.bed_light
 | 
			
		||||
    `,
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    heading: "Default Grid",
 | 
			
		||||
    config: `
 | 
			
		||||
- type: grid
 | 
			
		||||
  cards:
 | 
			
		||||
    - type: entity
 | 
			
		||||
      entity: light.kitchen_lights
 | 
			
		||||
    - type: entity
 | 
			
		||||
      entity: light.bed_light
 | 
			
		||||
    - type: entity
 | 
			
		||||
      entity: device_tracker.demo_paulus
 | 
			
		||||
    - type: sensor
 | 
			
		||||
      entity: sensor.illumination
 | 
			
		||||
      graph: line
 | 
			
		||||
    - type: entity
 | 
			
		||||
      entity: device_tracker.demo_anne_therese
 | 
			
		||||
    `,
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    heading: "Non-square Grid with 2 columns",
 | 
			
		||||
    config: `
 | 
			
		||||
- type: grid
 | 
			
		||||
  columns: 2
 | 
			
		||||
  square: false
 | 
			
		||||
  cards:
 | 
			
		||||
    - type: entity
 | 
			
		||||
      entity: light.kitchen_lights
 | 
			
		||||
    - type: entity
 | 
			
		||||
      entity: light.bed_light
 | 
			
		||||
    - type: entity
 | 
			
		||||
      entity: device_tracker.demo_paulus
 | 
			
		||||
    - type: sensor
 | 
			
		||||
      entity: sensor.illumination
 | 
			
		||||
      graph: line
 | 
			
		||||
    `,
 | 
			
		||||
  },
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
@customElement("demo-hui-grid-and-stack-card")
 | 
			
		||||
@customElement("demo-hui-stack-card")
 | 
			
		||||
class DemoStack extends LitElement {
 | 
			
		||||
  @query("#demos") private _demoRoot!: HTMLElement;
 | 
			
		||||
 | 
			
		||||
@@ -217,8 +155,4 @@ class DemoStack extends LitElement {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
declare global {
 | 
			
		||||
  interface HTMLElementTagNameMap {
 | 
			
		||||
    "demo-hui-grid-and-stack-card": DemoStack;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
customElements.define("demo-hui-stack-card", DemoStack);
 | 
			
		||||
@@ -1,5 +1,11 @@
 | 
			
		||||
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
 | 
			
		||||
import { customElement, query } from "lit/decorators";
 | 
			
		||||
import {
 | 
			
		||||
  customElement,
 | 
			
		||||
  html,
 | 
			
		||||
  LitElement,
 | 
			
		||||
  PropertyValues,
 | 
			
		||||
  query,
 | 
			
		||||
  TemplateResult,
 | 
			
		||||
} from "lit-element";
 | 
			
		||||
import { getEntity } from "../../../src/fake_data/entity";
 | 
			
		||||
import { provideHass } from "../../../src/fake_data/provide_hass";
 | 
			
		||||
import "../components/demo-cards";
 | 
			
		||||
@@ -90,8 +96,4 @@ class DemoThermostatEntity extends LitElement {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
declare global {
 | 
			
		||||
  interface HTMLElementTagNameMap {
 | 
			
		||||
    "demo-hui-thermostat-card": DemoThermostatEntity;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
customElements.define("demo-hui-thermostat-card", DemoThermostatEntity);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,12 @@
 | 
			
		||||
import { html, css, LitElement, TemplateResult } from "lit";
 | 
			
		||||
import {
 | 
			
		||||
  customElement,
 | 
			
		||||
  html,
 | 
			
		||||
  css,
 | 
			
		||||
  internalProperty,
 | 
			
		||||
  LitElement,
 | 
			
		||||
  TemplateResult,
 | 
			
		||||
  property,
 | 
			
		||||
} from "lit-element";
 | 
			
		||||
import "../../../src/components/ha-formfield";
 | 
			
		||||
import "../../../src/components/ha-switch";
 | 
			
		||||
 | 
			
		||||
@@ -15,8 +23,7 @@ import type {
 | 
			
		||||
} from "../../../src/panels/config/integrations/ha-config-integrations";
 | 
			
		||||
import { DeviceRegistryEntry } from "../../../src/data/device_registry";
 | 
			
		||||
import { EntityRegistryEntry } from "../../../src/data/entity_registry";
 | 
			
		||||
import { classMap } from "lit/directives/class-map";
 | 
			
		||||
import { customElement, property, state } from "lit/decorators";
 | 
			
		||||
import { classMap } from "lit-html/directives/class-map";
 | 
			
		||||
 | 
			
		||||
const createConfigEntry = (
 | 
			
		||||
  title: string,
 | 
			
		||||
@@ -28,11 +35,10 @@ const createConfigEntry = (
 | 
			
		||||
  title,
 | 
			
		||||
  source: "zeroconf",
 | 
			
		||||
  state: "loaded",
 | 
			
		||||
  connection_class: "local_push",
 | 
			
		||||
  supports_options: false,
 | 
			
		||||
  supports_unload: true,
 | 
			
		||||
  disabled_by: null,
 | 
			
		||||
  pref_disable_new_entities: false,
 | 
			
		||||
  pref_disable_polling: false,
 | 
			
		||||
  reason: null,
 | 
			
		||||
  ...override,
 | 
			
		||||
});
 | 
			
		||||
@@ -55,9 +61,6 @@ const nameAsDomainEntry = createConfigEntry("ESPHome");
 | 
			
		||||
const longNameEntry = createConfigEntry(
 | 
			
		||||
  "Entry with a super long name that is going to the next line"
 | 
			
		||||
);
 | 
			
		||||
const longNonBreakingNameEntry = createConfigEntry(
 | 
			
		||||
  "EntryWithASuperLongNameThatDoesNotBreak"
 | 
			
		||||
);
 | 
			
		||||
const configPanelEntry = createConfigEntry("Config Panel", {
 | 
			
		||||
  domain: "mqtt",
 | 
			
		||||
  localized_domain_name: "MQTT",
 | 
			
		||||
@@ -65,9 +68,6 @@ const configPanelEntry = createConfigEntry("Config Panel", {
 | 
			
		||||
const optionsFlowEntry = createConfigEntry("Options Flow", {
 | 
			
		||||
  supports_options: true,
 | 
			
		||||
});
 | 
			
		||||
const disabledPollingEntry = createConfigEntry("Disabled Polling", {
 | 
			
		||||
  pref_disable_polling: true,
 | 
			
		||||
});
 | 
			
		||||
const setupErrorEntry = createConfigEntry("Setup Error", {
 | 
			
		||||
  state: "setup_error",
 | 
			
		||||
});
 | 
			
		||||
@@ -83,8 +83,7 @@ const setupRetryReasonEntry = createConfigEntry("Setup Retry", {
 | 
			
		||||
});
 | 
			
		||||
const setupRetryReasonMissingKeyEntry = createConfigEntry("Setup Retry", {
 | 
			
		||||
  state: "setup_retry",
 | 
			
		||||
  reason:
 | 
			
		||||
    "HTTPSConnectionpool: Max retries exceeded with NewConnectionError('<urllib3.connection.HTTPSConnection object at 0x9eedfc10>: Failed to establish a new connection: [Errno 113] Host is unreachable')",
 | 
			
		||||
  reason: "resolve_error",
 | 
			
		||||
});
 | 
			
		||||
const failedUnloadEntry = createConfigEntry("Failed Unload", {
 | 
			
		||||
  state: "failed_unload",
 | 
			
		||||
@@ -140,10 +139,8 @@ const configEntries: Array<{
 | 
			
		||||
  { items: [loadedEntry] },
 | 
			
		||||
  { items: [configPanelEntry] },
 | 
			
		||||
  { items: [optionsFlowEntry] },
 | 
			
		||||
  { items: [disabledPollingEntry] },
 | 
			
		||||
  { items: [nameAsDomainEntry] },
 | 
			
		||||
  { items: [longNameEntry] },
 | 
			
		||||
  { items: [longNonBreakingNameEntry] },
 | 
			
		||||
  { items: [setupErrorEntry] },
 | 
			
		||||
  { items: [migrationErrorEntry] },
 | 
			
		||||
  { items: [setupRetryEntry] },
 | 
			
		||||
@@ -157,7 +154,6 @@ const configEntries: Array<{
 | 
			
		||||
      setupErrorEntry,
 | 
			
		||||
      migrationErrorEntry,
 | 
			
		||||
      longNameEntry,
 | 
			
		||||
      longNonBreakingNameEntry,
 | 
			
		||||
      setupRetryEntry,
 | 
			
		||||
      failedUnloadEntry,
 | 
			
		||||
      notLoadedEntry,
 | 
			
		||||
@@ -218,9 +214,9 @@ const createDeviceRegistryEntries = (
 | 
			
		||||
export class DemoIntegrationCard extends LitElement {
 | 
			
		||||
  @property({ attribute: false }) hass?: HomeAssistant;
 | 
			
		||||
 | 
			
		||||
  @state() isCustomIntegration = false;
 | 
			
		||||
  @internalProperty() isCustomIntegration = false;
 | 
			
		||||
 | 
			
		||||
  @state() isCloud = false;
 | 
			
		||||
  @internalProperty() isCloud = false;
 | 
			
		||||
 | 
			
		||||
  protected render(): TemplateResult {
 | 
			
		||||
    if (!this.hass) {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +1,21 @@
 | 
			
		||||
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
 | 
			
		||||
import { customElement, property, query } from "lit/decorators";
 | 
			
		||||
import {
 | 
			
		||||
  customElement,
 | 
			
		||||
  html,
 | 
			
		||||
  LitElement,
 | 
			
		||||
  property,
 | 
			
		||||
  PropertyValues,
 | 
			
		||||
  query,
 | 
			
		||||
  TemplateResult,
 | 
			
		||||
} from "lit-element";
 | 
			
		||||
import "../../../src/components/ha-card";
 | 
			
		||||
import {
 | 
			
		||||
  LightColorModes,
 | 
			
		||||
  SUPPORT_BRIGHTNESS,
 | 
			
		||||
  SUPPORT_COLOR,
 | 
			
		||||
  SUPPORT_COLOR_TEMP,
 | 
			
		||||
  SUPPORT_EFFECT,
 | 
			
		||||
  SUPPORT_FLASH,
 | 
			
		||||
  SUPPORT_TRANSITION,
 | 
			
		||||
  SUPPORT_WHITE_VALUE,
 | 
			
		||||
} from "../../../src/data/light";
 | 
			
		||||
import "../../../src/dialogs/more-info/more-info-content";
 | 
			
		||||
import { getEntity } from "../../../src/fake_data/entity";
 | 
			
		||||
@@ -22,8 +32,7 @@ const ENTITIES = [
 | 
			
		||||
  getEntity("light", "kitchen_light", "on", {
 | 
			
		||||
    friendly_name: "Brightness Light",
 | 
			
		||||
    brightness: 200,
 | 
			
		||||
    supported_color_modes: [LightColorModes.BRIGHTNESS],
 | 
			
		||||
    color_mode: LightColorModes.BRIGHTNESS,
 | 
			
		||||
    supported_features: SUPPORT_BRIGHTNESS,
 | 
			
		||||
  }),
 | 
			
		||||
  getEntity("light", "color_temperature_light", "on", {
 | 
			
		||||
    friendly_name: "White Color Temperature Light",
 | 
			
		||||
@@ -31,96 +40,20 @@ const ENTITIES = [
 | 
			
		||||
    color_temp: 75,
 | 
			
		||||
    min_mireds: 30,
 | 
			
		||||
    max_mireds: 150,
 | 
			
		||||
    supported_color_modes: [
 | 
			
		||||
      LightColorModes.BRIGHTNESS,
 | 
			
		||||
      LightColorModes.COLOR_TEMP,
 | 
			
		||||
    ],
 | 
			
		||||
    color_mode: LightColorModes.COLOR_TEMP,
 | 
			
		||||
    supported_features: SUPPORT_BRIGHTNESS + SUPPORT_COLOR_TEMP,
 | 
			
		||||
  }),
 | 
			
		||||
  getEntity("light", "color_hs_light", "on", {
 | 
			
		||||
    friendly_name: "Color HS Light",
 | 
			
		||||
    brightness: 255,
 | 
			
		||||
    hs_color: [30, 100],
 | 
			
		||||
    rgb_color: [30, 100, 255],
 | 
			
		||||
    min_mireds: 30,
 | 
			
		||||
    max_mireds: 150,
 | 
			
		||||
    supported_features: SUPPORT_EFFECT + SUPPORT_FLASH + SUPPORT_TRANSITION,
 | 
			
		||||
    supported_color_modes: [
 | 
			
		||||
      LightColorModes.BRIGHTNESS,
 | 
			
		||||
      LightColorModes.COLOR_TEMP,
 | 
			
		||||
      LightColorModes.HS,
 | 
			
		||||
    ],
 | 
			
		||||
    color_mode: LightColorModes.HS,
 | 
			
		||||
    effect_list: ["random", "colorloop"],
 | 
			
		||||
  }),
 | 
			
		||||
  getEntity("light", "color_rgb_ct_light", "on", {
 | 
			
		||||
    friendly_name: "Color RGB + CT Light",
 | 
			
		||||
    brightness: 255,
 | 
			
		||||
    color_temp: 75,
 | 
			
		||||
    min_mireds: 30,
 | 
			
		||||
    max_mireds: 150,
 | 
			
		||||
    supported_features: SUPPORT_EFFECT + SUPPORT_FLASH + SUPPORT_TRANSITION,
 | 
			
		||||
    supported_color_modes: [
 | 
			
		||||
      LightColorModes.BRIGHTNESS,
 | 
			
		||||
      LightColorModes.COLOR_TEMP,
 | 
			
		||||
      LightColorModes.RGB,
 | 
			
		||||
    ],
 | 
			
		||||
    color_mode: LightColorModes.COLOR_TEMP,
 | 
			
		||||
    effect_list: ["random", "colorloop"],
 | 
			
		||||
  }),
 | 
			
		||||
  getEntity("light", "color_RGB_light", "on", {
 | 
			
		||||
  getEntity("light", "color_effectslight", "on", {
 | 
			
		||||
    friendly_name: "Color Effets Light",
 | 
			
		||||
    brightness: 255,
 | 
			
		||||
    rgb_color: [30, 100, 255],
 | 
			
		||||
    supported_features: SUPPORT_EFFECT + SUPPORT_FLASH + SUPPORT_TRANSITION,
 | 
			
		||||
    supported_color_modes: [LightColorModes.BRIGHTNESS, LightColorModes.RGB],
 | 
			
		||||
    color_mode: LightColorModes.RGB,
 | 
			
		||||
    effect_list: ["random", "colorloop"],
 | 
			
		||||
  }),
 | 
			
		||||
  getEntity("light", "color_rgbw_light", "on", {
 | 
			
		||||
    friendly_name: "Color RGBW Light",
 | 
			
		||||
    brightness: 255,
 | 
			
		||||
    rgbw_color: [30, 100, 255, 125],
 | 
			
		||||
    min_mireds: 30,
 | 
			
		||||
    max_mireds: 150,
 | 
			
		||||
    supported_features: SUPPORT_EFFECT + SUPPORT_FLASH + SUPPORT_TRANSITION,
 | 
			
		||||
    supported_color_modes: [
 | 
			
		||||
      LightColorModes.BRIGHTNESS,
 | 
			
		||||
      LightColorModes.COLOR_TEMP,
 | 
			
		||||
      LightColorModes.RGBW,
 | 
			
		||||
    ],
 | 
			
		||||
    color_mode: LightColorModes.RGBW,
 | 
			
		||||
    effect_list: ["random", "colorloop"],
 | 
			
		||||
  }),
 | 
			
		||||
  getEntity("light", "color_rgbww_light", "on", {
 | 
			
		||||
    friendly_name: "Color RGBWW Light",
 | 
			
		||||
    brightness: 255,
 | 
			
		||||
    rgbww_color: [30, 100, 255, 125, 10],
 | 
			
		||||
    min_mireds: 30,
 | 
			
		||||
    max_mireds: 150,
 | 
			
		||||
    supported_features: SUPPORT_EFFECT + SUPPORT_FLASH + SUPPORT_TRANSITION,
 | 
			
		||||
    supported_color_modes: [
 | 
			
		||||
      LightColorModes.BRIGHTNESS,
 | 
			
		||||
      LightColorModes.COLOR_TEMP,
 | 
			
		||||
      LightColorModes.RGBWW,
 | 
			
		||||
    ],
 | 
			
		||||
    color_mode: LightColorModes.RGBWW,
 | 
			
		||||
    effect_list: ["random", "colorloop"],
 | 
			
		||||
  }),
 | 
			
		||||
  getEntity("light", "color_xy_light", "on", {
 | 
			
		||||
    friendly_name: "Color XY Light",
 | 
			
		||||
    brightness: 255,
 | 
			
		||||
    xy_color: [30, 100],
 | 
			
		||||
    rgb_color: [30, 100, 255],
 | 
			
		||||
    min_mireds: 30,
 | 
			
		||||
    max_mireds: 150,
 | 
			
		||||
    supported_features: SUPPORT_EFFECT + SUPPORT_FLASH + SUPPORT_TRANSITION,
 | 
			
		||||
    supported_color_modes: [
 | 
			
		||||
      LightColorModes.BRIGHTNESS,
 | 
			
		||||
      LightColorModes.COLOR_TEMP,
 | 
			
		||||
      LightColorModes.XY,
 | 
			
		||||
    ],
 | 
			
		||||
    color_mode: LightColorModes.XY,
 | 
			
		||||
    hs_color: [30, 100],
 | 
			
		||||
    white_value: 36,
 | 
			
		||||
    supported_features:
 | 
			
		||||
      SUPPORT_BRIGHTNESS +
 | 
			
		||||
      SUPPORT_EFFECT +
 | 
			
		||||
      SUPPORT_FLASH +
 | 
			
		||||
      SUPPORT_COLOR +
 | 
			
		||||
      SUPPORT_TRANSITION +
 | 
			
		||||
      SUPPORT_WHITE_VALUE,
 | 
			
		||||
    effect_list: ["random", "colorloop"],
 | 
			
		||||
  }),
 | 
			
		||||
];
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,5 @@
 | 
			
		||||
import "@material/mwc-button";
 | 
			
		||||
import { html, LitElement, TemplateResult } from "lit";
 | 
			
		||||
import { customElement } from "lit/decorators";
 | 
			
		||||
import { customElement, html, LitElement, TemplateResult } from "lit-element";
 | 
			
		||||
import "../../../src/components/ha-card";
 | 
			
		||||
import { ActionHandlerEvent } from "../../../src/data/lovelace";
 | 
			
		||||
import { actionHandler } from "../../../src/panels/lovelace/common/directives/action-handler-directive";
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,12 @@
 | 
			
		||||
import { mdiArrowUpBoldCircle, mdiPuzzle } from "@mdi/js";
 | 
			
		||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
 | 
			
		||||
import { property } from "lit/decorators";
 | 
			
		||||
import {
 | 
			
		||||
  css,
 | 
			
		||||
  CSSResultArray,
 | 
			
		||||
  html,
 | 
			
		||||
  LitElement,
 | 
			
		||||
  property,
 | 
			
		||||
  TemplateResult,
 | 
			
		||||
} from "lit-element";
 | 
			
		||||
import memoizeOne from "memoize-one";
 | 
			
		||||
import { atLeastVersion } from "../../../src/common/config/version";
 | 
			
		||||
import { navigate } from "../../../src/common/navigate";
 | 
			
		||||
@@ -41,7 +47,9 @@ class HassioAddonRepositoryEl extends LitElement {
 | 
			
		||||
    const repo = this.repo;
 | 
			
		||||
    let _addons = this.addons;
 | 
			
		||||
    if (!this.hass.userData?.showAdvanced) {
 | 
			
		||||
      _addons = _addons.filter((addon) => !addon.advanced);
 | 
			
		||||
      _addons = _addons.filter((addon) => {
 | 
			
		||||
        return !addon.advanced;
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
    const addons = this._getAddons(_addons, this.filter);
 | 
			
		||||
 | 
			
		||||
@@ -60,7 +68,9 @@ class HassioAddonRepositoryEl extends LitElement {
 | 
			
		||||
    }
 | 
			
		||||
    return html`
 | 
			
		||||
      <div class="content">
 | 
			
		||||
        <h1>${repo.name}</h1>
 | 
			
		||||
        <h1>
 | 
			
		||||
          ${repo.name}
 | 
			
		||||
        </h1>
 | 
			
		||||
        <div class="card-group">
 | 
			
		||||
          ${addons.map(
 | 
			
		||||
            (addon) => html`
 | 
			
		||||
@@ -120,10 +130,10 @@ class HassioAddonRepositoryEl extends LitElement {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private _addonTapped(ev) {
 | 
			
		||||
    navigate(`/hassio/addon/${ev.currentTarget.addon.slug}`);
 | 
			
		||||
    navigate(this, `/hassio/addon/${ev.currentTarget.addon.slug}`);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static get styles(): CSSResultGroup {
 | 
			
		||||
  static get styles(): CSSResultArray {
 | 
			
		||||
    return [
 | 
			
		||||
      hassioStyle,
 | 
			
		||||
      css`
 | 
			
		||||
 
 | 
			
		||||
@@ -4,13 +4,13 @@ import "@material/mwc-list/mwc-list-item";
 | 
			
		||||
import { mdiDotsVertical } from "@mdi/js";
 | 
			
		||||
import {
 | 
			
		||||
  css,
 | 
			
		||||
  CSSResultGroup,
 | 
			
		||||
  html,
 | 
			
		||||
  CSSResult,
 | 
			
		||||
  internalProperty,
 | 
			
		||||
  LitElement,
 | 
			
		||||
  property,
 | 
			
		||||
  PropertyValues,
 | 
			
		||||
  TemplateResult,
 | 
			
		||||
} from "lit";
 | 
			
		||||
import { property, state } from "lit/decorators";
 | 
			
		||||
} from "lit-element";
 | 
			
		||||
import { html, TemplateResult } from "lit-html";
 | 
			
		||||
import memoizeOne from "memoize-one";
 | 
			
		||||
import { atLeastVersion } from "../../../src/common/config/version";
 | 
			
		||||
import { fireEvent } from "../../../src/common/dom/fire_event";
 | 
			
		||||
@@ -58,7 +58,7 @@ class HassioAddonStore extends LitElement {
 | 
			
		||||
 | 
			
		||||
  @property({ attribute: false }) public route!: Route;
 | 
			
		||||
 | 
			
		||||
  @state() private _filter?: string;
 | 
			
		||||
  @internalProperty() private _filter?: string;
 | 
			
		||||
 | 
			
		||||
  public async refreshData() {
 | 
			
		||||
    await reloadHassioAddons(this.hass);
 | 
			
		||||
@@ -86,7 +86,9 @@ class HassioAddonStore extends LitElement {
 | 
			
		||||
        main-page
 | 
			
		||||
        supervisor
 | 
			
		||||
      >
 | 
			
		||||
        <span slot="header"> ${this.supervisor.localize("panel.store")} </span>
 | 
			
		||||
        <span slot="header">
 | 
			
		||||
          ${this.supervisor.localize("panel.store")}
 | 
			
		||||
        </span>
 | 
			
		||||
        <ha-button-menu
 | 
			
		||||
          corner="BOTTOM_START"
 | 
			
		||||
          slot="toolbar-icon"
 | 
			
		||||
@@ -138,7 +140,7 @@ class HassioAddonStore extends LitElement {
 | 
			
		||||
  protected firstUpdated(changedProps: PropertyValues) {
 | 
			
		||||
    super.firstUpdated(changedProps);
 | 
			
		||||
    const repositoryUrl = extractSearchParam("repository_url");
 | 
			
		||||
    navigate("/hassio/store", { replace: true });
 | 
			
		||||
    navigate(this, "/hassio/store", true);
 | 
			
		||||
    if (repositoryUrl) {
 | 
			
		||||
      this._manageRepositories(repositoryUrl);
 | 
			
		||||
    }
 | 
			
		||||
@@ -152,8 +154,8 @@ class HassioAddonStore extends LitElement {
 | 
			
		||||
      repositories: HassioAddonRepository[],
 | 
			
		||||
      addons: HassioAddonInfo[],
 | 
			
		||||
      filter?: string
 | 
			
		||||
    ) =>
 | 
			
		||||
      repositories.sort(sortRepos).map((repo) => {
 | 
			
		||||
    ) => {
 | 
			
		||||
      return repositories.sort(sortRepos).map((repo) => {
 | 
			
		||||
        const filteredAddons = addons.filter(
 | 
			
		||||
          (addon) => addon.repository === repo.slug
 | 
			
		||||
        );
 | 
			
		||||
@@ -169,7 +171,8 @@ class HassioAddonStore extends LitElement {
 | 
			
		||||
              ></hassio-addon-repository>
 | 
			
		||||
            `
 | 
			
		||||
          : html``;
 | 
			
		||||
      })
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  private _handleAction(ev: CustomEvent<ActionDetail>) {
 | 
			
		||||
@@ -218,7 +221,7 @@ class HassioAddonStore extends LitElement {
 | 
			
		||||
    this._filter = e.detail.value;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static get styles(): CSSResultGroup {
 | 
			
		||||
  static get styles(): CSSResult {
 | 
			
		||||
    return css`
 | 
			
		||||
      hassio-addon-repository {
 | 
			
		||||
        margin-top: 24px;
 | 
			
		||||
 
 | 
			
		||||
@@ -4,13 +4,15 @@ import "@polymer/paper-item/paper-item";
 | 
			
		||||
import "@polymer/paper-listbox/paper-listbox";
 | 
			
		||||
import {
 | 
			
		||||
  css,
 | 
			
		||||
  CSSResultGroup,
 | 
			
		||||
  CSSResult,
 | 
			
		||||
  customElement,
 | 
			
		||||
  html,
 | 
			
		||||
  internalProperty,
 | 
			
		||||
  LitElement,
 | 
			
		||||
  property,
 | 
			
		||||
  PropertyValues,
 | 
			
		||||
  TemplateResult,
 | 
			
		||||
} from "lit";
 | 
			
		||||
import { customElement, property, state } from "lit/decorators";
 | 
			
		||||
} from "lit-element";
 | 
			
		||||
import "web-animations-js/web-animations-next-lite.min";
 | 
			
		||||
import "../../../../src/components/buttons/ha-progress-button";
 | 
			
		||||
import "../../../../src/components/ha-card";
 | 
			
		||||
@@ -37,15 +39,15 @@ class HassioAddonAudio extends LitElement {
 | 
			
		||||
 | 
			
		||||
  @property({ attribute: false }) public addon!: HassioAddonDetails;
 | 
			
		||||
 | 
			
		||||
  @state() private _error?: string;
 | 
			
		||||
  @internalProperty() private _error?: string;
 | 
			
		||||
 | 
			
		||||
  @state() private _inputDevices?: HassioHardwareAudioDevice[];
 | 
			
		||||
  @internalProperty() private _inputDevices?: HassioHardwareAudioDevice[];
 | 
			
		||||
 | 
			
		||||
  @state() private _outputDevices?: HassioHardwareAudioDevice[];
 | 
			
		||||
  @internalProperty() private _outputDevices?: HassioHardwareAudioDevice[];
 | 
			
		||||
 | 
			
		||||
  @state() private _selectedInput!: null | string;
 | 
			
		||||
  @internalProperty() private _selectedInput!: null | string;
 | 
			
		||||
 | 
			
		||||
  @state() private _selectedOutput!: null | string;
 | 
			
		||||
  @internalProperty() private _selectedOutput!: null | string;
 | 
			
		||||
 | 
			
		||||
  protected render(): TemplateResult {
 | 
			
		||||
    return html`
 | 
			
		||||
@@ -67,13 +69,13 @@ class HassioAddonAudio extends LitElement {
 | 
			
		||||
              .selected=${this._selectedInput!}
 | 
			
		||||
            >
 | 
			
		||||
              ${this._inputDevices &&
 | 
			
		||||
              this._inputDevices.map(
 | 
			
		||||
                (item) => html`
 | 
			
		||||
              this._inputDevices.map((item) => {
 | 
			
		||||
                return html`
 | 
			
		||||
                  <paper-item device=${item.device || ""}>
 | 
			
		||||
                    ${item.name}
 | 
			
		||||
                  </paper-item>
 | 
			
		||||
                `
 | 
			
		||||
              )}
 | 
			
		||||
                `;
 | 
			
		||||
              })}
 | 
			
		||||
            </paper-listbox>
 | 
			
		||||
          </paper-dropdown-menu>
 | 
			
		||||
          <paper-dropdown-menu
 | 
			
		||||
@@ -88,13 +90,13 @@ class HassioAddonAudio extends LitElement {
 | 
			
		||||
              .selected=${this._selectedOutput!}
 | 
			
		||||
            >
 | 
			
		||||
              ${this._outputDevices &&
 | 
			
		||||
              this._outputDevices.map(
 | 
			
		||||
                (item) => html`
 | 
			
		||||
              this._outputDevices.map((item) => {
 | 
			
		||||
                return html`
 | 
			
		||||
                  <paper-item device=${item.device || ""}
 | 
			
		||||
                    >${item.name}</paper-item
 | 
			
		||||
                  >
 | 
			
		||||
                `
 | 
			
		||||
              )}
 | 
			
		||||
                `;
 | 
			
		||||
              })}
 | 
			
		||||
            </paper-listbox>
 | 
			
		||||
          </paper-dropdown-menu>
 | 
			
		||||
        </div>
 | 
			
		||||
@@ -107,7 +109,7 @@ class HassioAddonAudio extends LitElement {
 | 
			
		||||
    `;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static get styles(): CSSResultGroup {
 | 
			
		||||
  static get styles(): CSSResult[] {
 | 
			
		||||
    return [
 | 
			
		||||
      haStyle,
 | 
			
		||||
      hassioStyle,
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,12 @@
 | 
			
		||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
 | 
			
		||||
import { customElement, property } from "lit/decorators";
 | 
			
		||||
import {
 | 
			
		||||
  css,
 | 
			
		||||
  CSSResult,
 | 
			
		||||
  customElement,
 | 
			
		||||
  html,
 | 
			
		||||
  LitElement,
 | 
			
		||||
  property,
 | 
			
		||||
  TemplateResult,
 | 
			
		||||
} from "lit-element";
 | 
			
		||||
import "../../../../src/components/ha-circular-progress";
 | 
			
		||||
import { HassioAddonDetails } from "../../../../src/data/hassio/addon";
 | 
			
		||||
import { Supervisor } from "../../../../src/data/supervisor/supervisor";
 | 
			
		||||
@@ -63,7 +70,7 @@ class HassioAddonConfigDashboard extends LitElement {
 | 
			
		||||
    `;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static get styles(): CSSResultGroup {
 | 
			
		||||
  static get styles(): CSSResult[] {
 | 
			
		||||
    return [
 | 
			
		||||
      haStyle,
 | 
			
		||||
      hassioStyle,
 | 
			
		||||
 
 | 
			
		||||
@@ -3,16 +3,18 @@ import { ActionDetail } from "@material/mwc-list";
 | 
			
		||||
import "@material/mwc-list/mwc-list-item";
 | 
			
		||||
import { mdiDotsVertical } from "@mdi/js";
 | 
			
		||||
import "@polymer/iron-autogrow-textarea/iron-autogrow-textarea";
 | 
			
		||||
import { DEFAULT_SCHEMA, Type } from "js-yaml";
 | 
			
		||||
import {
 | 
			
		||||
  css,
 | 
			
		||||
  CSSResultGroup,
 | 
			
		||||
  CSSResult,
 | 
			
		||||
  customElement,
 | 
			
		||||
  html,
 | 
			
		||||
  internalProperty,
 | 
			
		||||
  LitElement,
 | 
			
		||||
  property,
 | 
			
		||||
  PropertyValues,
 | 
			
		||||
  query,
 | 
			
		||||
  TemplateResult,
 | 
			
		||||
} from "lit";
 | 
			
		||||
import { customElement, property, query, state } from "lit/decorators";
 | 
			
		||||
} from "lit-element";
 | 
			
		||||
import memoizeOne from "memoize-one";
 | 
			
		||||
import { fireEvent } from "../../../../src/common/dom/fire_event";
 | 
			
		||||
import "../../../../src/components/buttons/ha-progress-button";
 | 
			
		||||
@@ -28,7 +30,6 @@ import {
 | 
			
		||||
  HassioAddonDetails,
 | 
			
		||||
  HassioAddonSetOptionParams,
 | 
			
		||||
  setHassioAddonOption,
 | 
			
		||||
  validateHassioAddonOption,
 | 
			
		||||
} from "../../../../src/data/hassio/addon";
 | 
			
		||||
import { extractApiErrorMessage } from "../../../../src/data/hassio/common";
 | 
			
		||||
import { Supervisor } from "../../../../src/data/supervisor/supervisor";
 | 
			
		||||
@@ -40,13 +41,6 @@ import { hassioStyle } from "../../resources/hassio-style";
 | 
			
		||||
 | 
			
		||||
const SUPPORTED_UI_TYPES = ["string", "select", "boolean", "integer", "float"];
 | 
			
		||||
 | 
			
		||||
const ADDON_YAML_SCHEMA = DEFAULT_SCHEMA.extend([
 | 
			
		||||
  new Type("!secret", {
 | 
			
		||||
    kind: "scalar",
 | 
			
		||||
    construct: (data) => `!secret ${data}`,
 | 
			
		||||
  }),
 | 
			
		||||
]);
 | 
			
		||||
 | 
			
		||||
@customElement("hassio-addon-config")
 | 
			
		||||
class HassioAddonConfig extends LitElement {
 | 
			
		||||
  @property({ attribute: false }) public addon!: HassioAddonDetails;
 | 
			
		||||
@@ -59,27 +53,31 @@ class HassioAddonConfig extends LitElement {
 | 
			
		||||
 | 
			
		||||
  @property({ type: Boolean }) private _valid = true;
 | 
			
		||||
 | 
			
		||||
  @state() private _canShowSchema = false;
 | 
			
		||||
  @internalProperty() private _canShowSchema = false;
 | 
			
		||||
 | 
			
		||||
  @state() private _showOptional = false;
 | 
			
		||||
  @internalProperty() private _showOptional = false;
 | 
			
		||||
 | 
			
		||||
  @state() private _error?: string;
 | 
			
		||||
  @internalProperty() private _error?: string;
 | 
			
		||||
 | 
			
		||||
  @state() private _options?: Record<string, unknown>;
 | 
			
		||||
  @internalProperty() private _options?: Record<string, unknown>;
 | 
			
		||||
 | 
			
		||||
  @state() private _yamlMode = false;
 | 
			
		||||
  @internalProperty() private _yamlMode = false;
 | 
			
		||||
 | 
			
		||||
  @query("ha-yaml-editor") private _editor?: HaYamlEditor;
 | 
			
		||||
 | 
			
		||||
  public computeLabel = (entry: HaFormSchema): string =>
 | 
			
		||||
    this.addon.translations[this.hass.language]?.configuration?.[entry.name]
 | 
			
		||||
      ?.name ||
 | 
			
		||||
    this.addon.translations.en?.configuration?.[entry.name].name ||
 | 
			
		||||
    entry.name;
 | 
			
		||||
  public computeLabel = (entry: HaFormSchema): string => {
 | 
			
		||||
    return (
 | 
			
		||||
      this.addon.translations[this.hass.language]?.configuration?.[entry.name]
 | 
			
		||||
        ?.name ||
 | 
			
		||||
      this.addon.translations.en?.configuration?.[entry.name].name ||
 | 
			
		||||
      entry.name
 | 
			
		||||
    );
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  private _filteredShchema = memoizeOne(
 | 
			
		||||
    (options: Record<string, unknown>, schema: HaFormSchema[]) =>
 | 
			
		||||
      schema.filter((entry) => entry.name in options || entry.required)
 | 
			
		||||
    (options: Record<string, unknown>, schema: HaFormSchema[]) => {
 | 
			
		||||
      return schema.filter((entry) => entry.name in options || entry.required);
 | 
			
		||||
    }
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  protected render(): TemplateResult {
 | 
			
		||||
@@ -134,7 +132,6 @@ class HassioAddonConfig extends LitElement {
 | 
			
		||||
              ></ha-form>`
 | 
			
		||||
            : html` <ha-yaml-editor
 | 
			
		||||
                @value-changed=${this._configChanged}
 | 
			
		||||
                .schema=${ADDON_YAML_SCHEMA}
 | 
			
		||||
              ></ha-yaml-editor>`}
 | 
			
		||||
          ${this._error ? html` <div class="errors">${this._error}</div> ` : ""}
 | 
			
		||||
          ${!this._yamlMode ||
 | 
			
		||||
@@ -269,45 +266,36 @@ class HassioAddonConfig extends LitElement {
 | 
			
		||||
 | 
			
		||||
  private async _saveTapped(ev: CustomEvent): Promise<void> {
 | 
			
		||||
    const button = ev.currentTarget as any;
 | 
			
		||||
    const eventdata = {
 | 
			
		||||
      success: true,
 | 
			
		||||
      response: undefined,
 | 
			
		||||
      path: "options",
 | 
			
		||||
    };
 | 
			
		||||
    button.progress = true;
 | 
			
		||||
 | 
			
		||||
    this._error = undefined;
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
      const validation = await validateHassioAddonOption(
 | 
			
		||||
        this.hass,
 | 
			
		||||
        this.addon.slug,
 | 
			
		||||
        this._editor?.value
 | 
			
		||||
      );
 | 
			
		||||
      if (!validation.valid) {
 | 
			
		||||
        throw Error(validation.message);
 | 
			
		||||
      }
 | 
			
		||||
      await setHassioAddonOption(this.hass, this.addon.slug, {
 | 
			
		||||
        options: this._yamlMode ? this._editor?.value : this._options,
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      this._configHasChanged = false;
 | 
			
		||||
      const eventdata = {
 | 
			
		||||
        success: true,
 | 
			
		||||
        response: undefined,
 | 
			
		||||
        path: "options",
 | 
			
		||||
      };
 | 
			
		||||
      fireEvent(this, "hass-api-called", eventdata);
 | 
			
		||||
      if (this.addon?.state === "started") {
 | 
			
		||||
        await suggestAddonRestart(this, this.hass, this.supervisor, this.addon);
 | 
			
		||||
      }
 | 
			
		||||
    } catch (err) {
 | 
			
		||||
      this._error = this.supervisor.localize(
 | 
			
		||||
        "addon.failed_to_save",
 | 
			
		||||
        "addon.configuration.options.failed_to_save",
 | 
			
		||||
        "error",
 | 
			
		||||
        extractApiErrorMessage(err)
 | 
			
		||||
      );
 | 
			
		||||
      eventdata.success = false;
 | 
			
		||||
    }
 | 
			
		||||
    button.progress = false;
 | 
			
		||||
    fireEvent(this, "hass-api-called", eventdata);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static get styles(): CSSResultGroup {
 | 
			
		||||
  static get styles(): CSSResult[] {
 | 
			
		||||
    return [
 | 
			
		||||
      haStyle,
 | 
			
		||||
      hassioStyle,
 | 
			
		||||
 
 | 
			
		||||
@@ -1,13 +1,15 @@
 | 
			
		||||
import { PaperInputElement } from "@polymer/paper-input/paper-input";
 | 
			
		||||
import {
 | 
			
		||||
  css,
 | 
			
		||||
  CSSResultGroup,
 | 
			
		||||
  CSSResult,
 | 
			
		||||
  customElement,
 | 
			
		||||
  html,
 | 
			
		||||
  internalProperty,
 | 
			
		||||
  LitElement,
 | 
			
		||||
  property,
 | 
			
		||||
  PropertyValues,
 | 
			
		||||
  TemplateResult,
 | 
			
		||||
} from "lit";
 | 
			
		||||
import { customElement, property, state } from "lit/decorators";
 | 
			
		||||
} from "lit-element";
 | 
			
		||||
import { fireEvent } from "../../../../src/common/dom/fire_event";
 | 
			
		||||
import "../../../../src/components/buttons/ha-progress-button";
 | 
			
		||||
import "../../../../src/components/ha-card";
 | 
			
		||||
@@ -41,9 +43,9 @@ class HassioAddonNetwork extends LitElement {
 | 
			
		||||
 | 
			
		||||
  @property({ attribute: false }) public addon!: HassioAddonDetails;
 | 
			
		||||
 | 
			
		||||
  @state() private _error?: string;
 | 
			
		||||
  @internalProperty() private _error?: string;
 | 
			
		||||
 | 
			
		||||
  @state() private _config?: NetworkItem[];
 | 
			
		||||
  @internalProperty() private _config?: NetworkItem[];
 | 
			
		||||
 | 
			
		||||
  public connectedCallback(): void {
 | 
			
		||||
    super.connectedCallback();
 | 
			
		||||
@@ -77,10 +79,12 @@ class HassioAddonNetwork extends LitElement {
 | 
			
		||||
                    "addon.configuration.network.host"
 | 
			
		||||
                  )}
 | 
			
		||||
                </th>
 | 
			
		||||
                <th>${this.supervisor.localize("common.description")}</th>
 | 
			
		||||
                <th>
 | 
			
		||||
                  ${this.supervisor.localize("common.description")}
 | 
			
		||||
                </th>
 | 
			
		||||
              </tr>
 | 
			
		||||
              ${this._config!.map(
 | 
			
		||||
                (item) => html`
 | 
			
		||||
              ${this._config!.map((item) => {
 | 
			
		||||
                return html`
 | 
			
		||||
                  <tr>
 | 
			
		||||
                    <td>${item.container}</td>
 | 
			
		||||
                    <td>
 | 
			
		||||
@@ -96,8 +100,8 @@ class HassioAddonNetwork extends LitElement {
 | 
			
		||||
                    </td>
 | 
			
		||||
                    <td>${this._computeDescription(item)}</td>
 | 
			
		||||
                  </tr>
 | 
			
		||||
                `
 | 
			
		||||
              )}
 | 
			
		||||
                `;
 | 
			
		||||
              })}
 | 
			
		||||
            </tbody>
 | 
			
		||||
          </table>
 | 
			
		||||
        </div>
 | 
			
		||||
@@ -120,20 +124,25 @@ class HassioAddonNetwork extends LitElement {
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private _computeDescription = (item: NetworkItem): string =>
 | 
			
		||||
    this.addon.translations[this.hass.language]?.network?.[item.container]
 | 
			
		||||
      ?.description ||
 | 
			
		||||
    this.addon.translations.en?.network?.[item.container]?.description ||
 | 
			
		||||
    item.description;
 | 
			
		||||
  private _computeDescription = (item: NetworkItem): string => {
 | 
			
		||||
    return (
 | 
			
		||||
      this.addon.translations[this.hass.language]?.network?.[item.container]
 | 
			
		||||
        ?.description ||
 | 
			
		||||
      this.addon.translations.en?.network?.[item.container]?.description ||
 | 
			
		||||
      item.description
 | 
			
		||||
    );
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  private _setNetworkConfig(): void {
 | 
			
		||||
    const network = this.addon.network || {};
 | 
			
		||||
    const description = this.addon.network_description || {};
 | 
			
		||||
    const items: NetworkItem[] = Object.keys(network).map((key) => ({
 | 
			
		||||
      container: key,
 | 
			
		||||
      host: network[key],
 | 
			
		||||
      description: description[key],
 | 
			
		||||
    }));
 | 
			
		||||
    const items: NetworkItem[] = Object.keys(network).map((key) => {
 | 
			
		||||
      return {
 | 
			
		||||
        container: key,
 | 
			
		||||
        host: network[key],
 | 
			
		||||
        description: description[key],
 | 
			
		||||
      };
 | 
			
		||||
    });
 | 
			
		||||
    this._config = items.sort((a, b) => (a.container > b.container ? 1 : -1));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -214,7 +223,7 @@ class HassioAddonNetwork extends LitElement {
 | 
			
		||||
    button.progress = false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static get styles(): CSSResultGroup {
 | 
			
		||||
  static get styles(): CSSResult[] {
 | 
			
		||||
    return [
 | 
			
		||||
      haStyle,
 | 
			
		||||
      hassioStyle,
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,14 @@
 | 
			
		||||
import "../../../../src/components/ha-card";
 | 
			
		||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
 | 
			
		||||
import {
 | 
			
		||||
  css,
 | 
			
		||||
  CSSResult,
 | 
			
		||||
  customElement,
 | 
			
		||||
  html,
 | 
			
		||||
  internalProperty,
 | 
			
		||||
  LitElement,
 | 
			
		||||
  property,
 | 
			
		||||
  TemplateResult,
 | 
			
		||||
} from "lit-element";
 | 
			
		||||
import "../../../../src/components/ha-circular-progress";
 | 
			
		||||
import "../../../../src/components/ha-markdown";
 | 
			
		||||
import {
 | 
			
		||||
@@ -12,7 +21,6 @@ import { haStyle } from "../../../../src/resources/styles";
 | 
			
		||||
import { HomeAssistant } from "../../../../src/types";
 | 
			
		||||
import { hassioStyle } from "../../resources/hassio-style";
 | 
			
		||||
import { Supervisor } from "../../../../src/data/supervisor/supervisor";
 | 
			
		||||
import { customElement, property, state } from "lit/decorators";
 | 
			
		||||
 | 
			
		||||
@customElement("hassio-addon-documentation-tab")
 | 
			
		||||
class HassioAddonDocumentationDashboard extends LitElement {
 | 
			
		||||
@@ -22,9 +30,9 @@ class HassioAddonDocumentationDashboard extends LitElement {
 | 
			
		||||
 | 
			
		||||
  @property({ attribute: false }) public addon?: HassioAddonDetails;
 | 
			
		||||
 | 
			
		||||
  @state() private _error?: string;
 | 
			
		||||
  @internalProperty() private _error?: string;
 | 
			
		||||
 | 
			
		||||
  @state() private _content?: string;
 | 
			
		||||
  @internalProperty() private _content?: string;
 | 
			
		||||
 | 
			
		||||
  public async connectedCallback(): Promise<void> {
 | 
			
		||||
    super.connectedCallback();
 | 
			
		||||
@@ -49,7 +57,7 @@ class HassioAddonDocumentationDashboard extends LitElement {
 | 
			
		||||
    `;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static get styles(): CSSResultGroup {
 | 
			
		||||
  static get styles(): CSSResult[] {
 | 
			
		||||
    return [
 | 
			
		||||
      haStyle,
 | 
			
		||||
      hassioStyle,
 | 
			
		||||
 
 | 
			
		||||
@@ -4,8 +4,16 @@ import {
 | 
			
		||||
  mdiInformationVariant,
 | 
			
		||||
  mdiMathLog,
 | 
			
		||||
} from "@mdi/js";
 | 
			
		||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
 | 
			
		||||
import { customElement, property, state } from "lit/decorators";
 | 
			
		||||
import {
 | 
			
		||||
  css,
 | 
			
		||||
  CSSResult,
 | 
			
		||||
  customElement,
 | 
			
		||||
  html,
 | 
			
		||||
  internalProperty,
 | 
			
		||||
  LitElement,
 | 
			
		||||
  property,
 | 
			
		||||
  TemplateResult,
 | 
			
		||||
} from "lit-element";
 | 
			
		||||
import memoizeOne from "memoize-one";
 | 
			
		||||
import { fireEvent } from "../../../src/common/dom/fire_event";
 | 
			
		||||
import { navigate } from "../../../src/common/navigate";
 | 
			
		||||
@@ -44,7 +52,7 @@ class HassioAddonDashboard extends LitElement {
 | 
			
		||||
 | 
			
		||||
  @property({ type: Boolean }) public narrow!: boolean;
 | 
			
		||||
 | 
			
		||||
  @state() _error?: string;
 | 
			
		||||
  @internalProperty() _error?: string;
 | 
			
		||||
 | 
			
		||||
  private _computeTail = memoizeOne((route: Route) => {
 | 
			
		||||
    const dividerPos = route.path.indexOf("/", 1);
 | 
			
		||||
@@ -125,7 +133,7 @@ class HassioAddonDashboard extends LitElement {
 | 
			
		||||
    `;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static get styles(): CSSResultGroup {
 | 
			
		||||
  static get styles(): CSSResult[] {
 | 
			
		||||
    return [
 | 
			
		||||
      haStyle,
 | 
			
		||||
      hassioStyle,
 | 
			
		||||
@@ -175,7 +183,7 @@ class HassioAddonDashboard extends LitElement {
 | 
			
		||||
        if (!validAddon) {
 | 
			
		||||
          this._error = this.supervisor.localize("my.error_addon_not_found");
 | 
			
		||||
        } else {
 | 
			
		||||
          navigate(`/hassio/addon/${requestedAddon}`, { replace: true });
 | 
			
		||||
          navigate(this, `/hassio/addon/${requestedAddon}`, true);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
@@ -183,10 +191,6 @@ class HassioAddonDashboard extends LitElement {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private async _apiCalled(ev): Promise<void> {
 | 
			
		||||
    if (!ev.detail.success) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const pathSplit: string[] = ev.detail.path?.split("/");
 | 
			
		||||
 | 
			
		||||
    if (!pathSplit || pathSplit.length === 0) {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
import { customElement, property } from "lit/decorators";
 | 
			
		||||
import { customElement, property } from "lit-element";
 | 
			
		||||
import { HassioAddonDetails } from "../../../src/data/hassio/addon";
 | 
			
		||||
import { Supervisor } from "../../../src/data/supervisor/supervisor";
 | 
			
		||||
import {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,12 @@
 | 
			
		||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
 | 
			
		||||
import { customElement, property } from "lit/decorators";
 | 
			
		||||
import {
 | 
			
		||||
  css,
 | 
			
		||||
  CSSResult,
 | 
			
		||||
  customElement,
 | 
			
		||||
  html,
 | 
			
		||||
  LitElement,
 | 
			
		||||
  property,
 | 
			
		||||
  TemplateResult,
 | 
			
		||||
} from "lit-element";
 | 
			
		||||
import "../../../../src/components/ha-circular-progress";
 | 
			
		||||
import { HassioAddonDetails } from "../../../../src/data/hassio/addon";
 | 
			
		||||
import { Supervisor } from "../../../../src/data/supervisor/supervisor";
 | 
			
		||||
@@ -35,7 +42,7 @@ class HassioAddonInfoDashboard extends LitElement {
 | 
			
		||||
    `;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static get styles(): CSSResultGroup {
 | 
			
		||||
  static get styles(): CSSResult[] {
 | 
			
		||||
    return [
 | 
			
		||||
      haStyle,
 | 
			
		||||
      hassioStyle,
 | 
			
		||||
 
 | 
			
		||||
@@ -14,9 +14,17 @@ import {
 | 
			
		||||
  mdiPound,
 | 
			
		||||
  mdiShield,
 | 
			
		||||
} from "@mdi/js";
 | 
			
		||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
 | 
			
		||||
import { customElement, property, state } from "lit/decorators";
 | 
			
		||||
import { classMap } from "lit/directives/class-map";
 | 
			
		||||
import {
 | 
			
		||||
  css,
 | 
			
		||||
  CSSResult,
 | 
			
		||||
  customElement,
 | 
			
		||||
  html,
 | 
			
		||||
  internalProperty,
 | 
			
		||||
  LitElement,
 | 
			
		||||
  property,
 | 
			
		||||
  TemplateResult,
 | 
			
		||||
} from "lit-element";
 | 
			
		||||
import { classMap } from "lit-html/directives/class-map";
 | 
			
		||||
import memoizeOne from "memoize-one";
 | 
			
		||||
import { atLeastVersion } from "../../../../src/common/config/version";
 | 
			
		||||
import { fireEvent } from "../../../../src/common/dom/fire_event";
 | 
			
		||||
@@ -82,9 +90,9 @@ class HassioAddonInfo extends LitElement {
 | 
			
		||||
 | 
			
		||||
  @property({ attribute: false }) public supervisor!: Supervisor;
 | 
			
		||||
 | 
			
		||||
  @state() private _metrics?: HassioStats;
 | 
			
		||||
  @internalProperty() private _metrics?: HassioStats;
 | 
			
		||||
 | 
			
		||||
  @state() private _error?: string;
 | 
			
		||||
  @internalProperty() private _error?: string;
 | 
			
		||||
 | 
			
		||||
  private _addonStoreInfo = memoizeOne(
 | 
			
		||||
    (slug: string, storeAddons: StoreAddon[]) =>
 | 
			
		||||
@@ -163,16 +171,16 @@ class HassioAddonInfo extends LitElement {
 | 
			
		||||
                  : ""}
 | 
			
		||||
              </div>
 | 
			
		||||
              <div class="card-actions">
 | 
			
		||||
                <mwc-button @click=${this._updateClicked}>
 | 
			
		||||
                  ${this.supervisor.localize("common.update")}
 | 
			
		||||
                </mwc-button>
 | 
			
		||||
                ${this.addon.changelog
 | 
			
		||||
                  ? html`
 | 
			
		||||
                      <mwc-button @click=${this._openChangelog}>
 | 
			
		||||
                        ${this.supervisor.localize("addon.dashboard.changelog")}
 | 
			
		||||
                      </mwc-button>
 | 
			
		||||
                    `
 | 
			
		||||
                  : html`<span></span>`}
 | 
			
		||||
                <mwc-button @click=${this._updateClicked}>
 | 
			
		||||
                  ${this.supervisor.localize("common.update")}
 | 
			
		||||
                </mwc-button>
 | 
			
		||||
                  : ""}
 | 
			
		||||
              </div>
 | 
			
		||||
            </ha-card>
 | 
			
		||||
          `
 | 
			
		||||
@@ -253,9 +261,13 @@ class HassioAddonInfo extends LitElement {
 | 
			
		||||
            ${this.supervisor.localize(
 | 
			
		||||
              "addon.dashboard.visit_addon_page",
 | 
			
		||||
              "name",
 | 
			
		||||
              html`<a href="${this.addon.url!}" target="_blank" rel="noreferrer"
 | 
			
		||||
                >${this.addon.name}</a
 | 
			
		||||
              >`
 | 
			
		||||
              html`<a
 | 
			
		||||
                href="${this.addon.url!}"
 | 
			
		||||
                target="_blank"
 | 
			
		||||
                rel="noreferrer"
 | 
			
		||||
              >
 | 
			
		||||
                ${this.addon.name}
 | 
			
		||||
              </a>`
 | 
			
		||||
            )}
 | 
			
		||||
          </div>
 | 
			
		||||
          <div class="addon-container">
 | 
			
		||||
@@ -554,7 +566,9 @@ class HassioAddonInfo extends LitElement {
 | 
			
		||||
                      <span slot="heading">
 | 
			
		||||
                        ${this.supervisor.localize("addon.dashboard.hostname")}
 | 
			
		||||
                      </span>
 | 
			
		||||
                      <code slot="description"> ${this.addon.hostname} </code>
 | 
			
		||||
                      <code slot="description">
 | 
			
		||||
                        ${this.addon.hostname}
 | 
			
		||||
                      </code>
 | 
			
		||||
                    </ha-settings-row>
 | 
			
		||||
                    ${metrics.map(
 | 
			
		||||
                      (metric) =>
 | 
			
		||||
@@ -761,7 +775,7 @@ class HassioAddonInfo extends LitElement {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private _openIngress(): void {
 | 
			
		||||
    navigate(`/hassio/ingress/${this.addon.slug}`);
 | 
			
		||||
    navigate(this, `/hassio/ingress/${this.addon.slug}`);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private get _computeShowIngressUI(): boolean {
 | 
			
		||||
@@ -983,7 +997,7 @@ class HassioAddonInfo extends LitElement {
 | 
			
		||||
        addons: [this.addon.slug],
 | 
			
		||||
        homeassistant: false,
 | 
			
		||||
      },
 | 
			
		||||
      updateHandler: async () => this._updateAddon(),
 | 
			
		||||
      updateHandler: async () => await this._updateAddon(),
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -1051,7 +1065,7 @@ class HassioAddonInfo extends LitElement {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private _openConfiguration(): void {
 | 
			
		||||
    navigate(`/hassio/addon/${this.addon.slug}/config`);
 | 
			
		||||
    navigate(this, `/hassio/addon/${this.addon.slug}/config`);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private async _uninstallClicked(ev: CustomEvent): Promise<void> {
 | 
			
		||||
@@ -1090,7 +1104,7 @@ class HassioAddonInfo extends LitElement {
 | 
			
		||||
    button.progress = false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static get styles(): CSSResultGroup {
 | 
			
		||||
  static get styles(): CSSResult[] {
 | 
			
		||||
    return [
 | 
			
		||||
      haStyle,
 | 
			
		||||
      hassioStyle,
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,12 @@
 | 
			
		||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
 | 
			
		||||
import { customElement, property } from "lit/decorators";
 | 
			
		||||
import {
 | 
			
		||||
  css,
 | 
			
		||||
  CSSResult,
 | 
			
		||||
  customElement,
 | 
			
		||||
  html,
 | 
			
		||||
  LitElement,
 | 
			
		||||
  property,
 | 
			
		||||
  TemplateResult,
 | 
			
		||||
} from "lit-element";
 | 
			
		||||
import "../../../../src/components/ha-circular-progress";
 | 
			
		||||
import { HassioAddonDetails } from "../../../../src/data/hassio/addon";
 | 
			
		||||
import { Supervisor } from "../../../../src/data/supervisor/supervisor";
 | 
			
		||||
@@ -31,7 +38,7 @@ class HassioAddonLogDashboard extends LitElement {
 | 
			
		||||
    `;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static get styles(): CSSResultGroup {
 | 
			
		||||
  static get styles(): CSSResult[] {
 | 
			
		||||
    return [
 | 
			
		||||
      haStyle,
 | 
			
		||||
      hassioStyle,
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,14 @@
 | 
			
		||||
import "@material/mwc-button";
 | 
			
		||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
 | 
			
		||||
import { customElement, property, state } from "lit/decorators";
 | 
			
		||||
import {
 | 
			
		||||
  css,
 | 
			
		||||
  CSSResult,
 | 
			
		||||
  customElement,
 | 
			
		||||
  html,
 | 
			
		||||
  internalProperty,
 | 
			
		||||
  LitElement,
 | 
			
		||||
  property,
 | 
			
		||||
  TemplateResult,
 | 
			
		||||
} from "lit-element";
 | 
			
		||||
import "../../../../src/components/ha-card";
 | 
			
		||||
import {
 | 
			
		||||
  fetchHassioAddonLogs,
 | 
			
		||||
@@ -21,9 +29,9 @@ class HassioAddonLogs extends LitElement {
 | 
			
		||||
 | 
			
		||||
  @property({ attribute: false }) public addon!: HassioAddonDetails;
 | 
			
		||||
 | 
			
		||||
  @state() private _error?: string;
 | 
			
		||||
  @internalProperty() private _error?: string;
 | 
			
		||||
 | 
			
		||||
  @state() private _content?: string;
 | 
			
		||||
  @internalProperty() private _content?: string;
 | 
			
		||||
 | 
			
		||||
  public async connectedCallback(): Promise<void> {
 | 
			
		||||
    super.connectedCallback();
 | 
			
		||||
@@ -51,7 +59,7 @@ class HassioAddonLogs extends LitElement {
 | 
			
		||||
    `;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static get styles(): CSSResultGroup {
 | 
			
		||||
  static get styles(): CSSResult[] {
 | 
			
		||||
    return [
 | 
			
		||||
      haStyle,
 | 
			
		||||
      hassioStyle,
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,12 @@
 | 
			
		||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
 | 
			
		||||
import { customElement, property } from "lit/decorators";
 | 
			
		||||
import {
 | 
			
		||||
  css,
 | 
			
		||||
  CSSResult,
 | 
			
		||||
  customElement,
 | 
			
		||||
  html,
 | 
			
		||||
  LitElement,
 | 
			
		||||
  property,
 | 
			
		||||
  TemplateResult,
 | 
			
		||||
} from "lit-element";
 | 
			
		||||
 | 
			
		||||
interface State {
 | 
			
		||||
  bold: boolean;
 | 
			
		||||
@@ -18,7 +25,7 @@ class HassioAnsiToHtml extends LitElement {
 | 
			
		||||
    return html`${this._parseTextToColoredPre(this.content)}`;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static get styles(): CSSResultGroup {
 | 
			
		||||
  static get styles(): CSSResult {
 | 
			
		||||
    return css`
 | 
			
		||||
      pre {
 | 
			
		||||
        overflow-x: auto;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,13 @@
 | 
			
		||||
import { mdiHelpCircle } from "@mdi/js";
 | 
			
		||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
 | 
			
		||||
import { customElement, property } from "lit/decorators";
 | 
			
		||||
import {
 | 
			
		||||
  css,
 | 
			
		||||
  CSSResult,
 | 
			
		||||
  customElement,
 | 
			
		||||
  html,
 | 
			
		||||
  LitElement,
 | 
			
		||||
  property,
 | 
			
		||||
  TemplateResult,
 | 
			
		||||
} from "lit-element";
 | 
			
		||||
import "../../../src/components/ha-relative-time";
 | 
			
		||||
import "../../../src/components/ha-svg-icon";
 | 
			
		||||
import { HomeAssistant } from "../../../src/types";
 | 
			
		||||
@@ -49,13 +56,13 @@ class HassioCardContent extends LitElement {
 | 
			
		||||
            ></ha-svg-icon>
 | 
			
		||||
          `}
 | 
			
		||||
      <div>
 | 
			
		||||
        <div class="title">${this.title}</div>
 | 
			
		||||
        <div class="title">
 | 
			
		||||
          ${this.title}
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="addition">
 | 
			
		||||
          ${this.description}
 | 
			
		||||
          ${
 | 
			
		||||
            /* treat as available when undefined */
 | 
			
		||||
            this.available === false ? " (Not available)" : ""
 | 
			
		||||
          }
 | 
			
		||||
          ${/* treat as available when undefined */
 | 
			
		||||
          this.available === false ? " (Not available)" : ""}
 | 
			
		||||
          ${this.datetime
 | 
			
		||||
            ? html`
 | 
			
		||||
                <ha-relative-time
 | 
			
		||||
@@ -70,7 +77,7 @@ class HassioCardContent extends LitElement {
 | 
			
		||||
    `;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static get styles(): CSSResultGroup {
 | 
			
		||||
  static get styles(): CSSResult {
 | 
			
		||||
    return css`
 | 
			
		||||
      ha-svg-icon {
 | 
			
		||||
        margin-right: 24px;
 | 
			
		||||
 
 | 
			
		||||
@@ -2,8 +2,13 @@ import "@material/mwc-icon-button/mwc-icon-button";
 | 
			
		||||
import { mdiFolderUpload } from "@mdi/js";
 | 
			
		||||
import "@polymer/iron-input/iron-input";
 | 
			
		||||
import "@polymer/paper-input/paper-input-container";
 | 
			
		||||
import { html, LitElement, TemplateResult } from "lit";
 | 
			
		||||
import { customElement, state } from "lit/decorators";
 | 
			
		||||
import {
 | 
			
		||||
  customElement,
 | 
			
		||||
  html,
 | 
			
		||||
  internalProperty,
 | 
			
		||||
  LitElement,
 | 
			
		||||
  TemplateResult,
 | 
			
		||||
} from "lit-element";
 | 
			
		||||
import { fireEvent } from "../../../src/common/dom/fire_event";
 | 
			
		||||
import "../../../src/components/ha-circular-progress";
 | 
			
		||||
import "../../../src/components/ha-file-upload";
 | 
			
		||||
@@ -28,9 +33,9 @@ const MAX_FILE_SIZE = 1 * 1024 * 1024 * 1024; // 1GB
 | 
			
		||||
export class HassioUploadSnapshot extends LitElement {
 | 
			
		||||
  public hass!: HomeAssistant;
 | 
			
		||||
 | 
			
		||||
  @state() public value: string | null = null;
 | 
			
		||||
  @internalProperty() public value: string | null = null;
 | 
			
		||||
 | 
			
		||||
  @state() private _uploading = false;
 | 
			
		||||
  @internalProperty() private _uploading = false;
 | 
			
		||||
 | 
			
		||||
  public render(): TemplateResult {
 | 
			
		||||
    return html`
 | 
			
		||||
 
 | 
			
		||||
@@ -1,54 +0,0 @@
 | 
			
		||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
 | 
			
		||||
import { customElement, property } from "lit/decorators";
 | 
			
		||||
import "../../../src/components/ha-svg-icon";
 | 
			
		||||
 | 
			
		||||
@customElement("supervisor-formfield-label")
 | 
			
		||||
class SupervisorFormfieldLabel extends LitElement {
 | 
			
		||||
  @property({ type: String }) public label!: string;
 | 
			
		||||
 | 
			
		||||
  @property({ type: String }) public imageUrl?: string;
 | 
			
		||||
 | 
			
		||||
  @property({ type: String }) public iconPath?: string;
 | 
			
		||||
 | 
			
		||||
  @property({ type: String }) public version?: string;
 | 
			
		||||
 | 
			
		||||
  protected render(): TemplateResult {
 | 
			
		||||
    return html`
 | 
			
		||||
      ${this.imageUrl
 | 
			
		||||
        ? html`<img loading="lazy" .src=${this.imageUrl} class="icon" />`
 | 
			
		||||
        : this.iconPath
 | 
			
		||||
        ? html`<ha-svg-icon .path=${this.iconPath} class="icon"></ha-svg-icon>`
 | 
			
		||||
        : ""}
 | 
			
		||||
      <span class="label">${this.label}</span>
 | 
			
		||||
      ${this.version
 | 
			
		||||
        ? html`<span class="version">(${this.version})</span>`
 | 
			
		||||
        : ""}
 | 
			
		||||
    `;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static get styles(): CSSResultGroup {
 | 
			
		||||
    return css`
 | 
			
		||||
      :host {
 | 
			
		||||
        display: flex;
 | 
			
		||||
        align-items: center;
 | 
			
		||||
      }
 | 
			
		||||
      .label {
 | 
			
		||||
        margin-right: 4px;
 | 
			
		||||
      }
 | 
			
		||||
      .version {
 | 
			
		||||
        color: var(--secondary-text-color);
 | 
			
		||||
      }
 | 
			
		||||
      .icon {
 | 
			
		||||
        max-height: 22px;
 | 
			
		||||
        max-width: 22px;
 | 
			
		||||
        margin-right: 8px;
 | 
			
		||||
      }
 | 
			
		||||
    `;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
declare global {
 | 
			
		||||
  interface HTMLElementTagNameMap {
 | 
			
		||||
    "supervisor-formfield-label": SupervisorFormfieldLabel;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,6 +1,13 @@
 | 
			
		||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
 | 
			
		||||
import { customElement, property } from "lit/decorators";
 | 
			
		||||
import { classMap } from "lit/directives/class-map";
 | 
			
		||||
import {
 | 
			
		||||
  css,
 | 
			
		||||
  CSSResult,
 | 
			
		||||
  customElement,
 | 
			
		||||
  html,
 | 
			
		||||
  LitElement,
 | 
			
		||||
  property,
 | 
			
		||||
  TemplateResult,
 | 
			
		||||
} from "lit-element";
 | 
			
		||||
import { classMap } from "lit-html/directives/class-map";
 | 
			
		||||
import "../../../src/components/ha-bar";
 | 
			
		||||
import "../../../src/components/ha-settings-row";
 | 
			
		||||
import { roundWithOneDecimal } from "../../../src/util/calculate";
 | 
			
		||||
@@ -16,9 +23,13 @@ class SupervisorMetric extends LitElement {
 | 
			
		||||
  protected render(): TemplateResult {
 | 
			
		||||
    const roundedValue = roundWithOneDecimal(this.value);
 | 
			
		||||
    return html`<ha-settings-row>
 | 
			
		||||
      <span slot="heading"> ${this.description} </span>
 | 
			
		||||
      <span slot="heading">
 | 
			
		||||
        ${this.description}
 | 
			
		||||
      </span>
 | 
			
		||||
      <div slot="description" .title=${this.tooltip ?? ""}>
 | 
			
		||||
        <span class="value"> ${roundedValue} % </span>
 | 
			
		||||
        <span class="value">
 | 
			
		||||
          ${roundedValue} %
 | 
			
		||||
        </span>
 | 
			
		||||
        <ha-bar
 | 
			
		||||
          class="${classMap({
 | 
			
		||||
            "target-warning": roundedValue > 50,
 | 
			
		||||
@@ -30,7 +41,7 @@ class SupervisorMetric extends LitElement {
 | 
			
		||||
    </ha-settings-row>`;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static get styles(): CSSResultGroup {
 | 
			
		||||
  static get styles(): CSSResult {
 | 
			
		||||
    return css`
 | 
			
		||||
      ha-settings-row {
 | 
			
		||||
        padding: 0;
 | 
			
		||||
@@ -64,7 +75,6 @@ class SupervisorMetric extends LitElement {
 | 
			
		||||
      .value {
 | 
			
		||||
        width: 48px;
 | 
			
		||||
        padding-right: 4px;
 | 
			
		||||
        flex-shrink: 0;
 | 
			
		||||
      }
 | 
			
		||||
    `;
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,450 +0,0 @@
 | 
			
		||||
import { mdiFolder, mdiHomeAssistant, mdiPuzzle } from "@mdi/js";
 | 
			
		||||
import { PaperInputElement } from "@polymer/paper-input/paper-input";
 | 
			
		||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
 | 
			
		||||
import { customElement, property } from "lit/decorators";
 | 
			
		||||
import { atLeastVersion } from "../../../src/common/config/version";
 | 
			
		||||
import { formatDate } from "../../../src/common/datetime/format_date";
 | 
			
		||||
import { formatDateTime } from "../../../src/common/datetime/format_date_time";
 | 
			
		||||
import { LocalizeFunc } from "../../../src/common/translations/localize";
 | 
			
		||||
import "../../../src/components/ha-checkbox";
 | 
			
		||||
import "../../../src/components/ha-formfield";
 | 
			
		||||
import "../../../src/components/ha-radio";
 | 
			
		||||
import type { HaRadio } from "../../../src/components/ha-radio";
 | 
			
		||||
import {
 | 
			
		||||
  HassioFullSnapshotCreateParams,
 | 
			
		||||
  HassioPartialSnapshotCreateParams,
 | 
			
		||||
  HassioSnapshotDetail,
 | 
			
		||||
} from "../../../src/data/hassio/snapshot";
 | 
			
		||||
import { Supervisor } from "../../../src/data/supervisor/supervisor";
 | 
			
		||||
import { PolymerChangedEvent } from "../../../src/polymer-types";
 | 
			
		||||
import { HomeAssistant } from "../../../src/types";
 | 
			
		||||
import "./supervisor-formfield-label";
 | 
			
		||||
 | 
			
		||||
interface CheckboxItem {
 | 
			
		||||
  slug: string;
 | 
			
		||||
  checked: boolean;
 | 
			
		||||
  name: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
interface AddonCheckboxItem extends CheckboxItem {
 | 
			
		||||
  version: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const _computeFolders = (folders): CheckboxItem[] => {
 | 
			
		||||
  const list: CheckboxItem[] = [];
 | 
			
		||||
  if (folders.includes("homeassistant")) {
 | 
			
		||||
    list.push({
 | 
			
		||||
      slug: "homeassistant",
 | 
			
		||||
      name: "Home Assistant configuration",
 | 
			
		||||
      checked: false,
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
  if (folders.includes("ssl")) {
 | 
			
		||||
    list.push({ slug: "ssl", name: "SSL", checked: false });
 | 
			
		||||
  }
 | 
			
		||||
  if (folders.includes("share")) {
 | 
			
		||||
    list.push({ slug: "share", name: "Share", checked: false });
 | 
			
		||||
  }
 | 
			
		||||
  if (folders.includes("media")) {
 | 
			
		||||
    list.push({ slug: "media", name: "Media", checked: false });
 | 
			
		||||
  }
 | 
			
		||||
  if (folders.includes("addons/local")) {
 | 
			
		||||
    list.push({ slug: "addons/local", name: "Local add-ons", checked: false });
 | 
			
		||||
  }
 | 
			
		||||
  return list.sort((a, b) => (a.name > b.name ? 1 : -1));
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const _computeAddons = (addons): AddonCheckboxItem[] =>
 | 
			
		||||
  addons
 | 
			
		||||
    .map((addon) => ({
 | 
			
		||||
      slug: addon.slug,
 | 
			
		||||
      name: addon.name,
 | 
			
		||||
      version: addon.version,
 | 
			
		||||
      checked: false,
 | 
			
		||||
    }))
 | 
			
		||||
    .sort((a, b) => (a.name > b.name ? 1 : -1));
 | 
			
		||||
 | 
			
		||||
@customElement("supervisor-snapshot-content")
 | 
			
		||||
export class SupervisorSnapshotContent extends LitElement {
 | 
			
		||||
  @property({ attribute: false }) public hass!: HomeAssistant;
 | 
			
		||||
 | 
			
		||||
  @property() public localize?: LocalizeFunc;
 | 
			
		||||
 | 
			
		||||
  @property({ attribute: false }) public supervisor?: Supervisor;
 | 
			
		||||
 | 
			
		||||
  @property({ attribute: false }) public snapshot?: HassioSnapshotDetail;
 | 
			
		||||
 | 
			
		||||
  @property() public snapshotType: HassioSnapshotDetail["type"] = "full";
 | 
			
		||||
 | 
			
		||||
  @property({ attribute: false }) public folders?: CheckboxItem[];
 | 
			
		||||
 | 
			
		||||
  @property({ attribute: false }) public addons?: AddonCheckboxItem[];
 | 
			
		||||
 | 
			
		||||
  @property({ type: Boolean }) public homeAssistant = false;
 | 
			
		||||
 | 
			
		||||
  @property({ type: Boolean }) public snapshotHasPassword = false;
 | 
			
		||||
 | 
			
		||||
  @property({ type: Boolean }) public onboarding = false;
 | 
			
		||||
 | 
			
		||||
  @property() public snapshotName = "";
 | 
			
		||||
 | 
			
		||||
  @property() public snapshotPassword = "";
 | 
			
		||||
 | 
			
		||||
  @property() public confirmSnapshotPassword = "";
 | 
			
		||||
 | 
			
		||||
  public willUpdate(changedProps) {
 | 
			
		||||
    super.willUpdate(changedProps);
 | 
			
		||||
    if (!this.hasUpdated) {
 | 
			
		||||
      this.folders = _computeFolders(
 | 
			
		||||
        this.snapshot
 | 
			
		||||
          ? this.snapshot.folders
 | 
			
		||||
          : ["homeassistant", "ssl", "share", "media", "addons/local"]
 | 
			
		||||
      );
 | 
			
		||||
      this.addons = _computeAddons(
 | 
			
		||||
        this.snapshot
 | 
			
		||||
          ? this.snapshot.addons
 | 
			
		||||
          : this.supervisor?.supervisor.addons
 | 
			
		||||
      );
 | 
			
		||||
      this.snapshotType = this.snapshot?.type || "full";
 | 
			
		||||
      this.snapshotName = this.snapshot?.name || "";
 | 
			
		||||
      this.snapshotHasPassword = this.snapshot?.protected || false;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private _localize = (string: string) =>
 | 
			
		||||
    this.supervisor?.localize(`snapshot.${string}`) ||
 | 
			
		||||
    this.localize!(`ui.panel.page-onboarding.restore.${string}`);
 | 
			
		||||
 | 
			
		||||
  protected render(): TemplateResult {
 | 
			
		||||
    if (!this.onboarding && !this.supervisor) {
 | 
			
		||||
      return html``;
 | 
			
		||||
    }
 | 
			
		||||
    const foldersSection =
 | 
			
		||||
      this.snapshotType === "partial" ? this._getSection("folders") : undefined;
 | 
			
		||||
    const addonsSection =
 | 
			
		||||
      this.snapshotType === "partial" ? this._getSection("addons") : undefined;
 | 
			
		||||
 | 
			
		||||
    return html`
 | 
			
		||||
      ${this.snapshot
 | 
			
		||||
        ? html`<div class="details">
 | 
			
		||||
            ${this.snapshot.type === "full"
 | 
			
		||||
              ? this._localize("full_snapshot")
 | 
			
		||||
              : this._localize("partial_snapshot")}
 | 
			
		||||
            (${Math.ceil(this.snapshot.size * 10) / 10 + " MB"})<br />
 | 
			
		||||
            ${this.hass
 | 
			
		||||
              ? formatDateTime(new Date(this.snapshot.date), this.hass.locale)
 | 
			
		||||
              : this.snapshot.date}
 | 
			
		||||
          </div>`
 | 
			
		||||
        : html`<paper-input
 | 
			
		||||
            name="snapshotName"
 | 
			
		||||
            .label=${this.supervisor?.localize("snapshot.name") || "Name"}
 | 
			
		||||
            .value=${this.snapshotName}
 | 
			
		||||
            @value-changed=${this._handleTextValueChanged}
 | 
			
		||||
          >
 | 
			
		||||
          </paper-input>`}
 | 
			
		||||
      ${!this.snapshot || this.snapshot.type === "full"
 | 
			
		||||
        ? html`<div class="sub-header">
 | 
			
		||||
              ${!this.snapshot
 | 
			
		||||
                ? this._localize("type")
 | 
			
		||||
                : this._localize("select_type")}
 | 
			
		||||
            </div>
 | 
			
		||||
            <div class="snapshot-types">
 | 
			
		||||
              <ha-formfield .label=${this._localize("full_snapshot")}>
 | 
			
		||||
                <ha-radio
 | 
			
		||||
                  @change=${this._handleRadioValueChanged}
 | 
			
		||||
                  value="full"
 | 
			
		||||
                  name="snapshotType"
 | 
			
		||||
                  .checked=${this.snapshotType === "full"}
 | 
			
		||||
                >
 | 
			
		||||
                </ha-radio>
 | 
			
		||||
              </ha-formfield>
 | 
			
		||||
              <ha-formfield .label=${this._localize("partial_snapshot")}>
 | 
			
		||||
                <ha-radio
 | 
			
		||||
                  @change=${this._handleRadioValueChanged}
 | 
			
		||||
                  value="partial"
 | 
			
		||||
                  name="snapshotType"
 | 
			
		||||
                  .checked=${this.snapshotType === "partial"}
 | 
			
		||||
                >
 | 
			
		||||
                </ha-radio>
 | 
			
		||||
              </ha-formfield>
 | 
			
		||||
            </div>`
 | 
			
		||||
        : ""}
 | 
			
		||||
      ${this.snapshotType === "partial"
 | 
			
		||||
        ? html`<div class="partial-picker">
 | 
			
		||||
            ${this.snapshot && this.snapshot.homeassistant
 | 
			
		||||
              ? html`
 | 
			
		||||
                  <ha-formfield
 | 
			
		||||
                    .label=${html`<supervisor-formfield-label
 | 
			
		||||
                      label="Home Assistant"
 | 
			
		||||
                      .iconPath=${mdiHomeAssistant}
 | 
			
		||||
                      .version=${this.snapshot.homeassistant}
 | 
			
		||||
                    >
 | 
			
		||||
                    </supervisor-formfield-label>`}
 | 
			
		||||
                  >
 | 
			
		||||
                    <ha-checkbox
 | 
			
		||||
                      .checked=${this.homeAssistant}
 | 
			
		||||
                      @click=${() => {
 | 
			
		||||
                        this.homeAssistant = !this.homeAssistant;
 | 
			
		||||
                      }}
 | 
			
		||||
                    >
 | 
			
		||||
                    </ha-checkbox>
 | 
			
		||||
                  </ha-formfield>
 | 
			
		||||
                `
 | 
			
		||||
              : ""}
 | 
			
		||||
            ${foldersSection?.templates.length
 | 
			
		||||
              ? html`
 | 
			
		||||
                  <ha-formfield
 | 
			
		||||
                    .label=${html`<supervisor-formfield-label
 | 
			
		||||
                      .label=${this._localize("folders")}
 | 
			
		||||
                      .iconPath=${mdiFolder}
 | 
			
		||||
                    >
 | 
			
		||||
                    </supervisor-formfield-label>`}
 | 
			
		||||
                  >
 | 
			
		||||
                    <ha-checkbox
 | 
			
		||||
                      @change=${this._toggleSection}
 | 
			
		||||
                      .checked=${foldersSection.checked}
 | 
			
		||||
                      .indeterminate=${foldersSection.indeterminate}
 | 
			
		||||
                      .section=${"folders"}
 | 
			
		||||
                    >
 | 
			
		||||
                    </ha-checkbox>
 | 
			
		||||
                  </ha-formfield>
 | 
			
		||||
                  <div class="section-content">${foldersSection.templates}</div>
 | 
			
		||||
                `
 | 
			
		||||
              : ""}
 | 
			
		||||
            ${addonsSection?.templates.length
 | 
			
		||||
              ? html`
 | 
			
		||||
                  <ha-formfield
 | 
			
		||||
                    .label=${html`<supervisor-formfield-label
 | 
			
		||||
                      .label=${this._localize("addons")}
 | 
			
		||||
                      .iconPath=${mdiPuzzle}
 | 
			
		||||
                    >
 | 
			
		||||
                    </supervisor-formfield-label>`}
 | 
			
		||||
                  >
 | 
			
		||||
                    <ha-checkbox
 | 
			
		||||
                      @change=${this._toggleSection}
 | 
			
		||||
                      .checked=${addonsSection.checked}
 | 
			
		||||
                      .indeterminate=${addonsSection.indeterminate}
 | 
			
		||||
                      .section=${"addons"}
 | 
			
		||||
                    >
 | 
			
		||||
                    </ha-checkbox>
 | 
			
		||||
                  </ha-formfield>
 | 
			
		||||
                  <div class="section-content">${addonsSection.templates}</div>
 | 
			
		||||
                `
 | 
			
		||||
              : ""}
 | 
			
		||||
          </div> `
 | 
			
		||||
        : ""}
 | 
			
		||||
      ${this.snapshotType === "partial" &&
 | 
			
		||||
      (!this.snapshot || this.snapshotHasPassword)
 | 
			
		||||
        ? html`<hr />`
 | 
			
		||||
        : ""}
 | 
			
		||||
      ${!this.snapshot
 | 
			
		||||
        ? html`<ha-formfield
 | 
			
		||||
            class="password"
 | 
			
		||||
            .label=${this._localize("password_protection")}
 | 
			
		||||
          >
 | 
			
		||||
            <ha-checkbox
 | 
			
		||||
              .checked=${this.snapshotHasPassword}
 | 
			
		||||
              @change=${this._toggleHasPassword}
 | 
			
		||||
            >
 | 
			
		||||
            </ha-checkbox>
 | 
			
		||||
          </ha-formfield>`
 | 
			
		||||
        : ""}
 | 
			
		||||
      ${this.snapshotHasPassword
 | 
			
		||||
        ? html`
 | 
			
		||||
            <paper-input
 | 
			
		||||
              .label=${this._localize("password")}
 | 
			
		||||
              type="password"
 | 
			
		||||
              name="snapshotPassword"
 | 
			
		||||
              .value=${this.snapshotPassword}
 | 
			
		||||
              @value-changed=${this._handleTextValueChanged}
 | 
			
		||||
            >
 | 
			
		||||
            </paper-input>
 | 
			
		||||
            ${!this.snapshot
 | 
			
		||||
              ? html` <paper-input
 | 
			
		||||
                  .label=${this.supervisor?.localize("confirm_password")}
 | 
			
		||||
                  type="password"
 | 
			
		||||
                  name="confirmSnapshotPassword"
 | 
			
		||||
                  .value=${this.confirmSnapshotPassword}
 | 
			
		||||
                  @value-changed=${this._handleTextValueChanged}
 | 
			
		||||
                >
 | 
			
		||||
                </paper-input>`
 | 
			
		||||
              : ""}
 | 
			
		||||
          `
 | 
			
		||||
        : ""}
 | 
			
		||||
    `;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static get styles(): CSSResultGroup {
 | 
			
		||||
    return css`
 | 
			
		||||
      .partial-picker ha-formfield {
 | 
			
		||||
        display: block;
 | 
			
		||||
      }
 | 
			
		||||
      .partial-picker ha-checkbox {
 | 
			
		||||
        --mdc-checkbox-touch-target-size: 32px;
 | 
			
		||||
      }
 | 
			
		||||
      .partial-picker {
 | 
			
		||||
        display: block;
 | 
			
		||||
        margin: 0px -6px;
 | 
			
		||||
      }
 | 
			
		||||
      supervisor-formfield-label {
 | 
			
		||||
        display: inline-flex;
 | 
			
		||||
        align-items: center;
 | 
			
		||||
      }
 | 
			
		||||
      hr {
 | 
			
		||||
        border-color: var(--divider-color);
 | 
			
		||||
        border-bottom: none;
 | 
			
		||||
        margin: 16px 0;
 | 
			
		||||
      }
 | 
			
		||||
      .details {
 | 
			
		||||
        color: var(--secondary-text-color);
 | 
			
		||||
      }
 | 
			
		||||
      .section-content {
 | 
			
		||||
        display: flex;
 | 
			
		||||
        flex-direction: column;
 | 
			
		||||
        margin-left: 30px;
 | 
			
		||||
      }
 | 
			
		||||
      ha-formfield.password {
 | 
			
		||||
        display: block;
 | 
			
		||||
        margin: 0 -14px -16px;
 | 
			
		||||
      }
 | 
			
		||||
      .snapshot-types {
 | 
			
		||||
        display: flex;
 | 
			
		||||
        margin-left: -13px;
 | 
			
		||||
      }
 | 
			
		||||
      .sub-header {
 | 
			
		||||
        margin-top: 8px;
 | 
			
		||||
      }
 | 
			
		||||
    `;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public snapshotDetails():
 | 
			
		||||
    | HassioPartialSnapshotCreateParams
 | 
			
		||||
    | HassioFullSnapshotCreateParams {
 | 
			
		||||
    const data: any = {};
 | 
			
		||||
 | 
			
		||||
    if (!this.snapshot) {
 | 
			
		||||
      data.name = this.snapshotName || formatDate(new Date(), this.hass.locale);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (this.snapshotHasPassword) {
 | 
			
		||||
      data.password = this.snapshotPassword;
 | 
			
		||||
      if (!this.snapshot) {
 | 
			
		||||
        data.confirm_password = this.confirmSnapshotPassword;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (this.snapshotType === "full") {
 | 
			
		||||
      return data;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const addons = this.addons
 | 
			
		||||
      ?.filter((addon) => addon.checked)
 | 
			
		||||
      .map((addon) => addon.slug);
 | 
			
		||||
    const folders = this.folders
 | 
			
		||||
      ?.filter((folder) => folder.checked)
 | 
			
		||||
      .map((folder) => folder.slug);
 | 
			
		||||
 | 
			
		||||
    if (addons?.length) {
 | 
			
		||||
      data.addons = addons;
 | 
			
		||||
    }
 | 
			
		||||
    if (folders?.length) {
 | 
			
		||||
      data.folders = folders;
 | 
			
		||||
    }
 | 
			
		||||
    if (this.homeAssistant) {
 | 
			
		||||
      data.homeassistant = this.homeAssistant;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return data;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private _getSection(section: string) {
 | 
			
		||||
    const templates: TemplateResult[] = [];
 | 
			
		||||
    const addons =
 | 
			
		||||
      section === "addons"
 | 
			
		||||
        ? new Map(
 | 
			
		||||
            this.supervisor?.addon.addons.map((item) => [item.slug, item])
 | 
			
		||||
          )
 | 
			
		||||
        : undefined;
 | 
			
		||||
    let checkedItems = 0;
 | 
			
		||||
    this[section].forEach((item) => {
 | 
			
		||||
      templates.push(html`<ha-formfield
 | 
			
		||||
        .label=${html`<supervisor-formfield-label
 | 
			
		||||
          .label=${item.name}
 | 
			
		||||
          .iconPath=${section === "addons" ? mdiPuzzle : mdiFolder}
 | 
			
		||||
          .imageUrl=${section === "addons" &&
 | 
			
		||||
          !this.onboarding &&
 | 
			
		||||
          atLeastVersion(this.hass.config.version, 0, 105) &&
 | 
			
		||||
          addons?.get(item.slug)?.icon
 | 
			
		||||
            ? `/api/hassio/addons/${item.slug}/icon`
 | 
			
		||||
            : undefined}
 | 
			
		||||
          .version=${item.version}
 | 
			
		||||
        >
 | 
			
		||||
        </supervisor-formfield-label>`}
 | 
			
		||||
      >
 | 
			
		||||
        <ha-checkbox
 | 
			
		||||
          .item=${item}
 | 
			
		||||
          .checked=${item.checked}
 | 
			
		||||
          .section=${section}
 | 
			
		||||
          @change=${this._updateSectionEntry}
 | 
			
		||||
        >
 | 
			
		||||
        </ha-checkbox>
 | 
			
		||||
      </ha-formfield>`);
 | 
			
		||||
 | 
			
		||||
      if (item.checked) {
 | 
			
		||||
        checkedItems++;
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    const checked = checkedItems === this[section].length;
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
      templates,
 | 
			
		||||
      checked,
 | 
			
		||||
      indeterminate: !checked && checkedItems !== 0,
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private _handleRadioValueChanged(ev: CustomEvent) {
 | 
			
		||||
    const input = ev.currentTarget as HaRadio;
 | 
			
		||||
    this[input.name] = input.value;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private _handleTextValueChanged(ev: PolymerChangedEvent<string>) {
 | 
			
		||||
    const input = ev.currentTarget as PaperInputElement;
 | 
			
		||||
    this[input.name!] = ev.detail.value;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private _toggleHasPassword(): void {
 | 
			
		||||
    this.snapshotHasPassword = !this.snapshotHasPassword;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private _toggleSection(ev): void {
 | 
			
		||||
    const section = ev.currentTarget.section;
 | 
			
		||||
 | 
			
		||||
    this[section] = (section === "addons" ? this.addons : this.folders)!.map(
 | 
			
		||||
      (item) => ({
 | 
			
		||||
        ...item,
 | 
			
		||||
        checked: ev.currentTarget.checked,
 | 
			
		||||
      })
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private _updateSectionEntry(ev): void {
 | 
			
		||||
    const item = ev.currentTarget.item;
 | 
			
		||||
    const section = ev.currentTarget.section;
 | 
			
		||||
    this[section] = this[section].map((entry) =>
 | 
			
		||||
      entry.slug === item.slug
 | 
			
		||||
        ? {
 | 
			
		||||
            ...entry,
 | 
			
		||||
            checked: ev.currentTarget.checked,
 | 
			
		||||
          }
 | 
			
		||||
        : entry
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
declare global {
 | 
			
		||||
  interface HTMLElementTagNameMap {
 | 
			
		||||
    "supervisor-snapshot-content": SupervisorSnapshotContent;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,6 +1,13 @@
 | 
			
		||||
import { mdiArrowUpBoldCircle, mdiPuzzle } from "@mdi/js";
 | 
			
		||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
 | 
			
		||||
import { customElement, property } from "lit/decorators";
 | 
			
		||||
import {
 | 
			
		||||
  css,
 | 
			
		||||
  CSSResult,
 | 
			
		||||
  customElement,
 | 
			
		||||
  html,
 | 
			
		||||
  LitElement,
 | 
			
		||||
  property,
 | 
			
		||||
  TemplateResult,
 | 
			
		||||
} from "lit-element";
 | 
			
		||||
import { atLeastVersion } from "../../../src/common/config/version";
 | 
			
		||||
import { navigate } from "../../../src/common/navigate";
 | 
			
		||||
import { compare } from "../../../src/common/string/compare";
 | 
			
		||||
@@ -83,7 +90,7 @@ class HassioAddons extends LitElement {
 | 
			
		||||
    `;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static get styles(): CSSResultGroup {
 | 
			
		||||
  static get styles(): CSSResult[] {
 | 
			
		||||
    return [
 | 
			
		||||
      haStyle,
 | 
			
		||||
      hassioStyle,
 | 
			
		||||
@@ -96,11 +103,11 @@ class HassioAddons extends LitElement {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private _addonTapped(ev: any): void {
 | 
			
		||||
    navigate(`/hassio/addon/${ev.currentTarget.addon.slug}/info`);
 | 
			
		||||
    navigate(this, `/hassio/addon/${ev.currentTarget.addon.slug}/info`);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private _openStore(): void {
 | 
			
		||||
    navigate("/hassio/store");
 | 
			
		||||
    navigate(this, "/hassio/store");
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,12 @@
 | 
			
		||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
 | 
			
		||||
import { customElement, property } from "lit/decorators";
 | 
			
		||||
import {
 | 
			
		||||
  css,
 | 
			
		||||
  CSSResult,
 | 
			
		||||
  customElement,
 | 
			
		||||
  html,
 | 
			
		||||
  LitElement,
 | 
			
		||||
  property,
 | 
			
		||||
  TemplateResult,
 | 
			
		||||
} from "lit-element";
 | 
			
		||||
import { Supervisor } from "../../../src/data/supervisor/supervisor";
 | 
			
		||||
import "../../../src/layouts/hass-tabs-subpage";
 | 
			
		||||
import { haStyle } from "../../../src/resources/styles";
 | 
			
		||||
@@ -46,7 +53,7 @@ class HassioDashboard extends LitElement {
 | 
			
		||||
    `;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static get styles(): CSSResultGroup {
 | 
			
		||||
  static get styles(): CSSResult[] {
 | 
			
		||||
    return [
 | 
			
		||||
      haStyle,
 | 
			
		||||
      css`
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,14 @@
 | 
			
		||||
import "@material/mwc-button";
 | 
			
		||||
import { mdiHomeAssistant } from "@mdi/js";
 | 
			
		||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
 | 
			
		||||
import { customElement, property } from "lit/decorators";
 | 
			
		||||
import {
 | 
			
		||||
  css,
 | 
			
		||||
  CSSResult,
 | 
			
		||||
  customElement,
 | 
			
		||||
  html,
 | 
			
		||||
  LitElement,
 | 
			
		||||
  property,
 | 
			
		||||
  TemplateResult,
 | 
			
		||||
} from "lit-element";
 | 
			
		||||
import memoizeOne from "memoize-one";
 | 
			
		||||
import { atLeastVersion } from "../../../src/common/config/version";
 | 
			
		||||
import { fireEvent } from "../../../src/common/dom/fire_event";
 | 
			
		||||
@@ -33,8 +40,9 @@ import { HomeAssistant } from "../../../src/types";
 | 
			
		||||
import { showDialogSupervisorUpdate } from "../dialogs/update/show-dialog-update";
 | 
			
		||||
import { hassioStyle } from "../resources/hassio-style";
 | 
			
		||||
 | 
			
		||||
const computeVersion = (key: string, version: string): string =>
 | 
			
		||||
  key === "os" ? version : `${key}-${version}`;
 | 
			
		||||
const computeVersion = (key: string, version: string): string => {
 | 
			
		||||
  return key === "os" ? version : `${key}-${version}`;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@customElement("hassio-update")
 | 
			
		||||
export class HassioUpdate extends LitElement {
 | 
			
		||||
@@ -42,12 +50,11 @@ export class HassioUpdate extends LitElement {
 | 
			
		||||
 | 
			
		||||
  @property({ attribute: false }) public supervisor!: Supervisor;
 | 
			
		||||
 | 
			
		||||
  private _pendingUpdates = memoizeOne(
 | 
			
		||||
    (supervisor: Supervisor): number =>
 | 
			
		||||
      Object.keys(supervisor).filter(
 | 
			
		||||
        (value) => supervisor[value].update_available
 | 
			
		||||
      ).length
 | 
			
		||||
  );
 | 
			
		||||
  private _pendingUpdates = memoizeOne((supervisor: Supervisor): number => {
 | 
			
		||||
    return Object.keys(supervisor).filter(
 | 
			
		||||
      (value) => supervisor[value].update_available
 | 
			
		||||
    ).length;
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  protected render(): TemplateResult {
 | 
			
		||||
    if (!this.supervisor) {
 | 
			
		||||
@@ -220,32 +227,13 @@ export class HassioUpdate extends LitElement {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private async _updateCore(): Promise<void> {
 | 
			
		||||
    try {
 | 
			
		||||
      await updateCore(this.hass);
 | 
			
		||||
    } catch (err) {
 | 
			
		||||
      if (this.hass.connection.connected && !ignoreSupervisorError(err)) {
 | 
			
		||||
        showAlertDialog(this, {
 | 
			
		||||
          title: this.supervisor.localize(
 | 
			
		||||
            "common.failed_to_update_name",
 | 
			
		||||
            "name",
 | 
			
		||||
            "Home Assistant Core"
 | 
			
		||||
          ),
 | 
			
		||||
          text: extractApiErrorMessage(err),
 | 
			
		||||
        });
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    await updateCore(this.hass);
 | 
			
		||||
    fireEvent(this, "supervisor-collection-refresh", {
 | 
			
		||||
      collection: "core",
 | 
			
		||||
    });
 | 
			
		||||
    fireEvent(this, "supervisor-applying-update", {
 | 
			
		||||
      name: "Home Assistant Core",
 | 
			
		||||
      version: this.supervisor.core.version_latest,
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static get styles(): CSSResultGroup {
 | 
			
		||||
  static get styles(): CSSResult[] {
 | 
			
		||||
    return [
 | 
			
		||||
      haStyle,
 | 
			
		||||
      hassioStyle,
 | 
			
		||||
 
 | 
			
		||||
@@ -1,194 +0,0 @@
 | 
			
		||||
import { mdiClose } from "@mdi/js";
 | 
			
		||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
 | 
			
		||||
import { customElement, property, state } from "lit/decorators";
 | 
			
		||||
import memoizeOne from "memoize-one";
 | 
			
		||||
import { fireEvent } from "../../../../src/common/dom/fire_event";
 | 
			
		||||
import "../../../../src/common/search/search-input";
 | 
			
		||||
import { compare } from "../../../../src/common/string/compare";
 | 
			
		||||
import "../../../../src/components/ha-dialog";
 | 
			
		||||
import "../../../../src/components/ha-expansion-panel";
 | 
			
		||||
import { HassioHardwareInfo } from "../../../../src/data/hassio/hardware";
 | 
			
		||||
import { dump } from "../../../../src/resources/js-yaml-dump";
 | 
			
		||||
import { haStyle, haStyleDialog } from "../../../../src/resources/styles";
 | 
			
		||||
import { HomeAssistant } from "../../../../src/types";
 | 
			
		||||
import { HassioHardwareDialogParams } from "./show-dialog-hassio-hardware";
 | 
			
		||||
 | 
			
		||||
const _filterDevices = memoizeOne(
 | 
			
		||||
  (showAdvanced: boolean, hardware: HassioHardwareInfo, filter: string) =>
 | 
			
		||||
    hardware.devices
 | 
			
		||||
      .filter(
 | 
			
		||||
        (device) =>
 | 
			
		||||
          (showAdvanced ||
 | 
			
		||||
            ["tty", "gpio", "input"].includes(device.subsystem)) &&
 | 
			
		||||
          (device.by_id?.toLowerCase().includes(filter) ||
 | 
			
		||||
            device.name.toLowerCase().includes(filter) ||
 | 
			
		||||
            device.dev_path.toLocaleLowerCase().includes(filter) ||
 | 
			
		||||
            JSON.stringify(device.attributes)
 | 
			
		||||
              .toLocaleLowerCase()
 | 
			
		||||
              .includes(filter))
 | 
			
		||||
      )
 | 
			
		||||
      .sort((a, b) => compare(a.name, b.name))
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
@customElement("dialog-hassio-hardware")
 | 
			
		||||
class HassioHardwareDialog extends LitElement {
 | 
			
		||||
  @property({ attribute: false }) public hass!: HomeAssistant;
 | 
			
		||||
 | 
			
		||||
  @state() private _dialogParams?: HassioHardwareDialogParams;
 | 
			
		||||
 | 
			
		||||
  @state() private _filter?: string;
 | 
			
		||||
 | 
			
		||||
  public showDialog(params: HassioHardwareDialogParams) {
 | 
			
		||||
    this._dialogParams = params;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public closeDialog() {
 | 
			
		||||
    this._dialogParams = undefined;
 | 
			
		||||
    fireEvent(this, "dialog-closed", { dialog: this.localName });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  protected render(): TemplateResult {
 | 
			
		||||
    if (!this._dialogParams) {
 | 
			
		||||
      return html``;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const devices = _filterDevices(
 | 
			
		||||
      this.hass.userData?.showAdvanced || false,
 | 
			
		||||
      this._dialogParams.hardware,
 | 
			
		||||
      (this._filter || "").toLowerCase()
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    return html`
 | 
			
		||||
      <ha-dialog
 | 
			
		||||
        open
 | 
			
		||||
        scrimClickAction
 | 
			
		||||
        hideActions
 | 
			
		||||
        @closed=${this.closeDialog}
 | 
			
		||||
        .heading=${true}
 | 
			
		||||
      >
 | 
			
		||||
        <div class="header" slot="heading">
 | 
			
		||||
          <h2>
 | 
			
		||||
            ${this._dialogParams.supervisor.localize("dialog.hardware.title")}
 | 
			
		||||
          </h2>
 | 
			
		||||
          <mwc-icon-button dialogAction="close">
 | 
			
		||||
            <ha-svg-icon .path=${mdiClose}></ha-svg-icon>
 | 
			
		||||
          </mwc-icon-button>
 | 
			
		||||
          <search-input
 | 
			
		||||
            autofocus
 | 
			
		||||
            no-label-float
 | 
			
		||||
            .filter=${this._filter}
 | 
			
		||||
            @value-changed=${this._handleSearchChange}
 | 
			
		||||
            .label=${this._dialogParams.supervisor.localize(
 | 
			
		||||
              "dialog.hardware.search"
 | 
			
		||||
            )}
 | 
			
		||||
          >
 | 
			
		||||
          </search-input>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        ${devices.map(
 | 
			
		||||
          (device) =>
 | 
			
		||||
            html`<ha-expansion-panel
 | 
			
		||||
              .header=${device.name}
 | 
			
		||||
              .secondary=${device.by_id || undefined}
 | 
			
		||||
              outlined
 | 
			
		||||
            >
 | 
			
		||||
              <div class="device-property">
 | 
			
		||||
                <span>
 | 
			
		||||
                  ${this._dialogParams!.supervisor.localize(
 | 
			
		||||
                    "dialog.hardware.subsystem"
 | 
			
		||||
                  )}:
 | 
			
		||||
                </span>
 | 
			
		||||
                <span>${device.subsystem}</span>
 | 
			
		||||
              </div>
 | 
			
		||||
              <div class="device-property">
 | 
			
		||||
                <span>
 | 
			
		||||
                  ${this._dialogParams!.supervisor.localize(
 | 
			
		||||
                    "dialog.hardware.device_path"
 | 
			
		||||
                  )}:
 | 
			
		||||
                </span>
 | 
			
		||||
                <code>${device.dev_path}</code>
 | 
			
		||||
              </div>
 | 
			
		||||
              ${device.by_id
 | 
			
		||||
                ? html` <div class="device-property">
 | 
			
		||||
                    <span>
 | 
			
		||||
                      ${this._dialogParams!.supervisor.localize(
 | 
			
		||||
                        "dialog.hardware.id"
 | 
			
		||||
                      )}:
 | 
			
		||||
                    </span>
 | 
			
		||||
                    <code>${device.by_id}</code>
 | 
			
		||||
                  </div>`
 | 
			
		||||
                : ""}
 | 
			
		||||
              <div class="attributes">
 | 
			
		||||
                <span>
 | 
			
		||||
                  ${this._dialogParams!.supervisor.localize(
 | 
			
		||||
                    "dialog.hardware.attributes"
 | 
			
		||||
                  )}:
 | 
			
		||||
                </span>
 | 
			
		||||
                <pre>${dump(device.attributes, { indent: 2 })}</pre>
 | 
			
		||||
              </div>
 | 
			
		||||
            </ha-expansion-panel>`
 | 
			
		||||
        )}
 | 
			
		||||
      </ha-dialog>
 | 
			
		||||
    `;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private _handleSearchChange(ev: CustomEvent) {
 | 
			
		||||
    this._filter = ev.detail.value;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static get styles(): CSSResultGroup {
 | 
			
		||||
    return [
 | 
			
		||||
      haStyle,
 | 
			
		||||
      haStyleDialog,
 | 
			
		||||
      css`
 | 
			
		||||
        mwc-icon-button {
 | 
			
		||||
          position: absolute;
 | 
			
		||||
          right: 16px;
 | 
			
		||||
          top: 10px;
 | 
			
		||||
          text-decoration: none;
 | 
			
		||||
          color: var(--primary-text-color);
 | 
			
		||||
        }
 | 
			
		||||
        h2 {
 | 
			
		||||
          margin: 18px 42px 0 18px;
 | 
			
		||||
          color: var(--primary-text-color);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ha-expansion-panel {
 | 
			
		||||
          margin: 4px 0;
 | 
			
		||||
        }
 | 
			
		||||
        pre,
 | 
			
		||||
        code {
 | 
			
		||||
          background-color: var(--markdown-code-background-color, none);
 | 
			
		||||
          border-radius: 3px;
 | 
			
		||||
        }
 | 
			
		||||
        pre {
 | 
			
		||||
          padding: 16px;
 | 
			
		||||
          overflow: auto;
 | 
			
		||||
          line-height: 1.45;
 | 
			
		||||
          font-family: var(--code-font-family, monospace);
 | 
			
		||||
        }
 | 
			
		||||
        code {
 | 
			
		||||
          font-size: 85%;
 | 
			
		||||
          padding: 0.2em 0.4em;
 | 
			
		||||
        }
 | 
			
		||||
        search-input {
 | 
			
		||||
          margin: 0 16px;
 | 
			
		||||
          display: block;
 | 
			
		||||
        }
 | 
			
		||||
        .device-property {
 | 
			
		||||
          display: flex;
 | 
			
		||||
          justify-content: space-between;
 | 
			
		||||
        }
 | 
			
		||||
        .attributes {
 | 
			
		||||
          margin-top: 12px;
 | 
			
		||||
        }
 | 
			
		||||
      `,
 | 
			
		||||
    ];
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
declare global {
 | 
			
		||||
  interface HTMLElementTagNameMap {
 | 
			
		||||
    "dialog-hassio-hardware": HassioHardwareDialog;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,19 +0,0 @@
 | 
			
		||||
import { fireEvent } from "../../../../src/common/dom/fire_event";
 | 
			
		||||
import { HassioHardwareInfo } from "../../../../src/data/hassio/hardware";
 | 
			
		||||
import { Supervisor } from "../../../../src/data/supervisor/supervisor";
 | 
			
		||||
 | 
			
		||||
export interface HassioHardwareDialogParams {
 | 
			
		||||
  supervisor: Supervisor;
 | 
			
		||||
  hardware: HassioHardwareInfo;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const showHassioHardwareDialog = (
 | 
			
		||||
  element: HTMLElement,
 | 
			
		||||
  dialogParams: HassioHardwareDialogParams
 | 
			
		||||
): void => {
 | 
			
		||||
  fireEvent(element, "show-dialog", {
 | 
			
		||||
    dialogTag: "dialog-hassio-hardware",
 | 
			
		||||
    dialogImport: () => import("./dialog-hassio-hardware"),
 | 
			
		||||
    dialogParams,
 | 
			
		||||
  });
 | 
			
		||||
};
 | 
			
		||||
@@ -1,5 +1,13 @@
 | 
			
		||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
 | 
			
		||||
import { customElement, property, state } from "lit/decorators";
 | 
			
		||||
import {
 | 
			
		||||
  css,
 | 
			
		||||
  CSSResult,
 | 
			
		||||
  customElement,
 | 
			
		||||
  html,
 | 
			
		||||
  internalProperty,
 | 
			
		||||
  LitElement,
 | 
			
		||||
  property,
 | 
			
		||||
  TemplateResult,
 | 
			
		||||
} from "lit-element";
 | 
			
		||||
import { createCloseHeading } from "../../../../src/components/ha-dialog";
 | 
			
		||||
import "../../../../src/components/ha-markdown";
 | 
			
		||||
import { haStyleDialog } from "../../../../src/resources/styles";
 | 
			
		||||
@@ -15,7 +23,7 @@ class HassioMarkdownDialog extends LitElement {
 | 
			
		||||
 | 
			
		||||
  @property() public content!: string;
 | 
			
		||||
 | 
			
		||||
  @state() private _opened = false;
 | 
			
		||||
  @internalProperty() private _opened = false;
 | 
			
		||||
 | 
			
		||||
  public showDialog(params: HassioMarkdownDialogParams) {
 | 
			
		||||
    this.title = params.title;
 | 
			
		||||
@@ -42,7 +50,7 @@ class HassioMarkdownDialog extends LitElement {
 | 
			
		||||
    `;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static get styles(): CSSResultGroup {
 | 
			
		||||
  static get styles(): CSSResult[] {
 | 
			
		||||
    return [
 | 
			
		||||
      haStyleDialog,
 | 
			
		||||
      hassioStyle,
 | 
			
		||||
 
 | 
			
		||||
@@ -6,9 +6,17 @@ import "@material/mwc-tab";
 | 
			
		||||
import "@material/mwc-tab-bar";
 | 
			
		||||
import { mdiClose } from "@mdi/js";
 | 
			
		||||
import { PaperInputElement } from "@polymer/paper-input/paper-input";
 | 
			
		||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
 | 
			
		||||
import { customElement, property, state } from "lit/decorators";
 | 
			
		||||
import { cache } from "lit/directives/cache";
 | 
			
		||||
import {
 | 
			
		||||
  css,
 | 
			
		||||
  CSSResult,
 | 
			
		||||
  customElement,
 | 
			
		||||
  html,
 | 
			
		||||
  internalProperty,
 | 
			
		||||
  LitElement,
 | 
			
		||||
  property,
 | 
			
		||||
  TemplateResult,
 | 
			
		||||
} from "lit-element";
 | 
			
		||||
import { cache } from "lit-html/directives/cache";
 | 
			
		||||
import { fireEvent } from "../../../../src/common/dom/fire_event";
 | 
			
		||||
import "../../../../src/components/ha-circular-progress";
 | 
			
		||||
import "../../../../src/components/ha-dialog";
 | 
			
		||||
@@ -39,39 +47,38 @@ import { HassioNetworkDialogParams } from "./show-dialog-network";
 | 
			
		||||
const IP_VERSIONS = ["ipv4", "ipv6"];
 | 
			
		||||
 | 
			
		||||
@customElement("dialog-hassio-network")
 | 
			
		||||
export class DialogHassioNetwork
 | 
			
		||||
  extends LitElement
 | 
			
		||||
export class DialogHassioNetwork extends LitElement
 | 
			
		||||
  implements HassDialog<HassioNetworkDialogParams> {
 | 
			
		||||
  @property({ attribute: false }) public hass!: HomeAssistant;
 | 
			
		||||
 | 
			
		||||
  @property({ attribute: false }) public supervisor!: Supervisor;
 | 
			
		||||
 | 
			
		||||
  @state() private _accessPoints?: AccessPoints;
 | 
			
		||||
  @internalProperty() private _accessPoints?: AccessPoints;
 | 
			
		||||
 | 
			
		||||
  @state() private _curTabIndex = 0;
 | 
			
		||||
  @internalProperty() private _curTabIndex = 0;
 | 
			
		||||
 | 
			
		||||
  @state() private _dirty = false;
 | 
			
		||||
  @internalProperty() private _dirty = false;
 | 
			
		||||
 | 
			
		||||
  @state() private _interface?: NetworkInterface;
 | 
			
		||||
  @internalProperty() private _interface?: NetworkInterface;
 | 
			
		||||
 | 
			
		||||
  @state() private _interfaces!: NetworkInterface[];
 | 
			
		||||
  @internalProperty() private _interfaces!: NetworkInterface[];
 | 
			
		||||
 | 
			
		||||
  @state() private _params?: HassioNetworkDialogParams;
 | 
			
		||||
  @internalProperty() private _params?: HassioNetworkDialogParams;
 | 
			
		||||
 | 
			
		||||
  @state() private _processing = false;
 | 
			
		||||
  @internalProperty() private _processing = false;
 | 
			
		||||
 | 
			
		||||
  @state() private _scanning = false;
 | 
			
		||||
  @internalProperty() private _scanning = false;
 | 
			
		||||
 | 
			
		||||
  @state() private _wifiConfiguration?: WifiConfiguration;
 | 
			
		||||
  @internalProperty() private _wifiConfiguration?: WifiConfiguration;
 | 
			
		||||
 | 
			
		||||
  public async showDialog(params: HassioNetworkDialogParams): Promise<void> {
 | 
			
		||||
    this._params = params;
 | 
			
		||||
    this._dirty = false;
 | 
			
		||||
    this._curTabIndex = 0;
 | 
			
		||||
    this.supervisor = params.supervisor;
 | 
			
		||||
    this._interfaces = params.supervisor.network.interfaces.sort((a, b) =>
 | 
			
		||||
      a.primary > b.primary ? -1 : 1
 | 
			
		||||
    );
 | 
			
		||||
    this._interfaces = params.supervisor.network.interfaces.sort((a, b) => {
 | 
			
		||||
      return a.primary > b.primary ? -1 : 1;
 | 
			
		||||
    });
 | 
			
		||||
    this._interface = { ...this._interfaces[this._curTabIndex] };
 | 
			
		||||
 | 
			
		||||
    await this.updateComplete;
 | 
			
		||||
@@ -535,7 +542,7 @@ export class DialogHassioNetwork
 | 
			
		||||
    this._wifiConfiguration![id] = value;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static get styles(): CSSResultGroup {
 | 
			
		||||
  static get styles(): CSSResult[] {
 | 
			
		||||
    return [
 | 
			
		||||
      haStyleDialog,
 | 
			
		||||
      css`
 | 
			
		||||
 
 | 
			
		||||
@@ -3,8 +3,16 @@ import "@material/mwc-icon-button/mwc-icon-button";
 | 
			
		||||
import "@material/mwc-list/mwc-list-item";
 | 
			
		||||
import { mdiDelete } from "@mdi/js";
 | 
			
		||||
import { PaperInputElement } from "@polymer/paper-input/paper-input";
 | 
			
		||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
 | 
			
		||||
import { customElement, property, state } from "lit/decorators";
 | 
			
		||||
import {
 | 
			
		||||
  css,
 | 
			
		||||
  CSSResult,
 | 
			
		||||
  customElement,
 | 
			
		||||
  html,
 | 
			
		||||
  internalProperty,
 | 
			
		||||
  LitElement,
 | 
			
		||||
  property,
 | 
			
		||||
  TemplateResult,
 | 
			
		||||
} from "lit-element";
 | 
			
		||||
import "../../../../src/components/ha-circular-progress";
 | 
			
		||||
import { createCloseHeading } from "../../../../src/components/ha-dialog";
 | 
			
		||||
import "../../../../src/components/ha-svg-icon";
 | 
			
		||||
@@ -31,21 +39,21 @@ class HassioRegistriesDialog extends LitElement {
 | 
			
		||||
    username: string;
 | 
			
		||||
  }[];
 | 
			
		||||
 | 
			
		||||
  @state() private _registry?: string;
 | 
			
		||||
  @internalProperty() private _registry?: string;
 | 
			
		||||
 | 
			
		||||
  @state() private _username?: string;
 | 
			
		||||
  @internalProperty() private _username?: string;
 | 
			
		||||
 | 
			
		||||
  @state() private _password?: string;
 | 
			
		||||
  @internalProperty() private _password?: string;
 | 
			
		||||
 | 
			
		||||
  @state() private _opened = false;
 | 
			
		||||
  @internalProperty() private _opened = false;
 | 
			
		||||
 | 
			
		||||
  @state() private _addingRegistry = false;
 | 
			
		||||
  @internalProperty() private _addingRegistry = false;
 | 
			
		||||
 | 
			
		||||
  protected render(): TemplateResult {
 | 
			
		||||
    return html`
 | 
			
		||||
      <ha-dialog
 | 
			
		||||
        .open=${this._opened}
 | 
			
		||||
        @closed=${this.closeDialog}
 | 
			
		||||
        @closing=${this.closeDialog}
 | 
			
		||||
        scrimClickAction
 | 
			
		||||
        escapeKeyAction
 | 
			
		||||
        .heading=${createCloseHeading(
 | 
			
		||||
@@ -100,8 +108,8 @@ class HassioRegistriesDialog extends LitElement {
 | 
			
		||||
                </mwc-button>
 | 
			
		||||
              `
 | 
			
		||||
            : html`${this._registries?.length
 | 
			
		||||
                  ? this._registries.map(
 | 
			
		||||
                      (entry) => html`
 | 
			
		||||
                  ? this._registries.map((entry) => {
 | 
			
		||||
                      return html`
 | 
			
		||||
                        <mwc-list-item class="option" hasMeta twoline>
 | 
			
		||||
                          <span>${entry.registry}</span>
 | 
			
		||||
                          <span slot="secondary"
 | 
			
		||||
@@ -121,8 +129,8 @@ class HassioRegistriesDialog extends LitElement {
 | 
			
		||||
                            <ha-svg-icon .path=${mdiDelete}></ha-svg-icon>
 | 
			
		||||
                          </mwc-icon-button>
 | 
			
		||||
                        </mwc-list-item>
 | 
			
		||||
                      `
 | 
			
		||||
                    )
 | 
			
		||||
                      `;
 | 
			
		||||
                    })
 | 
			
		||||
                  : html`
 | 
			
		||||
                      <mwc-list-item>
 | 
			
		||||
                        <span
 | 
			
		||||
@@ -212,7 +220,7 @@ class HassioRegistriesDialog extends LitElement {
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static get styles(): CSSResultGroup {
 | 
			
		||||
  static get styles(): CSSResult[] {
 | 
			
		||||
    return [
 | 
			
		||||
      haStyle,
 | 
			
		||||
      haStyleDialog,
 | 
			
		||||
@@ -244,6 +252,9 @@ class HassioRegistriesDialog extends LitElement {
 | 
			
		||||
        mwc-list-item span[slot="secondary"] {
 | 
			
		||||
          color: var(--secondary-text-color);
 | 
			
		||||
        }
 | 
			
		||||
        ha-paper-dropdown-menu {
 | 
			
		||||
          display: block;
 | 
			
		||||
        }
 | 
			
		||||
      `,
 | 
			
		||||
    ];
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -5,8 +5,17 @@ import "@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-body";
 | 
			
		||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
 | 
			
		||||
import { customElement, property, query, state } from "lit/decorators";
 | 
			
		||||
import {
 | 
			
		||||
  css,
 | 
			
		||||
  CSSResult,
 | 
			
		||||
  customElement,
 | 
			
		||||
  html,
 | 
			
		||||
  internalProperty,
 | 
			
		||||
  LitElement,
 | 
			
		||||
  property,
 | 
			
		||||
  query,
 | 
			
		||||
  TemplateResult,
 | 
			
		||||
} from "lit-element";
 | 
			
		||||
import memoizeOne from "memoize-one";
 | 
			
		||||
import { fireEvent } from "../../../../src/common/dom/fire_event";
 | 
			
		||||
import "../../../../src/components/ha-circular-progress";
 | 
			
		||||
@@ -28,15 +37,15 @@ class HassioRepositoriesDialog extends LitElement {
 | 
			
		||||
 | 
			
		||||
  @query("#repository_input", true) private _optionInput?: PaperInputElement;
 | 
			
		||||
 | 
			
		||||
  @state() private _repositories?: HassioAddonRepository[];
 | 
			
		||||
  @internalProperty() private _repositories?: HassioAddonRepository[];
 | 
			
		||||
 | 
			
		||||
  @state() private _dialogParams?: HassioRepositoryDialogParams;
 | 
			
		||||
  @internalProperty() private _dialogParams?: HassioRepositoryDialogParams;
 | 
			
		||||
 | 
			
		||||
  @state() private _opened = false;
 | 
			
		||||
  @internalProperty() private _opened = false;
 | 
			
		||||
 | 
			
		||||
  @state() private _processing = false;
 | 
			
		||||
  @internalProperty() private _prosessing = false;
 | 
			
		||||
 | 
			
		||||
  @state() private _error?: string;
 | 
			
		||||
  @internalProperty() private _error?: string;
 | 
			
		||||
 | 
			
		||||
  public async showDialog(
 | 
			
		||||
    dialogParams: HassioRepositoryDialogParams
 | 
			
		||||
@@ -67,7 +76,7 @@ class HassioRepositoriesDialog extends LitElement {
 | 
			
		||||
    return html`
 | 
			
		||||
      <ha-dialog
 | 
			
		||||
        .open=${this._opened}
 | 
			
		||||
        @closed=${this.closeDialog}
 | 
			
		||||
        @closing=${this.closeDialog}
 | 
			
		||||
        scrimClickAction
 | 
			
		||||
        escapeKeyAction
 | 
			
		||||
        .heading=${createCloseHeading(
 | 
			
		||||
@@ -78,8 +87,8 @@ class HassioRepositoriesDialog extends LitElement {
 | 
			
		||||
        ${this._error ? html`<div class="error">${this._error}</div>` : ""}
 | 
			
		||||
        <div class="form">
 | 
			
		||||
          ${repositories.length
 | 
			
		||||
            ? repositories.map(
 | 
			
		||||
                (repo) => html`
 | 
			
		||||
            ? repositories.map((repo) => {
 | 
			
		||||
                return html`
 | 
			
		||||
                  <paper-item class="option">
 | 
			
		||||
                    <paper-item-body three-line>
 | 
			
		||||
                      <div>${repo.name}</div>
 | 
			
		||||
@@ -96,9 +105,13 @@ class HassioRepositoriesDialog extends LitElement {
 | 
			
		||||
                      <ha-svg-icon .path=${mdiDelete}></ha-svg-icon>
 | 
			
		||||
                    </mwc-icon-button>
 | 
			
		||||
                  </paper-item>
 | 
			
		||||
                `
 | 
			
		||||
              )
 | 
			
		||||
            : html` <paper-item> No repositories </paper-item> `}
 | 
			
		||||
                `;
 | 
			
		||||
              })
 | 
			
		||||
            : html`
 | 
			
		||||
                <paper-item>
 | 
			
		||||
                  No repositories
 | 
			
		||||
                </paper-item>
 | 
			
		||||
              `}
 | 
			
		||||
          <div class="layout horizontal bottom">
 | 
			
		||||
            <paper-input
 | 
			
		||||
              class="flex-auto"
 | 
			
		||||
@@ -110,11 +123,8 @@ class HassioRepositoriesDialog extends LitElement {
 | 
			
		||||
              @keydown=${this._handleKeyAdd}
 | 
			
		||||
            ></paper-input>
 | 
			
		||||
            <mwc-button @click=${this._addRepository}>
 | 
			
		||||
              ${this._processing
 | 
			
		||||
                ? html`<ha-circular-progress
 | 
			
		||||
                    active
 | 
			
		||||
                    size="small"
 | 
			
		||||
                  ></ha-circular-progress>`
 | 
			
		||||
              ${this._prosessing
 | 
			
		||||
                ? html`<ha-circular-progress active></ha-circular-progress>`
 | 
			
		||||
                : this._dialogParams!.supervisor.localize(
 | 
			
		||||
                    "dialog.repositories.add"
 | 
			
		||||
                  )}
 | 
			
		||||
@@ -128,7 +138,7 @@ class HassioRepositoriesDialog extends LitElement {
 | 
			
		||||
    `;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static get styles(): CSSResultGroup {
 | 
			
		||||
  static get styles(): CSSResult[] {
 | 
			
		||||
    return [
 | 
			
		||||
      haStyle,
 | 
			
		||||
      haStyleDialog,
 | 
			
		||||
@@ -150,6 +160,9 @@ class HassioRepositoriesDialog extends LitElement {
 | 
			
		||||
        mwc-button {
 | 
			
		||||
          margin-left: 8px;
 | 
			
		||||
        }
 | 
			
		||||
        ha-paper-dropdown-menu {
 | 
			
		||||
          display: block;
 | 
			
		||||
        }
 | 
			
		||||
        ha-circular-progress {
 | 
			
		||||
          display: block;
 | 
			
		||||
          margin: 32px;
 | 
			
		||||
@@ -192,9 +205,11 @@ class HassioRepositoriesDialog extends LitElement {
 | 
			
		||||
    if (!input || !input.value) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    this._processing = true;
 | 
			
		||||
    this._prosessing = true;
 | 
			
		||||
    const repositories = this._filteredRepositories(this._repositories!);
 | 
			
		||||
    const newRepositories = repositories.map((repo) => repo.source);
 | 
			
		||||
    const newRepositories = repositories.map((repo) => {
 | 
			
		||||
      return repo.source;
 | 
			
		||||
    });
 | 
			
		||||
    newRepositories.push(input.value);
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
@@ -207,19 +222,25 @@ class HassioRepositoriesDialog extends LitElement {
 | 
			
		||||
    } catch (err) {
 | 
			
		||||
      this._error = extractApiErrorMessage(err);
 | 
			
		||||
    }
 | 
			
		||||
    this._processing = false;
 | 
			
		||||
    this._prosessing = false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private async _removeRepository(ev: Event) {
 | 
			
		||||
    const slug = (ev.currentTarget as any).slug;
 | 
			
		||||
    const repositories = this._filteredRepositories(this._repositories!);
 | 
			
		||||
    const repository = repositories.find((repo) => repo.slug === slug);
 | 
			
		||||
    const repository = repositories.find((repo) => {
 | 
			
		||||
      return repo.slug === slug;
 | 
			
		||||
    });
 | 
			
		||||
    if (!repository) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    const newRepositories = repositories
 | 
			
		||||
      .map((repo) => repo.source)
 | 
			
		||||
      .filter((repo) => repo !== repository.source);
 | 
			
		||||
      .map((repo) => {
 | 
			
		||||
        return repo.source;
 | 
			
		||||
      })
 | 
			
		||||
      .filter((repo) => {
 | 
			
		||||
        return repo !== repository.source;
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
      await setSupervisorOption(this.hass, {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,151 +0,0 @@
 | 
			
		||||
import "@material/mwc-button";
 | 
			
		||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
 | 
			
		||||
import { customElement, property, query, state } from "lit/decorators";
 | 
			
		||||
import { fireEvent } from "../../../../src/common/dom/fire_event";
 | 
			
		||||
import "../../../../src/components/buttons/ha-progress-button";
 | 
			
		||||
import { createCloseHeading } from "../../../../src/components/ha-dialog";
 | 
			
		||||
import { extractApiErrorMessage } from "../../../../src/data/hassio/common";
 | 
			
		||||
import {
 | 
			
		||||
  createHassioFullSnapshot,
 | 
			
		||||
  createHassioPartialSnapshot,
 | 
			
		||||
} from "../../../../src/data/hassio/snapshot";
 | 
			
		||||
import { showAlertDialog } from "../../../../src/dialogs/generic/show-dialog-box";
 | 
			
		||||
import { haStyle, haStyleDialog } from "../../../../src/resources/styles";
 | 
			
		||||
import { HomeAssistant } from "../../../../src/types";
 | 
			
		||||
import "../../components/supervisor-snapshot-content";
 | 
			
		||||
import type { SupervisorSnapshotContent } from "../../components/supervisor-snapshot-content";
 | 
			
		||||
import { HassioCreateSnapshotDialogParams } from "./show-dialog-hassio-create-snapshot";
 | 
			
		||||
 | 
			
		||||
@customElement("dialog-hassio-create-snapshot")
 | 
			
		||||
class HassioCreateSnapshotDialog extends LitElement {
 | 
			
		||||
  @property({ attribute: false }) public hass!: HomeAssistant;
 | 
			
		||||
 | 
			
		||||
  @state() private _dialogParams?: HassioCreateSnapshotDialogParams;
 | 
			
		||||
 | 
			
		||||
  @state() private _error?: string;
 | 
			
		||||
 | 
			
		||||
  @state() private _creatingSnapshot = false;
 | 
			
		||||
 | 
			
		||||
  @query("supervisor-snapshot-content")
 | 
			
		||||
  private _snapshotContent!: SupervisorSnapshotContent;
 | 
			
		||||
 | 
			
		||||
  public showDialog(params: HassioCreateSnapshotDialogParams) {
 | 
			
		||||
    this._dialogParams = params;
 | 
			
		||||
    this._creatingSnapshot = false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public closeDialog() {
 | 
			
		||||
    this._dialogParams = undefined;
 | 
			
		||||
    this._creatingSnapshot = false;
 | 
			
		||||
    this._error = undefined;
 | 
			
		||||
    fireEvent(this, "dialog-closed", { dialog: this.localName });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  protected render(): TemplateResult {
 | 
			
		||||
    if (!this._dialogParams) {
 | 
			
		||||
      return html``;
 | 
			
		||||
    }
 | 
			
		||||
    return html`
 | 
			
		||||
      <ha-dialog
 | 
			
		||||
        open
 | 
			
		||||
        scrimClickAction
 | 
			
		||||
        @closed=${this.closeDialog}
 | 
			
		||||
        .heading=${createCloseHeading(
 | 
			
		||||
          this.hass,
 | 
			
		||||
          this._dialogParams.supervisor.localize("snapshot.create_snapshot")
 | 
			
		||||
        )}
 | 
			
		||||
      >
 | 
			
		||||
        ${this._creatingSnapshot
 | 
			
		||||
          ? html` <ha-circular-progress active></ha-circular-progress>`
 | 
			
		||||
          : html`<supervisor-snapshot-content
 | 
			
		||||
              .hass=${this.hass}
 | 
			
		||||
              .supervisor=${this._dialogParams.supervisor}
 | 
			
		||||
            >
 | 
			
		||||
            </supervisor-snapshot-content>`}
 | 
			
		||||
        ${this._error ? html`<p class="error">Error: ${this._error}</p>` : ""}
 | 
			
		||||
        <mwc-button slot="secondaryAction" @click=${this.closeDialog}>
 | 
			
		||||
          ${this._dialogParams.supervisor.localize("common.close")}
 | 
			
		||||
        </mwc-button>
 | 
			
		||||
        <mwc-button
 | 
			
		||||
          .disabled=${this._creatingSnapshot}
 | 
			
		||||
          slot="primaryAction"
 | 
			
		||||
          @click=${this._createSnapshot}
 | 
			
		||||
        >
 | 
			
		||||
          ${this._dialogParams.supervisor.localize("snapshot.create")}
 | 
			
		||||
        </mwc-button>
 | 
			
		||||
      </ha-dialog>
 | 
			
		||||
    `;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private async _createSnapshot(): Promise<void> {
 | 
			
		||||
    if (this._dialogParams!.supervisor.info.state !== "running") {
 | 
			
		||||
      showAlertDialog(this, {
 | 
			
		||||
        title: this._dialogParams!.supervisor.localize(
 | 
			
		||||
          "snapshot.could_not_create"
 | 
			
		||||
        ),
 | 
			
		||||
        text: this._dialogParams!.supervisor.localize(
 | 
			
		||||
          "snapshot.create_blocked_not_running",
 | 
			
		||||
          "state",
 | 
			
		||||
          this._dialogParams!.supervisor.info.state
 | 
			
		||||
        ),
 | 
			
		||||
      });
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    const snapshotDetails = this._snapshotContent.snapshotDetails();
 | 
			
		||||
    this._creatingSnapshot = true;
 | 
			
		||||
 | 
			
		||||
    this._error = "";
 | 
			
		||||
    if (snapshotDetails.password && !snapshotDetails.password.length) {
 | 
			
		||||
      this._error = this._dialogParams!.supervisor.localize(
 | 
			
		||||
        "snapshot.enter_password"
 | 
			
		||||
      );
 | 
			
		||||
      this._creatingSnapshot = false;
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    if (
 | 
			
		||||
      snapshotDetails.password &&
 | 
			
		||||
      snapshotDetails.password !== snapshotDetails.confirm_password
 | 
			
		||||
    ) {
 | 
			
		||||
      this._error = this._dialogParams!.supervisor.localize(
 | 
			
		||||
        "snapshot.passwords_not_matching"
 | 
			
		||||
      );
 | 
			
		||||
      this._creatingSnapshot = false;
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    delete snapshotDetails.confirm_password;
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
      if (this._snapshotContent.snapshotType === "full") {
 | 
			
		||||
        await createHassioFullSnapshot(this.hass, snapshotDetails);
 | 
			
		||||
      } else {
 | 
			
		||||
        await createHassioPartialSnapshot(this.hass, snapshotDetails);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      this._dialogParams!.onCreate();
 | 
			
		||||
      this.closeDialog();
 | 
			
		||||
    } catch (err) {
 | 
			
		||||
      this._error = extractApiErrorMessage(err);
 | 
			
		||||
    }
 | 
			
		||||
    this._creatingSnapshot = false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static get styles(): CSSResultGroup {
 | 
			
		||||
    return [
 | 
			
		||||
      haStyle,
 | 
			
		||||
      haStyleDialog,
 | 
			
		||||
      css`
 | 
			
		||||
        ha-circular-progress {
 | 
			
		||||
          display: block;
 | 
			
		||||
          text-align: center;
 | 
			
		||||
        }
 | 
			
		||||
      `,
 | 
			
		||||
    ];
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
declare global {
 | 
			
		||||
  interface HTMLElementTagNameMap {
 | 
			
		||||
    "dialog-hassio-create-snapshot": HassioCreateSnapshotDialog;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,6 +1,14 @@
 | 
			
		||||
import { mdiClose } from "@mdi/js";
 | 
			
		||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
 | 
			
		||||
import { customElement, property, state } from "lit/decorators";
 | 
			
		||||
import {
 | 
			
		||||
  css,
 | 
			
		||||
  CSSResult,
 | 
			
		||||
  customElement,
 | 
			
		||||
  html,
 | 
			
		||||
  internalProperty,
 | 
			
		||||
  LitElement,
 | 
			
		||||
  property,
 | 
			
		||||
  TemplateResult,
 | 
			
		||||
} from "lit-element";
 | 
			
		||||
import { fireEvent } from "../../../../src/common/dom/fire_event";
 | 
			
		||||
import "../../../../src/components/ha-header-bar";
 | 
			
		||||
import { HassDialog } from "../../../../src/dialogs/make-dialog-manager";
 | 
			
		||||
@@ -10,12 +18,11 @@ import "../../components/hassio-upload-snapshot";
 | 
			
		||||
import { HassioSnapshotUploadDialogParams } from "./show-dialog-snapshot-upload";
 | 
			
		||||
 | 
			
		||||
@customElement("dialog-hassio-snapshot-upload")
 | 
			
		||||
export class DialogHassioSnapshotUpload
 | 
			
		||||
  extends LitElement
 | 
			
		||||
export class DialogHassioSnapshotUpload extends LitElement
 | 
			
		||||
  implements HassDialog<HassioSnapshotUploadDialogParams> {
 | 
			
		||||
  @property({ attribute: false }) public hass!: HomeAssistant;
 | 
			
		||||
 | 
			
		||||
  @state() private _params?: HassioSnapshotUploadDialogParams;
 | 
			
		||||
  @internalProperty() private _params?: HassioSnapshotUploadDialogParams;
 | 
			
		||||
 | 
			
		||||
  public async showDialog(
 | 
			
		||||
    params: HassioSnapshotUploadDialogParams
 | 
			
		||||
@@ -50,7 +57,9 @@ export class DialogHassioSnapshotUpload
 | 
			
		||||
      >
 | 
			
		||||
        <div slot="heading">
 | 
			
		||||
          <ha-header-bar>
 | 
			
		||||
            <span slot="title"> Upload snapshot </span>
 | 
			
		||||
            <span slot="title">
 | 
			
		||||
              Upload snapshot
 | 
			
		||||
            </span>
 | 
			
		||||
            <mwc-icon-button slot="actionItems" dialogAction="cancel">
 | 
			
		||||
              <ha-svg-icon .path=${mdiClose}></ha-svg-icon>
 | 
			
		||||
            </mwc-icon-button>
 | 
			
		||||
@@ -70,7 +79,7 @@ export class DialogHassioSnapshotUpload
 | 
			
		||||
    this.closeDialog();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static get styles(): CSSResultGroup {
 | 
			
		||||
  static get styles(): CSSResult[] {
 | 
			
		||||
    return [
 | 
			
		||||
      haStyleDialog,
 | 
			
		||||
      css`
 | 
			
		||||
 
 | 
			
		||||
@@ -1,12 +1,19 @@
 | 
			
		||||
import { ActionDetail } from "@material/mwc-list";
 | 
			
		||||
import "@material/mwc-list/mwc-list-item";
 | 
			
		||||
import { mdiClose, mdiDotsVertical } from "@mdi/js";
 | 
			
		||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
 | 
			
		||||
import { customElement, property, query, state } from "lit/decorators";
 | 
			
		||||
import "@material/mwc-button";
 | 
			
		||||
import { mdiClose, mdiDelete, mdiDownload, mdiHistory } from "@mdi/js";
 | 
			
		||||
import "@polymer/paper-checkbox/paper-checkbox";
 | 
			
		||||
import type { PaperCheckboxElement } from "@polymer/paper-checkbox/paper-checkbox";
 | 
			
		||||
import "@polymer/paper-input/paper-input";
 | 
			
		||||
import {
 | 
			
		||||
  css,
 | 
			
		||||
  CSSResult,
 | 
			
		||||
  customElement,
 | 
			
		||||
  html,
 | 
			
		||||
  internalProperty,
 | 
			
		||||
  LitElement,
 | 
			
		||||
  property,
 | 
			
		||||
  TemplateResult,
 | 
			
		||||
} from "lit-element";
 | 
			
		||||
import { fireEvent } from "../../../../src/common/dom/fire_event";
 | 
			
		||||
import { slugify } from "../../../../src/common/string/slugify";
 | 
			
		||||
import "../../../../src/components/buttons/ha-progress-button";
 | 
			
		||||
import "../../../../src/components/ha-button-menu";
 | 
			
		||||
import "../../../../src/components/ha-header-bar";
 | 
			
		||||
import "../../../../src/components/ha-svg-icon";
 | 
			
		||||
import { getSignedPath } from "../../../../src/data/auth";
 | 
			
		||||
@@ -15,47 +22,96 @@ import {
 | 
			
		||||
  fetchHassioSnapshotInfo,
 | 
			
		||||
  HassioSnapshotDetail,
 | 
			
		||||
} from "../../../../src/data/hassio/snapshot";
 | 
			
		||||
import { Supervisor } from "../../../../src/data/supervisor/supervisor";
 | 
			
		||||
import {
 | 
			
		||||
  showAlertDialog,
 | 
			
		||||
  showConfirmationDialog,
 | 
			
		||||
} from "../../../../src/dialogs/generic/show-dialog-box";
 | 
			
		||||
import { HassDialog } from "../../../../src/dialogs/make-dialog-manager";
 | 
			
		||||
import { PolymerChangedEvent } from "../../../../src/polymer-types";
 | 
			
		||||
import { haStyle, haStyleDialog } from "../../../../src/resources/styles";
 | 
			
		||||
import { HomeAssistant } from "../../../../src/types";
 | 
			
		||||
import { fileDownload } from "../../../../src/util/file_download";
 | 
			
		||||
import "../../components/supervisor-snapshot-content";
 | 
			
		||||
import type { SupervisorSnapshotContent } from "../../components/supervisor-snapshot-content";
 | 
			
		||||
import { HassioSnapshotDialogParams } from "./show-dialog-hassio-snapshot";
 | 
			
		||||
 | 
			
		||||
const _computeFolders = (folders) => {
 | 
			
		||||
  const list: Array<{ slug: string; name: string; checked: boolean }> = [];
 | 
			
		||||
  if (folders.includes("homeassistant")) {
 | 
			
		||||
    list.push({
 | 
			
		||||
      slug: "homeassistant",
 | 
			
		||||
      name: "Home Assistant configuration",
 | 
			
		||||
      checked: true,
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
  if (folders.includes("ssl")) {
 | 
			
		||||
    list.push({ slug: "ssl", name: "SSL", checked: true });
 | 
			
		||||
  }
 | 
			
		||||
  if (folders.includes("share")) {
 | 
			
		||||
    list.push({ slug: "share", name: "Share", checked: true });
 | 
			
		||||
  }
 | 
			
		||||
  if (folders.includes("addons/local")) {
 | 
			
		||||
    list.push({ slug: "addons/local", name: "Local add-ons", checked: true });
 | 
			
		||||
  }
 | 
			
		||||
  return list;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const _computeAddons = (addons) => {
 | 
			
		||||
  return addons.map((addon) => ({
 | 
			
		||||
    slug: addon.slug,
 | 
			
		||||
    name: addon.name,
 | 
			
		||||
    version: addon.version,
 | 
			
		||||
    checked: true,
 | 
			
		||||
  }));
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
interface AddonItem {
 | 
			
		||||
  slug: string;
 | 
			
		||||
  name: string;
 | 
			
		||||
  version: string;
 | 
			
		||||
  checked: boolean | null | undefined;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
interface FolderItem {
 | 
			
		||||
  slug: string;
 | 
			
		||||
  name: string;
 | 
			
		||||
  checked: boolean | null | undefined;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@customElement("dialog-hassio-snapshot")
 | 
			
		||||
class HassioSnapshotDialog
 | 
			
		||||
  extends LitElement
 | 
			
		||||
  implements HassDialog<HassioSnapshotDialogParams> {
 | 
			
		||||
class HassioSnapshotDialog extends LitElement {
 | 
			
		||||
  @property({ attribute: false }) public hass!: HomeAssistant;
 | 
			
		||||
 | 
			
		||||
  @state() private _error?: string;
 | 
			
		||||
  @property({ attribute: false }) public supervisor?: Supervisor;
 | 
			
		||||
 | 
			
		||||
  @state() private _snapshot?: HassioSnapshotDetail;
 | 
			
		||||
  @internalProperty() private _error?: string;
 | 
			
		||||
 | 
			
		||||
  @state() private _dialogParams?: HassioSnapshotDialogParams;
 | 
			
		||||
  @internalProperty() private _onboarding = false;
 | 
			
		||||
 | 
			
		||||
  @state() private _restoringSnapshot = false;
 | 
			
		||||
  @internalProperty() private _snapshot?: HassioSnapshotDetail;
 | 
			
		||||
 | 
			
		||||
  @query("supervisor-snapshot-content")
 | 
			
		||||
  private _snapshotContent!: SupervisorSnapshotContent;
 | 
			
		||||
  @internalProperty() private _folders!: FolderItem[];
 | 
			
		||||
 | 
			
		||||
  @internalProperty() private _addons!: AddonItem[];
 | 
			
		||||
 | 
			
		||||
  @internalProperty() private _dialogParams?: HassioSnapshotDialogParams;
 | 
			
		||||
 | 
			
		||||
  @internalProperty() private _snapshotPassword!: string;
 | 
			
		||||
 | 
			
		||||
  @internalProperty() private _restoreHass = true;
 | 
			
		||||
 | 
			
		||||
  public async showDialog(params: HassioSnapshotDialogParams) {
 | 
			
		||||
    this._snapshot = await fetchHassioSnapshotInfo(this.hass, params.slug);
 | 
			
		||||
    this._dialogParams = params;
 | 
			
		||||
    this._restoringSnapshot = false;
 | 
			
		||||
  }
 | 
			
		||||
    this._folders = _computeFolders(
 | 
			
		||||
      this._snapshot?.folders
 | 
			
		||||
    ).sort((a: FolderItem, b: FolderItem) => (a.name > b.name ? 1 : -1));
 | 
			
		||||
    this._addons = _computeAddons(
 | 
			
		||||
      this._snapshot?.addons
 | 
			
		||||
    ).sort((a: AddonItem, b: AddonItem) => (a.name > b.name ? 1 : -1));
 | 
			
		||||
 | 
			
		||||
  public closeDialog() {
 | 
			
		||||
    this._snapshot = undefined;
 | 
			
		||||
    this._dialogParams = undefined;
 | 
			
		||||
    this._restoringSnapshot = false;
 | 
			
		||||
    this._error = undefined;
 | 
			
		||||
    fireEvent(this, "dialog-closed", { dialog: this.localName });
 | 
			
		||||
    this._dialogParams = params;
 | 
			
		||||
    this._onboarding = params.onboarding ?? false;
 | 
			
		||||
    this.supervisor = params.supervisor;
 | 
			
		||||
    if (!this._snapshot.homeassistant) {
 | 
			
		||||
      this._restoreHass = false;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  protected render(): TemplateResult {
 | 
			
		||||
@@ -63,110 +119,204 @@ class HassioSnapshotDialog
 | 
			
		||||
      return html``;
 | 
			
		||||
    }
 | 
			
		||||
    return html`
 | 
			
		||||
      <ha-dialog
 | 
			
		||||
        open
 | 
			
		||||
        scrimClickAction
 | 
			
		||||
        @closed=${this.closeDialog}
 | 
			
		||||
        .heading=${true}
 | 
			
		||||
      >
 | 
			
		||||
      <ha-dialog open @closing=${this._closeDialog} .heading=${true}>
 | 
			
		||||
        <div slot="heading">
 | 
			
		||||
          <ha-header-bar>
 | 
			
		||||
            <span slot="title">${this._snapshot.name}</span>
 | 
			
		||||
            <span slot="title">
 | 
			
		||||
              ${this._computeName}
 | 
			
		||||
            </span>
 | 
			
		||||
            <mwc-icon-button slot="actionItems" dialogAction="cancel">
 | 
			
		||||
              <ha-svg-icon .path=${mdiClose}></ha-svg-icon>
 | 
			
		||||
            </mwc-icon-button>
 | 
			
		||||
          </ha-header-bar>
 | 
			
		||||
        </div>
 | 
			
		||||
        ${this._restoringSnapshot
 | 
			
		||||
          ? html` <ha-circular-progress active></ha-circular-progress>`
 | 
			
		||||
          : html`<supervisor-snapshot-content
 | 
			
		||||
              .hass=${this.hass}
 | 
			
		||||
              .supervisor=${this._dialogParams.supervisor}
 | 
			
		||||
              .snapshot=${this._snapshot}
 | 
			
		||||
              .onboarding=${this._dialogParams.onboarding || false}
 | 
			
		||||
              .localize=${this._dialogParams.localize}
 | 
			
		||||
            >
 | 
			
		||||
            </supervisor-snapshot-content>`}
 | 
			
		||||
        ${this._error ? html`<p class="error">Error: ${this._error}</p>` : ""}
 | 
			
		||||
 | 
			
		||||
        <mwc-button
 | 
			
		||||
          .disabled=${this._restoringSnapshot}
 | 
			
		||||
          slot="secondaryAction"
 | 
			
		||||
          @click=${this._restoreClicked}
 | 
			
		||||
        >
 | 
			
		||||
          Restore
 | 
			
		||||
        </mwc-button>
 | 
			
		||||
 | 
			
		||||
        ${!this._dialogParams.onboarding
 | 
			
		||||
          ? html`<ha-button-menu
 | 
			
		||||
              fixed
 | 
			
		||||
              slot="primaryAction"
 | 
			
		||||
              @action=${this._handleMenuAction}
 | 
			
		||||
              @closed=${(ev: Event) => ev.stopPropagation()}
 | 
			
		||||
            >
 | 
			
		||||
              <mwc-icon-button slot="trigger" alt="menu">
 | 
			
		||||
                <ha-svg-icon .path=${mdiDotsVertical}></ha-svg-icon>
 | 
			
		||||
              </mwc-icon-button>
 | 
			
		||||
              <mwc-list-item>Download Snapshot</mwc-list-item>
 | 
			
		||||
              <mwc-list-item class="error">Delete Snapshot</mwc-list-item>
 | 
			
		||||
            </ha-button-menu>`
 | 
			
		||||
        <div class="details">
 | 
			
		||||
          ${this._snapshot.type === "full"
 | 
			
		||||
            ? "Full snapshot"
 | 
			
		||||
            : "Partial snapshot"}
 | 
			
		||||
          (${this._computeSize})<br />
 | 
			
		||||
          ${this._formatDatetime(this._snapshot.date)}
 | 
			
		||||
        </div>
 | 
			
		||||
        ${this._snapshot.homeassistant
 | 
			
		||||
          ? html`<div>Home Assistant:</div>
 | 
			
		||||
              <paper-checkbox
 | 
			
		||||
                .checked=${this._restoreHass}
 | 
			
		||||
                @change="${(ev: Event) => {
 | 
			
		||||
                  this._restoreHass = (ev.target as PaperCheckboxElement).checked!;
 | 
			
		||||
                }}"
 | 
			
		||||
              >
 | 
			
		||||
                Home Assistant ${this._snapshot.homeassistant}
 | 
			
		||||
              </paper-checkbox>`
 | 
			
		||||
          : ""}
 | 
			
		||||
        ${this._folders.length
 | 
			
		||||
          ? html`
 | 
			
		||||
              <div>Folders:</div>
 | 
			
		||||
              <paper-dialog-scrollable class="no-margin-top">
 | 
			
		||||
                ${this._folders.map((item) => {
 | 
			
		||||
                  return html`
 | 
			
		||||
                    <paper-checkbox
 | 
			
		||||
                      .checked=${item.checked}
 | 
			
		||||
                      @change="${(ev: Event) =>
 | 
			
		||||
                        this._updateFolders(
 | 
			
		||||
                          item,
 | 
			
		||||
                          (ev.target as PaperCheckboxElement).checked
 | 
			
		||||
                        )}"
 | 
			
		||||
                    >
 | 
			
		||||
                      ${item.name}
 | 
			
		||||
                    </paper-checkbox>
 | 
			
		||||
                  `;
 | 
			
		||||
                })}
 | 
			
		||||
              </paper-dialog-scrollable>
 | 
			
		||||
            `
 | 
			
		||||
          : ""}
 | 
			
		||||
        ${this._addons.length
 | 
			
		||||
          ? html`
 | 
			
		||||
              <div>Add-on:</div>
 | 
			
		||||
              <paper-dialog-scrollable class="no-margin-top">
 | 
			
		||||
                ${this._addons.map((item) => {
 | 
			
		||||
                  return html`
 | 
			
		||||
                    <paper-checkbox
 | 
			
		||||
                      .checked=${item.checked}
 | 
			
		||||
                      @change="${(ev: Event) =>
 | 
			
		||||
                        this._updateAddons(
 | 
			
		||||
                          item,
 | 
			
		||||
                          (ev.target as PaperCheckboxElement).checked
 | 
			
		||||
                        )}"
 | 
			
		||||
                    >
 | 
			
		||||
                      ${item.name}
 | 
			
		||||
                    </paper-checkbox>
 | 
			
		||||
                  `;
 | 
			
		||||
                })}
 | 
			
		||||
              </paper-dialog-scrollable>
 | 
			
		||||
            `
 | 
			
		||||
          : ""}
 | 
			
		||||
        ${this._snapshot.protected
 | 
			
		||||
          ? html`
 | 
			
		||||
              <paper-input
 | 
			
		||||
                autofocus=""
 | 
			
		||||
                label="Password"
 | 
			
		||||
                type="password"
 | 
			
		||||
                @value-changed=${this._passwordInput}
 | 
			
		||||
                .value=${this._snapshotPassword}
 | 
			
		||||
              ></paper-input>
 | 
			
		||||
            `
 | 
			
		||||
          : ""}
 | 
			
		||||
        ${this._error ? html` <p class="error">Error: ${this._error}</p> ` : ""}
 | 
			
		||||
 | 
			
		||||
        <div class="button-row" slot="primaryAction">
 | 
			
		||||
          <mwc-button @click=${this._partialRestoreClicked}>
 | 
			
		||||
            <ha-svg-icon .path=${mdiHistory} class="icon"></ha-svg-icon>
 | 
			
		||||
            Restore Selected
 | 
			
		||||
          </mwc-button>
 | 
			
		||||
          ${!this._onboarding
 | 
			
		||||
            ? html`
 | 
			
		||||
                <mwc-button @click=${this._deleteClicked}>
 | 
			
		||||
                  <ha-svg-icon .path=${mdiDelete} class="icon warning">
 | 
			
		||||
                  </ha-svg-icon>
 | 
			
		||||
                  <span class="warning">Delete Snapshot</span>
 | 
			
		||||
                </mwc-button>
 | 
			
		||||
              `
 | 
			
		||||
            : ""}
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="button-row" slot="secondaryAction">
 | 
			
		||||
          ${this._snapshot.type === "full"
 | 
			
		||||
            ? html`
 | 
			
		||||
                <mwc-button @click=${this._fullRestoreClicked}>
 | 
			
		||||
                  <ha-svg-icon .path=${mdiHistory} class="icon"></ha-svg-icon>
 | 
			
		||||
                  Restore Everything
 | 
			
		||||
                </mwc-button>
 | 
			
		||||
              `
 | 
			
		||||
            : ""}
 | 
			
		||||
          ${!this._onboarding
 | 
			
		||||
            ? html`<mwc-button @click=${this._downloadClicked}>
 | 
			
		||||
                <ha-svg-icon .path=${mdiDownload} class="icon"></ha-svg-icon>
 | 
			
		||||
                Download Snapshot
 | 
			
		||||
              </mwc-button>`
 | 
			
		||||
            : ""}
 | 
			
		||||
        </div>
 | 
			
		||||
      </ha-dialog>
 | 
			
		||||
    `;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static get styles(): CSSResultGroup {
 | 
			
		||||
  static get styles(): CSSResult[] {
 | 
			
		||||
    return [
 | 
			
		||||
      haStyle,
 | 
			
		||||
      haStyleDialog,
 | 
			
		||||
      css`
 | 
			
		||||
        ha-svg-icon {
 | 
			
		||||
          color: var(--primary-text-color);
 | 
			
		||||
        }
 | 
			
		||||
        ha-circular-progress {
 | 
			
		||||
        paper-checkbox {
 | 
			
		||||
          display: block;
 | 
			
		||||
          text-align: center;
 | 
			
		||||
          margin: 4px;
 | 
			
		||||
        }
 | 
			
		||||
        mwc-button ha-svg-icon {
 | 
			
		||||
          margin-right: 4px;
 | 
			
		||||
        }
 | 
			
		||||
        .button-row {
 | 
			
		||||
          display: grid;
 | 
			
		||||
          gap: 8px;
 | 
			
		||||
          margin-right: 8px;
 | 
			
		||||
        }
 | 
			
		||||
        .details {
 | 
			
		||||
          color: var(--secondary-text-color);
 | 
			
		||||
        }
 | 
			
		||||
        .warning,
 | 
			
		||||
        .error {
 | 
			
		||||
          color: var(--error-color);
 | 
			
		||||
        }
 | 
			
		||||
        .buttons li {
 | 
			
		||||
          list-style-type: none;
 | 
			
		||||
        }
 | 
			
		||||
        .buttons .icon {
 | 
			
		||||
          margin-right: 16px;
 | 
			
		||||
        }
 | 
			
		||||
        .no-margin-top {
 | 
			
		||||
          margin-top: 0;
 | 
			
		||||
        }
 | 
			
		||||
        ha-header-bar {
 | 
			
		||||
          --mdc-theme-on-primary: var(--primary-text-color);
 | 
			
		||||
          --mdc-theme-primary: var(--mdc-theme-surface);
 | 
			
		||||
          flex-shrink: 0;
 | 
			
		||||
          display: block;
 | 
			
		||||
        }
 | 
			
		||||
        /* 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);
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      `,
 | 
			
		||||
    ];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private _handleMenuAction(ev: CustomEvent<ActionDetail>) {
 | 
			
		||||
    switch (ev.detail.index) {
 | 
			
		||||
      case 0:
 | 
			
		||||
        this._downloadClicked();
 | 
			
		||||
        break;
 | 
			
		||||
      case 1:
 | 
			
		||||
        this._deleteClicked();
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
  private _updateFolders(item: FolderItem, value: boolean | null | undefined) {
 | 
			
		||||
    this._folders = this._folders.map((folder) => {
 | 
			
		||||
      if (folder.slug === item.slug) {
 | 
			
		||||
        folder.checked = value;
 | 
			
		||||
      }
 | 
			
		||||
      return folder;
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private async _restoreClicked() {
 | 
			
		||||
    const snapshotDetails = this._snapshotContent.snapshotDetails();
 | 
			
		||||
    this._restoringSnapshot = true;
 | 
			
		||||
    if (this._snapshotContent.snapshotType === "full") {
 | 
			
		||||
      await this._fullRestoreClicked(snapshotDetails);
 | 
			
		||||
    } else {
 | 
			
		||||
      await this._partialRestoreClicked(snapshotDetails);
 | 
			
		||||
    }
 | 
			
		||||
    this._restoringSnapshot = false;
 | 
			
		||||
  private _updateAddons(item: AddonItem, value: boolean | null | undefined) {
 | 
			
		||||
    this._addons = this._addons.map((addon) => {
 | 
			
		||||
      if (addon.slug === item.slug) {
 | 
			
		||||
        addon.checked = value;
 | 
			
		||||
      }
 | 
			
		||||
      return addon;
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private async _partialRestoreClicked(snapshotDetails) {
 | 
			
		||||
  private _passwordInput(ev: PolymerChangedEvent<string>) {
 | 
			
		||||
    this._snapshotPassword = ev.detail.value;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private async _partialRestoreClicked() {
 | 
			
		||||
    if (
 | 
			
		||||
      this._dialogParams?.supervisor !== undefined &&
 | 
			
		||||
      this._dialogParams?.supervisor.info.state !== "running"
 | 
			
		||||
      this.supervisor !== undefined &&
 | 
			
		||||
      this.supervisor.info.state !== "running"
 | 
			
		||||
    ) {
 | 
			
		||||
      await showAlertDialog(this, {
 | 
			
		||||
        title: "Could not restore snapshot",
 | 
			
		||||
        text: `Restoring a snapshot is not possible right now because the system is in ${this._dialogParams?.supervisor.info.state} state.`,
 | 
			
		||||
        text: `Restoring a snapshot is not possible right now because the system is in ${this.supervisor.info.state} state.`,
 | 
			
		||||
      });
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
@@ -180,17 +330,41 @@ class HassioSnapshotDialog
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!this._dialogParams?.onboarding) {
 | 
			
		||||
    const addons = this._addons
 | 
			
		||||
      .filter((addon) => addon.checked)
 | 
			
		||||
      .map((addon) => addon.slug);
 | 
			
		||||
 | 
			
		||||
    const folders = this._folders
 | 
			
		||||
      .filter((folder) => folder.checked)
 | 
			
		||||
      .map((folder) => folder.slug);
 | 
			
		||||
 | 
			
		||||
    const data: {
 | 
			
		||||
      homeassistant: boolean;
 | 
			
		||||
      addons: any;
 | 
			
		||||
      folders: any;
 | 
			
		||||
      password?: string;
 | 
			
		||||
    } = {
 | 
			
		||||
      homeassistant: this._restoreHass,
 | 
			
		||||
      addons,
 | 
			
		||||
      folders,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    if (this._snapshot!.protected) {
 | 
			
		||||
      data.password = this._snapshotPassword;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!this._onboarding) {
 | 
			
		||||
      this.hass
 | 
			
		||||
        .callApi(
 | 
			
		||||
          "POST",
 | 
			
		||||
 | 
			
		||||
          `hassio/snapshots/${this._snapshot!.slug}/restore/partial`,
 | 
			
		||||
          snapshotDetails
 | 
			
		||||
          data
 | 
			
		||||
        )
 | 
			
		||||
        .then(
 | 
			
		||||
          () => {
 | 
			
		||||
            this.closeDialog();
 | 
			
		||||
            alert("Snapshot restored!");
 | 
			
		||||
            this._closeDialog();
 | 
			
		||||
          },
 | 
			
		||||
          (error) => {
 | 
			
		||||
            this._error = error.body.message;
 | 
			
		||||
@@ -200,20 +374,20 @@ class HassioSnapshotDialog
 | 
			
		||||
      fireEvent(this, "restoring");
 | 
			
		||||
      fetch(`/api/hassio/snapshots/${this._snapshot!.slug}/restore/partial`, {
 | 
			
		||||
        method: "POST",
 | 
			
		||||
        body: JSON.stringify(snapshotDetails),
 | 
			
		||||
        body: JSON.stringify(data),
 | 
			
		||||
      });
 | 
			
		||||
      this.closeDialog();
 | 
			
		||||
      this._closeDialog();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private async _fullRestoreClicked(snapshotDetails) {
 | 
			
		||||
  private async _fullRestoreClicked() {
 | 
			
		||||
    if (
 | 
			
		||||
      this._dialogParams?.supervisor !== undefined &&
 | 
			
		||||
      this._dialogParams?.supervisor.info.state !== "running"
 | 
			
		||||
      this.supervisor !== undefined &&
 | 
			
		||||
      this.supervisor.info.state !== "running"
 | 
			
		||||
    ) {
 | 
			
		||||
      await showAlertDialog(this, {
 | 
			
		||||
        title: "Could not restore snapshot",
 | 
			
		||||
        text: `Restoring a snapshot is not possible right now because the system is in ${this._dialogParams?.supervisor.info.state} state.`,
 | 
			
		||||
        text: `Restoring a snapshot is not possible right now because the system is in ${this.supervisor.info.state} state.`,
 | 
			
		||||
      });
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
@@ -228,16 +402,20 @@ class HassioSnapshotDialog
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!this._dialogParams?.onboarding) {
 | 
			
		||||
    const data = this._snapshot!.protected
 | 
			
		||||
      ? { password: this._snapshotPassword }
 | 
			
		||||
      : undefined;
 | 
			
		||||
    if (!this._onboarding) {
 | 
			
		||||
      this.hass
 | 
			
		||||
        .callApi(
 | 
			
		||||
          "POST",
 | 
			
		||||
          `hassio/snapshots/${this._snapshot!.slug}/restore/full`,
 | 
			
		||||
          snapshotDetails
 | 
			
		||||
          data
 | 
			
		||||
        )
 | 
			
		||||
        .then(
 | 
			
		||||
          () => {
 | 
			
		||||
            this.closeDialog();
 | 
			
		||||
            alert("Snapshot restored!");
 | 
			
		||||
            this._closeDialog();
 | 
			
		||||
          },
 | 
			
		||||
          (error) => {
 | 
			
		||||
            this._error = error.body.message;
 | 
			
		||||
@@ -247,9 +425,9 @@ class HassioSnapshotDialog
 | 
			
		||||
      fireEvent(this, "restoring");
 | 
			
		||||
      fetch(`/api/hassio/snapshots/${this._snapshot!.slug}/restore/full`, {
 | 
			
		||||
        method: "POST",
 | 
			
		||||
        body: JSON.stringify(snapshotDetails),
 | 
			
		||||
        body: JSON.stringify(data),
 | 
			
		||||
      });
 | 
			
		||||
      this.closeDialog();
 | 
			
		||||
      this._closeDialog();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -272,7 +450,7 @@ class HassioSnapshotDialog
 | 
			
		||||
          if (this._dialogParams!.onDelete) {
 | 
			
		||||
            this._dialogParams!.onDelete();
 | 
			
		||||
          }
 | 
			
		||||
          this.closeDialog();
 | 
			
		||||
          this._closeDialog();
 | 
			
		||||
        },
 | 
			
		||||
        (error) => {
 | 
			
		||||
          this._error = error.body.message;
 | 
			
		||||
@@ -288,9 +466,7 @@ class HassioSnapshotDialog
 | 
			
		||||
        `/api/hassio/snapshots/${this._snapshot!.slug}/download`
 | 
			
		||||
      );
 | 
			
		||||
    } catch (err) {
 | 
			
		||||
      await showAlertDialog(this, {
 | 
			
		||||
        text: extractApiErrorMessage(err),
 | 
			
		||||
      });
 | 
			
		||||
      alert(`Error: ${extractApiErrorMessage(err)}`);
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -307,11 +483,13 @@ class HassioSnapshotDialog
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fileDownload(
 | 
			
		||||
      this,
 | 
			
		||||
      signedPath.path,
 | 
			
		||||
      `home_assistant_snapshot_${slugify(this._computeName)}.tar`
 | 
			
		||||
    );
 | 
			
		||||
    const name = this._computeName.replace(/[^a-z0-9]+/gi, "_");
 | 
			
		||||
    const a = document.createElement("a");
 | 
			
		||||
    a.href = signedPath.path;
 | 
			
		||||
    a.download = `Hass_io_${name}.tar`;
 | 
			
		||||
    this.shadowRoot!.appendChild(a);
 | 
			
		||||
    a.click();
 | 
			
		||||
    this.shadowRoot!.removeChild(a);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private get _computeName() {
 | 
			
		||||
@@ -319,6 +497,29 @@ class HassioSnapshotDialog
 | 
			
		||||
      ? this._snapshot.name || this._snapshot.slug
 | 
			
		||||
      : "Unnamed snapshot";
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private get _computeSize() {
 | 
			
		||||
    return Math.ceil(this._snapshot!.size * 10) / 10 + " MB";
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private _formatDatetime(datetime) {
 | 
			
		||||
    return new Date(datetime).toLocaleDateString(navigator.language, {
 | 
			
		||||
      weekday: "long",
 | 
			
		||||
      year: "numeric",
 | 
			
		||||
      month: "short",
 | 
			
		||||
      day: "numeric",
 | 
			
		||||
      hour: "numeric",
 | 
			
		||||
      minute: "2-digit",
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private _closeDialog() {
 | 
			
		||||
    this._dialogParams = undefined;
 | 
			
		||||
    this._snapshot = undefined;
 | 
			
		||||
    this._snapshotPassword = "";
 | 
			
		||||
    this._folders = [];
 | 
			
		||||
    this._addons = [];
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
declare global {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,18 +0,0 @@
 | 
			
		||||
import { fireEvent } from "../../../../src/common/dom/fire_event";
 | 
			
		||||
import { Supervisor } from "../../../../src/data/supervisor/supervisor";
 | 
			
		||||
 | 
			
		||||
export interface HassioCreateSnapshotDialogParams {
 | 
			
		||||
  supervisor: Supervisor;
 | 
			
		||||
  onCreate: () => void;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const showHassioCreateSnapshotDialog = (
 | 
			
		||||
  element: HTMLElement,
 | 
			
		||||
  dialogParams: HassioCreateSnapshotDialogParams
 | 
			
		||||
): void => {
 | 
			
		||||
  fireEvent(element, "show-dialog", {
 | 
			
		||||
    dialogTag: "dialog-hassio-create-snapshot",
 | 
			
		||||
    dialogImport: () => import("./dialog-hassio-create-snapshot"),
 | 
			
		||||
    dialogParams,
 | 
			
		||||
  });
 | 
			
		||||
};
 | 
			
		||||
@@ -1,5 +1,4 @@
 | 
			
		||||
import { fireEvent } from "../../../../src/common/dom/fire_event";
 | 
			
		||||
import { LocalizeFunc } from "../../../../src/common/translations/localize";
 | 
			
		||||
import { Supervisor } from "../../../../src/data/supervisor/supervisor";
 | 
			
		||||
 | 
			
		||||
export interface HassioSnapshotDialogParams {
 | 
			
		||||
@@ -7,7 +6,6 @@ export interface HassioSnapshotDialogParams {
 | 
			
		||||
  onDelete?: () => void;
 | 
			
		||||
  onboarding?: boolean;
 | 
			
		||||
  supervisor?: Supervisor;
 | 
			
		||||
  localize?: LocalizeFunc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const showHassioSnapshotDialog = (
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
import type { LitElement } from "lit";
 | 
			
		||||
import type { LitElement } from "lit-element";
 | 
			
		||||
import {
 | 
			
		||||
  HassioAddonDetails,
 | 
			
		||||
  restartHassioAddon,
 | 
			
		||||
 
 | 
			
		||||
@@ -1,13 +1,23 @@
 | 
			
		||||
import "@material/mwc-button/mwc-button";
 | 
			
		||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
 | 
			
		||||
import { customElement, state } from "lit/decorators";
 | 
			
		||||
import {
 | 
			
		||||
  css,
 | 
			
		||||
  CSSResult,
 | 
			
		||||
  customElement,
 | 
			
		||||
  html,
 | 
			
		||||
  internalProperty,
 | 
			
		||||
  LitElement,
 | 
			
		||||
  TemplateResult,
 | 
			
		||||
} from "lit-element";
 | 
			
		||||
import { fireEvent } from "../../../../src/common/dom/fire_event";
 | 
			
		||||
import "../../../../src/components/ha-circular-progress";
 | 
			
		||||
import "../../../../src/components/ha-dialog";
 | 
			
		||||
import "../../../../src/components/ha-settings-row";
 | 
			
		||||
import "../../../../src/components/ha-svg-icon";
 | 
			
		||||
import "../../../../src/components/ha-switch";
 | 
			
		||||
import { extractApiErrorMessage } from "../../../../src/data/hassio/common";
 | 
			
		||||
import {
 | 
			
		||||
  extractApiErrorMessage,
 | 
			
		||||
  ignoreSupervisorError,
 | 
			
		||||
} from "../../../../src/data/hassio/common";
 | 
			
		||||
import { createHassioPartialSnapshot } from "../../../../src/data/hassio/snapshot";
 | 
			
		||||
import { haStyle, haStyleDialog } from "../../../../src/resources/styles";
 | 
			
		||||
import type { HomeAssistant } from "../../../../src/types";
 | 
			
		||||
@@ -17,15 +27,15 @@ import { SupervisorDialogSupervisorUpdateParams } from "./show-dialog-update";
 | 
			
		||||
class DialogSupervisorUpdate extends LitElement {
 | 
			
		||||
  public hass!: HomeAssistant;
 | 
			
		||||
 | 
			
		||||
  @state() private _opened = false;
 | 
			
		||||
  @internalProperty() private _opened = false;
 | 
			
		||||
 | 
			
		||||
  @state() private _createSnapshot = true;
 | 
			
		||||
  @internalProperty() private _createSnapshot = true;
 | 
			
		||||
 | 
			
		||||
  @state() private _action: "snapshot" | "update" | null = null;
 | 
			
		||||
  @internalProperty() private _action: "snapshot" | "update" | null = null;
 | 
			
		||||
 | 
			
		||||
  @state() private _error?: string;
 | 
			
		||||
  @internalProperty() private _error?: string;
 | 
			
		||||
 | 
			
		||||
  @state()
 | 
			
		||||
  @internalProperty()
 | 
			
		||||
  private _dialogParams?: SupervisorDialogSupervisorUpdateParams;
 | 
			
		||||
 | 
			
		||||
  public async showDialog(
 | 
			
		||||
@@ -150,11 +160,20 @@ class DialogSupervisorUpdate extends LitElement {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    this._action = "update";
 | 
			
		||||
    await this._dialogParams!.updateHandler!();
 | 
			
		||||
    try {
 | 
			
		||||
      await this._dialogParams!.updateHandler!();
 | 
			
		||||
    } catch (err) {
 | 
			
		||||
      if (this.hass.connection.connected && !ignoreSupervisorError(err)) {
 | 
			
		||||
        this._error = extractApiErrorMessage(err);
 | 
			
		||||
      }
 | 
			
		||||
      this._action = null;
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    this.closeDialog();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static get styles(): CSSResultGroup {
 | 
			
		||||
  static get styles(): CSSResult[] {
 | 
			
		||||
    return [
 | 
			
		||||
      haStyle,
 | 
			
		||||
      haStyleDialog,
 | 
			
		||||
 
 | 
			
		||||
@@ -1,18 +1,17 @@
 | 
			
		||||
import { fireEvent } from "../../../../src/common/dom/fire_event";
 | 
			
		||||
import { HassioPartialSnapshotCreateParams } from "../../../../src/data/hassio/snapshot";
 | 
			
		||||
import { Supervisor } from "../../../../src/data/supervisor/supervisor";
 | 
			
		||||
 | 
			
		||||
export interface SupervisorDialogSupervisorUpdateParams {
 | 
			
		||||
  supervisor: Supervisor;
 | 
			
		||||
  name: string;
 | 
			
		||||
  version: string;
 | 
			
		||||
  snapshotParams: HassioPartialSnapshotCreateParams;
 | 
			
		||||
  snapshotParams: any;
 | 
			
		||||
  updateHandler: () => Promise<void>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const showDialogSupervisorUpdate = (
 | 
			
		||||
  element: HTMLElement,
 | 
			
		||||
  dialogParams: Partial<SupervisorDialogSupervisorUpdateParams>
 | 
			
		||||
  dialogParams: SupervisorDialogSupervisorUpdateParams
 | 
			
		||||
): void => {
 | 
			
		||||
  fireEvent(element, "show-dialog", {
 | 
			
		||||
    dialogTag: "dialog-supervisor-update",
 | 
			
		||||
 
 | 
			
		||||
@@ -15,11 +15,5 @@ body {
 | 
			
		||||
  padding: 0;
 | 
			
		||||
  height: 100vh;
 | 
			
		||||
}
 | 
			
		||||
@media (prefers-color-scheme: dark) {
 | 
			
		||||
  body {
 | 
			
		||||
    background-color: #111111;
 | 
			
		||||
    color: #e1e1e1;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
`;
 | 
			
		||||
document.head.appendChild(styleEl);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +1,7 @@
 | 
			
		||||
import { html, PropertyValues } from "lit";
 | 
			
		||||
import { customElement, property } from "lit/decorators";
 | 
			
		||||
import { customElement, html, property, PropertyValues } from "lit-element";
 | 
			
		||||
import { atLeastVersion } from "../../src/common/config/version";
 | 
			
		||||
import { applyThemesOnElement } from "../../src/common/dom/apply_themes_on_element";
 | 
			
		||||
import { fireEvent } from "../../src/common/dom/fire_event";
 | 
			
		||||
import { isNavigationClick } from "../../src/common/dom/is-navigation-click";
 | 
			
		||||
import { mainWindow } from "../../src/common/dom/get_main_window";
 | 
			
		||||
import { navigate } from "../../src/common/navigate";
 | 
			
		||||
import { HassioPanelInfo } from "../../src/data/hassio/supervisor";
 | 
			
		||||
import { Supervisor } from "../../src/data/supervisor/supervisor";
 | 
			
		||||
import { makeDialogManager } from "../../src/dialogs/make-dialog-manager";
 | 
			
		||||
@@ -50,23 +46,14 @@ export class HassioMain extends SupervisorBaseElement {
 | 
			
		||||
    // listen on this element for navigation events, so we need to forward them.
 | 
			
		||||
 | 
			
		||||
    // Joakim - April 26, 2021
 | 
			
		||||
    // Due to changes in behavior in Google Chrome, we changed navigate to listen on the top element
 | 
			
		||||
    mainWindow.addEventListener("location-changed", (ev) =>
 | 
			
		||||
    // Due to changes in behavior in Google Chrome, we changed navigate to fire on the top element
 | 
			
		||||
    top.addEventListener("location-changed", (ev) =>
 | 
			
		||||
      // @ts-ignore
 | 
			
		||||
      fireEvent(this, ev.type, ev.detail, {
 | 
			
		||||
        bubbles: false,
 | 
			
		||||
      })
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    // Paulus - May 17, 2021
 | 
			
		||||
    // Convert the <a> tags to native nav in Home Assistant < 2021.6
 | 
			
		||||
    document.body.addEventListener("click", (ev) => {
 | 
			
		||||
      const href = isNavigationClick(ev);
 | 
			
		||||
      if (href) {
 | 
			
		||||
        navigate(href);
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    // Forward haptic events to parent window.
 | 
			
		||||
    window.addEventListener("haptic", (ev) => {
 | 
			
		||||
      // @ts-ignore
 | 
			
		||||
@@ -103,7 +90,7 @@ export class HassioMain extends SupervisorBaseElement {
 | 
			
		||||
 | 
			
		||||
  private _applyTheme() {
 | 
			
		||||
    let themeName: string;
 | 
			
		||||
    let themeSettings: Partial<HomeAssistant["selectedTheme"]> | undefined;
 | 
			
		||||
    let options: Partial<HomeAssistant["selectedTheme"]> | undefined;
 | 
			
		||||
 | 
			
		||||
    if (atLeastVersion(this.hass.config.version, 0, 114)) {
 | 
			
		||||
      themeName =
 | 
			
		||||
@@ -112,9 +99,9 @@ export class HassioMain extends SupervisorBaseElement {
 | 
			
		||||
          ? this.hass.themes.default_dark_theme!
 | 
			
		||||
          : this.hass.themes.default_theme);
 | 
			
		||||
 | 
			
		||||
      themeSettings = this.hass.selectedTheme;
 | 
			
		||||
      if (themeSettings?.dark === undefined) {
 | 
			
		||||
        themeSettings = {
 | 
			
		||||
      options = this.hass.selectedTheme;
 | 
			
		||||
      if (themeName === "default" && options?.dark === undefined) {
 | 
			
		||||
        options = {
 | 
			
		||||
          ...this.hass.selectedTheme,
 | 
			
		||||
          dark: this.hass.themes.darkMode,
 | 
			
		||||
        };
 | 
			
		||||
@@ -129,7 +116,7 @@ export class HassioMain extends SupervisorBaseElement {
 | 
			
		||||
      this.parentElement,
 | 
			
		||||
      this.hass.themes,
 | 
			
		||||
      themeName,
 | 
			
		||||
      themeSettings
 | 
			
		||||
      options
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,11 @@
 | 
			
		||||
import { html, LitElement, TemplateResult } from "lit";
 | 
			
		||||
import {
 | 
			
		||||
  customElement,
 | 
			
		||||
  html,
 | 
			
		||||
  internalProperty,
 | 
			
		||||
  LitElement,
 | 
			
		||||
  property,
 | 
			
		||||
  TemplateResult,
 | 
			
		||||
} from "lit-element";
 | 
			
		||||
import { sanitizeUrl } from "@braintree/sanitize-url";
 | 
			
		||||
import {
 | 
			
		||||
  createSearchParam,
 | 
			
		||||
@@ -13,7 +20,6 @@ import {
 | 
			
		||||
import { navigate } from "../../src/common/navigate";
 | 
			
		||||
import { HomeAssistant, Route } from "../../src/types";
 | 
			
		||||
import { Supervisor } from "../../src/data/supervisor/supervisor";
 | 
			
		||||
import { customElement, property, state } from "lit/decorators";
 | 
			
		||||
 | 
			
		||||
const REDIRECTS: Redirects = {
 | 
			
		||||
  supervisor: {
 | 
			
		||||
@@ -37,12 +43,6 @@ const REDIRECTS: Redirects = {
 | 
			
		||||
      addon: "string",
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
  supervisor_ingress: {
 | 
			
		||||
    redirect: "/hassio/ingress",
 | 
			
		||||
    params: {
 | 
			
		||||
      addon: "string",
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
  supervisor_add_addon_repository: {
 | 
			
		||||
    redirect: "/hassio/store",
 | 
			
		||||
    params: {
 | 
			
		||||
@@ -59,7 +59,7 @@ class HassioMyRedirect extends LitElement {
 | 
			
		||||
 | 
			
		||||
  @property({ attribute: false }) public route!: Route;
 | 
			
		||||
 | 
			
		||||
  @state() public _error?: TemplateResult | string;
 | 
			
		||||
  @internalProperty() public _error?: TemplateResult | string;
 | 
			
		||||
 | 
			
		||||
  connectedCallback() {
 | 
			
		||||
    super.connectedCallback();
 | 
			
		||||
@@ -89,7 +89,7 @@ class HassioMyRedirect extends LitElement {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    navigate(url, { replace: true });
 | 
			
		||||
    navigate(this, url, true);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  protected render(): TemplateResult {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
import { customElement, property } from "lit/decorators";
 | 
			
		||||
import { customElement, property } from "lit-element";
 | 
			
		||||
import { Supervisor } from "../../src/data/supervisor/supervisor";
 | 
			
		||||
import {
 | 
			
		||||
  HassRouterPage,
 | 
			
		||||
 
 | 
			
		||||
@@ -1,26 +1,19 @@
 | 
			
		||||
import {
 | 
			
		||||
  css,
 | 
			
		||||
  CSSResultGroup,
 | 
			
		||||
  CSSResult,
 | 
			
		||||
  customElement,
 | 
			
		||||
  html,
 | 
			
		||||
  LitElement,
 | 
			
		||||
  PropertyValues,
 | 
			
		||||
  property,
 | 
			
		||||
  TemplateResult,
 | 
			
		||||
} from "lit";
 | 
			
		||||
import { customElement, property, state } from "lit/decorators";
 | 
			
		||||
} from "lit-element";
 | 
			
		||||
import {
 | 
			
		||||
  Supervisor,
 | 
			
		||||
  supervisorApplyUpdateDetails,
 | 
			
		||||
  supervisorCollection,
 | 
			
		||||
} from "../../src/data/supervisor/supervisor";
 | 
			
		||||
import { HomeAssistant, Route } from "../../src/types";
 | 
			
		||||
import "./hassio-panel-router";
 | 
			
		||||
 | 
			
		||||
declare global {
 | 
			
		||||
  interface HASSDomEvents {
 | 
			
		||||
    "supervisor-applying-update": supervisorApplyUpdateDetails;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@customElement("hassio-panel")
 | 
			
		||||
class HassioPanel extends LitElement {
 | 
			
		||||
  @property({ attribute: false }) public hass!: HomeAssistant;
 | 
			
		||||
@@ -31,16 +24,6 @@ class HassioPanel extends LitElement {
 | 
			
		||||
 | 
			
		||||
  @property({ attribute: false }) public route!: Route;
 | 
			
		||||
 | 
			
		||||
  @state() private _applyingUpdate?: supervisorApplyUpdateDetails;
 | 
			
		||||
 | 
			
		||||
  protected firstUpdated(changedProps: PropertyValues) {
 | 
			
		||||
    super.firstUpdated(changedProps);
 | 
			
		||||
    this._applyingUpdate = undefined;
 | 
			
		||||
    this.addEventListener("supervisor-applying-update", (ev) => {
 | 
			
		||||
      this._applyingUpdate = ev.detail;
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  protected render(): TemplateResult {
 | 
			
		||||
    if (!this.hass) {
 | 
			
		||||
      return html`<hass-loading-screen></hass-loading-screen>`;
 | 
			
		||||
@@ -53,16 +36,6 @@ class HassioPanel extends LitElement {
 | 
			
		||||
    ) {
 | 
			
		||||
      return html`<hass-loading-screen></hass-loading-screen>`;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (this._applyingUpdate !== undefined) {
 | 
			
		||||
      return html`<hass-loading-screen no-toolbar>
 | 
			
		||||
        ${this.supervisor.localize("dialog.update.updating", {
 | 
			
		||||
          name: this._applyingUpdate.name,
 | 
			
		||||
          version: this._applyingUpdate.version,
 | 
			
		||||
        })}
 | 
			
		||||
      </hass-loading-screen>`;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return html`
 | 
			
		||||
      <hassio-panel-router
 | 
			
		||||
        .hass=${this.hass}
 | 
			
		||||
@@ -73,7 +46,7 @@ class HassioPanel extends LitElement {
 | 
			
		||||
    `;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static get styles(): CSSResultGroup {
 | 
			
		||||
  static get styles(): CSSResult {
 | 
			
		||||
    return css`
 | 
			
		||||
      :host {
 | 
			
		||||
        --app-header-background-color: var(--sidebar-background-color);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
import { customElement, property } from "lit/decorators";
 | 
			
		||||
import { customElement, property } from "lit-element";
 | 
			
		||||
import { HassioPanelInfo } from "../../src/data/hassio/supervisor";
 | 
			
		||||
import { Supervisor } from "../../src/data/supervisor/supervisor";
 | 
			
		||||
import {
 | 
			
		||||
@@ -61,10 +61,11 @@ class HassioRouter extends HassRouterPage {
 | 
			
		||||
    el.hass = this.hass;
 | 
			
		||||
    el.narrow = this.narrow;
 | 
			
		||||
    el.route = route;
 | 
			
		||||
    el.supervisor = this.supervisor;
 | 
			
		||||
 | 
			
		||||
    if (el.localName === "hassio-ingress-view") {
 | 
			
		||||
      el.ingressPanel = this.panel.config && this.panel.config.ingress;
 | 
			
		||||
    } else {
 | 
			
		||||
      el.supervisor = this.supervisor;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,27 +1,25 @@
 | 
			
		||||
import { mdiMenu } from "@mdi/js";
 | 
			
		||||
import {
 | 
			
		||||
  css,
 | 
			
		||||
  CSSResultGroup,
 | 
			
		||||
  CSSResult,
 | 
			
		||||
  customElement,
 | 
			
		||||
  html,
 | 
			
		||||
  internalProperty,
 | 
			
		||||
  LitElement,
 | 
			
		||||
  property,
 | 
			
		||||
  PropertyValues,
 | 
			
		||||
  TemplateResult,
 | 
			
		||||
} from "lit";
 | 
			
		||||
import { customElement, property, state } from "lit/decorators";
 | 
			
		||||
} from "lit-element";
 | 
			
		||||
import { fireEvent } from "../../../src/common/dom/fire_event";
 | 
			
		||||
import { navigate } from "../../../src/common/navigate";
 | 
			
		||||
import { extractSearchParam } from "../../../src/common/url/search-params";
 | 
			
		||||
import { nextRender } from "../../../src/common/util/render-status";
 | 
			
		||||
import {
 | 
			
		||||
  fetchHassioAddonInfo,
 | 
			
		||||
  HassioAddonDetails,
 | 
			
		||||
} from "../../../src/data/hassio/addon";
 | 
			
		||||
import { extractApiErrorMessage } from "../../../src/data/hassio/common";
 | 
			
		||||
import {
 | 
			
		||||
  createHassioSession,
 | 
			
		||||
  validateHassioSession,
 | 
			
		||||
} from "../../../src/data/hassio/ingress";
 | 
			
		||||
import { Supervisor } from "../../../src/data/supervisor/supervisor";
 | 
			
		||||
import { showAlertDialog } from "../../../src/dialogs/generic/show-dialog-box";
 | 
			
		||||
import "../../../src/layouts/hass-loading-screen";
 | 
			
		||||
import "../../../src/layouts/hass-subpage";
 | 
			
		||||
@@ -31,13 +29,11 @@ import { HomeAssistant, Route } from "../../../src/types";
 | 
			
		||||
class HassioIngressView extends LitElement {
 | 
			
		||||
  @property({ attribute: false }) public hass!: HomeAssistant;
 | 
			
		||||
 | 
			
		||||
  @property({ attribute: false }) public supervisor!: Supervisor;
 | 
			
		||||
 | 
			
		||||
  @property() public route!: Route;
 | 
			
		||||
 | 
			
		||||
  @property() public ingressPanel = false;
 | 
			
		||||
 | 
			
		||||
  @state() private _addon?: HassioAddonDetails;
 | 
			
		||||
  @internalProperty() private _addon?: HassioAddonDetails;
 | 
			
		||||
 | 
			
		||||
  @property({ type: Boolean })
 | 
			
		||||
  public narrow = false;
 | 
			
		||||
@@ -84,43 +80,6 @@ class HassioIngressView extends LitElement {
 | 
			
		||||
      : iframe}`;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  protected async firstUpdated(): Promise<void> {
 | 
			
		||||
    if (this.route.path === "") {
 | 
			
		||||
      const requestedAddon = extractSearchParam("addon");
 | 
			
		||||
      let addonInfo: HassioAddonDetails;
 | 
			
		||||
      if (requestedAddon) {
 | 
			
		||||
        try {
 | 
			
		||||
          addonInfo = await fetchHassioAddonInfo(this.hass, requestedAddon);
 | 
			
		||||
        } catch (err) {
 | 
			
		||||
          await showAlertDialog(this, {
 | 
			
		||||
            text: extractApiErrorMessage(err),
 | 
			
		||||
            title: requestedAddon,
 | 
			
		||||
          });
 | 
			
		||||
          await nextRender();
 | 
			
		||||
          navigate("/hassio/store", { replace: true });
 | 
			
		||||
          return;
 | 
			
		||||
        }
 | 
			
		||||
        if (!addonInfo.version) {
 | 
			
		||||
          await showAlertDialog(this, {
 | 
			
		||||
            text: this.supervisor.localize("my.error_addon_not_installed"),
 | 
			
		||||
            title: addonInfo.name,
 | 
			
		||||
          });
 | 
			
		||||
          await nextRender();
 | 
			
		||||
          navigate(`/hassio/addon/${addonInfo.slug}/info`, { replace: true });
 | 
			
		||||
        } else if (!addonInfo.ingress) {
 | 
			
		||||
          await showAlertDialog(this, {
 | 
			
		||||
            text: this.supervisor.localize("my.error_addon_no_ingress"),
 | 
			
		||||
            title: addonInfo.name,
 | 
			
		||||
          });
 | 
			
		||||
          await nextRender();
 | 
			
		||||
          navigate(`/hassio/addon/${addonInfo.slug}/info`, { replace: true });
 | 
			
		||||
        } else {
 | 
			
		||||
          navigate(`/hassio/ingress/${addonInfo.slug}`, { replace: true });
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  protected updated(changedProps: PropertyValues) {
 | 
			
		||||
    super.updated(changedProps);
 | 
			
		||||
 | 
			
		||||
@@ -150,7 +109,6 @@ class HassioIngressView extends LitElement {
 | 
			
		||||
        text: "Unable to fetch add-on info to start Ingress",
 | 
			
		||||
        title: "Supervisor",
 | 
			
		||||
      });
 | 
			
		||||
      await nextRender();
 | 
			
		||||
      history.back();
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
@@ -160,7 +118,6 @@ class HassioIngressView extends LitElement {
 | 
			
		||||
        text: "Add-on does not support Ingress",
 | 
			
		||||
        title: addon.name,
 | 
			
		||||
      });
 | 
			
		||||
      await nextRender();
 | 
			
		||||
      history.back();
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
@@ -170,8 +127,7 @@ class HassioIngressView extends LitElement {
 | 
			
		||||
        text: "Add-on is not running. Please start it first",
 | 
			
		||||
        title: addon.name,
 | 
			
		||||
      });
 | 
			
		||||
      await nextRender();
 | 
			
		||||
      navigate(`/hassio/addon/${addon.slug}/info`, { replace: true });
 | 
			
		||||
      navigate(this, `/hassio/addon/${addon.slug}/info`, true);
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -184,7 +140,6 @@ class HassioIngressView extends LitElement {
 | 
			
		||||
        text: "Unable to create an Ingress session",
 | 
			
		||||
        title: addon.name,
 | 
			
		||||
      });
 | 
			
		||||
      await nextRender();
 | 
			
		||||
      history.back();
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
@@ -207,7 +162,7 @@ class HassioIngressView extends LitElement {
 | 
			
		||||
    fireEvent(this, "hass-toggle-menu");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static get styles(): CSSResultGroup {
 | 
			
		||||
  static get styles(): CSSResult {
 | 
			
		||||
    return css`
 | 
			
		||||
      iframe {
 | 
			
		||||
        display: block;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
import { css } from "lit";
 | 
			
		||||
import { css } from "lit-element";
 | 
			
		||||
 | 
			
		||||
export const hassioStyle = css`
 | 
			
		||||
  .content {
 | 
			
		||||
 
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user