From a2a039ebc5dc05253e5337f20eecbaf284bdeb9b Mon Sep 17 00:00:00 2001 From: "David F. Mulcahey" Date: Fri, 17 Jan 2020 10:39:57 -0500 Subject: [PATCH] Add group binding to the ZHA config panel and misc. cleanup (#4466) * clean up zha device card and usage * group binding tile * add cluster selection to group binding tile * fix css class name * fix filtering * multiselect for clusters in group binding * pass narrow to cluster table * fix tables * fix device page * address remaing comments from previous PR * fix bad cherry-pick * css cleanup * consistency * use properties * translations * add confirmation dialog to remove button * fix css * review comments * remove noise --- src/data/zha.ts | 26 ++ .../dialog-zha-device-info.ts | 3 +- src/panels/config/zha/functions.ts | 8 +- src/panels/config/zha/zha-add-devices-page.ts | 2 +- src/panels/config/zha/zha-add-group-page.ts | 5 +- .../config/zha/zha-clusters-data-table.ts | 92 +++++ src/panels/config/zha/zha-clusters.ts | 8 +- src/panels/config/zha/zha-config-dashboard.ts | 2 +- .../{zha-binding.ts => zha-device-binding.ts} | 10 +- src/panels/config/zha/zha-device-card.ts | 90 ++--- src/panels/config/zha/zha-device-page.ts | 45 ++- src/panels/config/zha/zha-group-binding.ts | 321 ++++++++++++++++++ src/panels/config/zha/zha-group-page.ts | 12 +- src/panels/config/zha/zha-groups-dashboard.ts | 9 +- .../config/zha/zha-groups-data-table.ts | 18 +- src/panels/config/zha/zha-node.ts | 32 +- src/translations/en.json | 14 + 17 files changed, 614 insertions(+), 83 deletions(-) create mode 100644 src/panels/config/zha/zha-clusters-data-table.ts rename src/panels/config/zha/{zha-binding.ts => zha-device-binding.ts} (95%) create mode 100644 src/panels/config/zha/zha-group-binding.ts diff --git a/src/data/zha.ts b/src/data/zha.ts index d0d57d38d8..1ef0f54ff6 100644 --- a/src/data/zha.ts +++ b/src/data/zha.ts @@ -126,6 +126,32 @@ export const unbindDevices = ( target_ieee: targetIEEE, }); +export const bindDeviceToGroup = ( + hass: HomeAssistant, + deviceIEEE: string, + groupId: number, + clusters: Cluster[] +): Promise => + hass.callWS({ + type: "zha/groups/bind", + source_ieee: deviceIEEE, + group_id: groupId, + bindings: clusters, + }); + +export const unbindDeviceFromGroup = ( + hass: HomeAssistant, + deviceIEEE: string, + groupId: number, + clusters: Cluster[] +): Promise => + hass.callWS({ + type: "zha/groups/unbind", + source_ieee: deviceIEEE, + group_id: groupId, + bindings: clusters, + }); + export const readAttributeValue = ( hass: HomeAssistant, data: ReadAttributeServiceData diff --git a/src/dialogs/zha-device-info-dialog/dialog-zha-device-info.ts b/src/dialogs/zha-device-info-dialog/dialog-zha-device-info.ts index 6d4abc4e66..436386e52e 100644 --- a/src/dialogs/zha-device-info-dialog/dialog-zha-device-info.ts +++ b/src/dialogs/zha-device-info-dialog/dialog-zha-device-info.ts @@ -54,9 +54,8 @@ class DialogZHADeviceInfo extends LitElement { class="card" .hass=${this.hass} .device=${this._device} - showActions - isJoinPage @zha-device-removed=${this._onDeviceRemoved} + .showEntityDetail=${false} > `} diff --git a/src/panels/config/zha/functions.ts b/src/panels/config/zha/functions.ts index 1e7a2858de..065eb9867e 100644 --- a/src/panels/config/zha/functions.ts +++ b/src/panels/config/zha/functions.ts @@ -1,4 +1,4 @@ -import { ZHADevice, ZHAGroup } from "../../../data/zha"; +import { ZHADevice, ZHAGroup, Cluster } from "../../../data/zha"; export const formatAsPaddedHex = (value: string | number): string => { let hex = value; @@ -19,3 +19,9 @@ export const sortZHAGroups = (a: ZHAGroup, b: ZHAGroup): number => { const nameb = b.name; return nameA.localeCompare(nameb); }; + +export const computeClusterKey = (cluster: Cluster): string => { + return `${cluster.name} (Endpoint id: ${ + cluster.endpoint_id + }, Id: ${formatAsPaddedHex(cluster.id)}, Type: ${cluster.type})`; +}; diff --git a/src/panels/config/zha/zha-add-devices-page.ts b/src/panels/config/zha/zha-add-devices-page.ts index 04e7532518..d08484bbc4 100644 --- a/src/panels/config/zha/zha-add-devices-page.ts +++ b/src/panels/config/zha/zha-add-devices-page.ts @@ -120,7 +120,7 @@ class ZHAAddDevicesPage extends LitElement { .narrow=${!this.isWide} .showHelp=${this._showHelp} .showActions=${!this._active} - isJoinPage + .showEntityDetail=${false} > ` )} diff --git a/src/panels/config/zha/zha-add-group-page.ts b/src/panels/config/zha/zha-add-group-page.ts index 076cddce50..6ce4e71f3e 100644 --- a/src/panels/config/zha/zha-add-group-page.ts +++ b/src/panels/config/zha/zha-add-group-page.ts @@ -117,7 +117,10 @@ export class ZHAAddGroupPage extends LitElement { private _handleAddSelectionChanged(ev: CustomEvent): void { const changedSelection = ev.detail as SelectionChangedEvent; const entity = changedSelection.id; - if (changedSelection.selected) { + if ( + changedSelection.selected && + !this._selectedDevicesToAdd.includes(entity) + ) { this._selectedDevicesToAdd.push(entity); } else { const index = this._selectedDevicesToAdd.indexOf(entity); diff --git a/src/panels/config/zha/zha-clusters-data-table.ts b/src/panels/config/zha/zha-clusters-data-table.ts new file mode 100644 index 0000000000..db28edad38 --- /dev/null +++ b/src/panels/config/zha/zha-clusters-data-table.ts @@ -0,0 +1,92 @@ +import "../../../components/data-table/ha-data-table"; +import "../../../components/entity/ha-state-icon"; + +import memoizeOne from "memoize-one"; + +import { + LitElement, + html, + TemplateResult, + property, + customElement, +} from "lit-element"; +import { HomeAssistant } from "../../../types"; +// tslint:disable-next-line +import { DataTableColumnContainer } from "../../../components/data-table/ha-data-table"; +// tslint:disable-next-line +import { Cluster } from "../../../data/zha"; +import { formatAsPaddedHex } from "./functions"; + +export interface ClusterRowData extends Cluster { + cluster?: Cluster; + cluster_id?: string; +} + +@customElement("zha-clusters-data-table") +export class ZHAClustersDataTable extends LitElement { + @property() public hass!: HomeAssistant; + @property() public narrow = false; + @property() public clusters: Cluster[] = []; + + private _clusters = memoizeOne((clusters: Cluster[]) => { + let outputClusters: ClusterRowData[] = clusters; + + outputClusters = outputClusters.map((cluster) => { + return { + ...cluster, + cluster_id: cluster.endpoint_id + "-" + cluster.id, + }; + }); + + return outputClusters; + }); + + private _columns = memoizeOne( + (narrow: boolean): DataTableColumnContainer => + narrow + ? { + name: { + title: "Name", + sortable: true, + direction: "asc", + }, + } + : { + name: { + title: "Name", + sortable: true, + direction: "asc", + }, + id: { + title: "ID", + template: (id: number) => { + return html` + ${formatAsPaddedHex(id)} + `; + }, + sortable: true, + }, + endpoint_id: { + title: "Endpoint ID", + sortable: true, + }, + } + ); + + protected render(): TemplateResult { + return html` + + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + "zha-clusters-data-table": ZHAClustersDataTable; + } +} diff --git a/src/panels/config/zha/zha-clusters.ts b/src/panels/config/zha/zha-clusters.ts index 5b3f6d56f7..999816e772 100644 --- a/src/panels/config/zha/zha-clusters.ts +++ b/src/panels/config/zha/zha-clusters.ts @@ -21,7 +21,7 @@ import { fireEvent } from "../../../common/dom/fire_event"; import { Cluster, fetchClustersForZhaNode, ZHADevice } from "../../../data/zha"; import { haStyle } from "../../../resources/styles"; import { HomeAssistant } from "../../../types"; -import { formatAsPaddedHex } from "./functions"; +import { computeClusterKey } from "./functions"; import { ItemSelectedEvent } from "./types"; declare global { @@ -33,12 +33,6 @@ declare global { } } -const computeClusterKey = (cluster: Cluster): string => { - return `${cluster.name} (Endpoint id: ${ - cluster.endpoint_id - }, Id: ${formatAsPaddedHex(cluster.id)}, Type: ${cluster.type})`; -}; - export class ZHAClusters extends LitElement { @property() public hass?: HomeAssistant; @property() public isWide?: boolean; diff --git a/src/panels/config/zha/zha-config-dashboard.ts b/src/panels/config/zha/zha-config-dashboard.ts index dcf8ff9870..4c86d128e8 100644 --- a/src/panels/config/zha/zha-config-dashboard.ts +++ b/src/panels/config/zha/zha-config-dashboard.ts @@ -48,7 +48,6 @@ class ZHAConfigDashboard extends LitElement { ...device, name: device.user_given_name ? device.user_given_name : device.name, nwk: formatAsPaddedHex(device.nwk), - id: device.ieee, }; }); @@ -142,6 +141,7 @@ class ZHAConfigDashboard extends LitElement { .columns=${this._columns(this.narrow)} .data=${this._memoizeDevices(this._devices)} @row-click=${this._handleDeviceClicked} + .id=${"ieee"} > diff --git a/src/panels/config/zha/zha-binding.ts b/src/panels/config/zha/zha-device-binding.ts similarity index 95% rename from src/panels/config/zha/zha-binding.ts rename to src/panels/config/zha/zha-device-binding.ts index 06a3d1b2dc..fa4fe4d8a9 100644 --- a/src/panels/config/zha/zha-binding.ts +++ b/src/panels/config/zha/zha-device-binding.ts @@ -24,8 +24,8 @@ import { HomeAssistant } from "../../../types"; import { ItemSelectedEvent } from "./types"; import "@polymer/paper-item/paper-item"; -@customElement("zha-binding-control") -export class ZHABindingControl extends LitElement { +@customElement("zha-device-binding-control") +export class ZHADeviceBindingControl extends LitElement { @property() public hass?: HomeAssistant; @property() public isWide?: boolean; @property() public selectedDevice?: ZHADevice; @@ -175,7 +175,9 @@ export class ZHABindingControl extends LitElement { .helpText { color: grey; - padding: 16px; + padding-left: 28px; + padding-right: 28px; + padding-bottom: 10px; } .header { @@ -204,6 +206,6 @@ export class ZHABindingControl extends LitElement { declare global { interface HTMLElementTagNameMap { - "zha-binding-control": ZHABindingControl; + "zha-device-binding-control": ZHADeviceBindingControl; } } diff --git a/src/panels/config/zha/zha-device-card.ts b/src/panels/config/zha/zha-device-card.ts index ef96c070dc..1e42de95e2 100644 --- a/src/panels/config/zha/zha-device-card.ts +++ b/src/panels/config/zha/zha-device-card.ts @@ -58,8 +58,11 @@ class ZHADeviceCard extends LitElement { @property() public device?: ZHADevice; @property({ type: Boolean }) public narrow?: boolean; @property({ type: Boolean }) public showHelp?: boolean = false; - @property({ type: Boolean }) public showActions?: boolean; - @property({ type: Boolean }) public isJoinPage?: boolean; + @property({ type: Boolean }) public showActions?: boolean = true; + @property({ type: Boolean }) public showName?: boolean = true; + @property({ type: Boolean }) public showEntityDetail?: boolean = true; + @property({ type: Boolean }) public showModelInfo?: boolean = true; + @property({ type: Boolean }) public showEditableInfo?: boolean = true; @property() private _serviceData?: NodeServiceData; @property() private _areas: AreaRegistryEntry[] = []; @property() private _selectedAreaIndex: number = -1; @@ -137,9 +140,9 @@ class ZHADeviceCard extends LitElement { protected render(): TemplateResult | void { return html` - + ${ - this.isJoinPage + this.showModelInfo ? html`
${this.device!.model}
@@ -202,7 +205,7 @@ class ZHADeviceCard extends LitElement { .stateObj="${this.hass!.states[entity.entity_id]}" slot="item-icon" > - ${!this.isJoinPage + ${this.showEntityDetail ? html`
@@ -218,40 +221,48 @@ class ZHADeviceCard extends LitElement { ` )}
-
- -
-
- - - - ${this.hass!.localize("ui.dialogs.zha_device_info.no_area")} - + ${ + this.showEditableInfo + ? html` +
+ +
+
+ + + + ${this.hass!.localize( + "ui.dialogs.zha_device_info.no_area" + )} + - ${this._areas.map( - (entry) => html` - ${entry.name} - ` - )} - - -
+ ${this._areas.map( + (entry) => html` + ${entry.name} + ` + )} +
+
+
+ ` + : "" + } ${ this.showActions ? html` @@ -275,6 +286,9 @@ class ZHADeviceCard extends LitElement { .hass="${this.hass}" domain="zha" service="remove" + .confirmation=${this.hass!.localize( + "ui.dialogs.zha_device_info.confirmations.remove" + )} .serviceData="${this._serviceData}" > ${this.hass!.localize( diff --git a/src/panels/config/zha/zha-device-page.ts b/src/panels/config/zha/zha-device-page.ts index 8813f6526b..9aa07a82b2 100755 --- a/src/panels/config/zha/zha-device-page.ts +++ b/src/panels/config/zha/zha-device-page.ts @@ -1,6 +1,7 @@ import "../../../layouts/hass-subpage"; import "../../../components/ha-paper-icon-button-arrow-prev"; -import "./zha-binding"; +import "./zha-device-binding"; +import "./zha-group-binding"; import "./zha-cluster-attributes"; import "./zha-cluster-commands"; import "./zha-clusters"; @@ -24,10 +25,12 @@ import { fetchBindableDevices, ZHADevice, fetchZHADevice, + ZHAGroup, + fetchGroups, } from "../../../data/zha"; import { haStyle } from "../../../resources/styles"; import { HomeAssistant } from "../../../types"; -import { sortZHADevices } from "./functions"; +import { sortZHADevices, sortZHAGroups } from "./functions"; import { ZHAClusterSelectedParams } from "./types"; @customElement("zha-device-page") @@ -36,8 +39,27 @@ export class ZHADevicePage extends LitElement { @property() public isWide?: boolean; @property() public ieee?: string; @property() public device?: ZHADevice; + @property() public narrow?: boolean; @property() private _selectedCluster?: Cluster; @property() private _bindableDevices: ZHADevice[] = []; + @property() private _groups: ZHAGroup[] = []; + + private _firstUpdatedCalled: boolean = false; + + public connectedCallback(): void { + super.connectedCallback(); + if (this.hass && this._firstUpdatedCalled) { + this._fetchGroups(); + } + } + + protected firstUpdated(changedProperties: PropertyValues): void { + super.firstUpdated(changedProperties); + if (this.hass) { + this._fetchGroups(); + } + this._firstUpdatedCalled = true; + } protected updated(changedProperties: PropertyValues): void { if (changedProperties.has("ieee")) { @@ -82,12 +104,23 @@ export class ZHADevicePage extends LitElement { : ""} ${this._bindableDevices.length > 0 ? html` - + > + ` + : ""} + ${this.device && this._groups.length > 0 + ? html` + ` : ""}
@@ -110,6 +143,10 @@ export class ZHADevicePage extends LitElement { } } + private async _fetchGroups() { + this._groups = (await fetchGroups(this.hass!)).sort(sortZHAGroups); + } + static get styles(): CSSResult[] { return [ haStyle, diff --git a/src/panels/config/zha/zha-group-binding.ts b/src/panels/config/zha/zha-group-binding.ts new file mode 100644 index 0000000000..769f72e005 --- /dev/null +++ b/src/panels/config/zha/zha-group-binding.ts @@ -0,0 +1,321 @@ +import "../../../components/buttons/ha-call-service-button"; +import "../../../components/ha-service-description"; +import "../../../components/ha-card"; +import "../ha-config-section"; +import "@material/mwc-button/mwc-button"; +import "@polymer/paper-dropdown-menu/paper-dropdown-menu"; +import "@polymer/paper-icon-button/paper-icon-button"; +import "@polymer/paper-listbox/paper-listbox"; + +import { + css, + CSSResult, + customElement, + html, + LitElement, + property, + PropertyValues, + TemplateResult, +} from "lit-element"; + +import { + bindDeviceToGroup, + unbindDeviceFromGroup, + ZHADevice, + ZHAGroup, + Cluster, + fetchClustersForZhaNode, +} from "../../../data/zha"; +import "./zha-clusters-data-table"; +import { haStyle } from "../../../resources/styles"; +import { HomeAssistant } from "../../../types"; +import { ItemSelectedEvent } from "./types"; +import "@polymer/paper-item/paper-item"; +import { SelectionChangedEvent } from "../../../components/data-table/ha-data-table"; + +@customElement("zha-group-binding-control") +export class ZHAGroupBindingControl extends LitElement { + @property() public hass?: HomeAssistant; + @property() public isWide?: boolean; + @property() public narrow?: boolean; + @property() public selectedDevice?: ZHADevice; + @property() private _showHelp: boolean = false; + @property() private _bindTargetIndex: number = -1; + @property() private groups: ZHAGroup[] = []; + @property() private _selectedClusters: string[] = []; + @property() private _clusters: Cluster[] = []; + private _groupToBind?: ZHAGroup; + private _clustersToBind?: Cluster[]; + + protected updated(changedProperties: PropertyValues): void { + if (changedProperties.has("selectedDevice")) { + this._bindTargetIndex = -1; + this._selectedClusters = []; + this._clustersToBind = []; + this._fetchClustersForZhaNode(); + } + super.update(changedProperties); + } + + protected render(): TemplateResult | void { + return html` + +
+ ${this.hass!.localize( + "ui.panel.config.zha.group_binding.header" + )} + + +
+ ${this.hass!.localize( + "ui.panel.config.zha.group_binding.introduction" + )} + + +
+ + + ${this.groups.map( + (group) => html` + ${group.name} + ` + )} + + +
+ ${this._showHelp + ? html` +
+ ${this.hass!.localize( + "ui.panel.config.zha.group_binding.group_picker_help" + )} +
+ ` + : ""} +
+ +
+ ${this._showHelp + ? html` +
+ ${this.hass!.localize( + "ui.panel.config.zha.group_binding.cluster_selection_help" + )} +
+ ` + : ""} +
+ ${this.hass!.localize( + "ui.panel.config.zha.group_binding.bind_button_label" + )} + ${this._showHelp + ? html` +
+ ${this.hass!.localize( + "ui.panel.config.zha.group_binding.bind_button_help" + )} +
+ ` + : ""} + ${this.hass!.localize( + "ui.panel.config.zha.group_binding.unbind_button_label" + )} + ${this._showHelp + ? html` +
+ ${this.hass!.localize( + "ui.panel.config.zha.group_binding.unbind_button_help" + )} +
+ ` + : ""} +
+
+
+ `; + } + + private _bindTargetIndexChanged(event: ItemSelectedEvent): void { + this._bindTargetIndex = event.target!.selected; + this._groupToBind = + this._bindTargetIndex === -1 + ? undefined + : this.groups[this._bindTargetIndex]; + } + + private _onHelpTap(): void { + this._showHelp = !this._showHelp; + } + + private async _onBindGroupClick(): Promise { + if (this.hass && this._canBind) { + await bindDeviceToGroup( + this.hass, + this.selectedDevice!.ieee, + this._groupToBind!.group_id, + this._clustersToBind! + ); + } + } + + private async _onUnbindGroupClick(): Promise { + if (this.hass && this._canBind) { + await unbindDeviceFromGroup( + this.hass, + this.selectedDevice!.ieee, + this._groupToBind!.group_id, + this._clustersToBind! + ); + } + } + + private _handleClusterSelectionChanged(event: CustomEvent): void { + const changedSelection = event.detail as SelectionChangedEvent; + const clusterId = changedSelection.id; + if ( + changedSelection.selected && + !this._selectedClusters.includes(clusterId) + ) { + this._selectedClusters.push(clusterId); + } else { + const index = this._selectedClusters.indexOf(clusterId); + if (index !== -1) { + this._selectedClusters.splice(index, 1); + } + } + this._selectedClusters = [...this._selectedClusters]; + this._clustersToBind = []; + for (const clusterIndex of this._selectedClusters) { + const selectedCluster = this._clusters.find((cluster) => { + return clusterIndex === cluster.endpoint_id + "-" + cluster.id; + }); + this._clustersToBind.push(selectedCluster!); + } + } + + private async _fetchClustersForZhaNode(): Promise { + if (this.hass) { + this._clusters = await fetchClustersForZhaNode( + this.hass, + this.selectedDevice!.ieee + ); + this._clusters = this._clusters + .filter((cluster) => { + return cluster.type.toLowerCase() === "out"; + }) + .sort((a, b) => { + return a.name.localeCompare(b.name); + }); + } + } + + private get _canBind(): boolean { + return Boolean( + this._groupToBind && + this._clustersToBind && + this._clustersToBind?.length > 0 && + this.selectedDevice + ); + } + + static get styles(): CSSResult[] { + return [ + haStyle, + css` + .menu { + width: 100%; + } + + .content { + margin-top: 24px; + } + + ha-card { + margin: 0 auto; + max-width: 600px; + } + + .card-actions.warning ha-call-service-button { + color: var(--google-red-500); + } + + .command-picker { + align-items: center; + padding-left: 28px; + padding-right: 28px; + padding-bottom: 10px; + } + + .input-text { + padding-left: 28px; + padding-right: 28px; + padding-bottom: 10px; + } + + .sectionHeader { + flex-grow: 1; + } + + .helpText { + color: grey; + padding-left: 28px; + padding-right: 28px; + padding-bottom: 10px; + } + + .toggle-help-icon { + float: right; + top: -6px; + right: 0; + color: var(--primary-color); + } + + ha-service-description { + display: block; + color: grey; + } + + [hidden] { + display: none; + } + `, + ]; + } +} + +declare global { + interface HTMLElementTagNameMap { + "zha-group-binding-control": ZHAGroupBindingControl; + } +} diff --git a/src/panels/config/zha/zha-group-page.ts b/src/panels/config/zha/zha-group-page.ts index 8aec748c87..afd265c2a4 100644 --- a/src/panels/config/zha/zha-group-page.ts +++ b/src/panels/config/zha/zha-group-page.ts @@ -121,6 +121,8 @@ export class ZHAGroupPage extends LitElement { .hass=${this.hass} .device=${member} .narrow=${this.narrow} + .showActions=${false} + .showEditableInfo=${false} > ` ) @@ -224,7 +226,10 @@ export class ZHAGroupPage extends LitElement { private _handleAddSelectionChanged(ev: CustomEvent): void { const changedSelection = ev.detail as SelectionChangedEvent; const entity = changedSelection.id; - if (changedSelection.selected) { + if ( + changedSelection.selected && + !this._selectedDevicesToAdd.includes(entity) + ) { this._selectedDevicesToAdd.push(entity); } else { const index = this._selectedDevicesToAdd.indexOf(entity); @@ -238,7 +243,10 @@ export class ZHAGroupPage extends LitElement { private _handleRemoveSelectionChanged(ev: CustomEvent): void { const changedSelection = ev.detail as SelectionChangedEvent; const entity = changedSelection.id; - if (changedSelection.selected) { + if ( + changedSelection.selected && + !this._selectedDevicesToRemove.includes(entity) + ) { this._selectedDevicesToRemove.push(entity); } else { const index = this._selectedDevicesToRemove.indexOf(entity); diff --git a/src/panels/config/zha/zha-groups-dashboard.ts b/src/panels/config/zha/zha-groups-dashboard.ts index 874918fd84..4e21dfa5a9 100644 --- a/src/panels/config/zha/zha-groups-dashboard.ts +++ b/src/panels/config/zha/zha-groups-dashboard.ts @@ -47,9 +47,9 @@ export class ZHAGroupsDashboard extends LitElement { protected render(): TemplateResult { return html` { + let outputGroups: GroupRowData[] = groups; + + outputGroups = outputGroups.map((group) => { + return { + ...group, + id: String(group.group_id), + }; + }); + + return outputGroups; + }); + private _columns = memoizeOne( (narrow: boolean): DataTableColumnContainer => narrow @@ -83,8 +96,7 @@ export class ZHAGroupsDataTable extends LitElement { return html` `; diff --git a/src/panels/config/zha/zha-node.ts b/src/panels/config/zha/zha-node.ts index 8281823bd3..0ce0d0bd25 100644 --- a/src/panels/config/zha/zha-node.ts +++ b/src/panels/config/zha/zha-node.ts @@ -55,16 +55,19 @@ export class ZHANode extends LitElement { "ui.panel.config.zha.node_management.hint_wakeup" )} - +
+ +
`; } @@ -93,17 +96,14 @@ export class ZHANode extends LitElement { padding-bottom: 16px; } - ha-card { - margin: 0 auto; + .content { max-width: 600px; + margin: 0 auto; } .card { + padding: 28px 20px 0; margin-top: 24px; - box-sizing: border-box; - display: flex; - flex: 1; - word-wrap: break-word; } ha-service-description { diff --git a/src/translations/en.json b/src/translations/en.json index 91c6e35a10..448baa3b93 100755 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -624,6 +624,9 @@ "updateDeviceName": "Set a custom name for this device in the device registry.", "remove": "Remove a device from the Zigbee network." }, + "confirmations": { + "remove": "Are you sure that you want to remove the device?" + }, "quirk": "Quirk", "last_seen": "Last Seen", "power_source": "Power Source", @@ -1510,6 +1513,17 @@ "create_group": "Zigbee Home Automation - Create Group", "create": "Create Group", "creating_group": "Creating Group" + }, + "group_binding": { + "header": "Group Binding", + "introduction": "Bind and unbind groups.", + "group_picker_label": "Bindable Groups", + "group_picker_help": "Select a group to issue a bind command.", + "cluster_selection_help": "Select clusters to bind to the selected group.", + "bind_button_label": "Bind Group", + "unbind_button_label": "Unbind Group", + "bind_button_help": "Bind the selected group to the selected device clusters.", + "unbind_button_help": "Unbind the selected group from the selected device clusters." } }, "zwave": {