mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-28 03:36:44 +00:00
Merge branch 'rc'
This commit is contained in:
commit
e3221ad4ee
@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "home-assistant-frontend"
|
||||
version = "20250502.1"
|
||||
version = "20250506.0"
|
||||
license = "Apache-2.0"
|
||||
license-files = ["LICENSE*"]
|
||||
description = "The Home Assistant frontend"
|
||||
|
@ -603,7 +603,7 @@ export class HaDataTable extends LitElement {
|
||||
.map(
|
||||
([key2, column2], i) =>
|
||||
html`${i !== 0
|
||||
? " ⸱ "
|
||||
? " · "
|
||||
: nothing}${column2.template
|
||||
? column2.template(row)
|
||||
: row[key2]}`
|
||||
|
@ -5,7 +5,6 @@ import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import type { PropertyValues, TemplateResult } from "lit";
|
||||
import { html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { styleMap } from "lit/directives/style-map";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import { computeAreaName } from "../../common/entity/compute_area_name";
|
||||
@ -30,28 +29,17 @@ import "../ha-icon-button";
|
||||
import "../ha-svg-icon";
|
||||
import "./state-badge";
|
||||
|
||||
const FAKE_ENTITY: HassEntity = {
|
||||
entity_id: "",
|
||||
state: "",
|
||||
last_changed: "",
|
||||
last_updated: "",
|
||||
context: { id: "", user_id: null, parent_id: null },
|
||||
attributes: {},
|
||||
};
|
||||
|
||||
interface EntityComboBoxItem extends HassEntity {
|
||||
interface EntityComboBoxItem {
|
||||
// Force empty label to always display empty value by default in the search field
|
||||
id: string;
|
||||
label: "";
|
||||
primary: string;
|
||||
secondary?: string;
|
||||
translated_domain?: string;
|
||||
show_entity_id?: boolean;
|
||||
entity_name?: string;
|
||||
area_name?: string;
|
||||
device_name?: string;
|
||||
friendly_name?: string;
|
||||
domain_name?: string;
|
||||
search_labels?: string[];
|
||||
sorting_label?: string;
|
||||
icon_path?: string;
|
||||
stateObj?: HassEntity;
|
||||
}
|
||||
|
||||
export type HaEntityComboBoxEntityFilterFunc = (entity: HassEntity) => boolean;
|
||||
@ -59,22 +47,6 @@ export type HaEntityComboBoxEntityFilterFunc = (entity: HassEntity) => boolean;
|
||||
const CREATE_ID = "___create-new-entity___";
|
||||
const NO_ENTITIES_ID = "___no-entities___";
|
||||
|
||||
const DOMAIN_STYLE = styleMap({
|
||||
fontSize: "var(--ha-font-size-s)",
|
||||
fontWeight: "var(--ha-font-weight-normal)",
|
||||
lineHeight: "var(--ha-line-height-normal)",
|
||||
alignSelf: "flex-end",
|
||||
maxWidth: "30%",
|
||||
textOverflow: "ellipsis",
|
||||
overflow: "hidden",
|
||||
whiteSpace: "nowrap",
|
||||
});
|
||||
|
||||
const ENTITY_ID_STYLE = styleMap({
|
||||
fontFamily: "var(--ha-font-family-code)",
|
||||
fontSize: "var(--ha-font-size-xs)",
|
||||
});
|
||||
|
||||
@customElement("ha-entity-combo-box")
|
||||
export class HaEntityComboBox extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
@ -177,14 +149,19 @@ export class HaEntityComboBox extends LitElement {
|
||||
private _rowRenderer: ComboBoxLitRenderer<EntityComboBoxItem> = (
|
||||
item,
|
||||
{ index }
|
||||
) => html`
|
||||
) => {
|
||||
const showEntityId = this.hass.userData?.showEntityIdPicker;
|
||||
|
||||
return html`
|
||||
<ha-combo-box-item type="button" compact .borderTop=${index !== 0}>
|
||||
${item.icon_path
|
||||
? html`<ha-svg-icon slot="start" .path=${item.icon_path}></ha-svg-icon>`
|
||||
? html`
|
||||
<ha-svg-icon slot="start" .path=${item.icon_path}></ha-svg-icon>
|
||||
`
|
||||
: html`
|
||||
<state-badge
|
||||
slot="start"
|
||||
.stateObj=${item}
|
||||
.stateObj=${item.stateObj}
|
||||
.hass=${this.hass}
|
||||
></state-badge>
|
||||
`}
|
||||
@ -192,18 +169,21 @@ export class HaEntityComboBox extends LitElement {
|
||||
${item.secondary
|
||||
? html`<span slot="supporting-text">${item.secondary}</span>`
|
||||
: nothing}
|
||||
${item.entity_id && item.show_entity_id
|
||||
? html`<span slot="supporting-text" style=${ENTITY_ID_STYLE}
|
||||
>${item.entity_id}</span
|
||||
>`
|
||||
${item.stateObj && showEntityId
|
||||
? html`
|
||||
<span slot="supporting-text" class="code">
|
||||
${item.stateObj.entity_id}
|
||||
</span>
|
||||
`
|
||||
: nothing}
|
||||
${item.translated_domain && !item.show_entity_id
|
||||
? html`<div slot="trailing-supporting-text" style=${DOMAIN_STYLE}>
|
||||
${item.translated_domain}
|
||||
</div>`
|
||||
${item.domain_name && !showEntityId
|
||||
? html`
|
||||
<div slot="trailing-supporting-text">${item.domain_name}</div>
|
||||
`
|
||||
: nothing}
|
||||
</ha-combo-box-item>
|
||||
`;
|
||||
};
|
||||
|
||||
private _getItems = memoizeOne(
|
||||
(
|
||||
@ -218,7 +198,7 @@ export class HaEntityComboBox extends LitElement {
|
||||
excludeEntities: this["excludeEntities"],
|
||||
createDomains: this["createDomains"]
|
||||
): EntityComboBoxItem[] => {
|
||||
let states: EntityComboBoxItem[] = [];
|
||||
let items: EntityComboBoxItem[] = [];
|
||||
|
||||
let entityIds = Object.keys(hass.states);
|
||||
|
||||
@ -236,9 +216,8 @@ export class HaEntityComboBox extends LitElement {
|
||||
);
|
||||
|
||||
return {
|
||||
...FAKE_ENTITY,
|
||||
id: CREATE_ID + domain,
|
||||
label: "",
|
||||
entity_id: CREATE_ID + domain,
|
||||
primary: primary,
|
||||
secondary: this.hass.localize(
|
||||
"ui.components.entity.entity-picker.new_entity"
|
||||
@ -251,9 +230,8 @@ export class HaEntityComboBox extends LitElement {
|
||||
if (!entityIds.length) {
|
||||
return [
|
||||
{
|
||||
...FAKE_ENTITY,
|
||||
id: NO_ENTITIES_ID,
|
||||
label: "",
|
||||
entity_id: NO_ENTITIES_ID,
|
||||
primary: this.hass!.localize(
|
||||
"ui.components.entity.entity-picker.no_entities"
|
||||
),
|
||||
@ -289,7 +267,7 @@ export class HaEntityComboBox extends LitElement {
|
||||
|
||||
const isRTL = computeRTL(this.hass);
|
||||
|
||||
states = entityIds
|
||||
items = entityIds
|
||||
.map<EntityComboBoxItem>((entityId) => {
|
||||
const stateObj = hass!.states[entityId];
|
||||
|
||||
@ -300,28 +278,32 @@ export class HaEntityComboBox extends LitElement {
|
||||
const deviceName = device ? computeDeviceName(device) : undefined;
|
||||
const areaName = area ? computeAreaName(area) : undefined;
|
||||
|
||||
const domainName = domainToName(
|
||||
this.hass.localize,
|
||||
computeDomain(entityId)
|
||||
);
|
||||
|
||||
const primary = entityName || deviceName || entityId;
|
||||
const secondary = [areaName, entityName ? deviceName : undefined]
|
||||
.filter(Boolean)
|
||||
.join(isRTL ? " ◂ " : " ▸ ");
|
||||
|
||||
const translatedDomain = domainToName(
|
||||
this.hass.localize,
|
||||
computeDomain(entityId)
|
||||
);
|
||||
|
||||
return {
|
||||
...hass!.states[entityId],
|
||||
id: entityId,
|
||||
label: "",
|
||||
primary: primary,
|
||||
secondary: secondary,
|
||||
translated_domain: translatedDomain,
|
||||
sorting_label: [deviceName, entityName].filter(Boolean).join("-"),
|
||||
entity_name: entityName || deviceName,
|
||||
area_name: areaName,
|
||||
device_name: deviceName,
|
||||
friendly_name: friendlyName,
|
||||
show_entity_id: hass.userData?.showEntityIdPicker,
|
||||
domain_name: domainName,
|
||||
sorting_label: [deviceName, entityName].filter(Boolean).join("_"),
|
||||
search_labels: [
|
||||
entityName,
|
||||
deviceName,
|
||||
areaName,
|
||||
domainName,
|
||||
friendlyName,
|
||||
entityId,
|
||||
].filter(Boolean) as string[],
|
||||
stateObj: stateObj,
|
||||
};
|
||||
})
|
||||
.sort((entityA, entityB) =>
|
||||
@ -333,41 +315,43 @@ export class HaEntityComboBox extends LitElement {
|
||||
);
|
||||
|
||||
if (includeDeviceClasses) {
|
||||
states = states.filter(
|
||||
(stateObj) =>
|
||||
items = items.filter(
|
||||
(item) =>
|
||||
// We always want to include the entity of the current value
|
||||
stateObj.entity_id === this.value ||
|
||||
(stateObj.attributes.device_class &&
|
||||
includeDeviceClasses.includes(stateObj.attributes.device_class))
|
||||
item.id === this.value ||
|
||||
(item.stateObj?.attributes.device_class &&
|
||||
includeDeviceClasses.includes(
|
||||
item.stateObj.attributes.device_class
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
if (includeUnitOfMeasurement) {
|
||||
states = states.filter(
|
||||
(stateObj) =>
|
||||
items = items.filter(
|
||||
(item) =>
|
||||
// We always want to include the entity of the current value
|
||||
stateObj.entity_id === this.value ||
|
||||
(stateObj.attributes.unit_of_measurement &&
|
||||
item.id === this.value ||
|
||||
(item.stateObj?.attributes.unit_of_measurement &&
|
||||
includeUnitOfMeasurement.includes(
|
||||
stateObj.attributes.unit_of_measurement
|
||||
item.stateObj.attributes.unit_of_measurement
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
if (entityFilter) {
|
||||
states = states.filter(
|
||||
(stateObj) =>
|
||||
items = items.filter(
|
||||
(item) =>
|
||||
// We always want to include the entity of the current value
|
||||
stateObj.entity_id === this.value || entityFilter!(stateObj)
|
||||
item.id === this.value ||
|
||||
(item.stateObj && entityFilter!(item.stateObj))
|
||||
);
|
||||
}
|
||||
|
||||
if (!states.length) {
|
||||
if (!items.length) {
|
||||
return [
|
||||
{
|
||||
...FAKE_ENTITY,
|
||||
id: NO_ENTITIES_ID,
|
||||
label: "",
|
||||
entity_id: NO_ENTITIES_ID,
|
||||
primary: this.hass!.localize(
|
||||
"ui.components.entity.entity-picker.no_match"
|
||||
),
|
||||
@ -378,10 +362,10 @@ export class HaEntityComboBox extends LitElement {
|
||||
}
|
||||
|
||||
if (createItems?.length) {
|
||||
states.push(...createItems);
|
||||
items.push(...createItems);
|
||||
}
|
||||
|
||||
return states;
|
||||
return items;
|
||||
}
|
||||
);
|
||||
|
||||
@ -424,7 +408,7 @@ export class HaEntityComboBox extends LitElement {
|
||||
protected render(): TemplateResult {
|
||||
return html`
|
||||
<ha-combo-box
|
||||
item-value-path="entity_id"
|
||||
item-value-path="id"
|
||||
.hass=${this.hass}
|
||||
.value=${this._value}
|
||||
.label=${this.label === undefined
|
||||
@ -476,17 +460,7 @@ export class HaEntityComboBox extends LitElement {
|
||||
}
|
||||
|
||||
private _fuseIndex = memoizeOne((states: EntityComboBoxItem[]) =>
|
||||
Fuse.createIndex(
|
||||
[
|
||||
"entity_name",
|
||||
"device_name",
|
||||
"area_name",
|
||||
"translated_domain",
|
||||
"friendly_name", // for backwards compatibility
|
||||
"entity_id", // for technical search
|
||||
],
|
||||
states
|
||||
)
|
||||
Fuse.createIndex(["search_labels"], states)
|
||||
);
|
||||
|
||||
private _filterChanged(ev: CustomEvent): void {
|
||||
@ -503,9 +477,8 @@ export class HaEntityComboBox extends LitElement {
|
||||
if (results.length === 0) {
|
||||
target.filteredItems = [
|
||||
{
|
||||
...FAKE_ENTITY,
|
||||
id: NO_ENTITIES_ID,
|
||||
label: "",
|
||||
entity_id: NO_ENTITIES_ID,
|
||||
primary: this.hass!.localize(
|
||||
"ui.components.entity.entity-picker.no_match"
|
||||
),
|
||||
|
@ -1,11 +1,10 @@
|
||||
import { mdiChartLine, mdiShape } from "@mdi/js";
|
||||
import { mdiChartLine, mdiHelpCircle, mdiShape } from "@mdi/js";
|
||||
import type { ComboBoxLitRenderer } from "@vaadin/combo-box/lit";
|
||||
import Fuse from "fuse.js";
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import type { PropertyValues, TemplateResult } from "lit";
|
||||
import { html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { styleMap } from "lit/directives/style-map";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { ensureArray } from "../../common/array/ensure-array";
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
@ -26,31 +25,27 @@ import type { HaComboBox } from "../ha-combo-box";
|
||||
import "../ha-combo-box-item";
|
||||
import "../ha-svg-icon";
|
||||
import "./state-badge";
|
||||
import { documentationUrl } from "../../util/documentation-url";
|
||||
|
||||
type StatisticItemType = "entity" | "external" | "no_state";
|
||||
|
||||
interface StatisticItem {
|
||||
// Force empty label to always display empty value by default in the search field
|
||||
id: string;
|
||||
statistic_id?: string;
|
||||
label: "";
|
||||
primary: string;
|
||||
secondary?: string;
|
||||
show_entity_id?: boolean;
|
||||
entity_name?: string;
|
||||
area_name?: string;
|
||||
device_name?: string;
|
||||
friendly_name?: string;
|
||||
search_labels?: string[];
|
||||
sorting_label?: string;
|
||||
state?: HassEntity;
|
||||
icon_path?: string;
|
||||
type?: StatisticItemType;
|
||||
iconPath?: string;
|
||||
stateObj?: HassEntity;
|
||||
}
|
||||
|
||||
const TYPE_ORDER = ["entity", "external", "no_state"] as StatisticItemType[];
|
||||
const MISSING_ID = "___missing-entity___";
|
||||
|
||||
const ENTITY_ID_STYLE = styleMap({
|
||||
fontFamily: "var(--ha-font-family-code)",
|
||||
fontSize: "11px",
|
||||
});
|
||||
const TYPE_ORDER = ["entity", "external", "no_state"] as StatisticItemType[];
|
||||
|
||||
@customElement("ha-statistic-combo-box")
|
||||
export class HaStatisticComboBox extends LitElement {
|
||||
@ -131,37 +126,39 @@ export class HaStatisticComboBox extends LitElement {
|
||||
private _rowRenderer: ComboBoxLitRenderer<StatisticItem> = (
|
||||
item,
|
||||
{ index }
|
||||
) => html`
|
||||
) => {
|
||||
const showEntityId = this.hass.userData?.showEntityIdPicker;
|
||||
return html`
|
||||
<ha-combo-box-item type="button" compact .borderTop=${index !== 0}>
|
||||
${!item.state
|
||||
${item.icon_path
|
||||
? html`
|
||||
<ha-svg-icon
|
||||
style="margin: 0 4px"
|
||||
slot="start"
|
||||
.path=${item.iconPath}
|
||||
.path=${item.icon_path}
|
||||
></ha-svg-icon>
|
||||
`
|
||||
: html`
|
||||
: item.stateObj
|
||||
? html`
|
||||
<state-badge
|
||||
slot="start"
|
||||
.stateObj=${item.state}
|
||||
.stateObj=${item.stateObj}
|
||||
.hass=${this.hass}
|
||||
></state-badge>
|
||||
`}
|
||||
|
||||
`
|
||||
: nothing}
|
||||
<span slot="headline">${item.primary} </span>
|
||||
${item.secondary
|
||||
? html`<span slot="supporting-text">${item.secondary}</span>`
|
||||
: nothing}
|
||||
${item.id && item.show_entity_id
|
||||
? html`
|
||||
<span slot="supporting-text" style=${ENTITY_ID_STYLE}>
|
||||
${item.id}
|
||||
</span>
|
||||
`
|
||||
${item.id && showEntityId
|
||||
? html`<span slot="supporting-text" class="code">
|
||||
${item.statistic_id}
|
||||
</span>`
|
||||
: nothing}
|
||||
</ha-combo-box-item>
|
||||
`;
|
||||
};
|
||||
|
||||
private _getItems = memoizeOne(
|
||||
(
|
||||
@ -249,19 +246,22 @@ export class HaStatisticComboBox extends LitElement {
|
||||
label: "",
|
||||
type,
|
||||
sorting_label: label,
|
||||
iconPath: mdiShape,
|
||||
search_labels: [label, id],
|
||||
icon_path: mdiShape,
|
||||
});
|
||||
} else if (type === "external") {
|
||||
const domain = id.split(":")[0];
|
||||
const domainName = domainToName(this.hass.localize, domain);
|
||||
output.push({
|
||||
id,
|
||||
statistic_id: id,
|
||||
primary: label,
|
||||
secondary: domainName,
|
||||
label: "",
|
||||
type,
|
||||
sorting_label: label,
|
||||
iconPath: mdiChartLine,
|
||||
search_labels: [label, domainName, id],
|
||||
icon_path: mdiChartLine,
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -283,17 +283,20 @@ export class HaStatisticComboBox extends LitElement {
|
||||
|
||||
output.push({
|
||||
id,
|
||||
statistic_id: id,
|
||||
label: "",
|
||||
primary,
|
||||
secondary,
|
||||
label: "",
|
||||
state: stateObj,
|
||||
stateObj: stateObj,
|
||||
type: "entity",
|
||||
sorting_label: [deviceName, entityName].join("_"),
|
||||
entity_name: entityName || deviceName,
|
||||
area_name: areaName,
|
||||
device_name: deviceName,
|
||||
friendly_name: friendlyName,
|
||||
show_entity_id: hass.userData?.showEntityIdPicker,
|
||||
search_labels: [
|
||||
entityName,
|
||||
deviceName,
|
||||
areaName,
|
||||
friendlyName,
|
||||
id,
|
||||
].filter(Boolean) as string[],
|
||||
});
|
||||
});
|
||||
|
||||
@ -323,11 +326,12 @@ export class HaStatisticComboBox extends LitElement {
|
||||
}
|
||||
|
||||
output.push({
|
||||
id: "__missing",
|
||||
id: MISSING_ID,
|
||||
primary: this.hass.localize(
|
||||
"ui.components.statistic-picker.missing_entity"
|
||||
),
|
||||
label: "",
|
||||
icon_path: mdiHelpCircle,
|
||||
});
|
||||
|
||||
return output;
|
||||
@ -422,8 +426,12 @@ export class HaStatisticComboBox extends LitElement {
|
||||
private _statisticChanged(ev: ValueChangedEvent<string>) {
|
||||
ev.stopPropagation();
|
||||
let newValue = ev.detail.value;
|
||||
if (newValue === "__missing") {
|
||||
if (newValue === MISSING_ID) {
|
||||
newValue = "";
|
||||
window.open(
|
||||
documentationUrl(this.hass, this.helpMissingEntityUrl),
|
||||
"_blank"
|
||||
);
|
||||
}
|
||||
|
||||
if (newValue !== this._value) {
|
||||
@ -436,16 +444,7 @@ export class HaStatisticComboBox extends LitElement {
|
||||
}
|
||||
|
||||
private _fuseIndex = memoizeOne((states: StatisticItem[]) =>
|
||||
Fuse.createIndex(
|
||||
[
|
||||
"entity_name",
|
||||
"device_name",
|
||||
"area_name",
|
||||
"friendly_name", // for backwards compatibility
|
||||
"id", // for technical search
|
||||
],
|
||||
states
|
||||
)
|
||||
Fuse.createIndex(["search_labels"], states)
|
||||
);
|
||||
|
||||
private _filterChanged(ev: CustomEvent): void {
|
||||
|
@ -35,6 +35,20 @@ export class HaComboBoxItem extends HaMdListItem {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
}
|
||||
::slotted(.code) {
|
||||
font-family: var(--ha-font-family-code);
|
||||
font-size: var(--ha-font-size-xs);
|
||||
}
|
||||
[slot="trailing-supporting-text"] {
|
||||
font-size: var(--ha-font-size-s);
|
||||
font-weight: var(--ha-font-weight-normal);
|
||||
line-height: var(--ha-line-height-normal);
|
||||
align-self: flex-end;
|
||||
max-width: 30%;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
@ -6,6 +6,13 @@ import { customElement } from "lit/decorators";
|
||||
|
||||
@customElement("ha-outlined-icon-button")
|
||||
export class HaOutlinedIconButton extends IconButton {
|
||||
protected override getRenderClasses() {
|
||||
return {
|
||||
...super.getRenderClasses(),
|
||||
outlined: true,
|
||||
};
|
||||
}
|
||||
|
||||
static override styles = [
|
||||
css`
|
||||
.icon-button {
|
||||
|
@ -852,8 +852,8 @@ class HaSidebar extends SubscribeMixin(LitElement) {
|
||||
color: var(--sidebar-icon-color);
|
||||
}
|
||||
.title {
|
||||
margin-left: 19px;
|
||||
margin-inline-start: 19px;
|
||||
margin-left: 3px;
|
||||
margin-inline-start: 3px;
|
||||
margin-inline-end: initial;
|
||||
width: 100%;
|
||||
display: none;
|
||||
@ -940,7 +940,6 @@ class HaSidebar extends SubscribeMixin(LitElement) {
|
||||
|
||||
ha-md-list-item .item-text {
|
||||
display: none;
|
||||
max-width: calc(100% - 56px);
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ import "@material/mwc-button/mwc-button";
|
||||
import type { PropertyValues } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { mdiContentCopy } from "@mdi/js";
|
||||
import { storage } from "../../common/decorators/storage";
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import type {
|
||||
@ -17,6 +18,8 @@ import "../ha-language-picker";
|
||||
import "../ha-tts-voice-picker";
|
||||
import "../ha-card";
|
||||
import { fetchCloudStatus } from "../../data/cloud";
|
||||
import { copyToClipboard } from "../../common/util/copy-clipboard";
|
||||
import { showToast } from "../../util/toast";
|
||||
|
||||
export interface TtsMediaPickedEvent {
|
||||
item: MediaPlayerItem;
|
||||
@ -51,7 +54,8 @@ class BrowseMediaTTS extends LitElement {
|
||||
private _message?: string;
|
||||
|
||||
protected render() {
|
||||
return html`<ha-card>
|
||||
return html`
|
||||
<ha-card>
|
||||
<div class="card-content">
|
||||
<ha-textarea
|
||||
autogrow
|
||||
@ -94,7 +98,25 @@ class BrowseMediaTTS extends LitElement {
|
||||
)}
|
||||
</mwc-button>
|
||||
</div>
|
||||
</ha-card> `;
|
||||
</ha-card>
|
||||
${this._voice
|
||||
? html`
|
||||
<div class="footer">
|
||||
${this.hass.localize(
|
||||
`ui.components.media-browser.tts.selected_voice_id`
|
||||
)}
|
||||
<code>${this._voice || "-"}</code>
|
||||
<ha-icon-button
|
||||
.path=${mdiContentCopy}
|
||||
@click=${this._copyVoiceId}
|
||||
title=${this.hass.localize(
|
||||
"ui.components.media-browser.tts.copy_voice_id"
|
||||
)}
|
||||
></ha-icon-button>
|
||||
</div>
|
||||
`
|
||||
: nothing}
|
||||
`;
|
||||
}
|
||||
|
||||
protected override willUpdate(changedProps: PropertyValues): void {
|
||||
@ -197,6 +219,14 @@ class BrowseMediaTTS extends LitElement {
|
||||
fireEvent(this, "tts-picked", { item });
|
||||
}
|
||||
|
||||
private async _copyVoiceId(ev) {
|
||||
ev.preventDefault();
|
||||
await copyToClipboard(this._voice);
|
||||
showToast(this, {
|
||||
message: this.hass.localize("ui.common.copied_clipboard"),
|
||||
});
|
||||
}
|
||||
|
||||
static override styles = [
|
||||
buttonLinkStyle,
|
||||
css`
|
||||
@ -218,6 +248,23 @@ class BrowseMediaTTS extends LitElement {
|
||||
button.link {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
.footer {
|
||||
font-size: var(--ha-font-size-s);
|
||||
color: var(--secondary-text-color);
|
||||
margin: 16px 0;
|
||||
text-align: center;
|
||||
}
|
||||
.footer code {
|
||||
font-weight: var(--ha-font-weight-bold);
|
||||
}
|
||||
.footer {
|
||||
--mdc-icon-size: 14px;
|
||||
--mdc-icon-button-size: 24px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
import "@material/mwc-button";
|
||||
import { mdiClose, mdiHelpCircle } from "@mdi/js";
|
||||
import type { UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||
import type { CSSResultGroup, PropertyValues } from "lit";
|
||||
@ -177,6 +176,17 @@ class DataEntryFlowDialog extends LitElement {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
const showDocumentationLink =
|
||||
([
|
||||
"form",
|
||||
"menu",
|
||||
"external",
|
||||
"progress",
|
||||
"data_entry_flow_progressed",
|
||||
].includes(this._step?.type as any) &&
|
||||
this._params.manifest?.is_built_in) ||
|
||||
!!this._params.manifest?.documentation;
|
||||
|
||||
return html`
|
||||
<ha-dialog
|
||||
open
|
||||
@ -191,7 +201,7 @@ class DataEntryFlowDialog extends LitElement {
|
||||
<step-flow-loading
|
||||
.flowConfig=${this._params.flowConfig}
|
||||
.hass=${this.hass}
|
||||
.loadingReason=${this._loading}
|
||||
.loadingReason=${this._loading!}
|
||||
.handler=${this._handler}
|
||||
.step=${this._step}
|
||||
></step-flow-loading>
|
||||
@ -199,26 +209,18 @@ class DataEntryFlowDialog extends LitElement {
|
||||
: this._step === undefined
|
||||
? // When we are going to next step, we render 1 round of empty
|
||||
// to reset the element.
|
||||
""
|
||||
nothing
|
||||
: html`
|
||||
<div class="dialog-actions">
|
||||
${([
|
||||
"form",
|
||||
"menu",
|
||||
"external",
|
||||
"progress",
|
||||
"data_entry_flow_progressed",
|
||||
].includes(this._step?.type as any) &&
|
||||
this._params.manifest?.is_built_in) ||
|
||||
this._params.manifest?.documentation
|
||||
${showDocumentationLink
|
||||
? html`
|
||||
<a
|
||||
href=${this._params.manifest.is_built_in
|
||||
href=${this._params.manifest!.is_built_in
|
||||
? documentationUrl(
|
||||
this.hass,
|
||||
`/integrations/${this._params.manifest.domain}`
|
||||
`/integrations/${this._params.manifest!.domain}`
|
||||
)
|
||||
: this._params?.manifest?.documentation}
|
||||
: this._params.manifest!.documentation}
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
>
|
||||
@ -229,7 +231,7 @@ class DataEntryFlowDialog extends LitElement {
|
||||
</ha-icon-button
|
||||
></a>
|
||||
`
|
||||
: ""}
|
||||
: nothing}
|
||||
<ha-icon-button
|
||||
.label=${this.hass.localize("ui.common.close")}
|
||||
.path=${mdiClose}
|
||||
@ -242,6 +244,7 @@ class DataEntryFlowDialog extends LitElement {
|
||||
.flowConfig=${this._params.flowConfig}
|
||||
.step=${this._step}
|
||||
.hass=${this.hass}
|
||||
.increasePaddingEnd=${showDocumentationLink}
|
||||
></step-flow-form>
|
||||
`
|
||||
: this._step.type === "external"
|
||||
@ -250,6 +253,7 @@ class DataEntryFlowDialog extends LitElement {
|
||||
.flowConfig=${this._params.flowConfig}
|
||||
.step=${this._step}
|
||||
.hass=${this.hass}
|
||||
.increasePaddingEnd=${showDocumentationLink}
|
||||
></step-flow-external>
|
||||
`
|
||||
: this._step.type === "abort"
|
||||
@ -261,6 +265,7 @@ class DataEntryFlowDialog extends LitElement {
|
||||
.handler=${this._step.handler}
|
||||
.domain=${this._params.domain ??
|
||||
this._step.handler}
|
||||
.increasePaddingEnd=${showDocumentationLink}
|
||||
></step-flow-abort>
|
||||
`
|
||||
: this._step.type === "progress"
|
||||
@ -270,6 +275,7 @@ class DataEntryFlowDialog extends LitElement {
|
||||
.step=${this._step}
|
||||
.hass=${this.hass}
|
||||
.progress=${this._progress}
|
||||
.increasePaddingEnd=${showDocumentationLink}
|
||||
></step-flow-progress>
|
||||
`
|
||||
: this._step.type === "menu"
|
||||
@ -278,6 +284,7 @@ class DataEntryFlowDialog extends LitElement {
|
||||
.flowConfig=${this._params.flowConfig}
|
||||
.step=${this._step}
|
||||
.hass=${this.hass}
|
||||
.increasePaddingEnd=${showDocumentationLink}
|
||||
></step-flow-menu>
|
||||
`
|
||||
: html`
|
||||
@ -286,7 +293,8 @@ class DataEntryFlowDialog extends LitElement {
|
||||
.step=${this._step}
|
||||
.hass=${this.hass}
|
||||
.navigateToResult=${this._params
|
||||
.navigateToResult}
|
||||
.navigateToResult ?? false}
|
||||
.increasePaddingEnd=${showDocumentationLink}
|
||||
></step-flow-create-entry>
|
||||
`}
|
||||
`}
|
||||
|
@ -22,6 +22,9 @@ class StepFlowAbort extends LitElement {
|
||||
|
||||
@property({ attribute: false }) public handler!: string;
|
||||
|
||||
@property({ type: Boolean, attribute: "increase-padding-end" })
|
||||
public increasePaddingEnd = false;
|
||||
|
||||
protected firstUpdated(changed: PropertyValues) {
|
||||
super.firstUpdated(changed);
|
||||
if (this.step.reason === "missing_credentials") {
|
||||
@ -34,7 +37,7 @@ class StepFlowAbort extends LitElement {
|
||||
return nothing;
|
||||
}
|
||||
return html`
|
||||
<h2>
|
||||
<h2 class=${this.increasePaddingEnd ? "end-space" : ""}>
|
||||
${this.params.flowConfig.renderAbortHeader
|
||||
? this.params.flowConfig.renderAbortHeader(this.hass, this.step)
|
||||
: this.hass.localize(`component.${this.domain}.title`)}
|
||||
|
@ -36,6 +36,9 @@ class StepFlowCreateEntry extends LitElement {
|
||||
|
||||
@property({ attribute: false }) public step!: DataEntryFlowStepCreateEntry;
|
||||
|
||||
@property({ type: Boolean, attribute: "increase-padding-end" })
|
||||
public increasePaddingEnd = false;
|
||||
|
||||
public navigateToResult = false;
|
||||
|
||||
@state() private _deviceUpdate: Record<
|
||||
@ -113,7 +116,7 @@ class StepFlowCreateEntry extends LitElement {
|
||||
this.step.result?.entry_id
|
||||
);
|
||||
return html`
|
||||
<h2>
|
||||
<h2 class=${this.increasePaddingEnd ? "end-space" : ""}>
|
||||
${devices.length
|
||||
? localize("ui.panel.config.integrations.config_flow.assign_area", {
|
||||
number: devices.length,
|
||||
|
@ -15,11 +15,16 @@ class StepFlowExternal extends LitElement {
|
||||
|
||||
@property({ attribute: false }) public step!: DataEntryFlowStepExternal;
|
||||
|
||||
@property({ type: Boolean, attribute: "increase-padding-end" })
|
||||
public increasePaddingEnd = false;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
const localize = this.hass.localize;
|
||||
|
||||
return html`
|
||||
<h2>${this.flowConfig.renderExternalStepHeader(this.hass, this.step)}</h2>
|
||||
<h2 class=${this.increasePaddingEnd ? "end-space" : ""}>
|
||||
${this.flowConfig.renderExternalStepHeader(this.hass, this.step)}
|
||||
</h2>
|
||||
<div class="content">
|
||||
${this.flowConfig.renderExternalStepDescription(this.hass, this.step)}
|
||||
<div class="open-button">
|
||||
@ -51,6 +56,9 @@ class StepFlowExternal extends LitElement {
|
||||
.open-button a {
|
||||
text-decoration: none;
|
||||
}
|
||||
h2.end-space {
|
||||
padding-inline-end: 72px;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
@ -27,6 +27,9 @@ class StepFlowForm extends LitElement {
|
||||
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ type: Boolean, attribute: "increase-padding-end" })
|
||||
public increasePaddingEnd = false;
|
||||
|
||||
@state() private _loading = false;
|
||||
|
||||
@state() private _stepData?: Record<string, any>;
|
||||
@ -43,7 +46,9 @@ class StepFlowForm extends LitElement {
|
||||
const stepData = this._stepDataProcessed;
|
||||
|
||||
return html`
|
||||
<h2>${this.flowConfig.renderShowFormStepHeader(this.hass, this.step)}</h2>
|
||||
<h2 class=${this.increasePaddingEnd ? "end-space" : ""}>
|
||||
${this.flowConfig.renderShowFormStepHeader(this.hass, this.step)}
|
||||
</h2>
|
||||
<div class="content" @click=${this._clickHandler}>
|
||||
${this.flowConfig.renderShowFormStepDescription(this.hass, this.step)}
|
||||
${this._errorMsg
|
||||
|
@ -17,6 +17,9 @@ class StepFlowMenu extends LitElement {
|
||||
|
||||
@property({ attribute: false }) public step!: DataEntryFlowStepMenu;
|
||||
|
||||
@property({ type: Boolean, attribute: "increase-padding-end" })
|
||||
public increasePaddingEnd = false;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
let options: string[];
|
||||
let translations: Record<string, string>;
|
||||
@ -42,7 +45,9 @@ class StepFlowMenu extends LitElement {
|
||||
);
|
||||
|
||||
return html`
|
||||
<h2>${this.flowConfig.renderMenuHeader(this.hass, this.step)}</h2>
|
||||
<h2 class=${this.increasePaddingEnd ? "end-space" : ""}>
|
||||
${this.flowConfig.renderMenuHeader(this.hass, this.step)}
|
||||
</h2>
|
||||
${description ? html`<div class="content">${description}</div>` : ""}
|
||||
<div class="options">
|
||||
${options.map(
|
||||
|
@ -24,9 +24,12 @@ class StepFlowProgress extends LitElement {
|
||||
@property({ type: Number })
|
||||
public progress?: number;
|
||||
|
||||
@property({ type: Boolean, attribute: "increase-padding-end" })
|
||||
public increasePaddingEnd = false;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`
|
||||
<h2>
|
||||
<h2 class=${this.increasePaddingEnd ? "end-space" : ""}>
|
||||
${this.flowConfig.renderShowFormProgressHeader(this.hass, this.step)}
|
||||
</h2>
|
||||
<div class="content">
|
||||
|
@ -22,6 +22,9 @@ export const configFlowContentStyles = css`
|
||||
text-transform: var(--mdc-typography-headline6-text-transform, inherit);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
h2.end-space {
|
||||
padding-inline-end: 72px;
|
||||
}
|
||||
|
||||
.content,
|
||||
.preview {
|
||||
|
@ -57,7 +57,7 @@ class MoreInfoCover extends LitElement {
|
||||
);
|
||||
|
||||
if (positionStateDisplay) {
|
||||
return `${stateDisplay} ⸱ ${positionStateDisplay}`;
|
||||
return `${stateDisplay} · ${positionStateDisplay}`;
|
||||
}
|
||||
return stateDisplay;
|
||||
}
|
||||
|
@ -57,7 +57,7 @@ class MoreInfoValve extends LitElement {
|
||||
);
|
||||
|
||||
if (positionStateDisplay) {
|
||||
return `${stateDisplay} ⸱ ${positionStateDisplay}`;
|
||||
return `${stateDisplay} · ${positionStateDisplay}`;
|
||||
}
|
||||
return stateDisplay;
|
||||
}
|
||||
|
@ -95,22 +95,6 @@ type BaseNavigationCommand = Pick<
|
||||
"primaryText" | "path"
|
||||
>;
|
||||
|
||||
const DOMAIN_STYLE = styleMap({
|
||||
fontSize: "var(--ha-font-size-s)",
|
||||
fontWeight: "var(--ha-font-weight-normal)",
|
||||
lineHeight: "var(--ha-line-height-normal)",
|
||||
alignSelf: "flex-end",
|
||||
maxWidth: "30%",
|
||||
textOverflow: "ellipsis",
|
||||
overflow: "hidden",
|
||||
whiteSpace: "nowrap",
|
||||
});
|
||||
|
||||
const ENTITY_ID_STYLE = styleMap({
|
||||
fontFamily: "var(--ha-font-family-code)",
|
||||
fontSize: "var(--ha-font-size-xs)",
|
||||
});
|
||||
|
||||
@customElement("ha-quick-bar")
|
||||
export class QuickBar extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
@ -397,12 +381,12 @@ export class QuickBar extends LitElement {
|
||||
? html` <span slot="supporting-text">${item.altText}</span> `
|
||||
: nothing}
|
||||
${item.entityId && showEntityId
|
||||
? html`<span slot="supporting-text" style=${ENTITY_ID_STYLE}
|
||||
>${item.entityId}</span
|
||||
>`
|
||||
? html`
|
||||
<span slot="supporting-text" class="code">${item.entityId}</span>
|
||||
`
|
||||
: nothing}
|
||||
${item.translatedDomain && !showEntityId
|
||||
? html`<div slot="trailing-supporting-text" style=${DOMAIN_STYLE}>
|
||||
? html`<div slot="trailing-supporting-text">
|
||||
${item.translatedDomain}
|
||||
</div>`
|
||||
: nothing}
|
||||
@ -1038,6 +1022,22 @@ export class QuickBar extends LitElement {
|
||||
--md-list-item-bottom-space: 8px;
|
||||
}
|
||||
|
||||
ha-md-list-item .code {
|
||||
font-family: var(--ha-font-family-code);
|
||||
font-size: var(--ha-font-size-xs);
|
||||
}
|
||||
|
||||
ha-md-list-item [slot="trailing-supporting-text"] {
|
||||
font-size: var(--ha-font-size-s);
|
||||
font-weight: var(--ha-font-weight-normal);
|
||||
line-height: var(--ha-line-height-normal);
|
||||
align-self: flex-end;
|
||||
max-width: 30%;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
ha-tip {
|
||||
padding: 20px;
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { mdiCog, mdiDelete, mdiHarddisk, mdiNas } from "@mdi/js";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { css, html, LitElement, nothing, type TemplateResult } from "lit";
|
||||
import { join } from "lit/directives/join";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { fireEvent } from "../../../../../common/dom/fire_event";
|
||||
@ -57,26 +58,51 @@ class HaBackupConfigAgents extends LitElement {
|
||||
);
|
||||
}
|
||||
|
||||
const texts: (TemplateResult | string)[] = [];
|
||||
|
||||
if (isNetworkMountAgent(agentId)) {
|
||||
texts.push(
|
||||
this.hass.localize(
|
||||
"ui.panel.config.backup.agents.network_mount_agent_description"
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
const encryptionTurnedOff =
|
||||
this.agentsConfig?.[agentId]?.protected === false;
|
||||
|
||||
if (encryptionTurnedOff) {
|
||||
return html`
|
||||
texts.push(
|
||||
html`<div class="unencrypted-warning">
|
||||
<span class="dot warning"></span>
|
||||
<span>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.backup.agents.encryption_turned_off"
|
||||
)}
|
||||
</span>
|
||||
`;
|
||||
}
|
||||
|
||||
if (isNetworkMountAgent(agentId)) {
|
||||
return this.hass.localize(
|
||||
"ui.panel.config.backup.agents.network_mount_agent_description"
|
||||
</div>`
|
||||
);
|
||||
}
|
||||
return "";
|
||||
|
||||
const retention = this.agentsConfig?.[agentId]?.retention;
|
||||
|
||||
if (retention) {
|
||||
if (retention.copies === null && retention.days === null) {
|
||||
texts.push(
|
||||
this.hass.localize("ui.panel.config.backup.agents.retention_all")
|
||||
);
|
||||
} else {
|
||||
texts.push(
|
||||
this.hass.localize(
|
||||
`ui.panel.config.backup.agents.retention_${retention.copies ? "backups" : "days"}`,
|
||||
{
|
||||
count: retention.copies || retention.days,
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
return join(texts, html`<span class="separator"> · </span>`);
|
||||
}
|
||||
|
||||
private _availableAgents = memoizeOne(
|
||||
@ -287,6 +313,11 @@ class HaBackupConfigAgents extends LitElement {
|
||||
gap: 8px;
|
||||
line-height: normal;
|
||||
}
|
||||
.unencrypted-warning {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
.dot {
|
||||
display: block;
|
||||
position: relative;
|
||||
@ -294,11 +325,22 @@ class HaBackupConfigAgents extends LitElement {
|
||||
height: 8px;
|
||||
background-color: var(--disabled-color);
|
||||
border-radius: 50%;
|
||||
flex: none;
|
||||
}
|
||||
.dot.warning {
|
||||
background-color: var(--warning-color);
|
||||
}
|
||||
@media all and (max-width: 500px) {
|
||||
.separator {
|
||||
display: none;
|
||||
}
|
||||
ha-md-list-item [slot="supporting-text"] {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
gap: 4px;
|
||||
}
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
|
@ -156,7 +156,7 @@ class HaConfigInfo extends LitElement {
|
||||
)}
|
||||
</span>
|
||||
<span class="version">
|
||||
${JS_VERSION}${JS_TYPE !== "modern" ? ` ⸱ ${JS_TYPE}` : ""}
|
||||
${JS_VERSION}${JS_TYPE !== "modern" ? ` · ${JS_TYPE}` : ""}
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
|
@ -70,7 +70,7 @@ class DownloadLogsDialog extends LitElement {
|
||||
<span slot="subtitle">
|
||||
${this._dialogParams.header}${this._dialogParams.boot === 0
|
||||
? ""
|
||||
: ` ⸱ ${this._dialogParams.boot === -1 ? this.hass.localize("ui.panel.config.logs.previous") : this.hass.localize("ui.panel.config.logs.startups_ago", { boot: this._dialogParams.boot * -1 })}`}
|
||||
: ` · ${this._dialogParams.boot === -1 ? this.hass.localize("ui.panel.config.logs.previous") : this.hass.localize("ui.panel.config.logs.startups_ago", { boot: this._dialogParams.boot * -1 })}`}
|
||||
</span>
|
||||
</ha-dialog-header>
|
||||
<div slot="content" class="content">
|
||||
|
@ -20,7 +20,7 @@ class DialogRepairsIssueSubtitle extends LitElement {
|
||||
protected render() {
|
||||
const domainName = domainToName(this.hass.localize, this.issue.domain);
|
||||
const reportedBy = domainName
|
||||
? ` ⸱ ${this.hass.localize("ui.panel.config.repairs.reported_by", {
|
||||
? ` · ${this.hass.localize("ui.panel.config.repairs.reported_by", {
|
||||
integration: domainName,
|
||||
})}`
|
||||
: "";
|
||||
|
@ -100,13 +100,13 @@ class HaConfigRepairs extends LitElement {
|
||||
${(issue.severity === "critical" ||
|
||||
issue.severity === "error") &&
|
||||
issue.created
|
||||
? " ⸱ "
|
||||
? " · "
|
||||
: ""}
|
||||
${createdBy
|
||||
? html`<span .title=${createdBy}>${createdBy}</span>`
|
||||
: nothing}
|
||||
${issue.ignored
|
||||
? ` ⸱ ${this.hass.localize(
|
||||
? ` · ${this.hass.localize(
|
||||
"ui.panel.config.repairs.dialog.ignored_in_version_short",
|
||||
{ version: issue.dismissed_version }
|
||||
)}`
|
||||
|
@ -1096,6 +1096,8 @@ class HUIRoot extends LitElement {
|
||||
.edit-mode sl-tab-group {
|
||||
flex-grow: 0;
|
||||
color: var(--app-header-edit-text-color, #fff);
|
||||
--ha-tab-active-text-color: var(--app-header-edit-text-color, #fff);
|
||||
--ha-tab-indicator-color: var(--app-header-edit-text-color, #fff);
|
||||
}
|
||||
.edit-mode sl-tab {
|
||||
height: 54px;
|
||||
|
@ -50,6 +50,7 @@ export class AreasOverviewViewStrategy extends ReactiveElement {
|
||||
|
||||
const entities = [
|
||||
...groups.lights,
|
||||
...groups.covers,
|
||||
...groups.climate,
|
||||
...groups.media_players,
|
||||
...groups.security,
|
||||
|
@ -366,7 +366,7 @@ export class HaStateControlClimateTemperature extends LitElement {
|
||||
>
|
||||
${this._renderTarget(this._targetTemperature.low!, "normal", true)}
|
||||
</button>
|
||||
<span>⸱</span>
|
||||
<span>·</span>
|
||||
<button
|
||||
@click=${this._handleSelectTemp}
|
||||
.target=${"high"}
|
||||
|
@ -183,7 +183,7 @@ class StateDisplay extends LitElement {
|
||||
return html`${this.hass!.formatEntityState(stateObj)}`;
|
||||
}
|
||||
|
||||
return join(values, " ⸱ ");
|
||||
return join(values, " · ");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -920,7 +920,9 @@
|
||||
"action_play": "Say",
|
||||
"action_pick": "Select",
|
||||
"set_as_default": "Set as default options",
|
||||
"faild_to_store_defaults": "Failed to store defaults: {error}"
|
||||
"faild_to_store_defaults": "Failed to store defaults: {error}",
|
||||
"selected_voice_id": "Selected voice ID",
|
||||
"copy_voice_id": "Copy voice ID"
|
||||
},
|
||||
"pick": "Pick",
|
||||
"play": "Play",
|
||||
@ -2489,7 +2491,10 @@
|
||||
"unavailable_agents": "Unavailable locations",
|
||||
"no_agents": "No locations configured",
|
||||
"encryption_turned_off": "Encryption turned off",
|
||||
"local_agent": "This system"
|
||||
"local_agent": "This system",
|
||||
"retention_all": "Keep all backups",
|
||||
"retention_backups": "Keep {count} {count, plural,\n one {backup}\n other {backups}\n}",
|
||||
"retention_days": "Keep {count} {count, plural,\n one {day}\n other {days}\n}"
|
||||
},
|
||||
"data": {
|
||||
"ha_settings": "Home Assistant settings",
|
||||
|
Loading…
x
Reference in New Issue
Block a user