20230102.0 (#14963)

This commit is contained in:
Bram Kragten 2023-01-02 21:42:03 +01:00 committed by GitHub
commit ebb19e4ed5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 314 additions and 123 deletions

View File

@ -71,7 +71,6 @@ class HaDemo extends HomeAssistantAppEl {
entity_category: null, entity_category: null,
has_entity_name: false, has_entity_name: false,
unique_id: "co2_intensity", unique_id: "co2_intensity",
aliases: [],
}, },
{ {
config_entry_id: "co2signal", config_entry_id: "co2signal",
@ -87,7 +86,6 @@ class HaDemo extends HomeAssistantAppEl {
entity_category: null, entity_category: null,
has_entity_name: false, has_entity_name: false,
unique_id: "grid_fossil_fuel_percentage", unique_id: "grid_fossil_fuel_percentage",
aliases: [],
}, },
]); ]);

View File

@ -197,7 +197,6 @@ const createEntityRegistryEntries = (
platform: "updater", platform: "updater",
has_entity_name: false, has_entity_name: false,
unique_id: "updater", unique_id: "updater",
aliases: [],
}, },
]; ];

View File

@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project] [project]
name = "home-assistant-frontend" name = "home-assistant-frontend"
version = "20221230.0" version = "20230102.0"
license = {text = "Apache-2.0"} license = {text = "Apache-2.0"}
description = "The Home Assistant frontend" description = "The Home Assistant frontend"
readme = "README.md" readme = "README.md"

View File

