mirror of
https://github.com/home-assistant/frontend.git
synced 2025-09-05 03:06:40 +00:00
Compare commits
41 Commits
add-eslint
...
20250401.0
Author | SHA1 | Date | |
---|---|---|---|
![]() |
0bcaa104e7 | ||
![]() |
6b3f807129 | ||
![]() |
c464d344db | ||
![]() |
69f0a4a728 | ||
![]() |
2ba8f9f99d | ||
![]() |
7e06bbc467 | ||
![]() |
6017d82c21 | ||
![]() |
40c200a172 | ||
![]() |
a2f70f682f | ||
![]() |
c42a899b52 | ||
![]() |
706f43e99e | ||
![]() |
f5496c21e8 | ||
![]() |
34dce5b279 | ||
![]() |
a4f07423ec | ||
![]() |
9e32c24f3c | ||
![]() |
b281d095cd | ||
![]() |
fe7e8e17ae | ||
![]() |
2161357226 | ||
![]() |
e8e65a4293 | ||
![]() |
724adab2d6 | ||
![]() |
345ad6c9c5 | ||
![]() |
a88d066d7e | ||
![]() |
a8e5c8482b | ||
![]() |
d5ff8ab1e1 | ||
![]() |
e765cc10fb | ||
![]() |
916dec101f | ||
![]() |
909fc119b7 | ||
![]() |
8751dc46f4 | ||
![]() |
118c25d25f | ||
![]() |
ae5427a75e | ||
![]() |
3b6e267fb5 | ||
![]() |
1770a51303 | ||
![]() |
534df3d378 | ||
![]() |
23229b3e3b | ||
![]() |
94ee99160b | ||
![]() |
b009d71e8f | ||
![]() |
2ab8209622 | ||
![]() |
ed2940edc3 | ||
![]() |
e2b9a06242 | ||
![]() |
a7acee0438 | ||
![]() |
1208af510c |
@@ -309,7 +309,7 @@ export class HcMain extends HassElement {
|
|||||||
"../../../../src/panels/lovelace/strategies/get-strategy"
|
"../../../../src/panels/lovelace/strategies/get-strategy"
|
||||||
);
|
);
|
||||||
const config = await generateLovelaceDashboardStrategy(
|
const config = await generateLovelaceDashboardStrategy(
|
||||||
rawConfig.strategy,
|
rawConfig,
|
||||||
this.hass!
|
this.hass!
|
||||||
);
|
);
|
||||||
this._handleNewLovelaceConfig(config);
|
this._handleNewLovelaceConfig(config);
|
||||||
@@ -351,10 +351,7 @@ export class HcMain extends HassElement {
|
|||||||
"../../../../src/panels/lovelace/strategies/get-strategy"
|
"../../../../src/panels/lovelace/strategies/get-strategy"
|
||||||
);
|
);
|
||||||
this._handleNewLovelaceConfig(
|
this._handleNewLovelaceConfig(
|
||||||
await generateLovelaceDashboardStrategy(
|
await generateLovelaceDashboardStrategy(DEFAULT_CONFIG, this.hass!)
|
||||||
DEFAULT_CONFIG.strategy,
|
|
||||||
this.hass!
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "home-assistant-frontend"
|
name = "home-assistant-frontend"
|
||||||
version = "20250326.0"
|
version = "20250401.0"
|
||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
license-files = ["LICENSE*"]
|
license-files = ["LICENSE*"]
|
||||||
description = "The Home Assistant frontend"
|
description = "The Home Assistant frontend"
|
||||||
|
@@ -6,6 +6,10 @@ import {
|
|||||||
differenceInMilliseconds,
|
differenceInMilliseconds,
|
||||||
differenceInMonths,
|
differenceInMonths,
|
||||||
endOfMonth,
|
endOfMonth,
|
||||||
|
startOfDay,
|
||||||
|
endOfDay,
|
||||||
|
differenceInDays,
|
||||||
|
addDays,
|
||||||
} from "date-fns";
|
} from "date-fns";
|
||||||
import { toZonedTime, fromZonedTime } from "date-fns-tz";
|
import { toZonedTime, fromZonedTime } from "date-fns-tz";
|
||||||
import type { HassConfig } from "home-assistant-js-websocket";
|
import type { HassConfig } from "home-assistant-js-websocket";
|
||||||
@@ -100,6 +104,32 @@ export const shiftDateRange = (
|
|||||||
locale,
|
locale,
|
||||||
config
|
config
|
||||||
);
|
);
|
||||||
|
} else if (
|
||||||
|
calcDateProperty(
|
||||||
|
startDate,
|
||||||
|
(date) => startOfDay(date).getMilliseconds() === date.getMilliseconds(),
|
||||||
|
locale,
|
||||||
|
config
|
||||||
|
) &&
|
||||||
|
calcDateProperty(
|
||||||
|
endDate,
|
||||||
|
(date) => endOfDay(date).getMilliseconds() === date.getMilliseconds(),
|
||||||
|
locale,
|
||||||
|
config
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
const difference =
|
||||||
|
((calcDateDifferenceProperty(
|
||||||
|
endDate,
|
||||||
|
startDate,
|
||||||
|
differenceInDays,
|
||||||
|
locale,
|
||||||
|
config
|
||||||
|
) as number) +
|
||||||
|
1) *
|
||||||
|
(forward ? 1 : -1);
|
||||||
|
start = calcDate(startDate, addDays, locale, config, difference);
|
||||||
|
end = calcDate(endDate, addDays, locale, config, difference);
|
||||||
} else {
|
} else {
|
||||||
const difference =
|
const difference =
|
||||||
((calcDateDifferenceProperty(
|
((calcDateDifferenceProperty(
|
||||||
|
@@ -33,8 +33,15 @@ export const computeEntityEntryName = (
|
|||||||
const device = entry.device_id ? hass.devices[entry.device_id] : undefined;
|
const device = entry.device_id ? hass.devices[entry.device_id] : undefined;
|
||||||
|
|
||||||
if (!device) {
|
if (!device) {
|
||||||
|
if (name) {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
const stateObj = hass.states[entry.entity_id] as HassEntity | undefined;
|
||||||
|
if (stateObj) {
|
||||||
|
return computeStateName(stateObj);
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
const deviceName = computeDeviceName(device);
|
const deviceName = computeDeviceName(device);
|
||||||
|
|
||||||
|
@@ -1,7 +1,11 @@
|
|||||||
import type { HassEntity } from "home-assistant-js-websocket";
|
import type { HassEntity } from "home-assistant-js-websocket";
|
||||||
import type { AreaRegistryEntry } from "../../data/area_registry";
|
import type { AreaRegistryEntry } from "../../data/area_registry";
|
||||||
import type { DeviceRegistryEntry } from "../../data/device_registry";
|
import type { DeviceRegistryEntry } from "../../data/device_registry";
|
||||||
import type { EntityRegistryDisplayEntry } from "../../data/entity_registry";
|
import type {
|
||||||
|
EntityRegistryDisplayEntry,
|
||||||
|
EntityRegistryEntry,
|
||||||
|
ExtEntityRegistryEntry,
|
||||||
|
} from "../../data/entity_registry";
|
||||||
import type { FloorRegistryEntry } from "../../data/floor_registry";
|
import type { FloorRegistryEntry } from "../../data/floor_registry";
|
||||||
import type { HomeAssistant } from "../../types";
|
import type { HomeAssistant } from "../../types";
|
||||||
|
|
||||||
@@ -19,6 +23,23 @@ export const getEntityContext = (
|
|||||||
| EntityRegistryDisplayEntry
|
| EntityRegistryDisplayEntry
|
||||||
| undefined;
|
| undefined;
|
||||||
|
|
||||||
|
if (!entry) {
|
||||||
|
return {
|
||||||
|
device: null,
|
||||||
|
area: null,
|
||||||
|
floor: null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return getEntityEntryContext(entry, hass);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getEntityEntryContext = (
|
||||||
|
entry:
|
||||||
|
| EntityRegistryDisplayEntry
|
||||||
|
| EntityRegistryEntry
|
||||||
|
| ExtEntityRegistryEntry,
|
||||||
|
hass: HomeAssistant
|
||||||
|
): EntityContext => {
|
||||||
const deviceId = entry?.device_id;
|
const deviceId = entry?.device_id;
|
||||||
const device = deviceId ? hass.devices[deviceId] : null;
|
const device = deviceId ? hass.devices[deviceId] : null;
|
||||||
const areaId = entry?.area_id || device?.area_id;
|
const areaId = entry?.area_id || device?.area_id;
|
||||||
|
@@ -1,13 +1,15 @@
|
|||||||
import "@material/mwc-button";
|
|
||||||
import { mdiAlertOctagram, mdiCheckBold } from "@mdi/js";
|
import { mdiAlertOctagram, mdiCheckBold } from "@mdi/js";
|
||||||
import type { TemplateResult } from "lit";
|
import type { TemplateResult } from "lit";
|
||||||
import { css, html, LitElement, nothing } from "lit";
|
import { css, html, LitElement, nothing } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
|
import "../ha-button";
|
||||||
import "../ha-spinner";
|
import "../ha-spinner";
|
||||||
import "../ha-svg-icon";
|
import "../ha-svg-icon";
|
||||||
|
|
||||||
@customElement("ha-progress-button")
|
@customElement("ha-progress-button")
|
||||||
export class HaProgressButton extends LitElement {
|
export class HaProgressButton extends LitElement {
|
||||||
|
@property() public label?: string;
|
||||||
|
|
||||||
@property({ type: Boolean }) public disabled = false;
|
@property({ type: Boolean }) public disabled = false;
|
||||||
|
|
||||||
@property({ type: Boolean }) public progress = false;
|
@property({ type: Boolean }) public progress = false;
|
||||||
@@ -21,14 +23,16 @@ export class HaProgressButton extends LitElement {
|
|||||||
public render(): TemplateResult {
|
public render(): TemplateResult {
|
||||||
const overlay = this._result || this.progress;
|
const overlay = this._result || this.progress;
|
||||||
return html`
|
return html`
|
||||||
<mwc-button
|
<ha-button
|
||||||
?raised=${this.raised}
|
.raised=${this.raised}
|
||||||
|
.label=${this.label}
|
||||||
.unelevated=${this.unelevated}
|
.unelevated=${this.unelevated}
|
||||||
.disabled=${this.disabled || this.progress}
|
.disabled=${this.disabled || this.progress}
|
||||||
class=${this._result || ""}
|
class=${this._result || ""}
|
||||||
>
|
>
|
||||||
|
<slot name="icon" slot="icon"></slot>
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</mwc-button>
|
</ha-button>
|
||||||
${!overlay
|
${!overlay
|
||||||
? nothing
|
? nothing
|
||||||
: html`
|
: html`
|
||||||
@@ -68,12 +72,12 @@ export class HaProgressButton extends LitElement {
|
|||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
mwc-button {
|
ha-button {
|
||||||
transition: all 1s;
|
transition: all 1s;
|
||||||
pointer-events: initial;
|
pointer-events: initial;
|
||||||
}
|
}
|
||||||
|
|
||||||
mwc-button.success {
|
ha-button.success {
|
||||||
--mdc-theme-primary: white;
|
--mdc-theme-primary: white;
|
||||||
background-color: var(--success-color);
|
background-color: var(--success-color);
|
||||||
transition: none;
|
transition: none;
|
||||||
@@ -81,13 +85,13 @@ export class HaProgressButton extends LitElement {
|
|||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
mwc-button[unelevated].success,
|
ha-button[unelevated].success,
|
||||||
mwc-button[raised].success {
|
ha-button[raised].success {
|
||||||
--mdc-theme-primary: var(--success-color);
|
--mdc-theme-primary: var(--success-color);
|
||||||
--mdc-theme-on-primary: white;
|
--mdc-theme-on-primary: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
mwc-button.error {
|
ha-button.error {
|
||||||
--mdc-theme-primary: white;
|
--mdc-theme-primary: white;
|
||||||
background-color: var(--error-color);
|
background-color: var(--error-color);
|
||||||
transition: none;
|
transition: none;
|
||||||
@@ -95,8 +99,8 @@ export class HaProgressButton extends LitElement {
|
|||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
mwc-button[unelevated].error,
|
ha-button[unelevated].error,
|
||||||
mwc-button[raised].error {
|
ha-button[raised].error {
|
||||||
--mdc-theme-primary: var(--error-color);
|
--mdc-theme-primary: var(--error-color);
|
||||||
--mdc-theme-on-primary: white;
|
--mdc-theme-on-primary: white;
|
||||||
}
|
}
|
||||||
@@ -113,8 +117,8 @@ export class HaProgressButton extends LitElement {
|
|||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
mwc-button.success slot,
|
ha-button.success slot,
|
||||||
mwc-button.error slot {
|
ha-button.error slot {
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
}
|
}
|
||||||
:host([destructive]) {
|
:host([destructive]) {
|
||||||
|
@@ -296,7 +296,11 @@ export class StatisticsChart extends LitElement {
|
|||||||
align: "left",
|
align: "left",
|
||||||
},
|
},
|
||||||
position: computeRTL(this.hass) ? "right" : "left",
|
position: computeRTL(this.hass) ? "right" : "left",
|
||||||
scale: true,
|
scale:
|
||||||
|
this.chartType !== "bar" ||
|
||||||
|
this.logarithmicScale ||
|
||||||
|
minYAxis !== undefined ||
|
||||||
|
maxYAxis !== undefined,
|
||||||
min: this._clampYAxis(minYAxis),
|
min: this._clampYAxis(minYAxis),
|
||||||
max: this._clampYAxis(maxYAxis),
|
max: this._clampYAxis(maxYAxis),
|
||||||
splitLine: {
|
splitLine: {
|
||||||
|
@@ -211,36 +211,12 @@ export class HaRelatedItems extends LitElement {
|
|||||||
)}
|
)}
|
||||||
</mwc-list>`
|
</mwc-list>`
|
||||||
: nothing}
|
: nothing}
|
||||||
${this._related.device
|
|
||||||
? html`<h3>
|
|
||||||
${this.hass.localize("ui.components.related-items.device")}
|
|
||||||
</h3>
|
|
||||||
${this._related.device.map((relatedDeviceId) => {
|
|
||||||
const device = this.hass.devices[relatedDeviceId];
|
|
||||||
if (!device) {
|
|
||||||
return nothing;
|
|
||||||
}
|
|
||||||
return html`
|
|
||||||
<a href="/config/devices/device/${relatedDeviceId}">
|
|
||||||
<ha-list-item hasMeta graphic="icon">
|
|
||||||
<ha-svg-icon
|
|
||||||
.path=${mdiDevices}
|
|
||||||
slot="graphic"
|
|
||||||
></ha-svg-icon>
|
|
||||||
${device.name_by_user || device.name}
|
|
||||||
<ha-icon-next slot="meta"></ha-icon-next>
|
|
||||||
</ha-list-item>
|
|
||||||
</a>
|
|
||||||
`;
|
|
||||||
})} </mwc-list>
|
|
||||||
`
|
|
||||||
: nothing}
|
|
||||||
${this._related.area
|
${this._related.area
|
||||||
? html`<h3>
|
? html`<h3>
|
||||||
${this.hass.localize("ui.components.related-items.area")}
|
${this.hass.localize("ui.components.related-items.area")}
|
||||||
</h3>
|
</h3>
|
||||||
<mwc-list
|
<mwc-list>
|
||||||
>${this._related.area.map((relatedAreaId) => {
|
${this._related.area.map((relatedAreaId) => {
|
||||||
const area = this.hass.areas[relatedAreaId];
|
const area = this.hass.areas[relatedAreaId];
|
||||||
if (!area) {
|
if (!area) {
|
||||||
return nothing;
|
return nothing;
|
||||||
@@ -268,8 +244,33 @@ export class HaRelatedItems extends LitElement {
|
|||||||
</ha-list-item>
|
</ha-list-item>
|
||||||
</a>
|
</a>
|
||||||
`;
|
`;
|
||||||
})}</mwc-list
|
})}
|
||||||
>`
|
</mwc-list>`
|
||||||
|
: nothing}
|
||||||
|
${this._related.device
|
||||||
|
? html`<h3>
|
||||||
|
${this.hass.localize("ui.components.related-items.device")}
|
||||||
|
</h3>
|
||||||
|
<mwc-list>
|
||||||
|
${this._related.device.map((relatedDeviceId) => {
|
||||||
|
const device = this.hass.devices[relatedDeviceId];
|
||||||
|
if (!device) {
|
||||||
|
return nothing;
|
||||||
|
}
|
||||||
|
return html`
|
||||||
|
<a href="/config/devices/device/${relatedDeviceId}">
|
||||||
|
<ha-list-item hasMeta graphic="icon">
|
||||||
|
<ha-svg-icon
|
||||||
|
.path=${mdiDevices}
|
||||||
|
slot="graphic"
|
||||||
|
></ha-svg-icon>
|
||||||
|
${device.name_by_user || device.name}
|
||||||
|
<ha-icon-next slot="meta"></ha-icon-next>
|
||||||
|
</ha-list-item>
|
||||||
|
</a>
|
||||||
|
`;
|
||||||
|
})}
|
||||||
|
</mwc-list>`
|
||||||
: nothing}
|
: nothing}
|
||||||
${this._related.entity
|
${this._related.entity
|
||||||
? html`
|
? html`
|
||||||
|
@@ -69,11 +69,14 @@ export class HaTemplateSelector extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _handleChange(ev) {
|
private _handleChange(ev) {
|
||||||
const value = ev.target.value;
|
let value = ev.target.value;
|
||||||
if (this.value === value) {
|
if (this.value === value) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.warn = WARNING_STRINGS.find((str) => value.includes(str));
|
this.warn = WARNING_STRINGS.find((str) => value.includes(str));
|
||||||
|
if (value === "" && !this.required) {
|
||||||
|
value = undefined;
|
||||||
|
}
|
||||||
fireEvent(this, "value-changed", { value });
|
fireEvent(this, "value-changed", { value });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -26,7 +26,6 @@ export class HaTileInfo extends LitElement {
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
height: 36px;
|
|
||||||
}
|
}
|
||||||
span {
|
span {
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
|
@@ -186,7 +186,10 @@ export class HatScriptGraph extends LitElement {
|
|||||||
? ensureArray(config.choose)?.map((branch, i) => {
|
? ensureArray(config.choose)?.map((branch, i) => {
|
||||||
const branchPath = `${path}/choose/${i}`;
|
const branchPath = `${path}/choose/${i}`;
|
||||||
const trackThis = tracePath.includes(i);
|
const trackThis = tracePath.includes(i);
|
||||||
this.renderedNodes[branchPath] = { config, path: branchPath };
|
this.renderedNodes[branchPath] = {
|
||||||
|
config: branch,
|
||||||
|
path: branchPath,
|
||||||
|
};
|
||||||
if (trackThis) {
|
if (trackThis) {
|
||||||
this.trackedNodes[branchPath] = this.renderedNodes[branchPath];
|
this.trackedNodes[branchPath] = this.renderedNodes[branchPath];
|
||||||
}
|
}
|
||||||
@@ -196,7 +199,7 @@ export class HatScriptGraph extends LitElement {
|
|||||||
.iconPath=${!trace || trackThis
|
.iconPath=${!trace || trackThis
|
||||||
? mdiCheckboxMarkedOutline
|
? mdiCheckboxMarkedOutline
|
||||||
: mdiCheckboxBlankOutline}
|
: mdiCheckboxBlankOutline}
|
||||||
@focus=${this._selectNode(config, branchPath)}
|
@focus=${this._selectNode(branch, branchPath)}
|
||||||
?track=${trackThis}
|
?track=${trackThis}
|
||||||
?active=${this.selected === branchPath}
|
?active=${this.selected === branchPath}
|
||||||
.notEnabled=${disabled || config.enabled === false}
|
.notEnabled=${disabled || config.enabled === false}
|
||||||
|
@@ -49,9 +49,12 @@ export const testAssistSatelliteConnection = (
|
|||||||
export const assistSatelliteAnnounce = (
|
export const assistSatelliteAnnounce = (
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
entity_id: string,
|
entity_id: string,
|
||||||
message: string
|
args: {
|
||||||
) =>
|
message?: string;
|
||||||
hass.callService("assist_satellite", "announce", { message }, { entity_id });
|
media_id?: string;
|
||||||
|
preannounce_media_id?: string | null;
|
||||||
|
}
|
||||||
|
) => hass.callService("assist_satellite", "announce", args, { entity_id });
|
||||||
|
|
||||||
export const fetchAssistSatelliteConfiguration = (
|
export const fetchAssistSatelliteConfiguration = (
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
|
@@ -38,7 +38,7 @@ export interface Statistic {
|
|||||||
|
|
||||||
export enum StatisticMeanType {
|
export enum StatisticMeanType {
|
||||||
NONE = 0,
|
NONE = 0,
|
||||||
ARIMETHIC = 1,
|
ARITHMETIC = 1,
|
||||||
CIRCULAR = 2,
|
CIRCULAR = 2,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -21,8 +21,10 @@ import { stopPropagation } from "../../common/dom/stop_propagation";
|
|||||||
import { computeAreaName } from "../../common/entity/compute_area_name";
|
import { computeAreaName } from "../../common/entity/compute_area_name";
|
||||||
import { computeDeviceName } from "../../common/entity/compute_device_name";
|
import { computeDeviceName } from "../../common/entity/compute_device_name";
|
||||||
import { computeDomain } from "../../common/entity/compute_domain";
|
import { computeDomain } from "../../common/entity/compute_domain";
|
||||||
import { computeEntityName } from "../../common/entity/compute_entity_name";
|
import {
|
||||||
import { getEntityContext } from "../../common/entity/get_entity_context";
|
computeEntityEntryName,
|
||||||
|
computeEntityName,
|
||||||
|
} from "../../common/entity/compute_entity_name";
|
||||||
import { shouldHandleRequestSelectedEvent } from "../../common/mwc/handle-request-selected-event";
|
import { shouldHandleRequestSelectedEvent } from "../../common/mwc/handle-request-selected-event";
|
||||||
import { navigate } from "../../common/navigate";
|
import { navigate } from "../../common/navigate";
|
||||||
import "../../components/ha-button-menu";
|
import "../../components/ha-button-menu";
|
||||||
@@ -56,6 +58,10 @@ import "./ha-more-info-history-and-logbook";
|
|||||||
import "./ha-more-info-info";
|
import "./ha-more-info-info";
|
||||||
import "./ha-more-info-settings";
|
import "./ha-more-info-settings";
|
||||||
import "./more-info-content";
|
import "./more-info-content";
|
||||||
|
import {
|
||||||
|
getEntityContext,
|
||||||
|
getEntityEntryContext,
|
||||||
|
} from "../../common/entity/get_entity_context";
|
||||||
|
|
||||||
export interface MoreInfoDialogParams {
|
export interface MoreInfoDialogParams {
|
||||||
entityId: string | null;
|
entityId: string | null;
|
||||||
@@ -270,6 +276,11 @@ export class MoreInfoDialog extends LitElement {
|
|||||||
this._setView("related");
|
this._setView("related");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _breadcrumbClick(ev: Event) {
|
||||||
|
ev.stopPropagation();
|
||||||
|
this._setView("related");
|
||||||
|
}
|
||||||
|
|
||||||
private async _loadNumericDeviceClasses() {
|
private async _loadNumericDeviceClasses() {
|
||||||
const deviceClasses = await getSensorNumericDeviceClasses(this.hass);
|
const deviceClasses = await getSensorNumericDeviceClasses(this.hass);
|
||||||
this._sensorNumericDeviceClasses = deviceClasses.numeric_device_classes;
|
this._sensorNumericDeviceClasses = deviceClasses.numeric_device_classes;
|
||||||
@@ -293,11 +304,18 @@ export class MoreInfoDialog extends LitElement {
|
|||||||
this._initialView !== DEFAULT_VIEW && !this._childView;
|
this._initialView !== DEFAULT_VIEW && !this._childView;
|
||||||
const showCloseIcon = isDefaultView || isSpecificInitialView;
|
const showCloseIcon = isDefaultView || isSpecificInitialView;
|
||||||
|
|
||||||
const context = stateObj ? getEntityContext(stateObj, this.hass) : null;
|
const context = stateObj
|
||||||
|
? getEntityContext(stateObj, this.hass)
|
||||||
|
: this._entry
|
||||||
|
? getEntityEntryContext(this._entry, this.hass)
|
||||||
|
: undefined;
|
||||||
|
|
||||||
const entityName = stateObj
|
const entityName = stateObj
|
||||||
? computeEntityName(stateObj, this.hass)
|
? computeEntityName(stateObj, this.hass)
|
||||||
: undefined;
|
: this._entry
|
||||||
|
? computeEntityEntryName(this._entry, this.hass)
|
||||||
|
: entityId;
|
||||||
|
|
||||||
const deviceName = context?.device
|
const deviceName = context?.device
|
||||||
? computeDeviceName(context.device)
|
? computeDeviceName(context.device)
|
||||||
: undefined;
|
: undefined;
|
||||||
@@ -306,7 +324,7 @@ export class MoreInfoDialog extends LitElement {
|
|||||||
const breadcrumb = [areaName, deviceName, entityName].filter(
|
const breadcrumb = [areaName, deviceName, entityName].filter(
|
||||||
(v): v is string => Boolean(v)
|
(v): v is string => Boolean(v)
|
||||||
);
|
);
|
||||||
const title = this._childView?.viewTitle || breadcrumb.pop();
|
const title = this._childView?.viewTitle || breadcrumb.pop() || entityId;
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<ha-dialog
|
<ha-dialog
|
||||||
@@ -337,14 +355,19 @@ export class MoreInfoDialog extends LitElement {
|
|||||||
)}
|
)}
|
||||||
></ha-icon-button-prev>
|
></ha-icon-button-prev>
|
||||||
`}
|
`}
|
||||||
<span
|
<span slot="title" @click=${this._enlarge} class="title">
|
||||||
slot="title"
|
|
||||||
.title=${title}
|
|
||||||
@click=${this._enlarge}
|
|
||||||
class="title"
|
|
||||||
>
|
|
||||||
${breadcrumb.length > 0
|
${breadcrumb.length > 0
|
||||||
|
? !__DEMO__ && isAdmin
|
||||||
? html`
|
? html`
|
||||||
|
<button
|
||||||
|
class="breadcrumb"
|
||||||
|
@click=${this._breadcrumbClick}
|
||||||
|
aria-label=${breadcrumb.join(" > ")}
|
||||||
|
>
|
||||||
|
${join(breadcrumb, html`<ha-icon-next></ha-icon-next>`)}
|
||||||
|
</button>
|
||||||
|
`
|
||||||
|
: html`
|
||||||
<p class="breadcrumb">
|
<p class="breadcrumb">
|
||||||
${join(breadcrumb, html`<ha-icon-next></ha-icon-next>`)}
|
${join(breadcrumb, html`<ha-icon-next></ha-icon-next>`)}
|
||||||
</p>
|
</p>
|
||||||
@@ -643,6 +666,7 @@ export class MoreInfoDialog extends LitElement {
|
|||||||
.title {
|
.title {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
}
|
}
|
||||||
|
|
||||||
.title p {
|
.title p {
|
||||||
@@ -663,11 +687,30 @@ export class MoreInfoDialog extends LitElement {
|
|||||||
color: var(--secondary-text-color);
|
color: var(--secondary-text-color);
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
line-height: 16px;
|
line-height: 16px;
|
||||||
margin-top: -6px;
|
--mdc-icon-size: 16px;
|
||||||
|
padding: 4px;
|
||||||
|
margin: -4px;
|
||||||
|
margin-top: -10px;
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
outline: none;
|
||||||
|
display: inline;
|
||||||
|
border-radius: 6px;
|
||||||
|
transition: background-color 180ms ease-in-out;
|
||||||
|
min-width: 0;
|
||||||
|
max-width: 100%;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
text-align: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
.title .breadcrumb {
|
.title button.breadcrumb {
|
||||||
--mdc-icon-size: 16px;
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title button.breadcrumb:focus-visible,
|
||||||
|
.title button.breadcrumb:hover {
|
||||||
|
background-color: rgba(var(--rgb-secondary-text-color), 0.08);
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
];
|
];
|
||||||
|
@@ -3,7 +3,6 @@ import { css, html, LitElement, nothing } from "lit";
|
|||||||
import { customElement, property, query, state } from "lit/decorators";
|
import { customElement, property, query, state } from "lit/decorators";
|
||||||
import { storage } from "../../common/decorators/storage";
|
import { storage } from "../../common/decorators/storage";
|
||||||
import { fireEvent } from "../../common/dom/fire_event";
|
import { fireEvent } from "../../common/dom/fire_event";
|
||||||
import "../../components/ha-button";
|
|
||||||
import { createCloseHeading } from "../../components/ha-dialog";
|
import { createCloseHeading } from "../../components/ha-dialog";
|
||||||
import "../../components/ha-textarea";
|
import "../../components/ha-textarea";
|
||||||
import type { HaTextArea } from "../../components/ha-textarea";
|
import type { HaTextArea } from "../../components/ha-textarea";
|
||||||
@@ -11,7 +10,7 @@ import { convertTextToSpeech } from "../../data/tts";
|
|||||||
import type { HomeAssistant } from "../../types";
|
import type { HomeAssistant } from "../../types";
|
||||||
import { showAlertDialog } from "../generic/show-dialog-box";
|
import { showAlertDialog } from "../generic/show-dialog-box";
|
||||||
import type { TTSTryDialogParams } from "./show-dialog-tts-try";
|
import type { TTSTryDialogParams } from "./show-dialog-tts-try";
|
||||||
import "../../components/ha-spinner";
|
import "../../components/buttons/ha-progress-button";
|
||||||
|
|
||||||
@customElement("dialog-tts-try")
|
@customElement("dialog-tts-try")
|
||||||
export class TTSTryDialog extends LitElement {
|
export class TTSTryDialog extends LitElement {
|
||||||
@@ -81,28 +80,17 @@ export class TTSTryDialog extends LitElement {
|
|||||||
?dialogInitialFocus=${!this._defaultMessage}
|
?dialogInitialFocus=${!this._defaultMessage}
|
||||||
>
|
>
|
||||||
</ha-textarea>
|
</ha-textarea>
|
||||||
${this._loadingExample
|
|
||||||
? html`
|
<ha-progress-button
|
||||||
<ha-spinner
|
.progress=${this._loadingExample}
|
||||||
size="small"
|
|
||||||
slot="primaryAction"
|
|
||||||
class="loading"
|
|
||||||
></ha-spinner>
|
|
||||||
`
|
|
||||||
: html`
|
|
||||||
<ha-button
|
|
||||||
?dialogInitialFocus=${Boolean(this._defaultMessage)}
|
?dialogInitialFocus=${Boolean(this._defaultMessage)}
|
||||||
slot="primaryAction"
|
slot="primaryAction"
|
||||||
.label=${this.hass.localize("ui.dialogs.tts-try.play")}
|
.label=${this.hass.localize("ui.dialogs.tts-try.play")}
|
||||||
@click=${this._playExample}
|
@click=${this._playExample}
|
||||||
.disabled=${!this._valid}
|
.disabled=${!this._valid}
|
||||||
>
|
>
|
||||||
<ha-svg-icon
|
<ha-svg-icon slot="icon" .path=${mdiPlayCircleOutline}></ha-svg-icon>
|
||||||
slot="icon"
|
</ha-progress-button>
|
||||||
.path=${mdiPlayCircleOutline}
|
|
||||||
></ha-svg-icon>
|
|
||||||
</ha-button>
|
|
||||||
`}
|
|
||||||
</ha-dialog>
|
</ha-dialog>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
@@ -243,7 +243,7 @@ export class HaVoiceAssistantSetupStepLocal extends LitElement {
|
|||||||
|
|
||||||
private readonly _ttsHostName = "core-piper";
|
private readonly _ttsHostName = "core-piper";
|
||||||
|
|
||||||
private readonly _ttsPort = "10200";
|
private readonly _ttsPort = 10200;
|
||||||
|
|
||||||
private get _sttProviderName() {
|
private get _sttProviderName() {
|
||||||
return this.localOption === "focused_local"
|
return this.localOption === "focused_local"
|
||||||
@@ -263,7 +263,7 @@ export class HaVoiceAssistantSetupStepLocal extends LitElement {
|
|||||||
: "core-whisper";
|
: "core-whisper";
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly _sttPort = "10300";
|
private readonly _sttPort = 10300;
|
||||||
|
|
||||||
private async _findLocalEntities() {
|
private async _findLocalEntities() {
|
||||||
const wyomingEntities = Object.values(this.hass.entities).filter(
|
const wyomingEntities = Object.values(this.hass.entities).filter(
|
||||||
@@ -325,14 +325,16 @@ export class HaVoiceAssistantSetupStepLocal extends LitElement {
|
|||||||
(flow) =>
|
(flow) =>
|
||||||
flow.handler === "wyoming" &&
|
flow.handler === "wyoming" &&
|
||||||
flow.context.source === "hassio" &&
|
flow.context.source === "hassio" &&
|
||||||
(flow.context.configuration_url.includes(
|
((flow.context.configuration_url &&
|
||||||
type === "tts" ? this._ttsHostName : this._sttHostName
|
flow.context.configuration_url.includes(
|
||||||
) ||
|
type === "tts" ? this._ttsAddonName : this._sttAddonName
|
||||||
flow.context.title_placeholders.title
|
)) ||
|
||||||
|
(flow.context.title_placeholders.name &&
|
||||||
|
flow.context.title_placeholders.name
|
||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
.includes(
|
.includes(
|
||||||
type === "tts" ? this._ttsProviderName : this._sttProviderName
|
type === "tts" ? this._ttsProviderName : this._sttProviderName
|
||||||
))
|
)))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -357,40 +359,24 @@ export class HaVoiceAssistantSetupStepLocal extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const pipelines = await listAssistPipelines(this.hass);
|
const pipelines = await listAssistPipelines(this.hass);
|
||||||
const preferredPipeline = pipelines.pipelines.find(
|
|
||||||
(pipeline) => pipeline.id === pipelines.preferred_pipeline
|
if (pipelines.preferred_pipeline) {
|
||||||
|
pipelines.pipelines.sort((a) =>
|
||||||
|
a.id === pipelines.preferred_pipeline ? -1 : 0
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const ttsEntityIds = this._localTts.map((ent) => ent.entity_id);
|
const ttsEntityIds = this._localTts.map((ent) => ent.entity_id);
|
||||||
const sttEntityIds = this._localStt.map((ent) => ent.entity_id);
|
const sttEntityIds = this._localStt.map((ent) => ent.entity_id);
|
||||||
|
|
||||||
if (preferredPipeline) {
|
|
||||||
if (
|
|
||||||
preferredPipeline.conversation_engine ===
|
|
||||||
"conversation.home_assistant" &&
|
|
||||||
preferredPipeline.tts_engine &&
|
|
||||||
ttsEntityIds.includes(preferredPipeline.tts_engine) &&
|
|
||||||
preferredPipeline.stt_engine &&
|
|
||||||
sttEntityIds.includes(preferredPipeline.stt_engine)
|
|
||||||
) {
|
|
||||||
await this.hass.callService(
|
|
||||||
"select",
|
|
||||||
"select_option",
|
|
||||||
{ option: "preferred" },
|
|
||||||
{ entity_id: this.assistConfiguration?.pipeline_entity_id }
|
|
||||||
);
|
|
||||||
this._nextStep();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let localPipeline = pipelines.pipelines.find(
|
let localPipeline = pipelines.pipelines.find(
|
||||||
(pipeline) =>
|
(pipeline) =>
|
||||||
pipeline.conversation_engine === "conversation.home_assistant" &&
|
pipeline.conversation_engine === "conversation.home_assistant" &&
|
||||||
pipeline.tts_engine &&
|
pipeline.tts_engine &&
|
||||||
ttsEntityIds.includes(pipeline.tts_engine) &&
|
ttsEntityIds.includes(pipeline.tts_engine) &&
|
||||||
pipeline.stt_engine &&
|
pipeline.stt_engine &&
|
||||||
sttEntityIds.includes(pipeline.stt_engine)
|
sttEntityIds.includes(pipeline.stt_engine) &&
|
||||||
|
pipeline.language.split("-")[0] === this.language.split("-")[0]
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!localPipeline) {
|
if (!localPipeline) {
|
||||||
@@ -463,7 +449,7 @@ export class HaVoiceAssistantSetupStepLocal extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let pipelineName = this.hass.localize(
|
let pipelineName = this.hass.localize(
|
||||||
"ui.panel.config.voice_assistants.satellite_wizard.local.local_pipeline"
|
`ui.panel.config.voice_assistants.satellite_wizard.local.${this.localOption}_pipeline`
|
||||||
);
|
);
|
||||||
let i = 1;
|
let i = 1;
|
||||||
while (
|
while (
|
||||||
@@ -472,7 +458,7 @@ export class HaVoiceAssistantSetupStepLocal extends LitElement {
|
|||||||
(pipeline) => pipeline.name === pipelineName
|
(pipeline) => pipeline.name === pipelineName
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
pipelineName = `${this.hass.localize("ui.panel.config.voice_assistants.satellite_wizard.local.local_pipeline")} ${i}`;
|
pipelineName = `${this.hass.localize(`ui.panel.config.voice_assistants.satellite_wizard.local.${this.localOption}_pipeline`)} ${i}`;
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -15,7 +15,7 @@ import {
|
|||||||
} from "../../data/assist_pipeline";
|
} from "../../data/assist_pipeline";
|
||||||
import type { AssistSatelliteConfiguration } from "../../data/assist_satellite";
|
import type { AssistSatelliteConfiguration } from "../../data/assist_satellite";
|
||||||
import { fetchCloudStatus } from "../../data/cloud";
|
import { fetchCloudStatus } from "../../data/cloud";
|
||||||
import type { LanguageScores } from "../../data/conversation";
|
import type { LanguageScore, LanguageScores } from "../../data/conversation";
|
||||||
import { getLanguageScores, listAgents } from "../../data/conversation";
|
import { getLanguageScores, listAgents } from "../../data/conversation";
|
||||||
import { listSTTEngines } from "../../data/stt";
|
import { listSTTEngines } from "../../data/stt";
|
||||||
import { listTTSEngines, listTTSVoices } from "../../data/tts";
|
import { listTTSEngines, listTTSVoices } from "../../data/tts";
|
||||||
@@ -26,6 +26,12 @@ import { documentationUrl } from "../../util/documentation-url";
|
|||||||
|
|
||||||
const OPTIONS = ["cloud", "focused_local", "full_local"] as const;
|
const OPTIONS = ["cloud", "focused_local", "full_local"] as const;
|
||||||
|
|
||||||
|
const EMPTY_SCORE: LanguageScore = {
|
||||||
|
cloud: 0,
|
||||||
|
focused_local: 0,
|
||||||
|
full_local: 0,
|
||||||
|
};
|
||||||
|
|
||||||
@customElement("ha-voice-assistant-setup-step-pipeline")
|
@customElement("ha-voice-assistant-setup-step-pipeline")
|
||||||
export class HaVoiceAssistantSetupStepPipeline extends LitElement {
|
export class HaVoiceAssistantSetupStepPipeline extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
@@ -61,12 +67,12 @@ export class HaVoiceAssistantSetupStepPipeline extends LitElement {
|
|||||||
this._languageScores
|
this._languageScores
|
||||||
) {
|
) {
|
||||||
const lang = this.language;
|
const lang = this.language;
|
||||||
if (this._value && this._languageScores[lang][this._value] === 0) {
|
if (this._value && this._languageScores[lang]?.[this._value] === 0) {
|
||||||
this._value = undefined;
|
this._value = undefined;
|
||||||
}
|
}
|
||||||
if (!this._value) {
|
if (!this._value) {
|
||||||
this._value = this._getOptions(
|
this._value = this._getOptions(
|
||||||
this._languageScores[lang],
|
this._languageScores[lang] || EMPTY_SCORE,
|
||||||
this.hass.localize
|
this.hass.localize
|
||||||
).supportedOptions[0]?.value as
|
).supportedOptions[0]?.value as
|
||||||
| "cloud"
|
| "cloud"
|
||||||
@@ -147,12 +153,9 @@ export class HaVoiceAssistantSetupStepPipeline extends LitElement {
|
|||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const score = this._languageScores[this.language];
|
const score = this._languageScores[this.language] || EMPTY_SCORE;
|
||||||
|
|
||||||
const options = this._getOptions(
|
const options = this._getOptions(score, this.hass.localize);
|
||||||
score || { cloud: 3, focused_local: 0, full_local: 0 },
|
|
||||||
this.hass.localize
|
|
||||||
);
|
|
||||||
|
|
||||||
const performance = !this._value
|
const performance = !this._value
|
||||||
? ""
|
? ""
|
||||||
@@ -162,11 +165,11 @@ export class HaVoiceAssistantSetupStepPipeline extends LitElement {
|
|||||||
|
|
||||||
const commands = !this._value
|
const commands = !this._value
|
||||||
? ""
|
? ""
|
||||||
: score?.[this._value] > 2
|
: score[this._value] > 2
|
||||||
? "high"
|
? "high"
|
||||||
: score?.[this._value] > 1
|
: score[this._value] > 1
|
||||||
? "ready"
|
? "ready"
|
||||||
: score?.[this._value] > 0
|
: score[this._value] > 0
|
||||||
? "low"
|
? "low"
|
||||||
: "";
|
: "";
|
||||||
|
|
||||||
@@ -243,7 +246,7 @@ export class HaVoiceAssistantSetupStepPipeline extends LitElement {
|
|||||||
|
|
||||||
private async _fetchData() {
|
private async _fetchData() {
|
||||||
const cloud =
|
const cloud =
|
||||||
(await this._hasCloud()) && (await this._createCloudPipeline());
|
(await this._hasCloud()) && (await this._createCloudPipeline(false));
|
||||||
if (!cloud) {
|
if (!cloud) {
|
||||||
this._cloudChecked = true;
|
this._cloudChecked = true;
|
||||||
this._languageScores = (await getLanguageScores(this.hass)).languages;
|
this._languageScores = (await getLanguageScores(this.hass)).languages;
|
||||||
@@ -261,7 +264,7 @@ export class HaVoiceAssistantSetupStepPipeline extends LitElement {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _createCloudPipeline(): Promise<boolean> {
|
private async _createCloudPipeline(useLanguage: boolean): Promise<boolean> {
|
||||||
let cloudTtsEntityId;
|
let cloudTtsEntityId;
|
||||||
let cloudSttEntityId;
|
let cloudSttEntityId;
|
||||||
for (const entity of Object.values(this.hass.entities)) {
|
for (const entity of Object.values(this.hass.entities)) {
|
||||||
@@ -281,36 +284,20 @@ export class HaVoiceAssistantSetupStepPipeline extends LitElement {
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const pipelines = await listAssistPipelines(this.hass);
|
const pipelines = await listAssistPipelines(this.hass);
|
||||||
const preferredPipeline = pipelines.pipelines.find(
|
|
||||||
(pipeline) => pipeline.id === pipelines.preferred_pipeline
|
|
||||||
);
|
|
||||||
|
|
||||||
if (preferredPipeline) {
|
if (pipelines.preferred_pipeline) {
|
||||||
if (
|
pipelines.pipelines.sort((a) =>
|
||||||
preferredPipeline.conversation_engine ===
|
a.id === pipelines.preferred_pipeline ? -1 : 0
|
||||||
"conversation.home_assistant" &&
|
|
||||||
preferredPipeline.tts_engine === cloudTtsEntityId &&
|
|
||||||
preferredPipeline.stt_engine === cloudSttEntityId
|
|
||||||
) {
|
|
||||||
await this.hass.callService(
|
|
||||||
"select",
|
|
||||||
"select_option",
|
|
||||||
{ option: "preferred" },
|
|
||||||
{ entity_id: this.assistConfiguration?.pipeline_entity_id }
|
|
||||||
);
|
);
|
||||||
fireEvent(this, "next-step", {
|
|
||||||
step: STEP.SUCCESS,
|
|
||||||
noPrevious: true,
|
|
||||||
});
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let cloudPipeline = pipelines.pipelines.find(
|
let cloudPipeline = pipelines.pipelines.find(
|
||||||
(pipeline) =>
|
(pipeline) =>
|
||||||
pipeline.conversation_engine === "conversation.home_assistant" &&
|
pipeline.conversation_engine === "conversation.home_assistant" &&
|
||||||
pipeline.tts_engine === cloudTtsEntityId &&
|
pipeline.tts_engine === cloudTtsEntityId &&
|
||||||
pipeline.stt_engine === cloudSttEntityId
|
pipeline.stt_engine === cloudSttEntityId &&
|
||||||
|
(!useLanguage ||
|
||||||
|
pipeline.language.split("-")[0] === this.language!.split("-")[0])
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!cloudPipeline) {
|
if (!cloudPipeline) {
|
||||||
@@ -402,7 +389,7 @@ export class HaVoiceAssistantSetupStepPipeline extends LitElement {
|
|||||||
|
|
||||||
private async _setupCloud() {
|
private async _setupCloud() {
|
||||||
if (await this._hasCloud()) {
|
if (await this._hasCloud()) {
|
||||||
this._createCloudPipeline();
|
this._createCloudPipeline(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
fireEvent(this, "next-step", { step: STEP.CLOUD });
|
fireEvent(this, "next-step", { step: STEP.CLOUD });
|
||||||
|
@@ -246,7 +246,10 @@ export class HaVoiceAssistantSetupStepSuccess extends LitElement {
|
|||||||
if (!this.assistEntityId) {
|
if (!this.assistEntityId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await assistSatelliteAnnounce(this.hass, this.assistEntityId, message);
|
await assistSatelliteAnnounce(this.hass, this.assistEntityId, {
|
||||||
|
message,
|
||||||
|
preannounce_media_id: null,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private _testWakeWord() {
|
private _testWakeWord() {
|
||||||
|
@@ -152,12 +152,14 @@ export class EnergyDeviceSettings extends LitElement {
|
|||||||
device_consumptions: this.preferences
|
device_consumptions: this.preferences
|
||||||
.device_consumption as DeviceConsumptionEnergyPreference[],
|
.device_consumption as DeviceConsumptionEnergyPreference[],
|
||||||
saveCallback: async (newDevice) => {
|
saveCallback: async (newDevice) => {
|
||||||
await this._savePreferences({
|
const newPrefs = {
|
||||||
...this.preferences,
|
...this.preferences,
|
||||||
device_consumption: this.preferences.device_consumption.map((d) =>
|
device_consumption: this.preferences.device_consumption.map((d) =>
|
||||||
d === origDevice ? newDevice : d
|
d === origDevice ? newDevice : d
|
||||||
),
|
),
|
||||||
});
|
};
|
||||||
|
this._sanitizeParents(newPrefs);
|
||||||
|
await this._savePreferences(newPrefs);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -177,6 +179,15 @@ export class EnergyDeviceSettings extends LitElement {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _sanitizeParents(prefs: EnergyPreferences) {
|
||||||
|
const statIds = prefs.device_consumption.map((d) => d.stat_consumption);
|
||||||
|
prefs.device_consumption.forEach((d) => {
|
||||||
|
if (d.included_in_stat && !statIds.includes(d.included_in_stat)) {
|
||||||
|
delete d.included_in_stat;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private async _deleteDevice(ev) {
|
private async _deleteDevice(ev) {
|
||||||
const deviceToDelete: DeviceConsumptionEnergyPreference =
|
const deviceToDelete: DeviceConsumptionEnergyPreference =
|
||||||
ev.currentTarget.device;
|
ev.currentTarget.device;
|
||||||
@@ -196,14 +207,7 @@ export class EnergyDeviceSettings extends LitElement {
|
|||||||
(device) => device !== deviceToDelete
|
(device) => device !== deviceToDelete
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
newPrefs.device_consumption.forEach((d, idx) => {
|
this._sanitizeParents(newPrefs);
|
||||||
if (d.included_in_stat === deviceToDelete.stat_consumption) {
|
|
||||||
newPrefs.device_consumption[idx] = {
|
|
||||||
...newPrefs.device_consumption[idx],
|
|
||||||
};
|
|
||||||
delete newPrefs.device_consumption[idx].included_in_stat;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
await this._savePreferences(newPrefs);
|
await this._savePreferences(newPrefs);
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
showAlertDialog(this, { title: `Failed to save config: ${err.message}` });
|
showAlertDialog(this, { title: `Failed to save config: ${err.message}` });
|
||||||
|
@@ -74,6 +74,7 @@ export class DialogEnergyDeviceSettings
|
|||||||
this._possibleParents = this._params.device_consumptions.filter(
|
this._possibleParents = this._params.device_consumptions.filter(
|
||||||
(d) =>
|
(d) =>
|
||||||
d.stat_consumption !== this._device!.stat_consumption &&
|
d.stat_consumption !== this._device!.stat_consumption &&
|
||||||
|
d.stat_consumption !== this._params?.device?.stat_consumption &&
|
||||||
!children.includes(d.stat_consumption)
|
!children.includes(d.stat_consumption)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -160,7 +161,15 @@ export class DialogEnergyDeviceSettings
|
|||||||
naturalMenuWidth
|
naturalMenuWidth
|
||||||
clearable
|
clearable
|
||||||
>
|
>
|
||||||
${this._possibleParents.map(
|
${!this._possibleParents.length
|
||||||
|
? html`
|
||||||
|
<mwc-list-item disabled value="-"
|
||||||
|
>${this.hass.localize(
|
||||||
|
"ui.panel.config.energy.device_consumption.dialog.no_upstream_devices"
|
||||||
|
)}</mwc-list-item
|
||||||
|
>
|
||||||
|
`
|
||||||
|
: this._possibleParents.map(
|
||||||
(stat) => html`
|
(stat) => html`
|
||||||
<mwc-list-item .value=${stat.stat_consumption}
|
<mwc-list-item .value=${stat.stat_consumption}
|
||||||
>${stat.name ||
|
>${stat.name ||
|
||||||
|
@@ -120,7 +120,7 @@ class HaConfigIntegrations extends SubscribeMixin(HassRouterPage) {
|
|||||||
const existingEntries = fullUpdate ? [] : this._configEntries;
|
const existingEntries = fullUpdate ? [] : this._configEntries;
|
||||||
this._configEntries = [...existingEntries!, ...newEntries];
|
this._configEntries = [...existingEntries!, ...newEntries];
|
||||||
},
|
},
|
||||||
{ type: ["device", "hub", "service"] }
|
{ type: ["device", "hub", "service", "hardware"] }
|
||||||
),
|
),
|
||||||
subscribeConfigFlowInProgress(this.hass, async (flowsInProgress) => {
|
subscribeConfigFlowInProgress(this.hass, async (flowsInProgress) => {
|
||||||
const integrations = new Set<string>();
|
const integrations = new Set<string>();
|
||||||
|
@@ -11,12 +11,19 @@ export class DialogZWaveJSAddNode extends HTMLElement {
|
|||||||
public configEntryId!: string;
|
public configEntryId!: string;
|
||||||
|
|
||||||
connectedCallback() {
|
connectedCallback() {
|
||||||
|
this._openDialog();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _openDialog() {
|
||||||
|
await navigate(
|
||||||
|
`/config/devices/dashboard?config_entry=${this.configEntryId}`,
|
||||||
|
{
|
||||||
|
replace: true,
|
||||||
|
}
|
||||||
|
);
|
||||||
showZWaveJSAddNodeDialog(this, {
|
showZWaveJSAddNodeDialog(this, {
|
||||||
entry_id: this.configEntryId,
|
entry_id: this.configEntryId,
|
||||||
});
|
});
|
||||||
navigate(`/config/devices/dashboard?config_entry=${this.configEntryId}`, {
|
|
||||||
replace: true,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -511,7 +511,20 @@ class HaPanelDevAction extends LitElement {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this._yamlValid = true;
|
this._yamlValid = true;
|
||||||
this._serviceDataChanged(ev);
|
|
||||||
|
if (typeof ev.detail.value !== "object") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._serviceData?.action !== ev.detail.value.action) {
|
||||||
|
this._error = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._serviceData = migrateAutomationAction(
|
||||||
|
ev.detail.value
|
||||||
|
) as ServiceAction;
|
||||||
|
|
||||||
|
this._checkUiSupported();
|
||||||
}
|
}
|
||||||
|
|
||||||
private _checkUiSupported() {
|
private _checkUiSupported() {
|
||||||
@@ -547,18 +560,18 @@ class HaPanelDevAction extends LitElement {
|
|||||||
if (this._serviceData?.action !== ev.detail.value.action) {
|
if (this._serviceData?.action !== ev.detail.value.action) {
|
||||||
this._error = undefined;
|
this._error = undefined;
|
||||||
}
|
}
|
||||||
this._serviceData = migrateAutomationAction(
|
this._serviceData = ev.detail.value;
|
||||||
ev.detail.value
|
|
||||||
) as ServiceAction;
|
|
||||||
this._checkUiSupported();
|
this._checkUiSupported();
|
||||||
}
|
}
|
||||||
|
|
||||||
private _serviceChanged(ev) {
|
private _serviceChanged(ev) {
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
this._serviceData = { action: ev.detail.value || "", data: {} };
|
if (ev.detail.value) {
|
||||||
|
this._serviceData = { action: ev.detail.value, data: {} };
|
||||||
|
this._yamlEditor?.setValue(this._serviceData);
|
||||||
|
}
|
||||||
this._response = undefined;
|
this._response = undefined;
|
||||||
this._error = undefined;
|
this._error = undefined;
|
||||||
this._yamlEditor?.setValue(this._serviceData);
|
|
||||||
this._checkUiSupported();
|
this._checkUiSupported();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -78,9 +78,6 @@ export class HuiButtonCard extends LitElement implements LovelaceCard {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
type: "button",
|
type: "button",
|
||||||
tap_action: {
|
|
||||||
action: "toggle",
|
|
||||||
},
|
|
||||||
entity: foundEntities[0] || "",
|
entity: foundEntities[0] || "",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -164,6 +161,7 @@ export class HuiButtonCard extends LitElement implements LovelaceCard {
|
|||||||
action: getEntityDefaultButtonAction(config.entity),
|
action: getEntityDefaultButtonAction(config.entity),
|
||||||
},
|
},
|
||||||
hold_action: { action: "more-info" },
|
hold_action: { action: "more-info" },
|
||||||
|
double_tap_action: { action: "none" },
|
||||||
show_icon: true,
|
show_icon: true,
|
||||||
show_name: true,
|
show_name: true,
|
||||||
state_color: true,
|
state_color: true,
|
||||||
|
@@ -65,7 +65,9 @@ export class HuiClockCard extends LitElement implements LovelaceCard {
|
|||||||
minute: "2-digit",
|
minute: "2-digit",
|
||||||
second: "2-digit",
|
second: "2-digit",
|
||||||
hourCycle: useAmPm(locale) ? "h12" : "h23",
|
hourCycle: useAmPm(locale) ? "h12" : "h23",
|
||||||
timeZone: resolveTimeZone(locale.time_zone, this.hass.config?.time_zone),
|
timeZone:
|
||||||
|
this._config?.time_zone ||
|
||||||
|
resolveTimeZone(locale.time_zone, this.hass.config?.time_zone),
|
||||||
});
|
});
|
||||||
|
|
||||||
this._tick();
|
this._tick();
|
||||||
@@ -79,7 +81,7 @@ export class HuiClockCard extends LitElement implements LovelaceCard {
|
|||||||
public getGridOptions(): LovelaceGridOptions {
|
public getGridOptions(): LovelaceGridOptions {
|
||||||
if (this._config?.clock_size === "medium") {
|
if (this._config?.clock_size === "medium") {
|
||||||
return {
|
return {
|
||||||
min_rows: 1,
|
min_rows: this._config?.title ? 2 : 1,
|
||||||
rows: 2,
|
rows: 2,
|
||||||
max_rows: 4,
|
max_rows: 4,
|
||||||
min_columns: 4,
|
min_columns: 4,
|
||||||
@@ -101,7 +103,7 @@ export class HuiClockCard extends LitElement implements LovelaceCard {
|
|||||||
min_rows: 1,
|
min_rows: 1,
|
||||||
rows: 1,
|
rows: 1,
|
||||||
max_rows: 4,
|
max_rows: 4,
|
||||||
min_columns: 4,
|
min_columns: 3,
|
||||||
columns: 6,
|
columns: 6,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -160,6 +162,9 @@ export class HuiClockCard extends LitElement implements LovelaceCard {
|
|||||||
? `size-${this._config.clock_size}`
|
? `size-${this._config.clock_size}`
|
||||||
: ""}"
|
: ""}"
|
||||||
>
|
>
|
||||||
|
${this._config.title !== undefined
|
||||||
|
? html`<div class="time-title">${this._config.title}</div>`
|
||||||
|
: nothing}
|
||||||
<div class="time-parts">
|
<div class="time-parts">
|
||||||
<div class="time-part hour">${this._timeHour}</div>
|
<div class="time-part hour">${this._timeHour}</div>
|
||||||
<div class="time-part minute">${this._timeMinute}</div>
|
<div class="time-part minute">${this._timeMinute}</div>
|
||||||
@@ -182,9 +187,41 @@ export class HuiClockCard extends LitElement implements LovelaceCard {
|
|||||||
|
|
||||||
.time-wrapper {
|
.time-wrapper {
|
||||||
display: flex;
|
display: flex;
|
||||||
height: 100%;
|
height: calc(100% - 12px);
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
flex-direction: column;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
padding: 6px 8px;
|
||||||
|
row-gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-wrapper.size-medium,
|
||||||
|
.time-wrapper.size-large {
|
||||||
|
height: calc(100% - 32px);
|
||||||
|
padding: 16px;
|
||||||
|
row-gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-title {
|
||||||
|
color: var(--primary-text-color);
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 18px;
|
||||||
|
overflow: hidden;
|
||||||
|
text-align: center;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-wrapper.size-medium .time-title {
|
||||||
|
font-size: 18px;
|
||||||
|
line-height: 21px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-wrapper.size-large .time-title {
|
||||||
|
font-size: 24px;
|
||||||
|
line-height: 28px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.time-parts {
|
.time-parts {
|
||||||
@@ -197,7 +234,11 @@ export class HuiClockCard extends LitElement implements LovelaceCard {
|
|||||||
font-size: 2rem;
|
font-size: 2rem;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
line-height: 0.8;
|
line-height: 0.8;
|
||||||
padding: 16px 0;
|
direction: ltr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-title + .time-parts {
|
||||||
|
font-size: 1.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.time-wrapper.size-medium .time-parts {
|
.time-wrapper.size-medium .time-parts {
|
||||||
@@ -242,8 +283,7 @@ export class HuiClockCard extends LitElement implements LovelaceCard {
|
|||||||
|
|
||||||
.time-parts .time-part.second,
|
.time-parts .time-part.second,
|
||||||
.time-parts .time-part.am-pm {
|
.time-parts .time-part.am-pm {
|
||||||
font-size: 12px;
|
font-size: 10px;
|
||||||
font-weight: 500;
|
|
||||||
margin-left: 4px;
|
margin-left: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -46,7 +46,10 @@ export class HuiPictureCard extends LitElement implements LovelaceCard {
|
|||||||
throw new Error("Image required");
|
throw new Error("Image required");
|
||||||
}
|
}
|
||||||
|
|
||||||
this._config = config;
|
this._config = {
|
||||||
|
tap_action: { action: "more-info" },
|
||||||
|
...config,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
protected shouldUpdate(changedProps: PropertyValues): boolean {
|
protected shouldUpdate(changedProps: PropertyValues): boolean {
|
||||||
|
@@ -6,9 +6,11 @@ import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_elemen
|
|||||||
import { computeDomain } from "../../../common/entity/compute_domain";
|
import { computeDomain } from "../../../common/entity/compute_domain";
|
||||||
import { computeStateName } from "../../../common/entity/compute_state_name";
|
import { computeStateName } from "../../../common/entity/compute_state_name";
|
||||||
import "../../../components/ha-card";
|
import "../../../components/ha-card";
|
||||||
|
import type { CameraEntity } from "../../../data/camera";
|
||||||
import type { ImageEntity } from "../../../data/image";
|
import type { ImageEntity } from "../../../data/image";
|
||||||
import { computeImageUrl } from "../../../data/image";
|
import { computeImageUrl } from "../../../data/image";
|
||||||
import type { ActionHandlerEvent } from "../../../data/lovelace/action_handler";
|
import type { ActionHandlerEvent } from "../../../data/lovelace/action_handler";
|
||||||
|
import type { PersonEntity } from "../../../data/person";
|
||||||
import type { HomeAssistant } from "../../../types";
|
import type { HomeAssistant } from "../../../types";
|
||||||
import { actionHandler } from "../common/directives/action-handler-directive";
|
import { actionHandler } from "../common/directives/action-handler-directive";
|
||||||
import { findEntities } from "../common/find-entities";
|
import { findEntities } from "../common/find-entities";
|
||||||
@@ -19,8 +21,6 @@ import "../components/hui-image";
|
|||||||
import { createEntityNotFoundWarning } from "../components/hui-warning";
|
import { createEntityNotFoundWarning } from "../components/hui-warning";
|
||||||
import type { LovelaceCard, LovelaceCardEditor } from "../types";
|
import type { LovelaceCard, LovelaceCardEditor } from "../types";
|
||||||
import type { PictureEntityCardConfig } from "./types";
|
import type { PictureEntityCardConfig } from "./types";
|
||||||
import type { CameraEntity } from "../../../data/camera";
|
|
||||||
import type { PersonEntity } from "../../../data/person";
|
|
||||||
|
|
||||||
export const STUB_IMAGE =
|
export const STUB_IMAGE =
|
||||||
"https://demo.home-assistant.io/stub_config/bedroom.png";
|
"https://demo.home-assistant.io/stub_config/bedroom.png";
|
||||||
@@ -75,7 +75,12 @@ class HuiPictureEntityCard extends LitElement implements LovelaceCard {
|
|||||||
throw new Error("No image source configured");
|
throw new Error("No image source configured");
|
||||||
}
|
}
|
||||||
|
|
||||||
this._config = { show_name: true, show_state: true, ...config };
|
this._config = {
|
||||||
|
show_name: true,
|
||||||
|
show_state: true,
|
||||||
|
tap_action: { action: "more-info" },
|
||||||
|
...config,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
protected shouldUpdate(changedProps: PropertyValues): boolean {
|
protected shouldUpdate(changedProps: PropertyValues): boolean {
|
||||||
|
@@ -105,7 +105,7 @@ class HuiPictureGlanceCard extends LitElement implements LovelaceCard {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this._config = {
|
this._config = {
|
||||||
hold_action: { action: "more-info" },
|
tap_action: { action: "more-info" },
|
||||||
...config,
|
...config,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@@ -349,9 +349,11 @@ export interface MarkdownCardConfig extends LovelaceCardConfig {
|
|||||||
|
|
||||||
export interface ClockCardConfig extends LovelaceCardConfig {
|
export interface ClockCardConfig extends LovelaceCardConfig {
|
||||||
type: "clock";
|
type: "clock";
|
||||||
|
title?: string;
|
||||||
clock_size?: "small" | "medium" | "large";
|
clock_size?: "small" | "medium" | "large";
|
||||||
show_seconds?: boolean | undefined;
|
show_seconds?: boolean | undefined;
|
||||||
time_format?: TimeFormat;
|
time_format?: TimeFormat;
|
||||||
|
time_zone?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MediaControlCardConfig extends LovelaceCardConfig {
|
export interface MediaControlCardConfig extends LovelaceCardConfig {
|
||||||
|
@@ -50,7 +50,7 @@ const HIDE_DOMAIN = new Set([
|
|||||||
...ASSIST_ENTITIES,
|
...ASSIST_ENTITIES,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const HIDE_PLATFORM = new Set(["mobile_app"]);
|
const HIDE_PLATFORM = new Set(["backup", "mobile_app"]);
|
||||||
|
|
||||||
interface SplittedByAreaDevice {
|
interface SplittedByAreaDevice {
|
||||||
areasWithEntities: Record<string, HassEntity[]>;
|
areasWithEntities: Record<string, HassEntity[]>;
|
||||||
|
@@ -1,12 +1,13 @@
|
|||||||
|
import { mdiGestureTap } from "@mdi/js";
|
||||||
import { html, LitElement, nothing } from "lit";
|
import { html, LitElement, nothing } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { any, assert, literal, object, optional, string } from "superstruct";
|
import { any, assert, literal, object, optional, string } from "superstruct";
|
||||||
import { fireEvent } from "../../../../../common/dom/fire_event";
|
import { fireEvent } from "../../../../../common/dom/fire_event";
|
||||||
|
import "../../../../../components/ha-form/ha-form";
|
||||||
import type { SchemaUnion } from "../../../../../components/ha-form/types";
|
import type { SchemaUnion } from "../../../../../components/ha-form/types";
|
||||||
import type { HomeAssistant } from "../../../../../types";
|
import type { HomeAssistant } from "../../../../../types";
|
||||||
import "../../../../../components/ha-form/ha-form";
|
|
||||||
import type { LovelacePictureElementEditor } from "../../../types";
|
|
||||||
import type { IconElementConfig } from "../../../elements/types";
|
import type { IconElementConfig } from "../../../elements/types";
|
||||||
|
import type { LovelacePictureElementEditor } from "../../../types";
|
||||||
import { actionConfigStruct } from "../../structs/action-struct";
|
import { actionConfigStruct } from "../../structs/action-struct";
|
||||||
|
|
||||||
const iconElementConfigStruct = object({
|
const iconElementConfigStruct = object({
|
||||||
@@ -24,18 +25,37 @@ const SCHEMA = [
|
|||||||
{ name: "icon", selector: { icon: {} } },
|
{ name: "icon", selector: { icon: {} } },
|
||||||
{ name: "title", selector: { text: {} } },
|
{ name: "title", selector: { text: {} } },
|
||||||
{ name: "entity", selector: { entity: {} } },
|
{ name: "entity", selector: { entity: {} } },
|
||||||
|
{
|
||||||
|
name: "interactions",
|
||||||
|
type: "expandable",
|
||||||
|
flatten: true,
|
||||||
|
iconPath: mdiGestureTap,
|
||||||
|
schema: [
|
||||||
{
|
{
|
||||||
name: "tap_action",
|
name: "tap_action",
|
||||||
selector: {
|
selector: {
|
||||||
ui_action: {},
|
ui_action: {
|
||||||
|
default_action: "more-info",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "hold_action",
|
name: "",
|
||||||
|
type: "optional_actions",
|
||||||
|
flatten: true,
|
||||||
|
schema: (["hold_action", "double_tap_action"] as const).map(
|
||||||
|
(action) => ({
|
||||||
|
name: action,
|
||||||
selector: {
|
selector: {
|
||||||
ui_action: {},
|
ui_action: {
|
||||||
|
default_action: "none" as const,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
})
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
{ name: "style", selector: { object: {} } },
|
{ name: "style", selector: { object: {} } },
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
|
@@ -1,12 +1,13 @@
|
|||||||
|
import { mdiGestureTap } from "@mdi/js";
|
||||||
import { html, LitElement, nothing } from "lit";
|
import { html, LitElement, nothing } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { any, assert, literal, object, optional, string } from "superstruct";
|
import { any, assert, literal, object, optional, string } from "superstruct";
|
||||||
import { fireEvent } from "../../../../../common/dom/fire_event";
|
import { fireEvent } from "../../../../../common/dom/fire_event";
|
||||||
|
import "../../../../../components/ha-form/ha-form";
|
||||||
import type { SchemaUnion } from "../../../../../components/ha-form/types";
|
import type { SchemaUnion } from "../../../../../components/ha-form/types";
|
||||||
import type { HomeAssistant } from "../../../../../types";
|
import type { HomeAssistant } from "../../../../../types";
|
||||||
import "../../../../../components/ha-form/ha-form";
|
|
||||||
import type { LovelacePictureElementEditor } from "../../../types";
|
|
||||||
import type { ImageElementConfig } from "../../../elements/types";
|
import type { ImageElementConfig } from "../../../elements/types";
|
||||||
|
import type { LovelacePictureElementEditor } from "../../../types";
|
||||||
import { actionConfigStruct } from "../../structs/action-struct";
|
import { actionConfigStruct } from "../../structs/action-struct";
|
||||||
|
|
||||||
const imageElementConfigStruct = object({
|
const imageElementConfigStruct = object({
|
||||||
@@ -29,18 +30,37 @@ const imageElementConfigStruct = object({
|
|||||||
const SCHEMA = [
|
const SCHEMA = [
|
||||||
{ name: "entity", selector: { entity: {} } },
|
{ name: "entity", selector: { entity: {} } },
|
||||||
{ name: "title", selector: { text: {} } },
|
{ name: "title", selector: { text: {} } },
|
||||||
|
{
|
||||||
|
name: "interactions",
|
||||||
|
type: "expandable",
|
||||||
|
flatten: true,
|
||||||
|
iconPath: mdiGestureTap,
|
||||||
|
schema: [
|
||||||
{
|
{
|
||||||
name: "tap_action",
|
name: "tap_action",
|
||||||
selector: {
|
selector: {
|
||||||
ui_action: {},
|
ui_action: {
|
||||||
|
default_action: "more-info",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "hold_action",
|
name: "",
|
||||||
|
type: "optional_actions",
|
||||||
|
flatten: true,
|
||||||
|
schema: (["hold_action", "double_tap_action"] as const).map(
|
||||||
|
(action) => ({
|
||||||
|
name: action,
|
||||||
selector: {
|
selector: {
|
||||||
ui_action: {},
|
ui_action: {
|
||||||
|
default_action: "none" as const,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
})
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
{ name: "image", selector: { image: {} } },
|
{ name: "image", selector: { image: {} } },
|
||||||
{ name: "camera_image", selector: { entity: { domain: "camera" } } },
|
{ name: "camera_image", selector: { entity: { domain: "camera" } } },
|
||||||
{
|
{
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
import { html, LitElement, nothing } from "lit";
|
import { html, LitElement, nothing } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { any, assert, literal, object, optional, string } from "superstruct";
|
import { any, assert, literal, object, optional, string } from "superstruct";
|
||||||
|
import { mdiGestureTap } from "@mdi/js";
|
||||||
import { fireEvent } from "../../../../../common/dom/fire_event";
|
import { fireEvent } from "../../../../../common/dom/fire_event";
|
||||||
import type { SchemaUnion } from "../../../../../components/ha-form/types";
|
import type { SchemaUnion } from "../../../../../components/ha-form/types";
|
||||||
import type { HomeAssistant } from "../../../../../types";
|
import type { HomeAssistant } from "../../../../../types";
|
||||||
@@ -22,18 +23,37 @@ const stateBadgeElementConfigStruct = object({
|
|||||||
const SCHEMA = [
|
const SCHEMA = [
|
||||||
{ name: "entity", required: true, selector: { entity: {} } },
|
{ name: "entity", required: true, selector: { entity: {} } },
|
||||||
{ name: "title", selector: { text: {} } },
|
{ name: "title", selector: { text: {} } },
|
||||||
|
{
|
||||||
|
name: "interactions",
|
||||||
|
type: "expandable",
|
||||||
|
flatten: true,
|
||||||
|
iconPath: mdiGestureTap,
|
||||||
|
schema: [
|
||||||
{
|
{
|
||||||
name: "tap_action",
|
name: "tap_action",
|
||||||
selector: {
|
selector: {
|
||||||
ui_action: {},
|
ui_action: {
|
||||||
|
default_action: "more-info",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "hold_action",
|
name: "",
|
||||||
|
type: "optional_actions",
|
||||||
|
flatten: true,
|
||||||
|
schema: (["hold_action", "double_tap_action"] as const).map(
|
||||||
|
(action) => ({
|
||||||
|
name: action,
|
||||||
selector: {
|
selector: {
|
||||||
ui_action: {},
|
ui_action: {
|
||||||
|
default_action: "none" as const,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
})
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
{ name: "style", selector: { object: {} } },
|
{ name: "style", selector: { object: {} } },
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
import { mdiGestureTap } from "@mdi/js";
|
||||||
import { html, LitElement, nothing } from "lit";
|
import { html, LitElement, nothing } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import {
|
import {
|
||||||
@@ -10,11 +11,11 @@ import {
|
|||||||
string,
|
string,
|
||||||
} from "superstruct";
|
} from "superstruct";
|
||||||
import { fireEvent } from "../../../../../common/dom/fire_event";
|
import { fireEvent } from "../../../../../common/dom/fire_event";
|
||||||
|
import "../../../../../components/ha-form/ha-form";
|
||||||
import type { SchemaUnion } from "../../../../../components/ha-form/types";
|
import type { SchemaUnion } from "../../../../../components/ha-form/types";
|
||||||
import type { HomeAssistant } from "../../../../../types";
|
import type { HomeAssistant } from "../../../../../types";
|
||||||
import "../../../../../components/ha-form/ha-form";
|
|
||||||
import type { LovelacePictureElementEditor } from "../../../types";
|
|
||||||
import type { StateIconElementConfig } from "../../../elements/types";
|
import type { StateIconElementConfig } from "../../../elements/types";
|
||||||
|
import type { LovelacePictureElementEditor } from "../../../types";
|
||||||
import { actionConfigStruct } from "../../structs/action-struct";
|
import { actionConfigStruct } from "../../structs/action-struct";
|
||||||
|
|
||||||
const stateIconElementConfigStruct = object({
|
const stateIconElementConfigStruct = object({
|
||||||
@@ -34,18 +35,37 @@ const SCHEMA = [
|
|||||||
{ name: "icon", selector: { icon: {} } },
|
{ name: "icon", selector: { icon: {} } },
|
||||||
{ name: "title", selector: { text: {} } },
|
{ name: "title", selector: { text: {} } },
|
||||||
{ name: "state_color", default: true, selector: { boolean: {} } },
|
{ name: "state_color", default: true, selector: { boolean: {} } },
|
||||||
|
{
|
||||||
|
name: "interactions",
|
||||||
|
type: "expandable",
|
||||||
|
flatten: true,
|
||||||
|
iconPath: mdiGestureTap,
|
||||||
|
schema: [
|
||||||
{
|
{
|
||||||
name: "tap_action",
|
name: "tap_action",
|
||||||
selector: {
|
selector: {
|
||||||
ui_action: {},
|
ui_action: {
|
||||||
|
default_action: "more-info",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "hold_action",
|
name: "",
|
||||||
|
type: "optional_actions",
|
||||||
|
flatten: true,
|
||||||
|
schema: (["hold_action", "double_tap_action"] as const).map(
|
||||||
|
(action) => ({
|
||||||
|
name: action,
|
||||||
selector: {
|
selector: {
|
||||||
ui_action: {},
|
ui_action: {
|
||||||
|
default_action: "none" as const,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
})
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
{ name: "style", selector: { object: {} } },
|
{ name: "style", selector: { object: {} } },
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
|
@@ -1,12 +1,13 @@
|
|||||||
|
import { mdiGestureTap } from "@mdi/js";
|
||||||
import { html, LitElement, nothing } from "lit";
|
import { html, LitElement, nothing } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { any, assert, literal, object, optional, string } from "superstruct";
|
import { any, assert, literal, object, optional, string } from "superstruct";
|
||||||
import { fireEvent } from "../../../../../common/dom/fire_event";
|
import { fireEvent } from "../../../../../common/dom/fire_event";
|
||||||
|
import "../../../../../components/ha-form/ha-form";
|
||||||
import type { SchemaUnion } from "../../../../../components/ha-form/types";
|
import type { SchemaUnion } from "../../../../../components/ha-form/types";
|
||||||
import type { HomeAssistant } from "../../../../../types";
|
import type { HomeAssistant } from "../../../../../types";
|
||||||
import "../../../../../components/ha-form/ha-form";
|
|
||||||
import type { LovelacePictureElementEditor } from "../../../types";
|
|
||||||
import type { StateLabelElementConfig } from "../../../elements/types";
|
import type { StateLabelElementConfig } from "../../../elements/types";
|
||||||
|
import type { LovelacePictureElementEditor } from "../../../types";
|
||||||
import { actionConfigStruct } from "../../structs/action-struct";
|
import { actionConfigStruct } from "../../structs/action-struct";
|
||||||
|
|
||||||
const stateLabelElementConfigStruct = object({
|
const stateLabelElementConfigStruct = object({
|
||||||
@@ -34,18 +35,37 @@ const SCHEMA = [
|
|||||||
{ name: "prefix", selector: { text: {} } },
|
{ name: "prefix", selector: { text: {} } },
|
||||||
{ name: "suffix", selector: { text: {} } },
|
{ name: "suffix", selector: { text: {} } },
|
||||||
{ name: "title", selector: { text: {} } },
|
{ name: "title", selector: { text: {} } },
|
||||||
|
{
|
||||||
|
name: "interactions",
|
||||||
|
type: "expandable",
|
||||||
|
flatten: true,
|
||||||
|
iconPath: mdiGestureTap,
|
||||||
|
schema: [
|
||||||
{
|
{
|
||||||
name: "tap_action",
|
name: "tap_action",
|
||||||
selector: {
|
selector: {
|
||||||
ui_action: {},
|
ui_action: {
|
||||||
|
default_action: "more-info",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "hold_action",
|
name: "",
|
||||||
|
type: "optional_actions",
|
||||||
|
flatten: true,
|
||||||
|
schema: (["hold_action", "double_tap_action"] as const).map(
|
||||||
|
(action) => ({
|
||||||
|
name: action,
|
||||||
selector: {
|
selector: {
|
||||||
ui_action: {},
|
ui_action: {
|
||||||
|
default_action: "none" as const,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
})
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
{ name: "style", selector: { object: {} } },
|
{ name: "style", selector: { object: {} } },
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
import { mdiGestureTap } from "@mdi/js";
|
||||||
import type { CSSResultGroup } from "lit";
|
import type { CSSResultGroup } from "lit";
|
||||||
import { html, LitElement, nothing } from "lit";
|
import { html, LitElement, nothing } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
@@ -28,6 +29,7 @@ const cardConfigStruct = assign(
|
|||||||
icon_height: optional(string()),
|
icon_height: optional(string()),
|
||||||
tap_action: optional(actionConfigStruct),
|
tap_action: optional(actionConfigStruct),
|
||||||
hold_action: optional(actionConfigStruct),
|
hold_action: optional(actionConfigStruct),
|
||||||
|
double_tap_action: optional(actionConfigStruct),
|
||||||
theme: optional(string()),
|
theme: optional(string()),
|
||||||
show_state: optional(boolean()),
|
show_state: optional(boolean()),
|
||||||
})
|
})
|
||||||
@@ -85,6 +87,12 @@ export class HuiButtonCardEditor
|
|||||||
{ name: "theme", selector: { theme: {} } },
|
{ name: "theme", selector: { theme: {} } },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "interactions",
|
||||||
|
type: "expandable",
|
||||||
|
flatten: true,
|
||||||
|
iconPath: mdiGestureTap,
|
||||||
|
schema: [
|
||||||
{
|
{
|
||||||
name: "tap_action",
|
name: "tap_action",
|
||||||
selector: {
|
selector: {
|
||||||
@@ -101,6 +109,23 @@ export class HuiButtonCardEditor
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "",
|
||||||
|
type: "optional_actions",
|
||||||
|
flatten: true,
|
||||||
|
schema: [
|
||||||
|
{
|
||||||
|
name: "double_tap_action",
|
||||||
|
selector: {
|
||||||
|
ui_action: {
|
||||||
|
default_action: "none",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
] as const satisfies readonly HaFormSchema[]
|
] as const satisfies readonly HaFormSchema[]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
import timezones from "google-timezones-json";
|
||||||
import { html, LitElement, nothing } from "lit";
|
import { html, LitElement, nothing } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
@@ -9,6 +10,7 @@ import {
|
|||||||
literal,
|
literal,
|
||||||
object,
|
object,
|
||||||
optional,
|
optional,
|
||||||
|
string,
|
||||||
union,
|
union,
|
||||||
} from "superstruct";
|
} from "superstruct";
|
||||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
@@ -27,10 +29,12 @@ import { TimeFormat } from "../../../../data/translation";
|
|||||||
const cardConfigStruct = assign(
|
const cardConfigStruct = assign(
|
||||||
baseLovelaceCardConfig,
|
baseLovelaceCardConfig,
|
||||||
object({
|
object({
|
||||||
|
title: optional(string()),
|
||||||
clock_size: optional(
|
clock_size: optional(
|
||||||
union([literal("small"), literal("medium"), literal("large")])
|
union([literal("small"), literal("medium"), literal("large")])
|
||||||
),
|
),
|
||||||
time_format: optional(enums(Object.values(TimeFormat))),
|
time_format: optional(enums(Object.values(TimeFormat))),
|
||||||
|
time_zone: optional(enums(Object.keys(timezones))),
|
||||||
show_seconds: optional(boolean()),
|
show_seconds: optional(boolean()),
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@@ -47,6 +51,7 @@ export class HuiClockCardEditor
|
|||||||
private _schema = memoizeOne(
|
private _schema = memoizeOne(
|
||||||
(localize: LocalizeFunc) =>
|
(localize: LocalizeFunc) =>
|
||||||
[
|
[
|
||||||
|
{ name: "title", selector: { text: {} } },
|
||||||
{
|
{
|
||||||
name: "clock_size",
|
name: "clock_size",
|
||||||
selector: {
|
selector: {
|
||||||
@@ -61,18 +66,13 @@ export class HuiClockCardEditor
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{ name: "show_seconds", selector: { boolean: {} } },
|
||||||
name: "show_seconds",
|
|
||||||
selector: {
|
|
||||||
boolean: {},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: "time_format",
|
name: "time_format",
|
||||||
selector: {
|
selector: {
|
||||||
select: {
|
select: {
|
||||||
mode: "dropdown",
|
mode: "dropdown",
|
||||||
options: Object.values(TimeFormat).map((value) => ({
|
options: ["auto", ...Object.values(TimeFormat)].map((value) => ({
|
||||||
value,
|
value,
|
||||||
label: localize(
|
label: localize(
|
||||||
`ui.panel.lovelace.editor.card.clock.time_formats.${value}`
|
`ui.panel.lovelace.editor.card.clock.time_formats.${value}`
|
||||||
@@ -81,12 +81,33 @@ export class HuiClockCardEditor
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "time_zone",
|
||||||
|
selector: {
|
||||||
|
select: {
|
||||||
|
mode: "dropdown",
|
||||||
|
options: [
|
||||||
|
[
|
||||||
|
"auto",
|
||||||
|
localize(
|
||||||
|
`ui.panel.lovelace.editor.card.clock.time_zones.auto`
|
||||||
|
),
|
||||||
|
],
|
||||||
|
...Object.entries(timezones as Record<string, string>),
|
||||||
|
].map(([key, value]) => ({
|
||||||
|
value: key,
|
||||||
|
label: value,
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
] as const satisfies readonly HaFormSchema[]
|
] as const satisfies readonly HaFormSchema[]
|
||||||
);
|
);
|
||||||
|
|
||||||
private _data = memoizeOne((config) => ({
|
private _data = memoizeOne((config) => ({
|
||||||
clock_size: "small",
|
clock_size: "small",
|
||||||
time_format: TimeFormat.language,
|
time_zone: "auto",
|
||||||
|
time_format: "auto",
|
||||||
show_seconds: false,
|
show_seconds: false,
|
||||||
...config,
|
...config,
|
||||||
}));
|
}));
|
||||||
@@ -113,6 +134,13 @@ export class HuiClockCardEditor
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _valueChanged(ev: CustomEvent): void {
|
private _valueChanged(ev: CustomEvent): void {
|
||||||
|
if (ev.detail.value.time_zone === "auto") {
|
||||||
|
delete ev.detail.value.time_zone;
|
||||||
|
}
|
||||||
|
if (ev.detail.value.time_format === "auto") {
|
||||||
|
delete ev.detail.value.time_format;
|
||||||
|
}
|
||||||
|
|
||||||
fireEvent(this, "config-changed", { config: ev.detail.value });
|
fireEvent(this, "config-changed", { config: ev.detail.value });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,6 +148,10 @@ export class HuiClockCardEditor
|
|||||||
schema: SchemaUnion<ReturnType<typeof this._schema>>
|
schema: SchemaUnion<ReturnType<typeof this._schema>>
|
||||||
) => {
|
) => {
|
||||||
switch (schema.name) {
|
switch (schema.name) {
|
||||||
|
case "title":
|
||||||
|
return this.hass!.localize(
|
||||||
|
"ui.panel.lovelace.editor.card.generic.title"
|
||||||
|
);
|
||||||
case "clock_size":
|
case "clock_size":
|
||||||
return this.hass!.localize(
|
return this.hass!.localize(
|
||||||
`ui.panel.lovelace.editor.card.clock.clock_size`
|
`ui.panel.lovelace.editor.card.clock.clock_size`
|
||||||
@@ -128,6 +160,10 @@ export class HuiClockCardEditor
|
|||||||
return this.hass!.localize(
|
return this.hass!.localize(
|
||||||
`ui.panel.lovelace.editor.card.clock.time_format`
|
`ui.panel.lovelace.editor.card.clock.time_format`
|
||||||
);
|
);
|
||||||
|
case "time_zone":
|
||||||
|
return this.hass!.localize(
|
||||||
|
`ui.panel.lovelace.editor.card.clock.time_zone`
|
||||||
|
);
|
||||||
case "show_seconds":
|
case "show_seconds":
|
||||||
return this.hass!.localize(
|
return this.hass!.localize(
|
||||||
`ui.panel.lovelace.editor.card.clock.show_seconds`
|
`ui.panel.lovelace.editor.card.clock.show_seconds`
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
import { mdiGestureTap } from "@mdi/js";
|
||||||
import { html, LitElement, nothing } from "lit";
|
import { html, LitElement, nothing } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
@@ -16,14 +17,21 @@ import { fireEvent } from "../../../../common/dom/fire_event";
|
|||||||
import "../../../../components/ha-form/ha-form";
|
import "../../../../components/ha-form/ha-form";
|
||||||
import type { SchemaUnion } from "../../../../components/ha-form/types";
|
import type { SchemaUnion } from "../../../../components/ha-form/types";
|
||||||
import type { HomeAssistant } from "../../../../types";
|
import type { HomeAssistant } from "../../../../types";
|
||||||
|
import { DEFAULT_MAX, DEFAULT_MIN } from "../../cards/hui-gauge-card";
|
||||||
import type { GaugeCardConfig } from "../../cards/types";
|
import type { GaugeCardConfig } from "../../cards/types";
|
||||||
|
import type { UiAction } from "../../components/hui-action-editor";
|
||||||
import type { LovelaceCardEditor } from "../../types";
|
import type { LovelaceCardEditor } from "../../types";
|
||||||
import { actionConfigStruct } from "../structs/action-struct";
|
import { actionConfigStruct } from "../structs/action-struct";
|
||||||
import { baseLovelaceCardConfig } from "../structs/base-card-struct";
|
import { baseLovelaceCardConfig } from "../structs/base-card-struct";
|
||||||
import { DEFAULT_MIN, DEFAULT_MAX } from "../../cards/hui-gauge-card";
|
|
||||||
import type { UiAction } from "../../components/hui-action-editor";
|
|
||||||
|
|
||||||
const TAP_ACTIONS: UiAction[] = ["navigate", "url", "perform-action", "none"];
|
const TAP_ACTIONS: UiAction[] = [
|
||||||
|
"more-info",
|
||||||
|
"navigate",
|
||||||
|
"url",
|
||||||
|
"perform-action",
|
||||||
|
"assist",
|
||||||
|
"none",
|
||||||
|
];
|
||||||
|
|
||||||
const gaugeSegmentStruct = object({
|
const gaugeSegmentStruct = object({
|
||||||
from: number(),
|
from: number(),
|
||||||
@@ -133,6 +141,12 @@ export class HuiGaugeCardEditor
|
|||||||
},
|
},
|
||||||
] as const)
|
] as const)
|
||||||
: []),
|
: []),
|
||||||
|
{
|
||||||
|
name: "interactions",
|
||||||
|
type: "expandable",
|
||||||
|
flatten: true,
|
||||||
|
iconPath: mdiGestureTap,
|
||||||
|
schema: [
|
||||||
{
|
{
|
||||||
name: "tap_action",
|
name: "tap_action",
|
||||||
selector: {
|
selector: {
|
||||||
@@ -142,6 +156,24 @@ export class HuiGaugeCardEditor
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "",
|
||||||
|
type: "optional_actions",
|
||||||
|
flatten: true,
|
||||||
|
schema: (["hold_action", "double_tap_action"] as const).map(
|
||||||
|
(action) => ({
|
||||||
|
name: action,
|
||||||
|
selector: {
|
||||||
|
ui_action: {
|
||||||
|
actions: TAP_ACTIONS,
|
||||||
|
default_action: "none" as const,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
] as const
|
] as const
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -231,7 +263,13 @@ export class HuiGaugeCardEditor
|
|||||||
return this.hass!.localize(
|
return this.hass!.localize(
|
||||||
"ui.panel.lovelace.editor.card.generic.unit"
|
"ui.panel.lovelace.editor.card.generic.unit"
|
||||||
);
|
);
|
||||||
|
case "interactions":
|
||||||
|
return this.hass!.localize(
|
||||||
|
"ui.panel.lovelace.editor.card.generic.interactions"
|
||||||
|
);
|
||||||
case "tap_action":
|
case "tap_action":
|
||||||
|
case "hold_action":
|
||||||
|
case "double_tap_action":
|
||||||
return `${this.hass!.localize(
|
return `${this.hass!.localize(
|
||||||
`ui.panel.lovelace.editor.card.generic.${schema.name}`
|
`ui.panel.lovelace.editor.card.generic.${schema.name}`
|
||||||
)} (${this.hass!.localize(
|
)} (${this.hass!.localize(
|
||||||
|
@@ -2,6 +2,7 @@ import type { CSSResultGroup } from "lit";
|
|||||||
import { html, LitElement, nothing } from "lit";
|
import { html, LitElement, nothing } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { assert, assign, object, optional, string } from "superstruct";
|
import { assert, assign, object, optional, string } from "superstruct";
|
||||||
|
import { mdiGestureTap } from "@mdi/js";
|
||||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
import "../../../../components/ha-form/ha-form";
|
import "../../../../components/ha-form/ha-form";
|
||||||
import type { SchemaUnion } from "../../../../components/ha-form/types";
|
import type { SchemaUnion } from "../../../../components/ha-form/types";
|
||||||
@@ -19,6 +20,7 @@ const cardConfigStruct = assign(
|
|||||||
entity: optional(string()),
|
entity: optional(string()),
|
||||||
theme: optional(string()),
|
theme: optional(string()),
|
||||||
icon: optional(string()),
|
icon: optional(string()),
|
||||||
|
tap_action: optional(actionConfigStruct),
|
||||||
hold_action: optional(actionConfigStruct),
|
hold_action: optional(actionConfigStruct),
|
||||||
double_tap_action: optional(actionConfigStruct),
|
double_tap_action: optional(actionConfigStruct),
|
||||||
})
|
})
|
||||||
@@ -48,12 +50,43 @@ const SCHEMA = [
|
|||||||
},
|
},
|
||||||
{ name: "theme", selector: { theme: {} } },
|
{ name: "theme", selector: { theme: {} } },
|
||||||
{
|
{
|
||||||
name: "hold_action",
|
name: "interactions",
|
||||||
selector: { ui_action: {} },
|
type: "expandable",
|
||||||
|
flatten: true,
|
||||||
|
iconPath: mdiGestureTap,
|
||||||
|
schema: [
|
||||||
|
{
|
||||||
|
name: "tap_action",
|
||||||
|
selector: {
|
||||||
|
ui_action: {
|
||||||
|
default_action: "toggle",
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "hold_action",
|
||||||
|
selector: {
|
||||||
|
ui_action: {
|
||||||
|
default_action: "more-info",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "",
|
||||||
|
type: "optional_actions",
|
||||||
|
flatten: true,
|
||||||
|
schema: [
|
||||||
{
|
{
|
||||||
name: "double_tap_action",
|
name: "double_tap_action",
|
||||||
selector: { ui_action: {} },
|
selector: {
|
||||||
|
ui_action: {
|
||||||
|
default_action: "none",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
import { mdiGestureTap } from "@mdi/js";
|
||||||
import { html, LitElement, nothing } from "lit";
|
import { html, LitElement, nothing } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { assert, assign, object, optional, string } from "superstruct";
|
import { assert, assign, object, optional, string } from "superstruct";
|
||||||
@@ -18,6 +19,7 @@ const cardConfigStruct = assign(
|
|||||||
image_entity: optional(string()),
|
image_entity: optional(string()),
|
||||||
tap_action: optional(actionConfigStruct),
|
tap_action: optional(actionConfigStruct),
|
||||||
hold_action: optional(actionConfigStruct),
|
hold_action: optional(actionConfigStruct),
|
||||||
|
double_tap_action: optional(actionConfigStruct),
|
||||||
theme: optional(string()),
|
theme: optional(string()),
|
||||||
alt_text: optional(string()),
|
alt_text: optional(string()),
|
||||||
})
|
})
|
||||||
@@ -31,13 +33,36 @@ const SCHEMA = [
|
|||||||
},
|
},
|
||||||
{ name: "alt_text", selector: { text: {} } },
|
{ name: "alt_text", selector: { text: {} } },
|
||||||
{ name: "theme", selector: { theme: {} } },
|
{ name: "theme", selector: { theme: {} } },
|
||||||
|
{
|
||||||
|
name: "interactions",
|
||||||
|
type: "expandable",
|
||||||
|
flatten: true,
|
||||||
|
iconPath: mdiGestureTap,
|
||||||
|
schema: [
|
||||||
{
|
{
|
||||||
name: "tap_action",
|
name: "tap_action",
|
||||||
selector: { ui_action: {} },
|
selector: {
|
||||||
|
ui_action: {
|
||||||
|
default_action: "more-info",
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "hold_action",
|
name: "",
|
||||||
selector: { ui_action: {} },
|
type: "optional_actions",
|
||||||
|
flatten: true,
|
||||||
|
schema: (["hold_action", "double_tap_action"] as const).map(
|
||||||
|
(action) => ({
|
||||||
|
name: action,
|
||||||
|
selector: {
|
||||||
|
ui_action: {
|
||||||
|
default_action: "none" as const,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
|
@@ -1,18 +1,19 @@
|
|||||||
|
import { mdiGestureTap } from "@mdi/js";
|
||||||
import type { CSSResultGroup } from "lit";
|
import type { CSSResultGroup } from "lit";
|
||||||
import { html, LitElement, nothing } from "lit";
|
import { html, LitElement, nothing } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { assert, assign, boolean, object, optional, string } from "superstruct";
|
import { assert, assign, boolean, object, optional, string } from "superstruct";
|
||||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
|
import { computeDomain } from "../../../../common/entity/compute_domain";
|
||||||
import "../../../../components/ha-form/ha-form";
|
import "../../../../components/ha-form/ha-form";
|
||||||
import type { SchemaUnion } from "../../../../components/ha-form/types";
|
import type { SchemaUnion } from "../../../../components/ha-form/types";
|
||||||
import type { HomeAssistant } from "../../../../types";
|
import type { HomeAssistant } from "../../../../types";
|
||||||
|
import { STUB_IMAGE } from "../../cards/hui-picture-entity-card";
|
||||||
import type { PictureEntityCardConfig } from "../../cards/types";
|
import type { PictureEntityCardConfig } from "../../cards/types";
|
||||||
import type { LovelaceCardEditor } from "../../types";
|
import type { LovelaceCardEditor } from "../../types";
|
||||||
import { actionConfigStruct } from "../structs/action-struct";
|
import { actionConfigStruct } from "../structs/action-struct";
|
||||||
import { baseLovelaceCardConfig } from "../structs/base-card-struct";
|
import { baseLovelaceCardConfig } from "../structs/base-card-struct";
|
||||||
import { configElementStyle } from "./config-elements-style";
|
import { configElementStyle } from "./config-elements-style";
|
||||||
import { computeDomain } from "../../../../common/entity/compute_domain";
|
|
||||||
import { STUB_IMAGE } from "../../cards/hui-picture-entity-card";
|
|
||||||
|
|
||||||
const cardConfigStruct = assign(
|
const cardConfigStruct = assign(
|
||||||
baseLovelaceCardConfig,
|
baseLovelaceCardConfig,
|
||||||
@@ -25,6 +26,7 @@ const cardConfigStruct = assign(
|
|||||||
aspect_ratio: optional(string()),
|
aspect_ratio: optional(string()),
|
||||||
tap_action: optional(actionConfigStruct),
|
tap_action: optional(actionConfigStruct),
|
||||||
hold_action: optional(actionConfigStruct),
|
hold_action: optional(actionConfigStruct),
|
||||||
|
double_tap_action: optional(actionConfigStruct),
|
||||||
show_name: optional(boolean()),
|
show_name: optional(boolean()),
|
||||||
show_state: optional(boolean()),
|
show_state: optional(boolean()),
|
||||||
theme: optional(string()),
|
theme: optional(string()),
|
||||||
@@ -63,13 +65,36 @@ const SCHEMA = [
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
{ name: "theme", selector: { theme: {} } },
|
{ name: "theme", selector: { theme: {} } },
|
||||||
|
{
|
||||||
|
name: "interactions",
|
||||||
|
type: "expandable",
|
||||||
|
flatten: true,
|
||||||
|
iconPath: mdiGestureTap,
|
||||||
|
schema: [
|
||||||
{
|
{
|
||||||
name: "tap_action",
|
name: "tap_action",
|
||||||
selector: { ui_action: {} },
|
selector: {
|
||||||
|
ui_action: {
|
||||||
|
default_action: "more-info",
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "hold_action",
|
name: "",
|
||||||
selector: { ui_action: {} },
|
type: "optional_actions",
|
||||||
|
flatten: true,
|
||||||
|
schema: (["hold_action", "double_tap_action"] as const).map(
|
||||||
|
(action) => ({
|
||||||
|
name: action,
|
||||||
|
selector: {
|
||||||
|
ui_action: {
|
||||||
|
default_action: "none" as const,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
@@ -132,6 +157,7 @@ export class HuiPictureEntityCardEditor
|
|||||||
case "theme":
|
case "theme":
|
||||||
case "tap_action":
|
case "tap_action":
|
||||||
case "hold_action":
|
case "hold_action":
|
||||||
|
case "double_tap_action":
|
||||||
return `${this.hass!.localize(
|
return `${this.hass!.localize(
|
||||||
`ui.panel.lovelace.editor.card.generic.${schema.name}`
|
`ui.panel.lovelace.editor.card.generic.${schema.name}`
|
||||||
)} (${this.hass!.localize(
|
)} (${this.hass!.localize(
|
||||||
|
@@ -2,6 +2,7 @@ import type { CSSResultGroup } from "lit";
|
|||||||
import { html, LitElement, nothing } from "lit";
|
import { html, LitElement, nothing } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { array, assert, assign, object, optional, string } from "superstruct";
|
import { array, assert, assign, object, optional, string } from "superstruct";
|
||||||
|
import { mdiGestureTap } from "@mdi/js";
|
||||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
import "../../../../components/ha-form/ha-form";
|
import "../../../../components/ha-form/ha-form";
|
||||||
import type { SchemaUnion } from "../../../../components/ha-form/types";
|
import type { SchemaUnion } from "../../../../components/ha-form/types";
|
||||||
@@ -29,6 +30,7 @@ const cardConfigStruct = assign(
|
|||||||
aspect_ratio: optional(string()),
|
aspect_ratio: optional(string()),
|
||||||
tap_action: optional(actionConfigStruct),
|
tap_action: optional(actionConfigStruct),
|
||||||
hold_action: optional(actionConfigStruct),
|
hold_action: optional(actionConfigStruct),
|
||||||
|
double_tap_action: optional(actionConfigStruct),
|
||||||
entities: array(entitiesConfigStruct),
|
entities: array(entitiesConfigStruct),
|
||||||
theme: optional(string()),
|
theme: optional(string()),
|
||||||
})
|
})
|
||||||
@@ -55,13 +57,36 @@ const SCHEMA = [
|
|||||||
},
|
},
|
||||||
{ name: "entity", selector: { entity: {} } },
|
{ name: "entity", selector: { entity: {} } },
|
||||||
{ name: "theme", selector: { theme: {} } },
|
{ name: "theme", selector: { theme: {} } },
|
||||||
|
{
|
||||||
|
name: "interactions",
|
||||||
|
type: "expandable",
|
||||||
|
flatten: true,
|
||||||
|
iconPath: mdiGestureTap,
|
||||||
|
schema: [
|
||||||
{
|
{
|
||||||
name: "tap_action",
|
name: "tap_action",
|
||||||
selector: { ui_action: {} },
|
selector: {
|
||||||
|
ui_action: {
|
||||||
|
default_action: "more-info",
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "hold_action",
|
name: "",
|
||||||
selector: { ui_action: {} },
|
type: "optional_actions",
|
||||||
|
flatten: true,
|
||||||
|
schema: (["hold_action", "double_tap_action"] as const).map(
|
||||||
|
(action) => ({
|
||||||
|
name: action,
|
||||||
|
selector: {
|
||||||
|
ui_action: {
|
||||||
|
default_action: "none" as const,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
@@ -136,6 +161,7 @@ export class HuiPictureGlanceCardEditor
|
|||||||
case "theme":
|
case "theme":
|
||||||
case "tap_action":
|
case "tap_action":
|
||||||
case "hold_action":
|
case "hold_action":
|
||||||
|
case "double_tap_action":
|
||||||
return `${this.hass!.localize(
|
return `${this.hass!.localize(
|
||||||
`ui.panel.lovelace.editor.card.generic.${schema.name}`
|
`ui.panel.lovelace.editor.card.generic.${schema.name}`
|
||||||
)} (${this.hass!.localize(
|
)} (${this.hass!.localize(
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
import { mdiGestureTap } from "@mdi/js";
|
||||||
import { html, LitElement, nothing } from "lit";
|
import { html, LitElement, nothing } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
@@ -5,12 +6,13 @@ import {
|
|||||||
assert,
|
assert,
|
||||||
assign,
|
assign,
|
||||||
boolean,
|
boolean,
|
||||||
|
number,
|
||||||
object,
|
object,
|
||||||
optional,
|
optional,
|
||||||
string,
|
string,
|
||||||
number,
|
|
||||||
} from "superstruct";
|
} from "superstruct";
|
||||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
|
import { supportsFeature } from "../../../../common/entity/supports-feature";
|
||||||
import type { LocalizeFunc } from "../../../../common/translations/localize";
|
import type { LocalizeFunc } from "../../../../common/translations/localize";
|
||||||
import "../../../../components/ha-form/ha-form";
|
import "../../../../components/ha-form/ha-form";
|
||||||
import type { SchemaUnion } from "../../../../components/ha-form/types";
|
import type { SchemaUnion } from "../../../../components/ha-form/types";
|
||||||
@@ -22,7 +24,6 @@ import type { WeatherForecastCardConfig } from "../../cards/types";
|
|||||||
import type { LovelaceCardEditor } from "../../types";
|
import type { LovelaceCardEditor } from "../../types";
|
||||||
import { actionConfigStruct } from "../structs/action-struct";
|
import { actionConfigStruct } from "../structs/action-struct";
|
||||||
import { baseLovelaceCardConfig } from "../structs/base-card-struct";
|
import { baseLovelaceCardConfig } from "../structs/base-card-struct";
|
||||||
import { supportsFeature } from "../../../../common/entity/supports-feature";
|
|
||||||
|
|
||||||
const cardConfigStruct = assign(
|
const cardConfigStruct = assign(
|
||||||
baseLovelaceCardConfig,
|
baseLovelaceCardConfig,
|
||||||
@@ -239,6 +240,37 @@ export class HuiWeatherForecastCardEditor
|
|||||||
selector: { number: { min: 1, max: 12 } },
|
selector: { number: { min: 1, max: 12 } },
|
||||||
default: 5,
|
default: 5,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "interactions",
|
||||||
|
type: "expandable",
|
||||||
|
flatten: true,
|
||||||
|
iconPath: mdiGestureTap,
|
||||||
|
schema: [
|
||||||
|
{
|
||||||
|
name: "tap_action",
|
||||||
|
selector: {
|
||||||
|
ui_action: {
|
||||||
|
default_action: "more-info",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "",
|
||||||
|
type: "optional_actions",
|
||||||
|
flatten: true,
|
||||||
|
schema: (["hold_action", "double_tap_action"] as const).map(
|
||||||
|
(action) => ({
|
||||||
|
name: action,
|
||||||
|
selector: {
|
||||||
|
ui_action: {
|
||||||
|
default_action: "none" as const,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
] as const)
|
] as const)
|
||||||
: []),
|
: []),
|
||||||
] as const
|
] as const
|
||||||
|
@@ -31,7 +31,7 @@ export class HuiIconElement extends LitElement implements LovelaceElement {
|
|||||||
throw Error("Icon required");
|
throw Error("Icon required");
|
||||||
}
|
}
|
||||||
|
|
||||||
this._config = { hold_action: { action: "more-info" }, ...config };
|
this._config = { tap_action: { action: "more-info" }, ...config };
|
||||||
}
|
}
|
||||||
|
|
||||||
protected render() {
|
protected render() {
|
||||||
|
@@ -29,7 +29,7 @@ export class HuiImageElement extends LitElement implements LovelaceElement {
|
|||||||
throw Error("Invalid configuration");
|
throw Error("Invalid configuration");
|
||||||
}
|
}
|
||||||
|
|
||||||
this._config = { hold_action: { action: "more-info" }, ...config };
|
this._config = { tap_action: { action: "more-info" }, ...config };
|
||||||
|
|
||||||
this.classList.toggle(
|
this.classList.toggle(
|
||||||
"clickable",
|
"clickable",
|
||||||
|
@@ -60,7 +60,7 @@ export class HuiStateBadgeElement
|
|||||||
throw Error("Entity required");
|
throw Error("Entity required");
|
||||||
}
|
}
|
||||||
|
|
||||||
this._config = { hold_action: { action: "more-info" }, ...config };
|
this._config = { tap_action: { action: "more-info" }, ...config };
|
||||||
}
|
}
|
||||||
|
|
||||||
protected shouldUpdate(changedProps: PropertyValues): boolean {
|
protected shouldUpdate(changedProps: PropertyValues): boolean {
|
||||||
|
@@ -59,7 +59,7 @@ export class HuiStateIconElement extends LitElement implements LovelaceElement {
|
|||||||
|
|
||||||
this._config = {
|
this._config = {
|
||||||
state_color: true,
|
state_color: true,
|
||||||
hold_action: { action: "more-info" },
|
tap_action: { action: "more-info" },
|
||||||
...config,
|
...config,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@@ -56,7 +56,7 @@ class HuiStateLabelElement extends LitElement implements LovelaceElement {
|
|||||||
throw Error("Entity required");
|
throw Error("Entity required");
|
||||||
}
|
}
|
||||||
|
|
||||||
this._config = { hold_action: { action: "more-info" }, ...config };
|
this._config = { tap_action: { action: "more-info" }, ...config };
|
||||||
}
|
}
|
||||||
|
|
||||||
protected shouldUpdate(changedProps: PropertyValues): boolean {
|
protected shouldUpdate(changedProps: PropertyValues): boolean {
|
||||||
|
@@ -187,7 +187,7 @@ export class LovelacePanel extends LitElement {
|
|||||||
|
|
||||||
private async _regenerateConfig() {
|
private async _regenerateConfig() {
|
||||||
const conf = await generateLovelaceDashboardStrategy(
|
const conf = await generateLovelaceDashboardStrategy(
|
||||||
DEFAULT_CONFIG.strategy,
|
DEFAULT_CONFIG,
|
||||||
this.hass!
|
this.hass!
|
||||||
);
|
);
|
||||||
this._setLovelaceConfig(conf, DEFAULT_CONFIG, "generated");
|
this._setLovelaceConfig(conf, DEFAULT_CONFIG, "generated");
|
||||||
@@ -281,10 +281,7 @@ export class LovelacePanel extends LitElement {
|
|||||||
// We need these to generate a dashboard, wait for them
|
// We need these to generate a dashboard, wait for them
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
conf = await generateLovelaceDashboardStrategy(
|
conf = await generateLovelaceDashboardStrategy(rawConf, this.hass!);
|
||||||
rawConf.strategy,
|
|
||||||
this.hass!
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
conf = rawConf;
|
conf = rawConf;
|
||||||
}
|
}
|
||||||
@@ -301,7 +298,7 @@ export class LovelacePanel extends LitElement {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
conf = await generateLovelaceDashboardStrategy(
|
conf = await generateLovelaceDashboardStrategy(
|
||||||
DEFAULT_CONFIG.strategy,
|
DEFAULT_CONFIG,
|
||||||
this.hass!
|
this.hass!
|
||||||
);
|
);
|
||||||
rawConf = DEFAULT_CONFIG;
|
rawConf = DEFAULT_CONFIG;
|
||||||
@@ -378,10 +375,7 @@ export class LovelacePanel extends LitElement {
|
|||||||
let conf: LovelaceConfig;
|
let conf: LovelaceConfig;
|
||||||
// If strategy defined, apply it here.
|
// If strategy defined, apply it here.
|
||||||
if (isStrategyDashboard(newConfig)) {
|
if (isStrategyDashboard(newConfig)) {
|
||||||
conf = await generateLovelaceDashboardStrategy(
|
conf = await generateLovelaceDashboardStrategy(newConfig, this.hass!);
|
||||||
newConfig.strategy,
|
|
||||||
this.hass!
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
conf = newConfig;
|
conf = newConfig;
|
||||||
}
|
}
|
||||||
@@ -415,7 +409,7 @@ export class LovelacePanel extends LitElement {
|
|||||||
try {
|
try {
|
||||||
// Optimistic update
|
// Optimistic update
|
||||||
const generatedConf = await generateLovelaceDashboardStrategy(
|
const generatedConf = await generateLovelaceDashboardStrategy(
|
||||||
DEFAULT_CONFIG.strategy,
|
DEFAULT_CONFIG,
|
||||||
this.hass!
|
this.hass!
|
||||||
);
|
);
|
||||||
this._updateLovelace({
|
this._updateLovelace({
|
||||||
|
@@ -76,9 +76,9 @@ import { getLovelaceStrategy } from "./strategies/get-strategy";
|
|||||||
import { isLegacyStrategyConfig } from "./strategies/legacy-strategy";
|
import { isLegacyStrategyConfig } from "./strategies/legacy-strategy";
|
||||||
import type { Lovelace } from "./types";
|
import type { Lovelace } from "./types";
|
||||||
import "./views/hui-view";
|
import "./views/hui-view";
|
||||||
|
import "./views/hui-view-container";
|
||||||
import type { HUIView } from "./views/hui-view";
|
import type { HUIView } from "./views/hui-view";
|
||||||
import "./views/hui-view-background";
|
import "./views/hui-view-background";
|
||||||
import "./views/hui-view-container";
|
|
||||||
|
|
||||||
@customElement("hui-root")
|
@customElement("hui-root")
|
||||||
class HUIRoot extends LitElement {
|
class HUIRoot extends LitElement {
|
||||||
@@ -101,6 +101,8 @@ class HUIRoot extends LitElement {
|
|||||||
|
|
||||||
private _viewScrollPositions: Record<string, number> = {};
|
private _viewScrollPositions: Record<string, number> = {};
|
||||||
|
|
||||||
|
private _restoreScroll = false;
|
||||||
|
|
||||||
private _debouncedConfigChanged: () => void;
|
private _debouncedConfigChanged: () => void;
|
||||||
|
|
||||||
private _conversation = memoizeOne((_components) =>
|
private _conversation = memoizeOne((_components) =>
|
||||||
@@ -112,7 +114,7 @@ class HUIRoot extends LitElement {
|
|||||||
// The view can trigger a re-render when it knows that certain
|
// The view can trigger a re-render when it knows that certain
|
||||||
// web components have been loaded.
|
// web components have been loaded.
|
||||||
this._debouncedConfigChanged = debounce(
|
this._debouncedConfigChanged = debounce(
|
||||||
() => this._selectView(this._curView, true, false),
|
() => this._selectView(this._curView, true),
|
||||||
100,
|
100,
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
@@ -487,6 +489,10 @@ class HUIRoot extends LitElement {
|
|||||||
this.toggleAttribute("scrolled", window.scrollY !== 0);
|
this.toggleAttribute("scrolled", window.scrollY !== 0);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private _handlePopState = () => {
|
||||||
|
this._restoreScroll = true;
|
||||||
|
};
|
||||||
|
|
||||||
private _isVisible = (view: LovelaceViewConfig) =>
|
private _isVisible = (view: LovelaceViewConfig) =>
|
||||||
Boolean(
|
Boolean(
|
||||||
this._editMode ||
|
this._editMode ||
|
||||||
@@ -528,21 +534,19 @@ class HUIRoot extends LitElement {
|
|||||||
passive: true,
|
passive: true,
|
||||||
});
|
});
|
||||||
window.addEventListener("popstate", this._handlePopState);
|
window.addEventListener("popstate", this._handlePopState);
|
||||||
|
// Disable history scroll restoration because it is managed manually here
|
||||||
|
window.history.scrollRestoration = "manual";
|
||||||
}
|
}
|
||||||
|
|
||||||
public disconnectedCallback(): void {
|
public disconnectedCallback(): void {
|
||||||
super.disconnectedCallback();
|
super.disconnectedCallback();
|
||||||
window.removeEventListener("scroll", this._handleWindowScroll);
|
window.removeEventListener("scroll", this._handleWindowScroll);
|
||||||
window.removeEventListener("popstate", this._handlePopState);
|
window.removeEventListener("popstate", this._handlePopState);
|
||||||
|
this.toggleAttribute("scrolled", window.scrollY !== 0);
|
||||||
|
// Re-enable history scroll restoration when leaving the page
|
||||||
|
window.history.scrollRestoration = "auto";
|
||||||
}
|
}
|
||||||
|
|
||||||
private _restoreScroll = false;
|
|
||||||
|
|
||||||
private _handlePopState = () => {
|
|
||||||
// If we navigated back, we want to restore the scroll position.
|
|
||||||
this._restoreScroll = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
protected updated(changedProperties: PropertyValues): void {
|
protected updated(changedProperties: PropertyValues): void {
|
||||||
super.updated(changedProperties);
|
super.updated(changedProperties);
|
||||||
|
|
||||||
@@ -622,8 +626,16 @@ class HUIRoot extends LitElement {
|
|||||||
}
|
}
|
||||||
// Will allow for ripples to start rendering
|
// Will allow for ripples to start rendering
|
||||||
afterNextRender(() => {
|
afterNextRender(() => {
|
||||||
this._selectView(newSelectView, force, this._restoreScroll);
|
if (changedProperties.has("route")) {
|
||||||
|
const position =
|
||||||
|
(this._restoreScroll && this._viewScrollPositions[newSelectView]) ||
|
||||||
|
0;
|
||||||
this._restoreScroll = false;
|
this._restoreScroll = false;
|
||||||
|
requestAnimationFrame(() =>
|
||||||
|
scrollTo({ behavior: "auto", top: position })
|
||||||
|
);
|
||||||
|
}
|
||||||
|
this._selectView(newSelectView, force);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -932,15 +944,12 @@ class HUIRoot extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _selectView(
|
private _selectView(viewIndex: HUIRoot["_curView"], force: boolean): void {
|
||||||
viewIndex: HUIRoot["_curView"],
|
|
||||||
force: boolean,
|
|
||||||
restoreScroll: boolean
|
|
||||||
): void {
|
|
||||||
if (!force && this._curView === viewIndex) {
|
if (!force && this._curView === viewIndex) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Save scroll position of current view
|
||||||
if (this._curView != null) {
|
if (this._curView != null) {
|
||||||
this._viewScrollPositions[this._curView] = window.scrollY;
|
this._viewScrollPositions[this._curView] = window.scrollY;
|
||||||
}
|
}
|
||||||
@@ -983,15 +992,10 @@ class HUIRoot extends LitElement {
|
|||||||
|
|
||||||
if (!force && this._viewCache![viewIndex]) {
|
if (!force && this._viewCache![viewIndex]) {
|
||||||
view = this._viewCache![viewIndex];
|
view = this._viewCache![viewIndex];
|
||||||
const position = restoreScroll
|
|
||||||
? this._viewScrollPositions[viewIndex] || 0
|
|
||||||
: 0;
|
|
||||||
setTimeout(() => scrollTo({ behavior: "auto", top: position }), 0);
|
|
||||||
} else {
|
} else {
|
||||||
view = document.createElement("hui-view");
|
view = document.createElement("hui-view");
|
||||||
view.index = viewIndex;
|
view.index = viewIndex;
|
||||||
this._viewCache![viewIndex] = view;
|
this._viewCache![viewIndex] = view;
|
||||||
setTimeout(() => scrollTo({ behavior: "auto", top: 0 }), 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
view.lovelace = this.lovelace;
|
view.lovelace = this.lovelace;
|
||||||
|
@@ -185,7 +185,7 @@ export class HuiSection extends ReactiveElement {
|
|||||||
if (isStrategySection(sectionConfig)) {
|
if (isStrategySection(sectionConfig)) {
|
||||||
isStrategy = true;
|
isStrategy = true;
|
||||||
sectionConfig = await generateLovelaceSectionStrategy(
|
sectionConfig = await generateLovelaceSectionStrategy(
|
||||||
sectionConfig.strategy,
|
sectionConfig,
|
||||||
this.hass!
|
this.hass!
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
import { ReactiveElement } from "lit";
|
import { ReactiveElement } from "lit";
|
||||||
import { customElement } from "lit/decorators";
|
import { customElement } from "lit/decorators";
|
||||||
|
import { clamp } from "../../../../common/number/clamp";
|
||||||
import type { LovelaceBadgeConfig } from "../../../../data/lovelace/config/badge";
|
import type { LovelaceBadgeConfig } from "../../../../data/lovelace/config/badge";
|
||||||
import type { LovelaceCardConfig } from "../../../../data/lovelace/config/card";
|
import type { LovelaceCardConfig } from "../../../../data/lovelace/config/card";
|
||||||
import type { LovelaceSectionRawConfig } from "../../../../data/lovelace/config/section";
|
import type { LovelaceSectionRawConfig } from "../../../../data/lovelace/config/section";
|
||||||
@@ -144,7 +145,10 @@ export class AreaViewStrategy extends ReactiveElement {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Take the full width if there is only one section to avoid misalignment between cards and header
|
// Allow between 2 and 3 columns (the max should be set to define the width of the header)
|
||||||
|
const maxColumns = clamp(sections.length, 2, 3);
|
||||||
|
|
||||||
|
// Take the full width if there is only one section to avoid narrow header on desktop
|
||||||
if (sections.length === 1) {
|
if (sections.length === 1) {
|
||||||
sections[0].column_span = 2;
|
sections[0].column_span = 2;
|
||||||
}
|
}
|
||||||
@@ -160,7 +164,7 @@ export class AreaViewStrategy extends ReactiveElement {
|
|||||||
content: `## ${area.name}`,
|
content: `## ${area.name}`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
max_columns: 2,
|
max_columns: maxColumns,
|
||||||
sections: sections,
|
sections: sections,
|
||||||
badges: badges,
|
badges: badges,
|
||||||
};
|
};
|
||||||
|
@@ -1,13 +1,14 @@
|
|||||||
|
import { STATE_NOT_RUNNING } from "home-assistant-js-websocket";
|
||||||
import { ReactiveElement } from "lit";
|
import { ReactiveElement } from "lit";
|
||||||
import { customElement } from "lit/decorators";
|
import { customElement } from "lit/decorators";
|
||||||
import type { LovelaceConfig } from "../../../../data/lovelace/config/types";
|
import type { LovelaceConfig } from "../../../../data/lovelace/config/types";
|
||||||
import type { LovelaceViewRawConfig } from "../../../../data/lovelace/config/view";
|
import type { LovelaceViewRawConfig } from "../../../../data/lovelace/config/view";
|
||||||
import type { HomeAssistant } from "../../../../types";
|
import type { HomeAssistant } from "../../../../types";
|
||||||
|
import type { LovelaceStrategyEditor } from "../types";
|
||||||
import type {
|
import type {
|
||||||
AreaViewStrategyConfig,
|
AreaViewStrategyConfig,
|
||||||
EntitiesDisplay,
|
EntitiesDisplay,
|
||||||
} from "./area-view-strategy";
|
} from "./area-view-strategy";
|
||||||
import type { LovelaceStrategyEditor } from "../types";
|
|
||||||
import type { AreasViewStrategyConfig } from "./areas-overview-view-strategy";
|
import type { AreasViewStrategyConfig } from "./areas-overview-view-strategy";
|
||||||
import { computeAreaPath, getAreas } from "./helpers/areas-strategy-helper";
|
import { computeAreaPath, getAreas } from "./helpers/areas-strategy-helper";
|
||||||
|
|
||||||
@@ -30,6 +31,28 @@ export class AreasDashboardStrategy extends ReactiveElement {
|
|||||||
config: AreasDashboardStrategyConfig,
|
config: AreasDashboardStrategyConfig,
|
||||||
hass: HomeAssistant
|
hass: HomeAssistant
|
||||||
): Promise<LovelaceConfig> {
|
): Promise<LovelaceConfig> {
|
||||||
|
if (hass.config.state === STATE_NOT_RUNNING) {
|
||||||
|
return {
|
||||||
|
views: [
|
||||||
|
{
|
||||||
|
type: "sections",
|
||||||
|
sections: [{ cards: [{ type: "starting" }] }],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hass.config.recovery_mode) {
|
||||||
|
return {
|
||||||
|
views: [
|
||||||
|
{
|
||||||
|
type: "sections",
|
||||||
|
sections: [{ cards: [{ type: "recovery-mode" }] }],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const areas = getAreas(
|
const areas = getAreas(
|
||||||
hass.areas,
|
hass.areas,
|
||||||
config.areas_display?.hidden,
|
config.areas_display?.hidden,
|
||||||
|
@@ -95,7 +95,7 @@ export class AreasOverviewViewStrategy extends ReactiveElement {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
type: "sections",
|
type: "sections",
|
||||||
max_columns: 2,
|
max_columns: 3,
|
||||||
sections: areaSections,
|
sections: areaSections,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@@ -1,10 +1,18 @@
|
|||||||
|
import type {
|
||||||
|
LovelaceSectionConfig,
|
||||||
|
LovelaceStrategySectionConfig,
|
||||||
|
} from "../../../data/lovelace/config/section";
|
||||||
|
import type { LovelaceStrategyConfig } from "../../../data/lovelace/config/strategy";
|
||||||
import type {
|
import type {
|
||||||
LovelaceConfig,
|
LovelaceConfig,
|
||||||
|
LovelaceDashboardStrategyConfig,
|
||||||
LovelaceRawConfig,
|
LovelaceRawConfig,
|
||||||
} from "../../../data/lovelace/config/types";
|
} from "../../../data/lovelace/config/types";
|
||||||
import { isStrategyDashboard } from "../../../data/lovelace/config/types";
|
import { isStrategyDashboard } from "../../../data/lovelace/config/types";
|
||||||
import type { LovelaceStrategyConfig } from "../../../data/lovelace/config/strategy";
|
import type {
|
||||||
import type { LovelaceViewConfig } from "../../../data/lovelace/config/view";
|
LovelaceStrategyViewConfig,
|
||||||
|
LovelaceViewConfig,
|
||||||
|
} from "../../../data/lovelace/config/view";
|
||||||
import { isStrategyView } from "../../../data/lovelace/config/view";
|
import { isStrategyView } from "../../../data/lovelace/config/view";
|
||||||
import type { AsyncReturnType, HomeAssistant } from "../../../types";
|
import type { AsyncReturnType, HomeAssistant } from "../../../types";
|
||||||
import { cleanLegacyStrategyConfig, isLegacyStrategy } from "./legacy-strategy";
|
import { cleanLegacyStrategyConfig, isLegacyStrategy } from "./legacy-strategy";
|
||||||
@@ -133,10 +141,11 @@ const generateStrategy = async <T extends LovelaceStrategyConfigType>(
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const generateLovelaceDashboardStrategy = async (
|
export const generateLovelaceDashboardStrategy = async (
|
||||||
strategyConfig: LovelaceStrategyConfig,
|
config: LovelaceDashboardStrategyConfig,
|
||||||
hass: HomeAssistant
|
hass: HomeAssistant
|
||||||
): Promise<LovelaceConfig> =>
|
): Promise<LovelaceConfig> => {
|
||||||
generateStrategy(
|
const { strategy, ...base } = config;
|
||||||
|
const generated = await generateStrategy(
|
||||||
"dashboard",
|
"dashboard",
|
||||||
(err) => ({
|
(err) => ({
|
||||||
views: [
|
views: [
|
||||||
@@ -151,15 +160,21 @@ export const generateLovelaceDashboardStrategy = async (
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
strategyConfig,
|
strategy,
|
||||||
hass
|
hass
|
||||||
);
|
);
|
||||||
|
return {
|
||||||
|
...base,
|
||||||
|
...generated,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
export const generateLovelaceViewStrategy = async (
|
export const generateLovelaceViewStrategy = async (
|
||||||
strategyConfig: LovelaceStrategyConfig,
|
config: LovelaceStrategyViewConfig,
|
||||||
hass: HomeAssistant
|
hass: HomeAssistant
|
||||||
): Promise<LovelaceViewConfig> =>
|
): Promise<LovelaceViewConfig> => {
|
||||||
generateStrategy(
|
const { strategy, ...base } = config;
|
||||||
|
const generated = await generateStrategy(
|
||||||
"view",
|
"view",
|
||||||
(err) => ({
|
(err) => ({
|
||||||
cards: [
|
cards: [
|
||||||
@@ -169,15 +184,21 @@ export const generateLovelaceViewStrategy = async (
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
strategyConfig,
|
strategy,
|
||||||
hass
|
hass
|
||||||
);
|
);
|
||||||
|
return {
|
||||||
|
...base,
|
||||||
|
...generated,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
export const generateLovelaceSectionStrategy = async (
|
export const generateLovelaceSectionStrategy = async (
|
||||||
strategyConfig: LovelaceStrategyConfig,
|
config: LovelaceStrategySectionConfig,
|
||||||
hass: HomeAssistant
|
hass: HomeAssistant
|
||||||
): Promise<LovelaceViewConfig> =>
|
): Promise<LovelaceSectionConfig> => {
|
||||||
generateStrategy(
|
const { strategy, ...base } = config;
|
||||||
|
const generated = await generateStrategy(
|
||||||
"section",
|
"section",
|
||||||
(err) => ({
|
(err) => ({
|
||||||
cards: [
|
cards: [
|
||||||
@@ -187,9 +208,14 @@ export const generateLovelaceSectionStrategy = async (
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
strategyConfig,
|
strategy,
|
||||||
hass
|
hass
|
||||||
);
|
);
|
||||||
|
return {
|
||||||
|
...base,
|
||||||
|
...generated,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find all references to strategies and replaces them with the generated output
|
* Find all references to strategies and replaces them with the generated output
|
||||||
@@ -199,20 +225,20 @@ export const expandLovelaceConfigStrategies = async (
|
|||||||
hass: HomeAssistant
|
hass: HomeAssistant
|
||||||
): Promise<LovelaceConfig> => {
|
): Promise<LovelaceConfig> => {
|
||||||
const newConfig = isStrategyDashboard(config)
|
const newConfig = isStrategyDashboard(config)
|
||||||
? await generateLovelaceDashboardStrategy(config.strategy, hass)
|
? await generateLovelaceDashboardStrategy(config, hass)
|
||||||
: { ...config };
|
: { ...config };
|
||||||
|
|
||||||
newConfig.views = await Promise.all(
|
newConfig.views = await Promise.all(
|
||||||
newConfig.views.map(async (view) => {
|
newConfig.views.map(async (view) => {
|
||||||
const newView = isStrategyView(view)
|
const newView = isStrategyView(view)
|
||||||
? await generateLovelaceViewStrategy(view.strategy, hass)
|
? await generateLovelaceViewStrategy(view, hass)
|
||||||
: { ...view };
|
: { ...view };
|
||||||
|
|
||||||
if (newView.sections) {
|
if (newView.sections) {
|
||||||
newView.sections = await Promise.all(
|
newView.sections = await Promise.all(
|
||||||
newView.sections.map(async (section) => {
|
newView.sections.map(async (section) => {
|
||||||
const newSection = isStrategyView(section)
|
const newSection = isStrategyView(section)
|
||||||
? await generateLovelaceSectionStrategy(section.strategy, hass)
|
? await generateLovelaceSectionStrategy(section, hass)
|
||||||
: { ...section };
|
: { ...section };
|
||||||
return newSection;
|
return newSection;
|
||||||
})
|
})
|
||||||
|
@@ -233,10 +233,7 @@ export class HUIView extends ReactiveElement {
|
|||||||
|
|
||||||
if (isStrategyView(viewConfig)) {
|
if (isStrategyView(viewConfig)) {
|
||||||
isStrategy = true;
|
isStrategy = true;
|
||||||
viewConfig = await generateLovelaceViewStrategy(
|
viewConfig = await generateLovelaceViewStrategy(viewConfig, this.hass!);
|
||||||
viewConfig.strategy,
|
|
||||||
this.hass!
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
viewConfig = {
|
viewConfig = {
|
||||||
|
@@ -2902,7 +2902,8 @@
|
|||||||
"device_consumption_energy": "Device energy consumption",
|
"device_consumption_energy": "Device energy consumption",
|
||||||
"selected_stat_intro": "Select the energy sensor that measures the device's energy usage in either of {unit}.",
|
"selected_stat_intro": "Select the energy sensor that measures the device's energy usage in either of {unit}.",
|
||||||
"included_in_device": "Upstream device",
|
"included_in_device": "Upstream device",
|
||||||
"included_in_device_helper": "If this device is already counted by another device (such as a smart switch measured by a smart breaker), selecting the upstream device prevents duplicate energy tracking."
|
"included_in_device_helper": "If this device is already counted by another device (such as a smart switch measured by a smart breaker), selecting the upstream device prevents duplicate energy tracking.",
|
||||||
|
"no_upstream_devices": "No eligible upstream devices"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -3395,8 +3396,8 @@
|
|||||||
"high": "High"
|
"high": "High"
|
||||||
},
|
},
|
||||||
"commands": {
|
"commands": {
|
||||||
"header": "Supported commands",
|
"header": "Language support",
|
||||||
"low": "Needs work",
|
"low": "Needs more work",
|
||||||
"ready": "Ready to be used",
|
"ready": "Ready to be used",
|
||||||
"high": "Fully supported"
|
"high": "Fully supported"
|
||||||
},
|
},
|
||||||
@@ -3430,21 +3431,22 @@
|
|||||||
},
|
},
|
||||||
"local": {
|
"local": {
|
||||||
"title": "Installing add-ons",
|
"title": "Installing add-ons",
|
||||||
"secondary": "The Whisper and Piper add-ons are being installed and configured based on your hardware.",
|
"secondary": "We are preparing your system for local voice processing.",
|
||||||
"failed_title": "Failed to install add-ons",
|
"failed_title": "Failed to install add-ons",
|
||||||
"failed_secondary": "We were unable to install the Whisper and Piper add-ons automatically for you. Read the documentation to learn how to install them.",
|
"failed_secondary": "We were unable to install the add-ons for speech-to-text and text-to-speech automatically for you. Read the documentation to learn how to install them.",
|
||||||
"not_supported_title": "Installation of add-ons is not supported on your system",
|
"not_supported_title": "Installation of add-ons is not supported on your system",
|
||||||
"not_supported_secondary": "Your system is not supported to automatically install a local TTS and STT provider. Learn how to set up local TTS and STT providers in the documentation.",
|
"not_supported_secondary": "Your system is not supported to automatically install a local TTS and STT provider. Learn how to set up local TTS and STT providers in the documentation.",
|
||||||
"local_pipeline": "Local Assistant",
|
"full_local_pipeline": "Full local Assistant",
|
||||||
|
"focused_local_pipeline": "Focused local Assistant",
|
||||||
"state": {
|
"state": {
|
||||||
"installing_piper": "Installing Piper add-on",
|
"installing_piper": "Installing Piper",
|
||||||
"starting_piper": "Starting Piper add-on",
|
"starting_piper": "Starting Piper",
|
||||||
"setup_piper": "Setting up Piper",
|
"setup_piper": "Setting up Piper",
|
||||||
"installing_faster-whisper": "Installing Whisper add-on",
|
"installing_faster-whisper": "Installing Whisper",
|
||||||
"starting_faster-whisper": "Starting Whisper add-on",
|
"starting_faster-whisper": "Starting Whisper",
|
||||||
"setup_faster-whisper": "Setting up Whisper",
|
"setup_faster-whisper": "Setting up Whisper",
|
||||||
"installing_speech-to-phrase": "Installing Speech-to-Phrase add-on",
|
"installing_speech-to-phrase": "Installing Speech-to-Phrase",
|
||||||
"starting_speech-to-phrase": "Starting Speech-to-Phrase add-on",
|
"starting_speech-to-phrase": "Starting Speech-to-Phrase",
|
||||||
"setup_speech-to-phrase": "Setting up Speech-to-Phrase",
|
"setup_speech-to-phrase": "Setting up Speech-to-Phrase",
|
||||||
"creating_pipeline": "Creating assistant"
|
"creating_pipeline": "Creating assistant"
|
||||||
},
|
},
|
||||||
@@ -7160,12 +7162,17 @@
|
|||||||
"large": "Large"
|
"large": "Large"
|
||||||
},
|
},
|
||||||
"show_seconds": "Display seconds",
|
"show_seconds": "Display seconds",
|
||||||
"time_format": "Time format",
|
"time_format": "[%key:ui::panel::profile::time_format::dropdown_label%]",
|
||||||
"time_formats": {
|
"time_formats": {
|
||||||
|
"auto": "Use user settings",
|
||||||
"language": "[%key:ui::panel::profile::time_format::formats::language%]",
|
"language": "[%key:ui::panel::profile::time_format::formats::language%]",
|
||||||
"system": "[%key:ui::panel::profile::time_format::formats::system%]",
|
"system": "[%key:ui::panel::profile::time_format::formats::system%]",
|
||||||
"24": "[%key:ui::panel::profile::time_format::formats::24%]",
|
"24": "[%key:ui::panel::profile::time_format::formats::24%]",
|
||||||
"12": "[%key:ui::panel::profile::time_format::formats::12%]"
|
"12": "[%key:ui::panel::profile::time_format::formats::12%]"
|
||||||
|
},
|
||||||
|
"time_zone": "[%key:ui::panel::profile::time_zone::dropdown_label%]",
|
||||||
|
"time_zones": {
|
||||||
|
"auto": "Use user settings"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"media-control": {
|
"media-control": {
|
||||||
@@ -8129,7 +8136,7 @@
|
|||||||
},
|
},
|
||||||
"mean_type": {
|
"mean_type": {
|
||||||
"0": "None",
|
"0": "None",
|
||||||
"1": "Arimethic",
|
"1": "Arithmetic",
|
||||||
"2": "Circular"
|
"2": "Circular"
|
||||||
},
|
},
|
||||||
"fix_issue": {
|
"fix_issue": {
|
||||||
|
Reference in New Issue
Block a user