From 0322354c37ea036386b94487f8349c97134101a3 Mon Sep 17 00:00:00 2001 From: Laurent Date: Sun, 7 Oct 2018 21:46:54 +0200 Subject: [PATCH] [RFC] graphviz network map, display all devices (#443) * network map, more info * add a get(All)Devices method to zigbee * graphviz: display all devices loop through all devices, display all devices even those that haven't responded to the lqi scan. * makes eslint happy :) * remove null chars from network map graphviz output makes graphviz happy :-) * Improvements to graphviz network map * Always add device type --- .eslintrc.json | 1 + lib/extension/networkMap.js | 53 ++++++++++++++++++++++++++++++------- lib/zigbee.js | 10 ++++--- 3 files changed, 52 insertions(+), 12 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index 96e45c73..ed48c1cb 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,5 +1,6 @@ { "env": { + "es6": true, "node": true }, "extends": ["eslint:recommended", "google"], diff --git a/lib/extension/networkMap.js b/lib/extension/networkMap.js index 59bd9615..5dc6fc0a 100644 --- a/lib/extension/networkMap.js +++ b/lib/extension/networkMap.js @@ -1,5 +1,6 @@ const settings = require('../util/settings'); +const zigbeeShepherdConverters = require('zigbee-shepherd-converters'); class NetworkMap { constructor(zigbee, mqtt, state) { @@ -23,7 +24,7 @@ class NetworkMap { if (topic === this.topic && this.supportedFormats.hasOwnProperty(message)) { this.zigbee.networkScan((result)=> { - const converted = this.supportedFormats[message](result); + const converted = this.supportedFormats[message](this.zigbee, result); this.mqtt.publish(`bridge/networkmap/${message}`, converted, {}); }); @@ -31,21 +32,55 @@ class NetworkMap { } } - raw(topology) { + raw(zigbee, topology) { return JSON.stringify(topology); } - graphviz(topology) { - let text = 'digraph G {\n'; - topology.forEach((item) => { - const friendlyName = settings.getDevice(item.ieeeAddr).friendly_name; - text += ` "${item.ieeeAddr}" [label="${friendlyName} (${item.ieeeAddr} - ${item.status})"];\n`; - text += ` "${item.ieeeAddr}" -> "${item.parent}" [label="${item.lqi}"]\n`; + graphviz(zigbee, topology) { + let text = 'digraph G {\nnode[shape=record];\n'; + const lqiDevices = new Map(topology.map((d) => [d.ieeeAddr, d])); + + zigbee.getDevices().forEach((device) => { + const labels = []; + const friendlyDevice = settings.getDevice(device.ieeeAddr); + const friendlyName = friendlyDevice ? friendlyDevice.friendly_name : device.ieeeAddr; + + // Add friendly name + labels.push(friendlyName); + + // Add the device type + labels.push(device.type); + + // Add the device model + const mappedModel = zigbeeShepherdConverters.findByZigbeeModel(device.modelId); + if (mappedModel) { + labels.push(`${mappedModel.vendor} ${mappedModel.description} (${mappedModel.model})`); + } else { + // This model is not supported by zigbee-shepherd-converters, add zigbee model information, if available + const zigbeeModel = [device.manufName, device.modelId].filter((a) => a).join(' '); + labels.push(zigbeeModel ? zigbeeModel : 'No model information available'); + } + + // Add the device status (online/offline) + labels.push(device.status); + + // Add the device with its labels to the graph as a node. + text += ` "${device.ieeeAddr}" [label="{${labels.join('|')}}"];\n`; + + /** + * Add an edge between the device and its parent to the graph + * NOTE: There are situations where a device is NOT in the topology, this can be e.g. + * due to not responded to the lqi scan. In that case we do not add an edge for this device. + */ + const lqiDevice = lqiDevices.get(device.ieeeAddr); + if (lqiDevice != undefined) { + text += ` "${device.ieeeAddr}" -> "${lqiDevice.parent}" [label="${lqiDevice.lqi}"]\n`; + } }); text += '}'; - return text; + return text.replace(/\0/g, ''); } } diff --git a/lib/zigbee.js b/lib/zigbee.js index f6e70237..a605c5ef 100644 --- a/lib/zigbee.js +++ b/lib/zigbee.js @@ -127,7 +127,7 @@ class Zigbee { } getAllClients() { - return this.shepherd.list().filter((device) => device.type !== 'Coordinator'); + return this.getDevices().filter((device) => device.type !== 'Coordinator'); } removeDevice(deviceID, callback) { @@ -173,12 +173,16 @@ class Zigbee { } } + getDevices() { + return this.shepherd.list(); + } + getDevice(deviceID) { - return this.shepherd.list().find((d) => d.ieeeAddr === deviceID); + return this.getDevices().find((d) => d.ieeeAddr === deviceID); } getCoordinator() { - const device = this.shepherd.list().find((d) => d.type === 'Coordinator'); + const device = this.getDevices().find((d) => d.type === 'Coordinator'); return this.shepherd.find(device.ieeeAddr, 1); }