Add view in visualization button to the device page for ZHA devices (#8090)

This commit is contained in:
David F. Mulcahey 2021-03-01 09:54:49 -05:00 committed by GitHub
parent f31f10cea9
commit 1642c68493
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 109 additions and 19 deletions

View File

@ -89,6 +89,11 @@ export const reconfigureNode = (
ieee: ieeeAddress, ieee: ieeeAddress,
}); });
export const refreshTopology = (hass: HomeAssistant): Promise<void> =>
hass.callWS({
type: "zha/topology/update",
});
export const fetchAttributesForCluster = ( export const fetchAttributesForCluster = (
hass: HomeAssistant, hass: HomeAssistant,
ieeeAddress: string, ieeeAddress: string,

View File

@ -79,6 +79,11 @@ export class HaDeviceActionsZha extends LitElement {
"ui.dialogs.zha_device_info.buttons.clusters" "ui.dialogs.zha_device_info.buttons.clusters"
)} )}
</mwc-button> </mwc-button>
<mwc-button @click=${this._onViewInVisualizationClick}>
${this.hass!.localize(
"ui.dialogs.zha_device_info.buttons.view_in_visualization"
)}
</mwc-button>
<mwc-button class="warning" @click=${this._removeDevice}> <mwc-button class="warning" @click=${this._removeDevice}>
${this.hass!.localize( ${this.hass!.localize(
"ui.dialogs.zha_device_info.buttons.remove" "ui.dialogs.zha_device_info.buttons.remove"
@ -104,6 +109,13 @@ export class HaDeviceActionsZha extends LitElement {
navigate(this, "/config/zha/add/" + this._zhaDevice!.ieee); navigate(this, "/config/zha/add/" + this._zhaDevice!.ieee);
} }
private _onViewInVisualizationClick() {
navigate(
this,
"/config/zha/visualization/" + this._zhaDevice!.device_reg_id
);
}
private async _handleZigbeeInfoClicked() { private async _handleZigbeeInfoClicked() {
showZHADeviceZigbeeInfoDialog(this, { device: this._zhaDevice! }); showZHADeviceZigbeeInfoDialog(this, { device: this._zhaDevice! });
} }

View File

@ -59,6 +59,8 @@ class ZHAConfigDashboardRouter extends HassRouterPage {
el.groupId = this.routeTail.path.substr(1); el.groupId = this.routeTail.path.substr(1);
} else if (this._currentPage === "device") { } else if (this._currentPage === "device") {
el.ieee = this.routeTail.path.substr(1); el.ieee = this.routeTail.path.substr(1);
} else if (this._currentPage === "visualization") {
el.zoomedDeviceId = this.routeTail.path.substr(1);
} }
const searchParams = new URLSearchParams(window.location.search); const searchParams = new URLSearchParams(window.location.search);

View File

@ -9,23 +9,33 @@ import {
PropertyValues, PropertyValues,
query, query,
} from "lit-element"; } from "lit-element";
import { Edge, EdgeOptions, Network, Node } from "vis-network";
import "@material/mwc-button";
import { navigate } from "../../../../../common/navigate"; import { navigate } from "../../../../../common/navigate";
import {
fetchDevices,
refreshTopology,
ZHADevice,
} from "../../../../../data/zha";
import "../../../../../layouts/hass-subpage";
import type { HomeAssistant } from "../../../../../types";
import { Network, Edge, Node, EdgeOptions } from "vis-network";
import "../../../../../common/search/search-input"; import "../../../../../common/search/search-input";
import "../../../../../components/device/ha-device-picker"; import "../../../../../components/device/ha-device-picker";
import "../../../../../components/ha-button-menu"; import "../../../../../components/ha-button-menu";
import "../../../../../components/ha-svg-icon"; import "../../../../../components/ha-svg-icon";
import { fetchDevices, ZHADevice } from "../../../../../data/zha";
import "../../../../../layouts/hass-subpage";
import { PolymerChangedEvent } from "../../../../../polymer-types"; import { PolymerChangedEvent } from "../../../../../polymer-types";
import type { HomeAssistant } from "../../../../../types";
import { formatAsPaddedHex } from "./functions"; import { formatAsPaddedHex } from "./functions";
import { DeviceRegistryEntry } from "../../../../../data/device_registry";
@customElement("zha-network-visualization-page") @customElement("zha-network-visualization-page")
export class ZHANetworkVisualizationPage extends LitElement { export class ZHANetworkVisualizationPage extends LitElement {
@property({ type: Object }) public hass!: HomeAssistant; @property({ type: Object }) public hass!: HomeAssistant;
@property({ type: Boolean }) public narrow!: boolean; @property({ type: Boolean, reflect: true }) public narrow = false;
@property()
public zoomedDeviceId?: string;
@query("#visualization", true) @query("#visualization", true)
private _visualization?: HTMLElement; private _visualization?: HTMLElement;
@ -45,9 +55,6 @@ export class ZHANetworkVisualizationPage extends LitElement {
@internalProperty() @internalProperty()
private _filter?: string; private _filter?: string;
@internalProperty()
private _zoomedDeviceId?: string;
protected firstUpdated(changedProperties: PropertyValues): void { protected firstUpdated(changedProperties: PropertyValues): void {
super.firstUpdated(changedProperties); super.firstUpdated(changedProperties);
if (this.hass) { if (this.hass) {
@ -98,6 +105,12 @@ export class ZHANetworkVisualizationPage extends LitElement {
} }
} }
}); });
this._network.on("stabilized", () => {
if (this.zoomedDeviceId) {
this._zoomToDevice();
}
});
} }
protected render() { protected render() {
@ -121,13 +134,18 @@ export class ZHANetworkVisualizationPage extends LitElement {
</search-input> </search-input>
<ha-device-picker <ha-device-picker
.hass=${this.hass} .hass=${this.hass}
.value=${this._zoomedDeviceId} .value=${this.zoomedDeviceId}
.label=${this.hass.localize( .label=${this.hass.localize(
"ui.panel.config.zha.visualization.zoom_label" "ui.panel.config.zha.visualization.zoom_label"
)} )}
.includeDomains="['zha']" .deviceFilter=${(device) => this._filterDevices(device)}
@value-changed=${this._zoomToDevice} @value-changed=${this._onZoomToDevice}
></ha-device-picker> ></ha-device-picker>
<mwc-button @click=${this._refreshTopology}
>${this.hass!.localize(
"ui.panel.config.zha.visualization.refresh_topology"
)}</mwc-button
>
</div> </div>
<div id="visualization"></div> <div id="visualization"></div>
</hass-subpage> </hass-subpage>
@ -248,7 +266,7 @@ export class ZHANetworkVisualizationPage extends LitElement {
filteredNodeIds.push(node.id!); filteredNodeIds.push(node.id!);
} }
}); });
this._zoomedDeviceId = ""; this.zoomedDeviceId = "";
this._zoomOut(); this._zoomOut();
this._network.selectNodes(filteredNodeIds, true); this._network.selectNodes(filteredNodeIds, true);
} else { } else {
@ -256,21 +274,25 @@ export class ZHANetworkVisualizationPage extends LitElement {
} }
} }
private _zoomToDevice(event: PolymerChangedEvent<string>) { private _onZoomToDevice(event: PolymerChangedEvent<string>) {
event.stopPropagation(); event.stopPropagation();
this._zoomedDeviceId = event.detail.value; this.zoomedDeviceId = event.detail.value;
if (!this._network) { if (!this._network) {
return; return;
} }
this._zoomToDevice();
}
private _zoomToDevice() {
this._filter = ""; this._filter = "";
if (!this._zoomedDeviceId) { if (!this.zoomedDeviceId) {
this._zoomOut(); this._zoomOut();
} else { } else {
const device: ZHADevice | undefined = this._devicesByDeviceId.get( const device: ZHADevice | undefined = this._devicesByDeviceId.get(
this._zoomedDeviceId this.zoomedDeviceId
); );
if (device) { if (device) {
this._network.fit({ this._network!.fit({
nodes: [device.ieee], nodes: [device.ieee],
animation: { duration: 500, easingFunction: "easeInQuad" }, animation: { duration: 500, easingFunction: "easeInQuad" },
}); });
@ -285,6 +307,24 @@ export class ZHANetworkVisualizationPage extends LitElement {
}); });
} }
private async _refreshTopology(): Promise<void> {
await refreshTopology(this.hass);
}
private _filterDevices(device: DeviceRegistryEntry): boolean {
if (!this.hass) {
return false;
}
for (const parts of device.identifiers) {
for (const part of parts) {
if (part === "zha") {
return true;
}
}
}
return false;
}
static get styles(): CSSResult[] { static get styles(): CSSResult[] {
return [ return [
css` css`
@ -299,30 +339,59 @@ export class ZHANetworkVisualizationPage extends LitElement {
line-height: var(--paper-font-display1_-_line-height); line-height: var(--paper-font-display1_-_line-height);
opacity: var(--dark-primary-opacity); opacity: var(--dark-primary-opacity);
} }
.table-header { .table-header {
border-bottom: 1px solid --divider-color; border-bottom: 1px solid --divider-color;
padding: 0 16px; padding: 0 16px;
display: flex; display: flex;
align-items: center; align-items: center;
flex-direction: row;
height: var(--header-height); height: var(--header-height);
} }
:host([narrow]) .table-header {
flex-direction: column;
align-items: stretch;
height: var(--header-height) * 3;
}
.search-toolbar { .search-toolbar {
display: flex; display: flex;
align-items: center; align-items: center;
color: var(--secondary-text-color); color: var(--secondary-text-color);
padding: 0 16px; padding: 0 16px;
} }
search-input { search-input {
position: relative; position: relative;
top: 2px; top: 2px;
flex: 1; flex: 1;
} }
:host(:not([narrow])) search-input {
margin: 5px;
}
search-input.header { search-input.header {
left: -8px; left: -8px;
} }
ha-device-picker { ha-device-picker {
flex: 1; flex: 1;
} }
:host(:not([narrow])) ha-device-picker {
margin: 5px;
}
mwc-button {
font-weight: 500;
color: var(--primary-color);
}
:host(:not([narrow])) mwc-button {
margin: 5px;
}
`, `,
]; ];
} }

View File

@ -745,7 +745,8 @@
"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",
"view_in_visualization": "View in Visualization"
}, },
"services": { "services": {
"reconfigure": "Reconfigure ZHA device (heal device). Use this if you are having issues with the device. If the device in question is a battery powered device please ensure it is awake and accepting commands when you use this service.", "reconfigure": "Reconfigure ZHA device (heal device). Use this if you are having issues with the device. If the device in question is a battery powered device please ensure it is awake and accepting commands when you use this service.",
@ -2365,7 +2366,8 @@
"header": "Network Visualization", "header": "Network Visualization",
"caption": "Visualization", "caption": "Visualization",
"highlight_label": "Highlight Devices", "highlight_label": "Highlight Devices",
"zoom_label": "Zoom To Device" "zoom_label": "Zoom To Device",
"refresh_topology": "Refresh Topology"
}, },
"group_binding": { "group_binding": {
"header": "Group Binding", "header": "Group Binding",