diff --git a/src/data/device_registry.ts b/src/data/device_registry.ts index 28161f0e91..e137ef8ff9 100644 --- a/src/data/device_registry.ts +++ b/src/data/device_registry.ts @@ -16,8 +16,8 @@ export interface DeviceRegistryEntry { } export interface DeviceRegistryEntryMutableParams { - area_id?: string; - name_by_user?: string; + area_id?: string | null; + name_by_user?: string | null; } export const updateDeviceRegistryEntry = ( diff --git a/src/panels/config/integrations/dialog-device-registry-detail.ts b/src/panels/config/integrations/dialog-device-registry-detail.ts new file mode 100644 index 0000000000..06da57b870 --- /dev/null +++ b/src/panels/config/integrations/dialog-device-registry-detail.ts @@ -0,0 +1,203 @@ +import { + LitElement, + html, + css, + CSSResult, + TemplateResult, + customElement, + property, +} from "lit-element"; +import "@polymer/paper-dialog-scrollable/paper-dialog-scrollable"; +import "@polymer/paper-input/paper-input"; +import "@polymer/paper-listbox/paper-listbox"; +import "@polymer/paper-dropdown-menu/paper-dropdown-menu"; +import "@polymer/paper-item/paper-item"; +import "@material/mwc-button/mwc-button"; + +import "../../../components/dialog/ha-paper-dialog"; + +import { DeviceRegistryDetailDialogParams } from "./show-dialog-device-registry-detail"; +import { PolymerChangedEvent } from "../../../polymer-types"; +import { haStyleDialog } from "../../../resources/styles"; +import { HomeAssistant } from "../../../types"; +import { + subscribeAreaRegistry, + AreaRegistryEntry, +} from "../../../data/area_registry"; + +@customElement("dialog-device-registry-detail") +class DialogDeviceRegistryDetail extends LitElement { + @property() public hass!: HomeAssistant; + + @property() private _nameByUser!: string; + @property() private _error?: string; + @property() private _params?: DeviceRegistryDetailDialogParams; + @property() private _areas?: AreaRegistryEntry[]; + @property() private _areaId?: string; + + private _submitting?: boolean; + private _unsubAreas?: any; + + public async showDialog( + params: DeviceRegistryDetailDialogParams + ): Promise { + this._params = params; + this._error = undefined; + this._nameByUser = this._params.device.name_by_user || ""; + this._areaId = this._params.device.area_id; + await this.updateComplete; + } + + public connectedCallback() { + super.connectedCallback(); + this._unsubAreas = subscribeAreaRegistry(this.hass.connection, (areas) => { + this._areas = areas; + }); + } + + public disconnectedCallback() { + super.disconnectedCallback(); + if (this._unsubAreas) { + this._unsubAreas(); + } + } + + protected render(): TemplateResult | void { + if (!this._params) { + return html``; + } + const device = this._params.device; + + return html` + +

${device.name}

+ + ${this._error + ? html` +
${this._error}
+ ` + : ""} +
+ +
+ + + + ${this.hass.localize( + "ui.panel.config.integrations.config_entry.no_area" + )} + + ${this._renderAreas()} + + +
+
+
+
+ + ${this.hass.localize( + "ui.panel.config.entity_registry.editor.update" + )} + +
+
+ `; + } + + private _nameChanged(ev: PolymerChangedEvent): void { + this._error = undefined; + this._nameByUser = ev.detail.value; + } + + private _renderAreas() { + if (!this._areas) { + return; + } + return this._areas!.map( + (area) => html` + ${area.name} + ` + ); + } + + private _computeSelectedArea() { + if (!this._params || !this._areas) { + return -1; + } + const device = this._params!.device; + if (!device.area_id) { + return 0; + } + // +1 because of "No Area" entry + return this._areas.findIndex((area) => area.area_id === device.area_id) + 1; + } + + private _areaIndexChanged(event): void { + const selectedAreaIdx = event.target!.selected; + this._areaId = + selectedAreaIdx < 1 + ? undefined + : this._areas![selectedAreaIdx - 1].area_id; + } + + private async _updateEntry(): Promise { + this._submitting = true; + try { + await this._params!.updateEntry({ + name_by_user: this._nameByUser.trim() || null, + area_id: this._areaId || null, + }); + this._params = undefined; + } catch (err) { + this._error = err.message || "Unknown error"; + } finally { + this._submitting = false; + } + } + + private _openedChanged(ev: PolymerChangedEvent): void { + if (!(ev.detail as any).value) { + this._params = undefined; + } + } + + static get styles(): CSSResult[] { + return [ + haStyleDialog, + css` + ha-paper-dialog { + min-width: 400px; + } + .form { + padding-bottom: 24px; + } + mwc-button.warning { + margin-right: auto; + } + .error { + color: var(--google-red-500); + } + `, + ]; + } +} + +declare global { + interface HTMLElementTagNameMap { + "dialog-device-registry-detail": DialogDeviceRegistryDetail; + } +} diff --git a/src/panels/config/integrations/ha-device-card.js b/src/panels/config/integrations/ha-device-card.js index b886334707..345274d0cd 100644 --- a/src/panels/config/integrations/ha-device-card.js +++ b/src/panels/config/integrations/ha-device-card.js @@ -14,7 +14,16 @@ import LocalizeMixin from "../../../mixins/localize-mixin"; import computeStateName from "../../../common/entity/compute_state_name"; import "../../../components/entity/state-badge"; import { compare } from "../../../common/string/compare"; -import { updateDeviceRegistryEntry } from "../../../data/device_registry"; +import { + subscribeDeviceRegistry, + updateDeviceRegistryEntry, +} from "../../../data/device_registry"; +import { subscribeAreaRegistry } from "../../../data/area_registry"; + +import { + showDeviceRegistryDetailDialog, + loadDeviceRegistryDetailDialog, +} from "./show-dialog-device-registry-detail"; function computeEntityName(hass, entity) { if (entity.name) return entity.name; @@ -38,6 +47,13 @@ class HaDeviceCard extends EventsMixin(LocalizeMixin(PolymerElement)) { padding-bottom: 10px; min-width: 0; } + .card-header { + display: flex; + justify-content: space-between; + } + .card-header .name { + width: 90%; + } .device { width: 30%; } @@ -45,9 +61,13 @@ class HaDeviceCard extends EventsMixin(LocalizeMixin(PolymerElement)) { font-weight: bold; } .device .model, - .device .manuf { + .device .manuf, + .device .area { color: var(--secondary-text-color); } + .area .extra-info .name { + color: var(--primary-text-color); + } .extra-info { margin-top: 8px; } @@ -57,39 +77,34 @@ class HaDeviceCard extends EventsMixin(LocalizeMixin(PolymerElement)) { padding-bottom: 4px; } .manuf, - .entity-id { + .entity-id, + .area { color: var(--secondary-text-color); } - + +
+
[[_deviceName(device)]]
+ +
-
[[device.model]]
[[localize('ui.panel.config.integrations.config_entry.manuf', 'manufacturer', device.manufacturer)]]
-
- - - - [[localize('ui.panel.config.integrations.config_entry.no_area')]] - - - - -
+