20230630.0 (#17120)

This commit is contained in:
Bram Kragten 2023-06-30 17:18:39 +02:00 committed by GitHub
commit 8f3d89da4f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 282 additions and 197 deletions

View File

@ -52,7 +52,7 @@
"@lit-labs/context": "0.3.3", "@lit-labs/context": "0.3.3",
"@lit-labs/motion": "1.0.3", "@lit-labs/motion": "1.0.3",
"@lit-labs/virtualizer": "2.0.3", "@lit-labs/virtualizer": "2.0.3",
"@lrnwebcomponents/simple-tooltip": "7.0.5", "@lrnwebcomponents/simple-tooltip": "7.0.10",
"@material/chips": "=14.0.0-canary.53b3cad2f.0", "@material/chips": "=14.0.0-canary.53b3cad2f.0",
"@material/data-table": "=14.0.0-canary.53b3cad2f.0", "@material/data-table": "=14.0.0-canary.53b3cad2f.0",
"@material/mwc-button": "0.27.0", "@material/mwc-button": "0.27.0",
@ -181,8 +181,8 @@
"@types/sortablejs": "1.15.1", "@types/sortablejs": "1.15.1",
"@types/tar": "6.1.5", "@types/tar": "6.1.5",
"@types/webspeechapi": "0.0.29", "@types/webspeechapi": "0.0.29",
"@typescript-eslint/eslint-plugin": "5.60.0", "@typescript-eslint/eslint-plugin": "5.60.1",
"@typescript-eslint/parser": "5.60.0", "@typescript-eslint/parser": "5.60.1",
"@web/dev-server": "0.1.38", "@web/dev-server": "0.1.38",
"@web/dev-server-rollup": "0.4.1", "@web/dev-server-rollup": "0.4.1",
"babel-loader": "9.1.2", "babel-loader": "9.1.2",

View File

@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project] [project]
name = "home-assistant-frontend" name = "home-assistant-frontend"
version = "20230629.0" version = "20230630.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

@ -163,6 +163,11 @@ export class HaTextField extends TextFieldBase {
.mdc-text-field__affix--prefix { .mdc-text-field__affix--prefix {
padding-right: var(--text-field-prefix-padding-right, 2px); padding-right: var(--text-field-prefix-padding-right, 2px);
} }
.mdc-text-field:not(.mdc-text-field--disabled)
.mdc-text-field__affix--prefix {
color: var(--mdc-text-field-label-ink-color);
}
`, `,
// safari workaround - must be explicit // safari workaround - must be explicit
document.dir === "rtl" document.dir === "rtl"

View File

@ -1,6 +1,6 @@
import { UnsubscribeFunc } from "home-assistant-js-websocket"; import type { UnsubscribeFunc } from "home-assistant-js-websocket";
import { HomeAssistant } from "../types"; import type { HomeAssistant } from "../types";
import { IntegrationType } from "./integration"; import type { IntegrationManifest, IntegrationType } from "./integration";
export interface ConfigEntry { export interface ConfigEntry {
entry_id: string; entry_id: string;
@ -143,3 +143,23 @@ export const enableConfigEntry = (hass: HomeAssistant, configEntryId: string) =>
entry_id: configEntryId, entry_id: configEntryId,
disabled_by: null, disabled_by: null,
}); });
export const sortConfigEntries = (
configEntries: ConfigEntry[],
manifestLookup: { [domain: string]: IntegrationManifest }
): ConfigEntry[] => {
const sortedConfigEntries = [...configEntries];
const getScore = (entry: ConfigEntry) => {
const manifest = manifestLookup[entry.domain] as
| IntegrationManifest
| undefined;
const isHelper = manifest?.integration_type === "helper";
return isHelper ? -1 : 1;
};
const configEntriesCompare = (a: ConfigEntry, b: ConfigEntry) =>
getScore(b) - getScore(a);
return sortedConfigEntries.sort(configEntriesCompare);
};

View File

