mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-21 08:16:36 +00:00
Allow to change device names during config flow (#25142)
* Allow to change device names during config flow * Update zha-device-card.ts --------- Co-authored-by: Simon Lamon <32477463+silamon@users.noreply.github.com>
This commit is contained in:
parent
0229f67751
commit
221e1d9ed8
@ -1,4 +1,4 @@
|
|||||||
import { html } from "lit";
|
import { html, nothing } from "lit";
|
||||||
import {
|
import {
|
||||||
createConfigFlow,
|
createConfigFlow,
|
||||||
deleteConfigFlow,
|
deleteConfigFlow,
|
||||||
@ -194,13 +194,7 @@ export const showConfigFlowDialog = (
|
|||||||
.content=${description}
|
.content=${description}
|
||||||
></ha-markdown>
|
></ha-markdown>
|
||||||
`
|
`
|
||||||
: ""}
|
: nothing}
|
||||||
<p>
|
|
||||||
${hass.localize(
|
|
||||||
"ui.panel.config.integrations.config_flow.created_config",
|
|
||||||
{ name: step.title }
|
|
||||||
)}
|
|
||||||
</p>
|
|
||||||
`;
|
`;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { html } from "lit";
|
import { html, nothing } from "lit";
|
||||||
import type { ConfigEntry } from "../../data/config_entries";
|
import type { ConfigEntry } from "../../data/config_entries";
|
||||||
import { domainToName } from "../../data/integration";
|
import { domainToName } from "../../data/integration";
|
||||||
import {
|
import {
|
||||||
@ -200,13 +200,7 @@ export const showSubConfigFlowDialog = (
|
|||||||
.content=${description}
|
.content=${description}
|
||||||
></ha-markdown>
|
></ha-markdown>
|
||||||
`
|
`
|
||||||
: ""}
|
: nothing}
|
||||||
<p>
|
|
||||||
${hass.localize(
|
|
||||||
"ui.panel.config.integrations.config_flow.created_config",
|
|
||||||
{ name: step.title }
|
|
||||||
)}
|
|
||||||
</p>
|
|
||||||
`;
|
`;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
import "@material/mwc-button";
|
import "@material/mwc-button";
|
||||||
import type { CSSResultGroup, PropertyValues, TemplateResult } from "lit";
|
import type { CSSResultGroup, PropertyValues, TemplateResult } from "lit";
|
||||||
import { css, html, LitElement, nothing } from "lit";
|
import { css, html, LitElement, nothing } from "lit";
|
||||||
import { customElement, property } 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";
|
||||||
import { computeDeviceNameDisplay } from "../../common/entity/compute_device_name";
|
import {
|
||||||
|
computeDeviceName,
|
||||||
|
computeDeviceNameDisplay,
|
||||||
|
} from "../../common/entity/compute_device_name";
|
||||||
import { computeDomain } from "../../common/entity/compute_domain";
|
import { computeDomain } from "../../common/entity/compute_domain";
|
||||||
import { navigate } from "../../common/navigate";
|
import { navigate } from "../../common/navigate";
|
||||||
import "../../components/ha-area-picker";
|
import "../../components/ha-area-picker";
|
||||||
@ -18,6 +21,8 @@ import { showAlertDialog } from "../generic/show-dialog-box";
|
|||||||
import { showVoiceAssistantSetupDialog } from "../voice-assistant-setup/show-voice-assistant-setup-dialog";
|
import { showVoiceAssistantSetupDialog } from "../voice-assistant-setup/show-voice-assistant-setup-dialog";
|
||||||
import type { FlowConfig } from "./show-dialog-data-entry-flow";
|
import type { FlowConfig } from "./show-dialog-data-entry-flow";
|
||||||
import { configFlowContentStyles } from "./styles";
|
import { configFlowContentStyles } from "./styles";
|
||||||
|
import { brandsUrl } from "../../util/brands-url";
|
||||||
|
import { domainToName } from "../../data/integration";
|
||||||
|
|
||||||
@customElement("step-flow-create-entry")
|
@customElement("step-flow-create-entry")
|
||||||
class StepFlowCreateEntry extends LitElement {
|
class StepFlowCreateEntry extends LitElement {
|
||||||
@ -27,7 +32,12 @@ class StepFlowCreateEntry extends LitElement {
|
|||||||
|
|
||||||
@property({ attribute: false }) public step!: DataEntryFlowStepCreateEntry;
|
@property({ attribute: false }) public step!: DataEntryFlowStepCreateEntry;
|
||||||
|
|
||||||
navigateToResult = false;
|
public navigateToResult = false;
|
||||||
|
|
||||||
|
@state() private _deviceUpdate: Record<
|
||||||
|
string,
|
||||||
|
{ name?: string; area?: string }
|
||||||
|
> = {};
|
||||||
|
|
||||||
private _devices = memoizeOne(
|
private _devices = memoizeOne(
|
||||||
(
|
(
|
||||||
@ -99,7 +109,13 @@ class StepFlowCreateEntry extends LitElement {
|
|||||||
this.step.result?.entry_id
|
this.step.result?.entry_id
|
||||||
);
|
);
|
||||||
return html`
|
return html`
|
||||||
<h2>${localize("ui.panel.config.integrations.config_flow.success")}!</h2>
|
<h2>
|
||||||
|
${devices.length
|
||||||
|
? localize("ui.panel.config.integrations.config_flow.assign_area", {
|
||||||
|
number: devices.length,
|
||||||
|
})
|
||||||
|
: `${localize("ui.panel.config.integrations.config_flow.success")}!`}
|
||||||
|
</h2>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
${this.flowConfig.renderCreateEntryDescription(this.hass, this.step)}
|
${this.flowConfig.renderCreateEntryDescription(this.hass, this.step)}
|
||||||
${this.step.result?.state === "not_loaded"
|
${this.step.result?.state === "not_loaded"
|
||||||
@ -110,31 +126,62 @@ class StepFlowCreateEntry extends LitElement {
|
|||||||
>`
|
>`
|
||||||
: nothing}
|
: nothing}
|
||||||
${devices.length === 0
|
${devices.length === 0
|
||||||
? nothing
|
? html`<p>
|
||||||
: html`
|
|
||||||
<p>
|
|
||||||
${localize(
|
${localize(
|
||||||
"ui.panel.config.integrations.config_flow.found_following_devices"
|
"ui.panel.config.integrations.config_flow.created_config",
|
||||||
)}:
|
{ name: this.step.title }
|
||||||
</p>
|
)}
|
||||||
|
</p>`
|
||||||
|
: html`
|
||||||
<div class="devices">
|
<div class="devices">
|
||||||
${devices.map(
|
${devices.map(
|
||||||
(device) => html`
|
(device) => html`
|
||||||
<div class="device">
|
<div class="device">
|
||||||
<div>
|
<div class="device-info">
|
||||||
<b>${computeDeviceNameDisplay(device, this.hass)}</b
|
${this.step.result?.domain
|
||||||
><br />
|
? html`<img
|
||||||
${!device.model && !device.manufacturer
|
slot="graphic"
|
||||||
? html` `
|
alt=${domainToName(
|
||||||
: html`${device.model}
|
this.hass.localize,
|
||||||
${device.manufacturer
|
this.step.result.domain
|
||||||
? html`(${device.manufacturer})`
|
)}
|
||||||
: ""}`}
|
src=${brandsUrl({
|
||||||
|
domain: this.step.result.domain,
|
||||||
|
type: "icon",
|
||||||
|
darkOptimized: this.hass.themes?.darkMode,
|
||||||
|
})}
|
||||||
|
crossorigin="anonymous"
|
||||||
|
referrerpolicy="no-referrer"
|
||||||
|
/>`
|
||||||
|
: nothing}
|
||||||
|
<div class="device-info-details">
|
||||||
|
<span>${device.model || device.manufacturer}</span>
|
||||||
|
${device.model
|
||||||
|
? html`<span class="secondary">
|
||||||
|
${device.manufacturer}
|
||||||
|
</span>`
|
||||||
|
: nothing}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
<ha-textfield
|
||||||
|
.label=${localize(
|
||||||
|
"ui.panel.config.integrations.config_flow.device_name"
|
||||||
|
)}
|
||||||
|
.placeholder=${computeDeviceNameDisplay(
|
||||||
|
device,
|
||||||
|
this.hass
|
||||||
|
)}
|
||||||
|
.value=${this._deviceUpdate[device.id]?.name ??
|
||||||
|
computeDeviceName(device)}
|
||||||
|
@change=${this._deviceNameChanged}
|
||||||
|
.device=${device.id}
|
||||||
|
></ha-textfield>
|
||||||
<ha-area-picker
|
<ha-area-picker
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.device=${device.id}
|
.device=${device.id}
|
||||||
.value=${device.area_id ?? undefined}
|
.value=${this._deviceUpdate[device.id]?.area ??
|
||||||
|
device.area_id ??
|
||||||
|
undefined}
|
||||||
@value-changed=${this._areaPicked}
|
@value-changed=${this._areaPicked}
|
||||||
></ha-area-picker>
|
></ha-area-picker>
|
||||||
</div>
|
</div>
|
||||||
@ -146,14 +193,32 @@ class StepFlowCreateEntry extends LitElement {
|
|||||||
<div class="buttons">
|
<div class="buttons">
|
||||||
<mwc-button @click=${this._flowDone}
|
<mwc-button @click=${this._flowDone}
|
||||||
>${localize(
|
>${localize(
|
||||||
"ui.panel.config.integrations.config_flow.finish"
|
`ui.panel.config.integrations.config_flow.${!devices.length || Object.keys(this._deviceUpdate).length ? "finish" : "finish_skip"}`
|
||||||
)}</mwc-button
|
)}</mwc-button
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _flowDone(): void {
|
private async _flowDone(): Promise<void> {
|
||||||
|
if (Object.keys(this._deviceUpdate).length) {
|
||||||
|
await Promise.all(
|
||||||
|
Object.entries(this._deviceUpdate).map(([deviceId, update]) =>
|
||||||
|
updateDeviceRegistryEntry(this.hass, deviceId, {
|
||||||
|
name_by_user: update.name,
|
||||||
|
area_id: update.area,
|
||||||
|
}).catch((err: any) => {
|
||||||
|
showAlertDialog(this, {
|
||||||
|
text: this.hass.localize(
|
||||||
|
"ui.panel.config.integrations.config_flow.error_saving_device",
|
||||||
|
{ error: err.message }
|
||||||
|
),
|
||||||
|
});
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
fireEvent(this, "flow-update", { step: undefined });
|
fireEvent(this, "flow-update", { step: undefined });
|
||||||
if (this.step.result && this.navigateToResult) {
|
if (this.step.result && this.navigateToResult) {
|
||||||
navigate(
|
navigate(
|
||||||
@ -165,21 +230,25 @@ class StepFlowCreateEntry extends LitElement {
|
|||||||
private async _areaPicked(ev: CustomEvent) {
|
private async _areaPicked(ev: CustomEvent) {
|
||||||
const picker = ev.currentTarget as any;
|
const picker = ev.currentTarget as any;
|
||||||
const device = picker.device;
|
const device = picker.device;
|
||||||
|
|
||||||
const area = ev.detail.value;
|
const area = ev.detail.value;
|
||||||
try {
|
|
||||||
await updateDeviceRegistryEntry(this.hass, device, {
|
if (!(device in this._deviceUpdate)) {
|
||||||
area_id: area,
|
this._deviceUpdate[device] = {};
|
||||||
});
|
|
||||||
} catch (err: any) {
|
|
||||||
showAlertDialog(this, {
|
|
||||||
text: this.hass.localize(
|
|
||||||
"ui.panel.config.integrations.config_flow.error_saving_area",
|
|
||||||
{ error: err.message }
|
|
||||||
),
|
|
||||||
});
|
|
||||||
picker.value = null;
|
|
||||||
}
|
}
|
||||||
|
this._deviceUpdate[device].area = area;
|
||||||
|
this.requestUpdate("_deviceUpdate");
|
||||||
|
}
|
||||||
|
|
||||||
|
private _deviceNameChanged(ev): void {
|
||||||
|
const picker = ev.currentTarget as any;
|
||||||
|
const device = picker.device;
|
||||||
|
const name = picker.value;
|
||||||
|
|
||||||
|
if (!(device in this._deviceUpdate)) {
|
||||||
|
this._deviceUpdate[device] = {};
|
||||||
|
}
|
||||||
|
this._deviceUpdate[device].name = name;
|
||||||
|
this.requestUpdate("_deviceUpdate");
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
static get styles(): CSSResultGroup {
|
||||||
@ -188,18 +257,41 @@ class StepFlowCreateEntry extends LitElement {
|
|||||||
css`
|
css`
|
||||||
.devices {
|
.devices {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
|
||||||
margin: -4px;
|
margin: -4px;
|
||||||
max-height: 600px;
|
max-height: 600px;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
.device {
|
.device {
|
||||||
border: 1px solid var(--divider-color);
|
border: 1px solid var(--divider-color);
|
||||||
padding: 5px;
|
padding: 6px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
margin: 4px;
|
margin: 4px;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
width: 250px;
|
}
|
||||||
|
.device-info {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
.device-info img {
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
}
|
||||||
|
.device-info-details {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
.secondary {
|
||||||
|
color: var(--secondary-text-color);
|
||||||
|
}
|
||||||
|
ha-textfield,
|
||||||
|
ha-area-picker {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
ha-textfield {
|
||||||
|
margin: 8px 0;
|
||||||
}
|
}
|
||||||
.buttons > *:last-child {
|
.buttons > *:last-child {
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
|
@ -189,7 +189,7 @@ class ZHADeviceCard extends SubscribeMixin(LitElement) {
|
|||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
showAlertDialog(this, {
|
showAlertDialog(this, {
|
||||||
text: this.hass.localize(
|
text: this.hass.localize(
|
||||||
"ui.panel.config.integrations.config_flow.error_saving_area",
|
"ui.panel.config.integrations.config_flow.error_saving_device",
|
||||||
{ error: err.message }
|
{ error: err.message }
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
|
@ -5304,19 +5304,21 @@
|
|||||||
},
|
},
|
||||||
"config_flow": {
|
"config_flow": {
|
||||||
"success": "Success",
|
"success": "Success",
|
||||||
|
"assign_area": "Assign {number, plural,\n one {device}\n other {devices}\n} to area",
|
||||||
|
"device_name": "Device name",
|
||||||
"aborted": "Aborted",
|
"aborted": "Aborted",
|
||||||
"close": "Close",
|
"close": "Close",
|
||||||
"finish": "Finish",
|
"finish": "Finish",
|
||||||
|
"finish_skip": "Skip and finish",
|
||||||
"submit": "Submit",
|
"submit": "Submit",
|
||||||
"next": "Next",
|
"next": "Next",
|
||||||
"preview": "Preview",
|
"preview": "Preview",
|
||||||
"found_following_devices": "We found the following devices",
|
|
||||||
"yaml_only_title": "This integration cannot be added from the UI",
|
"yaml_only_title": "This integration cannot be added from the UI",
|
||||||
"yaml_only": "You can add this integration by adding it to your ''configuration.yaml''. See the documentation for more information.",
|
"yaml_only": "You can add this integration by adding it to your ''configuration.yaml''. See the documentation for more information.",
|
||||||
"open_documentation": "Open documentation",
|
"open_documentation": "Open documentation",
|
||||||
"no_config_flow": "This integration does not support configuration via the UI. If you followed this link from the Home Assistant website, make sure you run the latest version of Home Assistant.",
|
"no_config_flow": "This integration does not support configuration via the UI. If you followed this link from the Home Assistant website, make sure you run the latest version of Home Assistant.",
|
||||||
"not_all_required_fields": "Not all required fields are filled in.",
|
"not_all_required_fields": "Not all required fields are filled in.",
|
||||||
"error_saving_area": "Error saving area: {error}",
|
"error_saving_device": "Error updating device: {error}",
|
||||||
"created_config": "Created configuration for {name}.",
|
"created_config": "Created configuration for {name}.",
|
||||||
"external_step": {
|
"external_step": {
|
||||||
"description": "This step requires you to visit an external website to be completed.",
|
"description": "This step requires you to visit an external website to be completed.",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user