mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-24 09:46:36 +00:00
Allow to change switch as x (#16671)
This commit is contained in:
parent
0771a780d9
commit
7e5a85dbe7
@ -95,12 +95,17 @@ export interface WeatherEntityOptions {
|
||||
wind_speed_unit?: string | null;
|
||||
}
|
||||
|
||||
export interface SwitchAsXEntityOptions {
|
||||
entity_id: string;
|
||||
}
|
||||
|
||||
export interface EntityRegistryOptions {
|
||||
number?: NumberEntityOptions;
|
||||
sensor?: SensorEntityOptions;
|
||||
lock?: LockEntityOptions;
|
||||
weather?: WeatherEntityOptions;
|
||||
light?: LightEntityOptions;
|
||||
switch_as_x?: SwitchAsXEntityOptions;
|
||||
conversation?: Record<string, unknown>;
|
||||
"cloud.alexa"?: Record<string, unknown>;
|
||||
"cloud.google_assistant"?: Record<string, unknown>;
|
||||
|
@ -16,6 +16,7 @@ import {
|
||||
} from "../../../../../data/entity_registry";
|
||||
import { HELPERS_CRUD } from "../../../../../data/helpers_crud";
|
||||
import { showConfirmationDialog } from "../../../../../dialogs/generic/show-dialog-box";
|
||||
import { hideMoreInfoDialog } from "../../../../../dialogs/more-info/show-ha-more-info-dialog";
|
||||
import { haStyle } from "../../../../../resources/styles";
|
||||
import type { HomeAssistant } from "../../../../../types";
|
||||
import type { Helper } from "../../../helpers/const";
|
||||
@ -148,8 +149,10 @@ export class EntityRegistrySettingsHelper extends LitElement {
|
||||
this._item
|
||||
);
|
||||
}
|
||||
await this._registryEditor!.updateEntry();
|
||||
fireEvent(this, "close-dialog");
|
||||
const result = await this._registryEditor!.updateEntry();
|
||||
if (result.close) {
|
||||
hideMoreInfoDialog(this);
|
||||
}
|
||||
} catch (err: any) {
|
||||
this._error = err.message || "Unknown error";
|
||||
} finally {
|
||||
|
@ -43,7 +43,7 @@ import {
|
||||
STREAM_TYPE_HLS,
|
||||
updateCameraPrefs,
|
||||
} from "../../../data/camera";
|
||||
import { ConfigEntry } from "../../../data/config_entries";
|
||||
import { ConfigEntry, deleteConfigEntry } from "../../../data/config_entries";
|
||||
import {
|
||||
createConfigFlow,
|
||||
handleConfigFlowStep,
|
||||
@ -57,10 +57,10 @@ import {
|
||||
EntityRegistryEntry,
|
||||
EntityRegistryEntryUpdateParams,
|
||||
ExtEntityRegistryEntry,
|
||||
fetchEntityRegistry,
|
||||
SensorEntityOptions,
|
||||
updateEntityRegistryEntry,
|
||||
LockEntityOptions,
|
||||
SensorEntityOptions,
|
||||
subscribeEntityRegistry,
|
||||
updateEntityRegistryEntry,
|
||||
} from "../../../data/entity_registry";
|
||||
import { domainToName } from "../../../data/integration";
|
||||
import { getNumberDeviceClassConvertibleUnits } from "../../../data/number";
|
||||
@ -307,6 +307,13 @@ export class EntityRegistrySettingsEditor extends LitElement {
|
||||
this._weatherConvertibleUnits = undefined;
|
||||
}
|
||||
}
|
||||
if (changedProps.has("helperConfigEntry")) {
|
||||
if (this.helperConfigEntry?.domain === "switch_as_x") {
|
||||
this._switchAs = computeDomain(this.entry.entity_id);
|
||||
} else {
|
||||
this._switchAs = "switch";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected render() {
|
||||
@ -366,6 +373,74 @@ export class EntityRegistrySettingsEditor extends LitElement {
|
||||
: undefined}
|
||||
.disabled=${this.disabled}
|
||||
></ha-icon-picker>`}
|
||||
${domain === "switch"
|
||||
? html`<ha-select
|
||||
.label=${this.hass.localize(
|
||||
"ui.dialogs.entity_registry.editor.device_class"
|
||||
)}
|
||||
naturalMenuWidth
|
||||
fixedMenuPosition
|
||||
@selected=${this._switchAsChanged}
|
||||
@closed=${stopPropagation}
|
||||
>
|
||||
<ha-list-item
|
||||
value="switch"
|
||||
.selected=${!this._deviceClass || this._deviceClass === "switch"}
|
||||
>
|
||||
${domainToName(this.hass.localize, "switch")}
|
||||
</ha-list-item>
|
||||
<ha-list-item
|
||||
value="outlet"
|
||||
.selected=${this._deviceClass === "outlet"}
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.dialogs.entity_registry.editor.device_classes.switch.outlet"
|
||||
)}
|
||||
</ha-list-item>
|
||||
<li divider role="separator"></li>
|
||||
${this._switchAsDomainsSorted(
|
||||
SWITCH_AS_DOMAINS,
|
||||
this.hass.localize
|
||||
).map(
|
||||
(entry) => html`
|
||||
<ha-list-item .value=${entry.domain}>
|
||||
${entry.label}
|
||||
</ha-list-item>
|
||||
`
|
||||
)}
|
||||
</ha-select>`
|
||||
: this.helperConfigEntry?.domain === "switch_as_x"
|
||||
? html`<ha-select
|
||||
.label=${this.hass.localize(
|
||||
"ui.dialogs.entity_registry.editor.switch_as_x"
|
||||
)}
|
||||
.value=${this._switchAs}
|
||||
naturalMenuWidth
|
||||
fixedMenuPosition
|
||||
@selected=${this._switchAsChanged}
|
||||
@closed=${stopPropagation}
|
||||
>
|
||||
<ha-list-item value="switch">
|
||||
${domainToName(this.hass.localize, "switch")}
|
||||
</ha-list-item>
|
||||
<ha-list-item .value=${domain}>
|
||||
${domainToName(this.hass.localize, domain)}
|
||||
</ha-list-item>
|
||||
<li divider role="separator"></li>
|
||||
${this._switchAsDomainsSorted(
|
||||
SWITCH_AS_DOMAINS,
|
||||
this.hass.localize
|
||||
).map((entry) =>
|
||||
domain === entry.domain
|
||||
? nothing
|
||||
: html`
|
||||
<ha-list-item .value=${entry.domain}>
|
||||
${entry.label}
|
||||
</ha-list-item>
|
||||
`
|
||||
)}
|
||||
</ha-select>`
|
||||
: nothing}
|
||||
${this._deviceClassOptions
|
||||
? html`
|
||||
<ha-select
|
||||
@ -599,45 +674,6 @@ export class EntityRegistrySettingsEditor extends LitElement {
|
||||
</ha-select>
|
||||
`
|
||||
: ""}
|
||||
${domain === "switch"
|
||||
? html`<ha-select
|
||||
.label=${this.hass.localize(
|
||||
"ui.dialogs.entity_registry.editor.device_class"
|
||||
)}
|
||||
naturalMenuWidth
|
||||
fixedMenuPosition
|
||||
@selected=${this._switchAsChanged}
|
||||
@closed=${stopPropagation}
|
||||
>
|
||||
<ha-list-item
|
||||
value="switch"
|
||||
.selected=${!this._deviceClass || this._deviceClass === "switch"}
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.dialogs.entity_registry.editor.device_classes.switch.switch"
|
||||
)}
|
||||
</ha-list-item>
|
||||
<ha-list-item
|
||||
value="outlet"
|
||||
.selected=${this._deviceClass === "outlet"}
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.dialogs.entity_registry.editor.device_classes.switch.outlet"
|
||||
)}
|
||||
</ha-list-item>
|
||||
<li divider role="separator"></li>
|
||||
${this._switchAsDomainsSorted(
|
||||
SWITCH_AS_DOMAINS,
|
||||
this.hass.localize
|
||||
).map(
|
||||
(entry) => html`
|
||||
<ha-list-item .value=${entry.domain}>
|
||||
${entry.label}
|
||||
</ha-list-item>
|
||||
`
|
||||
)}
|
||||
</ha-select>`
|
||||
: ""}
|
||||
<ha-textfield
|
||||
error-message="Domain needs to stay the same"
|
||||
.value=${this._entityId}
|
||||
@ -880,8 +916,16 @@ export class EntityRegistrySettingsEditor extends LitElement {
|
||||
`;
|
||||
}
|
||||
|
||||
public async updateEntry(): Promise<ExtEntityRegistryEntry> {
|
||||
const parent = (this.getRootNode() as ShadowRoot).host as HTMLElement;
|
||||
public async updateEntry(): Promise<{
|
||||
close: boolean;
|
||||
entry: ExtEntityRegistryEntry;
|
||||
}> {
|
||||
let close = true;
|
||||
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
||||
let parent: HTMLElement = this;
|
||||
while (parent?.localName !== "home-assistant") {
|
||||
parent = (parent.getRootNode() as ShadowRoot).host as HTMLElement;
|
||||
}
|
||||
|
||||
const params: Partial<EntityRegistryEntryUpdateParams> = {
|
||||
name: this._name.trim() || null,
|
||||
@ -979,13 +1023,18 @@ export class EntityRegistrySettingsEditor extends LitElement {
|
||||
});
|
||||
}
|
||||
|
||||
if (this._switchAs !== "switch") {
|
||||
if (domain === "switch" && this._switchAs !== "switch") {
|
||||
// generate config flow for switch_as_x
|
||||
if (
|
||||
await showConfirmationDialog(this, {
|
||||
text: this.hass!.localize(
|
||||
"ui.dialogs.entity_registry.editor.switch_as_x_confirm",
|
||||
"domain",
|
||||
this._switchAs
|
||||
{
|
||||
domain: domainToName(
|
||||
this.hass.localize,
|
||||
this._switchAs
|
||||
).toLowerCase(),
|
||||
}
|
||||
),
|
||||
})
|
||||
) {
|
||||
@ -999,24 +1048,107 @@ export class EntityRegistrySettingsEditor extends LitElement {
|
||||
}
|
||||
)) as DataEntryFlowStepCreateEntry;
|
||||
if (configFlowResult.result?.entry_id) {
|
||||
const unsub = await this.hass.connection.subscribeEvents(() => {
|
||||
unsub();
|
||||
fetchEntityRegistry(this.hass.connection).then((entityRegistry) => {
|
||||
const entity = entityRegistry.find(
|
||||
(reg) =>
|
||||
reg.config_entry_id === configFlowResult.result!.entry_id
|
||||
try {
|
||||
const entry = await this._waitForEntityRegistryUpdate(
|
||||
configFlowResult.result.entry_id
|
||||
);
|
||||
showMoreInfoDialog(parent, { entityId: entry.entity_id });
|
||||
close = false;
|
||||
} catch (err) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (
|
||||
this.helperConfigEntry?.domain === "switch_as_x" &&
|
||||
this._switchAs !== domain
|
||||
) {
|
||||
// change a current switch as x to something else
|
||||
if (
|
||||
await showConfirmationDialog(this, {
|
||||
text:
|
||||
this._switchAs === "switch"
|
||||
? this.hass!.localize(
|
||||
"ui.dialogs.entity_registry.editor.switch_as_x_remove_confirm",
|
||||
{
|
||||
domain: domainToName(
|
||||
this.hass.localize,
|
||||
domain
|
||||
).toLowerCase(),
|
||||
}
|
||||
)
|
||||
: this.hass!.localize(
|
||||
"ui.dialogs.entity_registry.editor.switch_as_x_change_confirm",
|
||||
{
|
||||
domain_1: domainToName(
|
||||
this.hass.localize,
|
||||
domain
|
||||
).toLowerCase(),
|
||||
domain_2: domainToName(
|
||||
this.hass.localize,
|
||||
this._switchAs
|
||||
).toLowerCase(),
|
||||
}
|
||||
),
|
||||
})
|
||||
) {
|
||||
const origEntityId = this.entry.options?.switch_as_x?.entity_id;
|
||||
// remove current helper
|
||||
await deleteConfigEntry(this.hass, this.helperConfigEntry.entry_id);
|
||||
|
||||
if (!origEntityId) {
|
||||
// should not happen, guard for types
|
||||
} else if (this._switchAs === "switch") {
|
||||
// done, original switch is back
|
||||
showMoreInfoDialog(parent, { entityId: origEntityId });
|
||||
close = false;
|
||||
} else {
|
||||
const configFlow = await createConfigFlow(this.hass, "switch_as_x");
|
||||
const configFlowResult = (await handleConfigFlowStep(
|
||||
this.hass,
|
||||
configFlow.flow_id,
|
||||
{
|
||||
entity_id: origEntityId,
|
||||
target_domain: this._switchAs,
|
||||
}
|
||||
)) as DataEntryFlowStepCreateEntry;
|
||||
if (configFlowResult.result?.entry_id) {
|
||||
try {
|
||||
const entry = await this._waitForEntityRegistryUpdate(
|
||||
configFlowResult.result.entry_id
|
||||
);
|
||||
if (!entity) {
|
||||
return;
|
||||
}
|
||||
showMoreInfoDialog(parent, { entityId: entity.entity_id });
|
||||
});
|
||||
}, "entity_registry_updated");
|
||||
showMoreInfoDialog(parent, { entityId: entry.entity_id });
|
||||
close = false;
|
||||
} catch (err) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result.entity_entry;
|
||||
return { close, entry: result.entity_entry };
|
||||
}
|
||||
|
||||
private async _waitForEntityRegistryUpdate(config_entry_id: string) {
|
||||
return new Promise<EntityRegistryEntry>((resolve, reject) => {
|
||||
const timeout = setTimeout(reject, 5000);
|
||||
const unsub = subscribeEntityRegistry(
|
||||
this.hass.connection,
|
||||
(entityRegistry) => {
|
||||
const entity = entityRegistry.find(
|
||||
(reg) => reg.config_entry_id === config_entry_id
|
||||
);
|
||||
if (entity) {
|
||||
clearTimeout(timeout);
|
||||
unsub();
|
||||
resolve(entity);
|
||||
}
|
||||
}
|
||||
);
|
||||
// @ts-ignore Force refresh
|
||||
this.hass.connection._entityRegistry?.refresh();
|
||||
});
|
||||
}
|
||||
|
||||
private _nameChanged(ev): void {
|
||||
@ -1090,7 +1222,11 @@ export class EntityRegistrySettingsEditor extends LitElement {
|
||||
const switchAs = ev.target.value === "outlet" ? "switch" : ev.target.value;
|
||||
this._switchAs = switchAs;
|
||||
|
||||
if (ev.target.value === "outlet" || ev.target.value === "switch") {
|
||||
if (
|
||||
(computeDomain(this.entry.entity_id) === "switch" &&
|
||||
ev.target.value === "outlet") ||
|
||||
ev.target.value === "switch"
|
||||
) {
|
||||
this._deviceClass = ev.target.value;
|
||||
}
|
||||
}
|
||||
@ -1177,9 +1313,9 @@ export class EntityRegistrySettingsEditor extends LitElement {
|
||||
private _switchAsDomainsSorted = memoizeOne(
|
||||
(domains: string[], localize: LocalizeFunc) =>
|
||||
domains
|
||||
.map((entry) => ({
|
||||
domain: entry,
|
||||
label: domainToName(localize, entry),
|
||||
.map((domain) => ({
|
||||
domain,
|
||||
label: domainToName(localize, domain),
|
||||
}))
|
||||
.sort((a, b) =>
|
||||
stringCompare(a.label, b.label, this.hass.locale.language)
|
||||
|
@ -9,7 +9,7 @@ import "../../../components/ha-alert";
|
||||
import {
|
||||
ConfigEntry,
|
||||
deleteConfigEntry,
|
||||
getConfigEntries,
|
||||
getConfigEntry,
|
||||
} from "../../../data/config_entries";
|
||||
import { updateDeviceRegistryEntry } from "../../../data/device_registry";
|
||||
import {
|
||||
@ -21,6 +21,7 @@ import {
|
||||
showAlertDialog,
|
||||
showConfirmationDialog,
|
||||
} from "../../../dialogs/generic/show-dialog-box";
|
||||
import { hideMoreInfoDialog } from "../../../dialogs/more-info/show-ha-more-info-dialog";
|
||||
import { SubscribeMixin } from "../../../mixins/subscribe-mixin";
|
||||
import { haStyle } from "../../../resources/styles";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
@ -48,13 +49,8 @@ export class EntityRegistrySettings extends SubscribeMixin(LitElement) {
|
||||
protected firstUpdated(changedProps: PropertyValues): void {
|
||||
super.firstUpdated(changedProps);
|
||||
if (this.entry.config_entry_id) {
|
||||
getConfigEntries(this.hass, {
|
||||
type: ["helper"],
|
||||
domain: this.entry.platform,
|
||||
}).then((entries) => {
|
||||
this._helperConfigEntry = entries.find(
|
||||
(ent) => ent.entry_id === this.entry.config_entry_id
|
||||
);
|
||||
getConfigEntry(this.hass, this.entry.config_entry_id).then((entry) => {
|
||||
this._helperConfigEntry = entry.config_entry;
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -183,8 +179,10 @@ export class EntityRegistrySettings extends SubscribeMixin(LitElement) {
|
||||
private async _updateEntry(): Promise<void> {
|
||||
this._submitting = true;
|
||||
try {
|
||||
await this._registryEditor!.updateEntry();
|
||||
fireEvent(this, "close-dialog");
|
||||
const result = await this._registryEditor!.updateEntry();
|
||||
if (result.close) {
|
||||
hideMoreInfoDialog(this);
|
||||
}
|
||||
} catch (err: any) {
|
||||
this._error = err.message || "Unknown error";
|
||||
} finally {
|
||||
|
@ -1002,6 +1002,7 @@
|
||||
"visibility_unit": "Visibility unit",
|
||||
"wind_speed_unit": "Wind speed unit",
|
||||
"device_class": "Show as",
|
||||
"switch_as_x": "Show switch as",
|
||||
"device_classes": {
|
||||
"binary_sensor": {
|
||||
"door": "Door",
|
||||
@ -1046,8 +1047,7 @@
|
||||
"shutter": "Shutter"
|
||||
},
|
||||
"switch": {
|
||||
"outlet": "Outlet",
|
||||
"switch": "Switch"
|
||||
"outlet": "Outlet"
|
||||
}
|
||||
},
|
||||
"unavailable": "This entity is unavailable.",
|
||||
@ -1063,6 +1063,8 @@
|
||||
"enable_entity": "Enable",
|
||||
"open_device_settings": "Open device settings",
|
||||
"switch_as_x_confirm": "This switch will be hidden and a new {domain} will be added. Your existing configurations using the switch will continue to work.",
|
||||
"switch_as_x_remove_confirm": "This {domain} will be removed and the original switch will be visible again. Your existing configurations using the {domain} will no longer work!",
|
||||
"switch_as_x_change_confirm": "This {domain_1} will be removed and will be replaced by a new {domain_2}. Your existing configurations using the {domain_1} will no longer work!",
|
||||
"enabled_description": "Disabled entities will not be added to Home Assistant.",
|
||||
"enabled_delay_confirm": "The enabled entities will be added to Home Assistant in {delay} seconds",
|
||||
"enabled_restart_confirm": "Restart Home Assistant to finish enabling the entities",
|
||||
|
Loading…
x
Reference in New Issue
Block a user