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