mirror of
https://github.com/Koenkk/zigbee2mqtt.git
synced 2026-06-27 15:41:41 +00:00
30227a13ae
* chore: Implement prettier * Run prettier * fix lint * process feedback * process feedback
830 lines
37 KiB
JavaScript
830 lines
37 KiB
JavaScript
const data = require('./stub/data');
|
|
const logger = require('./stub/logger');
|
|
const zigbeeHerdsman = require('./stub/zigbeeHerdsman');
|
|
const stringify = require('json-stable-stringify-without-jsonify');
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
const {coordinator, bulb, bulb_color, WXKG02LM_rev1, CC2530_ROUTER, unsupported_router, external_converter_device} = zigbeeHerdsman.devices;
|
|
|
|
zigbeeHerdsman.returnDevices.push(coordinator.ieeeAddr);
|
|
zigbeeHerdsman.returnDevices.push(bulb.ieeeAddr);
|
|
zigbeeHerdsman.returnDevices.push(bulb_color.ieeeAddr);
|
|
zigbeeHerdsman.returnDevices.push(WXKG02LM_rev1.ieeeAddr);
|
|
zigbeeHerdsman.returnDevices.push(CC2530_ROUTER.ieeeAddr);
|
|
zigbeeHerdsman.returnDevices.push(unsupported_router.ieeeAddr);
|
|
zigbeeHerdsman.returnDevices.push(external_converter_device.ieeeAddr);
|
|
const MQTT = require('./stub/mqtt');
|
|
const settings = require('../lib/util/settings');
|
|
const Controller = require('../lib/controller');
|
|
const flushPromises = require('./lib/flushPromises');
|
|
const mocksClear = [MQTT.publish, logger.warning, logger.debug];
|
|
const setTimeoutNative = setTimeout;
|
|
|
|
describe('Networkmap', () => {
|
|
let controller;
|
|
|
|
beforeAll(async () => {
|
|
jest.useFakeTimers();
|
|
Date.now = jest.fn();
|
|
Date.now.mockReturnValue(10000);
|
|
data.writeDefaultConfiguration();
|
|
settings.reRead();
|
|
data.writeEmptyState();
|
|
fs.copyFileSync(path.join(__dirname, 'assets', 'mock-external-converter.js'), path.join(data.mockDir, 'mock-external-converter.js'));
|
|
settings.set(['external_converters'], ['mock-external-converter.js']);
|
|
controller = new Controller(jest.fn(), jest.fn());
|
|
await controller.start();
|
|
mocksClear.forEach((m) => m.mockClear());
|
|
await flushPromises();
|
|
});
|
|
|
|
beforeEach(async () => {
|
|
mocksClear.forEach((m) => m.mockClear());
|
|
await flushPromises();
|
|
const device = zigbeeHerdsman.devices.bulb_color;
|
|
device.lastSeen = 1000;
|
|
external_converter_device.lastSeen = 1000;
|
|
global.setTimeout = (r) => r();
|
|
});
|
|
|
|
afterEach(async () => {
|
|
global.setTimeout = setTimeoutNative;
|
|
});
|
|
|
|
afterAll(async () => {
|
|
jest.useRealTimers();
|
|
});
|
|
|
|
function mock() {
|
|
/**
|
|
* Topology
|
|
* | -> external_device
|
|
* | -> bulb_color -> unsupported_router (offline)
|
|
* coordinator | ^ ^
|
|
* | | | (not valid)
|
|
* | -> bulb |
|
|
* | -> CC2530_ROUTER -> WXKG02LM_rev1
|
|
*
|
|
*/
|
|
coordinator.lqi = () => {
|
|
return {
|
|
neighbors: [
|
|
{ieeeAddr: bulb_color.ieeeAddr, networkAddress: bulb_color.networkAddress, relationship: 2, depth: 1, linkquality: 120},
|
|
{ieeeAddr: bulb.ieeeAddr, networkAddress: bulb.networkAddress, relationship: 2, depth: 1, linkquality: 92},
|
|
{
|
|
ieeeAddr: external_converter_device.ieeeAddr,
|
|
networkAddress: external_converter_device.networkAddress,
|
|
relationship: 2,
|
|
depth: 1,
|
|
linkquality: 92,
|
|
},
|
|
],
|
|
};
|
|
};
|
|
coordinator.routingTable = () => {
|
|
return {table: [{destinationAddress: CC2530_ROUTER.networkAddress, status: 'ACTIVE', nextHop: bulb.networkAddress}]};
|
|
};
|
|
|
|
bulb.lqi = () => {
|
|
return {
|
|
neighbors: [
|
|
{ieeeAddr: bulb_color.ieeeAddr, networkAddress: bulb_color.networkAddress, relationship: 1, depth: 2, linkquality: 110},
|
|
{ieeeAddr: CC2530_ROUTER.ieeeAddr, networkAddress: CC2530_ROUTER.networkAddress, relationship: 1, depth: 2, linkquality: 100},
|
|
],
|
|
};
|
|
};
|
|
bulb.routingTable = () => {
|
|
return {table: []};
|
|
};
|
|
|
|
bulb_color.lqi = () => {
|
|
return {neighbors: []};
|
|
};
|
|
bulb_color.routingTable = () => {
|
|
return {table: []};
|
|
};
|
|
|
|
CC2530_ROUTER.lqi = () => {
|
|
return {
|
|
neighbors: [
|
|
{ieeeAddr: '0x0000000000000000', networkAddress: WXKG02LM_rev1.networkAddress, relationship: 1, depth: 2, linkquality: 130},
|
|
{ieeeAddr: bulb_color.ieeeAddr, networkAddress: bulb_color.networkAddress, relationship: 4, depth: 2, linkquality: 130},
|
|
],
|
|
};
|
|
};
|
|
CC2530_ROUTER.routingTable = () => {
|
|
return {table: []};
|
|
};
|
|
|
|
unsupported_router.lqi = () => {
|
|
throw new Error('failed');
|
|
};
|
|
unsupported_router.routingTable = () => {
|
|
throw new Error('failed');
|
|
};
|
|
}
|
|
|
|
it('Output raw networkmap legacy api', async () => {
|
|
mock();
|
|
MQTT.events.message('zigbee2mqtt/bridge/networkmap/routes', 'raw');
|
|
await flushPromises();
|
|
expect(MQTT.publish).toHaveBeenCalledTimes(1);
|
|
let call = MQTT.publish.mock.calls[0];
|
|
expect(call[0]).toStrictEqual('zigbee2mqtt/bridge/networkmap/raw');
|
|
|
|
const expected = {
|
|
links: [
|
|
{
|
|
depth: 1,
|
|
linkquality: 120,
|
|
lqi: 120,
|
|
relationship: 2,
|
|
routes: [],
|
|
source: {ieeeAddr: '0x000b57fffec6a5b3', networkAddress: 40399},
|
|
sourceIeeeAddr: '0x000b57fffec6a5b3',
|
|
sourceNwkAddr: 40399,
|
|
target: {ieeeAddr: '0x00124b00120144ae', networkAddress: 0},
|
|
targetIeeeAddr: '0x00124b00120144ae',
|
|
},
|
|
{
|
|
depth: 1,
|
|
linkquality: 92,
|
|
lqi: 92,
|
|
relationship: 2,
|
|
routes: [{destinationAddress: 6540, nextHop: 40369, status: 'ACTIVE'}],
|
|
source: {ieeeAddr: '0x000b57fffec6a5b2', networkAddress: 40369},
|
|
sourceIeeeAddr: '0x000b57fffec6a5b2',
|
|
sourceNwkAddr: 40369,
|
|
target: {ieeeAddr: '0x00124b00120144ae', networkAddress: 0},
|
|
targetIeeeAddr: '0x00124b00120144ae',
|
|
},
|
|
{
|
|
depth: 1,
|
|
linkquality: 92,
|
|
lqi: 92,
|
|
relationship: 2,
|
|
routes: [],
|
|
source: {ieeeAddr: '0x0017880104e45511', networkAddress: 1114},
|
|
sourceIeeeAddr: '0x0017880104e45511',
|
|
sourceNwkAddr: 1114,
|
|
target: {ieeeAddr: '0x00124b00120144ae', networkAddress: 0},
|
|
targetIeeeAddr: '0x00124b00120144ae',
|
|
},
|
|
{
|
|
depth: 2,
|
|
linkquality: 110,
|
|
lqi: 110,
|
|
relationship: 1,
|
|
routes: [],
|
|
source: {ieeeAddr: '0x000b57fffec6a5b3', networkAddress: 40399},
|
|
sourceIeeeAddr: '0x000b57fffec6a5b3',
|
|
sourceNwkAddr: 40399,
|
|
target: {ieeeAddr: '0x000b57fffec6a5b2', networkAddress: 40369},
|
|
targetIeeeAddr: '0x000b57fffec6a5b2',
|
|
},
|
|
{
|
|
depth: 2,
|
|
linkquality: 100,
|
|
lqi: 100,
|
|
relationship: 1,
|
|
routes: [],
|
|
source: {ieeeAddr: '0x0017880104e45559', networkAddress: 6540},
|
|
sourceIeeeAddr: '0x0017880104e45559',
|
|
sourceNwkAddr: 6540,
|
|
target: {ieeeAddr: '0x000b57fffec6a5b2', networkAddress: 40369},
|
|
targetIeeeAddr: '0x000b57fffec6a5b2',
|
|
},
|
|
{
|
|
depth: 2,
|
|
linkquality: 130,
|
|
lqi: 130,
|
|
relationship: 1,
|
|
routes: [],
|
|
source: {ieeeAddr: '0x0017880104e45521', networkAddress: 6538},
|
|
sourceIeeeAddr: '0x0017880104e45521',
|
|
sourceNwkAddr: 6538,
|
|
target: {ieeeAddr: '0x0017880104e45559', networkAddress: 6540},
|
|
targetIeeeAddr: '0x0017880104e45559',
|
|
},
|
|
],
|
|
nodes: [
|
|
{
|
|
definition: null,
|
|
failed: [],
|
|
friendlyName: 'Coordinator',
|
|
ieeeAddr: '0x00124b00120144ae',
|
|
lastSeen: 1000,
|
|
modelID: null,
|
|
networkAddress: 0,
|
|
type: 'Coordinator',
|
|
},
|
|
{
|
|
definition: {
|
|
description: 'TRADFRI bulb E26/E27, white spectrum, globe, opal, 980 lm',
|
|
model: 'LED1545G12',
|
|
supports:
|
|
'light (state, brightness, color_temp, color_temp_startup), effect, power_on_behavior, color_options, identify, linkquality',
|
|
vendor: 'IKEA',
|
|
},
|
|
failed: [],
|
|
friendlyName: 'bulb',
|
|
ieeeAddr: '0x000b57fffec6a5b2',
|
|
lastSeen: 1000,
|
|
modelID: 'TRADFRI bulb E27 WS opal 980lm',
|
|
networkAddress: 40369,
|
|
type: 'Router',
|
|
},
|
|
{
|
|
definition: {
|
|
description: 'Hue Go',
|
|
model: '7146060PH',
|
|
supports:
|
|
'light (state, brightness, color_temp, color_temp_startup, color_xy, color_hs), power_on_behavior, effect, linkquality',
|
|
vendor: 'Philips',
|
|
},
|
|
failed: [],
|
|
friendlyName: 'bulb_color',
|
|
ieeeAddr: '0x000b57fffec6a5b3',
|
|
lastSeen: 1000,
|
|
modelID: 'LLC020',
|
|
networkAddress: 40399,
|
|
type: 'Router',
|
|
},
|
|
{
|
|
definition: {
|
|
description: 'Wireless remote switch (double rocker), 2016 model',
|
|
model: 'WXKG02LM_rev1',
|
|
supports: 'battery, voltage, power_outage_count, action, linkquality',
|
|
vendor: 'Aqara',
|
|
},
|
|
friendlyName: 'button_double_key',
|
|
ieeeAddr: '0x0017880104e45521',
|
|
lastSeen: 1000,
|
|
modelID: 'lumi.sensor_86sw2.es1',
|
|
networkAddress: 6538,
|
|
type: 'EndDevice',
|
|
},
|
|
{
|
|
definition: {
|
|
description: 'Automatically generated definition',
|
|
model: 'notSupportedModelID',
|
|
supports: 'action, linkquality',
|
|
vendor: 'Boef',
|
|
},
|
|
failed: ['lqi', 'routingTable'],
|
|
friendlyName: '0x0017880104e45525',
|
|
ieeeAddr: '0x0017880104e45525',
|
|
lastSeen: 1000,
|
|
manufacturerName: 'Boef',
|
|
modelID: 'notSupportedModelID',
|
|
networkAddress: 6536,
|
|
type: 'Router',
|
|
},
|
|
{
|
|
definition: {description: 'CC2530 router', model: 'CC2530.ROUTER', supports: 'led, linkquality', vendor: 'Custom devices (DiY)'},
|
|
failed: [],
|
|
friendlyName: 'cc2530_router',
|
|
ieeeAddr: '0x0017880104e45559',
|
|
lastSeen: 1000,
|
|
modelID: 'lumi.router',
|
|
networkAddress: 6540,
|
|
type: 'Router',
|
|
},
|
|
{
|
|
definition: {description: 'external', model: 'external_converter_device', supports: 'linkquality', vendor: 'external'},
|
|
friendlyName: '0x0017880104e45511',
|
|
ieeeAddr: '0x0017880104e45511',
|
|
lastSeen: 1000,
|
|
modelID: 'external_converter_device',
|
|
networkAddress: 1114,
|
|
type: 'EndDevice',
|
|
},
|
|
],
|
|
};
|
|
expect(JSON.parse(call[1])).toStrictEqual(expected);
|
|
|
|
/**
|
|
* Check again without routes
|
|
*/
|
|
MQTT.publish.mockClear();
|
|
MQTT.events.message('zigbee2mqtt/bridge/networkmap', 'raw');
|
|
await flushPromises();
|
|
call = MQTT.publish.mock.calls[0];
|
|
expect(MQTT.publish).toHaveBeenCalledTimes(1);
|
|
expect(call[0]).toStrictEqual('zigbee2mqtt/bridge/networkmap/raw');
|
|
|
|
// Remove routing information
|
|
expected.nodes.forEach((n) => {
|
|
if (n.failed && n.failed.includes('routingTable')) {
|
|
n.failed.splice(n.failed.indexOf('routingTable'), 1);
|
|
}
|
|
});
|
|
|
|
expected.links.forEach((l) => (l.routes = []));
|
|
expect(JSON.parse(call[1])).toStrictEqual(expected);
|
|
});
|
|
|
|
it('Output graphviz networkmap legacy api', async () => {
|
|
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', 'graphviz');
|
|
await flushPromises();
|
|
expect(MQTT.publish).toHaveBeenCalledTimes(1);
|
|
let call = MQTT.publish.mock.calls[0];
|
|
expect(call[0]).toStrictEqual('zigbee2mqtt/bridge/networkmap/graphviz');
|
|
|
|
const expected = `digraph G {
|
|
node[shape=record];
|
|
"0x00124b00120144ae" [style="bold, filled", fillcolor="#e04e5d", fontcolor="#ffffff", label="{Coordinator|0x00124b00120144ae (0x0000)|0 seconds ago}"];
|
|
"0x000b57fffec6a5b2" [style="rounded, filled", fillcolor="#4ea3e0", fontcolor="#ffffff", label="{bulb|0x000b57fffec6a5b2 (0x9db1)|IKEA TRADFRI bulb E26/E27, white spectrum, globe, opal, 980 lm (LED1545G12)|9 seconds ago}"];
|
|
"0x000b57fffec6a5b2" -> "0x00124b00120144ae" [penwidth=2, weight=1, color="#009900", label="92 (routes: 0x198c)"]
|
|
"0x000b57fffec6a5b3" [style="rounded, filled", fillcolor="#4ea3e0", fontcolor="#ffffff", label="{bulb_color|0x000b57fffec6a5b3 (0x9dcf)|Philips Hue Go (7146060PH)|unknown}"];
|
|
"0x000b57fffec6a5b3" -> "0x00124b00120144ae" [penwidth=0.5, weight=0, color="#994444", label="120"]
|
|
"0x000b57fffec6a5b3" -> "0x000b57fffec6a5b2" [penwidth=0.5, weight=0, color="#994444", label="110"]
|
|
"0x0017880104e45521" [style="rounded, dashed, filled", fillcolor="#fff8ce", fontcolor="#000000", label="{button_double_key|0x0017880104e45521 (0x198a)|Aqara Wireless remote switch (double rocker), 2016 model (WXKG02LM_rev1)|9 seconds ago}"];
|
|
"0x0017880104e45521" -> "0x0017880104e45559" [penwidth=1, weight=0, color="#994444", label="130"]
|
|
"0x0017880104e45525" [style="rounded, filled", fillcolor="#4ea3e0", fontcolor="#ffffff", label="{0x0017880104e45525|0x0017880104e45525 (0x1988)failed: lqi,routingTable|Boef Automatically generated definition (notSupportedModelID)|9 seconds ago}"];
|
|
"0x0017880104e45559" [style="rounded, filled", fillcolor="#4ea3e0", fontcolor="#ffffff", label="{cc2530_router|0x0017880104e45559 (0x198c)|Custom devices (DiY) CC2530 router (CC2530.ROUTER)|9 seconds ago}"];
|
|
"0x0017880104e45559" -> "0x000b57fffec6a5b2" [penwidth=0.5, weight=0, color="#994444", label="100"]
|
|
"0x0017880104e45511" [style="rounded, dashed, filled", fillcolor="#fff8ce", fontcolor="#000000", label="{0x0017880104e45511|0x0017880104e45511 (0x045a)|external external (external_converter_device)|9 seconds ago}"];
|
|
"0x0017880104e45511" -> "0x00124b00120144ae" [penwidth=1, weight=0, color="#994444", label="92"]
|
|
}`;
|
|
|
|
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());
|
|
}
|
|
});
|
|
|
|
it('Output plantuml networkmap legacy api', async () => {
|
|
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 0x0017880104e45511 [
|
|
0x0017880104e45511
|
|
---
|
|
0x0017880104e45511 (0x045a)
|
|
---
|
|
external external (external_converter_device)
|
|
---
|
|
9 seconds ago
|
|
]
|
|
|
|
card 0x0017880104e45525 [
|
|
0x0017880104e45525
|
|
---
|
|
0x0017880104e45525 (0x1988) failed: lqi,routingTable
|
|
---
|
|
Boef Automatically generated definition (notSupportedModelID)
|
|
---
|
|
9 seconds ago
|
|
]
|
|
|
|
card 0x000b57fffec6a5b2 [
|
|
bulb
|
|
---
|
|
0x000b57fffec6a5b2 (0x9db1)
|
|
---
|
|
IKEA TRADFRI bulb E26/E27, white spectrum, globe, opal, 980 lm (LED1545G12)
|
|
---
|
|
9 seconds ago
|
|
]
|
|
|
|
card 0x000b57fffec6a5b3 [
|
|
bulb_color
|
|
---
|
|
0x000b57fffec6a5b3 (0x9dcf)
|
|
---
|
|
Philips Hue Go (7146060PH)
|
|
---
|
|
unknown
|
|
]
|
|
|
|
card 0x0017880104e45521 [
|
|
button_double_key
|
|
---
|
|
0x0017880104e45521 (0x198a)
|
|
---
|
|
Aqara Wireless remote switch (double rocker), 2016 model (WXKG02LM_rev1)
|
|
---
|
|
9 seconds ago
|
|
]
|
|
|
|
card 0x0017880104e45559 [
|
|
cc2530_router
|
|
---
|
|
0x0017880104e45559 (0x198c)
|
|
---
|
|
Custom devices (DiY) CC2530 router (CC2530.ROUTER)
|
|
---
|
|
9 seconds ago
|
|
]
|
|
|
|
card 0x00124b00120144ae [
|
|
Coordinator
|
|
---
|
|
0x00124b00120144ae (0x0000)
|
|
---
|
|
0 seconds ago
|
|
]
|
|
|
|
0x000b57fffec6a5b3 --> 0x00124b00120144ae: 120
|
|
0x000b57fffec6a5b2 --> 0x00124b00120144ae: 92
|
|
0x0017880104e45511 --> 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());
|
|
}
|
|
});
|
|
|
|
it('Should output raw networkmap', async () => {
|
|
mock();
|
|
MQTT.publish.mockClear();
|
|
MQTT.events.message('zigbee2mqtt/bridge/request/networkmap', stringify({type: 'raw', routes: true}));
|
|
await flushPromises();
|
|
expect(MQTT.publish).toHaveBeenCalledTimes(1);
|
|
let call = MQTT.publish.mock.calls[0];
|
|
expect(call[0]).toStrictEqual('zigbee2mqtt/bridge/response/networkmap');
|
|
|
|
const expected = {
|
|
data: {
|
|
routes: true,
|
|
type: 'raw',
|
|
value: {
|
|
links: [
|
|
{
|
|
depth: 1,
|
|
linkquality: 120,
|
|
lqi: 120,
|
|
relationship: 2,
|
|
routes: [],
|
|
source: {ieeeAddr: '0x000b57fffec6a5b3', networkAddress: 40399},
|
|
sourceIeeeAddr: '0x000b57fffec6a5b3',
|
|
sourceNwkAddr: 40399,
|
|
target: {ieeeAddr: '0x00124b00120144ae', networkAddress: 0},
|
|
targetIeeeAddr: '0x00124b00120144ae',
|
|
},
|
|
{
|
|
depth: 1,
|
|
linkquality: 92,
|
|
lqi: 92,
|
|
relationship: 2,
|
|
routes: [{destinationAddress: 6540, nextHop: 40369, status: 'ACTIVE'}],
|
|
source: {ieeeAddr: '0x000b57fffec6a5b2', networkAddress: 40369},
|
|
sourceIeeeAddr: '0x000b57fffec6a5b2',
|
|
sourceNwkAddr: 40369,
|
|
target: {ieeeAddr: '0x00124b00120144ae', networkAddress: 0},
|
|
targetIeeeAddr: '0x00124b00120144ae',
|
|
},
|
|
{
|
|
depth: 1,
|
|
linkquality: 92,
|
|
lqi: 92,
|
|
relationship: 2,
|
|
routes: [],
|
|
source: {ieeeAddr: '0x0017880104e45511', networkAddress: 1114},
|
|
sourceIeeeAddr: '0x0017880104e45511',
|
|
sourceNwkAddr: 1114,
|
|
target: {ieeeAddr: '0x00124b00120144ae', networkAddress: 0},
|
|
targetIeeeAddr: '0x00124b00120144ae',
|
|
},
|
|
{
|
|
depth: 2,
|
|
linkquality: 110,
|
|
lqi: 110,
|
|
relationship: 1,
|
|
routes: [],
|
|
source: {ieeeAddr: '0x000b57fffec6a5b3', networkAddress: 40399},
|
|
sourceIeeeAddr: '0x000b57fffec6a5b3',
|
|
sourceNwkAddr: 40399,
|
|
target: {ieeeAddr: '0x000b57fffec6a5b2', networkAddress: 40369},
|
|
targetIeeeAddr: '0x000b57fffec6a5b2',
|
|
},
|
|
{
|
|
depth: 2,
|
|
linkquality: 100,
|
|
lqi: 100,
|
|
relationship: 1,
|
|
routes: [],
|
|
source: {ieeeAddr: '0x0017880104e45559', networkAddress: 6540},
|
|
sourceIeeeAddr: '0x0017880104e45559',
|
|
sourceNwkAddr: 6540,
|
|
target: {ieeeAddr: '0x000b57fffec6a5b2', networkAddress: 40369},
|
|
targetIeeeAddr: '0x000b57fffec6a5b2',
|
|
},
|
|
{
|
|
depth: 2,
|
|
linkquality: 130,
|
|
lqi: 130,
|
|
relationship: 1,
|
|
routes: [],
|
|
source: {ieeeAddr: '0x0017880104e45521', networkAddress: 6538},
|
|
sourceIeeeAddr: '0x0017880104e45521',
|
|
sourceNwkAddr: 6538,
|
|
target: {ieeeAddr: '0x0017880104e45559', networkAddress: 6540},
|
|
targetIeeeAddr: '0x0017880104e45559',
|
|
},
|
|
],
|
|
nodes: [
|
|
{
|
|
definition: null,
|
|
failed: [],
|
|
friendlyName: 'Coordinator',
|
|
ieeeAddr: '0x00124b00120144ae',
|
|
lastSeen: 1000,
|
|
modelID: null,
|
|
networkAddress: 0,
|
|
type: 'Coordinator',
|
|
},
|
|
{
|
|
definition: {
|
|
description: 'TRADFRI bulb E26/E27, white spectrum, globe, opal, 980 lm',
|
|
model: 'LED1545G12',
|
|
supports:
|
|
'light (state, brightness, color_temp, color_temp_startup), effect, power_on_behavior, color_options, identify, linkquality',
|
|
vendor: 'IKEA',
|
|
},
|
|
failed: [],
|
|
friendlyName: 'bulb',
|
|
ieeeAddr: '0x000b57fffec6a5b2',
|
|
lastSeen: 1000,
|
|
modelID: 'TRADFRI bulb E27 WS opal 980lm',
|
|
networkAddress: 40369,
|
|
type: 'Router',
|
|
},
|
|
{
|
|
definition: {
|
|
description: 'Hue Go',
|
|
model: '7146060PH',
|
|
supports:
|
|
'light (state, brightness, color_temp, color_temp_startup, color_xy, color_hs), power_on_behavior, effect, linkquality',
|
|
vendor: 'Philips',
|
|
},
|
|
failed: [],
|
|
friendlyName: 'bulb_color',
|
|
ieeeAddr: '0x000b57fffec6a5b3',
|
|
lastSeen: 1000,
|
|
modelID: 'LLC020',
|
|
networkAddress: 40399,
|
|
type: 'Router',
|
|
},
|
|
{
|
|
definition: {
|
|
description: 'Wireless remote switch (double rocker), 2016 model',
|
|
model: 'WXKG02LM_rev1',
|
|
supports: 'battery, voltage, power_outage_count, action, linkquality',
|
|
vendor: 'Aqara',
|
|
},
|
|
friendlyName: 'button_double_key',
|
|
ieeeAddr: '0x0017880104e45521',
|
|
lastSeen: 1000,
|
|
modelID: 'lumi.sensor_86sw2.es1',
|
|
networkAddress: 6538,
|
|
type: 'EndDevice',
|
|
},
|
|
{
|
|
definition: {
|
|
description: 'Automatically generated definition',
|
|
model: 'notSupportedModelID',
|
|
supports: 'action, linkquality',
|
|
vendor: 'Boef',
|
|
},
|
|
failed: ['lqi', 'routingTable'],
|
|
friendlyName: '0x0017880104e45525',
|
|
ieeeAddr: '0x0017880104e45525',
|
|
lastSeen: 1000,
|
|
manufacturerName: 'Boef',
|
|
modelID: 'notSupportedModelID',
|
|
networkAddress: 6536,
|
|
type: 'Router',
|
|
},
|
|
{
|
|
definition: {
|
|
description: 'CC2530 router',
|
|
model: 'CC2530.ROUTER',
|
|
supports: 'led, linkquality',
|
|
vendor: 'Custom devices (DiY)',
|
|
},
|
|
failed: [],
|
|
friendlyName: 'cc2530_router',
|
|
ieeeAddr: '0x0017880104e45559',
|
|
lastSeen: 1000,
|
|
modelID: 'lumi.router',
|
|
networkAddress: 6540,
|
|
type: 'Router',
|
|
},
|
|
{
|
|
definition: {description: 'external', model: 'external_converter_device', supports: 'linkquality', vendor: 'external'},
|
|
friendlyName: '0x0017880104e45511',
|
|
ieeeAddr: '0x0017880104e45511',
|
|
lastSeen: 1000,
|
|
modelID: 'external_converter_device',
|
|
networkAddress: 1114,
|
|
type: 'EndDevice',
|
|
},
|
|
],
|
|
},
|
|
},
|
|
status: 'ok',
|
|
};
|
|
const actual = JSON.parse(call[1]);
|
|
expect(actual).toStrictEqual(expected);
|
|
});
|
|
|
|
it('Should throw error when requesting invalid type', async () => {
|
|
mock();
|
|
MQTT.publish.mockClear();
|
|
MQTT.events.message('zigbee2mqtt/bridge/request/networkmap', 'not_existing');
|
|
await flushPromises();
|
|
expect(MQTT.publish).toHaveBeenCalledWith(
|
|
'zigbee2mqtt/bridge/response/networkmap',
|
|
stringify({data: {}, status: 'error', error: "Type 'not_existing' not supported, allowed are: raw,graphviz,plantuml"}),
|
|
{retain: false, qos: 0},
|
|
expect.any(Function),
|
|
);
|
|
});
|
|
|
|
it('Should exclude disabled devices from networkmap', async () => {
|
|
settings.set(['devices', '0x000b57fffec6a5b2', 'disabled'], true);
|
|
mock();
|
|
MQTT.publish.mockClear();
|
|
MQTT.events.message('zigbee2mqtt/bridge/request/networkmap', stringify({type: 'raw', routes: true}));
|
|
await flushPromises();
|
|
expect(MQTT.publish).toHaveBeenCalledTimes(1);
|
|
let call = MQTT.publish.mock.calls[0];
|
|
expect(call[0]).toStrictEqual('zigbee2mqtt/bridge/response/networkmap');
|
|
|
|
const expected = {
|
|
data: {
|
|
routes: true,
|
|
type: 'raw',
|
|
value: {
|
|
links: [
|
|
{
|
|
depth: 1,
|
|
linkquality: 120,
|
|
lqi: 120,
|
|
relationship: 2,
|
|
routes: [],
|
|
source: {ieeeAddr: '0x000b57fffec6a5b3', networkAddress: 40399},
|
|
sourceIeeeAddr: '0x000b57fffec6a5b3',
|
|
sourceNwkAddr: 40399,
|
|
target: {ieeeAddr: '0x00124b00120144ae', networkAddress: 0},
|
|
targetIeeeAddr: '0x00124b00120144ae',
|
|
},
|
|
{
|
|
depth: 1,
|
|
linkquality: 92,
|
|
lqi: 92,
|
|
relationship: 2,
|
|
routes: [{destinationAddress: 6540, nextHop: 40369, status: 'ACTIVE'}],
|
|
source: {ieeeAddr: '0x000b57fffec6a5b2', networkAddress: 40369},
|
|
sourceIeeeAddr: '0x000b57fffec6a5b2',
|
|
sourceNwkAddr: 40369,
|
|
target: {ieeeAddr: '0x00124b00120144ae', networkAddress: 0},
|
|
targetIeeeAddr: '0x00124b00120144ae',
|
|
},
|
|
{
|
|
depth: 1,
|
|
linkquality: 92,
|
|
lqi: 92,
|
|
relationship: 2,
|
|
routes: [],
|
|
source: {ieeeAddr: '0x0017880104e45511', networkAddress: 1114},
|
|
sourceIeeeAddr: '0x0017880104e45511',
|
|
sourceNwkAddr: 1114,
|
|
target: {ieeeAddr: '0x00124b00120144ae', networkAddress: 0},
|
|
targetIeeeAddr: '0x00124b00120144ae',
|
|
},
|
|
{
|
|
depth: 2,
|
|
linkquality: 130,
|
|
lqi: 130,
|
|
relationship: 1,
|
|
routes: [],
|
|
source: {ieeeAddr: '0x0017880104e45521', networkAddress: 6538},
|
|
sourceIeeeAddr: '0x0017880104e45521',
|
|
sourceNwkAddr: 6538,
|
|
target: {ieeeAddr: '0x0017880104e45559', networkAddress: 6540},
|
|
targetIeeeAddr: '0x0017880104e45559',
|
|
},
|
|
],
|
|
nodes: [
|
|
{
|
|
definition: null,
|
|
failed: [],
|
|
friendlyName: 'Coordinator',
|
|
ieeeAddr: '0x00124b00120144ae',
|
|
lastSeen: 1000,
|
|
modelID: null,
|
|
networkAddress: 0,
|
|
type: 'Coordinator',
|
|
},
|
|
{
|
|
definition: {
|
|
description: 'Hue Go',
|
|
model: '7146060PH',
|
|
supports:
|
|
'light (state, brightness, color_temp, color_temp_startup, color_xy, color_hs), power_on_behavior, effect, linkquality',
|
|
vendor: 'Philips',
|
|
},
|
|
failed: [],
|
|
friendlyName: 'bulb_color',
|
|
ieeeAddr: '0x000b57fffec6a5b3',
|
|
lastSeen: 1000,
|
|
modelID: 'LLC020',
|
|
networkAddress: 40399,
|
|
type: 'Router',
|
|
},
|
|
{
|
|
definition: {
|
|
description: 'Wireless remote switch (double rocker), 2016 model',
|
|
model: 'WXKG02LM_rev1',
|
|
supports: 'battery, voltage, power_outage_count, action, linkquality',
|
|
vendor: 'Aqara',
|
|
},
|
|
friendlyName: 'button_double_key',
|
|
ieeeAddr: '0x0017880104e45521',
|
|
lastSeen: 1000,
|
|
modelID: 'lumi.sensor_86sw2.es1',
|
|
networkAddress: 6538,
|
|
type: 'EndDevice',
|
|
},
|
|
{
|
|
definition: {
|
|
description: 'Automatically generated definition',
|
|
model: 'notSupportedModelID',
|
|
supports: 'action, linkquality',
|
|
vendor: 'Boef',
|
|
},
|
|
failed: ['lqi', 'routingTable'],
|
|
friendlyName: '0x0017880104e45525',
|
|
ieeeAddr: '0x0017880104e45525',
|
|
lastSeen: 1000,
|
|
manufacturerName: 'Boef',
|
|
modelID: 'notSupportedModelID',
|
|
networkAddress: 6536,
|
|
type: 'Router',
|
|
},
|
|
{
|
|
definition: {
|
|
description: 'CC2530 router',
|
|
model: 'CC2530.ROUTER',
|
|
supports: 'led, linkquality',
|
|
vendor: 'Custom devices (DiY)',
|
|
},
|
|
failed: [],
|
|
friendlyName: 'cc2530_router',
|
|
ieeeAddr: '0x0017880104e45559',
|
|
lastSeen: 1000,
|
|
modelID: 'lumi.router',
|
|
networkAddress: 6540,
|
|
type: 'Router',
|
|
},
|
|
{
|
|
definition: {description: 'external', model: 'external_converter_device', supports: 'linkquality', vendor: 'external'},
|
|
friendlyName: '0x0017880104e45511',
|
|
ieeeAddr: '0x0017880104e45511',
|
|
lastSeen: 1000,
|
|
modelID: 'external_converter_device',
|
|
networkAddress: 1114,
|
|
type: 'EndDevice',
|
|
},
|
|
],
|
|
},
|
|
},
|
|
status: 'ok',
|
|
};
|
|
const actual = JSON.parse(call[1]);
|
|
expect(actual).toStrictEqual(expected);
|
|
});
|
|
});
|