@ -500,7 +500,7 @@ class DialogCalendarEventEditor extends LitElement {
return; return;
} }
const eventData = this._calculateData(); const eventData = this._calculateData();
if (eventData.rrule && range === RecurrenceRange.THISEVENT) { if (entry.rrule && eventData.rrule && range === RecurrenceRange.THISEVENT) {
// Updates to a single instance of a recurring event by definition // Updates to a single instance of a recurring event by definition
// cannot change the recurrence rule and doing so would be invalid. // cannot change the recurrence rule and doing so would be invalid.
// It is difficult to detect if the user changed the recurrence rule // It is difficult to detect if the user changed the recurrence rule

View File

@ -10,6 +10,8 @@ import { showConfirmationDialog } from "../../../../../dialogs/generic/show-dial
import { HomeAssistant } from "../../../../../types"; import { HomeAssistant } from "../../../../../types";
import { TriggerElement } from "../ha-automation-trigger-row"; import { TriggerElement } from "../ha-automation-trigger-row";
const PATTERN = "^[^.。,?¿?؟!;:]+$";
@customElement("ha-automation-trigger-conversation") @customElement("ha-automation-trigger-conversation")
export class HaConversationTrigger export class HaConversationTrigger
extends LitElement extends LitElement
@ -39,6 +41,12 @@ export class HaConversationTrigger
iconTrailing iconTrailing
.index=${index} .index=${index}
.value=${option} .value=${option}
.validationMessage=${this.hass.localize(
"ui.panel.config.automation.editor.triggers.type.conversation.no_punctuation"
)}
autoValidate
validateOnInitialRender
pattern=${PATTERN}
@change=${this._updateOption} @change=${this._updateOption}
> >
<ha-icon-button <ha-icon-button
@ -56,6 +64,11 @@ export class HaConversationTrigger
.label=${this.hass.localize( .label=${this.hass.localize(
"ui.panel.config.automation.editor.triggers.type.conversation.add_sentence" "ui.panel.config.automation.editor.triggers.type.conversation.add_sentence"
)} )}
.validationMessage=${this.hass.localize(
"ui.panel.config.automation.editor.triggers.type.conversation.no_punctuation"
)}
autoValidate
pattern=${PATTERN}
@keydown=${this._handleKeyAdd} @keydown=${this._handleKeyAdd}
@change=${this._addOption} @change=${this._addOption}
></ha-textfield>`; ></ha-textfield>`;

View File

@ -41,6 +41,7 @@ import {
ConfigEntry, ConfigEntry,
disableConfigEntry, disableConfigEntry,
DisableConfigEntryResult, DisableConfigEntryResult,
sortConfigEntries,
} from "../../../data/config_entries"; } from "../../../data/config_entries";
import { import {
computeDeviceName, computeDeviceName,
@ -60,7 +61,7 @@ import {
findBatteryEntity, findBatteryEntity,
updateEntityRegistryEntry, updateEntityRegistryEntry,
} from "../../../data/entity_registry"; } from "../../../data/entity_registry";
import { domainToName } from "../../../data/integration"; import { IntegrationManifest, domainToName } from "../../../data/integration";
import { SceneEntities, showSceneEditor } from "../../../data/scene"; import { SceneEntities, showSceneEditor } from "../../../data/scene";
import { findRelated, RelatedResult } from "../../../data/search"; import { findRelated, RelatedResult } from "../../../data/search";
import { import {
@ -115,6 +116,8 @@ export class HaConfigDevicePage extends LitElement {
@property({ attribute: false }) public areas!: AreaRegistryEntry[]; @property({ attribute: false }) public areas!: AreaRegistryEntry[];
@property({ attribute: false }) public manifests!: IntegrationManifest[];
@property() public deviceId!: string; @property() public deviceId!: string;
@property({ type: Boolean, reflect: true }) public narrow!: boolean; @property({ type: Boolean, reflect: true }) public narrow!: boolean;
@ -145,14 +148,24 @@ export class HaConfigDevicePage extends LitElement {
); );
private _integrations = memoizeOne( private _integrations = memoizeOne(
(device: DeviceRegistryEntry, entries: ConfigEntry[]): ConfigEntry[] => { (
device: DeviceRegistryEntry,
entries: ConfigEntry[],
manifests: IntegrationManifest[]
): ConfigEntry[] => {
const entryLookup: { [entryId: string]: ConfigEntry } = {}; const entryLookup: { [entryId: string]: ConfigEntry } = {};
for (const entry of entries) { for (const entry of entries) {
entryLookup[entry.entry_id] = entry; entryLookup[entry.entry_id] = entry;
} }
return device.config_entries const manifestLookup: { [domain: string]: IntegrationManifest } = {};
.map((entry) => entryLookup[entry]) for (const manifest of manifests) {
.filter(Boolean); manifestLookup[manifest.domain] = manifest;
}
const deviceEntries = device.config_entries
.filter((entId) => entId in entryLookup)
.map((entry) => entryLookup[entry]);
return sortConfigEntries(deviceEntries, manifestLookup);
} }
); );
@ -292,7 +305,11 @@ export class HaConfigDevicePage extends LitElement {
} }
const deviceName = computeDeviceName(device, this.hass); const deviceName = computeDeviceName(device, this.hass);
const integrations = this._integrations(device, this.entries); const integrations = this._integrations(
device,
this.entries,
this.manifests
);
const entities = this._entities(this.deviceId, this.entities); const entities = this._entities(this.deviceId, this.entities);
const entitiesByCategory = this._entitiesByCategory(entities); const entitiesByCategory = this._entitiesByCategory(entities);
const batteryEntity = this._batteryEntity(entities); const batteryEntity = this._batteryEntity(entities);
@ -920,7 +937,7 @@ export class HaConfigDevicePage extends LitElement {
} }
let links = await Promise.all( let links = await Promise.all(
this._integrations(device, this.entries).map( this._integrations(device, this.entries, this.manifests).map(
async (entry): Promise<boolean | { link: string; domain: string }> => { async (entry): Promise<boolean | { link: string; domain: string }> => {
if (entry.state !== "loaded") { if (entry.state !== "loaded") {
return false; return false;
@ -983,50 +1000,55 @@ export class HaConfigDevicePage extends LitElement {
} }
const buttons: DeviceAction[] = []; const buttons: DeviceAction[] = [];
this._integrations(device, this.entries).forEach((entry) => { this._integrations(device, this.entries, this.manifests).forEach(
if (entry.state !== "loaded" || !entry.supports_remove_device) { (entry) => {
return; if (entry.state !== "loaded" || !entry.supports_remove_device) {
return;
}
buttons.push({
action: async () => {
const confirmed = await showConfirmationDialog(this, {
text:
this._integrations(device, this.entries, this.manifests)
.length > 1
? this.hass.localize(
`ui.panel.config.devices.confirm_delete_integration`,
{
integration: domainToName(
this.hass.localize,
entry.domain
),
}
)
: this.hass.localize(
`ui.panel.config.devices.confirm_delete`
),
});
if (!confirmed) {
return;
}
await removeConfigEntryFromDevice(
this.hass!,
this.deviceId,
entry.entry_id
);
},
classes: "warning",
icon: mdiDelete,
label:
this._integrations(device, this.entries, this.manifests).length > 1
? this.hass.localize(
`ui.panel.config.devices.delete_device_integration`,
{
integration: domainToName(this.hass.localize, entry.domain),
}
)
: this.hass.localize(`ui.panel.config.devices.delete_device`),
});
} }
buttons.push({ );
action: async () => {
const confirmed = await showConfirmationDialog(this, {
text:
this._integrations(device, this.entries).length > 1
? this.hass.localize(
`ui.panel.config.devices.confirm_delete_integration`,
{
integration: domainToName(
this.hass.localize,
entry.domain
),
}
)
: this.hass.localize(`ui.panel.config.devices.confirm_delete`),
});
if (!confirmed) {
return;
}
await removeConfigEntryFromDevice(
this.hass!,
this.deviceId,
entry.entry_id
);
},
classes: "warning",
icon: mdiDelete,
label:
this._integrations(device, this.entries).length > 1
? this.hass.localize(
`ui.panel.config.devices.delete_device_integration`,
{
integration: domainToName(this.hass.localize, entry.domain),
}
)
: this.hass.localize(`ui.panel.config.devices.delete_device`),
});
});
if (buttons.length > 0) { if (buttons.length > 0) {
this._deleteButtons = buttons; this._deleteButtons = buttons;
@ -1061,9 +1083,11 @@ export class HaConfigDevicePage extends LitElement {
}); });
} }
const domains = this._integrations(device, this.entries).map( const domains = this._integrations(
(int) => int.domain device,
); this.entries,
this.manifests
).map((int) => int.domain);
if (domains.includes("mqtt")) { if (domains.includes("mqtt")) {
const mqtt = await import( const mqtt = await import(
@ -1103,9 +1127,11 @@ export class HaConfigDevicePage extends LitElement {
const deviceAlerts: DeviceAlert[] = []; const deviceAlerts: DeviceAlert[] = [];
const domains = this._integrations(device, this.entries).map( const domains = this._integrations(
(int) => int.domain device,
); this.entries,
this.manifests
).map((int) => int.domain);
if (domains.includes("zwave_js")) { if (domains.includes("zwave_js")) {
const zwave = await import( const zwave = await import(

View File

@ -25,7 +25,7 @@ import "../../../components/ha-check-list-item";
import "../../../components/ha-fab"; import "../../../components/ha-fab";
import "../../../components/ha-icon-button"; import "../../../components/ha-icon-button";
import { AreaRegistryEntry } from "../../../data/area_registry"; import { AreaRegistryEntry } from "../../../data/area_registry";
import { ConfigEntry } from "../../../data/config_entries"; import { ConfigEntry, sortConfigEntries } from "../../../data/config_entries";
import { import {
computeDeviceName, computeDeviceName,
DeviceEntityLookup, DeviceEntityLookup,
@ -36,7 +36,7 @@ import {
findBatteryChargingEntity, findBatteryChargingEntity,
findBatteryEntity, findBatteryEntity,
} from "../../../data/entity_registry"; } from "../../../data/entity_registry";
import { domainToName } from "../../../data/integration"; import { IntegrationManifest, domainToName } from "../../../data/integration";
import "../../../layouts/hass-tabs-subpage-data-table"; import "../../../layouts/hass-tabs-subpage-data-table";
import { haStyle } from "../../../resources/styles"; import { haStyle } from "../../../resources/styles";
import { HomeAssistant, Route } from "../../../types"; import { HomeAssistant, Route } from "../../../types";
@ -68,6 +68,8 @@ export class HaConfigDeviceDashboard extends LitElement {
@property() public areas!: AreaRegistryEntry[]; @property() public areas!: AreaRegistryEntry[];
@property() public manifests!: IntegrationManifest[];
@property() public route!: Route; @property() public route!: Route;
@state() private _searchParms = new URLSearchParams(window.location.search); @state() private _searchParms = new URLSearchParams(window.location.search);
@ -149,6 +151,7 @@ export class HaConfigDeviceDashboard extends LitElement {
entries: ConfigEntry[], entries: ConfigEntry[],
entities: EntityRegistryEntry[], entities: EntityRegistryEntry[],
areas: AreaRegistryEntry[], areas: AreaRegistryEntry[],
manifests: IntegrationManifest[],
filters: URLSearchParams, filters: URLSearchParams,
showDisabled: boolean, showDisabled: boolean,
localize: LocalizeFunc localize: LocalizeFunc
@ -186,6 +189,11 @@ export class HaConfigDeviceDashboard extends LitElement {
areaLookup[area.area_id] = area; areaLookup[area.area_id] = area;
} }
const manifestLookup: { [domain: string]: IntegrationManifest } = {};
for (const manifest of manifests) {
manifestLookup[manifest.domain] = manifest;
}
let filterConfigEntry: ConfigEntry | undefined; let filterConfigEntry: ConfigEntry | undefined;
const filteredDomains = new Set<string>(); const filteredDomains = new Set<string>();
@ -217,47 +225,51 @@ export class HaConfigDeviceDashboard extends LitElement {
outputDevices = outputDevices.filter((device) => !device.disabled_by); outputDevices = outputDevices.filter((device) => !device.disabled_by);
} }
outputDevices = outputDevices.map((device) => ({ outputDevices = outputDevices.map((device) => {
...device, const deviceEntries = sortConfigEntries(
name: computeDeviceName( device.config_entries
device, .filter((entId) => entId in entryLookup)
this.hass, .map((entId) => entryLookup[entId]),
deviceEntityLookup[device.id] manifestLookup
), );
model: return {
device.model || ...device,
`<${localize("ui.panel.config.devices.data_table.unknown")}>`, name: computeDeviceName(
manufacturer: device,
device.manufacturer || this.hass,
`<${localize("ui.panel.config.devices.data_table.unknown")}>`, deviceEntityLookup[device.id]
area: ),
device.area_id && areaLookup[device.area_id] model:
? areaLookup[device.area_id].name device.model ||
: "—", `<${localize("ui.panel.config.devices.data_table.unknown")}>`,
integration: device.config_entries.length manufacturer:
? device.config_entries device.manufacturer ||
.filter((entId) => entId in entryLookup) `<${localize("ui.panel.config.devices.data_table.unknown")}>`,
.map( area:
(entId) => device.area_id && areaLookup[device.area_id]
localize(`component.${entryLookup[entId].domain}.title`) || ? areaLookup[device.area_id].name
entryLookup[entId].domain : "—",
) integration: deviceEntries.length
.join(", ") ? deviceEntries
: this.hass.localize( .map(
"ui.panel.config.devices.data_table.no_integration" (entry) =>
), localize(`component.${entry.domain}.title`) || entry.domain
domains: device.config_entries )
.filter((entId) => entId in entryLookup) .join(", ")
.map((entId) => entryLookup[entId].domain), : this.hass.localize(
battery_entity: [ "ui.panel.config.devices.data_table.no_integration"
this._batteryEntity(device.id, deviceEntityLookup), ),
this._batteryChargingEntity(device.id, deviceEntityLookup), domains: deviceEntries.map((entry) => entry.domain),
], battery_entity: [
battery_level: this._batteryEntity(device.id, deviceEntityLookup),
this.hass.states[ this._batteryChargingEntity(device.id, deviceEntityLookup),
this._batteryEntity(device.id, deviceEntityLookup) || "" ],
]?.state, battery_level:
})); this.hass.states[
this._batteryEntity(device.id, deviceEntityLookup) || ""
]?.state,
};
});
this._numHiddenDevices = startLength - outputDevices.length; this._numHiddenDevices = startLength - outputDevices.length;
return { return {
@ -429,6 +441,7 @@ export class HaConfigDeviceDashboard extends LitElement {
this.entries, this.entries,
this.entities, this.entities,
this.areas, this.areas,
this.manifests,
this._searchParms, this._searchParms,
this._showDisabled, this._showDisabled,
this.hass.localize this.hass.localize
@ -565,6 +578,7 @@ export class HaConfigDeviceDashboard extends LitElement {
this.entries, this.entries,
this.entities, this.entities,
this.areas, this.areas,
this.manifests,
this._searchParms, this._searchParms,
this._showDisabled, this._showDisabled,
this.hass.localize this.hass.localize

View File

@ -14,6 +14,10 @@ import {
EntityRegistryEntry, EntityRegistryEntry,
subscribeEntityRegistry, subscribeEntityRegistry,
} from "../../../data/entity_registry"; } from "../../../data/entity_registry";
import {
IntegrationManifest,
fetchIntegrationManifests,
} from "../../../data/integration";
import { import {
HassRouterPage, HassRouterPage,
RouterOptions, RouterOptions,
@ -47,6 +51,8 @@ class HaConfigDevices extends HassRouterPage {
@state() private _configEntries: ConfigEntry[] = []; @state() private _configEntries: ConfigEntry[] = [];
@state() private _manifests: IntegrationManifest[] = [];
@state() @state()
private _entityRegistryEntries: EntityRegistryEntry[] = []; private _entityRegistryEntries: EntityRegistryEntry[] = [];
@ -99,6 +105,7 @@ class HaConfigDevices extends HassRouterPage {
pageEl.entities = this._entityRegistryEntries; pageEl.entities = this._entityRegistryEntries;
pageEl.entries = this._configEntries; pageEl.entries = this._configEntries;
pageEl.manifests = this._manifests;
pageEl.devices = this._deviceRegistryEntries; pageEl.devices = this._deviceRegistryEntries;
pageEl.areas = this._areas; pageEl.areas = this._areas;
pageEl.narrow = this.narrow; pageEl.narrow = this.narrow;
@ -111,6 +118,10 @@ class HaConfigDevices extends HassRouterPage {
getConfigEntries(this.hass).then((configEntries) => { getConfigEntries(this.hass).then((configEntries) => {
this._configEntries = configEntries; this._configEntries = configEntries;
}); });
fetchIntegrationManifests(this.hass).then((manifests) => {
this._manifests = manifests;
});
if (this._unsubs) { if (this._unsubs) {
return; return;
} }

View File

@ -64,7 +64,6 @@ class HaCounterForm extends LitElement {
if (!this.hass) { if (!this.hass) {
return nothing; return nothing;
} }
const nameInvalid = !this._name || this._name.trim() === "";
return html` return html`
<div class="form"> <div class="form">
@ -75,10 +74,11 @@ class HaCounterForm extends LitElement {
.label=${this.hass!.localize( .label=${this.hass!.localize(
"ui.dialogs.helper_settings.generic.name" "ui.dialogs.helper_settings.generic.name"
)} )}
.errorMessage=${this.hass!.localize( autoValidate
required
.validationMessage=${this.hass!.localize(
"ui.dialogs.helper_settings.required_error_msg" "ui.dialogs.helper_settings.required_error_msg"
)} )}
.invalid=${nameInvalid}
dialogInitialFocus dialogInitialFocus
></ha-textfield> ></ha-textfield>
<ha-icon-picker <ha-icon-picker

View File

@ -42,7 +42,6 @@ class HaInputBooleanForm extends LitElement {
if (!this.hass) { if (!this.hass) {
return nothing; return nothing;
} }
const nameInvalid = !this._name || this._name.trim() === "";
return html` return html`
<div class="form"> <div class="form">
@ -53,10 +52,11 @@ class HaInputBooleanForm extends LitElement {
.label=${this.hass!.localize( .label=${this.hass!.localize(
"ui.dialogs.helper_settings.generic.name" "ui.dialogs.helper_settings.generic.name"
)} )}
.errorMessage=${this.hass!.localize( autoValidate
required
.validationMessage=${this.hass!.localize(
"ui.dialogs.helper_settings.required_error_msg" "ui.dialogs.helper_settings.required_error_msg"
)} )}
.invalid=${nameInvalid}
dialogInitialFocus dialogInitialFocus
></ha-textfield> ></ha-textfield>
<ha-icon-picker <ha-icon-picker

View File

@ -42,7 +42,6 @@ class HaInputButtonForm extends LitElement {
if (!this.hass) { if (!this.hass) {
return nothing; return nothing;
} }
const nameInvalid = !this._name || this._name.trim() === "";
return html` return html`
<div class="form"> <div class="form">
@ -53,10 +52,11 @@ class HaInputButtonForm extends LitElement {
.label=${this.hass!.localize( .label=${this.hass!.localize(
"ui.dialogs.helper_settings.generic.name" "ui.dialogs.helper_settings.generic.name"
)} )}
.errorMessage=${this.hass!.localize( autoValidate
required
.validationMessage=${this.hass!.localize(
"ui.dialogs.helper_settings.required_error_msg" "ui.dialogs.helper_settings.required_error_msg"
)} )}
.invalid=${nameInvalid}
dialogInitialFocus dialogInitialFocus
></ha-textfield> ></ha-textfield>
<ha-icon-picker <ha-icon-picker

View File

@ -56,7 +56,6 @@ class HaInputDateTimeForm extends LitElement {
if (!this.hass) { if (!this.hass) {
return nothing; return nothing;
} }
const nameInvalid = !this._name || this._name.trim() === "";
return html` return html`
<div class="form"> <div class="form">
@ -67,10 +66,11 @@ class HaInputDateTimeForm extends LitElement {
.label=${this.hass!.localize( .label=${this.hass!.localize(
"ui.dialogs.helper_settings.generic.name" "ui.dialogs.helper_settings.generic.name"
)} )}
.errorMessage=${this.hass!.localize( autoValidate
required
.validationMessage=${this.hass!.localize(
"ui.dialogs.helper_settings.required_error_msg" "ui.dialogs.helper_settings.required_error_msg"
)} )}
.invalid=${nameInvalid}
dialogInitialFocus dialogInitialFocus
></ha-textfield> ></ha-textfield>
<ha-icon-picker <ha-icon-picker

View File

@ -71,7 +71,6 @@ class HaInputNumberForm extends LitElement {
if (!this.hass) { if (!this.hass) {
return nothing; return nothing;
} }
const nameInvalid = !this._name || this._name.trim() === "";
return html` return html`
<div class="form"> <div class="form">
@ -82,10 +81,11 @@ class HaInputNumberForm extends LitElement {
.label=${this.hass!.localize( .label=${this.hass!.localize(
"ui.dialogs.helper_settings.generic.name" "ui.dialogs.helper_settings.generic.name"
)} )}
.errorMessage=${this.hass!.localize( autoValidate
required
.validationMessage=${this.hass!.localize(
"ui.dialogs.helper_settings.required_error_msg" "ui.dialogs.helper_settings.required_error_msg"
)} )}
.invalid=${nameInvalid}
dialogInitialFocus dialogInitialFocus
></ha-textfield> ></ha-textfield>
<ha-icon-picker <ha-icon-picker

View File

@ -54,20 +54,20 @@ class HaInputSelectForm extends LitElement {
if (!this.hass) { if (!this.hass) {
return nothing; return nothing;
} }
const nameInvalid = !this._name || this._name.trim() === "";
return html` return html`
<div class="form"> <div class="form">
<ha-textfield <ha-textfield
dialogInitialFocus dialogInitialFocus
.errorMessage=${this.hass!.localize( autoValidate
required
.validationMessage=${this.hass!.localize(
"ui.dialogs.helper_settings.required_error_msg" "ui.dialogs.helper_settings.required_error_msg"
)} )}
.value=${this._name} .value=${this._name}
.label=${this.hass!.localize( .label=${this.hass!.localize(
"ui.dialogs.helper_settings.generic.name" "ui.dialogs.helper_settings.generic.name"
)} )}
.invalid=${nameInvalid}
.configValue=${"name"} .configValue=${"name"}
@input=${this._valueChanged} @input=${this._valueChanged}
></ha-textfield> ></ha-textfield>

View File

@ -61,7 +61,6 @@ class HaInputTextForm extends LitElement {
if (!this.hass) { if (!this.hass) {
return nothing; return nothing;
} }
const nameInvalid = !this._name || this._name.trim() === "";
return html` return html`
<div class="form"> <div class="form">
@ -72,10 +71,11 @@ class HaInputTextForm extends LitElement {
.label=${this.hass!.localize( .label=${this.hass!.localize(
"ui.dialogs.helper_settings.generic.name" "ui.dialogs.helper_settings.generic.name"
)} )}
.errorMessage=${this.hass!.localize( autoValidate
required
.validationMessage=${this.hass!.localize(
"ui.dialogs.helper_settings.required_error_msg" "ui.dialogs.helper_settings.required_error_msg"
)} )}
.invalid=${nameInvalid}
dialogInitialFocus dialogInitialFocus
></ha-textfield> ></ha-textfield>
<ha-icon-picker <ha-icon-picker

View File

@ -141,7 +141,6 @@ class HaScheduleForm extends LitElement {
if (!this.hass) { if (!this.hass) {
return nothing; return nothing;
} }
const nameInvalid = !this._name || this._name.trim() === "";
return html` return html`
<div class="form"> <div class="form">
@ -152,10 +151,11 @@ class HaScheduleForm extends LitElement {
.label=${this.hass!.localize( .label=${this.hass!.localize(
"ui.dialogs.helper_settings.generic.name" "ui.dialogs.helper_settings.generic.name"
)} )}
.errorMessage=${this.hass!.localize( autoValidate
required
.validationMessage=${this.hass!.localize(
"ui.dialogs.helper_settings.required_error_msg" "ui.dialogs.helper_settings.required_error_msg"
)} )}
.invalid=${nameInvalid}
dialogInitialFocus dialogInitialFocus
></ha-textfield> ></ha-textfield>
<ha-icon-picker <ha-icon-picker

View File

@ -50,7 +50,6 @@ class HaTimerForm extends LitElement {
if (!this.hass) { if (!this.hass) {
return nothing; return nothing;
} }
const nameInvalid = !this._name || this._name.trim() === "";
return html` return html`
<div class="form"> <div class="form">
@ -61,10 +60,11 @@ class HaTimerForm extends LitElement {
.label=${this.hass!.localize( .label=${this.hass!.localize(
"ui.dialogs.helper_settings.generic.name" "ui.dialogs.helper_settings.generic.name"
)} )}
.errorMessage=${this.hass!.localize( autoValidate
required
.validationMessage=${this.hass!.localize(
"ui.dialogs.helper_settings.required_error_msg" "ui.dialogs.helper_settings.required_error_msg"
)} )}
.invalid=${nameInvalid}
dialogInitialFocus dialogInitialFocus
></ha-textfield> ></ha-textfield>
<ha-icon-picker <ha-icon-picker

View File

@ -1,10 +1,8 @@
import type { RequestSelectedDetail } from "@material/mwc-list/mwc-list-item";
import { mdiBookshelf, mdiCog, mdiDotsVertical, mdiOpenInNew } from "@mdi/js"; import { mdiBookshelf, mdiCog, mdiDotsVertical, mdiOpenInNew } from "@mdi/js";
import { css, html, LitElement, TemplateResult } from "lit"; import { LitElement, TemplateResult, css, html } from "lit";
import { customElement, property } from "lit/decorators"; import { customElement, property } from "lit/decorators";
import { classMap } from "lit/directives/class-map"; import { classMap } from "lit/directives/class-map";
import { fireEvent } from "../../../common/dom/fire_event"; import { fireEvent } from "../../../common/dom/fire_event";
import { shouldHandleRequestSelectedEvent } from "../../../common/mwc/handle-request-selected-event";
import { import {
ATTENTION_SOURCES, ATTENTION_SOURCES,
DISCOVERY_SOURCES, DISCOVERY_SOURCES,
@ -53,7 +51,7 @@ export class HaConfigFlowCard extends LitElement {
? html`<mwc-button ? html`<mwc-button
@click=${this._ignoreFlow} @click=${this._ignoreFlow}
.label=${this.hass.localize( .label=${this.hass.localize(
`ui.panel.config.integrations.ignore.ignore` "ui.panel.config.integrations.ignore.ignore"
)} )}
></mwc-button>` ></mwc-button>`
: ""} : ""}
@ -130,10 +128,7 @@ export class HaConfigFlowCard extends LitElement {
}); });
} }
private async _ignoreFlow(ev: CustomEvent<RequestSelectedDetail>) { private async _ignoreFlow() {
if (!shouldHandleRequestSelectedEvent(ev)) {
return;
}
const confirmed = await showConfirmationDialog(this, { const confirmed = await showConfirmationDialog(this, {
title: this.hass!.localize( title: this.hass!.localize(
"ui.panel.config.integrations.ignore.confirm_ignore_title", "ui.panel.config.integrations.ignore.confirm_ignore_title",

View File

@ -2382,6 +2382,7 @@
}, },
"conversation": { "conversation": {
"label": "Sentence", "label": "Sentence",
"no_punctuation": "You should not use punctuation in your sentence",
"add_sentence": "Add sentence", "add_sentence": "Add sentence",
"delete": "Delete sentence", "delete": "Delete sentence",
"confirm_delete": "Are you sure you want to delete this sentence?", "confirm_delete": "Are you sure you want to delete this sentence?",

110
yarn.lock
View File

@ -2107,12 +2107,12 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@lrnwebcomponents/simple-tooltip@npm:7.0.5": "@lrnwebcomponents/simple-tooltip@npm:7.0.10":
version: 7.0.5 version: 7.0.10
resolution: "@lrnwebcomponents/simple-tooltip@npm:7.0.5" resolution: "@lrnwebcomponents/simple-tooltip@npm:7.0.10"
dependencies: dependencies:
lit: ^2.7.5 lit: ^2.7.5
checksum: 57a50ed30be103f8fbe37fbef33bb4071f8c2bbecba6964643404532883f3479fe034ee64b1d71d11d7ea60ee63e39bb5ef5f53dc0b9c88d6ec525627135602b checksum: 4e355b580b21b246fed49d3625e6999777b12d8154b786ab27eeafba6d49d2efa4a49a8aa4e7b5e75874e68a1795f911f295f80eedeaf1ad5fbd633d6efeb7c4
languageName: node languageName: node
linkType: hard linkType: hard
@ -4671,14 +4671,14 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@typescript-eslint/eslint-plugin@npm:5.60.0": "@typescript-eslint/eslint-plugin@npm:5.60.1":
version: 5.60.0 version: 5.60.1
resolution: "@typescript-eslint/eslint-plugin@npm:5.60.0" resolution: "@typescript-eslint/eslint-plugin@npm:5.60.1"
dependencies: dependencies:
"@eslint-community/regexpp": ^4.4.0 "@eslint-community/regexpp": ^4.4.0
"@typescript-eslint/scope-manager": 5.60.0 "@typescript-eslint/scope-manager": 5.60.1
"@typescript-eslint/type-utils": 5.60.0 "@typescript-eslint/type-utils": 5.60.1
"@typescript-eslint/utils": 5.60.0 "@typescript-eslint/utils": 5.60.1
debug: ^4.3.4 debug: ^4.3.4
grapheme-splitter: ^1.0.4 grapheme-splitter: ^1.0.4
ignore: ^5.2.0 ignore: ^5.2.0
@ -4691,43 +4691,43 @@ __metadata:
peerDependenciesMeta: peerDependenciesMeta:
typescript: typescript:
optional: true optional: true
checksum: 61dd70a1ea9787e69d0d4cd14f6a4c94ba786b535a3f519ade7926d965ee1d4f8fefa8bf0224ee57c5c6517eec3674c0fd06f9226536aa428c2bdddeed1e70f4 checksum: 6ea3fdc64b216ee709318bfce1573cd8d90836150f0075aaa8755c347541af9ec026043e538a3264d28d1b32ff49b1fd7c6163826b8513f19f0957fefccf7752
languageName: node languageName: node
linkType: hard linkType: hard
"@typescript-eslint/parser@npm:5.60.0": "@typescript-eslint/parser@npm:5.60.1":
version: 5.60.0 version: 5.60.1
resolution: "@typescript-eslint/parser@npm:5.60.0" resolution: "@typescript-eslint/parser@npm:5.60.1"
dependencies: dependencies:
"@typescript-eslint/scope-manager": 5.60.0 "@typescript-eslint/scope-manager": 5.60.1
"@typescript-eslint/types": 5.60.0 "@typescript-eslint/types": 5.60.1
"@typescript-eslint/typescript-estree": 5.60.0 "@typescript-eslint/typescript-estree": 5.60.1
debug: ^4.3.4 debug: ^4.3.4
peerDependencies: peerDependencies:
eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 eslint: ^6.0.0 || ^7.0.0 || ^8.0.0
peerDependenciesMeta: peerDependenciesMeta:
typescript: typescript:
optional: true optional: true
checksum: 94e7931a5b356b16638b281b8e1d661f8b1660f0c75a323537f68b311dae91b7a575a0a019d4ea05a79cc5d42b5cb41cc367205691cdfd292ef96a3b66b1e58b checksum: 08f1552ab0da178524a8de3654d2fb7c8ecb9efdad8e771c9cbf4af555c42e77d17b2c182d139a531cc76c3cabd091d1d25024c2c215cb809dca8b147c8a493c
languageName: node languageName: node
linkType: hard linkType: hard
"@typescript-eslint/scope-manager@npm:5.60.0": "@typescript-eslint/scope-manager@npm:5.60.1":
version: 5.60.0 version: 5.60.1
resolution: "@typescript-eslint/scope-manager@npm:5.60.0" resolution: "@typescript-eslint/scope-manager@npm:5.60.1"
dependencies: dependencies:
"@typescript-eslint/types": 5.60.0 "@typescript-eslint/types": 5.60.1
"@typescript-eslint/visitor-keys": 5.60.0 "@typescript-eslint/visitor-keys": 5.60.1
checksum: b21ee1ef57be948a806aa31fd65a9186766b3e1a727030dc47025edcadc54bd1aa6133a439acd5f44a93e2b983dd55bc5571bb01cb834461dab733682d66256a checksum: 32c0786123f12fbb861aba3527471134a2e9978c7f712e0d7650080651870903482aed72a55f81deba9493118c1ca3c57edaaaa75d7acd9892818e3e9cc341ef
languageName: node languageName: node
linkType: hard linkType: hard
"@typescript-eslint/type-utils@npm:5.60.0": "@typescript-eslint/type-utils@npm:5.60.1":
version: 5.60.0 version: 5.60.1
resolution: "@typescript-eslint/type-utils@npm:5.60.0" resolution: "@typescript-eslint/type-utils@npm:5.60.1"
dependencies: dependencies:
"@typescript-eslint/typescript-estree": 5.60.0 "@typescript-eslint/typescript-estree": 5.60.1
"@typescript-eslint/utils": 5.60.0 "@typescript-eslint/utils": 5.60.1
debug: ^4.3.4 debug: ^4.3.4
tsutils: ^3.21.0 tsutils: ^3.21.0
peerDependencies: peerDependencies:
@ -4735,23 +4735,23 @@ __metadata:
peerDependenciesMeta: peerDependenciesMeta:
typescript: typescript:
optional: true optional: true
checksum: b90ce97592f2db899d88d7a325fec4d2ea11a7b8b4306787310890c27fb51862a6c003675252e9dc465908f791ad5320ea7307260ecd10e89ca1d209fbf8616d checksum: f8d5f87b5441d5c671f69631efd103f5f45e0cb7dbe0131a5b4234a5208ac845041219e8baaa3adc341e82a602165dd6fabf4fd06964d0109d0875425c8ac918
languageName: node languageName: node
linkType: hard linkType: hard
"@typescript-eslint/types@npm:5.60.0": "@typescript-eslint/types@npm:5.60.1":
version: 5.60.0 version: 5.60.1
resolution: "@typescript-eslint/types@npm:5.60.0" resolution: "@typescript-eslint/types@npm:5.60.1"
checksum: 48f29e5c084c5663cfed1a6c4458799a6690a213e7861a24501f9b96698ae59e89a1df1c77e481777e4da78f1b0a5573a549f7b8880e3f4071a7a8b686254db8 checksum: 766b6c857493b72a8f515e6a8e409476a317b7a7f0401fbcdf18f417839fca004dcaf06f58eb5ba00777e3ca9c68cd2f56fda79f3a8eb8a418095b5b1f625712
languageName: node languageName: node
linkType: hard linkType: hard
"@typescript-eslint/typescript-estree@npm:5.60.0": "@typescript-eslint/typescript-estree@npm:5.60.1":
version: 5.60.0 version: 5.60.1
resolution: "@typescript-eslint/typescript-estree@npm:5.60.0" resolution: "@typescript-eslint/typescript-estree@npm:5.60.1"
dependencies: dependencies:
"@typescript-eslint/types": 5.60.0 "@typescript-eslint/types": 5.60.1
"@typescript-eslint/visitor-keys": 5.60.0 "@typescript-eslint/visitor-keys": 5.60.1
debug: ^4.3.4 debug: ^4.3.4
globby: ^11.1.0 globby: ^11.1.0
is-glob: ^4.0.3 is-glob: ^4.0.3
@ -4760,35 +4760,35 @@ __metadata:
peerDependenciesMeta: peerDependenciesMeta:
typescript: typescript:
optional: true optional: true
checksum: 0f4f342730ead42ba60b5fca4bf1950abebd83030010c38b5df98ff9fd95d0ce1cfc3974a44c90c65f381f4f172adcf1a540e018d7968cc845d937bf6c734dae checksum: 5bb9d08c3cbc303fc64647878cae37283c4cfa9e3ed00da02ee25dc2e46798a1ad6964c9f04086f0134716671357e6569a65ea0ae75f0f3ff94ae67666385c6f
languageName: node languageName: node
linkType: hard linkType: hard
"@typescript-eslint/utils@npm:5.60.0": "@typescript-eslint/utils@npm:5.60.1":
version: 5.60.0 version: 5.60.1
resolution: "@typescript-eslint/utils@npm:5.60.0" resolution: "@typescript-eslint/utils@npm:5.60.1"
dependencies: dependencies:
"@eslint-community/eslint-utils": ^4.2.0 "@eslint-community/eslint-utils": ^4.2.0
"@types/json-schema": ^7.0.9 "@types/json-schema": ^7.0.9
"@types/semver": ^7.3.12 "@types/semver": ^7.3.12
"@typescript-eslint/scope-manager": 5.60.0 "@typescript-eslint/scope-manager": 5.60.1
"@typescript-eslint/types": 5.60.0 "@typescript-eslint/types": 5.60.1
"@typescript-eslint/typescript-estree": 5.60.0 "@typescript-eslint/typescript-estree": 5.60.1
eslint-scope: ^5.1.1 eslint-scope: ^5.1.1
semver: ^7.3.7 semver: ^7.3.7
peerDependencies: peerDependencies:
eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 eslint: ^6.0.0 || ^7.0.0 || ^8.0.0
checksum: cbe56567f0b53e24ad7ef7d2fb4cdc8596e2559c21ee639aa0560879b6216208550e51e9d8ae4b388ff21286809c6dc985cec66738294871051396a8ae5bccbc checksum: 00c1adaa09d5d5be947e98962a78c21ed08c3ac46dd5ddd7b78f6102537d50afd4578a42a3e09a24dd51f5bc493f0b968627b4423647540164b2d2380afa9246
languageName: node languageName: node
linkType: hard linkType: hard
"@typescript-eslint/visitor-keys@npm:5.60.0": "@typescript-eslint/visitor-keys@npm:5.60.1":
version: 5.60.0 version: 5.60.1
resolution: "@typescript-eslint/visitor-keys@npm:5.60.0" resolution: "@typescript-eslint/visitor-keys@npm:5.60.1"
dependencies: dependencies:
"@typescript-eslint/types": 5.60.0 "@typescript-eslint/types": 5.60.1
eslint-visitor-keys: ^3.3.0 eslint-visitor-keys: ^3.3.0
checksum: d39b2485d030f9755820d0f6f3748a8ec44e1ca23cb36ddcba67a9eb1f258c8ec83c61fc015c50e8f4a00d05df62d719dbda445625e3e71a64a659f1d248157e checksum: 137f6a6f8efb398969087147b59f99f7d0deed044d89d7efce3631bb90bc32e3a13a5cee6a65e1c9830862c5c4402ac1a9b2c9e31fe46d1716602af2813bffae
languageName: node languageName: node
linkType: hard linkType: hard
@ -9622,7 +9622,7 @@ __metadata:
"@lit-labs/context": 0.3.3 "@lit-labs/context": 0.3.3
"@lit-labs/motion": 1.0.3 "@lit-labs/motion": 1.0.3
"@lit-labs/virtualizer": 2.0.3 "@lit-labs/virtualizer": 2.0.3
"@lrnwebcomponents/simple-tooltip": 7.0.5 "@lrnwebcomponents/simple-tooltip": 7.0.10
"@material/chips": =14.0.0-canary.53b3cad2f.0 "@material/chips": =14.0.0-canary.53b3cad2f.0
"@material/data-table": =14.0.0-canary.53b3cad2f.0 "@material/data-table": =14.0.0-canary.53b3cad2f.0
"@material/mwc-button": 0.27.0 "@material/mwc-button": 0.27.0
@ -9688,8 +9688,8 @@ __metadata:
"@types/sortablejs": 1.15.1 "@types/sortablejs": 1.15.1
"@types/tar": 6.1.5 "@types/tar": 6.1.5
"@types/webspeechapi": 0.0.29 "@types/webspeechapi": 0.0.29
"@typescript-eslint/eslint-plugin": 5.60.0 "@typescript-eslint/eslint-plugin": 5.60.1
"@typescript-eslint/parser": 5.60.0 "@typescript-eslint/parser": 5.60.1
"@vaadin/combo-box": 24.1.1 "@vaadin/combo-box": 24.1.1
"@vaadin/vaadin-themable-mixin": 24.1.1 "@vaadin/vaadin-themable-mixin": 24.1.1
"@vibrant/color": 3.2.1-alpha.1 "@vibrant/color": 3.2.1-alpha.1