mirror of
https://github.com/Koenkk/zigbee2mqtt.git
synced 2024-11-16 02:18:31 -07:00
add support for plantuml based network graphs (#3742)
Add support for [PlantUML](http://plantuml.com/guide) based network graphs, which can be pasted into [this online editor](https://www.planttext.com/). The test configuration used for unit testing for example produces this output: ![output.svg](https://www.planttext.com/api/plantuml/svg/j5LTRzem57tVhxYFGSFbEbyG7gOoOBHggrMCQPsg2Zc9KwmwSPeufTjVsyD-qlx20dICZuJni4PHPDdtd7lkyRNpwyTFU7bESyqoNSJopP-PW6KXu3CYRNSwID7cjkEO3jok8_ONrqk4a0DXOBYEvJOWOaBoi0loKJGpLMYHt4-OBtabpNl58qYK_bIagSeq3wzKoENve3AOUcqaICjtWhIBs4NwTZbOHzojH_iLe--qeoZYWSiStzoes2-aNrlZ3igmqNAzHcNu-SMm1vDnR_1XV0wNmy6I68QcsngQRV0w2BA8UTA4KCJnmf4cp6T2SyXJ7kYiY9kWKYPV9esIWJgsCw9cTRh_w8QruyOqK59bntbYBJtnQZovdWafqXpE2WuZ1KQRsYwwU7rM7Lua3ucQ9qTLiDLzTAi2hLKo3LLHUZnzuD-EQs2wRE1EjR0RmLGieFV8CWOhPYYPOIuoBdcUmvn92VbSw606nfURYn6QbrUliN6RcXrle-mWC1qBfuiZn-ltRWTUbcnZjSn-aMiLGyYVHC1pC0RYQmDk-_r55bXbkJDcMgVTLYlLJ-f4995ghLGGd-Ky9D_5lSyv2PJAGf4mhJB2af2im8HIizK0OyLuClxuUJ8SlufZfkqllaXjc_4Dn_f_TTNxjGqsQipQKqZntmehpX8XxohITg4S7RgMwc2UUKooRR8o6SL3FLCBmtfOURhM6hVOIfY5-zQSyYHS_BT-0m00__y30000) The generated script looks like this: ``` ' paste into: https://www.planttext.com/ @startuml card 0x0017880104e45525 [ 0x0017880104e45525 --- 0x0017880104e45525 (6536) failed: lqi,routingTable --- Boef notSupportedModelID --- 1970-01-01T01:00:01+01:00 ] card 0x000b57fffec6a5b2 [ bulb --- 0x000b57fffec6a5b2 (40369) --- IKEA TRADFRI LED bulb E26/E27 980 lumen, dimmable, white spectrum, opal white (LED1545G12) --- 1970-01-01T01:00:01+01:00 ] card 0x000b57fffec6a5b3 [ bulb_color --- 0x000b57fffec6a5b3 (40399) --- Philips Hue Go (7146060PH) --- unknown ] card 0x0017880104e45521 [ button_double_key --- 0x0017880104e45521 (6538) --- Xiaomi Aqara double key wireless wall switch (WXKG02LM) --- 1970-01-01T01:00:01+01:00 ] card 0x0017880104e45559 [ cc2530_router --- 0x0017880104e45559 (6540) --- Custom devices (DiY) [CC2530 router](http://ptvo.info/cc2530-based-zigbee-coordinator-and-router-112/) (CC2530.ROUTER) --- 1970-01-01T01:00:01+01:00 ] card 0x00124b00120144ae [ Coordinator --- 0x00124b00120144ae (0) --- 1970-01-01T01:00:10+01:00 ] 0x000b57fffec6a5b3 --> 0x00124b00120144ae: 120 0x000b57fffec6a5b2 --> 0x00124b00120144ae: 92 0x000b57fffec6a5b3 --> 0x000b57fffec6a5b2: 110 0x0017880104e45559 --> 0x000b57fffec6a5b2: 100 0x0017880104e45521 --> 0x0017880104e45559: 130 @enduml ``` Co-authored-by: Konstantin Baumann <konstantin.baumann@autodesk.com>
This commit is contained in:
parent
40f026cdf8
commit
a2ea520b0c
@ -17,11 +17,13 @@ class NetworkMap extends Extension {
|
||||
// Bind
|
||||
this.raw = this.raw.bind(this);
|
||||
this.graphviz = this.graphviz.bind(this);
|
||||
this.plantuml = this.plantuml.bind(this);
|
||||
|
||||
// Set supported formats
|
||||
this.supportedFormats = {
|
||||
'raw': this.raw,
|
||||
'graphviz': this.graphviz,
|
||||
'plantuml': this.plantuml,
|
||||
};
|
||||
}
|
||||
|
||||
@ -126,6 +128,64 @@ class NetworkMap extends Extension {
|
||||
return text.replace(/\0/g, '');
|
||||
}
|
||||
|
||||
plantuml(topology) {
|
||||
const text = [];
|
||||
|
||||
text.push(`' paste into: https://www.planttext.com/`);
|
||||
text.push(``);
|
||||
text.push('@startuml');
|
||||
|
||||
topology.nodes.sort((a, b) => a.friendlyName.localeCompare(b.friendlyName)).forEach((node) => {
|
||||
// Add friendly name
|
||||
text.push(`card ${node.ieeeAddr} [`);
|
||||
text.push(`${node.friendlyName}`);
|
||||
text.push(`---`);
|
||||
|
||||
// Add the device short network address, ieeaddr and scan note (if any)
|
||||
text.push(
|
||||
`${node.ieeeAddr} (${node.networkAddress})` +
|
||||
((node.failed && node.failed.length) ? ` failed: ${node.failed.join(',')}` : ''),
|
||||
);
|
||||
|
||||
// Add the device model
|
||||
if (node.type !== 'Coordinator') {
|
||||
text.push(`---`);
|
||||
const definition = this.zigbee.resolveEntity(node.ieeeAddr).definition;
|
||||
if (definition) {
|
||||
text.push(`${definition.vendor} ${definition.description} (${definition.model})`);
|
||||
} else {
|
||||
// This model is not supported by zigbee-herdsman-converters, add zigbee model information
|
||||
text.push(`${node.manufacturerName} ${node.modelID}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Add the device last_seen timestamp
|
||||
let lastSeen = 'unknown';
|
||||
const date = node.type === 'Coordinator' ? Date.now() : node.lastSeen;
|
||||
if (date) {
|
||||
lastSeen = utils.formatDate(date, 'ISO_8601_local');
|
||||
}
|
||||
text.push(`---`);
|
||||
text.push(lastSeen);
|
||||
text.push(`]`);
|
||||
text.push(``);
|
||||
});
|
||||
|
||||
/**
|
||||
* Add edges between the devices
|
||||
* 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.
|
||||
*/
|
||||
topology.links.forEach((link) => {
|
||||
text.push(`${link.sourceIeeeAddr} --> ${link.targetIeeeAddr}: ${link.lqi}`);
|
||||
});
|
||||
text.push('');
|
||||
|
||||
text.push(`@enduml`);
|
||||
|
||||
return text.join(`\n`);
|
||||
}
|
||||
|
||||
async networkScan(includeRoutes) {
|
||||
logger.info(`Starting network scan (includeRoutes '${includeRoutes}')`);
|
||||
const devices = this.zigbee.getDevices().filter((d) => d.type !== 'GreenPower');
|
||||
|
@ -168,4 +168,101 @@ describe('Networkmap', () => {
|
||||
Date.prototype.getTimezoneOffset = getTimezoneOffset;
|
||||
Date.prototype.getHours = getHours;
|
||||
});
|
||||
|
||||
it('Output plantuml networkmap', async () => {
|
||||
const getTimezoneOffset = Date.prototype.getTimezoneOffset;
|
||||
const getHours = Date.prototype.getHours;
|
||||
Date.prototype.getTimezoneOffset = () => -60;
|
||||
Date.prototype.getHours = () => 1;
|
||||
mock();
|
||||
const device = zigbeeHerdsman.devices.bulb_color;
|
||||
device.lastSeen = null;
|
||||
const endpoint = device.getEndpoint(1);
|
||||
const data = {modelID: 'test'}
|
||||
const payload = {data, cluster: 'genOnOff', device, endpoint, type: 'readResponse', linkquality: 10};
|
||||
await zigbeeHerdsman.events.message(payload);
|
||||
MQTT.events.message('zigbee2mqtt/bridge/networkmap/routes', 'plantuml');
|
||||
await flushPromises();
|
||||
expect(MQTT.publish).toHaveBeenCalledTimes(1);
|
||||
let call = MQTT.publish.mock.calls[0];
|
||||
expect(call[0]).toStrictEqual('zigbee2mqtt/bridge/networkmap/plantuml');
|
||||
|
||||
const expected = `' paste into: https://www.planttext.com/
|
||||
|
||||
@startuml
|
||||
card 0x0017880104e45525 [
|
||||
0x0017880104e45525
|
||||
---
|
||||
0x0017880104e45525 (6536) failed: lqi,routingTable
|
||||
---
|
||||
Boef notSupportedModelID
|
||||
---
|
||||
1970-01-01T01:00:01+01:00
|
||||
]
|
||||
|
||||
card 0x000b57fffec6a5b2 [
|
||||
bulb
|
||||
---
|
||||
0x000b57fffec6a5b2 (40369)
|
||||
---
|
||||
IKEA TRADFRI LED bulb E26/E27 980 lumen, dimmable, white spectrum, opal white (LED1545G12)
|
||||
---
|
||||
1970-01-01T01:00:01+01:00
|
||||
]
|
||||
|
||||
card 0x000b57fffec6a5b3 [
|
||||
bulb_color
|
||||
---
|
||||
0x000b57fffec6a5b3 (40399)
|
||||
---
|
||||
Philips Hue Go (7146060PH)
|
||||
---
|
||||
unknown
|
||||
]
|
||||
|
||||
card 0x0017880104e45521 [
|
||||
button_double_key
|
||||
---
|
||||
0x0017880104e45521 (6538)
|
||||
---
|
||||
Xiaomi Aqara double key wireless wall switch (WXKG02LM)
|
||||
---
|
||||
1970-01-01T01:00:01+01:00
|
||||
]
|
||||
|
||||
card 0x0017880104e45559 [
|
||||
cc2530_router
|
||||
---
|
||||
0x0017880104e45559 (6540)
|
||||
---
|
||||
Custom devices (DiY) [CC2530 router](http://ptvo.info/cc2530-based-zigbee-coordinator-and-router-112/) (CC2530.ROUTER)
|
||||
---
|
||||
1970-01-01T01:00:01+01:00
|
||||
]
|
||||
|
||||
card 0x00124b00120144ae [
|
||||
Coordinator
|
||||
---
|
||||
0x00124b00120144ae (0)
|
||||
---
|
||||
1970-01-01T01:00:10+01:00
|
||||
]
|
||||
|
||||
0x000b57fffec6a5b3 --> 0x00124b00120144ae: 120
|
||||
0x000b57fffec6a5b2 --> 0x00124b00120144ae: 92
|
||||
0x000b57fffec6a5b3 --> 0x000b57fffec6a5b2: 110
|
||||
0x0017880104e45559 --> 0x000b57fffec6a5b2: 100
|
||||
0x0017880104e45521 --> 0x0017880104e45559: 130
|
||||
|
||||
@enduml`;
|
||||
|
||||
const expectedLines = expected.split('\n');
|
||||
const actualLines = call[1].split('\n');
|
||||
|
||||
for (let i = 0; i < expectedLines.length; i++) {
|
||||
expect(actualLines[i].trim()).toStrictEqual(expectedLines[i].trim());
|
||||
}
|
||||
Date.prototype.getTimezoneOffset = getTimezoneOffset;
|
||||
Date.prototype.getHours = getHours;
|
||||
});
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user