mirror of
https://github.com/home-assistant/frontend.git
synced 2025-09-14 23:49:44 +00:00
Compare commits
30 Commits
dashboard-
...
onboarding
Author | SHA1 | Date | |
---|---|---|---|
![]() |
c0a5c6fa61 | ||
![]() |
fe91dbb139 | ||
![]() |
a8e17da9f3 | ||
![]() |
e8f1a86005 | ||
![]() |
45b04a6188 | ||
![]() |
034ce56da5 | ||
![]() |
ae9fcebfd5 | ||
![]() |
6197b55da8 | ||
![]() |
4e5d57b5f3 | ||
![]() |
7040c6d469 | ||
![]() |
6f99a39b55 | ||
![]() |
7483833dcd | ||
![]() |
38fb48b231 | ||
![]() |
166acee1c6 | ||
![]() |
916a6df39b | ||
![]() |
70f37158fb | ||
![]() |
5011bba20e | ||
![]() |
8897bc703d | ||
![]() |
ea6e7d441a | ||
![]() |
f91396c986 | ||
![]() |
4598b530af | ||
![]() |
dfabb4bc36 | ||
![]() |
66e0100c95 | ||
![]() |
a08a989ef5 | ||
![]() |
000c28abf9 | ||
![]() |
6b67397c83 | ||
![]() |
f68823a09e | ||
![]() |
fc1782e676 | ||
![]() |
b4975344a1 | ||
![]() |
2dc08d782f |
File diff suppressed because one or more lines are too long
@@ -8,4 +8,4 @@ plugins:
|
||||
- path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
|
||||
spec: "@yarnpkg/plugin-interactive-tools"
|
||||
|
||||
yarnPath: .yarn/releases/yarn-3.6.2.cjs
|
||||
yarnPath: .yarn/releases/yarn-3.6.3.cjs
|
||||
|
@@ -0,0 +1,3 @@
|
||||
---
|
||||
title: Control Number Buttons
|
||||
---
|
100
gallery/src/pages/components/ha-control-number-buttons.ts
Normal file
100
gallery/src/pages/components/ha-control-number-buttons.ts
Normal file
@@ -0,0 +1,100 @@
|
||||
import { LitElement, TemplateResult, css, html } from "lit";
|
||||
import { customElement, state } from "lit/decorators";
|
||||
import "../../../../src/components/ha-card";
|
||||
import "../../../../src/components/ha-control-number-buttons";
|
||||
import { repeat } from "lit/directives/repeat";
|
||||
import { ifDefined } from "lit/directives/if-defined";
|
||||
|
||||
const buttons: {
|
||||
id: string;
|
||||
label: string;
|
||||
min?: number;
|
||||
max?: number;
|
||||
step?: number;
|
||||
class?: string;
|
||||
}[] = [
|
||||
{
|
||||
id: "basic",
|
||||
label: "Basic",
|
||||
},
|
||||
{
|
||||
id: "min_max_step",
|
||||
label: "With min/max and step",
|
||||
min: 5,
|
||||
max: 25,
|
||||
step: 0.5,
|
||||
},
|
||||
{
|
||||
id: "custom",
|
||||
label: "Custom",
|
||||
class: "custom",
|
||||
},
|
||||
];
|
||||
|
||||
@customElement("demo-components-ha-control-number-buttons")
|
||||
export class DemoHarControlNumberButtons extends LitElement {
|
||||
@state() value = 5;
|
||||
|
||||
private _valueChanged(ev) {
|
||||
this.value = ev.detail.value;
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`
|
||||
${repeat(buttons, (button) => {
|
||||
const { id, label, ...config } = button;
|
||||
return html`
|
||||
<ha-card>
|
||||
<div class="card-content">
|
||||
<label id=${id}>${label}</label>
|
||||
<pre>Config: ${JSON.stringify(config)}</pre>
|
||||
<ha-control-number-buttons
|
||||
.value=${this.value}
|
||||
.min=${config.min}
|
||||
.max=${config.max}
|
||||
.step=${config.step}
|
||||
class=${ifDefined(config.class)}
|
||||
@value-changed=${this._valueChanged}
|
||||
.label=${label}
|
||||
>
|
||||
</ha-control-number-buttons>
|
||||
</div>
|
||||
</ha-card>
|
||||
`;
|
||||
})}
|
||||
`;
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
return css`
|
||||
ha-card {
|
||||
max-width: 600px;
|
||||
margin: 24px auto;
|
||||
}
|
||||
pre {
|
||||
margin-top: 0;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
label {
|
||||
font-weight: 600;
|
||||
}
|
||||
.custom {
|
||||
color: #2196f3;
|
||||
--control-number-buttons-color: #2196f3;
|
||||
--control-number-buttons-background-color: #2196f3;
|
||||
--control-number-buttons-background-opacity: 0.1;
|
||||
--control-number-buttons-thickness: 100px;
|
||||
--control-number-buttons-border-radius: 24px;
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"demo-components-ha-control-number-buttons": DemoHarControlNumberButtons;
|
||||
}
|
||||
}
|
@@ -173,6 +173,7 @@ class HassioBackupDialog
|
||||
private async _restoreClicked() {
|
||||
const backupDetails = this._backupContent.backupDetails();
|
||||
this._restoringBackup = true;
|
||||
this._dialogParams?.onRestoring?.();
|
||||
if (this._backupContent.backupType === "full") {
|
||||
await this._fullRestoreClicked(backupDetails);
|
||||
} else {
|
||||
@@ -219,7 +220,7 @@ class HassioBackupDialog
|
||||
this._error = error.body.message;
|
||||
}
|
||||
} else {
|
||||
fireEvent(this, "restoring");
|
||||
this._dialogParams?.onRestoring?.();
|
||||
await fetch(`/api/hassio/backups/${this._backup!.slug}/restore/partial`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify(backupDetails),
|
||||
@@ -268,7 +269,7 @@ class HassioBackupDialog
|
||||
}
|
||||
);
|
||||
} else {
|
||||
fireEvent(this, "restoring");
|
||||
this._dialogParams?.onRestoring?.();
|
||||
fetch(`/api/hassio/backups/${this._backup!.slug}/restore/full`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify(backupDetails),
|
||||
|
@@ -5,6 +5,7 @@ import { Supervisor } from "../../../../src/data/supervisor/supervisor";
|
||||
export interface HassioBackupDialogParams {
|
||||
slug: string;
|
||||
onDelete?: () => void;
|
||||
onRestoring?: () => void;
|
||||
onboarding?: boolean;
|
||||
supervisor?: Supervisor;
|
||||
localize?: LocalizeFunc;
|
||||
|
32
package.json
32
package.json
@@ -25,7 +25,7 @@
|
||||
"license": "Apache-2.0",
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.22.10",
|
||||
"@babel/runtime": "7.22.11",
|
||||
"@braintree/sanitize-url": "6.0.4",
|
||||
"@codemirror/autocomplete": "6.9.0",
|
||||
"@codemirror/commands": "6.2.4",
|
||||
@@ -52,7 +52,7 @@
|
||||
"@lezer/highlight": "1.1.6",
|
||||
"@lit-labs/context": "0.4.0",
|
||||
"@lit-labs/motion": "1.0.4",
|
||||
"@lit-labs/virtualizer": "2.0.5",
|
||||
"@lit-labs/virtualizer": "2.0.6",
|
||||
"@lrnwebcomponents/simple-tooltip": "7.0.16",
|
||||
"@material/chips": "=14.0.0-canary.53b3cad2f.0",
|
||||
"@material/data-table": "=14.0.0-canary.53b3cad2f.0",
|
||||
@@ -79,7 +79,7 @@
|
||||
"@material/mwc-top-app-bar": "0.27.0",
|
||||
"@material/mwc-top-app-bar-fixed": "0.27.0",
|
||||
"@material/top-app-bar": "=14.0.0-canary.53b3cad2f.0",
|
||||
"@material/web": "=1.0.0-pre.15",
|
||||
"@material/web": "=1.0.0-pre.16",
|
||||
"@mdi/js": "7.2.96",
|
||||
"@mdi/svg": "7.2.96",
|
||||
"@polymer/app-layout": "3.1.0",
|
||||
@@ -94,8 +94,8 @@
|
||||
"@polymer/paper-toast": "3.0.1",
|
||||
"@polymer/polymer": "3.5.1",
|
||||
"@thomasloven/round-slider": "0.6.0",
|
||||
"@vaadin/combo-box": "24.1.5",
|
||||
"@vaadin/vaadin-themable-mixin": "24.1.5",
|
||||
"@vaadin/combo-box": "24.1.6",
|
||||
"@vaadin/vaadin-themable-mixin": "24.1.6",
|
||||
"@vibrant/color": "3.2.1-alpha.1",
|
||||
"@vibrant/core": "3.2.1-alpha.1",
|
||||
"@vibrant/quantizer-mmcq": "3.2.1-alpha.1",
|
||||
@@ -121,7 +121,7 @@
|
||||
"leaflet": "1.9.4",
|
||||
"leaflet-draw": "1.0.4",
|
||||
"lit": "2.8.0",
|
||||
"luxon": "3.4.0",
|
||||
"luxon": "3.4.2",
|
||||
"marked": "7.0.4",
|
||||
"memoize-one": "6.0.0",
|
||||
"node-vibrant": "3.2.1-alpha.1",
|
||||
@@ -154,11 +154,11 @@
|
||||
"xss": "1.0.14"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "7.22.10",
|
||||
"@babel/core": "7.22.11",
|
||||
"@babel/plugin-proposal-decorators": "7.22.10",
|
||||
"@babel/plugin-transform-runtime": "7.22.10",
|
||||
"@babel/preset-env": "7.22.10",
|
||||
"@babel/preset-typescript": "7.22.5",
|
||||
"@babel/preset-typescript": "7.22.11",
|
||||
"@koa/cors": "4.0.0",
|
||||
"@octokit/auth-oauth-device": "6.0.0",
|
||||
"@octokit/plugin-retry": "6.0.0",
|
||||
@@ -167,7 +167,7 @@
|
||||
"@rollup/plugin-babel": "6.0.3",
|
||||
"@rollup/plugin-commonjs": "25.0.4",
|
||||
"@rollup/plugin-json": "6.0.0",
|
||||
"@rollup/plugin-node-resolve": "15.2.0",
|
||||
"@rollup/plugin-node-resolve": "15.2.1",
|
||||
"@rollup/plugin-replace": "5.0.2",
|
||||
"@types/babel__plugin-transform-runtime": "7.9.2",
|
||||
"@types/chromecast-caf-receiver": "6.0.9",
|
||||
@@ -186,13 +186,13 @@
|
||||
"@types/tar": "6.1.5",
|
||||
"@types/ua-parser-js": "0.7.36",
|
||||
"@types/webspeechapi": "0.0.29",
|
||||
"@typescript-eslint/eslint-plugin": "6.4.0",
|
||||
"@typescript-eslint/parser": "6.4.0",
|
||||
"@typescript-eslint/eslint-plugin": "6.4.1",
|
||||
"@typescript-eslint/parser": "6.4.1",
|
||||
"@web/dev-server": "0.1.38",
|
||||
"@web/dev-server-rollup": "0.4.1",
|
||||
"babel-loader": "9.1.3",
|
||||
"babel-plugin-template-html-minifier": "4.1.0",
|
||||
"chai": "4.3.7",
|
||||
"chai": "4.3.8",
|
||||
"del": "7.0.0",
|
||||
"eslint": "8.47.0",
|
||||
"eslint-config-airbnb-base": "15.0.0",
|
||||
@@ -219,10 +219,10 @@
|
||||
"husky": "8.0.3",
|
||||
"instant-mocha": "1.5.2",
|
||||
"jszip": "3.10.1",
|
||||
"lint-staged": "14.0.0",
|
||||
"lint-staged": "14.0.1",
|
||||
"lit-analyzer": "2.0.0-pre.3",
|
||||
"lodash.template": "4.5.0",
|
||||
"magic-string": "0.30.2",
|
||||
"magic-string": "0.30.3",
|
||||
"map-stream": "0.0.7",
|
||||
"mocha": "10.2.0",
|
||||
"object-hash": "3.0.0",
|
||||
@@ -240,7 +240,7 @@
|
||||
"tar": "6.1.15",
|
||||
"terser-webpack-plugin": "5.3.9",
|
||||
"ts-lit-plugin": "2.0.0-pre.1",
|
||||
"typescript": "5.1.6",
|
||||
"typescript": "5.2.2",
|
||||
"vinyl-buffer": "1.0.1",
|
||||
"vinyl-source-stream": "2.0.0",
|
||||
"webpack": "5.88.2",
|
||||
@@ -257,5 +257,5 @@
|
||||
"sortablejs@1.15.0": "patch:sortablejs@npm%3A1.15.0#./.yarn/patches/sortablejs-npm-1.15.0-f3a393abcc.patch",
|
||||
"leaflet-draw@1.0.4": "patch:leaflet-draw@npm%3A1.0.4#./.yarn/patches/leaflet-draw-npm-1.0.4-0ca0ebcf65.patch"
|
||||
},
|
||||
"packageManager": "yarn@3.6.2"
|
||||
"packageManager": "yarn@3.6.3"
|
||||
}
|
||||
|
BIN
public/static/images/logo_discord.png
Normal file
BIN
public/static/images/logo_discord.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.3 KiB |
BIN
public/static/images/logo_twitter.png
Normal file
BIN
public/static/images/logo_twitter.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.2 KiB |
@@ -35,6 +35,8 @@ export class HaAuthorize extends litLocalizeLiteMixin(LitElement) {
|
||||
|
||||
@property() public oauth2State?: string;
|
||||
|
||||
@property() public translationFragment = "page-authorize";
|
||||
|
||||
@state() private _authProvider?: AuthProvider;
|
||||
|
||||
@state() private _authProviders?: AuthProvider[];
|
||||
@@ -45,7 +47,6 @@ export class HaAuthorize extends litLocalizeLiteMixin(LitElement) {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.translationFragment = "page-authorize";
|
||||
const query = extractSearchParamsObject() as AuthUrlSearchParams;
|
||||
if (query.client_id) {
|
||||
this.clientId = query.client_id;
|
||||
@@ -102,7 +103,6 @@ export class HaAuthorize extends litLocalizeLiteMixin(LitElement) {
|
||||
: nothing}
|
||||
|
||||
<ha-auth-flow
|
||||
.resources=${this.resources}
|
||||
.clientId=${this.clientId}
|
||||
.redirectUri=${this.redirectUri}
|
||||
.oauth2State=${this.oauth2State}
|
||||
|
@@ -4,7 +4,7 @@ export const clamp = (value: number, min: number, max: number) =>
|
||||
// Variant that only applies the clamping to a border if the border is defined
|
||||
export const conditionalClamp = (value: number, min?: number, max?: number) => {
|
||||
let result: number;
|
||||
result = min ? Math.max(value, min) : value;
|
||||
result = max ? Math.min(result, max) : result;
|
||||
result = min != null ? Math.max(value, min) : value;
|
||||
result = max != null ? Math.min(result, max) : result;
|
||||
return result;
|
||||
};
|
||||
|
@@ -324,6 +324,7 @@ export class HaDevicePicker extends SubscribeMixin(LitElement) {
|
||||
.renderer=${rowRenderer}
|
||||
.disabled=${this.disabled}
|
||||
.required=${this.required}
|
||||
item-id-path="id"
|
||||
item-value-path="id"
|
||||
item-label-path="name"
|
||||
@opened-changed=${this._openedChanged}
|
||||
|
@@ -312,6 +312,10 @@ export class HaComboBox extends LitElement {
|
||||
|
||||
private _valueChanged(ev: ComboBoxLightValueChangedEvent) {
|
||||
ev.stopPropagation();
|
||||
if (!this.allowCustomValue) {
|
||||
// @ts-ignore
|
||||
this._comboBox._closeOnBlurIsPrevented = true;
|
||||
}
|
||||
const newValue = ev.detail.value;
|
||||
|
||||
if (newValue !== this.value) {
|
||||
|
258
src/components/ha-control-number-buttons.ts
Normal file
258
src/components/ha-control-number-buttons.ts
Normal file
@@ -0,0 +1,258 @@
|
||||
import { mdiMinus, mdiPlus } from "@mdi/js";
|
||||
import { CSSResultGroup, LitElement, TemplateResult, css, html } from "lit";
|
||||
import { customElement, property, query } from "lit/decorators";
|
||||
import { ifDefined } from "lit/directives/if-defined";
|
||||
import { conditionalClamp } from "../common/number/clamp";
|
||||
import { formatNumber } from "../common/number/format_number";
|
||||
import { FrontendLocaleData } from "../data/translation";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
|
||||
const A11Y_KEY_CODES = new Set([
|
||||
"ArrowRight",
|
||||
"ArrowUp",
|
||||
"ArrowLeft",
|
||||
"ArrowDown",
|
||||
"PageUp",
|
||||
"PageDown",
|
||||
"Home",
|
||||
"End",
|
||||
]);
|
||||
|
||||
@customElement("ha-control-number-buttons")
|
||||
export class HaControlNumberButton extends LitElement {
|
||||
@property({ attribute: false }) public locale?: FrontendLocaleData;
|
||||
|
||||
@property({ type: Boolean, reflect: true }) disabled = false;
|
||||
|
||||
@property() public label?: string;
|
||||
|
||||
@property({ type: Number }) public step?: number;
|
||||
|
||||
@property({ type: Number }) public value?: number;
|
||||
|
||||
@property({ type: Number }) public min?: number;
|
||||
|
||||
@property({ type: Number }) public max?: number;
|
||||
|
||||
@property({ attribute: "false" })
|
||||
public formatOptions: Intl.NumberFormatOptions = {};
|
||||
|
||||
@query("#input") _input!: HTMLDivElement;
|
||||
|
||||
private boundedValue(value: number) {
|
||||
const clamped = conditionalClamp(value, this.min, this.max);
|
||||
return Math.round(clamped / this._step) * this._step;
|
||||
}
|
||||
|
||||
private get _step() {
|
||||
return this.step ?? 1;
|
||||
}
|
||||
|
||||
private get _value() {
|
||||
return this.value ?? 0;
|
||||
}
|
||||
|
||||
private get _tenPercentStep() {
|
||||
if (this.max == null || this.min == null) return this._step;
|
||||
const range = this.max - this.min / 10;
|
||||
|
||||
if (range <= this._step) return this._step;
|
||||
return Math.max(range / 10);
|
||||
}
|
||||
|
||||
private _handlePlusButton() {
|
||||
this._increment();
|
||||
fireEvent(this, "value-changed", { value: this.value });
|
||||
this._input.focus();
|
||||
}
|
||||
|
||||
private _handleMinusButton() {
|
||||
this._decrement();
|
||||
fireEvent(this, "value-changed", { value: this.value });
|
||||
this._input.focus();
|
||||
}
|
||||
|
||||
private _increment() {
|
||||
this.value = this.boundedValue(this._value + this._step);
|
||||
}
|
||||
|
||||
private _decrement() {
|
||||
this.value = this.boundedValue(this._value - this._step);
|
||||
}
|
||||
|
||||
_handleKeyDown(e: KeyboardEvent) {
|
||||
if (!A11Y_KEY_CODES.has(e.code)) return;
|
||||
e.preventDefault();
|
||||
switch (e.code) {
|
||||
case "ArrowRight":
|
||||
case "ArrowUp":
|
||||
this._increment();
|
||||
break;
|
||||
case "ArrowLeft":
|
||||
case "ArrowDown":
|
||||
this._decrement();
|
||||
break;
|
||||
case "PageUp":
|
||||
this.value = this.boundedValue(this._value + this._tenPercentStep);
|
||||
break;
|
||||
case "PageDown":
|
||||
this.value = this.boundedValue(this._value - this._tenPercentStep);
|
||||
break;
|
||||
case "Home":
|
||||
if (this.min != null) {
|
||||
this.value = this.min;
|
||||
}
|
||||
break;
|
||||
case "End":
|
||||
if (this.max != null) {
|
||||
this.value = this.max;
|
||||
}
|
||||
break;
|
||||
}
|
||||
fireEvent(this, "value-changed", { value: this.value });
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
const displayedValue =
|
||||
this.value != null
|
||||
? formatNumber(this.value, this.locale, this.formatOptions)
|
||||
: "-";
|
||||
|
||||
return html`
|
||||
<div class="container">
|
||||
<div
|
||||
id="input"
|
||||
class="value"
|
||||
role="number-button"
|
||||
tabindex="0"
|
||||
aria-valuenow=${this.value}
|
||||
aria-valuemin=${this.min}
|
||||
aria-valuemax=${this.max}
|
||||
aria-label=${ifDefined(this.label)}
|
||||
.disabled=${this.disabled}
|
||||
@keydown=${this._handleKeyDown}
|
||||
>
|
||||
${displayedValue}
|
||||
</div>
|
||||
<button
|
||||
class="button minus"
|
||||
type="button"
|
||||
tabindex="-1"
|
||||
aria-label="decrement"
|
||||
@click=${this._handleMinusButton}
|
||||
.disabled=${this.disabled ||
|
||||
(this.min != null && this._value <= this.min)}
|
||||
>
|
||||
<ha-svg-icon aria-hidden .path=${mdiMinus}></ha-svg-icon>
|
||||
</button>
|
||||
<button
|
||||
class="button plus"
|
||||
type="button"
|
||||
tabindex="-1"
|
||||
aria-label="increment"
|
||||
@click=${this._handlePlusButton}
|
||||
.disabled=${this.disabled ||
|
||||
(this.max != null && this._value >= this.max)}
|
||||
>
|
||||
<ha-svg-icon aria-hidden .path=${mdiPlus}></ha-svg-icon>
|
||||
</button>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return css`
|
||||
:host {
|
||||
display: block;
|
||||
--control-number-buttons-focus-color: var(--primary-color);
|
||||
--control-number-buttons-background-color: var(--disabled-color);
|
||||
--control-number-buttons-background-opacity: 0.2;
|
||||
--control-number-buttons-border-radius: 10px;
|
||||
--mdc-icon-size: 16px;
|
||||
height: 40px;
|
||||
width: 200px;
|
||||
color: var(--primary-text-color);
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
transition: color 180ms ease-in-out;
|
||||
}
|
||||
:host([disabled]) {
|
||||
color: var(--disabled-color);
|
||||
}
|
||||
.container {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.value {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 0 44px;
|
||||
border-radius: var(--control-number-buttons-border-radius);
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
box-sizing: border-box;
|
||||
line-height: 0;
|
||||
overflow: hidden;
|
||||
/* For safari border-radius overflow */
|
||||
z-index: 0;
|
||||
font-size: inherit;
|
||||
color: inherit;
|
||||
user-select: none;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
outline: none;
|
||||
}
|
||||
.value::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
background-color: var(--control-number-buttons-background-color);
|
||||
transition:
|
||||
background-color 180ms ease-in-out,
|
||||
opacity 180ms ease-in-out;
|
||||
opacity: var(--control-number-buttons-background-opacity);
|
||||
}
|
||||
.value:focus-visible {
|
||||
box-shadow: 0 0 0 2px var(--control-number-buttons-focus-color);
|
||||
}
|
||||
.button {
|
||||
color: inherit;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
padding: 0;
|
||||
width: 35px;
|
||||
height: 40px;
|
||||
border: none;
|
||||
background: none;
|
||||
cursor: pointer;
|
||||
outline: none;
|
||||
}
|
||||
.button[disabled] {
|
||||
opacity: 0.4;
|
||||
pointer-events: none;
|
||||
}
|
||||
.button.minus {
|
||||
left: 0;
|
||||
}
|
||||
.button.plus {
|
||||
right: 0;
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-control-number-buttons": HaControlNumberButton;
|
||||
}
|
||||
}
|
@@ -171,15 +171,12 @@ export class HaControlSelectMenu extends SelectBase {
|
||||
--control-select-menu-background-color: var(--disabled-color);
|
||||
--control-select-menu-background-opacity: 0.2;
|
||||
--control-select-menu-border-radius: 14px;
|
||||
--control-select-menu-min-width: 120px;
|
||||
--control-select-menu-max-width: 200px;
|
||||
--control-select-menu-width: 100%;
|
||||
--mdc-icon-size: 20px;
|
||||
width: auto;
|
||||
color: var(--primary-text-color);
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
.select-anchor {
|
||||
color: var(--control-select-menu-text-color);
|
||||
height: 48px;
|
||||
padding: 6px 10px;
|
||||
overflow: hidden;
|
||||
@@ -198,11 +195,8 @@ export class HaControlSelectMenu extends SelectBase {
|
||||
z-index: 0;
|
||||
font-size: inherit;
|
||||
transition: color 180ms ease-in-out;
|
||||
color: var(--control-text-icon-color);
|
||||
gap: 10px;
|
||||
min-width: var(--control-select-menu-min-width);
|
||||
max-width: var(--control-select-menu-max-width);
|
||||
width: var(--control-select-menu-width);
|
||||
width: 100%;
|
||||
user-select: none;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
|
@@ -10,12 +10,12 @@ import "./ha-icon-button";
|
||||
const SUPPRESS_DEFAULT_PRESS_SELECTOR = ["button", "ha-list-item"];
|
||||
|
||||
export const createCloseHeading = (
|
||||
hass: HomeAssistant,
|
||||
hass: HomeAssistant | undefined,
|
||||
title: string | TemplateResult
|
||||
) => html`
|
||||
<div class="header_title">${title}</div>
|
||||
<ha-icon-button
|
||||
.label=${hass.localize("ui.dialogs.generic.close")}
|
||||
.label=${hass?.localize("ui.dialogs.generic.close") ?? "Close"}
|
||||
.path=${mdiClose}
|
||||
dialogAction="close"
|
||||
class="header_button"
|
||||
|
@@ -7,6 +7,7 @@ import { formatLanguageCode } from "../common/language/format_language";
|
||||
import { caseInsensitiveStringCompare } from "../common/string/compare";
|
||||
import { FrontendLocaleData } from "../data/translation";
|
||||
import "../resources/intl-polyfill";
|
||||
import { translationMetadata } from "../resources/translations-metadata";
|
||||
import { HomeAssistant } from "../types";
|
||||
import "./ha-list-item";
|
||||
import "./ha-select";
|
||||
@@ -20,7 +21,7 @@ export class HaLanguagePicker extends LitElement {
|
||||
|
||||
@property() public languages?: string[];
|
||||
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property({ type: Boolean, reflect: true }) public disabled = false;
|
||||
|
||||
@@ -41,7 +42,18 @@ export class HaLanguagePicker extends LitElement {
|
||||
|
||||
protected updated(changedProperties: PropertyValues) {
|
||||
super.updated(changedProperties);
|
||||
if (changedProperties.has("languages") || changedProperties.has("value")) {
|
||||
|
||||
const localeChanged =
|
||||
changedProperties.has("hass") &&
|
||||
this.hass &&
|
||||
changedProperties.get("hass") &&
|
||||
changedProperties.get("hass").locale.language !==
|
||||
this.hass.locale.language;
|
||||
if (
|
||||
changedProperties.has("languages") ||
|
||||
changedProperties.has("value") ||
|
||||
localeChanged
|
||||
) {
|
||||
this._select.layoutOptions();
|
||||
if (this._select.value !== this.value) {
|
||||
fireEvent(this, "value-changed", { value: this._select.value });
|
||||
@@ -51,24 +63,27 @@ export class HaLanguagePicker extends LitElement {
|
||||
}
|
||||
const languageOptions = this._getLanguagesOptions(
|
||||
this.languages ?? this._defaultLanguages,
|
||||
this.hass.locale,
|
||||
this.nativeName
|
||||
this.nativeName,
|
||||
this.hass?.locale
|
||||
);
|
||||
const selectedItem = languageOptions.find(
|
||||
const selectedItemIndex = languageOptions.findIndex(
|
||||
(option) => option.value === this.value
|
||||
);
|
||||
if (!selectedItem) {
|
||||
if (selectedItemIndex === -1) {
|
||||
this.value = undefined;
|
||||
}
|
||||
if (localeChanged) {
|
||||
this._select.select(selectedItemIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _getLanguagesOptions = memoizeOne(
|
||||
(languages: string[], locale: FrontendLocaleData, nativeName: boolean) => {
|
||||
(languages: string[], nativeName: boolean, locale?: FrontendLocaleData) => {
|
||||
let options: { label: string; value: string }[] = [];
|
||||
|
||||
if (nativeName) {
|
||||
const translations = this.hass.translationMetadata.translations;
|
||||
const translations = translationMetadata.translations;
|
||||
options = languages.map((lang) => {
|
||||
let label = translations[lang]?.nativeName;
|
||||
if (!label) {
|
||||
@@ -87,14 +102,14 @@ export class HaLanguagePicker extends LitElement {
|
||||
label,
|
||||
};
|
||||
});
|
||||
} else {
|
||||
} else if (locale) {
|
||||
options = languages.map((lang) => ({
|
||||
value: lang,
|
||||
label: formatLanguageCode(lang, locale),
|
||||
}));
|
||||
}
|
||||
|
||||
if (!this.noSort) {
|
||||
if (!this.noSort && locale) {
|
||||
options.sort((a, b) =>
|
||||
caseInsensitiveStringCompare(a.label, b.label, locale.language)
|
||||
);
|
||||
@@ -104,20 +119,14 @@ export class HaLanguagePicker extends LitElement {
|
||||
);
|
||||
|
||||
private _computeDefaultLanguageOptions() {
|
||||
if (!this.hass.translationMetadata?.translations) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._defaultLanguages = Object.keys(
|
||||
this.hass.translationMetadata.translations
|
||||
);
|
||||
this._defaultLanguages = Object.keys(translationMetadata.translations);
|
||||
}
|
||||
|
||||
protected render() {
|
||||
const languageOptions = this._getLanguagesOptions(
|
||||
this.languages ?? this._defaultLanguages,
|
||||
this.hass.locale,
|
||||
this.nativeName
|
||||
this.nativeName,
|
||||
this.hass?.locale
|
||||
);
|
||||
|
||||
const value =
|
||||
@@ -125,9 +134,10 @@ export class HaLanguagePicker extends LitElement {
|
||||
|
||||
return html`
|
||||
<ha-select
|
||||
.label=${this.label ||
|
||||
this.hass.localize("ui.components.language-picker.language")}
|
||||
.value=${value}
|
||||
.label=${this.label ??
|
||||
(this.hass?.localize("ui.components.language-picker.language") ||
|
||||
"Language")}
|
||||
.value=${value || ""}
|
||||
.required=${this.required}
|
||||
.disabled=${this.disabled}
|
||||
@selected=${this._changed}
|
||||
@@ -137,9 +147,9 @@ export class HaLanguagePicker extends LitElement {
|
||||
>
|
||||
${languageOptions.length === 0
|
||||
? html`<ha-list-item value=""
|
||||
>${this.hass.localize(
|
||||
>${this.hass?.localize(
|
||||
"ui.components.language-picker.no_languages"
|
||||
)}</ha-list-item
|
||||
) || "No languages"}</ha-list-item
|
||||
>`
|
||||
: languageOptions.map(
|
||||
(option) => html`
|
||||
@@ -162,7 +172,7 @@ export class HaLanguagePicker extends LitElement {
|
||||
|
||||
private _changed(ev): void {
|
||||
const target = ev.target as HaSelect;
|
||||
if (!this.hass || target.value === "" || target.value === this.value) {
|
||||
if (target.value === "" || target.value === this.value) {
|
||||
return;
|
||||
}
|
||||
this.value = target.value;
|
||||
|
@@ -47,6 +47,9 @@ export class HaSelect extends SelectBase {
|
||||
.mdc-select__anchor {
|
||||
width: var(--ha-select-min-width, 200px);
|
||||
}
|
||||
.mdc-select--filled .mdc-select__anchor {
|
||||
height: var(--ha-select-height, 56px);
|
||||
}
|
||||
.mdc-select--filled .mdc-floating-label {
|
||||
inset-inline-start: 12px;
|
||||
inset-inline-end: initial;
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { css, CSSResultGroup, html, LitElement } from "lit";
|
||||
import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { Action } from "../../data/script";
|
||||
import { ActionSelector } from "../../data/selector";
|
||||
@@ -19,10 +19,13 @@ export class HaActionSelector extends LitElement {
|
||||
|
||||
protected render() {
|
||||
return html`
|
||||
${this.label ? html`<label>${this.label}</label>` : nothing}
|
||||
<ha-automation-action
|
||||
.disabled=${this.disabled}
|
||||
.actions=${this.value || []}
|
||||
.hass=${this.hass}
|
||||
.nested=${this.selector.action?.nested}
|
||||
.reOrderMode=${this.selector.action?.reorder_mode}
|
||||
></ha-automation-action>
|
||||
`;
|
||||
}
|
||||
@@ -37,6 +40,11 @@ export class HaActionSelector extends LitElement {
|
||||
opacity: var(--light-disabled-opacity);
|
||||
pointer-events: none;
|
||||
}
|
||||
label {
|
||||
display: block;
|
||||
margin-bottom: 4px;
|
||||
font-weight: 500;
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { css, CSSResultGroup, html, LitElement } from "lit";
|
||||
import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { Condition } from "../../data/automation";
|
||||
import { ConditionSelector } from "../../data/selector";
|
||||
@@ -19,10 +19,13 @@ export class HaConditionSelector extends LitElement {
|
||||
|
||||
protected render() {
|
||||
return html`
|
||||
${this.label ? html`<label>${this.label}</label>` : nothing}
|
||||
<ha-automation-condition
|
||||
.disabled=${this.disabled}
|
||||
.conditions=${this.value || []}
|
||||
.hass=${this.hass}
|
||||
.nested=${this.selector.condition?.nested}
|
||||
.reOrderMode=${this.selector.condition?.reorder_mode}
|
||||
></ha-automation-condition>
|
||||
`;
|
||||
}
|
||||
@@ -37,6 +40,11 @@ export class HaConditionSelector extends LitElement {
|
||||
opacity: var(--light-disabled-opacity);
|
||||
pointer-events: none;
|
||||
}
|
||||
label {
|
||||
display: block;
|
||||
margin-bottom: 4px;
|
||||
font-weight: 500;
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
@@ -1,15 +0,0 @@
|
||||
import { HomeAssistant } from "../types";
|
||||
|
||||
export const createLanguageListEl = (hass: HomeAssistant) => {
|
||||
const list = document.createElement("datalist");
|
||||
list.id = "languages";
|
||||
for (const [language, metadata] of Object.entries(
|
||||
hass.translationMetadata.translations
|
||||
)) {
|
||||
const option = document.createElement("option");
|
||||
option.value = language;
|
||||
option.innerText = metadata.nativeName;
|
||||
list.appendChild(option);
|
||||
}
|
||||
return list;
|
||||
};
|
@@ -61,7 +61,18 @@ export const createAuthForUser = async (
|
||||
password,
|
||||
});
|
||||
|
||||
export const adminChangePassword = async (
|
||||
export const changePassword = (
|
||||
hass: HomeAssistant,
|
||||
current_password: string,
|
||||
new_password: string
|
||||
) =>
|
||||
hass.callWS({
|
||||
type: "config/auth_provider/homeassistant/change_password",
|
||||
current_password,
|
||||
new_password,
|
||||
});
|
||||
|
||||
export const adminChangePassword = (
|
||||
hass: HomeAssistant,
|
||||
userId: string,
|
||||
password: string
|
||||
@@ -71,3 +82,8 @@ export const adminChangePassword = async (
|
||||
user_id: userId,
|
||||
password,
|
||||
});
|
||||
|
||||
export const deleteAllRefreshTokens = (hass: HomeAssistant) =>
|
||||
hass.callWS({
|
||||
type: "auth/delete_all_refresh_tokens",
|
||||
});
|
||||
|
@@ -67,7 +67,7 @@ export const DOMAIN_ATTRIBUTES_UNITS: Record<string, Record<string, string>> = {
|
||||
sun: {
|
||||
elevation: "°",
|
||||
},
|
||||
vaccum: {
|
||||
vacuum: {
|
||||
battery_level: "%",
|
||||
},
|
||||
};
|
||||
|
@@ -6,6 +6,7 @@ import { caseInsensitiveStringCompare } from "../common/string/compare";
|
||||
import { debounce } from "../common/util/debounce";
|
||||
import { HomeAssistant } from "../types";
|
||||
import { LightColor } from "./light";
|
||||
import { computeDomain } from "../common/entity/compute_domain";
|
||||
|
||||
type entityCategory = "config" | "diagnostic";
|
||||
|
||||
@@ -129,15 +130,29 @@ export interface EntityRegistryEntryUpdateParams {
|
||||
aliases?: string[];
|
||||
}
|
||||
|
||||
const batteryPriorities = ["sensor", "binary_sensor"];
|
||||
export const findBatteryEntity = <T extends { entity_id: string }>(
|
||||
hass: HomeAssistant,
|
||||
entities: T[]
|
||||
): T | undefined =>
|
||||
entities.find(
|
||||
(entity) =>
|
||||
hass.states[entity.entity_id] &&
|
||||
hass.states[entity.entity_id].attributes.device_class === "battery"
|
||||
);
|
||||
): T | undefined => {
|
||||
const batteryEntities = entities
|
||||
.filter(
|
||||
(entity) =>
|
||||
hass.states[entity.entity_id] &&
|
||||
hass.states[entity.entity_id].attributes.device_class === "battery" &&
|
||||
batteryPriorities.includes(computeDomain(entity.entity_id))
|
||||
)
|
||||
.sort(
|
||||
(a, b) =>
|
||||
batteryPriorities.indexOf(computeDomain(a.entity_id)) -
|
||||
batteryPriorities.indexOf(computeDomain(b.entity_id))
|
||||
);
|
||||
if (batteryEntities.length > 0) {
|
||||
return batteryEntities[0];
|
||||
}
|
||||
|
||||
return undefined;
|
||||
};
|
||||
|
||||
export const findBatteryChargingEntity = <T extends { entity_id: string }>(
|
||||
hass: HomeAssistant,
|
||||
|
@@ -17,6 +17,11 @@ export interface GroupEntity extends HassEntityBase {
|
||||
attributes: GroupEntityAttributes;
|
||||
}
|
||||
|
||||
export interface GroupPreview {
|
||||
state: string;
|
||||
attributes: Record<string, any>;
|
||||
}
|
||||
|
||||
export const computeGroupDomain = (
|
||||
stateObj: GroupEntity
|
||||
): string | undefined => {
|
||||
@@ -27,35 +32,15 @@ export const computeGroupDomain = (
|
||||
return uniqueDomains.length === 1 ? uniqueDomains[0] : undefined;
|
||||
};
|
||||
|
||||
export const subscribePreviewGroupSensor = (
|
||||
export const subscribePreviewGroup = (
|
||||
hass: HomeAssistant,
|
||||
flow_id: string,
|
||||
flow_type: "config_flow" | "options_flow",
|
||||
user_input: Record<string, any>,
|
||||
callback: (preview: {
|
||||
state: string;
|
||||
attributes: Record<string, any>;
|
||||
}) => void
|
||||
callback: (preview: GroupPreview) => void
|
||||
): Promise<UnsubscribeFunc> =>
|
||||
hass.connection.subscribeMessage(callback, {
|
||||
type: "group/sensor/start_preview",
|
||||
flow_id,
|
||||
flow_type,
|
||||
user_input,
|
||||
});
|
||||
|
||||
export const subscribePreviewGroupBinarySensor = (
|
||||
hass: HomeAssistant,
|
||||
flow_id: string,
|
||||
flow_type: "config_flow" | "options_flow",
|
||||
user_input: Record<string, any>,
|
||||
callback: (preview: {
|
||||
state: string;
|
||||
attributes: Record<string, any>;
|
||||
}) => void
|
||||
): Promise<UnsubscribeFunc> =>
|
||||
hass.connection.subscribeMessage(callback, {
|
||||
type: "group/binary_sensor/start_preview",
|
||||
type: "group/start_preview",
|
||||
flow_id,
|
||||
flow_type,
|
||||
user_input,
|
||||
|
@@ -54,8 +54,10 @@ export type Selector =
|
||||
| UiColorSelector;
|
||||
|
||||
export interface ActionSelector {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
action: {} | null;
|
||||
action: {
|
||||
reorder_mode?: boolean;
|
||||
nested?: boolean;
|
||||
} | null;
|
||||
}
|
||||
|
||||
export interface AddonSelector {
|
||||
@@ -98,8 +100,10 @@ export interface ColorTempSelector {
|
||||
}
|
||||
|
||||
export interface ConditionSelector {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
condition: {} | null;
|
||||
condition: {
|
||||
reorder_mode?: boolean;
|
||||
nested?: boolean;
|
||||
} | null;
|
||||
}
|
||||
|
||||
export interface ConversationAgentSelector {
|
||||
|
@@ -2,19 +2,20 @@ import { HassEntity, UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||
import { LitElement, html } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { FlowType } from "../../../data/data_entry_flow";
|
||||
import { subscribePreviewGroupBinarySensor } from "../../../data/group";
|
||||
import { GroupPreview, subscribePreviewGroup } from "../../../data/group";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
import "./entity-preview-row";
|
||||
import { debounce } from "../../../common/util/debounce";
|
||||
|
||||
@customElement("flow-preview-group_binary_sensor")
|
||||
class FlowPreviewGroupBinarySensor extends LitElement {
|
||||
@customElement("flow-preview-group")
|
||||
class FlowPreviewGroup extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property() public flowType!: FlowType;
|
||||
|
||||
public handler!: string;
|
||||
|
||||
public stepId!: string;
|
||||
@property() public stepId!: string;
|
||||
|
||||
@property() public flowId!: string;
|
||||
|
||||
@@ -34,7 +35,7 @@ class FlowPreviewGroupBinarySensor extends LitElement {
|
||||
|
||||
willUpdate(changedProps) {
|
||||
if (changedProps.has("stepData")) {
|
||||
this._subscribePreview();
|
||||
this._debouncedSubscribePreview();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,13 +46,10 @@ class FlowPreviewGroupBinarySensor extends LitElement {
|
||||
></entity-preview-row>`;
|
||||
}
|
||||
|
||||
private _setPreview = (preview: {
|
||||
state: string;
|
||||
attributes: Record<string, any>;
|
||||
}) => {
|
||||
private _setPreview = (preview: GroupPreview) => {
|
||||
const now = new Date().toISOString();
|
||||
this._preview = {
|
||||
entity_id: "binary_sensor.flow_preview",
|
||||
entity_id: `${this.stepId}.flow_preview`,
|
||||
last_changed: now,
|
||||
last_updated: now,
|
||||
context: { id: "", parent_id: null, user_id: null },
|
||||
@@ -59,6 +57,10 @@ class FlowPreviewGroupBinarySensor extends LitElement {
|
||||
};
|
||||
};
|
||||
|
||||
private _debouncedSubscribePreview = debounce(() => {
|
||||
this._subscribePreview();
|
||||
}, 250);
|
||||
|
||||
private async _subscribePreview() {
|
||||
if (this._unsub) {
|
||||
(await this._unsub)();
|
||||
@@ -68,7 +70,7 @@ class FlowPreviewGroupBinarySensor extends LitElement {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
this._unsub = subscribePreviewGroupBinarySensor(
|
||||
this._unsub = subscribePreviewGroup(
|
||||
this.hass,
|
||||
this.flowId,
|
||||
this.flowType,
|
||||
@@ -83,6 +85,6 @@ class FlowPreviewGroupBinarySensor extends LitElement {
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"flow-preview-group_binary_sensor": FlowPreviewGroupBinarySensor;
|
||||
"flow-preview-group": FlowPreviewGroup;
|
||||
}
|
||||
}
|
@@ -1,92 +0,0 @@
|
||||
import { HassEntity, UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||
import { LitElement, html } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { FlowType } from "../../../data/data_entry_flow";
|
||||
import { subscribePreviewGroupSensor } from "../../../data/group";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
import "./entity-preview-row";
|
||||
|
||||
@customElement("flow-preview-group_sensor")
|
||||
class FlowPreviewGroupSensor extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property() public flowType!: FlowType;
|
||||
|
||||
public handler!: string;
|
||||
|
||||
public stepId!: string;
|
||||
|
||||
@property() public flowId!: string;
|
||||
|
||||
@property() public stepData!: Record<string, any>;
|
||||
|
||||
@state() private _preview?: HassEntity;
|
||||
|
||||
private _unsub?: Promise<UnsubscribeFunc>;
|
||||
|
||||
disconnectedCallback(): void {
|
||||
super.disconnectedCallback();
|
||||
if (this._unsub) {
|
||||
this._unsub.then((unsub) => unsub());
|
||||
this._unsub = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
willUpdate(changedProps) {
|
||||
if (changedProps.has("stepData")) {
|
||||
this._subscribePreview();
|
||||
}
|
||||
}
|
||||
|
||||
protected render() {
|
||||
return html`<entity-preview-row
|
||||
.hass=${this.hass}
|
||||
.stateObj=${this._preview}
|
||||
></entity-preview-row>`;
|
||||
}
|
||||
|
||||
private _setPreview = (preview: {
|
||||
state: string;
|
||||
attributes: Record<string, any>;
|
||||
}) => {
|
||||
const now = new Date().toISOString();
|
||||
this._preview = {
|
||||
entity_id: "sensor.flow_preview",
|
||||
last_changed: now,
|
||||
last_updated: now,
|
||||
context: { id: "", parent_id: null, user_id: null },
|
||||
...preview,
|
||||
};
|
||||
};
|
||||
|
||||
private async _subscribePreview() {
|
||||
if (this._unsub) {
|
||||
(await this._unsub)();
|
||||
this._unsub = undefined;
|
||||
}
|
||||
if (this.flowType === "repair_flow") {
|
||||
return;
|
||||
}
|
||||
if (!this.stepData.type) {
|
||||
this._preview = undefined;
|
||||
return;
|
||||
}
|
||||
try {
|
||||
this._unsub = subscribePreviewGroupSensor(
|
||||
this.hass,
|
||||
this.flowId,
|
||||
this.flowType,
|
||||
this.stepData,
|
||||
this._setPreview
|
||||
);
|
||||
} catch (err) {
|
||||
this._preview = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"flow-preview-group_sensor": FlowPreviewGroupSensor;
|
||||
}
|
||||
}
|
@@ -10,6 +10,7 @@ import {
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { classMap } from "lit/directives/class-map";
|
||||
import { styleMap } from "lit/directives/style-map";
|
||||
import { UNIT_F } from "../../../../common/const";
|
||||
import { computeAttributeValueDisplay } from "../../../../common/entity/compute_attribute_display";
|
||||
import { stateActive } from "../../../../common/entity/state_active";
|
||||
import { stateColorCss } from "../../../../common/entity/state_color";
|
||||
@@ -67,7 +68,7 @@ export class HaMoreInfoClimateTemperature extends LitElement {
|
||||
private get _step() {
|
||||
return (
|
||||
this.stateObj.attributes.target_temp_step ||
|
||||
(this.hass.config.unit_system.temperature.indexOf("F") === -1 ? 0.5 : 1)
|
||||
(this.hass.config.unit_system.temperature === UNIT_F ? 1 : 0.5)
|
||||
);
|
||||
}
|
||||
|
||||
|
@@ -35,7 +35,9 @@ export class HaMoreInfoCoverPosition extends LitElement {
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
const color = stateColorCss(this.stateObj);
|
||||
const forcedState = this.stateObj.state === "closed" ? "open" : undefined;
|
||||
|
||||
const color = stateColorCss(this.stateObj, forcedState);
|
||||
|
||||
return html`
|
||||
<ha-control-slider
|
||||
@@ -68,8 +70,6 @@ export class HaMoreInfoCoverPosition extends LitElement {
|
||||
height: 45vh;
|
||||
max-height: 320px;
|
||||
min-height: 200px;
|
||||
/* Force inactive state to be colored for the slider */
|
||||
--state-cover-inactive-color: var(--state-cover-active-color);
|
||||
--control-slider-thickness: 100px;
|
||||
--control-slider-border-radius: 24px;
|
||||
--control-slider-color: var(--primary-color);
|
||||
|
@@ -72,8 +72,9 @@ export class HaMoreInfoCoverTiltPosition extends LitElement {
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
const color = stateColorCss(this.stateObj);
|
||||
const isUnavailable = this.stateObj.state === UNAVAILABLE;
|
||||
const forcedState = this.stateObj.state === "closed" ? "open" : undefined;
|
||||
|
||||
const color = stateColorCss(this.stateObj, forcedState);
|
||||
|
||||
return html`
|
||||
<ha-control-slider
|
||||
@@ -93,7 +94,7 @@ export class HaMoreInfoCoverTiltPosition extends LitElement {
|
||||
"--control-slider-color": color,
|
||||
"--control-slider-background": color,
|
||||
})}
|
||||
.disabled=${isUnavailable}
|
||||
.disabled=${this.stateObj.state === UNAVAILABLE}
|
||||
>
|
||||
<div slot="background" class="gradient"></div>
|
||||
</ha-control-slider>
|
||||
@@ -106,8 +107,6 @@ export class HaMoreInfoCoverTiltPosition extends LitElement {
|
||||
height: 45vh;
|
||||
max-height: 320px;
|
||||
min-height: 200px;
|
||||
/* Force inactive state to be colored for the slider */
|
||||
--state-cover-inactive-color: var(--state-cover-active-color);
|
||||
--control-slider-thickness: 100px;
|
||||
--control-slider-border-radius: 24px;
|
||||
--control-slider-color: var(--primary-color);
|
||||
|
@@ -0,0 +1,79 @@
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement } from "lit/decorators";
|
||||
import { classMap } from "lit/directives/class-map";
|
||||
|
||||
@customElement("ha-more-info-control-select-container")
|
||||
export class HaMoreInfoControlSelectContainer extends LitElement {
|
||||
protected render(): TemplateResult {
|
||||
const classname = `items-${this.childElementCount}`;
|
||||
|
||||
return html`
|
||||
<div class="controls">
|
||||
<div
|
||||
class="controls-scroll ${classMap({
|
||||
[classname]: true,
|
||||
multiline: this.childElementCount >= 4,
|
||||
})}"
|
||||
>
|
||||
<slot></slot>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return css`
|
||||
.controls {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
}
|
||||
.controls-scroll {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-start;
|
||||
gap: 12px;
|
||||
margin: auto;
|
||||
overflow: auto;
|
||||
-ms-overflow-style: none; /* IE and Edge */
|
||||
scrollbar-width: none; /* Firefox */
|
||||
margin: 0 -24px;
|
||||
padding: 0 24px;
|
||||
}
|
||||
.controls-scroll::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
::slotted(*) {
|
||||
min-width: 120px;
|
||||
max-width: 160px;
|
||||
flex: none;
|
||||
}
|
||||
|
||||
@media all and (hover: hover),
|
||||
all and (min-width: 600px) and (min-height: 501px) {
|
||||
.controls-scroll {
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
width: 100%;
|
||||
max-width: 450px;
|
||||
}
|
||||
.controls-scroll.items-4 {
|
||||
max-width: 300px;
|
||||
}
|
||||
.controls-scroll.items-3 ::slotted(*) {
|
||||
max-width: 140px;
|
||||
}
|
||||
.multiline ::slotted(*) {
|
||||
width: 140px;
|
||||
}
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-more-info-control-select-container": HaMoreInfoControlSelectContainer;
|
||||
}
|
||||
}
|
@@ -22,35 +22,6 @@ export const moreInfoControlStyle = css`
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.secondary-controls {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
}
|
||||
.secondary-controls-scroll {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-start;
|
||||
gap: 12px;
|
||||
margin: auto;
|
||||
overflow: auto;
|
||||
-ms-overflow-style: none; /* IE and Edge */
|
||||
scrollbar-width: none; /* Firefox */
|
||||
margin: 0 -24px;
|
||||
padding: 0 24px;
|
||||
}
|
||||
.secondary-controls-scroll::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Don't use scroll on device without touch support */
|
||||
@media (hover: hover) {
|
||||
.secondary-controls-scroll {
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
}
|
||||
|
||||
.buttons {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
@@ -9,6 +9,7 @@ import {
|
||||
} from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { styleMap } from "lit/directives/style-map";
|
||||
import { UNIT_F } from "../../../../common/const";
|
||||
import { stateActive } from "../../../../common/entity/state_active";
|
||||
import { stateColorCss } from "../../../../common/entity/state_color";
|
||||
import { supportsFeature } from "../../../../common/entity/supports-feature";
|
||||
@@ -44,7 +45,7 @@ export class HaMoreInfoWaterHeaterTemperature extends LitElement {
|
||||
private get _step() {
|
||||
return (
|
||||
this.stateObj.attributes.target_temp_step ||
|
||||
(this.hass.config.unit_system.temperature.indexOf("F") === -1 ? 0.5 : 1)
|
||||
(this.hass.config.unit_system.temperature === UNIT_F ? 1 : 0.5)
|
||||
);
|
||||
}
|
||||
|
||||
|
@@ -38,6 +38,7 @@ import { haOscillating } from "../../../data/icons/haOscillating";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
import "../components/climate/ha-more-info-climate-humidity";
|
||||
import "../components/climate/ha-more-info-climate-temperature";
|
||||
import "../components/ha-more-info-control-select-container";
|
||||
import { moreInfoControlStyle } from "../components/ha-more-info-control-style";
|
||||
|
||||
type MainControl = "temperature" | "humidity";
|
||||
@@ -163,140 +164,135 @@ class MoreInfoClimate extends LitElement {
|
||||
`
|
||||
: nothing}
|
||||
</div>
|
||||
<div class="secondary-controls">
|
||||
<div class="secondary-controls-scroll">
|
||||
<ha-control-select-menu
|
||||
.label=${this.hass.formatEntityAttributeName(
|
||||
this.stateObj,
|
||||
"hvac_mode"
|
||||
)}
|
||||
.value=${stateObj.state}
|
||||
.disabled=${this.stateObj.state === UNAVAILABLE}
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
@selected=${this._handleOperationModeChanged}
|
||||
@closed=${stopPropagation}
|
||||
>
|
||||
<ha-svg-icon slot="icon" .path=${mdiThermostat}></ha-svg-icon>
|
||||
${stateObj.attributes.hvac_modes
|
||||
.concat()
|
||||
.sort(compareClimateHvacModes)
|
||||
.map(
|
||||
(mode) => html`
|
||||
<ha-list-item .value=${mode} graphic="icon">
|
||||
<ha-svg-icon
|
||||
slot="graphic"
|
||||
.path=${computeHvacModeIcon(mode)}
|
||||
></ha-svg-icon>
|
||||
${this.hass.formatEntityState(stateObj, mode)}
|
||||
</ha-list-item>
|
||||
`
|
||||
)}
|
||||
</ha-control-select-menu>
|
||||
${supportPresetMode && stateObj.attributes.preset_modes
|
||||
? html`
|
||||
<ha-control-select-menu
|
||||
.label=${this.hass.formatEntityAttributeName(
|
||||
stateObj,
|
||||
"preset_mode"
|
||||
)}
|
||||
.value=${stateObj.attributes.preset_mode}
|
||||
.disabled=${this.stateObj.state === UNAVAILABLE}
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
@selected=${this._handlePresetmodeChanged}
|
||||
@closed=${stopPropagation}
|
||||
>
|
||||
<ha-more-info-control-select-container>
|
||||
<ha-control-select-menu
|
||||
.label=${this.hass.formatEntityAttributeName(
|
||||
this.stateObj,
|
||||
"hvac_mode"
|
||||
)}
|
||||
.value=${stateObj.state}
|
||||
.disabled=${this.stateObj.state === UNAVAILABLE}
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
@selected=${this._handleOperationModeChanged}
|
||||
@closed=${stopPropagation}
|
||||
>
|
||||
<ha-svg-icon slot="icon" .path=${mdiThermostat}></ha-svg-icon>
|
||||
${stateObj.attributes.hvac_modes
|
||||
.concat()
|
||||
.sort(compareClimateHvacModes)
|
||||
.map(
|
||||
(mode) => html`
|
||||
<ha-list-item .value=${mode} graphic="icon">
|
||||
<ha-svg-icon
|
||||
slot="icon"
|
||||
.path=${mdiTuneVariant}
|
||||
slot="graphic"
|
||||
.path=${computeHvacModeIcon(mode)}
|
||||
></ha-svg-icon>
|
||||
${stateObj.attributes.preset_modes!.map(
|
||||
(mode) => html`
|
||||
<ha-list-item .value=${mode} graphic="icon">
|
||||
<ha-svg-icon
|
||||
slot="graphic"
|
||||
.path=${computePresetModeIcon(mode)}
|
||||
></ha-svg-icon>
|
||||
${this.hass.formatEntityAttributeValue(
|
||||
stateObj,
|
||||
"preset_mode",
|
||||
mode
|
||||
)}
|
||||
</ha-list-item>
|
||||
`
|
||||
)}
|
||||
</ha-control-select-menu>
|
||||
${this.hass.formatEntityState(stateObj, mode)}
|
||||
</ha-list-item>
|
||||
`
|
||||
: nothing}
|
||||
${supportFanMode && stateObj.attributes.fan_modes
|
||||
? html`
|
||||
<ha-control-select-menu
|
||||
.label=${this.hass.formatEntityAttributeName(
|
||||
stateObj,
|
||||
"fan_mode"
|
||||
)}
|
||||
.value=${stateObj.attributes.fan_mode}
|
||||
.disabled=${this.stateObj.state === UNAVAILABLE}
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
@selected=${this._handleFanModeChanged}
|
||||
@closed=${stopPropagation}
|
||||
>
|
||||
<ha-svg-icon slot="icon" .path=${mdiFan}></ha-svg-icon>
|
||||
${stateObj.attributes.fan_modes!.map(
|
||||
(mode) => html`
|
||||
<ha-list-item .value=${mode} graphic="icon">
|
||||
<ha-svg-icon
|
||||
slot="graphic"
|
||||
.path=${computeFanModeIcon(mode)}
|
||||
></ha-svg-icon>
|
||||
${this.hass.formatEntityAttributeValue(
|
||||
stateObj,
|
||||
"fan_mode",
|
||||
mode
|
||||
)}
|
||||
</ha-list-item>
|
||||
`
|
||||
)}
|
||||
</ha-control-select-menu>
|
||||
`
|
||||
: nothing}
|
||||
${supportSwingMode && stateObj.attributes.swing_modes
|
||||
? html`
|
||||
<ha-control-select-menu
|
||||
.label=${this.hass.formatEntityAttributeName(
|
||||
stateObj,
|
||||
"swing_mode"
|
||||
)}
|
||||
.value=${stateObj.attributes.swing_mode}
|
||||
.disabled=${this.stateObj.state === UNAVAILABLE}
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
@selected=${this._handleSwingmodeChanged}
|
||||
@closed=${stopPropagation}
|
||||
>
|
||||
<ha-svg-icon slot="icon" .path=${haOscillating}></ha-svg-icon>
|
||||
${stateObj.attributes.swing_modes!.map(
|
||||
(mode) => html`
|
||||
<ha-list-item .value=${mode} graphic="icon">
|
||||
<ha-svg-icon
|
||||
slot="graphic"
|
||||
.path=${computeSwingModeIcon(mode)}
|
||||
></ha-svg-icon>
|
||||
${this.hass.formatEntityAttributeValue(
|
||||
stateObj,
|
||||
"swing_mode",
|
||||
mode
|
||||
)}
|
||||
</ha-list-item>
|
||||
`
|
||||
)}
|
||||
</ha-control-select-menu>
|
||||
`
|
||||
: nothing}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</ha-control-select-menu>
|
||||
${supportPresetMode && stateObj.attributes.preset_modes
|
||||
? html`
|
||||
<ha-control-select-menu
|
||||
.label=${this.hass.formatEntityAttributeName(
|
||||
stateObj,
|
||||
"preset_mode"
|
||||
)}
|
||||
.value=${stateObj.attributes.preset_mode}
|
||||
.disabled=${this.stateObj.state === UNAVAILABLE}
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
@selected=${this._handlePresetmodeChanged}
|
||||
@closed=${stopPropagation}
|
||||
>
|
||||
<ha-svg-icon slot="icon" .path=${mdiTuneVariant}></ha-svg-icon>
|
||||
${stateObj.attributes.preset_modes!.map(
|
||||
(mode) => html`
|
||||
<ha-list-item .value=${mode} graphic="icon">
|
||||
<ha-svg-icon
|
||||
slot="graphic"
|
||||
.path=${computePresetModeIcon(mode)}
|
||||
></ha-svg-icon>
|
||||
${this.hass.formatEntityAttributeValue(
|
||||
stateObj,
|
||||
"preset_mode",
|
||||
mode
|
||||
)}
|
||||
</ha-list-item>
|
||||
`
|
||||
)}
|
||||
</ha-control-select-menu>
|
||||
`
|
||||
: nothing}
|
||||
${supportFanMode && stateObj.attributes.fan_modes
|
||||
? html`
|
||||
<ha-control-select-menu
|
||||
.label=${this.hass.formatEntityAttributeName(
|
||||
stateObj,
|
||||
"fan_mode"
|
||||
)}
|
||||
.value=${stateObj.attributes.fan_mode}
|
||||
.disabled=${this.stateObj.state === UNAVAILABLE}
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
@selected=${this._handleFanModeChanged}
|
||||
@closed=${stopPropagation}
|
||||
>
|
||||
<ha-svg-icon slot="icon" .path=${mdiFan}></ha-svg-icon>
|
||||
${stateObj.attributes.fan_modes!.map(
|
||||
(mode) => html`
|
||||
<ha-list-item .value=${mode} graphic="icon">
|
||||
<ha-svg-icon
|
||||
slot="graphic"
|
||||
.path=${computeFanModeIcon(mode)}
|
||||
></ha-svg-icon>
|
||||
${this.hass.formatEntityAttributeValue(
|
||||
stateObj,
|
||||
"fan_mode",
|
||||
mode
|
||||
)}
|
||||
</ha-list-item>
|
||||
`
|
||||
)}
|
||||
</ha-control-select-menu>
|
||||
`
|
||||
: nothing}
|
||||
${supportSwingMode && stateObj.attributes.swing_modes
|
||||
? html`
|
||||
<ha-control-select-menu
|
||||
.label=${this.hass.formatEntityAttributeName(
|
||||
stateObj,
|
||||
"swing_mode"
|
||||
)}
|
||||
.value=${stateObj.attributes.swing_mode}
|
||||
.disabled=${this.stateObj.state === UNAVAILABLE}
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
@selected=${this._handleSwingmodeChanged}
|
||||
@closed=${stopPropagation}
|
||||
>
|
||||
<ha-svg-icon slot="icon" .path=${haOscillating}></ha-svg-icon>
|
||||
${stateObj.attributes.swing_modes!.map(
|
||||
(mode) => html`
|
||||
<ha-list-item .value=${mode} graphic="icon">
|
||||
<ha-svg-icon
|
||||
slot="graphic"
|
||||
.path=${computeSwingModeIcon(mode)}
|
||||
></ha-svg-icon>
|
||||
${this.hass.formatEntityAttributeValue(
|
||||
stateObj,
|
||||
"swing_mode",
|
||||
mode
|
||||
)}
|
||||
</ha-list-item>
|
||||
`
|
||||
)}
|
||||
</ha-control-select-menu>
|
||||
`
|
||||
: nothing}
|
||||
</ha-more-info-control-select-container>
|
||||
`;
|
||||
}
|
||||
|
||||
|
@@ -27,6 +27,7 @@ import { haOscillating } from "../../../data/icons/haOscillating";
|
||||
import { haOscillatingOff } from "../../../data/icons/haOscillatingOff";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import "../components/fan/ha-more-info-fan-speed";
|
||||
import "../components/ha-more-info-control-select-container";
|
||||
import { moreInfoControlStyle } from "../components/ha-more-info-control-style";
|
||||
import "../components/ha-more-info-state-header";
|
||||
import "../components/ha-more-info-toggle";
|
||||
@@ -191,117 +192,112 @@ class MoreInfoFan extends LitElement {
|
||||
`
|
||||
: nothing}
|
||||
</div>
|
||||
<div class="secondary-controls">
|
||||
<div class="secondary-controls-scroll">
|
||||
${supportsPresetMode && this.stateObj.attributes.preset_modes
|
||||
? html`
|
||||
<ha-control-select-menu
|
||||
.label=${this.hass.formatEntityAttributeName(
|
||||
this.stateObj,
|
||||
"preset_mode"
|
||||
)}
|
||||
.value=${this.stateObj.attributes.preset_mode}
|
||||
.disabled=${this.stateObj.state === UNAVAILABLE}
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
@selected=${this._handlePresetMode}
|
||||
@closed=${stopPropagation}
|
||||
>
|
||||
<ha-more-info-control-select-container>
|
||||
${supportsPresetMode && this.stateObj.attributes.preset_modes
|
||||
? html`
|
||||
<ha-control-select-menu
|
||||
.label=${this.hass.formatEntityAttributeName(
|
||||
this.stateObj,
|
||||
"preset_mode"
|
||||
)}
|
||||
.value=${this.stateObj.attributes.preset_mode}
|
||||
.disabled=${this.stateObj.state === UNAVAILABLE}
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
@selected=${this._handlePresetMode}
|
||||
@closed=${stopPropagation}
|
||||
>
|
||||
<ha-svg-icon slot="icon" .path=${mdiTuneVariant}></ha-svg-icon>
|
||||
${this.stateObj.attributes.preset_modes?.map(
|
||||
(mode) => html`
|
||||
<ha-list-item .value=${mode}>
|
||||
${this.hass.formatEntityAttributeValue(
|
||||
this.stateObj!,
|
||||
"preset_mode",
|
||||
mode
|
||||
)}
|
||||
</ha-list-item>
|
||||
`
|
||||
)}
|
||||
</ha-control-select-menu>
|
||||
`
|
||||
: nothing}
|
||||
${supportsDirection
|
||||
? html`
|
||||
<ha-control-select-menu
|
||||
.label=${this.hass.formatEntityAttributeName(
|
||||
this.stateObj,
|
||||
"direction"
|
||||
)}
|
||||
.value=${this.stateObj.attributes.direction}
|
||||
.disabled=${this.stateObj.state === UNAVAILABLE}
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
@selected=${this._handleDirection}
|
||||
@closed=${stopPropagation}
|
||||
>
|
||||
<ha-svg-icon slot="icon" .path=${mdiRotateLeft}></ha-svg-icon>
|
||||
<ha-list-item value="forward" graphic="icon">
|
||||
<ha-svg-icon
|
||||
slot="icon"
|
||||
.path=${mdiTuneVariant}
|
||||
slot="graphic"
|
||||
.path=${mdiRotateRight}
|
||||
></ha-svg-icon>
|
||||
${this.stateObj.attributes.preset_modes?.map(
|
||||
(mode) => html`
|
||||
<ha-list-item .value=${mode}>
|
||||
${this.hass.formatEntityAttributeValue(
|
||||
this.stateObj!,
|
||||
"preset_mode",
|
||||
mode
|
||||
)}
|
||||
</ha-list-item>
|
||||
`
|
||||
)}
|
||||
</ha-control-select-menu>
|
||||
`
|
||||
: nothing}
|
||||
${supportsDirection
|
||||
? html`
|
||||
<ha-control-select-menu
|
||||
.label=${this.hass.formatEntityAttributeName(
|
||||
${this.hass.formatEntityAttributeValue(
|
||||
this.stateObj,
|
||||
"direction"
|
||||
"direction",
|
||||
"forward"
|
||||
)}
|
||||
.value=${this.stateObj.attributes.direction}
|
||||
.disabled=${this.stateObj.state === UNAVAILABLE}
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
@selected=${this._handleDirection}
|
||||
@closed=${stopPropagation}
|
||||
>
|
||||
<ha-svg-icon slot="icon" .path=${mdiRotateLeft}></ha-svg-icon>
|
||||
<ha-list-item value="forward" graphic="icon">
|
||||
<ha-svg-icon
|
||||
slot="graphic"
|
||||
.path=${mdiRotateRight}
|
||||
></ha-svg-icon>
|
||||
${this.hass.formatEntityAttributeValue(
|
||||
this.stateObj,
|
||||
"direction",
|
||||
"forward"
|
||||
)}
|
||||
</ha-list-item>
|
||||
<ha-list-item value="reverse" graphic="icon">
|
||||
<ha-svg-icon
|
||||
slot="graphic"
|
||||
.path=${mdiRotateLeft}
|
||||
></ha-svg-icon>
|
||||
${this.hass.formatEntityAttributeValue(
|
||||
this.stateObj,
|
||||
"direction",
|
||||
"reverse"
|
||||
)}
|
||||
</ha-list-item>
|
||||
</ha-control-select-menu>
|
||||
`
|
||||
: nothing}
|
||||
${supportsOscillate
|
||||
? html`
|
||||
<ha-control-select-menu
|
||||
.label=${this.hass.formatEntityAttributeName(
|
||||
this.stateObj,
|
||||
"oscillating"
|
||||
)}
|
||||
.value=${this.stateObj.attributes.oscillating ? "on" : "off"}
|
||||
.disabled=${this.stateObj.state === UNAVAILABLE}
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
@selected=${this._handleOscillating}
|
||||
@closed=${stopPropagation}
|
||||
>
|
||||
</ha-list-item>
|
||||
<ha-list-item value="reverse" graphic="icon">
|
||||
<ha-svg-icon
|
||||
slot="icon"
|
||||
slot="graphic"
|
||||
.path=${mdiRotateLeft}
|
||||
></ha-svg-icon>
|
||||
${this.hass.formatEntityAttributeValue(
|
||||
this.stateObj,
|
||||
"direction",
|
||||
"reverse"
|
||||
)}
|
||||
</ha-list-item>
|
||||
</ha-control-select-menu>
|
||||
`
|
||||
: nothing}
|
||||
${supportsOscillate
|
||||
? html`
|
||||
<ha-control-select-menu
|
||||
.label=${this.hass.formatEntityAttributeName(
|
||||
this.stateObj,
|
||||
"oscillating"
|
||||
)}
|
||||
.value=${this.stateObj.attributes.oscillating ? "on" : "off"}
|
||||
.disabled=${this.stateObj.state === UNAVAILABLE}
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
@selected=${this._handleOscillating}
|
||||
@closed=${stopPropagation}
|
||||
>
|
||||
<ha-svg-icon
|
||||
slot="icon"
|
||||
.path=${haOscillatingOff}
|
||||
></ha-svg-icon>
|
||||
<ha-list-item value="on" graphic="icon">
|
||||
<ha-svg-icon
|
||||
slot="graphic"
|
||||
.path=${haOscillating}
|
||||
></ha-svg-icon>
|
||||
${this.hass.localize("state.default.on")}
|
||||
</ha-list-item>
|
||||
<ha-list-item value="off" graphic="icon">
|
||||
<ha-svg-icon
|
||||
slot="graphic"
|
||||
.path=${haOscillatingOff}
|
||||
></ha-svg-icon>
|
||||
<ha-list-item value="on" graphic="icon">
|
||||
<ha-svg-icon
|
||||
slot="graphic"
|
||||
.path=${haOscillating}
|
||||
></ha-svg-icon>
|
||||
${this.hass.localize("state.default.on")}
|
||||
</ha-list-item>
|
||||
<ha-list-item value="off" graphic="icon">
|
||||
<ha-svg-icon
|
||||
slot="graphic"
|
||||
.path=${haOscillatingOff}
|
||||
></ha-svg-icon>
|
||||
${this.hass.localize("state.default.off")}
|
||||
</ha-list-item>
|
||||
</ha-control-select-menu>
|
||||
`
|
||||
: nothing}
|
||||
</div>
|
||||
</div>
|
||||
${this.hass.localize("state.default.off")}
|
||||
</ha-list-item>
|
||||
</ha-control-select-menu>
|
||||
`
|
||||
: nothing}
|
||||
</ha-more-info-control-select-container>
|
||||
`;
|
||||
}
|
||||
|
||||
|
@@ -25,6 +25,7 @@ import {
|
||||
computeHumidiferModeIcon,
|
||||
} from "../../../data/humidifier";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
import "../components/ha-more-info-control-select-container";
|
||||
import { moreInfoControlStyle } from "../components/ha-more-info-control-style";
|
||||
import "../components/humidifier/ha-more-info-humidifier-humidity";
|
||||
|
||||
@@ -91,79 +92,74 @@ class MoreInfoHumidifier extends LitElement {
|
||||
></ha-more-info-humidifier-humidity>
|
||||
</div>
|
||||
|
||||
<div class="secondary-controls">
|
||||
<div class="secondary-controls-scroll">
|
||||
<ha-control-select-menu
|
||||
.label=${this.hass.localize("ui.card.humidifier.state")}
|
||||
.value=${this.stateObj.state}
|
||||
.disabled=${this.stateObj.state === UNAVAILABLE}
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
@selected=${this._handleStateChanged}
|
||||
@closed=${stopPropagation}
|
||||
>
|
||||
<ha-svg-icon slot="icon" .path=${mdiPower}></ha-svg-icon>
|
||||
<ha-list-item value="off">
|
||||
${computeStateDisplay(
|
||||
this.hass.localize,
|
||||
this.stateObj,
|
||||
this.hass.locale,
|
||||
this.hass.config,
|
||||
this.hass.entities,
|
||||
"off"
|
||||
)}
|
||||
</ha-list-item>
|
||||
<ha-list-item value="on">
|
||||
${computeStateDisplay(
|
||||
this.hass.localize,
|
||||
this.stateObj,
|
||||
this.hass.locale,
|
||||
this.hass.config,
|
||||
this.hass.entities,
|
||||
"on"
|
||||
)}
|
||||
</ha-list-item>
|
||||
</ha-control-select-menu>
|
||||
<ha-more-info-control-select-container>
|
||||
<ha-control-select-menu
|
||||
.label=${this.hass.localize("ui.card.humidifier.state")}
|
||||
.value=${this.stateObj.state}
|
||||
.disabled=${this.stateObj.state === UNAVAILABLE}
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
@selected=${this._handleStateChanged}
|
||||
@closed=${stopPropagation}
|
||||
>
|
||||
<ha-svg-icon slot="icon" .path=${mdiPower}></ha-svg-icon>
|
||||
<ha-list-item value="off">
|
||||
${computeStateDisplay(
|
||||
this.hass.localize,
|
||||
this.stateObj,
|
||||
this.hass.locale,
|
||||
this.hass.config,
|
||||
this.hass.entities,
|
||||
"off"
|
||||
)}
|
||||
</ha-list-item>
|
||||
<ha-list-item value="on">
|
||||
${computeStateDisplay(
|
||||
this.hass.localize,
|
||||
this.stateObj,
|
||||
this.hass.locale,
|
||||
this.hass.config,
|
||||
this.hass.entities,
|
||||
"on"
|
||||
)}
|
||||
</ha-list-item>
|
||||
</ha-control-select-menu>
|
||||
|
||||
${supportModes
|
||||
? html`
|
||||
<ha-control-select-menu
|
||||
.label=${hass.localize("ui.card.humidifier.mode")}
|
||||
.value=${stateObj.attributes.mode}
|
||||
.disabled=${this.stateObj.state === UNAVAILABLE}
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
@selected=${this._handleModeChanged}
|
||||
@closed=${stopPropagation}
|
||||
>
|
||||
<ha-svg-icon
|
||||
slot="icon"
|
||||
.path=${mdiTuneVariant}
|
||||
></ha-svg-icon>
|
||||
${stateObj.attributes.available_modes!.map(
|
||||
(mode) => html`
|
||||
<ha-list-item .value=${mode} graphic="icon">
|
||||
<ha-svg-icon
|
||||
slot="graphic"
|
||||
.path=${computeHumidiferModeIcon(mode)}
|
||||
></ha-svg-icon>
|
||||
${computeAttributeValueDisplay(
|
||||
hass.localize,
|
||||
stateObj!,
|
||||
hass.locale,
|
||||
hass.config,
|
||||
hass.entities,
|
||||
"mode",
|
||||
mode
|
||||
)}
|
||||
</ha-list-item>
|
||||
`
|
||||
)}
|
||||
</ha-control-select-menu>
|
||||
`
|
||||
: nothing}
|
||||
</div>
|
||||
</div>
|
||||
${supportModes
|
||||
? html`
|
||||
<ha-control-select-menu
|
||||
.label=${hass.localize("ui.card.humidifier.mode")}
|
||||
.value=${stateObj.attributes.mode}
|
||||
.disabled=${this.stateObj.state === UNAVAILABLE}
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
@selected=${this._handleModeChanged}
|
||||
@closed=${stopPropagation}
|
||||
>
|
||||
<ha-svg-icon slot="icon" .path=${mdiTuneVariant}></ha-svg-icon>
|
||||
${stateObj.attributes.available_modes!.map(
|
||||
(mode) => html`
|
||||
<ha-list-item .value=${mode} graphic="icon">
|
||||
<ha-svg-icon
|
||||
slot="graphic"
|
||||
.path=${computeHumidiferModeIcon(mode)}
|
||||
></ha-svg-icon>
|
||||
${computeAttributeValueDisplay(
|
||||
hass.localize,
|
||||
stateObj!,
|
||||
hass.locale,
|
||||
hass.config,
|
||||
hass.entities,
|
||||
"mode",
|
||||
mode
|
||||
)}
|
||||
</ha-list-item>
|
||||
`
|
||||
)}
|
||||
</ha-control-select-menu>
|
||||
`
|
||||
: nothing}
|
||||
</ha-more-info-control-select-container>
|
||||
`;
|
||||
}
|
||||
|
||||
|
@@ -36,6 +36,7 @@ import {
|
||||
lightSupportsFavoriteColors,
|
||||
} from "../../../data/light";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import "../components/ha-more-info-control-select-container";
|
||||
import { moreInfoControlStyle } from "../components/ha-more-info-control-style";
|
||||
import "../components/ha-more-info-state-header";
|
||||
import "../components/ha-more-info-toggle";
|
||||
@@ -286,39 +287,37 @@ class MoreInfoLight extends LitElement {
|
||||
: nothing}
|
||||
</div>
|
||||
<div>
|
||||
<div class="secondary-controls">
|
||||
<div class="secondary-controls-scroll">
|
||||
${supportsEffects && this.stateObj.attributes.effect_list
|
||||
? html`
|
||||
<ha-control-select-menu
|
||||
.label=${this.hass.formatEntityAttributeName(
|
||||
this.stateObj,
|
||||
"effect"
|
||||
)}
|
||||
.value=${this.stateObj.attributes.effect}
|
||||
.disabled=${this.stateObj.state === UNAVAILABLE}
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
@selected=${this._handleEffect}
|
||||
@closed=${stopPropagation}
|
||||
>
|
||||
<ha-svg-icon slot="icon" .path=${mdiCreation}></ha-svg-icon>
|
||||
${this.stateObj.attributes.effect_list?.map(
|
||||
(mode) => html`
|
||||
<ha-list-item .value=${mode}>
|
||||
${this.hass.formatEntityAttributeValue(
|
||||
this.stateObj!,
|
||||
"effect",
|
||||
mode
|
||||
)}
|
||||
</ha-list-item>
|
||||
`
|
||||
)}
|
||||
</ha-control-select-menu>
|
||||
`
|
||||
: nothing}
|
||||
</div>
|
||||
</div>
|
||||
<ha-more-info-control-select-container>
|
||||
${supportsEffects && this.stateObj.attributes.effect_list
|
||||
? html`
|
||||
<ha-control-select-menu
|
||||
.label=${this.hass.formatEntityAttributeName(
|
||||
this.stateObj,
|
||||
"effect"
|
||||
)}
|
||||
.value=${this.stateObj.attributes.effect}
|
||||
.disabled=${this.stateObj.state === UNAVAILABLE}
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
@selected=${this._handleEffect}
|
||||
@closed=${stopPropagation}
|
||||
>
|
||||
<ha-svg-icon slot="icon" .path=${mdiCreation}></ha-svg-icon>
|
||||
${this.stateObj.attributes.effect_list?.map(
|
||||
(mode) => html`
|
||||
<ha-list-item .value=${mode}>
|
||||
${this.hass.formatEntityAttributeValue(
|
||||
this.stateObj!,
|
||||
"effect",
|
||||
mode
|
||||
)}
|
||||
</ha-list-item>
|
||||
`
|
||||
)}
|
||||
</ha-control-select-menu>
|
||||
`
|
||||
: nothing}
|
||||
</ha-more-info-control-select-container>
|
||||
<ha-attributes
|
||||
.hass=${this.hass}
|
||||
.stateObj=${this.stateObj}
|
||||
|
@@ -262,12 +262,13 @@ class MoreInfoVacuum extends LitElement {
|
||||
const battery = batteryEntity
|
||||
? this.hass.states[batteryEntity.entity_id]
|
||||
: undefined;
|
||||
|
||||
const batteryIsBinary =
|
||||
battery && computeStateDomain(battery) === "binary_sensor";
|
||||
const batteryDomain = battery ? computeStateDomain(battery) : undefined;
|
||||
|
||||
// Use device battery entity
|
||||
if (battery && (batteryIsBinary || !isNaN(battery.state as any))) {
|
||||
if (
|
||||
battery &&
|
||||
(batteryDomain === "binary_sensor" || !isNaN(battery.state as any))
|
||||
) {
|
||||
const batteryChargingEntity = findBatteryChargingEntity(
|
||||
this.hass,
|
||||
entities
|
||||
@@ -279,7 +280,9 @@ class MoreInfoVacuum extends LitElement {
|
||||
return html`
|
||||
<div>
|
||||
<span>
|
||||
${batteryIsBinary ? "" : this.hass.formatEntityState(battery)}
|
||||
${batteryDomain === "sensor"
|
||||
? this.hass.formatEntityState(battery)
|
||||
: nothing}
|
||||
<ha-battery-icon
|
||||
.hass=${this.hass}
|
||||
.batteryStateObj=${battery}
|
||||
|
@@ -13,6 +13,7 @@ import {
|
||||
computeOperationModeIcon,
|
||||
} from "../../../data/water_heater";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
import "../components/ha-more-info-control-select-container";
|
||||
import { moreInfoControlStyle } from "../components/ha-more-info-control-style";
|
||||
import "../components/water_heater/ha-more-info-water_heater-temperature";
|
||||
|
||||
@@ -68,77 +69,69 @@ class MoreInfoWaterHeater extends LitElement {
|
||||
.stateObj=${this.stateObj}
|
||||
></ha-more-info-water_heater-temperature>
|
||||
</div>
|
||||
<div class="secondary-controls">
|
||||
<div class="secondary-controls-scroll">
|
||||
${supportOperationMode && stateObj.attributes.operation_list
|
||||
? html`
|
||||
<ha-control-select-menu
|
||||
.label=${this.hass.formatEntityAttributeName(
|
||||
stateObj,
|
||||
"operation"
|
||||
<ha-more-info-control-select-container>
|
||||
${supportOperationMode && stateObj.attributes.operation_list
|
||||
? html`
|
||||
<ha-control-select-menu
|
||||
.label=${this.hass.formatEntityAttributeName(
|
||||
stateObj,
|
||||
"operation"
|
||||
)}
|
||||
.value=${stateObj.state}
|
||||
.disabled=${stateObj.state === UNAVAILABLE}
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
@selected=${this._handleOperationModeChanged}
|
||||
@closed=${stopPropagation}
|
||||
>
|
||||
<ha-svg-icon slot="icon" .path=${mdiWaterBoiler}></ha-svg-icon>
|
||||
${stateObj.attributes.operation_list
|
||||
.concat()
|
||||
.sort(compareWaterHeaterOperationMode)
|
||||
.map(
|
||||
(mode) => html`
|
||||
<ha-list-item .value=${mode} graphic="icon">
|
||||
<ha-svg-icon
|
||||
slot="graphic"
|
||||
.path=${computeOperationModeIcon(mode)}
|
||||
></ha-svg-icon>
|
||||
${this.hass.formatEntityState(stateObj, mode)}
|
||||
</ha-list-item>
|
||||
`
|
||||
)}
|
||||
.value=${stateObj.state}
|
||||
.disabled=${stateObj.state === UNAVAILABLE}
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
@selected=${this._handleOperationModeChanged}
|
||||
@closed=${stopPropagation}
|
||||
>
|
||||
</ha-control-select-menu>
|
||||
`
|
||||
: nothing}
|
||||
${supportAwayMode
|
||||
? html`
|
||||
<ha-control-select-menu
|
||||
.label=${this.hass.formatEntityAttributeName(
|
||||
stateObj,
|
||||
"away_mode"
|
||||
)}
|
||||
.value=${stateObj.attributes.away_mode}
|
||||
.disabled=${stateObj.state === UNAVAILABLE}
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
@selected=${this._handleAwayModeChanged}
|
||||
@closed=${stopPropagation}
|
||||
>
|
||||
<ha-svg-icon slot="icon" .path=${mdiAccount}></ha-svg-icon>
|
||||
<ha-list-item value="on" graphic="icon">
|
||||
<ha-svg-icon
|
||||
slot="icon"
|
||||
.path=${mdiWaterBoiler}
|
||||
slot="graphic"
|
||||
.path=${mdiAccountArrowRight}
|
||||
></ha-svg-icon>
|
||||
${stateObj.attributes.operation_list
|
||||
.concat()
|
||||
.sort(compareWaterHeaterOperationMode)
|
||||
.map(
|
||||
(mode) => html`
|
||||
<ha-list-item .value=${mode} graphic="icon">
|
||||
<ha-svg-icon
|
||||
slot="graphic"
|
||||
.path=${computeOperationModeIcon(mode)}
|
||||
></ha-svg-icon>
|
||||
${this.hass.formatEntityState(stateObj, mode)}
|
||||
</ha-list-item>
|
||||
`
|
||||
)}
|
||||
</ha-control-select-menu>
|
||||
`
|
||||
: nothing}
|
||||
${supportAwayMode
|
||||
? html`
|
||||
<ha-control-select-menu
|
||||
.label=${this.hass.formatEntityAttributeName(
|
||||
stateObj,
|
||||
"away_mode"
|
||||
)}
|
||||
.value=${stateObj.attributes.away_mode}
|
||||
.disabled=${stateObj.state === UNAVAILABLE}
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
@selected=${this._handleAwayModeChanged}
|
||||
@closed=${stopPropagation}
|
||||
>
|
||||
<ha-svg-icon slot="icon" .path=${mdiAccount}></ha-svg-icon>
|
||||
<ha-list-item value="on" graphic="icon">
|
||||
<ha-svg-icon
|
||||
slot="graphic"
|
||||
.path=${mdiAccountArrowRight}
|
||||
></ha-svg-icon>
|
||||
${this.hass.localize("state.default.on")}
|
||||
</ha-list-item>
|
||||
<ha-list-item value="off" graphic="icon">
|
||||
<ha-svg-icon
|
||||
slot="graphic"
|
||||
.path=${mdiAccount}
|
||||
></ha-svg-icon>
|
||||
${this.hass.localize("state.default.off")}
|
||||
</ha-list-item>
|
||||
</ha-control-select-menu>
|
||||
`
|
||||
: nothing}
|
||||
</div>
|
||||
</div>
|
||||
${this.hass.localize("state.default.on")}
|
||||
</ha-list-item>
|
||||
<ha-list-item value="off" graphic="icon">
|
||||
<ha-svg-icon slot="graphic" .path=${mdiAccount}></ha-svg-icon>
|
||||
${this.hass.localize("state.default.off")}
|
||||
</ha-list-item>
|
||||
</ha-control-select-menu>
|
||||
`
|
||||
: nothing}
|
||||
</ha-more-info-control-select-container>
|
||||
`;
|
||||
}
|
||||
|
||||
|
@@ -6,57 +6,40 @@
|
||||
<%= renderTemplate("_style_base.html.template") %>
|
||||
<style>
|
||||
html {
|
||||
background-color: var(--primary-background-color, #fafafa);
|
||||
color: var(--primary-text-color, #212121);
|
||||
background-color: #0277bd !important;
|
||||
}
|
||||
@media (prefers-color-scheme: dark) {
|
||||
html {
|
||||
background-color: var(--primary-background-color, #111111);
|
||||
color: var(--primary-text-color, #e1e1e1);
|
||||
}
|
||||
}
|
||||
body {
|
||||
height: auto;
|
||||
padding: 64px 0;
|
||||
padding: 32px 0;
|
||||
}
|
||||
.content {
|
||||
box-sizing: border-box;
|
||||
padding: 20px 16px;
|
||||
border-radius: var(--ha-card-border-radius, 12px);
|
||||
max-width: 432px;
|
||||
max-width: 560px;
|
||||
margin: 0 auto;
|
||||
box-shadow: var(
|
||||
--ha-card-box-shadow,
|
||||
rgba(0, 0, 0, 0.25) 0px 54px 55px,
|
||||
rgba(0, 0, 0, 0.12) 0px -12px 30px,
|
||||
rgba(0, 0, 0, 0.12) 0px 4px 6px,
|
||||
rgba(0, 0, 0, 0.17) 0px 12px 13px,
|
||||
rgba(0, 0, 0, 0.09) 0px -3px 5px
|
||||
);
|
||||
background: var(
|
||||
--ha-card-background,
|
||||
var(--card-background-color, white)
|
||||
);
|
||||
color: var(--primary-text-color, #212121);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.header {
|
||||
text-align: center;
|
||||
font-size: 1.96em;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-weight: 300;
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
|
||||
.header img {
|
||||
margin-right: 16px;
|
||||
height: 56px;
|
||||
width: 56px;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
html {
|
||||
color: #e1e1e1;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 450px) {
|
||||
@media (max-width: 592px) {
|
||||
.content {
|
||||
min-height: 100%;
|
||||
margin: 0;
|
||||
margin: 0 16px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -64,8 +47,7 @@
|
||||
<body id="particles">
|
||||
<div class="content">
|
||||
<div class="header">
|
||||
<img src="/static/icons/favicon-192x192.png" height="52" width="52" alt="" />
|
||||
Home Assistant
|
||||
<img src="/static/icons/favicon-192x192.png" alt="Home Assistant" />
|
||||
</div>
|
||||
<ha-onboarding></ha-onboarding>
|
||||
</div>
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { LitElement, PropertyValues } from "lit";
|
||||
import { property } from "lit/decorators";
|
||||
import { property, state } from "lit/decorators";
|
||||
import { computeLocalize, LocalizeFunc } from "../common/translations/localize";
|
||||
import { Constructor, Resources } from "../types";
|
||||
import { getLocalLanguage, getTranslation } from "../util/common-translation";
|
||||
@@ -15,13 +15,13 @@ export const litLocalizeLiteMixin = <T extends Constructor<LitElement>>(
|
||||
// Initialized to empty will prevent undefined errors if called before connected to DOM.
|
||||
@property() public localize: LocalizeFunc = empty;
|
||||
|
||||
@property() public resources?: Resources;
|
||||
|
||||
// Use browser language setup before login.
|
||||
@property() public language?: string = getLocalLanguage();
|
||||
|
||||
@property() public translationFragment?: string;
|
||||
|
||||
@state() private _resources?: Resources;
|
||||
|
||||
public connectedCallback(): void {
|
||||
super.connectedCallback();
|
||||
this._initializeLocalizeLite();
|
||||
@@ -35,22 +35,27 @@ export const litLocalizeLiteMixin = <T extends Constructor<LitElement>>(
|
||||
);
|
||||
}
|
||||
|
||||
protected updated(changedProperties: PropertyValues) {
|
||||
super.updated(changedProperties);
|
||||
protected willUpdate(changedProperties: PropertyValues) {
|
||||
super.willUpdate(changedProperties);
|
||||
if (changedProperties.get("language")) {
|
||||
this._resources = undefined;
|
||||
this._initializeLocalizeLite();
|
||||
}
|
||||
|
||||
if (changedProperties.get("translationFragment")) {
|
||||
this._initializeLocalizeLite();
|
||||
}
|
||||
|
||||
if (
|
||||
this.language &&
|
||||
this.resources &&
|
||||
this._resources &&
|
||||
(changedProperties.has("language") ||
|
||||
changedProperties.has("resources"))
|
||||
changedProperties.has("_resources"))
|
||||
) {
|
||||
computeLocalize(
|
||||
this.constructor.prototype,
|
||||
this.language,
|
||||
this.resources
|
||||
this._resources
|
||||
).then((localize) => {
|
||||
this.localize = localize;
|
||||
});
|
||||
@@ -58,7 +63,7 @@ export const litLocalizeLiteMixin = <T extends Constructor<LitElement>>(
|
||||
}
|
||||
|
||||
protected async _initializeLocalizeLite() {
|
||||
if (this.resources) {
|
||||
if (this._resources) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -68,7 +73,7 @@ export const litLocalizeLiteMixin = <T extends Constructor<LitElement>>(
|
||||
if (__DEV__) {
|
||||
setTimeout(
|
||||
() =>
|
||||
!this.resources &&
|
||||
!this._resources &&
|
||||
// eslint-disable-next-line
|
||||
console.error(
|
||||
"Forgot to pass in resources or set translationFragment for",
|
||||
@@ -84,7 +89,7 @@ export const litLocalizeLiteMixin = <T extends Constructor<LitElement>>(
|
||||
this.translationFragment!,
|
||||
this.language!
|
||||
);
|
||||
this.resources = {
|
||||
this._resources = {
|
||||
[this.language!]: data,
|
||||
};
|
||||
}
|
||||
|
@@ -1,85 +0,0 @@
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import "../components/ha-svg-icon";
|
||||
|
||||
@customElement("action-badge")
|
||||
class ActionBadge extends LitElement {
|
||||
@property() public icon!: string;
|
||||
|
||||
@property() public title!: string;
|
||||
|
||||
@property() public badgeIcon?: string;
|
||||
|
||||
@property({ type: Boolean, reflect: true }) public clickable = false;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`
|
||||
<div class="icon">
|
||||
<ha-svg-icon .path=${this.icon}></ha-svg-icon>
|
||||
${this.badgeIcon
|
||||
? html`<ha-svg-icon
|
||||
class="badge"
|
||||
.path=${this.badgeIcon}
|
||||
></ha-svg-icon>`
|
||||
: ""}
|
||||
</div>
|
||||
<div class="title">${this.title}</div>
|
||||
`;
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return css`
|
||||
:host {
|
||||
display: inline-flex;
|
||||
flex-direction: column;
|
||||
text-align: center;
|
||||
color: var(--primary-text-color);
|
||||
}
|
||||
|
||||
:host([clickable]) {
|
||||
color: var(--primary-text-color);
|
||||
}
|
||||
|
||||
.icon {
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
margin: 0 auto 8px;
|
||||
height: 40px;
|
||||
width: 40px;
|
||||
border-radius: 50%;
|
||||
border: 1px solid var(--secondary-text-color);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
:host([clickable]) .icon {
|
||||
border-color: var(--primary-color);
|
||||
border-width: 2px;
|
||||
}
|
||||
|
||||
.badge {
|
||||
position: absolute;
|
||||
color: var(--primary-color);
|
||||
bottom: -5px;
|
||||
right: -5px;
|
||||
background-color: white;
|
||||
border-radius: 50%;
|
||||
width: 18px;
|
||||
display: block;
|
||||
height: 18px;
|
||||
}
|
||||
|
||||
.title {
|
||||
min-height: 2.3em;
|
||||
word-break: break-word;
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"action-badge": ActionBadge;
|
||||
}
|
||||
}
|
6759
src/onboarding/dialogs/app-dialog.ts
Normal file
6759
src/onboarding/dialogs/app-dialog.ts
Normal file
File diff suppressed because one or more lines are too long
106
src/onboarding/dialogs/community-dialog.ts
Normal file
106
src/onboarding/dialogs/community-dialog.ts
Normal file
@@ -0,0 +1,106 @@
|
||||
import "@material/mwc-list/mwc-list";
|
||||
import { mdiOpenInNew } from "@mdi/js";
|
||||
import { LitElement, css, html, nothing } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import { LocalizeFunc } from "../../common/translations/localize";
|
||||
import { createCloseHeading } from "../../components/ha-dialog";
|
||||
import "../../components/ha-list-item";
|
||||
|
||||
@customElement("community-dialog")
|
||||
class DialogCommunity extends LitElement {
|
||||
@property({ attribute: false }) public localize?: LocalizeFunc;
|
||||
|
||||
public async showDialog(params): Promise<void> {
|
||||
this.localize = params.localize;
|
||||
}
|
||||
|
||||
public async closeDialog(): Promise<void> {
|
||||
this.localize = undefined;
|
||||
fireEvent(this, "dialog-closed", { dialog: this.localName });
|
||||
}
|
||||
|
||||
protected render() {
|
||||
if (!this.localize) {
|
||||
return nothing;
|
||||
}
|
||||
return html`<ha-dialog
|
||||
open
|
||||
hideActions
|
||||
@closed=${this.closeDialog}
|
||||
.heading=${createCloseHeading(
|
||||
undefined,
|
||||
this.localize("ui.panel.page-onboarding.welcome.community")
|
||||
)}
|
||||
>
|
||||
<mwc-list>
|
||||
<a
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
href="https://community.home-assistant.io/"
|
||||
>
|
||||
<ha-list-item hasMeta graphic="icon">
|
||||
<img src="/static/icons/favicon-192x192.png" slot="graphic" />
|
||||
${this.localize("ui.panel.page-onboarding.welcome.forums")}
|
||||
<ha-svg-icon slot="meta" .path=${mdiOpenInNew}></ha-svg-icon>
|
||||
</ha-list-item>
|
||||
</a>
|
||||
<a
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
href="https://www.home-assistant.io/newsletter/"
|
||||
>
|
||||
<ha-list-item hasMeta graphic="icon">
|
||||
<img src="/static/icons/favicon-192x192.png" slot="graphic" />
|
||||
${this.localize(
|
||||
"ui.panel.page-onboarding.welcome.open_home_newsletter"
|
||||
)}
|
||||
<ha-svg-icon slot="meta" .path=${mdiOpenInNew}></ha-svg-icon>
|
||||
</ha-list-item>
|
||||
</a>
|
||||
<a
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
href="https://www.home-assistant.io/join-chat"
|
||||
>
|
||||
<ha-list-item hasMeta graphic="icon">
|
||||
<img src="/static/images/logo_discord.png" slot="graphic" />
|
||||
${this.localize("ui.panel.page-onboarding.welcome.discord")}
|
||||
<ha-svg-icon slot="meta" .path=${mdiOpenInNew}></ha-svg-icon>
|
||||
</ha-list-item>
|
||||
</a>
|
||||
<a
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
href="https://twitter.com/home_assistant"
|
||||
>
|
||||
<ha-list-item hasMeta graphic="icon">
|
||||
<img src="/static/images/logo_twitter.png" slot="graphic" />
|
||||
${this.localize("ui.panel.page-onboarding.welcome.twitter")}
|
||||
<ha-svg-icon slot="meta" .path=${mdiOpenInNew}></ha-svg-icon>
|
||||
</ha-list-item>
|
||||
</a>
|
||||
</mwc-list>
|
||||
</ha-dialog>`;
|
||||
}
|
||||
|
||||
static styles = css`
|
||||
ha-dialog {
|
||||
--mdc-dialog-min-width: min(400px, 90vw);
|
||||
--dialog-content-padding: 0;
|
||||
}
|
||||
ha-list-item {
|
||||
height: 56px;
|
||||
--mdc-list-item-meta-size: 20px;
|
||||
}
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"community-dialog": DialogCommunity;
|
||||
}
|
||||
}
|
15
src/onboarding/dialogs/show-app-dialog.ts
Normal file
15
src/onboarding/dialogs/show-app-dialog.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import { LocalizeFunc } from "../../common/translations/localize";
|
||||
|
||||
export const loadAppDialog = () => import("./app-dialog");
|
||||
|
||||
export const showAppDialog = (
|
||||
element: HTMLElement,
|
||||
params: { localize: LocalizeFunc }
|
||||
): void => {
|
||||
fireEvent(element, "show-dialog", {
|
||||
dialogTag: "app-dialog",
|
||||
dialogImport: loadAppDialog,
|
||||
dialogParams: params,
|
||||
});
|
||||
};
|
15
src/onboarding/dialogs/show-community-dialog.ts
Normal file
15
src/onboarding/dialogs/show-community-dialog.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import { LocalizeFunc } from "../../common/translations/localize";
|
||||
|
||||
export const loadCommunityDialog = () => import("./community-dialog");
|
||||
|
||||
export const showCommunityDialog = (
|
||||
element: HTMLElement,
|
||||
params: { localize: LocalizeFunc }
|
||||
): void => {
|
||||
fireEvent(element, "show-dialog", {
|
||||
dialogTag: "community-dialog",
|
||||
dialogImport: loadCommunityDialog,
|
||||
dialogParams: params,
|
||||
});
|
||||
};
|
@@ -1,3 +1,4 @@
|
||||
import "@material/mwc-linear-progress/mwc-linear-progress";
|
||||
import {
|
||||
Auth,
|
||||
createConnection,
|
||||
@@ -5,35 +6,45 @@ import {
|
||||
getAuth,
|
||||
subscribeConfig,
|
||||
} from "home-assistant-js-websocket";
|
||||
import { html, PropertyValues, nothing } from "lit";
|
||||
import { PropertyValues, css, html, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import {
|
||||
enableWrite,
|
||||
loadTokens,
|
||||
saveTokens,
|
||||
} from "../common/auth/token_storage";
|
||||
import { applyThemesOnElement } from "../common/dom/apply_themes_on_element";
|
||||
import { HASSDomEvent } from "../common/dom/fire_event";
|
||||
import { extractSearchParamsObject } from "../common/url/search-params";
|
||||
import { subscribeOne } from "../common/util/subscribe-one";
|
||||
import "../components/ha-card";
|
||||
import "../components/ha-language-picker";
|
||||
import { AuthUrlSearchParams, hassUrl } from "../data/auth";
|
||||
import {
|
||||
fetchInstallationType,
|
||||
fetchOnboardingOverview,
|
||||
OnboardingResponses,
|
||||
OnboardingStep,
|
||||
fetchInstallationType,
|
||||
fetchOnboardingOverview,
|
||||
onboardIntegrationStep,
|
||||
} from "../data/onboarding";
|
||||
import { subscribeUser } from "../data/ws-user";
|
||||
import { litLocalizeLiteMixin } from "../mixins/lit-localize-lite-mixin";
|
||||
import { HassElement } from "../state/hass-element";
|
||||
import { HomeAssistant } from "../types";
|
||||
import { storeState } from "../util/ha-pref-storage";
|
||||
import { registerServiceWorker } from "../util/register-service-worker";
|
||||
import "./onboarding-analytics";
|
||||
import "./onboarding-create-user";
|
||||
import "./onboarding-loading";
|
||||
import {
|
||||
enableWrite,
|
||||
loadTokens,
|
||||
saveTokens,
|
||||
} from "../common/auth/token_storage";
|
||||
import "./onboarding-welcome";
|
||||
import "./onboarding-welcome-links";
|
||||
import { makeDialogManager } from "../dialogs/make-dialog-manager";
|
||||
|
||||
type OnboardingEvent =
|
||||
| {
|
||||
type: "init";
|
||||
result: { restore: boolean };
|
||||
}
|
||||
| {
|
||||
type: "user";
|
||||
result: OnboardingResponses["user"];
|
||||
@@ -49,13 +60,21 @@ type OnboardingEvent =
|
||||
type: "analytics";
|
||||
};
|
||||
|
||||
interface OnboardingProgressEvent {
|
||||
increase?: number;
|
||||
decrease?: number;
|
||||
progress?: number;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HASSDomEvents {
|
||||
"onboarding-step": OnboardingEvent;
|
||||
"onboarding-progress": OnboardingProgressEvent;
|
||||
}
|
||||
|
||||
interface GlobalEventHandlersEventMap {
|
||||
"onboarding-step": HASSDomEvent<OnboardingEvent>;
|
||||
"onboarding-progress": HASSDomEvent<OnboardingProgressEvent>;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,8 +84,12 @@ class HaOnboarding extends litLocalizeLiteMixin(HassElement) {
|
||||
|
||||
@property() public translationFragment = "page-onboarding";
|
||||
|
||||
@state() private _progress = 0;
|
||||
|
||||
@state() private _loading = false;
|
||||
|
||||
@state() private _init = false;
|
||||
|
||||
@state() private _restoring = false;
|
||||
|
||||
@state() private _supervisor?: boolean;
|
||||
@@ -74,29 +97,58 @@ class HaOnboarding extends litLocalizeLiteMixin(HassElement) {
|
||||
@state() private _steps?: OnboardingStep[];
|
||||
|
||||
protected render() {
|
||||
return html`<mwc-linear-progress
|
||||
.progress=${this._progress}
|
||||
></mwc-linear-progress>
|
||||
<ha-card>
|
||||
<div class="card-content">${this._renderStep()}</div>
|
||||
</ha-card>
|
||||
${this._init
|
||||
? html`<onboarding-welcome-links
|
||||
.localize=${this.localize}
|
||||
></onboarding-welcome-links>`
|
||||
: nothing}
|
||||
<div class="footer">
|
||||
<ha-language-picker
|
||||
.value=${this.language}
|
||||
.label=${""}
|
||||
nativeName
|
||||
@value-changed=${this._languageChanged}
|
||||
></ha-language-picker>
|
||||
<a
|
||||
href="https://www.home-assistant.io/getting-started/onboarding/"
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
>${this.localize("ui.panel.page-onboarding.help")}</a
|
||||
>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
private _renderStep() {
|
||||
if (this._init) {
|
||||
return html`<onboarding-welcome
|
||||
.localize=${this.localize}
|
||||
.language=${this.language}
|
||||
.supervisor=${this._supervisor}
|
||||
></onboarding-welcome>`;
|
||||
}
|
||||
|
||||
if (this._restoring) {
|
||||
return html`<onboarding-restore-backup .localize=${this.localize}>
|
||||
</onboarding-restore-backup>`;
|
||||
}
|
||||
|
||||
const step = this._curStep()!;
|
||||
|
||||
if (this._loading || !step) {
|
||||
return html`<onboarding-loading></onboarding-loading> `;
|
||||
}
|
||||
if (step.step === "user") {
|
||||
return html`
|
||||
${!this._restoring
|
||||
? html`<onboarding-create-user
|
||||
.localize=${this.localize}
|
||||
.language=${this.language}
|
||||
>
|
||||
</onboarding-create-user>`
|
||||
: ""}
|
||||
${this._supervisor
|
||||
? html`<onboarding-restore-backup
|
||||
.localize=${this.localize}
|
||||
.restoring=${this._restoring}
|
||||
@restoring=${this._restoringBackup}
|
||||
>
|
||||
</onboarding-restore-backup>`
|
||||
: ""}
|
||||
`;
|
||||
return html`<onboarding-create-user
|
||||
.localize=${this.localize}
|
||||
.language=${this.language}
|
||||
>
|
||||
</onboarding-create-user>`;
|
||||
}
|
||||
if (step.step === "core_config") {
|
||||
return html`
|
||||
@@ -114,7 +166,6 @@ class HaOnboarding extends litLocalizeLiteMixin(HassElement) {
|
||||
></onboarding-analytics>
|
||||
`;
|
||||
}
|
||||
|
||||
if (step.step === "integration") {
|
||||
return html`
|
||||
<onboarding-integrations
|
||||
@@ -133,9 +184,13 @@ class HaOnboarding extends litLocalizeLiteMixin(HassElement) {
|
||||
import("./onboarding-core-config");
|
||||
registerServiceWorker(this, false);
|
||||
this.addEventListener("onboarding-step", (ev) => this._handleStepDone(ev));
|
||||
this.addEventListener("onboarding-progress", (ev) =>
|
||||
this._handleProgress(ev)
|
||||
);
|
||||
if (window.innerWidth > 450) {
|
||||
import("./particles");
|
||||
}
|
||||
makeDialogManager(this, this.shadowRoot!);
|
||||
}
|
||||
|
||||
protected updated(changedProps: PropertyValues) {
|
||||
@@ -170,10 +225,6 @@ class HaOnboarding extends litLocalizeLiteMixin(HassElement) {
|
||||
return this._steps ? this._steps.find((stp) => !stp.done) : undefined;
|
||||
}
|
||||
|
||||
private _restoringBackup() {
|
||||
this._restoring = true;
|
||||
}
|
||||
|
||||
private async _fetchInstallationType(): Promise<void> {
|
||||
try {
|
||||
const response = await fetchInstallationType();
|
||||
@@ -222,8 +273,12 @@ class HaOnboarding extends litLocalizeLiteMixin(HassElement) {
|
||||
});
|
||||
history.replaceState(null, "", location.pathname);
|
||||
await this._connectHass(auth);
|
||||
const currentStep = steps.findIndex((stp) => !stp.done);
|
||||
const singelStepProgress = 1 / steps.length;
|
||||
this._progress = currentStep * singelStepProgress + singelStepProgress;
|
||||
} else {
|
||||
// User creating screen needs to know the installation type.
|
||||
this._init = true;
|
||||
// Init screen needs to know the installation type.
|
||||
this._fetchInstallationType();
|
||||
}
|
||||
|
||||
@@ -233,15 +288,35 @@ class HaOnboarding extends litLocalizeLiteMixin(HassElement) {
|
||||
}
|
||||
}
|
||||
|
||||
private _handleProgress(ev: HASSDomEvent<OnboardingProgressEvent>) {
|
||||
const stepSize = 1 / this._steps!.length;
|
||||
if (ev.detail.increase) {
|
||||
this._progress += ev.detail.increase * stepSize;
|
||||
}
|
||||
if (ev.detail.decrease) {
|
||||
this._progress -= ev.detail.decrease * stepSize;
|
||||
}
|
||||
if (ev.detail.progress) {
|
||||
this._progress = ev.detail.progress;
|
||||
}
|
||||
}
|
||||
|
||||
private async _handleStepDone(ev: HASSDomEvent<OnboardingEvent>) {
|
||||
const stepResult = ev.detail;
|
||||
this._steps = this._steps!.map((step) =>
|
||||
step.step === stepResult.type ? { ...step, done: true } : step
|
||||
);
|
||||
|
||||
if (stepResult.type === "user") {
|
||||
if (stepResult.type === "init") {
|
||||
this._init = false;
|
||||
this._restoring = stepResult.result.restore;
|
||||
if (!this._restoring) {
|
||||
this._progress = 0.25;
|
||||
}
|
||||
} else if (stepResult.type === "user") {
|
||||
const result = stepResult.result as OnboardingResponses["user"];
|
||||
this._loading = true;
|
||||
this._progress = 0.5;
|
||||
enableWrite();
|
||||
try {
|
||||
const auth = await getAuth({
|
||||
@@ -258,6 +333,10 @@ class HaOnboarding extends litLocalizeLiteMixin(HassElement) {
|
||||
this._loading = false;
|
||||
}
|
||||
} else if (stepResult.type === "core_config") {
|
||||
this._progress = 0.75;
|
||||
// We do nothing
|
||||
} else if (stepResult.type === "analytics") {
|
||||
this._progress = 1;
|
||||
// We do nothing
|
||||
} else if (stepResult.type === "integration") {
|
||||
this._loading = true;
|
||||
@@ -331,6 +410,14 @@ class HaOnboarding extends litLocalizeLiteMixin(HassElement) {
|
||||
subscribeOne(conn, subscribeUser),
|
||||
]);
|
||||
this.initializeHass(auth, conn);
|
||||
if (this.language && this.language !== this.hass!.language) {
|
||||
this._updateHass({
|
||||
locale: { ...this.hass!.locale, language: this.language },
|
||||
language: this.language,
|
||||
selectedLanguage: this.language,
|
||||
});
|
||||
storeState(this.hass!);
|
||||
}
|
||||
// Load config strings for integrations
|
||||
(this as any)._loadFragmentTranslations(this.hass!.language, "config");
|
||||
// Make sure hass is initialized + the config/user callbacks have called.
|
||||
@@ -338,6 +425,60 @@ class HaOnboarding extends litLocalizeLiteMixin(HassElement) {
|
||||
setTimeout(resolve, 0);
|
||||
});
|
||||
}
|
||||
|
||||
private _languageChanged(ev: CustomEvent) {
|
||||
const language = ev.detail.value;
|
||||
this.language = language;
|
||||
if (this.hass) {
|
||||
this._updateHass({
|
||||
locale: { ...this.hass!.locale, language },
|
||||
language,
|
||||
selectedLanguage: language,
|
||||
});
|
||||
storeState(this.hass!);
|
||||
} else {
|
||||
try {
|
||||
localStorage.setItem("selectedLanguage", JSON.stringify(language));
|
||||
} catch (err: any) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static styles = css`
|
||||
mwc-linear-progress {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
z-index: 10;
|
||||
}
|
||||
.footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
ha-language-picker {
|
||||
display: block;
|
||||
width: 200px;
|
||||
margin-top: 8px;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
--ha-select-height: 40px;
|
||||
--mdc-select-fill-color: none;
|
||||
--mdc-select-label-ink-color: var(--primary-text-color, #212121);
|
||||
--mdc-select-ink-color: var(--primary-text-color, #212121);
|
||||
--mdc-select-idle-line-color: transparent;
|
||||
--mdc-select-hover-line-color: transparent;
|
||||
--mdc-select-dropdown-icon-color: var(--primary-text-color, #212121);
|
||||
--mdc-shape-small: 0;
|
||||
}
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: var(--primary-text-color);
|
||||
margin-right: 16px;
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
declare global {
|
||||
|
@@ -9,8 +9,6 @@ class IntegrationBadge extends LitElement {
|
||||
|
||||
@property() public title!: string;
|
||||
|
||||
@property() public badgeIcon?: string;
|
||||
|
||||
@property({ type: Boolean }) public darkOptimizedIcon?: boolean;
|
||||
|
||||
@property({ type: Boolean, reflect: true }) public clickable = false;
|
||||
@@ -27,12 +25,6 @@ class IntegrationBadge extends LitElement {
|
||||
})}
|
||||
referrerpolicy="no-referrer"
|
||||
/>
|
||||
${this.badgeIcon
|
||||
? html`<ha-svg-icon
|
||||
class="badge"
|
||||
.path=${this.badgeIcon}
|
||||
></ha-svg-icon>`
|
||||
: ""}
|
||||
</div>
|
||||
<div class="title">${this.title}</div>
|
||||
`;
|
||||
@@ -47,10 +39,6 @@ class IntegrationBadge extends LitElement {
|
||||
color: var(--primary-text-color);
|
||||
}
|
||||
|
||||
:host([clickable]) {
|
||||
color: var(--primary-text-color);
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
@@ -66,18 +54,6 @@ class IntegrationBadge extends LitElement {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.badge {
|
||||
position: absolute;
|
||||
color: white;
|
||||
bottom: -7px;
|
||||
right: -10px;
|
||||
background-color: var(--label-badge-green);
|
||||
border-radius: 50%;
|
||||
display: block;
|
||||
--mdc-icon-size: 18px;
|
||||
border: 2px solid white;
|
||||
}
|
||||
|
||||
.title {
|
||||
min-height: 2.3em;
|
||||
word-break: break-word;
|
||||
|
@@ -1,4 +1,5 @@
|
||||
import "@material/mwc-button/mwc-button";
|
||||
import { mdiOpenInNew } from "@mdi/js";
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
@@ -8,6 +9,7 @@ import { Analytics, setAnalyticsPreferences } from "../data/analytics";
|
||||
import { onboardAnalyticsStep } from "../data/onboarding";
|
||||
import type { HomeAssistant } from "../types";
|
||||
import { documentationUrl } from "../util/documentation-url";
|
||||
import { onBoardingStyles } from "./styles";
|
||||
|
||||
@customElement("onboarding-analytics")
|
||||
class OnboardingAnalytics extends LitElement {
|
||||
@@ -23,7 +25,18 @@ class OnboardingAnalytics extends LitElement {
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`
|
||||
<h1>${this.localize("ui.panel.page-onboarding.analytics.header")}</h1>
|
||||
<p>${this.localize("ui.panel.page-onboarding.analytics.intro")}</p>
|
||||
<p>
|
||||
<a
|
||||
.href=${documentationUrl(this.hass, "/integrations/analytics/")}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
${this.localize("ui.panel.page-onboarding.analytics.learn_more")}
|
||||
<ha-svg-icon .path=${mdiOpenInNew}></ha-svg-icon>
|
||||
</a>
|
||||
</p>
|
||||
<ha-analytics
|
||||
translation_key_panel="page-onboarding"
|
||||
@analytics-preferences-changed=${this._preferencesChanged}
|
||||
@@ -33,16 +46,13 @@ class OnboardingAnalytics extends LitElement {
|
||||
</ha-analytics>
|
||||
${this._error ? html`<div class="error">${this._error}</div>` : ""}
|
||||
<div class="footer">
|
||||
<mwc-button @click=${this._save} .disabled=${!this._analyticsDetails}>
|
||||
<mwc-button
|
||||
unelevated
|
||||
@click=${this._save}
|
||||
.disabled=${!this._analyticsDetails}
|
||||
>
|
||||
${this.localize("ui.panel.page-onboarding.analytics.finish")}
|
||||
</mwc-button>
|
||||
<a
|
||||
.href=${documentationUrl(this.hass, "/integrations/analytics/")}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
${this.localize("ui.panel.page-onboarding.analytics.learn_more")}
|
||||
</a>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
@@ -81,27 +91,19 @@ class OnboardingAnalytics extends LitElement {
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return css`
|
||||
p {
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
}
|
||||
.error {
|
||||
color: var(--error-color);
|
||||
}
|
||||
.footer {
|
||||
margin-top: 16px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
a {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
`;
|
||||
|
||||
// footer is direction reverse to tab to "NEXT" first
|
||||
return [
|
||||
onBoardingStyles,
|
||||
css`
|
||||
.error {
|
||||
color: var(--error-color);
|
||||
}
|
||||
a {
|
||||
color: var(--primary-color);
|
||||
text-decoration: none;
|
||||
--mdc-icon-size: 14px;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -13,22 +13,12 @@ import { fireEvent } from "../common/dom/fire_event";
|
||||
import type { LocalizeFunc } from "../common/translations/localize";
|
||||
import "../components/ha-alert";
|
||||
import "../components/ha-country-picker";
|
||||
import "../components/ha-currency-picker";
|
||||
import "../components/ha-formfield";
|
||||
import "../components/ha-language-picker";
|
||||
import "../components/ha-radio";
|
||||
import type { HaRadio } from "../components/ha-radio";
|
||||
import "../components/ha-textfield";
|
||||
import type { HaTextField } from "../components/ha-textfield";
|
||||
import "../components/ha-timezone-picker";
|
||||
import "../components/map/ha-locations-editor";
|
||||
import { ConfigUpdateValues, saveCoreConfig } from "../data/core";
|
||||
import { countryCurrency } from "../data/currency";
|
||||
import { onboardCoreConfigStep } from "../data/onboarding";
|
||||
import type { HomeAssistant, ValueChangedEvent } from "../types";
|
||||
import { getLocalLanguage } from "../util/common-translation";
|
||||
import "./onboarding-location";
|
||||
import "./onboarding-name";
|
||||
|
||||
@customElement("onboarding-core-config")
|
||||
class OnboardingCoreConfig extends LitElement {
|
||||
@@ -38,32 +28,26 @@ class OnboardingCoreConfig extends LitElement {
|
||||
|
||||
@state() private _working = false;
|
||||
|
||||
@state() private _name?: ConfigUpdateValues["location_name"];
|
||||
|
||||
@state() private _location?: [number, number];
|
||||
|
||||
@state() private _elevation?: string;
|
||||
private _elevation = "0";
|
||||
|
||||
@state() private _unitSystem?: ConfigUpdateValues["unit_system"];
|
||||
private _unitSystem: ConfigUpdateValues["unit_system"] = "metric";
|
||||
|
||||
@state() private _currency?: ConfigUpdateValues["currency"];
|
||||
private _currency: ConfigUpdateValues["currency"] = "EUR";
|
||||
|
||||
@state() private _timeZone?: ConfigUpdateValues["time_zone"];
|
||||
private _timeZone: ConfigUpdateValues["time_zone"] =
|
||||
Intl.DateTimeFormat?.().resolvedOptions?.().timeZone;
|
||||
|
||||
@state() private _language: ConfigUpdateValues["language"];
|
||||
private _language: ConfigUpdateValues["language"] = getLocalLanguage();
|
||||
|
||||
@state() private _country?: ConfigUpdateValues["country"];
|
||||
|
||||
@state() private _error?: string;
|
||||
|
||||
@state() private _skipCore = false;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this._name) {
|
||||
return html`<onboarding-name
|
||||
.hass=${this.hass}
|
||||
.onboardingLocalize=${this.onboardingLocalize}
|
||||
@value-changed=${this._nameChanged}
|
||||
></onboarding-name>`;
|
||||
}
|
||||
if (!this._location) {
|
||||
return html`<onboarding-location
|
||||
.hass=${this.hass}
|
||||
@@ -71,156 +55,34 @@ class OnboardingCoreConfig extends LitElement {
|
||||
@value-changed=${this._locationChanged}
|
||||
></onboarding-location>`;
|
||||
}
|
||||
if (this._skipCore) {
|
||||
return html`<div class="row center">
|
||||
<ha-circular-progress active></ha-circular-progress>
|
||||
</div>`;
|
||||
}
|
||||
return html`
|
||||
${
|
||||
this._error
|
||||
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
|
||||
: nothing
|
||||
}
|
||||
${this._error
|
||||
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
|
||||
: nothing}
|
||||
|
||||
<p>
|
||||
${this.onboardingLocalize(
|
||||
"ui.panel.page-onboarding.core-config.intro_core_config"
|
||||
)}
|
||||
${this.onboardingLocalize(
|
||||
"ui.panel.page-onboarding.core-config.country_intro"
|
||||
)}
|
||||
</p>
|
||||
|
||||
<div class="row">
|
||||
<ha-country-picker
|
||||
class="flex"
|
||||
.language=${this.hass.locale.language}
|
||||
.label=${
|
||||
this.hass.localize(
|
||||
"ui.panel.config.core.section.core.core_config.country"
|
||||
) || "Country"
|
||||
}
|
||||
name="country"
|
||||
required
|
||||
.disabled=${this._working}
|
||||
.value=${this._countryValue}
|
||||
@value-changed=${this._handleValueChanged}
|
||||
>
|
||||
</ha-country-picker>
|
||||
<ha-language-picker
|
||||
class="flex"
|
||||
.hass=${this.hass}
|
||||
nativeName
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.core.section.core.core_config.language"
|
||||
)}
|
||||
name="language"
|
||||
required
|
||||
.value=${this._languageValue}
|
||||
.disabled=${this._working}
|
||||
@value-changed=${this._handleValueChanged}
|
||||
>
|
||||
</ha-language-picker>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<ha-timezone-picker
|
||||
class="flex"
|
||||
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.core.section.core.core_config.time_zone"
|
||||
)}
|
||||
name="timeZone"
|
||||
.disabled=${this._working}
|
||||
.value=${this._timeZoneValue}
|
||||
@value-changed=${this._handleValueChanged}
|
||||
>
|
||||
</ha-timezone-picker>
|
||||
|
||||
<ha-textfield
|
||||
class="flex"
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.core.section.core.core_config.elevation"
|
||||
)}
|
||||
name="elevation"
|
||||
type="number"
|
||||
.disabled=${this._working}
|
||||
.value=${this._elevationValue}
|
||||
.suffix=${this.hass.localize(
|
||||
"ui.panel.config.core.section.core.core_config.elevation_meters"
|
||||
)}
|
||||
@change=${this._handleChange}
|
||||
>
|
||||
</ha-textfield>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="flex">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.core.section.core.core_config.unit_system"
|
||||
)}
|
||||
</div>
|
||||
<div class="radio-group">
|
||||
<ha-formfield
|
||||
.label=${html`${this.hass.localize(
|
||||
"ui.panel.config.core.section.core.core_config.unit_system_metric"
|
||||
)}
|
||||
<div class="secondary">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.core.section.core.core_config.metric_example"
|
||||
)}
|
||||
</div>`}
|
||||
>
|
||||
<ha-radio
|
||||
name="unit_system"
|
||||
value="metric"
|
||||
.checked=${this._unitSystemValue === "metric"}
|
||||
@change=${this._unitSystemChanged}
|
||||
.disabled=${this._working}
|
||||
></ha-radio>
|
||||
</ha-formfield>
|
||||
<ha-formfield
|
||||
.label=${html`${this.hass.localize(
|
||||
"ui.panel.config.core.section.core.core_config.unit_system_us_customary"
|
||||
)}
|
||||
<div class="secondary">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.core.section.core.core_config.us_customary_example"
|
||||
)}
|
||||
</div>`}
|
||||
>
|
||||
<ha-radio
|
||||
name="unit_system"
|
||||
value="us_customary"
|
||||
.checked=${this._unitSystemValue === "us_customary"}
|
||||
@change=${this._unitSystemChanged}
|
||||
.disabled=${this._working}
|
||||
></ha-radio>
|
||||
</ha-formfield>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="flex">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.core.section.core.core_config.currency"
|
||||
)}<br />
|
||||
<a
|
||||
href="https://en.wikipedia.org/wiki/ISO_4217#Active_codes"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>${this.hass.localize(
|
||||
"ui.panel.config.core.section.core.core_config.find_currency_value"
|
||||
)}</a
|
||||
>
|
||||
</div>
|
||||
<ha-currency-picker
|
||||
class="flex"
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.core.section.core.core_config.currency"
|
||||
)}
|
||||
name="currency"
|
||||
.disabled=${this._working}
|
||||
.value=${this._currencyValue}
|
||||
@value-changed=${this._handleValueChanged}
|
||||
>
|
||||
</ha-currency-picker
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<ha-country-picker
|
||||
class="flex"
|
||||
.language=${this.hass.locale.language}
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.core.section.core.core_config.country"
|
||||
) || "Country"}
|
||||
required
|
||||
.disabled=${this._working}
|
||||
.value=${this._countryValue}
|
||||
@value-changed=${this._handleCountryChanged}
|
||||
>
|
||||
</ha-country-picker>
|
||||
|
||||
<div class="footer">
|
||||
<mwc-button @click=${this._save} .disabled=${this._working}>
|
||||
@@ -232,20 +94,6 @@ class OnboardingCoreConfig extends LitElement {
|
||||
`;
|
||||
}
|
||||
|
||||
protected willUpdate(changedProps: PropertyValues): void {
|
||||
if (!changedProps.has("_country") || !this._country) {
|
||||
return;
|
||||
}
|
||||
if (!this._currency) {
|
||||
this._currency = countryCurrency[this._country];
|
||||
}
|
||||
if (!this._unitSystem) {
|
||||
this._unitSystem = ["US", "MM", "LR"].includes(this._country)
|
||||
? "us_customary"
|
||||
: "metric";
|
||||
}
|
||||
}
|
||||
|
||||
protected firstUpdated(changedProps: PropertyValues) {
|
||||
super.firstUpdated(changedProps);
|
||||
this.addEventListener("keyup", (ev) => {
|
||||
@@ -255,84 +103,69 @@ class OnboardingCoreConfig extends LitElement {
|
||||
});
|
||||
}
|
||||
|
||||
private get _elevationValue() {
|
||||
return this._elevation !== undefined ? this._elevation : 0;
|
||||
}
|
||||
|
||||
private get _timeZoneValue() {
|
||||
return this._timeZone || "";
|
||||
}
|
||||
|
||||
private get _languageValue() {
|
||||
return this._language || "";
|
||||
}
|
||||
|
||||
private get _countryValue() {
|
||||
return this._country || "";
|
||||
}
|
||||
|
||||
private get _unitSystemValue() {
|
||||
return this._unitSystem !== undefined ? this._unitSystem : "metric";
|
||||
}
|
||||
|
||||
private get _currencyValue() {
|
||||
return this._currency !== undefined ? this._currency : "";
|
||||
}
|
||||
|
||||
private _handleValueChanged(ev: ValueChangedEvent<string>) {
|
||||
const target = ev.currentTarget as HTMLElement;
|
||||
this[`_${target.getAttribute("name")}`] = ev.detail.value;
|
||||
}
|
||||
|
||||
private _handleChange(ev: Event) {
|
||||
const target = ev.currentTarget as HaTextField;
|
||||
this[`_${target.name}`] = target.value;
|
||||
}
|
||||
|
||||
private _nameChanged(ev: CustomEvent) {
|
||||
this._name = ev.detail.value;
|
||||
private _handleCountryChanged(ev: ValueChangedEvent<string>) {
|
||||
this._country = ev.detail.value;
|
||||
}
|
||||
|
||||
private async _locationChanged(ev) {
|
||||
this._location = ev.detail.value.location;
|
||||
this._country = ev.detail.value.country;
|
||||
this._elevation = ev.detail.value.elevation;
|
||||
this._currency = ev.detail.value.currency;
|
||||
this._language = ev.detail.value.language || getLocalLanguage();
|
||||
this._timeZone =
|
||||
ev.detail.value.timezone ||
|
||||
Intl.DateTimeFormat?.().resolvedOptions?.().timeZone;
|
||||
this._unitSystem = ev.detail.value.unit_system;
|
||||
if (ev.detail.value.country) {
|
||||
this._country = ev.detail.value.country;
|
||||
}
|
||||
if (ev.detail.value.elevation) {
|
||||
this._elevation = ev.detail.value.elevation;
|
||||
}
|
||||
if (ev.detail.value.currency) {
|
||||
this._currency = ev.detail.value.currency;
|
||||
}
|
||||
if (ev.detail.value.language) {
|
||||
this._language = ev.detail.value.language;
|
||||
}
|
||||
if (ev.detail.value.timezone) {
|
||||
this._timeZone = ev.detail.value.timezone;
|
||||
}
|
||||
if (ev.detail.value.unit_system) {
|
||||
this._unitSystem = ev.detail.value.unit_system;
|
||||
}
|
||||
if (this._country) {
|
||||
this._skipCore = true;
|
||||
this._save(ev);
|
||||
return;
|
||||
}
|
||||
fireEvent(this, "onboarding-progress", { increase: 0.5 });
|
||||
await this.updateComplete;
|
||||
setTimeout(
|
||||
() => this.renderRoot.querySelector("ha-textfield")!.focus(),
|
||||
() => this.renderRoot.querySelector("ha-country-picker")!.focus(),
|
||||
100
|
||||
);
|
||||
}
|
||||
|
||||
private _unitSystemChanged(ev: CustomEvent) {
|
||||
this._unitSystem = (ev.target as HaRadio).value as
|
||||
| "metric"
|
||||
| "us_customary";
|
||||
}
|
||||
|
||||
private async _save(ev) {
|
||||
if (!this._location) {
|
||||
if (!this._location || !this._country) {
|
||||
return;
|
||||
}
|
||||
ev.preventDefault();
|
||||
this._working = true;
|
||||
try {
|
||||
await saveCoreConfig(this.hass, {
|
||||
location_name: this._name,
|
||||
location_name: this.onboardingLocalize(
|
||||
"ui.panel.page-onboarding.core-config.location_name_default"
|
||||
),
|
||||
latitude: this._location[0],
|
||||
longitude: this._location[1],
|
||||
elevation: Number(this._elevationValue),
|
||||
unit_system: this._unitSystemValue,
|
||||
time_zone: this._timeZoneValue || "UTC",
|
||||
currency: this._currencyValue || "EUR",
|
||||
country: this._countryValue,
|
||||
language: this._languageValue,
|
||||
elevation: Number(this._elevation),
|
||||
unit_system:
|
||||
this._unitSystem || ["US", "MM", "LR"].includes(this._country)
|
||||
? "us_customary"
|
||||
: "metric",
|
||||
time_zone: this._timeZone || "UTC",
|
||||
currency: this._currency || countryCurrency[this._country] || "EUR",
|
||||
country: this._country,
|
||||
language: this._language,
|
||||
});
|
||||
const result = await onboardCoreConfigStep(this.hass);
|
||||
fireEvent(this, "onboarding-step", {
|
||||
@@ -340,6 +173,7 @@ class OnboardingCoreConfig extends LitElement {
|
||||
result,
|
||||
});
|
||||
} catch (err: any) {
|
||||
this._skipCore = false;
|
||||
this._working = false;
|
||||
this._error = err.message;
|
||||
}
|
||||
@@ -380,6 +214,10 @@ class OnboardingCoreConfig extends LitElement {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.center {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.row > * {
|
||||
margin: 0 8px;
|
||||
}
|
||||
|
@@ -1,7 +1,6 @@
|
||||
import "@material/mwc-button";
|
||||
import { genClientId } from "home-assistant-js-websocket";
|
||||
import {
|
||||
css,
|
||||
CSSResultGroup,
|
||||
html,
|
||||
LitElement,
|
||||
@@ -16,6 +15,8 @@ import type { HaForm } from "../components/ha-form/ha-form";
|
||||
import { HaFormDataContainer, HaFormSchema } from "../components/ha-form/types";
|
||||
import { onboardUserStep } from "../data/onboarding";
|
||||
import { ValueChangedEvent } from "../types";
|
||||
import { onBoardingStyles } from "./styles";
|
||||
import { debounce } from "../common/util/debounce";
|
||||
|
||||
const CREATE_USER_SCHEMA: HaFormSchema[] = [
|
||||
{
|
||||
@@ -58,7 +59,7 @@ class OnboardingCreateUser extends LitElement {
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`
|
||||
<p>${this.localize("ui.panel.page-onboarding.intro")}</p>
|
||||
<h1>${this.localize("ui.panel.page-onboarding.user.header")}</h1>
|
||||
<p>${this.localize("ui.panel.page-onboarding.user.intro")}</p>
|
||||
|
||||
${this._errorMsg
|
||||
@@ -67,25 +68,27 @@ class OnboardingCreateUser extends LitElement {
|
||||
|
||||
<ha-form
|
||||
.computeLabel=${this._computeLabel(this.localize)}
|
||||
.computeHelper=${this._computeHelper(this.localize)}
|
||||
.data=${this._newUser}
|
||||
.disabled=${this._loading}
|
||||
.error=${this._formError}
|
||||
.schema=${CREATE_USER_SCHEMA}
|
||||
@value-changed=${this._handleValueChanged}
|
||||
></ha-form>
|
||||
|
||||
<mwc-button
|
||||
raised
|
||||
@click=${this._submitForm}
|
||||
.disabled=${this._loading ||
|
||||
!this._newUser.name ||
|
||||
!this._newUser.username ||
|
||||
!this._newUser.password ||
|
||||
!this._newUser.password_confirm ||
|
||||
this._newUser.password !== this._newUser.password_confirm}
|
||||
>
|
||||
${this.localize("ui.panel.page-onboarding.user.create_account")}
|
||||
</mwc-button>
|
||||
<div class="footer">
|
||||
<mwc-button
|
||||
unelevated
|
||||
@click=${this._submitForm}
|
||||
.disabled=${this._loading ||
|
||||
!this._newUser.name ||
|
||||
!this._newUser.username ||
|
||||
!this._newUser.password ||
|
||||
!this._newUser.password_confirm ||
|
||||
this._newUser.password !== this._newUser.password_confirm}
|
||||
>
|
||||
${this.localize("ui.panel.page-onboarding.user.create_account")}
|
||||
</mwc-button>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -111,20 +114,48 @@ class OnboardingCreateUser extends LitElement {
|
||||
localize(`ui.panel.page-onboarding.user.data.${schema.name}`);
|
||||
}
|
||||
|
||||
private _computeHelper(localize) {
|
||||
return (schema: HaFormSchema) =>
|
||||
localize(`ui.panel.page-onboarding.user.helper.${schema.name}`);
|
||||
}
|
||||
|
||||
private _handleValueChanged(
|
||||
ev: ValueChangedEvent<HaFormDataContainer>
|
||||
): void {
|
||||
const nameChanged = ev.detail.value.name !== this._newUser.name;
|
||||
const passwordChanged =
|
||||
ev.detail.value.password !== this._newUser.password ||
|
||||
ev.detail.value.password_confirm !== this._newUser.password_confirm;
|
||||
this._newUser = ev.detail.value;
|
||||
if (nameChanged) {
|
||||
this._maybePopulateUsername();
|
||||
}
|
||||
if (passwordChanged) {
|
||||
if (this._formError.password_confirm) {
|
||||
this._checkPasswordMatch();
|
||||
} else {
|
||||
this._debouncedCheckPasswordMatch();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _debouncedCheckPasswordMatch = debounce(
|
||||
() => this._checkPasswordMatch(),
|
||||
500
|
||||
);
|
||||
|
||||
private _checkPasswordMatch(): void {
|
||||
const old = this._formError.password_confirm;
|
||||
this._formError.password_confirm =
|
||||
this._newUser.password_confirm &&
|
||||
this._newUser.password !== this._newUser.password_confirm
|
||||
? this.localize(
|
||||
"ui.panel.page-onboarding.user.error.password_not_match"
|
||||
)
|
||||
: "";
|
||||
if (old !== this._formError.password_confirm) {
|
||||
this.requestUpdate("_formError");
|
||||
}
|
||||
}
|
||||
|
||||
private _maybePopulateUsername(): void {
|
||||
@@ -167,14 +198,7 @@ class OnboardingCreateUser extends LitElement {
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return css`
|
||||
mwc-button {
|
||||
margin: 32px 0 0;
|
||||
text-align: center;
|
||||
display: block;
|
||||
text-align: right;
|
||||
}
|
||||
`;
|
||||
return onBoardingStyles;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,14 +1,12 @@
|
||||
import "@material/mwc-button/mwc-button";
|
||||
import { mdiCheck, mdiDotsHorizontal } from "@mdi/js";
|
||||
import { UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||
import {
|
||||
css,
|
||||
CSSResultGroup,
|
||||
html,
|
||||
LitElement,
|
||||
nothing,
|
||||
PropertyValues,
|
||||
TemplateResult,
|
||||
css,
|
||||
html,
|
||||
nothing,
|
||||
} from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { isComponentLoaded } from "../common/config/is_component_loaded";
|
||||
@@ -16,23 +14,14 @@ import { fireEvent } from "../common/dom/fire_event";
|
||||
import { stringCompare } from "../common/string/compare";
|
||||
import { LocalizeFunc } from "../common/translations/localize";
|
||||
import { ConfigEntry, subscribeConfigEntries } from "../data/config_entries";
|
||||
import {
|
||||
getConfigFlowInProgressCollection,
|
||||
localizeConfigFlowTitle,
|
||||
subscribeConfigFlowInProgress,
|
||||
} from "../data/config_flow";
|
||||
import { subscribeConfigFlowInProgress } from "../data/config_flow";
|
||||
import { DataEntryFlowProgress } from "../data/data_entry_flow";
|
||||
import { domainToName } from "../data/integration";
|
||||
import { scanUSBDevices } from "../data/usb";
|
||||
import {
|
||||
loadConfigFlowDialog,
|
||||
showConfigFlowDialog,
|
||||
} from "../dialogs/config-flow/show-dialog-config-flow";
|
||||
import { SubscribeMixin } from "../mixins/subscribe-mixin";
|
||||
import { showAddIntegrationDialog } from "../panels/config/integrations/show-add-integration-dialog";
|
||||
import { HomeAssistant } from "../types";
|
||||
import "./action-badge";
|
||||
import "./integration-badge";
|
||||
import { onBoardingStyles } from "./styles";
|
||||
|
||||
const HIDDEN_DOMAINS = new Set([
|
||||
"hassio",
|
||||
@@ -63,7 +52,7 @@ class OnboardingIntegrations extends SubscribeMixin(LitElement) {
|
||||
integrations.add(flow.handler);
|
||||
}
|
||||
}
|
||||
this.hass.loadBackendTranslation("config", Array.from(integrations));
|
||||
this.hass.loadBackendTranslation("title", Array.from(integrations));
|
||||
}),
|
||||
subscribeConfigEntries(
|
||||
this.hass,
|
||||
@@ -109,65 +98,68 @@ class OnboardingIntegrations extends SubscribeMixin(LitElement) {
|
||||
return nothing;
|
||||
}
|
||||
// Render discovered and existing entries together sorted by localized title.
|
||||
const entries: Array<[string, TemplateResult]> = this._entries.map(
|
||||
(entry) => {
|
||||
const title =
|
||||
entry.title ||
|
||||
domainToName(this.hass.localize, entry.domain) ||
|
||||
entry.domain;
|
||||
return [
|
||||
title,
|
||||
html`
|
||||
<integration-badge
|
||||
.domain=${entry.domain}
|
||||
.title=${title}
|
||||
.badgeIcon=${mdiCheck}
|
||||
.darkOptimizedIcon=${this.hass.themes?.darkMode}
|
||||
></integration-badge>
|
||||
`,
|
||||
];
|
||||
}
|
||||
const entries: Array<[string, string]> = this._entries.map((entry) => [
|
||||
entry.domain,
|
||||
domainToName(this.hass.localize, entry.domain),
|
||||
]);
|
||||
const discovered: Array<[string, string]> = this._discovered.map((flow) => [
|
||||
flow.handler,
|
||||
domainToName(this.hass.localize, flow.handler),
|
||||
]);
|
||||
let domains = [...entries, ...discovered].sort((a, b) =>
|
||||
stringCompare(a[0], b[0], this.hass.locale.language)
|
||||
);
|
||||
const discovered: Array<[string, TemplateResult]> = this._discovered.map(
|
||||
(flow) => {
|
||||
const title = localizeConfigFlowTitle(this.hass.localize, flow);
|
||||
return [
|
||||
title,
|
||||
html`
|
||||
<button .flowId=${flow.flow_id} @click=${this._continueFlow}>
|
||||
<integration-badge
|
||||
clickable
|
||||
.domain=${flow.handler}
|
||||
.title=${title}
|
||||
.darkOptimizedIcon=${this.hass.themes?.darkMode}
|
||||
></integration-badge>
|
||||
</button>
|
||||
`,
|
||||
];
|
||||
|
||||
const foundDevices = domains.length;
|
||||
|
||||
if (domains.length > 12) {
|
||||
const uniqueDomains: Set<string> = new Set();
|
||||
domains.forEach(([domain]) => {
|
||||
uniqueDomains.add(domain);
|
||||
});
|
||||
if (uniqueDomains.size < domains.length) {
|
||||
domains = domains.filter(([domain]) => {
|
||||
if (uniqueDomains.has(domain)) {
|
||||
uniqueDomains.delete(domain);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
);
|
||||
const content = [...entries, ...discovered]
|
||||
.sort((a, b) => stringCompare(a[0], b[0], this.hass.locale.language))
|
||||
.map((item) => item[1]);
|
||||
if (domains.length > 12) {
|
||||
domains = domains.slice(0, 11);
|
||||
}
|
||||
}
|
||||
|
||||
return html`
|
||||
<h1>
|
||||
${this.onboardingLocalize(
|
||||
"ui.panel.page-onboarding.integration.header"
|
||||
)}
|
||||
</h1>
|
||||
<p>
|
||||
${this.onboardingLocalize("ui.panel.page-onboarding.integration.intro")}
|
||||
</p>
|
||||
<div class="badges">
|
||||
${content}
|
||||
<button @click=${this._createFlow}>
|
||||
<action-badge
|
||||
clickable
|
||||
title=${this.onboardingLocalize(
|
||||
"ui.panel.page-onboarding.integration.more_integrations"
|
||||
)}
|
||||
.icon=${mdiDotsHorizontal}
|
||||
></action-badge>
|
||||
</button>
|
||||
${domains.map(
|
||||
([domain, title]) =>
|
||||
html`<integration-badge
|
||||
.domain=${domain}
|
||||
.title=${title}
|
||||
.darkOptimizedIcon=${this.hass.themes?.darkMode}
|
||||
></integration-badge>`
|
||||
)}
|
||||
${foundDevices > domains.length
|
||||
? html`<div class="more">
|
||||
${this.onboardingLocalize(
|
||||
"ui.panel.page-onboarding.integration.more_integrations",
|
||||
{ count: foundDevices - domains.length }
|
||||
)}
|
||||
</div>`
|
||||
: nothing}
|
||||
</div>
|
||||
<div class="footer">
|
||||
<mwc-button @click=${this._finish}>
|
||||
<mwc-button unelevated @click=${this._finish}>
|
||||
${this.onboardingLocalize(
|
||||
"ui.panel.page-onboarding.integration.finish"
|
||||
)}
|
||||
@@ -178,22 +170,8 @@ class OnboardingIntegrations extends SubscribeMixin(LitElement) {
|
||||
|
||||
protected firstUpdated(changedProps: PropertyValues) {
|
||||
super.firstUpdated(changedProps);
|
||||
this.hass.loadBackendTranslation("title", undefined, true);
|
||||
this.hass.loadBackendTranslation("title");
|
||||
this._scanUSBDevices();
|
||||
loadConfigFlowDialog();
|
||||
}
|
||||
|
||||
private _createFlow() {
|
||||
showAddIntegrationDialog(this);
|
||||
}
|
||||
|
||||
private _continueFlow(ev) {
|
||||
showConfigFlowDialog(this, {
|
||||
continueFlowId: ev.currentTarget.flowId,
|
||||
dialogClosedCallback: () => {
|
||||
getConfigFlowInProgressCollection(this.hass!.connection).refresh();
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
private async _scanUSBDevices() {
|
||||
@@ -210,34 +188,23 @@ class OnboardingIntegrations extends SubscribeMixin(LitElement) {
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return css`
|
||||
p {
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
}
|
||||
.badges {
|
||||
margin-top: 24px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-start;
|
||||
align-items: flex-start;
|
||||
}
|
||||
.badges > * {
|
||||
width: 96px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
button {
|
||||
cursor: pointer;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
background: 0;
|
||||
font: inherit;
|
||||
}
|
||||
.footer {
|
||||
text-align: right;
|
||||
}
|
||||
`;
|
||||
return [
|
||||
onBoardingStyles,
|
||||
css`
|
||||
.badges {
|
||||
margin-top: 24px;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(106px, 1fr));
|
||||
row-gap: 24px;
|
||||
}
|
||||
.more {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -4,7 +4,7 @@ import { customElement } from "lit/decorators";
|
||||
@customElement("onboarding-loading")
|
||||
class OnboardingLoading extends LitElement {
|
||||
protected render(): TemplateResult {
|
||||
return html` <div class="loader"></div> `;
|
||||
return html`<div class="loader"></div>`;
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
|
@@ -1,5 +1,10 @@
|
||||
import "@material/mwc-button/mwc-button";
|
||||
import { mdiCrosshairsGps, mdiMapMarker, mdiMapSearchOutline } from "@mdi/js";
|
||||
import {
|
||||
mdiCrosshairsGps,
|
||||
mdiMagnify,
|
||||
mdiMapMarker,
|
||||
mdiMapSearchOutline,
|
||||
} from "@mdi/js";
|
||||
import {
|
||||
css,
|
||||
CSSResultGroup,
|
||||
@@ -30,6 +35,7 @@ import {
|
||||
reverseGeocode,
|
||||
searchPlaces,
|
||||
} from "../data/openstreetmap";
|
||||
import { onBoardingStyles } from "./styles";
|
||||
|
||||
const AMSTERDAM: [number, number] = [52.3731339, 4.8903147];
|
||||
const mql = matchMedia("(prefers-color-scheme: dark)");
|
||||
@@ -43,7 +49,7 @@ class OnboardingLocation extends LitElement {
|
||||
|
||||
@state() private _working = false;
|
||||
|
||||
@state() private _location?: [number, number];
|
||||
@state() private _location: [number, number] = AMSTERDAM;
|
||||
|
||||
@state() private _places?: OpenStreetMapPlace[] | null;
|
||||
|
||||
@@ -87,6 +93,11 @@ class OnboardingLocation extends LitElement {
|
||||
);
|
||||
|
||||
return html`
|
||||
<h1>
|
||||
${this.onboardingLocalize(
|
||||
"ui.panel.page-onboarding.core-config.location_header"
|
||||
)}
|
||||
</h1>
|
||||
${this._error
|
||||
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
|
||||
: nothing}
|
||||
@@ -97,78 +108,85 @@ class OnboardingLocation extends LitElement {
|
||||
)}
|
||||
</p>
|
||||
|
||||
<ha-textfield
|
||||
label=${this.onboardingLocalize(
|
||||
"ui.panel.page-onboarding.core-config.address_label"
|
||||
)}
|
||||
.disabled=${this._working}
|
||||
iconTrailing
|
||||
@keyup=${this._addressSearch}
|
||||
>
|
||||
${this._working
|
||||
<div class="location-search">
|
||||
<ha-textfield
|
||||
label=${this.onboardingLocalize(
|
||||
"ui.panel.page-onboarding.core-config.address_label"
|
||||
)}
|
||||
.disabled=${this._working}
|
||||
icon
|
||||
iconTrailing
|
||||
@keyup=${this._addressSearch}
|
||||
>
|
||||
<ha-svg-icon slot="leadingIcon" .path=${mdiMagnify}></ha-svg-icon>
|
||||
${this._working
|
||||
? html`
|
||||
<ha-circular-progress
|
||||
slot="trailingIcon"
|
||||
active
|
||||
size="small"
|
||||
></ha-circular-progress>
|
||||
`
|
||||
: html`
|
||||
<ha-icon-button
|
||||
@click=${this._handleButtonClick}
|
||||
slot="trailingIcon"
|
||||
.disabled=${this._working}
|
||||
.label=${this.onboardingLocalize(
|
||||
this._search
|
||||
? "ui.common.search"
|
||||
: "ui.panel.page-onboarding.core-config.button_detect"
|
||||
)}
|
||||
.path=${this._search ? mdiMapSearchOutline : mdiCrosshairsGps}
|
||||
></ha-icon-button>
|
||||
`}
|
||||
</ha-textfield>
|
||||
${this._places !== undefined
|
||||
? html`
|
||||
<ha-circular-progress
|
||||
slot="trailingIcon"
|
||||
active
|
||||
size="small"
|
||||
></ha-circular-progress>
|
||||
<mwc-list activatable>
|
||||
${this._places?.length
|
||||
? this._places.map((place) => {
|
||||
const primary = [
|
||||
place.name || place.address[place.category],
|
||||
place.address.house_number,
|
||||
place.address.road || place.address.waterway,
|
||||
place.address.village || place.address.town,
|
||||
place.address.suburb || place.address.subdivision,
|
||||
place.address.city || place.address.municipality,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(", ");
|
||||
const secondary = [
|
||||
place.address.county ||
|
||||
place.address.state_district ||
|
||||
place.address.region,
|
||||
place.address.state,
|
||||
place.address.country,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(", ");
|
||||
return html`<ha-list-item
|
||||
@click=${this._itemClicked}
|
||||
.placeId=${place.place_id}
|
||||
.selected=${this._highlightedMarker === place.place_id}
|
||||
.activated=${this._highlightedMarker === place.place_id}
|
||||
.twoline=${primary && secondary}
|
||||
>
|
||||
${primary || secondary}
|
||||
<span slot="secondary"
|
||||
>${primary ? secondary : ""}</span
|
||||
>
|
||||
</ha-list-item>`;
|
||||
})
|
||||
: html`<ha-list-item noninteractive
|
||||
>${this._places === null
|
||||
? ""
|
||||
: "No results"}</ha-list-item
|
||||
>`}
|
||||
</mwc-list>
|
||||
`
|
||||
: html`
|
||||
<ha-icon-button
|
||||
@click=${this._handleButtonClick}
|
||||
slot="trailingIcon"
|
||||
.disabled=${this._working}
|
||||
.label=${this.onboardingLocalize(
|
||||
this._search
|
||||
? "ui.common.search"
|
||||
: "ui.panel.page-onboarding.core-config.button_detect"
|
||||
)}
|
||||
.path=${this._search ? mdiMapSearchOutline : mdiCrosshairsGps}
|
||||
></ha-icon-button>
|
||||
`}
|
||||
</ha-textfield>
|
||||
${this._places !== undefined
|
||||
? html`
|
||||
<mwc-list activatable>
|
||||
${this._places?.length
|
||||
? this._places.map((place) => {
|
||||
const primary = [
|
||||
place.name || place.address[place.category],
|
||||
place.address.house_number,
|
||||
place.address.road || place.address.waterway,
|
||||
place.address.village || place.address.town,
|
||||
place.address.suburb || place.address.subdivision,
|
||||
place.address.city || place.address.municipality,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(", ");
|
||||
const secondary = [
|
||||
place.address.county ||
|
||||
place.address.state_district ||
|
||||
place.address.region,
|
||||
place.address.state,
|
||||
place.address.country,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(", ");
|
||||
return html`<ha-list-item
|
||||
@click=${this._itemClicked}
|
||||
.placeId=${place.place_id}
|
||||
.selected=${this._highlightedMarker === place.place_id}
|
||||
.activated=${this._highlightedMarker === place.place_id}
|
||||
.twoline=${primary && secondary}
|
||||
>
|
||||
${primary || secondary}
|
||||
<span slot="secondary">${primary ? secondary : ""}</span>
|
||||
</ha-list-item>`;
|
||||
})
|
||||
: html`<ha-list-item noninteractive
|
||||
>${this._places === null ? "" : "No results"}</ha-list-item
|
||||
>`}
|
||||
</mwc-list>
|
||||
`
|
||||
: nothing}
|
||||
<p class="attribution">${addressAttribution}</p>
|
||||
: nothing}
|
||||
</div>
|
||||
<ha-locations-editor
|
||||
class="flex"
|
||||
.hass=${this.hass}
|
||||
@@ -184,11 +202,10 @@ class OnboardingLocation extends LitElement {
|
||||
@marker-clicked=${this._markerClicked}
|
||||
></ha-locations-editor>
|
||||
|
||||
<p class="attribution">${addressAttribution}</p>
|
||||
|
||||
<div class="footer">
|
||||
<mwc-button
|
||||
@click=${this._save}
|
||||
.disabled=${!this._location || this._working}
|
||||
>
|
||||
<mwc-button @click=${this._save} unelevated .disabled=${this._working}>
|
||||
${this.onboardingLocalize(
|
||||
"ui.panel.page-onboarding.core-config.finish"
|
||||
)}
|
||||
@@ -301,7 +318,6 @@ class OnboardingLocation extends LitElement {
|
||||
|
||||
private async _searchAddress(address: string) {
|
||||
this._working = true;
|
||||
this._location = undefined;
|
||||
this._highlightedMarker = undefined;
|
||||
this._error = undefined;
|
||||
this._places = null;
|
||||
@@ -464,74 +480,76 @@ class OnboardingLocation extends LitElement {
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return css`
|
||||
p {
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
}
|
||||
ha-textfield {
|
||||
display: block;
|
||||
}
|
||||
ha-textfield > ha-icon-button {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
--mdc-icon-button-size: 36px;
|
||||
--mdc-icon-size: 20px;
|
||||
color: var(--secondary-text-color);
|
||||
inset-inline-start: initial;
|
||||
inset-inline-end: 10px;
|
||||
direction: var(--direction);
|
||||
}
|
||||
ha-textfield > ha-circular-progress {
|
||||
position: relative;
|
||||
left: 12px;
|
||||
}
|
||||
ha-locations-editor {
|
||||
display: block;
|
||||
height: 300px;
|
||||
margin-top: 8px;
|
||||
border-radius: var(--mdc-shape-small, 4px);
|
||||
overflow: hidden;
|
||||
}
|
||||
mwc-list {
|
||||
width: 100%;
|
||||
border: 1px solid var(--divider-color);
|
||||
box-sizing: border-box;
|
||||
border-top-width: 0;
|
||||
border-bottom-left-radius: var(--mdc-shape-small, 4px);
|
||||
border-bottom-right-radius: var(--mdc-shape-small, 4px);
|
||||
--mdc-list-vertical-padding: 0;
|
||||
}
|
||||
ha-list-item {
|
||||
height: 72px;
|
||||
}
|
||||
.footer {
|
||||
margin-top: 16px;
|
||||
text-align: right;
|
||||
}
|
||||
.attribution {
|
||||
/* textfield helper style */
|
||||
margin: 0;
|
||||
padding: 4px 16px 12px 16px;
|
||||
color: var(--mdc-text-field-label-ink-color, rgba(0, 0, 0, 0.6));
|
||||
font-family: var(
|
||||
--mdc-typography-caption-font-family,
|
||||
var(--mdc-typography-font-family, Roboto, sans-serif)
|
||||
);
|
||||
font-size: var(--mdc-typography-caption-font-size, 0.75rem);
|
||||
font-weight: var(--mdc-typography-caption-font-weight, 400);
|
||||
letter-spacing: var(
|
||||
--mdc-typography-caption-letter-spacing,
|
||||
0.0333333333em
|
||||
);
|
||||
text-decoration: var(--mdc-typography-caption-text-decoration, inherit);
|
||||
text-transform: var(--mdc-typography-caption-text-transform, inherit);
|
||||
}
|
||||
.attribution a {
|
||||
color: inherit;
|
||||
}
|
||||
`;
|
||||
return [
|
||||
onBoardingStyles,
|
||||
css`
|
||||
.location-search {
|
||||
margin-top: 32px;
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
ha-textfield {
|
||||
display: block;
|
||||
}
|
||||
ha-textfield > ha-icon-button {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
--mdc-icon-button-size: 36px;
|
||||
--mdc-icon-size: 20px;
|
||||
color: var(--secondary-text-color);
|
||||
inset-inline-start: initial;
|
||||
inset-inline-end: 10px;
|
||||
direction: var(--direction);
|
||||
}
|
||||
ha-textfield > ha-circular-progress {
|
||||
position: relative;
|
||||
left: 12px;
|
||||
}
|
||||
ha-locations-editor {
|
||||
display: block;
|
||||
height: 300px;
|
||||
margin-top: 8px;
|
||||
border-radius: var(--mdc-shape-large, 16px);
|
||||
overflow: hidden;
|
||||
}
|
||||
mwc-list {
|
||||
width: 100%;
|
||||
border: 1px solid var(--divider-color);
|
||||
box-sizing: border-box;
|
||||
border-top-width: 0;
|
||||
border-bottom-left-radius: var(--mdc-shape-small, 4px);
|
||||
border-bottom-right-radius: var(--mdc-shape-small, 4px);
|
||||
--mdc-list-vertical-padding: 0;
|
||||
}
|
||||
ha-list-item {
|
||||
height: 72px;
|
||||
}
|
||||
.attribution {
|
||||
/* textfield helper style */
|
||||
margin: 0;
|
||||
padding: 4px 16px 12px 16px;
|
||||
color: var(--mdc-text-field-label-ink-color, rgba(0, 0, 0, 0.6));
|
||||
font-family: var(
|
||||
--mdc-typography-caption-font-family,
|
||||
var(--mdc-typography-font-family, Roboto, sans-serif)
|
||||
);
|
||||
font-size: var(--mdc-typography-caption-font-size, 0.75rem);
|
||||
font-weight: var(--mdc-typography-caption-font-weight, 400);
|
||||
letter-spacing: var(
|
||||
--mdc-typography-caption-letter-spacing,
|
||||
0.0333333333em
|
||||
);
|
||||
text-decoration: var(
|
||||
--mdc-typography-caption-text-decoration,
|
||||
inherit
|
||||
);
|
||||
text-transform: var(--mdc-typography-caption-text-transform, inherit);
|
||||
}
|
||||
.attribution a {
|
||||
color: inherit;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,111 +0,0 @@
|
||||
import "@material/mwc-button/mwc-button";
|
||||
import { CSSResultGroup, LitElement, TemplateResult, css, html } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
import type { LocalizeFunc } from "../common/translations/localize";
|
||||
import "../components/ha-alert";
|
||||
import "../components/ha-formfield";
|
||||
import "../components/ha-radio";
|
||||
import "../components/ha-textfield";
|
||||
import "../components/map/ha-locations-editor";
|
||||
import { ConfigUpdateValues } from "../data/core";
|
||||
import type { HomeAssistant } from "../types";
|
||||
|
||||
@customElement("onboarding-name")
|
||||
class OnboardingName extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property() public onboardingLocalize!: LocalizeFunc;
|
||||
|
||||
private _name?: ConfigUpdateValues["location_name"];
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`
|
||||
<p>
|
||||
${this.onboardingLocalize(
|
||||
"ui.panel.page-onboarding.core-config.intro",
|
||||
{ name: this.hass.user!.name }
|
||||
)}
|
||||
</p>
|
||||
|
||||
<ha-textfield
|
||||
.label=${this.onboardingLocalize(
|
||||
"ui.panel.page-onboarding.core-config.location_name"
|
||||
)}
|
||||
.value=${this._nameValue}
|
||||
@change=${this._nameChanged}
|
||||
></ha-textfield>
|
||||
|
||||
<p>
|
||||
${this.onboardingLocalize(
|
||||
"ui.panel.page-onboarding.core-config.intro_core"
|
||||
)}
|
||||
</p>
|
||||
|
||||
<div class="footer">
|
||||
<mwc-button @click=${this._save} .disabled=${!this._nameValue}>
|
||||
${this.onboardingLocalize(
|
||||
"ui.panel.page-onboarding.core-config.finish"
|
||||
)}
|
||||
</mwc-button>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
protected firstUpdated(changedProps) {
|
||||
super.firstUpdated(changedProps);
|
||||
setTimeout(
|
||||
() => this.renderRoot.querySelector("ha-textfield")!.focus(),
|
||||
100
|
||||
);
|
||||
this.addEventListener("keyup", (ev) => {
|
||||
if (ev.key === "Enter") {
|
||||
this._save(ev);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private get _nameValue() {
|
||||
return this._name !== undefined
|
||||
? this._name
|
||||
: this.onboardingLocalize(
|
||||
"ui.panel.page-onboarding.core-config.location_name_default"
|
||||
);
|
||||
}
|
||||
|
||||
private _nameChanged(ev) {
|
||||
this._name = ev.target.value;
|
||||
}
|
||||
|
||||
private async _save(ev) {
|
||||
ev.preventDefault();
|
||||
fireEvent(this, "value-changed", {
|
||||
value: this._nameValue,
|
||||
});
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return css`
|
||||
ha-textfield {
|
||||
display: block;
|
||||
}
|
||||
p {
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
}
|
||||
.footer {
|
||||
margin-top: 16px;
|
||||
text-align: right;
|
||||
}
|
||||
a {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"onboarding-name": OnboardingName;
|
||||
}
|
||||
}
|
@@ -1,44 +1,34 @@
|
||||
import "@material/mwc-button/mwc-button";
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { showBackupUploadDialog } from "../../hassio/src/dialogs/backup/show-dialog-backup-upload";
|
||||
import { showHassioBackupDialog } from "../../hassio/src/dialogs/backup/show-dialog-hassio-backup";
|
||||
import type { LocalizeFunc } from "../common/translations/localize";
|
||||
import "../components/ha-card";
|
||||
import "../components/ha-ansi-to-html";
|
||||
import "../components/ha-card";
|
||||
import { fetchInstallationType } from "../data/onboarding";
|
||||
import { makeDialogManager } from "../dialogs/make-dialog-manager";
|
||||
import { ProvideHassLitMixin } from "../mixins/provide-hass-lit-mixin";
|
||||
import { haStyle } from "../resources/styles";
|
||||
import "./onboarding-loading";
|
||||
|
||||
declare global {
|
||||
interface HASSDomEvents {
|
||||
restoring: undefined;
|
||||
}
|
||||
}
|
||||
import { onBoardingStyles } from "./styles";
|
||||
|
||||
@customElement("onboarding-restore-backup")
|
||||
class OnboardingRestoreBackup extends ProvideHassLitMixin(LitElement) {
|
||||
class OnboardingRestoreBackup extends LitElement {
|
||||
@property() public localize!: LocalizeFunc;
|
||||
|
||||
@property() public language!: string;
|
||||
|
||||
@property({ type: Boolean }) public restoring = false;
|
||||
@state() public _restoring = false;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return this.restoring
|
||||
? html`<ha-card
|
||||
.header=${this.localize(
|
||||
"ui.panel.page-onboarding.restore.in_progress"
|
||||
)}
|
||||
>
|
||||
<onboarding-loading></onboarding-loading>
|
||||
</ha-card>`
|
||||
return this._restoring
|
||||
? html`<h1>
|
||||
${this.localize("ui.panel.page-onboarding.restore.in_progress")}
|
||||
</h1>
|
||||
<onboarding-loading></onboarding-loading>`
|
||||
: html`
|
||||
<button class="link" @click=${this._uploadBackup}>
|
||||
${this.localize("ui.panel.page-onboarding.restore.description")}
|
||||
</button>
|
||||
<h1>${this.localize("ui.panel.page-onboarding.restore.header")}</h1>
|
||||
<ha-button unelevated @click=${this._uploadBackup}>
|
||||
${this.localize("ui.panel.page-onboarding.restore.upload_backup")}
|
||||
</ha-button>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -51,12 +41,11 @@ class OnboardingRestoreBackup extends ProvideHassLitMixin(LitElement) {
|
||||
|
||||
protected firstUpdated(changedProps) {
|
||||
super.firstUpdated(changedProps);
|
||||
makeDialogManager(this, this.shadowRoot!);
|
||||
setInterval(() => this._checkRestoreStatus(), 1000);
|
||||
}
|
||||
|
||||
private async _checkRestoreStatus(): Promise<void> {
|
||||
if (this.restoring) {
|
||||
if (this._restoring) {
|
||||
try {
|
||||
await fetchInstallationType();
|
||||
} catch (err: any) {
|
||||
@@ -72,32 +61,20 @@ class OnboardingRestoreBackup extends ProvideHassLitMixin(LitElement) {
|
||||
slug,
|
||||
onboarding: true,
|
||||
localize: this.localize,
|
||||
onRestoring: () => {
|
||||
this._restoring = true;
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return [
|
||||
haStyle,
|
||||
onBoardingStyles,
|
||||
css`
|
||||
.logentry {
|
||||
text-align: center;
|
||||
}
|
||||
ha-card {
|
||||
padding: 4px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
ha-ansi-to-html {
|
||||
display: block;
|
||||
line-height: 22px;
|
||||
padding: 0 8px;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
@media all and (min-width: 600px) {
|
||||
ha-card {
|
||||
width: 600px;
|
||||
margin-left: -100px;
|
||||
}
|
||||
:host {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
103
src/onboarding/onboarding-welcome-link.ts
Normal file
103
src/onboarding/onboarding-welcome-link.ts
Normal file
@@ -0,0 +1,103 @@
|
||||
import "@material/mwc-ripple";
|
||||
import type { Ripple } from "@material/mwc-ripple";
|
||||
import { RippleHandlers } from "@material/mwc-ripple/ripple-handlers";
|
||||
import { CSSResultGroup, LitElement, TemplateResult, css, html } from "lit";
|
||||
import {
|
||||
customElement,
|
||||
eventOptions,
|
||||
property,
|
||||
queryAsync,
|
||||
state,
|
||||
} from "lit/decorators";
|
||||
import "../components/ha-card";
|
||||
|
||||
@customElement("onboarding-welcome-link")
|
||||
class OnboardingWelcomeLink extends LitElement {
|
||||
@property() public label!: string;
|
||||
|
||||
@property() public iconPath!: string;
|
||||
|
||||
@queryAsync("mwc-ripple") private _ripple!: Promise<Ripple | null>;
|
||||
|
||||
@state() private _shouldRenderRipple = false;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`
|
||||
<ha-card
|
||||
@focus=${this.handleRippleFocus}
|
||||
@blur=${this.handleRippleBlur}
|
||||
@mousedown=${this.handleRippleActivate}
|
||||
@mouseup=${this.handleRippleDeactivate}
|
||||
@mouseenter=${this.handleRippleMouseEnter}
|
||||
@mouseleave=${this.handleRippleMouseLeave}
|
||||
@touchstart=${this.handleRippleActivate}
|
||||
@touchend=${this.handleRippleDeactivate}
|
||||
@touchcancel=${this.handleRippleDeactivate}
|
||||
>
|
||||
<ha-svg-icon .path=${this.iconPath}></ha-svg-icon>
|
||||
${this.label}
|
||||
${this._shouldRenderRipple ? html`<mwc-ripple></mwc-ripple>` : ""}
|
||||
</ha-card>
|
||||
`;
|
||||
}
|
||||
|
||||
private _rippleHandlers: RippleHandlers = new RippleHandlers(() => {
|
||||
this._shouldRenderRipple = true;
|
||||
return this._ripple;
|
||||
});
|
||||
|
||||
private handleRippleMouseEnter() {
|
||||
this._rippleHandlers.startHover();
|
||||
}
|
||||
|
||||
private handleRippleMouseLeave() {
|
||||
this._rippleHandlers.endHover();
|
||||
}
|
||||
|
||||
@eventOptions({ passive: true })
|
||||
private handleRippleActivate(evt?: Event) {
|
||||
this._rippleHandlers.startPress(evt);
|
||||
}
|
||||
|
||||
private handleRippleDeactivate() {
|
||||
this._rippleHandlers.endPress();
|
||||
}
|
||||
|
||||
private handleRippleFocus() {
|
||||
this._rippleHandlers.startFocus();
|
||||
}
|
||||
|
||||
private handleRippleBlur() {
|
||||
this._rippleHandlers.endFocus();
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return css`
|
||||
:host {
|
||||
cursor: pointer;
|
||||
}
|
||||
ha-card {
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
font-weight: 500;
|
||||
padding: 32px 16px;
|
||||
}
|
||||
ha-svg-icon {
|
||||
color: var(--text-primary-color);
|
||||
background: var(--welcome-link-color, var(--primary-color));
|
||||
border-radius: 50%;
|
||||
padding: 8px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"onboarding-welcome-link": OnboardingWelcomeLink;
|
||||
}
|
||||
}
|
84
src/onboarding/onboarding-welcome-links.ts
Normal file
84
src/onboarding/onboarding-welcome-links.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
import { mdiAccountGroup, mdiFileDocument, mdiTabletCellphone } from "@mdi/js";
|
||||
import { CSSResultGroup, LitElement, TemplateResult, css, html } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import type { LocalizeFunc } from "../common/translations/localize";
|
||||
import "../components/ha-card";
|
||||
import type { HomeAssistant } from "../types";
|
||||
import { showAppDialog } from "./dialogs/show-app-dialog";
|
||||
import { showCommunityDialog } from "./dialogs/show-community-dialog";
|
||||
import "./onboarding-welcome-link";
|
||||
|
||||
@customElement("onboarding-welcome-links")
|
||||
class OnboardingWelcomeLinks extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property() public localize!: LocalizeFunc;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`<a
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
href="https://www.home-assistant.io/blog/2016/01/19/perfect-home-automation/"
|
||||
>
|
||||
<onboarding-welcome-link
|
||||
.iconPath=${mdiFileDocument}
|
||||
.label=${this.localize("ui.panel.page-onboarding.welcome.vision")}
|
||||
>
|
||||
</onboarding-welcome-link>
|
||||
</a>
|
||||
<onboarding-welcome-link
|
||||
class="community"
|
||||
@click=${this._openCommunity}
|
||||
.iconPath=${mdiAccountGroup}
|
||||
.label=${this.localize("ui.panel.page-onboarding.welcome.community")}
|
||||
>
|
||||
</onboarding-welcome-link>
|
||||
<onboarding-welcome-link
|
||||
class="app"
|
||||
@click=${this._openApp}
|
||||
.iconPath=${mdiTabletCellphone}
|
||||
.label=${this.localize("ui.panel.page-onboarding.welcome.download_app")}
|
||||
>
|
||||
</onboarding-welcome-link>`;
|
||||
}
|
||||
|
||||
private _openCommunity(): void {
|
||||
showCommunityDialog(this, { localize: this.localize });
|
||||
}
|
||||
|
||||
private _openApp(): void {
|
||||
showAppDialog(this, { localize: this.localize });
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return css`
|
||||
:host {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
||||
margin-top: 16px;
|
||||
column-gap: 16px;
|
||||
row-gap: 16px;
|
||||
}
|
||||
@media (max-width: 550px) {
|
||||
:host {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
.community {
|
||||
--welcome-link-color: #008142;
|
||||
}
|
||||
.app {
|
||||
--welcome-link-color: #6e41ab;
|
||||
}
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"onboarding-welcome-links": OnboardingWelcomeLinks;
|
||||
}
|
||||
}
|
79
src/onboarding/onboarding-welcome.ts
Normal file
79
src/onboarding/onboarding-welcome.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
import {
|
||||
CSSResultGroup,
|
||||
LitElement,
|
||||
TemplateResult,
|
||||
css,
|
||||
html,
|
||||
nothing,
|
||||
} from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import type { LocalizeFunc } from "../common/translations/localize";
|
||||
import type { HomeAssistant } from "../types";
|
||||
import { onBoardingStyles } from "./styles";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
import "../components/ha-button";
|
||||
|
||||
@customElement("onboarding-welcome")
|
||||
class OnboardingWelcome extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property() public localize!: LocalizeFunc;
|
||||
|
||||
@property({ type: Boolean }) public supervisor?: boolean;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`
|
||||
<h1>${this.localize("ui.panel.page-onboarding.welcome.header")}</h1>
|
||||
<p>${this.localize("ui.panel.page-onboarding.intro")}</p>
|
||||
|
||||
<ha-button unelevated @click=${this._start} class="start">
|
||||
${this.localize("ui.panel.page-onboarding.welcome.start")}
|
||||
</ha-button>
|
||||
|
||||
${this.supervisor
|
||||
? html`<ha-button @click=${this._restoreBackup}>
|
||||
${this.localize("ui.panel.page-onboarding.welcome.restore_backup")}
|
||||
</ha-button>`
|
||||
: nothing}
|
||||
`;
|
||||
}
|
||||
|
||||
private _start(): void {
|
||||
fireEvent(this, "onboarding-step", {
|
||||
type: "init",
|
||||
result: { restore: false },
|
||||
});
|
||||
}
|
||||
|
||||
private _restoreBackup(): void {
|
||||
fireEvent(this, "onboarding-step", {
|
||||
type: "init",
|
||||
result: { restore: true },
|
||||
});
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return [
|
||||
onBoardingStyles,
|
||||
css`
|
||||
:host {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
.start {
|
||||
--button-height: 48px;
|
||||
--mdc-typography-button-font-size: 1rem;
|
||||
--mdc-button-horizontal-padding: 24px;
|
||||
margin: 16px 0;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"onboarding-welcome": OnboardingWelcome;
|
||||
}
|
||||
}
|
@@ -1,5 +1,6 @@
|
||||
import { tsParticles } from "tsparticles-engine";
|
||||
import { loadLinksPreset } from "tsparticles-preset-links";
|
||||
import { DEFAULT_PRIMARY_COLOR } from "../resources/ha-style";
|
||||
|
||||
loadLinksPreset(tsParticles).then(() => {
|
||||
tsParticles.load("particles", {
|
||||
@@ -22,16 +23,16 @@ loadLinksPreset(tsParticles).then(() => {
|
||||
},
|
||||
particles: {
|
||||
color: {
|
||||
value: "#fff",
|
||||
animation: {
|
||||
enable: true,
|
||||
speed: 50,
|
||||
sync: false,
|
||||
},
|
||||
value: DEFAULT_PRIMARY_COLOR,
|
||||
},
|
||||
animation: {
|
||||
enable: true,
|
||||
speed: 50,
|
||||
sync: false,
|
||||
},
|
||||
links: {
|
||||
color: {
|
||||
value: "random",
|
||||
value: DEFAULT_PRIMARY_COLOR,
|
||||
},
|
||||
distance: 100,
|
||||
enable: true,
|
||||
|
19
src/onboarding/styles.ts
Normal file
19
src/onboarding/styles.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { css } from "lit";
|
||||
|
||||
export const onBoardingStyles = css`
|
||||
h1 {
|
||||
text-align: center;
|
||||
font-weight: 400;
|
||||
font-size: 28px;
|
||||
line-height: 36px;
|
||||
}
|
||||
p {
|
||||
font-size: 1rem;
|
||||
line-height: 1.5rem;
|
||||
text-align: center;
|
||||
}
|
||||
.footer {
|
||||
margin-top: 16px;
|
||||
text-align: right;
|
||||
}
|
||||
`;
|
@@ -1,21 +1,19 @@
|
||||
import { css, CSSResultGroup, html, LitElement } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { fireEvent } from "../../../../../common/dom/fire_event";
|
||||
import "../../../../../components/ha-textfield";
|
||||
import {
|
||||
Action,
|
||||
CountRepeat,
|
||||
RepeatAction,
|
||||
UntilRepeat,
|
||||
WhileRepeat,
|
||||
} from "../../../../../data/script";
|
||||
import { RepeatAction } from "../../../../../data/script";
|
||||
import { haStyle } from "../../../../../resources/styles";
|
||||
import type { HomeAssistant } from "../../../../../types";
|
||||
import type { Condition } from "../../../../lovelace/common/validate-condition";
|
||||
import "../ha-automation-action";
|
||||
import type { ActionElement } from "../ha-automation-action-row";
|
||||
|
||||
const OPTIONS = ["count", "while", "until"] as const;
|
||||
import type { LocalizeFunc } from "../../../../../common/translations/localize";
|
||||
import "../../../../../components/ha-form/ha-form";
|
||||
import type { SchemaUnion } from "../../../../../components/ha-form/types";
|
||||
|
||||
const OPTIONS = ["count", "while", "until", "for_each"] as const;
|
||||
|
||||
const getType = (action) => OPTIONS.find((option) => option in action);
|
||||
|
||||
@@ -33,144 +31,115 @@ export class HaRepeatAction extends LitElement implements ActionElement {
|
||||
return { repeat: { count: 2, sequence: [] } };
|
||||
}
|
||||
|
||||
private _schema = memoizeOne(
|
||||
(localize: LocalizeFunc, type: string, reOrderMode: boolean) =>
|
||||
[
|
||||
{
|
||||
name: "type",
|
||||
selector: {
|
||||
select: {
|
||||
mode: "dropdown",
|
||||
options: OPTIONS.map((opt) => ({
|
||||
value: opt,
|
||||
label: localize(
|
||||
`ui.panel.config.automation.editor.actions.type.repeat.type.${opt}.label`
|
||||
),
|
||||
})),
|
||||
},
|
||||
},
|
||||
},
|
||||
...(type === "count"
|
||||
? ([
|
||||
{
|
||||
name: "count",
|
||||
required: true,
|
||||
selector: { number: { mode: "box", min: 1 } },
|
||||
},
|
||||
] as const)
|
||||
: []),
|
||||
...(type === "until" || type === "while"
|
||||
? ([
|
||||
{
|
||||
name: type,
|
||||
selector: {
|
||||
condition: { nested: true, reorder_mode: reOrderMode },
|
||||
},
|
||||
},
|
||||
] as const)
|
||||
: []),
|
||||
...(type === "for_each"
|
||||
? ([
|
||||
{
|
||||
name: "for_each",
|
||||
required: true,
|
||||
selector: { object: {} },
|
||||
},
|
||||
] as const)
|
||||
: []),
|
||||
{
|
||||
name: "sequence",
|
||||
selector: { action: { nested: true, reorder_mode: reOrderMode } },
|
||||
},
|
||||
] as const
|
||||
);
|
||||
|
||||
protected render() {
|
||||
const action = this.action.repeat;
|
||||
|
||||
const type = getType(action);
|
||||
|
||||
return html`
|
||||
<ha-select
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.actions.type.repeat.type_select"
|
||||
)}
|
||||
.value=${type}
|
||||
.disabled=${this.disabled}
|
||||
@selected=${this._typeChanged}
|
||||
>
|
||||
${OPTIONS.map(
|
||||
(opt) => html`
|
||||
<mwc-list-item .value=${opt}>
|
||||
${this.hass.localize(
|
||||
`ui.panel.config.automation.editor.actions.type.repeat.type.${opt}.label`
|
||||
)}
|
||||
</mwc-list-item>
|
||||
`
|
||||
)}
|
||||
</ha-select>
|
||||
<div>
|
||||
${type === "count"
|
||||
? html`
|
||||
<ha-textfield
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.actions.type.repeat.type.count.label"
|
||||
)}
|
||||
name="count"
|
||||
.value=${(action as CountRepeat).count || "0"}
|
||||
.disabled=${this.disabled}
|
||||
@change=${this._countChanged}
|
||||
></ha-textfield>
|
||||
`
|
||||
: type === "while"
|
||||
? html` <h3>
|
||||
${this.hass.localize(
|
||||
`ui.panel.config.automation.editor.actions.type.repeat.type.while.conditions`
|
||||
)}:
|
||||
</h3>
|
||||
<ha-automation-condition
|
||||
nested
|
||||
.conditions=${(action as WhileRepeat).while || []}
|
||||
.hass=${this.hass}
|
||||
.disabled=${this.disabled}
|
||||
@value-changed=${this._conditionChanged}
|
||||
></ha-automation-condition>`
|
||||
: type === "until"
|
||||
? html` <h3>
|
||||
${this.hass.localize(
|
||||
`ui.panel.config.automation.editor.actions.type.repeat.type.until.conditions`
|
||||
)}:
|
||||
</h3>
|
||||
<ha-automation-condition
|
||||
nested
|
||||
.conditions=${(action as UntilRepeat).until || []}
|
||||
.hass=${this.hass}
|
||||
.disabled=${this.disabled}
|
||||
@value-changed=${this._conditionChanged}
|
||||
></ha-automation-condition>`
|
||||
: ""}
|
||||
</div>
|
||||
<h3>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.actions.type.repeat.sequence"
|
||||
)}:
|
||||
</h3>
|
||||
<ha-automation-action
|
||||
nested
|
||||
.actions=${action.sequence}
|
||||
.reOrderMode=${this.reOrderMode}
|
||||
.disabled=${this.disabled}
|
||||
@value-changed=${this._actionChanged}
|
||||
.hass=${this.hass}
|
||||
></ha-automation-action>
|
||||
`;
|
||||
const schema = this._schema(
|
||||
this.hass.localize,
|
||||
type ?? "count",
|
||||
this.reOrderMode
|
||||
);
|
||||
const data = { ...action, type };
|
||||
return html` <ha-form
|
||||
.hass=${this.hass}
|
||||
.data=${data}
|
||||
.schema=${schema}
|
||||
.disabled=${this.disabled}
|
||||
@value-changed=${this._valueChanged}
|
||||
.computeLabel=${this._computeLabelCallback}
|
||||
></ha-form>`;
|
||||
}
|
||||
|
||||
private _typeChanged(ev) {
|
||||
const type = ev.target.value;
|
||||
private _valueChanged(ev: CustomEvent): void {
|
||||
ev.stopPropagation();
|
||||
const newVal = ev.detail.value;
|
||||
|
||||
if (!type || type === getType(this.action.repeat)) {
|
||||
return;
|
||||
const newType = newVal.type;
|
||||
delete newVal.type;
|
||||
const oldType = getType(this.action.repeat);
|
||||
|
||||
if (newType !== oldType) {
|
||||
if (newType === "count") {
|
||||
newVal.count = 2;
|
||||
delete newVal.while;
|
||||
delete newVal.until;
|
||||
delete newVal.for_each;
|
||||
}
|
||||
if (newType === "while") {
|
||||
newVal.while = newVal.until ?? [];
|
||||
delete newVal.count;
|
||||
delete newVal.until;
|
||||
delete newVal.for_each;
|
||||
}
|
||||
if (newType === "until") {
|
||||
newVal.until = newVal.while ?? [];
|
||||
delete newVal.count;
|
||||
delete newVal.while;
|
||||
delete newVal.for_each;
|
||||
}
|
||||
if (newType === "for_each") {
|
||||
newVal.for_each = {};
|
||||
delete newVal.count;
|
||||
delete newVal.while;
|
||||
delete newVal.until;
|
||||
}
|
||||
}
|
||||
|
||||
const value = type === "count" ? 2 : [];
|
||||
|
||||
fireEvent(this, "value-changed", {
|
||||
value: {
|
||||
...this.action,
|
||||
repeat: { [type]: value, sequence: this.action.repeat.sequence },
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
private _conditionChanged(ev: CustomEvent) {
|
||||
ev.stopPropagation();
|
||||
const value = ev.detail.value as Condition[];
|
||||
fireEvent(this, "value-changed", {
|
||||
value: {
|
||||
...this.action,
|
||||
repeat: {
|
||||
...this.action.repeat,
|
||||
[getType(this.action.repeat)!]: value,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
private _actionChanged(ev: CustomEvent) {
|
||||
ev.stopPropagation();
|
||||
const value = ev.detail.value as Action[];
|
||||
fireEvent(this, "value-changed", {
|
||||
value: {
|
||||
...this.action,
|
||||
repeat: {
|
||||
...this.action.repeat,
|
||||
sequence: value,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
private _countChanged(ev: CustomEvent): void {
|
||||
const newVal = (ev.target as any).value;
|
||||
if ((this.action.repeat as CountRepeat).count === newVal) {
|
||||
return;
|
||||
}
|
||||
fireEvent(this, "value-changed", {
|
||||
value: {
|
||||
...this.action,
|
||||
repeat: {
|
||||
...this.action.repeat,
|
||||
count: newVal,
|
||||
},
|
||||
repeat: { ...newVal },
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -185,6 +154,46 @@ export class HaRepeatAction extends LitElement implements ActionElement {
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
private _computeLabelCallback = (
|
||||
schema: SchemaUnion<ReturnType<typeof this._schema>>
|
||||
): string => {
|
||||
switch (schema.name) {
|
||||
case "type":
|
||||
return this.hass.localize(
|
||||
"ui.panel.config.automation.editor.actions.type.repeat.type_select"
|
||||
);
|
||||
case "count":
|
||||
return this.hass.localize(
|
||||
"ui.panel.config.automation.editor.actions.type.repeat.type.count.label"
|
||||
);
|
||||
case "while":
|
||||
return (
|
||||
this.hass.localize(
|
||||
"ui.panel.config.automation.editor.actions.type.repeat.type.while.conditions"
|
||||
) + ":"
|
||||
);
|
||||
case "until":
|
||||
return (
|
||||
this.hass.localize(
|
||||
"ui.panel.config.automation.editor.actions.type.repeat.type.until.conditions"
|
||||
) + ":"
|
||||
);
|
||||
case "for_each":
|
||||
return (
|
||||
this.hass.localize(
|
||||
"ui.panel.config.automation.editor.actions.type.repeat.type.for_each.items"
|
||||
) + ":"
|
||||
);
|
||||
case "sequence":
|
||||
return (
|
||||
this.hass.localize(
|
||||
"ui.panel.config.automation.editor.actions.type.repeat.sequence"
|
||||
) + ":"
|
||||
);
|
||||
}
|
||||
return "";
|
||||
};
|
||||
}
|
||||
|
||||
declare global {
|
||||
|
@@ -28,7 +28,6 @@ import { computeStateDomain } from "../../../common/entity/compute_state_domain"
|
||||
import { computeStateName } from "../../../common/entity/compute_state_name";
|
||||
import { stringCompare } from "../../../common/string/compare";
|
||||
import { slugify } from "../../../common/string/slugify";
|
||||
import { blankBeforePercent } from "../../../common/translations/blank_before_percent";
|
||||
import { groupBy } from "../../../common/util/group-by";
|
||||
import "../../../components/entity/ha-battery-icon";
|
||||
import "../../../components/ha-alert";
|
||||
@@ -329,11 +328,11 @@ export class HaConfigDevicePage extends LitElement {
|
||||
const entitiesByCategory = this._entitiesByCategory(entities);
|
||||
const batteryEntity = this._batteryEntity(entities);
|
||||
const batteryChargingEntity = this._batteryChargingEntity(entities);
|
||||
const batteryState = batteryEntity
|
||||
const battery = batteryEntity
|
||||
? this.hass.states[batteryEntity.entity_id]
|
||||
: undefined;
|
||||
const batteryIsBinary =
|
||||
batteryState && computeStateDomain(batteryState) === "binary_sensor";
|
||||
const batteryDomain = battery ? computeStateDomain(battery) : undefined;
|
||||
|
||||
const batteryChargingState = batteryChargingEntity
|
||||
? this.hass.states[batteryChargingEntity.entity_id]
|
||||
: undefined;
|
||||
@@ -712,17 +711,17 @@ export class HaConfigDevicePage extends LitElement {
|
||||
}
|
||||
<div class="header-right">
|
||||
${
|
||||
batteryState
|
||||
battery &&
|
||||
(batteryDomain === "binary_sensor" ||
|
||||
!isNaN(battery.state as any))
|
||||
? html`
|
||||
<div class="battery">
|
||||
${batteryIsBinary
|
||||
? ""
|
||||
: batteryState.state +
|
||||
blankBeforePercent(this.hass.locale) +
|
||||
"%"}
|
||||
${batteryDomain === "sensor"
|
||||
? this.hass.formatEntityState(battery)
|
||||
: nothing}
|
||||
<ha-battery-icon
|
||||
.hass=${this.hass!}
|
||||
.batteryStateObj=${batteryState}
|
||||
.hass=${this.hass}
|
||||
.batteryStateObj=${battery}
|
||||
.batteryChargingStateObj=${batteryChargingState}
|
||||
></ha-battery-icon>
|
||||
</div>
|
||||
|
@@ -1,7 +1,14 @@
|
||||
import "@lrnwebcomponents/simple-tooltip/simple-tooltip";
|
||||
import type { RequestSelectedDetail } from "@material/mwc-list/mwc-list-item";
|
||||
import { mdiCancel, mdiFilterVariant, mdiPlus } from "@mdi/js";
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import {
|
||||
css,
|
||||
CSSResultGroup,
|
||||
html,
|
||||
LitElement,
|
||||
nothing,
|
||||
TemplateResult,
|
||||
} from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { HASSDomEvent } from "../../../common/dom/fire_event";
|
||||
@@ -11,7 +18,6 @@ import {
|
||||
PROTOCOL_INTEGRATIONS,
|
||||
} from "../../../common/integrations/protocolIntegrationPicked";
|
||||
import { navigate } from "../../../common/navigate";
|
||||
import { blankBeforePercent } from "../../../common/translations/blank_before_percent";
|
||||
import { LocalizeFunc } from "../../../common/translations/localize";
|
||||
import { computeRTL } from "../../../common/util/compute_rtl";
|
||||
import {
|
||||
@@ -374,22 +380,22 @@ export class HaConfigDeviceDashboard extends LitElement {
|
||||
batteryEntityPair && batteryEntityPair[0]
|
||||
? this.hass.states[batteryEntityPair[0]]
|
||||
: undefined;
|
||||
const batteryDomain = battery
|
||||
? computeStateDomain(battery)
|
||||
: undefined;
|
||||
const batteryCharging =
|
||||
batteryEntityPair && batteryEntityPair[1]
|
||||
? this.hass.states[batteryEntityPair[1]]
|
||||
: undefined;
|
||||
const batteryIsBinary =
|
||||
battery && computeStateDomain(battery) === "binary_sensor";
|
||||
|
||||
return battery && (batteryIsBinary || !isNaN(battery.state as any))
|
||||
return battery &&
|
||||
(batteryDomain === "binary_sensor" || !isNaN(battery.state as any))
|
||||
? html`
|
||||
${batteryIsBinary
|
||||
? ""
|
||||
: Number(battery.state).toFixed() +
|
||||
blankBeforePercent(this.hass.locale) +
|
||||
"%"}
|
||||
${batteryDomain === "sensor"
|
||||
? this.hass.formatEntityState(battery)
|
||||
: nothing}
|
||||
<ha-battery-icon
|
||||
.hass=${this.hass!}
|
||||
.hass=${this.hass}
|
||||
.batteryStateObj=${battery}
|
||||
.batteryChargingStateObj=${batteryCharging}
|
||||
></ha-battery-icon>
|
||||
|
@@ -1,10 +1,14 @@
|
||||
import "@material/mwc-button/mwc-button";
|
||||
import "@material/mwc-list/mwc-list-item";
|
||||
import { mdiDelete } from "@mdi/js";
|
||||
import "@material/mwc-list/mwc-list";
|
||||
import { mdiDelete, mdiDrag } from "@mdi/js";
|
||||
import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { repeat } from "lit/directives/repeat";
|
||||
import type { SortableEvent } from "sortablejs";
|
||||
import { sortableStyles } from "../../../../resources/ha-sortable-style";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import "../../../../components/ha-button";
|
||||
import "../../../../components/ha-icon-button";
|
||||
import "../../../../components/ha-list-item";
|
||||
import "../../../../components/ha-icon-picker";
|
||||
import "../../../../components/ha-textfield";
|
||||
import type { HaTextField } from "../../../../components/ha-textfield";
|
||||
@@ -12,6 +16,10 @@ import type { InputSelect } from "../../../../data/input_select";
|
||||
import { showConfirmationDialog } from "../../../../dialogs/generic/show-dialog-box";
|
||||
import { haStyle } from "../../../../resources/styles";
|
||||
import type { HomeAssistant } from "../../../../types";
|
||||
import {
|
||||
loadSortable,
|
||||
SortableInstance,
|
||||
} from "../../../../resources/sortable.ondemand";
|
||||
|
||||
@customElement("ha-input_select-form")
|
||||
class HaInputSelectForm extends LitElement {
|
||||
@@ -27,8 +35,59 @@ class HaInputSelectForm extends LitElement {
|
||||
|
||||
@state() private _options: string[] = [];
|
||||
|
||||
private _sortable?: SortableInstance;
|
||||
|
||||
@query("#option_input", true) private _optionInput?: HaTextField;
|
||||
|
||||
public connectedCallback() {
|
||||
super.connectedCallback();
|
||||
this._createSortable();
|
||||
}
|
||||
|
||||
public disconnectedCallback() {
|
||||
super.disconnectedCallback();
|
||||
this._destroySortable();
|
||||
}
|
||||
|
||||
private async _createSortable() {
|
||||
const Sortable = await loadSortable();
|
||||
this._sortable = new Sortable(this.shadowRoot!.querySelector(".options")!, {
|
||||
animation: 150,
|
||||
fallbackClass: "sortable-fallback",
|
||||
handle: ".handle",
|
||||
onChoose: (evt: SortableEvent) => {
|
||||
(evt.item as any).placeholder =
|
||||
document.createComment("sort-placeholder");
|
||||
evt.item.after((evt.item as any).placeholder);
|
||||
},
|
||||
onEnd: (evt: SortableEvent) => {
|
||||
// put back in original location
|
||||
if ((evt.item as any).placeholder) {
|
||||
(evt.item as any).placeholder.replaceWith(evt.item);
|
||||
delete (evt.item as any).placeholder;
|
||||
}
|
||||
this._dragged(evt);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
private _dragged(ev: SortableEvent): void {
|
||||
if (ev.oldIndex === ev.newIndex) return;
|
||||
|
||||
const options = this._options.concat();
|
||||
const option = options.splice(ev.oldIndex!, 1)[0];
|
||||
options.splice(ev.newIndex!, 0, option);
|
||||
|
||||
fireEvent(this, "value-changed", {
|
||||
value: { ...this._item, options },
|
||||
});
|
||||
}
|
||||
|
||||
private _destroySortable() {
|
||||
this._sortable?.destroy();
|
||||
this._sortable = undefined;
|
||||
}
|
||||
|
||||
set item(item: InputSelect) {
|
||||
this._item = item;
|
||||
if (item) {
|
||||
@@ -86,30 +145,39 @@ class HaInputSelectForm extends LitElement {
|
||||
"ui.dialogs.helper_settings.input_select.options"
|
||||
)}:
|
||||
</div>
|
||||
${this._options.length
|
||||
? this._options.map(
|
||||
(option, index) => html`
|
||||
<mwc-list-item class="option" hasMeta>
|
||||
${option}
|
||||
<ha-icon-button
|
||||
slot="meta"
|
||||
.index=${index}
|
||||
.label=${this.hass.localize(
|
||||
"ui.dialogs.helper_settings.input_select.remove_option"
|
||||
)}
|
||||
@click=${this._removeOption}
|
||||
.path=${mdiDelete}
|
||||
></ha-icon-button>
|
||||
</mwc-list-item>
|
||||
`
|
||||
)
|
||||
: html`
|
||||
<mwc-list-item noninteractive>
|
||||
${this.hass!.localize(
|
||||
"ui.dialogs.helper_settings.input_select.no_options"
|
||||
)}
|
||||
</mwc-list-item>
|
||||
`}
|
||||
<mwc-list class="options">
|
||||
${this._options.length
|
||||
? repeat(
|
||||
this._options,
|
||||
(option) => option,
|
||||
(option, index) => html`
|
||||
<ha-list-item class="option" hasMeta>
|
||||
<div class="optioncontent">
|
||||
<div class="handle">
|
||||
<ha-svg-icon .path=${mdiDrag}></ha-svg-icon>
|
||||
</div>
|
||||
${option}
|
||||
</div>
|
||||
<ha-icon-button
|
||||
slot="meta"
|
||||
.index=${index}
|
||||
.label=${this.hass.localize(
|
||||
"ui.dialogs.helper_settings.input_select.remove_option"
|
||||
)}
|
||||
@click=${this._removeOption}
|
||||
.path=${mdiDelete}
|
||||
></ha-icon-button>
|
||||
</ha-list-item>
|
||||
`
|
||||
)
|
||||
: html`
|
||||
<ha-list-item noninteractive>
|
||||
${this.hass!.localize(
|
||||
"ui.dialogs.helper_settings.input_select.no_options"
|
||||
)}
|
||||
</ha-list-item>
|
||||
`}
|
||||
</mwc-list>
|
||||
<div class="layout horizontal center">
|
||||
<ha-textfield
|
||||
class="flex-auto"
|
||||
@@ -119,10 +187,10 @@ class HaInputSelectForm extends LitElement {
|
||||
)}
|
||||
@keydown=${this._handleKeyAdd}
|
||||
></ha-textfield>
|
||||
<mwc-button @click=${this._addOption}
|
||||
<ha-button @click=${this._addOption}
|
||||
>${this.hass!.localize(
|
||||
"ui.dialogs.helper_settings.input_select.add"
|
||||
)}</mwc-button
|
||||
)}</ha-button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
@@ -190,6 +258,7 @@ class HaInputSelectForm extends LitElement {
|
||||
static get styles(): CSSResultGroup {
|
||||
return [
|
||||
haStyle,
|
||||
sortableStyles,
|
||||
css`
|
||||
.form {
|
||||
color: var(--primary-text-color);
|
||||
@@ -199,6 +268,10 @@ class HaInputSelectForm extends LitElement {
|
||||
border-radius: 4px;
|
||||
margin-top: 4px;
|
||||
--mdc-icon-button-size: 24px;
|
||||
--mdc-ripple-color: transparent;
|
||||
--mdc-list-side-padding: 16px;
|
||||
cursor: default;
|
||||
background-color: var(--card-background-color);
|
||||
}
|
||||
mwc-button {
|
||||
margin-left: 8px;
|
||||
@@ -214,6 +287,18 @@ class HaInputSelectForm extends LitElement {
|
||||
margin-top: 8px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.handle {
|
||||
cursor: move;
|
||||
padding-right: 12px;
|
||||
}
|
||||
.handle ha-svg-icon {
|
||||
pointer-events: none;
|
||||
height: 24px;
|
||||
}
|
||||
.optioncontent {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
@@ -9,6 +9,7 @@ import {
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { classMap } from "lit/directives/class-map";
|
||||
import { styleMap } from "lit/directives/style-map";
|
||||
import { HassEntity } from "home-assistant-js-websocket";
|
||||
import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import { alarmPanelIcon } from "../../../common/entity/alarm_panel_icon";
|
||||
@@ -20,16 +21,48 @@ import type { HaTextField } from "../../../components/ha-textfield";
|
||||
import {
|
||||
callAlarmAction,
|
||||
FORMAT_NUMBER,
|
||||
ALARM_MODES,
|
||||
AlarmMode,
|
||||
} from "../../../data/alarm_control_panel";
|
||||
import { UNAVAILABLE } from "../../../data/entity";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import { findEntities } from "../common/find-entities";
|
||||
import { createEntityNotFoundWarning } from "../components/hui-warning";
|
||||
import type { LovelaceCard } from "../types";
|
||||
import { AlarmPanelCardConfig } from "./types";
|
||||
import { AlarmPanelCardConfig, AlarmPanelCardConfigState } from "./types";
|
||||
import { supportsFeature } from "../../../common/entity/supports-feature";
|
||||
|
||||
const BUTTONS = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "", "0", "clear"];
|
||||
|
||||
export const DEFAULT_STATES = [
|
||||
"arm_home",
|
||||
"arm_away",
|
||||
] as AlarmPanelCardConfigState[];
|
||||
|
||||
export const ALARM_MODE_STATE_MAP: Record<
|
||||
AlarmPanelCardConfigState,
|
||||
AlarmMode
|
||||
> = {
|
||||
arm_home: "armed_home",
|
||||
arm_away: "armed_away",
|
||||
arm_night: "armed_night",
|
||||
arm_vacation: "armed_vacation",
|
||||
arm_custom_bypass: "armed_custom_bypass",
|
||||
};
|
||||
|
||||
export const filterSupportedAlarmStates = (
|
||||
stateObj: HassEntity | undefined,
|
||||
states: AlarmPanelCardConfigState[]
|
||||
): AlarmPanelCardConfigState[] =>
|
||||
states.filter(
|
||||
(s) =>
|
||||
stateObj &&
|
||||
supportsFeature(
|
||||
stateObj,
|
||||
ALARM_MODES[ALARM_MODE_STATE_MAP[s]].feature || 0
|
||||
)
|
||||
);
|
||||
|
||||
@customElement("hui-alarm-panel-card")
|
||||
class HuiAlarmPanelCard extends LitElement implements LovelaceCard {
|
||||
public static async getConfigElement() {
|
||||
@@ -52,10 +85,13 @@ class HuiAlarmPanelCard extends LitElement implements LovelaceCard {
|
||||
includeDomains
|
||||
);
|
||||
|
||||
const entity = foundEntities[0] || "";
|
||||
const stateObj = hass.states[entity];
|
||||
|
||||
return {
|
||||
type: "alarm-panel",
|
||||
states: ["arm_home", "arm_away"],
|
||||
entity: foundEntities[0] || "",
|
||||
states: filterSupportedAlarmStates(stateObj, DEFAULT_STATES),
|
||||
entity,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -86,11 +122,7 @@ class HuiAlarmPanelCard extends LitElement implements LovelaceCard {
|
||||
throw new Error("Invalid configuration");
|
||||
}
|
||||
|
||||
const defaults = {
|
||||
states: ["arm_away", "arm_home"] as const,
|
||||
};
|
||||
|
||||
this._config = { ...defaults, ...config };
|
||||
this._config = { ...config };
|
||||
}
|
||||
|
||||
protected updated(changedProps: PropertyValues): void {
|
||||
@@ -138,6 +170,9 @@ class HuiAlarmPanelCard extends LitElement implements LovelaceCard {
|
||||
return nothing;
|
||||
}
|
||||
const stateObj = this.hass.states[this._config.entity];
|
||||
const states =
|
||||
this._config.states ||
|
||||
filterSupportedAlarmStates(stateObj, DEFAULT_STATES);
|
||||
|
||||
if (!stateObj) {
|
||||
return html`
|
||||
@@ -170,7 +205,7 @@ class HuiAlarmPanelCard extends LitElement implements LovelaceCard {
|
||||
</h1>
|
||||
<div id="armActions" class="actions">
|
||||
${(stateObj.state === "disarmed"
|
||||
? this._config.states!
|
||||
? states
|
||||
: (["disarm"] as const)
|
||||
).map(
|
||||
(stateAction) => html`
|
||||
|
@@ -424,6 +424,7 @@ export class HuiTileCard extends LitElement implements LovelaceCard {
|
||||
if (this.hass) {
|
||||
element.hass = this.hass;
|
||||
(element as LovelaceTileFeature).stateObj = stateObj;
|
||||
(element as LovelaceTileFeature).color = this._config!.color;
|
||||
}
|
||||
|
||||
return html`${element}`;
|
||||
|
@@ -14,10 +14,17 @@ import { HaDurationData } from "../../../components/ha-duration-input";
|
||||
import { LovelaceTileFeatureConfig } from "../tile-features/types";
|
||||
import { ForecastType } from "../../../data/weather";
|
||||
|
||||
export type AlarmPanelCardConfigState =
|
||||
| "arm_away"
|
||||
| "arm_home"
|
||||
| "arm_night"
|
||||
| "arm_vacation"
|
||||
| "arm_custom_bypass";
|
||||
|
||||
export interface AlarmPanelCardConfig extends LovelaceCardConfig {
|
||||
entity: string;
|
||||
name?: string;
|
||||
states?: readonly (keyof TranslationDict["ui"]["card"]["alarm_control_panel"])[];
|
||||
states?: AlarmPanelCardConfigState[];
|
||||
theme?: string;
|
||||
}
|
||||
|
||||
|
@@ -37,6 +37,8 @@ const HIDE_DOMAIN = new Set([
|
||||
"sun",
|
||||
"zone",
|
||||
"event",
|
||||
"tts",
|
||||
"stt",
|
||||
]);
|
||||
|
||||
const HIDE_PLATFORM = new Set(["mobile_app"]);
|
||||
|
@@ -1,14 +1,15 @@
|
||||
import "../tile-features/hui-alarm-modes-tile-feature";
|
||||
import "../tile-features/hui-climate-hvac-modes-tile-feature";
|
||||
import "../tile-features/hui-target-temperature-tile-feature";
|
||||
import "../tile-features/hui-cover-open-close-tile-feature";
|
||||
import "../tile-features/hui-cover-position-tile-feature";
|
||||
import "../tile-features/hui-cover-tilt-position-tile-feature";
|
||||
import "../tile-features/hui-cover-tilt-tile-feature";
|
||||
import "../tile-features/hui-fan-speed-tile-feature";
|
||||
import "../tile-features/hui-lawn-mower-commands-tile-feature";
|
||||
import "../tile-features/hui-light-brightness-tile-feature";
|
||||
import "../tile-features/hui-light-color-temp-tile-feature";
|
||||
import "../tile-features/hui-vacuum-commands-tile-feature";
|
||||
import "../tile-features/hui-lawn-mower-commands-tile-feature";
|
||||
import "../tile-features/hui-water-heater-operation-modes-tile-feature";
|
||||
import { LovelaceTileFeatureConfig } from "../tile-features/types";
|
||||
import {
|
||||
@@ -17,17 +18,18 @@ import {
|
||||
} from "./create-element-base";
|
||||
|
||||
const TYPES: Set<LovelaceTileFeatureConfig["type"]> = new Set([
|
||||
"cover-open-close",
|
||||
"cover-position",
|
||||
"cover-tilt",
|
||||
"cover-tilt-position",
|
||||
"light-brightness",
|
||||
"light-color-temp",
|
||||
"vacuum-commands",
|
||||
"lawn-mower-commands",
|
||||
"fan-speed",
|
||||
"alarm-modes",
|
||||
"climate-hvac-modes",
|
||||
"cover-open-close",
|
||||
"cover-position",
|
||||
"cover-tilt-position",
|
||||
"cover-tilt",
|
||||
"fan-speed",
|
||||
"lawn-mower-commands",
|
||||
"light-brightness",
|
||||
"light-color-temp",
|
||||
"target-temperature",
|
||||
"vacuum-commands",
|
||||
"water-heater-operation-modes",
|
||||
]);
|
||||
|
||||
|
@@ -2,14 +2,25 @@ import { html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { array, assert, assign, object, optional, string } from "superstruct";
|
||||
import { HassEntity } from "home-assistant-js-websocket";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import type { LocalizeFunc } from "../../../../common/translations/localize";
|
||||
import "../../../../components/ha-form/ha-form";
|
||||
import type { SchemaUnion } from "../../../../components/ha-form/types";
|
||||
import type { HomeAssistant } from "../../../../types";
|
||||
import type { AlarmPanelCardConfig } from "../../cards/types";
|
||||
import type {
|
||||
AlarmPanelCardConfig,
|
||||
AlarmPanelCardConfigState,
|
||||
} from "../../cards/types";
|
||||
import type { LovelaceCardEditor } from "../../types";
|
||||
import { baseLovelaceCardConfig } from "../structs/base-card-struct";
|
||||
import {
|
||||
DEFAULT_STATES,
|
||||
ALARM_MODE_STATE_MAP,
|
||||
filterSupportedAlarmStates,
|
||||
} from "../../cards/hui-alarm-panel-card";
|
||||
import { supportsFeature } from "../../../../common/entity/supports-feature";
|
||||
import { ALARM_MODES } from "../../../../data/alarm_control_panel";
|
||||
|
||||
const cardConfigStruct = assign(
|
||||
baseLovelaceCardConfig,
|
||||
@@ -21,13 +32,7 @@ const cardConfigStruct = assign(
|
||||
})
|
||||
);
|
||||
|
||||
const states = [
|
||||
"arm_home",
|
||||
"arm_away",
|
||||
"arm_night",
|
||||
"arm_vacation",
|
||||
"arm_custom_bypass",
|
||||
] as const;
|
||||
const states = Object.keys(ALARM_MODE_STATE_MAP) as AlarmPanelCardConfigState[];
|
||||
|
||||
@customElement("hui-alarm-panel-card-editor")
|
||||
export class HuiAlarmPanelCardEditor
|
||||
@@ -44,7 +49,11 @@ export class HuiAlarmPanelCardEditor
|
||||
}
|
||||
|
||||
private _schema = memoizeOne(
|
||||
(localize: LocalizeFunc) =>
|
||||
(
|
||||
localize: LocalizeFunc,
|
||||
stateObj: HassEntity | undefined,
|
||||
config_states: AlarmPanelCardConfigState[]
|
||||
) =>
|
||||
[
|
||||
{
|
||||
name: "entity",
|
||||
@@ -60,12 +69,24 @@ export class HuiAlarmPanelCardEditor
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "multi_select",
|
||||
name: "states",
|
||||
options: states.map((s) => [
|
||||
s,
|
||||
localize(`ui.card.alarm_control_panel.${s}`),
|
||||
]) as [string, string][],
|
||||
selector: {
|
||||
select: {
|
||||
multiple: true,
|
||||
mode: "list",
|
||||
options: states.map((s) => ({
|
||||
value: s,
|
||||
label: localize(`ui.card.alarm_control_panel.${s}`),
|
||||
disabled:
|
||||
!config_states.includes(s) &&
|
||||
(!stateObj ||
|
||||
!supportsFeature(
|
||||
stateObj,
|
||||
ALARM_MODES[ALARM_MODE_STATE_MAP[s]].feature || 0
|
||||
)),
|
||||
})),
|
||||
},
|
||||
},
|
||||
},
|
||||
] as const
|
||||
);
|
||||
@@ -75,11 +96,18 @@ export class HuiAlarmPanelCardEditor
|
||||
return nothing;
|
||||
}
|
||||
|
||||
const stateObj = this.hass.states[this._config.entity];
|
||||
const defaultFilteredStates = filterSupportedAlarmStates(
|
||||
stateObj,
|
||||
DEFAULT_STATES
|
||||
);
|
||||
const config = { states: defaultFilteredStates, ...this._config };
|
||||
|
||||
return html`
|
||||
<ha-form
|
||||
.hass=${this.hass}
|
||||
.data=${this._config}
|
||||
.schema=${this._schema(this.hass.localize)}
|
||||
.data=${config}
|
||||
.schema=${this._schema(this.hass.localize, stateObj, config.states)}
|
||||
.computeLabel=${this._computeLabelCallback}
|
||||
@value-changed=${this._valueChanged}
|
||||
></ha-form>
|
||||
@@ -87,7 +115,26 @@ export class HuiAlarmPanelCardEditor
|
||||
}
|
||||
|
||||
private _valueChanged(ev: CustomEvent): void {
|
||||
fireEvent(this, "config-changed", { config: ev.detail.value });
|
||||
const newConfig = ev.detail.value;
|
||||
|
||||
// Sort states in a consistent order
|
||||
if (newConfig.states) {
|
||||
const sortStates = states.filter((s) => newConfig.states.includes(s));
|
||||
newConfig.states = sortStates;
|
||||
}
|
||||
|
||||
// When changing entities, clear any states that the new entity does not support
|
||||
if (newConfig.states && newConfig.entity !== this._config?.entity) {
|
||||
const newStateObj = this.hass?.states[newConfig.entity];
|
||||
if (newStateObj) {
|
||||
newConfig.states = filterSupportedAlarmStates(
|
||||
newStateObj,
|
||||
newConfig.states
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fireEvent(this, "config-changed", { config: newConfig });
|
||||
}
|
||||
|
||||
private _computeLabelCallback = (
|
||||
|
@@ -37,6 +37,7 @@ import { supportsLightColorTempTileFeature } from "../../tile-features/hui-light
|
||||
import { supportsVacuumCommandTileFeature } from "../../tile-features/hui-vacuum-commands-tile-feature";
|
||||
import { supportsWaterHeaterOperationModesTileFeature } from "../../tile-features/hui-water-heater-operation-modes-tile-feature";
|
||||
import { LovelaceTileFeatureConfig } from "../../tile-features/types";
|
||||
import { supportsTargetTemperatureTileFeature } from "../../tile-features/hui-target-temperature-tile-feature";
|
||||
|
||||
type FeatureType = LovelaceTileFeatureConfig["type"];
|
||||
type SupportsFeature = (stateObj: HassEntity) => boolean;
|
||||
@@ -44,6 +45,7 @@ type SupportsFeature = (stateObj: HassEntity) => boolean;
|
||||
const FEATURE_TYPES: FeatureType[] = [
|
||||
"alarm-modes",
|
||||
"climate-hvac-modes",
|
||||
"target-temperature",
|
||||
"cover-open-close",
|
||||
"cover-position",
|
||||
"cover-tilt-position",
|
||||
@@ -76,6 +78,7 @@ const SUPPORTS_FEATURE_TYPES: Record<FeatureType, SupportsFeature | undefined> =
|
||||
"lawn-mower-commands": supportsLawnMowerCommandTileFeature,
|
||||
"light-brightness": supportsLightBrightnessTileFeature,
|
||||
"light-color-temp": supportsLightColorTempTileFeature,
|
||||
"target-temperature": supportsTargetTemperatureTileFeature,
|
||||
"vacuum-commands": supportsVacuumCommandTileFeature,
|
||||
"water-heater-operation-modes":
|
||||
supportsWaterHeaterOperationModesTileFeature,
|
||||
@@ -151,8 +154,10 @@ export class HuiTileCardFeaturesEditor extends LitElement {
|
||||
const customFeatureEntry = CUSTOM_FEATURE_ENTRIES[customType];
|
||||
return customFeatureEntry?.name || type;
|
||||
}
|
||||
return this.hass!.localize(
|
||||
`ui.panel.lovelace.editor.card.tile.features.types.${type}.label`
|
||||
return (
|
||||
this.hass!.localize(
|
||||
`ui.panel.lovelace.editor.card.tile.features.types.${type}.label`
|
||||
) || type
|
||||
);
|
||||
}
|
||||
|
||||
|
@@ -324,8 +324,6 @@ export class LovelacePanel extends LitElement {
|
||||
urlPath: this.urlPath,
|
||||
editMode: this.lovelace ? this.lovelace.editMode : false,
|
||||
locale: this.hass!.locale,
|
||||
configHistory: this.lovelace ? this.lovelace.configHistory : [config],
|
||||
configHistoryIndex: this.lovelace ? this.lovelace.configHistoryIndex : 0,
|
||||
enableFullEditMode: () => {
|
||||
if (!editorLoaded) {
|
||||
editorLoaded = true;
|
||||
@@ -345,11 +343,7 @@ export class LovelacePanel extends LitElement {
|
||||
}
|
||||
|
||||
if (!editMode || this.lovelace!.mode !== "generated") {
|
||||
this._updateLovelace({
|
||||
editMode,
|
||||
configHistory: [this.lovelace!.config],
|
||||
configHistoryIndex: 0,
|
||||
});
|
||||
this._updateLovelace({ editMode });
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -364,8 +358,6 @@ export class LovelacePanel extends LitElement {
|
||||
config: previousConfig,
|
||||
rawConfig: previousRawConfig,
|
||||
mode: previousMode,
|
||||
configHistory: previousConfigHistory,
|
||||
configHistoryIndex: previousConfigHistoryIndex,
|
||||
} = this.lovelace!;
|
||||
newConfig = this._checkLovelaceConfig(newConfig);
|
||||
let conf: LovelaceConfig;
|
||||
@@ -380,22 +372,11 @@ export class LovelacePanel extends LitElement {
|
||||
conf = newConfig;
|
||||
}
|
||||
try {
|
||||
const newConfigHistory = [...previousConfigHistory];
|
||||
let newConfigHistoryIndex = previousConfigHistoryIndex;
|
||||
|
||||
if (previousConfigHistoryIndex !== 0) {
|
||||
newConfigHistory.splice(0, previousConfigHistoryIndex);
|
||||
newConfigHistoryIndex = 0;
|
||||
}
|
||||
newConfigHistory.unshift(newConfig);
|
||||
|
||||
// Optimistic update
|
||||
this._updateLovelace({
|
||||
config: conf,
|
||||
rawConfig: newConfig,
|
||||
mode: "storage",
|
||||
configHistory: newConfigHistory,
|
||||
configHistoryIndex: newConfigHistoryIndex,
|
||||
});
|
||||
this._ignoreNextUpdateEvent = true;
|
||||
await saveConfig(this.hass!, urlPath, newConfig);
|
||||
@@ -407,7 +388,6 @@ export class LovelacePanel extends LitElement {
|
||||
config: previousConfig,
|
||||
rawConfig: previousRawConfig,
|
||||
mode: previousMode,
|
||||
configHistory: previousConfigHistory,
|
||||
});
|
||||
throw err;
|
||||
}
|
||||
@@ -447,57 +427,6 @@ export class LovelacePanel extends LitElement {
|
||||
throw err;
|
||||
}
|
||||
},
|
||||
restoreConfigFromHistory: async (
|
||||
configHistoryIndex: number
|
||||
): Promise<void> => {
|
||||
const {
|
||||
configHistory,
|
||||
configHistoryIndex: previousConfigHistoryIndex,
|
||||
config: previousConfig,
|
||||
rawConfig: previousRawConfig,
|
||||
} = this.lovelace!;
|
||||
|
||||
if (
|
||||
previousConfigHistoryIndex === configHistoryIndex ||
|
||||
configHistoryIndex < 0 ||
|
||||
configHistoryIndex >= configHistory.length
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
let conf: LovelaceConfig;
|
||||
const newConfig = configHistory[configHistoryIndex];
|
||||
|
||||
if (newConfig.strategy) {
|
||||
conf = await generateLovelaceDashboardStrategy({
|
||||
config: newConfig,
|
||||
hass: this.hass!,
|
||||
narrow: this.narrow,
|
||||
});
|
||||
} else {
|
||||
conf = newConfig;
|
||||
}
|
||||
try {
|
||||
// Optimistic update
|
||||
this._updateLovelace({
|
||||
config: conf,
|
||||
rawConfig: newConfig,
|
||||
configHistoryIndex: configHistoryIndex,
|
||||
});
|
||||
this._ignoreNextUpdateEvent = true;
|
||||
await saveConfig(this.hass!, urlPath, newConfig);
|
||||
} catch (err: any) {
|
||||
// eslint-disable-next-line
|
||||
console.error(err);
|
||||
// Rollback the optimistic update
|
||||
this._updateLovelace({
|
||||
config: previousConfig,
|
||||
rawConfig: previousRawConfig,
|
||||
configHistoryIndex: previousConfigHistoryIndex,
|
||||
});
|
||||
throw err;
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
@@ -12,10 +12,8 @@ import {
|
||||
mdiMagnify,
|
||||
mdiPencil,
|
||||
mdiPlus,
|
||||
mdiRedo,
|
||||
mdiRefresh,
|
||||
mdiShape,
|
||||
mdiUndo,
|
||||
mdiViewDashboard,
|
||||
} from "@mdi/js";
|
||||
import "@polymer/paper-tabs/paper-tab";
|
||||
@@ -108,31 +106,12 @@ class HUIRoot extends LitElement {
|
||||
);
|
||||
}
|
||||
|
||||
private _undo() {
|
||||
if (this.lovelace) {
|
||||
this.lovelace.restoreConfigFromHistory(
|
||||
this.lovelace.configHistoryIndex + 1
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private _redo() {
|
||||
if (this.lovelace) {
|
||||
this.lovelace.restoreConfigFromHistory(
|
||||
this.lovelace.configHistoryIndex - 1
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
const views = this.lovelace?.config.views ?? [];
|
||||
|
||||
const curViewConfig =
|
||||
typeof this._curView === "number" ? views[this._curView] : undefined;
|
||||
|
||||
const historyTotal = this.lovelace?.configHistory.length ?? 0 - 1;
|
||||
const historyIndex = this.lovelace?.configHistoryIndex ?? 0;
|
||||
|
||||
return html`
|
||||
<div
|
||||
class=${classMap({
|
||||
@@ -165,18 +144,6 @@ class HUIRoot extends LitElement {
|
||||
)}
|
||||
@click=${this._editModeDisable}
|
||||
></mwc-button>
|
||||
<ha-icon-button
|
||||
label="Undo"
|
||||
.path=${mdiUndo}
|
||||
.disabled=${historyIndex + 1 >= historyTotal}
|
||||
@click=${this._undo}
|
||||
></ha-icon-button>
|
||||
<ha-icon-button
|
||||
label="Redo"
|
||||
.path=${mdiRedo}
|
||||
.disabled=${historyIndex === 0}
|
||||
@click=${this._redo}
|
||||
></ha-icon-button>
|
||||
<a
|
||||
href=${documentationUrl(this.hass, "/dashboards/")}
|
||||
rel="noreferrer"
|
||||
|
@@ -1,15 +1,18 @@
|
||||
import { HassEntity } from "home-assistant-js-websocket";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { styleMap } from "lit/directives/style-map";
|
||||
import { computeCssColor } from "../../../common/color/compute-color";
|
||||
import { computeAttributeNameDisplay } from "../../../common/entity/compute_attribute_display";
|
||||
import { computeDomain } from "../../../common/entity/compute_domain";
|
||||
import { stateActive } from "../../../common/entity/state_active";
|
||||
import { stateColorCss } from "../../../common/entity/state_color";
|
||||
import { supportsFeature } from "../../../common/entity/supports-feature";
|
||||
import { CoverEntityFeature } from "../../../data/cover";
|
||||
import { UNAVAILABLE } from "../../../data/entity";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
import { LovelaceTileFeature } from "../types";
|
||||
import { CoverPositionTileFeatureConfig } from "./types";
|
||||
import { stateActive } from "../../../common/entity/state_active";
|
||||
import { computeAttributeNameDisplay } from "../../../common/entity/compute_attribute_display";
|
||||
import { UNAVAILABLE } from "../../../data/entity";
|
||||
|
||||
export const supportsCoverPositionTileFeature = (stateObj: HassEntity) => {
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
@@ -28,6 +31,8 @@ class HuiCoverPositionTileFeature
|
||||
|
||||
@property({ attribute: false }) public stateObj?: HassEntity;
|
||||
|
||||
@property({ attribute: false }) public color?: string;
|
||||
|
||||
@state() private _config?: CoverPositionTileFeatureConfig;
|
||||
|
||||
static getStubConfig(): CoverPositionTileFeatureConfig {
|
||||
@@ -59,24 +64,36 @@ class HuiCoverPositionTileFeature
|
||||
|
||||
const value = Math.max(Math.round(percentage), 0);
|
||||
|
||||
return html` <div class="container">
|
||||
<ha-control-slider
|
||||
.value=${value}
|
||||
min="0"
|
||||
max="100"
|
||||
step="1"
|
||||
inverted
|
||||
show-handle
|
||||
@value-changed=${this._valueChanged}
|
||||
.ariaLabel=${computeAttributeNameDisplay(
|
||||
this.hass.localize,
|
||||
this.stateObj,
|
||||
this.hass.entities,
|
||||
"current_position"
|
||||
)}
|
||||
.disabled=${this.stateObj!.state === UNAVAILABLE}
|
||||
></ha-control-slider>
|
||||
</div>`;
|
||||
const forcedState = this.stateObj.state === "closed" ? "open" : undefined;
|
||||
|
||||
const color = this.color
|
||||
? computeCssColor(this.color)
|
||||
: stateColorCss(this.stateObj, forcedState);
|
||||
|
||||
const style = {
|
||||
"--color": color,
|
||||
};
|
||||
|
||||
return html`
|
||||
<div class="container" style=${styleMap(style)}>
|
||||
<ha-control-slider
|
||||
.value=${value}
|
||||
min="0"
|
||||
max="100"
|
||||
step="1"
|
||||
inverted
|
||||
show-handle
|
||||
@value-changed=${this._valueChanged}
|
||||
.ariaLabel=${computeAttributeNameDisplay(
|
||||
this.hass.localize,
|
||||
this.stateObj,
|
||||
this.hass.entities,
|
||||
"current_position"
|
||||
)}
|
||||
.disabled=${this.stateObj!.state === UNAVAILABLE}
|
||||
></ha-control-slider>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
private _valueChanged(ev: CustomEvent) {
|
||||
@@ -92,9 +109,8 @@ class HuiCoverPositionTileFeature
|
||||
static get styles() {
|
||||
return css`
|
||||
ha-control-slider {
|
||||
/* Force inactive state to be colored for the slider */
|
||||
--control-slider-color: var(--tile-color);
|
||||
--control-slider-background: var(--tile-color);
|
||||
--control-slider-color: var(--color);
|
||||
--control-slider-background: var(--color);
|
||||
--control-slider-background-opacity: 0.2;
|
||||
--control-slider-thickness: 40px;
|
||||
--control-slider-border-radius: 10px;
|
||||
|
@@ -1,8 +1,11 @@
|
||||
import { HassEntity } from "home-assistant-js-websocket";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { styleMap } from "lit/directives/style-map";
|
||||
import { computeCssColor } from "../../../common/color/compute-color";
|
||||
import { computeAttributeNameDisplay } from "../../../common/entity/compute_attribute_display";
|
||||
import { computeDomain } from "../../../common/entity/compute_domain";
|
||||
import { stateColorCss } from "../../../common/entity/state_color";
|
||||
import { supportsFeature } from "../../../common/entity/supports-feature";
|
||||
import { CoverEntity, CoverEntityFeature } from "../../../data/cover";
|
||||
import { UNAVAILABLE } from "../../../data/entity";
|
||||
@@ -30,6 +33,8 @@ class HuiCoverTiltPositionTileFeature
|
||||
|
||||
@property({ attribute: false }) public stateObj?: CoverEntity;
|
||||
|
||||
@property({ attribute: false }) public color?: string;
|
||||
|
||||
@state() private _config?: CoverTiltPositionTileFeatureConfig;
|
||||
|
||||
static getStubConfig(): CoverTiltPositionTileFeatureConfig {
|
||||
@@ -59,8 +64,18 @@ class HuiCoverTiltPositionTileFeature
|
||||
|
||||
const value = Math.max(Math.round(percentage), 0);
|
||||
|
||||
const forcedState = this.stateObj.state === "closed" ? "open" : undefined;
|
||||
|
||||
const color = this.color
|
||||
? computeCssColor(this.color)
|
||||
: stateColorCss(this.stateObj, forcedState);
|
||||
|
||||
const style = {
|
||||
"--color": color,
|
||||
};
|
||||
|
||||
return html`
|
||||
<div class="container">
|
||||
<div class="container" style=${styleMap(style)}>
|
||||
<ha-control-slider
|
||||
.value=${value}
|
||||
min="0"
|
||||
@@ -96,8 +111,8 @@ class HuiCoverTiltPositionTileFeature
|
||||
return css`
|
||||
ha-control-slider {
|
||||
/* Force inactive state to be colored for the slider */
|
||||
--control-slider-color: var(--tile-color);
|
||||
--control-slider-background: var(--tile-color);
|
||||
--control-slider-color: var(--color);
|
||||
--control-slider-background: var(--color);
|
||||
--control-slider-background-opacity: 0.2;
|
||||
--control-slider-thickness: 40px;
|
||||
--control-slider-border-radius: 10px;
|
||||
|
@@ -0,0 +1,254 @@
|
||||
import { HassEntity } from "home-assistant-js-websocket";
|
||||
import { css, html, LitElement, nothing, PropertyValues } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { styleMap } from "lit/directives/style-map";
|
||||
import { UNIT_F } from "../../../common/const";
|
||||
import { computeDomain } from "../../../common/entity/compute_domain";
|
||||
import { computeStateDomain } from "../../../common/entity/compute_state_domain";
|
||||
import { stateColorCss } from "../../../common/entity/state_color";
|
||||
import { supportsFeature } from "../../../common/entity/supports-feature";
|
||||
import { debounce } from "../../../common/util/debounce";
|
||||
import "../../../components/ha-control-button-group";
|
||||
import "../../../components/ha-control-number-buttons";
|
||||
import { ClimateEntity, ClimateEntityFeature } from "../../../data/climate";
|
||||
import { UNAVAILABLE } from "../../../data/entity";
|
||||
import {
|
||||
WaterHeaterEntity,
|
||||
WaterHeaterEntityFeature,
|
||||
} from "../../../data/water_heater";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
import { LovelaceTileFeature } from "../types";
|
||||
import { TargetTemperatureTileFeatureConfig } from "./types";
|
||||
|
||||
type Target = "value" | "low" | "high";
|
||||
|
||||
export const supportsTargetTemperatureTileFeature = (stateObj: HassEntity) => {
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return (
|
||||
(domain === "climate" &&
|
||||
(supportsFeature(stateObj, ClimateEntityFeature.TARGET_TEMPERATURE) ||
|
||||
supportsFeature(
|
||||
stateObj,
|
||||
ClimateEntityFeature.TARGET_TEMPERATURE_RANGE
|
||||
))) ||
|
||||
(domain === "water_heater" &&
|
||||
supportsFeature(stateObj, WaterHeaterEntityFeature.TARGET_TEMPERATURE))
|
||||
);
|
||||
};
|
||||
|
||||
@customElement("hui-target-temperature-tile-feature")
|
||||
class HuiTargetTemperatureTileFeature
|
||||
extends LitElement
|
||||
implements LovelaceTileFeature
|
||||
{
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public stateObj?:
|
||||
| ClimateEntity
|
||||
| WaterHeaterEntity;
|
||||
|
||||
@state() private _config?: TargetTemperatureTileFeatureConfig;
|
||||
|
||||
@state() private _targetTemperature: Partial<Record<Target, number>> = {};
|
||||
|
||||
static getStubConfig(): TargetTemperatureTileFeatureConfig {
|
||||
return {
|
||||
type: "target-temperature",
|
||||
};
|
||||
}
|
||||
|
||||
public setConfig(config: TargetTemperatureTileFeatureConfig): void {
|
||||
if (!config) {
|
||||
throw new Error("Invalid configuration");
|
||||
}
|
||||
this._config = config;
|
||||
}
|
||||
|
||||
protected willUpdate(changedProp: PropertyValues): void {
|
||||
super.willUpdate(changedProp);
|
||||
if (changedProp.has("stateObj")) {
|
||||
this._targetTemperature = {
|
||||
value: this.stateObj!.attributes.temperature,
|
||||
low:
|
||||
"target_temp_low" in this.stateObj!.attributes
|
||||
? this.stateObj!.attributes.target_temp_low
|
||||
: undefined,
|
||||
high:
|
||||
"target_temp_high" in this.stateObj!.attributes
|
||||
? this.stateObj!.attributes.target_temp_high
|
||||
: undefined,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private get _step() {
|
||||
return (
|
||||
this.stateObj!.attributes.target_temp_step ||
|
||||
(this.hass!.config.unit_system.temperature === UNIT_F ? 1 : 0.5)
|
||||
);
|
||||
}
|
||||
|
||||
private get _min() {
|
||||
return this.stateObj!.attributes.min_temp;
|
||||
}
|
||||
|
||||
private get _max() {
|
||||
return this.stateObj!.attributes.max_temp;
|
||||
}
|
||||
|
||||
private async _valueChanged(ev: CustomEvent) {
|
||||
const value = (ev.detail as any).value;
|
||||
if (isNaN(value)) return;
|
||||
const target = (ev.currentTarget as any).target ?? "value";
|
||||
|
||||
this._targetTemperature = {
|
||||
...this._targetTemperature,
|
||||
[target]: value,
|
||||
};
|
||||
this._debouncedCallService(target);
|
||||
}
|
||||
|
||||
private _debouncedCallService = debounce(
|
||||
(target: Target) => this._callService(target),
|
||||
1000
|
||||
);
|
||||
|
||||
private _callService(type: string) {
|
||||
const domain = computeStateDomain(this.stateObj!);
|
||||
if (type === "high" || type === "low") {
|
||||
this.hass!.callService(domain, "set_temperature", {
|
||||
entity_id: this.stateObj!.entity_id,
|
||||
target_temp_low: this._targetTemperature.low,
|
||||
target_temp_high: this._targetTemperature.high,
|
||||
});
|
||||
return;
|
||||
}
|
||||
this.hass!.callService(domain, "set_temperature", {
|
||||
entity_id: this.stateObj!.entity_id,
|
||||
temperature: this._targetTemperature.value,
|
||||
});
|
||||
}
|
||||
|
||||
protected render() {
|
||||
if (
|
||||
!this._config ||
|
||||
!this.hass ||
|
||||
!this.stateObj ||
|
||||
!supportsTargetTemperatureTileFeature(this.stateObj)
|
||||
) {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
const stateColor = stateColorCss(this.stateObj);
|
||||
const digits = this._step.toString().split(".")?.[1]?.length ?? 0;
|
||||
|
||||
const options = {
|
||||
maximumFractionDigits: digits,
|
||||
minimumFractionDigits: digits,
|
||||
};
|
||||
|
||||
const domain = computeStateDomain(this.stateObj!);
|
||||
|
||||
if (
|
||||
(domain === "climate" &&
|
||||
supportsFeature(
|
||||
this.stateObj,
|
||||
ClimateEntityFeature.TARGET_TEMPERATURE
|
||||
)) ||
|
||||
(domain === "water_heater" &&
|
||||
supportsFeature(
|
||||
this.stateObj,
|
||||
WaterHeaterEntityFeature.TARGET_TEMPERATURE
|
||||
))
|
||||
) {
|
||||
return html`
|
||||
<ha-control-button-group>
|
||||
<ha-control-number-buttons
|
||||
.formatOptions=${options}
|
||||
.target="value"
|
||||
.value=${this.stateObj.attributes.temperature}
|
||||
.min=${this._min}
|
||||
.max=${this._max}
|
||||
.step=${this._step}
|
||||
@value-changed=${this._valueChanged}
|
||||
.label=${this.hass.formatEntityAttributeName(
|
||||
this.stateObj,
|
||||
"temperature"
|
||||
)}
|
||||
style=${styleMap({
|
||||
"--control-number-buttons-focus-color": stateColor,
|
||||
})}
|
||||
.disabled=${this.stateObj!.state === UNAVAILABLE}
|
||||
>
|
||||
</ha-control-number-buttons>
|
||||
</ha-control-number-buttons>
|
||||
`;
|
||||
}
|
||||
|
||||
if (
|
||||
domain === "climate" &&
|
||||
supportsFeature(
|
||||
this.stateObj,
|
||||
ClimateEntityFeature.TARGET_TEMPERATURE_RANGE
|
||||
)
|
||||
) {
|
||||
return html`
|
||||
<ha-control-button-group>
|
||||
<ha-control-number-buttons
|
||||
.formatOptions=${options}
|
||||
.target=${"low"}
|
||||
.value=${(this.stateObj as ClimateEntity).attributes.target_temp_low}
|
||||
.min=${this._min}
|
||||
.max=${Math.min(this._max, this._targetTemperature.high ?? this._max)}
|
||||
.step=${this._step}
|
||||
@value-changed=${this._valueChanged}
|
||||
.label=${this.hass.formatEntityAttributeName(
|
||||
this.stateObj,
|
||||
"temperature"
|
||||
)}
|
||||
style=${styleMap({
|
||||
"--control-number-buttons-focus-color": stateColor,
|
||||
})}
|
||||
.disabled=${this.stateObj!.state === UNAVAILABLE}
|
||||
>
|
||||
</ha-control-number-buttons>
|
||||
<ha-control-number-buttons
|
||||
.formatOptions=${options}
|
||||
.target=${"high"}
|
||||
.value=${(this.stateObj as ClimateEntity).attributes.target_temp_high}
|
||||
.min=${Math.max(this._min, this._targetTemperature.low ?? this._min)}
|
||||
.max=${this._max}
|
||||
.step=${this._step}
|
||||
@value-changed=${this._valueChanged}
|
||||
.label=${this.hass.formatEntityAttributeName(
|
||||
this.stateObj,
|
||||
"temperature"
|
||||
)}
|
||||
style=${styleMap({
|
||||
"--control-number-buttons-focus-color": stateColor,
|
||||
})}
|
||||
.disabled=${this.stateObj!.state === UNAVAILABLE}
|
||||
>
|
||||
</ha-control-number-buttons>
|
||||
</ha-control-number-buttons>
|
||||
`;
|
||||
}
|
||||
|
||||
return nothing;
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
return css`
|
||||
ha-control-button-group {
|
||||
margin: 0 12px 12px 12px;
|
||||
--control-button-group-spacing: 12px;
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"hui-target-temperature-tile-feature": HuiTargetTemperatureTileFeature;
|
||||
}
|
||||
}
|
@@ -40,6 +40,10 @@ export interface ClimateHvacModesTileFeatureConfig {
|
||||
hvac_modes?: HvacMode[];
|
||||
}
|
||||
|
||||
export interface TargetTemperatureTileFeatureConfig {
|
||||
type: "target-temperature";
|
||||
}
|
||||
|
||||
export interface WaterHeaterOperationModesTileFeatureConfig {
|
||||
type: "water-heater-operation-modes";
|
||||
operation_modes?: OperationMode[];
|
||||
@@ -81,6 +85,7 @@ export type LovelaceTileFeatureConfig =
|
||||
| LightBrightnessTileFeatureConfig
|
||||
| LightColorTempTileFeatureConfig
|
||||
| VacuumCommandsTileFeatureConfig
|
||||
| TargetTemperatureTileFeatureConfig
|
||||
| WaterHeaterOperationModesTileFeatureConfig;
|
||||
|
||||
export type LovelaceTileFeatureContext = {
|
||||
|
@@ -32,9 +32,6 @@ export interface Lovelace {
|
||||
setEditMode: (editMode: boolean) => void;
|
||||
saveConfig: (newConfig: LovelaceConfig) => Promise<void>;
|
||||
deleteConfig: () => Promise<void>;
|
||||
configHistory: LovelaceConfig[];
|
||||
configHistoryIndex: number;
|
||||
restoreConfigFromHistory: (index: number) => Promise<void>;
|
||||
}
|
||||
|
||||
export interface LovelaceBadge extends HTMLElement {
|
||||
@@ -119,6 +116,7 @@ export interface LovelaceTileFeature extends HTMLElement {
|
||||
hass?: HomeAssistant;
|
||||
stateObj?: HassEntity;
|
||||
setConfig(config: LovelaceTileFeatureConfig);
|
||||
color?: string;
|
||||
}
|
||||
|
||||
export interface LovelaceTileFeatureConstructor
|
||||
|
@@ -14,6 +14,12 @@ import "../../components/ha-textfield";
|
||||
import { haStyle } from "../../resources/styles";
|
||||
import type { HomeAssistant } from "../../types";
|
||||
import "../../components/ha-alert";
|
||||
import {
|
||||
showAlertDialog,
|
||||
showConfirmationDialog,
|
||||
} from "../../dialogs/generic/show-dialog-box";
|
||||
import { RefreshToken } from "../../data/refresh_token";
|
||||
import { changePassword, deleteAllRefreshTokens } from "../../data/auth";
|
||||
|
||||
@customElement("ha-change-password-card")
|
||||
class HaChangePasswordCard extends LitElement {
|
||||
@@ -31,6 +37,8 @@ class HaChangePasswordCard extends LitElement {
|
||||
|
||||
@state() private _passwordConfirm = "";
|
||||
|
||||
@property({ attribute: false }) public refreshTokens?: RefreshToken[];
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`
|
||||
<ha-card
|
||||
@@ -148,11 +156,7 @@ class HaChangePasswordCard extends LitElement {
|
||||
this._errorMsg = undefined;
|
||||
|
||||
try {
|
||||
await this.hass.callWS({
|
||||
type: "config/auth_provider/homeassistant/change_password",
|
||||
current_password: this._currentPassword,
|
||||
new_password: this._password,
|
||||
});
|
||||
await changePassword(this.hass, this._currentPassword, this._password);
|
||||
} catch (err: any) {
|
||||
this._errorMsg = err.message;
|
||||
return;
|
||||
@@ -163,6 +167,33 @@ class HaChangePasswordCard extends LitElement {
|
||||
this._statusMsg = this.hass.localize(
|
||||
"ui.panel.profile.change_password.success"
|
||||
);
|
||||
|
||||
if (
|
||||
this.refreshTokens &&
|
||||
(await showConfirmationDialog(this, {
|
||||
title: this.hass.localize(
|
||||
"ui.panel.profile.change_password.logout_all_sessions"
|
||||
),
|
||||
text: this.hass.localize(
|
||||
"ui.panel.profile.change_password.logout_all_sessions_text"
|
||||
),
|
||||
dismissText: this.hass.localize("ui.common.no"),
|
||||
confirmText: this.hass.localize("ui.common.yes"),
|
||||
destructive: true,
|
||||
}))
|
||||
) {
|
||||
try {
|
||||
await deleteAllRefreshTokens(this.hass);
|
||||
} catch (err: any) {
|
||||
await showAlertDialog(this, {
|
||||
title: this.hass.localize(
|
||||
"ui.panel.profile.change_password.delete_failed"
|
||||
),
|
||||
text: err.message,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
this._currentPassword = "";
|
||||
this._password = "";
|
||||
this._passwordConfirm = "";
|
||||
|
@@ -188,6 +188,8 @@ class HaPanelProfile extends LitElement {
|
||||
)
|
||||
? html`
|
||||
<ha-change-password-card
|
||||
.refreshTokens=${this._refreshTokens}
|
||||
@hass-refresh-tokens=${this._refreshRefreshTokens}
|
||||
.hass=${this.hass}
|
||||
></ha-change-password-card>
|
||||
`
|
||||
|
@@ -38,7 +38,6 @@ export const createLogMessage = async (
|
||||
.slice(0, MAX_STACK_FRAMES)
|
||||
.map((frame) => {
|
||||
frame.fileName ??= "";
|
||||
// @ts-expect-error canParse not in DOM library yet
|
||||
if (URL.canParse(frame.fileName)) {
|
||||
frame.fileName = new URL(frame.fileName).pathname;
|
||||
}
|
||||
|
@@ -2735,6 +2735,10 @@
|
||||
"until": {
|
||||
"label": "Until",
|
||||
"conditions": "Until conditions"
|
||||
},
|
||||
"for_each": {
|
||||
"label": "For each",
|
||||
"items": "For each item in list"
|
||||
}
|
||||
},
|
||||
"sequence": "Actions",
|
||||
@@ -5027,6 +5031,9 @@
|
||||
"label": "Climate HVAC modes",
|
||||
"hvac_modes": "HVAC modes"
|
||||
},
|
||||
"target-temperature": {
|
||||
"label": "Target temperature"
|
||||
},
|
||||
"water-heater-operation-modes": {
|
||||
"label": "Water heater operation modes",
|
||||
"operation_modes": "Operation modes"
|
||||
@@ -5305,7 +5312,10 @@
|
||||
"submit": "Submit",
|
||||
"error_new_mismatch": "Entered new password values do not match",
|
||||
"error_new_is_old": "New password must be different than current password",
|
||||
"success": "Password changed successfully"
|
||||
"success": "Password changed successfully",
|
||||
"logout_all_sessions": "Do you want to log out from all devices?",
|
||||
"logout_all_sessions_text": "This will require you to log in again on all devices with your new password, including this one.",
|
||||
"delete_failed": "Not all devices could be logged out"
|
||||
},
|
||||
"mfa": {
|
||||
"header": "Multi-factor authentication modules",
|
||||
@@ -5677,7 +5687,21 @@
|
||||
"intro": "Are you ready to awaken your home, reclaim your privacy and join a worldwide community of tinkerers?",
|
||||
"next": "Next",
|
||||
"finish": "Finish",
|
||||
"help": "Help",
|
||||
"welcome": {
|
||||
"header": "Welcome!",
|
||||
"start": "Create my smart home",
|
||||
"restore_backup": "Restore from backup",
|
||||
"vision": "Read our vision",
|
||||
"community": "Join our community",
|
||||
"download_app": "Download our app",
|
||||
"forums": "Home Assistant forums",
|
||||
"open_home_newsletter": "Building the Open Home newsletter",
|
||||
"discord": "Discord chat",
|
||||
"twitter": "Twitter"
|
||||
},
|
||||
"user": {
|
||||
"header": "Create user",
|
||||
"intro": "Let's get started by creating a user account.",
|
||||
"required_field": "Required",
|
||||
"data": {
|
||||
@@ -5686,20 +5710,22 @@
|
||||
"password": "Password",
|
||||
"password_confirm": "Confirm password"
|
||||
},
|
||||
"helper": {
|
||||
"password": "Choose a strong and unique password. Make sure to save it, so you don't forget it."
|
||||
},
|
||||
"create_account": "Create account",
|
||||
"error": {
|
||||
"password_not_match": "Passwords don't match"
|
||||
}
|
||||
},
|
||||
"core-config": {
|
||||
"intro": "Hello {name}, welcome to Home Assistant. How would you like to name your home?",
|
||||
"intro_core": "We will set up the basics together. You can always change this later in the settings.",
|
||||
"intro_location": "Let's set up the location of your home so that you can display information such as the local weather and use sun-based or presence-based automations. This data is never shared outside of your network.",
|
||||
"location_header": "Home location",
|
||||
"intro_location": "Let's set up the location of your home so that you can display information such as the local weather and use sun-based or presence-based automations.",
|
||||
"location_address": "Powered by {openstreetmap} ({osm_privacy_policy}).",
|
||||
"osm_privacy_policy": "Privacy policy",
|
||||
"title_location_detect": "Do you want us to detect your location?",
|
||||
"intro_location_detect": "We can detect your location by making a one-time request to an external service.",
|
||||
"intro_core_config": "We filled out some details about your location. Please check if they are correct and continue.",
|
||||
"country_intro": "We would like to know the country your home is in, so we can use the correct units for you.",
|
||||
"location_name": "Name of your Home Assistant installation",
|
||||
"location_name_default": "Home",
|
||||
"address_label": "Search address",
|
||||
@@ -5707,11 +5733,13 @@
|
||||
"finish": "Next"
|
||||
},
|
||||
"integration": {
|
||||
"intro": "Devices and services are represented in Home Assistant as integrations. You can set them up now, or do it later from the settings.",
|
||||
"more_integrations": "More",
|
||||
"header": "We found compatible devices!",
|
||||
"intro": "These are found on your local network. Some are already added, others may need extra configuration.",
|
||||
"more_integrations": "+{count} more",
|
||||
"finish": "Finish"
|
||||
},
|
||||
"analytics": {
|
||||
"header": "Help us help you",
|
||||
"finish": "Next",
|
||||
"preferences": {
|
||||
"base": {
|
||||
@@ -5736,8 +5764,9 @@
|
||||
"intro": "[%key:ui::panel::config::analytics::intro%]"
|
||||
},
|
||||
"restore": {
|
||||
"description": "Alternatively you can restore from a previous backup.",
|
||||
"header": "Restore a backup",
|
||||
"in_progress": "Restore in progress",
|
||||
"upload_backup": "[%key:supervisor::backup::upload_backup%]",
|
||||
"show_log": "Show full log",
|
||||
"hide_log": "Hide full log",
|
||||
"full_backup": "[%key:supervisor::backup::full_backup%]",
|
||||
@@ -5749,8 +5778,7 @@
|
||||
"addons": "[%key:supervisor::backup::addons%]",
|
||||
"password_protection": "[%key:supervisor::backup::password_protection%]",
|
||||
"password": "[%key:supervisor::backup::password%]",
|
||||
"confirm_password": "[%key:supervisor::backup::confirm_password%]",
|
||||
"upload_backup": "[%key:supervisor::backup::upload_backup%]"
|
||||
"confirm_password": "[%key:supervisor::backup::confirm_password%]"
|
||||
}
|
||||
},
|
||||
"custom": {
|
||||
@@ -5843,7 +5871,7 @@
|
||||
"new_update_available": "{name} {version} is available",
|
||||
"not_available_arch": "This add-on is not compatible with the processor of your device or the operating system you have installed on your device.",
|
||||
"not_available_version": "You are running Home Assistant {core_version_installed}, to update to this version of the add-on you need at least version {core_version_needed} of Home Assistant",
|
||||
"visit_addon_page": "Visit the {name} page for more details",
|
||||
"visit_addon_page": "Visit the {name} page for more details.",
|
||||
"restart": "restart",
|
||||
"start": "start",
|
||||
"stop": "stop",
|
||||
|
472
yarn.lock
472
yarn.lock
@@ -62,26 +62,26 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@babel/core@npm:7.22.10, @babel/core@npm:^7.11.1, @babel/core@npm:^7.12.3":
|
||||
version: 7.22.10
|
||||
resolution: "@babel/core@npm:7.22.10"
|
||||
"@babel/core@npm:7.22.11, @babel/core@npm:^7.11.1, @babel/core@npm:^7.12.3":
|
||||
version: 7.22.11
|
||||
resolution: "@babel/core@npm:7.22.11"
|
||||
dependencies:
|
||||
"@ampproject/remapping": ^2.2.0
|
||||
"@babel/code-frame": ^7.22.10
|
||||
"@babel/generator": ^7.22.10
|
||||
"@babel/helper-compilation-targets": ^7.22.10
|
||||
"@babel/helper-module-transforms": ^7.22.9
|
||||
"@babel/helpers": ^7.22.10
|
||||
"@babel/parser": ^7.22.10
|
||||
"@babel/helpers": ^7.22.11
|
||||
"@babel/parser": ^7.22.11
|
||||
"@babel/template": ^7.22.5
|
||||
"@babel/traverse": ^7.22.10
|
||||
"@babel/types": ^7.22.10
|
||||
"@babel/traverse": ^7.22.11
|
||||
"@babel/types": ^7.22.11
|
||||
convert-source-map: ^1.7.0
|
||||
debug: ^4.1.0
|
||||
gensync: ^1.0.0-beta.2
|
||||
json5: ^2.2.2
|
||||
json5: ^2.2.3
|
||||
semver: ^6.3.1
|
||||
checksum: cc4efa09209fe1f733cf512e9e4bb50870b191ab2dee8014e34cd6e731f204e48476cc53b4bbd0825d4d342304d577ae43ff5fd8ab3896080673c343321acb32
|
||||
checksum: f258b2539ea2e5bfe55a708c2f3e1093a1b4744f12becc35abeb896037b66210de9a8ad6296a706046d5dc3a24e564362b73a9b814e5bfe500c8baab60c22d2e
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -128,9 +128,9 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@babel/helper-create-class-features-plugin@npm:^7.22.10, @babel/helper-create-class-features-plugin@npm:^7.22.5":
|
||||
version: 7.22.10
|
||||
resolution: "@babel/helper-create-class-features-plugin@npm:7.22.10"
|
||||
"@babel/helper-create-class-features-plugin@npm:^7.22.10, @babel/helper-create-class-features-plugin@npm:^7.22.11, @babel/helper-create-class-features-plugin@npm:^7.22.5":
|
||||
version: 7.22.11
|
||||
resolution: "@babel/helper-create-class-features-plugin@npm:7.22.11"
|
||||
dependencies:
|
||||
"@babel/helper-annotate-as-pure": ^7.22.5
|
||||
"@babel/helper-environment-visitor": ^7.22.5
|
||||
@@ -143,7 +143,7 @@ __metadata:
|
||||
semver: ^6.3.1
|
||||
peerDependencies:
|
||||
"@babel/core": ^7.0.0
|
||||
checksum: 9683edbf73889abce183b06eac29524448aaab1dba7bdccdd6c26cf03e5ade3903b581b4d681da88fbff824fa117b840cc945bebf7db3c1f8c745f3c5a8a2595
|
||||
checksum: b7aeb22e29aba5327616328576363522b3b186918faeda605e300822af4a5f29416eb34b5bd825d07ab496550e271d02d7634f0022a62b5b8cbf0eb6389bc3fa
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -335,14 +335,14 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@babel/helpers@npm:^7.22.10":
|
||||
version: 7.22.10
|
||||
resolution: "@babel/helpers@npm:7.22.10"
|
||||
"@babel/helpers@npm:^7.22.11":
|
||||
version: 7.22.11
|
||||
resolution: "@babel/helpers@npm:7.22.11"
|
||||
dependencies:
|
||||
"@babel/template": ^7.22.5
|
||||
"@babel/traverse": ^7.22.10
|
||||
"@babel/types": ^7.22.10
|
||||
checksum: 3b1219e362df390b6c5d94b75a53fc1c2eb42927ced0b8022d6a29b833a839696206b9bdad45b4805d05591df49fc16b6fb7db758c9c2ecfe99e3e94cb13020f
|
||||
"@babel/traverse": ^7.22.11
|
||||
"@babel/types": ^7.22.11
|
||||
checksum: 93186544228b5e371486466ec3b86a77cce91beeff24a5670ca8ec46d50328f7700dab82d532351286e9d68624dc51d6d71589b051dd9535e44be077a43ec013
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -357,12 +357,12 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@babel/parser@npm:^7.18.4, @babel/parser@npm:^7.22.10, @babel/parser@npm:^7.22.5":
|
||||
version: 7.22.10
|
||||
resolution: "@babel/parser@npm:7.22.10"
|
||||
"@babel/parser@npm:^7.18.4, @babel/parser@npm:^7.22.11, @babel/parser@npm:^7.22.5":
|
||||
version: 7.22.11
|
||||
resolution: "@babel/parser@npm:7.22.11"
|
||||
bin:
|
||||
parser: ./bin/babel-parser.js
|
||||
checksum: af51567b7d3cdf523bc608eae057397486c7fa6c2e5753027c01fe5c36f0767b2d01ce3049b222841326cc5b8c7fda1d810ac1a01af0a97bb04679e2ef9f7049
|
||||
checksum: 332079ed09794d3685343e9fc39c6a12dcb6ea589119f2135952cdef2424296786bb609a33f6dfa9be271797bbf8339f1865118418ea50b32a0c701734c96664
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -914,16 +914,16 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@babel/plugin-transform-modules-commonjs@npm:^7.22.5":
|
||||
version: 7.22.5
|
||||
resolution: "@babel/plugin-transform-modules-commonjs@npm:7.22.5"
|
||||
"@babel/plugin-transform-modules-commonjs@npm:^7.22.11, @babel/plugin-transform-modules-commonjs@npm:^7.22.5":
|
||||
version: 7.22.11
|
||||
resolution: "@babel/plugin-transform-modules-commonjs@npm:7.22.11"
|
||||
dependencies:
|
||||
"@babel/helper-module-transforms": ^7.22.5
|
||||
"@babel/helper-module-transforms": ^7.22.9
|
||||
"@babel/helper-plugin-utils": ^7.22.5
|
||||
"@babel/helper-simple-access": ^7.22.5
|
||||
peerDependencies:
|
||||
"@babel/core": ^7.0.0-0
|
||||
checksum: 2067aca8f6454d54ffcce69b02c457cfa61428e11372f6a1d99ff4fcfbb55c396ed2ca6ca886bf06c852e38c1a205b8095921b2364fd0243f3e66bc1dda61caa
|
||||
checksum: c15ad7f1234a930cab214224bb85f6b3a3f301fa1d4d15bef193e5c11c614ce369551e5cbb708fde8d3f7e1cb84b05e9798a3647a11b56c3d67580e362a712d4
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -1195,17 +1195,17 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@babel/plugin-transform-typescript@npm:^7.22.5":
|
||||
version: 7.22.10
|
||||
resolution: "@babel/plugin-transform-typescript@npm:7.22.10"
|
||||
"@babel/plugin-transform-typescript@npm:^7.22.11":
|
||||
version: 7.22.11
|
||||
resolution: "@babel/plugin-transform-typescript@npm:7.22.11"
|
||||
dependencies:
|
||||
"@babel/helper-annotate-as-pure": ^7.22.5
|
||||
"@babel/helper-create-class-features-plugin": ^7.22.10
|
||||
"@babel/helper-create-class-features-plugin": ^7.22.11
|
||||
"@babel/helper-plugin-utils": ^7.22.5
|
||||
"@babel/plugin-syntax-typescript": ^7.22.5
|
||||
peerDependencies:
|
||||
"@babel/core": ^7.0.0-0
|
||||
checksum: e15ca8cd9f9715db5ca42a4b7883bc85356424497a711ea01ccb39793e647c54d9bd8ab37d9953ed9bed5d06b705fca681dce1f41d121bc47638f50a5b5ce9ee
|
||||
checksum: a0dc3c2427b55602944705c9a91b4c074524badd5ea87edb603ddeabe7fae531bcbe68475106d7a00079b67bb422dbf2e9f50e15c25ac24d7e9fe77f37ebcfb4
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -1359,18 +1359,18 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@babel/preset-typescript@npm:7.22.5":
|
||||
version: 7.22.5
|
||||
resolution: "@babel/preset-typescript@npm:7.22.5"
|
||||
"@babel/preset-typescript@npm:7.22.11":
|
||||
version: 7.22.11
|
||||
resolution: "@babel/preset-typescript@npm:7.22.11"
|
||||
dependencies:
|
||||
"@babel/helper-plugin-utils": ^7.22.5
|
||||
"@babel/helper-validator-option": ^7.22.5
|
||||
"@babel/plugin-syntax-jsx": ^7.22.5
|
||||
"@babel/plugin-transform-modules-commonjs": ^7.22.5
|
||||
"@babel/plugin-transform-typescript": ^7.22.5
|
||||
"@babel/plugin-transform-modules-commonjs": ^7.22.11
|
||||
"@babel/plugin-transform-typescript": ^7.22.11
|
||||
peerDependencies:
|
||||
"@babel/core": ^7.0.0-0
|
||||
checksum: 7be1670cb4404797d3a473bd72d66eb2b3e0f2f8a672a5e40bdb0812cc66085ec84bcd7b896709764cabf042fdc6b7f2d4755ac7cce10515eb596ff61dab5154
|
||||
checksum: 8ae7162c31db896f5eeecd6f67ab2e58555fdc06fe84e95fe4a3f60b64cd6f782d2d7dfbde0c0eac04b55dac18222752d91dd8786245cccedd7e42f080e07233
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -1381,12 +1381,12 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@babel/runtime@npm:7.22.10, @babel/runtime@npm:^7.10.2, @babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.21.0, @babel/runtime@npm:^7.7.2, @babel/runtime@npm:^7.8.4":
|
||||
version: 7.22.10
|
||||
resolution: "@babel/runtime@npm:7.22.10"
|
||||
"@babel/runtime@npm:7.22.11, @babel/runtime@npm:^7.10.2, @babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.21.0, @babel/runtime@npm:^7.7.2, @babel/runtime@npm:^7.8.4":
|
||||
version: 7.22.11
|
||||
resolution: "@babel/runtime@npm:7.22.11"
|
||||
dependencies:
|
||||
regenerator-runtime: ^0.14.0
|
||||
checksum: 524d41517e68953dbc73a4f3616b8475e5813f64e28ba89ff5fca2c044d535c2ea1a3f310df1e5bb06162e1f0b401b5c4af73fe6e2519ca2450d9d8c44cf268d
|
||||
checksum: a5cd6683a8fcdb8065cb1677f221e22f6c67ec8f15ad1d273b180b93ab3bd86c66da2c48f500d4e72d8d2cfa85ff4872a3f350e5aa3855630036af5da765c001
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -1401,9 +1401,9 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@babel/traverse@npm:^7.22.10":
|
||||
version: 7.22.10
|
||||
resolution: "@babel/traverse@npm:7.22.10"
|
||||
"@babel/traverse@npm:^7.22.11":
|
||||
version: 7.22.11
|
||||
resolution: "@babel/traverse@npm:7.22.11"
|
||||
dependencies:
|
||||
"@babel/code-frame": ^7.22.10
|
||||
"@babel/generator": ^7.22.10
|
||||
@@ -1411,22 +1411,22 @@ __metadata:
|
||||
"@babel/helper-function-name": ^7.22.5
|
||||
"@babel/helper-hoist-variables": ^7.22.5
|
||||
"@babel/helper-split-export-declaration": ^7.22.6
|
||||
"@babel/parser": ^7.22.10
|
||||
"@babel/types": ^7.22.10
|
||||
"@babel/parser": ^7.22.11
|
||||
"@babel/types": ^7.22.11
|
||||
debug: ^4.1.0
|
||||
globals: ^11.1.0
|
||||
checksum: 9f7b358563bfb0f57ac4ed639f50e5c29a36b821a1ce1eea0c7db084f5b925e3275846d0de63bde01ca407c85d9804e0efbe370d92cd2baaafde3bd13b0f4cdb
|
||||
checksum: 4ad62d548ca8b95dbf45bae16cc167428f174f3c837d55a5878b1f17bdbc8b384d6df741dc7c461b62c04d881cf25644d3ab885909ba46e3ac43224e2b15b504
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@babel/types@npm:^7.22.10, @babel/types@npm:^7.22.5, @babel/types@npm:^7.4.4, @babel/types@npm:^7.8.3":
|
||||
version: 7.22.10
|
||||
resolution: "@babel/types@npm:7.22.10"
|
||||
"@babel/types@npm:^7.22.10, @babel/types@npm:^7.22.11, @babel/types@npm:^7.22.5, @babel/types@npm:^7.4.4, @babel/types@npm:^7.8.3":
|
||||
version: 7.22.11
|
||||
resolution: "@babel/types@npm:7.22.11"
|
||||
dependencies:
|
||||
"@babel/helper-string-parser": ^7.22.5
|
||||
"@babel/helper-validator-identifier": ^7.22.5
|
||||
to-fast-properties: ^2.0.0
|
||||
checksum: 095c4f4b7503fa816e4094113f0ec2351ef96ff32012010b771693066ff628c7c664b21c6bd3fb93aeb46fe7c61f6b3a3c9e4ed0034d6a2481201c417371c8af
|
||||
checksum: 431a6446896adb62c876d0fe75263835735d3c974aae05356a87eb55f087c20a777028cf08eadcace7993e058bbafe3b21ce2119363222c6cef9eedd7a204810
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -2097,13 +2097,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@lit-labs/virtualizer@npm:2.0.5":
|
||||
version: 2.0.5
|
||||
resolution: "@lit-labs/virtualizer@npm:2.0.5"
|
||||
"@lit-labs/virtualizer@npm:2.0.6":
|
||||
version: 2.0.6
|
||||
resolution: "@lit-labs/virtualizer@npm:2.0.6"
|
||||
dependencies:
|
||||
lit: ^2.8.0
|
||||
tslib: ^2.0.3
|
||||
checksum: f2f2805551fe97128d131ce3fb12a275ab0935c3be4d4da816d088ee4abf0737b63e3ad0310c24bfb0afd89a85120033e55dd3ec4887e33b538d45e9644a02d9
|
||||
checksum: 8bf5d45f2aafe53cdee3a320a03bd3f95e80a1ff0aab9058624f9cec80d842b08765f04a68555146ad2aa236e974451a7d4539a8533259a7d11b5afc3459a90a
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -3150,13 +3150,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@material/web@npm:=1.0.0-pre.15":
|
||||
version: 1.0.0-pre.15
|
||||
resolution: "@material/web@npm:1.0.0-pre.15"
|
||||
"@material/web@npm:=1.0.0-pre.16":
|
||||
version: 1.0.0-pre.16
|
||||
resolution: "@material/web@npm:1.0.0-pre.16"
|
||||
dependencies:
|
||||
lit: ^2.7.4
|
||||
tslib: ^2.4.0
|
||||
checksum: 83eaa3ba9988a2c20df72219aeef86afba9f8b388011fd66fc021d36ebfa54dc4b5ce8676a94fb7d7f908dab2a8566e734a07e13f6319a00a6c5a27543f85643
|
||||
checksum: 387e7ea35aa280b3122bd998345402ec5d5c8e416b376bc1ea52584142ed79b469d2bf74e8a2b10f1dc1782fb7c50318e4aa5aff465e6798974cfbd1abf361ff
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -3866,9 +3866,9 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@rollup/plugin-node-resolve@npm:15.2.0":
|
||||
version: 15.2.0
|
||||
resolution: "@rollup/plugin-node-resolve@npm:15.2.0"
|
||||
"@rollup/plugin-node-resolve@npm:15.2.1":
|
||||
version: 15.2.1
|
||||
resolution: "@rollup/plugin-node-resolve@npm:15.2.1"
|
||||
dependencies:
|
||||
"@rollup/pluginutils": ^5.0.1
|
||||
"@types/resolve": 1.20.2
|
||||
@@ -3881,7 +3881,7 @@ __metadata:
|
||||
peerDependenciesMeta:
|
||||
rollup:
|
||||
optional: true
|
||||
checksum: 92abfd7f1f5df08d71e4b8dd433d31cf1d62f68891ae3dd5e03a8a7f8d42d9c4433c14a25ead04204fea8235c663a3bbb66ba5528103a6dab66bd8c124f85a0d
|
||||
checksum: e8f706db6ab826e80d1c9a85d2d1e736f2f78a34ea5d49dd0004d6603249a504696967674b2f021cd144536b88d24ffa058383f08b61b4b665be3c7bfacc006a
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -4634,15 +4634,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/eslint-plugin@npm:6.4.0":
|
||||
version: 6.4.0
|
||||
resolution: "@typescript-eslint/eslint-plugin@npm:6.4.0"
|
||||
"@typescript-eslint/eslint-plugin@npm:6.4.1":
|
||||
version: 6.4.1
|
||||
resolution: "@typescript-eslint/eslint-plugin@npm:6.4.1"
|
||||
dependencies:
|
||||
"@eslint-community/regexpp": ^4.5.1
|
||||
"@typescript-eslint/scope-manager": 6.4.0
|
||||
"@typescript-eslint/type-utils": 6.4.0
|
||||
"@typescript-eslint/utils": 6.4.0
|
||||
"@typescript-eslint/visitor-keys": 6.4.0
|
||||
"@typescript-eslint/scope-manager": 6.4.1
|
||||
"@typescript-eslint/type-utils": 6.4.1
|
||||
"@typescript-eslint/utils": 6.4.1
|
||||
"@typescript-eslint/visitor-keys": 6.4.1
|
||||
debug: ^4.3.4
|
||||
graphemer: ^1.4.0
|
||||
ignore: ^5.2.4
|
||||
@@ -4655,44 +4655,44 @@ __metadata:
|
||||
peerDependenciesMeta:
|
||||
typescript:
|
||||
optional: true
|
||||
checksum: d59e88228a4088f3dcaa614103eefa7a0c57315ed79ee1c48afd9817ad013522aa9a9f987e90e1fd7dccc0bbb03ed23e4df6f5ea5cceef8856db33c78ea13d53
|
||||
checksum: aa5f2f516a4ea07d1a9878d347dcb915808862f41efd3c4acd4955e616d265e051c4c93d597d30e54bee10bab9b965e2ef9cea1b497bf16f23a475d7911a8078
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/parser@npm:6.4.0":
|
||||
version: 6.4.0
|
||||
resolution: "@typescript-eslint/parser@npm:6.4.0"
|
||||
"@typescript-eslint/parser@npm:6.4.1":
|
||||
version: 6.4.1
|
||||
resolution: "@typescript-eslint/parser@npm:6.4.1"
|
||||
dependencies:
|
||||
"@typescript-eslint/scope-manager": 6.4.0
|
||||
"@typescript-eslint/types": 6.4.0
|
||||
"@typescript-eslint/typescript-estree": 6.4.0
|
||||
"@typescript-eslint/visitor-keys": 6.4.0
|
||||
"@typescript-eslint/scope-manager": 6.4.1
|
||||
"@typescript-eslint/types": 6.4.1
|
||||
"@typescript-eslint/typescript-estree": 6.4.1
|
||||
"@typescript-eslint/visitor-keys": 6.4.1
|
||||
debug: ^4.3.4
|
||||
peerDependencies:
|
||||
eslint: ^7.0.0 || ^8.0.0
|
||||
peerDependenciesMeta:
|
||||
typescript:
|
||||
optional: true
|
||||
checksum: 36c8dbeacfc03af9c5a4a0f065861ac6f3747fc64be582a32b0b084de5b5247cef086a0c0052291b97145e0ea8f82acbec452dd927b7b7a1917d56381d59a17c
|
||||
checksum: cb61c757963f2a7964c2f846087eadda044720da769d96600f9f0069fe796d612caef5d9bb0c785aa4fa95028b2d231e7c83847ce44f02b1fa41f2102d6f444c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/scope-manager@npm:6.4.0":
|
||||
version: 6.4.0
|
||||
resolution: "@typescript-eslint/scope-manager@npm:6.4.0"
|
||||
"@typescript-eslint/scope-manager@npm:6.4.1":
|
||||
version: 6.4.1
|
||||
resolution: "@typescript-eslint/scope-manager@npm:6.4.1"
|
||||
dependencies:
|
||||
"@typescript-eslint/types": 6.4.0
|
||||
"@typescript-eslint/visitor-keys": 6.4.0
|
||||
checksum: 19406eac3a1899f77eb7c3aa52577e2146075e1318c6eb34d220678afa167832b89c90860714f33b99e107544b48f6970594ca4bcf48c5ede8f2a14a0795ba33
|
||||
"@typescript-eslint/types": 6.4.1
|
||||
"@typescript-eslint/visitor-keys": 6.4.1
|
||||
checksum: 8f7f90aa378a19838301b31cfa58a4b0641d2b84891705c8c006c67aacb5c0d07112b714e1f0e7a159c5736779c934ec26dadef42a0711fccb635596aba391fc
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/type-utils@npm:6.4.0":
|
||||
version: 6.4.0
|
||||
resolution: "@typescript-eslint/type-utils@npm:6.4.0"
|
||||
"@typescript-eslint/type-utils@npm:6.4.1":
|
||||
version: 6.4.1
|
||||
resolution: "@typescript-eslint/type-utils@npm:6.4.1"
|
||||
dependencies:
|
||||
"@typescript-eslint/typescript-estree": 6.4.0
|
||||
"@typescript-eslint/utils": 6.4.0
|
||||
"@typescript-eslint/typescript-estree": 6.4.1
|
||||
"@typescript-eslint/utils": 6.4.1
|
||||
debug: ^4.3.4
|
||||
ts-api-utils: ^1.0.1
|
||||
peerDependencies:
|
||||
@@ -4700,23 +4700,23 @@ __metadata:
|
||||
peerDependenciesMeta:
|
||||
typescript:
|
||||
optional: true
|
||||
checksum: 7930d2ffdc844a5b706d48ae3e4584882f7f0c06d581a3b06bc280a351c55974b16dbb73f1842f7389f04b80c2cfaf867edd2f261b699804d8a4fea9c20b3869
|
||||
checksum: 33bcdd48bd4e07258ed1919b598d50354dd67d8f01702cd2fd46aa9250b7b7cba9caab640df01f4dc0e45dabeddbb3ca47bee88f81fe2087350ed6f70a4cbe5d
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/types@npm:6.4.0":
|
||||
version: 6.4.0
|
||||
resolution: "@typescript-eslint/types@npm:6.4.0"
|
||||
checksum: 85b293ad1559dbf8103b2c4cfd0db11c3d9c970d502e2c13d4b1d35e420567042d7077a716d2b4e5113286314d5260f378f242a6dd22ad4b94b4aa69c5f79223
|
||||
"@typescript-eslint/types@npm:6.4.1":
|
||||
version: 6.4.1
|
||||
resolution: "@typescript-eslint/types@npm:6.4.1"
|
||||
checksum: 16ba46140dbe426407bbb940e87fb347e7eb53b64f74e8f6a819cd662aa25ccd0c25b1e588867ce3cd36a8b4eccea7bd81f4d429595e6e86d9a24c655b1c8617
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/typescript-estree@npm:6.4.0":
|
||||
version: 6.4.0
|
||||
resolution: "@typescript-eslint/typescript-estree@npm:6.4.0"
|
||||
"@typescript-eslint/typescript-estree@npm:6.4.1":
|
||||
version: 6.4.1
|
||||
resolution: "@typescript-eslint/typescript-estree@npm:6.4.1"
|
||||
dependencies:
|
||||
"@typescript-eslint/types": 6.4.0
|
||||
"@typescript-eslint/visitor-keys": 6.4.0
|
||||
"@typescript-eslint/types": 6.4.1
|
||||
"@typescript-eslint/visitor-keys": 6.4.1
|
||||
debug: ^4.3.4
|
||||
globby: ^11.1.0
|
||||
is-glob: ^4.0.3
|
||||
@@ -4725,157 +4725,157 @@ __metadata:
|
||||
peerDependenciesMeta:
|
||||
typescript:
|
||||
optional: true
|
||||
checksum: a8db3896550515d0adf140ee115527b409916c4a14ac1f45b5623d130a27ae2d08a1ac906ceda440b01167c88846e2b91ca2025f3d718bff389948f66990c1e7
|
||||
checksum: 34c289e50a6337321154efe6c20c762e94fea308f9032971e356a266f63e99b908b1a00dd8cf51eba50a6f69db01d665faf2cf13454b355767fd167eebe60f1c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/utils@npm:6.4.0":
|
||||
version: 6.4.0
|
||||
resolution: "@typescript-eslint/utils@npm:6.4.0"
|
||||
"@typescript-eslint/utils@npm:6.4.1":
|
||||
version: 6.4.1
|
||||
resolution: "@typescript-eslint/utils@npm:6.4.1"
|
||||
dependencies:
|
||||
"@eslint-community/eslint-utils": ^4.4.0
|
||||
"@types/json-schema": ^7.0.12
|
||||
"@types/semver": ^7.5.0
|
||||
"@typescript-eslint/scope-manager": 6.4.0
|
||||
"@typescript-eslint/types": 6.4.0
|
||||
"@typescript-eslint/typescript-estree": 6.4.0
|
||||
"@typescript-eslint/scope-manager": 6.4.1
|
||||
"@typescript-eslint/types": 6.4.1
|
||||
"@typescript-eslint/typescript-estree": 6.4.1
|
||||
semver: ^7.5.4
|
||||
peerDependencies:
|
||||
eslint: ^7.0.0 || ^8.0.0
|
||||
checksum: abc55382c601c7ed298076548d2df78f15b07ed6830086db6ce1b82d461f0a190ee103a804690ac9205cdca9f373a864e1dd3e20012e9d103f3137963e0aa5ea
|
||||
checksum: 54e642a345790f912393a6f2821495e2359eff0f874a94cbe6fb3ef4411702983ed54fe88ca3ea9d28f2e93800a74dee22b7888838154bc1afd57c7e119e17ec
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/visitor-keys@npm:6.4.0":
|
||||
version: 6.4.0
|
||||
resolution: "@typescript-eslint/visitor-keys@npm:6.4.0"
|
||||
"@typescript-eslint/visitor-keys@npm:6.4.1":
|
||||
version: 6.4.1
|
||||
resolution: "@typescript-eslint/visitor-keys@npm:6.4.1"
|
||||
dependencies:
|
||||
"@typescript-eslint/types": 6.4.0
|
||||
"@typescript-eslint/types": 6.4.1
|
||||
eslint-visitor-keys: ^3.4.1
|
||||
checksum: 42eb614b9c0a49b6929e093757d772fd27fe5dda9c75f4c7820d1710012c8257eea9bd4f1c4173e2265a8a9ad86cefc1a21869893e7304f3b29b94fa1f987554
|
||||
checksum: bd9cd56fc793e1d880c24193f939c4992b2653f330baece41cd461d1fb48edb2c53696987cba0e29074bbb452dd181fd009db92dd19060fdcc417ad76768f18a
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@vaadin/a11y-base@npm:~24.1.5":
|
||||
version: 24.1.5
|
||||
resolution: "@vaadin/a11y-base@npm:24.1.5"
|
||||
"@vaadin/a11y-base@npm:~24.1.6":
|
||||
version: 24.1.6
|
||||
resolution: "@vaadin/a11y-base@npm:24.1.6"
|
||||
dependencies:
|
||||
"@open-wc/dedupe-mixin": ^1.3.0
|
||||
"@polymer/polymer": ^3.0.0
|
||||
"@vaadin/component-base": ~24.1.5
|
||||
"@vaadin/component-base": ~24.1.6
|
||||
lit: ^2.0.0
|
||||
checksum: b005dabc5bf0173c0b51149a0444364ce054d990017c75d3955169f9a95ba8fd19de8578b65003bca0ebcd8179d3b52834fe332a29a89c6de396792ac7217d3f
|
||||
checksum: 5f739cba4c950d8a4f0ce9bfdab296241c922389fdf9929e371c11e76f0b33d961d0dacc4bbeae8c04b6fbc68a194db631bb49cf415670fa63fb49007f90d01c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@vaadin/combo-box@npm:24.1.5":
|
||||
version: 24.1.5
|
||||
resolution: "@vaadin/combo-box@npm:24.1.5"
|
||||
"@vaadin/combo-box@npm:24.1.6":
|
||||
version: 24.1.6
|
||||
resolution: "@vaadin/combo-box@npm:24.1.6"
|
||||
dependencies:
|
||||
"@open-wc/dedupe-mixin": ^1.3.0
|
||||
"@polymer/polymer": ^3.0.0
|
||||
"@vaadin/a11y-base": ~24.1.5
|
||||
"@vaadin/component-base": ~24.1.5
|
||||
"@vaadin/field-base": ~24.1.5
|
||||
"@vaadin/input-container": ~24.1.5
|
||||
"@vaadin/item": ~24.1.5
|
||||
"@vaadin/lit-renderer": ~24.1.5
|
||||
"@vaadin/overlay": ~24.1.5
|
||||
"@vaadin/vaadin-lumo-styles": ~24.1.5
|
||||
"@vaadin/vaadin-material-styles": ~24.1.5
|
||||
"@vaadin/vaadin-themable-mixin": ~24.1.5
|
||||
checksum: d632a8d8226f8c2ea1146c10d947182379fabd533d53e3602e1f58b6b89fc4bcc3b862e704efcbd29fffb647839b9783eb87c248d9961e56e080a526f07d4cbd
|
||||
"@vaadin/a11y-base": ~24.1.6
|
||||
"@vaadin/component-base": ~24.1.6
|
||||
"@vaadin/field-base": ~24.1.6
|
||||
"@vaadin/input-container": ~24.1.6
|
||||
"@vaadin/item": ~24.1.6
|
||||
"@vaadin/lit-renderer": ~24.1.6
|
||||
"@vaadin/overlay": ~24.1.6
|
||||
"@vaadin/vaadin-lumo-styles": ~24.1.6
|
||||
"@vaadin/vaadin-material-styles": ~24.1.6
|
||||
"@vaadin/vaadin-themable-mixin": ~24.1.6
|
||||
checksum: e5c1579af87612acbc66b5d64ab85d674c7be179feec3659e879400021277c2edee8b117d0ae5742e6026bbb5d2631b59a55c4273be8d828a0397b4c682725c7
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@vaadin/component-base@npm:~24.1.5":
|
||||
version: 24.1.5
|
||||
resolution: "@vaadin/component-base@npm:24.1.5"
|
||||
"@vaadin/component-base@npm:~24.1.6":
|
||||
version: 24.1.6
|
||||
resolution: "@vaadin/component-base@npm:24.1.6"
|
||||
dependencies:
|
||||
"@open-wc/dedupe-mixin": ^1.3.0
|
||||
"@polymer/polymer": ^3.0.0
|
||||
"@vaadin/vaadin-development-mode-detector": ^2.0.0
|
||||
"@vaadin/vaadin-usage-statistics": ^2.1.0
|
||||
lit: ^2.0.0
|
||||
checksum: 0150b72ab01bc9c5408bd144196451964f781f1791f45b061d2fd74ec04590a7f0a0f23988b16de80447aebef78eae75f8a2a558414df01fd0cbc9b6fbc964ab
|
||||
checksum: 034da5eb6367ca2e6b1f129fee453341390f967a05a8fec15c66316a0f5d6de78c534e6e41bb4a5acbb93075b4405b0018f7f64c6d8d09add0b949174164ec2b
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@vaadin/field-base@npm:~24.1.5":
|
||||
version: 24.1.5
|
||||
resolution: "@vaadin/field-base@npm:24.1.5"
|
||||
"@vaadin/field-base@npm:~24.1.6":
|
||||
version: 24.1.6
|
||||
resolution: "@vaadin/field-base@npm:24.1.6"
|
||||
dependencies:
|
||||
"@open-wc/dedupe-mixin": ^1.3.0
|
||||
"@polymer/polymer": ^3.0.0
|
||||
"@vaadin/a11y-base": ~24.1.5
|
||||
"@vaadin/component-base": ~24.1.5
|
||||
"@vaadin/a11y-base": ~24.1.6
|
||||
"@vaadin/component-base": ~24.1.6
|
||||
lit: ^2.0.0
|
||||
checksum: 21391eb15ab64e2351b15ec5bf6e529625921414eecc9d8e6f7b57d60f5c32ce84a1a5a44b8f957053fb398e6cf68cbb7031a10e7d1ad331927711a11b9f9da5
|
||||
checksum: 547324a7c84781781979eec988740acac8c2eefc4be3fef5953a668ff43b7ebc8bb7373fb3f32b3874ddc1f32965c4c98381a8ff38f1822e39ca5173c8163115
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@vaadin/icon@npm:~24.1.5":
|
||||
version: 24.1.5
|
||||
resolution: "@vaadin/icon@npm:24.1.5"
|
||||
"@vaadin/icon@npm:~24.1.6":
|
||||
version: 24.1.6
|
||||
resolution: "@vaadin/icon@npm:24.1.6"
|
||||
dependencies:
|
||||
"@polymer/polymer": ^3.0.0
|
||||
"@vaadin/component-base": ~24.1.5
|
||||
"@vaadin/vaadin-lumo-styles": ~24.1.5
|
||||
"@vaadin/vaadin-themable-mixin": ~24.1.5
|
||||
"@vaadin/component-base": ~24.1.6
|
||||
"@vaadin/vaadin-lumo-styles": ~24.1.6
|
||||
"@vaadin/vaadin-themable-mixin": ~24.1.6
|
||||
lit: ^2.0.0
|
||||
checksum: 54f6312a5c00b3c86dd17bd17ea4da430197ede7ab6465073315c10d62c70c1d9f5d570463876f3e222c62a5ca7ff037c16b310fc310ec0b6c99ba16a91a7b83
|
||||
checksum: dfb5c8d586f058c0bc09e17562e469325aef6fffaa3fe27024c6782c787c0a9ab774cdf121ce4a7da4c08418ffc6d4a5a0f101f60064a29d0f2c73973b34c101
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@vaadin/input-container@npm:~24.1.5":
|
||||
version: 24.1.5
|
||||
resolution: "@vaadin/input-container@npm:24.1.5"
|
||||
"@vaadin/input-container@npm:~24.1.6":
|
||||
version: 24.1.6
|
||||
resolution: "@vaadin/input-container@npm:24.1.6"
|
||||
dependencies:
|
||||
"@polymer/polymer": ^3.0.0
|
||||
"@vaadin/component-base": ~24.1.5
|
||||
"@vaadin/vaadin-lumo-styles": ~24.1.5
|
||||
"@vaadin/vaadin-material-styles": ~24.1.5
|
||||
"@vaadin/vaadin-themable-mixin": ~24.1.5
|
||||
checksum: ad903518d15f05b832ea45edb86f4814891f879ff9d7c485bdcf49377774dd6d73b5223de1ec9b69712aa1f71e3280aa6db664060d897041c0cea402f81ddfe7
|
||||
"@vaadin/component-base": ~24.1.6
|
||||
"@vaadin/vaadin-lumo-styles": ~24.1.6
|
||||
"@vaadin/vaadin-material-styles": ~24.1.6
|
||||
"@vaadin/vaadin-themable-mixin": ~24.1.6
|
||||
checksum: 0f36911a99f76afec7bbbbe36972edb7bebdaea270dbe1457cbe4c29d0ed250616f2dd41bc97f28aa9486151980ffb133293d6cb02547ff496d4986343ea47fb
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@vaadin/item@npm:~24.1.5":
|
||||
version: 24.1.5
|
||||
resolution: "@vaadin/item@npm:24.1.5"
|
||||
"@vaadin/item@npm:~24.1.6":
|
||||
version: 24.1.6
|
||||
resolution: "@vaadin/item@npm:24.1.6"
|
||||
dependencies:
|
||||
"@open-wc/dedupe-mixin": ^1.3.0
|
||||
"@polymer/polymer": ^3.0.0
|
||||
"@vaadin/a11y-base": ~24.1.5
|
||||
"@vaadin/component-base": ~24.1.5
|
||||
"@vaadin/vaadin-lumo-styles": ~24.1.5
|
||||
"@vaadin/vaadin-material-styles": ~24.1.5
|
||||
"@vaadin/vaadin-themable-mixin": ~24.1.5
|
||||
checksum: 73ec92a3487ec206877c8395c48aa0540a9037d78ada8ba161fa33a9d6596c9a7ed8cecca5654d63847f325862157e6aec1a50dd432d5c88a3f69a1b5990e6ab
|
||||
"@vaadin/a11y-base": ~24.1.6
|
||||
"@vaadin/component-base": ~24.1.6
|
||||
"@vaadin/vaadin-lumo-styles": ~24.1.6
|
||||
"@vaadin/vaadin-material-styles": ~24.1.6
|
||||
"@vaadin/vaadin-themable-mixin": ~24.1.6
|
||||
checksum: 815b7aa44370c0f1b0166390134868995987c8349a53725b014475eaf2cf0db034c95948424c882b2dc8c5405994b9ffdd897022781243727039bc542ad1bf5e
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@vaadin/lit-renderer@npm:~24.1.5":
|
||||
version: 24.1.5
|
||||
resolution: "@vaadin/lit-renderer@npm:24.1.5"
|
||||
"@vaadin/lit-renderer@npm:~24.1.6":
|
||||
version: 24.1.6
|
||||
resolution: "@vaadin/lit-renderer@npm:24.1.6"
|
||||
dependencies:
|
||||
lit: ^2.0.0
|
||||
checksum: 963567770025297b3fecddc7f568835d2ca3304b3f80e524635360319e1bb82138cf8ecc1af5b5f4e6fe3751b67144f864481e4ac211e7d64f1c0b98a44da6cf
|
||||
checksum: 4aee7c9b204db58146fbb1a08d5b9eb6636fdc52c44dc3ca94b37e3f5caa95255a0dcf4ceaf62a6a32a5426a144954e64733d9b4f14fde2d6fe8bbdec9270118
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@vaadin/overlay@npm:~24.1.5":
|
||||
version: 24.1.5
|
||||
resolution: "@vaadin/overlay@npm:24.1.5"
|
||||
"@vaadin/overlay@npm:~24.1.6":
|
||||
version: 24.1.6
|
||||
resolution: "@vaadin/overlay@npm:24.1.6"
|
||||
dependencies:
|
||||
"@open-wc/dedupe-mixin": ^1.3.0
|
||||
"@polymer/polymer": ^3.0.0
|
||||
"@vaadin/a11y-base": ~24.1.5
|
||||
"@vaadin/component-base": ~24.1.5
|
||||
"@vaadin/vaadin-lumo-styles": ~24.1.5
|
||||
"@vaadin/vaadin-material-styles": ~24.1.5
|
||||
"@vaadin/vaadin-themable-mixin": ~24.1.5
|
||||
checksum: df8bb6e5a20870d70a06a50ff6f6042f032758b21ce73abb133f29b3575724a3152353e56c8a5cc0511723ad5527cf75e7581cab41c9ffdd4b0ec5ee147925f3
|
||||
"@vaadin/a11y-base": ~24.1.6
|
||||
"@vaadin/component-base": ~24.1.6
|
||||
"@vaadin/vaadin-lumo-styles": ~24.1.6
|
||||
"@vaadin/vaadin-material-styles": ~24.1.6
|
||||
"@vaadin/vaadin-themable-mixin": ~24.1.6
|
||||
checksum: 34829445869911439222749814783863e603cc4162486016e7bc73b83cc414dab479bc51e6f13d1afab4c1536782d9257cab5af6c3f7f5115d70e11ca0ea04f9
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -4886,34 +4886,34 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@vaadin/vaadin-lumo-styles@npm:~24.1.5":
|
||||
version: 24.1.5
|
||||
resolution: "@vaadin/vaadin-lumo-styles@npm:24.1.5"
|
||||
"@vaadin/vaadin-lumo-styles@npm:~24.1.6":
|
||||
version: 24.1.6
|
||||
resolution: "@vaadin/vaadin-lumo-styles@npm:24.1.6"
|
||||
dependencies:
|
||||
"@polymer/polymer": ^3.0.0
|
||||
"@vaadin/icon": ~24.1.5
|
||||
"@vaadin/vaadin-themable-mixin": ~24.1.5
|
||||
checksum: ecec8b2c0039f7bef23b19717f5a590f083a6c9c62f09ac355858fbcac56b9f164efe92f93254a27ee5661e1cead249bd6af618b5a31ad597119063e9c1ecfd9
|
||||
"@vaadin/icon": ~24.1.6
|
||||
"@vaadin/vaadin-themable-mixin": ~24.1.6
|
||||
checksum: 5cf898c166c9ad369e384b493b0b0b0e0ae98388056fb7b6e5539a05a0be87cd48499f184cbe48be8222092abec8386fe36549d4588bfc2b2150fda8acd09c65
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@vaadin/vaadin-material-styles@npm:~24.1.5":
|
||||
version: 24.1.5
|
||||
resolution: "@vaadin/vaadin-material-styles@npm:24.1.5"
|
||||
"@vaadin/vaadin-material-styles@npm:~24.1.6":
|
||||
version: 24.1.6
|
||||
resolution: "@vaadin/vaadin-material-styles@npm:24.1.6"
|
||||
dependencies:
|
||||
"@polymer/polymer": ^3.0.0
|
||||
"@vaadin/vaadin-themable-mixin": ~24.1.5
|
||||
checksum: fe4cc4a3c981a5c041b57a2f57bbb6ebd9c4ba0a393258faf195d94eb9f74f28085ba699451c00c37c29dbacad13843b8606fed98f1d1f0c20b8f39b7514d6a2
|
||||
"@vaadin/vaadin-themable-mixin": ~24.1.6
|
||||
checksum: 27a3bd5651338419e0b3ecc63fc7186c2ae8db0e6029ebb5b0ab935f1abc52ff0d8a7e7134916019b61bb6b156e9e2e051ea175f7b7c568c8164e959a50a75f0
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@vaadin/vaadin-themable-mixin@npm:24.1.5, @vaadin/vaadin-themable-mixin@npm:~24.1.5":
|
||||
version: 24.1.5
|
||||
resolution: "@vaadin/vaadin-themable-mixin@npm:24.1.5"
|
||||
"@vaadin/vaadin-themable-mixin@npm:24.1.6, @vaadin/vaadin-themable-mixin@npm:~24.1.6":
|
||||
version: 24.1.6
|
||||
resolution: "@vaadin/vaadin-themable-mixin@npm:24.1.6"
|
||||
dependencies:
|
||||
"@open-wc/dedupe-mixin": ^1.3.0
|
||||
lit: ^2.0.0
|
||||
checksum: d543cb06f2b6f767c0db4d7ff9541f7906fb84570d6d9993e512a5d99797430f748c9aa71f135a8f11f30e1946ec80bc1325badf6972d6feb945410794563f04
|
||||
checksum: b80493fb7445ad6ba5c5bb272b544541625abbb62f06bc4d7c15c1f8d68addba6c4ac2b0c86b433e192b40bb0fd18c439687c0a6cc735dd7dbbdee93408976d1
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -6518,9 +6518,9 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"chai@npm:4.3.7":
|
||||
version: 4.3.7
|
||||
resolution: "chai@npm:4.3.7"
|
||||
"chai@npm:4.3.8":
|
||||
version: 4.3.8
|
||||
resolution: "chai@npm:4.3.8"
|
||||
dependencies:
|
||||
assertion-error: ^1.1.0
|
||||
check-error: ^1.0.2
|
||||
@@ -6529,7 +6529,7 @@ __metadata:
|
||||
loupe: ^2.3.1
|
||||
pathval: ^1.1.1
|
||||
type-detect: ^4.0.5
|
||||
checksum: 0bba7d267848015246a66995f044ce3f0ebc35e530da3cbdf171db744e14cbe301ab913a8d07caf7952b430257ccbb1a4a983c570a7c5748dc537897e5131f7c
|
||||
checksum: 29e0984ed13308319cadc35437c8ef0a3e271544d226c991bf7e3b6d771bf89707321669e11d05e362bc0ad0bd26585079b989d1032f3c106e3bb95d7f079cce
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -9603,12 +9603,12 @@ __metadata:
|
||||
version: 0.0.0-use.local
|
||||
resolution: "home-assistant-frontend@workspace:."
|
||||
dependencies:
|
||||
"@babel/core": 7.22.10
|
||||
"@babel/core": 7.22.11
|
||||
"@babel/plugin-proposal-decorators": 7.22.10
|
||||
"@babel/plugin-transform-runtime": 7.22.10
|
||||
"@babel/preset-env": 7.22.10
|
||||
"@babel/preset-typescript": 7.22.5
|
||||
"@babel/runtime": 7.22.10
|
||||
"@babel/preset-typescript": 7.22.11
|
||||
"@babel/runtime": 7.22.11
|
||||
"@braintree/sanitize-url": 6.0.4
|
||||
"@codemirror/autocomplete": 6.9.0
|
||||
"@codemirror/commands": 6.2.4
|
||||
@@ -9636,7 +9636,7 @@ __metadata:
|
||||
"@lezer/highlight": 1.1.6
|
||||
"@lit-labs/context": 0.4.0
|
||||
"@lit-labs/motion": 1.0.4
|
||||
"@lit-labs/virtualizer": 2.0.5
|
||||
"@lit-labs/virtualizer": 2.0.6
|
||||
"@lrnwebcomponents/simple-tooltip": 7.0.16
|
||||
"@material/chips": =14.0.0-canary.53b3cad2f.0
|
||||
"@material/data-table": =14.0.0-canary.53b3cad2f.0
|
||||
@@ -9663,7 +9663,7 @@ __metadata:
|
||||
"@material/mwc-top-app-bar": 0.27.0
|
||||
"@material/mwc-top-app-bar-fixed": 0.27.0
|
||||
"@material/top-app-bar": =14.0.0-canary.53b3cad2f.0
|
||||
"@material/web": =1.0.0-pre.15
|
||||
"@material/web": =1.0.0-pre.16
|
||||
"@mdi/js": 7.2.96
|
||||
"@mdi/svg": 7.2.96
|
||||
"@octokit/auth-oauth-device": 6.0.0
|
||||
@@ -9684,7 +9684,7 @@ __metadata:
|
||||
"@rollup/plugin-babel": 6.0.3
|
||||
"@rollup/plugin-commonjs": 25.0.4
|
||||
"@rollup/plugin-json": 6.0.0
|
||||
"@rollup/plugin-node-resolve": 15.2.0
|
||||
"@rollup/plugin-node-resolve": 15.2.1
|
||||
"@rollup/plugin-replace": 5.0.2
|
||||
"@thomasloven/round-slider": 0.6.0
|
||||
"@types/babel__plugin-transform-runtime": 7.9.2
|
||||
@@ -9704,10 +9704,10 @@ __metadata:
|
||||
"@types/tar": 6.1.5
|
||||
"@types/ua-parser-js": 0.7.36
|
||||
"@types/webspeechapi": 0.0.29
|
||||
"@typescript-eslint/eslint-plugin": 6.4.0
|
||||
"@typescript-eslint/parser": 6.4.0
|
||||
"@vaadin/combo-box": 24.1.5
|
||||
"@vaadin/vaadin-themable-mixin": 24.1.5
|
||||
"@typescript-eslint/eslint-plugin": 6.4.1
|
||||
"@typescript-eslint/parser": 6.4.1
|
||||
"@vaadin/combo-box": 24.1.6
|
||||
"@vaadin/vaadin-themable-mixin": 24.1.6
|
||||
"@vibrant/color": 3.2.1-alpha.1
|
||||
"@vibrant/core": 3.2.1-alpha.1
|
||||
"@vibrant/quantizer-mmcq": 3.2.1-alpha.1
|
||||
@@ -9719,7 +9719,7 @@ __metadata:
|
||||
app-datepicker: 5.1.1
|
||||
babel-loader: 9.1.3
|
||||
babel-plugin-template-html-minifier: 4.1.0
|
||||
chai: 4.3.7
|
||||
chai: 4.3.8
|
||||
chart.js: 3.3.2
|
||||
comlink: 4.4.1
|
||||
core-js: 3.32.1
|
||||
@@ -9763,12 +9763,12 @@ __metadata:
|
||||
jszip: 3.10.1
|
||||
leaflet: 1.9.4
|
||||
leaflet-draw: 1.0.4
|
||||
lint-staged: 14.0.0
|
||||
lint-staged: 14.0.1
|
||||
lit: 2.8.0
|
||||
lit-analyzer: 2.0.0-pre.3
|
||||
lodash.template: 4.5.0
|
||||
luxon: 3.4.0
|
||||
magic-string: 0.30.2
|
||||
luxon: 3.4.2
|
||||
magic-string: 0.30.3
|
||||
map-stream: 0.0.7
|
||||
marked: 7.0.4
|
||||
memoize-one: 6.0.0
|
||||
@@ -9802,7 +9802,7 @@ __metadata:
|
||||
ts-lit-plugin: 2.0.0-pre.1
|
||||
tsparticles-engine: 2.12.0
|
||||
tsparticles-preset-links: 2.12.0
|
||||
typescript: 5.1.6
|
||||
typescript: 5.2.2
|
||||
ua-parser-js: 1.0.35
|
||||
unfetch: 5.0.0
|
||||
vinyl-buffer: 1.0.1
|
||||
@@ -11023,7 +11023,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"json5@npm:^2.2.0, json5@npm:^2.2.1, json5@npm:^2.2.2":
|
||||
"json5@npm:^2.2.0, json5@npm:^2.2.1, json5@npm:^2.2.3":
|
||||
version: 2.2.3
|
||||
resolution: "json5@npm:2.2.3"
|
||||
bin:
|
||||
@@ -11337,9 +11337,9 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"lint-staged@npm:14.0.0":
|
||||
version: 14.0.0
|
||||
resolution: "lint-staged@npm:14.0.0"
|
||||
"lint-staged@npm:14.0.1":
|
||||
version: 14.0.1
|
||||
resolution: "lint-staged@npm:14.0.1"
|
||||
dependencies:
|
||||
chalk: 5.3.0
|
||||
commander: 11.0.0
|
||||
@@ -11353,7 +11353,7 @@ __metadata:
|
||||
yaml: 2.3.1
|
||||
bin:
|
||||
lint-staged: bin/lint-staged.js
|
||||
checksum: 7269cd21c15a7b5734a28775f879a91f6b59291bec9a897a1f2faae1cd8316dd50e19fd13207fb6798c705eb5371a25caee0e2dfd5932fbcd38ea5f8155f918c
|
||||
checksum: 8c5d740cb3c90fab2d970fa6bbffe5ddf35ec66ed374a52caf3a3cf83d8f4d5fd01a949994822bce5ea18c0b8dc8fa4ce087ef886a8c11db674139a063cdfe4f
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -11662,19 +11662,19 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"luxon@npm:3.4.0":
|
||||
version: 3.4.0
|
||||
resolution: "luxon@npm:3.4.0"
|
||||
checksum: ca9b6d0e0a8d156574b7e84014ac8e4b60d116d6d7a0fb65ef48eeb8e4415dad6350b76c5ee34f795cadf8b3f9a5fe7a6885733f561e554c50dffb0b704a840b
|
||||
"luxon@npm:3.4.2":
|
||||
version: 3.4.2
|
||||
resolution: "luxon@npm:3.4.2"
|
||||
checksum: efefdfaea90f4c8e502db8eb255385e6dac6733b6a9e1372eaf1a866cc1118363e235a3f6df505b837abc8bbcdfd8f0718a8de387b80cef9ee624d8791ca0844
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"magic-string@npm:0.30.2":
|
||||
version: 0.30.2
|
||||
resolution: "magic-string@npm:0.30.2"
|
||||
"magic-string@npm:0.30.3":
|
||||
version: 0.30.3
|
||||
resolution: "magic-string@npm:0.30.3"
|
||||
dependencies:
|
||||
"@jridgewell/sourcemap-codec": ^1.4.15
|
||||
checksum: c0bbb9b27b2772e6bfaa5d0f6452d47c462d588ae7c43fbaac062b07836d3ec0140fcdd42a57aa53ed990abafcdd0fc17907813921b5df04eccf43e67674bc57
|
||||
checksum: a5a9ddf9bd3bf49a2de1048bf358464f1bda7b3cc1311550f4a0ba8f81a4070e25445d53a5ee28850161336f1bff3cf28aa3320c6b4aeff45ce3e689f300b2f3
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -15576,13 +15576,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"typescript@npm:5.1.6":
|
||||
version: 5.1.6
|
||||
resolution: "typescript@npm:5.1.6"
|
||||
"typescript@npm:5.2.2":
|
||||
version: 5.2.2
|
||||
resolution: "typescript@npm:5.2.2"
|
||||
bin:
|
||||
tsc: bin/tsc
|
||||
tsserver: bin/tsserver
|
||||
checksum: b2f2c35096035fe1f5facd1e38922ccb8558996331405eb00a5111cc948b2e733163cc22fab5db46992aba7dd520fff637f2c1df4996ff0e134e77d3249a7350
|
||||
checksum: 7912821dac4d962d315c36800fe387cdc0a6298dba7ec171b350b4a6e988b51d7b8f051317786db1094bd7431d526b648aba7da8236607febb26cf5b871d2d3c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -15596,13 +15596,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"typescript@patch:typescript@5.1.6#~builtin<compat/typescript>":
|
||||
version: 5.1.6
|
||||
resolution: "typescript@patch:typescript@npm%3A5.1.6#~builtin<compat/typescript>::version=5.1.6&hash=5da071"
|
||||
"typescript@patch:typescript@5.2.2#~builtin<compat/typescript>":
|
||||
version: 5.2.2
|
||||
resolution: "typescript@patch:typescript@npm%3A5.2.2#~builtin<compat/typescript>::version=5.2.2&hash=f3b441"
|
||||
bin:
|
||||
tsc: bin/tsc
|
||||
tsserver: bin/tsserver
|
||||
checksum: f53bfe97f7c8b2b6d23cf572750d4e7d1e0c5fff1c36d859d0ec84556a827b8785077bc27676bf7e71fae538e517c3ecc0f37e7f593be913d884805d931bc8be
|
||||
checksum: 0f4da2f15e6f1245e49db15801dbee52f2bbfb267e1c39225afdab5afee1a72839cd86000e65ee9d7e4dfaff12239d28beaf5ee431357fcced15fb08583d72ca
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
Reference in New Issue
Block a user