@ -28,7 +28,7 @@ class StateInfo extends LitElement {
const name = computeStateName(this.stateObj); const name = computeStateName(this.stateObj);
return html` <state-badge return html`<state-badge
.stateObj=${this.stateObj} .stateObj=${this.stateObj}
.stateColor=${true} .stateColor=${true}
.color=${this.color} .color=${this.color}

View File

@ -3,10 +3,12 @@ import { styles } from "@material/mwc-dialog/mwc-dialog.css";
import { mdiClose } from "@mdi/js"; import { mdiClose } from "@mdi/js";
import { css, html, TemplateResult } from "lit"; import { css, html, TemplateResult } from "lit";
import { customElement } from "lit/decorators"; import { customElement } from "lit/decorators";
import type { HomeAssistant } from "../types";
import { FOCUS_TARGET } from "../dialogs/make-dialog-manager"; import { FOCUS_TARGET } from "../dialogs/make-dialog-manager";
import type { HomeAssistant } from "../types";
import "./ha-icon-button"; import "./ha-icon-button";
const SUPPRESS_DEFAULT_PRESS_SELECTOR = ["button"];
export const createCloseHeading = ( export const createCloseHeading = (
hass: HomeAssistant, hass: HomeAssistant,
title: string | TemplateResult title: string | TemplateResult
@ -32,6 +34,14 @@ export class HaDialog extends DialogBase {
return html`<slot name="heading"> ${super.renderHeading()} </slot>`; return html`<slot name="heading"> ${super.renderHeading()} </slot>`;
} }
protected firstUpdated(): void {
super.firstUpdated();
this.suppressDefaultPressSelector = [
this.suppressDefaultPressSelector,
SUPPRESS_DEFAULT_PRESS_SELECTOR,
].join(", ");
}
static override styles = [ static override styles = [
styles, styles,
css` css`

View File

@ -67,6 +67,9 @@ export class HaFormInteger extends LitElement implements HaFormElement {
@change=${this._valueChanged} @change=${this._valueChanged}
></ha-slider> ></ha-slider>
</div> </div>
${this.helper
? html`<ha-input-helper-text>${this.helper}</ha-input-helper-text>`
: ""}
</div> </div>
`; `;
} }

View File

@ -22,7 +22,6 @@ export interface EntityRegistryEntry {
original_name?: string; original_name?: string;
unique_id: string; unique_id: string;
translation_key?: string; translation_key?: string;
aliases: string[];
} }
export interface ExtEntityRegistryEntry extends EntityRegistryEntry { export interface ExtEntityRegistryEntry extends EntityRegistryEntry {
@ -30,6 +29,7 @@ export interface ExtEntityRegistryEntry extends EntityRegistryEntry {
original_icon?: string; original_icon?: string;
device_class?: string; device_class?: string;
original_device_class?: string; original_device_class?: string;
aliases: string[];
} }
export interface UpdateEntityRegistryEntryResult { export interface UpdateEntityRegistryEntryResult {

View File

@ -6,6 +6,7 @@ import type { Options, WeekdayStr } from "rrule";
import { ByWeekday, RRule, Weekday } from "rrule"; import { ByWeekday, RRule, Weekday } from "rrule";
import { firstWeekdayIndex } from "../../common/datetime/first_weekday"; import { firstWeekdayIndex } from "../../common/datetime/first_weekday";
import { stopPropagation } from "../../common/dom/stop_propagation"; import { stopPropagation } from "../../common/dom/stop_propagation";
import { LocalizeKeys } from "../../common/translations/localize";
import "../../components/ha-chip"; import "../../components/ha-chip";
import "../../components/ha-list-item"; import "../../components/ha-list-item";
import "../../components/ha-select"; import "../../components/ha-select";
@ -19,12 +20,10 @@ import {
getWeekday, getWeekday,
getWeekdays, getWeekdays,
getMonthlyRepeatItems, getMonthlyRepeatItems,
intervalSuffix,
RepeatEnd, RepeatEnd,
RepeatFrequency, RepeatFrequency,
ruleByWeekDay, ruleByWeekDay,
untilValue, untilValue,
WEEKDAY_NAME,
MonthlyRepeatItem, MonthlyRepeatItem,
getMonthlyRepeatWeekdayFromRule, getMonthlyRepeatWeekdayFromRule,
getMonthdayRepeatFromRule, getMonthdayRepeatFromRule,
@ -174,18 +173,36 @@ export class RecurrenceRuleEditor extends LitElement {
return html` return html`
<ha-select <ha-select
id="freq" id="freq"
label="Repeat" label=${this.hass.localize("ui.components.calendar.event.repeat.label")}
@selected=${this._onRepeatSelected} @selected=${this._onRepeatSelected}
@closed=${stopPropagation} @closed=${stopPropagation}
fixedMenuPosition fixedMenuPosition
naturalMenuWidth naturalMenuWidth
.value=${this._freq} .value=${this._freq}
> >
<ha-list-item value="none">None</ha-list-item> <ha-list-item value="none">
<ha-list-item value="yearly">Yearly</ha-list-item> ${this.hass.localize("ui.components.calendar.event.repeat.freq.none")}
<ha-list-item value="monthly">Monthly</ha-list-item> </ha-list-item>
<ha-list-item value="weekly">Weekly</ha-list-item> <ha-list-item value="yearly">
<ha-list-item value="daily">Daily</ha-list-item> ${this.hass.localize(
"ui.components.calendar.event.repeat.freq.yearly"
)}
</ha-list-item>
<ha-list-item value="monthly">
${this.hass.localize(
"ui.components.calendar.event.repeat.freq.monthly"
)}
</ha-list-item>
<ha-list-item value="weekly">
${this.hass.localize(
"ui.components.calendar.event.repeat.freq.weekly"
)}
</ha-list-item>
<ha-list-item value="daily">
${this.hass.localize(
"ui.components.calendar.event.repeat.freq.daily"
)}
</ha-list-item>
</ha-select> </ha-select>
`; `;
} }
@ -196,7 +213,9 @@ export class RecurrenceRuleEditor extends LitElement {
${this._monthlyRepeatItems.length > 0 ${this._monthlyRepeatItems.length > 0
? html`<ha-select ? html`<ha-select
id="monthly" id="monthly"
label="Repeat Monthly" label=${this.hass.localize(
"ui.components.calendar.event.repeat.monthly.label"
)}
@selected=${this._onMonthlyDetailSelected} @selected=${this._onMonthlyDetailSelected}
.value=${this._monthlyRepeat || this._monthlyRepeatItems[0]?.value} .value=${this._monthlyRepeat || this._monthlyRepeatItems[0]?.value}
@closed=${stopPropagation} @closed=${stopPropagation}
@ -225,7 +244,11 @@ export class RecurrenceRuleEditor extends LitElement {
.value=${item} .value=${item}
class=${classMap({ active: this._weekday.has(item) })} class=${classMap({ active: this._weekday.has(item) })}
@click=${this._onWeekdayToggle} @click=${this._onWeekdayToggle}
>${WEEKDAY_NAME[item]}</ha-chip >${this.hass.localize(
`ui.components.calendar.event.repeat.weekly.weekday.${
item.toLowerCase() as Lowercase<WeekdayStr>
}`
)}</ha-chip
> >
` `
)} )}
@ -241,11 +264,16 @@ export class RecurrenceRuleEditor extends LitElement {
return html` return html`
<ha-textfield <ha-textfield
id="interval" id="interval"
label="Repeat interval" label=${this.hass.localize(
"ui.components.calendar.event.repeat.interval.label"
)}
type="number" type="number"
min="1" min="1"
.value=${this._interval} .value=${this._interval}
.suffix=${intervalSuffix(this._freq!)} .suffix=${this.hass.localize(
`ui.components.calendar.event.repeat.interval.${this
._freq!}` as LocalizeKeys
)}
@change=${this._onIntervalChange} @change=${this._onIntervalChange}
></ha-textfield> ></ha-textfield>
`; `;
@ -255,26 +283,38 @@ export class RecurrenceRuleEditor extends LitElement {
return html` return html`
<ha-select <ha-select
id="end" id="end"
label="Ends" label=${this.hass.localize(
"ui.components.calendar.event.repeat.end.label"
)}
.value=${this._end} .value=${this._end}
@selected=${this._onEndSelected} @selected=${this._onEndSelected}
@closed=${stopPropagation} @closed=${stopPropagation}
fixedMenuPosition fixedMenuPosition
naturalMenuWidth naturalMenuWidth
> >
<ha-list-item value="never">Never</ha-list-item> <ha-list-item value="never">
<ha-list-item value="after">After</ha-list-item> ${this.hass.localize("ui.components.calendar.event.repeat.end.never")}
<ha-list-item value="on">On</ha-list-item> </ha-list-item>
<ha-list-item value="after">
${this.hass.localize("ui.components.calendar.event.repeat.end.after")}
</ha-list-item>
<ha-list-item value="on">
${this.hass.localize("ui.components.calendar.event.repeat.end.on")}
</ha-list-item>
</ha-select> </ha-select>
${this._end === "after" ${this._end === "after"
? html` ? html`
<ha-textfield <ha-textfield
id="after" id="after"
label="End after" label=${this.hass.localize(
"ui.components.calendar.event.repeat.end_after.label"
)}
type="number" type="number"
min="1" min="1"
.value=${this._count!} .value=${this._count!}
suffix="ocurrences" suffix=${this.hass.localize(
"ui.components.calendar.event.repeat.end_after.ocurrences"
)}
@change=${this._onCountChange} @change=${this._onCountChange}
></ha-textfield> ></ha-textfield>
` `
@ -283,7 +323,9 @@ export class RecurrenceRuleEditor extends LitElement {
? html` ? html`
<ha-date-input <ha-date-input
id="on" id="on"
label="End on" label=${this.hass.localize(
"ui.components.calendar.event.repeat.end_on.label"
)}
.locale=${this.locale} .locale=${this.locale}
.value=${this._until!.toISOString()} .value=${this._until!.toISOString()}
@value-changed=${this._onUntilChange} @value-changed=${this._onUntilChange}

View File

@ -42,16 +42,6 @@ export interface MonthlyRepeatItem {
label: string; label: string;
} }
export function intervalSuffix(freq: RepeatFrequency) {
if (freq === "monthly") {
return "months";
}
if (freq === "weekly") {
return "weeks";
}
return "days";
}
export function untilValue(freq: RepeatFrequency): Date { export function untilValue(freq: RepeatFrequency): Date {
const today = new Date(); const today = new Date();
const increment = DEFAULT_COUNT[freq]; const increment = DEFAULT_COUNT[freq];
@ -102,16 +92,6 @@ export const convertRepeatFrequency = (
} }
}; };
export const WEEKDAY_NAME = {
SU: "Sun",
MO: "Mon",
TU: "Tue",
WE: "Wed",
TH: "Thu",
FR: "Fri",
SA: "Sat",
};
export const WEEKDAYS = [ export const WEEKDAYS = [
RRule.SU, RRule.SU,
RRule.MO, RRule.MO,

View File

@ -541,15 +541,10 @@ export class HaAutomationTrace extends LitElement {
justify-content: center; justify-content: center;
display: flex; display: flex;
} }
.info { .info {
flex: 1; flex: 1;
background-color: var(--card-background-color); background-color: var(--card-background-color);
} }
.linkButton {
color: var(--primary-text-color);
}
.trace-link { .trace-link {
text-decoration: none; text-decoration: none;
} }

View File

@ -46,7 +46,7 @@ export class HaCalendarTrigger extends LitElement implements TriggerElement {
], ],
], ],
}, },
{ name: "offset", selector: { duration: { enable_day: true } } }, { name: "offset", selector: { duration: {} } },
{ {
name: "offset_type", name: "offset_type",
type: "select", type: "select",

View File

@ -9,7 +9,6 @@ import {
mdiFormatListChecks, mdiFormatListChecks,
mdiSync, mdiSync,
} from "@mdi/js"; } from "@mdi/js";
import type { UnsubscribeFunc } from "home-assistant-js-websocket";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property, state } from "lit/decorators"; import { customElement, property, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map"; import { classMap } from "lit/directives/class-map";
@ -43,20 +42,20 @@ import {
} from "../../../../data/cloud"; } from "../../../../data/cloud";
import { import {
EntityRegistryEntry, EntityRegistryEntry,
subscribeEntityRegistry, getExtendedEntityRegistryEntry,
updateEntityRegistryEntry,
} from "../../../../data/entity_registry"; } from "../../../../data/entity_registry";
import { showDomainTogglerDialog } from "../../../../dialogs/domain-toggler/show-dialog-domain-toggler"; import { showDomainTogglerDialog } from "../../../../dialogs/domain-toggler/show-dialog-domain-toggler";
import "../../../../layouts/hass-loading-screen"; import "../../../../layouts/hass-loading-screen";
import "../../../../layouts/hass-subpage"; import "../../../../layouts/hass-subpage";
import { SubscribeMixin } from "../../../../mixins/subscribe-mixin";
import { haStyle } from "../../../../resources/styles"; import { haStyle } from "../../../../resources/styles";
import type { HomeAssistant } from "../../../../types"; import type { HomeAssistant } from "../../../../types";
import { showEntityAliasesDialog } from "../../entities/entity-aliases/show-dialog-entity-aliases";
const DEFAULT_CONFIG_EXPOSE = true; const DEFAULT_CONFIG_EXPOSE = true;
const IGNORE_INTERFACES = ["Alexa.EndpointHealth"];
@customElement("cloud-alexa") @customElement("cloud-alexa")
class CloudAlexa extends SubscribeMixin(LitElement) { class CloudAlexa extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant; @property({ attribute: false }) public hass!: HomeAssistant;
@property() @property()
@ -171,10 +170,17 @@ class CloudAlexa extends SubscribeMixin(LitElement) {
secondary-line secondary-line
@click=${this._showMoreInfo} @click=${this._showMoreInfo}
> >
${entity.interfaces ${entity.entity_id in this.hass.entities
.filter((ifc) => !IGNORE_INTERFACES.includes(ifc)) ? html`<button
.map((ifc) => ifc.replace(/(Alexa.|Controller)/g, "")) class="link"
.join(", ")} .entityId=${entity.entity_id}
@click=${this._openAliasesSettings}
>
${this.hass.localize(
"ui.panel.config.cloud.alexa.manage_aliases"
)}
</button>`
: ""}
</state-info> </state-info>
${!emptyFilter ${!emptyFilter
? html`${iconButton}` ? html`${iconButton}`
@ -323,23 +329,33 @@ class CloudAlexa extends SubscribeMixin(LitElement) {
if (changedProps.has("cloudStatus")) { if (changedProps.has("cloudStatus")) {
this._entityConfigs = this.cloudStatus.prefs.alexa_entity_configs; this._entityConfigs = this.cloudStatus.prefs.alexa_entity_configs;
} }
if (
changedProps.has("hass") &&
changedProps.get("hass")?.entities !== this.hass.entities
) {
const categories = {};
for (const entry of Object.values(this.hass.entities)) {
categories[entry.entity_id] = entry.entity_category;
}
this._entityCategories = categories;
}
} }
protected override hassSubscribe(): ( private async _openAliasesSettings(ev) {
| UnsubscribeFunc ev.stopPropagation();
| Promise<UnsubscribeFunc> const entityId = ev.target.entityId;
)[] { const entry = await getExtendedEntityRegistryEntry(this.hass, entityId);
return [ if (!entry) {
subscribeEntityRegistry(this.hass.connection, (entries) => { return;
const categories = {}; }
showEntityAliasesDialog(this, {
for (const entry of entries) { entity: entry,
categories[entry.entity_id] = entry.entity_category; updateEntry: async (updates) => {
} await updateEntityRegistryEntry(this.hass, entry.entity_id, updates);
},
this._entityCategories = categories; });
}),
];
} }
private async _fetchData() { private async _fetchData() {

View File

@ -9,7 +9,6 @@ import {
mdiFormatListChecks, mdiFormatListChecks,
mdiSync, mdiSync,
} from "@mdi/js"; } from "@mdi/js";
import type { UnsubscribeFunc } from "home-assistant-js-websocket";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property, state } from "lit/decorators"; import { customElement, property, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map"; import { classMap } from "lit/directives/class-map";
@ -41,7 +40,8 @@ import {
} from "../../../../data/cloud"; } from "../../../../data/cloud";
import { import {
EntityRegistryEntry, EntityRegistryEntry,
subscribeEntityRegistry, getExtendedEntityRegistryEntry,
updateEntityRegistryEntry,
} from "../../../../data/entity_registry"; } from "../../../../data/entity_registry";
import { import {
fetchCloudGoogleEntities, fetchCloudGoogleEntities,
@ -51,15 +51,15 @@ import { showDomainTogglerDialog } from "../../../../dialogs/domain-toggler/show
import { showAlertDialog } from "../../../../dialogs/generic/show-dialog-box"; import { showAlertDialog } from "../../../../dialogs/generic/show-dialog-box";
import "../../../../layouts/hass-loading-screen"; import "../../../../layouts/hass-loading-screen";
import "../../../../layouts/hass-subpage"; import "../../../../layouts/hass-subpage";
import { SubscribeMixin } from "../../../../mixins/subscribe-mixin"; import { buttonLinkStyle, haStyle } from "../../../../resources/styles";
import { haStyle } from "../../../../resources/styles";
import type { HomeAssistant } from "../../../../types"; import type { HomeAssistant } from "../../../../types";
import { showToast } from "../../../../util/toast"; import { showToast } from "../../../../util/toast";
import { showEntityAliasesDialog } from "../../entities/entity-aliases/show-dialog-entity-aliases";
const DEFAULT_CONFIG_EXPOSE = true; const DEFAULT_CONFIG_EXPOSE = true;
@customElement("cloud-google-assistant") @customElement("cloud-google-assistant")
class CloudGoogleAssistant extends SubscribeMixin(LitElement) { class CloudGoogleAssistant extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant; @property({ attribute: false }) public hass!: HomeAssistant;
@property() public cloudStatus!: CloudStatusLoggedIn; @property() public cloudStatus!: CloudStatusLoggedIn;
@ -174,15 +174,23 @@ class CloudGoogleAssistant extends SubscribeMixin(LitElement) {
secondary-line secondary-line
@click=${this._showMoreInfo} @click=${this._showMoreInfo}
> >
${entity.traits ${entity.entity_id in this.hass.entities
.map((trait) => trait.substr(trait.lastIndexOf(".") + 1)) ? html`<button
.join(", ")} class="link"
.entityId=${entity.entity_id}
@click=${this._openAliasesSettings}
>
${this.hass.localize(
"ui.panel.config.cloud.google.manage_aliases"
)}
</button>`
: ""}
</state-info> </state-info>
${!emptyFilter ${!emptyFilter
? html`${iconButton}` ? html`${iconButton}`
: html`<ha-button-menu : html`<ha-button-menu
corner="BOTTOM_START" corner="BOTTOM_START"
.entityId=${stateObj.entity_id} .entityId=${entity.entity_id}
@action=${this._exposeChanged} @action=${this._exposeChanged}
> >
${iconButton} ${iconButton}
@ -308,7 +316,7 @@ class CloudGoogleAssistant extends SubscribeMixin(LitElement) {
</h3> </h3>
${!this.narrow ${!this.narrow
? this.hass!.localize( ? this.hass!.localize(
"ui.panel.config.cloud.alexa.exposed", "ui.panel.config.cloud.google.exposed",
"selected", "selected",
selected selected
) )
@ -329,7 +337,7 @@ class CloudGoogleAssistant extends SubscribeMixin(LitElement) {
</h3> </h3>
${!this.narrow ${!this.narrow
? this.hass!.localize( ? this.hass!.localize(
"ui.panel.config.cloud.alexa.not_exposed", "ui.panel.config.cloud.google.not_exposed",
"selected", "selected",
this._entities.length - selected this._entities.length - selected
) )
@ -354,23 +362,33 @@ class CloudGoogleAssistant extends SubscribeMixin(LitElement) {
if (changedProps.has("cloudStatus")) { if (changedProps.has("cloudStatus")) {
this._entityConfigs = this.cloudStatus.prefs.google_entity_configs; this._entityConfigs = this.cloudStatus.prefs.google_entity_configs;
} }
if (
changedProps.has("hass") &&
changedProps.get("hass")?.entities !== this.hass.entities
) {
const categories = {};
for (const entry of Object.values(this.hass.entities)) {
categories[entry.entity_id] = entry.entity_category;
}
this._entityCategories = categories;
}
} }
protected override hassSubscribe(): ( private async _openAliasesSettings(ev) {
| UnsubscribeFunc ev.stopPropagation();
| Promise<UnsubscribeFunc> const entityId = ev.target.entityId;
)[] { const entry = await getExtendedEntityRegistryEntry(this.hass, entityId);
return [ if (!entry) {
subscribeEntityRegistry(this.hass.connection, (entries) => { return;
const categories = {}; }
showEntityAliasesDialog(this, {
for (const entry of entries) { entity: entry,
categories[entry.entity_id] = entry.entity_category; updateEntry: async (updates) => {
} await updateEntityRegistryEntry(this.hass, entry.entity_id, updates);
},
this._entityCategories = categories; });
}),
];
} }
private _configIsDomainExposed( private _configIsDomainExposed(
@ -583,6 +601,7 @@ class CloudGoogleAssistant extends SubscribeMixin(LitElement) {
static get styles(): CSSResultGroup { static get styles(): CSSResultGroup {
return [ return [
haStyle, haStyle,
buttonLinkStyle,
css` css`
mwc-list-item > [slot="meta"] { mwc-list-item > [slot="meta"] {
margin-left: 4px; margin-left: 4px;

View File

@ -57,7 +57,13 @@ export class EntityRegistrySettingsHelper extends LitElement {
super.updated(changedProperties); super.updated(changedProperties);
if (changedProperties.has("entry")) { if (changedProperties.has("entry")) {
this._error = undefined; this._error = undefined;
this._item = undefined; if (
this.entry.unique_id !==
(changedProperties.get("entry") as ExtEntityRegistryEntry)?.unique_id
) {
this._item = undefined;
}
this._getItem(); this._getItem();
} }
} }

View File

@ -72,16 +72,21 @@ class DialogEntityAliases extends LitElement {
dialogInitialFocus=${index} dialogInitialFocus=${index}
.index=${index} .index=${index}
class="flex-auto" class="flex-auto"
label="Alias" .label=${this.hass!.localize(
"ui.dialogs.entity_registry.editor.aliases.input_label",
{ number: index + 1 }
)}
.value=${alias} .value=${alias}
?data-last=${index === this._aliases.length - 1} ?data-last=${index === this._aliases.length - 1}
@change=${this._editAlias} @input=${this._editAlias}
@keydown=${this._keyDownAlias}
></ha-textfield> ></ha-textfield>
<ha-icon-button <ha-icon-button
.index=${index} .index=${index}
slot="navigationIcon" slot="navigationIcon"
label=${this.hass!.localize( label=${this.hass!.localize(
"ui.dialogs.entity_registry.editor.aliases.remove_alias" "ui.dialogs.entity_registry.editor.aliases.remove_alias",
{ number: index + 1 }
)} )}
@click=${this._removeAlias} @click=${this._removeAlias}
.path=${mdiDeleteOutline} .path=${mdiDeleteOutline}
@ -133,6 +138,13 @@ class DialogEntityAliases extends LitElement {
this._aliases[index] = (ev.target as any).value; this._aliases[index] = (ev.target as any).value;
} }
private async _keyDownAlias(ev: KeyboardEvent) {
if (ev.key === "Enter") {
ev.stopPropagation();
this._addAlias();
}
}
private async _removeAlias(ev: Event) { private async _removeAlias(ev: Event) {
const index = (ev.target as any).index; const index = (ev.target as any).index;
const aliases = [...this._aliases]; const aliases = [...this._aliases];

View File

@ -1,11 +1,11 @@
import { fireEvent } from "../../../../common/dom/fire_event"; import { fireEvent } from "../../../../common/dom/fire_event";
import { import {
EntityRegistryEntry,
EntityRegistryEntryUpdateParams, EntityRegistryEntryUpdateParams,
ExtEntityRegistryEntry,
} from "../../../../data/entity_registry"; } from "../../../../data/entity_registry";
export interface EntityAliasesDialogParams { export interface EntityAliasesDialogParams {
entity: EntityRegistryEntry; entity: ExtEntityRegistryEntry;
updateEntry: ( updateEntry: (
updates: Partial<EntityRegistryEntryUpdateParams> updates: Partial<EntityRegistryEntryUpdateParams>
) => Promise<unknown>; ) => Promise<unknown>;

View File

@ -1,7 +1,11 @@
import "@material/mwc-formfield/mwc-formfield"; import "@material/mwc-formfield/mwc-formfield";
import "@material/mwc-list/mwc-list";
import "@material/mwc-list/mwc-list-item";
import { mdiPencil } from "@mdi/js";
import { UnsubscribeFunc } from "home-assistant-js-websocket"; import { UnsubscribeFunc } from "home-assistant-js-websocket";
import { css, html, LitElement, PropertyValues, TemplateResult } from "lit"; import { css, html, LitElement, PropertyValues, TemplateResult } from "lit";
import { customElement, property, state } from "lit/decorators"; import { customElement, property, state } from "lit/decorators";
import { fireEvent } from "../../../common/dom/fire_event";
import { computeDomain } from "../../../common/entity/compute_domain"; import { computeDomain } from "../../../common/entity/compute_domain";
import "../../../components/ha-area-picker"; import "../../../components/ha-area-picker";
import "../../../components/ha-expansion-panel"; import "../../../components/ha-expansion-panel";
@ -21,6 +25,7 @@ import {
import { showAlertDialog } from "../../../dialogs/generic/show-dialog-box"; import { showAlertDialog } from "../../../dialogs/generic/show-dialog-box";
import { SubscribeMixin } from "../../../mixins/subscribe-mixin"; import { SubscribeMixin } from "../../../mixins/subscribe-mixin";
import type { HomeAssistant } from "../../../types"; import type { HomeAssistant } from "../../../types";
import { showEntityAliasesDialog } from "./entity-aliases/show-dialog-entity-aliases";
@customElement("ha-registry-basic-editor") @customElement("ha-registry-basic-editor")
export class HaEntityRegistryBasicEditor extends SubscribeMixin(LitElement) { export class HaEntityRegistryBasicEditor extends SubscribeMixin(LitElement) {
@ -44,6 +49,21 @@ export class HaEntityRegistryBasicEditor extends SubscribeMixin(LitElement) {
@state() private _submitting = false; @state() private _submitting = false;
private _handleAliasesClicked(ev: CustomEvent) {
if (ev.detail.index !== 0) return;
showEntityAliasesDialog(this, {
entity: this.entry!,
updateEntry: async (updates) => {
const result = await updateEntityRegistryEntry(
this.hass,
this.entry.entity_id,
updates
);
fireEvent(this, "entity-entry-updated", result.entity_entry);
},
});
}
public async updateEntry(): Promise<void> { public async updateEntry(): Promise<void> {
this._submitting = true; this._submitting = true;
const params: Partial<EntityRegistryEntryUpdateParams> = { const params: Partial<EntityRegistryEntryUpdateParams> = {
@ -247,6 +267,33 @@ export class HaEntityRegistryBasicEditor extends SubscribeMixin(LitElement) {
</div> </div>
` `
: ""} : ""}
<div class="label">
${this.hass.localize(
"ui.dialogs.entity_registry.editor.aliases_section"
)}
</div>
<mwc-list class="aliases" @action=${this._handleAliasesClicked}>
<mwc-list-item .twoline=${this.entry.aliases.length > 0} hasMeta>
<span>
${this.entry.aliases.length > 0
? this.hass.localize(
"ui.dialogs.entity_registry.editor.configured_aliases",
{ count: this.entry.aliases.length }
)
: this.hass.localize(
"ui.dialogs.entity_registry.editor.no_aliases"
)}
</span>
<span slot="secondary">${this.entry.aliases.join(", ")}</span>
<ha-svg-icon slot="meta" .path=${mdiPencil}></ha-svg-icon>
</mwc-list-item>
</mwc-list>
<div class="secondary">
${this.hass.localize(
"ui.dialogs.entity_registry.editor.aliases.description"
)}
</div>
</ha-expansion-panel> </ha-expansion-panel>
`; `;
} }
@ -300,6 +347,13 @@ export class HaEntityRegistryBasicEditor extends SubscribeMixin(LitElement) {
.label { .label {
margin-top: 16px; margin-top: 16px;
} }
.aliases {
border-radius: 4px;
margin-top: 4px;
margin-bottom: 4px;
--mdc-icon-button-size: 24px;
overflow: hidden;
}
`; `;
} }
} }

View File

@ -119,6 +119,42 @@ const OVERRIDE_NUMBER_UNITS = {
const OVERRIDE_SENSOR_UNITS = { const OVERRIDE_SENSOR_UNITS = {
current: ["A", "mA"], current: ["A", "mA"],
data_rate: [
"bit/s",
"kbit/s",
"Mbit/s",
"Gbit/s",
"B/s",
"kB/s",
"MB/s",
"GB/s",
"KiB/s",
"MiB/s",
"GiB/s",
],
data_size: [
"bit",
"kbit",
"Mbit",
"Gbit",
"B",
"kB",
"MB",
"GB",
"TB",
"PB",
"EB",
"ZB",
"YB",
"KiB",
"MiB",
"GiB",
"TiB",
"PiB",
"EiB",
"ZiB",
"YiB",
],
distance: ["cm", "ft", "in", "km", "m", "mi", "mm", "yd"], distance: ["cm", "ft", "in", "km", "m", "mi", "mm", "yd"],
gas: ["CCF", "ft³", "m³"], gas: ["CCF", "ft³", "m³"],
precipitation: ["cm", "in", "mm"], precipitation: ["cm", "in", "mm"],
@ -771,12 +807,8 @@ export class EntityRegistrySettings extends SubscribeMixin(LitElement) {
"ui.dialogs.entity_registry.editor.aliases_section" "ui.dialogs.entity_registry.editor.aliases_section"
)} )}
</div> </div>
<mwc-list class="aliases"> <mwc-list class="aliases" @action=${this._handleAliasesClicked}>
<mwc-list-item <mwc-list-item .twoline=${this.entry.aliases.length > 0} hasMeta>
.twoline=${this.entry.aliases.length > 0}
hasMeta
@click=${this._openAliasesSettings}
>
<span> <span>
${this.entry.aliases.length > 0 ${this.entry.aliases.length > 0
? this.hass.localize( ? this.hass.localize(
@ -979,7 +1011,8 @@ export class EntityRegistrySettings extends SubscribeMixin(LitElement) {
}); });
} }
private _openAliasesSettings() { private _handleAliasesClicked(ev: CustomEvent) {
if (ev.detail.index !== 0) return;
showEntityAliasesDialog(this, { showEntityAliasesDialog(this, {
entity: this.entry!, entity: this.entry!,
updateEntry: async (updates) => { updateEntry: async (updates) => {

View File

@ -728,7 +728,6 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
selectable: false, selectable: false,
entity_category: null, entity_category: null,
has_entity_name: false, has_entity_name: false,
aliases: [],
}); });
} }
if (changed) { if (changed) {

View File

@ -527,15 +527,10 @@ export class HaScriptTrace extends LitElement {
:host([narrow]) .graph { :host([narrow]) .graph {
max-width: 100%; max-width: 100%;
} }
.info { .info {
flex: 1; flex: 1;
background-color: var(--card-background-color); background-color: var(--card-background-color);
} }
.linkButton {
color: var(--primary-text-color);
}
.trace-link { .trace-link {
text-decoration: none; text-decoration: none;
} }

View File

@ -644,6 +644,33 @@
"monthly": "months", "monthly": "months",
"weekly": "weeks", "weekly": "weeks",
"daily": "days" "daily": "days"
},
"monthly": {
"label": "Repeat Monthly"
},
"weekly": {
"weekday": {
"su": "Sun",
"mo": "Mon",
"tu": "Tue",
"we": "Wed",
"th": "Thu",
"fr": "Fri",
"sa": "Sat"
}
},
"end": {
"label": "End",
"never": "Never",
"after": "After",
"on": "On"
},
"end_on": {
"label": "End On"
},
"end_after": {
"label": "End After",
"ocurrences": "ocurrences"
} }
}, },
"rrule": { "rrule": {
@ -976,7 +1003,8 @@
"aliases": { "aliases": {
"heading": "{name} aliases", "heading": "{name} aliases",
"description": "Aliases are alternative names used in voice assistants to refer to this entity.", "description": "Aliases are alternative names used in voice assistants to refer to this entity.",
"remove_alias": "Remove alias", "remove_alias": "Remove alias {number}",
"input_label": "Alias {number}",
"save": "Save", "save": "Save",
"add_alias": "Add alias", "add_alias": "Add alias",
"no_aliases": "No aliases have been added yet", "no_aliases": "No aliases have been added yet",
@ -2634,7 +2662,7 @@
"enable_state_reporting": "Enable State Reporting", "enable_state_reporting": "Enable State Reporting",
"info_state_reporting": "If you enable state reporting, Home Assistant will send all state changes of exposed entities to Amazon. This allows you to always see the latest states in the Alexa app and use the state changes to create routines.", "info_state_reporting": "If you enable state reporting, Home Assistant will send all state changes of exposed entities to Amazon. This allows you to always see the latest states in the Alexa app and use the state changes to create routines.",
"state_reporting_error": "Unable to {enable_disable} report state.", "state_reporting_error": "Unable to {enable_disable} report state.",
"manage_entities": "Manage Entities", "manage_entities": "[%key:ui::panel::config::cloud::account::google::manage_entities%]",
"enable": "enable", "enable": "enable",
"disable": "disable", "disable": "disable",
"not_configured_title": "Alexa is not activated", "not_configured_title": "Alexa is not activated",
@ -2685,6 +2713,7 @@
"follow_domain": "[%key:ui::panel::config::cloud::google::follow_domain%]", "follow_domain": "[%key:ui::panel::config::cloud::google::follow_domain%]",
"exposed": "[%key:ui::panel::config::cloud::google::exposed%]", "exposed": "[%key:ui::panel::config::cloud::google::exposed%]",
"not_exposed": "[%key:ui::panel::config::cloud::google::not_exposed%]", "not_exposed": "[%key:ui::panel::config::cloud::google::not_exposed%]",
"manage_aliases": "[%key:ui::panel::config::cloud::google::manage_aliases%]",
"expose": "Expose to Alexa", "expose": "Expose to Alexa",
"sync_entities": "Synchronize entities", "sync_entities": "Synchronize entities",
"sync_entities_error": "Failed to sync entities:" "sync_entities_error": "Failed to sync entities:"
@ -2710,6 +2739,7 @@
"follow_domain": "Follow domain", "follow_domain": "Follow domain",
"exposed": "{selected} exposed", "exposed": "{selected} exposed",
"not_exposed": "{selected} not exposed", "not_exposed": "{selected} not exposed",
"manage_aliases": "Manage aliases",
"sync_to_google": "Synchronizing changes to Google.", "sync_to_google": "Synchronizing changes to Google.",
"sync_entities": "Synchronize entities", "sync_entities": "Synchronize entities",
"sync_entities_error": "Failed to sync entities:", "sync_entities_error": "Failed to sync entities:",