mirror of
https://github.com/home-assistant/frontend.git
synced 2025-08-02 14:07:55 +00:00
Merge branch 'dev' into persistent_notification_trigger
This commit is contained in:
commit
1e5c35c158
@ -1,5 +1,5 @@
|
||||
# See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.148.1/containers/python-3/.devcontainer/base.Dockerfile
|
||||
FROM mcr.microsoft.com/vscode/devcontainers/python:0-3.10
|
||||
FROM mcr.microsoft.com/vscode/devcontainers/python:0-3.11
|
||||
|
||||
ENV \
|
||||
DEBIAN_FRONTEND=noninteractive \
|
||||
|
2
.github/workflows/nightly.yaml
vendored
2
.github/workflows/nightly.yaml
vendored
@ -6,7 +6,7 @@ on:
|
||||
- cron: "0 1 * * *"
|
||||
|
||||
env:
|
||||
PYTHON_VERSION: "3.10"
|
||||
PYTHON_VERSION: "3.11"
|
||||
NODE_OPTIONS: --max_old_space_size=6144
|
||||
|
||||
permissions:
|
||||
|
4
.github/workflows/release.yaml
vendored
4
.github/workflows/release.yaml
vendored
@ -6,7 +6,7 @@ on:
|
||||
- published
|
||||
|
||||
env:
|
||||
PYTHON_VERSION: "3.10"
|
||||
PYTHON_VERSION: "3.11"
|
||||
NODE_OPTIONS: --max_old_space_size=6144
|
||||
|
||||
# Set default workflow permissions
|
||||
@ -76,7 +76,7 @@ jobs:
|
||||
- name: Build wheels
|
||||
uses: home-assistant/wheels@2023.04.0
|
||||
with:
|
||||
abi: cp310
|
||||
abi: cp311
|
||||
tag: musllinux_1_2
|
||||
arch: amd64
|
||||
wheels-key: ${{ secrets.WHEELS_KEY }}
|
||||
|
@ -167,6 +167,8 @@ const createWebpackConfig = ({
|
||||
"lit/polyfill-support$": "lit/polyfill-support.js",
|
||||
"@lit-labs/virtualizer/layouts/grid":
|
||||
"@lit-labs/virtualizer/layouts/grid.js",
|
||||
"@lit-labs/virtualizer/polyfills/resize-observer-polyfill/ResizeObserver":
|
||||
"@lit-labs/virtualizer/polyfills/resize-observer-polyfill/ResizeObserver.js",
|
||||
},
|
||||
},
|
||||
output: {
|
||||
|
24
gallery/src/data/date-options.ts
Normal file
24
gallery/src/data/date-options.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import type { ControlSelectOption } from "../../../src/components/ha-control-select";
|
||||
|
||||
export const timeOptions: ControlSelectOption[] = [
|
||||
{
|
||||
value: "now",
|
||||
label: "Now",
|
||||
},
|
||||
{
|
||||
value: "00:15:30",
|
||||
label: "12:15:30 AM",
|
||||
},
|
||||
{
|
||||
value: "06:15:30",
|
||||
label: "06:15:30 AM",
|
||||
},
|
||||
{
|
||||
value: "12:15:30",
|
||||
label: "12:15:30 PM",
|
||||
},
|
||||
{
|
||||
value: "18:15:30",
|
||||
label: "06:15:30 PM",
|
||||
},
|
||||
];
|
7
gallery/src/pages/date-time/date-time-numeric.markdown
Normal file
7
gallery/src/pages/date-time/date-time-numeric.markdown
Normal file
@ -0,0 +1,7 @@
|
||||
---
|
||||
title: Date-Time Format (Numeric)
|
||||
---
|
||||
|
||||
This pages lists all supported languages with their available date-time formats.
|
||||
|
||||
Formatting function: `const formatDateTimeNumeric: (dateObj: Date, locale: FrontendLocaleData) => string`
|
136
gallery/src/pages/date-time/date-time-numeric.ts
Normal file
136
gallery/src/pages/date-time/date-time-numeric.ts
Normal file
@ -0,0 +1,136 @@
|
||||
import { html, css, LitElement } from "lit";
|
||||
import { customElement, state } from "lit/decorators";
|
||||
import "../../../../src/components/ha-card";
|
||||
import "../../../../src/components/ha-control-select";
|
||||
import { translationMetadata } from "../../../../src/resources/translations-metadata";
|
||||
import { formatDateTimeNumeric } from "../../../../src/common/datetime/format_date_time";
|
||||
import { timeOptions } from "../../data/date-options";
|
||||
import { demoConfig } from "../../../../src/fake_data/demo_config";
|
||||
import {
|
||||
FrontendLocaleData,
|
||||
NumberFormat,
|
||||
TimeFormat,
|
||||
DateFormat,
|
||||
FirstWeekday,
|
||||
TimeZone,
|
||||
} from "../../../../src/data/translation";
|
||||
|
||||
@customElement("demo-date-time-date-time-numeric")
|
||||
export class DemoDateTimeDateTimeNumeric extends LitElement {
|
||||
@state() private selection?: string = "now";
|
||||
|
||||
@state() private date: Date = new Date();
|
||||
|
||||
handleValueChanged(e: CustomEvent) {
|
||||
this.selection = e.detail.value as string;
|
||||
this.date = new Date();
|
||||
if (this.selection !== "now") {
|
||||
const [hours, minutes, seconds] = this.selection.split(":").map(Number);
|
||||
this.date.setHours(hours);
|
||||
this.date.setMinutes(minutes);
|
||||
this.date.setSeconds(seconds);
|
||||
}
|
||||
}
|
||||
|
||||
protected render() {
|
||||
const defaultLocale: FrontendLocaleData = {
|
||||
language: "en",
|
||||
number_format: NumberFormat.language,
|
||||
time_format: TimeFormat.language,
|
||||
date_format: DateFormat.language,
|
||||
first_weekday: FirstWeekday.language,
|
||||
time_zone: TimeZone.local,
|
||||
};
|
||||
return html`
|
||||
<ha-control-select
|
||||
.value=${this.selection}
|
||||
.options=${timeOptions}
|
||||
@value-changed=${this.handleValueChanged}
|
||||
>
|
||||
</ha-control-select>
|
||||
<mwc-list>
|
||||
<div class="container header">
|
||||
<div>Language</div>
|
||||
<div class="center">Default (lang)</div>
|
||||
<div class="center">12 Hours</div>
|
||||
<div class="center">24 Hours</div>
|
||||
</div>
|
||||
${Object.entries(translationMetadata.translations)
|
||||
.filter(([key, _]) => key !== "test")
|
||||
.map(
|
||||
([key, value]) => html`
|
||||
<div class="container">
|
||||
<div>${value.nativeName}</div>
|
||||
<div class="center">
|
||||
${formatDateTimeNumeric(
|
||||
this.date,
|
||||
{
|
||||
...defaultLocale,
|
||||
language: key,
|
||||
time_format: TimeFormat.language,
|
||||
},
|
||||
demoConfig
|
||||
)}
|
||||
</div>
|
||||
<div class="center">
|
||||
${formatDateTimeNumeric(
|
||||
this.date,
|
||||
{
|
||||
...defaultLocale,
|
||||
language: key,
|
||||
time_format: TimeFormat.am_pm,
|
||||
},
|
||||
demoConfig
|
||||
)}
|
||||
</div>
|
||||
<div class="center">
|
||||
${formatDateTimeNumeric(
|
||||
this.date,
|
||||
{
|
||||
...defaultLocale,
|
||||
language: key,
|
||||
time_format: TimeFormat.twenty_four,
|
||||
},
|
||||
demoConfig
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
)}
|
||||
</mwc-list>
|
||||
`;
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
return css`
|
||||
ha-control-select {
|
||||
max-width: 800px;
|
||||
margin: 12px auto;
|
||||
}
|
||||
.header {
|
||||
font-weight: bold;
|
||||
}
|
||||
.center {
|
||||
text-align: center;
|
||||
}
|
||||
.container {
|
||||
max-width: 900px;
|
||||
margin: 12px auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-evenly;
|
||||
}
|
||||
|
||||
.container > div {
|
||||
flex-grow: 1;
|
||||
width: 20%;
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"demo-date-time-date-time-numeric": DemoDateTimeDateTimeNumeric;
|
||||
}
|
||||
}
|
7
gallery/src/pages/date-time/date-time-seconds.markdown
Normal file
7
gallery/src/pages/date-time/date-time-seconds.markdown
Normal file
@ -0,0 +1,7 @@
|
||||
---
|
||||
title: Date-Time Format (Seconds)
|
||||
---
|
||||
|
||||
This pages lists all supported languages with their available date-time formats.
|
||||
|
||||
Formatting function: `const formatDateTimeWithSeconds: (dateObj: Date, locale: FrontendLocaleData) => string`
|
136
gallery/src/pages/date-time/date-time-seconds.ts
Normal file
136
gallery/src/pages/date-time/date-time-seconds.ts
Normal file
@ -0,0 +1,136 @@
|
||||
import { html, css, LitElement } from "lit";
|
||||
import { customElement, state } from "lit/decorators";
|
||||
import "../../../../src/components/ha-card";
|
||||
import "../../../../src/components/ha-control-select";
|
||||
import { translationMetadata } from "../../../../src/resources/translations-metadata";
|
||||
import { formatDateTimeWithSeconds } from "../../../../src/common/datetime/format_date_time";
|
||||
import { timeOptions } from "../../data/date-options";
|
||||
import { demoConfig } from "../../../../src/fake_data/demo_config";
|
||||
import {
|
||||
FrontendLocaleData,
|
||||
NumberFormat,
|
||||
TimeFormat,
|
||||
DateFormat,
|
||||
FirstWeekday,
|
||||
TimeZone,
|
||||
} from "../../../../src/data/translation";
|
||||
|
||||
@customElement("demo-date-time-date-time-seconds")
|
||||
export class DemoDateTimeDateTimeSeconds extends LitElement {
|
||||
@state() private selection?: string = "now";
|
||||
|
||||
@state() private date: Date = new Date();
|
||||
|
||||
handleValueChanged(e: CustomEvent) {
|
||||
this.selection = e.detail.value as string;
|
||||
this.date = new Date();
|
||||
if (this.selection !== "now") {
|
||||
const [hours, minutes, seconds] = this.selection.split(":").map(Number);
|
||||
this.date.setHours(hours);
|
||||
this.date.setMinutes(minutes);
|
||||
this.date.setSeconds(seconds);
|
||||
}
|
||||
}
|
||||
|
||||
protected render() {
|
||||
const defaultLocale: FrontendLocaleData = {
|
||||
language: "en",
|
||||
number_format: NumberFormat.language,
|
||||
time_format: TimeFormat.language,
|
||||
date_format: DateFormat.language,
|
||||
first_weekday: FirstWeekday.language,
|
||||
time_zone: TimeZone.local,
|
||||
};
|
||||
return html`
|
||||
<ha-control-select
|
||||
.value=${this.selection}
|
||||
.options=${timeOptions}
|
||||
@value-changed=${this.handleValueChanged}
|
||||
>
|
||||
</ha-control-select>
|
||||
<mwc-list>
|
||||
<div class="container header">
|
||||
<div>Language</div>
|
||||
<div class="center">Default (lang)</div>
|
||||
<div class="center">12 Hours</div>
|
||||
<div class="center">24 Hours</div>
|
||||
</div>
|
||||
${Object.entries(translationMetadata.translations)
|
||||
.filter(([key, _]) => key !== "test")
|
||||
.map(
|
||||
([key, value]) => html`
|
||||
<div class="container">
|
||||
<div>${value.nativeName}</div>
|
||||
<div class="center">
|
||||
${formatDateTimeWithSeconds(
|
||||
this.date,
|
||||
{
|
||||
...defaultLocale,
|
||||
language: key,
|
||||
time_format: TimeFormat.language,
|
||||
},
|
||||
demoConfig
|
||||
)}
|
||||
</div>
|
||||
<div class="center">
|
||||
${formatDateTimeWithSeconds(
|
||||
this.date,
|
||||
{
|
||||
...defaultLocale,
|
||||
language: key,
|
||||
time_format: TimeFormat.am_pm,
|
||||
},
|
||||
demoConfig
|
||||
)}
|
||||
</div>
|
||||
<div class="center">
|
||||
${formatDateTimeWithSeconds(
|
||||
this.date,
|
||||
{
|
||||
...defaultLocale,
|
||||
language: key,
|
||||
time_format: TimeFormat.twenty_four,
|
||||
},
|
||||
demoConfig
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
)}
|
||||
</mwc-list>
|
||||
`;
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
return css`
|
||||
ha-control-select {
|
||||
max-width: 800px;
|
||||
margin: 12px auto;
|
||||
}
|
||||
.header {
|
||||
font-weight: bold;
|
||||
}
|
||||
.center {
|
||||
text-align: center;
|
||||
}
|
||||
.container {
|
||||
max-width: 900px;
|
||||
margin: 12px auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-evenly;
|
||||
}
|
||||
|
||||
.container > div {
|
||||
flex-grow: 1;
|
||||
width: 20%;
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"demo-date-time-date-time-seconds": DemoDateTimeDateTimeSeconds;
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
---
|
||||
title: Date-Time Format (Short w/ Year)
|
||||
---
|
||||
|
||||
This pages lists all supported languages with their available date-time formats.
|
||||
|
||||
Formatting function: `const formatShortDateTimeWithYear: (dateObj: Date, locale: FrontendLocaleData) => string`
|
136
gallery/src/pages/date-time/date-time-short-year.ts
Normal file
136
gallery/src/pages/date-time/date-time-short-year.ts
Normal file
@ -0,0 +1,136 @@
|
||||
import { html, css, LitElement } from "lit";
|
||||
import { customElement, state } from "lit/decorators";
|
||||
import "../../../../src/components/ha-card";
|
||||
import "../../../../src/components/ha-control-select";
|
||||
import { translationMetadata } from "../../../../src/resources/translations-metadata";
|
||||
import { formatShortDateTimeWithYear } from "../../../../src/common/datetime/format_date_time";
|
||||
import { timeOptions } from "../../data/date-options";
|
||||
import { demoConfig } from "../../../../src/fake_data/demo_config";
|
||||
import {
|
||||
FrontendLocaleData,
|
||||
NumberFormat,
|
||||
TimeFormat,
|
||||
DateFormat,
|
||||
FirstWeekday,
|
||||
TimeZone,
|
||||
} from "../../../../src/data/translation";
|
||||
|
||||
@customElement("demo-date-time-date-time-short-year")
|
||||
export class DemoDateTimeDateTimeShortYear extends LitElement {
|
||||
@state() private selection?: string = "now";
|
||||
|
||||
@state() private date: Date = new Date();
|
||||
|
||||
handleValueChanged(e: CustomEvent) {
|
||||
this.selection = e.detail.value as string;
|
||||
this.date = new Date();
|
||||
if (this.selection !== "now") {
|
||||
const [hours, minutes, seconds] = this.selection.split(":").map(Number);
|
||||
this.date.setHours(hours);
|
||||
this.date.setMinutes(minutes);
|
||||
this.date.setSeconds(seconds);
|
||||
}
|
||||
}
|
||||
|
||||
protected render() {
|
||||
const defaultLocale: FrontendLocaleData = {
|
||||
language: "en",
|
||||
number_format: NumberFormat.language,
|
||||
time_format: TimeFormat.language,
|
||||
date_format: DateFormat.language,
|
||||
first_weekday: FirstWeekday.language,
|
||||
time_zone: TimeZone.local,
|
||||
};
|
||||
return html`
|
||||
<ha-control-select
|
||||
.value=${this.selection}
|
||||
.options=${timeOptions}
|
||||
@value-changed=${this.handleValueChanged}
|
||||
>
|
||||
</ha-control-select>
|
||||
<mwc-list>
|
||||
<div class="container header">
|
||||
<div>Language</div>
|
||||
<div class="center">Default (lang)</div>
|
||||
<div class="center">12 Hours</div>
|
||||
<div class="center">24 Hours</div>
|
||||
</div>
|
||||
${Object.entries(translationMetadata.translations)
|
||||
.filter(([key, _]) => key !== "test")
|
||||
.map(
|
||||
([key, value]) => html`
|
||||
<div class="container">
|
||||
<div>${value.nativeName}</div>
|
||||
<div class="center">
|
||||
${formatShortDateTimeWithYear(
|
||||
this.date,
|
||||
{
|
||||
...defaultLocale,
|
||||
language: key,
|
||||
time_format: TimeFormat.language,
|
||||
},
|
||||
demoConfig
|
||||
)}
|
||||
</div>
|
||||
<div class="center">
|
||||
${formatShortDateTimeWithYear(
|
||||
this.date,
|
||||
{
|
||||
...defaultLocale,
|
||||
language: key,
|
||||
time_format: TimeFormat.am_pm,
|
||||
},
|
||||
demoConfig
|
||||
)}
|
||||
</div>
|
||||
<div class="center">
|
||||
${formatShortDateTimeWithYear(
|
||||
this.date,
|
||||
{
|
||||
...defaultLocale,
|
||||
language: key,
|
||||
time_format: TimeFormat.twenty_four,
|
||||
},
|
||||
demoConfig
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
)}
|
||||
</mwc-list>
|
||||
`;
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
return css`
|
||||
ha-control-select {
|
||||
max-width: 800px;
|
||||
margin: 12px auto;
|
||||
}
|
||||
.header {
|
||||
font-weight: bold;
|
||||
}
|
||||
.center {
|
||||
text-align: center;
|
||||
}
|
||||
.container {
|
||||
max-width: 900px;
|
||||
margin: 12px auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-evenly;
|
||||
}
|
||||
|
||||
.container > div {
|
||||
flex-grow: 1;
|
||||
width: 20%;
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"demo-date-time-date-time-short-year": DemoDateTimeDateTimeShortYear;
|
||||
}
|
||||
}
|
7
gallery/src/pages/date-time/date-time-short.markdown
Normal file
7
gallery/src/pages/date-time/date-time-short.markdown
Normal file
@ -0,0 +1,7 @@
|
||||
---
|
||||
title: Date-Time Format (Short)
|
||||
---
|
||||
|
||||
This pages lists all supported languages with their available date-time formats.
|
||||
|
||||
Formatting function: `const formatShortDateTime: (dateObj: Date, locale: FrontendLocaleData) => string`
|
136
gallery/src/pages/date-time/date-time-short.ts
Normal file
136
gallery/src/pages/date-time/date-time-short.ts
Normal file
@ -0,0 +1,136 @@
|
||||
import { html, css, LitElement } from "lit";
|
||||
import { customElement, state } from "lit/decorators";
|
||||
import "../../../../src/components/ha-card";
|
||||
import "../../../../src/components/ha-control-select";
|
||||
import { translationMetadata } from "../../../../src/resources/translations-metadata";
|
||||
import { formatShortDateTime } from "../../../../src/common/datetime/format_date_time";
|
||||
import { timeOptions } from "../../data/date-options";
|
||||
import { demoConfig } from "../../../../src/fake_data/demo_config";
|
||||
import {
|
||||
FrontendLocaleData,
|
||||
NumberFormat,
|
||||
TimeFormat,
|
||||
DateFormat,
|
||||
FirstWeekday,
|
||||
TimeZone,
|
||||
} from "../../../../src/data/translation";
|
||||
|
||||
@customElement("demo-date-time-date-time-short")
|
||||
export class DemoDateTimeDateTimeShort extends LitElement {
|
||||
@state() private selection?: string = "now";
|
||||
|
||||
@state() private date: Date = new Date();
|
||||
|
||||
handleValueChanged(e: CustomEvent) {
|
||||
this.selection = e.detail.value as string;
|
||||
this.date = new Date();
|
||||
if (this.selection !== "now") {
|
||||
const [hours, minutes, seconds] = this.selection.split(":").map(Number);
|
||||
this.date.setHours(hours);
|
||||
this.date.setMinutes(minutes);
|
||||
this.date.setSeconds(seconds);
|
||||
}
|
||||
}
|
||||
|
||||
protected render() {
|
||||
const defaultLocale: FrontendLocaleData = {
|
||||
language: "en",
|
||||
number_format: NumberFormat.language,
|
||||
time_format: TimeFormat.language,
|
||||
date_format: DateFormat.language,
|
||||
first_weekday: FirstWeekday.language,
|
||||
time_zone: TimeZone.local,
|
||||
};
|
||||
return html`
|
||||
<ha-control-select
|
||||
.value=${this.selection}
|
||||
.options=${timeOptions}
|
||||
@value-changed=${this.handleValueChanged}
|
||||
>
|
||||
</ha-control-select>
|
||||
<mwc-list>
|
||||
<div class="container header">
|
||||
<div>Language</div>
|
||||
<div class="center">Default (lang)</div>
|
||||
<div class="center">12 Hours</div>
|
||||
<div class="center">24 Hours</div>
|
||||
</div>
|
||||
${Object.entries(translationMetadata.translations)
|
||||
.filter(([key, _]) => key !== "test")
|
||||
.map(
|
||||
([key, value]) => html`
|
||||
<div class="container">
|
||||
<div>${value.nativeName}</div>
|
||||
<div class="center">
|
||||
${formatShortDateTime(
|
||||
this.date,
|
||||
{
|
||||
...defaultLocale,
|
||||
language: key,
|
||||
time_format: TimeFormat.language,
|
||||
},
|
||||
demoConfig
|
||||
)}
|
||||
</div>
|
||||
<div class="center">
|
||||
${formatShortDateTime(
|
||||
this.date,
|
||||
{
|
||||
...defaultLocale,
|
||||
language: key,
|
||||
time_format: TimeFormat.am_pm,
|
||||
},
|
||||
demoConfig
|
||||
)}
|
||||
</div>
|
||||
<div class="center">
|
||||
${formatShortDateTime(
|
||||
this.date,
|
||||
{
|
||||
...defaultLocale,
|
||||
language: key,
|
||||
time_format: TimeFormat.twenty_four,
|
||||
},
|
||||
demoConfig
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
)}
|
||||
</mwc-list>
|
||||
`;
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
return css`
|
||||
ha-control-select {
|
||||
max-width: 800px;
|
||||
margin: 12px auto;
|
||||
}
|
||||
.header {
|
||||
font-weight: bold;
|
||||
}
|
||||
.center {
|
||||
text-align: center;
|
||||
}
|
||||
.container {
|
||||
max-width: 900px;
|
||||
margin: 12px auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-evenly;
|
||||
}
|
||||
|
||||
.container > div {
|
||||
flex-grow: 1;
|
||||
width: 20%;
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"demo-date-time-date-time-short": DemoDateTimeDateTimeShort;
|
||||
}
|
||||
}
|
7
gallery/src/pages/date-time/date-time.markdown
Normal file
7
gallery/src/pages/date-time/date-time.markdown
Normal file
@ -0,0 +1,7 @@
|
||||
---
|
||||
title: Date-Time Format
|
||||
---
|
||||
|
||||
This pages lists all supported languages with their available date-time formats.
|
||||
|
||||
Formatting function: `const formatDateTime: (dateObj: Date, locale: FrontendLocaleData) => string`
|
136
gallery/src/pages/date-time/date-time.ts
Normal file
136
gallery/src/pages/date-time/date-time.ts
Normal file
@ -0,0 +1,136 @@
|
||||
import { html, css, LitElement } from "lit";
|
||||
import { customElement, state } from "lit/decorators";
|
||||
import "../../../../src/components/ha-card";
|
||||
import "../../../../src/components/ha-control-select";
|
||||
import { translationMetadata } from "../../../../src/resources/translations-metadata";
|
||||
import { formatDateTime } from "../../../../src/common/datetime/format_date_time";
|
||||
import { timeOptions } from "../../data/date-options";
|
||||
import { demoConfig } from "../../../../src/fake_data/demo_config";
|
||||
import {
|
||||
FrontendLocaleData,
|
||||
NumberFormat,
|
||||
TimeFormat,
|
||||
DateFormat,
|
||||
FirstWeekday,
|
||||
TimeZone,
|
||||
} from "../../../../src/data/translation";
|
||||
|
||||
@customElement("demo-date-time-date-time")
|
||||
export class DemoDateTimeDateTime extends LitElement {
|
||||
@state() private selection?: string = "now";
|
||||
|
||||
@state() private date: Date = new Date();
|
||||
|
||||
handleValueChanged(e: CustomEvent) {
|
||||
this.selection = e.detail.value as string;
|
||||
this.date = new Date();
|
||||
if (this.selection !== "now") {
|
||||
const [hours, minutes, seconds] = this.selection.split(":").map(Number);
|
||||
this.date.setHours(hours);
|
||||
this.date.setMinutes(minutes);
|
||||
this.date.setSeconds(seconds);
|
||||
}
|
||||
}
|
||||
|
||||
protected render() {
|
||||
const defaultLocale: FrontendLocaleData = {
|
||||
language: "en",
|
||||
number_format: NumberFormat.language,
|
||||
time_format: TimeFormat.language,
|
||||
date_format: DateFormat.language,
|
||||
first_weekday: FirstWeekday.language,
|
||||
time_zone: TimeZone.local,
|
||||
};
|
||||
return html`
|
||||
<ha-control-select
|
||||
.value=${this.selection}
|
||||
.options=${timeOptions}
|
||||
@value-changed=${this.handleValueChanged}
|
||||
>
|
||||
</ha-control-select>
|
||||
<mwc-list>
|
||||
<div class="container header">
|
||||
<div>Language</div>
|
||||
<div class="center">Default (lang)</div>
|
||||
<div class="center">12 Hours</div>
|
||||
<div class="center">24 Hours</div>
|
||||
</div>
|
||||
${Object.entries(translationMetadata.translations)
|
||||
.filter(([key, _]) => key !== "test")
|
||||
.map(
|
||||
([key, value]) => html`
|
||||
<div class="container">
|
||||
<div>${value.nativeName}</div>
|
||||
<div class="center">
|
||||
${formatDateTime(
|
||||
this.date,
|
||||
{
|
||||
...defaultLocale,
|
||||
language: key,
|
||||
time_format: TimeFormat.language,
|
||||
},
|
||||
demoConfig
|
||||
)}
|
||||
</div>
|
||||
<div class="center">
|
||||
${formatDateTime(
|
||||
this.date,
|
||||
{
|
||||
...defaultLocale,
|
||||
language: key,
|
||||
time_format: TimeFormat.am_pm,
|
||||
},
|
||||
demoConfig
|
||||
)}
|
||||
</div>
|
||||
<div class="center">
|
||||
${formatDateTime(
|
||||
this.date,
|
||||
{
|
||||
...defaultLocale,
|
||||
language: key,
|
||||
time_format: TimeFormat.twenty_four,
|
||||
},
|
||||
demoConfig
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
)}
|
||||
</mwc-list>
|
||||
`;
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
return css`
|
||||
ha-control-select {
|
||||
max-width: 800px;
|
||||
margin: 12px auto;
|
||||
}
|
||||
.header {
|
||||
font-weight: bold;
|
||||
}
|
||||
.center {
|
||||
text-align: center;
|
||||
}
|
||||
.container {
|
||||
max-width: 900px;
|
||||
margin: 12px auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-evenly;
|
||||
}
|
||||
|
||||
.container > div {
|
||||
flex-grow: 1;
|
||||
width: 20%;
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"demo-date-time-date-time": DemoDateTimeDateTime;
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
---
|
||||
title: (Numeric) Date Formatting
|
||||
title: Date Format (Numeric)
|
||||
---
|
||||
|
||||
This pages lists all supported languages with their available (numeric) date formats.
|
||||
|
7
gallery/src/pages/date-time/time-seconds.markdown
Normal file
7
gallery/src/pages/date-time/time-seconds.markdown
Normal file
@ -0,0 +1,7 @@
|
||||
---
|
||||
title: Time Format (Seconds)
|
||||
---
|
||||
|
||||
This pages lists all supported languages with their available time formats.
|
||||
|
||||
Formatting function: `const formatTimeWithSeconds: (dateObj: Date, locale: FrontendLocaleData) => string`
|
135
gallery/src/pages/date-time/time-seconds.ts
Normal file
135
gallery/src/pages/date-time/time-seconds.ts
Normal file
@ -0,0 +1,135 @@
|
||||
import { html, css, LitElement } from "lit";
|
||||
import { customElement, state } from "lit/decorators";
|
||||
import "../../../../src/components/ha-card";
|
||||
import { translationMetadata } from "../../../../src/resources/translations-metadata";
|
||||
import { formatTimeWithSeconds } from "../../../../src/common/datetime/format_time";
|
||||
import { timeOptions } from "../../data/date-options";
|
||||
import { demoConfig } from "../../../../src/fake_data/demo_config";
|
||||
import {
|
||||
FrontendLocaleData,
|
||||
NumberFormat,
|
||||
TimeFormat,
|
||||
DateFormat,
|
||||
FirstWeekday,
|
||||
TimeZone,
|
||||
} from "../../../../src/data/translation";
|
||||
|
||||
@customElement("demo-date-time-time-seconds")
|
||||
export class DemoDateTimeTimeSeconds extends LitElement {
|
||||
@state() private selection?: string = "now";
|
||||
|
||||
@state() private date: Date = new Date();
|
||||
|
||||
handleValueChanged(e: CustomEvent) {
|
||||
this.selection = e.detail.value as string;
|
||||
this.date = new Date();
|
||||
if (this.selection !== "now") {
|
||||
const [hours, minutes, seconds] = this.selection.split(":").map(Number);
|
||||
this.date.setHours(hours);
|
||||
this.date.setMinutes(minutes);
|
||||
this.date.setSeconds(seconds);
|
||||
}
|
||||
}
|
||||
|
||||
protected render() {
|
||||
const defaultLocale: FrontendLocaleData = {
|
||||
language: "en",
|
||||
number_format: NumberFormat.language,
|
||||
time_format: TimeFormat.language,
|
||||
date_format: DateFormat.language,
|
||||
first_weekday: FirstWeekday.language,
|
||||
time_zone: TimeZone.local,
|
||||
};
|
||||
return html`
|
||||
<ha-control-select
|
||||
.value=${this.selection}
|
||||
.options=${timeOptions}
|
||||
@value-changed=${this.handleValueChanged}
|
||||
>
|
||||
</ha-control-select>
|
||||
<mwc-list>
|
||||
<div class="container header">
|
||||
<div>Language</div>
|
||||
<div class="center">Default (lang)</div>
|
||||
<div class="center">12 Hours</div>
|
||||
<div class="center">24 Hours</div>
|
||||
</div>
|
||||
${Object.entries(translationMetadata.translations)
|
||||
.filter(([key, _]) => key !== "test")
|
||||
.map(
|
||||
([key, value]) => html`
|
||||
<div class="container">
|
||||
<div>${value.nativeName}</div>
|
||||
<div class="center">
|
||||
${formatTimeWithSeconds(
|
||||
this.date,
|
||||
{
|
||||
...defaultLocale,
|
||||
language: key,
|
||||
time_format: TimeFormat.language,
|
||||
},
|
||||
demoConfig
|
||||
)}
|
||||
</div>
|
||||
<div class="center">
|
||||
${formatTimeWithSeconds(
|
||||
this.date,
|
||||
{
|
||||
...defaultLocale,
|
||||
language: key,
|
||||
time_format: TimeFormat.am_pm,
|
||||
},
|
||||
demoConfig
|
||||
)}
|
||||
</div>
|
||||
<div class="center">
|
||||
${formatTimeWithSeconds(
|
||||
this.date,
|
||||
{
|
||||
...defaultLocale,
|
||||
language: key,
|
||||
time_format: TimeFormat.twenty_four,
|
||||
},
|
||||
demoConfig
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
)}
|
||||
</mwc-list>
|
||||
`;
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
return css`
|
||||
ha-control-select {
|
||||
max-width: 800px;
|
||||
margin: 12px auto;
|
||||
}
|
||||
.header {
|
||||
font-weight: bold;
|
||||
}
|
||||
.center {
|
||||
text-align: center;
|
||||
}
|
||||
.container {
|
||||
max-width: 600px;
|
||||
margin: 12px auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-evenly;
|
||||
}
|
||||
|
||||
.container > div {
|
||||
flex-grow: 1;
|
||||
width: 20%;
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"demo-date-time-time-seconds": DemoDateTimeTimeSeconds;
|
||||
}
|
||||
}
|
7
gallery/src/pages/date-time/time-weekday.markdown
Normal file
7
gallery/src/pages/date-time/time-weekday.markdown
Normal file
@ -0,0 +1,7 @@
|
||||
---
|
||||
title: Time Format (Weekday)
|
||||
---
|
||||
|
||||
This pages lists all supported languages with their available time formats.
|
||||
|
||||
Formatting function: `const formatTimeWeekday: (dateObj: Date, locale: FrontendLocaleData) => string`
|
135
gallery/src/pages/date-time/time-weekday.ts
Normal file
135
gallery/src/pages/date-time/time-weekday.ts
Normal file
@ -0,0 +1,135 @@
|
||||
import { html, css, LitElement } from "lit";
|
||||
import { customElement, state } from "lit/decorators";
|
||||
import "../../../../src/components/ha-card";
|
||||
import { translationMetadata } from "../../../../src/resources/translations-metadata";
|
||||
import { formatTimeWeekday } from "../../../../src/common/datetime/format_time";
|
||||
import { timeOptions } from "../../data/date-options";
|
||||
import { demoConfig } from "../../../../src/fake_data/demo_config";
|
||||
import {
|
||||
FrontendLocaleData,
|
||||
NumberFormat,
|
||||
TimeFormat,
|
||||
DateFormat,
|
||||
FirstWeekday,
|
||||
TimeZone,
|
||||
} from "../../../../src/data/translation";
|
||||
|
||||
@customElement("demo-date-time-time-weekday")
|
||||
export class DemoDateTimeTimeWeekday extends LitElement {
|
||||
@state() private selection?: string = "now";
|
||||
|
||||
@state() private date: Date = new Date();
|
||||
|
||||
handleValueChanged(e: CustomEvent) {
|
||||
this.selection = e.detail.value as string;
|
||||
this.date = new Date();
|
||||
if (this.selection !== "now") {
|
||||
const [hours, minutes, seconds] = this.selection.split(":").map(Number);
|
||||
this.date.setHours(hours);
|
||||
this.date.setMinutes(minutes);
|
||||
this.date.setSeconds(seconds);
|
||||
}
|
||||
}
|
||||
|
||||
protected render() {
|
||||
const defaultLocale: FrontendLocaleData = {
|
||||
language: "en",
|
||||
number_format: NumberFormat.language,
|
||||
time_format: TimeFormat.language,
|
||||
date_format: DateFormat.language,
|
||||
first_weekday: FirstWeekday.language,
|
||||
time_zone: TimeZone.local,
|
||||
};
|
||||
return html`
|
||||
<ha-control-select
|
||||
.value=${this.selection}
|
||||
.options=${timeOptions}
|
||||
@value-changed=${this.handleValueChanged}
|
||||
>
|
||||
</ha-control-select>
|
||||
<mwc-list>
|
||||
<div class="container header">
|
||||
<div>Language</div>
|
||||
<div class="center">Default (lang)</div>
|
||||
<div class="center">12 Hours</div>
|
||||
<div class="center">24 Hours</div>
|
||||
</div>
|
||||
${Object.entries(translationMetadata.translations)
|
||||
.filter(([key, _]) => key !== "test")
|
||||
.map(
|
||||
([key, value]) => html`
|
||||
<div class="container">
|
||||
<div>${value.nativeName}</div>
|
||||
<div class="center">
|
||||
${formatTimeWeekday(
|
||||
this.date,
|
||||
{
|
||||
...defaultLocale,
|
||||
language: key,
|
||||
time_format: TimeFormat.language,
|
||||
},
|
||||
demoConfig
|
||||
)}
|
||||
</div>
|
||||
<div class="center">
|
||||
${formatTimeWeekday(
|
||||
this.date,
|
||||
{
|
||||
...defaultLocale,
|
||||
language: key,
|
||||
time_format: TimeFormat.am_pm,
|
||||
},
|
||||
demoConfig
|
||||
)}
|
||||
</div>
|
||||
<div class="center">
|
||||
${formatTimeWeekday(
|
||||
this.date,
|
||||
{
|
||||
...defaultLocale,
|
||||
language: key,
|
||||
time_format: TimeFormat.twenty_four,
|
||||
},
|
||||
demoConfig
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
)}
|
||||
</mwc-list>
|
||||
`;
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
return css`
|
||||
ha-control-select {
|
||||
max-width: 800px;
|
||||
margin: 12px auto;
|
||||
}
|
||||
.header {
|
||||
font-weight: bold;
|
||||
}
|
||||
.center {
|
||||
text-align: center;
|
||||
}
|
||||
.container {
|
||||
max-width: 800px;
|
||||
margin: 12px auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-evenly;
|
||||
}
|
||||
|
||||
.container > div {
|
||||
flex-grow: 1;
|
||||
width: 20%;
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"demo-date-time-time-weekday": DemoDateTimeTimeWeekday;
|
||||
}
|
||||
}
|
7
gallery/src/pages/date-time/time.markdown
Normal file
7
gallery/src/pages/date-time/time.markdown
Normal file
@ -0,0 +1,7 @@
|
||||
---
|
||||
title: Time Format
|
||||
---
|
||||
|
||||
This pages lists all supported languages with their available time formats.
|
||||
|
||||
Formatting function: `const formatTime: (dateObj: Date, locale: FrontendLocaleData) => string`
|
136
gallery/src/pages/date-time/time.ts
Normal file
136
gallery/src/pages/date-time/time.ts
Normal file
@ -0,0 +1,136 @@
|
||||
import { html, css, LitElement } from "lit";
|
||||
import { customElement, state } from "lit/decorators";
|
||||
import "../../../../src/components/ha-card";
|
||||
import "../../../../src/components/ha-control-select";
|
||||
import { translationMetadata } from "../../../../src/resources/translations-metadata";
|
||||
import { formatTime } from "../../../../src/common/datetime/format_time";
|
||||
import { timeOptions } from "../../data/date-options";
|
||||
import { demoConfig } from "../../../../src/fake_data/demo_config";
|
||||
import {
|
||||
FrontendLocaleData,
|
||||
NumberFormat,
|
||||
TimeFormat,
|
||||
DateFormat,
|
||||
FirstWeekday,
|
||||
TimeZone,
|
||||
} from "../../../../src/data/translation";
|
||||
|
||||
@customElement("demo-date-time-time")
|
||||
export class DemoDateTimeTime extends LitElement {
|
||||
@state() private selection?: string = "now";
|
||||
|
||||
@state() private date: Date = new Date();
|
||||
|
||||
handleValueChanged(e: CustomEvent) {
|
||||
this.selection = e.detail.value as string;
|
||||
this.date = new Date();
|
||||
if (this.selection !== "now") {
|
||||
const [hours, minutes, seconds] = this.selection.split(":").map(Number);
|
||||
this.date.setHours(hours);
|
||||
this.date.setMinutes(minutes);
|
||||
this.date.setSeconds(seconds);
|
||||
}
|
||||
}
|
||||
|
||||
protected render() {
|
||||
const defaultLocale: FrontendLocaleData = {
|
||||
language: "en",
|
||||
number_format: NumberFormat.language,
|
||||
time_format: TimeFormat.language,
|
||||
date_format: DateFormat.language,
|
||||
first_weekday: FirstWeekday.language,
|
||||
time_zone: TimeZone.local,
|
||||
};
|
||||
return html`
|
||||
<ha-control-select
|
||||
.value=${this.selection}
|
||||
.options=${timeOptions}
|
||||
@value-changed=${this.handleValueChanged}
|
||||
>
|
||||
</ha-control-select>
|
||||
<mwc-list>
|
||||
<div class="container header">
|
||||
<div>Language</div>
|
||||
<div class="center">Default (lang)</div>
|
||||
<div class="center">12 Hours</div>
|
||||
<div class="center">24 Hours</div>
|
||||
</div>
|
||||
${Object.entries(translationMetadata.translations)
|
||||
.filter(([key, _]) => key !== "test")
|
||||
.map(
|
||||
([key, value]) => html`
|
||||
<div class="container">
|
||||
<div>${value.nativeName}</div>
|
||||
<div class="center">
|
||||
${formatTime(
|
||||
this.date,
|
||||
{
|
||||
...defaultLocale,
|
||||
language: key,
|
||||
time_format: TimeFormat.language,
|
||||
},
|
||||
demoConfig
|
||||
)}
|
||||
</div>
|
||||
<div class="center">
|
||||
${formatTime(
|
||||
this.date,
|
||||
{
|
||||
...defaultLocale,
|
||||
language: key,
|
||||
time_format: TimeFormat.am_pm,
|
||||
},
|
||||
demoConfig
|
||||
)}
|
||||
</div>
|
||||
<div class="center">
|
||||
${formatTime(
|
||||
this.date,
|
||||
{
|
||||
...defaultLocale,
|
||||
language: key,
|
||||
time_format: TimeFormat.twenty_four,
|
||||
},
|
||||
demoConfig
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
)}
|
||||
</mwc-list>
|
||||
`;
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
return css`
|
||||
ha-control-select {
|
||||
max-width: 800px;
|
||||
margin: 12px auto;
|
||||
}
|
||||
.header {
|
||||
font-weight: bold;
|
||||
}
|
||||
.center {
|
||||
text-align: center;
|
||||
}
|
||||
.container {
|
||||
max-width: 600px;
|
||||
margin: 12px auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-evenly;
|
||||
}
|
||||
|
||||
.container > div {
|
||||
flex-grow: 1;
|
||||
width: 20%;
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"demo-date-time-time": DemoDateTimeTime;
|
||||
}
|
||||
}
|
@ -135,6 +135,9 @@ const ENTITIES: HassEntity[] = [
|
||||
createEntity("climate.fan_only", "fan_only"),
|
||||
createEntity("climate.auto_idle", "auto", undefined, { hvac_action: "idle" }),
|
||||
createEntity("climate.auto_off", "auto", undefined, { hvac_action: "off" }),
|
||||
createEntity("climate.auto_preheating", "auto", undefined, {
|
||||
hvac_action: "preheating",
|
||||
}),
|
||||
createEntity("climate.auto_heating", "auto", undefined, {
|
||||
hvac_action: "heating",
|
||||
}),
|
||||
|
22
package.json
22
package.json
@ -49,9 +49,9 @@
|
||||
"@fullcalendar/list": "6.1.8",
|
||||
"@fullcalendar/timegrid": "6.1.8",
|
||||
"@lezer/highlight": "1.1.6",
|
||||
"@lit-labs/context": "0.3.2",
|
||||
"@lit-labs/context": "0.3.3",
|
||||
"@lit-labs/motion": "1.0.3",
|
||||
"@lit-labs/virtualizer": "2.0.2",
|
||||
"@lit-labs/virtualizer": "2.0.3",
|
||||
"@lrnwebcomponents/simple-tooltip": "7.0.2",
|
||||
"@material/chips": "=14.0.0-canary.53b3cad2f.0",
|
||||
"@material/data-table": "=14.0.0-canary.53b3cad2f.0",
|
||||
@ -93,8 +93,8 @@
|
||||
"@polymer/paper-toast": "3.0.1",
|
||||
"@polymer/polymer": "3.5.1",
|
||||
"@thomasloven/round-slider": "0.6.0",
|
||||
"@vaadin/combo-box": "24.1.0",
|
||||
"@vaadin/vaadin-themable-mixin": "24.1.0",
|
||||
"@vaadin/combo-box": "24.1.1",
|
||||
"@vaadin/vaadin-themable-mixin": "24.1.1",
|
||||
"@vibrant/color": "3.2.1-alpha.1",
|
||||
"@vibrant/core": "3.2.1-alpha.1",
|
||||
"@vibrant/quantizer-mmcq": "3.2.1-alpha.1",
|
||||
@ -156,9 +156,9 @@
|
||||
"@babel/preset-env": "7.22.5",
|
||||
"@babel/preset-typescript": "7.22.5",
|
||||
"@koa/cors": "4.0.0",
|
||||
"@octokit/auth-oauth-device": "5.0.0",
|
||||
"@octokit/plugin-retry": "5.0.3",
|
||||
"@octokit/rest": "19.0.11",
|
||||
"@octokit/auth-oauth-device": "5.0.2",
|
||||
"@octokit/plugin-retry": "5.0.4",
|
||||
"@octokit/rest": "19.0.13",
|
||||
"@open-wc/dev-server-hmr": "0.1.4",
|
||||
"@rollup/plugin-babel": "6.0.3",
|
||||
"@rollup/plugin-commonjs": "25.0.1",
|
||||
@ -189,7 +189,7 @@
|
||||
"babel-plugin-template-html-minifier": "4.1.0",
|
||||
"chai": "4.3.7",
|
||||
"del": "7.0.0",
|
||||
"eslint": "8.42.0",
|
||||
"eslint": "8.43.0",
|
||||
"eslint-config-airbnb-base": "15.0.0",
|
||||
"eslint-config-airbnb-typescript": "17.0.0",
|
||||
"eslint-config-prettier": "8.8.0",
|
||||
@ -215,7 +215,7 @@
|
||||
"instant-mocha": "1.5.1",
|
||||
"jszip": "3.10.1",
|
||||
"lint-staged": "13.2.2",
|
||||
"lit-analyzer": "1.2.1",
|
||||
"lit-analyzer": "2.0.0-pre.3",
|
||||
"lodash.template": "4.5.0",
|
||||
"magic-string": "0.30.0",
|
||||
"map-stream": "0.0.7",
|
||||
@ -235,8 +235,8 @@
|
||||
"systemjs": "6.14.1",
|
||||
"tar": "6.1.15",
|
||||
"terser-webpack-plugin": "5.3.9",
|
||||
"ts-lit-plugin": "1.2.1",
|
||||
"typescript": "4.9.5",
|
||||
"ts-lit-plugin": "2.0.0-pre.1",
|
||||
"typescript": "5.1.3",
|
||||
"vinyl-buffer": "1.0.1",
|
||||
"vinyl-source-stream": "2.0.0",
|
||||
"webpack": "5.87.0",
|
||||
|
@ -11,7 +11,7 @@ readme = "README.md"
|
||||
authors = [
|
||||
{name = "The Home Assistant Authors", email = "hello@home-assistant.io"}
|
||||
]
|
||||
requires-python = ">=3.4.0"
|
||||
requires-python = ">=3.10.0"
|
||||
|
||||
[project.urls]
|
||||
"Homepage" = "https://github.com/home-assistant/frontend"
|
||||
|
@ -8,9 +8,9 @@ cd "$(dirname "$0")/.."
|
||||
|
||||
# Install/upgrade node when inside devcontainer
|
||||
if [[ -n "$DEVCONTAINER" ]]; then
|
||||
nodeCurrent=$(nvm version default || echo "")
|
||||
nodeCurrent=$(nvm version default || :)
|
||||
nodeLatest=$(nvm version-remote "$(cat .nvmrc)")
|
||||
if [[ -z "$nodeCurrent" ]]; then
|
||||
if [[ -z "$nodeCurrent" || "$nodeCurrent" == "N/A" ]]; then
|
||||
nvm install
|
||||
elif [[ "$nodeCurrent" != "$nodeLatest" ]]; then
|
||||
nvm install --reinstall-packages-from="$nodeCurrent" --default
|
||||
|
@ -15,20 +15,15 @@ export const formatDateTime = (
|
||||
|
||||
const formatDateTimeMem = memoizeOne(
|
||||
(locale: FrontendLocaleData, serverTimeZone: string) =>
|
||||
new Intl.DateTimeFormat(
|
||||
locale.language === "en" && !useAmPm(locale)
|
||||
? "en-u-hc-h23"
|
||||
: locale.language,
|
||||
{
|
||||
year: "numeric",
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
hour: useAmPm(locale) ? "numeric" : "2-digit",
|
||||
minute: "2-digit",
|
||||
hour12: useAmPm(locale),
|
||||
timeZone: locale.time_zone === "server" ? serverTimeZone : undefined,
|
||||
}
|
||||
)
|
||||
new Intl.DateTimeFormat(locale.language, {
|
||||
year: "numeric",
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
hour: useAmPm(locale) ? "numeric" : "2-digit",
|
||||
minute: "2-digit",
|
||||
hourCycle: useAmPm(locale) ? "h12" : "h23",
|
||||
timeZone: locale.time_zone === "server" ? serverTimeZone : undefined,
|
||||
})
|
||||
);
|
||||
|
||||
// Aug 9, 2021, 8:23 AM
|
||||
@ -40,20 +35,15 @@ export const formatShortDateTimeWithYear = (
|
||||
|
||||
const formatShortDateTimeWithYearMem = memoizeOne(
|
||||
(locale: FrontendLocaleData, serverTimeZone: string) =>
|
||||
new Intl.DateTimeFormat(
|
||||
locale.language === "en" && !useAmPm(locale)
|
||||
? "en-u-hc-h23"
|
||||
: locale.language,
|
||||
{
|
||||
year: "numeric",
|
||||
month: "short",
|
||||
day: "numeric",
|
||||
hour: useAmPm(locale) ? "numeric" : "2-digit",
|
||||
minute: "2-digit",
|
||||
hour12: useAmPm(locale),
|
||||
timeZone: locale.time_zone === "server" ? serverTimeZone : undefined,
|
||||
}
|
||||
)
|
||||
new Intl.DateTimeFormat(locale.language, {
|
||||
year: "numeric",
|
||||
month: "short",
|
||||
day: "numeric",
|
||||
hour: useAmPm(locale) ? "numeric" : "2-digit",
|
||||
minute: "2-digit",
|
||||
hourCycle: useAmPm(locale) ? "h12" : "h23",
|
||||
timeZone: locale.time_zone === "server" ? serverTimeZone : undefined,
|
||||
})
|
||||
);
|
||||
|
||||
// Aug 9, 8:23 AM
|
||||
@ -65,19 +55,14 @@ export const formatShortDateTime = (
|
||||
|
||||
const formatShortDateTimeMem = memoizeOne(
|
||||
(locale: FrontendLocaleData, serverTimeZone: string) =>
|
||||
new Intl.DateTimeFormat(
|
||||
locale.language === "en" && !useAmPm(locale)
|
||||
? "en-u-hc-h23"
|
||||
: locale.language,
|
||||
{
|
||||
month: "short",
|
||||
day: "numeric",
|
||||
hour: useAmPm(locale) ? "numeric" : "2-digit",
|
||||
minute: "2-digit",
|
||||
hour12: useAmPm(locale),
|
||||
timeZone: locale.time_zone === "server" ? serverTimeZone : undefined,
|
||||
}
|
||||
)
|
||||
new Intl.DateTimeFormat(locale.language, {
|
||||
month: "short",
|
||||
day: "numeric",
|
||||
hour: useAmPm(locale) ? "numeric" : "2-digit",
|
||||
minute: "2-digit",
|
||||
hourCycle: useAmPm(locale) ? "h12" : "h23",
|
||||
timeZone: locale.time_zone === "server" ? serverTimeZone : undefined,
|
||||
})
|
||||
);
|
||||
|
||||
// August 9, 2021, 8:23:15 AM
|
||||
@ -89,21 +74,16 @@ export const formatDateTimeWithSeconds = (
|
||||
|
||||
const formatDateTimeWithSecondsMem = memoizeOne(
|
||||
(locale: FrontendLocaleData, serverTimeZone: string) =>
|
||||
new Intl.DateTimeFormat(
|
||||
locale.language === "en" && !useAmPm(locale)
|
||||
? "en-u-hc-h23"
|
||||
: locale.language,
|
||||
{
|
||||
year: "numeric",
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
hour: useAmPm(locale) ? "numeric" : "2-digit",
|
||||
minute: "2-digit",
|
||||
second: "2-digit",
|
||||
hour12: useAmPm(locale),
|
||||
timeZone: locale.time_zone === "server" ? serverTimeZone : undefined,
|
||||
}
|
||||
)
|
||||
new Intl.DateTimeFormat(locale.language, {
|
||||
year: "numeric",
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
hour: useAmPm(locale) ? "numeric" : "2-digit",
|
||||
minute: "2-digit",
|
||||
second: "2-digit",
|
||||
hourCycle: useAmPm(locale) ? "h12" : "h23",
|
||||
timeZone: locale.time_zone === "server" ? serverTimeZone : undefined,
|
||||
})
|
||||
);
|
||||
|
||||
// 9/8/2021, 8:23 AM
|
||||
|
@ -13,17 +13,12 @@ export const formatTime = (
|
||||
|
||||
const formatTimeMem = memoizeOne(
|
||||
(locale: FrontendLocaleData, serverTimeZone: string) =>
|
||||
new Intl.DateTimeFormat(
|
||||
locale.language === "en" && !useAmPm(locale)
|
||||
? "en-u-hc-h23"
|
||||
: locale.language,
|
||||
{
|
||||
hour: "numeric",
|
||||
minute: "2-digit",
|
||||
hour12: useAmPm(locale),
|
||||
timeZone: locale.time_zone === "server" ? serverTimeZone : undefined,
|
||||
}
|
||||
)
|
||||
new Intl.DateTimeFormat(locale.language, {
|
||||
hour: "numeric",
|
||||
minute: "2-digit",
|
||||
hourCycle: useAmPm(locale) ? "h12" : "h23",
|
||||
timeZone: locale.time_zone === "server" ? serverTimeZone : undefined,
|
||||
})
|
||||
);
|
||||
|
||||
// 9:15:24 PM || 21:15:24
|
||||
@ -35,18 +30,13 @@ export const formatTimeWithSeconds = (
|
||||
|
||||
const formatTimeWithSecondsMem = memoizeOne(
|
||||
(locale: FrontendLocaleData, serverTimeZone: string) =>
|
||||
new Intl.DateTimeFormat(
|
||||
locale.language === "en" && !useAmPm(locale)
|
||||
? "en-u-hc-h23"
|
||||
: locale.language,
|
||||
{
|
||||
hour: useAmPm(locale) ? "numeric" : "2-digit",
|
||||
minute: "2-digit",
|
||||
second: "2-digit",
|
||||
hour12: useAmPm(locale),
|
||||
timeZone: locale.time_zone === "server" ? serverTimeZone : undefined,
|
||||
}
|
||||
)
|
||||
new Intl.DateTimeFormat(locale.language, {
|
||||
hour: useAmPm(locale) ? "numeric" : "2-digit",
|
||||
minute: "2-digit",
|
||||
second: "2-digit",
|
||||
hourCycle: useAmPm(locale) ? "h12" : "h23",
|
||||
timeZone: locale.time_zone === "server" ? serverTimeZone : undefined,
|
||||
})
|
||||
);
|
||||
|
||||
// Tuesday 7:00 PM || Tuesday 19:00
|
||||
@ -58,18 +48,13 @@ export const formatTimeWeekday = (
|
||||
|
||||
const formatTimeWeekdayMem = memoizeOne(
|
||||
(locale: FrontendLocaleData, serverTimeZone: string) =>
|
||||
new Intl.DateTimeFormat(
|
||||
locale.language === "en" && !useAmPm(locale)
|
||||
? "en-u-hc-h23"
|
||||
: locale.language,
|
||||
{
|
||||
weekday: "long",
|
||||
hour: useAmPm(locale) ? "numeric" : "2-digit",
|
||||
minute: "2-digit",
|
||||
hour12: useAmPm(locale),
|
||||
timeZone: locale.time_zone === "server" ? serverTimeZone : undefined,
|
||||
}
|
||||
)
|
||||
new Intl.DateTimeFormat(locale.language, {
|
||||
weekday: "long",
|
||||
hour: useAmPm(locale) ? "numeric" : "2-digit",
|
||||
minute: "2-digit",
|
||||
hourCycle: useAmPm(locale) ? "h12" : "h23",
|
||||
timeZone: locale.time_zone === "server" ? serverTimeZone : undefined,
|
||||
})
|
||||
);
|
||||
|
||||
// 21:15
|
||||
|
@ -8,8 +8,10 @@ export const useAmPm = memoizeOne((locale: FrontendLocaleData): boolean => {
|
||||
) {
|
||||
const testLanguage =
|
||||
locale.time_format === TimeFormat.language ? locale.language : undefined;
|
||||
const test = new Date().toLocaleString(testLanguage);
|
||||
return test.includes("AM") || test.includes("PM");
|
||||
const test = new Date("January 1, 2023 22:00:00").toLocaleString(
|
||||
testLanguage
|
||||
);
|
||||
return test.includes("10");
|
||||
}
|
||||
|
||||
return locale.time_format === TimeFormat.am_pm;
|
||||
|
@ -102,7 +102,15 @@ const FIXED_DOMAIN_ATTRIBUTE_STATES = {
|
||||
frontend_stream_type: ["hls", "web_rtc"],
|
||||
},
|
||||
climate: {
|
||||
hvac_action: ["off", "idle", "heating", "cooling", "drying", "fan"],
|
||||
hvac_action: [
|
||||
"off",
|
||||
"idle",
|
||||
"preheating",
|
||||
"heating",
|
||||
"cooling",
|
||||
"drying",
|
||||
"fan",
|
||||
],
|
||||
},
|
||||
cover: {
|
||||
device_class: [
|
||||
|
@ -30,6 +30,8 @@ class StateHistoryChartLine extends LitElement {
|
||||
|
||||
@property({ type: Boolean }) public showNames = true;
|
||||
|
||||
@property({ attribute: false }) public startTime!: Date;
|
||||
|
||||
@property({ attribute: false }) public endTime!: Date;
|
||||
|
||||
@property({ type: Number }) public paddingYAxis = 0;
|
||||
@ -57,7 +59,12 @@ class StateHistoryChartLine extends LitElement {
|
||||
}
|
||||
|
||||
public willUpdate(changedProps: PropertyValues) {
|
||||
if (!this.hasUpdated || changedProps.has("showNames")) {
|
||||
if (
|
||||
!this.hasUpdated ||
|
||||
changedProps.has("showNames") ||
|
||||
changedProps.has("startTime") ||
|
||||
changedProps.has("endTime")
|
||||
) {
|
||||
this._chartOptions = {
|
||||
parsing: false,
|
||||
animation: false,
|
||||
@ -74,6 +81,7 @@ class StateHistoryChartLine extends LitElement {
|
||||
config: this.hass.config,
|
||||
},
|
||||
},
|
||||
suggestedMin: this.startTime,
|
||||
suggestedMax: this.endTime,
|
||||
ticks: {
|
||||
maxRotation: 0,
|
||||
@ -146,6 +154,8 @@ class StateHistoryChartLine extends LitElement {
|
||||
}
|
||||
if (
|
||||
changedProps.has("data") ||
|
||||
changedProps.has("startTime") ||
|
||||
changedProps.has("endTime") ||
|
||||
this._chartTime <
|
||||
new Date(this.endTime.getTime() - MIN_TIME_BETWEEN_UPDATES)
|
||||
) {
|
||||
|
@ -52,8 +52,12 @@ export class StateHistoryCharts extends LitElement {
|
||||
|
||||
@property({ attribute: false }) public endTime?: Date;
|
||||
|
||||
@property({ attribute: false }) public startTime?: Date;
|
||||
|
||||
@property({ type: Boolean, attribute: "up-to-now" }) public upToNow = false;
|
||||
|
||||
@property() public hoursToShow?: number;
|
||||
|
||||
@property({ type: Boolean }) public showNames = true;
|
||||
|
||||
@property({ type: Boolean }) public isLoadingData = false;
|
||||
@ -95,13 +99,24 @@ export class StateHistoryCharts extends LitElement {
|
||||
this._computedEndTime =
|
||||
this.upToNow || !this.endTime || this.endTime > now ? now : this.endTime;
|
||||
|
||||
this._computedStartTime = new Date(
|
||||
this.historyData.timeline.reduce(
|
||||
(minTime, stateInfo) =>
|
||||
Math.min(minTime, new Date(stateInfo.data[0].last_changed).getTime()),
|
||||
new Date().getTime()
|
||||
)
|
||||
);
|
||||
if (this.startTime) {
|
||||
this._computedStartTime = this.startTime;
|
||||
} else if (this.hoursToShow) {
|
||||
this._computedStartTime = new Date(
|
||||
new Date().getTime() - 60 * 60 * this.hoursToShow * 1000
|
||||
);
|
||||
} else {
|
||||
this._computedStartTime = new Date(
|
||||
this.historyData.timeline.reduce(
|
||||
(minTime, stateInfo) =>
|
||||
Math.min(
|
||||
minTime,
|
||||
new Date(stateInfo.data[0].last_changed).getTime()
|
||||
),
|
||||
new Date().getTime()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
const combinedItems = this.historyData.timeline.length
|
||||
? (this.virtualize
|
||||
@ -142,6 +157,7 @@ export class StateHistoryCharts extends LitElement {
|
||||
.data=${item.data}
|
||||
.identifier=${item.identifier}
|
||||
.showNames=${this.showNames}
|
||||
.startTime=${this._computedStartTime}
|
||||
.endTime=${this._computedEndTime}
|
||||
.paddingYAxis=${this._maxYWidth}
|
||||
.names=${this.names}
|
||||
|
@ -26,6 +26,10 @@ import { SubscribeMixin } from "../../mixins/subscribe-mixin";
|
||||
import { ValueChangedEvent, HomeAssistant } from "../../types";
|
||||
import "../ha-combo-box";
|
||||
import type { HaComboBox } from "../ha-combo-box";
|
||||
import {
|
||||
fuzzyFilterSort,
|
||||
ScorableTextItem,
|
||||
} from "../../common/string/filter/sequence-matching";
|
||||
|
||||
interface Device {
|
||||
name: string;
|
||||
@ -33,6 +37,8 @@ interface Device {
|
||||
id: string;
|
||||
}
|
||||
|
||||
type ScorableDevice = ScorableTextItem & Device;
|
||||
|
||||
export type HaDevicePickerDeviceFilterFunc = (
|
||||
device: DeviceRegistryEntry
|
||||
) => boolean;
|
||||
@ -119,13 +125,14 @@ export class HaDevicePicker extends SubscribeMixin(LitElement) {
|
||||
deviceFilter: this["deviceFilter"],
|
||||
entityFilter: this["entityFilter"],
|
||||
excludeDevices: this["excludeDevices"]
|
||||
): Device[] => {
|
||||
): ScorableDevice[] => {
|
||||
if (!devices.length) {
|
||||
return [
|
||||
{
|
||||
id: "no_devices",
|
||||
area: "",
|
||||
name: this.hass.localize("ui.components.device-picker.no_devices"),
|
||||
strings: [],
|
||||
},
|
||||
];
|
||||
}
|
||||
@ -235,6 +242,7 @@ export class HaDevicePicker extends SubscribeMixin(LitElement) {
|
||||
device.area_id && areaLookup[device.area_id]
|
||||
? areaLookup[device.area_id].name
|
||||
: this.hass.localize("ui.components.device-picker.no_area"),
|
||||
strings: [device.name || ""],
|
||||
}));
|
||||
if (!outputDevices.length) {
|
||||
return [
|
||||
@ -242,6 +250,7 @@ export class HaDevicePicker extends SubscribeMixin(LitElement) {
|
||||
id: "no_devices",
|
||||
area: "",
|
||||
name: this.hass.localize("ui.components.device-picker.no_match"),
|
||||
strings: [],
|
||||
},
|
||||
];
|
||||
}
|
||||
@ -284,7 +293,7 @@ export class HaDevicePicker extends SubscribeMixin(LitElement) {
|
||||
(this._init && changedProps.has("_opened") && this._opened)
|
||||
) {
|
||||
this._init = true;
|
||||
(this.comboBox as any).items = this._getDevices(
|
||||
const devices = this._getDevices(
|
||||
this.devices!,
|
||||
this.areas!,
|
||||
this.entities!,
|
||||
@ -295,6 +304,8 @@ export class HaDevicePicker extends SubscribeMixin(LitElement) {
|
||||
this.entityFilter,
|
||||
this.excludeDevices
|
||||
);
|
||||
this.comboBox.items = devices;
|
||||
this.comboBox.filteredItems = devices;
|
||||
}
|
||||
}
|
||||
|
||||
@ -314,6 +325,7 @@ export class HaDevicePicker extends SubscribeMixin(LitElement) {
|
||||
item-label-path="name"
|
||||
@opened-changed=${this._openedChanged}
|
||||
@value-changed=${this._deviceChanged}
|
||||
@filter-changed=${this._filterChanged}
|
||||
></ha-combo-box>
|
||||
`;
|
||||
}
|
||||
@ -322,6 +334,14 @@ export class HaDevicePicker extends SubscribeMixin(LitElement) {
|
||||
return this.value || "";
|
||||
}
|
||||
|
||||
private _filterChanged(ev: CustomEvent): void {
|
||||
const target = ev.target as HaComboBox;
|
||||
const filterString = ev.detail.value.toLowerCase();
|
||||
target.filteredItems = filterString.length
|
||||
? fuzzyFilterSort<ScorableDevice>(filterString, target.items || [])
|
||||
: target.items;
|
||||
}
|
||||
|
||||
private _deviceChanged(ev: ValueChangedEvent<string>) {
|
||||
ev.stopPropagation();
|
||||
let newValue = ev.detail.value;
|
||||
|
@ -7,15 +7,19 @@ import memoizeOne from "memoize-one";
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import { computeDomain } from "../../common/entity/compute_domain";
|
||||
import { computeStateName } from "../../common/entity/compute_state_name";
|
||||
import { caseInsensitiveStringCompare } from "../../common/string/compare";
|
||||
import {
|
||||
fuzzyFilterSort,
|
||||
ScorableTextItem,
|
||||
} from "../../common/string/filter/sequence-matching";
|
||||
import { ValueChangedEvent, HomeAssistant } from "../../types";
|
||||
import "../ha-combo-box";
|
||||
import type { HaComboBox } from "../ha-combo-box";
|
||||
import "../ha-icon-button";
|
||||
import "../ha-svg-icon";
|
||||
import "./state-badge";
|
||||
import { caseInsensitiveStringCompare } from "../../common/string/compare";
|
||||
|
||||
interface HassEntityWithCachedName extends HassEntity {
|
||||
interface HassEntityWithCachedName extends HassEntity, ScorableTextItem {
|
||||
friendly_name: string;
|
||||
}
|
||||
|
||||
@ -159,6 +163,7 @@ export class HaEntityPicker extends LitElement {
|
||||
),
|
||||
icon: "mdi:magnify",
|
||||
},
|
||||
strings: [],
|
||||
},
|
||||
];
|
||||
}
|
||||
@ -169,10 +174,14 @@ export class HaEntityPicker extends LitElement {
|
||||
);
|
||||
|
||||
return entityIds
|
||||
.map((key) => ({
|
||||
...hass!.states[key],
|
||||
friendly_name: computeStateName(hass!.states[key]) || key,
|
||||
}))
|
||||
.map((key) => {
|
||||
const friendly_name = computeStateName(hass!.states[key]) || key;
|
||||
return {
|
||||
...hass!.states[key],
|
||||
friendly_name,
|
||||
strings: [key, friendly_name],
|
||||
};
|
||||
})
|
||||
.sort((entityA, entityB) =>
|
||||
caseInsensitiveStringCompare(
|
||||
entityA.friendly_name,
|
||||
@ -201,10 +210,14 @@ export class HaEntityPicker extends LitElement {
|
||||
}
|
||||
|
||||
states = entityIds
|
||||
.map((key) => ({
|
||||
...hass!.states[key],
|
||||
friendly_name: computeStateName(hass!.states[key]) || key,
|
||||
}))
|
||||
.map((key) => {
|
||||
const friendly_name = computeStateName(hass!.states[key]) || key;
|
||||
return {
|
||||
...hass!.states[key],
|
||||
friendly_name,
|
||||
strings: [key, friendly_name],
|
||||
};
|
||||
})
|
||||
.sort((entityA, entityB) =>
|
||||
caseInsensitiveStringCompare(
|
||||
entityA.friendly_name,
|
||||
@ -260,6 +273,7 @@ export class HaEntityPicker extends LitElement {
|
||||
),
|
||||
icon: "mdi:magnify",
|
||||
},
|
||||
strings: [],
|
||||
},
|
||||
];
|
||||
}
|
||||
@ -293,7 +307,7 @@ export class HaEntityPicker extends LitElement {
|
||||
this.excludeEntities
|
||||
);
|
||||
if (this._initedStates) {
|
||||
(this.comboBox as any).filteredItems = this._states;
|
||||
this.comboBox.filteredItems = this._states;
|
||||
}
|
||||
this._initedStates = true;
|
||||
}
|
||||
@ -340,12 +354,11 @@ export class HaEntityPicker extends LitElement {
|
||||
}
|
||||
|
||||
private _filterChanged(ev: CustomEvent): void {
|
||||
const target = ev.target as HaComboBox;
|
||||
const filterString = ev.detail.value.toLowerCase();
|
||||
(this.comboBox as any).filteredItems = this._states.filter(
|
||||
(entityState) =>
|
||||
entityState.entity_id.toLowerCase().includes(filterString) ||
|
||||
computeStateName(entityState).toLowerCase().includes(filterString)
|
||||
);
|
||||
target.filteredItems = filterString.length
|
||||
? fuzzyFilterSort<HassEntityWithCachedName>(filterString, this._states)
|
||||
: this._states;
|
||||
}
|
||||
|
||||
private _setValue(value: string) {
|
||||
|
@ -7,6 +7,10 @@ import { classMap } from "lit/directives/class-map";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
import { computeDomain } from "../common/entity/compute_domain";
|
||||
import {
|
||||
fuzzyFilterSort,
|
||||
ScorableTextItem,
|
||||
} from "../common/string/filter/sequence-matching";
|
||||
import {
|
||||
AreaRegistryEntry,
|
||||
createAreaRegistryEntry,
|
||||
@ -28,6 +32,8 @@ import type { HaComboBox } from "./ha-combo-box";
|
||||
import "./ha-icon-button";
|
||||
import "./ha-svg-icon";
|
||||
|
||||
type ScorableAreaRegistryEntry = ScorableTextItem & AreaRegistryEntry;
|
||||
|
||||
const rowRenderer: ComboBoxLitRenderer<AreaRegistryEntry> = (
|
||||
item
|
||||
) => html`<mwc-list-item
|
||||
@ -306,9 +312,12 @@ export class HaAreaPicker extends LitElement {
|
||||
this.entityFilter,
|
||||
this.noAdd,
|
||||
this.excludeAreas
|
||||
);
|
||||
(this.comboBox as any).items = areas;
|
||||
(this.comboBox as any).filteredItems = areas;
|
||||
).map((area) => ({
|
||||
...area,
|
||||
strings: [area.area_id, ...area.aliases, area.name],
|
||||
}));
|
||||
this.comboBox.items = areas;
|
||||
this.comboBox.filteredItems = areas;
|
||||
}
|
||||
}
|
||||
|
||||
@ -345,8 +354,9 @@ export class HaAreaPicker extends LitElement {
|
||||
return;
|
||||
}
|
||||
|
||||
const filteredItems = this.comboBox.items?.filter((item) =>
|
||||
item.name.toLowerCase().includes(filter!.toLowerCase())
|
||||
const filteredItems = fuzzyFilterSort<ScorableAreaRegistryEntry>(
|
||||
filter,
|
||||
this.comboBox?.items || []
|
||||
);
|
||||
if (!this.noAdd && filteredItems?.length === 0) {
|
||||
this._suggestion = filter;
|
||||
@ -409,7 +419,7 @@ export class HaAreaPicker extends LitElement {
|
||||
name,
|
||||
});
|
||||
const areas = [...Object.values(this.hass.areas), area];
|
||||
(this.comboBox as any).filteredItems = this._getAreas(
|
||||
this.comboBox.filteredItems = this._getAreas(
|
||||
areas,
|
||||
Object.values(this.hass.devices)!,
|
||||
Object.values(this.hass.entities)!,
|
||||
|
@ -186,9 +186,8 @@ class HaHsColorPicker extends LitElement {
|
||||
}
|
||||
if (changedProps.has("value")) {
|
||||
if (
|
||||
this.value !== undefined &&
|
||||
(this._localValue?.[0] !== this.value[0] ||
|
||||
this._localValue?.[1] !== this.value[1])
|
||||
this._localValue?.[0] !== this.value?.[0] ||
|
||||
this._localValue?.[1] !== this.value?.[1]
|
||||
) {
|
||||
this._resetPosition();
|
||||
}
|
||||
@ -243,7 +242,11 @@ class HaHsColorPicker extends LitElement {
|
||||
}
|
||||
|
||||
private _resetPosition() {
|
||||
if (this.value === undefined) return;
|
||||
if (this.value === undefined) {
|
||||
this._cursorPosition = undefined;
|
||||
this._localValue = undefined;
|
||||
return;
|
||||
}
|
||||
this._cursorPosition = this._getCoordsFromValue(this.value);
|
||||
this._localValue = this.value;
|
||||
}
|
||||
@ -384,6 +387,7 @@ class HaHsColorPicker extends LitElement {
|
||||
canvas {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
border-radius: 50%;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
38
src/components/ha-icon-button-group.ts
Normal file
38
src/components/ha-icon-button-group.ts
Normal file
@ -0,0 +1,38 @@
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement } from "lit/decorators";
|
||||
|
||||
@customElement("ha-icon-button-group")
|
||||
export class HaIconButtonGroup extends LitElement {
|
||||
protected render(): TemplateResult {
|
||||
return html`<slot></slot>`;
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return css`
|
||||
:host {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
height: 56px;
|
||||
border-radius: 28px;
|
||||
background-color: rgba(139, 145, 151, 0.1);
|
||||
box-sizing: border-box;
|
||||
width: auto;
|
||||
padding: 4px;
|
||||
gap: 4px;
|
||||
}
|
||||
::slotted(.separator) {
|
||||
background-color: rgba(var(--rgb-primary-text-color), 0.15);
|
||||
width: 1px;
|
||||
height: 40px;
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-icon-button-group": HaIconButtonGroup;
|
||||
}
|
||||
}
|
52
src/components/ha-icon-button-toggle.ts
Normal file
52
src/components/ha-icon-button-toggle.ts
Normal file
@ -0,0 +1,52 @@
|
||||
import { css, CSSResultGroup } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { HaIconButton } from "./ha-icon-button";
|
||||
|
||||
@customElement("ha-icon-button-toggle")
|
||||
export class HaIconButtonToggle extends HaIconButton {
|
||||
@property({ type: Boolean, reflect: true }) selected = false;
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return css`
|
||||
:host {
|
||||
position: relative;
|
||||
}
|
||||
mwc-icon-button {
|
||||
position: relative;
|
||||
transition: color 180ms ease-in-out;
|
||||
}
|
||||
mwc-icon-button::before {
|
||||
opacity: 0;
|
||||
transition: opacity 180ms ease-in-out;
|
||||
background-color: var(--primary-text-color);
|
||||
border-radius: 20px;
|
||||
height: 40px;
|
||||
width: 40px;
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: -10px;
|
||||
left: -10px;
|
||||
bottom: -10px;
|
||||
right: -10px;
|
||||
margin: auto;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
:host([border-only]) mwc-icon-button::before {
|
||||
background-color: transparent;
|
||||
border: 2px solid var(--primary-text-color);
|
||||
}
|
||||
:host([selected]) mwc-icon-button {
|
||||
color: var(--primary-background-color);
|
||||
}
|
||||
:host([selected]) mwc-icon-button::before {
|
||||
opacity: 1;
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-icon-button-toggle": HaIconButtonToggle;
|
||||
}
|
||||
}
|
@ -4,6 +4,7 @@ import { css, html, LitElement } from "lit";
|
||||
import { customElement, property, query } from "lit/decorators";
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import { stopPropagation } from "../../common/dom/stop_propagation";
|
||||
import { ensureArray } from "../../common/array/ensure-array";
|
||||
import type { SelectOption, SelectSelector } from "../../data/selector";
|
||||
import type { HomeAssistant } from "../../types";
|
||||
import "../ha-checkbox";
|
||||
@ -40,7 +41,7 @@ export class HaSelectSelector extends LitElement {
|
||||
|
||||
protected render() {
|
||||
const options =
|
||||
this.selector.select?.options.map((option) =>
|
||||
this.selector.select?.options?.map((option) =>
|
||||
typeof option === "object"
|
||||
? (option as SelectOption)
|
||||
: ({ value: option, label: option } as SelectOption)
|
||||
@ -77,7 +78,8 @@ export class HaSelectSelector extends LitElement {
|
||||
${this._renderHelper()}
|
||||
`;
|
||||
}
|
||||
|
||||
const value =
|
||||
!this.value || this.value === "" ? [] : ensureArray(this.value);
|
||||
return html`
|
||||
<div>
|
||||
${this.label}
|
||||
@ -85,7 +87,7 @@ export class HaSelectSelector extends LitElement {
|
||||
(item: SelectOption) => html`
|
||||
<ha-formfield .label=${item.label}>
|
||||
<ha-checkbox
|
||||
.checked=${this.value?.includes(item.value)}
|
||||
.checked=${value.includes(item.value)}
|
||||
.value=${item.value}
|
||||
.disabled=${item.disabled || this.disabled}
|
||||
@change=${this._checkboxChanged}
|
||||
@ -100,7 +102,7 @@ export class HaSelectSelector extends LitElement {
|
||||
|
||||
if (this.selector.select?.multiple) {
|
||||
const value =
|
||||
!this.value || this.value === "" ? [] : (this.value as string[]);
|
||||
!this.value || this.value === "" ? [] : ensureArray(this.value);
|
||||
|
||||
const optionItems = options.filter(
|
||||
(option) => !option.disabled && !value?.includes(option.value)
|
||||
@ -231,19 +233,19 @@ export class HaSelectSelector extends LitElement {
|
||||
const value: string = ev.target.value;
|
||||
const checked = ev.target.checked;
|
||||
|
||||
const oldValue =
|
||||
!this.value || this.value === "" ? [] : ensureArray(this.value);
|
||||
|
||||
if (checked) {
|
||||
if (!this.value) {
|
||||
newValue = [value];
|
||||
} else if (this.value.includes(value)) {
|
||||
if (oldValue.includes(value)) {
|
||||
return;
|
||||
} else {
|
||||
newValue = [...this.value, value];
|
||||
}
|
||||
newValue = [...oldValue, value];
|
||||
} else {
|
||||
if (!this.value?.includes(value)) {
|
||||
if (!oldValue?.includes(value)) {
|
||||
return;
|
||||
}
|
||||
newValue = (this.value as string[]).filter((v) => v !== value);
|
||||
newValue = oldValue.filter((v) => v !== value);
|
||||
}
|
||||
|
||||
fireEvent(this, "value-changed", {
|
||||
@ -252,7 +254,7 @@ export class HaSelectSelector extends LitElement {
|
||||
}
|
||||
|
||||
private async _removeItem(ev) {
|
||||
const value: string[] = [...(this.value! as string[])];
|
||||
const value: string[] = [...ensureArray(this.value!)];
|
||||
value.splice(ev.target.idx, 1);
|
||||
|
||||
fireEvent(this, "value-changed", {
|
||||
@ -277,7 +279,10 @@ export class HaSelectSelector extends LitElement {
|
||||
return;
|
||||
}
|
||||
|
||||
if (newValue !== undefined && this.value?.includes(newValue)) {
|
||||
const currentValue =
|
||||
!this.value || this.value === "" ? [] : ensureArray(this.value);
|
||||
|
||||
if (newValue !== undefined && currentValue.includes(newValue)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -286,9 +291,6 @@ export class HaSelectSelector extends LitElement {
|
||||
this.comboBox.setInputValue("");
|
||||
}, 0);
|
||||
|
||||
const currentValue =
|
||||
!this.value || this.value === "" ? [] : (this.value as string[]);
|
||||
|
||||
fireEvent(this, "value-changed", {
|
||||
value: [...currentValue, newValue],
|
||||
});
|
||||
|
@ -24,6 +24,7 @@ export class HaThemeSelector extends LitElement {
|
||||
.hass=${this.hass}
|
||||
.value=${this.value}
|
||||
.label=${this.label}
|
||||
.includeDefault=${this.selector.theme?.include_default}
|
||||
.disabled=${this.disabled}
|
||||
.required=${this.required}
|
||||
></ha-theme-picker>
|
||||
|
@ -139,7 +139,7 @@ class HaTempColorPicker extends LitElement {
|
||||
this.setAttribute("aria-valuemax", this.max.toString());
|
||||
}
|
||||
if (changedProps.has("value")) {
|
||||
if (this.value != null && this._localValue !== this.value) {
|
||||
if (this._localValue !== this.value) {
|
||||
this._resetPosition();
|
||||
}
|
||||
}
|
||||
@ -197,7 +197,11 @@ class HaTempColorPicker extends LitElement {
|
||||
}
|
||||
|
||||
private _resetPosition() {
|
||||
if (this.value === undefined) return;
|
||||
if (this.value === undefined) {
|
||||
this._cursorPosition = undefined;
|
||||
this._localValue = undefined;
|
||||
return;
|
||||
}
|
||||
const [, y] = this._getCoordsFromValue(this.value);
|
||||
const currentX = this._cursorPosition?.[0] ?? 0;
|
||||
const x =
|
||||
@ -391,6 +395,7 @@ class HaTempColorPicker extends LitElement {
|
||||
canvas {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
border-radius: 50%;
|
||||
transition: box-shadow 180ms ease-in-out;
|
||||
cursor: pointer;
|
||||
|
@ -99,6 +99,10 @@ export class HaTextField extends TextFieldBase {
|
||||
direction: var(--direction);
|
||||
}
|
||||
|
||||
.mdc-text-field__icon--trailing {
|
||||
padding: var(--textfield-icon-trailing-padding, 12px);
|
||||
}
|
||||
|
||||
.mdc-floating-label:not(.mdc-floating-label--float-above) {
|
||||
text-overflow: ellipsis;
|
||||
width: inherit;
|
||||
|
@ -1,17 +1,28 @@
|
||||
import "@material/mwc-list/mwc-list-item";
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import {
|
||||
css,
|
||||
CSSResultGroup,
|
||||
html,
|
||||
nothing,
|
||||
LitElement,
|
||||
TemplateResult,
|
||||
} from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
import { stopPropagation } from "../common/dom/stop_propagation";
|
||||
import { HomeAssistant } from "../types";
|
||||
import "./ha-select";
|
||||
|
||||
const DEFAULT_THEME = "default";
|
||||
|
||||
@customElement("ha-theme-picker")
|
||||
export class HaThemePicker extends LitElement {
|
||||
@property() public value?: string;
|
||||
|
||||
@property() public label?: string;
|
||||
|
||||
@property() includeDefault?: boolean = false;
|
||||
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property({ type: Boolean, reflect: true }) public disabled = false;
|
||||
@ -36,6 +47,13 @@ export class HaThemePicker extends LitElement {
|
||||
"ui.components.theme-picker.no_theme"
|
||||
)}</mwc-list-item
|
||||
>
|
||||
${this.includeDefault
|
||||
? html`<mwc-list-item .value=${DEFAULT_THEME}
|
||||
>${this.hass!.localize(
|
||||
"ui.components.theme-picker.default"
|
||||
)}</mwc-list-item
|
||||
>`
|
||||
: nothing}
|
||||
${Object.keys(this.hass!.themes.themes)
|
||||
.sort()
|
||||
.map(
|
||||
|
@ -24,6 +24,9 @@ import { EntityRegistryEntry } from "./entity_registry";
|
||||
import "../resources/intl-polyfill";
|
||||
import { FrontendLocaleData } from "./translation";
|
||||
|
||||
const triggerTranslationBaseKey =
|
||||
"ui.panel.config.automation.editor.triggers.type";
|
||||
|
||||
const describeDuration = (forTime: number | string | ForDict) => {
|
||||
let duration: string | null;
|
||||
if (typeof forTime === "number") {
|
||||
@ -90,29 +93,30 @@ export const describeTrigger = (
|
||||
|
||||
// Event Trigger
|
||||
if (trigger.platform === "event" && trigger.event_type) {
|
||||
let eventTypes = "";
|
||||
const eventTypes: string[] = [];
|
||||
|
||||
if (Array.isArray(trigger.event_type)) {
|
||||
for (const [index, state] of trigger.event_type.entries()) {
|
||||
eventTypes += `${index > 0 ? "," : ""} ${
|
||||
trigger.event_type.length > 1 &&
|
||||
index === trigger.event_type.length - 1
|
||||
? "or"
|
||||
: ""
|
||||
} ${state}`;
|
||||
for (const state of trigger.event_type.values()) {
|
||||
eventTypes.push(state);
|
||||
}
|
||||
} else {
|
||||
eventTypes = trigger.event_type.toString();
|
||||
eventTypes.push(trigger.event_type);
|
||||
}
|
||||
|
||||
return `When ${eventTypes} event is fired`;
|
||||
const eventTypesString = disjunctionFormatter.format(eventTypes);
|
||||
return hass.localize(
|
||||
`${triggerTranslationBaseKey}.event.description.full`,
|
||||
{ eventTypes: eventTypesString }
|
||||
);
|
||||
}
|
||||
|
||||
// Home Assistant Trigger
|
||||
if (trigger.platform === "homeassistant" && trigger.event) {
|
||||
return `When Home Assistant is ${
|
||||
trigger.event === "start" ? "started" : "shutdown"
|
||||
}`;
|
||||
return hass.localize(
|
||||
trigger.event === "start"
|
||||
? `${triggerTranslationBaseKey}.homeassistant.description.started`
|
||||
: `${triggerTranslationBaseKey}.homeassistant.description.shutdown`
|
||||
);
|
||||
}
|
||||
|
||||
// Numeric State Trigger
|
||||
@ -157,7 +161,7 @@ export const describeTrigger = (
|
||||
// State Trigger
|
||||
if (trigger.platform === "state") {
|
||||
let base = "When";
|
||||
let entities = "";
|
||||
const entities: string[] = [];
|
||||
const states = hass.states;
|
||||
|
||||
if (trigger.attribute) {
|
||||
@ -173,25 +177,22 @@ export const describeTrigger = (
|
||||
}
|
||||
|
||||
if (Array.isArray(trigger.entity_id)) {
|
||||
for (const [index, entity] of trigger.entity_id.entries()) {
|
||||
for (const entity of trigger.entity_id.values()) {
|
||||
if (states[entity]) {
|
||||
entities += `${index > 0 ? "," : ""} ${
|
||||
trigger.entity_id.length > 1 &&
|
||||
index === trigger.entity_id.length - 1
|
||||
? "or"
|
||||
: ""
|
||||
} ${computeStateName(states[entity]) || entity}`;
|
||||
entities.push(computeStateName(states[entity]) || entity);
|
||||
}
|
||||
}
|
||||
} else if (trigger.entity_id) {
|
||||
entities = states[trigger.entity_id]
|
||||
? computeStateName(states[trigger.entity_id])
|
||||
: trigger.entity_id;
|
||||
entities.push(
|
||||
states[trigger.entity_id]
|
||||
? computeStateName(states[trigger.entity_id])
|
||||
: trigger.entity_id
|
||||
);
|
||||
}
|
||||
|
||||
if (!entities) {
|
||||
if (entities.length === 0) {
|
||||
// no entity_id or empty array
|
||||
entities = "something";
|
||||
entities.push("something");
|
||||
}
|
||||
|
||||
base += ` ${entities} changes`;
|
||||
@ -208,13 +209,9 @@ export const describeTrigger = (
|
||||
base += " from any state";
|
||||
}
|
||||
} else if (Array.isArray(trigger.from)) {
|
||||
let from = "";
|
||||
for (const [index, state] of trigger.from.entries()) {
|
||||
from += `${index > 0 ? "," : ""} ${
|
||||
trigger.from.length > 1 && index === trigger.from.length - 1
|
||||
? "or"
|
||||
: ""
|
||||
} '${
|
||||
const from: string[] = [];
|
||||
for (const state of trigger.from.values()) {
|
||||
from.push(
|
||||
trigger.attribute
|
||||
? computeAttributeValueDisplay(
|
||||
hass.localize,
|
||||
@ -224,7 +221,7 @@ export const describeTrigger = (
|
||||
hass.entities,
|
||||
trigger.attribute,
|
||||
state
|
||||
)
|
||||
).toString()
|
||||
: computeStateDisplay(
|
||||
hass.localize,
|
||||
stateObj,
|
||||
@ -233,13 +230,14 @@ export const describeTrigger = (
|
||||
hass.entities,
|
||||
state
|
||||
)
|
||||
}'`;
|
||||
);
|
||||
}
|
||||
if (from) {
|
||||
base += ` from ${from}`;
|
||||
if (from.length !== 0) {
|
||||
const fromString = disjunctionFormatter.format(from);
|
||||
base += ` from ${fromString}`;
|
||||
}
|
||||
} else {
|
||||
base += ` from '${
|
||||
base += ` from ${
|
||||
trigger.attribute
|
||||
? computeAttributeValueDisplay(
|
||||
hass.localize,
|
||||
@ -258,7 +256,7 @@ export const describeTrigger = (
|
||||
hass.entities,
|
||||
trigger.from.toString()
|
||||
).toString()
|
||||
}'`;
|
||||
}`;
|
||||
}
|
||||
}
|
||||
|
||||
@ -268,11 +266,9 @@ export const describeTrigger = (
|
||||
base += " to any state";
|
||||
}
|
||||
} else if (Array.isArray(trigger.to)) {
|
||||
let to = "";
|
||||
for (const [index, state] of trigger.to.entries()) {
|
||||
to += `${index > 0 ? "," : ""} ${
|
||||
trigger.to.length > 1 && index === trigger.to.length - 1 ? "or" : ""
|
||||
} '${
|
||||
const to: string[] = [];
|
||||
for (const state of trigger.to.values()) {
|
||||
to.push(
|
||||
trigger.attribute
|
||||
? computeAttributeValueDisplay(
|
||||
hass.localize,
|
||||
@ -291,13 +287,14 @@ export const describeTrigger = (
|
||||
hass.entities,
|
||||
state
|
||||
).toString()
|
||||
}'`;
|
||||
);
|
||||
}
|
||||
if (to) {
|
||||
base += ` to ${to}`;
|
||||
if (to.length !== 0) {
|
||||
const toString = disjunctionFormatter.format(to);
|
||||
base += ` to ${toString}`;
|
||||
}
|
||||
} else {
|
||||
base += ` to '${
|
||||
base += ` to ${
|
||||
trigger.attribute
|
||||
? computeAttributeValueDisplay(
|
||||
hass.localize,
|
||||
@ -315,8 +312,8 @@ export const describeTrigger = (
|
||||
hass.config,
|
||||
hass.entities,
|
||||
trigger.to.toString()
|
||||
).toString()
|
||||
}'`;
|
||||
)
|
||||
}`;
|
||||
}
|
||||
}
|
||||
|
||||
@ -340,29 +337,28 @@ export const describeTrigger = (
|
||||
|
||||
// Sun Trigger
|
||||
if (trigger.platform === "sun" && trigger.event) {
|
||||
let base = `When the sun ${trigger.event === "sunset" ? "sets" : "rises"}`;
|
||||
|
||||
let duration = "";
|
||||
if (trigger.offset) {
|
||||
let duration = "";
|
||||
|
||||
if (trigger.offset) {
|
||||
if (typeof trigger.offset === "number") {
|
||||
duration = ` offset by ${secondsToDuration(trigger.offset)!}`;
|
||||
} else if (typeof trigger.offset === "string") {
|
||||
duration = ` offset by ${trigger.offset}`;
|
||||
} else {
|
||||
duration = ` offset by ${JSON.stringify(trigger.offset)}`;
|
||||
}
|
||||
if (typeof trigger.offset === "number") {
|
||||
duration = secondsToDuration(trigger.offset)!;
|
||||
} else if (typeof trigger.offset === "string") {
|
||||
duration = trigger.offset;
|
||||
} else {
|
||||
duration = JSON.stringify(trigger.offset);
|
||||
}
|
||||
base += duration;
|
||||
}
|
||||
|
||||
return base;
|
||||
return hass.localize(
|
||||
trigger.event === "sunset"
|
||||
? `${triggerTranslationBaseKey}.sun.description.sets`
|
||||
: `${triggerTranslationBaseKey}.sun.description.rises`,
|
||||
{ hasDuration: duration !== "", duration: duration }
|
||||
);
|
||||
}
|
||||
|
||||
// Tag Trigger
|
||||
if (trigger.platform === "tag") {
|
||||
return "When a tag is scanned";
|
||||
return hass.localize(`${triggerTranslationBaseKey}.tag.description.full`);
|
||||
}
|
||||
|
||||
// Time Trigger
|
||||
@ -375,10 +371,9 @@ export const describeTrigger = (
|
||||
: localizeTimeString(at, hass.locale, hass.config)
|
||||
);
|
||||
|
||||
const last = result.splice(-1, 1)[0];
|
||||
return `When the time is equal to ${
|
||||
result.length ? `${result.join(", ")} or ` : ""
|
||||
}${last}`;
|
||||
return hass.localize(`${triggerTranslationBaseKey}.time.description.full`, {
|
||||
time: disjunctionFormatter.format(result),
|
||||
});
|
||||
}
|
||||
|
||||
// Time Pattern Trigger
|
||||
@ -501,9 +496,9 @@ export const describeTrigger = (
|
||||
const states = hass.states;
|
||||
|
||||
if (Array.isArray(trigger.entity_id)) {
|
||||
for (const [entity] of trigger.entity_id.entries()) {
|
||||
for (const entity of trigger.entity_id.values()) {
|
||||
if (states[entity]) {
|
||||
entities.push(`${computeStateName(states[entity]) || entity}`);
|
||||
entities.push(computeStateName(states[entity]) || entity);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -515,9 +510,9 @@ export const describeTrigger = (
|
||||
}
|
||||
|
||||
if (Array.isArray(trigger.zone)) {
|
||||
for (const [zone] of trigger.zone.entries()) {
|
||||
for (const zone of trigger.zone.values()) {
|
||||
if (states[zone]) {
|
||||
zones.push(`${computeStateName(states[zone]) || zone}`);
|
||||
zones.push(computeStateName(states[zone]) || zone);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -537,67 +532,62 @@ export const describeTrigger = (
|
||||
|
||||
// Geo Location Trigger
|
||||
if (trigger.platform === "geo_location" && trigger.source && trigger.zone) {
|
||||
let sources = "";
|
||||
let zones = "";
|
||||
let zonesPlural = false;
|
||||
const sources: string[] = [];
|
||||
const zones: string[] = [];
|
||||
const states = hass.states;
|
||||
|
||||
if (Array.isArray(trigger.source)) {
|
||||
for (const [index, source] of trigger.source.entries()) {
|
||||
sources += `${index > 0 ? "," : ""} ${
|
||||
trigger.source.length > 1 && index === trigger.source.length - 1
|
||||
? "or"
|
||||
: ""
|
||||
} ${source}`;
|
||||
for (const source of trigger.source.values()) {
|
||||
sources.push(source);
|
||||
}
|
||||
} else {
|
||||
sources = trigger.source;
|
||||
sources.push(trigger.source);
|
||||
}
|
||||
|
||||
if (Array.isArray(trigger.zone)) {
|
||||
if (trigger.zone.length > 1) {
|
||||
zonesPlural = true;
|
||||
}
|
||||
|
||||
for (const [index, zone] of trigger.zone.entries()) {
|
||||
for (const zone of trigger.zone.values()) {
|
||||
if (states[zone]) {
|
||||
zones += `${index > 0 ? "," : ""} ${
|
||||
trigger.zone.length > 1 && index === trigger.zone.length - 1
|
||||
? "or"
|
||||
: ""
|
||||
} ${computeStateName(states[zone]) || zone}`;
|
||||
zones.push(computeStateName(states[zone]) || zone);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
zones = states[trigger.zone]
|
||||
? computeStateName(states[trigger.zone])
|
||||
: trigger.zone;
|
||||
zones.push(
|
||||
states[trigger.zone]
|
||||
? computeStateName(states[trigger.zone])
|
||||
: trigger.zone
|
||||
);
|
||||
}
|
||||
|
||||
return `When ${sources} ${trigger.event}s ${zones} ${
|
||||
zonesPlural ? "zones" : "zone"
|
||||
const sourcesString = disjunctionFormatter.format(sources);
|
||||
const zonesString = disjunctionFormatter.format(zones);
|
||||
return `When ${sourcesString} ${trigger.event}s ${zonesString} ${
|
||||
zones.length > 1 ? "zones" : "zone"
|
||||
}`;
|
||||
}
|
||||
|
||||
// MQTT Trigger
|
||||
if (trigger.platform === "mqtt") {
|
||||
return "When an MQTT message has been received";
|
||||
return hass.localize(`${triggerTranslationBaseKey}.mqtt.description.full`);
|
||||
}
|
||||
|
||||
// Template Trigger
|
||||
if (trigger.platform === "template") {
|
||||
let base = "When a template triggers";
|
||||
let duration = "";
|
||||
if (trigger.for) {
|
||||
const duration = describeDuration(trigger.for);
|
||||
if (duration) {
|
||||
base += ` for ${duration}`;
|
||||
}
|
||||
duration = describeDuration(trigger.for) ?? "";
|
||||
}
|
||||
return base;
|
||||
|
||||
return hass.localize(
|
||||
`${triggerTranslationBaseKey}.template.description.full`,
|
||||
{ hasDuration: duration !== "", duration: duration }
|
||||
);
|
||||
}
|
||||
|
||||
// Webhook Trigger
|
||||
if (trigger.platform === "webhook") {
|
||||
return "When a Webhook payload has been received";
|
||||
return hass.localize(
|
||||
`${triggerTranslationBaseKey}.webhook.description.full`
|
||||
);
|
||||
}
|
||||
|
||||
// Persistent Notification Trigger
|
||||
@ -640,6 +630,10 @@ export const describeCondition = (
|
||||
return condition.alias;
|
||||
}
|
||||
|
||||
const conjunctionFormatter = new Intl.ListFormat("en", {
|
||||
style: "long",
|
||||
type: "conjunction",
|
||||
});
|
||||
const disjunctionFormatter = new Intl.ListFormat("en", {
|
||||
style: "long",
|
||||
type: "disjunction",
|
||||
@ -714,21 +708,20 @@ export const describeCondition = (
|
||||
}
|
||||
|
||||
if (Array.isArray(condition.entity_id)) {
|
||||
let entities = "";
|
||||
for (const [index, entity] of condition.entity_id.entries()) {
|
||||
const entities: string[] = [];
|
||||
for (const entity of condition.entity_id.values()) {
|
||||
if (hass.states[entity]) {
|
||||
entities += `${index > 0 ? "," : ""} ${
|
||||
condition.entity_id.length > 1 &&
|
||||
index === condition.entity_id.length - 1
|
||||
? condition.match === "any"
|
||||
? "or"
|
||||
: "and"
|
||||
: ""
|
||||
} ${computeStateName(hass.states[entity]) || entity}`;
|
||||
entities.push(computeStateName(hass.states[entity]) || entity);
|
||||
}
|
||||
}
|
||||
if (entities) {
|
||||
base += ` ${entities} ${condition.entity_id.length > 1 ? "are" : "is"}`;
|
||||
if (entities.length !== 0) {
|
||||
const entitiesString =
|
||||
condition.match === "any"
|
||||
? disjunctionFormatter.format(entities)
|
||||
: conjunctionFormatter.format(entities);
|
||||
base += ` ${entitiesString} ${
|
||||
condition.entity_id.length > 1 ? "are" : "is"
|
||||
}`;
|
||||
} else {
|
||||
// no entity_id or empty array
|
||||
base += " an entity";
|
||||
@ -741,7 +734,7 @@ export const describeCondition = (
|
||||
} is`;
|
||||
}
|
||||
|
||||
let states = "";
|
||||
const states: string[] = [];
|
||||
const stateObj =
|
||||
hass.states[
|
||||
Array.isArray(condition.entity_id)
|
||||
@ -749,12 +742,8 @@ export const describeCondition = (
|
||||
: condition.entity_id
|
||||
];
|
||||
if (Array.isArray(condition.state)) {
|
||||
for (const [index, state] of condition.state.entries()) {
|
||||
states += `${index > 0 ? "," : ""} ${
|
||||
condition.state.length > 1 && index === condition.state.length - 1
|
||||
? "or"
|
||||
: ""
|
||||
} '${
|
||||
for (const state of condition.state.values()) {
|
||||
states.push(
|
||||
condition.attribute
|
||||
? computeAttributeValueDisplay(
|
||||
hass.localize,
|
||||
@ -764,7 +753,7 @@ export const describeCondition = (
|
||||
hass.entities,
|
||||
condition.attribute,
|
||||
state
|
||||
)
|
||||
).toString()
|
||||
: computeStateDisplay(
|
||||
hass.localize,
|
||||
stateObj,
|
||||
@ -773,10 +762,10 @@ export const describeCondition = (
|
||||
hass.entities,
|
||||
state
|
||||
)
|
||||
}'`;
|
||||
);
|
||||
}
|
||||
} else if (condition.state !== "") {
|
||||
states = `'${
|
||||
states.push(
|
||||
condition.attribute
|
||||
? computeAttributeValueDisplay(
|
||||
hass.localize,
|
||||
@ -794,15 +783,16 @@ export const describeCondition = (
|
||||
hass.config,
|
||||
hass.entities,
|
||||
condition.state.toString()
|
||||
).toString()
|
||||
}'`;
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (!states) {
|
||||
states = "a state";
|
||||
if (states.length === 0) {
|
||||
states.push("a state");
|
||||
}
|
||||
|
||||
base += ` ${states}`;
|
||||
const statesString = disjunctionFormatter.format(states);
|
||||
base += ` ${statesString}`;
|
||||
|
||||
if (condition.for) {
|
||||
const duration = describeDuration(condition.for);
|
||||
@ -891,17 +881,7 @@ export const describeCondition = (
|
||||
`ui.panel.config.automation.editor.conditions.type.time.weekdays.${d}`
|
||||
)
|
||||
);
|
||||
const last = localizedDays.pop();
|
||||
|
||||
result += " day is " + localizedDays.join(", ");
|
||||
|
||||
if (localizedDays.length) {
|
||||
if (localizedDays.length > 1) {
|
||||
result += ",";
|
||||
}
|
||||
result += " or ";
|
||||
}
|
||||
result += last;
|
||||
result += " day is " + disjunctionFormatter.format(localizedDays);
|
||||
}
|
||||
|
||||
return result;
|
||||
@ -953,9 +933,9 @@ export const describeCondition = (
|
||||
const states = hass.states;
|
||||
|
||||
if (Array.isArray(condition.entity_id)) {
|
||||
for (const [entity] of condition.entity_id.entries()) {
|
||||
for (const entity of condition.entity_id.values()) {
|
||||
if (states[entity]) {
|
||||
entities.push(`${computeStateName(states[entity]) || entity}`);
|
||||
entities.push(computeStateName(states[entity]) || entity);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -967,9 +947,9 @@ export const describeCondition = (
|
||||
}
|
||||
|
||||
if (Array.isArray(condition.zone)) {
|
||||
for (const [zone] of condition.zone.entries()) {
|
||||
for (const zone of condition.zone.values()) {
|
||||
if (states[zone]) {
|
||||
zones.push(`${computeStateName(states[zone]) || zone}`);
|
||||
zones.push(computeStateName(states[zone]) || zone);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -16,6 +16,7 @@ export const CLIMATE_PRESET_NONE = "none";
|
||||
|
||||
export type HvacAction =
|
||||
| "off"
|
||||
| "preheating"
|
||||
| "heating"
|
||||
| "cooling"
|
||||
| "drying"
|
||||
@ -77,6 +78,7 @@ export const HVAC_ACTION_TO_MODE: Record<HvacAction, HvacMode> = {
|
||||
cooling: "cool",
|
||||
drying: "dry",
|
||||
fan: "fan_only",
|
||||
preheating: "heat",
|
||||
heating: "heat",
|
||||
idle: "off",
|
||||
off: "off",
|
||||
|
@ -159,3 +159,5 @@ export const computeDefaultFavoriteColors = (
|
||||
|
||||
return colors;
|
||||
};
|
||||
|
||||
export const formatTempColor = (value: number) => `${value} K`;
|
||||
|
20
src/data/lock.ts
Normal file
20
src/data/lock.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import {
|
||||
HassEntityAttributeBase,
|
||||
HassEntityBase,
|
||||
} from "home-assistant-js-websocket";
|
||||
|
||||
export const FORMAT_TEXT = "text";
|
||||
export const FORMAT_NUMBER = "number";
|
||||
|
||||
export const enum LockEntityFeature {
|
||||
OPEN = 1,
|
||||
}
|
||||
|
||||
interface LockEntityAttributes extends HassEntityAttributeBase {
|
||||
code_format?: string;
|
||||
changed_by?: string | null;
|
||||
}
|
||||
|
||||
export interface LockEntity extends HassEntityBase {
|
||||
attributes: LockEntityAttributes;
|
||||
}
|
@ -152,6 +152,12 @@ export interface MoreInfoActionConfig extends BaseActionConfig {
|
||||
action: "more-info";
|
||||
}
|
||||
|
||||
export interface AssistActionConfig extends BaseActionConfig {
|
||||
action: "assist";
|
||||
pipeline_id?: string;
|
||||
start_listening?: boolean;
|
||||
}
|
||||
|
||||
export interface NoActionConfig extends BaseActionConfig {
|
||||
action: "none";
|
||||
}
|
||||
@ -180,6 +186,7 @@ export type ActionConfig =
|
||||
| NavigateActionConfig
|
||||
| UrlActionConfig
|
||||
| MoreInfoActionConfig
|
||||
| AssistActionConfig
|
||||
| NoActionConfig
|
||||
| CustomActionConfig;
|
||||
|
||||
|
@ -33,6 +33,7 @@ export const isMaxMode = arrayLiteralIncludes(MODES_MAX);
|
||||
|
||||
export const baseActionStruct = object({
|
||||
alias: optional(string()),
|
||||
continue_on_error: optional(boolean()),
|
||||
enabled: optional(boolean()),
|
||||
});
|
||||
|
||||
@ -99,6 +100,7 @@ export interface BlueprintScriptConfig extends ManualScriptConfig {
|
||||
|
||||
interface BaseAction {
|
||||
alias?: string;
|
||||
continue_on_error?: boolean;
|
||||
enabled?: boolean;
|
||||
}
|
||||
|
||||
@ -230,14 +232,10 @@ interface UnknownAction extends BaseAction {
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
export type Action =
|
||||
export type NonConditionAction =
|
||||
| EventAction
|
||||
| DeviceAction
|
||||
| ServiceAction
|
||||
| Condition
|
||||
| ShorthandAndCondition
|
||||
| ShorthandOrCondition
|
||||
| ShorthandNotCondition
|
||||
| DelayAction
|
||||
| SceneAction
|
||||
| WaitAction
|
||||
@ -251,6 +249,13 @@ export type Action =
|
||||
| ParallelAction
|
||||
| UnknownAction;
|
||||
|
||||
export type Action =
|
||||
| NonConditionAction
|
||||
| Condition
|
||||
| ShorthandAndCondition
|
||||
| ShorthandOrCondition
|
||||
| ShorthandNotCondition;
|
||||
|
||||
export interface ActionTypes {
|
||||
delay: DelayAction;
|
||||
wait_template: WaitAction;
|
||||
|
@ -345,8 +345,7 @@ export interface TemplateSelector {
|
||||
}
|
||||
|
||||
export interface ThemeSelector {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
theme: {} | null;
|
||||
theme: { include_default?: boolean } | null;
|
||||
}
|
||||
export interface TimeSelector {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
|
@ -1,14 +1,15 @@
|
||||
import { mdiCheck, mdiClose } from "@mdi/js";
|
||||
import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import "../../../../components/ha-button";
|
||||
import "../../../../components/ha-control-button";
|
||||
import { createCloseHeading } from "../../../../components/ha-dialog";
|
||||
import "../../../../components/ha-textfield";
|
||||
import type { HaTextField } from "../../../../components/ha-textfield";
|
||||
import { HomeAssistant } from "../../../../types";
|
||||
import { HassDialog } from "../../../make-dialog-manager";
|
||||
import { ifDefined } from "lit/directives/if-defined";
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import "../../components/ha-button";
|
||||
import "../../components/ha-control-button";
|
||||
import { createCloseHeading } from "../../components/ha-dialog";
|
||||
import "../../components/ha-textfield";
|
||||
import type { HaTextField } from "../../components/ha-textfield";
|
||||
import { HomeAssistant } from "../../types";
|
||||
import { HassDialog } from "../make-dialog-manager";
|
||||
import { EnterCodeDialogParams } from "./show-enter-code-dialog";
|
||||
|
||||
const BUTTONS = [
|
||||
@ -72,7 +73,8 @@ export class DialogEnterCode
|
||||
}
|
||||
|
||||
private _inputValueChange(e) {
|
||||
const val = (e.currentTarget! as any).value;
|
||||
const field = e.currentTarget as HaTextField;
|
||||
const val = field.value;
|
||||
this._showClearButton = !!val;
|
||||
}
|
||||
|
||||
@ -97,6 +99,7 @@ export class DialogEnterCode
|
||||
id="code"
|
||||
.label=${this.hass.localize("ui.dialogs.enter_code.input_label")}
|
||||
type="password"
|
||||
pattern=${ifDefined(this._dialogParams.codePattern)}
|
||||
input-mode="text"
|
||||
></ha-textfield>
|
||||
<ha-button slot="secondaryAction" dialogAction="cancel">
|
@ -1,7 +1,8 @@
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
|
||||
export interface EnterCodeDialogParams {
|
||||
codeFormat: "text" | "number";
|
||||
codePattern?: string;
|
||||
submitText?: string;
|
||||
cancelText?: string;
|
||||
title?: string;
|
@ -14,7 +14,7 @@ import {
|
||||
} from "../../../../data/alarm_control_panel";
|
||||
import { UNAVAILABLE } from "../../../../data/entity";
|
||||
import { HomeAssistant } from "../../../../types";
|
||||
import { showEnterCodeDialogDialog } from "./show-enter-code-dialog";
|
||||
import { showEnterCodeDialogDialog } from "../../../enter-code/show-enter-code-dialog";
|
||||
|
||||
@customElement("ha-more-info-alarm_control_panel-modes")
|
||||
export class HaMoreInfoAlarmControlPanelModes extends LitElement {
|
||||
|
@ -1,16 +1,28 @@
|
||||
import { mdiClose } from "@mdi/js";
|
||||
import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { classMap } from "lit/directives/class-map";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import "../../../../components/ha-button";
|
||||
import "../../../../components/ha-dialog";
|
||||
import "../../../../components/ha-dialog-header";
|
||||
import { EntityRegistryEntry } from "../../../../data/entity_registry";
|
||||
import { LightColor } from "../../../../data/light";
|
||||
import "../../../../components/ha-icon-button-toggle";
|
||||
import type { EntityRegistryEntry } from "../../../../data/entity_registry";
|
||||
import {
|
||||
formatTempColor,
|
||||
LightColor,
|
||||
LightColorMode,
|
||||
LightEntity,
|
||||
lightSupportsColor,
|
||||
lightSupportsColorMode,
|
||||
} from "../../../../data/light";
|
||||
import { haStyleDialog } from "../../../../resources/styles";
|
||||
import { HomeAssistant } from "../../../../types";
|
||||
import "./light-color-picker";
|
||||
import { LightColorFavoriteDialogParams } from "./show-dialog-light-color-favorite";
|
||||
import type { HomeAssistant } from "../../../../types";
|
||||
import "./light-color-rgb-picker";
|
||||
import "./light-color-temp-picker";
|
||||
import type { LightColorFavoriteDialogParams } from "./show-dialog-light-color-favorite";
|
||||
|
||||
export type LightPickerMode = "color_temp" | "color";
|
||||
|
||||
@customElement("dialog-light-color-favorite")
|
||||
class DialogLightColorFavorite extends LitElement {
|
||||
@ -22,11 +34,26 @@ class DialogLightColorFavorite extends LitElement {
|
||||
|
||||
@state() _color?: LightColor;
|
||||
|
||||
@state() private _mode?: LightPickerMode;
|
||||
|
||||
@state() private _modes: LightPickerMode[] = [];
|
||||
|
||||
@state() private _currentValue?: string;
|
||||
|
||||
private _colorHovered(ev: CustomEvent<HASSDomEvents["color-hovered"]>) {
|
||||
if (ev.detail && "color_temp_kelvin" in ev.detail) {
|
||||
this._currentValue = formatTempColor(ev.detail.color_temp_kelvin);
|
||||
} else {
|
||||
this._currentValue = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
public async showDialog(
|
||||
dialogParams: LightColorFavoriteDialogParams
|
||||
): Promise<void> {
|
||||
this._entry = dialogParams.entry;
|
||||
this._dialogParams = dialogParams;
|
||||
this._updateModes(dialogParams.defaultMode);
|
||||
await this.updateComplete;
|
||||
}
|
||||
|
||||
@ -37,10 +64,43 @@ class DialogLightColorFavorite extends LitElement {
|
||||
fireEvent(this, "dialog-closed", { dialog: this.localName });
|
||||
}
|
||||
|
||||
private _updateModes(defaultMode?: LightPickerMode) {
|
||||
const supportsTemp = lightSupportsColorMode(
|
||||
this.stateObj!,
|
||||
LightColorMode.COLOR_TEMP
|
||||
);
|
||||
|
||||
const supportsColor = lightSupportsColor(this.stateObj!);
|
||||
|
||||
const modes: LightPickerMode[] = [];
|
||||
if (supportsColor) {
|
||||
modes.push("color");
|
||||
}
|
||||
if (supportsTemp) {
|
||||
modes.push("color_temp");
|
||||
}
|
||||
|
||||
this._modes = modes;
|
||||
this._mode =
|
||||
defaultMode ??
|
||||
(this.stateObj!.attributes.color_mode
|
||||
? this.stateObj!.attributes.color_mode === LightColorMode.COLOR_TEMP
|
||||
? LightColorMode.COLOR_TEMP
|
||||
: "color"
|
||||
: this._modes[0]);
|
||||
}
|
||||
|
||||
private _colorChanged(ev: CustomEvent) {
|
||||
this._color = ev.detail;
|
||||
}
|
||||
|
||||
get stateObj() {
|
||||
return (
|
||||
this._entry &&
|
||||
(this.hass.states[this._entry.entity_id] as LightEntity | undefined)
|
||||
);
|
||||
}
|
||||
|
||||
private async _cancel() {
|
||||
this._dialogParams?.cancel?.();
|
||||
this.closeDialog();
|
||||
@ -55,8 +115,16 @@ class DialogLightColorFavorite extends LitElement {
|
||||
this.closeDialog();
|
||||
}
|
||||
|
||||
private _modeChanged(ev): void {
|
||||
const newMode = ev.currentTarget.mode;
|
||||
if (newMode === this._mode) {
|
||||
return;
|
||||
}
|
||||
this._mode = newMode;
|
||||
}
|
||||
|
||||
protected render() {
|
||||
if (!this._entry) {
|
||||
if (!this._entry || !this.stateObj) {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
@ -76,13 +144,58 @@ class DialogLightColorFavorite extends LitElement {
|
||||
></ha-icon-button>
|
||||
<span slot="title">${this._dialogParams?.title}</span>
|
||||
</ha-dialog-header>
|
||||
<light-color-picker
|
||||
.hass=${this.hass}
|
||||
entityId=${this._entry.entity_id}
|
||||
.defaultMode=${this._dialogParams?.defaultMode}
|
||||
@color-changed=${this._colorChanged}
|
||||
>
|
||||
</light-color-picker>
|
||||
<div class="header">
|
||||
<span class="value">${this._currentValue}</span>
|
||||
${this._modes.length > 1
|
||||
? html`
|
||||
<div class="modes">
|
||||
${this._modes.map(
|
||||
(value) =>
|
||||
html`
|
||||
<ha-icon-button-toggle
|
||||
border-only
|
||||
.selected=${value === this._mode}
|
||||
.label=${this.hass.localize(
|
||||
`ui.dialogs.more_info_control.light.color_picker.mode.${value}`
|
||||
)}
|
||||
.mode=${value}
|
||||
@click=${this._modeChanged}
|
||||
>
|
||||
<span
|
||||
class="wheel ${classMap({ [value]: true })}"
|
||||
></span>
|
||||
</ha-icon-button-toggle>
|
||||
`
|
||||
)}
|
||||
</div>
|
||||
`
|
||||
: nothing}
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
${this._mode === "color_temp"
|
||||
? html`
|
||||
<light-color-temp-picker
|
||||
.hass=${this.hass}
|
||||
.stateObj=${this.stateObj}
|
||||
@color-changed=${this._colorChanged}
|
||||
@color-hovered=${this._colorHovered}
|
||||
>
|
||||
</light-color-temp-picker>
|
||||
`
|
||||
: nothing}
|
||||
${this._mode === "color"
|
||||
? html`
|
||||
<light-color-rgb-picker
|
||||
.hass=${this.hass}
|
||||
.stateObj=${this.stateObj}
|
||||
@color-changed=${this._colorChanged}
|
||||
@color-hovered=${this._colorHovered}
|
||||
>
|
||||
</light-color-rgb-picker>
|
||||
`
|
||||
: nothing}
|
||||
</div>
|
||||
<ha-button slot="secondaryAction" dialogAction="cancel">
|
||||
${this.hass.localize("ui.common.cancel")}
|
||||
</ha-button>
|
||||
@ -101,16 +214,10 @@ class DialogLightColorFavorite extends LitElement {
|
||||
--dialog-content-padding: 0;
|
||||
}
|
||||
|
||||
light-color-picker {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
@media all and (max-width: 450px), all and (max-height: 500px) {
|
||||
ha-dialog {
|
||||
--dialog-surface-margin-top: 100px;
|
||||
--mdc-dialog-min-height: calc(100% - 100px);
|
||||
--mdc-dialog-min-height: auto;
|
||||
--mdc-dialog-max-height: calc(100% - 100px);
|
||||
--ha-dialog-border-radius: var(
|
||||
--ha-dialog-bottom-sheet-border-radius,
|
||||
@ -118,6 +225,54 @@ class DialogLightColorFavorite extends LitElement {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 24px;
|
||||
flex: 1;
|
||||
}
|
||||
.modes {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-end;
|
||||
padding: 0 24px;
|
||||
}
|
||||
.wheel {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
flex: none;
|
||||
border-radius: 15px;
|
||||
}
|
||||
.wheel.color {
|
||||
background-image: url("/static/images/color_wheel.png");
|
||||
background-size: cover;
|
||||
}
|
||||
.wheel.color_temp {
|
||||
background: linear-gradient(
|
||||
0,
|
||||
rgb(166, 209, 255) 0%,
|
||||
white 50%,
|
||||
rgb(255, 160, 0) 100%
|
||||
);
|
||||
}
|
||||
.value {
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
margin: auto;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-size: 16px;
|
||||
height: 48px;
|
||||
line-height: 48px;
|
||||
letter-spacing: 0.1px;
|
||||
text-align: center;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
@ -30,10 +30,16 @@ import {
|
||||
} from "../../../../resources/sortable.ondemand";
|
||||
import { HomeAssistant } from "../../../../types";
|
||||
import { showConfirmationDialog } from "../../../generic/show-dialog-box";
|
||||
import type { LightPickerMode } from "./dialog-light-color-favorite";
|
||||
import "./ha-favorite-color-button";
|
||||
import type { LightPickerMode } from "./light-color-picker";
|
||||
import { showLightColorFavoriteDialog } from "./show-dialog-light-color-favorite";
|
||||
|
||||
declare global {
|
||||
interface HASSDomEvents {
|
||||
"favorite-color-edit-started";
|
||||
}
|
||||
}
|
||||
|
||||
@customElement("ha-more-info-light-favorite-colors")
|
||||
export class HaMoreInfoLightFavoriteColors extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
@ -147,8 +153,8 @@ export class HaMoreInfoLightFavoriteColors extends LitElement {
|
||||
|
||||
private _edit = async (index) => {
|
||||
// Make sure the current favorite color is set
|
||||
fireEvent(this, "favorite-color-edit-started");
|
||||
await this._apply(index);
|
||||
|
||||
const defaultMode: LightPickerMode =
|
||||
"color_temp_kelvin" in this._favoriteColors[index]
|
||||
? "color_temp"
|
||||
|
@ -1,51 +0,0 @@
|
||||
import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { HomeAssistant } from "../../../../types";
|
||||
import "./light-color-picker";
|
||||
import { LightColorPickerViewParams } from "./show-view-light-color-picker";
|
||||
|
||||
@customElement("ha-more-info-view-light-color-picker")
|
||||
class MoreInfoViewLightColorPicker extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property() public params?: LightColorPickerViewParams;
|
||||
|
||||
protected render() {
|
||||
if (!this.params) {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
return html`
|
||||
<light-color-picker
|
||||
.hass=${this.hass}
|
||||
.entityId=${this.params.entityId}
|
||||
.defaultMode=${this.params.defaultMode}
|
||||
>
|
||||
</light-color-picker>
|
||||
`;
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return [
|
||||
css`
|
||||
:host {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
}
|
||||
light-color-picker {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-more-info-view-light-color-picker": MoreInfoViewLightColorPicker;
|
||||
}
|
||||
}
|
@ -23,21 +23,18 @@ import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import { throttle } from "../../../../common/util/throttle";
|
||||
import "../../../../components/ha-button-toggle-group";
|
||||
import "../../../../components/ha-hs-color-picker";
|
||||
import "../../../../components/ha-icon";
|
||||
import "../../../../components/ha-icon-button-prev";
|
||||
import "../../../../components/ha-labeled-slider";
|
||||
import "../../../../components/ha-temp-color-picker";
|
||||
import {
|
||||
LightColor,
|
||||
getLightCurrentModeRgbColor,
|
||||
LightColor,
|
||||
LightColorMode,
|
||||
LightEntity,
|
||||
lightSupportsColor,
|
||||
lightSupportsColorMode,
|
||||
} from "../../../../data/light";
|
||||
import { HomeAssistant } from "../../../../types";
|
||||
import "../../../../components/ha-icon";
|
||||
|
||||
export type LightPickerMode = "color_temp" | "color";
|
||||
|
||||
declare global {
|
||||
interface HASSDomEvents {
|
||||
@ -45,13 +42,11 @@ declare global {
|
||||
}
|
||||
}
|
||||
|
||||
@customElement("light-color-picker")
|
||||
class LightColorPicker extends LitElement {
|
||||
@customElement("light-color-rgb-picker")
|
||||
class LightRgbColorPicker extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property() public entityId!: string;
|
||||
|
||||
@property() public defaultMode?: LightPickerMode;
|
||||
@property({ attribute: false }) public stateObj!: LightEntity;
|
||||
|
||||
@state() private _cwSliderValue?: number;
|
||||
|
||||
@ -65,16 +60,6 @@ class LightColorPicker extends LitElement {
|
||||
|
||||
@state() private _hsPickerValue?: [number, number];
|
||||
|
||||
@state() private _ctPickerValue?: number;
|
||||
|
||||
@state() private _mode?: LightPickerMode;
|
||||
|
||||
@state() private _modes: LightPickerMode[] = [];
|
||||
|
||||
get stateObj() {
|
||||
return this.hass.states[this.entityId] as LightEntity | undefined;
|
||||
}
|
||||
|
||||
protected render() {
|
||||
if (!this.stateObj) {
|
||||
return nothing;
|
||||
@ -100,135 +85,89 @@ class LightColorPicker extends LitElement {
|
||||
: "";
|
||||
|
||||
return html`
|
||||
${this._modes.length > 1
|
||||
<div class="color-container">
|
||||
<label class="native-color-picker">
|
||||
<input
|
||||
type="color"
|
||||
.value=${hexValue ?? ""}
|
||||
@input=${this._nativeColorChanged}
|
||||
/>
|
||||
<ha-svg-icon .path=${mdiEyedropper}></ha-svg-icon>
|
||||
</label>
|
||||
|
||||
<ha-hs-color-picker
|
||||
@value-changed=${this._hsColorChanged}
|
||||
@cursor-moved=${this._hsColorCursorMoved}
|
||||
.value=${this._hsPickerValue}
|
||||
.colorBrightness=${this._colorBrightnessSliderValue != null
|
||||
? (this._colorBrightnessSliderValue * 255) / 100
|
||||
: undefined}
|
||||
.wv=${this._wvSliderValue != null
|
||||
? (this._wvSliderValue * 255) / 100
|
||||
: undefined}
|
||||
.ww=${this._wwSliderValue != null
|
||||
? (this._wwSliderValue * 255) / 100
|
||||
: undefined}
|
||||
.cw=${this._cwSliderValue != null
|
||||
? (this._cwSliderValue * 255) / 100
|
||||
: undefined}
|
||||
.minKelvin=${this.stateObj.attributes.min_color_temp_kelvin}
|
||||
.maxKelvin=${this.stateObj.attributes.max_color_temp_kelvin}
|
||||
>
|
||||
</ha-hs-color-picker>
|
||||
</div>
|
||||
${supportsRgbw || supportsRgbww
|
||||
? html`<ha-labeled-slider
|
||||
.caption=${this.hass.localize("ui.card.light.color_brightness")}
|
||||
icon="hass:brightness-7"
|
||||
max="100"
|
||||
.value=${this._colorBrightnessSliderValue}
|
||||
@change=${this._colorBrightnessSliderChanged}
|
||||
pin
|
||||
></ha-labeled-slider>`
|
||||
: nothing}
|
||||
${supportsRgbw
|
||||
? html`
|
||||
<mwc-tab-bar
|
||||
.activeIndex=${this._mode ? this._modes.indexOf(this._mode) : 0}
|
||||
@MDCTabBar:activated=${this._handleTabChanged}
|
||||
>
|
||||
${this._modes.map(
|
||||
(value) =>
|
||||
html`<mwc-tab
|
||||
.label=${this.hass.localize(
|
||||
`ui.dialogs.more_info_control.light.color_picker.mode.${value}`
|
||||
)}
|
||||
></mwc-tab>`
|
||||
)}
|
||||
</mwc-tab-bar>
|
||||
<ha-labeled-slider
|
||||
.caption=${this.hass.localize("ui.card.light.white_value")}
|
||||
icon="hass:file-word-box"
|
||||
max="100"
|
||||
.name=${"wv"}
|
||||
.value=${this._wvSliderValue}
|
||||
@change=${this._wvSliderChanged}
|
||||
pin
|
||||
></ha-labeled-slider>
|
||||
`
|
||||
: nothing}
|
||||
${supportsRgbww
|
||||
? html`
|
||||
<ha-labeled-slider
|
||||
.caption=${this.hass.localize("ui.card.light.cold_white_value")}
|
||||
icon="hass:file-word-box-outline"
|
||||
max="100"
|
||||
.name=${"cw"}
|
||||
.value=${this._cwSliderValue}
|
||||
@change=${this._wvSliderChanged}
|
||||
pin
|
||||
></ha-labeled-slider>
|
||||
<ha-labeled-slider
|
||||
.caption=${this.hass.localize("ui.card.light.warm_white_value")}
|
||||
icon="hass:file-word-box"
|
||||
max="100"
|
||||
.name=${"ww"}
|
||||
.value=${this._wwSliderValue}
|
||||
@change=${this._wvSliderChanged}
|
||||
pin
|
||||
></ha-labeled-slider>
|
||||
`
|
||||
: nothing}
|
||||
<div class="content">
|
||||
${this._mode === LightColorMode.COLOR_TEMP
|
||||
? html`
|
||||
<p class="color-temp-value">
|
||||
${this._ctPickerValue ? `${this._ctPickerValue} K` : nothing}
|
||||
</p>
|
||||
<ha-temp-color-picker
|
||||
@value-changed=${this._ctColorChanged}
|
||||
@cursor-moved=${this._ctColorCursorMoved}
|
||||
.min=${this.stateObj.attributes.min_color_temp_kelvin!}
|
||||
.max=${this.stateObj.attributes.max_color_temp_kelvin!}
|
||||
.value=${this._ctPickerValue}
|
||||
>
|
||||
</ha-temp-color-picker>
|
||||
`
|
||||
: nothing}
|
||||
${this._mode === "color"
|
||||
? html`
|
||||
<div class="color-container">
|
||||
<label class="native-color-picker">
|
||||
<input
|
||||
type="color"
|
||||
.value=${hexValue ?? ""}
|
||||
@input=${this._nativeColorChanged}
|
||||
/>
|
||||
<ha-svg-icon .path=${mdiEyedropper}></ha-svg-icon>
|
||||
</label>
|
||||
|
||||
<ha-hs-color-picker
|
||||
@value-changed=${this._hsColorChanged}
|
||||
@cursor-moved=${this._hsColorCursorMoved}
|
||||
.value=${this._hsPickerValue}
|
||||
.colorBrightness=${this._colorBrightnessSliderValue != null
|
||||
? (this._colorBrightnessSliderValue * 255) / 100
|
||||
: undefined}
|
||||
.wv=${this._wvSliderValue != null
|
||||
? (this._wvSliderValue * 255) / 100
|
||||
: undefined}
|
||||
.ww=${this._wwSliderValue != null
|
||||
? (this._wwSliderValue * 255) / 100
|
||||
: undefined}
|
||||
.cw=${this._cwSliderValue != null
|
||||
? (this._cwSliderValue * 255) / 100
|
||||
: undefined}
|
||||
.minKelvin=${this.stateObj.attributes.min_color_temp_kelvin}
|
||||
.maxKelvin=${this.stateObj.attributes.max_color_temp_kelvin}
|
||||
>
|
||||
</ha-hs-color-picker>
|
||||
</div>
|
||||
${supportsRgbw || supportsRgbww
|
||||
? html`<ha-labeled-slider
|
||||
.caption=${this.hass.localize(
|
||||
"ui.card.light.color_brightness"
|
||||
)}
|
||||
icon="hass:brightness-7"
|
||||
max="100"
|
||||
.value=${this._colorBrightnessSliderValue}
|
||||
@change=${this._colorBrightnessSliderChanged}
|
||||
pin
|
||||
></ha-labeled-slider>`
|
||||
: nothing}
|
||||
${supportsRgbw
|
||||
? html`
|
||||
<ha-labeled-slider
|
||||
.caption=${this.hass.localize(
|
||||
"ui.card.light.white_value"
|
||||
)}
|
||||
icon="hass:file-word-box"
|
||||
max="100"
|
||||
.name=${"wv"}
|
||||
.value=${this._wvSliderValue}
|
||||
@change=${this._wvSliderChanged}
|
||||
pin
|
||||
></ha-labeled-slider>
|
||||
`
|
||||
: nothing}
|
||||
${supportsRgbww
|
||||
? html`
|
||||
<ha-labeled-slider
|
||||
.caption=${this.hass.localize(
|
||||
"ui.card.light.cold_white_value"
|
||||
)}
|
||||
icon="hass:file-word-box-outline"
|
||||
max="100"
|
||||
.name=${"cw"}
|
||||
.value=${this._cwSliderValue}
|
||||
@change=${this._wvSliderChanged}
|
||||
pin
|
||||
></ha-labeled-slider>
|
||||
<ha-labeled-slider
|
||||
.caption=${this.hass.localize(
|
||||
"ui.card.light.warm_white_value"
|
||||
)}
|
||||
icon="hass:file-word-box"
|
||||
max="100"
|
||||
.name=${"ww"}
|
||||
.value=${this._wwSliderValue}
|
||||
@change=${this._wvSliderChanged}
|
||||
pin
|
||||
></ha-labeled-slider>
|
||||
`
|
||||
: nothing}
|
||||
`
|
||||
: nothing}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
public _updateSliderValues() {
|
||||
const stateObj = this.stateObj;
|
||||
|
||||
if (stateObj?.state === "on") {
|
||||
if (stateObj.state === "on") {
|
||||
this._brightnessAdjusted = undefined;
|
||||
if (
|
||||
stateObj.attributes.color_mode === LightColorMode.RGB &&
|
||||
@ -242,10 +181,6 @@ class LightColorPicker extends LitElement {
|
||||
this._brightnessAdjusted = maxVal;
|
||||
}
|
||||
}
|
||||
this._ctPickerValue =
|
||||
stateObj.attributes.color_mode === LightColorMode.COLOR_TEMP
|
||||
? stateObj.attributes.color_temp_kelvin
|
||||
: undefined;
|
||||
|
||||
this._wvSliderValue =
|
||||
stateObj.attributes.color_mode === LightColorMode.RGBW &&
|
||||
@ -273,8 +208,7 @@ class LightColorPicker extends LitElement {
|
||||
? rgb2hs(currentRgbColor.slice(0, 3) as [number, number, number])
|
||||
: undefined;
|
||||
} else {
|
||||
this._hsPickerValue = [0, 0];
|
||||
this._ctPickerValue = undefined;
|
||||
this._hsPickerValue = undefined;
|
||||
this._wvSliderValue = undefined;
|
||||
this._cwSliderValue = undefined;
|
||||
this._wwSliderValue = undefined;
|
||||
@ -288,43 +222,9 @@ class LightColorPicker extends LitElement {
|
||||
return;
|
||||
}
|
||||
|
||||
if (changedProps.has("entityId")) {
|
||||
const supportsTemp = lightSupportsColorMode(
|
||||
this.stateObj!,
|
||||
LightColorMode.COLOR_TEMP
|
||||
);
|
||||
|
||||
const supportsColor = lightSupportsColor(this.stateObj!);
|
||||
|
||||
const modes: LightPickerMode[] = [];
|
||||
if (supportsColor) {
|
||||
modes.push("color");
|
||||
}
|
||||
if (supportsTemp) {
|
||||
modes.push("color_temp");
|
||||
}
|
||||
|
||||
this._modes = modes;
|
||||
this._mode =
|
||||
this.defaultMode ??
|
||||
(this.stateObj!.attributes.color_mode
|
||||
? this.stateObj!.attributes.color_mode === LightColorMode.COLOR_TEMP
|
||||
? LightColorMode.COLOR_TEMP
|
||||
: "color"
|
||||
: this._modes[0]);
|
||||
}
|
||||
|
||||
this._updateSliderValues();
|
||||
}
|
||||
|
||||
private _handleTabChanged(ev: CustomEvent): void {
|
||||
const newMode = this._modes[ev.detail.index];
|
||||
if (newMode === this._mode) {
|
||||
return;
|
||||
}
|
||||
this._mode = newMode;
|
||||
}
|
||||
|
||||
private _hsColorCursorMoved(ev: CustomEvent) {
|
||||
if (!ev.detail.value) {
|
||||
return;
|
||||
@ -404,40 +304,6 @@ class LightColorPicker extends LitElement {
|
||||
this._updateColor();
|
||||
}
|
||||
|
||||
private _ctColorCursorMoved(ev: CustomEvent) {
|
||||
const ct = ev.detail.value;
|
||||
|
||||
if (isNaN(ct) || this._ctPickerValue === ct) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._ctPickerValue = ct;
|
||||
|
||||
this._throttleUpdateColorTemp();
|
||||
}
|
||||
|
||||
private _throttleUpdateColorTemp = throttle(() => {
|
||||
this._updateColorTemp();
|
||||
}, 500);
|
||||
|
||||
private _ctColorChanged(ev: CustomEvent) {
|
||||
const ct = ev.detail.value;
|
||||
|
||||
if (isNaN(ct) || this._ctPickerValue === ct) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._ctPickerValue = ct;
|
||||
|
||||
this._updateColorTemp();
|
||||
}
|
||||
|
||||
private _updateColorTemp() {
|
||||
const color_temp_kelvin = this._ctPickerValue!;
|
||||
|
||||
this._applyColor({ color_temp_kelvin });
|
||||
}
|
||||
|
||||
private _wvSliderChanged(ev: CustomEvent) {
|
||||
const target = ev.target as any;
|
||||
let wv = Number(target.value);
|
||||
@ -574,19 +440,12 @@ class LightColorPicker extends LitElement {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 24px;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.native-color-picker {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.native-color-picker ha-svg-icon {
|
||||
@ -639,37 +498,18 @@ class LightColorPicker extends LitElement {
|
||||
|
||||
.color-container {
|
||||
position: relative;
|
||||
max-width: 300px;
|
||||
min-width: 200px;
|
||||
margin: 0 0 44px 0;
|
||||
padding-top: 44px;
|
||||
}
|
||||
|
||||
ha-hs-color-picker {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
ha-temp-color-picker {
|
||||
max-width: 300px;
|
||||
min-width: 200px;
|
||||
margin: 20px 0 44px 0;
|
||||
height: 45vh;
|
||||
max-height: 320px;
|
||||
min-height: 200px;
|
||||
}
|
||||
|
||||
ha-labeled-slider {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.color-temp-value {
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-size: 16px;
|
||||
height: 24px;
|
||||
line-height: 24px;
|
||||
letter-spacing: 0.1px;
|
||||
margin: 0;
|
||||
direction: ltr;
|
||||
}
|
||||
|
||||
hr {
|
||||
border-color: var(--divider-color);
|
||||
border-bottom: none;
|
||||
@ -682,6 +522,6 @@ class LightColorPicker extends LitElement {
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"light-color-picker": LightColorPicker;
|
||||
"light-color-rgb-picker": LightRgbColorPicker;
|
||||
}
|
||||
}
|
@ -0,0 +1,146 @@
|
||||
import {
|
||||
css,
|
||||
CSSResultGroup,
|
||||
html,
|
||||
LitElement,
|
||||
nothing,
|
||||
PropertyValues,
|
||||
} from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import { throttle } from "../../../../common/util/throttle";
|
||||
import "../../../../components/ha-temp-color-picker";
|
||||
import {
|
||||
LightColor,
|
||||
LightColorMode,
|
||||
LightEntity,
|
||||
} from "../../../../data/light";
|
||||
import { HomeAssistant } from "../../../../types";
|
||||
|
||||
declare global {
|
||||
interface HASSDomEvents {
|
||||
"color-changed": LightColor;
|
||||
"color-hovered": LightColor | undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@customElement("light-color-temp-picker")
|
||||
class LightColorTempPicker extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public stateObj!: LightEntity;
|
||||
|
||||
@state() private _ctPickerValue?: number;
|
||||
|
||||
protected render() {
|
||||
if (!this.stateObj) {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
return html`
|
||||
<ha-temp-color-picker
|
||||
@value-changed=${this._ctColorChanged}
|
||||
@cursor-moved=${this._ctColorCursorMoved}
|
||||
.min=${this.stateObj.attributes.min_color_temp_kelvin!}
|
||||
.max=${this.stateObj.attributes.max_color_temp_kelvin!}
|
||||
.value=${this._ctPickerValue}
|
||||
>
|
||||
</ha-temp-color-picker>
|
||||
`;
|
||||
}
|
||||
|
||||
public _updateSliderValues() {
|
||||
const stateObj = this.stateObj;
|
||||
|
||||
if (stateObj.state === "on") {
|
||||
this._ctPickerValue =
|
||||
stateObj.attributes.color_mode === LightColorMode.COLOR_TEMP
|
||||
? stateObj.attributes.color_temp_kelvin
|
||||
: undefined;
|
||||
} else {
|
||||
this._ctPickerValue = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
public willUpdate(changedProps: PropertyValues) {
|
||||
super.willUpdate(changedProps);
|
||||
|
||||
if (!changedProps.has("stateObj")) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._updateSliderValues();
|
||||
}
|
||||
|
||||
private _ctColorCursorMoved(ev: CustomEvent) {
|
||||
const ct = ev.detail.value;
|
||||
|
||||
if (isNaN(ct) || this._ctPickerValue === ct) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._ctPickerValue = ct;
|
||||
|
||||
fireEvent(this, "color-hovered", {
|
||||
color_temp_kelvin: ct,
|
||||
});
|
||||
|
||||
this._throttleUpdateColorTemp();
|
||||
}
|
||||
|
||||
private _throttleUpdateColorTemp = throttle(() => {
|
||||
this._updateColorTemp();
|
||||
}, 500);
|
||||
|
||||
private _ctColorChanged(ev: CustomEvent) {
|
||||
const ct = ev.detail.value;
|
||||
|
||||
fireEvent(this, "color-hovered", undefined);
|
||||
|
||||
if (isNaN(ct) || this._ctPickerValue === ct) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._ctPickerValue = ct;
|
||||
|
||||
this._updateColorTemp();
|
||||
}
|
||||
|
||||
private _updateColorTemp() {
|
||||
const color_temp_kelvin = this._ctPickerValue!;
|
||||
|
||||
this._applyColor({ color_temp_kelvin });
|
||||
}
|
||||
|
||||
private _applyColor(color: LightColor, params?: Record<string, any>) {
|
||||
fireEvent(this, "color-changed", color);
|
||||
this.hass.callService("light", "turn_on", {
|
||||
entity_id: this.stateObj!.entity_id,
|
||||
...color,
|
||||
...params,
|
||||
});
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return [
|
||||
css`
|
||||
:host {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
ha-temp-color-picker {
|
||||
height: 45vh;
|
||||
max-height: 320px;
|
||||
min-height: 200px;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"light-color-temp-picker": LightColorTempPicker;
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import { ExtEntityRegistryEntry } from "../../../../data/entity_registry";
|
||||
import { LightColor } from "../../../../data/light";
|
||||
import type { LightPickerMode } from "./light-color-picker";
|
||||
import type { LightPickerMode } from "./dialog-light-color-favorite";
|
||||
|
||||
export interface LightColorFavoriteDialogParams {
|
||||
entry: ExtEntityRegistryEntry;
|
||||
|
@ -1,23 +0,0 @@
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import type { LightPickerMode } from "./light-color-picker";
|
||||
|
||||
export interface LightColorPickerViewParams {
|
||||
entityId: string;
|
||||
defaultMode: LightPickerMode;
|
||||
}
|
||||
|
||||
export const loadLightColorPickerView = () =>
|
||||
import("./ha-more-info-view-light-color-picker");
|
||||
|
||||
export const showLightColorPickerView = (
|
||||
element: HTMLElement,
|
||||
title: string,
|
||||
params: LightColorPickerViewParams
|
||||
): void => {
|
||||
fireEvent(element, "show-child-view", {
|
||||
viewTag: "ha-more-info-view-light-color-picker",
|
||||
viewImport: loadLightColorPickerView,
|
||||
viewTitle: title,
|
||||
viewParams: params,
|
||||
});
|
||||
};
|
@ -0,0 +1,199 @@
|
||||
import {
|
||||
css,
|
||||
CSSResultGroup,
|
||||
html,
|
||||
LitElement,
|
||||
PropertyValues,
|
||||
TemplateResult,
|
||||
} from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { styleMap } from "lit/directives/style-map";
|
||||
import { domainIcon } from "../../../../common/entity/domain_icon";
|
||||
import { stateColorCss } from "../../../../common/entity/state_color";
|
||||
import "../../../../components/ha-control-button";
|
||||
import "../../../../components/ha-control-switch";
|
||||
import { UNAVAILABLE, UNKNOWN } from "../../../../data/entity";
|
||||
import { forwardHaptic } from "../../../../data/haptics";
|
||||
import { LockEntity } from "../../../../data/lock";
|
||||
import { HomeAssistant } from "../../../../types";
|
||||
import { showEnterCodeDialogDialog } from "../../../enter-code/show-enter-code-dialog";
|
||||
|
||||
@customElement("ha-more-info-lock-toggle")
|
||||
export class HaMoreInfoLockToggle extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public stateObj!: LockEntity;
|
||||
|
||||
@state() private _isOn = false;
|
||||
|
||||
public willUpdate(changedProps: PropertyValues): void {
|
||||
super.willUpdate(changedProps);
|
||||
if (changedProps.has("stateObj")) {
|
||||
this._isOn =
|
||||
this.stateObj.state === "locked" || this.stateObj.state === "locking";
|
||||
}
|
||||
}
|
||||
|
||||
private _valueChanged(ev) {
|
||||
const checked = ev.target.checked as boolean;
|
||||
|
||||
if (checked) {
|
||||
this._turnOn();
|
||||
} else {
|
||||
this._turnOff();
|
||||
}
|
||||
}
|
||||
|
||||
private async _turnOn() {
|
||||
this._isOn = true;
|
||||
try {
|
||||
await this._callService(true);
|
||||
} catch (err) {
|
||||
this._isOn = false;
|
||||
}
|
||||
}
|
||||
|
||||
private async _turnOff() {
|
||||
this._isOn = false;
|
||||
try {
|
||||
await this._callService(false);
|
||||
} catch (err) {
|
||||
this._isOn = true;
|
||||
}
|
||||
}
|
||||
|
||||
private async _callService(turnOn: boolean): Promise<void> {
|
||||
if (!this.hass || !this.stateObj) {
|
||||
return;
|
||||
}
|
||||
forwardHaptic("light");
|
||||
|
||||
let code: string | undefined;
|
||||
|
||||
if (this.stateObj.attributes.code_format) {
|
||||
const response = await showEnterCodeDialogDialog(this, {
|
||||
codeFormat: "text",
|
||||
codePattern: this.stateObj.attributes.code_format,
|
||||
title: this.hass.localize(
|
||||
`ui.dialogs.more_info_control.lock.${turnOn ? "lock" : "unlock"}`
|
||||
),
|
||||
submitText: this.hass.localize(
|
||||
`ui.dialogs.more_info_control.lock.${turnOn ? "lock" : "unlock"}`
|
||||
),
|
||||
});
|
||||
if (response == null) {
|
||||
throw new Error("cancel");
|
||||
}
|
||||
code = response;
|
||||
}
|
||||
|
||||
await this.hass.callService("lock", turnOn ? "lock" : "unlock", {
|
||||
entity_id: this.stateObj.entity_id,
|
||||
code,
|
||||
});
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
const locking = this.stateObj.state === "locking";
|
||||
const unlocking = this.stateObj.state === "unlocking";
|
||||
|
||||
const color = stateColorCss(this.stateObj);
|
||||
|
||||
const onIcon = domainIcon(
|
||||
"lock",
|
||||
this.stateObj,
|
||||
locking ? "locking" : "locked"
|
||||
);
|
||||
|
||||
const offIcon = domainIcon(
|
||||
"lock",
|
||||
this.stateObj,
|
||||
unlocking ? "unlocking" : "unlocked"
|
||||
);
|
||||
|
||||
if (this.stateObj.state === UNKNOWN) {
|
||||
return html`
|
||||
<div class="buttons">
|
||||
<ha-control-button
|
||||
.label=${this.hass.localize(
|
||||
"ui.dialogs.more_info_control.lock.lock"
|
||||
)}
|
||||
@click=${this._turnOn}
|
||||
>
|
||||
<ha-svg-icon .path=${onIcon}></ha-svg-icon>
|
||||
</ha-control-button>
|
||||
<ha-control-button
|
||||
.label=${this.hass.localize(
|
||||
"ui.dialogs.more_info_control.lock.unlock"
|
||||
)}
|
||||
@click=${this._turnOff}
|
||||
>
|
||||
<ha-svg-icon .path=${offIcon}></ha-svg-icon>
|
||||
</ha-control-button>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
return html`
|
||||
<ha-control-switch
|
||||
.pathOn=${onIcon}
|
||||
.pathOff=${offIcon}
|
||||
vertical
|
||||
reversed
|
||||
.checked=${this._isOn}
|
||||
@change=${this._valueChanged}
|
||||
.ariaLabel=${this.hass.localize("ui.dialogs.more_info_control.toggle")}
|
||||
style=${styleMap({
|
||||
"--control-switch-on-color": color,
|
||||
"--control-switch-off-color": color,
|
||||
})}
|
||||
.disabled=${this.stateObj.state === UNAVAILABLE}
|
||||
>
|
||||
</ha-control-switch>
|
||||
`;
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return css`
|
||||
ha-control-switch {
|
||||
height: 45vh;
|
||||
max-height: 320px;
|
||||
min-height: 200px;
|
||||
--control-switch-thickness: 100px;
|
||||
--control-switch-border-radius: 24px;
|
||||
--control-switch-padding: 6px;
|
||||
--mdc-icon-size: 24px;
|
||||
}
|
||||
.buttons {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100px;
|
||||
height: 45vh;
|
||||
max-height: 320px;
|
||||
min-height: 200px;
|
||||
padding: 6px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
ha-control-button {
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
--control-button-border-radius: 18px;
|
||||
--mdc-icon-size: 24px;
|
||||
}
|
||||
ha-control-button.active {
|
||||
--control-button-icon-color: white;
|
||||
--control-button-background-color: var(--color);
|
||||
--control-button-background-opacity: 1;
|
||||
}
|
||||
ha-control-button:not(:last-child) {
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-more-info-lock-toggle": HaMoreInfoLockToggle;
|
||||
}
|
||||
}
|
@ -22,6 +22,7 @@ export const DOMAINS_WITH_NEW_MORE_INFO = [
|
||||
"fan",
|
||||
"input_boolean",
|
||||
"light",
|
||||
"lock",
|
||||
"siren",
|
||||
"switch",
|
||||
];
|
||||
|
@ -7,8 +7,8 @@ import { stateColorCss } from "../../../common/entity/state_color";
|
||||
import "../../../components/ha-outlined-button";
|
||||
import { AlarmControlPanelEntity } from "../../../data/alarm_control_panel";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import { showEnterCodeDialogDialog } from "../../enter-code/show-enter-code-dialog";
|
||||
import "../components/alarm_control_panel/ha-more-info-alarm_control_panel-modes";
|
||||
import { showEnterCodeDialogDialog } from "../components/alarm_control_panel/show-enter-code-dialog";
|
||||
import { moreInfoControlStyle } from "../components/ha-more-info-control-style";
|
||||
import "../components/ha-more-info-state-header";
|
||||
|
||||
|
@ -11,6 +11,8 @@ import { customElement, property, state } from "lit/decorators";
|
||||
import { computeStateDisplay } from "../../../common/entity/compute_state_display";
|
||||
import { supportsFeature } from "../../../common/entity/supports-feature";
|
||||
import "../../../components/ha-attributes";
|
||||
import "../../../components/ha-icon-button-group";
|
||||
import "../../../components/ha-icon-button-toggle";
|
||||
import {
|
||||
computeCoverPositionStateDisplay,
|
||||
CoverEntity,
|
||||
@ -24,6 +26,8 @@ import "../components/cover/ha-more-info-cover-toggle";
|
||||
import { moreInfoControlStyle } from "../components/ha-more-info-control-style";
|
||||
import "../components/ha-more-info-state-header";
|
||||
|
||||
type Mode = "position" | "button";
|
||||
|
||||
@customElement("more-info-cover")
|
||||
class MoreInfoCover extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
@ -34,10 +38,10 @@ class MoreInfoCover extends LitElement {
|
||||
|
||||
@state() private _liveTilt?: number;
|
||||
|
||||
@state() private _mode?: "position" | "button";
|
||||
@state() private _mode?: Mode;
|
||||
|
||||
private _toggleMode() {
|
||||
this._mode = this._mode === "position" ? "button" : "position";
|
||||
private _setMode(ev) {
|
||||
this._mode = ev.currentTarget.mode;
|
||||
}
|
||||
|
||||
private _positionSliderMoved(ev) {
|
||||
@ -192,19 +196,26 @@ class MoreInfoCover extends LitElement {
|
||||
(supportsPosition || supportsTiltPosition) &&
|
||||
(supportsOpenClose || supportsTilt)
|
||||
? html`
|
||||
<div class="actions">
|
||||
<ha-icon-button
|
||||
<ha-icon-button-group>
|
||||
<ha-icon-button-toggle
|
||||
.label=${this.hass.localize(
|
||||
`ui.dialogs.more_info_control.cover.switch_mode.${
|
||||
this._mode === "position" ? "button" : "position"
|
||||
}`
|
||||
`ui.dialogs.more_info_control.cover.switch_mode.position`
|
||||
)}
|
||||
.path=${this._mode === "position"
|
||||
? mdiSwapVertical
|
||||
: mdiMenu}
|
||||
@click=${this._toggleMode}
|
||||
></ha-icon-button>
|
||||
</div>
|
||||
.selected=${this._mode === "position"}
|
||||
.path=${mdiMenu}
|
||||
.mode=${"position"}
|
||||
@click=${this._setMode}
|
||||
></ha-icon-button-toggle>
|
||||
<ha-icon-button-toggle
|
||||
.label=${this.hass.localize(
|
||||
`ui.dialogs.more_info_control.cover.switch_mode.button`
|
||||
)}
|
||||
.selected=${this._mode === "button"}
|
||||
.path=${mdiSwapVertical}
|
||||
.mode=${"button"}
|
||||
@click=${this._setMode}
|
||||
></ha-icon-button-toggle>
|
||||
</ha-icon-button-group>
|
||||
`
|
||||
: nothing
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
import "@material/mwc-list/mwc-list-item";
|
||||
import {
|
||||
mdiBrightness6,
|
||||
mdiCreation,
|
||||
mdiFileWordBox,
|
||||
mdiLightbulb,
|
||||
@ -24,6 +25,8 @@ import { supportsFeature } from "../../../common/entity/supports-feature";
|
||||
import { blankBeforePercent } from "../../../common/translations/blank_before_percent";
|
||||
import "../../../components/ha-attributes";
|
||||
import "../../../components/ha-button-menu";
|
||||
import "../../../components/ha-icon-button-group";
|
||||
import "../../../components/ha-icon-button-toggle";
|
||||
import "../../../components/ha-outlined-button";
|
||||
import "../../../components/ha-outlined-icon-button";
|
||||
import "../../../components/ha-select";
|
||||
@ -31,6 +34,7 @@ import { UNAVAILABLE } from "../../../data/entity";
|
||||
import { ExtEntityRegistryEntry } from "../../../data/entity_registry";
|
||||
import { forwardHaptic } from "../../../data/haptics";
|
||||
import {
|
||||
formatTempColor,
|
||||
LightColorMode,
|
||||
LightEntity,
|
||||
LightEntityFeature,
|
||||
@ -46,7 +50,10 @@ import "../components/ha-more-info-toggle";
|
||||
import "../components/lights/ha-favorite-color-button";
|
||||
import "../components/lights/ha-more-info-light-brightness";
|
||||
import "../components/lights/ha-more-info-light-favorite-colors";
|
||||
import { showLightColorPickerView } from "../components/lights/show-view-light-color-picker";
|
||||
import "../components/lights/light-color-rgb-picker";
|
||||
import "../components/lights/light-color-temp-picker";
|
||||
|
||||
type MainControl = "brightness" | "color_temp" | "color";
|
||||
|
||||
@customElement("more-info-light")
|
||||
class MoreInfoLight extends LitElement {
|
||||
@ -62,12 +69,24 @@ class MoreInfoLight extends LitElement {
|
||||
|
||||
@state() private _selectedBrightness?: number;
|
||||
|
||||
@state() private _colorTempPreview?: number;
|
||||
|
||||
@state() private _mainControl: MainControl = "brightness";
|
||||
|
||||
private _brightnessChanged(ev) {
|
||||
const value = (ev.detail as any).value;
|
||||
if (isNaN(value)) return;
|
||||
this._selectedBrightness = value;
|
||||
}
|
||||
|
||||
private _tempColorHovered(ev: CustomEvent<HASSDomEvents["color-hovered"]>) {
|
||||
if (ev.detail && "color_temp_kelvin" in ev.detail) {
|
||||
this._colorTempPreview = ev.detail.color_temp_kelvin;
|
||||
} else {
|
||||
this._colorTempPreview = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
protected updated(changedProps: PropertyValues<typeof this>): void {
|
||||
if (changedProps.has("stateObj")) {
|
||||
this._selectedBrightness = this.stateObj?.attributes.brightness
|
||||
@ -77,6 +96,28 @@ class MoreInfoLight extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
private _setMainControl(ev: any) {
|
||||
ev.stopPropagation();
|
||||
this._mainControl = ev.currentTarget.control;
|
||||
}
|
||||
|
||||
private _resetMainControl(ev: any) {
|
||||
ev.stopPropagation();
|
||||
this._mainControl = "brightness";
|
||||
}
|
||||
|
||||
private get _stateOverride() {
|
||||
if (this._colorTempPreview) {
|
||||
return formatTempColor(this._colorTempPreview);
|
||||
}
|
||||
if (this._selectedBrightness) {
|
||||
return `${Math.round(this._selectedBrightness)}${blankBeforePercent(
|
||||
this.hass!.locale
|
||||
)}%`;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
protected render() {
|
||||
if (!this.hass || !this.stateObj) {
|
||||
return nothing;
|
||||
@ -106,47 +147,60 @@ class MoreInfoLight extends LitElement {
|
||||
(this.entry.options?.light?.favorite_colors == null ||
|
||||
this.entry.options.light.favorite_colors.length > 0);
|
||||
|
||||
const stateOverride = this._selectedBrightness
|
||||
? `${Math.round(this._selectedBrightness)}${blankBeforePercent(
|
||||
this.hass!.locale
|
||||
)}%`
|
||||
: undefined;
|
||||
|
||||
return html`
|
||||
<ha-more-info-state-header
|
||||
.hass=${this.hass}
|
||||
.stateObj=${this.stateObj}
|
||||
.stateOverride=${stateOverride}
|
||||
.stateOverride=${this._stateOverride}
|
||||
></ha-more-info-state-header>
|
||||
<div class="controls">
|
||||
${supportsBrightness
|
||||
${!supportsBrightness
|
||||
? html`
|
||||
<ha-more-info-light-brightness
|
||||
.stateObj=${this.stateObj}
|
||||
.hass=${this.hass}
|
||||
@slider-moved=${this._brightnessChanged}
|
||||
>
|
||||
</ha-more-info-light-brightness>
|
||||
`
|
||||
: html`
|
||||
<ha-more-info-toggle
|
||||
.stateObj=${this.stateObj}
|
||||
.hass=${this.hass}
|
||||
.iconPathOn=${mdiLightbulb}
|
||||
.iconPathOff=${mdiLightbulbOff}
|
||||
></ha-more-info-toggle>
|
||||
`}
|
||||
`
|
||||
: nothing}
|
||||
${supportsColorTemp || supportsColor || supportsBrightness
|
||||
? html`
|
||||
<div class="button-bar">
|
||||
${supportsBrightness && this._mainControl === "brightness"
|
||||
? html`
|
||||
<ha-more-info-light-brightness
|
||||
.stateObj=${this.stateObj}
|
||||
.hass=${this.hass}
|
||||
@slider-moved=${this._brightnessChanged}
|
||||
>
|
||||
</ha-more-info-light-brightness>
|
||||
`
|
||||
: nothing}
|
||||
${supportsColor && this._mainControl === "color"
|
||||
? html`
|
||||
<light-color-rgb-picker
|
||||
.hass=${this.hass}
|
||||
.stateObj=${this.stateObj}
|
||||
>
|
||||
</light-color-rgb-picker>
|
||||
`
|
||||
: nothing}
|
||||
${supportsColorTemp && this._mainControl === "color_temp"
|
||||
? html`
|
||||
<light-color-temp-picker
|
||||
.hass=${this.hass}
|
||||
.stateObj=${this.stateObj}
|
||||
@color-hovered=${this._tempColorHovered}
|
||||
>
|
||||
</light-color-temp-picker>
|
||||
`
|
||||
: nothing}
|
||||
<ha-icon-button-group>
|
||||
${supportsBrightness
|
||||
? html`
|
||||
<ha-icon-button
|
||||
.disabled=${this.stateObj!.state === UNAVAILABLE}
|
||||
.title=${this.hass.localize(
|
||||
"ui.dialogs.more_info_control.light.toggle"
|
||||
)}
|
||||
.ariaLabel=${this.hass.localize(
|
||||
.label=${this.hass.localize(
|
||||
"ui.dialogs.more_info_control.light.toggle"
|
||||
)}
|
||||
@click=${this._toggle}
|
||||
@ -155,49 +209,60 @@ class MoreInfoLight extends LitElement {
|
||||
</ha-icon-button>
|
||||
`
|
||||
: nothing}
|
||||
${supportsColor || supportsColorTemp
|
||||
? html`
|
||||
<div class="separator"></div>
|
||||
<ha-icon-button-toggle
|
||||
.selected=${this._mainControl === "brightness"}
|
||||
.disabled=${this.stateObj!.state === UNAVAILABLE}
|
||||
.label=${this.hass.localize(
|
||||
"ui.dialogs.more_info_control.light.brightness"
|
||||
)}
|
||||
.control=${"brightness"}
|
||||
@click=${this._setMainControl}
|
||||
>
|
||||
<ha-svg-icon .path=${mdiBrightness6}></ha-svg-icon>
|
||||
</ha-icon-button-toggle>
|
||||
`
|
||||
: nothing}
|
||||
${supportsColor
|
||||
? html`
|
||||
<ha-icon-button
|
||||
class="color-mode"
|
||||
<ha-icon-button-toggle
|
||||
border-only
|
||||
.selected=${this._mainControl === "color"}
|
||||
.disabled=${this.stateObj!.state === UNAVAILABLE}
|
||||
.title=${this.hass.localize(
|
||||
"ui.dialogs.more_info_control.light.change_color"
|
||||
.label=${this.hass.localize(
|
||||
"ui.dialogs.more_info_control.light.color"
|
||||
)}
|
||||
.ariaLabel=${this.hass.localize(
|
||||
"ui.dialogs.more_info_control.light.change_color"
|
||||
)}
|
||||
.mode=${"color"}
|
||||
@click=${this._showLightColorPickerView}
|
||||
.control=${"color"}
|
||||
@click=${this._setMainControl}
|
||||
>
|
||||
<span class="wheel color"></span>
|
||||
</ha-icon-button>
|
||||
</ha-icon-button-toggle>
|
||||
`
|
||||
: nothing}
|
||||
${supportsColorTemp
|
||||
? html`
|
||||
<ha-icon-button
|
||||
<ha-icon-button-toggle
|
||||
border-only
|
||||
.selected=${this._mainControl === "color_temp"}
|
||||
.disabled=${this.stateObj!.state === UNAVAILABLE}
|
||||
.title=${this.hass.localize(
|
||||
"ui.dialogs.more_info_control.light.change_color_temp"
|
||||
.label=${this.hass.localize(
|
||||
"ui.dialogs.more_info_control.light.color_temp"
|
||||
)}
|
||||
.ariaLabel=${this.hass.localize(
|
||||
"ui.dialogs.more_info_control.light.change_color_temp"
|
||||
)}
|
||||
.mode=${"color_temp"}
|
||||
@click=${this._showLightColorPickerView}
|
||||
.control=${"color_temp"}
|
||||
@click=${this._setMainControl}
|
||||
>
|
||||
<span class="wheel color-temp"></span>
|
||||
</ha-icon-button>
|
||||
</ha-icon-button-toggle>
|
||||
`
|
||||
: nothing}
|
||||
${supportsWhite
|
||||
? html`
|
||||
<div class="separator"></div>
|
||||
<ha-icon-button
|
||||
.disabled=${this.stateObj!.state === UNAVAILABLE}
|
||||
.title=${this.hass.localize(
|
||||
"ui.dialogs.more_info_control.light.set_white"
|
||||
)}
|
||||
.ariaLabel=${this.hass.localize(
|
||||
.label=${this.hass.localize(
|
||||
"ui.dialogs.more_info_control.light.set_white"
|
||||
)}
|
||||
@click=${this._setWhite}
|
||||
@ -206,7 +271,7 @@ class MoreInfoLight extends LitElement {
|
||||
</ha-icon-button>
|
||||
`
|
||||
: nothing}
|
||||
</div>
|
||||
</ha-icon-button-group>
|
||||
${this.entry &&
|
||||
lightSupportsFavoriteColors(this.stateObj) &&
|
||||
(this.editMode || hasFavoriteColors)
|
||||
@ -216,6 +281,7 @@ class MoreInfoLight extends LitElement {
|
||||
.stateObj=${this.stateObj}
|
||||
.entry=${this.entry}
|
||||
.editMode=${this.editMode}
|
||||
@favorite-color-edit-started=${this._resetMainControl}
|
||||
>
|
||||
</ha-more-info-light-favorite-colors>
|
||||
`
|
||||
@ -291,19 +357,6 @@ class MoreInfoLight extends LitElement {
|
||||
});
|
||||
};
|
||||
|
||||
private _showLightColorPickerView = (ev) => {
|
||||
showLightColorPickerView(
|
||||
this,
|
||||
this.hass.localize(
|
||||
"ui.dialogs.more_info_control.light.color_picker.title"
|
||||
),
|
||||
{
|
||||
entityId: this.stateObj!.entity_id,
|
||||
defaultMode: ev.currentTarget.mode,
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
private _setWhite = () => {
|
||||
this.hass.callService("light", "turn_on", {
|
||||
entity_id: this.stateObj!.entity_id,
|
||||
@ -347,9 +400,6 @@ class MoreInfoLight extends LitElement {
|
||||
flex: none;
|
||||
border-radius: 15px;
|
||||
}
|
||||
ha-icon-button[disabled] .wheel {
|
||||
filter: grayscale(1) opacity(0.5);
|
||||
}
|
||||
.wheel.color {
|
||||
background-image: url("/static/images/color_wheel.png");
|
||||
background-size: cover;
|
||||
@ -362,6 +412,9 @@ class MoreInfoLight extends LitElement {
|
||||
rgb(255, 160, 0) 100%
|
||||
);
|
||||
}
|
||||
*[disabled] .wheel {
|
||||
filter: grayscale(1) opacity(0.5);
|
||||
}
|
||||
.buttons {
|
||||
flex-wrap: wrap;
|
||||
max-width: 250px;
|
||||
|
@ -1,43 +1,156 @@
|
||||
import "@material/mwc-button";
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, query } from "lit/decorators";
|
||||
import "@material/web/iconbutton/outlined-icon-button";
|
||||
import { mdiDoorOpen, mdiLock, mdiLockOff } from "@mdi/js";
|
||||
import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { styleMap } from "lit/directives/style-map";
|
||||
import { domainIcon } from "../../../common/entity/domain_icon";
|
||||
import { stateColorCss } from "../../../common/entity/state_color";
|
||||
import { supportsFeature } from "../../../common/entity/supports-feature";
|
||||
import "../../../components/ha-attributes";
|
||||
import "../../../components/ha-textfield";
|
||||
import type { HaTextField } from "../../../components/ha-textfield";
|
||||
import { UNAVAILABLE } from "../../../data/entity";
|
||||
import { LockEntity, LockEntityFeature } from "../../../data/lock";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import { showEnterCodeDialogDialog } from "../../enter-code/show-enter-code-dialog";
|
||||
import { moreInfoControlStyle } from "../components/ha-more-info-control-style";
|
||||
import "../components/ha-more-info-state-header";
|
||||
import "../components/lock/ha-more-info-lock-toggle";
|
||||
|
||||
@customElement("more-info-lock")
|
||||
class MoreInfoLock extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public stateObj?: HassEntity;
|
||||
@property({ attribute: false }) public stateObj?: LockEntity;
|
||||
|
||||
@query("ha-textfield") private _textfield?: HaTextField;
|
||||
private async _open() {
|
||||
this._callService("open");
|
||||
}
|
||||
|
||||
private async _lock() {
|
||||
this._callService("lock");
|
||||
}
|
||||
|
||||
private async _unlock() {
|
||||
this._callService("unlock");
|
||||
}
|
||||
|
||||
private async _callService(service: "open" | "lock" | "unlock") {
|
||||
let code: string | undefined;
|
||||
|
||||
if (this.stateObj!.attributes.code_format) {
|
||||
const response = await showEnterCodeDialogDialog(this, {
|
||||
codeFormat: "text",
|
||||
codePattern: this.stateObj!.attributes.code_format,
|
||||
title: this.hass.localize(
|
||||
`ui.dialogs.more_info_control.lock.${service}`
|
||||
),
|
||||
submitText: this.hass.localize(
|
||||
`ui.dialogs.more_info_control.lock.${service}`
|
||||
),
|
||||
});
|
||||
if (!response) {
|
||||
return;
|
||||
}
|
||||
code = response;
|
||||
}
|
||||
|
||||
this.hass.callService("lock", service, {
|
||||
entity_id: this.stateObj!.entity_id,
|
||||
code,
|
||||
});
|
||||
}
|
||||
|
||||
protected render() {
|
||||
if (!this.hass || !this.stateObj) {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
const supportsOpen = supportsFeature(this.stateObj, LockEntityFeature.OPEN);
|
||||
|
||||
const color = stateColorCss(this.stateObj);
|
||||
const style = {
|
||||
"--icon-color": color,
|
||||
};
|
||||
|
||||
const isJammed = this.stateObj.state === "jammed";
|
||||
|
||||
return html`
|
||||
${this.stateObj.attributes.code_format
|
||||
? html`<div class="code">
|
||||
<ha-textfield
|
||||
.label=${this.hass.localize("ui.card.lock.code")}
|
||||
.pattern=${this.stateObj.attributes.code_format}
|
||||
type="password"
|
||||
></ha-textfield>
|
||||
${this.stateObj.state === "locked"
|
||||
? html`<mwc-button
|
||||
@click=${this._callService}
|
||||
data-service="unlock"
|
||||
>${this.hass.localize("ui.card.lock.unlock")}</mwc-button
|
||||
>`
|
||||
: html`<mwc-button @click=${this._callService} data-service="lock"
|
||||
>${this.hass.localize("ui.card.lock.lock")}</mwc-button
|
||||
>`}
|
||||
</div>`
|
||||
: ""}
|
||||
<ha-more-info-state-header
|
||||
.hass=${this.hass}
|
||||
.stateObj=${this.stateObj}
|
||||
></ha-more-info-state-header>
|
||||
<div class="controls" style=${styleMap(style)}>
|
||||
${
|
||||
this.stateObj.state === "jammed"
|
||||
? html`
|
||||
<div class="status">
|
||||
<span></span>
|
||||
<div class="icon">
|
||||
<ha-svg-icon
|
||||
.path=${domainIcon("lock", this.stateObj)}
|
||||
></ha-svg-icon>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
: html`
|
||||
<ha-more-info-lock-toggle
|
||||
.stateObj=${this.stateObj}
|
||||
.hass=${this.hass}
|
||||
>
|
||||
</ha-more-info-lock-toggle>
|
||||
`
|
||||
}
|
||||
${
|
||||
supportsOpen || isJammed
|
||||
? html`
|
||||
<div class="buttons">
|
||||
${supportsOpen
|
||||
? html`
|
||||
<md-outlined-icon-button
|
||||
.disabled=${this.stateObj.state === UNAVAILABLE}
|
||||
.title=${this.hass.localize(
|
||||
"ui.dialogs.more_info_control.lock.open"
|
||||
)}
|
||||
.ariaLabel=${this.hass.localize(
|
||||
"ui.dialogs.more_info_control.lock.open"
|
||||
)}
|
||||
@click=${this._open}
|
||||
>
|
||||
<ha-svg-icon .path=${mdiDoorOpen}></ha-svg-icon>
|
||||
</md-outlined-icon-button>
|
||||
`
|
||||
: nothing}
|
||||
${isJammed
|
||||
? html`
|
||||
<md-outlined-icon-button
|
||||
.title=${this.hass.localize(
|
||||
"ui.dialogs.more_info_control.lock.lock"
|
||||
)}
|
||||
.ariaLabel=${this.hass.localize(
|
||||
"ui.dialogs.more_info_control.lock.lock"
|
||||
)}
|
||||
@click=${this._lock}
|
||||
>
|
||||
<ha-svg-icon .path=${mdiLock}></ha-svg-icon>
|
||||
</md-outlined-icon-button>
|
||||
<md-outlined-icon-button
|
||||
.title=${this.hass.localize(
|
||||
"ui.dialogs.more_info_control.lock.unlock"
|
||||
)}
|
||||
.ariaLabel=${this.hass.localize(
|
||||
"ui.dialogs.more_info_control.lock.unlock"
|
||||
)}
|
||||
@click=${this._unlock}
|
||||
>
|
||||
<ha-svg-icon .path=${mdiLockOff}></ha-svg-icon>
|
||||
</md-outlined-icon-button>
|
||||
`
|
||||
: nothing}
|
||||
</div>
|
||||
`
|
||||
: nothing
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<ha-attributes
|
||||
.hass=${this.hass}
|
||||
.stateObj=${this.stateObj}
|
||||
@ -46,33 +159,64 @@ class MoreInfoLock extends LitElement {
|
||||
`;
|
||||
}
|
||||
|
||||
private _callService(ev) {
|
||||
const service = ev.target.getAttribute("data-service");
|
||||
const data = {
|
||||
entity_id: this.stateObj!.entity_id,
|
||||
code: this._textfield?.value,
|
||||
};
|
||||
this.hass.callService("lock", service, data);
|
||||
static get styles(): CSSResultGroup {
|
||||
return [
|
||||
moreInfoControlStyle,
|
||||
css`
|
||||
md-outlined-icon-button {
|
||||
--ha-icon-display: block;
|
||||
--md-sys-color-on-surface: var(--secondary-text-color);
|
||||
--md-sys-color-on-surface-variant: var(--secondary-text-color);
|
||||
--md-sys-color-on-surface-rgb: var(--rgb-secondary-text-color);
|
||||
--md-sys-color-outline: var(--secondary-text-color);
|
||||
}
|
||||
@keyframes pulse {
|
||||
0% {
|
||||
opacity: 1;
|
||||
}
|
||||
50% {
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
.status {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
height: 45vh;
|
||||
max-height: 320px;
|
||||
min-height: 200px;
|
||||
}
|
||||
.status .icon {
|
||||
position: relative;
|
||||
--mdc-icon-size: 80px;
|
||||
animation: pulse 1s infinite;
|
||||
color: var(--icon-color);
|
||||
border-radius: 50%;
|
||||
width: 144px;
|
||||
height: 144px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.status .icon::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
border-radius: 50%;
|
||||
background-color: var(--icon-color);
|
||||
transition: background-color 180ms ease-in-out;
|
||||
opacity: 0.2;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
static styles = css`
|
||||
:host {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
}
|
||||
.code {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
margin-bottom: 8px;
|
||||
width: 100%;
|
||||
}
|
||||
ha-attributes {
|
||||
width: 100%;
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
declare global {
|
||||
|
@ -24,10 +24,10 @@ import { stopPropagation } from "../../common/dom/stop_propagation";
|
||||
import "../../components/ha-button";
|
||||
import "../../components/ha-button-menu";
|
||||
import "../../components/ha-dialog";
|
||||
import "../../components/ha-dialog-header";
|
||||
import "../../components/ha-icon-button";
|
||||
import "../../components/ha-list-item";
|
||||
import "../../components/ha-textfield";
|
||||
import "../../components/ha-dialog-header";
|
||||
import type { HaTextField } from "../../components/ha-textfield";
|
||||
import {
|
||||
AssistPipeline,
|
||||
@ -41,6 +41,7 @@ import type { HomeAssistant } from "../../types";
|
||||
import { AudioRecorder } from "../../util/audio-recorder";
|
||||
import { documentationUrl } from "../../util/documentation-url";
|
||||
import { showAlertDialog } from "../generic/show-dialog-box";
|
||||
import { VoiceCommandDialogParams } from "./show-ha-voice-command-dialog";
|
||||
|
||||
interface Message {
|
||||
who: string;
|
||||
@ -82,7 +83,13 @@ export class HaVoiceCommandDialog extends LitElement {
|
||||
|
||||
private _stt_binary_handler_id?: number | null;
|
||||
|
||||
public async showDialog(): Promise<void> {
|
||||
private _pipelinePromise?: Promise<AssistPipeline>;
|
||||
|
||||
public async showDialog(params?: VoiceCommandDialogParams): Promise<void> {
|
||||
if (params?.pipeline_id) {
|
||||
this._pipelineId = params?.pipeline_id;
|
||||
}
|
||||
|
||||
this._conversation = [
|
||||
{
|
||||
who: "hass",
|
||||
@ -92,6 +99,11 @@ export class HaVoiceCommandDialog extends LitElement {
|
||||
this._opened = true;
|
||||
await this.updateComplete;
|
||||
this._scrollMessagesBottom();
|
||||
|
||||
await this._pipelinePromise;
|
||||
if (params?.start_listening && this._pipeline?.stt_engine) {
|
||||
this._toggleListening();
|
||||
}
|
||||
}
|
||||
|
||||
public async closeDialog(): Promise<void> {
|
||||
@ -230,7 +242,7 @@ export class HaVoiceCommandDialog extends LitElement {
|
||||
<div class="listening-icon">
|
||||
<ha-icon-button
|
||||
.path=${mdiMicrophone}
|
||||
@click=${this._toggleListening}
|
||||
@click=${this._handleListeningButton}
|
||||
.label=${this.hass.localize(
|
||||
"ui.dialogs.voice_command.start_listening"
|
||||
)}
|
||||
@ -275,7 +287,8 @@ export class HaVoiceCommandDialog extends LitElement {
|
||||
|
||||
private async _getPipeline() {
|
||||
try {
|
||||
this._pipeline = await getAssistPipeline(this.hass, this._pipelineId);
|
||||
this._pipelinePromise = getAssistPipeline(this.hass, this._pipelineId);
|
||||
this._pipeline = await this._pipelinePromise;
|
||||
} catch (e: any) {
|
||||
if (e.code === "not_found") {
|
||||
this._pipelineId = undefined;
|
||||
@ -392,9 +405,13 @@ export class HaVoiceCommandDialog extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
private _toggleListening(ev) {
|
||||
private _handleListeningButton(ev) {
|
||||
ev.stopPropagation();
|
||||
ev.preventDefault();
|
||||
this._toggleListening();
|
||||
}
|
||||
|
||||
private _toggleListening() {
|
||||
const supportsMicrophone = AudioRecorder.isSupported;
|
||||
if (!supportsMicrophone) {
|
||||
this._showNotSupportedMessage();
|
||||
|
@ -3,19 +3,29 @@ import { HomeAssistant } from "../../types";
|
||||
|
||||
const loadVoiceCommandDialog = () => import("./ha-voice-command-dialog");
|
||||
|
||||
export interface VoiceCommandDialogParams {
|
||||
pipeline_id?: string;
|
||||
start_listening?: boolean;
|
||||
}
|
||||
|
||||
export const showVoiceCommandDialog = (
|
||||
element: HTMLElement,
|
||||
hass: HomeAssistant
|
||||
hass: HomeAssistant,
|
||||
dialogParams?: VoiceCommandDialogParams
|
||||
): void => {
|
||||
if (hass.auth.external?.config.hasAssist) {
|
||||
hass.auth.external!.fireMessage({
|
||||
type: "assist/show",
|
||||
payload: {
|
||||
pipeline_id: dialogParams?.pipeline_id,
|
||||
start_listening: dialogParams?.start_listening,
|
||||
},
|
||||
});
|
||||
return;
|
||||
}
|
||||
fireEvent(element, "show-dialog", {
|
||||
dialogTag: "ha-voice-command-dialog",
|
||||
dialogImport: loadVoiceCommandDialog,
|
||||
dialogParams: {},
|
||||
dialogParams,
|
||||
});
|
||||
};
|
||||
|
@ -133,7 +133,15 @@ window.hassConnection.then(({ conn }) => {
|
||||
});
|
||||
|
||||
window.addEventListener("error", (e) => {
|
||||
if (!__DEV__ && e.message === "ResizeObserver loop limit exceeded") {
|
||||
if (
|
||||
!__DEV__ &&
|
||||
typeof e.message === "string" &&
|
||||
(e.message.includes("ResizeObserver loop limit exceeded") ||
|
||||
e.message.includes(
|
||||
"ResizeObserver loop completed with undelivered notifications"
|
||||
))
|
||||
) {
|
||||
e.preventDefault();
|
||||
e.stopImmediatePropagation();
|
||||
e.stopPropagation();
|
||||
return;
|
||||
|
@ -131,7 +131,7 @@ export class ExternalAuth extends Auth {
|
||||
export const createExternalAuth = async (hassUrl: string) => {
|
||||
const auth = new ExternalAuth(hassUrl);
|
||||
if (
|
||||
(window.externalApp && window.externalApp.externalBus) ||
|
||||
window.externalApp?.externalBus ||
|
||||
(window.webkit && window.webkit.messageHandlers.externalBus)
|
||||
) {
|
||||
auth.external = new ExternalMessaging();
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { ActionDetail } from "@material/mwc-list/mwc-list-foundation";
|
||||
import "@material/mwc-list/mwc-list-item";
|
||||
import {
|
||||
mdiAlertCircleCheck,
|
||||
mdiCheck,
|
||||
mdiContentDuplicate,
|
||||
mdiContentCopy,
|
||||
@ -14,7 +15,14 @@ import {
|
||||
mdiStopCircleOutline,
|
||||
} from "@mdi/js";
|
||||
import { UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||
import { css, CSSResultGroup, html, LitElement, PropertyValues } from "lit";
|
||||
import {
|
||||
css,
|
||||
CSSResultGroup,
|
||||
html,
|
||||
LitElement,
|
||||
nothing,
|
||||
PropertyValues,
|
||||
} from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { classMap } from "lit/directives/class-map";
|
||||
import { dynamicElement } from "../../../../common/dom/dynamic-element-directive";
|
||||
@ -34,7 +42,11 @@ import {
|
||||
subscribeEntityRegistry,
|
||||
} from "../../../../data/entity_registry";
|
||||
import { Clipboard } from "../../../../data/automation";
|
||||
import { Action, getActionType } from "../../../../data/script";
|
||||
import {
|
||||
Action,
|
||||
NonConditionAction,
|
||||
getActionType,
|
||||
} from "../../../../data/script";
|
||||
import { describeAction } from "../../../../data/script_i18n";
|
||||
import { callExecuteScript } from "../../../../data/service";
|
||||
import {
|
||||
@ -184,6 +196,17 @@ export default class HaAutomationActionRow extends LitElement {
|
||||
</h3>
|
||||
|
||||
<slot name="icons" slot="icons"></slot>
|
||||
${type !== "condition" &&
|
||||
(this.action as NonConditionAction).continue_on_error === true
|
||||
? html`<div slot="icons">
|
||||
<ha-svg-icon .path=${mdiAlertCircleCheck}></ha-svg-icon>
|
||||
<simple-tooltip animation-delay="0">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.actions.continue_on_error"
|
||||
)}
|
||||
</simple-tooltip>
|
||||
</div> `
|
||||
: nothing}
|
||||
${this.hideMenu
|
||||
? ""
|
||||
: html`
|
||||
|
@ -1,7 +1,10 @@
|
||||
import { css, CSSResultGroup, html, LitElement, PropertyValues } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { assert } from "superstruct";
|
||||
import { fireEvent } from "../../../../../common/dom/fire_event";
|
||||
import { computeDomain } from "../../../../../common/entity/compute_domain";
|
||||
import { computeObjectId } from "../../../../../common/entity/compute_object_id";
|
||||
import { hasTemplate } from "../../../../../common/string/has-template";
|
||||
import "../../../../../components/ha-service-control";
|
||||
import { ServiceAction, serviceActionStruct } from "../../../../../data/script";
|
||||
@ -20,6 +23,26 @@ export class HaServiceAction extends LitElement implements ActionElement {
|
||||
|
||||
@state() private _action!: ServiceAction;
|
||||
|
||||
private _fields = memoizeOne(
|
||||
(
|
||||
serviceDomains: HomeAssistant["services"],
|
||||
domainService: string | undefined
|
||||
): { fields: any } => {
|
||||
if (!domainService) {
|
||||
return { fields: {} };
|
||||
}
|
||||
const domain = computeDomain(domainService);
|
||||
const service = computeObjectId(domainService);
|
||||
if (!(domain in serviceDomains)) {
|
||||
return { fields: {} };
|
||||
}
|
||||
if (!(service in serviceDomains[domain])) {
|
||||
return { fields: {} };
|
||||
}
|
||||
return { fields: serviceDomains[domain][service].fields };
|
||||
}
|
||||
);
|
||||
|
||||
public static get defaultConfig() {
|
||||
return { service: "", data: {} };
|
||||
}
|
||||
@ -34,7 +57,28 @@ export class HaServiceAction extends LitElement implements ActionElement {
|
||||
fireEvent(this, "ui-mode-not-available", err);
|
||||
return;
|
||||
}
|
||||
if (this.action && hasTemplate(this.action)) {
|
||||
|
||||
const fields = this._fields(
|
||||
this.hass.services,
|
||||
this.action?.service
|
||||
).fields;
|
||||
if (
|
||||
this.action &&
|
||||
(Object.entries(this.action).some(
|
||||
([key, val]) => key !== "data" && hasTemplate(val)
|
||||
) ||
|
||||
(this.action.data &&
|
||||
Object.entries(this.action.data).some(([key, val]) => {
|
||||
const field = fields[key];
|
||||
if (
|
||||
field?.selector &&
|
||||
("template" in field.selector || "object" in field.selector)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
return hasTemplate(val);
|
||||
})))
|
||||
) {
|
||||
fireEvent(
|
||||
this,
|
||||
"ui-mode-not-available",
|
||||
|
@ -1,9 +1,11 @@
|
||||
import "@material/mwc-list/mwc-list-item";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { fireEvent } from "../../../../../common/dom/fire_event";
|
||||
import { ensureArray } from "../../../../../common/array/ensure-array";
|
||||
import type { SchemaUnion } from "../../../../../components/ha-form/types";
|
||||
import "../../../../../components/ha-select";
|
||||
import type {
|
||||
AutomationConfig,
|
||||
@ -30,6 +32,22 @@ export class HaTriggerCondition extends LitElement {
|
||||
};
|
||||
}
|
||||
|
||||
private _schema = memoizeOne(
|
||||
(triggers: Trigger[]) =>
|
||||
[
|
||||
{
|
||||
name: "id",
|
||||
selector: {
|
||||
select: {
|
||||
multiple: true,
|
||||
options: triggers.map((trigger) => trigger.id!),
|
||||
},
|
||||
},
|
||||
required: true,
|
||||
},
|
||||
] as const
|
||||
);
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
const details = { callback: (config) => this._automationUpdated(config) };
|
||||
@ -45,30 +63,33 @@ export class HaTriggerCondition extends LitElement {
|
||||
}
|
||||
|
||||
protected render() {
|
||||
const { id } = this.condition;
|
||||
|
||||
if (!this._triggers.length) {
|
||||
return this.hass.localize(
|
||||
"ui.panel.config.automation.editor.conditions.type.trigger.no_triggers"
|
||||
);
|
||||
}
|
||||
return html`<ha-select
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.conditions.type.trigger.id"
|
||||
)}
|
||||
.value=${id}
|
||||
.disabled=${this.disabled}
|
||||
@selected=${this._triggerPicked}
|
||||
>
|
||||
${this._triggers.map(
|
||||
(trigger) =>
|
||||
html`
|
||||
<mwc-list-item .value=${trigger.id}> ${trigger.id} </mwc-list-item>
|
||||
`
|
||||
)}
|
||||
</ha-select>`;
|
||||
|
||||
const schema = this._schema(this._triggers);
|
||||
|
||||
return html`
|
||||
<ha-form
|
||||
.schema=${schema}
|
||||
.data=${this.condition}
|
||||
.hass=${this.hass}
|
||||
.disabled=${this.disabled}
|
||||
.computeLabel=${this._computeLabelCallback}
|
||||
@value-changed=${this._valueChanged}
|
||||
></ha-form>
|
||||
`;
|
||||
}
|
||||
|
||||
private _computeLabelCallback = (
|
||||
schema: SchemaUnion<ReturnType<typeof this._schema>>
|
||||
): string =>
|
||||
this.hass.localize(
|
||||
`ui.panel.config.automation.editor.conditions.type.trigger.${schema.name}`
|
||||
);
|
||||
|
||||
private _automationUpdated(config?: AutomationConfig) {
|
||||
const seenIds = new Set();
|
||||
this._triggers = config?.trigger
|
||||
@ -78,18 +99,24 @@ export class HaTriggerCondition extends LitElement {
|
||||
: [];
|
||||
}
|
||||
|
||||
private _triggerPicked(ev) {
|
||||
private _valueChanged(ev: CustomEvent): void {
|
||||
ev.stopPropagation();
|
||||
if (!ev.target.value) {
|
||||
return;
|
||||
const newValue = ev.detail.value;
|
||||
|
||||
if (typeof newValue.id === "string") {
|
||||
if (!this._triggers.some((trigger) => trigger.id === newValue.id)) {
|
||||
newValue.id = "";
|
||||
}
|
||||
} else if (Array.isArray(newValue.id)) {
|
||||
newValue.id = newValue.id.filter((id) =>
|
||||
this._triggers.some((trigger) => trigger.id === id)
|
||||
);
|
||||
if (!newValue.id.length) {
|
||||
newValue.id = "";
|
||||
}
|
||||
}
|
||||
const newTrigger = ev.target.value;
|
||||
if (this.condition.id === newTrigger) {
|
||||
return;
|
||||
}
|
||||
fireEvent(this, "value-changed", {
|
||||
value: { ...this.condition, id: newTrigger },
|
||||
});
|
||||
|
||||
fireEvent(this, "value-changed", { value: newValue });
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -18,8 +18,10 @@ import "../../../components/ha-icon-next";
|
||||
import "../../../components/ha-list-item";
|
||||
import "../../../components/ha-tip";
|
||||
import { showAutomationEditor } from "../../../data/automation";
|
||||
import { showScriptEditor } from "../../../data/script";
|
||||
import {
|
||||
Blueprint,
|
||||
BlueprintDomain,
|
||||
Blueprints,
|
||||
BlueprintSourceType,
|
||||
fetchBlueprints,
|
||||
@ -29,6 +31,7 @@ import { HassDialog } from "../../../dialogs/make-dialog-manager";
|
||||
import { haStyle, haStyleDialog } from "../../../resources/styles";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import { documentationUrl } from "../../../util/documentation-url";
|
||||
import type { NewAutomationDialogParams } from "./show-dialog-new-automation";
|
||||
|
||||
const SOURCE_TYPE_ICONS: Record<BlueprintSourceType, string> = {
|
||||
local: mdiFile,
|
||||
@ -42,11 +45,15 @@ class DialogNewAutomation extends LitElement implements HassDialog {
|
||||
|
||||
@state() private _opened = false;
|
||||
|
||||
@state() private _mode: BlueprintDomain = "automation";
|
||||
|
||||
@state() public blueprints?: Blueprints;
|
||||
|
||||
public showDialog(): void {
|
||||
public showDialog(params: NewAutomationDialogParams): void {
|
||||
this._opened = true;
|
||||
fetchBlueprints(this.hass!, "automation").then((blueprints) => {
|
||||
this._mode = params?.mode || "automation";
|
||||
|
||||
fetchBlueprints(this.hass!, this._mode).then((blueprints) => {
|
||||
this.blueprints = blueprints;
|
||||
});
|
||||
}
|
||||
@ -92,14 +99,14 @@ class DialogNewAutomation extends LitElement implements HassDialog {
|
||||
@closed=${this.closeDialog}
|
||||
.heading=${createCloseHeading(
|
||||
this.hass,
|
||||
this.hass.localize("ui.panel.config.automation.dialog_new.header")
|
||||
this.hass.localize(`ui.panel.config.${this._mode}.dialog_new.header`)
|
||||
)}
|
||||
>
|
||||
<mwc-list
|
||||
innerRole="listbox"
|
||||
itemRoles="option"
|
||||
innerAriaLabel=${this.hass.localize(
|
||||
"ui.panel.config.automation.dialog_new.header"
|
||||
`ui.panel.config.${this._mode}.dialog_new.header`
|
||||
)}
|
||||
rootTabbable
|
||||
dialogInitialFocus
|
||||
@ -112,11 +119,11 @@ class DialogNewAutomation extends LitElement implements HassDialog {
|
||||
>
|
||||
<ha-svg-icon slot="graphic" .path=${mdiPencilOutline}></ha-svg-icon>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.dialog_new.create_empty"
|
||||
`ui.panel.config.${this._mode}.dialog_new.create_empty`
|
||||
)}
|
||||
<span slot="secondary">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.dialog_new.create_empty_description"
|
||||
`ui.panel.config.${this._mode}.dialog_new.create_empty_description`
|
||||
)}
|
||||
</span>
|
||||
<ha-icon-next slot="meta"></ha-icon-next>
|
||||
@ -139,11 +146,11 @@ class DialogNewAutomation extends LitElement implements HassDialog {
|
||||
<span slot="secondary">
|
||||
${blueprint.author
|
||||
? this.hass.localize(
|
||||
`ui.panel.config.automation.dialog_new.blueprint_source.author`,
|
||||
`ui.panel.config.${this._mode}.dialog_new.blueprint_source.author`,
|
||||
{ author: blueprint.author }
|
||||
)
|
||||
: this.hass.localize(
|
||||
`ui.panel.config.automation.dialog_new.blueprint_source.${blueprint.sourceType}`
|
||||
`ui.panel.config.${this._mode}.dialog_new.blueprint_source.${blueprint.sourceType}`
|
||||
)}
|
||||
</span>
|
||||
<ha-icon-next slot="meta"></ha-icon-next>
|
||||
@ -161,11 +168,11 @@ class DialogNewAutomation extends LitElement implements HassDialog {
|
||||
<ha-list-item hasmeta twoline graphic="icon">
|
||||
<ha-svg-icon slot="graphic" .path=${mdiWeb}></ha-svg-icon>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.dialog_new.create_blueprint"
|
||||
`ui.panel.config.${this._mode}.dialog_new.create_blueprint`
|
||||
)}
|
||||
<span slot="secondary">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.dialog_new.create_blueprint_description"
|
||||
`ui.panel.config.${this._mode}.dialog_new.create_blueprint_description`
|
||||
)}
|
||||
</span>
|
||||
<ha-svg-icon slot="meta" path=${mdiOpenInNew}></ha-svg-icon>
|
||||
@ -180,7 +187,7 @@ class DialogNewAutomation extends LitElement implements HassDialog {
|
||||
rel="noreferrer noopener"
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.dialog_new.discover_blueprint_tip"
|
||||
`ui.panel.config.${this._mode}.dialog_new.discover_blueprint_tip`
|
||||
)}
|
||||
</a>
|
||||
</ha-tip>
|
||||
@ -196,7 +203,11 @@ class DialogNewAutomation extends LitElement implements HassDialog {
|
||||
}
|
||||
const path = (ev.currentTarget! as any).path;
|
||||
this.closeDialog();
|
||||
showAutomationEditor({ use_blueprint: { path } });
|
||||
if (this._mode === "script") {
|
||||
showScriptEditor({ use_blueprint: { path } });
|
||||
} else {
|
||||
showAutomationEditor({ use_blueprint: { path } });
|
||||
}
|
||||
}
|
||||
|
||||
private async _blank(ev) {
|
||||
@ -204,7 +215,11 @@ class DialogNewAutomation extends LitElement implements HassDialog {
|
||||
return;
|
||||
}
|
||||
this.closeDialog();
|
||||
showAutomationEditor();
|
||||
if (this._mode === "script") {
|
||||
showScriptEditor();
|
||||
} else {
|
||||
showAutomationEditor();
|
||||
}
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
|
@ -486,7 +486,7 @@ class HaAutomationPicker extends LitElement {
|
||||
|
||||
private _createNew() {
|
||||
if (isComponentLoaded(this.hass, "blueprint")) {
|
||||
showNewAutomationDialog(this);
|
||||
showNewAutomationDialog(this, { mode: "automation" });
|
||||
} else {
|
||||
navigate("/config/automation/edit/new");
|
||||
}
|
||||
|
@ -1,11 +1,18 @@
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
|
||||
export interface NewAutomationDialogParams {
|
||||
mode: "script" | "automation";
|
||||
}
|
||||
|
||||
export const loadNewAutomationDialog = () => import("./dialog-new-automation");
|
||||
|
||||
export const showNewAutomationDialog = (element: HTMLElement): void => {
|
||||
export const showNewAutomationDialog = (
|
||||
element: HTMLElement,
|
||||
newAutomationDialogParams: NewAutomationDialogParams
|
||||
): void => {
|
||||
fireEvent(element, "show-dialog", {
|
||||
dialogTag: "ha-dialog-new-automation",
|
||||
dialogImport: loadNewAutomationDialog,
|
||||
dialogParams: {},
|
||||
dialogParams: newAutomationDialogParams,
|
||||
});
|
||||
};
|
||||
|
@ -160,14 +160,16 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
const useBlueprint = "use_blueprint" in this._config;
|
||||
|
||||
const schema = this._schema(
|
||||
!!this.scriptId,
|
||||
"use_blueprint" in this._config,
|
||||
useBlueprint,
|
||||
this._config.mode
|
||||
);
|
||||
|
||||
const data = {
|
||||
mode: MODES[0],
|
||||
...(!this._config.mode && !useBlueprint && { mode: MODES[0] }),
|
||||
icon: undefined,
|
||||
max: this._config.mode && isMaxMode(this._config.mode) ? 10 : undefined,
|
||||
...this._config,
|
||||
@ -332,7 +334,7 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
|
||||
</ha-card>
|
||||
</div>
|
||||
|
||||
${"use_blueprint" in this._config
|
||||
${useBlueprint
|
||||
? html`
|
||||
<blueprint-script-editor
|
||||
.hass=${this.hass}
|
||||
|
@ -12,6 +12,7 @@ import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { differenceInDays } from "date-fns/esm";
|
||||
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
|
||||
import { formatShortDateTime } from "../../../common/datetime/format_date_time";
|
||||
import { relativeTime } from "../../../common/datetime/relative_time";
|
||||
import { fireEvent, HASSDomEvent } from "../../../common/dom/fire_event";
|
||||
@ -44,6 +45,7 @@ import { HomeAssistant, Route } from "../../../types";
|
||||
import { documentationUrl } from "../../../util/documentation-url";
|
||||
import { showToast } from "../../../util/toast";
|
||||
import { configSections } from "../ha-panel-config";
|
||||
import { showNewAutomationDialog } from "../automation/show-dialog-new-automation";
|
||||
import { EntityRegistryEntry } from "../../../data/entity_registry";
|
||||
import { findRelated } from "../../../data/search";
|
||||
import { fetchBlueprints } from "../../../data/blueprint";
|
||||
@ -242,19 +244,19 @@ class HaScriptPicker extends LitElement {
|
||||
@related-changed=${this._relatedFilterChanged}
|
||||
>
|
||||
</ha-button-related-filter-menu>
|
||||
<a href="/config/script/edit/new" slot="fab">
|
||||
<ha-fab
|
||||
?is-wide=${this.isWide}
|
||||
?narrow=${this.narrow}
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.script.picker.add_script"
|
||||
)}
|
||||
extended
|
||||
?rtl=${computeRTL(this.hass)}
|
||||
>
|
||||
<ha-svg-icon slot="icon" .path=${mdiPlus}></ha-svg-icon>
|
||||
</ha-fab>
|
||||
</a>
|
||||
<ha-fab
|
||||
slot="fab"
|
||||
?is-wide=${this.isWide}
|
||||
?narrow=${this.narrow}
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.script.picker.add_script"
|
||||
)}
|
||||
extended
|
||||
?rtl=${computeRTL(this.hass)}
|
||||
@click=${this._createNew}
|
||||
>
|
||||
<ha-svg-icon slot="icon" .path=${mdiPlus}></ha-svg-icon>
|
||||
</ha-fab>
|
||||
</hass-tabs-subpage-data-table>
|
||||
`;
|
||||
}
|
||||
@ -312,6 +314,14 @@ class HaScriptPicker extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
private _createNew() {
|
||||
if (isComponentLoaded(this.hass, "blueprint")) {
|
||||
showNewAutomationDialog(this, { mode: "script" });
|
||||
} else {
|
||||
navigate("/config/script/edit/new");
|
||||
}
|
||||
}
|
||||
|
||||
private _runScript = async (script: any) => {
|
||||
const entry = this.entityRegistry.find(
|
||||
(e) => e.entity_id === script.entity_id
|
||||
|
@ -1,8 +1,10 @@
|
||||
import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { mdiHelpCircle } from "@mdi/js";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import "../../../components/ha-form/ha-form";
|
||||
import "../../../components/ha-icon-button";
|
||||
import { extractApiErrorMessage } from "../../../data/hassio/common";
|
||||
import {
|
||||
createSupervisorMount,
|
||||
@ -17,6 +19,8 @@ import { HomeAssistant } from "../../../types";
|
||||
import { MountViewDialogParams } from "./show-dialog-view-mount";
|
||||
import { LocalizeFunc } from "../../../common/translations/localize";
|
||||
import type { SchemaUnion } from "../../../components/ha-form/types";
|
||||
import { documentationUrl } from "../../../util/documentation-url";
|
||||
import { computeRTLDirection } from "../../../common/util/compute_rtl";
|
||||
|
||||
const mountSchema = memoizeOne(
|
||||
(
|
||||
@ -158,6 +162,33 @@ class ViewMountDialog extends LitElement {
|
||||
)}
|
||||
@closed=${this.closeDialog}
|
||||
>
|
||||
<ha-dialog-header slot="heading">
|
||||
<span slot="title"
|
||||
>${this._existing
|
||||
? this.hass.localize(
|
||||
"ui.panel.config.storage.network_mounts.update_title"
|
||||
)
|
||||
: this.hass.localize(
|
||||
"ui.panel.config.storage.network_mounts.add_title"
|
||||
)}
|
||||
</span>
|
||||
<a
|
||||
slot="actionItems"
|
||||
class="header_button"
|
||||
href=${documentationUrl(
|
||||
this.hass,
|
||||
"/common-tasks/os#network-storage"
|
||||
)}
|
||||
title=${this.hass.localize(
|
||||
"ui.panel.config.storage.network_mounts.documentation"
|
||||
)}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
dir=${computeRTLDirection(this.hass)}
|
||||
>
|
||||
<ha-icon-button .path=${mdiHelpCircle}></ha-icon-button>
|
||||
</a>
|
||||
</ha-dialog-header>
|
||||
${this._error
|
||||
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
|
||||
: nothing}
|
||||
@ -274,6 +305,9 @@ class ViewMountDialog extends LitElement {
|
||||
haStyle,
|
||||
haStyleDialog,
|
||||
css`
|
||||
ha-icon-button {
|
||||
color: var(--primary-text-color);
|
||||
}
|
||||
.delete-btn {
|
||||
--mdc-theme-primary: var(--error-color);
|
||||
}
|
||||
|
@ -346,7 +346,27 @@ class HaPanelDevService extends LitElement {
|
||||
}
|
||||
|
||||
private _checkUiSupported() {
|
||||
if (this._serviceData && hasTemplate(this._serviceData)) {
|
||||
const fields = this._fields(
|
||||
this.hass.services,
|
||||
this._serviceData?.service
|
||||
).fields;
|
||||
if (
|
||||
this._serviceData &&
|
||||
(Object.entries(this._serviceData).some(
|
||||
([key, val]) => key !== "data" && hasTemplate(val)
|
||||
) ||
|
||||
(this._serviceData.data &&
|
||||
Object.entries(this._serviceData.data).some(([key, val]) => {
|
||||
const field = fields.find((f) => f.key === key);
|
||||
if (
|
||||
field?.selector &&
|
||||
("template" in field.selector || "object" in field.selector)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
return hasTemplate(val);
|
||||
})))
|
||||
) {
|
||||
this._yamlMode = true;
|
||||
this._uiAvailable = false;
|
||||
} else {
|
||||
|
@ -141,6 +141,11 @@ export class EnergyStrategy {
|
||||
view_layout: { position: "sidebar" },
|
||||
collection_key: "energy_dashboard",
|
||||
});
|
||||
view.cards!.push({
|
||||
type: "energy-self-sufficiency-gauge",
|
||||
view_layout: { position: "sidebar" },
|
||||
collection_key: "energy_dashboard",
|
||||
});
|
||||
}
|
||||
|
||||
// Only include if we have a grid
|
||||
|
@ -190,6 +190,7 @@ class HaPanelHistory extends SubscribeMixin(LitElement) {
|
||||
<state-history-charts
|
||||
.hass=${this.hass}
|
||||
.historyData=${this._stateHistory}
|
||||
.startTime=${this._startDate}
|
||||
.endTime=${this._endDate}
|
||||
>
|
||||
</state-history-charts>
|
||||
|
@ -0,0 +1,257 @@
|
||||
import "@lrnwebcomponents/simple-tooltip/simple-tooltip";
|
||||
import { mdiInformation } from "@mdi/js";
|
||||
import { UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||
import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { styleMap } from "lit/directives/style-map";
|
||||
import "../../../../components/ha-card";
|
||||
import "../../../../components/ha-gauge";
|
||||
import "../../../../components/ha-svg-icon";
|
||||
import {
|
||||
EnergyData,
|
||||
energySourcesByType,
|
||||
getEnergyDataCollection,
|
||||
} from "../../../../data/energy";
|
||||
import { calculateStatisticsSumGrowth } from "../../../../data/recorder";
|
||||
import { SubscribeMixin } from "../../../../mixins/subscribe-mixin";
|
||||
import type { HomeAssistant } from "../../../../types";
|
||||
import type { LovelaceCard } from "../../types";
|
||||
import { severityMap } from "../hui-gauge-card";
|
||||
import type { EnergySelfSufficiencyGaugeCardConfig } from "../types";
|
||||
|
||||
@customElement("hui-energy-self-sufficiency-gauge-card")
|
||||
class HuiEnergySelfSufficiencyGaugeCard
|
||||
extends SubscribeMixin(LitElement)
|
||||
implements LovelaceCard
|
||||
{
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@state() private _config?: EnergySelfSufficiencyGaugeCardConfig;
|
||||
|
||||
@state() private _data?: EnergyData;
|
||||
|
||||
protected hassSubscribeRequiredHostProps = ["_config"];
|
||||
|
||||
public hassSubscribe(): UnsubscribeFunc[] {
|
||||
return [
|
||||
getEnergyDataCollection(this.hass!, {
|
||||
key: this._config?.collection_key,
|
||||
}).subscribe((data) => {
|
||||
this._data = data;
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
||||
public getCardSize(): number {
|
||||
return 4;
|
||||
}
|
||||
|
||||
public setConfig(config: EnergySelfSufficiencyGaugeCardConfig): void {
|
||||
this._config = config;
|
||||
}
|
||||
|
||||
protected render() {
|
||||
if (!this._config || !this.hass) {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
if (!this._data) {
|
||||
return html`${this.hass.localize(
|
||||
"ui.panel.lovelace.cards.energy.loading"
|
||||
)}`;
|
||||
}
|
||||
|
||||
const prefs = this._data.prefs;
|
||||
const types = energySourcesByType(prefs);
|
||||
|
||||
// The strategy only includes this card if we have a grid.
|
||||
const hasConsumption = true;
|
||||
|
||||
const hasSolarProduction = types.solar !== undefined;
|
||||
const hasBattery = types.battery !== undefined;
|
||||
const hasReturnToGrid = hasConsumption && types.grid![0].flow_to.length > 0;
|
||||
|
||||
const totalFromGrid =
|
||||
calculateStatisticsSumGrowth(
|
||||
this._data.stats,
|
||||
types.grid![0].flow_from.map((flow) => flow.stat_energy_from)
|
||||
) ?? 0;
|
||||
|
||||
let totalSolarProduction: number | null = null;
|
||||
|
||||
if (hasSolarProduction) {
|
||||
totalSolarProduction =
|
||||
calculateStatisticsSumGrowth(
|
||||
this._data.stats,
|
||||
types.solar!.map((source) => source.stat_energy_from)
|
||||
) || 0;
|
||||
}
|
||||
|
||||
let totalBatteryIn: number | null = null;
|
||||
let totalBatteryOut: number | null = null;
|
||||
|
||||
if (hasBattery) {
|
||||
totalBatteryIn =
|
||||
calculateStatisticsSumGrowth(
|
||||
this._data.stats,
|
||||
types.battery!.map((source) => source.stat_energy_to)
|
||||
) || 0;
|
||||
totalBatteryOut =
|
||||
calculateStatisticsSumGrowth(
|
||||
this._data.stats,
|
||||
types.battery!.map((source) => source.stat_energy_from)
|
||||
) || 0;
|
||||
}
|
||||
|
||||
let returnedToGrid: number | null = null;
|
||||
|
||||
if (hasReturnToGrid) {
|
||||
returnedToGrid =
|
||||
calculateStatisticsSumGrowth(
|
||||
this._data.stats,
|
||||
types.grid![0].flow_to.map((flow) => flow.stat_energy_to)
|
||||
) || 0;
|
||||
}
|
||||
|
||||
let solarConsumption: number | null = null;
|
||||
if (hasSolarProduction) {
|
||||
solarConsumption =
|
||||
(totalSolarProduction || 0) -
|
||||
(returnedToGrid || 0) -
|
||||
(totalBatteryIn || 0);
|
||||
}
|
||||
|
||||
let batteryFromGrid: null | number = null;
|
||||
let batteryToGrid: null | number = null;
|
||||
if (solarConsumption !== null && solarConsumption < 0) {
|
||||
// What we returned to the grid and what went in to the battery is more than produced,
|
||||
// so we have used grid energy to fill the battery
|
||||
// or returned battery energy to the grid
|
||||
if (hasBattery) {
|
||||
batteryFromGrid = solarConsumption * -1;
|
||||
if (batteryFromGrid > totalFromGrid) {
|
||||
batteryToGrid = Math.min(0, batteryFromGrid - totalFromGrid);
|
||||
batteryFromGrid = totalFromGrid;
|
||||
}
|
||||
}
|
||||
solarConsumption = 0;
|
||||
}
|
||||
|
||||
let batteryConsumption: number | null = null;
|
||||
if (hasBattery) {
|
||||
batteryConsumption = (totalBatteryOut || 0) - (batteryToGrid || 0);
|
||||
}
|
||||
|
||||
const gridConsumption = Math.max(0, totalFromGrid - (batteryFromGrid || 0));
|
||||
|
||||
const totalHomeConsumption = Math.max(
|
||||
0,
|
||||
gridConsumption + (solarConsumption || 0) + (batteryConsumption || 0)
|
||||
);
|
||||
|
||||
let value: number | undefined;
|
||||
if (
|
||||
totalFromGrid !== null &&
|
||||
totalHomeConsumption !== null &&
|
||||
totalHomeConsumption > 0
|
||||
) {
|
||||
value = (1 - totalFromGrid / totalHomeConsumption) * 100;
|
||||
}
|
||||
|
||||
return html`
|
||||
<ha-card>
|
||||
${value !== undefined
|
||||
? html`
|
||||
<ha-svg-icon id="info" .path=${mdiInformation}></ha-svg-icon>
|
||||
<simple-tooltip animation-delay="0" for="info" position="left">
|
||||
<span>
|
||||
${this.hass.localize(
|
||||
"ui.panel.lovelace.cards.energy.self_sufficiency_gauge.card_indicates_self_sufficiency_quota"
|
||||
)}
|
||||
</span>
|
||||
</simple-tooltip>
|
||||
<ha-gauge
|
||||
min="0"
|
||||
max="100"
|
||||
.value=${value}
|
||||
.locale=${this.hass.locale}
|
||||
label="%"
|
||||
style=${styleMap({
|
||||
"--gauge-color": this._computeSeverity(value),
|
||||
})}
|
||||
></ha-gauge>
|
||||
<div class="name">
|
||||
${this.hass.localize(
|
||||
"ui.panel.lovelace.cards.energy.self_sufficiency_gauge.self_sufficiency_quota"
|
||||
)}
|
||||
</div>
|
||||
`
|
||||
: this.hass.localize(
|
||||
"ui.panel.lovelace.cards.energy.self_sufficiency_gauge.self_sufficiency_could_not_calc"
|
||||
)}
|
||||
</ha-card>
|
||||
`;
|
||||
}
|
||||
|
||||
private _computeSeverity(numberValue: number): string {
|
||||
if (numberValue > 75) {
|
||||
return severityMap.green;
|
||||
}
|
||||
if (numberValue < 50) {
|
||||
return severityMap.yellow;
|
||||
}
|
||||
return severityMap.normal;
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return css`
|
||||
ha-card {
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
padding: 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
ha-gauge {
|
||||
width: 100%;
|
||||
max-width: 250px;
|
||||
direction: ltr;
|
||||
}
|
||||
|
||||
.name {
|
||||
text-align: center;
|
||||
line-height: initial;
|
||||
color: var(--primary-text-color);
|
||||
width: 100%;
|
||||
font-size: 15px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
ha-svg-icon {
|
||||
position: absolute;
|
||||
right: 4px;
|
||||
top: 4px;
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
simple-tooltip > span {
|
||||
font-size: 12px;
|
||||
line-height: 12px;
|
||||
}
|
||||
simple-tooltip {
|
||||
width: 80%;
|
||||
max-width: 250px;
|
||||
top: 8px !important;
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"hui-energy-self-sufficiency-gauge-card": HuiEnergySelfSufficiencyGaugeCard;
|
||||
}
|
||||
}
|
@ -68,10 +68,11 @@ class HuiEnergySolarGaugeCard
|
||||
return nothing;
|
||||
}
|
||||
|
||||
const totalSolarProduction = calculateStatisticsSumGrowth(
|
||||
this._data.stats,
|
||||
types.solar.map((source) => source.stat_energy_from)
|
||||
);
|
||||
const totalSolarProduction =
|
||||
calculateStatisticsSumGrowth(
|
||||
this._data.stats,
|
||||
types.solar.map((source) => source.stat_energy_from)
|
||||
) || 0;
|
||||
|
||||
const productionReturnedToGrid = calculateStatisticsSumGrowth(
|
||||
this._data.stats,
|
||||
|
@ -205,6 +205,7 @@ export class HuiHistoryGraphCard extends LitElement implements LovelaceCard {
|
||||
.historyData=${this._stateHistory}
|
||||
.names=${this._names}
|
||||
up-to-now
|
||||
.hoursToShow=${this._hoursToShow}
|
||||
.showNames=${this._config.show_names !== undefined
|
||||
? this._config.show_names
|
||||
: true}
|
||||
|
@ -2,6 +2,7 @@ import {
|
||||
mdiClockOutline,
|
||||
mdiFan,
|
||||
mdiFire,
|
||||
mdiHeatWave,
|
||||
mdiPower,
|
||||
mdiSnowflake,
|
||||
mdiWaterPercent,
|
||||
@ -21,6 +22,7 @@ export const CLIMATE_HVAC_ACTION_ICONS: Record<HvacAction, string> = {
|
||||
heating: mdiFire,
|
||||
idle: mdiClockOutline,
|
||||
off: mdiPower,
|
||||
preheating: mdiHeatWave,
|
||||
};
|
||||
|
||||
export const CLIMATE_HVAC_ACTION_MODE: Record<HvacAction, HvacMode> = {
|
||||
@ -30,6 +32,7 @@ export const CLIMATE_HVAC_ACTION_MODE: Record<HvacAction, HvacMode> = {
|
||||
heating: "heat",
|
||||
idle: "off",
|
||||
off: "off",
|
||||
preheating: "heat",
|
||||
};
|
||||
|
||||
export const computeClimateBadge: ComputeBadgeFunction = (stateObj) => {
|
||||
|
@ -159,6 +159,13 @@ export interface EnergySolarGaugeCardConfig extends LovelaceCardConfig {
|
||||
collection_key?: string;
|
||||
}
|
||||
|
||||
export interface EnergySelfSufficiencyGaugeCardConfig
|
||||
extends LovelaceCardConfig {
|
||||
type: "energy-self-sufficiency-gauge";
|
||||
title?: string;
|
||||
collection_key?: string;
|
||||
}
|
||||
|
||||
export interface EnergyGridGaugeCardConfig extends LovelaceCardConfig {
|
||||
type: "energy-grid-result-gauge";
|
||||
title?: string;
|
||||
|
@ -4,6 +4,7 @@ import { forwardHaptic } from "../../../data/haptics";
|
||||
import { domainToName } from "../../../data/integration";
|
||||
import { ActionConfig } from "../../../data/lovelace";
|
||||
import { showConfirmationDialog } from "../../../dialogs/generic/show-dialog-box";
|
||||
import { showVoiceCommandDialog } from "../../../dialogs/voice-command-dialog/show-ha-voice-command-dialog";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
import { showToast } from "../../../util/toast";
|
||||
import { toggleEntity } from "./entity/toggle-entity";
|
||||
@ -155,6 +156,13 @@ export const handleAction = async (
|
||||
forwardHaptic("light");
|
||||
break;
|
||||
}
|
||||
case "assist": {
|
||||
showVoiceCommandDialog(node, hass, {
|
||||
start_listening: actionConfig.start_listening,
|
||||
pipeline_id: actionConfig.pipeline_id,
|
||||
});
|
||||
break;
|
||||
}
|
||||
case "fire-dom-event": {
|
||||
fireEvent(node, "ll-custom", actionConfig);
|
||||
}
|
||||
|
@ -3,6 +3,8 @@ import { customElement, property } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import { stopPropagation } from "../../../common/dom/stop_propagation";
|
||||
import "../../../components/ha-assist-pipeline-picker";
|
||||
import { HaFormSchema, SchemaUnion } from "../../../components/ha-form/types";
|
||||
import "../../../components/ha-help-tooltip";
|
||||
import "../../../components/ha-navigation-picker";
|
||||
import "../../../components/ha-service-control";
|
||||
@ -24,9 +26,31 @@ const DEFAULT_ACTIONS: UiAction[] = [
|
||||
"navigate",
|
||||
"url",
|
||||
"call-service",
|
||||
"assist",
|
||||
"none",
|
||||
];
|
||||
|
||||
const ASSIST_SCHEMA = [
|
||||
{
|
||||
type: "grid",
|
||||
name: "",
|
||||
schema: [
|
||||
{
|
||||
name: "pipeline_id",
|
||||
selector: {
|
||||
assist_pipeline: {},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "start_listening",
|
||||
selector: {
|
||||
boolean: {},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
] as const satisfies readonly HaFormSchema[];
|
||||
|
||||
@customElement("hui-action-editor")
|
||||
export class HuiActionEditor extends LitElement {
|
||||
@property() public config?: ActionConfig;
|
||||
@ -101,7 +125,7 @@ export class HuiActionEditor extends LitElement {
|
||||
? html`
|
||||
<ha-help-tooltip .label=${this.tooltipText}></ha-help-tooltip>
|
||||
`
|
||||
: ""}
|
||||
: nothing}
|
||||
</div>
|
||||
${this.config?.action === "navigate"
|
||||
? html`
|
||||
@ -114,7 +138,7 @@ export class HuiActionEditor extends LitElement {
|
||||
@value-changed=${this._navigateValueChanged}
|
||||
></ha-navigation-picker>
|
||||
`
|
||||
: ""}
|
||||
: nothing}
|
||||
${this.config?.action === "url"
|
||||
? html`
|
||||
<ha-textfield
|
||||
@ -126,7 +150,7 @@ export class HuiActionEditor extends LitElement {
|
||||
@input=${this._valueChanged}
|
||||
></ha-textfield>
|
||||
`
|
||||
: ""}
|
||||
: nothing}
|
||||
${this.config?.action === "call-service"
|
||||
? html`
|
||||
<ha-service-control
|
||||
@ -137,7 +161,19 @@ export class HuiActionEditor extends LitElement {
|
||||
@value-changed=${this._serviceValueChanged}
|
||||
></ha-service-control>
|
||||
`
|
||||
: ""}
|
||||
: nothing}
|
||||
${this.config?.action === "assist"
|
||||
? html`
|
||||
<ha-form
|
||||
.hass=${this.hass}
|
||||
.schema=${ASSIST_SCHEMA}
|
||||
.data=${this.config}
|
||||
.computeLabel=${this._computeFormLabel}
|
||||
@value-changed=${this._formValueChanged}
|
||||
>
|
||||
</ha-form>
|
||||
`
|
||||
: nothing}
|
||||
`;
|
||||
}
|
||||
|
||||
@ -182,7 +218,7 @@ export class HuiActionEditor extends LitElement {
|
||||
return;
|
||||
}
|
||||
const target = ev.target! as EditorTarget;
|
||||
const value = ev.target.value;
|
||||
const value = ev.target.value ?? ev.target.checked;
|
||||
if (this[`_${target.configValue}`] === value) {
|
||||
return;
|
||||
}
|
||||
@ -193,6 +229,21 @@ export class HuiActionEditor extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
private _formValueChanged(ev): void {
|
||||
ev.stopPropagation();
|
||||
const value = ev.detail.value;
|
||||
|
||||
fireEvent(this, "value-changed", {
|
||||
value: value,
|
||||
});
|
||||
}
|
||||
|
||||
private _computeFormLabel(schema: SchemaUnion<typeof ASSIST_SCHEMA>) {
|
||||
return this.hass?.localize(
|
||||
`ui.panel.lovelace.editor.action-editor.${schema.name}`
|
||||
);
|
||||
}
|
||||
|
||||
private _serviceValueChanged(ev: CustomEvent) {
|
||||
ev.stopPropagation();
|
||||
const value = {
|
||||
@ -240,17 +291,25 @@ export class HuiActionEditor extends LitElement {
|
||||
width: 100%;
|
||||
}
|
||||
ha-service-control,
|
||||
ha-navigation-picker {
|
||||
ha-navigation-picker,
|
||||
ha-form {
|
||||
display: block;
|
||||
}
|
||||
ha-textfield,
|
||||
ha-service-control,
|
||||
ha-navigation-picker {
|
||||
ha-navigation-picker,
|
||||
ha-form {
|
||||
margin-top: 8px;
|
||||
}
|
||||
ha-service-control {
|
||||
--service-control-padding: 0;
|
||||
}
|
||||
ha-formfield {
|
||||
display: flex;
|
||||
height: 56px;
|
||||
align-items: center;
|
||||
--mdc-typography-body2-font-size: 1em;
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
@ -53,6 +53,8 @@ const LAZY_LOAD_TYPES = {
|
||||
import("../cards/energy/hui-energy-grid-neutrality-gauge-card"),
|
||||
"energy-solar-consumed-gauge": () =>
|
||||
import("../cards/energy/hui-energy-solar-consumed-gauge-card"),
|
||||
"energy-self-sufficiency-gauge": () =>
|
||||
import("../cards/energy/hui-energy-self-sufficiency-gauge-card"),
|
||||
"energy-solar-graph": () =>
|
||||
import("../cards/energy/hui-energy-solar-graph-card"),
|
||||
"energy-sources-table": () =>
|
||||
|
@ -51,6 +51,12 @@ const actionConfigStructNavigate = object({
|
||||
confirmation: optional(actionConfigStructConfirmation),
|
||||
});
|
||||
|
||||
const actionConfigStructAssist = type({
|
||||
action: literal("assist"),
|
||||
pipeline_id: optional(string()),
|
||||
start_listening: optional(boolean()),
|
||||
});
|
||||
|
||||
const actionConfigStructCustom = type({
|
||||
action: literal("fire-dom-event"),
|
||||
});
|
||||
@ -63,6 +69,7 @@ export const actionConfigStructType = object({
|
||||
"call-service",
|
||||
"url",
|
||||
"navigate",
|
||||
"assist",
|
||||
]),
|
||||
confirmation: optional(actionConfigStructConfirmation),
|
||||
});
|
||||
@ -82,6 +89,9 @@ export const actionConfigStruct = dynamic<any>((value) => {
|
||||
case "url": {
|
||||
return actionConfigStructUrl;
|
||||
}
|
||||
case "assist": {
|
||||
return actionConfigStructAssist;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -18,10 +18,10 @@ import {
|
||||
ALARM_MODES,
|
||||
} from "../../../data/alarm_control_panel";
|
||||
import { UNAVAILABLE } from "../../../data/entity";
|
||||
import { showEnterCodeDialogDialog } from "../../../dialogs/more-info/components/alarm_control_panel/show-enter-code-dialog";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
import { LovelaceTileFeature, LovelaceTileFeatureEditor } from "../types";
|
||||
import { AlarmModesFileFeatureConfig } from "./types";
|
||||
import { showEnterCodeDialogDialog } from "../../../dialogs/enter-code/show-enter-code-dialog";
|
||||
|
||||
export const supportsAlarmModesTileFeature = (stateObj: HassEntity) => {
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
|
@ -1,4 +1,5 @@
|
||||
import "@material/mwc-button";
|
||||
import { mdiContentCopy } from "@mdi/js";
|
||||
import {
|
||||
css,
|
||||
CSSResultGroup,
|
||||
@ -11,9 +12,13 @@ import { customElement, property, state } from "lit/decorators";
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import { createCloseHeading } from "../../components/ha-dialog";
|
||||
import "../../components/ha-textfield";
|
||||
import "../../components/ha-icon-button";
|
||||
import { haStyleDialog } from "../../resources/styles";
|
||||
import type { HomeAssistant } from "../../types";
|
||||
import { LongLivedAccessTokenDialogParams } from "./show-long-lived-access-token-dialog";
|
||||
import type { HaTextField } from "../../components/ha-textfield";
|
||||
import { copyToClipboard } from "../../common/util/copy-clipboard";
|
||||
import { showToast } from "../../util/toast";
|
||||
|
||||
const QR_LOGO_URL = "/static/icons/favicon-192x192.png";
|
||||
|
||||
@ -55,8 +60,15 @@ export class HaLongLivedAccessTokenDialog extends LitElement {
|
||||
"ui.panel.profile.long_lived_access_tokens.prompt_copy_token"
|
||||
)}
|
||||
type="text"
|
||||
iconTrailing
|
||||
readOnly
|
||||
></ha-textfield>
|
||||
>
|
||||
<ha-icon-button
|
||||
@click=${this._copyToken}
|
||||
slot="trailingIcon"
|
||||
.path=${mdiContentCopy}
|
||||
></ha-icon-button>
|
||||
</ha-textfield>
|
||||
<div id="qr">
|
||||
${this._qrCode
|
||||
? this._qrCode
|
||||
@ -71,6 +83,14 @@ export class HaLongLivedAccessTokenDialog extends LitElement {
|
||||
`;
|
||||
}
|
||||
|
||||
private async _copyToken(ev): Promise<void> {
|
||||
const textField = ev.target.parentElement as HaTextField;
|
||||
await copyToClipboard(textField.value);
|
||||
showToast(this, {
|
||||
message: this.hass.localize("ui.common.copied_clipboard"),
|
||||
});
|
||||
}
|
||||
|
||||
private async _generateQR() {
|
||||
const qrcode = await import("qrcode");
|
||||
const canvas = await qrcode.toCanvas(this._params!.token, {
|
||||
@ -111,6 +131,17 @@ export class HaLongLivedAccessTokenDialog extends LitElement {
|
||||
}
|
||||
ha-textfield {
|
||||
display: block;
|
||||
--textfield-icon-trailing-padding: 0;
|
||||
}
|
||||
ha-textfield > ha-icon-button {
|
||||
position: relative;
|
||||
right: -8px;
|
||||
--mdc-icon-button-size: 36px;
|
||||
--mdc-icon-size: 20px;
|
||||
color: var(--secondary-text-color);
|
||||
inset-inline-start: initial;
|
||||
inset-inline-end: -8px;
|
||||
direction: var(--direction);
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
@ -5,7 +5,7 @@ export const loadPolyfillIfNeeded = async () => {
|
||||
} catch (e) {
|
||||
window.ResizeObserver = (
|
||||
await import(
|
||||
"@lit-labs/virtualizer/polyfills/resize-observer-polyfill/ResizeObserver.js"
|
||||
"@lit-labs/virtualizer/polyfills/resize-observer-polyfill/ResizeObserver"
|
||||
)
|
||||
).default;
|
||||
}
|
||||
|
83
src/translations/en.json
Executable file → Normal file
83
src/translations/en.json
Executable file → Normal file
@ -396,7 +396,8 @@
|
||||
},
|
||||
"theme-picker": {
|
||||
"theme": "Theme",
|
||||
"no_theme": "No theme"
|
||||
"no_theme": "No theme",
|
||||
"default": "[%key:ui::panel::profile::themes::default%]"
|
||||
},
|
||||
"language-picker": {
|
||||
"language": "Language",
|
||||
@ -929,8 +930,8 @@
|
||||
"light": {
|
||||
"edit_mode": "Edit favorite colors",
|
||||
"toggle": "Toggle",
|
||||
"change_color": "Change color",
|
||||
"change_color_temp": "Change color temperature",
|
||||
"color": "Color",
|
||||
"color_temp": "Temperature",
|
||||
"set_white": "Set white",
|
||||
"select_effect": "Select effect",
|
||||
"brightness": "Brightness",
|
||||
@ -978,6 +979,11 @@
|
||||
"disarm_action": "Disarm",
|
||||
"arm_title": "Arm",
|
||||
"arm_action": "Arm"
|
||||
},
|
||||
"lock": {
|
||||
"open": "Open",
|
||||
"lock": "Lock",
|
||||
"unlock": "Unlock"
|
||||
}
|
||||
},
|
||||
"entity_registry": {
|
||||
@ -2286,7 +2292,10 @@
|
||||
"event_data": "Event data",
|
||||
"context_users": "Limit to events triggered by",
|
||||
"context_user_picked": "User firing event",
|
||||
"context_user_pick": "Select user"
|
||||
"context_user_pick": "Select user",
|
||||
"description": {
|
||||
"full": "When {eventTypes} event is fired"
|
||||
}
|
||||
},
|
||||
"geo_location": {
|
||||
"label": "Geolocation",
|
||||
@ -2308,12 +2317,19 @@
|
||||
"label": "Home Assistant",
|
||||
"event": "Event:",
|
||||
"start": "Start",
|
||||
"shutdown": "Shutdown"
|
||||
"shutdown": "Shutdown",
|
||||
"description": {
|
||||
"started": "When Home Assistant is started",
|
||||
"shutdown": "When Home Assistant is shutdown"
|
||||
}
|
||||
},
|
||||
"mqtt": {
|
||||
"label": "MQTT",
|
||||
"topic": "Topic",
|
||||
"payload": "Payload (optional)"
|
||||
"payload": "Payload (optional)",
|
||||
"description": {
|
||||
"full": "When an MQTT message has been received"
|
||||
}
|
||||
},
|
||||
"numeric_state": {
|
||||
"label": "Numeric state",
|
||||
@ -2341,22 +2357,35 @@
|
||||
"event": "[%key:ui::panel::config::automation::editor::triggers::type::homeassistant::event%]",
|
||||
"sunrise": "Sunrise",
|
||||
"sunset": "Sunset",
|
||||
"offset": "Offset (optional)"
|
||||
"offset": "Offset (optional)",
|
||||
"description": {
|
||||
"sets": "When the sun sets{hasDuration, select, \n true { offset by {duration}} \n other {}\n }",
|
||||
"rises": "When the sun rises{hasDuration, select, \n true { offset by {duration}} \n other {}\n }"
|
||||
}
|
||||
},
|
||||
"tag": {
|
||||
"label": "Tag"
|
||||
"label": "Tag",
|
||||
"description": {
|
||||
"full": "When a tag is scanned"
|
||||
}
|
||||
},
|
||||
"template": {
|
||||
"label": "Template",
|
||||
"value_template": "Value template",
|
||||
"for": "For"
|
||||
"for": "For",
|
||||
"description": {
|
||||
"full": "When a template triggers{hasDuration, select, \n true { for {duration}} \n other {}\n }"
|
||||
}
|
||||
},
|
||||
"time": {
|
||||
"type_value": "Fixed time",
|
||||
"type_input": "Value of a date/time helper or timestamp-class sensor",
|
||||
"label": "Time",
|
||||
"at": "At time",
|
||||
"mode": "Mode"
|
||||
"mode": "Mode",
|
||||
"description": {
|
||||
"full": "When the time is equal to {time}"
|
||||
}
|
||||
},
|
||||
"time_pattern": {
|
||||
"label": "Time Pattern",
|
||||
@ -2370,7 +2399,10 @@
|
||||
"local_only": "Only accessible from the local network",
|
||||
"webhook_id": "Webhook ID",
|
||||
"webhook_id_helper": "Treat this ID like a password: keep it secret, and make it hard to guess.",
|
||||
"webhook_settings": "Webhook Settings"
|
||||
"webhook_settings": "Webhook Settings",
|
||||
"description": {
|
||||
"full": "When a Webhook payload has been received"
|
||||
}
|
||||
},
|
||||
"zone": {
|
||||
"label": "Zone",
|
||||
@ -2507,6 +2539,7 @@
|
||||
"delete_confirm_text": "[%key:ui::panel::config::automation::editor::triggers::delete_confirm_text%]",
|
||||
"unsupported_action": "No visual editor support for action: {action}",
|
||||
"type_select": "Action type",
|
||||
"continue_on_error": "Continue on error",
|
||||
"type": {
|
||||
"service": {
|
||||
"label": "Call service"
|
||||
@ -2703,6 +2736,21 @@
|
||||
"delete": "[%key:ui::common::delete%]",
|
||||
"duplicate": "[%key:ui::common::duplicate%]"
|
||||
},
|
||||
"dialog_new": {
|
||||
"header": "Create script",
|
||||
"create_empty": "Create new script",
|
||||
"create_empty_description": "Start with an empty script from scratch",
|
||||
"create_blueprint": "[%key:ui::panel::config::automation::dialog_new::create_blueprint%]",
|
||||
"create_blueprint_description": "[%key:ui::panel::config::automation::dialog_new::create_blueprint_description%]",
|
||||
"blueprint_source": {
|
||||
"author": "[%key:ui::panel::config::automation::dialog_new::blueprint_source::author%]",
|
||||
"local": "[%key:ui::panel::config::automation::dialog_new::blueprint_source::local%]",
|
||||
"community": "[%key:ui::panel::config::automation::dialog_new::blueprint_source::community%]",
|
||||
"homeassistant": "[%key:ui::panel::config::automation::dialog_new::blueprint_source::homeassistant%]"
|
||||
},
|
||||
"discover_blueprint_tip": "[%key:ui::panel::config::automation::dialog_new::discover_blueprint_tip%]"
|
||||
},
|
||||
|
||||
"editor": {
|
||||
"alias": "Name",
|
||||
"icon": "Icon",
|
||||
@ -3499,11 +3547,11 @@
|
||||
},
|
||||
"thread": {
|
||||
"other_networks": "Other networks",
|
||||
"my_network": "My network",
|
||||
"my_network": "Preferred network",
|
||||
"no_preferred_network": "You don't have a preferred network yet.",
|
||||
"add_open_thread_border_router": "Add an OpenThread border router",
|
||||
"reset_border_router": "Reset border router",
|
||||
"add_to_my_network": "Add to my network",
|
||||
"add_to_my_network": "Add to preferred network",
|
||||
"no_routers_otbr_network": "No border routers where found, maybe the border router is not configured correctly. You can try to reset it to the factory settings.",
|
||||
"add_dataset_from_tlv": "Add dataset from TLV",
|
||||
"add_dataset": "Add Thread dataset",
|
||||
@ -4066,6 +4114,7 @@
|
||||
"add_title": "Add network storage",
|
||||
"update_title": "Update network storage",
|
||||
"no_mounts": "No connected network storage",
|
||||
"documentation": "Documentation",
|
||||
"not_supported": {
|
||||
"title": "The operating system does not support network storage",
|
||||
"supervised": "Network storage is not supported on this host",
|
||||
@ -4229,6 +4278,11 @@
|
||||
"not_produced_solar_energy": "You have not produced any solar energy",
|
||||
"self_consumed_solar_could_not_calc": "Self-consumed solar energy couldn't be calculated"
|
||||
},
|
||||
"self_sufficiency_gauge": {
|
||||
"card_indicates_self_sufficiency_quota": "This card indicates how self-sufficient your home is.",
|
||||
"self_sufficiency_quota": "Self-sufficiency quota",
|
||||
"self_sufficiency_could_not_calc": "Self-sufficiency quota couldn't be calculated"
|
||||
},
|
||||
"grid_neutrality_gauge": {
|
||||
"energy_dependency": "This card indicates your net energy usage.",
|
||||
"color_explain": "If the needle is in the purple, you returned more energy to the grid than you consumed from it. If it's in the blue, you consumed more energy from the grid than you returned.",
|
||||
@ -4416,12 +4470,15 @@
|
||||
"action-editor": {
|
||||
"navigation_path": "Navigation Path",
|
||||
"url_path": "URL Path",
|
||||
"start_listening": "Start listening",
|
||||
"pipeline_id": "Assistant",
|
||||
"actions": {
|
||||
"default_action": "Default Action",
|
||||
"call-service": "Call Service",
|
||||
"more-info": "More Info",
|
||||
"toggle": "Toggle",
|
||||
"navigate": "Navigate",
|
||||
"assist": "Assist",
|
||||
"url": "URL",
|
||||
"none": "No Action"
|
||||
}
|
||||
|
434
yarn.lock
434
yarn.lock
@ -1582,10 +1582,10 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@eslint/js@npm:8.42.0":
|
||||
version: 8.42.0
|
||||
resolution: "@eslint/js@npm:8.42.0"
|
||||
checksum: 750558843ac458f7da666122083ee05306fc087ecc1e5b21e7e14e23885775af6c55bcc92283dff1862b7b0d8863ec676c0f18c7faf1219c722fe91a8ece56b6
|
||||
"@eslint/js@npm:8.43.0":
|
||||
version: 8.43.0
|
||||
resolution: "@eslint/js@npm:8.43.0"
|
||||
checksum: 580487a09c82ac169744d36e4af77bc4f582c9a37749d1e9481eb93626c8f3991b2390c6e4e69e5642e3b6e870912b839229a0e23594fae348156ea5a8ed7e2e
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@ -2062,13 +2062,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@lit-labs/context@npm:0.3.2":
|
||||
version: 0.3.2
|
||||
resolution: "@lit-labs/context@npm:0.3.2"
|
||||
"@lit-labs/context@npm:0.3.3":
|
||||
version: 0.3.3
|
||||
resolution: "@lit-labs/context@npm:0.3.3"
|
||||
dependencies:
|
||||
"@lit/reactive-element": ^1.5.0
|
||||
lit: ^2.7.0
|
||||
checksum: 55920366798a3337a455c627c0b6911c7b78dee94a783ad77edb9a9e237a2e48201d6cf869f3d0b805316e5c8e8fb817f52f663bc556dd40ca6e8b3168662daf
|
||||
checksum: 3607a7f965f22072feeef8db791e37be45921d9168ea3f432e160cb1fb337de19b2b41a2cd8db5d4fd2675d704d567a24695b796d0b14590616e9232f27579d3
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@ -2088,13 +2088,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@lit-labs/virtualizer@npm:2.0.2":
|
||||
version: 2.0.2
|
||||
resolution: "@lit-labs/virtualizer@npm:2.0.2"
|
||||
"@lit-labs/virtualizer@npm:2.0.3":
|
||||
version: 2.0.3
|
||||
resolution: "@lit-labs/virtualizer@npm:2.0.3"
|
||||
dependencies:
|
||||
lit: ^2.7.0
|
||||
tslib: ^2.0.3
|
||||
checksum: 419aedf72f2b08f8fda43d9729810d5c7f34f470484bd9dff2df49d42cc56e57fcdfd98f69dd84e582d07fd2f372b90077cf42e12c4464b2c04c83755eebb495
|
||||
checksum: 594b89aca53210a6c0127c331fd05b795074df41aba086b63cb13ad5990e6962b86ca8403fe3a673e3bf46735e2def75d5412afe582702346fbd92a3331d34e1
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@ -3166,16 +3166,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@mrmlnc/readdir-enhanced@npm:^2.2.1":
|
||||
version: 2.2.1
|
||||
resolution: "@mrmlnc/readdir-enhanced@npm:2.2.1"
|
||||
dependencies:
|
||||
call-me-maybe: ^1.0.1
|
||||
glob-to-regexp: ^0.3.0
|
||||
checksum: d3b82b29368821154ce8e10bef5ccdbfd070d3e9601643c99ea4607e56f3daeaa4e755dd6d2355da20762c695c1b0570543d9f84b48f70c211ec09c4aaada2e1
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@nodelib/fs.scandir@npm:2.1.5":
|
||||
version: 2.1.5
|
||||
resolution: "@nodelib/fs.scandir@npm:2.1.5"
|
||||
@ -3193,13 +3183,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@nodelib/fs.stat@npm:^1.1.2":
|
||||
version: 1.1.3
|
||||
resolution: "@nodelib/fs.stat@npm:1.1.3"
|
||||
checksum: 318deab369b518a34778cdaa0054dd28a4381c0c78e40bbd20252f67d084b1d7bf9295fea4423de2c19ac8e1a34f120add9125f481b2a710f7068bcac7e3e305
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@nodelib/fs.walk@npm:^1.2.3, @nodelib/fs.walk@npm:^1.2.8":
|
||||
version: 1.2.8
|
||||
resolution: "@nodelib/fs.walk@npm:1.2.8"
|
||||
@ -3219,15 +3202,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@octokit/auth-oauth-device@npm:5.0.0":
|
||||
version: 5.0.0
|
||||
resolution: "@octokit/auth-oauth-device@npm:5.0.0"
|
||||
"@octokit/auth-oauth-device@npm:5.0.2":
|
||||
version: 5.0.2
|
||||
resolution: "@octokit/auth-oauth-device@npm:5.0.2"
|
||||
dependencies:
|
||||
"@octokit/oauth-methods": ^2.0.0
|
||||
"@octokit/request": ^6.0.0
|
||||
"@octokit/types": ^9.0.0
|
||||
"@octokit/oauth-methods": ^3.0.1
|
||||
"@octokit/request": ^7.0.0
|
||||
"@octokit/types": ^10.0.0
|
||||
universal-user-agent: ^6.0.0
|
||||
checksum: 7089bf13bc01629e501af88dc01c18b29b70dba87e26bd4eb2b7faf6baefe3ba9e4ed92f77d5b7a8a56afbbdb1a01b4b264c140c10c4ca6fbd28a7976fcfdc6e
|
||||
checksum: b625a2d7604351e52df46d3fdad04d1eb2ec68f80bce065047691ea83044967ef1e7dd0a70e9f8aab818d8c5ecf7f2550d2aa029ffdba85e0ff8c0ce2e25736a
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@ -3264,6 +3247,17 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@octokit/endpoint@npm:^8.0.0":
|
||||
version: 8.0.1
|
||||
resolution: "@octokit/endpoint@npm:8.0.1"
|
||||
dependencies:
|
||||
"@octokit/types": ^10.0.0
|
||||
is-plain-object: ^5.0.0
|
||||
universal-user-agent: ^6.0.0
|
||||
checksum: 0cff7c972d8304cb59c4cc28016c15bca05e6d7e9e2d9b00af88ce05bf9abdfdb17025f38080162a71ea15b21c740bcb5079361396f18a24bbe55134c504a581
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@octokit/graphql@npm:^5.0.0":
|
||||
version: 5.0.6
|
||||
resolution: "@octokit/graphql@npm:5.0.6"
|
||||
@ -3275,23 +3269,23 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@octokit/oauth-authorization-url@npm:^5.0.0":
|
||||
version: 5.0.0
|
||||
resolution: "@octokit/oauth-authorization-url@npm:5.0.0"
|
||||
checksum: bc457c4af9559e9e8f752e643fc9d116247f4e4246e69959d99b9e39196c93d7af53c1c8e3bd946bd0e4fc29f7ba27efe9bced8525ffa41fe45ef56a8281014b
|
||||
"@octokit/oauth-authorization-url@npm:^6.0.2":
|
||||
version: 6.0.2
|
||||
resolution: "@octokit/oauth-authorization-url@npm:6.0.2"
|
||||
checksum: 0f11169a3eeb782cc08312c923de1a702b25ae033b972ba40380b6d72cb3f684543c8b6a5cf6f05936fdc6b8892070d4f7581138d8efc1b4c4a55ae6d7762327
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@octokit/oauth-methods@npm:^2.0.0":
|
||||
version: 2.0.6
|
||||
resolution: "@octokit/oauth-methods@npm:2.0.6"
|
||||
"@octokit/oauth-methods@npm:^3.0.1":
|
||||
version: 3.0.1
|
||||
resolution: "@octokit/oauth-methods@npm:3.0.1"
|
||||
dependencies:
|
||||
"@octokit/oauth-authorization-url": ^5.0.0
|
||||
"@octokit/request": ^6.2.3
|
||||
"@octokit/request-error": ^3.0.3
|
||||
"@octokit/types": ^9.0.0
|
||||
"@octokit/oauth-authorization-url": ^6.0.2
|
||||
"@octokit/request": ^7.0.0
|
||||
"@octokit/request-error": ^4.0.1
|
||||
"@octokit/types": ^10.0.0
|
||||
btoa-lite: ^1.0.0
|
||||
checksum: 151b933d79d6fbf36fdfae8cdc868a3d43316352eaccf46cb8c420cfd238658275e41996d2d377177553bc0c637c3aefe8ca99c1ab7fd62054654b6119b7b1cc
|
||||
checksum: ad327084f97d2f3be270d8957545dbd06c35df3e99d8e58702217beb7ac3574c361b49dfe28ba5d96b7f1911ac9c8e26ae07d6180a0598eef8b7fab4b0fe4ad5
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@ -3334,20 +3328,20 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@octokit/plugin-retry@npm:5.0.3":
|
||||
version: 5.0.3
|
||||
resolution: "@octokit/plugin-retry@npm:5.0.3"
|
||||
"@octokit/plugin-retry@npm:5.0.4":
|
||||
version: 5.0.4
|
||||
resolution: "@octokit/plugin-retry@npm:5.0.4"
|
||||
dependencies:
|
||||
"@octokit/request-error": ^4.0.1
|
||||
"@octokit/types": ^9.0.0
|
||||
"@octokit/types": ^10.0.0
|
||||
bottleneck: ^2.15.3
|
||||
peerDependencies:
|
||||
"@octokit/core": ">=3"
|
||||
checksum: f98b90333e26a214f057557ee5b13a926e0472a47d103345c504f99e6c3d8564a8fa54bf2871eda8ef47a8f9c1dba84fb68e749ab7a1a749c0a86a3ae74bdfa7
|
||||
checksum: 0c5645613f7ff758ac126da11ba20b4d49e4067676e30808f5ee3ee471adbd2ccfdea2200adfa5a4663b207964b3d60987f4c5e8682fb275bf134b33f2ef5178
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@octokit/request-error@npm:^3.0.0, @octokit/request-error@npm:^3.0.3":
|
||||
"@octokit/request-error@npm:^3.0.0":
|
||||
version: 3.0.3
|
||||
resolution: "@octokit/request-error@npm:3.0.3"
|
||||
dependencies:
|
||||
@ -3369,7 +3363,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@octokit/request@npm:^6.0.0, @octokit/request@npm:^6.2.3":
|
||||
"@octokit/request@npm:^6.0.0":
|
||||
version: 6.2.8
|
||||
resolution: "@octokit/request@npm:6.2.8"
|
||||
dependencies:
|
||||
@ -3383,15 +3377,28 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@octokit/rest@npm:19.0.11":
|
||||
version: 19.0.11
|
||||
resolution: "@octokit/rest@npm:19.0.11"
|
||||
"@octokit/request@npm:^7.0.0":
|
||||
version: 7.0.0
|
||||
resolution: "@octokit/request@npm:7.0.0"
|
||||
dependencies:
|
||||
"@octokit/endpoint": ^8.0.0
|
||||
"@octokit/request-error": ^4.0.1
|
||||
"@octokit/types": ^10.0.0
|
||||
is-plain-object: ^5.0.0
|
||||
universal-user-agent: ^6.0.0
|
||||
checksum: d3b8ac25c3702bb69c5b345f7a9f16b158209db7e244cc2d60dbcbfbaf1edec8252d78885de3607ee85eb86db7c1d2e07fa2515ba6e25cf2880440c0df5e918a
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@octokit/rest@npm:19.0.13":
|
||||
version: 19.0.13
|
||||
resolution: "@octokit/rest@npm:19.0.13"
|
||||
dependencies:
|
||||
"@octokit/core": ^4.2.1
|
||||
"@octokit/plugin-paginate-rest": ^6.1.2
|
||||
"@octokit/plugin-request-log": ^1.0.4
|
||||
"@octokit/plugin-rest-endpoint-methods": ^7.1.2
|
||||
checksum: 147518ad51d214ead88adc717b5fdc4f33317949d58c124f4069bdf07d2e6b49fa66861036b9e233aed71fcb88ff367a6da0357653484e466175ab4fb7183b3b
|
||||
checksum: ca1553e3fe46efabffef60e68e4a228d4cc0f0d545daf7f019560f666d3e934c6f3a6402a42bbd786af4f3c0a6e69380776312f01b7d52998fe1bbdd1b068f69
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@ -4785,126 +4792,126 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@vaadin/a11y-base@npm:~24.1.0":
|
||||
version: 24.1.0
|
||||
resolution: "@vaadin/a11y-base@npm:24.1.0"
|
||||
"@vaadin/a11y-base@npm:~24.1.1":
|
||||
version: 24.1.1
|
||||
resolution: "@vaadin/a11y-base@npm:24.1.1"
|
||||
dependencies:
|
||||
"@open-wc/dedupe-mixin": ^1.3.0
|
||||
"@polymer/polymer": ^3.0.0
|
||||
"@vaadin/component-base": ~24.1.0
|
||||
"@vaadin/component-base": ~24.1.1
|
||||
lit: ^2.0.0
|
||||
checksum: f019f2c04473c60c3714ec3da65a129833e1fab4e2eefb4f88d4caa81eb45da756dce2cec8b222bd259d0b87bc67439a4aa88f636b90e6f704c037197bdc1492
|
||||
checksum: 1b83e3e44ebc8c395a5ba9a6bc92d42aeb6afce3bcd6a1c492bbc7eb166a228474bf7dc56fb6509d47618fdd87107c60e4ebc60d2aab3c72dcc1392465fd7ac1
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@vaadin/combo-box@npm:24.1.0":
|
||||
version: 24.1.0
|
||||
resolution: "@vaadin/combo-box@npm:24.1.0"
|
||||
"@vaadin/combo-box@npm:24.1.1":
|
||||
version: 24.1.1
|
||||
resolution: "@vaadin/combo-box@npm:24.1.1"
|
||||
dependencies:
|
||||
"@open-wc/dedupe-mixin": ^1.3.0
|
||||
"@polymer/polymer": ^3.0.0
|
||||
"@vaadin/a11y-base": ~24.1.0
|
||||
"@vaadin/component-base": ~24.1.0
|
||||
"@vaadin/field-base": ~24.1.0
|
||||
"@vaadin/input-container": ~24.1.0
|
||||
"@vaadin/item": ~24.1.0
|
||||
"@vaadin/lit-renderer": ~24.1.0
|
||||
"@vaadin/overlay": ~24.1.0
|
||||
"@vaadin/vaadin-lumo-styles": ~24.1.0
|
||||
"@vaadin/vaadin-material-styles": ~24.1.0
|
||||
"@vaadin/vaadin-themable-mixin": ~24.1.0
|
||||
checksum: cbdbfba535a16faa84a897d61565e91d1b2ec0dad87c1644e287c180fed13fcf3e2b0c436fb85ad7f394a4bb7aceb596b9070e3e171afe8167f2158908e71ea5
|
||||
"@vaadin/a11y-base": ~24.1.1
|
||||
"@vaadin/component-base": ~24.1.1
|
||||
"@vaadin/field-base": ~24.1.1
|
||||
"@vaadin/input-container": ~24.1.1
|
||||
"@vaadin/item": ~24.1.1
|
||||
"@vaadin/lit-renderer": ~24.1.1
|
||||
"@vaadin/overlay": ~24.1.1
|
||||
"@vaadin/vaadin-lumo-styles": ~24.1.1
|
||||
"@vaadin/vaadin-material-styles": ~24.1.1
|
||||
"@vaadin/vaadin-themable-mixin": ~24.1.1
|
||||
checksum: 0011a1271ebe41c6eaf5d5cf5f15c7cfdfab46534c70b4bdbcc0b1afdd6e83e0c7983d3927db6df4c7669269d7909a3d886586696b9d75d291a6a7876db22ce6
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@vaadin/component-base@npm:~24.1.0":
|
||||
version: 24.1.0
|
||||
resolution: "@vaadin/component-base@npm:24.1.0"
|
||||
"@vaadin/component-base@npm:~24.1.1":
|
||||
version: 24.1.1
|
||||
resolution: "@vaadin/component-base@npm:24.1.1"
|
||||
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: 1df8786d8ef1b3a2a104101c8877464d6fca3ea70fa851b2f99bcf32bb83780ced05f524b6225e95f901edb8ec9379fe625ac18154367a3a684846004277badc
|
||||
checksum: 34c698266897cf7c3c5f8b8798468f5035ae764b1743e6a93f5ea1921b3d29e642db51e4d6d71c6594ba03dee14fa0704b34ccb70b7f50ecbfd07677bde231ac
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@vaadin/field-base@npm:~24.1.0":
|
||||
version: 24.1.0
|
||||
resolution: "@vaadin/field-base@npm:24.1.0"
|
||||
"@vaadin/field-base@npm:~24.1.1":
|
||||
version: 24.1.1
|
||||
resolution: "@vaadin/field-base@npm:24.1.1"
|
||||
dependencies:
|
||||
"@open-wc/dedupe-mixin": ^1.3.0
|
||||
"@polymer/polymer": ^3.0.0
|
||||
"@vaadin/a11y-base": ~24.1.0
|
||||
"@vaadin/component-base": ~24.1.0
|
||||
"@vaadin/a11y-base": ~24.1.1
|
||||
"@vaadin/component-base": ~24.1.1
|
||||
lit: ^2.0.0
|
||||
checksum: 5e37ede91e05dd8eb9fa43749b89670904abfc30e522eebb4ec2225318cf72774dd4e94e49deb1b55daea719818803d90968c4d0f4b87132d24289345e728abd
|
||||
checksum: b16d5579d4a5f43a62df431b7e7e3107bf5a8062ad681b6ce0c1c345dc56ce4f0ae4f0909e2e9ff0fb05d9f2f4b54e12f17b75a680c69b9f24f8f82b63c0b234
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@vaadin/icon@npm:~24.1.0":
|
||||
version: 24.1.0
|
||||
resolution: "@vaadin/icon@npm:24.1.0"
|
||||
"@vaadin/icon@npm:~24.1.1":
|
||||
version: 24.1.1
|
||||
resolution: "@vaadin/icon@npm:24.1.1"
|
||||
dependencies:
|
||||
"@polymer/polymer": ^3.0.0
|
||||
"@vaadin/component-base": ~24.1.0
|
||||
"@vaadin/vaadin-lumo-styles": ~24.1.0
|
||||
"@vaadin/vaadin-themable-mixin": ~24.1.0
|
||||
"@vaadin/component-base": ~24.1.1
|
||||
"@vaadin/vaadin-lumo-styles": ~24.1.1
|
||||
"@vaadin/vaadin-themable-mixin": ~24.1.1
|
||||
lit: ^2.0.0
|
||||
checksum: 60ee5e3056d175b032c1ad41b3b208b2289c2ef043e4f073e86f691ed135ea98a8780fe0624c9347858f5f0f44a0cc58c345d51eb3795fac47cdafd6cc1a8c59
|
||||
checksum: be3f8986e04f163791c0fdbc51c5d7c8074b12548f151b58a3f357ab639cc5c0c53b3375ded7936cd8c618df19dcfef4c616735b24e81da6ae1fca753d4c0774
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@vaadin/input-container@npm:~24.1.0":
|
||||
version: 24.1.0
|
||||
resolution: "@vaadin/input-container@npm:24.1.0"
|
||||
"@vaadin/input-container@npm:~24.1.1":
|
||||
version: 24.1.1
|
||||
resolution: "@vaadin/input-container@npm:24.1.1"
|
||||
dependencies:
|
||||
"@polymer/polymer": ^3.0.0
|
||||
"@vaadin/component-base": ~24.1.0
|
||||
"@vaadin/vaadin-lumo-styles": ~24.1.0
|
||||
"@vaadin/vaadin-material-styles": ~24.1.0
|
||||
"@vaadin/vaadin-themable-mixin": ~24.1.0
|
||||
checksum: d222ec0df6c3e169341d8e1c4bc5b15da6f4324bb962537929b864df389f24474195c0088b1d06d88aaecc18f63938c5f5fa614d8fcda233c8ea5222fc31f183
|
||||
"@vaadin/component-base": ~24.1.1
|
||||
"@vaadin/vaadin-lumo-styles": ~24.1.1
|
||||
"@vaadin/vaadin-material-styles": ~24.1.1
|
||||
"@vaadin/vaadin-themable-mixin": ~24.1.1
|
||||
checksum: b8934fae0f5578b78f4ee05c506b59e66c247e3fdf6d42b1ba7d198d77af170907b1f3cd98ee5ce7bd540d0f5c1f4c08d8febdfa35f7231ed56d289b0eb7432b
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@vaadin/item@npm:~24.1.0":
|
||||
version: 24.1.0
|
||||
resolution: "@vaadin/item@npm:24.1.0"
|
||||
"@vaadin/item@npm:~24.1.1":
|
||||
version: 24.1.1
|
||||
resolution: "@vaadin/item@npm:24.1.1"
|
||||
dependencies:
|
||||
"@open-wc/dedupe-mixin": ^1.3.0
|
||||
"@polymer/polymer": ^3.0.0
|
||||
"@vaadin/a11y-base": ~24.1.0
|
||||
"@vaadin/component-base": ~24.1.0
|
||||
"@vaadin/vaadin-lumo-styles": ~24.1.0
|
||||
"@vaadin/vaadin-material-styles": ~24.1.0
|
||||
"@vaadin/vaadin-themable-mixin": ~24.1.0
|
||||
checksum: 643f47f7e4ae74cffa3e891789c0689063d73552d81aa84dad66d3f415e624e734f13ae0b0123710984fa8390ea2df1f468d9415d7d914150695821045e09ea0
|
||||
"@vaadin/a11y-base": ~24.1.1
|
||||
"@vaadin/component-base": ~24.1.1
|
||||
"@vaadin/vaadin-lumo-styles": ~24.1.1
|
||||
"@vaadin/vaadin-material-styles": ~24.1.1
|
||||
"@vaadin/vaadin-themable-mixin": ~24.1.1
|
||||
checksum: a87529f5c0c385920d36173b670afbfaaa83d0c171cea048daf7986fbdfb7d82cfef651bc806043ca007a61c1f92b47bba489240b0917d15c75ad49fabab2153
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@vaadin/lit-renderer@npm:~24.1.0":
|
||||
version: 24.1.0
|
||||
resolution: "@vaadin/lit-renderer@npm:24.1.0"
|
||||
"@vaadin/lit-renderer@npm:~24.1.1":
|
||||
version: 24.1.1
|
||||
resolution: "@vaadin/lit-renderer@npm:24.1.1"
|
||||
dependencies:
|
||||
lit: ^2.0.0
|
||||
checksum: 9f0940e0245f608dc613cb33ffdb4f88c275597f7b25fac04892d29ddfc752801fde118fca47bd8445a9d51bca203c339216186ca9b4941b0b6f07a52cc4fc9a
|
||||
checksum: 17a22abce1654c9b6c86e8a113778d61d5780e45164d3362741b00f47e061cfd88521127802f16ce2ad3ba92ed1535829c8b154183cc6f4fbececdbbb70f4233
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@vaadin/overlay@npm:~24.1.0":
|
||||
version: 24.1.0
|
||||
resolution: "@vaadin/overlay@npm:24.1.0"
|
||||
"@vaadin/overlay@npm:~24.1.1":
|
||||
version: 24.1.1
|
||||
resolution: "@vaadin/overlay@npm:24.1.1"
|
||||
dependencies:
|
||||
"@open-wc/dedupe-mixin": ^1.3.0
|
||||
"@polymer/polymer": ^3.0.0
|
||||
"@vaadin/a11y-base": ~24.1.0
|
||||
"@vaadin/component-base": ~24.1.0
|
||||
"@vaadin/vaadin-lumo-styles": ~24.1.0
|
||||
"@vaadin/vaadin-material-styles": ~24.1.0
|
||||
"@vaadin/vaadin-themable-mixin": ~24.1.0
|
||||
checksum: 8362db034347e8186c4397de55fd51b69e645f621614298b68fa383e4957a6ea8290b0770b3d686217ce937a7a18d33ea0ea6844d3da4d3aa3a61d7498210b80
|
||||
"@vaadin/a11y-base": ~24.1.1
|
||||
"@vaadin/component-base": ~24.1.1
|
||||
"@vaadin/vaadin-lumo-styles": ~24.1.1
|
||||
"@vaadin/vaadin-material-styles": ~24.1.1
|
||||
"@vaadin/vaadin-themable-mixin": ~24.1.1
|
||||
checksum: d0def2106e4fff7d7c49931e9b917c68994f371a0246e076442e33d97ac7a25341d9794aee9e41c7c05c94111dacac74cb5648f1814fb45f11cf37ffe6850fa1
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@ -4915,34 +4922,34 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@vaadin/vaadin-lumo-styles@npm:~24.1.0":
|
||||
version: 24.1.0
|
||||
resolution: "@vaadin/vaadin-lumo-styles@npm:24.1.0"
|
||||
"@vaadin/vaadin-lumo-styles@npm:~24.1.1":
|
||||
version: 24.1.1
|
||||
resolution: "@vaadin/vaadin-lumo-styles@npm:24.1.1"
|
||||
dependencies:
|
||||
"@polymer/polymer": ^3.0.0
|
||||
"@vaadin/icon": ~24.1.0
|
||||
"@vaadin/vaadin-themable-mixin": ~24.1.0
|
||||
checksum: 68c55fadb2468048b3fe2ae14c8e5fdb90cb35a171c1a2dc7e33b369d8f79565b6e1c5a93a26dbc5e24b3f7b7e5634b87459fd5528e58bf045cc6c5717840703
|
||||
"@vaadin/icon": ~24.1.1
|
||||
"@vaadin/vaadin-themable-mixin": ~24.1.1
|
||||
checksum: ab344ce558de8f1075de6290517169bd3e95cf5038549b987ca7cfb14a9798ca12573e099958fecd518dd74f2bcfa76030dfc5d3b6e46b34dff10e4d675182c5
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@vaadin/vaadin-material-styles@npm:~24.1.0":
|
||||
version: 24.1.0
|
||||
resolution: "@vaadin/vaadin-material-styles@npm:24.1.0"
|
||||
"@vaadin/vaadin-material-styles@npm:~24.1.1":
|
||||
version: 24.1.1
|
||||
resolution: "@vaadin/vaadin-material-styles@npm:24.1.1"
|
||||
dependencies:
|
||||
"@polymer/polymer": ^3.0.0
|
||||
"@vaadin/vaadin-themable-mixin": ~24.1.0
|
||||
checksum: 205e67f5a99dda6cdf1410a0786408d42b8ea48c18c26b3a89d9524fd651b89993db12f3ccfff6635d7981a557416aa8656696d42e6f58fe593d6845b88334ac
|
||||
"@vaadin/vaadin-themable-mixin": ~24.1.1
|
||||
checksum: 601e345da8858a62804b1afde06a79f93e9b5c41fcc263bb692b6db167937e3dd1b754b502ceeaf4054586d3e04a68a167ba7bc2719ec4ad8ac10005795d9a6e
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@vaadin/vaadin-themable-mixin@npm:24.1.0, @vaadin/vaadin-themable-mixin@npm:~24.1.0":
|
||||
version: 24.1.0
|
||||
resolution: "@vaadin/vaadin-themable-mixin@npm:24.1.0"
|
||||
"@vaadin/vaadin-themable-mixin@npm:24.1.1, @vaadin/vaadin-themable-mixin@npm:~24.1.1":
|
||||
version: 24.1.1
|
||||
resolution: "@vaadin/vaadin-themable-mixin@npm:24.1.1"
|
||||
dependencies:
|
||||
"@open-wc/dedupe-mixin": ^1.3.0
|
||||
lit: ^2.0.0
|
||||
checksum: 0abe57312bdda606b52ce93843e82628310e419cbfe4c8bd564c574f7883c8979861b1eb34982bab4a488a82a467dd80cd482e018154ce343310b2918146808d
|
||||
checksum: 5066300dcf6c987e43bb9c2e16d75198188220dfbde0c76d3d875444200f05b4de70d50fd3d082c0ac0b5075953835d1499ed01d51236e06a56d5ca9e6d25e4c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@ -5065,6 +5072,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@vscode/web-custom-data@npm:^0.4.2":
|
||||
version: 0.4.6
|
||||
resolution: "@vscode/web-custom-data@npm:0.4.6"
|
||||
checksum: 2d87f3e50dc6eeacdbbca224f1c8837154acd6e1588f733ff812423e72f0a14d819740bbb91732a9c09978faa0fcfe1be339a17fd0beb130e51784752cef4b4f
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@vue/compiler-sfc@npm:2.7.14":
|
||||
version: 2.7.14
|
||||
resolution: "@vue/compiler-sfc@npm:2.7.14"
|
||||
@ -6472,13 +6486,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"call-me-maybe@npm:^1.0.1":
|
||||
version: 1.0.2
|
||||
resolution: "call-me-maybe@npm:1.0.2"
|
||||
checksum: 42ff2d0bed5b207e3f0122589162eaaa47ba618f79ad2382fe0ba14d9e49fbf901099a6227440acc5946f86a4953e8aa2d242b330b0a5de4d090bb18f8935cae
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"callsites@npm:^3.0.0":
|
||||
version: 3.1.0
|
||||
resolution: "callsites@npm:3.1.0"
|
||||
@ -8144,14 +8151,14 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"eslint@npm:8.42.0":
|
||||
version: 8.42.0
|
||||
resolution: "eslint@npm:8.42.0"
|
||||
"eslint@npm:8.43.0":
|
||||
version: 8.43.0
|
||||
resolution: "eslint@npm:8.43.0"
|
||||
dependencies:
|
||||
"@eslint-community/eslint-utils": ^4.2.0
|
||||
"@eslint-community/regexpp": ^4.4.0
|
||||
"@eslint/eslintrc": ^2.0.3
|
||||
"@eslint/js": 8.42.0
|
||||
"@eslint/js": 8.43.0
|
||||
"@humanwhocodes/config-array": ^0.11.10
|
||||
"@humanwhocodes/module-importer": ^1.0.1
|
||||
"@nodelib/fs.walk": ^1.2.8
|
||||
@ -8189,7 +8196,7 @@ __metadata:
|
||||
text-table: ^0.2.0
|
||||
bin:
|
||||
eslint: bin/eslint.js
|
||||
checksum: 07105397b5f2ff4064b983b8971e8c379ec04b1dfcc9d918976b3e00377189000161dac991d82ba14f8759e466091b8c71146f602930ca810c290ee3fcb3faf0
|
||||
checksum: 55654ce00b0d128822b57526e40473d0497c7c6be3886afdc0b41b6b0dfbd34d0eae8159911b18451b4db51a939a0e6d2e117e847ae419086884fc3d4fe23c7c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@ -8494,20 +8501,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"fast-glob@npm:^2.2.6":
|
||||
version: 2.2.7
|
||||
resolution: "fast-glob@npm:2.2.7"
|
||||
dependencies:
|
||||
"@mrmlnc/readdir-enhanced": ^2.2.1
|
||||
"@nodelib/fs.stat": ^1.1.2
|
||||
glob-parent: ^3.1.0
|
||||
is-glob: ^4.0.0
|
||||
merge2: ^1.2.3
|
||||
micromatch: ^3.1.10
|
||||
checksum: 304ccff1d437fcc44ae0168b0c3899054b92e0fd6af6ad7c3ccc82ab4ddd210b99c7c739d60ee3686da2aa165cd1a31810b31fd91f7c2a575d297342a9fc0534
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"fast-glob@npm:^3.2.11, fast-glob@npm:^3.2.2, fast-glob@npm:^3.2.9":
|
||||
version: 3.2.12
|
||||
resolution: "fast-glob@npm:3.2.12"
|
||||
@ -9141,13 +9134,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"glob-to-regexp@npm:^0.3.0":
|
||||
version: 0.3.0
|
||||
resolution: "glob-to-regexp@npm:0.3.0"
|
||||
checksum: d34b3219d860042d508c4893b67617cd16e2668827e445ff39cff9f72ef70361d3dc24f429e003cdfb6607c75c9664b8eadc41d2eeb95690af0b0d3113c1b23b
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"glob-to-regexp@npm:^0.4.1":
|
||||
version: 0.4.1
|
||||
resolution: "glob-to-regexp@npm:0.4.1"
|
||||
@ -9633,9 +9619,9 @@ __metadata:
|
||||
"@fullcalendar/timegrid": 6.1.8
|
||||
"@koa/cors": 4.0.0
|
||||
"@lezer/highlight": 1.1.6
|
||||
"@lit-labs/context": 0.3.2
|
||||
"@lit-labs/context": 0.3.3
|
||||
"@lit-labs/motion": 1.0.3
|
||||
"@lit-labs/virtualizer": 2.0.2
|
||||
"@lit-labs/virtualizer": 2.0.3
|
||||
"@lrnwebcomponents/simple-tooltip": 7.0.2
|
||||
"@material/chips": =14.0.0-canary.53b3cad2f.0
|
||||
"@material/data-table": =14.0.0-canary.53b3cad2f.0
|
||||
@ -9665,9 +9651,9 @@ __metadata:
|
||||
"@material/web": =1.0.0-pre.10
|
||||
"@mdi/js": 7.2.96
|
||||
"@mdi/svg": 7.2.96
|
||||
"@octokit/auth-oauth-device": 5.0.0
|
||||
"@octokit/plugin-retry": 5.0.3
|
||||
"@octokit/rest": 19.0.11
|
||||
"@octokit/auth-oauth-device": 5.0.2
|
||||
"@octokit/plugin-retry": 5.0.4
|
||||
"@octokit/rest": 19.0.13
|
||||
"@open-wc/dev-server-hmr": 0.1.4
|
||||
"@polymer/app-layout": 3.1.0
|
||||
"@polymer/iron-flex-layout": 3.0.1
|
||||
@ -9704,8 +9690,8 @@ __metadata:
|
||||
"@types/webspeechapi": 0.0.29
|
||||
"@typescript-eslint/eslint-plugin": 5.59.11
|
||||
"@typescript-eslint/parser": 5.59.11
|
||||
"@vaadin/combo-box": 24.1.0
|
||||
"@vaadin/vaadin-themable-mixin": 24.1.0
|
||||
"@vaadin/combo-box": 24.1.1
|
||||
"@vaadin/vaadin-themable-mixin": 24.1.1
|
||||
"@vibrant/color": 3.2.1-alpha.1
|
||||
"@vibrant/core": 3.2.1-alpha.1
|
||||
"@vibrant/quantizer-mmcq": 3.2.1-alpha.1
|
||||
@ -9727,7 +9713,7 @@ __metadata:
|
||||
deep-clone-simple: 1.1.1
|
||||
deep-freeze: 0.0.1
|
||||
del: 7.0.0
|
||||
eslint: 8.42.0
|
||||
eslint: 8.43.0
|
||||
eslint-config-airbnb-base: 15.0.0
|
||||
eslint-config-airbnb-typescript: 17.0.0
|
||||
eslint-config-prettier: 8.8.0
|
||||
@ -9763,7 +9749,7 @@ __metadata:
|
||||
leaflet-draw: 1.0.4
|
||||
lint-staged: 13.2.2
|
||||
lit: 2.7.5
|
||||
lit-analyzer: 1.2.1
|
||||
lit-analyzer: 2.0.0-pre.3
|
||||
lodash.template: 4.5.0
|
||||
magic-string: 0.30.0
|
||||
map-stream: 0.0.7
|
||||
@ -9796,10 +9782,10 @@ __metadata:
|
||||
tar: 6.1.15
|
||||
terser-webpack-plugin: 5.3.9
|
||||
tinykeys: 2.1.0
|
||||
ts-lit-plugin: 1.2.1
|
||||
ts-lit-plugin: 2.0.0-pre.1
|
||||
tsparticles-engine: 2.10.1
|
||||
tsparticles-preset-links: 2.10.1
|
||||
typescript: 4.9.5
|
||||
typescript: 5.1.3
|
||||
unfetch: 5.0.0
|
||||
vinyl-buffer: 1.0.1
|
||||
vinyl-source-stream: 2.0.0
|
||||
@ -11367,21 +11353,22 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"lit-analyzer@npm:1.2.1":
|
||||
version: 1.2.1
|
||||
resolution: "lit-analyzer@npm:1.2.1"
|
||||
"lit-analyzer@npm:2.0.0-pre.3, lit-analyzer@npm:^2.0.0-pre.3":
|
||||
version: 2.0.0-pre.3
|
||||
resolution: "lit-analyzer@npm:2.0.0-pre.3"
|
||||
dependencies:
|
||||
"@vscode/web-custom-data": ^0.4.2
|
||||
chalk: ^2.4.2
|
||||
didyoumean2: 4.1.0
|
||||
fast-glob: ^2.2.6
|
||||
fast-glob: ^3.2.11
|
||||
parse5: 5.1.0
|
||||
ts-simple-type: ~1.0.5
|
||||
ts-simple-type: ~2.0.0-next.0
|
||||
vscode-css-languageservice: 4.3.0
|
||||
vscode-html-languageservice: 3.1.0
|
||||
web-component-analyzer: ~1.1.1
|
||||
web-component-analyzer: ^2.0.0-next.5
|
||||
bin:
|
||||
lit-analyzer: cli.js
|
||||
checksum: b89646033b45262a863bf32d8bf177bfa4f22cde4e2c3f2cd006abdd68aeab434505f67c3c5ed213d8a5936d063ec2845efb15b0968ec9cf9e0863e53e6c118c
|
||||
checksum: c6dcf657a0030342d183fcd9d550753bd5dd0692b478aff271085a5a0e7e08aff39cc6dde3d547ffca72897975ef07aac7de8a97e0060d2db70a85350412efae
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@ -11798,7 +11785,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"merge2@npm:^1.2.3, merge2@npm:^1.3.0, merge2@npm:^1.4.1":
|
||||
"merge2@npm:^1.3.0, merge2@npm:^1.4.1":
|
||||
version: 1.4.1
|
||||
resolution: "merge2@npm:1.4.1"
|
||||
checksum: 7268db63ed5169466540b6fb947aec313200bcf6d40c5ab722c22e242f651994619bcd85601602972d3c85bd2cc45a358a4c61937e9f11a061919a1da569b0c2
|
||||
@ -15215,19 +15202,20 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"ts-lit-plugin@npm:1.2.1":
|
||||
version: 1.2.1
|
||||
resolution: "ts-lit-plugin@npm:1.2.1"
|
||||
"ts-lit-plugin@npm:2.0.0-pre.1":
|
||||
version: 2.0.0-pre.1
|
||||
resolution: "ts-lit-plugin@npm:2.0.0-pre.1"
|
||||
dependencies:
|
||||
lit-analyzer: 1.2.1
|
||||
checksum: 3ba191d8924b18ba1aa1072db82cd10bca19f20693d98735dc1bbf3692ec759f2d4c728b789a1c1ade4d96e5ddf25e574fdba7c5e42b557c7e82da7a1ad298d7
|
||||
lit-analyzer: ^2.0.0-pre.3
|
||||
web-component-analyzer: ^2.0.0-next.5
|
||||
checksum: d9c8b3c0d8867e9564c7a3083accaf9f8b48b04c8f42e32c224806a7a606983abb3b7acb912e57d0d8f90ff650745bb1af18b1e379316b433838e37eddccfe1e
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"ts-simple-type@npm:~1.0.5":
|
||||
version: 1.0.7
|
||||
resolution: "ts-simple-type@npm:1.0.7"
|
||||
checksum: 3cffb45eab7ab7fd963e2765914c41488d9611dc0619334ac1cf01bc2b02cf9746adf12172d785894474c6c5f3cfbf8212e675e456362373a0c2a61441ad8572
|
||||
"ts-simple-type@npm:2.0.0-next.0, ts-simple-type@npm:~2.0.0-next.0":
|
||||
version: 2.0.0-next.0
|
||||
resolution: "ts-simple-type@npm:2.0.0-next.0"
|
||||
checksum: 025c5c1e4f2f7f2627300b2605a0346d5007f9c3d20d075807b01b3ae8179261e6be2d471f74948f3bae3208ca042203d97e80b984d6cb133396c5c4a3af5301
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@ -15440,43 +15428,43 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"typescript@npm:4.9.5":
|
||||
version: 4.9.5
|
||||
resolution: "typescript@npm:4.9.5"
|
||||
"typescript@npm:5.1.3":
|
||||
version: 5.1.3
|
||||
resolution: "typescript@npm:5.1.3"
|
||||
bin:
|
||||
tsc: bin/tsc
|
||||
tsserver: bin/tsserver
|
||||
checksum: ee000bc26848147ad423b581bd250075662a354d84f0e06eb76d3b892328d8d4440b7487b5a83e851b12b255f55d71835b008a66cbf8f255a11e4400159237db
|
||||
checksum: d9d51862d98efa46534f2800a1071a613751b1585dc78884807d0c179bcd93d6e9d4012a508e276742f5f33c480adefc52ffcafaf9e0e00ab641a14cde9a31c7
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"typescript@npm:^3.8.3":
|
||||
version: 3.9.10
|
||||
resolution: "typescript@npm:3.9.10"
|
||||
"typescript@npm:~4.4.3":
|
||||
version: 4.4.4
|
||||
resolution: "typescript@npm:4.4.4"
|
||||
bin:
|
||||
tsc: bin/tsc
|
||||
tsserver: bin/tsserver
|
||||
checksum: 46c842e2cd4797b88b66ef06c9c41dd21da48b95787072ccf39d5f2aa3124361bc4c966aa1c7f709fae0509614d76751455b5231b12dbb72eb97a31369e1ff92
|
||||
checksum: 89ecb8436bb48ef5594d49289f5f89103071716b6e4844278f4fb3362856e31203e187a9c76d205c3f0b674d221a058fd28310dbcbcf5d95e9a57229bb5203f1
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"typescript@patch:typescript@4.9.5#~builtin<compat/typescript>":
|
||||
version: 4.9.5
|
||||
resolution: "typescript@patch:typescript@npm%3A4.9.5#~builtin<compat/typescript>::version=4.9.5&hash=289587"
|
||||
"typescript@patch:typescript@5.1.3#~builtin<compat/typescript>":
|
||||
version: 5.1.3
|
||||
resolution: "typescript@patch:typescript@npm%3A5.1.3#~builtin<compat/typescript>::version=5.1.3&hash=5da071"
|
||||
bin:
|
||||
tsc: bin/tsc
|
||||
tsserver: bin/tsserver
|
||||
checksum: 1f8f3b6aaea19f0f67cba79057674ba580438a7db55057eb89cc06950483c5d632115c14077f6663ea76fd09fce3c190e6414bb98582ec80aa5a4eaf345d5b68
|
||||
checksum: 6f0a9dca6bf4ce9dcaf4e282aade55ef4c56ecb5fb98d0a4a5c0113398815aea66d871b5611e83353e5953a19ed9ef103cf5a76ac0f276d550d1e7cd5344f61e
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"typescript@patch:typescript@^3.8.3#~builtin<compat/typescript>":
|
||||
version: 3.9.10
|
||||
resolution: "typescript@patch:typescript@npm%3A3.9.10#~builtin<compat/typescript>::version=3.9.10&hash=3bd3d3"
|
||||
"typescript@patch:typescript@~4.4.3#~builtin<compat/typescript>":
|
||||
version: 4.4.4
|
||||
resolution: "typescript@patch:typescript@npm%3A4.4.4#~builtin<compat/typescript>::version=4.4.4&hash=bbeadb"
|
||||
bin:
|
||||
tsc: bin/tsc
|
||||
tsserver: bin/tsserver
|
||||
checksum: dc7141ab555b23a8650a6787f98845fc11692063d02b75ff49433091b3af2fe3d773650dea18389d7c21f47d620fb3b110ea363dab4ab039417a6ccbbaf96fc2
|
||||
checksum: 3d1b04449662193544b81d055479d03b4c5dca95f1a82f8922596f089d894c9fefbe16639d1d9dfe26a7054419645530cef44001bc17aed1fe1eb3c237e9b3c7
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@ -15978,18 +15966,18 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"web-component-analyzer@npm:~1.1.1":
|
||||
version: 1.1.7
|
||||
resolution: "web-component-analyzer@npm:1.1.7"
|
||||
"web-component-analyzer@npm:^2.0.0-next.5":
|
||||
version: 2.0.0-next.5
|
||||
resolution: "web-component-analyzer@npm:2.0.0-next.5"
|
||||
dependencies:
|
||||
fast-glob: ^3.2.2
|
||||
ts-simple-type: ~1.0.5
|
||||
typescript: ^3.8.3
|
||||
ts-simple-type: 2.0.0-next.0
|
||||
typescript: ~4.4.3
|
||||
yargs: ^15.3.1
|
||||
bin:
|
||||
wca: cli.js
|
||||
web-component-analyzer: cli.js
|
||||
checksum: 6c36521b7b79d5547ffdbc359029651ad1d929df6e09f8adfbafb2a34c23199712b7080f08f941f056b6a989718c11eb9221171d97ad397ed8a20cf08dd78e4b
|
||||
checksum: bc8eaf57884b81d378014376bcab92bce6b127971952831dd5bb06803b590cc99fb6fa17c7476c02ee014dfccfcee80e670d7fdc7aff4aae6aebc2e262055a65
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user