Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature request]: mdns zeroconf discovery for network coordinators #17430

Closed
Tarik2142 opened this issue Apr 23, 2023 · 0 comments
Closed

[Feature request]: mdns zeroconf discovery for network coordinators #17430

Tarik2142 opened this issue Apr 23, 2023 · 0 comments
Labels
feature request Feature request

Comments

@Tarik2142
Copy link

Is your feature request related to a problem? Please describe

The main problem is the need to scan the network or find the IP address of the network coordinator in other ways. An easy and quick solution that requires a minimum of effort from the user is needed

Describe the solution you'd like

I suggest adding support for easy disovery of network coordinators through mdns.

I suggest using the already existing zeroconf ZHA structure so that all coordinators with autodiscovery in ZHA are available for discovery in Z2M without changing the device firmware.
image
image

In the configuration, you need to write:

serial:
  port: mdns://device_type
  #port: mdns://slzb-06

Describe alternatives you've considered

Alternatively, you can develop your own network discovery protocol, perhaps based on UDP broadcast

Additional context

I ran a series of tests and added my code to Adapter.js, and I was able to automatically detect the coordinator and start Z2M.
Here is my implementation:

 static async create(networkOptions, serialPortOptions, backupPath, adapterOptions, logger) {
        const { ZStackAdapter } = await Promise.resolve().then(() => __importStar(require('./z-stack/adapter')));
        const { DeconzAdapter } = await Promise.resolve().then(() => __importStar(require('./deconz/adapter')));
        const { ZiGateAdapter } = await Promise.resolve().then(() => __importStar(require('./zigate/adapter')));
        const { EZSPAdapter } = await Promise.resolve().then(() => __importStar(require('./ezsp/adapter')));
        let adapters;
        const adapterLookup = { zstack: ZStackAdapter, deconz: DeconzAdapter, zigate: ZiGateAdapter,
            ezsp: EZSPAdapter };
        if (serialPortOptions.adapter && serialPortOptions.adapter !== 'auto') {
            if (adapterLookup.hasOwnProperty(serialPortOptions.adapter)) {
                adapters = [adapterLookup[serialPortOptions.adapter]];
            }
            else {
                throw new Error(`Adapter '${serialPortOptions.adapter}' does not exists, possible ` +
                    `options: ${Object.keys(adapterLookup).join(', ')}`);
            }
        }
        else {
            adapters = Object.values(adapterLookup);
        }
        // Use ZStackAdapter by default
        let adapter = adapters[0];
        if (!serialPortOptions.path) {
            debug('No path provided, auto detecting path');
            for (const candidate of adapters) {
                const path = await candidate.autoDetectPath();
                if (path) {
                    debug(`Auto detected path '${path}' from adapter '${candidate.name}'`);
                    serialPortOptions.path = path;
                    adapter = candidate;
                    break;
                }
            }
            if (!serialPortOptions.path) {
                throw new Error("No path provided and failed to auto detect path");
            }
        }
        else if(serialPortOptions.path.indexOf("mdns://") != -1){
                logger.info("Starting mdns discovery...");
                const bonjour = require('bonjour-hap')();
                const mdnsDevice = serialPortOptions.path.substr(7);
                var mdnsIp;
                var mdnsPort;
                var mdnsRadio;
                var mdnsBaud;
                bonjour.findOne({ type: mdnsDevice}, function (service) {
                    mdnsIp = service.addresses[0];
                    mdnsPort = service.port;
                    mdnsRadio = service.txt.radio_type == "znp" ? "zstack" : service.txt.service;
                    mdnsBaud = service.txt.baud_rate;
                    logger.info("mdns discovery done!");
                    logger.info("Adapter Ip: " + mdnsIp);
                    logger.info("Adapter Port: " + mdnsPort);
                    logger.info("Adapter Radio: " + mdnsRadio);
                    logger.info("Adapter Baud: " + mdnsBaud + "\n");
                    bonjour.destroy();
                });
                return await new Promise((resolve, reject) => {
                    setTimeout(() => {
                        if(mdnsIp && mdnsPort && mdnsRadio && mdnsBaud){
                            serialPortOptions.path = "tcp://" + mdnsIp + ":" + mdnsPort;
                            serialPortOptions.adapter = mdnsRadio;
                            serialPortOptions.baudrate = mdnsBaud;
                            resolve(new adapter(networkOptions, serialPortOptions, backupPath, adapterOptions, logger));
                        }else{
                            bonjour.destroy();
                            reject(new Error("mdns device " + mdnsDevice + " not found!"));
                        }
                }, 5000)});
            }else{
            try {
                // Determine adapter to use
                for (const candidate of adapters) {
                    if (await candidate.isValidPath(serialPortOptions.path)) {
                        debug(`Path '${serialPortOptions.path}' is valid for '${candidate.name}'`);
                        adapter = candidate;
                        break;
                    }
                }
            }
            catch (error) {
                debug(`Failed to validate path: '${error}'`);
            }
        }
        if(serialPortOptions.path.indexOf("mdns://") < 0) return new adapter(networkOptions, serialPortOptions, backupPath, adapterOptions, logger);
    }

image

The problem of this solution is the support of multi-adapter setups because it is determined by type not by hostname (this is a limitation of the bonjour-hap library).
Possible solutions:

  • specify type+hostname in the configuration and then filter devices by hostname.
  • changes in the firmware of the coordinators, add the ability to change the device type and not just the hostname.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature request Feature request
Projects
None yet
Development

No branches or pull requests

1 participant