ZHA UI enhancements (#8573)

Co-authored-by: Bram Kragten <mail@bramkragten.nl>
This commit is contained in:
David F. Mulcahey 2021-03-08 07:23:14 -05:00 committed by GitHub
parent 194024edb9
commit 01c4d662f2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 206 additions and 0 deletions

View File

@ -21,6 +21,7 @@ import { haStyle } from "../../../../../../resources/styles";
import { HomeAssistant } from "../../../../../../types"; import { HomeAssistant } from "../../../../../../types";
import { showZHAClusterDialog } from "../../../../integrations/integration-panels/zha/show-dialog-zha-cluster"; import { showZHAClusterDialog } from "../../../../integrations/integration-panels/zha/show-dialog-zha-cluster";
import { showZHADeviceZigbeeInfoDialog } from "../../../../integrations/integration-panels/zha/show-dialog-zha-device-zigbee-info"; import { showZHADeviceZigbeeInfoDialog } from "../../../../integrations/integration-panels/zha/show-dialog-zha-device-zigbee-info";
import { showZHADeviceChildrenDialog } from "../../../../integrations/integration-panels/zha/show-dialog-zha-device-children";
@customElement("ha-device-actions-zha") @customElement("ha-device-actions-zha")
export class HaDeviceActionsZha extends LitElement { export class HaDeviceActionsZha extends LitElement {
@ -65,6 +66,11 @@ export class HaDeviceActionsZha extends LitElement {
<mwc-button @click=${this._onAddDevicesClick}> <mwc-button @click=${this._onAddDevicesClick}>
${this.hass!.localize("ui.dialogs.zha_device_info.buttons.add")} ${this.hass!.localize("ui.dialogs.zha_device_info.buttons.add")}
</mwc-button> </mwc-button>
<mwc-button @click=${this._handleDeviceChildrenClicked}>
${this.hass!.localize(
"ui.dialogs.zha_device_info.buttons.device_children"
)}
</mwc-button>
` `
: ""} : ""}
${this._zhaDevice.device_type !== "Coordinator" ${this._zhaDevice.device_type !== "Coordinator"
@ -120,6 +126,10 @@ export class HaDeviceActionsZha extends LitElement {
showZHADeviceZigbeeInfoDialog(this, { device: this._zhaDevice! }); showZHADeviceZigbeeInfoDialog(this, { device: this._zhaDevice! });
} }
private async _handleDeviceChildrenClicked() {
showZHADeviceChildrenDialog(this, { device: this._zhaDevice! });
}
private async _removeDevice() { private async _removeDevice() {
const confirmed = await showConfirmationDialog(this, { const confirmed = await showConfirmationDialog(this, {
text: this.hass.localize( text: this.hass.localize(

View File

@ -0,0 +1,146 @@
import {
CSSResult,
customElement,
html,
internalProperty,
LitElement,
property,
TemplateResult,
} from "lit-element";
import memoizeOne from "memoize-one";
import { computeRTLDirection } from "../../../../../common/util/compute_rtl";
import "../../../../../components/ha-code-editor";
import { createCloseHeading } from "../../../../../components/ha-dialog";
import { haStyleDialog } from "../../../../../resources/styles";
import { HomeAssistant } from "../../../../../types";
import { ZHADeviceChildrenDialogParams } from "./show-dialog-zha-device-children";
import "../../../../../components/data-table/ha-data-table";
import type {
DataTableColumnContainer,
DataTableRowData,
} from "../../../../../components/data-table/ha-data-table";
import "../../../../../components/ha-circular-progress";
import { fetchDevices, ZHADevice } from "../../../../../data/zha";
import { fireEvent } from "../../../../../common/dom/fire_event";
export interface DeviceRowData extends DataTableRowData {
id: string;
name: string;
lqi: number;
}
@customElement("dialog-zha-device-children")
class DialogZHADeviceChildren extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@internalProperty() private _device: ZHADevice | undefined;
@internalProperty() private _devices: Map<string, ZHADevice> | undefined;
private _deviceChildren = memoizeOne(
(
device: ZHADevice | undefined,
devices: Map<string, ZHADevice> | undefined
) => {
const outputDevices: DeviceRowData[] = [];
if (device && devices) {
device.neighbors.forEach((child) => {
const zhaDevice: ZHADevice | undefined = devices.get(child.ieee);
if (zhaDevice) {
outputDevices.push({
name: zhaDevice.user_given_name || zhaDevice.name,
id: zhaDevice.device_reg_id,
lqi: child.lqi,
});
}
});
}
return outputDevices;
}
);
private _columns: DataTableColumnContainer = {
name: {
title: "Name",
sortable: true,
filterable: true,
direction: "asc",
grows: true,
},
lqi: {
title: "LQI",
sortable: true,
filterable: true,
direction: "asc",
width: "75px",
},
};
public showDialog(
params: ZHADeviceChildrenDialogParams
): void {
this._device = params.device;
this._fetchData();
}
public closeDialog(): void {
this._device = undefined;
this._devices = undefined;
fireEvent(this, "dialog-closed", { dialog: this.localName });
}
protected render(): TemplateResult {
if (!this._device) {
return html``;
}
return html`
<ha-dialog
hideActions
open
@closed=${this.closeDialog}
.heading=${createCloseHeading(
this.hass,
this.hass.localize(`ui.dialogs.zha_device_info.device_children`)
)}
>
${!this._devices
? html`<ha-circular-progress
alt="Loading"
size="large"
active
></ha-circular-progress>`
: html`<ha-data-table
.columns=${this._columns}
.data=${this._deviceChildren(this._device, this._devices)}
auto-height
.dir=${computeRTLDirection(this.hass)}
.searchLabel=${this.hass.localize(
"ui.components.data-table.search"
)}
.noDataText=${this.hass.localize(
"ui.components.data-table.no-data"
)}
></ha-data-table>`}
</ha-dialog>
`;
}
private async _fetchData(): Promise<void> {
if (this._device && this.hass) {
const devices = await fetchDevices(this.hass!);
this._devices = new Map(
devices.map((device: ZHADevice) => [device.ieee, device])
);
}
}
static get styles(): CSSResult {
return haStyleDialog;
}
}
declare global {
interface HTMLElementTagNameMap {
"dialog-zha-device-children": DialogZHADeviceChildren;
}
}

View File

@ -0,0 +1,20 @@
import { fireEvent } from "../../../../../common/dom/fire_event";
import { ZHADevice } from "../../../../../data/zha";
export interface ZHADeviceChildrenDialogParams {
device: ZHADevice;
}
export const loadZHADeviceChildrenDialog = () =>
import("./dialog-zha-device-children");
export const showZHADeviceChildrenDialog = (
element: HTMLElement,
zhaDeviceChildrenParams: ZHADeviceChildrenDialogParams
): void => {
fireEvent(element, "show-dialog", {
dialogTag: "dialog-zha-device-children",
dialogImport: loadZHADeviceChildrenDialog,
dialogParams: zhaDeviceChildrenParams,
});
};

View File

@ -27,6 +27,8 @@ import "../../../../../components/ha-svg-icon";
import { PolymerChangedEvent } from "../../../../../polymer-types"; import { PolymerChangedEvent } from "../../../../../polymer-types";
import { formatAsPaddedHex } from "./functions"; import { formatAsPaddedHex } from "./functions";
import { DeviceRegistryEntry } from "../../../../../data/device_registry"; import { DeviceRegistryEntry } from "../../../../../data/device_registry";
import "../../../../../components/ha-checkbox";
import type { HaCheckbox } from "../../../../../components/ha-checkbox";
@customElement("zha-network-visualization-page") @customElement("zha-network-visualization-page")
export class ZHANetworkVisualizationPage extends LitElement { export class ZHANetworkVisualizationPage extends LitElement {
@ -55,11 +57,15 @@ export class ZHANetworkVisualizationPage extends LitElement {
@internalProperty() @internalProperty()
private _filter?: string; private _filter?: string;
private _autoZoom = true;
protected firstUpdated(changedProperties: PropertyValues): void { protected firstUpdated(changedProperties: PropertyValues): void {
super.firstUpdated(changedProperties); super.firstUpdated(changedProperties);
if (this.hass) { if (this.hass) {
this._fetchData(); this._fetchData();
} }
this._network = new Network( this._network = new Network(
this._visualization!, this._visualization!,
{}, {},
@ -92,6 +98,7 @@ export class ZHANetworkVisualizationPage extends LitElement {
}, },
} }
); );
this._network.on("doubleClick", (properties) => { this._network.on("doubleClick", (properties) => {
const ieee = properties.nodes[0]; const ieee = properties.nodes[0];
if (ieee) { if (ieee) {
@ -106,6 +113,17 @@ export class ZHANetworkVisualizationPage extends LitElement {
} }
}); });
this._network.on("click", (properties) => {
const ieee = properties.nodes[0];
if (ieee) {
const device = this._devices.get(ieee);
if (device && this._autoZoom) {
this.zoomedDeviceId = device.device_reg_id;
this._zoomToDevice();
}
}
});
this._network.on("stabilized", () => { this._network.on("stabilized", () => {
if (this.zoomedDeviceId) { if (this.zoomedDeviceId) {
this._zoomToDevice(); this._zoomToDevice();
@ -141,6 +159,11 @@ export class ZHANetworkVisualizationPage extends LitElement {
.deviceFilter=${(device) => this._filterDevices(device)} .deviceFilter=${(device) => this._filterDevices(device)}
@value-changed=${this._onZoomToDevice} @value-changed=${this._onZoomToDevice}
></ha-device-picker> ></ha-device-picker>
<ha-checkbox
@change=${this._handleCheckboxChange}
.checked=${this._autoZoom}
></ha-checkbox
>${this.hass!.localize("ui.panel.config.zha.visualization.auto_zoom")}
<mwc-button @click=${this._refreshTopology} <mwc-button @click=${this._refreshTopology}
>${this.hass!.localize( >${this.hass!.localize(
"ui.panel.config.zha.visualization.refresh_topology" "ui.panel.config.zha.visualization.refresh_topology"
@ -325,6 +348,10 @@ export class ZHANetworkVisualizationPage extends LitElement {
return false; return false;
} }
private _handleCheckboxChange(ev: Event) {
this._autoZoom = (ev.target as HaCheckbox).checked;
}
static get styles(): CSSResult[] { static get styles(): CSSResult[] {
return [ return [
css` css`

View File

@ -743,12 +743,14 @@
"manuf": "by {manufacturer}", "manuf": "by {manufacturer}",
"no_area": "No Area", "no_area": "No Area",
"device_signature": "Zigbee device signature", "device_signature": "Zigbee device signature",
"device_children": "Zigbee device children",
"buttons": { "buttons": {
"add": "Add Devices via this device", "add": "Add Devices via this device",
"remove": "Remove Device", "remove": "Remove Device",
"clusters": "Manage Clusters", "clusters": "Manage Clusters",
"reconfigure": "Reconfigure Device", "reconfigure": "Reconfigure Device",
"zigbee_information": "Zigbee device signature", "zigbee_information": "Zigbee device signature",
"device_children": "View Children",
"view_in_visualization": "View in Visualization" "view_in_visualization": "View in Visualization"
}, },
"services": { "services": {
@ -2373,6 +2375,7 @@
"caption": "Visualization", "caption": "Visualization",
"highlight_label": "Highlight Devices", "highlight_label": "Highlight Devices",
"zoom_label": "Zoom To Device", "zoom_label": "Zoom To Device",
"auto_zoom": "Auto Zoom",
"refresh_topology": "Refresh Topology" "refresh_topology": "Refresh Topology"
}, },
"group_binding": { "group_binding": {