From 4979d2a372b70d4e8597cc242af775e0d4bc782f Mon Sep 17 00:00:00 2001 From: Daniel Wippermann Date: Sat, 20 Jan 2024 11:10:29 +0100 Subject: [PATCH] Improve discovery example to support IPv6. --- examples/discovery/index.js | 121 +++++++++++++++++++++++++----------- 1 file changed, 86 insertions(+), 35 deletions(-) diff --git a/examples/discovery/index.js b/examples/discovery/index.js index a20a636c..69fd7afb 100644 --- a/examples/discovery/index.js +++ b/examples/discovery/index.js @@ -1,5 +1,7 @@ /*! resol-vbus | Copyright (c) 2013-present, Daniel Wippermann | MIT license */ +const os = require('os'); + const { TcpDataSourceProvider, } = require('../resol-vbus'); @@ -9,79 +11,128 @@ const { const knownDataSources = new Map(); -const fetchCallback = async (address) => { +async function getOrFetchInformation(key, interfaceName, address, discoveredAddress) { let result; - if (knownDataSources.has(address)) { - result = knownDataSources.get(address); + if (knownDataSources.has(key)) { + result = knownDataSources.get(key); } else { - // console.log('QUERY: ', address); - result = TcpDataSourceProvider.fetchDeviceInformation(address); - result = result.then((result) => { - // console.log('QUERY SUCCESS: ', address); - return result; - }, (reason) => { - console.log('QUERY FAILED: ', address); - return {}; - }); + if (address.family === 'IPv4') { + result = await TcpDataSourceProvider.fetchDeviceInformation({ + hostname: discoveredAddress, + family: 4, + }); + } else if (address.family === 'IPv6') { + result = await TcpDataSourceProvider.fetchDeviceInformation({ + hostname: discoveredAddress, + localAdress: `${address.address}%${interfaceName}`, + family: 6, + }); + } + + knownDataSources.set(key, result); } return result; -}; +} + +async function discover() { + const interfaceMap = os.networkInterfaces(); + + const interfaceNames = Object.getOwnPropertyNames(interfaceMap); + const knownKeySet = new Set(knownDataSources.keys()); + const fetchedKeySet = new Set(); -const discover = async () => { - const devices = await TcpDataSourceProvider.discoverDevices({ - // ipv6: true, - // broadcastInterface: 'en0', + const commonDiscoveryOptions = { broadcastPort: 7053, tries: 3, timeout: 500, - fetchCallback, - }); + }; + + const promises = []; + for (const interfaceName of interfaceNames) { + const addresses = interfaceMap [interfaceName]; + + let preferredIPv6Address = null; + for (const address of addresses) { + if (address.family === 'IPv4') { + const promise = TcpDataSourceProvider.discoverDevices({ + localAddress: address.address, + netmask: address.netmask, + ...commonDiscoveryOptions, + fetchCallback(discoveredAddress) { + const key = discoveredAddress; + fetchedKeySet.add(key); + return getOrFetchInformation(key, interfaceName, address, discoveredAddress); + }, + }); + + promises.push(promise); + } else if (address.family === 'IPv6') { + if ((preferredIPv6Address == null) || (preferredIPv6Address.scopeid < address.scopeid)) { + preferredIPv6Address = address; + } + } + } - const deviceById = devices.reduce((memo, device) => { - const id = device.__address__; - memo.set(id, device); - return memo; - }, new Map()); + if (preferredIPv6Address) { + const promise = TcpDataSourceProvider.discoverDevices({ + family: 'IPv6', + localAddress: preferredIPv6Address.address, + broadcastInterface: interfaceName, + ...commonDiscoveryOptions, + fetchCallback(discoveredAddress) { + const key = `${discoveredAddress}%${interfaceName}`; + fetchedKeySet.add(key); + return getOrFetchInformation(key, interfaceName, preferredIPv6Address, discoveredAddress); + }, + }); + + promises.push(promise); + } + } - const addedDevices = devices.reduce((memo, device) => { - const id = device.__address__; - if (!knownDataSources.has(id)) { + await Promise.all(promises); + + const addedDevices = Array.from(fetchedKeySet.values()).reduce((memo, key) => { + if (!knownKeySet.has(key)) { + const device = knownDataSources.get(key); memo.push(device); } return memo; }, []); - const removedDevices = [...knownDataSources.keys()].reduce((memo, id) => { - if (!deviceById.has(id)) { - memo.push(knownDataSources.get(id)); + const removedDevices = Array.from(knownKeySet.values()).reduce((memo, key) => { + if (!fetchedKeySet.has(key)) { + const device = knownDataSources.get(key); + knownDataSources.delete(key); + memo.push(device); } return memo; }, []); for (const device of removedDevices) { const id = device.__address__; - knownDataSources.delete(id); console.log('REMOVED: ', id, device.name); } for (const device of addedDevices) { const id = device.__address__; - knownDataSources.set(id, device); console.log('ADDED: ', id, device.name); } -}; +} + +async function main() { + const interval = 10000; -const main = async () => { while (true) { console.log('------', new Date(), '------'); await discover(); await new Promise((resolve) => { - setTimeout(resolve, 10000); + setTimeout(resolve, interval - (Date.now() % interval)); }); } };