danielwippermann/resol-vbus

View on GitHub
examples/discovery/index.js

Summary

Maintainability
B
5 hrs
Test Coverage
/*! resol-vbus | Copyright (c) 2013-present, Daniel Wippermann | MIT license */

const os = require('os');

const {
    TcpDataSourceProvider,
} = require('../resol-vbus');



const knownDataSources = new Map();


async function getOrFetchInformation(key, interfaceName, address, discoveredAddress) {
    let result;
    if (knownDataSources.has(key)) {
        result = knownDataSources.get(key);
    } else {
        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 commonDiscoveryOptions = {
        broadcastPort: 7053,
        tries: 3,
        timeout: 500,
    };

    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;
                }
            }
        }

        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);
        }
    }

    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 = 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__;
        console.log('REMOVED: ', id, device.name);
    }

    for (const device of addedDevices) {
        const id = device.__address__;
        console.log('ADDED: ', id, device.name);
    }
}



async function main() {
    const interval = 10000;

    while (true) {
        console.log('------', new Date(), '------');

        await discover();

        await new Promise((resolve) => {
            setTimeout(resolve, interval - (Date.now() % interval));
        });
    }
};



if (require.main === module) {
    main().then(null, err => {
        console.log(err);
    });
}