Compare commits

..
Author SHA1 Message Date
Donnie 56d4fbab86 Highlight matched letters in quick bar 2021-03-25 08:43:24 -07:00
905 changed files with 18617 additions and 27364 deletions
+3 -22
View File
@@ -4,7 +4,8 @@
"plugin:@typescript-eslint/recommended",
"plugin:wc/recommended",
"plugin:lit/recommended",
"prettier"
"prettier",
"prettier/@typescript-eslint"
],
"parser": "@typescript-eslint/parser",
"parserOptions": {
@@ -83,27 +84,7 @@
"@typescript-eslint/no-unused-vars": 0,
"@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
"@typescript-eslint/no-shadow": ["error"]
},
"plugins": ["disable", "import", "lit", "prettier", "@typescript-eslint"],
"processor": "disable/disable"
+26 -9
View File
@@ -1,6 +1,8 @@
name: Report a bug with the UI, Frontend or Lovelace
description: Report an issue related to the Home Assistant frontend.
about: Report an issue related to the Home Assistant frontend.
labels: bug
title: ""
issue_body: true
body:
- type: markdown
attributes:
@@ -95,7 +97,11 @@ body:
If your issue is about how an entity is shown in the UI, please add the
state and attributes for all situations. You can find this information
at Developer Tools -> States.
render: txt
value: |
```yaml
# Paste your state here.
```
- type: textarea
attributes:
label: Problem-relevant frontend configuration
@@ -104,18 +110,29 @@ body:
configuration of the used cards. Fill this out even if it seems
unimportant to you. Please be sure to remove personal information like
passwords, private URLs and other credentials.
render: yaml
value: |
```yaml
# Paste your YAML here.
```
- type: textarea
attributes:
label: Javascript errors shown in your browser console/inspector
description: >
If you come across any Javascript or other error logs, e.g., in your
browser console/inspector please provide them.
render: txt
- type: textarea
value: |
```txt
# Paste your logs here.
```
- type: markdown
attributes:
label: Additional information
description: >
value: |
## Additional information
- type: markdown
attributes:
value: |
If you have any additional information for us, use the field below.
Please note, you can attach screenshots or screen recordings here, by
dragging and dropping files in the field below.
Please note, you can attach screenshots or screen recordings here,
by dragging and dropping files in the field below.
+2 -4
View File
@@ -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:
+2 -2
View File
@@ -6,8 +6,8 @@ on:
- published
env:
WHEELS_TAG: 3.8-alpine3.12
PYTHON_VERSION: 3.8
WHEELS_TAG: 3.7-alpine3.11
PYTHON_VERSION: 3.7
NODE_VERSION: 12.1
jobs:
+10 -6
View File
@@ -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");
@@ -54,13 +53,13 @@ module.exports.babelOptions = ({ latestBuild }) => ({
babelrc: 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})
@@ -73,9 +72,14 @@ 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),
});
-1
View File
@@ -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
View File
@@ -1,4 +1,3 @@
/* eslint-disable @typescript-eslint/no-var-requires */
const path = require("path");
module.exports = {
+94 -82
View File
@@ -1,4 +1,3 @@
/* eslint-disable @typescript-eslint/no-var-requires */
const path = require("path");
const commonjs = require("@rollup/plugin-commonjs");
@@ -33,82 +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({
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,
}) => {
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,
@@ -116,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
View File
@@ -1,4 +1,3 @@
/* eslint-disable @typescript-eslint/no-var-requires */
const path = require("path");
const fs = require("fs");
+21 -19
View File
@@ -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 {
@@ -95,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,
@@ -117,7 +115,7 @@ const createWebpackConfig = ({
new webpack.NormalModuleReplacementPlugin(
new RegExp(
require.resolve(
"@lit-labs/virtualizer/lib/uni-virtualizer/lib/polyfillLoaders/EventTarget.js"
"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,
+17 -7
View File
@@ -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;
+19 -10
View File
@@ -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);
+10 -3
View File
@@ -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;
+10 -5
View File
@@ -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>) =>
+10 -3
View File
@@ -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;
+11 -5
View File
@@ -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";
@@ -28,12 +35,11 @@ class HcLovelace extends LitElement {
}
const lovelace: Lovelace = {
config: this.lovelaceConfig,
rawConfig: this.lovelaceConfig,
editMode: false,
urlPath: this.urlPath!,
enableFullEditMode: () => undefined,
mode: "storage",
locale: this.hass.locale,
language: "en",
saveConfig: async () => undefined,
deleteConfig: async () => undefined,
setEditMode: () => undefined,
@@ -84,7 +90,7 @@ class HcLovelace extends LitElement {
return undefined;
}
static get styles(): CSSResultGroup {
static get styles(): CSSResult {
return css`
:host {
min-height: 100vh;
+13 -15
View File
@@ -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;
@@ -217,17 +221,11 @@ export class HcMain extends HassElement {
}
private async _generateLovelaceConfig() {
const { generateLovelaceDashboardStrategy } = await import(
"../../../../src/panels/lovelace/strategies/get-strategy"
const { generateLovelaceConfigFromHass } = await import(
"../../../../src/panels/lovelace/common/generate-lovelace-config"
);
this._handleNewLovelaceConfig(
await generateLovelaceDashboardStrategy(
{
hass: this.hass!,
narrow: false,
},
"original-states"
)
await generateLovelaceConfigFromHass(this.hass!)
);
}
+13 -21
View File
@@ -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",
},
+43 -29
View File
@@ -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 -1
View File
@@ -1,5 +1,5 @@
/* eslint-disable */
import { LitElement } from "lit";
import { LitElement } from "lit-element";
import "./card-tools";
class CardModder extends LitElement {
+1 -1
View File
@@ -1,5 +1,5 @@
/* eslint-disable */
import { html, LitElement } from "lit";
import { html, LitElement } from "lit-element";
if (!window.cardTools) {
const version = 0.2;
+11 -4
View File
@@ -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;
+12 -5
View File
@@ -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 {
+2 -2
View File
@@ -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),
+3 -1
View File
@@ -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 -24
View File
@@ -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,
},
};
}
@@ -82,17 +70,6 @@ class DemoCard extends PolymerElement {
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) {
+92 -58
View File
@@ -2,7 +2,8 @@ import { DemoTrace } from "./types";
export const basicTrace: DemoTrace = {
trace: {
last_step: "action/2",
last_action: "action/2",
last_condition: "condition/0",
run_id: "0",
state: "stopped",
timestamp: {
@@ -12,61 +13,7 @@ export const basicTrace: DemoTrace = {
trigger: "state of input_boolean.toggle_1",
domain: "automation",
item_id: "1615419646544",
trace: {
"trigger/0": [
{
path: "trigger/0",
timestamp: "2021-03-25T04:36:51.223693+00:00",
},
],
"condition/0": [
{
path: "condition/0",
timestamp: "2021-03-25T04:36:51.228243+00:00",
changed_variables: {
trigger: {
platform: "state",
entity_id: "input_boolean.toggle_1",
from_state: {
entity_id: "input_boolean.toggle_1",
state: "on",
attributes: {
editable: true,
friendly_name: "Toggle 1",
},
last_changed: "2021-03-24T19:03:59.141440+00:00",
last_updated: "2021-03-24T19:03:59.141440+00:00",
context: {
id: "5d0918eb379214d07554bdab6a08bcff",
parent_id: null,
user_id: null,
},
},
to_state: {
entity_id: "input_boolean.toggle_1",
state: "off",
attributes: {
editable: true,
friendly_name: "Toggle 1",
},
last_changed: "2021-03-25T04:36:51.220696+00:00",
last_updated: "2021-03-25T04:36:51.220696+00:00",
context: {
id: "664d6d261450a9ecea6738e97269a149",
parent_id: null,
user_id: "d1b4e89da01445fa8bc98e39fac477ca",
},
},
for: null,
attribute: null,
description: "state of input_boolean.toggle_1",
},
},
result: {
result: true,
},
},
],
action_trace: {
"action/0": [
{
path: "action/0",
@@ -211,7 +158,56 @@ export const basicTrace: DemoTrace = {
},
],
},
condition_trace: {
"condition/0": [
{
path: "condition/0",
timestamp: "2021-03-25T04:36:51.228243+00:00",
changed_variables: {
trigger: {
platform: "state",
entity_id: "input_boolean.toggle_1",
from_state: {
entity_id: "input_boolean.toggle_1",
state: "on",
attributes: {
editable: true,
friendly_name: "Toggle 1",
},
last_changed: "2021-03-24T19:03:59.141440+00:00",
last_updated: "2021-03-24T19:03:59.141440+00:00",
context: {
id: "5d0918eb379214d07554bdab6a08bcff",
parent_id: null,
user_id: null,
},
},
to_state: {
entity_id: "input_boolean.toggle_1",
state: "off",
attributes: {
editable: true,
friendly_name: "Toggle 1",
},
last_changed: "2021-03-25T04:36:51.220696+00:00",
last_updated: "2021-03-25T04:36:51.220696+00:00",
context: {
id: "664d6d261450a9ecea6738e97269a149",
parent_id: null,
user_id: "d1b4e89da01445fa8bc98e39fac477ca",
},
},
for: null,
attribute: null,
description: "state of input_boolean.toggle_1",
},
},
result: {
result: true,
},
},
],
},
config: {
id: "1615419646544",
alias: "Ensure Party mode",
@@ -289,7 +285,45 @@ export const basicTrace: DemoTrace = {
parent_id: "664d6d261450a9ecea6738e97269a149",
user_id: null,
},
script_execution: "finished",
variables: {
trigger: {
platform: "state",
entity_id: "input_boolean.toggle_1",
from_state: {
entity_id: "input_boolean.toggle_1",
state: "on",
attributes: {
editable: true,
friendly_name: "Toggle 1",
},
last_changed: "2021-03-24T19:03:59.141440+00:00",
last_updated: "2021-03-24T19:03:59.141440+00:00",
context: {
id: "5d0918eb379214d07554bdab6a08bcff",
parent_id: null,
user_id: null,
},
},
to_state: {
entity_id: "input_boolean.toggle_1",
state: "off",
attributes: {
editable: true,
friendly_name: "Toggle 1",
},
last_changed: "2021-03-25T04:36:51.220696+00:00",
last_updated: "2021-03-25T04:36:51.220696+00:00",
context: {
id: "664d6d261450a9ecea6738e97269a149",
parent_id: null,
user_id: "d1b4e89da01445fa8bc98e39fac477ca",
},
},
for: null,
attribute: null,
description: "state of input_boolean.toggle_1",
},
},
},
logbookEntries: [
{
@@ -1,44 +0,0 @@
import { LogbookEntry } from "../../../../src/data/logbook";
import { AutomationTraceExtended } from "../../../../src/data/trace";
import { DemoTrace } from "./types";
export const mockDemoTrace = (
tracePartial: Partial<AutomationTraceExtended>,
logbookEntries?: LogbookEntry[]
): DemoTrace => ({
trace: {
last_step: "",
run_id: "0",
state: "stopped",
timestamp: {
start: "2021-03-25T04:36:51.223693+00:00",
finish: "2021-03-25T04:36:51.266132+00:00",
},
trigger: "mocked trigger",
domain: "automation",
item_id: "1615419646544",
trace: {
"trigger/0": [
{
path: "trigger/0",
changed_variables: {
trigger: {
description: "mocked trigger",
},
},
timestamp: "2021-03-25T04:36:51.223693+00:00",
},
],
},
config: {
trigger: [],
action: [],
},
context: {
id: "abcd",
},
script_execution: "finished",
...tracePartial,
},
logbookEntries: logbookEntries || [],
});
+43 -9
View File
@@ -2,7 +2,8 @@ import { DemoTrace } from "./types";
export const motionLightTrace: DemoTrace = {
trace: {
last_step: "action/3",
last_action: "action/3",
last_condition: null,
run_id: "1",
state: "stopped",
timestamp: {
@@ -12,13 +13,7 @@ export const motionLightTrace: DemoTrace = {
trigger: "state of binary_sensor.pauluss_macbook_pro_camera_in_use",
domain: "automation",
item_id: "1614732497392",
trace: {
"trigger/0": [
{
path: "trigger/0",
timestamp: "2021-03-25T04:36:51.223693+00:00",
},
],
action_trace: {
"action/0": [
{
path: "action/0",
@@ -129,6 +124,7 @@ export const motionLightTrace: DemoTrace = {
},
],
},
condition_trace: {},
config: {
mode: "restart",
max_exceeded: "silent",
@@ -176,7 +172,45 @@ export const motionLightTrace: DemoTrace = {
parent_id: "e22ddfd5f11dc4aad9a52fc10dab613b",
user_id: null,
},
script_execution: "finished",
variables: {
trigger: {
platform: "state",
entity_id: "binary_sensor.pauluss_macbook_pro_camera_in_use",
from_state: {
entity_id: "binary_sensor.pauluss_macbook_pro_camera_in_use",
state: "off",
attributes: {
friendly_name: "Pauluss MacBook Pro Camera In Use",
icon: "mdi:camera-off",
},
last_changed: "2021-03-14T06:06:29.235325+00:00",
last_updated: "2021-03-14T06:06:29.235325+00:00",
context: {
id: "ad4864c5ce957c38a07b50378eeb245d",
parent_id: null,
user_id: null,
},
},
to_state: {
entity_id: "binary_sensor.pauluss_macbook_pro_camera_in_use",
state: "on",
attributes: {
friendly_name: "Pauluss MacBook Pro Camera In Use",
icon: "mdi:camera",
},
last_changed: "2021-03-14T06:07:01.762009+00:00",
last_updated: "2021-03-14T06:07:01.762009+00:00",
context: {
id: "e22ddfd5f11dc4aad9a52fc10dab613b",
parent_id: null,
user_id: null,
},
},
for: null,
attribute: null,
description: "state of binary_sensor.pauluss_macbook_pro_camera_in_use",
},
},
},
logbookEntries: [
{
@@ -1,96 +0,0 @@
import { safeDump } from "js-yaml";
import { html, css, LitElement, TemplateResult } from "lit";
import { customElement, property } from "lit/decorators";
import "../../../src/components/ha-card";
import { describeAction } from "../../../src/data/script_i18n";
import { provideHass } from "../../../src/fake_data/provide_hass";
import { HomeAssistant } from "../../../src/types";
const actions = [
{ wait_template: "{{ true }}", alias: "Something with an alias" },
{ delay: "0:05" },
{ wait_template: "{{ true }}" },
{
condition: "template",
value_template: "{{ true }}",
},
{ event: "happy_event" },
{
device_id: "abcdefgh",
domain: "plex",
entity_id: "media_player.kitchen",
},
{ scene: "scene.kitchen_morning" },
{
wait_for_trigger: [
{
platform: "state",
entity_id: "input_boolean.toggle_1",
},
],
},
{
variables: {
hello: "world",
},
},
{
service: "input_boolean.toggle",
target: {
entity_id: "input_boolean.toggle_4",
},
},
];
@customElement("demo-automation-describe-action")
export class DemoAutomationDescribeAction extends LitElement {
@property({ attribute: false }) hass!: HomeAssistant;
protected render(): TemplateResult {
if (!this.hass) {
return html``;
}
return html`
<ha-card header="Actions">
${actions.map(
(conf) => html`
<div class="action">
<span>${describeAction(this.hass, conf as any)}</span>
<pre>${safeDump(conf)}</pre>
</div>
`
)}
</ha-card>
`;
}
protected firstUpdated(changedProps) {
super.firstUpdated(changedProps);
const hass = provideHass(this);
hass.updateTranslations(null, "en");
}
static get styles() {
return css`
ha-card {
max-width: 600px;
margin: 24px auto;
}
.action {
padding: 16px;
display: flex;
align-items: center;
justify-content: space-between;
}
span {
margin-right: 16px;
}
`;
}
}
declare global {
interface HTMLElementTagNameMap {
"demo-automation-describe-action": DemoAutomationDescribeAction;
}
}
@@ -1,60 +0,0 @@
import { safeDump } from "js-yaml";
import { html, css, LitElement, TemplateResult } from "lit";
import { customElement } from "lit/decorators";
import "../../../src/components/ha-card";
import { describeCondition } from "../../../src/data/automation_i18n";
const conditions = [
{ condition: "and" },
{ condition: "not" },
{ condition: "or" },
{ condition: "state" },
{ condition: "numeric_state" },
{ condition: "sun", after: "sunset" },
{ condition: "sun", after: "sunrise" },
{ condition: "zone" },
{ condition: "time" },
{ condition: "template" },
];
@customElement("demo-automation-describe-condition")
export class DemoAutomationDescribeCondition extends LitElement {
protected render(): TemplateResult {
return html`
<ha-card header="Conditions">
${conditions.map(
(conf) => html`
<div class="condition">
<span>${describeCondition(conf as any)}</span>
<pre>${safeDump(conf)}</pre>
</div>
`
)}
</ha-card>
`;
}
static get styles() {
return css`
ha-card {
max-width: 600px;
margin: 24px auto;
}
.condition {
padding: 16px;
display: flex;
align-items: center;
justify-content: space-between;
}
span {
margin-right: 16px;
}
`;
}
}
declare global {
interface HTMLElementTagNameMap {
"demo-automation-describe-condition": DemoAutomationDescribeCondition;
}
}
@@ -1,63 +0,0 @@
import { safeDump } from "js-yaml";
import { html, css, LitElement, TemplateResult } from "lit";
import { customElement } from "lit/decorators";
import "../../../src/components/ha-card";
import { describeTrigger } from "../../../src/data/automation_i18n";
const triggers = [
{ platform: "state" },
{ platform: "mqtt" },
{ platform: "geo_location" },
{ platform: "homeassistant" },
{ platform: "numeric_state" },
{ platform: "sun" },
{ platform: "time_pattern" },
{ platform: "webhook" },
{ platform: "zone" },
{ platform: "tag" },
{ platform: "time" },
{ platform: "template" },
{ platform: "event" },
];
@customElement("demo-automation-describe-trigger")
export class DemoAutomationDescribeTrigger extends LitElement {
protected render(): TemplateResult {
return html`
<ha-card header="Triggers">
${triggers.map(
(conf) => html`
<div class="trigger">
<span>${describeTrigger(conf as any)}</span>
<pre>${safeDump(conf)}</pre>
</div>
`
)}
</ha-card>
`;
}
static get styles() {
return css`
ha-card {
max-width: 600px;
margin: 24px auto;
}
.trigger {
padding: 16px;
display: flex;
align-items: center;
justify-content: space-between;
}
span {
margin-right: 16px;
}
`;
}
}
declare global {
interface HTMLElementTagNameMap {
"demo-automation-describe-trigger": DemoAutomationDescribeTrigger;
}
}
@@ -1,81 +0,0 @@
import { html, css, LitElement, TemplateResult } from "lit";
import { customElement, property } from "lit/decorators";
import "../../../src/components/ha-card";
import "../../../src/components/trace/hat-script-graph";
import "../../../src/components/trace/hat-trace-timeline";
import { provideHass } from "../../../src/fake_data/provide_hass";
import { HomeAssistant } from "../../../src/types";
import { mockDemoTrace } from "../data/traces/mock-demo-trace";
import { DemoTrace } from "../data/traces/types";
const traces: DemoTrace[] = [
mockDemoTrace({ state: "running" }),
mockDemoTrace({ state: "debugged" }),
mockDemoTrace({ state: "stopped", script_execution: "failed_conditions" }),
mockDemoTrace({ state: "stopped", script_execution: "failed_single" }),
mockDemoTrace({ state: "stopped", script_execution: "failed_max_runs" }),
mockDemoTrace({ state: "stopped", script_execution: "finished" }),
mockDemoTrace({ state: "stopped", script_execution: "aborted" }),
mockDemoTrace({
state: "stopped",
script_execution: "error",
error: 'Variable "beer" cannot be None',
}),
mockDemoTrace({ state: "stopped", script_execution: "cancelled" }),
];
@customElement("demo-automation-trace-timeline")
export class DemoAutomationTraceTimeline extends LitElement {
@property({ attribute: false }) hass?: HomeAssistant;
protected render(): TemplateResult {
if (!this.hass) {
return html``;
}
return html`
${traces.map(
(trace) => html`
<ha-card .header=${trace.trace.config.alias}>
<div class="card-content">
<hat-trace-timeline
.hass=${this.hass}
.trace=${trace.trace}
.logbookEntries=${trace.logbookEntries}
></hat-trace-timeline>
<button @click=${() => console.log(trace)}>Log trace</button>
</div>
</ha-card>
`
)}
`;
}
protected firstUpdated(changedProps) {
super.firstUpdated(changedProps);
const hass = provideHass(this);
hass.updateTranslations(null, "en");
}
static get styles() {
return css`
ha-card {
max-width: 600px;
margin: 24px;
}
.card-content {
display: flex;
}
button {
position: absolute;
top: 0;
right: 0;
}
`;
}
}
declare global {
interface HTMLElementTagNameMap {
"demo-automation-trace-timeline": DemoAutomationTraceTimeline;
}
}
+13 -40
View File
@@ -1,13 +1,18 @@
import { html, css, LitElement, TemplateResult } from "lit";
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";
import "../../../src/components/trace/hat-trace";
import { provideHass } from "../../../src/fake_data/provide_hass";
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,38 +20,20 @@ const traces: DemoTrace[] = [basicTrace, motionLightTrace];
export class DemoAutomationTrace extends LitElement {
@property({ attribute: false }) hass?: HomeAssistant;
@state() private _selected = {};
protected render(): TemplateResult {
if (!this.hass) {
return html``;
}
return html`
${traces.map(
(trace, idx) => html`
<ha-card .header=${trace.trace.config.alias}>
(trace) => html`
<ha-card .heading=${trace.trace.config.alias}>
<div class="card-content">
<hat-script-graph
.trace=${trace.trace}
.selected=${this._selected[idx]}
@graph-node-selected=${(ev) => {
this._selected = { ...this._selected, [idx]: ev.detail.path };
}}
></hat-script-graph>
<hat-trace-timeline
allowPick
<hat-trace
.hass=${this.hass}
.trace=${trace.trace}
.logbookEntries=${trace.logbookEntries}
.selectedPath=${this._selected[idx]}
@value-changed=${(ev) => {
this._selected = {
...this._selected,
[idx]: ev.detail.value,
};
}}
></hat-trace-timeline>
<button @click=${() => console.log(trace)}>Log trace</button>
></hat-trace>
</div>
</ha-card>
`
@@ -66,20 +53,6 @@ export class DemoAutomationTrace extends LitElement {
max-width: 600px;
margin: 24px;
}
.card-content {
display: flex;
}
.card-content > * {
margin-right: 16px;
}
.card-content > *:last-child {
margin-right: 0;
}
button {
position: absolute;
top: 0;
right: 0;
}
`;
}
}
@@ -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);
+9 -7
View File
@@ -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);
+9 -7
View File
@@ -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);
+11 -9
View File
@@ -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);
+2 -7
View File
@@ -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);
+9 -7
View File
@@ -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);
+9 -7
View File
@@ -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);
+9 -7
View File
@@ -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);
+9 -7
View File
@@ -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);
-349
View File
@@ -1,349 +0,0 @@
import { html, css, LitElement, TemplateResult } from "lit";
import "../../../src/components/ha-formfield";
import "../../../src/components/ha-switch";
import { IntegrationManifest } from "../../../src/data/integration";
import { provideHass } from "../../../src/fake_data/provide_hass";
import { HomeAssistant } from "../../../src/types";
import "../../../src/panels/config/integrations/ha-integration-card";
import "../../../src/panels/config/integrations/ha-ignored-config-entry-card";
import "../../../src/panels/config/integrations/ha-config-flow-card";
import type {
ConfigEntryExtended,
DataEntryFlowProgressExtended,
} 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";
const createConfigEntry = (
title: string,
override: Partial<ConfigEntryExtended> = {}
): ConfigEntryExtended => ({
entry_id: title,
domain: "esphome",
localized_domain_name: "ESPHome",
title,
source: "zeroconf",
state: "loaded",
connection_class: "local_push",
supports_options: false,
supports_unload: true,
disabled_by: null,
reason: null,
...override,
});
const createManifest = (
isCustom: boolean,
isCloud: boolean,
name = "ESPHome"
): IntegrationManifest => ({
name,
domain: "esphome",
is_built_in: !isCustom,
config_flow: false,
documentation: "https://www.home-assistant.io/integrations/esphome/",
iot_class: isCloud ? "cloud_polling" : "local_polling",
});
const loadedEntry = createConfigEntry("Loaded");
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",
});
const optionsFlowEntry = createConfigEntry("Options Flow", {
supports_options: true,
});
const setupErrorEntry = createConfigEntry("Setup Error", {
state: "setup_error",
});
const migrationErrorEntry = createConfigEntry("Migration Error", {
state: "migration_error",
});
const setupRetryEntry = createConfigEntry("Setup Retry", {
state: "setup_retry",
});
const setupRetryReasonEntry = createConfigEntry("Setup Retry", {
state: "setup_retry",
reason: "connection_error",
});
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')",
});
const failedUnloadEntry = createConfigEntry("Failed Unload", {
state: "failed_unload",
});
const notLoadedEntry = createConfigEntry("Not Loaded", { state: "not_loaded" });
const disabledEntry = createConfigEntry("Disabled", {
state: "not_loaded",
disabled_by: "user",
});
const disabledFailedUnloadEntry = createConfigEntry(
"Disabled - Failed Unload",
{
state: "failed_unload",
disabled_by: "user",
}
);
const configFlows: DataEntryFlowProgressExtended[] = [
{
flow_id: "adbb401329d8439ebb78ef29837826a8",
handler: "roku",
context: {
source: "ssdp",
unique_id: "YF008D862864",
title_placeholders: {
name: "Living room Roku",
},
},
step_id: "discovery_confirm",
localized_title: "Living room Roku",
},
{
flow_id: "adbb401329d8439ebb78ef29837826a8",
handler: "hue",
context: {
source: "reauth",
unique_id: "YF008D862864",
title_placeholders: {
name: "Living room Roku",
},
},
step_id: "discovery_confirm",
localized_title: "Philips Hue",
},
];
const configEntries: Array<{
items: ConfigEntryExtended[];
is_custom?: boolean;
disabled?: boolean;
highlight?: string;
}> = [
{ items: [loadedEntry] },
{ items: [configPanelEntry] },
{ items: [optionsFlowEntry] },
{ items: [nameAsDomainEntry] },
{ items: [longNameEntry] },
{ items: [longNonBreakingNameEntry] },
{ items: [setupErrorEntry] },
{ items: [migrationErrorEntry] },
{ items: [setupRetryEntry] },
{ items: [setupRetryReasonEntry] },
{ items: [setupRetryReasonMissingKeyEntry] },
{ items: [failedUnloadEntry] },
{ items: [notLoadedEntry] },
{
items: [
loadedEntry,
setupErrorEntry,
migrationErrorEntry,
longNameEntry,
longNonBreakingNameEntry,
setupRetryEntry,
failedUnloadEntry,
notLoadedEntry,
disabledEntry,
nameAsDomainEntry,
configPanelEntry,
optionsFlowEntry,
],
},
{ disabled: true, items: [disabledEntry] },
{ disabled: true, items: [disabledFailedUnloadEntry] },
{
disabled: true,
items: [disabledEntry, disabledFailedUnloadEntry],
},
{
items: [loadedEntry, configPanelEntry],
highlight: "Loaded",
},
];
const createEntityRegistryEntries = (
item: ConfigEntryExtended
): EntityRegistryEntry[] => [
{
config_entry_id: item.entry_id,
device_id: "mock-device-id",
area_id: null,
disabled_by: null,
entity_id: "binary_sensor.updater",
name: null,
icon: null,
platform: "updater",
},
];
const createDeviceRegistryEntries = (
item: ConfigEntryExtended
): DeviceRegistryEntry[] => [
{
entry_type: null,
config_entries: [item.entry_id],
connections: [],
manufacturer: "ESPHome",
model: "Mock Device",
name: "Tag Reader",
sw_version: null,
id: "mock-device-id",
identifiers: [],
via_device_id: null,
area_id: null,
name_by_user: null,
disabled_by: null,
},
];
@customElement("demo-integration-card")
export class DemoIntegrationCard extends LitElement {
@property({ attribute: false }) hass?: HomeAssistant;
@state() isCustomIntegration = false;
@state() isCloud = false;
protected render(): TemplateResult {
if (!this.hass) {
return html``;
}
return html`
<div class="container">
<div class="filters">
<ha-formfield label="Custom Integration">
<ha-switch @change=${this._toggleCustomIntegration}></ha-switch>
</ha-formfield>
<ha-formfield label="Relies on cloud">
<ha-switch @change=${this._toggleCloud}></ha-switch>
</ha-formfield>
</div>
<ha-ignored-config-entry-card
.hass=${this.hass}
.entry=${createConfigEntry("Ignored Entry")}
.manifest=${createManifest(this.isCustomIntegration, this.isCloud)}
></ha-ignored-config-entry-card>
${configFlows.map(
(flow) => html`
<ha-config-flow-card
.hass=${this.hass}
.flow=${flow}
.manifest=${createManifest(
this.isCustomIntegration,
this.isCloud,
flow.handler === "roku" ? "Roku" : "Philips Hue"
)}
></ha-config-flow-card>
`
)}
${configEntries.map(
(info) => html`
<ha-integration-card
class=${classMap({
highlight: info.highlight !== undefined,
})}
.hass=${this.hass}
domain="esphome"
.items=${info.items}
.manifest=${createManifest(
this.isCustomIntegration,
this.isCloud
)}
.entityRegistryEntries=${createEntityRegistryEntries(
info.items[0]
)}
.deviceRegistryEntries=${createDeviceRegistryEntries(
info.items[0]
)}
?disabled=${info.disabled}
.selectedConfigEntryId=${info.highlight}
></ha-integration-card>
`
)}
</div>
<div class="container">
<!-- One that is standalone to see how it increases height if height
not defined by other cards. -->
<ha-integration-card
.hass=${this.hass}
domain="esphome"
.items=${[
loadedEntry,
setupErrorEntry,
migrationErrorEntry,
setupRetryEntry,
failedUnloadEntry,
]}
.manifest=${createManifest(this.isCustomIntegration, this.isCloud)}
.entityRegistryEntries=${createEntityRegistryEntries(loadedEntry)}
.deviceRegistryEntries=${createDeviceRegistryEntries(loadedEntry)}
></ha-integration-card>
</div>
`;
}
protected firstUpdated(changedProps) {
super.firstUpdated(changedProps);
const hass = provideHass(this);
hass.updateTranslations(null, "en");
hass.updateTranslations("config", "en");
// Normally this string is loaded from backend
hass.addTranslations(
{
"component.esphome.config.error.connection_error":
"Can't connect to ESP. Please make sure your YAML file contains an 'api:' line.",
},
"en"
);
}
private _toggleCustomIntegration() {
this.isCustomIntegration = !this.isCustomIntegration;
}
private _toggleCloud() {
this.isCloud = !this.isCloud;
}
static get styles() {
return css`
.container {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
grid-gap: 16px 16px;
padding: 8px 16px 16px;
margin-bottom: 64px;
}
.container > * {
max-width: 500px;
}
ha-formfield {
margin: 8px 0;
display: block;
}
`;
}
}
declare global {
interface HTMLElementTagNameMap {
"demo-integration-card": DemoIntegrationCard;
}
}
+25 -92
View File
@@ -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 -2
View File
@@ -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`
@@ -123,7 +133,7 @@ class HassioAddonRepositoryEl extends LitElement {
navigate(this, `/hassio/addon/${ev.currentTarget.addon.slug}`);
}
static get styles(): CSSResultGroup {
static get styles(): CSSResultArray {
return [
hassioStyle,
css`
+14 -11
View File
@@ -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"
@@ -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,
@@ -5,13 +5,16 @@ import { mdiDotsVertical } from "@mdi/js";
import "@polymer/iron-autogrow-textarea/iron-autogrow-textarea";
import {
css,
CSSResultGroup,
CSSResult,
customElement,
html,
internalProperty,
LitElement,
property,
PropertyValues,
query,
TemplateResult,
} from "lit";
import { customElement, property, state, query } 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";
@@ -50,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 {
@@ -259,11 +266,6 @@ 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;
@@ -274,22 +276,26 @@ class HassioAddonConfig extends LitElement {
});
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,
+14 -11
View File
@@ -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,
@@ -169,9 +177,8 @@ class HassioAddonDashboard extends LitElement {
const requestedAddon = extractSearchParam("addon");
if (requestedAddon) {
const addonsInfo = await fetchHassioAddonsInfo(this.hass);
const validAddon = addonsInfo.addons.some(
(addon) => addon.slug === requestedAddon
);
const validAddon = addonsInfo.addons
.some((addon) => addon.slug === requestedAddon);
if (!validAddon) {
this._error = this.supervisor.localize("my.error_addon_not_found");
} else {
@@ -183,10 +190,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 -1
View File
@@ -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,
+34 -24
View File
@@ -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>
`
@@ -234,18 +242,14 @@ class HassioAddonInfo extends LitElement {
? html`
Current version: ${this.addon.version}
<div class="changelog" @click=${this._openChangelog}>
(<span class="changelog-link"
>${this.supervisor.localize(
"addon.dashboard.changelog"
)}</span
(<span class="changelog-link">${
this.supervisor.localize("addon.dashboard.changelog")}</span
>)
</div>
`
: html`<span class="changelog-link" @click=${this._openChangelog}
>${this.supervisor.localize(
"addon.dashboard.changelog"
)}</span
>`}
: html`<span class="changelog-link" @click=${this._openChangelog}>${
this.supervisor.localize("addon.dashboard.changelog")
}</span>`}
</div>
<div class="description light-color">
@@ -253,9 +257,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 +562,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) =>
@@ -983,7 +993,7 @@ class HassioAddonInfo extends LitElement {
addons: [this.addon.slug],
homeassistant: false,
},
updateHandler: async () => this._updateAddon(),
updateHandler: async () => await this._updateAddon(),
});
}
@@ -1090,7 +1100,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,
+13 -5
View File
@@ -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,
+10 -3
View File
@@ -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;
+15 -8
View File
@@ -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`
+18 -7
View File
@@ -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;
@@ -62,7 +73,7 @@ class SupervisorMetric extends LitElement {
);
}
.value {
width: 48px;
width: 42px;
padding-right: 4px;
}
`;
+10 -3
View File
@@ -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,
+10 -3
View File
@@ -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`
+18 -11
View File
@@ -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) {
@@ -226,7 +233,7 @@ export class HassioUpdate extends LitElement {
});
}
static get styles(): CSSResultGroup {
static get styles(): CSSResult[] {
return [
haStyle,
hassioStyle,
@@ -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,15 +39,15 @@ 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`
@@ -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,
@@ -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
@@ -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,
@@ -195,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 {
@@ -210,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,360 +0,0 @@
import "@material/mwc-button";
import "@polymer/paper-input/paper-input";
import type { PaperInputElement } from "@polymer/paper-input/paper-input";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property, state } from "lit/decorators";
import { formatDate } from "../../../../src/common/datetime/format_date";
import { fireEvent } from "../../../../src/common/dom/fire_event";
import { compare } from "../../../../src/common/string/compare";
import "../../../../src/components/buttons/ha-progress-button";
import "../../../../src/components/ha-checkbox";
import type { HaCheckbox } from "../../../../src/components/ha-checkbox";
import { createCloseHeading } from "../../../../src/components/ha-dialog";
import "../../../../src/components/ha-formfield";
import "../../../../src/components/ha-radio";
import type { HaRadio } from "../../../../src/components/ha-radio";
import "../../../../src/components/ha-settings-row";
import { extractApiErrorMessage } from "../../../../src/data/hassio/common";
import {
createHassioFullSnapshot,
createHassioPartialSnapshot,
HassioFullSnapshotCreateParams,
HassioPartialSnapshotCreateParams,
HassioSnapshot,
} from "../../../../src/data/hassio/snapshot";
import { showAlertDialog } from "../../../../src/dialogs/generic/show-dialog-box";
import { PolymerChangedEvent } from "../../../../src/polymer-types";
import { haStyle, haStyleDialog } from "../../../../src/resources/styles";
import { HomeAssistant } from "../../../../src/types";
import { HassioCreateSnapshotDialogParams } from "./show-dialog-hassio-create-snapshot";
interface CheckboxItem {
slug: string;
checked: boolean;
name?: string;
version?: string;
}
const folderList = () => [
{
slug: "homeassistant",
checked: true,
},
{ slug: "ssl", checked: true },
{ slug: "share", checked: true },
{ slug: "media", checked: true },
{ slug: "addons/local", checked: true },
];
@customElement("dialog-hassio-create-snapshot")
class HassioCreateSnapshotDialog extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@state() private _snapshotName = "";
@state() private _snapshotPassword = "";
@state() private _snapshotHasPassword = false;
@state() private _snapshotType: HassioSnapshot["type"] = "full";
@state() private _dialogParams?: HassioCreateSnapshotDialogParams;
@state() private _addonList: CheckboxItem[] = [];
@state() private _folderList: CheckboxItem[] = folderList();
@state() private _error = "";
public showDialog(params: HassioCreateSnapshotDialogParams) {
this._dialogParams = params;
this._addonList = this._dialogParams.supervisor.supervisor.addons
.map((addon) => ({
slug: addon.slug,
name: addon.name,
version: addon.version,
checked: true,
}))
.sort((a, b) => compare(a.name, b.name));
this._snapshotType = "full";
this._error = "";
this._folderList = folderList();
this._snapshotHasPassword = false;
this._snapshotPassword = "";
this._snapshotName = "";
}
public closeDialog() {
this._dialogParams = undefined;
fireEvent(this, "dialog-closed", { dialog: this.localName });
}
protected render(): TemplateResult {
if (!this._dialogParams) {
return html``;
}
return html`
<ha-dialog
open
@closing=${this.closeDialog}
.heading=${createCloseHeading(
this.hass,
this._dialogParams.supervisor.localize("snapshot.create_snapshot")
)}
>
<paper-input
name="snapshotName"
.label=${this._dialogParams.supervisor.localize("snapshot.name")}
.value=${this._snapshotName}
@value-changed=${this._handleTextValueChanged}
>
</paper-input>
<div class="snapshot-types">
<div>
${this._dialogParams.supervisor.localize("snapshot.type")}:
</div>
<ha-formfield
.label=${this._dialogParams.supervisor.localize(
"snapshot.full_snapshot"
)}
>
<ha-radio
@change=${this._handleRadioValueChanged}
value="full"
name="snapshotType"
.checked=${this._snapshotType === "full"}
>
</ha-radio>
</ha-formfield>
<ha-formfield
.label=${this._dialogParams.supervisor.localize(
"snapshot.partial_snapshot"
)}
>
<ha-radio
@change=${this._handleRadioValueChanged}
value="partial"
name="snapshotType"
.checked=${this._snapshotType === "partial"}
>
</ha-radio>
</ha-formfield>
</div>
${
this._snapshotType === "full"
? undefined
: html`
${this._dialogParams.supervisor.localize("snapshot.folders")}:
<div class="checkbox-section">
${this._folderList.map(
(folder, idx) => html`
<div class="checkbox-line">
<ha-checkbox
.idx=${idx}
.checked=${folder.checked}
@change=${this._folderChecked}
slot="prefix"
>
</ha-checkbox>
<span>
${this._dialogParams!.supervisor.localize(
`snapshot.folder.${folder.slug}`
)}
</span>
</div>
`
)}
</div>
${this._dialogParams.supervisor.localize("snapshot.addons")}:
<div class="checkbox-section">
${this._addonList.map(
(addon, idx) => html`
<div class="checkbox-line">
<ha-checkbox
.idx=${idx}
.checked=${addon.checked}
@change=${this._addonChecked}
slot="prefix"
>
</ha-checkbox>
<span>
${addon.name}<span class="version">
(${addon.version})
</span>
</span>
</div>
`
)}
</div>
`
}
${this._dialogParams.supervisor.localize("snapshot.security")}:
<div class="checkbox-section">
<div class="checkbox-line">
<ha-checkbox
.checked=${this._snapshotHasPassword}
@change=${this._handleCheckboxValueChanged}
slot="prefix"
>
</ha-checkbox>
<span>
${this._dialogParams.supervisor.localize(
"snapshot.password_protection"
)}
</span>
</span>
</div>
</div>
${
this._snapshotHasPassword
? html`
<paper-input
.label=${this._dialogParams.supervisor.localize(
"snapshot.password"
)}
type="password"
name="snapshotPassword"
.value=${this._snapshotPassword}
@value-changed=${this._handleTextValueChanged}
>
</paper-input>
`
: undefined
}
${
this._error !== ""
? html` <p class="error">${this._error}</p> `
: undefined
}
<mwc-button slot="secondaryAction" @click=${this.closeDialog}>
${this._dialogParams.supervisor.localize("common.close")}
</mwc-button>
<ha-progress-button slot="primaryAction" @click=${this._createSnapshot}>
${this._dialogParams.supervisor.localize("snapshot.create")}
</ha-progress-button>
</ha-dialog>
`;
}
private _handleTextValueChanged(ev: PolymerChangedEvent<string>) {
const input = ev.currentTarget as PaperInputElement;
this[`_${input.name}`] = ev.detail.value;
}
private _handleCheckboxValueChanged(ev: CustomEvent) {
const input = ev.currentTarget as HaCheckbox;
this._snapshotHasPassword = input.checked;
}
private _handleRadioValueChanged(ev: CustomEvent) {
const input = ev.currentTarget as HaRadio;
this[`_${input.name}`] = input.value;
}
private _folderChecked(ev) {
const { idx, checked } = ev.currentTarget!;
this._folderList = this._folderList.map((folder, curIdx) =>
curIdx === idx ? { ...folder, checked } : folder
);
}
private _addonChecked(ev) {
const { idx, checked } = ev.currentTarget!;
this._addonList = this._addonList.map((addon, curIdx) =>
curIdx === idx ? { ...addon, checked } : addon
);
}
private async _createSnapshot(ev: CustomEvent): 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 button = ev.currentTarget as any;
button.progress = true;
this._error = "";
if (this._snapshotHasPassword && !this._snapshotPassword.length) {
this._error = this._dialogParams!.supervisor.localize(
"snapshot.enter_password"
);
button.progress = false;
return;
}
const name = this._snapshotName || formatDate(new Date(), this.hass.locale);
try {
if (this._snapshotType === "full") {
const data: HassioFullSnapshotCreateParams = { name };
if (this._snapshotHasPassword) {
data.password = this._snapshotPassword;
}
await createHassioFullSnapshot(this.hass, data);
} else {
const data: HassioPartialSnapshotCreateParams = {
name,
folders: this._folderList
.filter((folder) => folder.checked)
.map((folder) => folder.slug),
addons: this._addonList
.filter((addon) => addon.checked)
.map((addon) => addon.slug),
};
if (this._snapshotHasPassword) {
data.password = this._snapshotPassword;
}
await createHassioPartialSnapshot(this.hass, data);
}
this._dialogParams!.onCreate();
this.closeDialog();
} catch (err) {
this._error = extractApiErrorMessage(err);
}
button.progress = false;
}
static get styles(): CSSResultGroup {
return [
haStyle,
haStyleDialog,
css`
.error {
color: var(--error-color);
}
paper-input[type="password"] {
display: block;
margin: 4px 0 4px 16px;
}
span.version {
color: var(--secondary-text-color);
}
.checkbox-section {
display: grid;
}
.checkbox-line {
display: inline-flex;
align-items: 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`
@@ -3,9 +3,16 @@ 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, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property, state } from "lit/decorators";
import { formatDateTime } from "../../../../src/common/datetime/format_date_time";
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 "../../../../src/components/ha-svg-icon";
@@ -46,13 +53,14 @@ const _computeFolders = (folders) => {
return list;
};
const _computeAddons = (addons) =>
addons.map((addon) => ({
const _computeAddons = (addons) => {
return addons.map((addon) => ({
slug: addon.slug,
name: addon.name,
version: addon.version,
checked: true,
}));
};
interface AddonItem {
slug: string;
@@ -73,21 +81,21 @@ class HassioSnapshotDialog extends LitElement {
@property({ attribute: false }) public supervisor?: Supervisor;
@state() private _error?: string;
@internalProperty() private _error?: string;
@state() private _onboarding = false;
@internalProperty() private _onboarding = false;
@state() private _snapshot?: HassioSnapshotDetail;
@internalProperty() private _snapshot?: HassioSnapshotDetail;
@state() private _folders!: FolderItem[];
@internalProperty() private _folders!: FolderItem[];
@state() private _addons!: AddonItem[];
@internalProperty() private _addons!: AddonItem[];
@state() private _dialogParams?: HassioSnapshotDialogParams;
@internalProperty() private _dialogParams?: HassioSnapshotDialogParams;
@state() private _snapshotPassword!: string;
@internalProperty() private _snapshotPassword!: string;
@state() private _restoreHass = true;
@internalProperty() private _restoreHass = true;
public async showDialog(params: HassioSnapshotDialogParams) {
this._snapshot = await fetchHassioSnapshotInfo(this.hass, params.slug);
@@ -114,7 +122,9 @@ class HassioSnapshotDialog extends LitElement {
<ha-dialog open @closing=${this._closeDialog} .heading=${true}>
<div slot="heading">
<ha-header-bar>
<span slot="title"> ${this._computeName} </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>
@@ -125,7 +135,7 @@ class HassioSnapshotDialog extends LitElement {
? "Full snapshot"
: "Partial snapshot"}
(${this._computeSize})<br />
${formatDateTime(new Date(this._snapshot.date), this.hass.locale)}
${this._formatDatetime(this._snapshot.date)}
</div>
${this._snapshot.homeassistant
? html`<div>Home Assistant:</div>
@@ -135,16 +145,15 @@ class HassioSnapshotDialog extends LitElement {
this._restoreHass = (ev.target as PaperCheckboxElement).checked!;
}}"
>
Home Assistant
<span class="version">(${this._snapshot.homeassistant})</span>
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) => html`
${this._folders.map((item) => {
return html`
<paper-checkbox
.checked=${item.checked}
@change="${(ev: Event) =>
@@ -155,8 +164,8 @@ class HassioSnapshotDialog extends LitElement {
>
${item.name}
</paper-checkbox>
`
)}
`;
})}
</paper-dialog-scrollable>
`
: ""}
@@ -164,8 +173,8 @@ class HassioSnapshotDialog extends LitElement {
? html`
<div>Add-on:</div>
<paper-dialog-scrollable class="no-margin-top">
${this._addons.map(
(item) => html`
${this._addons.map((item) => {
return html`
<paper-checkbox
.checked=${item.checked}
@change="${(ev: Event) =>
@@ -175,10 +184,9 @@ class HassioSnapshotDialog extends LitElement {
)}"
>
${item.name}
<span class="version">(${item.version})</span>
</paper-checkbox>
`
)}
`;
})}
</paper-dialog-scrollable>
`
: ""}
@@ -230,7 +238,7 @@ class HassioSnapshotDialog extends LitElement {
`;
}
static get styles(): CSSResultGroup {
static get styles(): CSSResult[] {
return [
haStyle,
haStyleDialog,
@@ -263,9 +271,6 @@ class HassioSnapshotDialog extends LitElement {
.no-margin-top {
margin-top: 0;
}
span.version {
color: var(--secondary-text-color);
}
ha-header-bar {
--mdc-theme-on-primary: var(--primary-text-color);
--mdc-theme-primary: var(--mdc-theme-surface);
@@ -497,6 +502,17 @@ class HassioSnapshotDialog extends LitElement {
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;
@@ -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 -1
View File
@@ -1,4 +1,4 @@
import type { LitElement } from "lit";
import type { LitElement } from "lit-element";
import {
HassioAddonDetails,
restartHassioAddon,
@@ -1,6 +1,13 @@
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";
@@ -20,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(
@@ -166,7 +173,7 @@ class DialogSupervisorUpdate extends LitElement {
this.closeDialog();
}
static get styles(): CSSResultGroup {
static get styles(): CSSResult[] {
return [
haStyle,
haStyleDialog,
+2 -17
View File
@@ -1,10 +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 { 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";
@@ -47,25 +44,13 @@ export class HassioMain extends SupervisorBaseElement {
// We changed the navigate event to fire directly on the window, as that's
// where we are listening for it. However, the older panel_custom will
// 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
top.addEventListener("location-changed", (ev) =>
window.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(document.body, href);
}
});
// Forward haptic events to parent window.
window.addEventListener("haptic", (ev) => {
// @ts-ignore
+9 -9
View File
@@ -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();
+1 -1
View File
@@ -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,
+10 -3
View File
@@ -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,
supervisorCollection,
@@ -39,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);
+3 -2
View File
@@ -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;
}
}
+7 -38
View File
@@ -1,26 +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 {
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";
@@ -30,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;
@@ -83,34 +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,
});
history.back();
return;
}
if (!addonInfo.ingress) {
await showAlertDialog(this, {
text: this.supervisor.localize("my.error_addon_no_ingress"),
title: addonInfo.name,
});
history.back();
} else {
navigate(this, `/hassio/ingress/${addonInfo.slug}`, true);
}
}
}
}
protected updated(changedProps: PropertyValues) {
super.updated(changedProps);
@@ -193,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 -1
View File
@@ -1,4 +1,4 @@
import { css } from "lit";
import { css } from "lit-element";
export const hassioStyle = css`
.content {
+368 -161
View File
@@ -1,163 +1,118 @@
import "@material/mwc-button";
import { ActionDetail } from "@material/mwc-list";
import "@material/mwc-icon-button";
import { ActionDetail } from "@material/mwc-list/mwc-list-foundation";
import "@material/mwc-list/mwc-list-item";
import { mdiDotsVertical, mdiPlus } from "@mdi/js";
import {
CSSResultGroup,
mdiDotsVertical,
mdiPackageVariant,
mdiPackageVariantClosed,
} 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 type { PaperInputElement } from "@polymer/paper-input/paper-input";
import "@polymer/paper-radio-button/paper-radio-button";
import "@polymer/paper-radio-group/paper-radio-group";
import type { PaperRadioGroupElement } from "@polymer/paper-radio-group/paper-radio-group";
import {
css,
CSSResultArray,
customElement,
html,
internalProperty,
LitElement,
property,
PropertyValues,
TemplateResult,
} from "lit";
import { customElement, property, state } from "lit/decorators";
import memoizeOne from "memoize-one";
} from "lit-element";
import { atLeastVersion } from "../../../src/common/config/version";
import relativeTime from "../../../src/common/datetime/relative_time";
import { HASSDomEvent } from "../../../src/common/dom/fire_event";
import {
DataTableColumnContainer,
RowClickedEvent,
} from "../../../src/components/data-table/ha-data-table";
import "../../../src/components/buttons/ha-progress-button";
import "../../../src/components/ha-button-menu";
import "../../../src/components/ha-fab";
import "../../../src/components/ha-card";
import "../../../src/components/ha-svg-icon";
import { extractApiErrorMessage } from "../../../src/data/hassio/common";
import {
createHassioFullSnapshot,
createHassioPartialSnapshot,
fetchHassioSnapshots,
friendlyFolderName,
HassioFullSnapshotCreateParams,
HassioPartialSnapshotCreateParams,
HassioSnapshot,
reloadHassioSnapshots,
} from "../../../src/data/hassio/snapshot";
import { Supervisor } from "../../../src/data/supervisor/supervisor";
import { showAlertDialog } from "../../../src/dialogs/generic/show-dialog-box";
import "../../../src/layouts/hass-tabs-subpage-data-table";
import "../../../src/layouts/hass-tabs-subpage";
import { PolymerChangedEvent } from "../../../src/polymer-types";
import { haStyle } from "../../../src/resources/styles";
import { HomeAssistant, Route } from "../../../src/types";
import { showHassioCreateSnapshotDialog } from "../dialogs/snapshot/show-dialog-hassio-create-snapshot";
import "../components/hassio-card-content";
import "../components/hassio-upload-snapshot";
import { showHassioSnapshotDialog } from "../dialogs/snapshot/show-dialog-hassio-snapshot";
import { showSnapshotUploadDialog } from "../dialogs/snapshot/show-dialog-snapshot-upload";
import { supervisorTabs } from "../hassio-tabs";
import { hassioStyle } from "../resources/hassio-style";
interface CheckboxItem {
slug: string;
checked: boolean;
name?: string;
}
@customElement("hassio-snapshots")
export class HassioSnapshots extends LitElement {
class HassioSnapshots extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public supervisor!: Supervisor;
@property({ type: Object }) public route!: Route;
@property({ type: Boolean }) public narrow!: boolean;
@property({ type: Boolean }) public isWide!: boolean;
@property({ attribute: false }) public route!: Route;
private _firstUpdatedCalled = false;
@property({ attribute: false }) public supervisor!: Supervisor;
@state() private _snapshots?: HassioSnapshot[] = [];
@internalProperty() private _snapshotName = "";
public connectedCallback(): void {
super.connectedCallback();
if (this.hass && this._firstUpdatedCalled) {
this.refreshData();
}
}
@internalProperty() private _snapshotPassword = "";
@internalProperty() private _snapshotHasPassword = false;
@internalProperty() private _snapshotType: HassioSnapshot["type"] = "full";
@internalProperty() private _snapshots?: HassioSnapshot[] = [];
@internalProperty() private _addonList: CheckboxItem[] = [];
@internalProperty() private _folderList: CheckboxItem[] = [
{
slug: "homeassistant",
checked: true,
},
{ slug: "ssl", checked: true },
{ slug: "share", checked: true },
{ slug: "media", checked: true },
{ slug: "addons/local", checked: true },
];
@internalProperty() private _error = "";
public async refreshData() {
await reloadHassioSnapshots(this.hass);
await this.fetchSnapshots();
await this._updateSnapshots();
}
private _computeSnapshotContent = (snapshot: HassioSnapshot): string => {
if (snapshot.type === "full") {
return this.supervisor.localize("snapshot.full_snapshot");
}
const content: string[] = [];
if (snapshot.content.homeassistant) {
content.push("Home Assistant");
}
if (snapshot.content.folders.length !== 0) {
for (const folder of snapshot.content.folders) {
content.push(friendlyFolderName[folder] || folder);
}
}
if (snapshot.content.addons.length !== 0) {
for (const addon of snapshot.content.addons) {
content.push(
this.supervisor.supervisor.addons.find(
(entry) => entry.slug === addon
)?.name || addon
);
}
}
return content.join(", ");
};
protected firstUpdated(changedProperties: PropertyValues): void {
super.firstUpdated(changedProperties);
if (this.hass && this.isConnected) {
this.refreshData();
}
this._firstUpdatedCalled = true;
}
private _columns = memoizeOne(
(narrow: boolean): DataTableColumnContainer => ({
name: {
title: this.supervisor?.localize("snapshot.name") || "",
sortable: true,
filterable: true,
grows: true,
template: (entry: string, snapshot: any) =>
html`${entry || snapshot.slug}
<div class="secondary">${snapshot.secondary}</div>`,
},
date: {
title: this.supervisor?.localize("snapshot.created") || "",
width: "15%",
direction: "desc",
hidden: narrow,
filterable: true,
sortable: true,
template: (entry: string) =>
relativeTime(new Date(entry), this.hass.localize),
},
secondary: {
title: "",
hidden: true,
filterable: true,
},
})
);
private _snapshotData = memoizeOne((snapshots: HassioSnapshot[]) =>
snapshots.map((snapshot) => ({
...snapshot,
secondary: this._computeSnapshotContent(snapshot),
}))
);
protected render(): TemplateResult {
if (!this.supervisor) {
return html``;
}
return html`
<hass-tabs-subpage-data-table
.tabs=${supervisorTabs}
<hass-tabs-subpage
.hass=${this.hass}
.localizeFunc=${this.supervisor.localize}
.searchLabel=${this.supervisor.localize("search")}
.noDataText=${this.supervisor.localize("snapshot.no_snapshots")}
.narrow=${this.narrow}
.route=${this.route}
.columns=${this._columns(this.narrow)}
.data=${this._snapshotData(this._snapshots || [])}
id="slug"
@row-click=${this._handleRowClicked}
clickable
hasFab
.tabs=${supervisorTabs}
main-page
supervisor
>
<span slot="header">
${this.supervisor.localize("panel.snapshots")}
</span>
<ha-button-menu
corner="BOTTOM_START"
slot="toolbar-icon"
@@ -167,27 +122,174 @@ export class HassioSnapshots extends LitElement {
<ha-svg-icon .path=${mdiDotsVertical}></ha-svg-icon>
</mwc-icon-button>
<mwc-list-item>
${this.supervisor?.localize("common.reload")}
${this.supervisor.localize("common.reload")}
</mwc-list-item>
${atLeastVersion(this.hass.config.version, 0, 116)
? html`<mwc-list-item>
${this.supervisor?.localize("snapshot.upload_snapshot")}
${this.supervisor.localize("snapshot.upload_snapshot")}
</mwc-list-item>`
: ""}
</ha-button-menu>
<ha-fab
slot="fab"
@click=${this._createSnapshot}
.label=${this.supervisor.localize("snapshot.create_snapshot")}
extended
>
<ha-svg-icon slot="icon" .path=${mdiPlus}></ha-svg-icon>
</ha-fab>
</hass-tabs-subpage-data-table>
<div class="content">
<h1>
${this.supervisor.localize("snapshot.create_snapshot")}
</h1>
<p class="description">
${this.supervisor.localize("snapshot.description")}
</p>
<div class="card-group">
<ha-card>
<div class="card-content">
<paper-input
autofocus
.label=${this.supervisor.localize("snapshot.name")}
name="snapshotName"
.value=${this._snapshotName}
@value-changed=${this._handleTextValueChanged}
></paper-input>
${this.supervisor.localize("snapshot.type")}:
<paper-radio-group
name="snapshotType"
type="${this.supervisor.localize("snapshot.type")}"
.selected=${this._snapshotType}
@selected-changed=${this._handleRadioValueChanged}
>
<paper-radio-button name="full">
${this.supervisor.localize("snapshot.full_snapshot")}
</paper-radio-button>
<paper-radio-button name="partial">
${this.supervisor.localize("snapshot.partial_snapshot")}
</paper-radio-button>
</paper-radio-group>
${this._snapshotType === "full"
? undefined
: html`
${this.supervisor.localize("snapshot.folders")}:
${this._folderList.map(
(folder, idx) => html`
<paper-checkbox
.idx=${idx}
.checked=${folder.checked}
@checked-changed=${this._folderChecked}
>
${this.supervisor.localize(
`snapshot.folder.${folder.slug}`
)}
</paper-checkbox>
`
)}
${this.supervisor.localize("snapshot.addons")}:
${this._addonList.map(
(addon, idx) => html`
<paper-checkbox
.idx=${idx}
.checked=${addon.checked}
@checked-changed=${this._addonChecked}
>
${addon.name}
</paper-checkbox>
`
)}
`}
${this.supervisor.localize("snapshot.security")}:
<paper-checkbox
name="snapshotHasPassword"
.checked=${this._snapshotHasPassword}
@checked-changed=${this._handleCheckboxValueChanged}
>
${this.supervisor.localize("snapshot.password_protection")}
</paper-checkbox>
${this._snapshotHasPassword
? html`
<paper-input
.label=${this.supervisor.localize("snapshot.password")}
type="password"
name="snapshotPassword"
.value=${this._snapshotPassword}
@value-changed=${this._handleTextValueChanged}
></paper-input>
`
: undefined}
${this._error !== ""
? html` <p class="error">${this._error}</p> `
: undefined}
</div>
<div class="card-actions">
<ha-progress-button
@click=${this._createSnapshot}
.title=${this.supervisor.info.state !== "running"
? this.supervisor.localize(
"snapshot.create_blocked_not_running",
"state",
this.supervisor.info.state
)
: ""}
.disabled=${this.supervisor.info.state !== "running"}
>
${this.supervisor.localize("snapshot.create")}
</ha-progress-button>
</div>
</ha-card>
</div>
<h1>${this.supervisor.localize("snapshot.available_snapshots")}</h1>
<div class="card-group">
${this._snapshots === undefined
? undefined
: this._snapshots.length === 0
? html`
<ha-card>
<div class="card-content">
${this.supervisor.localize("snapshot.no_snapshots")}
</div>
</ha-card>
`
: this._snapshots.map(
(snapshot) => html`
<ha-card
class="pointer"
.snapshot=${snapshot}
@click=${this._snapshotClicked}
>
<div class="card-content">
<hassio-card-content
.hass=${this.hass}
.title=${snapshot.name || snapshot.slug}
.description=${this._computeDetails(snapshot)}
.datetime=${snapshot.date}
.icon=${snapshot.type === "full"
? mdiPackageVariantClosed
: mdiPackageVariant}
icon-class="snapshot"
></hassio-card-content>
</div>
</ha-card>
`
)}
</div>
</div>
</hass-tabs-subpage>
`;
}
protected firstUpdated(changedProps: PropertyValues) {
super.firstUpdated(changedProps);
this.refreshData();
}
protected updated(changedProps: PropertyValues) {
if (changedProps.has("supervisor")) {
this._addonList = this.supervisor.supervisor.addons
.map((addon) => ({
slug: addon.slug,
name: addon.name,
checked: true,
}))
.sort((a, b) => (a.name < b.name ? -1 : 1));
}
}
private _handleAction(ev: CustomEvent<ActionDetail>) {
switch (ev.detail.index) {
case 0:
@@ -199,52 +301,157 @@ export class HassioSnapshots extends LitElement {
}
}
private _handleTextValueChanged(ev: PolymerChangedEvent<string>) {
const input = ev.currentTarget as PaperInputElement;
this[`_${input.name}`] = ev.detail.value;
}
private _handleCheckboxValueChanged(ev) {
const input = ev.currentTarget as PaperCheckboxElement;
this[`_${input.name}`] = input.checked;
}
private _handleRadioValueChanged(ev: PolymerChangedEvent<string>) {
const input = ev.currentTarget as PaperRadioGroupElement;
this[`_${input.getAttribute("name")}`] = ev.detail.value;
}
private _folderChecked(ev) {
const { idx, checked } = ev.currentTarget!;
this._folderList = this._folderList.map((folder, curIdx) =>
curIdx === idx ? { ...folder, checked } : folder
);
}
private _addonChecked(ev) {
const { idx, checked } = ev.currentTarget!;
this._addonList = this._addonList.map((addon, curIdx) =>
curIdx === idx ? { ...addon, checked } : addon
);
}
private async _updateSnapshots() {
try {
this._snapshots = await fetchHassioSnapshots(this.hass);
this._snapshots.sort((a, b) => (a.date < b.date ? 1 : -1));
} catch (err) {
this._error = extractApiErrorMessage(err);
}
}
private async _createSnapshot(ev: CustomEvent): Promise<void> {
if (this.supervisor.info.state !== "running") {
await showAlertDialog(this, {
title: this.supervisor.localize("snapshot.could_not_create"),
text: this.supervisor.localize(
"snapshot.create_blocked_not_running",
"state",
this.supervisor.info.state
),
});
}
const button = ev.currentTarget as any;
button.progress = true;
this._error = "";
if (this._snapshotHasPassword && !this._snapshotPassword.length) {
this._error = this.supervisor.localize("snapshot.enter_password");
button.progress = false;
return;
}
await this.updateComplete;
const name =
this._snapshotName ||
new Date().toLocaleDateString(navigator.language, {
weekday: "long",
year: "numeric",
month: "short",
day: "numeric",
});
try {
if (this._snapshotType === "full") {
const data: HassioFullSnapshotCreateParams = { name };
if (this._snapshotHasPassword) {
data.password = this._snapshotPassword;
}
await createHassioFullSnapshot(this.hass, data);
} else {
const addons = this._addonList
.filter((addon) => addon.checked)
.map((addon) => addon.slug);
const folders = this._folderList
.filter((folder) => folder.checked)
.map((folder) => folder.slug);
const data: HassioPartialSnapshotCreateParams = {
name,
folders,
addons,
};
if (this._snapshotHasPassword) {
data.password = this._snapshotPassword;
}
await createHassioPartialSnapshot(this.hass, data);
}
this._updateSnapshots();
} catch (err) {
this._error = extractApiErrorMessage(err);
}
button.progress = false;
}
private _computeDetails(snapshot: HassioSnapshot) {
const type =
snapshot.type === "full"
? this.supervisor.localize("snapshot.full_snapshot")
: this.supervisor.localize("snapshot.partial_snapshot");
return snapshot.protected ? `${type}, password protected` : type;
}
private _snapshotClicked(ev) {
showHassioSnapshotDialog(this, {
slug: ev.currentTarget!.snapshot.slug,
supervisor: this.supervisor,
onDelete: () => this._updateSnapshots(),
});
}
private _showUploadSnapshotDialog() {
showSnapshotUploadDialog(this, {
showSnapshot: (slug: string) =>
showHassioSnapshotDialog(this, {
slug,
supervisor: this.supervisor,
onDelete: () => this.fetchSnapshots(),
onDelete: () => this._updateSnapshots(),
}),
reloadSnapshot: () => this.refreshData(),
});
}
private async fetchSnapshots() {
await reloadHassioSnapshots(this.hass);
this._snapshots = await fetchHassioSnapshots(this.hass);
}
private _handleRowClicked(ev: HASSDomEvent<RowClickedEvent>) {
const slug = ev.detail.id;
showHassioSnapshotDialog(this, {
slug,
supervisor: this.supervisor,
onDelete: () => this.fetchSnapshots(),
});
}
private _createSnapshot() {
if (this.supervisor!.info.state !== "running") {
showAlertDialog(this, {
title: this.supervisor!.localize("snapshot.could_not_create"),
text: this.supervisor!.localize(
"snapshot.create_blocked_not_running",
"state",
this.supervisor!.info.state
),
});
return;
}
showHassioCreateSnapshotDialog(this, {
supervisor: this.supervisor!,
onCreate: () => this.fetchSnapshots(),
});
}
static get styles(): CSSResultGroup {
return [haStyle, hassioStyle];
static get styles(): CSSResultArray {
return [
haStyle,
hassioStyle,
css`
paper-radio-group {
display: block;
}
paper-radio-button {
padding: 0 0 2px 2px;
}
paper-radio-button,
paper-checkbox,
paper-input[type="password"] {
display: block;
margin: 4px 0 4px 48px;
}
.pointer {
cursor: pointer;
}
`,
];
}
}
+12 -5
View File
@@ -1,6 +1,10 @@
import { Collection, UnsubscribeFunc } from "home-assistant-js-websocket";
import { LitElement, PropertyValues } from "lit";
import { property, state } from "lit/decorators";
import {
internalProperty,
LitElement,
property,
PropertyValues,
} from "lit-element";
import { atLeastVersion } from "../../src/common/config/version";
import { computeLocalize } from "../../src/common/translations/localize";
import { fetchHassioAddonsInfo } from "../../src/data/hassio/addon";
@@ -42,11 +46,14 @@ export class SupervisorBaseElement extends urlSyncMixin(
localize: () => "",
};
@state() private _unsubs: Record<string, UnsubscribeFunc> = {};
@internalProperty() private _unsubs: Record<string, UnsubscribeFunc> = {};
@state() private _collections: Record<string, Collection<unknown>> = {};
@internalProperty() private _collections: Record<
string,
Collection<unknown>
> = {};
@state() private _language = "en";
@internalProperty() private _language = "en";
public connectedCallback(): void {
super.connectedCallback();
+13 -5
View File
@@ -1,7 +1,15 @@
import "@material/mwc-button";
import "@material/mwc-list/mwc-list-item";
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/buttons/ha-progress-button";
import "../../../src/components/ha-button-menu";
@@ -31,7 +39,7 @@ class HassioCoreInfo extends LitElement {
@property({ attribute: false }) public supervisor!: Supervisor;
@state() private _metrics?: HassioStats;
@internalProperty() private _metrics?: HassioStats;
protected render(): TemplateResult | void {
const metrics = [
@@ -170,7 +178,7 @@ class HassioCoreInfo extends LitElement {
folders: ["homeassistant"],
homeassistant: true,
},
updateHandler: async () => this._updateCore(),
updateHandler: async () => await this._updateCore(),
});
}
@@ -181,7 +189,7 @@ class HassioCoreInfo extends LitElement {
});
}
static get styles(): CSSResultGroup {
static get styles(): CSSResult[] {
return [
haStyle,
hassioStyle,
+13 -4
View File
@@ -3,8 +3,15 @@ import { ActionDetail } from "@material/mwc-list/mwc-list-foundation";
import "@material/mwc-list/mwc-list-item";
import { mdiDotsVertical } from "@mdi/js";
import { safeDump } from "js-yaml";
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";
@@ -90,7 +97,9 @@ class HassioHostInfo extends LitElement {
<span slot="heading">
${this.supervisor.localize("system.host.ip_address")}
</span>
<span slot="description"> ${primaryIpAddress} </span>
<span slot="description">
${primaryIpAddress}
</span>
<mwc-button
.label=${this.supervisor.localize("system.host.change")}
@click=${this._changeNetworkClicked}
@@ -408,7 +417,7 @@ class HassioHostInfo extends LitElement {
}
}
static get styles(): CSSResultGroup {
static get styles(): CSSResult[] {
return [
haStyle,
hassioStyle,
+44 -40
View File
@@ -1,6 +1,13 @@
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property, state } from "lit/decorators";
import { atLeastVersion } from "../../../src/common/config/version";
import {
css,
CSSResult,
customElement,
html,
internalProperty,
LitElement,
property,
TemplateResult,
} from "lit-element";
import { fireEvent } from "../../../src/common/dom/fire_event";
import "../../../src/components/buttons/ha-progress-button";
import "../../../src/components/ha-card";
@@ -31,7 +38,6 @@ import "../components/supervisor-metric";
import { hassioStyle } from "../resources/hassio-style";
const UNSUPPORTED_REASON_URL = {
apparmor: "/more-info/unsupported/apparmor",
container: "/more-info/unsupported/container",
dbus: "/more-info/unsupported/dbus",
docker_configuration: "/more-info/unsupported/docker_configuration",
@@ -59,7 +65,7 @@ class HassioSupervisorInfo extends LitElement {
@property({ attribute: false }) public supervisor!: Supervisor;
@state() private _metrics?: HassioStats;
@internalProperty() private _metrics?: HassioStats;
protected render(): TemplateResult | void {
const metrics = [
@@ -144,32 +150,30 @@ class HassioSupervisorInfo extends LitElement {
</ha-settings-row>
${this.supervisor.supervisor.supported
? !atLeastVersion(this.hass.config.version, 2021, 4)
? html` <ha-settings-row three-line>
<span slot="heading">
${this.supervisor.localize(
"system.supervisor.share_diagnostics"
)}
</span>
<div slot="description" class="diagnostics-description">
${this.supervisor.localize(
"system.supervisor.share_diagnostics_description"
)}
<button
class="link"
.title=${this.supervisor.localize("common.show_more")}
@click=${this._diagnosticsInformationDialog}
>
${this.supervisor.localize("common.learn_more")}
</button>
</div>
<ha-switch
haptic
.checked=${this.supervisor.supervisor.diagnostics}
@change=${this._toggleDiagnostics}
></ha-switch>
</ha-settings-row>`
: ""
? html` <ha-settings-row three-line>
<span slot="heading">
${this.supervisor.localize(
"system.supervisor.share_diagnostics"
)}
</span>
<div slot="description" class="diagnostics-description">
${this.supervisor.localize(
"system.supervisor.share_diagnostics_description"
)}
<button
class="link"
.title=${this.supervisor.localize("common.show_more")}
@click=${this._diagnosticsInformationDialog}
>
${this.supervisor.localize("common.learn_more")}
</button>
</div>
<ha-switch
haptic
.checked=${this.supervisor.supervisor.diagnostics}
@change=${this._toggleDiagnostics}
></ha-switch>
</ha-settings-row>`
: html`<div class="error">
${this.supervisor.localize(
"system.supervisor.unsupported_title"
@@ -256,18 +260,18 @@ class HassioSupervisorInfo extends LitElement {
title: this.supervisor.localize("system.supervisor.warning"),
text: html`${this.supervisor.localize("system.supervisor.beta_warning")}
<br />
<b> ${this.supervisor.localize("system.supervisor.beta_backup")} </b>
<b>
${this.supervisor.localize("system.supervisor.beta_backup")}
</b>
<br /><br />
${this.supervisor.localize("system.supervisor.beta_release_items")}
<ul>
<li>Home Assistant Core</li>
<li>Home Assistant Supervisor</li>
<li>Home Assistant Operating System</li>
</ul>
<li>Home Assistant Core</li>
<li>Home Assistant Supervisor</li>
<li>Home Assistant Operating System</li>
<br />
${this.supervisor.localize("system.supervisor.beta_join_confirm")}`,
${this.supervisor.localize("system.supervisor.join_beta_action")}`,
confirmText: this.supervisor.localize(
"system.supervisor.join_beta_action"
"system.supervisor.beta_join_confirm"
),
dismissText: this.supervisor.localize("common.cancel"),
});
@@ -496,7 +500,7 @@ class HassioSupervisorInfo extends LitElement {
}
}
static get styles(): CSSResultGroup {
static get styles(): CSSResult[] {
return [
haStyle,
hassioStyle,

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